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
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
Lifecycle/state
The service has no retained lifecycle. Each call moves one temporary builder
result through the following call-local states.
Main sequence
Dependency diagram
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