feat(net): port retail physics spawn and event timestamps
Parse the complete PhysicsDesc plus F754/F755 packets, correct every PhysicsState bit, and gate all nine retail update channels with generation-safe immutable snapshots. Preserve ForcePosition, teleport, placement, velocity, parent, pickup, delete, and same-generation CreateObject ordering from the named client. Separate accepted logical lifecycle notifications from retained UI qualities, make GUID replacement and session reset clear every projection exactly once, and add packet, wraparound, malformed-input, parent FIFO, canonical-position, reconnect, and GUID-reuse conformance coverage. Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
parent
d53fe30ffe
commit
8a5d77f7f4
50 changed files with 3809 additions and 649 deletions
|
|
@ -42,7 +42,7 @@ behavior, just with an apparatus artifact in place of clean walkthrough.
|
|||
**indoor cell 0xA9B40150**, target (132.43, 17.20, 94). Live
|
||||
result.Position = target with `collisionNormalValid = false`. Door
|
||||
centered at world XY (132.57, 16.99), BSP radius 1.975, state
|
||||
`0x00010008` = `PERSISTENT_PS | 0x8` (NO `ETHEREAL_PS = 0x4` →
|
||||
`0x00010008` = `HAS_PHYSICS_BSP_PS | REPORT_COLLISIONS_PS` (NO `ETHEREAL_PS = 0x4` →
|
||||
**CLOSED**).
|
||||
- **Tick 22760** — the working block. Player at (133.14, 18.02, 94)
|
||||
in **outdoor cell 0xA9B40029**, target (133.10, 17.60, 94). Live
|
||||
|
|
@ -68,7 +68,7 @@ Confirmed the door state at the time of every walkthrough:
|
|||
| Log line | Event |
|
||||
|---|---|
|
||||
| 10796 | `[setstate]` door state → `0x0001000C` (PERSISTENT + ETHEREAL = OPEN) |
|
||||
| 10993 | `[setstate]` door state → `0x00010008` (PERSISTENT, NOT ethereal = CLOSED) |
|
||||
| 10993 | `[setstate]` door state → `0x00010008` (physics BSP + collision reports, NOT ethereal = CLOSED) |
|
||||
| 10995–11071 | First and last `[bsp-test]` line on door 0x000F4246. All `state=0x00010008` |
|
||||
|
||||
So every `[bsp-test]` hit on the door, and every walkthrough event in
|
||||
|
|
|
|||
|
|
@ -173,6 +173,99 @@ stamp wins. At exactly `0x8000` and throughout the wrap half, the numerically
|
|||
lower stamp wins. This exact boundary must be ported; a generic half-range
|
||||
subtraction helper can choose the opposite result at `0x8000`.
|
||||
|
||||
### 3.1 Event freshness and object generations
|
||||
|
||||
Additional oracles:
|
||||
|
||||
- `SmartBox::HandleReceivedPosition` at `0x00453FD0`
|
||||
- `SmartBox::HandleObjDescEvent` at `0x00453340` and
|
||||
`SmartBox::UpdateVisualDesc` at `0x00451F40`
|
||||
- `SmartBox::HandleDeleteObject` at `0x00451EA0`
|
||||
|
||||
```text
|
||||
on CreateObject(desc):
|
||||
if no live generation: copy all nine desc timestamps wholesale
|
||||
else if desc.Instance is newer: begin a new generation and replace all nine
|
||||
else if current Instance is newer: reject the stale CreateObject
|
||||
else: keep the object and route each PhysicsDesc branch through its
|
||||
ordinary per-channel handler; do not authorize the whole snapshot
|
||||
|
||||
on State / Vector / Movement / ObjDesc:
|
||||
require the exact live Instance
|
||||
require a strictly newer channel timestamp
|
||||
apply only after both gates pass
|
||||
|
||||
on DeleteObject:
|
||||
reject unconditionally when guid is the local player
|
||||
require the exact live Instance before either object projection is removed
|
||||
|
||||
on ParentEvent(parent, child):
|
||||
require the parent's exact live Instance
|
||||
strictly advance the child's Position timestamp
|
||||
apply parent + placement without creating a second position counter
|
||||
|
||||
on PickupEvent(object):
|
||||
require the exact live Instance
|
||||
strictly advance the shared Position timestamp
|
||||
unparent and leave the world, retaining the object and its timestamps
|
||||
|
||||
on Position(localPlayer):
|
||||
require the exact live Instance
|
||||
if ForcePosition is newer:
|
||||
store ForcePosition
|
||||
if incoming Teleport exactly equals stored Teleport:
|
||||
assign Position directly, even when equal or older
|
||||
preserve the current heading
|
||||
do not advance Teleport
|
||||
BlipPlayer via SetPositionSimple (preserve active motion, velocity,
|
||||
contact state, and PositionManager stick)
|
||||
if Contact AND OnWalkable are set and position is valid:
|
||||
send PositionEvent immediately from the post-blip canonical Position
|
||||
return
|
||||
|
||||
tentatively advance Position, requiring strictly newer
|
||||
if stored Teleport is newer than incoming Teleport:
|
||||
restore the old Position timestamp and reject
|
||||
if incoming Teleport is newer: advance Teleport
|
||||
PositionPack supplies placement 0 and velocity (0,0,0) when their flags are absent
|
||||
unset the parent
|
||||
if the object has no active animations: apply the decoded placement id
|
||||
if object is remote: MoveOrTeleport receives the decoded velocity
|
||||
else if Teleport advanced: teleport player and explicitly zero velocity
|
||||
else: constrain/interpolate without installing packet velocity
|
||||
apply the position
|
||||
```
|
||||
|
||||
`PositionPack::UnPack` at `0x00516740` is the source of the absent-as-zero
|
||||
defaults; retaining a prior placement or remote velocity when the wire flag is
|
||||
absent is not retail behavior. The immutable parsed type still preserves
|
||||
presence so diagnostics and conformance tests can distinguish absent from an
|
||||
explicit zero. `HandleReceivedPosition` consumes those defaults only after the
|
||||
timestamp gate admits the normal path. ForcePosition returns before placement
|
||||
or packet velocity is consumed and therefore preserves the player's live
|
||||
heading and velocity.
|
||||
|
||||
`SmartBox::HandlePlayerTeleport` at `0x00452150` handles standalone F751:
|
||||
|
||||
```text
|
||||
on PlayerTeleport(sequence):
|
||||
if sequence is older than current Teleport timestamp: reject
|
||||
equality and newer sequences are accepted
|
||||
enter the waiting/portal state
|
||||
do not advance Teleport here
|
||||
```
|
||||
|
||||
The accepted destination Position packet advances `TELEPORT_TS`. Parsing F751
|
||||
must therefore never publish its sequence directly into outbound AP state.
|
||||
|
||||
Retail queues a packet whose Instance belongs to a not-yet-created future
|
||||
generation. `ParentAttachmentState` now does so for ParentEvent, retaining the
|
||||
parent generation through atomic object replacement. Until `LiveEntityRuntime`
|
||||
owns the complete mixed pending queue, acdream still drops future Movement,
|
||||
Position, State, Vector, Pickup, Delete, and ObjDesc packets without touching
|
||||
the current generation (AD-32); critically, it never adopts the future Instance
|
||||
into the current object.
|
||||
|
||||
## 4. Direct and typed effect packets
|
||||
|
||||
Oracles:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue