refactor(runtime): own world reveal generation

This commit is contained in:
Erik 2026-07-26 17:09:54 +02:00
parent 4ab98b080e
commit a6860d5563
26 changed files with 1294 additions and 502 deletions

View file

@ -3,7 +3,9 @@ using AcDream.App.Audio;
using AcDream.App.Streaming;
using AcDream.Core.Selection;
using AcDream.Core.World;
using AcDream.Runtime;
using AcDream.Runtime.Entities;
using AcDream.Runtime.World;
using DatReaderWriter.DBObjs;
namespace AcDream.App.Tests.Streaming;
@ -11,12 +13,14 @@ namespace AcDream.App.Tests.Streaming;
public sealed class WorldGenerationQuiescenceTests
{
private const uint LandblockId = 0x1010FFFFu;
private const uint DestinationCell = 0x10100001u;
private const uint ServerGuid = 0x70000001u;
[Fact]
public void ActiveGeneration_HidesWorldQueriesButRetainsCanonicalOwners()
public void RuntimeGeneration_HidesWorldQueriesButRetainsCanonicalOwners()
{
var availability = new WorldGenerationAvailabilityState();
var transit = new RuntimeWorldTransitState();
var availability = new WorldGenerationAvailabilityState(transit);
var world = new GpuWorldState(availability: availability);
WorldEntity entity = Entity();
world.AddLandblock(new LoadedLandblock(
@ -32,10 +36,12 @@ public sealed class WorldGenerationQuiescenceTests
Assert.Single(world.LandblockEntries);
Assert.Single(world.LandblockBounds);
availability.Begin(17);
long generation =
transit.BeginReveal(RuntimePortalKind.Portal, DestinationCell);
world.CopyLiveEntitiesNearLandblock(LandblockId, 0, nearby);
Assert.False(availability.IsWorldAvailable);
Assert.Equal(generation, availability.QuiescedGeneration);
Assert.False(world.IsLiveEntityVisible(key));
Assert.Empty(world.LandblockEntries);
Assert.Empty(world.LandblockBounds);
@ -43,17 +49,25 @@ public sealed class WorldGenerationQuiescenceTests
Assert.Same(entity, Assert.Single(world.Entities));
Assert.True(world.IsLoaded(LandblockId));
Assert.False(availability.End(16));
Assert.False(transit.Complete(generation + 1));
Assert.False(availability.IsWorldAvailable);
Assert.True(availability.End(17));
Assert.True(transit.AcknowledgeDestinationReadiness(
Ready(generation, DestinationCell)));
Assert.True(transit.AcknowledgeMaterialized(
generation,
DestinationCell));
Assert.True(transit.Complete(generation));
Assert.True(world.IsLiveEntityVisible(key));
Assert.Same(entity, Assert.Single(world.LandblockEntries).Entities.Single());
Assert.Same(
entity,
Assert.Single(world.LandblockEntries).Entities.Single());
}
[Fact]
public void SupersededReveal_StaysContinuouslySilentAndOnlyLatestGenerationReopens()
public void SupersededReveal_StaysSilentAndOnlyLatestRuntimeEdgeResumesAudio()
{
var availability = new WorldGenerationAvailabilityState();
var transit = new RuntimeWorldTransitState();
var availability = new WorldGenerationAvailabilityState(transit);
var world = new GpuWorldState(availability: availability);
world.AddLandblock(new LoadedLandblock(
LandblockId,
@ -65,23 +79,36 @@ public sealed class WorldGenerationQuiescenceTests
selection.Select(ServerGuid, SelectionChangeSource.World);
var audio = new RecordingAudioQuiescence();
var quiescence = new WorldGenerationQuiescence(
availability,
selection,
world,
guid => guid == ServerGuid ? Key(entity) : null,
audio);
quiescence.Begin(1);
quiescence.Begin(2);
quiescence.End(1);
WorldGenerationQuiescenceEdge firstEdge =
quiescence.CaptureBegin();
long first =
transit.BeginReveal(RuntimePortalKind.Login, DestinationCell);
quiescence.CommitBegin(firstEdge);
WorldGenerationQuiescenceEdge secondEdge =
quiescence.CaptureBegin();
long second =
transit.BeginReveal(RuntimePortalKind.Portal, DestinationCell);
quiescence.CommitBegin(secondEdge);
Assert.False(transit.Complete(first));
Assert.False(availability.IsWorldAvailable);
Assert.Equal(2, availability.QuiescedGeneration);
Assert.Equal(second, availability.QuiescedGeneration);
Assert.Null(selection.SelectedObjectId);
Assert.Equal(1, audio.SuspendCalls);
Assert.Equal(0, audio.ResumeCalls);
quiescence.End(2);
Assert.True(transit.AcknowledgeDestinationReadiness(
Ready(second, DestinationCell)));
Assert.True(transit.AcknowledgeMaterialized(
second,
DestinationCell));
Assert.True(transit.Complete(second));
quiescence.ObserveReleased();
Assert.True(availability.IsWorldAvailable);
Assert.Equal(1, audio.ResumeCalls);
@ -91,22 +118,37 @@ public sealed class WorldGenerationQuiescenceTests
public void Quiesce_PreservesNonWorldInventorySelection()
{
const uint inventoryGuid = 0x80000001u;
var availability = new WorldGenerationAvailabilityState();
var transit = new RuntimeWorldTransitState();
var availability = new WorldGenerationAvailabilityState(transit);
var world = new GpuWorldState(availability: availability);
var selection = new SelectionState();
selection.Select(inventoryGuid, SelectionChangeSource.Inventory);
var quiescence = new WorldGenerationQuiescence(
availability,
selection,
world,
_ => null,
audio: null);
quiescence.Begin(3);
WorldGenerationQuiescenceEdge edge = quiescence.CaptureBegin();
transit.BeginReveal(RuntimePortalKind.Portal, DestinationCell);
quiescence.CommitBegin(edge);
Assert.Equal(inventoryGuid, selection.SelectedObjectId);
}
private static RuntimeDestinationReadiness Ready(
long generation,
uint destinationCell) =>
new(
generation,
destinationCell,
IsIndoor: false,
IsUnhydratable: false,
RequiredRenderRadius: 1,
IsRenderNeighborhoodReady: true,
AreCompositeTexturesReady: true,
IsCollisionReady: true);
private static WorldEntity Entity() => new()
{
Id = 1,