acdream/docs/plans/2026-07-21-gamewindow-slice-4-live-entity-integration.md

442 lines
22 KiB
Markdown

# GameWindow Slice 4 — live-entity App integration
**Status:** Complete 2026-07-21.
**Parent program:** [`docs/architecture/code-structure.md`](../architecture/code-structure.md), Slice 4.
**Baseline:** `ed9dc366`; `GameWindow.cs` is 14,310 lines, 274 fields, and
190 methods before this slice. Release baseline: 6,714 passed / 5 skipped.
**Behavior rule:** Preserve the accepted live-object identity, packet FIFO,
independent timestamp authorities, render/physics/effect lifetime, and connected
R6 behavior while moving their App integration to focused owners. Correct only
proven retail-conformance defects with an explicit oracle and test; do not hide
them inside a mechanical extraction.
**Progress ledger:**
- [x] A — canonical seams: `LiveWorldOriginState`, single-bind runtime-component
lifecycle bridge, extracted `RemoteMotion`, and canonical physics-host
ownership. Landed in `d68c83d1`, `5882b308`, and `fcb66198`. The final
exact-record host checkpoint passed three clean reviews plus 6,743 Release
tests / 5 skipped on 2026-07-21.
- [x] B — pure appearance/projection helpers and controller-level
characterization tests. Landed in `69a2ca0c`. Exact appearance/collision
replacement, default-pose resolution, local-shadow ownership, and recursive
retained projection withdrawal passed three clean reviews plus 6,799 Release
tests / 5 skipped on 2026-07-21.
- [x] C — hydration create/materialize/landblock-recovery ownership and direct
CreateObject/streaming cutover. Landed in `d10c5f2d`; 6,854 Release tests
passed / 5 skipped after three clean retail, architecture, and adversarial
re-reviews on 2026-07-21.
- [x] D — ObjDesc/Parent/Pickup/ready/withdraw ownership and exact retained
identity across leave-world/re-entry. Landed in `fe551496`; 6,877 Release
tests passed / 5 skipped after three clean retail, architecture, and
adversarial re-reviews on 2026-07-21.
- [x] E — Delete/prune and retryable exact-incarnation teardown ownership.
- [x] F — Position/Vector/State/Movement controller, same-generation CreateObject
tail, independent authority gates, and direct inbound cutover.
- [x] G — `GameWindow` cleanup, three-agent review cycle, full Release suite,
connected gates, documentation, and durable memory closeout.
## 1. Outcome and non-goals
This slice creates two cooperating App owners:
- `LiveEntityHydrationController` owns CreateObject, ObjDesc, Parent, Pickup,
Delete/prune, DAT-backed appearance hydration, first materialization,
landblock rehydration, ready publication, projection withdrawal, and exact
App-component teardown over `LiveEntityRuntime`.
- `LiveEntityNetworkUpdateController` owns accepted Position, Vector, State,
and Movement routing into the existing motion, physics, projectile,
presentation, and teleport owners. It also consumes the same-generation
CreateObject update tail without reconstructing the entity.
`LiveEntityRuntime` remains the only owner of server GUID ↔ local identity,
incarnation, accepted snapshots, independent authority versions, spatial
worksets, and teardown tombstones. Neither new controller may contain a GUID
dictionary or cache an incarnation outside the runtime record it is currently
validating.
This is not a new entity model, physics algorithm, renderer, packet decoder, or
DAT reader. It does not retire the documented future-packet, visibility-PVS,
or null-parent-queue divergences. It does not redesign the accepted update
thread or `RetailInboundEventDispatcher` FIFO.
## 2. Retail and shipped oracles
The extraction is pinned to:
- `SmartBox::HandleCreateObject @ 0x00454C80`;
- `ACCObjectMaint::CreateObject @ 0x00558870`;
- `SmartBox::ProcessObjectNetBlobs @ 0x00454B20` and
`QueueBlobForObject @ 0x00451B90`;
- `SmartBox::HandleReceivedPosition @ 0x00453FD0`;
- `SmartBox::DoPickupEvent @ 0x00452240` and
`DoParentEvent @ 0x00452290`;
- `SmartBox::HandleDeleteObject @ 0x00451EA0`;
- `CPhysicsObj::set_description @ 0x00514F40`;
- `CPhysicsObj::set_state @ 0x00514DD0`;
- `CPhysicsObj::change_cell @ 0x00513390`;
- `CPhysicsObj::exit_world @ 0x00514E60` and
`leave_world @ 0x005155A0`;
- [`2026-07-13-retail-projectile-vfx-pseudocode.md`](../research/2026-07-13-retail-projectile-vfx-pseudocode.md);
- [`2026-07-18-retail-object-liveness-and-mesh-reclamation-pseudocode.md`](../research/2026-07-18-retail-object-liveness-and-mesh-reclamation-pseudocode.md);
- [`2026-07-19-r6-update-object-order-pseudocode.md`](../research/2026-07-19-r6-update-object-order-pseudocode.md);
- ACE and Holtburger object-update handlers as interpretation and protocol
cross-checks, never as replacements for the named-retail ordering.
The following rules are load-bearing:
1. One logical object exists per accepted `INSTANCE_TS`. Equal generation
mutates it; newer generation tears down and replaces it; older generation
is ignored using retail's wrap-safe comparison, including the `0x8000`
boundary.
2. A fresh CreateObject constructs, describes, attaches, enters world, then
replays queued blobs. An equal-generation CreateObject applies ObjDesc,
Parent or Position/Pickup, Movement, State, Vector, then WeenieDesc without
recreating the object.
3. Position, Movement, State, Vector, ObjDesc, Teleport, ForcePosition,
ServerControlledMove, and Instance authorities remain independent. After
every reentrant callback, the exact record and relevant authority version
are revalidated.
4. Movement consumes strict `MOVEMENT_TS` before it can discover a stale
server-controlled timestamp; equality on the latter is accepted.
5. Parent, Pickup, and Position share `POSITION_TS`. Pickup unparents and
leaves world; a later fresh Position unparents and restores the same object
identity/resources.
6. `leave_world` is not deletion. It clears cell/shadow/contact/transient
presentation while retaining PartArray, movement, animation, scripts,
effects, timestamps, and identity. Logical destruction alone performs
`exit_world` manager cleanup.
7. Inbound processing is one FIFO. Nested heterogeneous packets wait until the
active packet reaches its complete tail.
## 3. Architecture and interfaces
### 3.1 Shared origin state
`LiveWorldOriginState` owns the session-scoped world center currently spread
across `_liveCenterX`, `_liveCenterY`, and `_liveCenterKnown`:
```csharp
bool IsKnown { get; }
double CenterX { get; }
double CenterY { get; }
bool TryInitialize(double worldX, double worldY);
void Recenter(double worldX, double worldY);
void Reset();
```
Hydration, accepted Position/teleport, streaming, and render consumers share
this state directly. No GameWindow callback conceals origin mutation.
### 3.2 Runtime component lifecycle
Introduce:
```csharp
interface ILiveEntityRuntimeComponentLifecycle
{
void TearDown(LiveEntityRecord exactRecord);
}
```
`DeferredLiveEntityRuntimeComponentLifecycle` is a composition-only,
single-assignment bridge. Construction order is fixed:
1. construct the unbound bridge;
2. construct `LiveEntityRuntime` with the bridge;
3. construct effect, projectile, presentation, child, and related owners;
4. construct hydration;
5. bind the bridge exactly once to hydration;
6. construct and bind the network-update sink;
7. only then permit a live session to start.
Calling before bind fails fast; second bind is rejected. This removes the
circular GameWindow teardown façade without making runtime mutation optional.
The existing `CompositeLiveEntityResourceLifecycle` remains the sole logical
mesh/script registration transaction; hydration does not become a second
resource owner.
### 3.3 Canonical runtime components
Move `RemoteMotion` out of `GameWindow` into `AcDream.App.Physics` without
semantic changes. Store physics-host resolution on the exact
`LiveEntityRecord` (or an explicit runtime-owned component index) rather than
the duplicate `_physicsHosts` GUID dictionary. Local-player host publication
uses the same focused owner, not a second identity table. Preserve the full-cell
binding and remote-placement contract.
### 3.4 Hydration API
The controller surface is narrow and event-shaped:
```csharp
void OnCreate(WorldSession.EntitySpawn spawn);
void OnObjDesc(ObjDescEvent.Parsed update);
void OnDelete(DeleteObject.Parsed update);
void OnPrune(LiveEntityPruneCandidate candidate);
void OnPickup(PickupEvent.Parsed update);
void OnParent(ParentEvent.Parsed update);
bool TryAcceptParentForRender(ParentEvent.Parsed update);
void OnLandblockLoaded(uint landblockId);
bool EnsureWorldOriginAndRecover(
LiveEntityRecord expected,
ulong positionAuthorityVersion,
WorldSession.EntitySpawn acceptedSpawn);
void OnEntityReady(uint guid);
bool WithdrawWorldProjection(uint guid);
```
Hydration receives focused concrete collaborators/ports: runtime and object
table, the sole `DatCollection` and shared DAT lock, physics data cache,
spawn/animation/collision builders, spatial state/origin/streaming, and the
existing effect/projectile/child/presentation/light owners. It must not receive
a generic GameWindow context or a bundle of callbacks to old substantial
methods.
ObjDesc mutates the captured exact entity. Acquire-before-publish rollback and
post-callback incarnation checks preserve local ID, body, motion, physics host,
effect owner, and parent state. Parent-on-spawn may reenter; the canonical
snapshot is reread afterward. A materialized object only rebuckets; a pending,
never-materialized object hydrates once. Landblock load never replays
create-time logical resource activation.
### 3.5 Network-update API
The network owner accepts Position, Vector, State, Movement, and the equal-
generation CreateObject tail. It resolves through `LiveEntityRuntime`, captures
the relevant authority version, calls existing focused runtime owners, and
revalidates exact record plus authority after every callback.
Hydration calls a once-bound `ILiveEntityNetworkUpdateSink` for the
same-generation tail. Network position recovery calls
`EnsureWorldOriginAndRecover`; no substantial dispatch returns to GameWindow.
Local ForcePosition, teleport presentation, projectile corrections, shadow
updates, movement animation, and static/headless branches retain their shipped
ordering.
### 3.6 Direct composition
At slice exit these callbacks point directly to the new owners:
- session Create/Delete/Pickup/Parent/ObjDesc/Position/Vector/State/Movement;
- streaming landblock-loaded rehydration;
- liveness prune;
- equipped-child parent acceptance and entity-ready replay;
- runtime exact-component teardown.
`GameWindow` keeps controller construction plus narrow frame calls. The old
handler, hydration, collision-registration, appearance-rebind, ready,
leave-world, and teardown method bodies must be absent, not retained as
one-line façades.
## 4. Commit sequence and gates
### A — canonical seams
- Add and test `LiveWorldOriginState`.
- Add and test the fail-fast, single-bind lifecycle bridge.
- Extract `RemoteMotion` mechanically; retarget all 51 App-test references.
- Canonicalize physics-host ownership and remove `_physicsHosts` only after
resolution/teardown/local-player tests prove equivalence.
- Run App remote-motion, runtime, physics-host, session-reset, and structural
suites; full Release build/test; three-agent review and re-review.
### B — presentation helpers and characterization
- Extract `AppearanceUpdateState`, appearance rebind, default-pose, and
collision-build helpers behind focused App types.
- Preserve the exact DAT lock and existing extracted WorldBuilder pipeline.
- Add controller-harness fakes that require no Silk/GL context.
- Characterize current leave-world failure ordering rather than silently
redesigning it.
- Run appearance, animation, spawn-adapter, collision, resource-lifecycle,
and new characterization suites; full Release build/test; review cycle.
Completed in `69a2ca0c`. The checkpoint also pinned the retail `add_child` /
`set_parent` / `leave_world` boundary required by C and D: parent timestamps
are staged before validation, accepted relationships are committed one at a
time, and world withdrawal recursively removes the exact parent-first
projection subtree while retaining logical identity and retry ownership.
Release build completed with zero warnings and errors; 6,799 tests passed with
five intentional skips; retail, architecture, and adversarial re-reviews were
clean.
### C — create and materialization
- Implement hydration CreateObject registration, first materialization,
pending deferral, landblock recovery, and ready publication.
- Preserve prior-incarnation cleanup failure aggregation and rollback.
- Route session CreateObject and streaming callbacks directly.
- Tests: loaded create order; pending→loaded; materialized rebucket; same/
older/new/wrapped generations; nested callback replacement; registration
failure/retry; no duplicate mesh/script/effect activation.
- Full Release build/test; review cycle.
Completed in `d10c5f2d`. `LiveEntityHydrationController` now owns the exact
CreateObject registration transaction, world-origin bootstrap, pending
deferral, first DAT projection materialization, landblock recovery, and the
ready/PES replay boundary. Same-generation CreateObject supersession carries
a monotonic integration version and a persistent retry obligation, so an
older reentrant materialization cannot publish or erase the newer canonical
snapshot. Parent and pickup creates complete through their retail cell-less
paths, while interrupted initial hydration reuses the same animation
sequencer and completed owners retain their exact phase and callback queues.
Session CreateObject and streaming callbacks route directly to the extracted
owner; the old GameWindow bodies are gone. Release build completed with zero
warnings and errors; 6,854 tests passed with five intentional skips; retail,
architecture, and adversarial re-reviews were clean.
### D — appearance, parenting, pickup, withdrawal
- Move ObjDesc, Parent, Pickup, child-ready, collision registration, and
projection withdrawal.
- Tests: identity/component preservation; acquire failure rollback; stale or
reentrant ObjDesc; parent-before-create canonical reread; Pickup/Parent
leave-world; Position re-entry with unchanged local ID/resources.
- Bind child callbacks directly and remove their GameWindow façades.
- Full Release build/test; review cycle.
Completed in `fe551496`. `LiveEntityHydrationController` now owns accepted
ObjDesc, Parent, Pickup, child-ready, and exact leave-world/re-entry routing.
ObjDesc carries an independent authority/retry transaction and reports success
only when the current visual projection is published; delayed landblock or
Position recovery therefore refreshes the player paperdoll exactly once.
Attached objects re-realize through the equipped-child owner without requiring
a world Position, retain their local identity/resources, and restore their
complete descendant chain synchronously. Ready publication pins the exact
create, Position, ObjDesc, projection-mutation, projection-kind, spatial, and
entity authorities across every callback. The retail absent AnimationFrame
default is placement zero. Release build completed with zero warnings and
errors; 6,877 tests passed with five intentional skips; retail, architecture,
and adversarial re-reviews were clean.
### E — delete and exact teardown
- Move Delete/prune, teardown-plan construction, leave-world component cleanup,
and retry tombstone behavior.
- Bind runtime bridge and liveness directly.
- Tests: unknown/stale/current Delete; callback deletion; dual failure;
teardown retry; same-GUID newer generation during old teardown; reset
convergence; no surviving renderer/collision/script/effect/light owner.
- Full Release build/test; review cycle.
Completed in `f38822c4`. `LiveEntityRuntimeTeardownController` now owns the
retryable App-component half of retail `CObjectMaint::DeleteObject` and keeps
the stable cleanup plan on the exact record tombstone. Hydration rejects the
local player before every lookup or side effect, gates remote Delete by the
exact Instance timestamp, discards only unknown-owner pending effects, and
routes the 25-second prune through the same transaction. Successful cleanup
steps never replay after a failure; a same-GUID replacement cannot be mutated
by the old incarnation's delayed cleanup. The derived remote stop-observation
state also has a focused session owner instead of a loose GameWindow
dictionary. Full Release validation passed 6,891 tests with five intentional
skips and no errors; all three corrected-diff re-reviews were clean.
### F — network-update owner
- Move Movement, Vector, State, and Position routing plus the equal-generation
CreateObject tail.
- Preserve FIFO and independent authorities; test stale, equal, wrap, nested,
and callback-invalidated updates for every channel.
- Preserve ForcePosition, server-controlled move, teleport, projectile,
shadow, static, airborne, autonomous, animated, and headless branches.
- Route session callbacks directly and remove old GameWindow handlers.
- Full Release build/test; review cycle.
Completed in `aa90c646`. `LiveEntityNetworkUpdateController` now owns accepted
Position, Vector, State, and Movement routing;
`LiveEntitySameGenerationUpdateRouter` owns the retail equal-generation
CreateObject tail. `LiveEntityInboundAuthorityGate` pins the exact record,
independent channel authority, and velocity authority before any reentrant App
callback. `LiveEntityMotionRuntimeController` owns the shared physics-host,
Setup, MoveTo/Sticky, Hidden-target, and server-controlled-cycle policy behind
a once-bound construction bridge. ForcePosition preserves retail's
blip-then-immediate-F7B1 order and stamps the exact Position snapshot serialized
on the wire. Focused tests cover stale/equal/wrapped timestamps, callback
replacement, GUID reuse, projectile/body/remote Vector precedence, mixed
same-generation routing, bridge binding, and the exact 56-byte autonomous
position payload. All three corrected-diff re-reviews were clean.
### G — closeout
- Structural tests prove both controllers lack identity dictionaries and
`GameWindow` lacks every moved field/type/method/body.
- Run all live-runtime, object-table, parent/attachment, appearance/animation,
effect/light/projectile, remote-motion/physics, inbound FIFO, liveness,
stress, session-reset, and router suites.
- Run `dotnet build AcDream.slnx -c Release` and full Release tests.
- Run the connected R6 route and graceful reconnect gate, then user visual
checks for inventory/equip/parenting, death/corpse, and portals when
available.
- Update architecture, roadmap, milestones, issues/divergence register, this
ledger, `AGENTS.md`/`CLAUDE.md` when current-state pointers change, and
durable GameWindow/live-runtime memory.
Completed 2026-07-21. `GameWindow` fell from the Slice-3 baseline of 14,310
lines to 10,301 lines; its top-level field and method declarations are now 267
and 163. Release build completed with zero warnings/errors and the full suite
passed 6,940 tests with five intentional skips. The 310-second connected R6
route passed login, Caul/Sawato/Rynthid/Aerlinthe/Sawato-revisit/Holtburg/
Caul-return materialization, turn sampling, three locomotion/jump/combat
exercises, and graceful close. Live-server population churn, previously known
missing DAT VFX diagnostics, and empty world-edge landblocks remained warnings,
not lifecycle failures. Slice 5 is the next structural owner boundary.
## 5. Adversarial acceptance matrix
Automated acceptance includes:
- equal/old/new/wrapped `INSTANCE_TS`, including `0x8000`;
- full same-generation call trace and WeenieDesc tail ordering;
- nested heterogeneous FIFO and exact authority invalidation;
- fresh loaded create and pending→loaded without replay;
- loaded→loaded rebucket and loaded↔pending oscillation;
- ObjDesc identity/body/motion/host/effect/parent preservation;
- Parent/Pickup→cell-less→Position re-entry with unchanged local ID;
- unknown/stale/current Delete, callback delete, GUID reuse, and retry;
- state side-effect order and movement/ForcePosition timestamp order;
- projectile and remote-position corrections across landblocks;
- bridge unbound/second-bind failures;
- session reset with zero surviving live owners;
- 96-owner and repeated-recall stress with no resource growth;
- source/reflection ownership gates.
Connected acceptance includes:
- capped login and populated radar/world;
- remote creature motion and target/status projection;
- wield/dewield, bow/wand/melee switching, ammo parenting, inventory pickup;
- open and loot a corpse after approach;
- kill/death/corpse lifetime;
- recall and portal travel across landblocks, revisit, graceful close, and
fresh-process reconnect;
- uncapped post-reconnect performance checkpoint.
## 6. Review discipline
Every ownership commit receives three independent read-only reviews:
1. retail conformance — symbols, ordering, timestamps, and documented
divergences;
2. architecture/integration — identity ownership, dependency direction,
update-thread rules, lifecycle symmetry, and GameWindow reduction;
3. adversarial testing — malformed/stale packets, callback reentrancy,
registration failure, GUID reuse, landblock churn, reset, and leaks.
The primary agent reproduces and fixes confirmed findings, reruns focused and
full gates, and requests re-review until no actionable finding remains. No
subagent writes the implementation.
## 7. Explicit carried divergences
Slice 4 preserves, rather than disguises:
- AD-32 future-generation non-effect packet dropping;
- AP-65 picked-up data-only weenie retention;
- AP-69 approximate 384-unit visibility envelope;
- TS-32 missing complete child-before-parent null-object queue;
- the equal-generation PES-table refresh and VectorUpdate airborne/state
adaptations recorded during Slice 4 planning.
These remain visible in the divergence register and are not evidence that the
ownership extraction itself is incomplete.