acdream/docs/research/2026-08-01-runtime-local-player-physics-publication.md

287 lines
17 KiB
Markdown

# Runtime local-player physics publication - 2026-08-01
## Scope
This is placement Slice 4B2 checkpoints 4-6. It adds the dormant,
presentation-independent transaction which prepares and assigns ownership of
one local-player `PhysicsBody` and `PlayerMovementController`, retains an exact
post-ownership evaluation lease, and commits the canonical Runtime SetPosition
activation in retail order. No App or Headless production route invokes this
transaction yet, so graphical and no-window game behavior is unchanged and
AP-1/AD-1 remain open until their hosts cut over.
Checkpoint 6 consumes the prepared placement operation only after the exact
body/controller/identity/collision envelope is current. It publishes FullCell,
world residence, host, shadow, workset, object-clock, and ordered Place state
from that same Runtime-owned dormant body. There is no second body, mirrored
gameplay owner, rollback mutation, or presentation callback inside the
canonical tail.
## Ownership contract
`RuntimeLocalPlayerPhysicsPublicationState` is the sole owner of unpublished
local-player body/controller candidates. Each candidate is bound to a token
containing:
- The exact `RuntimeEntityKey` and authored SetPosition placement token.
- A monotonic publication ID.
- The nonzero canonical local-player server GUID and exact identity revision.
- The record's physics-body and object-clock ownership epochs.
- The movement state's controller ownership epoch.
- The entity directory's session-lifetime authority.
Preparation constructs a private controller, body, and object clock. It applies
the exact authored cell frame, orientation, Setup sphere list, scale, step
heights, and accepted final physics state without mutating the canonical entity,
shared object clock, engine/worksets, shadow registry, FullCell, host state, or
presentation. The candidate remains explicitly out of world and inactive. No
method exposes its controller, body, clock, or another mutable reference while
it is owned by the publication transaction.
This checkpoint accepts only a pristine initial graph: no canonical body,
movement controller, physics host, remote motion, projectile, acquisition or
binding operation, or remote-placement contract may exist. It cannot replace or
upgrade a live graph. The local-player identity must be live, nonzero, and name
the same server GUID as the exact entity incarnation.
Unpublished candidates and ownership-committed dormant controllers reject live
movement operations: update, public SetPosition, blip, outbound-position
capture, movement/position send tracking, and shared-engine position commit.
Only the checkpoint-6 activation transaction may promote `RuntimeOwnedDormant`
to `RuntimePublished`; preparation and evaluation never invoke that transition.
Once a
Runtime-owned dormant or published controller is replaced, reset, or disposed,
its terminal retirement state rejects the same operations plus
body/configuration mutation and manager acquisition. Publicly constructed
legacy controllers keep their existing standalone behavior.
## Failure-atomic commit
Commit revalidates every authority after preparation:
- The entity record is the current incarnation and is not accepted for delete.
- The local-player identity still has the token's exact GUID and revision and
has not been disposed.
- The exact authored SetPosition operation and sealed command remain current.
- Session, body, object-clock, and controller ownership epochs still match.
- The body/controller/host/remote/projectile graph remains completely pristine,
with no acquisition, binding, or remote-placement operation in progress.
Only after validation completes does the callback-free update-thread tail:
1. Rebind the candidate controller from its private clock to the record's exact
canonical object clock and mark it Runtime-owned but dormant.
2. Store the candidate's exact body on the canonical record, advancing the
physics ownership epoch once.
3. Store the same controller in `RuntimeLocalPlayerMovementState`, advancing the
controller ownership epoch once.
The dormant controller rejects every live/configuration operation after these
stores; ownership commit alone cannot tick physics, mutate the canonical clock,
or publish an outbound frame. These stores allocate no new gameplay owner,
invoke no host or presentation callback, and cannot replay an older
incarnation. Replacing SetPosition,
changing any accepted physics authority, binding remote/projectile state,
replacing body/clock/controller ownership, delete plus GUID reuse, reset, or
disposal causes the token to reject. An identity switch away and back also
rejects because its revision changed. A rejected or superseded candidate is
discarded and cannot perform a later live operation. Reset and disposal converge
the publication ledger to zero candidates.
Repeated stores of the same body/controller do not advance their epochs; real
bind, replacement, and unbind edges do. This makes ABA-shaped reference changes
observable even if a later value happens to equal an earlier reference.
## Dormant SetPosition evaluation lease
Ownership commit now returns one private activation token captured only after
the canonical body and controller stores. It binds the exact entity key,
authored placement token and sealed command, local identity GUID/revision,
session lifetime, and the post-store physics-body, object-clock, and controller
ownership epochs. The owner retains the same record, body, controller, and
command behind that token; no caller can substitute an equivalent-looking
body or rebuild the mover.
`EvaluateActivation` revalidates that complete lease and calls Core
`PhysicsEngine.SetPosition` synchronously with an immutable request. Core's
transaction is pure: it returns committed, deferred-cell, or rejected
placement data without writing the canonical body, FullCell, clock, spatial
worksets, shadows, collision-report owners, host, operation stage, or Place
projection. A missing cell therefore leaves the exact body dormant and the
authored operation retryable. During evaluation, a valid result likewise
remains only an immutable receipt; checkpoint 6's separate commit API consumes
that receipt only after revalidating the complete activation envelope.
Each evaluation carries an append-only, stable-order union of every cell read
by the complete Core transaction: the AdjustPosition seed and adjusted cell,
visible-child probes (including rejected lateral siblings), rejected
portal/building containment probes, transition/compass retries, and every
normal or scatter attempt. Rejected probes enter only the authority union and
never the final successful shadow/CrossCell footprint. Scatter keeps that union
in retained scratch and materializes its
immutable receipt exactly once after the final attempt, avoiding quadratic
copy/allocation growth at the 64-attempt retail ceiling. The final
`CrossCellIds` remains the successful placement's authored
shadow footprint; failed scatter probes cannot leak into that commit payload.
Runtime seals every distinct queried landblock against the exact collision
generation, the global collision-world authority, and the dynamic-shadow
mutation revision. An active replacement admission rejects evaluation even
before it commits, while begin/cancel, a re-entrant generation commit, or any
owner insert/remove/move/state/suspend/reflood mutation invalidates an older
receipt.
Entry restrictions also consult the live `ClientObjectTable` for the resolved
house object, owner and complete restriction record, plus the mover's monarch.
The receipt therefore seals the exact object-table reference, the engine's
monotonic binding epoch, and the table's synchronous mutation revision. Object
creation/removal, owner-property, guest-list, or mover-monarch updates invalidate
the receipt; a null/fresh replacement and an equal-revision A-B-A binding cycle
cannot resurrect it. Retained `ClientObject` owner, monarch, and restriction
setters synchronously advance every exact owning table even when callers mutate
the object directly rather than re-submit it through `AddOrUpdate`. Replacement,
removal, and clear detach that observer exactly, and every
`HouseRestrictionRecord` freezes a defensive snapshot of its input guest map so
no caller-owned dictionary or mutable downcast can alter entry authority behind
the revision.
`IsEvaluationCurrent` accepts only the newest receipt for the exact activation
lease and rejects it after a position/vector/state/object-description/Create
authority change, identity revision, body/controller replacement, session or
incarnation change, or any sealed collision/shadow authority change.
Re-evaluation supersedes the older receipt without mutating world state.
Re-entrant reset or delete-plus-GUID-reuse during Core evaluation immediately
retires the invalid lease instead of leaving an orphaned dormant graph. An
existing activation lease also blocks candidate preparation even if an
external owner has already cleared the body/controller references; explicit
discard is required before a new candidate can be prepared. Reset and disposal
retire the lease, body, and dormant controller and include the pending
activation in the ownership convergence ledger.
## Canonical activation and retail ordering
The implementation follows the named-retail chain rather than treating
SetPosition as a single opaque callback:
- `CPhysicsObj::SetPosition` at `0x005160C0` owns the outer placement call.
- The internal wrapper at `0x00515BD0` evaluates residence and collision.
- `CPhysicsObj::SetPositionInternal(CTransition*)` at `0x00515330` commits the
accepted frame/contact prefix and later shadow/cell state.
- `CPhysicsObj::enter_world` at `0x00516170` is the final live edge.
- `CPhysicsObj::leave_world` at `0x005155A0` is the canonical retirement edge.
Runtime splits that chain into a prepared, callback-free transaction and an
ordered notification suffix:
1. Install the accepted frame and contact prefix on the still-dormant body and
perform the first acceleration calculation.
2. Open one narrow dormant ground phase and invoke `HitGround` or
`LeaveGround`. Movement reapplication may call retail `set_velocity`, but
the phase closes with `Active=false`; the body is still out of world, has no
host/spatial membership, and its object clock is inactive.
3. Synchronize accepted State and Vector authorities, run the post-ground
acceleration/sliding phase, and dispatch the already-installed collision
batch.
4. Revalidate the complete ownership/collision envelope. Accepted State and
Vector updates are synchronized; Position, ObjDesc, Create, Setup,
incarnation, identity, collision-generation, body, controller, host, or
session displacement aborts the old transaction.
5. Apply velocity-current physical response and stationary bits, prepare the
final shadow mutation and Place receipt, then perform the callback-free
FullCell/body/host/controller/spatial/object-clock tail.
6. Dispatch exact shadow notifications and the ordered Place projection only
after the complete live graph is visible.
Collision and shadow mutations use explicit prepare/apply/dispatch receipts.
Receipt dispatch is exact-once and owner-local, so reverse-order receipts for
different owners remain valid while a superseding mutation of the same owner
stops the stale suffix. Collision owner states carry the exact SetPosition
batch ID. Reentrant Position or newer-batch replacement suppresses remaining
reciprocal/environment callbacks, and abort cleanup force-ends/removes only the
still-exact old batch, including reverse rows and the environment latch. The
combined Runtime physics ownership ledger includes pending collision and
shadow SetPosition receipts; teardown cannot report convergence while either
receipt remains.
Candidate construction applies the accepted `PhysicsDesc` values in retail
`CPhysicsObj::set_description` order before sealing ownership: final state,
friction, clamped elasticity, `set_velocity` (including the 50-unit clamp),
and angular velocity. Network acceleration remains parse-only because retail
recalculates it from the final physics state. This initial vector bootstrap is
required even when the SetPosition receipt's source Vector authority is still
current; the later refresh intentionally skips in that case. Collision
callbacks may advance State/Vector authority without invalidating the
immutable geometry/identity envelope, and a changed Vector authority refreshes
the dormant body through the same `set_velocity` path before physical response.
A deferred-cell commit atomically suspends an authored shadow registration and
consumes its notification receipt. Explicit publication discard cancels the
exact SetPosition lease and body/controller ownership, while the suspended
registration remains owned by the live entity/shadow registry and is reusable
by a later activation. A deterministic discard -> generation-ready -> new
activation gate proves the same registration restores without stale rows or a
pending receipt. Entity/lifetime teardown remains the terminal owner of that
suspended registration.
## Gates
- Candidate privacy and live-operation rejection.
- Pristine-only admission for body, controller, host, remote/projectile,
acquisition/binding, and remote-placement ownership.
- Exact local-player identity, identity-switch, and disposed-identity rejection.
- Exact same-body ownership in entity record and dormant movement controller.
- Initial PhysicsDesc velocity, angular velocity, friction, and elasticity
bootstrap, including activation with the retail 50-unit velocity clamp.
- Dormant rejection after ownership commit plus the controller-level
`dormant -> activated -> live` lifecycle contract exercised by checkpoint 6.
- No mutation of SetPosition, FullCell, spatial roots, host projections,
shadows, worksets, world residence, or presentation during preparation or
evaluation; the separately gated activation commit owns those mutations.
- Replacement by position, vector, final physics state, object description,
CreateObject, remote/projectile/body/clock/controller ownership, and explicit
placement cancellation.
- Delete plus same-GUID reincarnation.
- Candidate replacement, reset, disposal, and ownership convergence.
- Publication/activation sequence exhaustion is preflighted before candidate
allocation or replacement, leaving no private or canonical owner behind.
- Shadow-registry reset invalidates even a prepared, unapplied shapeless
transaction which owns no logical rows or pending dispatch receipt.
- Pure committed/deferred/rejected SetPosition evaluation with bit-exact
body-state snapshots and no canonical, collision-report, projection,
clock, FullCell, host, shadow, workset, or operation-stage mutation.
- Complete stable-order queried-cell capture across AdjustPosition,
visible-child lookup, normal/scatter retries, map-edge/deferred, rejected,
committed, and defensive NoCell outcomes. Scatter deliberately retains
retail's RNG consumption; only its authority footprint and commit payload
are deterministic for a fixed draw sequence.
- Newest-receipt selection, active-admission rejection, collision-generation
replacement, re-entrant begin/cancel and commit invalidation, plus dynamic
shadow insert/move/state/suspend/remove invalidation.
- Exact object-table reference/revision/binding authority, including
post-evaluation and re-entrant house-object, owner, guest-list, and mover-
monarch mutations plus null/fresh/equal-revision ABA replacement. Direct
retained-object setters, replacement/removal/clear observer lifetime, shared
multi-table ownership, and frozen guest-map input are covered explicitly.
- Re-entrant reset and delete/GUID-reuse convergence plus activation-lease
overwrite prevention after an external body/controller clear.
- Post-ownership position, vector, object-description, Create, identity,
body, and controller authority replacement.
- Terminal stale-controller rejection after replacement, reset, and disposal.
- Body/controller epochs advance only on actual ownership changes.
The checkpoint-6 focused publication/collision suite passes 129/129, the
focused Core shadow transaction suite passes 16/16, and the complete Runtime
project passes 695/695 under invariant globalization. The Runtime Release build
passes with zero warnings and zero errors. Broader Core/App/solution and
connected gates remain for the parent integration checkpoint. Under the
machine's Swedish current culture, the three previously known formatting
assertions remain unrelated (`0,5` versus `0.5` and localized sky text), so the
canonical Runtime gate runs under invariant globalization.
## Next checkpoint
Cut the graphical and no-window local-player hosts over to this Runtime-owned
activation transaction, then delete their duplicate SetPosition
activation/publication paths. The cutover must preserve the same exact body,
controller, shadow payload, deferred-cell lease, collision receipt ordering,
and graceful teardown proven here; no host may reconstruct or replay the
canonical transaction.