using System.Diagnostics; using Microsoft.Extensions.Hosting; namespace TanksServer.GameLogic; internal sealed class GameTickWorker( IEnumerable steps, IHostApplicationLifetime lifetime, ILogger logger ) : IHostedLifecycleService, IDisposable { private readonly CancellationTokenSource _cancellation = new(); private readonly TaskCompletionSource _shutdownCompletion = new(); private readonly List _steps = steps.ToList(); public async Task StartedAsync(CancellationToken cancellationToken) { await Task.Yield(); // the first tick is really short (< 0.01ms) if this line is directly above the while var sw = Stopwatch.StartNew(); await Task.Delay(1, CancellationToken.None).ConfigureAwait(false); try { while (!_cancellation.IsCancellationRequested) { var delta = sw.Elapsed; sw.Restart(); foreach (var step in _steps) await step.TickAsync(delta); } } catch (Exception ex) { logger.LogError(ex, "game tick service crashed"); lifetime.StopApplication(); } _shutdownCompletion.SetResult(); } public Task StoppingAsync(CancellationToken cancellationToken) => _cancellation.CancelAsync(); public Task StopAsync(CancellationToken cancellationToken) => _shutdownCompletion.Task; public void Dispose() => _cancellation.Dispose(); public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask; public Task StartingAsync(CancellationToken cancellationToken) => Task.CompletedTask; public Task StoppedAsync(CancellationToken cancellationToken) => Task.CompletedTask; }