@@ -139,7 +139,60 @@ v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
139
139
return v3f (0 ,0 ,0 );
140
140
}
141
141
142
+ void Server::ShutdownState::reset ()
143
+ {
144
+ m_timer = 0 .0f ;
145
+ message.clear ();
146
+ should_reconnect = false ;
147
+ is_requested = false ;
148
+ }
149
+
150
+ void Server::ShutdownState::trigger (float delay, const std::string &msg, bool reconnect)
151
+ {
152
+ m_timer = delay;
153
+ message = msg;
154
+ should_reconnect = reconnect;
155
+ }
142
156
157
+ void Server::ShutdownState::tick (float dtime, Server *server)
158
+ {
159
+ if (m_timer <= 0 .0f )
160
+ return ;
161
+
162
+ // Timed shutdown
163
+ static const float shutdown_msg_times[] =
164
+ {
165
+ 1 , 2 , 3 , 4 , 5 , 10 , 20 , 40 , 60 , 120 , 180 , 300 , 600 , 1200 , 1800 , 3600
166
+ };
167
+
168
+ // Automated messages
169
+ if (m_timer < shutdown_msg_times[ARRLEN (shutdown_msg_times) - 1 ]) {
170
+ for (float t : shutdown_msg_times) {
171
+ // If shutdown timer matches an automessage, shot it
172
+ if (m_timer > t && m_timer - dtime < t) {
173
+ std::wstring periodicMsg = getShutdownTimerMessage ();
174
+
175
+ infostream << wide_to_utf8 (periodicMsg).c_str () << std::endl;
176
+ server->SendChatMessage (PEER_ID_INEXISTENT, periodicMsg);
177
+ break ;
178
+ }
179
+ }
180
+ }
181
+
182
+ m_timer -= dtime;
183
+ if (m_timer < 0 .0f ) {
184
+ m_timer = 0 .0f ;
185
+ is_requested = true ;
186
+ }
187
+ }
188
+
189
+ std::wstring Server::ShutdownState::getShutdownTimerMessage () const
190
+ {
191
+ std::wstringstream ws;
192
+ ws << L" *** Server shutting down in "
193
+ << duration_to_string (myround (m_timer)).c_str () << " ." ;
194
+ return ws.str ();
195
+ }
143
196
144
197
/*
145
198
Server
@@ -174,22 +227,96 @@ Server::Server(
174
227
{
175
228
m_lag = g_settings->getFloat (" dedicated_server_step" );
176
229
177
- if (path_world .empty ())
230
+ if (m_path_world .empty ())
178
231
throw ServerError (" Supplied empty world path" );
179
232
180
- if (!gamespec.isValid ())
233
+ if (!gamespec.isValid ())
181
234
throw ServerError (" Supplied invalid gamespec" );
235
+ }
236
+
237
+ Server::~Server ()
238
+ {
239
+ infostream << " Server destructing" << std::endl;
240
+
241
+ // Send shutdown message
242
+ SendChatMessage (PEER_ID_INEXISTENT, ChatMessage (CHATMESSAGE_TYPE_ANNOUNCE,
243
+ L" *** Server shutting down" ));
244
+
245
+ if (m_env) {
246
+ MutexAutoLock envlock (m_env_mutex);
247
+
248
+ infostream << " Server: Saving players" << std::endl;
249
+ m_env->saveLoadedPlayers ();
182
250
183
- infostream<<" Server created for gameid \" " <<m_gamespec.id <<" \" " ;
184
- if (m_simple_singleplayer_mode)
185
- infostream<<" in simple singleplayer mode" <<std::endl;
251
+ infostream << " Server: Kicking players" << std::endl;
252
+ std::string kick_msg;
253
+ bool reconnect = false ;
254
+ if (isShutdownRequested ()) {
255
+ reconnect = m_shutdown_state.should_reconnect ;
256
+ kick_msg = m_shutdown_state.message ;
257
+ }
258
+ if (kick_msg.empty ()) {
259
+ kick_msg = g_settings->get (" kick_msg_shutdown" );
260
+ }
261
+ m_env->kickAllPlayers (SERVER_ACCESSDENIED_SHUTDOWN,
262
+ kick_msg, reconnect);
263
+ }
264
+
265
+ // Do this before stopping the server in case mapgen callbacks need to access
266
+ // server-controlled resources (like ModStorages). Also do them before
267
+ // shutdown callbacks since they may modify state that is finalized in a
268
+ // callback.
269
+ if (m_emerge)
270
+ m_emerge->stopThreads ();
271
+
272
+ if (m_env) {
273
+ MutexAutoLock envlock (m_env_mutex);
274
+
275
+ // Execute script shutdown hooks
276
+ infostream << " Executing shutdown hooks" << std::endl;
277
+ m_script->on_shutdown ();
278
+
279
+ infostream << " Server: Saving environment metadata" << std::endl;
280
+ m_env->saveMeta ();
281
+ }
282
+
283
+ // Stop threads
284
+ if (m_thread) {
285
+ stop ();
286
+ delete m_thread;
287
+ }
288
+
289
+ // Delete things in the reverse order of creation
290
+ delete m_emerge;
291
+ delete m_env;
292
+ delete m_rollback;
293
+ delete m_banmanager;
294
+ delete m_itemdef;
295
+ delete m_nodedef;
296
+ delete m_craftdef;
297
+
298
+ // Deinitialize scripting
299
+ infostream << " Server: Deinitializing scripting" << std::endl;
300
+ delete m_script;
301
+
302
+ // Delete detached inventories
303
+ for (auto &detached_inventory : m_detached_inventories) {
304
+ delete detached_inventory.second ;
305
+ }
306
+ }
307
+
308
+ void Server::init ()
309
+ {
310
+ infostream << " Server created for gameid \" " << m_gamespec.id << " \" " ;
311
+ if (m_simple_singleplayer_mode)
312
+ infostream << " in simple singleplayer mode" << std::endl;
186
313
else
187
- infostream<< std::endl;
188
- infostream<< " - world: " << m_path_world<< std::endl;
189
- infostream<< " - game: " << m_gamespec.path << std::endl;
314
+ infostream << std::endl;
315
+ infostream << " - world: " << m_path_world << std::endl;
316
+ infostream << " - game: " << m_gamespec.path << std::endl;
190
317
191
318
// Create world if it doesn't exist
192
- if (!loadGameConfAndInitWorld (m_path_world, m_gamespec))
319
+ if (!loadGameConfAndInitWorld (m_path_world, m_gamespec))
193
320
throw ServerError (" Failed to initialize world" );
194
321
195
322
// Create server thread
@@ -202,8 +329,7 @@ Server::Server(
202
329
std::string ban_path = m_path_world + DIR_DELIM " ipban.txt" ;
203
330
m_banmanager = new BanManager (ban_path);
204
331
205
- m_modmgr = std::unique_ptr<ServerModManager>(new ServerModManager (
206
- m_path_world));
332
+ m_modmgr = std::unique_ptr<ServerModManager>(new ServerModManager (m_path_world));
207
333
std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods ();
208
334
// complain about mods with unsatisfied dependencies
209
335
if (!m_modmgr->isConsistent ()) {
@@ -214,10 +340,10 @@ Server::Server(
214
340
MutexAutoLock envlock (m_env_mutex);
215
341
216
342
// Create the Map (loads map_meta.txt, overriding configured mapgen params)
217
- ServerMap *servermap = new ServerMap (path_world , this , m_emerge);
343
+ ServerMap *servermap = new ServerMap (m_path_world , this , m_emerge);
218
344
219
345
// Initialize scripting
220
- infostream<< " Server: Initializing Lua" << std::endl;
346
+ infostream << " Server: Initializing Lua" << std::endl;
221
347
222
348
m_script = new ServerScripting (this );
223
349
@@ -279,74 +405,6 @@ Server::Server(
279
405
m_csm_noderange_limit = g_settings->getU32 (" csm_flavour_noderange_limit" );
280
406
}
281
407
282
- Server::~Server ()
283
- {
284
- infostream << " Server destructing" << std::endl;
285
-
286
- // Send shutdown message
287
- SendChatMessage (PEER_ID_INEXISTENT, ChatMessage (CHATMESSAGE_TYPE_ANNOUNCE,
288
- L" *** Server shutting down" ));
289
-
290
- {
291
- MutexAutoLock envlock (m_env_mutex);
292
-
293
- infostream << " Server: Saving players" << std::endl;
294
- m_env->saveLoadedPlayers ();
295
-
296
- infostream << " Server: Kicking players" << std::endl;
297
- std::string kick_msg;
298
- bool reconnect = false ;
299
- if (getShutdownRequested ()) {
300
- reconnect = m_shutdown_ask_reconnect;
301
- kick_msg = m_shutdown_msg;
302
- }
303
- if (kick_msg.empty ()) {
304
- kick_msg = g_settings->get (" kick_msg_shutdown" );
305
- }
306
- m_env->kickAllPlayers (SERVER_ACCESSDENIED_SHUTDOWN,
307
- kick_msg, reconnect);
308
- }
309
-
310
- // Do this before stopping the server in case mapgen callbacks need to access
311
- // server-controlled resources (like ModStorages). Also do them before
312
- // shutdown callbacks since they may modify state that is finalized in a
313
- // callback.
314
- m_emerge->stopThreads ();
315
-
316
- {
317
- MutexAutoLock envlock (m_env_mutex);
318
-
319
- // Execute script shutdown hooks
320
- infostream << " Executing shutdown hooks" << std::endl;
321
- m_script->on_shutdown ();
322
-
323
- infostream << " Server: Saving environment metadata" << std::endl;
324
- m_env->saveMeta ();
325
- }
326
-
327
- // Stop threads
328
- stop ();
329
- delete m_thread;
330
-
331
- // Delete things in the reverse order of creation
332
- delete m_emerge;
333
- delete m_env;
334
- delete m_rollback;
335
- delete m_banmanager;
336
- delete m_itemdef;
337
- delete m_nodedef;
338
- delete m_craftdef;
339
-
340
- // Deinitialize scripting
341
- infostream << " Server: Deinitializing scripting" << std::endl;
342
- delete m_script;
343
-
344
- // Delete detached inventories
345
- for (auto &detached_inventory : m_detached_inventories) {
346
- delete detached_inventory.second ;
347
- }
348
- }
349
-
350
408
void Server::start ()
351
409
{
352
410
infostream << " Starting server on " << m_bind_addr.serializeString ()
@@ -916,38 +974,7 @@ void Server::AsyncRunStep(bool initial_step)
916
974
}
917
975
}
918
976
919
- // Timed shutdown
920
- static const float shutdown_msg_times[] =
921
- {
922
- 1 , 2 , 3 , 4 , 5 , 10 , 15 , 20 , 25 , 30 , 45 , 60 , 120 , 180 , 300 , 600 , 1200 , 1800 , 3600
923
- };
924
-
925
- if (m_shutdown_timer > 0 .0f ) {
926
- // Automated messages
927
- if (m_shutdown_timer < shutdown_msg_times[ARRLEN (shutdown_msg_times) - 1 ]) {
928
- for (u16 i = 0 ; i < ARRLEN (shutdown_msg_times) - 1 ; i++) {
929
- // If shutdown timer matches an automessage, shot it
930
- if (m_shutdown_timer > shutdown_msg_times[i] &&
931
- m_shutdown_timer - dtime < shutdown_msg_times[i]) {
932
- std::wstringstream ws;
933
-
934
- ws << L" *** Server shutting down in "
935
- << duration_to_string (myround (m_shutdown_timer - dtime)).c_str ()
936
- << " ." ;
937
-
938
- infostream << wide_to_utf8 (ws.str ()).c_str () << std::endl;
939
- SendChatMessage (PEER_ID_INEXISTENT, ws.str ());
940
- break ;
941
- }
942
- }
943
- }
944
-
945
- m_shutdown_timer -= dtime;
946
- if (m_shutdown_timer < 0 .0f ) {
947
- m_shutdown_timer = 0 .0f ;
948
- m_shutdown_requested = true ;
949
- }
950
- }
977
+ m_shutdown_state.tick (dtime, this );
951
978
}
952
979
953
980
void Server::Receive ()
@@ -3398,16 +3425,13 @@ void Server::requestShutdown(const std::string &msg, bool reconnect, float delay
3398
3425
{
3399
3426
if (delay == 0 .0f ) {
3400
3427
// No delay, shutdown immediately
3401
- m_shutdown_requested = true ;
3428
+ m_shutdown_state. is_requested = true ;
3402
3429
// only print to the infostream, a chat message saying
3403
3430
// "Server Shutting Down" is sent when the server destructs.
3404
3431
infostream << " *** Immediate Server shutdown requested." << std::endl;
3405
- } else if (delay < 0 .0f && m_shutdown_timer > 0 .0f ) {
3406
- // Negative delay, cancel shutdown if requested
3407
- m_shutdown_timer = 0 .0f ;
3408
- m_shutdown_msg = " " ;
3409
- m_shutdown_ask_reconnect = false ;
3410
- m_shutdown_requested = false ;
3432
+ } else if (delay < 0 .0f && m_shutdown_state.isTimerRunning ()) {
3433
+ // Negative delay, cancel shutdown if requested
3434
+ m_shutdown_state.reset ();
3411
3435
std::wstringstream ws;
3412
3436
3413
3437
ws << L" *** Server shutdown canceled." ;
@@ -3428,9 +3452,7 @@ void Server::requestShutdown(const std::string &msg, bool reconnect, float delay
3428
3452
SendChatMessage (PEER_ID_INEXISTENT, ws.str ());
3429
3453
}
3430
3454
3431
- m_shutdown_timer = delay;
3432
- m_shutdown_msg = msg;
3433
- m_shutdown_ask_reconnect = reconnect;
3455
+ m_shutdown_state.trigger (delay, msg, reconnect);
3434
3456
}
3435
3457
3436
3458
PlayerSAO* Server::emergePlayer (const char *name, session_t peer_id, u16 proto_version)
@@ -3518,7 +3540,7 @@ void dedicated_server_loop(Server &server, bool &kill)
3518
3540
}
3519
3541
server.step (steplen);
3520
3542
3521
- if (server.getShutdownRequested () || kill )
3543
+ if (server.isShutdownRequested () || kill )
3522
3544
break ;
3523
3545
3524
3546
/*
0 commit comments