fix(physics): #307 — PreviousTeleport was always 0 on the live Position path
Shipped defect in route 2 (9966b531), found while reviewing route 4a.
`InboundPhysicsStateController.TryApplyPosition` built its AcceptedPhysicsTimestamps
via `Current(gate, teleportAdvanced: ...)`, omitting `previousTeleport`, which
defaulted to a literal 0. The only site that populated it was the deferred
initial-create path — which is why the continuation executor was correct and
every newer consumer was not.
Consequence in shipped code: route 2 feeds this into
`ValidAcceptedAuthority`, which requires Previous == Accepted for a
ForcePosition. Any local player whose TELEPORT_TS is nonzero — anyone who has
portalled or recalled this session — had the authority rejected and the force
correction SILENTLY DROPPED. The user's @pklite acceptance was genuine but
narrow: that character had not teleported, so the stamp was still 0.
Second latent consequence: with an accepted stamp >= 0x8000, wrap-safe
TeleportRegressed also fires against the 0 and rejects ordinary Apply positions,
not just ForcePosition.
The fix captures `previousTeleport = gate.TeleportTimestamp` BEFORE
`TryAcceptPositionEvent` mutates it, matching the shape the deferred path
already used. Ordering is the whole point: capturing after would make
Previous == Accepted unconditionally, so ValidAcceptedAuthority's check would
pass vacuously — the symptom would disappear while the semantics broke.
Also removes the footgun that allowed it. `Current`'s parameter is now
`ushort? previousTeleport = null` resolving to `gate.TeleportTimestamp`, so the
eleven non-Position channels — none of which can move TELEPORT_TS — get
"previous == current" by omission rather than a literal 0 that is
indistinguishable from a genuine "never teleported".
Consumer audit: only TryApplyPosition was defective. The two route-2 call sites
trace back to it; the RuntimeEntityObjectLifetime sites source from
TryAcceptDeferredPosition and were already correct.
Tests discrimination-verified by reverting the argument to 0: the stamp test
fails Expected 10 / Actual 0, and the classifier test fails Expected
SetPositionSimple / Actual RejectedAuthority — the shipped defect reproduced
exactly.
Gates: complete Release solution 10,935 passed / 4 skipped / 0 failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
b633b10967
commit
19d9509497
3 changed files with 206 additions and 4 deletions
|
|
@ -763,6 +763,50 @@ it. Do #297 FIRST — #298 depends on it.
|
|||
`data_7dXXXX` symbol) and
|
||||
`tests/AcDream.Core.Tests/Chat/WeenieErrorMessagesTests.cs`.
|
||||
|
||||
## C4 accepted-position authority — 2026-08-03
|
||||
|
||||
- **#307 — DONE (2026-08-03) — `AcceptedPhysicsTimestamps.PreviousTeleport` was
|
||||
always 0 on the live Position path, silently dropping every local-player
|
||||
ForcePosition correction after the character's first teleport.**
|
||||
`InboundPhysicsStateController.TryApplyPosition` called its private
|
||||
`Current(gate, teleportAdvanced: …)` helper without the `previousTeleport`
|
||||
argument, which defaulted to a literal `0`. The only site that populated it
|
||||
was the deferred initial-create path `TryAcceptDeferredPosition`, which is
|
||||
why `RuntimeInitialCreateContinuationExecutor` was correct and every newer
|
||||
consumer was not.
|
||||
|
||||
**Live blast radius.** Shipped in C4 route 2 (`9966b531`).
|
||||
`LiveEntityNetworkUpdateController.cs` and
|
||||
`RuntimeLiveEntitySessionController.cs` feed the value into
|
||||
`RuntimeAcceptedPositionDriveController.TryExecuteAcceptedLocalPosition`,
|
||||
where `RuntimeAuthoritativePositionRouteClassifier.ValidAcceptedAuthority`
|
||||
requires `PreviousTeleportSequence == AcceptedTeleportSequence` for a
|
||||
`ForcePosition` disposition — which is exactly what retail's FORCE_POSITION
|
||||
branch guarantees (`SmartBox::HandleReceivedPosition` @0x00453FD0 fires only
|
||||
when the packet's teleport stamp equals the live one, and never advances it).
|
||||
With `Previous` pinned to 0, any player whose TELEPORT_TS had advanced — i.e.
|
||||
anyone who had portalled or recalled that session — had the authority
|
||||
rejected and the server's force correction dropped. Route 2's user
|
||||
acceptance was genuine but narrow: the acceptance character had never
|
||||
teleported, so the stamp was still 0. A second latent consequence: with
|
||||
an accepted stamp ≥ 0x8000 the wrap-safe `TeleportRegressed` check would
|
||||
also have fired against the 0, rejecting ordinary `Apply` positions too.
|
||||
|
||||
**Fix.** Capture `previousTeleport = gate.TeleportTimestamp` BEFORE
|
||||
`TryAcceptPositionEvent` mutates it (the exact shape
|
||||
`TryAcceptDeferredPosition` already used) and pass it through. The zero
|
||||
default on `Current` is removed outright — the parameter is now `ushort?`
|
||||
defaulting to the gate's own live stamp, so the channels that cannot move
|
||||
TELEPORT_TS get "previous == current" by omission instead of a silent 0 that
|
||||
is indistinguishable from a real "never teleported".
|
||||
|
||||
Regression tests:
|
||||
`InboundPhysicsStateControllerTests.TryApplyPosition_ReportsThePreEventTeleportStamp`
|
||||
and
|
||||
`…LocalPlayerForcePositionAfterATeleport_ClassifiesAsAnAcceptedForceCorrection`
|
||||
(the second drives the real classifier and fails with `RejectedAuthority`
|
||||
against the pre-fix behaviour).
|
||||
|
||||
## C3c placement cutover — 2026-08-02
|
||||
|
||||
- **#276 — OPEN — SpawnPlacementSettler discards the settle's resolved
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue