diff --git a/VoxelEngine.Tests/Persistence/InMemoryChunkModificationStoreTests.cs b/VoxelEngine.Tests/Persistence/InMemoryChunkModificationStoreTests.cs new file mode 100644 index 0000000..cab4b20 --- /dev/null +++ b/VoxelEngine.Tests/Persistence/InMemoryChunkModificationStoreTests.cs @@ -0,0 +1,65 @@ +using OpenTK.Mathematics; +using VoxelEngine.World.Persistence; +using Xunit; + +namespace VoxelEngine.Tests.Persistence; + +public class InMemoryChunkModificationStoreTests +{ + [Fact] + public void NewStore_HasNoDiffs() + { + var s = new InMemoryChunkModificationStore(); + Assert.False(s.HasDiff(new Vector3i(0, 0, 0))); + Assert.False(s.TryGetDiff(new Vector3i(0, 0, 0), out _)); + } + + [Fact] + public void RecordEdit_StoresAndIsRetrievable() + { + var s = new InMemoryChunkModificationStore(); + Vector3i pos = new(1, 2, 3); + s.RecordEdit(pos, 42, 7); + + Assert.True(s.HasDiff(pos)); + Assert.True(s.TryGetDiff(pos, out var diff)); + Assert.Single(diff!); + Assert.Equal((ushort)7, diff![42]); + } + + [Fact] + public void RecordEdit_OverwritesSameLocalIndex() + { + var s = new InMemoryChunkModificationStore(); + Vector3i pos = new(0, 0, 0); + s.RecordEdit(pos, 10, 1); + s.RecordEdit(pos, 10, 2); + + Assert.True(s.TryGetDiff(pos, out var diff)); + Assert.Single(diff!); + Assert.Equal((ushort)2, diff![10]); + } + + [Fact] + public void RecordEdit_MultipleChunks_AreIndependent() + { + var s = new InMemoryChunkModificationStore(); + s.RecordEdit(new Vector3i(0, 0, 0), 0, 1); + s.RecordEdit(new Vector3i(1, 0, 0), 0, 2); + + Assert.True(s.TryGetDiff(new Vector3i(0, 0, 0), out var a)); + Assert.True(s.TryGetDiff(new Vector3i(1, 0, 0), out var b)); + Assert.Equal((ushort)1, a![0]); + Assert.Equal((ushort)2, b![0]); + } + + [Fact] + public void ClearDiff_RemovesChunk() + { + var s = new InMemoryChunkModificationStore(); + Vector3i pos = new(5, 5, 5); + s.RecordEdit(pos, 0, 1); + s.ClearDiff(pos); + Assert.False(s.HasDiff(pos)); + } +} diff --git a/World/Persistence/IChunkModificationStore.cs b/World/Persistence/IChunkModificationStore.cs new file mode 100644 index 0000000..2f543a9 --- /dev/null +++ b/World/Persistence/IChunkModificationStore.cs @@ -0,0 +1,11 @@ +using OpenTK.Mathematics; + +namespace VoxelEngine.World.Persistence; + +public interface IChunkModificationStore +{ + bool TryGetDiff(Vector3i chunkPos, out IReadOnlyDictionary? diff); + void RecordEdit(Vector3i chunkPos, int localIndex, ushort blockId); + void ClearDiff(Vector3i chunkPos); + bool HasDiff(Vector3i chunkPos); +} diff --git a/World/Persistence/InMemoryChunkModificationStore.cs b/World/Persistence/InMemoryChunkModificationStore.cs new file mode 100644 index 0000000..5928276 --- /dev/null +++ b/World/Persistence/InMemoryChunkModificationStore.cs @@ -0,0 +1,33 @@ +using OpenTK.Mathematics; + +namespace VoxelEngine.World.Persistence; + +public sealed class InMemoryChunkModificationStore : IChunkModificationStore +{ + private readonly Dictionary> _diffs = new(); + + public bool TryGetDiff(Vector3i chunkPos, out IReadOnlyDictionary? diff) + { + if (this._diffs.TryGetValue(chunkPos, out Dictionary? d)) + { + diff = d; + return true; + } + diff = null; + return false; + } + + public void RecordEdit(Vector3i chunkPos, int localIndex, ushort blockId) + { + if (!this._diffs.TryGetValue(chunkPos, out Dictionary? d)) + { + d = new Dictionary(); + this._diffs[chunkPos] = d; + } + d[localIndex] = blockId; + } + + public void ClearDiff(Vector3i chunkPos) => this._diffs.Remove(chunkPos); + + public bool HasDiff(Vector3i chunkPos) => this._diffs.ContainsKey(chunkPos); +}