12
12
namespace Content . Server . ServerUpdates ;
13
13
14
14
/// <summary>
15
- /// Responsible for restarting the server for update, when not disruptive.
15
+ /// Responsible for restarting the server periodically or for update, when not disruptive.
16
16
/// </summary>
17
- public sealed class ServerUpdateManager
17
+ /// <remarks>
18
+ /// This was originally only designed for restarting on *update*,
19
+ /// but now also handles periodic restarting to keep server uptime via <see cref="CCVars.ServerUptimeRestartMinutes"/>.
20
+ /// </remarks>
21
+ public sealed class ServerUpdateManager : IPostInjectInit
18
22
{
19
23
[ Dependency ] private readonly IGameTiming _gameTiming = default ! ;
20
24
[ Dependency ] private readonly IWatchdogApi _watchdog = default ! ;
21
25
[ Dependency ] private readonly IPlayerManager _playerManager = default ! ;
22
26
[ Dependency ] private readonly IChatManager _chatManager = default ! ;
23
27
[ Dependency ] private readonly IBaseServer _server = default ! ;
24
28
[ Dependency ] private readonly IConfigurationManager _cfg = default ! ;
29
+ [ Dependency ] private readonly ILogManager _logManager = default ! ;
30
+
31
+ private ISawmill _sawmill = default ! ;
25
32
26
33
[ ViewVariables ]
27
34
private bool _updateOnRoundEnd ;
28
35
29
36
private TimeSpan ? _restartTime ;
30
37
38
+ private TimeSpan _uptimeRestart ;
39
+
31
40
public void Initialize ( )
32
41
{
33
42
_watchdog . UpdateReceived += WatchdogOnUpdateReceived ;
34
43
_playerManager . PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged ;
44
+
45
+ _cfg . OnValueChanged (
46
+ CCVars . ServerUptimeRestartMinutes ,
47
+ minutes => _uptimeRestart = TimeSpan . FromMinutes ( minutes ) ,
48
+ true ) ;
35
49
}
36
50
37
51
public void Update ( )
38
52
{
39
- if ( _restartTime != null && _restartTime < _gameTiming . RealTime )
53
+ if ( _restartTime != null )
40
54
{
41
- DoShutdown ( ) ;
55
+ if ( _restartTime < _gameTiming . RealTime )
56
+ {
57
+ DoShutdown ( ) ;
58
+ }
59
+ }
60
+ else
61
+ {
62
+ if ( ShouldShutdownDueToUptime ( ) )
63
+ {
64
+ ServerEmptyUpdateRestartCheck ( "uptime" ) ;
65
+ }
42
66
}
43
67
}
44
68
@@ -48,7 +72,7 @@ public void Update()
48
72
/// <returns>True if the server is going to restart.</returns>
49
73
public bool RoundEnded ( )
50
74
{
51
- if ( _updateOnRoundEnd )
75
+ if ( _updateOnRoundEnd || ShouldShutdownDueToUptime ( ) )
52
76
{
53
77
DoShutdown ( ) ;
54
78
return true ;
@@ -61,11 +85,14 @@ private void PlayerManagerOnPlayerStatusChanged(object? sender, SessionStatusEve
61
85
{
62
86
switch ( e . NewStatus )
63
87
{
64
- case SessionStatus . Connecting :
88
+ case SessionStatus . Connected :
89
+ if ( _restartTime != null )
90
+ _sawmill . Debug ( "Aborting server restart timer due to player connection" ) ;
91
+
65
92
_restartTime = null ;
66
93
break ;
67
94
case SessionStatus . Disconnected :
68
- ServerEmptyUpdateRestartCheck ( ) ;
95
+ ServerEmptyUpdateRestartCheck ( "last player disconnect" ) ;
69
96
break ;
70
97
}
71
98
}
@@ -74,37 +101,51 @@ private void WatchdogOnUpdateReceived()
74
101
{
75
102
_chatManager . DispatchServerAnnouncement ( Loc . GetString ( "server-updates-received" ) ) ;
76
103
_updateOnRoundEnd = true ;
77
- ServerEmptyUpdateRestartCheck ( ) ;
104
+ ServerEmptyUpdateRestartCheck ( "update notification" ) ;
78
105
}
79
106
80
107
/// <summary>
81
108
/// Checks whether there are still players on the server,
82
109
/// and if not starts a timer to automatically reboot the server if an update is available.
83
110
/// </summary>
84
- private void ServerEmptyUpdateRestartCheck ( )
111
+ private void ServerEmptyUpdateRestartCheck ( string reason )
85
112
{
86
113
// Can't simple check the current connected player count since that doesn't update
87
114
// before PlayerStatusChanged gets fired.
88
115
// So in the disconnect handler we'd still see a single player otherwise.
89
116
var playersOnline = _playerManager . Sessions . Any ( p => p . Status != SessionStatus . Disconnected ) ;
90
- if ( playersOnline || ! _updateOnRoundEnd )
117
+ if ( playersOnline || ! ( _updateOnRoundEnd || ShouldShutdownDueToUptime ( ) ) )
91
118
{
92
119
// Still somebody online.
93
120
return ;
94
121
}
95
122
96
123
if ( _restartTime != null )
97
124
{
98
- // Do nothing because I guess we already have a timer running..?
125
+ // Do nothing because we already have a timer running.
99
126
return ;
100
127
}
101
128
102
129
var restartDelay = TimeSpan . FromSeconds ( _cfg . GetCVar ( CCVars . UpdateRestartDelay ) ) ;
103
130
_restartTime = restartDelay + _gameTiming . RealTime ;
131
+
132
+ _sawmill . Debug ( "Started server-empty restart timer due to {Reason}" , reason ) ;
104
133
}
105
134
106
135
private void DoShutdown ( )
107
136
{
108
- _server . Shutdown ( Loc . GetString ( "server-updates-shutdown" ) ) ;
137
+ _sawmill . Debug ( $ "Shutting down via { nameof ( ServerUpdateManager ) } !") ;
138
+ var reason = _updateOnRoundEnd ? "server-updates-shutdown" : "server-updates-shutdown-uptime" ;
139
+ _server . Shutdown ( Loc . GetString ( reason ) ) ;
140
+ }
141
+
142
+ private bool ShouldShutdownDueToUptime ( )
143
+ {
144
+ return _uptimeRestart != TimeSpan . Zero && _gameTiming . RealTime > _uptimeRestart ;
145
+ }
146
+
147
+ void IPostInjectInit . PostInject ( )
148
+ {
149
+ _sawmill = _logManager . GetSawmill ( "restart" ) ;
109
150
}
110
151
}
0 commit comments