refactor(runtime): own teleport destination correlation

This commit is contained in:
Erik 2026-07-26 17:52:34 +02:00
parent 38b3773cb9
commit 6a063a27d4
23 changed files with 1209 additions and 487 deletions

View file

@ -1,5 +1,7 @@
using System.Numerics;
using AcDream.App.Streaming;
using AcDream.App.World;
using AcDream.Core.Physics;
using AcDream.Core.Selection;
using AcDream.Runtime;
using AcDream.Runtime.World;
@ -10,6 +12,7 @@ public sealed class WorldRevealCoordinatorTests
{
private sealed class State
{
public RuntimeWorldTransitState Transit { get; private set; } = null!;
public bool RenderReady { get; set; }
public bool CollisionReady { get; set; }
public bool CompositesReady { get; set; }
@ -24,6 +27,7 @@ public sealed class WorldRevealCoordinatorTests
{
transit ??= new RuntimeWorldTransitState(
logs is null ? null : new Action<string>(logs.Add));
Transit = transit;
return new WorldRevealCoordinator(
transit,
isRenderNeighborhoodReady: (_, _) => RenderReady,
@ -48,7 +52,7 @@ public sealed class WorldRevealCoordinatorTests
var state = new State { CompositesReady = true };
WorldRevealCoordinator coordinator = state.Build();
long generation = coordinator.Begin(RuntimePortalKind.Login, 0x11340021u);
long generation = coordinator.BeginLogin(0x11340021u);
Assert.Equal(1, generation);
Assert.Equal(1, state.InvalidateCount);
@ -66,7 +70,7 @@ public sealed class WorldRevealCoordinatorTests
CollisionReady = true,
};
WorldRevealCoordinator coordinator = state.Build();
coordinator.Begin(RuntimePortalKind.Login, outdoorCell);
coordinator.BeginLogin(outdoorCell);
WorldRevealReadinessSnapshot first = coordinator.PrepareAndEvaluate(outdoorCell);
state.CompositesReady = true;
@ -91,12 +95,16 @@ public sealed class WorldRevealCoordinatorTests
CompositesReady = true,
};
WorldRevealCoordinator coordinator = state.Build(logs);
coordinator.Begin(RuntimePortalKind.Portal, outdoorCell);
long generation = BeginPortal(
coordinator,
state.Transit,
outdoorCell,
sequence: 1);
state.CompositesReady = true;
Assert.True(coordinator.Evaluate(outdoorCell).IsReady);
coordinator.ObserveMaterialized();
coordinator.ObserveMaterialized();
coordinator.ObserveMaterialized(generation, 1, outdoorCell);
coordinator.ObserveMaterialized(generation, 1, outdoorCell);
coordinator.ObserveWorldViewportVisible();
coordinator.Complete();
@ -108,6 +116,40 @@ public sealed class WorldRevealCoordinatorTests
Assert.Single(logs, line => line.Contains("event=materialized", StringComparison.Ordinal));
}
[Fact]
public void PortalBegin_WrongSequenceOrCellCannotAcquireHostResources()
{
const uint destinationCell = 0x3032001Cu;
var state = new State { CompositesReady = true };
var scheduler = new RecordingDestinationScheduler();
WorldRevealCoordinator coordinator = state.Build(streaming: scheduler);
RuntimeWorldTransitTestDriver.AcceptPortalDestination(
state.Transit,
destinationCell,
sequence: 7);
Assert.False(coordinator.TryBeginPortal(
8,
destinationCell,
out _));
Assert.False(coordinator.TryBeginPortal(
7,
0x11340021u,
out _));
Assert.Empty(scheduler.Begins);
Assert.Equal(0, state.InvalidateCount);
Assert.Equal(0, coordinator.Snapshot.Generation);
Assert.True(coordinator.TryBeginPortal(
7,
destinationCell,
out long generation));
Assert.Equal(
(generation, destinationCell, 1),
Assert.Single(scheduler.Begins));
Assert.Equal(1, state.InvalidateCount);
}
[Fact]
public void Cancel_PreventsLateDestinationWorkFromMutatingLifecycle()
{
@ -118,11 +160,18 @@ public sealed class WorldRevealCoordinatorTests
CompositesReady = true,
};
WorldRevealCoordinator coordinator = state.Build();
coordinator.Begin(RuntimePortalKind.Portal, 0x11340021u);
long generation = BeginPortal(
coordinator,
state.Transit,
0x11340021u,
sequence: 1);
coordinator.Cancel();
coordinator.PrepareAndEvaluate(0x11340021u);
coordinator.ObserveMaterialized();
coordinator.ObserveMaterialized(
generation,
1,
0x11340021u);
coordinator.ObserveWorldViewportVisible();
coordinator.Complete();
@ -155,14 +204,22 @@ public sealed class WorldRevealCoordinatorTests
isSpawnClaimUnhydratable: _ => false,
quiescence: quiescence);
Assert.Equal(1, coordinator.Begin(RuntimePortalKind.Login, 0x11340021u));
Assert.Equal(2, coordinator.Begin(RuntimePortalKind.Portal, 0x11340021u));
Assert.Equal(1, coordinator.BeginLogin(0x11340021u));
long generation = BeginPortal(
coordinator,
transit,
0x11340021u,
sequence: 1);
Assert.Equal(2, generation);
Assert.False(availability.IsWorldAvailable);
Assert.Equal(2, availability.QuiescedGeneration);
Assert.Equal(1, audio.SuspendCalls);
Assert.True(coordinator.Evaluate(0x11340021u).IsReady);
coordinator.ObserveMaterialized();
coordinator.ObserveMaterialized(
generation,
1,
0x11340021u);
coordinator.Complete();
Assert.True(availability.IsWorldAvailable);
@ -193,14 +250,21 @@ public sealed class WorldRevealCoordinatorTests
isSpawnClaimUnhydratable: _ => false,
quiescence: quiescence,
streaming: scheduler);
long generation = coordinator.Begin(
RuntimePortalKind.Portal,
0x3032001Cu);
const uint destinationCell = 0x3032001Cu;
const ushort teleportSequence = 1;
long generation = BeginPortal(
coordinator,
transit,
destinationCell,
teleportSequence);
Assert.False(availability.IsWorldAvailable);
Assert.True(coordinator.Evaluate(0x3032001Cu).IsReady);
Assert.True(coordinator.Evaluate(destinationCell).IsReady);
coordinator.ObserveMaterialized();
coordinator.ObserveMaterialized(
generation,
teleportSequence,
destinationCell);
Assert.True(availability.IsWorldAvailable);
Assert.False(coordinator.Snapshot.WorldViewportObserved);
@ -259,13 +323,21 @@ public sealed class WorldRevealCoordinatorTests
() => calls.Add("spatial")),
availability);
coordinator.Begin(RuntimePortalKind.Portal, 0x3032001Cu);
const uint destinationCell = 0x3032001Cu;
long generation = BeginPortal(
coordinator,
transit,
destinationCell,
sequence: 1);
frame.Tick(1f / 60f);
Assert.Equal(["network", "commands"], calls);
calls.Clear();
Assert.True(coordinator.Evaluate(0x3032001Cu).IsReady);
coordinator.ObserveMaterialized();
Assert.True(coordinator.Evaluate(destinationCell).IsReady);
coordinator.ObserveMaterialized(
generation,
1,
destinationCell);
frame.Tick(1f / 60f);
Assert.Equal(["objects", "network", "commands", "spatial"], calls);
@ -297,9 +369,11 @@ public sealed class WorldRevealCoordinatorTests
isSpawnClaimUnhydratable: _ => false,
quiescence: quiescence,
streaming: scheduler);
long generation = coordinator.Begin(
RuntimePortalKind.Portal,
0x3032001Cu);
long generation = BeginPortal(
coordinator,
transit,
0x3032001Cu,
sequence: 1);
coordinator.ResetSession();
@ -317,12 +391,17 @@ public sealed class WorldRevealCoordinatorTests
var scheduler = new RecordingDestinationScheduler();
WorldRevealCoordinator coordinator = state.Build(streaming: scheduler);
long first = coordinator.Begin(
RuntimePortalKind.Portal,
0x11340021u);
long second = coordinator.Begin(
RuntimePortalKind.Portal,
0x20210123u);
long first = BeginPortal(
coordinator,
state.Transit,
0x11340021u,
sequence: 1);
state.Transit.EndTeleport();
long second = BeginPortal(
coordinator,
state.Transit,
0x20210123u,
sequence: 2);
Assert.Equal(
[
@ -342,12 +421,17 @@ public sealed class WorldRevealCoordinatorTests
WorldRevealCoordinator coordinator =
state.Build(renderResources: resources);
long first = coordinator.Begin(
RuntimePortalKind.Portal,
0x11340021u);
long second = coordinator.Begin(
RuntimePortalKind.Portal,
0x20210123u);
long first = BeginPortal(
coordinator,
state.Transit,
0x11340021u,
sequence: 1);
state.Transit.EndTeleport();
long second = BeginPortal(
coordinator,
state.Transit,
0x20210123u,
sequence: 2);
Assert.Equal([first, second], resources.Begins);
coordinator.RevealWorldViewport();
@ -356,6 +440,33 @@ public sealed class WorldRevealCoordinatorTests
Assert.Equal([second], resources.Ends);
}
private static long BeginPortal(
WorldRevealCoordinator coordinator,
RuntimeWorldTransitState transit,
uint destinationCell,
ushort sequence)
{
Assert.True(transit.TryQueueTeleportStart(sequence));
Assert.True(transit.ActivateQueuedTeleport());
Assert.True(transit.OfferTeleportDestination(
new RuntimeTeleportDestination(
EntityGuid: 0x50000001u,
InstanceSequence: 1,
PositionSequence: 1,
TeleportSequence: sequence,
ForcePositionSequence: 1,
Position: new Position(
destinationCell,
Vector3.Zero,
Quaternion.Identity)),
teleportTimestampAdvanced: true));
Assert.True(coordinator.TryBeginPortal(
sequence,
destinationCell,
out long generation));
return generation;
}
private sealed class RecordingAudioQuiescence : AcDream.App.Audio.IWorldAudioQuiescence
{
public int SuspendCalls { get; private set; }