#126 RETAIL-CORRECTED: restores commit the server Z - retail never re-derives position from surfaces

The user caught the process failure: two snap fixes were written without
reading retails restore code. The named decomp settles it -
CPhysicsObj::SetPositionInternal (0x00515bd0, pc:283892-283945) treats
the supplied Position as INPUT: AdjustPosition resolves which cell
CONTAINS it, CheckPositionInternal/find_valid_position VALIDATES it
through the collision transition, and the no-cell case goes
store_position + GotoLostCell. There is NO terrain or surface
re-grounding anywhere in the restore path. Trust + validate.

Both prior shapes diverged: grounding outdoor claims to terrainZ warped
a roof-deck logout (ACEs authoritative z=127.2 on the AAB3 tower)
through the roof into the building volume -> the transparent-interior
spawn on every login; the cell-walkable scan that replaced it missed
shell-geometry decks entirely (no EnvCell owns the deck surface) and
failed silently - the user logged in transparent at the tower bottom
again.

Fix: a zero-delta outdoor restore above terrain commits the claims Z
verbatim ([snap] line says so); the first physics tick validates and
settles against the REAL collision world (the BR-7 building channel
covers the deck). max(terrain, z) stays as the under-terrain sanity
bound - our recoverable stand-in for retails lost-cell machinery
(documented divergence, same class as the #107 demote).

Suites: App 242+1skip, Core 1422+2skip, UI 420, Net 294.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-11 20:22:17 +02:00
parent b94a7e8017
commit 120aeff720
2 changed files with 54 additions and 50 deletions

View file

@ -788,43 +788,31 @@ public sealed class PhysicsEngine
targetZ = terrainZ;
targetCellId = physics.Terrain.ComputeOutdoorCellId(localCandX, localCandY);
// #126 (2026-06-11): a zero-delta RESTORE of an OUTDOOR claim
// standing far ABOVE terrain means the player logged out on a
// walkable surface that is not the ground — a building roof
// deck (the AAB3 tower's 0x010A slab, z=127.2 over terrain
// 112). Grounding to terrain warps the player THROUGH the
// roof into the building's interior volume, outdoor-classified
// → the transparent-interior spawn. Retail's restore settles
// via AdjustPosition onto real surfaces, not the heightmap.
// Ground to the nearest CELL WALKABLE at/below the claim Z
// (the #111 walkable query — real floors only, never ceiling
// soup); the cell id stays OUTDOOR (the claim is honest: the
// player's center on a deck sits above the slab cell's BSP).
// GfxObj-shell roofs without cells are not covered — file if
// a real case shows.
if (snapDiag && currentPos.Z > terrainZ + stepUpHeight)
// #126 (2026-06-11, RETAIL-CORRECTED same day): a zero-delta
// RESTORE commits the server's position — it does NOT
// re-derive Z. Retail CPhysicsObj::SetPositionInternal
// (0x00515bd0, pc:283892-283945) treats the supplied Position
// as INPUT: AdjustPosition resolves which cell CONTAINS it,
// CheckPositionInternal/find_valid_position VALIDATES it
// through the collision transition, and failure goes
// store_position + GotoLostCell — there is NO terrain/surface
// re-grounding anywhere in the restore path. Our previous
// shapes both diverged: grounding to terrainZ warped a
// roof-deck logout (ACE's authoritative z=127.2 on the AAB3
// tower) THROUGH the roof into the building volume → the
// transparent-interior spawn; the cell-walkable scan that
// replaced it missed shell-geometry decks entirely (no
// EnvCell owns the surface) and failed silently. Trust the
// claim's Z; the first physics tick validates/settles against
// the REAL collision world (BR-7 building channel included).
// max(terrain, z) stays as the under-terrain sanity bound —
// our recoverable stand-in for retail's lost-cell machinery
// (documented divergence, same as the #107 demote).
if (snapDiag && currentPos.Z > terrainZ)
{
float? bestWalkZ = null;
float bestWalkDist = float.MaxValue;
foreach (var cell in physics.Cells)
{
float? wz = WalkableFloorZNearest(
lbPrefix | (cell.CellId & 0xFFFFu), candidatePos, currentPos.Z);
if (wz is null) continue;
if (wz.Value > currentPos.Z + stepUpHeight) continue; // above the claim — not the surface stood on
float dist = MathF.Abs(wz.Value - currentPos.Z);
if (dist < bestWalkDist)
{
bestWalkDist = dist;
bestWalkZ = wz;
}
}
if (bestWalkZ is not null && bestWalkZ.Value > terrainZ)
{
Console.WriteLine(System.FormattableString.Invariant(
$"[snap] OUTDOOR claim 0x{cellId:X8} at z={currentPos.Z:F3} stands on an elevated walkable z={bestWalkZ.Value:F3} (terrain {terrainZ:F3}) — grounding to the walkable, not through it"));
targetZ = bestWalkZ.Value;
}
Console.WriteLine(System.FormattableString.Invariant(
$"[snap] OUTDOOR claim 0x{cellId:X8} z={currentPos.Z:F3} above terrain {terrainZ:F3} — committing the server Z (retail SetPositionInternal shape; physics settles on tick 1)"));
targetZ = currentPos.Z;
}
}
}