diff --git a/Content.Server/Power/EntitySystems/PowerNetSystem.cs b/Content.Server/Power/EntitySystems/PowerNetSystem.cs index 2fc3e7c855..a912b1df3b 100644 --- a/Content.Server/Power/EntitySystems/PowerNetSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerNetSystem.cs @@ -30,21 +30,14 @@ namespace Content.Server.Power.EntitySystems private readonly PowerState _powerState = new(); private readonly HashSet _powerNetReconnectQueue = new(); private readonly HashSet _apcNetReconnectQueue = new(); - private BatteryRampPegSolver.UpdateNetworkJob _networkJob; + private float _updateAccumulator; + private const float UpdateInterval = 0.5f; private EntityQuery _apcBatteryQuery; private EntityQuery _batteryQuery; private BatteryRampPegSolver _solver = new(); - public PowerNetSystem() - { - _networkJob = new BatteryRampPegSolver.UpdateNetworkJob - { - Solver = _solver - }; - } - public override void Initialize() { base.Initialize(); @@ -283,95 +276,88 @@ namespace Content.Server.Power.EntitySystems { base.Update(frameTime); + _updateAccumulator += frameTime; + if (_updateAccumulator < UpdateInterval) + return; + + _updateAccumulator = 0; + ReconnectNetworks(); - RecalculateGroupedNets(); + // Synchronize batteries + RaiseLocalEvent(new NetworkBatteryPreSync()); - var preSyncEvent = new NetworkBatteryPreSync(); - RaiseLocalEvent(preSyncEvent); + // Run power solver. + _solver.Tick(frameTime, _powerState, _parMan); - if (_powerState.GroupedNets != null) + // Synchronize batteries, the other way around. + RaiseLocalEvent(new NetworkBatteryPostSync()); + + var apcPowerReceiverQuery = AllEntityQuery(); + while (apcPowerReceiverQuery.MoveNext(out var uid, out var apcReceiver)) { - foreach (var group in _powerState.GroupedNets) - { - if (group.Count > 100) - { - try - { - _networkJob.Networks = group; - _networkJob.State = _powerState; - _networkJob.FrameTime = frameTime; - _parMan.ProcessNow(_networkJob, group.Count); - } - catch (Exception e) - { - foreach (var network in group) - { - _solver.UpdateNetwork(network, _powerState, frameTime); - } - } - } - else - { - foreach (var network in group) - { - _solver.UpdateNetwork(network, _powerState, frameTime); - } - } - } + UpdateApcPowerReceiverSingle(uid, apcReceiver, frameTime); } - var postSyncEvent = new NetworkBatteryPostSync(); - RaiseLocalEvent(postSyncEvent); - - ValidatePowerState(); - - UpdateComponents(frameTime); - } - - private void ValidatePowerState() - { - foreach (var battery in _powerState.Batteries.Values) + var powerConsumererQuery = AllEntityQuery(); + while (powerConsumererQuery.MoveNext(out var uid, out var consumer)) { - if (battery.CurrentStorage < 0 || battery.CurrentStorage > battery.Capacity) - { - battery.CurrentStorage = Math.Clamp(battery.CurrentStorage, 0, battery.Capacity); - } + UpdatePowerConsumerSingle(uid, consumer); + } + + var powerNetworkBatteryQuery = AllEntityQuery(); + while (powerNetworkBatteryQuery.MoveNext(out var uid, out var powerNetBattery)) + { + UpdateNetworkBatterySingle(uid, powerNetBattery); } } - private void UpdateComponents(float frameTime) - { - var apcQuery = EntityQueryEnumerator(); - var consumerQuery = EntityQueryEnumerator(); - var batteryQuery = EntityQueryEnumerator(); - - while (apcQuery.MoveNext(out var uid, out var apcReceiver)) - { - UpdateApcReceiver(uid, apcReceiver, frameTime); - } - - while (consumerQuery.MoveNext(out var uid, out var consumer)) - { - UpdateConsumer(uid, consumer); - } - - while (batteryQuery.MoveNext(out var uid, out var battery)) - { - UpdateBattery(uid, battery); - } - } - - private void UpdateApcReceiver(EntityUid uid, ApcPowerReceiverComponent apcReceiver, float frameTime) + private void UpdateApcPowerReceiverSingle(EntityUid uid, ApcPowerReceiverComponent apcReceiver, float frameTime) { var mapId = Transform(uid).MapUid; var isAlwaysPowered = HasComp(mapId); + var powered = isAlwaysPowered || IsPoweredCalculate(apcReceiver); + MetaDataComponent? metadata = null; + + if (_apcBatteryQuery.TryComp(uid, out var apcBattery) && _batteryQuery.TryComp(uid, out var battery)) + { + apcReceiver.Load = apcBattery.IdleLoad; + + var requireBattery = !powered && !apcReceiver.PowerDisabled; + + if (requireBattery) + { + _battery.SetCharge(uid, battery.CurrentCharge - apcBattery.IdleLoad * frameTime, battery); + } + else if (powered && !_battery.IsFull(uid, battery)) + { + apcReceiver.Load += apcBattery.BatteryRechargeRate * apcBattery.BatteryRechargeEfficiency; + _battery.SetCharge(uid, battery.CurrentCharge + apcBattery.BatteryRechargeRate * frameTime, battery); + } + + var enableBattery = requireBattery && battery.CurrentCharge > 0; + + if (apcBattery.Enabled != enableBattery) + { + apcBattery.Enabled = enableBattery; + metadata = MetaData(uid); + Dirty(uid, apcBattery, metadata); + + var apcBatteryEv = new ApcPowerReceiverBatteryChangedEvent(enableBattery); + RaiseLocalEvent(uid, ref apcBatteryEv); + + _appearance.SetData(uid, PowerDeviceVisuals.BatteryPowered, enableBattery); + } + + powered |= enableBattery; + } + if (!apcReceiver.Recalculate && apcReceiver.Powered == powered) return; - var metadata = MetaData(uid); + metadata ??= MetaData(uid); if (Paused(uid, metadata)) return; @@ -383,21 +369,22 @@ namespace Content.Server.Power.EntitySystems RaiseLocalEvent(uid, ref ev); } - private void UpdateConsumer(EntityUid uid, PowerConsumerComponent consumer) + private void UpdatePowerConsumerSingle(EntityUid uid, PowerConsumerComponent consumer) { var mapId = Transform(uid).MapUid; var isAlwaysPowered = HasComp(mapId); var newRecv = isAlwaysPowered ? consumer.DrawRate : consumer.NetworkLoad.ReceivingPower; - if (MathHelper.CloseToPercent(consumer.LastReceived, newRecv)) + ref var lastRecv = ref consumer.LastReceived; + if (MathHelper.CloseToPercent(lastRecv, newRecv)) return; - consumer.LastReceived = newRecv; + lastRecv = newRecv; var msg = new PowerConsumerReceivedChanged(newRecv, consumer.DrawRate); RaiseLocalEvent(uid, ref msg); } - private void UpdateBattery(EntityUid uid, PowerNetworkBatteryComponent powerNetBattery) + private void UpdateNetworkBatterySingle(EntityUid uid, PowerNetworkBatteryComponent powerNetBattery) { var mapId = Transform(uid).MapUid; var isAlwaysPowered = HasComp(mapId); @@ -440,8 +427,6 @@ namespace Content.Server.Power.EntitySystems } _powerNetReconnectQueue.Clear(); - - _powerState.GroupedNets = null; } private bool IsPoweredCalculate(ApcPowerReceiverComponent comp) @@ -558,53 +543,6 @@ namespace Content.Server.Power.EntitySystems { _solver.Validate(_powerState); } - - private void RecalculateGroupedNets() - { - if (_powerState.GroupedNets != null) - return; - - var nets = _powerState.Networks.Values.ToList(); - var groups = new List>(); - var visited = new HashSet(); - - foreach (var net in nets) - { - if (visited.Contains(net)) - continue; - - var group = new List(); - var queue = new Queue(); - queue.Enqueue(net); - visited.Add(net); - - while (queue.Count > 0) - { - var current = queue.Dequeue(); - group.Add(current); - - foreach (var batteryId in current.BatteryLoads.Concat(current.BatterySupplies)) - { - var battery = _powerState.Batteries[batteryId]; - if (battery.LinkedNetworkCharging != default && !visited.Contains(_powerState.Networks[battery.LinkedNetworkCharging])) - { - queue.Enqueue(_powerState.Networks[battery.LinkedNetworkCharging]); - visited.Add(_powerState.Networks[battery.LinkedNetworkCharging]); - } - if (battery.LinkedNetworkDischarging != default && !visited.Contains(_powerState.Networks[battery.LinkedNetworkDischarging])) - { - queue.Enqueue(_powerState.Networks[battery.LinkedNetworkDischarging]); - visited.Add(_powerState.Networks[battery.LinkedNetworkDischarging]); - } - } - } - - if (group.Count > 0) - groups.Add(group); - } - - _powerState.GroupedNets = groups; - } } /// diff --git a/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs b/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs index 22c407b8e3..71773f0c5a 100644 --- a/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs +++ b/Content.Server/Power/Pow3r/BatteryRampPegSolver.cs @@ -114,6 +114,7 @@ namespace Content.Server.Power.Pow3r if (!load.Enabled || load.Paused) continue; + DebugTools.Assert(load.DesiredPower >= 0); demand += load.DesiredPower; } @@ -188,8 +189,6 @@ namespace Content.Server.Power.Pow3r battery.AvailableSupply = Math.Min(scaledSpace, supplyAndPassthrough); battery.LoadingNetworkDemand = unmet; - battery.MaxEffectiveSupply = Math.Min(battery.CurrentStorage / frameTime, - battery.MaxSupply + battery.CurrentReceiving * battery.Efficiency); battery.MaxEffectiveSupply = Math.Min(battery.CurrentStorage / frameTime, battery.MaxSupply + battery.CurrentReceiving * battery.Efficiency); totalBatterySupply += battery.AvailableSupply; diff --git a/Content.Server/Power/Pow3r/PowerState.cs b/Content.Server/Power/Pow3r/PowerState.cs index 73639e9838..34926124ab 100644 --- a/Content.Server/Power/Pow3r/PowerState.cs +++ b/Content.Server/Power/Pow3r/PowerState.cs @@ -127,24 +127,6 @@ namespace Content.Server.Power.Pow3r _storage = Array.Empty(); } - public GenIdStorage(int initialCapacity) - { - if (initialCapacity <= 0) - throw new ArgumentException("Initial capacity must be greater than 0", nameof(initialCapacity)); - - _storage = new Slot[initialCapacity]; - - for (var i = 0; i < initialCapacity - 1; i++) - { - _storage[i].NextSlot = i + 1; - _storage[i].Generation = 1; - } - _storage[initialCapacity - 1].NextSlot = int.MaxValue; - _storage[initialCapacity - 1].Generation = 1; - - _nextFree = 0; - } - public static GenIdStorage FromEnumerable(IEnumerable<(NodeId, T)> enumerable) { var storage = new GenIdStorage(); diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index d68ab16874..87b2da129a 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -36,5 +36,5 @@ public sealed partial class CCVars : CVars /// Set to true to disable parallel processing in the pow3r solver. /// public static readonly CVarDef DebugPow3rDisableParallel = - CVarDef.Create("debug.pow3r_disable_parallel", true, CVar.SERVERONLY); + CVarDef.Create("debug.pow3r_disable_parallel", false, CVar.SERVERONLY); } diff --git a/Resources/Locale/ru-RU/_strings/_lib.ftl b/Resources/Locale/ru-RU/_strings/_lib.ftl index ad4345852b..cc2204c811 100644 --- a/Resources/Locale/ru-RU/_strings/_lib.ftl +++ b/Resources/Locale/ru-RU/_strings/_lib.ftl @@ -25,12 +25,23 @@ zzzz-fmt-power-watts = # Therefore 1 kilowatt-hour is equal to 3,600,000 joules (3.6MJ) zzzz-fmt-power-joules = { TOSTRING($divided, "F1") } { $places -> - [0] Дж - [1] кДж - [2] МДж - [3] ГДж - [4] ТДж - *[5] ??? - } + [0] Дж + [1] кДж + [2] МДж + [3] ГДж + [4] ТДж + *[5] ??? + } + +# Used internally by the ENERGYWATTHOURS() function. +zzzz-fmt-energy-watt-hours = { TOSTRING($divided, "F1") } { $places -> + [0] Wh + [1] kWh + [2] MWh + [3] GWh + [4] TWh + *[5] ??? +} + # Used internally by the PLAYTIME() function. zzzz-fmt-playtime = { $hours }Ч { $minutes }М diff --git a/Resources/Locale/ru-RU/_strings/power/battery.ftl b/Resources/Locale/ru-RU/_strings/power/battery.ftl new file mode 100644 index 0000000000..21ba96c226 --- /dev/null +++ b/Resources/Locale/ru-RU/_strings/power/battery.ftl @@ -0,0 +1,22 @@ +## Strings for the battery (SMES/substation) menu + +battery-menu-footer-left = Danger: high voltage +battery-menu-footer-right = 7.2 REV 6 +battery-menu-out = OUT +battery-menu-in = IN +battery-menu-charge-header = Charge Circuit +battery-menu-discharge-header = Discharge Circuit +battery-menu-storage-header = Storage Cells +battery-menu-passthrough = Passthrough +battery-menu-max = Max: +battery-menu-current = Current: +battery-menu-stored = Stored: +battery-menu-energy = Energy: +battery-menu-eta-full = ETA (full): +battery-menu-eta-empty = ETA (empty): +battery-menu-eta-value = ~{ $minutes } min +battery-menu-eta-value-max = >{ $minutes } min +battery-menu-eta-value-na = N/A +battery-menu-power-value = { POWERWATTS($value) } +battery-menu-stored-percent-value = { TOSTRING($value, "P1") } +battery-menu-stored-energy-value = { ENERGYWATTHOURS($value) }