From aeca6a8f533ebebbc78ac63957be5a4420f87562 Mon Sep 17 00:00:00 2001 From: ORelio Date: Sat, 23 Apr 2022 12:00:50 +0200 Subject: [PATCH] Add thead safety to terrain data (#1999) Allow safely reading terrain data from other threads --- MinecraftClient/Mapping/Chunk.cs | 28 +++++++++- MinecraftClient/Mapping/ChunkColumn.cs | 26 +++++++++- MinecraftClient/Mapping/World.cs | 72 ++++++++++++++++++-------- 3 files changed, 101 insertions(+), 25 deletions(-) diff --git a/MinecraftClient/Mapping/Chunk.cs b/MinecraftClient/Mapping/Chunk.cs index 5b84ecf2..3ae88b73 100644 --- a/MinecraftClient/Mapping/Chunk.cs +++ b/MinecraftClient/Mapping/Chunk.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; namespace MinecraftClient.Mapping { @@ -19,6 +20,11 @@ namespace MinecraftClient.Mapping /// private readonly Block[,,] blocks = new Block[SizeX, SizeY, SizeZ]; + /// + /// Lock for thread safety + /// + private readonly ReaderWriterLockSlim blockLock = new ReaderWriterLockSlim(); + /// /// Read, or set the specified block /// @@ -36,7 +42,16 @@ namespace MinecraftClient.Mapping throw new ArgumentOutOfRangeException("blockY", "Must be between 0 and " + (SizeY - 1) + " (inclusive)"); if (blockZ < 0 || blockZ >= SizeZ) throw new ArgumentOutOfRangeException("blockZ", "Must be between 0 and " + (SizeZ - 1) + " (inclusive)"); - return blocks[blockX, blockY, blockZ]; + + blockLock.EnterReadLock(); + try + { + return blocks[blockX, blockY, blockZ]; + } + finally + { + blockLock.ExitReadLock(); + } } set { @@ -46,7 +61,16 @@ namespace MinecraftClient.Mapping throw new ArgumentOutOfRangeException("blockY", "Must be between 0 and " + (SizeY - 1) + " (inclusive)"); if (blockZ < 0 || blockZ >= SizeZ) throw new ArgumentOutOfRangeException("blockZ", "Must be between 0 and " + (SizeZ - 1) + " (inclusive)"); - blocks[blockX, blockY, blockZ] = value; + + blockLock.EnterWriteLock(); + try + { + blocks[blockX, blockY, blockZ] = value; + } + finally + { + blockLock.ExitWriteLock(); + } } } diff --git a/MinecraftClient/Mapping/ChunkColumn.cs b/MinecraftClient/Mapping/ChunkColumn.cs index 007bdb22..22c9d2ae 100644 --- a/MinecraftClient/Mapping/ChunkColumn.cs +++ b/MinecraftClient/Mapping/ChunkColumn.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; namespace MinecraftClient.Mapping { @@ -17,6 +18,11 @@ namespace MinecraftClient.Mapping /// private readonly Chunk[] chunks = new Chunk[ColumnSize]; + /// + /// Lock for thread safety + /// + private readonly ReaderWriterLockSlim chunkLock = new ReaderWriterLockSlim(); + /// /// Get or set the specified chunk column /// @@ -27,11 +33,27 @@ namespace MinecraftClient.Mapping { get { - return chunks[chunkY]; + chunkLock.EnterReadLock(); + try + { + return chunks[chunkY]; + } + finally + { + chunkLock.ExitReadLock(); + } } set { - chunks[chunkY] = value; + chunkLock.EnterWriteLock(); + try + { + chunks[chunkY] = value; + } + finally + { + chunkLock.ExitWriteLock(); + } } } diff --git a/MinecraftClient/Mapping/World.cs b/MinecraftClient/Mapping/World.cs index 55ceaa75..9a57baae 100644 --- a/MinecraftClient/Mapping/World.cs +++ b/MinecraftClient/Mapping/World.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; namespace MinecraftClient.Mapping { @@ -15,6 +16,11 @@ namespace MinecraftClient.Mapping /// private Dictionary> chunks = new Dictionary>(); + /// + /// Lock for thread safety + /// + private readonly ReaderWriterLockSlim chunksLock = new ReaderWriterLockSlim(); + /// /// Read, set or unload the specified chunk column /// @@ -25,34 +31,50 @@ namespace MinecraftClient.Mapping { get { - //Read a chunk - if (chunks.ContainsKey(chunkX)) - if (chunks[chunkX].ContainsKey(chunkZ)) - return chunks[chunkX][chunkZ]; - return null; + chunksLock.EnterReadLock(); + try + { + //Read a chunk + if (chunks.ContainsKey(chunkX)) + if (chunks[chunkX].ContainsKey(chunkZ)) + return chunks[chunkX][chunkZ]; + return null; + } + finally + { + chunksLock.ExitReadLock(); + } } set { - if (value != null) + chunksLock.EnterWriteLock(); + try { - //Update a chunk column - if (!chunks.ContainsKey(chunkX)) - chunks[chunkX] = new Dictionary(); - chunks[chunkX][chunkZ] = value; - } - else - { - //Unload a chunk column - if (chunks.ContainsKey(chunkX)) + if (value != null) { - if (chunks[chunkX].ContainsKey(chunkZ)) + //Update a chunk column + if (!chunks.ContainsKey(chunkX)) + chunks[chunkX] = new Dictionary(); + chunks[chunkX][chunkZ] = value; + } + else + { + //Unload a chunk column + if (chunks.ContainsKey(chunkX)) { - chunks[chunkX].Remove(chunkZ); - if (chunks[chunkX].Count == 0) - chunks.Remove(chunkX); + if (chunks[chunkX].ContainsKey(chunkZ)) + { + chunks[chunkX].Remove(chunkZ); + if (chunks[chunkX].Count == 0) + chunks.Remove(chunkX); + } } } } + finally + { + chunksLock.ExitWriteLock(); + } } } @@ -117,7 +139,7 @@ namespace MinecraftClient.Mapping { Location doneloc = new Location(x, y, z); Block doneblock = GetBlock(doneloc); - Material blockType = GetBlock(doneloc).Type; + Material blockType = doneblock.Type; if (blockType == block) { list.Add(doneloc); @@ -150,7 +172,15 @@ namespace MinecraftClient.Mapping /// public void Clear() { - chunks = new Dictionary>(); + chunksLock.EnterWriteLock(); + try + { + chunks = new Dictionary>(); + } + finally + { + chunksLock.ExitWriteLock(); + } } ///