first commit

This commit is contained in:
Stan44 2025-05-09 20:04:37 -05:00
commit f1b3868d2c
21 changed files with 1871 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
# Ignore Build/Benchmarks.
/src/AdvChkSys.Benchmarks
/docfx_project
/src/AdvChkSys/bin
/src/AdvChkSys/obj
src/bindings/python/__pycache__/

126
README.md Normal file
View File

@ -0,0 +1,126 @@
# AdvChkSys
**AdvChkSys** is a high-performance, extensible chunked world management library for .NET, designed for games, simulations, and voxel engines. It provides efficient memory management, chunk loading/unloading, resource tracking, event hooks, serialization, and optional constraints for 2D and 3D chunk-based worlds.
---
## Features
- **2D and 3D Chunk Management**
Efficient, thread-safe managers for 2D and 3D chunks with support for custom data types.
- **Memory Efficiency**
LRU caching, array pooling, and all-air chunk singletons minimize memory usage and maximize performance.
- **Resource Tracking**
Built-in resource manager tracks allocated chunks and supports diagnostics and pooling.
- **Event System**
Thread-safe events for chunk lifecycle: loading, unloading, saving, and more.
- **World Constraints**
Optional constraints for world size and loaded chunk limits.
- **Serialization**
Fast binary serialization/deserialization for chunk data.
- **Async Utilities**
Helpers for running chunk tasks asynchronously.
- **Python/.NET Interop**
Python bindings for scripting and integration.
---
## Getting Started
### Requirements
- .NET Standard 2.1 or later
### Building
```bash
dotnet build src/AdvChkSys/AdvChkSys.csproj
```
### Basic Usage
```csharp
using AdvChkSys;
using AdvChkSys.Manager;
// Create a 2D chunk manager
var manager2D = AdvChkSys.AdvChkSys.Create2DManager();
// Create or load a chunk
var chunk = manager2D.LoadOrCreateChunk(0, 0, 32, 32);
// Set a cell value
chunk[0, 0] = 42;
// Unload a chunk
manager2D.UnloadChunk(0, 0);
```
---
## Python Bindings
Python bindings are available in `src/bindings/python/`.
See [`src/bindings/python/README.md`](src/bindings/python/README.md) for usage.
---
## Project Structure
```
advchksys/
src/
AdvChkSys/
Chunk/
Manager/
Constraints/
Events/
Interfaces/
Resources/
Serialization/
Threading/
Util/
AdvChkSys.csproj
bindings/
python/
godot/
tests/
AdvChkSys.Tests/
README.md
LICENSE
```
---
## Documentation
- API documentation can be generated with [DocFX](https://dotnet.github.io/docfx/).
- XML documentation is included in the build.
---
## License
This project is licensed under the MIT License. See [LICENSE](LICENSE) for details.
---
## Acknowledgments
- Inspired by voxel engines and chunked world systems such as Minecraft.
- Uses [DocFX](https://dotnet.github.io/docfx/) for documentation generation.
---
## Contributing
Pull requests and issues are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) if available, or open an issue to discuss your ideas.
---

View File

@ -0,0 +1,79 @@
#nullable enable
using AdvChkSys.Constraints;
using AdvChkSys.Manager;
using AdvChkSys.Chunk;
using AdvChkSys.Resources;
namespace AdvChkSys
{
/// <summary>
/// Entry point and static facade for the AdvChkSys library.
/// Provides version info, helpers for creating managers and constraints, and a basic self-test.
/// </summary>
public static class AdvChkSys
{
/// <summary>
/// The current version of the AdvChkSys library.
/// </summary>
public static string Version => "0.1.0";
/// <summary>
/// Creates a new WorldConstraints object.
/// </summary>
public static WorldConstraints CreateDefaultConstraints() => new WorldConstraints();
/// <summary>
/// Creates a new 2D chunk manager with optional constraints.
/// </summary>
public static ChunkManager2D<byte> Create2DManager(WorldConstraints? constraints = null) =>
new ChunkManager2D<byte>(constraints);
/// <summary>
/// Creates a new 3D chunk manager with optional constraints.
/// </summary>
public static ChunkManager3D<byte> Create3DManager(WorldConstraints? constraints = null) =>
new ChunkManager3D<byte>(constraints);
/// <summary>
/// Performs a basic self-test of the AdvChkSys core functionality.
/// Returns true if all core systems are operational.
/// </summary>
public static bool SelfTest()
{
// Create constraints
var constraints = new WorldConstraints
{
MinChunkX = 0,
MinChunkY = 0,
MaxChunkX = 2,
MaxChunkY = 2,
MaxLoadedChunks = 4
};
// Create managers
var manager2D = new ChunkManager2D<byte>(constraints);
var manager3D = new ChunkManager3D<byte>(constraints);
// Test 2D chunk creation
var chunk2D = manager2D.LoadOrCreateChunk(1, 1, 8, 8);
chunk2D[0, 0] = 42;
// Test 3D chunk creation
var chunk3D = manager3D.LoadOrCreateChunk(1, 1, 1, 4, 4, 4);
chunk3D[0, 0, 0] = 99;
// Check resource manager tracking
bool resourceTracking2D = ChunkResourceManager.AllocatedChunkCount >= 1;
bool resourceTracking3D = ChunkResourceManager.AllocatedChunkCount >= 2;
// Check constraints enforcement
bool constraintsWork = constraints.IsWithinBounds(1, 1) && constraints.IsWithinChunkLimit(2);
// Clean up
manager2D.UnloadChunk(1, 1);
manager3D.UnloadChunk(1, 1, 1);
return resourceTracking2D && resourceTracking3D && constraintsWork;
}
}
}

View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>9.0</LangVersion>
<Nullable>enable</Nullable>
<RootNamespace>AdvChkSys</RootNamespace>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,134 @@
#nullable enable
using System.Collections.Concurrent;
using System.Collections.Generic;
using AdvChkSys.Interfaces;
namespace AdvChkSys.Chunk
{
/// <summary>
/// Represents a 2D chunk of data in the world.
/// Supports all-air singleton for memory efficiency and chunk array pooling.
/// </summary>
public class Chunk2D<T> : IChunk
{
// Flyweight: one all-air instance per size
private static readonly Dictionary<(int, int), Chunk2D<T>> _allAirChunks = new();
// Array pool for chunk data arrays
private static readonly ConcurrentBag<T[,]> _arrayPool = new();
/// <summary>
/// Returns a singleton all-air chunk for the given size and position.
/// </summary>
public static Chunk2D<T> AllAir(int x, int y, int width, int height)
{
var key = (width, height);
if (!_allAirChunks.TryGetValue(key, out var chunk))
{
chunk = new Chunk2D<T>(x, y, width, height, isAllAir: true);
_allAirChunks[key] = chunk;
}
chunk.SetPosition(x, y); // Use explicit method for position update
return chunk;
}
private readonly bool _isAllAir;
private T[,]? _data;
// Properties required by IChunk
public int X { get; private set; }
public int Y { get; private set; }
public int Width { get; }
public int Height { get; }
public Dictionary<string, object> Metadata { get; }
/// <summary>
/// Normal constructor (private for singleton/factory).
/// </summary>
public Chunk2D(int x, int y, int width, int height, bool isAllAir = false)
{
X = x;
Y = y;
Width = width;
Height = height;
_isAllAir = isAllAir;
_data = isAllAir ? null : RentArray(width, height);
Metadata = new Dictionary<string, object>();
}
/// <summary>
/// Explicitly sets the chunk's position. Used internally for all-air singleton.
/// </summary>
internal void SetPosition(int x, int y)
{
X = x;
Y = y;
}
/// <summary>
/// Gets or sets the value at the given local chunk coordinates.
/// </summary>
public T this[int localX, int localY]
{
get => _isAllAir ? default! : _data![localX, localY];
set
{
if (_isAllAir)
throw new System.InvalidOperationException("Cannot set cell in all-air chunk.");
_data![localX, localY] = value;
}
}
/// <summary>
/// Fills the chunk with a specified value (no-op for all-air).
/// </summary>
public void Fill(T value)
{
if (_isAllAir) return;
var data = _data!;
for (int x = 0; x < Width; x++)
for (int y = 0; y < Height; y++)
data[x, y] = value;
}
/// <summary>
/// Returns true if this chunk is the all-air singleton.
/// </summary>
public bool IsAllAir => _isAllAir;
/// <summary>
/// Returns the underlying data array (for pooling).
/// </summary>
internal T[,]? GetDataArray() => _data;
/// <summary>
/// Releases the data array back to the pool (for chunk manager).
/// </summary>
internal void ReleaseDataArray()
{
if (_data != null)
{
ReturnArray(_data);
_data = null;
}
}
/// <summary>
/// Rents an array from the pool or creates a new one.
/// </summary>
private static T[,] RentArray(int width, int height)
{
if (_arrayPool.TryTake(out var arr) && arr.GetLength(0) == width && arr.GetLength(1) == height)
return arr;
return new T[width, height];
}
/// <summary>
/// Returns an array to the pool.
/// </summary>
internal static void ReturnArray(T[,] arr)
{
_arrayPool.Add(arr);
}
}
}

View File

@ -0,0 +1,68 @@
using System.Collections.Generic;
using AdvChkSys.Interfaces;
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
{
/// <summary>
/// The chunk's position in chunk coordinates (not world coordinates).
/// </summary>
public int X { get; }
public int Y { get; }
public int Z { get; }
/// <summary>
/// The width, height, and depth of the chunk (in cells/tiles/units).
/// </summary>
public int Width { get; }
public int Height { get; }
public int Depth { get; }
/// <summary>
/// The chunk's data array.
/// </summary>
private readonly T[,,] _data;
/// <summary>
/// Metadata dictionary for arbitrary chunk information (e.g., biome, tags).
/// </summary>
public Dictionary<string, object> Metadata { get; }
public Chunk3D(int x, int y, int z, int width, int height, int depth)
{
X = x;
Y = y;
Z = z;
Width = width;
Height = height;
Depth = depth;
_data = new T[width, height, depth];
Metadata = new Dictionary<string, object>();
}
/// <summary>
/// Gets or sets the value at the given local chunk coordinates.
/// </summary>
public T this[int localX, int localY, int localZ]
{
get => _data[localX, localY, localZ];
set => _data[localX, localY, localZ] = value;
}
/// <summary>
/// Fills the chunk with a specified value.
/// </summary>
public void Fill(T value)
{
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;
}
}
}

View File

@ -0,0 +1,57 @@
using System;
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>
/// If set, the minimum allowed chunk X coordinate (inclusive).
/// </summary>
public int? MinChunkX { get; set; }
/// <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 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

@ -0,0 +1,126 @@
#nullable enable
using System;
using System.Threading;
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>
/// Occurs when a chunk has been loaded into memory.
/// </summary>
public static event Action<Interfaces.IChunk> ChunkLoaded
{
add => AddHandler(ref _chunkLoaded, value);
remove => RemoveHandler(ref _chunkLoaded, value);
}
/// <summary>
/// Occurs when a chunk is about to be loaded into memory.
/// </summary>
public static event Action<Interfaces.IChunk> ChunkLoading
{
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;
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

@ -0,0 +1,32 @@
using System.Collections.Generic;
namespace AdvChkSys.Interfaces
{
/// <summary>
/// Represents a generic chunk in the world.
/// Provides basic properties for position, size, and metadata.
/// </summary>
public interface IChunk
{
/// <summary>
/// The chunk's position in chunk-space coordinates.
/// </summary>
int X { get; }
int Y { get; }
/// <summary>
/// The width of the chunk (in cells/tiles/units).
/// </summary>
int Width { get; }
/// <summary>
/// The height of the chunk (in cells/tiles/units).
/// </summary>
int Height { get; }
/// <summary>
/// Metadata dictionary for arbitrary chunk information (e.g., biome, tags).
/// </summary>
Dictionary<string, object> Metadata { get; }
}
}

View File

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

View File

@ -0,0 +1,156 @@
#nullable enable
using System.Collections.Generic;
using System.Threading.Tasks;
using AdvChkSys.Chunk;
using AdvChkSys.Events;
using AdvChkSys.Interfaces;
using AdvChkSys.Resources;
using AdvChkSys.Constraints;
using AdvChkSys.Util;
using System;
namespace AdvChkSys.Manager
{
/// <summary>
/// Manages 2D chunks in memory. Handles loading, unloading, and access.
/// </summary>
public class ChunkManager2D<T> : IChunkManager
{
private readonly LRUCache<(int, int), Chunk2D<T>> _chunks;
private readonly WorldConstraints? _constraints;
private readonly int _capacity;
public ChunkManager2D(WorldConstraints? constraints = null, int capacity = 0, int chunkWidth = 32, int chunkHeight = 32)
{
_constraints = constraints;
if (capacity <= 0)
{
// Use dynamic calculation
int bytesPerChunk = chunkWidth * chunkHeight * System.Runtime.InteropServices.Marshal.SizeOf<T>();
ulong availableBytes = MemoryHelper.GetAvailableMemoryBytes();
_capacity = (int)(availableBytes * 0.8 / (ulong)bytesPerChunk);
}
else
{
_capacity = capacity;
}
_chunks = new LRUCache<(int, int), Chunk2D<T>>(_capacity);
}
/// <summary>
/// Returns true if the chunk at (x, y) is loaded.
/// </summary>
public bool IsChunkLoaded(int x, int y) => _chunks.TryGet((x, y), out _);
/// <summary>
/// Gets the chunk at (x, y) if loaded, or null if not.
/// </summary>
public Chunk2D<T>? GetChunk(int x, int y)
{
_chunks.TryGet((x, y), out var chunk);
return chunk;
}
/// <summary>
/// Loads or creates a chunk at (x, y) with the given size.
/// Returns the loaded or newly created chunk.
/// </summary>
public Chunk2D<T> LoadOrCreateChunk(int x, int y, int width, int height)
{
if (_constraints != null)
{
if (!_constraints.IsWithinBounds(x, y))
throw new InvalidOperationException("Chunk coordinates out of bounds.");
if (!_constraints.IsWithinChunkLimit(_chunks.Count + 1))
throw new InvalidOperationException("Chunk limit exceeded.");
}
if (!_chunks.TryGet((x, y), out var chunk))
{
// If this region is all air, use the singleton
if (ShouldBeAllAir(x, y, width, height))
chunk = Chunk2D<T>.AllAir(x, y, width, height);
else
chunk = new Chunk2D<T>(x, y, width, height);
_chunks.Add((x, y), chunk, OnChunkEvicted);
ChunkResourceManager.AllocateChunk(chunk);
ChunkEvents.OnChunkLoaded(chunk);
}
return chunk;
}
private void OnChunkEvicted((int, int) key, Chunk2D<T> chunk)
{
var arrField = chunk.GetType().GetField("_data", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
T[,]? arr = null;
if (arrField != null)
{
arr = arrField.GetValue(chunk) as T[,];
}
if (!chunk.IsAllAir && arr != null)
{
Chunk2D<T>.ReturnArray(arr);
}
ChunkResourceManager.ReleaseChunk(chunk);
ChunkEvents.OnChunkUnloaded(chunk);
// Optionally: compress or persist chunk here
}
// Example stub: you can implement your own logic for "all air"
private bool ShouldBeAllAir(int x, int y, int width, int height)
{
// For now, always false. In a real system, check worldgen or disk.
return false;
}
/// <summary>
/// Loads or creates a chunk at (x, y) with the given size asynchronously.
/// Returns the loaded or newly created chunk.
/// </summary>
public async Task<Chunk2D<T>> LoadOrCreateChunkAsync(int x, int y, int width, int height)
{
// Simulate async work (e.g., loading from disk/network)
return await Task.Run(() => LoadOrCreateChunk(x, y, width, height));
}
/// <summary>
/// Unloads (removes) the chunk at (x, y) if loaded.
/// </summary>
public bool UnloadChunk(int x, int y)
{
if (_chunks.Remove((x, y), out var chunk))
{
var arr = chunk.GetDataArray();
if (!chunk.IsAllAir && arr != null)
{
Chunk2D<T>.ReturnArray(arr);
}
ChunkResourceManager.ReleaseChunk(chunk);
ChunkEvents.OnChunkUnloaded(chunk);
return true;
}
return false;
}
/// <summary>
/// Unloads (removes) the chunk at (x, y) if loaded asynchronously.
/// </summary>
public async Task<bool> UnloadChunkAsync(int x, int y)
{
return await Task.Run(() => UnloadChunk(x, y));
}
/// <summary>
/// Enumerates all loaded chunks.
/// </summary>
public IEnumerable<Chunk2D<T>> GetAllChunks() => _chunks.Values;
// IChunkManager interface compatibility
bool IChunkManager.IsChunkLoaded(int x, int y) => IsChunkLoaded(x, y);
IChunk? IChunkManager.GetChunk(int x, int y) => GetChunk(x, y);
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<IChunk> IChunkManager.GetAllChunks() => _chunks.Values;
}
}

View File

@ -0,0 +1,102 @@
#nullable enable
using System.Collections.Generic;
using System.Threading.Tasks;
using AdvChkSys.Chunk;
using AdvChkSys.Events;
using AdvChkSys.Interfaces;
using AdvChkSys.Resources;
using AdvChkSys.Constraints;
using AdvChkSys.Util;
using System;
namespace AdvChkSys.Manager
{
/// <summary>
/// Manages 3D chunks in memory. Handles loading, unloading, and access.
/// </summary>
public class ChunkManager3D<T> : IChunkManager
{
private readonly LRUCache<(int, int, int), Chunk3D<T>> _chunks;
private readonly WorldConstraints? _constraints;
private readonly int _capacity;
public ChunkManager3D(WorldConstraints? constraints = null, int capacity = 4096)
{
_constraints = constraints;
_capacity = capacity;
_chunks = new LRUCache<(int, int, int), Chunk3D<T>>(_capacity);
}
/// <summary>
/// Returns true if the chunk at (x, y, z) is loaded.
/// </summary>
public bool IsChunkLoaded(int x, int y, int z) => _chunks.TryGet((x, y, z), out _);
/// <summary>
/// Gets the chunk at (x, y, z) if loaded, or null if not.
/// </summary>
public Chunk3D<T>? GetChunk(int x, int y, int z)
{
_chunks.TryGet((x, y, z), out var chunk);
return chunk;
}
/// <summary>
/// Loads or creates a chunk at (x, y, z) with the given size.
/// Returns the loaded or newly created chunk.
/// </summary>
public Chunk3D<T> LoadOrCreateChunk(int x, int y, int z, int width, int height, int depth)
{
if (_constraints != null)
{
if (!_constraints.IsWithinBounds(x, y))
throw new InvalidOperationException("Chunk coordinates out of bounds.");
if (!_constraints.IsWithinChunkLimit(_chunks.Count + 1))
throw new InvalidOperationException("Chunk limit exceeded.");
}
if (!_chunks.TryGet((x, y, z), out var chunk))
{
chunk = new Chunk3D<T>(x, y, z, width, height, depth);
_chunks.Add((x, y, z), chunk, OnChunkEvicted);
ChunkResourceManager.AllocateChunk(chunk);
ChunkEvents.OnChunkLoaded(chunk);
}
return chunk;
}
private void OnChunkEvicted((int, int, int) key, Chunk3D<T> chunk)
{
ChunkResourceManager.ReleaseChunk(chunk);
ChunkEvents.OnChunkUnloaded(chunk);
// Optionally: compress or persist chunk here
}
/// <summary>
/// Unloads (removes) the chunk at (x, y, z) if loaded.
/// </summary>
public bool UnloadChunk(int x, int y, int z)
{
if (_chunks.Remove((x, y, z), out var chunk))
{
ChunkResourceManager.ReleaseChunk(chunk);
ChunkEvents.OnChunkUnloaded(chunk);
return true;
}
return false;
}
/// <summary>
/// Enumerates all loaded chunks.
/// </summary>
public IEnumerable<Chunk3D<T>> GetAllChunks() => _chunks.Values;
// IChunkManager interface compatibility (for 2D interface)
bool IChunkManager.IsChunkLoaded(int x, int y) => _chunks.TryGet((x, y, 0), out _);
IChunk? IChunkManager.GetChunk(int x, int y) => _chunks.TryGet((x, y, 0), out var chunk) ? chunk : null;
IChunk IChunkManager.LoadOrCreateChunk(int x, int y, int width, int height) =>
LoadOrCreateChunk(x, y, 0, width, height, 1);
bool IChunkManager.UnloadChunk(int x, int y) => UnloadChunk(x, y, 0);
IEnumerable<IChunk> IChunkManager.GetAllChunks() => _chunks.Values;
}
}

View File

@ -0,0 +1,50 @@
#nullable enable
using AdvChkSys.Interfaces;
using System;
using System.Collections.Concurrent;
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>();
/// <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)
{
_allocatedChunks[chunk] = DateTime.UtcNow;
// Optionally: implement resource limits, pooling, or logging here.
}
/// <summary>
/// Called when a chunk is released/unloaded from memory.
/// Removes the chunk from tracking and can be extended for pooling or cleanup.
/// </summary>
public static void ReleaseChunk(IChunk chunk)
{
_allocatedChunks.TryRemove(chunk, out _);
// Optionally: implement cleanup, pooling, or logging here.
}
/// <summary>
/// Gets the current number of allocated chunks.
/// </summary>
public static int AllocatedChunkCount => _allocatedChunks.Count;
/// <summary>
/// Clears all tracked chunks (for diagnostics or shutdown).
/// </summary>
public static void Clear()
{
_allocatedChunks.Clear();
}
}
}

View File

@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.IO;
using AdvChkSys.Chunk;
namespace AdvChkSys.Serialization
{
// <summary>
// Provides serialization and deserialization for chunk instances.
// Supports Chunk2D<T> and Chunk3D<T> with primitive types (e.g., byte, int, float).
// </summary>
public static class ChunkSerializer
{
// -------- 2D --------
public static byte[] Serialize2D<T>(Chunk2D<T> chunk) where T : struct
{
using var ms = new MemoryStream();
using var writer = new BinaryWriter(ms);
writer.Write(chunk.X);
writer.Write(chunk.Y);
writer.Write(chunk.Width);
writer.Write(chunk.Height);
writer.Write(chunk.Metadata.Count);
foreach (var kvp in chunk.Metadata)
{
writer.Write(kvp.Key);
writer.Write(kvp.Value?.ToString() ?? "");
}
for (int x = 0; x < chunk.Width; x++)
for (int y = 0; y < chunk.Height; y++)
WritePrimitive(writer, chunk[x, y]);
writer.Flush();
return ms.ToArray();
}
public static Chunk2D<T> Deserialize2D<T>(byte[] data) where T : struct
{
using var ms = new MemoryStream(data);
using var reader = new BinaryReader(ms);
int x = reader.ReadInt32();
int y = reader.ReadInt32();
int width = reader.ReadInt32();
int height = reader.ReadInt32();
var chunk = new Chunk2D<T>(x, y, width, height);
int metaCount = reader.ReadInt32();
for (int i = 0; i < metaCount; i++)
{
string key = reader.ReadString();
string value = reader.ReadString();
chunk.Metadata[key] = value;
}
for (int ix = 0; ix < width; ix++)
for (int iy = 0; iy < height; iy++)
chunk[ix, iy] = ReadPrimitive<T>(reader);
return chunk;
}
// -------- 3D --------
public static byte[] Serialize3D<T>(Chunk3D<T> chunk) where T : struct
{
using var ms = new MemoryStream();
using var writer = new BinaryWriter(ms);
writer.Write(chunk.X);
writer.Write(chunk.Y);
writer.Write(chunk.Z);
writer.Write(chunk.Width);
writer.Write(chunk.Height);
writer.Write(chunk.Depth);
writer.Write(chunk.Metadata.Count);
foreach (var kvp in chunk.Metadata)
{
writer.Write(kvp.Key);
writer.Write(kvp.Value?.ToString() ?? "");
}
for (int x = 0; x < chunk.Width; x++)
for (int y = 0; y < chunk.Height; y++)
for (int z = 0; z < chunk.Depth; z++)
WritePrimitive(writer, chunk[x, y, z]);
writer.Flush();
return ms.ToArray();
}
public static Chunk3D<T> Deserialize3D<T>(byte[] data) where T : struct
{
using var ms = new MemoryStream(data);
using var reader = new BinaryReader(ms);
int x = reader.ReadInt32();
int y = reader.ReadInt32();
int z = reader.ReadInt32();
int width = reader.ReadInt32();
int height = reader.ReadInt32();
int depth = reader.ReadInt32();
var chunk = new Chunk3D<T>(x, y, z, width, height, depth);
int metaCount = reader.ReadInt32();
for (int i = 0; i < metaCount; i++)
{
string key = reader.ReadString();
string value = reader.ReadString();
chunk.Metadata[key] = value;
}
for (int ix = 0; ix < width; ix++)
for (int iy = 0; iy < height; iy++)
for (int iz = 0; iz < depth; iz++)
chunk[ix, iy, iz] = ReadPrimitive<T>(reader);
return chunk;
}
// -------- Helpers --------
private static void WritePrimitive<T>(BinaryWriter writer, T value) where T : struct
{
if (typeof(T) == typeof(byte))
writer.Write((byte)(object)value);
else if (typeof(T) == typeof(int))
writer.Write((int)(object)value);
else if (typeof(T) == typeof(float))
writer.Write((float)(object)value);
else if (typeof(T) == typeof(double))
writer.Write((double)(object)value);
else if (typeof(T) == typeof(short))
writer.Write((short)(object)value);
else if (typeof(T) == typeof(long))
writer.Write((long)(object)value);
else if (typeof(T) == typeof(bool))
writer.Write((bool)(object)value);
else
throw new NotSupportedException($"Type {typeof(T)} is not supported for serialization.");
}
private static T ReadPrimitive<T>(BinaryReader reader) where T : struct
{
if (typeof(T) == typeof(byte))
return (T)(object)reader.ReadByte();
else if (typeof(T) == typeof(int))
return (T)(object)reader.ReadInt32();
else if (typeof(T) == typeof(float))
return (T)(object)reader.ReadSingle();
else if (typeof(T) == typeof(double))
return (T)(object)reader.ReadDouble();
else if (typeof(T) == typeof(short))
return (T)(object)reader.ReadInt16();
else if (typeof(T) == typeof(long))
return (T)(object)reader.ReadInt64();
else if (typeof(T) == typeof(bool))
return (T)(object)reader.ReadBoolean();
else
throw new NotSupportedException($"Type {typeof(T)} is not supported for deserialization.");
}
}
}

View File

@ -0,0 +1,45 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AdvChkSys.Threading
{
/// <summary>
/// Provides utilities for running chunk-related tasks asynchronously.
/// Supports scheduling, cancellation, and custom task options.
/// </summary>
public static class ChunkTaskScheduler
{
/// <summary>
/// Runs the given action asynchronously on the thread pool.
/// </summary>
public static Task RunAsync(Action action, CancellationToken cancellationToken = default)
{
return Task.Run(action, cancellationToken);
}
/// <summary>
/// Runs the given function asynchronously on the thread pool and returns a result.
/// </summary>
public static Task<TResult> RunAsync<TResult>(Func<TResult> func, CancellationToken cancellationToken = default)
{
return Task.Run(func, cancellationToken);
}
/// <summary>
/// Runs the given asynchronous function.
/// </summary>
public static Task RunAsync(Func<Task> func, CancellationToken cancellationToken = default)
{
return Task.Run(func, cancellationToken);
}
/// <summary>
/// Runs the given asynchronous function and returns a result.
/// </summary>
public static Task<TResult> RunAsync<TResult>(Func<Task<TResult>> func, CancellationToken cancellationToken = default)
{
return Task.Run(func, cancellationToken);
}
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Runtime.InteropServices;
namespace AdvChkSys.Util
{
public static class CacheCapacityHelper
{
/// <summary>
/// Calculates the maximum number of chunks that can fit in available memory.
/// </summary>
/// <param name="chunkWidth">Chunk width (cells)</param>
/// <param name="chunkHeight">Chunk height (cells)</param>
/// <param name="elementSize">Size of a single element in bytes (e.g., use sizeof(byte) or Marshal.SizeOf(typeof(T)))</param>
/// <param name="safetyFactor">Fraction of available memory to use (0.8 = 80%)</param>
public static int CalculateChunkCapacity(int chunkWidth, int chunkHeight, int elementSize, double safetyFactor = 0.8)
{
ulong availableBytes = MemoryHelper.GetAvailableMemoryBytes();
ulong usableBytes = (ulong)(availableBytes * safetyFactor);
int bytesPerChunk = chunkWidth * chunkHeight * elementSize;
if (bytesPerChunk == 0) bytesPerChunk = 1; // avoid div by zero
ulong maxChunks = usableBytes / (ulong)bytesPerChunk;
return (int)Math.Max(1, Math.Min(maxChunks, int.MaxValue));
}
}
}

View File

@ -0,0 +1,107 @@
#nullable enable
using System;
using System.Collections.Generic;
namespace AdvChkSys.Util
{
/// <summary>
/// Thread-safe LRU (Least Recently Used) cache.
/// </summary>
public class LRUCache<TKey, TValue>
where TKey : notnull
{
private readonly int _capacity;
private readonly Dictionary<TKey, LinkedListNode<(TKey key, TValue value)>> _map;
private readonly LinkedList<(TKey key, TValue value)> _lruList;
private readonly object _lock = new();
public LRUCache(int capacity)
{
if (capacity <= 0) throw new ArgumentOutOfRangeException(nameof(capacity));
_capacity = capacity;
_map = new Dictionary<TKey, LinkedListNode<(TKey, TValue)>>();
_lruList = new LinkedList<(TKey, TValue)>();
}
public bool TryGet(TKey key, out TValue value)
{
lock (_lock)
{
if (_map.TryGetValue(key, out var node))
{
_lruList.Remove(node);
_lruList.AddFirst(node);
value = node.Value.value;
return true;
}
value = default!;
return false;
}
}
public void Add(TKey key, TValue value, Action<TKey, TValue>? onEvict = null)
{
lock (_lock)
{
if (_map.TryGetValue(key, out var node))
{
_lruList.Remove(node);
_lruList.AddFirst(node);
node.Value = (key, value);
}
else
{
if (_map.Count >= _capacity)
{
var last = _lruList.Last;
if (last != null)
{
_map.Remove(last.Value.key);
onEvict?.Invoke(last.Value.key, last.Value.value);
_lruList.RemoveLast();
}
}
var newNode = new LinkedListNode<(TKey, TValue)>((key, value));
_lruList.AddFirst(newNode);
_map[key] = newNode;
}
}
}
public bool Remove(TKey key, out TValue? value)
{
lock (_lock)
{
if (_map.TryGetValue(key, out var node))
{
value = node.Value.value;
_lruList.Remove(node);
_map.Remove(key);
return true;
}
value = default;
return false;
}
}
public IEnumerable<TValue> Values
{
get
{
lock (_lock)
{
foreach (var node in _lruList)
yield return node.value;
}
}
}
public int Count
{
get
{
lock (_lock) { return _map.Count; }
}
}
}
}

View File

@ -0,0 +1,94 @@
using System;
using System.Runtime.InteropServices;
namespace AdvChkSys.Util
{
public static class MemoryHelper
{
public static ulong GetAvailableMemoryBytes()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return GetAvailableMemoryWindows();
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return GetAvailableMemoryLinux();
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return GetAvailableMemoryMac();
}
else
{
// Fallback: unknown, assume 1GB
return 1UL << 30;
}
}
private static ulong GetAvailableMemoryWindows()
{
var memStatus = new MEMORYSTATUSEX();
memStatus.dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
if (GlobalMemoryStatusEx(ref memStatus))
{
return memStatus.ullAvailPhys;
}
return 1UL << 30;
}
private static ulong GetAvailableMemoryLinux()
{
try
{
string[] lines = System.IO.File.ReadAllLines("/proc/meminfo");
ulong memFree = 0, buffers = 0, cached = 0;
foreach (var line in lines)
{
if (line.StartsWith("MemAvailable:"))
memFree = ParseKb(line);
else if (line.StartsWith("Buffers:"))
buffers = ParseKb(line);
else if (line.StartsWith("Cached:"))
cached = ParseKb(line);
}
return memFree > 0 ? memFree * 1024 : (buffers + cached) * 1024;
}
catch
{
return 1UL << 30;
}
}
private static ulong GetAvailableMemoryMac()
{
// macOS: fallback to 1GB (implementing this is complex and rarely needed)
return 1UL << 30;
}
private static ulong ParseKb(string line)
{
var parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 2 && ulong.TryParse(parts[1], out var kb))
return kb;
return 0;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct MEMORYSTATUSEX
{
public uint dwLength;
public uint dwMemoryLoad;
public ulong ullTotalPhys;
public ulong ullAvailPhys;
public ulong ullTotalPageFile;
public ulong ullAvailPageFile;
public ulong ullTotalVirtual;
public ulong ullAvailVirtual;
public ulong ullAvailExtendedVirtual;
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);
}
}

338
src/AdvChkSys/advchksys.md Normal file
View File

@ -0,0 +1,338 @@
AdvChkSys API Documentation
Overview
AdvChkSys is a high-performance, extensible chunked world management library for .NET. It provides efficient memory management, chunk loading/unloading, resource tracking, event hooks, serialization, and optional constraints for 2D and 3D chunk-based worlds. The system is suitable for games, simulations, and voxel engines.
Namespaces
AdvChkSys
AdvChkSys.Chunk
AdvChkSys.Manager
AdvChkSys.Interfaces
AdvChkSys.Events
AdvChkSys.Resources
AdvChkSys.Constraints
AdvChkSys.Serialization
AdvChkSys.Threading
AdvChkSys.Util
AdvChkSys (Root)
public static class AdvChkSys
Entry point and static facade for the AdvChkSys library.
Properties
string Version
The current version of the AdvChkSys library.
Methods
WorldConstraints CreateDefaultConstraints()
Creates a new WorldConstraints object with default settings.
ChunkManager2D<byte> Create2DManager(WorldConstraints? constraints = null)
Creates a new 2D chunk manager with optional constraints.
ChunkManager3D<byte> Create3DManager(WorldConstraints? constraints = null)
Creates a new 3D chunk manager with optional constraints.
bool SelfTest()
Performs a basic self-test of the AdvChkSys core functionality. Returns true if all core systems are operational.
AdvChkSys.Chunk
public class Chunk2D<T> : IChunk
Represents a 2D chunk of data in the world. Supports all-air singleton for memory efficiency and chunk array pooling.
Constructors
Chunk2D(int x, int y, int width, int height, bool isAllAir = false)
Static Methods
Chunk2D<T> AllAir(int x, int y, int width, int height)
Returns a singleton all-air chunk for the given size and position.
Properties
int X
The chunk's X position in chunk-space coordinates.
int Y
The chunk's Y position in chunk-space coordinates.
int Width
The width of the chunk (in cells/tiles/units).
int Height
The height of the chunk (in cells/tiles/units).
Dictionary<string, object> Metadata
Metadata dictionary for arbitrary chunk information (e.g., biome, tags).
bool IsAllAir
Returns true if this chunk is the all-air singleton.
Indexers
T this[int localX, int localY]
Gets or sets the value at the given local chunk coordinates.
Methods
void Fill(T value)
Fills the chunk with a specified value (no-op for all-air).
T[,]? GetDataArray()
Returns the underlying data array (for pooling).
void ReleaseDataArray()
Releases the data array back to the pool (for chunk manager).
public class Chunk3D<T> : IChunk
Represents a 3D chunk of data in the world. Perspective-agnostic and supports arbitrary data types and metadata.
Constructors
Chunk3D(int x, int y, int z, int width, int height, int depth)
Properties
int X
The chunk's X position in chunk-space coordinates.
int Y
The chunk's Y position in chunk-space coordinates.
int Z
The chunk's Z position in chunk-space coordinates.
int Width
The width of the chunk (in cells/tiles/units).
int Height
The height of the chunk (in cells/tiles/units).
int Depth
The depth of the chunk (in cells/tiles/units).
Dictionary<string, object> Metadata
Metadata dictionary for arbitrary chunk information (e.g., biome, tags).
Indexers
T this[int localX, int localY, int localZ]
Gets or sets the value at the given local chunk coordinates.
Methods
void Fill(T value)
Fills the chunk with a specified value.
AdvChkSys.Manager
public class ChunkManager2D<T> : IChunkManager
Manages 2D chunks in memory. Handles loading, unloading, and access. Uses an LRU cache for memory efficiency.
Constructors
ChunkManager2D(WorldConstraints? constraints = null, int capacity = 4096)
Methods
bool IsChunkLoaded(int x, int y)
Returns true if the chunk at (x, y) is loaded.
Chunk2D<T>? GetChunk(int x, int y)
Gets the chunk at (x, y) if loaded, or null if not.
Chunk2D<T> LoadOrCreateChunk(int x, int y, int width, int height)
Loads or creates a chunk at (x, y) with the given size.
Task<Chunk2D<T>> LoadOrCreateChunkAsync(int x, int y, int width, int height)
Loads or creates a chunk asynchronously.
bool UnloadChunk(int x, int y)
Unloads (removes) the chunk at (x, y) if loaded.
Task<bool> UnloadChunkAsync(int x, int y)
Unloads the chunk asynchronously.
IEnumerable<Chunk2D<T>> GetAllChunks()
Enumerates all loaded chunks.
public class ChunkManager3D<T> : IChunkManager
Manages 3D chunks in memory. Handles loading, unloading, and access.
Constructors
ChunkManager3D(WorldConstraints? constraints = null, int capacity = 4096)
Methods
bool IsChunkLoaded(int x, int y, int z)
Returns true if the chunk at (x, y, z) is loaded.
Chunk3D<T>? GetChunk(int x, int y, int z)
Gets the chunk at (x, y, z) if loaded, or null if not.
Chunk3D<T> LoadOrCreateChunk(int x, int y, int z, int width, int height, int depth)
Loads or creates a chunk at (x, y, z) with the given size.
bool UnloadChunk(int x, int y, int z)
Unloads (removes) the chunk at (x, y, z) if loaded.
IEnumerable<Chunk3D<T>> GetAllChunks()
Enumerates all loaded chunks.
AdvChkSys.Interfaces
public interface IChunk
Represents a generic chunk in the world. Provides basic properties for position, size, and metadata.
Properties
int X { get; }
int Y { get; }
int Width { get; }
int Height { get; }
Dictionary<string, object> Metadata { get; }
public interface IChunkManager
Interface for managing chunks in memory. Provides methods for loading, unloading, and accessing chunks.
Methods
bool IsChunkLoaded(int x, int y)
IChunk? GetChunk(int x, int y)
IChunk LoadOrCreateChunk(int x, int y, int width, int height)
bool UnloadChunk(int x, int y)
IEnumerable<IChunk> GetAllChunks()
AdvChkSys.Events
public static class ChunkEvents
Provides events for chunk lifecycle operations such as loading, unloading, saving, and more. Thread-safe event subscription and invocation.
Events
event Action<IChunk> ChunkLoaded
Occurs when a chunk has been loaded into memory.
event Action<IChunk> ChunkLoading
Occurs when a chunk is about to be loaded into memory.
event Action<IChunk> ChunkUnloading
Occurs when a chunk is about to be unloaded from memory.
event Action<IChunk> ChunkUnloaded
Occurs when a chunk has been unloaded from memory.
event Action<IChunk> ChunkSaving
Occurs when a chunk is about to be saved.
event Action<IChunk> ChunkSaved
Occurs when a chunk has been saved.
Methods
void OnChunkLoading(IChunk chunk)
Raises the ChunkLoading event.
void OnChunkLoaded(IChunk chunk)
Raises the ChunkLoaded event.
void OnChunkUnloading(IChunk chunk)
Raises the ChunkUnloading event.
void OnChunkUnloaded(IChunk chunk)
Raises the ChunkUnloaded event.
void OnChunkSaving(IChunk chunk)
Raises the ChunkSaving event.
void OnChunkSaved(IChunk chunk)
Raises the ChunkSaved event.
AdvChkSys.Resources
public static class ChunkResourceManager
Manages allocation and release of chunk resources in memory. Can be extended to track resource usage, pooling, or implement custom memory strategies.
Methods
void AllocateChunk(IChunk chunk)
Called when a chunk is allocated/loaded into memory. Tracks the chunk and can be extended for pooling or resource limits.
void ReleaseChunk(IChunk chunk)
Called when a chunk is released/unloaded from memory. Removes the chunk from tracking and can be extended for pooling or cleanup.
int AllocatedChunkCount
Gets the current number of allocated chunks.
void Clear()
Clears all tracked chunks (for diagnostics or shutdown).
AdvChkSys.Constraints
public class WorldConstraints
Represents constraints and limits for the chunk world. Can be used to restrict world size, chunk counts, and other resource limits.
Properties
int? MinChunkX
The minimum allowed chunk X coordinate (inclusive).
int? MaxChunkX
The maximum allowed chunk X coordinate (inclusive).
int? MinChunkY
The minimum allowed chunk Y coordinate (inclusive).
int? MaxChunkY
The maximum allowed chunk Y coordinate (inclusive).
int? MaxLoadedChunks
The maximum number of chunks allowed to be loaded in memory at once.
Methods
bool IsWithinBounds(int chunkX, int chunkY)
Checks if the given chunk coordinates are within the allowed world bounds.
bool IsWithinChunkLimit(int loadedChunkCount)
Checks if the current number of loaded chunks is within the allowed limit.
AdvChkSys.Serialization
public static class ChunkSerializer
Provides serialization and deserialization for chunk instances. Supports Chunk2D<T> and Chunk3D<T> with primitive types (e.g., byte, int, float).
Methods
byte[] Serialize2D<T>(Chunk2D<T> chunk) where T : struct
Serializes a Chunk2D<T> to a byte array.
Chunk2D<T> Deserialize2D<T>(byte[] data) where T : struct
Deserializes a Chunk2D<T> from a byte array.
byte[] Serialize3D<T>(Chunk3D<T> chunk) where T : struct
Serializes a Chunk3D<T> to a byte array.
Chunk3D<T> Deserialize3D<T>(byte[] data) where T : struct
Deserializes a Chunk3D<T> from a byte array.
AdvChkSys.Threading
public static class ChunkTaskScheduler
Provides utilities for running chunk-related tasks asynchronously. Supports scheduling, cancellation, and custom task options.
Methods
Task RunAsync(Action action, CancellationToken cancellationToken = default)
Runs the given action asynchronously on the thread pool.
Task<TResult> RunAsync<TResult>(Func<TResult> func, CancellationToken cancellationToken = default)
Runs the given function asynchronously and returns a result.
Task RunAsync(Func<Task> func, CancellationToken cancellationToken = default)
Runs the given asynchronous function.
Task<TResult> RunAsync<TResult>(Func<Task<TResult>> func, CancellationToken cancellationToken = default)
Runs the given asynchronous function and returns a result.
AdvChkSys.Util
public class LRUCache<TKey, TValue> where TKey : notnull
Thread-safe LRU (Least Recently Used) cache. Used for chunk memory management.
Constructors
LRUCache(int capacity)
Methods
bool TryGet(TKey key, out TValue value)
Tries to get a value by key and marks it as recently used.
void Add(TKey key, TValue value, Action<TKey, TValue>? onEvict = null)
Adds a value to the cache, evicting the least recently used if over capacity.
bool Remove(TKey key, out TValue? value)
Removes a value by key.
Properties
IEnumerable<TValue> Values
Enumerates all values in the cache.
int Count
Gets the number of items in the cache.
Python Bindings (src/bindings/python/advchksys.py)
Provides Python access to AdvChkSys via .NET interop.
Functions
get_version()
Returns the AdvChkSys library version.
create_2d_manager(constraints=None)
Creates a 2D chunk manager (byte type).
create_3d_manager(constraints=None)
Creates a 3D chunk manager (byte type).
create_constraints(min_x=None, max_x=None, min_y=None, max_y=None, max_loaded=None)
Creates a WorldConstraints object.
Summary
AdvChkSys provides a modular, high-performance, and extensible API for chunked world management in .NET, with robust support for memory pooling, resource tracking, event hooks, serialization, constraints, and async operations. It is suitable for games, simulations, and any application requiring efficient spatial partitioning and management of large worlds.

View File

@ -0,0 +1,36 @@
# AdvChkSys Python Bindings
These bindings allow you to use the AdvChkSys C# chunk system from Python via [pythonnet](https://github.com/pythonnet/pythonnet).
## Requirements
- .NET 6.0+ or .NET Core 3.1+ (to build AdvChkSys)
- Python 3.8+
- `pythonnet` (`pip install pythonnet`)
## Usage
1. **Build the AdvChkSys C# library** (DLL must be present in `src/AdvChkSys/bin/Debug/netstandard2.1/AdvChkSys.dll`).
2. **Install pythonnet:**
```bash
pip install pythonnet
```
3. **Use the bindings in Python:**
```python
from advchksys import get_version, create_2d_manager, create_constraints
print("AdvChkSys version:", get_version())
constraints = create_constraints(min_x=0, max_x=10, min_y=0, max_y=10, max_loaded=100)
manager = create_2d_manager(constraints)
chunk = manager.LoadOrCreateChunk(1, 1, 16, 16)
chunk[0, 0] = 42
print("Chunk value at (0,0):", chunk[0, 0])
```
## Notes
- You can access all public methods and properties of the C# classes.
- For advanced usage, see the [pythonnet documentation](https://pythonnet.github.io/pythonnet/).

View File

@ -0,0 +1,71 @@
import os
import sys
import clr # type: ignore
# Path to the compiled AdvChkSys .NET DLL (adjust as needed)
DLL_PATH = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"..",
"..",
"AdvChkSys",
"bin",
"Debug",
"netstandard2.1",
"AdvChkSys.dll",
)
)
if not os.path.exists(DLL_PATH):
raise FileNotFoundError(f"Could not find AdvChkSys.dll at {DLL_PATH}")
sys.path.append(os.path.dirname(DLL_PATH))
clr.AddReference("AdvChkSys")
# Import .NET types
from System import Byte # type: ignore # noqa: E402
import AdvChkSys # type: ignore # noqa: E402
import AdvChkSys.Manager # type: ignore # noqa: E402
import AdvChkSys.Constraints # type: ignore # noqa: E402
def get_version():
"""Return the AdvChkSys library version."""
return AdvChkSys.AdvChkSys.Version # type: ignore
def create_2d_manager(constraints=None):
"""Create a 2D chunk manager (byte type)."""
if constraints is None:
constraints = AdvChkSys.Constraints.WorldConstraints() # type: ignore
# Use generic type with System.Byte
return AdvChkSys.Manager.ChunkManager2D[Byte](constraints) # type: ignore
def create_3d_manager(constraints=None):
"""Create a 3D chunk manager (byte type)."""
if constraints is None:
constraints = AdvChkSys.Constraints.WorldConstraints() # type: ignore
# Use generic type with System.Byte
return AdvChkSys.Manager.ChunkManager3D[Byte](constraints) # type: ignore
def create_constraints(
min_x=None, max_x=None, min_y=None, max_y=None, max_loaded=None
):
"""Create a WorldConstraints object."""
wc = AdvChkSys.Constraints.WorldConstraints() # type: ignore
if min_x is not None:
wc.MinChunkX = min_x
if max_x is not None:
wc.MaxChunkX = max_x
if min_y is not None:
wc.MinChunkY = min_y
if max_y is not None:
wc.MaxChunkY = max_y
if max_loaded is not None:
wc.MaxLoadedChunks = max_loaded
return wc