89 lines
4.8 KiB
Markdown
89 lines
4.8 KiB
Markdown
# Atomic collision-generation activation (Slice 3B)
|
|
|
|
## Retail anchor
|
|
|
|
Retail hydrates a cell synchronously. `CObjCell::init_objects`
|
|
(`0x0052B420`) visits objects associated with that cell and invokes
|
|
`CPhysicsObj::recalc_cross_cells` (`0x00515A30`). The final position path also
|
|
replaces shadows as one `SetPositionInternal` operation (`0x00515330`). Retail
|
|
therefore never exposes a world where the new cell exists but the objects that
|
|
overlap it still have their old cross-cell set.
|
|
|
|
Acdream streams a landblock over several update frames. Literal per-cell
|
|
mutation during those frames was not equivalent: the active `PhysicsDataCache`,
|
|
`CellGraph`, `PhysicsEngine`, buildings, static shadows, and retained-object
|
|
refloods changed at different cursors. Collision queries could observe a mixed
|
|
generation, and correctness depended on a later optional landblock callback.
|
|
|
|
## Ported adaptation
|
|
|
|
The asynchronous unit is now one Runtime-owned collision generation:
|
|
|
|
1. `BeginCollisionAdmission` issues the exact Runtime/landblock generation.
|
|
2. `PrepareCollisionGeneration` clones the bounded resident spatial records
|
|
into a private cache, graph, engine, and shadow registry. Global immutable
|
|
GfxObj/Setup catalogs are not copied; the accepted build's exact closure is
|
|
populated by the existing cursors.
|
|
3. App and Headless publish terrain, EnvCells, topology, buildings, prepared
|
|
collision assets, and target-root static owners only into that private
|
|
generation.
|
|
4. A mutation-stable cursor captures every non-suspended owner that touches —
|
|
or has a withdrawn repair marker for — the target prefix. That includes
|
|
live dynamic owners and statics rooted in an adjacent landblock. Only a
|
|
target-root static is omitted, because the authored replacement supersedes
|
|
it. Each retained owner refresh captures its exact
|
|
`ShadowObjectRegistry` mutation version; a rowless withdrawn owner therefore
|
|
remains freshness-gated when state or payload changes.
|
|
5. Explicit one-work-unit cursors build the complete replacement before the
|
|
activation frame: requested global collision records, cells/topology,
|
|
buildings, cell graph removals, affected static owners, retained-owner
|
|
states, and removal lists. An active-owner mutation restarts capture and
|
|
sealing without touching the active world.
|
|
6. `CommitCollisionGeneration` performs only the final mutation-version check
|
|
and installs the already sealed replacement synchronously on the update
|
|
thread. The dense 256-owner gate measures zero managed bytes in this final
|
|
activation. Only after the complete replacement does Runtime emit
|
|
`CollisionGenerationCommitted` and a ready acknowledgement.
|
|
|
|
The stable borrowed `PhysicsEngine` and `PhysicsDataCache` object identities do
|
|
not change. Presentation and no-window hosts use the same Runtime transaction.
|
|
Network workers still enqueue immutable messages and cannot mutate collision or
|
|
shadow state.
|
|
|
|
## Failure and lifetime rules
|
|
|
|
- A newer admission invalidates an older prepared generation.
|
|
- Cancellation names one admission and its private staging generation. It can
|
|
never withdraw or demote the active landblock, and cancelling a stale receipt
|
|
cannot invalidate a newer admission.
|
|
- Demotion, withdrawal, reset, and disposal invalidate the admission before
|
|
changing the active generation.
|
|
- Disposing a stale/cancelled prepared generation clears only its private
|
|
engine/cache/shadows.
|
|
- The prior complete generation remains queryable throughout preparation.
|
|
- The commit notification is the future lost-cell-registry seam. Slice 3B does
|
|
not implement `GotoLostCell` or change `SetPosition` recovery behavior.
|
|
|
|
## Deterministic evidence
|
|
|
|
The focused Runtime/App tests pin:
|
|
|
|
- previous terrain/cells/buildings/statics remain visible until commit;
|
|
- exactly one notification after a successful complete activation;
|
|
- stale admission replacement has no active-world side effect;
|
|
- movement during staging rejects, refreshes only the dirty owner, and then
|
|
installs its latest cell set;
|
|
- an authoritative state change on a retained rowless owner rejects a stale
|
|
seal and installs the refreshed state on retry;
|
|
- a neighboring static whose shadow crossed the seam is restored atomically
|
|
on reload and its withdrawn-prefix marker clears only at activation;
|
|
- spawn and deletion during staging both reject stale activation;
|
|
- Headless faults immediately after admission and after staging preserve the
|
|
prior complete world and leave no collision admission behind;
|
|
- dense sealing consumes at most one work unit per call, while its final
|
|
256-owner activation allocates zero managed bytes;
|
|
- graphical and no-window publishers use the same Runtime transaction;
|
|
- removal and terminal teardown converge the active ownership ledger.
|
|
|
|
This retires divergence row AD-6. The remaining lost-cell state-machine work is
|
|
deliberately outside this slice.
|