255 lines
8.2 KiB
C#
255 lines
8.2 KiB
C#
using AdvChkSys.Chunk;
|
|
using AdvChkSys.Manager;
|
|
|
|
namespace Sand.ChunkPrototype;
|
|
|
|
public sealed class PrototypeChunkResidencyWorld
|
|
{
|
|
private readonly ChunkManager2D<byte> _manager;
|
|
private readonly Dictionary<(int ChunkX, int ChunkY), int> _occupancyCounts = new();
|
|
private readonly HashSet<(int ChunkX, int ChunkY)> _dirtyChunks = new();
|
|
private readonly ChunkResidencyConfig _config;
|
|
|
|
public PrototypeChunkResidencyWorld(ChunkResidencyConfig? config = null)
|
|
{
|
|
_config = config ?? ChunkResidencyConfig.Default;
|
|
_manager = new ChunkManager2D<byte>(
|
|
capacity: _config.Capacity,
|
|
chunkWidth: _config.ChunkWidth,
|
|
chunkHeight: _config.ChunkHeight);
|
|
}
|
|
|
|
public int LoadedChunkCount => _manager.GetAllChunks().Count();
|
|
public int ChunkWidth => _config.ChunkWidth;
|
|
public int ChunkHeight => _config.ChunkHeight;
|
|
|
|
public int ActiveChunkCount => _occupancyCounts.Count;
|
|
|
|
public int DirtyChunkCount => _dirtyChunks.Count;
|
|
|
|
public long EstimatedLoadedBytes => (long)LoadedChunkCount * _config.ChunkWidth * _config.ChunkHeight * sizeof(byte);
|
|
|
|
public IReadOnlyCollection<(int ChunkX, int ChunkY)> ActiveChunks => _occupancyCounts.Keys.ToArray();
|
|
|
|
public IReadOnlyCollection<(int ChunkX, int ChunkY)> DirtyChunks => _dirtyChunks.ToArray();
|
|
|
|
public void SetOccupied(int cellX, int cellY, byte value = 1)
|
|
{
|
|
var chunk = GetChunk(cellX, cellY, out var localX, out var localY, out var key);
|
|
if (chunk[localX, localY] == 0)
|
|
{
|
|
_occupancyCounts[key] = _occupancyCounts.TryGetValue(key, out var count) ? count + 1 : 1;
|
|
}
|
|
|
|
chunk[localX, localY] = value;
|
|
_dirtyChunks.Add(key);
|
|
}
|
|
|
|
public void ClearOccupied(int cellX, int cellY)
|
|
{
|
|
var chunk = GetChunk(cellX, cellY, out var localX, out var localY, out var key);
|
|
if (chunk[localX, localY] == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
chunk[localX, localY] = 0;
|
|
if (!_occupancyCounts.TryGetValue(key, out var count))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (count <= 1)
|
|
{
|
|
_occupancyCounts.Remove(key);
|
|
_dirtyChunks.Add(key);
|
|
return;
|
|
}
|
|
|
|
_occupancyCounts[key] = count - 1;
|
|
_dirtyChunks.Add(key);
|
|
}
|
|
|
|
public bool IsOccupied(int cellX, int cellY)
|
|
{
|
|
var chunk = GetChunk(cellX, cellY, out var localX, out var localY, out _);
|
|
return chunk[localX, localY] != 0;
|
|
}
|
|
|
|
public bool IsChunkLoaded(int chunkX, int chunkY) => _manager.GetChunk(chunkX, chunkY) is not null;
|
|
|
|
public int GetChunkOccupancyCount(int chunkX, int chunkY) =>
|
|
_occupancyCounts.TryGetValue((chunkX, chunkY), out var count) ? count : 0;
|
|
|
|
public void ClearDirtyChunks() => _dirtyChunks.Clear();
|
|
|
|
public bool MoveOccupied(int fromCellX, int fromCellY, int toCellX, int toCellY, byte value = 1)
|
|
{
|
|
var sourceChunk = GetChunk(fromCellX, fromCellY, out var sourceLocalX, out var sourceLocalY, out var sourceKey);
|
|
if (sourceChunk[sourceLocalX, sourceLocalY] == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (fromCellX == toCellX && fromCellY == toCellY)
|
|
{
|
|
sourceChunk[sourceLocalX, sourceLocalY] = value;
|
|
_dirtyChunks.Add(sourceKey);
|
|
return true;
|
|
}
|
|
|
|
var destinationChunk = GetChunk(toCellX, toCellY, out var destinationLocalX, out var destinationLocalY, out var destinationKey);
|
|
if (destinationChunk[destinationLocalX, destinationLocalY] != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
sourceChunk[sourceLocalX, sourceLocalY] = 0;
|
|
destinationChunk[destinationLocalX, destinationLocalY] = value;
|
|
|
|
DecrementChunkOccupancy(sourceKey);
|
|
_occupancyCounts[destinationKey] = _occupancyCounts.TryGetValue(destinationKey, out var destinationCount) ? destinationCount + 1 : 1;
|
|
|
|
_dirtyChunks.Add(sourceKey);
|
|
_dirtyChunks.Add(destinationKey);
|
|
return true;
|
|
}
|
|
|
|
public bool MoveOccupiedWithinChunk(int chunkX, int chunkY, int fromLocalX, int fromLocalY, int toLocalX, int toLocalY, byte value = 1)
|
|
{
|
|
var chunk = _manager.LoadOrCreateChunk(chunkX, chunkY, _config.ChunkWidth, _config.ChunkHeight);
|
|
if (chunk[fromLocalX, fromLocalY] == 0 || chunk[toLocalX, toLocalY] != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
chunk[fromLocalX, fromLocalY] = 0;
|
|
chunk[toLocalX, toLocalY] = value;
|
|
_dirtyChunks.Add((chunkX, chunkY));
|
|
return true;
|
|
}
|
|
|
|
public bool SwapOccupied(int firstCellX, int firstCellY, int secondCellX, int secondCellY, byte firstValue, byte secondValue)
|
|
{
|
|
var firstChunk = GetChunk(firstCellX, firstCellY, out var firstLocalX, out var firstLocalY, out var firstKey);
|
|
var secondChunk = GetChunk(secondCellX, secondCellY, out var secondLocalX, out var secondLocalY, out var secondKey);
|
|
if (firstChunk[firstLocalX, firstLocalY] == 0 || secondChunk[secondLocalX, secondLocalY] == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
firstChunk[firstLocalX, firstLocalY] = secondValue;
|
|
secondChunk[secondLocalX, secondLocalY] = firstValue;
|
|
_dirtyChunks.Add(firstKey);
|
|
_dirtyChunks.Add(secondKey);
|
|
return true;
|
|
}
|
|
|
|
public bool SwapOccupiedWithinChunk(int chunkX, int chunkY, int firstLocalX, int firstLocalY, int secondLocalX, int secondLocalY, byte firstValue, byte secondValue)
|
|
{
|
|
var chunk = _manager.LoadOrCreateChunk(chunkX, chunkY, _config.ChunkWidth, _config.ChunkHeight);
|
|
if (chunk[firstLocalX, firstLocalY] == 0 || chunk[secondLocalX, secondLocalY] == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
chunk[firstLocalX, firstLocalY] = secondValue;
|
|
chunk[secondLocalX, secondLocalY] = firstValue;
|
|
_dirtyChunks.Add((chunkX, chunkY));
|
|
return true;
|
|
}
|
|
|
|
public int UnloadEmptyChunks()
|
|
{
|
|
var unloaded = 0;
|
|
foreach (var chunk in _manager.GetAllChunks().ToArray())
|
|
{
|
|
var key = (chunk.X, chunk.Y);
|
|
if (_occupancyCounts.ContainsKey(key))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (_manager.UnloadChunk(chunk.X, chunk.Y))
|
|
{
|
|
unloaded++;
|
|
}
|
|
}
|
|
|
|
return unloaded;
|
|
}
|
|
|
|
public int UnloadInactiveChunks(int marginChunks = 0)
|
|
{
|
|
if (_occupancyCounts.Count == 0)
|
|
{
|
|
return UnloadEmptyChunks();
|
|
}
|
|
|
|
var keep = new HashSet<(int ChunkX, int ChunkY)>();
|
|
foreach (var (chunkX, chunkY) in _occupancyCounts.Keys)
|
|
{
|
|
for (var offsetX = -marginChunks; offsetX <= marginChunks; offsetX++)
|
|
{
|
|
for (var offsetY = -marginChunks; offsetY <= marginChunks; offsetY++)
|
|
{
|
|
keep.Add((chunkX + offsetX, chunkY + offsetY));
|
|
}
|
|
}
|
|
}
|
|
|
|
var unloaded = 0;
|
|
foreach (var chunk in _manager.GetAllChunks().ToArray())
|
|
{
|
|
var key = (chunk.X, chunk.Y);
|
|
if (keep.Contains(key))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (_manager.UnloadChunk(chunk.X, chunk.Y))
|
|
{
|
|
unloaded++;
|
|
}
|
|
}
|
|
|
|
return unloaded;
|
|
}
|
|
|
|
private void DecrementChunkOccupancy((int ChunkX, int ChunkY) key)
|
|
{
|
|
if (!_occupancyCounts.TryGetValue(key, out var count))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (count <= 1)
|
|
{
|
|
_occupancyCounts.Remove(key);
|
|
return;
|
|
}
|
|
|
|
_occupancyCounts[key] = count - 1;
|
|
}
|
|
|
|
private Chunk2D<byte> GetChunk(int cellX, int cellY, out int localX, out int localY, out (int ChunkX, int ChunkY) key)
|
|
{
|
|
var chunkX = Math.DivRem(cellX, _config.ChunkWidth, out localX);
|
|
var chunkY = Math.DivRem(cellY, _config.ChunkHeight, out localY);
|
|
if (localX < 0)
|
|
{
|
|
localX += _config.ChunkWidth;
|
|
chunkX--;
|
|
}
|
|
|
|
if (localY < 0)
|
|
{
|
|
localY += _config.ChunkHeight;
|
|
chunkY--;
|
|
}
|
|
|
|
key = (chunkX, chunkY);
|
|
return _manager.LoadOrCreateChunk(chunkX, chunkY, _config.ChunkWidth, _config.ChunkHeight);
|
|
}
|
|
}
|