Add thead safety to terrain data (#1999)

Allow safely reading terrain data from other threads
This commit is contained in:
ORelio 2022-04-23 12:00:50 +02:00
parent d6220ff779
commit aeca6a8f53
3 changed files with 101 additions and 25 deletions

View file

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
namespace MinecraftClient.Mapping namespace MinecraftClient.Mapping
{ {
@ -19,6 +20,11 @@ namespace MinecraftClient.Mapping
/// </summary> /// </summary>
private readonly Block[,,] blocks = new Block[SizeX, SizeY, SizeZ]; private readonly Block[,,] blocks = new Block[SizeX, SizeY, SizeZ];
/// <summary>
/// Lock for thread safety
/// </summary>
private readonly ReaderWriterLockSlim blockLock = new ReaderWriterLockSlim();
/// <summary> /// <summary>
/// Read, or set the specified block /// Read, or set the specified block
/// </summary> /// </summary>
@ -36,7 +42,16 @@ namespace MinecraftClient.Mapping
throw new ArgumentOutOfRangeException("blockY", "Must be between 0 and " + (SizeY - 1) + " (inclusive)"); throw new ArgumentOutOfRangeException("blockY", "Must be between 0 and " + (SizeY - 1) + " (inclusive)");
if (blockZ < 0 || blockZ >= SizeZ) if (blockZ < 0 || blockZ >= SizeZ)
throw new ArgumentOutOfRangeException("blockZ", "Must be between 0 and " + (SizeZ - 1) + " (inclusive)"); 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 set
{ {
@ -46,7 +61,16 @@ namespace MinecraftClient.Mapping
throw new ArgumentOutOfRangeException("blockY", "Must be between 0 and " + (SizeY - 1) + " (inclusive)"); throw new ArgumentOutOfRangeException("blockY", "Must be between 0 and " + (SizeY - 1) + " (inclusive)");
if (blockZ < 0 || blockZ >= SizeZ) if (blockZ < 0 || blockZ >= SizeZ)
throw new ArgumentOutOfRangeException("blockZ", "Must be between 0 and " + (SizeZ - 1) + " (inclusive)"); 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();
}
} }
} }

View file

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
namespace MinecraftClient.Mapping namespace MinecraftClient.Mapping
{ {
@ -17,6 +18,11 @@ namespace MinecraftClient.Mapping
/// </summary> /// </summary>
private readonly Chunk[] chunks = new Chunk[ColumnSize]; private readonly Chunk[] chunks = new Chunk[ColumnSize];
/// <summary>
/// Lock for thread safety
/// </summary>
private readonly ReaderWriterLockSlim chunkLock = new ReaderWriterLockSlim();
/// <summary> /// <summary>
/// Get or set the specified chunk column /// Get or set the specified chunk column
/// </summary> /// </summary>
@ -27,11 +33,27 @@ namespace MinecraftClient.Mapping
{ {
get get
{ {
return chunks[chunkY]; chunkLock.EnterReadLock();
try
{
return chunks[chunkY];
}
finally
{
chunkLock.ExitReadLock();
}
} }
set set
{ {
chunks[chunkY] = value; chunkLock.EnterWriteLock();
try
{
chunks[chunkY] = value;
}
finally
{
chunkLock.ExitWriteLock();
}
} }
} }

View file

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
namespace MinecraftClient.Mapping namespace MinecraftClient.Mapping
{ {
@ -15,6 +16,11 @@ namespace MinecraftClient.Mapping
/// </summary> /// </summary>
private Dictionary<int, Dictionary<int, ChunkColumn>> chunks = new Dictionary<int, Dictionary<int, ChunkColumn>>(); private Dictionary<int, Dictionary<int, ChunkColumn>> chunks = new Dictionary<int, Dictionary<int, ChunkColumn>>();
/// <summary>
/// Lock for thread safety
/// </summary>
private readonly ReaderWriterLockSlim chunksLock = new ReaderWriterLockSlim();
/// <summary> /// <summary>
/// Read, set or unload the specified chunk column /// Read, set or unload the specified chunk column
/// </summary> /// </summary>
@ -25,34 +31,50 @@ namespace MinecraftClient.Mapping
{ {
get get
{ {
//Read a chunk chunksLock.EnterReadLock();
if (chunks.ContainsKey(chunkX)) try
if (chunks[chunkX].ContainsKey(chunkZ)) {
return chunks[chunkX][chunkZ]; //Read a chunk
return null; if (chunks.ContainsKey(chunkX))
if (chunks[chunkX].ContainsKey(chunkZ))
return chunks[chunkX][chunkZ];
return null;
}
finally
{
chunksLock.ExitReadLock();
}
} }
set set
{ {
if (value != null) chunksLock.EnterWriteLock();
try
{ {
//Update a chunk column if (value != null)
if (!chunks.ContainsKey(chunkX))
chunks[chunkX] = new Dictionary<int, ChunkColumn>();
chunks[chunkX][chunkZ] = value;
}
else
{
//Unload a chunk column
if (chunks.ContainsKey(chunkX))
{ {
if (chunks[chunkX].ContainsKey(chunkZ)) //Update a chunk column
if (!chunks.ContainsKey(chunkX))
chunks[chunkX] = new Dictionary<int, ChunkColumn>();
chunks[chunkX][chunkZ] = value;
}
else
{
//Unload a chunk column
if (chunks.ContainsKey(chunkX))
{ {
chunks[chunkX].Remove(chunkZ); if (chunks[chunkX].ContainsKey(chunkZ))
if (chunks[chunkX].Count == 0) {
chunks.Remove(chunkX); 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); Location doneloc = new Location(x, y, z);
Block doneblock = GetBlock(doneloc); Block doneblock = GetBlock(doneloc);
Material blockType = GetBlock(doneloc).Type; Material blockType = doneblock.Type;
if (blockType == block) if (blockType == block)
{ {
list.Add(doneloc); list.Add(doneloc);
@ -150,7 +172,15 @@ namespace MinecraftClient.Mapping
/// </summary> /// </summary>
public void Clear() public void Clear()
{ {
chunks = new Dictionary<int, Dictionary<int, ChunkColumn>>(); chunksLock.EnterWriteLock();
try
{
chunks = new Dictionary<int, Dictionary<int, ChunkColumn>>();
}
finally
{
chunksLock.ExitWriteLock();
}
} }
/// <summary> /// <summary>