diff --git a/src/MHServerEmu.PlayerManagement/Players/LoginQueueManager.cs b/src/MHServerEmu.PlayerManagement/Players/LoginQueueManager.cs index f8733a39..278cada4 100644 --- a/src/MHServerEmu.PlayerManagement/Players/LoginQueueManager.cs +++ b/src/MHServerEmu.PlayerManagement/Players/LoginQueueManager.cs @@ -13,10 +13,12 @@ namespace MHServerEmu.PlayerManagement.Players public class LoginQueueManager { private const ushort MuxChannel = 1; + private const float ReconnectPermissionPercentile = 0.1f; private static readonly Logger Logger = LogManager.CreateLogger(); private static readonly TimeSpan PendingClientTimeout = TimeSpan.FromSeconds(15); private static readonly TimeSpan StatusUpdateInterval = TimeSpan.FromSeconds(10); + private static readonly TimeSpan ReconnectPermissionDuration = TimeSpan.FromMinutes(10); private readonly DoubleBufferQueue _newClientQueue = new(); @@ -34,6 +36,9 @@ namespace MHServerEmu.PlayerManagement.Players private readonly LoginQueueStatus.Builder _statusBuilder = LoginQueueStatus.CreateBuilder(); private readonly Dictionary _statusUpdateTimes; + private readonly Dictionary _reconnectPermissions = new(); + private CooldownTimer _reconnectPermissionPurgeTimer = new(TimeSpan.FromMinutes(1)); + public int PlayersInLine { get => _defaultQueue.Count + _reconnectQueue.Count; } public LoginQueueManager(PlayerManagerService playerManager) @@ -55,6 +60,7 @@ namespace MHServerEmu.PlayerManagement.Players public void Update() { TimeOutPendingClients(); + PurgeReconnectPermissions(); AcceptNewClients(); ProcessLoginQueue(); } @@ -94,6 +100,24 @@ namespace MHServerEmu.PlayerManagement.Players } } + private void PurgeReconnectPermissions() + { + if (_reconnectPermissionPurgeTimer.Check() == false) + return; + + TimeSpan now = Clock.UnixTime; + + foreach (var kvp in _reconnectPermissions) + { + TimeSpan duration = now - kvp.Value; + if (duration >= ReconnectPermissionDuration) + { + Logger.Info($"Purged reconnect permission for account 0x{kvp.Key:X}"); + _reconnectPermissions.Remove(kvp.Key); + } + } + } + /// /// Accepts asynchronously added clients to the login queue. /// @@ -122,9 +146,6 @@ namespace MHServerEmu.PlayerManagement.Players } else { - // TODO: check reconnect - LinkedList queueToUse = _defaultQueue; - if (_queueNodes.TryPop(out LinkedListNode queueNode) == false) { Logger.Warn($"AcceptNewClients(): Unable to accept client [{client}], the queue already has {PlayersInLine} clients, which is the maximum number allowed by the current server configuration"); @@ -133,6 +154,14 @@ namespace MHServerEmu.PlayerManagement.Players continue; } + LinkedList queueToUse = _defaultQueue; + + if (_reconnectPermissions.Remove(client.DbId)) + { + queueToUse = _reconnectQueue; + Logger.Info($"Consumed reconnect permission for client [{client}]"); + } + queueNode.Value = client; queueToUse.AddLast(queueNode); } @@ -208,6 +237,7 @@ namespace MHServerEmu.PlayerManagement.Players if (client.IsConnected == false) { Logger.Warn($"ProcessQueuedClient(): Client [{client}] disconnected while waiting in the login queue"); + TryAddReconnectPermission(client, 1, Clock.UnixTime); RemoveClientSession(client); return false; } @@ -228,12 +258,6 @@ namespace MHServerEmu.PlayerManagement.Players return true; } - private void RemoveClientSession(IFrontendClient client) - { - ulong sessionId = client.Session.Id; - _playerManager.SessionManager.RemoveActiveSession(sessionId); - } - private void UpdateQueueStatus(LinkedList queue, ref ulong nextPlaceInLine, TimeSpan now) { LinkedListNode current = queue.First; @@ -249,6 +273,7 @@ namespace MHServerEmu.PlayerManagement.Players current = current.Next; RemoveQueueNode(prev); + TryAddReconnectPermission(client, nextPlaceInLine, now); RemoveClientSession(client); continue; @@ -274,5 +299,24 @@ namespace MHServerEmu.PlayerManagement.Players node.Remove(); _queueNodes.Push(node); } + + private void RemoveClientSession(IFrontendClient client) + { + ulong sessionId = client.Session.Id; + _playerManager.SessionManager.RemoveActiveSession(sessionId); + } + + private void TryAddReconnectPermission(IFrontendClient client, ulong placeInLine, TimeSpan now) + { + if (PlayersInLine == 0) + return; + + float percentile = (float)placeInLine / PlayersInLine; + if (placeInLine == 1 || percentile <= ReconnectPermissionPercentile) + { + _reconnectPermissions[client.DbId] = now; + Logger.Info($"Added reconnect permission for client [{client}] (placeInLine={placeInLine})"); + } + } } }