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:
shenlei
2026-09-29 19:08:19 +09:00
co-authored by Claude Opus 5.5
parent 70710477cf
commit a46093104c
2817 changed files with 13728 additions and 13744 deletions
@@ -32,15 +32,15 @@ one of three layers:
| Layer | 40250 units | How to port | Where |
| --- | --- | --- | --- |
| `logic` | `UserInterface/`, `GameLib/`, `EterLib` net/timer/text parsing, `EterPack`, `EterLocale`, `EterBase` pure units (`tea`, `lzo`, `cipher`, `Random`, `Stl`, `Timer`, `Poly/`) (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/` | 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` once its evidence gate passes, 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, `EterBase` file/OS units (`_ETERBASE_PLATFORM` in `port_map.py`) | Adapter behind the interface the 40250 caller uses; equivalence by observable output | `extension/src/platform/` + existing render code (`metin2_model`, `metin2_anim`, `gr2_bridge`, ...) |
| `logic` | `UserInterface/`, `GameLib/`, `EterLib` net/timer/text parsing, `EterPack`, `EterLocale`, `EterBase` pure units (`tea`, `lzo`, `cipher`, `Random`, `Stl`, `Timer`, `Poly/`) (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 | `src/port/<Lib>/<File>.{h,cpp}` |
| `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` once its evidence gate passes, never `N_A`. Reference is 40250 `Eternexus/root`, **not** `assets/root` | `src/port/<Lib>/` (bindings), scripts from pack |
| `platform` | Direct3D/`Grp*`, Granny (`EterGrnLib`), Miles, SpeedTree, `EffectLib`/terrain rendering, Win32 window/input/IME, threads, anti-cheat, `EterBase` file/OS units (`_ETERBASE_PLATFORM` in `port_map.py`) | Adapter behind the interface the 40250 caller uses; equivalence by observable output | `src/platform/` + existing render code (`metin2_model`, `metin2_anim`, `gr2_bridge`, ...) |
Rules for the `logic` layer:
- Port a whole unit into its mirror file. Where 40250 calls a platform class (`CGraphicThingInstance`,
`CStateManager`, `CSoundManager`, ...), call a same-named adapter interface declared under
`extension/src/platform/`; never inline Godot calls into ported logic.
`src/platform/`; never inline Godot calls into ported logic.
- 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
@@ -149,12 +149,12 @@ behavior verified from code inspection alone.
per layer, and tag/port-map/layout consistency. Run `check` before every commit that touches
`audit/port-map/`.
- `scripts/port_copy.py copy <Lib/File>... | diff [<Lib/File>...]` — copy 40250 files into the
`extension/src/port/` mirror with mechanical edits only (CP949→UTF-8, LF, include-path case); `diff`
`src/port/` mirror with mechanical edits only (CP949→UTF-8, LF, include-path case); `diff`
lists the manual `// PORT:` edits.
- `scripts/platform_stub.py list | gen [<Lib/File.h>...] [--force]` — generate the empty platform
skeleton `extension/src/platform/<Lib>/<File>.cpp` for platform-layer mirror headers from clang's AST
(needs `script/port_gate.sh macos` for compile commands); never overwrites without `--force`.
`grep -r MT_PLATFORM_STUB extension/src/platform` lists the platform functions still unimplemented.
skeleton `src/platform/<Lib>/<File>.cpp` for platform-layer mirror headers from clang's AST
(needs `scripts/port_gate.sh macos` for compile commands); never overwrites without `--force`.
`grep -r MT_PLATFORM_STUB src/platform` lists the platform functions still unimplemented.
- `scripts/stub_constants.py [--check]` — regenerates the 2V0 stub modules' constants
(`platform/ScriptLib/GameplayModuleConstants.cpp`) and stub return shapes (`GameplayModules.cpp`)
from the 40250 module sources; rerun when a stub module is ported for real and removed.
@@ -30,16 +30,17 @@
## Current client
- GDScript runtime: `project/` (entry `project/app_flow.gd`, world `project/game_scene.gd`); UI in `project/ui/`.
- Native extension: `extension/src/` (network in `extension/src/net/`, classic 40250 protocol in `extension/src/net/classic/`).
- Format readers: `formats/`; Granny: `libgr2/`; assets/tables: `assets/`; Granny oracle: `oracle/`; tools: `tools/`.
- `src/port/<Lib>/<File>.{h,cpp}` — mirror of each 40250 `logic`/`python` unit (see SKILL.md "Code layout").
- `src/platform/` — implementations of the mirrored 40250 platform classes (D3D8 recorder, Granny,
Miles, SpeedTree stand-in, window/input, ScriptLib boot).
- `src/host/` — SDL3 + Vulkan host (`main.cpp`); `src/gr2/` — Granny reader; `src/codepage/` — code pages.
- `third_party/` — vendored cpython 2.7.18, cryptopp, freetype, minilzo.
- `tests/port/` — port tests and the fake login server; `tests/gr2/` — Granny reader tests.
- `android/` — SDLActivity shell, `android/build.sh`, `android/push-client.sh`.
- `scripts/` — gates and build helpers; `tools/` — standalone dev tools (Granny oracle, perf, server).
- The GDScript runtime and `extension/src/net` below are gone (2026-09-23 … 09-29); the table records
where logic came from.
### Target layout
- `extension/src/port/<Lib>/<File>.{h,cpp}` — mirror of each 40250 `logic` unit (see SKILL.md
"Code layout"). Does not exist yet; the first ported unit creates it and adds it to
`extension/CMakeLists.txt`.
- `extension/src/platform/` — adapter interfaces named after the 40250 platform classes they replace.
- The files in the table below are **migration sources**: when a unit is ported, its logic moves out
of them into the mirror file.
@@ -67,16 +68,15 @@ The 106 `project/*_system.gd` files that only their own `test_*_parity.gd` loade
- `docs/CLIENT-GAP.md`, `docs/CLIENT-PARITY-AUDIT-AND-FIX-GUIDE.md`, `docs/archive/CLIENT-40250-PORT.md`,
`docs/PARITY-GAP.md`: background only; trust the port-map, not their status text.
- `oracle/run-diff-suite.sh`: Granny numerical comparison suite.
- `tools/granny-oracle/run-diff-suite.sh`: Granny numerical comparison suite.
## Verification commands
```bash
godot --headless --path project --script <test>.gd # most project tests
cmake --build build -j8 && ctest --test-dir build --output-on-failure -j4
cmake -S . -B build/native -DCMAKE_BUILD_TYPE=Release -DMT_BUILD_NATIVE_RENDER=ON -DCMAKE_PREFIX_PATH=/opt/homebrew
cmake --build build/native -j8 && ctest --test-dir build/native --output-on-failure -j4
./scripts/port_gate.sh macos android # build/port-gate
./android/build.sh Release --install # build/android + APK
```
`build/` (Debug) is the configured tree; it also writes `project/bin/libmtgodot.*.dylib`, so rebuild
before running Godot tests after pulling native changes (a stale dylib shows up as "method not
found" parse errors). `build-debug/` is a stale tree from another checkout; do not use it.
Always finish with `git diff --check`.
All build trees live under `build/` (gitignored). Always finish with `git diff --check`.
@@ -2,13 +2,13 @@
"""Generate the empty platform skeleton for mirrored platform-layer headers (docs/PORT-PLAN.md 2A).
platform_stub.py list platform-layer headers in the mirror, with their stub state
platform_stub.py gen [<header>...] [--force] write extension/src/platform/<Lib>/<File>.cpp
platform_stub.py gen [<header>...] [--force] write src/platform/<Lib>/<File>.cpp
(no args: every platform header; never overwrites
without --force, so hand-written implementations stay)
platform_stub.py pending <header> <symbol>... [--ctor]
append stubs of just those members (the linker's
undefined-symbol names, demangled as ld prints them) to the stand-in of a not-yet-ported
logic unit, extension/src/platform/pending/<Lib>/<File>.cpp;
logic unit, src/platform/pending/<Lib>/<File>.cpp;
--ctor adds the class constructor/destructor so the
singleton can exist (its members still default-construct)
@@ -18,7 +18,7 @@
A stub file defines everything the header declares and does not define: non-pure, non-template member
functions, constructors/destructors, free functions, and static data members. Declarations come from
clang's AST of the header's gate translation unit (the library StdAfx.h + the header, with the exact
flags from the macOS gate build's compile_commands.json: `script/port_gate.sh macos` first), dumped per
flags from the macOS gate build's compile_commands.json: `scripts/port_gate.sh macos` first), dumped per
class/function name with -ast-dump-filter. Definitions use trailing return types, so types nested in the
class resolve in class scope, and `decltype(C::x) C::x{}` for static data. Each body is
MT_PLATFORM_STUB() plus a default return (platform/PlatformStub.h); `grep -r MT_PLATFORM_STUB` is the
@@ -45,7 +45,7 @@ import refroot # script directory is on sys.path when run directly
REPO = refroot.repo_root()
PORT = REPO / "extension" / "src" / "port"
PLATFORM = REPO / "extension" / "src" / "platform"
GATE_BUILD = REPO / "build-port-gate" / "macos"
GATE_BUILD = REPO / "build/port-gate" / "macos"
MARK = "platform_stub.py"
COMMENT = re.compile(r"//[^\n]*|/\*.*?\*/", re.S)
@@ -103,7 +103,7 @@ def scan(header: Path) -> tuple[list[str], list[str]]:
def gate_command(rel: str) -> tuple[list[str], str]:
db = GATE_BUILD / "compile_commands.json"
if not db.is_file():
raise SystemExit(f"{db}: missing; run `script/port_gate.sh macos` (it exports compile commands)")
raise SystemExit(f"{db}: missing; run `scripts/port_gate.sh macos` (it exports compile commands)")
tu = re.sub(r"[^A-Za-z0-9_]", "_", rel)
for entry in json.loads(db.read_text()):
if Path(entry["file"]).name == f"{tu}.cpp":
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Copy 40250 sources into the port mirror (extension/src/port/<Lib>/<File>) with mechanical edits only.
"""Copy 40250 sources into the port mirror (src/port/<Lib>/<File>) with mechanical edits only.
port_copy.py copy <rel> [<rel>...] [--force] write the mechanical copy (refuses to overwrite without --force)
port_copy.py diff [<rel>...] show the manual edits: port file vs its mechanical copy
@@ -11,7 +11,7 @@ live in `audit/port-map/<unit path>.json`; functions without an entry count as T
port_map.py show <unit> per-function status of one unit
port_map.py check stale hashes, unknown functions, tag mismatches, layout
Layers: `logic` (ported into extension/src/port/<Lib>/<File>, same names as 40250), `python`
Layers: `logic` (ported into src/port/<Lib>/<File>, same names as 40250), `python`
(the embedded-Python track: Python binding modules, EterPythonLib window system, ScriptLib and the
Python root) and `platform` (Direct3D/Granny/Miles/Win32 rendering, audio and OS plumbing, covered
by adapters). Progress is reported per layer so platform code does not dilute the logic figure.
@@ -35,7 +35,7 @@ STATUSES = ("TODO", "PORTED", "RUN_AS_IS", "ADAPTED", "N_A", "DIVERGENT", "NEEDS
DONE = {"PORTED", "RUN_AS_IS", "ADAPTED", "N_A"}
PY_ROOT = "../../Client/Eternexus/root"
UNIT_DIRS = REFERENCE_DIRS + ("EterBase", "EterPythonLib", "ScriptLib", "SpeedTreeLib", "SphereLib", "EterImageLib")
PORT_DIR = "extension/src/port"
PORT_DIR = "src/port"
LAYERS = ("logic", "python", "platform")
# EterBase units that wrap Win32 file/OS/UI resources. The rest (tea, lzo, cipher, Random, Stl, Timer, Poly/*)
# is plain logic and is ported into the mirror; CRC32 and Utils mix pure helpers with Win32 calls and stay here.
@@ -180,7 +180,7 @@ def layer(unit: str) -> str:
def port_file(unit: str) -> str:
"""Expected implementation path stem for a `logic` unit (`extension/src/port/GameLib/ActorInstance`)."""
"""Expected implementation path stem for a `logic` unit (`src/port/GameLib/ActorInstance`)."""
return f"{PORT_DIR}/{unit.rsplit('.', 1)[0]}"
@@ -300,7 +300,7 @@ def cmd_show(repo: Path, units: dict[str, Path], args) -> int:
def scan_tags(repo: Path) -> dict[str, list[str]]:
tags: dict[str, list[str]] = {}
for base, pattern in (("project", "*.gd"), ("extension/src", "*.[ch]*")):
for base, pattern in (("src", "*.[ch]*"),):
for path in (repo / base).rglob(pattern):
if "/addons/" in path.as_posix():
continue
@@ -3,11 +3,11 @@
stub_constants.py [--check]
The stub modules in extension/src/platform/ScriptLib/GameplayModules.cpp only export names. Their
The stub modules in src/platform/ScriptLib/GameplayModules.cpp only export names. Their
constants are data the root scripts compute with (uiSafebox divides by safebox.SAFEBOX_SLOT_Y_COUNT),
so each stub module gets its 40250 init function's `PyModule_Add{Int,String}Constant` lines verbatim,
with the preprocessor lines around them and the source file's includes, in
extension/src/platform/ScriptLib/GameplayModuleConstants.cpp as `AddStubConstants_<module>(poModule)`.
src/platform/ScriptLib/GameplayModuleConstants.cpp as `AddStubConstants_<module>(poModule)`.
A module that is ported for real drops out of the stub list and out of this file.
The stub functions themselves (`stub_<module>_<Name>` in GameplayModules.cpp) return a zero value of
@@ -26,8 +26,8 @@ import sys
import refroot
REPO = refroot.repo_root()
STUBS = REPO / "extension/src/platform/ScriptLib/GameplayModules.cpp"
OUT = REPO / "extension/src/platform/ScriptLib/GameplayModuleConstants.cpp"
STUBS = REPO / "src/platform/ScriptLib/GameplayModules.cpp"
OUT = REPO / "src/platform/ScriptLib/GameplayModuleConstants.cpp"
INIT = re.compile(r'Py_InitModule\("([A-Za-z_]+)"')
KEEP = re.compile(r'^\s*(PyModule_Add(Int|String)Constant\s*\(|#\s*(if|ifdef|ifndef|elif|else|endif)\b)')
INCLUDE = re.compile(r'^\s*#\s*include\s*"([^"]+)"')
@@ -169,7 +169,7 @@ def generate() -> str:
"// module's init function with the preprocessor lines around them.",
'#include "UserInterface/StdAfx.h"',
]
root = REPO / "extension/src/port"
root = REPO / "src/port"
port = {p.relative_to(root).as_posix().lower(): p.relative_to(root).as_posix() for p in root.rglob("*.h")}
includes, bodies = [], []
for module in modules: