// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using EpicGames.Core; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace HordeServer.Server { /// /// Service which manages the downtime schedule /// public sealed class DowntimeService : IDowntimeService, IHostedService, IAsyncDisposable { /// /// Whether the server is currently in downtime /// public bool IsDowntimeActive { get; private set; } readonly IClock _clock; readonly IOptionsMonitor _globalConfig; readonly ILogger _logger; readonly ITicker _ticker; /// /// Constructor /// public DowntimeService(IClock clock, IOptionsMonitor globalConfig, ILogger logger) { _clock = clock; _globalConfig = globalConfig; _logger = logger; // Ensure the initial value to be correct Tick(); _ticker = clock.AddTicker(TimeSpan.FromMinutes(1.0), TickAsync, logger); } /// public Task StartAsync(CancellationToken cancellationToken) => _ticker.StartAsync(); /// public Task StopAsync(CancellationToken cancellationToken) => _ticker.StopAsync(); /// public async ValueTask DisposeAsync() => await _ticker.DisposeAsync(); /// /// Periodically called tick function /// /// Token indicating that the service should stop /// Async task ValueTask TickAsync(CancellationToken stoppingToken) { Tick(); return new ValueTask(); } internal void Tick() { GlobalConfig globalConfig = _globalConfig.CurrentValue; DateTimeOffset now = TimeZoneInfo.ConvertTime(new DateTimeOffset(_clock.UtcNow), _clock.TimeZone); bool isActive = globalConfig.Downtime.Any(x => x.IsActive(now)); DateTimeOffset? next = null; foreach (ScheduledDowntime schedule in globalConfig.Downtime) { DateTimeOffset start = schedule.GetNext(now).StartTime; if (next == null || start < next) { next = start; } } if (next != null) { _logger.LogInformation("Server time: {Time}. Downtime: {Downtime}. Next: {Next}.", now, isActive, next.Value); } if (isActive != IsDowntimeActive) { IsDowntimeActive = isActive; if (IsDowntimeActive) { _logger.LogInformation("Entering downtime"); } else { _logger.LogInformation("Leaving downtime"); } } } } }