diff --git a/src/AdvChkSys/AdvChkSys.cs b/src/AdvChkSys/AdvChkSys.cs
index c371024..9774819 100644
--- a/src/AdvChkSys/AdvChkSys.cs
+++ b/src/AdvChkSys/AdvChkSys.cs
@@ -27,19 +27,19 @@ namespace AdvChkSys
///
/// Creates a new WorldConstraints object.
///
- public static WorldConstraints CreateDefaultConstraints() => new WorldConstraints();
+ public static WorldConstraints CreateDefaultConstraints() => new();
///
/// Creates a new 2D chunk manager with optional constraints.
///
public static ChunkManager2D Create2DManager(WorldConstraints? constraints = null) =>
- new ChunkManager2D(constraints);
+ new(constraints);
///
/// Creates a new 3D chunk manager with optional constraints.
///
public static ChunkManager3D Create3DManager(WorldConstraints? constraints = null) =>
- new ChunkManager3D(constraints);
+ new(constraints);
///
/// Gets a detailed report of the current memory usage by the chunk system.
@@ -51,7 +51,7 @@ namespace AdvChkSys
/// Logs the current memory usage to the provided logging action.
///
/// Action that will receive the log messages
- public static void LogMemoryUsage(Action logAction) =>
+ public static void LogMemoryUsage(Action logAction) =>
MemoryUsageReporter.LogMemoryUsage(logAction);
///
@@ -62,7 +62,7 @@ namespace AdvChkSys
///
/// Runs a batch of chunk operations in parallel.
///
- public static Task RunParallelChunkOperationsAsync(IEnumerable operations,
+ public static Task RunParallelChunkOperationsAsync(IEnumerable operations,
int? maxDegreeOfParallelism = null,
CancellationToken cancellationToken = default)
{
@@ -73,13 +73,13 @@ namespace AdvChkSys
/// Creates a spatial index for 2D chunks.
///
public static SpatialChunkIndex> CreateSpatialIndex2D() =>
- new SpatialChunkIndex>();
+ new();
///
/// Creates a spatial index for 3D chunks.
///
public static SpatialChunkIndex> CreateSpatialIndex3D() =>
- new SpatialChunkIndex>();
+ new();
///
/// Configures the threading system for high throughput.
@@ -155,7 +155,7 @@ namespace AdvChkSys
manager2D.UnloadChunk(1, 1);
manager3D.UnloadChunk(1, 1, 1);
- return resourceTracking2D && resourceTracking3D && constraintsWork &&
+ return resourceTracking2D && resourceTracking3D && constraintsWork &&
spatialIndexWorks && threadingWorks;
}
}
diff --git a/src/AdvChkSys/Chunk/Chunk2D.cs b/src/AdvChkSys/Chunk/Chunk2D.cs
index 31501e7..b04f260 100644
--- a/src/AdvChkSys/Chunk/Chunk2D.cs
+++ b/src/AdvChkSys/Chunk/Chunk2D.cs
@@ -91,13 +91,13 @@ namespace AdvChkSys.Chunk
get
{
if (localX < 0 || localX >= Width || localY < 0 || localY >= Height)
- throw new ArgumentOutOfRangeException($"Coordinates ({localX}, {localY}) out of bounds [0-{Width-1}, 0-{Height-1}]");
+ throw new ArgumentOutOfRangeException($"Coordinates ({localX}, {localY}) out of bounds [0-{Width - 1}, 0-{Height - 1}]");
return _isAllAir ? default! : _data![localX, localY];
}
set
{
if (localX < 0 || localX >= Width || localY < 0 || localY >= Height)
- throw new ArgumentOutOfRangeException($"Coordinates ({localX}, {localY}) out of bounds [0-{Width-1}, 0-{Height-1}]");
+ throw new ArgumentOutOfRangeException($"Coordinates ({localX}, {localY}) out of bounds [0-{Width - 1}, 0-{Height - 1}]");
if (_isAllAir)
throw new InvalidOperationException("Cannot set cell in all-air chunk.");
_data![localX, localY] = value;
diff --git a/src/AdvChkSys/Chunk/Chunk3D.cs b/src/AdvChkSys/Chunk/Chunk3D.cs
index eba7aff..4fe2300 100644
--- a/src/AdvChkSys/Chunk/Chunk3D.cs
+++ b/src/AdvChkSys/Chunk/Chunk3D.cs
@@ -6,210 +6,210 @@ using AdvChkSys.Interfaces;
namespace AdvChkSys.Chunk
{
+ ///
+ /// Represents a 3D chunk of data in the world.
+ /// Perspective-agnostic and supports arbitrary data types and metadata.
+ ///
+ public class Chunk3D : IChunk, IDisposable
+ {
+ // Array pool for chunk data arrays
+ private static readonly ConcurrentBag _arrayPool = new();
+
+ // Flyweight: one all-air instance per size
+ private static readonly Dictionary<(int, int, int), Chunk3D> _allAirChunks = new();
+
///
- /// Represents a 3D chunk of data in the world.
- /// Perspective-agnostic and supports arbitrary data types and metadata.
+ /// Returns a singleton all-air chunk for the given size and position.
///
- public class Chunk3D : IChunk, IDisposable
+ public static Chunk3D AllAir(int x, int y, int z, int width, int height, int depth)
{
- // Array pool for chunk data arrays
- private static readonly ConcurrentBag _arrayPool = new();
-
- // Flyweight: one all-air instance per size
- private static readonly Dictionary<(int, int, int), Chunk3D> _allAirChunks = new();
-
- ///
- /// Returns a singleton all-air chunk for the given size and position.
- ///
- public static Chunk3D AllAir(int x, int y, int z, int width, int height, int depth)
+ var key = (width, height, depth);
+ if (!_allAirChunks.TryGetValue(key, out var chunk))
{
- var key = (width, height, depth);
- if (!_allAirChunks.TryGetValue(key, out var chunk))
- {
- chunk = new Chunk3D(x, y, z, width, height, depth, isAllAir: true);
- _allAirChunks[key] = chunk;
- }
- chunk.SetPosition(x, y, z); // Use explicit method for position update
- return chunk;
+ chunk = new Chunk3D(x, y, z, width, height, depth, isAllAir: true);
+ _allAirChunks[key] = chunk;
}
+ chunk.SetPosition(x, y, z); // Use explicit method for position update
+ return chunk;
+ }
- ///
- /// The chunk's X position in chunk coordinates.
- ///
- public int X { get; private set; }
-
- ///
- /// The chunk's Y position in chunk coordinates.
- ///
- public int Y { get; private set; }
-
- ///
- /// The chunk's Z position in chunk coordinates.
- ///
- public int Z { get; private set; }
+ ///
+ /// The chunk's X position in chunk coordinates.
+ ///
+ public int X { get; private set; }
- ///
- /// The width of the chunk in cells.
- ///
- public int Width { get; }
-
- ///
- /// The height of the chunk in cells.
- ///
- public int Height { get; }
-
- ///
- /// The depth of the chunk in cells.
- ///
- public int Depth { get; }
+ ///
+ /// The chunk's Y position in chunk coordinates.
+ ///
+ public int Y { get; private set; }
- ///
- /// The chunk's data array.
- ///
- private readonly T[,,]? _data;
+ ///
+ /// The chunk's Z position in chunk coordinates.
+ ///
+ public int Z { get; private set; }
- ///
- /// Metadata dictionary for arbitrary chunk information (e.g., biome, tags).
- ///
- public Dictionary Metadata { get; }
+ ///
+ /// The width of the chunk in cells.
+ ///
+ public int Width { get; }
- ///
- /// Returns true if this chunk is the all-air singleton.
- ///
- public bool IsAllAir { get; private set; } = false;
-
- ///
- /// Tracks whether the chunk has been disposed.
- ///
- private bool _disposed;
+ ///
+ /// The height of the chunk in cells.
+ ///
+ public int Height { get; }
- ///
- /// Creates a new 3D chunk at the specified position with the given dimensions.
- ///
- /// X coordinate in chunk space
- /// Y coordinate in chunk space
- /// Z coordinate in chunk space
- /// Width of the chunk in cells
- /// Height of the chunk in cells
- /// Depth of the chunk in cells
- public Chunk3D(int x, int y, int z, int width, int height, int depth)
- : this(x, y, z, width, height, depth, false)
- {
- }
-
- ///
- /// Creates a new 3D chunk at the specified position with the given dimensions.
- ///
- /// X coordinate in chunk space
- /// Y coordinate in chunk space
- /// Z coordinate in chunk space
- /// Width of the chunk in cells
- /// Height of the chunk in cells
- /// Depth of the chunk in cells
- /// Whether this is an all-air chunk
- public Chunk3D(int x, int y, int z, int width, int height, int depth, bool isAllAir = false)
- {
- X = x;
- Y = y;
- Z = z;
- Width = width;
- Height = height;
- Depth = depth;
- IsAllAir = isAllAir;
- _data = isAllAir ? null : RentArray(width, height, depth);
- Metadata = new Dictionary();
- }
-
- ///
- /// Explicitly sets the chunk's position. Used internally for all-air singleton.
- ///
- internal void SetPosition(int x, int y, int z)
- {
- X = x;
- Y = y;
- Z = z;
- }
+ ///
+ /// The depth of the chunk in cells.
+ ///
+ public int Depth { get; }
- ///
- /// Gets or sets the value at the given local chunk coordinates.
- ///
- public T this[int localX, int localY, int localZ]
- {
- get
- {
- if (IsAllAir)
- return default!;
- return _data![localX, localY, localZ];
- }
- set
- {
- if (IsAllAir)
- throw new InvalidOperationException("Cannot modify an all-air chunk.");
- _data![localX, localY, localZ] = value;
- }
- }
+ ///
+ /// The chunk's data array.
+ ///
+ private readonly T[,,]? _data;
- ///
- /// Fills the chunk with a specified value.
- ///
- public void Fill(T value)
+ ///
+ /// Metadata dictionary for arbitrary chunk information (e.g., biome, tags).
+ ///
+ public Dictionary Metadata { get; }
+
+ ///
+ /// Returns true if this chunk is the all-air singleton.
+ ///
+ public bool IsAllAir { get; private set; } = false;
+
+ ///
+ /// Tracks whether the chunk has been disposed.
+ ///
+ private bool _disposed;
+
+ ///
+ /// Creates a new 3D chunk at the specified position with the given dimensions.
+ ///
+ /// X coordinate in chunk space
+ /// Y coordinate in chunk space
+ /// Z coordinate in chunk space
+ /// Width of the chunk in cells
+ /// Height of the chunk in cells
+ /// Depth of the chunk in cells
+ public Chunk3D(int x, int y, int z, int width, int height, int depth)
+ : this(x, y, z, width, height, depth, false)
+ {
+ }
+
+ ///
+ /// Creates a new 3D chunk at the specified position with the given dimensions.
+ ///
+ /// X coordinate in chunk space
+ /// Y coordinate in chunk space
+ /// Z coordinate in chunk space
+ /// Width of the chunk in cells
+ /// Height of the chunk in cells
+ /// Depth of the chunk in cells
+ /// Whether this is an all-air chunk
+ public Chunk3D(int x, int y, int z, int width, int height, int depth, bool isAllAir = false)
+ {
+ X = x;
+ Y = y;
+ Z = z;
+ Width = width;
+ Height = height;
+ Depth = depth;
+ IsAllAir = isAllAir;
+ _data = isAllAir ? null : RentArray(width, height, depth);
+ Metadata = new Dictionary();
+ }
+
+ ///
+ /// Explicitly sets the chunk's position. Used internally for all-air singleton.
+ ///
+ internal void SetPosition(int x, int y, int z)
+ {
+ X = x;
+ Y = y;
+ Z = z;
+ }
+
+ ///
+ /// Gets or sets the value at the given local chunk coordinates.
+ ///
+ public T this[int localX, int localY, int localZ]
+ {
+ get
{
if (IsAllAir)
- throw new InvalidOperationException("Cannot fill an all-air chunk.");
-
- for (int x = 0; x < Width; x++)
- for (int y = 0; y < Height; y++)
- for (int z = 0; z < Depth; z++)
- _data![x, y, z] = value;
+ return default!;
+ return _data![localX, localY, localZ];
}
-
- ///
- /// Rents an array from the pool or creates a new one.
- ///
- private static T[,,] RentArray(int width, int height, int depth)
+ set
{
- if (_arrayPool.TryTake(out var arr) &&
- arr.GetLength(0) == width &&
- arr.GetLength(1) == height &&
- arr.GetLength(2) == depth)
- return arr;
- return new T[width, height, depth];
- }
-
- ///
- /// Returns an array to the pool.
- ///
- internal static void ReturnArray(T[,,] arr)
- {
- _arrayPool.Add(arr);
- }
-
- ///
- /// Returns the underlying data array (for pooling).
- ///
- internal T[,,]? GetDataArray() => _data;
-
- ///
- /// Releases the data array back to the pool.
- ///
- internal void ReleaseDataArray()
- {
- if (_data != null && !IsAllAir)
- {
- ReturnArray(_data);
- }
- }
-
- ///
- /// Disposes the chunk and returns its resources to the pool.
- ///
- public void Dispose()
- {
- if (!_disposed)
- {
- ReleaseDataArray();
- _disposed = true;
- }
+ if (IsAllAir)
+ throw new InvalidOperationException("Cannot modify an all-air chunk.");
+ _data![localX, localY, localZ] = value;
}
}
+
+ ///
+ /// Fills the chunk with a specified value.
+ ///
+ public void Fill(T value)
+ {
+ if (IsAllAir)
+ throw new InvalidOperationException("Cannot fill an all-air chunk.");
+
+ for (int x = 0; x < Width; x++)
+ for (int y = 0; y < Height; y++)
+ for (int z = 0; z < Depth; z++)
+ _data![x, y, z] = value;
+ }
+
+ ///
+ /// Rents an array from the pool or creates a new one.
+ ///
+ private static T[,,] RentArray(int width, int height, int depth)
+ {
+ if (_arrayPool.TryTake(out var arr) &&
+ arr.GetLength(0) == width &&
+ arr.GetLength(1) == height &&
+ arr.GetLength(2) == depth)
+ return arr;
+ return new T[width, height, depth];
+ }
+
+ ///
+ /// Returns an array to the pool.
+ ///
+ internal static void ReturnArray(T[,,] arr)
+ {
+ _arrayPool.Add(arr);
+ }
+
+ ///
+ /// Returns the underlying data array (for pooling).
+ ///
+ internal T[,,]? GetDataArray() => _data;
+
+ ///
+ /// Releases the data array back to the pool.
+ ///
+ internal void ReleaseDataArray()
+ {
+ if (_data != null && !IsAllAir)
+ {
+ ReturnArray(_data);
+ }
+ }
+
+ ///
+ /// Disposes the chunk and returns its resources to the pool.
+ ///
+ public void Dispose()
+ {
+ if (!_disposed)
+ {
+ ReleaseDataArray();
+ _disposed = true;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/AdvChkSys/Constraints/WorldConstraints.cs b/src/AdvChkSys/Constraints/WorldConstraints.cs
index 7b97766..5362272 100644
--- a/src/AdvChkSys/Constraints/WorldConstraints.cs
+++ b/src/AdvChkSys/Constraints/WorldConstraints.cs
@@ -1,67 +1,67 @@
using System;
namespace AdvChkSys.Constraints
-{
-///
-/// Represents constraints and limits for the chunk world.
-/// Can be used to restrict world size, chunk counts, and other resource limits.
-///
-public class WorldConstraints
{
///
- /// If set, the minimum allowed chunk X coordinate (inclusive).
+ /// Represents constraints and limits for the chunk world.
+ /// Can be used to restrict world size, chunk counts, and other resource limits.
///
- public int? MinChunkX { get; set; }
-
- ///
- /// If set, the maximum allowed chunk X coordinate (inclusive).
- ///
- public int? MaxChunkX { get; set; }
-
- ///
- /// If set, the minimum allowed chunk Y coordinate (inclusive).
- ///
- public int? MinChunkY { get; set; }
-
- ///
- /// If set, the maximum allowed chunk Y coordinate (inclusive).
- ///
- public int? MaxChunkY { get; set; }
-
- ///
- /// If set, the minimum allowed chunk Z coordinate (inclusive).
- ///
- public int? MinChunkZ { get; set; }
-
- ///
- /// If set, the maximum allowed chunk Z coordinate (inclusive).
- ///
- public int? MaxChunkZ { get; set; }
-
- ///
- /// If set, the maximum number of chunks allowed to be loaded in memory at once.
- ///
- public int? MaxLoadedChunks { get; set; }
-
- ///
- /// Checks if the given chunk coordinates are within the allowed world bounds.
- ///
- public bool IsWithinBounds(int chunkX, int chunkY)
+ public class WorldConstraints
{
- if (MinChunkX.HasValue && chunkX < MinChunkX.Value) return false;
- if (MaxChunkX.HasValue && chunkX > MaxChunkX.Value) return false;
- if (MinChunkY.HasValue && chunkY < MinChunkY.Value) return false;
- if (MaxChunkY.HasValue && chunkY > MaxChunkY.Value) return false;
- return true;
- }
+ ///
+ /// If set, the minimum allowed chunk X coordinate (inclusive).
+ ///
+ public int? MinChunkX { get; set; }
- ///
- /// Checks if the current number of loaded chunks is within the allowed limit.
- ///
- public bool IsWithinChunkLimit(int loadedChunkCount)
- {
- if (MaxLoadedChunks.HasValue && loadedChunkCount > MaxLoadedChunks.Value) return false;
- return true;
+ ///
+ /// If set, the maximum allowed chunk X coordinate (inclusive).
+ ///
+ public int? MaxChunkX { get; set; }
+
+ ///
+ /// If set, the minimum allowed chunk Y coordinate (inclusive).
+ ///
+ public int? MinChunkY { get; set; }
+
+ ///
+ /// If set, the maximum allowed chunk Y coordinate (inclusive).
+ ///
+ public int? MaxChunkY { get; set; }
+
+ ///
+ /// If set, the minimum allowed chunk Z coordinate (inclusive).
+ ///
+ public int? MinChunkZ { get; set; }
+
+ ///
+ /// If set, the maximum allowed chunk Z coordinate (inclusive).
+ ///
+ public int? MaxChunkZ { get; set; }
+
+ ///
+ /// If set, the maximum number of chunks allowed to be loaded in memory at once.
+ ///
+ public int? MaxLoadedChunks { get; set; }
+
+ ///
+ /// Checks if the given chunk coordinates are within the allowed world bounds.
+ ///
+ public bool IsWithinBounds(int chunkX, int chunkY)
+ {
+ if (MinChunkX.HasValue && chunkX < MinChunkX.Value) return false;
+ if (MaxChunkX.HasValue && chunkX > MaxChunkX.Value) return false;
+ if (MinChunkY.HasValue && chunkY < MinChunkY.Value) return false;
+ if (MaxChunkY.HasValue && chunkY > MaxChunkY.Value) return false;
+ return true;
+ }
+
+ ///
+ /// Checks if the current number of loaded chunks is within the allowed limit.
+ ///
+ public bool IsWithinChunkLimit(int loadedChunkCount)
+ {
+ if (MaxLoadedChunks.HasValue && loadedChunkCount > MaxLoadedChunks.Value) return false;
+ return true;
+ }
}
-}
}
\ No newline at end of file
diff --git a/src/AdvChkSys/Diagnostics/MemoryUsageReporter.cs b/src/AdvChkSys/Diagnostics/MemoryUsageReporter.cs
index 2f7626c..ce42c3a 100644
--- a/src/AdvChkSys/Diagnostics/MemoryUsageReporter.cs
+++ b/src/AdvChkSys/Diagnostics/MemoryUsageReporter.cs
@@ -29,7 +29,7 @@ namespace AdvChkSys.Diagnostics
// Calculate estimated chunk memory usage
report.EstimatedChunkMemoryBytes = EstimateChunkMemoryUsage(report.ActiveChunkCount);
-
+
return report;
}
@@ -51,14 +51,16 @@ namespace AdvChkSys.Diagnostics
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- var memStatus = new MemoryHelper.MEMORYSTATUSEX();
- memStatus.dwLength = (uint)Marshal.SizeOf(typeof(MemoryHelper.MEMORYSTATUSEX));
+ var memStatus = new MemoryHelper.MEMORYSTATUSEX
+ {
+ dwLength = (uint)Marshal.SizeOf(typeof(MemoryHelper.MEMORYSTATUSEX))
+ };
if (MemoryHelper.GlobalMemoryStatusEx(ref memStatus))
{
return memStatus.ullTotalPhys;
}
}
-
+
// For non-Windows platforms or if Windows call fails, use a fallback
return 8UL * 1024 * 1024 * 1024; // Assume 8GB as fallback
}
@@ -70,7 +72,7 @@ namespace AdvChkSys.Diagnostics
public static void LogMemoryUsage(Action logAction)
{
var report = GetMemoryUsage();
-
+
logAction($"--- AdvChkSys Memory Report ({report.Timestamp:yyyy-MM-dd HH:mm:ss}) ---");
logAction($"Active Chunks: {report.ActiveChunkCount:N0}");
logAction($"Estimated Chunk Memory: {FormatByteSize(report.EstimatedChunkMemoryBytes)}");
@@ -88,7 +90,7 @@ namespace AdvChkSys.Diagnostics
string[] sizes = { "B", "KB", "MB", "GB", "TB" };
double formattedSize = bytes;
int order = 0;
-
+
while (formattedSize >= 1024 && order < sizes.Length - 1)
{
order++;
@@ -132,9 +134,9 @@ namespace AdvChkSys.Diagnostics
///
/// Percentage of total system memory used by chunks.
///
- public double MemoryUsagePercentage =>
- TotalSystemMemoryBytes > 0
- ? (double)EstimatedChunkMemoryBytes / TotalSystemMemoryBytes * 100
+ public double MemoryUsagePercentage =>
+ TotalSystemMemoryBytes > 0
+ ? (double)EstimatedChunkMemoryBytes / TotalSystemMemoryBytes * 100
: 0;
}
}
\ No newline at end of file
diff --git a/src/AdvChkSys/Events/ChunkEvents.cs b/src/AdvChkSys/Events/ChunkEvents.cs
index 2980bca..5afd54e 100644
--- a/src/AdvChkSys/Events/ChunkEvents.cs
+++ b/src/AdvChkSys/Events/ChunkEvents.cs
@@ -4,123 +4,123 @@ using System.Threading;
namespace AdvChkSys.Events
{
-///
-/// Provides events for chunk lifecycle operations such as loading, unloading, saving, and more.
-/// Thread-safe event subscription and invocation.
-///
-public static class ChunkEvents
-{
- // Backing fields for thread-safe event handling
- private static Action? _chunkLoaded;
- private static Action? _chunkUnloaded;
- private static Action? _chunkLoading;
- private static Action? _chunkUnloading;
- private static Action? _chunkSaving;
- private static Action? _chunkSaved;
-
///
- /// Occurs when a chunk has been loaded into memory.
+ /// Provides events for chunk lifecycle operations such as loading, unloading, saving, and more.
+ /// Thread-safe event subscription and invocation.
///
- public static event Action ChunkLoaded
+ public static class ChunkEvents
{
- add => AddHandler(ref _chunkLoaded, value);
- remove => RemoveHandler(ref _chunkLoaded, value);
- }
+ // Backing fields for thread-safe event handling
+ private static Action? _chunkLoaded;
+ private static Action? _chunkUnloaded;
+ private static Action? _chunkLoading;
+ private static Action? _chunkUnloading;
+ private static Action? _chunkSaving;
+ private static Action? _chunkSaved;
- ///
- /// Occurs when a chunk is about to be loaded into memory.
- ///
- public static event Action ChunkLoading
- {
- add => AddHandler(ref _chunkLoading, value);
- remove => RemoveHandler(ref _chunkLoading, value);
- }
-
- ///
- /// Occurs when a chunk is about to be unloaded from memory.
- ///
- public static event Action ChunkUnloading
- {
- add => AddHandler(ref _chunkUnloading, value);
- remove => RemoveHandler(ref _chunkUnloading, value);
- }
-
- ///
- /// Occurs when a chunk has been unloaded from memory.
- ///
- public static event Action ChunkUnloaded
- {
- add => AddHandler(ref _chunkUnloaded, value);
- remove => RemoveHandler(ref _chunkUnloaded, value);
- }
-
- ///
- /// Occurs when a chunk is about to be saved.
- ///
- public static event Action ChunkSaving
- {
- add => AddHandler(ref _chunkSaving, value);
- remove => RemoveHandler(ref _chunkSaving, value);
- }
-
- ///
- /// Occurs when a chunk has been saved.
- ///
- public static event Action ChunkSaved
- {
- add => AddHandler(ref _chunkSaved, value);
- remove => RemoveHandler(ref _chunkSaved, value);
- }
-
- ///
- /// Raises the ChunkLoading event.
- ///
- public static void OnChunkLoading(Interfaces.IChunk chunk) => _chunkLoading?.Invoke(chunk);
-
- ///
- /// Raises the ChunkLoaded event.
- ///
- public static void OnChunkLoaded(Interfaces.IChunk chunk) => _chunkLoaded?.Invoke(chunk);
-
- ///
- /// Raises the ChunkUnloading event.
- ///
- public static void OnChunkUnloading(Interfaces.IChunk chunk) => _chunkUnloading?.Invoke(chunk);
-
- ///
- /// Raises the ChunkUnloaded event.
- ///
- public static void OnChunkUnloaded(Interfaces.IChunk chunk) => _chunkUnloaded?.Invoke(chunk);
-
- ///
- /// Raises the ChunkSaving event.
- ///
- public static void OnChunkSaving(Interfaces.IChunk chunk) => _chunkSaving?.Invoke(chunk);
-
- ///
- /// Raises the ChunkSaved event.
- ///
- public static void OnChunkSaved(Interfaces.IChunk chunk) => _chunkSaved?.Invoke(chunk);
-
- // Thread-safe add/remove for event handlers
- private static void AddHandler(ref Action? field, Action handler)
- {
- Action? prevHandler, newHandler;
- do
+ ///
+ /// Occurs when a chunk has been loaded into memory.
+ ///
+ public static event Action ChunkLoaded
{
- prevHandler = field;
- newHandler = (Action?)Delegate.Combine(prevHandler, handler);
- } while (Interlocked.CompareExchange(ref field, newHandler, prevHandler) != prevHandler);
- }
+ add => AddHandler(ref _chunkLoaded, value);
+ remove => RemoveHandler(ref _chunkLoaded, value);
+ }
- private static void RemoveHandler(ref Action? field, Action handler)
- {
- Action? prevHandler, newHandler;
- do
+ ///
+ /// Occurs when a chunk is about to be loaded into memory.
+ ///
+ public static event Action ChunkLoading
{
- prevHandler = field;
- newHandler = (Action?)Delegate.Remove(prevHandler, handler);
- } while (Interlocked.CompareExchange(ref field, newHandler, prevHandler) != prevHandler);
+ add => AddHandler(ref _chunkLoading, value);
+ remove => RemoveHandler(ref _chunkLoading, value);
+ }
+
+ ///
+ /// Occurs when a chunk is about to be unloaded from memory.
+ ///
+ public static event Action ChunkUnloading
+ {
+ add => AddHandler(ref _chunkUnloading, value);
+ remove => RemoveHandler(ref _chunkUnloading, value);
+ }
+
+ ///
+ /// Occurs when a chunk has been unloaded from memory.
+ ///
+ public static event Action ChunkUnloaded
+ {
+ add => AddHandler(ref _chunkUnloaded, value);
+ remove => RemoveHandler(ref _chunkUnloaded, value);
+ }
+
+ ///
+ /// Occurs when a chunk is about to be saved.
+ ///
+ public static event Action ChunkSaving
+ {
+ add => AddHandler(ref _chunkSaving, value);
+ remove => RemoveHandler(ref _chunkSaving, value);
+ }
+
+ ///
+ /// Occurs when a chunk has been saved.
+ ///
+ public static event Action ChunkSaved
+ {
+ add => AddHandler(ref _chunkSaved, value);
+ remove => RemoveHandler(ref _chunkSaved, value);
+ }
+
+ ///
+ /// Raises the ChunkLoading event.
+ ///
+ public static void OnChunkLoading(Interfaces.IChunk chunk) => _chunkLoading?.Invoke(chunk);
+
+ ///
+ /// Raises the ChunkLoaded event.
+ ///
+ public static void OnChunkLoaded(Interfaces.IChunk chunk) => _chunkLoaded?.Invoke(chunk);
+
+ ///
+ /// Raises the ChunkUnloading event.
+ ///
+ public static void OnChunkUnloading(Interfaces.IChunk chunk) => _chunkUnloading?.Invoke(chunk);
+
+ ///
+ /// Raises the ChunkUnloaded event.
+ ///
+ public static void OnChunkUnloaded(Interfaces.IChunk chunk) => _chunkUnloaded?.Invoke(chunk);
+
+ ///
+ /// Raises the ChunkSaving event.
+ ///
+ public static void OnChunkSaving(Interfaces.IChunk chunk) => _chunkSaving?.Invoke(chunk);
+
+ ///
+ /// Raises the ChunkSaved event.
+ ///
+ public static void OnChunkSaved(Interfaces.IChunk chunk) => _chunkSaved?.Invoke(chunk);
+
+ // Thread-safe add/remove for event handlers
+ private static void AddHandler(ref Action? field, Action handler)
+ {
+ Action? prevHandler, newHandler;
+ do
+ {
+ prevHandler = field;
+ newHandler = (Action?)Delegate.Combine(prevHandler, handler);
+ } while (Interlocked.CompareExchange(ref field, newHandler, prevHandler) != prevHandler);
+ }
+
+ private static void RemoveHandler(ref Action? field, Action handler)
+ {
+ Action? prevHandler, newHandler;
+ do
+ {
+ prevHandler = field;
+ newHandler = (Action?)Delegate.Remove(prevHandler, handler);
+ } while (Interlocked.CompareExchange(ref field, newHandler, prevHandler) != prevHandler);
+ }
}
-}
}
\ No newline at end of file
diff --git a/src/AdvChkSys/Interfaces/IChunk.cs b/src/AdvChkSys/Interfaces/IChunk.cs
index 59ea3eb..b83440e 100644
--- a/src/AdvChkSys/Interfaces/IChunk.cs
+++ b/src/AdvChkSys/Interfaces/IChunk.cs
@@ -12,7 +12,7 @@ namespace AdvChkSys.Interfaces
/// The chunk's X position in chunk-space coordinates.
///
int X { get; }
-
+
///
/// The chunk's Y position in chunk-space coordinates.
///
diff --git a/src/AdvChkSys/Interfaces/IChunkManager.cs b/src/AdvChkSys/Interfaces/IChunkManager.cs
index 95fd641..dd097e9 100644
--- a/src/AdvChkSys/Interfaces/IChunkManager.cs
+++ b/src/AdvChkSys/Interfaces/IChunkManager.cs
@@ -2,37 +2,37 @@
using System.Collections.Generic;
namespace AdvChkSys.Interfaces
-{
-///
-/// Interface for managing chunks in memory.
-/// Provides methods for loading, unloading, and accessing chunks.
-///
-public interface IChunkManager
{
///
- /// Returns true if the chunk at (x, y) is loaded.
+ /// Interface for managing chunks in memory.
+ /// Provides methods for loading, unloading, and accessing chunks.
///
- bool IsChunkLoaded(int x, int y);
+ public interface IChunkManager
+ {
+ ///
+ /// Returns true if the chunk at (x, y) is loaded.
+ ///
+ bool IsChunkLoaded(int x, int y);
- ///
- /// Gets the chunk at (x, y) if loaded, or null if not.
- ///
- IChunk? GetChunk(int x, int y);
+ ///
+ /// Gets the chunk at (x, y) if loaded, or null if not.
+ ///
+ IChunk? GetChunk(int x, int y);
- ///
- /// Loads or creates a chunk at (x, y) with the given size.
- /// Returns the loaded or newly created chunk.
- ///
- IChunk LoadOrCreateChunk(int x, int y, int width, int height);
+ ///
+ /// Loads or creates a chunk at (x, y) with the given size.
+ /// Returns the loaded or newly created chunk.
+ ///
+ IChunk LoadOrCreateChunk(int x, int y, int width, int height);
- ///
- /// Unloads (removes) the chunk at (x, y) if loaded.
- ///
- bool UnloadChunk(int x, int y);
+ ///
+ /// Unloads (removes) the chunk at (x, y) if loaded.
+ ///
+ bool UnloadChunk(int x, int y);
- ///
- /// Enumerates all loaded chunks.
- ///
- IEnumerable GetAllChunks();
-}
+ ///
+ /// Enumerates all loaded chunks.
+ ///
+ IEnumerable GetAllChunks();
+ }
}
\ No newline at end of file
diff --git a/src/AdvChkSys/Manager/ChunkManager2D.cs b/src/AdvChkSys/Manager/ChunkManager2D.cs
index 20ad0ee..df1aa92 100644
--- a/src/AdvChkSys/Manager/ChunkManager2D.cs
+++ b/src/AdvChkSys/Manager/ChunkManager2D.cs
@@ -20,23 +20,23 @@ namespace AdvChkSys.Manager
private readonly WorldConstraints? _constraints;
private readonly int _capacity;
private readonly Dictionary<(int, int), Task>> _loadingChunks = new();
- private readonly object _lock = new object();
+ private readonly object _lock = new();
///
/// Delegate for custom air check logic
///
private Func? _airCheckDelegate;
-
+
///
/// World generator for determining empty regions
///
private IWorldGenerator? _worldGenerator;
-
+
///
/// Data provider for determining empty regions
///
private IDataProvider? _dataProvider;
-
+
///
/// Height map for determining sky chunks
///
@@ -155,7 +155,7 @@ namespace AdvChkSys.Manager
{
var key = (x, y);
Task>? task = null;
-
+
// First check if we already have a task
lock (_lock)
{
@@ -173,7 +173,7 @@ namespace AdvChkSys.Manager
// Otherwise, create a new task outside the lock
task = Task.Run(() => LoadOrCreateChunk(x, y, width, height));
-
+
// Register the task
lock (_lock)
{
@@ -244,7 +244,7 @@ namespace AdvChkSys.Manager
IChunk IChunkManager.LoadOrCreateChunk(int x, int y, int width, int height) => LoadOrCreateChunk(x, y, width, height);
bool IChunkManager.UnloadChunk(int x, int y) => UnloadChunk(x, y);
IEnumerable IChunkManager.GetAllChunks() => _chunks.Values;
-
+
///
/// Sets a custom delegate for determining if a region should be all air.
///
@@ -252,7 +252,7 @@ namespace AdvChkSys.Manager
{
_airCheckDelegate = airCheckDelegate;
}
-
+
///
/// Sets a world generator for determining empty regions.
///
@@ -260,7 +260,7 @@ namespace AdvChkSys.Manager
{
_worldGenerator = worldGenerator;
}
-
+
///
/// Sets a data provider for determining empty regions.
///
@@ -268,7 +268,7 @@ namespace AdvChkSys.Manager
{
_dataProvider = dataProvider;
}
-
+
///
/// Sets a height map for determining sky chunks.
///
@@ -277,7 +277,7 @@ namespace AdvChkSys.Manager
_heightMap = heightMap;
}
}
-
+
///
/// Interface for world generators that can determine if regions are empty.
///
@@ -288,7 +288,7 @@ namespace AdvChkSys.Manager
///
bool IsRegionEmpty(int x, int y, int width, int height);
}
-
+
///
/// Interface for data providers that can determine if regions are empty.
///
@@ -299,7 +299,7 @@ namespace AdvChkSys.Manager
///
bool IsEmptyRegion(int x, int y, int width, int height);
}
-
+
///
/// Interface for height maps that can determine the maximum height at a position.
///
diff --git a/src/AdvChkSys/Manager/ChunkManager3D.cs b/src/AdvChkSys/Manager/ChunkManager3D.cs
index 149aa20..23013c0 100644
--- a/src/AdvChkSys/Manager/ChunkManager3D.cs
+++ b/src/AdvChkSys/Manager/ChunkManager3D.cs
@@ -21,7 +21,7 @@ namespace AdvChkSys.Manager
private readonly LRUCache<(int, int, int), Chunk3D> _chunks;
private readonly WorldConstraints? _constraints;
private readonly int _capacity;
-
+
///
/// Delegate for custom air check logic
///
@@ -67,7 +67,7 @@ namespace AdvChkSys.Manager
_chunks.TryGet((x, y, z), out var chunk);
return chunk;
}
-
+
///
/// Determines if a region should be represented as an all-air chunk.
///
@@ -139,7 +139,7 @@ namespace AdvChkSys.Manager
arr = arrField.GetValue(chunk) as T[,,];
}
}
-
+
if (!chunk.IsAllAir && arr != null)
{
Chunk3D.ReturnArray(arr);
@@ -175,7 +175,7 @@ namespace AdvChkSys.Manager
LoadOrCreateChunk(x, y, 0, width, height, 1);
bool IChunkManager.UnloadChunk(int x, int y) => UnloadChunk(x, y, 0);
IEnumerable IChunkManager.GetAllChunks() => _chunks.Values;
-
+
///
/// Sets a custom delegate for determining if a region should be all air.
///
@@ -208,7 +208,7 @@ namespace AdvChkSys.Manager
_heightMap = heightMap;
}
}
-
+
///
/// Interface for 3D world generators that can determine if regions are empty.
///
diff --git a/src/AdvChkSys/Resources/ChunkResourceManager.cs b/src/AdvChkSys/Resources/ChunkResourceManager.cs
index 2617277..f4793dd 100644
--- a/src/AdvChkSys/Resources/ChunkResourceManager.cs
+++ b/src/AdvChkSys/Resources/ChunkResourceManager.cs
@@ -6,67 +6,67 @@ using System.Collections.Generic;
namespace AdvChkSys.Resources
{
-///
-/// Manages allocation and release of chunk resources in memory.
-/// Can be extended to track resource usage, pooling, or implement custom memory strategies.
-///
-public static class ChunkResourceManager
-{
- // Example: Track allocated chunks (for diagnostics, pooling, or resource limits)
- private static readonly ConcurrentDictionary _allocatedChunks = new ConcurrentDictionary();
- private static int _allocatedChunkCount;
- private static readonly object _lock = new object();
- private static readonly HashSet _activeChunks = new();
-
///
- /// Called when a chunk is allocated/loaded into memory.
- /// Tracks the chunk and can be extended for pooling or resource limits.
+ /// Manages allocation and release of chunk resources in memory.
+ /// Can be extended to track resource usage, pooling, or implement custom memory strategies.
///
- public static void AllocateChunk(IChunk chunk)
+ public static class ChunkResourceManager
{
- lock (_lock)
+ // Example: Track allocated chunks (for diagnostics, pooling, or resource limits)
+ private static readonly ConcurrentDictionary _allocatedChunks = new();
+ private static int _allocatedChunkCount;
+ private static readonly object _lock = new();
+ private static readonly HashSet _activeChunks = new();
+
+ ///
+ /// Called when a chunk is allocated/loaded into memory.
+ /// Tracks the chunk and can be extended for pooling or resource limits.
+ ///
+ public static void AllocateChunk(IChunk chunk)
{
- _allocatedChunkCount++;
- _activeChunks.Add(chunk);
+ lock (_lock)
+ {
+ _allocatedChunkCount++;
+ _activeChunks.Add(chunk);
+ }
+ }
+
+ ///
+ /// Called when a chunk is released/unloaded from memory.
+ /// Removes the chunk from tracking and can be extended for pooling or cleanup.
+ ///
+ public static void ReleaseChunk(IChunk chunk)
+ {
+ lock (_lock)
+ {
+ _allocatedChunkCount--;
+ _activeChunks.Remove(chunk);
+ }
+ }
+
+ ///
+ /// Gets the current number of allocated chunks.
+ ///
+ public static int AllocatedChunkCount => _allocatedChunks.Count;
+
+ ///
+ /// Clears all tracked chunks (for diagnostics or shutdown).
+ ///
+ public static void Clear()
+ {
+ _allocatedChunks.Clear();
+ }
+
+ ///
+ /// Gets the current count of active chunks being tracked by the resource manager.
+ ///
+ /// The number of active chunks
+ public static int GetActiveChunkCount()
+ {
+ lock (_lock)
+ {
+ return _activeChunks.Count;
+ }
}
}
-
- ///
- /// Called when a chunk is released/unloaded from memory.
- /// Removes the chunk from tracking and can be extended for pooling or cleanup.
- ///
- public static void ReleaseChunk(IChunk chunk)
- {
- lock (_lock)
- {
- _allocatedChunkCount--;
- _activeChunks.Remove(chunk);
- }
- }
-
- ///
- /// Gets the current number of allocated chunks.
- ///
- public static int AllocatedChunkCount => _allocatedChunks.Count;
-
- ///
- /// Clears all tracked chunks (for diagnostics or shutdown).
- ///
- public static void Clear()
- {
- _allocatedChunks.Clear();
- }
-
- ///
- /// Gets the current count of active chunks being tracked by the resource manager.
- ///
- /// The number of active chunks
- public static int GetActiveChunkCount()
- {
- lock (_lock)
- {
- return _activeChunks.Count;
- }
- }
-}
}
\ No newline at end of file
diff --git a/src/AdvChkSys/Spatial/ChunkExtensions.cs b/src/AdvChkSys/Spatial/ChunkExtensions.cs
index 95d355d..c37ec4e 100644
--- a/src/AdvChkSys/Spatial/ChunkExtensions.cs
+++ b/src/AdvChkSys/Spatial/ChunkExtensions.cs
@@ -15,19 +15,19 @@ namespace AdvChkSys.Spatial
{
return new Chunk3DAdapter(chunk);
}
-
+
///
/// Adapter to make Chunk3D implement IChunk3D.
///
private class Chunk3DAdapter : IChunk3D
{
private readonly Chunk3D _chunk;
-
+
public Chunk3DAdapter(Chunk3D chunk)
{
_chunk = chunk;
}
-
+
public int X => _chunk.X;
public int Y => _chunk.Y;
public int Z => _chunk.Z;
diff --git a/src/AdvChkSys/Spatial/SpatialChunkIndex.cs b/src/AdvChkSys/Spatial/SpatialChunkIndex.cs
index dc92d0a..23183f0 100644
--- a/src/AdvChkSys/Spatial/SpatialChunkIndex.cs
+++ b/src/AdvChkSys/Spatial/SpatialChunkIndex.cs
@@ -16,13 +16,13 @@ namespace AdvChkSys.Spatial
{
// Grid-based spatial index for fast lookups
private readonly Dictionary<(int, int), HashSet> _grid = new();
-
+
// All chunks in the index for iteration
private readonly HashSet _allChunks = new();
-
+
// Lock for thread safety
private readonly object _lock = new();
-
+
///
/// Adds a chunk to the spatial index.
///
@@ -31,12 +31,12 @@ namespace AdvChkSys.Spatial
{
if (chunk == null)
throw new ArgumentNullException(nameof(chunk));
-
+
lock (_lock)
{
// Add to all chunks set
_allChunks.Add(chunk);
-
+
// Add to grid cells
var key = (chunk.X, chunk.Y);
if (!_grid.TryGetValue(key, out var chunks))
@@ -47,7 +47,7 @@ namespace AdvChkSys.Spatial
chunks.Add(chunk);
}
}
-
+
///
/// Removes a chunk from the spatial index.
///
@@ -57,12 +57,12 @@ namespace AdvChkSys.Spatial
{
if (chunk == null)
throw new ArgumentNullException(nameof(chunk));
-
+
lock (_lock)
{
// Remove from all chunks set
bool removed = _allChunks.Remove(chunk);
-
+
// Remove from grid cells
var key = (chunk.X, chunk.Y);
if (_grid.TryGetValue(key, out var chunks))
@@ -73,11 +73,11 @@ namespace AdvChkSys.Spatial
_grid.Remove(key);
}
}
-
+
return removed;
}
}
-
+
///
/// Finds all chunks within a rectangular region.
///
@@ -91,7 +91,7 @@ namespace AdvChkSys.Spatial
lock (_lock)
{
var result = new HashSet();
-
+
// Check each grid cell that overlaps with the region
for (int x = minX; x <= maxX; x++)
{
@@ -107,11 +107,11 @@ namespace AdvChkSys.Spatial
}
}
}
-
+
return result;
}
}
-
+
///
/// Finds all chunks within a specified distance from a point.
///
@@ -125,13 +125,13 @@ namespace AdvChkSys.Spatial
{
var result = new HashSet();
float radiusSquared = radius * radius;
-
+
// Calculate the bounding box of the circle
int minX = (int)(centerX - radius);
int minY = (int)(centerY - radius);
int maxX = (int)(centerX + radius);
int maxY = (int)(centerY + radius);
-
+
// Check each grid cell that might overlap with the circle
for (int x = minX; x <= maxX; x++)
{
@@ -146,7 +146,7 @@ namespace AdvChkSys.Spatial
float dx = chunk.X - centerX;
float dy = chunk.Y - centerY;
float distanceSquared = dx * dx + dy * dy;
-
+
// Check if the chunk is within the radius
if (distanceSquared <= radiusSquared)
{
@@ -156,11 +156,11 @@ namespace AdvChkSys.Spatial
}
}
}
-
+
return result;
}
}
-
+
///
/// Finds the nearest chunk to a point.
///
@@ -173,27 +173,27 @@ namespace AdvChkSys.Spatial
{
if (_allChunks.Count == 0)
return default;
-
+
T? nearest = default;
float nearestDistanceSquared = float.MaxValue;
-
+
foreach (var chunk in _allChunks)
{
float dx = chunk.X - x;
float dy = chunk.Y - y;
float distanceSquared = dx * dx + dy * dy;
-
+
if (distanceSquared < nearestDistanceSquared)
{
nearestDistanceSquared = distanceSquared;
nearest = chunk;
}
}
-
+
return nearest;
}
}
-
+
///
/// Finds all chunks that contain a point.
///
@@ -205,7 +205,7 @@ namespace AdvChkSys.Spatial
lock (_lock)
{
var result = new HashSet();
-
+
// Get the grid cell for this point
var key = (x, y);
if (_grid.TryGetValue(key, out var chunks))
@@ -220,11 +220,11 @@ namespace AdvChkSys.Spatial
}
}
}
-
+
return result;
}
}
-
+
///
/// Gets all chunks in the index.
///
@@ -235,7 +235,7 @@ namespace AdvChkSys.Spatial
return _allChunks.ToList();
}
}
-
+
///
/// Clears all chunks from the index.
///
@@ -247,7 +247,7 @@ namespace AdvChkSys.Spatial
_grid.Clear();
}
}
-
+
///
/// Gets the number of chunks in the index.
///
@@ -261,7 +261,7 @@ namespace AdvChkSys.Spatial
}
}
}
-
+
///
/// Checks if a chunk is in the index.
///
@@ -271,13 +271,13 @@ namespace AdvChkSys.Spatial
{
if (chunk == null)
throw new ArgumentNullException(nameof(chunk));
-
+
lock (_lock)
{
return _allChunks.Contains(chunk);
}
}
-
+
///
/// Updates the position of a chunk in the spatial index.
///
@@ -289,13 +289,13 @@ namespace AdvChkSys.Spatial
{
if (chunk == null)
throw new ArgumentNullException(nameof(chunk));
-
+
lock (_lock)
{
// Remove from old grid cell
var oldKey = (oldX, oldY);
bool removed = false;
-
+
if (_grid.TryGetValue(oldKey, out var chunks))
{
removed = chunks.Remove(chunk);
@@ -304,10 +304,10 @@ namespace AdvChkSys.Spatial
_grid.Remove(oldKey);
}
}
-
+
if (!removed)
return false;
-
+
// Add to new grid cell
var newKey = (chunk.X, chunk.Y);
if (!_grid.TryGetValue(newKey, out var newChunks))
@@ -316,11 +316,11 @@ namespace AdvChkSys.Spatial
_grid[newKey] = newChunks;
}
newChunks.Add(chunk);
-
+
return true;
}
}
-
+
///
/// Performs a spatial query using a custom filter.
///
@@ -330,13 +330,13 @@ namespace AdvChkSys.Spatial
{
if (filter == null)
throw new ArgumentNullException(nameof(filter));
-
+
lock (_lock)
{
return _allChunks.Where(filter).ToList();
}
}
-
+
///
/// Finds all chunks that intersect with a given chunk.
///
@@ -346,14 +346,14 @@ namespace AdvChkSys.Spatial
{
if (chunk == null)
throw new ArgumentNullException(nameof(chunk));
-
+
return FindChunksInRegion(
- chunk.X,
- chunk.Y,
- chunk.X + chunk.Width - 1,
+ chunk.X,
+ chunk.Y,
+ chunk.X + chunk.Width - 1,
chunk.Y + chunk.Height - 1);
}
-
+
///
/// Finds all chunks that are neighbors of a given chunk.
///
@@ -364,17 +364,17 @@ namespace AdvChkSys.Spatial
{
if (chunk == null)
throw new ArgumentNullException(nameof(chunk));
-
+
lock (_lock)
{
var result = new HashSet();
-
+
// Check the four adjacent cells
CheckNeighbor(chunk.X - 1, chunk.Y, result);
CheckNeighbor(chunk.X + 1, chunk.Y, result);
CheckNeighbor(chunk.X, chunk.Y - 1, result);
CheckNeighbor(chunk.X, chunk.Y + 1, result);
-
+
// Check diagonal cells if requested
if (includeDiagonals)
{
@@ -383,11 +383,11 @@ namespace AdvChkSys.Spatial
CheckNeighbor(chunk.X - 1, chunk.Y + 1, result);
CheckNeighbor(chunk.X + 1, chunk.Y + 1, result);
}
-
+
return result;
}
}
-
+
///
/// Helper method to check for chunks at a specific grid cell.
///
@@ -402,7 +402,7 @@ namespace AdvChkSys.Spatial
}
}
}
-
+
///
/// Performs a spatial operation on chunks in parallel.
///
@@ -417,21 +417,21 @@ namespace AdvChkSys.Spatial
{
if (operation == null)
throw new ArgumentNullException(nameof(operation));
-
+
IEnumerable chunksToProcess;
-
+
lock (_lock)
{
- chunksToProcess = filter != null
+ chunksToProcess = filter != null
? _allChunks.Where(filter).ToList()
: _allChunks.ToList();
}
-
+
return ChunkTaskScheduler.RunBatchParallelAsync(
chunksToProcess.Select(chunk => new Action(() => operation(chunk))).ToArray(),
maxDegreeOfParallelism);
}
-
+
///
/// Finds all chunks in a region and performs an operation on them in parallel.
///
@@ -449,14 +449,14 @@ namespace AdvChkSys.Spatial
{
if (operation == null)
throw new ArgumentNullException(nameof(operation));
-
+
var chunksInRegion = FindChunksInRegion(minX, minY, maxX, maxY).ToList();
-
+
return ChunkTaskScheduler.RunBatchParallelAsync(
chunksInRegion.Select(chunk => new Action(() => operation(chunk))).ToArray(),
maxDegreeOfParallelism);
}
-
+
///
/// Creates a quadtree-based spatial index for more efficient queries.
///
@@ -468,7 +468,7 @@ namespace AdvChkSys.Spatial
public QuadtreeSpatialIndex CreateQuadtreeIndex(int minX, int minY, int maxX, int maxY)
{
var quadtree = new QuadtreeSpatialIndex(minX, minY, maxX, maxY);
-
+
lock (_lock)
{
foreach (var chunk in _allChunks)
@@ -476,11 +476,11 @@ namespace AdvChkSys.Spatial
quadtree.AddChunk(chunk);
}
}
-
+
return quadtree;
}
}
-
+
///
/// Provides a quadtree-based spatial index for more efficient region queries.
///
@@ -489,7 +489,7 @@ namespace AdvChkSys.Spatial
private readonly QuadtreeNode _root;
private readonly HashSet _allChunks = new();
private readonly object _lock = new();
-
+
///
/// Initializes a new instance of the QuadtreeSpatialIndex class.
///
@@ -501,7 +501,7 @@ namespace AdvChkSys.Spatial
{
_root = new QuadtreeNode(minX, minY, maxX, maxY);
}
-
+
///
/// Adds a chunk to the quadtree.
///
@@ -510,14 +510,14 @@ namespace AdvChkSys.Spatial
{
if (chunk == null)
throw new ArgumentNullException(nameof(chunk));
-
+
lock (_lock)
{
_allChunks.Add(chunk);
_root.Insert(chunk);
}
}
-
+
///
/// Removes a chunk from the quadtree.
///
@@ -527,7 +527,7 @@ namespace AdvChkSys.Spatial
{
if (chunk == null)
throw new ArgumentNullException(nameof(chunk));
-
+
lock (_lock)
{
bool removed = _allChunks.Remove(chunk);
@@ -538,7 +538,7 @@ namespace AdvChkSys.Spatial
return removed;
}
}
-
+
///
/// Finds all chunks within a rectangular region.
///
@@ -556,7 +556,7 @@ namespace AdvChkSys.Spatial
return result;
}
}
-
+
///
/// Gets all chunks in the quadtree.
///
@@ -567,7 +567,7 @@ namespace AdvChkSys.Spatial
return _allChunks.ToList();
}
}
-
+
///
/// Clears all chunks from the quadtree.
///
@@ -579,7 +579,7 @@ namespace AdvChkSys.Spatial
_root.Clear();
}
}
-
+
///
/// Gets the number of chunks in the quadtree.
///
@@ -593,7 +593,7 @@ namespace AdvChkSys.Spatial
}
}
}
-
+
///
/// Represents a node in the quadtree.
///
@@ -601,22 +601,22 @@ namespace AdvChkSys.Spatial
{
// Boundary of this node
private readonly int _minX, _minY, _maxX, _maxY;
-
+
// Children nodes (NW, NE, SW, SE)
private QuadtreeNode? _northWest;
private QuadtreeNode? _northEast;
private QuadtreeNode? _southWest;
private QuadtreeNode? _southEast;
-
+
// Chunks in this node
private readonly HashSet _chunks = new();
-
+
// Maximum number of chunks before splitting
private const int MAX_CHUNKS = 8;
-
+
// Minimum size of a node
private const int MIN_SIZE = 1;
-
+
///
/// Initializes a new instance of the QuadtreeNode class.
///
@@ -627,7 +627,7 @@ namespace AdvChkSys.Spatial
_maxX = maxX;
_maxY = maxY;
}
-
+
///
/// Inserts a chunk into the quadtree.
///
@@ -636,27 +636,27 @@ namespace AdvChkSys.Spatial
// Check if the chunk is within this node's boundary
if (!Intersects(chunk))
return;
-
+
// If we haven't split yet and have room, add the chunk to this node
if (_northWest == null && _chunks.Count < MAX_CHUNKS)
{
_chunks.Add(chunk);
return;
}
-
+
// If we haven't split yet but need to, split the node
if (_northWest == null)
{
Split();
}
-
+
// Try to insert the chunk into the children
_northWest?.Insert(chunk);
_northEast?.Insert(chunk);
_southWest?.Insert(chunk);
_southEast?.Insert(chunk);
}
-
+
///
/// Removes a chunk from the quadtree.
///
@@ -665,10 +665,10 @@ namespace AdvChkSys.Spatial
// Check if the chunk is within this node's boundary
if (!Intersects(chunk))
return false;
-
+
// Try to remove from this node
bool removed = _chunks.Remove(chunk);
-
+
// If we have children, try to remove from them too
if (_northWest != null)
{
@@ -677,10 +677,10 @@ namespace AdvChkSys.Spatial
removed |= _southWest!.Remove(chunk);
removed |= _southEast!.Remove(chunk);
}
-
+
return removed;
}
-
+
///
/// Queries the quadtree for chunks in a region.
///
@@ -689,7 +689,7 @@ namespace AdvChkSys.Spatial
// Check if the query region intersects this node
if (maxX < _minX || minX > _maxX || maxY < _minY || minY > _maxY)
return;
-
+
// Add chunks from this node that intersect the query region
foreach (var chunk in _chunks)
{
@@ -699,7 +699,7 @@ namespace AdvChkSys.Spatial
result.Add(chunk);
}
}
-
+
// If we have children, query them too
if (_northWest != null)
{
@@ -709,7 +709,7 @@ namespace AdvChkSys.Spatial
_southEast!.Query(minX, minY, maxX, maxY, result);
}
}
-
+
///
/// Splits this node into four children.
///
@@ -717,51 +717,51 @@ namespace AdvChkSys.Spatial
{
int midX = (_minX + _maxX) / 2;
int midY = (_minY + _maxY) / 2;
-
+
// Don't split if the node is too small
if (midX == _minX || midY == _minY)
return;
-
+
_northWest = new QuadtreeNode(_minX, _minY, midX, midY);
_northEast = new QuadtreeNode(midX, _minY, _maxX, midY);
_southWest = new QuadtreeNode(_minX, midY, midX, _maxY);
_southEast = new QuadtreeNode(midX, midY, _maxX, _maxY);
-
+
// Redistribute chunks to children
var chunksToRedistribute = new List(_chunks);
_chunks.Clear();
-
+
foreach (var chunk in chunksToRedistribute)
{
Insert(chunk);
}
}
-
+
///
/// Checks if a chunk intersects with this node's boundary.
///
private bool Intersects(T chunk)
{
- return chunk.X <= _maxX &&
- chunk.X + chunk.Width >= _minX &&
- chunk.Y <= _maxY &&
+ return chunk.X <= _maxX &&
+ chunk.X + chunk.Width >= _minX &&
+ chunk.Y <= _maxY &&
chunk.Y + chunk.Height >= _minY;
}
-
+
///
/// Clears all chunks from this node and its children.
///
public void Clear()
{
_chunks.Clear();
-
+
if (_northWest != null)
{
_northWest.Clear();
_northEast!.Clear();
_southWest!.Clear();
_southEast!.Clear();
-
+
_northWest = null;
_northEast = null;
_southWest = null;
@@ -770,7 +770,7 @@ namespace AdvChkSys.Spatial
}
}
}
-
+
///
/// Provides a spatial index for 3D chunks.
///
@@ -778,13 +778,13 @@ namespace AdvChkSys.Spatial
{
// Grid-based spatial index for fast lookups
private readonly Dictionary<(int, int, int), HashSet> _grid = new();
-
+
// All chunks in the index for iteration
private readonly HashSet _allChunks = new();
-
+
// Lock for thread safety
private readonly object _lock = new();
-
+
///
/// Adds a chunk to the spatial index.
///
@@ -793,12 +793,12 @@ namespace AdvChkSys.Spatial
{
if (chunk == null)
throw new ArgumentNullException(nameof(chunk));
-
+
lock (_lock)
{
// Add to all chunks set
_allChunks.Add(chunk);
-
+
// Add to grid cells
var key = (chunk.X, chunk.Y, chunk.Z);
if (!_grid.TryGetValue(key, out var chunks))
@@ -809,7 +809,7 @@ namespace AdvChkSys.Spatial
chunks.Add(chunk);
}
}
-
+
///
/// Removes a chunk from the spatial index.
///
@@ -819,12 +819,12 @@ namespace AdvChkSys.Spatial
{
if (chunk == null)
throw new ArgumentNullException(nameof(chunk));
-
+
lock (_lock)
{
// Remove from all chunks set
bool removed = _allChunks.Remove(chunk);
-
+
// Remove from grid cells
var key = (chunk.X, chunk.Y, chunk.Z);
if (_grid.TryGetValue(key, out var chunks))
@@ -835,11 +835,11 @@ namespace AdvChkSys.Spatial
_grid.Remove(key);
}
}
-
+
return removed;
}
}
-
+
///
/// Finds all chunks within a rectangular region.
///
@@ -855,7 +855,7 @@ namespace AdvChkSys.Spatial
lock (_lock)
{
var result = new HashSet();
-
+
// Check each grid cell that overlaps with the region
for (int x = minX; x <= maxX; x++)
{
@@ -874,11 +874,11 @@ namespace AdvChkSys.Spatial
}
}
}
-
+
return result;
}
}
-
+
///
/// Finds all chunks within a specified distance from a point.
///
@@ -893,7 +893,7 @@ namespace AdvChkSys.Spatial
{
var result = new HashSet();
float radiusSquared = radius * radius;
-
+
// Calculate the bounding box of the sphere
int minX = (int)(centerX - radius);
int minY = (int)(centerY - radius);
@@ -901,7 +901,7 @@ namespace AdvChkSys.Spatial
int maxX = (int)(centerX + radius);
int maxY = (int)(centerY + radius);
int maxZ = (int)(centerZ + radius);
-
+
// Check each grid cell that might overlap with the sphere
for (int x = minX; x <= maxX; x++)
{
@@ -919,7 +919,7 @@ namespace AdvChkSys.Spatial
float dy = chunk.Y - centerY;
float dz = chunk.Z - centerZ;
float distanceSquared = dx * dx + dy * dy + dz * dz;
-
+
// Check if the chunk is within the radius
if (distanceSquared <= radiusSquared)
{
@@ -930,11 +930,11 @@ namespace AdvChkSys.Spatial
}
}
}
-
+
return result;
}
}
-
+
///
/// Finds the nearest chunk to a point.
///
@@ -948,28 +948,28 @@ namespace AdvChkSys.Spatial
{
if (_allChunks.Count == 0)
return default;
-
+
T? nearest = default;
float nearestDistanceSquared = float.MaxValue;
-
+
foreach (var chunk in _allChunks)
{
float dx = chunk.X - x;
float dy = chunk.Y - y;
float dz = chunk.Z - z;
float distanceSquared = dx * dx + dy * dy + dz * dz;
-
+
if (distanceSquared < nearestDistanceSquared)
{
nearestDistanceSquared = distanceSquared;
nearest = chunk;
}
}
-
+
return nearest;
}
}
-
+
///
/// Gets all chunks in the index.
///
@@ -980,7 +980,7 @@ namespace AdvChkSys.Spatial
return _allChunks.ToList();
}
}
-
+
///
/// Clears all chunks from the index.
///
@@ -992,7 +992,7 @@ namespace AdvChkSys.Spatial
_grid.Clear();
}
}
-
+
///
/// Gets the number of chunks in the index.
///
@@ -1006,7 +1006,7 @@ namespace AdvChkSys.Spatial
}
}
}
-
+
///
/// Checks if a chunk is in the index.
///
@@ -1016,13 +1016,13 @@ namespace AdvChkSys.Spatial
{
if (chunk == null)
throw new ArgumentNullException(nameof(chunk));
-
+
lock (_lock)
{
return _allChunks.Contains(chunk);
}
}
-
+
///
/// Updates the position of a chunk in the spatial index.
///
@@ -1035,13 +1035,13 @@ namespace AdvChkSys.Spatial
{
if (chunk == null)
throw new ArgumentNullException(nameof(chunk));
-
+
lock (_lock)
{
// Remove from old grid cell
var oldKey = (oldX, oldY, oldZ);
bool removed = false;
-
+
if (_grid.TryGetValue(oldKey, out var chunks))
{
removed = chunks.Remove(chunk);
@@ -1050,10 +1050,10 @@ namespace AdvChkSys.Spatial
_grid.Remove(oldKey);
}
}
-
+
if (!removed)
return false;
-
+
// Add to new grid cell
var newKey = (chunk.X, chunk.Y, chunk.Z);
if (!_grid.TryGetValue(newKey, out var newChunks))
@@ -1062,11 +1062,11 @@ namespace AdvChkSys.Spatial
_grid[newKey] = newChunks;
}
newChunks.Add(chunk);
-
+
return true;
}
}
-
+
///
/// Performs a spatial query using a custom filter.
///
@@ -1076,13 +1076,13 @@ namespace AdvChkSys.Spatial
{
if (filter == null)
throw new ArgumentNullException(nameof(filter));
-
+
lock (_lock)
{
return _allChunks.Where(filter).ToList();
}
}
-
+
///
/// Finds all chunks that intersect with a given chunk.
///
@@ -1092,16 +1092,16 @@ namespace AdvChkSys.Spatial
{
if (chunk == null)
throw new ArgumentNullException(nameof(chunk));
-
+
return FindChunksInRegion(
- chunk.X,
- chunk.Y,
+ chunk.X,
+ chunk.Y,
chunk.Z,
- chunk.X + chunk.Width - 1,
+ chunk.X + chunk.Width - 1,
chunk.Y + chunk.Height - 1,
chunk.Z + chunk.Depth - 1);
}
-
+
///
/// Finds all chunks that are neighbors of a given chunk.
///
@@ -1113,11 +1113,11 @@ namespace AdvChkSys.Spatial
{
if (chunk == null)
throw new ArgumentNullException(nameof(chunk));
-
+
lock (_lock)
{
var result = new HashSet();
-
+
// Check the six adjacent cells (faces)
CheckNeighbor(chunk.X - 1, chunk.Y, chunk.Z, result);
CheckNeighbor(chunk.X + 1, chunk.Y, chunk.Z, result);
@@ -1125,7 +1125,7 @@ namespace AdvChkSys.Spatial
CheckNeighbor(chunk.X, chunk.Y + 1, chunk.Z, result);
CheckNeighbor(chunk.X, chunk.Y, chunk.Z - 1, result);
CheckNeighbor(chunk.X, chunk.Y, chunk.Z + 1, result);
-
+
// Check edge neighbors if requested
if (includeEdges)
{
@@ -1134,20 +1134,20 @@ namespace AdvChkSys.Spatial
CheckNeighbor(chunk.X + 1, chunk.Y - 1, chunk.Z, result);
CheckNeighbor(chunk.X - 1, chunk.Y + 1, chunk.Z, result);
CheckNeighbor(chunk.X + 1, chunk.Y + 1, chunk.Z, result);
-
+
// X-Z edges
CheckNeighbor(chunk.X - 1, chunk.Y, chunk.Z - 1, result);
CheckNeighbor(chunk.X + 1, chunk.Y, chunk.Z - 1, result);
CheckNeighbor(chunk.X - 1, chunk.Y, chunk.Z + 1, result);
CheckNeighbor(chunk.X + 1, chunk.Y, chunk.Z + 1, result);
-
+
// Y-Z edges
CheckNeighbor(chunk.X, chunk.Y - 1, chunk.Z - 1, result);
CheckNeighbor(chunk.X, chunk.Y + 1, chunk.Z - 1, result);
CheckNeighbor(chunk.X, chunk.Y - 1, chunk.Z + 1, result);
CheckNeighbor(chunk.X, chunk.Y + 1, chunk.Z + 1, result);
}
-
+
// Check diagonal corners if requested
if (includeDiagonals)
{
@@ -1160,11 +1160,11 @@ namespace AdvChkSys.Spatial
CheckNeighbor(chunk.X - 1, chunk.Y + 1, chunk.Z + 1, result);
CheckNeighbor(chunk.X + 1, chunk.Y + 1, chunk.Z + 1, result);
}
-
+
return result;
}
}
-
+
///
/// Helper method to check for chunks at a specific grid cell.
///
@@ -1179,7 +1179,7 @@ namespace AdvChkSys.Spatial
}
}
}
-
+
///
/// Performs a spatial operation on chunks in parallel.
///
@@ -1194,22 +1194,22 @@ namespace AdvChkSys.Spatial
{
if (operation == null)
throw new ArgumentNullException(nameof(operation));
-
+
IEnumerable chunksToProcess;
-
+
lock (_lock)
{
- chunksToProcess = filter != null
+ chunksToProcess = filter != null
? _allChunks.Where(filter).ToList()
: _allChunks.ToList();
}
-
+
return ChunkTaskScheduler.RunBatchParallelAsync(
chunksToProcess.Select(chunk => new Action(() => operation(chunk))).ToArray(),
maxDegreeOfParallelism);
}
}
-
+
///
/// Interface for 3D chunks.
///
@@ -1219,7 +1219,7 @@ namespace AdvChkSys.Spatial
/// The chunk's Z position in chunk coordinates.
///
int Z { get; }
-
+
///
/// The depth of the chunk (in cells/tiles/units).
///
diff --git a/src/AdvChkSys/Threading/ChunkAsyncLock.cs b/src/AdvChkSys/Threading/ChunkAsyncLock.cs
index 27c9088..58ac3e1 100644
--- a/src/AdvChkSys/Threading/ChunkAsyncLock.cs
+++ b/src/AdvChkSys/Threading/ChunkAsyncLock.cs
@@ -13,16 +13,16 @@ namespace AdvChkSys.Threading
{
// Lock objects for each chunk
private readonly ConcurrentDictionary _locks = new();
-
+
// Timer for cleanup
private readonly Timer _cleanupTimer;
-
+
// Last access time for each lock
private readonly ConcurrentDictionary _lastAccessTime = new();
-
+
// Cleanup interval in minutes
private readonly int _cleanupIntervalMinutes;
-
+
///
/// Initializes a new instance of the ChunkAsyncLock class.
///
@@ -30,11 +30,11 @@ namespace AdvChkSys.Threading
public ChunkAsyncLock(int cleanupIntervalMinutes = 10)
{
_cleanupIntervalMinutes = cleanupIntervalMinutes;
- _cleanupTimer = new Timer(CleanupUnusedLocks, null,
- TimeSpan.FromMinutes(cleanupIntervalMinutes),
+ _cleanupTimer = new Timer(CleanupUnusedLocks, null,
+ TimeSpan.FromMinutes(cleanupIntervalMinutes),
TimeSpan.FromMinutes(cleanupIntervalMinutes));
}
-
+
///
/// Acquires a lock on a chunk asynchronously.
///
@@ -45,7 +45,7 @@ namespace AdvChkSys.Threading
{
var semaphore = _locks.GetOrAdd(chunk, _ => new SemaphoreSlim(1, 1));
_lastAccessTime[chunk] = DateTime.UtcNow;
-
+
try
{
await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
@@ -60,13 +60,13 @@ namespace AdvChkSys.Threading
_lastAccessTime.TryRemove(chunk, out _);
semaphore.Dispose();
}
-
+
throw;
}
-
+
return new LockReleaser(semaphore, chunk, this);
}
-
+
///
/// Tries to acquire a lock on a chunk asynchronously with a timeout.
///
@@ -75,24 +75,24 @@ namespace AdvChkSys.Threading
/// Cancellation token
/// A tuple with a boolean indicating success and the lock releaser if successful
public async Task<(bool Success, IDisposable? LockReleaser)> TryLockAsync(
- IChunk chunk,
- TimeSpan timeout,
+ IChunk chunk,
+ TimeSpan timeout,
CancellationToken cancellationToken = default)
{
if (chunk == null)
throw new ArgumentNullException(nameof(chunk));
-
+
var semaphore = _locks.GetOrAdd(chunk, _ => new SemaphoreSlim(1, 1));
_lastAccessTime[chunk] = DateTime.UtcNow;
-
+
if (await semaphore.WaitAsync(timeout, cancellationToken).ConfigureAwait(false))
{
return (true, new LockReleaser(semaphore, chunk, this));
}
-
+
return (false, null);
}
-
+
///
/// Cleans up unused locks.
///
@@ -100,10 +100,10 @@ namespace AdvChkSys.Threading
{
var now = DateTime.UtcNow;
var threshold = now.AddMinutes(-_cleanupIntervalMinutes);
-
+
foreach (var chunk in _lastAccessTime.Keys)
{
- if (_lastAccessTime.TryGetValue(chunk, out var lastAccess) &&
+ if (_lastAccessTime.TryGetValue(chunk, out var lastAccess) &&
lastAccess < threshold &&
_locks.TryGetValue(chunk, out var semaphore))
{
@@ -119,23 +119,23 @@ namespace AdvChkSys.Threading
}
}
}
-
+
///
/// Disposes resources.
///
public void Dispose()
{
_cleanupTimer.Dispose();
-
+
foreach (var semaphore in _locks.Values)
{
semaphore.Dispose();
}
-
+
_locks.Clear();
_lastAccessTime.Clear();
}
-
+
///
/// Releases a lock when disposed.
///
@@ -145,14 +145,14 @@ namespace AdvChkSys.Threading
private readonly IChunk _chunk;
private readonly ChunkAsyncLock _parent;
private bool _disposed;
-
+
public LockReleaser(SemaphoreSlim semaphore, IChunk chunk, ChunkAsyncLock parent)
{
_semaphore = semaphore;
_chunk = chunk;
_parent = parent;
}
-
+
public void Dispose()
{
if (!_disposed)
diff --git a/src/AdvChkSys/Threading/ChunkOperationQueue.cs b/src/AdvChkSys/Threading/ChunkOperationQueue.cs
index 0a54717..601735c 100644
--- a/src/AdvChkSys/Threading/ChunkOperationQueue.cs
+++ b/src/AdvChkSys/Threading/ChunkOperationQueue.cs
@@ -14,16 +14,16 @@ namespace AdvChkSys.Threading
{
// Queue of pending operations for each chunk
private readonly ConcurrentDictionary Operation, TaskCompletionSource