]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Make pow3r validation logic available on release (#36075)
authorPieter-Jan Briers <pieterjan.briers+git@gmail.com>
Tue, 25 Mar 2025 17:02:39 +0000 (18:02 +0100)
committerGitHub <noreply@github.com>
Tue, 25 Mar 2025 17:02:39 +0000 (10:02 -0700)
I want to run this on live servers. It's behind a power_validate command, it still doesn't automatically get ran outside debug otherwise.

Content.Server/Power/Commands/PowerValidateCommand.cs [new file with mode: 0644]
Content.Server/Power/EntitySystems/PowerNetSystem.cs
Content.Server/Power/Pow3r/BatteryRampPegSolver.cs
Content.Server/Power/Pow3r/IPowerSolver.cs
Content.Server/Power/Pow3r/NoOpSolver.cs
Resources/Locale/en-US/power/commands.ftl [new file with mode: 0644]

diff --git a/Content.Server/Power/Commands/PowerValidateCommand.cs b/Content.Server/Power/Commands/PowerValidateCommand.cs
new file mode 100644 (file)
index 0000000..a3a6b09
--- /dev/null
@@ -0,0 +1,29 @@
+using Content.Server.Administration;
+using Content.Server.Power.EntitySystems;
+using Content.Shared.Administration;
+using Robust.Shared.Console;
+
+namespace Content.Server.Power.Commands;
+
+[AdminCommand(AdminFlags.Debug)]
+public sealed class PowerValidateCommand : LocalizedEntityCommands
+{
+    [Dependency] private readonly PowerNetSystem _powerNet = null!;
+
+    public override string Command => "power_validate";
+
+    public override void Execute(IConsoleShell shell, string argStr, string[] args)
+    {
+        try
+        {
+            _powerNet.Validate();
+        }
+        catch (Exception e)
+        {
+            shell.WriteLine(LocalizationManager.GetString("cmd-power_validate-error", ("err", e.ToString())));
+            return;
+        }
+
+        shell.WriteLine(LocalizationManager.GetString("cmd-power_validate-success"));
+    }
+}
index 666d3fcbe8e63560fb6462d16259eded5f42e7c2..ab2e27600ab25941e67049fe16432314469c8084 100644 (file)
@@ -519,6 +519,14 @@ namespace Content.Server.Power.EntitySystems
                 supplier.NetworkSupply.LinkedNetwork = netNode.Id;
             }
         }
+
+        /// <summary>
+        /// Validate integrity of the power state data. Throws if an error is found.
+        /// </summary>
+        public void Validate()
+        {
+            _solver.Validate(_powerState);
+        }
     }
 
     /// <summary>
index 599c55b6ba4bd44bc6f90ab520561cbedd2ef22d..bd998a0151e9ac70fc2ec2bf32a13b11c1af57b0 100644 (file)
@@ -1,6 +1,8 @@
-using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
 using Robust.Shared.Utility;
 using System.Linq;
+using System.Runtime.CompilerServices;
+using JetBrains.Annotations;
 using Robust.Shared.Threading;
 using static Content.Server.Power.Pow3r.PowerState;
 
@@ -40,7 +42,9 @@ namespace Content.Server.Power.Pow3r
             DebugTools.Assert(state.GroupedNets.Select(x => x.Count).Sum() == state.Networks.Count);
             _networkJob.State = state;
             _networkJob.FrameTime = frameTime;
+#if DEBUG
             ValidateNetworkGroups(state, state.GroupedNets);
+#endif
 
             // Each network height layer can be run in parallel without issues.
             foreach (var group in state.GroupedNets)
@@ -328,16 +332,22 @@ namespace Content.Server.Power.Pow3r
                     RecursivelyEstimateNetworkDepth(state, network, groupedNetworks);
             }
 
-            ValidateNetworkGroups(state, groupedNetworks);
             return groupedNetworks;
         }
 
+        public void Validate(PowerState state)
+        {
+            if (state.GroupedNets == null)
+                throw new InvalidOperationException("We don't have grouped networks cached??");
+
+            ValidateNetworkGroups(state, state.GroupedNets);
+        }
+
         /// <summary>
         /// Validate that network grouping is up to date. I.e., that it is safe to solve each networking in a given
         /// group in parallel. This assumes that batteries are the only device that connects to multiple networks, and
         /// is thus the only obstacle to solving everything in parallel.
         /// </summary>
-        [Conditional("DEBUG")]
         private void ValidateNetworkGroups(PowerState state, List<List<Network>> groupedNetworks)
         {
             HashSet<Network> nets = new();
@@ -362,9 +372,9 @@ namespace Content.Server.Power.Pow3r
                             continue;
                         }
 
-                        DebugTools.Assert(!nets.Contains(subNet));
-                        DebugTools.Assert(!netIds.Contains(subNet.Id));
-                        DebugTools.Assert(subNet.Height < net.Height);
+                        Check(!nets.Contains(subNet), $"Net {net.Id}, battery {batteryId}");
+                        Check(!netIds.Contains(subNet.Id), $"Net {net.Id}, battery {batteryId}");
+                        Check(subNet.Height < net.Height, $"Net {net.Id}, battery {batteryId}");
                     }
 
                     foreach (var batteryId in net.BatterySupplies)
@@ -380,15 +390,32 @@ namespace Content.Server.Power.Pow3r
                             continue;
                         }
 
-                        DebugTools.Assert(!nets.Contains(parentNet));
-                        DebugTools.Assert(!netIds.Contains(parentNet.Id));
-                        DebugTools.Assert(parentNet.Height > net.Height);
+                        Check(!nets.Contains(parentNet), $"Net {net.Id}, battery {batteryId}");
+                        Check(!netIds.Contains(parentNet.Id), $"Net {net.Id}, battery {batteryId}");
+                        Check(parentNet.Height > net.Height, $"Net {net.Id}, battery {batteryId}");
                     }
 
-                    DebugTools.Assert(nets.Add(net));
-                    DebugTools.Assert(netIds.Add(net.Id));
+                    Check(nets.Add(net), $"Net {net.Id}");
+                    Check(netIds.Add(net.Id), $"Net {net.Id}");
                 }
             }
+
+            return;
+
+            // Most readable C# function def.
+            [AssertionMethod]
+            static void Check(
+                [AssertionCondition(AssertionConditionType.IS_TRUE)]
+                [DoesNotReturnIf(false)]
+                bool condition,
+                [InterpolatedStringHandlerArgument("condition")]
+                ref DebugTools.AssertInterpolatedStringHandler handler,
+                [CallerArgumentExpression(nameof(condition))]
+                string check = "")
+            {
+                if (!condition)
+                    throw new DebugAssertException($"{handler.ToStringAndClear()}: failed check: {check}");
+            }
         }
 
         private static void RecursivelyEstimateNetworkDepth(PowerState state, Network network, List<List<Network>> groupedNetworks)
index bcc33212aed5b4286decc2ac940039034ea2c14c..44d1a47d00ef4494366129e9c4ce9fd5bc4c0924 100644 (file)
@@ -5,5 +5,6 @@ namespace Content.Server.Power.Pow3r
     public interface IPowerSolver
     {
         void Tick(float frameTime, PowerState state, IParallelManager parallel);
+        void Validate(PowerState state);
     }
 }
index d82de3fd57f57fe5328bcb51ad5a1d4a2f2d9929..c8829fc6f8a5fcb50c3d939b7587bd46cec94432 100644 (file)
@@ -8,5 +8,10 @@ namespace Content.Server.Power.Pow3r
         {
             // Literally nothing.
         }
+
+        public void Validate(PowerState state)
+        {
+            // Literally nothing.
+        }
     }
 }
diff --git a/Resources/Locale/en-US/power/commands.ftl b/Resources/Locale/en-US/power/commands.ftl
new file mode 100644 (file)
index 0000000..0908af1
--- /dev/null
@@ -0,0 +1,4 @@
+cmd-power_validate-desc = Validate power network state integrity
+cmd-power_validate-help = Usage: power_validate
+cmd-power_validate-error = Error while validating: { $err }
+cmd-power_validate-success = Validation succeeded without error