diff --git a/Content.Server/Interaction/InteractionSystem.cs b/Content.Server/Interaction/InteractionSystem.cs index d39618d0e5..823b369b54 100644 --- a/Content.Server/Interaction/InteractionSystem.cs +++ b/Content.Server/Interaction/InteractionSystem.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; using Content.Server.Administration.Logs; @@ -7,6 +6,7 @@ using Content.Server.CombatMode; using Content.Server.Hands.Components; using Content.Server.Pulling; using Content.Server.Storage.Components; +using Content.Server.Timing; using Content.Shared.ActionBlocker; using Content.Shared.Database; using Content.Shared.DragDrop; @@ -14,18 +14,16 @@ using Content.Shared.Input; using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; using Content.Shared.Item; -using Content.Shared.Popups; using Content.Shared.Pulling.Components; +using Content.Shared.Timing; using Content.Shared.Weapons.Melee; using JetBrains.Annotations; using Robust.Server.GameObjects; -using Robust.Server.Player; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.Input; using Robust.Shared.Input.Binding; using Robust.Shared.IoC; -using Robust.Shared.Localization; using Robust.Shared.Log; using Robust.Shared.Map; using Robust.Shared.Players; @@ -41,6 +39,7 @@ namespace Content.Server.Interaction [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly PullingSystem _pullSystem = default!; [Dependency] private readonly AdminLogSystem _adminLogSystem = default!; + [Dependency] private readonly UseDelaySystem _useDelay = default!; public override void Initialize() { @@ -87,6 +86,11 @@ namespace Content.Server.Interaction return storage.SubscribedSessions.Contains(actor.PlayerSession); } + protected override void BeginDelay(UseDelayComponent? component = null) + { + _useDelay.BeginDelay(component); + } + #region Drag drop private void HandleDragDropRequestEvent(DragDropRequestEvent msg, EntitySessionEventArgs args) { diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index de4df3d0b6..9e4f0e5d60 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -202,7 +202,7 @@ namespace Content.Server.Nutrition.EntitySystems } if (string.IsNullOrEmpty(food.TrashPrototype)) - EntityManager.QueueDeleteEntity((food).Owner); + EntityManager.QueueDeleteEntity(food.Owner); else DeleteAndSpawnTrash(food, user); @@ -231,7 +231,7 @@ namespace Content.Server.Nutrition.EntitySystems return; } - EntityManager.QueueDeleteEntity((component).Owner); + EntityManager.QueueDeleteEntity(component.Owner); } private void AddEatVerb(EntityUid uid, FoodComponent component, GetInteractionVerbsEvent ev) diff --git a/Content.Server/Timing/UseDelaySystem.cs b/Content.Server/Timing/UseDelaySystem.cs new file mode 100644 index 0000000000..aa9f6977a8 --- /dev/null +++ b/Content.Server/Timing/UseDelaySystem.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using Content.Shared.Cooldown; +using Content.Shared.Timing; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Timing; +using Robust.Shared.Utility; + +namespace Content.Server.Timing; + +public sealed class UseDelaySystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + + private HashSet _activeDelays = new(); + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var toRemove = new RemQueue(); + + foreach (var delay in _activeDelays) + { + MetaDataComponent? metaData = null; + + if (Deleted(delay.Owner, metaData) || + delay.CancellationTokenSource?.Token.IsCancellationRequested == true) + { + toRemove.Add(delay); + continue; + } + + if (Paused(delay.Owner, metaData)) continue; + + delay.Elapsed += frameTime; + + if (delay.Elapsed < delay.Delay) continue; + + toRemove.Add(delay); + + } + + foreach (var delay in toRemove) + { + delay.CancellationTokenSource = null; + delay.Elapsed = 0f; + _activeDelays.Remove(delay); + } + } + + public void BeginDelay(UseDelayComponent? component = null) + { + if (component == null || + component.ActiveDelay || + Deleted(component.Owner)) return; + + component.CancellationTokenSource = new CancellationTokenSource(); + + DebugTools.Assert(!_activeDelays.Contains(component)); + _activeDelays.Add(component); + + var currentTime = _gameTiming.CurTime; + component.LastUseTime = currentTime; + + var cooldown = EnsureComp(component.Owner); + cooldown.CooldownStart = currentTime; + cooldown.CooldownEnd = currentTime + TimeSpan.FromSeconds(component.Delay); + } + + public void Cancel(UseDelayComponent component) + { + component.CancellationTokenSource?.Cancel(); + component.CancellationTokenSource = null; + + if (TryComp(component.Owner, out var cooldown)) + { + cooldown.CooldownEnd = _gameTiming.CurTime; + } + } + + public void Restart(UseDelayComponent component) + { + component.CancellationTokenSource?.Cancel(); + component.CancellationTokenSource = null; + BeginDelay(component); + } +} diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index bf2560c0cb..3ed5f9d5db 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -1,17 +1,14 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Content.Shared.ActionBlocker; using Content.Shared.Administration.Logs; using Content.Shared.CombatMode; using Content.Shared.Database; -using Content.Shared.Hands; using Content.Shared.Hands.Components; using Content.Shared.Input; using Content.Shared.Interaction.Helpers; -using Content.Shared.Inventory; using Content.Shared.Physics; using Content.Shared.Popups; using Content.Shared.Throwing; @@ -625,7 +622,7 @@ namespace Content.Shared.Interaction RaiseLocalEvent(used, activateMsg); if (activateMsg.Handled) { - delayComponent?.BeginDelay(); + BeginDelay(delayComponent); _adminLogSystem.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}"); return; } @@ -635,7 +632,7 @@ namespace Content.Shared.Interaction var activateEventArgs = new ActivateEventArgs(user, used); activateComp.Activate(activateEventArgs); - delayComponent?.BeginDelay(); + BeginDelay(delayComponent); _adminLogSystem.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}"); // No way to check success. } #endregion @@ -669,7 +666,7 @@ namespace Content.Shared.Interaction RaiseLocalEvent(used, useMsg); if (useMsg.Handled) { - delayComponent?.BeginDelay(); + BeginDelay(delayComponent); return true; } @@ -681,7 +678,7 @@ namespace Content.Shared.Interaction // If a Use returns a status completion we finish our interaction if (use.UseEntity(new UseEntityEventArgs(user))) { - delayComponent?.BeginDelay(); + BeginDelay(delayComponent); return true; } } @@ -689,6 +686,12 @@ namespace Content.Shared.Interaction return false; } + protected virtual void BeginDelay(UseDelayComponent? component = null) + { + // This is temporary until we have predicted UseDelay. + return; + } + /// /// Alternative interactions on an entity. /// diff --git a/Content.Shared/Timing/UseDelayComponent.cs b/Content.Shared/Timing/UseDelayComponent.cs index 87ad332fb8..c6e73732a0 100644 --- a/Content.Shared/Timing/UseDelayComponent.cs +++ b/Content.Shared/Timing/UseDelayComponent.cs @@ -1,10 +1,7 @@ using System; using System.Threading; -using Content.Shared.Cooldown; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.Timing; using Robust.Shared.ViewVariables; namespace Content.Shared.Timing @@ -12,61 +9,21 @@ namespace Content.Shared.Timing /// /// Timer that creates a cooldown each time an object is activated/used /// - [RegisterComponent] - public class UseDelayComponent : Component + [RegisterComponent, ComponentProtoName("UseDelay")] + public sealed class UseDelayComponent : Component { - public override string Name => "UseDelay"; - - private TimeSpan _lastUseTime; + [ViewVariables] + public TimeSpan LastUseTime; + [ViewVariables] [DataField("delay")] - private float _delay = 1; - /// - /// The time, in seconds, between an object's use and when it can be used again - /// - [ViewVariables(VVAccess.ReadWrite)] - public float Delay { get => _delay; set => _delay = value; } + public float Delay = 1; - public bool ActiveDelay{ get; private set; } + [ViewVariables] + public float Elapsed = 0f; - private CancellationTokenSource? cancellationTokenSource; + public CancellationTokenSource? CancellationTokenSource; - public void BeginDelay() - { - if (ActiveDelay) - { - return; - } - - ActiveDelay = true; - - cancellationTokenSource = new CancellationTokenSource(); - - Owner.SpawnTimer(TimeSpan.FromSeconds(Delay), () => ActiveDelay = false, cancellationTokenSource.Token); - - _lastUseTime = IoCManager.Resolve().CurTime; - - var cooldown = IoCManager.Resolve().EnsureComponent(Owner); - cooldown.CooldownStart = _lastUseTime; - cooldown.CooldownEnd = _lastUseTime + TimeSpan.FromSeconds(Delay); - } - - public void Cancel() - { - cancellationTokenSource?.Cancel(); - ActiveDelay = false; - - if (IoCManager.Resolve().TryGetComponent(Owner, out ItemCooldownComponent? cooldown)) - { - cooldown.CooldownEnd = IoCManager.Resolve().CurTime; - } - } - - public void Restart() - { - cancellationTokenSource?.Cancel(); - ActiveDelay = false; - BeginDelay(); - } + public bool ActiveDelay => CancellationTokenSource is { Token: { IsCancellationRequested: false } }; } }