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 _particleProfiles = new(); private readonly Dictionary _cellPages = new(); private readonly Dictionary _fieldPages = new(); private readonly HashSet _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> ParticleEntries { get { var particles = new List>(_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++; } }