layout: restructure into src/ tests/ android/ scripts/ tools/
- extension/src/{port,platform,codepage} -> src/; native_render -> src/host
(+ dxt, shaders/); libgr2 -> src/gr2; extension/third_party -> third_party
- extension/tests -> tests/port, libgr2/tests -> tests/gr2
- android-native -> android (build.sh, push-client.sh moved in)
- script -> scripts; tools/40250 -> tools/server; oracle -> tools/granny-oracle;
perf tools -> tools/perf
- all build trees under build/ (native, release, android, port-gate)
- xrender:: CMake aliases -> mt::; port-map ledger impl paths rewritten
No code changes. ctest 15/15, port_gate macos+android PASS, port_map check 0 errors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
70710477cf
commit
a46093104c
+33
-33
@@ -2,15 +2,15 @@
|
||||
|
||||
本文是移植工作的总入口,换一台电脑继续开发时先读这份。更新日期:2026-09-29。
|
||||
|
||||
**当前架构**:原生 SDL3 + Vulkan 客户端(`native_render/`),macOS 上经 MoltenVK 走 Metal,Android 上是 SDLActivity
|
||||
外壳加原生 `libmain.so`。40250 的 C++ 逻辑逐文件照抄在 `extension/src/port/`,平台类实现在 `extension/src/platform/`,
|
||||
**当前架构**:原生 SDL3 + Vulkan 客户端(`src/host/`),macOS 上经 MoltenVK 走 Metal,Android 上是 SDLActivity
|
||||
外壳加原生 `libmain.so`。40250 的 C++ 逻辑逐文件照抄在 `src/port/`,平台类实现在 `src/platform/`,
|
||||
40250 的 `root/*.py`、`uiscript/` 由内嵌 CPython 2.7.18 原样运行,资源直接读 40250 的 EPK。运行时不依赖 Godot;
|
||||
Godot 时期(2026-09-22 至 09-27)的批次记录已移到 `docs/archive/PORT-HISTORY.md`。
|
||||
|
||||
- 方法细则:`.agents/skills/metin2-40250-parity-audit/SKILL.md`(每轮的做法、状态定义、工具)
|
||||
- 批次队列:`audit/remediation-roadmap.md`
|
||||
- 逐函数进度:`audit/port-map/**`;每轮一行记录:`audit/history.jsonl`
|
||||
- 构建与启动:根目录 `README.md`、`native_render/README.md`;Android 真机测试:`docs/ANDROID-TESTING.md`
|
||||
- 构建与启动:根目录 `README.md`、`src/host/README.md`;Android 真机测试:`docs/ANDROID-TESTING.md`
|
||||
- 渲染差距与门禁:`docs/PARITY-GAP.md`;中文语言包:`docs/ZH-LOCALE.md`;移动端 UI:`docs/MOBILE-UI-IMPLEMENTATION.md`
|
||||
- Python UI 评估:`docs/PYTHON-EMBED-EVAL.md`
|
||||
- 旧方案 `docs/CLIENT-REFACTOR-40250-PLAN.md`("形式上不照搬类文件")已被本文取代
|
||||
@@ -19,7 +19,7 @@ Godot 时期(2026-09-22 至 09-27)的批次记录已移到 `docs/archive/POR
|
||||
|
||||
metin2-client 是 40250 Windows 客户端的跨平台版本。**首要目标是 Android arm64 原生客户端**(2026-09-28 定),
|
||||
**macOS arm64 是开发和验收平台**;iOS arm64 保持交叉编译干净,之后再做。**Linux 和 Windows 不做**——不验收、
|
||||
不作为任何步骤的阻塞项。`script/port_gate.sh` 里的 mingw-w64 / Linux 交叉编译只留作可移植性门禁,不代表支持这些平台。
|
||||
不作为任何步骤的阻塞项。`scripts/port_gate.sh` 里的 mingw-w64 / Linux 交叉编译只留作可移植性门禁,不代表支持这些平台。
|
||||
|
||||
除渲染和平台 API 外,所有玩法算法、分支、常量、状态顺序、计时来源、数据来源、封包副作用和清理路径都以 40250
|
||||
为准。**40250 源码就是规格,不设计行为,只照抄。**
|
||||
@@ -38,7 +38,7 @@ metin2-client 是 40250 Windows 客户端的跨平台版本。**首要目标是
|
||||
| 2026-09-22 | 数据源统一为 40250(`Client/pack` 的 EPK 和 `Client/Eternexus` 的 root/uiscript/locale),不用 m2dev 资源 | 两者有实质差异,见 `docs/archive/PORT-HISTORY.md` |
|
||||
| 2026-09-22 | 照抄时保持 40250 的**宽度和溢出语义**(Win32 下 `long` 为 32 位),序列化结构用定宽类型并 `static_assert` 大小 | 64 位平台 `long` 是 8 字节,机械照抄会破坏 proto/封包/EPK 布局和 TEA |
|
||||
| 2026-09-23 | 网络层用 40250 经典协议(1 字节包头 + 包长表 + 序列字节 + Crypto++ DH2/CTR),m2dev 分支协议放弃 | 真服 192.168.21.203 跑的是 40250 服务端 |
|
||||
| 2026-09-27/28 | **宿主换成原生 SDL3 + Vulkan**(`native_render/`),Godot 工程、GDExtension、`project/*.gd` 全部删除 | D3D8 固定管线按 40250 语义直译比经 Godot 适配更接近原版;去掉一层引擎,移动端体积和功耗更可控 |
|
||||
| 2026-09-27/28 | **宿主换成原生 SDL3 + Vulkan**(`src/host/`),Godot 工程、GDExtension、`project/*.gd` 全部删除 | D3D8 固定管线按 40250 语义直译比经 Godot 适配更接近原版;去掉一层引擎,移动端体积和功耗更可控 |
|
||||
| 2026-09-28 | Android 原生客户端优先,iOS 延后 | 目标用户在手机上 |
|
||||
| 2026-09-28 | 代码页转换用内置 WindowsBestFit 表(`mt_codepage`),不再用 iconv | Android API 24 没有可用的 iconv;CP949/GBK 结果与 Windows 一致 |
|
||||
| 2026-09-29 | 移动端专有功能(触控、自动狩猎、账号注册、帧率设置)以 PORT 模块形式注入,不改 40250 脚本 | 40250 脚本字节保持不变,账本 `RUN_AS_IS` 仍成立 |
|
||||
@@ -47,30 +47,30 @@ metin2-client 是 40250 Windows 客户端的跨平台版本。**首要目标是
|
||||
|
||||
| 层 | 40250 单元 | 做法 | 位置 |
|
||||
| --- | --- | --- | --- |
|
||||
| `logic` | `UserInterface/`、`GameLib/`、`EterLib` 的网络/计时/文本解析、`EterPack`、`EterLocale`、`EterBase` 的纯逻辑单元(`tea`、`lzo`、`cipher`、`Random`、`Stl`、`Timer`、`Poly/`) | 逐文件照抄 C++ | `extension/src/port/<Lib>/<File>.{h,cpp}` |
|
||||
| `python` | `root/*.py`、`uiscript/`、`UserInterface/*Module.cpp`、`EterPythonLib/`、`ScriptLib/` | 脚本原样运行;`*Module.cpp`、窗口系统、`CPythonLauncher` 照抄 | `extension/src/port/<Lib>/` + `extension/third_party/cpython-2.7.18/` |
|
||||
| `platform` | Direct3D/`Grp*`、Granny、Miles、SpeedTree、特效/地形渲染、Win32 窗口/输入/IME、线程、反作弊、`EterBase` 的文件/OS 单元(`CPostIt`、`CRC32`、`Debug`、`FileBase`、`FileDir`、`FileLoader`、`MappedFile`、`TempFile`、`Utils`、`error`;`CRC32`、`Utils` 里的纯函数在 platform 实现里照抄) | 适配层,接口与 40250 调用方看到的一致,按可观察输出核对 | `extension/src/platform/` + `native_render/` |
|
||||
| `logic` | `UserInterface/`、`GameLib/`、`EterLib` 的网络/计时/文本解析、`EterPack`、`EterLocale`、`EterBase` 的纯逻辑单元(`tea`、`lzo`、`cipher`、`Random`、`Stl`、`Timer`、`Poly/`) | 逐文件照抄 C++ | `src/port/<Lib>/<File>.{h,cpp}` |
|
||||
| `python` | `root/*.py`、`uiscript/`、`UserInterface/*Module.cpp`、`EterPythonLib/`、`ScriptLib/` | 脚本原样运行;`*Module.cpp`、窗口系统、`CPythonLauncher` 照抄 | `src/port/<Lib>/` + `third_party/cpython-2.7.18/` |
|
||||
| `platform` | Direct3D/`Grp*`、Granny、Miles、SpeedTree、特效/地形渲染、Win32 窗口/输入/IME、线程、反作弊、`EterBase` 的文件/OS 单元(`CPostIt`、`CRC32`、`Debug`、`FileBase`、`FileDir`、`FileLoader`、`MappedFile`、`TempFile`、`Utils`、`error`;`CRC32`、`Utils` 里的纯函数在 platform 实现里照抄) | 适配层,接口与 40250 调用方看到的一致,按可观察输出核对 | `src/platform/` + `src/host/` |
|
||||
|
||||
```
|
||||
extension/src/port/UserInterface/InstanceBase.cpp # 40250 同名文件的照抄
|
||||
extension/src/port/GameLib/ActorInstance.cpp
|
||||
extension/src/port/ScriptLib/PythonLauncher.cpp
|
||||
extension/src/port/EterPythonLib/PythonWindow*.cpp
|
||||
extension/src/port/UserInterface/*Module.cpp # app/net/player/chr/... Python 模块
|
||||
extension/src/port/EterLib/GrpBase.h # 平台类的 40250 头文件同样照抄在镜像路径(接口)
|
||||
extension/src/platform/ # 这些平台类的实现(CGraphicThingInstance、CSoundManager 等)
|
||||
extension/src/platform/EterLib/RecordingDevice.cpp # IDirect3DDevice8 的记录实现:把 D3D8 调用录成绘制命令
|
||||
extension/src/platform/ScriptLib/PythonBoot.cpp # 解释器启动、RunMainScript、脚本线程、PORT 模块注入
|
||||
extension/third_party/cpython-2.7.18/ # 静态库
|
||||
native_render/main.cpp # SDL3 宿主:窗口/输入/触控、VulkanWindow 渲染器、帧率策略
|
||||
native_render/android_perf.h # Android 帧率、ADPF、电量/温度(JNI 到 MainActivity)
|
||||
android-native/ # Gradle 工程:SDLActivity 外壳 MainActivity、APK 打包
|
||||
src/port/UserInterface/InstanceBase.cpp # 40250 同名文件的照抄
|
||||
src/port/GameLib/ActorInstance.cpp
|
||||
src/port/ScriptLib/PythonLauncher.cpp
|
||||
src/port/EterPythonLib/PythonWindow*.cpp
|
||||
src/port/UserInterface/*Module.cpp # app/net/player/chr/... Python 模块
|
||||
src/port/EterLib/GrpBase.h # 平台类的 40250 头文件同样照抄在镜像路径(接口)
|
||||
src/platform/ # 这些平台类的实现(CGraphicThingInstance、CSoundManager 等)
|
||||
src/platform/EterLib/RecordingDevice.cpp # IDirect3DDevice8 的记录实现:把 D3D8 调用录成绘制命令
|
||||
src/platform/ScriptLib/PythonBoot.cpp # 解释器启动、RunMainScript、脚本线程、PORT 模块注入
|
||||
third_party/cpython-2.7.18/ # 静态库
|
||||
src/host/main.cpp # SDL3 宿主:窗口/输入/触控、VulkanWindow 渲染器、帧率策略
|
||||
src/host/android_perf.h # Android 帧率、ADPF、电量/温度(JNI 到 MainActivity)
|
||||
android/ # Gradle 工程:SDLActivity 外壳 MainActivity、APK 打包
|
||||
```
|
||||
|
||||
运行结构(每帧):
|
||||
|
||||
```
|
||||
SDL3 宿主主循环(native_render/main.cpp run_live_client)
|
||||
SDL3 宿主主循环(src/host/main.cpp run_live_client)
|
||||
├─ SDL 事件 → 40250 的窗口消息/输入接口(鼠标、键盘、IME 文本、触控摇杆/按钮)
|
||||
├─ PythonBoot::AppFrame():交给脚本线程跑一次 CPythonApplication::Process()(宿主等待,两者不并发)
|
||||
│ └─ system.py / app.Loop() → UpdateGame / RenderGame / 窗口系统 Render
|
||||
@@ -80,18 +80,18 @@ SDL3 宿主主循环(native_render/main.cpp run_live_client)
|
||||
|
||||
逻辑层规则:
|
||||
|
||||
- 40250 调用平台类的地方,调用 `extension/src/platform/` 下同名的适配接口,ported 代码里不直接写 SDL/Vulkan/JNI 调用。
|
||||
- 40250 调用平台类的地方,调用 `src/platform/` 下同名的适配接口,ported 代码里不直接写 SDL/Vulkan/JNI 调用。
|
||||
闭包里的 40250 头文件**不分逻辑层和平台层,一律照抄到镜像路径**(`port/EterLib/GrpBase.h` 等),作为平台类的接口,
|
||||
这样原来的相对 include(`../eterLib/StdAfx.h`)保持不变;`platform/` 只放这些类的 `.cpp` 实现,不另写同名头文件。
|
||||
- 平台层对渲染的适配止于 D3D8 接口:40250 的 `Grp*`、地形、水、SpeedTree、特效照抄后调用 `IDirect3DDevice8`,
|
||||
由 `RecordingDevice` 记录,`native_render` 按 D3D8 固定管线语义(光照、雾、纹理阶段、alpha 测试/混合、填充规则)直译成 Vulkan。
|
||||
由 `RecordingDevice` 记录,`src/host` 按 D3D8 固定管线语义(光照、雾、纹理阶段、alpha 测试/混合、填充规则)直译成 Vulkan。
|
||||
- 照抄只做机械转换,用 `port_copy.py copy <Lib/File>`:CP949 转 UTF-8、CRLF 转 LF、`#include "..."` 路径改成磁盘上的
|
||||
大小写;含非 ASCII 字符串字面量的文件拒绝照抄,需手工处理。其他任何改动都是手工修改,行上标 `// PORT:` 并写原因,
|
||||
`port_copy.py diff` 列出全部手工修改。
|
||||
- 40250 源码里的系统头保持原样 include,由 `port/common/shim/` 提供替身:`shim/sdk/`(`d3d8.h`、`d3dx8.h`、`mss.h`,
|
||||
所有平台都用,只含值类型和不透明接口)、`shim/win32/`(`windows.h`、`winsock.h`、`mmsystem.h` 等,仅非 Windows 平台用)。
|
||||
- 40250 的单例(`CPythonPlayer`、`CPythonCharacterManager`、`CPythonNetworkStream`)由移植代码持有,生命周期与 40250
|
||||
相同;宿主(`native_render`)不持有玩法状态,只通过 `PythonBoot` 和平台接口交互。
|
||||
相同;宿主(`src/host`)不持有玩法状态,只通过 `PythonBoot` 和平台接口交互。
|
||||
- **保持 40250 的宽度和溢出语义,不是机械保留 C++ 类型名。** 40250 可执行文件目标是 32 位 Win32(ILP32):
|
||||
`long`/`unsigned long` 和指针都是 32 位;移植目标则可能是 LP64 或 LLP64。`port/common/Win32Types.h`
|
||||
只为 `BYTE`/`WORD`/`DWORD`/`LONG`/`BOOL`/`UINT` 等 Win32 **标量别名**提供定宽定义,不能重定义 C++ 关键字 `long`:
|
||||
@@ -107,10 +107,10 @@ PORT 扩展(40250 没有对应物,只为移动端/平台而加;不能改 4
|
||||
|
||||
- 由 `PythonBoot::RunMainScript` 注入的 Python 模块:`mt_autohunt`(自动狩猎,`AutoHuntScript.inc`)、`mt_register`
|
||||
(客户端内账号注册)、`mt_framerate`(系统选项里的"画面帧率"行)。它们包装 40250 窗口类的方法,不修改脚本文件。
|
||||
- 触控控制(`native_render/touch_controller.h`):摇杆、攻击/技能按钮,转成 40250 的输入接口。
|
||||
- 触控控制(`src/host/touch_controller.h`):摇杆、攻击/技能按钮,转成 40250 的输入接口。
|
||||
- 帧率与功耗(`platform/EterBase/FrameRateMode.h` + `main.cpp` 的 `FrameRatePolicy`):30 / 60(默认)/ 显示器最高三档,
|
||||
无输入一段时间降到 30 fps(自动狩猎时除外),Android 上配合 `setFrameRate` 和 ADPF。
|
||||
- 性能遥测:`--perf-log` 写 CSV(`native_render/perf_log.h`、`platform/EterBase/PerfCounters.h`)。
|
||||
- 性能遥测:`--perf-log` 写 CSV(`src/host/perf_log.h`、`platform/EterBase/PerfCounters.h`)。
|
||||
|
||||
## 4. 路线
|
||||
|
||||
@@ -148,14 +148,14 @@ PORT 扩展(40250 没有对应物,只为移动端/平台而加;不能改 4
|
||||
- 资源、proto、root/uiscript/locale 全部来自 40250 `Client`:`Client/pack` 的经典 `.eix/.epk`(103 个包加 `root`,
|
||||
52,609 个路径,先注册的包优先),经移植的 `EterPack`/`EterPackManager` 读取;`item_proto`/`mob_proto` 用移植的
|
||||
`CLZO` + TEA 读(记录 156 / 255 字节)。
|
||||
- 中文:`push-android-client.sh --locale zh` 推送 `build/zh-locale` 生成的 `locale_zh`,译名来源见 `docs/ZH-LOCALE.md`。
|
||||
- 中文:`android/push-client.sh --locale zh` 推送 `build/zh-locale` 生成的 `locale_zh`,译名来源见 `docs/ZH-LOCALE.md`。
|
||||
- 环境变量:
|
||||
- `MT_40250_CLIENT`:40250 的 `Client` 目录(`pack/`、`Eternexus/`)。native 测试的 CMake 缓存变量默认读它,
|
||||
再退回 `${MT_40250_SOURCE}/../../Client`;
|
||||
- `MT_ASSETS_STRICT=1`:native 测试在资源缺失时失败(返回 1),不按跳过(77)处理。
|
||||
- 来源清单:`tools/asset_manifest.py generate|verify <Client>` 记录 `pack/Index`、全部 `.eix/.epk` 和
|
||||
`Eternexus/root/*.msm` 的路径、大小、sha256,结果在 `audit/assets/40250-client.json`。
|
||||
- `Client/root` 的 `RUN_AS_IS` 账本指向 `pack://<script>.py`,`script/verify_root_pack.py` 逐个解码 `root.epk`
|
||||
- `Client/root` 的 `RUN_AS_IS` 账本指向 `pack://<script>.py`,`scripts/verify_root_pack.py` 逐个解码 `root.epk`
|
||||
核对参考源码和账本 SHA-256。
|
||||
|
||||
## 6. 进度与 port-map
|
||||
@@ -187,7 +187,7 @@ PORT 扩展(40250 没有对应物,只为移动端/平台而加;不能改 4
|
||||
- macOS:`brew install cmake sdl3 vulkan-headers vulkan-loader molten-vk shaderc`;构建见根目录 README
|
||||
(`-DMT_BUILD_NATIVE_RENDER=ON`,目标 `mt_native_render`)。
|
||||
- Android:NDK 27.2(`ANDROID_NDK_HOME` 指向它)、openjdk@21、Android SDK;`adb` 用 `/opt/homebrew/bin/adb`。
|
||||
`./build-android-native.sh Release --install` 构建安装 APK,`./push-android-client.sh [--locale zh] <Client>` 推送资源
|
||||
`./android/build.sh Release --install` 构建安装 APK,`./android/push-client.sh [--locale zh] <Client>` 推送资源
|
||||
(APK 只含代码,资源在应用数据目录)。启动前先唤醒手机。
|
||||
- Python 3:用于 `port_map.py` 和其他审计脚本。Python 2.7.18 只在复核 `py_embed_spike.py` 时需要。
|
||||
- 真服账号、密码不写进仓库或 audit 账本。
|
||||
@@ -196,12 +196,12 @@ PORT 扩展(40250 没有对应物,只为移动端/平台而加;不能改 4
|
||||
|
||||
```bash
|
||||
./script/port_gate.sh macos # port_platform 构建 + ctest '^port\.';另有 android/ios/windows/linux 交叉编译门禁
|
||||
ctest --test-dir build-native --output-on-failure
|
||||
node script/native_mac_acceptance.mjs # macOS 原生客户端验收
|
||||
ctest --test-dir build/native --output-on-failure
|
||||
node scripts/native_mac_acceptance.mjs # macOS 原生客户端验收
|
||||
```
|
||||
|
||||
性能/功耗:客户端加 `--perf-log`(可选 `--show-fps`、`--fps-mode 0|1|2`、`--fps-cap N`),Android 上用
|
||||
`tools/pull_perf_logs.sh` 拉 CSV,`tools/perf_summary.py` 汇总(含按帧率档位/空闲分组的电流和功耗)。插着 USB 时电量读数
|
||||
`tools/perf/pull_perf_logs.sh` 拉 CSV,`tools/perf/perf_summary.py` 汇总(含按帧率档位/空闲分组的电流和功耗)。插着 USB 时电量读数
|
||||
没有意义,真实功耗需无线 adb 并拔线。
|
||||
|
||||
新会话恢复工作:
|
||||
|
||||
Reference in New Issue
Block a user