fix(phys): #111 - a validated indoor claim is authoritative at the snap; stop the whole-landblock bestCell floor-pick from clobbering it
The [snap] apparatus (issue111-snap1.log) caught the mechanism live: ACE restored a CLEAN pair (0xA9B40171, on-floor) which AdjustPosition validated - and the legacy Resolve then committed 0xA9B4013F instead: its bestCell floor- pick scans EVERY CellSurface in the landblock (123 at Holtburg) for "any floor under this XY nearest currentZ" and breaks same-height ties by iteration order. The wrong cell then fails containment on the first movement -> outdoor demote inside the building -> the #111 transparent interior. This free-pick also explains the earlier "committed verbatim" mystery (the winning tie happened to echo the input pair) AND seeded the ACE poison loop: our outbound heartbeats reported the clobbered cell, ACE persisted it, and the NEXT login inherited it (this run's [spawn-adjust] rejecting 0xA9B4013F is exactly that echo coming back). Fix (retail SetPositionInternal shape): when AdjustPosition VALIDATED an indoor claim, the cell choice is settled - the snap grounds Z onto the validated cell's own floor (find_valid_position's settle role, :283426) and returns; it never re-picks the cell from floor geometry. Claims whose cell has no own floor surface under the XY (thresholds, stair lips) fall through to the legacy path unchanged; mover-shaped calls (delta != 0, tests) untouched. [snap] diagnostic kept (snaps only - one line per login/teleport). Baseline: Core 1381+4 pre-existing #99 failures+1 skip; App/UI/Net green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
383af0ab5f
commit
5f1eb7c4b1
1 changed files with 46 additions and 0 deletions
|
|
@ -582,6 +582,13 @@ public sealed class PhysicsEngine
|
|||
|
||||
var candidatePos = currentPos + new Vector3(delta.X, delta.Y, 0f);
|
||||
|
||||
// #111 apparatus: one [snap] line per Resolve call (entry + teleport
|
||||
// arrival only — low volume, permanent). The gate-3/4/5 runs committed
|
||||
// ACE's restored pair VERBATIM through this method while every read
|
||||
// path should have changed Z or cell — this line answers which branch
|
||||
// actually ran. Remove or demote to env-gate once #111 closes.
|
||||
bool snapDiag = (delta.X == 0f && delta.Y == 0f);
|
||||
|
||||
// Find the landblock this candidate position falls in.
|
||||
// #106 follow-up (2026-06-09): capture its high-16 prefix — every
|
||||
// computed cell id below is returned FULL (lbPrefix | low). The old
|
||||
|
|
@ -605,11 +612,47 @@ public sealed class PhysicsEngine
|
|||
}
|
||||
|
||||
if (physics is null)
|
||||
{
|
||||
if (snapDiag)
|
||||
Console.WriteLine(System.FormattableString.Invariant(
|
||||
$"[snap] claim=0x{cellId:X8} pos=({currentPos.X:F3},{currentPos.Y:F3},{currentPos.Z:F3}) branch=NO-LANDBLOCK (lbs={_landblocks.Count}) -> verbatim"));
|
||||
return new ResolveResult(candidatePos, cellId, IsOnGround: false);
|
||||
}
|
||||
|
||||
float localCandX = candidatePos.X - physics.WorldOffsetX;
|
||||
float localCandY = candidatePos.Y - physics.WorldOffsetY;
|
||||
|
||||
// #111 (2026-06-10): a VALIDATED indoor claim is AUTHORITATIVE for the
|
||||
// cell — retail SetPositionInternal commits the AdjustPosition cell and
|
||||
// only settles Z (CheckPositionInternal → find_valid_position, :283426);
|
||||
// it never re-picks the cell from floor geometry. The legacy bestCell
|
||||
// floor-pick below scans EVERY CellSurface in the landblock (123 at
|
||||
// Holtburg) and breaks same-height ties by iteration order — on a live
|
||||
// login it clobbered ACE's clean, validated claim 0xA9B40171 with
|
||||
// 0xA9B4013F (issue111-snap1.log), putting the player in a wrong cell
|
||||
// → outdoor demote on first movement → transparent interior (#111).
|
||||
// Snap shape only (zero delta): ground Z onto the validated claim's own
|
||||
// floor when it has one under this XY; cells without their own floor
|
||||
// surface here (thresholds, stair lips) fall through to the legacy path.
|
||||
if (snapDiag && adjustedFound && (cellId & 0xFFFFu) >= 0x0100u)
|
||||
{
|
||||
CellSurface? claimSurface = null;
|
||||
foreach (var c in physics.Cells)
|
||||
{
|
||||
if ((c.CellId & 0xFFFFu) == (cellId & 0xFFFFu)) { claimSurface = c; break; }
|
||||
}
|
||||
float? claimFloorZ = claimSurface?.SampleFloorZ(candidatePos.X, candidatePos.Y);
|
||||
if (claimFloorZ is not null)
|
||||
{
|
||||
Console.WriteLine(System.FormattableString.Invariant(
|
||||
$"[snap] claim=0x{cellId:X8} pos=({currentPos.X:F3},{currentPos.Y:F3},{currentPos.Z:F3}) VALIDATED -> grounded to its floor z={claimFloorZ.Value:F3}"));
|
||||
return new ResolveResult(
|
||||
new Vector3(candidatePos.X, candidatePos.Y, claimFloorZ.Value),
|
||||
lbPrefix | (cellId & 0xFFFFu),
|
||||
IsOnGround: true);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the candidate position falls on any indoor cell floor.
|
||||
// Pick the cell whose floor Z is closest to the entity's current Z.
|
||||
CellSurface? bestCell = null;
|
||||
|
|
@ -757,6 +800,9 @@ public sealed class PhysicsEngine
|
|||
|
||||
// Step-height enforcement: block upward movement that exceeds the limit.
|
||||
float zDelta = targetZ - currentPos.Z;
|
||||
if (snapDiag)
|
||||
Console.WriteLine(System.FormattableString.Invariant(
|
||||
$"[snap] claim=0x{cellId:X8} pos=({currentPos.X:F3},{currentPos.Y:F3},{currentPos.Z:F3}) cells={physics.Cells.Count} bestCell=0x{(bestCell?.CellId ?? 0u):X8} bestZ={(bestCellZ?.ToString("F3") ?? "none")} terrainZ={terrainZ:F3} indoor={currentlyIndoor} -> targetZ={targetZ:F3} targetCell=0x{(lbPrefix | (targetCellId & 0xFFFFu)):X8} stepReject={zDelta > stepUpHeight}"));
|
||||
if (zDelta > stepUpHeight)
|
||||
{
|
||||
// Too steep to step up — reject horizontal movement.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue