]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Throttle people trying to connect to a full server. (#20972)
authorPieter-Jan Briers <pieterjan.briers+git@gmail.com>
Thu, 14 Mar 2024 08:00:47 +0000 (09:00 +0100)
committerGitHub <noreply@github.com>
Thu, 14 Mar 2024 08:00:47 +0000 (19:00 +1100)
* Throttle people trying to connect to a full server.

To reduce spam/load on the server and connection logs table.

Players are forced to wait 30 seconds after getting denied for "server full", before they can try connecting again.

This code is an absolute nightmare mess. I tried to re-use the existing code for the redial timer but god everything here sucks so much.

Requires https://github.com/space-wizards/RobustToolbox/pull/4487

* Use new NetDisconnectMessage API instead.

* Add admin.bypass_max_players CVar.

Just something to help with debugging the player cap on dev, I don't expect this to ever be disabled on real servers.

Content.Client/Launcher/LauncherConnecting.cs
Content.Client/Launcher/LauncherConnectingGui.xaml
Content.Client/Launcher/LauncherConnectingGui.xaml.cs
Content.Server/Connection/ConnectionManager.cs
Content.Shared/CCVar/CCVars.cs

index 9625f9e31c52d44bca797cb31453ca072b01b902..29b241ae5df35e0e2924540c8dabc6e4cfc0ff78 100644 (file)
@@ -54,6 +54,7 @@ namespace Content.Client.Launcher
         public event Action<Page>? PageChanged;
         public event Action<string?>? ConnectFailReasonChanged;
         public event Action<ClientConnectionState>? ConnectionStateChanged;
+        public event Action<NetConnectFailArgs>? ConnectFailed;
 
         protected override void Startup()
         {
@@ -85,6 +86,7 @@ namespace Content.Client.Launcher
             }
             ConnectFailReason = args.Reason;
             CurrentPage = Page.ConnectFailed;
+            ConnectFailed?.Invoke(args);
         }
 
         private void OnConnectStateChanged(ClientConnectionState state)
index 083e4ca8717a90f9234174aa291eae3279e5b3e1..3734fa5b3adc56bee2f7352f9e9bee154dcc7bf4 100644 (file)
                         <Button Name="ReconnectButton" Text="{Loc 'connecting-reconnect'}"
                                 HorizontalAlignment="Center"
                                 VerticalExpand="True" VerticalAlignment="Bottom" />
-                        <Button Name="RedialButton" Text="{Loc 'connecting-redial'}"
-                                Disabled="True"
-                                HorizontalAlignment="Center"
-                                VerticalExpand="True" VerticalAlignment="Bottom" />
                     </BoxContainer>
                 </Control>
                 <Label Name="ConnectingAddress" StyleClasses="LabelSubText" HorizontalAlignment="Center" />
index 79fba7c285dff7e1498234a0f2b81eeb6e3d310d..ac74ad7b60d21e9ff05918ab0ef25dfed7339d26 100644 (file)
@@ -9,7 +9,6 @@ using Robust.Client.UserInterface.XAML;
 using Robust.Shared.Configuration;
 using Robust.Shared.IoC;
 using Robust.Shared.Timing;
-using Robust.Shared.Localization;
 using Robust.Shared.Network;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
@@ -21,12 +20,15 @@ namespace Content.Client.Launcher
     {
         private const float RedialWaitTimeSeconds = 15f;
         private readonly LauncherConnecting _state;
+        private float _waitTime;
+
+        // Pressing reconnect will redial instead of simply reconnecting.
+        private bool _redial;
+
         private readonly IRobustRandom _random;
         private readonly IPrototypeManager _prototype;
         private readonly IConfigurationManager _cfg;
 
-        private float _redialWaitTime = RedialWaitTimeSeconds;
-
         public LauncherConnectingGui(LauncherConnecting state, IRobustRandom random,
             IPrototypeManager prototype, IConfigurationManager config)
         {
@@ -42,14 +44,8 @@ namespace Content.Client.Launcher
             Stylesheet = IoCManager.Resolve<IStylesheetManager>().SheetSpace;
 
             ChangeLoginTip();
-            ReconnectButton.OnPressed += _ => _state.RetryConnect();
-            // Redial shouldn't fail, but if it does, try a reconnect (maybe we're being run from debug)
-            RedialButton.OnPressed += _ =>
-            {
-                if (!_state.Redial())
-                    _state.RetryConnect();
-            };
-            RetryButton.OnPressed += _ => _state.RetryConnect();
+            ReconnectButton.OnPressed += ReconnectButtonPressed;
+            RetryButton.OnPressed += ReconnectButtonPressed;
             ExitButton.OnPressed += _ => _state.Exit();
 
             var addr = state.Address;
@@ -59,6 +55,7 @@ namespace Content.Client.Launcher
             state.PageChanged += OnPageChanged;
             state.ConnectFailReasonChanged += ConnectFailReasonChanged;
             state.ConnectionStateChanged += ConnectionStateChanged;
+            state.ConnectFailed += HandleDisconnectReason;
 
             ConnectionStateChanged(state.ConnectionState);
 
@@ -68,6 +65,19 @@ namespace Content.Client.Launcher
             LastNetDisconnectedArgsChanged(edim.LastNetDisconnectedArgs);
         }
 
+        // Just button, there's only one at once anyways :)
+        private void ReconnectButtonPressed(BaseButton.ButtonEventArgs args)
+        {
+            if (_redial)
+            {
+                // Redial shouldn't fail, but if it does, try a reconnect (maybe we're being run from debug)
+                if (_state.Redial())
+                    return;
+            }
+
+            _state.RetryConnect();
+        }
+
         private void ConnectFailReasonChanged(string? reason)
         {
             ConnectFailReason.SetMessage(reason == null
@@ -77,9 +87,30 @@ namespace Content.Client.Launcher
 
         private void LastNetDisconnectedArgsChanged(NetDisconnectedArgs? args)
         {
-            var redialFlag = args?.RedialFlag ?? false;
-            RedialButton.Visible = redialFlag;
-            ReconnectButton.Visible = !redialFlag;
+            HandleDisconnectReason(args);
+        }
+
+        private void HandleDisconnectReason(INetStructuredReason? reason)
+        {
+            if (reason == null)
+            {
+                _waitTime = 0;
+                _redial = false;
+            }
+            else
+            {
+                _redial = reason.RedialFlag;
+
+                if (reason.Message.Int32Of("delay") is { } delay)
+                {
+                    _waitTime = delay;
+                }
+                else if (_redial)
+                {
+                    _waitTime = RedialWaitTimeSeconds;
+                }
+
+            }
         }
 
         private void ChangeLoginTip()
@@ -108,16 +139,27 @@ namespace Content.Client.Launcher
         protected override void FrameUpdate(FrameEventArgs args)
         {
             base.FrameUpdate(args);
-            _redialWaitTime -= args.DeltaSeconds;
-            if (_redialWaitTime <= 0)
+
+            var button = _state.CurrentPage == LauncherConnecting.Page.ConnectFailed
+                ? RetryButton
+                : ReconnectButton;
+
+            _waitTime -= args.DeltaSeconds;
+            if (_waitTime <= 0)
             {
-                RedialButton.Disabled = false;
-                RedialButton.Text = Loc.GetString("connecting-redial");
+                button.Disabled = false;
+                var key = _redial
+                    ? "connecting-redial"
+                    : _state.CurrentPage == LauncherConnecting.Page.ConnectFailed
+                        ? "connecting-reconnect"
+                        : "connecting-retry";
+
+                button.Text = Loc.GetString(key);
             }
             else
             {
-                RedialButton.Disabled = true;
-                RedialButton.Text = Loc.GetString("connecting-redial-wait", ("time", _redialWaitTime.ToString("00.000")));
+                button.Disabled = true;
+                button.Text = Loc.GetString("connecting-redial-wait", ("time", _waitTime.ToString("00.000")));
             }
         }
 
index 2fbc6f23593d26154889a215b992ad015b2679ee..1367cae82c1df9b011b71d422459a3ff062c2883 100644 (file)
@@ -1,4 +1,5 @@
 using System.Collections.Immutable;
+using System.Text.Json.Nodes;
 using System.Threading.Tasks;
 using Content.Server.Database;
 using Content.Server.GameTicking;
@@ -77,7 +78,11 @@ namespace Content.Server.Connection
                 if (banHits is { Count: > 0 })
                     await _db.AddServerBanHitsAsync(id, banHits);
 
-                e.Deny(msg);
+                var properties = new Dictionary<string, object>();
+                if (reason == ConnectionDenyReason.Full)
+                    properties["delay"] = _cfg.GetCVar(CCVars.GameServerFullReconnectDelay);
+
+                e.Deny(new NetDenyReason(msg, properties));
             }
             else
             {
@@ -156,7 +161,8 @@ namespace Content.Server.Connection
             var wasInGame = EntitySystem.TryGet<GameTicker>(out var ticker) &&
                             ticker.PlayerGameStatuses.TryGetValue(userId, out var status) &&
                             status == PlayerGameStatus.JoinedGame;
-            if ((_plyMgr.PlayerCount >= _cfg.GetCVar(CCVars.SoftMaxPlayers) && adminData is null) && !wasInGame)
+            var adminBypass = _cfg.GetCVar(CCVars.AdminBypassMaxPlayers) && adminData != null;
+            if ((_plyMgr.PlayerCount >= _cfg.GetCVar(CCVars.SoftMaxPlayers) && !adminBypass) && !wasInGame)
             {
                 return (ConnectionDenyReason.Full, Loc.GetString("soft-player-cap-full"), null);
             }
index c48a203c5912b289b001f0c46c3b398be89754a3..8a592d4b3073230960be18ca7a5ec60625ee94dd 100644 (file)
@@ -260,6 +260,13 @@ namespace Content.Shared.CCVar
         public static readonly CVarDef<int> SoftMaxPlayers =
             CVarDef.Create("game.soft_max_players", 30, CVar.SERVERONLY | CVar.ARCHIVE);
 
+        /// <summary>
+        /// If a player gets denied connection to the server,
+        /// how long they are forced to wait before attempting to reconnect.
+        /// </summary>
+        public static readonly CVarDef<int> GameServerFullReconnectDelay =
+            CVarDef.Create("game.server_full_reconnect_delay", 30, CVar.SERVERONLY);
+
         /// <summary>
         /// Whether or not panic bunker is currently enabled.
         /// </summary>
@@ -852,6 +859,13 @@ namespace Content.Shared.CCVar
         public static readonly CVarDef<float> AdminAfkTime =
             CVarDef.Create("admin.afk_time", 600f, CVar.SERVERONLY);
 
+        /// <summary>
+        /// If true, admins are able to connect even if
+        /// <see cref="SoftMaxPlayers"/> would otherwise block regular players.
+        /// </summary>
+        public static readonly CVarDef<bool> AdminBypassMaxPlayers =
+            CVarDef.Create("admin.bypass_max_players", true, CVar.SERVERONLY);
+
         /*
          * Explosions
          */