]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Fix bugs resulting in quantum APC visual states (#29475)
authorPieter-Jan Briers <pieterjan.briers+git@gmail.com>
Sat, 29 Jun 2024 02:07:00 +0000 (04:07 +0200)
committerGitHub <noreply@github.com>
Sat, 29 Jun 2024 02:07:00 +0000 (22:07 -0400)
There were TWO bugs here

FIRST, APCs *did* update their visual state on initialization, but at that point the relevant power state hasn't been initialized yet, so it always returns a bogus result. There aren't guaranteed to be subsequent power updates that actually trigger the APC to update so this can get it stuck.

Fixed by just deferring the on-init update to be after the first update tick, which is itself ordered to be after power update.

SECOND: Once I fixed that, I ran into the issue that APCs created at *server startup* also fail to update, because the throttling system (to prevent frequent APC updates) thinks the LastChargeStateTime was at server startup.

Fixed by making that variable nullable so it defaults to null.

Also removed the useless datafields on the "last update" fields. These are all just used to cache and throttle updates, something that should not be persisted to a map file.

Content.Server/Power/Components/ApcComponent.cs
Content.Server/Power/EntitySystems/ApcSystem.cs

index 8f74146b458c0f9c3308be58f098eb54df23797c..bee8defc43e46cacd66343e9533f9e3f48860f53 100644 (file)
@@ -11,12 +11,9 @@ public sealed partial class ApcComponent : BaseApcNetComponent
     [DataField("onReceiveMessageSound")]
     public SoundSpecifier OnReceiveMessageSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");
 
-    [DataField("lastChargeState")]
     public ApcChargeState LastChargeState;
-    [DataField("lastChargeStateTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
-    public TimeSpan LastChargeStateTime;
+    public TimeSpan? LastChargeStateTime;
 
-    [DataField("lastExternalState")]
     public ApcExternalPowerState LastExternalState;
 
     /// <summary>
@@ -24,7 +21,6 @@ public sealed partial class ApcComponent : BaseApcNetComponent
     /// Done after every <see cref="VisualsChangeDelay"/> to show the latest load.
     /// If charge state changes it will be instantly updated.
     /// </summary>
-    [DataField("lastUiUpdate", customTypeSerializer: typeof(TimeOffsetSerializer))]
     public TimeSpan LastUiUpdate;
 
     [DataField("enabled")]
@@ -33,6 +29,11 @@ public sealed partial class ApcComponent : BaseApcNetComponent
     [DataField("hasAccess")]
     public bool HasAccess = false;
 
+    /// <summary>
+    /// APC state needs to always be updated after first processing tick.
+    /// </summary>
+    public bool NeedStateUpdate;
+
     public const float HighPowerThreshold = 0.9f;
     public static TimeSpan VisualsChangeDelay = TimeSpan.FromSeconds(1);
 
index d88edd85cb5d99d87a02248ee463e5402c465c88..52c19c302cee907aa45d7b25e567b1de0b430a52 100644 (file)
@@ -32,7 +32,7 @@ public sealed class ApcSystem : EntitySystem
         UpdatesAfter.Add(typeof(PowerNetSystem));
 
         SubscribeLocalEvent<ApcComponent, BoundUIOpenedEvent>(OnBoundUiOpen);
-        SubscribeLocalEvent<ApcComponent, MapInitEvent>(OnApcInit);
+        SubscribeLocalEvent<ApcComponent, ComponentStartup>(OnApcStartup);
         SubscribeLocalEvent<ApcComponent, ChargeChangedEvent>(OnBatteryChargeChanged);
         SubscribeLocalEvent<ApcComponent, ApcToggleMainBreakerMessage>(OnToggleMainBreaker);
         SubscribeLocalEvent<ApcComponent, GotEmaggedEvent>(OnEmagged);
@@ -50,6 +50,11 @@ public sealed class ApcSystem : EntitySystem
                 apc.LastUiUpdate = _gameTiming.CurTime;
                 UpdateUIState(uid, apc, battery);
             }
+
+            if (apc.NeedStateUpdate)
+            {
+                UpdateApcState(uid, apc, battery);
+            }
         }
     }
 
@@ -59,9 +64,11 @@ public sealed class ApcSystem : EntitySystem
         UpdateApcState(uid, component);
     }
 
-    private void OnApcInit(EntityUid uid, ApcComponent component, MapInitEvent args)
+    private static void OnApcStartup(EntityUid uid, ApcComponent component, ComponentStartup args)
     {
-        UpdateApcState(uid, component);
+        // We cannot update immediately, as various network/battery state is not valid yet.
+        // Defer until the next tick.
+        component.NeedStateUpdate = true;
     }
 
     //Update the HasAccess var for UI to read
@@ -119,15 +126,18 @@ public sealed class ApcSystem : EntitySystem
         if (!Resolve(uid, ref apc, ref battery, false))
             return;
 
-        var newState = CalcChargeState(uid, battery.NetworkBattery);
-        if (newState != apc.LastChargeState && apc.LastChargeStateTime + ApcComponent.VisualsChangeDelay < _gameTiming.CurTime)
+        if (apc.LastChargeStateTime == null || apc.LastChargeStateTime + ApcComponent.VisualsChangeDelay < _gameTiming.CurTime)
         {
-            apc.LastChargeState = newState;
-            apc.LastChargeStateTime = _gameTiming.CurTime;
-
-            if (TryComp(uid, out AppearanceComponent? appearance))
+            var newState = CalcChargeState(uid, battery.NetworkBattery);
+            if (newState != apc.LastChargeState)
             {
-                _appearance.SetData(uid, ApcVisuals.ChargeState, newState, appearance);
+                apc.LastChargeState = newState;
+                apc.LastChargeStateTime = _gameTiming.CurTime;
+
+                if (TryComp(uid, out AppearanceComponent? appearance))
+                {
+                    _appearance.SetData(uid, ApcVisuals.ChargeState, newState, appearance);
+                }
             }
         }
 
@@ -137,6 +147,8 @@ public sealed class ApcSystem : EntitySystem
             apc.LastExternalState = extPowerState;
             UpdateUIState(uid, apc, battery);
         }
+
+        apc.NeedStateUpdate = false;
     }
 
     public void UpdateUIState(EntityUid uid,