System to automatically restart server after certain uptime. (#32814)
This commit is contained in:
committed by
GitHub
parent
eec533cb77
commit
c221ef06b9
@@ -12,9 +12,13 @@ using Robust.Shared.Timing;
|
|||||||
namespace Content.Server.ServerUpdates;
|
namespace Content.Server.ServerUpdates;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Responsible for restarting the server for update, when not disruptive.
|
/// Responsible for restarting the server periodically or for update, when not disruptive.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class ServerUpdateManager
|
/// <remarks>
|
||||||
|
/// This was originally only designed for restarting on *update*,
|
||||||
|
/// but now also handles periodic restarting to keep server uptime via <see cref="CCVars.ServerUptimeRestartMinutes"/>.
|
||||||
|
/// </remarks>
|
||||||
|
public sealed class ServerUpdateManager : IPostInjectInit
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
[Dependency] private readonly IWatchdogApi _watchdog = default!;
|
[Dependency] private readonly IWatchdogApi _watchdog = default!;
|
||||||
@@ -22,23 +26,43 @@ public sealed class ServerUpdateManager
|
|||||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||||
[Dependency] private readonly IBaseServer _server = default!;
|
[Dependency] private readonly IBaseServer _server = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
|
[Dependency] private readonly ILogManager _logManager = default!;
|
||||||
|
|
||||||
|
private ISawmill _sawmill = default!;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
private bool _updateOnRoundEnd;
|
private bool _updateOnRoundEnd;
|
||||||
|
|
||||||
private TimeSpan? _restartTime;
|
private TimeSpan? _restartTime;
|
||||||
|
|
||||||
|
private TimeSpan _uptimeRestart;
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
_watchdog.UpdateReceived += WatchdogOnUpdateReceived;
|
_watchdog.UpdateReceived += WatchdogOnUpdateReceived;
|
||||||
_playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged;
|
_playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged;
|
||||||
|
|
||||||
|
_cfg.OnValueChanged(
|
||||||
|
CCVars.ServerUptimeRestartMinutes,
|
||||||
|
minutes => _uptimeRestart = TimeSpan.FromMinutes(minutes),
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update()
|
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
|
|||||||
/// <returns>True if the server is going to restart.</returns>
|
/// <returns>True if the server is going to restart.</returns>
|
||||||
public bool RoundEnded()
|
public bool RoundEnded()
|
||||||
{
|
{
|
||||||
if (_updateOnRoundEnd)
|
if (_updateOnRoundEnd || ShouldShutdownDueToUptime())
|
||||||
{
|
{
|
||||||
DoShutdown();
|
DoShutdown();
|
||||||
return true;
|
return true;
|
||||||
@@ -61,11 +85,14 @@ public sealed class ServerUpdateManager
|
|||||||
{
|
{
|
||||||
switch (e.NewStatus)
|
switch (e.NewStatus)
|
||||||
{
|
{
|
||||||
case SessionStatus.Connecting:
|
case SessionStatus.Connected:
|
||||||
|
if (_restartTime != null)
|
||||||
|
_sawmill.Debug("Aborting server restart timer due to player connection");
|
||||||
|
|
||||||
_restartTime = null;
|
_restartTime = null;
|
||||||
break;
|
break;
|
||||||
case SessionStatus.Disconnected:
|
case SessionStatus.Disconnected:
|
||||||
ServerEmptyUpdateRestartCheck();
|
ServerEmptyUpdateRestartCheck("last player disconnect");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,20 +101,20 @@ public sealed class ServerUpdateManager
|
|||||||
{
|
{
|
||||||
_chatManager.DispatchServerAnnouncement(Loc.GetString("server-updates-received"));
|
_chatManager.DispatchServerAnnouncement(Loc.GetString("server-updates-received"));
|
||||||
_updateOnRoundEnd = true;
|
_updateOnRoundEnd = true;
|
||||||
ServerEmptyUpdateRestartCheck();
|
ServerEmptyUpdateRestartCheck("update notification");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks whether there are still players on the server,
|
/// 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.
|
/// and if not starts a timer to automatically reboot the server if an update is available.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void ServerEmptyUpdateRestartCheck()
|
private void ServerEmptyUpdateRestartCheck(string reason)
|
||||||
{
|
{
|
||||||
// Can't simple check the current connected player count since that doesn't update
|
// Can't simple check the current connected player count since that doesn't update
|
||||||
// before PlayerStatusChanged gets fired.
|
// before PlayerStatusChanged gets fired.
|
||||||
// So in the disconnect handler we'd still see a single player otherwise.
|
// So in the disconnect handler we'd still see a single player otherwise.
|
||||||
var playersOnline = _playerManager.Sessions.Any(p => p.Status != SessionStatus.Disconnected);
|
var playersOnline = _playerManager.Sessions.Any(p => p.Status != SessionStatus.Disconnected);
|
||||||
if (playersOnline || !_updateOnRoundEnd)
|
if (playersOnline || !(_updateOnRoundEnd || ShouldShutdownDueToUptime()))
|
||||||
{
|
{
|
||||||
// Still somebody online.
|
// Still somebody online.
|
||||||
return;
|
return;
|
||||||
@@ -95,16 +122,30 @@ public sealed class ServerUpdateManager
|
|||||||
|
|
||||||
if (_restartTime != null)
|
if (_restartTime != null)
|
||||||
{
|
{
|
||||||
// Do nothing because I guess we already have a timer running..?
|
// Do nothing because we already have a timer running.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var restartDelay = TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.UpdateRestartDelay));
|
var restartDelay = TimeSpan.FromSeconds(_cfg.GetCVar(CCVars.UpdateRestartDelay));
|
||||||
_restartTime = restartDelay + _gameTiming.RealTime;
|
_restartTime = restartDelay + _gameTiming.RealTime;
|
||||||
|
|
||||||
|
_sawmill.Debug("Started server-empty restart timer due to {Reason}", reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DoShutdown()
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,21 @@ namespace Content.Shared.CCVar
|
|||||||
public static readonly CVarDef<string> DefaultGuide =
|
public static readonly CVarDef<string> DefaultGuide =
|
||||||
CVarDef.Create("server.default_guide", "NewPlayer", CVar.REPLICATED | CVar.SERVER);
|
CVarDef.Create("server.default_guide", "NewPlayer", CVar.REPLICATED | CVar.SERVER);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If greater than 0, automatically restart the server after this many minutes of uptime.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// This is intended to work around various bugs and performance issues caused by long continuous server uptime.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// 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.
|
||||||
|
/// </para>
|
||||||
|
/// </remarks>
|
||||||
|
public static readonly CVarDef<int> ServerUptimeRestartMinutes =
|
||||||
|
CVarDef.Create("server.uptime_restart_minutes", 0, CVar.SERVERONLY);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ambience
|
* Ambience
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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-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 = 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.
|
||||||
|
|||||||
Reference in New Issue
Block a user