Implement delayed server shutdown with cancelation (#4664)
authorLoïc Blot <nerzhul@users.noreply.github.com>
Sat, 15 Apr 2017 21:19:18 +0000 (23:19 +0200)
committerGitHub <noreply@github.com>
Sat, 15 Apr 2017 21:19:18 +0000 (23:19 +0200)
builtin/game/chatcommands.lua
builtin/game/misc.lua
doc/lua_api.txt
src/network/serverpackethandler.cpp
src/script/lua_api/l_server.cpp
src/server.cpp
src/server.h
src/util/string.h

index 8df3903d21734b7d8f9207ed63978a4a57316cd5..25cc06178e13d04b992d77cc00c2313449179014 100644 (file)
@@ -763,14 +763,20 @@ core.register_chatcommand("days", {
 
 core.register_chatcommand("shutdown", {
        description = "Shutdown server",
-       params = "[reconnect] [message]",
+       params = "[delay_in_seconds(0..inf) or -1 for cancel] [reconnect] [message]",
        privs = {server=true},
        func = function(name, param)
-               core.log("action", name .. " shuts down server")
-               core.chat_send_all("*** Server shutting down (operator request).")
-               local reconnect, message = param:match("([^ ]+)(.*)")
+               local delay, reconnect, message = param:match("([^ ][-]?[0-9]+)([^ ]+)(.*)")
                message = message or ""
-               core.request_shutdown(message:trim(), core.is_yes(reconnect))
+
+               if delay ~= "" then
+                       delay = tonumber(param) or 0
+               else
+                       delay = 0
+                       core.log("action", name .. " shuts down server")
+                       core.chat_send_all("*** Server shutting down (operator request).")
+               end
+               core.request_shutdown(message:trim(), core.is_yes(reconnect), delay)
        end,
 })
 
index 618d4d97ff4c1cc6b8b230fd37745576ad43a1a9..a3eb26ac25ddacfcae46d67f0f112371fd7bfd23 100644 (file)
@@ -173,3 +173,8 @@ end
 function core.close_formspec(player_name, formname)
        return minetest.show_formspec(player_name, formname, "")
 end
+
+function core.cancel_shutdown_requests()
+       core.request_shutdown("", false, -1)
+end
+
index 721f5448a3e5e26a10d756a2e02fb2096235d4ff..7b967726d426809d5a4eb84bd4e67f6d4451985f 100644 (file)
@@ -2576,8 +2576,12 @@ These functions return the leftover itemstack.
     * Optional: Variable number of arguments that are passed to `func`
 
 ### Server
-* `minetest.request_shutdown([message],[reconnect])`: request for server shutdown. Will display `message` to clients,
-    and `reconnect` == true displays a reconnect button.
+* `minetest.request_shutdown([message],[reconnect],[delay])`: request for server shutdown. Will display `message` to clients,
+    `reconnect` == true displays a reconnect button,
+    `delay` adds an optional delay (in seconds) before shutdown
+        negative delay cancels the current active shutdown
+        zero delay triggers an immediate shutdown.
+* `minetest.cancel_shutdown_requests()`: cancel current delayed shutdown
 * `minetest.get_server_status()`: returns server status string
 * `minetest.get_server_uptime()`: returns the server uptime in seconds
 
index 27c33a4f60b679f65cf20e0486914ebd0738b33c..2e4c5b6be8cf54c94df7384fd5fdacf0f2a64957 100644 (file)
@@ -722,6 +722,13 @@ void Server::handleCommand_ClientReady(NetworkPacket* pkt)
 
        m_clients.event(peer_id, CSE_SetClientReady);
        m_script->on_joinplayer(playersao);
+       // Send shutdown timer if shutdown has been scheduled
+       if (m_shutdown_timer > 0.0f) {
+               std::wstringstream ws;
+               ws << L"*** Server shutting down in "
+                               << duration_to_string(round(m_shutdown_timer)).c_str() << ".";
+               SendChatMessage(pkt->getPeerId(), ws.str());
+       }
 }
 
 void Server::handleCommand_GotBlocks(NetworkPacket* pkt)
index b6d44e0ff5251a98e64f999f59d8fe35c4366001..343d11b7e33bc2b39cd190e3b8b1f32ea3676d01 100644 (file)
@@ -33,7 +33,8 @@ int ModApiServer::l_request_shutdown(lua_State *L)
        NO_MAP_LOCK_REQUIRED;
        const char *msg = lua_tolstring(L, 1, NULL);
        bool reconnect = lua_toboolean(L, 2);
-       getServer(L)->requestShutdown(msg ? msg : "", reconnect);
+       float seconds_before_shutdown = lua_tonumber(L, 3);
+       getServer(L)->requestShutdown(msg ? msg : "", reconnect, seconds_before_shutdown);
        return 0;
 }
 
index 7ed8a8bf4877303f8453e17cbb4e153e78bd58b5..5328b6897a703e33ff0cecff06afc574d16ce581 100644 (file)
@@ -177,6 +177,7 @@ Server::Server(
        m_clients(&m_con),
        m_shutdown_requested(false),
        m_shutdown_ask_reconnect(false),
+       m_shutdown_timer(0.0f),
        m_admin_chat(iface),
        m_ignore_map_edit_events(false),
        m_ignore_map_edit_events_peer_id(0),
@@ -1029,6 +1030,39 @@ void Server::AsyncRunStep(bool initial_step)
                        m_env->saveMeta();
                }
        }
+
+       // Timed shutdown
+       static const float shutdown_msg_times[] =
+       {
+               1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600
+       };
+
+       if (m_shutdown_timer > 0.0f) {
+               // Automated messages
+               if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
+                       for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) {
+                               // If shutdown timer matches an automessage, shot it
+                               if (m_shutdown_timer > shutdown_msg_times[i] &&
+                                       m_shutdown_timer - dtime < shutdown_msg_times[i]) {
+                                       std::wstringstream ws;
+
+                                       ws << L"*** Server shutting down in "
+                                               << duration_to_string(round(m_shutdown_timer - dtime)).c_str()
+                                               << ".";
+
+                                       infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
+                                       SendChatMessage(PEER_ID_INEXISTENT, ws.str());
+                                       break;
+                               }
+                       }
+               }
+
+               m_shutdown_timer -= dtime;
+               if (m_shutdown_timer < 0.0f) {
+                       m_shutdown_timer = 0.0f;
+                       m_shutdown_requested = true;
+               }
+       }
 }
 
 void Server::Receive()
@@ -3443,6 +3477,39 @@ v3f Server::findSpawnPos()
        return nodeposf;
 }
 
+void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
+{
+       if (delay == 0.0f) {
+       // No delay, shutdown immediately
+               m_shutdown_requested = true;
+       } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
+       // Negative delay, cancel shutdown if requested
+               m_shutdown_timer = 0.0f;
+               m_shutdown_msg = "";
+               m_shutdown_ask_reconnect = false;
+               m_shutdown_requested = false;
+               std::wstringstream ws;
+
+               ws << L"*** Server shutdown canceled.";
+
+               infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
+               SendChatMessage(PEER_ID_INEXISTENT, ws.str());
+       } else if (delay > 0.0f) {
+       // Positive delay, delay the shutdown
+               m_shutdown_timer = delay;
+               m_shutdown_msg = msg;
+               m_shutdown_ask_reconnect = reconnect;
+               std::wstringstream ws;
+
+               ws << L"*** Server shutting down in "
+                               << duration_to_string(round(m_shutdown_timer)).c_str()
+                               << ".";
+
+               infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
+               SendChatMessage(PEER_ID_INEXISTENT, ws.str());
+       }
+}
+
 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
 {
        bool newplayer = false;
index 1df9d0a938149d02c7116f97d78b74affbb91a92..e2445f8338ff266db37ab1a69c8bacc5cc62b088 100644 (file)
@@ -226,12 +226,7 @@ public:
        inline bool getShutdownRequested() const { return m_shutdown_requested; }
 
        // request server to shutdown
-       void requestShutdown(const std::string &msg, bool reconnect)
-       {
-               m_shutdown_requested = true;
-               m_shutdown_msg = msg;
-               m_shutdown_ask_reconnect = reconnect;
-       }
+       void requestShutdown(const std::string &msg, bool reconnect, float delay = 0.0f);
 
        // Returns -1 if failed, sound handle on success
        // Envlock
@@ -602,6 +597,7 @@ private:
        bool m_shutdown_requested;
        std::string m_shutdown_msg;
        bool m_shutdown_ask_reconnect;
+       float m_shutdown_timer;
 
        ChatInterface *m_admin_chat;
        std::string m_admin_nick;
index 572c371502c5965167c764d3110cd72488034b2d..c155d2f4adf336b78916460ac1e7b9aa41d27a32 100644 (file)
@@ -614,4 +614,28 @@ inline const char *bool_to_cstr(bool val)
        return val ? "true" : "false";
 }
 
+inline const std::string duration_to_string(int sec)
+{
+       int min = floor(sec / 60);
+       sec %= 60;
+       int hour = floor(min / 60);
+       min %= 60;
+
+       std::stringstream ss;
+       if (hour > 0) {
+               ss << hour << "h ";
+       }
+
+       if (min > 0) {
+               ss << min << "m ";
+       }
+
+       if (sec > 0) {
+               ss << sec << "s ";
+       }
+
+       return ss.str();
+}
+
+
 #endif