fix(physics): route remote Positions on contact, not walkability (AP-140)
The two gates that decide whether an accepted remote Position is interpolated
or hard-snapped read `Airborne`, which is `!Body.OnWalkable` — WALKABILITY.
Retail reads CONTACT: InterpolationManager::adjust_offset @0x00555D30 gates its
entire body on `transient_state & 1` @0x00555D52, so a retail body in contact
with a non-walkable face still interpolates.
The two predicates disagree in exactly one state — in contact, not on walkable
ground — which 204d0ae0 turned from unreachable into ordinary. Before it, the
per-tick forge made every non-airborne remote walkable by construction, so the
disagreement could not occur.
Both gates now read `!Body.InContact`: ApplyRemoteContactRouting's flight
carve-out and OnPosition's player-remote arm.
`Airborne` is deliberately NOT re-derived from CONTACT. That would perturb all
five of its writers and contradict a pinned assertion in
RemoteTeleportPlacementTests.Apply_PendingGroundToSteepContact_ (InContact:
true, OnWalkable: false -> Assert.True(remote.Airborne)); a previous
implementer attempted it and correctly backed out rather than editing the
assertion. This narrower shape touches no existing test.
AP-140's register row is retired in this commit, as the row itself specified.
Honest scope: this is a faithfulness fix, not a visible one. ACE derives its
IsGrounded flag with the same floor_z test, so during a slide it almost
certainly reports not-grounded, the classifier returns NoPositionOperation, and
neither arm is taken. Expect no observable change against ACE.
Suite 11,027 passed / 4 skipped / 0 failed (baseline 11,023).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
f4f2579575
commit
2eb39a0250
6 changed files with 273 additions and 33 deletions
|
|
@ -96,7 +96,21 @@ internal sealed class RemotePlacementDriveFixture : IDisposable
|
|||
Orientation = Quaternion.Identity,
|
||||
LastUpdateTime = 1d,
|
||||
State = PhysicsStateFlags.Gravity,
|
||||
TransientState = TransientStateFlags.Active,
|
||||
// AP-140 (retired 2026-08-04): the remote stands on the flat
|
||||
// source landblock, so the sweep's SetPositionInternal commit
|
||||
// would derive CONTACT (`contact_plane_valid` @0x00515430) and
|
||||
// ON_WALKABLE (`contact_plane.N.z >= floor_z`
|
||||
// @0x00515465-0x0051548E) for it. Both bits now have to be
|
||||
// present, because the accepted-Position routing gates read
|
||||
// retail's CONTACT predicate
|
||||
// (`InterpolationManager::adjust_offset` @0x00555D52) rather than
|
||||
// the client `Airborne` walkability flag: a body with a bare
|
||||
// `Active` transient state is in FREE FLIGHT and every routing
|
||||
// test would take the free-flight snap arm. Tests that want free
|
||||
// flight clear these explicitly.
|
||||
TransientState = TransientStateFlags.Active
|
||||
| TransientStateFlags.Contact
|
||||
| TransientStateFlags.OnWalkable,
|
||||
};
|
||||
body.SnapToCell(SourceCell, body.Position, body.Position);
|
||||
Lifetime.Entities.SetPhysicsBody(record, body);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue