feat(slice2): TeleportArrivalRules.Decide — outdoor holds until landblock loaded (#145 §3.4)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-21 20:12:37 +02:00
parent ad8c24ef8b
commit b47b21570a
2 changed files with 48 additions and 34 deletions

View file

@ -116,29 +116,29 @@ public static class TeleportArrivalRules
/// <param name="claimUnhydratable">The destination cell can never hydrate.</param>
/// <param name="indoor">Destination is an indoor cell (cell index ≥ 0x0100).</param>
/// <param name="indoorCellReady">For an indoor destination, the EnvCell floor has hydrated.</param>
/// <param name="outdoorReady">For an outdoor destination, the physics landblock is loaded
/// (<see cref="AcDream.Core.Physics.PhysicsEngine.IsLandblockLoaded"/> returned true for the
/// destination landblock id). Ignored for indoor destinations.</param>
public static ArrivalReadiness Decide(
bool claimUnhydratable,
bool indoor,
bool indoorCellReady)
bool indoorCellReady,
bool outdoorReady)
{
if (claimUnhydratable)
return ArrivalReadiness.Impossible;
// Indoor (sealed dungeon / building interior): gate on the EnvCell floor hydrating,
// exactly as #135. The dungeon-IN path pre-collapses + waits for the cell struct, and
// its placement validates the specific claimed cell (the indoor resolve is cell-keyed,
// not the grid-snap), so it is unaffected by the overlap that broke teleport-OUT.
// exactly as #135. Unaffected by the outdoor-landblock gate.
if (indoor)
return indoorCellReady ? ArrivalReadiness.Ready : ArrivalReadiness.NotReady;
// Outdoor: place IMMEDIATELY on the server-authoritative position. #145/#138 — holding
// for the destination to stream in is futile: streaming does NOT progress while the
// player is held in PortalSpace (the destination only loads once placement flips the
// state to InWorld). The stale source center landblock is dropped at recenter (see
// GameWindow.OnLivePositionUpdated), so the arrival resolve falls through to the
// server-authoritative cell/position (Resolve NO-LANDBLOCK verbatim) and the player is
// placed grounded there; streaming then loads the destination and the per-frame resolve
// grounds onto it. This trusts the server's teleport position — retail-faithful.
return ArrivalReadiness.Ready;
// Outdoor (#145 §3.4): hold until the physics landblock is registered.
// AddLandblock is atomic (terrain + cells + portals in one write — PhysicsEngine.cs:70),
// so ContainsKey implies the collision mesh is present and resolvable. The hold is
// self-resolving: streaming progresses unconditionally during the hold because the
// streaming controller runs before TeleportArrivalController.Tick (GameWindow.cs:7420-7551).
// The 10 s timeout safety-net (_maxHoldFrames) remains active.
return outdoorReady ? ArrivalReadiness.Ready : ArrivalReadiness.NotReady;
}
}