docs(port-plan): add 2A/2R/2V prerequisites after plan review
- keep 40250 widths/overflow semantics (Win32 long = 32-bit), fixed-width serialized structs with static_assert, instead of copying C type names - 2A base batch (types, header closure, port_logic target, platform stubs, header gate, port-map re-baseline) and include-order porting - 2R pack inventory: six EPK compression types, HybridCrypt key source, index override order, path case, mobile delivery - 2P: shared source list, per-platform pyconfig.h, all five platforms - 2V minimal vertical slice; old and new paths coexist until wired - strict asset tests (MT_ASSETS vs M2_ASSETS), RUN_AS_IS instead of N_A, void the 6 pre-architecture 'done' functions Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -26,13 +26,14 @@ The 40250 source is the specification. Do not design behavior; transcribe it. Co
|
||||
|
||||
## Code layout: mirror 40250
|
||||
|
||||
The logic layer has the 40250 structure, not a new architecture. `port_map.py` assigns every unit
|
||||
The logic layer has the 40250 structure, not a new architecture. Route, batch order and
|
||||
prerequisites (2A base, 2R pack inventory, 2V vertical slice) are in `docs/PORT-PLAN.md`. `port_map.py` assigns every unit
|
||||
one of three layers:
|
||||
|
||||
| Layer | 40250 units | How to port | Where |
|
||||
| --- | --- | --- | --- |
|
||||
| `logic` | `UserInterface/`, `GameLib/`, `EterLib` net/timer/text parsing, `EterPack`, `EterLocale` (everything not listed below) | Copy C++ -> C++. Same file name, class name, method names, member names and statement order; one reference file = one implementation file | `extension/src/port/<Lib>/<File>.{h,cpp}` |
|
||||
| `python` | `Client/Eternexus/root/*.py`, `uiscript/`, `UserInterface/*Module.cpp`, `EterPythonLib/`, `ScriptLib/` | Being evaluated as embedded CPython 2.7 running the scripts unchanged (`docs/PYTHON-EMBED-EVAL.md`). **Do not port or translate this layer until that evaluation is decided.** Reference is 40250 `Eternexus/root`, **not** `assets/root` | `extension/src/port/<Lib>/` (bindings), scripts from pack |
|
||||
| `python` | `Client/Eternexus/root/*.py`, `uiscript/`, `UserInterface/*Module.cpp`, `EterPythonLib/`, `ScriptLib/` | Decided: embedded CPython 2.7.18 runs the scripts unchanged (`docs/PYTHON-EMBED-EVAL.md`, batch 2P); port only the C++ side. Never translate the scripts. Script functions get `RUN_AS_IS` (after the 2A re-baseline), never `N_A`. Reference is 40250 `Eternexus/root`, **not** `assets/root` | `extension/src/port/<Lib>/` (bindings), scripts from pack |
|
||||
| `platform` | Direct3D/`Grp*`, Granny (`EterGrnLib`), Miles, SpeedTree, `EffectLib`/terrain rendering, Win32 window/input/IME, threads, anti-cheat | Adapter behind the interface the 40250 caller uses; equivalence by observable output | `extension/src/platform/` + existing render code (`metin2_model`, `metin2_anim`, `gr2_bridge`, ...) |
|
||||
|
||||
Rules for the `logic` layer:
|
||||
@@ -43,11 +44,18 @@ Rules for the `logic` layer:
|
||||
- 40250 singletons (`CPythonPlayer::Instance()`, `CPythonCharacterManager`, `CPythonNetworkStream`)
|
||||
stay singletons owned by the extension. GDScript does not hold gameplay state.
|
||||
- GDScript (`net_play.gd`, `net_world.gd`, `game_scene.gd`, `player_controller.gd`) and
|
||||
`extension/src/net/entity_store.cpp` are migration sources. In the commit that ports a unit, delete
|
||||
the old logic it replaces there and leave only glue: node creation, forwarding Godot input to the
|
||||
ported input handlers, and reading ported state to place nodes.
|
||||
- Keep 40250 types and units (`TPixelPosition` in cm, `DWORD` ms from `ELTimer_GetMSec`, degrees).
|
||||
`extension/src/net/entity_store.cpp` are migration sources. Old and new paths coexist behind a
|
||||
switch until the new path is wired into the runtime; delete the old logic in the same commit that
|
||||
switches its callers to the ported code, leaving only glue (node creation, forwarding Godot input,
|
||||
reading ported state to place nodes). A ported function nothing calls at runtime stays `TODO`.
|
||||
- Keep 40250 **widths and overflow semantics**, not its C type names: 40250 is Win32, where `long` and
|
||||
`unsigned long` are 32-bit. Use the fixed-width types from `port/common/Win32Types.h`; every
|
||||
serialized struct (proto records, packets, EPK index, msa/msm) gets 40250's `#pragma pack` and a
|
||||
`static_assert(sizeof(T) == N)`; pointers stored in `DWORD` become `uintptr_t` with a port-map note.
|
||||
- Keep 40250 units (`TPixelPosition` in cm, `DWORD` ms from `ELTimer_GetMSec`, degrees).
|
||||
Convert to Godot space only in the adapter.
|
||||
- Port in `#include`-dependency order: a shared header belongs to the first unit that needs it, and
|
||||
units depending on it start only after it lands. Separate `.cpp` files do not make units independent.
|
||||
- `port_map.py check` prints `LEGACY` for a `logic` function whose `impl` is outside its mirror file.
|
||||
|
||||
Mark each ported function with a one-line tag at its definition:
|
||||
@@ -67,7 +75,8 @@ optional, because the file and method name already map one to one.
|
||||
constants and units, state writes and their order, timing/event source, data source
|
||||
(proto/msa/msm/txt, never hardcoded), packets sent, cleanup. Then:
|
||||
- keep the 40250 version; record each old behavior that differed as a divergence found;
|
||||
- delete the old logic it replaces and rewire callers to the mirror class;
|
||||
- when the ported path is wired into the runtime, delete the old logic it replaces and rewire
|
||||
callers in the same commit (see `docs/PORT-PLAN.md`, "迁移方式");
|
||||
- mark `N_A` only for pure platform plumbing (D3D state, Python binding glue, Win32), with a reason.
|
||||
4. **Test what changed**: a focused test using the reference's own boundary values for each changed
|
||||
formula/branch (fails before, passes after). Do not write tests for unchanged or trivial code.
|
||||
|
||||
@@ -16,16 +16,19 @@ python3 .agents/skills/metin2-40250-parity-audit/scripts/port_map.py queue --lim
|
||||
| --- | --- | --- |
|
||||
| 0 | 工具:参考根解析、缺失文件报错、`port_map.py`、构建/测试入口 | 完成 2026-09-22 |
|
||||
| 1 | 删除只被自身测试引用的 `project/*_system.gd` 与其 `test_*_parity.gd`,清除 manifest 中对应证据 | 完成 2026-09-22(106+106 个文件) |
|
||||
| 2 | P0 角色/移动单元,移植到 `extension/src/port/<Lib>/<File>`(并行 worktree) | 未开始 |
|
||||
| 2P | CPython 2.7.18 静态编入 libmtgodot(macOS → Android APK → iOS),见 `docs/PYTHON-EMBED-EVAL.md` | 未开始(Android 独立程序已验证 b02c49bb) |
|
||||
| 2D | 数据源切到 40250:msm 路径、proto(TEA + `TItemTable`)、EPK 解包比对、切资源根,见 `docs/PORT-PLAN.md` 第 5 节 | 未开始 |
|
||||
| 2A | 基础:Win32 定宽类型层、参考公共头最小闭包、`port_logic` CMake、platform 接口骨架、头文件可编译门禁、port-map 重新基线 | 下一步 |
|
||||
| 2R | 资源包盘点:EPK 压缩/加密类型、密钥来源、覆盖顺序、路径大小写、移动端交付 | 下一步(可与 2A 并行) |
|
||||
| 2P | CPython 2.7.18 编进 libmtgodot,五个平台各自 `pyconfig.h` 并分别验证 | 2A 之后(Android 独立程序已验证 b02c49bb) |
|
||||
| 2D | 数据源切到 40250:msm 路径、proto(TEA + `TItemTable`)、严格资源测试、资源根 | proto 在 2A 之后,资源根在 2R 之后 |
|
||||
| 2V | 最小纵向切片:PythonLauncher → system.py → wndMgr → 登录/选角 → GamePhase → 本地移动 | 需要 2A、2P、2D |
|
||||
| 2 | 其余 P0 角色/移动单元,按 `#include` 依赖拓扑移植 | 2V 之后 |
|
||||
| 3 | P1 战斗/技能 → P2 游戏阶段封包 → P3 物品;Python 层按 2P 的结论进行 | 未开始 |
|
||||
| 4 | `NEEDS_LIVE` 真服验证批 | 未开始 |
|
||||
|
||||
## 批次 2 单元(互不共享实现文件的可并行)
|
||||
## 批次 2 单元(顺序由 2A 的依赖图重新生成;下表仅为候选)
|
||||
|
||||
实现位置一律是镜像文件 `extension/src/port/<Lib>/<File>.{h,cpp}`;下面列出的是需要删减旧逻辑的迁移来源。
|
||||
第一个单元负责建立 `extension/src/port/` 与 `extension/src/platform/` 目录和 CMake 目标。
|
||||
目录、CMake 目标和公共头由 2A 建立。
|
||||
|
||||
- [ ] `UserInterface/PythonPlayerEventHandler.cpp`(OnMove/OnMoving/OnStop 已在 `net_play.gd` 对齐,需迁到镜像文件;OnWaiting DIVERGENT)— `net_play.gd`
|
||||
- [ ] `UserInterface/InstanceBaseMovement.cpp` + `UserInterface/InstanceBase.cpp`(同一 owner,串行)— `player_controller.gd`、`entity_store.cpp`
|
||||
|
||||
+91
-37
@@ -26,6 +26,8 @@ metin2-client 是 40250 Windows 客户端的跨平台版本(macOS / Windows /
|
||||
| 2026-09-22 | **UI 走内嵌 CPython 2.7.18**(方案 A),原样运行 40250 的 `root/*.py` 和 `uiscript/`,只移植 C++ 模块 | 脚本无需改动;macOS 和 Android 真机均已验证,见 `docs/PYTHON-EMBED-EVAL.md` |
|
||||
| 2026-09-22 | 数据源统一改为 40250(`Client/Eternexus` 的 root/uiscript/locale,以及 `Client/pack` 的资源),**不再用**仓库里 m2dev 版本的 `assets/root`、`assets/uiscript`、`assets/locale` | 两者有实质差异,见第 5 节 |
|
||||
| 2026-09-22 | 现有 `project/ui/*.gd`(60 个文件,依据的是错误版本的脚本)不再维护,由 Python 层取代 | — |
|
||||
| 2026-09-22 | 照抄时保持 40250 的**宽度和溢出语义**(Win32 下 `long` 为 32 位),序列化结构用定宽类型并 `static_assert` 大小 | 64 位平台 `long` 是 8 字节,机械照抄会破坏 proto/封包/EPK 布局和 TEA |
|
||||
| 2026-09-22 | 迁移期新旧路径共存,新路径接通运行后才在同一提交删除旧逻辑;先做最小纵向切片 2V | `main` 始终可玩;运行时不可达的新代码不算实现 |
|
||||
|
||||
## 3. 三个层和代码位置
|
||||
|
||||
@@ -49,8 +51,16 @@ extension/third_party/cpython-2.7.18/ # 静态库(2P 批次
|
||||
|
||||
- 40250 调用平台类的地方,调用 `extension/src/platform/` 下同名的适配接口,ported 代码里不直接写 Godot 调用。
|
||||
- 40250 的单例(`CPythonPlayer`、`CPythonCharacterManager`、`CPythonNetworkStream`)归扩展所有,GDScript 不持有玩法状态。
|
||||
- **保持 40250 的宽度和溢出语义,不是机械保留 C++ 类型名。** 40250 是 Win32(ILP32/LLP64):`long`/`unsigned long`
|
||||
是 32 位,指针是 32 位。64 位的 macOS/Linux/Android/iOS 上 `long` 是 64 位,照抄会改变结构大小、TEA 的读取宽度和
|
||||
溢出行为。统一由 `port/common/Win32Types.h`(批次 2A)把 `LONG`/`DWORD`/`long` 等映射到 `int32_t`/`uint32_t`:
|
||||
- 凡是参与序列化的结构(proto 记录、封包、EPK 索引、msa/msm 二进制)一律用定宽类型,`#pragma pack` 与 40250 相同,
|
||||
并对 40250 已知大小加 `static_assert(sizeof(...) == N)`(如 `TItemTable` == 156);
|
||||
- 纯计算里的 `long` 如依赖 32 位回绕,也换成 `int32_t`;
|
||||
- 指针存进 `DWORD` 的写法(句柄、`SetUserData` 等)改成 `uintptr_t`,并在 port-map 的 `note` 里记下。
|
||||
- 单位保持 40250 的(`TPixelPosition` 用 cm,时间是 `ELTimer_GetMSec` 的毫秒,角度用度),只在适配层换算。
|
||||
- `net_play.gd`、`net_world.gd`、`game_scene.gd`、`player_controller.gd`、`entity_store.cpp` 是迁移来源:移植某个单元的那次提交,同时删掉它们里面被取代的逻辑,只留胶水(建节点、转发输入、读状态摆节点)。
|
||||
- 迁移来源:`net_play.gd`、`net_world.gd`、`game_scene.gd`、`player_controller.gd`、`entity_store.cpp`。**旧逻辑只在新路径
|
||||
已接通运行时之后才删除**(见第 4 节"迁移方式"),删除与接通在同一次提交完成。
|
||||
- 迁移结束后 Godot 这边只剩:场景节点、渲染适配、一个把输入转发给 `CPythonWindowManager` 并每帧调用 Update/Render 的宿主 Control。
|
||||
|
||||
## 4. 路线
|
||||
@@ -59,35 +69,72 @@ extension/third_party/cpython-2.7.18/ # 静态库(2P 批次
|
||||
| --- | --- | --- |
|
||||
| 0 | 工具:参考根解析、`port_map.py`、构建/测试入口 | 完成(55f733fd、5afd5a4f) |
|
||||
| 1 | 删除运行时不加载的 106 个 `*_system.gd` 及其测试 | 完成(a989b8f1) |
|
||||
| — | 定下目录结构,评估内嵌 Python | 完成(9d0e50de) |
|
||||
| — | 内嵌 Python Android 真机验证 | 完成(b02c49bb) |
|
||||
| 2 | P0 角色/移动单元照抄到镜像文件;第一个单元负责建立 `port/`、`platform/` 和 CMake 目标 | **下一步,未开始** |
|
||||
| 2P | CPython 2.7.18 编进 libmtgodot:macOS → Android APK → iOS | **下一步,未开始**,可与 2 并行 |
|
||||
| 2D | 数据源切到 40250(第 5 节的 4 步) | **下一步,未开始**,可与 2、2P 并行;2P 跑 40250 脚本前必须完成 |
|
||||
| 3 | P1 战斗/技能 → P2 游戏阶段封包 → P3 物品;Python 层(`PythonLauncher` → 窗口系统 → `*Module.cpp`) | 未开始 |
|
||||
| — | 定下目录结构,评估内嵌 Python;Android 独立程序验证 | 完成(9d0e50de、b02c49bb) |
|
||||
| **2A** | 基础:Win32 类型层、参考公共头的最小闭包、`port_logic` CMake 目标、platform 接口骨架、头文件可编译门禁、port-map 重新基线 | **下一步** |
|
||||
| **2R** | 资源包能力盘点:EPK 类型/密钥/覆盖顺序/路径大小写/移动端交付 | **下一步**,可与 2A 并行(只读分析 + 独立工具) |
|
||||
| 2P | CPython 2.7.18 编进 libmtgodot,五个平台分别配置和验证 | 2A 之后 |
|
||||
| 2D | 数据源切到 40250(msm 路径、proto、资源根、严格资源测试) | proto 部分在 2A 之后;资源根在 2R 之后 |
|
||||
| **2V** | 最小纵向切片:`CPythonLauncher` → `system.py` → `wndMgr` → 登录/选角 → GamePhase → 本地角色移动 | 需要 2A、2P、2D;**这是第一个可运行的新架构里程碑** |
|
||||
| 2 | 其余 P0 角色/移动单元,按依赖拓扑移植 | 2V 之后 |
|
||||
| 3 | P1 战斗/技能 → P2 游戏阶段封包 → P3 物品;Python 层(窗口系统 → `*Module.cpp`) | 未开始 |
|
||||
| 4 | `NEEDS_LIVE` 真服验证 | 未开始 |
|
||||
|
||||
### 批次 2:P0 单元(每个单元一个 worktree,互不共享实现文件的可并行)
|
||||
### 迁移方式:始终保持可玩
|
||||
|
||||
1. `UserInterface/PythonPlayerEventHandler.cpp`:OnMove/OnMoving/OnStop 已在 `net_play.gd` 对齐,需迁到镜像文件;OnWaiting 标为 DIVERGENT
|
||||
2. `UserInterface/InstanceBaseMovement.cpp` + `InstanceBase.cpp`(串行)
|
||||
3. `UserInterface/PythonPlayerInput*.cpp`(排在第 2 项之后)
|
||||
4. `GameLib/ActorInstanceMotion.cpp` + `ActorInstanceEvent.cpp`
|
||||
5. `GameLib/ActorInstance.cpp` + `Position`/`Rotation`/`CollisionDetection`
|
||||
6. `UserInterface/PythonCharacterManager.cpp`
|
||||
7. `UserInterface/PythonPlayer.cpp`
|
||||
旧 GDScript 路径和新的 ported 路径在切片接通前共存,由构建/运行开关选择(例如 `MT_PORT_PATH=legacy|port`,默认
|
||||
`legacy`)。规则:
|
||||
|
||||
每轮的步骤(详见 SKILL.md):`port_map.py init/show` → 通读参考文件 → 照抄到镜像文件,逐函数与旧实现对比、
|
||||
记录差异 → 删旧逻辑、改调用方 → 只为改动的公式/分支写测试 → 更新 port-map 和 history → 一个单元一次提交。
|
||||
- 新单元先编译进 `port_logic`,但**只有被新路径的运行时调用到**,才算实现("运行时不可达不算实现"同样适用于新代码);
|
||||
没有接通前,port-map 状态保持 `TODO`,`note` 写"已照抄,未接通"。
|
||||
- 每个接通步骤的提交同时:切换调用方到新路径、删除被取代的旧逻辑、跑一遍能覆盖该路径的运行测试(离线 FakeClient
|
||||
或真服 e2e)。
|
||||
- `main` 在任意提交上都能进游戏走动;`legacy` 路径在 2V 完成、默认值切到 `port` 并稳定后整体删除。
|
||||
|
||||
### 批次 2A:基础
|
||||
|
||||
1. `extension/src/port/common/`:`Win32Types.h`(`BYTE`/`WORD`/`DWORD`/`LONG`/`BOOL`/`UINT`/`HANDLE` 等的定宽映射)、
|
||||
40250 用到的 Win32/CRT 宏和函数(`ZeroMemory`、`_snprintf`、`stricmp`、`timeGetTime` 等)的最小实现、
|
||||
`StdAfx.h` 等价物。
|
||||
2. 参考公共头的最小闭包:从第一批要移植的单元出发(`PythonPlayerEventHandler.h` 依赖 `ActorInstance.h`、`FlyHandler.h`、
|
||||
`PythonNetworkStream.h`、`InstanceBase.h`),用脚本列出 `#include` 闭包,把闭包里的头文件先照抄为可编译的声明。
|
||||
3. `port_logic` 静态库 CMake 目标,链接进 `libmtgodot`;在 macOS 和 Android 两个工具链上编译。
|
||||
4. `extension/src/platform/` 接口骨架:闭包里出现的平台类(`CGraphicThingInstance`、`CSoundManager` 等)只声明
|
||||
40250 调用方用到的方法,先给空实现。
|
||||
5. 门禁:`port/**` 下每个头文件单独编译通过(header self-containment),序列化结构的 `static_assert` 全部通过。
|
||||
6. port-map 重新基线(见第 6 节)。
|
||||
7. 用 `#include` 依赖图生成批次 2 的移植顺序(拓扑序),替换原来"互不共享实现文件即可并行"的假设:共享头文件的单元,
|
||||
头文件由先做的那个单元负责,后面的单元只能在它合入后开始。
|
||||
|
||||
### 批次 2R:资源包能力盘点
|
||||
|
||||
40250 的读取链不只是 `EterPack.cpp`,还有 `EterPackManager.cpp`(多包覆盖顺序、路径归一化)、
|
||||
`EterPackPolicy_CSHybridCrypt.cpp`(HybridCrypt,密钥可能来自登录/握手阶段的服务器下发)、`CMappedFile`、`CLZO`,
|
||||
以及 TEA/Panama/Camellia/Twofish/XTEA。压缩类型有六种:`NONE`、`COMPRESS`、`SECURITY`、`PANAMA`、
|
||||
`HYBRIDCRYPT`、`HYBRIDCRYPT_WITHSDB`。`Client/pack` 有 217 个文件、约 1.3 GB。
|
||||
|
||||
1. 写一个只读的扫描工具(Python,放在 `tools/`),解析所有 `.eix`,统计每个包、每种 `compressed_type` 的文件数和字节数。
|
||||
2. 按统计结果决定:
|
||||
- 只有 `NONE/COMPRESS/SECURITY` 的包可以离线解出;
|
||||
- `PANAMA`/`HYBRIDCRYPT*` 如存在,确认密钥来源:本地的 `Index`/配置,还是服务器 `GC_HYBRIDCRYPT_KEYS`/`SDB` 下发。需要服务器密钥的包,要么移植密钥链,要么在开发期用一次真服登录抓取密钥后离线解包(密钥不进仓库)。
|
||||
3. 验证 `Index` 文件列出的包顺序和同名文件的覆盖优先级,与 `CEterPackManager` 一致。
|
||||
4. 路径归一化:40250 在 Windows 上大小写不敏感,并把 `d:/ymir work/` 等前缀映射到包内路径。确定统一的小写化规则,扫描大小写冲突。
|
||||
5. 移动端交付:决定最终形式(例如解包后重新打成我们自己的 `mtpack`,或直接在设备上读 EPK),给出 Android/iOS 的包体积、首包与按需下载的划分,以及更新方式。`MT_ASSETS` 只是开发期覆盖。
|
||||
6. 输出写进本文第 5 节,再决定 2D 的资源根切换方式。
|
||||
|
||||
### 批次 2P:内嵌 Python 集成
|
||||
|
||||
1. 把 CPython 2.7.18 源码放进 `extension/third_party/cpython-2.7.18/`,用我们自己的 CMake 编译。macOS/Android/iOS
|
||||
共用一份源码清单和 `pyconfig.h`(Android 需关掉 `HAVE_LANGINFO_H`),C 模块清单见 `tools/py_embed_android/build-and-run.sh`
|
||||
2. 静态链接进 `libmtgodot`,照抄 `ScriptLib/PythonLauncher.cpp`
|
||||
3. `pack` 模块通过 `asset_io` 读取,标准库的纯 Python 文件放进资源包,由 `system.py` 自带的导入钩子加载
|
||||
4. 先用桩 C++ 模块在 APK 进程里跑通 `system.py` → `prototype.RunApp()`(对照结果:74 个模块中 66 个加载成功,引导期调用 33 个 C++ 函数)
|
||||
5. 同样的流程在 iOS 上跑一遍
|
||||
1. CPython 2.7.18 源码放进 `extension/third_party/cpython-2.7.18/`。**共用源码清单和静态模块清单**(`Modules/Setup` 中启用的 C 模块,见 `tools/py_embed_android/build-and-run.sh`);**每个平台各自一份 `pyconfig.h`**(由该平台的 configure 生成后提交,或 Windows 用 `PC/pyconfig.h`),因为它是对目标平台类型大小和系统 API 的探测结果,不能共用。已知平台差异:Android API 24 需关掉 `HAVE_LANGINFO_H`。
|
||||
2. 静态链接进 `libmtgodot`,照抄 `ScriptLib/PythonLauncher.cpp`。
|
||||
3. `pack` 模块通过 `asset_io` 读取,标准库的纯 Python 文件放进资源包,由 `system.py` 自带的导入钩子加载。
|
||||
4. 五个平台分别验证:编译、链接无未定义符号、`Py_Initialize`、静态 C 模块逐个 `import`、在 app 进程里跑 `system.py` → `prototype.RunApp()`(对照结果:74 个模块中 66 个加载成功,引导期调用 33 个 C++ 函数)。
|
||||
|
||||
| 平台 | 状态 |
|
||||
| --- | --- |
|
||||
| macOS arm64 | 系统 Python 2.7.18 跑通 spike;静态嵌入未做 |
|
||||
| Android arm64 | 独立可执行文件在真机跑通(b02c49bb);APK 进程内未做 |
|
||||
| iOS arm64 | 未做 |
|
||||
| Linux x86_64 | 未做 |
|
||||
| Windows x64 | 未做(用 `PCbuild` 的源码清单和 `PC/pyconfig.h`,不走 configure) |
|
||||
|
||||
## 5. 数据源切换:m2dev assets → 40250
|
||||
|
||||
@@ -101,28 +148,35 @@ extension/third_party/cpython-2.7.18/ # 静态库(2P 批次
|
||||
| `playersettingmodule.py` 的特效注册 | 暂无 | 40250 多了 `EFFECT_LEVELUP_*_FOR_GERMANY`、`EFFECT_EMPIRE+1..3`,少了 `EFFECT_AGGREGATE_MONSTER` | 移植特效时按 40250 |
|
||||
| `atlasinfo.txt`、`grpblk.txt` | — | 相同 | 无 |
|
||||
| `uiscript/uiscript` | 现有 `project/ui/*.gd` | 40250 有 94 个,m2dev 有 80 个;共有的 71 个中,去掉空白差异后有 11 个不同(建角/选角/密码/信使/公会等) | 由 2P 直接运行 40250 的 uiscript,不单独处理 |
|
||||
| `item_proto` | `proto.cpp` | **读不了**:40250 每条 156 字节(`TItemTable`),m2dev 236 字节;40250 用经典 TEA(32 轮,`0x9E3779B9`),m2dev 用 XChaCha20 | 按 40250 移植 |
|
||||
| `mob_proto` | `proto.cpp` | **读不了**:每条 255 字节 vs 335 字节,加密同上 | 按 40250 移植 |
|
||||
| `item_proto` | `proto.cpp` | **读不了**:40250 每条 156 字节(`TItemTable`,含 `long` 字段,需按 32 位布局),m2dev 236 字节;40250 用经典 TEA(32 轮,`0x9E3779B9`,按 32 位字处理),m2dev 用 XChaCha20 | 按 40250 移植(需 2A 的类型层) |
|
||||
| `mob_proto` | `proto.cpp` | **读不了**:每条 255 字节 vs 335 字节,加密同上 | 同上 |
|
||||
| `item_list.txt` | `item_list.gd` | 177 行不同(m2dev 有 dummy 条目等);40250 放在 `locale/en/`,m2dev 在 `locale/common/` | 按 40250 路径读取 |
|
||||
| `locale_interface.txt`、`itemdesc.txt`、`skilldesc.txt`、`skilltable.txt` | UI、技能表 | 20 行 / 2 行 / 2 行 / 位置不同 | 按 40250 路径读取 |
|
||||
| 模型、贴图、地图(PC、Monster、Outdoor 等) | 渲染 | **未核对**:40250 是经典 `.eix/.epk` 包 | 见第 3 步 |
|
||||
| 模型、贴图、地图(PC、Monster、Outdoor 等) | 渲染 | **未核对**:40250 是经典 `.eix/.epk` 包 | 由 2R 决定 |
|
||||
|
||||
切换步骤(批次 2D):
|
||||
|
||||
1. msm 查找路径加上 `root/<cls>_<m/w>.msm`。
|
||||
2. proto 读取按 40250 移植:`EterBase/tea.cpp`、`GameLib/ItemData.h` 的 `TItemTable`、`CPythonNonPlayer` 的 mob 表,放进镜像文件,替换 `extension/src/proto/proto.cpp` 里的 m2dev 格式。
|
||||
3. 移植 40250 的 EPK 读取(`EterPack/EterPack.cpp`),解出 `Client/pack`,与 `assets/` 抽样比对模型/贴图/地图;有差异的以 40250 为准。
|
||||
4. 资源根切到 40250 数据(`MT_ASSETS` 指向解出的目录),跑渲染和解析测试,更新本节状态。
|
||||
2. proto 读取按 40250 移植:`EterBase/tea.cpp`、`GameLib/ItemData.h` 的 `TItemTable`、`CPythonNonPlayer` 的 mob 表,放进镜像文件(使用 2A 的定宽类型和 `static_assert`),替换 `extension/src/proto/proto.cpp` 里的 m2dev 格式。
|
||||
3. 资源测试严格模式:
|
||||
- 统一资源环境变量:C++ 测试(`extension/CMakeLists.txt` 目前只透传 `M2_ASSETS`)和 GDScript(`MT_ASSETS`)统一读 `MT_ASSETS`,过渡期两者都透传;
|
||||
- 新增 `MT_ASSETS_STRICT=1`:指定了 40250 资源时,缺文件必须失败,不能跳过;
|
||||
- 为 40250 的 root、locale、proto 和关键包生成来源清单(路径 + sha256),提交到 `audit/`,测试开始时核对。
|
||||
4. 资源根切换方式按 2R 的结论执行,跑渲染和解析测试,更新本节状态。
|
||||
|
||||
## 6. 当前进度
|
||||
## 6. 进度与 port-map 基线
|
||||
|
||||
实时数字用 `port_map.py status` 查看。2026-09-22 的状态:
|
||||
实时数字用 `port_map.py status` 查看。
|
||||
|
||||
| 层 | 函数数 | 已完成 |
|
||||
| --- | --- | --- |
|
||||
| logic | 3285 | 6 |
|
||||
| python | 4890 | 0(其中 `Client/root` 的 3350 个在 2P 完成后记为 `N_A`,属于原样运行) |
|
||||
| platform | 2229 | 0 |
|
||||
2A 时做一次重新基线,之后的数字才代表新架构的进度:
|
||||
|
||||
- 之前"完成"的 6 个函数(`PythonPlayerEventHandler.cpp`)作废:OnMove/OnMoving/OnStop 的实现在 `net_play.gd`,属于迁移来源;
|
||||
3 个 `N_A`(单例、构造、析构,理由是"由 NetPlay 场景节点持有")与"单例归扩展所有"冲突。全部回退为 `TODO`。
|
||||
- 原样运行的 Python 脚本函数**不标 `N_A`**(`N_A` 只用于没有玩法语义的平台胶水)。新增状态 `RUN_AS_IS`:
|
||||
`impl` 指向随包运行的原脚本,`evidence` 要求该函数所在模块在目标平台的运行证据(导入成功且被运行路径调用到)。
|
||||
需要同步修改 `port_map.py` 和 `references/audit-schema.md`。
|
||||
|
||||
2026-09-22 基线前的数字(仅供参考):logic 3285 个函数、python 4890、platform 2229,完成数视为 0。
|
||||
|
||||
## 7. 换机器后的环境准备
|
||||
|
||||
@@ -155,4 +209,4 @@ python2.7 .agents/skills/metin2-40250-parity-audit/scripts/py_embed_spike.py --o
|
||||
tools/py_embed_android/build-and-run.sh # 可选:连接 Android 真机复核
|
||||
```
|
||||
|
||||
然后按第 4 节的"下一步"继续,一个单元完成后在第 4、6 节更新状态并提交。
|
||||
然后按第 4 节标为"下一步"的批次继续,每完成一步在第 4 节(及 2P 平台表、第 5 节)更新状态并提交。
|
||||
|
||||
Reference in New Issue
Block a user