Work-Package: M01-FND-COORDS-001 Agent: sindo-main-codex Tests: coordinate mapper, M00 coordinate calibration, baseline manifest, coordination and documentation gates passed Fidelity: five build 12340 camera points map within 0.002 yard; no visual parity claim
12 KiB
Coordinate Mapping
Metadata
| Field | Value |
|---|---|
| Status | Implemented |
| Target/work package | M01-FND-COORDS-001 |
| Owners | Foundation/domain |
| Last verified | Worktree work/sindo-main-codex/m01-coordinate-mapper, 2026-07-13 |
| Profiles/capabilities | All profiles; contract version 1 |
Purpose
Provide scene-free value objects and one pure CoordinateMapper for positions,
ADT grid ownership and world-facing yaw. The module makes every source space
explicit before renderer, network, gameplay and authoring consumers migrate.
Non-goals
- Reading ADT, packet, SQL or scene data.
- Constructing
Vector3,Transform3D, Node or Resource values. - Correcting M2/WMO local axes or MDDF/MODF Euler rotations.
- Selecting maps, validating terrain height or owning streaming focus.
- Persisting coordinate values in this work package.
Context and boundaries
flowchart LR
Server[Server / movement X,Y,Z] --> ServerPosition[ServerWorldPosition]
ADT[MDDF/MODF X,height,Z] --> AdtPosition[AdtPlacementPosition]
ServerPosition --> Mapper[CoordinateMapper]
AdtPosition --> Mapper
Mapper --> Canonical[CanonicalWowWorldPosition]
Canonical --> Tile[AdtTileCoordinate + AdtTileLocalPosition]
Canonical --> GodotPosition[GodotWorldPosition]
GodotPosition --> Adapter[Future scene/render adapter]
Adapter --> Vector[Vector3 at boundary]
Scene[Node / Camera3D / scene lookup] -. forbidden .-> Mapper
Allowed dependencies:
- GDScript scalar math and
RefCountedlifetime; - other value objects in
src/domain/coordinates; - callers in network, gameplay, render and server-data adapters.
Forbidden dependencies:
Node,Resource, SceneTree, Camera3D and global scene lookup;- packet buffers, SQL rows, asset parsers and renderer state;
Vector3as a public mapper input or output;- model-specific placement corrections.
Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
CanonicalWowWorldPosition |
Immutable value | Client/server X/Y/Z in yards | Any thread; caller-owned reference | No bounds validation |
ServerWorldPosition |
Immutable value | Server/wire X/Y/Z in yards | Any thread; caller-owned reference | No adapter validation |
AdtPlacementPosition |
Immutable value | MDDF/MODF X/height/Z fields | Any thread; caller-owned reference | No file validation |
GodotWorldPosition |
Immutable value | Godot east/up/south scalars | Any thread; caller-owned reference | Does not create Vector3 |
AdtTileCoordinate |
Immutable value | Map_tileX_tileY.adt indices |
Any thread; caller-owned reference | is_within_map_grid() reports invalid indices |
AdtTileLocalPosition |
Immutable value | East/height/south offsets from tile north-west corner | Any thread; caller-owned reference | Values are intentionally unclamped |
AdtChunkCoordinate |
Immutable value | MCNK chunk indices inside one tile | Any thread; caller-owned reference | is_within_map_grid() reports invalid indices |
CanonicalWowWorldYaw |
Immutable value | Canonical world yaw in radians | Any thread; caller-owned reference | Constructor does not normalize |
ServerWorldYaw |
Immutable value | Movement orientation in radians | Any thread; caller-owned reference | Constructor does not normalize |
GodotWorldYaw |
Immutable value | Godot Y-axis world yaw in radians | Any thread; caller-owned reference | Constructor does not normalize |
CoordinateMapper.server_to_canonical / inverse |
Pure static methods | Preserve server X/Y/Z as canonical | Synchronous; new result value | Null input is a caller contract violation |
CoordinateMapper.canonical_to_godot / inverse |
Pure static methods | Apply Y-up renderer basis and map offset | Synchronous; new result value | Non-finite scalars propagate |
CoordinateMapper.canonical_to_adt_placement / inverse |
Pure static methods | Convert ADT placement field basis | Synchronous; new result value | Non-finite scalars propagate |
CoordinateMapper.canonical_to_adt_tile |
Pure static method | Compute unclamped filename indices | Synchronous; new result value | Out-of-map result remains invalid |
CoordinateMapper.canonical_to_adt_tile_local / inverse |
Pure static methods | Split/reconstruct tile-local position | Synchronous; new result value | Caller validates tile ownership |
CoordinateMapper.adt_tile_local_to_chunk |
Pure static method | Compute unclamped MCNK indices | Synchronous; new result value | Boundary/out-of-range remains diagnosable |
| World-yaw mapper methods | Pure static methods | Normalize and cross the world-basis boundary | Synchronous; new result value | Non-finite input is unsupported |
Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | ServerWorldPosition, ServerWorldYaw |
Future protocol/server adapter | CoordinateMapper |
Immutable caller value | Any thread, call duration |
| Input | AdtPlacementPosition |
Future ADT adapter | CoordinateMapper |
Immutable caller value | Any thread, call duration |
| Input | CanonicalWowWorldPosition, CanonicalWowWorldYaw |
Gameplay/content/domain | CoordinateMapper |
Immutable caller value | Any thread, call duration |
| Output | CanonicalWowWorldPosition |
CoordinateMapper |
Gameplay/content/adapters | New immutable value | Caller-owned reference |
| Output | AdtTileCoordinate, AdtTileLocalPosition, AdtChunkCoordinate |
CoordinateMapper |
Streaming/content adapters | New immutable values | Caller-owned references |
| Output | GodotWorldPosition, GodotWorldYaw |
CoordinateMapper |
Future render/scene adapter | New immutable values | Caller-owned references |
Side effects:
- none;
- no scene mutation, file/cache write, network send, database access or logging.
Data flow
flowchart LR
Server[ServerWorldPosition X,Y,Z] -->|identity components| Canonical[CanonicalWowWorldPosition]
ADT[AdtPlacementPosition X,height,Z] -->|half extent - horizontal| Canonical
Canonical -->|floor 32 - axis / tile size| Tile[AdtTileCoordinate]
Canonical -->|north-west edge offsets| Local[AdtTileLocalPosition]
Local -->|floor offset / chunk size| Chunk[AdtChunkCoordinate]
Canonical -->|half extent - Y; Z; half extent - X| Godot[GodotWorldPosition]
ServerYaw[ServerWorldYaw] -->|normalize only| CanonicalYaw[CanonicalWowWorldYaw]
CanonicalYaw -->|same numeric world angle| GodotYaw[GodotWorldYaw]
Lifecycle/state
The module is stateless. Each call reads immutable scalar inputs and returns a new value object. There is no initialization, shutdown, cancellation or retry state machine.
Main sequence
No cross-layer asynchronous sequence occurs inside this module. Producers parse or validate their external format first, call the mapper synchronously, and then pass only typed results across the next boundary.
Ownership, threading and resources
- Value objects own only scalar fields and, for chunks, one immutable tile reference.
- Callers own returned
RefCountedreferences. - Methods do not mutate inputs or shared state and are safe for worker use.
- No Node, RID, file, socket, database or cache resource is acquired.
Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Tile/chunk outside standard grid | is_within_map_grid() |
Unclamped indices are preserved | Caller reports source and map context | Reject or apply explicit map policy |
| Tile-local value outside half-open tile | Consumer boundary validation | Value is preserved | Caller reports source coordinate | Recompute ownership or reject malformed input |
| Null value object | Static typing/call failure | Programming contract violation | GDScript error with call site | Fix producer; no fallback value |
| NaN/infinite scalar | Adapter validation | Arithmetic propagates invalid value | Adapter must report source field | Reject malformed external input |
There is no cancellation because every conversion is constant-time scalar math.
Configuration and capabilities
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| Contract version | 1 | All | No | Axis, unit and boundary semantics in ADR 0001 |
| Godot units per WoW yard | 1 | Blizzlike335 and current tools |
No | Preserves current renderer scale |
| ADT grid | 64 tiles/axis, 16 chunks/tile | WotLK 3.3.5a | No | Defines index and boundary math |
Persistence, cache and migration
This package adds no persisted format, cache or migration. Existing dictionaries,
native Vector3 payloads and renderer conversion helpers remain compatible.
Consumer migration is ordered after this contract and must remove old manual
conversions only after its producer/consumer tests pass.
Any future serialized coordinate must store a schema version and explicit space; raw three-number arrays are not an accepted new persistence contract.
Diagnostics and observability
- Logs: none in production mapper.
- Metrics: none; conversion cost is constant and outside frame-budget concern.
- Debug views: headless contract probe prints one PASS summary.
- Correlation IDs: supplied by external adapters if an invalid source is reported.
Verification
- Unit/contract tests:
src/tools/verify_coordinate_mapper.gd. - Integration/E2E: consumer migration and
StreamingFocuspackages remain pending. - Fidelity evidence: five build-12340 camera positions from the M00 manifest map
within
0.002yard; independent reference formulas are recorded in ADR 0001. - Performance budgets: constant-time arithmetic; no scene or file access.
- Manual diagnostics: not required for the pure contract; renderer checkpoints are required when consumers migrate.
Extension points
- Add typed map IDs around positions without changing axis semantics.
- Add a format-specific placement-orientation adapter with its own fixtures.
- Add serialization only with explicit space/version fields and migrations.
- Add consumer adapters that construct engine-native vectors at their boundary.
Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Canonical/server/Godot position mapping | Implemented | Headless round-trip and five M00 golden points | Migrate producers/consumers |
| ADT placement mapping | Implemented | Synthetic/direct round-trip fixtures | Compare native parsed placements during migration |
| ADT tile/local/chunk ownership | Implemented | Center, extent, tile and chunk boundary fixtures | Migrate streaming names/ownership |
| World-facing yaw mapping | Implemented | Cardinal/wrap round-trip fixtures | Placement Euler remains separate |
| Scene-free production contract | Implemented | Source boundary and headless test | Add dependency search gate later in M01 |
| Visual/server-spawn integration | Planned | M01 target criteria | Add golden server spawn after adapter migration |
Known gaps and risks
- Existing native loaders and renderer scripts still contain manual conversions.
- Current
StreamingWorldLoadertile names may represent Godot X/Z ordering rather than ADT filename X/Y; migration must preserve visible baseline behavior. - MDDF/MODF rotation comments and implementations disagree across existing paths; this package deliberately does not generalize them into world yaw.
- Finite-number validation belongs to external format adapters and is not yet implemented.
- Arithmetic agreement does not prove terrain, placement, camera direction or visual parity.
Source map
| Path | Responsibility |
|---|---|
src/domain/coordinates/coordinate_mapper.gd |
Constants and all coordinate-axis conversions |
src/domain/coordinates/*_position.gd |
Immutable typed position values |
src/domain/coordinates/adt_tile_coordinate.gd |
ADT filename tile indices and bounds |
src/domain/coordinates/adt_chunk_coordinate.gd |
MCNK indices and bounds |
src/domain/coordinates/*_yaw.gd |
Immutable typed world-facing yaw values |
src/tools/verify_coordinate_mapper.gd |
Headless fixtures and boundary regressions |
docs/adr/0001-canonical-world-coordinates.md |
Normative axes, units, boundaries and rollout decision |
Related decisions and references
- ADR:
../adr/0001-canonical-world-coordinates.md - M00 evidence:
../M00_FINAL_PAIRED_EVIDENCE.md - Architecture:
../ARCHITECTURE.md - Testing rules:
../TESTING.md