fix(physics): #345 — a grounded mover glides along a too-steep face; validate_walkable's return is scoped as retail's bytes scope it
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run
Retail's OBJECTINFO::validate_walkable @0x0050d010 initializes its return slot to OK (0x0050d025) and assigns ADJUSTED only inside the below-plane guard, immediately after the push executes (0x0050d249). The guard-fail path — grounded, OnWalkable, plane too steep — jumps past the contact write, the push, and the assignment (0x0050d1b9 -> 0x0050d251): retail deliberately IGNORES the steep plane at primary validation so the insert proceeds, the step-down phase fails on the steep landing, and the edge family produces the per-tick lateral glide. ACE flattened this into an unconditional return Adjusted (ObjectInfo.cs:169) and we inherited it; our TransitionalInsert then retried the byte-identical Adjusted forever — the user's stop-instead-of-slide. Evidence chain: the user's retail observation (the axiom), the live cdb glide profile (edge_slide/cliff_slide 594 each in lockstep, step_up 0), the D0 implementer's correct STOP (fixtures reproduced the stuck fingerprint while faithfully executing the ACE-shaped reading — refuting the reading, not the code), and the capstone byte-decode both Opus reviewers re-derived independently, including the stack-slot frame arithmetic and every ret site's eax. The conformance fixture is the live topology: flat and steep terrain triangles sharing ONE cell's diagonal (a cell-boundary face does NOT reproduce the loop — the cell-scoped primary sample never validates a neighbour's triangle — and is pinned as supplementary). Sabotage: restoring the unconditional Adjusted reds the discriminator with the exact stuck position (0.325 m lateral, 28/30 stuck ticks vs 2.602 m / 14/30 fixed; reviewer B's independent five-angle table is monotone 10-85 degrees). Stuck ticks are counted from positions so the assertion survives the eventual probe strip. In-game glide gate PASSED 2026-08-08: "Well it works, we are sliding. I cant detect any speed change from retail." Filed alongside: #347 + AD-70 (our glide alternates arm/move at half retail's per-tick rate — retail redirects within the tick; next up by user direction), AD-71 (the guard's mutable WalkableAllowance operand vs retail's fixed is_valid_walkable global — now return-value-bearing), and the reviewers' named residuals in the #345 closure entry (placement-arm flip, other-cell coverage gap, EdgeSlide-less projectiles, ACE's server-side shared misport predicting remote drift-then-snap on steep terrain). The unported IsViewer arm of validate_walkable is noted in the D0 doc. Suite: clean-room complete solution 11,271 passed / 4 skipped / 0 failed; Core assembly re-run green after the review-driven test hardening. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
7542cfd3c2
commit
ab89ebdf92
5 changed files with 413 additions and 5 deletions
|
|
@ -3743,6 +3743,15 @@ public sealed class Transition
|
|||
// contactPlane.Normal.Z is 1 for flat ground, so this is just dist.
|
||||
float zDist = dist / contactPlane.Normal.Z;
|
||||
|
||||
// Retail 0x0050d025/0x0050d249: the return value starts OK and
|
||||
// becomes Adjusted ONLY when the push below executes. A grounded
|
||||
// mover already OnWalkable dipping below a TOO-STEEP plane skips
|
||||
// the whole block (0x0050d1b9 -> 0x0050d251) and returns OK —
|
||||
// primary validation deliberately ignores the steep face so the
|
||||
// insert proceeds and the step-down failure routes into the
|
||||
// edge-slide family (#345: retail's glide-along-steep-slope).
|
||||
// ACE's unconditional Adjusted here is a misport.
|
||||
var result = TransitionState.OK;
|
||||
bool walkable = contactPlane.Normal.Z >= sp.WalkableAllowance;
|
||||
if (sp.StepDown || !oi.OnWalkable || walkable)
|
||||
{
|
||||
|
|
@ -3751,7 +3760,8 @@ public sealed class Transition
|
|||
|
||||
if (sp.StepDown)
|
||||
{
|
||||
// Validate step-down interpolation factor.
|
||||
// Validate step-down interpolation factor
|
||||
// (failure returns Collided: retail 0x0050d28b).
|
||||
float interp = (1f - (-1f / (sp.StepDownAmt * sp.WalkInterp)) * zDist) * sp.WalkInterp;
|
||||
if (interp >= sp.WalkInterp || interp < -0.1f)
|
||||
{
|
||||
|
|
@ -3766,6 +3776,7 @@ public sealed class Transition
|
|||
|
||||
// Push the sphere up out of the terrain.
|
||||
sp.AddOffsetToCheckPos(new Vector3(0f, 0f, -zDist));
|
||||
result = TransitionState.Adjusted;
|
||||
}
|
||||
|
||||
// #345 probe (2026-08-08): named local, same rationale as
|
||||
|
|
@ -3780,9 +3791,9 @@ public sealed class Transition
|
|||
PhysicsDiagnostics.TraceTransitValidateWalkable(
|
||||
oi.SelfEntityId, "below-push", dist, waterDepth,
|
||||
oi.Contact, sp.StepDown, belowPushGuardPassed,
|
||||
contactPlane.Normal, TransitionState.Adjusted);
|
||||
contactPlane.Normal, result);
|
||||
|
||||
return TransitionState.Adjusted;
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void CacheWalkableContext(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue