using OpenTK.Mathematics; using VoxelEngine.Core; using VoxelEngine.World; using VoxelEngine.World.Blocks; using VoxelEngine.World.Chunk; using VoxelEngine.World.Persistence; using VoxelEngine.World.Streaming; using VoxelEngine.World.WorldGen; using Xunit; namespace VoxelEngine.Tests.Streaming; public class StreamingIntegrationTests { private static (ChunkStreamer streamer, World.World world, InMemoryChunkModificationStore store) BuildRealStack(Configuration cfg) { var registry = new BlockRegistry(); var store = new InMemoryChunkModificationStore(); var world = new World.World(registry, store); var gen = new WorldGenerator(seed: 1337, blocks: registry, cfg: new Configuration()); var mesher = new ChunkMesher(atlasWidthPixels: 128, atlasHeightPixels: 32); var executor = new SynchronousJobExecutor(); Func factory = m => new FakeRenderer(m); var streamer = new ChunkStreamer(cfg, world, gen.Generate, mesher, registry, executor, factory); return (streamer, world, store); } [Fact] public void PlayerWalksAcrossWorld_LoadBoxFollowsPlayer() { var cfg = new Configuration { RenderDistanceHorizontal = 1, RenderDistanceVertical = 0, MaxGenerateJobsInFlight = 100, MaxMeshUploadsPerFrame = 100, }; var (streamer, world, _) = BuildRealStack(cfg); for (int step = 0; step < 5; step++) { streamer.Tick(new Vector3i(step, 0, 0)); for (int dx = -1; dx <= 1; dx++) for (int dz = -1; dz <= 1; dz++) { var pos = new Vector3i(step + dx, 0, dz); Assert.NotNull(world.GetChunk(pos)); Assert.True(streamer.IsReady(pos)); } } } [Fact] public void ModificationStore_SurvivesUnloadReload() { var cfg = new Configuration { RenderDistanceHorizontal = 0, RenderDistanceVertical = 0, MaxGenerateJobsInFlight = 100, MaxMeshUploadsPerFrame = 100, }; var (streamer, world, store) = BuildRealStack(cfg); // Use a block id that differs from what WorldGenerator places at // world (0,0,0). At y=0 every biome's "Under" block is Stone (id 1), // so SetBlock(..., Stone) would hit World.SetBlock's no-op guard and // skip the diff record. Dirt (id 2) is always a real change. var pos = new Vector3i(0, 0, 0); streamer.Tick(pos); Assert.True(streamer.IsReady(pos)); Assert.True(world.SetBlock(0, 0, 0, /*Dirt*/ (ushort)2)); streamer.Tick(new Vector3i(50, 0, 0)); Assert.Null(world.GetChunk(pos)); Assert.True(store.HasDiff(pos)); streamer.Tick(pos); Assert.NotNull(world.GetChunk(pos)); Assert.Equal((ushort)2, world.GetBlock(0, 0, 0).Id); } }