refactor(runtime): own per-session physics simulation
Move the sole PhysicsEngine, production cache, collision admissions, canonical bodies and hosts, remote components, ordinary/remote worksets, simulation, cell commits, and shadow synchronization under RuntimeEntityObjectLifetime. Keep App as the prepared-asset, animation-input, and render-projection adapter while preserving the named-retail update and collision order. Add exact-incarnation, object-clock, callback-reentrancy, GUID-reuse, two-runtime isolation, source ownership, collision publication, and graphical projection coverage. Release build and the complete 8,588-test solution pass. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
parent
0dc3bfdeff
commit
7e6033d0ad
39 changed files with 3685 additions and 1722 deletions
200
docs/research/2026-07-26-slice-j5-5-physics-remote-ownership.md
Normal file
200
docs/research/2026-07-26-slice-j5-5-physics-remote-ownership.md
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
# Slice J5.5 — per-session physics and remote-simulation ownership
|
||||
|
||||
Date: 2026-07-26
|
||||
Scope: ownership relocation only; no retail algorithm or gameplay-policy change
|
||||
|
||||
## Objective
|
||||
|
||||
Make one `AcDream.Runtime` instance own one complete mutable physics world:
|
||||
|
||||
- one `PhysicsEngine`, its transition-scratch arena, and its shadow registry;
|
||||
- one production `PhysicsDataCache`/cell graph;
|
||||
- canonical physics bodies and physics hosts;
|
||||
- canonical remote movement/interpolation state;
|
||||
- loaded/cell-backed ordinary and remote update worksets.
|
||||
|
||||
App remains responsible for DAT/package preparation, streaming policy, animation
|
||||
pose production, render projection, effects, meshes, debug drawing, and UI. It
|
||||
publishes immutable prepared collision records and animation-root inputs to the
|
||||
Runtime owner, then applies committed Runtime snapshots to graphical objects.
|
||||
|
||||
This is the J5.5 boundary from
|
||||
`docs/plans/2026-07-26-modern-runtime-slice-j5.md`. Projectile component
|
||||
ownership and prediction remain explicitly deferred to J5.6.
|
||||
|
||||
## Named-retail oracle
|
||||
|
||||
Primary source:
|
||||
`docs/research/named-retail/acclient_2013_pseudo_c.txt`.
|
||||
|
||||
- `CPhysics::UseTime` `0x00509950`
|
||||
- `CPhysicsObj::UpdatePositionInternal` `0x00512C30`
|
||||
- `CPhysicsObj::transition` `0x00512DC0`
|
||||
- `CPhysicsObj::SetPositionInternal(CTransition const*)` `0x00515330`
|
||||
- `CPhysicsObj::UpdateObjectInternal` `0x005156B0`
|
||||
- `CPhysicsObj::update_object` `0x00515D10`
|
||||
- `CPhysicsObj::MoveOrTeleport` `0x00516330`
|
||||
- `CPhysicsObj::add_shadows_to_cells` `0x00514AE0`
|
||||
|
||||
The complete translated control flow is already pinned in
|
||||
`docs/research/2026-07-19-r6-update-object-order-pseudocode.md`. The current
|
||||
Core/App implementation was cross-checked there and in the R6/physics digest
|
||||
against ACE's `PhysicsObj` interpretation and the extracted WorldBuilder
|
||||
collision/content pipeline. J5.5 reuses that port unchanged.
|
||||
|
||||
## Preserved pseudocode
|
||||
|
||||
```text
|
||||
for each ordinary object in the active object table:
|
||||
elapsed = current physics time - object update time
|
||||
apply retail epsilon / stale-quantum / subdivision gates
|
||||
|
||||
if object is Active:
|
||||
if not Hidden:
|
||||
advance PartArray and obtain the complete root-motion Frame
|
||||
apply PositionManager correction to that Frame
|
||||
compose the candidate position and orientation
|
||||
if not Hidden:
|
||||
integrate acceleration, velocity, and omega
|
||||
drain object animation hooks
|
||||
|
||||
if the candidate frame moved:
|
||||
run Transition against the object's current full cell
|
||||
commit accepted frame, contact state, and full cell
|
||||
update the object's collision shadows
|
||||
|
||||
DetectionManager.CheckDetection
|
||||
TargetManager.HandleTargetting
|
||||
MovementManager.UseTime
|
||||
PartArray.HandleMovement
|
||||
PositionManager.UseTime
|
||||
|
||||
ParticleManager.UpdateParticles
|
||||
ScriptManager.UpdateScripts
|
||||
```
|
||||
|
||||
The App/Runtime seam may split pose production from simulation, but it must not
|
||||
change this observable ordering. In particular:
|
||||
|
||||
- root motion and the position-manager delta are complete before collision;
|
||||
- the transition's swept full cell is canonical;
|
||||
- a Hidden object is not the same as an inactive or deleted object;
|
||||
- exact record/incarnation, authority version, and object-clock epoch are
|
||||
revalidated after every callback;
|
||||
- remote shadows follow the committed body frame, never a raw server sample;
|
||||
- `CellGraph.CurrCell` remains local-player-only state.
|
||||
|
||||
## Baseline ownership gap
|
||||
|
||||
The canonical `RuntimeEntityRecord` already owns `PhysicsBody`,
|
||||
`IPhysicsObjHost`, full cell, final physics state, accepted authority versions,
|
||||
and `RetailObjectQuantumClock`. The remaining duplicate boundary is:
|
||||
|
||||
- `GameWindow` constructs the session `PhysicsEngine` and production
|
||||
`PhysicsDataCache`;
|
||||
- App `LiveEntityRecord` stores the remote-motion component;
|
||||
- App `LiveEntityRuntime` stores remote and ordinary spatial workset indexes;
|
||||
- App updaters both mutate canonical simulation and project `WorldEntity`;
|
||||
- App streaming mutates the engine directly.
|
||||
|
||||
That baseline shape prevented a no-window Runtime from owning the same
|
||||
simulation graph and made two simultaneous sessions harder to prove isolated.
|
||||
J5.5 retires this gap.
|
||||
|
||||
## Target ownership
|
||||
|
||||
`RuntimeEntityObjectLifetime` constructs one `RuntimePhysicsState` beside its
|
||||
entity directory. `RuntimePhysicsState` owns:
|
||||
|
||||
- the exact `PhysicsEngine` and production cache;
|
||||
- remote components and physics hosts stored on `RuntimeEntityRecord`;
|
||||
- keyed ordinary/remote worksets containing exact `RuntimeEntityKey` values;
|
||||
- typed collision admission/withdrawal receipts;
|
||||
- simulation commits and immutable presentation snapshots;
|
||||
- reset/disposal convergence and an ownership ledger.
|
||||
|
||||
App acknowledgements contain no gameplay policy:
|
||||
|
||||
```text
|
||||
prepared collision publication -> Runtime typed admission -> receipt
|
||||
render spatial visibility edge -> Runtime workset acknowledgement
|
||||
animated root Frame -> Runtime remote/ordinary tick
|
||||
Runtime committed snapshot -> App WorldEntity/rebucket/pose projection
|
||||
```
|
||||
|
||||
The immutable prepared collision payload may originate in App streaming. The
|
||||
engine, transition scratch, cell graph, body, shadow registry, interpolation,
|
||||
manager state, and workset are never shared across Runtime instances.
|
||||
|
||||
## Implemented boundary
|
||||
|
||||
- `RuntimeEntityObjectLifetime` constructs and disposes exactly one
|
||||
`RuntimePhysicsState`.
|
||||
- `RuntimePhysicsState` constructs the production `PhysicsDataCache` and the
|
||||
sole `PhysicsEngine`, including its ten-deep reusable transition scratch and
|
||||
shadow registry. App borrows both objects and cannot construct alternatives.
|
||||
- `RuntimeEntityRecord` owns the canonical `PhysicsBody`, `RemoteMotion`,
|
||||
`EntityPhysicsHost`, full cell, object clock, and exact-incarnation binding
|
||||
guards.
|
||||
- `RuntimePhysicsState` owns keyed ordinary/remote worksets, body acquisition,
|
||||
host installation/rebinding/lookup, remote component binding, and typed
|
||||
canonical cell commits.
|
||||
- `RuntimeRemotePhysicsUpdater` and `RuntimeOrdinaryPhysicsUpdater` contain the
|
||||
unchanged presentation-free retail simulation. Their App adapters supply
|
||||
DAT-derived shape/animation inputs and project immutable pose snapshots only.
|
||||
- `LandblockPhysicsPublisher` publishes the prepared collision root through a
|
||||
generation-scoped `RuntimeCollisionAdmission`; demotion and withdrawal cross
|
||||
the same typed Runtime boundary.
|
||||
- Runtime cell commits synchronously publish
|
||||
`RuntimePhysicsCellCommit`. The graphical subscriber revalidates the exact
|
||||
record and spatial-authority version before rebucketing the render
|
||||
projection.
|
||||
- Two Runtime instances own distinct engines, caches, cell graphs, scratch
|
||||
arenas, shadow registries, components, hosts, bodies, and worksets.
|
||||
|
||||
No retail algorithm, constant, order, packet, or gameplay policy changed, and
|
||||
this ownership relocation introduces no retail-divergence row.
|
||||
|
||||
## Staged implementation boundary
|
||||
|
||||
1. Construct engine/cache under `RuntimePhysicsState`; make App borrow them.
|
||||
2. Move presentation-free `RemoteMotion` and `EntityPhysicsHost` types into
|
||||
Runtime and store the remote component on `RuntimeEntityRecord`.
|
||||
3. Move remote/ordinary keyed worksets and their visibility acknowledgements
|
||||
into `RuntimePhysicsState`.
|
||||
4. Split remote/ordinary update into Runtime simulation commits and App
|
||||
presentation acknowledgements without changing update order.
|
||||
5. Replace direct streaming mutation with typed Runtime
|
||||
admission/withdrawal receipts.
|
||||
6. Remove App simulation mirrors and prove reset/two-instance isolation.
|
||||
|
||||
Each intermediate state must build and retain exact behavior. The production
|
||||
commit remains independently revertible as the single J5.5 ownership unit.
|
||||
|
||||
## Acceptance evidence
|
||||
|
||||
- existing flat-collision conformance corpus remains bit-identical;
|
||||
- reused versus fresh transition scratch stays equivalent and allocation-free;
|
||||
- local and remote position/cell/orientation fixtures remain exact;
|
||||
- crowd, doors, stairs, cell seams, Hidden, teleport, delete, and GUID-reuse
|
||||
fixtures pass;
|
||||
- two Runtime instances have distinct engine, cache, scratch, shadows, bodies,
|
||||
hosts, remote components, and worksets;
|
||||
- App production source cannot construct a `PhysicsEngine`,
|
||||
`PhysicsDataCache`, canonical remote component, or simulation workset;
|
||||
- Release build, focused Runtime/App suites, complete Release suite, connected
|
||||
lifecycle/reconnect, and the canonical movement route pass.
|
||||
|
||||
Automated gate on the J5.5 production tree:
|
||||
|
||||
- Release solution build: zero warnings and zero errors.
|
||||
- Core collision/physics suite: 3,273 passed / 2 skipped.
|
||||
- Runtime suite: 314 passed.
|
||||
- App suite: 3,718 passed / 3 skipped.
|
||||
- Complete solution: 8,588 passed / 5 skipped.
|
||||
- Source ownership guards prove App does not construct a `PhysicsEngine` or
|
||||
production cache, does not contain the remote/ordinary transition solver,
|
||||
and cannot directly admit/remove streamed landblock collision.
|
||||
|
||||
Connected lifecycle/reconnect and the canonical movement/collision route are
|
||||
the exact-binary post-commit gate.
|
||||
Loading…
Add table
Add a link
Reference in a new issue