]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Refactor FTL time tracking code to fix a UI bug (#26538)
authorPieter-Jan Briers <pieterjan.briers+git@gmail.com>
Sat, 30 Mar 2024 01:40:55 +0000 (02:40 +0100)
committerGitHub <noreply@github.com>
Sat, 30 Mar 2024 01:40:55 +0000 (12:40 +1100)
The FTL UI on the shuttle console would reset the FTL progress bar every time you open it. This is because the server only sends "time until completion", not a start/end time. The FTL code now uses a separate start/end time so the exact same progress bar can be preserved.

For convenience, I made a StartEndTime record struct that stores the actual tuple. This is now used by the code and has some helpers.

Content.Client/Shuttles/UI/MapScreen.xaml.cs
Content.Server/Shuttles/Components/FTLComponent.cs
Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs
Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs
Content.Server/Shuttles/Systems/ShuttleSystem.cs
Content.Shared/Shuttles/BUIStates/ShuttleMapInterfaceState.cs
Content.Shared/Timing/StartEndTime.cs [new file with mode: 0644]

index 8430699bae17952a042039e9ee0eb81ac8321761..225d1be14e5e4a7d572b4aec7630ad458d6b2d40 100644 (file)
@@ -5,6 +5,7 @@ using Content.Shared.Shuttles.BUIStates;
 using Content.Shared.Shuttles.Components;
 using Content.Shared.Shuttles.Systems;
 using Content.Shared.Shuttles.UI.MapObjects;
+using Content.Shared.Timing;
 using Robust.Client.AutoGenerated;
 using Robust.Client.Graphics;
 using Robust.Client.UserInterface;
@@ -38,16 +39,11 @@ public sealed partial class MapScreen : BoxContainer
     private EntityUid? _shuttleEntity;
 
     private FTLState _state;
-    private float _ftlDuration;
+    private StartEndTime _ftlTime;
 
     private List<ShuttleBeaconObject> _beacons = new();
     private List<ShuttleExclusionObject> _exclusions = new();
 
-    /// <summary>
-    /// When the next FTL state change happens.
-    /// </summary>
-    private TimeSpan _nextFtlTime;
-
     private TimeSpan _nextPing;
     private TimeSpan _pingCooldown = TimeSpan.FromSeconds(3);
     private TimeSpan _nextMapDequeue;
@@ -114,8 +110,7 @@ public sealed partial class MapScreen : BoxContainer
         _beacons = state.Destinations;
         _exclusions = state.Exclusions;
         _state = state.FTLState;
-        _ftlDuration = state.FTLDuration;
-        _nextFtlTime = _timing.CurTime + TimeSpan.FromSeconds(_ftlDuration);
+        _ftlTime = state.FTLTime;
         MapRadar.InFtl = true;
         MapFTLState.Text = Loc.GetString($"shuttle-console-ftl-state-{_state.ToString()}");
 
@@ -511,20 +506,8 @@ public sealed partial class MapScreen : BoxContainer
             MapRebuildButton.Disabled = false;
         }
 
-        var ftlDiff = (float) (_nextFtlTime - _timing.CurTime).TotalSeconds;
-
-        float ftlRatio;
-
-        if (_ftlDuration.Equals(0f))
-        {
-            ftlRatio = 1f;
-        }
-        else
-        {
-            ftlRatio = Math.Clamp(1f - (ftlDiff / _ftlDuration), 0f, 1f);
-        }
-
-        FTLBar.Value = ftlRatio;
+        var progress = _ftlTime.ProgressAt(curTime);
+        FTLBar.Value = float.IsFinite(progress) ? progress : 1;
     }
 
     protected override void Draw(DrawingHandleScreen handle)
index d15f65a35558e945c22b1fa44c18e75c61593960..a3da4855f753bece5dc538e55d7e268c4a932b05 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Shared.Shuttles.Systems;
 using Content.Shared.Tag;
+using Content.Shared.Timing;
 using Robust.Shared.Audio;
 using Robust.Shared.Map;
 using Robust.Shared.Prototypes;
@@ -17,13 +18,13 @@ public sealed partial class FTLComponent : Component
     public FTLState State = FTLState.Available;
 
     [ViewVariables(VVAccess.ReadWrite)]
-    public float StartupTime = 0f;
+    public StartEndTime StateTime;
 
     [ViewVariables(VVAccess.ReadWrite)]
-    public float TravelTime = 0f;
+    public float StartupTime = 0f;
 
     [ViewVariables(VVAccess.ReadWrite)]
-    public float Accumulator = 0f;
+    public float TravelTime = 0f;
 
     /// <summary>
     /// Coordinates to arrive it: May be relative to another grid (for docking) or map coordinates.
index f0368ed3a95cb7ba1f468fcd4a9e78160cbb2e31..a4f2c7b4db39ad4410e2ecaf17fb827a890b719c 100644 (file)
@@ -13,6 +13,7 @@ using Content.Shared.Shuttles.Systems;
 using Content.Shared.Tag;
 using Content.Shared.Movement.Systems;
 using Content.Shared.Shuttles.UI.MapObjects;
+using Content.Shared.Timing;
 using Robust.Server.GameObjects;
 using Robust.Shared.Collections;
 using Robust.Shared.GameStates;
@@ -257,7 +258,11 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
         else
         {
             navState = new NavInterfaceState(0f, null, null, new Dictionary<NetEntity, List<DockingPortState>>());
-            mapState = new ShuttleMapInterfaceState(FTLState.Invalid, 0f, new List<ShuttleBeaconObject>(), new List<ShuttleExclusionObject>());
+            mapState = new ShuttleMapInterfaceState(
+                FTLState.Invalid,
+                default,
+                new List<ShuttleBeaconObject>(),
+                new List<ShuttleExclusionObject>());
         }
 
         if (_ui.TryGetUi(consoleUid, ShuttleConsoleUiKey.Key, out var bui))
@@ -408,12 +413,12 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
     public ShuttleMapInterfaceState GetMapState(Entity<FTLComponent?> shuttle)
     {
         FTLState ftlState = FTLState.Available;
-        float stateDuration = 0f;
+        StartEndTime stateDuration = default;
 
         if (Resolve(shuttle, ref shuttle.Comp, false) && shuttle.Comp.LifeStage < ComponentLifeStage.Stopped)
         {
             ftlState = shuttle.Comp.State;
-            stateDuration = _shuttle.GetStateDuration(shuttle.Comp);
+            stateDuration = _shuttle.GetStateTime(shuttle.Comp);
         }
 
         List<ShuttleBeaconObject>? beacons = null;
@@ -422,7 +427,8 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
         GetExclusions(ref exclusions);
 
         return new ShuttleMapInterfaceState(
-            ftlState, stateDuration,
+            ftlState,
+            stateDuration,
             beacons ?? new List<ShuttleBeaconObject>(),
             exclusions ?? new List<ShuttleExclusionObject>());
     }
index cb322ac396408c5fb8e3e9ff6a3722e140ed7e36..512886910395367508b94454504684a3136a06bf 100644 (file)
@@ -12,6 +12,7 @@ using Content.Shared.Parallax;
 using Content.Shared.Shuttles.Components;
 using Content.Shared.Shuttles.Systems;
 using Content.Shared.StatusEffect;
+using Content.Shared.Timing;
 using Content.Shared.Whitelist;
 using JetBrains.Annotations;
 using Robust.Shared.Audio;
@@ -131,7 +132,7 @@ public sealed partial class ShuttleSystem
         return mapUid;
     }
 
-    public float GetStateDuration(FTLComponent component)
+    public StartEndTime GetStateTime(FTLComponent component)
     {
         var state = component.State;
 
@@ -141,9 +142,9 @@ public sealed partial class ShuttleSystem
             case FTLState.Travelling:
             case FTLState.Arriving:
             case FTLState.Cooldown:
-                return component.Accumulator;
+                return component.StateTime;
             case FTLState.Available:
-                return 0f;
+                return default;
             default:
                 throw new NotImplementedException();
         }
@@ -251,7 +252,9 @@ public sealed partial class ShuttleSystem
 
         hyperspace.StartupTime = startupTime;
         hyperspace.TravelTime = hyperspaceTime;
-        hyperspace.Accumulator = hyperspace.StartupTime;
+        hyperspace.StateTime = StartEndTime.FromStartDuration(
+            _gameTiming.CurTime,
+            TimeSpan.FromSeconds(hyperspace.StartupTime));
         hyperspace.TargetCoordinates = coordinates;
         hyperspace.TargetAngle = angle;
         hyperspace.PriorityTag = priorityTag;
@@ -282,7 +285,9 @@ public sealed partial class ShuttleSystem
         var config = _dockSystem.GetDockingConfig(shuttleUid, target, priorityTag);
         hyperspace.StartupTime = startupTime;
         hyperspace.TravelTime = hyperspaceTime;
-        hyperspace.Accumulator = hyperspace.StartupTime;
+        hyperspace.StateTime = StartEndTime.FromStartDuration(
+            _gameTiming.CurTime,
+            TimeSpan.FromSeconds(hyperspace.StartupTime));
         hyperspace.PriorityTag = priorityTag;
 
         _console.RefreshShuttleConsoles(shuttleUid);
@@ -366,7 +371,7 @@ public sealed partial class ShuttleSystem
         // Reset rotation so they always face the same direction.
         xform.LocalRotation = Angle.Zero;
         _index += width + Buffer;
-        comp.Accumulator += comp.TravelTime - DefaultArrivalTime;
+        comp.StateTime = StartEndTime.FromCurTime(_gameTiming, comp.TravelTime - DefaultArrivalTime);
 
         Enable(uid, component: body);
         _physics.SetLinearVelocity(uid, new Vector2(0f, 20f), body: body);
@@ -401,7 +406,7 @@ public sealed partial class ShuttleSystem
     {
         var shuttle = entity.Comp2;
         var comp = entity.Comp1;
-        comp.Accumulator += DefaultArrivalTime;
+        comp.StateTime = StartEndTime.FromCurTime(_gameTiming, DefaultArrivalTime);
         comp.State = FTLState.Arriving;
         // TODO: Arrival effects
         // For now we'll just use the ss13 bubbles but we can do fancier.
@@ -504,7 +509,7 @@ public sealed partial class ShuttleSystem
         }
 
         comp.State = FTLState.Cooldown;
-        comp.Accumulator += FTLCooldown;
+        comp.StateTime = StartEndTime.FromCurTime(_gameTiming, FTLCooldown);
         _console.RefreshShuttleConsoles(uid);
         _mapManager.SetMapPaused(mapId, false);
         Smimsh(uid, xform: xform);
@@ -519,15 +524,14 @@ public sealed partial class ShuttleSystem
         _console.RefreshShuttleConsoles(entity);
     }
 
-    private void UpdateHyperspace(float frameTime)
+    private void UpdateHyperspace()
     {
+        var curTime = _gameTiming.CurTime;
         var query = EntityQueryEnumerator<FTLComponent, ShuttleComponent>();
 
         while (query.MoveNext(out var uid, out var comp, out var shuttle))
         {
-            comp.Accumulator -= frameTime;
-
-            if (comp.Accumulator > 0f)
+            if (curTime < comp.StateTime.End)
                 continue;
 
             var entity = (uid, comp, shuttle);
index 7c42753a7de9dd4851d5babb84e8426c6d0c614f..6dc25e8d76609097abeefd308a733a4ebd7fe190 100644 (file)
@@ -19,6 +19,7 @@ using Robust.Shared.Physics;
 using Robust.Shared.Physics.Components;
 using Robust.Shared.Physics.Systems;
 using Robust.Shared.Random;
+using Robust.Shared.Timing;
 
 namespace Content.Server.Shuttles.Systems;
 
@@ -30,6 +31,7 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem
     [Dependency] private readonly IMapManager _mapManager = default!;
     [Dependency] private readonly IRobustRandom _random = default!;
     [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!;
+    [Dependency] private readonly IGameTiming _gameTiming = default!;
     [Dependency] private readonly BiomeSystem _biomes = default!;
     [Dependency] private readonly BodySystem _bobby = default!;
     [Dependency] private readonly DockingSystem _dockSystem = default!;
@@ -68,7 +70,7 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem
     public override void Update(float frameTime)
     {
         base.Update(frameTime);
-        UpdateHyperspace(frameTime);
+        UpdateHyperspace();
     }
 
     private void OnGridFixtureChange(EntityUid uid, FixturesComponent manager, GridFixtureChangeEvent args)
index cee0daab4be28698d485b238635f8df63d384697..3cb7cb6412aa305ec5170de951ffbaa2efdf1a95 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Shared.Shuttles.Systems;
 using Content.Shared.Shuttles.UI.MapObjects;
+using Content.Shared.Timing;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Shuttles.BUIStates;
@@ -16,9 +17,9 @@ public sealed class ShuttleMapInterfaceState
     public readonly FTLState FTLState;
 
     /// <summary>
-    /// How long the FTL state takes.
+    /// When the current FTL state starts and ends.
     /// </summary>
-    public float FTLDuration;
+    public StartEndTime FTLTime;
 
     public List<ShuttleBeaconObject> Destinations;
 
@@ -26,12 +27,12 @@ public sealed class ShuttleMapInterfaceState
 
     public ShuttleMapInterfaceState(
         FTLState ftlState,
-        float ftlDuration,
+        StartEndTime ftlTime,
         List<ShuttleBeaconObject> destinations,
         List<ShuttleExclusionObject> exclusions)
     {
         FTLState = ftlState;
-        FTLDuration = ftlDuration;
+        FTLTime = ftlTime;
         Destinations = destinations;
         Exclusions = exclusions;
     }
diff --git a/Content.Shared/Timing/StartEndTime.cs b/Content.Shared/Timing/StartEndTime.cs
new file mode 100644 (file)
index 0000000..fde9f73
--- /dev/null
@@ -0,0 +1,68 @@
+using Robust.Shared.Timing;
+
+namespace Content.Shared.Timing;
+
+/// <summary>
+/// Represents a range of an "action" in time, as start/end times.
+/// </summary>
+/// <remarks>
+/// Positions in time are represented as <see cref="TimeSpan"/>s, usually from <see cref="IGameTiming.CurTime"/>
+/// or <see cref="IGameTiming.RealTime"/>.
+/// </remarks>
+/// <param name="Start">The time the action starts.</param>
+/// <param name="End">The time action ends.</param>
+[Serializable]
+public record struct StartEndTime(TimeSpan Start, TimeSpan End)
+{
+    /// <summary>
+    /// How long the action takes.
+    /// </summary>
+    public TimeSpan Length => End - Start;
+
+    /// <summary>
+    /// Get how far the action has progressed relative to a time value.
+    /// </summary>
+    /// <param name="time">The time to get the current progress value for.</param>
+    /// <param name="clamp">If true, clamp values outside the time range to 0 through 1.</param>
+    /// <returns>
+    /// <para>
+    /// A progress value. Zero means <paramref name="time"/> is at <see cref="Start"/>,
+    /// one means <paramref name="time"/> is at <see cref="End"/>.
+    /// </para>
+    /// <para>
+    /// This function returns <see cref="float.NaN"/> if <see cref="Start"/> and <see cref="End"/> are identical.
+    /// </para>
+    /// </returns>
+    public float ProgressAt(TimeSpan time, bool clamp = true)
+    {
+        var length = Length;
+        if (length == default)
+            return float.NaN;
+
+        var progress = (float) ((time - Start) / length);
+        if (clamp)
+            progress = MathHelper.Clamp01(progress);
+
+        return progress;
+    }
+
+    public static StartEndTime FromStartDuration(TimeSpan start, TimeSpan duration)
+    {
+        return new StartEndTime(start, start + duration);
+    }
+
+    public static StartEndTime FromStartDuration(TimeSpan start, float durationSeconds)
+    {
+        return new StartEndTime(start, start + TimeSpan.FromSeconds(durationSeconds));
+    }
+
+    public static StartEndTime FromCurTime(IGameTiming gameTiming, TimeSpan duration)
+    {
+        return FromStartDuration(gameTiming.CurTime, duration);
+    }
+
+    public static StartEndTime FromCurTime(IGameTiming gameTiming, float durationSeconds)
+    {
+        return FromStartDuration(gameTiming.CurTime, durationSeconds);
+    }
+}