test: end-to-end streaming + modification-store roundtrip
This commit is contained in:
84
VoxelEngine.Tests/Streaming/StreamingIntegrationTests.cs
Normal file
84
VoxelEngine.Tests/Streaming/StreamingIntegrationTests.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
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);
|
||||||
|
var mesher = new ChunkMesher(atlasWidthPixels: 128, atlasHeightPixels: 32);
|
||||||
|
var executor = new SynchronousJobExecutor();
|
||||||
|
Func<float[], IChunkRenderer> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user