Files
metin2-client/extension/src/platform/ScriptLib/RegisterScript.inc
T
shenleiandClaude Opus 5.5 3e3708ef06 android-native: auto hunt, in-client account registration, mobile UI polish
- Auto hunt (client-only): PythonPlayerAutoHunt.cpp + AutoHuntScript.inc as the
  embedded mt_autohunt module, AUTO touch button, F9/F8.
- Account registration: tools/40250/register_server.py (rc.d mt_register, :11080)
  inserts into account.account; net.RegisterAccount/GetRegisterAccountResult
  (PythonAccountRegister.cpp) and the mt_register login-window dialog
  (RegisterScript.inc).
- --py-exec also runs in --login-screen mode for scripted login-screen tests.
- Touch controller, window manager and render command updates for the
  Android native client.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 16:15:04 +09:00

329 lines
9.4 KiB
PHP

// The account registration window (PORT; 40250 has no counterpart, its accounts were made on the web),
// run by PythonBoot::RunMainScript as module mt_register. It hooks introLogin.LoginWindow.Open/Close and
// puts a register button under the login board; the dialog posts to the server's registration service
// (tools/40250/register_server.py) through net.RegisterAccount. Strings are UTF-8 here and go through
// player.AutoHuntLocalize into the locale's code page.
static const char c_szRegisterScript[] = R"PY(# -*- coding: utf-8 -*-
import introLogin
import net
import player
import ui
import wndMgr
L = player.AutoHuntLocalize
REGISTER_PORT = 11080
LOGIN_MIN, LOGIN_MAX = 4, 16
PASSWORD_MIN, PASSWORD_MAX = 4, 16
SOCIAL_ID_LEN = 7
LARGE_BUTTON = "d:/ymir work/ui/public/large_button_%02d.sub"
MIDDLE_BUTTON = "d:/ymir work/ui/public/middle_button_%02d.sub"
COLOR_ERROR = 0xffff7f7f
COLOR_INFO = 0xffc8c8c8
ERROR_TEXT = {
"BAD_LOGIN": "账号需为4-16位英文字母或数字",
"BAD_PASSWORD": "密码需为4-16位字母、数字或符号",
"BAD_SOCIAL_ID": "删除码需为7位数字",
"EXISTS": "该账号已存在",
"RATE_LIMIT": "注册过于频繁,请稍后再试",
"CONNECT": "无法连接注册服务器",
"TIMEOUT": "注册服务器无响应",
"SERVER": "注册服务器出错,请稍后再试",
}
def _IsAlnum(text):
for c in text:
if not (("a" <= c <= "z") or ("A" <= c <= "Z") or ("0" <= c <= "9")):
return False
return True
def _IsPrintable(text):
for c in text:
if not ("!" <= c <= "~"):
return False
return True
def _Validate(login, password, confirm, socialID):
if not (LOGIN_MIN <= len(login) <= LOGIN_MAX) or not _IsAlnum(login):
return ERROR_TEXT["BAD_LOGIN"]
if not (PASSWORD_MIN <= len(password) <= PASSWORD_MAX) or not _IsPrintable(password):
return ERROR_TEXT["BAD_PASSWORD"]
if password != confirm:
return "两次输入的密码不一致"
if len(socialID) != SOCIAL_ID_LEN or not socialID.isdigit():
return ERROR_TEXT["BAD_SOCIAL_ID"]
return None
def _MakeButton(parent, x, y, image, text, event):
button = ui.Button()
button.SetParent(parent)
button.SetPosition(x, y)
button.SetUpVisual(image % 1)
button.SetOverVisual(image % 2)
button.SetDownVisual(image % 3)
button.SetText(L(text))
button.SetEvent(event)
button.Show()
return button
class InputRow:
LABEL_X = 18
SLOT_X = 96
SLOT_WIDTH = 160
def __init__(self, parent, y, label, maxLen):
self.label = ui.TextLine()
self.label.SetParent(parent)
self.label.SetPosition(self.LABEL_X, y + 2)
self.label.SetText(L(label))
self.label.Show()
self.slot = ui.SlotBar()
self.slot.SetParent(parent)
self.slot.SetPosition(self.SLOT_X, y)
self.slot.SetSize(self.SLOT_WIDTH, 18)
self.slot.Show()
self.edit = ui.EditLine()
self.edit.SetParent(self.slot)
self.edit.SetPosition(4, 2)
self.edit.SetSize(self.SLOT_WIDTH - 8, 16)
self.edit.SetMax(maxLen)
self.edit.SetIMEFlag(0)
self.edit.Show()
def GetText(self):
return self.edit.GetText()
def Destroy(self):
self.edit.SetReturnEvent(0)
self.edit.SetTabEvent(0)
self.edit = None
self.slot = None
self.label = None
class RegisterDialog(ui.BoardWithTitleBar):
WIDTH = 280
HEIGHT = 222
def __init__(self, loginWindow):
ui.BoardWithTitleBar.__init__(self)
self.loginWindow = loginWindow
self.pending = False
self.pendingLogin = ""
self.pendingPassword = ""
self.AddFlag("movable")
self.AddFlag("float")
self.SetSize(self.WIDTH, self.HEIGHT)
self.SetTitleName(L("注册账号"))
self.SetCloseEvent(ui.__mem_func__(self.Close))
self.loginRow = InputRow(self, 38, "账号", LOGIN_MAX)
self.passwordRow = InputRow(self, 66, "密码", PASSWORD_MAX)
self.passwordRow.edit.SetSecret(True)
self.confirmRow = InputRow(self, 94, "确认密码", PASSWORD_MAX)
self.confirmRow.edit.SetSecret(True)
self.socialRow = InputRow(self, 122, "删除码", SOCIAL_ID_LEN)
self.socialRow.edit.SetNumberMode()
rows = (self.loginRow, self.passwordRow, self.confirmRow, self.socialRow)
for i in xrange(len(rows)):
nextEdit = rows[(i + 1) % len(rows)].edit
rows[i].edit.SetTabEvent(ui.__mem_func__(nextEdit.SetFocus))
if i + 1 < len(rows):
rows[i].edit.SetReturnEvent(ui.__mem_func__(nextEdit.SetFocus))
self.socialRow.edit.SetReturnEvent(ui.__mem_func__(self.__OnClickRegister))
for row in rows:
row.edit.SetEscapeEvent(ui.__mem_func__(self.Close))
self.hintLine = ui.TextLine()
self.hintLine.SetParent(self)
self.hintLine.SetPosition(0, 150)
self.hintLine.SetWindowHorizontalAlignCenter()
self.hintLine.SetHorizontalAlignCenter()
self.hintLine.Show()
self.registerButton = _MakeButton(self, self.WIDTH / 2 - 66, 180, MIDDLE_BUTTON, "注册",
ui.__mem_func__(self.__OnClickRegister))
self.cancelButton = _MakeButton(self, self.WIDTH / 2 + 5, 180, MIDDLE_BUTTON, "取消",
ui.__mem_func__(self.Close))
def Destroy(self):
self.Hide()
for row in (self.loginRow, self.passwordRow, self.confirmRow, self.socialRow):
row.Destroy()
self.loginRow = self.passwordRow = self.confirmRow = self.socialRow = None
self.registerButton = None
self.cancelButton = None
self.hintLine = None
self.loginWindow = None
def Open(self):
for row in (self.loginRow, self.passwordRow, self.confirmRow, self.socialRow):
row.edit.SetText("")
self.__SetHint("账号仅限英文字母和数字,删除码用于删除角色", COLOR_INFO)
self.SetPosition((wndMgr.GetScreenWidth() - self.WIDTH) / 2, (wndMgr.GetScreenHeight() - self.HEIGHT) / 2)
self.Show()
self.SetTop()
self.loginRow.edit.SetFocus()
def Close(self):
for row in (self.loginRow, self.passwordRow, self.confirmRow, self.socialRow):
row.edit.KillFocus()
self.Hide()
if self.loginWindow and self.loginWindow.idEditLine:
self.loginWindow.idEditLine.SetFocus()
return True
def OnPressEscapeKey(self):
return self.Close()
def __SetHint(self, text, color):
self.hintLine.SetText(L(text))
self.hintLine.SetPackedFontColor(color)
def __GetHost(self):
stream = self.loginWindow.stream
if stream.account_addr:
return stream.account_addr
return stream.addr
def __OnClickRegister(self):
if self.pending:
return
login = self.loginRow.GetText()
password = self.passwordRow.GetText()
error = _Validate(login, password, self.confirmRow.GetText(), self.socialRow.GetText())
if error:
self.__SetHint(error, COLOR_ERROR)
return
host = self.__GetHost()
if not host:
self.__SetHint("请先选择服务器", COLOR_ERROR)
return
if not net.RegisterAccount(host, REGISTER_PORT, login, password, self.socialRow.GetText()):
return
self.pending = True
self.pendingLogin = login.lower()
self.pendingPassword = password
self.registerButton.Disable()
self.__SetHint("正在注册...", COLOR_INFO)
def OnUpdate(self):
if not self.pending:
return
result = net.GetRegisterAccountResult()
if not result:
return
self.pending = False
self.registerButton.Enable()
if result == "OK":
self.__OnRegistered()
return
code = result[4:] if result.startswith("ERR ") else "SERVER"
self.__SetHint(ERROR_TEXT.get(code, ERROR_TEXT["SERVER"]), COLOR_ERROR)
def __OnRegistered(self):
loginWindow = self.loginWindow
login, password = self.pendingLogin, self.pendingPassword
self.pendingPassword = ""
self.Close()
if loginWindow.idEditLine:
loginWindow.idEditLine.SetText(login)
if loginWindow.pwdEditLine:
loginWindow.pwdEditLine.SetText(password)
loginWindow.PopupNotifyMessage(L("账号 %s 注册成功,请点击登录") % login)
class RegisterEntry(ui.Window):
"The button under the login board; it follows the board's position and visibility every frame."
def __init__(self, loginWindow):
ui.Window.__init__(self)
self.loginWindow = loginWindow
self.dialog = RegisterDialog(loginWindow)
self.button = ui.Button()
self.button.SetUpVisual(LARGE_BUTTON % 1)
self.button.SetOverVisual(LARGE_BUTTON % 2)
self.button.SetDownVisual(LARGE_BUTTON % 3)
self.button.SetText(L("注册账号"))
self.button.SetEvent(ui.__mem_func__(self.dialog.Open))
self.SetSize(0, 0)
self.Show()
def Destroy(self):
self.Hide()
self.button.Hide()
self.button = None
self.dialog.Destroy()
self.dialog = None
self.loginWindow = None
def OnUpdate(self):
board = self.loginWindow.loginBoard if self.loginWindow else None
if not board or not self.loginWindow.IsShow() or not board.IsShow() or self.dialog.IsShow():
if self.button.IsShow():
self.button.Hide()
return
x, y = board.GetGlobalPosition()
self.button.SetPosition(x + (board.GetWidth() - self.button.GetWidth()) / 2, y + board.GetHeight() + 6)
if not self.button.IsShow():
self.button.Show()
self.button.SetTop()
_entries = {}
def Attach(loginWindow):
key = id(loginWindow)
if key not in _entries:
_entries[key] = RegisterEntry(loginWindow)
def Detach(loginWindow):
entry = _entries.pop(id(loginWindow), None)
if entry:
entry.Destroy()
def __Install():
openLogin = introLogin.LoginWindow.Open
closeLogin = introLogin.LoginWindow.Close
def Open(self):
result = openLogin(self)
try:
Attach(self)
except:
import dbg
dbg.TraceError("mt_register: attach failed")
return result
def Close(self):
Detach(self)
return closeLogin(self)
introLogin.LoginWindow.Open = Open
introLogin.LoginWindow.Close = Close
# system.py reaches app.Loop() before this runs, so a login window may already be open.
import gc
for obj in gc.get_objects():
if isinstance(obj, introLogin.LoginWindow) and getattr(obj, "loginBoard", None):
Attach(obj)
__Install()
)PY";