The contract's D0 byte-pin refuted the tangent-placement premise: validate_walkable @0x0050d010 is planted for every normal mover (Ghidra + BN + ACE agree; ours is already byte-faithful; only the camera branch is tangent). With the byte-proven bare-radius push-out the coherent retail mechanism is plant-then-lift — the push fires once per settle, raises the body to tangent equilibrium, and both checks go quiet there. The slope float comes from the push, not the placement, exactly as the original substitution's own comment argued. The 84% live fire rate was measured against our push-disabled steady state; the #341 assert-shape flip now reads as order-dependent settle state. The user's 'port the retail pair' therefore maps to relanding AD-66's bare radius alone, with the ten-run stability protocol. The STOP rule paid for itself: an implementer without it would have made ValidateWalkable DIVERGE from retail in the name of faithfulness. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
14 KiB
S4b D0 — byte-pin of OBJECTINFO::validate_walkable's distance basis
Date: 2026-08-07. Author: S4b implementer (single Sonnet agent, no subagents). Status: PREMISE REFUTED — SLICE STOPPED AT D0. No code was changed. D1/D2/D3/D4 were not performed. Nothing is committed.
The question D0 was scoped to answer
Per docs/research/2026-08-07-s4b-tangent-rest-contract.md: does retail's
OBJECTINFO::validate_walkable measure the perpendicular sphere clearance
(dot(center,N)+D − radius, tangent semantics — the same shape as
CPolygon::adjust_sphere_to_plane), or a vertical foot point
(lowPoint = center − (0,0,radius), planted semantics — the shape acdream's
current Transition.ValidateWalkable already implements)?
The contract's explicit, pre-authorized stop condition: "If retail's validate_walkable turns out to ALSO use a vertical foot point, STOP — the whole slice premise changes and the session lead re-decides."
Answer: vertical foot point (planted), for the branch that governs every normal mover (players, creatures — anything not the camera/viewer). The stop condition is triggered by direct byte evidence from two independent decompilers plus ACE's independent C# port. All three agree with each other and with acdream's current code.
Byte evidence
1. Ghidra decompile (primary; PDB-paired, patchmem.gpr, port 8081)
OBJECTINFO::validate_walkable @ 0x0050d010:
TransitionState __thiscall
OBJECTINFO::validate_walkable(OBJECTINFO *this, CSphere *param_1, Plane *param_2,
int param_3, float param_4, SPHEREPATH *param_5,
COLLISIONINFO *param_6, ulong param_7)
{
...
if ((this->state & 4) != 0) { // IsViewer branch
fVar2 = (N.x*center.x + N.y*center.y + N.z*center.z + d) - radius; // BARE radius — tangent
...
return OK_TS / ADJUSTED_TS;
}
// else branch — every normal mover (player, creature, missile, etc.)
fVar2 = center.x*N.x + (center.z - radius)*N.z + center.y*N.y + d + param_4;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// = dot(center, N) + d - radius*N.z + waterDepth -- PLANTED, not tangent
if (-EPSILON <= fVar2) { ... return OK_TS; }
else {
if (param_5->check_walkable != 0) return COLLIDED_TS;
fVar2 = fVar2 / N.z; // zDist
... push sphere up by -fVar2 along Z, step-down interp, etc.
return ADJUSTED_TS;
}
}
Full raw decompile captured via GET /decompile_function?address=0x0050d010
against the currently-open patchmem.gpr project (Sept 2013 EoR build, same
source as docs/research/named-retail/).
this->state & 4 is ObjectInfoState.IsViewer (= 0x4) — confirmed by
references/ACE/Source/ACE.Server/Physics/ObjectInfo.cs:13 and by
LandCell.cs:56's !objInfo.State.HasFlag(ObjectInfoState.IsViewer) && !objInfo.Object.State.HasFlag(PhysicsState.Missile) guard on env-collision
processing. IsViewer marks the camera/eye collision object, not the player's
own movement sphere. Every walking mover (player body, creature, NPC) takes
the else branch — the planted one.
param_4 is waterDepth — confirmed by the caller (below): it is used
exactly once, added directly after the d term:
... + param_2->d + param_4. It is NOT folded into the lowPoint subtraction
— it modifies the threshold, not the sphere's virtual bottom. This is
identical in shape and placement to ACE's
... contactPlane.D + waterDepth and to acdream's current
contactPlane.D + waterDepth (TransitionTypes.cs:3673). AP-10's sink-in
byte-pin: dist = dot(center,N) + D − radius·N.z + waterDepth,
unchanged from acdream's current formula.
2. Binary Ninja pseudo-C (secondary; corroborates, was initially ambiguous)
acclient_2013_pseudo_c.txt:274479-274617. The state & 4 branch (lines
274487-274488) computes ((N.x*cx + N.y*cy + cz*N.z) + d) - radius — bare
radius, matching Ghidra's IsViewer arm exactly.
The else branch (lines 274531-274532) computes
N.y*cy + (cz - radius)*N.z + N.x*cx + d — matching Ghidra's planted arm
exactly, modulo one artifact: BN reuses the SSA name x87_r7_16 for both
the bare radius value (line 274531) and the freshly-computed dot-product
expression one line later (274532), because both values transiently occupy
the same logical x87 stack slot. This is the "flag-idiom artifact class" the
project CLAUDE.md warns about — a naming collision, not an algebraic one; the
raw expression at 274532 is unambiguous regardless of what BN chose to call
its result. arg5 (waterDepth) never appears by name inside BN's rendering
of this function — another BN-only artifact (the float stack parameter gets
folded directly into the compound expression without a preserved argN
token). Ghidra's param_4 resolves this cleanly. This is exactly the
"disassemble if BN is ambiguous" case D0 anticipated, and Ghidra is the
byte-pin of record here.
3. ACE's independently-authored C# port (tertiary cross-check)
references/ACE/Source/ACE.Server/Physics/ObjectInfo.cs:101-172,
ValidateWalkable:
if (State.HasFlag(ObjectInfoState.IsViewer))
{
var dist = Vector3.Dot(checkPos.Center, contactPlane.Normal) + contactPlane.D - checkPos.Radius;
... // bare radius — tangent
}
else
{
var dist = Vector3.Dot(checkPos.Center - new Vector3(0, 0, checkPos.Radius), contactPlane.Normal)
+ contactPlane.D + waterDepth;
... // vertical foot point — planted, waterDepth added after
}
Independently produced (ACE was reverse-engineered years before this project's PDB-named decomp existed), and it agrees with Ghidra byte-for-byte on both branches, including waterDepth's placement. Three independent sources, zero disagreement.
4. Caller context — confirms AD-69 is a separate, correctly-scoped finding
CLandCell::find_env_collisions @ 0x00532f20 (pseudo-C
lines 317070-317113, Ghidra-cross-readable) is the sole caller of
validate_walkable for outdoor terrain. It calls
LandDefs::get_block_offset twice: once to convert
global_low_point into the terrain-poly lookup's block frame (for
find_terrain_poly), and again (0x00533007) to subtract the block offset
from global_sphere->center before the sphere passed into
validate_walkable is built. This is the cell-relative correction AD-69
already names, scoped to CTransition::adjust_offset @ 0x0050a370
(a different function, pc:272271-272393 per the register row) — not to
validate_walkable. AD-69 is unaffected by this finding; it was already
correctly scoped before this slice started.
5. CPolygon::adjust_sphere_to_plane @ 0x00538210 — re-confirmed independently, unaffected
Ghidra decompile (GET /decompile_function?address=0x00538210):
fVar3 = dot(N, center) + d;
fVar4 = dot(N, movement_dir);
if (fVar4 <= EPS) { if (-EPS <= fVar4) return 0; fVar3 -= radius; } // bare radius
else { fVar3 = -radius - fVar3; } // bare radius
fVar3 /= fVar4;
...
Bare radius, both branches — matches AD-66's byte evidence exactly and
matches acdream's existing BSPQuery.cs:364 AdjustSphereToPlane (already a
faithful port, confirmed by direct read: dist = dpPos - validPos.Radius /
dist = -validPos.Radius - dpPos, bare Radius, no N.z factor). This
function's tangent semantics are correct and were never in question.
What this means for the contract's premise
The contract's framing was: "acdream today mixes tangent (BSP walk solve)
with planted (ValidateWalkable) and the planted one wins the resting height
on terrain [instead of retail's own consistent tangent pairing]." That
framing assumed retail pairs tangent-with-tangent (adjust_sphere_to_plane
validate_walkableboth bare-radius) and that acdream's plantedValidateWalkableis therefore a deviation to fix.
The byte evidence shows retail itself pairs tangent-with-planted, not tangent-with-tangent:
| Function | Retail semantics | acdream today |
|---|---|---|
CPolygon::adjust_sphere_to_plane @0x00538210 (BSP walk / push-out during collision resolution) |
tangent, bare radius | tangent, bare radius (BSPQuery.cs:364) — matches |
OBJECTINFO::validate_walkable @0x0050d010, mover branch (per-tick terrain rest/step validation) |
planted, radius·N.z, waterDepth added after | planted, radius·N.z, waterDepth added after (TransitionTypes.cs:3652-3730) — matches |
CTransition::adjust_offset @0x0050a370 safety push-out (AD-66) |
tangent, bare radius (byte-confirmed, withheld pending #341) | planted substitution (radius·N.z), the AD-66 deviation |
acdream's ValidateWalkable is already a byte-faithful port of retail's
mover-path formula — same low-point construction, same epsilon bracket
(dist >= -EPSILON / dist <= EPSILON), same zDist = dist/N.z push-up,
same step-down interpolation guard, same OnWalkable/Contact flag-setting
conditions (state bits 1/2 map onto oi.Contact/oi.OnWalkable exactly).
There is no ValidateWalkable distance-basis change to port — porting one
would make acdream diverge from retail, not converge to it.
D1 item 1 (ValidateWalkable's distance basis → tangent) is refuted and must
not be implemented. Because the contract frames the pair as inseparable
("The pair ports together or not at all" / "STOP — the whole slice premise
changes"), the whole D1/D2/D3 program as specified is out, not just item
1 in isolation. Items 2 (AD-66 bare-radius landing in AdjustOffset) and 3
(AD-69's get_block_offset correction) remain independently well-founded by
their own byte evidence and their own register rows — but landing them was
explicitly conditioned by this contract on the ValidateWalkable pairing
argument that just failed, and the #341 anomaly this slice was named for
(the flipping absorbed-tick behavior on AD-66 alone) has not been
explained by anything found here. Landing AD-66/AD-69 now, under a
different justification than the one this contract authorized, is a
decision for the session lead, not a call for the implementer to make
unilaterally under a refuted contract.
What was NOT done (by design, per the contract's own stop clause)
- D1 (port the pair) — not implemented. No changes to
TransitionTypes.csValidateWalkableorAdjustOffset, no changes toBSPQuery.cs/FlatBspQuery.cs. - D2 (ten-run
RuntimeRemoteUphillProgressTestsretest) — not run under this slice's changes, because there are no changes to test. (The existingS4AdjustOffsetConformanceTests.Uphill_NoContactFlapAcrossTicksand the two[Skip]-ed AD-66 tests were read but left untouched and un-unskipped.) - D3 (conformance tests + sabotage) — not written.
- D4 (register/ISSUES bookkeeping) — not performed; AD-66 and AD-69 remain exactly as they are (AD-66 ACTIVE/WITHHELD, AD-69 FILED, #341 open).
- No files under
src/were edited. No commits were made.
Open question for the session lead
The #341 measurement anomaly (AD-66's bare radius alone producing contradictory absorbed-tick outcomes that flip with test-assert shape) is still unexplained. This slice's finding rules out "ValidateWalkable should also be tangent" as the fix, since that would itself be a retail deviation. Candidate directions the session lead may want to weigh (not investigated here — out of this slice's scope once D0 refuted the premise):
- The anomaly may be a test-harness artifact (the tests' own docstring already flags "flipping with nothing but the shape of the test's post-tick asserts" as suspicious) rather than a real engine behavior difference.
- AD-69's
get_block_offsetgap could be entangled with AD-66's in a way that only shows up when both change together — but that is a testable hypothesis, not yet tested. - The #331 absorb-tick semantics (explicitly OUT of scope for both this contract and S4b) may be the actual source of the flip, independent of AD-66/AD-69 entirely.
Citations
OBJECTINFO::validate_walkable@ 0x0050d010 (Ghidrapatchmem.gprdecompile; BN pseudo-Cacclient_2013_pseudo_c.txt:274479-274617)ObjectInfoState.IsViewer = 0x4(references/ACE/Source/ACE.Server/Physics/ObjectInfo.cs:13)references/ACE/Source/ACE.Server/Physics/ObjectInfo.cs:101-172(ValidateWalkable, independent port)references/ACE/Source/ACE.Server/Physics/Common/LandCell.cs:56(IsViewer/Missile env-collision guard)CLandCell::find_env_collisions@ 0x00532f20 (acclient_2013_pseudo_c.txt:317070-317113; theget_block_offsetcalls andvalidate_walkablecall site)CPolygon::adjust_sphere_to_plane@ 0x00538210 (Ghidra decompile; AD-66's cited address, re-confirmed)src/AcDream.Core/Physics/TransitionTypes.cs:3652-3730(acdream's currentValidateWalkable— read directly, confirmed byte-faithful to the mover branch above)src/AcDream.Core/Physics/TransitionTypes.cs:5501-5655(acdream's currentAdjustOffset, including the AD-66 WITHHELD comment block and AD-69's targetdistcomputation)src/AcDream.Core/Physics/BSPQuery.cs:364-428(acdream's currentAdjustSphereToPlane— read directly, confirmed byte-faithful bare-radius)docs/architecture/retail-divergence-register.mdrows AD-66 (line 65 header context), AD-69 (line 166)tests/AcDream.Core.Tests/Physics/S4AdjustOffsetConformanceTests.cs(the two[Skip]-ed AD-66 tests, read but left untouched)docs/research/2026-08-07-s4b-tangent-rest-contract.md(this slice's pinned contract; the stop clause invoked above is quoted from its D0 section verbatim)