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>
This commit is contained in:
shenlei
2026-09-22 18:06:32 +09:00
co-authored by Claude Opus 5
parent a989b8f19b
commit 9d0e50de8f
7 changed files with 285 additions and 37 deletions
+82
View File
@@ -0,0 +1,82 @@
# 内嵌 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:直接编译,无风险。
- Androidarm64NDK 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)不受影响,可以并行推进。