From: Pieter-Jan Briers Date: Sun, 20 Oct 2024 14:46:22 +0000 (+0200) Subject: System to automatically restart server after certain uptime. (#32814) X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=c221ef06b9510465993ff952185c34df800f0e91;p=space-station-14.git System to automatically restart server after certain uptime. (#32814) --- diff --git a/Content.Server/ServerUpdates/ServerUpdateManager.cs b/Content.Server/ServerUpdates/ServerUpdateManager.cs index f4e54984e9..bf18428e25 100644 --- a/Content.Server/ServerUpdates/ServerUpdateManager.cs +++ b/Content.Server/ServerUpdates/ServerUpdateManager.cs @@ -12,9 +12,13 @@ using Robust.Shared.Timing; namespace Content.Server.ServerUpdates; /// -/// Responsible for restarting the server for update, when not disruptive. +/// Responsible for restarting the server periodically or for update, when not disruptive. /// -public sealed class ServerUpdateManager +/// +/// This was originally only designed for restarting on *update*, +/// but now also handles periodic restarting to keep server uptime via . +/// +public sealed class ServerUpdateManager : IPostInjectInit { [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IWatchdogApi _watchdog = default!; @@ -22,23 +26,43 @@ public sealed class ServerUpdateManager [Dependency] private readonly IChatManager _chatManager = default!; [Dependency] private readonly IBaseServer _server = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly ILogManager _logManager = default!; + + private ISawmill _sawmill = default!; [ViewVariables] private bool _updateOnRoundEnd; private TimeSpan? _restartTime; + private TimeSpan _uptimeRestart; + public void Initialize() { _watchdog.UpdateReceived += WatchdogOnUpdateReceived; _playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged; + + _cfg.OnValueChanged( + CCVars.ServerUptimeRestartMinutes, + minutes => _uptimeRestart = TimeSpan.FromMinutes(minutes), + true); } public void Update() { - if (_restartTime != null && _restartTime < _gameTiming.RealTime) + if (_restartTime != null) { - DoShutdown(); + if (_restartTime < _gameTiming.RealTime) + { + DoShutdown(); + } + } + else + { + if (ShouldShutdownDueToUptime()) + { + ServerEmptyUpdateRestartCheck("uptime"); + } } } @@ -48,7 +72,7 @@ public sealed class ServerUpdateManager /// True if the server is going to restart. public bool RoundEnded() { - if (_updateOnRoundEnd) + if (_updateOnRoundEnd || ShouldShutdownDueToUptime()) { DoShutdown(); return true; @@ -61,11 +85,14 @@ public sealed class ServerUpdateManager { switch (e.NewStatus) { - case SessionStatus.Connecting: + case SessionStatus.Connected: + if (_restartTime != null) + _sawmill.Debug("Aborting server restart timer due to player connection"); + _restartTime = null; break; case SessionStatus.Disconnected: - ServerEmptyUpdateRestartCheck(); + ServerEmptyUpdateRestartCheck("last player disconnect"); break; } } @@ -74,20 +101,20 @@ public sealed class ServerUpdateManager { _chatManager.DispatchServerAnnouncement(Loc.GetString("server-updates-received")); _updateOnRoundEnd = true; - ServerEmptyUpdateRestartCheck(); + ServerEmptyUpdateRestartCheck("update notification"); } /// /// Checks whether there are still players on the server, /// and if not starts a timer to automatically reboot the server if an update is available. /// - private void ServerEmptyUpdateRestartCheck() + private void ServerEmptyUpdateRestartCheck(string reason) { // Can't simple check the current connected player count since that doesn't update // before PlayerStatusChanged gets fired. // So in the disconnect handler we'd still see a single player otherwise. var playersOnline = _playerManager.Sessions.Any(p => p.Status != SessionStatus.Disconnected); - if (playersOnline || !_updateOnRoundEnd) + if (playersOnline || !(_updateOnRoundEnd || ShouldShutdownDueToUptime())) { // Still somebody online. return; @@ -95,16 +122,30 @@ public sealed class ServerUpdateManager if (_restartTime != null) { - // Do nothing because I guess we already have a timer running..? + // Do nothing because we already have a timer running. return; } var restartDelay = TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.UpdateRestartDelay)); _restartTime = restartDelay + _gameTiming.RealTime; + + _sawmill.Debug("Started server-empty restart timer due to {Reason}", reason); } private void DoShutdown() { - _server.Shutdown(Loc.GetString("server-updates-shutdown")); + _sawmill.Debug($"Shutting down via {nameof(ServerUpdateManager)}!"); + var reason = _updateOnRoundEnd ? "server-updates-shutdown" : "server-updates-shutdown-uptime"; + _server.Shutdown(Loc.GetString(reason)); + } + + private bool ShouldShutdownDueToUptime() + { + return _uptimeRestart != TimeSpan.Zero && _gameTiming.RealTime > _uptimeRestart; + } + + void IPostInjectInit.PostInject() + { + _sawmill = _logManager.GetSawmill("restart"); } } diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 2d6aa56390..339c0f895f 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -32,6 +32,21 @@ namespace Content.Shared.CCVar public static readonly CVarDef DefaultGuide = CVarDef.Create("server.default_guide", "NewPlayer", CVar.REPLICATED | CVar.SERVER); + /// + /// If greater than 0, automatically restart the server after this many minutes of uptime. + /// + /// + /// + /// This is intended to work around various bugs and performance issues caused by long continuous server uptime. + /// + /// + /// This uses the same non-disruptive logic as update restarts, + /// i.e. the game will only restart at round end or when there is nobody connected. + /// + /// + public static readonly CVarDef ServerUptimeRestartMinutes = + CVarDef.Create("server.uptime_restart_minutes", 0, CVar.SERVERONLY); + /* * Ambience */ diff --git a/Resources/Locale/en-US/server-updates/server-updates.ftl b/Resources/Locale/en-US/server-updates/server-updates.ftl index 72047432bb..ae775c9931 100644 --- a/Resources/Locale/en-US/server-updates/server-updates.ftl +++ b/Resources/Locale/en-US/server-updates/server-updates.ftl @@ -1,2 +1,3 @@ server-updates-received = Update has been received, server will automatically restart for update at the end of this round. server-updates-shutdown = Server is shutting down for update and will automatically restart. +server-updates-shutdown-uptime = Server is shutting down for periodic cleanup and will automatically restart.