Files
AdamuSw/src/Dapr/ConnectServer.Host/GameServerRegistry.cs
2026-07-14 19:00:35 +03:00

106 lines
3.6 KiB
C#

// <copyright file="GameServerRegistry.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.ConnectServer.Host;
using System.Net;
using System.Threading;
using Microsoft.Extensions.Logging;
using MUnique.OpenMU.Interfaces;
using Nito.AsyncEx;
/// <summary>
/// A registry which keeps track of available <see cref="IGameServer"/>s.
/// </summary>
/// <seealso cref="System.IDisposable" />
public sealed class GameServerRegistry : IDisposable
{
private readonly TimeSpan _timeout = TimeSpan.FromSeconds(10);
private readonly CancellationTokenSource _disposeCts = new();
private readonly IConnectServer _connectServer;
private readonly ILogger<GameServerRegistry> _logger;
private readonly Dictionary<ushort, DateTime> _entries = new();
private readonly AsyncLock _lock = new();
/// <summary>
/// Initializes a new instance of the <see cref="GameServerRegistry"/> class.
/// </summary>
/// <param name="connectServer">The connect server.</param>
/// <param name="logger">The logger.</param>
public GameServerRegistry(IConnectServer connectServer, ILogger<GameServerRegistry> 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();
}
/// <inheritdoc />
public void Dispose()
{
this._disposeCts.Cancel();
this._disposeCts.Dispose();
}
/// <summary>
/// Updates the registration.
/// </summary>
/// <param name="serverInfo">The server information.</param>
/// <param name="publicEndPoint">The public end point.</param>
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<ushort>();
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();
}
}
}