fix(physics): port retail stair edge backprobe

This commit is contained in:
Erik 2026-07-31 09:26:28 +02:00
parent 5a0f9868a6
commit 2dcb4f1d94
6 changed files with 218 additions and 6 deletions

View file

@ -0,0 +1,108 @@
# #271 — Stair-side uphill reversal capture
**Date:** 2026-07-31
**Status:** closed; retail control flow restored and user live gate passed
## Symptom
When the local player ran diagonally uphill while pressing into the side of
an outdoor staircase, the character could suddenly move backward and rapidly
slide to the bottom. The symptom was intermittent because it required the
forward candidate to hit the side wall while the step-down recovery crossed a
tread edge.
This is not an RDP, render-rate, animation, or gravity symptom. It reproduced
inside the pure Core collision resolver from one captured input frame.
## Live evidence
The bounded capture is under the ignored local artifact pointer:
`artifacts/issue271-stair-side/LATEST.txt`
It contains 677 local-player physics quanta plus the matching resolver stream.
The first decisive frame is quantum 310:
```text
current = (133.03775, 75.53931, 59.608147)
target = (133.33783, 76.42308, 59.608147)
input = forward + run
result = (133.18779, 75.19872, 59.316677)
normal = (-1, approximately 0, approximately 0)
```
The X side-wall collision was valid, but the tangential Y component reversed:
an uphill request of `+0.88377` produced `-0.34059`. Three frames later,
quantum 313 snapped from Z `59.52598` to terrain Z `58.005`. A second attempt
reproduced the same family at quanta 479482, falling from Z `60.96376` to
`58.005`.
## Retail oracle
Named retail:
- `CTransition::edge_slide` at `0x0050B3D0`
- current-walkable branch at `0x0050B44A`
- no-walkable back-probe at `0x0050B4580x0050B50F`
- `SPHEREPATH::precipice_slide` at `0x0050CC80`
Retail tests only the current `SPHEREPATH::walkable` pointer. If it is null,
retail:
1. offsets the failed candidate back to the current sphere center;
2. runs `step_down` there to rediscover the surface actually under the mover;
3. restores the failed candidate;
4. runs `precipice_slide` against the newly discovered polygon; or
5. returns `COLLIDED_TS` when the back-probe found no walkable polygon.
Retail has no substitution of an older saved walkable polygon in either null
case.
## ACDream divergence and root cause
`EdgeSlideAfterStepDownFailed` previously called
`SpherePath.RestoreLastWalkable()`:
- before deciding whether to enter the retail back-probe; and
- again when the back-probe found no current walkable polygon.
`LastWalkable` is a separate ACDream history used by the still-open
CliffSlide compatibility path. At a staircase side wall it could describe the
preceding tread rather than the surface below the current player position.
Promoting it into the current slot bypassed retail's back-probe.
`PrecipiceSlide` then projected the failed forward candidate along the stale
tread edge, producing the backward/downhill displacement seen in the capture.
The fix removes both stale-history promotions from the edge-slide dispatch.
Current walkable state still takes retail's direct precipice path; absent
state now always takes retail's current-position back-probe.
## Deterministic regression
`Issue185OutdoorStairsSeamReplayTests` reuses the captured
`0x01000AC5` staircase collision fixture and the exact quantum-310 position,
contact plane, movement delta, player flags, and 1.5 m Setup step-down height.
Pre-fix:
```text
out = (133.187790, 75.346481, 59.430882)
```
Fixed:
```text
out = (133.187790, 76.078186, 60.016247)
```
The regression requires meaningful positive uphill progress and forbids a
downhill Z displacement. The complete Core Release suite passes 4,108 tests /
2 skips; the complete Release solution passes 10,062 tests / 5 skips.
## Live acceptance
The user repeatedly ran uphill while pressing into both sides of the affected
staircase. Movement remained stable and the former rapid downhill reversal did
not recur. The client then closed through the normal logout path, with ACE
confirming graceful logout.