using System.Numerics; using AcDream.App.Audio; using AcDream.App.Streaming; using AcDream.Core.Selection; using AcDream.Core.World; using DatReaderWriter.DBObjs; namespace AcDream.App.Tests.Streaming; public sealed class WorldGenerationQuiescenceTests { private const uint LandblockId = 0x1010FFFFu; private const uint ServerGuid = 0x70000001u; [Fact] public void ActiveGeneration_HidesWorldQueriesButRetainsCanonicalOwners() { var availability = new WorldGenerationAvailabilityState(); var world = new GpuWorldState(availability: availability); WorldEntity entity = Entity(); world.AddLandblock(new LoadedLandblock( LandblockId, new LandBlock(), Array.Empty())); world.PlaceLiveEntityProjection(LandblockId, entity); world.SetLandblockAabb(LandblockId, Vector3.Zero, Vector3.One); var nearby = new List>(); Assert.True(world.IsLiveEntityVisible(entity.Id)); Assert.Single(world.LandblockEntries); Assert.Single(world.LandblockBounds); availability.Begin(17); world.CopyLiveEntitiesNearLandblock(LandblockId, 0, nearby); Assert.False(availability.IsWorldAvailable); Assert.False(world.IsLiveEntityVisible(entity.Id)); Assert.Empty(world.LandblockEntries); Assert.Empty(world.LandblockBounds); Assert.Empty(nearby); Assert.Same(entity, Assert.Single(world.Entities)); Assert.True(world.IsLoaded(LandblockId)); Assert.False(availability.End(16)); Assert.False(availability.IsWorldAvailable); Assert.True(availability.End(17)); Assert.True(world.IsLiveEntityVisible(entity.Id)); Assert.Same(entity, Assert.Single(world.LandblockEntries).Entities.Single()); } [Fact] public void SupersededReveal_StaysContinuouslySilentAndOnlyLatestGenerationReopens() { var availability = new WorldGenerationAvailabilityState(); var world = new GpuWorldState(availability: availability); world.AddLandblock(new LoadedLandblock( LandblockId, new LandBlock(), Array.Empty())); WorldEntity entity = Entity(); world.PlaceLiveEntityProjection(LandblockId, entity); var selection = new SelectionState(); selection.Select(ServerGuid, SelectionChangeSource.World); var audio = new RecordingAudioQuiescence(); var quiescence = new WorldGenerationQuiescence( availability, selection, world, guid => guid == ServerGuid ? entity.Id : null, audio); quiescence.Begin(1); quiescence.Begin(2); quiescence.End(1); Assert.False(availability.IsWorldAvailable); Assert.Equal(2, availability.QuiescedGeneration); Assert.Null(selection.SelectedObjectId); Assert.Equal(1, audio.SuspendCalls); Assert.Equal(0, audio.ResumeCalls); quiescence.End(2); Assert.True(availability.IsWorldAvailable); Assert.Equal(1, audio.ResumeCalls); } [Fact] public void Quiesce_PreservesNonWorldInventorySelection() { const uint inventoryGuid = 0x80000001u; var availability = new WorldGenerationAvailabilityState(); 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); Assert.Equal(inventoryGuid, selection.SelectedObjectId); } private static WorldEntity Entity() => new() { Id = 1, ServerGuid = ServerGuid, SourceGfxObjOrSetupId = 0x02000001u, Position = Vector3.Zero, Rotation = Quaternion.Identity, MeshRefs = Array.Empty(), }; private sealed class RecordingAudioQuiescence : IWorldAudioQuiescence { public int SuspendCalls { get; private set; } public int ResumeCalls { get; private set; } public void SuspendWorldAudio() => SuspendCalls++; public void ResumeWorldAudio() => ResumeCalls++; } }