docs(runtime): pin J3 entity ownership migration
Reconcile J3 with later gameplay lifetime groups, inventory the current mixed entity/object ownership, and define the staged canonical identity, projection, object-table, delta, teardown, and no-window gates. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
9496c01be1
commit
b0fecc5f65
3 changed files with 365 additions and 0 deletions
|
|
@ -179,6 +179,9 @@ git revert 75930787741db40a83eab8663e4464dce8d687ba
|
||||||
|
|
||||||
### J3 — canonical identity, properties, and object-table group
|
### 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
|
- Strip renderer, particle, Wb, and streaming components from the canonical
|
||||||
record into an App projection store keyed only by local identity/incarnation.
|
record into an App projection store keyed only by local identity/incarnation.
|
||||||
- Move `LiveEntityRuntime`, accepted spawn/state/property timestamps,
|
- Move `LiveEntityRuntime`, accepted spawn/state/property timestamps,
|
||||||
|
|
|
||||||
265
docs/plans/2026-07-25-modern-runtime-slice-j3.md
Normal file
265
docs/plans/2026-07-25-modern-runtime-slice-j3.md
Normal file
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue