diff --git a/Content.Server/DeviceLinking/Systems/DeviceLinkSystem.cs b/Content.Server/DeviceLinking/Systems/DeviceLinkSystem.cs index cec92db44c..d957f0171e 100644 --- a/Content.Server/DeviceLinking/Systems/DeviceLinkSystem.cs +++ b/Content.Server/DeviceLinking/Systems/DeviceLinkSystem.cs @@ -20,23 +20,6 @@ public sealed class DeviceLinkSystem : SharedDeviceLinkSystem SubscribeLocalEvent(OnNewLink); } - public override void Update(float frameTime) - { - var query = EntityQueryEnumerator(); - - while (query.MoveNext(out var component)) - { - if (component.InvokeLimit < 1) - { - component.InvokeCounter = 0; - continue; - } - - if(component.InvokeCounter > 0) - component.InvokeCounter--; - } - } - #region Sending & Receiving public override void InvokePort(EntityUid uid, string port, NetworkPayload? data = null, DeviceLinkSourceComponent? sourceComponent = null) { @@ -67,16 +50,17 @@ public sealed class DeviceLinkSystem : SharedDeviceLinkSystem if (!Resolve(sink, ref sink.Comp)) return; - if (sink.Comp.InvokeCounter > sink.Comp.InvokeLimit) + var invokeCounter = GetEffectiveInvokeCounter(sink.Comp); + if (invokeCounter > sink.Comp.InvokeLimit) { - sink.Comp.InvokeCounter = 0; + SetInvokeCounter(sink.Comp, 0); var args = new DeviceLinkOverloadedEvent(); RaiseLocalEvent(sink, ref args); RemoveAllFromSink(sink, sink.Comp); return; } - sink.Comp.InvokeCounter++; + SetInvokeCounter(sink.Comp, invokeCounter + 1); //Just skip using device networking if the source or the sink doesn't support it if (!HasComp(source) || !TryComp(sink, out var sinkNetwork)) diff --git a/Content.Shared/DeviceLinking/DeviceLinkSinkComponent.cs b/Content.Shared/DeviceLinking/DeviceLinkSinkComponent.cs index 5d901b3fa6..83a55a9bb6 100644 --- a/Content.Shared/DeviceLinking/DeviceLinkSinkComponent.cs +++ b/Content.Shared/DeviceLinking/DeviceLinkSinkComponent.cs @@ -1,6 +1,7 @@ using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; +using Robust.Shared.Timing; namespace Content.Shared.DeviceLinking; @@ -23,11 +24,20 @@ public sealed partial class DeviceLinkSinkComponent : Component public HashSet LinkedSources = new(); /// - /// Counts the amount of times a sink has been invoked for severing the link if this counter gets to high - /// The counter is counted down by one every tick if it's higher than 0 - /// This is for preventing infinite loops + /// The tick was set at. Used to calculate the real value for the current tick. /// + [Access(typeof(SharedDeviceLinkSystem), Other = AccessPermissions.None)] + public GameTick InvokeCounterTick; + + /// + /// Counter used to throttle device invocations to avoid infinite loops. + /// + /// + /// This is stored relative to . For reading the real value, + /// should be used. + /// [DataField] + [Access(typeof(SharedDeviceLinkSystem), Other = AccessPermissions.None)] public int InvokeCounter; /// diff --git a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs index 3f969684b6..e51087fdf6 100644 --- a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs +++ b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.DeviceLinking.Events; using Content.Shared.DeviceNetwork; using Content.Shared.Popups; using Robust.Shared.Prototypes; +using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Shared.DeviceLinking; @@ -14,6 +15,7 @@ public abstract class SharedDeviceLinkSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; public const string InvokedPort = "link_port"; @@ -525,4 +527,30 @@ public abstract class SharedDeviceLinkSystem : EntitySystem // NOOP on client for the moment. } #endregion + + /// + /// Gets how many times a has been invoked recently. + /// + /// + /// The return value of this function goes up by one every time a sink is invoked, and goes down by one every tick. + /// + public int GetEffectiveInvokeCounter(DeviceLinkSinkComponent sink) + { + // Shouldn't be possible but just to be safe. + var curTick = _gameTiming.CurTick; + if (curTick < sink.InvokeCounterTick) + return 0; + + var tickDelta = curTick.Value - sink.InvokeCounterTick.Value; + if (tickDelta >= sink.InvokeCounter) + return 0; + + return Math.Max(0, sink.InvokeCounter - (int)tickDelta); + } + + protected void SetInvokeCounter(DeviceLinkSinkComponent sink, int value) + { + sink.InvokeCounterTick = _gameTiming.CurTick; + sink.InvokeCounter = value; + } }