sandpypi/Sand.ChunkPrototype/PrototypeSparseSandAdapter.cs

184 lines
6.0 KiB
C#

using Sand.Core;
using System.Diagnostics;
namespace Sand.ChunkPrototype;
public sealed partial class PrototypeSparseSandAdapter
{
private const float FieldDecayFactor = 0.84f;
private const int RenderHaloCells = 1;
private readonly ChunkResidencyConfig _config;
private readonly Dictionary<ushort, PrototypeParticle> _particleProfiles = new();
private readonly Dictionary<ChunkCoord, ChunkCellPage> _cellPages = new();
private readonly Dictionary<ChunkCoord, ChunkFieldPage> _fieldPages = new();
private readonly HashSet<ChunkCoord> _dirtyVisualChunks = new();
private readonly ChunkStepScheduler _scheduler = new();
private readonly byte[] _rgbaBuffer;
private readonly byte[] _rgbBuffer;
private readonly float _ambientTemperature;
private bool _fullFrameDirty = true;
private int _stepCounter;
private int _particleCount;
private int _moveAttemptCount;
private int _verticalMoveAttemptCount;
private int _diagonalMoveAttemptCount;
private int _lateralMoveAttemptCount;
private int _swapAttemptCount;
private int _stalledMovableCount;
private int _movementOnlyFastPathCount;
private int _fullRuntimeStepCount;
private int _fullRuntimeSolidCount;
private int _fullRuntimeLiquidCount;
private int _fullRuntimeGasCount;
private int _movedParticles;
private int _swappedParticles;
private ChunkStepStats _lastStepStats;
public PrototypeSparseSandAdapter(int width, int height)
: this(width, height, 22f, null)
{
}
public PrototypeSparseSandAdapter(int width, int height, ChunkResidencyConfig config)
: this(width, height, 22f, config)
{
}
public PrototypeSparseSandAdapter(int width, int height, float ambientTemperature)
: this(width, height, ambientTemperature, null)
{
}
public PrototypeSparseSandAdapter(int width, int height, float ambientTemperature, ChunkResidencyConfig? config)
{
if (width <= 0)
{
throw new ArgumentOutOfRangeException(nameof(width));
}
if (height <= 0)
{
throw new ArgumentOutOfRangeException(nameof(height));
}
Width = width;
Height = height;
_ambientTemperature = ambientTemperature;
_config = config ?? ChunkResidencyConfig.Default;
World = new PrototypeChunkResidencyWorld(_config);
_rgbaBuffer = new byte[width * height * 4];
_rgbBuffer = new byte[width * height * 3];
RegisterParticleProfile(CreateLegacyParticle(PrototypeParticleType.Sand));
RegisterParticleProfile(CreateLegacyParticle(PrototypeParticleType.Water));
RegisterParticleProfile(CreateLegacyParticle(PrototypeParticleType.Steam));
RegisterParticleProfile(CreateLegacyParticle(PrototypeParticleType.Wall));
}
public int Width { get; }
public int Height { get; }
public PrototypeChunkResidencyWorld World { get; }
public int ParticleCount => _particleCount;
public ChunkStepStats LastStepStats => _lastStepStats;
public int FieldCellCount => _fieldPages.Values.Sum(static page => page.ActiveCellCount);
public long EstimatedStorageBytes =>
World.EstimatedLoadedBytes +
((long)_cellPages.Count * _config.ChunkWidth * _config.ChunkHeight * 16L) +
((long)_fieldPages.Count * _config.ChunkWidth * _config.ChunkHeight * 20L);
public IReadOnlyCollection<(int X, int Y)> Particles
{
get
{
var particles = new List<(int X, int Y)>(_particleCount);
foreach (var (coord, page) in _cellPages)
{
foreach (var (localX, localY, _) in page.EnumerateOccupied())
{
particles.Add((ToWorldX(coord, localX), ToWorldY(coord, localY)));
}
}
return particles;
}
}
public IReadOnlyCollection<KeyValuePair<(int X, int Y), PrototypeParticle>> ParticleEntries
{
get
{
var particles = new List<KeyValuePair<(int X, int Y), PrototypeParticle>>(_particleCount);
foreach (var (coord, page) in _cellPages)
{
foreach (var (localX, localY, particle) in page.EnumerateOccupied())
{
particles.Add(new KeyValuePair<(int X, int Y), PrototypeParticle>((ToWorldX(coord, localX), ToWorldY(coord, localY)), particle));
}
}
return particles;
}
}
public bool AddParticle(int x, int y, PrototypeParticleType type = PrototypeParticleType.Sand) => AddParticle(x, y, CreateLegacyParticle(type));
public void RegisterParticleProfile(PrototypeParticle particle)
{
if (!particle.IsEmpty)
{
_particleProfiles[particle.TypeId] = particle;
}
}
private static long ToMicroseconds(long startTimestamp, long endTimestamp)
{
if (endTimestamp <= startTimestamp)
{
return 0;
}
return (endTimestamp - startTimestamp) * 1_000_000L / Stopwatch.Frequency;
}
private void ResetStepMetrics()
{
_moveAttemptCount = 0;
_verticalMoveAttemptCount = 0;
_diagonalMoveAttemptCount = 0;
_lateralMoveAttemptCount = 0;
_swapAttemptCount = 0;
_stalledMovableCount = 0;
_movementOnlyFastPathCount = 0;
_fullRuntimeStepCount = 0;
_fullRuntimeSolidCount = 0;
_fullRuntimeLiquidCount = 0;
_fullRuntimeGasCount = 0;
_movedParticles = 0;
_swappedParticles = 0;
}
private void RecordMoveAttempt(int deltaX, int deltaY)
{
_moveAttemptCount++;
if (deltaY == 0)
{
_lateralMoveAttemptCount++;
return;
}
if (deltaX == 0)
{
_verticalMoveAttemptCount++;
return;
}
_diagonalMoveAttemptCount++;
}
private void RecordSwapAttempt() => _swapAttemptCount++;
private void RecordStalledMovable()
{
_stalledMovableCount++;
}
}