PowerNetSystem.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. using System.Linq;
  2. using Content.Server.NodeContainer.EntitySystems;
  3. using Content.Server.Power.Components;
  4. using Content.Server.Power.NodeGroups;
  5. using Content.Server.Power.Pow3r;
  6. using Content.Shared.CCVar;
  7. using Content.Shared.Power;
  8. using Content.Shared.Power.Components;
  9. using Content.Shared.Power.EntitySystems;
  10. using JetBrains.Annotations;
  11. using Robust.Server.GameObjects;
  12. using Robust.Shared.Configuration;
  13. using Robust.Shared.Threading;
  14. namespace Content.Server.Power.EntitySystems
  15. {
  16. /// <summary>
  17. /// Manages power networks, power state, and all power components.
  18. /// </summary>
  19. [UsedImplicitly]
  20. public sealed class PowerNetSystem : SharedPowerNetSystem
  21. {
  22. [Dependency] private readonly AppearanceSystem _appearance = default!;
  23. [Dependency] private readonly PowerNetConnectorSystem _powerNetConnector = default!;
  24. [Dependency] private readonly IConfigurationManager _cfg = default!;
  25. [Dependency] private readonly IParallelManager _parMan = default!;
  26. [Dependency] private readonly BatterySystem _battery = default!;
  27. private readonly PowerState _powerState = new();
  28. private readonly HashSet<PowerNet> _powerNetReconnectQueue = new();
  29. private readonly HashSet<ApcNet> _apcNetReconnectQueue = new();
  30. private EntityQuery<ApcPowerReceiverBatteryComponent> _apcBatteryQuery;
  31. private EntityQuery<AppearanceComponent> _appearanceQuery;
  32. private EntityQuery<BatteryComponent> _batteryQuery;
  33. private BatteryRampPegSolver _solver = new();
  34. public override void Initialize()
  35. {
  36. base.Initialize();
  37. _apcBatteryQuery = GetEntityQuery<ApcPowerReceiverBatteryComponent>();
  38. _appearanceQuery = GetEntityQuery<AppearanceComponent>();
  39. _batteryQuery = GetEntityQuery<BatteryComponent>();
  40. UpdatesAfter.Add(typeof(NodeGroupSystem));
  41. _solver = new(_cfg.GetCVar(CCVars.DebugPow3rDisableParallel));
  42. SubscribeLocalEvent<ApcPowerReceiverComponent, ComponentInit>(ApcPowerReceiverInit);
  43. SubscribeLocalEvent<ApcPowerReceiverComponent, ComponentShutdown>(ApcPowerReceiverShutdown);
  44. SubscribeLocalEvent<ApcPowerReceiverComponent, ComponentRemove>(ApcPowerReceiverRemove);
  45. SubscribeLocalEvent<ApcPowerReceiverComponent, EntityPausedEvent>(ApcPowerReceiverPaused);
  46. SubscribeLocalEvent<ApcPowerReceiverComponent, EntityUnpausedEvent>(ApcPowerReceiverUnpaused);
  47. SubscribeLocalEvent<PowerNetworkBatteryComponent, ComponentInit>(BatteryInit);
  48. SubscribeLocalEvent<PowerNetworkBatteryComponent, ComponentShutdown>(BatteryShutdown);
  49. SubscribeLocalEvent<PowerNetworkBatteryComponent, EntityPausedEvent>(BatteryPaused);
  50. SubscribeLocalEvent<PowerNetworkBatteryComponent, EntityUnpausedEvent>(BatteryUnpaused);
  51. SubscribeLocalEvent<PowerConsumerComponent, ComponentInit>(PowerConsumerInit);
  52. SubscribeLocalEvent<PowerConsumerComponent, ComponentShutdown>(PowerConsumerShutdown);
  53. SubscribeLocalEvent<PowerConsumerComponent, EntityPausedEvent>(PowerConsumerPaused);
  54. SubscribeLocalEvent<PowerConsumerComponent, EntityUnpausedEvent>(PowerConsumerUnpaused);
  55. SubscribeLocalEvent<PowerSupplierComponent, ComponentInit>(PowerSupplierInit);
  56. SubscribeLocalEvent<PowerSupplierComponent, ComponentShutdown>(PowerSupplierShutdown);
  57. SubscribeLocalEvent<PowerSupplierComponent, EntityPausedEvent>(PowerSupplierPaused);
  58. SubscribeLocalEvent<PowerSupplierComponent, EntityUnpausedEvent>(PowerSupplierUnpaused);
  59. Subs.CVar(_cfg, CCVars.DebugPow3rDisableParallel, DebugPow3rDisableParallelChanged);
  60. }
  61. private void DebugPow3rDisableParallelChanged(bool val)
  62. {
  63. _solver = new(val);
  64. }
  65. private void ApcPowerReceiverInit(EntityUid uid, ApcPowerReceiverComponent component, ComponentInit args)
  66. {
  67. AllocLoad(component.NetworkLoad);
  68. }
  69. private void ApcPowerReceiverShutdown(EntityUid uid, ApcPowerReceiverComponent component,
  70. ComponentShutdown args)
  71. {
  72. _powerState.Loads.Free(component.NetworkLoad.Id);
  73. }
  74. private void ApcPowerReceiverRemove(EntityUid uid, ApcPowerReceiverComponent component, ComponentRemove args)
  75. {
  76. component.Provider?.RemoveReceiver(component);
  77. }
  78. private static void ApcPowerReceiverPaused(
  79. EntityUid uid,
  80. ApcPowerReceiverComponent component,
  81. ref EntityPausedEvent args)
  82. {
  83. component.NetworkLoad.Paused = true;
  84. }
  85. private static void ApcPowerReceiverUnpaused(
  86. EntityUid uid,
  87. ApcPowerReceiverComponent component,
  88. ref EntityUnpausedEvent args)
  89. {
  90. component.NetworkLoad.Paused = false;
  91. }
  92. private void BatteryInit(EntityUid uid, PowerNetworkBatteryComponent component, ComponentInit args)
  93. {
  94. AllocBattery(component.NetworkBattery);
  95. }
  96. private void BatteryShutdown(EntityUid uid, PowerNetworkBatteryComponent component, ComponentShutdown args)
  97. {
  98. _powerState.Batteries.Free(component.NetworkBattery.Id);
  99. }
  100. private static void BatteryPaused(EntityUid uid, PowerNetworkBatteryComponent component, ref EntityPausedEvent args)
  101. {
  102. component.NetworkBattery.Paused = true;
  103. }
  104. private static void BatteryUnpaused(EntityUid uid, PowerNetworkBatteryComponent component, ref EntityUnpausedEvent args)
  105. {
  106. component.NetworkBattery.Paused = false;
  107. }
  108. private void PowerConsumerInit(EntityUid uid, PowerConsumerComponent component, ComponentInit args)
  109. {
  110. _powerNetConnector.BaseNetConnectorInit(component);
  111. AllocLoad(component.NetworkLoad);
  112. }
  113. private void PowerConsumerShutdown(EntityUid uid, PowerConsumerComponent component, ComponentShutdown args)
  114. {
  115. _powerState.Loads.Free(component.NetworkLoad.Id);
  116. }
  117. private static void PowerConsumerPaused(EntityUid uid, PowerConsumerComponent component, ref EntityPausedEvent args)
  118. {
  119. component.NetworkLoad.Paused = true;
  120. }
  121. private static void PowerConsumerUnpaused(EntityUid uid, PowerConsumerComponent component, ref EntityUnpausedEvent args)
  122. {
  123. component.NetworkLoad.Paused = false;
  124. }
  125. private void PowerSupplierInit(EntityUid uid, PowerSupplierComponent component, ComponentInit args)
  126. {
  127. _powerNetConnector.BaseNetConnectorInit(component);
  128. AllocSupply(component.NetworkSupply);
  129. }
  130. private void PowerSupplierShutdown(EntityUid uid, PowerSupplierComponent component, ComponentShutdown args)
  131. {
  132. _powerState.Supplies.Free(component.NetworkSupply.Id);
  133. }
  134. private static void PowerSupplierPaused(EntityUid uid, PowerSupplierComponent component, ref EntityPausedEvent args)
  135. {
  136. component.NetworkSupply.Paused = true;
  137. }
  138. private static void PowerSupplierUnpaused(EntityUid uid, PowerSupplierComponent component, ref EntityUnpausedEvent args)
  139. {
  140. component.NetworkSupply.Paused = false;
  141. }
  142. public void InitPowerNet(PowerNet powerNet)
  143. {
  144. AllocNetwork(powerNet.NetworkNode);
  145. _powerState.GroupedNets = null;
  146. }
  147. public void DestroyPowerNet(PowerNet powerNet)
  148. {
  149. _powerState.Networks.Free(powerNet.NetworkNode.Id);
  150. _powerState.GroupedNets = null;
  151. }
  152. public void QueueReconnectPowerNet(PowerNet powerNet)
  153. {
  154. _powerNetReconnectQueue.Add(powerNet);
  155. _powerState.GroupedNets = null;
  156. }
  157. public void InitApcNet(ApcNet apcNet)
  158. {
  159. AllocNetwork(apcNet.NetworkNode);
  160. _powerState.GroupedNets = null;
  161. }
  162. public void DestroyApcNet(ApcNet apcNet)
  163. {
  164. _powerState.Networks.Free(apcNet.NetworkNode.Id);
  165. _powerState.GroupedNets = null;
  166. }
  167. public void QueueReconnectApcNet(ApcNet apcNet)
  168. {
  169. _apcNetReconnectQueue.Add(apcNet);
  170. _powerState.GroupedNets = null;
  171. }
  172. public PowerStatistics GetStatistics()
  173. {
  174. return new()
  175. {
  176. CountBatteries = _powerState.Batteries.Count,
  177. CountLoads = _powerState.Loads.Count,
  178. CountNetworks = _powerState.Networks.Count,
  179. CountSupplies = _powerState.Supplies.Count
  180. };
  181. }
  182. public NetworkPowerStatistics GetNetworkStatistics(PowerState.Network network)
  183. {
  184. // Right, consumption. Now this is a big mess.
  185. // Start by summing up consumer draw rates.
  186. // Then deal with batteries.
  187. // While for consumers we want to use their max draw rates,
  188. // for batteries we ought to use their current draw rates,
  189. // because there's all sorts of weirdness with them.
  190. // A full battery will still have the same max draw rate,
  191. // but will likely have deliberately limited current draw rate.
  192. float consumptionW = network.Loads.Sum(s => _powerState.Loads[s].DesiredPower);
  193. consumptionW += network.BatteryLoads.Sum(s => _powerState.Batteries[s].CurrentReceiving);
  194. // This is interesting because LastMaxSupplySum seems to match LastAvailableSupplySum for some reason.
  195. // I suspect it's accounting for current supply rather than theoretical supply.
  196. float maxSupplyW = network.Supplies.Sum(s => _powerState.Supplies[s].MaxSupply);
  197. // Battery stuff is more complex.
  198. // Without stealing PowerState, the most efficient way
  199. // to grab the necessary discharge data is from
  200. // PowerNetworkBatteryComponent (has Pow3r reference).
  201. float supplyBatteriesW = 0.0f;
  202. float storageCurrentJ = 0.0f;
  203. float storageMaxJ = 0.0f;
  204. foreach (var discharger in network.BatterySupplies)
  205. {
  206. var nb = _powerState.Batteries[discharger];
  207. supplyBatteriesW += nb.CurrentSupply;
  208. storageCurrentJ += nb.CurrentStorage;
  209. storageMaxJ += nb.Capacity;
  210. maxSupplyW += nb.MaxSupply;
  211. }
  212. // And charging
  213. float outStorageCurrentJ = 0.0f;
  214. float outStorageMaxJ = 0.0f;
  215. foreach (var charger in network.BatteryLoads)
  216. {
  217. var nb = _powerState.Batteries[charger];
  218. outStorageCurrentJ += nb.CurrentStorage;
  219. outStorageMaxJ += nb.Capacity;
  220. }
  221. return new()
  222. {
  223. SupplyCurrent = network.LastCombinedMaxSupply,
  224. SupplyBatteries = supplyBatteriesW,
  225. SupplyTheoretical = maxSupplyW,
  226. Consumption = consumptionW,
  227. InStorageCurrent = storageCurrentJ,
  228. InStorageMax = storageMaxJ,
  229. OutStorageCurrent = outStorageCurrentJ,
  230. OutStorageMax = outStorageMaxJ
  231. };
  232. }
  233. public override void Update(float frameTime)
  234. {
  235. base.Update(frameTime);
  236. ReconnectNetworks();
  237. // Synchronize batteries
  238. RaiseLocalEvent(new NetworkBatteryPreSync());
  239. // Run power solver.
  240. _solver.Tick(frameTime, _powerState, _parMan);
  241. // Synchronize batteries, the other way around.
  242. RaiseLocalEvent(new NetworkBatteryPostSync());
  243. // Send events where necessary.
  244. // TODO: Instead of querying ALL power components every tick, and then checking if an event needs to be
  245. // raised, should probably assemble a list of entity Uids during the actual solver steps.
  246. UpdateApcPowerReceiver(frameTime);
  247. UpdatePowerConsumer();
  248. UpdateNetworkBattery();
  249. }
  250. private void ReconnectNetworks()
  251. {
  252. foreach (var apcNet in _apcNetReconnectQueue)
  253. {
  254. if (apcNet.Removed)
  255. continue;
  256. DoReconnectApcNet(apcNet);
  257. }
  258. _apcNetReconnectQueue.Clear();
  259. foreach (var powerNet in _powerNetReconnectQueue)
  260. {
  261. if (powerNet.Removed)
  262. continue;
  263. DoReconnectPowerNet(powerNet);
  264. }
  265. _powerNetReconnectQueue.Clear();
  266. }
  267. private void UpdateApcPowerReceiver(float frameTime)
  268. {
  269. var enumerator = AllEntityQuery<ApcPowerReceiverComponent>();
  270. while (enumerator.MoveNext(out var uid, out var apcReceiver))
  271. {
  272. var powered = !apcReceiver.PowerDisabled
  273. && (!apcReceiver.NeedsPower
  274. || MathHelper.CloseToPercent(apcReceiver.NetworkLoad.ReceivingPower,
  275. apcReceiver.Load));
  276. MetaDataComponent? metadata = null;
  277. // TODO: If we get archetypes would be better to split this out.
  278. // Check if the entity has an internal battery
  279. if (_apcBatteryQuery.TryComp(uid, out var apcBattery) && _batteryQuery.TryComp(uid, out var battery))
  280. {
  281. apcReceiver.Load = apcBattery.IdleLoad;
  282. // Try to draw power from the battery if there isn't sufficient external power
  283. var requireBattery = !powered && !apcReceiver.PowerDisabled;
  284. if (requireBattery)
  285. {
  286. _battery.SetCharge(uid, battery.CurrentCharge - apcBattery.IdleLoad * frameTime, battery);
  287. }
  288. // Otherwise try to charge the battery
  289. else if (powered && !_battery.IsFull(uid, battery))
  290. {
  291. apcReceiver.Load += apcBattery.BatteryRechargeRate * apcBattery.BatteryRechargeEfficiency;
  292. _battery.SetCharge(uid, battery.CurrentCharge + apcBattery.BatteryRechargeRate * frameTime, battery);
  293. }
  294. // Enable / disable the battery if the state changed
  295. var enableBattery = requireBattery && battery.CurrentCharge > 0;
  296. if (apcBattery.Enabled != enableBattery)
  297. {
  298. apcBattery.Enabled = enableBattery;
  299. metadata = MetaData(uid);
  300. Dirty(uid, apcBattery, metadata);
  301. var apcBatteryEv = new ApcPowerReceiverBatteryChangedEvent(enableBattery);
  302. RaiseLocalEvent(uid, ref apcBatteryEv);
  303. _appearance.SetData(uid, PowerDeviceVisuals.BatteryPowered, enableBattery);
  304. }
  305. powered |= enableBattery;
  306. }
  307. // If new value is the same as the old, then exit
  308. if (!apcReceiver.Recalculate && apcReceiver.Powered == powered)
  309. continue;
  310. metadata ??= MetaData(uid);
  311. if (Paused(uid, metadata))
  312. continue;
  313. apcReceiver.Recalculate = false;
  314. apcReceiver.Powered = powered;
  315. Dirty(uid, apcReceiver, metadata);
  316. var ev = new PowerChangedEvent(powered, apcReceiver.NetworkLoad.ReceivingPower);
  317. RaiseLocalEvent(uid, ref ev);
  318. if (_appearanceQuery.TryComp(uid, out var appearance))
  319. _appearance.SetData(uid, PowerDeviceVisuals.Powered, powered, appearance);
  320. }
  321. }
  322. private void UpdatePowerConsumer()
  323. {
  324. var enumerator = EntityQueryEnumerator<PowerConsumerComponent>();
  325. while (enumerator.MoveNext(out var uid, out var consumer))
  326. {
  327. var newRecv = consumer.NetworkLoad.ReceivingPower;
  328. ref var lastRecv = ref consumer.LastReceived;
  329. if (MathHelper.CloseToPercent(lastRecv, newRecv))
  330. continue;
  331. lastRecv = newRecv;
  332. var msg = new PowerConsumerReceivedChanged(newRecv, consumer.DrawRate);
  333. RaiseLocalEvent(uid, ref msg);
  334. }
  335. }
  336. private void UpdateNetworkBattery()
  337. {
  338. var enumerator = EntityQueryEnumerator<PowerNetworkBatteryComponent>();
  339. while (enumerator.MoveNext(out var uid, out var powerNetBattery))
  340. {
  341. var lastSupply = powerNetBattery.LastSupply;
  342. var currentSupply = powerNetBattery.CurrentSupply;
  343. if (lastSupply == 0f && currentSupply != 0f)
  344. {
  345. var ev = new PowerNetBatterySupplyEvent(true);
  346. RaiseLocalEvent(uid, ref ev);
  347. }
  348. else if (lastSupply > 0f && currentSupply == 0f)
  349. {
  350. var ev = new PowerNetBatterySupplyEvent(false);
  351. RaiseLocalEvent(uid, ref ev);
  352. }
  353. powerNetBattery.LastSupply = currentSupply;
  354. }
  355. }
  356. private void AllocLoad(PowerState.Load load)
  357. {
  358. _powerState.Loads.Allocate(out load.Id) = load;
  359. }
  360. private void AllocSupply(PowerState.Supply supply)
  361. {
  362. _powerState.Supplies.Allocate(out supply.Id) = supply;
  363. }
  364. private void AllocBattery(PowerState.Battery battery)
  365. {
  366. _powerState.Batteries.Allocate(out battery.Id) = battery;
  367. }
  368. private void AllocNetwork(PowerState.Network network)
  369. {
  370. _powerState.Networks.Allocate(out network.Id) = network;
  371. }
  372. private void DoReconnectApcNet(ApcNet net)
  373. {
  374. var netNode = net.NetworkNode;
  375. netNode.Loads.Clear();
  376. netNode.BatterySupplies.Clear();
  377. netNode.BatteryLoads.Clear();
  378. netNode.Supplies.Clear();
  379. foreach (var provider in net.Providers)
  380. {
  381. foreach (var receiver in provider.LinkedReceivers)
  382. {
  383. netNode.Loads.Add(receiver.NetworkLoad.Id);
  384. receiver.NetworkLoad.LinkedNetwork = netNode.Id;
  385. }
  386. }
  387. DoReconnectBasePowerNet(net, netNode);
  388. var batteryQuery = GetEntityQuery<PowerNetworkBatteryComponent>();
  389. foreach (var apc in net.Apcs)
  390. {
  391. var netBattery = batteryQuery.GetComponent(apc.Owner);
  392. netNode.BatterySupplies.Add(netBattery.NetworkBattery.Id);
  393. netBattery.NetworkBattery.LinkedNetworkDischarging = netNode.Id;
  394. }
  395. }
  396. private void DoReconnectPowerNet(PowerNet net)
  397. {
  398. var netNode = net.NetworkNode;
  399. netNode.Loads.Clear();
  400. netNode.Supplies.Clear();
  401. netNode.BatteryLoads.Clear();
  402. netNode.BatterySupplies.Clear();
  403. DoReconnectBasePowerNet(net, netNode);
  404. var batteryQuery = GetEntityQuery<PowerNetworkBatteryComponent>();
  405. foreach (var charger in net.Chargers)
  406. {
  407. var battery = batteryQuery.GetComponent(charger.Owner);
  408. netNode.BatteryLoads.Add(battery.NetworkBattery.Id);
  409. battery.NetworkBattery.LinkedNetworkCharging = netNode.Id;
  410. }
  411. foreach (var discharger in net.Dischargers)
  412. {
  413. var battery = batteryQuery.GetComponent(discharger.Owner);
  414. netNode.BatterySupplies.Add(battery.NetworkBattery.Id);
  415. battery.NetworkBattery.LinkedNetworkDischarging = netNode.Id;
  416. }
  417. }
  418. private void DoReconnectBasePowerNet<TNetType>(BasePowerNet<TNetType> net, PowerState.Network netNode)
  419. where TNetType : IBasePowerNet
  420. {
  421. foreach (var consumer in net.Consumers)
  422. {
  423. netNode.Loads.Add(consumer.NetworkLoad.Id);
  424. consumer.NetworkLoad.LinkedNetwork = netNode.Id;
  425. }
  426. foreach (var supplier in net.Suppliers)
  427. {
  428. netNode.Supplies.Add(supplier.NetworkSupply.Id);
  429. supplier.NetworkSupply.LinkedNetwork = netNode.Id;
  430. }
  431. }
  432. }
  433. /// <summary>
  434. /// Raised before power network simulation happens, to synchronize battery state from
  435. /// components like <see cref="BatteryComponent"/> into <see cref="PowerNetworkBatteryComponent"/>.
  436. /// </summary>
  437. public readonly struct NetworkBatteryPreSync
  438. {
  439. }
  440. /// <summary>
  441. /// Raised after power network simulation happens, to synchronize battery charge changes from
  442. /// <see cref="PowerNetworkBatteryComponent"/> to components like <see cref="BatteryComponent"/>.
  443. /// </summary>
  444. public readonly struct NetworkBatteryPostSync
  445. {
  446. }
  447. /// <summary>
  448. /// Raised when the amount of receiving power on a <see cref="PowerConsumerComponent"/> changes.
  449. /// </summary>
  450. [ByRefEvent]
  451. public readonly record struct PowerConsumerReceivedChanged(float ReceivedPower, float DrawRate)
  452. {
  453. public readonly float ReceivedPower = ReceivedPower;
  454. public readonly float DrawRate = DrawRate;
  455. }
  456. /// <summary>
  457. /// Raised whenever a <see cref="PowerNetworkBatteryComponent"/> changes from / to 0 CurrentSupply.
  458. /// </summary>
  459. [ByRefEvent]
  460. public readonly record struct PowerNetBatterySupplyEvent(bool Supply)
  461. {
  462. public readonly bool Supply = Supply;
  463. }
  464. public struct PowerStatistics
  465. {
  466. public int CountNetworks;
  467. public int CountLoads;
  468. public int CountSupplies;
  469. public int CountBatteries;
  470. }
  471. public struct NetworkPowerStatistics
  472. {
  473. public float SupplyCurrent;
  474. public float SupplyBatteries;
  475. public float SupplyTheoretical;
  476. public float Consumption;
  477. public float InStorageCurrent;
  478. public float InStorageMax;
  479. public float OutStorageCurrent;
  480. public float OutStorageMax;
  481. }
  482. }