using System; using System.Collections.Generic; using AcDream.App.Streaming; using AcDream.Core.Terrain; using AcDream.Core.World; using DatReaderWriter.DBObjs; using Xunit; namespace AcDream.Core.Tests.Streaming; public class StreamingControllerPriorityApplyTests { private static LandblockStreamResult.Loaded LoadedOf(uint canonicalId) => new(canonicalId, LandblockStreamTier.Near, new LoadedLandblock(canonicalId, new LandBlock(), Array.Empty()), new LandblockMeshData(Array.Empty(), Array.Empty())); [Fact] public void PriorityLandblock_isApplied_evenWhenBeyondPerFrameCap() { uint priority = StreamingRegion.EncodeLandblockIdForTest(169, 180); var outbox = new Queue(new LandblockStreamResult[] { LoadedOf(StreamingRegion.EncodeLandblockIdForTest(0, 0)), LoadedOf(StreamingRegion.EncodeLandblockIdForTest(0, 1)), LoadedOf(StreamingRegion.EncodeLandblockIdForTest(0, 2)), LoadedOf(StreamingRegion.EncodeLandblockIdForTest(0, 3)), LoadedOf(priority), }); var applied = new List(); var state = new GpuWorldState(); var ctrl = new StreamingController( enqueueLoad: (_, _) => { }, enqueueUnload: _ => { }, drainCompletions: max => { var batch = new List(); while (batch.Count < max && outbox.Count > 0) batch.Add(outbox.Dequeue()); return batch; }, applyTerrain: (lb, _) => applied.Add(lb.LandblockId), state: state, nearRadius: 4, farRadius: 12) { MaxCompletionsPerFrame = 4 }; ctrl.PriorityLandblockId = priority; ctrl.Tick(169, 180); Assert.Contains(priority, applied); // priority applied THIS tick Assert.True(applied.Count <= 5); // did not blindly flush the whole outbox } [Fact] public void Deferred_nonPriority_completions_applyOnLaterFrames_withoutLoss() { // After the priority is found, the non-priority items drained past it must // still be applied (over subsequent ticks), never dropped. uint priority = StreamingRegion.EncodeLandblockIdForTest(169, 180); var outbox = new Queue(new LandblockStreamResult[] { LoadedOf(StreamingRegion.EncodeLandblockIdForTest(0, 0)), LoadedOf(StreamingRegion.EncodeLandblockIdForTest(0, 1)), LoadedOf(StreamingRegion.EncodeLandblockIdForTest(0, 2)), LoadedOf(StreamingRegion.EncodeLandblockIdForTest(0, 3)), LoadedOf(StreamingRegion.EncodeLandblockIdForTest(0, 4)), LoadedOf(priority), }); var applied = new List(); var state = new GpuWorldState(); var ctrl = new StreamingController( (_, _) => { }, _ => { }, max => { var b = new List(); while (b.Count < max && outbox.Count > 0) b.Add(outbox.Dequeue()); return b; }, (lb, _) => applied.Add(lb.LandblockId), state, 4, 12) { MaxCompletionsPerFrame = 4 }; ctrl.PriorityLandblockId = priority; ctrl.Tick(169, 180); // priority + some others ctrl.PriorityLandblockId = 0u; ctrl.Tick(169, 180); // drains the deferred remainder ctrl.Tick(169, 180); Assert.Contains(priority, applied); Assert.Equal(6, applied.Count); // all six applied, none lost } }