feat(physics): port retail complete object frame pipeline

Restore the named-retail object update order across local, remote, static, projectile, animation, shadow, teleport, and effect lifetimes. Separate authoritative root commits from spatial rebucketing, preserve per-owner hook/FIFO ordering, and remove update-path allocations with exact lifecycle and residency gates.

Add deterministic conformance, adversarial lifetime, GUID-reuse, pending-cell, quaternion, timestamp, and allocation coverage. Release build is warning-free and all 6,446 tests pass with five intentional skips; retail, architecture, and adversarial reviews are clean.

Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-20 09:10:31 +02:00
parent 31a0889f08
commit f961d70023
77 changed files with 12513 additions and 1871 deletions

View file

@ -207,16 +207,22 @@ src/
Physics/
ProjectileController.cs -> live-record projectile orchestration/corrections
RemotePhysicsUpdater.cs -> ordinary/Hidden remote narrow-tick integration
LiveEntityOrdinaryPhysicsUpdater.cs -> manager-less body Transition commits
RemoteInboundMotionDispatcher.cs -> animation-optional retail UM funnel
RemoteTeleportController.cs -> incarnation-scoped loaded/pending placement owner
RemoteTeleportHook.cs -> ordered retail teleport teardown seam
RemoteTeleportPlacement.cs -> collision-seated SetPosition transition commit
World/
LiveEntityRuntime.cs -> canonical identity/state/body/projectile/spatial ownership
RetailInboundEventDispatcher.cs -> update-thread packet/frame FIFO barrier
LiveEntityPresentationController.cs -> Hidden/NoDraw/effect/collision presentation
LiveEntityTeardown.cs -> failure-isolated multi-owner lifecycle drain
RetailLiveFrameCoordinator.cs -> object/physics-before-network frame barrier
Rendering/
GameWindow.cs -> still owns too much runtime wiring
LiveEntityAnimationScheduler.cs -> canonical ordinary live-object workset
RetailStaticAnimatingObjectScheduler.cs -> retail static-animation workset
StaticLiveRootCommitter.cs -> static root pose/collision commit boundary
TerrainModernRenderer.cs -> mandatory bindless+MDI terrain path
Wb/WbDrawDispatcher.cs -> ordinary live/static entity draw dispatch
Wb/EnvCellRenderer.cs -> indoor cell-shell draw path
@ -285,6 +291,67 @@ What exists and is active:
owns the frame;
delete, generation replacement, pickup/parent leave-world, and session reset
use `LiveEntityRuntime`'s normal lifecycle and never create a second GUID map.
- `LiveEntityAnimationScheduler` is the one update-thread walk over spatial
live roots. It admits each incarnation's `RetailObjectQuantumClock`, advances
PartArray state, then selects exactly one movement owner: remote motion, the
retained projectile body, or `LiveEntityOrdinaryPhysicsUpdater` for a
manager-less canonical body. Every hook boundary revalidates record,
component, and object-clock epoch before transition, manager-tail, pose, or
rebucket commits. `RetailStaticAnimatingObjectScheduler` remains a separate
workset, matching `CPhysics::UseTime`: Setup DefaultAnimation is installed at
PartArray construction, while DAT and live PhysicsState-Static owners advance
whole elapsed intervals through `animate_static_object` semantics. A live
static owner shares the record's canonical PartArray and PhysicsBody; raw
static omega commits the root, effect pose, and collision shadow together,
while zero omega does not reflood collision and Hidden never restores it.
The typed animation view copies the runtime's concrete spatial dictionary
through reusable storage, so update and render classification allocate
nothing and scale with resident owners rather than retained KnownObjects.
- `RemoteInboundMotionDispatcher` is the single remote
`MovementManager::unpack_movement` owner. Animated and animation-less live
objects differ only by the presence of a PartArray dispatch sink; packet
head interrupt/style routing, MoveTo cases, case-0 wholesale state, sticky,
and long-jump ordering cannot drift between two `GameWindow` branches.
- Ordinary remote/manager-less Transition commits publish the resolved body,
`WorldEntity` root, and full cell before invoking the canonical rebucket
writer. That writer is a re-entrant lifetime boundary: the caller immediately
revalidates the exact record/runtime and publishes no collision shadow or
manager tail if the destination became pending or the GUID was replaced.
`LiveEntityPresentationController` owns the symmetric non-projectile
collision-residency edge: loaded-to-pending suspends the retained shadow,
hydration restores it immediately, and `OnLiveEntityReady` reconciles an
object that materialized pending before its collision registration existed.
Direct local-player and authoritative remote publication use the same
ordering: commit root, rebucket, then prove exact-record spatial residency
before touching collision. Remote shadows are pose/cell-gated, including
complete sign-invariant orientation for offset/multipart Setup shapes and a
forced refresh on cell-only transitions. Projectile residency remains
exclusively in `ProjectileController`.
- `RetailInboundEventDispatcher` wraps every live-object session callback and
each complete live-object frame phase. Synchronous App observers can enqueue
another packet, but that packet cannot interleave halfway through the older
packet's collision, rebucket, shadow, pose, hook, or manager tail. The live
record retains independent Position, State, Vector, and Movement authority
versions plus a narrow shared velocity version for the three packet families
that can install velocity. This mirrors retail's single update-thread FIFO
without incorrectly making an accepted State cancel an accepted Position.
Its direct frame/packet path accepts state plus a cached static callback and
performs zero allocation; only real nested reentrancy materializes a queued
heterogeneous operation.
- Projectile prediction is a two-phase transaction around retail's
`process_hooks` slot. `BeginQuantum` holds a candidate while leaving the
canonical body at its begin frame; `CompleteQuantum` commits only if the
exact record/runtime and prediction version still match. Position, Vector,
and State corrections therefore win if accepted between the two halves.
Teleport placement uses the same exact-record and per-channel authority
checks, so a newer Position supersedes an older resolver while an independent
State is preserved alongside the completed placement.
- `EntityEffectPoseRegistry` assigns a monotonic lifetime to every local-ID
incarnation. `AnimationHookFrameQueue` snapshots it before semantic
`AnimationDone` callbacks and revalidates it before every semantic completion
and every routed hook. A delete/local-ID reuse during capture or during an
earlier hook can never advance the displaced sequencer or send the old
owner's remaining sound, particle, or light hooks to its replacement.
- `RemoteTeleportController` owns the placement half of a fresh remote
teleport after `RemoteTeleportHook` has torn down movement/target state. It
collision-seats loaded destinations through `RemoteTeleportPlacement`; an