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:
parent
ad8c24ef8b
commit
b47b21570a
2 changed files with 48 additions and 34 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,49 +4,63 @@ using Xunit;
|
|||
namespace AcDream.App.Tests.World;
|
||||
|
||||
/// <summary>
|
||||
/// The teleport-arrival readiness decision (#145/#138).
|
||||
/// <list type="bullet">
|
||||
/// <item>Unhydratable claim → place immediately via the caller's safety-net (Impossible).</item>
|
||||
/// <item>Indoor (sealed dungeon / building interior) → hold until the EnvCell floor hydrates.</item>
|
||||
/// <item>Outdoor → place IMMEDIATELY on the server-authoritative position. Holding is futile —
|
||||
/// streaming does not progress while the player is held in PortalSpace, so the
|
||||
/// destination can't stream in during a hold. The stale source landblock is dropped at
|
||||
/// recenter (GameWindow.OnLivePositionUpdated), so the resolve falls through to the
|
||||
/// server cell (Resolve NO-LANDBLOCK verbatim) until the destination streams in.</item>
|
||||
/// </list>
|
||||
/// Full truth-table for TeleportArrivalRules.Decide after the §3.4 readiness-gate change.
|
||||
/// </summary>
|
||||
public class TeleportArrivalRulesTests
|
||||
{
|
||||
[Fact]
|
||||
public void Unhydratable_IsImpossible()
|
||||
public void Unhydratable_IsImpossible_Outdoor()
|
||||
{
|
||||
Assert.Equal(ArrivalReadiness.Impossible,
|
||||
TeleportArrivalRules.Decide(claimUnhydratable: true, indoor: false, indoorCellReady: false));
|
||||
// …even for an indoor claim.
|
||||
TeleportArrivalRules.Decide(
|
||||
claimUnhydratable: true, indoor: false,
|
||||
indoorCellReady: false, outdoorReady: false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Unhydratable_IsImpossible_Indoor()
|
||||
{
|
||||
Assert.Equal(ArrivalReadiness.Impossible,
|
||||
TeleportArrivalRules.Decide(claimUnhydratable: true, indoor: true, indoorCellReady: false));
|
||||
TeleportArrivalRules.Decide(
|
||||
claimUnhydratable: true, indoor: true,
|
||||
indoorCellReady: true, outdoorReady: false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Indoor_CellReady_IsReady()
|
||||
{
|
||||
Assert.Equal(ArrivalReadiness.Ready,
|
||||
TeleportArrivalRules.Decide(claimUnhydratable: false, indoor: true, indoorCellReady: true));
|
||||
TeleportArrivalRules.Decide(
|
||||
claimUnhydratable: false, indoor: true,
|
||||
indoorCellReady: true, outdoorReady: false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Indoor_CellNotReady_HoldsNotReady()
|
||||
public void Indoor_CellNotReady_IsNotReady()
|
||||
{
|
||||
Assert.Equal(ArrivalReadiness.NotReady,
|
||||
TeleportArrivalRules.Decide(claimUnhydratable: false, indoor: true, indoorCellReady: false));
|
||||
TeleportArrivalRules.Decide(
|
||||
claimUnhydratable: false, indoor: true,
|
||||
indoorCellReady: false, outdoorReady: false));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Outdoor_PlacesImmediately()
|
||||
public void Outdoor_LandblockLoaded_IsReady()
|
||||
{
|
||||
// #145: never hold an outdoor arrival — place at once on the server position. The
|
||||
// indoorCellReady flag is irrelevant for an outdoor destination.
|
||||
Assert.Equal(ArrivalReadiness.Ready,
|
||||
TeleportArrivalRules.Decide(claimUnhydratable: false, indoor: false, indoorCellReady: false));
|
||||
TeleportArrivalRules.Decide(
|
||||
claimUnhydratable: false, indoor: false,
|
||||
indoorCellReady: false, outdoorReady: true));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Outdoor_LandblockNotLoaded_IsNotReady()
|
||||
{
|
||||
// §3.4: the #145 residual — outdoor arrival on a not-yet-streamed landblock
|
||||
// must hold. Previously returned Ready immediately.
|
||||
Assert.Equal(ArrivalReadiness.NotReady,
|
||||
TeleportArrivalRules.Decide(
|
||||
claimUnhydratable: false, indoor: false,
|
||||
indoorCellReady: false, outdoorReady: false));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue