diff --git a/Content.Client/NodeContainer/NodeGroupSystem.cs b/Content.Client/NodeContainer/NodeGroupSystem.cs index 30b2f46a36..565e158fad 100644 --- a/Content.Client/NodeContainer/NodeGroupSystem.cs +++ b/Content.Client/NodeContainer/NodeGroupSystem.cs @@ -59,6 +59,14 @@ namespace Content.Client.NodeContainer Groups.Add(group.NetId, group); } + foreach (var (groupId, debugData) in ev.GroupDataUpdates) + { + if (Groups.TryGetValue(groupId, out var group)) + { + group.DebugData = debugData; + } + } + Entities = Groups.Values .SelectMany(g => g.Nodes, (data, nodeData) => (data, nodeData)) .GroupBy(n => n.nodeData.Entity) diff --git a/Content.Client/NodeContainer/NodeVisualizationOverlay.cs b/Content.Client/NodeContainer/NodeVisualizationOverlay.cs index ad62b483f5..c2ccfae442 100644 --- a/Content.Client/NodeContainer/NodeVisualizationOverlay.cs +++ b/Content.Client/NodeContainer/NodeVisualizationOverlay.cs @@ -1,14 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Text; using Content.Client.Resources; using Robust.Client.Graphics; using Robust.Client.Input; using Robust.Client.ResourceManagement; using Robust.Shared.Enums; -using Robust.Shared.GameObjects; using Robust.Shared.Map; -using Robust.Shared.Maths; using Robust.Shared.Timing; using Robust.Shared.Utility; using static Content.Shared.NodeContainer.NodeVis; @@ -90,6 +86,7 @@ namespace Content.Client.NodeContainer sb.Append($"node: {node.Name}\n"); sb.Append($"type: {node.Type}\n"); sb.Append($"grid pos: {gridTile}\n"); + sb.Append(group.DebugData); args.ScreenHandle.DrawString(_font, mousePos + (20, -20), sb.ToString()); } diff --git a/Content.Server/NodeContainer/EntitySystems/NodeGroupSystem.cs b/Content.Server/NodeContainer/EntitySystems/NodeGroupSystem.cs index fe3ed92af0..b88f856569 100644 --- a/Content.Server/NodeContainer/EntitySystems/NodeGroupSystem.cs +++ b/Content.Server/NodeContainer/EntitySystems/NodeGroupSystem.cs @@ -36,11 +36,15 @@ namespace Content.Server.NodeContainer.EntitySystems private readonly HashSet _visPlayers = new(); private readonly HashSet _toRemake = new(); + private readonly HashSet _nodeGroups = new(); private readonly HashSet _toRemove = new(); private readonly List _toReflood = new(); private ISawmill _sawmill = default!; + private const float VisDataUpdateInterval = 1; + private float _accumulatedFrameTime; + public bool VisEnabled => _visPlayers.Count != 0; private int _gen = 1; @@ -99,6 +103,11 @@ namespace Content.Server.NodeContainer.EntitySystems { QueueReflood(node); } + + if (group.NodeCount == 0) + { + _nodeGroups.Remove(group); + } } public void QueueReflood(Node node) @@ -130,7 +139,7 @@ namespace Content.Server.NodeContainer.EntitySystems base.Update(frameTime); DoGroupUpdates(); - VisDoUpdate(); + VisDoUpdate(frameTime); } private void DoGroupUpdates() @@ -235,6 +244,7 @@ namespace Content.Server.NodeContainer.EntitySystems oldGroup.Removed = true; oldGroup.AfterRemake(newGrouped); + _nodeGroups.Remove(oldGroup); if (VisEnabled) _visDeletes.Add(oldGroup.NetId); } @@ -288,6 +298,8 @@ namespace Content.Server.NodeContainer.EntitySystems newGroup.LoadNodes(groupNodes); + _nodeGroups.Add(newGroup); + if (VisEnabled) _visSends.Add(newGroup); @@ -340,9 +352,16 @@ namespace Content.Server.NodeContainer.EntitySystems } } - private void VisDoUpdate() + private void VisDoUpdate(float frametime) { - if (_visSends.Count == 0 && _visDeletes.Count == 0) + if (_visPlayers.Count == 0) + return; + + _accumulatedFrameTime += frametime; + + if (_accumulatedFrameTime < VisDataUpdateInterval + && _visSends.Count == 0 + && _visDeletes.Count == 0) return; var msg = new NodeVis.MsgData(); @@ -350,6 +369,18 @@ namespace Content.Server.NodeContainer.EntitySystems msg.GroupDeletions.AddRange(_visDeletes); msg.Groups.AddRange(_visSends.Select(VisMakeGroupState)); + if (_accumulatedFrameTime > VisDataUpdateInterval) + { + _accumulatedFrameTime -= VisDataUpdateInterval; + foreach (var group in _nodeGroups) + { + if (_visSends.Contains(group)) + continue; + + msg.GroupDataUpdates.Add(group.NetId, group.GetDebugData()); + } + } + _visSends.Clear(); _visDeletes.Clear(); @@ -363,14 +394,7 @@ namespace Content.Server.NodeContainer.EntitySystems { var msg = new NodeVis.MsgData(); - var allNetworks = EntityManager - .EntityQuery() - .SelectMany(nc => nc.Nodes.Values) - .Select(n => (BaseNodeGroup?) n.NodeGroup) - .Where(n => n != null) - .Distinct(); - - foreach (var network in allNetworks) + foreach (var network in _nodeGroups) { msg.Groups.Add(VisMakeGroupState(network!)); } @@ -392,7 +416,8 @@ namespace Content.Server.NodeContainer.EntitySystems Reachable = n.ReachableNodes.Select(r => r.NetId).ToArray(), Entity = n.Owner, Type = n.GetType().Name - }).ToArray() + }).ToArray(), + DebugData = group.GetDebugData() }; } diff --git a/Content.Server/NodeContainer/NodeGroups/BaseNodeGroup.cs b/Content.Server/NodeContainer/NodeGroups/BaseNodeGroup.cs index cc4f14be4c..c0eb4a4469 100644 --- a/Content.Server/NodeContainer/NodeGroups/BaseNodeGroup.cs +++ b/Content.Server/NodeContainer/NodeGroups/BaseNodeGroup.cs @@ -33,8 +33,10 @@ namespace Content.Server.NodeContainer.NodeGroups // In theory, the SS13 curse ensures this method will never be called. void AfterRemake(IEnumerable> newGroups); - // TODO: Why is this method needed? - void QueueRemake(); + /// + /// Return any additional data to display for the node-visualizer debug overlay. + /// + string? GetDebugData(); } [NodeGroup(NodeGroupID.Default, NodeGroupID.WireNet)] @@ -111,9 +113,9 @@ namespace Content.Server.NodeContainer.NodeGroups /// A list of new groups for this group's former nodes. public virtual void AfterRemake(IEnumerable> newGroups) { } - public void QueueRemake() + public virtual string? GetDebugData() { - EntitySystem.Get().QueueRemakeGroup(this); + return null; } } } diff --git a/Content.Server/NodeContainer/NodeGroups/PipeNet.cs b/Content.Server/NodeContainer/NodeGroups/PipeNet.cs index 180b21ccbb..ce0c9b5463 100644 --- a/Content.Server/NodeContainer/NodeGroups/PipeNet.cs +++ b/Content.Server/NodeContainer/NodeGroups/PipeNet.cs @@ -1,16 +1,10 @@ -using System; -using System.Collections.Generic; using System.Linq; using Content.Server.Atmos; -using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.NodeContainer.Nodes; using Content.Shared.Atmos; -using Robust.Shared.GameObjects; using Robust.Shared.Map; -using Robust.Shared.Maths; using Robust.Shared.Utility; -using Robust.Shared.ViewVariables; namespace Content.Server.NodeContainer.NodeGroups { @@ -23,7 +17,7 @@ namespace Content.Server.NodeContainer.NodeGroups } [NodeGroup(NodeGroupID.Pipe)] - public class PipeNet : BaseNodeGroup, IPipeNet + public sealed class PipeNet : BaseNodeGroup, IPipeNet { [ViewVariables] public GasMixture Air { get; set; } = new() {Temperature = Atmospherics.T20C}; @@ -95,5 +89,12 @@ namespace Content.Server.NodeContainer.NodeGroups DebugTools.AssertNotNull(_atmosphereSystem); _atmosphereSystem?.RemovePipeNet(this); } + + public override string GetDebugData() + { + return @$"Pressure: { Air.Pressure:G3} +Temperature: {Air.Temperature:G3} +Volume: {Air.Volume:G3}"; + } } } diff --git a/Content.Server/Power/NodeGroups/ApcNet.cs b/Content.Server/Power/NodeGroups/ApcNet.cs index 2dc7bd53bc..90b2c9a8a2 100644 --- a/Content.Server/Power/NodeGroups/ApcNet.cs +++ b/Content.Server/Power/NodeGroups/ApcNet.cs @@ -120,5 +120,21 @@ namespace Content.Server.Power.NodeGroups { netConnectorComponent.Net = this; } + + public override string? GetDebugData() + { + // This is just recycling the multi-tool examine. + + var ps = _powerNetSystem.GetNetworkStatistics(NetworkNode); + + float storageRatio = ps.InStorageCurrent / Math.Max(ps.InStorageMax, 1.0f); + float outStorageRatio = ps.OutStorageCurrent / Math.Max(ps.OutStorageMax, 1.0f); + return @$"Current Supply: {ps.SupplyCurrent:G3} +From Batteries: {ps.SupplyBatteries:G3} +Theoretical Supply: {ps.SupplyTheoretical:G3} +Ideal Consumption: {ps.Consumption:G3} +Input Storage: {ps.InStorageCurrent:G3} / {ps.InStorageMax:G3} ({storageRatio:P1}) +Output Storage: {ps.OutStorageCurrent:G3} / {ps.OutStorageMax:G3} ({outStorageRatio:P1})"; + } } } diff --git a/Content.Server/Power/NodeGroups/PowerNet.cs b/Content.Server/Power/NodeGroups/PowerNet.cs index b49d251186..45c347b6b2 100644 --- a/Content.Server/Power/NodeGroups/PowerNet.cs +++ b/Content.Server/Power/NodeGroups/PowerNet.cs @@ -124,5 +124,20 @@ namespace Content.Server.Power.NodeGroups Chargers.Remove(charger); _powerNetSystem.QueueReconnectPowerNet(this); } + + public override string? GetDebugData() + { + // This is just recycling the multi-tool examine. + var ps = _powerNetSystem.GetNetworkStatistics(NetworkNode); + + float storageRatio = ps.InStorageCurrent / Math.Max(ps.InStorageMax, 1.0f); + float outStorageRatio = ps.OutStorageCurrent / Math.Max(ps.OutStorageMax, 1.0f); + return @$"Current Supply: {ps.SupplyCurrent:G3} +From Batteries: {ps.SupplyBatteries:G3} +Theoretical Supply: {ps.SupplyTheoretical:G3} +Ideal Consumption: {ps.Consumption:G3} +Input Storage: {ps.InStorageCurrent:G3} / {ps.InStorageMax:G3} ({storageRatio:P1}) +Output Storage: {ps.OutStorageCurrent:G3} / {ps.OutStorageMax:G3} ({outStorageRatio:P1})"; + } } } diff --git a/Content.Shared/NodeContainer/NodeVis.cs b/Content.Shared/NodeContainer/NodeVis.cs index d3f9ceb93a..0208eb613e 100644 --- a/Content.Shared/NodeContainer/NodeVis.cs +++ b/Content.Shared/NodeContainer/NodeVis.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using Robust.Shared.GameObjects; -using Robust.Shared.Maths; -using Robust.Shared.Serialization; +using Robust.Shared.Serialization; namespace Content.Shared.NodeContainer { @@ -24,6 +20,7 @@ namespace Content.Shared.NodeContainer { public List Groups = new(); public List GroupDeletions = new(); + public Dictionary GroupDataUpdates = new(); } [Serializable, NetSerializable] @@ -33,6 +30,7 @@ namespace Content.Shared.NodeContainer public string GroupId = ""; public Color Color; public NodeDatum[] Nodes = Array.Empty(); + public string? DebugData; } [Serializable, NetSerializable]