Compare commits
173 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e1b2886657 | |||
| f4707750b7 | |||
| 78adb54cfe | |||
| b7c036f14d | |||
| 7973945582 | |||
| 4387c66c0a | |||
| ada0fd9467 | |||
| ff4f320412 | |||
| c83929428d | |||
| 71fa093118 | |||
| e690756301 | |||
| b4955d6162 | |||
| b4b94b3cbb | |||
| d39f591e50 | |||
| 4314f961f6 | |||
| c9547041da | |||
| 60603e18c8 | |||
| 3411156f8f | |||
| e576ae2cad | |||
| a878e7c4e7 | |||
| ab40a76b62 | |||
| e1bb105fff | |||
| 040b635ec6 | |||
| 1e740aaa65 | |||
| c06aed568b | |||
| 01bad4ca26 | |||
| 3a6b1d306d | |||
| e420a8724f | |||
| 1e73e597f8 | |||
| f9e5a4f2e0 | |||
| 16053b46ca | |||
| c5900368d4 | |||
| 79bc4e6ffc | |||
| 5aa40d6afb | |||
| d2cb52ff6b | |||
| c2e0696fe0 | |||
| 05e216a953 | |||
| f01302fd44 | |||
| 4f40c779b9 | |||
| 3f847171f8 | |||
| af9d077905 | |||
| be6db7f7e0 | |||
| a562d126b9 | |||
| 9d74d996d5 | |||
| f88bf9714e | |||
| 7587d3bd3c | |||
| 9df32d38ef | |||
| 9c9020e60f | |||
| c34e538dcf | |||
| 396be5e69d | |||
| 1792938567 | |||
| 0a09d7bc1e | |||
| 91f0ba8433 | |||
| 1b6c46870d | |||
| 2342430439 | |||
| c41e8886a6 | |||
| acb3a809bb | |||
| c13c993645 | |||
| 57ae1b8f16 | |||
| 24aef13801 | |||
| 4b90dbc1c1 | |||
| f800e11cd2 | |||
| 0d2c820ea7 | |||
| b568768866 | |||
| f36fabb8e1 | |||
| 9e69a8a95b | |||
| eb94cfc646 | |||
| aa21aafdfa | |||
| ea08b8ef51 | |||
| 630a0c131d | |||
| 10815be766 | |||
| 97480e06bb | |||
| 6617dfe74e | |||
| 61bad09a9c | |||
| 606770c525 | |||
| b73b2e74a2 | |||
| 1807c3a363 | |||
| 3c5880f190 | |||
| a7b2f9475b | |||
| 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 |
@@ -35,3 +35,7 @@
|
|||||||
|
|
||||||
# CMake build директория
|
# CMake build директория
|
||||||
/src/native/build/
|
/src/native/build/
|
||||||
|
|
||||||
|
# Local root-level diagnostic capture logs
|
||||||
|
/*.stdout.log
|
||||||
|
/*.stderr.log
|
||||||
|
|||||||
@@ -25,7 +25,15 @@ 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/render/wmo/wmo_placement_resolver.gd` - value-only WMO path, identity and transform rules.
|
||||||
|
- `src/render/wmo/wmo_placement_registry.gd` - WMO placement-key to tile/global reference sets; Nodes and render jobs remain in the streamer.
|
||||||
|
- `src/render/wmo/wmo_render_build_step_planner.gd` - mesh-first lightweight WMO group operation and cursor planning without Nodes or Resources.
|
||||||
|
- `src/render/wmo/wmo_render_build_queue.gd` / `wmo_render_build_job.gd` - typed pending group jobs, FIFO placement keys and strong root/resource references without engine destruction.
|
||||||
|
- `src/render/wmo/wmo_render_resource_cache_state.gd` - validated lightweight WMO render Resources, negative cache and pending cache paths without ResourceLoader I/O.
|
||||||
|
- `src/render/wmo/wmo_scene_resource_cache_state.gd` - validated cached-WMO PackedScenes, negative cache and pending `.tscn` paths without file/I/O/Node ownership.
|
||||||
|
- `src/render/liquid/adt_water_load_pipeline_state.gd` - ADT water pending FIFO/dedupe, active task IDs and worker-safe parsed-result mailbox without parser or Node ownership.
|
||||||
- `src/scenes/streaming/eastern_kingdoms_streaming.tscn` - текущая сцена для проверки Azeroth/Eastern Kingdoms.
|
- `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/adt_builder.gd` - сборка ADT terrain, control splat материалов и MH2O liquids.
|
||||||
- `addons/mpq_extractor/loaders/m2_builder.gd` - сборка статического M2 mesh/material.
|
- `addons/mpq_extractor/loaders/m2_builder.gd` - сборка статического M2 mesh/material.
|
||||||
@@ -393,6 +401,12 @@ Known limits:
|
|||||||
- `WMOBuilder` строит group meshes;
|
- `WMOBuilder` строит group meshes;
|
||||||
- runtime registry дедуплицирует WMO по `unique_id`;
|
- runtime registry дедуплицирует WMO по `unique_id`;
|
||||||
- WMO на границе ADT не дублируются;
|
- WMO на границе ADT не дублируются;
|
||||||
|
- `WmoPlacementRegistry` owns only placement reference sets; the streamer keeps
|
||||||
|
the parallel key-to-Node map and performs final-release cancellation/free;
|
||||||
|
- `WmoRenderBuildStepPlanner` selects one mesh or MultiMesh group and its next
|
||||||
|
cursors; the streamer still materializes Nodes and consumes the frame permit;
|
||||||
|
- `WmoRenderBuildQueue` owns pending typed jobs/FIFO keys and releases references
|
||||||
|
on cancel/reset; the streamer still validates and frees engine objects;
|
||||||
- большие WMO не должны инстанцироваться как тяжелые `.tscn` во время движения;
|
- большие WMO не должны инстанцироваться как тяжелые `.tscn` во время движения;
|
||||||
- добавлен lightweight render-cache path для WMO groups;
|
- добавлен lightweight render-cache path для WMO groups;
|
||||||
- `wmo_render_group_ops_per_tick` ограничивает подключение WMO groups по кадрам;
|
- `wmo_render_group_ops_per_tick` ограничивает подключение WMO groups по кадрам;
|
||||||
@@ -830,6 +844,296 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
|||||||
- 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.
|
- 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.
|
- 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 Terrain Chunk LOD Planner Extraction
|
||||||
|
|
||||||
|
- `TerrainChunkLodPlanner` now owns the pure mapping from populated parsed ADT
|
||||||
|
chunk indices to desired terrain LOD 0/1/2.
|
||||||
|
- `TerrainChunkLodPolicy` snapshots the existing enabled flag, three raw chunk
|
||||||
|
radii and chunk size once per streaming-target application.
|
||||||
|
- Horizontal XZ distance to the chunk center, inclusive squared thresholds,
|
||||||
|
sparse-index omission and historical negative-radius squaring are unchanged.
|
||||||
|
- `StreamingWorldLoader` still owns tile/chunk state, queues, operation budgets,
|
||||||
|
creation/removal, Mesh/Node/RID finalization and tile-LOD fallback.
|
||||||
|
- Cache formats, quality presets and visible behavior are unchanged. The
|
||||||
|
scene-free contract is formula evidence only; asset-backed p95/p99 and visual
|
||||||
|
acceptance remain required.
|
||||||
|
|
||||||
|
## 2026-07-16 Terrain Chunk Geometry Queue Planner Extraction
|
||||||
|
|
||||||
|
- `TerrainChunkGeometryQueuePlanner` now compares current and desired chunk state
|
||||||
|
and returns fresh chunk create/remove request arrays.
|
||||||
|
- LOD mismatches and absent chunks retain the existing request shape; creates
|
||||||
|
remain sorted by horizontal distance from chunk origin to streaming focus.
|
||||||
|
- Desired tile LOD still removes every current chunk, while sparse parsed chunk
|
||||||
|
records retain the existing tile-origin distance fallback.
|
||||||
|
- `StreamingWorldLoader` still owns queue storage/drains, removal-first execution,
|
||||||
|
the shared `chunk_geometry` budget lane and every Mesh/Node/RID side effect.
|
||||||
|
- Cache formats, quality profiles and visual rules are unchanged. Asset-backed
|
||||||
|
p95/p99 and visual acceptance remain required.
|
||||||
|
|
||||||
|
## 2026-07-16 M2 Unique Placement Registry Extraction
|
||||||
|
|
||||||
|
- `M2UniquePlacementRegistry` now owns positive ADT `MDDF.uniqueId` reservations
|
||||||
|
across streamed tiles using the existing `uid:<decimal>` keys.
|
||||||
|
- Missing/zero/negative IDs remain untracked; duplicates inside one reservation
|
||||||
|
call are suppressed and another tile records one skipped key until owner release.
|
||||||
|
- Owner release still lets the loader find eligible waiting tiles and enqueue the
|
||||||
|
existing detail retry path. First ownership remains tile-processing-order based.
|
||||||
|
- `StreamingWorldLoader` still owns tile state, retry queues, grouping/build tasks,
|
||||||
|
M2 caches, MultiMesh/Node/Mesh/material/RID creation and all operation budgets.
|
||||||
|
- M2/ADT cache formats, transforms, profiles and visible rules are unchanged.
|
||||||
|
Asset-backed cross-boundary p95/p99 and visual acceptance remain required.
|
||||||
|
|
||||||
|
## 2026-07-16 M2 Placement Transform Resolver Extraction
|
||||||
|
|
||||||
|
- `M2PlacementTransformResolver` now owns the pure ADT M2 basis and local
|
||||||
|
origin-compensation formulas used by worker grouping, placeholders and direct
|
||||||
|
instance creation.
|
||||||
|
- Regular models retain `Basis.from_euler`; `ElwynnCliffRock01/02` retain their
|
||||||
|
narrow shell correction; `NewWaterfall` and `ElwynnTallWaterfall01` retain
|
||||||
|
calibrated world yaw, local fall-axis twist and tall-sheet anchor compensation.
|
||||||
|
- Path separator/case/MDX handling, constants and caller-specific final scale
|
||||||
|
clamps are unchanged. The resolver owns no Node, task, cache or RID.
|
||||||
|
- Cache formats, placement positions and visible rules are unchanged by the
|
||||||
|
extraction. Asset-backed visual recheck and general placement parity remain gaps.
|
||||||
|
|
||||||
|
## 2026-07-16 M2 Placement Grouper Extraction
|
||||||
|
|
||||||
|
- `M2PlacementGrouper` now owns pure worker-side validation, historical path
|
||||||
|
normalization and ordered tile-local transform grouping of ADT M2 placements.
|
||||||
|
- Invalid records, backslash replacement, lowercase `.mdx/.mdl` conversion,
|
||||||
|
first-seen ordering and the `0.0001` scale clamp are unchanged.
|
||||||
|
- The loader retains tasks, mutex/result queues, stale-result checks, tile/build
|
||||||
|
state, caches and all Node/MultiMesh/Mesh/material/RID finalization.
|
||||||
|
- Cache formats, batching, profiles and visible rules are unchanged. Asset-backed
|
||||||
|
traversal p95/p99, visual recheck and spatial-cell grouping remain pending.
|
||||||
|
|
||||||
|
## 2026-07-17 M2 Build Batch Planner Extraction
|
||||||
|
|
||||||
|
- `M2BuildBatchPlanner` now owns pure static/animated batch-limit selection,
|
||||||
|
remaining-count capping and next-offset/group-completion calculation.
|
||||||
|
- Existing animated/static limits still clamp to at least one; resource readiness,
|
||||||
|
queue rotation, serial progression and `M2_BUILD` permit consumption are unchanged.
|
||||||
|
- The loader retains build jobs/queues, tile cancellation, animation/mesh caches,
|
||||||
|
retries and all Node/MultiMesh/Mesh/material/RID finalization.
|
||||||
|
- Cache formats, quality profiles, batching output and visible rules are unchanged.
|
||||||
|
Asset-backed p95/p99 and spatial-cell batching evidence remain pending.
|
||||||
|
|
||||||
|
## 2026-07-17 WMO Placement Resolver Extraction
|
||||||
|
|
||||||
|
- `WmoPlacementResolver` now owns lowercase/slash cache-key normalization,
|
||||||
|
positive MODF `uid:<decimal>` identity with the legacy tile/index fallback and
|
||||||
|
world `Transform3D` composition.
|
||||||
|
- Lightweight render roots, cached scenes and live prototypes now share the same
|
||||||
|
position/Euler/unclamped-scale formula.
|
||||||
|
- The loader retains registry refs, jobs/queues, cache/load state, resource
|
||||||
|
fallback, cancellation and all Node/Mesh/MultiMesh/material/RID ownership.
|
||||||
|
- Cache formats, placement values, profiles and visible rules are unchanged.
|
||||||
|
Asset-backed placement/p95/p99 and general WMO parity remain pending.
|
||||||
|
|
||||||
|
## 2026-07-17 WMO Render Resource Cache State Extraction
|
||||||
|
|
||||||
|
- `WmoRenderResourceCacheState` now owns validated lightweight-WMO render
|
||||||
|
Resources, negative entries and normalized-path to pending-cache-path records.
|
||||||
|
- `StreamingWorldLoader` still constructs cache paths, calls `ResourceLoader`,
|
||||||
|
polls requests and validates `WMOStreamingResource` script identity plus
|
||||||
|
`FORMAT_VERSION` before completing cache state.
|
||||||
|
- Map reset and orderly request draining clear pending/negative state while
|
||||||
|
retaining accepted Resources; final runtime cache release clears all state.
|
||||||
|
- Missing render-cache files still are not negatively cached, preserving retry
|
||||||
|
and cached-scene/live fallback behavior. Formats, profiles and visuals are unchanged.
|
||||||
|
- Asset-backed corrupt-cache, traversal/leak p95/p99 and paired fidelity evidence
|
||||||
|
remain pending.
|
||||||
|
|
||||||
|
## 2026-07-17 WMO Scene Resource Cache State Extraction
|
||||||
|
|
||||||
|
- `WmoSceneResourceCacheState` now owns validated cached-WMO PackedScenes,
|
||||||
|
negative entries and normalized-path to pending-`.tscn` records.
|
||||||
|
- `StreamingWorldLoader` still checks file existence and
|
||||||
|
`wmo_max_runtime_scene_mb`, calls `ResourceLoader`, instantiates a validation
|
||||||
|
probe, checks WMOBuilder cache metadata and frees the probe before adoption.
|
||||||
|
- Missing files, oversize scenes, request errors, load failures and stale scenes
|
||||||
|
retain their prior negative-cache and live-prototype fallback behavior.
|
||||||
|
- Map reset clears pending/negative state while retaining accepted scenes; final
|
||||||
|
shutdown releases all scene state. Formats, profiles and visuals are unchanged.
|
||||||
|
- Asset-backed oversize/stale fixtures, traversal/leak p95/p99 and paired fidelity
|
||||||
|
evidence remain pending.
|
||||||
|
|
||||||
|
## 2026-07-17 ADT Water Load Pipeline State Extraction
|
||||||
|
|
||||||
|
- `AdtWaterLoadPipelineState` now owns ADT water pending FIFO/deduplication,
|
||||||
|
active WorkerThreadPool task IDs and the mutex-protected parsed-result mailbox.
|
||||||
|
- `StreamingWorldLoader` still selects concurrency, starts/waits tasks, invokes
|
||||||
|
`ADTLoader`, consumes `WATER_FINALIZE` permits, rejects stale tile/path/root
|
||||||
|
results and builds/attaches water Nodes through `ADTBuilder` on the main thread.
|
||||||
|
- Tile release still cancels only pending water requests; active work completes
|
||||||
|
and its result is discarded when tile ownership is stale. Shutdown waits task
|
||||||
|
IDs before clearing state; reset does not interrupt workers.
|
||||||
|
- Pending-plus-active `water` metrics, parsing, formats, materials, profiles and
|
||||||
|
visible behavior are unchanged.
|
||||||
|
- Asset-backed MH2O/MCLQ fidelity, long-traversal leak/p95/p99 and paired capture
|
||||||
|
evidence remain pending.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 2026-07-16 World Entity Presentation Facade
|
||||||
|
|
||||||
|
- `EntityPresentationSnapshot` contract version 1 combines session `EntityId`,
|
||||||
|
typed Godot world position, visual PackedScene path, finite yaw/scale and visibility.
|
||||||
|
- `WorldRenderFacade.present_entity()` and `remove_entity()` delegate to an explicit
|
||||||
|
scene-owned `WorldEntityPresenter`; both runtime scenes compose that service.
|
||||||
|
- The presenter owns only visual subtrees. Same-path snapshots update transform/
|
||||||
|
visibility, changed paths replace after successful instantiation, and removal
|
||||||
|
detaches plus queues the owned root for deletion.
|
||||||
|
- The local player, authoritative state, GUID mapping and asset/display selection
|
||||||
|
remain outside this service. Runtime emits no entity commands yet, so baseline
|
||||||
|
output is unchanged.
|
||||||
|
- PackedScene loading is currently synchronous on create/replace and is a documented
|
||||||
|
prototype limitation, not an accepted network-scale frame-critical path.
|
||||||
|
|
||||||
|
## 2026-07-16 Terrain Quality Mesh Cache Extraction
|
||||||
|
|
||||||
|
- `TerrainQualityMeshCache` now owns full-quality terrain revisit Mesh references,
|
||||||
|
tile-key LRU order and source admission outside `StreamingWorldLoader`.
|
||||||
|
- The historical rules are unchanged: successful restore touches newest; store
|
||||||
|
trims oldest to `terrain_quality_mesh_cache_limit`; zero/negative capacity keeps
|
||||||
|
no entries; control-splat/splat sources are never retained in this revisit cache.
|
||||||
|
- The loader keeps three narrow adapters: restore into tile state, store after a
|
||||||
|
full-quality result and clear during existing world reset/shutdown.
|
||||||
|
- ADT parsing, quality tasks/results, tile state, cache format versions, material/
|
||||||
|
Node/RID finalization, budgets and visible terrain behavior remain loader-owned.
|
||||||
|
|
||||||
## Practical Rule For Future 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.
|
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.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# M00-QAR-ANIMATION-SKY-EVIDENCE-001 — Animation and sky evidence
|
# 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_CLAIM:M00-QAR-ANIMATION-SKY-EVIDENCE-001:sindo-main-codex:2026-07-15 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-ANIMATION-SKY-EVIDENCE-001:d1df794 -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# M00-QAR-CAMERA-FRAMING-REFINEMENT-001 — Goldshire pitch/FOV refinement
|
# 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_CLAIM:M00-QAR-CAMERA-FRAMING-REFINEMENT-001:sindo-main-codex:2026-07-14 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-CAMERA-FRAMING-REFINEMENT-001:3172f41 -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# M00-QAR-CAMERA-OCCLUDERS-001 — Camera occluder diagnostic
|
# M00-QAR-CAMERA-OCCLUDERS-001 — Camera occluder diagnostic
|
||||||
|
|
||||||
<!-- OPENWC_CLAIM:M00-QAR-CAMERA-OCCLUDERS-001:sindo-main-codex:2026-07-13 -->
|
<!-- OPENWC_CLAIM:M00-QAR-CAMERA-OCCLUDERS-001:sindo-main-codex:2026-07-13 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-CAMERA-OCCLUDERS-001:9d34a47 -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# M00-QAR-CAMERA-POSE-EVIDENCE-001 — Goldshire camera pose evidence
|
# 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_CLAIM:M00-QAR-CAMERA-POSE-EVIDENCE-001:sindo-main-codex:2026-07-14 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-CAMERA-POSE-EVIDENCE-001:eca76c5 -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# M00-QAR-CAMERA-POSE-SWEEP-001 — Empirical camera pose sweep
|
# 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_CLAIM:M00-QAR-CAMERA-POSE-SWEEP-001:sindo-main-codex:2026-07-14 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-CAMERA-POSE-SWEEP-001:657a1d8 -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# M00-QAR-CAPTURE-SHUTDOWN-001 — Capture shutdown drain
|
# M00-QAR-CAPTURE-SHUTDOWN-001 — Capture shutdown drain
|
||||||
|
|
||||||
<!-- OPENWC_CLAIM:M00-QAR-CAPTURE-SHUTDOWN-001:sindo-main-codex:2026-07-14 -->
|
<!-- OPENWC_CLAIM:M00-QAR-CAPTURE-SHUTDOWN-001:sindo-main-codex:2026-07-14 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-CAPTURE-SHUTDOWN-001:8d4641a -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# M00-QAR-COORD-CALIBRATION-001 — Renderer coordinate calibration
|
# M00-QAR-COORD-CALIBRATION-001 — Renderer coordinate calibration
|
||||||
|
|
||||||
<!-- OPENWC_CLAIM:M00-QAR-COORD-CALIBRATION-001:sindo-main-codex:2026-07-13 -->
|
<!-- OPENWC_CLAIM:M00-QAR-COORD-CALIBRATION-001:sindo-main-codex:2026-07-13 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-COORD-CALIBRATION-001:dfc1031 -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# M00-QAR-FINAL-PAIRED-EVIDENCE-001 — Final paired evidence audit
|
# 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_CLAIM:M00-QAR-FINAL-PAIRED-EVIDENCE-001:sindo-main-codex:2026-07-14 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-FINAL-PAIRED-EVIDENCE-001:239cb24 -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# M00-QAR-FOV-SWEEP-001 — Empirical camera FOV sweep
|
# M00-QAR-FOV-SWEEP-001 — Empirical camera FOV sweep
|
||||||
|
|
||||||
<!-- OPENWC_CLAIM:M00-QAR-FOV-SWEEP-001:sindo-main-codex:2026-07-14 -->
|
<!-- OPENWC_CLAIM:M00-QAR-FOV-SWEEP-001:sindo-main-codex:2026-07-14 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-FOV-SWEEP-001:f548507 -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# M00-QAR-GUI-RID-SHUTDOWN-001 — GUI capture RID shutdown
|
# 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_CLAIM:M00-QAR-GUI-RID-SHUTDOWN-001:sindo-main-codex:2026-07-15 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-GUI-RID-SHUTDOWN-001:f19c0c4 -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
# M00-QAR-LANDMARK-REGION-DIFF-001 — Landmark-region visual diff
|
# 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_CLAIM:M00-QAR-LANDMARK-REGION-DIFF-001:sindo-main-codex:2026-07-14 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-LANDMARK-REGION-DIFF-001:3d90c6d -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# M00-QAR-TERRAIN-HEIGHT-001 — Terrain height diagnostic
|
# M00-QAR-TERRAIN-HEIGHT-001 — Terrain height diagnostic
|
||||||
|
|
||||||
<!-- OPENWC_CLAIM:M00-QAR-TERRAIN-HEIGHT-001:sindo-main-codex:2026-07-13 -->
|
<!-- OPENWC_CLAIM:M00-QAR-TERRAIN-HEIGHT-001:sindo-main-codex:2026-07-13 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-TERRAIN-HEIGHT-001:f8538ba -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# M00-QAR-TILE-OWNERSHIP-001 — Waterfall terrain tile ownership
|
# M00-QAR-TILE-OWNERSHIP-001 — Waterfall terrain tile ownership
|
||||||
|
|
||||||
<!-- OPENWC_CLAIM:M00-QAR-TILE-OWNERSHIP-001:sindo-main-codex:2026-07-13 -->
|
<!-- OPENWC_CLAIM:M00-QAR-TILE-OWNERSHIP-001:sindo-main-codex:2026-07-13 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-TILE-OWNERSHIP-001:2306200 -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# M00-QAR-VISUAL-DIFF-001 — Renderer checkpoint visual diff
|
# M00-QAR-VISUAL-DIFF-001 — Renderer checkpoint visual diff
|
||||||
|
|
||||||
<!-- OPENWC_CLAIM:M00-QAR-VISUAL-DIFF-001:sindo-main-codex:2026-07-13 -->
|
<!-- OPENWC_CLAIM:M00-QAR-VISUAL-DIFF-001:sindo-main-codex:2026-07-13 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-VISUAL-DIFF-001:19df5b7 -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# M00-QAR-WMO-READINESS-001 — Goldshire WMO readiness
|
# M00-QAR-WMO-READINESS-001 — Goldshire WMO readiness
|
||||||
|
|
||||||
<!-- OPENWC_CLAIM:M00-QAR-WMO-READINESS-001:sindo-main-codex:2026-07-14 -->
|
<!-- OPENWC_CLAIM:M00-QAR-WMO-READINESS-001:sindo-main-codex:2026-07-14 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-WMO-READINESS-001:5092995 -->
|
||||||
|
|
||||||
## Ownership
|
## Ownership
|
||||||
|
|
||||||
|
|||||||
@@ -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,115 @@
|
|||||||
|
# M03-RND-ADT-WATER-LOAD-PIPELINE-001 — ADT water load pipeline state
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-ADT-WATER-LOAD-PIPELINE-001:sindo-main-codex:2026-07-20 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M03
|
||||||
|
- Program: RND
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m03-adt-water-load-pipeline`
|
||||||
|
- Lease expires UTC: 2026-07-20
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Extract ADT water pending-request FIFO/deduplication, active worker task IDs and
|
||||||
|
worker-safe result mailbox from the loader while preserving loader-owned parse,
|
||||||
|
budget, stale-result and scene finalization behavior.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Instantiate `ADTLoader`, start/wait WorkerThreadPool tasks or parse ADT bytes.
|
||||||
|
- Consume `WATER_FINALIZE` permits or choose concurrency/profile limits.
|
||||||
|
- Build/attach/free water Nodes, materials, Meshes or RIDs.
|
||||||
|
- Change tile/path stale checks, empty-data behavior or `water_loaded` timing.
|
||||||
|
- Change MH2O/MCLQ parsing or claim liquid fidelity/performance parity.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/liquid/adt_water_load_pipeline_state.gd`,
|
||||||
|
`src/tools/verify_adt_water_load_pipeline_state.gd`,
|
||||||
|
`docs/modules/adt-water-load-pipeline-state.md`, this claim
|
||||||
|
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
|
||||||
|
`src/tools/verify_render_runtime_cache_shutdown.gd`,
|
||||||
|
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
|
||||||
|
`targets/03-renderer-facade.md`
|
||||||
|
- Generated/ignored: `.godot`, native DLL, generated ADT resources, caches and
|
||||||
|
proprietary renderer corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- Pending requests preserve FIFO and tile-key deduplication.
|
||||||
|
- Tile release removes all matching pending entries but does not cancel active work.
|
||||||
|
- Active task IDs are loader-created opaque integers keyed by tile.
|
||||||
|
- Worker publication and main-thread result pop/clear are mutex protected.
|
||||||
|
- Result order and Dictionary payload identity remain unchanged.
|
||||||
|
- Clear releases bookkeeping only; loader waits tasks at orderly shutdown.
|
||||||
|
- Water parsing, coordinate, material, profile and visual rules are unchanged.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: current master `4387c66`; accepted scheduler and renderer lifecycle
|
||||||
|
- Blocks: further liquid parse/finalization decomposition
|
||||||
|
- External state: none; verification uses synthetic task IDs and payloads
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: dedicated FIFO/dedupe/task/result/cancel/clear/thread/source/timing
|
||||||
|
verifier; shutdown, water/material and adjacent renderer/streaming/coordinate/
|
||||||
|
documentation/coordination gates
|
||||||
|
- Fixtures: synthetic tile/path requests, task IDs and water dictionaries
|
||||||
|
- Fidelity evidence: exact current queue/task/result transitions preserved
|
||||||
|
- Performance budget: 100 cycles over 256 requests/results under 1 second
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline public API docs
|
||||||
|
- Module specification with inputs/outputs, ownership and source map
|
||||||
|
- Data-flow, lifecycle/state, worker sequence and dependency diagrams
|
||||||
|
- World-renderer, module registry and RENDER status updates
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important names introduced: `AdtWaterLoadPipelineState`
|
||||||
|
- Simplest considered solution: one RefCounted with FIFO/dedupe/task maps and one mutex mailbox
|
||||||
|
- Rejected complexity/abstractions: generic async scheduler, signals, callbacks,
|
||||||
|
WorkerThreadPool wrapper, parser/material ownership or multi-lane framework
|
||||||
|
- Unavoidable complexity and justification: result mailbox crosses worker/main threads
|
||||||
|
- Measured optimization evidence: bounded synthetic lifecycle loop
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: accepted
|
||||||
|
- Done: pipeline state implemented; loader enqueue/start/publish/drain/release/
|
||||||
|
reset/shutdown/metrics adapters migrated; required documentation added
|
||||||
|
- Next: continue liquid parse/finalization extraction or another remaining M03
|
||||||
|
service boundary
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-ADT-WATER-LOAD-PIPELINE-001:f470775 -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `b7c036f`
|
||||||
|
- Results: water pipeline PASS (`cases=12`, `iterations=100`,
|
||||||
|
`elapsed_ms=86.170`; initial direct cold run `94.576ms`); real Thread result
|
||||||
|
publication, FIFO/dedupe/task/cancel/clear/source contracts PASS; WMO caches/
|
||||||
|
queue/planner/registry/resolver, materials, shutdown and 18 adjacent M2/terrain/
|
||||||
|
facade/scheduler/streaming/coordinate regressions PASS; documentation and
|
||||||
|
coordination gates PASS; checkpoint dry-run PASS `7/7`.
|
||||||
|
- Remaining risks: clear does not interrupt active WorkerThreadPool work and a
|
||||||
|
post-reset publication remains stale until drained/cleared, matching prior
|
||||||
|
behavior; payload Dictionaries require no mutation after publication; parser,
|
||||||
|
main-thread water materialization and asset-backed MH2O/MCLQ traversal/leak/
|
||||||
|
p95/p99/original-client evidence remain pending.
|
||||||
|
- Documentation updated: inline public API; new
|
||||||
|
`docs/modules/adt-water-load-pipeline-state.md`; module registry, world renderer
|
||||||
|
and `RENDER.md`; data-flow/state/sequence/dependency diagrams, ownership,
|
||||||
|
threading, failure/recovery, profiles, performance limit and source map included.
|
||||||
|
- Integration: merged as `f470775`; post-merge water pipeline (`cases=12`,
|
||||||
|
`iterations=100`, `elapsed_ms=86.935`), shutdown, renderer materials, facade,
|
||||||
|
internal-access (`private_symbols=36`), `7/7` manifest, documentation and
|
||||||
|
coordination gates passed.
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-ADT-WATER-LOAD-PIPELINE-001:b7c036f -->
|
||||||
@@ -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,130 @@
|
|||||||
|
# M03-RND-FACADE-ENTITY-001 — World entity presentation commands
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-FACADE-ENTITY-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-entity`
|
||||||
|
- Lease expires UTC: 2026-07-18
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Add immutable, versioned world-entity presentation input and stable facade
|
||||||
|
commands for idempotent present/update and removal, backed by a renderer-owned
|
||||||
|
scene service with explicit entity subtree ownership.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Define authoritative entity/gameplay/network state or allocate/map identities.
|
||||||
|
- Move the local player controller/presenters behind the world-entity service.
|
||||||
|
- Implement build-12340 display IDs, equipment, animation, effects or visibility rules.
|
||||||
|
- Add background loading, pooling, MultiMesh conversion or a generic ECS/framework.
|
||||||
|
- Change streamer queues, caches, world assets, baseline visuals or target status.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/entities/entity_presentation_snapshot.gd`,
|
||||||
|
`src/render/entities/world_entity_presenter.gd`,
|
||||||
|
`src/tools/verify_world_entity_presentation.gd`,
|
||||||
|
`src/tests/scenes/world_entity_visual_replacement_fixture.tscn`,
|
||||||
|
`docs/modules/world-entity-presentation.md`, this claim
|
||||||
|
- Shared/hotspots: `src/render/world_render_facade.gd`, both runtime streaming
|
||||||
|
scenes, `src/tools/verify_world_render_facade.gd`, `docs/modules/world-renderer.md`,
|
||||||
|
`docs/modules/README.md`, `RENDER.md`, `targets/03-renderer-facade.md`
|
||||||
|
- Reused unchanged: `EntityId`, `GodotWorldPosition`, existing asset-free
|
||||||
|
character presentation model fixture
|
||||||
|
- Generated/ignored: `.godot`, native DLL, generated resources, caches and
|
||||||
|
proprietary renderer corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- Contract version: `EntityPresentationSnapshot.CONTRACT_VERSION = 1`
|
||||||
|
- Snapshot: valid session-local `EntityId`, typed Godot world position, visual
|
||||||
|
PackedScene path, finite yaw radians, positive finite uniform scale and visibility
|
||||||
|
- Public API: `WorldRenderFacade.present_entity`, `remove_entity`,
|
||||||
|
`entity_presentation_snapshot`
|
||||||
|
- Presenter behavior: present is idempotent by entity key; same visual updates
|
||||||
|
transform/visibility, changed visual replaces the owned subtree; remove is explicit
|
||||||
|
- Schema/cache/coordinate contracts: no persisted schema or cache change
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: integrated M03 environment facade master `a85e416`, M01 identity and
|
||||||
|
coordinate contracts
|
||||||
|
- Blocks: completion of the first M03 facade checklist item and later entity renderer extraction
|
||||||
|
- External state: none; tests use a repository-owned asset-free PackedScene fixture
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: cold snapshot contract; presenter lifecycle/ownership regression;
|
||||||
|
facade delegation and runtime wiring; identity/coordinate/internal-access;
|
||||||
|
renderer manifest/shutdown/planner/scheduler/focus and repository gates
|
||||||
|
- Fixtures: invalid identity/position/yaw/scale/path, spawn, idempotent update,
|
||||||
|
visual replacement, missing asset, removal, detached diagnostic snapshot and teardown
|
||||||
|
- Fidelity evidence: no runtime commands are produced yet, so existing scene output
|
||||||
|
and seven baseline plans remain unchanged
|
||||||
|
- Performance budget: constant-time entity lookup; synchronous resource load only
|
||||||
|
on first presentation/visual replacement and explicitly excluded from hot-path acceptance
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline public API docs: snapshot, facade commands and presenter lifecycle
|
||||||
|
- Module specification: new world-entity-presentation specification
|
||||||
|
- Data-flow diagram: read model through facade into owned visual subtree
|
||||||
|
- Sequence/state/dependency diagrams: present/update/replace/remove/failure/teardown
|
||||||
|
- Source map/status updates: module registry, world renderer and `RENDER.md`
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important names introduced: `EntityPresentationSnapshot`, `WorldEntityPresenter`,
|
||||||
|
`present_entity`, `remove_entity`
|
||||||
|
- Simplest considered solution: one immutable full visual snapshot and one
|
||||||
|
dictionary-owned Node3D presenter below an explicit scene service
|
||||||
|
- Rejected complexity/abstractions: ECS, event bus, command hierarchy, factory
|
||||||
|
registry, pooling, async loader framework or duplicate identity registry
|
||||||
|
- Unavoidable complexity and justification: renderer must own and deterministically
|
||||||
|
replace/free visual subtrees while authoritative state remains external
|
||||||
|
- Measured optimization evidence: no optimization claim; lifecycle is asset-free tested
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: ready-for-review
|
||||||
|
- Done: immutable contract-v1 snapshot, presenter ownership lifecycle, facade
|
||||||
|
commands, runtime composition, asset-free regressions and module documentation
|
||||||
|
- Next: M03 integrator reviews and merges `1807c3a`
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-FACADE-ENTITY-001:1807c3a -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-FACADE-ENTITY-001:606770c -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `1807c3a`
|
||||||
|
- Results: facade now exposes idempotent `present_entity`, explicit
|
||||||
|
`remove_entity` and detached diagnostics. Both runtime scenes compose a
|
||||||
|
`WorldEntityPresenter` that owns one replaceable visual subtree per valid
|
||||||
|
session `EntityId`; local player and authoritative state remain separate.
|
||||||
|
- Verification: entity `snapshot_cases=8 lifecycle_cases=14 runtime_scenes=2`;
|
||||||
|
facade `delegation=9 entity_contract=6`; identities `5/5/6/5`; character
|
||||||
|
presentation `9/10/3/1`; coordinate boundary `files=119`; internal access
|
||||||
|
`43/7/9/1`; environment/ground/terrain, manifest `7/7`, shutdown, scheduler
|
||||||
|
(`20000` iterations), planner (`250` iterations), focus, editor parse, dry-run,
|
||||||
|
coordination, documentation and diff gates passed.
|
||||||
|
- Fidelity: runtime produces no world-entity commands yet, so all seven baseline
|
||||||
|
plans and existing player/world output remain unchanged. No build-12340
|
||||||
|
display, equipment, animation, effects or visibility parity claim is added.
|
||||||
|
- External/local inputs: ignored native DLL and generated ADT scripts supported
|
||||||
|
editor/dry-run only. Missing proprietary ADT/DBC/character corpus produced the
|
||||||
|
expected degraded diagnostics.
|
||||||
|
- Remaining risks: PackedScene create/replace loads synchronously and is not an
|
||||||
|
accepted network-scale hot path; resource paths are adapter inputs rather than
|
||||||
|
stable content IDs; animation/equipment/effects and scale profiling remain open.
|
||||||
|
- Documentation updated: new world-entity-presentation API/input-output/data-flow/
|
||||||
|
lifecycle/sequence/ownership/failure/configuration/migration/diagnostic/source-map
|
||||||
|
spec; world renderer, module registry and `RENDER.md` updated.
|
||||||
|
- Integration: accepted in master merge `606770c`; post-merge entity/facade,
|
||||||
|
character, internal-access, manifest, shutdown, scheduler and planner gates passed.
|
||||||
@@ -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,110 @@
|
|||||||
|
# M03-RND-M2-BUILD-BATCH-PLANNER-001 — M2 build batch planner
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-M2-BUILD-BATCH-PLANNER-001:sindo-main-codex:2026-07-20 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M03
|
||||||
|
- Program: RND
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m03-m2-build-batch-planner`
|
||||||
|
- Lease expires UTC: 2026-07-20
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Extract pure static/animated M2 build batch sizing and cursor progression from
|
||||||
|
the loader build-job loop into a scene-free planner.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Move build-job/queue ownership, tile eligibility or cancellation.
|
||||||
|
- Move animation/mesh requests, cache lookup, queue rotation or retry behavior.
|
||||||
|
- Create Nodes, MultiMesh, Mesh, materials or RIDs.
|
||||||
|
- Change render-budget permit consumption or per-profile batch limits.
|
||||||
|
- Introduce spatial-cell batching or claim asset-backed performance parity.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/m2/m2_build_batch_planner.gd`,
|
||||||
|
`src/tools/verify_m2_build_batch_planner.gd`,
|
||||||
|
`docs/modules/m2-build-batch-planner.md`, this claim
|
||||||
|
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
|
||||||
|
`docs/modules/m2-placement-grouper.md`, `docs/modules/world-renderer.md`,
|
||||||
|
`docs/modules/README.md`, `RENDER.md`, `targets/03-renderer-facade.md`
|
||||||
|
- Generated/ignored: `.godot`, native DLL, generated ADT resources, caches and
|
||||||
|
proprietary renderer corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- Input: transform count, current offset, animated/static path selector and raw
|
||||||
|
animated/static batch limits
|
||||||
|
- Output: fresh plan Dictionary with effective batch size, batch count, next
|
||||||
|
offset and group-complete flag
|
||||||
|
- Selected batch limit clamps to at least one
|
||||||
|
- Batch count clamps to remaining non-negative transforms
|
||||||
|
- Group completes when no batch can be consumed or offset plus batch reaches end
|
||||||
|
- Queue/cache/schema/coordinate/profile values: unchanged
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: current master `9d74d99`; grouped transform arrays from
|
||||||
|
`M2PlacementGrouper`
|
||||||
|
- Blocks: broader M2 build-job state extraction
|
||||||
|
- External state: none; tests use scalar synthetic counts/limits
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: dedicated static/animated/limit/remaining/complete/empty/offset and
|
||||||
|
bounded timing verifier; loader source boundary; grouper/transform/registry plus
|
||||||
|
facade/internal-access/manifest/shutdown/documentation/coordination gates
|
||||||
|
- Fidelity evidence: exact batch/count/cursor formulas moved unchanged
|
||||||
|
- Performance budget: constant-time scalar/container calculation; no Node,
|
||||||
|
Resource, RID, task, queue, mutex, cache or file access
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline planner API documentation
|
||||||
|
- New module spec with inputs/outputs, data-flow, lifecycle, ownership, failure,
|
||||||
|
performance, fidelity limitations and source map
|
||||||
|
- Grouper, renderer/module registry and `RENDER.md` updates
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important name: `M2BuildBatchPlanner`
|
||||||
|
- Simplest solution: one stateless `plan_batch` query with a small detached result
|
||||||
|
- Rejected complexity: job classes, state machine framework, callbacks, signals,
|
||||||
|
Resources, strategies or scheduler integration
|
||||||
|
- Unavoidable complexity: loader retains raw build-job Dictionaries
|
||||||
|
- Measured optimization evidence: bounded synthetic planning loop
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: accepted
|
||||||
|
- Done: planner, loader adapter, regressions and documentation verified
|
||||||
|
- Next: M2 queue/resource state extraction and spatial-cell evidence
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-BUILD-BATCH-PLANNER-001:be6db7f -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-BUILD-BATCH-PLANNER-001:3f84717 -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `be6db7f` (`refactor(M03): extract M2 build batch planner`)
|
||||||
|
- Results: dedicated planner PASS (`cases=8`, `iterations=20000`,
|
||||||
|
`elapsed_ms=13.464`); grouper, transform resolver, M2 registry/dedupe, terrain
|
||||||
|
cache/LOD/queue, facade, internal-access, manifest, shutdown, scheduler,
|
||||||
|
streaming planner/focus, terrain-query and coordinate-boundary suites PASS;
|
||||||
|
editor import parsed with only expected missing private corpus diagnostics;
|
||||||
|
documentation PASS (`module_specs=18`, `required_files=7`); coordination PASS
|
||||||
|
with 23 pre-existing expired M00/M01 warnings; checkpoint dry-run planned `7/7`.
|
||||||
|
- Remaining risks: no private asset-backed p95/p99 or visual comparison; result
|
||||||
|
and loader job remain Dictionaries; resource/queue transitions and spatial-cell
|
||||||
|
batching are still loader-owned; negative offset behavior remains permissive.
|
||||||
|
- Documentation updated: inline public API; new
|
||||||
|
`docs/modules/m2-build-batch-planner.md`; placement grouper module spec,
|
||||||
|
renderer module registry, `docs/modules/world-renderer.md` and `RENDER.md`.
|
||||||
|
- Integration: accepted in master merge `3f84717`; post-merge planner
|
||||||
|
(`cases=8`, `iterations=20000`, `elapsed_ms=14.532`), grouper, transform
|
||||||
|
resolver, registry, internal-access, facade, manifest, documentation and
|
||||||
|
coordination gates passed.
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
# M03-RND-M2-PLACEMENT-GROUPER-001 — M2 placement grouper
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-M2-PLACEMENT-GROUPER-001:sindo-main-codex:2026-07-19 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M03
|
||||||
|
- Program: RND
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m03-m2-placement-grouper`
|
||||||
|
- Lease expires UTC: 2026-07-19
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Extract pure ADT M2 placement validation, path normalization and local-transform
|
||||||
|
grouping from the worker callback into a scene-free service.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Change placement ordering, coordinate conversion, transform formulas or clamps.
|
||||||
|
- Move WorkerThreadPool tasks, mutex/result queues, tile state or retry behavior.
|
||||||
|
- Move build jobs, animation/mesh loading, caches, Nodes, MultiMesh or RIDs.
|
||||||
|
- Generalize grouping policy, change batching or add a dependency.
|
||||||
|
- Claim build-12340 placement or visual parity beyond preserved formulas.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/m2/m2_placement_grouper.gd`,
|
||||||
|
`src/tools/verify_m2_placement_grouper.gd`,
|
||||||
|
`docs/modules/m2-placement-grouper.md`, this claim
|
||||||
|
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
|
||||||
|
`src/tools/verify_m2_placement_transform_resolver.gd`,
|
||||||
|
`docs/modules/m2-placement-transform-resolver.md`,
|
||||||
|
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
|
||||||
|
`targets/03-renderer-facade.md`
|
||||||
|
- Generated/ignored: `.godot`, native DLL, generated ADT resources, caches and
|
||||||
|
proprietary renderer corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- Input: tile-local origin, MMDX name table and raw ADT M2 placement array
|
||||||
|
- Output: fresh Dictionary keyed by historically normalized relative M2 path;
|
||||||
|
each value is a fresh ordered Array of tile-local `Transform3D` values
|
||||||
|
- Invalid variants, out-of-range name IDs and empty paths are skipped
|
||||||
|
- Backslashes become slashes; lowercase `.mdx/.mdl` suffixes become `.m2`
|
||||||
|
- Transform basis/origin delegates to `M2PlacementTransformResolver`; final basis
|
||||||
|
scale retains the grouping-path clamp `max(scale, 0.0001)`
|
||||||
|
- Cache/schema/coordinate versions: unchanged
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: current master `c34e538`, `M2PlacementTransformResolver`
|
||||||
|
- Blocks: broader M2 worker/build decomposition
|
||||||
|
- External state: none; tests use synthetic names and placement dictionaries
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: dedicated invalid/name/path/group-order/default/transform/scale and
|
||||||
|
bounded timing verifier; loader source boundary; resolver/registry/dedupe plus
|
||||||
|
facade/internal-access/manifest/shutdown/documentation/coordination gates
|
||||||
|
- Fidelity evidence: exact loop rules and transform composition moved unchanged
|
||||||
|
- Performance budget: linear over placement count with fresh output containers;
|
||||||
|
no Node, Resource, RID, task, mutex, cache or file access
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline grouper API documentation
|
||||||
|
- New module spec with inputs/outputs, data-flow, lifecycle, ownership, failure,
|
||||||
|
performance, fidelity limitations and source map
|
||||||
|
- Transform resolver, renderer/module registry and `RENDER.md` updates
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important name: `M2PlacementGrouper`
|
||||||
|
- Simplest solution: one stateless `group_placements` query
|
||||||
|
- Rejected complexity: group/result classes, strategy interfaces, callbacks,
|
||||||
|
Resources, policies or generic spatial batching framework
|
||||||
|
- Unavoidable complexity: raw ADT dictionaries remain at the parser adapter
|
||||||
|
- Measured optimization evidence: bounded synthetic grouping loop
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: accepted
|
||||||
|
- Done: pure grouper, worker adapter, regressions and documentation verified
|
||||||
|
- Next: M2 worker/build state extraction and spatial-cell evidence
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-PLACEMENT-GROUPER-001:9df32d3 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-PLACEMENT-GROUPER-001:f88bf97 -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `9df32d3` (`refactor(M03): extract M2 placement grouper`)
|
||||||
|
- Results: dedicated grouper PASS (`cases=9`, `iterations=100`,
|
||||||
|
`placements=256`, `elapsed_ms=67.736`); transform resolver PASS (`cases=9`,
|
||||||
|
`iterations=10000`, `elapsed_ms=28.571`); M2 registry/dedupe, terrain
|
||||||
|
cache/LOD/queue, facade, internal-access, manifest, shutdown, scheduler,
|
||||||
|
streaming planner/focus, terrain-query and coordinate-boundary suites PASS;
|
||||||
|
editor import parsed the project with only expected missing private corpus
|
||||||
|
diagnostics; documentation PASS (`module_specs=17`, `required_files=7`);
|
||||||
|
coordination PASS with 23 pre-existing expired M00/M01 warnings; checkpoint
|
||||||
|
dry-run planned `7/7`.
|
||||||
|
- Remaining risks: no private asset-backed traversal/visual comparison;
|
||||||
|
uppercase extension preservation is historical but inconsistent with the
|
||||||
|
resolver's case-insensitive exception matching; grouping remains model-path
|
||||||
|
based rather than spatial-cell based; worker/build state and p95/p99 remain.
|
||||||
|
- Documentation updated: inline public API; new
|
||||||
|
`docs/modules/m2-placement-grouper.md`; transform resolver module spec,
|
||||||
|
renderer module registry, `docs/modules/world-renderer.md` and `RENDER.md`.
|
||||||
|
- Integration: accepted in master merge `f88bf97`; post-merge grouper
|
||||||
|
(`cases=9`, `iterations=100`, `placements=256`, `elapsed_ms=67.104`),
|
||||||
|
transform resolver, registry, internal-access, facade, manifest,
|
||||||
|
documentation and coordination gates passed.
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# M03-RND-M2-PLACEMENT-TRANSFORM-001 — M2 placement transform resolver
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-M2-PLACEMENT-TRANSFORM-001:sindo-main-codex:2026-07-19 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M03
|
||||||
|
- Program: RND
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m03-m2-placement-transform`
|
||||||
|
- Lease expires UTC: 2026-07-19
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Extract pure ADT M2 placement basis/origin-offset calculation, including the
|
||||||
|
existing waterfall and Elwynn cliff-rock calibrations, into a scene-free service.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Change coordinate conversion, placement positions, rotations or scale clamps.
|
||||||
|
- Generalize model-specific corrections or add new allowlisted models.
|
||||||
|
- Move grouping/tasks, MultiMesh/Node/Mesh/material/RID creation or caches.
|
||||||
|
- Change M2 animation/material behavior, cache versions or renderer profiles.
|
||||||
|
- Claim build-12340 placement parity beyond existing fixtures.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/m2/m2_placement_transform_resolver.gd`,
|
||||||
|
`src/tools/verify_m2_placement_transform_resolver.gd`,
|
||||||
|
`docs/modules/m2-placement-transform-resolver.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 ADT resources, caches and
|
||||||
|
proprietary renderer corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- Input: ADT-loader Godot Euler rotation, normalized/original M2 relative path,
|
||||||
|
and raw placement scale where origin compensation is requested
|
||||||
|
- Output: `Basis` and local origin-offset `Vector3`; no retained state
|
||||||
|
- Regular models use `Basis.from_euler`; two cliff rocks retain narrow YZX-style
|
||||||
|
shell correction; two waterfall sheets retain yaw/twist/anchor compensation
|
||||||
|
- Scale clamps remain caller-owned for final basis and resolver-owned at `0.0001`
|
||||||
|
only for historical waterfall anchor compensation
|
||||||
|
- Cache/schema/coordinate versions: unchanged
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: current master `1b6c468`
|
||||||
|
- Blocks: pure M2 grouping extraction and broader M2 service decomposition
|
||||||
|
- External state: none; tests use synthetic rotations/paths and pinned constants
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: dedicated regular/path-normalization/cliffrock/waterfall/twist-anchor/
|
||||||
|
scale and bounded timing verifier; three loader call-site/source boundaries;
|
||||||
|
M2 unique/dedupe plus facade/terrain/internal-access/manifest/shutdown gates
|
||||||
|
- Fidelity evidence: exact formulas/constants and model allowlists moved unchanged
|
||||||
|
- Performance budget: allocation-free scalar/Basis math per placement aside from
|
||||||
|
path normalization; no Node, Resource, RID, task, cache or file access
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline resolver API documentation
|
||||||
|
- New module spec with inputs/outputs, data-flow, lifecycle, ownership, failure,
|
||||||
|
performance, fidelity limitations and source map
|
||||||
|
- World renderer/module registry/`RENDER.md` updates
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important name: `M2PlacementTransformResolver`
|
||||||
|
- Simplest solution: stateless service with basis and origin-offset queries
|
||||||
|
- Rejected complexity: transform strategy hierarchy, generic correction registry,
|
||||||
|
Resources, data-driven override format or callback injection
|
||||||
|
- Unavoidable complexity: four measured model-specific exceptions remain explicit
|
||||||
|
- Measured optimization evidence: bounded synthetic transform loop
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: accepted
|
||||||
|
- Done: resolver extracted, three call sites migrated, contracts and docs verified
|
||||||
|
- Next: pure M2 grouping extraction and broader M2 service decomposition
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-PLACEMENT-TRANSFORM-001:0a09d7b -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-PLACEMENT-TRANSFORM-001:396be5e -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `0a09d7b` (`refactor(M03): extract M2 placement transform resolver`)
|
||||||
|
- Results: dedicated formula/source/timing contract PASS (`cases=9`,
|
||||||
|
`iterations=10000`, `elapsed_ms=28.732`); cold editor parse PASS;
|
||||||
|
documentation gate PASS (`module_specs=16`, `required_files=7`);
|
||||||
|
coordination gate PASS with 23 pre-existing expired M00/M01 warnings; related
|
||||||
|
M2 unique/dedupe, terrain cache/LOD/queue, facade, internal-access, manifest,
|
||||||
|
shutdown, scheduler, streaming focus/planner and coordinate-boundary suites
|
||||||
|
PASS; renderer checkpoint dry-run PASS `7/7`.
|
||||||
|
- Remaining risks: no private asset-backed visual recheck in this package;
|
||||||
|
general ADT M2 placement parity and asset-backed p95/p99 remain pending.
|
||||||
|
- Documentation updated: inline public API; new
|
||||||
|
`docs/modules/m2-placement-transform-resolver.md`; renderer module registry,
|
||||||
|
`docs/modules/world-renderer.md` and `RENDER.md`.
|
||||||
|
- Integration: accepted in master merge `396be5e`; post-merge resolver
|
||||||
|
(`cases=9`, `iterations=10000`, `elapsed_ms=27.409`), M2 registry,
|
||||||
|
internal-access, facade, manifest, documentation and coordination gates passed.
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
# M03-RND-M2-UNIQUE-REGISTRY-001 — M2 unique placement registry
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-M2-UNIQUE-REGISTRY-001:sindo-main-codex:2026-07-19 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M03
|
||||||
|
- Program: RND
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m03-m2-unique-registry`
|
||||||
|
- Lease expires UTC: 2026-07-19
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Extract cross-tile positive `MDDF.uniqueId` reservation ownership from the
|
||||||
|
streamer into a scene-free `M2UniquePlacementRegistry`.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Move tile state, candidate requeue, group/build tasks or result queues.
|
||||||
|
- Move MultiMesh/Node/Mesh/material creation, animation or resource loading.
|
||||||
|
- Change unique-key format, first-owner semantics or unkeyed placement behavior.
|
||||||
|
- Change placement transforms, detail radius, cache versions or quality profiles.
|
||||||
|
- Add dependencies or mark the broad M2 service complete.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/m2/m2_unique_placement_registry.gd`,
|
||||||
|
`src/tools/verify_m2_unique_placement_registry.gd`,
|
||||||
|
`docs/modules/m2-unique-placement-registry.md`, this claim
|
||||||
|
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
|
||||||
|
`src/tools/verify_m2_unique_dedupe.gd`,
|
||||||
|
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
|
||||||
|
`targets/03-renderer-facade.md`
|
||||||
|
- Generated/ignored: `.godot`, native DLL, generated ADT resources, caches and
|
||||||
|
proprietary renderer corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- Input: tile key and raw ADT M2 placement dictionaries
|
||||||
|
- Reserve output: fresh filtered placements, owned unique keys and skipped keys
|
||||||
|
- Release input/output: tile key plus claimed keys; only current owner releases
|
||||||
|
- Positive IDs map to `uid:<decimal>`; zero/negative/missing IDs remain untracked
|
||||||
|
- Registry is session-memory only and clears on renderer world reset
|
||||||
|
- Queue/cache/schema/coordinate versions: unchanged
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: current master `57ae1b8`
|
||||||
|
- Blocks: broader M2 service extraction and dependency/performance acceptance
|
||||||
|
- External state: none; tests use synthetic placement dictionaries
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: dedicated unkeyed/duplicate/cross-tile/release/clear/detached-
|
||||||
|
diagnostic and bounded timing verifier; loader source boundary; M2 unique-id
|
||||||
|
smoke plus facade/terrain/internal-access/manifest/shutdown/scheduler gates
|
||||||
|
- Fidelity evidence: exact positive-ID key format and first-owner/retry behavior
|
||||||
|
- Performance budget: dictionary membership over placement count; no Node,
|
||||||
|
Resource, RID, task, file or persistent cache
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline registry API documentation
|
||||||
|
- New module spec with inputs/outputs, data-flow, lifecycle, sequence, ownership,
|
||||||
|
failure, recovery, performance and source map
|
||||||
|
- World renderer/module registry/`RENDER.md` updates
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important name: `M2UniquePlacementRegistry`
|
||||||
|
- Simplest solution: one private key-to-owner dictionary and explicit operations
|
||||||
|
- Rejected complexity: generic identity registry, signals, callbacks, Resources,
|
||||||
|
locks or ownership graph
|
||||||
|
- Unavoidable complexity: raw placement dictionaries remain at ADT adapter boundary
|
||||||
|
- Measured optimization evidence: bounded synthetic reserve/release verifier
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: accepted
|
||||||
|
- Done: stateful registry, loader adapters, migrated smoke, regressions and docs
|
||||||
|
- Next: asset-backed boundary traversal and further M2-service extraction later in M03
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-UNIQUE-REGISTRY-001:acb3a80 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-UNIQUE-REGISTRY-001:2342430 -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `acb3a80` (`refactor(M03): extract M2 unique placement registry`)
|
||||||
|
- Results: dedicated contract PASS (`cases=10`, `iterations=100`, `35.688 ms`);
|
||||||
|
historical `uid:11785` smoke PASS; terrain/facade/internal-access/manifest/
|
||||||
|
shutdown/scheduler/streaming/focus/terrain-query/coordinate regressions PASS;
|
||||||
|
internal inventory remains `42`; editor parse and documentation/coordination/
|
||||||
|
diff gates PASS; checkpoint dry-run planned `7/7`
|
||||||
|
- Remaining risks: private Azeroth corpus was absent, so the asset-backed ADT
|
||||||
|
placement probe could not open the map and is not counted as passed evidence;
|
||||||
|
first ownership remains processing-order based; M2 grouping/build/tasks/caches/
|
||||||
|
finalization and asset-backed p95/p99 remain pending
|
||||||
|
- Documentation updated: `m2-unique-placement-registry.md`, module registry,
|
||||||
|
`world-renderer.md`, `RENDER.md`, inline API documentation and data-flow,
|
||||||
|
lifecycle and sequence diagrams
|
||||||
|
- Integration: accepted in master merge `2342430`; post-merge registry, legacy
|
||||||
|
smoke, internal-access, facade and manifest 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.
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
# M03-RND-TERRAIN-CACHE-SERVICE-001 — Terrain quality mesh cache service
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-TERRAIN-CACHE-SERVICE-001:sindo-main-codex:2026-07-18 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M03
|
||||||
|
- Program: RND
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m03-terrain-quality-cache`
|
||||||
|
- Lease expires UTC: 2026-07-18
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Extract the full-quality terrain revisit Mesh cache, its LRU ownership and
|
||||||
|
source-admission rules from `StreamingWorldLoader` into a small scene-free
|
||||||
|
`TerrainQualityMeshCache`, preserving all call sites and observable behavior.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Move ADT parsing, tile state, quality-upgrade tasks/results or SceneTree/RID finalization.
|
||||||
|
- Change cache capacity defaults, quality profiles, source priority or cache formats.
|
||||||
|
- Cache control-splat/splat meshes that the current loader deliberately rejects.
|
||||||
|
- Extract chunk LOD, terrain material building, ground query or other asset services.
|
||||||
|
- Add dependencies, persistence, background work or mark the terrain service complete.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/terrain/terrain_quality_mesh_cache.gd`,
|
||||||
|
`src/tools/verify_terrain_quality_mesh_cache.gd`,
|
||||||
|
`docs/modules/terrain-quality-mesh-cache.md`, this claim
|
||||||
|
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
|
||||||
|
`src/tools/verify_render_runtime_cache_shutdown.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 service API: `store`, `restore`, `clear`, detached `diagnostic_snapshot`
|
||||||
|
- Entry: tile key, retained Mesh reference and source string
|
||||||
|
- Admission: non-empty key, non-null Mesh, source other than
|
||||||
|
`control_splat_cache`, `splat_cache` or `splat`
|
||||||
|
- LRU: successful restore touches newest; store touches then trims oldest to
|
||||||
|
`max(0, capacity)`; invalid restored records are removed
|
||||||
|
- Persistence/cache format: none; session-memory ownership only
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: integrated facade contracts master `61bad09`
|
||||||
|
- Blocks: later terrain service extraction and cache/finalization acceptance audit
|
||||||
|
- External state: none; tests use synthetic ArrayMesh references
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: dedicated cache contract/LRU/ownership regression; streamer source
|
||||||
|
boundary; shutdown/facade/internal-access/manifest/planner/scheduler and repository gates
|
||||||
|
- Fixtures: admission/rejection, replacement, LRU touch/eviction, zero/negative
|
||||||
|
capacity, invalid record cleanup, clear and detached diagnostics
|
||||||
|
- Fidelity evidence: same sources/capacity/call sites; all seven baseline plans unchanged
|
||||||
|
- Performance budget: dictionary lookup plus bounded single-entry LRU operations;
|
||||||
|
no Node, task, file, cache serialization or RID creation
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline public API docs: store/restore/clear/diagnostics and ownership
|
||||||
|
- Module specification: new terrain quality mesh cache specification
|
||||||
|
- Data-flow diagram: upgrade result through cache to revisit state
|
||||||
|
- Sequence/state/dependency diagrams: store/restore/evict/reset and failure paths
|
||||||
|
- Source map/status updates: module registry, world renderer and `RENDER.md`
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important names introduced: `TerrainQualityMeshCache`
|
||||||
|
- Simplest considered solution: one RefCounted with dictionary plus ordered tile-key array
|
||||||
|
- Rejected complexity/abstractions: generic cache framework, weak references,
|
||||||
|
callbacks, Resource cache integration, background eviction or persistence
|
||||||
|
- Unavoidable complexity and justification: Mesh references must survive unloaded
|
||||||
|
tile state while LRU keeps revisit memory bounded
|
||||||
|
- Measured optimization evidence: exact ownership extraction, no optimization claim
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: ready-for-review
|
||||||
|
- Done: scene-free cache ownership/LRU extraction, loader adapter migration,
|
||||||
|
contract regression and module documentation
|
||||||
|
- Next: M03 integrator reviews and merges `97480e0`
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-TERRAIN-CACHE-SERVICE-001:97480e0 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-TERRAIN-CACHE-SERVICE-001:630a0c1 -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `97480e0`
|
||||||
|
- Results: `TerrainQualityMeshCache` now owns retained full-quality Mesh
|
||||||
|
references, source admission and oldest-to-newest LRU. The loader retains only
|
||||||
|
restore-to-state, store-after-upgrade and clear lifecycle adapters.
|
||||||
|
- Verification: cache `contract_cases=15 loader_calls=3`; facade/entity/ground/
|
||||||
|
terrain regressions; internal access `private_symbols=42 gameplay=7
|
||||||
|
editor_sources=9 renderer_tools=1`; manifest `7/7`; shutdown; scheduler
|
||||||
|
`20000` iterations; planner `250` iterations; focus; editor parse; dry-run;
|
||||||
|
coordination, documentation and diff gates passed.
|
||||||
|
- Fidelity: `baked_full` revisit retention, three excluded splat sources, LRU
|
||||||
|
touch/trim, profile capacities and all call sites remain unchanged. All seven
|
||||||
|
checkpoint plans remain valid. No visible or build-12340 parity claim is added.
|
||||||
|
- External/local inputs: ignored native DLL and generated ADT scripts supported
|
||||||
|
parse/dry-run only; missing proprietary ADT/DBC/character data produced expected diagnostics.
|
||||||
|
- Remaining risks: entry count does not measure Mesh bytes; ordered-array touch
|
||||||
|
remains linear; asset-backed revisit flash, memory and p95/p99 evidence remain pending;
|
||||||
|
terrain build/tasks/state/finalization are still monolithic.
|
||||||
|
- Documentation updated: new terrain cache API/input-output/data-flow/lifecycle/
|
||||||
|
sequence/ownership/failure/configuration/migration/diagnostic/source-map spec;
|
||||||
|
world renderer, registry and `RENDER.md` updated.
|
||||||
|
- Integration: accepted in master merge `630a0c1`; post-merge cache, facade,
|
||||||
|
internal-access, manifest, shutdown, scheduler and planner gates passed.
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# M03-RND-TERRAIN-CHUNK-QUEUE-001 — Pure terrain chunk geometry queue planner
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-TERRAIN-CHUNK-QUEUE-001:sindo-main-codex:2026-07-19 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M03
|
||||||
|
- Program: RND
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m03-terrain-chunk-queue`
|
||||||
|
- Lease expires UTC: 2026-07-19
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Extract chunk-geometry create/remove desired-operation calculation from the
|
||||||
|
streamer into a scene-free `TerrainChunkGeometryQueuePlanner`.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Move queue storage/drains, render budgets, tile-LOD queues or async tasks.
|
||||||
|
- Move chunk Mesh/Node/RID creation/removal or terrain state ownership.
|
||||||
|
- Change removal-first execution, nearest-first create order or LOD selection.
|
||||||
|
- Change coordinate conversions, radii, cache formats or quality profiles.
|
||||||
|
- Add dependencies or mark the broad terrain service checklist complete.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/terrain/terrain_chunk_geometry_queue_planner.gd`,
|
||||||
|
`src/tools/verify_terrain_chunk_geometry_queue_planner.gd`,
|
||||||
|
`docs/modules/terrain-chunk-geometry-queue-planner.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 ADT resources, caches and
|
||||||
|
proprietary renderer corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- Input: loader-owned tile-state read model plus immutable `GodotWorldPosition`
|
||||||
|
- Output: fresh create/remove request arrays; planner retains no queue state
|
||||||
|
- Removal: current chunks absent from desired state, or all current chunks while
|
||||||
|
tile LOD is desired
|
||||||
|
- Creation: missing or LOD-mismatched chunks, sorted by origin XZ distance
|
||||||
|
- Queue/cache/schema/coordinate versions: unchanged
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: integrated terrain LOD planner master `b568768`
|
||||||
|
- Blocks: broader terrain service extraction and dependency/performance acceptance
|
||||||
|
- External state: none; tests use synthetic tile/chunk dictionaries
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: dedicated removal/create/mismatch/tile-fallback/sparse/order and
|
||||||
|
bounded timing verifier; loader source boundary; facade/cache/LOD/internal-access/
|
||||||
|
manifest/shutdown/scheduler/streaming planner and repository gates
|
||||||
|
- Fidelity evidence: extracted branch/order/distance rules and loader drain order
|
||||||
|
- Performance budget: bounded linear planning plus existing create sort; no engine
|
||||||
|
resource, task, cache or persistent allocation framework
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline planner API documentation
|
||||||
|
- New module spec with inputs/outputs, data-flow, lifecycle, sequence, ownership,
|
||||||
|
failure, performance and source map
|
||||||
|
- World renderer/module registry/`RENDER.md` updates
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important name: `TerrainChunkGeometryQueuePlanner`
|
||||||
|
- Simplest solution: one stateless planner returning two fresh arrays
|
||||||
|
- Rejected complexity: generic operation graph, queue class, callback strategy,
|
||||||
|
spatial index or ownership transfer
|
||||||
|
- Unavoidable complexity: raw dictionaries remain at the loader/ADT adapter boundary
|
||||||
|
- Measured optimization evidence: bounded synthetic multi-tile verifier
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: accepted
|
||||||
|
- Done: pure planner, loader queue adoption, semantic/timing regressions and docs
|
||||||
|
- Next: asset-backed performance acceptance and further terrain-service extraction later in M03
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-TERRAIN-CHUNK-QUEUE-001:f800e11 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-TERRAIN-CHUNK-QUEUE-001:24aef13 -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `f800e11` (`refactor(M03): extract terrain chunk queue planner`)
|
||||||
|
- Results: dedicated contract PASS (`cases=8`, `iterations=100`, `235.096 ms`);
|
||||||
|
LOD/cache/facade/internal-access/manifest/shutdown/scheduler/streaming/focus/
|
||||||
|
ground/terrain-query/coordinate regressions PASS; internal inventory remains
|
||||||
|
`42`; editor parse has no script errors; documentation/coordination/diff gates
|
||||||
|
PASS; checkpoint dry-run planned `7/7` entries with expected missing-private-
|
||||||
|
corpus diagnostics
|
||||||
|
- Remaining risks: planner adds a second linear tile-state pass; asset-backed
|
||||||
|
p95/p99 and visual acceptance remain pending; queue drains, terrain state and
|
||||||
|
Mesh/Node/RID finalization remain loader-owned
|
||||||
|
- Documentation updated: `terrain-chunk-geometry-queue-planner.md`, module
|
||||||
|
registry, `world-renderer.md`, `RENDER.md`, inline API documentation and
|
||||||
|
data-flow/lifecycle/sequence diagrams
|
||||||
|
- Integration: accepted in master merge `24aef13`; post-merge planner,
|
||||||
|
internal-access, facade and manifest gates passed
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
# M03-RND-TERRAIN-LOD-PLANNER-001 — Pure terrain chunk LOD planner
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-TERRAIN-LOD-PLANNER-001:sindo-main-codex:2026-07-18 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M03
|
||||||
|
- Program: RND
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m03-terrain-chunk-lod-planner`
|
||||||
|
- Lease expires UTC: 2026-07-18
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Extract chunk-level terrain LOD desired-state calculation from the streamer into
|
||||||
|
a scene-free `TerrainChunkLodPlanner` with immutable policy and typed focus input.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Move chunk/tile queues, loaded state, Mesh/Node/RID creation or budget drains.
|
||||||
|
- Change radii, chunk size, threshold inclusivity, priority sorting or tile LOD policy.
|
||||||
|
- Normalize/reorder custom radii or reinterpret negative values.
|
||||||
|
- Move ADT parsing, quality cache/tasks, materials or ground query.
|
||||||
|
- Add dependencies, persistence, workers or mark terrain extraction complete.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/terrain/terrain_chunk_lod_policy.gd`,
|
||||||
|
`src/render/terrain/terrain_chunk_lod_planner.gd`,
|
||||||
|
`src/tools/verify_terrain_chunk_lod_planner.gd`,
|
||||||
|
`docs/modules/terrain-chunk-lod-planner.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 resources, caches and
|
||||||
|
proprietary renderer corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- Input: ADT chunk adapter records, immutable `GodotWorldPosition`, immutable
|
||||||
|
enabled/radii/chunk-size policy
|
||||||
|
- Output: detached Dictionary mapping source chunk index to LOD `0`, `1` or `2`
|
||||||
|
- Formula: horizontal distance to `origin + half chunk`; inclusive squared thresholds
|
||||||
|
- Disabled policy and empty/sparse chunks return absent entries exactly as before
|
||||||
|
- Queue/cache/schema/coordinate versions: unchanged
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: integrated terrain cache slice master `ea08b8e`
|
||||||
|
- Blocks: broader terrain service extraction and dependency/performance acceptance
|
||||||
|
- External state: none; tests use synthetic chunk adapter records
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: dedicated center/boundary/outside/sparse/disabled/custom-radius and
|
||||||
|
bounded timing verifier; loader source boundary; facade/cache/internal-access/
|
||||||
|
manifest/shutdown/scheduler/streaming planner and repository gates
|
||||||
|
- Fidelity evidence: extracted formulas and all seven baseline plans remain unchanged
|
||||||
|
- Performance budget: bounded linear scan over at most the ADT chunk record count;
|
||||||
|
no Node, Resource, RID, task, cache entry or allocation framework
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline API docs for policy/planner
|
||||||
|
- New module spec with inputs/outputs, data-flow, lifecycle, sequence, ownership,
|
||||||
|
failure, performance and source map
|
||||||
|
- World renderer/module registry/`RENDER.md` updates
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important names: `TerrainChunkLodPolicy`, `TerrainChunkLodPlanner`
|
||||||
|
- Simplest solution: immutable scalar policy plus one pure linear planner
|
||||||
|
- Rejected complexity: generic spatial index, quadtree, callback strategy,
|
||||||
|
normalized profile model or queue ownership
|
||||||
|
- Unavoidable complexity: raw chunk dictionaries remain at the ADT adapter boundary
|
||||||
|
- Measured optimization evidence: verifier records bounded High-like planning time
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: accepted
|
||||||
|
- Done: immutable policy, pure planner, both loader call sites, regressions and docs
|
||||||
|
- Next: asset-backed performance acceptance and further terrain-service extraction later in M03
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-TERRAIN-LOD-PLANNER-001:eb94cfc -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-TERRAIN-LOD-PLANNER-001:f36fabb -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `eb94cfc` (`refactor(M03): extract terrain chunk LOD planner`)
|
||||||
|
- Results: dedicated contract PASS (`cases=7`, `iterations=250`, `12.726 ms`);
|
||||||
|
cache/facade/internal-access/manifest/shutdown/scheduler/streaming-focus/
|
||||||
|
streaming-planner/ground/terrain-query/coordinate regressions PASS; editor parse
|
||||||
|
has no script errors; documentation and coordination gates PASS; checkpoint
|
||||||
|
dry-run planned `7/7` entries with expected missing-private-corpus diagnostics
|
||||||
|
- Remaining risks: asset-backed p95/p99 and visual acceptance remain pending;
|
||||||
|
terrain state, queue, quality-upgrade and finalization ownership remains in loader
|
||||||
|
- Documentation updated: `terrain-chunk-lod-planner.md`, module registry,
|
||||||
|
`world-renderer.md`, `RENDER.md`, inline public API documentation and data-flow,
|
||||||
|
lifecycle and sequence diagrams
|
||||||
|
- Integration: accepted in master merge `f36fabb`; post-merge planner, facade,
|
||||||
|
internal-access and manifest gates passed
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
# M03-RND-WMO-PLACEMENT-REGISTRY-001 — WMO placement registry
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-WMO-PLACEMENT-REGISTRY-001:sindo-main-codex:2026-07-20 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M03
|
||||||
|
- Program: RND
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m03-wmo-placement-registry`
|
||||||
|
- Lease expires UTC: 2026-07-20
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Extract WMO placement-key to tile-reference ownership from the loader while
|
||||||
|
leaving every Node and renderer-resource lifecycle in the loader.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Change placement identity rules or WMO transforms.
|
||||||
|
- Move Node maps, world roots, tile state, jobs, queues or cancellation.
|
||||||
|
- Move cache/load requests, material refresh or render-group building.
|
||||||
|
- Change duplicate ordering, global-WMO lifetime or build budgets.
|
||||||
|
- Claim asset-backed WMO fidelity or performance acceptance.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/wmo/wmo_placement_registry.gd`,
|
||||||
|
`src/tools/verify_wmo_placement_registry.gd`,
|
||||||
|
`docs/modules/wmo-placement-registry.md`, this claim
|
||||||
|
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
|
||||||
|
`docs/modules/wmo-placement-resolver.md`, `docs/modules/world-renderer.md`,
|
||||||
|
`docs/modules/README.md`, `RENDER.md`, `targets/03-renderer-facade.md`
|
||||||
|
- Generated/ignored: `.godot`, native DLL, generated ADT/WMO resources, caches
|
||||||
|
and proprietary renderer corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- Input: resolved WMO placement key and tile/global reference key
|
||||||
|
- `add_reference` creates a key/ref set or idempotently retains an existing ref
|
||||||
|
- `release_reference` erases only the supplied ref and returns true only when the
|
||||||
|
final ref removes the placement key
|
||||||
|
- `contains`, `active_count`, detached sorted diagnostics and idempotent `clear`
|
||||||
|
- Loader retains key→Node mapping and reacts to final release by cancelling the
|
||||||
|
render build and queue-freeing the corresponding Node
|
||||||
|
- Cache/schema/coordinate/profile versions: unchanged
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: current master `5aa40d6`, `WmoPlacementResolver` identity contract
|
||||||
|
- Blocks: WMO build/resource state decomposition
|
||||||
|
- External state: none; tests use synthetic placement/tile keys
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: dedicated add/shared/idempotent/non-owner/final/global/clear/
|
||||||
|
diagnostics/source/timing verifier; resolver/material/shutdown plus M2/terrain/
|
||||||
|
facade/internal-access/manifest/scheduler/streaming/coordinate/docs/coord gates
|
||||||
|
- Fidelity evidence: exact shared-ref and final-release behavior preserved
|
||||||
|
- Performance budget: dictionary membership over active WMO keys/refs; no Node,
|
||||||
|
Resource, RID, task, queue, mutex, cache or file access
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline registry API documentation
|
||||||
|
- New module spec with inputs/outputs, data-flow, lifecycle/state, ownership,
|
||||||
|
failure/recovery, performance, fidelity limitations and source map
|
||||||
|
- Placement resolver, renderer/module registry and `RENDER.md` updates
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important name: `WmoPlacementRegistry`
|
||||||
|
- Simplest solution: one private key-to-reference-set Dictionary
|
||||||
|
- Rejected complexity: generic ref-count registry, signals, callbacks, Resources,
|
||||||
|
weak refs or Node ownership inside the service
|
||||||
|
- Unavoidable complexity: loader keeps a parallel key-to-Node map
|
||||||
|
- Measured optimization evidence: bounded synthetic add/release loop
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: accepted
|
||||||
|
- Done: registry extracted; global/tile adapters migrated; Node/resource/job
|
||||||
|
ownership retained in loader; contract, regressions, dry-run and docs passed
|
||||||
|
- Next: WMO build/resource state extraction and asset-backed placement evidence
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-WMO-PLACEMENT-REGISTRY-001:c590036 -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-WMO-PLACEMENT-REGISTRY-001:f9e5a4f -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `c590036`
|
||||||
|
- Results: `verify_wmo_placement_registry.gd` PASS cases=10,
|
||||||
|
iterations=100, elapsed_ms=53.941; 21 adjacent WMO/M2/terrain/facade/
|
||||||
|
scheduler/streaming/coordinate regressions PASS; documentation and
|
||||||
|
coordination gates PASS; render dry-run PASS 7/7.
|
||||||
|
- Remaining risks: no proprietary asset corpus or original-client paired WMO
|
||||||
|
capture; dry-run reports expected missing ADT/DBC/character assets; WMO
|
||||||
|
build/resource state remains loader-owned.
|
||||||
|
- Documentation updated: `docs/modules/wmo-placement-registry.md`,
|
||||||
|
`docs/modules/wmo-placement-resolver.md`, `docs/modules/world-renderer.md`,
|
||||||
|
`docs/modules/README.md`, `RENDER.md`; inline public API comments added.
|
||||||
|
- Integration: accepted in master merge `f9e5a4f`; post-merge registry
|
||||||
|
(`cases=10`, `iterations=100`, `elapsed_ms=54.178`), resolver, runtime-cache
|
||||||
|
shutdown, facade, documentation and coordination gates passed.
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
# M03-RND-WMO-PLACEMENT-RESOLVER-001 — WMO placement resolver
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-WMO-PLACEMENT-RESOLVER-001:sindo-main-codex:2026-07-20 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M03
|
||||||
|
- Program: RND
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m03-wmo-placement-resolver`
|
||||||
|
- Lease expires UTC: 2026-07-20
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Extract pure WMO cache-path normalization, placement identity and world-transform
|
||||||
|
resolution from the loader into a scene-free service.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Change WDT/ADT coordinate conversion or WMO placement values.
|
||||||
|
- Own WMO registry references, tile lifecycle, jobs, queues or retries.
|
||||||
|
- Load cached/live WMO resources or validate cache format versions.
|
||||||
|
- Create Nodes, Mesh/MultiMesh, materials or RIDs.
|
||||||
|
- Change portal/room/material/occlusion behavior or claim WMO parity.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/wmo/wmo_placement_resolver.gd`,
|
||||||
|
`src/tools/verify_wmo_placement_resolver.gd`,
|
||||||
|
`docs/modules/wmo-placement-resolver.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 ADT/WMO resources, caches
|
||||||
|
and proprietary renderer corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- `normalize_relative_path`: replace backslashes, lowercase entire cache key
|
||||||
|
- `resolve_unique_key`: positive `unique_id` becomes `uid:<decimal>`; otherwise
|
||||||
|
preserve `tile:<tile-key>:<placement-index>` fallback
|
||||||
|
- `resolve_world_transform`: defaults position/rotation to zero and scale to one;
|
||||||
|
compose the same Godot Euler basis and un-clamped uniform scale
|
||||||
|
- Three transform consumers: render-cache root, cached scene and live prototype
|
||||||
|
- Cache/schema/coordinate/profile versions: unchanged
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: current master `4f40c77`
|
||||||
|
- Blocks: WMO registry/build decomposition
|
||||||
|
- External state: none; tests use synthetic paths and placement dictionaries
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: dedicated path/identity/default/rotation/scale/source and bounded
|
||||||
|
timing verifier; M2/terrain/facade/internal-access/manifest/shutdown/scheduler/
|
||||||
|
streaming/coordinate/documentation/coordination gates
|
||||||
|
- Fidelity evidence: exact normalization, identity and property-transform rules
|
||||||
|
moved unchanged
|
||||||
|
- Performance budget: constant-time string/value math; no Node, Resource, RID,
|
||||||
|
task, queue, mutex, cache or file access
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline resolver API documentation
|
||||||
|
- New module spec with inputs/outputs, data-flow, lifecycle, ownership, failure,
|
||||||
|
performance, fidelity limitations and source map
|
||||||
|
- Renderer/module registry and `RENDER.md` updates
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important name: `WmoPlacementResolver`
|
||||||
|
- Simplest solution: one stateless service with three explicit pure queries
|
||||||
|
- Rejected complexity: placement class hierarchy, generic model resolver,
|
||||||
|
Resources, strategies, callbacks or registry framework
|
||||||
|
- Unavoidable complexity: raw placement Dictionary remains parser boundary
|
||||||
|
- Measured optimization evidence: bounded synthetic resolver loop
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: accepted
|
||||||
|
- Done: resolver, seven loader adapters, regressions and documentation verified
|
||||||
|
- Next: WMO registry/build extraction and asset-backed placement evidence
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-WMO-PLACEMENT-RESOLVER-001:05e216a -->
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-WMO-PLACEMENT-RESOLVER-001:d2cb52f -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `05e216a` (`refactor(M03): extract WMO placement resolver`)
|
||||||
|
- Results: dedicated resolver PASS (`cases=9`, `iterations=20000`,
|
||||||
|
`elapsed_ms=28.332`), including historical Node3D property equivalence;
|
||||||
|
renderer materials, M2 planner/grouper/transform/registry/dedupe, terrain
|
||||||
|
cache/LOD/queue, facade, internal-access, manifest, shutdown, scheduler,
|
||||||
|
streaming planner/focus, terrain-query and coordinate-boundary suites PASS;
|
||||||
|
editor import parsed with only expected missing private corpus diagnostics;
|
||||||
|
documentation PASS (`module_specs=19`, `required_files=7`); coordination PASS
|
||||||
|
with 23 pre-existing expired M00/M01 warnings; checkpoint dry-run planned `7/7`.
|
||||||
|
- Remaining risks: no private asset-backed WMO placement/visual/p95/p99 evidence;
|
||||||
|
old caches without positive UID retain per-tile fallback; raw Dictionaries and
|
||||||
|
WMO registry/build/resource state remain loader-owned; scale stays unclamped.
|
||||||
|
- Documentation updated: inline public API; new
|
||||||
|
`docs/modules/wmo-placement-resolver.md`; renderer module registry,
|
||||||
|
`docs/modules/world-renderer.md` and `RENDER.md`.
|
||||||
|
- Integration: accepted in master merge `d2cb52f`; post-merge resolver
|
||||||
|
(`cases=9`, `iterations=20000`, `elapsed_ms=27.919`), renderer materials,
|
||||||
|
shutdown, internal-access, facade, manifest, documentation and coordination
|
||||||
|
gates passed.
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
# M03-RND-WMO-RENDER-BUILD-PLANNER-001 — WMO render build step planner
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-WMO-RENDER-BUILD-PLANNER-001:sindo-main-codex:2026-07-20 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M03
|
||||||
|
- Program: RND
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m03-wmo-render-build-planner`
|
||||||
|
- Lease expires UTC: 2026-07-20
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Extract the pure one-operation WMO render build cursor decision from the loader
|
||||||
|
while preserving mesh-before-MultiMesh ordering and one budget permit per group.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Move Nodes, Resources, materials, jobs, queues, cancellation or permits.
|
||||||
|
- Change WMO placement, cache loading, transforms, visibility or shadows.
|
||||||
|
- Change build ordering, group names, editor ownership or finalization thread.
|
||||||
|
- Change cache formats, settings, presets or renderer diagnostics.
|
||||||
|
- Claim asset-backed WMO fidelity or p95/p99 acceptance.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/wmo/wmo_render_build_step_planner.gd`,
|
||||||
|
`src/tools/verify_wmo_render_build_step_planner.gd`,
|
||||||
|
`docs/modules/wmo-render-build-step-planner.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 ADT/WMO resources, caches
|
||||||
|
and proprietary renderer corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- Input: mesh count/index and MultiMesh count/index
|
||||||
|
- Output: `mesh`, `multimesh` or `complete` operation, selected index and next
|
||||||
|
cursor values
|
||||||
|
- Mesh is selected while its cursor is below count; MultiMesh is selected only
|
||||||
|
after mesh exhaustion; complete follows both exhausted ranges
|
||||||
|
- Loader retains cursor adoption, Resource array access, Node creation, material
|
||||||
|
refresh, ownership, queue cancellation and budget permit consumption
|
||||||
|
- Cache/schema/coordinate/profile versions: unchanged
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: current master `1e73e59`, accepted WMO placement registry/resolver
|
||||||
|
- Blocks: further WMO render build state decomposition
|
||||||
|
- External state: none; verification uses scalar synthetic fixtures
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: dedicated ordering/cursor/empty/raw-edge/source/timing verifier;
|
||||||
|
WMO registry/resolver/material/shutdown plus M2/terrain/facade/internal-access/
|
||||||
|
manifest/scheduler/streaming/coordinate/docs/coord gates
|
||||||
|
- Fidelity evidence: exact mesh-first, one-step cursor behavior preserved
|
||||||
|
- Performance budget: 20,000 pure plans under 1 second; no Node, Resource, RID,
|
||||||
|
task, queue, mutex, cache or file access
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline public planner API documentation
|
||||||
|
- New module spec with inputs/outputs, data flow, sequence, ownership,
|
||||||
|
failure/recovery, performance/fidelity limits and source map
|
||||||
|
- Renderer module registry, world renderer and `RENDER.md` updates
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important name: `WmoRenderBuildStepPlanner`
|
||||||
|
- Simplest solution: one stateless function and three operation constants
|
||||||
|
- Rejected complexity: job class, iterator, generic cursor framework, callbacks,
|
||||||
|
signals or ownership of engine objects
|
||||||
|
- Unavoidable complexity: loader still has separate mesh/MultiMesh materializers
|
||||||
|
- Measured optimization evidence: bounded synthetic planning loop
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: accepted
|
||||||
|
- Done: planner and loader adapter implemented; Node/Resource/material/job/queue/
|
||||||
|
permit ownership retained in loader; regressions, dry-run and docs passed
|
||||||
|
- Next: WMO build job/resource ownership or liquid service extraction
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-WMO-RENDER-BUILD-PLANNER-001:c06aed5 -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `3a6b1d3`
|
||||||
|
- Results: dedicated planner PASS (`cases=9`, `iterations=20000`,
|
||||||
|
`elapsed_ms=11.914`); WMO registry/resolver/material/shutdown and 18 adjacent
|
||||||
|
M2/terrain/facade/scheduler/streaming/coordinate regressions PASS;
|
||||||
|
documentation and coordination gates PASS; checkpoint dry-run PASS `7/7`.
|
||||||
|
- Remaining risks: no proprietary WMO corpus, original-client paired capture or
|
||||||
|
traversal p95/p99; dry-run reports expected missing ADT/DBC/character assets;
|
||||||
|
WMO Resource arrays and untyped build jobs/queues remain loader-owned.
|
||||||
|
- Documentation updated: inline planner API;
|
||||||
|
`docs/modules/wmo-render-build-step-planner.md`, module registry,
|
||||||
|
`docs/modules/world-renderer.md`, `RENDER.md` with data-flow and sequence
|
||||||
|
diagrams, ownership, failure/recovery and source map.
|
||||||
|
- Integration: accepted in master merge `c06aed5`; post-merge planner
|
||||||
|
(`cases=9`, `iterations=20000`, `elapsed_ms=11.059`), WMO registry/resolver,
|
||||||
|
runtime-cache shutdown, facade, documentation and coordination gates passed.
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-WMO-RENDER-BUILD-PLANNER-001:3a6b1d3 -->
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
# M03-RND-WMO-RENDER-BUILD-QUEUE-001 — WMO render build queue
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-WMO-RENDER-BUILD-QUEUE-001:sindo-main-codex:2026-07-20 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M03
|
||||||
|
- Program: RND
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m03-wmo-render-build-queue`
|
||||||
|
- Lease expires UTC: 2026-07-20
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Extract typed lightweight-WMO render build jobs and their FIFO key queue from
|
||||||
|
the loader while leaving all engine-object destruction and materialization in
|
||||||
|
the loader.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Queue-free Nodes or free Resources/RIDs.
|
||||||
|
- Move material refresh, Node creation, editor ownership or budget permits.
|
||||||
|
- Change duplicate enqueue, stale-key cleanup, cancel or clear ordering.
|
||||||
|
- Change placement, cache loading/versioning, transforms, shadows or visibility.
|
||||||
|
- Claim asset-backed WMO fidelity, leak acceptance or traversal p95/p99.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/wmo/wmo_render_build_job.gd`,
|
||||||
|
`src/render/wmo/wmo_render_build_queue.gd`,
|
||||||
|
`src/tools/verify_wmo_render_build_queue.gd`,
|
||||||
|
`docs/modules/wmo-render-build-queue.md`, this claim
|
||||||
|
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
|
||||||
|
`docs/modules/wmo-render-build-step-planner.md`,
|
||||||
|
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
|
||||||
|
`targets/03-renderer-facade.md`
|
||||||
|
- Generated/ignored: `.godot`, native DLL, generated ADT/WMO resources, caches
|
||||||
|
and proprietary renderer corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- Typed job: placement key, strong Node3D/Resource references and two cursors
|
||||||
|
- FIFO queue preserves duplicate key append and first-occurrence erase behavior
|
||||||
|
- Stale front keys remain observable for loader-compatible pop-and-continue
|
||||||
|
- Cancel releases the keyed job reference and first queued key only
|
||||||
|
- Clear releases every job/key reference idempotently; never frees engine objects
|
||||||
|
- Loader retains validity checks, planner invocation, cursor adoption,
|
||||||
|
materialization, permits, cancellation reactions and Node destruction
|
||||||
|
- Cache/schema/coordinate/profile versions: unchanged
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: current master `1e740aa`, accepted WMO build step planner
|
||||||
|
- Blocks: further WMO cache/load and materialization decomposition
|
||||||
|
- External state: none; verification uses synthetic Node3D/Resource instances
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: dedicated invalid/FIFO/duplicate/stale/cursor/cancel/clear/
|
||||||
|
diagnostics/source/timing verifier; WMO planner/registry/resolver/material/
|
||||||
|
shutdown plus M2/terrain/facade/internal-access/manifest/scheduler/streaming/
|
||||||
|
coordinate/docs/coord gates
|
||||||
|
- Fidelity evidence: exact current queue/dictionary behavior preserved
|
||||||
|
- Performance budget: 100 cycles over 256 jobs under 1 second
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline job/queue API documentation
|
||||||
|
- New module spec with inputs/outputs, data flow, state/sequence, ownership,
|
||||||
|
threading, failure/recovery, performance/fidelity limits and source map
|
||||||
|
- Build-step planner, renderer module registry, world renderer and RENDER updates
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important names: `WmoRenderBuildJob`, `WmoRenderBuildQueue`
|
||||||
|
- Simplest solution: one typed record plus Dictionary/Array queue composition
|
||||||
|
- Rejected complexity: generic job scheduler, signals, callbacks, mutexes,
|
||||||
|
Resources as job records or engine lifetime callbacks
|
||||||
|
- Unavoidable complexity: duplicate keys can intentionally leave stale entries
|
||||||
|
- Measured optimization evidence: bounded synthetic enqueue/cancel/clear loop
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: accepted
|
||||||
|
- Done: typed job/queue implemented; three clear sites, metrics, enqueue, drain and
|
||||||
|
cancel migrated; engine destruction/materialization retained in loader
|
||||||
|
- Next: WMO cache/load state or liquid service extraction
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-WMO-RENDER-BUILD-QUEUE-001:60603e1 -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `e576ae2` (implementation `e1bb105` plus cold-load fix)
|
||||||
|
- Results: queue PASS (`cases=11`, `iterations=100`, `elapsed_ms=59.874`);
|
||||||
|
WMO planner/registry/resolver/material/shutdown and 18 adjacent M2/terrain/
|
||||||
|
facade/scheduler/streaming/coordinate regressions PASS; documentation and
|
||||||
|
coordination gates PASS; checkpoint dry-run PASS `7/7`.
|
||||||
|
- Cold-load recovery: post-merge smoke exposed an editor class-cache dependency;
|
||||||
|
typed queue accessors removed the loader/global-class dependency and a fresh
|
||||||
|
run without `.godot` passed (`cases=11`, `elapsed_ms=57.835`).
|
||||||
|
- Remaining risks: strong Node/Resource references require every loader cancel/
|
||||||
|
clear path; duplicate enqueue intentionally preserves stale FIFO keys; no
|
||||||
|
proprietary WMO corpus, original-client paired capture, leak run or traversal
|
||||||
|
p95/p99; dry-run reports expected missing ADT/DBC/character assets.
|
||||||
|
- Documentation updated: inline job/queue API;
|
||||||
|
`docs/modules/wmo-render-build-queue.md`, build-step planner spec, module
|
||||||
|
registry, world renderer and `RENDER.md`; data-flow/state/sequence diagrams,
|
||||||
|
ownership, threading, failure/recovery and source map included.
|
||||||
|
- Integration: implementation merged as `a878e7c`; its first post-merge smoke
|
||||||
|
exposed and rejected a cold global-class registration dependency. Fix merged
|
||||||
|
as `60603e1`; post-fix master queue (`cases=11`, `iterations=100`,
|
||||||
|
`elapsed_ms=56.454`), planner, registry, resolver, shutdown, facade,
|
||||||
|
documentation and coordination gates passed without parse diagnostics.
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-WMO-RENDER-BUILD-QUEUE-001:e576ae2 -->
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
# M03-RND-WMO-RENDER-RESOURCE-CACHE-001 — WMO render resource cache state
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-WMO-RENDER-RESOURCE-CACHE-001:sindo-main-codex:2026-07-20 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M03
|
||||||
|
- Program: RND
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m03-wmo-render-resource-cache`
|
||||||
|
- Lease expires UTC: 2026-07-20
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Extract lightweight-WMO render Resource cache, negative cache and asynchronous
|
||||||
|
request bookkeeping from the loader while preserving loader-owned I/O and
|
||||||
|
cache-format validation.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Call `ResourceLoader`, construct cache paths or inspect files.
|
||||||
|
- Validate `WMOStreamingResource` script identity or format version.
|
||||||
|
- Own cached WMO scenes, live prototypes, placement/build jobs or Nodes/RIDs.
|
||||||
|
- Change fallback order, retry behavior, quality profiles or visual output.
|
||||||
|
- Claim asset-backed WMO fidelity, leak acceptance or traversal p95/p99.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/wmo/wmo_render_resource_cache_state.gd`,
|
||||||
|
`src/tools/verify_wmo_render_resource_cache_state.gd`,
|
||||||
|
`docs/modules/wmo-render-resource-cache-state.md`, this claim
|
||||||
|
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
|
||||||
|
`src/tools/verify_render_runtime_cache_shutdown.gd`,
|
||||||
|
`docs/modules/wmo-render-build-queue.md`, `docs/modules/world-renderer.md`,
|
||||||
|
`docs/modules/README.md`, `RENDER.md`, `targets/03-renderer-facade.md`
|
||||||
|
- Generated/ignored: `.godot`, native DLL, generated ADT/WMO resources, caches
|
||||||
|
and proprietary renderer corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- State keys are loader-normalized relative WMO paths copied as `String`.
|
||||||
|
- Cached values are already validated `Resource` references.
|
||||||
|
- Pending values are cache paths for loader-owned `ResourceLoader` requests.
|
||||||
|
- Terminal request removal precedes validated-resource or missing-state adoption.
|
||||||
|
- Request/missing reset preserves accepted resources; full clear releases all.
|
||||||
|
- Cache/schema/coordinate/profile versions and validation rules remain unchanged.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: current master `c954704`, accepted WMO render build queue
|
||||||
|
- Blocks: further WMO cached-scene/load and materialization decomposition
|
||||||
|
- External state: none; verification uses synthetic Resources and paths
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: dedicated lifecycle/request/cache/reset/diagnostics/source/timing
|
||||||
|
verifier; WMO queue/planner/registry/resolver/material/shutdown plus adjacent
|
||||||
|
renderer/streaming/coordinate/documentation/coordination gates
|
||||||
|
- Fixtures: synthetic Resource instances and normalized relative/cache paths
|
||||||
|
- Fidelity evidence: exact current cache/missing/request transitions preserved
|
||||||
|
- Performance budget: 100 cycles over 256 resource/request keys under 1 second
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline public API docs
|
||||||
|
- Module specification with inputs/outputs, ownership and source map
|
||||||
|
- Data-flow, lifecycle/state and loader interaction sequence diagrams
|
||||||
|
- World-renderer, module registry, WMO queue and RENDER status updates
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important names introduced: `WmoRenderResourceCacheState`
|
||||||
|
- Simplest considered solution: one RefCounted around three Dictionaries
|
||||||
|
- Rejected complexity/abstractions: generic cache, callbacks, signals, mutexes,
|
||||||
|
ResourceLoader wrapper, persistence layer or shared WMO/M2 cache hierarchy
|
||||||
|
- Unavoidable complexity and justification: partial reset must retain validated
|
||||||
|
Resources while discarding pending/negative state at map reset and shutdown
|
||||||
|
- Measured optimization evidence: bounded synthetic store/request/reset loop
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: accepted
|
||||||
|
- Done: state holder implemented; loader cache/request/reset/metrics adapters
|
||||||
|
migrated; shutdown regression and required documentation updated
|
||||||
|
- Next: cached WMO scene/load state or liquid service extraction
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-WMO-RENDER-RESOURCE-CACHE-001:b4955d6 -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `d39f591`
|
||||||
|
- Results: cache-state PASS (`cases=10`, `iterations=100`,
|
||||||
|
`elapsed_ms=58.727`); direct no-`.godot` cache-state run PASS (`45.361ms`);
|
||||||
|
WMO queue/planner/registry/resolver/material/shutdown plus 18 adjacent
|
||||||
|
M2/terrain/facade/scheduler/streaming/coordinate regressions PASS;
|
||||||
|
documentation and coordination gates PASS; checkpoint dry-run PASS `7/7`.
|
||||||
|
- Remaining risks: accepted Resources remain strongly referenced until full
|
||||||
|
shutdown release; cached-scene and ResourceLoader I/O seams remain in loader;
|
||||||
|
no proprietary corrupt-cache fixture, asset-backed WMO traversal/leak run,
|
||||||
|
p95/p99 or original-client paired evidence. Full loader cannot be used as a
|
||||||
|
direct empty-class-cache smoke because pre-existing typed global coordinate/
|
||||||
|
focus dependencies require editor class registration; the extracted service
|
||||||
|
itself passed cold and normal editor parse passed.
|
||||||
|
- Documentation updated: inline public API; new
|
||||||
|
`docs/modules/wmo-render-resource-cache-state.md`; module registry, WMO queue,
|
||||||
|
world renderer and `RENDER.md`; data-flow/state/sequence diagrams, ownership,
|
||||||
|
threading, cache-version boundary, failure/recovery and source map included.
|
||||||
|
- Integration: merged as `b4955d6`; post-merge cache state (`cases=10`,
|
||||||
|
`iterations=100`, `elapsed_ms=51.949`), shutdown, WMO queue/resolver, facade,
|
||||||
|
internal-access (`private_symbols=41`), `7/7` manifest, documentation and
|
||||||
|
coordination gates passed.
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-WMO-RENDER-RESOURCE-CACHE-001:d39f591 -->
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
# M03-RND-WMO-SCENE-RESOURCE-CACHE-001 — WMO cached-scene resource state
|
||||||
|
|
||||||
|
<!-- OPENWC_CLAIM:M03-RND-WMO-SCENE-RESOURCE-CACHE-001:sindo-main-codex:2026-07-20 -->
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
- Target: M03
|
||||||
|
- Program: RND
|
||||||
|
- Owner/Agent ID: sindo-main-codex
|
||||||
|
- Branch: `work/sindo-main-codex/m03-wmo-scene-resource-cache`
|
||||||
|
- Lease expires UTC: 2026-07-20
|
||||||
|
- Integrator: M03 milestone integrator
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Extract cached-WMO `PackedScene`, negative-cache and asynchronous request state
|
||||||
|
from the loader while preserving loader-owned file limits, I/O and scene-cache
|
||||||
|
version validation.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Call `ResourceLoader`/`FileAccess`, construct `.tscn` paths or measure files.
|
||||||
|
- Instantiate scenes or validate WMO builder cache metadata/version.
|
||||||
|
- Own live WMO prototypes, placement/build jobs, instantiated Nodes or RIDs.
|
||||||
|
- Change missing/oversize/request-error fallback behavior or debug output.
|
||||||
|
- Claim asset-backed WMO fidelity, leak acceptance or traversal p95/p99.
|
||||||
|
|
||||||
|
## Paths
|
||||||
|
|
||||||
|
- Exclusive: `src/render/wmo/wmo_scene_resource_cache_state.gd`,
|
||||||
|
`src/tools/verify_wmo_scene_resource_cache_state.gd`,
|
||||||
|
`docs/modules/wmo-scene-resource-cache-state.md`, this claim
|
||||||
|
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
|
||||||
|
`src/tools/verify_render_runtime_cache_shutdown.gd`,
|
||||||
|
`docs/modules/wmo-render-resource-cache-state.md`,
|
||||||
|
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
|
||||||
|
`targets/03-renderer-facade.md`
|
||||||
|
- Generated/ignored: `.godot`, native DLL, generated ADT/WMO resources, caches
|
||||||
|
and proprietary renderer corpus
|
||||||
|
|
||||||
|
## Contracts and data
|
||||||
|
|
||||||
|
- State keys are loader-normalized relative WMO paths copied as `String`.
|
||||||
|
- Cached values are loader-validated `PackedScene` references.
|
||||||
|
- Pending values are `.tscn` paths for loader-owned threaded requests.
|
||||||
|
- Missing state accepts absent file, oversize, request-start, load and validation failures.
|
||||||
|
- Terminal pending removal precedes validated-scene or missing adoption.
|
||||||
|
- Transient clear retains accepted scenes; full clear releases all state.
|
||||||
|
- Cache metadata/version, file-size limit, profile and fallback rules are unchanged.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Requires: current master `e690756`; accepted WMO render Resource cache state
|
||||||
|
- Blocks: further WMO I/O/fallback and materialization decomposition
|
||||||
|
- External state: none; verification uses synthetic PackedScenes and paths
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- Commands: dedicated lifecycle/request/missing/reset/diagnostics/source/timing
|
||||||
|
verifier; WMO render-cache/queue/planner/registry/resolver/material/shutdown plus
|
||||||
|
adjacent renderer/streaming/coordinate/documentation/coordination gates
|
||||||
|
- Fixtures: synthetic PackedScene instances and normalized relative/cache paths
|
||||||
|
- Fidelity evidence: exact current cached-scene/missing/request transitions preserved
|
||||||
|
- Performance budget: 100 cycles over 256 scene/request keys under 1 second
|
||||||
|
|
||||||
|
## Documentation deliverables
|
||||||
|
|
||||||
|
- Inline public API docs
|
||||||
|
- Module specification with inputs/outputs, ownership and source map
|
||||||
|
- Data-flow, lifecycle/state and loader interaction sequence diagrams
|
||||||
|
- World-renderer, module registry, render-cache and RENDER status updates
|
||||||
|
|
||||||
|
## Simplicity and naming
|
||||||
|
|
||||||
|
- Important names introduced: `WmoSceneResourceCacheState`
|
||||||
|
- Simplest considered solution: one RefCounted around three Dictionaries
|
||||||
|
- Rejected complexity/abstractions: generic cache hierarchy, callbacks, signals,
|
||||||
|
mutexes, ResourceLoader wrapper, persistence layer or render/scene cache merger
|
||||||
|
- Unavoidable complexity and justification: missing can be recorded without a
|
||||||
|
pending request, and partial reset must retain validated PackedScenes
|
||||||
|
- Measured optimization evidence: bounded synthetic store/request/reset loop
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
- State: accepted
|
||||||
|
- Done: state holder implemented; loader lookup/request/drain/reset/metrics
|
||||||
|
adapters migrated; shutdown regression and required documentation updated
|
||||||
|
- Next: WMO I/O/live fallback or liquid service extraction
|
||||||
|
- Blocked by:
|
||||||
|
|
||||||
|
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-WMO-SCENE-RESOURCE-CACHE-001:ada0fd9 -->
|
||||||
|
|
||||||
|
## Handoff
|
||||||
|
|
||||||
|
- Commit: `c839294`
|
||||||
|
- Results: scene cache-state PASS (`cases=11`, `iterations=100`,
|
||||||
|
`elapsed_ms=40.768`; initial direct cold run `43.636ms`); render cache-state,
|
||||||
|
WMO queue/planner/registry/resolver/material/shutdown plus 18 adjacent
|
||||||
|
M2/terrain/facade/scheduler/streaming/coordinate regressions PASS;
|
||||||
|
documentation and coordination gates PASS; checkpoint dry-run PASS `7/7`.
|
||||||
|
- Remaining risks: accepted PackedScenes remain strongly referenced until full
|
||||||
|
shutdown release; ResourceLoader/FileAccess I/O, cache-validation probes and
|
||||||
|
live fallback remain in loader; no proprietary stale/oversize fixture,
|
||||||
|
asset-backed traversal/leak run, p95/p99 or original-client paired evidence.
|
||||||
|
The extracted scene state passed cold; full loader cold execution still needs
|
||||||
|
editor class registration for pre-existing focus/coordinate global classes.
|
||||||
|
- Documentation updated: inline public API; new
|
||||||
|
`docs/modules/wmo-scene-resource-cache-state.md`; render-cache spec, module
|
||||||
|
registry, world renderer and `RENDER.md`; data-flow/state/sequence diagrams,
|
||||||
|
ownership, threading, file-size/cache-version boundary, failure/recovery and
|
||||||
|
source map included.
|
||||||
|
- Integration: merged as `ada0fd9`; post-merge scene state (`cases=11`,
|
||||||
|
`iterations=100`, `elapsed_ms=40.113`), render state (`43.434ms`), shutdown,
|
||||||
|
WMO queue/resolver, facade, internal-access (`private_symbols=40`), `7/7`
|
||||||
|
manifest, documentation and coordination gates passed.
|
||||||
|
|
||||||
|
<!-- OPENWC_HANDOFF:READY:M03-RND-WMO-SCENE-RESOURCE-CACHE-001:c839294 -->
|
||||||
@@ -36,7 +36,12 @@ TrinityCore/AzerothCore ◄── Network Adapter ◄── Runtime Client
|
|||||||
|
|
||||||
### Domain
|
### 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
|
### Gameplay
|
||||||
|
|
||||||
|
|||||||
@@ -237,6 +237,23 @@ TODO/FIXME обязан содержать target/work-package ID или diagnos
|
|||||||
- Медленный понятный reference implementation полезен как oracle для optimized implementation.
|
- Медленный понятный 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
|
## Review checklist
|
||||||
|
|
||||||
- Можно ли понять назначение сущности по имени?
|
- Можно ли понять назначение сущности по имени?
|
||||||
@@ -245,6 +262,8 @@ TODO/FIXME обязан содержать target/work-package ID или diagnos
|
|||||||
- Можно ли убрать abstraction без потери correctness/boundary?
|
- Можно ли убрать abstraction без потери correctness/boundary?
|
||||||
- Есть ли скрытый mutable state или side effect?
|
- Есть ли скрытый mutable state или side effect?
|
||||||
- Не появился ли второй источник истины?
|
- Не появился ли второй источник истины?
|
||||||
|
- Все ли cross-space world coordinates проходят через `CoordinateMapper` или
|
||||||
|
документированную native boundary?
|
||||||
- Измерена ли сложная optimization?
|
- Измерена ли сложная optimization?
|
||||||
- Изолирована и документирована ли неизбежная сложность?
|
- Изолирована и документирована ли неизбежная сложность?
|
||||||
- Соответствуют ли API docs и diagrams фактическому коду?
|
- Соответствуют ли API docs и diagrams фактическому коду?
|
||||||
|
|||||||
+19
-1
@@ -10,7 +10,10 @@
|
|||||||
.\tools\run_render_baseline.ps1
|
.\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
|
```powershell
|
||||||
.\tools\run_render_baseline.ps1 -DryRun -WaitSeconds 0.1 -MeasureSeconds 0.1
|
.\tools\run_render_baseline.ps1 -DryRun -WaitSeconds 0.1 -MeasureSeconds 0.1
|
||||||
@@ -18,6 +21,21 @@
|
|||||||
|
|
||||||
Если `godot` отсутствует в `PATH`, скрипт использует локальный executable, указанный в `RENDER.md`. Другой executable задаётся через `-GodotPath`.
|
Если `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`:
|
Результат по умолчанию находится в `user://render_baseline`:
|
||||||
|
|
||||||
- `report.json` — Godot, OS, CPU, GPU/backend, viewport, revision, cache inventory, cache versions, параметры checkpoint-ов и результаты;
|
- `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,31 @@
|
|||||||
| Module | Current status | Primary specification |
|
| Module | Current status | Primary specification |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Foundation/data | Planned/Partial | [`../../targets/roadmap/01-foundation-and-data.md`](../../targets/roadmap/01-foundation-and-data.md) |
|
| 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) |
|
||||||
|
| Terrain quality Mesh cache | Implemented extraction | [`terrain-quality-mesh-cache.md`](terrain-quality-mesh-cache.md) |
|
||||||
|
| Terrain chunk LOD planner | Implemented extraction | [`terrain-chunk-lod-planner.md`](terrain-chunk-lod-planner.md) |
|
||||||
|
| Terrain chunk geometry queue planner | Implemented extraction | [`terrain-chunk-geometry-queue-planner.md`](terrain-chunk-geometry-queue-planner.md) |
|
||||||
|
| ADT water load pipeline state | Implemented extraction | [`adt-water-load-pipeline-state.md`](adt-water-load-pipeline-state.md) |
|
||||||
|
| M2 unique placement registry | Implemented extraction | [`m2-unique-placement-registry.md`](m2-unique-placement-registry.md) |
|
||||||
|
| M2 placement transform resolver | Implemented | [`m2-placement-transform-resolver.md`](m2-placement-transform-resolver.md) |
|
||||||
|
| M2 placement grouper | Implemented extraction | [`m2-placement-grouper.md`](m2-placement-grouper.md) |
|
||||||
|
| M2 build batch planner | Implemented extraction | [`m2-build-batch-planner.md`](m2-build-batch-planner.md) |
|
||||||
|
| WMO placement resolver | Implemented extraction | [`wmo-placement-resolver.md`](wmo-placement-resolver.md) |
|
||||||
|
| WMO placement registry | Implemented extraction | [`wmo-placement-registry.md`](wmo-placement-registry.md) |
|
||||||
|
| WMO render build step planner | Implemented extraction | [`wmo-render-build-step-planner.md`](wmo-render-build-step-planner.md) |
|
||||||
|
| WMO render build queue | Implemented extraction | [`wmo-render-build-queue.md`](wmo-render-build-queue.md) |
|
||||||
|
| WMO render Resource cache state | Implemented extraction | [`wmo-render-resource-cache-state.md`](wmo-render-resource-cache-state.md) |
|
||||||
|
| WMO scene Resource cache state | Implemented extraction | [`wmo-scene-resource-cache-state.md`](wmo-scene-resource-cache-state.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) |
|
| Renderer | Partial | [`world-renderer.md`](world-renderer.md), [`../../RENDER.md`](../../RENDER.md) |
|
||||||
|
| World entity presentation | Implemented boundary / Prototype visuals | [`world-entity-presentation.md`](world-entity-presentation.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) |
|
| 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) |
|
| 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) |
|
| UI/Lua/audio | Prototype/Planned | [`../../targets/roadmap/05-ui-lua-audio.md`](../../targets/roadmap/05-ui-lua-audio.md) |
|
||||||
|
|||||||
@@ -0,0 +1,247 @@
|
|||||||
|
# ADT Water Load Pipeline State
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented |
|
||||||
|
| Target/work package | M03 / `M03-RND-ADT-WATER-LOAD-PIPELINE-001` |
|
||||||
|
| Owners | ADT water pending FIFO/dedupe, active task IDs and worker-safe result mailbox |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-adt-water-load-pipeline`, 2026-07-17 |
|
||||||
|
| Profiles/capabilities | ADT MH2O/MCLQ water loading; profile-independent state |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Own the cross-frame and cross-thread bookkeeping for asynchronous ADT water
|
||||||
|
loads outside `StreamingWorldLoader`: FIFO requests, queued-key deduplication,
|
||||||
|
opaque active task IDs and parsed-result delivery. The loader retains task
|
||||||
|
execution, parsing, budgets, stale-result decisions and scene finalization.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Start, cancel or wait WorkerThreadPool tasks.
|
||||||
|
- Instantiate `ADTLoader` or interpret MH2O/MCLQ data.
|
||||||
|
- Select concurrency limits or consume render permits.
|
||||||
|
- Create, attach or free Nodes, Meshes, materials or RIDs.
|
||||||
|
- Change liquid visuals, coordinates, tile acceptance or loaded-state timing.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Tile[Loaded tile needing water] --> Loader[StreamingWorldLoader]
|
||||||
|
Loader --> State[AdtWaterLoadPipelineState]
|
||||||
|
State --> Request[Oldest tile/path request]
|
||||||
|
Request --> Loader
|
||||||
|
Loader --> Worker[WorkerThreadPool ADTLoader task]
|
||||||
|
Worker --> State
|
||||||
|
State --> Result[Parsed ADT result]
|
||||||
|
Result --> Loader
|
||||||
|
Loader --> Budget[WATER_FINALIZE permit]
|
||||||
|
Loader --> Builder[ADTBuilder water scene]
|
||||||
|
Builder --> Scene[Main-thread tile Node attach]
|
||||||
|
```
|
||||||
|
|
||||||
|
Allowed dependencies are RefCounted, Dictionary/Array/String and one Godot
|
||||||
|
`Mutex` around results. WorkerThreadPool, ADTLoader, scheduler, tile state,
|
||||||
|
scene-tree/render resources and gameplay/editor/network are forbidden.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `enqueue_request(tile_key, adt_path)` | Command/query | Append deduplicated FIFO request | Renderer main thread; until dequeue/cancel/clear | Empty/queued/active returns false |
|
||||||
|
| `take_next_request()` | Command/query | Pop oldest request and release queued marker | Renderer main thread | Empty returns `{}` |
|
||||||
|
| `cancel_pending_requests(tile_key)` | Command/query | Remove all matching pending entries | Tile release/main thread | Empty/unknown removes zero; active unaffected |
|
||||||
|
| `remember_active_task(tile_key, task_id)` | Command/query | Retain loader-created opaque task ID | Main thread; until completion/clear | Empty/duplicate returns false |
|
||||||
|
| `has_active_task` / `active_task_id_for` | Query | Test/borrow active task identity | Main thread | Unknown ID is `-1` |
|
||||||
|
| `complete_active_task(tile_key)` | Command/query | Remove and return completed task ID | Main thread after wait | Unknown returns `-1` |
|
||||||
|
| `active_task_ids_snapshot()` | Query | Copy IDs for orderly shutdown waiting | Main thread | Detached Array |
|
||||||
|
| `publish_result(tile_key, path, data)` | Command/query | Append parsed result under mutex | Worker or main thread | Empty key/path returns false |
|
||||||
|
| `has_result()` / `pop_result()` | Query/command | Observe/pop oldest result under mutex | Main-thread drain | Empty pop returns `{}` |
|
||||||
|
| `total_load_count()` | Query | Pending plus active metric | Main thread | Results intentionally excluded |
|
||||||
|
| `pending_request_count()` / `active_task_count()` | Query | Support loader concurrency loop | Main thread | None |
|
||||||
|
| `clear()` | Command | Release all bookkeeping and results | Main thread after wait or map reset | Does not interrupt workers |
|
||||||
|
| `diagnostic_snapshot()` | Query | Return detached scalar requests/tasks/results | Main thread; result mailbox locked | Water data omitted |
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Tile key and ADT path | Loader tile state | Pending FIFO | Copied Strings | Until dequeue/cancel/clear |
|
||||||
|
| Input | Opaque task ID | Loader WorkerThreadPool adapter | Active task map | Integer value | Until wait/completion/clear |
|
||||||
|
| Input | Tile/path/parsed ADT Dictionary | Worker parser adapter | Result mailbox | Dictionary reference retained | Until pop/clear |
|
||||||
|
| Output | Oldest request Dictionary | Pipeline state | Loader task-start adapter | Fresh record from FIFO | One start attempt |
|
||||||
|
| Output | Active task ID snapshot | Pipeline state | Loader shutdown wait | Detached Array | Shutdown loop |
|
||||||
|
| Output | Oldest result Dictionary | Pipeline state | Loader finalize adapter | Mailbox record transferred | One finalize attempt |
|
||||||
|
| Output | Scalar diagnostics | Pipeline state | Verifier/future metrics | Detached records | Snapshot lifetime |
|
||||||
|
|
||||||
|
Side effects are limited to collection mutation, reference retention and result
|
||||||
|
mutex locking. Parsing, permits and scene mutation remain outside the module.
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Enqueue[enqueue tile/path] --> Duplicate{Queued or active?}
|
||||||
|
Duplicate -->|yes| Reject[false; unchanged]
|
||||||
|
Duplicate -->|no| FIFO[Append FIFO and queued key]
|
||||||
|
FIFO --> Take[take next; release queued marker]
|
||||||
|
Take --> Validate[Loader checks tile/path]
|
||||||
|
Validate --> Start[Loader starts worker and remembers task ID]
|
||||||
|
Start --> Parse[Worker parses ADT]
|
||||||
|
Parse --> Publish[Mutex-protected publish result]
|
||||||
|
Publish --> Permit[Loader obtains WATER_FINALIZE permit]
|
||||||
|
Permit --> Pop[Pop oldest result]
|
||||||
|
Pop --> Wait[Loader waits task and completes active ID]
|
||||||
|
Wait --> Stale{Tile/path/root current?}
|
||||||
|
Stale -->|no| Discard[Discard result]
|
||||||
|
Stale -->|yes| Attach[Build and attach water scene]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle/state
|
||||||
|
|
||||||
|
A tile request progresses through `Absent`, `Queued`, `Active` and
|
||||||
|
`ResultReady`; the loader wait/completion returns bookkeeping to `Absent` before
|
||||||
|
validating/attaching the result. Queued tile release cancels only `Queued`.
|
||||||
|
Active work is not interrupted; a later stale result is drained and discarded.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> Absent
|
||||||
|
Absent --> Queued: accepted enqueue
|
||||||
|
Queued --> Absent: pending cancellation or invalid dequeue
|
||||||
|
Queued --> Active: loader starts task
|
||||||
|
Active --> ResultReady: worker publishes result
|
||||||
|
ResultReady --> Absent: loader waits and completes task
|
||||||
|
Queued --> Absent: clear
|
||||||
|
Active --> Absent: clear bookkeeping only
|
||||||
|
ResultReady --> Absent: clear
|
||||||
|
```
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Loader as StreamingWorldLoader main thread
|
||||||
|
participant State as AdtWaterLoadPipelineState
|
||||||
|
participant Worker as WorkerThreadPool task
|
||||||
|
participant Budget as RenderBudgetScheduler
|
||||||
|
participant Builder as ADTBuilder
|
||||||
|
Loader->>State: enqueue_request(tile key, ADT path)
|
||||||
|
Loader->>State: take_next_request()
|
||||||
|
Loader->>Worker: add_task(parse binding)
|
||||||
|
Loader->>State: remember_active_task(key, task ID)
|
||||||
|
Worker->>Worker: ADTLoader.load_adt(path)
|
||||||
|
Worker->>State: publish_result(key, path, data)
|
||||||
|
Note over State: result mutex protects publication/pop
|
||||||
|
Loader->>Budget: try_consume_permit(WATER_FINALIZE)
|
||||||
|
Loader->>State: pop_result()
|
||||||
|
Loader->>Worker: wait_for_task_completion(task ID)
|
||||||
|
Loader->>State: complete_active_task(key)
|
||||||
|
Loader->>Loader: reject stale tile/path/root
|
||||||
|
Loader->>Builder: build_tile_water_scene(data, origin)
|
||||||
|
Loader->>Loader: attach Node and mark water_loaded
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependency diagram
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
Loader[StreamingWorldLoader] --> State[AdtWaterLoadPipelineState]
|
||||||
|
Loader --> Scheduler[RenderBudgetScheduler]
|
||||||
|
Loader --> Worker[WorkerThreadPool / ADTLoader]
|
||||||
|
Loader --> Builder[ADTBuilder / SceneTree]
|
||||||
|
Worker -->|publish only| State
|
||||||
|
State -. no dependency .-> Scheduler
|
||||||
|
State -. no dependency .-> Builder
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- Main thread owns pending and active-task collection mutation.
|
||||||
|
- Worker threads may call only `publish_result`; result observe/pop/clear also lock.
|
||||||
|
- Parsed water Dictionary identity is retained until main-thread pop.
|
||||||
|
- Loader owns WorkerThreadPool task lifetime and must wait before orderly shutdown clear.
|
||||||
|
- Loader owns tile states, ADTBuilder, water Nodes and every render resource.
|
||||||
|
- `clear` cannot cancel a worker; post-reset publication becomes a stale result
|
||||||
|
and is discarded because no active task/tile ownership remains.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Empty/duplicate request | Enqueue guards | Reject unchanged | Contract verifier | Correct caller state |
|
||||||
|
| Tile removed while queued | Loader release | Remove matching pending requests | Source/lifecycle verifier | Reload can enqueue later |
|
||||||
|
| Tile removed while active | Loader stale check after result | Wait task, remove active ID, discard result | Existing metrics | Reload can enqueue later |
|
||||||
|
| Parser unavailable/empty | Worker publishes empty Dictionary | Loader marks `water_loaded` without Node | Existing behavior | Future tile reload |
|
||||||
|
| Path changed | Loader compares result path | Discard after task completion | Source regression | Current tile can request again |
|
||||||
|
| Shutdown | Loader snapshots/waits active IDs | Clear all bookkeeping/results | Shutdown/source verifier | New loader starts empty |
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `enable_water` | Loader setting | Quality/custom | Yes | Gates enqueue/process, unchanged |
|
||||||
|
| `max_concurrent_water_tasks` | Loader setting | Quality/custom | Yes | Bounds active tasks outside state |
|
||||||
|
| `water_finalize_ops_per_tick` | Loader setting | Quality/custom | Yes | Bounds result pops via scheduler |
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
No state is serialized or cached. ADT source/cache formats and liquid material
|
||||||
|
versions are unchanged; no migration or rebuild is required.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
- `total_load_count` preserves the prior pending-plus-active `water` metric.
|
||||||
|
- Snapshot exposes FIFO tile/path, sorted active keys and result tile/path only.
|
||||||
|
- Parsed liquid payloads, Nodes and task internals are not exposed.
|
||||||
|
- No logs or correlation IDs are introduced.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `verify_adt_water_load_pipeline_state.gd`: invalid/dedupe/FIFO behavior,
|
||||||
|
active IDs, payload identity, real Thread publication, pending cancellation,
|
||||||
|
clear, detached diagnostics, loader boundaries and bounded timing.
|
||||||
|
- Existing material/shutdown/facade/streaming regressions cover adjacent behavior.
|
||||||
|
- Fidelity evidence is preservation of current scheduling/finalize transitions;
|
||||||
|
no MH2O/MCLQ visual or original-client parity claim is made.
|
||||||
|
- Performance budget: 100 cycles of 256 request/task/result transitions under 1 second.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
- ADT parse execution can later move behind a parser/job adapter without changing state.
|
||||||
|
- Water scene materialization can be extracted separately because it owns Nodes
|
||||||
|
and main-thread effects rather than async bookkeeping.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| ADT water load pipeline state | Implemented extraction | FIFO/task/thread/source/timing verifier | Asset-backed traversal/leak evidence pending |
|
||||||
|
| ADT liquid parsing | Existing/loader-owned adapter | Native ADTLoader and dry-run paths | MH2O/MCLQ fidelity fixtures pending |
|
||||||
|
| Water scene finalization | Existing/loader-owned | Scheduler/material/shutdown regressions | Separate materialization extraction possible |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- `clear` intentionally does not interrupt active WorkerThreadPool work.
|
||||||
|
- Result payload Dictionaries are retained by reference and must not be mutated after publication.
|
||||||
|
- Result availability and pop use separate mutex acquisitions; one serialized
|
||||||
|
main-thread consumer preserves ordering, but the API is not a multi-consumer queue.
|
||||||
|
- No proprietary liquid corpus, traversal/leak run, p95/p99 or paired capture is included.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/liquid/adt_water_load_pipeline_state.gd` | Request/task/result bookkeeping and result mutex |
|
||||||
|
| `src/scenes/streaming/streaming_world_loader.gd` | Parse task execution, permits, stale checks and Node finalization |
|
||||||
|
| `addons/mpq_extractor/loaders/adt_builder.gd` | ADT liquid geometry/material construction |
|
||||||
|
| `src/tools/verify_adt_water_load_pipeline_state.gd` | Lifecycle/thread/boundary/timing regression |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
- [`render-budget-scheduler.md`](render-budget-scheduler.md)
|
||||||
|
- [`../../RENDER.md`](../../RENDER.md)
|
||||||
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.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,187 @@
|
|||||||
|
# M2 Build Batch Planner
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented |
|
||||||
|
| Target/work package | M03 / `M03-RND-M2-BUILD-BATCH-PLANNER-001` |
|
||||||
|
| Owners | Pure static/animated M2 batch sizing and group cursor progression |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-m2-build-batch-planner`, 2026-07-17 |
|
||||||
|
| Profiles/capabilities | Existing static MultiMesh and animated-instance build paths |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Select the effective per-tick M2 batch limit, cap it to remaining transforms and
|
||||||
|
plan the next group offset without accessing loader state or renderer resources.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Own M2 build jobs, queues, tile state, retries or cancellation.
|
||||||
|
- Decide whether an animated prototype or static mesh is ready.
|
||||||
|
- Rotate queues or consume `RenderBudgetScheduler` permits.
|
||||||
|
- Create or modify Nodes, MultiMesh, Mesh, materials or RIDs.
|
||||||
|
- Introduce spatial cells or change profile batch limits.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Groups[M2PlacementGrouper transform arrays] --> Loader[Loader build loop]
|
||||||
|
Limits[Animated/static batch limits] --> Planner[M2BuildBatchPlanner]
|
||||||
|
Loader --> Planner
|
||||||
|
Planner --> Plan[Detached batch plan]
|
||||||
|
Plan --> Loader
|
||||||
|
Loader --> Ready{Resource ready?}
|
||||||
|
Ready --> Materialize[Animated or MultiMesh materialization]
|
||||||
|
Loader --> Budget[RenderBudgetScheduler permit]
|
||||||
|
```
|
||||||
|
|
||||||
|
Allowed dependencies are integer/boolean scalar math and a fresh Dictionary.
|
||||||
|
Node, SceneTree, RenderingServer, ResourceLoader, WorkerThreadPool, mutexes,
|
||||||
|
renderer caches, gameplay, network, editor UI and files are forbidden.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `plan_batch(transform_count, current_offset, has_animated_prototype, animated_batch_limit, static_batch_limit)` | Pure query | Return the selected batch size/count and next group cursor | Any thread; call-local result | Non-positive selected limit clamps to one; empty/past-end range completes |
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Transform count | Grouped transform array | Planner remaining-count formula | Scalar copy | One plan |
|
||||||
|
| Input | Current group offset | Loader build job | Planner cursor formula | Scalar copy | One plan |
|
||||||
|
| Input | Animated prototype availability | Loader resource selection | Planner limit selector | Scalar copy | One plan |
|
||||||
|
| Input | Animated/static raw limits | Renderer profile/exports | Planner clamp | Scalar copy | One plan |
|
||||||
|
| Output | `effective_batch_size` | Planner | Tests/diagnostics | Detached integer | One plan |
|
||||||
|
| Output | `batch_count` | Planner | Loader materialization adapter | Detached integer | One plan |
|
||||||
|
| Output | `next_offset` | Planner | Loader build-job cursor | Detached integer | Until next batch |
|
||||||
|
| Output | `group_complete` | Planner | Loader group-index progression | Detached boolean | One plan |
|
||||||
|
|
||||||
|
The output Dictionary is newly allocated and caller-owned. The planner retains
|
||||||
|
no reference to it or any input.
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Select{Animated prototype?} -->|yes| Animated[animated_batch_limit]
|
||||||
|
Select -->|no| Static[static_batch_limit]
|
||||||
|
Animated --> Clamp[max 1]
|
||||||
|
Static --> Clamp
|
||||||
|
Clamp --> Remaining[max 0 of transform_count - current_offset]
|
||||||
|
Remaining --> Count[min effective limit, remaining]
|
||||||
|
Count --> Complete{count <= 0 or offset + count >= total?}
|
||||||
|
Complete -->|yes| Done[group_complete true; next_offset 0]
|
||||||
|
Complete -->|no| Continue[group_complete false; next_offset offset + count]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle/state
|
||||||
|
|
||||||
|
The planner is stateless. It has no initialization, reset, cancellation,
|
||||||
|
recovery or shutdown work. Each call returns a complete detached value plan.
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Loader as StreamingWorldLoader build loop
|
||||||
|
participant Planner as M2BuildBatchPlanner
|
||||||
|
participant Materializer as Animated/MultiMesh adapter
|
||||||
|
participant Scheduler as RenderBudgetScheduler
|
||||||
|
Loader->>Planner: plan_batch(count, offset, path kind, limits)
|
||||||
|
Planner-->>Loader: batch_count, next_offset, group_complete
|
||||||
|
Loader->>Loader: resolve/request prototype or mesh
|
||||||
|
Loader->>Materializer: materialize selected transform slice
|
||||||
|
Loader->>Loader: adopt cursor/group completion
|
||||||
|
Loader->>Scheduler: consume M2_BUILD permit
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- The planner owns only call-local scalar values and the returned Dictionary.
|
||||||
|
- The loader owns build-job Dictionaries, queue ordering, tile checks, resource
|
||||||
|
readiness/retry, serial numbers and group-index mutation.
|
||||||
|
- Materializers own main-thread Node/MultiMesh construction under loader roots.
|
||||||
|
- The scheduler owns the frame-local `M2_BUILD` counter.
|
||||||
|
- Pure planning is thread-safe, though the current adapter calls it on main thread.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Zero/negative selected limit | Scalar clamp | Effective size becomes one | Contract fixture | Correct profile value later |
|
||||||
|
| Empty transform group | Remaining count zero | Count zero; group completes | Contract fixture | Loader advances group |
|
||||||
|
| Offset at/past end | Remaining count clamp | Count zero; offset resets | Contract fixture | Loader advances group |
|
||||||
|
| Negative offset | No new validation | Historical arithmetic is preserved | Contract fixture | Loader inputs should remain valid |
|
||||||
|
| Resource pending/missing | Outside planner | Loader rotates or advances as before | Existing cache/build diagnostics | Resource loader retry/fallback |
|
||||||
|
| Tile cancelled | Outside planner | Loader cancels job before planning/materialization | Existing lifecycle gates | Eligible tile may requeue |
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
The planner introduces no setting. It consumes existing
|
||||||
|
`m2_animated_instances_per_tick` and `m2_multimesh_batch_size` values after the
|
||||||
|
loader determines whether an animated prototype is available.
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
No persistence, schema or cache format participates. ADT/M2 cache versions,
|
||||||
|
quality presets and rebuild instructions are unchanged.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
The planner emits no logs. Its verifier covers deterministic formula/source
|
||||||
|
boundaries and records bounded 20,000-plan timing. Existing loader metrics retain
|
||||||
|
queue depth, build activity and hitch observability.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `verify_m2_build_batch_planner.gd`: static/animated selection, invalid-limit
|
||||||
|
clamp, remaining cap, exact completion, empty/past-end and negative-offset
|
||||||
|
behavior, loader/dependency source boundaries and bounded timing.
|
||||||
|
- Grouper, transform resolver, unique registry/dedupe, facade, internal-access,
|
||||||
|
manifest, shutdown, scheduler, streaming, coordinate, documentation and
|
||||||
|
coordination regressions remain required.
|
||||||
|
- Fidelity evidence is formula extraction only. Asset-backed frame pacing and
|
||||||
|
visual output require the renderer checkpoint/performance workflow.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
- A later package may define typed build-job state once queue/resource transitions
|
||||||
|
are extracted together with explicit cancellation and recovery.
|
||||||
|
- Spatial-cell batching must use measured culling/performance evidence and must
|
||||||
|
not silently change this model-path batch cursor.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Static/animated batch cursor planning | Implemented extraction | Contract/source/timing verifier | Asset-backed p95/p99 pending |
|
||||||
|
| Build queue/resource state machine | Remains in loader | Existing lifecycle regressions | Stateful extraction pending |
|
||||||
|
| Spatial-cell batching | Planned | Renderer roadmap | Culling evidence/design pending |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- The result remains a Dictionary while loader build jobs are untyped Dictionaries.
|
||||||
|
- Negative offsets are preserved rather than rejected to avoid a hidden behavior change.
|
||||||
|
- Planning timing does not measure resource loading or materialization.
|
||||||
|
- Private asset-backed p95/p99 and visual comparison remain unavailable.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/m2/m2_build_batch_planner.gd` | Pure limit/count/cursor planning |
|
||||||
|
| `src/scenes/streaming/streaming_world_loader.gd` | Build job, queue, resources, materialization and budgets |
|
||||||
|
| `src/render/m2/m2_placement_grouper.gd` | Produces grouped transform arrays |
|
||||||
|
| `src/tools/verify_m2_build_batch_planner.gd` | Formula, source and timing regression |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`m2-placement-grouper.md`](m2-placement-grouper.md)
|
||||||
|
- [`m2-placement-transform-resolver.md`](m2-placement-transform-resolver.md)
|
||||||
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
- [`../../RENDER.md`](../../RENDER.md)
|
||||||
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
# M2 Placement Grouper
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented extraction |
|
||||||
|
| Target/work package | M03 / `M03-RND-M2-PLACEMENT-GROUPER-001` |
|
||||||
|
| Owners | Pure ADT M2 placement validation, normalization and transform grouping |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-m2-placement-grouper`, 2026-07-16 |
|
||||||
|
| Profiles/capabilities | Existing static M2 worker-grouping path |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Convert valid raw ADT M2 placement records into ordered tile-local transforms
|
||||||
|
grouped by the historically normalized relative model path.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Reserve cross-tile unique IDs or choose placement ownership.
|
||||||
|
- Own WorkerThreadPool tasks, mutexes, result queues or tile lifecycle.
|
||||||
|
- Load models, choose animated/static rendering or build MultiMesh batches.
|
||||||
|
- Create Node, Mesh, material, Resource or RID objects.
|
||||||
|
- Change path case, transform rules, batching or visual fidelity.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
ADT[ADT names + placements] --> Registry[M2UniquePlacementRegistry]
|
||||||
|
Registry --> Grouper[M2PlacementGrouper]
|
||||||
|
Grouper --> Resolver[M2PlacementTransformResolver]
|
||||||
|
Resolver --> Grouper
|
||||||
|
Grouper --> Groups[Path to ordered Transform3D arrays]
|
||||||
|
Groups --> Worker[Loader worker result adapter]
|
||||||
|
Worker --> Build[Loader-owned build queue]
|
||||||
|
```
|
||||||
|
|
||||||
|
Allowed dependencies are value containers, `Vector3`, `Basis`, `Transform3D`
|
||||||
|
and `M2PlacementTransformResolver`. SceneTree, Node, RenderingServer,
|
||||||
|
WorkerThreadPool, mutexes, ResourceLoader, files, gameplay, network and editor UI
|
||||||
|
are forbidden.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `group_placements(tile_origin, m2_names, m2_placements)` | Pure query | Return valid placements grouped as ordered tile-local transforms | Worker/main thread; call-local output | Invalid variants/name IDs/empty paths are skipped |
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Godot-space tile origin | Loader tile state | Local-position calculation | Value copy | One call |
|
||||||
|
| Input | ADT MMDX name table | Parsed ADT tile | Path lookup | Read-only PackedStringArray | One call |
|
||||||
|
| Input | Raw placement dictionaries | Unique registry result | Validation/transforms | Read-only shallow values | One call |
|
||||||
|
| Output | Relative path to ordered `Transform3D` arrays | Grouper | Loader worker/build adapter | Fresh Dictionary and arrays | One call/build job |
|
||||||
|
|
||||||
|
Output retains first-seen group-key order through Godot Dictionary insertion
|
||||||
|
order and placement order inside each array. Inputs are not retained or returned.
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Call[group_placements] --> Next[Read placement]
|
||||||
|
Next --> Dict{Dictionary?}
|
||||||
|
Dict -->|no| Next
|
||||||
|
Dict -->|yes| Name{Valid name_id?}
|
||||||
|
Name -->|no| Next
|
||||||
|
Name -->|yes| Normalize[Backslash to slash; lowercase MDX/MDL to M2]
|
||||||
|
Normalize --> Empty{Path empty?}
|
||||||
|
Empty -->|yes| Next
|
||||||
|
Empty -->|no| Local[world position - tile origin]
|
||||||
|
Local --> Resolve[Resolve basis and origin offset]
|
||||||
|
Resolve --> Scale[Apply max scale 0.0001]
|
||||||
|
Scale --> Append[Append Transform3D to path group]
|
||||||
|
Append --> Next
|
||||||
|
Next -->|complete| Return[Return fresh groups]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle/state
|
||||||
|
|
||||||
|
The grouper retains no state. Every call constructs one stateless transform
|
||||||
|
resolver and fresh output containers. Reset, cancellation and shutdown require
|
||||||
|
no grouper action.
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Loader as Loader worker callback
|
||||||
|
participant Grouper as M2PlacementGrouper
|
||||||
|
participant Resolver as M2PlacementTransformResolver
|
||||||
|
Loader->>Grouper: group_placements(origin, names, placements)
|
||||||
|
loop valid placement
|
||||||
|
Grouper->>Resolver: resolve basis and origin offset
|
||||||
|
Resolver-->>Grouper: value-only basis and offset
|
||||||
|
Grouper->>Grouper: append local Transform3D
|
||||||
|
end
|
||||||
|
Grouper-->>Loader: fresh path groups
|
||||||
|
Loader->>Loader: mutex-protected result enqueue
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- The grouper owns only call-local containers and transforms.
|
||||||
|
- The loader owns tasks, mutex/result queue, tile/build state, caches, model
|
||||||
|
loading, Nodes, MultiMesh, Mesh, materials and RIDs.
|
||||||
|
- Worker use is safe because the grouper and resolver mutate no shared state.
|
||||||
|
- The caller owns returned containers; mutation cannot affect later calls.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Non-Dictionary placement | Type guard | Skip | Contract fixture | Repair parser input |
|
||||||
|
| Invalid name ID | Bounds guard | Skip | Contract fixture | Repair name table/placement |
|
||||||
|
| Empty path | Empty guard | Skip | Contract fixture | Supply valid MMDX entry |
|
||||||
|
| Missing position/rotation/scale | Defaults | Zero position/rotation and scale 1 | Contract fixture | Expected fallback |
|
||||||
|
| Zero/negative scale | Historical clamp | Basis scale uses `0.0001` | Contract fixture | Supply valid ADT scale |
|
||||||
|
| Cancellation after grouping | Loader state check | Discard stale result | Loader regressions | Re-request eligible detail |
|
||||||
|
|
||||||
|
Cancellation stays loader-owned because grouping has no task or partial state.
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
No setting or profile branch is introduced. Preserved behavior includes
|
||||||
|
backslash replacement without lowercasing, case-sensitive lowercase `.mdx/.mdl`
|
||||||
|
conversion, first-seen ordering, tile-local subtraction and scale clamp `0.0001`.
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
No persistence or cache format participates. ADT and M2 cache versions are
|
||||||
|
unchanged, so no rebuild or migration is required.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
The service emits no logs. Its verifier records contract results, source
|
||||||
|
boundaries and bounded synthetic 100-by-256 placement timing. Loader metrics
|
||||||
|
continue to own task/build/cache observability.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `verify_m2_placement_grouper.gd`: invalid input, path behavior, ordering,
|
||||||
|
defaults, local origin, regular/waterfall transforms, scale clamp, fresh output,
|
||||||
|
loader/source boundary and timing.
|
||||||
|
- `verify_m2_placement_transform_resolver.gd`: three transform consumers remain
|
||||||
|
covered as two direct loader adapters plus the grouper adapter.
|
||||||
|
- Registry/dedupe, facade, internal-access, manifest, shutdown, documentation and
|
||||||
|
coordination regressions remain required.
|
||||||
|
- Fidelity evidence is exact extraction; no new asset-backed parity claim is made.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
- `M2BuildBatchPlanner` now owns pure batch sizing/cursor progression; the
|
||||||
|
remaining build-job state machine stays behind explicit loader budgets.
|
||||||
|
- Spatial cells require measured culling evidence and are outside this contract.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Pure path/transform grouping | Implemented extraction | Contract/source/timing verifier | Asset-backed traversal timing pending |
|
||||||
|
| Worker/build orchestration | Remains in loader | Existing regressions | Separate stateful extraction required |
|
||||||
|
| Spatial MultiMesh cells | Partial | Current path batches | Culling-driven design/evidence pending |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- Raw placement dictionaries remain the ADT parser boundary.
|
||||||
|
- Uppercase extensions remain unchanged even though transform exception matching
|
||||||
|
is case-insensitive.
|
||||||
|
- Grouping is by model path, not spatial culling cell.
|
||||||
|
- Asset-backed p95/p99 and visual comparison remain unavailable.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/m2/m2_placement_grouper.gd` | Validation, normalization and grouping |
|
||||||
|
| `src/render/m2/m2_placement_transform_resolver.gd` | Basis and origin rules |
|
||||||
|
| `src/render/m2/m2_build_batch_planner.gd` | Downstream batch sizing and cursor planning |
|
||||||
|
| `src/scenes/streaming/streaming_world_loader.gd` | Worker/result/build lifecycle and resources |
|
||||||
|
| `src/tools/verify_m2_placement_grouper.gd` | Contract, dependency and timing regression |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`m2-build-batch-planner.md`](m2-build-batch-planner.md)
|
||||||
|
- [`m2-placement-transform-resolver.md`](m2-placement-transform-resolver.md)
|
||||||
|
- [`m2-unique-placement-registry.md`](m2-unique-placement-registry.md)
|
||||||
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
- [`../../RENDER.md`](../../RENDER.md)
|
||||||
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
# M2 Placement Transform Resolver
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented |
|
||||||
|
| Target/work package | M03 / `M03-RND-M2-PLACEMENT-TRANSFORM-001` |
|
||||||
|
| Owners | Pure ADT M2 placement basis and model-specific origin compensation |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-m2-placement-transform`, 2026-07-16 |
|
||||||
|
| Profiles/capabilities | Existing static M2 paths; four calibrated model exceptions |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Resolve the unscaled Godot-space orientation and optional local origin offset
|
||||||
|
used by the pure placement grouper, placeholder rendering and instance creation.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Convert WoW/server/ADT coordinates or own placement positions.
|
||||||
|
- Apply final scale clamps, build transforms, group placements or create Nodes.
|
||||||
|
- Add, generalize or configure model-specific corrections.
|
||||||
|
- Change M2 materials, animation, caches, visibility or quality profiles.
|
||||||
|
- Claim general build-12340 placement parity.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
ADT[ADT placement rotation/path/scale] --> Resolver[M2PlacementTransformResolver]
|
||||||
|
Resolver --> Basis[Unscaled Basis]
|
||||||
|
Resolver --> Offset[Local origin offset]
|
||||||
|
Basis --> Group[M2PlacementGrouper]
|
||||||
|
Basis --> Placeholder[Placeholder MultiMesh path]
|
||||||
|
Basis --> Instance[Instance path]
|
||||||
|
Offset --> Group
|
||||||
|
Offset --> Placeholder
|
||||||
|
Offset --> Instance
|
||||||
|
Group --> Loader[StreamingWorldLoader-owned queues/build]
|
||||||
|
```
|
||||||
|
|
||||||
|
Allowed dependencies are `Vector3`, `Basis`, scalar math and path-string
|
||||||
|
normalization. Node, SceneTree, RenderingServer, ResourceLoader, tasks, caches,
|
||||||
|
files, gameplay, network and editor UI are forbidden.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `resolve_basis(rotation_radians, relative_m2_path)` | Pure query | Return unscaled regular or calibrated placement orientation | Worker/main thread; call-local result | Unknown paths use `Basis.from_euler` |
|
||||||
|
| `resolve_origin_offset(rotation_radians, relative_m2_path, scale_value)` | Pure query | Compensate the measured tall-waterfall twist anchor | Worker/main thread; call-local result | Regular/unanchored paths return zero; scale clamps to `0.0001` for compensation |
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Godot Euler rotation in radians | ADT loader placement record | Resolver | Scalar copy | One call |
|
||||||
|
| Input | Relative M2 path | ADT MMDX/name table | Resolver allowlist | Caller string | One call |
|
||||||
|
| Input | Raw placement scale | ADT placement record | Offset compensation | Scalar copy | One call |
|
||||||
|
| Output | Unscaled `Basis` | Resolver | Grouper/placeholder/instance adapter | Value copy | One call |
|
||||||
|
| Output | Local `Vector3` offset | Resolver | Grouper/placeholder/instance adapter | Value copy | One call |
|
||||||
|
|
||||||
|
Callers preserve their historical basis scale minima: grouping/instance use
|
||||||
|
`0.0001`, while placeholders use `0.01`.
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
BasisCall[resolve_basis] --> ModelName[Normalize separators/case/basename]
|
||||||
|
ModelName --> Waterfall{Waterfall sheet?}
|
||||||
|
Waterfall -->|yes| WaterfallBasis[+90 degree world yaw * Euler * local-axis twist]
|
||||||
|
Waterfall -->|no| Cliff{Elwynn cliff rock?}
|
||||||
|
Cliff -->|yes| CliffBasis[Corrected Y/BACK/RIGHT composition]
|
||||||
|
Cliff -->|no| Regular[Basis.from_euler]
|
||||||
|
OffsetCall[resolve_origin_offset] --> Anchored{Tall waterfall anchor?}
|
||||||
|
Anchored -->|no| Zero[Vector3.ZERO]
|
||||||
|
Anchored -->|yes| Compensation[(base anchor - twisted anchor) * clamped scale]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle/state
|
||||||
|
|
||||||
|
The resolver is stateless. It may be shared between the main thread and worker
|
||||||
|
calls because each result is computed solely from value inputs. Construction,
|
||||||
|
world reset and shutdown require no clear, cancellation or resource release.
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Caller as Grouper or StreamingWorldLoader
|
||||||
|
participant Resolver as M2PlacementTransformResolver
|
||||||
|
participant Consumer as Grouper/placeholder/instance path
|
||||||
|
Caller->>Resolver: resolve_basis(rotation, path)
|
||||||
|
Resolver-->>Caller: unscaled Basis
|
||||||
|
Caller->>Resolver: resolve_origin_offset(rotation, path, scale)
|
||||||
|
Resolver-->>Caller: local offset
|
||||||
|
Caller->>Consumer: compose historical scaled Transform3D
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- The resolver retains no mutable state or engine resources.
|
||||||
|
- The grouper owns worker-path final transforms; the loader owns placement records,
|
||||||
|
tasks, queues, caches and MultiMesh/Node/Mesh/material/RID creation and cleanup.
|
||||||
|
- Pure methods are safe for grouper worker use and main-thread consumers.
|
||||||
|
- Returned `Basis`/`Vector3` values have no lifetime coupling to the resolver.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Empty/unknown path | Allowlist miss | Regular Euler basis and zero offset | Contract fixture | Expected fallback |
|
||||||
|
| Mixed case/backslashes/MDX | Normalization | Same calibrated model match | Contract fixture | None required |
|
||||||
|
| Zero/negative scale for anchored offset | Numeric clamp | Use `0.0001` compensation scale | Contract fixture | Caller may supply valid ADT scale later |
|
||||||
|
| World reset/shutdown | No retained state | No action | Existing shutdown verifier | New calls remain independent |
|
||||||
|
|
||||||
|
There is no asynchronous operation or cancellation inside the resolver.
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
The resolver introduces no settings. The preserved constants and allowlists are:
|
||||||
|
|
||||||
|
- world yaw `PI` for `ElwynnCliffRock01/02`;
|
||||||
|
- world yaw `PI/2` for `NewWaterfall` and `ElwynnTallWaterfall01`;
|
||||||
|
- measured local axes/twist signs for both waterfall sheets;
|
||||||
|
- measured tall-waterfall anchor `(-2.667799, 89.62273, 0.00129)`.
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
No persistence, schema or cache format participates. Existing ADT/M2 cache
|
||||||
|
versions and rebuild instructions remain unchanged.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
The resolver emits no logs. The contract verifier compares formulas/constants,
|
||||||
|
checks two direct loader consumers plus the grouper consumer and records bounded
|
||||||
|
synthetic timing. Visual
|
||||||
|
and asset-backed evidence remains in the renderer checkpoint workflow.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `verify_m2_placement_transform_resolver.gd`: regular fallback, path
|
||||||
|
normalization, both cliff rocks, both waterfalls, twist anchor, negative-scale
|
||||||
|
compensation, three consumers, source boundary and 10,000-pair timing.
|
||||||
|
- M2 unique/dedupe, terrain, facade, internal-access, manifest, shutdown,
|
||||||
|
scheduler, streaming and coordinate regressions remain required.
|
||||||
|
- Fidelity evidence is exact extraction of existing calibrated formulas. The
|
||||||
|
private asset/camera comparison that motivated them is not reproduced here.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
- `M2PlacementGrouper` composes this resolver for the worker grouping path.
|
||||||
|
- New model corrections require separate measured fidelity evidence; this module
|
||||||
|
is not a generic override registry.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Consistent transform rules across three M2 paths | Implemented | Formula/source/timing contract | Asset-backed visual recheck pending |
|
||||||
|
| General ADT M2 placement parity | Partial | Existing checkpoint notes | Broader fixture coverage required |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- Four model-specific exceptions are empirical compatibility rules.
|
||||||
|
- Most placements use raw Godot Euler behavior; independent client-wide
|
||||||
|
transform validation is incomplete.
|
||||||
|
- Callers intentionally retain different final scale clamps.
|
||||||
|
- Grouping, build, cache and render ownership remain in the streamer.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/m2/m2_placement_transform_resolver.gd` | Pure basis/offset rules and calibrated allowlists |
|
||||||
|
| `src/render/m2/m2_placement_grouper.gd` | Grouping adapter and local Transform3D ownership |
|
||||||
|
| `src/scenes/streaming/streaming_world_loader.gd` | Two direct adapters and renderer resource ownership |
|
||||||
|
| `src/tools/verify_m2_placement_transform_resolver.gd` | Formula, source and performance regression |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`m2-placement-grouper.md`](m2-placement-grouper.md)
|
||||||
|
- [`m2-unique-placement-registry.md`](m2-unique-placement-registry.md)
|
||||||
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
- [`../../RENDER.md`](../../RENDER.md)
|
||||||
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
# M2 Unique Placement Registry
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented extraction |
|
||||||
|
| Target/work package | M03 / `M03-RND-M2-UNIQUE-REGISTRY-001` |
|
||||||
|
| Owners | Cross-tile positive ADT MDDF unique-ID reservations |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-m2-unique-registry`, 2026-07-16 |
|
||||||
|
| Profiles/capabilities | Existing static M2 placement path; session-memory only |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Ensure a positive ADT `MDDF.uniqueId` has at most one owning streamed tile while
|
||||||
|
allowing unkeyed placements through and returning skipped keys for loader-driven
|
||||||
|
candidate retry after the owner unloads.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Own tile state, retry queues, worker/group/build jobs or detail radius rules.
|
||||||
|
- Group placements or create MultiMesh, Mesh, Node, material or RID resources.
|
||||||
|
- Validate M2 paths, transforms, animation, materials or build-12340 fidelity.
|
||||||
|
- Persist reservations or define a generic identity/ownership framework.
|
||||||
|
- Change cache formats, quality profiles or placement visibility.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
ADT[ADT M2 placement dictionaries] --> Loader[StreamingWorldLoader adapter]
|
||||||
|
Loader --> Registry[M2UniquePlacementRegistry]
|
||||||
|
Registry --> Accepted[Filtered placements + owned keys]
|
||||||
|
Registry --> Skipped[Skipped keys]
|
||||||
|
Accepted --> Group[Existing worker grouping/build path]
|
||||||
|
Skipped --> TileState[Loader-owned tile retry state]
|
||||||
|
Unload[Owner tile unload] --> Loader
|
||||||
|
Loader -->|release keys| Registry
|
||||||
|
Loader -->|notify released candidates| Retry[Existing detail sync queue]
|
||||||
|
```
|
||||||
|
|
||||||
|
Allowed dependencies are scalar/container values from the ADT adapter. Node,
|
||||||
|
SceneTree, RenderingServer, ResourceLoader, WorkerThreadPool, gameplay, network,
|
||||||
|
editor UI, files and persistent caches are forbidden.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `reserve(tile_key, m2_placements)` | Command/query | Reserve available positive IDs and return fresh filtered/owned/skipped arrays | Renderer owner thread/session | Invalid non-Dictionary entries are ignored; unkeyed entries pass through |
|
||||||
|
| `release(tile_key, unique_keys)` | Command | Release only keys owned by the supplied tile | Renderer owner thread/session | Unknown/non-owner/empty keys are ignored |
|
||||||
|
| `clear()` | Lifecycle command | Drop every reservation on world reset/shutdown | Renderer owner thread | Idempotent |
|
||||||
|
| `active_count()` | Query | Return current reservation count | Renderer owner thread | No failure state |
|
||||||
|
| `diagnostic_snapshot()` | Read model | Return sorted detached key/tile ownership records | Caller-owned result | Exposes no mutable registry reference |
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Tile key | Streamer tile state | Registry reservation owner | Copied string | Reservation lifetime |
|
||||||
|
| Input | Raw M2 placement dictionaries | ADT parser result | Registry | Loader-owned; read only | One reserve call |
|
||||||
|
| Input | Claimed key array | Tile state | Registry release | Loader-owned; read only | One release call |
|
||||||
|
| Output | Filtered placement array | Registry | M2 grouping task adapter | Fresh shallow array; placement dictionaries remain source values | One request |
|
||||||
|
| Output | Owned `uid:<decimal>` keys | Registry | Loader tile state | Fresh strings/array | Until release |
|
||||||
|
| Output | Skipped keys | Registry | Loader candidate retry state | Fresh strings/array | Until owner release/retry |
|
||||||
|
| Output | Released keys | Registry | Loader candidate notifier | Fresh array in input order | One release |
|
||||||
|
| Output | Detached diagnostics/count | Registry | Metrics/tests | Caller-owned scalars/dictionaries | One query |
|
||||||
|
|
||||||
|
Only `unique_id > 0` enters the registry. Missing, zero and negative values have
|
||||||
|
an empty key and remain untracked, matching the prior loader behavior.
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Reserve[reserve tile + placements] --> Placement[Iterate placement order]
|
||||||
|
Placement --> Dict{Dictionary?}
|
||||||
|
Dict -->|no| Placement
|
||||||
|
Dict -->|yes| Positive{unique_id > 0?}
|
||||||
|
Positive -->|no| Pass[Append untracked placement]
|
||||||
|
Positive -->|yes| Owned{Key already owned?}
|
||||||
|
Owned -->|no| Claim[Set owner and append placement/key]
|
||||||
|
Owned -->|same tile + already in this call| Duplicate[Suppress duplicate]
|
||||||
|
Owned -->|same tile + first in this call| PassOwner[Append placement/key]
|
||||||
|
Owned -->|other tile| Skip[Append skipped key once]
|
||||||
|
Pass --> Placement
|
||||||
|
Claim --> Placement
|
||||||
|
Duplicate --> Placement
|
||||||
|
PassOwner --> Placement
|
||||||
|
Skip --> Placement
|
||||||
|
Release[release tile + keys] --> Owner{Tile is current owner?}
|
||||||
|
Owner -->|yes| Erase[Erase and return key]
|
||||||
|
Owner -->|no| Ignore[Leave reservation unchanged]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle/state
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> Empty
|
||||||
|
Empty --> Reserved: reserve positive unowned ID
|
||||||
|
Reserved --> Reserved: same-owner reserve / competing skip / non-owner release
|
||||||
|
Reserved --> Empty: owner releases final ID
|
||||||
|
Reserved --> Empty: clear world/reset
|
||||||
|
Empty --> Empty: unkeyed reserve / unknown release / clear
|
||||||
|
Empty --> [*]
|
||||||
|
Reserved --> [*]: owner releases registry after clear
|
||||||
|
```
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant TileA as First streamed tile
|
||||||
|
participant Loader as StreamingWorldLoader
|
||||||
|
participant Registry as M2UniquePlacementRegistry
|
||||||
|
participant TileB as Cross-border candidate tile
|
||||||
|
TileA->>Loader: placements include positive unique ID
|
||||||
|
Loader->>Registry: reserve(tile A, placements)
|
||||||
|
Registry-->>Loader: accepted placement + owned key
|
||||||
|
TileB->>Loader: same placement ID
|
||||||
|
Loader->>Registry: reserve(tile B, placements)
|
||||||
|
Registry-->>Loader: skipped key
|
||||||
|
Loader->>Loader: retain skipped key in tile B state
|
||||||
|
TileA->>Loader: unload/remove details
|
||||||
|
Loader->>Registry: release(tile A, owned keys)
|
||||||
|
Registry-->>Loader: released key
|
||||||
|
Loader->>Loader: enqueue eligible tile B detail retry
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- The registry owns one private key-to-owner-string dictionary.
|
||||||
|
- The loader owns tile-held owned/skipped arrays, retry discovery, task/build
|
||||||
|
lifecycle and every MultiMesh/Node/Mesh/material/RID side effect.
|
||||||
|
- Calls currently occur on the renderer main thread. The registry has no lock and
|
||||||
|
must not be mutated concurrently.
|
||||||
|
- Returned arrays and diagnostic dictionaries are fresh; source placement
|
||||||
|
dictionaries are passed through shallowly and must remain read-only.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Invalid placement variant | Type guard | Ignore entry | Contract test | Repair parser/adapter input |
|
||||||
|
| Missing/non-positive ID | Key rule | Pass placement without ownership | Smoke/contract test | Expected legacy behavior |
|
||||||
|
| Cross-tile duplicate | Owner lookup | Skip placement and return key once | Tile skipped-key state | Owner release triggers eligible candidate retry |
|
||||||
|
| Non-owner release | Owner comparison | Ignore; preserve owner | Contract test | Release from recorded tile |
|
||||||
|
| Reset/shutdown | Loader lifecycle | Clear all reservations | Shutdown regression/count | New world reserves afresh |
|
||||||
|
|
||||||
|
There is no asynchronous work or cancellation inside the registry. Loader-owned
|
||||||
|
tasks are cancelled independently before/reset around registry clearing.
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
The registry introduces no settings. It applies to all positive IDs in the
|
||||||
|
existing static M2 placement path. Detail radius, M2 enablement, build budgets
|
||||||
|
and animation allowlists remain loader/profile configuration.
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
Reservations are runtime-only and are not serialized. M2 `.tscn/.glb`, animation,
|
||||||
|
mesh/material refresh and ADT cache versions remain unchanged. No rebuild or
|
||||||
|
migration is required.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
- `active_count()` continues to feed `m2_active_unique_ids` baseline metrics.
|
||||||
|
- Detached diagnostics sort by unique key for deterministic inspection and expose
|
||||||
|
only strings/counts.
|
||||||
|
- Contract timing covers repeated 256-placement reserve/release cycles. Asset-
|
||||||
|
backed streaming p95/p99 remains milestone acceptance work.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `verify_m2_unique_placement_registry.gd`: invalid/unkeyed handling, within-call
|
||||||
|
duplicate suppression, same-owner repeat, cross-tile skip, owner-checked release,
|
||||||
|
retry, idempotent clear, detached/sorted diagnostics, source boundary and timing.
|
||||||
|
- `verify_m2_unique_dedupe.gd`: existing `uid:11785` cross-tile smoke now targets
|
||||||
|
the extracted public contract rather than loader-private methods.
|
||||||
|
- Facade, terrain, internal-access, manifest, shutdown, scheduler, streaming and
|
||||||
|
coordinate regressions remain required.
|
||||||
|
- Fidelity evidence is dedupe behavior preservation only. No new build-12340
|
||||||
|
visual parity claim is made.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
- Expose detached ownership diagnostics through facade metrics only if needed.
|
||||||
|
- Extract grouping/build orchestration as separate services with explicit budgets.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Cross-tile positive-ID ownership | Implemented extraction | Dedicated contract plus legacy smoke | Asset-backed boundary traversal timing pending |
|
||||||
|
| Candidate retry and M2 build | Remains in loader | Existing release adapter/source boundary | Broader M2 service extraction required |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- Placement dictionaries are shallow pass-through values at the ADT boundary.
|
||||||
|
- Same-owner reservations across separate calls intentionally pass again; the
|
||||||
|
caller's existing root/task guards prevent duplicate build requests.
|
||||||
|
- First owner follows tile processing order, matching prior behavior rather than
|
||||||
|
a canonical geometric owner rule.
|
||||||
|
- Group/build/load/animation caches and render finalization remain monolithic.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/m2/m2_unique_placement_registry.gd` | Positive-ID keying, ownership, release and diagnostics |
|
||||||
|
| `src/scenes/streaming/streaming_world_loader.gd` | Reservation adapters, tile state, candidate retry and M2 rendering |
|
||||||
|
| `src/tools/verify_m2_unique_placement_registry.gd` | Contract, lifecycle, source and timing regression |
|
||||||
|
| `src/tools/verify_m2_unique_dedupe.gd` | Historical cross-tile smoke fixture |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
- [`../../RENDER.md`](../../RENDER.md)
|
||||||
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.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,195 @@
|
|||||||
|
# Terrain Chunk Geometry Queue Planner
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented extraction |
|
||||||
|
| Target/work package | M03 / `M03-RND-TERRAIN-CHUNK-QUEUE-001` |
|
||||||
|
| Owners | Pure chunk geometry create/remove request calculation |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-terrain-chunk-queue`, 2026-07-16 |
|
||||||
|
| Profiles/capabilities | Existing chunk/tile LOD desired state; all profiles |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Compare loader-owned current chunk geometry with desired chunk/tile LOD state
|
||||||
|
and produce fresh removal and nearest-first creation request arrays.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Retain, drain or bound queues, or own per-frame operation permits.
|
||||||
|
- Create/remove Mesh, Node, material or RID resources.
|
||||||
|
- Calculate desired chunk/tile LOD, parse ADT data or schedule worker tasks.
|
||||||
|
- Change cache versions, renderer profiles or visible fidelity rules.
|
||||||
|
- Provide a generic queue/operation framework.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Current[Loader-owned tile/chunk state] --> Planner[TerrainChunkGeometryQueuePlanner]
|
||||||
|
Desired[Desired chunk and tile LOD state] --> Planner
|
||||||
|
Focus[GodotWorldPosition] --> Planner
|
||||||
|
Planner --> Remove[Stable-order remove requests]
|
||||||
|
Planner --> Create[Nearest-first create requests]
|
||||||
|
Remove --> Loader[StreamingWorldLoader queues]
|
||||||
|
Create --> Loader
|
||||||
|
Loader --> Budget[RenderBudgetScheduler permit]
|
||||||
|
Budget --> GPU[Existing Mesh/Node/RID side effects]
|
||||||
|
```
|
||||||
|
|
||||||
|
Allowed dependencies are typed Godot position and call-local scalar/container
|
||||||
|
values. SceneTree, RenderingServer, ResourceLoader, worker APIs, gameplay,
|
||||||
|
network, editor UI, files and persistent caches are forbidden.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `TerrainChunkGeometryQueuePlanner.plan(tile_states, focus_position)` | Pure query | Return fresh `create_requests` and `remove_requests` arrays | Synchronous; caller owns result | Null/wrong focus returns two empty arrays |
|
||||||
|
|
||||||
|
Script-identity validation uses a preload and therefore works from a cold Godot
|
||||||
|
global-class cache.
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Tile-state read model with current chunks and desired LODs | Streamer | Planner | Loader-owned; read only | One rebuild |
|
||||||
|
| Input | Desired tile LOD | Tile LOD calculation | Planner | Scalar in loader state | One rebuild |
|
||||||
|
| Input | Parsed chunk origins and tile fallback origin | ADT result/loader | Planner | Loader-owned; read only | Map session |
|
||||||
|
| Input | `GodotWorldPosition` | Streamer focus adapter | Planner | Immutable caller reference | One rebuild |
|
||||||
|
| Output | Remove `{tile, chunk}` records | Planner | Loader removal queue | Fresh caller array/dictionaries | Until drained/rebuilt |
|
||||||
|
| Output | Create `{tile, chunk, lod, dist_sq}` records | Planner | Loader creation queue | Fresh nearest-first array/dictionaries | Until drained/rebuilt |
|
||||||
|
|
||||||
|
Distance is horizontal XZ squared distance from the chunk origin, not its center.
|
||||||
|
Y is intentionally ignored. A missing/sparse parsed origin falls back to the
|
||||||
|
tile state's existing origin exactly as before extraction.
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Start[plan] --> FocusValid{Typed focus?}
|
||||||
|
FocusValid -->|no| Empty[Return two empty arrays]
|
||||||
|
FocusValid -->|yes| Tile[Iterate tile state insertion order]
|
||||||
|
Tile --> TileLod{Desired tile LOD active?}
|
||||||
|
TileLod -->|yes| RemoveAll[Append every current chunk removal]
|
||||||
|
TileLod -->|no| Compare[Compare current and desired chunks]
|
||||||
|
Compare --> RemoveMissing[Append current chunks absent from desired]
|
||||||
|
Compare --> CreateMismatch[Append LOD-mismatched creates]
|
||||||
|
Compare --> CreateMissing[Append absent desired creates]
|
||||||
|
CreateMismatch --> Origin[Parsed chunk origin or tile fallback]
|
||||||
|
CreateMissing --> Origin
|
||||||
|
Origin --> Distance[Attach squared XZ distance]
|
||||||
|
RemoveAll --> Next[Next tile]
|
||||||
|
RemoveMissing --> Next
|
||||||
|
Distance --> Next
|
||||||
|
Next --> Sort[Sort creates nearest-first]
|
||||||
|
Sort --> Result[Return fresh arrays]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle/state
|
||||||
|
|
||||||
|
The planner is stateless. Each loader queue rebuild supplies current read models
|
||||||
|
and receives new arrays. The loader adopts and drains those arrays until another
|
||||||
|
rebuild replaces them. Planner destruction requires no cleanup or cancellation.
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Loader as StreamingWorldLoader
|
||||||
|
participant Planner as TerrainChunkGeometryQueuePlanner
|
||||||
|
participant Budget as RenderBudgetScheduler
|
||||||
|
participant Render as Existing terrain finalization
|
||||||
|
Loader->>Planner: plan(tile states, typed focus)
|
||||||
|
Planner-->>Loader: fresh create/remove arrays
|
||||||
|
Loader->>Budget: request shared chunk_geometry permit
|
||||||
|
Budget-->>Loader: permit
|
||||||
|
Loader->>Render: pop_back removal first
|
||||||
|
Loader->>Budget: request shared chunk_geometry permit
|
||||||
|
Budget-->>Loader: permit
|
||||||
|
Loader->>Render: pop_front nearest creation
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- The planner owns only call-local arrays and dictionaries until return.
|
||||||
|
- The loader owns adopted queue storage, drain ordering, tile/chunk state and all
|
||||||
|
Mesh/Node/material/RID side effects.
|
||||||
|
- Calls and drains currently run on the renderer main thread. The planner has no
|
||||||
|
lock, task, retained resource or concurrent mutation.
|
||||||
|
- Removal and creation continue to share the existing `chunk_geometry` budget lane.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Null/wrong focus script | Identity guard | Return empty request arrays | Contract verifier | Supply typed focus and rebuild |
|
||||||
|
| Sparse/out-of-range chunk data | Origin resolver | Use tile fallback origin | Synthetic fixture | Normal ADT completion restores precise origin |
|
||||||
|
| Tile LOD becomes desired | Desired tile LOD check | Remove every current chunk; no chunk creates | Queue metrics | Existing tile-LOD create path takes over |
|
||||||
|
| Queue rebuild before drain | Loader lifecycle | Replace old desired-operation arrays | Existing queue metrics | Latest state wins |
|
||||||
|
| Shutdown/map reset | Loader lifecycle | Loader clears queues; planner has nothing to cancel | Shutdown verifier | New session replans |
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
The planner introduces no settings. It consumes already-calculated desired LOD
|
||||||
|
state. Operation limits, profile defaults and removal-first drain order remain
|
||||||
|
loader/scheduler configuration.
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
No serialization, cache entry or format changes. Baked terrain, control-splat
|
||||||
|
and splat caches require no rebuild or migration.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
The planner emits no logs. Existing queue metrics observe the adopted arrays.
|
||||||
|
The contract verifier reports semantic cases and bounded multi-tile timing.
|
||||||
|
Asset-backed p95/p99 remains milestone acceptance work.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `verify_terrain_chunk_geometry_queue_planner.gd`: missing/mismatched/unchanged
|
||||||
|
chunks, tile-LOD removal, sparse origin fallback, XZ-only distance, stable
|
||||||
|
removals, nearest creates, invalid focus, loader boundary and bounded timing.
|
||||||
|
- Existing LOD planner, terrain cache, facade, internal-access, manifest,
|
||||||
|
shutdown, scheduler, streaming and coordinate regressions remain required.
|
||||||
|
- Fidelity evidence is exact branch/request/order preservation only. No new
|
||||||
|
build-12340 visual parity claim is made.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
- Extract tile-LOD operation planning as its own bounded contract if justified.
|
||||||
|
- Add explicit typed tile-state adapters only when more than one consumer exists.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Pure chunk create/remove planning | Implemented extraction | Asset-free semantic/source/timing verifier | Asset-backed p95/p99 pending |
|
||||||
|
| Queue execution/resource ownership | Remains in loader | Drain source boundary and scheduler regression | Further terrain decomposition required |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- Raw tile/chunk dictionaries remain a transitional ADT adapter boundary.
|
||||||
|
- The planner performs a second linear tile-state pass beside tile-LOD planning;
|
||||||
|
bounded synthetic timing is green, but asset-backed frame metrics are pending.
|
||||||
|
- Equal-distance create ordering retains comparator behavior without a tie-breaker.
|
||||||
|
- Terrain geometry finalization and state mutation remain monolithic.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/terrain/terrain_chunk_geometry_queue_planner.gd` | Pure request calculation and nearest-first creation order |
|
||||||
|
| `src/scenes/streaming/streaming_world_loader.gd` | Queue adoption, tile-LOD planning, budgets and render side effects |
|
||||||
|
| `src/tools/verify_terrain_chunk_geometry_queue_planner.gd` | Semantic, boundary and timing regression |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`terrain-chunk-lod-planner.md`](terrain-chunk-lod-planner.md)
|
||||||
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
- [`../../RENDER.md`](../../RENDER.md)
|
||||||
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
# Terrain Chunk LOD Planner
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented extraction |
|
||||||
|
| Target/work package | M03 / `M03-RND-TERRAIN-LOD-PLANNER-001` |
|
||||||
|
| Owners | Pure desired chunk-LOD calculation only |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-terrain-chunk-lod-planner`, 2026-07-16 |
|
||||||
|
| Profiles/capabilities | Existing Performance/Balanced/High/Custom radii |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Calculate the desired terrain LOD for each populated parsed ADT chunk from one
|
||||||
|
typed Godot-space focus and one immutable renderer-policy snapshot.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Own tile/chunk state, queues, budgets, tasks, Mesh, Node, material or RID.
|
||||||
|
- Select tile LOD, create/remove geometry or apply a completed plan.
|
||||||
|
- Normalize custom radii, change profile defaults or claim visual parity.
|
||||||
|
- Parse ADT data or convert between WoW and Godot coordinate spaces.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Focus[GodotWorldPosition] --> Planner[TerrainChunkLodPlanner]
|
||||||
|
Chunks[Parsed ADT chunk records] --> Planner
|
||||||
|
Config[Streamer LOD configuration] --> Policy[TerrainChunkLodPolicy]
|
||||||
|
Policy --> Planner
|
||||||
|
Planner --> Desired[chunk index to LOD 0/1/2]
|
||||||
|
Desired --> Loader[StreamingWorldLoader state adapter]
|
||||||
|
Loader --> Queue[Existing create/remove queues and budgets]
|
||||||
|
```
|
||||||
|
|
||||||
|
Allowed dependencies are the typed Godot position, immutable policy and
|
||||||
|
call-local Array/Dictionary/Vector3 values. SceneTree, RenderingServer, gameplay,
|
||||||
|
network, files, tasks and persistent caches are forbidden.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `TerrainChunkLodPolicy.new(...)` | Immutable value | Snapshot enabled flag, three raw radii and chunk size | Caller-owned; one planning refresh | Values are preserved without normalization |
|
||||||
|
| `TerrainChunkLodPlanner.plan(chunks, focus_position, policy)` | Pure query | Map populated source indices to LOD 0/1/2 | Synchronous; result caller-owned | Invalid/null typed inputs or disabled policy return empty |
|
||||||
|
|
||||||
|
The planner validates scripts by preloaded identity so it works from a cold
|
||||||
|
Godot global-class cache.
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Parsed chunk array with `origin: Vector3` | ADT load result | Planner | Loader-owned; read only | One call |
|
||||||
|
| Input | `GodotWorldPosition` | Streamer focus adapter | Planner | Immutable caller reference | One call |
|
||||||
|
| Input | `TerrainChunkLodPolicy` | Streamer exported configuration | Planner | Immutable caller reference | One call |
|
||||||
|
| Output | `{source_chunk_index: 0/1/2}` | Planner | Loader desired state | Fresh caller dictionary | One call |
|
||||||
|
|
||||||
|
The Y coordinate is intentionally ignored. Empty chunk records and chunks beyond
|
||||||
|
the third radius are absent from the result.
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Start[plan] --> Valid{Typed inputs and enabled?}
|
||||||
|
Valid -->|no| Empty[Return empty dictionary]
|
||||||
|
Valid -->|yes| Radii[Square raw radius times chunk size]
|
||||||
|
Radii --> Iterate[Visit source chunk indices in order]
|
||||||
|
Iterate --> Populated{Record populated?}
|
||||||
|
Populated -->|no| Iterate
|
||||||
|
Populated -->|yes| Center[origin XZ plus half chunk]
|
||||||
|
Center --> Distance[Squared horizontal distance]
|
||||||
|
Distance --> Threshold{First inclusive threshold}
|
||||||
|
Threshold -->|LOD0/1/2| Record[Record source index]
|
||||||
|
Threshold -->|outside| Iterate
|
||||||
|
Record --> Iterate
|
||||||
|
Iterate --> Done[Return desired mapping]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle/state
|
||||||
|
|
||||||
|
The planner is stateless. A policy is constructed for one refresh, read during
|
||||||
|
synchronous planning and released. The returned dictionary is then owned by the
|
||||||
|
loader state. There is no cancellation, recovery state or retained resource.
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Stream as StreamingWorldLoader
|
||||||
|
participant Policy as TerrainChunkLodPolicy
|
||||||
|
participant Planner as TerrainChunkLodPlanner
|
||||||
|
Stream->>Policy: snapshot enabled/radii/chunk size
|
||||||
|
Stream->>Planner: plan(chunks, typed focus, policy)
|
||||||
|
Planner-->>Stream: fresh desired LOD dictionary
|
||||||
|
Stream->>Stream: existing tile state, queue and budget application
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- Planner and policy own no engine resources, nodes, RIDs, tasks or caches.
|
||||||
|
- The loader retains parsed chunks, desired state and every application side effect.
|
||||||
|
- Current calls run synchronously on the renderer main thread; the pure inputs
|
||||||
|
also make isolated contract testing possible without a world scene.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Null/wrong focus or policy script | Identity guard | Empty plan | Contract verifier | Supply typed values next refresh |
|
||||||
|
| Disabled chunk LOD | Policy flag | Empty plan; existing tile-LOD fallback applies | Existing renderer state | Enable profile option |
|
||||||
|
| Empty chunk record | Record guard | Preserve sparse source index; omit record | None | Not an error |
|
||||||
|
| Custom negative radius | No normalization | Existing squared-radius behavior is retained | Regression fixture | Change caller configuration if undesired |
|
||||||
|
|
||||||
|
There is no asynchronous work or cancellation token.
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
The policy snapshots `use_chunk_terrain_lods`, `lod0_radius_chunks`,
|
||||||
|
`lod1_radius_chunks`, `lod2_radius_chunks` and the existing `CHUNK_SIZE`. Inclusive
|
||||||
|
squared thresholds and raw custom values deliberately preserve prior behavior.
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
No serialization or cache format changes. Existing baked terrain, control-splat
|
||||||
|
and splat cache versions remain unchanged and require no rebuild.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
The service emits no logs. The contract verifier reports semantic cases and a
|
||||||
|
bounded synthetic timing. Asset-backed p95/p99 remains milestone acceptance work.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `verify_terrain_chunk_lod_planner.gd`: center and vertical invariance,
|
||||||
|
inclusive thresholds, sparse/outside chunks, disabled/custom-negative policy,
|
||||||
|
loader delegation and 250-by-256 bounded planning.
|
||||||
|
- Renderer cache, facade, internal-access, baseline, scheduler, target-planner,
|
||||||
|
focus, ground-query and coordinate-boundary regressions remain required.
|
||||||
|
- Fidelity evidence is formula preservation only; no new build-12340 visual
|
||||||
|
equivalence claim is made.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
- Add measured direction/velocity only through an explicit policy/contract change.
|
||||||
|
- Keep queue prioritization and LOD application as separately bounded services.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Pure chunk desired-LOD calculation | Implemented extraction | Asset-free semantic/source-boundary contract | Asset-backed p95/p99 pending |
|
||||||
|
| Queue/state/finalization ownership | Remains in loader | Source-boundary regression | Further terrain decomposition required |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- Raw negative and non-monotonic custom radii retain historical squared/ordered
|
||||||
|
semantics, even though such settings may be surprising.
|
||||||
|
- The planner is linear in parsed chunk count and allocates one result dictionary.
|
||||||
|
- Terrain build, quality upgrades and geometry lifecycle remain monolithic.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/terrain/terrain_chunk_lod_policy.gd` | Immutable configuration snapshot |
|
||||||
|
| `src/render/terrain/terrain_chunk_lod_planner.gd` | Pure horizontal-distance plan |
|
||||||
|
| `src/scenes/streaming/streaming_world_loader.gd` | Composition and plan application adapter |
|
||||||
|
| `src/tools/verify_terrain_chunk_lod_planner.gd` | Contract, dependency and timing regression |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
- [`terrain-quality-mesh-cache.md`](terrain-quality-mesh-cache.md)
|
||||||
|
- [`../../RENDER.md`](../../RENDER.md)
|
||||||
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
# Terrain Quality Mesh Cache
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented extraction |
|
||||||
|
| Target/work package | M03 / `M03-RND-TERRAIN-CACHE-SERVICE-001` |
|
||||||
|
| Owners | Full-quality terrain revisit Mesh retention and LRU policy |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-terrain-quality-cache`, 2026-07-16 |
|
||||||
|
| Profiles/capabilities | Performance/Balanced/High/Custom capacity; session-memory only |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Retain eligible full-quality terrain Mesh references after ADT tile state unload,
|
||||||
|
so a revisit can restore quality without flashing to a lower-quality fallback,
|
||||||
|
while bounding retained references with the existing least-recently-used policy.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Parse/build terrain, own tile state or schedule quality-upgrade tasks.
|
||||||
|
- Create/finalize Nodes or RIDs, mutate materials or choose terrain LOD.
|
||||||
|
- Persist cache entries or change baked/control-splat/splat format versions.
|
||||||
|
- Cache control-splat/splat sources whose active radius has separate ownership.
|
||||||
|
- Provide a generic cache framework or change profile capacity defaults.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Upgrade[Full-quality baked upgrade result] --> Loader[StreamingWorldLoader adapter]
|
||||||
|
Loader --> Cache[TerrainQualityMeshCache]
|
||||||
|
Cache -->|retained Mesh reference| Revisit[Reloaded tile state]
|
||||||
|
Revisit --> Finalize[Existing tile LOD/finalization path]
|
||||||
|
Splat[Control/splat quality sources] -. rejected .-> Cache
|
||||||
|
```
|
||||||
|
|
||||||
|
Allowed dependencies: `Mesh`, Dictionary/Array scalar containers and the loader
|
||||||
|
adapter. Forbidden dependencies: Node/SceneTree/RID, tasks/mutexes, ADT parsing,
|
||||||
|
gameplay/network/editor UI, files and persistent Resource cache ownership.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `TerrainQualityMeshCache.store(tile_key, mesh, source, capacity)` | Command | Admit/touch a Mesh and trim oldest entries | Owning renderer thread/session | Returns false for rejected input or immediate zero-capacity eviction |
|
||||||
|
| `TerrainQualityMeshCache.restore(tile_key)` | Query/touch | Return a fresh Mesh/source record and mark newest | Owning renderer thread | Empty on miss; invalid record is removed |
|
||||||
|
| `TerrainQualityMeshCache.clear()` | Lifecycle command | Release all retained Mesh references and LRU keys | Reset/shutdown | Idempotent |
|
||||||
|
| `TerrainQualityMeshCache.diagnostic_snapshot()` | Read model | Detached count and oldest-to-newest key/source list | Caller-owned result | Never exposes Mesh references |
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Tile key | Streamer tile catalog/state | Cache index | Copied string | Map session |
|
||||||
|
| Input | Full-quality Mesh | Baked upgrade/fallback path | Cache | Retained shared reference | Until eviction/clear |
|
||||||
|
| Input | Quality source | Streamer quality adapter | Admission/restore | Copied string | Entry lifetime |
|
||||||
|
| Input | Current capacity | Active renderer profile | Store trim | Scalar copy | One store |
|
||||||
|
| Output | Fresh `{mesh, source}` record | Cache | Loader state adapter | Caller dictionary; shared Mesh reference | One restore |
|
||||||
|
| Output | Detached diagnostics | Cache | Tests/future metrics | Caller-owned scalars | One query |
|
||||||
|
|
||||||
|
Side effects are limited to retained reference and LRU container mutation. There
|
||||||
|
is no scene, GPU, task, file, database, network or persistent-cache mutation.
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Store[store tile/mesh/source/capacity] --> Admit{Valid key + Mesh + source?}
|
||||||
|
Admit -->|no| Reject[Return false]
|
||||||
|
Admit -->|yes| Touch[Insert/replace and touch newest]
|
||||||
|
Touch --> Trim[Evict oldest until count <= capacity]
|
||||||
|
Restore[restore tile] --> Hit{Valid admitted entry?}
|
||||||
|
Hit -->|no| Miss[Remove invalid / return empty]
|
||||||
|
Hit -->|yes| TouchRestore[Touch newest and return fresh record]
|
||||||
|
Clear[world reset/shutdown] --> Empty[Release all references and keys]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle/state
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> Empty
|
||||||
|
Empty --> Populated: admitted store with capacity > 0
|
||||||
|
Populated --> Populated: store / restore touch / bounded eviction
|
||||||
|
Populated --> Empty: capacity zero store evicts all
|
||||||
|
Populated --> Empty: clear on reset/shutdown
|
||||||
|
Empty --> Empty: rejected store / miss / clear
|
||||||
|
Empty --> [*]
|
||||||
|
Populated --> [*]: owner releases cache
|
||||||
|
```
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Upgrade as Terrain upgrade result
|
||||||
|
participant Loader as StreamingWorldLoader
|
||||||
|
participant Cache as TerrainQualityMeshCache
|
||||||
|
Upgrade->>Loader: full Mesh + baked_full source
|
||||||
|
Loader->>Cache: store(key, mesh, source, profile capacity)
|
||||||
|
Note over Loader,Cache: tile state may later unload
|
||||||
|
Loader->>Cache: restore(key) on tile creation/revisit
|
||||||
|
Cache-->>Loader: fresh record or empty
|
||||||
|
Loader->>Loader: apply Mesh/source to existing tile state
|
||||||
|
Loader->>Cache: clear() on world reset/shutdown
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- The cache owns its dictionary, ordered tile keys and retained Mesh references.
|
||||||
|
- The loader retains tile state, async tasks/results, quality selection and all
|
||||||
|
Node/RID/material finalization.
|
||||||
|
- Returned records are new dictionaries; their Mesh is a shared reference.
|
||||||
|
- Calls currently occur on the main renderer thread. The service has no lock and
|
||||||
|
must not be mutated concurrently.
|
||||||
|
- `clear` deterministically drops references during the existing world reset path.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Empty key/null Mesh | Store guard | Reject unchanged | Boolean/test | Fix caller result |
|
||||||
|
| Splat/control source | Admission list | Reject unchanged | Boolean/test | Keep source in active-radius ownership |
|
||||||
|
| Zero/negative capacity | Clamp and trim | Entry immediately evicted; cache empty | Snapshot/count | Increase profile capacity |
|
||||||
|
| Miss/invalid entry | Restore validation | Remove invalid and return empty | Empty record | Existing loader fallback/rebuild |
|
||||||
|
| World reset/shutdown | Loader lifecycle | Clear every reference/key | Shutdown regression | Refill during next session |
|
||||||
|
|
||||||
|
There is no asynchronous work or cancellation token inside this service.
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `terrain_quality_mesh_cache_limit` | Existing loader value | `0/24/48` in current presets | Read on every store | Maximum retained entries |
|
||||||
|
| Admitted source | Non-splat (currently `baked_full`) | All | No hidden override | Revisit retention |
|
||||||
|
| Excluded sources | `control_splat_cache`, `splat_cache`, `splat` | All | No | Preserve active quality ownership |
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
- This is an in-memory reference cache, not a serialized cache format.
|
||||||
|
- `REQUIRED_BAKED_TILE_FORMAT_VERSION=5`, control splat v3 and splat v1 remain
|
||||||
|
loader/resource validation contracts and are unchanged.
|
||||||
|
- No migration or rebuild is required. Entry keys retain the existing tile-key form.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
- Detached diagnostics report count and oldest-to-newest tile key/source entries.
|
||||||
|
- Mesh, material, Node and RID references are deliberately absent.
|
||||||
|
- Performance is bounded dictionary lookup plus one ordered-array erase/append;
|
||||||
|
no new optimization or scale claim is made.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `verify_terrain_quality_mesh_cache.gd`: admission, exclusions, Mesh identity,
|
||||||
|
source, LRU touch/eviction, detached diagnostics, negative capacity and clear.
|
||||||
|
- Loader source boundary proves cache composition/delegation and removal of its
|
||||||
|
former LRU array/touch/trim implementation.
|
||||||
|
- Existing shutdown, facade, internal-access, manifest, planner and scheduler
|
||||||
|
regressions remain required.
|
||||||
|
- Fidelity: source rules, capacities and three loader adapter sites are unchanged;
|
||||||
|
no visible terrain or build-12340 parity change is claimed.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
- Expose scalar cache counts through facade metrics when needed.
|
||||||
|
- Move additional terrain responsibilities only as separate bounded services.
|
||||||
|
- Replace ordered-array LRU only after measured scale evidence.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Bounded quality Mesh retention | Implemented extraction | Contract/LRU fixtures | Asset-backed memory/revisit timing pending |
|
||||||
|
| Deterministic reset ownership | Implemented | Clear path plus shutdown regression | Full terrain service still monolithic |
|
||||||
|
| Persistent terrain cache formats | Unchanged | Loader version constants | Separate acceptance audit remains |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- Ordered-array touch is linear in entry count, matching prior behavior.
|
||||||
|
- Mesh memory size is not measured; capacity remains entry-count based.
|
||||||
|
- Only revisit Mesh ownership moved; build/tasks/state/finalization remain loader-owned.
|
||||||
|
- Asset-backed flash/revisit timing and p95/p99 evidence remain pending.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/terrain/terrain_quality_mesh_cache.gd` | Session cache entries, LRU and admission |
|
||||||
|
| `src/scenes/streaming/streaming_world_loader.gd` | Store/restore state adapter and reset lifecycle |
|
||||||
|
| `src/tools/verify_terrain_quality_mesh_cache.gd` | Scene-free cache and loader-boundary regression |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
- [`terrain-query.md`](terrain-query.md)
|
||||||
|
- [`../../RENDER.md`](../../RENDER.md)
|
||||||
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.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`.
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
# WMO Placement Registry
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented extraction |
|
||||||
|
| Target/work package | M03 / `M03-RND-WMO-PLACEMENT-REGISTRY-001` |
|
||||||
|
| Owners | WMO placement-key to tile/global reference-set ownership |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-wmo-placement-registry`, 2026-07-17 |
|
||||||
|
| Profiles/capabilities | Existing ADT/WDT WMO placement paths; profile-independent |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Own the reference sets that keep one resolved WMO placement active across one
|
||||||
|
or more ADT tiles, or across the global WDT placement lifetime. This separates
|
||||||
|
value-only placement ownership from scene and render-resource ownership.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Resolve WMO paths, placement identity or world transforms.
|
||||||
|
- Own Nodes, Resources, RIDs, roots, jobs, queues, tasks or caches.
|
||||||
|
- Load, instantiate, build or free WMO content.
|
||||||
|
- Change duplicate order, budgets, retries or global-WMO behavior.
|
||||||
|
- Claim original-client WMO visual, portal, room or material parity.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Parsed[ADT/WDT placement] --> Resolver[WmoPlacementResolver]
|
||||||
|
Resolver --> Key[Placement key]
|
||||||
|
Tile[Tile/global reference key] --> Loader[StreamingWorldLoader]
|
||||||
|
Key --> Loader
|
||||||
|
Loader --> Registry[WmoPlacementRegistry]
|
||||||
|
Registry --> Decision[Existing or final-release decision]
|
||||||
|
Decision --> Loader
|
||||||
|
Loader --> NodeMap[Loader key-to-Node map]
|
||||||
|
Loader --> Jobs[Loader jobs/queues/resources]
|
||||||
|
```
|
||||||
|
|
||||||
|
The registry depends only on `RefCounted`, `Dictionary`, `Array` and `String`.
|
||||||
|
SceneTree, Node, Resource, RID, files, loaders, threads, locks, network,
|
||||||
|
gameplay and editor UI are forbidden dependencies.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `add_reference(placement_key, reference_key)` | Command/query | Idempotently add a reference; return true only when creating the placement key | Renderer main thread; map session | Empty key returns false and changes nothing |
|
||||||
|
| `release_reference(placement_key, reference_key)` | Command/query | Remove an owned reference; return true only on final removal | Renderer main thread; map session | Empty, unknown or non-owner input returns false |
|
||||||
|
| `contains(placement_key)` | Query | Test whether any reference keeps a placement active | Renderer main thread; map session | Empty key returns false |
|
||||||
|
| `active_count()` | Query | Count active placement keys, not references | Renderer main thread; map session | None |
|
||||||
|
| `diagnostic_snapshot()` | Query | Return detached, placement/reference-sorted records | Renderer main thread; caller-owned result | None |
|
||||||
|
| `clear()` | Command | Remove all ownership records idempotently | Renderer main thread; reset/shutdown | None |
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Resolved `uid:*`, `tile:*:*` or `global` placement key | `WmoPlacementResolver` / loader global adapter | Registry | Copied String dictionary key | Until final release/clear |
|
||||||
|
| Input | ADT tile key or `__global__` reference key | Loader | Registry | Copied String set key | Until release/clear |
|
||||||
|
| Output | First-placement Boolean | Registry | Loader creation path | Value | One call |
|
||||||
|
| Output | Final-release Boolean | Registry | Loader cancellation/free path | Value | One call |
|
||||||
|
| Output | Active count and sorted diagnostic records | Registry | Renderer diagnostics/verifier | Detached values | Caller lifetime |
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Add[add placement + reference] --> Valid{Both non-empty?}
|
||||||
|
Valid -->|no| Reject[false; unchanged]
|
||||||
|
Valid -->|yes| Existing{Placement exists?}
|
||||||
|
Existing -->|no| Create[Create reference set; true]
|
||||||
|
Existing -->|yes| Retain[Idempotently add reference; false]
|
||||||
|
Release[release placement + reference] --> Owner{Known owner reference?}
|
||||||
|
Owner -->|no| Reject
|
||||||
|
Owner -->|yes| Erase[Erase reference]
|
||||||
|
Erase --> Empty{Set empty?}
|
||||||
|
Empty -->|no| Retained[false; placement remains]
|
||||||
|
Empty -->|yes| Final[Erase placement; true]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle and state
|
||||||
|
|
||||||
|
The states for one placement are `Absent`, `Referenced` and back to `Absent`.
|
||||||
|
The first valid add enters `Referenced`; further tile/global adds remain there.
|
||||||
|
Only release of the final owned reference returns to `Absent`. Map reset and
|
||||||
|
shutdown call `clear`, which is safe from either state.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> Absent
|
||||||
|
Absent --> Referenced: first valid add / true
|
||||||
|
Referenced --> Referenced: idempotent/shared add / false
|
||||||
|
Referenced --> Referenced: non-owner/non-final release / false
|
||||||
|
Referenced --> Absent: final owned release / true
|
||||||
|
Referenced --> Absent: clear
|
||||||
|
```
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Loader as StreamingWorldLoader
|
||||||
|
participant Registry as WmoPlacementRegistry
|
||||||
|
participant Nodes as Loader Node map
|
||||||
|
Loader->>Registry: contains(placement key)
|
||||||
|
alt existing placement
|
||||||
|
Loader->>Registry: add_reference(key, tile)
|
||||||
|
else new placement built successfully
|
||||||
|
Loader->>Nodes: store key-to-Node
|
||||||
|
Loader->>Registry: add_reference(key, tile/global)
|
||||||
|
end
|
||||||
|
Loader->>Registry: release_reference(key, tile)
|
||||||
|
alt final reference
|
||||||
|
Registry-->>Loader: true
|
||||||
|
Loader->>Loader: cancel render build
|
||||||
|
Loader->>Nodes: queue-free and erase Node
|
||||||
|
else shared/unknown reference
|
||||||
|
Registry-->>Loader: false
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- The registry owns only placement-key Dictionaries and reference-key sets.
|
||||||
|
- The loader owns the parallel placement-key-to-Node map and every Node action.
|
||||||
|
- The loader also owns WMO roots, tile state, caches, requests, jobs, queues,
|
||||||
|
retries, cancellation and render-resource lifetime.
|
||||||
|
- Current calls are serialized on the renderer main thread. The registry has no
|
||||||
|
lock and must not be mutated concurrently.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Empty placement/reference key | API guard | Reject without mutation | Contract verifier | Repair caller input |
|
||||||
|
| Duplicate add | Existing set member | Idempotent retain | Sorted snapshot | No recovery required |
|
||||||
|
| Unknown/non-owner release | Missing placement/reference | Ignore without mutation | Contract verifier | Release from actual owner or reset |
|
||||||
|
| Tile build cancellation | Loader-owned condition | Loader cancels job, then releases recorded refs | Existing queue metrics | Retry when tile becomes wanted |
|
||||||
|
| Final release | Empty set after erase | Return true and erase registry record | Active count | Loader cancels/free Node exactly once |
|
||||||
|
| Map reset/shutdown | Loader lifecycle | Loader frees Nodes/jobs and clears registry | Shutdown regression | Next map begins empty |
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
There is no setting, profile branch or capacity knob. WMO tile radius, cache
|
||||||
|
limits, render budgets and visibility settings remain loader-owned.
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
No data is serialized and no cache/schema/version changes. Placement and
|
||||||
|
reference keys exist only for the current renderer map session.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
`active_count` backs existing WMO instance metrics. `diagnostic_snapshot`
|
||||||
|
returns deterministic detached records for tests and future read-only
|
||||||
|
diagnostics; mutating the returned arrays cannot alter registry state.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `verify_wmo_placement_registry.gd`: invalid/first/idempotent/shared/non-owner/
|
||||||
|
final/global/clear behavior, deterministic detached diagnostics, source
|
||||||
|
ownership boundaries and bounded 100-by-256 shared-reference cycles.
|
||||||
|
- WMO resolver, material, runtime-cache shutdown and the renderer regression
|
||||||
|
suite verify the unchanged loader adapters and surrounding lifecycle.
|
||||||
|
- Fidelity evidence is exact preservation of the repository's prior shared-ref
|
||||||
|
and final-release semantics. Asset-backed 3.3.5a comparison is still pending.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
- WMO build/resource state can be extracted separately without moving Nodes
|
||||||
|
into this registry.
|
||||||
|
- A future diagnostic adapter may expose the detached snapshot through the
|
||||||
|
facade without exposing mutable loader internals.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| WMO placement reference ownership | Implemented extraction | Contract/source/timing verifier | Asset-backed cross-tile corpus pending |
|
||||||
|
| WMO build/resource scheduling | Remains in loader | Existing runtime regressions | Separate state/cancellation package |
|
||||||
|
| WMO visual/portal/material fidelity | Partial | Renderer baseline/material checks | Original-client paired evidence pending |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- Correctness assumes the loader records each successful placement reference in
|
||||||
|
the tile `wmo_refs` array before unregister.
|
||||||
|
- Old caches without positive UID retain resolver-defined per-tile identity.
|
||||||
|
- No proprietary WMO corpus or original-client paired capture is in this package.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/wmo/wmo_placement_registry.gd` | Placement-key/reference-set ownership |
|
||||||
|
| `src/render/wmo/wmo_placement_resolver.gd` | Stable path, identity and transform rules |
|
||||||
|
| `src/scenes/streaming/streaming_world_loader.gd` | Node map, instances, resources, jobs, queues and lifecycle reactions |
|
||||||
|
| `src/tools/verify_wmo_placement_registry.gd` | Contract, boundary and timing regression |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`wmo-placement-resolver.md`](wmo-placement-resolver.md)
|
||||||
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
- [`../../RENDER.md`](../../RENDER.md)
|
||||||
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
# WMO Placement Resolver
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented |
|
||||||
|
| Target/work package | M03 / `M03-RND-WMO-PLACEMENT-RESOLVER-001` |
|
||||||
|
| Owners | Pure WMO cache-key, placement-identity and world-transform rules |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-wmo-placement-resolver`, 2026-07-17 |
|
||||||
|
| Profiles/capabilities | Existing ADT/WDT WMO placement paths |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Provide one scene-free boundary for normalized WMO cache keys, MODF placement
|
||||||
|
identity and world-space Godot transforms across render-cache, cached-scene and
|
||||||
|
live-prototype instance paths.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Parse ADT/WDT or convert canonical WoW coordinates.
|
||||||
|
- Own WMO registry references, tile state, jobs, queues or retries; reference
|
||||||
|
ownership belongs to `WmoPlacementRegistry`.
|
||||||
|
- Load/validate cached scenes or lightweight render resources.
|
||||||
|
- Create Nodes, Mesh/MultiMesh, materials or RIDs.
|
||||||
|
- Implement portals, rooms, visibility, materials, doodads or WMO parity.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Parsed[ADT/WDT WMO placement] --> Loader[StreamingWorldLoader adapter]
|
||||||
|
Loader --> Resolver[WmoPlacementResolver]
|
||||||
|
Resolver --> CacheKey[Normalized cache key]
|
||||||
|
Resolver --> Identity[Registry unique key]
|
||||||
|
Resolver --> Transform[World Transform3D]
|
||||||
|
CacheKey --> Cache[Loader WMO caches/requests]
|
||||||
|
Identity --> Registry[WmoPlacementRegistry]
|
||||||
|
Transform --> RenderRoot[Lightweight render root]
|
||||||
|
Transform --> Scene[Cached scene instance]
|
||||||
|
Transform --> Live[Live prototype instance]
|
||||||
|
```
|
||||||
|
|
||||||
|
Allowed dependencies are Dictionary/String values and Godot `Vector3`, `Basis`
|
||||||
|
and `Transform3D` math. Node, SceneTree, RenderingServer, ResourceLoader,
|
||||||
|
WorkerThreadPool, mutexes, files, gameplay, network and editor UI are forbidden.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `normalize_relative_path(relative_wmo_path)` | Pure query | Return lowercase slash-normalized cache/lookup key | Any thread; value result | Empty input returns empty |
|
||||||
|
| `resolve_unique_key(placement, tile_key, placement_index)` | Pure query | Return positive UID identity or tile/index fallback | Any thread; value result | Missing/non-positive UID uses fallback |
|
||||||
|
| `resolve_world_transform(placement)` | Pure query | Compose world-space position, Godot Euler rotation and uniform scale | Any thread; value result | Missing fields use zero/zero/one defaults; scale is not clamped |
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Relative WMO path | WDT/ADT name table | Path normalizer | Caller String | One call |
|
||||||
|
| Input | Raw placement Dictionary | WDT global or ADT MODF parser result | Identity/transform queries | Read-only shallow value | One call |
|
||||||
|
| Input | Tile key and placement index | Loader build job | Synthetic identity fallback | Copied scalar/String | Registry entry lifetime |
|
||||||
|
| Output | Normalized relative path | Resolver | Render/scene cache and load-request maps | New String value | Request/cache lookup |
|
||||||
|
| Output | `uid:*` or `tile:*:*` key | Resolver | `WmoPlacementRegistry` and loader ref arrays | New String value | Until unregister/reset |
|
||||||
|
| Output | World `Transform3D` | Resolver | Three WMO instance adapters | Value copy | Instance lifetime after assignment |
|
||||||
|
|
||||||
|
The resolver retains no source Dictionary, output or engine resource.
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Path[relative path] --> Slash[replace backslash with slash]
|
||||||
|
Slash --> Lower[lowercase entire key]
|
||||||
|
Placement[placement Dictionary] --> UID{unique_id > 0?}
|
||||||
|
UID -->|yes| UidKey[uid decimal]
|
||||||
|
UID -->|no| TileKey[tile key + placement index]
|
||||||
|
Placement --> Defaults[position/rotation/scale defaults]
|
||||||
|
Defaults --> Basis[Godot Basis.from_euler]
|
||||||
|
Basis --> Scale[unclamped uniform scale]
|
||||||
|
Scale --> World[world Transform3D]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle/state
|
||||||
|
|
||||||
|
The resolver is stateless. Construction, world reset, map switch, cancellation
|
||||||
|
and shutdown require no resolver operation.
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Loader as StreamingWorldLoader
|
||||||
|
participant Resolver as WmoPlacementResolver
|
||||||
|
participant Registry as WmoPlacementRegistry
|
||||||
|
participant Instance as Render/cached/live instance
|
||||||
|
Loader->>Resolver: normalize_relative_path(path)
|
||||||
|
Resolver-->>Loader: cache key
|
||||||
|
Loader->>Resolver: resolve_unique_key(placement, tile, index)
|
||||||
|
Resolver-->>Registry: identity adopted by loader
|
||||||
|
Loader->>Resolver: resolve_world_transform(placement)
|
||||||
|
Resolver-->>Loader: value Transform3D
|
||||||
|
Loader->>Instance: assign transform and attach/build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- The resolver owns only call-local scalar/value calculations.
|
||||||
|
- `WmoPlacementRegistry` owns placement-key reference sets. The loader owns its
|
||||||
|
key-to-Node map, cache/load-request state, jobs/queues, resource fallback and
|
||||||
|
cancellation.
|
||||||
|
- The loader and builders own every Node/Mesh/MultiMesh/material/RID lifecycle.
|
||||||
|
- Pure calls are thread-safe; current consumers execute on the main thread.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Empty path | Empty String | Empty normalized key | Contract fixture | Loader cache path rejects/degrades |
|
||||||
|
| Missing/non-positive UID | Integer rule | Tile/index key prevents same-tile collisions | Contract fixture | Rebake old ADT cache to restore cross-tile UID dedupe |
|
||||||
|
| Missing transform fields | Dictionary defaults | Zero position/rotation; scale one | Contract fixture | Repair parser/cache input |
|
||||||
|
| Zero/negative scale | No historical clamp | Preserve zero/mirrored basis | Contract fixture | Preserve source behavior |
|
||||||
|
| Resource missing/pending | Outside resolver | Loader retry/fallback path unchanged | Existing loader diagnostics | Restore cache/raw asset |
|
||||||
|
| Tile unregister/reset | Outside resolver | Loader releases refs/nodes/jobs | Shutdown/registry regressions | New placements resolve afresh |
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
The resolver introduces no setting or profile branch. WMO radii, visibility,
|
||||||
|
shadow/occlusion flags, cache size limits and build budgets remain loader-owned.
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
No format is written. Existing WMO `.tscn`/lightweight render cache versions and
|
||||||
|
ADT/WDT formats are unchanged. Older placement caches without positive UID retain
|
||||||
|
the existing per-tile fallback and require no migration for this extraction.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
The resolver emits no logs. Its verifier records path/identity/transform/source
|
||||||
|
contracts and bounded 20,000-triple timing. WMO queue/cache/instance metrics remain
|
||||||
|
in the facade/loader diagnostic snapshot.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `verify_wmo_placement_resolver.gd`: slash/case/empty path, positive UID,
|
||||||
|
missing/zero/negative UID fallback, default and rotated/scaled transform,
|
||||||
|
unclamped scale, historical Node3D property equivalence, stateless results,
|
||||||
|
all loader adapters, dependency boundary
|
||||||
|
and bounded timing.
|
||||||
|
- Existing WMO material/cache/shutdown and renderer baseline regressions remain
|
||||||
|
required alongside M2/terrain/facade/internal-access/coordinate gates.
|
||||||
|
- Fidelity evidence is exact extraction of current placement rules. No new
|
||||||
|
original-client WMO visual/portal/material parity claim is made.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
- `WmoPlacementRegistry` consumes these stable keys; later packages may extract
|
||||||
|
WMO build/resource state without changing the resolver.
|
||||||
|
- Render-group build cursor and resource-selection state require separate
|
||||||
|
lifecycle/cancellation contracts.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Consistent WMO placement identity/transform | Implemented extraction | Contract/source/timing verifier | Asset-backed placement comparison pending |
|
||||||
|
| WMO placement registry | Implemented extraction | Ownership/source/timing verifier | Asset-backed cross-tile corpus pending |
|
||||||
|
| WMO build services | Remains in loader | Existing runtime regressions | Stateful extraction pending |
|
||||||
|
| Portals/rooms/material fidelity | Partial | M00 checkpoint/material evidence | Broader WMO implementation pending |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- Raw placement Dictionaries remain the parser/cache boundary.
|
||||||
|
- Old caches without UID cannot dedupe one WMO across ADT tile boundaries.
|
||||||
|
- WMO scale remains intentionally unclamped, including zero/negative input.
|
||||||
|
- Asset-backed visual/p95/p99 evidence is unavailable in this package.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/wmo/wmo_placement_resolver.gd` | Path, identity and transform rules |
|
||||||
|
| `src/render/wmo/wmo_placement_registry.gd` | Placement reference-set ownership |
|
||||||
|
| `src/scenes/streaming/streaming_world_loader.gd` | Node map/cache/build/resource ownership |
|
||||||
|
| `src/tools/verify_wmo_placement_resolver.gd` | Contract, source and timing regression |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
- [`wmo-placement-registry.md`](wmo-placement-registry.md)
|
||||||
|
- [`../../RENDER.md`](../../RENDER.md)
|
||||||
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||||
@@ -0,0 +1,244 @@
|
|||||||
|
# WMO Render Build Queue
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented |
|
||||||
|
| Target/work package | M03 / `M03-RND-WMO-RENDER-BUILD-QUEUE-001` |
|
||||||
|
| Owners | Typed lightweight-WMO build jobs and FIFO placement-key queue |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-wmo-render-build-queue`, 2026-07-17 |
|
||||||
|
| Profiles/capabilities | Lightweight WMO render-cache path; profile-independent |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Own pending lightweight-WMO render build records and their FIFO placement-key
|
||||||
|
order outside the monolithic loader. Each typed job retains its placement key,
|
||||||
|
scene root, render resource and mesh/MultiMesh cursors while the loader performs
|
||||||
|
all validation, planning, materialization, permit consumption and destruction.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Free or queue-free Nodes, Resources or RIDs.
|
||||||
|
- Create MeshInstance3D/MultiMeshInstance3D or refresh materials.
|
||||||
|
- Own frame permits, placement references, render cache state or load I/O.
|
||||||
|
- Change duplicate enqueue, stale-key cleanup, cancellation or ordering.
|
||||||
|
- Change cache versions, WMO transforms, shadows, visibility or fidelity rules.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Placement[Successful WMO placement] --> Loader[StreamingWorldLoader]
|
||||||
|
Root[Node3D root] --> Loader
|
||||||
|
Cache[WMOStreamingResource] --> Loader
|
||||||
|
Loader --> Queue[WmoRenderBuildQueue]
|
||||||
|
Queue --> Job[WmoRenderBuildJob]
|
||||||
|
Job --> Loader
|
||||||
|
Loader --> Planner[WmoRenderBuildStepPlanner]
|
||||||
|
Planner --> Step[Operation and next cursors]
|
||||||
|
Step --> Loader
|
||||||
|
Loader --> Scene[Main-thread group Node attach]
|
||||||
|
Loader --> Budget[RenderBudgetScheduler permit]
|
||||||
|
```
|
||||||
|
|
||||||
|
The queue may strongly reference Node3D and Resource through typed jobs, but it
|
||||||
|
must not destroy, mutate or inspect their render content. RenderingServer, RID,
|
||||||
|
Mesh/MultiMesh instances, files, worker threads, mutexes, gameplay, network and
|
||||||
|
editor UI are forbidden dependencies.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
### `WmoRenderBuildJob`
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| constructor `(placement_key, root, render_resource)` | Constructor | Create cursor-zero job with strong references | Renderer main thread; until cancel/clear/replacement | Queue validates inputs before construction |
|
||||||
|
| `placement_key()` | Query | Return immutable queue identity | Any serialized caller; job lifetime | None |
|
||||||
|
| `root()` | Query | Borrow retained Node3D reference | Main thread; no ownership transfer | May later be invalid; loader checks |
|
||||||
|
| `render_resource()` | Query | Borrow retained Resource reference | Main thread; no ownership transfer | Queue does not validate cache contents |
|
||||||
|
| `mesh_index()` / `multimesh_index()` | Query | Return current group cursors | Serialized job drain | None |
|
||||||
|
| `adopt_cursors(next_mesh_index, next_multimesh_index)` | Command | Atomically adopt planner cursors | Renderer main thread | Raw integers are accepted |
|
||||||
|
| `diagnostic_snapshot()` | Query | Return detached scalar state and presence flags | Caller-owned result | Never exposes engine objects |
|
||||||
|
|
||||||
|
### `WmoRenderBuildQueue`
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `enqueue(placement_key, root, render_resource)` | Command | Replace keyed job and append FIFO key | Renderer main thread; map session | Empty/null input returns false |
|
||||||
|
| `has_pending()` / `pending_count()` | Query | Observe FIFO entries including duplicate/stale keys | Renderer main thread | None |
|
||||||
|
| `active_job_count()` | Query | Count current keyed jobs | Renderer main thread | None |
|
||||||
|
| `front_key()` / `pop_front()` | Queue operation | Inspect/remove one front FIFO key | Renderer main thread | Empty queue returns empty String |
|
||||||
|
| `has_job(placement_key)` | Query | Distinguish a live job from a stale FIFO key | Renderer main thread | Unknown key returns false |
|
||||||
|
| `job_for(placement_key)` | Diagnostic/test query | Borrow current job through its `RefCounted` base or return null | Renderer main thread | Runtime loader uses typed accessors instead |
|
||||||
|
| `root_for` / `render_resource_for` | Typed query | Borrow current engine references without exposing job implementation type | Renderer main thread | Stale key returns null |
|
||||||
|
| `mesh_index_for` / `multimesh_index_for` | Typed query | Return current cursors | Renderer main thread | Stale key returns zero |
|
||||||
|
| `adopt_cursors(placement_key, next_mesh_index, next_multimesh_index)` | Command/query | Atomically update current job cursors | Renderer main thread | Stale key returns false |
|
||||||
|
| `cancel(placement_key)` | Command/query | Remove keyed job and first matching FIFO key | Renderer main thread | Unknown key returns false; stale duplicates remain |
|
||||||
|
| `clear()` | Command | Release all job/key references idempotently | Reset/shutdown | Does not free engine objects |
|
||||||
|
| `diagnostic_snapshot()` | Query | Return detached FIFO order and key-sorted scalar jobs | Caller-owned result | None |
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Resolved placement key | Placement/build adapter | Queue/job | Copied String | Until cancel/clear/replacement |
|
||||||
|
| Input | WMO root Node3D | Loader instance adapter | Job | Strong reference; loader owns engine lifetime | Until job release |
|
||||||
|
| Input | Validated WMOStreamingResource | Loader cache adapter | Job | Strong Resource reference; cache/loader owns semantics | Until job release |
|
||||||
|
| Input | Next mesh/MultiMesh cursors | Build step planner through loader | Job | Integer values | Until next adoption |
|
||||||
|
| Output | FIFO front key and typed job | Queue | Loader drain | Borrowed references | One drain iteration |
|
||||||
|
| Output | Detached scalar diagnostics | Job/queue | Verifier/future facade adapter | Fresh values | Caller lifetime |
|
||||||
|
|
||||||
|
Side effects are limited to reference retention/release, cursor mutation and
|
||||||
|
Dictionary/Array ordering. Scene tree, material, RID, file and permit effects
|
||||||
|
remain outside the module.
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Enqueue[enqueue key/root/resource] --> Valid{Inputs valid?}
|
||||||
|
Valid -->|no| Reject[false; unchanged]
|
||||||
|
Valid -->|yes| Replace[Replace keyed typed job]
|
||||||
|
Replace --> Append[Append key even when duplicate]
|
||||||
|
Drain[front key] --> Lookup{Current keyed job exists?}
|
||||||
|
Lookup -->|no stale| Pop[pop front and continue]
|
||||||
|
Lookup -->|yes| Borrow[Borrow job to loader]
|
||||||
|
Borrow --> Plan[Loader invokes step planner]
|
||||||
|
Plan --> Adopt[Job adopts both next cursors]
|
||||||
|
Cancel[cancel key] --> EraseJob[Erase keyed job]
|
||||||
|
EraseJob --> EraseFirst[Erase first matching FIFO key]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle/state
|
||||||
|
|
||||||
|
The queue can be `Empty`, `Pending` or `StaleFront`. Duplicate enqueue followed
|
||||||
|
by cancel can intentionally produce `StaleFront`, matching the previous separate
|
||||||
|
Dictionary/Array behavior. The loader pops a stale front without consuming a
|
||||||
|
permit. `clear` returns every state to `Empty` without freeing engine objects.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> Empty
|
||||||
|
Empty --> Pending: valid enqueue
|
||||||
|
Pending --> Pending: enqueue / cursor adoption
|
||||||
|
Pending --> Empty: final cancel or clear
|
||||||
|
Pending --> StaleFront: duplicate key then cancel first occurrence
|
||||||
|
StaleFront --> Pending: pop stale, more live entries remain
|
||||||
|
StaleFront --> Empty: pop stale or clear
|
||||||
|
```
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Loader as StreamingWorldLoader
|
||||||
|
participant Queue as WmoRenderBuildQueue
|
||||||
|
participant Job as WmoRenderBuildJob
|
||||||
|
participant Planner as WmoRenderBuildStepPlanner
|
||||||
|
Loader->>Queue: enqueue(key, root, resource)
|
||||||
|
loop while permit and pending key
|
||||||
|
Loader->>Queue: front_key + has_job
|
||||||
|
alt stale key
|
||||||
|
Loader->>Queue: pop_front
|
||||||
|
else live job
|
||||||
|
Loader->>Queue: typed root/resource/cursor accessors
|
||||||
|
Loader->>Planner: plan_step(counts, job cursors)
|
||||||
|
Planner-->>Loader: operation and next cursors
|
||||||
|
Loader->>Loader: materialize one group on main thread
|
||||||
|
Loader->>Queue: adopt_cursors(key, next mesh, next MultiMesh)
|
||||||
|
Loader->>Loader: consume one permit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Loader->>Queue: cancel(key) or clear()
|
||||||
|
Note over Queue: references released; Nodes/Resources not freed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- Queue owns its job Dictionary and FIFO key Array.
|
||||||
|
- Job owns strong references, but loader owns Node/Resource semantic lifetime.
|
||||||
|
- Loader checks `is_instance_valid`, creates/frees Nodes and controls caches.
|
||||||
|
- Loader invokes planner and adopts cursor output on the main thread.
|
||||||
|
- Queue/job contain no mutex; all mutation is serialized by the renderer drain.
|
||||||
|
- `clear` may release the last Resource reference but never calls engine free APIs.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Empty key/null root/resource | Enqueue guards | Reject without mutation | Contract verifier | Repair caller input |
|
||||||
|
| Duplicate key | Existing keyed job | Replace job and append key | Queue diagnostics | Historical stale handling remains |
|
||||||
|
| Stale FIFO key | `job_for` returns null | Loader pops front without permit | Contract/source verifier | Continue drain |
|
||||||
|
| Root invalidated externally | Loader validity check | Loader cancels keyed job | Shutdown/runtime diagnostics | Placement can rebuild later |
|
||||||
|
| Resource absent | Loader check | Loader cancels keyed job | Runtime diagnostics | Reload/requeue placement |
|
||||||
|
| Final placement release | Placement registry returns final | Loader cancels build then frees Node | Registry/shutdown regressions | Later placement starts fresh |
|
||||||
|
| Reset/shutdown | Loader lifecycle | Queue clear releases references | Shutdown verifier | Next map starts empty |
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
The module introduces no settings or profile branches. WMO group permit count,
|
||||||
|
quality presets, cache limits, visibility and shadows remain loader-owned.
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
No data is serialized. WMO cache format/version/path and load request behavior
|
||||||
|
are unchanged. No migration or cache rebuild is required.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
`pending_count` preserves existing queue metrics, including stale/duplicate keys.
|
||||||
|
The detached snapshot exposes FIFO order, cursor values and root/resource presence
|
||||||
|
without exposing mutable jobs or engine objects. No runtime logs are emitted.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `verify_wmo_render_build_queue.gd`: invalid inputs, FIFO, typed references,
|
||||||
|
cursor adoption, duplicate replacement, stale pop, first-key cancel, clear,
|
||||||
|
engine lifetime, detached sorted diagnostics, source boundaries and timing.
|
||||||
|
- WMO planner/registry/resolver/material/shutdown and renderer regressions verify
|
||||||
|
unchanged ordering, permit use, destruction and surrounding lifecycle.
|
||||||
|
- Fidelity evidence is exact preservation of current queue/Dictionary semantics;
|
||||||
|
no original-client visual or asset-backed performance claim is made.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
- Cache/load-request ownership can be extracted separately without changing jobs.
|
||||||
|
- A later typed materialization command may consume the borrowed job and planner
|
||||||
|
output while this queue remains the pending-state owner.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| WMO render build pending-state ownership | Implemented | Lifecycle/source/timing verifier | Asset-backed traversal/leak evidence pending |
|
||||||
|
| WMO group planning | Implemented | Build-step planner verifier | Asset-backed p95/p99 pending |
|
||||||
|
| WMO render Resource state | Implemented extraction | Cache-state lifecycle/source/timing verifier | Asset-backed traversal/leak evidence pending |
|
||||||
|
| WMO materialization/cache I/O | Partial | Existing runtime regressions | Cached-scene and I/O seams remain |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- Jobs intentionally retain strong Node/Resource references until cancel/clear.
|
||||||
|
- Duplicate enqueue preserves a stale-key edge case rather than normalizing it.
|
||||||
|
- Resource array shape remains a WMO cache-builder invariant.
|
||||||
|
- No proprietary WMO corpus, traversal profile or original-client paired capture
|
||||||
|
is included in this package.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/wmo/wmo_render_build_job.gd` | Typed references and cursors |
|
||||||
|
| `src/render/wmo/wmo_render_build_queue.gd` | Keyed jobs, FIFO order, cancel/clear and diagnostics |
|
||||||
|
| `src/render/wmo/wmo_render_build_step_planner.gd` | Pure operation/cursor planning |
|
||||||
|
| `src/scenes/streaming/streaming_world_loader.gd` | Validation, materialization, permits and engine destruction |
|
||||||
|
| `src/tools/verify_wmo_render_build_queue.gd` | Lifecycle, ownership, boundary and timing regression |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`wmo-render-build-step-planner.md`](wmo-render-build-step-planner.md)
|
||||||
|
- [`wmo-render-resource-cache-state.md`](wmo-render-resource-cache-state.md)
|
||||||
|
- [`wmo-placement-registry.md`](wmo-placement-registry.md)
|
||||||
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
- [`../../RENDER.md`](../../RENDER.md)
|
||||||
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
# WMO Render Build Step Planner
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented |
|
||||||
|
| Target/work package | M03 / `M03-RND-WMO-RENDER-BUILD-PLANNER-001` |
|
||||||
|
| Owners | Pure mesh-first WMO render-group cursor decision |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-wmo-render-build-planner`, 2026-07-17 |
|
||||||
|
| Profiles/capabilities | Lightweight WMO render-cache path; profile-independent |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Select exactly one next lightweight WMO render build operation from mesh and
|
||||||
|
MultiMesh counts/cursors. The planner makes the ordering and cursor transition
|
||||||
|
testable without loading a WMO, creating a Node or consuming a frame budget.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Own render Resources, Nodes, materials, jobs, queues or permits.
|
||||||
|
- Read WMO arrays, create group instances or adopt cursors in a job.
|
||||||
|
- Change group ordering, names, transforms, shadows or visibility.
|
||||||
|
- Load, validate, version or persist a WMO cache.
|
||||||
|
- Implement portals, rooms, doodad semantics or WMO visual parity.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Cache[WMOStreamingResource arrays] --> Loader[StreamingWorldLoader]
|
||||||
|
Job[Loader mesh/MultiMesh cursors] --> Loader
|
||||||
|
Loader --> Planner[WmoRenderBuildStepPlanner]
|
||||||
|
Planner --> Step[Operation + selected index + next cursors]
|
||||||
|
Step --> Loader
|
||||||
|
Loader --> Material[Material refresh]
|
||||||
|
Loader --> Node[MeshInstance3D or MultiMeshInstance3D]
|
||||||
|
Loader --> Budget[RenderBudgetScheduler permit]
|
||||||
|
```
|
||||||
|
|
||||||
|
The planner depends only on `RefCounted`, integers, `StringName` and a fresh
|
||||||
|
Dictionary result. Node, Resource, Mesh, MultiMesh, RID, SceneTree, files,
|
||||||
|
threads, locks, caches, gameplay, network and editor UI are forbidden.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `OPERATION_MESH` | Constant | Identifies one mesh-group operation | Immutable | None |
|
||||||
|
| `OPERATION_MULTIMESH` | Constant | Identifies one doodad MultiMesh-group operation | Immutable | None |
|
||||||
|
| `OPERATION_COMPLETE` | Constant | Identifies exhausted mesh and MultiMesh ranges | Immutable | None |
|
||||||
|
| `plan_step(mesh_count, mesh_index, multimesh_count, multimesh_index)` | Pure query | Select one operation, selected index and next cursors | Any thread; caller-owned result | No clamp/error branch; raw integer comparisons are preserved |
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Mesh array count and current mesh cursor | Loader resource/job adapter | Planner | Integer values | One call |
|
||||||
|
| Input | MultiMesh array count and current MultiMesh cursor | Loader resource/job adapter | Planner | Integer values | One call |
|
||||||
|
| Output | `mesh`, `multimesh` or `complete` operation | Planner | Loader materializer/cancellation branch | Immutable `StringName` | One call |
|
||||||
|
| Output | Selected index | Planner | Loader Resource array access | Integer value | One operation |
|
||||||
|
| Output | Next mesh and MultiMesh cursors | Planner | Loader job adoption | Fresh Dictionary values | Until adopted/discarded |
|
||||||
|
|
||||||
|
The planner retains no input or output. Scene-tree mutation, material refresh,
|
||||||
|
job writes, permit consumption and cancellation are loader side effects.
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Input[Counts and cursors] --> MeshRemaining{mesh index < mesh count?}
|
||||||
|
MeshRemaining -->|yes| MeshStep[mesh at mesh index; increment mesh cursor]
|
||||||
|
MeshRemaining -->|no| MultiRemaining{MultiMesh index < MultiMesh count?}
|
||||||
|
MultiRemaining -->|yes| MultiStep[MultiMesh at its index; increment MultiMesh cursor]
|
||||||
|
MultiRemaining -->|no| Complete[complete; keep both cursors]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle/state
|
||||||
|
|
||||||
|
The planner is stateless. State belongs to the loader job. Repeated loader calls
|
||||||
|
advance through `mesh` steps, then `multimesh` steps, then `complete`. Planner
|
||||||
|
construction, cancellation, map reset and shutdown require no cleanup.
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Loader as StreamingWorldLoader
|
||||||
|
participant Planner as WmoRenderBuildStepPlanner
|
||||||
|
participant Scene as SceneTree
|
||||||
|
participant Budget as RenderBudgetScheduler
|
||||||
|
Loader->>Planner: plan_step(counts, cursors)
|
||||||
|
Planner-->>Loader: operation, selected index, next cursors
|
||||||
|
alt mesh or MultiMesh
|
||||||
|
Loader->>Loader: refresh material and create group Node
|
||||||
|
Loader->>Scene: attach Node and editor owner
|
||||||
|
Loader->>Loader: adopt returned cursor
|
||||||
|
Loader->>Budget: consume one WMO group permit
|
||||||
|
else complete
|
||||||
|
Loader->>Loader: cancel/remove job and queue key
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- The planner owns only call-local scalar comparisons and the returned value map.
|
||||||
|
- `WmoRenderBuildQueue` owns typed jobs, FIFO keys and strong Node3D/Resource
|
||||||
|
references. The loader owns mesh-array interpretation and engine lifetime.
|
||||||
|
- The loader creates and owns every MeshInstance3D/MultiMeshInstance3D and
|
||||||
|
performs material refresh and editor-owner assignment on the main thread.
|
||||||
|
- `RenderBudgetScheduler` owns permits; the planner neither reads nor consumes one.
|
||||||
|
- Pure calls are thread-safe because the planner has no mutable state.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Empty arrays | Both `0 < 0` comparisons false | Return `complete` | Contract verifier | Loader removes job |
|
||||||
|
| Cursor at/beyond count | Raw integer comparison | Skip that range | Contract verifier | Continue next range or complete |
|
||||||
|
| Negative cursor/count | Raw historical comparison | No clamp; select exactly as prior loader condition | Edge fixture | Caller should maintain valid job cursors |
|
||||||
|
| Missing/invalid Resource | Loader validation before planning | Loader cancels job | Existing shutdown/runtime diagnostics | Reload/requeue placement |
|
||||||
|
| Root freed | Loader validity check before planning | Loader cancels job | Runtime-cache shutdown verifier | Placement may rebuild later |
|
||||||
|
| Placement final release | Loader/registry lifecycle | Loader cancels queued build | Registry regression | Later placement starts at cursor zero |
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
The planner has no setting or profile branch. `wmo_render_group_ops_per_tick`,
|
||||||
|
quality presets, shadow/visibility settings and cache limits remain loader-owned.
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
No data is persisted. WMO render resource format/version, cache path and job
|
||||||
|
Dictionary fields are unchanged. No migration or cache rebuild is required.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
The planner emits no logs. Its verifier reports operation/cursor/source contracts
|
||||||
|
and bounded timing. Queue lengths, active WMO count and hitch sections remain in
|
||||||
|
existing loader/facade diagnostics.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `verify_wmo_render_build_step_planner.gd`: mesh-first order, cursor increments,
|
||||||
|
completion, empty and raw negative integer behavior, fresh results, loader
|
||||||
|
delegation, dependency boundary and 20,000-plan timing.
|
||||||
|
- WMO registry/resolver/material/shutdown plus renderer regressions verify the
|
||||||
|
unchanged ownership and surrounding runtime path.
|
||||||
|
- Fidelity evidence is exact extraction of existing mesh-before-MultiMesh and
|
||||||
|
one-index-per-permit behavior. No asset-backed build-12340 visual claim is made.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
- `WmoRenderBuildQueue` now owns typed pending state; cache/load requests and
|
||||||
|
materialization remain separate extraction seams.
|
||||||
|
- Materialization can later receive typed commands without changing this planner.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| WMO render build step planning | Implemented | Pure contract/source/timing verifier | Asset-backed traversal p95/p99 pending |
|
||||||
|
| WMO build job/queue ownership | Implemented | Typed lifecycle/source/timing verifier | Asset-backed traversal/leak evidence pending |
|
||||||
|
| WMO visual/portal/material fidelity | Partial | Renderer baseline/material checks | Original-client paired evidence pending |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- Loader build jobs remain untyped Dictionaries.
|
||||||
|
- Resource array shape/length consistency remains a cache-builder invariant.
|
||||||
|
- No proprietary WMO corpus, traversal profile or paired client capture is part
|
||||||
|
of this extraction.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/wmo/wmo_render_build_step_planner.gd` | Pure operation and cursor selection |
|
||||||
|
| `src/render/wmo/wmo_render_build_job.gd` | Typed engine references and cursors |
|
||||||
|
| `src/render/wmo/wmo_render_build_queue.gd` | Pending jobs, FIFO keys and cancellation state |
|
||||||
|
| `src/scenes/streaming/streaming_world_loader.gd` | Resource validation, materialization, permits and engine lifetime |
|
||||||
|
| `src/tools/verify_wmo_render_build_step_planner.gd` | Contract, boundary and timing regression |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`wmo-placement-registry.md`](wmo-placement-registry.md)
|
||||||
|
- [`wmo-render-build-queue.md`](wmo-render-build-queue.md)
|
||||||
|
- [`wmo-placement-resolver.md`](wmo-placement-resolver.md)
|
||||||
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
- [`../../RENDER.md`](../../RENDER.md)
|
||||||
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
# WMO Render Resource Cache State
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented |
|
||||||
|
| Target/work package | M03 / `M03-RND-WMO-RENDER-RESOURCE-CACHE-001` |
|
||||||
|
| Owners | Validated lightweight-WMO render Resources, negative cache and pending request paths |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-wmo-render-resource-cache`, 2026-07-17 |
|
||||||
|
| Profiles/capabilities | Lightweight WMO render-cache path; profile-independent |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Own the mutually exclusive cached, missing and pending states for normalized
|
||||||
|
lightweight-WMO render paths outside the monolithic streamer. The state holder
|
||||||
|
accepts only caller-validated `Resource` references and records cache paths for
|
||||||
|
threaded requests whose I/O lifecycle remains in `StreamingWorldLoader`.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Normalize WMO paths or construct `.res` cache paths.
|
||||||
|
- Call `ResourceLoader`, inspect files or validate script/format versions.
|
||||||
|
- Own WMO cached scenes, live prototypes, placements, build jobs or engine Nodes.
|
||||||
|
- Change fallback order, retry timing, quality profiles or visible output.
|
||||||
|
- Persist, migrate or build WMO caches.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Placement[Normalized WMO render lookup] --> Loader[StreamingWorldLoader]
|
||||||
|
Loader --> State[WmoRenderResourceCacheState]
|
||||||
|
State -->|cached Resource| Loader
|
||||||
|
Loader -->|cache path| ResourceLoader[Godot ResourceLoader]
|
||||||
|
ResourceLoader -->|status and loaded Resource| Loader
|
||||||
|
Loader --> Validate[Script and FORMAT_VERSION validation]
|
||||||
|
Validate -->|accepted Resource or missing| State
|
||||||
|
Loader --> Fallback[Cached scene or live-prototype fallback]
|
||||||
|
```
|
||||||
|
|
||||||
|
The module may retain validated `Resource` references and copied path Strings.
|
||||||
|
It has no FileAccess, ResourceLoader, Node, RID, worker, mutex, gameplay, network
|
||||||
|
or editor dependency. Cache validation stays at the I/O boundary in the loader.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `resource_for(path)` | Query | Borrow validated cached Resource | Renderer main thread; until full clear | Empty/unknown returns null |
|
||||||
|
| `contains_resource(path)` | Query | Test accepted cache state | Renderer main thread | Empty returns false |
|
||||||
|
| `is_missing(path)` | Query | Test negative cache state | Renderer main thread | Empty returns false |
|
||||||
|
| `has_request(path)` | Query | Test pending threaded-request state | Renderer main thread | Empty returns false |
|
||||||
|
| `remember_request(path, cache_path)` | Command/query | Record one loader-started request | Renderer main thread; until terminal/reset | Invalid or occupied state returns false |
|
||||||
|
| `request_paths_snapshot()` | Query | Copy pending normalized/cache-path mapping | Poll or shutdown drain | Detached Dictionary |
|
||||||
|
| `complete_request_with_resource(path, resource)` | Command/query | Remove pending request and adopt caller-validated Resource | Terminal loader poll | Unknown/null returns false |
|
||||||
|
| `complete_request_as_missing(path)` | Command/query | Remove pending request and adopt negative state | Terminal loader poll | Unknown returns false |
|
||||||
|
| `clear_transient_state()` | Command | Clear pending and missing while retaining accepted Resources | Map reset/request drain | Idempotent |
|
||||||
|
| `clear_all()` | Command | Release Resources, pending and missing | Final runtime cache release | Idempotent |
|
||||||
|
| `pending_request_count()` | Query | Preserve renderer queue metric contribution | Renderer diagnostics | None |
|
||||||
|
| `diagnostic_snapshot()` | Query | Return sorted detached scalar keys | Tests/diagnostics | Never exposes Resources |
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Normalized relative WMO path | Placement resolver through loader | Cache state | Copied String | Map/cache lifetime |
|
||||||
|
| Input | `.res` cache path after accepted threaded-request result | Loader | Pending state | Copied String | Until terminal/reset |
|
||||||
|
| Input | Script/version-validated WMO Resource | Loader validation adapter | Resource cache | Strong reference | Until full clear |
|
||||||
|
| Output | Borrowed validated Resource | Cache state | Loader render-root/build adapters | State retains reference | Lookup/build start |
|
||||||
|
| Output | Detached pending-path snapshot | Cache state | Loader ResourceLoader polling/shutdown | Caller-owned Dictionary | One drain pass |
|
||||||
|
| Output | Sorted scalar diagnostic keys | Cache state | Verifier/future metrics | Caller-owned values | Snapshot lifetime |
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Lookup[Lookup normalized path] --> Cached{Cached?}
|
||||||
|
Cached -->|yes| Return[Return validated Resource]
|
||||||
|
Cached -->|no| Blocked{Missing or pending?}
|
||||||
|
Blocked -->|yes| Null[Return null; loader waits/falls back]
|
||||||
|
Blocked -->|no| Start[Loader starts threaded request]
|
||||||
|
Start --> Remember[remember_request]
|
||||||
|
Remember --> Poll[Loader polls detached request snapshot]
|
||||||
|
Poll --> Terminal{Loaded or failed?}
|
||||||
|
Terminal -->|no| Poll
|
||||||
|
Terminal -->|failed| Missing[complete as missing]
|
||||||
|
Terminal -->|loaded| Validate[Loader validates script/version]
|
||||||
|
Validate -->|valid| Adopt[complete with Resource]
|
||||||
|
Validate -->|invalid| Missing
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle/state
|
||||||
|
|
||||||
|
Each normalized path is `Absent`, `Pending`, `Cached` or `Missing`. Valid request
|
||||||
|
completion leaves exactly one terminal state. Transient clear moves `Pending` and
|
||||||
|
`Missing` to `Absent` but retains `Cached`; full clear moves every state to
|
||||||
|
`Absent` and releases retained Resources.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> Absent
|
||||||
|
Absent --> Pending: remember valid request
|
||||||
|
Pending --> Cached: validated Resource completion
|
||||||
|
Pending --> Missing: load/validation failure
|
||||||
|
Pending --> Absent: clear transient/full
|
||||||
|
Missing --> Absent: clear transient/full
|
||||||
|
Cached --> Absent: full clear
|
||||||
|
```
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Loader as StreamingWorldLoader
|
||||||
|
participant State as WmoRenderResourceCacheState
|
||||||
|
participant RL as ResourceLoader
|
||||||
|
Loader->>State: cached/missing/pending queries
|
||||||
|
Loader->>RL: exists + load_threaded_request(cache path)
|
||||||
|
Loader->>State: remember_request(normalized, cache path)
|
||||||
|
loop renderer tick
|
||||||
|
Loader->>State: request_paths_snapshot()
|
||||||
|
Loader->>RL: load_threaded_get_status(cache path)
|
||||||
|
end
|
||||||
|
alt load failed
|
||||||
|
Loader->>State: complete_request_as_missing(normalized)
|
||||||
|
else loaded
|
||||||
|
Loader->>RL: load_threaded_get(cache path)
|
||||||
|
Loader->>Loader: validate script and FORMAT_VERSION
|
||||||
|
Loader->>State: complete with Resource or as missing
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- The state owns three Dictionaries and strong references to accepted Resources.
|
||||||
|
- The loader owns normalization, cache paths, ResourceLoader calls and validation.
|
||||||
|
- All mutation is serialized by the renderer main-thread lookup/drain lifecycle.
|
||||||
|
- No mutex or callback is needed; detached request snapshots permit safe removal.
|
||||||
|
- The loader/build queue borrow Resources without transferring ownership.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Empty path/cache path | Request guard | Reject unchanged | Contract verifier | Repair caller input |
|
||||||
|
| Duplicate or occupied path | State membership | Reject request unchanged | Contract/source verifier | Await terminal/reset |
|
||||||
|
| Threaded load failed | Loader status | Complete as missing | Existing renderer metrics | Transient reset permits retry |
|
||||||
|
| Wrong script/old format | Loader validation | Complete as missing | Cache-version source gate | Rebuild cache, then reset |
|
||||||
|
| Shutdown while pending | Loader drains ResourceLoader snapshot | Clear transient afterward | Shutdown verifier | Next runtime starts absent |
|
||||||
|
| Final cache release | Loader shutdown lifecycle | Full clear releases Resources | Shutdown verifier | Recreated with loader |
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
The module adds no setting or profile branch. `wmo_render_cache_dir`, resource
|
||||||
|
format version, request scheduling and WMO build budgets remain loader-owned.
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
The state is runtime-only and serializes nothing. `WMOStreamingResource` script
|
||||||
|
identity and `FORMAT_VERSION` validation remain unchanged in the loader; no cache
|
||||||
|
migration or rebuild is introduced by this extraction.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
`pending_request_count` preserves the existing `wmobuild` metric contribution.
|
||||||
|
Diagnostics expose only sorted normalized-path keys for cached/missing/pending
|
||||||
|
states; Resource references and cache file paths are not exposed. No logs emit.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `verify_wmo_render_resource_cache_state.gd`: invalid input, detached requests,
|
||||||
|
successful/missing terminal transitions, exclusivity, partial/full reset,
|
||||||
|
sorted diagnostics, ownership/source boundary and bounded timing.
|
||||||
|
- `verify_render_runtime_cache_shutdown.gd`: full release of accepted Resources
|
||||||
|
and negative state through the loader lifecycle.
|
||||||
|
- WMO queue/planner/registry/resolver and surrounding renderer regressions verify
|
||||||
|
unchanged fallback/build behavior. This package makes no visual parity claim.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
- Cached WMO scene request state is a separate typed module because its size
|
||||||
|
limit, PackedScene validation and missing-file behavior differ.
|
||||||
|
- A later loader I/O adapter may consume the detached request snapshot while
|
||||||
|
preserving this state contract and cache-version boundary.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Lightweight WMO render Resource state | Implemented extraction | Lifecycle/source/timing and shutdown verifiers | Asset-backed traversal/leak evidence pending |
|
||||||
|
| Cache script/version validation | Preserved in loader | Source boundary and WMO regressions | Dedicated corrupt-cache fixture could follow |
|
||||||
|
| Cached WMO scene state | Implemented extraction | Scene-cache lifecycle/source/timing verifier | Asset-backed traversal/leak evidence pending |
|
||||||
|
| WMO materialization | Partial/loader-owned | Queue/planner regressions | Further safe extraction |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- Strong Resource references live until final cache release, matching prior state.
|
||||||
|
- Missing cache files remain unrecorded for render Resources, so lookup can retry;
|
||||||
|
this intentionally differs from the cached-scene path.
|
||||||
|
- No proprietary WMO corpus, corrupt-version fixture, leak run, traversal p95/p99
|
||||||
|
or paired original-client capture is included.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/wmo/wmo_render_resource_cache_state.gd` | Resource/missing/request state and resets |
|
||||||
|
| `src/scenes/streaming/streaming_world_loader.gd` | Normalize, request, poll, validate, fallback and shutdown I/O |
|
||||||
|
| `src/tools/verify_wmo_render_resource_cache_state.gd` | State, boundary and timing regression |
|
||||||
|
| `src/tools/verify_render_runtime_cache_shutdown.gd` | Loader final Resource ownership regression |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`wmo-render-build-queue.md`](wmo-render-build-queue.md)
|
||||||
|
- [`wmo-scene-resource-cache-state.md`](wmo-scene-resource-cache-state.md)
|
||||||
|
- [`wmo-placement-resolver.md`](wmo-placement-resolver.md)
|
||||||
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
- [`../../RENDER.md`](../../RENDER.md)
|
||||||
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
# WMO Scene Resource Cache State
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented |
|
||||||
|
| Target/work package | M03 / `M03-RND-WMO-SCENE-RESOURCE-CACHE-001` |
|
||||||
|
| Owners | Validated cached-WMO PackedScenes, negative cache and pending request paths |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-wmo-scene-resource-cache`, 2026-07-17 |
|
||||||
|
| Profiles/capabilities | Cached WMO `.tscn` fallback; profile-independent state |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Own mutually exclusive cached, missing and pending states for normalized WMO
|
||||||
|
scene paths. The state accepts only caller-validated `PackedScene` references and
|
||||||
|
records `.tscn` paths for threaded requests whose I/O, size limit and cache
|
||||||
|
metadata validation remain in `StreamingWorldLoader`.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Normalize WMO paths, build `.tscn` paths or measure files.
|
||||||
|
- Call ResourceLoader/FileAccess, instantiate scenes or validate cache metadata.
|
||||||
|
- Own live prototypes, placement/build jobs, instantiated Nodes or RIDs.
|
||||||
|
- Change missing, oversize, request-error, retry or fallback behavior.
|
||||||
|
- Persist, migrate or build WMO caches.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Lookup[Normalized WMO scene lookup] --> Loader[StreamingWorldLoader]
|
||||||
|
Loader --> State[WmoSceneResourceCacheState]
|
||||||
|
Loader --> Size[File existence and size limit]
|
||||||
|
Size --> ResourceLoader[Godot ResourceLoader]
|
||||||
|
ResourceLoader --> Loader
|
||||||
|
Loader --> Probe[Instantiate, metadata/version check, free]
|
||||||
|
Probe -->|accepted PackedScene or missing| State
|
||||||
|
Loader --> Live[Live-prototype fallback]
|
||||||
|
```
|
||||||
|
|
||||||
|
The module retains validated PackedScenes and copied paths only. ResourceLoader,
|
||||||
|
FileAccess, WMOBuilder, Node, RID, worker, gameplay, network and editor concerns
|
||||||
|
remain outside it.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `scene_for(path)` / `contains_scene(path)` | Query | Borrow/test validated PackedScene | Renderer main thread; until full clear | Empty/unknown returns null/false |
|
||||||
|
| `is_missing(path)` / `has_request(path)` | Query | Test negative/pending state | Renderer main thread | Empty returns false |
|
||||||
|
| `remember_request(path, cache_path)` | Command/query | Record loader-started request | Until terminal/reset | Invalid/occupied returns false |
|
||||||
|
| `mark_missing(path)` | Command/query | Record absent/oversize/request-start failure without pending state | Until transient/full clear | Invalid/occupied returns false |
|
||||||
|
| `request_paths_snapshot()` | Query | Copy pending normalized/cache-path mapping | Poll/shutdown drain | Detached Dictionary |
|
||||||
|
| `complete_request_with_scene(path, scene)` | Command/query | Remove pending and adopt validated PackedScene | Terminal poll | Unknown/null returns false |
|
||||||
|
| `complete_request_as_missing(path)` | Command/query | Remove pending and adopt negative state | Terminal poll | Unknown returns false |
|
||||||
|
| `clear_transient_state()` | Command | Clear pending/missing and retain scenes | Map reset/request drain | Idempotent |
|
||||||
|
| `clear_all()` | Command | Release scenes and transient state | Final runtime-cache release | Idempotent |
|
||||||
|
| `pending_request_count()` | Query | Preserve renderer queue metric | Diagnostics | None |
|
||||||
|
| `diagnostic_snapshot()` | Query | Return sorted detached scalar keys | Tests/diagnostics | No PackedScene exposure |
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Normalized WMO path | Placement resolver through loader | State | Copied String | Cache lifetime |
|
||||||
|
| Input | `.tscn` path after accepted request start | Loader | Pending state | Copied String | Until terminal/reset |
|
||||||
|
| Input | Metadata/version-validated PackedScene | Loader probe adapter | Scene cache | Strong reference | Until full clear |
|
||||||
|
| Input | Missing decision | Loader file/size/request/load/validation adapters | Negative cache | Boolean membership | Until transient/full clear |
|
||||||
|
| Output | Borrowed PackedScene | State | Loader scene instantiation | State retains reference | Lookup/instantiate call |
|
||||||
|
| Output | Detached request snapshot | State | Loader polling/shutdown | Caller-owned Dictionary | One drain pass |
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Lookup --> Known{Cached, missing or pending?}
|
||||||
|
Known -->|cached| Return[Return PackedScene]
|
||||||
|
Known -->|missing/pending| Null[Return null]
|
||||||
|
Known -->|absent| Check[Loader checks file and size]
|
||||||
|
Check -->|missing/oversize| Mark[mark_missing]
|
||||||
|
Check -->|allowed| Request[Start threaded request]
|
||||||
|
Request -->|error| Mark
|
||||||
|
Request -->|accepted| Remember[remember_request]
|
||||||
|
Remember --> Poll[Loader polls snapshot]
|
||||||
|
Poll -->|failure| CompleteMissing[complete as missing]
|
||||||
|
Poll -->|loaded| Validate[Instantiate and validate cache metadata]
|
||||||
|
Validate -->|valid| CompleteScene[complete with scene]
|
||||||
|
Validate -->|invalid| CompleteMissing
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle/state
|
||||||
|
|
||||||
|
Each normalized path is `Absent`, `Pending`, `Cached` or `Missing`. Direct file,
|
||||||
|
size and request-start rejection moves `Absent` directly to `Missing`; terminal
|
||||||
|
loads move `Pending` to `Cached` or `Missing`. Transient clear retains `Cached`,
|
||||||
|
while full clear returns every path to `Absent`.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> Absent
|
||||||
|
Absent --> Pending: accepted request
|
||||||
|
Absent --> Missing: absent, oversize or request error
|
||||||
|
Pending --> Cached: validated PackedScene
|
||||||
|
Pending --> Missing: load or validation failure
|
||||||
|
Pending --> Absent: transient/full clear
|
||||||
|
Missing --> Absent: transient/full clear
|
||||||
|
Cached --> Absent: full clear
|
||||||
|
```
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Loader as StreamingWorldLoader
|
||||||
|
participant State as WmoSceneResourceCacheState
|
||||||
|
participant RL as ResourceLoader
|
||||||
|
Loader->>State: cached/missing/pending queries
|
||||||
|
Loader->>Loader: exists and wmo_max_runtime_scene_mb check
|
||||||
|
Loader->>RL: load_threaded_request(.tscn)
|
||||||
|
Loader->>State: remember_request(normalized, path)
|
||||||
|
loop renderer tick
|
||||||
|
Loader->>State: request_paths_snapshot()
|
||||||
|
Loader->>RL: load_threaded_get_status(path)
|
||||||
|
end
|
||||||
|
Loader->>RL: load_threaded_get(path)
|
||||||
|
Loader->>Loader: instantiate, validate WMO metadata, free probe
|
||||||
|
Loader->>State: complete with scene or as missing
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- State owns three Dictionaries and strong references to accepted PackedScenes.
|
||||||
|
- Loader owns paths, file measurement, requests, validation and all Node lifetime.
|
||||||
|
- All mutation is serialized on the renderer main thread; no mutex is required.
|
||||||
|
- Detached request snapshots allow terminal removal while polling.
|
||||||
|
- Scene instantiation borrows the PackedScene and does not transfer cache ownership.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure/state | Detection | Behavior | Recovery |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Missing `.tscn` | Loader ResourceLoader existence check | Direct missing state | Transient reset permits later retry |
|
||||||
|
| Oversize `.tscn` | Loader byte limit | Missing plus unchanged debug log | Raise limit/rebuild, then reset |
|
||||||
|
| Request-start/load failure | Loader error/status | Direct or terminal missing | Reset and retry later |
|
||||||
|
| Wrong type/stale metadata | Loader PackedScene/probe validation | Terminal missing; probe freed | Rebuild cache and reset |
|
||||||
|
| Shutdown while pending | Loader drains snapshot | Clear transient, then full cache release | New loader starts absent |
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
No setting is added. `wmo_cache_dir`, `wmo_max_runtime_scene_mb`, request timing,
|
||||||
|
quality profiles and WMO build budgets stay loader-owned.
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
The module is runtime-only. Existing WMOBuilder cache metadata/version checks
|
||||||
|
remain unchanged and execute before state adoption. No cache format, migration or
|
||||||
|
rebuild is introduced.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
Pending count preserves the existing `wmobuild` metric contribution. The snapshot
|
||||||
|
exposes sorted scene/missing/request keys only. Existing oversize logging remains
|
||||||
|
in the loader; the state emits no logs.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `verify_wmo_scene_resource_cache_state.gd`: invalid input, direct missing,
|
||||||
|
exclusivity, request completion, partial/full reset, diagnostics, boundaries
|
||||||
|
and bounded timing.
|
||||||
|
- `verify_render_runtime_cache_shutdown.gd`: loader full release of scenes,
|
||||||
|
negative and pending state.
|
||||||
|
- Adjacent WMO/renderer regressions verify unchanged lookup/fallback behavior.
|
||||||
|
This extraction makes no visual or build-12340 parity claim.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
- A later I/O adapter may consume request snapshots while retaining this contract.
|
||||||
|
- Scene instantiation/material preparation can be extracted separately because it
|
||||||
|
owns Nodes and main-thread side effects rather than cache state.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Cached WMO PackedScene state | Implemented extraction | Lifecycle/source/timing and shutdown verifiers | Asset-backed traversal/leak evidence pending |
|
||||||
|
| Size and cache metadata validation | Preserved in loader | Source boundary and WMO regressions | Oversize/stale asset fixtures could follow |
|
||||||
|
| ResourceLoader I/O and live fallback | Partial/loader-owned | Existing runtime behavior | Separate extraction if justified |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- PackedScenes remain strongly referenced until final shutdown cache release.
|
||||||
|
- Negative caching suppresses retries until transient reset, matching prior behavior.
|
||||||
|
- No proprietary WMO corpus, stale/oversize asset fixture, leak run, traversal
|
||||||
|
p95/p99 or paired original-client capture is included.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/wmo/wmo_scene_resource_cache_state.gd` | Scene/missing/request state and resets |
|
||||||
|
| `src/scenes/streaming/streaming_world_loader.gd` | Paths, file limit, requests, validation, fallback and Node lifetime |
|
||||||
|
| `src/tools/verify_wmo_scene_resource_cache_state.gd` | State, boundary and timing regression |
|
||||||
|
| `src/tools/verify_render_runtime_cache_shutdown.gd` | Loader final cache ownership regression |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`wmo-render-resource-cache-state.md`](wmo-render-resource-cache-state.md)
|
||||||
|
- [`wmo-render-build-queue.md`](wmo-render-build-queue.md)
|
||||||
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
- [`../../RENDER.md`](../../RENDER.md)
|
||||||
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
# World Entity Presentation
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented boundary / Prototype visuals |
|
||||||
|
| Target/work package | M03 / `M03-RND-FACADE-ENTITY-001` |
|
||||||
|
| Owners | Renderer entity-presentation boundary and visual subtree lifecycle |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-facade-entity`, 2026-07-16 |
|
||||||
|
| Profiles/capabilities | Contract version 1; asset-free scene presentation only |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Accept immutable session-local entity visual snapshots through
|
||||||
|
`WorldRenderFacade`, create or update renderer-owned visual subtrees idempotently,
|
||||||
|
and remove them explicitly without making the renderer authoritative for entity,
|
||||||
|
gameplay, network or content state.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Allocate `EntityId` or map it to `WowGuid`, display ID or server entry.
|
||||||
|
- Present the separately composed local player through this service.
|
||||||
|
- Define build-12340 model, equipment, animation, effects or visibility rules.
|
||||||
|
- Own gameplay/world state, packet decoding, terrain or streaming decisions.
|
||||||
|
- Provide asynchronous loading, pooling, batching, persistence or cache formats.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
WorldState[Future authoritative WorldState] --> Adapter[Entity presentation adapter]
|
||||||
|
Adapter --> Snapshot[EntityPresentationSnapshot v1]
|
||||||
|
Snapshot --> Facade[WorldRenderFacade]
|
||||||
|
Facade --> Presenter[WorldEntityPresenter]
|
||||||
|
Asset[PackedScene visual path] --> Presenter
|
||||||
|
Presenter --> Visual[Owned Node3D visual subtree]
|
||||||
|
LocalPlayer[Local player composition] -. separate boundary .-> Viewport[Rendered world]
|
||||||
|
Visual --> Viewport
|
||||||
|
```
|
||||||
|
|
||||||
|
Allowed dependencies:
|
||||||
|
|
||||||
|
- immutable M01 `EntityId` and `GodotWorldPosition` values;
|
||||||
|
- renderer-facing Godot transform and scene APIs;
|
||||||
|
- an explicit repository/resource path selected outside the renderer;
|
||||||
|
- `WorldRenderFacade` and main-thread runtime scene composition.
|
||||||
|
|
||||||
|
Forbidden dependencies:
|
||||||
|
|
||||||
|
- gameplay reducers, intents, combat/movement authority or entity allocation;
|
||||||
|
- packet codecs, GUID interpretation, server DB access or Content Project writes;
|
||||||
|
- streamer queues/tasks/caches, terrain query or camera/input state;
|
||||||
|
- hidden autoload, SceneTree group service lookup or persisted session IDs.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `EntityPresentationSnapshot.CONTRACT_VERSION` | Contract constant | Identifies additive snapshot contract version `1` | Compile/runtime constant | Incompatible changes require migration/ADR |
|
||||||
|
| `EntityPresentationSnapshot.create(...)` | Immutable value factory | Captures identity, typed position, visual path, yaw, scale and visibility | Any thread to construct; caller-owned | Invalid value carries stable `entity_presentation_*` failure code |
|
||||||
|
| `WorldRenderFacade.present_entity(snapshot)` | Command | Idempotently creates/updates/replaces one world visual | Main thread; world session | Returns `false` for invalid snapshot, missing service or failed scene creation |
|
||||||
|
| `WorldRenderFacade.remove_entity(entity_id)` | Command | Removes one known session visual | Main thread; world session | Returns `false` for invalid or unknown identity |
|
||||||
|
| `WorldRenderFacade.entity_presentation_snapshot()` | Diagnostic query | Returns detached scalar entity inventory | Main thread; caller-owned copy | Missing/invalid service returns empty snapshot with diagnostic |
|
||||||
|
| `WorldEntityPresenter.present_entity(snapshot)` | Internal service command | Owns visual load, attach, transform update and replacement | Main thread; service lifetime | Failed replacement retains prior valid subtree |
|
||||||
|
| `WorldEntityPresenter.remove_entity(entity_id)` | Internal service command | Detaches and schedules owned subtree deletion | Main thread | Unknown identity is unchanged/false |
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Valid session-local `EntityId` | Future entity registry/adapter | Snapshot/presenter index | Immutable caller reference | One runtime session |
|
||||||
|
| Input | `GodotWorldPosition` | World-state presentation adapter | Snapshot/presenter transform | Immutable caller reference | One snapshot |
|
||||||
|
| Input | PackedScene resource path | Asset-selection adapter | Presenter | Copied string; resource remains Godot-owned | Until visual replacement |
|
||||||
|
| Input | Finite yaw radians, positive scale, visibility | Presentation adapter | Presenter | Scalar copies | One snapshot |
|
||||||
|
| Output | Owned Node3D subtree | Presenter | SceneTree/viewport | Presenter child ownership | Until replacement/removal/world teardown |
|
||||||
|
| Output | Detached inventory dictionary | Presenter/facade | Diagnostics/tests | Caller-owned deep copy | One query |
|
||||||
|
|
||||||
|
Side effects:
|
||||||
|
|
||||||
|
- synchronously loads a PackedScene only for first presentation or visual-path change;
|
||||||
|
- instantiates, attaches, transforms, detaches and queues visual Node3D subtrees;
|
||||||
|
- does not mutate authoritative state, streamer queues, caches, files, DB or network;
|
||||||
|
- does not retain caller snapshots after an entity entry is replaced or removed.
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
ReadModel[Entity visual read model] --> Validate[Validate typed immutable snapshot]
|
||||||
|
Validate -->|invalid| Reject[Return false; no scene mutation]
|
||||||
|
Validate -->|valid| Lookup[Lookup by EntityId debug key]
|
||||||
|
Lookup -->|same visual path| Transform[Update world transform and visibility]
|
||||||
|
Lookup -->|new or changed path| Instantiate[Load and instantiate Node3D]
|
||||||
|
Instantiate -->|failure| Retain[Retain previous valid visual]
|
||||||
|
Instantiate -->|success| Replace[Detach old, attach replacement]
|
||||||
|
Replace --> Transform
|
||||||
|
Transform --> Inventory[Detached diagnostics]
|
||||||
|
Remove[remove_entity] --> Detach[Detach and queue_free owned root]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle/state
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> Absent
|
||||||
|
Absent --> Presented: valid present + successful instantiate
|
||||||
|
Absent --> Absent: invalid/missing visual
|
||||||
|
Presented --> Presented: same-path snapshot update
|
||||||
|
Presented --> Replacing: changed visual path
|
||||||
|
Replacing --> Presented: replacement succeeds
|
||||||
|
Replacing --> Presented: replacement fails; retain old
|
||||||
|
Presented --> Absent: remove_entity
|
||||||
|
Presented --> [*]: world/service teardown
|
||||||
|
Absent --> [*]: world/service teardown
|
||||||
|
```
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Adapter as World-state adapter
|
||||||
|
participant Facade as WorldRenderFacade
|
||||||
|
participant Presenter as WorldEntityPresenter
|
||||||
|
participant Loader as Godot ResourceLoader
|
||||||
|
participant Tree as SceneTree
|
||||||
|
Adapter->>Facade: present_entity(immutable snapshot)
|
||||||
|
Facade->>Presenter: present_entity(snapshot)
|
||||||
|
alt same identity and visual path
|
||||||
|
Presenter->>Tree: update transform/visibility
|
||||||
|
else new or changed visual
|
||||||
|
Presenter->>Loader: load PackedScene
|
||||||
|
Loader-->>Presenter: scene or failure
|
||||||
|
alt scene is valid Node3D
|
||||||
|
Presenter->>Tree: detach old, attach/configure new
|
||||||
|
else load/shape failure
|
||||||
|
Presenter-->>Facade: false; old visual retained
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Adapter->>Facade: remove_entity(EntityId)
|
||||||
|
Facade->>Presenter: remove_entity(EntityId)
|
||||||
|
Presenter->>Tree: detach + queue_free owned root
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- `EntityPresentationSnapshot` owns copied scalars/strings and immutable references
|
||||||
|
to caller-owned identity/position values; it owns no Node, Resource or RID.
|
||||||
|
- `WorldEntityPresenter` owns its dictionary and every visual root attached below it.
|
||||||
|
- A dictionary entry retains the latest snapshot only while the entity is presented;
|
||||||
|
diagnostics expose no Node, Resource or snapshot reference.
|
||||||
|
- Scene load, instantiation, transform mutation, replacement and removal are main-thread only.
|
||||||
|
- Successful replacement instantiates first, then detaches the previous root, so an
|
||||||
|
invalid/missing new asset cannot erase the last valid presentation.
|
||||||
|
- Parent SceneTree teardown owns attached children; `_exit_tree` drops the index so
|
||||||
|
no stale lookup survives world unload.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Invalid identity/type | Snapshot/service validation | Reject without mutation | `entity_presentation_invalid_entity_id` | Supply valid session `EntityId` |
|
||||||
|
| Non-finite position/yaw | Snapshot validation | Reject without mutation | Stable position/yaw failure code | Repair adapter read model |
|
||||||
|
| Empty path or invalid scale | Snapshot validation | Reject without mutation | Stable path/scale failure code | Supply valid visual descriptor |
|
||||||
|
| Missing/non-PackedScene asset | Resource existence/load check | New visual absent; old replacement retained | Named presenter warning | Restore asset mapping and resend snapshot |
|
||||||
|
| Non-Node3D scene root | Instance type check | Free invalid instance; retain old | Named presenter warning | Fix visual scene root |
|
||||||
|
| Unknown remove | Dictionary lookup | No mutation; return `false` | Caller command result | Reconcile registry/presentation lifecycle |
|
||||||
|
| World teardown | Scene lifecycle | SceneTree frees children; index clears | Shutdown regression | Recreate service with next world |
|
||||||
|
|
||||||
|
There is no background job to cancel. Synchronous asset load is an explicit
|
||||||
|
prototype limitation and must be replaced/budgeted before network-scale use.
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Contract version | `1` | All | No | Identity/path/position/yaw/scale/visibility full snapshot |
|
||||||
|
| Present semantics | Idempotent by `EntityId` | Runtime/tools | No | Duplicate snapshots do not duplicate roots |
|
||||||
|
| Resource loading | Synchronous on create/replace | Prototype only | No | Not accepted as frame-critical production path |
|
||||||
|
| Local player | Separate composition | Current sandbox | No | Prevents facade service from owning gameplay player root |
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
- No snapshot, entity key or resource instance is persisted.
|
||||||
|
- `EntityId` remains session-local and must never enter content/cache schemas.
|
||||||
|
- Resource paths are current renderer adapter inputs, not stable content identity.
|
||||||
|
- Contract version 1 is additive. Changing identity lifetime, coordinate basis or
|
||||||
|
persisted representation requires a migration and ADR.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
- `entity_presentation_snapshot()` reports contract version, entity count and a
|
||||||
|
sorted list of `EntityId.to_debug_key`, visual path and visibility.
|
||||||
|
- The snapshot is deep-detached by the facade and contains no Node/Resource/RID.
|
||||||
|
- Load/type failures include the visual path but never proprietary bytes.
|
||||||
|
- Future performance telemetry should measure create/replace duration and active
|
||||||
|
entity/material/RID counts before introducing pooling or batching.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `src/tools/verify_world_entity_presentation.gd` covers eight value-contract and
|
||||||
|
fourteen lifecycle/ownership cases with repository-owned asset-free scenes.
|
||||||
|
- `src/tools/verify_world_render_facade.gd` covers command delegation, detached
|
||||||
|
diagnostics and both runtime scene service paths.
|
||||||
|
- Identity, coordinate, renderer-internal-access, shutdown, manifest, planner,
|
||||||
|
scheduler and documentation gates remain required.
|
||||||
|
- Fidelity boundary: runtime emits no entity commands yet, so existing seven
|
||||||
|
checkpoint scenes remain unchanged. No build-12340 display/animation parity is claimed.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
- AssetRepository/display-ID adapter that produces validated visual descriptors.
|
||||||
|
- Async/budgeted load and main-thread finalize behind the same facade commands.
|
||||||
|
- Animation/equipment/effect snapshots after build-12340 fixtures define behavior.
|
||||||
|
- Spatial batching/MultiMesh only after profiling proves per-Node overhead.
|
||||||
|
- Entity registry adapter that maps `WowGuid` to session `EntityId` outside render.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Typed entity visual input | Implemented contract | Versioned immutable validation fixtures | Asset/display mapping not implemented |
|
||||||
|
| Present/update/remove lifecycle | Implemented boundary | Asset-free create/update/replace/remove tests | Network-scale async/budget path missing |
|
||||||
|
| Deterministic ownership | Implemented | One presenter-owned root per EntityId; teardown checks | RID/material budgets remain future work |
|
||||||
|
| WoW entity visual fidelity | Planned | No claim | Requires build-12340 display/equipment/animation fixtures |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- Synchronous PackedScene load may hitch and is not a production hot-path design.
|
||||||
|
- Resource path is renderer adapter data, not stable authored content identity.
|
||||||
|
- Full snapshots currently cover transform/visibility only, not animation/equipment/effects.
|
||||||
|
- One Node3D per entity is unprofiled at world scale; batching is deliberately deferred.
|
||||||
|
- The local player remains separately composed until gameplay/network architecture is ready.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/entities/entity_presentation_snapshot.gd` | Immutable versioned entity visual input and validation |
|
||||||
|
| `src/render/entities/world_entity_presenter.gd` | Main-thread entity visual ownership and lifecycle |
|
||||||
|
| `src/render/world_render_facade.gd` | Stable public present/remove/diagnostic commands |
|
||||||
|
| `src/tools/verify_world_entity_presentation.gd` | Snapshot and scene lifecycle regression |
|
||||||
|
| `src/tests/scenes/character_presentation_model_fixture.tscn` | Primary asset-free visual fixture |
|
||||||
|
| `src/tests/scenes/world_entity_visual_replacement_fixture.tscn` | Replacement asset-free visual fixture |
|
||||||
|
| `src/scenes/streaming/*_streaming.tscn` | Runtime facade/service composition |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
- [`domain-identities.md`](domain-identities.md)
|
||||||
|
- [`coordinate-mapping.md`](coordinate-mapping.md)
|
||||||
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||||
|
- [`../ARCHITECTURE.md`](../ARCHITECTURE.md)
|
||||||
|
- [`../GODOT_BEST_PRACTICES.md`](../GODOT_BEST_PRACTICES.md)
|
||||||
+365
-33
@@ -5,9 +5,9 @@
|
|||||||
| Field | Value |
|
| Field | Value |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Status | Partial |
|
| 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 facade/planner/scheduler/internal-access/ground/environment/entity packages; M03 terrain packages; M03 M2 packages; M03 WMO placement package |
|
||||||
| Owners | Renderer workstream / milestone integrator |
|
| Owners | Renderer workstream / milestone integrator |
|
||||||
| Last verified | M00 visual-diff worktree, 2026-07-11 |
|
| Last verified | Worktree `work/sindo-main-codex/m03-adt-water-load-pipeline`, 2026-07-17 |
|
||||||
| Profiles/capabilities | `Performance`, `Balanced`, `High`, `Custom`; Blizzlike fidelity incomplete |
|
| Profiles/capabilities | `Performance`, `Balanced`, `High`, `Custom`; Blizzlike fidelity incomplete |
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
@@ -26,15 +26,55 @@
|
|||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
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]
|
Assets[Extracted WDT/ADT/M2/WMO/BLP] --> Native[Native loaders]
|
||||||
Cache[Baked terrain/M2/WMO caches] --> Loader
|
Cache[Baked terrain/M2/WMO caches] --> Loader
|
||||||
|
Loader --> TerrainCache[TerrainQualityMeshCache]
|
||||||
|
TerrainCache --> Loader
|
||||||
|
Loader --> ChunkLod[TerrainChunkLodPlanner]
|
||||||
|
ChunkLod --> Loader
|
||||||
|
Loader --> ChunkQueue[TerrainChunkGeometryQueuePlanner]
|
||||||
|
ChunkQueue --> Loader
|
||||||
|
Loader --> M2Registry[M2UniquePlacementRegistry]
|
||||||
|
M2Registry --> Loader
|
||||||
|
Loader --> M2Grouper[M2PlacementGrouper]
|
||||||
|
M2Grouper --> M2Transform[M2PlacementTransformResolver]
|
||||||
|
M2Transform --> M2Grouper
|
||||||
|
M2Transform --> Loader
|
||||||
|
Loader --> M2Batch[M2BuildBatchPlanner]
|
||||||
|
M2Batch --> Loader
|
||||||
|
Loader --> WmoPlacement[WmoPlacementResolver]
|
||||||
|
WmoPlacement --> Loader
|
||||||
|
Loader --> WmoRegistry[WmoPlacementRegistry]
|
||||||
|
WmoRegistry --> Loader
|
||||||
|
Loader --> WmoBuildStep[WmoRenderBuildStepPlanner]
|
||||||
|
WmoBuildStep --> Loader
|
||||||
|
Loader --> WmoBuildQueue[WmoRenderBuildQueue]
|
||||||
|
WmoBuildQueue --> Loader
|
||||||
Native --> Parsed[Parsed tile/model data]
|
Native --> Parsed[Parsed tile/model data]
|
||||||
Parsed --> Loader
|
Parsed --> Loader
|
||||||
Loader --> Scene[SceneTree nodes]
|
Loader --> Scene[SceneTree nodes]
|
||||||
Loader --> RS[RenderingServer RIDs/MultiMesh]
|
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
|
SkyController --> Scene
|
||||||
|
EntityState[Entity visual read model] --> EntitySnapshot[EntityPresentationSnapshot]
|
||||||
|
EntitySnapshot --> Facade
|
||||||
|
Facade --> EntityPresenter[WorldEntityPresenter]
|
||||||
|
EntityPresenter --> Scene
|
||||||
```
|
```
|
||||||
|
|
||||||
Allowed dependencies:
|
Allowed dependencies:
|
||||||
@@ -54,29 +94,95 @@ Forbidden dependencies:
|
|||||||
|
|
||||||
## Public API
|
## Public API
|
||||||
|
|
||||||
Текущая система ещё не имеет стабильного facade. Фактический integration surface — `StreamingWorldLoader` Node3D и exported properties. M03 должен заменить этот surface на `WorldRenderFacade`.
|
Runtime, capture and probe callers use `WorldRenderFacade` for focus, immutable
|
||||||
|
environment/entity presentation input, metrics and rendered-ground diagnostics.
|
||||||
|
`StreamingWorldLoader`, `WowSkyController` and `WorldEntityPresenter` remain
|
||||||
|
internal scene implementations.
|
||||||
|
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 |
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| `map_name` | Exported property | WDT/map directory name | Set before world load | Missing WDT produces diagnostic/empty world |
|
| `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 |
|
| `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 |
|
||||||
|
| `EntityPresentationSnapshot.create` | Immutable value factory | Validates contract-v1 identity, typed position, visual path, yaw, scale and visibility | Any thread; caller-owned | Stable invalid code; never mutates scene |
|
||||||
|
| `WorldRenderFacade.present_entity` | Public command | Idempotently creates/updates/replaces one session entity visual | Main thread/session | Returns false for invalid input, missing service or visual load/type failure |
|
||||||
|
| `WorldRenderFacade.remove_entity` | Public command | Removes one known session entity visual | Main thread/session | Invalid/unknown identity returns false |
|
||||||
|
| `WorldRenderFacade.entity_presentation_snapshot` | Public diagnostic query | Returns detached scalar entity inventory | Main thread/caller-owned copy | Missing/invalid service returns empty dictionary with diagnostic |
|
||||||
|
| `WorldRenderFacade.world_entity_presenter_path` | Internal adapter property | Resolves renderer-owned entity visual service | Main thread/scene lifetime | Missing methods/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 |
|
| `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 |
|
| `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 |
|
| `hitch_profiler_enabled` | Exported property | Enables named hitch sections | Runtime mutable | Logging overhead only |
|
||||||
|
| `TerrainQualityMeshCache.store/restore/clear` | Internal terrain service | Owns admitted full-quality revisit Mesh references and LRU | Renderer thread/map session | Rejected/missed entry leaves loader fallback unchanged |
|
||||||
|
| `TerrainChunkLodPlanner.plan` | Internal pure terrain query | Maps populated parsed chunk indices to desired LOD 0/1/2 | Synchronous per target refresh | Invalid/disabled input returns empty for existing tile-LOD fallback |
|
||||||
|
| `TerrainChunkGeometryQueuePlanner.plan` | Internal pure terrain query | Produces chunk create/remove requests from current and desired state | Synchronous per queue rebuild | Invalid focus returns empty arrays |
|
||||||
|
| `M2UniquePlacementRegistry.reserve/release/clear` | Internal M2 service | Owns positive cross-tile ADT placement-ID reservations | Renderer thread/map session | Invalid/unkeyed/non-owner inputs preserve documented fallback |
|
||||||
|
| `M2PlacementTransformResolver.resolve_basis/resolve_origin_offset` | Internal pure M2 service | Resolves regular and calibrated model-specific ADT placement transforms | Worker/main thread; stateless | Unknown paths use regular basis and zero offset |
|
||||||
|
| `M2PlacementGrouper.group_placements` | Internal pure M2 service | Validates and groups ordered tile-local placement transforms by normalized path | Worker/main thread; stateless | Invalid variants/name IDs/empty paths are skipped |
|
||||||
|
| `M2BuildBatchPlanner.plan_batch` | Internal pure M2 service | Selects static/animated batch count and next group cursor | Main/any thread; stateless | Non-positive selected limit clamps to one; empty range completes |
|
||||||
|
| `WmoPlacementResolver.normalize_relative_path/resolve_unique_key/resolve_world_transform` | Internal pure WMO service | Resolves cache key, registry identity and world transform | Main/any thread; stateless | Missing UID uses tile/index fallback; transform fields use historical defaults |
|
||||||
|
| `WmoPlacementRegistry.add_reference/release_reference/contains/active_count/diagnostic_snapshot/clear` | Internal WMO service | Owns placement-key to tile/global reference sets | Renderer main thread; map session | Empty/unknown/non-owner input is rejected without mutation |
|
||||||
|
| `WmoRenderBuildStepPlanner.plan_step` | Internal pure WMO service | Selects one mesh-first lightweight render-group operation and next cursors | Main/any thread; stateless | Raw integer comparisons are preserved without clamping |
|
||||||
|
| `WmoRenderBuildQueue` / `WmoRenderBuildJob` | Internal WMO pending-state service | Owns typed root/resource/cursor jobs and FIFO placement keys | Renderer main thread; map session | Invalid enqueue rejected; duplicate/stale behavior preserved |
|
||||||
|
| `WmoRenderResourceCacheState` | Internal WMO cache-state service | Owns validated Resources, negative entries and pending cache paths | Renderer main thread; map/cache session | Invalid/occupied request and unknown completion are rejected |
|
||||||
|
| `WmoSceneResourceCacheState` | Internal WMO cache-state service | Owns validated PackedScenes, negative entries and pending `.tscn` paths | Renderer main thread; map/cache session | Direct missing and terminal request transitions remain distinct |
|
||||||
|
| `AdtWaterLoadPipelineState` | Internal liquid async-state service | Owns ADT water FIFO/dedupe, active task IDs and mutex result mailbox | Main-thread state; worker result publication | Invalid/duplicate requests rejected; clear does not interrupt workers |
|
||||||
|
|
||||||
Публичным 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
|
## Inputs and outputs
|
||||||
|
|
||||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
| 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 | `EntityPresentationSnapshot` | Future world-state/preview adapter | `WorldRenderFacade` / entity presenter | Immutable caller-held full visual snapshot | One main-thread present/update |
|
||||||
|
| Input | `EntityId` removal command | Future entity registry/adapter | `WorldRenderFacade` / entity presenter | Immutable session-local value | One main-thread removal |
|
||||||
|
| 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 | 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 |
|
| 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 |
|
||||||
|
| Internal cache | Full-quality terrain Mesh/source | Loader terrain upgrade / `TerrainQualityMeshCache` | Revisited tile state | Cache-retained Mesh reference | Map session until eviction/reset |
|
||||||
|
| Internal plan | Parsed chunks, typed focus and immutable chunk-LOD policy | Loader / `TerrainChunkLodPlanner` | Loader desired state | Fresh dictionary; no retained resources | One target refresh |
|
||||||
|
| Internal plan | Current/desired chunk state and typed focus | Loader / `TerrainChunkGeometryQueuePlanner` | Loader chunk queues | Fresh request arrays; no retained resources | One queue rebuild |
|
||||||
|
| Internal registry | Tile key and M2 placement unique IDs | Loader / `M2UniquePlacementRegistry` | Filtered grouping input and tile retry state | Registry-owned strings; fresh result arrays | Map session |
|
||||||
|
| Internal transform | Rotation/path/scale | Loader or grouper / `M2PlacementTransformResolver` | Group/placeholder/instance transforms | Value-only Basis/Vector3 | One placement |
|
||||||
|
| Internal grouping | Tile origin, M2 names and placements | Loader / `M2PlacementGrouper` | Loader worker result/build job | Fresh Dictionary/Transform3D arrays | One grouping task |
|
||||||
|
| Internal batch plan | Transform count/offset, path kind and limits | Loader / `M2BuildBatchPlanner` | Loader materialization/cursor adapter | Fresh scalar Dictionary | One build operation |
|
||||||
|
| Internal WMO placement | Path, MODF placement, tile/index | Loader / `WmoPlacementResolver` | WMO caches, registry and three instance adapters | Value-only String/Transform3D | Lookup/placement lifetime |
|
||||||
|
| Internal WMO ownership | Resolved placement key and tile/global reference key | Loader / `WmoPlacementRegistry` | Loader create/retain/final-free decisions | Registry-owned String sets; detached diagnostics | Map session or final release |
|
||||||
|
| Internal WMO build step | Mesh/MultiMesh counts and job cursors | Loader / `WmoRenderBuildStepPlanner` | Loader materialization/cursor adapter | Fresh scalar Dictionary | One group operation |
|
||||||
|
| Internal WMO pending build | Placement key, Node3D root, WMO Resource and cursors | Loader / `WmoRenderBuildQueue` | Loader drain and step planner adapter | Queue-owned job and strong references | Until cancel/clear/replacement |
|
||||||
|
| Internal WMO render cache | Normalized path, cache path and validated Resource | Loader / `WmoRenderResourceCacheState` | Loader lookup, ResourceLoader poll and build queue | State-owned Resource/path references; detached request snapshots | Until transient/full clear |
|
||||||
|
| Internal WMO scene cache | Normalized path, `.tscn` path and validated PackedScene | Loader / `WmoSceneResourceCacheState` | Loader lookup, request poll and scene instantiation | State-owned PackedScene/path references; detached request snapshots | Until transient/full clear |
|
||||||
|
| Internal ADT water load | Tile key, ADT path, task ID and parsed Dictionary | Loader/worker / `AdtWaterLoadPipelineState` | Loader task start, budgeted drain and finalization | State-owned records; mutex result mailbox | Request through result completion/reset |
|
||||||
|
| 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 | 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 | Entity visual Node3D subtree | `WorldEntityPresenter` | SceneTree/viewport | Presenter-owned child | Until replace/remove/teardown |
|
||||||
|
| Output | Detached entity inventory | `WorldRenderFacade` | Diagnostics/tests | 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:
|
Side effects:
|
||||||
|
|
||||||
@@ -90,22 +196,61 @@ Side effects:
|
|||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
F[Streaming focus] --> T[Compute desired ADT tiles]
|
PlayerSource[Player/spectator Node3D] --> A[Explicit scene adapter]
|
||||||
WDT[WDT tile catalog] --> T
|
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]
|
T --> Q[Tile load queue]
|
||||||
Q --> P[Worker parse/cache load]
|
Q --> Parse[Worker parse/cache load]
|
||||||
P --> R[Result queues]
|
Parse --> R[Result queues]
|
||||||
R --> B[Per-frame budget scheduler]
|
R --> TerrainCache[TerrainQualityMeshCache store]
|
||||||
|
TerrainCache --> Revisit[Revisited tile state restore]
|
||||||
|
R --> ChunkLod[TerrainChunkLodPlanner]
|
||||||
|
ChunkLod --> DesiredChunkLod[Desired chunk LOD state]
|
||||||
|
DesiredChunkLod --> ChunkQueue[TerrainChunkGeometryQueuePlanner]
|
||||||
|
ChunkQueue --> B
|
||||||
|
R --> M2Registry[M2UniquePlacementRegistry reserve]
|
||||||
|
M2Registry --> M2Grouper[M2PlacementGrouper]
|
||||||
|
M2Transform[M2PlacementTransformResolver] --> M2Grouper
|
||||||
|
M2Grouper --> M2Batch[M2BuildBatchPlanner]
|
||||||
|
M2Batch --> M2
|
||||||
|
R --> WmoPlacement[WmoPlacementResolver]
|
||||||
|
WmoPlacement --> WmoRegistry[WmoPlacementRegistry]
|
||||||
|
WmoRegistry --> WmoBuildQueue[WmoRenderBuildQueue]
|
||||||
|
WmoBuildQueue --> WmoBuildStep[WmoRenderBuildStepPlanner]
|
||||||
|
WmoBuildStep --> WMO
|
||||||
|
R --> B[RenderBudgetScheduler permits]
|
||||||
B --> Terrain[Terrain attach/upgrade]
|
B --> Terrain[Terrain attach/upgrade]
|
||||||
B --> M2[M2 group/MultiMesh attach]
|
B --> M2[M2 group/MultiMesh attach]
|
||||||
B --> WMO[WMO instance/group attach]
|
B --> WMO[WMO instance/group attach]
|
||||||
B --> Liquid[Liquid 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
|
||||||
|
EntityReadModel[Entity visual read model] --> EntitySnapshot[EntityPresentationSnapshot]
|
||||||
|
EntitySnapshot --> RF
|
||||||
|
RF --> EntityPresenter[WorldEntityPresenter]
|
||||||
|
EntityAsset[PackedScene path] --> EntityPresenter
|
||||||
|
EntityPresenter --> World
|
||||||
Terrain --> World[Godot world]
|
Terrain --> World[Godot world]
|
||||||
M2 --> World
|
M2 --> World
|
||||||
WMO --> World
|
WMO --> World
|
||||||
Liquid --> World
|
Liquid --> World
|
||||||
F --> E[Eviction/retention decisions]
|
Marker --> World
|
||||||
E --> World
|
F --> Eviction[Eviction/retention decisions]
|
||||||
|
Eviction --> World
|
||||||
```
|
```
|
||||||
|
|
||||||
## Lifecycle/state
|
## Lifecycle/state
|
||||||
@@ -113,13 +258,14 @@ flowchart TD
|
|||||||
```mermaid
|
```mermaid
|
||||||
stateDiagram-v2
|
stateDiagram-v2
|
||||||
[*] --> Initializing
|
[*] --> Initializing
|
||||||
Initializing --> Ready: WDT/catalog/config ready
|
Initializing --> WaitingForFocus: WDT/catalog/config ready
|
||||||
Initializing --> Degraded: missing loaders/data/cache
|
Initializing --> Degraded: missing loaders/data/cache
|
||||||
Ready --> Streaming: focus update
|
WaitingForFocus --> Streaming: valid StreamingFocus
|
||||||
Streaming --> Streaming: load/finalize/evict ticks
|
Streaming --> Streaming: load/finalize/evict ticks
|
||||||
|
Streaming --> WaitingForFocus: world reset without replacement focus
|
||||||
Streaming --> SwitchingMap: map/focus reset
|
Streaming --> SwitchingMap: map/focus reset
|
||||||
SwitchingMap --> Initializing
|
SwitchingMap --> Initializing
|
||||||
Ready --> ShuttingDown
|
WaitingForFocus --> ShuttingDown
|
||||||
Degraded --> ShuttingDown
|
Degraded --> ShuttingDown
|
||||||
Streaming --> ShuttingDown
|
Streaming --> ShuttingDown
|
||||||
ShuttingDown --> [*]
|
ShuttingDown --> [*]
|
||||||
@@ -129,18 +275,39 @@ stateDiagram-v2
|
|||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
participant Focus as Camera/Player/Editor
|
participant Source as Player/Editor/Capture source
|
||||||
participant Stream as StreamingWorldLoader
|
participant Focus as StreamingFocus adapter
|
||||||
|
participant Facade as WorldRenderFacade
|
||||||
|
participant Stream as StreamingWorldLoader internal
|
||||||
|
participant Planner as StreamingTargetPlanner
|
||||||
participant Worker as Worker task
|
participant Worker as Worker task
|
||||||
participant Budget as Main-thread budget
|
participant Budget as RenderBudgetScheduler
|
||||||
participant Render as SceneTree/RenderingServer
|
participant Render as SceneTree/RenderingServer
|
||||||
Focus->>Stream: focus position changed
|
Source->>Focus: sample explicit Godot position
|
||||||
Stream->>Stream: desired tiles and priorities
|
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
|
Stream->>Worker: parse/load tile and detail data
|
||||||
Worker-->>Stream: immutable result
|
Worker-->>Stream: immutable result
|
||||||
Stream->>Budget: enqueue finalize operations
|
Stream->>Budget: begin_frame(operation limits)
|
||||||
Budget->>Render: attach bounded terrain/M2/WMO/liquid work
|
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
|
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
|
||||||
|
Source->>Facade: present_entity(full visual snapshot)
|
||||||
|
Facade->>Render: delegate to WorldEntityPresenter
|
||||||
|
Render->>Render: create/update/replace owned entity root
|
||||||
|
Source->>Facade: remove_entity(EntityId)
|
||||||
|
Facade->>Render: detach and queue_free owned root
|
||||||
|
Stream->>Budget: shutdown: cancel permit issuance
|
||||||
Stream->>Worker: shutdown: wait for WorkerThreadPool tasks
|
Stream->>Worker: shutdown: wait for WorkerThreadPool tasks
|
||||||
Stream->>Stream: shutdown: finish registered ResourceLoader requests
|
Stream->>Stream: shutdown: finish registered ResourceLoader requests
|
||||||
Stream->>Render: clear queues and owned tree nodes/RIDs
|
Stream->>Render: clear queues and owned tree nodes/RIDs
|
||||||
@@ -150,6 +317,61 @@ sequenceDiagram
|
|||||||
## Ownership, threading and resources
|
## Ownership, threading and resources
|
||||||
|
|
||||||
- `StreamingWorldLoader` владеет tile states, queues, task registries, active world instances и render caches session scope.
|
- `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.
|
||||||
|
- `WorldEntityPresenter` owns one optional visual Node3D subtree per presented
|
||||||
|
`EntityId`; it owns no authoritative entity state and excludes the local player.
|
||||||
|
- Entity presentation snapshots remain caller-owned inputs. Detached diagnostics
|
||||||
|
expose only identity debug keys, paths and visibility, never Node/Resource/RID.
|
||||||
|
- `TerrainQualityMeshCache` owns only full-quality revisit Mesh references and
|
||||||
|
LRU keys. The loader retains terrain tasks/results, tile state, source choice,
|
||||||
|
cache versions and every material/Node/RID finalization side effect.
|
||||||
|
- `TerrainChunkLodPlanner` is stateless and owns only call-local horizontal
|
||||||
|
distance calculations. The loader retains parsed chunks, desired state,
|
||||||
|
queues, budgets and all Mesh/Node/RID application side effects.
|
||||||
|
- `TerrainChunkGeometryQueuePlanner` is stateless and owns only call-local
|
||||||
|
create/remove request calculation and sorting. The loader adopts the fresh
|
||||||
|
arrays and retains removal-first drains, budgets and render side effects.
|
||||||
|
- `M2UniquePlacementRegistry` owns only positive unique-key to tile-key strings.
|
||||||
|
The loader retains owned/skipped tile arrays, candidate retry, grouping/build
|
||||||
|
tasks, caches and all MultiMesh/Node/Mesh/material/RID side effects.
|
||||||
|
- `M2PlacementTransformResolver` is stateless and retains no resources. The
|
||||||
|
grouper owns worker-path final transforms; direct placeholder/instance
|
||||||
|
transforms and every build/render side effect remain loader-owned.
|
||||||
|
- `M2PlacementGrouper` is stateless and owns only call-local grouped transforms.
|
||||||
|
The loader retains tasks, mutex/result queues, stale checks and build state.
|
||||||
|
- `M2BuildBatchPlanner` is stateless and owns only call-local scalar plans. The
|
||||||
|
loader retains queue/resource transitions, cursor adoption and materialization.
|
||||||
|
- `WmoPlacementResolver` is stateless and owns only call-local cache-key,
|
||||||
|
identity and transform values. `WmoPlacementRegistry` owns only placement-key
|
||||||
|
reference sets. `WmoRenderBuildStepPlanner` owns only a call-local operation
|
||||||
|
and cursor plan. `WmoRenderBuildQueue` owns typed pending jobs, FIFO keys and
|
||||||
|
strong root/resource references without freeing engine objects.
|
||||||
|
`WmoRenderResourceCacheState` owns validated render Resources, negative entries
|
||||||
|
and pending cache paths. `WmoSceneResourceCacheState` similarly owns validated
|
||||||
|
PackedScenes, negative entries and pending `.tscn` paths. The loader retains
|
||||||
|
ResourceLoader/FileAccess I/O, size and cache-version validation, live fallback,
|
||||||
|
materialization, permits, validity reactions and every Node lifecycle action.
|
||||||
|
- `AdtWaterLoadPipelineState` owns pending request order/deduplication, opaque
|
||||||
|
active task IDs and the worker-safe parsed-result mailbox. The loader retains
|
||||||
|
WorkerThreadPool start/wait, ADTLoader parsing, concurrency/finalize permits,
|
||||||
|
stale tile/path/root checks and ADTBuilder/Node finalization.
|
||||||
|
- 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.
|
- Worker tasks не должны менять SceneTree и shared Resource concurrently.
|
||||||
- Parsed/grouped results передаются обратно через guarded result queues.
|
- Parsed/grouped results передаются обратно через guarded result queues.
|
||||||
- Mesh/material/node/RID finalization выполняется main thread и ограничивается exported budgets.
|
- Mesh/material/node/RID finalization выполняется main thread и ограничивается exported budgets.
|
||||||
@@ -163,9 +385,19 @@ sequenceDiagram
|
|||||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|---|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| Missing WDT/ADT | File/catalog check | Tile/map unavailable | Loader warning/error | Fix extraction/profile, reload |
|
| 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 |
|
||||||
|
| Invalid entity visual snapshot | Contract validation | Reject without scene mutation | Stable `entity_presentation_*` failure code | Repair adapter read model and resend |
|
||||||
|
| Missing/invalid entity visual asset | Resource existence/load/root check | New entity absent or prior replacement retained | Named presenter warning | Restore asset mapping and resend snapshot |
|
||||||
|
| Missing entity presenter | Facade path/method validation | Reject entity commands; other facade APIs remain available | One facade diagnostic until recovery | Fix runtime scene composition |
|
||||||
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 + cache shutdown verifier | Drain requests, preserve tree ownership, then free detached prototypes and clear resource caches |
|
| 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 |
|
||||||
@@ -184,6 +416,9 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
|||||||
|
|
||||||
## Persistence, cache and migration
|
## 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.
|
- Renderer reads caches under `data/cache`; caches are not committed.
|
||||||
- Format changes require `FORMAT_VERSION`/required-version bump and rebuild instructions.
|
- Format changes require `FORMAT_VERSION`/required-version bump and rebuild instructions.
|
||||||
- Atomic cache publication and unified manifest are target-state work from FND/M03.
|
- Atomic cache publication and unified manifest are target-state work from FND/M03.
|
||||||
@@ -192,14 +427,61 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
|||||||
## Diagnostics and observability
|
## Diagnostics and observability
|
||||||
|
|
||||||
- Logs: `HITCH`, `PERF`, `TERRAIN_QUALITY`, `SKY_LIGHT`, `SKYBOX_MODEL`.
|
- 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.
|
- 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.
|
- Debug views: render checkpoint scenes/captures; future streaming/LOD/portal overlays.
|
||||||
- Correlation IDs: currently mostly tile/path keys; target architecture adds session/job IDs.
|
- Correlation IDs: currently mostly tile/path keys; target architecture adds session/job IDs.
|
||||||
|
|
||||||
## Verification
|
## 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.
|
- 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.
|
||||||
- Integration/E2E: Eastern Kingdoms/Kalimdor streaming scenes and seven cold/warm checkpoints.
|
- Pure planner contract: center/corner/catalog/clamp/editor cases plus bounded
|
||||||
|
High-like iteration timing without loading a world scene.
|
||||||
|
- Terrain chunk LOD contract: horizontal chunk-center distance, inclusive
|
||||||
|
thresholds, sparse indices, disabled/raw-custom policy, loader delegation and
|
||||||
|
bounded 250-by-256 planning without loading a world scene.
|
||||||
|
- Terrain chunk geometry queue contract: current/desired comparison, tile-LOD
|
||||||
|
takeover, sparse origin fallback, stable removals, nearest creates, loader
|
||||||
|
drain boundary and bounded multi-tile timing without a world scene.
|
||||||
|
- M2 unique placement contract: unkeyed pass-through, within-call/cross-tile
|
||||||
|
dedupe, owner release/retry, clear, detached diagnostics, loader boundary and
|
||||||
|
bounded reserve/release timing without a world scene.
|
||||||
|
- M2 placement transform contract: regular/cliffrock/waterfall formulas, path
|
||||||
|
normalization, scale compensation, all three consumers and bounded timing.
|
||||||
|
- M2 placement grouper contract: invalid/default records, historical path rules,
|
||||||
|
group order, local transforms, fresh output, source boundary and bounded timing.
|
||||||
|
- M2 build batch contract: static/animated limit selection, clamp, remaining
|
||||||
|
count, completion/cursor rules, source boundary and bounded timing.
|
||||||
|
- WMO placement contract: cache-path normalization, positive/fallback identity,
|
||||||
|
default/rotated/unclamped transforms, three consumers and bounded timing.
|
||||||
|
- WMO placement registry contract: first/idempotent/shared/non-owner/final/global
|
||||||
|
lifecycle, detached sorted diagnostics, source ownership and bounded timing.
|
||||||
|
- WMO render build step contract: mesh-before-MultiMesh order, one-index cursor
|
||||||
|
transition, completion/raw integer behavior, source ownership and bounded timing.
|
||||||
|
- WMO render build queue contract: typed references/cursors, FIFO, duplicate
|
||||||
|
replacement, stale-front cleanup, cancel/clear engine lifetime and bounded timing.
|
||||||
|
- WMO render Resource cache contract: invalid/duplicate request rejection,
|
||||||
|
validated/missing terminal transitions, transient/full reset, detached sorted
|
||||||
|
diagnostics, loader-owned version validation and bounded timing.
|
||||||
|
- WMO scene Resource cache contract: direct missing sources, pending terminal
|
||||||
|
transitions, transient/full reset, detached diagnostics, loader-owned size/
|
||||||
|
metadata validation and bounded timing.
|
||||||
|
- ADT water load pipeline contract: FIFO/dedupe, active task identity, real
|
||||||
|
worker-thread publication, pending-only cancellation, result payload/order,
|
||||||
|
clear/source boundaries and bounded timing.
|
||||||
|
- 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.
|
- 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.
|
- 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).
|
- Manual diagnostics: [`../RENDER_BASELINE.md`](../RENDER_BASELINE.md) and [`../../RENDER.md`](../../RENDER.md).
|
||||||
@@ -207,7 +489,7 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
|||||||
## Extension points
|
## Extension points
|
||||||
|
|
||||||
- Target `WorldRenderFacade` for runtime/editor/capture callers.
|
- 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.
|
- Extracted planner, budget scheduler, terrain, M2, WMO, liquid, sky and character services.
|
||||||
- Separate Blizzlike/Enhanced material and quality profiles.
|
- Separate Blizzlike/Enhanced material and quality profiles.
|
||||||
|
|
||||||
@@ -217,16 +499,39 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
|||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| ADT streaming/terrain | Partial | M00 checkpoints and current scenes | Fidelity/performance gaps remain |
|
| ADT streaming/terrain | Partial | M00 checkpoints and current scenes | Fidelity/performance gaps remain |
|
||||||
| Static M2 placement | Partial | MultiMesh/dedupe probes | Full materials/animation/effects |
|
| Static M2 placement | Partial | MultiMesh/dedupe probes | Full materials/animation/effects |
|
||||||
|
| M2 unique placement registry | Implemented extraction | Scene-free ownership/lifecycle/timing contract and historical `uid:11785` smoke | Group/build/tasks/finalization and asset-backed p95/p99 remain pending |
|
||||||
|
| M2 placement transform resolver | Implemented extraction | Scene-free formula/source/timing contract across three consumers | Asset-backed visual recheck and general placement parity pending |
|
||||||
|
| M2 placement grouper | Implemented extraction | Scene-free validation/order/transform/source/timing contract | Worker/build state, spatial cells and asset-backed p95/p99 remain pending |
|
||||||
|
| M2 build batch planner | Implemented extraction | Scene-free limit/count/cursor/source/timing contract | Queue/resource state, spatial cells and asset-backed p95/p99 remain pending |
|
||||||
|
| WMO placement resolver | Implemented extraction | Scene-free path/identity/transform/source/timing contract | Asset-backed comparison pending |
|
||||||
|
| WMO placement registry | Implemented extraction | Scene-free ownership/lifecycle/source/timing contract | Build/resource state and asset-backed cross-tile corpus pending |
|
||||||
|
| WMO render build step planner | Implemented extraction | Scene-free order/cursor/source/timing contract | Asset-backed traversal p95/p99 pending |
|
||||||
|
| WMO render build queue | Implemented extraction | Typed lifecycle/order/ownership/source/timing contract | Materialization and asset-backed traversal/leak evidence pending |
|
||||||
|
| WMO render Resource cache state | Implemented extraction | Scene-free lifecycle/exclusivity/source/timing plus shutdown contract | ResourceLoader I/O and asset-backed traversal/leak evidence pending |
|
||||||
|
| WMO scene Resource cache state | Implemented extraction | Scene-free lifecycle/direct-missing/source/timing plus shutdown contract | ResourceLoader/live-fallback extraction and asset-backed traversal/leak evidence pending |
|
||||||
| WMO rendering | Partial | Cached group rendering | Portals/rooms/material parity |
|
| WMO rendering | Partial | Cached group rendering | Portals/rooms/material parity |
|
||||||
|
| ADT water load pipeline state | Implemented extraction | Scene-free FIFO/task/thread/source/timing contract | Parse/finalization and asset-backed traversal/leak evidence pending |
|
||||||
| Liquids | Partial | MH2O/MLIQ paths | LiquidType/depth/shore fidelity |
|
| Liquids | Partial | MH2O/MLIQ paths | LiquidType/depth/shore fidelity |
|
||||||
| Sky/light | Partial | DBC controller/logs | Zone transition paired validation |
|
| Sky/light | Partial | DBC controller/logs | Zone transition paired validation |
|
||||||
| Character presentation | Prototype | Character creator/player experiment | Full customization/equipment states |
|
| 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 | Implemented boundary / Partial fidelity | Facade contracts cover focus, environment, entity visuals, metrics and loaded-mesh ground query | Production consumers and fidelity 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 |
|
||||||
|
| Entity presentation commands | Implemented boundary / Prototype visuals | Versioned typed snapshot plus present/remove lifecycle and runtime service composition | Display/equipment/animation/effects and async scale path remain |
|
||||||
|
| Terrain quality Mesh cache service | Implemented extraction | Scene-free admission/LRU/clear contract and loader delegation | Remaining terrain build/state/finalization is monolithic |
|
||||||
|
| Terrain chunk LOD planner | Implemented extraction | Scene-free formula/dependency/timing contract and loader delegation | Queue/state/finalization and asset-backed p95/p99 remain pending |
|
||||||
|
| Terrain chunk geometry queue planner | Implemented extraction | Scene-free request/order/dependency/timing contract and loader delegation | Queue drains/state/finalization and asset-backed p95/p99 remain pending |
|
||||||
|
| 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
|
## Known gaps and risks
|
||||||
|
|
||||||
- Monolithic streamer mixes planning, jobs, caches and presentation.
|
- 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.
|
- Original-client paired fidelity evidence incomplete.
|
||||||
- Первый paired run выявил coordinate/placement mismatch: несколько совпадающих server-derived camera positions оказываются под terrain или внутри WMO/rocks OpenWC.
|
- Первый 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.
|
- Terrain-height probe исключил under-terrain состояние для всех пяти точек; waterfall exact-XZ miss классифицирован как TriangleMesh seam/edge и подтверждён nearby sample в 2 units.
|
||||||
@@ -241,12 +546,37 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
|||||||
- 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.
|
- 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.
|
- M2/WMO/material/particle/ribbon/portal parity incomplete.
|
||||||
- Public API is mostly exported configuration rather than stable contracts.
|
- 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
|
## Source map
|
||||||
|
|
||||||
| Path | Responsibility |
|
| 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/entities/entity_presentation_snapshot.gd` | Immutable contract-v1 entity visual input and validation |
|
||||||
|
| `src/render/entities/world_entity_presenter.gd` | Main-thread entity visual subtree ownership and lifecycle |
|
||||||
|
| `src/render/terrain/rendered_ground_sample.gd` | Immutable renderer-owned available/unavailable ground query result |
|
||||||
|
| `src/render/terrain/terrain_quality_mesh_cache.gd` | Full-quality terrain revisit Mesh retention and LRU ownership |
|
||||||
|
| `src/render/liquid/adt_water_load_pipeline_state.gd` | ADT water request/task/result bookkeeping and worker-safe mailbox |
|
||||||
|
| `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/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_world_entity_presentation.gd` | Entity snapshot, create/update/replace/remove and ownership regression |
|
||||||
|
| `src/tools/verify_rendered_ground_sample.gd` | Cold-start renderer ground result invariant regression |
|
||||||
|
| `src/tools/verify_terrain_quality_mesh_cache.gd` | Terrain cache admission, LRU, ownership and loader-boundary regression |
|
||||||
|
| `src/tools/verify_adt_water_load_pipeline_state.gd` | ADT water FIFO/task/thread/boundary/timing 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/adt_builder.gd` | Terrain/material/liquid construction |
|
||||||
| `addons/mpq_extractor/loaders/m2_builder.gd` | Static M2 mesh/material 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 |
|
| `addons/mpq_extractor/loaders/m2_native_animated_builder.gd` | Native animated M2 experiment |
|
||||||
@@ -258,9 +588,11 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
|||||||
| `src/tools/compare_render_checkpoints.gd` | Offline JPG/PNG paired-image perceptual metrics and JSON pass/fail report |
|
| `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/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/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 |
|
| `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/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 |
|
| `src/tools/probe_render_camera_occluders.gd` | Scene-tree placement containment and camera-to-target AABB intersection report |
|
||||||
|
|
||||||
## Related decisions and references
|
## Related decisions and references
|
||||||
|
|||||||
@@ -19,6 +19,93 @@ config/icon="res://icon.svg"
|
|||||||
|
|
||||||
enabled=PackedStringArray("res://addons/mpq_extractor/plugin.cfg")
|
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]
|
[physics]
|
||||||
|
|
||||||
3d/physics_engine="Jolt 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.
|
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.
|
- `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.
|
- `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.
|
- `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
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user