feat(slice2): wire outdoorReady into TeleportArrivalReadiness (GameWindow)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-21 20:12:40 +02:00
parent b47b21570a
commit b745850ab8

View file

@ -5471,6 +5471,7 @@ public sealed class GameWindow : IDisposable
// impossible claim / timeout. PortalSpace keeps input frozen meanwhile.
EnsureTeleportArrivalController();
_pendingTeleportRot = rot;
_lastArrivalVerdict = AcDream.App.World.ArrivalReadiness.NotReady; // reset probe state for each new hold
_teleportArrival!.BeginArrival(newWorldPos, p.LandblockId);
}
}
@ -5481,6 +5482,8 @@ public sealed class GameWindow : IDisposable
// runtime deps are wired by then).
private AcDream.App.World.TeleportArrivalController? _teleportArrival;
private System.Numerics.Quaternion _pendingTeleportRot = System.Numerics.Quaternion.Identity;
// §3.4 arrival-gate probe: tracks previous verdict so we log only the flip.
private AcDream.App.World.ArrivalReadiness _lastArrivalVerdict = AcDream.App.World.ArrivalReadiness.NotReady;
// #145: the LANDBLOCK-relative (cell-local) position used to SEED the player
// body's cell-relative CellPosition. This is the ONE place the streaming center
@ -5509,24 +5512,36 @@ public sealed class GameWindow : IDisposable
// Reuses the #107 login readiness triplet (GameWindow.cs:1010-1024), evaluated
// against the teleport's (destPos, destCell): an impossible indoor claim short-
// circuits to immediate placement; otherwise hold until terrain is sampled and,
// for an indoor cell, the cell struct has hydrated.
// for an indoor cell, the cell struct has hydrated. For an outdoor cell, hold
// until the physics landblock is registered (§3.4 #145 residual fix).
private AcDream.App.World.ArrivalReadiness TeleportArrivalReadiness(
System.Numerics.Vector3 destPos, uint destCell)
{
bool claimUnhydratable = IsSpawnClaimUnhydratable(destCell);
// #135: an INDOOR destination (sealed dungeon / building interior) gates on the
// EnvCell FLOOR hydrating. Retail places on the cell floor. An OUTDOOR destination
// places immediately on the server-authoritative position — see TeleportArrivalRules
// (#145: holding is futile because streaming doesn't progress during a PortalSpace
// hold; the stale source landblock is dropped at recenter so the resolve trusts the
// server cell until the destination streams in).
bool indoor = (destCell & 0xFFFFu) >= 0x0100u;
bool indoorCellReady = indoor && !claimUnhydratable
&& _physicsEngine.IsSpawnCellReady(destCell);
// §3.4: for outdoor arrivals, gate on the physics landblock being resident.
// The landblock id is the high 16 bits of the cell id (i.e. destCell & 0xFFFF0000).
// Streaming progresses during the hold because the streaming controller runs before
// TeleportArrivalController.Tick. The 10 s _maxHoldFrames safety-net remains active.
bool outdoorReady = !indoor
&& _physicsEngine.IsLandblockLoaded(destCell & 0xFFFF0000u);
return AcDream.App.World.TeleportArrivalRules.Decide(
claimUnhydratable, indoor, indoorCellReady);
var verdict = AcDream.App.World.TeleportArrivalRules.Decide(
claimUnhydratable, indoor, indoorCellReady, outdoorReady);
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeArrivalGateEnabled
&& verdict != _lastArrivalVerdict
&& verdict != AcDream.App.World.ArrivalReadiness.NotReady)
{
Console.WriteLine(System.FormattableString.Invariant(
$"[arrival-gate] destCell=0x{destCell:X8} landblock=0x{(destCell & 0xFFFF0000u):X8} verdict={verdict} indoor={indoor} landblockCount={_physicsEngine.LandblockCount}"));
}
_lastArrivalVerdict = verdict;
return verdict;
}
// The deferred snap (the original OnLivePositionUpdated steps 2-5), now run only