fix(phys): #107 gate-run regression — auto-entry hold opened on a mid-population cache read; wait for the claimed cell itself

Gate-run finding (wedge-107-gate-indoor-login.log + dat scan): ACE saved the
cell one room off (claim 0xA9B40172, position inside 0xA9B40171 — adjacent
rooms). FindVisibleChildCell corrects this fine when hydrated (proven by the
new AdjacentRoomClaim regression test), but the live entry committed the raw
claim: IsSpawnCellReady's "any cell struct in the landblock => claim is bogus,
proceed" disambiguator observed the MID-POPULATION state (interiors hydrate in
id order on the background worker; the render-thread predicate read the cache
mid-loop) and opened the gate before the claim and its stab neighbors were
cached. AdjustPosition then saw a null cell struct and silently passed the
claim through; the first movement demoted the player to outdoor inside the
house — the user-visible "transparent interior, see straight through walls"
(render is downstream of membership: an outdoor-classified viewer only sees
the interior through the doorway flood).

Fix: the hold now waits for THE CLAIMED CELL's struct, full stop
(IsSpawnCellReady simplification; HasAnyCellStructInLandblock removed).
Claims that can never hydrate are filtered by GameWindow against the dat's
LandBlockInfo.NumCells range (memoized IsSpawnClaimUnhydratable), and
PhysicsEngine.Resolve carries a loud lost-cell-equivalent safety net: an
indoor claim with NO cell struct AND NO CellSurface floor data demotes to the
outdoor landcell with a [spawn-adjust] line instead of committing raw
(retail GotoLostCell :283418; documented divergence). Partial hydration
(CellSurface present, struct pending) keeps the legacy floor-snap behavior —
HasCellSurface uses the file's masked-low-word norm so bare-id fixtures and
full-id production both resolve.

Baseline restored: Core 1381 (+4 new #107 conformance tests) + 4 pre-existing
#99-era failures + 1 skip; App 223 / UI 420 / Net 294.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-10 13:10:01 +02:00
parent 34fcbc3806
commit e4f6750e09
4 changed files with 130 additions and 24 deletions

View file

@ -160,21 +160,6 @@ public sealed class PhysicsDataCache
/// (indoor room geometry). No-ops if the id is already cached or the
/// CellStruct has no physics BSP.
/// </summary>
/// <summary>
/// #107: true when ANY cell struct for the landblock (high-16 prefix of
/// <paramref name="cellId"/>) is cached. Cell structs for a landblock are
/// registered as a batch by the streaming completion, so "some cells present
/// but the claimed one absent" means the claimed id is bogus (poisoned save)
/// rather than still-streaming.
/// </summary>
public bool HasAnyCellStructInLandblock(uint cellId)
{
uint prefix = cellId & 0xFFFF0000u;
foreach (var key in _cellStruct.Keys)
if ((key & 0xFFFF0000u) == prefix) return true;
return false;
}
public void CacheCellStruct(uint envCellId, DatReaderWriter.DBObjs.EnvCell envCell,
CellStruct cellStruct, Matrix4x4 worldTransform)
{