feat(physics): integrate live projectile runtime
Attach the retail projectile driver to canonical LiveEntityRecord ownership, sharing one PhysicsBody and full-cell identity with RemoteMotion regardless of creation order. Apply timestamp-gated state, vector, and position corrections in place, preserve active ordinary-body behavior when Missile clears, and keep renderer, effects, shadows, and spatial buckets synchronized across loaded/pending transitions. Validate malformed packets before canonical timestamp mutation, validate adopted bodies from their current frame rather than stale CreateObject data, serialize late Setup resolution with streaming DAT reads, and preserve classification across clock anomalies. Retain shadow registrations through temporary leave-world residence while logical teardown remains generation-scoped. Add 31 App lifecycle/adversarial tests plus Core shadow suspension coverage, and synchronize the retail pseudocode, architecture, milestones, roadmap, and durable physics memory. Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
parent
f02b995e4f
commit
a51ebc66e9
12 changed files with 2559 additions and 32 deletions
|
|
@ -872,3 +872,105 @@ observable ordering without exposing a half-committed pose to modern sinks.
|
|||
Boundary conformance covers positive and negative X and Y crossings.
|
||||
- TS-2 is retired. AP-83 and AP-91 remain because ordinary missiles are
|
||||
PathClipped and do not arm their dormant PerfectClip time-of-impact paths.
|
||||
|
||||
## 16. Step 7 live integration map (2026-07-14)
|
||||
|
||||
The App integration preserves the same retail object across every spatial and
|
||||
network operation:
|
||||
|
||||
```text
|
||||
after materialized CreateObject:
|
||||
if final PhysicsState has never carried Missile: no projectile component
|
||||
if Setup does not have the audited one ordinary sphere: diagnose, no guess
|
||||
otherwise:
|
||||
construct one PhysicsBody, or adopt the same body already owned by the
|
||||
object's MovementManager
|
||||
validate the inbound frame/vectors, canonicalize the outdoor cell,
|
||||
retain valid friction in [0,1], and clamp elasticity to [0,0.1]
|
||||
apply velocity through set_velocity (immediate 50-unit/second clamp)
|
||||
bind it to LiveEntityRecord.ProjectileRuntime
|
||||
|
||||
when MovementManager and projectile classification arrive in either order:
|
||||
bind MovementManager cell reads/writes to this exact live-record incarnation
|
||||
share its PhysicsBody with ProjectileRuntime
|
||||
install CreateObject velocity/omega when the ordinary body is constructed
|
||||
if projectile classification arrives second, preserve that body's CURRENT
|
||||
vectors/Active state, validate that current body rather than stale
|
||||
CreateObject vectors, normalize its current cell frame, and translate
|
||||
its clock to the App game clock without replaying retained vectors;
|
||||
a safely ignored malformed CreateObject vector cannot veto a later
|
||||
finite body, while a non-finite current body cannot enter simulation
|
||||
|
||||
each frame:
|
||||
for each canonical live record with a projectile component:
|
||||
skip/clear Active through update_object when parented, cell-less,
|
||||
Frozen, Static, or out of world
|
||||
if the object has a PartArray, clear Active beyond the retail
|
||||
96-world-unit player-distance boundary; reactivation stamps now
|
||||
run ProjectilePhysicsStepper with local WorldEntity.Id for self-shadow
|
||||
exclusion and target id zero
|
||||
if this exact record generation still owns this exact component:
|
||||
WorldEntity.SetPosition(resolved body frame)
|
||||
update orientation and full parent cell
|
||||
RebucketLiveEntity only when the full cell changed
|
||||
move the existing collision shadow to the resolved frame
|
||||
publish the new root to the effect-pose owner
|
||||
if Missile is now clear and MovementManager exists: that ordinary owner
|
||||
advances the shared body instead
|
||||
if Missile is clear with no MovementManager: keep advancing the active
|
||||
retained CPhysicsObj; collision reads final state, so missile_ignore
|
||||
and Missile-derived PathClipped are disabled automatically
|
||||
|
||||
fresh State/Vector/Position/Movement packet:
|
||||
reject structurally invalid Position/Vector values before advancing the
|
||||
snapshot or timestamp, even if Missile classification has not arrived
|
||||
first pass the existing per-channel retail timestamp gate
|
||||
State: update the canonical body first, even when missile acquisition is
|
||||
impossible because Setup or sphere data is unavailable; the accepted
|
||||
SetState collision flags must never remain only on the live record.
|
||||
A non-finite local receipt clock does not discard first classification:
|
||||
bind against the controller's last finite game clock (the clock epoch
|
||||
is zero before the first frame), while keeping the wire state authoritative.
|
||||
Update this body without newly activating it; removing Missile
|
||||
disables missile classification but retains the component/body on this
|
||||
object. MovementManager becomes the update owner when present;
|
||||
otherwise the retained active CPhysicsObj continues ordinary physics.
|
||||
A later Missile state reuses that same current body and cell;
|
||||
align the projectile clock at that ownership transfer, preserve the
|
||||
existing Active bit, and never activate a body that was already inactive
|
||||
Vector: set_velocity + omega on this body; clamp immediately and stamp the
|
||||
receipt clock only when the packet reactivates an inactive object; if
|
||||
Missile is currently clear and MovementManager exists, its ordinary
|
||||
Airborne/LeaveGround funnel owns those writes on the shared body
|
||||
Position: hard-correct world frame plus canonical wire cell-local frame,
|
||||
shadow, spatial bucket, and effect pose; do not overwrite velocity
|
||||
Movement: unpack through the normal MovementManager/animation funnel while
|
||||
sharing this exact PhysicsBody and the live record's canonical cell;
|
||||
projectile stepping suppresses the generic remote translation tick,
|
||||
so there is still only one integrator and one spatial authority
|
||||
|
||||
leave_world (pickup/parent):
|
||||
clear InWorld and Active, withdraw collision shadows but retain their exact
|
||||
registration payload and the component with the incarnation
|
||||
fresh Position re-entry restores the same shadows, stamps the current
|
||||
physics clock, and never consumes the time spent outside the world
|
||||
|
||||
loaded/pending transition:
|
||||
the rebucket result is checked in the same physics quantum
|
||||
pending clears InWorld/Active and suspends shadows without teardown
|
||||
hydration restores body/shadow membership even if Missile was cleared while
|
||||
pending; projectile integration itself remains disabled
|
||||
landblock rehydration keys materialized movers by LiveEntityRecord.FullCellId,
|
||||
never by the retained create-time Snapshot.Position
|
||||
|
||||
DeleteObject / generation replacement / session reset:
|
||||
normal LiveEntityRuntime teardown destroys the owning record; the
|
||||
projectile controller has no second GUID dictionary to clear
|
||||
```
|
||||
|
||||
`ProjectileController` runs before animation advancement in the render/update
|
||||
phase and publishes its current root directly; an animated projectile then
|
||||
publishes final part poses later in the same frame. PhysicsScript/particle/light
|
||||
ticks therefore observe the projectile's current predicted root. The controller never
|
||||
creates impact effects, collision messages, damage, or deletion; ACE's later
|
||||
packets remain authoritative for every gameplay outcome.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue