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>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
53d06c3c26
commit
3e3708ef06
@@ -0,0 +1,328 @@
|
||||
// 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";
|
||||
Reference in New Issue
Block a user