Backported graceful restart feature to server
This commit is contained in:
parent
69c5a288cf
commit
c57f1ac24e
8 changed files with 116 additions and 9 deletions
|
@ -5,6 +5,7 @@ Version 1.4.2+svn:
|
|||
* starting a campaing without any installed now gives an error.
|
||||
* fixed issues with campaign info in non-compressed saved games
|
||||
(bug #11386)
|
||||
* Backported gracefull restart feature to server
|
||||
* fixed an alignement issue which caused a SIGBUS on a Sparc
|
||||
(debian bug #426318)
|
||||
* added some includes to fix compilation problems with Sun Studio 12
|
||||
|
|
|
@ -1082,6 +1082,11 @@ void connect::process_network_data(const config& data, const network::connection
|
|||
{
|
||||
ui::process_network_data(data, sock);
|
||||
|
||||
if(data.child("leave_game")) {
|
||||
set_result(QUIT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data["side_drop"].empty()) {
|
||||
const int side_drop = lexical_cast_default<int>(data["side_drop"], 0) - 1;
|
||||
if(side_drop >= 0 && side_drop < int(sides_.size())) {
|
||||
|
|
|
@ -160,6 +160,7 @@ static void check_timeout()
|
|||
// Reset last_ping if we didn't check for the last 10s.
|
||||
if (last_ping_check + 10 <= now) last_ping = now;
|
||||
if (static_cast<time_t>(last_ping + network::ping_timeout) <= now) {
|
||||
|
||||
int timeout = now - last_ping;
|
||||
ERR_NW << "No server ping since " << timeout
|
||||
<< " seconds. Connection timed out.\n";
|
||||
|
@ -291,10 +292,16 @@ server_manager::server_manager(int port, CREATE_SERVER create_server) : free_(fa
|
|||
}
|
||||
|
||||
server_manager::~server_manager()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void server_manager::stop()
|
||||
{
|
||||
if(free_) {
|
||||
SDLNet_TCP_Close(server_socket);
|
||||
server_socket = 0;
|
||||
free_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -448,8 +455,10 @@ void connect_operation::run()
|
|||
while(!notify_finished());
|
||||
}
|
||||
|
||||
|
||||
} // end namespace
|
||||
|
||||
|
||||
connection connect(const std::string& host, int port)
|
||||
{
|
||||
connect_operation op(host,port);
|
||||
|
|
|
@ -79,6 +79,7 @@ struct server_manager {
|
|||
~server_manager();
|
||||
|
||||
bool is_running() const;
|
||||
void stop();
|
||||
|
||||
private:
|
||||
bool free_;
|
||||
|
|
|
@ -51,10 +51,18 @@ input_stream::input_stream(const std::string& path) : fd_(-1), path_(path)
|
|||
|
||||
input_stream::~input_stream()
|
||||
{
|
||||
#ifndef _WIN32
|
||||
stop();
|
||||
#endif
|
||||
}
|
||||
|
||||
void input_stream::stop()
|
||||
{
|
||||
#ifndef _WIN32
|
||||
if(fd_ != -1) {
|
||||
close(fd_);
|
||||
unlink(path_.c_str());
|
||||
fd_ = -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ public:
|
|||
~input_stream();
|
||||
|
||||
bool read_line(std::string& str);
|
||||
void stop();
|
||||
|
||||
private:
|
||||
input_stream(const input_stream&);
|
||||
|
|
|
@ -224,7 +224,7 @@ private:
|
|||
void send_error(network::connection sock, const char* msg) const;
|
||||
void send_error_dup(network::connection sock, const std::string& msg) const;
|
||||
const network::manager net_manager_;
|
||||
const network::server_manager server_;
|
||||
network::server_manager server_;
|
||||
|
||||
//! std::map<network::connection,player>
|
||||
player_map players_;
|
||||
|
@ -252,6 +252,8 @@ private:
|
|||
size_t default_max_messages_;
|
||||
size_t default_time_period_;
|
||||
size_t concurrent_connections_;
|
||||
bool gracefull_restart;
|
||||
std::string restart_command;
|
||||
//! Parse the server config into local variables.
|
||||
void load_config();
|
||||
|
||||
|
@ -289,6 +291,8 @@ private:
|
|||
void delete_game(std::vector<game*>::iterator game_it);
|
||||
|
||||
void update_game_in_lobby(const game* g, network::connection exclude=0);
|
||||
|
||||
void start_new_server();
|
||||
};
|
||||
|
||||
server::server(int port, input_stream& input, const std::string& config_file, size_t min_threads,size_t max_threads)
|
||||
|
@ -299,6 +303,7 @@ server::server(int port, input_stream& input, const std::string& config_file, si
|
|||
input_(input),
|
||||
config_file_(config_file),
|
||||
cfg_(read_config()),
|
||||
gracefull_restart(false),
|
||||
version_query_response_("[version]\n[/version]\n", simple_wml::INIT_COMPRESSED),
|
||||
login_response_("[mustlogin]\n[/mustlogin]\n", simple_wml::INIT_COMPRESSED),
|
||||
join_lobby_response_("[join_lobby]\n[/join_lobby]\n", simple_wml::INIT_COMPRESSED),
|
||||
|
@ -373,6 +378,10 @@ void server::load_config() {
|
|||
lexical_cast_default<size_t>(cfg_["messages_time_period"],10);
|
||||
concurrent_connections_ =
|
||||
lexical_cast_default<size_t>(cfg_["connections_allowed"],5);
|
||||
// Example config line:
|
||||
// restart_command="./wesnothd-debug -d -c ~/.wesnoth1.5/server.cfg"
|
||||
// remember to make new one as a daemon or it will block old one
|
||||
restart_command = cfg_["restart_command"];
|
||||
|
||||
accepted_versions_.clear();
|
||||
const std::string& versions = cfg_["versions_accepted"];
|
||||
|
@ -443,9 +452,22 @@ void server::dump_stats(const time_t& now) {
|
|||
}
|
||||
|
||||
void server::run() {
|
||||
int gracefull_counter = 0;
|
||||
for (int loop = 0;; ++loop) {
|
||||
SDL_Delay(20);
|
||||
try {
|
||||
// We are going to waith 10 seconds before shutting down so users can get out of game.
|
||||
if (gracefull_restart && games_.size() == 0 && ++gracefull_counter > 500 )
|
||||
{
|
||||
// TODO: We should implement client side autoreconnect.
|
||||
// Idea:
|
||||
// server should send [reconnect]host=host,port=number[/reconnect]
|
||||
// Then client would reconnect to new server automaticaly.
|
||||
// This would also allow server to move to new port or address if there is need
|
||||
|
||||
lobby_.send_server_message("Server is restarting. You can connect to new server now.");
|
||||
throw network::error("shut down");
|
||||
}
|
||||
if (config_reload == 1) {
|
||||
cfg_ = read_config();
|
||||
load_config();
|
||||
|
@ -553,7 +575,7 @@ void server::run() {
|
|||
{
|
||||
network::disconnect(pl->first);
|
||||
}
|
||||
std::cout << "Shutting server down.\n";
|
||||
LOG_SERVER << "Shutting server down.\n";
|
||||
break;
|
||||
}
|
||||
if (!e.socket) {
|
||||
|
@ -836,6 +858,18 @@ void server::process_query(const network::connection sock,
|
|||
lobby_.send_server_message(response.str().c_str(), sock);
|
||||
}
|
||||
|
||||
void server::start_new_server() {
|
||||
if (restart_command.empty())
|
||||
return;
|
||||
|
||||
// Example config line:
|
||||
// restart_command="./wesnothd-debug -d -c ~/.wesnoth1.5/server.cfg"
|
||||
// remember to make new one as a daemon or it will block old one
|
||||
std::system(restart_command.c_str());
|
||||
|
||||
LOG_SERVER << "New server started with command: " << restart_command << "\n";
|
||||
}
|
||||
|
||||
std::string server::process_command(const std::string& query) {
|
||||
std::ostringstream out;
|
||||
const std::string::const_iterator i = std::find(query.begin(),query.end(),' ');
|
||||
|
@ -845,9 +879,41 @@ std::string server::process_command(const std::string& query) {
|
|||
const std::string& help_msg = "Available commands are: ban(s) [<mask>] [<reason>],"
|
||||
"kick <mask>, k(ick)ban [<mask>] [<reason>], help, metrics, netstats,"
|
||||
" (lobby)msg <message>, motd [<message>], status [<mask>],"
|
||||
" unban <ipmask>";
|
||||
" unban <ipmask>, shut_down [now], restart";
|
||||
if (command == "shut_down") {
|
||||
throw network::error("shut down");
|
||||
|
||||
if (parameters == "now")
|
||||
throw network::error("shut down");
|
||||
else
|
||||
{
|
||||
// Gracefull shut down
|
||||
server_.stop();
|
||||
input_.stop();
|
||||
gracefull_restart = true;
|
||||
process_command("msg Server is shuting down. No more new games.");
|
||||
out << "Server is doing gracefull shutdown\n";
|
||||
}
|
||||
|
||||
#ifndef _WIN32 // Not sure if this works on windows
|
||||
// TODO: check if this works in windows.
|
||||
} else if (command == "restart") {
|
||||
if (restart_command.empty())
|
||||
{
|
||||
out << "Server isn't configured for restart\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_SERVER << "Gracefull restart requested\n";
|
||||
gracefull_restart = true;
|
||||
// stop listening socket
|
||||
server_.stop();
|
||||
input_.stop();
|
||||
// start new server
|
||||
start_new_server();
|
||||
process_command("msg Server has been restarted. You can continue playing but new players cannot join this server.");
|
||||
out << "New server strated.\n";
|
||||
}
|
||||
#endif
|
||||
} else if (command == "help") {
|
||||
out << help_msg;
|
||||
} else if (command == "metrics") {
|
||||
|
@ -1224,6 +1290,7 @@ void server::process_data_game(const network::connection sock,
|
|||
lobby_.send_server_message(msg.str().c_str(), sock);
|
||||
return;
|
||||
}
|
||||
|
||||
// If this game is having its level data initialized
|
||||
// for the first time, and is ready for players to join.
|
||||
// We should currently have a summary of the game in g->level().
|
||||
|
@ -1302,6 +1369,11 @@ void server::process_data_game(const network::connection sock,
|
|||
make_add_diff(*games_and_users_list_.child("gamelist"), "gamelist", "game", diff);
|
||||
lobby_.send_data(diff);
|
||||
|
||||
if (gracefull_restart)
|
||||
{
|
||||
delete_game(itor);
|
||||
lobby_.send_server_message("You aren't allowed to make new game because server has been resarted. Please, reconnect to new server.", sock);
|
||||
}
|
||||
//! @todo FIXME: Why not save the level data in the history_?
|
||||
return;
|
||||
// Everything below should only be processed if the game is already intialized.
|
||||
|
@ -1551,7 +1623,7 @@ void server::delete_game(std::vector<game*>::iterator game_it) {
|
|||
gamelist->remove_child("game", index);
|
||||
} else {
|
||||
// Can happen when the game ends before the scenario was transfered.
|
||||
DBG_SERVER << "Could not find game (" << (*game_it)->id()
|
||||
LOG_SERVER << "Could not find game (" << (*game_it)->id()
|
||||
<< ") to delete in games_and_users_list_.\n";
|
||||
}
|
||||
const user_vector& users = (*game_it)->all_game_users();
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
#Example wesnothd.cfg
|
||||
#versions_accepted="1.5.0+svn"
|
||||
#restart_command="/home/user/source/trunk/utils/mp-server/run_server 1.5 &"
|
||||
#
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Syntax: $0 <server version> [<additional parameters for wesnothd>]"
|
||||
exit 1
|
||||
|
@ -29,9 +35,6 @@ trap "$HOME/bin/send_server_message $SERVER; sleep 2; $HOME/bin/send_server_comm
|
|||
|
||||
while [ true ]
|
||||
do
|
||||
if ps -C wesnothd-$SERVER >/dev/null; then
|
||||
sleep 10
|
||||
else
|
||||
DATE=$(date +"%Y%m%d-%H%M%S")
|
||||
PORT=$(cat $SERVERBASE/port)
|
||||
REV=$(ls -l $SERVERBASE/build | sed -e 's,.*wesnothd-\(svn-.*\)_.*/,\1,')
|
||||
|
@ -55,6 +58,13 @@ do
|
|||
fi
|
||||
# wait for the server to terminate
|
||||
wait $PID
|
||||
EXIT_CODE=$?
|
||||
echo "wesnoth exited with code: ${EXIT_CODE}"
|
||||
cd
|
||||
fi
|
||||
|
||||
# check for return code if not zero server should be restarted
|
||||
if [ "$EXIT_CODE" = "0" ]; then
|
||||
echo "script shutting down"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
|
Loading…
Add table
Reference in a new issue