using Microsoft.Extensions.Options; namespace SpaceGame.Api.Universe.Simulation; public sealed class BalanceService : IBalanceService { private readonly Lock _sync = new(); private BalanceOptions _current; public BalanceService(IOptions defaults) { _current = Sanitize(defaults.Value); } public float SimulationSpeedMultiplier { get { lock (_sync) { return _current.SimulationSpeedMultiplier; } } } public float YPlane { get { lock (_sync) { return _current.YPlane; } } } public float ArrivalThreshold { get { lock (_sync) { return _current.ArrivalThreshold; } } } public float MiningRate { get { lock (_sync) { return _current.MiningRate; } } } public float MiningCycleSeconds { get { lock (_sync) { return _current.MiningCycleSeconds; } } } public float TransferRate { get { lock (_sync) { return _current.TransferRate; } } } public float DockingDuration { get { lock (_sync) { return _current.DockingDuration; } } } public float UndockingDuration { get { lock (_sync) { return _current.UndockingDuration; } } } public float UndockDistance { get { lock (_sync) { return _current.UndockDistance; } } } public BalanceOptions GetCurrent() { lock (_sync) { return Clone(_current); } } public BalanceOptions Update(BalanceOptions candidate) { lock (_sync) { _current = Sanitize(candidate); return Clone(_current); } } private static BalanceOptions Clone(BalanceOptions value) { return new BalanceOptions { SimulationSpeedMultiplier = value.SimulationSpeedMultiplier, YPlane = value.YPlane, ArrivalThreshold = value.ArrivalThreshold, MiningRate = value.MiningRate, MiningCycleSeconds = value.MiningCycleSeconds, TransferRate = value.TransferRate, DockingDuration = value.DockingDuration, UndockingDuration = value.UndockingDuration, UndockDistance = value.UndockDistance, }; } private static BalanceOptions Sanitize(BalanceOptions candidate) { static float finiteOr(float value, float fallback) => float.IsFinite(value) ? value : fallback; return new BalanceOptions { SimulationSpeedMultiplier = MathF.Max(0.01f, finiteOr(candidate.SimulationSpeedMultiplier, 1f)), YPlane = MathF.Max(0f, finiteOr(candidate.YPlane, 0f)), ArrivalThreshold = MathF.Max(0.1f, finiteOr(candidate.ArrivalThreshold, 16f)), MiningRate = MathF.Max(0f, finiteOr(candidate.MiningRate, 10f)), MiningCycleSeconds = MathF.Max(0.1f, finiteOr(candidate.MiningCycleSeconds, 10f)), TransferRate = MathF.Max(0f, finiteOr(candidate.TransferRate, 56f)), DockingDuration = MathF.Max(0.1f, finiteOr(candidate.DockingDuration, 1.2f)), UndockingDuration = MathF.Max(0.1f, finiteOr(candidate.UndockingDuration, 1.2f)), UndockDistance = MathF.Max(0f, finiteOr(candidate.UndockDistance, 42f)), }; } }