fix(teleport #145): IsLandblockLoaded key mismatch — outdoor gate was permanently NotReady
The Slice 2 outdoor readiness gate queried IsLandblockLoaded(destCell & 0xFFFF0000) = e.g. 0x7D640000, but streaming stores landblocks under the EncodeLandblockId form (low 16 = 0xFFFF), e.g. 0x7D64FFFF. The raw ContainsKey never matched, so the outdoor teleport gate could NEVER flip Ready and every outdoor arrival ran to the 600-frame (~10 s) timeout and force-placed. The cascade was still prevented (the timeout force-place lands cleanly), but the gate did no work — the 10 s freeze the apparatus showed was this bug, NOT the #138 streaming stall I first suspected. Root cause found via the apparatus re-test (3-agent investigation wf_8b67a9d1-35c, all high-confidence) + verified against StreamingRegion.cs:99 (EncodeLandblockId | 0xFFFF), PhysicsEngine.cs:79 (stores as-is), GameWindow.cs:5530 (queries & 0xFFFF0000). Fix: IsLandblockLoaded normalizes its arg to the canonical 0xFFFF landblock key, so the prefix form, any contained cell id, and the dat-id form all resolve. Added the regression test the original Slice 2 test missed (it had checked the same 0xFFFF form it added; the real caller passes the 0x..0000 form). Red on the prefix/cell forms before the fix, green after. 9/9. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
589c7cfb57
commit
c8809735f3
2 changed files with 48 additions and 5 deletions
|
|
@ -54,4 +54,37 @@ public class PhysicsEngineIsLandblockLoadedTests
|
|||
|
||||
Assert.False(engine.IsLandblockLoaded(0xA9B4FFFFu));
|
||||
}
|
||||
|
||||
// Regression for the Slice 2 key-mismatch bug (#145 apparatus re-test, 2026-06-21):
|
||||
// landblocks are stored under the EncodeLandblockId form (low 16 = 0xFFFF), but the
|
||||
// teleport gate (TeleportArrivalReadiness) queries with (destCell & 0xFFFF0000) (low
|
||||
// 16 = 0x0000). A raw ContainsKey never matched, so the outdoor gate was permanently
|
||||
// NotReady and every outdoor teleport timed out at 10 s. IsLandblockLoaded must
|
||||
// normalize so ANY id within the landblock resolves to the stored entry.
|
||||
[Theory]
|
||||
[InlineData(0xA9B40000u)] // landblock-prefix form — exactly what the gate passes (destCell & 0xFFFF0000)
|
||||
[InlineData(0xA9B4000Du)] // an outdoor cell id within the landblock
|
||||
[InlineData(0xA9B40143u)] // an indoor cell id within the landblock
|
||||
[InlineData(0xA9B4FFFFu)] // the dat-id storage form
|
||||
public void IsLandblockLoaded_NormalizesLow16_FindsLandblockForAnyContainedId(uint queryId)
|
||||
{
|
||||
var engine = new PhysicsEngine();
|
||||
engine.AddLandblock(0xA9B4FFFFu, MakeFlatTerrain(),
|
||||
Array.Empty<CellSurface>(), Array.Empty<PortalPlane>(),
|
||||
worldOffsetX: 0f, worldOffsetY: 0f);
|
||||
|
||||
Assert.True(engine.IsLandblockLoaded(queryId));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsLandblockLoaded_DifferentLandblockPrefix_ReturnsFalse()
|
||||
{
|
||||
var engine = new PhysicsEngine();
|
||||
engine.AddLandblock(0xA9B4FFFFu, MakeFlatTerrain(),
|
||||
Array.Empty<CellSurface>(), Array.Empty<PortalPlane>(),
|
||||
worldOffsetX: 0f, worldOffsetY: 0f);
|
||||
|
||||
// A different landblock prefix must NOT match, even in the 0x..0000 query form.
|
||||
Assert.False(engine.IsLandblockLoaded(0xA9B50000u));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue