fixed code formatter.

minor bug fixes to a few systems as well.
This commit is contained in:
Stan44 2025-05-11 22:47:43 -05:00
parent 657e19f48f
commit 01d2cbf4b3
25 changed files with 914 additions and 912 deletions

View File

@ -27,19 +27,19 @@ namespace AdvChkSys
/// <summary> /// <summary>
/// Creates a new WorldConstraints object. /// Creates a new WorldConstraints object.
/// </summary> /// </summary>
public static WorldConstraints CreateDefaultConstraints() => new WorldConstraints(); public static WorldConstraints CreateDefaultConstraints() => new();
/// <summary> /// <summary>
/// Creates a new 2D chunk manager with optional constraints. /// Creates a new 2D chunk manager with optional constraints.
/// </summary> /// </summary>
public static ChunkManager2D<byte> Create2DManager(WorldConstraints? constraints = null) => public static ChunkManager2D<byte> Create2DManager(WorldConstraints? constraints = null) =>
new ChunkManager2D<byte>(constraints); new(constraints);
/// <summary> /// <summary>
/// Creates a new 3D chunk manager with optional constraints. /// Creates a new 3D chunk manager with optional constraints.
/// </summary> /// </summary>
public static ChunkManager3D<byte> Create3DManager(WorldConstraints? constraints = null) => public static ChunkManager3D<byte> Create3DManager(WorldConstraints? constraints = null) =>
new ChunkManager3D<byte>(constraints); new(constraints);
/// <summary> /// <summary>
/// Gets a detailed report of the current memory usage by the chunk system. /// Gets a detailed report of the current memory usage by the chunk system.
@ -73,13 +73,13 @@ namespace AdvChkSys
/// Creates a spatial index for 2D chunks. /// Creates a spatial index for 2D chunks.
/// </summary> /// </summary>
public static SpatialChunkIndex<Chunk2D<T>> CreateSpatialIndex2D<T>() => public static SpatialChunkIndex<Chunk2D<T>> CreateSpatialIndex2D<T>() =>
new SpatialChunkIndex<Chunk2D<T>>(); new();
/// <summary> /// <summary>
/// Creates a spatial index for 3D chunks. /// Creates a spatial index for 3D chunks.
/// </summary> /// </summary>
public static SpatialChunkIndex<Chunk3D<T>> CreateSpatialIndex3D<T>() => public static SpatialChunkIndex<Chunk3D<T>> CreateSpatialIndex3D<T>() =>
new SpatialChunkIndex<Chunk3D<T>>(); new();
/// <summary> /// <summary>
/// Configures the threading system for high throughput. /// Configures the threading system for high throughput.

View File

@ -91,13 +91,13 @@ namespace AdvChkSys.Chunk
get get
{ {
if (localX < 0 || localX >= Width || localY < 0 || localY >= Height) 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]; return _isAllAir ? default! : _data![localX, localY];
} }
set set
{ {
if (localX < 0 || localX >= Width || localY < 0 || localY >= Height) 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) if (_isAllAir)
throw new InvalidOperationException("Cannot set cell in all-air chunk."); throw new InvalidOperationException("Cannot set cell in all-air chunk.");
_data![localX, localY] = value; _data![localX, localY] = value;

View File

@ -6,210 +6,210 @@ using AdvChkSys.Interfaces;
namespace AdvChkSys.Chunk namespace AdvChkSys.Chunk
{ {
/// <summary>
/// Represents a 3D chunk of data in the world.
/// Perspective-agnostic and supports arbitrary data types and metadata.
/// </summary>
public class Chunk3D<T> : IChunk, IDisposable
{
// Array pool for chunk data arrays
private static readonly ConcurrentBag<T[,,]> _arrayPool = new();
// Flyweight: one all-air instance per size
private static readonly Dictionary<(int, int, int), Chunk3D<T>> _allAirChunks = new();
/// <summary> /// <summary>
/// Represents a 3D chunk of data in the world. /// Returns a singleton all-air chunk for the given size and position.
/// Perspective-agnostic and supports arbitrary data types and metadata.
/// </summary> /// </summary>
public class Chunk3D<T> : IChunk, IDisposable public static Chunk3D<T> AllAir(int x, int y, int z, int width, int height, int depth)
{ {
// Array pool for chunk data arrays var key = (width, height, depth);
private static readonly ConcurrentBag<T[,,]> _arrayPool = new(); if (!_allAirChunks.TryGetValue(key, out var chunk))
// Flyweight: one all-air instance per size
private static readonly Dictionary<(int, int, int), Chunk3D<T>> _allAirChunks = new();
/// <summary>
/// Returns a singleton all-air chunk for the given size and position.
/// </summary>
public static Chunk3D<T> AllAir(int x, int y, int z, int width, int height, int depth)
{ {
var key = (width, height, depth); chunk = new Chunk3D<T>(x, y, z, width, height, depth, isAllAir: true);
if (!_allAirChunks.TryGetValue(key, out var chunk)) _allAirChunks[key] = chunk;
{
chunk = new Chunk3D<T>(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.SetPosition(x, y, z); // Use explicit method for position update
return chunk;
}
/// <summary> /// <summary>
/// The chunk's X position in chunk coordinates. /// The chunk's X position in chunk coordinates.
/// </summary> /// </summary>
public int X { get; private set; } public int X { get; private set; }
/// <summary> /// <summary>
/// The chunk's Y position in chunk coordinates. /// The chunk's Y position in chunk coordinates.
/// </summary> /// </summary>
public int Y { get; private set; } public int Y { get; private set; }
/// <summary> /// <summary>
/// The chunk's Z position in chunk coordinates. /// The chunk's Z position in chunk coordinates.
/// </summary> /// </summary>
public int Z { get; private set; } public int Z { get; private set; }
/// <summary> /// <summary>
/// The width of the chunk in cells. /// The width of the chunk in cells.
/// </summary> /// </summary>
public int Width { get; } public int Width { get; }
/// <summary> /// <summary>
/// The height of the chunk in cells. /// The height of the chunk in cells.
/// </summary> /// </summary>
public int Height { get; } public int Height { get; }
/// <summary> /// <summary>
/// The depth of the chunk in cells. /// The depth of the chunk in cells.
/// </summary> /// </summary>
public int Depth { get; } public int Depth { get; }
/// <summary> /// <summary>
/// The chunk's data array. /// The chunk's data array.
/// </summary> /// </summary>
private readonly T[,,]? _data; private readonly T[,,]? _data;
/// <summary> /// <summary>
/// Metadata dictionary for arbitrary chunk information (e.g., biome, tags). /// Metadata dictionary for arbitrary chunk information (e.g., biome, tags).
/// </summary> /// </summary>
public Dictionary<string, object> Metadata { get; } public Dictionary<string, object> Metadata { get; }
/// <summary> /// <summary>
/// Returns true if this chunk is the all-air singleton. /// Returns true if this chunk is the all-air singleton.
/// </summary> /// </summary>
public bool IsAllAir { get; private set; } = false; public bool IsAllAir { get; private set; } = false;
/// <summary> /// <summary>
/// Tracks whether the chunk has been disposed. /// Tracks whether the chunk has been disposed.
/// </summary> /// </summary>
private bool _disposed; private bool _disposed;
/// <summary> /// <summary>
/// Creates a new 3D chunk at the specified position with the given dimensions. /// Creates a new 3D chunk at the specified position with the given dimensions.
/// </summary> /// </summary>
/// <param name="x">X coordinate in chunk space</param> /// <param name="x">X coordinate in chunk space</param>
/// <param name="y">Y coordinate in chunk space</param> /// <param name="y">Y coordinate in chunk space</param>
/// <param name="z">Z coordinate in chunk space</param> /// <param name="z">Z coordinate in chunk space</param>
/// <param name="width">Width of the chunk in cells</param> /// <param name="width">Width of the chunk in cells</param>
/// <param name="height">Height of the chunk in cells</param> /// <param name="height">Height of the chunk in cells</param>
/// <param name="depth">Depth of the chunk in cells</param> /// <param name="depth">Depth of the chunk in cells</param>
public Chunk3D(int x, int y, int z, int width, int height, int depth) public Chunk3D(int x, int y, int z, int width, int height, int depth)
: this(x, y, z, width, height, depth, false) : this(x, y, z, width, height, depth, false)
{ {
} }
/// <summary> /// <summary>
/// Creates a new 3D chunk at the specified position with the given dimensions. /// Creates a new 3D chunk at the specified position with the given dimensions.
/// </summary> /// </summary>
/// <param name="x">X coordinate in chunk space</param> /// <param name="x">X coordinate in chunk space</param>
/// <param name="y">Y coordinate in chunk space</param> /// <param name="y">Y coordinate in chunk space</param>
/// <param name="z">Z coordinate in chunk space</param> /// <param name="z">Z coordinate in chunk space</param>
/// <param name="width">Width of the chunk in cells</param> /// <param name="width">Width of the chunk in cells</param>
/// <param name="height">Height of the chunk in cells</param> /// <param name="height">Height of the chunk in cells</param>
/// <param name="depth">Depth of the chunk in cells</param> /// <param name="depth">Depth of the chunk in cells</param>
/// <param name="isAllAir">Whether this is an all-air chunk</param> /// <param name="isAllAir">Whether this is an all-air chunk</param>
public Chunk3D(int x, int y, int z, int width, int height, int depth, bool isAllAir = false) public Chunk3D(int x, int y, int z, int width, int height, int depth, bool isAllAir = false)
{ {
X = x; X = x;
Y = y; Y = y;
Z = z; Z = z;
Width = width; Width = width;
Height = height; Height = height;
Depth = depth; Depth = depth;
IsAllAir = isAllAir; IsAllAir = isAllAir;
_data = isAllAir ? null : RentArray(width, height, depth); _data = isAllAir ? null : RentArray(width, height, depth);
Metadata = new Dictionary<string, object>(); Metadata = new Dictionary<string, object>();
} }
/// <summary> /// <summary>
/// Explicitly sets the chunk's position. Used internally for all-air singleton. /// Explicitly sets the chunk's position. Used internally for all-air singleton.
/// </summary> /// </summary>
internal void SetPosition(int x, int y, int z) internal void SetPosition(int x, int y, int z)
{ {
X = x; X = x;
Y = y; Y = y;
Z = z; Z = z;
} }
/// <summary> /// <summary>
/// Gets or sets the value at the given local chunk coordinates. /// Gets or sets the value at the given local chunk coordinates.
/// </summary> /// </summary>
public T this[int localX, int localY, int localZ] public T this[int localX, int localY, int localZ]
{ {
get 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;
}
}
/// <summary>
/// Fills the chunk with a specified value.
/// </summary>
public void Fill(T value)
{ {
if (IsAllAir) if (IsAllAir)
throw new InvalidOperationException("Cannot fill an all-air chunk."); return default!;
return _data![localX, localY, localZ];
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;
} }
set
/// <summary>
/// Rents an array from the pool or creates a new one.
/// </summary>
private static T[,,] RentArray(int width, int height, int depth)
{ {
if (_arrayPool.TryTake(out var arr) && if (IsAllAir)
arr.GetLength(0) == width && throw new InvalidOperationException("Cannot modify an all-air chunk.");
arr.GetLength(1) == height && _data![localX, localY, localZ] = value;
arr.GetLength(2) == depth)
return arr;
return new T[width, height, depth];
}
/// <summary>
/// Returns an array to the pool.
/// </summary>
internal static void ReturnArray(T[,,] arr)
{
_arrayPool.Add(arr);
}
/// <summary>
/// Returns the underlying data array (for pooling).
/// </summary>
internal T[,,]? GetDataArray() => _data;
/// <summary>
/// Releases the data array back to the pool.
/// </summary>
internal void ReleaseDataArray()
{
if (_data != null && !IsAllAir)
{
ReturnArray(_data);
}
}
/// <summary>
/// Disposes the chunk and returns its resources to the pool.
/// </summary>
public void Dispose()
{
if (!_disposed)
{
ReleaseDataArray();
_disposed = true;
}
} }
} }
/// <summary>
/// Fills the chunk with a specified value.
/// </summary>
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;
}
/// <summary>
/// Rents an array from the pool or creates a new one.
/// </summary>
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];
}
/// <summary>
/// Returns an array to the pool.
/// </summary>
internal static void ReturnArray(T[,,] arr)
{
_arrayPool.Add(arr);
}
/// <summary>
/// Returns the underlying data array (for pooling).
/// </summary>
internal T[,,]? GetDataArray() => _data;
/// <summary>
/// Releases the data array back to the pool.
/// </summary>
internal void ReleaseDataArray()
{
if (_data != null && !IsAllAir)
{
ReturnArray(_data);
}
}
/// <summary>
/// Disposes the chunk and returns its resources to the pool.
/// </summary>
public void Dispose()
{
if (!_disposed)
{
ReleaseDataArray();
_disposed = true;
}
}
}
} }

View File

@ -1,67 +1,67 @@
using System; using System;
namespace AdvChkSys.Constraints namespace AdvChkSys.Constraints
{
/// <summary>
/// Represents constraints and limits for the chunk world.
/// Can be used to restrict world size, chunk counts, and other resource limits.
/// </summary>
public class WorldConstraints
{ {
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
public int? MinChunkX { get; set; } public class WorldConstraints
/// <summary>
/// If set, the maximum allowed chunk X coordinate (inclusive).
/// </summary>
public int? MaxChunkX { get; set; }
/// <summary>
/// If set, the minimum allowed chunk Y coordinate (inclusive).
/// </summary>
public int? MinChunkY { get; set; }
/// <summary>
/// If set, the maximum allowed chunk Y coordinate (inclusive).
/// </summary>
public int? MaxChunkY { get; set; }
/// <summary>
/// If set, the minimum allowed chunk Z coordinate (inclusive).
/// </summary>
public int? MinChunkZ { get; set; }
/// <summary>
/// If set, the maximum allowed chunk Z coordinate (inclusive).
/// </summary>
public int? MaxChunkZ { get; set; }
/// <summary>
/// If set, the maximum number of chunks allowed to be loaded in memory at once.
/// </summary>
public int? MaxLoadedChunks { get; set; }
/// <summary>
/// Checks if the given chunk coordinates are within the allowed world bounds.
/// </summary>
public bool IsWithinBounds(int chunkX, int chunkY)
{ {
if (MinChunkX.HasValue && chunkX < MinChunkX.Value) return false; /// <summary>
if (MaxChunkX.HasValue && chunkX > MaxChunkX.Value) return false; /// If set, the minimum allowed chunk X coordinate (inclusive).
if (MinChunkY.HasValue && chunkY < MinChunkY.Value) return false; /// </summary>
if (MaxChunkY.HasValue && chunkY > MaxChunkY.Value) return false; public int? MinChunkX { get; set; }
return true;
}
/// <summary> /// <summary>
/// Checks if the current number of loaded chunks is within the allowed limit. /// If set, the maximum allowed chunk X coordinate (inclusive).
/// </summary> /// </summary>
public bool IsWithinChunkLimit(int loadedChunkCount) public int? MaxChunkX { get; set; }
{
if (MaxLoadedChunks.HasValue && loadedChunkCount > MaxLoadedChunks.Value) return false; /// <summary>
return true; /// If set, the minimum allowed chunk Y coordinate (inclusive).
/// </summary>
public int? MinChunkY { get; set; }
/// <summary>
/// If set, the maximum allowed chunk Y coordinate (inclusive).
/// </summary>
public int? MaxChunkY { get; set; }
/// <summary>
/// If set, the minimum allowed chunk Z coordinate (inclusive).
/// </summary>
public int? MinChunkZ { get; set; }
/// <summary>
/// If set, the maximum allowed chunk Z coordinate (inclusive).
/// </summary>
public int? MaxChunkZ { get; set; }
/// <summary>
/// If set, the maximum number of chunks allowed to be loaded in memory at once.
/// </summary>
public int? MaxLoadedChunks { get; set; }
/// <summary>
/// Checks if the given chunk coordinates are within the allowed world bounds.
/// </summary>
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;
}
/// <summary>
/// Checks if the current number of loaded chunks is within the allowed limit.
/// </summary>
public bool IsWithinChunkLimit(int loadedChunkCount)
{
if (MaxLoadedChunks.HasValue && loadedChunkCount > MaxLoadedChunks.Value) return false;
return true;
}
} }
} }
}

View File

@ -51,8 +51,10 @@ namespace AdvChkSys.Diagnostics
{ {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
var memStatus = new MemoryHelper.MEMORYSTATUSEX(); var memStatus = new MemoryHelper.MEMORYSTATUSEX
memStatus.dwLength = (uint)Marshal.SizeOf(typeof(MemoryHelper.MEMORYSTATUSEX)); {
dwLength = (uint)Marshal.SizeOf(typeof(MemoryHelper.MEMORYSTATUSEX))
};
if (MemoryHelper.GlobalMemoryStatusEx(ref memStatus)) if (MemoryHelper.GlobalMemoryStatusEx(ref memStatus))
{ {
return memStatus.ullTotalPhys; return memStatus.ullTotalPhys;

View File

@ -4,123 +4,123 @@ using System.Threading;
namespace AdvChkSys.Events namespace AdvChkSys.Events
{ {
/// <summary>
/// Provides events for chunk lifecycle operations such as loading, unloading, saving, and more.
/// Thread-safe event subscription and invocation.
/// </summary>
public static class ChunkEvents
{
// Backing fields for thread-safe event handling
private static Action<Interfaces.IChunk>? _chunkLoaded;
private static Action<Interfaces.IChunk>? _chunkUnloaded;
private static Action<Interfaces.IChunk>? _chunkLoading;
private static Action<Interfaces.IChunk>? _chunkUnloading;
private static Action<Interfaces.IChunk>? _chunkSaving;
private static Action<Interfaces.IChunk>? _chunkSaved;
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
public static event Action<Interfaces.IChunk> ChunkLoaded public static class ChunkEvents
{ {
add => AddHandler(ref _chunkLoaded, value); // Backing fields for thread-safe event handling
remove => RemoveHandler(ref _chunkLoaded, value); private static Action<Interfaces.IChunk>? _chunkLoaded;
} private static Action<Interfaces.IChunk>? _chunkUnloaded;
private static Action<Interfaces.IChunk>? _chunkLoading;
private static Action<Interfaces.IChunk>? _chunkUnloading;
private static Action<Interfaces.IChunk>? _chunkSaving;
private static Action<Interfaces.IChunk>? _chunkSaved;
/// <summary> /// <summary>
/// Occurs when a chunk is about to be loaded into memory. /// Occurs when a chunk has been loaded into memory.
/// </summary> /// </summary>
public static event Action<Interfaces.IChunk> ChunkLoading public static event Action<Interfaces.IChunk> ChunkLoaded
{
add => AddHandler(ref _chunkLoading, value);
remove => RemoveHandler(ref _chunkLoading, value);
}
/// <summary>
/// Occurs when a chunk is about to be unloaded from memory.
/// </summary>
public static event Action<Interfaces.IChunk> ChunkUnloading
{
add => AddHandler(ref _chunkUnloading, value);
remove => RemoveHandler(ref _chunkUnloading, value);
}
/// <summary>
/// Occurs when a chunk has been unloaded from memory.
/// </summary>
public static event Action<Interfaces.IChunk> ChunkUnloaded
{
add => AddHandler(ref _chunkUnloaded, value);
remove => RemoveHandler(ref _chunkUnloaded, value);
}
/// <summary>
/// Occurs when a chunk is about to be saved.
/// </summary>
public static event Action<Interfaces.IChunk> ChunkSaving
{
add => AddHandler(ref _chunkSaving, value);
remove => RemoveHandler(ref _chunkSaving, value);
}
/// <summary>
/// Occurs when a chunk has been saved.
/// </summary>
public static event Action<Interfaces.IChunk> ChunkSaved
{
add => AddHandler(ref _chunkSaved, value);
remove => RemoveHandler(ref _chunkSaved, value);
}
/// <summary>
/// Raises the ChunkLoading event.
/// </summary>
public static void OnChunkLoading(Interfaces.IChunk chunk) => _chunkLoading?.Invoke(chunk);
/// <summary>
/// Raises the ChunkLoaded event.
/// </summary>
public static void OnChunkLoaded(Interfaces.IChunk chunk) => _chunkLoaded?.Invoke(chunk);
/// <summary>
/// Raises the ChunkUnloading event.
/// </summary>
public static void OnChunkUnloading(Interfaces.IChunk chunk) => _chunkUnloading?.Invoke(chunk);
/// <summary>
/// Raises the ChunkUnloaded event.
/// </summary>
public static void OnChunkUnloaded(Interfaces.IChunk chunk) => _chunkUnloaded?.Invoke(chunk);
/// <summary>
/// Raises the ChunkSaving event.
/// </summary>
public static void OnChunkSaving(Interfaces.IChunk chunk) => _chunkSaving?.Invoke(chunk);
/// <summary>
/// Raises the ChunkSaved event.
/// </summary>
public static void OnChunkSaved(Interfaces.IChunk chunk) => _chunkSaved?.Invoke(chunk);
// Thread-safe add/remove for event handlers
private static void AddHandler(ref Action<Interfaces.IChunk>? field, Action<Interfaces.IChunk> handler)
{
Action<Interfaces.IChunk>? prevHandler, newHandler;
do
{ {
prevHandler = field; add => AddHandler(ref _chunkLoaded, value);
newHandler = (Action<Interfaces.IChunk>?)Delegate.Combine(prevHandler, handler); remove => RemoveHandler(ref _chunkLoaded, value);
} while (Interlocked.CompareExchange(ref field, newHandler, prevHandler) != prevHandler); }
}
private static void RemoveHandler(ref Action<Interfaces.IChunk>? field, Action<Interfaces.IChunk> handler) /// <summary>
{ /// Occurs when a chunk is about to be loaded into memory.
Action<Interfaces.IChunk>? prevHandler, newHandler; /// </summary>
do public static event Action<Interfaces.IChunk> ChunkLoading
{ {
prevHandler = field; add => AddHandler(ref _chunkLoading, value);
newHandler = (Action<Interfaces.IChunk>?)Delegate.Remove(prevHandler, handler); remove => RemoveHandler(ref _chunkLoading, value);
} while (Interlocked.CompareExchange(ref field, newHandler, prevHandler) != prevHandler); }
/// <summary>
/// Occurs when a chunk is about to be unloaded from memory.
/// </summary>
public static event Action<Interfaces.IChunk> ChunkUnloading
{
add => AddHandler(ref _chunkUnloading, value);
remove => RemoveHandler(ref _chunkUnloading, value);
}
/// <summary>
/// Occurs when a chunk has been unloaded from memory.
/// </summary>
public static event Action<Interfaces.IChunk> ChunkUnloaded
{
add => AddHandler(ref _chunkUnloaded, value);
remove => RemoveHandler(ref _chunkUnloaded, value);
}
/// <summary>
/// Occurs when a chunk is about to be saved.
/// </summary>
public static event Action<Interfaces.IChunk> ChunkSaving
{
add => AddHandler(ref _chunkSaving, value);
remove => RemoveHandler(ref _chunkSaving, value);
}
/// <summary>
/// Occurs when a chunk has been saved.
/// </summary>
public static event Action<Interfaces.IChunk> ChunkSaved
{
add => AddHandler(ref _chunkSaved, value);
remove => RemoveHandler(ref _chunkSaved, value);
}
/// <summary>
/// Raises the ChunkLoading event.
/// </summary>
public static void OnChunkLoading(Interfaces.IChunk chunk) => _chunkLoading?.Invoke(chunk);
/// <summary>
/// Raises the ChunkLoaded event.
/// </summary>
public static void OnChunkLoaded(Interfaces.IChunk chunk) => _chunkLoaded?.Invoke(chunk);
/// <summary>
/// Raises the ChunkUnloading event.
/// </summary>
public static void OnChunkUnloading(Interfaces.IChunk chunk) => _chunkUnloading?.Invoke(chunk);
/// <summary>
/// Raises the ChunkUnloaded event.
/// </summary>
public static void OnChunkUnloaded(Interfaces.IChunk chunk) => _chunkUnloaded?.Invoke(chunk);
/// <summary>
/// Raises the ChunkSaving event.
/// </summary>
public static void OnChunkSaving(Interfaces.IChunk chunk) => _chunkSaving?.Invoke(chunk);
/// <summary>
/// Raises the ChunkSaved event.
/// </summary>
public static void OnChunkSaved(Interfaces.IChunk chunk) => _chunkSaved?.Invoke(chunk);
// Thread-safe add/remove for event handlers
private static void AddHandler(ref Action<Interfaces.IChunk>? field, Action<Interfaces.IChunk> handler)
{
Action<Interfaces.IChunk>? prevHandler, newHandler;
do
{
prevHandler = field;
newHandler = (Action<Interfaces.IChunk>?)Delegate.Combine(prevHandler, handler);
} while (Interlocked.CompareExchange(ref field, newHandler, prevHandler) != prevHandler);
}
private static void RemoveHandler(ref Action<Interfaces.IChunk>? field, Action<Interfaces.IChunk> handler)
{
Action<Interfaces.IChunk>? prevHandler, newHandler;
do
{
prevHandler = field;
newHandler = (Action<Interfaces.IChunk>?)Delegate.Remove(prevHandler, handler);
} while (Interlocked.CompareExchange(ref field, newHandler, prevHandler) != prevHandler);
}
} }
} }
}

View File

@ -2,37 +2,37 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace AdvChkSys.Interfaces namespace AdvChkSys.Interfaces
{
/// <summary>
/// Interface for managing chunks in memory.
/// Provides methods for loading, unloading, and accessing chunks.
/// </summary>
public interface IChunkManager
{ {
/// <summary> /// <summary>
/// Returns true if the chunk at (x, y) is loaded. /// Interface for managing chunks in memory.
/// Provides methods for loading, unloading, and accessing chunks.
/// </summary> /// </summary>
bool IsChunkLoaded(int x, int y); public interface IChunkManager
{
/// <summary>
/// Returns true if the chunk at (x, y) is loaded.
/// </summary>
bool IsChunkLoaded(int x, int y);
/// <summary> /// <summary>
/// Gets the chunk at (x, y) if loaded, or null if not. /// Gets the chunk at (x, y) if loaded, or null if not.
/// </summary> /// </summary>
IChunk? GetChunk(int x, int y); IChunk? GetChunk(int x, int y);
/// <summary> /// <summary>
/// Loads or creates a chunk at (x, y) with the given size. /// Loads or creates a chunk at (x, y) with the given size.
/// Returns the loaded or newly created chunk. /// Returns the loaded or newly created chunk.
/// </summary> /// </summary>
IChunk LoadOrCreateChunk(int x, int y, int width, int height); IChunk LoadOrCreateChunk(int x, int y, int width, int height);
/// <summary> /// <summary>
/// Unloads (removes) the chunk at (x, y) if loaded. /// Unloads (removes) the chunk at (x, y) if loaded.
/// </summary> /// </summary>
bool UnloadChunk(int x, int y); bool UnloadChunk(int x, int y);
/// <summary> /// <summary>
/// Enumerates all loaded chunks. /// Enumerates all loaded chunks.
/// </summary> /// </summary>
IEnumerable<IChunk> GetAllChunks(); IEnumerable<IChunk> GetAllChunks();
} }
} }

View File

@ -20,7 +20,7 @@ namespace AdvChkSys.Manager
private readonly WorldConstraints? _constraints; private readonly WorldConstraints? _constraints;
private readonly int _capacity; private readonly int _capacity;
private readonly Dictionary<(int, int), Task<Chunk2D<T>>> _loadingChunks = new(); private readonly Dictionary<(int, int), Task<Chunk2D<T>>> _loadingChunks = new();
private readonly object _lock = new object(); private readonly object _lock = new();
/// <summary> /// <summary>
/// Delegate for custom air check logic /// Delegate for custom air check logic

View File

@ -6,67 +6,67 @@ using System.Collections.Generic;
namespace AdvChkSys.Resources namespace AdvChkSys.Resources
{ {
/// <summary>
/// Manages allocation and release of chunk resources in memory.
/// Can be extended to track resource usage, pooling, or implement custom memory strategies.
/// </summary>
public static class ChunkResourceManager
{
// Example: Track allocated chunks (for diagnostics, pooling, or resource limits)
private static readonly ConcurrentDictionary<IChunk, DateTime> _allocatedChunks = new ConcurrentDictionary<IChunk, DateTime>();
private static int _allocatedChunkCount;
private static readonly object _lock = new object();
private static readonly HashSet<IChunk> _activeChunks = new();
/// <summary> /// <summary>
/// Called when a chunk is allocated/loaded into memory. /// Manages allocation and release of chunk resources in memory.
/// Tracks the chunk and can be extended for pooling or resource limits. /// Can be extended to track resource usage, pooling, or implement custom memory strategies.
/// </summary> /// </summary>
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<IChunk, DateTime> _allocatedChunks = new();
private static int _allocatedChunkCount;
private static readonly object _lock = new();
private static readonly HashSet<IChunk> _activeChunks = new();
/// <summary>
/// Called when a chunk is allocated/loaded into memory.
/// Tracks the chunk and can be extended for pooling or resource limits.
/// </summary>
public static void AllocateChunk(IChunk chunk)
{ {
_allocatedChunkCount++; lock (_lock)
_activeChunks.Add(chunk); {
_allocatedChunkCount++;
_activeChunks.Add(chunk);
}
} }
}
/// <summary> /// <summary>
/// Called when a chunk is released/unloaded from memory. /// Called when a chunk is released/unloaded from memory.
/// Removes the chunk from tracking and can be extended for pooling or cleanup. /// Removes the chunk from tracking and can be extended for pooling or cleanup.
/// </summary> /// </summary>
public static void ReleaseChunk(IChunk chunk) public static void ReleaseChunk(IChunk chunk)
{
lock (_lock)
{ {
_allocatedChunkCount--; lock (_lock)
_activeChunks.Remove(chunk); {
_allocatedChunkCount--;
_activeChunks.Remove(chunk);
}
} }
}
/// <summary> /// <summary>
/// Gets the current number of allocated chunks. /// Gets the current number of allocated chunks.
/// </summary> /// </summary>
public static int AllocatedChunkCount => _allocatedChunks.Count; public static int AllocatedChunkCount => _allocatedChunks.Count;
/// <summary> /// <summary>
/// Clears all tracked chunks (for diagnostics or shutdown). /// Clears all tracked chunks (for diagnostics or shutdown).
/// </summary> /// </summary>
public static void Clear() public static void Clear()
{
_allocatedChunks.Clear();
}
/// <summary>
/// Gets the current count of active chunks being tracked by the resource manager.
/// </summary>
/// <returns>The number of active chunks</returns>
public static int GetActiveChunkCount()
{
lock (_lock)
{ {
return _activeChunks.Count; _allocatedChunks.Clear();
}
/// <summary>
/// Gets the current count of active chunks being tracked by the resource manager.
/// </summary>
/// <returns>The number of active chunks</returns>
public static int GetActiveChunkCount()
{
lock (_lock)
{
return _activeChunks.Count;
}
} }
} }
} }
}

View File

@ -221,8 +221,8 @@ namespace AdvChkSys.Threading
// Cancel all pending operations // Cancel all pending operations
while (queue.Count > 0) while (queue.Count > 0)
{ {
var operation = queue.Dequeue(); var (Operation, Completion) = queue.Dequeue();
operation.Completion.TrySetCanceled(); Completion.TrySetCanceled();
} }
return count; return count;

View File

@ -16,7 +16,7 @@ namespace AdvChkSys.Threading
private static bool _currentThreadIsProcessingItems; private static bool _currentThreadIsProcessingItems;
// The list of tasks to be executed // The list of tasks to be executed
private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); private readonly LinkedList<Task> _tasks = new();
// The maximum concurrency level allowed by this scheduler. // The maximum concurrency level allowed by this scheduler.
private int _maximumConcurrencyLevel; private int _maximumConcurrencyLevel;

View File

@ -115,8 +115,8 @@ namespace AdvChkSys.Util
{ {
lock (_lock) lock (_lock)
{ {
foreach (var node in _lruList) foreach (var (key, value) in _lruList)
yield return node.value; yield return value;
} }
} }
} }