using AcDream.App.Streaming; using AcDream.Core.Physics; using AcDream.Core.World; using AcDream.Runtime.World; using DatReaderWriter.DBObjs; namespace AcDream.App.Tests.Streaming; /// /// #280 end-to-end: the derived reveal window, the tiered render predicate, /// the terrain-residency gate, the destination reservation, and the loosened /// Runtime shape invariant, wired together with the REAL /// , , and /// rather than fakes. /// /// /// This is the test that fails if any single piece is missing: raising the /// radius without the tier-aware predicate hangs forever (T2), and loosening /// neither the Runtime invariant nor the predicate makes /// AcknowledgeDestinationReadiness fail /// invalid-readiness-shape (T1). /// /// public sealed class WorldRevealDerivedWindowIntegrationTests { private const int CenterX = 0x40; private const int CenterY = 0x40; private const uint DestinationCell = (uint)CenterX << 24 | (uint)CenterY << 16 | 0x0021u; [Theory] [InlineData(1, 2)] [InlineData(2, 4)] public void OutdoorReveal_HoldsUntilTheWholeDerivedWindowIsPublished( int nearRadius, int farRadius) { var world = new GpuWorldState(); var physics = new PhysicsEngine(); var transit = new RuntimeWorldTransitState(); var streaming = new RecordingReservations(); StreamingController controller = CreateController( world, nearRadius, farRadius); WorldRevealCoordinator coordinator = CreateCoordinator( transit, controller, physics, streaming); long generation = coordinator.BeginLogin(DestinationCell); // The reservation opens on the same square the gate measures. Assert.Equal( (generation, DestinationCell, farRadius), Assert.Single(streaming.Begins)); // Publish the Near window only. Pre-#280 this was the entire gate; it // must no longer be enough. for (int radius = 0; radius <= nearRadius; radius++) PublishRing(world, physics, radius, LandblockStreamTier.Near); Assert.False(coordinator.Evaluate(DestinationCell).IsReady); // Fill the outer rings with Far-tier terrain publication, one ring at // a time, so the hold is proven to track the outer boundary and not // just the first missing member. for (int radius = nearRadius + 1; radius < farRadius; radius++) { PublishRing(world, physics, radius, LandblockStreamTier.Far); Assert.False(coordinator.Evaluate(DestinationCell).IsReady); } PublishRing(world, physics, farRadius, LandblockStreamTier.Far); WorldRevealReadinessSnapshot ready = coordinator.Evaluate(DestinationCell); Assert.True(ready.IsReady); Assert.Equal(farRadius, ready.RequiredRenderRadius); Assert.Equal(nearRadius, ready.RequiredNearRadius); // The loosened Runtime shape invariant accepted the derived radius. Assert.Equal(0, transit.Snapshot.InvariantFailureCount); Assert.True(transit.Snapshot.IsReady); } /// /// P7/ยง9-8: login and portal share the barrier, so first login gets the /// same widened gate. Asserted through the production readiness predicate /// the login auto-entry context calls. /// [Fact] public void LoginReveal_UsesTheSameWidenedGateAsPortalArrival() { const int nearRadius = 1; const int farRadius = 3; var world = new GpuWorldState(); var physics = new PhysicsEngine(); var transit = new RuntimeWorldTransitState(); StreamingController controller = CreateController( world, nearRadius, farRadius); WorldRevealCoordinator coordinator = CreateCoordinator( transit, controller, physics, streaming: null); coordinator.BeginLogin(DestinationCell); for (int radius = 0; radius < farRadius; radius++) { PublishRing( world, physics, radius, radius <= nearRadius ? LandblockStreamTier.Near : LandblockStreamTier.Far); } Assert.False(coordinator.Evaluate(DestinationCell).IsReady); PublishRing(world, physics, farRadius, LandblockStreamTier.Far); Assert.True(coordinator.Evaluate(DestinationCell).IsReady); Assert.Equal(0, transit.Snapshot.InvariantFailureCount); } /// /// Indoor destinations keep retail's EnvCell arm: no landscape ring at /// all, regardless of how wide the streaming window is. /// [Fact] public void IndoorReveal_IgnoresTheStreamingWindowEntirely() { const uint indoorCell = (uint)CenterX << 24 | (uint)CenterY << 16 | 0x0100u; var world = new GpuWorldState(); var physics = new PhysicsEngine(); var transit = new RuntimeWorldTransitState(); StreamingController controller = CreateController(world, 2, 6); WorldRevealCoordinator coordinator = CreateCoordinator( transit, controller, physics, streaming: null); coordinator.BeginLogin(indoorCell); Assert.False(coordinator.Evaluate(indoorCell).IsReady); // Only the destination's own landblock, at Near tier. PublishRing(world, physics, 0, LandblockStreamTier.Near); WorldRevealReadinessSnapshot snapshot = coordinator.Evaluate(indoorCell); Assert.True(snapshot.IsRenderNeighborhoodReady); Assert.Equal(0, snapshot.RequiredRenderRadius); Assert.Equal(0, transit.Snapshot.InvariantFailureCount); } private sealed class RecordingReservations : IWorldRevealStreamingScheduler { public List<(long Generation, uint Cell, int Radius)> Begins { get; } = []; public List Ends { get; } = []; public void BeginDestinationReservation( long revealGeneration, uint destinationCell, int requiredRenderRadius) => Begins.Add((revealGeneration, destinationCell, requiredRenderRadius)); public void EndDestinationReservation(long revealGeneration) => Ends.Add(revealGeneration); } private static WorldRevealCoordinator CreateCoordinator( RuntimeWorldTransitState transit, StreamingController controller, PhysicsEngine physics, IWorldRevealStreamingScheduler? streaming) => new( transit, () => new StreamingRevealWindow( controller.NearRadius, controller.FarRadius), controller.IsRenderNeighborhoodResident, physics.IsSpawnCellReady, physics.IsNeighborhoodTerrainResident, () => true, (_, _) => { }, () => { }, _ => false, streaming: streaming); private static StreamingController CreateController( GpuWorldState state, int nearRadius, int farRadius) => new( (_, _, _) => { }, (_, _) => { }, _ => Array.Empty(), (_, _) => { }, state, nearRadius: nearRadius, farRadius: farRadius); private static void PublishRing( GpuWorldState world, PhysicsEngine physics, int radius, LandblockStreamTier tier) { for (int dx = -radius; dx <= radius; dx++) for (int dy = -radius; dy <= radius; dy++) { if (Math.Abs(dx) != radius && Math.Abs(dy) != radius) continue; uint id = ((uint)(CenterX + dx) << 24) | ((uint)(CenterY + dy) << 16) | 0xFFFFu; world.AddLandblock( new LoadedLandblock(id, new LandBlock(), Array.Empty()), tier: tier); // The Far tier publishes terrain COLLISION as well as terrain // render (LandblockPhysicsPublisher, reached for // PublicationKind.Far), which is what makes the outer arm of the // reveal gate satisfiable on the collision side too. physics.AddLandblock( id, new TerrainSurface(new byte[81], new float[256]), Array.Empty(), Array.Empty(), worldOffsetX: 0f, worldOffsetY: 0f); } } }