Skip to content
Permalink
Browse files

Server: move shutdown parts to a specific shutdown state object (#7437)

* Server: move shutdown parts to a specific shutdown state object
  • Loading branch information...
nerzhul committed Jun 13, 2018
1 parent 10634f0 commit 9a1d3584c22013860ec4b664858076ab6ddf3ea1
Showing with 296 additions and 137 deletions.
  1. +2 −1 src/game.cpp
  2. +2 −0 src/main.cpp
  3. +2 −5 src/network/serverpackethandler.cpp
  4. +146 −124 src/server.cpp
  5. +21 −7 src/server.h
  6. +1 −0 src/unittest/CMakeLists.txt
  7. +122 −0 src/unittest/test_server_shutdown_state.cpp
@@ -1065,7 +1065,7 @@ void Game::run()

while (RenderingEngine::run()
&& !(*kill || g_gamecallback->shutdown_requested
|| (server && server->getShutdownRequested()))) {
|| (server && server->isShutdownRequested()))) {

const irr::core::dimension2d<u32> &current_screen_size =
RenderingEngine::get_video_driver()->getScreenSize();
@@ -1271,6 +1271,7 @@ bool Game::createSingleplayerServer(const std::string &map_dir,
}

server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr, false);
server->init();
server->start();

return true;
@@ -870,6 +870,7 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings &
// Create server
Server server(game_params.world_path, game_params.game_spec,
false, bind_addr, true, &iface);
server.init();

g_term_console.setup(&iface, &kill, admin_nick);

@@ -904,6 +905,7 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings &
// Create server
Server server(game_params.world_path, game_params.game_spec, false,
bind_addr, true);
server.init();
server.start();

// Run server
@@ -402,11 +402,8 @@ 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(myround(m_shutdown_timer)).c_str() << ".";
SendChatMessage(pkt->getPeerId(), ws.str());
if (m_shutdown_state.isTimerRunning()) {
SendChatMessage(pkt->getPeerId(), m_shutdown_state.getShutdownTimerMessage());
}
}

@@ -139,7 +139,60 @@ v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
return v3f(0,0,0);
}

void Server::ShutdownState::reset()
{
m_timer = 0.0f;
message.clear();
should_reconnect = false;
is_requested = false;
}

void Server::ShutdownState::trigger(float delay, const std::string &msg, bool reconnect)
{
m_timer = delay;
message = msg;
should_reconnect = reconnect;
}

void Server::ShutdownState::tick(float dtime, Server *server)
{
if (m_timer <= 0.0f)
return;

// Timed shutdown
static const float shutdown_msg_times[] =
{
1, 2, 3, 4, 5, 10, 20, 40, 60, 120, 180, 300, 600, 1200, 1800, 3600
};

// Automated messages
if (m_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
for (float t : shutdown_msg_times) {
// If shutdown timer matches an automessage, shot it
if (m_timer > t && m_timer - dtime < t) {
std::wstring periodicMsg = getShutdownTimerMessage();

infostream << wide_to_utf8(periodicMsg).c_str() << std::endl;
server->SendChatMessage(PEER_ID_INEXISTENT, periodicMsg);
break;
}
}
}

m_timer -= dtime;
if (m_timer < 0.0f) {
m_timer = 0.0f;
is_requested = true;
}
}

std::wstring Server::ShutdownState::getShutdownTimerMessage() const
{
std::wstringstream ws;
ws << L"*** Server shutting down in "
<< duration_to_string(myround(m_timer)).c_str() << ".";
return ws.str();
}

/*
Server
@@ -174,22 +227,96 @@ Server::Server(
{
m_lag = g_settings->getFloat("dedicated_server_step");

if (path_world.empty())
if (m_path_world.empty())
throw ServerError("Supplied empty world path");

if(!gamespec.isValid())
if (!gamespec.isValid())
throw ServerError("Supplied invalid gamespec");
}

Server::~Server()
{
infostream << "Server destructing" << std::endl;

// Send shutdown message
SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
L"*** Server shutting down"));

if (m_env) {
MutexAutoLock envlock(m_env_mutex);

infostream << "Server: Saving players" << std::endl;
m_env->saveLoadedPlayers();

infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
if(m_simple_singleplayer_mode)
infostream<<" in simple singleplayer mode"<<std::endl;
infostream << "Server: Kicking players" << std::endl;
std::string kick_msg;
bool reconnect = false;
if (isShutdownRequested()) {
reconnect = m_shutdown_state.should_reconnect;
kick_msg = m_shutdown_state.message;
}
if (kick_msg.empty()) {
kick_msg = g_settings->get("kick_msg_shutdown");
}
m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
kick_msg, reconnect);
}

// Do this before stopping the server in case mapgen callbacks need to access
// server-controlled resources (like ModStorages). Also do them before
// shutdown callbacks since they may modify state that is finalized in a
// callback.
if (m_emerge)
m_emerge->stopThreads();

if (m_env) {
MutexAutoLock envlock(m_env_mutex);

// Execute script shutdown hooks
infostream << "Executing shutdown hooks" << std::endl;
m_script->on_shutdown();

infostream << "Server: Saving environment metadata" << std::endl;
m_env->saveMeta();
}

// Stop threads
if (m_thread) {
stop();
delete m_thread;
}

// Delete things in the reverse order of creation
delete m_emerge;
delete m_env;
delete m_rollback;
delete m_banmanager;
delete m_itemdef;
delete m_nodedef;
delete m_craftdef;

// Deinitialize scripting
infostream << "Server: Deinitializing scripting" << std::endl;
delete m_script;

// Delete detached inventories
for (auto &detached_inventory : m_detached_inventories) {
delete detached_inventory.second;
}
}

void Server::init()
{
infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
if (m_simple_singleplayer_mode)
infostream << " in simple singleplayer mode" << std::endl;
else
infostream<<std::endl;
infostream<<"- world: "<<m_path_world<<std::endl;
infostream<<"- game: "<<m_gamespec.path<<std::endl;
infostream << std::endl;
infostream << "- world: " << m_path_world << std::endl;
infostream << "- game: " << m_gamespec.path << std::endl;

// Create world if it doesn't exist
if(!loadGameConfAndInitWorld(m_path_world, m_gamespec))
if (!loadGameConfAndInitWorld(m_path_world, m_gamespec))
throw ServerError("Failed to initialize world");

// Create server thread
@@ -202,8 +329,7 @@ Server::Server(
std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
m_banmanager = new BanManager(ban_path);

m_modmgr = std::unique_ptr<ServerModManager>(new ServerModManager(
m_path_world));
m_modmgr = std::unique_ptr<ServerModManager>(new ServerModManager(m_path_world));
std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
// complain about mods with unsatisfied dependencies
if (!m_modmgr->isConsistent()) {
@@ -214,10 +340,10 @@ Server::Server(
MutexAutoLock envlock(m_env_mutex);

// Create the Map (loads map_meta.txt, overriding configured mapgen params)
ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge);

// Initialize scripting
infostream<<"Server: Initializing Lua"<<std::endl;
infostream << "Server: Initializing Lua" << std::endl;

m_script = new ServerScripting(this);

@@ -279,74 +405,6 @@ Server::Server(
m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit");
}

Server::~Server()
{
infostream << "Server destructing" << std::endl;

// Send shutdown message
SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
L"*** Server shutting down"));

{
MutexAutoLock envlock(m_env_mutex);

infostream << "Server: Saving players" << std::endl;
m_env->saveLoadedPlayers();

infostream << "Server: Kicking players" << std::endl;
std::string kick_msg;
bool reconnect = false;
if (getShutdownRequested()) {
reconnect = m_shutdown_ask_reconnect;
kick_msg = m_shutdown_msg;
}
if (kick_msg.empty()) {
kick_msg = g_settings->get("kick_msg_shutdown");
}
m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
kick_msg, reconnect);
}

// Do this before stopping the server in case mapgen callbacks need to access
// server-controlled resources (like ModStorages). Also do them before
// shutdown callbacks since they may modify state that is finalized in a
// callback.
m_emerge->stopThreads();

{
MutexAutoLock envlock(m_env_mutex);

// Execute script shutdown hooks
infostream << "Executing shutdown hooks" << std::endl;
m_script->on_shutdown();

infostream << "Server: Saving environment metadata" << std::endl;
m_env->saveMeta();
}

// Stop threads
stop();
delete m_thread;

// Delete things in the reverse order of creation
delete m_emerge;
delete m_env;
delete m_rollback;
delete m_banmanager;
delete m_itemdef;
delete m_nodedef;
delete m_craftdef;

// Deinitialize scripting
infostream << "Server: Deinitializing scripting" << std::endl;
delete m_script;

// Delete detached inventories
for (auto &detached_inventory : m_detached_inventories) {
delete detached_inventory.second;
}
}

void Server::start()
{
infostream << "Starting server on " << m_bind_addr.serializeString()
@@ -916,38 +974,7 @@ void Server::AsyncRunStep(bool initial_step)
}
}

// 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(myround(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;
}
}
m_shutdown_state.tick(dtime, this);
}

void Server::Receive()
@@ -3398,16 +3425,13 @@ void Server::requestShutdown(const std::string &msg, bool reconnect, float delay
{
if (delay == 0.0f) {
// No delay, shutdown immediately
m_shutdown_requested = true;
m_shutdown_state.is_requested = true;
// only print to the infostream, a chat message saying
// "Server Shutting Down" is sent when the server destructs.
infostream << "*** Immediate Server shutdown requested." << std::endl;
} 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;
} else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
// Negative delay, cancel shutdown if requested
m_shutdown_state.reset();
std::wstringstream ws;

ws << L"*** Server shutdown canceled.";
@@ -3428,9 +3452,7 @@ void Server::requestShutdown(const std::string &msg, bool reconnect, float delay
SendChatMessage(PEER_ID_INEXISTENT, ws.str());
}

m_shutdown_timer = delay;
m_shutdown_msg = msg;
m_shutdown_ask_reconnect = reconnect;
m_shutdown_state.trigger(delay, msg, reconnect);
}

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)
}
server.step(steplen);

if (server.getShutdownRequested() || kill)
if (server.isShutdownRequested() || kill)
break;

/*

0 comments on commit 9a1d358

Please sign in to comment.
You can’t perform that action at this time.