// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // namespace MUnique.OpenMU.ConnectServer.Host; using System.Net; using System.Threading; using Microsoft.Extensions.Logging; using MUnique.OpenMU.Interfaces; using Nito.AsyncEx; /// /// A registry which keeps track of available s. /// /// public sealed class GameServerRegistry : IDisposable { private readonly TimeSpan _timeout = TimeSpan.FromSeconds(10); private readonly CancellationTokenSource _disposeCts = new(); private readonly IConnectServer _connectServer; private readonly ILogger _logger; private readonly Dictionary _entries = new(); private readonly AsyncLock _lock = new(); /// /// Initializes a new instance of the class. /// /// The connect server. /// The logger. public GameServerRegistry(IConnectServer connectServer, ILogger logger) { this._connectServer = connectServer; this._logger = logger; async Task RunCleanupLoop() { try { await this.CleanupLoopAsync(this._disposeCts.Token).ConfigureAwait(false); } catch (Exception ex) { this._logger.LogError(ex, "Error in cleanup loop"); } } _ = RunCleanupLoop(); } /// public void Dispose() { this._disposeCts.Cancel(); this._disposeCts.Dispose(); } /// /// Updates the registration. /// /// The server information. /// The public end point. public async Task UpdateRegistrationAsync(ServerInfo serverInfo, IPEndPoint publicEndPoint) { using var l = await this._lock.LockAsync().ConfigureAwait(false); var isNew = !this._entries.ContainsKey(serverInfo.Id); this._entries[serverInfo.Id] = DateTime.UtcNow; if (isNew) { this._connectServer.RegisterGameServer(serverInfo, publicEndPoint); } else { this._connectServer.CurrentConnectionsChanged(serverInfo.Id, serverInfo.CurrentConnections); } } private async Task CleanupLoopAsync(CancellationToken cancellationToken) { var tempRemoved = new List(); while (!this._disposeCts.IsCancellationRequested) { await Task.Delay(2000, cancellationToken).ConfigureAwait(false); using var l = await this._lock.LockAsync(cancellationToken).ConfigureAwait(false); foreach (var serverId in this._entries.Keys) { var lastUpdate = this._entries[serverId]; var diff = DateTime.UtcNow - lastUpdate; if (diff > this._timeout) { this._logger.LogInformation("Difference of {0} higher than timeout for server {1}", diff, serverId); this._connectServer.UnregisterGameServer(serverId); tempRemoved.Add(serverId); } } foreach (var serverId in tempRemoved) { this._entries.Remove(serverId, out _); } tempRemoved.Clear(); } } }