255 lines
14 KiB
Markdown
255 lines
14 KiB
Markdown
# 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)
|