fix(G.3a): validated-claim placement keeps the claim's landblock prefix (#133)

The #111 validated-claim branch returned lbPrefix | (cellId & 0xFFFF), where
lbPrefix is found by searching resident landblocks for one containing the
candidate position. A dungeon EnvCell's local Y can be negative, so the dungeon
landblock fails the [0,192) bounds test and the loop matches a neighbouring
(e.g. Holtburg) resident block -> the validated claim 0x00070143 got re-stamped
0xA9B30143, making the client mis-resolve the player to the wrong landblock and
spam ACE with rejected moves. The validated claim's full id is authoritative;
return it directly. Byte-identical for the login case (position in the claim's
own landblock); fixes the far-teleport dungeon case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-13 18:27:45 +02:00
parent e7058caa79
commit 2ce5e5c862
2 changed files with 157 additions and 1 deletions

View file

@ -638,9 +638,23 @@ public sealed class PhysicsEngine
{
Console.WriteLine(System.FormattableString.Invariant(
$"[snap] claim=0x{cellId:X8} pos=({currentPos.X:F3},{currentPos.Y:F3},{currentPos.Z:F3}) VALIDATED -> grounded to its walkable floor z={claimFloorZ.Value:F3}"));
// #133 (2026-06-13): return the VALIDATED claim's OWN full cell id,
// NOT lbPrefix | (cellId & 0xFFFF). lbPrefix is found by scanning
// resident landblocks for one whose [0,192) local bounds contain
// the candidate XY — but a dungeon EnvCell's local Y can be NEGATIVE
// (server teleport to 0x00070143 at local (70,-60,0.01)). The dungeon
// landblock fails the localY>=0 bounds test, so the loop matches a
// neighbouring still-resident block (e.g. Holtburg 0xA9B3), re-stamping
// the validated claim 0x00070143 -> 0xA9B30143. The client then
// mis-resolves the player into the wrong landblock and spams ACE with
// rejected moves. The validated claim's prefix is AUTHORITATIVE; a
// position falling in a neighbouring resident landblock must not
// re-stamp it. Byte-identical for the login case (the position lies in
// the claim's own landblock, so lbPrefix == cellId & 0xFFFF0000);
// diverges only — and correctly — in the far-teleport dungeon case.
return new ResolveResult(
new Vector3(candidatePos.X, candidatePos.Y, claimFloorZ.Value),
lbPrefix | (cellId & 0xFFFFu),
cellId,
IsOnGround: true);
}
}