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:
shenlei
2026-09-22 18:38:04 +09:00
co-authored by Claude Opus 5
parent 67a62e3390
commit dcefc83211
3 changed files with 115 additions and 49 deletions
@@ -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.