using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Robust.Shared.Exceptions; using Robust.Shared.IoC; using Robust.Shared.ViewVariables; using Timer = Robust.Shared.Timing.Timer; namespace Robust.Shared.GameObjects { public sealed class TimerComponent : Component { [Dependency] private readonly IRuntimeLog _runtimeLog = default!; private readonly List<(Timer timer, CancellationToken source)> _timers = new(); public int TimerCount => _timers.Count; /// /// Should this component be removed when no more timers are running? /// [ViewVariables(VVAccess.ReadWrite)] public bool RemoveOnEmpty { get; set; } = true; public void Update(float frameTime) { // Manual for loop so we can modify the list while enumerating. // ReSharper disable once ForCanBeConvertedToForeach for (var i = 0; i < _timers.Count; i++) { var (timer, cancellationToken) = _timers[i]; if (cancellationToken.IsCancellationRequested) { continue; } timer.Update(frameTime, _runtimeLog); } _timers.RemoveAll(timer => !timer.Item1.IsActive || timer.source.IsCancellationRequested); } public void AddTimer(Timer timer, CancellationToken cancellationToken = default) { _timers.Add((timer, cancellationToken)); } /// /// Creates a task that will complete after a given delay. /// The task is resumed on the main game logic thread. /// /// The length of time, in milliseconds, to delay for. /// /// The task that can be awaited. public Task Delay(int milliseconds, CancellationToken cancellationToken = default) { var tcs = new TaskCompletionSource(); Spawn(milliseconds, () => tcs.SetResult(null), cancellationToken); return tcs.Task; } /// /// Creates a task that will complete after a given delay. /// The task is resumed on the main game logic thread. /// /// The length of time to delay for. /// /// The task that can be awaited. public Task Delay(TimeSpan duration, CancellationToken cancellationToken = default) { return Delay((int) duration.TotalMilliseconds, cancellationToken); } /// /// Schedule an action to be fired after a certain delay. /// The action will be resumed on the main game logic thread. /// /// The length of time, in milliseconds, to wait before firing the action. /// The action to fire. /// public void Spawn(int milliseconds, Action onFired, CancellationToken cancellationToken = default) { var timer = new Timer(milliseconds, false, onFired); AddTimer(timer, cancellationToken); } /// /// Schedule an action to be fired after a certain delay. /// The action will be resumed on the main game logic thread. /// /// The length of time, to wait before firing the action. /// The action to fire. /// public void Spawn(TimeSpan duration, Action onFired, CancellationToken cancellationToken = default) { Spawn((int) duration.TotalMilliseconds, onFired, cancellationToken); } /// /// Schedule an action that repeatedly fires after a delay specified in milliseconds. /// /// The length of time, in milliseconds, to delay before firing the repeated action. /// The action to fire. /// The CancellationToken for stopping the Timer. public void SpawnRepeating(int milliseconds, Action onFired, CancellationToken cancellationToken) { var timer = new Timer(milliseconds, true, onFired); AddTimer(timer, cancellationToken); } /// /// Schedule an action that repeatedly fires after a delay. /// /// The length of time to delay before firing the repeated action. /// The action to fire. /// The CancellationToken for stopping the Timer. public void SpawnRepeating(TimeSpan duration, Action onFired, CancellationToken cancellationToken) { SpawnRepeating((int) duration.TotalMilliseconds, onFired, cancellationToken); } } }