bridge.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. """Lupa 桥: VM 构建 + stub 装配 + 游戏 Lua 只读加载。
  2. Task 6: bootstrap_runtime 建 VM+装 stub+配 package.path+注 networkUtil 公共面薄复刻
  3. +加载 netMessageDef;load_game_module 按 require 语义加载游戏原版模块。
  4. Task 7 (host.py) 依赖本模块接口: LUA_SRC / bootstrap_runtime / load_game_module。
  5. """
  6. from __future__ import annotations
  7. import json
  8. import os
  9. from typing import Any
  10. import lupa.luajit21 as lj # type: ignore
  11. from apilot.stubs import install_all
  12. from apilot.stubs.env import EnvData
  13. from apilot.stubs.netclient import NetClientStub
  14. from apilot.stubs.networkctl import NetworkCtlStub
  15. from apilot.stubs.scheduler import Scheduler
  16. ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  17. LUA_SRC = os.path.join(ROOT, "lua脚本解密源码", "mod_fgcq", "stab", "scripts")
  18. # 协议定义文件名(brief 指定第一个加载的游戏原文件)
  19. _NETMSG_DEF = "network/netMessageDef.lua"
  20. def bootstrap_runtime(nc: NetClientStub, ctl: NetworkCtlStub, sched: Scheduler,
  21. env: EnvData, state_dir: str) -> Any:
  22. """建 Lupa VM,装 stub,设 package.path,注 class()/cjson/LuaSendMsg/... 公共面,加载 netMessageDef。"""
  23. lua = lj.LuaRuntime(unpack_returned_tuples=True)
  24. install_all(lua, nc, ctl, sched, env, state_dir)
  25. # package.path 指向游戏 scripts(只读),优先于系统默认
  26. lua.globals()["_LUA_SRC"] = LUA_SRC
  27. lua.execute("package.path = _LUA_SRC .. '/?.lua;' .. package.path")
  28. # PureMVC 极简 class()(单继承),够 framework.init 链: Facade/Mediator/Proxy/Notifier/...
  29. lua.execute(r"""
  30. class = function(name, super)
  31. local cls = {__cname = name}
  32. if super ~= nil then
  33. setmetatable(cls, {__index = super})
  34. cls.super = super
  35. else
  36. setmetatable(cls, {__index = cls})
  37. end
  38. function cls.new(...)
  39. local inst = setmetatable({}, cls)
  40. if cls.ctor then cls.ctor(inst, ...) end
  41. return inst
  42. end
  43. return cls
  44. end
  45. """)
  46. # cjson 桥: Lua table<->JSON 字符串 走 Python json
  47. g = lua.globals()
  48. def _to_jsonable(obj: Any) -> Any:
  49. if isinstance(obj, dict):
  50. return {k: _to_jsonable(v) for k, v in obj.items()}
  51. if hasattr(obj, "items"):
  52. items = list(obj.items())
  53. if items and all(isinstance(k, int) and k > 0 for k, _ in items):
  54. keys = sorted(k for k, _ in items)
  55. if keys == list(range(1, len(items) + 1)):
  56. return [_to_jsonable(v) for k, v in sorted(items)]
  57. return {k: _to_jsonable(v) for k, v in items}
  58. return obj
  59. def _enc(t: Any) -> str:
  60. return json.dumps(_to_jsonable(t), ensure_ascii=False, separators=(",", ":"))
  61. def _dec(s: str) -> Any:
  62. d = json.loads(s)
  63. return d if isinstance(d, dict) else {}
  64. g["_PY_cjson_encode"] = _enc
  65. g["_PY_cjson_decode"] = _dec
  66. # 薄复刻 networkUtil.lua 公共面(签名对齐;不真加载以免引入更多 C++ 面)
  67. lua.execute(r"""
  68. cjson = cjson or {encode = function(t) return _PY_cjson_encode(t) end,
  69. decode = function(s) return _PY_cjson_decode(s) end,
  70. safe = nil}
  71. slen = slen or function(s) return #s end
  72. function LuaSendMsg(msgId, recog, p1, p2, p3, dataStr, dataLen)
  73. networkCtl:SendMsg(msgId, recog, p1, p2, p3, dataStr, dataLen)
  74. end
  75. function LuaRegisterMsgHandler(msgId, fn)
  76. networkCtl:RegisterLuaHandler(msgId, fn)
  77. end
  78. function SendTableToServer(msgId, t)
  79. local s = cjson.encode(t)
  80. LuaSendMsg(msgId, 0, 0, 0, 0, s, #s)
  81. end
  82. """)
  83. # 加载第一个游戏原文件(协议定义)
  84. nmd_path = os.path.join(LUA_SRC, _NETMSG_DEF)
  85. with open(nmd_path, encoding="utf-8") as f:
  86. lua.execute(f.read())
  87. return lua
  88. def load_game_module(lua: Any, modname: str) -> None:
  89. """按 require 语义加载游戏原版模块(modname 用点号或斜杠均可)。"""
  90. lua.execute(f"require('{modname}')")