Unit of work is a 40250 source unit; current-only logic is a defect; unreachable code is not evidence. Adds per-function port-map schema, priority by player-visible gameplay, parallel worktree rules, and a corrected project map. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
127 lines
7.5 KiB
Markdown
127 lines
7.5 KiB
Markdown
---
|
|
name: metin2-40250-parity-audit
|
|
description: Port, fix, or verify 1:1 parity between the Metin2 40250 Windows C++ client and this Godot client. Use for any 40250 comparison, 1:1 parity, missing original-client behavior, parity regression, or continuation of the client port/audit. Work is done by transliterating 40250 source units, not by hunting behavior differences.
|
|
---
|
|
|
|
# Metin2 40250 1:1 port
|
|
|
|
## Principle
|
|
|
|
metin2-client is the cross-platform build of the 40250 Windows client. Apart from rendering and
|
|
platform APIs, every gameplay algorithm, branch, constant, state order, timing source, data
|
|
source, protocol side effect and cleanup path must be the 40250 one.
|
|
|
|
The 40250 source is the specification. Do not design behavior; transcribe it. Consequences:
|
|
|
|
- **The unit of work is a 40250 source unit** (one `.cpp` file, or one cohesive function group in a
|
|
very large file such as `PythonNetworkStreamPhaseGame.cpp`), not a behavior branch. A round reads
|
|
the whole unit and fixes every divergence in it.
|
|
- **Current-client logic with no 40250 counterpart is a defect**, not an adaptation: delete it
|
|
(e.g. an invented `clampf(0.25, 3.0)` on move speed, a periodic idle `FUNC_WAIT` resend, a
|
|
client-side boss AI). "The reference has no mechanism for X, so we added one" is never a
|
|
platform adaptation.
|
|
- **Unreachable code is not an implementation, and tests of it are not evidence.** Code that the
|
|
runtime never loads (e.g. a `*_system.gd` referenced only by its own `test_*_parity.gd`) must be
|
|
deleted or wired in by porting the real 40250 caller, never cited as parity.
|
|
|
|
## Porting target per layer
|
|
|
|
| 40250 layer | How to port | Where |
|
|
| --- | --- | --- |
|
|
| Platform-independent C++ logic (packet structs/handlers, sequence table, formulas, state machines in `UserInterface/`, `GameLib/`; `EterPack`, `EterLocale`) | Transliterate C++ -> C++, keeping class/function structure and names so each function has one counterpart | `extension/src/` |
|
|
| 40250 C++ logic that currently lives in GDScript | Port function by function, same names in snake_case, same order of statements | existing `.gd` owner |
|
|
| Python UI (`assets/root/*.py`, `uiscript/`) | Translate per `.py` file into its GDScript window, keeping method structure | `project/ui/` |
|
|
| Direct3D, Granny, Miles, Win32 input/window, IME, DirectX math | Platform adapter; equivalence by observable output | `extension/`, `project/` |
|
|
|
|
Mark every ported function with a one-line tag at its definition so coverage can be scanned
|
|
mechanically: `// 40250: CInstanceBase::SetMoveSpeed` (C++) or `# 40250: CInstanceBase::SetMoveSpeed`
|
|
(GDScript). Tag only real counterparts.
|
|
|
|
## Round workflow
|
|
|
|
1. **Pick a unit** from the queue in `audit/remediation-roadmap.md` (ordered by user-visible
|
|
gameplay; see Priority). Skip units whose port-map entries are all `PORTED`/`ADAPTED`/`N_A` and
|
|
whose reference hash is unchanged.
|
|
2. **Read the whole reference unit** under the reference root (see `references/project-map.md`;
|
|
use `grep -a`, many files contain CP949 bytes). Read every current counterpart, found by the
|
|
`40250:` tags, the port-map entry, or `references/project-map.md`.
|
|
3. **For each reference function**, compare statement by statement: preconditions and early
|
|
returns, branches, formulas, constants and units, state writes and their order, timing/event
|
|
source, data source (proto/msa/msm/txt, never hardcoded), packets sent, cleanup. Then:
|
|
- transliterate it where missing or divergent;
|
|
- delete current-only logic it replaces;
|
|
- 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.
|
|
Run only the tests touching the changed files; the full suite runs once per batch.
|
|
5. **Record**: update the unit's port-map entry (per-function status) and append one line to
|
|
`audit/history.jsonl`. Touch `audit/manifest.json` and `audit/contracts/*.md` only when a
|
|
contract's status changes. Do not write narrative into `remediation-roadmap.md`; only reorder or
|
|
tick the queue.
|
|
6. **Commit** the unit (code + test + port-map) as one commit.
|
|
|
|
A round that finds no divergence still records the per-function result so the unit is never re-read
|
|
without a hash change.
|
|
|
|
## Port-map (dedup and coverage)
|
|
|
|
`audit/port-map/<Lib>/<File>.json`, one file per reference source unit (per-file to avoid merge
|
|
conflicts between parallel rounds). Schema in `references/audit-schema.md`. Function statuses:
|
|
|
|
- `TODO` — not yet compared.
|
|
- `PORTED` — current counterpart is statement-equivalent; `impl` names it.
|
|
- `ADAPTED` — platform adapter; `note` states both sides and the invariant, with a test.
|
|
- `N_A` — platform plumbing with no gameplay semantics; `note` states why.
|
|
- `DIVERGENT` — known different and not yet fixed; `note` says how.
|
|
- `NEEDS_LIVE` — code ported, but equivalence depends on real-server/Windows evidence.
|
|
|
|
A function is revisited only if its reference or implementation hash changed or new evidence
|
|
contradicts its status. This replaces the old per-branch "do not repeat" prose.
|
|
|
|
## Priority
|
|
|
|
Order the queue by what a player sees, P0 first:
|
|
|
|
1. Actor/movement: `InstanceBase*.cpp`, `GameLib/ActorInstance*.cpp`, `PythonPlayer*.cpp`,
|
|
`PythonPlayerEventHandler.cpp`, `PythonCharacterManager*.cpp`.
|
|
2. Combat/skill: `ActorInstanceBattle.cpp`, `ActorInstanceAttach*`, `FlyingObject*`,
|
|
`PythonPlayerSkill.cpp`, `PythonSkill.cpp`.
|
|
3. Game-phase packet handlers: `PythonNetworkStreamPhaseGame*.cpp`, `PythonNetworkStreamEvent.cpp`.
|
|
4. Items/inventory/UI data: `PythonItem.cpp`, `PythonExchange.cpp`, `PythonSafeBox.cpp`, `PythonShop.cpp`,
|
|
then the Python UI scripts.
|
|
5. World/map/terrain/effects, then audio, weather, platform.
|
|
|
|
Login/phase edge cases whose only open item is real-server evidence are `NEEDS_LIVE` and go to a
|
|
separate live-verification batch; do not spend porting rounds on them.
|
|
|
|
## Parallel rounds
|
|
|
|
Independent units may run in parallel, one git worktree per unit. Two parallel rounds must not
|
|
edit the same implementation file; if a unit's counterpart overlaps a running round, queue it.
|
|
Port-map files are per unit and `history.jsonl` is append-only, so merges are mechanical.
|
|
|
|
## Contracts and verification states
|
|
|
|
`audit/manifest.json` contracts remain the behavior-level roll-up (status meanings and gates in
|
|
`references/audit-schema.md`). A contract may reach `STATIC_VERIFIED` only when every reference
|
|
unit it lists has no `TODO`/`DIVERGENT` functions, and `TEST_VERIFIED` when its tests also pass.
|
|
Never cite a test of unreachable code, and never mark visual, timing-feel, driver or Windows-only
|
|
behavior verified from code inspection alone.
|
|
|
|
## Tools
|
|
|
|
- `scripts/audit_ledger.py refresh --write | report --write | validate` — run once per batch, not
|
|
per round. Missing reference files are an error, not a silent fingerprint.
|
|
- `scripts/audit_packet_registry.py`, `audit_phase_dispatch.py`, `audit_sequence_table.py` — run
|
|
when a round touches `extension/src/net/`.
|
|
- `scripts/audit_source_coverage.py` — maps active 40250 sources (from the `.vcxproj` files) to
|
|
contracts.
|
|
- Reference root: `MT_40250_SOURCE` env var, else `reference_root` in `audit/manifest.json`
|
|
(relative to the repo root).
|
|
|
|
## Completion report
|
|
|
|
Per round: unit, functions ported/fixed/deleted/marked `N_A`, divergences found (one line each:
|
|
reference vs old current behavior), tests run, commit hash, remaining `TODO`/`DIVERGENT`/`NEEDS_LIVE`
|
|
in the unit. Keep it short.
|