|
|
@@ -0,0 +1,125 @@
|
|
|
+"""环境注入: L_GameEnvManager / FileUtils / LuaBridgeCtl / gameEnviroment / 杂项全局函数。
|
|
|
+
|
|
|
+Task 5 契约: EnvData + install_env; 不引入 python.eval, FileUtils 走 AP_* 直挂。
|
|
|
+"""
|
|
|
+from __future__ import annotations
|
|
|
+
|
|
|
+import os
|
|
|
+from typing import Any
|
|
|
+
|
|
|
+SWITCHES_DEFAULT: dict[int, int] = {2: 1} # Modules_Index_Enable_Check_Heart_Beat=1(心跳开)
|
|
|
+
|
|
|
+
|
|
|
+class EnvData:
|
|
|
+ """游戏环境数据: 登录态/账号/渠道/签名等。data 透传给 L_GameEnvManager.GetEnvDataByKey; switches / settings 独立 dict。"""
|
|
|
+
|
|
|
+ def __init__(self, data: dict[str, Any]) -> None:
|
|
|
+ self.data: dict[str, Any] = dict(data)
|
|
|
+ self.switches: dict[int, int] = dict(SWITCHES_DEFAULT)
|
|
|
+ self.settings: dict[int, list[Any]] = {}
|
|
|
+
|
|
|
+
|
|
|
+def _safe_join(abs_state: str, rel: str) -> str:
|
|
|
+ """join 后必须仍在 abs_state 内(防 rel 形如 ../escape)。返回 abspath。"""
|
|
|
+ p = os.path.abspath(os.path.join(abs_state, rel))
|
|
|
+ if os.path.commonpath([p, abs_state]) != abs_state:
|
|
|
+ raise ValueError(f"path escape: {rel!r} not under state_dir")
|
|
|
+ return p
|
|
|
+
|
|
|
+
|
|
|
+def install_env(lua: Any, env: EnvData, state_dir: str) -> None:
|
|
|
+ """注入环境: L_GameEnvManager / LuaBridgeCtl / FileUtils / gameEnviroment / 杂项全局。"""
|
|
|
+ os.makedirs(state_dir, exist_ok=True)
|
|
|
+ abs_state = os.path.abspath(state_dir)
|
|
|
+
|
|
|
+ g = lua.globals()
|
|
|
+ g["_PY_ENV"] = env
|
|
|
+ g["_PY_STATE_DIR"] = state_dir
|
|
|
+
|
|
|
+ # Lupa 不翻译 KeyError -> nil, 暴露 get/switch helper 规避
|
|
|
+ def _get_env_key(k: str) -> Any:
|
|
|
+ return env.data.get(k)
|
|
|
+
|
|
|
+ def _get_switch(i: int) -> int:
|
|
|
+ return env.switches.get(int(i), 0)
|
|
|
+
|
|
|
+ def _set_switch(i: int, v: int) -> None:
|
|
|
+ env.switches[int(i)] = int(v)
|
|
|
+
|
|
|
+ def _get_setting(i: int) -> Any:
|
|
|
+ return env.settings.get(int(i))
|
|
|
+
|
|
|
+ def _set_setting(i: int, v: Any) -> None:
|
|
|
+ env.settings[int(i)] = v
|
|
|
+
|
|
|
+ g["AP_getEnvKey"] = _get_env_key
|
|
|
+ g["AP_getSwitch"] = _get_switch
|
|
|
+ g["AP_setSwitch"] = _set_switch
|
|
|
+ g["AP_getSetting"] = _get_setting
|
|
|
+ g["AP_setSetting"] = _set_setting
|
|
|
+
|
|
|
+ # Lua 侧: env manager / bridge ctl / 杂项全局
|
|
|
+ # 冒号定义使 self 显式, 兼容游戏里 L_GameEnvManager:GetEnvDataByKey(...) / LuaBridgeCtl:Inst():...
|
|
|
+ lua.execute(r"""
|
|
|
+L_GameEnvManager = {}
|
|
|
+function L_GameEnvManager:GetEnvDataByKey(k) return AP_getEnvKey(k) end
|
|
|
+
|
|
|
+LuaBridgeCtl = {}
|
|
|
+function LuaBridgeCtl:Inst() return self end
|
|
|
+function LuaBridgeCtl:GetModulesSwitch(i) return AP_getSwitch(i) end
|
|
|
+function LuaBridgeCtl:SetModulesSwitch(i, v) AP_setSwitch(i, v) end
|
|
|
+
|
|
|
+function release_print(...) end
|
|
|
+releasePrint = release_print
|
|
|
+function GET_STRING(id) return "" end
|
|
|
+function JUMPTO(...) end
|
|
|
+function GET_SETTING(i) return AP_getSetting(i) end
|
|
|
+function CHECK_SETTING(i)
|
|
|
+ local s = AP_getSetting(i)
|
|
|
+ return s ~= nil and s[1] == 1 or false
|
|
|
+end
|
|
|
+function CHANGE_SETTING(i, v) AP_setSetting(i, v) end
|
|
|
+""")
|
|
|
+
|
|
|
+ # FileUtils: Python 静态方法暴露为 Lua 全局, cc.FileUtils Lua 薄壳包一层
|
|
|
+ class _FU:
|
|
|
+ @staticmethod
|
|
|
+ def getInstance() -> "type[_FU]": # noqa: N802
|
|
|
+ return _FU
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def getWritablePath() -> str: # noqa: N802
|
|
|
+ return abs_state + os.sep
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def writeStringToFile(s: str, rel: str) -> None: # noqa: N802
|
|
|
+ p = _safe_join(abs_state, rel)
|
|
|
+ os.makedirs(os.path.dirname(p), exist_ok=True)
|
|
|
+ with open(p, "w", encoding="utf-8") as f:
|
|
|
+ f.write(s)
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def isFileExist(rel: str) -> bool: # noqa: N802
|
|
|
+ try:
|
|
|
+ return os.path.exists(_safe_join(abs_state, rel))
|
|
|
+ except ValueError:
|
|
|
+ return False
|
|
|
+
|
|
|
+ g["AP_writeStringToFile"] = _FU.writeStringToFile
|
|
|
+ g["AP_isFileExist"] = _FU.isFileExist
|
|
|
+ g["AP_getWritablePath"] = _FU.getWritablePath
|
|
|
+ lua.execute("""
|
|
|
+cc = cc or {}
|
|
|
+cc.FileUtils = {getInstance = function() return cc.FileUtils end}
|
|
|
+function cc.FileUtils:writeStringToFile(s, rel) AP_writeStringToFile(s, rel) end
|
|
|
+function cc.FileUtils:isFileExist(rel) return AP_isFileExist(rel) end
|
|
|
+function cc.FileUtils:getWritablePath() return AP_getWritablePath() end
|
|
|
+""")
|
|
|
+
|
|
|
+ # gameEnviroment(小写 e) - 全局 C++ 单例, 最小面
|
|
|
+ lua.execute("""
|
|
|
+gameEnviroment = gameEnviroment or {}
|
|
|
+function gameEnviroment.GetNetClient() return nil end
|
|
|
+function gameEnviroment.SetLoginAccount(a) end
|
|
|
+function gameEnviroment.GetDownloadRootURL() return "" end
|
|
|
+""")
|