| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017 |
- using Content.Server.NodeContainer;
- using Content.Server.NodeContainer.EntitySystems;
- using Content.Server.NodeContainer.Nodes;
- using Content.Server.Power.Components;
- using Content.Server.Power.Nodes;
- using Content.Server.Power.NodeGroups;
- using Content.Server.StationEvents.Components;
- using Content.Shared.GameTicking.Components;
- using Content.Shared.Pinpointer;
- using Content.Shared.Station.Components;
- using Content.Shared.Power;
- using JetBrains.Annotations;
- using Robust.Server.GameObjects;
- using Robust.Shared.Map.Components;
- using Robust.Shared.Utility;
- using System.Linq;
- namespace Content.Server.Power.EntitySystems;
- [UsedImplicitly]
- internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitoringConsoleSystem
- {
- [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
- [Dependency] private readonly SharedMapSystem _sharedMapSystem = default!;
- // Note: this data does not need to be saved
- private Dictionary<EntityUid, Dictionary<Vector2i, PowerCableChunk>> _gridPowerCableChunks = new();
- private float _updateTimer = 1.0f;
- private const float UpdateTime = 1.0f;
- private const float RoguePowerConsumerThreshold = 100000;
- public override void Initialize()
- {
- base.Initialize();
- // Console events
- SubscribeLocalEvent<PowerMonitoringConsoleComponent, ComponentInit>(OnConsoleInit);
- SubscribeLocalEvent<PowerMonitoringConsoleComponent, EntParentChangedMessage>(OnConsoleParentChanged);
- SubscribeLocalEvent<PowerMonitoringCableNetworksComponent, ComponentInit>(OnCableNetworksInit);
- SubscribeLocalEvent<PowerMonitoringCableNetworksComponent, EntParentChangedMessage>(OnCableNetworksParentChanged);
- // UI events
- SubscribeLocalEvent<PowerMonitoringConsoleComponent, PowerMonitoringConsoleMessage>(OnPowerMonitoringConsoleMessage);
- SubscribeLocalEvent<PowerMonitoringConsoleComponent, BoundUIOpenedEvent>(OnBoundUIOpened);
- // Grid events
- SubscribeLocalEvent<GridSplitEvent>(OnGridSplit);
- SubscribeLocalEvent<CableComponent, CableAnchorStateChangedEvent>(OnCableAnchorStateChanged);
- SubscribeLocalEvent<PowerMonitoringDeviceComponent, AnchorStateChangedEvent>(OnDeviceAnchoringChanged);
- SubscribeLocalEvent<PowerMonitoringDeviceComponent, NodeGroupsRebuilt>(OnNodeGroupRebuilt);
- // Game rule events
- SubscribeLocalEvent<GameRuleStartedEvent>(OnPowerGridCheckStarted);
- SubscribeLocalEvent<GameRuleEndedEvent>(OnPowerGridCheckEnded);
- }
- #region EventHandling
- private void OnConsoleInit(EntityUid uid, PowerMonitoringConsoleComponent component, ComponentInit args)
- {
- RefreshPowerMonitoringConsole(uid, component);
- }
- private void OnConsoleParentChanged(EntityUid uid, PowerMonitoringConsoleComponent component, EntParentChangedMessage args)
- {
- RefreshPowerMonitoringConsole(uid, component);
- }
- private void OnCableNetworksInit(EntityUid uid, PowerMonitoringCableNetworksComponent component, ComponentInit args)
- {
- RefreshPowerMonitoringCableNetworks(uid, component);
- }
- private void OnCableNetworksParentChanged(EntityUid uid, PowerMonitoringCableNetworksComponent component, EntParentChangedMessage args)
- {
- RefreshPowerMonitoringCableNetworks(uid, component);
- }
- private void OnPowerMonitoringConsoleMessage(EntityUid uid, PowerMonitoringConsoleComponent component, PowerMonitoringConsoleMessage args)
- {
- var focus = EntityManager.GetEntity(args.FocusDevice);
- var group = args.FocusGroup;
- // Update this if the focus device has changed
- if (component.Focus != focus)
- {
- component.Focus = focus;
- if (TryComp<PowerMonitoringCableNetworksComponent>(uid, out var cableNetworks))
- {
- cableNetworks.FocusChunks.Clear(); // Component will be dirtied when these chunks are rebuilt, unless the focus is null
- if (focus == null)
- Dirty(uid, cableNetworks);
- }
- }
- // Update this if the focus group has changed
- if (component.FocusGroup != group)
- {
- component.FocusGroup = args.FocusGroup;
- Dirty(uid, component);
- }
- }
- private void OnBoundUIOpened(EntityUid uid, PowerMonitoringConsoleComponent component, BoundUIOpenedEvent args)
- {
- component.Focus = null;
- component.FocusGroup = PowerMonitoringConsoleGroup.Generator;
- if (TryComp<PowerMonitoringCableNetworksComponent>(uid, out var cableNetworks))
- {
- cableNetworks.FocusChunks.Clear();
- Dirty(uid, cableNetworks);
- }
- }
- private void OnGridSplit(ref GridSplitEvent args)
- {
- // Collect grids
- var allGrids = args.NewGrids.ToList();
- if (!allGrids.Contains(args.Grid))
- allGrids.Add(args.Grid);
- // Refresh affected power cable grids
- foreach (var grid in allGrids)
- {
- if (!TryComp<MapGridComponent>(grid, out var map))
- continue;
- RefreshPowerCableGrid(grid, map);
- }
- // Update power monitoring consoles that stand upon an updated grid
- var query = AllEntityQuery<PowerMonitoringConsoleComponent, PowerMonitoringCableNetworksComponent, TransformComponent>();
- while (query.MoveNext(out var ent, out var entConsole, out var entCableNetworks, out var entXform))
- {
- if (entXform.GridUid == null)
- continue;
- if (!allGrids.Contains(entXform.GridUid.Value))
- continue;
- RefreshPowerMonitoringConsole(ent, entConsole);
- RefreshPowerMonitoringCableNetworks(ent, entCableNetworks);
- }
- }
- public void OnCableAnchorStateChanged(EntityUid uid, CableComponent component, CableAnchorStateChangedEvent args)
- {
- var xform = args.Transform;
- if (xform.GridUid == null || !TryComp<MapGridComponent>(xform.GridUid, out var grid))
- return;
- if (!_gridPowerCableChunks.TryGetValue(xform.GridUid.Value, out var allChunks))
- allChunks = new();
- var tile = _sharedMapSystem.LocalToTile(xform.GridUid.Value, grid, xform.Coordinates);
- var chunkOrigin = SharedMapSystem.GetChunkIndices(tile, ChunkSize);
- if (!allChunks.TryGetValue(chunkOrigin, out var chunk))
- {
- chunk = new PowerCableChunk(chunkOrigin);
- allChunks[chunkOrigin] = chunk;
- }
- var relative = SharedMapSystem.GetChunkRelative(tile, ChunkSize);
- var flag = GetFlag(relative);
- if (args.Anchored)
- chunk.PowerCableData[(int) component.CableType] |= flag;
- else
- chunk.PowerCableData[(int) component.CableType] &= ~flag;
- var query = AllEntityQuery<PowerMonitoringCableNetworksComponent, TransformComponent>();
- while (query.MoveNext(out var ent, out var entCableNetworks, out var entXform))
- {
- if (entXform.GridUid != xform.GridUid)
- continue;
- entCableNetworks.AllChunks = allChunks;
- Dirty(ent, entCableNetworks);
- }
- }
- private void OnDeviceAnchoringChanged(EntityUid uid, PowerMonitoringDeviceComponent component, AnchorStateChangedEvent args)
- {
- var xform = Transform(uid);
- var gridUid = xform.GridUid;
- if (gridUid == null)
- return;
- if (component.IsCollectionMasterOrChild)
- AssignEntityAsCollectionMaster(uid, component, xform);
- var query = AllEntityQuery<PowerMonitoringConsoleComponent, TransformComponent>();
- while (query.MoveNext(out var ent, out var entConsole, out var entXform))
- {
- if (gridUid != entXform.GridUid)
- continue;
- if (!args.Anchored)
- {
- entConsole.PowerMonitoringDeviceMetaData.Remove(EntityManager.GetNetEntity(uid));
- Dirty(ent, entConsole);
- continue;
- }
- var name = MetaData(uid).EntityName;
- var coords = EntityManager.GetNetCoordinates(xform.Coordinates);
- var metaData = new PowerMonitoringDeviceMetaData(name, coords, component.Group, component.SpritePath, component.SpriteState);
- entConsole.PowerMonitoringDeviceMetaData.TryAdd(EntityManager.GetNetEntity(uid), metaData);
- Dirty(ent, entConsole);
- }
- }
- public void OnNodeGroupRebuilt(EntityUid uid, PowerMonitoringDeviceComponent component, NodeGroupsRebuilt args)
- {
- if (component.IsCollectionMasterOrChild)
- AssignEntityAsCollectionMaster(uid, component);
- var query = AllEntityQuery<PowerMonitoringConsoleComponent, PowerMonitoringCableNetworksComponent>();
- while (query.MoveNext(out var _, out var entConsole, out var entCableNetworks))
- {
- if (entConsole.Focus == uid)
- entCableNetworks.FocusChunks.Clear(); // Component is dirtied when these chunks are rebuilt
- }
- }
- private void OnPowerGridCheckStarted(ref GameRuleStartedEvent ev)
- {
- if (!TryComp<PowerGridCheckRuleComponent>(ev.RuleEntity, out var rule))
- return;
- var query = AllEntityQuery<PowerMonitoringConsoleComponent, TransformComponent>();
- while (query.MoveNext(out var uid, out var console, out var xform))
- {
- if (CompOrNull<StationMemberComponent>(xform.GridUid)?.Station == rule.AffectedStation)
- {
- console.Flags |= PowerMonitoringFlags.PowerNetAbnormalities;
- Dirty(uid, console);
- }
- }
- }
- private void OnPowerGridCheckEnded(ref GameRuleEndedEvent ev)
- {
- if (!TryComp<PowerGridCheckRuleComponent>(ev.RuleEntity, out var rule))
- return;
- var query = AllEntityQuery<PowerMonitoringConsoleComponent, TransformComponent>();
- while (query.MoveNext(out var uid, out var console, out var xform))
- {
- if (CompOrNull<StationMemberComponent>(xform.GridUid)?.Station == rule.AffectedStation)
- {
- console.Flags &= ~PowerMonitoringFlags.PowerNetAbnormalities;
- Dirty(uid, console);
- }
- }
- }
- #endregion
- public override void Update(float frameTime)
- {
- base.Update(frameTime);
- _updateTimer += frameTime;
- if (_updateTimer >= UpdateTime)
- {
- _updateTimer -= UpdateTime;
- var query = AllEntityQuery<PowerMonitoringConsoleComponent>();
- while (query.MoveNext(out var ent, out var console))
- {
- if (!_userInterfaceSystem.IsUiOpen(ent, PowerMonitoringConsoleUiKey.Key))
- continue;
- UpdateUIState(ent, console);
- }
- }
- }
- private void UpdateUIState(EntityUid uid, PowerMonitoringConsoleComponent component)
- {
- var consoleXform = Transform(uid);
- if (consoleXform?.GridUid == null)
- return;
- var gridUid = consoleXform.GridUid.Value;
- if (!TryComp<MapGridComponent>(gridUid, out var mapGrid))
- return;
- // The grid must have a NavMapComponent to visualize the map in the UI
- EnsureComp<NavMapComponent>(gridUid);
- // Initializing data to be send to the client
- var totalSources = 0d;
- var totalBatteryUsage = 0d;
- var totalLoads = 0d;
- var allEntries = new List<PowerMonitoringConsoleEntry>();
- var sourcesForFocus = new List<PowerMonitoringConsoleEntry>();
- var loadsForFocus = new List<PowerMonitoringConsoleEntry>();
- var flags = component.Flags;
- // Reset RoguePowerConsumer flag
- component.Flags &= ~PowerMonitoringFlags.RoguePowerConsumer;
- // Record the load value of all non-tracked power consumers on the same grid as the console
- var powerConsumerQuery = AllEntityQuery<PowerConsumerComponent, TransformComponent>();
- while (powerConsumerQuery.MoveNext(out var ent, out var powerConsumer, out var xform))
- {
- if (xform.Anchored == false || xform.GridUid != gridUid)
- continue;
- if (TryComp<PowerMonitoringDeviceComponent>(ent, out var device))
- continue;
- // Flag an alert if power consumption is ridiculous
- if (powerConsumer.ReceivedPower >= RoguePowerConsumerThreshold)
- component.Flags |= PowerMonitoringFlags.RoguePowerConsumer;
- totalLoads += powerConsumer.DrawRate;
- }
- if (component.Flags != flags)
- Dirty(uid, component);
- // Loop over all tracked devices
- var powerMonitoringDeviceQuery = AllEntityQuery<PowerMonitoringDeviceComponent, TransformComponent>();
- while (powerMonitoringDeviceQuery.MoveNext(out var ent, out var device, out var xform))
- {
- // Ignore joint, non-master entities
- if (device.IsCollectionMasterOrChild && !device.IsCollectionMaster)
- continue;
- if (xform.Anchored == false || xform.GridUid != gridUid)
- continue;
- // Get the device power stats
- var powerStats = GetPowerStats(ent, device);
- //, out var powerSupplied, out var powerUsage, out var batteryUsage);
- // Update all running totals
- totalSources += powerStats.PowerSupplied;
- totalLoads += powerStats.PowerUsage;
- totalBatteryUsage += powerStats.BatteryUsage;
- // Continue on if the device is not in the current focus group
- if (device.Group != component.FocusGroup)
- continue;
- // Generate a new console entry with which to populate the UI
- var entry = new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), device.Group, powerStats.PowerValue, powerStats.BatteryLevel);
- allEntries.Add(entry);
- }
- // Update the UI focus data (if applicable)
- if (component.Focus != null)
- {
- if (TryComp<NodeContainerComponent>(component.Focus, out var nodeContainer) &&
- TryComp<PowerMonitoringDeviceComponent>(component.Focus, out var device))
- {
- // Record the tracked sources powering the device
- if (nodeContainer.Nodes.TryGetValue(device.SourceNode, out var sourceNode))
- GetSourcesForNode(component.Focus.Value, sourceNode, out sourcesForFocus);
- // Search for the enabled load node (required for portable generators)
- var loadNodeName = device.LoadNode;
- if (device.LoadNodes != null)
- {
- var foundNode = nodeContainer.Nodes.FirstOrNull(x => x.Value is CableDeviceNode && (x.Value as CableDeviceNode)?.Enabled == true);
- if (foundNode != null)
- loadNodeName = foundNode.Value.Key;
- }
- // Record the tracked loads on the device
- if (nodeContainer.Nodes.TryGetValue(loadNodeName, out var loadNode))
- GetLoadsForNode(component.Focus.Value, loadNode, out loadsForFocus);
- // If the UI focus changed, update the highlighted power network
- if (TryComp<PowerMonitoringCableNetworksComponent>(uid, out var cableNetworks) &&
- cableNetworks.FocusChunks.Count == 0)
- {
- var reachableEntities = new List<EntityUid>();
- if (sourceNode?.NodeGroup != null)
- {
- foreach (var node in sourceNode.NodeGroup.Nodes)
- reachableEntities.Add(node.Owner);
- }
- if (loadNode?.NodeGroup != null)
- {
- foreach (var node in loadNode.NodeGroup.Nodes)
- reachableEntities.Add(node.Owner);
- }
- UpdateFocusNetwork(uid, cableNetworks, gridUid, mapGrid, reachableEntities);
- }
- }
- }
- // Set the UI state
- _userInterfaceSystem.SetUiState(uid,
- PowerMonitoringConsoleUiKey.Key,
- new PowerMonitoringConsoleBoundInterfaceState
- (totalSources,
- totalBatteryUsage,
- totalLoads,
- allEntries.ToArray(),
- sourcesForFocus.ToArray(),
- loadsForFocus.ToArray()));
- }
- private PowerStats GetPowerStats(EntityUid uid, PowerMonitoringDeviceComponent device)
- {
- var stats = new PowerStats();
- if (device.Group == PowerMonitoringConsoleGroup.Generator)
- {
- // This covers most power sources
- if (TryComp<PowerSupplierComponent>(uid, out var supplier))
- {
- stats.PowerValue = supplier.CurrentSupply;
- stats.PowerSupplied += stats.PowerValue;
- }
- // Edge case: radiation collectors
- else if (TryComp<BatteryDischargerComponent>(uid, out var _) &&
- TryComp<PowerNetworkBatteryComponent>(uid, out var battery))
- {
- stats.PowerValue = battery.NetworkBattery.CurrentSupply;
- stats.PowerSupplied += stats.PowerValue;
- stats.BatteryLevel = GetBatteryLevel(uid);
- }
- }
- else if (device.Group == PowerMonitoringConsoleGroup.SMES ||
- device.Group == PowerMonitoringConsoleGroup.Substation ||
- device.Group == PowerMonitoringConsoleGroup.APC)
- {
- if (TryComp<PowerNetworkBatteryComponent>(uid, out var battery))
- {
- stats.BatteryLevel = GetBatteryLevel(uid);
- stats.PowerValue = battery.CurrentSupply;
- // Load due to network battery recharging
- stats.PowerUsage += Math.Max(battery.CurrentReceiving - battery.CurrentSupply, 0d);
- // Track battery usage
- stats.BatteryUsage += Math.Max(battery.CurrentSupply - battery.CurrentReceiving, 0d);
- // Records loads attached to APCs
- if (device.Group == PowerMonitoringConsoleGroup.APC && battery.Enabled)
- {
- stats.PowerUsage += battery.NetworkBattery.LoadingNetworkDemand;
- }
- }
- }
- // Master devices add the power values from all entities they represent (if applicable)
- if (device.IsCollectionMasterOrChild && device.IsCollectionMaster)
- {
- foreach ((var child, var childDevice) in device.ChildDevices)
- {
- if (child == uid)
- continue;
- // Safeguard to prevent infinite loops
- if (childDevice.IsCollectionMaster && childDevice.ChildDevices.ContainsKey(uid))
- continue;
- var childResult = GetPowerStats(child, childDevice);
- stats.PowerValue += childResult.PowerValue;
- stats.PowerSupplied += childResult.PowerSupplied;
- stats.PowerUsage += childResult.PowerUsage;
- stats.BatteryUsage += childResult.BatteryUsage;
- }
- }
- return stats;
- }
- private float? GetBatteryLevel(EntityUid uid)
- {
- if (!TryComp<BatteryComponent>(uid, out var battery))
- return null;
- var effectiveMax = battery.MaxCharge;
- if (effectiveMax == 0)
- effectiveMax = 1;
- return battery.CurrentCharge / effectiveMax;
- }
- private void GetSourcesForNode(EntityUid uid, Node node, out List<PowerMonitoringConsoleEntry> sources)
- {
- sources = new List<PowerMonitoringConsoleEntry>();
- if (node.NodeGroup is not PowerNet netQ)
- return;
- var indexedSources = new Dictionary<EntityUid, PowerMonitoringConsoleEntry>();
- var currentSupply = 0f;
- var currentDemand = 0f;
- foreach (var powerSupplier in netQ.Suppliers)
- {
- var ent = powerSupplier.Owner;
- if (uid == ent)
- continue;
- currentSupply += powerSupplier.CurrentSupply;
- if (TryComp<PowerMonitoringDeviceComponent>(ent, out var entDevice))
- {
- // Combine entities represented by an master into a single entry
- if (entDevice.IsCollectionMasterOrChild && !entDevice.IsCollectionMaster)
- ent = entDevice.CollectionMaster;
- if (indexedSources.TryGetValue(ent, out var entry))
- {
- entry.PowerValue += powerSupplier.CurrentSupply;
- indexedSources[ent] = entry;
- continue;
- }
- indexedSources.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, powerSupplier.CurrentSupply, GetBatteryLevel(ent)));
- }
- }
- foreach (var batteryDischarger in netQ.Dischargers)
- {
- var ent = batteryDischarger.Owner;
- if (uid == ent)
- continue;
- if (!TryComp<PowerNetworkBatteryComponent>(ent, out var entBattery))
- continue;
- currentSupply += entBattery.CurrentSupply;
- if (TryComp<PowerMonitoringDeviceComponent>(ent, out var entDevice))
- {
- // Combine entities represented by an master into a single entry
- if (entDevice.IsCollectionMasterOrChild && !entDevice.IsCollectionMaster)
- ent = entDevice.CollectionMaster;
- if (indexedSources.TryGetValue(ent, out var entry))
- {
- entry.PowerValue += entBattery.CurrentSupply;
- indexedSources[ent] = entry;
- continue;
- }
- indexedSources.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, entBattery.CurrentSupply, GetBatteryLevel(ent)));
- }
- }
- sources = indexedSources.Values.ToList();
- // Get the total demand for the network
- foreach (var powerConsumer in netQ.Consumers)
- {
- currentDemand += powerConsumer.ReceivedPower;
- }
- foreach (var batteryCharger in netQ.Chargers)
- {
- var ent = batteryCharger.Owner;
- if (!TryComp<PowerNetworkBatteryComponent>(ent, out var entBattery))
- continue;
- currentDemand += entBattery.CurrentReceiving;
- }
- // Exit if supply / demand is negligible
- if (MathHelper.CloseTo(currentDemand, 0) || MathHelper.CloseTo(currentSupply, 0))
- return;
- // Work out how much power this device (and those it represents) is actually receiving
- if (!TryComp<PowerNetworkBatteryComponent>(uid, out var battery))
- return;
- var powerUsage = battery.CurrentReceiving;
- if (TryComp<PowerMonitoringDeviceComponent>(uid, out var device) && device.IsCollectionMaster)
- {
- foreach ((var child, var _) in device.ChildDevices)
- {
- if (TryComp<PowerNetworkBatteryComponent>(child, out var childBattery))
- powerUsage += childBattery.CurrentReceiving;
- }
- }
- // Update the power value for each source based on the fraction of power the entity is actually draining from each
- var powerFraction = Math.Min(powerUsage / currentSupply, 1f) * Math.Min(currentSupply / currentDemand, 1f);
- for (int i = 0; i < sources.Count; i++)
- {
- var entry = sources[i];
- sources[i] = new PowerMonitoringConsoleEntry(entry.NetEntity, entry.Group, entry.PowerValue * powerFraction, entry.BatteryLevel);
- }
- }
- private void GetLoadsForNode(EntityUid uid, Node node, out List<PowerMonitoringConsoleEntry> loads, List<EntityUid>? children = null)
- {
- loads = new List<PowerMonitoringConsoleEntry>();
- if (node.NodeGroup is not PowerNet netQ)
- return;
- var indexedLoads = new Dictionary<EntityUid, PowerMonitoringConsoleEntry>();
- var currentDemand = 0f;
- foreach (var powerConsumer in netQ.Consumers)
- {
- var ent = powerConsumer.Owner;
- if (uid == ent)
- continue;
- currentDemand += powerConsumer.ReceivedPower;
- if (TryComp<PowerMonitoringDeviceComponent>(ent, out var entDevice))
- {
- // Combine entities represented by an master into a single entry
- if (entDevice.IsCollectionMasterOrChild && !entDevice.IsCollectionMaster)
- ent = entDevice.CollectionMaster;
- if (indexedLoads.TryGetValue(ent, out var entry))
- {
- entry.PowerValue += powerConsumer.ReceivedPower;
- indexedLoads[ent] = entry;
- continue;
- }
- indexedLoads.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, powerConsumer.ReceivedPower, GetBatteryLevel(ent)));
- }
- }
- foreach (var batteryCharger in netQ.Chargers)
- {
- var ent = batteryCharger.Owner;
- if (uid == ent)
- continue;
- if (!TryComp<PowerNetworkBatteryComponent>(ent, out var battery))
- continue;
- currentDemand += battery.CurrentReceiving;
- if (TryComp<PowerMonitoringDeviceComponent>(ent, out var entDevice))
- {
- // Combine entities represented by an master into a single entry
- if (entDevice.IsCollectionMasterOrChild && !entDevice.IsCollectionMaster)
- ent = entDevice.CollectionMaster;
- if (indexedLoads.TryGetValue(ent, out var entry))
- {
- entry.PowerValue += battery.CurrentReceiving;
- indexedLoads[ent] = entry;
- continue;
- }
- indexedLoads.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, battery.CurrentReceiving, GetBatteryLevel(ent)));
- }
- }
- loads = indexedLoads.Values.ToList();
- // Exit if demand is negligible
- if (MathHelper.CloseTo(currentDemand, 0))
- return;
- var supplying = 0f;
- // Work out how much power this device (and those it represents) is actually supplying
- if (TryComp<PowerNetworkBatteryComponent>(uid, out var entBattery))
- supplying = entBattery.CurrentSupply;
- else if (TryComp<PowerSupplierComponent>(uid, out var entSupplier))
- supplying = entSupplier.CurrentSupply;
- if (TryComp<PowerMonitoringDeviceComponent>(uid, out var device) && device.IsCollectionMaster)
- {
- foreach ((var child, var _) in device.ChildDevices)
- {
- if (TryComp<PowerNetworkBatteryComponent>(child, out var childBattery))
- supplying += childBattery.CurrentSupply;
- else if (TryComp<PowerSupplierComponent>(child, out var childSupplier))
- supplying += childSupplier.CurrentSupply;
- }
- }
- // Update the power value for each load based on the fraction of power these entities are actually draining from this device
- var powerFraction = Math.Min(supplying / currentDemand, 1f);
- for (int i = 0; i < indexedLoads.Values.Count; i++)
- {
- var entry = loads[i];
- loads[i] = new PowerMonitoringConsoleEntry(entry.NetEntity, entry.Group, entry.PowerValue * powerFraction, entry.BatteryLevel);
- }
- }
- // Designates a supplied entity as a 'collection master'. Other entities which share this
- // entities collection name and are attached on the same load network are assigned this entity
- // as the master that represents them on the console UI. This way you can have one device
- // represent multiple connected devices
- private void AssignEntityAsCollectionMaster
- (EntityUid uid,
- PowerMonitoringDeviceComponent? device = null,
- TransformComponent? xform = null,
- NodeContainerComponent? nodeContainer = null)
- {
- if (!Resolve(uid, ref device, ref nodeContainer, ref xform, false))
- return;
- // If the device is not attached to a network, exit
- var nodeName = device.SourceNode == string.Empty ? device.LoadNode : device.SourceNode;
- if (!nodeContainer.Nodes.TryGetValue(nodeName, out var node) ||
- node.ReachableNodes.Count == 0)
- {
- // Make a child the new master of the collection if necessary
- if (device.ChildDevices.TryFirstOrNull(out var kvp))
- {
- var newMaster = kvp.Value.Key;
- var newMasterDevice = kvp.Value.Value;
- newMasterDevice.CollectionMaster = newMaster;
- newMasterDevice.ChildDevices.Clear();
- foreach ((var child, var childDevice) in device.ChildDevices)
- {
- newMasterDevice.ChildDevices.Add(child, childDevice);
- childDevice.CollectionMaster = newMaster;
- UpdateCollectionChildMetaData(child, newMaster);
- }
- UpdateCollectionMasterMetaData(newMaster, newMasterDevice.ChildDevices.Count);
- }
- device.CollectionMaster = uid;
- device.ChildDevices.Clear();
- UpdateCollectionMasterMetaData(uid, 0);
- return;
- }
- // Check to see if the device has a valid existing master
- if (!device.IsCollectionMaster &&
- device.CollectionMaster.IsValid() &&
- TryComp<NodeContainerComponent>(device.CollectionMaster, out var masterNodeContainer) &&
- DevicesHaveMatchingNodes(nodeContainer, masterNodeContainer))
- return;
- // If not, make this a new master
- device.CollectionMaster = uid;
- device.ChildDevices.Clear();
- // Search for children
- var query = AllEntityQuery<PowerMonitoringDeviceComponent, TransformComponent, NodeContainerComponent>();
- while (query.MoveNext(out var ent, out var entDevice, out var entXform, out var entNodeContainer))
- {
- if (entDevice.CollectionName != device.CollectionName)
- continue;
- if (ent == uid)
- continue;
- if (entXform.GridUid != xform.GridUid)
- continue;
- if (!DevicesHaveMatchingNodes(nodeContainer, entNodeContainer))
- continue;
- device.ChildDevices.Add(ent, entDevice);
- entDevice.CollectionMaster = uid;
- UpdateCollectionChildMetaData(ent, uid);
- }
- UpdateCollectionMasterMetaData(uid, device.ChildDevices.Count);
- }
- private bool DevicesHaveMatchingNodes(NodeContainerComponent nodeContainerA, NodeContainerComponent nodeContainerB)
- {
- foreach ((var key, var nodeA) in nodeContainerA.Nodes)
- {
- if (!nodeContainerB.Nodes.TryGetValue(key, out var nodeB))
- return false;
- if (nodeA.NodeGroup != nodeB.NodeGroup)
- return false;
- }
- return true;
- }
- private void UpdateCollectionChildMetaData(EntityUid child, EntityUid master)
- {
- var netEntity = EntityManager.GetNetEntity(child);
- var xform = Transform(child);
- var query = AllEntityQuery<PowerMonitoringConsoleComponent, TransformComponent>();
- while (query.MoveNext(out var ent, out var entConsole, out var entXform))
- {
- if (entXform.GridUid != xform.GridUid)
- continue;
- if (!entConsole.PowerMonitoringDeviceMetaData.TryGetValue(netEntity, out var metaData))
- continue;
- metaData.CollectionMaster = EntityManager.GetNetEntity(master);
- entConsole.PowerMonitoringDeviceMetaData[netEntity] = metaData;
- Dirty(ent, entConsole);
- }
- }
- private void UpdateCollectionMasterMetaData(EntityUid master, int childCount)
- {
- var netEntity = EntityManager.GetNetEntity(master);
- var xform = Transform(master);
- var query = AllEntityQuery<PowerMonitoringConsoleComponent, TransformComponent>();
- while (query.MoveNext(out var ent, out var entConsole, out var entXform))
- {
- if (entXform.GridUid != xform.GridUid)
- continue;
- if (!entConsole.PowerMonitoringDeviceMetaData.TryGetValue(netEntity, out var metaData))
- continue;
- if (childCount > 0)
- {
- var name = MetaData(master).EntityPrototype?.Name ?? MetaData(master).EntityName;
- metaData.EntityName = Loc.GetString("power-monitoring-window-object-array", ("name", name), ("count", childCount + 1));
- }
- else
- {
- metaData.EntityName = MetaData(master).EntityName;
- }
- metaData.CollectionMaster = null;
- entConsole.PowerMonitoringDeviceMetaData[netEntity] = metaData;
- Dirty(ent, entConsole);
- }
- }
- private Dictionary<Vector2i, PowerCableChunk> RefreshPowerCableGrid(EntityUid gridUid, MapGridComponent grid)
- {
- // Clears all chunks for the associated grid
- var allChunks = new Dictionary<Vector2i, PowerCableChunk>();
- _gridPowerCableChunks[gridUid] = allChunks;
- // Adds all power cables to the grid
- var query = AllEntityQuery<CableComponent, TransformComponent>();
- while (query.MoveNext(out var ent, out var cable, out var entXform))
- {
- if (entXform.GridUid != gridUid)
- continue;
- var tile = _sharedMapSystem.GetTileRef(gridUid, grid, entXform.Coordinates);
- var chunkOrigin = SharedMapSystem.GetChunkIndices(tile.GridIndices, ChunkSize);
- if (!allChunks.TryGetValue(chunkOrigin, out var chunk))
- {
- chunk = new PowerCableChunk(chunkOrigin);
- allChunks[chunkOrigin] = chunk;
- }
- var relative = SharedMapSystem.GetChunkRelative(tile.GridIndices, ChunkSize);
- var flag = GetFlag(relative);
- chunk.PowerCableData[(int) cable.CableType] |= flag;
- }
- return allChunks;
- }
- private void UpdateFocusNetwork(EntityUid uid, PowerMonitoringCableNetworksComponent component, EntityUid gridUid, MapGridComponent grid, List<EntityUid> nodeList)
- {
- component.FocusChunks.Clear();
- foreach (var ent in nodeList)
- {
- var xform = Transform(ent);
- var tile = _sharedMapSystem.GetTileRef(gridUid, grid, xform.Coordinates);
- var gridIndices = tile.GridIndices;
- var chunkOrigin = SharedMapSystem.GetChunkIndices(gridIndices, ChunkSize);
- if (!component.FocusChunks.TryGetValue(chunkOrigin, out var chunk))
- {
- chunk = new PowerCableChunk(chunkOrigin);
- component.FocusChunks[chunkOrigin] = chunk;
- }
- var relative = SharedMapSystem.GetChunkRelative(gridIndices, ChunkSize);
- var flag = GetFlag(relative);
- if (TryComp<CableComponent>(ent, out var cable))
- chunk.PowerCableData[(int) cable.CableType] |= flag;
- }
- Dirty(uid, component);
- }
- private void RefreshPowerMonitoringConsole(EntityUid uid, PowerMonitoringConsoleComponent component)
- {
- component.Focus = null;
- component.FocusGroup = PowerMonitoringConsoleGroup.Generator;
- component.PowerMonitoringDeviceMetaData.Clear();
- component.Flags = 0;
- var xform = Transform(uid);
- if (xform.GridUid == null)
- return;
- var grid = xform.GridUid.Value;
- var query = AllEntityQuery<PowerMonitoringDeviceComponent, TransformComponent>();
- while (query.MoveNext(out var ent, out var entDevice, out var entXform))
- {
- if (grid != entXform.GridUid)
- continue;
- var netEntity = EntityManager.GetNetEntity(ent);
- var name = MetaData(ent).EntityName;
- var netCoords = EntityManager.GetNetCoordinates(entXform.Coordinates);
- var metaData = new PowerMonitoringDeviceMetaData(name, netCoords, entDevice.Group, entDevice.SpritePath, entDevice.SpriteState);
- if (entDevice.IsCollectionMasterOrChild)
- {
- if (!entDevice.IsCollectionMaster)
- {
- metaData.CollectionMaster = EntityManager.GetNetEntity(entDevice.CollectionMaster);
- }
- else if (entDevice.ChildDevices.Count > 0)
- {
- name = MetaData(ent).EntityPrototype?.Name ?? MetaData(ent).EntityName;
- metaData.EntityName = Loc.GetString("power-monitoring-window-object-array", ("name", name), ("count", entDevice.ChildDevices.Count + 1));
- }
- }
- component.PowerMonitoringDeviceMetaData.Add(netEntity, metaData);
- }
- Dirty(uid, component);
- }
- private void RefreshPowerMonitoringCableNetworks(EntityUid uid, PowerMonitoringCableNetworksComponent component)
- {
- var xform = Transform(uid);
- if (xform.GridUid == null)
- return;
- var grid = xform.GridUid.Value;
- if (!TryComp<MapGridComponent>(grid, out var map))
- return;
- if (!_gridPowerCableChunks.TryGetValue(grid, out var allChunks))
- allChunks = RefreshPowerCableGrid(grid, map);
- component.AllChunks = allChunks;
- component.FocusChunks.Clear();
- Dirty(uid, component);
- }
- private struct PowerStats
- {
- public double PowerValue { get; set; }
- public double PowerSupplied { get; set; }
- public double PowerUsage { get; set; }
- public double BatteryUsage { get; set; }
- public float? BatteryLevel { get; set; }
- }
- }
|