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
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user