diff --git a/World/Persistence/InMemoryChunkModificationStore.cs b/World/Persistence/InMemoryChunkModificationStore.cs index 5928276..e22809c 100644 --- a/World/Persistence/InMemoryChunkModificationStore.cs +++ b/World/Persistence/InMemoryChunkModificationStore.cs @@ -10,7 +10,9 @@ public sealed class InMemoryChunkModificationStore : IChunkModificationStore { if (this._diffs.TryGetValue(chunkPos, out Dictionary? d)) { - diff = d; + // Returned snapshot: callers may safely retain and enumerate even + // after concurrent RecordEdit on the same chunk. + diff = new Dictionary(d); return true; } diff = null; diff --git a/World/Streaming/ChunkJob.cs b/World/Streaming/ChunkJob.cs index e643aa1..58bf9fc 100644 --- a/World/Streaming/ChunkJob.cs +++ b/World/Streaming/ChunkJob.cs @@ -3,12 +3,12 @@ using VoxelEngine.World.Chunk; namespace VoxelEngine.World.Streaming; -// Job dispatched to an IJobExecutor. For a fresh load, ExistingChunk is null -// and the worker generates + applies diff + meshes. For a remesh-only job, -// ExistingChunk holds the chunk and the worker only meshes (no generation). +// Job dispatched to an IJobExecutor for a fresh chunk load. The worker +// generates the chunk, applies any modification diff, and meshes the result. +// Remesh-only jobs are dispatched separately by DispatchRemesh and capture +// their state via closures, so they do not use this record. internal sealed record ChunkJob( Vector3i Pos, - Chunk.Chunk? ExistingChunk, NeighborSnapshots Neighbors, IReadOnlyDictionary? Diff); diff --git a/World/Streaming/ChunkStreamer.cs b/World/Streaming/ChunkStreamer.cs index c9a61ca..8a4b3ea 100644 --- a/World/Streaming/ChunkStreamer.cs +++ b/World/Streaming/ChunkStreamer.cs @@ -299,15 +299,9 @@ public sealed class ChunkStreamer : IDisposable private void DispatchLoad(Vector3i pos) { NeighborSnapshots neighbors = this.SnapshotNeighbors(pos); - IReadOnlyDictionary? diff = null; - if (this._world.ModificationStore.TryGetDiff(pos, out var d) && d != null) - { - // Copy: TryGetDiff returns the live internal dictionary, which the - // main thread may mutate via World.SetBlock while the worker iterates. - diff = new Dictionary(d); - } + this._world.ModificationStore.TryGetDiff(pos, out IReadOnlyDictionary? diff); - var job = new ChunkJob(pos, ExistingChunk: null, neighbors, diff); + var job = new ChunkJob(pos, neighbors, diff); this._inFlight++; IChunkMesher mesher = this._mesher;