diff --git a/docs/plans/2026-07-25-modern-runtime-slice-j.md b/docs/plans/2026-07-25-modern-runtime-slice-j.md index a599e7ab..d6aca36d 100644 --- a/docs/plans/2026-07-25-modern-runtime-slice-j.md +++ b/docs/plans/2026-07-25-modern-runtime-slice-j.md @@ -179,6 +179,9 @@ git revert 75930787741db40a83eab8663e4464dce8d687ba ### J3 — canonical identity, properties, and object-table group +**Detailed execution plan:** +[`2026-07-25-modern-runtime-slice-j3.md`](2026-07-25-modern-runtime-slice-j3.md). + - Strip renderer, particle, Wb, and streaming components from the canonical record into an App projection store keyed only by local identity/incarnation. - Move `LiveEntityRuntime`, accepted spawn/state/property timestamps, diff --git a/docs/plans/2026-07-25-modern-runtime-slice-j3.md b/docs/plans/2026-07-25-modern-runtime-slice-j3.md new file mode 100644 index 00000000..7d74b72e --- /dev/null +++ b/docs/plans/2026-07-25-modern-runtime-slice-j3.md @@ -0,0 +1,265 @@ +# Modern runtime Slice J3 — canonical identity, properties, and object table + +**Status:** ACTIVE +**Parent:** `2026-07-25-modern-runtime-slice-j.md`, J3 +**Production base:** `9496c01b` +**J2 production commit:** `75930787741db40a83eab8663e4464dce8d687ba` + +## 1. Objective + +Move the one canonical server-object identity/incarnation owner, accepted +CreateObject/property/timestamp state, parent relation state, and +`ClientObjectTable` lifetime into `AcDream.Runtime`. + +App becomes a projection host. Its world entity, animation, effect, light, +selection, paperdoll, spatial bucket, and hydration state is keyed by a +Runtime-issued local identity plus exact incarnation. App must not retain a +second server-GUID map or decide whether a packet belongs to the current +incarnation. + +This is a structural move. Existing named-retail CreateObject, timestamp, +parenting, pickup, Hidden, delete, and generation-replacement behavior remains +unchanged. No renderer, particle, streaming, GL, or retained-UI type may enter +Runtime. + +## 2. Scope reconciliation + +The umbrella J3 text overlaps later lifetime groups. This detailed plan is the +authoritative split: + +- J3 moves raw accepted entity identity, immutable spawn/property snapshots, + timestamp gates, parent relations, and the canonical client object/container + table. +- J4 moves higher-level inventory transactions, vitals, enchantments, + spell/component state, cooldown services, chat, and their ViewModel-facing + revisions. +- J5 moves movement/physics/interaction/combat authority. + +J3 may retain existing Core physics references already stored on the entity +record while separating presentation, but it does not redesign or relocate +their behavior. That coherent move remains J5. + +## 3. Fixed ownership after J3 + +`AcDream.Runtime` owns: + +- server GUID to current incarnation; +- exact Runtime local identity allocation and reverse lookup; +- accepted `INSTANCE_TS` and every retail packet-channel timestamp; +- the latest accepted immutable `WorldSession.EntitySpawn`; +- logical create, same-incarnation refresh, pickup, parent, Hidden/state, + delete, generation replacement, tombstone, and session-clear state; +- unresolved/staged/committed parent relations; +- `ClientObjectTable`, containers, placement indices, pending authoritative + property state, and generation-aware removal; +- ordered immutable entity/object deltas; +- retryable logical teardown state and the acknowledgement from an attached + projection host. + +`AcDream.App` owns: + +- `WorldEntity` and render-scene projection; +- loaded/pending spatial buckets and visibility derived from streaming; +- animation, remote-motion presentation, projectile presentation, effects, + lights, selection geometry, radar presentation, and private paperdolls; +- projection hydration/recovery transaction state; +- render/resource registration and its exact retryable teardown suffix. + +App projection keys are: + +```text +RuntimeLocalEntityId + INSTANCE_TS +``` + +Server GUID may be carried as immutable metadata for diagnostics and outbound +commands, but it is not an App lookup authority. + +## 4. Runtime contracts + +Introduce or finish contracts equivalent to: + +```csharp +public readonly record struct RuntimeEntityKey( + uint LocalEntityId, + ushort Incarnation); + +public readonly record struct RuntimeEntityDelta( + ulong Sequence, + RuntimeGenerationToken SessionGeneration, + RuntimeEntityKey Entity, + RuntimeEntityDeltaKind Kind, + RuntimeEntitySnapshot Snapshot); + +public interface IRuntimeEntityProjectionHost +{ + RuntimeProjectionAcknowledgement Register(RuntimeEntitySnapshot entity); + RuntimeProjectionAcknowledgement Update(RuntimeEntityDelta delta); + RuntimeProjectionAcknowledgement Withdraw(RuntimeEntityKey entity); + RuntimeProjectionAcknowledgement TearDown(RuntimeEntityKey entity); +} +``` + +The concrete names may follow existing project vocabulary, but these +invariants are mandatory: + +- Runtime issues identity before App creates a projection. +- Every App callback carries the exact local identity and incarnation. +- Callback acknowledgement is exact and retryable. +- Completed teardown work never replays. +- App cannot query or mutate Runtime by a stale local identity/incarnation. +- Runtime emits deltas only after canonical state commits. +- Callback reentrancy may supersede the outer operation; the outer operation + must revalidate generation/incarnation/operation version before continuing. + +## 5. Execution slices + +### J3.0 — oracle and ownership inventory + +- Pin the current field/method ownership table for `LiveEntityRecord`, + `LiveEntityRuntime`, `InboundPhysicsStateController`, + `ParentAttachmentState`, `ClientObjectTable`, `GpuWorldState`, hydration, + and teardown owners. +- Reuse the existing retail pseudocode and citations; no behavior is being + invented. Add research only where a current branch lacks a cited oracle. +- Capture normalized existing traces for create, same-generation refresh, + parent/unparent, pickup, Hidden, delete/recreate, GUID reuse, container + placement, and session clear. +- Record baseline render-scene identity/digest at deterministic fixtures. + +### J3.1 — move narrow accepted-wire owners + +- Move `InboundPhysicsStateController`, `ParentAttachmentState`, their result + types, and their complete tests into Runtime. +- Preserve timestamp cursor/order behavior and parent candidate ordering + line-for-line. +- App's existing `LiveEntityRuntime` temporarily composes those Runtime owners; + no second state is introduced. +- Add Runtime dependency/load guards around the moved types. + +Gate: all timestamp, malformed, wraparound, parent-generation, and packet-order +tests pass in Runtime; App behavior and complete suite remain unchanged. + +### J3.2 — canonical Runtime entity directory + +- Extract current GUID/incarnation maps, accepted snapshot state, session + lifetime version, operation versions, tombstones, delete/generation + replacement, and local-ID allocation into one Runtime owner. +- Split `LiveEntityRecord` into canonical Runtime state and an App projection + sidecar. Canonical fields never reference App interfaces or `WorldEntity`. +- Preserve current local-ID allocation order during graphical hydration for + parity; Runtime owns the allocator and claim, while a no-window host may + claim immediately. +- Retain exact reentrant supersession and retryable tombstone behavior. +- Keep physics/movement fields structurally in place where required for J5, + but remove every renderer/effect/hydration reference from the canonical + record. + +Gate: the complete existing `LiveEntityRuntimeTests` identity/timestamp/ +teardown subset moves to Runtime and produces the same normalized trace. + +### J3.3 — App projection store and cutover + +- Add one App projection store keyed only by Runtime local identity plus + incarnation. +- Move `WorldEntity`, spatial visibility, hydration/recovery flags, animation, + remote-motion presentation, projectile presentation, effect profiles, + resource flags, and active presentation worksets into that store. +- Convert `GpuWorldState`, hydration, equipped children, render journal, + animation/effect/light owners, selection/radar queries, and teardown to the + exact projection key. +- Replace App's mixed `LiveEntityRuntime` facade with focused Runtime directory + and App projection-store dependencies; delete its GUID maps and mixed record. +- Preserve current register-once, rebucket-only, withdraw-without-destroy, + Hidden, and retryable logical teardown order. + +Gate: projection identity/digest and all existing hydration, rebucket, +visibility, effect, animation, projectile, selection, and teardown tests match. +Source guards reject any App server-GUID authority map. + +### J3.4 — canonical object-table ownership + +- Add a Runtime entity/object lifetime-group root that owns the canonical + entity directory and the existing Core `ClientObjectTable`. +- Construct it before either graphical UI or live presentation; both borrow + the same instances. +- Remove the public `ClientObjectTable` owner field from `GameWindow`. +- Route J2 inbound object/property/container updates directly to the Runtime + group. +- Keep UI controllers, item icons, paperdoll, grids, cooldown drawing, and + interaction presentation as App borrowers. +- Preserve synchronous press-time reads and current event ordering; add no + queue or copied inventory model. + +Gate: object add/update/remove, placement, container replacement, optimistic +move rollback, stack response, GUID reuse, and session-clear traces match. + +### J3.5 — ordered deltas and borrowed views + +- Publish one monotonic per-session Runtime entity/object delta stream. +- Make J1 `IGameRuntimeView.Entities` and inventory snapshots read the Runtime + owners directly rather than App adapters. +- Feed App projection mutation from the same committed deltas/acknowledgements; + do not create a second queue or frame delay. +- Remove superseded App entity/object event mirroring and source adapters. +- Assert same-thread graphical input and inbound presentation remain + synchronous. + +Gate: direct-host and graphical-host normalized traces are identical, including +sequence, generation, identity, properties, placement, and teardown edges. + +### J3.6 — hardening and closeout + +- Failure-inject every projection registration/update/withdraw/teardown stage. +- Exercise callback reentrancy, duplicate CreateObject, stale/equal/wrapped + timestamps, delete/recreate with the same GUID, pending-to-loaded movement, + parent events before either object, malformed property packets, and session + reset during callbacks. +- Prove zero leaked canonical records, tombstones, App projections, resources, + container entries, parent candidates, queued deltas, or subscriptions. +- Add a Runtime-only entity/object fixture that performs create, property + updates, parent/container moves, Hidden, delete/recreate, clear, and teardown + without loading presentation/backend assemblies. +- Run Release build, focused suites, complete tests, exact-binary connected + lifecycle/reconnect, and the canonical world route. +- Because J3 changes projection identity plumbing, run the existing automated + render digest/referee at every checkpoint. Pause for user visual verification + only if the referee or connected client exposes a pixel/interaction symptom. + +## 6. Commit order + +1. `refactor(runtime): move accepted entity wire state` +2. `refactor(runtime): own canonical entity identity and incarnations` +3. `refactor(app): key live projections by runtime identity` +4. `refactor(runtime): own canonical object table` +5. `feat(runtime): publish ordered entity and object deltas` +6. `test(runtime): close J3 entity ownership parity` + +Each commit is independently buildable and bisectable. The exact rollback for +each lands in this document before that commit's connected or visual gate. + +## 7. Acceptance + +J3 is complete only when: + +- Runtime owns the only server-GUID/incarnation map and object table; +- App projections are keyed by local identity plus incarnation; +- Runtime has no App/UI/backend dependency; +- App has no authoritative GUID map or accepted timestamp gate; +- no mirrored collection, asynchronous seam, or added input frame exists; +- normalized direct/graphical traces match; +- deterministic render identity/digests match; +- Runtime-only no-window entity/object lifecycle passes; +- Release build and complete tests pass; +- exact-binary connected login/travel/logout/reconnect passes gracefully; +- every teardown owner converges to zero. + +## 8. Current rollback chain + +```text +git revert 75930787741db40a83eab8663e4464dce8d687ba +git revert 854d9e9cd13092bd5aaa3cf025d73eeb4600e9f8 +git revert b632672e5ccabfb44c551e08f1c411ab2669c44a +``` + +These are J2, J1, and J0 respectively. Do not use them for a J3 sub-slice +failure; record and revert only the exact failing J3 commit. diff --git a/docs/research/2026-07-25-slice-j3-entity-ownership-inventory.md b/docs/research/2026-07-25-slice-j3-entity-ownership-inventory.md new file mode 100644 index 00000000..e6887924 --- /dev/null +++ b/docs/research/2026-07-25-slice-j3-entity-ownership-inventory.md @@ -0,0 +1,97 @@ +# Slice J3 entity ownership inventory + +## Baseline + +Production base: `9496c01b`. + +The accepted J2 binary passes 8,428 Release tests with five skips and the +seven-checkpoint connected lifecycle/reconnect gate at +`logs/connected-world-gate-20260725-193936/report.json`. + +At this baseline: + +- `LiveEntityRuntime.cs` is 2,866 lines and is referenced by roughly 100 App + source/test files; +- it owns both canonical GUID/incarnation/timestamp state and graphical + projection/component state; +- `InboundPhysicsStateController` and `ParentAttachmentState` are already + presentation-free but still reside in App; +- `ClientObjectTable` is a presentation-free Core type, but its production + instance is publicly owned by `GameWindow`; +- the retained render scene is a derived projection and is not a second + gameplay/GUID authority. + +## Existing retail oracle + +J3 changes ownership, not behavior. It preserves the already cited ports: + +- `CObjectMaint::CreateObject` +- `CObjectMaint::DeleteObject @ 0x00508460` +- `CPhysicsObj::set_description @ 0x00514F40` +- `CPhysicsObj::change_cell @ 0x00513390` +- `CPhysicsObj::exit_world @ 0x00514E60` +- `ACCWeenieObject::ServerSaysMoveItem @ 0x0058DBB0` +- the SmartBox CreateObject/event flow in + `docs/research/2026-07-13-retail-projectile-vfx-pseudocode.md` + +No new AC algorithm or constant is introduced. Any later behavioral question +returns to named retail before implementation. + +## Field ownership + +| State | J3 owner | Notes | +|---|---|---| +| Server GUID and `INSTANCE_TS` | Runtime | One current incarnation only | +| Runtime local identity and reverse lookup | Runtime | Issued by Runtime; never inferred by App | +| Latest accepted immutable spawn | Runtime | Includes accepted raw properties and timestamps | +| Position/state/vector/movement/object-description authority versions | Runtime | Reentrant operation validation | +| Full cell and canonical landblock | Runtime | Authoritative object placement, not streaming residency | +| Raw/final physics state and object clock | Runtime | Existing behavior; later physics relocation is J5 | +| Parent candidates and accepted parent relation | Runtime | Exact arrival and generation order | +| Session lifetime version, GUID operation versions, tombstones | Runtime | Retryable replacement/delete/clear | +| `ClientObjectTable` and placement/container indices | Runtime | Existing Core type, same instance | +| `WorldEntity` and mesh/resource registration | App projection | Keyed by local identity + incarnation | +| Loaded/pending bucket and derived visibility | App projection | Streaming fact, not object identity | +| Animation/effect/light/projectile presentation | App projection | No references from Runtime | +| Hydration/recovery/appearance synchronization flags | App projection | Retryable graphical transaction | +| Selection geometry, radar and paperdoll | App projection | Borrow canonical object facts | + +`HasPartArray`, `PhysicsBody`, and movement-manager/physics-host fields are +presentation-independent simulation facts. J3 must not push them into App just +to simplify the split. Their behavior and final lifetime-group relocation are +handled in J5. + +## Current mixed-owner hazards + +1. Local IDs are currently reserved when App materializes a `WorldEntity`. + Runtime must own the allocator while preserving that graphical allocation + order until parity is proven. +2. Logical tombstones currently retain App teardown plans on + `LiveEntityRecord`. J3 replaces this with a typed, exact, retryable + projection acknowledgement. +3. Active animation/remote/projectile worksets currently live beside the GUID + map. They move intact to the App projection store. +4. `GpuWorldState` and several presentation controllers accept server GUID as + their primary key. They must consume Runtime local identity plus incarnation + and treat GUID only as immutable payload metadata. +5. `GameWindow.Objects` makes the graphical shell appear to own the canonical + object table. J3 replaces it with a Runtime lifetime-group owner borrowed by + UI and presentation composition. + +## Do not retry + +| Dead direction | Why | +|---|---| +| Copy the GUID map into Runtime while keeping App's map | Creates two worlds and ambiguous teardown | +| Let App allocate an independent local ID | Breaks cross-host identity and stale-incarnation rejection | +| Put `WorldEntity` or App component interfaces in Runtime | Makes headless a hidden graphical host | +| Deliver projection updates through a later-frame queue | Changes accepted presentation and input timing | +| Move vitals/spells/combat opportunistically in J3 | Splits the J4/J5 lifetime groups | +| Re-key everything by GUID and call it a projection cache | Leaves App as a second identity authority | + +## J3.1 first move + +`InboundPhysicsStateController`, `ParentAttachmentState`, their immutable +result types, and dedicated tests move into `AcDream.Runtime.Entities` +unchanged. App temporarily composes these Runtime owners until the canonical +directory and projection store cut over in later J3 sub-slices.