Files
mtgodot-poc/docs/PYTHON-EMBED-EVAL.md
T
shenleiandClaude Opus 5 9d0e50de8f docs(parity): mirror 40250 layout for the logic layer; evaluate embedded Python UI
- SKILL.md: logic units port into extension/src/port/<Lib>/<File>, platform
  adapters under extension/src/platform; legacy GDScript owners are migration
  sources; python layer frozen until the embedding decision
- port_map.py: logic/python/platform layers with per-layer progress, queue
  --layer, LEGACY layout check; inventory adds EterBase, EterPythonLib,
  ScriptLib, SpeedTreeLib, SphereLib, EterImageLib
- py_embed_spike.py + docs/PYTHON-EMBED-EVAL.md: the 40250 system.py bootstrap
  loads 67/74 root modules unchanged under CPython 2.7 with stub natives

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 18:06:32 +09:00

83 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 内嵌 Python 运行 40250 UI 的评估
日期:2026-09-22。目的:判断能否不翻译 40250 的 Python UI,而是内嵌解释器原样运行
`Client/Eternexus/root/*.py` 和 `uiscript/`,只移植它调用的 C++ 模块。
## 40250 的实际情况
| 项 | 数据 |
| --- | --- |
| 解释器 | CPython 2.7.6 内嵌(`extern/include/Python-2.7`、`python27.lib`) |
| 启动 | `ScriptLib/PythonLauncher.cpp`:`CPythonLauncher::Create` 注入 `TRUE`/`FALSE` 到 `__builtin__`,然后把 `system.py` 作为 `__main__` 运行;`system.py` 替换 `__import__`,从 pack 读源码并 `compile` |
| 脚本 | `root/*.py` 90 个(其中 74 个 `.py` 模块),`uiscript/uiscript/*.py` 94 个 |
| 语法 | 154 个文件在 Python 2.7.18 下全部能编译;Python 3 下有 56 个编译失败;约 292 行只在 Python 2 下成立(`print` 语句、`except X, e`、`has_key`、`iteritems` 等) |
| 语义风险 | 约 185 行在整数之间用 `/`(Python 2 是整除,Python 3 是浮点);字符串是 CP949 字节串 |
| C++ 暴露的模块 | 35 个(`app` `net` `player` `chr` `chrmgr` `wndMgr` `grp` `item` `skill` 等),约 1160 个函数、约 994 个常量 |
| 需要移植的 C++ | `UserInterface/*Module.cpp` + `EterPythonLib`(窗口系统)+ `ScriptLib`,共约 2.44 万行 |
| 脚本实际引用 | 1627 个不同的 `模块.属性`(含常量) |
对比:如果逐个翻译成 GDScript,需要翻译 `root` 下的 3350 个函数,外加 uiscript 数据文件;而这些 C++
模块在任何方案里都要移植(它们就是 UI 和游戏逻辑的接口)。现在仓库里 `project/ui/*.gd` 有 60 个文件、
2.39 万行,当初依据的是错误版本的脚本(m2dev `assets/root`),无论选哪条路都要重做。
## 原型验证(已完成)
`.agents/skills/metin2-40250-parity-audit/scripts/py_embed_spike.py`,在本机 Python 2.7.18 下:
- 按 `CPythonLauncher::Create` 注入 `TRUE`/`FALSE`,所有 C++ 模块用返回 0 的桩代替,`pack` 用目录代替;
- 原样运行 40250 的 `system.py`,再通过它自己的 `__pack_import` 导入全部 74 个 root 模块。
结果:**不改任何脚本,67/74 个模块加载成功**,引导过程一路执行到 `prototype.py` 的
`app.Create` 等调用。剩下 7 个失败都是桩的问题,不是解释器或脚本的问题:
- `chr.MOTION_SKILL + 121` 这类常量需要真实整数(真实模块用 `PyModule_AddIntConstant` 提供);
- locale 数据路径(`app.GetLocaleServiceName` 等返回值决定走哪个 locale 目录);
- `rootlibcythonizer`、`test_affect` 是开发用脚本,40250 运行时也不加载。
结论:在 Python 2.7 上,40250 的脚本和导入机制可以原封不动运行;工作量集中在 C++ 模块本身。
## 方案对比
| 方案 | 脚本改动 | 与 40250 一致性 | 平台风险 | 结论 |
| --- | --- | --- | --- | --- |
| A. 内嵌 CPython 2.7.18(静态链接) | 无 | 最高:解释器语义、`/` 整除、字节串、C API(`Py_InitModule`、`PyTuple_GetInteger`)都和 40250 相同,`*Module.cpp` 可以几乎原样复制 | 需要自己为 Android/iOS 交叉编译;2.7 已停止维护 | **推荐** |
| B. 内嵌 CPython 3.13+ | 56 个文件要转换,约 185 处 `/` 需逐一判断 | 中:字符串/整除/字典顺序语义不同,转换本身会引入偏差;C API 要改写 | 3.13 起官方支持 Android/iOS | A 失败时的备选 |
| C. pocketpy 等轻量解释器 | 需要大量改动 | 低:只支持 Python 3 子集 | 小 | 不推荐 |
| D. 翻译成 GDScript | 全部重写 3350 个函数 | 取决于翻译质量,难以机械核对 | 无 | 不推荐 |
### 方案 A 的平台问题
- macOS / Linux / Windows:直接编译,无风险。
- Android(arm64,NDK 27):CPython 2.7 能用 NDK 编译(python-for-android、Kivy 曾长期支持),但需要补丁:
关闭 `dlopen` 扩展、把用到的标准库 C 模块静态编进去、`pyconfig.h` 按 NDK 调整。
- iOS:同样静态链接(Kivy-ios 曾支持 2.7)。App Store 允许包内自带的解释型代码(不下载代码即可)。
- 标准库只需要极少一部分:脚本用到的是 `sys` `os` `marshal` `imp` `types` `copy` `math` `traceback`
`_weakref` `__builtin__` `locale`,`psyco` 有 try 保护。可以冻结为内置模块,不需要带整个 `Lib/`。
- 2.7 停止维护的风险:解释器只运行包内自带的脚本;服务器数据先经过 C++ 解析再以普通值交给 Python,
不会执行远端代码。
## 采用方案 A 后的结构
```
extension/src/port/ScriptLib/PythonLauncher.cpp # CPythonLauncher,几乎原样
extension/src/port/EterPythonLib/PythonWindow*.cpp # 窗口树、焦点、命中、事件:逻辑照搬
extension/src/port/UserInterface/*Module.cpp # net/player/chr/... 模块,调用已移植的 CPython* 单例
extension/src/platform/ui/ # 适配层:CGraphicImageInstance/CGraphicTextInstance
# 的绘制 -> Godot RenderingServer/CanvasItem
extension/third_party/cpython-2.7.18/ # 静态库
```
Godot 这边只剩一个宿主 Control:把输入转发给 `CPythonWindowManager`,每帧调用它的 Update/Render。
`root/*.py` 和 `uiscript/` 直接从 pack 读取,和 40250 相同。port-map 中 `Client/root` 的 3350 个函数
全部记为 `N_A`(原样运行,无需移植),python 层剩下约 1540 个 C++ 函数要移植。
## 下一步:原型 2(待确认后执行)
1. 把 CPython 2.7.18 静态编进 `libmtgodot`,先做 macOS arm64 和 Android arm64,再做 iOS 模拟器;
2. 在 Godot 进程里运行 `py_embed_spike.py` 的同等流程(C++ 桩模块),确认 67/74 的结果在三个平台上一致;
3. 验收门槛:Android 和 iOS 都能加载 `system.py` 并导入 `ui.py`。如果 Android/iOS 编译在合理时间内做不出来,
就改用方案 B,并对每处 `/` 做单独核对。
原型 2 通过之前,python 层(`Client/root`、`EterPythonLib`、`ScriptLib`、`*Module.cpp`)不开工;
逻辑层(阶段 2)不受影响,可以并行推进。