Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Implement delayed server shutdown with cancelation (#4664)
  • Loading branch information
nerzhul committed Apr 15, 2017
1 parent 0f955bf commit 34d32ce
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 14 deletions.
16 changes: 11 additions & 5 deletions builtin/game/chatcommands.lua
Expand Up @@ -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,
})

Expand Down
5 changes: 5 additions & 0 deletions builtin/game/misc.lua
Expand Up @@ -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

8 changes: 6 additions & 2 deletions doc/lua_api.txt
Expand Up @@ -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

Expand Down
7 changes: 7 additions & 0 deletions src/network/serverpackethandler.cpp
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion src/script/lua_api/l_server.cpp
Expand Up @@ -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;
}

Expand Down
67 changes: 67 additions & 0 deletions src/server.cpp
Expand Up @@ -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),
Expand Down Expand Up @@ -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()

This comment has been minimized.

Copy link
@SmallJoker

SmallJoker Apr 17, 2017

Member

myround here too.

<< ".";

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()
Expand Down Expand Up @@ -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()

This comment has been minimized.

Copy link
@SmallJoker

SmallJoker Apr 17, 2017

Member

myround since MSVC doens't contain them in cmath.

<< ".";

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;
Expand Down
8 changes: 2 additions & 6 deletions src/server.h
Expand Up @@ -226,12 +226,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
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
Expand Down Expand Up @@ -602,6 +597,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
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;
Expand Down
24 changes: 24 additions & 0 deletions src/util/string.h
Expand Up @@ -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);

This comment has been minimized.

Copy link
@SmallJoker

SmallJoker Apr 17, 2017

Member

Superflous floor call. Also, there's no overload for int arguments in MSVC.
Sorry, I should've tested this pull before it was merged.
EDIT: Fixed by 6120251

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

0 comments on commit 34d32ce

Please sign in to comment.