Files
open-wc/docs/modules/world-entity-presentation.md

14 KiB

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

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

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

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

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