Compare commits
106 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a85e416cb8 | |||
| c69abd634f | |||
| cd23a62be8 | |||
| 2ee647a220 | |||
| 60f1e363c0 | |||
| cb103e1fde | |||
| d6e5b530ef | |||
| 8c46065ada | |||
| 9408d887ba | |||
| 80cb08422b | |||
| 0773de5977 | |||
| b697a896e8 | |||
| 6117e5282e | |||
| 53d84ccc8a | |||
| 7e35de7f61 | |||
| ba9f89691d | |||
| b58bf2caa6 | |||
| 52ea639d64 | |||
| a023d76eea | |||
| 7ece2abea2 | |||
| 10c5d4cc12 | |||
| e52f703da5 | |||
| 91f0724ce2 | |||
| 650d584ff0 | |||
| 643850c8ee | |||
| ad8fc41ace | |||
| d0f74f840b | |||
| 5b10288ff7 | |||
| e887c3bad5 | |||
| 0c24f96ae1 | |||
| ad0532925a | |||
| 9c3f7018fb | |||
| a100cede46 | |||
| 2466fd67d3 | |||
| 3d528e3bbf | |||
| a629bedccf | |||
| 4f566b1957 | |||
| d9c847c557 | |||
| 1bf5b526be | |||
| 9275fbe04f | |||
| eb122d52a9 | |||
| e819eeb35b | |||
| 8776a6b362 | |||
| c3094c9413 | |||
| 7d5d13f702 | |||
| 742c415885 | |||
| 8c71e46872 | |||
| ad820699bc | |||
| 97bb53f6cd | |||
| 68e6f60d90 | |||
| 02dec23c2a | |||
| 8c1cf9be36 | |||
| f5bb64e6c7 | |||
| 0ce3cae208 | |||
| a45d521567 | |||
| 5206c42cf0 | |||
| d2d6df3655 | |||
| 435e1c95d2 | |||
| 14dead194b | |||
| e794764269 | |||
| 6bd2e84048 | |||
| 62eece991c | |||
| fc81059dfa | |||
| dd412e6d0d | |||
| 07f08044f4 | |||
| c8e99f28d6 | |||
| 48cc976ba0 | |||
| 318e6ae757 | |||
| 747fae8e2e | |||
| 24ddc99a7b | |||
| 52b6fa2ebb | |||
| 3933dbfc47 | |||
| a07a487368 | |||
| 47dec6c32c | |||
| c8e62acf4c | |||
| 7c64e8d60a | |||
| c1dc07c705 | |||
| d68786355c | |||
| 77cd17a5bc | |||
| 7815385b3b | |||
| fbef131bcb | |||
| 52e1b2ba67 | |||
| ce4f7ec0e2 | |||
| 758b375e7e | |||
| b55bfca2a7 | |||
| a7072e3d14 | |||
| f9c467b71f | |||
| 1c0dfc761d | |||
| f45695c26a | |||
| c2be8ae95e | |||
| 8636427234 | |||
| 5627180fd5 | |||
| ba81098797 | |||
| f19c0c45a7 | |||
| 09b3e87a8d | |||
| d1df794cd6 | |||
| 0327e4862d | |||
| 239cb2432e | |||
| b5ddc86c92 | |||
| 3d90c6d79a | |||
| a10206b797 | |||
| 3172f41ee5 | |||
| 5a957d2ebb | |||
| 5092995d0c | |||
| 4908eb2e31 | |||
| eca76c5ed1 |
@@ -35,3 +35,7 @@
|
||||
|
||||
# CMake build директория
|
||||
/src/native/build/
|
||||
|
||||
# Local root-level diagnostic capture logs
|
||||
/*.stdout.log
|
||||
/*.stderr.log
|
||||
|
||||
@@ -25,7 +25,8 @@ Paired run 2026-07-11 подтвердил крупный coordinate/placement g
|
||||
|
||||
## Основные файлы
|
||||
|
||||
- `src/scenes/streaming/streaming_world_loader.gd` - главный runtime streamer мира.
|
||||
- `src/render/world_render_facade.gd` - стабильная граница runtime/tools для focus и metrics.
|
||||
- `src/scenes/streaming/streaming_world_loader.gd` - внутренний runtime streamer мира.
|
||||
- `src/scenes/streaming/eastern_kingdoms_streaming.tscn` - текущая сцена для проверки Azeroth/Eastern Kingdoms.
|
||||
- `addons/mpq_extractor/loaders/adt_builder.gd` - сборка ADT terrain, control splat материалов и MH2O liquids.
|
||||
- `addons/mpq_extractor/loaders/m2_builder.gd` - сборка статического M2 mesh/material.
|
||||
@@ -822,6 +823,147 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
- Added `src/tools/capture_render_checkpoints.gd` to load the Eastern Kingdoms streaming scene, move a dedicated camera to fixed regression viewpoints and save PNGs under `user://render_checkpoints`.
|
||||
- Current checkpoints cover the Elwynn waterfall from front/side and Goldshire Inn/Blacksmith windows. Headless runs are dry-run only because the headless display server cannot produce viewport PNGs.
|
||||
|
||||
## 2026-07-13 Renderer Shutdown Ownership
|
||||
|
||||
- `StreamingWorldLoader` drains its asynchronous work before teardown, leaves parented streamed nodes to SceneTree ownership, then explicitly frees detached M2/WMO prototype nodes and clears resource caches.
|
||||
- `ADTBuilder` explicitly frees a temporary `Water` root when an ADT liquid payload produces no child geometry. Dry tiles must not leave empty nodes in ObjectDB.
|
||||
- `src/tools/verify_render_runtime_cache_shutdown.gd` regression-checks detached prototype and resource-cache release plus empty-liquid-root ownership. The unified render-baseline runner executes it before capture.
|
||||
- A focused 8-second GUI checkpoint and a seven-checkpoint single-pass GUI run finish without Node, renderer resource or RID leak diagnostics. A timing-dependent anonymous zero-reference `RefCounted` ObjectDB warning can remain at engine teardown; it is tracked separately and is not suppressed.
|
||||
- This is shutdown-only ownership behavior and does not alter checkpoint rendering, cache formats or the `Blizzlike335` compatibility profile.
|
||||
|
||||
## 2026-07-13 Explicit Streaming Focus
|
||||
|
||||
- `StreamingWorldLoader` consumes an immutable `StreamingFocus` containing a
|
||||
typed `GodotWorldPosition`; runtime streaming no longer discovers the active
|
||||
`Camera3D` from the viewport or child nodes.
|
||||
- Eastern Kingdoms and Kalimdor runtime scenes explicitly use the player as the
|
||||
streaming source. Checkpoint capture and terrain/occluder probes explicitly
|
||||
use their dedicated camera through the same public refresh path.
|
||||
- The editor viewport camera remains behind a dedicated editor-only adapter.
|
||||
`camera_path` is retained solely for optional automatic overview positioning.
|
||||
- Streaming radii, LOD decisions, cache formats and renderer quality profiles
|
||||
are unchanged by this migration.
|
||||
|
||||
## 2026-07-13 Coordinate Conversion Boundary Gate
|
||||
|
||||
- Sky/area lookup, streaming tile ownership, player spawn/terrain sampling and
|
||||
terrain diagnostics now cross world-coordinate spaces through typed
|
||||
`CoordinateMapper` methods.
|
||||
- Renderer distance, LOD, ray and model-local M2/WMO calculations remain in
|
||||
their existing Godot/local spaces; they are not axis conversions.
|
||||
- The repository gate rejects new manual world-center, WoW/Godot and
|
||||
Godot-position-to-ADT formulas. The unified renderer baseline runs this gate.
|
||||
- Native ADT/WDT parsers keep one shared world conversion in
|
||||
`wow_chunk_reader.h`; the independent M00 calibration formula remains a test oracle.
|
||||
- Coordinate contract version 2, renderer radii, cache formats and visual
|
||||
quality profiles are unchanged.
|
||||
|
||||
## 2026-07-13 Golden Server Spawn Renderer Evidence
|
||||
|
||||
- The pinned AzerothCore Human Warrior start at server position
|
||||
`(-8949.95, -132.493, 83.5312)` maps through `CoordinateMapper` to Godot
|
||||
`(17199.159666667, 83.5312, 26016.616666667)`, ADT `(32,48)` and MCNK `(3,12)`.
|
||||
- `server_spawn_render_manifest.json` drives the ordinary Eastern Kingdoms
|
||||
checkpoint capture pipeline at that location; no live database or production
|
||||
spawn entity is introduced.
|
||||
- A renderer-native emissive marker starts at the exact mapped origin and keeps
|
||||
depth testing enabled so GUI evidence exposes its relationship to terrain.
|
||||
- `verify_server_spawn_renderer.gd` rejects drift between the pinned fixture,
|
||||
manifest target/player coordinates and expected ADT ownership. The unified
|
||||
renderer baseline runs this contract check before capture.
|
||||
- This closes a coordinate/renderer integration criterion only. It does not
|
||||
claim TrinityCore row equivalence or visual parity with the original client.
|
||||
|
||||
## 2026-07-15 WorldRenderFacade First Seam
|
||||
|
||||
- Runtime streaming scenes, checkpoint capture and renderer probes now submit
|
||||
typed focus through `WorldRenderFacade`; those callers no longer call the
|
||||
monolithic streamer's focus methods or configure its focus source directly.
|
||||
- Baseline capture reads queue/cache/activity counts through
|
||||
`renderer_metrics_snapshot()`. The facade returns a deep copy, so diagnostics
|
||||
cannot mutate renderer-owned state.
|
||||
- `StreamingWorldLoader` remains the internal implementation and still owns all
|
||||
queues, tasks, caches, nodes and RIDs. The facade adds no parallel state or
|
||||
replacement scheduler.
|
||||
- This package does not alter targets, budgets, cache versions, placement,
|
||||
visibility, materials or animation. Environment, entity visuals and ground
|
||||
query remain explicit follow-up facade contracts in M03.
|
||||
|
||||
## 2026-07-15 StreamingTargetPlanner Extraction
|
||||
|
||||
- Runtime and editor wanted/retained ADT set calculation now lives in the
|
||||
scene-free `StreamingTargetPlanner`.
|
||||
- `StreamingTargetPolicy` preserves existing chunk/tile radii, warm/prewarm/
|
||||
retain margins and the clamped boundary-prefetch threshold.
|
||||
- `StreamingTargetPlan` freezes its focus/wanted/retained result; the streamer
|
||||
still owns available paths, load-queue distance sorting, loaded-tile LOD/detail
|
||||
state, async work, caches, nodes and RIDs.
|
||||
- Asset-free fixtures cover center, edge/corner prefetch, catalog filtering,
|
||||
clamp and editor behavior plus bounded High-like iteration timing.
|
||||
- Cache versions, quality profiles, queue priority and visible rendering rules
|
||||
are unchanged by the extraction.
|
||||
|
||||
## 2026-07-16 RenderBudgetScheduler Extraction
|
||||
|
||||
- The scene-free `RenderBudgetScheduler` now owns the per-frame counters for 16
|
||||
named renderer operation lanes. It owns no queue, task, cache, Node or RID.
|
||||
- `StreamingWorldLoader` snapshots its existing exported limits once per process
|
||||
frame and asks for a boolean permit immediately before each historically
|
||||
budgeted operation.
|
||||
- Chunk removals and creations intentionally share one `chunk_geometry` lane;
|
||||
the existing removal-first drain order and combined limit are preserved.
|
||||
- Loader teardown cancels permit issuance before waiting for asynchronous work.
|
||||
Existing worker/result staleness checks and resource cleanup remain unchanged.
|
||||
- Asset-free contracts cover exact bounds, independent/shared lanes, frame reset,
|
||||
invalid inputs, terminal cancellation, detached diagnostics and hot-path timing.
|
||||
- Defaults, quality presets, queue order, cache versions and visible rendering
|
||||
rules are unchanged. Asset-backed p95/p99 comparison remains required.
|
||||
|
||||
## 2026-07-16 Streamer Internal Access Gate
|
||||
|
||||
- `verify_renderer_internal_access.gd` derives private queue, task, cache and
|
||||
tile-state field names from `StreamingWorldLoader` declarations and rejects
|
||||
external member/reflection access in gameplay and Godot `EditorPlugin` package sources.
|
||||
- The gate follows the implementation inventory instead of duplicating a manual
|
||||
list, so a newly named private renderer field is covered automatically when it
|
||||
matches those ownership categories.
|
||||
- Scene composition may still reference the loader script and the facade's
|
||||
implementation path; those are composition details, not mutable queue access.
|
||||
- Renderer diagnostic probes are added explicitly as their facade contracts land.
|
||||
- This is a source-only boundary check and changes no renderer behavior or fidelity.
|
||||
|
||||
## 2026-07-16 Rendered Ground Query Facade
|
||||
|
||||
- `WorldRenderFacade.sample_ground_height()` accepts `GodotWorldPosition` and
|
||||
returns renderer-owned immutable `RenderedGroundSample` from already loaded
|
||||
quality/tile-LOD render meshes, avoiding a render-to-gameplay dependency.
|
||||
- Its factories load the predeclared Script path instead of relying on a warm
|
||||
global-class cache; a dedicated cold-start verifier covers this bootstrap path.
|
||||
- `renderer_ground_query_snapshot()` preserves the terrain probe's tile readiness,
|
||||
queue position, mesh bounds and nearby seam-fallback diagnostics as a detached
|
||||
Dictionary; callers receive no queue, tile-state, Mesh or Node reference.
|
||||
- `probe_render_terrain_height.gd` now uses this facade snapshot and no longer reads
|
||||
`_tile_states`, `_tile_load_queue`, `_tile_loading_tasks` or `_available_tiles`.
|
||||
- Gameplay continues using the independently composed M02 `TerrainQuery`/
|
||||
`AdtTerrainQuery`. Loaded render meshes are diagnostic and are not authoritative
|
||||
movement collision.
|
||||
- The ray height, 3x3 tile search and `2/5/10/20/40`-unit nearby fallback are
|
||||
preserved. No terrain geometry, cache, streaming or visual behavior changed.
|
||||
|
||||
## 2026-07-16 World Environment Snapshot Facade
|
||||
|
||||
- `WorldEnvironmentSnapshot` carries one immutable finite time-of-day value,
|
||||
normalized to `[0, 24)` hours; invalid non-finite input is explicit.
|
||||
- `WorldRenderFacade.apply_environment_snapshot()` delegates the snapshot to the
|
||||
scene-owned `WowSkyController`. The facade owns no `Environment`, sun, skybox,
|
||||
shader global or DBC sampling state.
|
||||
- Checkpoint capture now submits its manifest time through the facade instead of
|
||||
locating `WowSkyController` and mutating `use_system_time`, `time_speed` and
|
||||
`fixed_time_hours` directly.
|
||||
- Applying a snapshot performs the same three fixed-clock assignments as the old
|
||||
capture path. Light/Area/Skybox DBC lookup, interpolation, fog, sun and shader
|
||||
behavior remain unchanged; weather and indoor environment state are follow-up work.
|
||||
|
||||
## Practical Rule For Future Work
|
||||
|
||||
If something improves quality but creates visible hitch, it is not done. Move it to bake/cache/background work, split finalization over frames, or prewarm it before the player can see it.
|
||||
|
||||
@@ -788,7 +788,10 @@ static func _build_tile_water_root(data: Dictionary, origin_offset: Vector3) ->
|
||||
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
root.add_child(mi)
|
||||
|
||||
return root if root.get_child_count() > 0 else null
|
||||
if root.get_child_count() > 0:
|
||||
return root
|
||||
root.free()
|
||||
return null
|
||||
|
||||
|
||||
static func _append_liquid_geometry(
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# M00-QAR-ANIMATION-SKY-EVIDENCE-001 — Animation and sky evidence
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-ANIMATION-SKY-EVIDENCE-001:sindo-main-codex:2026-07-15 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-ANIMATION-SKY-EVIDENCE-001:d1df794 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M00
|
||||
- Program: QAR
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m00-animation-sky-evidence`
|
||||
- Lease expires UTC: 2026-07-15
|
||||
- Integrator: milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Add private build 12340 evidence for the remaining sky-transition and native animated-M2 coverage, rerun all paired comparisons, and hand the resulting audit to the milestone integrator.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Committing proprietary screenshots or extracted assets.
|
||||
- Claiming renderer parity from mismatched world placement or animation phase.
|
||||
- Marking M00 DONE; only the milestone integrator may change target status or Evidence.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: this claim and animation/sky evidence documentation
|
||||
- Shared/hotspots: `docs/M00_FINAL_PAIRED_EVIDENCE.md`, `docs/modules/world-renderer.md`
|
||||
- Generated/ignored: normalized private references under `sources/OpenWCReferenceCheckpoints`; captures and reports under `user://`
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API/events: unchanged
|
||||
- Schema/format version: unchanged
|
||||
- Migration/compatibility: existing five-reference comparison remains valid
|
||||
- Consumers: M00 milestone integrator
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: user-captured build 12340 screenshots at 19:00 and two real `GryphonRoost01` animation phases
|
||||
- Blocks: final integrator decision on M00 completion
|
||||
- External state: private screenshots remain outside Git
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: private-reference preparation, full baseline runner, comparator self-test, coordination and documentation gates
|
||||
- Fixtures: seven private build 12340 checkpoint references plus a second animation-phase provenance frame
|
||||
- Fidelity evidence: sky-transition screenshot and observed wing pose change over three seconds
|
||||
- Performance budget: existing M00 capture budgets
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs: unchanged
|
||||
- Module specification: paired coverage and remaining limitations
|
||||
- Data-flow diagram: unchanged
|
||||
- Sequence/state/dependency diagrams: unchanged
|
||||
- Source map/status updates: final paired evidence audit
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: none
|
||||
- Simplest considered solution: normalize private captures outside Git and reuse the existing comparator
|
||||
- Rejected complexity/abstractions: adding a video comparator or committing an asset corpus
|
||||
- Unavoidable complexity and justification: animation phase and synthetic placement require human evidence classification alongside pixel metrics
|
||||
- Measured optimization evidence: not applicable
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: exact-size private references registered; real ADT placement verified; canonical and visual captures completed; fourteen comparisons audited
|
||||
- Next: milestone integrator reviews synthetic/real placement classification and GUI RID risk
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: this work-package commit
|
||||
- Results: `compared=14`, `failed=14`, `missing=0`; mean error `0.066893..0.176955`; original-client Gryphon wing phase changed over three seconds
|
||||
- Remaining risks: synthetic probe placement differs from the original-client world placement; GUI RID/resource diagnostics remain separate
|
||||
- Documentation updated: `docs/M00_FINAL_PAIRED_EVIDENCE.md`, `docs/modules/world-renderer.md`
|
||||
@@ -0,0 +1,79 @@
|
||||
# M00-QAR-CAMERA-FRAMING-REFINEMENT-001 — Goldshire pitch/FOV refinement
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-CAMERA-FRAMING-REFINEMENT-001:sindo-main-codex:2026-07-14 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-CAMERA-FRAMING-REFINEMENT-001:3172f41 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M00
|
||||
- Program: QAR
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m00-camera-framing-refinement`
|
||||
- Lease expires UTC: 2026-07-14
|
||||
- Integrator: milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Jointly refine Goldshire checkpoint pitch and FOV against a WMO-ready build 12340 reference.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Changing runtime player camera defaults.
|
||||
- Changing manifest values without an interior optimum and human approval.
|
||||
- Optimizing yaw beyond the ready-scene coarse result.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: joint FOV option in camera-pose sweep and framing evidence
|
||||
- Shared/hotspots: camera-pose operational documentation and renderer module findings
|
||||
- Generated/ignored: candidate PNGs and ranking JSON under `user://`
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API/events: additive `-CameraFovValues`; scalar `-CameraFov` remains compatible
|
||||
- Schema/format version: ranking schema remains version 1 with existing per-candidate FOV field
|
||||
- Migration/compatibility: scalar sweeps retain existing candidate paths
|
||||
- Consumers: M00 fidelity workflow
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: WMO-ready 8-second capture contract
|
||||
- Blocks: Goldshire manifest framing decision
|
||||
- External state: private build 12340 reference remains outside Git
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: joint-grid plan regression, real ready-scene sweep, comparator self-test, repository gates
|
||||
- Fixtures: private Goldshire Inn reference
|
||||
- Fidelity evidence: joint pitch/FOV ranking and human inspection
|
||||
- Performance budget: offline diagnostic
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs: PowerShell parameter
|
||||
- Module specification: framing finding
|
||||
- Data-flow diagram: FOV joins pose grid
|
||||
- Sequence/state/dependency diagrams: unchanged
|
||||
- Source map/status updates: operational guide
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: `CameraFovValues`, `effectiveCameraFovValues`
|
||||
- Simplest considered solution: one additional loop around the existing grid
|
||||
- Rejected complexity/abstractions: generic optimizer
|
||||
- Unavoidable complexity and justification: projection and pitch jointly affect framing
|
||||
- Measured optimization evidence: bounded 1x5x3 grid
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: additive joint FOV grid, compatibility-preserving paths, plan regression, ready-scene 15-candidate ranking and human inspection
|
||||
- Next: add approved landmark/region scoring before any manifest camera calibration
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: this work-package commit
|
||||
- Results: numeric best FOV 62/yaw 10/pitch -25 at mean 0.078843 and ratio 0.667721 was visually rejected as grass-dominated; FOV 38/yaw 10/pitch -10 better matched building scale at mean 0.084220
|
||||
- Remaining risks: full-frame metric does not preserve landmark framing; no manifest values were changed
|
||||
- Documentation updated: `docs/CAMERA_POSE_SWEEP.md`, `docs/modules/world-renderer.md`
|
||||
@@ -1,6 +1,7 @@
|
||||
# M00-QAR-CAMERA-OCCLUDERS-001 — Camera occluder diagnostic
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-CAMERA-OCCLUDERS-001:sindo-main-codex:2026-07-13 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-CAMERA-OCCLUDERS-001:9d34a47 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# M00-QAR-CAMERA-POSE-EVIDENCE-001 — Goldshire camera pose evidence
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-CAMERA-POSE-EVIDENCE-001:sindo-main-codex:2026-07-14 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-CAMERA-POSE-EVIDENCE-001:eca76c5 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# M00-QAR-CAMERA-POSE-SWEEP-001 — Empirical camera pose sweep
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-CAMERA-POSE-SWEEP-001:sindo-main-codex:2026-07-14 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-CAMERA-POSE-SWEEP-001:657a1d8 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# M00-QAR-CAPTURE-SHUTDOWN-001 — Capture shutdown drain
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-CAPTURE-SHUTDOWN-001:sindo-main-codex:2026-07-14 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-CAPTURE-SHUTDOWN-001:8d4641a -->
|
||||
|
||||
## Ownership
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# M00-QAR-COORD-CALIBRATION-001 — Renderer coordinate calibration
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-COORD-CALIBRATION-001:sindo-main-codex:2026-07-13 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-COORD-CALIBRATION-001:dfc1031 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# M00-QAR-FINAL-PAIRED-EVIDENCE-001 — Final paired evidence audit
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-FINAL-PAIRED-EVIDENCE-001:sindo-main-codex:2026-07-14 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-FINAL-PAIRED-EVIDENCE-001:239cb24 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M00
|
||||
- Program: QAR
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m00-final-paired-evidence`
|
||||
- Lease expires UTC: 2026-07-14
|
||||
- Integrator: milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Produce one final WMO-ready paired report for all available build 12340 references and reconcile the remaining M00 acceptance gaps.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Achieving renderer parity inside M00.
|
||||
- Calibrating unknown manual camera parameters.
|
||||
- Marking the target DONE; only the milestone integrator may do so.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: paired-run exit handling and final evidence record
|
||||
- Shared/hotspots: renderer baseline/module findings
|
||||
- Generated/ignored: final captures and reports under `user://`
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API/events: visual comparison exit 0/1 both count as completed report generation
|
||||
- Schema/format version: unchanged
|
||||
- Migration/compatibility: comparator infrastructure errors remain fatal
|
||||
- Consumers: M00 integrator
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: merged region diagnostic and five private reference images
|
||||
- Blocks: integrator decision on M00 completion
|
||||
- External state: private images remain outside Git
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: complete paired baseline run, report audit, repository gates
|
||||
- Fixtures: five build 12340 references
|
||||
- Fidelity evidence: cold/warm comparison metrics and human gap classification
|
||||
- Performance budget: existing M00 capture budgets
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs: PowerShell comparison step semantics
|
||||
- Module specification: final evidence status
|
||||
- Data-flow diagram: unchanged
|
||||
- Sequence/state/dependency diagrams: unchanged
|
||||
- Source map/status updates: baseline acceptance summary
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: `Invoke-GodotComparisonStep`
|
||||
- Simplest considered solution: allow only comparator exit codes 0 and 1
|
||||
- Rejected complexity/abstractions: suppressing all process failures
|
||||
- Unavoidable complexity and justification: threshold exceedance is evidence, not infrastructure failure
|
||||
- Measured optimization evidence: not applicable
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: paired-run exit contract fixed; full baseline and visual capture completed; ten pairs audited; final acceptance document written
|
||||
- Next: integrator decides animated/sky reference policy and GUI RID-risk disposition
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: this work-package commit
|
||||
- Results: compared=10, failed=10, missing=0; mean error 0.066892..0.176952; cold/warm gaps stable; full workflow returned success with threshold evidence preserved
|
||||
- Remaining risks: no build 12340 animated-M2 or sky-transition reference; GUI renderer RID/resources still reported at exit
|
||||
- Documentation updated: `docs/M00_FINAL_PAIRED_EVIDENCE.md`, `docs/modules/world-renderer.md`
|
||||
@@ -1,6 +1,7 @@
|
||||
# M00-QAR-FOV-SWEEP-001 — Empirical camera FOV sweep
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-FOV-SWEEP-001:sindo-main-codex:2026-07-14 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-FOV-SWEEP-001:f548507 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# M00-QAR-GUI-RID-SHUTDOWN-001 — GUI capture RID shutdown
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-GUI-RID-SHUTDOWN-001:sindo-main-codex:2026-07-15 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-GUI-RID-SHUTDOWN-001:f19c0c4 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M00
|
||||
- Program: QAR
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m00-gui-rid-shutdown`
|
||||
- Lease expires UTC: 2026-07-15
|
||||
- Integrator: milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Identify ownership of GUI render-baseline shutdown leaks and apply the smallest regression-covered fix that lets queued world resources release before engine teardown.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Renderer architecture decomposition.
|
||||
- Hiding or filtering Godot leak diagnostics.
|
||||
- Changing M00 status, Evidence or `OPENWC_TARGET_DONE`.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: capture shutdown sequencing and its regression diagnostic
|
||||
- Shared/hotspots: `src/tools/capture_render_checkpoints.gd`, renderer module shutdown documentation
|
||||
- Generated/ignored: GUI capture output and verbose logs under `user://`
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API/events: unchanged
|
||||
- Schema/format version: unchanged
|
||||
- Migration/compatibility: capture images and report schema remain unchanged
|
||||
- Consumers: M00 baseline runner and milestone integrator
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: merged seven-reference evidence audit
|
||||
- Blocks: integrator disposition of the final GUI renderer leak risk
|
||||
- External state: proprietary references are not required for the focused shutdown regression
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: focused GUI single-checkpoint capture with verbose shutdown, headless project load, manifest/self-tests, repository gates
|
||||
- Fixtures: existing non-proprietary render-baseline manifest and local extracted corpus
|
||||
- Fidelity evidence: no visual behavior change; focused PNG hash/report remains valid
|
||||
- Performance budget: shutdown-only change
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs: shutdown method semantics if introduced
|
||||
- Module specification: shutdown ownership and observed leak status
|
||||
- Data-flow diagram: unchanged
|
||||
- Sequence/state/dependency diagrams: update shutdown sequence only if behavior changes
|
||||
- Source map/status updates: capture tool if changed
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: `_release_runtime_caches_for_shutdown`, `_free_detached_node_cache`, `_queue_free_streamed_node`
|
||||
- Simplest considered solution: explicitly release capture-owned scene references and drain deferred deletion
|
||||
- Rejected complexity/abstractions: global RID registry or suppressed engine diagnostics
|
||||
- Unavoidable complexity and justification: async streaming jobs must finish before resource ownership can drain
|
||||
- Measured optimization evidence: not applicable
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: reproduced the focused leak; assigned detached prototype/resource caches and empty liquid roots to explicit owners; added a headless regression; verified zero Node/resource/RID leak lines in focused and seven-checkpoint GUI captures
|
||||
- Next: integrator review and disposition of the residual engine-level zero-reference `RefCounted` diagnostic
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: this work-package commit
|
||||
- Results: before the fix the focused capture retained 6 mesh, 6 material, 4 shader, 6 texture, 6 instance and RenderingDevice RIDs (30 resources); after the fix focused and seven-checkpoint captures retained none
|
||||
- Remaining risks: D3D12 descriptor fallback remains unrelated; Godot can report a timing-dependent anonymous `RefCounted` whose reference count is already zero, without retained Node/resource/RID ownership
|
||||
- Documentation updated: `RENDER.md`, `docs/modules/world-renderer.md`, `docs/M00_FINAL_PAIRED_EVIDENCE.md`; existing renderer shutdown sequence updated, no new diagram required
|
||||
@@ -0,0 +1,79 @@
|
||||
# M00-QAR-INTEGRATOR-CLOSEOUT-001 — M00 milestone closeout
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-INTEGRATOR-CLOSEOUT-001:sindo-main-codex:2026-07-15 -->
|
||||
<!-- OPENWC_HANDOFF:READY:M00-QAR-INTEGRATOR-CLOSEOUT-001:ba81098 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M00
|
||||
- Program: QAR
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m00-integrator-closeout`
|
||||
- Lease expires UTC: 2026-07-15
|
||||
- Integrator: sindo-main-codex, assigned by the user on 2026-07-13
|
||||
|
||||
## Outcome
|
||||
|
||||
Audit merged M00 work packages, refresh final Evidence, close the milestone and activate M01.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change renderer behavior or compatibility claims.
|
||||
- Start M01 implementation.
|
||||
- Commit proprietary references or generated capture output.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: M00/M01 target status and final M00 Evidence
|
||||
- Shared/hotspots: `targets/README.md`, `targets/DEVELOPMENT_ROADMAP.md`, completed M00 claims
|
||||
- Generated/ignored: `user://` post-merge reports
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API/events: unchanged
|
||||
- Schema/format version: unchanged
|
||||
- Migration/compatibility: none
|
||||
- Consumers: milestone integrator and M01 work packages
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: merged M00 PRs #1 through #15 and final paired evidence
|
||||
- Blocks: M01 activation
|
||||
- External state: private reference images remain outside Git; their hashes and metrics are already recorded
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: post-merge render baseline dry-run, coordination gate, documentation gate
|
||||
- Fixtures: seven-checkpoint non-proprietary manifest and recorded private-reference hashes/metrics
|
||||
- Fidelity evidence: closeout preserves the explicit non-parity classification in `docs/M00_FINAL_PAIRED_EVIDENCE.md`
|
||||
- Performance budget: no runtime change
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs: unchanged
|
||||
- Module specification: unchanged
|
||||
- Data-flow diagram: unchanged
|
||||
- Sequence/state/dependency diagrams: unchanged
|
||||
- Source map/status updates: target index, roadmap current state and M00 Evidence
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: none
|
||||
- Simplest considered solution: status and Evidence-only integrator patch
|
||||
- Rejected complexity/abstractions: new closeout tooling for a one-time milestone transition
|
||||
- Unavoidable complexity and justification: none
|
||||
- Measured optimization evidence: not applicable
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: verified all M00 PRs and merge commits, refreshed final Evidence, closed M00, activated M01 and passed transition gates
|
||||
- Next: review and merge the closeout PR before starting an M01 implementation claim
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `ba81098`
|
||||
- Results: exactly one ACTIVE target (M01); M00 DONE marker accepted; post-merge baseline, comparator self-test, coordination and documentation gates passed
|
||||
- Remaining risks: documented renderer fidelity gaps and D3D12 fallback continue beyond M00
|
||||
- Documentation updated: target status/index, development-roadmap current state and final M00 Evidence
|
||||
@@ -0,0 +1,79 @@
|
||||
# M00-QAR-LANDMARK-REGION-DIFF-001 — Landmark-region visual diff
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-LANDMARK-REGION-DIFF-001:sindo-main-codex:2026-07-14 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-LANDMARK-REGION-DIFF-001:3d90c6d -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M00
|
||||
- Program: QAR
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m00-landmark-region-diff`
|
||||
- Lease expires UTC: 2026-07-14
|
||||
- Integrator: milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Rank camera candidates inside an approved common screen region so unrelated grass/sky coverage cannot dominate landmark framing.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Automatic feature detection or image warping.
|
||||
- Different crops for reference and candidate.
|
||||
- Changing manifest camera values without human approval.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: comparator common-region option and sweep pass-through
|
||||
- Shared/hotspots: camera-pose documentation and renderer fidelity findings
|
||||
- Generated/ignored: local ROI sweep images/reports
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API/events: additive comparator `--region x,y,width,height` and sweep `-ComparisonRegion`
|
||||
- Schema/format version: report/ranking schema remains version 1 with additive region metadata
|
||||
- Migration/compatibility: omitted region preserves full-frame behavior
|
||||
- Consumers: M00 paired camera calibration
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: ready-scene joint pitch/FOV sweep
|
||||
- Blocks: landmark-weighted Goldshire framing decision
|
||||
- External state: private build 12340 reference
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: comparator synthetic ROI regression, invalid-bound regression, sweep plan, bounded real ROI sweep, repository gates
|
||||
- Fixtures: synthetic 2x2 images and private Goldshire reference
|
||||
- Fidelity evidence: facade-region ranking plus human inspection
|
||||
- Performance budget: region comparison visits only selected pixels
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs: comparator usage and PowerShell parameter
|
||||
- Module specification: ROI finding
|
||||
- Data-flow diagram: approved region into comparator
|
||||
- Sequence/state/dependency diagrams: unchanged
|
||||
- Source map/status updates: camera-pose guide
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: `ComparisonRegion`, `comparison_region`
|
||||
- Simplest considered solution: one common integer rectangle
|
||||
- Rejected complexity/abstractions: masks, feature detectors, separate movable crops
|
||||
- Unavoidable complexity and justification: full-frame color error is biased by large terrain regions
|
||||
- Measured optimization evidence: bounded pixel loop
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: common ROI parser/validation/reporting, synthetic inclusion/exclusion regression, sweep pass-through, 15-candidate Goldshire rescoring and human inspection
|
||||
- Next: define explicit approved landmark coordinates or mask; do not calibrate manifest from the common rectangle alone
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: this work-package commit
|
||||
- Results: ROI [300,100,1400,650] ranked FOV 38/yaw 10/pitch -15 first at mean 0.081777 and ratio 0.697185; visually the facade remains oversized and cropped
|
||||
- Remaining risks: rectangle includes non-semantic road/terrain pixels and cannot prove landmark alignment
|
||||
- Documentation updated: `docs/CAMERA_POSE_SWEEP.md`, `docs/modules/world-renderer.md`
|
||||
@@ -1,6 +1,7 @@
|
||||
# M00-QAR-TERRAIN-HEIGHT-001 — Terrain height diagnostic
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-TERRAIN-HEIGHT-001:sindo-main-codex:2026-07-13 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-TERRAIN-HEIGHT-001:f8538ba -->
|
||||
|
||||
## Ownership
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# M00-QAR-TILE-OWNERSHIP-001 — Waterfall terrain tile ownership
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-TILE-OWNERSHIP-001:sindo-main-codex:2026-07-13 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-TILE-OWNERSHIP-001:2306200 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# M00-QAR-VISUAL-DIFF-001 — Renderer checkpoint visual diff
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-VISUAL-DIFF-001:sindo-main-codex:2026-07-13 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-VISUAL-DIFF-001:19df5b7 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# M00-QAR-WMO-READINESS-001 — Goldshire WMO readiness
|
||||
|
||||
<!-- OPENWC_CLAIM:M00-QAR-WMO-READINESS-001:sindo-main-codex:2026-07-14 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-WMO-READINESS-001:5092995 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M00
|
||||
- Program: QAR
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m00-wmo-readiness`
|
||||
- Lease expires UTC: 2026-07-14
|
||||
- Integrator: milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Prevent pose calibration against an under-loaded WMO scene and replace invalid Goldshire evidence with a baseline-ready comparison.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Changing runtime streaming budgets.
|
||||
- Claiming all WMO queues must globally drain before a checkpoint.
|
||||
- Changing manifest camera pose without valid paired evidence.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: pose-sweep readiness default and Goldshire timing evidence
|
||||
- Shared/hotspots: renderer baseline/module fidelity findings
|
||||
- Generated/ignored: timing and sweep images under `user://`
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API/events: pose sweep default wait becomes 8 seconds, matching the M00 manifest
|
||||
- Schema/format version: unchanged
|
||||
- Migration/compatibility: explicit `-WaitSeconds` remains supported
|
||||
- Consumers: M00 paired-fidelity workflow
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: Goldshire pose evidence and private build 12340 reference
|
||||
- Blocks: valid Goldshire camera-pose ranking
|
||||
- External state: private reference and generated captures remain outside Git
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: 2/8/15 second timing audit, ready-scene 3x3 pose sweep, baseline dry-run, repository gates
|
||||
- Fixtures: private Goldshire Inn screenshot
|
||||
- Fidelity evidence: visual WMO presence plus streaming snapshot and paired metrics
|
||||
- Performance budget: offline diagnostic
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs: PowerShell default
|
||||
- Module specification: corrected readiness/fidelity finding
|
||||
- Data-flow diagram: readiness precondition
|
||||
- Sequence/state/dependency diagrams: unchanged
|
||||
- Source map/status updates: camera-pose operational guide
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: none
|
||||
- Simplest considered solution: reuse the manifest's measured 8-second stabilization default
|
||||
- Rejected complexity/abstractions: global queue barrier that would include unrelated distant work
|
||||
- Unavoidable complexity and justification: WMO publication is asynchronous
|
||||
- Measured optimization evidence: 2/8/15 second snapshots
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: timing audit invalidated the 2-second evidence; sweep default now matches the 8-second manifest; ready-scene 3x3 grid and human inspection completed
|
||||
- Next: jointly refine negative pitch and FOV around the ready-scene Goldshire candidate
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: this work-package commit
|
||||
- Results: WMO instances 9/113/328 at 2/8/15 seconds; inn absent at 2 and visible at 8/15; ready grid improved mean error from 0.101402 at (0,0) to 0.087952 at (10,-10)
|
||||
- Remaining risks: global WMO queues still contain unrelated work; best pitch is at the tested boundary and building scale indicates unresolved FOV/framing
|
||||
- Documentation updated: `docs/CAMERA_POSE_SWEEP.md`, `docs/modules/world-renderer.md`
|
||||
@@ -0,0 +1,131 @@
|
||||
# M01-FND-COORD-BOUNDARY-GATE-001 — Coordinate conversion boundary gate
|
||||
|
||||
<!-- OPENWC_CLAIM:M01-FND-COORD-BOUNDARY-GATE-001:sindo-main-codex:2026-07-15 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M01-FND-COORD-BOUNDARY-GATE-001:c8e99f2 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M01
|
||||
- Program: FND/QAR
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m01-coordinate-boundary-gate`
|
||||
- Lease expires UTC: 2026-07-15
|
||||
- Integrator: M01 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Route remaining GDScript world/tile axis conversions through `CoordinateMapper`
|
||||
and add a repository-wide gate that rejects new manual world-center, axis-swap
|
||||
or world-to-ADT tile formulas outside explicit contract/native-oracle boundaries.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Rewriting model-local M2/WMO vertex or placement orientation transforms.
|
||||
- Moving renderer distance/LOD geometry out of Godot space.
|
||||
- Replacing the native parser boundary with GDScript calls from worker threads.
|
||||
- Changing coordinate contract semantics, renderer radii or cache formats.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/tools/verify_coordinate_conversion_boundaries.gd`
|
||||
- Shared/hotspots: `src/domain/coordinates/coordinate_mapper.gd`, coordinate
|
||||
tests/docs/ADR, streamer/player/sky/terrain-probe consumers,
|
||||
`docs/CODING_STANDARD.md`, this claim
|
||||
- Generated/ignored: Godot import metadata, extracted assets and renderer caches
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Additive mapper methods: Godot position to ADT tile/local and ADT tile/local
|
||||
to Godot position
|
||||
- Native exception: one formula definition in `src/native/src/wow_chunk_reader.h`;
|
||||
ADT/WDT loaders may call it but may not define copies
|
||||
- Oracle exception: `verify_render_coordinate_calibration.gd` retains one
|
||||
independent M00 formula to detect common-mode mapper regressions
|
||||
- Model-local transforms are outside the world-coordinate gate
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: coordinate contract v2 and M01 seams through `7c64e8d`
|
||||
- Blocks: M01 coordinate-centralization exit criterion and milestone evidence audit
|
||||
- External state: no dependency or proprietary input
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: conversion-boundary gate, mapper/golden fixtures, streaming focus,
|
||||
identities, renderer calibration/manifest and repository gates
|
||||
- Fixtures: Godot/ADT tile center, local offsets and exact boundaries
|
||||
- Fidelity evidence: existing build-12340 points and raw ADT/server fixture remain unchanged
|
||||
- Performance budget: mapper allocations occur only at adapter/update boundaries;
|
||||
renderer distance/LOD math remains in Godot scalars
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs: new mapper methods
|
||||
- Module specification/ADR: boundary APIs, allowed exceptions and gate behavior
|
||||
- Coding standard: mandatory review rule for cross-space conversions
|
||||
- Data-flow diagram: existing coordinate module diagram updated for direct adapters
|
||||
- State/sequence diagrams: unchanged; pure synchronous mapping
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: `godot_to_adt_tile`, `godot_to_adt_tile_local`,
|
||||
`adt_tile_local_to_godot`, `verify_coordinate_conversion_boundaries`
|
||||
- Simplest considered solution: extend the existing mapper and scan source text
|
||||
for narrow high-signal formula signatures
|
||||
- Rejected complexity/abstractions: generic coordinate algebra framework,
|
||||
native-to-GDScript calls on parser workers and a broad numeric-literal ban
|
||||
- Unavoidable complexity and justification: one native boundary and one
|
||||
independent test oracle must remain explicit to avoid worker coupling and common-mode tests
|
||||
- Measured optimization evidence: existing renderer distance/LOD scalar math is unchanged
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: mapper extensions, consumer migration, repository gate, review rule,
|
||||
integrated renderer gate, regression fixtures and documentation
|
||||
- Next: integrator review, merge and M01 completion-evidence audit
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `47dec6c`
|
||||
- Branch: `work/sindo-main-codex/m01-coordinate-boundary-gate`
|
||||
- Outcome: all audited GDScript world/tile conversions in sky, streaming,
|
||||
player spawn/ground sampling and terrain probes route through typed
|
||||
`CoordinateMapper` boundaries. A repository scan rejects new manual
|
||||
world-center, legacy axis-conversion, world-to-tile, tile-local and
|
||||
tile-center formulas.
|
||||
- Contract/schema: additive `godot_to_adt_tile`,
|
||||
`godot_to_adt_tile_local` and `adt_tile_local_to_godot` APIs; no persisted
|
||||
format, cache version or renderer radius changes.
|
||||
- Verification: CoordinateConversionBoundaries PASS (`83` files, one native
|
||||
boundary, one independent oracle, four consumers, six classifier guards);
|
||||
CoordinateMapper PASS (`5` golden points, `8` tile boundaries, `2` raw MCNK,
|
||||
`5` yaw cases); renderer calibration PASS (`5` points, maximum error
|
||||
`0.000015`); renderer manifest PASS (`7/7/7`); unified render baseline dry-run,
|
||||
headless project load, runtime cache shutdown, coordination, documentation
|
||||
and whitespace gates passed.
|
||||
- GUI regression: final ADT-boundary capture compared `2/2`, missing `0`,
|
||||
failed `0` (cold mean `0.0002324`, changed ratio `0.0013741`; warm mean
|
||||
`0.0000226`, changed ratio `0.0001285`). Earlier full capture produced all
|
||||
`14` images; `11/14` met the strict changed-pixel threshold. The three
|
||||
threshold-only differences retained low mean error (`<= 0.00622`) and were
|
||||
visually classified as cold M2 readiness and liquid animation phase, with
|
||||
unchanged camera, geometry alignment, area and streaming focus.
|
||||
- Fidelity: existing build-12340 calibrated points, raw ADT fixture and server
|
||||
spawn fixture remain unchanged. Native parser workers retain exactly one
|
||||
formula in `wow_chunk_reader.h`; the calibration verifier retains exactly one
|
||||
independent formula so mapper and oracle cannot share a common-mode defect.
|
||||
- Rebuild: none. Local ignored extracted assets/native binary/cache junctions
|
||||
were used for runtime checks and are not part of the commit.
|
||||
- Remaining risks: source scanning intentionally recognizes narrow high-signal
|
||||
formula signatures rather than parsing arbitrary algebra; native formula
|
||||
parity still relies on calibration/raw fixtures. Model-local M2/WMO basis
|
||||
transforms and Godot-space LOD distance math are intentionally outside scope.
|
||||
- Recommended merge order: after `7c64e8d`; this package closes the remaining
|
||||
M01 manual axis-conversion checklist item, but only the milestone integrator
|
||||
may update target Evidence/status or set `OPENWC_TARGET_DONE`.
|
||||
- Documentation: coding review rule, coordinate-mapping module API/input-output/
|
||||
data-flow/capability/risk/source-map sections, ADR 0001 and `RENDER.md` were
|
||||
updated. Existing pure synchronous data-flow diagram remains authoritative;
|
||||
no state/sequence diagram changed.
|
||||
@@ -0,0 +1,104 @@
|
||||
# M01-FND-COORD-TILE-AXIS-002 — ADT filename axis correction
|
||||
|
||||
<!-- OPENWC_CLAIM:M01-FND-COORD-TILE-AXIS-002:sindo-main-codex:2026-07-15 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M01-FND-COORD-TILE-AXIS-002:c8e99f2 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M01
|
||||
- Program: FND
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m01-coordinate-tile-axis-fix`
|
||||
- Lease expires UTC: 2026-07-15
|
||||
- Integrator: M01 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Correct the additive coordinate contract so `AdtTileCoordinate(tile_x, tile_y)`
|
||||
matches actual `Map_tileX_tileY.adt` filenames and MCNK `indexX/indexY` axes.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Migrating renderer/native consumers.
|
||||
- Changing canonical, server, Godot or ADT placement position arithmetic.
|
||||
- Adding server-spawn fixtures or `StreamingFocus`.
|
||||
- Committing proprietary ADT bytes.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/domain/coordinates/coordinate_mapper.gd`,
|
||||
`src/domain/coordinates/adt_chunk_coordinate.gd`,
|
||||
`src/tools/verify_coordinate_mapper.gd`
|
||||
- Shared/hotspots: `docs/adr/0001-canonical-world-coordinates.md`,
|
||||
`docs/modules/coordinate-mapping.md`, this claim
|
||||
- Generated/ignored: local extracted ADT and Godot import metadata
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API/events: method names unchanged; corrected tile/chunk axis semantics
|
||||
- Schema/format version: coordinate contract version increments from 1 to 2
|
||||
- Migration/compatibility: no released consumer exists; contract was merged but
|
||||
not yet consumed, so correction precedes consumer rollout
|
||||
- Consumers: future renderer, network, gameplay and server-data adapters
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: merged `M01-FND-COORDS-001` at `1c0dfc7`
|
||||
- Blocks: coordinate golden fixtures and all coordinate consumers
|
||||
- External state: private `Azeroth_31_49.adt` used read-only for evidence
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: coordinate mapper probe, M00 calibration, manifest, documentation
|
||||
and coordination gates
|
||||
- Fixtures: build 12340 Elwynn point must resolve to `Azeroth_31_49.adt`;
|
||||
MCNK `(0,0)` and `(15,15)` establish chunk-axis direction
|
||||
- Fidelity evidence: local ADT SHA-256
|
||||
`18185B5FAC9566D9C57A9C19ABD75F108370D1795639B6077167C0EFD3672FAE`;
|
||||
OpenRealm independently indexes filename X from world Y and filename Y from world X
|
||||
- Performance budget: unchanged constant-time scalar arithmetic
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs: corrected tile/chunk direction comments
|
||||
- Module specification: contract version, formulas, capability evidence and risk
|
||||
- Data-flow diagram: corrected filename/chunk axes
|
||||
- ADR: corrected decision and evidence
|
||||
- Sequence/state diagrams: not applicable; pure synchronous mapper
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: none
|
||||
- Simplest considered solution: swap the two filename formulas and corresponding
|
||||
local/chunk ownership calculations
|
||||
- Rejected complexity/abstractions: compatibility flag for a contract with no consumers
|
||||
- Unavoidable complexity and justification: ADT filename axes differ from client X/Y order
|
||||
- Measured optimization evidence: not applicable
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: raw-corpus diagnosis, filename/local/chunk axis correction, contract
|
||||
version 2 documentation and regression fixtures
|
||||
- Next: integrator review and merge before coordinate consumer work
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `a7072e3`
|
||||
- Branch: `work/sindo-main-codex/m01-coordinate-tile-axis-fix`
|
||||
- Outcome: `tile_x/chunk_x` now grow east from canonical Y and
|
||||
`tile_y/chunk_y` grow south from canonical X; public method names unchanged
|
||||
- Contract/schema: coordinate contract version 2; no persisted format, cache or migration
|
||||
- Verification: coordinate mapper PASS (5 build-12340 points, 8 boundaries,
|
||||
2 recorded raw MCNK origins, 5 yaw cases); M00 calibration PASS with maximum
|
||||
error `0.000015`; renderer manifest, coordination, documentation, dependency
|
||||
and whitespace checks passed
|
||||
- Fidelity: private `Azeroth_31_49.adt` hash and raw MCNK origins recorded in
|
||||
ADR 0001; OpenRealm independently uses the same filename-axis ordering
|
||||
- Rebuild: none; private/generated prerequisites remain outside Git
|
||||
- Remaining risks: server spawn fixture, native placement conversion and
|
||||
MDDF/MODF Euler semantics remain subsequent M01 packages
|
||||
- Recommended merge order: merge this correction before golden fixture or consumer migration
|
||||
- Documentation: ADR 0001 and coordinate-mapping module data flow, contract
|
||||
version, capability evidence and known risks updated
|
||||
@@ -0,0 +1,111 @@
|
||||
# M01-FND-COORDS-001 — Canonical coordinate contracts
|
||||
|
||||
<!-- OPENWC_CLAIM:M01-FND-COORDS-001:sindo-main-codex:2026-07-15 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M01-FND-COORDS-001:c8e99f2 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M01
|
||||
- Program: FND
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m01-coordinate-mapper`
|
||||
- Lease expires UTC: 2026-07-15
|
||||
- Integrator: M01 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Introduce scene-free value objects for canonical WoW, server, ADT placement,
|
||||
ADT tile/chunk/local and Godot world coordinate spaces, plus the single pure
|
||||
`CoordinateMapper` contract for position and world-yaw conversions.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Migrating `StreamingWorldLoader`, native parsers, sky, player or renderer code.
|
||||
- Defining model-specific MDDF/MODF Euler rotation corrections.
|
||||
- Introducing `StreamingFocus`, typed IDs or a test framework dependency.
|
||||
- Claiming visual parity from arithmetic round trips.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/domain/coordinates/**`, `src/tools/verify_coordinate_mapper.gd`,
|
||||
`docs/modules/coordinate-mapping.md`, `docs/adr/0001-canonical-world-coordinates.md`
|
||||
- Shared/hotspots: `docs/modules/README.md`, this claim
|
||||
- Generated/ignored: Godot import metadata and local test output
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API/events: immutable coordinate value objects and pure mapper methods
|
||||
- Schema/format version: no persisted format; contract version 1 in module docs
|
||||
- Migration/compatibility: additive contract; existing renderer conversions remain
|
||||
unchanged until a later consumer migration package
|
||||
- Consumers: future renderer, network, gameplay and server-data adapters
|
||||
- Rollout order: contract and fixtures first, then native/parser and renderer consumers
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: merged M00 build 12340 coordinate calibration evidence
|
||||
- Blocks: M01 renderer coordinate migration and `StreamingFocus`
|
||||
- External state: no proprietary inputs; reference formulas are source-only evidence
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: headless coordinate contract probe, headless project load,
|
||||
coordination and documentation gates
|
||||
- Fixtures: five build 12340 camera points, ADT map center/edge/boundary points,
|
||||
server/ADT/Godot round trips and yaw normalization cases
|
||||
- Fidelity evidence: M00 observed build 12340 positions plus independent
|
||||
OpenRealm/WoWee and TrinityCore grid formulas
|
||||
- Performance budget: constant-time scalar arithmetic with no allocation beyond result values
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs: every public value object and mapper method
|
||||
- Module specification: purpose, public API, inputs/outputs, ownership, errors and source map
|
||||
- Data-flow diagram: server/ADT/canonical/Godot mappings
|
||||
- Dependency diagram: canonical contract consumers and forbidden scene dependency
|
||||
- Sequence/state diagrams: not applicable; mapper is synchronous and stateless
|
||||
- ADR: coordinate axes, units, tile indexing and compatibility rollout
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: `CanonicalWowWorldPosition`, `ServerWorldPosition`,
|
||||
`AdtPlacementPosition`, `AdtTileCoordinate`, `AdtChunkCoordinate`,
|
||||
`AdtTileLocalPosition`, `GodotWorldPosition`, `WorldYaw`, `CoordinateMapper`
|
||||
- Simplest considered solution: scalar immutable value objects and one pure mapper
|
||||
- Rejected complexity/abstractions: generic vector-space framework, matrices,
|
||||
dependency injection and a third-party test framework
|
||||
- Unavoidable complexity and justification: distinct source spaces prevent silent axis swaps
|
||||
- Measured optimization evidence: not applicable
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: repository/reference audit, ADR, immutable value objects, pure mapper,
|
||||
build 12340/boundary/yaw fixtures, module specification and repository gates
|
||||
- Next: integrator review and merge before renderer/native consumer migration
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `f45695c`
|
||||
- Branch: `work/sindo-main-codex/m01-coordinate-mapper`
|
||||
- Outcome: additive contract for canonical/server/ADT/Godot positions, ADT
|
||||
tile/chunk/local ownership and world-facing yaw; no existing consumer changed
|
||||
- Public contracts: contract version 1 documented in
|
||||
`docs/modules/coordinate-mapping.md`; no persisted schema or cache change
|
||||
- Verification: coordinate mapper PASS (5 golden points, 8 tile boundaries,
|
||||
5 yaw cases); M00 coordinate calibration PASS (5 points, maximum error
|
||||
`0.000015`); renderer manifest, coordination, documentation, dependency and
|
||||
whitespace checks passed; headless project load completed with expected
|
||||
missing proprietary-data diagnostics in the isolated worktree
|
||||
- Fidelity: observed build 12340 positions match within `0.002` yard; formulas
|
||||
cross-checked against TrinityCore, OpenRealm and WoWee; visual parity is not claimed
|
||||
- Migration/rebuild: none for this additive package; consumers migrate only
|
||||
after merge and must preserve renderer checkpoints
|
||||
- Remaining risks: native loaders and renderer still use manual conversions;
|
||||
MDDF/MODF Euler mapping is intentionally unresolved; server-spawn integration
|
||||
and finite-value adapter validation remain later M01 packages
|
||||
- Recommended merge order: this contract first, coordinate fixtures/server-spawn
|
||||
audit second, renderer/native consumers third, manual-conversion search gate last
|
||||
- Documentation: ADR 0001; coordinate-mapping module spec with data-flow and
|
||||
dependency diagrams; inline API documentation on all public symbols
|
||||
@@ -0,0 +1,112 @@
|
||||
# M01-FND-DOMAIN-IDENTITIES-001 — Stable domain identity namespaces
|
||||
|
||||
<!-- OPENWC_CLAIM:M01-FND-DOMAIN-IDENTITIES-001:sindo-main-codex:2026-07-15 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M01-FND-DOMAIN-IDENTITIES-001:c8e99f2 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M01
|
||||
- Program: FND
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m01-domain-identities`
|
||||
- Lease expires UTC: 2026-07-15
|
||||
- Integrator: M01 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Add scene-free, immutable identity values for stable authored content,
|
||||
session-local entities, opaque WoW wire GUIDs and namespaced server entries so
|
||||
callers cannot silently interchange their numeric/string representations.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Allocating Content IDs, Entity IDs or server entries.
|
||||
- Decoding WoW GUID high types before the build-12340 protocol package.
|
||||
- Defining map/spell/item/quest/display/sound IDs in this package.
|
||||
- Migrating gameplay, network or Content Project consumers that do not exist yet.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/domain/identity/`, `src/tools/verify_domain_identities.gd`,
|
||||
`docs/modules/domain-identities.md`, `docs/adr/0002-domain-identity-namespaces.md`
|
||||
- Shared/hotspots: `docs/ARCHITECTURE.md`, this claim
|
||||
- Generated/ignored: Godot import metadata
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- `ContentId`: canonical lowercase UUID text persisted with schema-owned content
|
||||
- `EntityId`: positive session-local integer, never a persisted content/server identity
|
||||
- `WowGuid`: opaque high/low unsigned 32-bit words preserving wire bits
|
||||
- `ServerEntryId`: positive integer plus non-empty adapter/table namespace
|
||||
- Migration: additive types only; no existing persisted data changes
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: M01 coordinate/focus seams through `77cd17a`
|
||||
- Blocks: Content Project schemas, server adapters, network entity mapping and gameplay registry
|
||||
- External state: no dependency or proprietary input
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: identity contract/boundary verifier, dependency search,
|
||||
coordination/documentation gates and headless project load
|
||||
- Fixtures: valid/invalid UUIDs, session IDs, full unsigned GUID words and
|
||||
duplicate numeric server entries in different namespaces
|
||||
- Fidelity evidence: GUID words remain opaque and lossless until build-12340
|
||||
packet fixtures establish semantic decomposition
|
||||
- Performance budget: constant-time scalar/string validation outside frame loops
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs: every value and validation/equality method
|
||||
- Module specification: persistence, ownership, namespace mapping and recovery
|
||||
- Data-flow diagram: authored/runtime/wire/server identities into adapters
|
||||
- State/sequence diagrams: lifecycle diagram for content/session/wire/server identity
|
||||
- ADR: namespace separation and UUID persistence decision
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: `ContentId`, `EntityId`, `WowGuid`, `ServerEntryId`
|
||||
- Simplest considered solution: four independent values with explicit fields
|
||||
- Rejected complexity/abstractions: generic ID base class, registry service,
|
||||
UUID generator and speculative GUID high-type enum
|
||||
- Unavoidable complexity and justification: server entry namespace and split GUID
|
||||
words prevent collisions and signed 64-bit loss
|
||||
- Measured optimization evidence: not applicable
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: four identity values, boundary verifier, ADR, module specification,
|
||||
architecture mapping and repository gates
|
||||
- Next: integrator review and merge before the manual axis-conversion gate
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `c1dc07c`
|
||||
- Branch: `work/sindo-main-codex/m01-domain-identities`
|
||||
- Outcome: authored content, runtime entities, WoW wire GUIDs and namespaced
|
||||
server entries now have separate immutable scene-free value objects
|
||||
- Contract/schema: identity contract version 1; `ContentId` uses canonical
|
||||
lowercase UUID text. No existing schema/cache is migrated; containing future
|
||||
schemas still require their own version and duplicate/reference policy
|
||||
- Verification: DomainIdentities PASS (`5` ContentId, `5` EntityId, `6` WowGuid,
|
||||
`5` ServerEntryId cases); StreamingFocus, coordinate golden fixtures and
|
||||
CoordinateMapper PASS; M00 calibration PASS with maximum error `0.000015`;
|
||||
renderer manifest PASS (`7` checkpoints); headless project load exit `0`;
|
||||
coordination, documentation, naming/dependency and whitespace checks passed
|
||||
- Fidelity: `WowGuid` preserves two complete unsigned 32-bit wire words and
|
||||
explicitly does not claim build-12340 high-type semantics without packet
|
||||
fixtures; zero GUID remains an uninterpreted observable sentinel
|
||||
- Rebuild: none; no dependency or proprietary input. Clean-worktree project
|
||||
load reports expected missing ignored extracted assets/character resources
|
||||
- Remaining risks: no ID allocator, mapping registry or server namespace
|
||||
vocabulary exists yet; UUID shape does not enforce generator version/variant;
|
||||
GDScript callers must use domain `equals` or canonical keys rather than
|
||||
reference `==`
|
||||
- Recommended merge order: after StreamingFocus at `77cd17a`, before the M01
|
||||
manual axis-conversion rule/search gate
|
||||
- Documentation: ADR 0002, domain-identities module API/input-output/data-flow/
|
||||
lifecycle/persistence/recovery/capability/source-map sections, module registry
|
||||
and architecture Domain mapping added or updated
|
||||
@@ -0,0 +1,109 @@
|
||||
# M01-QAR-COORD-FIXTURES-001 — Coordinate golden fixtures
|
||||
|
||||
<!-- OPENWC_CLAIM:M01-QAR-COORD-FIXTURES-001:sindo-main-codex:2026-07-15 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M01-QAR-COORD-FIXTURES-001:c8e99f2 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M01
|
||||
- Program: QAR
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m01-coordinate-fixtures`
|
||||
- Lease expires UTC: 2026-07-15
|
||||
- Integrator: M01 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Add a versioned, non-proprietary fixture and headless validator joining one
|
||||
pinned AzerothCore player spawn, raw ADT/MCNK metadata and the five M00
|
||||
original-client coordinate observations through coordinate contract version 2.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Committing ADT files, SQL dumps or original-client screenshots.
|
||||
- Claiming TrinityCore spawn equivalence without a pinned populated TDB source.
|
||||
- Migrating renderer/native consumers or rendering a live server spawn.
|
||||
- Changing coordinate formulas, IDs or `StreamingFocus`.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/tests/fixtures/coordinate_golden_points.json`,
|
||||
`src/tools/verify_coordinate_golden_fixtures.gd`
|
||||
- Shared/hotspots: `docs/modules/coordinate-mapping.md`, this claim
|
||||
- Generated/ignored: extracted ADT data, server database downloads and Godot metadata
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API/events: no new production API; fixture schema version 1
|
||||
- Coordinate contract: version 2, unchanged
|
||||
- Migration/compatibility: additive test data; fixture consumers reject unknown schema versions
|
||||
- Consumers: M01 coordinate mapper, future native/renderer/server adapter migrations
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: merged coordinate contract version 2 at `b55bfca`
|
||||
- Blocks: coordinate consumer migration and golden server-spawn renderer integration
|
||||
- External state: source evidence reduced to pinned revision, numeric rows and SHA-256 hashes
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: fixture validator, coordinate mapper probe, M00 calibration,
|
||||
manifest, coordination and documentation gates
|
||||
- Fixtures: AzerothCore Human Warrior start; raw `Azeroth_32_48.adt` MCNK
|
||||
`(3,12)`; five build 12340 camera points
|
||||
- Fidelity evidence: exact source provenance and mapper outputs with bounded tolerances
|
||||
- Performance budget: negligible headless JSON and scalar arithmetic
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs: fixture-validator header and field validation functions
|
||||
- Module specification: fixture schema, provenance, verification and source map
|
||||
- Data-flow diagram: server/raw/client fixtures into mapper assertions
|
||||
- State/sequence/dependency diagrams: unchanged; synchronous stateless validation
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: `coordinate_golden_points`,
|
||||
`verify_coordinate_golden_fixtures`
|
||||
- Simplest considered solution: one JSON fixture and one existing-style headless script
|
||||
- Rejected complexity/abstractions: test framework dependency, SQL parser, ADT bytes in Git
|
||||
- Unavoidable complexity and justification: three independent source categories need explicit provenance
|
||||
- Measured optimization evidence: not applicable
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: pinned source research, schema 1 fixture, headless validator, module
|
||||
documentation and all package gates
|
||||
- Next: integrator review and merge before coordinate consumer migration
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `ce4f7ec`
|
||||
- Branch: `work/sindo-main-codex/m01-coordinate-fixtures`
|
||||
- Outcome: one versioned fixture now joins a pinned AzerothCore Human Warrior
|
||||
spawn, private raw MCNK metadata and five original-client build 12340 points
|
||||
through coordinate contract version 2
|
||||
- Contract/schema: additive test fixture schema 1; production coordinate API and
|
||||
contract version 2 are unchanged; proprietary bytes remain outside Git
|
||||
- Verification: golden fixture PASS (`1` server spawn, `1` raw ADT chunk, `5`
|
||||
original-client points); CoordinateMapper PASS (`5` golden points, `8` tile
|
||||
boundaries, `2` raw MCNK cases, `5` yaw cases); M00 calibration PASS with
|
||||
maximum error `0.000015`; renderer manifest PASS (`7` checkpoints); coordination,
|
||||
documentation, dependency and whitespace checks passed
|
||||
- Fidelity: AzerothCore source pinned at revision
|
||||
`68fcf0098b16388093989627f37be530fc91f07d`; private
|
||||
`Azeroth_32_48.adt` SHA-256
|
||||
`D66E27FD82B461AE38B78562952AED428E619777A3F3E35DD8FF97444644770A`;
|
||||
server and raw data independently resolve tile `(32,48)` and chunk `(3,12)`
|
||||
- Rebuild: no proprietary input is required to rerun the committed fixture;
|
||||
local ignored renderer resources are still required by the inherited M00
|
||||
manifest probe
|
||||
- Remaining risks: TrinityCore equivalence needs a pinned populated TDB snapshot;
|
||||
live server adapter, renderer integration and MDDF/MODF Euler semantics remain
|
||||
subsequent M01 work
|
||||
- Recommended merge order: after coordinate contract version 2 (`b55bfca`) and
|
||||
before live coordinate consumer migration
|
||||
- Documentation: coordinate-mapping module inputs/outputs, persistence, data-flow
|
||||
diagram, verification, capability matrix, risks and source map updated
|
||||
@@ -0,0 +1,101 @@
|
||||
# M01-QAR-INTEGRATOR-CLOSEOUT-001 — M01 milestone closeout
|
||||
|
||||
<!-- OPENWC_CLAIM:M01-QAR-INTEGRATOR-CLOSEOUT-001:sindo-main-codex:2026-07-16 -->
|
||||
<!-- OPENWC_HANDOFF:READY:M01-QAR-INTEGRATOR-CLOSEOUT-001:dd412e6 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M01
|
||||
- Program: QAR
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m01-integrator-closeout`
|
||||
- Lease expires UTC: 2026-07-16
|
||||
- Integrator: sindo-main-codex, continuing the user-directed milestone workflow on 2026-07-14
|
||||
|
||||
## Outcome
|
||||
|
||||
Audit every merged M01 work package on current `master`, record final evidence,
|
||||
close M01 and activate M02 only if all target verification and documentation
|
||||
criteria pass.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change coordinate, identity, streaming or renderer behavior.
|
||||
- Start M02 implementation.
|
||||
- Add new compatibility or visual-parity claims.
|
||||
- Commit proprietary assets, generated UIDs, caches or capture output.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: M01/M02 target status, M01 final Evidence and M01 claim acceptance markers
|
||||
- Shared/hotspots: `targets/README.md`, `targets/DEVELOPMENT_ROADMAP.md`, this claim
|
||||
- Generated/ignored: local Godot imports, renderer corpus and `user://` reports
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public APIs, schemas, cache formats and migrations: unchanged
|
||||
- Coordinate contract remains version 2
|
||||
- Fixture schema remains version 1
|
||||
- Consumers: milestone sequence and M02 work packages
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: all seven M01 READY commits integrated through master `c8e99f2`
|
||||
- Blocks: M02 activation
|
||||
- External state: server-spawn GUI hashes and original-client numeric evidence are recorded; proprietary files remain outside Git
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: all M01 domain/fixture/boundary/focus/identity/spawn gates,
|
||||
unified renderer dry-run, coordination/documentation checks and diff hygiene
|
||||
- Fidelity evidence: five build-12340 points, raw ADT metadata, pinned
|
||||
AzerothCore spawn and cache-complete GUI marker capture
|
||||
- Performance: no runtime change; preserve recorded renderer cold/warm evidence
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- M01 final target Evidence and checklist
|
||||
- M01 claim integration-acceptance markers
|
||||
- Target index and roadmap transition to M02
|
||||
- API/module specifications and diagrams are audited, not changed unless a gap is found
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: none beyond the closeout work-package ID
|
||||
- Simplest approach: evidence/status-only integrator patch
|
||||
- Rejected complexity: new one-off closeout framework or behavioral changes
|
||||
- Unavoidable complexity: none
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: accepted seven M01 packages, reran post-merge gates, recorded complete
|
||||
target Evidence, closed M01 and activated M02
|
||||
- Next: merge closeout commit before starting an M02 implementation claim
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `dd412e6`
|
||||
- Branch: `work/sindo-main-codex/m01-integrator-closeout`
|
||||
- Outcome: M01 is `DONE` with all seven checklist items and complete Evidence;
|
||||
M02 is the only `ACTIVE` target and `targets/README.md` points to it.
|
||||
- Integration audit: all seven M01 READY commits are ancestors of master
|
||||
`c8e99f2`; their claims now carry `OPENWC_INTEGRATION:ACCEPTED` markers.
|
||||
- Verification: unified renderer dry-run passed project load, material/dedupe/
|
||||
shutdown gates, manifest `7/7/7`, calibration maximum error `0.000015`,
|
||||
coordinate boundary (`84` files) and server-spawn renderer `(32,48)/(3,12)`;
|
||||
coordinate mapper, golden fixtures, StreamingFocus and domain identity gates
|
||||
passed. Coordination and documentation gates passed; coordination reports
|
||||
only five already-expired M00 claim warnings.
|
||||
- Fidelity: closeout preserves five build-12340 coordinate observations, raw ADT
|
||||
evidence, pinned AzerothCore spawn mapping and cache-complete Northshire GUI
|
||||
evidence without claiming original-client visual parity or TrinityCore row
|
||||
equivalence.
|
||||
- Rebuild/migration: none. Coordinate contract remains version 2; fixture schema
|
||||
remains version 1; renderer cache formats are unchanged.
|
||||
- Remaining risks: recorded in M01 Evidence—TrinityCore populated spawn,
|
||||
placement Euler parity, native parser boundary and broader renderer parity.
|
||||
- Documentation: M01 target Evidence/checklist, target index and roadmap current
|
||||
state updated. Existing coordinate, identity and renderer module specs/API/
|
||||
diagrams were audited and passed the documentation gate.
|
||||
@@ -0,0 +1,135 @@
|
||||
# M01-QAR-SERVER-SPAWN-RENDERER-001 — Golden server spawn renderer evidence
|
||||
|
||||
<!-- OPENWC_CLAIM:M01-QAR-SERVER-SPAWN-RENDERER-001:sindo-main-codex:2026-07-15 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M01-QAR-SERVER-SPAWN-RENDERER-001:c8e99f2 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M01
|
||||
- Program: QAR/RND
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m01-server-spawn-renderer`
|
||||
- Lease expires UTC: 2026-07-15
|
||||
- Integrator: M01 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Route the pinned AzerothCore Human Warrior spawn fixture through
|
||||
`CoordinateMapper` into the live Eastern Kingdoms renderer and produce
|
||||
repeatable headless contract checks plus GUI screenshot evidence at the mapped
|
||||
ADT tile/chunk.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Connecting to a live AzerothCore or TrinityCore database.
|
||||
- Claiming TrinityCore spawn equivalence without a pinned populated TDB row.
|
||||
- Changing coordinate formulas, streaming radii, terrain, player movement or
|
||||
production character presentation.
|
||||
- Adding proprietary assets or screenshots to Git.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/tools/server_spawn_render_manifest.json`,
|
||||
`src/tools/verify_server_spawn_renderer.gd`
|
||||
- Shared/hotspots: `src/tools/capture_render_checkpoints.gd`, renderer runner,
|
||||
coordinate/world-renderer module documentation, `RENDER.md`, this claim
|
||||
- Generated/ignored: Godot UIDs/import metadata and `user://` PNG/JSON evidence
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Fixture schema and coordinate contract version remain unchanged.
|
||||
- Additive diagnostic checkpoint metadata may request a renderer-native marker;
|
||||
existing M00 checkpoints remain byte-for-byte compatible.
|
||||
- The spawn remains sourced from pinned AzerothCore revision
|
||||
`68fcf0098b16388093989627f37be530fc91f07d`.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: merged M01 packages through `a07a487`
|
||||
- Blocks: M01 golden server-spawn renderer criterion and milestone closeout
|
||||
- External state: GUI capture requires the existing local extracted/cached
|
||||
renderer corpus; headless contract verification remains non-proprietary
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated spawn verifier, coordinate fixture/mapper/boundary gates,
|
||||
renderer baseline dry-run, GUI checkpoint capture and human inspection
|
||||
- Fixture: AzerothCore race `1`, class `1`, map `0`, server position
|
||||
`(-8949.95, -132.493, 83.5312)`
|
||||
- Expected renderer result: Godot position
|
||||
`(17199.159666667, 83.5312, 26016.616666667)`, ADT `(32,48)`, MCNK `(3,12)`
|
||||
- Fidelity classification: server/coordinate integration evidence only; it does
|
||||
not establish full original-client visual parity
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API/tool documentation for new verifier and marker metadata
|
||||
- Coordinate-mapping module capability/evidence update
|
||||
- World-renderer checkpoint input/output/source-map update when marker support is added
|
||||
- Existing synchronous coordinate data-flow diagram updated only if flow changes
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names: `server_spawn_render_manifest`,
|
||||
`verify_server_spawn_renderer`, `diagnostic_spawn_marker`
|
||||
- Simplest approach: reuse the existing checkpoint capture pipeline with one
|
||||
dedicated manifest and a renderer-native diagnostic marker
|
||||
- Rejected complexity: live DB adapter, new scene-test framework and production
|
||||
spawn entity implementation before the network/gameplay milestones
|
||||
- Performance: one diagnostic checkpoint outside production runtime
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: fixture-to-renderer manifest/verifier, renderer-native marker, unified
|
||||
baseline integration, cold/warm GUI evidence, documentation and log cleanup
|
||||
- Next: integrator review, merge cleanup/handoff and M01 closeout audit
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commits: implementation `24ddc99`; required cleanup `318e6ae`
|
||||
- Branch: `work/sindo-main-codex/m01-server-spawn-renderer`
|
||||
- Outcome: the pinned AzerothCore Human Warrior start is routed through
|
||||
`CoordinateMapper` into the ordinary Eastern Kingdoms capture scene. A
|
||||
depth-tested magenta marker begins at the exact mapped spawn origin.
|
||||
- Contract/schema: additive diagnostic manifest schema 1 and marker metadata;
|
||||
coordinate contract version 2, production renderer APIs, cache formats and
|
||||
`Blizzlike335` behavior are unchanged.
|
||||
- Verification: `SERVER_SPAWN_RENDERER PASS spawn=1 checkpoint=1 tile=32_48
|
||||
chunk=3_12 profile=Blizzlike335`; unified renderer dry-run passed project,
|
||||
materials, M2 dedupe, shutdown, `7/7/7` manifest, five-point calibration,
|
||||
coordinate-boundary and server-spawn steps; coordinate golden fixtures,
|
||||
mapper, StreamingFocus, domain identities, documentation and whitespace gates
|
||||
passed. Coordination passed with five expired M00-claim warnings only.
|
||||
- GUI evidence: two `1280x900` cold/warm PNGs captured under
|
||||
`user://server_spawn_renderer_m01_gui_run2`; focus tile `(32,48)`, terrain
|
||||
quality `100/100`, missing cache `0`, queues drained, report results `2`.
|
||||
Human inspection confirms the marker is on rendered terrain beside the road
|
||||
at Northshire Abbey rather than below terrain or in an adjacent tile. PNG
|
||||
hashes: cold `c08c43bcb75e5b3277105afb0d4e5a76caae9db91a7db64488770ffa03e32589`,
|
||||
warm `1b75237a090ea163836372a8e39e778c909773f4508063adc3f70a3e12a85a62`.
|
||||
- Fidelity: source remains AzerothCore revision
|
||||
`68fcf0098b16388093989627f37be530fc91f07d`; server
|
||||
`(-8949.95,-132.493,83.5312)` maps to Godot
|
||||
`(17199.159666667,83.5312,26016.616666667)`, ADT `(32,48)`, MCNK `(3,12)`.
|
||||
This proves coordinate/renderer integration, not original-client visual parity
|
||||
or TrinityCore populated-row equivalence.
|
||||
- Runtime notes: D3D12 descriptor-heap initialization fell back to Vulkan;
|
||||
optional ignored character/ambient GLB imports were unavailable. Neither
|
||||
affected terrain/marker evidence. The known anonymous ObjectDB teardown
|
||||
warning remained; no Node, renderer resource or RID leak diagnostic occurred
|
||||
in the cache-complete run.
|
||||
- Repository hygiene: four accidentally committed local GUI logs are removed by
|
||||
`318e6ae`; root `*.stdout.log`/`*.stderr.log` files are now ignored. Generated
|
||||
PNG/JSON evidence and proprietary corpus remain outside Git.
|
||||
- Remaining risks: the diagnostic marker is not a production network entity;
|
||||
live server adapters belong to M06/M08. Exact original-client character spawn
|
||||
presentation and TrinityCore database equivalence remain unverified.
|
||||
- Documentation: `RENDER.md`, renderer-baseline procedure, coordinate-mapping
|
||||
and world-renderer module API/input-output/data-flow/capability/source-map
|
||||
sections updated. No lifecycle/state diagram changed because the verifier and
|
||||
mapper path are synchronous; capture reuses the documented renderer lifecycle.
|
||||
- Recommended merge order: `318e6ae` after master merge `747fae8`. Then the M01
|
||||
milestone integrator may accept all M01 claims, update target Evidence/checks
|
||||
and evaluate `OPENWC_TARGET_DONE`.
|
||||
@@ -0,0 +1,120 @@
|
||||
# M01-RND-STREAMING-FOCUS-001 — Explicit streaming focus contract
|
||||
|
||||
<!-- OPENWC_CLAIM:M01-RND-STREAMING-FOCUS-001:sindo-main-codex:2026-07-15 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M01-RND-STREAMING-FOCUS-001:c8e99f2 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M01
|
||||
- Program: RND/FND
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m01-streaming-focus`
|
||||
- Lease expires UTC: 2026-07-15
|
||||
- Integrator: M01 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Introduce a scene-free `StreamingFocus` value and make runtime/test streaming
|
||||
consume it through an explicit source or public setter instead of discovering
|
||||
the active `Camera3D` from a viewport.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Extracting `WorldRenderFacade` or `StreamingTargetPlanner` from the monolithic loader.
|
||||
- Adding direction, velocity, frustum or multi-focus planning.
|
||||
- Decomposing the player controller before M02.
|
||||
- Changing terrain, M2, WMO, liquid, cache or LOD formulas.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/domain/streaming/`, `src/tools/verify_streaming_focus.gd`
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
|
||||
`src/scenes/streaming/eastern_kingdoms_streaming.tscn`,
|
||||
`src/scenes/streaming/kalimdor_streaming.tscn`, renderer diagnostic tools,
|
||||
`docs/modules/world-renderer.md`, this claim
|
||||
- Generated/ignored: local extracted assets, renderer caches and Godot import metadata
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API: immutable `StreamingFocus`; loader `set_streaming_focus` and
|
||||
`refresh_streaming_focus`
|
||||
- Source adapter: explicit `streaming_focus_source_path` may reference any `Node3D`
|
||||
- Migration: runtime scenes use player focus; capture/probe tools explicitly use
|
||||
their dedicated camera as the source
|
||||
- Compatibility: streaming radii/LOD/cache behavior and coordinate contract v2 unchanged
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: coordinate contract version 2 and golden fixtures at `52e1b2b`
|
||||
- Blocks: M01 renderer/test focus exit criterion and later renderer facade extraction
|
||||
- External state: no new dependency or proprietary input
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: streaming focus contract/integration verifier, renderer baseline
|
||||
manifest/calibration, headless scene smoke, coordination/documentation gates
|
||||
- Fixtures: Node3D player-style source and explicit capture-style setter
|
||||
- Fidelity evidence: existing seven renderer checkpoints and build-12340 coordinate
|
||||
calibration must remain unchanged
|
||||
- Performance budget: one scalar focus snapshot per configured streaming interval;
|
||||
no new per-tile or per-chunk work
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs: focus value and loader public methods
|
||||
- Module specification: inputs/outputs, ownership, lifecycle, failure/recovery and source map
|
||||
- Data-flow diagram: player/editor/capture source to focus contract to loader
|
||||
- State/sequence diagram: focus sampling and refresh threshold sequence
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: `StreamingFocus`, `streaming_focus_source_path`,
|
||||
`set_streaming_focus`, `refresh_streaming_focus`
|
||||
- Simplest considered solution: one immutable value plus one explicit scene adapter path
|
||||
- Rejected complexity/abstractions: provider interface hierarchy, service locator,
|
||||
multi-focus registry and predictive motion fields
|
||||
- Unavoidable complexity and justification: editor viewport remains a separate
|
||||
adapter because its camera is owned by `EditorInterface`
|
||||
- Measured optimization evidence: not applicable
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: contract, runtime/editor/capture migration, headless regression,
|
||||
module diagrams, renderer documentation and cold/warm GUI capture
|
||||
- Next: integrator review and merge before typed identity work
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `7815385`
|
||||
- Branch: `work/sindo-main-codex/m01-streaming-focus`
|
||||
- Outcome: runtime streaming consumes immutable `StreamingFocus`; Eastern
|
||||
Kingdoms and Kalimdor scenes explicitly follow the player, while capture and
|
||||
terrain/occluder probes explicitly follow their dedicated camera
|
||||
- Contract/API: new scene-free `StreamingFocus(GodotWorldPosition)` plus loader
|
||||
`set_streaming_focus`, `refresh_streaming_focus` and
|
||||
`streaming_focus_source_path`; `camera_path` remains only for optional overview positioning
|
||||
- Verification: StreamingFocus PASS (`1` contract, `2` runtime scenes, `3`
|
||||
capture tools); coordinate golden fixtures and CoordinateMapper PASS; M00
|
||||
calibration PASS with maximum error `0.000015`; manifest PASS (`7`
|
||||
checkpoints); runtime-cache shutdown PASS; headless runtime scene exit `0`;
|
||||
coordination, documentation, dependency and whitespace checks passed
|
||||
- Renderer checkpoint: standard 8-second GUI capture completed `14` images
|
||||
(`7` cold + `7` warm) and wrote schema-1 `report.json`; focus tiles changed
|
||||
through the public contract; no Node/resource/RID leak diagnostic appeared
|
||||
- Fidelity: checkpoint camera positions, coordinate contract v2, streaming radii,
|
||||
LOD formulas, cache formats and quality profiles are unchanged; this is
|
||||
regression evidence, not a new `1:1` parity claim
|
||||
- Rebuild: none; GUI verification used local ignored extracted assets/cache
|
||||
junctions. Missing worktree `.godot/imported` copies for several incidental M2
|
||||
GLBs produced existing cache-load fallback diagnostics only
|
||||
- Remaining risks: the monolithic loader still owns planning/jobs/presentation;
|
||||
direction, velocity, frustum and multiple foci remain M03 planner work. The
|
||||
known timing-dependent zero-reference `ObjectDB` shutdown warning occurred,
|
||||
without Node/resource/RID leak diagnostics
|
||||
- Recommended merge order: after coordinate fixtures at `52e1b2b`, before typed
|
||||
identity and renderer-facade extraction
|
||||
- Documentation: `world-renderer.md` inputs/outputs, public API, lifecycle,
|
||||
data-flow and sequence diagrams, ownership, recovery, verification,
|
||||
capability matrix, risks and source map updated; `RENDER.md` updated
|
||||
@@ -0,0 +1,113 @@
|
||||
# M02-GMP-INPUT-001 — Player input intent seam
|
||||
|
||||
<!-- OPENWC_CLAIM:M02-GMP-INPUT-001:sindo-main-codex:2026-07-16 -->
|
||||
<!-- OPENWC_HANDOFF:READY:M02-GMP-INPUT-001:6bd2e84 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-GMP-INPUT-001:14dead1 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M02
|
||||
- Program: GMP/FND
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m02-player-input`
|
||||
- Lease expires UTC: 2026-07-16
|
||||
- Integrator: M02 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Replace direct keyboard polling in the runtime player with remappable Input Map
|
||||
actions and an immutable `PlayerInputSource → MoveIntent` boundary while
|
||||
preserving the current render-sandbox movement behavior.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Extract local movement, terrain query, camera rig or character presenters.
|
||||
- Claim exact build-12340 movement/keybinding parity before original-client fixtures.
|
||||
- Change movement speeds, camera geometry, terrain sampling or renderer behavior.
|
||||
- Mark M02 complete or edit its checklist/Evidence.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/domain/input/`, `src/gameplay/input/`,
|
||||
`src/tests/scenes/player_input_regression.tscn`,
|
||||
`src/tools/verify_player_input.gd`, `docs/modules/player-input.md`, this claim
|
||||
- Shared/hotspots: `project.godot`,
|
||||
`src/scenes/player/third_person_wow_controller.gd`, `docs/modules/README.md`
|
||||
- Generated/ignored: Godot import metadata, `.uid` files and local renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API: immutable `MoveIntent`; `PlayerInputSource.sample_move_intent`
|
||||
- Configuration: additive `openwc_player_*` Input Map actions
|
||||
- Persistence/schema/cache: unchanged
|
||||
- Consumers: current sandbox player controller; later local movement controller
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: merged M01 coordinate/streaming-focus contracts on master `62eece9`
|
||||
- Blocks: later M02 movement-controller extraction
|
||||
- External state: none; tests use synthetic Input Map events
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated player-input verifier, headless project load, coordinate
|
||||
boundary gate, coordination/documentation gates and diff hygiene
|
||||
- Fixtures: synthetic action strengths and configured default bindings
|
||||
- Fidelity evidence: current W/S/A/D, Shift, E/Q and Space sandbox behavior remains
|
||||
mapped through actions; exact 3.3.5a semantics remain unverified
|
||||
- Performance budget: one immutable intent allocation per physics tick; no I/O
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API documentation for `MoveIntent`, `PlayerInputActions` and
|
||||
`PlayerInputSource`
|
||||
- Player-input module specification with inputs/outputs, data-flow and sequence diagrams
|
||||
- Module registry/source map and honest capability status
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names: `MoveIntent`, `PlayerInputSource`, `PlayerInputActions`
|
||||
- Simplest approach: immutable value, one input adapter and one controller consumer
|
||||
- Rejected complexity: input provider hierarchy, event bus and generic command framework
|
||||
- Unavoidable complexity: none
|
||||
- Measured optimization evidence: not applicable
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: twelve remappable actions, immutable `MoveIntent`, stateless
|
||||
`PlayerInputSource`, controller migration, asset-free regression scene,
|
||||
dedicated verifier and module specification
|
||||
- Next: M02 integrator reviews and merges before local movement extraction
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `6bd2e84`
|
||||
- Branch: `work/sindo-main-codex/m02-player-input`
|
||||
- Outcome: the runtime player no longer polls physical keyboard keys. Movement
|
||||
action strengths cross an immutable `MoveIntent` seam; camera and immediate
|
||||
sandbox-flight events use the same remappable action catalog.
|
||||
- Public contract: additive `MoveIntent`, `PlayerInputActions` and
|
||||
`PlayerInputSource`; no schema, cache, coordinate or renderer contract change.
|
||||
- Verification: `PLAYER_INPUT PASS actions=12 intent_cases=6 controller=1
|
||||
regression_scene=1`; asset-free scene smoke exited `0`; coordinate boundary
|
||||
(`88` files) and StreamingFocus gates passed; unified renderer dry-run passed
|
||||
all eight pre-capture gates and produced seven dry-run checkpoint results;
|
||||
coordination/documentation/diff gates passed.
|
||||
- Fidelity: W/S/A/D, Shift, E/Q, Space, right mouse, wheel and Escape remain the
|
||||
default sandbox bindings. Regression drives forward movement, camera zoom and
|
||||
immediate flight toggle through the real controller. This does not establish
|
||||
exact build-12340 default bindings, turn/strafe policy or gameplay movement parity.
|
||||
- Local verification inputs: the isolated worktree reused the existing ignored
|
||||
native DLL and generated ADT resource scripts. Proprietary extracted data and
|
||||
the character GLB were absent, so the renderer dry-run logged the expected
|
||||
missing-corpus diagnostics while all contract steps returned success.
|
||||
- Rebuild/migration: none. Action names are additive; persisted user-binding
|
||||
migration does not exist yet and is documented as a gap.
|
||||
- Remaining risks: production profile gating for sprint/free flight, exact
|
||||
3.3.5a bindings, local movement/terrain/camera/presentation extraction and
|
||||
broader movement/camera state tests remain M02 work.
|
||||
- Documentation: inline API comments plus `docs/modules/player-input.md` with
|
||||
inputs/outputs, data-flow and sequence diagrams, ownership, recovery,
|
||||
capability status, known gaps and source map.
|
||||
@@ -0,0 +1,136 @@
|
||||
# M02-GMP-INPUT-PROFILE-335-001 — WoW 3.3.5a input profile
|
||||
|
||||
<!-- OPENWC_CLAIM:M02-GMP-INPUT-PROFILE-335-001:sindo-main-codex:2026-07-17 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M02
|
||||
- Program: GMP/QAR
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m02-input-profile-335`
|
||||
- Lease expires UTC: 2026-07-17
|
||||
- Integrator: M02 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Add a profile-aware, remappable `Blizzlike335` input path whose default movement
|
||||
bindings match the extracted build-12340 binding table while preserving the
|
||||
existing `RenderSandbox` controls. Add deterministic turn intent and keyboard
|
||||
turn consumption at the server-contract default turn rate.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Implement jump/fall/swim, network prediction or server reconciliation.
|
||||
- Claim complete movement or camera parity from binding and turn-rate evidence.
|
||||
- Persist user keybindings or build keybinding UI.
|
||||
- Commit proprietary extracted client files.
|
||||
- Mark M02 complete or edit its checklist/Evidence.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/tests/fixtures/player_input_profile_335.json`, this claim
|
||||
- Shared/hotspots: `project.godot`, `src/domain/input/move_intent.gd`,
|
||||
`src/gameplay/input/player_input_actions.gd`,
|
||||
`src/gameplay/input/player_input_source.gd`,
|
||||
`src/gameplay/movement/local_player_movement_controller.gd`,
|
||||
`src/scenes/player/third_person_wow_controller.gd`,
|
||||
`src/scenes/player/third_person_camera_rig.gd`, player/movement verifiers
|
||||
and regression scene, `docs/modules/player-input.md`,
|
||||
`docs/modules/local-player-movement.md`, `docs/modules/third-person-camera.md`
|
||||
- Generated/ignored: local `.godot`, native DLL and proprietary extracted corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- `MoveIntent` gains an immutable, clamped signed turn axis.
|
||||
- `PlayerInputSource` selects explicit sandbox or compatibility action sets at
|
||||
construction and routes compatibility A/D to strafe while camera rotation is held.
|
||||
- `LocalPlayerMovementController` converts turn intent into a deterministic yaw
|
||||
delta without reading engine input or owning a scene transform.
|
||||
- A sanitized JSON fixture records selected binding facts, source hashes and the
|
||||
pinned TrinityCore 3.3.5 server-contract commit.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: merged sandbox capability package on master `c3094c9`.
|
||||
- Blocks: final M02 integration audit and milestone closeout.
|
||||
- External state: local extracted build-12340 binding files are evidence inputs
|
||||
only; committed tests are reproducible without them.
|
||||
|
||||
## Verification
|
||||
|
||||
- Fixture schema/provenance and selected default-binding regression.
|
||||
- Pure profile composition, A/D turn, Q/E strafe and camera-held A/D strafe cases.
|
||||
- Deterministic turn-rate and controller integration regression.
|
||||
- Existing M02 and repository gates remain required.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Updated input/movement public API, profile matrix, data flow, sequence,
|
||||
failure/recovery, fidelity provenance and source maps.
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names: `turn_axis`, `BLIZZLIKE_STRAFE_LEFT`, `TURN_LEFT`.
|
||||
- Simplest approach: one profile selection at composition plus one scalar intent.
|
||||
- Rejected complexity: input-context framework, command bus and persisted binding store.
|
||||
- Unavoidable complexity: compatibility A/D behavior depends on camera-rotate state.
|
||||
- Measured optimization evidence: not applicable.
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: added distinct remappable profiles, signed turn intent/controller path,
|
||||
camera-yaw synchronization, sanitized provenance fixture, regression coverage
|
||||
and updated module specifications
|
||||
- Next: milestone integrator reviews and merges `8776a6b`
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M02-GMP-INPUT-PROFILE-335-001:8776a6b -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-GMP-INPUT-PROFILE-335-001:eb122d5 -->
|
||||
|
||||
## Handoff
|
||||
|
||||
- Branch/commit: `work/sindo-main-codex/m02-input-profile-335` at `8776a6b`.
|
||||
- Outcome: `RenderSandbox` retains A/D strafe and debug E/Q/Space/Shift behavior;
|
||||
`Blizzlike335` now samples W/S and Up/Down forward/back, Q/E strafe, A/D and
|
||||
Left/Right turn, with A/D routed to strafe while right-mouse rotation is active.
|
||||
- Public contracts: `MoveIntent.turn_axis`; profile-selecting
|
||||
`PlayerInputSource.new(profile_id)`; pure compatibility composition;
|
||||
`LocalPlayerMovementController.calculate_yaw_delta_radians(...)`; camera yaw
|
||||
synchronization after keyboard turn. Unknown input profiles fail closed to
|
||||
`Blizzlike335`.
|
||||
- Project configuration: four additive compatibility actions; existing action
|
||||
names and sandbox mappings remain valid. W/S also gain the original Up/Down
|
||||
secondary defaults.
|
||||
- Verification: player input `actions=16 intent_cases=9 profiles=3 fixture=1
|
||||
controller=1 regression_scene=3`; movement `cases=15 state_transitions=2`;
|
||||
capabilities `profiles=3 sprint=2 flight=2 scenes=2`; terrain
|
||||
`contract=4 interpolation=1 cache=1 failures=2`; camera
|
||||
`state_cases=12 policy_cases=3`; presentation `appearance_cases=9
|
||||
animation_cases=10`; coordinate boundaries `files=101`; StreamingFocus
|
||||
`runtime_scenes=2 capture_tools=3`.
|
||||
- Renderer: unified dry-run passed project/material/dedupe/cache/manifest/
|
||||
calibration/coordinate/server-spawn gates and planned all seven checkpoints;
|
||||
coordinate gate reported `files=103`. Missing proprietary GLB/DBC/ADT inputs
|
||||
produced the expected diagnostics only.
|
||||
- Repository gates: documentation `module_specs=8`; coordination
|
||||
`fallback_claims=31`, with the same five unrelated expired M00 warnings.
|
||||
- Fidelity: local build-12340 `DefaultBindings.wtf` SHA-256
|
||||
`35FFA0E4E2356A13A0633A848F9E57ABE7DF9D8A16C2245E15D33013AD2F56A5`
|
||||
and `Bindings.xml` SHA-256
|
||||
`2E01276AFB7462F1417710F13B4BF1A456341F7F28CC8AE0D6D9C7442D7F6793`
|
||||
were rechecked and selected facts sanitized into the committed fixture.
|
||||
TrinityCore revision `2853a621d6af91a803787a2b8a509f4ce3e0300d` supplies the
|
||||
3.141594 rad/s player server-contract turn rate. WoWee revision
|
||||
`626243e937fb93965fa583a6507ed5a1aa7dda4b` supports right-mouse A/D routing.
|
||||
- Fidelity limits: server turn rate and public reference routing are not direct
|
||||
original-client timing/behavior proof. Jump, autorun and left-button
|
||||
camera/select defaults are recorded but not implemented. This package does
|
||||
not claim complete input, movement or camera parity.
|
||||
- Migration/cache: no persisted settings or cache schema. Future saved bindings
|
||||
must migrate stable action names; current project users receive additive actions.
|
||||
- Documentation: updated input, movement and camera APIs, inputs/outputs, data
|
||||
flow, sequence, ownership, failure/recovery, configuration, fidelity and source maps.
|
||||
- Recommended merge order: merge after sandbox capabilities (already on master),
|
||||
then run the final M02 integration/evidence audit without marking the target
|
||||
done solely from this package.
|
||||
@@ -0,0 +1,118 @@
|
||||
# M02-GMP-MOVEMENT-001 — Local movement state/controller
|
||||
|
||||
<!-- OPENWC_CLAIM:M02-GMP-MOVEMENT-001:sindo-main-codex:2026-07-16 -->
|
||||
<!-- OPENWC_HANDOFF:READY:M02-GMP-MOVEMENT-001:435e1c9 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-GMP-MOVEMENT-001:5206c42 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M02
|
||||
- Program: GMP
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m02-local-movement`
|
||||
- Lease expires UTC: 2026-07-16
|
||||
- Integrator: M02 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Move local velocity, sandbox flight state, acceleration and directional speed
|
||||
calculation from `ThirdPersonWowController` into a scene-free,
|
||||
independently-testable local movement controller without changing current
|
||||
sandbox motion.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Extract terrain queries, camera rig, character presentation or animation.
|
||||
- Add jump/fall/swim, server prediction or reconciliation.
|
||||
- Change current movement speeds, acceleration, camera-relative flight or ground snap.
|
||||
- Gate debug movement by build profile; that remains a separate M02 package.
|
||||
- Mark M02 complete or edit its checklist/Evidence.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/gameplay/movement/`,
|
||||
`src/tools/verify_local_player_movement.gd`,
|
||||
`docs/modules/local-player-movement.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/player/third_person_wow_controller.gd`,
|
||||
`src/tools/verify_player_input.gd`, `docs/modules/player-input.md`,
|
||||
`docs/modules/README.md`
|
||||
- Generated/ignored: local `.godot`, native DLL, generated ADT resources and renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API: stateful `LocalPlayerMovementController`
|
||||
- Inputs: immutable `MoveIntent`, local Godot movement basis and physics delta seconds
|
||||
- Outputs: local Godot displacement and read-only velocity/flight state
|
||||
- Schema/cache/coordinate contracts: unchanged
|
||||
- Consumer: current sandbox player scene; later gameplay composition root
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: merged input seam on master `14dead1`
|
||||
- Blocks: terrain query and camera/presentation extraction from the player hotspot
|
||||
- External state: none; unit tests use synthetic intents and bases
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated pure movement verifier, player-input regression, asset-free
|
||||
scene smoke, coordinate/StreamingFocus gates, renderer dry-run and repository gates
|
||||
- Fixtures: identity/rotated/pitched bases, acceleration steps, sprint and flight transitions
|
||||
- Fidelity evidence: exact current 7.0/4.5/4.5 speeds, 28.0 acceleration,
|
||||
6x sandbox sprint and camera-relative free flight are regression-locked only
|
||||
- Performance budget: one small state update per physics tick; no allocation, I/O or scene lookup
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs for movement state/configuration and update methods
|
||||
- Local movement module specification with data-flow, state and sequence diagrams
|
||||
- Updated player-input consumer diagram/status and module registry
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name: `LocalPlayerMovementController`
|
||||
- Simplest approach: one stateful RefCounted owning velocity and flight toggle
|
||||
- Rejected complexity: generic locomotion framework, ECS component hierarchy and event bus
|
||||
- Unavoidable complexity: movement basis remains an explicit local-space input
|
||||
so the pure controller does not discover player/camera Nodes
|
||||
- Measured optimization evidence: not applicable
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: scene-free movement state/controller, player scene migration, twelve
|
||||
numeric cases, two flight transitions, integrated regression and module documentation
|
||||
- Next: M02 integrator reviews and merges before TerrainQuery extraction
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `435e1c9`
|
||||
- Branch: `work/sindo-main-codex/m02-local-movement`
|
||||
- Outcome: `ThirdPersonWowController` no longer owns velocity, flight state,
|
||||
acceleration or directional speed calculation. It selects an explicit
|
||||
Godot-world basis, applies returned displacement, and retains terrain and
|
||||
presentation adapters for later M02 packages.
|
||||
- Public contract: additive `LocalPlayerMovementController` with read-only
|
||||
Godot-world velocity/flight state, `toggle_sandbox_flight` and `advance`.
|
||||
Input, coordinate, cache and renderer contracts remain unchanged.
|
||||
- Verification: `LOCAL_PLAYER_MOVEMENT PASS cases=12 state_transitions=2
|
||||
scene_boundary=1`; `PLAYER_INPUT PASS actions=12 intent_cases=6 controller=1
|
||||
regression_scene=1`; asset-free scene smoke exited `0`; coordinate boundary
|
||||
and StreamingFocus passed; renderer dry-run passed all eight pre-capture gates
|
||||
and seven checkpoint plans; coordination/documentation/diff gates passed.
|
||||
- Fidelity: exact existing defaults are regression-locked—forward `7.0`,
|
||||
backward/strafe `4.5`, acceleration `28.0`, sandbox sprint `6x`, vertical
|
||||
flight `7.0`, velocity reset on toggle and camera-relative pitched flight.
|
||||
This preserves the sandbox baseline but is not original-client evidence.
|
||||
- Local verification inputs: the isolated worktree reused the existing ignored
|
||||
native DLL and generated ADT resource scripts. Proprietary extracted data and
|
||||
character GLB were absent, so renderer dry-run logged expected missing-corpus
|
||||
diagnostics while every contract step returned success.
|
||||
- Rebuild/migration: none; transient movement state is not persisted.
|
||||
- Remaining risks: exported speed changes after `_ready` do not reconfigure the
|
||||
controller; exact build-12340 movement semantics, terrain/collision state,
|
||||
jump/fall/swim, server prediction and debug profile gating remain open.
|
||||
- Documentation: inline public API plus `docs/modules/local-player-movement.md`
|
||||
with inputs/outputs, data-flow, state and sequence diagrams, ownership,
|
||||
recovery, capability status, risks and source map; player-input consumer
|
||||
diagrams/status were updated.
|
||||
@@ -0,0 +1,127 @@
|
||||
# M02-GMP-SANDBOX-CAPABILITIES-001 — Debug movement capability gate
|
||||
|
||||
<!-- OPENWC_CLAIM:M02-GMP-SANDBOX-CAPABILITIES-001:sindo-main-codex:2026-07-16 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M02
|
||||
- Program: GMP/FND
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m02-sandbox-capabilities`
|
||||
- Lease expires UTC: 2026-07-16
|
||||
- Integrator: M02 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Introduce an immutable local-player movement capability contract and require it
|
||||
at the movement boundary so debug sprint and free flight execute only in the
|
||||
explicit `RenderSandbox` profile and are rejected by `Blizzlike335`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Claim that current movement values or `Blizzlike335` locomotion are 1:1.
|
||||
- Build a global application/profile framework before the application shell exists.
|
||||
- Remove debug Input Map actions or change their current sandbox bindings.
|
||||
- Implement jump, fall, swim, collision, server authority or reconciliation.
|
||||
- Mark M02 complete or edit its checklist/Evidence.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/gameplay/movement/player_movement_capabilities.gd`,
|
||||
`src/tools/verify_player_movement_capabilities.gd`, this claim
|
||||
- Shared/hotspots: `src/gameplay/movement/local_player_movement_controller.gd`,
|
||||
`src/scenes/player/third_person_wow_controller.gd`, movement/input verifiers,
|
||||
player regression scene, `docs/modules/local-player-movement.md`,
|
||||
`docs/modules/player-input.md`
|
||||
- Generated/ignored: local `.godot`, native DLL, generated resources and renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- `PlayerMovementCapabilities` owns immutable profile ID plus explicit debug
|
||||
sprint/flight booleans and safe named factories.
|
||||
- `LocalPlayerMovementController` requires the capability value; missing input
|
||||
falls back to `Blizzlike335`, never sandbox.
|
||||
- Player scene maps its exported profile selection once at the composition boundary.
|
||||
- Input actions and `MoveIntent` remain unchanged; rejected debug requests have no effect.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: merged character presentation package on master `8c71e46`.
|
||||
- Blocks: final M02 integration audit and milestone closeout.
|
||||
- External state: none; verification is asset-free.
|
||||
|
||||
## Verification
|
||||
|
||||
- Pure profile factory/default and immutability regression.
|
||||
- Sprint accepted in RenderSandbox and ignored in Blizzlike335.
|
||||
- Flight toggle/vertical basis accepted in RenderSandbox and rejected in Blizzlike335.
|
||||
- Real regression scene verifies both profile compositions.
|
||||
- Existing input/movement/terrain/camera/presentation and renderer gates remain required.
|
||||
- Fidelity evidence: negative proof that debug capabilities cannot affect the
|
||||
compatibility profile; no positive movement parity claim.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API and profile semantics on the capability/controller boundary.
|
||||
- Updated movement and input specs: API, inputs/outputs, data flow, lifecycle,
|
||||
failure/recovery, capability matrix and source map.
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name: `PlayerMovementCapabilities`.
|
||||
- Simplest approach: one immutable value injected into the existing controller.
|
||||
- Rejected complexity: autoload singleton, generic feature-flag service and global profile registry.
|
||||
- Unavoidable complexity: one profile-to-capability mapping at scene composition.
|
||||
- Measured optimization evidence: not applicable.
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: added immutable capability contract, gated sprint/free-flight, covered
|
||||
pure and real-scene profiles and updated movement/input specifications
|
||||
- Next: milestone integrator reviews and merges `742c415`
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M02-GMP-SANDBOX-CAPABILITIES-001:742c415 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-GMP-SANDBOX-CAPABILITIES-001:c3094c9 -->
|
||||
|
||||
## Handoff
|
||||
|
||||
- Branch/commit: `work/sindo-main-codex/m02-sandbox-capabilities` at `742c415`.
|
||||
- Outcome: debug sprint and free flight now require explicit
|
||||
`PlayerMovementCapabilities.render_sandbox()`; `Blizzlike335`, unknown and
|
||||
null capability inputs reject both effects.
|
||||
- Public contract: immutable profile ID plus `allows_debug_sprint` and
|
||||
`allows_debug_free_flight`; named factories map configuration once at the
|
||||
composition boundary. The movement controller exposes its effective profile ID.
|
||||
- Runtime composition: `ThirdPersonWowController.movement_profile_id` defaults
|
||||
to `RenderSandbox`, preserving renderer-scene behavior. Selecting
|
||||
`Blizzlike335` keeps ordinary movement while ignoring Shift/Space/E/Q effects.
|
||||
- Verification: capability `profiles=3 sprint=2 flight=2 scenes=2 boundary=1`,
|
||||
movement `cases=12 state_transitions=2`, input `actions=12 intent_cases=6`,
|
||||
terrain `contract=4 interpolation=1 cache=1 failures=2`, camera
|
||||
`state_cases=12 policy_cases=3`, presentation
|
||||
`appearance_cases=9 animation_cases=10`, coordinate boundaries
|
||||
`files=101 consumers=5`, and StreamingFocus `runtime_scenes=2 capture_tools=3` passed.
|
||||
- Renderer: dry-run passed project/material/dedupe/cache/manifest/calibration/
|
||||
coordinate/server-spawn gates and planned all seven checkpoints; the combined
|
||||
run reported coordinate `files=103`. Missing proprietary GLB/DBC/ADT inputs
|
||||
produced the expected diagnostics only.
|
||||
- Repository gates: documentation `module_specs=8`, coordination
|
||||
`fallback_claims=30`; five unrelated expired M00 claim warnings remain.
|
||||
- Fidelity: this package provides negative evidence that sandbox-only debug
|
||||
movement cannot alter the `Blizzlike335` profile. It does not prove ordinary
|
||||
movement, bindings, flight or timing match build 12340.
|
||||
- Migration: direct `LocalPlayerMovementController.new(...)` callers that need
|
||||
debug behavior must now pass `render_sandbox()`; omitted/null capability is
|
||||
intentionally compatibility-safe. Existing repository scenes require no edit.
|
||||
- Cache/persistence: no schema, cache, binding or stored-profile migration.
|
||||
- Local ignored inputs: native extension DLL, UID cache and generated ADT
|
||||
resource scripts supported renderer verification and are not committed.
|
||||
- Risks: runtime renderer scenes still default to `RenderSandbox`; the future
|
||||
application shell must explicitly choose its profile. `Blizzlike335` currently
|
||||
guarantees only debug exclusion, not complete movement fidelity.
|
||||
- Recommended merge order: merge after presentation (already on master), then
|
||||
perform the final M02 integration/evidence audit before any target closeout.
|
||||
- Documentation: updated movement/input API, inputs/outputs, data flow, state,
|
||||
sequence, ownership, failure/recovery, capabilities, verification and source maps.
|
||||
@@ -0,0 +1,119 @@
|
||||
# M02-GMP-TERRAIN-QUERY-001 — Player terrain query boundary
|
||||
|
||||
<!-- OPENWC_CLAIM:M02-GMP-TERRAIN-QUERY-001:sindo-main-codex:2026-07-16 -->
|
||||
<!-- OPENWC_HANDOFF:READY:M02-GMP-TERRAIN-QUERY-001:a45d521 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-GMP-TERRAIN-QUERY-001:f5bb64e -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M02
|
||||
- Program: GMP/RND
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m02-terrain-query`
|
||||
- Lease expires UTC: 2026-07-16
|
||||
- Integrator: M02 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Introduce a typed `TerrainQuery` boundary and move ADT loading, caching, chunk
|
||||
selection and height interpolation out of `ThirdPersonWowController` without
|
||||
changing current spawn/ground-snap behavior.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change ADT topology, coordinate formulas, terrain mesh or renderer caches.
|
||||
- Add physics collision, slopes, water, jump/fall/swim or server movement.
|
||||
- Reuse the streaming renderer cache before its public terrain-query contract exists.
|
||||
- Extract camera or character presentation.
|
||||
- Mark M02 complete or edit its checklist/Evidence.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/gameplay/terrain/`, `src/tools/verify_terrain_query.gd`,
|
||||
`docs/modules/terrain-query.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/player/third_person_wow_controller.gd`,
|
||||
`src/tools/verify_player_input.gd`,
|
||||
`src/tools/verify_coordinate_conversion_boundaries.gd`,
|
||||
`docs/modules/local-player-movement.md`,
|
||||
`docs/modules/README.md`
|
||||
- Generated/ignored: local `.godot`, native DLL, generated ADT resources and renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API: `TerrainQuery.sample_ground_height(GodotWorldPosition)` returning
|
||||
immutable `TerrainGroundSample`
|
||||
- Production adapter: `AdtTerrainQuery` with owned per-tile parse cache
|
||||
- Test/data-source seam: optional ADT tile loader callable at construction
|
||||
- Coordinate contract version, ADT format, renderer cache and persistence: unchanged
|
||||
- Consumers: current player scene; future movement/collision policies
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: merged movement controller on master `5206c42`
|
||||
- Blocks: camera/presentation extraction from the player hotspot
|
||||
- External state: production uses existing native `ADTLoader`; synthetic tests need no proprietary data
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: synthetic height/cache/failure verifier, player input/movement
|
||||
regressions, asset-free scene, coordinate/StreamingFocus gates, renderer dry-run
|
||||
and repository gates
|
||||
- Fixtures: synthetic 145-height chunk payload and injected flat terrain query
|
||||
- Fidelity evidence: preserves existing outer-grid bilinear interpolation,
|
||||
spawn ground offset and per-tick snap formula only
|
||||
- Performance budget: one cached tile lookup and local interpolation per ground query;
|
||||
no new I/O after first tile load
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs for query, sample and ADT adapter
|
||||
- Terrain-query module spec with inputs/outputs, data-flow, cache lifecycle and sequence diagrams
|
||||
- Updated movement/coordinate consumer boundaries, source maps and module registry
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names: `TerrainQuery`, `TerrainGroundSample`, `AdtTerrainQuery`
|
||||
- Simplest approach: one narrow query contract, one typed result and one existing-format adapter
|
||||
- Rejected complexity: general collision framework, terrain service locator and renderer-cache coupling
|
||||
- Unavoidable complexity: ADT 145-height layout and chunk lookup stay isolated in adapter
|
||||
- Measured optimization evidence: existing per-tile cache retained; no new optimization
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: typed query/sample contract, cached ADT adapter, player migration,
|
||||
synthetic interpolation/cache/failure tests, injected scene regression and documentation
|
||||
- Next: M02 integrator reviews and merges before camera-rig extraction
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `a45d521`
|
||||
- Branch: `work/sindo-main-codex/m02-terrain-query`
|
||||
- Outcome: `ThirdPersonWowController` no longer loads ADTs, caches tile
|
||||
dictionaries, selects chunks or interpolates height grids. It consumes a
|
||||
replaceable typed `TerrainQuery` and retains only existing offset/snap policy.
|
||||
- Public contracts: additive immutable `TerrainGroundSample`, replaceable
|
||||
`TerrainQuery`, cached `AdtTerrainQuery` and player `set_terrain_query`.
|
||||
Coordinate contract version, ADT/native format and renderer caches are unchanged.
|
||||
- Verification: `TERRAIN_QUERY PASS contract=4 interpolation=1 cache=1
|
||||
failures=2 player_injection=1`; player-input and local-movement regressions
|
||||
passed; asset-free scene exited `0`; coordinate boundary passed with five
|
||||
required consumers and StreamingFocus passed; renderer dry-run passed eight
|
||||
pre-capture gates and seven checkpoint plans; coordination/documentation/diff gates passed.
|
||||
- Fidelity: synthetic fractional-grid fixture locks the inherited 145-height
|
||||
outer-grid bilinear result within `0.002` Godot units; injected scene locks
|
||||
spawn offset `0.05` and physics snap. This is sandbox-regression evidence,
|
||||
not build-12340 triangle/hole/collision parity.
|
||||
- Local verification inputs: isolated worktree reused the existing ignored
|
||||
native DLL and generated ADT resource scripts. Proprietary extracted data and
|
||||
character GLB were absent, so renderer dry-run logged expected missing-corpus
|
||||
diagnostics while all contract steps returned success.
|
||||
- Rebuild/migration: none; caches are transient and existing formats remain unchanged.
|
||||
- Remaining risks: first ADT parse remains synchronous as before; unavailable
|
||||
tiles cache until query replacement; player still owns snap policy; exact
|
||||
ADT triangle choice, holes, slopes, liquids and collision remain open.
|
||||
- Documentation: inline public APIs plus `docs/modules/terrain-query.md` with
|
||||
inputs/outputs, data-flow, cache state and cross-boundary sequence diagrams,
|
||||
ownership, failure/recovery, capability status and source map. Coordinate and
|
||||
movement consumer specifications were updated.
|
||||
@@ -0,0 +1,111 @@
|
||||
# M02-QAR-INTEGRATOR-CLOSEOUT-001 — M02 milestone closeout
|
||||
|
||||
<!-- OPENWC_CLAIM:M02-QAR-INTEGRATOR-CLOSEOUT-001:sindo-main-codex:2026-07-17 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M02
|
||||
- Program: QAR
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m02-integrator-closeout`
|
||||
- Lease expires UTC: 2026-07-17
|
||||
- Integrator: sindo-main-codex, continuing the user-directed milestone workflow on 2026-07-15
|
||||
|
||||
## Outcome
|
||||
|
||||
Audit every merged M02 package on current master, record final structural and
|
||||
fidelity evidence, close M02 and activate M03 only when all target and repository
|
||||
gates pass.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change player, movement, terrain, camera, presentation or renderer behavior.
|
||||
- Claim complete WoW 3.3.5a locomotion/camera parity.
|
||||
- Implement recorded jump/fall/swim/fly, collision or timing gaps.
|
||||
- Commit proprietary assets, generated UIDs, caches or user scene changes.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: M02/M03 target status, M02 final Evidence, all M02 claim acceptance markers
|
||||
- Shared/hotspots: `targets/README.md`, `targets/DEVELOPMENT_ROADMAP.md`, this claim
|
||||
- Generated/ignored: local Godot imports, native DLL, renderer corpus and `user://` reports
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Runtime APIs, Input Map actions, fixture schemas and cache formats: unchanged
|
||||
- Consumers: milestone sequence and future M03 renderer-facade packages
|
||||
- Fidelity classification distinguishes direct build-12340 binding evidence,
|
||||
server/reference compatibility evidence and explicitly unverified gaps
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: all seven M02 READY packages integrated through master `eb122d5`
|
||||
- Blocks: M03 activation
|
||||
- External state: private original-client files are hash/provenance inputs only
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: all M02 input/movement/capability/terrain/camera/presentation tests,
|
||||
coordinate/streaming boundaries, unified renderer dry-run, documentation,
|
||||
coordination and diff hygiene
|
||||
- Fidelity evidence: build-12340 default binding hashes and selected facts;
|
||||
pinned TrinityCore turn rate; public reference routing; explicit remaining gaps
|
||||
- Performance: no runtime change; renderer baseline remains the performance guard
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- M02 final target Evidence/checklist and limitation statement
|
||||
- M02 claim integration-acceptance markers
|
||||
- Target index and roadmap transition to M03
|
||||
- Existing API/module specifications and diagrams audited without behavioral edits
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: none beyond the closeout work-package ID
|
||||
- Simplest approach: evidence/status-only integrator patch
|
||||
- Rejected complexity: behavioral fixes or a new closeout framework
|
||||
- Unavoidable complexity: explicit evidence classification prevents false 1:1 claims
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: accepted seven M02 packages, reran post-merge gates, recorded complete
|
||||
target Evidence, closed M02 and activated M03
|
||||
- Next: merge closeout commit `1bf5b52` before starting an M03 implementation claim
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M02-QAR-INTEGRATOR-CLOSEOUT-001:1bf5b52 -->
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `1bf5b52`.
|
||||
- Branch: `work/sindo-main-codex/m02-integrator-closeout`.
|
||||
- Outcome: M02 is `DONE` with all eight structural checklist items and complete
|
||||
classified Evidence; M03 is the only `ACTIVE` target and the target index/
|
||||
roadmap point to renderer-facade extraction.
|
||||
- Integration audit: all seven READY implementation commits are ancestors of
|
||||
merged master `eb122d5`; their claims now carry the corresponding
|
||||
`OPENWC_INTEGRATION:ACCEPTED` merge markers.
|
||||
- Verification: input `16/9/3/1/3`, movement `15` cases and `2` transitions,
|
||||
capability `3` profiles and `2` scenes, terrain `4/1/1/2`, camera `12/3`,
|
||||
presentation `9/10/3`, coordinate boundaries `103` files and StreamingFocus
|
||||
`2` runtime scenes/`3` capture tools all passed. Unified renderer dry-run
|
||||
passed project/material/dedupe/shutdown gates, manifest `7/7/7`, calibration
|
||||
maximum error `0.000015`, server-spawn renderer and seven checkpoint plans.
|
||||
- Repository gates: documentation passed with `8` module specs and `7` required
|
||||
files. Coordination passed with one active target; `13` expired M00 claim
|
||||
warnings are historical and unrelated to M02.
|
||||
- Fidelity: direct build-12340 binding hashes/facts are preserved in the
|
||||
sanitized fixture. TrinityCore turn rate and WoWee mouse routing remain
|
||||
explicitly reference-only. Translation speeds/camera values are sandbox
|
||||
regressions, and jump/fall/swim/fly parity remains unimplemented/unverified.
|
||||
Debug sprint/free flight is negatively proven absent from `Blizzlike335`.
|
||||
- Remaining risks: target Evidence carries exact follow-up scope for M09/M10/M12:
|
||||
movement/camera timing, jump/fall/swim/fly, autorun/LMB semantics, active
|
||||
collision, terrain/liquid policy, persisted bindings, server authority and
|
||||
prediction/reconciliation. No complete `1:1` claim is made.
|
||||
- Runtime/cache/migration: no behavioral change, dependency, cache schema or
|
||||
persisted settings migration in closeout.
|
||||
- Documentation: M02 target Evidence/checklist, seven acceptance markers,
|
||||
target index and roadmap state updated. Existing input, movement, terrain,
|
||||
camera and presentation API/module specs and diagrams passed the documentation gate.
|
||||
@@ -0,0 +1,119 @@
|
||||
# M02-RND-CAMERA-001 — Third-person camera rig boundary
|
||||
|
||||
<!-- OPENWC_CLAIM:M02-RND-CAMERA-001:sindo-main-codex:2026-07-16 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M02
|
||||
- Program: RND/GMP
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m02-camera-rig`
|
||||
- Lease expires UTC: 2026-07-16
|
||||
- Integrator: M02 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Move mouse capture, orbit yaw/pitch, zoom state and Camera3D transforms from
|
||||
`ThirdPersonWowController` into a reusable `ThirdPersonCameraRig`, with an
|
||||
explicit replaceable camera-collision policy that preserves the current
|
||||
no-collision sandbox baseline.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Enable a new collision/raycast behavior before build-12340 comparison.
|
||||
- Change initial yaw/pitch, zoom limits, sensitivity, FOV, far plane or camera path.
|
||||
- Implement first-person, follow lag, shoulder offsets or cinematic cameras.
|
||||
- Change movement, terrain query, character presentation or renderer streaming focus.
|
||||
- Mark M02 complete or edit its checklist/Evidence.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/scenes/player/third_person_camera_rig.gd`,
|
||||
`src/scenes/player/camera_collision_policy.gd`,
|
||||
`src/scenes/player/no_camera_collision_policy.gd`,
|
||||
`src/tools/verify_third_person_camera_rig.gd`,
|
||||
`docs/modules/third-person-camera.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/player/third_person_wow_controller.gd`,
|
||||
runtime streaming scenes, player regression scene,
|
||||
`src/tools/verify_player_input.gd`, `docs/modules/player-input.md`,
|
||||
`docs/modules/local-player-movement.md`, `docs/modules/README.md`
|
||||
- Generated/ignored: local `.godot`, native DLL, generated resources and renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API: `ThirdPersonCameraRig.initialize_for_character`,
|
||||
`handle_camera_input`, `godot_world_flight_movement_basis`, state getters and
|
||||
`set_collision_policy`
|
||||
- Policy: `CameraCollisionPolicy.resolve_camera_distance`; default
|
||||
`NoCameraCollisionPolicy` returns requested distance unchanged
|
||||
- Scene paths and renderer camera path remain unchanged
|
||||
- Schema/cache/coordinate contracts: unchanged
|
||||
- Consumers: player composition, sandbox flight movement and renderer viewport
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: merged TerrainQuery package on master `f5bb64e`
|
||||
- Blocks: character appearance/animation extraction from remaining player hotspot
|
||||
- External state: none; tests use asset-free camera/player scene
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated orbit/zoom/clamp/policy verifier, input/movement/terrain
|
||||
regressions, asset-free scene, coordinate/StreamingFocus gates, renderer dry-run
|
||||
and repository gates
|
||||
- Fixtures: initial state, remappable action events, mouse deltas and fixed-distance policy
|
||||
- Fidelity evidence: preserve current `-18°` pitch, `8` distance, `1` zoom step,
|
||||
`2..18` clamp, `0.003` sensitivity and no-collision output
|
||||
- Performance budget: constant-time state/transform update; no raycast in baseline policy
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API/export documentation for rig and collision policies
|
||||
- Camera module spec with inputs/outputs, data-flow, state and sequence diagrams
|
||||
- Updated input/movement consumer diagrams/source maps and module registry
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names: `ThirdPersonCameraRig`, `CameraCollisionPolicy`,
|
||||
`NoCameraCollisionPolicy`
|
||||
- Simplest approach: one Node3D rig on existing CameraPivot and one explicit identity policy
|
||||
- Rejected complexity: generic camera framework, spring arm migration and hidden physics raycast
|
||||
- Unavoidable complexity: rig mutates Camera3D/character Node transforms on main thread
|
||||
- Measured optimization evidence: not applicable
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: extracted rig and collision policy, migrated all three scene compositions,
|
||||
added regressions and completed required module documentation
|
||||
- Next: milestone integrator reviews and merges `8c1cf9b`
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M02-RND-CAMERA-001:8c1cf9b -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-RND-CAMERA-001:68e6f60 -->
|
||||
|
||||
## Handoff
|
||||
|
||||
- Outcome: `ThirdPersonCameraRig` now owns capture, orbit, zoom and camera
|
||||
transforms; `ThirdPersonWowController` only dispatches camera events and
|
||||
consumes the rig basis for sandbox flight.
|
||||
- Public contracts: `initialize_for_character`, `handle_camera_input`,
|
||||
`godot_world_flight_movement_basis`, `set_collision_policy`, read-only state,
|
||||
and `CameraCollisionPolicy.resolve_camera_distance`.
|
||||
- Verification: camera `state_cases=12 policy_cases=3 scenes=3`, input
|
||||
`actions=12 intent_cases=6`, movement `cases=12 state_transitions=2`, terrain
|
||||
`contract=4 interpolation=1 cache=1 failures=2`, coordinate boundary
|
||||
`files=98 consumers=5`; documentation, coordination and diff checks passed.
|
||||
- Renderer dry-run: seven checkpoint plans and report completed; expected
|
||||
diagnostics only for unavailable proprietary corpus/character GLB.
|
||||
- Fidelity: preserves the sandbox defaults (`-18 degrees`, distance `8`, zoom
|
||||
`2..18` step `1`, sensitivity `0.003`, pivot `1.7`, far `50000`) and the prior
|
||||
no-collision behavior. No build-12340 parity claim is made.
|
||||
- Local ignored inputs: native extension DLL and generated ADT resource scripts
|
||||
were used for the renderer dry-run and are not part of the commit.
|
||||
- Migration: none; `ThirdPersonPlayer/CameraPivot/Camera3D` remains stable.
|
||||
- Risks: active collision and exact original-client camera semantics remain
|
||||
unverified; exported initial values are captured at ready; mouse mode is
|
||||
process-global.
|
||||
- Documentation: added the camera API/module spec plus data-flow, state and
|
||||
sequence diagrams; updated player-input and local-movement consumer maps.
|
||||
@@ -0,0 +1,125 @@
|
||||
# M02-RND-CHARACTER-PRESENTATION-001 — Character presentation boundary
|
||||
|
||||
<!-- OPENWC_CLAIM:M02-RND-CHARACTER-PRESENTATION-001:sindo-main-codex:2026-07-16 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M02
|
||||
- Program: RND/GMP
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m02-character-presentation`
|
||||
- Lease expires UTC: 2026-07-16
|
||||
- Integrator: M02 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Move character GLB composition, geoset/texture/outfit setup and locomotion
|
||||
animation selection from `ThirdPersonWowController` into independently
|
||||
replaceable `CharacterAppearancePresenter` and `CharacterAnimationPresenter`
|
||||
components without changing the current sandbox presentation.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Add new model, equipment, animation or build-12340 behavior.
|
||||
- Change character asset formats, DBC parsing, texture composition or geoset rules.
|
||||
- Add network-driven appearance/movement snapshots.
|
||||
- Change input, movement, terrain, camera or renderer streaming behavior.
|
||||
- Mark M02 complete or edit its checklist/Evidence.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/scenes/character/character_appearance_presenter.gd`,
|
||||
`src/scenes/character/character_animation_presenter.gd`,
|
||||
`src/tools/verify_character_presentation.gd`,
|
||||
`docs/modules/character-presentation.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/player/third_person_wow_controller.gd`, runtime
|
||||
streaming scenes, player regression scene, `src/tools/verify_player_input.gd`,
|
||||
`docs/modules/local-player-movement.md`, `docs/modules/README.md`
|
||||
- Reused unchanged: geoset controller, texture compositor and outfit resolver
|
||||
- Generated/ignored: local `.godot`, native DLL, generated resources and renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Appearance API loads/replaces one character root under the existing `Visual` node.
|
||||
- Animation API binds an `AnimationPlayer` from that root and accepts a typed
|
||||
moving/stationary presentation state.
|
||||
- Scene paths, model defaults, DBC/cache/coordinate contracts remain unchanged.
|
||||
- Consumers: player scene composition and renderer viewport only.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: merged camera package on master `68e6f60`.
|
||||
- Blocks: final M02 sandbox-profile gate and target integration audit.
|
||||
- External state: asset-free tests use a synthetic mesh and AnimationPlayer.
|
||||
|
||||
## Verification
|
||||
|
||||
- Dedicated appearance composition, replacement, grounding and failure regression.
|
||||
- Dedicated animation discovery, fallback, looping, blend and state regression.
|
||||
- Player source-boundary and three-scene wiring checks.
|
||||
- Existing input/movement/terrain/camera regressions, coordinate gates and renderer dry-run.
|
||||
- Fidelity evidence: preserve current sandbox model path, scale, yaw, outfit,
|
||||
Stand/Idle and Run/Walk selection only; no original-client parity claim.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API/export documentation for both presenters.
|
||||
- Module specification with inputs/outputs, data-flow, lifecycle/state and sequence diagrams.
|
||||
- Updated movement consumer/source map and module registry.
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names: `CharacterAppearancePresenter`, `CharacterAnimationPresenter`.
|
||||
- Simplest approach: two small scene components on the existing `Visual` subtree.
|
||||
- Rejected complexity: generic presenter framework, async asset scheduler and duplicated character systems.
|
||||
- Unavoidable complexity: scene-tree composition and AnimationPlayer discovery run on the main thread.
|
||||
- Measured optimization evidence: not applicable.
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: extracted both presenters, migrated all runtime/regression scenes,
|
||||
added asset-free regressions and completed required module documentation
|
||||
- Next: milestone integrator reviews and merges `97bb53f`
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M02-RND-CHARACTER-PRESENTATION-001:97bb53f -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-RND-CHARACTER-PRESENTATION-001:8c71e46 -->
|
||||
|
||||
## Handoff
|
||||
|
||||
- Branch/commit: `work/sindo-main-codex/m02-character-presentation` at `97bb53f`.
|
||||
- Outcome: `CharacterAppearancePresenter` owns model/geoset/compositor/outfit
|
||||
composition and `CharacterAnimationPresenter` owns AnimationPlayer discovery,
|
||||
loop preparation and moving/stationary playback. The player is now only the
|
||||
composition and horizontal-velocity adapter.
|
||||
- Changed composition: both runtime scenes and the player regression scene keep
|
||||
`ThirdPersonPlayer/Visual`; they add `Visual/AnimationPresenter`.
|
||||
- Public contracts: appearance load/read-root/ready signal and animation
|
||||
bind/present/read-state APIs documented in the module specification.
|
||||
- Verification: presentation `appearance_cases=9 animation_cases=10 scenes=3`,
|
||||
input `actions=12 intent_cases=6`, movement `cases=12 state_transitions=2`,
|
||||
terrain `contract=4 interpolation=1 cache=1 failures=2`, camera
|
||||
`state_cases=12 policy_cases=3`, coordinate boundaries `files=99 consumers=5`,
|
||||
and StreamingFocus `runtime_scenes=2 capture_tools=3` passed.
|
||||
- Renderer: dry-run passed project/material/dedupe/cache/manifest/calibration/
|
||||
coordinate/server-spawn gates and planned all seven checkpoints. Missing
|
||||
proprietary HumanMale GLB, DBC and ADT corpus produced expected diagnostics.
|
||||
- Repository gates: documentation `module_specs=8`, coordination
|
||||
`fallback_claims=29`; five unrelated expired M00 claim warnings remain.
|
||||
- Fidelity: current model path, scale `1`, yaw `90 degrees`, class `1`, blend
|
||||
`0.15 seconds`, starter outfit and Stand/Idle/Run/Walk selection are preserved.
|
||||
No build-12340 appearance or animation parity claim is made.
|
||||
- Migration: repository scenes are migrated. External/custom scenes must move
|
||||
model/class/scale/yaw exports from the player node to the scripted `Visual`
|
||||
node and add `Visual/AnimationPresenter`. No cache/database rebuild is needed.
|
||||
- Local ignored inputs: native extension DLL, UID cache and generated ADT
|
||||
resource scripts supported the dry-run and are not committed.
|
||||
- Risks: synchronous model load may hitch; first descendant AnimationPlayer wins;
|
||||
moving state is a sandbox threshold boolean; complete equipment/animation
|
||||
fidelity remains open.
|
||||
- Recommended merge order: merge this package after camera (already on master),
|
||||
then implement the sandbox profile gate and perform the final M02 integration audit.
|
||||
- Documentation: added character-presentation API, inputs/outputs, data-flow,
|
||||
lifecycle/state, sequence, ownership/failure and source-map sections; updated
|
||||
the module registry and movement presentation consumer map.
|
||||
@@ -0,0 +1,117 @@
|
||||
# M03-RND-FACADE-001 — Renderer facade first seam
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-FACADE-001:sindo-main-codex:2026-07-17 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M03
|
||||
- Program: RND
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m03-renderer-facade`
|
||||
- Lease expires UTC: 2026-07-17
|
||||
- Integrator: M03 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Introduce `WorldRenderFacade` as the only runtime/tool caller surface for typed
|
||||
streaming focus updates and read-only renderer metrics, while retaining
|
||||
`StreamingWorldLoader` as the unchanged internal implementation.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Extract the pure target planner or budget scheduler in this package.
|
||||
- Add environment or entity-presentation commands before their contracts exist.
|
||||
- Replace the M02 `TerrainQuery` gameplay boundary in this package.
|
||||
- Change streaming radii, queues, cache formats, visuals or quality presets.
|
||||
- Mark M03 complete or edit its checklist/Evidence.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/world_render_facade.gd`,
|
||||
`src/tools/verify_world_render_facade.gd`, this claim
|
||||
- Shared/hotspots: both runtime streaming scenes, three renderer capture/probe
|
||||
tools, `src/tools/verify_streaming_focus.gd`, `docs/modules/world-renderer.md`,
|
||||
`docs/modules/README.md`, `RENDER.md`
|
||||
- Generated/ignored: local `.godot`, native build outputs, caches and proprietary
|
||||
renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API: `set_streaming_focus`, `refresh_streaming_focus`,
|
||||
`renderer_metrics_snapshot`
|
||||
- Scene adapter: explicit `streaming_focus_source_path` owned by the facade
|
||||
- Internal implementation seam: `streaming_world_loader_path`; callers must not
|
||||
read streamer queues or call its methods directly
|
||||
- Schema/cache/coordinate contracts: unchanged
|
||||
- Consumers: runtime scenes, capture tool and renderer probes
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: M01 `StreamingFocus` and merged M02 closeout on master `4f566b1`
|
||||
- Blocks: environment/entity/ground facade contracts and planner extraction
|
||||
- External state: none; contract verification uses a test double
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated facade verifier, streaming-focus regression, renderer
|
||||
baseline manifest/dry-run, coordinate boundary and repository gates
|
||||
- Fixtures: typed focus, source Node3D and read-only metrics test double
|
||||
- Fidelity evidence: no renderer configuration, planner, queue, placement,
|
||||
visibility, material, animation or cache behavior changes
|
||||
- Performance budget: one source position sample per process frame; target
|
||||
recomputation remains throttled inside the existing streamer
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs for facade properties and methods
|
||||
- Updated world-renderer module specification
|
||||
- Updated data-flow, sequence and dependency diagrams
|
||||
- Updated source map/status and renderer notes
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names: `WorldRenderFacade`, `renderer_metrics_snapshot`
|
||||
- Simplest approach: one small Node adapter delegating to the existing streamer
|
||||
- Rejected complexity: service container, generic command bus, streamer rewrite
|
||||
or duplicate queue/cache state
|
||||
- Unavoidable complexity: scene-relative source and implementation paths are
|
||||
resolved on the main thread with explicit diagnostics
|
||||
- Measured optimization evidence: not applicable; behavior-preserving seam
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: introduced the facade, migrated two runtime scenes and three renderer
|
||||
tools, added regression coverage and updated renderer documentation
|
||||
- Next: M03 integrator reviews and merges `3d528e3`
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-FACADE-001:3d528e3 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-FACADE-001:a100ced -->
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `3d528e3`
|
||||
- Integration: accepted in master merge `a100ced`
|
||||
- Results: `WorldRenderFacade` is the runtime/tool surface for typed focus and
|
||||
detached metrics; the existing streamer remains the sole owner of queues,
|
||||
caches, tasks, nodes and RIDs.
|
||||
- Verification: facade `delegation=3 runtime_scenes=2 tools=3`; streaming focus
|
||||
`contract=1 runtime_scenes=2 capture_tools=3`; coordinate boundary
|
||||
`files=105 native_boundary=1 oracle=1 consumers=5 classifier_cases=6`;
|
||||
render manifest `checkpoints=7 coverage=7 caches=7`; seven-checkpoint headless
|
||||
dry-run; coordination/documentation/diff gates passed.
|
||||
- Fidelity: no changes to renderer target calculation, budgets, queue order,
|
||||
cache versions, placement, visibility, materials or animation. The dry-run
|
||||
retained all seven checkpoint plans. No build-12340 parity claim is added.
|
||||
- External/local inputs: ignored native DLL and generated ADT resource scripts
|
||||
were copied from master only to run the worktree smoke. Proprietary extracted
|
||||
ADT and character GLB data were unavailable, producing the expected dry-run
|
||||
degraded-data diagnostics.
|
||||
- Remaining risks: environment, entity visuals and ground query are not yet
|
||||
facade contracts; streamer exported configuration remains transitional scene
|
||||
composition; focus-source sampling adds one small main-thread adapter step per
|
||||
process frame while target refresh remains streamer-throttled.
|
||||
- Documentation updated: inline facade API, `docs/modules/world-renderer.md`
|
||||
public API/inputs/outputs/data-flow/sequence/ownership/recovery/status/source
|
||||
map, and `RENDER.md` file map plus implementation note.
|
||||
@@ -0,0 +1,120 @@
|
||||
# M03-RND-FACADE-ENVIRONMENT-001 — Renderer environment snapshot boundary
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-FACADE-ENVIRONMENT-001:sindo-main-codex:2026-07-18 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M03
|
||||
- Program: RND
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m03-facade-environment`
|
||||
- Lease expires UTC: 2026-07-18
|
||||
- Integrator: M03 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Add an immutable world-environment snapshot accepted by `WorldRenderFacade`,
|
||||
delegate it to the existing DBC sky controller, and migrate deterministic render
|
||||
checkpoint time setup away from direct sky-controller property mutation.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change Light/Area/Skybox DBC selection, interpolation, fog, sun or shader rules.
|
||||
- Add weather, indoor lighting, new clock semantics or build-12340 parity claims.
|
||||
- Extract the sky service or move its Environment/Node/RID ownership.
|
||||
- Change runtime scene defaults, cache formats, streaming behavior or target status.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/environment/world_environment_snapshot.gd`,
|
||||
`src/tools/verify_world_environment_snapshot.gd`, this claim
|
||||
- Shared/hotspots: `src/render/world_render_facade.gd`,
|
||||
`src/scenes/sky/wow_sky_controller.gd`, both runtime streaming scenes,
|
||||
`src/tools/capture_render_checkpoints.gd`,
|
||||
`src/tools/verify_world_render_facade.gd`, `docs/modules/world-renderer.md`,
|
||||
`docs/modules/README.md`, `RENDER.md`, `targets/03-renderer-facade.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated resources, caches and
|
||||
proprietary renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Public API: immutable `WorldEnvironmentSnapshot` and
|
||||
`WorldRenderFacade.apply_environment_snapshot`
|
||||
- Snapshot input: finite time-of-day hours normalized into one 24-hour day
|
||||
- Adapter: explicit `world_environment_controller_path`; facade/controller retain
|
||||
no ownership of caller snapshot
|
||||
- Schema/cache/coordinate contracts and runtime defaults: unchanged
|
||||
- Consumer: deterministic renderer checkpoint capture; future world-state adapter
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: integrated M03 facade/ground-query master `cb103e1`
|
||||
- Blocks: completion of the facade environment checklist slice and later sky extraction
|
||||
- External state: none for contract tests; visual comparison still needs proprietary corpus
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated cold snapshot contract, facade delegation/wiring regression,
|
||||
capture source-boundary regression, renderer manifest/dry-run and repository gates
|
||||
- Fixtures: normalization, non-finite rejection, missing/wrong controller,
|
||||
exact snapshot delegation and existing runtime scene wiring
|
||||
- Fidelity evidence: preserve existing fixed-time application and all DBC-derived
|
||||
visual logic; seven checkpoint plans remain valid
|
||||
- Performance budget: one immutable value allocation per environment update and
|
||||
constant-time main-thread delegation
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs: snapshot, facade adapter and sky-controller application
|
||||
- Module specification: update existing world-renderer specification
|
||||
- Data-flow diagram: snapshot producer through facade into DBC sky controller
|
||||
- Sequence/state/dependency diagrams: application/failure path and ownership notes
|
||||
- Source map/status updates: world-renderer module, module registry if needed, `RENDER.md`
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: `WorldEnvironmentSnapshot`, `apply_environment_snapshot`
|
||||
- Simplest considered solution: one immutable time snapshot over the existing sky implementation
|
||||
- Rejected complexity/abstractions: generic weather graph, duplicated lighting DTO,
|
||||
event bus, service container or alternate Environment owner
|
||||
- Unavoidable complexity and justification: facade resolves a separate scene-owned
|
||||
sky controller because the streamer does not own outdoor environment rendering
|
||||
- Measured optimization evidence: not applicable; bounded constant-time adapter
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: immutable value, facade/controller adapter, checkpoint migration,
|
||||
regressions and renderer module documentation
|
||||
- Next: M03 integrator reviews and merges `2ee647a`
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-FACADE-ENVIRONMENT-001:2ee647a -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-FACADE-ENVIRONMENT-001:c69abd6 -->
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `2ee647a`
|
||||
- Results: `WorldEnvironmentSnapshot` normalizes finite time into one day and
|
||||
exposes non-finite failure explicitly. `WorldRenderFacade` delegates the
|
||||
immutable value to the existing sky controller; checkpoint capture no longer
|
||||
locates or mutates that controller directly.
|
||||
- Verification: snapshot `contract=5`; facade `delegation=6 ground_contract=2
|
||||
environment_contract=7 runtime_scenes=2 tools=3`; internal access `43/7/9/1`;
|
||||
terrain, coordinate (`files=116 consumers=5`), manifest (`7/7`), shutdown,
|
||||
scheduler (`20000` iterations), planner (`250` iterations), focus, editor parse,
|
||||
dry-run capture, coordination, documentation and diff gates passed.
|
||||
- Fidelity: applying a snapshot retains the previous three fixed-clock assignments
|
||||
and leaves DBC profile/area/skybox lookup, interpolation, fog, sun, shader,
|
||||
cache and streaming rules unchanged. Dry-run retained all seven plans and
|
||||
reported dusk as `time=19.00`. No build-12340 parity claim is added.
|
||||
- External/local inputs: ignored native DLL and generated ADT resource scripts
|
||||
were copied from master for parse/dry-run only. Missing proprietary ADT/DBC/
|
||||
character data produced expected degraded diagnostics.
|
||||
- Remaining risks: the snapshot currently covers exact world time only; weather,
|
||||
indoor state and asset-backed paired zone-transition evidence remain open.
|
||||
- Documentation updated: inline value/facade/controller API plus world-renderer
|
||||
API/input-output/data-flow/sequence/ownership/failure/capability/source-map and
|
||||
`RENDER.md` implementation notes.
|
||||
- Integration: accepted in master merge `c69abd6`; post-merge snapshot, facade,
|
||||
internal-access, manifest, shutdown, scheduler, planner and focus gates passed.
|
||||
@@ -0,0 +1,115 @@
|
||||
# M03-RND-FACADE-GROUND-QUERY-001 — Rendered ground query facade
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-FACADE-GROUND-QUERY-001:sindo-main-codex:2026-07-18 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M03
|
||||
- Program: RND
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m03-facade-ground-query`
|
||||
- Lease expires UTC: 2026-07-18
|
||||
- Integrator: M03 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Expose typed ground-height sampling and a detached readiness diagnostic through
|
||||
`WorldRenderFacade`, using already loaded render terrain meshes, and migrate the
|
||||
terrain-height probe away from direct streamer state/queue access.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Replace the M02 gameplay-owned `TerrainQuery`/`AdtTerrainQuery` composition.
|
||||
- Make rendered mesh sampling authoritative for movement, collision or server state.
|
||||
- Add slopes, holes, liquid, WMO/M2 collision or async query callbacks.
|
||||
- Change terrain meshes, streaming policy, queue order, cache formats or visuals.
|
||||
- Complete environment/entity-visual facade contracts or mark M03 complete.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: this claim
|
||||
- Shared/hotspots: `src/render/world_render_facade.gd`,
|
||||
`src/scenes/streaming/streaming_world_loader.gd`,
|
||||
`src/tools/probe_render_terrain_height.gd`,
|
||||
`src/tools/verify_world_render_facade.gd`,
|
||||
`src/tools/verify_renderer_internal_access.gd`, `docs/modules/world-renderer.md`,
|
||||
`docs/modules/terrain-query.md`, `RENDER.md`, `targets/03-renderer-facade.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated ADT resource scripts,
|
||||
extracted renderer corpus and probe reports
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Input: immutable `GodotWorldPosition` in Godot world units
|
||||
- Output: renderer-owned immutable `RenderedGroundSample` plus optional detached diagnostic Dictionary
|
||||
- Sampling source: loaded quality or tile-LOD Mesh and its live tile transform
|
||||
- M02 ADT query cache, renderer cache versions and coordinate contracts: unchanged
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: M02 terrain query contract and integrated M03 facade/access gate `53d84cc`
|
||||
- Blocks: removal of renderer diagnostic internal-access exception and later terrain service extraction
|
||||
- External state: asset-backed probe is optional; contract tests use a facade double
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: facade query/snapshot isolation contract, internal-access gate,
|
||||
terrain-query regression, focus/coordinate/manifest/shutdown regressions and repository gates
|
||||
- Fixtures: available/unavailable sample, detached diagnostic, probe source migration
|
||||
- Fidelity evidence: mesh ray/nearest fallback algorithm moves unchanged from the probe
|
||||
- Performance budget: on-demand diagnostic query only; no per-frame query added
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline facade/streamer API docs
|
||||
- World-renderer API/data-flow/sequence/ownership/recovery/source-map updates
|
||||
- Terrain-query backend status/ownership clarification and renderer implementation note
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names: `sample_ground_height`, `renderer_ground_query_snapshot`
|
||||
- Simplest approach: one typed facade query over one existing diagnostic read model
|
||||
- Rejected complexity: duplicate ADT cache, physics service, callback bus, query node,
|
||||
generic spatial interface or new dependency
|
||||
- Unavoidable complexity: triangle-mesh ray query retains the existing bounded
|
||||
nearby fallback for seam/edge diagnostic behavior
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: added renderer-owned typed sample, facade query/snapshot, migrated terrain
|
||||
probe, expanded internal-access/coordinate gates and updated module docs
|
||||
- Next: M03 integrator reviews and merges `b697a89` plus cold-start fix `9408d88`
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-FACADE-GROUND-QUERY-001:9408d88 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-FACADE-GROUND-QUERY-001:d6e5b53 -->
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commits: implementation `b697a89`; cold-start fix `9408d88`
|
||||
- Results: `WorldRenderFacade` delegates loaded-mesh ground sampling and returns
|
||||
renderer-owned `RenderedGroundSample`; detached diagnostic snapshots preserve
|
||||
the probe report while hiding queue/tile/Mesh/Node references. Gameplay remains
|
||||
on the independent M02 `TerrainQuery` composition.
|
||||
- Verification: facade `delegation=5 ground_contract=2 runtime_scenes=2 tools=3`;
|
||||
cold rendered-ground value `contract=3` from an empty Godot class cache;
|
||||
internal access `private_symbols=43 gameplay=7 editor_sources=9 renderer_tools=1`;
|
||||
terrain query contracts passed; coordinate boundary `files=113 consumers=5`;
|
||||
renderer manifest `7/7`; shutdown ownership, scheduler/planner, coordination,
|
||||
documentation and diff gates passed.
|
||||
- Fidelity: the probe's 5000-unit ray height, 3x3 tile search, highest-hit choice,
|
||||
readiness fields and `2/5/10/20/40` nearby fallback moved unchanged. No terrain
|
||||
geometry, streaming, cache, material, placement or visual rule changed. No
|
||||
build-12340 parity claim is added.
|
||||
- External/local inputs: ignored native DLL and generated ADT resource scripts
|
||||
were copied from master only for editor/streamer parse and then removed.
|
||||
Proprietary extracted world data was unavailable, producing expected degraded diagnostics.
|
||||
- Remaining risks: triangle-mesh generation is on-demand/main-thread and diagnostic;
|
||||
it is unsuitable for per-physics-frame gameplay; exact asset-backed probe output
|
||||
still depends on local corpus readiness; environment/entity facade contracts remain.
|
||||
- Documentation: inline facade/loader/value API; world-renderer API/data-flow/
|
||||
sequence/ownership/recovery/capability/source map; terrain-query authority
|
||||
clarification and renderer implementation notes updated.
|
||||
- Integration: implementation accepted in master merge `80cb084`; cold-start fix
|
||||
accepted in merge `d6e5b53`. Post-merge cold/facade/access/terrain/coordinate/
|
||||
manifest/shutdown/scheduler/planner/focus and repository gates passed.
|
||||
@@ -0,0 +1,106 @@
|
||||
# M03-RND-INTERNAL-ACCESS-GATE-001 — Streamer internal access gate
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-INTERNAL-ACCESS-GATE-001:sindo-main-codex:2026-07-18 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M03
|
||||
- Program: RND
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m03-streamer-internal-access-gate`
|
||||
- Lease expires UTC: 2026-07-18
|
||||
- Integrator: M03 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Add an automated repository boundary gate proving gameplay code and Godot
|
||||
`EditorPlugin` implementations do not access `StreamingWorldLoader` private
|
||||
queue, task, cache or tile-state fields.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change renderer behavior, queues, jobs, cache formats or facade contracts.
|
||||
- Ban scene composition references to the loader script or facade adapter path.
|
||||
- Migrate renderer-only diagnostic probes that inspect internal readiness/terrain;
|
||||
those require the later metrics/ground-query facade contract.
|
||||
- Mark M03 complete.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/tools/verify_renderer_internal_access.gd`, this claim
|
||||
- Shared/hotspots: `docs/modules/world-renderer.md`, `RENDER.md`,
|
||||
`targets/03-renderer-facade.md`
|
||||
- Read-only scan scope: `src/gameplay/**/*.gd`, `src/editor/**/*.gd`, Godot
|
||||
`EditorPlugin` package scripts under `src/**/*.gd` and `addons/**/*.gd`, and
|
||||
the streamer field declarations
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Input: repository GDScript source and actual private streamer declarations
|
||||
- Output: deterministic pass/fail diagnostics with consumer path and symbol
|
||||
- Runtime, scene, cache, coordinate and asset contracts: unchanged
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: integrated facade and scheduler master `a023d76`
|
||||
- Blocks: safe service extraction and M03 dependency regression acceptance
|
||||
- External state: none
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated internal-access gate, facade regression, coordination,
|
||||
documentation and diff gates
|
||||
- Fixtures: gameplay source discovery, EditorPlugin discovery and synthetic
|
||||
forbidden symbol detection
|
||||
- Fidelity evidence: source-only enforcement; no runtime or visual code changes
|
||||
- Performance budget: one bounded repository source scan in a headless test process
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- World-renderer boundary/verification/known-gap/source-map updates
|
||||
- Renderer implementation note documenting diagnostic-tool exception
|
||||
- No new module spec because this package adds a verifier to the existing renderer module
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name: `verify_renderer_internal_access.gd`
|
||||
- Simplest approach: derive forbidden fields from the current streamer instead
|
||||
of maintaining a second hand-written queue list
|
||||
- Rejected complexity: language plugin, custom linter framework or runtime proxy
|
||||
- Unavoidable complexity: recursive source discovery expands each actual
|
||||
`EditorPlugin` root to its package scripts while excluding reference mirrors
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: derived 43 private renderer symbols, scanned seven gameplay and nine
|
||||
EditorPlugin-package sources, added synthetic positive/negative fixtures and docs
|
||||
- Next: M03 integrator reviews and merges `b58bf2c`
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-INTERNAL-ACCESS-GATE-001:b58bf2c -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-INTERNAL-ACCESS-GATE-001:7e35de7 -->
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `b58bf2c`
|
||||
- Results: repository gate derives private queue/task/cache/state declarations
|
||||
from the streamer and rejects external member/reflection access in gameplay and
|
||||
EditorPlugin package sources. Current scope passes with `43/7/9` inventory counts.
|
||||
- Verification: internal access, facade and budget scheduler headless contracts;
|
||||
Godot editor scan with local ignored dependencies; coordination, documentation
|
||||
and diff gates passed.
|
||||
- Fidelity: source-only enforcement changes no runtime configuration, queue order,
|
||||
cache version, placement, material, animation or visibility behavior. No
|
||||
build-12340 parity claim is added.
|
||||
- External/local inputs: ignored native DLL and two generated ADT resource scripts
|
||||
were copied from master only for facade/editor smoke and then removed. Proprietary
|
||||
extracted world data was unavailable, producing expected degraded-data diagnostics.
|
||||
- Remaining risks: renderer diagnostic terrain probe remains an explicit exception;
|
||||
whitespace-obfuscated dynamic reflection is outside the lightweight source gate;
|
||||
future non-EditorPlugin authoring code is already prohibited by repository policy.
|
||||
- Documentation: world-renderer boundary/verification/capability/known-gap/source
|
||||
map and `RENDER.md` implementation note updated; no new module was introduced.
|
||||
- Integration: accepted in master merge `7e35de7`; post-merge access gate
|
||||
`private_symbols=43 gameplay=7 editor_sources=9`, facade/scheduler/planner and
|
||||
repository gates passed.
|
||||
@@ -0,0 +1,116 @@
|
||||
# M03-RND-SCHEDULER-001 — Bounded render budget scheduler
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-SCHEDULER-001:sindo-main-codex:2026-07-18 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M03
|
||||
- Program: RND
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m03-render-budget-scheduler`
|
||||
- Lease expires UTC: 2026-07-18
|
||||
- Integrator: M03 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract the streamer's per-frame main-thread operation quotas into a scene-free
|
||||
`RenderBudgetScheduler`, preserving existing queue priority and quota values and
|
||||
stopping permit issuance after renderer teardown cancellation.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Move queue, worker-task, cache, Node, Resource or RID ownership out of the streamer.
|
||||
- Change queue ordering, concurrency limits, exported defaults or visual behavior.
|
||||
- Add a generic job framework, priority graph, time-based adaptive budgeting or dependency.
|
||||
- Change cache formats, renderer profiles or target milestone status.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/streaming/render_budget_scheduler.gd`,
|
||||
`src/tools/verify_render_budget_scheduler.gd`,
|
||||
`docs/modules/render-budget-scheduler.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
|
||||
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
|
||||
`targets/03-renderer-facade.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated cache resources and
|
||||
proprietary renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Input: immutable per-frame map from documented operation lane IDs to
|
||||
non-negative operation limits
|
||||
- Output: boolean permits and remaining/consumed diagnostic snapshots
|
||||
- Lifecycle: `begin_frame` resets permits; `cancel` permanently rejects permits
|
||||
for the current scheduler instance
|
||||
- Queue contents, ordering, task concurrency and schema/cache versions: unchanged
|
||||
- Consumer: `StreamingWorldLoader` main-thread queue/finalize drains
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: integrated M03 facade and streaming planner on master `650d584`
|
||||
- Blocks: renderer performance regression gate and per-service extraction
|
||||
- External state: none; tests use synthetic lane limits and no world assets
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated scheduler verifier, facade/planner/focus/coordinate
|
||||
regressions, renderer manifest/dry-run and repository gates
|
||||
- Fixtures: shared lane priority, independent lanes, zero/negative limits,
|
||||
frame reset, unknown lane and cancellation
|
||||
- Fidelity evidence: operation defaults and loop order are captured before
|
||||
migration; all seven renderer checkpoint plans remain valid
|
||||
- Performance budget: permit checks are constant-time dictionary operations and
|
||||
create no Node, Resource, worker task, cache entry or RID
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs for scheduler lifecycle, permits and diagnostics
|
||||
- New module specification with input/output table, data-flow, sequence and
|
||||
lifecycle diagrams
|
||||
- Updated world-renderer flow/ownership/source map, module registry and renderer notes
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name: `RenderBudgetScheduler`
|
||||
- Simplest approach: one small main-thread service with named quota lanes
|
||||
- Rejected complexity: generic job graph, callbacks, queue ownership, adaptive
|
||||
timing controller, service container or third-party dependency
|
||||
- Unavoidable complexity: chunk removal and creation intentionally share one
|
||||
lane so removal retains priority while consuming the same historical budget
|
||||
- Measured optimization evidence: no optimization claim; exact bounded extraction
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: extracted all 16 historical operation quotas, migrated ordered drains,
|
||||
added terminal teardown cancellation, asset-free verification and module docs
|
||||
- Next: M03 integrator reviews and merges `e52f703`
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-SCHEDULER-001:e52f703 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-SCHEDULER-001:7ece2ab -->
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `e52f703`
|
||||
- Results: `RenderBudgetScheduler` owns only per-frame lane counters and terminal
|
||||
cancellation; `StreamingWorldLoader` retains queue order, task/cache/node/RID
|
||||
ownership and operation execution. Chunk removal/create share one historical budget.
|
||||
- Verification: scheduler `cases=6 iterations=20000 elapsed_ms=7.495`; planner
|
||||
`cases=5 average_ms=1.879`; facade/focus contracts passed; coordinate boundary
|
||||
`files=111 consumers=6`; renderer manifest `checkpoints=7 coverage=7 caches=7`;
|
||||
shutdown ownership, documentation, coordination and diff gates passed.
|
||||
- Fidelity: exported defaults, quality presets, queue order, operation sites,
|
||||
cache versions and visible rendering rules are unchanged. Seven checkpoint
|
||||
plans remain valid. No build-12340 parity claim is added.
|
||||
- External/local inputs: ignored native DLL and two generated ADT resource scripts
|
||||
were copied from master only for the worktree parse/smoke and then removed.
|
||||
Proprietary extracted ADT data was unavailable, producing expected degraded-data diagnostics.
|
||||
- Remaining risks: queue storage length and worker concurrency remain streamer-owned;
|
||||
cancellation stops permit issuance but does not interrupt in-flight work; exact
|
||||
p95/p99 impact still requires an asset-backed baseline.
|
||||
- Documentation: new scheduler module spec with API, input/output, data-flow,
|
||||
sequence, lifecycle, ownership, cancellation, performance and source-map sections;
|
||||
world renderer, module registry and renderer implementation notes updated.
|
||||
- Integration: accepted in master merge `7ece2ab`; post-merge scheduler timing
|
||||
`cases=6 iterations=20000 elapsed_ms=10.216`, all listed regressions and gates passed.
|
||||
@@ -0,0 +1,120 @@
|
||||
# M03-RND-STREAMING-PLANNER-001 — Pure streaming target planner
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-STREAMING-PLANNER-001:sindo-main-codex:2026-07-17 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M03
|
||||
- Program: RND
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m03-streaming-target-planner`
|
||||
- Lease expires UTC: 2026-07-17
|
||||
- Integrator: M03 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract runtime/editor ADT wanted/retained-set calculation and boundary prefetch
|
||||
selection from `StreamingWorldLoader` into a scene-free, deterministic
|
||||
`StreamingTargetPlanner` with typed focus input and asset-free contract tests.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change load/retain radii, boundary thresholds, queue priority or LOD policy.
|
||||
- Move queue mutation, async jobs, cache ownership or GPU finalization.
|
||||
- Extract `RenderBudgetScheduler` or asset services in this package.
|
||||
- Change cache formats, renderer profiles or target milestone status.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/streaming/streaming_target_planner.gd`,
|
||||
`src/render/streaming/streaming_target_policy.gd`,
|
||||
`src/render/streaming/streaming_target_plan.gd`,
|
||||
`src/tools/verify_streaming_target_planner.gd`,
|
||||
`docs/modules/streaming-target-planner.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
|
||||
`src/tools/verify_coordinate_conversion_boundaries.gd`,
|
||||
`docs/modules/world-renderer.md`, `docs/modules/coordinate-mapping.md`,
|
||||
`docs/modules/README.md`, `RENDER.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated cache resources and
|
||||
proprietary renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Input: immutable `GodotWorldPosition`, available-tile key set and immutable
|
||||
`StreamingTargetPolicy`
|
||||
- Output: immutable `StreamingTargetPlan` with focus tile plus detached
|
||||
wanted/retained tile-key sets
|
||||
- Tile-key representation remains the streamer's existing `<x>_<y>` internal key
|
||||
- Schema/cache/coordinate versions: unchanged
|
||||
- Consumers: `StreamingWorldLoader` runtime/editor refresh paths
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: M01 typed coordinates/focus and integrated facade package `a100ced`
|
||||
- Blocks: budget scheduler extraction and planner performance regression gate
|
||||
- External state: none; tests use synthetic tile catalogs
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated planner verifier, facade/focus/coordinate regressions,
|
||||
renderer manifest/dry-run and repository gates
|
||||
- Fixtures: center, edge, corner, unavailable-tile, clamped-radius and editor cases
|
||||
- Fidelity evidence: expected tile sets are captured from current formulas; full
|
||||
renderer dry-run retains all seven checkpoint plans
|
||||
- Performance budget: planner work remains bounded by square radii and creates no
|
||||
Node, Resource, task, cache or RID
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs for planner/policy/plan
|
||||
- New module specification with input/output table and data-flow diagram
|
||||
- Updated world-renderer data-flow/sequence/ownership/source map
|
||||
- Updated module registry and renderer notes
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names: `StreamingTargetPlanner`, `StreamingTargetPolicy`,
|
||||
`StreamingTargetPlan`
|
||||
- Simplest approach: one pure calculator plus two immutable value objects
|
||||
- Rejected complexity: generic graph planner, priority framework, service
|
||||
container, job ownership or duplicate available-tile catalog
|
||||
- Unavoidable complexity: boundary-prefetch cross product must preserve corner
|
||||
behavior and existing string tile keys
|
||||
- Measured optimization evidence: no new optimization; exact extraction
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready-for-review
|
||||
- Done: extracted immutable policy/plan and pure planner, migrated runtime/editor
|
||||
target refresh, added behavior/dependency/performance coverage and module docs
|
||||
- Next: integrated in master; next M03 package extracts the budget scheduler
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-STREAMING-PLANNER-001:ad8fc41 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-STREAMING-PLANNER-001:643850c -->
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `e887c3b`; cold-start fix `ad8fc41`
|
||||
- Integration: accepted in master merges `d0f74f8` and `643850c`
|
||||
- Results: runtime/editor wanted and retained ADT sets plus boundary-prefetch
|
||||
centers are calculated by scene-free `StreamingTargetPlanner`; streamer keeps
|
||||
queue ordering, LOD/detail state, jobs, caches, nodes and RIDs.
|
||||
- Verification: planner `cases=5 iterations=250 average_ms=1.765`; facade
|
||||
`delegation=3 runtime_scenes=2 tools=3`; focus `contract=1 runtime_scenes=2
|
||||
capture_tools=3`; coordinate boundary `files=109 consumers=6`; renderer
|
||||
manifest `checkpoints=7 coverage=7 caches=7`; shutdown ownership passed;
|
||||
documentation, coordination and diff gates passed.
|
||||
- Fidelity: center/corner/catalog/clamp/editor fixtures preserve the extracted
|
||||
formulas; seven-checkpoint dry-run retained all plans. No radius, threshold,
|
||||
load priority, cache version, placement, material, animation or visibility
|
||||
rule changed. No build-12340 parity claim is added.
|
||||
- External/local inputs: ignored native DLL and generated ADT resource scripts
|
||||
were copied from master for worktree smoke only. Proprietary ADT and character
|
||||
assets were unavailable, so the dry-run emitted expected degraded-data diagnostics.
|
||||
- Remaining risks: exact p95/p99 impact still needs an asset-backed baseline;
|
||||
pathological custom radii are not benchmarked; tile-key strings remain the
|
||||
existing internal catalog representation; queue/LOD application remains monolithic.
|
||||
- Documentation updated: new planner module spec with API/input/output/data-flow/
|
||||
sequence/ownership/recovery/performance/source-map sections; world renderer,
|
||||
coordinate mapping, module registry and renderer implementation notes updated.
|
||||
@@ -36,7 +36,12 @@ TrinityCore/AzerothCore ◄── Network Adapter ◄── Runtime Client
|
||||
|
||||
### Domain
|
||||
|
||||
Содержит идентификаторы, value objects, команды и события: `EntityId`, `WowGuid`, `MapPosition`, `SpellId`, `MoveIntent`, `EntityMoved`. Не содержит side effects.
|
||||
Содержит идентификаторы, value objects, команды и события: stable authored
|
||||
`ContentId`, session-local `EntityId`, opaque wire `WowGuid`, namespaced
|
||||
`ServerEntryId`, typed coordinates, `SpellId`, `MoveIntent`, `EntityMoved`.
|
||||
Эти identity namespaces не конвертируются неявно и не содержат side effects;
|
||||
их lifecycle и mapping определены в
|
||||
[`modules/domain-identities.md`](modules/domain-identities.md).
|
||||
|
||||
### Gameplay
|
||||
|
||||
|
||||
@@ -14,11 +14,12 @@ This offline M00 diagnostic recovers reproducible checkpoint framing when an ori
|
||||
-Checkpoint elwynn_adt_boundary `
|
||||
-YawOffsets -15,-10,-5,0,5,10,15 `
|
||||
-PitchOffsets -10,-5,0,5,10 `
|
||||
-CameraFovValues 38,50,62 `
|
||||
-ViewportWidth 1280 -ViewportHeight 960 `
|
||||
-WaitSeconds 2
|
||||
-WaitSeconds 8
|
||||
```
|
||||
|
||||
Each candidate receives its own output directory and comparison report. The viewport must exactly match the reference image dimensions; the runner fails on `size_mismatch` instead of inventing a score. `ranking.json` sorts candidates by mean perceptual error and then changed-pixel ratio. Use `-PlanOnly` to validate the Cartesian grid and output paths without rendering. Run a coarse grid first, then a finer grid around the best candidate.
|
||||
Each candidate receives its own output directory and comparison report. `-CameraFovValues` adds FOV as another bounded grid dimension; omit it to retain the scalar `-CameraFov` workflow and legacy candidate paths. The viewport must exactly match the reference image dimensions; the runner fails on `size_mismatch` instead of inventing a score. The default 8-second wait matches the M00 manifest and is a readiness precondition: lowering it requires evidence that the checkpoint's WMO/M2 content has already appeared. `ranking.json` sorts candidates by mean perceptual error and then changed-pixel ratio. Use `-PlanOnly` to validate the Cartesian grid and output paths without rendering. Run a coarse grid first, then a finer grid around the best candidate.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
@@ -43,6 +44,20 @@ The headless capture dry-run must report requested yaw and pitch offsets. Compar
|
||||
|
||||
The private `2560x1440` build 12340 `goldshire_inn_large_wmo` reference was evaluated at FOV 62 degrees. A coarse 5x5 grid over yaw/pitch `[-20, -10, 0, 10, 20]` reduced mean error from `0.099632` at `(0, 0)` to `0.077575` at `(-10, -20)`. Extending pitch produced `0.070901` at `(0, -40)` and `0.063574` at `(0, -60)` with changed-pixel ratio `0.549556`.
|
||||
|
||||
This is not a camera-pose solution. Human inspection shows the original-client reference centered on the Goldshire Inn, while the zero-offset OpenWC candidate contains forest/roads and no inn; negative pitch increasingly fills the frame with grass. The monotonically improving full-frame metric rewards similar green color coverage rather than landmark alignment. Therefore the manifest pose is unchanged and this checkpoint remains spatial/placement-composition evidence. A future registration metric must use approved landmark or region masks before it can select camera pose automatically.
|
||||
These values are invalid camera-pose evidence because the sweep used a 2-second wait. A subsequent readiness audit showed 9 WMO instances and no inn after 2 seconds, 113 instances with the inn visible after 8 seconds, and 328 instances after 15 seconds. The apparent monotonic improvement rewarded grass while the landmark was still absent; it does not demonstrate a spatial/placement defect. The manifest pose remains unchanged pending a ready-scene rerun.
|
||||
|
||||
The ready-scene 3x3 rerun at FOV 62 degrees ranked `(yaw=10, pitch=-10)` first with mean error `0.087952` and changed-pixel ratio `0.665527`, compared with `0.101402`/`0.702489` at zero offsets. Human inspection confirms that the inn is present and the direction is plausible. Pitch remains on the tested boundary and the building scale differs from the reference, so these offsets are coarse evidence only; a joint pitch/FOV refinement is required before changing manifest camera values.
|
||||
|
||||
## Goldshire joint pitch/FOV refinement — 2026-07-12
|
||||
|
||||
A WMO-ready joint grid fixed yaw at 10 degrees and evaluated pitch `[-25, -20, -15, -10, -5]` across FOV `[38, 50, 62]`. Full-frame error ranked FOV 62/pitch -25 first at `0.078843` with changed-pixel ratio `0.667721`. Human inspection rejects it as a manifest calibration: the view is dominated by grass and crops the inn. FOV 38/pitch -10 is closer to the reference building scale but ranks only `0.084220`, and horizontal landmark alignment remains different.
|
||||
|
||||
The grid therefore has no approved joint optimum. Full-frame color error continues to trade landmark framing against large terrain regions. The manifest remains unchanged. The next registration method must score an approved inn/road/tree landmark region or explicit landmark coordinates rather than the entire image.
|
||||
|
||||
## Common landmark region — 2026-07-12
|
||||
|
||||
The comparator accepts an optional common screen rectangle through `--region x,y,width,height`; the sweep runner exposes it as `-ComparisonRegion x,y,width,height`. Coordinates are integer pixels in the shared reference/candidate image space. Both images must have identical dimensions and the rectangle must be fully inside them. Omitting the option preserves full-frame behavior.
|
||||
|
||||
The existing 15 WMO-ready Goldshire candidates were rescored inside `[300,100,1400,650]`, covering the reference facade/chimney and nearby trees. ROI error ranked FOV 38/yaw 10/pitch -15 first at `0.081777` with changed-pixel ratio `0.697185`. This is an interior pitch candidate and improves on the full-frame grass preference, but human inspection still rejects manifest calibration: the facade is too large, its upper part is cropped, and the rectangle retains substantial road/terrain pixels. A common rectangle is useful diagnostic weighting, not semantic landmark registration. Explicit approved landmark coordinates or a mask remain necessary.
|
||||
|
||||
During this run the sweep orchestration was hardened in two ways: expected comparator exit code `1` is now collected through an explicit child process, and viewport dimensions are explicit. A reference/candidate `size_mismatch` now fails before ranking instead of producing an empty metric.
|
||||
|
||||
@@ -237,6 +237,23 @@ TODO/FIXME обязан содержать target/work-package ID или diagnos
|
||||
- Медленный понятный reference implementation полезен как oracle для optimized implementation.
|
||||
- Сложная оптимизация удаляется или упрощается, если она не даёт измеримого результата.
|
||||
|
||||
## Coordinate conversion boundary
|
||||
|
||||
- Cross-space world conversions выполняются только через typed API
|
||||
`CoordinateMapper`; ручные world-center offsets, swaps осей и вычисление ADT
|
||||
filename indices из Godot position запрещены.
|
||||
- Scene/render adapter МОЖЕТ создать `Vector3` только после получения
|
||||
`GodotWorldPosition` или перевести engine vector в typed scalar value на своей границе.
|
||||
- Единственная native world-position формула находится в
|
||||
`src/native/src/wow_chunk_reader.h`, поскольку parser workers не вызывают
|
||||
GDScript. Новая native копия требует отдельного boundary decision и parity fixture.
|
||||
- `verify_render_coordinate_calibration.gd` остаётся независимым test oracle;
|
||||
production code не копирует его формулы.
|
||||
- Model-local M2/WMO vertex basis и placement orientation не являются world
|
||||
coordinate conversion. Они документируются и тестируются своим format adapter.
|
||||
- `verify_coordinate_conversion_boundaries.gd` обязателен при изменении
|
||||
coordinates, native parsers, sky, player, streaming или renderer diagnostics.
|
||||
|
||||
## Review checklist
|
||||
|
||||
- Можно ли понять назначение сущности по имени?
|
||||
@@ -245,6 +262,8 @@ TODO/FIXME обязан содержать target/work-package ID или diagnos
|
||||
- Можно ли убрать abstraction без потери correctness/boundary?
|
||||
- Есть ли скрытый mutable state или side effect?
|
||||
- Не появился ли второй источник истины?
|
||||
- Все ли cross-space world coordinates проходят через `CoordinateMapper` или
|
||||
документированную native boundary?
|
||||
- Измерена ли сложная optimization?
|
||||
- Изолирована и документирована ли неизбежная сложность?
|
||||
- Соответствуют ли API docs и diagrams фактическому коду?
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# M00 Final Paired Evidence — 2026-07-13
|
||||
|
||||
## Scope and result
|
||||
|
||||
The final local M00 audit used Godot 4.6.1, the existing cache state, the manifest-standard 8-second readiness wait, and `2560x1440` visual candidates. Seven private WoW 3.3.5a build 12340 references produced fourteen cold/warm comparisons with no missing pair. Private images and generated reports remain outside Git.
|
||||
|
||||
Every pair exceeded the strict regression tolerance. Comparator exit code `1` means a valid report with measured visual gaps; only codes outside `0/1` are infrastructure failures. This evidence establishes coverage and current differences, not renderer parity.
|
||||
|
||||
```powershell
|
||||
.\tools\run_render_baseline.ps1 `
|
||||
-Output user://render_baseline_m00_complete_2026-07-13 `
|
||||
-CacheState existing `
|
||||
-WaitSeconds 8 `
|
||||
-MeasureSeconds 0.5 `
|
||||
-ReferenceCheckpointDirectory .\sources\OpenWCReferenceCheckpoints `
|
||||
-VisualComparisonReport user://render_baseline_m00_complete_2026-07-13/visual_comparison.json `
|
||||
-VisualComparisonWidth 2560 `
|
||||
-VisualComparisonHeight 1440
|
||||
```
|
||||
|
||||
The initial shell timeout detached the canonical capture process after it started. The process completed all fourteen canonical captures and wrote `report.json`; the documented visual-capture and comparator stages were then invoked separately with the same arguments. The final output directory contains both complete reports.
|
||||
|
||||
## Paired metrics
|
||||
|
||||
| Checkpoint | Pass | Mean perceptual error | Changed-pixel ratio | Classification |
|
||||
|---|---|---:|---:|---|
|
||||
| `elwynn_adt_boundary` | cold | 0.087258 | 0.634580 | Composition plus terrain/vegetation/material gaps |
|
||||
| `elwynn_adt_boundary` | warm | 0.087255 | 0.634598 | Stable against revisit |
|
||||
| `elwynn_sky_dusk` | cold | 0.094778 | 0.575956 | Approximate paired camera; sky color, lighting and scene-composition gaps |
|
||||
| `elwynn_sky_dusk` | warm | 0.094757 | 0.575898 | Stable against revisit |
|
||||
| `elwynn_terrain_overview` | cold | 0.174137 | 0.851791 | Largest composition/framing gap |
|
||||
| `elwynn_terrain_overview` | warm | 0.176955 | 0.861271 | Stable gap, not a cold-load artifact |
|
||||
| `elwynn_waterfall_liquid` | cold | 0.088629 | 0.706522 | Framing, terrain, liquid and lighting gaps |
|
||||
| `elwynn_waterfall_liquid` | warm | 0.087398 | 0.693690 | Stable against revisit |
|
||||
| `goldshire_dense_m2` | cold | 0.066893 | 0.575257 | Smallest available paired gap; vegetation/material differences remain |
|
||||
| `goldshire_dense_m2` | warm | 0.066927 | 0.575592 | Stable against revisit |
|
||||
| `goldshire_inn_large_wmo` | cold | 0.101224 | 0.701835 | WMO is ready; manual framing and presentation gaps remain |
|
||||
| `goldshire_inn_large_wmo` | warm | 0.101206 | 0.701855 | Stable against revisit |
|
||||
| `gryphon_roost_native_animation` | cold | 0.119467 | 0.882073 | Same build-12340 asset, different real/synthetic placement and uncontrolled animation phase |
|
||||
| `gryphon_roost_native_animation` | warm | 0.119471 | 0.882076 | Full-frame metric is diagnostic only; animation observation is human evidence |
|
||||
|
||||
Cold and warm values are close for all seven references. The measured gaps are therefore not explained by first-visit cache readiness. Goldshire timing evidence separately demonstrated that the 8-second wait is necessary for WMO publication.
|
||||
|
||||
## Animation and sky provenance
|
||||
|
||||
The private source corpus preserves exact screenshot bytes; no resize, crop or generated content was used:
|
||||
|
||||
| Evidence | SHA-256 | Interpretation |
|
||||
|---|---|---|
|
||||
| `elwynn_sky_dusk.jpg` | `1fb06836cacae714c76eaf9b8bf7c292fd5e2f2df33eef821bfe4c4a6bd97072` | Build 12340, Elwynn camera position, server time approximately 19:00, UI hidden |
|
||||
| `gryphon_roost_native_animation.jpg` | `d3488c741cb8dfe7212198e48332842631a951862a4388b67045338f37357447` | Build 12340 real-world `GryphonRoost01` placement, first visible wing phase |
|
||||
| diagnostic animation phase B | `1025572c7ab9fa0d2e500ff026be46eb9c1e6e5447116dbdc3675dd5f44574ab` | Same camera three seconds later; visibly raised wing phase |
|
||||
| diagnostic placement context | `b12c0b713a7d75123ed30f5a316b33537331442cd56615aea69214c7cc4e7a42` | Two real Lakeshire placements and surrounding build-12340 scene |
|
||||
|
||||
An ADT scan found no `GryphonRoost01` placement in Goldshire. The M00 checkpoint deliberately injects the model there as a synthetic probe. The original-client observation therefore uses two real Lakeshire placements. The three-second pose change establishes `Observed` native animation behavior, but the full-frame comparison cannot establish spatial or animation-phase parity.
|
||||
|
||||
## Fidelity interpretation
|
||||
|
||||
M00 now has private build 12340 evidence for every required coverage category: terrain, ADT boundary, dense M2, large WMO, liquid, animated M2 and sky transition. The sky reference is approximately paired by map position/time and manually approved composition. Animated-M2 evidence is asset-paired but placement-unpaired because the OpenWC probe is synthetic.
|
||||
|
||||
The reports demonstrate current differences and do not claim `1:1`. Camera calibration experiments also show that unknown manual yaw/pitch/FOV cannot be recovered reliably from full-frame color error alone. Region scoring reduces terrain bias but still requires semantic landmark approval.
|
||||
|
||||
## Operational risks
|
||||
|
||||
A follow-up shutdown ownership fix removed the GUI renderer leaks found by the final visual capture. Before the fix, the focused animated-M2 checkpoint retained 6 mesh, 6 material, 4 shader, 6 texture, 6 instance and associated RenderingDevice RIDs, for 30 resources in use. After the fix, both the focused 8-second capture and a seven-checkpoint single-pass capture completed without Node, resource or RID leak diagnostics.
|
||||
|
||||
Godot can still emit a timing-dependent ObjectDB warning for one or more anonymous `RefCounted` objects whose reported reference count is already zero. The warning has no retained Node, renderer resource or RID ownership in the verified captures. It remains recorded as an engine-teardown diagnostic rather than being hidden, but it is distinct from the resolved GUI renderer leak.
|
||||
|
||||
## Integrator decision checklist
|
||||
|
||||
- Seven required reference checkpoints: cold/warm report complete, `compared=14`, `missing=0`.
|
||||
- Animated M2: build-12340 native motion observed in two phases; synthetic/real placement mismatch explicitly classified.
|
||||
- Sky transition: build-12340 19:00 evidence present with approximate paired camera and human-approved composition.
|
||||
- Renderer visual differences: registered as gaps, not parity claims.
|
||||
- GUI renderer RID/resource diagnostics: resolved by explicit shutdown ownership and covered by a headless regression verifier; the residual zero-reference `RefCounted` engine-teardown warning remains tracked separately.
|
||||
- Target status, target Evidence and `OPENWC_TARGET_DONE` marker: integrator-only actions.
|
||||
+19
-1
@@ -10,7 +10,10 @@
|
||||
.\tools\run_render_baseline.ps1
|
||||
```
|
||||
|
||||
Скрипт последовательно проверяет headless-загрузку проекта, renderer materials, M2 unique-id dedupe, regression manifest и затем снимает checkpoint-ы. Для CI или быстрой проверки контракта без PNG:
|
||||
Скрипт последовательно проверяет headless-загрузку проекта, renderer materials,
|
||||
M2 unique-id dedupe, regression manifest, coordinate boundaries и dedicated
|
||||
server-spawn renderer contract, затем снимает checkpoint-ы. Для CI или быстрой
|
||||
проверки контракта без PNG:
|
||||
|
||||
```powershell
|
||||
.\tools\run_render_baseline.ps1 -DryRun -WaitSeconds 0.1 -MeasureSeconds 0.1
|
||||
@@ -18,6 +21,21 @@
|
||||
|
||||
Если `godot` отсутствует в `PATH`, скрипт использует локальный executable, указанный в `RENDER.md`. Другой executable задаётся через `-GodotPath`.
|
||||
|
||||
Отдельный M01 server-spawn checkpoint использует тот же capture pipeline, но не
|
||||
расширяет семичастный M00 visual baseline:
|
||||
|
||||
```powershell
|
||||
godot --headless --path . --script res://src/tools/verify_server_spawn_renderer.gd
|
||||
godot --path . --script res://src/tools/capture_render_checkpoints.gd -- `
|
||||
--manifest res://src/tools/server_spawn_render_manifest.json `
|
||||
--output user://server_spawn_renderer
|
||||
```
|
||||
|
||||
Manifest связывает pinned AzerothCore Human Warrior spawn с Godot position,
|
||||
ADT tile/chunk и renderer-native diagnostic marker. Маркер начинается точно в
|
||||
spawn origin и проходит обычный depth test, поэтому screenshot показывает его
|
||||
отношение к terrain; это integration evidence, а не заявление visual parity.
|
||||
|
||||
Результат по умолчанию находится в `user://render_baseline`:
|
||||
|
||||
- `report.json` — Godot, OS, CPU, GPU/backend, viewport, revision, cache inventory, cache versions, параметры checkpoint-ов и результаты;
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
# ADR 0001: Canonical world coordinate contract
|
||||
|
||||
- Status: Accepted for M01 contract implementation
|
||||
- Date: 2026-07-13
|
||||
- Work packages: `M01-FND-COORDS-001`, corrected by `M01-FND-COORD-TILE-AXIS-002`
|
||||
- Decision owners: M01 foundation/integration owners
|
||||
|
||||
## Context
|
||||
|
||||
OpenWC currently performs coordinate conversion in native ADT/WDT loaders,
|
||||
`StreamingWorldLoader`, sky, player and renderer diagnostic scripts. The same
|
||||
numbers are frequently carried in `Vector3` or dictionaries, so the coordinate
|
||||
space is implicit. Existing names also mix ADT filename indices, renderer X/Z
|
||||
indices and MDDF/MODF placement fields.
|
||||
|
||||
M00 recorded five positions observed in WoW 3.3.5a build 12340. They verify the
|
||||
current client-position to Godot-position arithmetic within `0.002` yard, but
|
||||
they do not define a cross-layer contract and do not prove camera composition
|
||||
or visual parity.
|
||||
|
||||
Independent references agree on a 64 by 64 ADT grid and an approximately
|
||||
`533.3333` yard tile size. They do not all use the word “canonical” for the same
|
||||
axis order. OpenWC therefore needs names tied to observable formats instead of
|
||||
adopting an internal convention from one reference project.
|
||||
|
||||
## Decision
|
||||
|
||||
### Canonical position
|
||||
|
||||
`CanonicalWowWorldPosition(X, Y, Z)` preserves the numeric X/Y/Z order used by
|
||||
the WoW client, movement protocol and TrinityCore/AzerothCore world positions.
|
||||
X and Y are horizontal; Z is height. Units are WoW yards.
|
||||
|
||||
`ServerWorldPosition` is a distinct type but converts component-for-component
|
||||
to canonical. The type boundary prevents a server record from being passed
|
||||
directly to renderer code and leaves room for adapter validation without a
|
||||
silent axis swap.
|
||||
|
||||
### World extent and ADT indices
|
||||
|
||||
The contract uses:
|
||||
|
||||
```text
|
||||
ADT_TILE_SIZE_YARDS = 1600 / 3
|
||||
ADT_TILES_PER_MAP_AXIS = 64
|
||||
WOW_WORLD_HALF_EXTENT_YARDS = 32 * ADT_TILE_SIZE_YARDS
|
||||
|
||||
tileX = floor(32 - canonical.Y / ADT_TILE_SIZE_YARDS)
|
||||
tileY = floor(32 - canonical.X / ADT_TILE_SIZE_YARDS)
|
||||
```
|
||||
|
||||
`AdtTileCoordinate(tile_x, tile_y)` always means the indices in
|
||||
`Map_tileX_tileY.adt`. Results are intentionally not clamped: the exact
|
||||
south-east extent produces index 64 and remains diagnosable as out of bounds.
|
||||
Normal valid indices are 0 through 63.
|
||||
|
||||
An `AdtTileLocalPosition` is measured east and south from the tile's
|
||||
north-west corner. Filename `tile_x` and MCNK `chunk_x` grow east; filename
|
||||
`tile_y` and MCNK `chunk_y` grow south, with valid indices 0 through 15. These
|
||||
direction names avoid pretending that ADT file axes are Godot X/Z axes.
|
||||
|
||||
### ADT placement fields
|
||||
|
||||
MDDF/MODF position fields are represented in on-disk X/height/Z order:
|
||||
|
||||
```text
|
||||
adt.X = halfExtent - canonical.X
|
||||
adt.height = canonical.Z
|
||||
adt.Z = halfExtent - canonical.Y
|
||||
```
|
||||
|
||||
Model-local MDDF/MODF Euler corrections are not world orientation. They remain
|
||||
outside this contract until a placement-orientation package has format- and
|
||||
model-specific fixtures.
|
||||
|
||||
### Godot renderer basis
|
||||
|
||||
The compatibility renderer keeps one Godot unit equal to one WoW yard and uses
|
||||
Y-up coordinates from the north-west map extent:
|
||||
|
||||
```text
|
||||
godot.X = halfExtent - canonical.Y # east
|
||||
godot.Y = canonical.Z # up
|
||||
godot.Z = halfExtent - canonical.X # south
|
||||
```
|
||||
|
||||
`GodotWorldPosition` contains scalars rather than `Vector3`. A scene/render
|
||||
adapter may construct `Vector3` only at its boundary.
|
||||
|
||||
### World yaw
|
||||
|
||||
Server/canonical yaw zero faces increasing WoW X; positive yaw turns toward
|
||||
increasing WoW Y. In the mapped Godot basis, Godot forward `-Z` and positive
|
||||
Y-axis yaw produce the same numeric world angle. Mapper conversions therefore
|
||||
normalize the value to `[-PI, PI)` without adding an offset.
|
||||
|
||||
This identity applies to world-facing yaw only. It does not apply to M2/WMO
|
||||
asset axes or placement Euler triples.
|
||||
|
||||
### Contract version 2 correction evidence
|
||||
|
||||
Contract version 1 incorrectly calculated filename `tile_x` from canonical X
|
||||
and `tile_y` from canonical Y. The private WotLK corpus contains the observed
|
||||
Elwynn point in `Azeroth_31_49.adt`, SHA-256
|
||||
`18185B5FAC9566D9C57A9C19ABD75F108370D1795639B6077167C0EFD3672FAE`; the
|
||||
transposed `Azeroth_49_31.adt` does not exist. Raw MCNK headers in that file
|
||||
show `indexX` decreasing world Y (east) and `indexY` decreasing world X (south).
|
||||
OpenRealm independently calculates filename X from world Y and filename Y from
|
||||
world X. Contract version 2 corrects the formulas before any consumer migration.
|
||||
|
||||
### Enforced conversion boundaries
|
||||
|
||||
GDScript production consumers use `CoordinateMapper` for canonical/Godot and
|
||||
Godot/ADT tile-local conversion. `StreamingWorldLoader`, player spawn/terrain
|
||||
sampling, sky/area lookup and terrain diagnostics may construct `Vector3` only
|
||||
at their scene/tool boundary.
|
||||
|
||||
The native ADT/WDT parser path retains one equivalent formula definition in
|
||||
`src/native/src/wow_chunk_reader.h`; worker parsers cannot call GDScript. The
|
||||
M00 renderer calibration keeps one independent formula as a test oracle so a
|
||||
shared mapper defect cannot make both implementation and evidence agree.
|
||||
Repository verification rejects additional world-center, world/tile or named
|
||||
WoW/Godot formulas outside those explicit exceptions. Model-local M2/WMO basis
|
||||
transforms are separate format contracts and are not classified as world-space mapping.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
### Make renderer coordinates canonical
|
||||
|
||||
Rejected because protocol, database and ADT adapters would silently inherit a
|
||||
presentation-specific offset and Y-up basis.
|
||||
|
||||
### Adopt WoWee's internal canonical axis names unchanged
|
||||
|
||||
Rejected because that reference swaps server X/Y into its chosen north/west
|
||||
ordering. OpenWC preserves observable client/server field names and makes the
|
||||
render/ADT transformations explicit instead.
|
||||
|
||||
### Use `Vector3` plus comments
|
||||
|
||||
Rejected because comments cannot prevent server, ADT and Godot values from
|
||||
being passed across the wrong boundary.
|
||||
|
||||
### Generic vector-space or matrix framework
|
||||
|
||||
Rejected as unnecessary complexity for fixed affine transforms and integer
|
||||
grid ownership rules.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Network and server-data adapters can expose typed positions without knowing
|
||||
Godot.
|
||||
- Renderer consumers must explicitly map and then create Godot vectors at their
|
||||
own boundary.
|
||||
- ADT filename indices can no longer be confused with renderer X/Z tile names.
|
||||
- GDScript world/tile consumers use mapper APIs; renderer distance and LOD math
|
||||
remains ordinary Godot-space geometry.
|
||||
- Native ADT/WDT loaders share one boundary formula and the M00 calibration
|
||||
keeps one independent non-production oracle.
|
||||
- New manual axis conversions are review/test violations enforced by the
|
||||
repository-wide source gate.
|
||||
- Persisted schemas are unchanged. Future serialized positions must include an
|
||||
explicit coordinate-space/version discriminator.
|
||||
|
||||
## Verification
|
||||
|
||||
- Five M00 build-12340 client camera positions map to the recorded Godot
|
||||
checkpoints within `0.002` yard.
|
||||
- Server, ADT placement, Godot and tile-local conversions round-trip.
|
||||
- Exact center, tile, chunk and world-extent boundaries have explicit tests.
|
||||
- The observed Elwynn position resolves to the real `Azeroth_31_49.adt` file;
|
||||
MCNK `(0,0)` to `(15,15)` origins confirm X-east and Y-south chunk growth.
|
||||
- World-yaw normalization and round-trip cases cover cardinal angles and wrap.
|
||||
- Direct Godot/ADT tile-local conversions round-trip and production consumers
|
||||
are checked by `verify_coordinate_conversion_boundaries.gd`.
|
||||
|
||||
## References
|
||||
|
||||
- [TrinityCore GridDefines](https://github.com/TrinityCore/TrinityCore/blob/master/src/server/game/Grids/GridDefines.h)
|
||||
- `reference/open-realm/games/world-of-warcraft/renderer/wow/r_wowmap_adt.c`
|
||||
- `reference/open-realm/games/world-of-warcraft/renderer/wow/r_wowmap_objects.c`
|
||||
- `reference/WoWee/include/core/coordinates.hpp`
|
||||
- [`../M00_FINAL_PAIRED_EVIDENCE.md`](../M00_FINAL_PAIRED_EVIDENCE.md)
|
||||
@@ -0,0 +1,122 @@
|
||||
# ADR 0002: Separate domain identity namespaces
|
||||
|
||||
- Status: Accepted for M01 contract implementation
|
||||
- Date: 2026-07-13
|
||||
- Work package: `M01-FND-DOMAIN-IDENTITIES-001`
|
||||
- Decision owners: M01 foundation/integration owners
|
||||
|
||||
## Context
|
||||
|
||||
OpenWC must relate authored content, runtime objects, WoW protocol objects and
|
||||
TrinityCore/AzerothCore rows. All four domains can expose an integer or string,
|
||||
but their identity lifetimes and collision rules are different. A raw `int`, a
|
||||
generic `id` field or one universal ID class would allow an entity sequence to
|
||||
be persisted as content identity, a database entry to be sent as a wire GUID,
|
||||
or equal entry numbers from different tables to collide.
|
||||
|
||||
The Content Project requires stable references before server entries are
|
||||
allocated. Runtime entities require cheap session-local identity even when no
|
||||
server object exists. WoW 3.3.5a GUIDs carry opaque 64 wire bits whose semantic
|
||||
decomposition must be established by protocol fixtures. Server templates use
|
||||
numeric entries scoped by an adapter/table namespace.
|
||||
|
||||
## Decision
|
||||
|
||||
### ContentId
|
||||
|
||||
`ContentId` stores canonical lowercase UUID text in the `8-4-4-4-12` shape.
|
||||
It is the only identity in this decision intended for stable authored-content
|
||||
references and serialization. Construction normalizes whitespace and letter
|
||||
case; `is_valid()` reports malformed input without crashing an import.
|
||||
|
||||
The value validates text shape, not UUID version or variant bits. A future
|
||||
Content Project allocator chooses the UUID generation policy and owns duplicate
|
||||
detection. Every persisted schema that contains a `ContentId` still declares
|
||||
its own schema version and field migration.
|
||||
|
||||
### EntityId
|
||||
|
||||
`EntityId` stores a positive integer allocated by one runtime session owner.
|
||||
It is valid only inside that session and must not be persisted as content,
|
||||
protocol or server identity. Sequence reuse policy belongs to the future entity
|
||||
registry, not the value object.
|
||||
|
||||
### WowGuid
|
||||
|
||||
`WowGuid` stores opaque `high_32_bits` and `low_32_bits` integers. Each word is
|
||||
validated against `0..0xffffffff`, preserving every unsigned wire bit without
|
||||
combining it into GDScript's signed 64-bit integer. The all-zero value remains
|
||||
an observable protocol sentinel and receives no gameplay interpretation here.
|
||||
|
||||
High-type masks, packed-GUID codecs and object-kind extraction are explicitly
|
||||
deferred to the build-12340 protocol package and require packet fixtures.
|
||||
|
||||
### ServerEntryId
|
||||
|
||||
`ServerEntryId` stores a positive numeric entry together with a normalized,
|
||||
non-empty adapter/table namespace. Equal numbers in different namespaces are
|
||||
different identities. Mapping a server entry to a `ContentId` is explicit
|
||||
adapter/snapshot data; neither value derives the other.
|
||||
|
||||
### Equality and keys
|
||||
|
||||
The types expose domain-named `equals` methods because `RefCounted` `==`
|
||||
compares object references. Stable keys are available only where their lifetime
|
||||
allows them: `ContentId.to_key()`, `WowGuid.to_hex_string()` and
|
||||
`ServerEntryId.to_key()`. `EntityId` exposes only `to_debug_key()` to discourage
|
||||
persistence.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
### One generic `Id<T>` abstraction
|
||||
|
||||
Rejected because GDScript has no compile-time generic type distinction and the
|
||||
shared API would obscure different validation, lifetime and persistence rules.
|
||||
|
||||
### Use strings everywhere
|
||||
|
||||
Rejected because prefixes are a convention rather than a type boundary and
|
||||
would still permit accidental cross-layer assignment.
|
||||
|
||||
### Store WowGuid in one integer
|
||||
|
||||
Rejected because unsigned 64-bit GUIDs can exceed GDScript's positive signed
|
||||
integer range. Split words preserve the wire representation losslessly.
|
||||
|
||||
### Use server entries as ContentId
|
||||
|
||||
Rejected because entries are allocated by a particular backend/table, can
|
||||
collide between namespaces and do not exist while authoring offline content.
|
||||
|
||||
### Generate UUIDs inside ContentId
|
||||
|
||||
Rejected because randomness/allocation is a side effect requiring duplicate,
|
||||
fixture and deterministic-build policy. This value object only normalizes and
|
||||
validates an allocator-provided value.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Cross-layer contracts must name the identity type they accept.
|
||||
- Content references remain stable before and after server deployment.
|
||||
- Runtime entity registries need explicit mappings to wire GUIDs/content IDs.
|
||||
- Server adapters must supply a namespace and maintain entry-to-content maps.
|
||||
- Malformed external values remain representable long enough for adapters to
|
||||
produce diagnostics, but consumers must call the relevant validation method.
|
||||
- Additional typed IDs such as map, spell, item and quest remain separate
|
||||
follow-up contracts rather than aliases of these four values.
|
||||
|
||||
## Verification
|
||||
|
||||
- UUID normalization, shape validation and value equality are fixture-tested.
|
||||
- Zero/positive session sequences establish `EntityId` validity boundaries.
|
||||
- Maximum unsigned GUID words round-trip to exactly sixteen hexadecimal digits;
|
||||
overflow is rejected and zero remains detectable.
|
||||
- Equal server entry numbers in creature/gameobject namespaces do not compare equal.
|
||||
- Dependency search verifies that identity values do not inherit Node/Resource
|
||||
or expose `Vector3`.
|
||||
|
||||
## References
|
||||
|
||||
- [RFC 9562: Universally Unique IDentifiers](https://www.rfc-editor.org/rfc/rfc9562.html)
|
||||
- [`../ARCHITECTURE.md`](../ARCHITECTURE.md)
|
||||
- [`../../targets/roadmap/01-foundation-and-data.md`](../../targets/roadmap/01-foundation-and-data.md)
|
||||
@@ -7,7 +7,16 @@
|
||||
| Module | Current status | Primary specification |
|
||||
|---|---|---|
|
||||
| Foundation/data | Planned/Partial | [`../../targets/roadmap/01-foundation-and-data.md`](../../targets/roadmap/01-foundation-and-data.md) |
|
||||
| Coordinate mapping | Implemented | [`coordinate-mapping.md`](coordinate-mapping.md) |
|
||||
| Domain identities | Implemented contract | [`domain-identities.md`](domain-identities.md) |
|
||||
| Player input | Partial | [`player-input.md`](player-input.md) |
|
||||
| Local player movement | Implemented | [`local-player-movement.md`](local-player-movement.md) |
|
||||
| Terrain query | Implemented | [`terrain-query.md`](terrain-query.md) |
|
||||
| Third-person camera | Implemented | [`third-person-camera.md`](third-person-camera.md) |
|
||||
| Character presentation | Implemented boundary / Partial fidelity | [`character-presentation.md`](character-presentation.md) |
|
||||
| Renderer | Partial | [`world-renderer.md`](world-renderer.md), [`../../RENDER.md`](../../RENDER.md) |
|
||||
| Streaming target planner | Implemented | [`streaming-target-planner.md`](streaming-target-planner.md) |
|
||||
| Render budget scheduler | Implemented | [`render-budget-scheduler.md`](render-budget-scheduler.md) |
|
||||
| Network/session | Planned | [`../../targets/roadmap/03-network-and-session.md`](../../targets/roadmap/03-network-and-session.md) |
|
||||
| Gameplay | Prototype/Planned | [`../../targets/roadmap/04-gameplay-systems.md`](../../targets/roadmap/04-gameplay-systems.md) |
|
||||
| UI/Lua/audio | Prototype/Planned | [`../../targets/roadmap/05-ui-lua-audio.md`](../../targets/roadmap/05-ui-lua-audio.md) |
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
# Character Presentation
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented boundary / Partial fidelity |
|
||||
| Target/work package | M02 / M02-RND-CHARACTER-PRESENTATION-001 |
|
||||
| Owners | Character appearance composition and locomotion animation presentation |
|
||||
| Last verified | `97bb53f`, 2026-07-14 |
|
||||
| Profiles/capabilities | Current render sandbox character experiment |
|
||||
|
||||
## Purpose
|
||||
|
||||
Compose the current sandbox character GLB, geosets, skin textures and starter
|
||||
outfit below the existing `Visual` node, then independently present its
|
||||
stationary or moving animation. Keep visual asset loading and AnimationPlayer
|
||||
state out of gameplay movement and the player scene composition root.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Define authoritative character identity, equipment or movement state.
|
||||
- Change the existing GLB, geoset, texture-compositor, DBC or outfit formats.
|
||||
- Implement full build-12340 animation selection, blending or equipment visuals.
|
||||
- Load world assets, query terrain, process input or control the camera.
|
||||
- Introduce asynchronous streaming, background scene mutation or persistence.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Scene[ThirdPersonWowController composition] --> Appearance[CharacterAppearancePresenter]
|
||||
Model[Configured PackedScene] --> Appearance
|
||||
DBC[Extracted DBC/item textures] --> Appearance
|
||||
Appearance --> Root[CharacterModel root]
|
||||
Root --> Geosets[Existing geoset controller]
|
||||
Root --> Compositor[Existing texture compositor]
|
||||
Appearance -->|ready root| Scene
|
||||
Scene --> Animation[CharacterAnimationPresenter]
|
||||
Velocity[Movement read model] -->|moving boolean| Animation
|
||||
Animation --> Player[AnimationPlayer below root]
|
||||
```
|
||||
|
||||
Allowed dependencies:
|
||||
|
||||
- configured Godot `PackedScene` character asset;
|
||||
- existing `CharacterGeosetController`, `CharacterTextureCompositor` and
|
||||
`WowCharacterOutfitResolver` presentation components;
|
||||
- `AnimationPlayer` and animation resources below the composed character root;
|
||||
- main-thread player scene composition and movement velocity read model.
|
||||
|
||||
Forbidden dependencies:
|
||||
|
||||
- `MoveIntent`, input devices or movement integration;
|
||||
- `TerrainQuery`, ADT parsing, coordinate conversion or streaming decisions;
|
||||
- cameras, network packets, server databases or gameplay reducers;
|
||||
- direct production database writes or cache-format ownership.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `CharacterAppearancePresenter.load_character_appearance(content_root_override)` | Command | Replaces the owned character root and schedules final grounding/outfit composition | Main thread; may be called again during scene lifetime | Returns `false` for empty/invalid model; warning for invalid resource |
|
||||
| `CharacterAppearancePresenter.character_root` | Read-only state | Current owned model root or null | Stable until next load | None |
|
||||
| `character_appearance_ready(character_root)` | Signal | Publishes a fully attached, grounded root | Deferred main-thread completion | Not emitted for invalid/empty model |
|
||||
| `CharacterAnimationPresenter.bind_character_root(character_root)` | Command | Discovers and prepares the first descendant AnimationPlayer | Main thread; binding lifetime | Returns `false` and clears state when absent |
|
||||
| `CharacterAnimationPresenter.present_locomotion(is_moving)` | Command | Selects current stationary/moving candidate and starts it once | Main/physics thread | Returns `false` when unavailable, missing or unchanged |
|
||||
| `active_animation_name` | Read-only state | Selected animation name | Stable until bind/state transition | Empty when unbound |
|
||||
| `has_animation_player` | Read-only state | Whether the presenter has a valid bound player | Stable until bind | None |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Model resource path, class, scale and yaw | Runtime scene composition | Appearance presenter | Exported configuration | Captured at load |
|
||||
| Input | Extracted content root | Player scene configuration | Appearance presenter | Copied string | Captured at load |
|
||||
| Input | Packed character scene | Godot resource loader | Appearance presenter | Shared resource; presenter owns instance | Scene lifetime |
|
||||
| Output | Composed `CharacterModel` root | Appearance presenter | Scene/animation presenter | Appearance presenter owns node | Until replacement |
|
||||
| Output | Ready signal | Appearance presenter | Player composition | Synchronous signal after deferred completion | One per successful load |
|
||||
| Input | Moving/stationary boolean | Player adapter from horizontal velocity | Animation presenter | Value copy | One physics tick |
|
||||
| Output | AnimationPlayer playback mutation | Animation presenter | Rendered character | Bound root owns player/resources | Until next transition |
|
||||
|
||||
Side effects:
|
||||
|
||||
- loads and instantiates the configured model on the main thread;
|
||||
- adds/removes one owned `CharacterModel` subtree;
|
||||
- composes the existing texture/outfit components and reads their extracted inputs;
|
||||
- changes model transform for scale, yaw and ground alignment;
|
||||
- configures locomotion loop modes and starts AnimationPlayer playback;
|
||||
- does not write files, caches, databases or network state.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Config[Model/class/scale/yaw/content root] --> Load[Load PackedScene]
|
||||
Load --> Replace[Replace owned CharacterModel]
|
||||
Replace --> Attach[Attach model and compositor]
|
||||
Attach --> Ground[Fit mesh bounds to visual ground]
|
||||
DBC[Existing outfit resolver] --> Outfit[Apply starter outfit]
|
||||
Ground --> Ready[Emit ready root]
|
||||
Outfit --> Ready
|
||||
Ready --> Bind[Discover AnimationPlayer]
|
||||
Velocity[Horizontal movement velocity] --> State[Moving or stationary]
|
||||
State --> Select[Run/Walk or Stand/Idle selection]
|
||||
Bind --> Select
|
||||
Select --> Playback[Loop and play with blend]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Empty
|
||||
Empty --> Loading: valid load command
|
||||
Loading --> Ready: attach + deferred composition
|
||||
Loading --> Empty: invalid resource
|
||||
Ready --> Loading: replacement load
|
||||
Ready --> Empty: empty model command
|
||||
|
||||
state Animation {
|
||||
[*] --> Unbound
|
||||
Unbound --> Stationary: bind root with animations
|
||||
Stationary --> Moving: moving=true
|
||||
Moving --> Stationary: moving=false
|
||||
Stationary --> Unbound: bind missing player
|
||||
Moving --> Unbound: bind missing player
|
||||
}
|
||||
```
|
||||
|
||||
Appearance replacement removes the old root from the scene immediately and
|
||||
queues it for deletion. Deferred completion checks root identity, so a stale
|
||||
completion cannot publish a replaced model.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Scene as ThirdPersonWowController
|
||||
participant Appearance as CharacterAppearancePresenter
|
||||
participant Existing as Geoset/Compositor/Outfit
|
||||
participant Animation as CharacterAnimationPresenter
|
||||
participant Player as AnimationPlayer
|
||||
Scene->>Appearance: load_character_appearance(content root)
|
||||
Appearance->>Appearance: load, instantiate and attach root
|
||||
Appearance->>Existing: compose textures and starter outfit
|
||||
Appearance-->>Scene: character_appearance_ready(root)
|
||||
Scene->>Animation: bind_character_root(root)
|
||||
Animation->>Player: prepare loops and play Stand/Idle
|
||||
loop Physics ticks
|
||||
Scene->>Animation: present_locomotion(is moving)
|
||||
Animation->>Player: play only on state/name change
|
||||
end
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- The runtime scene owns one appearance presenter on `Visual` and one animation
|
||||
presenter below it.
|
||||
- The appearance presenter exclusively owns the generated `CharacterModel`
|
||||
subtree; imported shared resources remain Godot-owned.
|
||||
- The animation presenter borrows an AnimationPlayer reference until rebind or
|
||||
subtree replacement; the player scene orders rebind through the ready signal.
|
||||
- Loading, node mutation, bounds calculation, outfit setup and playback occur
|
||||
on the main thread. There are no workers, locks, RIDs or cancellation tokens.
|
||||
- The player controller owns neither model resources nor animation state.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Empty model path | Load guard | Clears old root and returns `false` | Contract regression | Configure a path and retry |
|
||||
| Invalid model resource | Failed PackedScene cast | Remains empty | Named warning with path | Correct/extract asset and retry |
|
||||
| Replaced deferred completion | Root identity check | Stale completion ignored | Replacement regression | No action required |
|
||||
| Missing DBC/outfit inputs | Existing resolver returns false/empty | Base model remains visible | Existing resolver diagnostics | Restore extracted inputs and reload |
|
||||
| Missing AnimationPlayer | Recursive discovery returns null | Presenter becomes unbound | Contract return/read state | Bind a root with animations |
|
||||
| Missing candidate animation | Candidate selection returns empty | Current playback unchanged | Verifier/source diagnostics | Extend verified candidate policy later |
|
||||
|
||||
There is no background load cancellation. Replacement is the recovery path and
|
||||
invalid model state is explicit through the return value and null read model.
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| Character model | Human male sandbox GLB | Current sandbox | Before reload | Model scene input |
|
||||
| Character class ID | `1` | Current sandbox | Before reload | Starter outfit lookup |
|
||||
| Uniform scale | `1.0` | Current sandbox | Before reload | Character root scale |
|
||||
| Yaw offset | `90` degrees | Current sandbox | Before reload | Imported model correction |
|
||||
| Extracted content root | `res://data/extracted` | Local content | Per load override | DBC/item texture input |
|
||||
| Animation blend | `0.15` seconds | Current sandbox | Before bind/transition | Playback cross-fade/default blend |
|
||||
| Stationary candidates | `Stand`, then `Idle` | Current sandbox | Fixed | Exact then substring selection |
|
||||
| Moving candidates | `Run`, then `Walk` | Current sandbox | Fixed | Exact then substring selection |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
Presenter state is transient. No saved settings, user profile, database schema
|
||||
or cache format changes. Existing scene paths remain
|
||||
`ThirdPersonPlayer/Visual` and add only `Visual/AnimationPresenter`; model and
|
||||
extracted-data paths retain their defaults. Moving the model exports from the
|
||||
player node to `Visual` is a scene-authoring ownership change, not persisted
|
||||
runtime data migration.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- Logs: invalid model path warns with the responsible presenter and path.
|
||||
- Read models: current character root, animation binding and active name.
|
||||
- Tests: dedicated verifier reports appearance, animation, scene and player boundaries.
|
||||
- Metrics/correlation IDs: not applicable to this synchronous sandbox presenter.
|
||||
|
||||
## Verification
|
||||
|
||||
- Unit/contract: synthetic model fixture verifies load, scale, yaw, grounding,
|
||||
compositor composition, ready signal, replacement and empty-model clearing.
|
||||
- Animation: synthetic libraries verify nested discovery, exact and substring
|
||||
candidate selection, loop policy, blend configuration and duplicate suppression.
|
||||
- Boundary: player source contains no model loading, outfit, bounds or
|
||||
AnimationPlayer logic; appearance and animation dependencies remain separate.
|
||||
- Integration: Eastern Kingdoms, Kalimdor and player regression scenes compose
|
||||
both presenters; existing input/movement/terrain/camera tests remain required.
|
||||
- Fidelity evidence: only preservation of the current sandbox defaults and
|
||||
selection rules. No build-12340 appearance or animation parity is claimed.
|
||||
- Performance: one synchronous model composition per load and constant-time
|
||||
locomotion calls except on state transition; asset streaming remains future work.
|
||||
|
||||
## Extension points
|
||||
|
||||
- Replace starter outfit inputs with a versioned appearance/equipment snapshot.
|
||||
- Replace boolean locomotion input with a typed presentation state after
|
||||
movement/network contracts define walk, run, jump, fall, swim, mount and emotes.
|
||||
- Add asynchronous/preloaded model providers behind the appearance command
|
||||
without moving resource loading back into gameplay.
|
||||
- Add verified animation IDs/variation policy after build-12340 fixtures exist.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Independent appearance composition | Implemented | Synthetic fixture and three scene wirings | Integrator target update |
|
||||
| Independent locomotion animation | Implemented | Exact/fallback/loop/blend regression | Add typed presentation state later |
|
||||
| Current starter outfit | Partial | Existing resolver reused unchanged | Needs extracted DBC fixture and client comparison |
|
||||
| Current skin/geoset composition | Partial | Existing components reused | Full equipment/customization fidelity incomplete |
|
||||
| Build-12340 animation semantics | Planned | No original-client fixture | Capture animation IDs/transitions/timing |
|
||||
| Runtime equipment/network updates | Planned | No snapshot contract | M08/M09/M12 work |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Model loading remains synchronous and can hitch when not preloaded.
|
||||
- Exact appearance, equipment, animation choice, playback speed and blending are unverified.
|
||||
- The moving input is a sandbox threshold boolean, not authoritative locomotion state.
|
||||
- The first descendant AnimationPlayer wins; multi-player imported scenes need a
|
||||
more explicit binding contract if they appear.
|
||||
- Existing geoset/compositor/outfit implementations retain their own fidelity gaps.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/scenes/character/character_appearance_presenter.gd` | Model-root ownership, composition, ground fit and outfit setup |
|
||||
| `src/scenes/character/character_animation_presenter.gd` | AnimationPlayer binding and locomotion playback state |
|
||||
| `src/scenes/character/character_geoset_controller.gd` | Existing geoset visibility and outfit defaults |
|
||||
| `src/scenes/character/character_texture_compositor.gd` | Existing skin/equipment texture composition |
|
||||
| `src/scenes/character/wow_character_outfit_resolver.gd` | Existing DBC starter outfit lookup |
|
||||
| `src/scenes/player/third_person_wow_controller.gd` | Scene composition and moving-state adapter |
|
||||
| `src/tests/scenes/character_presentation_model_fixture.tscn` | Asset-free model/grounding fixture |
|
||||
| `src/tools/verify_character_presentation.gd` | Presenter, scene and dependency regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- ADR: none; current behavior and resource formats are preserved.
|
||||
- Upstream/reference: `targets/02-player-decomposition.md`,
|
||||
`targets/roadmap/02-rendering-and-graphics.md`,
|
||||
`targets/roadmap/04-gameplay-systems.md`, `RENDER.md`.
|
||||
@@ -0,0 +1,265 @@
|
||||
# Coordinate Mapping
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target/work package | `M01-FND-COORDS-001`, `M01-FND-COORD-TILE-AXIS-002`, `M01-QAR-COORD-FIXTURES-001`, `M01-FND-COORD-BOUNDARY-GATE-001`, `M01-QAR-SERVER-SPAWN-RENDERER-001`, `M02-GMP-TERRAIN-QUERY-001` consumer migration |
|
||||
| Owners | Foundation/domain |
|
||||
| Last verified | `a45d521`, 2026-07-14 |
|
||||
| Profiles/capabilities | All profiles; contract version 2 |
|
||||
|
||||
## 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
|
||||
|
||||
```mermaid
|
||||
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 `RefCounted` lifetime;
|
||||
- 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;
|
||||
- `Vector3` as 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 X-east/Y-south 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.godot_to_adt_tile` | Pure static method | Resolve filename indices from typed renderer position | Synchronous; new result value | Result remains unclamped |
|
||||
| `CoordinateMapper.godot_to_adt_tile_local` | Pure static method | Resolve east/height/south offsets from typed renderer position | Synchronous; new result value | Caller validates ownership/range |
|
||||
| `CoordinateMapper.adt_tile_local_to_godot` | Pure static method | Compose typed renderer position from tile/local values | 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/terrain-query adapters | New immutable values | Caller-owned references |
|
||||
| Output | `GodotWorldPosition`, `GodotWorldYaw` | `CoordinateMapper` | Future render/scene adapter | New immutable values | Caller-owned references |
|
||||
| Test input | `coordinate_golden_points.json` schema 1 | Pinned public server row plus private numeric observations | Headless fixture verifier | Versioned repository fixture | Test-process lifetime |
|
||||
|
||||
Side effects:
|
||||
|
||||
- none;
|
||||
- no scene mutation, file/cache write, network send, database access or logging.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
ServerFixture[Pinned AzerothCore spawn] --> FixtureVerifier[Golden fixture verifier]
|
||||
RawADTFixture[Raw ADT numeric metadata] --> FixtureVerifier
|
||||
ClientFixture[Build 12340 observations] --> FixtureVerifier
|
||||
FixtureVerifier --> Mapper
|
||||
ServerFixture --> SpawnManifest[Server spawn render manifest]
|
||||
SpawnManifest --> SpawnVerifier[Renderer checkpoint verifier]
|
||||
SpawnVerifier --> Capture[Live renderer capture]
|
||||
Capture --> Marker[Diagnostic marker at mapped spawn]
|
||||
Server[ServerWorldPosition X,Y,Z] -->|identity components| Canonical[CanonicalWowWorldPosition]
|
||||
ADT[AdtPlacementPosition X,height,Z] -->|half extent - horizontal| Canonical
|
||||
Canonical -->|tileX from Y; tileY from X| 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]
|
||||
Godot -->|typed direct adapter| Tile
|
||||
Godot -->|typed direct adapter| Local
|
||||
Godot --> TerrainQuery[AdtTerrainQuery consumer]
|
||||
Godot --> TargetPlanner[StreamingTargetPlanner consumer]
|
||||
TerrainQuery --> Tile
|
||||
TerrainQuery --> Local
|
||||
TerrainQuery --> Chunk
|
||||
TargetPlanner --> Tile
|
||||
TargetPlanner --> Local
|
||||
Tile -->|tile plus local| Godot
|
||||
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 `RefCounted` references.
|
||||
- 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 | 2 | All | No | Corrected filename/chunk axes 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
|
||||
|
||||
Production code adds no persisted format, cache or migration. Test schema 1 in
|
||||
`coordinate_golden_points.json` persists explicit source provenance, coordinate
|
||||
contract version 2, tolerances, source-space values and expected outputs. It
|
||||
contains no proprietary bytes, database dump or original-client image. 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`.
|
||||
- Cross-source golden test: `src/tools/verify_coordinate_golden_fixtures.gd`.
|
||||
- Boundary enforcement: `src/tools/verify_coordinate_conversion_boundaries.gd`.
|
||||
- Server-spawn renderer contract: `src/tools/verify_server_spawn_renderer.gd`
|
||||
validates the dedicated manifest against the same pinned fixture before GUI capture.
|
||||
- Integration/E2E: sky, player spawn, streamer, terrain probe and the extracted
|
||||
`AdtTerrainQuery` use mapper boundaries;
|
||||
`StreamingFocus` provides typed renderer focus, and the pinned AzerothCore
|
||||
Human Warrior spawn is captured in the Eastern Kingdoms renderer at ADT
|
||||
`(32,48)`, chunk `(3,12)`.
|
||||
- Fidelity evidence: five build-12340 camera positions from the M00 manifest map
|
||||
within `0.002` yard; a pinned AzerothCore human-warrior spawn and one raw MCNK
|
||||
header observation independently confirm tile `(32,48)` and chunk `(3,12)`;
|
||||
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, five M00 golden points and migrated GDScript consumers | Network/server adapters remain future consumers |
|
||||
| ADT placement mapping | Implemented | Synthetic/direct round-trip fixtures | Compare native parsed placements during migration |
|
||||
| ADT tile/local/chunk ownership | Implemented | Center/extent boundaries, raw `Azeroth_31_49.adt` evidence and direct Godot adapter round-trip | Native parser remains explicit boundary |
|
||||
| World-facing yaw mapping | Implemented | Cardinal/wrap round-trip fixtures | Placement Euler remains separate |
|
||||
| Scene-free production contract | Implemented | Source boundary, headless tests and repository-wide conversion gate | Maintain gate with new consumers |
|
||||
| Cross-source coordinate fixture | Implemented | Pinned AzerothCore spawn, raw ADT MCNK metadata and five build-12340 points pass one headless verifier | Add live adapter/renderer integration |
|
||||
| TrinityCore populated spawn equivalence | Not verified | The base development schema has no pinned populated row used by this fixture | Pin and verify a TDB snapshot before claiming parity |
|
||||
| Visual/server-spawn integration | Implemented | Dedicated manifest/verifier plus cold/warm GUI capture of the mapped AzerothCore spawn | This is coordinate/renderer integration evidence, not original-client visual parity |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Native ADT/WDT parsing retains one shared boundary formula because worker
|
||||
parsers cannot call GDScript; the gate prevents additional definitions.
|
||||
- Contract version 1 briefly transposed ADT filename/chunk axes; version 2 fixes
|
||||
the defect before any consumer migration.
|
||||
- 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.
|
||||
- The golden server row is verified against pinned AzerothCore data only; no
|
||||
TrinityCore equivalence claim is made without a pinned populated TDB snapshot.
|
||||
|
||||
## 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 |
|
||||
| `src/tests/fixtures/coordinate_golden_points.json` | Versioned cross-source values, provenance and tolerances |
|
||||
| `src/tools/verify_coordinate_golden_fixtures.gd` | Cross-source fixture schema and mapper validation |
|
||||
| `src/tools/verify_coordinate_conversion_boundaries.gd` | Repository-wide manual conversion and required-consumer gate |
|
||||
| `src/gameplay/terrain/adt_terrain_query.gd` | Typed Godot-position consumer for ADT tile/local/chunk lookup |
|
||||
| `src/render/streaming/streaming_target_planner.gd` | Typed Godot-position consumer for ADT focus tile and boundary-local lookup |
|
||||
| `src/tools/server_spawn_render_manifest.json` | Pinned server-spawn camera, player, marker and expected ADT ownership |
|
||||
| `src/tools/verify_server_spawn_renderer.gd` | Fixture-to-renderer manifest contract verifier |
|
||||
| `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`](../adr/0001-canonical-world-coordinates.md)
|
||||
- M00 evidence: [`../M00_FINAL_PAIRED_EVIDENCE.md`](../M00_FINAL_PAIRED_EVIDENCE.md)
|
||||
- Architecture: [`../ARCHITECTURE.md`](../ARCHITECTURE.md)
|
||||
- Testing rules: [`../TESTING.md`](../TESTING.md)
|
||||
@@ -0,0 +1,232 @@
|
||||
# Domain Identities
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target/work package | `M01-FND-DOMAIN-IDENTITIES-001` |
|
||||
| Owners | Foundation/domain |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m01-domain-identities`, 2026-07-13 |
|
||||
| Profiles/capabilities | All profiles; identity contract version 1 |
|
||||
|
||||
## Purpose
|
||||
|
||||
Provide non-interchangeable, scene-free identity values for authored content,
|
||||
runtime entities, WoW wire objects and namespaced server entries. Each value
|
||||
states its lifetime and serialization rules instead of exposing a generic `id`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Allocating, reserving or globally registering IDs.
|
||||
- Parsing packet bytes or server database rows.
|
||||
- Mapping GUID high types or server entries to gameplay object kinds.
|
||||
- Defining Content Project, snapshot or replay schemas.
|
||||
- Replacing future typed map/spell/item/quest/display/sound IDs.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Authoring[Content Project allocator] --> ContentId[ContentId]
|
||||
Session[Entity registry] --> EntityId[EntityId]
|
||||
Wire[Build 12340 packet adapter] --> WowGuid[WowGuid]
|
||||
Server[Core snapshot adapter] --> ServerEntry[ServerEntryId]
|
||||
ContentId --> Mapping[Explicit identity mapping records]
|
||||
EntityId --> Mapping
|
||||
WowGuid --> Mapping
|
||||
ServerEntry --> Mapping
|
||||
Mapping --> Consumers[Gameplay / network / authoring consumers]
|
||||
Raw[Raw int or string] -. forbidden across boundary .-> Consumers
|
||||
```
|
||||
|
||||
Allowed dependencies:
|
||||
|
||||
- GDScript scalar/string operations and `RefCounted` lifetime;
|
||||
- callers in content, gameplay, network and server adapter layers.
|
||||
|
||||
Forbidden dependencies:
|
||||
|
||||
- Node, Resource, SceneTree, renderer and UI state;
|
||||
- packet buffers, SQL connections or database table implementations;
|
||||
- implicit conversion between identity namespaces;
|
||||
- random/global allocation inside the value objects.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `ContentId` | Immutable value | Stable authored identity as canonical lowercase UUID text | Any thread; persisted content lifetime | `is_valid()` reports malformed shape |
|
||||
| `ContentId.equals` | Pure method | Value equality across references | Call duration | Null compares false |
|
||||
| `ContentId.to_key` | Pure method | Canonical serialization/dictionary key | Stable across schema lifetime | Caller must validate first |
|
||||
| `EntityId` | Immutable value | Positive session-local runtime sequence | One runtime session | `is_valid()` rejects zero/negative |
|
||||
| `EntityId.equals` | Pure method | Equality within an explicitly shared session | Call duration | Cross-session comparison is a caller error |
|
||||
| `EntityId.to_debug_key` | Pure method | Non-persistent diagnostic label | Session/log lifetime | Must not be serialized as content |
|
||||
| `WowGuid` | Immutable value | Opaque unsigned high/low wire words | Packet/replay/entity mapping lifetime | `has_valid_word_ranges()` reports overflow |
|
||||
| `WowGuid.is_empty` | Pure method | Detect all-zero wire sentinel | Call duration | Does not assign semantic meaning |
|
||||
| `WowGuid.to_hex_string` | Pure method | Sixteen-digit diagnostic/replay key | Wire/replay lifetime | Caller validates ranges first |
|
||||
| `ServerEntryId` | Immutable value | Adapter/table namespace plus positive entry | Snapshot/deployment mapping lifetime | `is_valid()` rejects missing namespace/non-positive entry |
|
||||
| `ServerEntryId.to_key` | Pure method | Namespaced mapping key | Snapshot/schema lifetime | Caller validates first |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | UUID text | Future Content Project allocator/parser | `ContentId` | Copied normalized string | Build/content lifetime |
|
||||
| Input | Session sequence | Future entity registry | `EntityId` | Copied integer | One runtime session |
|
||||
| Input | Two unsigned 32-bit words | Future build-12340 packet codec | `WowGuid` | Copied integers | Packet/replay/entity lifetime |
|
||||
| Input | Namespace and numeric entry | Future core snapshot adapter | `ServerEntryId` | Copied normalized string/integer | Snapshot/deployment lifetime |
|
||||
| Output | Validation/equality/key results | Identity values | Adapters/registries/schemas | Returned scalar/string | Call duration |
|
||||
|
||||
Side effects:
|
||||
|
||||
- none;
|
||||
- no allocation registry, random generation, file/cache write, packet read,
|
||||
database query, scene mutation or logging.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
ContentSource[Authored document] -->|UUID text| ContentId
|
||||
ServerRow[Backend table row] -->|adapter + table + entry| ServerEntryId
|
||||
Packet[World packet] -->|high/low wire words| WowGuid
|
||||
Registry[Session entity allocator] -->|positive sequence| EntityId
|
||||
ServerEntryId --> DeployMap[Explicit deployment mapping]
|
||||
ContentId --> DeployMap
|
||||
WowGuid --> RuntimeMap[Explicit runtime mapping]
|
||||
EntityId --> RuntimeMap
|
||||
DeployMap --> Snapshot[Versioned snapshot/change set]
|
||||
RuntimeMap --> WorldState[Session world state]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
state ContentIdentity {
|
||||
[*] --> Parsed
|
||||
Parsed --> Persisted: valid ContentId
|
||||
Persisted --> Referenced
|
||||
}
|
||||
state RuntimeIdentity {
|
||||
[*] --> Allocated
|
||||
Allocated --> Active
|
||||
Active --> Released: session owner removes entity
|
||||
Released --> [*]
|
||||
}
|
||||
state ExternalIdentity {
|
||||
[*] --> Observed
|
||||
Observed --> Mapped: WowGuid or ServerEntryId
|
||||
Mapped --> Stale: disconnect or snapshot replacement
|
||||
Stale --> [*]
|
||||
}
|
||||
```
|
||||
|
||||
The values themselves are stateless and immutable. Allocation, release,
|
||||
mapping and stale-state ownership belong to future registries/adapters.
|
||||
|
||||
## Main sequence
|
||||
|
||||
Identity construction and validation are synchronous. An external adapter reads
|
||||
its format, constructs the matching value, validates it, then publishes an
|
||||
explicit mapping or typed domain event. No value object performs I/O or retries.
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Values own only copied strings/integers and are caller-owned `RefCounted` references.
|
||||
- Public methods do not mutate values or shared state and are worker-safe.
|
||||
- Content/session/wire/server mapping records are owned by their future layer,
|
||||
not by these values.
|
||||
- No Node, RID, file, socket, database, lock or cache resource is acquired.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Malformed ContentId | `is_valid()` | Invalid value remains inspectable | Source field/path supplied by parser | Regenerate or repair authored ID |
|
||||
| Invalid EntityId sequence | `is_valid()` | Registry must reject publication | Session/correlation context | Fix allocator/session mapping |
|
||||
| GUID word overflow | `has_valid_word_ranges()` | Codec must reject packet/replay value | Packet offset/opcode context | Reject malformed input; do not truncate |
|
||||
| Empty GUID | `is_empty()` | Preserved as protocol sentinel | Adapter decides whether field permits empty | Apply opcode/field-specific rule |
|
||||
| Missing server namespace/entry | `is_valid()` | Snapshot adapter rejects mapping | Backend/table/row context | Correct adapter schema mapping |
|
||||
| Unknown/stale mapping | Registry/snapshot lookup | No implicit numeric fallback | Typed IDs in diagnostic | Refresh snapshot/session or reject event |
|
||||
|
||||
There is no cancellation inside constant-time value operations.
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| Identity contract version | 1 | All | No | Four separate namespaces and validation rules |
|
||||
| ContentId text form | Lowercase `8-4-4-4-12` UUID | Authoring/runtime/tools | No | Stable persisted key |
|
||||
| GUID decomposition | Opaque two-word storage | `wotlk_335a_build_12340` | No | Prevents unverified high-type assumptions |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
- `ContentId.uuid_text` is the only stable persisted identity introduced here.
|
||||
- Containing schemas must store their own schema version and duplicate/reference policy.
|
||||
- `EntityId` must never be persisted across sessions.
|
||||
- `WowGuid` may appear in versioned packet replays as explicit high/low words or
|
||||
the exact sixteen-digit hex form; a replay schema owns that choice.
|
||||
- `ServerEntryId` may appear in snapshots only with both namespace and entry.
|
||||
- This package is additive and migrates no existing data or cache.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- Stable diagnostics use `ContentId.to_key`, `WowGuid.to_hex_string` and
|
||||
`ServerEntryId.to_key`.
|
||||
- Runtime diagnostics may use `EntityId.to_debug_key` with a session correlation ID.
|
||||
- Production metrics/logging belong to registries and adapters; values emit none.
|
||||
|
||||
## Verification
|
||||
|
||||
- Contract test: `src/tools/verify_domain_identities.gd`.
|
||||
- Fixtures cover UUID normalization/malformed shape, positive/zero session IDs,
|
||||
maximum/overflow GUID words, zero GUID and cross-table server entry collisions.
|
||||
- Dependency gate confirms no Node/Resource/Vector3 exposure.
|
||||
- Fidelity boundary: GUID bit meaning remains unclaimed until build-12340 packet fixtures.
|
||||
- Performance: constant-time integer checks and a bounded 36-character UUID scan.
|
||||
|
||||
## Extension points
|
||||
|
||||
- Content Project allocator and duplicate/reference validation.
|
||||
- Session-owned entity registry and explicit `WowGuid ↔ EntityId` mappings.
|
||||
- Backend capability adapters that define server entry namespaces.
|
||||
- Separate typed map/spell/item/quest/display/sound values.
|
||||
- Fixture-backed build-12340 GUID high-type decoding in the protocol layer.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Stable authored ContentId | Implemented contract | UUID normalization/validation fixtures | Allocator and schema arrive with Content Project |
|
||||
| Session-local EntityId | Implemented contract | Positive/zero/equality fixtures | Registry lifecycle arrives with gameplay world state |
|
||||
| Lossless opaque WowGuid | Implemented contract | Full unsigned words and hex fixture | Packet codec/high-type semantics need build-12340 fixtures |
|
||||
| Namespaced ServerEntryId | Implemented contract | Cross-table collision fixture | Backend namespace capability mapping arrives with server adapters |
|
||||
| Cross-namespace mappings | Planned | Architecture/ADR boundary | Implement only in owning registries/adapters |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- No allocator or duplicate registry exists yet.
|
||||
- GDScript `==` compares references; consumers must use domain `equals` methods
|
||||
or canonical keys until a registry owns keying.
|
||||
- UUID shape validation deliberately does not enforce generation version/variant.
|
||||
- Server namespace vocabulary is adapter-owned and not yet centrally catalogued.
|
||||
- GUID high-type/object-kind semantics remain unverified rather than guessed.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/domain/identity/content_id.gd` | Stable canonical UUID content identity |
|
||||
| `src/domain/identity/entity_id.gd` | Session-local runtime identity |
|
||||
| `src/domain/identity/wow_guid.gd` | Opaque unsigned WoW wire identity |
|
||||
| `src/domain/identity/server_entry_id.gd` | Namespaced backend entry identity |
|
||||
| `src/tools/verify_domain_identities.gd` | Headless value/boundary regression |
|
||||
| `docs/adr/0002-domain-identity-namespaces.md` | Normative namespace/lifetime decision |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- ADR: [`../adr/0002-domain-identity-namespaces.md`](../adr/0002-domain-identity-namespaces.md)
|
||||
- Architecture: [`../ARCHITECTURE.md`](../ARCHITECTURE.md)
|
||||
- Foundation plan: [`../../targets/roadmap/01-foundation-and-data.md`](../../targets/roadmap/01-foundation-and-data.md)
|
||||
@@ -0,0 +1,292 @@
|
||||
# Local Player Movement
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target/work package | M02 / M02-GMP-MOVEMENT-001, M02-GMP-SANDBOX-CAPABILITIES-001, M02-GMP-INPUT-PROFILE-335-001 and presentation consumers |
|
||||
| Owners | Gameplay local movement state; scene composition owns the instance |
|
||||
| Last verified | M02-GMP-INPUT-PROFILE-335-001 worktree, 2026-07-15 |
|
||||
| Profiles/capabilities | `RenderSandbox` debug movement; `Blizzlike335` selected bindings and keyboard turn with debug sprint/flight rejected |
|
||||
|
||||
## Purpose
|
||||
|
||||
Own current local velocity, acceleration and sandbox flight state independently
|
||||
of the player scene. Convert an immutable `MoveIntent` plus an explicit
|
||||
Godot-world movement basis into deterministic per-tick displacement and convert
|
||||
signed turn intent into a yaw delta without querying terrain, cameras, input
|
||||
devices or visual assets.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Apply world transforms or own a `CharacterBody3D`.
|
||||
- Query terrain height, collision, water or slopes.
|
||||
- Implement jump, fall, swim, server prediction or reconciliation.
|
||||
- Select the player basis versus camera-pivot basis; the scene adapter owns that decision.
|
||||
- Claim build-12340 movement fidelity from sandbox regression alone.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Input[MoveIntent] --> Movement[LocalPlayerMovementController]
|
||||
Basis[Explicit local movement Basis] --> Movement
|
||||
Delta[Physics delta seconds] --> Movement
|
||||
Profile[Scene profile ID] --> Capabilities[PlayerMovementCapabilities]
|
||||
Capabilities --> Movement
|
||||
Movement --> Displacement[Godot-world displacement]
|
||||
Movement --> Yaw[Godot yaw delta radians]
|
||||
Movement --> Velocity[Read-only velocity state]
|
||||
Scene[ThirdPersonWowController] --> Basis
|
||||
Displacement --> Scene
|
||||
Scene --> Transform[Player world transform]
|
||||
Scene --> Terrain[TerrainQuery and ground snap adapter]
|
||||
Velocity --> Presentation[Visual facing and CharacterAnimationPresenter adapter]
|
||||
```
|
||||
|
||||
Allowed dependencies:
|
||||
|
||||
- `MoveIntent` from the gameplay input boundary.
|
||||
- Godot value types `Basis` and `Vector3` for renderer world-space direction math.
|
||||
- Main-thread scene adapter for basis selection and displacement application.
|
||||
|
||||
Forbidden dependencies:
|
||||
|
||||
- `Node`, `Resource`, `Input`, `Camera3D`, `ADTLoader` or scene lookup.
|
||||
- World-coordinate conversion or manual ADT/tile formulas.
|
||||
- Character assets, animation players, materials, packets or server state.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `PlayerMovementCapabilities.render_sandbox()` | Factory | Creates the only debug sprint/free-flight capability set | Immutable caller value | None |
|
||||
| `PlayerMovementCapabilities.blizzlike_335()` | Factory | Creates the compatibility-safe set with debug movement disabled | Immutable caller value | Name is intent, not a parity claim |
|
||||
| `PlayerMovementCapabilities.for_profile_id(...)` | Factory | Maps composition configuration once; unknown IDs fail closed | Immutable caller value | Unknown maps to `Blizzlike335` |
|
||||
| `LocalPlayerMovementController.new(...)` | Constructor | Sets explicit speeds, acceleration, sprint multiplier and movement capabilities | Created by composition root; retained for player scene lifetime | Null capability defaults to `Blizzlike335` |
|
||||
| `godot_world_velocity_units_per_second` | Read-only state | Current Godot-world velocity after the last update | Stable until next mutation on owning thread | None |
|
||||
| `is_flight_enabled` | Read-only state | Current sandbox free-flight mode | Stable until toggle | None |
|
||||
| `movement_profile_id` | Read-only state | Effective capability profile ID | Controller lifetime | None |
|
||||
| `toggle_sandbox_flight()` | Command | Toggles flight only when explicitly permitted and clears velocity | Owning physics/input thread | Rejected request returns `false` with state unchanged |
|
||||
| `advance(move_intent, godot_world_movement_basis, delta_seconds)` | Stateful update | Accelerates toward requested velocity and returns displacement for the tick | Owning physics thread; one call per tick | Negative delta is treated as zero |
|
||||
| `calculate_yaw_delta_radians(move_intent, delta_seconds)` | Pure query | Converts signed turn input to Godot yaw delta | Owning physics thread | Negative delta is treated as zero |
|
||||
|
||||
Constructor units:
|
||||
|
||||
| Parameter | Unit/meaning |
|
||||
|---|---|
|
||||
| `run_speed_units_per_second` | Forward Godot units per second |
|
||||
| `backward_speed_units_per_second` | Backward Godot units per second |
|
||||
| `strafe_speed_units_per_second` | Lateral Godot units per second |
|
||||
| `flight_vertical_speed_units_per_second` | Sandbox vertical Godot units per second |
|
||||
| `acceleration_units_per_second_squared` | Velocity change per second |
|
||||
| `sandbox_sprint_multiplier` | Dimensionless debug multiplier |
|
||||
| `keyboard_turn_speed_radians_per_second` | Positive keyboard yaw speed; defaults to PI rad/s |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | `MoveIntent` | `PlayerInputSource` | Movement controller | Immutable caller-held value | One physics tick |
|
||||
| Input | `PlayerMovementCapabilities` | Application/player scene composition | Movement controller | Immutable caller-held value | Controller lifetime |
|
||||
| Input | Godot-world `Basis` | Player or `ThirdPersonCameraRig` adapter | Movement controller | Value copy | One physics tick |
|
||||
| Input | Delta seconds | Godot physics loop | Movement controller | Scalar value | One physics tick |
|
||||
| Input | Flight toggle command | Remappable sandbox action via scene adapter | Movement controller | Synchronous command | Input event dispatch |
|
||||
| Output | Displacement `Vector3` | Movement controller | Player scene adapter | Value copy | Applied in same tick |
|
||||
| Output | Yaw delta radians | Movement controller | Player scene adapter | Scalar value | Applied before movement basis selection |
|
||||
| Output | Velocity `Vector3` | Movement controller | Facing and `CharacterAnimationPresenter` scene adapter | Read-only value copy | Until next update |
|
||||
| Output | Flight state | Movement controller | Basis/terrain scene adapter | Read-only boolean | Until next toggle |
|
||||
|
||||
Side effects:
|
||||
|
||||
- Mutates only controller-owned velocity and flight state.
|
||||
- Does not mutate scene tree, transforms, resources, filesystem, network or cache.
|
||||
- The scene adapter remains responsible for transform mutation and ground snap.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Intent[MoveIntent axes] --> Target[Calculate target velocity]
|
||||
Intent --> Turn[Calculate signed yaw delta]
|
||||
Turn --> SceneYaw[Scene applies character yaw]
|
||||
Basis[Player or camera-pivot Basis] --> Directions[Forward/right directions]
|
||||
Directions --> Target
|
||||
Config[Speeds and sprint multiplier] --> Target
|
||||
Profile[RenderSandbox or Blizzlike335] --> Capability[Immutable debug capabilities]
|
||||
Capability -->|allow or reject sprint| Target
|
||||
Capability -->|allow or reject flight toggle| Current
|
||||
Previous[Owned velocity] --> Integrate[move_toward by acceleration × delta]
|
||||
Target --> Integrate
|
||||
Integrate --> Current[Updated velocity]
|
||||
Current --> Step[velocity × delta]
|
||||
Step --> Displacement[Scene-applied displacement]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> GroundedSandbox
|
||||
GroundedSandbox --> FlyingSandbox: toggle [debug flight allowed] / clear velocity
|
||||
GroundedSandbox --> GroundedSandbox: toggle [debug flight rejected]
|
||||
FlyingSandbox --> GroundedSandbox: toggle / clear velocity
|
||||
GroundedSandbox --> GroundedSandbox: advance
|
||||
FlyingSandbox --> FlyingSandbox: advance
|
||||
```
|
||||
|
||||
The names describe the existing sandbox mode only. They are not authoritative
|
||||
gameplay movement states and do not imply terrain contact or server permission.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Input as PlayerInputSource
|
||||
participant Scene as ThirdPersonWowController
|
||||
participant Move as LocalPlayerMovementController
|
||||
participant Cap as PlayerMovementCapabilities
|
||||
participant Present as CharacterAnimationPresenter adapter
|
||||
Input-->>Scene: MoveIntent
|
||||
Scene->>Cap: map configured profile once
|
||||
Scene->>Move: construct with immutable capabilities
|
||||
Scene->>Move: calculate_yaw_delta_radians(intent, delta)
|
||||
Move-->>Scene: yaw delta
|
||||
Scene->>Scene: apply yaw and synchronize camera orbit yaw
|
||||
Scene->>Move: advance(intent, selected basis, delta)
|
||||
Move-->>Scene: displacement
|
||||
Scene->>Scene: apply global position and ground snap
|
||||
Scene->>Move: godot_world_velocity_units_per_second
|
||||
Scene->>Present: horizontal motion
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- The player scene creates and exclusively owns one movement controller.
|
||||
- The composition root creates one immutable capability value; the controller
|
||||
retains it and exposes only its effective profile ID.
|
||||
- Velocity and flight state have the same lifetime as that controller.
|
||||
- All mutations currently occur on the main input/physics thread.
|
||||
- Read-only value returns are copies; consumers cannot mutate owned state.
|
||||
- No Nodes, Resources, RIDs, files, jobs or worker threads are created.
|
||||
- The supplied `Basis` is a Godot-world direction basis, not a typed world-position contract or coordinate conversion.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Negative physics delta | `advance` guard | Returns zero displacement; velocity unchanged | Unit regression | Resume with valid non-negative delta |
|
||||
| Missing scene basis owner | Scene composition defect | Controller cannot be called correctly | Scene/parser error or boundary test | Restore explicit scene dependency |
|
||||
| Flight toggle during motion | Command path | Velocity clears before next step | State-transition regression | Continue from zero velocity |
|
||||
| Flight/sprint request in `Blizzlike335` | Capability check | Request has no effect | Profile regression | Select explicit `RenderSandbox` only for tooling/debug scenes |
|
||||
| Unknown/null profile capability | Factory/constructor safe default | Uses `Blizzlike335`; debug movement disabled | Capability regression | Correct composition profile ID |
|
||||
| Missing terrain data | `TerrainGroundSample.is_available == false` | Scene skips existing ground snap | Stable terrain failure code | Replace/recover query backend |
|
||||
|
||||
There is no asynchronous cancellation, retry or rollback. Recreating the
|
||||
controller resets velocity and flight state deterministically.
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| Forward speed | `7.0` units/s | Current sandbox | Fixed for controller lifetime | Target forward velocity |
|
||||
| Backward speed | `4.5` units/s | Current sandbox | Fixed for controller lifetime | Target backward velocity |
|
||||
| Strafe speed | `4.5` units/s | Current sandbox | Fixed for controller lifetime | Target lateral velocity |
|
||||
| Acceleration | `28.0` units/s² | Current sandbox | Fixed for controller lifetime | Acceleration and deceleration |
|
||||
| Flight vertical speed | `7.0` units/s | Sandbox debug | Fixed for controller lifetime | Vertical free-flight target |
|
||||
| Sprint multiplier | `6.0` | Sandbox debug | Fixed for controller lifetime | Multiplies planar and vertical targets |
|
||||
| Camera-relative basis | Selected only while flight is enabled | Sandbox debug | Per tick | Preserves existing pitched flight direction |
|
||||
| Debug sprint permission | Enabled | `RenderSandbox` only | Immutable | Allows sprint request multiplier |
|
||||
| Debug free-flight permission | Enabled | `RenderSandbox` only | Immutable | Allows toggle and vertical movement |
|
||||
| Compatibility-safe debug permissions | Disabled | `Blizzlike335` | Immutable | Ignores sprint/toggle requests |
|
||||
| Keyboard turn rate | `3.141594` rad/s | `Blizzlike335` composition | Fixed for controller lifetime | Server-contract default applied to signed turn intent |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
Movement state is transient and not serialized. No schema, cache, migration or
|
||||
saved settings change is introduced. Future reconnect/replay state requires a
|
||||
separate versioned movement snapshot contract.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- Logs: verifier emits `LOCAL_PLAYER_MOVEMENT PASS` or named invariant failures.
|
||||
- Metrics: none yet; update is constant-time vector math.
|
||||
- Debug views: current character facing and animation remain observable scene outputs.
|
||||
- Correlation IDs: not applicable to synchronous local sandbox state.
|
||||
|
||||
## Verification
|
||||
|
||||
- Unit/contract: `src/tools/verify_local_player_movement.gd` covers forward,
|
||||
backward, strafe, rotated basis, acceleration, deceleration, sprint, flight,
|
||||
pitched camera basis, state reset, signed keyboard turn and invalid delta.
|
||||
- Capability: `verify_player_movement_capabilities.gd` covers three profile-ID
|
||||
cases, safe defaults, sprint/flight allow/reject behavior and two real scene compositions.
|
||||
- Boundary: verifier rejects Node/Input/Camera/ADT dependencies and movement
|
||||
integration/state remaining in the player scene.
|
||||
- Scene integration: `verify_player_input.gd` drives the asset-free player
|
||||
regression scene through forward motion and immediate flight toggle.
|
||||
- Fidelity evidence: translation defaults remain regression-locked against the
|
||||
pre-extraction sandbox. The compatibility keyboard turn default is pinned to
|
||||
TrinityCore 3.3.5 `playerBaseMoveSpeed[MOVE_TURN_RATE]`; that server contract
|
||||
does not alone prove original-client timing parity.
|
||||
- Performance budget: constant-time vector math, no per-tick I/O or scene lookup;
|
||||
`MoveIntent` allocation remains owned by the input package.
|
||||
|
||||
## Extension points
|
||||
|
||||
- Terrain/collision policy can consume displacement without changing input or velocity ownership.
|
||||
- A future server-aware predictor may replace this controller behind the scene composition boundary.
|
||||
- A typed movement snapshot may expose deterministic replay state when M08/M09 require it.
|
||||
- A future application profile can map to this narrow capability value without
|
||||
coupling the movement controller to the application shell.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Scene-free local velocity state | Implemented | Pure controller and source boundary regression | Integrate target checklist after review |
|
||||
| Existing planar acceleration/speeds | Implemented | Exact numeric unit cases and real scene regression | Compare with build 12340 before fidelity claim |
|
||||
| Compatibility keyboard turn | Implemented | Pinned server-core value, signed pure cases and real-scene profile regression | Direct original-client timing comparison remains required |
|
||||
| Existing camera-relative free flight | Implemented in `RenderSandbox` | Pitched-basis, profile and toggle/reset cases | Original-client flight semantics remain out of scope |
|
||||
| Typed sprint/free-flight exclusion | Implemented | Pure and real-scene profile regressions | Application shell must select profile explicitly later |
|
||||
| Terrain height query | Implemented | Typed `TerrainQuery` and injected player regression | Ground-snap policy remains scene-owned |
|
||||
| Terrain collision movement policy | Planned | Height-only query does not model collision | Add slopes/holes/collision later |
|
||||
| Jump/fall/swim | Planned | M02/M09 roadmap | Requires terrain/liquid and server contracts |
|
||||
| Prediction/reconciliation | Planned | M08/M09 roadmap | Requires movement snapshot/network contract |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Translation/acceleration defaults preserve the existing sandbox and are not original-client evidence.
|
||||
- Configuration is captured at scene `_ready`; runtime mutation of exported speed
|
||||
properties does not reconfigure an existing controller.
|
||||
- Ground snap remains in the player scene, but its ADT data access is now behind
|
||||
a replaceable `TerrainQuery`.
|
||||
- Runtime renderer scenes intentionally default to `RenderSandbox`; a future
|
||||
game-client composition must explicitly select `Blizzlike335`.
|
||||
- `Blizzlike335` currently means only debug movement exclusion and must not be
|
||||
interpreted as evidence that remaining movement semantics are 1:1.
|
||||
- The module uses Godot math value types; a future engine-independent gameplay
|
||||
predictor may require scalar/domain movement contracts.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/gameplay/movement/local_player_movement_controller.gd` | Owned velocity/flight state and deterministic integration |
|
||||
| `src/gameplay/movement/player_movement_capabilities.gd` | Immutable profile-to-debug-capability contract and safe defaults |
|
||||
| `src/gameplay/terrain/terrain_query.gd` | Replaceable ground-height input boundary |
|
||||
| `src/scenes/player/third_person_camera_rig.gd` | Camera-relative sandbox flight basis producer |
|
||||
| `src/scenes/character/character_animation_presenter.gd` | Locomotion presentation consumer behind the scene adapter |
|
||||
| `src/scenes/player/third_person_wow_controller.gd` | Composition, basis selection, displacement/terrain and presentation-state adapters |
|
||||
| `src/tools/verify_local_player_movement.gd` | Pure numeric, state-transition and dependency regression |
|
||||
| `src/tools/verify_player_movement_capabilities.gd` | Profile factory, allow/reject and real-scene regression |
|
||||
| `src/tests/scenes/player_input_regression.tscn` | Asset-free integrated player fixture |
|
||||
| `src/tools/verify_player_input.gd` | Input-to-movement scene regression |
|
||||
| `src/tests/fixtures/player_input_profile_335.json` | Turn-rate and selected binding provenance |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- ADR: none; this follows the existing M02 decomposition boundary.
|
||||
- Upstream/reference: `targets/02-player-decomposition.md`,
|
||||
`targets/roadmap/04-gameplay-systems.md`, `docs/ARCHITECTURE.md`.
|
||||
@@ -0,0 +1,243 @@
|
||||
# Player Input
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Partial |
|
||||
| Target/work package | M02 / M02-GMP-INPUT-001, M02-GMP-SANDBOX-CAPABILITIES-001, M02-GMP-INPUT-PROFILE-335-001 |
|
||||
| Owners | Gameplay input boundary; `sindo-main-codex` for current package |
|
||||
| Last verified | M02-GMP-INPUT-PROFILE-335-001 worktree, 2026-07-15 |
|
||||
| Profiles/capabilities | Distinct RenderSandbox and Blizzlike335 action layouts; debug execution remains RenderSandbox-only |
|
||||
|
||||
## Purpose
|
||||
|
||||
Convert remappable Godot Input Map actions into an immutable, scene-free
|
||||
`MoveIntent` that can be consumed by the independently testable local movement
|
||||
controller. Select either the preserved renderer-sandbox layout or the verified
|
||||
build-12340 movement bindings at the scene composition boundary.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Define authoritative server movement, prediction or reconciliation.
|
||||
- Own character transform, terrain queries, camera state or animation state.
|
||||
- Implement jump, autorun, left-button select/move or persisted binding settings.
|
||||
- Remove process-wide debug actions; Blizzlike335 sampling ignores them and the
|
||||
movement capability boundary also rejects directly constructed debug requests.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Devices[Keyboard and mouse] --> InputMap[Godot Input Map]
|
||||
InputMap --> Source[PlayerInputSource]
|
||||
Profile[RenderSandbox or Blizzlike335] --> Source
|
||||
Source --> Intent[MoveIntent]
|
||||
Intent --> Movement[LocalPlayerMovementController]
|
||||
Profile[PlayerMovementCapabilities] --> Movement
|
||||
Movement --> Controller[ThirdPersonWowController adapter]
|
||||
Controller --> Camera[ThirdPersonCameraRig]
|
||||
```
|
||||
|
||||
Allowed dependencies:
|
||||
|
||||
- `PlayerInputSource` may read Godot's process-wide `Input` adapter.
|
||||
- The scene controller may consume `MoveIntent` and camera-specific action names.
|
||||
- `MoveIntent` may contain scalar axes and request flags only.
|
||||
|
||||
Forbidden dependencies:
|
||||
|
||||
- `MoveIntent` must not read `Input`, inherit `Node`/`Resource`, or contain world coordinates.
|
||||
- The input source must not mutate player transforms, camera nodes or animation state.
|
||||
- Renderer, terrain parsers and network codecs must not read Input Map actions.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `MoveIntent` | Immutable value | Carries normalized forward, strafe, vertical and turn axes plus the sandbox sprint request | Created and consumed on the main physics tick; caller retains reference | Constructor clamps axes; no I/O failure |
|
||||
| `MoveIntent.has_translation()` | Query | Reports whether any translation axis is non-zero | Any thread while value remains immutable | None |
|
||||
| `PlayerInputActions` | Constants | Provides stable action names shared by configuration and adapters | Process lifetime | Missing project actions are detected by verification |
|
||||
| `PlayerInputSource.new(profile_id)` | Constructor | Selects sandbox or compatibility action layout once | Scene lifetime | Unknown IDs fail closed to `Blizzlike335` |
|
||||
| `PlayerInputSource.sample_move_intent()` | Adapter method | Samples the selected action layout for one physics tick | Main thread; returned intent is independent of adapter lifetime | Missing actions resolve to zero strength and are a configuration defect |
|
||||
| `PlayerInputSource.compose_move_intent(...)` | Pure factory | Clamps strengths, cancels opposites and normalizes diagonal planar input | Any thread with scalar inputs | None |
|
||||
| `PlayerInputSource.compose_blizzlike_335_move_intent(...)` | Pure factory | Composes Q/E strafe, A/D turn and right-mouse A/D strafe semantics | Any thread with scalar inputs | None |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Movement action strengths | Godot `InputMap`/`Input` | `PlayerInputSource` | Engine-owned snapshot | Sampled on main physics tick |
|
||||
| Input | Mouse action events | Godot `_unhandled_input` dispatch | `ThirdPersonCameraRig` through player dispatch | Engine-owned event | Current input dispatch only |
|
||||
| Output | `MoveIntent` | `PlayerInputSource` | `LocalPlayerMovementController` through scene composition | Immutable caller-held value | One physics tick |
|
||||
| Output | Debug sprint request/vertical axes | `PlayerInputSource` | Capability-gated movement controller | Immutable intent fields | One physics tick |
|
||||
| Output | Signed turn axis | `PlayerInputSource` | `LocalPlayerMovementController` | Immutable scalar | One physics tick |
|
||||
| Output | Camera action names | `PlayerInputActions` | `ThirdPersonCameraRig` | Static constants | Process lifetime |
|
||||
|
||||
Side effects:
|
||||
|
||||
- `PlayerInputSource` has no side effects beyond reading current engine input.
|
||||
- `ThirdPersonCameraRig` owns mouse mode, orbit state and camera transforms;
|
||||
player remains only the event-dispatch composition boundary.
|
||||
- No filesystem, cache, network, database or renderer-resource mutation occurs.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Project[project.godot action defaults] --> Map[InputMap]
|
||||
Remap[User or editor remapping] --> Map
|
||||
Map -->|movement strengths| Source[PlayerInputSource]
|
||||
Profile[Selected input profile] --> Source
|
||||
Map -->|camera events| Camera[ThirdPersonCameraRig]
|
||||
Source -->|clamp cancel normalize| Intent[MoveIntent]
|
||||
Intent --> Consumer[LocalPlayerMovementController]
|
||||
Profile[PlayerMovementCapabilities] --> Consumer
|
||||
Consumer --> Adapter[ThirdPersonWowController]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
The adapter is stateless. The scene composition root creates one
|
||||
`PlayerInputSource` and `LocalPlayerMovementController` in `_ready`; each physics
|
||||
tick produces a new immutable intent. Flight enabled/disabled state is owned by
|
||||
the movement controller and is not hidden in the input adapter. RenderSandbox
|
||||
retains debug requests in its intent; Blizzlike335 does not sample them, and the
|
||||
immutable movement capability also rejects directly constructed requests.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Engine as Godot Input
|
||||
participant Source as PlayerInputSource
|
||||
participant Intent as MoveIntent
|
||||
participant Move as LocalPlayerMovementController
|
||||
participant Player as ThirdPersonWowController adapter
|
||||
Engine->>Source: movement action strengths
|
||||
Source->>Intent: compose normalized axes and requests
|
||||
Source-->>Player: sample_move_intent()
|
||||
Player->>Move: calculate yaw delta, then advance(intent, selected basis, delta)
|
||||
Move-->>Player: displacement and velocity state
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- The controller owns the input-source reference for its scene lifetime.
|
||||
- Each `MoveIntent` is immutable and retained only by its consumer.
|
||||
- Sampling and controller mutation occur on the main physics thread.
|
||||
- The module creates no Nodes, Resources, RIDs, workers or background jobs.
|
||||
- Input Map definitions are project configuration; runtime remapping remains
|
||||
engine-owned and does not mutate the domain value.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Required action missing | Headless contract verifier | Godot returns zero input for that action | `PLAYER_INPUT` failure naming action | Restore action or user mapping |
|
||||
| Conflicting opposite actions | Pure composition | Strengths cancel deterministically | Covered by contract test | Release/remap one action |
|
||||
| Strength outside range | Pure composition | Value clamps to `[0, 1]` before axis calculation | Covered by contract test | Input adapter may be corrected independently |
|
||||
| Controller disabled/unloaded | Scene lifecycle | Sampling stops with physics processing | Existing scene lifecycle | Re-enable or recreate scene |
|
||||
| Debug request in `Blizzlike335` | Input profile and movement capability checks | Input adapter omits it; directly constructed request has no effect | Profile/capability regressions | Use `RenderSandbox` only when debug movement is intended |
|
||||
|
||||
There is no asynchronous operation to cancel and no persisted state to roll back.
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| Forward/backward | Physical W/S and Up/Down | Both | Yes, through Input Map | Produces signed forward axis |
|
||||
| Strafe left/right | Physical A/D | `RenderSandbox` | Yes | Preserves renderer baseline |
|
||||
| Strafe left/right | Physical Q/E | `Blizzlike335` | Yes | Matches selected build-12340 defaults |
|
||||
| Keyboard turn | Physical A/D and Left/Right | `Blizzlike335` | Yes | Produces signed turn axis |
|
||||
| Camera-held A/D | Physical A/D while right mouse is held | `Blizzlike335` | Yes | Produces strafe and suppresses keyboard turn |
|
||||
| Camera rotate | Right mouse button | Current render sandbox | Yes | Captures/releases mouse for orbit |
|
||||
| Camera zoom | Mouse wheel | Current render sandbox | Yes | Adjusts current camera distance |
|
||||
| Debug sprint | Shift | Executable only in `RenderSandbox` | Yes | Requests multiplier; `Blizzlike335` ignores it |
|
||||
| Debug flight up/down/toggle | E/Q/Space | Executable only in `RenderSandbox` | Yes | Requests free flight; `Blizzlike335` remains grounded |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
The action names are additive project configuration. No saved binding schema,
|
||||
cache or migration exists yet. Renaming an action is a contract change and must
|
||||
provide a settings migration once user settings are persisted.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- Logs: dedicated verifier emits `PLAYER_INPUT PASS` or one failure per invariant.
|
||||
- Metrics: none; sampling cost is one small value allocation per physics tick.
|
||||
- Debug views: none.
|
||||
- Correlation IDs: not applicable to local synchronous input.
|
||||
|
||||
## Verification
|
||||
|
||||
- Unit/contract tests: `src/tools/verify_player_input.gd` validates all actions,
|
||||
both profile defaults, axis cancellation/clamping/normalization, turn routing,
|
||||
safe profile fallback, fixture provenance and debug request flags.
|
||||
- Integration/E2E: `src/tests/scenes/player_input_regression.tscn` is loaded by
|
||||
the verifier, which drives sandbox forward/camera/flight plus compatibility
|
||||
keyboard turn and right-mouse strafe through real scene controllers.
|
||||
- Profile integration: `verify_player_movement_capabilities.gd` drives sprint
|
||||
and flight actions through real `RenderSandbox` and `Blizzlike335` scene instances.
|
||||
- Fidelity evidence: sanitized fixture `player_input_profile_335.json` pins the
|
||||
private build-12340 `DefaultBindings.wtf`/`Bindings.xml` hashes and selected
|
||||
movement/camera commands. TrinityCore 3.3.5 revision
|
||||
`2853a621d6af91a803787a2b8a509f4ce3e0300d` pins the server-contract player
|
||||
turn rate. WoWee revision `626243e937fb93965fa583a6507ed5a1aa7dda4b`
|
||||
supports right-mouse A/D strafe routing. Both are compatibility references,
|
||||
not direct original-client timing/conditional-behavior proof.
|
||||
- Performance budgets: no I/O, jobs or scene lookup per sample; one immutable
|
||||
intent allocation per physics tick.
|
||||
- Manual diagnostics: run the Eastern Kingdoms scene and remap one movement
|
||||
action in Project Settings before checking movement.
|
||||
|
||||
## Extension points
|
||||
|
||||
- A gamepad/accessibility adapter may call `compose_move_intent` without changing
|
||||
movement consumers.
|
||||
- A later input-context owner may suppress gameplay actions while preserving the
|
||||
same intent contract.
|
||||
- A future server-aware movement predictor may replace the current local
|
||||
movement consumer without changing `PlayerInputSource`.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Remappable current sandbox controls | Implemented | `verify_player_input.gd` action/default checks | Add persisted user binding settings later |
|
||||
| `PlayerInputSource → MoveIntent` seam | Implemented | Pure composition and controller boundary checks | Local movement consumer is now extracted |
|
||||
| Selected 3.3.5a movement defaults | Implemented | Pinned private-source hashes, sanitized selected bindings and profile regressions | Jump, autorun and left-button behavior remain planned |
|
||||
| Compatibility-profile exclusion of sprint/flight | Implemented | Typed capability and two real-scene regressions | Application shell selection remains future work |
|
||||
| Jump/fall/swim/fly gameplay semantics | Planned | M02 target and gameplay roadmap | Implement after terrain/movement state extraction |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Only selected movement/camera binding facts are implemented; the fixture explicitly records unimplemented defaults.
|
||||
- Input Map still exposes debug bindings process-wide; the movement consumer now
|
||||
rejects their effects outside `RenderSandbox`.
|
||||
- Camera state is extracted; active collision remains an explicit planned policy.
|
||||
- There is no persisted user keybinding format or UI yet.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `project.godot` | Additive default Input Map bindings |
|
||||
| `src/domain/input/move_intent.gd` | Immutable locomotion request contract |
|
||||
| `src/gameplay/input/player_input_actions.gd` | Stable project action names |
|
||||
| `src/gameplay/input/player_input_source.gd` | Engine Input Map adapter and pure composition |
|
||||
| `src/gameplay/movement/local_player_movement_controller.gd` | Scene-free movement consumer |
|
||||
| `src/gameplay/movement/player_movement_capabilities.gd` | Defense-in-depth execution gate for debug requests |
|
||||
| `src/scenes/player/third_person_camera_rig.gd` | Camera-action consumer and orbit/zoom state |
|
||||
| `src/scenes/player/third_person_wow_controller.gd` | Composition and input-event dispatcher |
|
||||
| `src/tests/scenes/player_input_regression.tscn` | Asset-free controller movement/camera regression fixture |
|
||||
| `src/tests/fixtures/player_input_profile_335.json` | Sanitized build-12340 binding and server turn-rate provenance |
|
||||
| `src/tools/verify_player_input.gd` | Headless contract/integration regression |
|
||||
| `src/tools/verify_player_movement_capabilities.gd` | Sandbox versus compatibility-profile integration regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- ADR: none; this implements the existing architecture and M02 seam.
|
||||
- Upstream/reference: `targets/02-player-decomposition.md`,
|
||||
`targets/roadmap/04-gameplay-systems.md`, `docs/ARCHITECTURE.md`, and the
|
||||
pinned sources recorded in `player_input_profile_335.json`.
|
||||
@@ -0,0 +1,216 @@
|
||||
# Render Budget Scheduler
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target/work package | `M03-RND-SCHEDULER-001` |
|
||||
| Owners | Renderer workstream / M03 integrator |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-render-budget-scheduler`, 2026-07-16 |
|
||||
| Profiles/capabilities | Runtime and Editor main-thread operation quotas; profile values supplied by streamer composition |
|
||||
|
||||
## Purpose
|
||||
|
||||
Issue a bounded number of permits for each renderer operation lane during one
|
||||
process frame. The scheduler centralizes quota accounting and teardown
|
||||
cancellation without taking ownership of queues or render work.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Own, sort, drain or cap the stored length of a renderer queue.
|
||||
- Submit, cancel or await worker and `ResourceLoader` tasks.
|
||||
- Execute callbacks or mutate the SceneTree, resources, caches or RIDs.
|
||||
- Adapt limits from elapsed time or create a general job/dependency framework.
|
||||
- Select renderer quality or compatibility profiles.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Config[Streamer exported per-frame limits] --> Frame[operation limit snapshot]
|
||||
Frame --> Scheduler[RenderBudgetScheduler]
|
||||
Queues[StreamingWorldLoader owned queues] --> Drains[Ordered queue drains]
|
||||
Scheduler -->|boolean permits| Drains
|
||||
Drains --> Render[Main-thread finalize/attach/evict]
|
||||
Shutdown[StreamingWorldLoader exit] -->|cancel| Scheduler
|
||||
Scheduler -. does not own .-> Queues
|
||||
```
|
||||
|
||||
Allowed dependencies are Godot value containers and `RefCounted`. The module
|
||||
must not depend on `Node`, `WorkerThreadPool`, `ResourceLoader`, `RenderingServer`,
|
||||
mutexes, renderer assets or streamer queue fields.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Failure behavior |
|
||||
|---|---|---|---|---|
|
||||
| Lane constants | `StringName` constants | Stable operation categories used in the frame limit map | Process lifetime | Unknown lanes have zero permits |
|
||||
| `begin_frame` | Method | Replaces all limits and clears consumption diagnostics for one frame | Main thread; once before drains | Negative limits clamp to zero; cannot revive a cancelled instance |
|
||||
| `has_remaining_permit` | Query | Tests a lane without consuming | Main thread; current frame | Unknown/exhausted/cancelled returns `false` |
|
||||
| `try_consume_permit` | Mutation | Atomically checks and consumes one caller operation permit | Main thread; current frame | Unknown/exhausted/cancelled returns `false` |
|
||||
| `remaining_permits` | Query | Returns a lane's current unconsumed count | Main thread; current frame | Unknown/cancelled returns zero |
|
||||
| `consumed_permits_snapshot` | Query | Returns detached per-lane diagnostics | Caller owns returned dictionary | Mutation cannot affect scheduler state |
|
||||
| `cancel` | Lifecycle method | Permanently stops new permits and clears remaining limits | Main thread; renderer teardown | Idempotent; does not interrupt in-flight caller work |
|
||||
| `is_cancelled` | Query | Reports terminal lifecycle state | Scheduler lifetime | No failure |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | `Dictionary[StringName, int]` operation limits | `StreamingWorldLoader` configuration snapshot | Scheduler | Copied by scheduler | One process frame |
|
||||
| Input | Lane ID | Ordered streamer drain | Scheduler | Constant value | One permit check |
|
||||
| Input | Cancellation | Streamer `_exit_tree` | Scheduler | Terminal flag | Renderer instance lifetime |
|
||||
| Output | Permit boolean | Scheduler | Streamer drain loop | Scalar | One attempted operation |
|
||||
| Output | Remaining count | Scheduler | Streamer/tests | Scalar | Current frame |
|
||||
| Output | Consumed snapshot | Scheduler | Diagnostics/tests | Detached caller-owned copy | Current frame snapshot |
|
||||
|
||||
The scheduler has no I/O, logging, SceneTree, task, cache or GPU side effects.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Exports[Exported operation limits] --> Limits[_render_operation_limits_for_frame]
|
||||
Limits --> Begin[begin_frame]
|
||||
Begin --> Remaining[remaining permits by lane]
|
||||
Queue[Non-empty caller-owned queue] --> Check[try_consume_permit]
|
||||
Remaining --> Check
|
||||
Check -->|true| Execute[Caller performs one operation]
|
||||
Check -->|false| Defer[Operation remains queued]
|
||||
Execute --> Consumed[consumed permits diagnostics]
|
||||
```
|
||||
|
||||
## Lifecycle and state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Ready
|
||||
Ready --> FrameActive: begin_frame
|
||||
FrameActive --> FrameActive: consume/check
|
||||
FrameActive --> FrameActive: begin_frame resets limits
|
||||
Ready --> Cancelled: cancel
|
||||
FrameActive --> Cancelled: cancel
|
||||
Cancelled --> Cancelled: begin_frame/check/cancel
|
||||
Cancelled --> [*]
|
||||
```
|
||||
|
||||
Cancellation is intentionally terminal. A new renderer scene creates a new
|
||||
scheduler instance; an old instance cannot accidentally resume after teardown.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Stream as StreamingWorldLoader
|
||||
participant Budget as RenderBudgetScheduler
|
||||
participant Queue as Loader-owned queues
|
||||
participant Render as SceneTree/RenderingServer
|
||||
Stream->>Budget: begin_frame(operation limits)
|
||||
loop existing fixed drain order
|
||||
Stream->>Queue: inspect next operation
|
||||
Stream->>Budget: try_consume_permit(lane)
|
||||
Budget-->>Stream: true or false
|
||||
Stream->>Render: execute one operation when true
|
||||
end
|
||||
Stream->>Budget: cancel() during _exit_tree
|
||||
Budget-->>Stream: later permits rejected
|
||||
Stream->>Stream: await tasks and release owned resources
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- `StreamingWorldLoader` owns every queue, task registry, cache, Node, Resource and RID.
|
||||
- The scheduler owns two small per-frame counter dictionaries and one terminal flag.
|
||||
- The streamer preserves operation order. In particular, chunk removals and
|
||||
creations share `CHUNK_GEOMETRY`, with removals drained first.
|
||||
- All current scheduler calls occur on the Godot main thread. The service has no
|
||||
mutex and is not safe for concurrent mutation.
|
||||
- A scheduler instance lives exactly as long as its loader instance.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Condition | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Negative/zero limit | `begin_frame` clamp | Lane issues no permits | Contract verifier | Correct configuration or accept disabled lane |
|
||||
| Unknown lane | Permit lookup miss | Returns `false`/zero | Contract verifier | Add the lane to the frame snapshot |
|
||||
| Exhausted lane | Remaining count is zero | Work remains in caller queue for a later frame | Queue-depth/hitch metrics | Next `begin_frame` replenishes permits |
|
||||
| Renderer teardown | Streamer calls `cancel` before task waits | New permits stop immediately | Shutdown ownership regression | New scene creates a new scheduler |
|
||||
| In-flight operation at cancel | Caller already consumed permit | Scheduler does not interrupt it | Ownership docs | Caller completes/cleans it through existing shutdown path |
|
||||
|
||||
This cancellation scope is permit issuance, not worker cancellation. Existing
|
||||
task/result staleness and shutdown waits remain streamer responsibilities.
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The streamer's existing exported values populate 16 lanes: tile finalization,
|
||||
terrain upgrade/control-splat/splat-cache/splat-build, water finalization, shared
|
||||
chunk geometry, tile-load starts, tile LOD create/remove, M2 animation/mesh
|
||||
finalization and build, WMO instance/group build, and detail synchronization.
|
||||
Defaults and quality-preset assignments are unchanged. `tile_finalize` retains
|
||||
its historical minimum of one; other negative values behave as zero.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
The module is runtime-only and serializes nothing. It changes no cache, asset,
|
||||
scene, protocol or database format and requires no version bump or migration.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- `consumed_permits_snapshot` exposes detached per-frame counts for tests and
|
||||
future facade metrics without exposing mutable scheduler state.
|
||||
- Existing queue depths, named hitch sections and `HITCH`/`PERF` logs remain in
|
||||
`StreamingWorldLoader`.
|
||||
- No scheduler logging occurs on the hot permit path.
|
||||
|
||||
## Verification
|
||||
|
||||
- `src/tools/verify_render_budget_scheduler.gd` covers exact exhaustion,
|
||||
independent lanes, shared removal/create priority, frame reset, invalid/unknown
|
||||
lanes, detached diagnostics, terminal cancellation and source boundaries.
|
||||
- The verifier performs 20,000 permit checks with a generous 150 ms regression
|
||||
ceiling on the headless runner; this is a guard, not a renderer p95 claim.
|
||||
- Integration regressions must retain the seven renderer checkpoint plans and
|
||||
existing focus/planner/facade contracts.
|
||||
- Fidelity evidence is behavior-preserving: exported values, process order,
|
||||
cache versions and visual operations are unchanged. No build-12340 parity
|
||||
claim follows from this extraction.
|
||||
|
||||
## Extension points
|
||||
|
||||
- Add facade consumption metrics using detached snapshots.
|
||||
- Move a service's queue drain behind its own boundary while retaining these lane IDs.
|
||||
- Add measured time-aware policy only after asset-backed p95/p99 evidence proves
|
||||
fixed operation counts insufficient; do not embed callbacks in this scheduler.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Per-lane frame bounds | Implemented | Exact exhaustion and independent-lane contracts | Asset-backed p95/p99 baseline remains |
|
||||
| Shared removal/create priority | Implemented | Synthetic shared `CHUNK_GEOMETRY` contract | Other service priorities remain streamer-owned |
|
||||
| Teardown permit cancellation | Implemented | Terminal cancellation contract and loader source gate | Worker stale-result cancellation remains streamer-owned |
|
||||
| Detached consumption diagnostics | Implemented | Snapshot isolation contract | Not yet exposed through facade metrics |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Queue storage length is not capped; only per-frame draining is bounded.
|
||||
- Worker concurrency and stale-result cancellation remain existing streamer logic.
|
||||
- Exact p95/p99 impact requires local extracted assets and paired baseline runs.
|
||||
- Lane IDs are dynamic dictionary keys; the verifier guards the current contract.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/streaming/render_budget_scheduler.gd` | Scene-free lane counters, permits, diagnostics and cancellation |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Limit composition, fixed drain order, queue/resource ownership and operation execution |
|
||||
| `src/tools/verify_render_budget_scheduler.gd` | Asset-free behavior, dependency and bounded timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`world-renderer.md`](world-renderer.md)
|
||||
- [`streaming-target-planner.md`](streaming-target-planner.md)
|
||||
- [`../../targets/03-renderer-facade.md`](../../targets/03-renderer-facade.md)
|
||||
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||
- [`../CODING_STANDARD.md`](../CODING_STANDARD.md)
|
||||
@@ -0,0 +1,239 @@
|
||||
# Streaming Target Planner
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target/work package | `M03-RND-STREAMING-PLANNER-001` |
|
||||
| Owners | Renderer workstream / M03 integrator |
|
||||
| Last verified | `e887c3b`, 2026-07-15 |
|
||||
| Profiles/capabilities | Runtime and Editor preview ADT target selection; profile values supplied by caller |
|
||||
|
||||
## Purpose
|
||||
|
||||
Calculate which available ADT tiles are wanted and retained for one typed focus
|
||||
without accessing the SceneTree, worker tasks, renderer queues, caches or GPU
|
||||
resources. Runtime planning preserves prewarm, hysteresis and boundary-prefetch
|
||||
behavior; editor planning preserves the square preview radius.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Mutate load/finalize/eviction queues.
|
||||
- Prioritize queued tile paths by world distance.
|
||||
- Calculate chunk or tile LOD levels after a tile is loaded.
|
||||
- Own the available WDT tile catalog or cache paths.
|
||||
- Create, cancel or await jobs.
|
||||
- Implement M2, WMO, liquid or terrain attachment.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Focus[GodotWorldPosition] --> Planner[StreamingTargetPlanner]
|
||||
Policy[StreamingTargetPolicy] --> Planner
|
||||
Catalog[Available tile keys from WDT/directory] --> Planner
|
||||
Planner --> Plan[StreamingTargetPlan]
|
||||
Plan --> Streamer[StreamingWorldLoader queue/state apply]
|
||||
Streamer --> Queue[Tile load queue]
|
||||
```
|
||||
|
||||
Allowed dependencies:
|
||||
|
||||
- typed coordinate values and `CoordinateMapper`;
|
||||
- immutable planner policy/result contracts;
|
||||
- read-only available-tile key membership.
|
||||
|
||||
Forbidden dependencies:
|
||||
|
||||
- `Node`, SceneTree and viewport/camera discovery;
|
||||
- `WorkerThreadPool`, `ResourceLoader` and filesystem access;
|
||||
- `RenderingServer`, meshes, materials, nodes and RIDs;
|
||||
- gameplay, network, UI, editor UI or server adapters;
|
||||
- streamer queues, tile states and cache dictionaries.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `StreamingTargetPlanner.plan_runtime` | Pure method | Builds wanted/retained sets with edge/corner prefetch | Any thread if caller does not mutate catalog concurrently; call-scoped | Empty catalog produces empty sets |
|
||||
| `StreamingTargetPlanner.plan_editor` | Pure method | Builds identical editor wanted/retained square sets | Same as runtime planning | Negative radius clamps to zero |
|
||||
| `StreamingTargetPolicy` | Immutable value | Captures radius margins and boundary threshold for one plan | Caller-owned, shareable | Values retain scene input; effective margins/threshold are clamped |
|
||||
| `visible_tile_radius` | Pure query | Resolves chunk-radius versus tile-radius visibility | Any thread/value lifetime | No failure |
|
||||
| `load_tile_radius` | Pure query | Adds non-negative prewarm margin | Any thread/value lifetime | No failure |
|
||||
| `retained_tile_radius` | Pure query | Adds non-negative hysteresis margin | Any thread/value lifetime | No failure |
|
||||
| `StreamingTargetPlan` | Immutable value | Holds focus tile and read-only wanted/retained key sets | Caller-owned plan lifetime | No failure |
|
||||
| `wanted_tile_keys` | Read-only query | Returns immutable wanted set | Plan lifetime | Mutation is rejected by Godot read-only Dictionary |
|
||||
| `retained_tile_keys` | Read-only query | Returns immutable retained set | Plan lifetime | Mutation is rejected by Godot read-only Dictionary |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | `GodotWorldPosition` | `WorldRenderFacade`/streamer focus adapter | Planner | Immutable caller value | One plan |
|
||||
| Input | Available `<tile_x>_<tile_y>` keys | `StreamingWorldLoader` WDT/directory catalog | Planner membership checks | Loader-owned, read-only during call | Map session |
|
||||
| Input | `StreamingTargetPolicy` | Streamer configuration snapshot | Planner | Immutable caller value | One or more plans |
|
||||
| Input | Editor preview radius | Editor scene configuration | Planner | Scalar copy | One plan |
|
||||
| Output | `StreamingTargetPlan.focus_tile` | Planner/`CoordinateMapper` | Streamer diagnostics | Immutable typed coordinate | Plan lifetime |
|
||||
| Output | Wanted tile-key set | Planner | Streamer queue/state apply | Plan-owned read-only Dictionary | Plan lifetime |
|
||||
| Output | Retained tile-key set | Planner | Streamer hysteresis/state apply | Plan-owned read-only Dictionary | Plan lifetime |
|
||||
|
||||
Side effects:
|
||||
|
||||
- none;
|
||||
- no input mutation;
|
||||
- no logging, I/O, task submission, cache access, SceneTree or GPU work.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Position[GodotWorldPosition] --> Tile[CoordinateMapper.godot_to_adt_tile]
|
||||
Position --> Local[CoordinateMapper.godot_to_adt_tile_local]
|
||||
Policy[StreamingTargetPolicy] --> Radii[load and retained radii]
|
||||
Policy --> Threshold[clamped boundary threshold]
|
||||
Tile --> Centers[base prefetch center]
|
||||
Local --> Centers
|
||||
Threshold --> Centers
|
||||
Centers --> Wanted[available keys within load radius]
|
||||
Centers --> Retained[available keys within retained radius]
|
||||
Catalog[Available tile keys] --> Wanted
|
||||
Catalog --> Retained
|
||||
Wanted --> Plan[StreamingTargetPlan]
|
||||
Retained --> Plan
|
||||
Tile --> Plan
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
The planner is stateless. A call creates local center/set dictionaries, freezes
|
||||
the result dictionaries in `StreamingTargetPlan`, returns the plan and retains
|
||||
no reference to inputs or output. There is no retry, cancellation or shutdown
|
||||
state machine.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Stream as StreamingWorldLoader
|
||||
participant Planner as StreamingTargetPlanner
|
||||
participant Mapper as CoordinateMapper
|
||||
participant Plan as StreamingTargetPlan
|
||||
Stream->>Planner: plan_runtime(catalog, typed focus, policy)
|
||||
Planner->>Mapper: focus tile and tile-local position
|
||||
Mapper-->>Planner: typed coordinates
|
||||
Planner->>Planner: prefetch centers and available key sets
|
||||
Planner->>Plan: freeze focus/wanted/retained result
|
||||
Plan-->>Stream: immutable target plan
|
||||
Stream->>Stream: apply queues, LOD and detail state
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- `StreamingWorldLoader` owns the available catalog and must not mutate it during a planning call.
|
||||
- `StreamingTargetPolicy` and typed focus remain caller-owned immutable values.
|
||||
- The planner owns temporary center/set values only until plan construction.
|
||||
- `StreamingTargetPlan` owns its read-only result dictionaries.
|
||||
- The planner creates no Node, Resource, RID, thread, mutex or task.
|
||||
- Current runtime calls occur on the main thread; scene-free contracts permit a
|
||||
future worker call only after catalog publication/ownership is explicit.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Empty/unavailable catalog | No membership matches | Valid plan with empty sets | Streamer retains existing missing-data diagnostics | Load a valid WDT/directory catalog and replan |
|
||||
| Focus outside catalog/grid | Typed coordinate does not match keys | Valid focus tile with filtered empty/partial sets | Caller diagnostic if required | Supply valid focus/map or catalog |
|
||||
| Negative prewarm/retain margin | Policy effective-radius query | Margin clamps to zero | Contract test | Correct configuration if negative value was unintended |
|
||||
| Boundary threshold outside range | Policy query | Clamp to `0.0..0.49` | Contract test | Correct configuration or accept safe clamp |
|
||||
| Plan no longer current | New focus/config/catalog revision | Caller discards ephemeral plan | Streamer refresh diagnostics | Replan from current inputs |
|
||||
|
||||
Cancellation is not applicable: planning is synchronous, bounded and owns no
|
||||
external work. A returned stale plan is simply not retained by the planner.
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default source | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| `lod2_radius_chunks` | Streamer scene/profile | Renderer | Yes at composition/debug boundary | Converts chunk coverage to minimum ADT radius |
|
||||
| `lod2_tile_radius` | Streamer scene/profile | Renderer | Yes | Minimum visible ADT radius |
|
||||
| `warm_tile_margin` | Streamer scene/profile | Renderer | Yes | Extends chunk-derived visible radius |
|
||||
| `prewarm_tile_margin` | Streamer scene/profile | Renderer | Yes | Extends wanted/load radius |
|
||||
| `retain_tile_margin` | Streamer scene/profile | Renderer | Yes | Extends retained hysteresis radius |
|
||||
| `boundary_prefetch_threshold` | Streamer scene/profile | Renderer | Yes | Adds adjacent/corner prefetch centers near tile boundaries |
|
||||
| Editor preview radius | Editor scene | Authoring preview | Yes | Square wanted/retained preview set |
|
||||
|
||||
The planner does not select `Blizzlike335` or `Enhanced`; it applies the values
|
||||
already selected by renderer composition.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
- Planner contracts are runtime-only and are not serialized.
|
||||
- Existing tile keys and cache paths are unchanged.
|
||||
- No cache/resource/schema version bump or migration is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- Logs remain emitted by `StreamingWorldLoader` after applying a plan.
|
||||
- `StreamingTargetPlan` exposes focus/wanted/retained data for deterministic tests.
|
||||
- Dedicated verifier reports case count, iteration count, total time and average plan time.
|
||||
- No correlation ID is required for synchronous ephemeral plans.
|
||||
|
||||
## Verification
|
||||
|
||||
- Unit/contract: `src/tools/verify_streaming_target_planner.gd` covers center
|
||||
radii, edge/corner cross product, catalog filtering, clamps, editor behavior,
|
||||
input/result ownership and scene-free dependencies.
|
||||
- Integration: streamer source gate confirms both runtime/editor delegation and
|
||||
removal of `_predictive_focus_tiles` from the monolith.
|
||||
- Fidelity evidence: expected sets encode the pre-extraction formulas; renderer
|
||||
checkpoint dry-run must retain all seven checkpoint plans.
|
||||
- Performance budget: 250 synthetic High-like corner plans average at most
|
||||
`4.0 ms` each on the headless regression runner; measured result is recorded
|
||||
in test output.
|
||||
- Manual diagnostics: unchanged `StreamingWorld` focus/wanted/queued log.
|
||||
|
||||
## Extension points
|
||||
|
||||
- Direction/velocity/frustum priority can extend policy/input only with fixtures
|
||||
and a compatibility-safe default reproducing the current position-only plan.
|
||||
- Multi-focus planning may union additional typed centers without giving the
|
||||
planner ownership of queues or consumers.
|
||||
- Queue ordering remains a separate scheduler/application responsibility.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Runtime wanted/retained sets | Implemented | Center/corner/catalog fixtures | Add paired traversal performance evidence |
|
||||
| Boundary prefetch centers | Implemented | Edge/corner cross-product fixture | Direction/velocity prediction not implemented |
|
||||
| Editor preview set | Implemented | Negative/radius-one fixtures | Editor viewport integration remains existing adapter |
|
||||
| Scene-free deterministic execution | Implemented | Dependency/source gate and 250-iteration run | Worker execution is unnecessary today |
|
||||
| Queue priority/LOD | Not in module | Preserved in streamer | Separate extraction packages |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Tile-key sets preserve the streamer's internal string representation; a typed
|
||||
catalog migration is deferred until it can remove, rather than duplicate, that source of truth.
|
||||
- Planner time scales with the union of square radii/prefetch centers. The
|
||||
current bounded regression covers High-like corner input but not pathological custom radii.
|
||||
- Exact p95/p99 frame impact still requires renderer baseline runs with local assets.
|
||||
- Direction, velocity and camera frustum do not influence target selection yet.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/streaming/streaming_target_planner.gd` | Pure runtime/editor set calculation and boundary centers |
|
||||
| `src/render/streaming/streaming_target_policy.gd` | Immutable radius/threshold snapshot and effective clamps |
|
||||
| `src/render/streaming/streaming_target_plan.gd` | Immutable typed focus and read-only result sets |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Planner composition and plan application to queues/state |
|
||||
| `src/tools/verify_streaming_target_planner.gd` | Asset-free behavior/dependency/performance regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`world-renderer.md`](world-renderer.md)
|
||||
- [`../../targets/03-renderer-facade.md`](../../targets/03-renderer-facade.md)
|
||||
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||
- [`../GODOT_BEST_PRACTICES.md`](../GODOT_BEST_PRACTICES.md)
|
||||
- [`../CODING_STANDARD.md`](../CODING_STANDARD.md)
|
||||
@@ -0,0 +1,259 @@
|
||||
# Terrain Query
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target/work package | M02 / M02-GMP-TERRAIN-QUERY-001; M03 renderer consumer `M03-RND-FACADE-GROUND-QUERY-001` |
|
||||
| Owners | Gameplay terrain-query contract; ADT adapter owns parsed tile cache |
|
||||
| Last verified | `a45d521`, 2026-07-14 |
|
||||
| Profiles/capabilities | Current sandbox ADT ground height; loaded-render-mesh diagnostic query; authoritative physics backend planned |
|
||||
|
||||
## Purpose
|
||||
|
||||
Provide a replaceable, scene-free ground-height query for player movement.
|
||||
Isolate native ADT loading, per-tile caching, chunk selection and legacy outer
|
||||
height-grid interpolation from the player scene behind a typed
|
||||
`GodotWorldPosition → TerrainGroundSample` contract.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Own player transforms, movement velocity or ground-snap policy.
|
||||
- Provide collision normals, slopes, holes, liquids, WMO/M2 collision or ray queries.
|
||||
- Change ADT topology, coordinate conversion, renderer caches or native parser format.
|
||||
- Claim original-client terrain-contact parity from height regression alone.
|
||||
- Couple gameplay to the monolithic streaming renderer before a public backend exists.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Scene[ThirdPersonWowController adapter] --> Position[GodotWorldPosition]
|
||||
Position --> Contract[TerrainQuery]
|
||||
Contract --> Adapter[AdtTerrainQuery]
|
||||
Adapter --> Mapper[CoordinateMapper]
|
||||
Adapter --> Native[ADTLoader]
|
||||
Native --> Tile[ADT tile Dictionary]
|
||||
Tile --> Cache[Owned tile cache]
|
||||
Cache --> Sample[TerrainGroundSample]
|
||||
Sample --> Scene
|
||||
Scene --> Snap[Existing ground-snap policy]
|
||||
```
|
||||
|
||||
Allowed dependencies:
|
||||
|
||||
- Typed `GodotWorldPosition` and `CoordinateMapper` contracts.
|
||||
- Existing native `ADTLoader` at the ADT adapter boundary.
|
||||
- Dynamic dictionaries only inside the native-format adapter.
|
||||
- Player scene composition may inject any `TerrainQuery` implementation.
|
||||
- Renderer diagnostics reuse `GodotWorldPosition` through `WorldRenderFacade`
|
||||
but return renderer-owned `RenderedGroundSample`, preserving the render/gameplay boundary.
|
||||
|
||||
Forbidden dependencies:
|
||||
|
||||
- `TerrainQuery` contract depending on Nodes, cameras, input or renderer state.
|
||||
- Player scene loading/parsing ADT data or owning a parsed tile cache.
|
||||
- Manual WoW/Godot/ADT world-coordinate conversion outside `CoordinateMapper`.
|
||||
- Terrain query mutating transforms, gameplay movement state or visual meshes.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `TerrainGroundSample.available(height_units)` | Immutable value factory | Creates a finite Godot-world ground height | Any thread; caller-held value | Non-finite height becomes unavailable |
|
||||
| `TerrainGroundSample.unavailable(failure_code)` | Immutable value factory | Creates explicit unavailable result | Any thread; caller-held value | Empty code normalizes to `terrain_unavailable` |
|
||||
| `TerrainQuery.sample_ground_height(position)` | Replaceable boundary | Samples ground at typed Godot world position | Implementation-defined; current adapter main-thread owned | Base reports `terrain_query_not_implemented` |
|
||||
| `AdtTerrainQuery.new(extracted_directory, map_directory_name, loader_override)` | Adapter constructor | Configures one map and optional test/data-source loader | Query lifetime | Production validates native loader and file presence lazily |
|
||||
| `AdtTerrainQuery.sample_ground_height(position)` | Adapter method | Loads/caches tile and interpolates ADT outer heights | Main thread; cache lives with query | Returns stable failure code |
|
||||
| `ThirdPersonWowController.set_terrain_query(query)` | Composition method | Replaces terrain backend independently of movement/presentation | Call before scene ready or on main thread | Null is rejected and current query retained |
|
||||
| `WorldRenderFacade.sample_ground_height(position)` | Separate renderer diagnostic API | Samples already loaded terrain Mesh and returns `RenderedGroundSample` without exposing streamer state | Main thread; immutable sample | Stable `render_terrain_*` unavailable code; not injected into player by default |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | `GodotWorldPosition` | Player scene adapter | `TerrainQuery` | Immutable caller-held value | One query |
|
||||
| Input | Extracted directory/map name | Scene composition | `AdtTerrainQuery` | Copied strings | Query lifetime |
|
||||
| Input | ADT file/dictionary | Filesystem/native loader or injected loader | `AdtTerrainQuery` | Adapter-owned cached dictionary | Query lifetime |
|
||||
| Output | `TerrainGroundSample` | Terrain query | Player ground-snap adapter | Immutable caller-held value | One query |
|
||||
| Output | Failure code | Terrain query | Diagnostics/tests | Immutable `StringName` | Sample lifetime |
|
||||
|
||||
Side effects:
|
||||
|
||||
- Production adapter performs lazy file existence check and native ADT parse on first tile query.
|
||||
- Adapter mutates only its per-query tile/failure caches.
|
||||
- No scene tree, transform, renderer RID, network, database or persisted setting mutation occurs.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Position[GodotWorldPosition] --> TileCoord[CoordinateMapper tile/local/chunk]
|
||||
TileCoord --> Lookup{Tile cached?}
|
||||
Lookup -->|no| Load[ADTLoader or injected loader]
|
||||
Load --> Cache[Cache Dictionary or unavailable result]
|
||||
Lookup -->|yes| Cache
|
||||
Cache --> Chunk[Select MCNK by origin]
|
||||
Chunk --> Heights[Validate 145 heights]
|
||||
Heights --> Bilinear[Interpolate 9x9 outer grid]
|
||||
Bilinear --> Available[TerrainGroundSample available]
|
||||
Cache -->|missing/invalid| Unavailable[TerrainGroundSample unavailable]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> TileUnknown
|
||||
TileUnknown --> TileAvailable: first query / successful load
|
||||
TileUnknown --> TileUnavailable: first query / missing or failed
|
||||
TileAvailable --> TileAvailable: cached queries
|
||||
TileUnavailable --> TileUnavailable: cached queries
|
||||
TileAvailable --> [*]: query released
|
||||
TileUnavailable --> [*]: query released
|
||||
```
|
||||
|
||||
Unavailable tiles are cached intentionally to avoid repeated filesystem/native
|
||||
work each physics tick. Recovery after files appear requires replacing the query
|
||||
instance; hot-reload invalidation is outside this M02 package.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Player as ThirdPersonWowController
|
||||
participant Query as TerrainQuery
|
||||
participant ADT as AdtTerrainQuery
|
||||
participant Mapper as CoordinateMapper
|
||||
participant Native as ADTLoader
|
||||
Player->>Query: sample_ground_height(GodotWorldPosition)
|
||||
Query->>ADT: implementation dispatch
|
||||
ADT->>Mapper: tile/local/chunk coordinates
|
||||
alt tile not cached
|
||||
ADT->>Native: load_adt(absolute path)
|
||||
Native-->>ADT: ADT Dictionary
|
||||
end
|
||||
ADT->>ADT: select chunk and interpolate heights
|
||||
ADT-->>Player: TerrainGroundSample
|
||||
Player->>Player: apply existing offset/snap if available
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Player scene owns the active `TerrainQuery` reference.
|
||||
- `AdtTerrainQuery` exclusively owns tile/failure dictionaries until released.
|
||||
- Current production calls and cache mutations occur on the main physics thread.
|
||||
- Native loader result dictionaries do not cross the adapter boundary.
|
||||
- Samples and typed positions are immutable caller-held values.
|
||||
- The module creates no Nodes, Resources, RIDs or background jobs.
|
||||
- Future worker-backed queries require an explicit async/result contract rather
|
||||
than concurrent mutation of the current dictionaries.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Base contract used directly | Base method | Unavailable sample | `terrain_query_not_implemented` | Inject implementation |
|
||||
| Native loader absent | ClassDB check | Cache unavailable tile | `adt_loader_unavailable` | Load extension and replace query |
|
||||
| ADT file absent | File check | Cache unavailable tile | `adt_source_missing` | Restore corpus and replace query |
|
||||
| Native parse empty/fails | Result validation | Cache unavailable tile | `adt_load_failed` | Inspect source/parser; replace query |
|
||||
| Injected loader returns wrong type | Type check | Cache unavailable tile | `adt_loader_result_invalid` | Fix adapter |
|
||||
| Chunk absent | Chunk lookup | Unavailable sample | `adt_chunk_missing` | Validate ADT dictionary |
|
||||
| Height array shorter than 145 | Size check | Unavailable sample | `adt_chunk_heights_invalid` | Fix parser/source |
|
||||
| Non-finite interpolated height | Sample invariant | Unavailable sample | `terrain_height_not_finite` | Inspect source values |
|
||||
| Null query injection | Scene guard | Existing query retained | Player composition error | Pass valid query |
|
||||
|
||||
There are no asynchronous operations to cancel. Replacing/releasing the query
|
||||
is the deterministic cache-reset and recovery path.
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| Extracted directory | Player `extracted_dir` | Current sandbox | Captured at creation | ADT source root |
|
||||
| Map directory name | Player `map_name` | Current sandbox | Captured at creation | ADT filename/path |
|
||||
| Loader override | Invalid callable | Test/custom data source | Constructor only | Bypasses production ClassDB/file loading |
|
||||
| Tile cache | Enabled | All current uses | Cleared by replacing query | Avoids repeated parse/failure work |
|
||||
| Height policy | 9x9 outer-grid bilinear | Current sandbox | Fixed | Preserves pre-extraction behavior |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
The cache is in-memory and unversioned because it holds native loader output for
|
||||
one query lifetime. No disk cache, schema or migration changes are introduced.
|
||||
Existing ADT/native/cache format versions remain unchanged.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- Logs: dedicated verifier emits `TERRAIN_QUERY PASS` or named failures.
|
||||
- Structured status: every unavailable sample has a stable `failure_code`.
|
||||
- Metrics: synthetic verifier counts loader calls to prove one load per tile.
|
||||
- Debug views: none.
|
||||
- Correlation IDs: not applicable to synchronous local query.
|
||||
|
||||
## Verification
|
||||
|
||||
- Contract: available/unavailable/base/non-finite result invariants.
|
||||
- Pure synthetic adapter: typed coordinate mapping, exact chunk selection,
|
||||
145-height layout and bilinear interpolation within `0.002` units (Vector3
|
||||
source precision), plus cached second query.
|
||||
- Failures: empty tile and invalid height array produce stable codes.
|
||||
- Integration: asset-free player scene accepts injected flat query and preserves
|
||||
spawn offset `0.05` plus physics ground snap.
|
||||
- Boundary: source regression rejects ADT loader/cache/chunk helpers in player.
|
||||
- Fidelity evidence: existing interpolation and snap behavior only; no
|
||||
original-client or collision parity claim.
|
||||
- Performance budget: one dictionary cache lookup and scalar interpolation per
|
||||
cached query; native parse only on first tile access.
|
||||
|
||||
## Extension points
|
||||
|
||||
- Streaming renderer or physics backend may implement `TerrainQuery` without changing player/movement.
|
||||
- The current facade intentionally uses a separate renderer-owned result and is
|
||||
not a `TerrainQuery` implementation. A future adapter must explicitly translate
|
||||
readiness/results and define gameplay authority before composition.
|
||||
- Future result may grow separate slope, surface, liquid or collision contracts;
|
||||
incompatible additions require a versioned contract rather than Dictionary fields.
|
||||
- Test/authoring data sources may use constructor loader override.
|
||||
- Async streaming requires a separate availability/update model.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Typed replaceable ground-height query | Implemented | Contract and injected player regression | Integrate target checklist after review |
|
||||
| Existing ADT height interpolation | Implemented | Synthetic fractional-grid fixture | Compare terrain contact with build 12340 |
|
||||
| Per-tile success/failure cache | Implemented | Loader call-count regression | Add explicit invalidation only with authoring/hot reload |
|
||||
| Renderer loaded-mesh diagnostic backend | Implemented | M03 facade typed sample and detached snapshot contract | Not composed into gameplay; triangle Mesh ray is diagnostic only |
|
||||
| Authoritative renderer/physics backend | Planned | Boundary permits replacement | Define holes/slopes/collision/readiness semantics before gameplay composition |
|
||||
| Holes/slopes/collision | Planned | Outside height-only contract | Later movement/physics package |
|
||||
| Liquid/swim query | Planned | Outside contract | M09/M12 world gameplay |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Bilinear outer-grid height is the inherited sandbox approximation; exact ADT
|
||||
triangle choice, holes and collision are not represented.
|
||||
- Query is synchronous; first access may parse an ADT on the main thread exactly
|
||||
as before extraction. A streaming/backend replacement is future work.
|
||||
- Unavailable results remain cached until query replacement.
|
||||
- Player still owns ground offset/snap policy; only data access is extracted.
|
||||
- Existing native dictionaries remain dynamic inside the adapter.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/gameplay/terrain/terrain_ground_sample.gd` | Immutable available/unavailable result |
|
||||
| `src/gameplay/terrain/terrain_query.gd` | Replaceable typed query boundary |
|
||||
| `src/gameplay/terrain/adt_terrain_query.gd` | Native ADT adapter, cache, chunk selection and interpolation |
|
||||
| `src/scenes/player/third_person_wow_controller.gd` | Query composition and existing ground-snap consumer |
|
||||
| `src/render/world_render_facade.gd` | Read-only loaded-render-mesh diagnostic consumer of typed position/sample contracts |
|
||||
| `src/tools/verify_terrain_query.gd` | Synthetic contract/cache/failure/injection regression |
|
||||
| `src/tests/scenes/player_input_regression.tscn` | Asset-free player composition fixture |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- ADR: none; no coordinate, format or authority decision changed.
|
||||
- Upstream/reference: `targets/02-player-decomposition.md`,
|
||||
`targets/roadmap/02-rendering-and-graphics.md`,
|
||||
`targets/roadmap/04-gameplay-systems.md`, `docs/ARCHITECTURE.md`.
|
||||
@@ -0,0 +1,257 @@
|
||||
# Third-Person Camera
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target/work package | M02 / M02-RND-CAMERA-001 and M02-GMP-INPUT-PROFILE-335-001 consumer update |
|
||||
| Owners | Camera presentation state and CameraPivot/Camera3D lifecycle |
|
||||
| Last verified | M02-GMP-INPUT-PROFILE-335-001 worktree, 2026-07-15 |
|
||||
| Profiles/capabilities | Current sandbox orbit/zoom; identity collision policy |
|
||||
|
||||
## Purpose
|
||||
|
||||
Own third-person mouse capture, orbit yaw/pitch, zoom state and Camera3D local
|
||||
transforms on the existing `CameraPivot` node. Expose an explicit replaceable
|
||||
camera-distance collision policy while preserving the current no-collision
|
||||
sandbox baseline.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Implement an unverified physics raycast or claim build-12340 collision parity.
|
||||
- Own player movement, terrain queries, streaming focus or character visuals.
|
||||
- Implement first-person, follow lag, shoulder camera, cinematics or view modes.
|
||||
- Change current sensitivity, pitch/zoom limits, FOV or far plane.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
InputMap[Remappable camera actions] --> Player[ThirdPersonWowController dispatch]
|
||||
Player --> Rig[ThirdPersonCameraRig]
|
||||
Mouse[Mouse motion] --> Player
|
||||
Rig --> Character[Character yaw transform]
|
||||
Rig --> Pivot[CameraPivot local transform]
|
||||
Rig --> Policy[CameraCollisionPolicy]
|
||||
Policy --> Distance[Resolved distance]
|
||||
Distance --> Camera[Camera3D local transform]
|
||||
Rig --> FlightBasis[Godot-world pivot basis]
|
||||
FlightBasis --> Movement[LocalPlayerMovementController adapter]
|
||||
```
|
||||
|
||||
Allowed dependencies:
|
||||
|
||||
- Existing `PlayerInputActions` names and Godot input events.
|
||||
- Explicit character, pivot and Camera3D Node references on the main thread.
|
||||
- Replaceable `CameraCollisionPolicy` selected by composition/tests.
|
||||
- Movement adapter may read the rig's Godot-world basis during sandbox flight.
|
||||
|
||||
Forbidden dependencies:
|
||||
|
||||
- ADT/terrain parsing, movement velocity, gameplay state or animation ownership.
|
||||
- Camera rig discovering renderer/network/server services.
|
||||
- Default collision policy performing hidden raycasts or changing baseline distance.
|
||||
- Collision policy mutating rig/camera/character nodes.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `ThirdPersonCameraRig` | `Node3D` component | Owns orbit/zoom state on CameraPivot | Main thread; scene-owned | Reports missing Camera3D |
|
||||
| `initialize_for_character(character_body)` | Composition method | Attaches yaw target and captures initial character yaw | Main thread after scene ready | Null leaves yaw target absent |
|
||||
| `synchronize_yaw_from_character()` | Composition method | Refreshes orbit yaw after keyboard movement turns the character | Main physics thread | Missing character leaves yaw unchanged |
|
||||
| `handle_camera_input(event)` | Input adapter | Applies capture/release/zoom/orbit actions | Main input thread | Returns whether camera consumed state |
|
||||
| `set_collision_policy(policy)` | Composition method | Replaces distance resolution policy | Main thread | Null rejected; old policy retained |
|
||||
| `refresh_camera_transform()` | Presentation update | Applies pivot pose and resolved camera distance | Main/physics thread | Missing camera leaves state but no Camera3D mutation |
|
||||
| `godot_world_flight_movement_basis()` | Read model | Returns pivot global basis for sandbox flight | Main thread | None |
|
||||
| `CameraCollisionPolicy.resolve_camera_distance(pivot, requested)` | Policy boundary | Resolves camera distance in Godot units | Main thread in current implementation | Rig clamps output to `[0, requested]` |
|
||||
| `NoCameraCollisionPolicy` | Identity policy | Preserves requested distance with no physics query | Any main-thread call | None |
|
||||
|
||||
Read-only rig state: `yaw_radians`, `pitch_radians`,
|
||||
`requested_distance_units`, `resolved_distance_units` and `is_mouse_captured`.
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Camera action `InputEvent` | Godot/player dispatch | Camera rig | Engine-owned event | One dispatch |
|
||||
| Input | Mouse relative pixels | Godot input | Camera rig | Engine-owned event | One dispatch |
|
||||
| Input | Character `Node3D` | Player composition | Camera rig | Scene-owned reference | Rig scene lifetime |
|
||||
| Input | Keyboard-turn synchronization | Player scene after yaw application | Camera rig | Synchronous notification | Current physics tick |
|
||||
| Input | Collision policy | Composition/test | Camera rig | Rig-held RefCounted | Until replacement |
|
||||
| Output | Character yaw | Camera rig | Player transform | Scene mutation | Orbit event |
|
||||
| Output | Pivot pitch/height | Camera rig | CameraPivot | Scene mutation | Refresh |
|
||||
| Output | Requested/resolved distance | Camera rig/policy | Camera3D | Rig-owned scalar/read model | Until next input/refresh |
|
||||
| Output | Godot-world `Basis` | Camera rig | Flight movement adapter | Value copy | Physics tick |
|
||||
|
||||
Side effects:
|
||||
|
||||
- Mutates mouse mode, character yaw, CameraPivot local transform and Camera3D local transform.
|
||||
- Marks Camera3D current and sets its far plane once at ready.
|
||||
- Releases captured mouse on rig removal.
|
||||
- Creates no files, caches, RIDs, workers, network messages or gameplay state.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Action[Camera action or mouse delta] --> State[Yaw pitch requested distance capture]
|
||||
State --> Clamp[Pitch/zoom clamps]
|
||||
Clamp --> Pivot[Pivot height and pitch]
|
||||
Clamp --> Request[Requested distance]
|
||||
Request --> Policy[CameraCollisionPolicy]
|
||||
Policy --> Safe[Clamp 0..requested]
|
||||
Safe --> Camera[Camera3D Z distance]
|
||||
State --> Character[Character yaw]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> MouseVisible
|
||||
MouseVisible --> MouseCaptured: camera rotate pressed
|
||||
MouseCaptured --> MouseVisible: camera rotate released
|
||||
MouseCaptured --> MouseVisible: release cursor
|
||||
MouseCaptured --> MouseCaptured: mouse motion / orbit and clamp
|
||||
MouseVisible --> MouseVisible: zoom and clamp
|
||||
MouseCaptured --> [*]: rig removed / release mouse
|
||||
MouseVisible --> [*]: rig removed
|
||||
```
|
||||
|
||||
Zoom and collision distance are orthogonal to capture state. The requested zoom
|
||||
persists while a policy may temporarily choose a shorter resolved distance.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Engine as Godot Input
|
||||
participant Player as ThirdPersonWowController
|
||||
participant Rig as ThirdPersonCameraRig
|
||||
participant Policy as CameraCollisionPolicy
|
||||
participant Camera as Camera3D
|
||||
Engine->>Player: InputEvent
|
||||
Player->>Rig: handle_camera_input(event)
|
||||
Rig->>Rig: update capture/orbit/zoom state
|
||||
Rig->>Policy: resolve_camera_distance(pivot, requested)
|
||||
Policy-->>Rig: resolved distance
|
||||
Rig->>Camera: apply local position/rotation
|
||||
opt captured mouse motion
|
||||
Rig->>Player: mutate character yaw transform
|
||||
end
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Parent player scene owns the CameraPivot rig and Camera3D child.
|
||||
- Rig holds non-owning scene references to character and camera.
|
||||
- Rig owns scalar camera state and the RefCounted collision policy.
|
||||
- All input/state/scene mutations occur on the main thread.
|
||||
- Policy output is a scalar; policies must not mutate supplied nodes.
|
||||
- No background tasks, resources, RIDs or caches are created.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Camera3D missing | `_ready` lookup | State continues; transform application skipped | `ThirdPersonCameraRig` error with path | Restore child/path and recreate scene |
|
||||
| Character missing | Initialization | Orbit state updates without character yaw mutation | Composition/test failure | Call initializer with player node |
|
||||
| Null collision policy | Setter guard | Current policy retained | Rig error | Pass valid policy |
|
||||
| Policy returns negative/too large | Rig clamp | Distance clamped to `[0, requested]` | Policy regression tests | Fix/replace policy |
|
||||
| Rig removed while captured | `_exit_tree` | Mouse made visible | Lifecycle regression | Recreate rig normally |
|
||||
|
||||
There is no asynchronous cancellation or persisted rollback. Recreating the rig
|
||||
restores configured defaults.
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| Initial pitch | `-18°` | Current sandbox | State-owned after ready | Initial orbit elevation |
|
||||
| Pitch limits | `-65°..35°` | Current sandbox | Exported | Mouse clamp |
|
||||
| Mouse sensitivity | `0.003` rad/pixel | Current sandbox | Exported | Orbit delta |
|
||||
| Pivot height | `1.7` units | Current sandbox | Exported | Camera eye origin |
|
||||
| Initial distance | `8` units | Current sandbox | Exported before ready | Requested zoom |
|
||||
| Zoom limits/step | `2..18`, step `1` | Current sandbox | Exported | Requested distance clamp |
|
||||
| Far plane | `50000` units | Renderer sandbox | Exported | Camera far plane |
|
||||
| Collision policy | `NoCameraCollisionPolicy` | Current sandbox | Replaceable | Identity distance/no raycast |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
Camera state and policy are transient. No SavedVariables/settings schema, cache
|
||||
or migration is introduced. Scene paths remain
|
||||
`ThirdPersonPlayer/CameraPivot/Camera3D`, so renderer diagnostics and capture
|
||||
tools require no path migration.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- Logs: missing camera/null policy emit explicit component errors.
|
||||
- Read models: yaw, pitch, requested/resolved distance and capture state.
|
||||
- Tests: dedicated verifier reports state/policy/scene counts.
|
||||
- Metrics/correlation IDs: not applicable to constant-time local presentation.
|
||||
|
||||
## Verification
|
||||
|
||||
- State regression: initial yaw/pitch/distance/height/far plane, capture/release,
|
||||
mouse orbit, character yaw, pitch clamps and zoom clamps.
|
||||
- Input-profile integration verifies keyboard yaw synchronization, preventing a
|
||||
later right-mouse orbit from restoring stale character yaw.
|
||||
- Policy regression: fixed-distance test policy changes Camera3D Z; identity
|
||||
policy restores requested distance; baseline policy contains no physics query.
|
||||
- Scene integration: Eastern Kingdoms, Kalimdor and asset-free regression scenes
|
||||
attach the rig to the existing CameraPivot.
|
||||
- Boundary: player no longer owns capture/yaw/pitch/distance or camera transform helper.
|
||||
- Existing input/movement/terrain regressions and renderer dry-run remain required.
|
||||
- Fidelity evidence: current numeric defaults and identity collision behavior
|
||||
only; original-client orbit/collision comparison remains open.
|
||||
- Performance: constant-time scalar/transform work once per physics tick and
|
||||
input event; no default raycast.
|
||||
|
||||
## Extension points
|
||||
|
||||
- Add an opt-in raycast collision policy after reference fixtures define mask,
|
||||
margin, recovery smoothing and first-person limit.
|
||||
- Add follow/shoulder/cinematic rigs as separate components, not branches inside this rig.
|
||||
- Persist user camera settings through a future versioned settings module.
|
||||
- Input contexts may suppress camera actions before dispatch without changing rig state.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Independent third-person rig | Implemented | Three scene wirings and player boundary regression | Integrate target checklist after review |
|
||||
| Existing orbit/zoom behavior | Implemented | Numeric state/transform regression | Compare against build 12340 |
|
||||
| Camera-relative sandbox flight basis | Implemented | Basis and movement regressions | Restrict flight to sandbox profile |
|
||||
| Replaceable collision policy | Implemented | Fixed and identity policy tests | Implement verified collision policy |
|
||||
| Active camera collision | Planned | Identity policy intentionally preserves baseline | Capture original-client occluder fixtures first |
|
||||
| First-person/follow policies | Planned | Outside current sandbox baseline | Later completeness work |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Default collision policy performs no occluder avoidance, matching the previous sandbox.
|
||||
- Exact WoW camera orbit direction, pitch limits, zoom curve, collision margin and
|
||||
recovery smoothing are unverified.
|
||||
- Exported initial values are captured at ready; runtime mutation may require an
|
||||
explicit reconfiguration API later.
|
||||
- Mouse mode remains process-global Godot state and must be coordinated with future UI contexts.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/scenes/player/third_person_camera_rig.gd` | CameraPivot state, input and transforms |
|
||||
| `src/scenes/player/camera_collision_policy.gd` | Replaceable distance-policy boundary |
|
||||
| `src/scenes/player/no_camera_collision_policy.gd` | Identity baseline policy |
|
||||
| `src/scenes/player/third_person_wow_controller.gd` | Camera event dispatch and movement-basis consumer |
|
||||
| `src/scenes/streaming/*_streaming.tscn` | Runtime rig composition |
|
||||
| `src/tests/scenes/player_input_regression.tscn` | Asset-free rig/player composition |
|
||||
| `src/tools/verify_third_person_camera_rig.gd` | State, policy, scene and boundary regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- ADR: none; current behavior and scene paths are preserved.
|
||||
- Upstream/reference: `targets/02-player-decomposition.md`,
|
||||
`targets/roadmap/02-rendering-and-graphics.md`,
|
||||
`targets/roadmap/04-gameplay-systems.md`, `docs/ARCHITECTURE.md`.
|
||||
+196
-38
@@ -5,9 +5,9 @@
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Partial |
|
||||
| Target/work package | M00 active; декомпозиция M01–M03 |
|
||||
| Target/work package | M00 baseline; `M01-RND-STREAMING-FOCUS-001`; `M01-QAR-SERVER-SPAWN-RENDERER-001`; `M03-RND-FACADE-001`; `M03-RND-STREAMING-PLANNER-001`; `M03-RND-SCHEDULER-001`; `M03-RND-INTERNAL-ACCESS-GATE-001`; `M03-RND-FACADE-GROUND-QUERY-001`; `M03-RND-FACADE-ENVIRONMENT-001` |
|
||||
| Owners | Renderer workstream / milestone integrator |
|
||||
| Last verified | M00 visual-diff worktree, 2026-07-11 |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-facade-environment`, 2026-07-16 |
|
||||
| Profiles/capabilities | `Performance`, `Balanced`, `High`, `Custom`; Blizzlike fidelity incomplete |
|
||||
|
||||
## Purpose
|
||||
@@ -26,14 +26,28 @@
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Focus[Camera/player/editor focus] --> Loader[StreamingWorldLoader]
|
||||
Player[Player or spectator Node3D] --> Adapter[Explicit source adapter]
|
||||
Editor[Editor viewport adapter] --> Focus[StreamingFocus]
|
||||
Capture[Capture tool] --> Adapter
|
||||
SpawnFixture[Pinned server spawn fixture] --> SpawnManifest[Spawn render manifest]
|
||||
SpawnManifest --> Capture
|
||||
Adapter --> Focus
|
||||
Focus --> Facade[WorldRenderFacade]
|
||||
Facade --> Loader[StreamingWorldLoader internal]
|
||||
Loader --> Planner[StreamingTargetPlanner]
|
||||
Planner --> TargetPlan[StreamingTargetPlan]
|
||||
TargetPlan --> Loader
|
||||
Loader --> Budget[RenderBudgetScheduler]
|
||||
Assets[Extracted WDT/ADT/M2/WMO/BLP] --> Native[Native loaders]
|
||||
Cache[Baked terrain/M2/WMO caches] --> Loader
|
||||
Native --> Parsed[Parsed tile/model data]
|
||||
Parsed --> Loader
|
||||
Loader --> Scene[SceneTree nodes]
|
||||
Loader --> RS[RenderingServer RIDs/MultiMesh]
|
||||
Sky[DBC sky/light data] --> SkyController[WowSkyController]
|
||||
EnvironmentState[World time producer] --> EnvironmentSnapshot[WorldEnvironmentSnapshot]
|
||||
EnvironmentSnapshot --> Facade
|
||||
Facade --> SkyController[WowSkyController]
|
||||
Sky[DBC sky/light data] --> SkyController
|
||||
SkyController --> Scene
|
||||
```
|
||||
|
||||
@@ -54,29 +68,58 @@ Forbidden dependencies:
|
||||
|
||||
## Public API
|
||||
|
||||
Текущая система ещё не имеет стабильного facade. Фактический integration surface — `StreamingWorldLoader` Node3D и exported properties. M03 должен заменить этот surface на `WorldRenderFacade`.
|
||||
Runtime, capture and probe callers use `WorldRenderFacade` for focus, immutable
|
||||
environment input, metrics and rendered-ground diagnostics. `StreamingWorldLoader`
|
||||
and `WowSkyController` remain internal scene implementations while a later M03
|
||||
package adds entity-visual commands.
|
||||
Gameplay modules and Godot `EditorPlugin` package sources are repository-gated
|
||||
from externally reading/writing loader-private queue, task, cache and tile-state fields.
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `map_name` | Exported property | WDT/map directory name | Set before world load | Missing WDT produces diagnostic/empty world |
|
||||
| `extracted_dir` | Exported property | Root of legally extracted client data | Session lifetime | Missing files reported per loader path |
|
||||
| `camera_path` | Exported property | Current streaming focus compatibility path | Main thread, scene lifetime | Missing camera prevents normal focus update |
|
||||
| `StreamingFocus` | Immutable value | Typed Godot-basis position without Node/Camera dependency | Any thread; caller-owned reference | Null position is a caller contract violation |
|
||||
| `WorldRenderFacade.streaming_focus_source_path` | Exported adapter property | Samples any explicit `Node3D` source into `StreamingFocus` | Main thread, scene lifetime | Missing/wrong node warns once and retains prior focus |
|
||||
| `WorldRenderFacade.set_streaming_focus` | Public method | Accepts focus from app/editor/tool composition | Main thread/session | Missing internal renderer is diagnosed; no state is duplicated |
|
||||
| `WorldRenderFacade.refresh_streaming_focus` | Public method | Samples configured source and delegates existing refresh thresholds | Main thread/session | Returns `false` when dependency/focus is unavailable |
|
||||
| `WorldRenderFacade.renderer_metrics_snapshot` | Public method | Returns a deep-detached queue/cache/activity snapshot | Main thread/caller-owned result | Missing/invalid implementation result returns empty snapshot with diagnostic |
|
||||
| `WorldRenderFacade.sample_ground_height` | Public read-only query | Samples already loaded render terrain at a typed Godot world position | Main thread; immutable caller-held result | Stable unavailable code for missing mesh/intersection/facade; not gameplay authority |
|
||||
| `WorldRenderFacade.renderer_ground_query_snapshot` | Public diagnostic query | Returns ground height plus detached tile/readiness context without exposing mutable state | Main thread; caller-owned deep copy | Missing/invalid renderer returns empty snapshot with diagnostic |
|
||||
| `WorldEnvironmentSnapshot.at_time_of_day` | Immutable value factory | Normalizes a finite authoritative time into `[0, 24)` hours | Any thread; caller-held value | Non-finite input creates invalid value with `environment_time_not_finite` |
|
||||
| `WorldRenderFacade.apply_environment_snapshot` | Public command | Delegates exact world time to the scene-owned DBC sky controller | Main thread; snapshot remains caller-owned | Returns `false` for invalid input or missing/incompatible controller |
|
||||
| `WorldRenderFacade.streaming_world_loader_path` | Internal adapter property | Resolves the existing implementation without exposing it to consumers | Main thread, scene lifetime | Missing method/path produces one diagnostic until recovery |
|
||||
| `WorldRenderFacade.world_environment_controller_path` | Internal adapter property | Resolves the existing `WowSkyController` without exposing it to consumers | Main thread, scene lifetime | Missing method/path produces one diagnostic until recovery |
|
||||
| `camera_path` | Exported property | Camera used only by optional automatic overview positioning | Main thread, scene lifetime | Missing camera skips automatic positioning |
|
||||
| `quality_preset` | Exported property | Applies renderer budgets/radii | Before/at runtime depending property | Invalid combination should be diagnosed |
|
||||
| `runtime_stats_enabled` | Exported property | Enables periodic performance snapshot | Runtime mutable | Logging overhead only |
|
||||
| `hitch_profiler_enabled` | Exported property | Enables named hitch sections | Runtime mutable | Logging overhead only |
|
||||
|
||||
Публичным contract не считаются внутренние dictionaries, queues, job records и generated node names.
|
||||
Публичным contract не считаются `StreamingWorldLoader` methods/properties,
|
||||
внутренние dictionaries, queues, job records и generated node names. Scene-owned
|
||||
loader configuration remains transitional composition data, not a caller API.
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Map/focus/configuration | Scene/app/editor | `StreamingWorldLoader` | Caller config, copied/read | Main thread/session |
|
||||
| Input | `StreamingFocus` | Player/spectator/editor/capture adapter | `WorldRenderFacade` | Immutable caller value | Main thread/session |
|
||||
| Input | Focus-source `NodePath` | Runtime/test scene composition | `WorldRenderFacade` source adapter | Scene-owned node, sampled position | Main thread/scene lifetime |
|
||||
| Input | Ground-query `GodotWorldPosition` | Renderer probe/future adapter | `WorldRenderFacade` | Immutable caller-held value | One main-thread query |
|
||||
| Input | `WorldEnvironmentSnapshot` | World-state/capture adapter | `WorldRenderFacade` / `WowSkyController` | Immutable caller-held value; not retained | One main-thread update |
|
||||
| Input | Map/configuration | Scene/app/editor | `StreamingWorldLoader` | Caller config, copied/read | Main thread/session |
|
||||
| Input | WDT/ADT/M2/WMO/BLP bytes | Extracted asset repository | Native loaders/builders | Loader result owns parsed data | Worker or controlled load |
|
||||
| Input | Baked resources | Cache build tools | Streamer/builders | Shared immutable resource/cache entry | Session/cache lifetime |
|
||||
| Output | Desired tile/detail operations | Streaming planner inside loader | Finalize queues | Loader-owned | Cross-frame |
|
||||
| Test input | Server-spawn render manifest | Coordinate fixture/verifier | Checkpoint capture tool | Repository-owned diagnostic data | Test-process lifetime |
|
||||
| Output | `StreamingTargetPlan` | `StreamingTargetPlanner` | Streamer queue/state apply | Immutable ephemeral value | One refresh |
|
||||
| Internal control | Per-frame lane limits and boolean permits | Streamer configuration / `RenderBudgetScheduler` | Ordered streamer drains | Scheduler-owned counters | Main thread/frame |
|
||||
| Output | Desired tile/detail operations | Streamer plan application | Finalize queues | Loader-owned | Cross-frame |
|
||||
| Output | Terrain/M2/WMO/liquid instances | Loader/builders | Godot world/renderer | Loader/world owner | Main-thread attach |
|
||||
| Output | Metrics/diagnostics | Hitch/stat instrumentation | Logs/baseline tools | Immutable records | Periodic/frame |
|
||||
| Output | Detached renderer metrics | `WorldRenderFacade` | Capture/baseline tools | Caller-owned deep copy | On demand |
|
||||
| Output | `RenderedGroundSample` | `WorldRenderFacade`/loaded terrain | Renderer diagnostics | Immutable caller-held value | One query |
|
||||
| Output | Detached rendered-ground snapshot | `WorldRenderFacade` | Terrain-height probe | Caller-owned deep copy | One query |
|
||||
| Output | Logs/diagnostics | Hitch/stat instrumentation | Logs/baseline tools | Immutable records | Periodic/frame |
|
||||
| Test output | Diagnostic spawn marker and cold/warm PNGs | Checkpoint capture tool | Human/integrator review | SceneTree marker; `user://` files | One capture process |
|
||||
|
||||
Side effects:
|
||||
|
||||
@@ -90,22 +133,40 @@ Side effects:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
F[Streaming focus] --> T[Compute desired ADT tiles]
|
||||
WDT[WDT tile catalog] --> T
|
||||
PlayerSource[Player/spectator Node3D] --> A[Explicit scene adapter]
|
||||
C[Capture camera] --> A
|
||||
SF[Pinned server spawn] --> SM[Validated spawn manifest]
|
||||
SM --> C
|
||||
SM --> Marker[Renderer-native origin marker]
|
||||
E[Editor viewport] --> EA[Editor adapter]
|
||||
A --> F[StreamingFocus]
|
||||
EA --> F
|
||||
F --> RF[WorldRenderFacade]
|
||||
RF --> Planner[StreamingTargetPlanner]
|
||||
Planner --> T[StreamingTargetPlan apply]
|
||||
WDT[WDT tile catalog] --> Planner
|
||||
T --> Q[Tile load queue]
|
||||
Q --> P[Worker parse/cache load]
|
||||
P --> R[Result queues]
|
||||
R --> B[Per-frame budget scheduler]
|
||||
Q --> Parse[Worker parse/cache load]
|
||||
Parse --> R[Result queues]
|
||||
R --> B[RenderBudgetScheduler permits]
|
||||
B --> Terrain[Terrain attach/upgrade]
|
||||
B --> M2[M2 group/MultiMesh attach]
|
||||
B --> WMO[WMO instance/group attach]
|
||||
B --> Liquid[Liquid attach]
|
||||
Loaded[Loaded terrain Mesh and Transform] --> Ground[Facade ground query]
|
||||
Ground --> GroundSample[RenderedGroundSample / detached diagnostics]
|
||||
WorldTime[Authoritative/test time] --> EnvironmentSnapshot[WorldEnvironmentSnapshot]
|
||||
EnvironmentSnapshot --> RF
|
||||
RF --> SkyController[WowSkyController]
|
||||
DBC[Light/Area/Skybox DBC] --> SkyController
|
||||
SkyController --> World
|
||||
Terrain --> World[Godot world]
|
||||
M2 --> World
|
||||
WMO --> World
|
||||
Liquid --> World
|
||||
F --> E[Eviction/retention decisions]
|
||||
E --> World
|
||||
Marker --> World
|
||||
F --> Eviction[Eviction/retention decisions]
|
||||
Eviction --> World
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
@@ -113,13 +174,14 @@ flowchart TD
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Initializing
|
||||
Initializing --> Ready: WDT/catalog/config ready
|
||||
Initializing --> WaitingForFocus: WDT/catalog/config ready
|
||||
Initializing --> Degraded: missing loaders/data/cache
|
||||
Ready --> Streaming: focus update
|
||||
WaitingForFocus --> Streaming: valid StreamingFocus
|
||||
Streaming --> Streaming: load/finalize/evict ticks
|
||||
Streaming --> WaitingForFocus: world reset without replacement focus
|
||||
Streaming --> SwitchingMap: map/focus reset
|
||||
SwitchingMap --> Initializing
|
||||
Ready --> ShuttingDown
|
||||
WaitingForFocus --> ShuttingDown
|
||||
Degraded --> ShuttingDown
|
||||
Streaming --> ShuttingDown
|
||||
ShuttingDown --> [*]
|
||||
@@ -129,30 +191,67 @@ stateDiagram-v2
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Focus as Camera/Player/Editor
|
||||
participant Stream as StreamingWorldLoader
|
||||
participant Source as Player/Editor/Capture source
|
||||
participant Focus as StreamingFocus adapter
|
||||
participant Facade as WorldRenderFacade
|
||||
participant Stream as StreamingWorldLoader internal
|
||||
participant Planner as StreamingTargetPlanner
|
||||
participant Worker as Worker task
|
||||
participant Budget as Main-thread budget
|
||||
participant Budget as RenderBudgetScheduler
|
||||
participant Render as SceneTree/RenderingServer
|
||||
Focus->>Stream: focus position changed
|
||||
Stream->>Stream: desired tiles and priorities
|
||||
Source->>Focus: sample explicit Godot position
|
||||
Focus->>Facade: set/refresh typed focus
|
||||
Facade->>Stream: delegate focus and refresh
|
||||
Stream->>Planner: typed focus, catalog and target policy
|
||||
Planner-->>Stream: immutable wanted/retained plan
|
||||
Stream->>Stream: queue priority and loaded-tile LOD/detail state
|
||||
Stream->>Worker: parse/load tile and detail data
|
||||
Worker-->>Stream: immutable result
|
||||
Stream->>Budget: enqueue finalize operations
|
||||
Budget->>Render: attach bounded terrain/M2/WMO/liquid work
|
||||
Stream->>Budget: begin_frame(operation limits)
|
||||
Stream->>Budget: try_consume_permit(lane)
|
||||
Budget-->>Stream: true while lane remains bounded
|
||||
Stream->>Render: attach permitted terrain/M2/WMO/liquid work
|
||||
Stream->>Render: evict outside retention range
|
||||
Source->>Facade: sample_ground_height(typed position)
|
||||
Facade->>Stream: sample loaded render terrain
|
||||
Stream-->>Facade: RenderedGroundSample / diagnostic snapshot
|
||||
Source->>Facade: apply_environment_snapshot(exact time)
|
||||
Facade->>Render: delegate to WowSkyController
|
||||
Render->>Render: freeze local clock; retain DBC sampling/apply rules
|
||||
Stream->>Budget: shutdown: cancel permit issuance
|
||||
Stream->>Worker: shutdown: wait for WorkerThreadPool tasks
|
||||
Stream->>Stream: shutdown: finish registered ResourceLoader requests
|
||||
Stream->>Render: clear queues, nodes, caches and RIDs
|
||||
Stream->>Render: clear queues and owned tree nodes/RIDs
|
||||
Stream->>Stream: free detached prototypes and release resource caches
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- `StreamingWorldLoader` владеет tile states, queues, task registries, active world instances и render caches session scope.
|
||||
- `WorldRenderFacade` owns only scene-relative adapter paths. It does not own the
|
||||
streamer, environment controller, focus source, returned metrics, queues,
|
||||
caches, nodes or RIDs.
|
||||
- `WorldEnvironmentSnapshot` is an immutable caller-owned scalar value. Neither
|
||||
facade nor sky controller retains it; the controller copies normalized hours
|
||||
into its existing fixed-clock fields on the main thread.
|
||||
- `WowSkyController` continues to own DBC sampling state and mutations of its
|
||||
duplicated Godot `Environment`, sun, skybox node and world shader globals.
|
||||
- Rendered-ground query results and diagnostic snapshots are caller-owned values;
|
||||
the facade never returns Mesh, Node, tile-state or queue references.
|
||||
- Loaded-mesh ground sampling is renderer diagnostics, not authoritative gameplay
|
||||
collision. M02 `TerrainQuery` composition and ADT cache ownership remain unchanged.
|
||||
- `StreamingTargetPlanner` is stateless and owns only call-local calculations;
|
||||
its immutable plan is consumed synchronously by the streamer.
|
||||
- `RenderBudgetScheduler` owns only per-frame lane counters and a terminal
|
||||
cancellation flag. The streamer retains queue ordering and all operation side effects.
|
||||
- Focus producers own the immutable `StreamingFocus` reference; the loader retains
|
||||
the latest reference and converts it to `Vector3` only inside the render boundary.
|
||||
- Worker tasks не должны менять SceneTree и shared Resource concurrently.
|
||||
- Parsed/grouped results передаются обратно через guarded result queues.
|
||||
- Mesh/material/node/RID finalization выполняется main thread и ограничивается exported budgets.
|
||||
- Shutdown/map switch обязан дождаться WorkerThreadPool jobs и зарегистрированных threaded ResourceLoader requests до очистки очередей, nodes, caches и RIDs.
|
||||
- Parented streamed nodes остаются owned by SceneTree during shutdown; detached M2/WMO prototype caches are explicitly `free()`d after asynchronous work drains.
|
||||
- A temporary liquid root with no generated child geometry is explicitly freed by `ADTBuilder` instead of being returned or abandoned.
|
||||
- Cache resources считаются immutable после публикации.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
@@ -160,12 +259,19 @@ sequenceDiagram
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Missing WDT/ADT | File/catalog check | Tile/map unavailable | Loader warning/error | Fix extraction/profile, reload |
|
||||
| Missing focus source | Explicit NodePath resolution | Retain current streamed content and prior valid focus | One warning until source recovers | Fix composition path or call `set_streaming_focus` |
|
||||
| Missing renderer implementation | Facade path/method validation | Focus/metrics call fails without mutation | One facade diagnostic until recovery | Fix scene composition path/implementation |
|
||||
| Invalid metrics result | Facade return-type validation | Return empty caller snapshot | Facade error | Restore `Dictionary` implementation contract |
|
||||
| Invalid environment time | Snapshot finite-value validation | Reject update; retain prior sky clock | `environment_time_not_finite` on invalid value | Supply a finite time snapshot |
|
||||
| Missing environment controller | Facade path/method validation | Reject environment update; other facade APIs remain available | One facade diagnostic until recovery | Fix scene composition path/implementation |
|
||||
| No focus supplied | Refresh contract | No target recomputation; queues continue draining | `refresh_streaming_focus()` returns `false` | Supply player/editor/capture focus |
|
||||
| Cache version mismatch | Required format constant | Reject stale cache/fallback | Version diagnostic | Rebuild matching cache |
|
||||
| Worker failure | Task result/empty data | Skip/degrade affected asset | Queue/task warning | Retry/rebuild source |
|
||||
| Main-thread hitch | Named section timing | Frame spike, work remains queued | `HITCH` log | Lower budget/fix finalize path |
|
||||
| Render terrain unavailable | Facade ground query | Immutable unavailable sample | Stable `render_terrain_*` failure code and optional snapshot | Wait for streaming or use gameplay-owned query backend |
|
||||
| D3D12 descriptor exhaustion | Rendering backend error | Render failure/fallback backend | Godot error + baseline report | Dedup resources/fix settings |
|
||||
| Teleport/map change | Focus/session transition | Old jobs become stale | Target/session generation | Cancel/drop stale results |
|
||||
| Shutdown leak | Godot leak/RID diagnostics | Resource retained | Verbose shutdown report | Drain owned worker and resource requests before clearing registries |
|
||||
| Shutdown leak | Godot leak/RID diagnostics | Resource retained | Verbose shutdown report + cache shutdown verifier | Drain requests, preserve tree ownership, then free detached prototypes and clear resource caches |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
@@ -181,6 +287,9 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
- `StreamingFocus` is runtime-only and is not serialized. Scene migration adds
|
||||
`streaming_focus_source_path`; existing `camera_path` remains valid only for
|
||||
optional automatic overview positioning.
|
||||
- Renderer reads caches under `data/cache`; caches are not committed.
|
||||
- Format changes require `FORMAT_VERSION`/required-version bump and rebuild instructions.
|
||||
- Atomic cache publication and unified manifest are target-state work from FND/M03.
|
||||
@@ -189,22 +298,37 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
## Diagnostics and observability
|
||||
|
||||
- Logs: `HITCH`, `PERF`, `TERRAIN_QUALITY`, `SKY_LIGHT`, `SKYBOX_MODEL`.
|
||||
- API snapshot: `WorldRenderFacade.renderer_metrics_snapshot()` returns detached
|
||||
queue/cache/activity counts without exposing mutable renderer state.
|
||||
- Metrics: queue depths, active tiles/assets, named finalize times, frame percentiles and max hitch.
|
||||
- Debug views: render checkpoint scenes/captures; future streaming/LOD/portal overlays.
|
||||
- Correlation IDs: currently mostly tile/path keys; target architecture adds session/job IDs.
|
||||
|
||||
## Verification
|
||||
|
||||
- Unit/contract tests: material mapping, unique-ID dedupe, placement probes, baseline manifest, five-point coordinate calibration, synthetic perceptual checkpoint diff, camera-pose grid plan.
|
||||
- Integration/E2E: Eastern Kingdoms/Kalimdor streaming scenes and seven cold/warm checkpoints.
|
||||
- Fidelity evidence: пять локальных build 12340 reference JPG откалибровали terrain/ADT/M2/WMO/liquid viewpoints; automated paired-image metrics exist, но synthetic animation/dusk и полный human approval ещё не закрыты.
|
||||
- Unit/contract tests: streaming-focus contract/wiring, material mapping, unique-ID dedupe, placement probes, baseline manifest, five-point coordinate calibration, synthetic perceptual checkpoint diff, camera-pose grid plan.
|
||||
- Pure planner contract: center/corner/catalog/clamp/editor cases plus bounded
|
||||
High-like iteration timing without loading a world scene.
|
||||
- Budget scheduler contract: exact lane exhaustion, shared chunk removal/create
|
||||
priority, independent lanes, frame reset, invalid limits, terminal cancellation,
|
||||
dependency boundaries and bounded permit timing without loading a world scene.
|
||||
- Internal-access contract: derives the current streamer's private queue/task/
|
||||
cache/state field inventory and rejects external member/reflection access from
|
||||
gameplay or EditorPlugin package code.
|
||||
- Ground-query facade contract: available/unavailable typed result delegation,
|
||||
detached nested diagnostics and terrain-probe source migration without private access.
|
||||
- Rendered-ground value contract: available/non-finite/unavailable invariants pass
|
||||
from a cold Godot class cache before editor import.
|
||||
- Integration/E2E: Eastern Kingdoms/Kalimdor streaming scenes, seven M00
|
||||
cold/warm checkpoints and one dedicated mapped server-spawn checkpoint.
|
||||
- Fidelity evidence: семь локальных build 12340 reference JPG покрывают terrain/ADT/M2/WMO/liquid/sky и реальный `GryphonRoost01`; automated cold/warm metrics имеют `compared=14`, `missing=0`. Sky использует приблизительно paired camera, а animated-M2 остаётся asset-paired/placement-unpaired из-за synthetic probe.
|
||||
- Performance budgets: M00 report records cold/warm p95 and max hitch; no final acceptance threshold yet.
|
||||
- Manual diagnostics: [`../RENDER_BASELINE.md`](../RENDER_BASELINE.md) and [`../../RENDER.md`](../../RENDER.md).
|
||||
|
||||
## Extension points
|
||||
|
||||
- Target `WorldRenderFacade` for runtime/editor/capture callers.
|
||||
- Target `StreamingFocus` independent of Camera3D.
|
||||
- Extend `StreamingFocus` with measured direction/velocity needs when the pure planner is extracted.
|
||||
- Extracted planner, budget scheduler, terrain, M2, WMO, liquid, sky and character services.
|
||||
- Separate Blizzlike/Enhanced material and quality profiles.
|
||||
|
||||
@@ -218,28 +342,58 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
| Liquids | Partial | MH2O/MLIQ paths | LiquidType/depth/shore fidelity |
|
||||
| Sky/light | Partial | DBC controller/logs | Zone transition paired validation |
|
||||
| Character presentation | Prototype | Character creator/player experiment | Full customization/equipment states |
|
||||
| Stable renderer facade | Planned | M03 | Current caller uses monolithic Node API |
|
||||
| Camera-independent streaming focus | Implemented | Typed contract, two player-focused runtime scenes and three explicitly camera-focused diagnostic tools | Multi-focus/direction/velocity belong to planner extraction |
|
||||
| Golden server-spawn visualization | Implemented | Pinned AzerothCore fixture, validated renderer manifest and diagnostic origin-marker capture at ADT `(32,48)`/MCNK `(3,12)` | No TrinityCore row or original-client visual-parity claim |
|
||||
| Stable renderer facade | Partial | Facade contracts cover focus, environment time, metrics and loaded-mesh ground query | Entity visuals remain |
|
||||
| Environment snapshot input | Implemented boundary / Partial fidelity | Immutable time contract, facade/controller delegation and checkpoint migration | Weather/indoor state and paired zone-transition fidelity remain |
|
||||
| Pure streaming target planner | Implemented | `M03-RND-STREAMING-PLANNER-001` asset-free contract/performance regression | Queue scheduling and loaded-tile LOD application remain in streamer |
|
||||
| Bounded render budget scheduler | Implemented | `M03-RND-SCHEDULER-001` asset-free permit/cancellation/performance regression | Queue storage, worker concurrency and operation execution remain in streamer/services |
|
||||
| Gameplay/editor/tool internal-access boundary | Implemented | Access gate scans gameplay, EditorPlugin packages and terrain probe against actual private streamer declarations | Add future renderer diagnostic tools explicitly to the protected list |
|
||||
| Rendered terrain ground query | Implemented | `M03-RND-FACADE-GROUND-QUERY-001` typed result/snapshot isolation and probe migration | Diagnostic Mesh ray only; not movement/collision authority |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Monolithic streamer mixes planning, jobs, caches and presentation.
|
||||
- Direct camera path remains until M01/M03.
|
||||
- `camera_path` remains only for optional automatic overview positioning; it no
|
||||
longer drives runtime streaming or discovers the active viewport camera.
|
||||
- Original-client paired fidelity evidence incomplete.
|
||||
- Первый paired run выявил coordinate/placement mismatch: несколько совпадающих server-derived camera positions оказываются под terrain или внутри WMO/rocks OpenWC.
|
||||
- Terrain-height probe исключил under-terrain состояние для всех пяти точек; waterfall exact-XZ miss классифицирован как TriangleMesh seam/edge и подтверждён nearby sample в 2 units.
|
||||
- Camera-occluder probe не нашёл camera containment в пяти точках; paired mismatch локализован прежде всего в manual look direction/target/FOV calibration, с явным ограничением по RID-only geometry.
|
||||
- Empirical FOV sweep выявил, что checkpoint camera должна явно вызывать `make_current()`; после исправления projection ranking остаётся inconclusive из-за неизвестного manual yaw/pitch/framing reference.
|
||||
- Camera-pose sweep can now rank bounded yaw/pitch grids without changing manifest defaults; perceptual ranking remains diagnostic and requires human framing approval.
|
||||
- Goldshire Inn pose evidence has no landmark-aligned optimum: full-frame error decreases as negative pitch replaces the missing inn composition with grass. The checkpoint remains a spatial/placement gap, not a validated camera offset.
|
||||
- D3D12 descriptor issues remain; the capture-path anonymous `RefCounted` shutdown leak is regression-covered by a clean verbose dry-run, while other RID/resource diagnostics still require independent evidence.
|
||||
- The first Goldshire pose grid was invalidated by WMO readiness: the inn is absent after 2 seconds but visible after the manifest-standard 8-second wait. Pose comparisons must stabilize asynchronous checkpoint content first.
|
||||
- A ready-scene Goldshire 3x3 grid improved mean error from `0.101402` at zero offsets to `0.087952` at yaw `10`/pitch `-10`; pitch/FOV refinement remains required before manifest calibration.
|
||||
- Joint Goldshire pitch/FOV refinement had no human-approved optimum: full-frame error preferred grass-heavy FOV 62/pitch -25, while FOV 38/pitch -10 better matched building scale. Landmark/region scoring is required before calibration.
|
||||
- Common-region scoring `[300,100,1400,650]` moved Goldshire's numeric optimum to FOV 38/yaw 10/pitch -15, but human inspection still found an oversized/cropped facade; semantic landmarks or masks remain required.
|
||||
- Final paired audit compared fourteen cold/warm images for seven private build 12340 references with zero missing pairs; mean error ranges `0.066893..0.176955`. Animated-M2 native motion is observed across two original-client phases, but its real Lakeshire placement differs from the synthetic Goldshire probe. Sky-transition evidence uses the checkpoint map position/time with manually approved approximate framing.
|
||||
- D3D12 descriptor issues remain. Explicit shutdown ownership removes the GUI capture's Node/resource/RID leaks in focused and seven-checkpoint runs; a timing-dependent ObjectDB warning for anonymous zero-reference `RefCounted` objects remains as a separate engine-teardown diagnostic.
|
||||
- M2/WMO/material/particle/ribbon/portal parity incomplete.
|
||||
- Public API is mostly exported configuration rather than stable contracts.
|
||||
- Rendered-ground sampling generates a triangle mesh on demand and retains the
|
||||
historical nearby seam fallback. It is appropriate for diagnostics, not a
|
||||
per-physics-frame gameplay query or a claim of exact terrain collision parity.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/world_render_facade.gd` | Stable caller boundary for typed focus/environment, detached metrics and rendered-ground queries |
|
||||
| `src/render/environment/world_environment_snapshot.gd` | Immutable normalized world time input with explicit invalid state |
|
||||
| `src/render/terrain/rendered_ground_sample.gd` | Immutable renderer-owned available/unavailable ground query result |
|
||||
| `src/render/streaming/streaming_target_planner.gd` | Scene-free wanted/retained ADT target calculation |
|
||||
| `src/render/streaming/streaming_target_policy.gd` | Immutable renderer radius/prefetch policy |
|
||||
| `src/render/streaming/streaming_target_plan.gd` | Immutable planner result with read-only tile-key sets |
|
||||
| `src/render/streaming/render_budget_scheduler.gd` | Scene-free per-frame operation permits and terminal cancellation |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Current runtime orchestration, queues, caches and attachment |
|
||||
| `src/domain/streaming/streaming_focus.gd` | Immutable camera-independent streaming position contract |
|
||||
| `src/tools/verify_streaming_focus.gd` | Headless contract, dependency and runtime/tool wiring regression |
|
||||
| `src/tools/verify_world_render_facade.gd` | Focus/environment/metrics/ground delegation, snapshot isolation and consumer wiring regression |
|
||||
| `src/tools/verify_world_environment_snapshot.gd` | Cold-start environment value normalization and invalid-input regression |
|
||||
| `src/tools/verify_rendered_ground_sample.gd` | Cold-start renderer ground result invariant regression |
|
||||
| `src/tools/verify_streaming_target_planner.gd` | Planner behavior, dependency and bounded timing regression |
|
||||
| `src/tools/verify_render_budget_scheduler.gd` | Scheduler bounds, shared-lane priority, cancellation and timing regression |
|
||||
| `src/tools/verify_renderer_internal_access.gd` | Gameplay/EditorPlugin/registered renderer-tool boundary gate derived from private streamer fields |
|
||||
| `addons/mpq_extractor/loaders/adt_builder.gd` | Terrain/material/liquid construction |
|
||||
| `addons/mpq_extractor/loaders/m2_builder.gd` | Static M2 mesh/material construction |
|
||||
| `addons/mpq_extractor/loaders/m2_native_animated_builder.gd` | Native animated M2 experiment |
|
||||
@@ -249,10 +403,13 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
| `src/tools/build_*cache.gd`, `src/tools/bake_*cache.gd` | Offline cache generation |
|
||||
| `tools/run_render_baseline.ps1` | Unified M00 baseline runner |
|
||||
| `src/tools/compare_render_checkpoints.gd` | Offline JPG/PNG paired-image perceptual metrics and JSON pass/fail report |
|
||||
| `src/tools/verify_render_runtime_cache_shutdown.gd` | Headless ownership regression for detached runtime prototypes, resource caches and empty liquid roots |
|
||||
| `src/tools/capture_render_checkpoints.gd` | Deterministic no-roll checkpoint camera, performance and visual capture |
|
||||
| `src/tools/server_spawn_render_manifest.json` | Dedicated mapped AzerothCore spawn checkpoint and diagnostic marker configuration |
|
||||
| `src/tools/verify_server_spawn_renderer.gd` | Fixture-to-renderer checkpoint contract verification |
|
||||
| `tools/sweep_render_checkpoint_camera_pose.ps1` | Offline yaw/pitch capture grid and paired-error ranking |
|
||||
| `src/tools/verify_render_coordinate_calibration.gd` | Build 12340 camera-coordinate golden point round-trip diagnostic |
|
||||
| `src/tools/probe_render_terrain_height.gd` | Offline active-mesh terrain height and camera-clearance report |
|
||||
| `src/tools/probe_render_terrain_height.gd` | Offline facade-backed active-mesh terrain height and camera-clearance report |
|
||||
| `src/tools/probe_render_camera_occluders.gd` | Scene-tree placement containment and camera-to-target AABB intersection report |
|
||||
|
||||
## Related decisions and references
|
||||
@@ -260,5 +417,6 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
- [`../../RENDER.md`](../../RENDER.md)
|
||||
- [`../RENDER_BASELINE.md`](../RENDER_BASELINE.md)
|
||||
- [`../CAMERA_POSE_SWEEP.md`](../CAMERA_POSE_SWEEP.md)
|
||||
- [`../M00_FINAL_PAIRED_EVIDENCE.md`](../M00_FINAL_PAIRED_EVIDENCE.md)
|
||||
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||
- [`../GODOT_BEST_PRACTICES.md`](../GODOT_BEST_PRACTICES.md)
|
||||
|
||||
@@ -19,6 +19,93 @@ config/icon="res://icon.svg"
|
||||
|
||||
enabled=PackedStringArray("res://addons/mpq_extractor/plugin.cfg")
|
||||
|
||||
[input]
|
||||
|
||||
openwc_player_move_forward={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
openwc_player_move_backward={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
openwc_player_strafe_left={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
openwc_player_strafe_right={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
openwc_player_blizzlike_strafe_left={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
openwc_player_blizzlike_strafe_right={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
openwc_player_turn_left={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
openwc_player_turn_right={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
openwc_player_debug_fly_up={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
openwc_player_debug_fly_down={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
openwc_player_debug_sprint={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194325,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
openwc_player_debug_toggle_flight={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":32,"key_label":32,"unicode":32,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
openwc_player_camera_rotate={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":2,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||
]
|
||||
}
|
||||
openwc_player_camera_zoom_in={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":4,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||
]
|
||||
}
|
||||
openwc_player_camera_zoom_out={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":5,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||
]
|
||||
}
|
||||
openwc_player_release_cursor={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194305,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[physics]
|
||||
|
||||
3d/physics_engine="Jolt Physics"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# Reference projects are research inputs, not OpenWC runtime resources.
|
||||
@@ -2,6 +2,10 @@ Reference-only sources and tools.
|
||||
|
||||
This directory contains external projects used to compare WoW file-format behavior and editor pipelines while implementing OpenWC. Runtime code should not depend on anything in this directory.
|
||||
|
||||
Godot project scanning is disabled for this directory by `.gdignore`. Reference
|
||||
assets remain available to Git, text search and external tooling, but Godot must
|
||||
not generate `.import` sidecars inside nested reference repositories.
|
||||
|
||||
- `blender-wow-studio-3.4-1.1.0_Experimental/` - Blender add-on sources for M2/WMO import/export reference.
|
||||
- `wow.export/` - wow.export source tree and Blender OBJ importer reference.
|
||||
- `noggit-red/` - Noggit RED source tree from `https://gitlab.com/dirtbikercj/noggit-red`, used as a reference for WoW 3.3.5 map editing, ADT/WMO/M2 placement behavior, UID handling and editor workflows.
|
||||
|
||||
+1
-1
Submodule reference/open-realm updated: dfe316d3b0...c7ed0545c8
@@ -0,0 +1,44 @@
|
||||
class_name AdtChunkCoordinate
|
||||
extends RefCounted
|
||||
|
||||
## Immutable MCNK chunk indices inside one ADT tile.
|
||||
## `chunk_x` grows east and `chunk_y` grows south, matching the ADT header and
|
||||
## tile conversion contract rather than Godot's X/Z member names.
|
||||
|
||||
const MINIMUM_CHUNK_INDEX: int = 0
|
||||
const MAXIMUM_CHUNK_INDEX: int = 15
|
||||
|
||||
var tile_coordinate: AdtTileCoordinate:
|
||||
get:
|
||||
return _tile_coordinate
|
||||
|
||||
var chunk_x: int:
|
||||
get:
|
||||
return _chunk_x
|
||||
|
||||
var chunk_y: int:
|
||||
get:
|
||||
return _chunk_y
|
||||
|
||||
var _tile_coordinate: AdtTileCoordinate
|
||||
var _chunk_x: int
|
||||
var _chunk_y: int
|
||||
|
||||
|
||||
## Creates chunk indices without clamping so invalid parser input is visible.
|
||||
func _init(tile_coordinate_value: AdtTileCoordinate, chunk_x_value: int, chunk_y_value: int) -> void:
|
||||
_tile_coordinate = tile_coordinate_value
|
||||
_chunk_x = chunk_x_value
|
||||
_chunk_y = chunk_y_value
|
||||
|
||||
|
||||
## Returns whether the tile and both chunk indices are inside standard bounds.
|
||||
func is_within_map_grid() -> bool:
|
||||
return (
|
||||
_tile_coordinate != null
|
||||
and _tile_coordinate.is_within_map_grid()
|
||||
and _chunk_x >= MINIMUM_CHUNK_INDEX
|
||||
and _chunk_x <= MAXIMUM_CHUNK_INDEX
|
||||
and _chunk_y >= MINIMUM_CHUNK_INDEX
|
||||
and _chunk_y <= MAXIMUM_CHUNK_INDEX
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
uid://b1lf5w5p3b6ye
|
||||
@@ -0,0 +1,29 @@
|
||||
class_name AdtPlacementPosition
|
||||
extends RefCounted
|
||||
|
||||
## Immutable MDDF/MODF position in ADT file field order.
|
||||
## `x_offset_yards` and `z_offset_yards` are measured from the north-west map
|
||||
## extent; `height_yards` is the vertical field stored between them.
|
||||
|
||||
var x_offset_yards: float:
|
||||
get:
|
||||
return _x_offset_yards
|
||||
|
||||
var height_yards: float:
|
||||
get:
|
||||
return _height_yards
|
||||
|
||||
var z_offset_yards: float:
|
||||
get:
|
||||
return _z_offset_yards
|
||||
|
||||
var _x_offset_yards: float
|
||||
var _height_yards: float
|
||||
var _z_offset_yards: float
|
||||
|
||||
|
||||
## Creates an ADT placement position in on-disk X/height/Z field order.
|
||||
func _init(x_offset_yards_value: float, height_yards_value: float, z_offset_yards_value: float) -> void:
|
||||
_x_offset_yards = x_offset_yards_value
|
||||
_height_yards = height_yards_value
|
||||
_z_offset_yards = z_offset_yards_value
|
||||
@@ -0,0 +1 @@
|
||||
uid://cxxlbkjmdtg7m
|
||||
@@ -0,0 +1,35 @@
|
||||
class_name AdtTileCoordinate
|
||||
extends RefCounted
|
||||
|
||||
## Immutable indices from an ADT filename `Map_tileX_tileY.adt`.
|
||||
## Both axes use the inclusive range 0..63 for a normal WotLK world map.
|
||||
|
||||
const MINIMUM_TILE_INDEX: int = 0
|
||||
const MAXIMUM_TILE_INDEX: int = 63
|
||||
|
||||
var tile_x: int:
|
||||
get:
|
||||
return _tile_x
|
||||
|
||||
var tile_y: int:
|
||||
get:
|
||||
return _tile_y
|
||||
|
||||
var _tile_x: int
|
||||
var _tile_y: int
|
||||
|
||||
|
||||
## Creates file indices without clamping so out-of-map input remains diagnosable.
|
||||
func _init(tile_x_value: int, tile_y_value: int) -> void:
|
||||
_tile_x = tile_x_value
|
||||
_tile_y = tile_y_value
|
||||
|
||||
|
||||
## Returns whether both file indices address the standard 64 by 64 ADT grid.
|
||||
func is_within_map_grid() -> bool:
|
||||
return (
|
||||
_tile_x >= MINIMUM_TILE_INDEX
|
||||
and _tile_x <= MAXIMUM_TILE_INDEX
|
||||
and _tile_y >= MINIMUM_TILE_INDEX
|
||||
and _tile_y <= MAXIMUM_TILE_INDEX
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
uid://bg0a286yty1jw
|
||||
@@ -0,0 +1,29 @@
|
||||
class_name AdtTileLocalPosition
|
||||
extends RefCounted
|
||||
|
||||
## Immutable position relative to the north-west corner of one ADT tile.
|
||||
## East and south offsets are normally in `[0, ADT_TILE_SIZE_YARDS)`; height
|
||||
## remains in the canonical vertical space.
|
||||
|
||||
var east_yards: float:
|
||||
get:
|
||||
return _east_yards
|
||||
|
||||
var height_yards: float:
|
||||
get:
|
||||
return _height_yards
|
||||
|
||||
var south_yards: float:
|
||||
get:
|
||||
return _south_yards
|
||||
|
||||
var _east_yards: float
|
||||
var _height_yards: float
|
||||
var _south_yards: float
|
||||
|
||||
|
||||
## Creates a tile-local position without clamping boundary values.
|
||||
func _init(east_yards_value: float, height_yards_value: float, south_yards_value: float) -> void:
|
||||
_east_yards = east_yards_value
|
||||
_height_yards = height_yards_value
|
||||
_south_yards = south_yards_value
|
||||
@@ -0,0 +1 @@
|
||||
uid://cpit2uvrdud6h
|
||||
@@ -0,0 +1,29 @@
|
||||
class_name CanonicalWowWorldPosition
|
||||
extends RefCounted
|
||||
|
||||
## Immutable WoW 3.3.5a world position in client/server axis order and yards.
|
||||
## X and Y are the horizontal coordinates reported by the client and world
|
||||
## server; Z is height. This type deliberately does not expose a [Vector3].
|
||||
|
||||
var x_yards: float:
|
||||
get:
|
||||
return _x_yards
|
||||
|
||||
var y_yards: float:
|
||||
get:
|
||||
return _y_yards
|
||||
|
||||
var z_yards: float:
|
||||
get:
|
||||
return _z_yards
|
||||
|
||||
var _x_yards: float
|
||||
var _y_yards: float
|
||||
var _z_yards: float
|
||||
|
||||
|
||||
## Creates a canonical position without changing precision or validating map bounds.
|
||||
func _init(x_yards_value: float, y_yards_value: float, z_yards_value: float) -> void:
|
||||
_x_yards = x_yards_value
|
||||
_y_yards = y_yards_value
|
||||
_z_yards = z_yards_value
|
||||
@@ -0,0 +1 @@
|
||||
uid://b75i20d4vnkyh
|
||||
@@ -0,0 +1,16 @@
|
||||
class_name CanonicalWowWorldYaw
|
||||
extends RefCounted
|
||||
|
||||
## Immutable canonical world-facing yaw in radians.
|
||||
## Zero faces increasing WoW X and positive rotation turns toward increasing Y.
|
||||
|
||||
var radians: float:
|
||||
get:
|
||||
return _radians
|
||||
|
||||
var _radians: float
|
||||
|
||||
|
||||
## Creates a yaw without normalization; [CoordinateMapper] normalizes conversions.
|
||||
func _init(radians_value: float) -> void:
|
||||
_radians = radians_value
|
||||
@@ -0,0 +1 @@
|
||||
uid://ddu5eiobntlrm
|
||||
@@ -0,0 +1,167 @@
|
||||
class_name CoordinateMapper
|
||||
extends RefCounted
|
||||
|
||||
## Pure, stateless conversions between OpenWC coordinate value objects.
|
||||
## This is the only domain contract allowed to change coordinate axes. It does
|
||||
## not depend on Node, Resource, scene state, map assets or renderer globals.
|
||||
|
||||
const ADT_TILES_PER_MAP_AXIS: int = 64
|
||||
const ADT_CHUNKS_PER_TILE_AXIS: int = 16
|
||||
const ADT_TILE_SIZE_YARDS: float = 1600.0 / 3.0
|
||||
const ADT_CHUNK_SIZE_YARDS: float = ADT_TILE_SIZE_YARDS / float(ADT_CHUNKS_PER_TILE_AXIS)
|
||||
const WOW_WORLD_HALF_EXTENT_YARDS: float = ADT_TILE_SIZE_YARDS * float(ADT_TILES_PER_MAP_AXIS) * 0.5
|
||||
|
||||
|
||||
## Converts server/wire X/Y/Z to canonical WoW X/Y/Z without an axis swap.
|
||||
static func server_to_canonical(server_position: ServerWorldPosition) -> CanonicalWowWorldPosition:
|
||||
return CanonicalWowWorldPosition.new(
|
||||
server_position.x_yards,
|
||||
server_position.y_yards,
|
||||
server_position.z_yards
|
||||
)
|
||||
|
||||
|
||||
## Converts canonical WoW X/Y/Z to server/wire X/Y/Z without an axis swap.
|
||||
static func canonical_to_server(canonical_position: CanonicalWowWorldPosition) -> ServerWorldPosition:
|
||||
return ServerWorldPosition.new(
|
||||
canonical_position.x_yards,
|
||||
canonical_position.y_yards,
|
||||
canonical_position.z_yards
|
||||
)
|
||||
|
||||
|
||||
## Converts canonical WoW world axes to the Godot Y-up compatibility basis.
|
||||
static func canonical_to_godot(canonical_position: CanonicalWowWorldPosition) -> GodotWorldPosition:
|
||||
return GodotWorldPosition.new(
|
||||
WOW_WORLD_HALF_EXTENT_YARDS - canonical_position.y_yards,
|
||||
canonical_position.z_yards,
|
||||
WOW_WORLD_HALF_EXTENT_YARDS - canonical_position.x_yards
|
||||
)
|
||||
|
||||
|
||||
## Converts the Godot Y-up compatibility basis to canonical WoW world axes.
|
||||
static func godot_to_canonical(godot_position: GodotWorldPosition) -> CanonicalWowWorldPosition:
|
||||
return CanonicalWowWorldPosition.new(
|
||||
WOW_WORLD_HALF_EXTENT_YARDS - godot_position.z_units,
|
||||
WOW_WORLD_HALF_EXTENT_YARDS - godot_position.x_units,
|
||||
godot_position.y_units
|
||||
)
|
||||
|
||||
|
||||
## Returns ADT filename indices directly from a Godot renderer position.
|
||||
static func godot_to_adt_tile(godot_position: GodotWorldPosition) -> AdtTileCoordinate:
|
||||
return AdtTileCoordinate.new(
|
||||
int(floor(godot_position.x_units / ADT_TILE_SIZE_YARDS)),
|
||||
int(floor(godot_position.z_units / ADT_TILE_SIZE_YARDS))
|
||||
)
|
||||
|
||||
|
||||
## Returns east/height/south offsets inside the owning ADT tile.
|
||||
static func godot_to_adt_tile_local(godot_position: GodotWorldPosition) -> AdtTileLocalPosition:
|
||||
var tile_coordinate := godot_to_adt_tile(godot_position)
|
||||
return AdtTileLocalPosition.new(
|
||||
godot_position.x_units - float(tile_coordinate.tile_x) * ADT_TILE_SIZE_YARDS,
|
||||
godot_position.y_units,
|
||||
godot_position.z_units - float(tile_coordinate.tile_y) * ADT_TILE_SIZE_YARDS
|
||||
)
|
||||
|
||||
|
||||
## Converts an ADT tile/local pair directly to the Godot renderer basis.
|
||||
static func adt_tile_local_to_godot(
|
||||
tile_coordinate: AdtTileCoordinate,
|
||||
local_position: AdtTileLocalPosition
|
||||
) -> GodotWorldPosition:
|
||||
return GodotWorldPosition.new(
|
||||
float(tile_coordinate.tile_x) * ADT_TILE_SIZE_YARDS + local_position.east_yards,
|
||||
local_position.height_yards,
|
||||
float(tile_coordinate.tile_y) * ADT_TILE_SIZE_YARDS + local_position.south_yards
|
||||
)
|
||||
|
||||
|
||||
## Converts canonical WoW coordinates to MDDF/MODF X/height/Z file fields.
|
||||
static func canonical_to_adt_placement(canonical_position: CanonicalWowWorldPosition) -> AdtPlacementPosition:
|
||||
return AdtPlacementPosition.new(
|
||||
WOW_WORLD_HALF_EXTENT_YARDS - canonical_position.x_yards,
|
||||
canonical_position.z_yards,
|
||||
WOW_WORLD_HALF_EXTENT_YARDS - canonical_position.y_yards
|
||||
)
|
||||
|
||||
|
||||
## Converts MDDF/MODF X/height/Z file fields to canonical WoW coordinates.
|
||||
static func adt_placement_to_canonical(adt_position: AdtPlacementPosition) -> CanonicalWowWorldPosition:
|
||||
return CanonicalWowWorldPosition.new(
|
||||
WOW_WORLD_HALF_EXTENT_YARDS - adt_position.x_offset_yards,
|
||||
WOW_WORLD_HALF_EXTENT_YARDS - adt_position.z_offset_yards,
|
||||
adt_position.height_yards
|
||||
)
|
||||
|
||||
|
||||
## Returns unclamped ADT filename indices for a canonical position.
|
||||
## Exact south/east boundaries belong to the next tile by floor semantics.
|
||||
static func canonical_to_adt_tile(canonical_position: CanonicalWowWorldPosition) -> AdtTileCoordinate:
|
||||
return AdtTileCoordinate.new(
|
||||
int(floor(float(ADT_TILES_PER_MAP_AXIS) * 0.5 - canonical_position.y_yards / ADT_TILE_SIZE_YARDS)),
|
||||
int(floor(float(ADT_TILES_PER_MAP_AXIS) * 0.5 - canonical_position.x_yards / ADT_TILE_SIZE_YARDS))
|
||||
)
|
||||
|
||||
|
||||
## Converts a canonical position to east/south offsets inside its ADT tile.
|
||||
static func canonical_to_adt_tile_local(canonical_position: CanonicalWowWorldPosition) -> AdtTileLocalPosition:
|
||||
var tile_coordinate := canonical_to_adt_tile(canonical_position)
|
||||
var tile_west_edge_y_yards := (float(ADT_TILES_PER_MAP_AXIS) * 0.5 - float(tile_coordinate.tile_x)) * ADT_TILE_SIZE_YARDS
|
||||
var tile_north_edge_x_yards := (float(ADT_TILES_PER_MAP_AXIS) * 0.5 - float(tile_coordinate.tile_y)) * ADT_TILE_SIZE_YARDS
|
||||
return AdtTileLocalPosition.new(
|
||||
tile_west_edge_y_yards - canonical_position.y_yards,
|
||||
canonical_position.z_yards,
|
||||
tile_north_edge_x_yards - canonical_position.x_yards
|
||||
)
|
||||
|
||||
|
||||
## Reconstructs canonical coordinates from an ADT tile and tile-local offsets.
|
||||
static func adt_tile_local_to_canonical(
|
||||
tile_coordinate: AdtTileCoordinate,
|
||||
local_position: AdtTileLocalPosition
|
||||
) -> CanonicalWowWorldPosition:
|
||||
var tile_west_edge_y_yards := (float(ADT_TILES_PER_MAP_AXIS) * 0.5 - float(tile_coordinate.tile_x)) * ADT_TILE_SIZE_YARDS
|
||||
var tile_north_edge_x_yards := (float(ADT_TILES_PER_MAP_AXIS) * 0.5 - float(tile_coordinate.tile_y)) * ADT_TILE_SIZE_YARDS
|
||||
return CanonicalWowWorldPosition.new(
|
||||
tile_north_edge_x_yards - local_position.south_yards,
|
||||
tile_west_edge_y_yards - local_position.east_yards,
|
||||
local_position.height_yards
|
||||
)
|
||||
|
||||
|
||||
## Returns unclamped MCNK indices for a tile-local position.
|
||||
static func adt_tile_local_to_chunk(
|
||||
tile_coordinate: AdtTileCoordinate,
|
||||
local_position: AdtTileLocalPosition
|
||||
) -> AdtChunkCoordinate:
|
||||
return AdtChunkCoordinate.new(
|
||||
tile_coordinate,
|
||||
int(floor(local_position.east_yards / ADT_CHUNK_SIZE_YARDS)),
|
||||
int(floor(local_position.south_yards / ADT_CHUNK_SIZE_YARDS))
|
||||
)
|
||||
|
||||
|
||||
## Converts server movement yaw to canonical yaw and normalizes it to [-PI, PI).
|
||||
static func server_to_canonical_yaw(server_yaw: ServerWorldYaw) -> CanonicalWowWorldYaw:
|
||||
return CanonicalWowWorldYaw.new(_normalize_yaw(server_yaw.radians))
|
||||
|
||||
|
||||
## Converts canonical yaw to server movement yaw and normalizes it to [-PI, PI).
|
||||
static func canonical_to_server_yaw(canonical_yaw: CanonicalWowWorldYaw) -> ServerWorldYaw:
|
||||
return ServerWorldYaw.new(_normalize_yaw(canonical_yaw.radians))
|
||||
|
||||
|
||||
## Converts canonical world yaw to Godot Y-axis yaw in the mapped world basis.
|
||||
static func canonical_to_godot_yaw(canonical_yaw: CanonicalWowWorldYaw) -> GodotWorldYaw:
|
||||
return GodotWorldYaw.new(_normalize_yaw(canonical_yaw.radians))
|
||||
|
||||
|
||||
## Converts Godot Y-axis world yaw to canonical yaw in the mapped world basis.
|
||||
static func godot_to_canonical_yaw(godot_yaw: GodotWorldYaw) -> CanonicalWowWorldYaw:
|
||||
return CanonicalWowWorldYaw.new(_normalize_yaw(godot_yaw.radians))
|
||||
|
||||
|
||||
static func _normalize_yaw(radians: float) -> float:
|
||||
return fposmod(radians + PI, TAU) - PI
|
||||
@@ -0,0 +1 @@
|
||||
uid://bg8p8rrkmg84d
|
||||
@@ -0,0 +1,29 @@
|
||||
class_name GodotWorldPosition
|
||||
extends RefCounted
|
||||
|
||||
## Immutable Godot Y-up renderer position.
|
||||
## One Godot unit equals one WoW yard in the compatibility renderer. X grows
|
||||
## east, Y grows up and Z grows south from the north-west map extent.
|
||||
|
||||
var x_units: float:
|
||||
get:
|
||||
return _x_units
|
||||
|
||||
var y_units: float:
|
||||
get:
|
||||
return _y_units
|
||||
|
||||
var z_units: float:
|
||||
get:
|
||||
return _z_units
|
||||
|
||||
var _x_units: float
|
||||
var _y_units: float
|
||||
var _z_units: float
|
||||
|
||||
|
||||
## Creates a renderer position without exposing it as a cross-layer [Vector3].
|
||||
func _init(x_units_value: float, y_units_value: float, z_units_value: float) -> void:
|
||||
_x_units = x_units_value
|
||||
_y_units = y_units_value
|
||||
_z_units = z_units_value
|
||||
@@ -0,0 +1 @@
|
||||
uid://tp7jo14w25nc
|
||||
@@ -0,0 +1,17 @@
|
||||
class_name GodotWorldYaw
|
||||
extends RefCounted
|
||||
|
||||
## Immutable Godot Y-axis yaw in radians for the canonical world basis.
|
||||
## With Godot forward `-Z`, the numeric angle equals WoW world yaw after the
|
||||
## position basis conversion. Model-local MDDF/MODF corrections are separate.
|
||||
|
||||
var radians: float:
|
||||
get:
|
||||
return _radians
|
||||
|
||||
var _radians: float
|
||||
|
||||
|
||||
## Creates a Godot world yaw without normalization.
|
||||
func _init(radians_value: float) -> void:
|
||||
_radians = radians_value
|
||||
@@ -0,0 +1 @@
|
||||
uid://cuy2skhdv3f6g
|
||||
@@ -0,0 +1,29 @@
|
||||
class_name ServerWorldPosition
|
||||
extends RefCounted
|
||||
|
||||
## Immutable TrinityCore/AzerothCore world position in wire/database X/Y/Z order.
|
||||
## Units are WoW yards. A distinct type prevents server positions from being
|
||||
## passed directly to renderer APIs even though the scalar values are canonical.
|
||||
|
||||
var x_yards: float:
|
||||
get:
|
||||
return _x_yards
|
||||
|
||||
var y_yards: float:
|
||||
get:
|
||||
return _y_yards
|
||||
|
||||
var z_yards: float:
|
||||
get:
|
||||
return _z_yards
|
||||
|
||||
var _x_yards: float
|
||||
var _y_yards: float
|
||||
var _z_yards: float
|
||||
|
||||
|
||||
## Creates a server position without changing precision or validating map bounds.
|
||||
func _init(x_yards_value: float, y_yards_value: float, z_yards_value: float) -> void:
|
||||
_x_yards = x_yards_value
|
||||
_y_yards = y_yards_value
|
||||
_z_yards = z_yards_value
|
||||
@@ -0,0 +1 @@
|
||||
uid://dilhr7ym3xr43
|
||||
@@ -0,0 +1,16 @@
|
||||
class_name ServerWorldYaw
|
||||
extends RefCounted
|
||||
|
||||
## Immutable TrinityCore/AzerothCore movement orientation in radians.
|
||||
## It uses the WoW client convention: zero faces +X and positive turns toward +Y.
|
||||
|
||||
var radians: float:
|
||||
get:
|
||||
return _radians
|
||||
|
||||
var _radians: float
|
||||
|
||||
|
||||
## Creates a server yaw without normalization.
|
||||
func _init(radians_value: float) -> void:
|
||||
_radians = radians_value
|
||||
@@ -0,0 +1 @@
|
||||
uid://dr8k82t2qgewd
|
||||
@@ -0,0 +1,43 @@
|
||||
class_name ContentId
|
||||
extends RefCounted
|
||||
|
||||
## Stable authored-content identity serialized as canonical lowercase UUID text.
|
||||
## It is independent of runtime entities, server database entries and wire GUIDs.
|
||||
|
||||
var uuid_text: String:
|
||||
get:
|
||||
return _uuid_text
|
||||
|
||||
var _uuid_text: String
|
||||
|
||||
|
||||
## Normalizes surrounding whitespace and hexadecimal letter case.
|
||||
func _init(uuid_text_value: String) -> void:
|
||||
_uuid_text = uuid_text_value.strip_edges().to_lower()
|
||||
|
||||
|
||||
## Reports whether the value has the canonical 8-4-4-4-12 UUID text shape.
|
||||
func is_valid() -> bool:
|
||||
if _uuid_text.length() != 36:
|
||||
return false
|
||||
for character_index in range(_uuid_text.length()):
|
||||
if character_index in [8, 13, 18, 23]:
|
||||
if _uuid_text[character_index] != "-":
|
||||
return false
|
||||
elif not _is_lowercase_hexadecimal_character(_uuid_text[character_index]):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
## Compares stable content identity by normalized UUID text.
|
||||
func equals(other_content_id: ContentId) -> bool:
|
||||
return other_content_id != null and _uuid_text == other_content_id.uuid_text
|
||||
|
||||
|
||||
## Returns the canonical dictionary/serialization key.
|
||||
func to_key() -> String:
|
||||
return _uuid_text
|
||||
|
||||
|
||||
func _is_lowercase_hexadecimal_character(character: String) -> bool:
|
||||
return (character >= "0" and character <= "9") or (character >= "a" and character <= "f")
|
||||
@@ -0,0 +1 @@
|
||||
uid://dk6splyui2an1
|
||||
@@ -0,0 +1,31 @@
|
||||
class_name EntityId
|
||||
extends RefCounted
|
||||
|
||||
## Session-local runtime entity identity.
|
||||
## This sequence is never a stable content ID, server entry or WoW wire GUID.
|
||||
|
||||
var session_sequence: int:
|
||||
get:
|
||||
return _session_sequence
|
||||
|
||||
var _session_sequence: int
|
||||
|
||||
|
||||
## Stores an allocator-issued session sequence without persistence semantics.
|
||||
func _init(session_sequence_value: int) -> void:
|
||||
_session_sequence = session_sequence_value
|
||||
|
||||
|
||||
## Reports whether this is a positive allocator-issued sequence.
|
||||
func is_valid() -> bool:
|
||||
return _session_sequence > 0
|
||||
|
||||
|
||||
## Compares identity inside the same explicitly owned runtime session.
|
||||
func equals(other_entity_id: EntityId) -> bool:
|
||||
return other_entity_id != null and _session_sequence == other_entity_id.session_sequence
|
||||
|
||||
|
||||
## Returns a diagnostic key; it must not be persisted across sessions.
|
||||
func to_debug_key() -> String:
|
||||
return "entity:%d" % _session_sequence
|
||||
@@ -0,0 +1 @@
|
||||
uid://bqdd8el4me5ng
|
||||
@@ -0,0 +1,41 @@
|
||||
class_name ServerEntryId
|
||||
extends RefCounted
|
||||
|
||||
## Adapter-namespaced numeric entry from a server database or canonical snapshot.
|
||||
## Equal numbers in different template tables are intentionally different IDs.
|
||||
|
||||
var entry_namespace: String:
|
||||
get:
|
||||
return _entry_namespace
|
||||
|
||||
var entry_value: int:
|
||||
get:
|
||||
return _entry_value
|
||||
|
||||
var _entry_namespace: String
|
||||
var _entry_value: int
|
||||
|
||||
|
||||
## Normalizes a table/adapter namespace and preserves its positive entry value.
|
||||
func _init(entry_namespace_value: String, entry_value_value: int) -> void:
|
||||
_entry_namespace = entry_namespace_value.strip_edges().to_lower()
|
||||
_entry_value = entry_value_value
|
||||
|
||||
|
||||
## Reports whether both collision-prevention fields are present.
|
||||
func is_valid() -> bool:
|
||||
return not _entry_namespace.is_empty() and _entry_value > 0
|
||||
|
||||
|
||||
## Compares both namespace and numeric entry.
|
||||
func equals(other_server_entry_id: ServerEntryId) -> bool:
|
||||
return (
|
||||
other_server_entry_id != null
|
||||
and _entry_namespace == other_server_entry_id.entry_namespace
|
||||
and _entry_value == other_server_entry_id.entry_value
|
||||
)
|
||||
|
||||
|
||||
## Returns the stable adapter key without implying a ContentId mapping.
|
||||
func to_key() -> String:
|
||||
return "%s:%d" % [_entry_namespace, _entry_value]
|
||||
@@ -0,0 +1 @@
|
||||
uid://cg3mht5bhgsya
|
||||
@@ -0,0 +1,53 @@
|
||||
class_name WowGuid
|
||||
extends RefCounted
|
||||
|
||||
## Opaque 64-bit WoW wire GUID stored as two lossless unsigned 32-bit words.
|
||||
## Build-specific high-type decoding belongs to the protocol adapter and must be
|
||||
## fixture-backed before becoming a domain contract.
|
||||
|
||||
const MAXIMUM_UNSIGNED_32_BIT_VALUE := 0xffffffff
|
||||
|
||||
var high_32_bits: int:
|
||||
get:
|
||||
return _high_32_bits
|
||||
|
||||
var low_32_bits: int:
|
||||
get:
|
||||
return _low_32_bits
|
||||
|
||||
var _high_32_bits: int
|
||||
var _low_32_bits: int
|
||||
|
||||
|
||||
## Preserves both wire words without signed 64-bit conversion.
|
||||
func _init(high_32_bits_value: int, low_32_bits_value: int) -> void:
|
||||
_high_32_bits = high_32_bits_value
|
||||
_low_32_bits = low_32_bits_value
|
||||
|
||||
|
||||
## Reports whether both words fit their unsigned wire ranges.
|
||||
func has_valid_word_ranges() -> bool:
|
||||
return _is_unsigned_32_bit_word(_high_32_bits) and _is_unsigned_32_bit_word(_low_32_bits)
|
||||
|
||||
|
||||
## Reports the all-zero protocol sentinel without assigning gameplay meaning.
|
||||
func is_empty() -> bool:
|
||||
return _high_32_bits == 0 and _low_32_bits == 0
|
||||
|
||||
|
||||
## Compares all 64 opaque wire bits.
|
||||
func equals(other_wow_guid: WowGuid) -> bool:
|
||||
return (
|
||||
other_wow_guid != null
|
||||
and _high_32_bits == other_wow_guid.high_32_bits
|
||||
and _low_32_bits == other_wow_guid.low_32_bits
|
||||
)
|
||||
|
||||
|
||||
## Returns exactly sixteen lowercase hexadecimal digits for diagnostics/replays.
|
||||
func to_hex_string() -> String:
|
||||
return "%08x%08x" % [_high_32_bits, _low_32_bits]
|
||||
|
||||
|
||||
func _is_unsigned_32_bit_word(word_value: int) -> bool:
|
||||
return word_value >= 0 and word_value <= MAXIMUM_UNSIGNED_32_BIT_VALUE
|
||||
@@ -0,0 +1 @@
|
||||
uid://bmwlyacooq2lc
|
||||
@@ -0,0 +1,55 @@
|
||||
class_name MoveIntent
|
||||
extends RefCounted
|
||||
|
||||
## Immutable player locomotion request expressed in character-local axes.
|
||||
## Axis values are normalized to `[-1, 1]`: forward/right/up are positive.
|
||||
## The intent contains no world coordinates, engine input events or presentation state.
|
||||
|
||||
var forward_axis: float:
|
||||
get:
|
||||
return _forward_axis
|
||||
|
||||
var strafe_axis: float:
|
||||
get:
|
||||
return _strafe_axis
|
||||
|
||||
var vertical_axis: float:
|
||||
get:
|
||||
return _vertical_axis
|
||||
|
||||
var is_sprint_requested: bool:
|
||||
get:
|
||||
return _is_sprint_requested
|
||||
|
||||
## Signed keyboard turn request: left is negative and right is positive.
|
||||
var turn_axis: float:
|
||||
get:
|
||||
return _turn_axis
|
||||
|
||||
var _forward_axis: float
|
||||
var _strafe_axis: float
|
||||
var _vertical_axis: float
|
||||
var _is_sprint_requested: bool
|
||||
var _turn_axis: float
|
||||
|
||||
|
||||
## Creates one frame's movement request without retaining mutable input state.
|
||||
func _init(
|
||||
forward_axis_value: float = 0.0,
|
||||
strafe_axis_value: float = 0.0,
|
||||
vertical_axis_value: float = 0.0,
|
||||
sprint_requested: bool = false,
|
||||
turn_axis_value: float = 0.0
|
||||
) -> void:
|
||||
_forward_axis = clampf(forward_axis_value, -1.0, 1.0)
|
||||
_strafe_axis = clampf(strafe_axis_value, -1.0, 1.0)
|
||||
_vertical_axis = clampf(vertical_axis_value, -1.0, 1.0)
|
||||
_is_sprint_requested = sprint_requested
|
||||
_turn_axis = clampf(turn_axis_value, -1.0, 1.0)
|
||||
|
||||
|
||||
## Returns true when the intent requests planar or vertical translation.
|
||||
func has_translation() -> bool:
|
||||
return not is_zero_approx(_forward_axis) \
|
||||
or not is_zero_approx(_strafe_axis) \
|
||||
or not is_zero_approx(_vertical_axis)
|
||||
@@ -0,0 +1 @@
|
||||
uid://btk7brcavbeec
|
||||
@@ -0,0 +1,17 @@
|
||||
class_name StreamingFocus
|
||||
extends RefCounted
|
||||
|
||||
## Immutable world-streaming point independent of cameras and the scene tree.
|
||||
## Renderer adapters may obtain this position from a player, spectator, editor
|
||||
## viewport or capture tool, but streaming consumers receive only this value.
|
||||
|
||||
var world_position: GodotWorldPosition:
|
||||
get:
|
||||
return _world_position
|
||||
|
||||
var _world_position: GodotWorldPosition
|
||||
|
||||
|
||||
## Creates a focus at an explicit position in the Godot renderer basis.
|
||||
func _init(world_position_value: GodotWorldPosition) -> void:
|
||||
_world_position = world_position_value
|
||||
@@ -0,0 +1 @@
|
||||
uid://ehv78jvge0q2
|
||||
@@ -0,0 +1,42 @@
|
||||
class_name PlayerInputActions
|
||||
extends RefCounted
|
||||
|
||||
## Stable Input Map action names consumed by the runtime player input adapter.
|
||||
## Separate translation actions preserve the renderer sandbox while allowing
|
||||
## Blizzlike335 defaults to match build-12340 movement bindings.
|
||||
|
||||
const MOVE_FORWARD := &"openwc_player_move_forward"
|
||||
const MOVE_BACKWARD := &"openwc_player_move_backward"
|
||||
const STRAFE_LEFT := &"openwc_player_strafe_left"
|
||||
const STRAFE_RIGHT := &"openwc_player_strafe_right"
|
||||
const BLIZZLIKE_STRAFE_LEFT := &"openwc_player_blizzlike_strafe_left"
|
||||
const BLIZZLIKE_STRAFE_RIGHT := &"openwc_player_blizzlike_strafe_right"
|
||||
const TURN_LEFT := &"openwc_player_turn_left"
|
||||
const TURN_RIGHT := &"openwc_player_turn_right"
|
||||
const DEBUG_FLY_UP := &"openwc_player_debug_fly_up"
|
||||
const DEBUG_FLY_DOWN := &"openwc_player_debug_fly_down"
|
||||
const DEBUG_SPRINT := &"openwc_player_debug_sprint"
|
||||
const DEBUG_TOGGLE_FLIGHT := &"openwc_player_debug_toggle_flight"
|
||||
const CAMERA_ROTATE := &"openwc_player_camera_rotate"
|
||||
const CAMERA_ZOOM_IN := &"openwc_player_camera_zoom_in"
|
||||
const CAMERA_ZOOM_OUT := &"openwc_player_camera_zoom_out"
|
||||
const RELEASE_CURSOR := &"openwc_player_release_cursor"
|
||||
|
||||
const REQUIRED_ACTIONS: Array[StringName] = [
|
||||
MOVE_FORWARD,
|
||||
MOVE_BACKWARD,
|
||||
STRAFE_LEFT,
|
||||
STRAFE_RIGHT,
|
||||
BLIZZLIKE_STRAFE_LEFT,
|
||||
BLIZZLIKE_STRAFE_RIGHT,
|
||||
TURN_LEFT,
|
||||
TURN_RIGHT,
|
||||
DEBUG_FLY_UP,
|
||||
DEBUG_FLY_DOWN,
|
||||
DEBUG_SPRINT,
|
||||
DEBUG_TOGGLE_FLIGHT,
|
||||
CAMERA_ROTATE,
|
||||
CAMERA_ZOOM_IN,
|
||||
CAMERA_ZOOM_OUT,
|
||||
RELEASE_CURSOR,
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
uid://bbhtyxm428bkm
|
||||
@@ -0,0 +1,110 @@
|
||||
class_name PlayerInputSource
|
||||
extends RefCounted
|
||||
|
||||
## Converts remappable Godot Input Map actions into immutable [MoveIntent] values.
|
||||
## This adapter owns no movement state and may be replaced independently of the
|
||||
## local movement controller.
|
||||
|
||||
const RENDER_SANDBOX_PROFILE_ID := &"RenderSandbox"
|
||||
const BLIZZLIKE_335_PROFILE_ID := &"Blizzlike335"
|
||||
|
||||
var input_profile_id: StringName:
|
||||
get:
|
||||
return _input_profile_id
|
||||
|
||||
var _input_profile_id: StringName
|
||||
|
||||
|
||||
## Selects one supported action layout. Unknown profiles fail closed to Blizzlike335.
|
||||
func _init(requested_profile_id: StringName = BLIZZLIKE_335_PROFILE_ID) -> void:
|
||||
_input_profile_id = (
|
||||
RENDER_SANDBOX_PROFILE_ID
|
||||
if requested_profile_id == RENDER_SANDBOX_PROFILE_ID
|
||||
else BLIZZLIKE_335_PROFILE_ID
|
||||
)
|
||||
|
||||
|
||||
## Samples the current Input singleton state for one physics tick.
|
||||
func sample_move_intent() -> MoveIntent:
|
||||
if _input_profile_id == RENDER_SANDBOX_PROFILE_ID:
|
||||
return compose_move_intent(
|
||||
Input.get_action_strength(PlayerInputActions.MOVE_FORWARD),
|
||||
Input.get_action_strength(PlayerInputActions.MOVE_BACKWARD),
|
||||
Input.get_action_strength(PlayerInputActions.STRAFE_LEFT),
|
||||
Input.get_action_strength(PlayerInputActions.STRAFE_RIGHT),
|
||||
Input.get_action_strength(PlayerInputActions.DEBUG_FLY_UP),
|
||||
Input.get_action_strength(PlayerInputActions.DEBUG_FLY_DOWN),
|
||||
Input.is_action_pressed(PlayerInputActions.DEBUG_SPRINT)
|
||||
)
|
||||
return compose_blizzlike_335_move_intent(
|
||||
Input.get_action_strength(PlayerInputActions.MOVE_FORWARD),
|
||||
Input.get_action_strength(PlayerInputActions.MOVE_BACKWARD),
|
||||
Input.get_action_strength(PlayerInputActions.BLIZZLIKE_STRAFE_LEFT),
|
||||
Input.get_action_strength(PlayerInputActions.BLIZZLIKE_STRAFE_RIGHT),
|
||||
Input.get_action_strength(PlayerInputActions.TURN_LEFT),
|
||||
Input.get_action_strength(PlayerInputActions.TURN_RIGHT),
|
||||
Input.is_action_pressed(PlayerInputActions.CAMERA_ROTATE)
|
||||
)
|
||||
|
||||
|
||||
## Builds the compatibility layout: Q/E always strafe; A/D turn unless
|
||||
## right-mouse camera rotation is active, when A/D also strafe. Default bindings
|
||||
## come from build 12340; conditional routing is reference-implementation evidence.
|
||||
static func compose_blizzlike_335_move_intent(
|
||||
forward_strength: float,
|
||||
backward_strength: float,
|
||||
strafe_left_strength: float,
|
||||
strafe_right_strength: float,
|
||||
turn_left_strength: float,
|
||||
turn_right_strength: float,
|
||||
camera_rotate_active: bool
|
||||
) -> MoveIntent:
|
||||
var effective_strafe_left := strafe_left_strength
|
||||
var effective_strafe_right := strafe_right_strength
|
||||
var turn_axis := clampf(turn_right_strength, 0.0, 1.0) - clampf(turn_left_strength, 0.0, 1.0)
|
||||
if camera_rotate_active:
|
||||
effective_strafe_left += turn_left_strength
|
||||
effective_strafe_right += turn_right_strength
|
||||
turn_axis = 0.0
|
||||
var move_intent := compose_move_intent(
|
||||
forward_strength,
|
||||
backward_strength,
|
||||
effective_strafe_left,
|
||||
effective_strafe_right,
|
||||
0.0,
|
||||
0.0,
|
||||
false
|
||||
)
|
||||
return MoveIntent.new(
|
||||
move_intent.forward_axis,
|
||||
move_intent.strafe_axis,
|
||||
0.0,
|
||||
false,
|
||||
turn_axis
|
||||
)
|
||||
|
||||
|
||||
## Builds a normalized sandbox intent from action strengths for deterministic tests and
|
||||
## alternative input adapters. Strengths outside `[0, 1]` are clamped.
|
||||
static func compose_move_intent(
|
||||
forward_strength: float,
|
||||
backward_strength: float,
|
||||
strafe_left_strength: float,
|
||||
strafe_right_strength: float,
|
||||
fly_up_strength: float,
|
||||
fly_down_strength: float,
|
||||
sprint_requested: bool
|
||||
) -> MoveIntent:
|
||||
var planar_axis := Vector2(
|
||||
clampf(strafe_right_strength, 0.0, 1.0) - clampf(strafe_left_strength, 0.0, 1.0),
|
||||
clampf(forward_strength, 0.0, 1.0) - clampf(backward_strength, 0.0, 1.0)
|
||||
)
|
||||
if planar_axis.length_squared() > 1.0:
|
||||
planar_axis = planar_axis.normalized()
|
||||
var vertical_axis := clampf(fly_up_strength, 0.0, 1.0) - clampf(fly_down_strength, 0.0, 1.0)
|
||||
return MoveIntent.new(
|
||||
planar_axis.y,
|
||||
planar_axis.x,
|
||||
vertical_axis,
|
||||
sprint_requested
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user