Compare commits
169 Commits
02dec23c2a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 20a64b5dfc | |||
| 1de50f98ce | |||
| 5e828aaf01 | |||
| cd664eb4cb | |||
| 87bf067493 | |||
| ca77d32be1 | |||
| 7cd07dda4f | |||
| 8605b43b1f | |||
| b608a81766 | |||
| e4bb10cee7 | |||
| 1d94eace68 | |||
| 1fafddecb6 | |||
| 0007a69769 | |||
| ece7724a28 | |||
| 951bd54fff | |||
| dd8cfbf79c | |||
| fa85985ec5 | |||
| f68e971cce | |||
| 7b0e1eac5f | |||
| 6607100686 | |||
| 5e2afbc400 | |||
| 30945615b9 | |||
| c5ecaba0ab | |||
| e990a6503d | |||
| e2bb501695 | |||
| ef782d1f2b | |||
| 83b3618c50 | |||
| ccbb27a9fd | |||
| 82df7a555d | |||
| 5f6287905c | |||
| 8ec13d6018 | |||
| 38f831ea2f | |||
| 0544cf22df | |||
| 19ca8f48e8 | |||
| 324e64dd6e | |||
| 90b1dac56e | |||
| 874fd0f3f2 | |||
| 9cb8bdb137 | |||
| 235be3a36c | |||
| 4f8fc3b3b8 | |||
| 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 |
@@ -25,7 +25,22 @@ 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/render/liquid/adt_water_scene_finalizer.gd` - stateless main-thread ADT water build/attach and optional persisted Editor ownership through the existing ADTBuilder.
|
||||
- `src/render/m2/m2_runtime_mesh_rebuild_classifier.gd` - memoized billboard/UV-rotation decision for stale cached M2 runtime mesh refresh.
|
||||
- `src/render/m2/m2_mesh_load_pipeline_state.gd` - static M2 threaded-load request records, terminal statuses and completion-order finalize FIFO without I/O or Mesh ownership.
|
||||
- `src/render/m2/m2_mesh_resource_cache_state.gd` - normalized-path prepared static M2 Mesh references with final-shutdown lifetime.
|
||||
- `src/render/m2/m2_mesh_resource_extractor.gd` - first-Mesh selection from direct/PackedScene/Node inputs with temporary instance cleanup.
|
||||
- `src/render/m2/m2_runtime_mesh_finalizer.gd` - refresh version, classifier lifetime, M2Builder rebuild and original-Mesh fallback.
|
||||
- `src/render/m2/m2_prototype_cache_state.gd` - shutdown-lifetime static/animated prototype Nodes and missing/static-only path outcomes.
|
||||
- `src/scenes/streaming/eastern_kingdoms_streaming.tscn` - текущая сцена для проверки Azeroth/Eastern Kingdoms.
|
||||
- `addons/mpq_extractor/loaders/adt_builder.gd` - сборка ADT terrain, control splat материалов и MH2O liquids.
|
||||
- `addons/mpq_extractor/loaders/m2_builder.gd` - сборка статического M2 mesh/material.
|
||||
@@ -393,6 +408,12 @@ Known limits:
|
||||
- `WMOBuilder` строит group meshes;
|
||||
- runtime registry дедуплицирует WMO по `unique_id`;
|
||||
- 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` во время движения;
|
||||
- добавлен lightweight render-cache path для WMO groups;
|
||||
- `wmo_render_group_ops_per_tick` ограничивает подключение WMO groups по кадрам;
|
||||
@@ -788,7 +809,12 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
- Billboard metadata is resolved per drawn vertex through the skin section bone palette and vertex bone indices. This avoids treating an entire mixed surface as billboard just because the palette contains a billboard bone.
|
||||
- `M2Builder` stores billboard pivot/mode in `Mesh.ARRAY_CUSTOM0` as RGBA float data. `CUSTOM0.xyz` is the Godot-space bone pivot; `CUSTOM0.w` is the billboard mode: spherical, lock X, lock Y, or lock Z.
|
||||
- `WowM2Material` applies camera-facing billboarding in the vertex shader for marked vertices. This works with grouped ADT M2 `MultiMesh` batches because the shader uses each instance transform.
|
||||
- Runtime M2 cache refresh now has `M2_MATERIAL_REFRESH_VERSION`. Old cached `.tscn/.glb` meshes are replaced in memory from raw `.m2` only when the raw model actually needs custom billboard attributes or UV-rotation data. Do not rebuild every stale cached M2 during streaming: creating fresh materials for ordinary props can exhaust the D3D12 resource descriptor heap.
|
||||
- Runtime M2 cache refresh uses material refresh version `2` (now
|
||||
`M2RuntimeMeshFinalizer.MATERIAL_REFRESH_VERSION`). Old cached `.tscn/.glb`
|
||||
meshes are replaced in memory from raw `.m2` only when the raw model actually
|
||||
needs custom billboard attributes or UV-rotation data. Do not rebuild every
|
||||
stale cached M2 during streaming: creating fresh materials for ordinary props
|
||||
can exhaust the D3D12 resource descriptor heap.
|
||||
- Verified with `ElwynnGrass1.m2`: native data exposes a billboard batch, the built mesh contains `CUSTOM0`, and the M2 shader compiles with the billboard uniform enabled.
|
||||
- Remaining parity work: many Elwynn tree canopy M2s do not carry billboard bone flags, so their flat-looking leaves are a separate material/geometry parity issue, not this bone-billboard path.
|
||||
|
||||
@@ -798,7 +824,8 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
- Texture transform dictionaries now expose static UV rotation matrices and approximate Z-rotation speeds, in addition to existing translation, translation speed and scale.
|
||||
- `WowM2Material` applies UV rotation and scale around WoW's texture center `(0.5, 0.5)`, then applies translation/scroll. This is closer to the client path shown by `CM2Model::AnimateTextureTransformsMT`.
|
||||
- All four M2 texture stages receive UV rotation and rotation-speed uniforms, matching the existing four-stage texture binding path.
|
||||
- `M2_MATERIAL_REFRESH_VERSION` and `M2Builder.MATERIAL_FORMAT_VERSION` are bumped to `2`, so cached M2 meshes are rebuilt in memory when raw `.m2` data is available.
|
||||
- Runtime M2 material refresh and `M2Builder.MATERIAL_FORMAT_VERSION` are `2`,
|
||||
so stale cached M2 meshes can be rebuilt in memory when raw `.m2` data is available.
|
||||
- Verified with `Spells/ArcaneForceShield_Blue.m2`: native data exposes a non-identity UV rotation, the built material receives the stage rotation uniform, and the shader compiles.
|
||||
|
||||
## 2026-07-08 Renderer Material Regression Smoke
|
||||
@@ -873,6 +900,372 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
- 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 M2 Runtime Mesh Rebuild Classifier Extraction
|
||||
|
||||
- `M2RuntimeMeshRebuildClassifier` now owns the memoized decision used when a
|
||||
stale cached M2 mesh may need an in-memory rebuild from raw data.
|
||||
- Existing triggers are unchanged: any billboard flag/positive billboard vertex
|
||||
count, or a referenced non-identity UV rotation/absolute rotation speed above
|
||||
`0.000001` across at most four texture stages.
|
||||
- Invalid batch variants and combo/transform indices remain skipped. The first
|
||||
decision for a normalized path remains cached until the same two historical
|
||||
map/shutdown clear sites.
|
||||
- `StreamingWorldLoader` still requests raw M2 data and owns ResourceLoader,
|
||||
returned-Mesh adoption and finalization budgets. Refresh-version checks,
|
||||
M2Builder rebuild and fallback now belong to `M2RuntimeMeshFinalizer`.
|
||||
- No cache version, shader, material, geometry or visible rule changed. Synthetic
|
||||
predicate timing is not asset-backed descriptor-pressure/p95/p99 evidence.
|
||||
|
||||
## 2026-07-17 M2 Mesh Load Pipeline State Extraction
|
||||
|
||||
- `M2MeshLoadPipelineState` now owns static M2 successful threaded-load request
|
||||
records and the FIFO populated when ResourceLoader polling reaches loaded or
|
||||
failed terminal status.
|
||||
- Pending polling snapshots preserve Dictionary insertion order; terminal FIFO
|
||||
order follows completion transition order. Existing `m2_mesh` metrics still
|
||||
count pending plus finalize records at all three sites.
|
||||
- Shutdown still drains every pending ResourceLoader path before state clear;
|
||||
map reset and shutdown retain the same two clear sites.
|
||||
- `StreamingWorldLoader` still selects `.tscn`/`.glb` paths, excludes animated
|
||||
pivot-prefix GLBs, performs all ResourceLoader calls, consumes
|
||||
`M2_MESH_FINALIZE`, extracts/prepares Meshes and updates Mesh/missing caches.
|
||||
- Cache formats, material refresh, profiles and visible output are unchanged.
|
||||
Synthetic state timing is not asset-backed I/O, leak or p95/p99 evidence.
|
||||
|
||||
## 2026-07-17 M2 Mesh Resource Cache State Extraction
|
||||
|
||||
- `M2MeshResourceCacheState` now owns normalized-path references to prepared
|
||||
static M2 Mesh resources. Lookup returns the exact retained reference and a
|
||||
later store for one path keeps the historical replacement behavior.
|
||||
- The cache retains map-refresh lifetime and clears only at the existing final
|
||||
runtime-cache shutdown site after asynchronous work is drained.
|
||||
- `StreamingWorldLoader` still owns normalization, `.tscn`/`.glb` selection,
|
||||
ResourceLoader I/O, Mesh extraction, material refresh/rebuild, cache adoption
|
||||
decisions, permits and MultiMesh materialization. Prototype/negative state is
|
||||
now a sibling cache service.
|
||||
- Cache formats, refresh metadata, profiles and visible output are unchanged.
|
||||
Synthetic cache timing is not asset-backed memory/leak or p95/p99 evidence.
|
||||
|
||||
## 2026-07-17 M2 Mesh Resource Extractor
|
||||
|
||||
- `M2MeshResourceExtractor` now selects the first Mesh from direct Mesh
|
||||
Resources, temporary PackedScene instances and existing prototype/rebuild
|
||||
Node subtrees using the prior depth-first child order.
|
||||
- PackedScene extraction still instantiates exactly once and synchronously frees
|
||||
the temporary root before returning the borrowed Mesh reference.
|
||||
- `StreamingWorldLoader` still owns ResourceLoader I/O and raw M2 consumers,
|
||||
material refresh/rebuild, cache/missing adoption, permits and MultiMesh builds.
|
||||
- Cache formats, traversal rules, profiles and visible output are unchanged.
|
||||
Synthetic traversal timing is not asset-backed leak or p95/p99 evidence.
|
||||
|
||||
## 2026-07-17 M2 Runtime Mesh Finalizer
|
||||
|
||||
- `M2RuntimeMeshFinalizer` now owns material refresh version `2`, stale-Mesh
|
||||
rebuild classification, M2Builder rebuild and original-Mesh fallback.
|
||||
- Current Meshes still skip raw `.m2` loading. `M2RawModelRepository` now owns
|
||||
FileAccess/ClassDB M2Loader I/O and supplies raw data through the loader only
|
||||
when the finalizer reports a stale Mesh; both historical clear sites persist.
|
||||
- Billboard/UV-rotation predicates, rebuild extraction, metadata key and failure
|
||||
fallback are unchanged. Cache adoption decisions, permits and MultiMesh
|
||||
materialization remain loader-owned; negative outcomes belong to prototype state.
|
||||
- Synthetic triangle rebuild/fallback timing is not asset-backed material,
|
||||
descriptor-pressure/leak or p95/p99 evidence.
|
||||
|
||||
## 2026-07-17 M2 Raw Model Repository
|
||||
|
||||
- `M2RawModelRepository` now owns the repeated extracted-file and optional
|
||||
native `M2Loader` boundary for static `load_m2` and animated
|
||||
`load_m2_animated` raw Dictionaries.
|
||||
- `StreamingWorldLoader` delegates the stale-Mesh refresh, static prototype and
|
||||
native animated prototype reads. It retains normalization, `.tscn/.glb`
|
||||
fallback order, builders, permits and Node/Mesh use; prototype/negative state
|
||||
is now isolated in `M2PrototypeCacheState`.
|
||||
- The repository retains no path, native object or parsed data. Empty paths,
|
||||
absent files/classes/methods and invalid results produce the same empty-value
|
||||
fallback contract; path join/globalization and native method names are exact.
|
||||
- No cache format, parser, material, geometry, animation or visible rule changed.
|
||||
Synthetic missing-input timing is not asset-backed I/O/p95/p99 or fidelity evidence.
|
||||
|
||||
## 2026-07-17 M2 Prototype Cache State
|
||||
|
||||
- `M2PrototypeCacheState` now owns the exact static and animated prototype Node
|
||||
references plus missing-model and static-animation negative path outcomes.
|
||||
- Sync static, native animated, threaded GLB animation, Mesh finalize and
|
||||
material-prototype paths delegate lookup/adoption/negative transitions to the
|
||||
service. First valid prototype remains canonical for each normalized path.
|
||||
- Map refresh still retains all four states. Final shutdown still occurs after
|
||||
asynchronous drain and now calls one idempotent release operation that frees
|
||||
detached prototypes, queues defensive in-tree Nodes and clears path outcomes.
|
||||
- `StreamingWorldLoader` retains normalization, ResourceLoader/native I/O,
|
||||
builders, animation policy, fallback order, permits and materialization.
|
||||
- No cache format, visible rule or profile changed. Synthetic ownership/timing
|
||||
evidence is not an asset-backed traversal/leak/p95/p99 or fidelity claim.
|
||||
|
||||
## 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 owns `water_loaded` timing. Main-thread Water subtree construction
|
||||
and attachment are delegated separately as described below.
|
||||
- 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-17 ADT Water Scene Finalizer Extraction
|
||||
|
||||
- `AdtWaterSceneFinalizer` now owns the two main-thread operations that turn
|
||||
validated parsed ADT data into a Water subtree: the existing
|
||||
`ADTBuilder.build_tile_water_scene` call and attachment to the tile root.
|
||||
- Both raw tile finalization and delayed cached-tile water results use the same
|
||||
service. Optional persisted Editor ownership is assigned recursively only
|
||||
when the loader supplies its existing edited-scene owner.
|
||||
- The service is stateless and retains no Node, Mesh, material or RID. The tile
|
||||
SceneTree subtree remains the lifetime owner; dry data still causes ADTBuilder
|
||||
to free its temporary empty Water root.
|
||||
- Parsing, task/result state, concurrency, stale validation, `WATER_FINALIZE`
|
||||
permits, `enable_water` and `water_loaded` timing remain loader-owned.
|
||||
- Geometry, materials, coordinates, cache formats, profiles and visible output
|
||||
are unchanged. Synthetic geometry/ownership evidence is not an MH2O/MCLQ or
|
||||
original-client parity claim; asset-backed traversal/p95/p99 remains 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
|
||||
|
||||
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.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<!-- 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
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<!-- 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
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<!-- 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
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -90,6 +90,7 @@ no-collision sandbox baseline.
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M02-RND-CAMERA-001:8c1cf9b -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-RND-CAMERA-001:68e6f60 -->
|
||||
|
||||
## Handoff
|
||||
|
||||
|
||||
@@ -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,116 @@
|
||||
# M03-RND-ADT-WATER-SCENE-FINALIZER-001 — ADT water scene finalizer
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-ADT-WATER-SCENE-FINALIZER-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-scene-finalizer`
|
||||
- Lease expires UTC: 2026-07-20
|
||||
- Integrator: M03 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract main-thread ADT water subtree construction, tile-root attachment and
|
||||
optional recursive editor ownership from `StreamingWorldLoader` while retaining
|
||||
the existing `ADTBuilder` geometry/material implementation.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Parse or reinterpret MH2O/MCLQ data.
|
||||
- Own pending requests, WorkerThreadPool tasks or parsed-result delivery.
|
||||
- Consume `WATER_FINALIZE` permits or validate tile/path/result staleness.
|
||||
- Mark tile state `water_loaded` or choose whether water is enabled.
|
||||
- Change liquid geometry, materials, coordinates, cache formats or profiles.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/liquid/adt_water_scene_finalizer.gd`,
|
||||
`src/tools/verify_adt_water_scene_finalizer.gd`,
|
||||
`docs/modules/adt-water-scene-finalizer.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
|
||||
`src/tools/verify_adt_water_load_pipeline_state.gd`,
|
||||
`docs/modules/adt-water-load-pipeline-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 resources, caches and
|
||||
proprietary renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Empty water data or invalid tile root produces no Node and no scene mutation.
|
||||
- Existing `ADTBuilder.build_tile_water_scene` remains the only geometry/material source.
|
||||
- A non-empty built Water root is attached exactly once to the supplied tile root.
|
||||
- Optional editor ownership is assigned recursively only when explicitly enabled
|
||||
and an edited scene root is available.
|
||||
- The returned Node is borrowed and remains owned by the tile SceneTree subtree.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted water pipeline package on current master `e1b2886`
|
||||
- Blocks: further liquid finalization/material responsibility extraction
|
||||
- External state: none; verification uses synthetic liquid dictionaries
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated empty/invalid/build/attach/editor-owner/source/timing
|
||||
verifier; water pipeline, materials, shutdown, facade, internal-access,
|
||||
manifest, documentation and coordination gates
|
||||
- Fixtures: synthetic one-cell liquid mask/heights and temporary Node roots
|
||||
- Fidelity evidence: exact existing ADTBuilder output and two attachment paths preserved
|
||||
- Performance budget: 100 empty finalization attempts under 1 second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs
|
||||
- Module specification with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state, main-thread sequence and dependency diagrams
|
||||
- Water-pipeline, world-renderer, module registry and RENDER status updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: `AdtWaterSceneFinalizer`
|
||||
- Simplest considered solution: one RefCounted with one explicit attach operation
|
||||
- Rejected complexity/abstractions: generic scene finalizer, signals, callbacks,
|
||||
builder interface, queue ownership or alternate liquid renderer
|
||||
- Unavoidable complexity and justification: editor-persisted generated subtrees
|
||||
require recursive owner assignment after SceneTree attachment
|
||||
- Measured optimization evidence: bounded synthetic no-water loop
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: stateless finalizer implemented; both loader water paths migrated;
|
||||
synthetic scene/owner regression and required documentation added
|
||||
- Next: continue liquid parsing/task execution extraction or another remaining M03 boundary
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-ADT-WATER-SCENE-FINALIZER-001:874fd0f -->
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `235be3a`
|
||||
- Results: scene finalizer PASS (`cases=8`, `iterations=100`,
|
||||
`elapsed_ms=0.042`; initial `0.024ms`); synthetic liquid produced one
|
||||
four-vertex/six-index surface with tile attachment and recursive owner; water
|
||||
pipeline (`119.648ms`), WMO/M2/terrain/facade/scheduler/streaming/coordinate,
|
||||
materials, shutdown, internal-access `36` and 32 headless contract regressions
|
||||
PASS; documentation and coordination gates PASS; checkpoint dry-run PASS `7/7`.
|
||||
- Remaining risks: main-thread mesh/material construction remains synchronous
|
||||
behind one existing permit; the Dictionary builder boundary remains untyped;
|
||||
proprietary MH2O/MCLQ traversal/leak/p95/p99/original-client evidence is absent.
|
||||
A verifier launched concurrently with the initial cold editor import observed
|
||||
the pre-existing global-class cache race; serialized editor parse and shutdown
|
||||
rerun passed, with only expected missing private asset diagnostics.
|
||||
- Documentation updated: inline public API; new
|
||||
`docs/modules/adt-water-scene-finalizer.md`; water-pipeline spec, module registry,
|
||||
world renderer and `RENDER.md`; data-flow/state/sequence/dependency diagrams,
|
||||
ownership, main-thread effects, failure/recovery, migration and source map included.
|
||||
- Integration: merged as `874fd0f`; post-merge finalizer (`cases=8`,
|
||||
`iterations=100`, `elapsed_ms=0.025`), water pipeline (`98.398ms`), shutdown,
|
||||
renderer materials, facade, internal-access (`private_symbols=36`), `7/7`
|
||||
manifest, documentation and coordination gates passed.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-ADT-WATER-SCENE-FINALIZER-001:235be3a -->
|
||||
@@ -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,109 @@
|
||||
# M03-RND-M2-MESH-LOAD-PIPELINE-001 — M2 mesh load pipeline state
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-MESH-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-m2-mesh-load-pipeline`
|
||||
- Lease expires UTC: 2026-07-20
|
||||
- Integrator: M03 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract static M2 threaded ResourceLoader request records and their terminal
|
||||
finalize FIFO from `StreamingWorldLoader`, preserving request order, status
|
||||
transition, metrics and reset/shutdown behavior.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Call ResourceLoader or choose `.tscn`/`.glb` cache paths.
|
||||
- Own M2 Mesh, scene, missing or material-refresh caches.
|
||||
- Extract Mesh from Resources or rebuild/adopt runtime Meshes.
|
||||
- Consume `M2_MESH_FINALIZE` permits.
|
||||
- Change cache formats, animation loading or visible M2 behavior.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_mesh_load_pipeline_state.gd`,
|
||||
`src/tools/verify_m2_mesh_load_pipeline_state.gd`,
|
||||
`docs/modules/m2-mesh-load-pipeline-state.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 M2 resources, caches and
|
||||
proprietary renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- One normalized path owns at most one pending request.
|
||||
- Pending records preserve Dictionary insertion order for polling snapshots.
|
||||
- Terminal completion removes the request and appends its record/status to FIFO.
|
||||
- Finalize pop remains oldest-first; duplicate/unknown transitions are rejected.
|
||||
- Total work count remains pending plus finalize records.
|
||||
- Clear drops bookkeeping only; loader drains ResourceLoader requests first.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted classifier package on current master `8ec13d6`
|
||||
- Blocks: further M2 Mesh cache/finalization service extraction
|
||||
- External state: none; verification uses synthetic paths and opaque statuses
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated validation/order/transition/FIFO/clear/source/timing
|
||||
verifier; M2 classifier/builder/grouper/transform/registry, materials, shutdown,
|
||||
facade, internal-access, manifest, documentation and coordination gates
|
||||
- Fixtures: synthetic normalized paths, resource paths and integer terminal statuses
|
||||
- Fidelity evidence: exact request/finalize transitions and metric/reset sites preserved
|
||||
- Performance budget: 100 cycles over 256 request/completion/pop records under 1 second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs
|
||||
- Module specification with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state, sequence and dependency diagrams
|
||||
- World-renderer, module registry and RENDER status updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: `M2MeshLoadPipelineState`
|
||||
- Simplest considered solution: one RefCounted with request map and finalize FIFO
|
||||
- Rejected complexity/abstractions: generic ResourceLoader queue, signals,
|
||||
callbacks, Mesh cache ownership or animation/static pipeline merger
|
||||
- Unavoidable complexity and justification: polling and budgeted finalization are
|
||||
distinct lifecycle stages across frames
|
||||
- Measured optimization evidence: bounded synthetic transition loop
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: state holder, loader adapters, contract verifier and required documentation
|
||||
- Next: further M2 Mesh cache/finalizer extraction
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `82df7a5`
|
||||
- Results: 34 headless contract regressions pass; dedicated pipeline state
|
||||
verifier passes 100-by-256 transitions in 100.273 ms; documentation and
|
||||
coordination gates pass; checkpoint dry-run passes 7/7.
|
||||
- Remaining risks: no private asset traversal, leak/descriptor-pressure or
|
||||
original-client visual comparison; ResourceLoader I/O and Mesh cache/finalizer
|
||||
ownership remain in `StreamingWorldLoader` by design.
|
||||
- Documentation updated: inline API; `m2-mesh-load-pipeline-state.md` with
|
||||
data-flow, state, sequence and dependency diagrams; module registry,
|
||||
`world-renderer.md` and `RENDER.md`.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-MESH-LOAD-PIPELINE-001:82df7a5 -->
|
||||
|
||||
## Integration
|
||||
|
||||
- Merge commit: `83b3618`
|
||||
- Post-merge results: pipeline state `cases=11 iterations=100
|
||||
elapsed_ms=69.196`; classifier, materials, shutdown, facade, internal-access
|
||||
`34`, manifest `7/7`, documentation and coordination gates passed.
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-MESH-LOAD-PIPELINE-001:83b3618 -->
|
||||
@@ -0,0 +1,108 @@
|
||||
# M03-RND-M2-MESH-RESOURCE-CACHE-001 — M2 mesh resource cache state
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-MESH-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-m2-mesh-resource-cache`
|
||||
- Lease expires UTC: 2026-07-20
|
||||
- Integrator: M03 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract the normalized-path to static M2 Mesh resource cache from
|
||||
`StreamingWorldLoader`, preserving lookup, replacement, shutdown lifetime and
|
||||
all existing cache consumers.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Own the shared M2 missing-path cache or animated/prototype node caches.
|
||||
- Call ResourceLoader, extract Meshes from Resources or select cache paths.
|
||||
- Prepare, rebuild or materialize M2 Meshes.
|
||||
- Change cache formats, material refresh, animation or visible behavior.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_mesh_resource_cache_state.gd`,
|
||||
`src/tools/verify_m2_mesh_resource_cache_state.gd`,
|
||||
`docs/modules/m2-mesh-resource-cache-state.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
|
||||
`src/tools/verify_m2_mesh_load_pipeline_state.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 M2 resources, caches and
|
||||
proprietary renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Non-empty normalized M2 paths map to at most one Mesh resource reference.
|
||||
- Store replaces the previous reference for the same path.
|
||||
- Lookup returns the exact retained Mesh reference; unknown/empty paths return null.
|
||||
- Clear releases all cache references only at the existing final-shutdown site.
|
||||
- Shared missing/prototype/animated caches remain loader-owned and unchanged.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted mesh load pipeline package on master `ef782d1`
|
||||
- Blocks: isolated M2 Mesh resource finalizer extraction
|
||||
- External state: none; verification uses synthetic Mesh resources
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated validation/store/replace/lookup/clear/source/timing
|
||||
verifier; mesh pipeline/classifier/material/shutdown/M2 build/facade/internal-
|
||||
access/manifest, documentation and coordination gates
|
||||
- Fixtures: synthetic ArrayMesh resources and normalized paths
|
||||
- Fidelity evidence: exact cache call-site and shutdown lifetime preservation
|
||||
- Performance budget: 100 cycles over 256 store/lookup entries under 1 second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs
|
||||
- Module specification with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state and dependency diagrams
|
||||
- World-renderer, module registry and RENDER status updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name introduced: `M2MeshResourceCacheState`
|
||||
- Simplest solution: one RefCounted with one typed-purpose Dictionary
|
||||
- Rejected complexity: generic resource cache, missing-state merger, eviction,
|
||||
signals, callbacks or materialization ownership
|
||||
- Unavoidable complexity: none beyond retaining engine Resource references
|
||||
- Measured optimization evidence: bounded synthetic store/lookup loop
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: cache state, loader/test adapters, contract verifier and required documentation
|
||||
- Next: isolated M2 Mesh resource finalizer extraction
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `e990a65`
|
||||
- Results: dedicated cache verifier passes 100-by-256 store/lookups in
|
||||
38.974 ms; 35 headless renderer/coordinate regressions pass; checkpoint
|
||||
dry-run passes 7/7; documentation and coordination gates pass.
|
||||
- Remaining risks: no private M2 traversal, Mesh memory/leak/descriptor-pressure
|
||||
run or original-client visual comparison; extraction/preparation, shared
|
||||
missing state and materialization remain loader-owned by design.
|
||||
- Documentation updated: inline API; `m2-mesh-resource-cache-state.md` with
|
||||
data-flow, lifecycle, sequence and dependency diagrams; pipeline spec, module
|
||||
registry, `world-renderer.md` and `RENDER.md`.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-MESH-RESOURCE-CACHE-001:e990a65 -->
|
||||
|
||||
## Integration
|
||||
|
||||
- Merge commit: `3094561`
|
||||
- Post-merge results: cache state `cases=9 iterations=100
|
||||
elapsed_ms=38.686`; pipeline, classifier, materials, shutdown, facade,
|
||||
internal-access `34`, manifest `7/7`, documentation and coordination gates passed.
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-MESH-RESOURCE-CACHE-001:3094561 -->
|
||||
@@ -0,0 +1,109 @@
|
||||
# M03-RND-M2-MESH-RESOURCE-EXTRACTOR-001 — M2 mesh resource extractor
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-MESH-RESOURCE-EXTRACTOR-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-mesh-resource-extractor`
|
||||
- Lease expires UTC: 2026-07-20
|
||||
- Integrator: M03 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract first-Mesh selection from loaded M2 Resources and Node subtrees,
|
||||
including temporary PackedScene instance cleanup, from `StreamingWorldLoader`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Call ResourceLoader or select/request cache paths.
|
||||
- Refresh/rebuild materials or load raw M2 data.
|
||||
- Own the Mesh cache, missing state, prototypes or MultiMesh materialization.
|
||||
- Change traversal order, cache formats, profiles or visible behavior.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_mesh_resource_extractor.gd`,
|
||||
`src/tools/verify_m2_mesh_resource_extractor.gd`,
|
||||
`docs/modules/m2-mesh-resource-extractor.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
|
||||
M2 pipeline/cache/material/shutdown verifiers, `docs/modules/world-renderer.md`,
|
||||
`docs/modules/README.md`, M2 module specs, `RENDER.md`,
|
||||
`targets/03-renderer-facade.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated M2 resources, caches and
|
||||
proprietary renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- A direct Mesh Resource returns the same Mesh reference.
|
||||
- A PackedScene is instantiated once, searched depth-first in child order and
|
||||
its temporary root is freed before return.
|
||||
- A Node subtree search returns the first MeshInstance3D Mesh in historical
|
||||
depth-first preorder without changing the tree.
|
||||
- Null, unsupported Resources and subtrees without a Mesh return null.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted Mesh resource cache package on master `5e2afbc`
|
||||
- Blocks: isolated material refresh/rebuild finalizer extraction
|
||||
- External state: none; verification uses synthetic Mesh/Node/PackedScene fixtures
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated direct/PackedScene/subtree/order/lifetime/source/timing
|
||||
verifier; M2 pipeline/cache/classifier/material/shutdown/build/facade/internal-
|
||||
access/manifest, documentation and coordination gates
|
||||
- Fixtures: ArrayMesh, MeshInstance3D subtrees and synthetic PackedScene
|
||||
- Fidelity evidence: exact traversal order and temporary-node destruction
|
||||
- Performance budget: 10,000 synthetic subtree lookups under 1 second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs
|
||||
- Module specification with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state, sequence and dependency diagrams
|
||||
- M2 cache/pipeline, world-renderer, registry and RENDER updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name introduced: `M2MeshResourceExtractor`
|
||||
- Simplest solution: one stateless RefCounted with two explicit extraction APIs
|
||||
- Rejected complexity: callback-driven finalizer, generic scene utility,
|
||||
cache ownership, signals or visitor framework
|
||||
- Unavoidable complexity: temporary PackedScene instance must be freed while
|
||||
its referenced Mesh remains borrowed
|
||||
- Measured optimization evidence: bounded synthetic depth-first traversal
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: extractor, three loader/test adapters, contract verifier and required documentation
|
||||
- Next: isolated M2 Mesh material preparation finalizer extraction
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `7b0e1ea`
|
||||
- Results: extractor passes direct/PackedScene/subtree/order/lifetime contracts
|
||||
and 10,000 traversals in 39.243 ms; 36 headless renderer/coordinate
|
||||
regressions pass; checkpoint dry-run passes 7/7; documentation and
|
||||
coordination gates pass.
|
||||
- Remaining risks: synchronous PackedScene instantiation remains behind the
|
||||
existing permit; no private M2 corrupt-scene/traversal/leak/p95/p99 or
|
||||
original-client comparison; material preparation remains loader-owned.
|
||||
- Documentation updated: inline API; `m2-mesh-resource-extractor.md` with
|
||||
data-flow, lifecycle, sequence and dependency diagrams; cache/pipeline specs,
|
||||
module registry, `world-renderer.md` and `RENDER.md`.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-MESH-RESOURCE-EXTRACTOR-001:7b0e1ea -->
|
||||
|
||||
## Integration
|
||||
|
||||
- Merge commit: `fa85985`
|
||||
- Post-merge results: extractor `cases=10 iterations=10000
|
||||
elapsed_ms=38.172`; cache, pipeline, classifier, materials, shutdown, facade,
|
||||
internal-access `34`, manifest `7/7`, documentation and coordination gates passed.
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-MESH-RESOURCE-EXTRACTOR-001:fa85985 -->
|
||||
@@ -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,111 @@
|
||||
# M03-RND-M2-PROTOTYPE-CACHE-001 — M2 prototype cache state
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-PROTOTYPE-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-m2-prototype-cache`
|
||||
- Lease expires UTC: 2026-07-20
|
||||
- Integrator: M03 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract static/animated detached M2 prototype references plus missing-model and
|
||||
static-animation negative outcomes from `StreamingWorldLoader` into one explicit
|
||||
shutdown-lifetime cache-state service.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Choose `.tscn/.glb` paths, call ResourceLoader/native loaders or build Nodes.
|
||||
- Own animation/Mesh request queues, Mesh resources, materials or MultiMeshes.
|
||||
- Change candidate/allowlist/denylist rules, fallback order or visible behavior.
|
||||
- Add persistence, eviction, async work, dependencies or cache-format changes.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_prototype_cache_state.gd`,
|
||||
`src/tools/verify_m2_prototype_cache_state.gd`,
|
||||
`docs/modules/m2-prototype-cache-state.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, runtime
|
||||
cache shutdown/M2 raw/finalizer/extractor/pipeline/material/animation verifiers
|
||||
and module specs, `docs/modules/world-renderer.md`, `docs/modules/README.md`,
|
||||
`RENDER.md`, `targets/03-renderer-facade.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated M2 resources, caches and
|
||||
proprietary renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Normalized non-empty paths key static and animated detached `Node3D` prototypes.
|
||||
- Missing-model and static-animation outcomes are independent path sets.
|
||||
- Exact cached Node reference is returned; first adoption for an occupied path wins.
|
||||
- Shutdown frees valid detached nodes synchronously, queues in-tree nodes for free,
|
||||
clears all four states and remains idempotent.
|
||||
- Map refresh retains the state exactly as before; only final shutdown clears it.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted raw model repository package on master `ca77d32`
|
||||
- Blocks: remaining M2 animation request/materialization extraction
|
||||
- External state: none; verification uses synthetic Node3D instances
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated admission/identity/independence/duplicate/free/source/timing
|
||||
verifier; runtime shutdown/raw/finalizer/extractor/cache/pipeline/material/
|
||||
animation/M2 build/facade/internal-access/manifest, docs and coordination gates
|
||||
- Fixtures: detached and in-tree synthetic Node3D prototypes
|
||||
- Fidelity evidence: all existing lookup/store/negative/clear caller transitions
|
||||
- Performance budget: 100 cycles over 256 path-state operations under 1 second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs
|
||||
- Module specification with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state, sequence and dependency diagrams
|
||||
- Adjacent M2/world-renderer/module-registry/RENDER updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name introduced: `M2PrototypeCacheState`
|
||||
- Simplest solution: one main-thread RefCounted with two Node maps and two path sets
|
||||
- Rejected complexity: generic Node cache, signals, callbacks, eviction policy,
|
||||
weak references, async cleanup or dependency injection
|
||||
- Unavoidable complexity: detached Nodes require explicit free at final shutdown
|
||||
- Measured optimization evidence: bounded synthetic cache-cycle loop
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: cache state, loader/test adapters, lifecycle verifier and required documentation
|
||||
- Next: continue M2 animation request/materialization extraction
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `cd664eb`
|
||||
- Results: prototype state passes 16 admission/identity/negative/lifecycle/source
|
||||
cases and 100-by-256 cycles in 50.266 ms; 46 autonomous headless verifiers
|
||||
pass, while the proprietary ADT placement probe is unavailable without
|
||||
`data/extracted`; checkpoint dry-run passes 7/7; documentation and
|
||||
coordination gates pass; loader-private inventory decreases from 34 to 31.
|
||||
- Remaining risks: strong prototypes remain unbounded until shutdown; successful
|
||||
asset traversal, descriptor pressure, leaks and p95/p99 require proprietary
|
||||
data; no original-client visual comparison is claimed.
|
||||
- Documentation updated: inline API; `m2-prototype-cache-state.md` with data-flow,
|
||||
lifecycle, sequence and dependency diagrams; pipeline/Mesh-cache/extractor/raw
|
||||
repository/world-renderer specs, module registry and `RENDER.md`.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-PROTOTYPE-CACHE-001:cd664eb -->
|
||||
|
||||
## Integration
|
||||
|
||||
- Merge commit: `1de50f9`
|
||||
- Post-merge results: prototype cache `cases=16 iterations=100
|
||||
elapsed_ms=37.978`; shutdown, raw repository, finalizer, extractor, Mesh cache,
|
||||
pipeline, materials, facade, internal-access `31`, manifest `7/7`,
|
||||
documentation and coordination gates passed.
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-PROTOTYPE-CACHE-001:1de50f9 -->
|
||||
@@ -0,0 +1,109 @@
|
||||
# M03-RND-M2-RAW-MODEL-REPOSITORY-001 — raw M2 model repository
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-RAW-MODEL-REPOSITORY-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-raw-model-repository`
|
||||
- Lease expires UTC: 2026-07-20
|
||||
- Integrator: M03 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract the repeated FileAccess/ClassDB native `M2Loader` boundary for static
|
||||
and animated raw model Dictionaries from `StreamingWorldLoader`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Cache raw data, Meshes, prototypes, missing paths or animation decisions.
|
||||
- Build Nodes/Meshes, refresh materials or choose `.tscn/.glb` caches.
|
||||
- Change static/animated parser methods, path resolution or fallback order.
|
||||
- Add dependencies, persistence, retries or visible behavior.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_raw_model_repository.gd`,
|
||||
`src/tools/verify_m2_raw_model_repository.gd`,
|
||||
`docs/modules/m2-raw-model-repository.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2
|
||||
finalizer/extractor/cache/pipeline/material/animation/shutdown verifiers and
|
||||
module specs, `docs/modules/world-renderer.md`, `docs/modules/README.md`,
|
||||
`RENDER.md`, `targets/03-renderer-facade.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated M2 resources, caches and
|
||||
proprietary renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Static load calls native `load_m2`; animated load calls `load_m2_animated`.
|
||||
- Paths remain `globalize_path(extracted_directory.path_join(normalized_path))`.
|
||||
- Empty path, missing native class/file/instance/method, parse failure or
|
||||
non-Dictionary result returns an empty Dictionary without retained state.
|
||||
- Each successful request creates one native loader instance as before.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted runtime Mesh finalizer package on master `1d94eac`
|
||||
- Blocks: M2 prototype/cache state extraction
|
||||
- External state: native M2Loader registration and local extracted files
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated invalid/missing/class/source/timing verifier; finalizer/
|
||||
classifier/extractor/cache/pipeline/material/animation/shutdown/M2 build/facade/
|
||||
internal-access/manifest, documentation and coordination gates
|
||||
- Fixtures: empty/missing normalized paths plus source contract; proprietary
|
||||
success corpus remains external
|
||||
- Fidelity evidence: exact native method names, path expression and three caller transitions
|
||||
- Performance budget: 10,000 rejected empty-path calls under 1 second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs
|
||||
- Module specification with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state, sequence and dependency diagrams
|
||||
- Finalizer/extractor/cache/pipeline, world-renderer, registry and RENDER updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name introduced: `M2RawModelRepository`
|
||||
- Simplest solution: one stateless RefCounted with static and animated load methods
|
||||
- Rejected complexity: generic native repository, caching, dependency injection,
|
||||
retries, diagnostics framework or async wrapper
|
||||
- Unavoidable complexity: animated native method availability is a distinct guard
|
||||
- Measured optimization evidence: bounded rejected-input loop
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: repository, three loader adapters, contract verifier and required documentation
|
||||
- Next: continue M2 prototype/cache state extraction
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `b608a81`
|
||||
- Results: repository passes invalid/missing/stateless/native-boundary contracts
|
||||
and 10,000 empty-path rejects in 3.275 ms; 45 repository-independent
|
||||
headless verifiers pass, while the proprietary ADT placement probe is
|
||||
unavailable without `data/extracted`; checkpoint dry-run passes 7/7;
|
||||
documentation and coordination gates pass.
|
||||
- Remaining risks: successful/corrupt native parsing, descriptor pressure,
|
||||
leaks and p95/p99 remain asset-backed work; native calls are synchronous and
|
||||
cannot be cancelled mid-parse; no original-client visual comparison is claimed.
|
||||
- Documentation updated: inline API; `m2-raw-model-repository.md` with data-flow,
|
||||
lifecycle, sequence and dependency diagrams; finalizer/classifier/world-renderer
|
||||
specs, module registry and `RENDER.md`.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-RAW-MODEL-REPOSITORY-001:b608a81 -->
|
||||
|
||||
## Integration
|
||||
|
||||
- Merge commit: `7cd07dd`
|
||||
- Post-merge results: repository `cases=10 iterations=10000
|
||||
elapsed_ms=3.200`; finalizer, extractor, cache, pipeline, shutdown, facade,
|
||||
internal-access `34`, manifest `7/7`, documentation and coordination gates passed.
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-RAW-MODEL-REPOSITORY-001:7cd07dd -->
|
||||
@@ -0,0 +1,110 @@
|
||||
# M03-RND-M2-RUNTIME-MESH-FINALIZER-001 — M2 runtime mesh finalizer
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-RUNTIME-MESH-FINALIZER-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-runtime-mesh-finalizer`
|
||||
- Lease expires UTC: 2026-07-20
|
||||
- Integrator: M03 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract stale cached-Mesh refresh classification, metadata adoption,
|
||||
M2Builder rebuild and fallback from `StreamingWorldLoader` into one runtime M2
|
||||
Mesh finalizer while retaining raw-file I/O in the loader.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Load raw `.m2` files, call FileAccess/ClassDB M2Loader or choose cache paths.
|
||||
- Own ResourceLoader requests, Mesh cache/missing state or MultiMesh builds.
|
||||
- Change material refresh version `2`, classifier predicates or rebuild behavior.
|
||||
- Change cache formats, profiles or visible output.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_runtime_mesh_finalizer.gd`,
|
||||
`src/tools/verify_m2_runtime_mesh_finalizer.gd`,
|
||||
`docs/modules/m2-runtime-mesh-finalizer.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2
|
||||
classifier/extractor/cache/pipeline/material/shutdown verifiers and module
|
||||
specs, `docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
|
||||
`targets/03-renderer-facade.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated M2 resources, caches and
|
||||
proprietary renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Refresh version remains `2`; already-current Meshes return unchanged without raw data.
|
||||
- Stale Meshes require caller-supplied raw data only when refresh is necessary.
|
||||
- Empty/ordinary raw data marks the original Mesh current and returns it.
|
||||
- Billboard/UV-rotation data attempts M2Builder rebuild and returns the rebuilt
|
||||
first Mesh; rebuild failure marks and returns the original Mesh.
|
||||
- Classifier memoization retains both existing clear sites through finalizer clear.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted Mesh resource extractor package on master `dd8cfbf`
|
||||
- Blocks: further M2 raw-data I/O/prototype state extraction
|
||||
- External state: none; verification uses synthetic Mesh/raw-data fixtures
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated current/stale/ordinary/rebuild/fallback/clear/source/timing
|
||||
verifier; classifier/extractor/cache/pipeline/material/shutdown/M2 build/facade/
|
||||
internal-access/manifest, documentation and coordination gates
|
||||
- Fixtures: ArrayMesh and one synthetic triangle/billboard M2 Dictionary
|
||||
- Fidelity evidence: exact refresh version, predicate, rebuild and fallback transitions
|
||||
- Performance budget: 100 cycles over 256 ordinary finalizations under 1 second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs
|
||||
- Module specification with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state, sequence and dependency diagrams
|
||||
- Classifier/extractor/cache/pipeline, world-renderer, registry and RENDER updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name introduced: `M2RuntimeMeshFinalizer`
|
||||
- Simplest solution: one service composing existing classifier/extractor and M2Builder
|
||||
- Rejected complexity: callbacks for loader I/O, generic material pipeline,
|
||||
cache ownership, signals or strategy framework
|
||||
- Unavoidable complexity: two-phase raw-data request avoids loading raw files for current Meshes
|
||||
- Measured optimization evidence: bounded synthetic ordinary finalization loop
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: finalizer, loader/test adapters, transition/rebuild verifier and required documentation
|
||||
- Next: further M2 raw-data I/O/prototype state extraction
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `ece7724`
|
||||
- Results: finalizer passes current/stale/ordinary/synthetic-rebuild/fallback/
|
||||
clear contracts and 100-by-256 finalizations in 53.719 ms; 37 headless
|
||||
renderer/coordinate regressions pass; checkpoint dry-run passes 7/7;
|
||||
documentation and coordination gates pass.
|
||||
- Remaining risks: rebuild remains synchronous behind the existing permit; no
|
||||
private M2 material/traversal/descriptor-pressure/leak/p95/p99 or original-
|
||||
client comparison; raw `.m2` I/O remains loader-owned by design.
|
||||
- Documentation updated: inline API; `m2-runtime-mesh-finalizer.md` with
|
||||
data-flow, lifecycle, sequence and dependency diagrams; classifier/extractor/
|
||||
cache/pipeline specs, module registry, `world-renderer.md` and `RENDER.md`.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-RUNTIME-MESH-FINALIZER-001:ece7724 -->
|
||||
|
||||
## Integration
|
||||
|
||||
- Merge commit: `1fafdde`
|
||||
- Post-merge results: finalizer `cases=11 iterations=100
|
||||
elapsed_ms=54.349`; classifier, extractor, cache, pipeline, materials,
|
||||
shutdown, facade, internal-access `34`, manifest `7/7`, documentation and
|
||||
coordination gates passed.
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-RUNTIME-MESH-FINALIZER-001:1fafdde -->
|
||||
@@ -0,0 +1,115 @@
|
||||
# M03-RND-M2-RUNTIME-REBUILD-CLASSIFIER-001 — M2 runtime rebuild classifier
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-RUNTIME-REBUILD-CLASSIFIER-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-runtime-rebuild-classifier`
|
||||
- Lease expires UTC: 2026-07-20
|
||||
- Integrator: M03 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract the memoized decision that stale cached M2 meshes require an in-memory
|
||||
runtime rebuild because raw batches contain billboard vertices or referenced UV
|
||||
rotation transforms.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Load raw M2 files or cache Resources.
|
||||
- Build, replace or mutate Meshes, materials, Nodes or RIDs.
|
||||
- Change `M2_MATERIAL_REFRESH_VERSION` or cache formats.
|
||||
- Interpret animation, particles, ribbons or general M2 shader fidelity.
|
||||
- Change path normalization, ResourceLoader transitions or finalize budgets.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_runtime_mesh_rebuild_classifier.gd`,
|
||||
`src/tools/verify_m2_runtime_mesh_rebuild_classifier.gd`,
|
||||
`docs/modules/m2-runtime-mesh-rebuild-classifier.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/M2 resources, caches
|
||||
and proprietary renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Billboard metadata requires rebuild when `has_billboard` is true or
|
||||
`billboard_vertex_count` is positive in any Dictionary batch.
|
||||
- UV rotation requires non-empty transforms and combos plus a referenced
|
||||
non-identity `Vector4` rotation or absolute rotation speed above `0.000001`.
|
||||
- Only up to four texture stages per batch are inspected; invalid variants and
|
||||
out-of-range combo/transform indices are skipped.
|
||||
- The first decision for a normalized path is memoized until clear, matching the
|
||||
existing loader cache lifetime.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: current accepted master `90b1dac`
|
||||
- Blocks: further M2 mesh request/cache/finalization extraction
|
||||
- External state: none; verification uses synthetic raw M2 Dictionaries
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated billboard/UV/index/stage/cache/clear/source/timing verifier;
|
||||
M2 builder/grouper/transform/registry, materials, shutdown, facade,
|
||||
internal-access, manifest, documentation and coordination gates
|
||||
- Fixtures: synthetic batches, transform combos and rotation dictionaries
|
||||
- Fidelity evidence: exact existing predicate thresholds, stage cap and cache lifetime preserved
|
||||
- Performance budget: 100 classifications over 256 paths under 1 second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs
|
||||
- Module specification with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state, sequence and dependency diagrams
|
||||
- World-renderer, module registry and RENDER status updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names introduced: `M2RuntimeMeshRebuildClassifier`
|
||||
- Simplest considered solution: one RefCounted with one memoized query and clear
|
||||
- Rejected complexity/abstractions: generic predicate cache, strategy interface,
|
||||
ResourceLoader wrapper, signals or typed rewrite of raw parser Dictionaries
|
||||
- Unavoidable complexity and justification: four-stage combo indirection is the
|
||||
existing build-12340 M2 material boundary
|
||||
- Measured optimization evidence: bounded synthetic classification loop
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: classifier/cache implemented; loader query and both clear sites migrated;
|
||||
synthetic predicate/cache verifier and required documentation added
|
||||
- Next: continue M2 mesh request/cache/finalization extraction
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-RUNTIME-REBUILD-CLASSIFIER-001:38f831e -->
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `19ca8f4`
|
||||
- Results: classifier PASS (`cases=12`, `iterations=100`,
|
||||
`elapsed_ms=64.585`; initial `35.461ms`); billboard flag/count, invalid
|
||||
variants/indices, UV rotation/speed/epsilon, four-stage cap, memoization,
|
||||
detached diagnostics and clear contracts PASS; 33 M2/WMO/liquid/terrain/
|
||||
facade/scheduler/streaming/coordinate/material/shutdown regressions PASS;
|
||||
internal-access decreased to `35`; documentation and coordination gates PASS;
|
||||
checkpoint dry-run PASS `7/7`.
|
||||
- Remaining risks: raw parser data remains Dictionary-based; the first decision
|
||||
for a path remains authoritative until clear; classification covers the current
|
||||
four-stage material implementation only; private asset descriptor-pressure,
|
||||
traversal, p95/p99 and paired original-client evidence remain absent.
|
||||
- Documentation updated: inline public API; new
|
||||
`docs/modules/m2-runtime-mesh-rebuild-classifier.md`; module registry, world
|
||||
renderer and `RENDER.md`; data-flow/state/sequence/dependency diagrams,
|
||||
ownership, failure/recovery, thresholds, cache lifetime and source map included.
|
||||
- Integration: merged as `38f831e`; post-merge classifier (`cases=12`,
|
||||
`iterations=100`, `elapsed_ms=45.439`), materials, shutdown, M2 batch planner,
|
||||
facade, internal-access (`private_symbols=35`), `7/7` manifest, documentation
|
||||
and coordination gates passed.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-RUNTIME-REBUILD-CLASSIFIER-001:19ca8f4 -->
|
||||
@@ -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 -->
|
||||
@@ -12,8 +12,34 @@
|
||||
| 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) |
|
||||
| ADT water scene finalizer | Implemented extraction | [`adt-water-scene-finalizer.md`](adt-water-scene-finalizer.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) |
|
||||
| M2 runtime mesh rebuild classifier | Implemented extraction | [`m2-runtime-mesh-rebuild-classifier.md`](m2-runtime-mesh-rebuild-classifier.md) |
|
||||
| M2 mesh load pipeline state | Implemented extraction | [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.md) |
|
||||
| M2 mesh resource cache state | Implemented extraction | [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.md) |
|
||||
| M2 mesh resource extractor | Implemented extraction | [`m2-mesh-resource-extractor.md`](m2-mesh-resource-extractor.md) |
|
||||
| M2 runtime mesh finalizer | Implemented extraction | [`m2-runtime-mesh-finalizer.md`](m2-runtime-mesh-finalizer.md) |
|
||||
| M2 raw model repository | Implemented extraction | [`m2-raw-model-repository.md`](m2-raw-model-repository.md) |
|
||||
| M2 prototype cache state | Implemented extraction | [`m2-prototype-cache-state.md`](m2-prototype-cache-state.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) |
|
||||
| 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) |
|
||||
| Gameplay | Prototype/Planned | [`../../targets/roadmap/04-gameplay-systems.md`](../../targets/roadmap/04-gameplay-systems.md) |
|
||||
| UI/Lua/audio | Prototype/Planned | [`../../targets/roadmap/05-ui-lua-audio.md`](../../targets/roadmap/05-ui-lua-audio.md) |
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
# 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 --> Finalizer[AdtWaterSceneFinalizer]
|
||||
Finalizer --> Builder[ADTBuilder water scene]
|
||||
Finalizer --> 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 Finalizer as AdtWaterSceneFinalizer
|
||||
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->>Finalizer: attach_water_scene(data, origin, tile root, editor owner)
|
||||
Loader->>Loader: mark water_loaded
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> State[AdtWaterLoadPipelineState]
|
||||
Loader --> Scheduler[RenderBudgetScheduler]
|
||||
Loader --> Worker[WorkerThreadPool / ADTLoader]
|
||||
Loader --> Finalizer[AdtWaterSceneFinalizer]
|
||||
Finalizer --> Builder[ADTBuilder / SceneTree]
|
||||
Worker -->|publish only| State
|
||||
State -. no dependency .-> Scheduler
|
||||
State -. no dependency .-> Finalizer
|
||||
```
|
||||
|
||||
## 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 and the tile subtree; `AdtWaterSceneFinalizer` performs
|
||||
the main-thread ADTBuilder call and attachment without retaining Nodes.
|
||||
- `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 is now delegated to `AdtWaterSceneFinalizer`,
|
||||
independently of this async bookkeeping service.
|
||||
|
||||
## 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 | Implemented extraction | Dedicated synthetic scene/ownership verifier | Asset-backed traversal/hitch evidence pending |
|
||||
|
||||
## 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/render/liquid/adt_water_scene_finalizer.gd` | Main-thread ADTBuilder call, attach and optional editor ownership |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Parse task execution, permits, stale checks and tile loaded timing |
|
||||
| `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)
|
||||
- [`adt-water-scene-finalizer.md`](adt-water-scene-finalizer.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,232 @@
|
||||
# ADT Water Scene Finalizer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-ADT-WATER-SCENE-FINALIZER-001` |
|
||||
| Owners | Main-thread ADT water subtree build, attach and optional editor ownership |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-adt-water-scene-finalizer`, 2026-07-17 |
|
||||
| Profiles/capabilities | Existing ADT MH2O/MCLQ scene output; profile-independent adapter |
|
||||
|
||||
## Purpose
|
||||
|
||||
Provide one liquid-specific main-thread boundary for converting parsed ADT
|
||||
water data through the existing `ADTBuilder`, attaching the resulting `Water`
|
||||
subtree to a caller-owned tile root and assigning optional persisted Editor
|
||||
ownership. The service is stateless and retains no Node or Resource.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Parse or validate MH2O/MCLQ binary structures.
|
||||
- Replace ADTBuilder geometry, material or coordinate calculations.
|
||||
- Own water request/task/result state, permits or concurrency.
|
||||
- Validate tile/path/root freshness beyond receiving a valid root.
|
||||
- Mark `water_loaded`, enable water or remove water subtrees.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Parse[Parsed ADT Dictionary] --> Loader[StreamingWorldLoader]
|
||||
Loader -->|validated data, origin, tile root| Finalizer[AdtWaterSceneFinalizer]
|
||||
Finalizer --> Builder[ADTBuilder]
|
||||
Builder --> Water[Water Node3D or null]
|
||||
Water --> Finalizer
|
||||
Finalizer -->|main-thread add_child| Tile[Caller-owned tile root]
|
||||
Loader -->|after return| Loaded[water_loaded timing]
|
||||
```
|
||||
|
||||
Allowed dependencies are `ADTBuilder` and Godot Node/mesh types needed to adopt
|
||||
its output. WorkerThreadPool, scheduler, tile-state dictionaries, facade,
|
||||
gameplay, network and editor plugin packages are forbidden.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `attach_water_scene(water_data, tile_origin_godot_units, tile_root, editor_owner)` | Command/query | Build and attach one existing-format water subtree | Renderer main thread; returned Node is borrowed for tile lifetime | Empty data, invalid root or dry build returns null without attachment |
|
||||
|
||||
`editor_owner` is optional. When supplied it must be an ancestor suitable for
|
||||
Godot `Node.owner`; ownership is recursively assigned to the generated subtree.
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Parsed ADT water-containing Dictionary | ADTLoader/raw tile path | Finalizer/ADTBuilder | Caller retains Dictionary; not mutated | One main-thread call |
|
||||
| Input | Tile origin in Godot world units | Loader tile state/ADTBuilder | Finalizer | Value copy | One call |
|
||||
| Input | Valid tile root | StreamingWorldLoader | Finalizer | Caller/SceneTree-owned | Must outlive call and attached subtree |
|
||||
| Input | Optional edited scene root | Loader Editor policy | Finalizer | SceneTree-owned borrowed Node | One call |
|
||||
| Output | Attached `Water` Node3D or null | Finalizer | Loader/SceneTree | Tile root owns attached Node | Tile subtree lifetime |
|
||||
|
||||
Side effects are limited to ADTBuilder mesh/material creation, `add_child` and
|
||||
optional recursive `Node.owner` assignment. No cache, filesystem, task or tile
|
||||
state is mutated.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Input[water data + Godot origin + tile root] --> Guard{Data and root valid?}
|
||||
Guard -->|no| None[Return null; unchanged]
|
||||
Guard -->|yes| Build[ADTBuilder.build_tile_water_scene]
|
||||
Build --> Dry{Water root exists?}
|
||||
Dry -->|no| None
|
||||
Dry -->|yes| Attach[tile root add_child]
|
||||
Attach --> Owner{Editor owner supplied?}
|
||||
Owner -->|yes| Assign[Assign owner recursively]
|
||||
Owner -->|no| Return[Return borrowed root]
|
||||
Assign --> Return
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
The service has no retained lifecycle. Each call moves one temporary builder
|
||||
result through the following call-local states.
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Validating
|
||||
Validating --> Rejected: empty data or invalid root
|
||||
Validating --> Building: valid input
|
||||
Building --> Dry: no geometry
|
||||
Building --> Attached: Water root built
|
||||
Attached --> EditorOwned: owner supplied
|
||||
Attached --> Complete: no owner
|
||||
EditorOwned --> Complete
|
||||
Rejected --> [*]
|
||||
Dry --> [*]
|
||||
Complete --> [*]
|
||||
```
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader main thread
|
||||
participant Finalizer as AdtWaterSceneFinalizer
|
||||
participant Builder as ADTBuilder
|
||||
participant Tile as Tile root
|
||||
Loader->>Finalizer: attach_water_scene(data, origin, tile, editor owner)
|
||||
Finalizer->>Builder: build_tile_water_scene(data, origin)
|
||||
Builder-->>Finalizer: Water root or null
|
||||
alt Water root exists
|
||||
Finalizer->>Tile: add_child(Water)
|
||||
opt editor owner supplied
|
||||
Finalizer->>Finalizer: assign owner recursively
|
||||
end
|
||||
Finalizer-->>Loader: borrowed Water root
|
||||
else dry/invalid
|
||||
Finalizer-->>Loader: null
|
||||
end
|
||||
Loader->>Loader: preserve existing water_loaded timing
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Finalizer[AdtWaterSceneFinalizer]
|
||||
Finalizer --> Builder[ADTBuilder]
|
||||
Builder --> Material[WowLiquidMaterial]
|
||||
Finalizer --> Scene[Godot SceneTree]
|
||||
Pipeline[AdtWaterLoadPipelineState] --> Loader
|
||||
Scheduler[RenderBudgetScheduler] --> Loader
|
||||
Finalizer -. no dependency .-> Pipeline
|
||||
Finalizer -. no dependency .-> Scheduler
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- The method is main-thread only because it creates Mesh/Material/Node objects
|
||||
and mutates the SceneTree.
|
||||
- `ADTBuilder` owns temporary construction. It frees an empty `Water` root and
|
||||
returns null for dry/invalid liquid geometry.
|
||||
- After `add_child`, the caller's tile subtree owns the returned Water root and
|
||||
all MeshInstance children; the finalizer retains no references.
|
||||
- Optional editor ownership affects serialization ownership only and does not
|
||||
replace SceneTree lifetime ownership.
|
||||
- Parsed Dictionaries are borrowed and not mutated.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Empty parsed data | Input guard | Return null, no builder call | Contract verifier | Loader preserves existing loaded timing |
|
||||
| Missing/freed tile root | Input guard | Return null, no scene mutation | Contract verifier | Stale-result validation normally rejects earlier |
|
||||
| Dry or malformed liquid arrays | ADTBuilder returns null | No attachment | Builder/shutdown regression | Reload after corrected source |
|
||||
| Tile/path stale | Loader before call | Finalizer is not invoked | Existing loader behavior | Current tile may enqueue again |
|
||||
| Shutdown/cancellation | Loader drains/cancels before call | No retained finalizer work | Shutdown verifier | New loader starts stateless |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| `enable_water` | Loader setting | Quality/custom | Yes | Gates both caller paths; not owned here |
|
||||
| `water_finalize_ops_per_tick` | `1` | Quality/custom | Yes | Budgets async calls before finalizer |
|
||||
| `editor_persist_generated_nodes` | `false` | Editor only | Yes | Supplies or omits editor owner |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
The service writes no files and changes no cache/resource schema. Existing ADT,
|
||||
terrain cache and liquid material versions remain valid; no rebuild or migration
|
||||
is required. Persisted Editor ownership follows the loader's existing opt-in.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- Logs: none; existing loader/builder diagnostics remain authoritative.
|
||||
- Metrics: no independent counters; existing water queue/finalize metrics remain.
|
||||
- Debug views: generated `Water/Liquid_<id>` names remain unchanged.
|
||||
- Correlation IDs: tile/path correlation stays in loader pipeline state.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_adt_water_scene_finalizer.gd`: empty/invalid/dry guards, one-cell
|
||||
geometry, attachment, recursive owner, loader delegation and bounded timing.
|
||||
- `verify_adt_water_load_pipeline_state.gd`: adjacent async ownership and loaded timing.
|
||||
- Renderer material and shutdown verifiers cover material construction and dry-root cleanup.
|
||||
- Fidelity evidence is the unchanged ADTBuilder implementation and unchanged two
|
||||
loader call positions; no new original-client liquid parity claim is made.
|
||||
- Performance budget: 100 empty calls under one second.
|
||||
|
||||
## Extension points
|
||||
|
||||
- A future liquid renderer may replace the internal builder only after paired
|
||||
MH2O/MCLQ fixtures establish equivalent geometry/material behavior.
|
||||
- Removal/eviction remains ordinary tile subtree ownership and needs no parallel API.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| ADT water scene finalization seam | Implemented extraction | Synthetic geometry/attach/owner/source verifier | Asset-backed traversal and hitch metrics pending |
|
||||
| ADT liquid geometry/materials | Existing implementation | ADTBuilder/material/checkpoint evidence | MH2O/MCLQ and original-client parity incomplete |
|
||||
| Async water scheduling | Existing extracted state | Water pipeline verifier | Active worker interruption remains loader-owned |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- The service intentionally wraps the existing Dictionary-based ADTBuilder boundary.
|
||||
- It does not verify that `editor_owner` is an ancestor; the loader supplies the
|
||||
edited scene root under the existing policy.
|
||||
- Mesh/material creation remains synchronous main-thread work behind the existing permit.
|
||||
- No proprietary liquid corpus, long traversal leak run, p95/p99 or paired capture is included.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/liquid/adt_water_scene_finalizer.gd` | Main-thread build, attach and optional recursive editor ownership |
|
||||
| `addons/mpq_extractor/loaders/adt_builder.gd` | Existing ADT liquid geometry/material creation and dry-root cleanup |
|
||||
| `src/render/liquid/adt_water_load_pipeline_state.gd` | Pending/task/result state before finalization |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Permits, stale checks, tile state and finalizer calls |
|
||||
| `src/tools/verify_adt_water_scene_finalizer.gd` | Synthetic scene/ownership/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`adt-water-load-pipeline-state.md`](adt-water-load-pipeline-state.md)
|
||||
- [`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`.
|
||||
@@ -116,9 +116,12 @@ flowchart LR
|
||||
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]
|
||||
@@ -249,6 +252,7 @@ raw three-number arrays are not an accepted new persistence contract.
|
||||
| `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 |
|
||||
|
||||
@@ -5,17 +5,18 @@
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target/work package | M02 / M02-GMP-MOVEMENT-001, M02-RND-CAMERA-001 basis producer update |
|
||||
| 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 | `8c1cf9b`, 2026-07-14 |
|
||||
| Profiles/capabilities | Current render sandbox; production/debug profile gate pending |
|
||||
| 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 without
|
||||
querying terrain, cameras, input devices or visual assets.
|
||||
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
|
||||
|
||||
@@ -32,13 +33,16 @@ 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 animation adapter]
|
||||
Velocity --> Presentation[Visual facing and CharacterAnimationPresenter adapter]
|
||||
```
|
||||
|
||||
Allowed dependencies:
|
||||
@@ -57,11 +61,16 @@ Forbidden dependencies:
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `LocalPlayerMovementController.new(...)` | Constructor | Sets explicit speeds, acceleration and sandbox sprint multiplier | Created by composition root; retained for player scene lifetime | Caller owns configuration validation |
|
||||
| `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 |
|
||||
| `toggle_sandbox_flight()` | Command | Toggles flight and clears velocity | Owning physics/input thread | Returns new flight state |
|
||||
| `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:
|
||||
|
||||
@@ -73,17 +82,20 @@ Constructor units:
|
||||
| `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 | Velocity `Vector3` | Movement controller | Facing/animation adapter | Read-only value copy | Until next update |
|
||||
| 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:
|
||||
@@ -97,9 +109,14 @@ Side effects:
|
||||
```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]
|
||||
@@ -112,7 +129,8 @@ flowchart LR
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> GroundedSandbox
|
||||
GroundedSandbox --> FlyingSandbox: toggle / clear velocity
|
||||
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
|
||||
@@ -128,8 +146,14 @@ sequenceDiagram
|
||||
participant Input as PlayerInputSource
|
||||
participant Scene as ThirdPersonWowController
|
||||
participant Move as LocalPlayerMovementController
|
||||
participant Present as Facing/Animation adapter
|
||||
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
|
||||
@@ -140,6 +164,8 @@ sequenceDiagram
|
||||
## 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.
|
||||
@@ -153,6 +179,8 @@ sequenceDiagram
|
||||
| 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
|
||||
@@ -169,6 +197,10 @@ controller resets velocity and flight state deterministically.
|
||||
| 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
|
||||
|
||||
@@ -187,13 +219,17 @@ separate versioned movement snapshot contract.
|
||||
|
||||
- Unit/contract: `src/tools/verify_local_player_movement.gd` covers forward,
|
||||
backward, strafe, rotated basis, acceleration, deceleration, sprint, flight,
|
||||
pitched camera basis, state reset and invalid delta.
|
||||
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: default values and formulas are regression-locked against
|
||||
the pre-extraction sandbox. No original-client comparison is claimed.
|
||||
- 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.
|
||||
|
||||
@@ -202,7 +238,8 @@ separate versioned movement snapshot contract.
|
||||
- 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.
|
||||
- Sandbox debug capability gating can reject sprint/flight before commands reach this controller.
|
||||
- A future application profile can map to this narrow capability value without
|
||||
coupling the movement controller to the application shell.
|
||||
|
||||
## Capability status
|
||||
|
||||
@@ -210,7 +247,9 @@ separate versioned movement snapshot contract.
|
||||
|---|---|---|---|
|
||||
| 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 |
|
||||
| Existing camera-relative free flight | Implemented | Pitched-basis and toggle/reset cases | Restrict to sandbox profile |
|
||||
| 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 |
|
||||
@@ -218,12 +257,15 @@ separate versioned movement snapshot contract.
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Numeric defaults preserve the existing sandbox but are not original-client evidence.
|
||||
- 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`.
|
||||
- Sprint and free flight still lack a typed sandbox capability gate.
|
||||
- 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.
|
||||
|
||||
@@ -232,12 +274,16 @@ separate versioned movement snapshot contract.
|
||||
| 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/player/third_person_wow_controller.gd` | Composition, basis selection, displacement/terrain/presentation adapters |
|
||||
| `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
|
||||
|
||||
|
||||
@@ -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,241 @@
|
||||
# M2 Mesh Load Pipeline State
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-MESH-LOAD-PIPELINE-001` |
|
||||
| Owners | Static M2 threaded request records and terminal finalize FIFO |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-mesh-load-pipeline`, 2026-07-17 |
|
||||
| Profiles/capabilities | Existing static M2 cached `.tscn`/`.glb` load path |
|
||||
|
||||
## Purpose
|
||||
|
||||
Own cross-frame bookkeeping between successful static M2 ResourceLoader request
|
||||
start, terminal loaded/failed polling and budgeted main-thread finalization. The
|
||||
loader retains every I/O and engine-resource operation.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Call ResourceLoader or select cache paths/formats.
|
||||
- Own M2 scene, missing or material-refresh caches, or prepared Mesh references.
|
||||
- Extract Meshes from PackedScene/GLB Resources.
|
||||
- Consume finalize permits or rebuild/adopt runtime Meshes.
|
||||
- Merge static and animated M2 pipelines.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Need[Static M2 mesh needed] --> Loader[StreamingWorldLoader]
|
||||
Loader --> IO[ResourceLoader request]
|
||||
Loader --> State[M2MeshLoadPipelineState]
|
||||
State --> Poll[Detached pending records]
|
||||
Poll --> Loader
|
||||
Loader --> IO
|
||||
Loader -->|terminal status| State
|
||||
State --> FIFO[Finalize FIFO]
|
||||
FIFO --> Loader
|
||||
Loader --> Budget[M2_MESH_FINALIZE permit]
|
||||
Loader --> Cache[M2 Mesh resource cache or M2 prototype outcome state]
|
||||
```
|
||||
|
||||
Allowed dependencies are value containers, Strings and opaque integer statuses.
|
||||
ResourceLoader, filesystem, WorkerThreadPool, Mesh/Resource/Node/RID ownership,
|
||||
scheduler and other application layers are forbidden.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `remember_request(normalized_relative_path, resource_path)` | Command/query | Insert one pending request | Renderer main thread; until terminal/discard/clear | Empty/duplicate rejected |
|
||||
| `has_request(path)` | Query | Test pending dedupe | Main thread | Empty/unknown false |
|
||||
| `request_records_snapshot()` | Query | Copy pending records in insertion order | Main thread; caller-owned | None |
|
||||
| `complete_request(path, terminal_status)` | Command/query | Move request to terminal FIFO | Main thread after poll | Unknown rejected |
|
||||
| `discard_request(path)` | Command/query | Remove request without finalization | Main thread | Unknown false |
|
||||
| `has_finalize_record()` / `pop_finalize_record()` | Query/command | Observe/pop completion-order FIFO | Main-thread budget drain | Empty pop returns `{}` |
|
||||
| `total_work_count()` | Query | Preserve pending-plus-finalize metric | Main thread | None |
|
||||
| `pending_request_count()` / `finalize_record_count()` | Query | Stage diagnostics | Main thread | None |
|
||||
| `clear()` | Command | Drop bookkeeping after I/O drain/reset | Main thread | Idempotent; does not drain I/O |
|
||||
| `diagnostic_snapshot()` | Query | Detached request/finalize records | Main thread | No Resources exposed |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Normalized M2 path and cache Resource path | Loader request adapter | Pending map | Copied Strings | Until terminal/discard/clear |
|
||||
| Input | Opaque terminal ResourceLoader status | Loader poll adapter | Finalize record | Integer value | Until pop/clear |
|
||||
| Output | Detached pending records | Pipeline state | Loader poll/shutdown adapter | Caller-owned copies | One poll/wait pass |
|
||||
| Output | Oldest terminal record | Pipeline state | Loader finalizer | Record ownership transferred | One finalize attempt |
|
||||
| Output | Detached diagnostics | Pipeline state | Verifier/future metrics | Caller-owned copies | Snapshot lifetime |
|
||||
|
||||
Side effects are limited to collection mutation and retaining String/integer values.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start[Successful ResourceLoader request] --> Remember[remember request]
|
||||
Remember --> Snapshot[request records snapshot]
|
||||
Snapshot --> Poll[Loader polls status]
|
||||
Poll --> Active{In progress?}
|
||||
Active -->|yes| Snapshot
|
||||
Active -->|no| Complete[complete request with terminal status]
|
||||
Complete --> FIFO[Append finalize FIFO]
|
||||
FIFO --> Permit{Loader permit available?}
|
||||
Permit -->|no| FIFO
|
||||
Permit -->|yes| Pop[Pop oldest terminal record]
|
||||
Pop --> Finalize[Loader gets Resource and delegates first-Mesh extraction]
|
||||
Finalize --> Prepare[M2RuntimeMeshFinalizer prepares Mesh]
|
||||
Prepare --> Adopt[Loader adopts Mesh or marks prototype outcome state]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Absent
|
||||
Absent --> Pending: remember request
|
||||
Pending --> Pending: non-terminal poll
|
||||
Pending --> TerminalQueued: complete request
|
||||
Pending --> Absent: discard
|
||||
TerminalQueued --> Absent: pop finalize
|
||||
Pending --> Absent: clear
|
||||
TerminalQueued --> Absent: clear
|
||||
```
|
||||
|
||||
One path has at most one pending request. After completion it may be requested
|
||||
again only if loader cache/missing rules permit it.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Resource as ResourceLoader
|
||||
participant State as M2MeshLoadPipelineState
|
||||
participant Budget as RenderBudgetScheduler
|
||||
Loader->>Resource: load_threaded_request(cache path)
|
||||
Resource-->>Loader: OK or ERR_BUSY
|
||||
Loader->>State: remember_request(normalized, cache path)
|
||||
loop frames
|
||||
Loader->>State: request_records_snapshot()
|
||||
Loader->>Resource: load_threaded_get_status(path)
|
||||
end
|
||||
Loader->>State: complete_request(normalized, terminal status)
|
||||
Loader->>Budget: try_consume_permit(M2_MESH_FINALIZE)
|
||||
Loader->>State: pop_finalize_record()
|
||||
Loader->>Resource: load_threaded_get(path)
|
||||
Loader->>Loader: extract/refresh/adopt Mesh or mark missing
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> State[M2MeshLoadPipelineState]
|
||||
Loader --> Resource[ResourceLoader]
|
||||
Loader --> Budget[RenderBudgetScheduler]
|
||||
Loader --> MeshCache[M2 Mesh resource and prototype outcome cache states]
|
||||
Loader --> Finalizer[M2RuntimeMeshFinalizer]
|
||||
State -. no dependency .-> Resource
|
||||
State -. no dependency .-> Budget
|
||||
State -. no dependency .-> MeshCache
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Main thread serializes all state mutation and snapshots.
|
||||
- State owns only request/finalize Dictionaries containing Strings and status integers.
|
||||
- Loader owns ResourceLoader request lifetime and drains active paths before shutdown clear.
|
||||
- `M2MeshResourceCacheState` owns prepared static Mesh references and
|
||||
`M2RuntimeMeshFinalizer` owns refresh/rebuild/fallback. The loader owns shared
|
||||
adoption decisions, raw I/O and remaining engine resources; the prototype
|
||||
cache service owns missing outcomes.
|
||||
- Snapshot and diagnostic records are detached; caller mutation cannot affect state.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Empty/duplicate request | State guard | Reject unchanged | Contract verifier | Correct caller/request later |
|
||||
| Request start failure | Loader return code | No state insert; mark missing | Existing loader behavior | Cache/source correction |
|
||||
| Non-terminal status | Loader poll | Keep pending | Existing queue metric | Poll next frame |
|
||||
| Terminal load failure | Status in popped record | Loader marks missing | Existing missing behavior | World/cache reload |
|
||||
| Empty defensive path | Loader before poll | Discard and mark missing | Source regression | Correct request producer |
|
||||
| Shutdown | Loader drains pending Resource paths | Clear state | Shutdown verifier | New loader starts empty |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| `m2_mesh_finalize_ops_per_tick` | `1` | Quality/custom | Yes | Bounds terminal FIFO pops outside state |
|
||||
| Cache extension order | `.tscn`, `.glb` | Existing M2 path | No | Loader selects request path |
|
||||
| Animated pivot-prefix GLB exclusion | Existing rule | All | No | Loader filters before request |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No pipeline state is serialized. M2 cache formats, material refresh version and
|
||||
rebuild policy are unchanged; no migration or rebake is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- `total_work_count` preserves all three historical `m2_mesh` metrics.
|
||||
- Diagnostic snapshots expose only paths/statuses, never loaded Resources or Meshes.
|
||||
- Existing loader logs and hitch sections remain unchanged.
|
||||
- Normalized M2 path is the correlation key.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_mesh_load_pipeline_state.gd`: validation, dedupe, insertion-order
|
||||
polling, terminal transition, completion FIFO, opaque status, discard, metrics,
|
||||
detached diagnostics, loader ownership and 100-by-256 timing.
|
||||
- Classifier/material/shutdown/M2 placement/build regressions cover adjacent behavior.
|
||||
- Fidelity evidence is exact state/lifecycle extraction; no asset-backed or
|
||||
original-client visual parity claim is made.
|
||||
- Performance budget: 25,600 request/complete/pop transitions under one second.
|
||||
|
||||
## Extension points
|
||||
|
||||
- Mesh/missing cache adoption can move to a separate finalizer without changing this state.
|
||||
- Animated M2 has distinct candidate/static fallback semantics and remains separate.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Static M2 request/finalize state | Implemented extraction | Contract/source/timing verifier | Asset-backed long traversal pending |
|
||||
| ResourceLoader I/O | Existing loader-owned | Shutdown/material regressions | I/O adapter extraction optional |
|
||||
| Mesh cache | Implemented extraction | Mesh resource cache state verifier | Asset-backed memory/leak run pending |
|
||||
| First-Mesh extraction | Implemented extraction | Resource/order/lifetime verifier | Asset-backed corrupt-scene fixture pending |
|
||||
| Mesh preparation | Implemented extraction | Runtime finalizer transition/rebuild verifier | Asset-backed material comparison pending |
|
||||
| Mesh materialization | Existing loader-owned | Renderer/material tests | Further safe extraction pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Records remain Dictionaries because ResourceLoader polling is a dynamic Godot boundary.
|
||||
- Clear does not drain ResourceLoader; caller ordering is mandatory.
|
||||
- Status integers are intentionally opaque inside state.
|
||||
- No private asset traversal, leak/descriptor-pressure or p95/p99 run is included.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_mesh_load_pipeline_state.gd` | Pending records, terminal FIFO and metrics |
|
||||
| `src/render/m2/m2_mesh_resource_cache_state.gd` | Prepared static Mesh references and final clear |
|
||||
| `src/render/m2/m2_mesh_resource_extractor.gd` | First-Mesh selection and temporary PackedScene lifetime |
|
||||
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Refresh/rebuild/fallback preparation |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Missing-model outcome retention |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Cache path choice, I/O polling, permits and adoption decisions |
|
||||
| `src/render/m2/m2_runtime_mesh_rebuild_classifier.gd` | Downstream stale Mesh rebuild decision |
|
||||
| `src/tools/verify_m2_mesh_load_pipeline_state.gd` | Lifecycle/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-runtime-mesh-rebuild-classifier.md`](m2-runtime-mesh-rebuild-classifier.md)
|
||||
- [`m2-build-batch-planner.md`](m2-build-batch-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,216 @@
|
||||
# M2 Mesh Resource Cache State
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-MESH-RESOURCE-CACHE-001` |
|
||||
| Owners | Prepared static M2 Mesh resource references by normalized path |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-mesh-resource-cache`, 2026-07-17 |
|
||||
| Profiles/capabilities | Existing static M2 cached and raw fallback paths |
|
||||
|
||||
## Purpose
|
||||
|
||||
Own the runtime cache of prepared static M2 `Mesh` references outside
|
||||
`StreamingWorldLoader`. Dedicated services extract and prepare each Mesh; the
|
||||
loader remains responsible for finding, loading, adopting and materializing it.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Own the shared M2 missing-path, prototype or animated-scene caches.
|
||||
- Call ResourceLoader/FileAccess or select `.tscn`/`.glb` paths.
|
||||
- Extract a Mesh from a Resource or Node.
|
||||
- Refresh materials, rebuild Meshes or create MultiMesh instances.
|
||||
- Add eviction, persistence, cache versions or quality-profile behavior.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Need[Static M2 mesh lookup] --> Loader[StreamingWorldLoader]
|
||||
Loader --> Cache[M2MeshResourceCacheState]
|
||||
Cache -->|cached Mesh or null| Loader
|
||||
Loader --> Pipeline[M2MeshLoadPipelineState]
|
||||
Pipeline --> IO[ResourceLoader]
|
||||
IO --> Extract[M2MeshResourceExtractor]
|
||||
Extract --> Prepare[M2RuntimeMeshFinalizer]
|
||||
Prepare --> Cache
|
||||
Loader --> Build[M2 MultiMesh materialization]
|
||||
```
|
||||
|
||||
The cache may retain `Mesh` resources and copied normalized-path Strings. It has
|
||||
no Node, ResourceLoader, filesystem, worker, scheduler, missing-state, builder,
|
||||
gameplay, network or editor dependency.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `store_mesh(path, mesh)` | Command/query | Retain or replace one prepared Mesh | Renderer main thread; final shutdown | Empty path/null Mesh rejected |
|
||||
| `has_mesh(path)` | Query | Test whether a Mesh is retained | Renderer main thread | Empty/unknown false |
|
||||
| `find_mesh(path)` | Query | Borrow exact retained Mesh reference | Renderer main thread | Empty/unknown null |
|
||||
| `mesh_count()` | Query | Return retained entry count | Diagnostics/tests | None |
|
||||
| `normalized_paths_snapshot()` | Query | Copy insertion-order path keys | Diagnostics/tests | Never exposes Mesh references |
|
||||
| `clear()` | Command | Release every retained Mesh reference | Final shutdown after async drain | Idempotent |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Normalized relative M2 path | Loader normalization adapter | Cache key | Copied String | Cache lifetime |
|
||||
| Input | Prepared static Mesh | Loader finalizer or synchronous fallback | Cache value | Strong reference retained | Until replacement/clear |
|
||||
| Output | Borrowed exact Mesh | Cache lookup | Static M2 batch builder | Cache retains reference | One build/materialization pass |
|
||||
| Output | Detached normalized paths | Cache diagnostics | Verifier/future metrics | Caller-owned Strings | Snapshot lifetime |
|
||||
|
||||
Store and clear mutate only the cache Dictionary and Resource reference counts.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Lookup[Lookup normalized M2 path] --> Cached{Mesh cached?}
|
||||
Cached -->|yes| Return[Return retained Mesh]
|
||||
Cached -->|no| Missing{Prototype outcome says missing?}
|
||||
Missing -->|yes| Null[Return null]
|
||||
Missing -->|no| Load[Loader request or synchronous fallback]
|
||||
Load --> Prepare[Extractor and runtime finalizer prepare Mesh]
|
||||
Prepare --> Valid{Prepared Mesh valid?}
|
||||
Valid -->|yes| Store[Store or replace cache entry]
|
||||
Valid -->|no| MarkMissing[Loader updates prototype outcome state]
|
||||
Store --> Return
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Absent
|
||||
Absent --> Cached: store valid Mesh
|
||||
Cached --> Cached: replace Mesh reference
|
||||
Cached --> Absent: final shutdown clear
|
||||
```
|
||||
|
||||
Map/world refresh retains cached Meshes. Only the existing final runtime-cache
|
||||
release clears them after asynchronous shutdown drain.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Cache as M2MeshResourceCacheState
|
||||
participant Pipeline as M2MeshLoadPipelineState
|
||||
Loader->>Cache: find/has normalized path
|
||||
alt cache miss
|
||||
Loader->>Pipeline: request/poll/finalize record
|
||||
Loader->>Loader: ResourceLoader get; delegate extract + prepare
|
||||
Loader->>Cache: store_mesh(path, prepared Mesh)
|
||||
end
|
||||
Cache-->>Loader: exact retained Mesh
|
||||
Loader->>Loader: materialize static M2 batch
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Cache[M2MeshResourceCacheState]
|
||||
Loader --> Pipeline[M2MeshLoadPipelineState]
|
||||
Loader --> Finalizer[M2RuntimeMeshFinalizer]
|
||||
Loader --> Builder[M2Builder and MultiMesh]
|
||||
Cache --> Mesh[Godot Mesh Resource]
|
||||
Cache -. no dependency .-> Pipeline
|
||||
Cache -. no dependency .-> Classifier
|
||||
Cache -. no dependency .-> Builder
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Renderer main-thread adapters serialize every cache operation.
|
||||
- The cache owns normalized-path Strings and strong Mesh references.
|
||||
- Borrowed Mesh lookups do not transfer ownership or duplicate resources.
|
||||
- `M2MeshResourceExtractor` owns first-Mesh selection and temporary PackedScene
|
||||
instances. `M2PrototypeCacheState` owns missing/prototype/animated state; the
|
||||
loader owns MultiMeshes and adoption decisions.
|
||||
- The loader drains asynchronous work before the final cache clear.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Empty path or null Mesh | Cache guard | Reject unchanged | Contract verifier | Correct caller input |
|
||||
| Unknown lookup | Membership query | Return null | Existing loader path | Request/load or missing decision |
|
||||
| Replacement | Existing key | Retain new exact reference | Contract verifier | Normal subsequent lookup |
|
||||
| Load/preparation failure | Loader boundary | Do not store; loader marks missing | Existing renderer behavior | Cache/source correction and reload |
|
||||
| Final shutdown | Loader lifecycle | Clear releases references | Shutdown/source verifier | New loader starts empty |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The module adds no setting, budget or profile branch. Cache path order, finalize
|
||||
permits and batching remain loader-owned; refresh version `2` belongs to
|
||||
`M2RuntimeMeshFinalizer`.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
The state is runtime-only and serializes nothing. Existing `.tscn`/`.glb`
|
||||
formats, import caches and material refresh metadata are unchanged; no migration
|
||||
or rebake is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
`mesh_count` and `normalized_paths_snapshot` expose scalar ownership diagnostics
|
||||
without Mesh references. Existing renderer metrics/logging are unchanged because
|
||||
the historical Mesh cache had no queue contribution or log site.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_mesh_resource_cache_state.gd`: invalid input, exact reference,
|
||||
replacement, lookup, detached path snapshot, idempotent clear, loader source
|
||||
ownership and 100-by-256 bounded timing.
|
||||
- Mesh pipeline/classifier/material/shutdown/M2 build/facade regressions cover
|
||||
adjacent behavior.
|
||||
- Fidelity evidence is exact cache/lifetime extraction; no original-client
|
||||
visual-parity or asset-backed performance claim is made.
|
||||
|
||||
## Extension points
|
||||
|
||||
- `M2RuntimeMeshFinalizer` returns a prepared Mesh for `store_mesh` without
|
||||
changing cache ownership.
|
||||
- Eviction requires measured memory pressure and explicit lifetime semantics.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Prepared static M2 Mesh cache | Implemented extraction | Contract/source/timing verifier | Asset-backed memory/leak run pending |
|
||||
| M2 Mesh request lifecycle | Implemented extraction | Pipeline state verifier | ResourceLoader I/O remains loader-owned |
|
||||
| M2 Mesh extraction | Implemented extraction | Resource/order/lifetime verifier | Asset-backed corrupt-scene fixture pending |
|
||||
| M2 Mesh preparation | Implemented extraction | Runtime finalizer transition/rebuild verifier | Asset-backed material comparison pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Strong Mesh references intentionally remain until final shutdown.
|
||||
- Missing/prototype/animated lookup state is now a separate sibling service.
|
||||
- No proprietary M2 traversal, leak/descriptor-pressure, p95/p99 or paired
|
||||
original-client capture is included.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_mesh_resource_cache_state.gd` | Prepared Mesh references and final clear |
|
||||
| `src/render/m2/m2_mesh_resource_extractor.gd` | First-Mesh selection and temporary PackedScene lifetime |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Prototype references and negative lookup outcomes |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Normalization, raw/resource I/O and materialization |
|
||||
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Refresh/rebuild/fallback preparation |
|
||||
| `src/render/m2/m2_mesh_load_pipeline_state.gd` | Pending request and terminal finalize records |
|
||||
| `src/tools/verify_m2_mesh_resource_cache_state.gd` | Cache ownership/lifetime/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.md)
|
||||
- [`m2-runtime-mesh-rebuild-classifier.md`](m2-runtime-mesh-rebuild-classifier.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,210 @@
|
||||
# M2 Mesh Resource Extractor
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-MESH-RESOURCE-EXTRACTOR-001` |
|
||||
| Owners | First-Mesh selection and temporary PackedScene instance lifetime |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-mesh-resource-extractor`, 2026-07-17 |
|
||||
| Profiles/capabilities | Static M2 threaded cache and synchronous raw/prototype fallback paths |
|
||||
|
||||
## Purpose
|
||||
|
||||
Select the first M2 Mesh from a direct Resource, PackedScene or existing Node
|
||||
subtree while keeping traversal and temporary-node lifetime outside
|
||||
`StreamingWorldLoader`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Request/load Resources or choose cache paths.
|
||||
- Load raw M2 data, refresh materials or rebuild Meshes.
|
||||
- Own Mesh/prototype/missing caches or pending/finalize queues.
|
||||
- Create MultiMeshes, attach Nodes or consume render permits.
|
||||
- Change first-Mesh selection order or visible output.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Pipeline[M2 load pipeline] --> Loader[StreamingWorldLoader]
|
||||
Loader --> Resource[Loaded Mesh or PackedScene Resource]
|
||||
Resource --> Extractor[M2MeshResourceExtractor]
|
||||
Extractor -->|first Mesh or null| Loader
|
||||
Loader --> Prepare[M2RuntimeMeshFinalizer]
|
||||
Prepare --> Cache[M2MeshResourceCacheState]
|
||||
Prototype[Existing M2 Node subtree] --> Extractor
|
||||
```
|
||||
|
||||
The extractor may inspect `Resource`, `PackedScene`, `Node`, `MeshInstance3D`
|
||||
and `Mesh` engine types. It has no ResourceLoader, filesystem, worker, cache,
|
||||
scheduler, builder, gameplay, network or editor dependency.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `extract_first_mesh(resource)` | Query with temporary ownership | Return direct Mesh or first Mesh from one PackedScene instance | Renderer main thread; temporary root freed before return | Null/unsupported/empty returns null |
|
||||
| `find_first_mesh_in_subtree(root)` | Query | Return first Mesh in historical depth-first preorder | Renderer main thread; caller-owned subtree unchanged | Null/no Mesh returns null |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Direct Mesh Resource | Loader threaded finalizer | Extractor | Borrowed Resource | One call |
|
||||
| Input | PackedScene Resource | Loader threaded finalizer | Extractor | Borrowed scene; extractor owns temporary instance | Temporary root until return |
|
||||
| Input | Existing prototype/rebuild Node subtree | Loader builder/fallback | Extractor | Borrowed Node tree | One traversal |
|
||||
| Output | First Mesh reference | Extractor | Loader preparation/cache adapter | Borrowed exact reference | Cache may retain later |
|
||||
|
||||
The only side effect is synchronous creation and immediate `free()` of a
|
||||
temporary PackedScene instance.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Input[Resource or Node subtree] --> Direct{Direct Mesh?}
|
||||
Direct -->|yes| Return[Return exact Mesh]
|
||||
Direct -->|no| Packed{PackedScene?}
|
||||
Packed -->|yes| Instantiate[Instantiate temporary root]
|
||||
Packed -->|no| Traverse[Traverse caller-owned subtree]
|
||||
Instantiate --> TraverseTemp[Depth-first preorder traversal]
|
||||
TraverseTemp --> Free[Free temporary root]
|
||||
Free --> ReturnOrNull[Return first Mesh or null]
|
||||
Traverse --> ReturnOrNull
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Stateless
|
||||
Stateless --> TemporaryInstance: extract PackedScene
|
||||
TemporaryInstance --> Traversing: instantiate succeeded
|
||||
Traversing --> Stateless: free temporary root before return
|
||||
Stateless --> Stateless: direct Mesh/subtree/invalid input
|
||||
```
|
||||
|
||||
No state or Resource reference is retained between calls.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Extractor as M2MeshResourceExtractor
|
||||
participant Scene as PackedScene
|
||||
Loader->>Extractor: extract_first_mesh(loaded Resource)
|
||||
alt direct Mesh
|
||||
Extractor-->>Loader: same Mesh reference
|
||||
else PackedScene
|
||||
Extractor->>Scene: instantiate()
|
||||
Scene-->>Extractor: temporary root
|
||||
Extractor->>Extractor: depth-first first-Mesh search
|
||||
Extractor->>Extractor: temporary_root.free()
|
||||
Extractor-->>Loader: Mesh or null
|
||||
end
|
||||
Loader->>Loader: prepare and cache Mesh or mark missing
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Extractor[M2MeshResourceExtractor]
|
||||
Extractor --> Types[Resource / PackedScene / Node / Mesh]
|
||||
Loader --> Pipeline[M2MeshLoadPipelineState]
|
||||
Loader --> Cache[M2MeshResourceCacheState]
|
||||
Loader --> Finalizer[M2RuntimeMeshFinalizer]
|
||||
Extractor -. no dependency .-> Pipeline
|
||||
Extractor -. no dependency .-> Cache
|
||||
Extractor -. no dependency .-> Classifier
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- The renderer main thread performs all traversal and PackedScene instantiation.
|
||||
- Direct and subtree Mesh references are borrowed without duplication.
|
||||
- A PackedScene temporary root is extractor-owned and synchronously freed.
|
||||
- Existing prototype/rebuild subtrees remain caller-owned and are never mutated.
|
||||
- Cache retention remains a cache-state concern; runtime material preparation
|
||||
belongs to `M2RuntimeMeshFinalizer`.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Null/unsupported Resource | Type guard | Return null | Contract verifier | Loader marks missing/falls back |
|
||||
| PackedScene instantiate failure | Null root | Return null | Existing loader behavior | Rebuild cache/source |
|
||||
| No Mesh in subtree | Traversal exhaustion | Return null | Contract verifier | Loader marks missing/falls back |
|
||||
| Null MeshInstance Mesh | Null traversal result | Parent continues with later siblings | Order verifier | Normal sibling search |
|
||||
| Shutdown/cancel | No retained state | No action | Ownership docs | Caller owns surrounding job |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The module adds no settings or profile branches. Cache format order, material
|
||||
refresh version, render permits and batching remain unchanged outside it.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
The extractor is stateless and serializes nothing. Existing `.tscn`/`.glb`
|
||||
formats, cache versions and rebuild metadata are unchanged; no migration or
|
||||
rebake is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
The module emits no logs and retains no counters. Loader correlation remains the
|
||||
normalized M2 path. Its verifier measures traversal time and temporary Node count.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_mesh_resource_extractor.gd`: direct/invalid Resources, depth-first
|
||||
child order, null-Mesh sibling continuation, subtree ownership, PackedScene
|
||||
exact Mesh identity, temporary Node release, source boundaries and 10,000
|
||||
bounded traversals.
|
||||
- M2 pipeline/cache/classifier/material/shutdown/build regressions cover adjacent
|
||||
behavior. No asset-backed visual parity claim is made.
|
||||
|
||||
## Extension points
|
||||
|
||||
- `M2RuntimeMeshFinalizer` consumes the Mesh returned here and the loader stores
|
||||
its result in `M2MeshResourceCacheState`.
|
||||
- Broader generic scene traversal is intentionally excluded until another real
|
||||
consumer requires the same exact contract.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Direct/PackedScene first-Mesh extraction | Implemented extraction | Resource/order/lifetime verifier | Asset-backed corrupt-scene fixture pending |
|
||||
| Prototype/rebuild subtree selection | Implemented extraction | Synthetic preorder verifier | Asset-backed M2 traversal pending |
|
||||
| Material refresh/rebuild finalization | Implemented extraction | Runtime finalizer transition/rebuild verifier | Asset-backed material comparison pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- PackedScene instantiation remains synchronous main-thread work behind the
|
||||
existing finalize permit.
|
||||
- The first Mesh rule intentionally ignores additional MeshInstances.
|
||||
- No proprietary M2 corpus, corrupt cache, leak/descriptor-pressure, p95/p99 or
|
||||
paired original-client capture is included.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_mesh_resource_extractor.gd` | First-Mesh traversal and temporary PackedScene lifetime |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Prototype and missing/static-only lookup state |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | ResourceLoader/raw I/O, cache decisions and materialization |
|
||||
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Refresh/rebuild/fallback preparation |
|
||||
| `src/render/m2/m2_mesh_resource_cache_state.gd` | Prepared Mesh retention |
|
||||
| `src/tools/verify_m2_mesh_resource_extractor.gd` | Resource/order/lifetime/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.md)
|
||||
- [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.md)
|
||||
- [`m2-runtime-mesh-rebuild-classifier.md`](m2-runtime-mesh-rebuild-classifier.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,247 @@
|
||||
# M2 Prototype Cache State
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-PROTOTYPE-CACHE-001` |
|
||||
| Owners | Detached static/animated prototypes and missing/static-animation outcomes |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-prototype-cache`, 2026-07-17 |
|
||||
| Profiles/capabilities | Static, GLB animated and native animated M2 prototype reuse |
|
||||
|
||||
## Purpose
|
||||
|
||||
Own the four shutdown-lifetime M2 prototype lookup states previously stored in
|
||||
`StreamingWorldLoader`: static and animated `Node3D` references, missing model
|
||||
paths and paths whose animation fallback is static-only.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Normalize paths, choose cache files, call ResourceLoader or parse raw M2 data.
|
||||
- Build, repair or instantiate prototypes, Meshes, materials or MultiMeshes.
|
||||
- Own animation/Mesh request queues, permits or tile/build state.
|
||||
- Add eviction, persistence, retries, profile rules or format changes.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader] --> State[M2PrototypeCacheState]
|
||||
Resource[ResourceLoader / raw builders] --> Loader
|
||||
State --> Static[Static prototype Node3D]
|
||||
State --> Animated[Animated prototype Node3D]
|
||||
State --> Missing[Missing-model path set]
|
||||
State --> StaticOnly[Static-animation path set]
|
||||
Loader --> Instances[Duplicate/animated instance materialization]
|
||||
```
|
||||
|
||||
The state may depend on `Node3D` lifetime APIs and value-only path containers.
|
||||
Filesystem/native/ResourceLoader APIs, builders, worker pools, render scheduling,
|
||||
Mesh traversal and other application layers are forbidden.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `has_static_prototype(path)` | Query | Test for one valid retained static prototype | Renderer main thread; shutdown lifetime | Empty/invalid returns false |
|
||||
| `find_static_prototype(path)` | Query | Return exact retained static Node reference | Renderer main thread; borrowed | Empty/unknown/invalid returns null |
|
||||
| `adopt_static_prototype(path, prototype)` | Command/query | Retain first valid static prototype and return canonical reference | Renderer main thread; until shutdown | Invalid input returns null; later candidate released |
|
||||
| `has_animated_prototype(path)` | Query | Test for one valid retained animated prototype | Renderer main thread; shutdown lifetime | Empty/invalid returns false |
|
||||
| `find_animated_prototype(path)` | Query | Return exact retained animated Node reference | Renderer main thread; borrowed | Empty/unknown/invalid returns null |
|
||||
| `adopt_animated_prototype(path, prototype)` | Command/query | Retain first valid animated prototype and return canonical reference | Renderer main thread; until shutdown | Invalid input returns null; later candidate released |
|
||||
| `mark_model_missing(path)` / `is_model_missing(path)` | Command/query | Store/query a missing-model outcome | Renderer main thread; until shutdown | Empty mark rejected |
|
||||
| `mark_animation_static(path)` / `is_animation_static(path)` | Command/query | Store/query static-only animation outcome | Renderer main thread; until shutdown | Empty mark rejected |
|
||||
| `diagnostic_snapshot()` | Diagnostic query | Return detached sorted path arrays for all four states | Caller-owned result | Never exposes Nodes |
|
||||
| `clear_and_release()` | Lifecycle command | Release prototypes and clear all state | Final shutdown after async drain | Idempotent |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Normalized non-empty M2 path | Loader normalization | Cache state | Copied String key | Shutdown lifetime |
|
||||
| Input | Detached static Node3D | Cached scene/raw builder adapter | Cache state | Adopted on success | Until final shutdown |
|
||||
| Input | Detached animated Node3D | GLB/native animation adapter | Cache state | Adopted on success | Until final shutdown |
|
||||
| Input | Missing/static-only outcome | Loader failure/fallback adapter | Cache state | Boolean set entry | Until final shutdown |
|
||||
| Output | Canonical prototype Node3D | Cache state | Loader instance/material adapter | Borrowed exact reference | One lookup/use |
|
||||
| Output | Detached path-only snapshot | Cache state | Tests/diagnostics | Fresh caller-owned arrays | One query |
|
||||
|
||||
Side effects are strong Node retention, release of rejected duplicate candidates,
|
||||
final Node destruction/queueing and mutation of runtime-only path state.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Request[Normalized prototype request] --> Positive{Valid prototype cached?}
|
||||
Positive -->|yes| Return[Return exact prototype]
|
||||
Positive -->|no| Negative{Missing or static-only outcome?}
|
||||
Negative -->|yes| Fallback[Return caller fallback]
|
||||
Negative -->|no| Load[Loader performs existing I/O/build]
|
||||
Load --> Success{Valid prototype?}
|
||||
Success -->|yes| Adopt[Adopt first candidate]
|
||||
Adopt --> Return
|
||||
Success -->|no| Mark[Mark relevant negative outcome]
|
||||
Mark --> Fallback
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Empty
|
||||
Empty --> Positive: adopt static/animated prototype
|
||||
Empty --> Negative: mark missing/static-only path
|
||||
Positive --> Positive: lookup or same-reference adoption
|
||||
Positive --> Positive: duplicate candidate released; first wins
|
||||
Negative --> Negative: repeated mark/query
|
||||
Positive --> Released: final shutdown
|
||||
Negative --> Released: final shutdown
|
||||
Released --> Released: repeated clear
|
||||
Released --> [*]
|
||||
```
|
||||
|
||||
Map refresh does not transition or clear this service. Final scene shutdown calls
|
||||
`clear_and_release` only after worker and ResourceLoader work is drained.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant State as M2PrototypeCacheState
|
||||
participant Source as ResourceLoader/raw builder
|
||||
Loader->>State: find static/animated(path)
|
||||
alt positive hit
|
||||
State-->>Loader: exact retained Node3D
|
||||
else negative hit
|
||||
Loader->>State: is missing/static-only(path)
|
||||
State-->>Loader: true; use historical fallback
|
||||
else uncached
|
||||
Loader->>Source: existing load/build operation
|
||||
alt valid prototype
|
||||
Loader->>State: adopt(path, prototype)
|
||||
State-->>Loader: canonical Node3D
|
||||
else failure
|
||||
Loader->>State: mark missing/static-only(path)
|
||||
end
|
||||
end
|
||||
Loader->>State: shutdown clear_and_release()
|
||||
State-->>Loader: all paths empty; Nodes released
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> State[M2PrototypeCacheState]
|
||||
State --> Node3D
|
||||
Loader --> Raw[M2RawModelRepository]
|
||||
Loader --> ResourceLoader
|
||||
Loader --> StaticBuilder[M2Builder]
|
||||
Loader --> AnimatedBuilder[M2NativeAnimatedBuilder]
|
||||
State -. no dependency .-> Raw
|
||||
State -. no dependency .-> ResourceLoader
|
||||
State -. no dependency .-> StaticBuilder
|
||||
State -. no dependency .-> MeshCache[M2MeshResourceCacheState]
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Renderer main-thread adapters serialize all operations.
|
||||
- The state owns strong references to adopted static and animated prototype Nodes.
|
||||
- A duplicate detached candidate is released immediately; the first valid Node
|
||||
stays canonical for that path.
|
||||
- Lookups borrow exact Node references without transferring ownership.
|
||||
- Detached Nodes are synchronously `free()`d at shutdown. Defensive in-tree
|
||||
Nodes use `queue_free()` to preserve SceneTree rules.
|
||||
- Negative entries own only copied normalized-path Strings.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Empty path/null/invalid Node | Admission guard | Reject/null/false | Dedicated verifier | Correct caller input |
|
||||
| Unknown positive lookup | Map miss | Return null | Path snapshot | Loader continues existing load/fallback |
|
||||
| Duplicate candidate | Occupied valid path | Release candidate; return first | Identity fixture | None required |
|
||||
| Model source/load failure | Loader result | Mark missing outcome | Existing fallback behavior | New loader session/source repair |
|
||||
| Animation unavailable/unsafe | Loader policy/result | Mark static-only outcome | Existing fallback behavior | New loader session/cache repair |
|
||||
| Shutdown | Loader lifecycle | Release positive and clear all state | Shutdown verifier | New loader begins empty |
|
||||
| Cancellation | Not owned | No state transition inside this service | N/A | Loader drains/cancels before shutdown clear |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| Positive cache lifetime | Final loader shutdown | All | No | Reuses detached prototypes across map refresh |
|
||||
| Negative cache lifetime | Final loader shutdown | All | No | Preserves historical fallback suppression |
|
||||
| Eviction capacity | Unbounded historical behavior | All | No | No mid-session Node destruction |
|
||||
|
||||
Animation enablement, candidate/allow/deny rules, cache paths and per-frame
|
||||
permits remain loader configuration.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
This is runtime-only state and serializes nothing. `.tscn/.glb`, material refresh
|
||||
and native M2 formats are unchanged; no migration or rebake is introduced.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- `diagnostic_snapshot` exposes four sorted path arrays without Node references.
|
||||
- Existing `M2_ANIM_CACHE` and native animation logs remain loader-owned.
|
||||
- Existing renderer queue metrics remain unchanged because these tables never
|
||||
contributed work counts.
|
||||
- Normalized relative path remains the correlation key.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_prototype_cache_state.gd`: invalid admission, exact static/animated
|
||||
identity, independent positive/negative states, first-wins duplicate release,
|
||||
detached/in-tree shutdown ownership, idempotent clear, detached diagnostics,
|
||||
loader source boundaries and 100-by-256 bounded path-state timing.
|
||||
- Runtime-cache shutdown regression verifies final release through the real loader.
|
||||
- Raw repository/finalizer/extractor/Mesh cache/pipeline/material/animation/M2
|
||||
build regressions protect adjacent behavior.
|
||||
- Fidelity evidence is exact cache transition and lifetime extraction. No new
|
||||
proprietary asset or original-client visual comparison is claimed.
|
||||
|
||||
## Extension points
|
||||
|
||||
Eviction or byte/count budgets require measured memory evidence and explicit
|
||||
prototype-user lifetime rules. Animation request-state extraction can consume
|
||||
this service without moving ResourceLoader or builder ownership into it.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Static prototype retention | Implemented extraction | Identity/source/shutdown verifier | Asset-backed traversal/leak run pending |
|
||||
| Animated prototype retention | Implemented extraction | Independent identity/shutdown verifier | Legal animated success fixture pending |
|
||||
| Missing/static-only outcomes | Implemented extraction | Independent negative-state verifier | Recovery still requires new loader session |
|
||||
| Bounded eviction | Historical unbounded behavior | Documented lifetime | Add only after memory profiling |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Strong Node references intentionally live until final shutdown.
|
||||
- Negative outcomes do not retain reason codes and cannot be individually retried,
|
||||
preserving historical behavior.
|
||||
- No proprietary long traversal, descriptor pressure, leak, p95/p99 or paired
|
||||
original-client evidence is included.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Positive Node ownership, negative path state and shutdown release |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Normalization, I/O/build/fallback decisions and cache adapters |
|
||||
| `src/tools/verify_m2_prototype_cache_state.gd` | Admission/identity/lifecycle/source/timing regression |
|
||||
| `src/tools/verify_render_runtime_cache_shutdown.gd` | Integrated final-shutdown release regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-raw-model-repository.md`](m2-raw-model-repository.md)
|
||||
- [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.md)
|
||||
- [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.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,226 @@
|
||||
# M2 Raw Model Repository
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-RAW-MODEL-REPOSITORY-001` |
|
||||
| Owners | Native M2Loader availability, file existence and raw Dictionary loading |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-raw-model-repository`, 2026-07-17 |
|
||||
| Profiles/capabilities | Static and native-animated raw M2 reads |
|
||||
|
||||
## Purpose
|
||||
|
||||
Provide one stateless boundary between renderer orchestration and the optional
|
||||
native `M2Loader`. The repository resolves an extracted path and returns the raw
|
||||
static or animated Dictionary consumed by existing builders and classifiers.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Normalize relative paths or choose `.tscn`/`.glb` cache fallbacks.
|
||||
- Cache successful data, missing paths, Meshes, Nodes or animation decisions.
|
||||
- Build geometry/materials, own queues or attach renderer objects.
|
||||
- Change native parsing, cache formats, retry policy or visible behavior.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader] --> Repository[M2RawModelRepository]
|
||||
Repository --> File[Extracted M2 file]
|
||||
Repository --> Native[ClassDB M2Loader]
|
||||
Native --> Raw[Raw Dictionary]
|
||||
Raw --> Loader
|
||||
Loader --> Builder[Existing M2 builders/finalizer]
|
||||
```
|
||||
|
||||
Allowed dependencies are `ProjectSettings`, `FileAccess` and `ClassDB` for the
|
||||
native file boundary. Resource caches, builders, SceneTree types, worker pools,
|
||||
renderer policy and other application layers are forbidden.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `load_static_model_data(extracted_directory, normalized_relative_path)` | Query | Invoke native `load_m2` for one existing extracted file | Synchronous; stateless | Returns `{}` for rejected/unavailable/invalid result |
|
||||
| `load_animated_model_data(extracted_directory, normalized_relative_path)` | Query | Invoke native `load_m2_animated` for one existing extracted file | Synchronous; stateless | Returns `{}` for rejected/unavailable/invalid result |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Extracted directory String | Loader configuration | Repository path resolution | Copied value | One call |
|
||||
| Input | Already-normalized relative M2 path String | Loader normalization | Repository | Copied value | One call |
|
||||
| Input | Extracted `.m2` bytes | Local legal extraction | Native M2Loader | File-owned | Native call |
|
||||
| Output | Static raw M2 Dictionary | Native `load_m2` | Loader/finalizer/M2Builder | Fresh native result | One caller operation |
|
||||
| Output | Animated raw M2 Dictionary | Native `load_m2_animated` | Loader/animated builder | Fresh native result | One caller operation |
|
||||
| Output | Empty Dictionary | Repository guards | Loader fallback and prototype outcome adapter | Fresh value | One failed call |
|
||||
|
||||
Side effects are limited to file-existence inspection, synchronous native file
|
||||
parsing and creation of one temporary native loader instance per accepted call.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Request[Directory plus normalized path] --> Empty{Path empty?}
|
||||
Empty -->|yes| Reject[Return empty Dictionary]
|
||||
Empty -->|no| Class{M2Loader registered?}
|
||||
Class -->|no| Reject
|
||||
Class -->|yes| Resolve[globalize directory/path join]
|
||||
Resolve --> Exists{File exists?}
|
||||
Exists -->|no| Reject
|
||||
Exists -->|yes| Instance[Instantiate M2Loader]
|
||||
Instance --> Method{Instance and method available?}
|
||||
Method -->|no| Reject
|
||||
Method -->|yes| Parse[Call static or animated method]
|
||||
Parse --> Type{Dictionary result?}
|
||||
Type -->|no| Reject
|
||||
Type -->|yes| Return[Return raw Dictionary]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Ready: construct repository
|
||||
Ready --> Resolving: load request
|
||||
Resolving --> Ready: rejected input/unavailable dependency
|
||||
Resolving --> Parsing: file and native method available
|
||||
Parsing --> Ready: Dictionary or empty result returned
|
||||
Ready --> [*]: caller releases reference
|
||||
```
|
||||
|
||||
No request, result or failure state survives a call.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Repo as M2RawModelRepository
|
||||
participant File as FileAccess
|
||||
participant Native as M2Loader
|
||||
Loader->>Repo: load static/animated(directory, path)
|
||||
Repo->>File: file_exists(globalized joined path)
|
||||
alt dependency or file unavailable
|
||||
Repo-->>Loader: empty Dictionary
|
||||
else available
|
||||
Repo->>Native: instantiate and call exact native method
|
||||
Native-->>Repo: Variant
|
||||
Repo-->>Loader: Dictionary or empty Dictionary
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Repository[M2RawModelRepository]
|
||||
Repository --> ProjectSettings
|
||||
Repository --> FileAccess
|
||||
Repository --> ClassDB
|
||||
ClassDB --> Native[M2Loader extension]
|
||||
Loader --> Finalizer[M2RuntimeMeshFinalizer]
|
||||
Loader --> StaticBuilder[M2Builder]
|
||||
Loader --> AnimatedBuilder[M2NativeAnimatedBuilder]
|
||||
Repository -. no dependency .-> Finalizer
|
||||
Repository -. no dependency .-> StaticBuilder
|
||||
Repository -. no dependency .-> Cache[Renderer caches/queues]
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- The repository owns only call-local path, native instance and result values.
|
||||
- The loader owns path normalization and fallback selection;
|
||||
`M2PrototypeCacheState` owns prototype/negative adoption.
|
||||
- Native `M2Loader` owns parsing behavior and returns a new Dictionary value.
|
||||
- Calls are synchronous on the caller's thread; current renderer callers use the
|
||||
main thread. The module starts no tasks and touches no SceneTree objects.
|
||||
- No file handle, native instance, Dictionary or engine Resource is retained.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Empty normalized path | String guard | Return `{}` | Dedicated verifier | Correct caller normalization |
|
||||
| Native extension absent | `ClassDB.class_exists` | Return `{}` | Existing renderer fallback | Install/load extension and retry |
|
||||
| Extracted file absent | `FileAccess.file_exists` | Return `{}` | Prototype cache records historical missing state through loader | Restore extraction and start a new loader session |
|
||||
| Instance/method absent | Null/`has_method` guard | Return `{}` | Source contract verifier | Repair extension registration |
|
||||
| Parse/non-Dictionary result | Result type check | Return `{}` | Existing loader fallback | Repair asset/parser and retry |
|
||||
| Cancellation requested | Not supported inside synchronous native call | Call completes | None | Caller controls whether to start call |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| Native class name | `M2Loader` | All | No | Exact GDExtension registration |
|
||||
| Static method | `load_m2` | All | No | Existing static parser path |
|
||||
| Animated method | `load_m2_animated` | Experimental animated path | No | Existing native animation parser |
|
||||
| Extracted directory | Loader `extracted_dir` | Scene configuration | Yes, caller-owned | Root used for path join |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
The module adds no persistence or cache. Native methods, normalized relative
|
||||
paths and `.tscn/.glb`/material versions are unchanged, so no migration or rebake
|
||||
is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- Logs: none; loader retains existing fallback and debug output.
|
||||
- Metrics: none; rejected-input timing is verifier-only.
|
||||
- Debug views: none.
|
||||
- Correlation ID: normalized relative M2 path in the caller.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_raw_model_repository.gd` covers empty/missing/repeated failures,
|
||||
exact static/animated method ownership, three loader delegations, forbidden
|
||||
dependencies and 10,000 empty-path rejects under one second.
|
||||
- Adjacent finalizer/extractor/cache/pipeline/animation/material/shutdown tests
|
||||
protect caller behavior and ownership.
|
||||
- Fidelity evidence is structural: exact path join/globalization and native
|
||||
method names are preserved. No proprietary M2 or original-client visual claim
|
||||
is made by this extraction.
|
||||
|
||||
## Extension points
|
||||
|
||||
Future asset-backed verification may add a legal fixture around these value
|
||||
contracts. Caching, asynchronous I/O and parser replacement require separate
|
||||
measured work packages rather than expansion of this repository.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Static raw M2 read | Implemented extraction | Source/invalid/missing verifier | Legal success fixture pending |
|
||||
| Native animated raw M2 read | Implemented extraction | Exact method/source contract | Legal animated fixture pending |
|
||||
| Stateless failure behavior | Verified synthetic | Repeated missing call and source boundary | Asset-backed corrupt input pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Native parsing remains synchronous and has no mid-call cancellation.
|
||||
- The repository deliberately does not distinguish missing dependency, missing
|
||||
file and parser failure; loader fallback behavior historically treats them alike.
|
||||
- Proprietary asset traversal, descriptor pressure, p95/p99 and visual comparison
|
||||
remain outside this structural extraction.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_raw_model_repository.gd` | Stateless native class/file/method boundary |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Prototype and negative-result retention |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Path normalization, fallback decisions and result consumers |
|
||||
| `src/native/src/m2_loader.cpp` | Native static/animated parsing implementation |
|
||||
| `src/tools/verify_m2_raw_model_repository.gd` | Rejected-input/source/dependency/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-runtime-mesh-finalizer.md`](m2-runtime-mesh-finalizer.md)
|
||||
- [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.md)
|
||||
- [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.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,224 @@
|
||||
# M2 Runtime Mesh Finalizer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-RUNTIME-MESH-FINALIZER-001` |
|
||||
| Owners | Stale Mesh refresh version, rebuild classification, M2Builder rebuild and fallback |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-runtime-mesh-finalizer`, 2026-07-17 |
|
||||
| Profiles/capabilities | Static cached and prototype M2 runtime material preparation |
|
||||
|
||||
## Purpose
|
||||
|
||||
Finalize an extracted static M2 Mesh for runtime use by accepting already-loaded
|
||||
raw M2 data, preserving material refresh version `2`, rebuilding only billboard/
|
||||
UV-rotation cases and returning the historical original-Mesh fallback.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Read raw `.m2` files or call ClassDB/FileAccess/ResourceLoader.
|
||||
- Own request/finalize queues, Mesh/missing/prototype caches or MultiMeshes.
|
||||
- Select cache paths or extract a Mesh from the originally loaded Resource.
|
||||
- Change classifier predicates, builder materials, refresh version or profiles.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Extractor[M2MeshResourceExtractor] --> Loader[StreamingWorldLoader]
|
||||
Loader -->|is raw data required?| Finalizer[M2RuntimeMeshFinalizer]
|
||||
Loader --> Raw[M2RawModelRepository]
|
||||
Raw --> Finalizer
|
||||
Finalizer --> Classifier[M2RuntimeMeshRebuildClassifier]
|
||||
Finalizer --> Builder[M2Builder]
|
||||
Builder --> Subtree[M2MeshResourceExtractor]
|
||||
Finalizer -->|prepared Mesh| Cache[M2MeshResourceCacheState]
|
||||
```
|
||||
|
||||
The finalizer may use Mesh metadata, raw value Dictionaries, M2Builder and the
|
||||
two existing scene-free M2 services. Raw-file I/O, cache state, render permits,
|
||||
Nodes outside temporary rebuild roots and other application layers are forbidden.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `requires_raw_data_for_refresh(mesh)` | Query | Tell loader whether stale Mesh needs raw-file lookup | Renderer main thread | Null/current false |
|
||||
| `finalize_mesh(path, mesh, raw_data, extracted_directory)` | Command/query | Return current, marked, rebuilt or fallback Mesh | Renderer main thread; one finalize operation | Null Mesh returns null; invalid rebuild falls back |
|
||||
| `clear()` | Command | Clear memoized classifier decisions | Map reset/final shutdown | Idempotent |
|
||||
| `cached_rebuild_decision_count()` | Diagnostic query | Expose memoized path-decision count | Tests/diagnostics | None |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Extracted Mesh and normalized M2 path | Loader/extractor adapter | Finalizer | Borrowed Mesh reference/String copy | One call |
|
||||
| Input | Already-loaded raw M2 Dictionary | `M2RawModelRepository` through loader adapter | Classifier/M2Builder | Caller-owned value container | One call |
|
||||
| Input | Extracted directory path | Loader configuration | M2Builder texture resolution | Copied String | One rebuild |
|
||||
| Output | Original or rebuilt current Mesh | Finalizer | Cache/prototype adapter | Borrowed/new Resource reference | Cache may retain |
|
||||
|
||||
Side effects are Mesh refresh metadata, classifier memoization and temporary
|
||||
M2Builder prototype creation/destruction during required rebuilds.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Mesh[Extracted Mesh] --> Current{Refresh version >= 2?}
|
||||
Current -->|yes| ReturnOriginal[Return original unchanged]
|
||||
Current -->|no| NeedRaw[Repository supplies raw M2 data through loader]
|
||||
NeedRaw --> HasData{Raw data available?}
|
||||
HasData -->|no| Mark[Mark original version 2]
|
||||
HasData -->|yes| Classify{Billboard or UV rotation?}
|
||||
Classify -->|no| Mark
|
||||
Classify -->|yes| Build[M2Builder temporary prototype]
|
||||
Build --> Extract[M2MeshResourceExtractor first Mesh]
|
||||
Extract --> Rebuilt{Rebuilt Mesh exists?}
|
||||
Rebuilt -->|yes| ReturnRebuilt[Return rebuilt Mesh]
|
||||
Rebuilt -->|no| Mark
|
||||
Mark --> ReturnOriginal
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Current: Mesh metadata >= 2
|
||||
[*] --> Stale: Mesh metadata < 2
|
||||
Stale --> Current: empty/ordinary data marks original
|
||||
Stale --> Rebuilding: classifier requires rebuild
|
||||
Rebuilding --> Current: rebuilt Mesh returned
|
||||
Rebuilding --> Current: failure marks original fallback
|
||||
Current --> Current: later preparation is no-op
|
||||
```
|
||||
|
||||
Classifier decisions are memoized per normalized path until either existing
|
||||
map/reset clear site calls `clear()`.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Finalizer as M2RuntimeMeshFinalizer
|
||||
participant Raw as M2Loader boundary
|
||||
participant Classifier as RebuildClassifier
|
||||
participant Builder as M2Builder
|
||||
Loader->>Finalizer: requires_raw_data_for_refresh(mesh)
|
||||
alt current Mesh
|
||||
Finalizer-->>Loader: false; reuse Mesh
|
||||
else stale Mesh
|
||||
Finalizer-->>Loader: true
|
||||
Loader->>Raw: load static raw Dictionary
|
||||
Loader->>Finalizer: finalize_mesh(path, mesh, raw, extracted dir)
|
||||
Finalizer->>Classifier: needs_runtime_mesh_rebuild(path, raw)
|
||||
opt rebuild required
|
||||
Finalizer->>Builder: build(raw, extracted dir)
|
||||
Finalizer->>Finalizer: extract first Mesh; free prototype
|
||||
end
|
||||
Finalizer-->>Loader: rebuilt or marked fallback Mesh
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Finalizer[M2RuntimeMeshFinalizer]
|
||||
Finalizer --> Classifier[M2RuntimeMeshRebuildClassifier]
|
||||
Finalizer --> Extractor[M2MeshResourceExtractor]
|
||||
Finalizer --> Builder[M2Builder]
|
||||
Loader --> Raw[M2RawModelRepository]
|
||||
Loader --> Cache[M2MeshResourceCacheState]
|
||||
Finalizer -. no dependency .-> Raw
|
||||
Finalizer -. no dependency .-> Cache
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Renderer main thread executes metadata changes and M2Builder work.
|
||||
- `M2RawModelRepository` owns raw M2 file/native calls; loader supplies its value result.
|
||||
- Finalizer owns classifier memoization and temporary rebuild prototype lifetime.
|
||||
- M2Builder owns construction rules; extractor selects the first rebuilt Mesh.
|
||||
- Cache/prototype adapters decide where the returned Mesh reference is retained.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Null Mesh | Guard | Return null | Contract verifier | Caller marks missing |
|
||||
| Current Mesh | Metadata query | Return exact reference; no raw load | Contract verifier | None needed |
|
||||
| Raw file/parse unavailable | Empty Dictionary from loader | Mark/reuse original | Existing renderer behavior | Source/cache repair |
|
||||
| Ordinary stale Mesh | Classifier false | Mark/reuse original | Classifier verifier | None needed |
|
||||
| Builder produces no Mesh | Extractor null | Mark/reuse original | Finalizer fallback fixture | Source/builder repair |
|
||||
| Reset/shutdown | Loader lifecycle | Clear classifier decisions | Source/clear verifier | Recompute next session |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
`MATERIAL_REFRESH_VERSION` remains `2` and is now owned by this finalizer.
|
||||
Finalize permits, cache paths, extracted directory configuration and quality
|
||||
profiles remain unchanged in the loader.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No new format is serialized. Existing Mesh metadata key
|
||||
`wow_m2_material_refresh_version`, M2Builder `MATERIAL_FORMAT_VERSION == 2` and
|
||||
`.tscn/.glb` caches are unchanged; no migration or rebake is introduced.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
`cached_rebuild_decision_count` exposes only a scalar count. Existing renderer
|
||||
logs/queue metrics are unchanged. Normalized M2 path remains the correlation key.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_runtime_mesh_finalizer.gd`: null/current/raw requirement, empty and
|
||||
ordinary marking, synthetic triangle billboard rebuild, rebuild failure
|
||||
fallback, refresh metadata, classifier clear, source boundary and 100-by-256
|
||||
bounded finalization.
|
||||
- Classifier/extractor/cache/pipeline/material/shutdown/M2 build regressions cover
|
||||
adjacent behavior. No asset-backed or original-client visual claim is made.
|
||||
|
||||
## Extension points
|
||||
|
||||
- Asset-backed fixture coverage can be added without changing the finalizer's
|
||||
value-data contract.
|
||||
- Refresh versions above `2` require explicit cache/fidelity evidence and docs.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Runtime Mesh refresh/fallback | Implemented extraction | Synthetic transition/rebuild verifier | Asset-backed corrupt/stale cache pending |
|
||||
| Rebuild predicate | Implemented extraction dependency | Classifier fixtures | Original-client material comparison pending |
|
||||
| Raw M2 refresh I/O | Implemented repository dependency | Repository source/invalid verifier | Legal success fixture pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Required rebuild remains synchronous main-thread work behind existing permits.
|
||||
- Synthetic geometry proves transition/build behavior, not full asset materials.
|
||||
- No proprietary M2 traversal, descriptor-pressure/leak, p95/p99 or paired
|
||||
original-client capture is included.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Refresh version, classification, rebuild and fallback |
|
||||
| `src/render/m2/m2_raw_model_repository.gd` | Raw-file/native I/O and Dictionary result |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Repository call and returned-Mesh adoption |
|
||||
| `src/render/m2/m2_runtime_mesh_rebuild_classifier.gd` | Memoized billboard/UV-rotation predicate |
|
||||
| `src/render/m2/m2_mesh_resource_extractor.gd` | First rebuilt-Mesh selection |
|
||||
| `src/tools/verify_m2_runtime_mesh_finalizer.gd` | Transition/rebuild/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-runtime-mesh-rebuild-classifier.md`](m2-runtime-mesh-rebuild-classifier.md)
|
||||
- [`m2-mesh-resource-extractor.md`](m2-mesh-resource-extractor.md)
|
||||
- [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.md)
|
||||
- [`m2-raw-model-repository.md`](m2-raw-model-repository.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,224 @@
|
||||
# M2 Runtime Mesh Rebuild Classifier
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-RUNTIME-REBUILD-CLASSIFIER-001` |
|
||||
| Owners | Billboard/UV-rotation rebuild decision and per-path memoization |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-runtime-rebuild-classifier`, 2026-07-17 |
|
||||
| Profiles/capabilities | Existing M2 material-refresh path; profile-independent |
|
||||
|
||||
## Purpose
|
||||
|
||||
Determine whether a stale cached M2 mesh must be rebuilt in memory from raw M2
|
||||
data to restore billboard custom attributes or UV-rotation material inputs. The
|
||||
first decision is memoized by normalized relative path for the current map/
|
||||
runtime-cache lifetime.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Read M2 files or invoke `M2Loader`/`M2Builder`.
|
||||
- Own cached Meshes, scenes, materials, Nodes or RIDs.
|
||||
- Select cache paths, perform ResourceLoader transitions or consume permits.
|
||||
- Change refresh/cache format versions or shader behavior.
|
||||
- Classify animation, particles, ribbons or general visual fidelity.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Cache[Stale cached M2 Mesh] --> Finalizer[M2RuntimeMeshFinalizer]
|
||||
Loader[StreamingWorldLoader] --> Raw[M2Loader raw Dictionary]
|
||||
Raw --> Classifier[M2RuntimeMeshRebuildClassifier]
|
||||
Classifier --> Decision[Memoized rebuild boolean]
|
||||
Decision --> Finalizer
|
||||
Finalizer -->|true| Builder[M2Builder rebuild]
|
||||
Finalizer -->|false| Refresh[Mark material refresh version]
|
||||
```
|
||||
|
||||
Allowed dependencies are Dictionary/Array, `PackedInt32Array`, `Vector4` and
|
||||
scalar math. Files, ResourceLoader, parser/builder classes, SceneTree,
|
||||
WorkerThreadPool, Mesh/Node/RID ownership and other layers are forbidden.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `needs_runtime_mesh_rebuild(normalized_relative_path, raw_m2_data)` | Memoized query | Detect billboard or referenced UV rotation requirements | Renderer main thread; cached until clear | Invalid variants/indices are skipped; first path decision wins |
|
||||
| `clear()` | Command | Drop all memoized path decisions | Main thread; map reset/shutdown | Idempotent |
|
||||
| `cached_path_count()` | Query | Return memoized path count | Main thread | None |
|
||||
| `diagnostic_snapshot()` | Query | Return detached path/decision map | Main thread; caller-owned | None |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Normalized M2 relative path | Loader cache lookup | Classifier cache key | String value copied | Until clear |
|
||||
| Input | Raw M2 batches/transforms/combos Dictionary | `M2RawModelRepository` through loader adapter | Classifier | Borrowed; not mutated | First classification call per path |
|
||||
| Output | Rebuild-required boolean | Classifier | Runtime Mesh finalizer | Scalar | Memoized until clear |
|
||||
| Output | Detached decision Dictionary | Classifier | Verifier/future diagnostics | Caller-owned copy | Snapshot lifetime |
|
||||
|
||||
Side effects are limited to inserting and clearing boolean memoization entries.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Query[path + raw M2 data] --> Cached{Path cached?}
|
||||
Cached -->|yes| Return[Return first decision]
|
||||
Cached -->|no| Billboard{Any billboard flag or positive vertex count?}
|
||||
Billboard -->|yes| StoreTrue[Store true]
|
||||
Billboard -->|no| Inputs{Transforms and combos present?}
|
||||
Inputs -->|no| StoreFalse[Store false]
|
||||
Inputs -->|yes| Batch[For each Dictionary batch]
|
||||
Batch --> Stage[Inspect at most four referenced stages]
|
||||
Stage --> Rotation{Non-identity rotation or speed above epsilon?}
|
||||
Rotation -->|yes| StoreTrue
|
||||
Rotation -->|no| StoreFalse
|
||||
StoreTrue --> Return
|
||||
StoreFalse --> Return
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
Each path moves from unknown to one immutable cached boolean. Clear returns all
|
||||
paths to unknown; no resource lifecycle participates.
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Unknown
|
||||
Unknown --> RebuildRequired: billboard or referenced UV rotation
|
||||
Unknown --> RebuildNotRequired: no relevant metadata
|
||||
RebuildRequired --> RebuildRequired: repeated query
|
||||
RebuildNotRequired --> RebuildNotRequired: repeated query
|
||||
RebuildRequired --> Unknown: clear
|
||||
RebuildNotRequired --> Unknown: clear
|
||||
```
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Native as M2Loader
|
||||
participant Finalizer as M2RuntimeMeshFinalizer
|
||||
participant Classifier as M2RuntimeMeshRebuildClassifier
|
||||
participant Builder as M2Builder
|
||||
Loader->>Native: load_m2(normalized path)
|
||||
Native-->>Loader: raw Dictionary
|
||||
Loader->>Finalizer: finalize_mesh(path, mesh, data, dir)
|
||||
Finalizer->>Classifier: needs_runtime_mesh_rebuild(path, data)
|
||||
Classifier-->>Finalizer: memoized boolean
|
||||
alt rebuild required
|
||||
Finalizer->>Builder: rebuild mesh from raw data
|
||||
else no rebuild required
|
||||
Finalizer->>Finalizer: stamp material refresh version
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Finalizer[M2RuntimeMeshFinalizer]
|
||||
Finalizer --> Classifier[M2RuntimeMeshRebuildClassifier]
|
||||
Loader --> Native[M2Loader]
|
||||
Loader --> Builder[M2Builder]
|
||||
Loader --> Cache[M2 Mesh/Scene caches]
|
||||
Classifier -. no dependency .-> Native
|
||||
Classifier -. no dependency .-> Builder
|
||||
Classifier -. no dependency .-> Cache
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- The classifier owns only normalized-path boolean entries.
|
||||
- The finalizer owns classifier lifetime, rebuild decisions and refresh metadata.
|
||||
- The loader owns raw loading, cache/resource selection and Mesh adoption.
|
||||
- Raw Dictionaries are borrowed and never retained; only the computed boolean is cached.
|
||||
- Current calls and clears are serialized on the renderer main thread.
|
||||
- No Node, Resource, Mesh, material or RID reference crosses into the classifier.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Non-Dictionary batch/transform | Type guard | Skip entry or use identity defaults | Synthetic verifier | Correct parser data on next cleared lifetime |
|
||||
| Missing transforms/combos | Empty guard | No UV-driven rebuild | Synthetic verifier | Billboard classification still applies |
|
||||
| Invalid combo/transform index | Bounds guard | Skip stage | Synthetic verifier | Correct raw data and clear |
|
||||
| Repeated path with changed data | Cache hit | Preserve first decision | Memoization fixture | Map/reset clear recomputes |
|
||||
| Load/build failure | Outside classifier | Existing mesh fallback remains | Loader diagnostics | Existing reload/fallback path |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| Maximum texture stages inspected | `4` | Existing M2 material path | No | Preserves historical four-stage shader boundary |
|
||||
| UV rotation-speed epsilon | `0.000001` exclusive | Existing M2 material path | No | Larger absolute speed requires rebuild |
|
||||
| `MATERIAL_REFRESH_VERSION` | Finalizer constant `2` | All | Code version | Gates whether classifier is called |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
Memoized booleans are runtime-only and are cleared at the two historical loader
|
||||
reset/shutdown sites. No persisted cache version or rebuild instruction changes.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- Logs: none.
|
||||
- Metrics: `cached_path_count` is contract-level only and not added to runtime logs.
|
||||
- Debug views: detached path-to-boolean snapshot.
|
||||
- Correlation IDs: normalized M2 relative path.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_runtime_mesh_rebuild_classifier.gd`: billboard flag/count, invalid
|
||||
variants, identity/non-identity rotation, positive/negative speed, exact
|
||||
epsilon, four-stage cap, invalid indices, memoization, clear, source boundary
|
||||
and 100-by-256 timing.
|
||||
- Existing M2 builder/material verifiers cover downstream shader/cache behavior.
|
||||
- Fidelity evidence is exact predicate/cache extraction. No new asset-backed or
|
||||
original-client parity claim is made.
|
||||
- Performance budget: 25,600 classifications and clears under one second.
|
||||
|
||||
## Extension points
|
||||
|
||||
- A typed raw M2 material descriptor may replace the Dictionary only with parser
|
||||
fixtures and a compatibility adapter.
|
||||
- Mesh request/cache/finalization state can be extracted separately without
|
||||
changing this rebuild predicate.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Billboard rebuild classification | Implemented extraction | Flag/count fixtures | Asset-backed cache refresh timing pending |
|
||||
| UV-rotation rebuild classification | Implemented extraction | Combo/stage/threshold fixtures | More build-12340 material fixtures pending |
|
||||
| Runtime memoization lifetime | Implemented extraction | First-decision/clear/source fixtures | Byte/count runtime metric not exposed |
|
||||
| Runtime Mesh finalization | Implemented extraction | Finalizer transition/rebuild fixtures | Asset-backed material comparison pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Raw parser data remains Dictionary-based.
|
||||
- The first decision wins even if different raw data is passed for the same path;
|
||||
this intentionally preserves prior cache behavior.
|
||||
- Only four texture stages are inspected, matching current material support.
|
||||
- No private asset traversal, descriptor-pressure, p95/p99 or paired visual run is included.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_runtime_mesh_rebuild_classifier.gd` | Predicate, thresholds and boolean memoization |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Raw loading, Mesh rebuild/adoption, refresh version and clear calls |
|
||||
| `addons/mpq_extractor/loaders/m2_builder.gd` | Mesh/material rebuild implementation |
|
||||
| `src/tools/verify_m2_runtime_mesh_rebuild_classifier.gd` | Synthetic predicate/cache/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-build-batch-planner.md`](m2-build-batch-planner.md)
|
||||
- [`m2-placement-grouper.md`](m2-placement-grouper.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)
|
||||
@@ -5,24 +5,25 @@
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Partial |
|
||||
| Target/work package | M02 / M02-GMP-INPUT-001, M02-GMP-MOVEMENT-001 and M02-RND-CAMERA-001 consumer updates |
|
||||
| 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 | `8c1cf9b`, 2026-07-14 |
|
||||
| Profiles/capabilities | Current render-sandbox defaults; `Blizzlike335` binding semantics not yet verified |
|
||||
| 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 current player controller and a later
|
||||
independently testable local movement controller.
|
||||
`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.
|
||||
- Claim that the current W/S/A/D sandbox mapping exactly matches build 12340.
|
||||
- Make sprint or free flight available to the production compatibility profile;
|
||||
their profile gate remains a later M02 package.
|
||||
- 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
|
||||
|
||||
@@ -30,8 +31,10 @@ independently testable local movement controller.
|
||||
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]
|
||||
```
|
||||
@@ -52,11 +55,13 @@ Forbidden dependencies:
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `MoveIntent` | Immutable value | Carries normalized forward, strafe and vertical 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` | 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.sample_move_intent()` | Adapter method | Samples configured actions 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.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
|
||||
|
||||
@@ -65,6 +70,8 @@ Forbidden dependencies:
|
||||
| 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:
|
||||
@@ -81,9 +88,11 @@ 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]
|
||||
```
|
||||
|
||||
@@ -92,7 +101,9 @@ flowchart LR
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -106,7 +117,7 @@ sequenceDiagram
|
||||
Engine->>Source: movement action strengths
|
||||
Source->>Intent: compose normalized axes and requests
|
||||
Source-->>Player: sample_move_intent()
|
||||
Player->>Move: advance(intent, selected basis, delta)
|
||||
Player->>Move: calculate yaw delta, then advance(intent, selected basis, delta)
|
||||
Move-->>Player: displacement and velocity state
|
||||
```
|
||||
|
||||
@@ -127,6 +138,7 @@ sequenceDiagram
|
||||
| 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.
|
||||
|
||||
@@ -134,12 +146,15 @@ There is no asynchronous operation to cancel and no persisted state to roll back
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| Forward/backward | Physical W/S | Current render sandbox | Yes, through Input Map | Produces signed forward axis |
|
||||
| Strafe left/right | Physical A/D | Current render sandbox | Yes | Produces signed right axis |
|
||||
| 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 | Sandbox debug only by intent; profile gate pending | Yes | Requests existing speed multiplier |
|
||||
| Debug flight up/down/toggle | E/Q/Space | Sandbox debug only by intent; profile gate pending | Yes | Requests existing free-flight controls |
|
||||
| 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
|
||||
|
||||
@@ -157,13 +172,20 @@ provide a settings migration once user settings are persisted.
|
||||
## Verification
|
||||
|
||||
- Unit/contract tests: `src/tools/verify_player_input.gd` validates all actions,
|
||||
default bindings, axis cancellation/clamping/normalization and debug request flags.
|
||||
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 forward, camera-zoom and immediate sandbox-flight
|
||||
actions through the real scene controller and also rejects direct
|
||||
physical-key polling.
|
||||
- Fidelity evidence: current sandbox defaults retain W/S/A/D, E/Q, Shift, Space,
|
||||
right mouse and wheel bindings. This is observable-regression coverage only.
|
||||
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
|
||||
@@ -184,15 +206,15 @@ provide a settings migration once user settings are persisted.
|
||||
|---|---|---|---|
|
||||
| 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 |
|
||||
| Exact 3.3.5a default semantics | Unknown | No original-client binding fixture in this package | Capture build-12340 defaults and mouse-turn/strafe policy |
|
||||
| Production profile exclusion of sprint/flight | Partial | Debug actions are named explicitly | Add typed profile/capability gate in M02 |
|
||||
| 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
|
||||
|
||||
- The defaults preserve the pre-M02 sandbox behavior, not verified original-client semantics.
|
||||
- Sprint and free flight are still executable by the sandbox controller until a
|
||||
typed build-profile gate is introduced.
|
||||
- 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.
|
||||
|
||||
@@ -205,13 +227,17 @@ provide a settings migration once user settings are persisted.
|
||||
| `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`.
|
||||
`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)
|
||||
@@ -5,10 +5,10 @@
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target/work package | M02 / M02-GMP-TERRAIN-QUERY-001 |
|
||||
| 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; renderer/physics backend planned |
|
||||
| Profiles/capabilities | Current sandbox ADT ground height; loaded-render-mesh diagnostic query; authoritative physics backend planned |
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -47,6 +47,8 @@ Allowed dependencies:
|
||||
- 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:
|
||||
|
||||
@@ -65,6 +67,7 @@ Forbidden dependencies:
|
||||
| `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
|
||||
|
||||
@@ -206,6 +209,9 @@ Existing ADT/native/cache format versions remain unchanged.
|
||||
## 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.
|
||||
@@ -218,7 +224,8 @@ Existing ADT/native/cache format versions remain unchanged.
|
||||
| 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/streaming cache backend | Planned | Boundary permits replacement | M03 renderer facade/public terrain backend |
|
||||
| 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 |
|
||||
|
||||
@@ -240,6 +247,7 @@ Existing ADT/native/cache format versions remain unchanged.
|
||||
| `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 |
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target/work package | M02 / M02-RND-CAMERA-001 |
|
||||
| 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 | `8c1cf9b`, 2026-07-14 |
|
||||
| Last verified | M02-GMP-INPUT-PROFILE-335-001 worktree, 2026-07-15 |
|
||||
| Profiles/capabilities | Current sandbox orbit/zoom; identity collision policy |
|
||||
|
||||
## Purpose
|
||||
@@ -60,6 +60,7 @@ Forbidden dependencies:
|
||||
|---|---|---|---|---|
|
||||
| `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 |
|
||||
@@ -77,6 +78,7 @@ Read-only rig state: `yaw_radians`, `pitch_radians`,
|
||||
| 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 |
|
||||
@@ -194,6 +196,8 @@ tools require no path migration.
|
||||
|
||||
- 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
|
||||
|
||||
@@ -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)
|
||||
+372
-29
@@ -5,9 +5,9 @@
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Partial |
|
||||
| Target/work package | M00 baseline; `M01-RND-STREAMING-FOCUS-001`; `M01-QAR-SERVER-SPAWN-RENDERER-001`; декомпозиция M02–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 |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m01-server-spawn-renderer`, 2026-07-13 |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-prototype-cache`, 2026-07-17 |
|
||||
| Profiles/capabilities | `Performance`, `Balanced`, `High`, `Custom`; Blizzlike fidelity incomplete |
|
||||
|
||||
## Purpose
|
||||
@@ -32,15 +32,49 @@ flowchart LR
|
||||
SpawnFixture[Pinned server spawn fixture] --> SpawnManifest[Spawn render manifest]
|
||||
SpawnManifest --> Capture
|
||||
Adapter --> Focus
|
||||
Focus --> Loader[StreamingWorldLoader]
|
||||
Focus --> Facade[WorldRenderFacade]
|
||||
Facade --> Loader[StreamingWorldLoader internal]
|
||||
Loader --> Planner[StreamingTargetPlanner]
|
||||
Planner --> TargetPlan[StreamingTargetPlan]
|
||||
TargetPlan --> Loader
|
||||
Loader --> Budget[RenderBudgetScheduler]
|
||||
Assets[Extracted WDT/ADT/M2/WMO/BLP] --> Native[Native loaders]
|
||||
Cache[Baked terrain/M2/WMO caches] --> Loader
|
||||
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]
|
||||
Parsed --> Loader
|
||||
Loader --> Scene[SceneTree nodes]
|
||||
Loader --> RS[RenderingServer RIDs/MultiMesh]
|
||||
Sky[DBC sky/light data] --> SkyController[WowSkyController]
|
||||
EnvironmentState[World time producer] --> EnvironmentSnapshot[WorldEnvironmentSnapshot]
|
||||
EnvironmentSnapshot --> Facade
|
||||
Facade --> SkyController[WowSkyController]
|
||||
Sky[DBC sky/light data] --> SkyController
|
||||
SkyController --> Scene
|
||||
EntityState[Entity visual read model] --> EntitySnapshot[EntityPresentationSnapshot]
|
||||
EntitySnapshot --> Facade
|
||||
Facade --> EntityPresenter[WorldEntityPresenter]
|
||||
EntityPresenter --> Scene
|
||||
```
|
||||
|
||||
Allowed dependencies:
|
||||
@@ -60,36 +94,104 @@ Forbidden dependencies:
|
||||
|
||||
## Public API
|
||||
|
||||
Текущая система ещё не имеет стабильного facade. Фактический integration surface — `StreamingWorldLoader` Node3D и exported properties. M03 должен заменить этот surface на `WorldRenderFacade`.
|
||||
Runtime, capture and probe callers use `WorldRenderFacade` for focus, immutable
|
||||
environment/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 |
|
||||
|---|---|---|---|---|
|
||||
| `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 |
|
||||
| `StreamingFocus` | Immutable value | Typed Godot-basis position without Node/Camera dependency | Any thread; caller-owned reference | Null position is a caller contract violation |
|
||||
| `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 |
|
||||
| `set_streaming_focus` | Public method | Accepts focus from app/editor/tool composition | Main thread/session | Null focus is ignored by refresh until replaced |
|
||||
| `refresh_streaming_focus` | Public method | Samples configured source and applies refresh thresholds | Main thread/session | Returns `false` when no valid focus exists |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `M2RuntimeMeshRebuildClassifier` | Internal memoized M2 service | Detects billboard/UV-rotation metadata requiring stale cached-mesh rebuild | Renderer main thread; cached until reset | Invalid variants/indices skipped; first path decision wins |
|
||||
| `M2MeshLoadPipelineState` | Internal M2 async-state service | Owns static Mesh pending request records and terminal finalize FIFO | Renderer main thread; map/session | Empty/duplicate/unknown transitions rejected |
|
||||
| `M2MeshResourceCacheState` | Internal M2 Resource cache | Owns prepared static Mesh references by normalized path | Renderer main thread; final shutdown | Empty path/null Mesh rejected; same path replaces |
|
||||
| `M2MeshResourceExtractor` | Internal M2 scene/resource service | Selects first Mesh from direct Resource, PackedScene or Node subtree | Renderer main thread; stateless except temporary instance | Invalid/no Mesh returns null; temporary PackedScene root freed |
|
||||
| `M2RuntimeMeshFinalizer` | Internal M2 preparation service | Owns refresh version, rebuild classification, M2Builder rebuild and fallback | Renderer main thread; decisions cached until reset | Null returns null; missing/failed rebuild marks and reuses original Mesh |
|
||||
| `M2RawModelRepository` | Internal M2 native repository | Reads static/animated raw Dictionaries through exact M2Loader methods | Synchronous; stateless | Invalid/unavailable/non-Dictionary result returns empty Dictionary |
|
||||
| `M2PrototypeCacheState` | Internal M2 prototype cache | Owns detached static/animated Nodes and missing/static-only outcomes | Renderer main thread; final shutdown | Invalid admission rejected; first valid prototype wins |
|
||||
| `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 |
|
||||
| `AdtWaterSceneFinalizer.attach_water_scene` | Internal liquid main-thread service | Builds and attaches one existing-format ADT Water subtree | Main thread; stateless, returned Node tile-owned | Empty/invalid/dry input returns null without attachment |
|
||||
|
||||
Публичным contract не считаются внутренние dictionaries, queues, job records и generated node names.
|
||||
Публичным contract не считаются `StreamingWorldLoader` methods/properties,
|
||||
внутренние dictionaries, queues, job records и generated node names. Scene-owned
|
||||
loader configuration remains transitional composition data, not a caller API.
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | `StreamingFocus` | Player/spectator/editor/capture adapter | `StreamingWorldLoader` | Immutable caller value | Main thread/session |
|
||||
| Input | Focus-source `NodePath` | Runtime/test scene composition | Loader source adapter | Scene-owned node, sampled position | Main thread/scene lifetime |
|
||||
| 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 | Baked resources | Cache build tools | Streamer/builders | Shared immutable resource/cache entry | Session/cache lifetime |
|
||||
| Test input | Server-spawn render manifest | Coordinate fixture/verifier | Checkpoint capture tool | Repository-owned diagnostic data | Test-process lifetime |
|
||||
| Output | Desired tile/detail operations | Streaming planner inside loader | Finalize queues | Loader-owned | Cross-frame |
|
||||
| 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 |
|
||||
| Internal raw M2 read | Extracted directory and normalized relative path | Loader / `M2RawModelRepository` | Finalizer, static or animated builder adapters | Fresh Dictionary; repository retains nothing | One synchronous native call |
|
||||
| Internal M2 prototype state | Normalized path, adopted static/animated Node or negative outcome | Loader / `M2PrototypeCacheState` | Loader reuse/fallback adapters | State-owned strong Node refs and Strings; detached diagnostics | Until final shutdown |
|
||||
| Output | Desired tile/detail operations | Streamer plan application | Finalize queues | Loader-owned | Cross-frame |
|
||||
| Output | Terrain/M2/WMO/liquid instances | Loader/builders | Godot world/renderer | Loader/world owner | Main-thread attach |
|
||||
| Output | Metrics/diagnostics | Hitch/stat instrumentation | Logs/baseline tools | Immutable records | Periodic/frame |
|
||||
| Output | Detached renderer metrics | `WorldRenderFacade` | Capture/baseline tools | Caller-owned deep copy | On demand |
|
||||
| Output | `RenderedGroundSample` | `WorldRenderFacade`/loaded terrain | Renderer diagnostics | Immutable caller-held value | One query |
|
||||
| Output | Detached rendered-ground snapshot | `WorldRenderFacade` | Terrain-height probe | Caller-owned deep copy | One query |
|
||||
| Output | 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:
|
||||
@@ -104,7 +206,7 @@ Side effects:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
P[Player/spectator Node3D] --> A[Explicit scene adapter]
|
||||
PlayerSource[Player/spectator Node3D] --> A[Explicit scene adapter]
|
||||
C[Capture camera] --> A
|
||||
SF[Pinned server spawn] --> SM[Validated spawn manifest]
|
||||
SM --> C
|
||||
@@ -112,23 +214,53 @@ flowchart TD
|
||||
E[Editor viewport] --> EA[Editor adapter]
|
||||
A --> F[StreamingFocus]
|
||||
EA --> F
|
||||
F --> T[Compute desired ADT tiles]
|
||||
WDT[WDT tile catalog] --> T
|
||||
F --> RF[WorldRenderFacade]
|
||||
RF --> Planner[StreamingTargetPlanner]
|
||||
Planner --> T[StreamingTargetPlan apply]
|
||||
WDT[WDT tile catalog] --> Planner
|
||||
T --> Q[Tile load queue]
|
||||
Q --> P[Worker parse/cache load]
|
||||
P --> R[Result queues]
|
||||
R --> B[Per-frame budget scheduler]
|
||||
Q --> Parse[Worker parse/cache load]
|
||||
Parse --> R[Result queues]
|
||||
R --> 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 --> M2[M2 group/MultiMesh attach]
|
||||
B --> WMO[WMO instance/group attach]
|
||||
B --> Liquid[Liquid attach]
|
||||
Loaded[Loaded terrain Mesh and Transform] --> Ground[Facade ground query]
|
||||
Ground --> GroundSample[RenderedGroundSample / detached diagnostics]
|
||||
WorldTime[Authoritative/test time] --> EnvironmentSnapshot[WorldEnvironmentSnapshot]
|
||||
EnvironmentSnapshot --> RF
|
||||
RF --> SkyController[WowSkyController]
|
||||
DBC[Light/Area/Skybox DBC] --> SkyController
|
||||
SkyController --> World
|
||||
EntityReadModel[Entity visual read model] --> EntitySnapshot[EntityPresentationSnapshot]
|
||||
EntitySnapshot --> RF
|
||||
RF --> EntityPresenter[WorldEntityPresenter]
|
||||
EntityAsset[PackedScene path] --> EntityPresenter
|
||||
EntityPresenter --> World
|
||||
Terrain --> World[Godot world]
|
||||
M2 --> World
|
||||
WMO --> World
|
||||
Liquid --> World
|
||||
Marker --> World
|
||||
F --> E[Eviction/retention decisions]
|
||||
E --> World
|
||||
F --> Eviction[Eviction/retention decisions]
|
||||
Eviction --> World
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
@@ -155,18 +287,37 @@ stateDiagram-v2
|
||||
sequenceDiagram
|
||||
participant Source as Player/Editor/Capture source
|
||||
participant Focus as StreamingFocus adapter
|
||||
participant Stream as StreamingWorldLoader
|
||||
participant Facade as WorldRenderFacade
|
||||
participant Stream as StreamingWorldLoader internal
|
||||
participant Planner as StreamingTargetPlanner
|
||||
participant Worker as Worker task
|
||||
participant Budget as Main-thread budget
|
||||
participant Budget as RenderBudgetScheduler
|
||||
participant Render as SceneTree/RenderingServer
|
||||
Source->>Focus: sample explicit Godot position
|
||||
Focus->>Stream: set/refresh typed focus
|
||||
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
|
||||
Worker-->>Stream: immutable result
|
||||
Stream->>Budget: enqueue finalize operations
|
||||
Budget->>Render: attach bounded terrain/M2/WMO/liquid work
|
||||
Stream->>Budget: begin_frame(operation limits)
|
||||
Stream->>Budget: try_consume_permit(lane)
|
||||
Budget-->>Stream: true while lane remains bounded
|
||||
Stream->>Render: attach permitted terrain/M2/WMO/liquid work
|
||||
Stream->>Render: evict outside retention range
|
||||
Source->>Facade: sample_ground_height(typed position)
|
||||
Facade->>Stream: sample loaded render terrain
|
||||
Stream-->>Facade: RenderedGroundSample / diagnostic snapshot
|
||||
Source->>Facade: apply_environment_snapshot(exact time)
|
||||
Facade->>Render: delegate to WowSkyController
|
||||
Render->>Render: freeze local clock; retain DBC sampling/apply rules
|
||||
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->>Stream: shutdown: finish registered ResourceLoader requests
|
||||
Stream->>Render: clear queues and owned tree nodes/RIDs
|
||||
@@ -176,6 +327,84 @@ sequenceDiagram
|
||||
## Ownership, threading and resources
|
||||
|
||||
- `StreamingWorldLoader` владеет tile states, queues, task registries, active world instances и render caches session scope.
|
||||
- `WorldRenderFacade` owns only scene-relative adapter paths. It does not own the
|
||||
streamer, environment controller, focus source, returned metrics, queues,
|
||||
caches, nodes or RIDs.
|
||||
- `WorldEnvironmentSnapshot` is an immutable caller-owned scalar value. Neither
|
||||
facade nor sky controller retains it; the controller copies normalized hours
|
||||
into its existing fixed-clock fields on the main thread.
|
||||
- `WowSkyController` continues to own DBC sampling state and mutations of its
|
||||
duplicated Godot `Environment`, sun, skybox node and world shader globals.
|
||||
- `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 `water_loaded` timing. `AdtWaterSceneFinalizer`
|
||||
performs the existing ADTBuilder call, tile attach and optional recursive
|
||||
Editor ownership on the main thread without retaining the generated subtree.
|
||||
- `M2RuntimeMeshRebuildClassifier` owns only normalized-path boolean decisions
|
||||
for billboard/UV-rotation material refresh and is composed by the runtime Mesh
|
||||
finalizer. The raw repository loads value data; loader retains Mesh adoption.
|
||||
- `M2MeshLoadPipelineState` owns static M2 pending Resource paths, opaque
|
||||
terminal statuses and completion-order finalize FIFO. The loader retains cache
|
||||
path selection, ResourceLoader calls, permits and adoption decisions; prototype
|
||||
cache state owns shared missing outcomes.
|
||||
- `M2MeshResourceCacheState` owns prepared static Mesh references and releases
|
||||
them at the existing final-shutdown site. Prototype state and materialization
|
||||
belong to the sibling cache service and loader respectively.
|
||||
- `M2MeshResourceExtractor` owns depth-first first-Mesh selection and temporary
|
||||
PackedScene instance destruction. The loader retains ResourceLoader I/O,
|
||||
cache/missing adoption and materialization.
|
||||
- `M2RuntimeMeshFinalizer` owns refresh version `2`, classifier lifetime,
|
||||
M2Builder rebuild and original-Mesh fallback. The loader loads raw data only
|
||||
after the finalizer reports that a cached Mesh is stale.
|
||||
- `M2RawModelRepository` owns FileAccess/ClassDB availability and the exact
|
||||
`load_m2`/`load_m2_animated` calls. The loader retains normalization, fallback
|
||||
order and every result consumer; `M2PrototypeCacheState` retains outcomes.
|
||||
- `M2PrototypeCacheState` owns exact static/animated prototype references,
|
||||
missing-model and static-animation path sets, duplicate-candidate release and
|
||||
final-shutdown Node cleanup. ResourceLoader, builders and fallback decisions
|
||||
remain loader-owned.
|
||||
- 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.
|
||||
@@ -192,10 +421,18 @@ sequenceDiagram
|
||||
|---|---|---|---|---|
|
||||
| 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 |
|
||||
| Worker failure | Task result/empty data | Skip/degrade affected asset | Queue/task warning | Retry/rebuild source |
|
||||
| Main-thread hitch | Named section timing | Frame spike, work remains queued | `HITCH` log | Lower budget/fix finalize path |
|
||||
| Render terrain unavailable | Facade ground query | Immutable unavailable sample | Stable `render_terrain_*` failure code and optional snapshot | Wait for streaming or use gameplay-owned query backend |
|
||||
| D3D12 descriptor exhaustion | Rendering backend error | Render failure/fallback backend | Godot error + baseline report | Dedup resources/fix settings |
|
||||
| Teleport/map change | Focus/session transition | Old jobs become stale | Target/session generation | Cancel/drop stale results |
|
||||
| Shutdown leak | Godot leak/RID diagnostics | Resource retained | Verbose shutdown report + cache shutdown verifier | Drain requests, preserve tree ownership, then free detached prototypes and clear resource caches |
|
||||
@@ -225,6 +462,8 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
## Diagnostics and observability
|
||||
|
||||
- Logs: `HITCH`, `PERF`, `TERRAIN_QUALITY`, `SKY_LIGHT`, `SKYBOX_MODEL`.
|
||||
- API snapshot: `WorldRenderFacade.renderer_metrics_snapshot()` returns detached
|
||||
queue/cache/activity counts without exposing mutable renderer state.
|
||||
- Metrics: queue depths, active tiles/assets, named finalize times, frame percentiles and max hitch.
|
||||
- Debug views: render checkpoint scenes/captures; future streaming/LOD/portal overlays.
|
||||
- Correlation IDs: currently mostly tile/path keys; target architecture adds session/job IDs.
|
||||
@@ -232,6 +471,50 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
## Verification
|
||||
|
||||
- Unit/contract tests: streaming-focus contract/wiring, material mapping, unique-ID dedupe, placement probes, baseline manifest, five-point coordinate calibration, synthetic perceptual checkpoint diff, camera-pose grid plan.
|
||||
- Pure planner contract: center/corner/catalog/clamp/editor cases plus bounded
|
||||
High-like iteration timing without loading a world scene.
|
||||
- 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.
|
||||
@@ -251,13 +534,34 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
|---|---|---|---|
|
||||
| ADT streaming/terrain | Partial | M00 checkpoints and current scenes | Fidelity/performance gaps remain |
|
||||
| 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 |
|
||||
| M2 prototype cache state | Implemented extraction | Synthetic identity/negative/lifecycle/source/timing plus shutdown contract | Asset-backed traversal/leak/p95/p99 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 |
|
||||
| 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 |
|
||||
| Sky/light | Partial | DBC controller/logs | Zone transition paired validation |
|
||||
| Character presentation | Prototype | Character creator/player experiment | Full customization/equipment states |
|
||||
| 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 | Planned | M03 | Current caller uses monolithic Node API |
|
||||
| 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
|
||||
|
||||
@@ -278,14 +582,53 @@ 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.
|
||||
- M2/WMO/material/particle/ribbon/portal parity incomplete.
|
||||
- Public API is mostly exported configuration rather than stable contracts.
|
||||
- Rendered-ground sampling generates a triangle mesh on demand and retains the
|
||||
historical nearby seam fallback. It is appropriate for diagnostics, not a
|
||||
per-physics-frame gameplay query or a claim of exact terrain collision parity.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/world_render_facade.gd` | Stable caller boundary for typed focus/environment, detached metrics and rendered-ground queries |
|
||||
| `src/render/environment/world_environment_snapshot.gd` | Immutable normalized world time input with explicit invalid state |
|
||||
| `src/render/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/liquid/adt_water_scene_finalizer.gd` | Main-thread ADT water build, tile attachment and optional editor ownership |
|
||||
| `src/render/m2/m2_runtime_mesh_rebuild_classifier.gd` | Billboard/UV-rotation stale cached-mesh rebuild decision and memoization |
|
||||
| `src/render/m2/m2_mesh_load_pipeline_state.gd` | Static M2 pending Resource paths, terminal statuses and finalize FIFO |
|
||||
| `src/render/m2/m2_mesh_resource_cache_state.gd` | Prepared static M2 Mesh references and final-shutdown release |
|
||||
| `src/render/m2/m2_mesh_resource_extractor.gd` | First-Mesh selection and temporary PackedScene instance lifetime |
|
||||
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Runtime refresh version, rebuild classification/build and fallback |
|
||||
| `src/render/m2/m2_raw_model_repository.gd` | Stateless static/animated native M2 file boundary |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Detached prototype ownership and negative lookup outcomes |
|
||||
| `src/render/streaming/streaming_target_planner.gd` | Scene-free wanted/retained ADT target calculation |
|
||||
| `src/render/streaming/streaming_target_policy.gd` | Immutable renderer radius/prefetch policy |
|
||||
| `src/render/streaming/streaming_target_plan.gd` | Immutable planner result with read-only tile-key sets |
|
||||
| `src/render/streaming/render_budget_scheduler.gd` | Scene-free per-frame operation permits and terminal cancellation |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Current runtime orchestration, queues, caches and attachment |
|
||||
| `src/domain/streaming/streaming_focus.gd` | Immutable camera-independent streaming position contract |
|
||||
| `src/tools/verify_streaming_focus.gd` | Headless contract, dependency and runtime/tool wiring regression |
|
||||
| `src/tools/verify_world_render_facade.gd` | Focus/environment/metrics/ground delegation, snapshot isolation and consumer wiring regression |
|
||||
| `src/tools/verify_world_environment_snapshot.gd` | Cold-start environment value normalization and invalid-input regression |
|
||||
| `src/tools/verify_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_adt_water_scene_finalizer.gd` | Synthetic ADT water scene/ownership/boundary/timing regression |
|
||||
| `src/tools/verify_m2_runtime_mesh_rebuild_classifier.gd` | M2 rebuild predicate/cache/boundary/timing regression |
|
||||
| `src/tools/verify_m2_mesh_load_pipeline_state.gd` | Static M2 request/terminal/FIFO/boundary/timing regression |
|
||||
| `src/tools/verify_m2_mesh_resource_cache_state.gd` | Static M2 Mesh cache ownership/lifetime/boundary/timing regression |
|
||||
| `src/tools/verify_m2_mesh_resource_extractor.gd` | M2 direct/PackedScene/subtree order/lifetime/boundary/timing regression |
|
||||
| `src/tools/verify_m2_runtime_mesh_finalizer.gd` | M2 current/stale/rebuild/fallback/boundary/timing regression |
|
||||
| `src/tools/verify_m2_raw_model_repository.gd` | M2 invalid/missing/native-boundary/dependency/timing regression |
|
||||
| `src/tools/verify_m2_prototype_cache_state.gd` | M2 prototype identity/negative/lifecycle/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/m2_builder.gd` | Static M2 mesh/material construction |
|
||||
| `addons/mpq_extractor/loaders/m2_native_animated_builder.gd` | Native animated M2 experiment |
|
||||
@@ -301,7 +644,7 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
| `src/tools/verify_server_spawn_renderer.gd` | Fixture-to-renderer checkpoint contract verification |
|
||||
| `tools/sweep_render_checkpoint_camera_pose.ps1` | Offline yaw/pitch capture grid and paired-error ranking |
|
||||
| `src/tools/verify_render_coordinate_calibration.gd` | Build 12340 camera-coordinate golden point round-trip diagnostic |
|
||||
| `src/tools/probe_render_terrain_height.gd` | Offline active-mesh terrain height and camera-clearance report |
|
||||
| `src/tools/probe_render_terrain_height.gd` | Offline facade-backed active-mesh terrain height and camera-clearance report |
|
||||
| `src/tools/probe_render_camera_occluders.gd` | Scene-tree placement containment and camera-to-target AABB intersection report |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
@@ -24,11 +24,13 @@ enabled=PackedStringArray("res://addons/mpq_extractor/plugin.cfg")
|
||||
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={
|
||||
@@ -41,6 +43,28 @@ openwc_player_strafe_right={
|
||||
"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)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# Reference projects are research inputs, not OpenWC runtime resources.
|
||||
@@ -2,6 +2,10 @@ Reference-only sources and tools.
|
||||
|
||||
This directory contains external projects used to compare WoW file-format behavior and editor pipelines while implementing OpenWC. Runtime code should not depend on anything in this directory.
|
||||
|
||||
Godot project scanning is disabled for this directory by `.gdignore`. Reference
|
||||
assets remain available to Git, text search and external tooling, but Godot must
|
||||
not generate `.import` sidecars inside nested reference repositories.
|
||||
|
||||
- `blender-wow-studio-3.4-1.1.0_Experimental/` - Blender add-on sources for M2/WMO import/export reference.
|
||||
- `wow.export/` - wow.export source tree and Blender OBJ importer reference.
|
||||
- `noggit-red/` - Noggit RED source tree from `https://gitlab.com/dirtbikercj/noggit-red`, used as a reference for WoW 3.3.5 map editing, ADT/WMO/M2 placement behavior, UID handling and editor workflows.
|
||||
|
||||
+1
-1
Submodule reference/open-realm updated: dfe316d3b0...c7ed0545c8
@@ -21,10 +21,16 @@ var is_sprint_requested: bool:
|
||||
get:
|
||||
return _is_sprint_requested
|
||||
|
||||
## Signed keyboard turn request: left is negative and right is positive.
|
||||
var turn_axis: float:
|
||||
get:
|
||||
return _turn_axis
|
||||
|
||||
var _forward_axis: float
|
||||
var _strafe_axis: float
|
||||
var _vertical_axis: float
|
||||
var _is_sprint_requested: bool
|
||||
var _turn_axis: float
|
||||
|
||||
|
||||
## Creates one frame's movement request without retaining mutable input state.
|
||||
@@ -32,12 +38,14 @@ func _init(
|
||||
forward_axis_value: float = 0.0,
|
||||
strafe_axis_value: float = 0.0,
|
||||
vertical_axis_value: float = 0.0,
|
||||
sprint_requested: bool = false
|
||||
sprint_requested: bool = false,
|
||||
turn_axis_value: float = 0.0
|
||||
) -> void:
|
||||
_forward_axis = clampf(forward_axis_value, -1.0, 1.0)
|
||||
_strafe_axis = clampf(strafe_axis_value, -1.0, 1.0)
|
||||
_vertical_axis = clampf(vertical_axis_value, -1.0, 1.0)
|
||||
_is_sprint_requested = sprint_requested
|
||||
_turn_axis = clampf(turn_axis_value, -1.0, 1.0)
|
||||
|
||||
|
||||
## Returns true when the intent requests planar or vertical translation.
|
||||
|
||||
@@ -2,13 +2,17 @@ class_name PlayerInputActions
|
||||
extends RefCounted
|
||||
|
||||
## Stable Input Map action names consumed by the runtime player input adapter.
|
||||
## Defaults preserve the current render-sandbox controls; users may remap every
|
||||
## action through Godot's Input Map without changing gameplay code.
|
||||
## Separate translation actions preserve the renderer sandbox while allowing
|
||||
## Blizzlike335 defaults to match build-12340 movement bindings.
|
||||
|
||||
const MOVE_FORWARD := &"openwc_player_move_forward"
|
||||
const MOVE_BACKWARD := &"openwc_player_move_backward"
|
||||
const STRAFE_LEFT := &"openwc_player_strafe_left"
|
||||
const STRAFE_RIGHT := &"openwc_player_strafe_right"
|
||||
const BLIZZLIKE_STRAFE_LEFT := &"openwc_player_blizzlike_strafe_left"
|
||||
const BLIZZLIKE_STRAFE_RIGHT := &"openwc_player_blizzlike_strafe_right"
|
||||
const TURN_LEFT := &"openwc_player_turn_left"
|
||||
const TURN_RIGHT := &"openwc_player_turn_right"
|
||||
const DEBUG_FLY_UP := &"openwc_player_debug_fly_up"
|
||||
const DEBUG_FLY_DOWN := &"openwc_player_debug_fly_down"
|
||||
const DEBUG_SPRINT := &"openwc_player_debug_sprint"
|
||||
@@ -23,6 +27,10 @@ const REQUIRED_ACTIONS: Array[StringName] = [
|
||||
MOVE_BACKWARD,
|
||||
STRAFE_LEFT,
|
||||
STRAFE_RIGHT,
|
||||
BLIZZLIKE_STRAFE_LEFT,
|
||||
BLIZZLIKE_STRAFE_RIGHT,
|
||||
TURN_LEFT,
|
||||
TURN_RIGHT,
|
||||
DEBUG_FLY_UP,
|
||||
DEBUG_FLY_DOWN,
|
||||
DEBUG_SPRINT,
|
||||
|
||||
@@ -5,9 +5,28 @@ extends RefCounted
|
||||
## This adapter owns no movement state and may be replaced independently of the
|
||||
## local movement controller.
|
||||
|
||||
const RENDER_SANDBOX_PROFILE_ID := &"RenderSandbox"
|
||||
const BLIZZLIKE_335_PROFILE_ID := &"Blizzlike335"
|
||||
|
||||
var input_profile_id: StringName:
|
||||
get:
|
||||
return _input_profile_id
|
||||
|
||||
var _input_profile_id: StringName
|
||||
|
||||
|
||||
## Selects one supported action layout. Unknown profiles fail closed to Blizzlike335.
|
||||
func _init(requested_profile_id: StringName = BLIZZLIKE_335_PROFILE_ID) -> void:
|
||||
_input_profile_id = (
|
||||
RENDER_SANDBOX_PROFILE_ID
|
||||
if requested_profile_id == RENDER_SANDBOX_PROFILE_ID
|
||||
else BLIZZLIKE_335_PROFILE_ID
|
||||
)
|
||||
|
||||
|
||||
## Samples the current Input singleton state for one physics tick.
|
||||
func sample_move_intent() -> MoveIntent:
|
||||
if _input_profile_id == RENDER_SANDBOX_PROFILE_ID:
|
||||
return compose_move_intent(
|
||||
Input.get_action_strength(PlayerInputActions.MOVE_FORWARD),
|
||||
Input.get_action_strength(PlayerInputActions.MOVE_BACKWARD),
|
||||
@@ -17,9 +36,55 @@ func sample_move_intent() -> MoveIntent:
|
||||
Input.get_action_strength(PlayerInputActions.DEBUG_FLY_DOWN),
|
||||
Input.is_action_pressed(PlayerInputActions.DEBUG_SPRINT)
|
||||
)
|
||||
return compose_blizzlike_335_move_intent(
|
||||
Input.get_action_strength(PlayerInputActions.MOVE_FORWARD),
|
||||
Input.get_action_strength(PlayerInputActions.MOVE_BACKWARD),
|
||||
Input.get_action_strength(PlayerInputActions.BLIZZLIKE_STRAFE_LEFT),
|
||||
Input.get_action_strength(PlayerInputActions.BLIZZLIKE_STRAFE_RIGHT),
|
||||
Input.get_action_strength(PlayerInputActions.TURN_LEFT),
|
||||
Input.get_action_strength(PlayerInputActions.TURN_RIGHT),
|
||||
Input.is_action_pressed(PlayerInputActions.CAMERA_ROTATE)
|
||||
)
|
||||
|
||||
|
||||
## Builds a normalized intent from action strengths for deterministic tests and
|
||||
## Builds the compatibility layout: Q/E always strafe; A/D turn unless
|
||||
## right-mouse camera rotation is active, when A/D also strafe. Default bindings
|
||||
## come from build 12340; conditional routing is reference-implementation evidence.
|
||||
static func compose_blizzlike_335_move_intent(
|
||||
forward_strength: float,
|
||||
backward_strength: float,
|
||||
strafe_left_strength: float,
|
||||
strafe_right_strength: float,
|
||||
turn_left_strength: float,
|
||||
turn_right_strength: float,
|
||||
camera_rotate_active: bool
|
||||
) -> MoveIntent:
|
||||
var effective_strafe_left := strafe_left_strength
|
||||
var effective_strafe_right := strafe_right_strength
|
||||
var turn_axis := clampf(turn_right_strength, 0.0, 1.0) - clampf(turn_left_strength, 0.0, 1.0)
|
||||
if camera_rotate_active:
|
||||
effective_strafe_left += turn_left_strength
|
||||
effective_strafe_right += turn_right_strength
|
||||
turn_axis = 0.0
|
||||
var move_intent := compose_move_intent(
|
||||
forward_strength,
|
||||
backward_strength,
|
||||
effective_strafe_left,
|
||||
effective_strafe_right,
|
||||
0.0,
|
||||
0.0,
|
||||
false
|
||||
)
|
||||
return MoveIntent.new(
|
||||
move_intent.forward_axis,
|
||||
move_intent.strafe_axis,
|
||||
0.0,
|
||||
false,
|
||||
turn_axis
|
||||
)
|
||||
|
||||
|
||||
## Builds a normalized sandbox intent from action strengths for deterministic tests and
|
||||
## alternative input adapters. Strengths outside `[0, 1]` are clamped.
|
||||
static func compose_move_intent(
|
||||
forward_strength: float,
|
||||
|
||||
@@ -13,25 +13,34 @@ var is_flight_enabled: bool:
|
||||
get:
|
||||
return _is_flight_enabled
|
||||
|
||||
var movement_profile_id: StringName:
|
||||
get:
|
||||
return _movement_capabilities.profile_id
|
||||
|
||||
var _run_speed_units_per_second: float
|
||||
var _backward_speed_units_per_second: float
|
||||
var _strafe_speed_units_per_second: float
|
||||
var _flight_vertical_speed_units_per_second: float
|
||||
var _acceleration_units_per_second_squared: float
|
||||
var _sandbox_sprint_multiplier: float
|
||||
var _keyboard_turn_speed_radians_per_second: float
|
||||
var _movement_capabilities: PlayerMovementCapabilities
|
||||
var _godot_world_velocity_units_per_second := Vector3.ZERO
|
||||
var _is_flight_enabled := false
|
||||
|
||||
|
||||
## Creates a movement state with explicit Godot-unit speeds and acceleration.
|
||||
## Creates a movement state with explicit Godot-unit speeds, acceleration and capabilities.
|
||||
## The current compatibility renderer treats one Godot unit as one WoW yard.
|
||||
## A null capability input fails closed to Blizzlike335 debug permissions.
|
||||
func _init(
|
||||
run_speed_units_per_second: float,
|
||||
backward_speed_units_per_second: float,
|
||||
strafe_speed_units_per_second: float,
|
||||
flight_vertical_speed_units_per_second: float,
|
||||
acceleration_units_per_second_squared: float,
|
||||
sandbox_sprint_multiplier: float
|
||||
sandbox_sprint_multiplier: float,
|
||||
movement_capabilities: PlayerMovementCapabilities = null,
|
||||
keyboard_turn_speed_radians_per_second: float = PI
|
||||
) -> void:
|
||||
_run_speed_units_per_second = run_speed_units_per_second
|
||||
_backward_speed_units_per_second = backward_speed_units_per_second
|
||||
@@ -39,11 +48,32 @@ func _init(
|
||||
_flight_vertical_speed_units_per_second = flight_vertical_speed_units_per_second
|
||||
_acceleration_units_per_second_squared = acceleration_units_per_second_squared
|
||||
_sandbox_sprint_multiplier = sandbox_sprint_multiplier
|
||||
_keyboard_turn_speed_radians_per_second = maxf(
|
||||
keyboard_turn_speed_radians_per_second,
|
||||
0.0
|
||||
)
|
||||
_movement_capabilities = (
|
||||
movement_capabilities
|
||||
if movement_capabilities != null
|
||||
else PlayerMovementCapabilities.blizzlike_335()
|
||||
)
|
||||
|
||||
|
||||
## Toggles sandbox free flight and clears velocity exactly like the pre-M02 controller.
|
||||
## Returns the new flight state.
|
||||
## Returns the Godot yaw delta for one signed keyboard-turn request.
|
||||
## Positive intent turns right, which is negative rotation around Godot +Y.
|
||||
func calculate_yaw_delta_radians(move_intent: MoveIntent, delta_seconds: float) -> float:
|
||||
return (
|
||||
-move_intent.turn_axis
|
||||
* _keyboard_turn_speed_radians_per_second
|
||||
* maxf(delta_seconds, 0.0)
|
||||
)
|
||||
|
||||
|
||||
## Toggles sandbox free flight and clears velocity when the profile permits it.
|
||||
## Returns the resulting state; rejected compatibility-profile requests return false.
|
||||
func toggle_sandbox_flight() -> bool:
|
||||
if not _movement_capabilities.allows_debug_free_flight:
|
||||
return false
|
||||
_is_flight_enabled = not _is_flight_enabled
|
||||
_godot_world_velocity_units_per_second = Vector3.ZERO
|
||||
return _is_flight_enabled
|
||||
@@ -83,7 +113,11 @@ func _calculate_target_velocity(move_intent: MoveIntent, godot_world_movement_ba
|
||||
if move_intent.forward_axis > 0.0
|
||||
else _backward_speed_units_per_second
|
||||
)
|
||||
var speed_multiplier := _sandbox_sprint_multiplier if move_intent.is_sprint_requested else 1.0
|
||||
var speed_multiplier := (
|
||||
_sandbox_sprint_multiplier
|
||||
if move_intent.is_sprint_requested and _movement_capabilities.allows_debug_sprint
|
||||
else 1.0
|
||||
)
|
||||
var target_velocity := (
|
||||
forward_direction * move_intent.forward_axis * forward_speed
|
||||
+ right_direction * move_intent.strafe_axis * _strafe_speed_units_per_second
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
class_name PlayerMovementCapabilities
|
||||
extends RefCounted
|
||||
|
||||
## Immutable local-player movement capability selection.
|
||||
## Unknown or missing profile identifiers collapse to the safe Blizzlike335
|
||||
## capability set; only RenderSandbox enables debug sprint and free flight.
|
||||
|
||||
const BLIZZLIKE_335_PROFILE_ID: StringName = &"Blizzlike335"
|
||||
const RENDER_SANDBOX_PROFILE_ID: StringName = &"RenderSandbox"
|
||||
|
||||
var profile_id: StringName:
|
||||
get:
|
||||
return _profile_id
|
||||
|
||||
var allows_debug_sprint: bool:
|
||||
get:
|
||||
return _allows_debug_sprint
|
||||
|
||||
var allows_debug_free_flight: bool:
|
||||
get:
|
||||
return _allows_debug_free_flight
|
||||
|
||||
var _profile_id: StringName
|
||||
var _allows_debug_sprint: bool
|
||||
var _allows_debug_free_flight: bool
|
||||
|
||||
|
||||
func _init(requested_profile_id: StringName = BLIZZLIKE_335_PROFILE_ID) -> void:
|
||||
if requested_profile_id == RENDER_SANDBOX_PROFILE_ID:
|
||||
_profile_id = RENDER_SANDBOX_PROFILE_ID
|
||||
_allows_debug_sprint = true
|
||||
_allows_debug_free_flight = true
|
||||
return
|
||||
_profile_id = BLIZZLIKE_335_PROFILE_ID
|
||||
_allows_debug_sprint = false
|
||||
_allows_debug_free_flight = false
|
||||
|
||||
|
||||
## Returns the debug-enabled capability set used by current renderer scenes.
|
||||
static func render_sandbox() -> PlayerMovementCapabilities:
|
||||
return PlayerMovementCapabilities.new(RENDER_SANDBOX_PROFILE_ID)
|
||||
|
||||
|
||||
## Returns the compatibility-safe capability set with debug movement disabled.
|
||||
## This name identifies the intended compatibility profile, not proven 1:1 movement.
|
||||
static func blizzlike_335() -> PlayerMovementCapabilities:
|
||||
return PlayerMovementCapabilities.new(BLIZZLIKE_335_PROFILE_ID)
|
||||
|
||||
|
||||
## Maps scene/application profile configuration to a known capability set.
|
||||
## Unknown identifiers fail closed to Blizzlike335.
|
||||
static func for_profile_id(requested_profile_id: StringName) -> PlayerMovementCapabilities:
|
||||
return PlayerMovementCapabilities.new(requested_profile_id)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cdnulu22drbdw
|
||||
@@ -0,0 +1,104 @@
|
||||
class_name EntityPresentationSnapshot
|
||||
extends RefCounted
|
||||
|
||||
## Immutable renderer input for one session-local world entity visual.
|
||||
## Gameplay authority, identity allocation and asset selection remain external.
|
||||
|
||||
const CONTRACT_VERSION := 1
|
||||
const SCRIPT_PATH := "res://src/render/entities/entity_presentation_snapshot.gd"
|
||||
const ENTITY_ID_SCRIPT := preload("res://src/domain/identity/entity_id.gd")
|
||||
const GODOT_WORLD_POSITION_SCRIPT := preload("res://src/domain/coordinates/godot_world_position.gd")
|
||||
|
||||
var is_valid: bool:
|
||||
get:
|
||||
return _failure_code.is_empty()
|
||||
|
||||
var failure_code: StringName:
|
||||
get:
|
||||
return _failure_code
|
||||
|
||||
var entity_id: RefCounted:
|
||||
get:
|
||||
return _entity_id
|
||||
|
||||
var world_position: RefCounted:
|
||||
get:
|
||||
return _world_position
|
||||
|
||||
var visual_scene_path: String:
|
||||
get:
|
||||
return _visual_scene_path
|
||||
|
||||
var yaw_radians: float:
|
||||
get:
|
||||
return _yaw_radians
|
||||
|
||||
var uniform_scale: float:
|
||||
get:
|
||||
return _uniform_scale
|
||||
|
||||
var is_visible: bool:
|
||||
get:
|
||||
return _is_visible
|
||||
|
||||
var _failure_code: StringName
|
||||
var _entity_id: RefCounted
|
||||
var _world_position: RefCounted
|
||||
var _visual_scene_path: String
|
||||
var _yaw_radians: float
|
||||
var _uniform_scale: float
|
||||
var _is_visible: bool
|
||||
|
||||
|
||||
## Creates a version-1 full visual snapshot. Invalid values remain inspectable and
|
||||
## are rejected without scene mutation by the facade/presenter.
|
||||
static func create(
|
||||
entity_id_value: RefCounted,
|
||||
world_position_value: RefCounted,
|
||||
visual_scene_path_value: String,
|
||||
yaw_radians_value: float = 0.0,
|
||||
uniform_scale_value: float = 1.0,
|
||||
is_visible_value: bool = true
|
||||
) -> RefCounted:
|
||||
return load(SCRIPT_PATH).new(
|
||||
entity_id_value,
|
||||
world_position_value,
|
||||
visual_scene_path_value,
|
||||
yaw_radians_value,
|
||||
uniform_scale_value,
|
||||
is_visible_value
|
||||
)
|
||||
|
||||
|
||||
func _init(
|
||||
entity_id_value: RefCounted,
|
||||
world_position_value: RefCounted,
|
||||
visual_scene_path_value: String,
|
||||
yaw_radians_value: float,
|
||||
uniform_scale_value: float,
|
||||
is_visible_value: bool
|
||||
) -> void:
|
||||
_entity_id = entity_id_value
|
||||
_world_position = world_position_value
|
||||
_visual_scene_path = visual_scene_path_value.strip_edges()
|
||||
_yaw_radians = yaw_radians_value
|
||||
_uniform_scale = uniform_scale_value
|
||||
_is_visible = is_visible_value
|
||||
_failure_code = _validate()
|
||||
|
||||
|
||||
func _validate() -> StringName:
|
||||
if _entity_id == null or _entity_id.get_script() != ENTITY_ID_SCRIPT or not bool(_entity_id.call("is_valid")):
|
||||
return &"entity_presentation_invalid_entity_id"
|
||||
if _world_position == null or _world_position.get_script() != GODOT_WORLD_POSITION_SCRIPT:
|
||||
return &"entity_presentation_invalid_world_position"
|
||||
for coordinate_name in [&"x_units", &"y_units", &"z_units"]:
|
||||
if not is_finite(float(_world_position.get(coordinate_name))):
|
||||
return &"entity_presentation_position_not_finite"
|
||||
if _visual_scene_path.is_empty():
|
||||
return &"entity_presentation_visual_path_empty"
|
||||
if not is_finite(_yaw_radians):
|
||||
return &"entity_presentation_yaw_not_finite"
|
||||
if not is_finite(_uniform_scale) or _uniform_scale <= 0.0:
|
||||
return &"entity_presentation_scale_invalid"
|
||||
return &""
|
||||
@@ -0,0 +1 @@
|
||||
uid://djvnorls0yf06
|
||||
@@ -0,0 +1,130 @@
|
||||
@tool
|
||||
class_name WorldEntityPresenter
|
||||
extends Node3D
|
||||
|
||||
## Main-thread owner of visual subtrees for session-local world entities.
|
||||
## The full immutable snapshot is reapplied idempotently by entity identity.
|
||||
|
||||
const ENTITY_PRESENTATION_SNAPSHOT_SCRIPT := preload("res://src/render/entities/entity_presentation_snapshot.gd")
|
||||
const ENTITY_ID_SCRIPT := preload("res://src/domain/identity/entity_id.gd")
|
||||
|
||||
var _presented_entities: Dictionary = {}
|
||||
|
||||
|
||||
## Creates or updates one entity visual. A changed visual path is instantiated
|
||||
## successfully before the previous subtree is detached, so failed replacement
|
||||
## retains the last valid presentation.
|
||||
func present_entity(entity_presentation_snapshot: RefCounted) -> bool:
|
||||
if not _is_valid_snapshot(entity_presentation_snapshot):
|
||||
return false
|
||||
var entity_id: RefCounted = entity_presentation_snapshot.get("entity_id")
|
||||
var entity_key := _entity_key(entity_id)
|
||||
var visual_scene_path := String(entity_presentation_snapshot.get("visual_scene_path"))
|
||||
var current_entry: Dictionary = _presented_entities.get(entity_key, {})
|
||||
var visual_root := current_entry.get("visual_root") as Node3D
|
||||
if visual_root == null or not is_instance_valid(visual_root) or String(current_entry.get("visual_scene_path", "")) != visual_scene_path:
|
||||
var replacement_root := _instantiate_visual_root(entity_id, visual_scene_path)
|
||||
if replacement_root == null:
|
||||
return false
|
||||
if visual_root != null and is_instance_valid(visual_root):
|
||||
remove_child(visual_root)
|
||||
add_child(replacement_root)
|
||||
_apply_snapshot_transform(replacement_root, entity_presentation_snapshot)
|
||||
if visual_root != null and is_instance_valid(visual_root):
|
||||
visual_root.queue_free()
|
||||
visual_root = replacement_root
|
||||
else:
|
||||
_apply_snapshot_transform(visual_root, entity_presentation_snapshot)
|
||||
_presented_entities[entity_key] = {
|
||||
"visual_root": visual_root,
|
||||
"visual_scene_path": visual_scene_path,
|
||||
"snapshot": entity_presentation_snapshot,
|
||||
}
|
||||
return true
|
||||
|
||||
|
||||
## Removes one owned entity visual. Returns false for an invalid/unknown identity.
|
||||
func remove_entity(entity_id: RefCounted) -> bool:
|
||||
if entity_id == null or entity_id.get_script() != ENTITY_ID_SCRIPT or not bool(entity_id.call("is_valid")):
|
||||
return false
|
||||
var entity_key := _entity_key(entity_id)
|
||||
if not _presented_entities.has(entity_key):
|
||||
return false
|
||||
var entry: Dictionary = _presented_entities[entity_key]
|
||||
var visual_root := entry.get("visual_root") as Node3D
|
||||
_presented_entities.erase(entity_key)
|
||||
if visual_root != null and is_instance_valid(visual_root):
|
||||
remove_child(visual_root)
|
||||
visual_root.queue_free()
|
||||
return true
|
||||
|
||||
|
||||
## Returns detached scalar diagnostics without exposing owned Nodes or snapshots.
|
||||
func entity_presentation_snapshot() -> Dictionary:
|
||||
var entities: Array[Dictionary] = []
|
||||
var sorted_entity_keys: Array = _presented_entities.keys()
|
||||
sorted_entity_keys.sort()
|
||||
for entity_key_variant in sorted_entity_keys:
|
||||
var entity_key := String(entity_key_variant)
|
||||
var entry: Dictionary = _presented_entities[entity_key]
|
||||
var snapshot: RefCounted = entry.get("snapshot")
|
||||
entities.append({
|
||||
"entity_key": entity_key,
|
||||
"visual_scene_path": String(entry.get("visual_scene_path", "")),
|
||||
"visible": bool(snapshot.get("is_visible")),
|
||||
})
|
||||
return {
|
||||
"contract_version": ENTITY_PRESENTATION_SNAPSHOT_SCRIPT.CONTRACT_VERSION,
|
||||
"entity_count": entities.size(),
|
||||
"entities": entities,
|
||||
}
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
_presented_entities.clear()
|
||||
|
||||
|
||||
func _is_valid_snapshot(entity_presentation_snapshot: RefCounted) -> bool:
|
||||
return (
|
||||
entity_presentation_snapshot != null
|
||||
and entity_presentation_snapshot.get_script() == ENTITY_PRESENTATION_SNAPSHOT_SCRIPT
|
||||
and bool(entity_presentation_snapshot.get("is_valid"))
|
||||
)
|
||||
|
||||
|
||||
func _entity_key(entity_id: RefCounted) -> String:
|
||||
return String(entity_id.call("to_debug_key"))
|
||||
|
||||
|
||||
func _instantiate_visual_root(entity_id: RefCounted, visual_scene_path: String) -> Node3D:
|
||||
if not ResourceLoader.exists(visual_scene_path, "PackedScene"):
|
||||
push_warning("WorldEntityPresenter cannot find visual scene: %s" % visual_scene_path)
|
||||
return null
|
||||
var visual_scene := load(visual_scene_path) as PackedScene
|
||||
if visual_scene == null:
|
||||
push_warning("WorldEntityPresenter cannot load visual scene: %s" % visual_scene_path)
|
||||
return null
|
||||
var visual_instance := visual_scene.instantiate()
|
||||
if not (visual_instance is Node3D):
|
||||
visual_instance.free()
|
||||
push_warning("WorldEntityPresenter visual scene root is not Node3D: %s" % visual_scene_path)
|
||||
return null
|
||||
var visual_root := visual_instance as Node3D
|
||||
visual_root.name = "EntityVisual_%020d" % int(entity_id.get("session_sequence"))
|
||||
return visual_root
|
||||
|
||||
|
||||
func _apply_snapshot_transform(visual_root: Node3D, entity_presentation_snapshot: RefCounted) -> void:
|
||||
var world_position: RefCounted = entity_presentation_snapshot.get("world_position")
|
||||
var position_units := Vector3(
|
||||
float(world_position.get("x_units")),
|
||||
float(world_position.get("y_units")),
|
||||
float(world_position.get("z_units"))
|
||||
)
|
||||
var uniform_scale := float(entity_presentation_snapshot.get("uniform_scale"))
|
||||
var yaw_radians := float(entity_presentation_snapshot.get("yaw_radians"))
|
||||
visual_root.global_transform = Transform3D(
|
||||
Basis(Vector3.UP, yaw_radians).scaled(Vector3.ONE * uniform_scale),
|
||||
position_units
|
||||
)
|
||||
visual_root.visible = bool(entity_presentation_snapshot.get("is_visible"))
|
||||
@@ -0,0 +1 @@
|
||||
uid://b0keiyi1wuu5i
|
||||
@@ -0,0 +1,40 @@
|
||||
class_name WorldEnvironmentSnapshot
|
||||
extends RefCounted
|
||||
|
||||
## Immutable renderer input describing the authoritative time within one world day.
|
||||
## DBC light, area, fog and skybox selection remain owned by the sky controller.
|
||||
|
||||
const HOURS_PER_DAY := 24.0
|
||||
const SCRIPT_PATH := "res://src/render/environment/world_environment_snapshot.gd"
|
||||
|
||||
var is_valid: bool:
|
||||
get:
|
||||
return _is_valid
|
||||
|
||||
var time_of_day_hours: float:
|
||||
get:
|
||||
return _time_of_day_hours
|
||||
|
||||
var failure_code: StringName:
|
||||
get:
|
||||
return _failure_code
|
||||
|
||||
var _is_valid: bool
|
||||
var _time_of_day_hours: float
|
||||
var _failure_code: StringName
|
||||
|
||||
|
||||
## Creates a snapshot and normalizes finite input into the half-open [0, 24) day.
|
||||
## Non-finite input produces an explicit invalid value instead of renderer state.
|
||||
static func at_time_of_day(time_of_day_hours_value: float) -> RefCounted:
|
||||
return load(SCRIPT_PATH).new(time_of_day_hours_value)
|
||||
|
||||
|
||||
func _init(time_of_day_hours_value: float) -> void:
|
||||
_is_valid = is_finite(time_of_day_hours_value)
|
||||
_time_of_day_hours = (
|
||||
fposmod(time_of_day_hours_value, HOURS_PER_DAY)
|
||||
if _is_valid
|
||||
else 0.0
|
||||
)
|
||||
_failure_code = &"" if _is_valid else &"environment_time_not_finite"
|
||||
@@ -0,0 +1 @@
|
||||
uid://dass4mi3ex2fu
|
||||
@@ -0,0 +1,167 @@
|
||||
class_name AdtWaterLoadPipelineState
|
||||
extends RefCounted
|
||||
|
||||
## Owns ADT water request, active-task and worker-result bookkeeping. The caller
|
||||
## owns WorkerThreadPool operations, ADT parsing, permits and scene finalization.
|
||||
|
||||
var _pending_requests: Array[Dictionary] = []
|
||||
var _queued_tile_keys: Dictionary = {}
|
||||
var _active_task_ids_by_tile_key: Dictionary = {}
|
||||
var _result_mutex := Mutex.new()
|
||||
var _results: Array[Dictionary] = []
|
||||
|
||||
|
||||
## Appends one tile/path request in FIFO order. Invalid, queued or active tile
|
||||
## keys are rejected without mutation.
|
||||
func enqueue_request(tile_key: String, adt_path: String) -> bool:
|
||||
if tile_key.is_empty() or adt_path.is_empty():
|
||||
return false
|
||||
if _queued_tile_keys.has(tile_key) or _active_task_ids_by_tile_key.has(tile_key):
|
||||
return false
|
||||
_pending_requests.append({
|
||||
"key": tile_key,
|
||||
"path": adt_path,
|
||||
})
|
||||
_queued_tile_keys[tile_key] = true
|
||||
return true
|
||||
|
||||
|
||||
## Removes and returns the oldest request, also releasing its queued-key marker.
|
||||
## An empty Dictionary means no pending request exists.
|
||||
func take_next_request() -> Dictionary:
|
||||
if _pending_requests.is_empty():
|
||||
return {}
|
||||
var request: Dictionary = _pending_requests.pop_front()
|
||||
_queued_tile_keys.erase(String(request.get("key", "")))
|
||||
return request
|
||||
|
||||
|
||||
## Removes all pending requests for one tile without affecting active work.
|
||||
## Returns the number of removed FIFO entries.
|
||||
func cancel_pending_requests(tile_key: String) -> int:
|
||||
if tile_key.is_empty():
|
||||
return 0
|
||||
_queued_tile_keys.erase(tile_key)
|
||||
var removed_count := 0
|
||||
for request_index in range(_pending_requests.size() - 1, -1, -1):
|
||||
var request: Dictionary = _pending_requests[request_index]
|
||||
if String(request.get("key", "")) != tile_key:
|
||||
continue
|
||||
_pending_requests.remove_at(request_index)
|
||||
removed_count += 1
|
||||
return removed_count
|
||||
|
||||
|
||||
## Records one caller-started worker task. The opaque task ID is retained until
|
||||
## the caller waits for and completes it.
|
||||
func remember_active_task(tile_key: String, task_id: int) -> bool:
|
||||
if tile_key.is_empty() or _active_task_ids_by_tile_key.has(tile_key):
|
||||
return false
|
||||
_active_task_ids_by_tile_key[tile_key] = task_id
|
||||
return true
|
||||
|
||||
|
||||
## Returns whether a worker task is active for the tile.
|
||||
func has_active_task(tile_key: String) -> bool:
|
||||
return not tile_key.is_empty() and _active_task_ids_by_tile_key.has(tile_key)
|
||||
|
||||
|
||||
## Returns the opaque active task ID, or -1 for an unknown tile.
|
||||
func active_task_id_for(tile_key: String) -> int:
|
||||
return int(_active_task_ids_by_tile_key.get(tile_key, -1))
|
||||
|
||||
|
||||
## Removes and returns one completed task ID, or -1 for an unknown tile.
|
||||
func complete_active_task(tile_key: String) -> int:
|
||||
if tile_key.is_empty() or not _active_task_ids_by_tile_key.has(tile_key):
|
||||
return -1
|
||||
var task_id := int(_active_task_ids_by_tile_key[tile_key])
|
||||
_active_task_ids_by_tile_key.erase(tile_key)
|
||||
return task_id
|
||||
|
||||
|
||||
## Returns a detached snapshot of opaque task IDs for orderly shutdown waiting.
|
||||
func active_task_ids_snapshot() -> Array:
|
||||
return _active_task_ids_by_tile_key.values().duplicate()
|
||||
|
||||
|
||||
## Publishes one parsed ADT result from a worker thread. Payload identity is
|
||||
## retained; the caller must not mutate it after publication.
|
||||
func publish_result(tile_key: String, adt_path: String, water_data: Dictionary) -> bool:
|
||||
if tile_key.is_empty() or adt_path.is_empty():
|
||||
return false
|
||||
_result_mutex.lock()
|
||||
_results.append({
|
||||
"key": tile_key,
|
||||
"path": adt_path,
|
||||
"data": water_data,
|
||||
})
|
||||
_result_mutex.unlock()
|
||||
return true
|
||||
|
||||
|
||||
## Returns whether a worker result is currently ready.
|
||||
func has_result() -> bool:
|
||||
_result_mutex.lock()
|
||||
var result_available := not _results.is_empty()
|
||||
_result_mutex.unlock()
|
||||
return result_available
|
||||
|
||||
|
||||
## Removes and returns the oldest worker result. An empty Dictionary means no
|
||||
## result was ready. Intended for the serialized renderer main thread.
|
||||
func pop_result() -> Dictionary:
|
||||
_result_mutex.lock()
|
||||
var result: Dictionary = {}
|
||||
if not _results.is_empty():
|
||||
result = _results.pop_front()
|
||||
_result_mutex.unlock()
|
||||
return result
|
||||
|
||||
|
||||
## Returns pending plus active load count for existing renderer metrics.
|
||||
func total_load_count() -> int:
|
||||
return _pending_requests.size() + _active_task_ids_by_tile_key.size()
|
||||
|
||||
|
||||
## Returns the pending request count.
|
||||
func pending_request_count() -> int:
|
||||
return _pending_requests.size()
|
||||
|
||||
|
||||
## Returns the active worker-task count.
|
||||
func active_task_count() -> int:
|
||||
return _active_task_ids_by_tile_key.size()
|
||||
|
||||
|
||||
## Clears bookkeeping and queued results without interrupting worker tasks.
|
||||
## Orderly shutdown must wait the task-ID snapshot before calling this method.
|
||||
func clear() -> void:
|
||||
_pending_requests.clear()
|
||||
_queued_tile_keys.clear()
|
||||
_active_task_ids_by_tile_key.clear()
|
||||
_result_mutex.lock()
|
||||
_results.clear()
|
||||
_result_mutex.unlock()
|
||||
|
||||
|
||||
## Returns detached FIFO/task/result scalar diagnostics without water payloads.
|
||||
func diagnostic_snapshot() -> Dictionary:
|
||||
var pending_requests: Array[Dictionary] = []
|
||||
for request in _pending_requests:
|
||||
pending_requests.append(request.duplicate())
|
||||
var active_tile_keys: Array = _active_task_ids_by_tile_key.keys()
|
||||
active_tile_keys.sort()
|
||||
_result_mutex.lock()
|
||||
var result_records: Array[Dictionary] = []
|
||||
for result in _results:
|
||||
result_records.append({
|
||||
"key": String(result.get("key", "")),
|
||||
"path": String(result.get("path", "")),
|
||||
})
|
||||
_result_mutex.unlock()
|
||||
return {
|
||||
"pending_requests": pending_requests,
|
||||
"active_tile_keys": active_tile_keys.duplicate(),
|
||||
"result_records": result_records,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://pwqm3n25pnn7
|
||||
@@ -0,0 +1,40 @@
|
||||
class_name AdtWaterSceneFinalizer
|
||||
extends RefCounted
|
||||
|
||||
## Builds and attaches one ADT water subtree on the renderer main thread. The
|
||||
## supplied tile root owns the returned subtree; this service retains no state.
|
||||
|
||||
const ADT_BUILDER_SCRIPT := preload("res://addons/mpq_extractor/loaders/adt_builder.gd")
|
||||
|
||||
|
||||
## Builds water from parsed ADT data and attaches it once to [param tile_root].
|
||||
## [param tile_origin_godot_units] is the existing Godot-world tile origin used
|
||||
## by [ADTBuilder]. When [param editor_owner] is non-null and is an ancestor,
|
||||
## ownership is assigned recursively for persisted Editor previews. Returns the
|
||||
## attached borrowed root, or null for empty/invalid input or dry ADT data.
|
||||
## This method mutates the SceneTree and must run on the main thread.
|
||||
func attach_water_scene(
|
||||
water_data: Dictionary,
|
||||
tile_origin_godot_units: Vector3,
|
||||
tile_root: Node,
|
||||
editor_owner: Node = null) -> Node3D:
|
||||
if water_data.is_empty() or tile_root == null or not is_instance_valid(tile_root):
|
||||
return null
|
||||
|
||||
var water_root: Node3D = ADT_BUILDER_SCRIPT.build_tile_water_scene(
|
||||
water_data,
|
||||
tile_origin_godot_units
|
||||
)
|
||||
if water_root == null:
|
||||
return null
|
||||
|
||||
tile_root.add_child(water_root)
|
||||
if editor_owner != null and is_instance_valid(editor_owner):
|
||||
_assign_owner_recursive(water_root, editor_owner)
|
||||
return water_root
|
||||
|
||||
|
||||
func _assign_owner_recursive(generated_node: Node, editor_owner: Node) -> void:
|
||||
generated_node.owner = editor_owner
|
||||
for child in generated_node.get_children():
|
||||
_assign_owner_recursive(child, editor_owner)
|
||||
@@ -0,0 +1 @@
|
||||
uid://ck4sdc8a3kg00
|
||||
@@ -0,0 +1,30 @@
|
||||
class_name M2BuildBatchPlanner
|
||||
extends RefCounted
|
||||
|
||||
## Stateless M2 build batch sizing and group-cursor progression.
|
||||
|
||||
|
||||
## Plans one static or animated build batch. The returned Dictionary is detached
|
||||
## and contains effective_batch_size, batch_count, next_offset and group_complete.
|
||||
func plan_batch(
|
||||
transform_count: int,
|
||||
current_offset: int,
|
||||
has_animated_prototype: bool,
|
||||
animated_batch_limit: int,
|
||||
static_batch_limit: int
|
||||
) -> Dictionary:
|
||||
var selected_batch_limit := (
|
||||
animated_batch_limit if has_animated_prototype else static_batch_limit
|
||||
)
|
||||
var effective_batch_size := maxi(1, selected_batch_limit)
|
||||
var remaining_transform_count := maxi(0, transform_count - current_offset)
|
||||
var batch_count := mini(effective_batch_size, remaining_transform_count)
|
||||
var is_group_complete := (
|
||||
batch_count <= 0 or current_offset + batch_count >= transform_count
|
||||
)
|
||||
return {
|
||||
"effective_batch_size": effective_batch_size,
|
||||
"batch_count": batch_count,
|
||||
"next_offset": 0 if is_group_complete else current_offset + batch_count,
|
||||
"group_complete": is_group_complete,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://d0my7yntfsvuy
|
||||
@@ -0,0 +1,103 @@
|
||||
class_name M2MeshLoadPipelineState
|
||||
extends RefCounted
|
||||
|
||||
## Owns static M2 threaded-load request records and the terminal finalize FIFO.
|
||||
## The caller owns ResourceLoader I/O, permits, Mesh caches and materialization.
|
||||
|
||||
var _request_by_normalized_path: Dictionary = {}
|
||||
var _finalize_records: Array[Dictionary] = []
|
||||
|
||||
|
||||
## Records one normalized M2 path and ResourceLoader path. Empty or duplicate
|
||||
## values are rejected without mutation.
|
||||
func remember_request(normalized_relative_path: String, resource_path: String) -> bool:
|
||||
if normalized_relative_path.is_empty() or resource_path.is_empty():
|
||||
return false
|
||||
if _request_by_normalized_path.has(normalized_relative_path):
|
||||
return false
|
||||
_request_by_normalized_path[normalized_relative_path] = {
|
||||
"normalized": normalized_relative_path,
|
||||
"path": resource_path,
|
||||
}
|
||||
return true
|
||||
|
||||
|
||||
## Returns whether one normalized path currently has a pending threaded request.
|
||||
func has_request(normalized_relative_path: String) -> bool:
|
||||
return (
|
||||
not normalized_relative_path.is_empty()
|
||||
and _request_by_normalized_path.has(normalized_relative_path)
|
||||
)
|
||||
|
||||
|
||||
## Returns detached pending records in their existing Dictionary insertion order.
|
||||
func request_records_snapshot() -> Array[Dictionary]:
|
||||
var records: Array[Dictionary] = []
|
||||
for request_variant in _request_by_normalized_path.values():
|
||||
var request: Dictionary = request_variant
|
||||
records.append(request.duplicate())
|
||||
return records
|
||||
|
||||
|
||||
## Removes one pending request and appends its terminal status to the finalize
|
||||
## FIFO. Unknown paths are rejected without mutation.
|
||||
func complete_request(normalized_relative_path: String, terminal_status: int) -> bool:
|
||||
if not _request_by_normalized_path.has(normalized_relative_path):
|
||||
return false
|
||||
var request: Dictionary = _request_by_normalized_path[normalized_relative_path]
|
||||
_request_by_normalized_path.erase(normalized_relative_path)
|
||||
var finalize_record := request.duplicate()
|
||||
finalize_record["status"] = terminal_status
|
||||
_finalize_records.append(finalize_record)
|
||||
return true
|
||||
|
||||
|
||||
## Removes one pending request without enqueuing finalization. Returns whether a
|
||||
## record was removed.
|
||||
func discard_request(normalized_relative_path: String) -> bool:
|
||||
return _request_by_normalized_path.erase(normalized_relative_path)
|
||||
|
||||
|
||||
## Returns whether a terminal record is waiting for budgeted finalization.
|
||||
func has_finalize_record() -> bool:
|
||||
return not _finalize_records.is_empty()
|
||||
|
||||
|
||||
## Removes and returns the oldest terminal record. Empty means none is ready.
|
||||
func pop_finalize_record() -> Dictionary:
|
||||
if _finalize_records.is_empty():
|
||||
return {}
|
||||
return _finalize_records.pop_front()
|
||||
|
||||
|
||||
## Returns pending plus terminal-finalize work for existing renderer metrics.
|
||||
func total_work_count() -> int:
|
||||
return _request_by_normalized_path.size() + _finalize_records.size()
|
||||
|
||||
|
||||
## Returns the pending request count.
|
||||
func pending_request_count() -> int:
|
||||
return _request_by_normalized_path.size()
|
||||
|
||||
|
||||
## Returns the terminal finalize FIFO count.
|
||||
func finalize_record_count() -> int:
|
||||
return _finalize_records.size()
|
||||
|
||||
|
||||
## Clears request/finalize bookkeeping. The caller must drain ResourceLoader
|
||||
## requests before orderly shutdown clear.
|
||||
func clear() -> void:
|
||||
_request_by_normalized_path.clear()
|
||||
_finalize_records.clear()
|
||||
|
||||
|
||||
## Returns detached request and finalize records without loaded Resources/Meshes.
|
||||
func diagnostic_snapshot() -> Dictionary:
|
||||
var finalize_records: Array[Dictionary] = []
|
||||
for record in _finalize_records:
|
||||
finalize_records.append(record.duplicate())
|
||||
return {
|
||||
"requests": request_records_snapshot(),
|
||||
"finalize_records": finalize_records,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://dadsxuswq0tnd
|
||||
@@ -0,0 +1,50 @@
|
||||
class_name M2MeshResourceCacheState
|
||||
extends RefCounted
|
||||
|
||||
## Owns normalized-path references to prepared static M2 Mesh resources.
|
||||
## The caller owns loading, preparation, missing-state and materialization.
|
||||
|
||||
var _mesh_by_normalized_path: Dictionary = {}
|
||||
|
||||
|
||||
## Stores one prepared Mesh for a non-empty normalized M2 path. A later store
|
||||
## for the same path replaces the retained reference.
|
||||
func store_mesh(normalized_relative_path: String, mesh: Mesh) -> bool:
|
||||
if normalized_relative_path.is_empty() or mesh == null:
|
||||
return false
|
||||
_mesh_by_normalized_path[normalized_relative_path] = mesh
|
||||
return true
|
||||
|
||||
|
||||
## Returns whether one normalized path currently has a retained Mesh.
|
||||
func has_mesh(normalized_relative_path: String) -> bool:
|
||||
return (
|
||||
not normalized_relative_path.is_empty()
|
||||
and _mesh_by_normalized_path.has(normalized_relative_path)
|
||||
)
|
||||
|
||||
|
||||
## Returns the exact retained Mesh reference, or null for empty/unknown paths.
|
||||
func find_mesh(normalized_relative_path: String) -> Mesh:
|
||||
if not has_mesh(normalized_relative_path):
|
||||
return null
|
||||
return _mesh_by_normalized_path[normalized_relative_path] as Mesh
|
||||
|
||||
|
||||
## Returns the number of retained static M2 Mesh resources.
|
||||
func mesh_count() -> int:
|
||||
return _mesh_by_normalized_path.size()
|
||||
|
||||
|
||||
## Returns a detached insertion-order list of cached normalized paths.
|
||||
func normalized_paths_snapshot() -> Array[String]:
|
||||
var normalized_paths: Array[String] = []
|
||||
for normalized_path_variant in _mesh_by_normalized_path.keys():
|
||||
normalized_paths.append(String(normalized_path_variant))
|
||||
return normalized_paths
|
||||
|
||||
|
||||
## Releases all retained Mesh references. The loader calls this only after
|
||||
## asynchronous work has completed during final scene shutdown.
|
||||
func clear() -> void:
|
||||
_mesh_by_normalized_path.clear()
|
||||
@@ -0,0 +1 @@
|
||||
uid://dhkyjcrdblgkd
|
||||
@@ -0,0 +1,34 @@
|
||||
class_name M2MeshResourceExtractor
|
||||
extends RefCounted
|
||||
|
||||
## Selects the first Mesh from loaded M2 Resources or existing Node subtrees.
|
||||
## The caller owns ResourceLoader I/O, cache adoption and material preparation.
|
||||
|
||||
|
||||
## Returns the direct Mesh Resource or the first Mesh in a temporary PackedScene
|
||||
## instance. The temporary instance is freed before this method returns.
|
||||
func extract_first_mesh(resource: Resource) -> Mesh:
|
||||
if resource is Mesh:
|
||||
return resource as Mesh
|
||||
if not (resource is PackedScene):
|
||||
return null
|
||||
var temporary_root := (resource as PackedScene).instantiate()
|
||||
if temporary_root == null:
|
||||
return null
|
||||
var mesh := find_first_mesh_in_subtree(temporary_root)
|
||||
temporary_root.free()
|
||||
return mesh
|
||||
|
||||
|
||||
## Returns the first MeshInstance3D Mesh in depth-first preorder. The subtree
|
||||
## and Mesh ownership remain unchanged.
|
||||
func find_first_mesh_in_subtree(root: Node) -> Mesh:
|
||||
if root == null:
|
||||
return null
|
||||
if root is MeshInstance3D:
|
||||
return (root as MeshInstance3D).mesh
|
||||
for child in root.get_children():
|
||||
var mesh := find_first_mesh_in_subtree(child)
|
||||
if mesh != null:
|
||||
return mesh
|
||||
return null
|
||||
@@ -0,0 +1 @@
|
||||
uid://dea4gekrxshjf
|
||||
@@ -0,0 +1,65 @@
|
||||
class_name M2PlacementGrouper
|
||||
extends RefCounted
|
||||
|
||||
## Stateless ADT M2 placement validation, normalization and transform grouping.
|
||||
|
||||
const M2_PLACEMENT_TRANSFORM_RESOLVER_SCRIPT := preload(
|
||||
"res://src/render/m2/m2_placement_transform_resolver.gd"
|
||||
)
|
||||
|
||||
|
||||
## Groups valid placements by normalized relative M2 path while preserving
|
||||
## placement order inside each group. Returned containers are caller-owned.
|
||||
func group_placements(
|
||||
tile_origin: Vector3,
|
||||
m2_names: PackedStringArray,
|
||||
m2_placements: Array
|
||||
) -> Dictionary:
|
||||
var groups: Dictionary = {}
|
||||
var placement_transform_resolver := (
|
||||
M2_PLACEMENT_TRANSFORM_RESOLVER_SCRIPT.new()
|
||||
)
|
||||
for placement_variant in m2_placements:
|
||||
if not (placement_variant is Dictionary):
|
||||
continue
|
||||
var placement: Dictionary = placement_variant
|
||||
var name_id: int = int(placement.get("name_id", -1))
|
||||
if name_id < 0 or name_id >= m2_names.size():
|
||||
continue
|
||||
|
||||
var normalized_relative_path := _normalize_relative_path(m2_names[name_id])
|
||||
if normalized_relative_path.is_empty():
|
||||
continue
|
||||
|
||||
var world_position: Vector3 = placement.get("pos", Vector3.ZERO)
|
||||
var local_position := world_position - tile_origin
|
||||
var rotation_radians: Vector3 = placement.get("rot", Vector3.ZERO)
|
||||
var scale_value := float(placement.get("scale", 1.0))
|
||||
var placement_basis: Basis = placement_transform_resolver.resolve_basis(
|
||||
rotation_radians,
|
||||
normalized_relative_path
|
||||
)
|
||||
var origin_offset: Vector3 = placement_transform_resolver.resolve_origin_offset(
|
||||
rotation_radians,
|
||||
normalized_relative_path,
|
||||
scale_value
|
||||
)
|
||||
var local_transform := Transform3D(
|
||||
placement_basis.scaled(Vector3.ONE * maxf(scale_value, 0.0001)),
|
||||
local_position + origin_offset
|
||||
)
|
||||
|
||||
if not groups.has(normalized_relative_path):
|
||||
groups[normalized_relative_path] = []
|
||||
(groups[normalized_relative_path] as Array).append(local_transform)
|
||||
return groups
|
||||
|
||||
|
||||
func _normalize_relative_path(relative_path: String) -> String:
|
||||
var normalized_relative_path := relative_path.replace("\\", "/")
|
||||
if (
|
||||
normalized_relative_path.ends_with(".mdx")
|
||||
or normalized_relative_path.ends_with(".mdl")
|
||||
):
|
||||
normalized_relative_path = normalized_relative_path.get_basename() + ".m2"
|
||||
return normalized_relative_path
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user