47 #include <boost/algorithm/string.hpp> 48 #include <boost/scope_exit.hpp> 68 #define ERR_SERVER LOG_STREAM(err, log_server) 71 #define WRN_SERVER LOG_STREAM(warn, log_server) 74 #define LOG_SERVER LOG_STREAM(info, log_server) 75 #define DBG_SERVER LOG_STREAM(debug, log_server) 78 #define ERR_CONFIG LOG_STREAM(err, log_config) 79 #define WRN_CONFIG LOG_STREAM(warn, log_config) 90 if(!out.
child(
"gamelist_diff")) {
103 assert(!children.empty());
106 index = children.size() - 1;
109 assert(
index < static_cast<int>(children.size()));
116 const char* gamelist,
121 if(!out.
child(
"gamelist_diff")) {
133 const auto itor = std::find(children.begin(), children.end(),
remove);
135 if(itor == children.end()) {
139 const int index = std::distance(children.begin(), itor);
149 const char* gamelist,
154 if(!out.
child(
"gamelist_diff")) {
166 const auto itor = std::find(children.begin(), children.end(),
item);
168 if(itor == children.end()) {
175 const int index = std::distance(children.begin(), itor);
191 std::ostringstream out;
196 const std::string
denied_msg =
"You're not allowed to execute this command.";
198 "Available commands are: adminmsg <msg>," 199 " ban <mask> <time> <reason>, bans [deleted] [<ipmask>], clones," 200 " dul|deny_unregistered_login [yes|no], kick <mask> [<reason>]," 201 " k[ick]ban <mask> <time> <reason>, help, games, metrics," 202 " [lobby]msg <message>, motd [<message>]," 203 " pm|privatemsg <nickname> <message>, requests, roll <sides>, sample, searchlog <mask>," 204 " signout, stats, status [<mask>], stopgame <nick> [<reason>], unban <ipmask>\n" 205 "Specific strings (those not in between <> like the command names)" 206 " are case insensitive.";
210 const std::string& config_file,
217 , user_handler_(nullptr)
218 , die_(static_cast<unsigned>(
std::time(nullptr)))
223 , config_file_(config_file)
225 , accepted_versions_()
226 , redirected_versions_()
228 , disallowed_names_()
234 , default_max_messages_(0)
235 , default_time_period_(0)
236 , concurrent_connections_(0)
237 , graceful_restart(false)
238 , lan_server_(
std::time(nullptr))
239 , last_user_seen_time_(
std::time(nullptr))
241 , max_ip_log_size_(0)
242 , deny_unregistered_login_(false)
243 , save_replays_(false)
244 , replay_save_path_()
245 , allow_remote_shutdown_(false)
248 , failed_login_limit_()
249 , failed_login_ban_()
250 , failed_login_buffer_size_()
255 , dump_stats_timer_(io_service_)
256 , tournaments_timer_(io_service_)
258 , timer_(io_service_)
259 , lan_server_timer_(io_service_)
276 WRN_SERVER <<
"SIGHUP caught, reloading config\n";
289 if(
games().empty()) {
290 process_command(
"msg All games ended. Shutting down now. Reconnect to the new server instance.",
"system");
293 timer_.expires_from_now(std::chrono::seconds(1));
321 if(res != 0 && errno != EEXIST) {
325 int fifo = open(
input_path_.c_str(), O_RDWR | O_NONBLOCK);
327 LOG_SERVER <<
"opened fifo at '" <<
input_path_ <<
"'. Server commands may be written to this file.\n";
337 std::cout << error.message() << std::endl;
343 std::getline(is, cmd);
345 LOG_SERVER <<
"Admin Command: type: " << cmd <<
"\n";
350 if(!cmd.empty() && cmd.at(0) ==
'+') {
353 <<
"[/admin_command_response]\n";
365 #define SETUP_HANDLER(name, function) \ 366 cmd_handlers_[name] = std::bind(function, this, \ 367 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); 411 return configuration;
416 read(configuration, *stream);
422 return configuration;
429 #warning No FIFODIR set 430 #define FIFODIR "/var/run/wesnothd" 432 const std::string fifo_path
433 = (
cfg_[
"fifo_path"].
empty() ? std::string(
FIFODIR) +
"/socket" : std::string(
cfg_[
"fifo_path"]));
459 for(
const std::string& source :
utils::split(
cfg_[
"client_sources"].str())) {
464 if(
cfg_[
"disallow_names"].empty()) {
495 const std::string& versions =
cfg_[
"versions_accepted"];
496 if(versions.empty() ==
false) {
512 for(
const std::string& version :
utils::split(proxy[
"version"])) {
540 std::size_t connections = 0;
570 ERR_SERVER <<
"Error waiting for dump stats timer: " << ec.message() <<
"\n";
588 ERR_SERVER <<
"Error waiting for tournament refresh timer: " << ec.message() <<
"\n";
599 boost::asio::spawn(
io_service_, [socket,
this](boost::asio::yield_context yield) {
login_client(yield, socket); });
604 boost::system::error_code ec;
612 std::string client_version, client_source;
615 client_version = std::string { version_str_span.
begin(), version_str_span.
end() };
618 client_source = std::string { source_str_span.
begin(), source_str_span.
end() };
626 <<
":\ttelling them to log in.\n";
636 <<
":\tredirecting them to " << redirect_version.second[
"host"] <<
":" 637 << redirect_version.second[
"port"] <<
"\n";
640 for(
const auto& attr : redirect_version.second.attribute_range()) {
641 redirect.
set_attr_dup(attr.first.c_str(), attr.second.str().c_str());
650 <<
":\trejecting them\n";
665 std::string username;
666 bool registered, is_moderator;
673 username = (*login)[
"username"].to_string();
684 join_lobby_response.
root().
add_child(
"join_lobby").
set_attr(
"is_moderator", is_moderator ?
"yes" :
"no");
685 join_lobby_response.
root().
child(
"join_lobby")->
set_attr_dup(
"profile_url_prefix",
"https://r.wesnoth.org/u");
702 [
this, socket, new_player](boost::asio::yield_context yield) {
handle_player(yield, socket, new_player); }
706 << (registered ?
" to a registered account" :
"") <<
"\n";
708 std::shared_ptr<game> last_sent;
710 auto g_ptr = record.get_game();
711 if(g_ptr != last_sent) {
713 g_ptr->send_server_message_to_all(username +
" has logged into the lobby");
736 "The nickname '" + username +
"' contains invalid " 737 "characters. Only alpha-numeric characters, underscores and hyphens are allowed.",
744 if(username.size() > 20) {
745 async_send_error(socket,
"The nickname '" + username +
"' is too long. Nicks must be 20 characters or less.",
754 async_send_error(socket,
"The nickname '" + username +
"' is reserved and cannot be used by players",
767 if(!
authenticate(socket, username, (*login)[
"password"].to_string(), name_taken, registered))
773 "The nickname '" + username +
"' is not registered. This server disallows unregistered nicknames.",
788 std::string ban_type_desc;
789 std::string ban_reason;
790 const char* msg_numeric;
791 std::string ban_duration = std::to_string(auth_ban.
duration);
793 switch(auth_ban.
type) {
795 ban_type_desc =
"account";
797 ban_reason =
"a ban has been issued on your user account.";
800 ban_type_desc =
"IP address";
802 ban_reason =
"a ban has been issued on your IP address.";
805 ban_type_desc =
"email address";
807 ban_reason =
"a ban has been issued on your email address.";
810 ban_type_desc =
"<unknown ban type>";
812 ban_reason = ban_type_desc;
815 ban_reason +=
" (" + ban_duration +
")";
822 async_send_error(socket,
"You are banned from this server: " + ban_reason, msg_numeric, {{
"duration", ban_duration}});
825 async_send_error(socket,
"You are banned from this server: " + ban_reason, msg_numeric);
830 <<
"), " <<
"ignoring due to moderator flag\n";
837 process_command(
"kick " +
p->info().name() +
" autokick by registered user", username);
845 send_server_message(socket,
"You are currently banned by the forum administration.",
"alert");
852 socket_ptr socket,
const std::string& username,
const std::string&
password,
bool name_taken,
bool& registered)
872 "The nickname '" + username +
"' is inactive. You cannot claim ownership of this " 873 "nickname until you activate your account via email or ask an administrator to do it for you.",
877 if(password.empty()) {
883 "The nickname '" + username +
"' is registered on this server." 884 "\n\nWARNING: There is already a client using this username, " 885 "logging in will cause that client to be kicked!",
895 if(
seeds_[socket.get()].empty()) {
902 const std::time_t now = std::time(
nullptr);
905 seeds_.erase(socket.get());
931 "Maximum login attempts exceeded",
"automatic",
"", username);
936 "The password you provided for the nickname '" + username +
"' was incorrect.", username,
942 <<
"Login attempt with incorrect password for nickname '" << username <<
"'.\n";
950 seeds_.erase(socket.get());
959 const std::string&
msg,
960 const std::string& user,
961 const char* error_code,
962 bool force_confirmation)
968 std::string nonce{(salt[1] ==
'2')
972 std::string password_challenge = salt + nonce;
975 "Even though your nickname is registered on this server you " 976 "cannot log in due to an error in the hashing algorithm. " 977 "Logging into your forum account on https://forums.wesnoth.org " 978 "may fix this problem.");
982 seeds_[socket.get()] = nonce;
987 e.
set_attr(
"password_request",
"yes");
988 e.
set_attr(
"phpbb_encryption",
"yes");
990 e.
set_attr(
"force_confirmation", force_confirmation ?
"yes" :
"no");
992 if(*error_code !=
'\0') {
993 e.
set_attr(
"error_code", error_code);
1006 std::tie(player, inserted) =
player_connections_.insert(player_connections::value_type(socket, player_data));
1009 BOOST_SCOPE_EXIT_ALL(
this, &player) {
1015 if(!
motd_.empty()) {
1021 send_server_message(player,
"You are using version " + player_data.
version() +
" which has known security issues that can be used to compromise your computer. We strongly recommend updating to a Wesnoth version " + secure_version.
str() +
" or newer!",
"alert");
1033 boost::system::error_code ec;
1038 if(doc->child(
"refresh_lobby")) {
1082 if((whisper[
"receiver"].empty()) || (whisper[
"message"].empty())) {
1085 "message=\"Invalid number of arguments\"\n" 1086 "sender=\"server\"\n" 1095 whisper.
set_attr_dup(
"sender", player->name().c_str());
1099 send_server_message(player,
"Can't find '" + whisper[
"receiver"].to_string() +
"'.",
"error");
1103 auto g = player->get_game();
1105 send_server_message(player,
"You cannot send private messages to players in a running game you observe.",
"error");
1124 const std::string command(query[
"type"].to_string());
1125 std::ostringstream response;
1127 const std::string& query_help_msg =
1128 "Available commands are: adminmsg <msg>, help, games, metrics," 1129 " motd, requests, roll <sides>, sample, stats, status, version, wml.";
1132 if(command ==
"status") {
1135 command.compare(0, 8,
"adminmsg") == 0 ||
1136 command.compare(0, 6,
"report") == 0 ||
1137 command ==
"games" ||
1138 command ==
"metrics" ||
1139 command ==
"motd" ||
1140 command.compare(0, 7,
"version") == 0 ||
1141 command ==
"requests" ||
1142 command.compare(0, 4,
"roll") == 0 ||
1143 command ==
"sample" ||
1144 command ==
"stats" ||
1145 command ==
"status " + player.
name() ||
1150 if(command ==
"signout") {
1151 LOG_SERVER <<
"Admin signed out: IP: " << iter->client_ip() <<
"\tnick: " << player.
name()
1155 response <<
"You are no longer recognized as an administrator.";
1160 LOG_SERVER <<
"Admin Command: type: " << command <<
"\tIP: " << iter->client_ip()
1161 <<
"\tnick: " << player.
name() << std::endl;
1165 }
else if(command ==
"help" || command.empty()) {
1166 response << query_help_msg;
1167 }
else if(command ==
"admin" || command.compare(0, 6,
"admin ") == 0) {
1174 if(command.size() >= 6) {
1175 passwd = command.substr(6);
1179 LOG_SERVER <<
"New Admin recognized: IP: " << iter->client_ip() <<
"\tnick: " << player.
name()
1183 response <<
"You are now recognized as an administrator.";
1189 WRN_SERVER <<
"FAILED Admin attempt with password: '" << passwd <<
"'\tIP: " << iter->client_ip()
1190 <<
"\tnick: " << player.
name() << std::endl;
1191 response <<
"Error: wrong password";
1194 response <<
"Error: unrecognized query: '" << command <<
"'\n" << query_help_msg;
1204 send_server_message(player,
"This server does not allow username registration.",
"error");
1209 if(nickserv.
child(
"info")) {
1211 std::string res =
user_handler_->user_info((*nickserv.
child(
"info"))[
"name"].to_string());
1215 "There was an error looking up the details of the user '" 1216 + (*nickserv.
child(
"info"))[
"name"].to_string() +
"'. " 1217 +
" The error message was: " + e.
message,
"error" 1227 if(user->info().is_message_flooding()) {
1229 "Warning: you are sending too many messages too fast. Your message has not been relayed.",
"error");
1243 LOG_SERVER << user->client_ip() <<
"\t<" << user->name()
1246 LOG_SERVER << user->client_ip() <<
"\t<" << user->name() <<
"> " << msg <<
"\n";
1259 "This server is shutting down. You aren't allowed to make new games. Please " 1260 "reconnect to the new server.",
"error");
1266 const std::string game_name = create_game[
"name"].to_string();
1267 const std::string game_password = create_game[
"password"].to_string();
1268 const std::string initial_bans = create_game[
"ignored"].to_string();
1270 DBG_SERVER << player->client_ip() <<
"\t" << player->info().name()
1271 <<
"\tcreates a new game: \"" << game_name <<
"\".\n";
1284 DBG_SERVER <<
"initial bans: " << initial_bans <<
"\n";
1285 if(initial_bans !=
"") {
1289 if(game_password.empty() ==
false) {
1305 assert(gamelist !=
nullptr);
1315 const auto g = std::find(games.begin(), games.end(), game_ptr->
description());
1317 if(
g != games.end()) {
1318 const std::size_t
index = std::distance(games.begin(),
g);
1322 LOG_SERVER <<
"Could not find game (" << game_ptr->
id() <<
", " << game_ptr->
db_id() <<
") to delete in games_and_users_list_.\n";
1331 const std::string&
password = join[
"password"].to_string();
1332 int game_id = join[
"id"].to_int();
1336 std::shared_ptr<game>
g;
1338 g = g_iter->get_game();
1343 WRN_SERVER << player->client_ip() <<
"\t" << player->info().name()
1344 <<
"\tattempted to join unknown game:\t" << game_id <<
".\n";
1349 }
else if(!g->level_init()) {
1350 WRN_SERVER << player->client_ip() <<
"\t" << player->info().name()
1351 <<
"\tattempted to join uninitialized game:\t\"" << g->name() <<
"\" (" << game_id <<
").\n";
1356 }
else if(player->info().is_moderator()) {
1358 }
else if(g->player_is_banned(player, player->info().name())) {
1360 <<
"\tReject banned player: " << player->info().name()
1361 <<
"\tfrom game:\t\"" << g->name() <<
"\" (" << game_id <<
").\n";
1366 }
else if(!g->password_matches(password)) {
1367 WRN_SERVER << player->client_ip() <<
"\t" << player->info().name()
1368 <<
"\tattempted to join game:\t\"" << g->name() <<
"\" (" << game_id <<
") with bad password\n";
1375 bool joined = g->add_player(player, observer);
1377 WRN_SERVER << player->client_ip() <<
"\t" << player->info().name()
1378 <<
"\tattempted to observe game:\t\"" << g->name() <<
"\" (" << game_id
1379 <<
") which doesn't allow observers.\n";
1383 "Attempt to observe a game that doesn't allow observers. (You probably joined the " 1384 "game shortly after it filled up.)",
"error");
1393 g->describe_slots();
1399 player->info().config_address(), diff);
1401 if(diff1 || diff2) {
1412 game&
g = *(p->get_game());
1413 std::weak_ptr<game> g_ptr{p->get_game()};
1416 if(data.
child(
"snapshot") || data.
child(
"scenario")) {
1429 << g.
id() <<
", " << g.
db_id() <<
").\n";
1433 assert(gamelist !=
nullptr);
1442 << g.
name() <<
"\" (" << g.
id() <<
", " << g.
db_id() <<
") without a 'multiplayer' child.\n";
1448 "The scenario data is missing the [multiplayer] tag which contains the " 1449 "game settings. Game aborted.",
"error");
1457 << g.
name() <<
"\" (" << g.
id() <<
", " << g.
db_id() <<
") although it's already initialized.\n";
1471 if(!data[
"mp_shroud"].to_bool()) {
1476 if(!
e->attr(
"require_era").to_bool(
true)) {
1477 desc.
set_attr(
"require_era",
"no");
1481 if(s[
"require_scenario"].to_bool(
false)) {
1482 desc.
set_attr(
"require_scenario",
"yes");
1487 desc.add_child_at(
"modification", 0);
1488 desc.child(
"modification")->set_attr_dup(
"id", m->attr(
"id"));
1489 desc.child(
"modification")->set_attr_dup(
"name", m->attr(
"name"));
1490 desc.child(
"modification")->set_attr_dup(
"addon_id", m->attr(
"addon_id"));
1492 if(m->attr(
"require_modification").to_bool(
false)) {
1493 desc.child(
"modification")->set_attr(
"require_modification",
"yes");
1518 <<
" (socket:" << p->socket() <<
") while the scenario wasn't yet initialized.\n" 1529 <<
"\tsent [store_next_scenario] in game:\t\"" << g.
name() <<
"\" (" << g.
id()
1530 <<
", " << g.
db_id() <<
") while the scenario is not yet initialized.";
1548 ERR_SERVER << p->client_ip() <<
"\tERROR: \"" << g.
name() <<
"\" (" << g.
id()
1549 <<
", " << g.
db_id() <<
") is initialized but has no description_.\n";
1560 << g.
name() <<
"\" (" << g.
id() <<
", " << g.
db_id() <<
") without a 'multiplayer' child.\n";
1565 "The scenario data is missing the [multiplayer] tag which contains the game " 1566 "settings. Game aborted.",
"error");
1573 desc.
set_attr_dup(
"map_data", s[
"mp_shroud"].to_bool() ?
"" : s[
"map_data"]);
1576 if(!
e->attr(
"require_era").to_bool(
true)) {
1577 desc.
set_attr(
"require_era",
"no");
1581 if(s[
"require_scenario"].to_bool(
false)) {
1582 desc.
set_attr(
"require_scenario",
"yes");
1594 }
else if(data.
child(
"load_next_scenario")) {
1597 }
else if(data.
child(
"start_game")) {
1615 for(
const auto& addon : m.
children(
"addon")) {
1616 for(
const auto& content : addon->children(
"content")) {
1617 user_handler_->db_insert_game_content_info(
uuid_, g.
db_id(), content->attr(
"type").to_string(), content->attr(
"name").to_string(), content->attr(
"id").to_string(), addon->attr(
"id").to_string(), addon->attr(
"version").to_string());
1624 for(
unsigned side_index = 0; side_index < sides.size(); ++side_index) {
1627 std::string version;
1642 user_handler_->db_insert_game_player_info(
uuid_, g.
db_id(), side[
"player_id"].to_string(), side[
"side"].to_int(), side[
"is_host"].to_bool(), side[
"faction"].to_string(), version, source, side[
"current_player"].to_string());
1649 }
else if(data.
child(
"leave_game")) {
1659 if(
auto gStrong = g_ptr.lock()) {
1660 gStrong->describe_slots();
1668 if(diff1 || diff2) {
1699 }
else if(data.
child(
"change_faction")) {
1711 }
else if(data.
child(
"muteall")) {
1728 }
else if(data.
child(
"kick") || data.
child(
"ban")) {
1729 bool ban = (data.
child(
"ban") !=
nullptr);
1761 if((*
info)[
"type"] ==
"termination") {
1763 if((*
info)[
"condition"].to_string() ==
"out of sync") {
1772 }
else if(data.
child(
"turn")) {
1781 }
else if(data.
child(
"whiteboard")) {
1784 }
else if(data.
child(
"change_turns_wml")) {
1791 }
else if(data.
child(
"message")) {
1794 }
else if(data.
child(
"stop_updates")) {
1799 int offset = request->attr(
"offset").to_int();
1805 if(!request->has_attr(
"search_for")) {
1808 std::string player_name = request->attr(
"search_for").to_string();
1813 player_id = player_ptr->info().config_address()->attr(
"forum_id").to_int();
1817 if(player_id != 0) {
1818 LOG_SERVER <<
"Querying game history requested by player `" <<
player.
name() <<
"` for player id `" << player_id <<
"`." << std::endl;
1825 data.
child(
"error") ||
1826 data.
child(
"side_secured") ||
1833 WRN_SERVER << p->client_ip() <<
"\tReceived unknown data from: " <<
player.
name() <<
" (socket:" << p->socket()
1834 <<
") in game: \"" << g.
name() <<
"\" (" << g.
id() <<
", " << g.
db_id() <<
")\n" 1851 player->socket()->shutdown(boost::asio::ip::tcp::socket::shutdown_receive);
1856 std::string ip = iter->client_ip();
1858 const std::shared_ptr<game>
g = iter->get_game();
1859 bool game_ended =
false;
1861 game_ended = g->remove_player(iter,
true,
false);
1865 const std::size_t
index =
1866 std::distance(users.begin(), std::find(users.begin(), users.end(), iter->info().config_address()));
1876 LOG_SERVER << ip <<
"\t" << iter->info().name() <<
"\twas logged off" 1884 i->log_off = std::time(
nullptr);
1944 if(issuer_name ==
"*socket*" && !query.empty() && query.at(0) ==
'+') {
1948 auto issuer_end = std::find(query.begin(), query.end(),
':');
1950 std::string issuer(query.begin() + 1, issuer_end);
1951 if(!issuer.empty()) {
1952 issuer_name =
"+" + issuer +
"+";
1953 query = std::string(issuer_end + 1, query.end());
1958 const auto i = std::find(query.begin(), query.end(),
' ');
1961 const std::string command =
utf8::lowercase(std::string(query.begin(),
i));
1963 std::string parameters = (
i == query.end() ?
"" : std::string(
i + 1, query.end()));
1966 std::ostringstream out;
1970 out <<
"Command '" << command <<
"' is not recognized.\n" <<
help_msg;
1972 const cmd_handler& handler = handler_itor->second;
1974 handler(issuer_name, query, parameters, &out);
1975 }
catch(
const std::bad_function_call& ex) {
1976 ERR_SERVER <<
"While handling a command '" << command
1977 <<
"', caught a std::bad_function_call exception.\n";
1979 out <<
"An internal server error occurred (std::bad_function_call) while executing '" << command
1987 std::string
msg =
"While handling a command, caught an invalid utf8 exception: ";
1990 return (msg +
'\n');
1996 const std::string& issuer_name,
const std::string& , std::string& parameters, std::ostringstream* out)
1998 assert(out !=
nullptr);
2005 if(parameters ==
"now") {
2013 timer_.expires_from_now(std::chrono::seconds(10));
2017 "msg The server is shutting down. You may finish your games but can't start new ones. Once all " 2018 "games have ended the server will exit.",
2022 *out <<
"Server is doing graceful shut down.";
2027 const std::string& ,
2029 std::ostringstream* out)
2031 assert(out !=
nullptr);
2039 *out <<
"No restart_command configured! Not restarting.";
2044 timer_.expires_from_now(std::chrono::seconds(10));
2050 "msg The server has been restarted. You may finish current games but can't start new ones and " 2051 "new players can't join this (old) server instance. (So if a player of your game disconnects " 2052 "you have to save, reconnect and reload the game on the new server instance. It is actually " 2053 "recommended to do that right away.)",
2057 *out <<
"New server started.";
2062 const std::string& issuer_name,
const std::string& , std::string& parameters, std::ostringstream* out)
2064 assert(out !=
nullptr);
2066 if(parameters.empty()) {
2069 }
else if(issuer_name !=
"*socket*") {
2074 request_sample_frequency = atoi(parameters.c_str());
2075 if(request_sample_frequency <= 0) {
2076 *out <<
"Sampling turned off.";
2078 *out <<
"Sampling every " << request_sample_frequency <<
" requests.";
2083 const std::string& ,
2085 std::ostringstream* out)
2087 assert(out !=
nullptr);
2092 const std::string& ,
2094 std::ostringstream* out)
2096 assert(out !=
nullptr);
2103 const std::string& ,
2105 std::ostringstream* out)
2107 assert(out !=
nullptr);
2112 const std::string& ,
2114 std::ostringstream* out)
2116 assert(out !=
nullptr);
2121 const std::string& ,
2122 std::string& parameters,
2123 std::ostringstream* out)
2125 assert(out !=
nullptr);
2126 if(parameters.empty()) {
2132 N = std::stoi(parameters);
2133 }
catch(
const std::invalid_argument&) {
2134 *out <<
"The number of die sides must be a number!";
2136 }
catch(
const std::out_of_range&) {
2137 *out <<
"The number of sides is too big for the die!";
2142 *out <<
"The die cannot have less than 1 side!";
2145 std::uniform_int_distribution<int> dice_distro(1, N);
2146 std::string value = std::to_string(dice_distro(
die_));
2148 *out <<
"You rolled a die [1 - " + parameters +
"] and got a " + value +
".";
2155 auto g_ptr = player_ptr->get_game();
2157 g_ptr->send_server_message_to_all(issuer_name +
" rolled a die [1 - " + parameters +
"] and got a " + value +
".",
player_connections_.project<0>(player_ptr));
2159 *out <<
" (The result is shown to others only in a game.)";
2164 const std::string& ,
2166 std::ostringstream* out)
2168 assert(out !=
nullptr);
2173 const std::string& ,
2175 std::ostringstream* out)
2177 assert(out !=
nullptr);
2182 const std::string& issuer_name,
const std::string& , std::string& parameters, std::ostringstream* out)
2184 assert(out !=
nullptr);
2186 if(parameters.empty()) {
2187 *out <<
"You must type a message.";
2191 const std::string& sender = issuer_name;
2192 const std::string& message = parameters;
2194 << (message.find(
"/me ") == 0 ? std::string(message.begin() + 3, message.end()) +
">" :
"> " + message)
2199 msg.
set_attr_dup(
"sender", (
"admin message from " + sender).c_str());
2210 bool is_admin =
false;
2212 for(
const auto&
player : player_connections_) {
2220 *out <<
"Your report has been logged and sent to the server administrators. Thanks!";
2224 *out <<
"Your report has been logged and sent to " << n <<
" online administrators. Thanks!";
2228 const std::string& issuer_name,
const std::string& , std::string& parameters, std::ostringstream* out)
2230 assert(out !=
nullptr);
2232 auto first_space = std::find(parameters.begin(), parameters.end(),
' ');
2233 if(first_space == parameters.end()) {
2234 *out <<
"You must name a receiver.";
2238 const std::string& sender = issuer_name;
2239 const std::string receiver(parameters.begin(), first_space);
2241 std::string message(first_space + 1, parameters.end());
2244 if(message.empty()) {
2245 *out <<
"You must type a message.";
2253 msg.
set_attr_dup(
"sender", (
"server message from " + sender).c_str());
2257 if(receiver !=
player.info().
name().c_str()) {
2262 *out <<
"Message to " << receiver <<
" successfully sent.";
2266 *out <<
"No such nick: " << receiver;
2270 const std::string& ,
2271 std::string& parameters,
2272 std::ostringstream* out)
2274 assert(out !=
nullptr);
2276 if(parameters.empty()) {
2277 *out <<
"You must type a message.";
2284 << (parameters.find(
"/me ") == 0
2285 ? std::string(parameters.begin() + 3, parameters.end()) +
">" 2286 :
"> " + parameters)
2289 *out <<
"message '" << parameters <<
"' relayed to players";
2293 const std::string& ,
2294 std::string& parameters,
2295 std::ostringstream* out)
2297 assert(out !=
nullptr);
2299 if(parameters.empty()) {
2300 *out <<
"You must type a message.";
2306 << (parameters.find(
"/me ") == 0
2307 ? std::string(parameters.begin() + 3, parameters.end()) +
">" 2308 :
"> " + parameters)
2311 *out <<
"message '" << parameters <<
"' relayed to players";
2315 const std::string& ,
const std::string& , std::string& parameters, std::ostringstream* out)
2317 assert(out !=
nullptr);
2319 if(parameters.empty()) {
2326 *out <<
"Player " << parameters <<
" is using wesnoth " <<
player.info().
version();
2331 *out <<
"Player '" << parameters <<
"' not found.";
2335 const std::string& issuer_name,
const std::string& , std::string& parameters, std::ostringstream* out)
2337 assert(out !=
nullptr);
2339 *out <<
"STATUS REPORT for '" << parameters <<
"'";
2340 bool found_something =
false;
2346 parameters =
player.client_ip();
2347 found_something =
true;
2352 if(!found_something) {
2360 const bool match_ip = (std::count(parameters.begin(), parameters.end(),
'.') >= 1);
2362 if(parameters.empty() || parameters ==
"*" ||
2366 found_something =
true;
2371 if(!found_something) {
2372 *out <<
"\nNo match found. You may want to check with 'searchlog'.";
2377 const std::string& ,
2379 std::ostringstream* out)
2381 assert(out !=
nullptr);
2382 *out <<
"CLONES STATUS REPORT";
2384 std::set<std::string> clones;
2387 if(clones.find(it->client_ip()) != clones.end()) {
2393 if(it->client_ip() == clone->client_ip()) {
2396 clones.insert(it->client_ip());
2405 if(clones.empty()) {
2406 *out << std::endl <<
"No clones found.";
2411 const std::string& ,
2412 std::string& parameters,
2413 std::ostringstream* out)
2415 assert(out !=
nullptr);
2418 if(parameters.empty()) {
2423 std::string mask = parameters.substr(7);
2431 ERR_SERVER <<
"While handling bans, caught an invalid utf8 exception: " << e.what() << std::endl;
2436 const std::string& issuer_name,
const std::string& , std::string& parameters, std::ostringstream* out)
2438 assert(out !=
nullptr);
2441 auto first_space = std::find(parameters.begin(), parameters.end(),
' ');
2443 if(first_space == parameters.end()) {
2448 auto second_space = std::find(first_space + 1, parameters.end(),
' ');
2449 const std::string target(parameters.begin(), first_space);
2450 const std::string duration(first_space + 1, second_space);
2451 std::time_t parsed_time = std::time(
nullptr);
2458 if(second_space == parameters.end()) {
2462 std::string reason(second_space + 1, parameters.end());
2465 if(reason.empty()) {
2466 *out <<
"You need to give a reason for the ban.";
2470 std::string dummy_group;
2474 if(std::count(target.begin(), target.end(),
'.') >= 1) {
2477 *out <<
ban_manager_.
ban(target, parsed_time, reason, issuer_name, dummy_group);
2487 const std::string ip =
player.client_ip();
2488 *out <<
ban_manager_.
ban(ip, parsed_time, reason, issuer_name, dummy_group, target);
2508 *out <<
"Nickname mask '" << target <<
"' did not match, no bans set.";
2515 const std::string& issuer_name,
const std::string& , std::string& parameters, std::ostringstream* out)
2517 assert(out !=
nullptr);
2520 auto first_space = std::find(parameters.begin(), parameters.end(),
' ');
2521 if(first_space == parameters.end()) {
2526 auto second_space = std::find(first_space + 1, parameters.end(),
' ');
2527 const std::string target(parameters.begin(), first_space);
2528 const std::string duration(first_space + 1, second_space);
2529 std::time_t parsed_time = std::time(
nullptr);
2536 if(second_space == parameters.end()) {
2540 std::string reason(second_space + 1, parameters.end());
2543 if(reason.empty()) {
2544 *out <<
"You need to give a reason for the ban.";
2548 std::string dummy_group;
2549 std::vector<player_iterator> users_to_kick;
2553 if(std::count(target.begin(), target.end(),
'.') >= 1) {
2556 *out <<
ban_manager_.
ban(target, parsed_time, reason, issuer_name, dummy_group);
2560 users_to_kick.push_back(
player);
2572 const std::string ip =
player->client_ip();
2573 *out <<
ban_manager_.
ban(ip, parsed_time, reason, issuer_name, dummy_group, target);
2574 users_to_kick.push_back(
player);
2593 *out <<
"Nickname mask '" << target <<
"' did not match, no bans set.";
2598 for(
auto user : users_to_kick) {
2599 *out <<
"\nKicked " << user->info().name() <<
" (" << user->client_ip() <<
").";
2600 async_send_error(user->socket(),
"You have been banned. Reason: " + reason);
2606 const std::string& issuer_name,
const std::string& , std::string& parameters, std::ostringstream* out)
2608 assert(out !=
nullptr);
2611 auto first_space = std::find(parameters.begin(), parameters.end(),
' ');
2612 if(first_space == parameters.end()) {
2617 auto second_space = std::find(first_space + 1, parameters.end(),
' ');
2618 const std::string target(parameters.begin(), first_space);
2620 std::string group = std::string(first_space + 1, second_space);
2621 first_space = second_space;
2622 second_space = std::find(first_space + 1, parameters.end(),
' ');
2624 const std::string duration(first_space + 1, second_space);
2625 std::time_t parsed_time = std::time(
nullptr);
2632 if(second_space == parameters.end()) {
2636 std::string reason(second_space + 1, parameters.end());
2639 if(reason.empty()) {
2640 *out <<
"You need to give a reason for the ban.";
2646 if(std::count(target.begin(), target.end(),
'.') >= 1) {
2649 *out <<
ban_manager_.
ban(target, parsed_time, reason, issuer_name, group);
2659 const std::string ip =
player.client_ip();
2660 *out <<
ban_manager_.
ban(ip, parsed_time, reason, issuer_name, group, target);
2679 *out <<
"Nickname mask '" << target <<
"' did not match, no bans set.";
2686 const std::string& ,
2687 std::string& parameters,
2688 std::ostringstream* out)
2690 assert(out !=
nullptr);
2692 if(parameters.empty()) {
2693 *out <<
"You must enter an ipmask to unban.";
2701 const std::string& ,
2702 std::string& parameters,
2703 std::ostringstream* out)
2705 assert(out !=
nullptr);
2707 if(parameters.empty()) {
2708 *out <<
"You must enter an ipmask to ungban.";
2716 const std::string& ,
2717 std::string& parameters,
2718 std::ostringstream* out)
2720 assert(out !=
nullptr);
2722 if(parameters.empty()) {
2723 *out <<
"You must enter a mask to kick.";
2727 auto i = std::find(parameters.begin(), parameters.end(),
' ');
2728 const std::string kick_mask = std::string(parameters.begin(),
i);
2729 const std::string kick_message = (
i == parameters.end()
2730 ?
"You have been kicked." 2731 :
"You have been kicked. Reason: " + std::string(
i + 1, parameters.end()));
2733 bool kicked =
false;
2736 const bool match_ip = (std::count(kick_mask.begin(), kick_mask.end(),
'.') >= 1);
2738 std::vector<player_iterator> users_to_kick;
2743 users_to_kick.push_back(
player);
2747 for(
const auto&
player : users_to_kick) {
2754 *out <<
"Kicked " <<
player->
name() <<
" (" <<
player->client_ip() <<
"). '" 2755 << kick_message <<
"'";
2762 *out <<
"No user matched '" << kick_mask <<
"'.";
2767 const std::string& ,
2768 std::string& parameters,
2769 std::ostringstream* out)
2771 assert(out !=
nullptr);
2773 if(parameters.empty()) {
2774 if(!
motd_.empty()) {
2775 *out <<
"Message of the day:\n" <<
motd_;
2778 *out <<
"No message of the day set.";
2784 *out <<
"Message of the day set to: " <<
motd_;
2788 const std::string& ,
2789 std::string& parameters,
2790 std::ostringstream* out)
2792 assert(out !=
nullptr);
2794 if(parameters.empty()) {
2795 *out <<
"You must enter a mask to search for.";
2799 *out <<
"IP/NICK LOG for '" << parameters <<
"'";
2801 bool found_something =
false;
2805 const bool match_ip = (std::count(parameters.begin(), parameters.end(),
'.') >= 1);
2808 const std::string& username =
i.nick;
2809 const std::string& ip =
i.ip;
2814 found_something =
true;
2820 *out <<
"\n'" << username <<
"' @ " << ip
2826 if(!found_something) {
2827 *out <<
"\nNo match found.";
2832 const std::string& ,
2833 std::string& parameters,
2834 std::ostringstream* out)
2836 assert(out !=
nullptr);
2839 if(parameters.empty()) {
2847 ERR_SERVER <<
"While handling dul (deny unregistered logins), caught an invalid utf8 exception: " << e.what()
2853 const std::string& ,
2854 std::string& parameters,
2855 std::ostringstream* out)
2857 const std::string nick = parameters.substr(0, parameters.find(
' '));
2858 const std::string reason = parameters.length() > nick.length()+1 ? parameters.substr(nick.length()+1) :
"";
2862 std::shared_ptr<game>
g =
player->get_game();
2864 *out <<
"Player '" << nick <<
"' is in game with id '" << g->id() <<
", " << g->db_id() <<
"' named '" << g->
name() <<
"'. Ending game for reason: '" << reason <<
"'...";
2867 *out <<
"Player '" << nick <<
"' is not currently in a game.";
2870 *out <<
"Player '" << nick <<
"' is not currently logged in.";
2881 std::vector<decltype(range_pair)::first_type> range_vctor;
2883 for(
auto it = range_pair.first; it != range_pair.second; ++it) {
2884 range_vctor.push_back(it);
2885 it->info().mark_available();
2891 ERR_SERVER <<
"ERROR: delete_game(): Could not find user in players_. (socket: " << it->socket() <<
")\n";
2898 for(
const auto& it : range_vctor) {
2905 for(
const auto& it : range_vctor) {
2908 leave_game_doc_reason.
child(
"leave_game")->
set_attr_dup(
"reason", reason.c_str());
2930 bool keep_alive =
false;
2931 std::size_t min_threads = 5;
2932 std::size_t max_threads = 0;
2934 srand(static_cast<unsigned>(std::time(
nullptr)));
2936 std::string config_file;
2945 for(
int arg = 1; arg != argc; ++arg) {
2946 const std::string val(argv[arg]);
2951 if((val ==
"--config" || val ==
"-c") && arg + 1 != argc) {
2952 config_file = argv[++arg];
2953 }
else if(val ==
"--verbose" || val ==
"-v") {
2955 }
else if(val ==
"--dump-wml" || val ==
"-w") {
2957 }
else if(val.substr(0, 6) ==
"--log-") {
2958 std::size_t
p = val.find(
'=');
2959 if(p == std::string::npos) {
2960 std::cerr <<
"unknown option: " << val <<
'\n';
2964 std::string
s = val.substr(6, p - 6);
2969 }
else if(s ==
"warning") {
2971 }
else if(s ==
"info") {
2973 }
else if(s ==
"debug") {
2976 std::cerr <<
"unknown debug level: " << s <<
'\n';
2980 while(p != std::string::npos) {
2981 std::size_t q = val.find(
',', p + 1);
2982 s = val.substr(p + 1, q == std::string::npos ? q : q - (p + 1));
2985 std::cerr <<
"unknown debug domain: " << s <<
'\n';
2991 }
else if((val ==
"--port" || val ==
"-p") && arg + 1 != argc) {
2992 port = atoi(argv[++arg]);
2993 }
else if(val ==
"--keepalive") {
2995 }
else if(val ==
"--help" || val ==
"-h") {
2996 std::cout <<
"usage: " << argv[0]
2997 <<
" [-dvV] [-c path] [-m n] [-p port] [-t n]\n" 2998 <<
" -c, --config <path> Tells wesnothd where to find the config file to use.\n" 2999 <<
" -d, --daemon Runs wesnothd as a daemon.\n" 3000 <<
" -h, --help Shows this usage message.\n" 3001 <<
" --log-<level>=<domain1>,<domain2>,...\n" 3002 <<
" sets the severity level of the debug domains.\n" 3003 <<
" 'all' can be used to match any debug domain.\n" 3004 <<
" Available levels: error, warning, info, debug.\n" 3005 <<
" -p, --port <port> Binds the server to the specified port.\n" 3006 <<
" --keepalive Enable TCP keepalive.\n" 3007 <<
" -t, --threads <n> Uses n worker threads for network I/O (default: 5).\n" 3008 <<
" -v --verbose Turns on more verbose logging.\n" 3009 <<
" -V, --version Returns the server version.\n" 3010 <<
" -w, --dump-wml Print all WML sent to clients to stdout.\n";
3012 }
else if(val ==
"--version" || val ==
"-V") {
3015 }
else if(val ==
"--daemon" || val ==
"-d") {
3017 ERR_SERVER <<
"Running as a daemon is not supported on this platform" << std::endl;
3020 const pid_t pid = fork();
3022 ERR_SERVER <<
"Could not fork and run as a daemon" << std::endl;
3024 }
else if(pid > 0) {
3025 std::cout <<
"Started wesnothd as a daemon with process id " << pid <<
"\n";
3031 }
else if((val ==
"--threads" || val ==
"-t") && arg + 1 != argc) {
3032 min_threads = atoi(argv[++arg]);
3033 if(min_threads > 30) {
3036 }
else if((val ==
"--max-threads" || val ==
"-T") && arg + 1 != argc) {
3037 max_threads = atoi(argv[++arg]);
3038 }
else if(val ==
"--request_sample_frequency" && arg + 1 != argc) {
3041 ERR_SERVER <<
"unknown option: " << val << std::endl;
3048 }
catch(
const std::exception&
e) {
3049 ERR_SERVER <<
"terminated by C++ exception: " << e.what() << std::endl;
boost::asio::ip::tcp::acceptor acceptor_v4_
node & add_child(const char *name)
void handle_player(boost::asio::yield_context yield, socket_ptr socket, const player &player)
std::set< std::string > client_sources_
const string_span & attr(const char *key) const
void handle_whisper(player_iterator player, simple_wml::node &whisper)
std::string get_timestamp(const std::time_t &t, const std::string &format)
void handle_choice(const simple_wml::node &data, player_iterator user)
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
void send_and_record_server_message(const char *message, std::optional< player_iterator > exclude={})
Send data to all players in this game except 'exclude'.
std::time_t duration
Ban duration (0 if permanent)
std::unique_ptr< simple_wml::document > coro_receive_doc(socket_ptr socket, boost::asio::yield_context yield)
Receive WML document from a coroutine.
bool check_error(const boost::system::error_code &error, socket_ptr socket)
const simple_wml::node * config_address() const
bool isvalid_username(const std::string &username)
Check if the username contains only valid characters.
void shut_down_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
simple_wml::document games_and_users_list_
std::deque< login_log >::size_type failed_login_buffer_size_
void apply_diff(const node &diff)
const std::string & name() const
void send_to_lobby(simple_wml::document &data, std::optional< player_iterator > exclude={})
Interfaces for manipulating version numbers of engine, add-ons, etc.
void load_config(const config &)
#define MP_NAME_AUTH_BAN_USER_ERROR
void async_send_error(socket_ptr socket, const std::string &msg, const char *error_code="", const info_table &info={})
const std::string denied_msg
void status_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
void mute_observer(const simple_wml::node &mute, player_iterator muter)
Mute an observer or give a message of all currently muted observers if no name is given...
std::optional< player_iterator > ban_user(const simple_wml::node &ban, player_iterator banner)
Ban and kick a user by name.
bool has_attr(const char *key) const
void process_change_turns_wml(simple_wml::document &data, player_iterator user)
Handles incoming [change_turns_wml] data.
void games_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
std::ostream & games(std::ostream &out) const
bool is_player(player_iterator player) const
void refresh_tournaments(const boost::system::error_code &ec)
void remove_player(player_iterator player)
static lg::log_domain log_server("server")
const std::string & version() const
static std::string player_status(const wesnothd::player_record &player)
const simple_wml::node::child_list & get_sides_list() const
boost::asio::signal_set sighup_
static l_noret error(LoadState *S, const char *why)
#define MP_TOO_MANY_ATTEMPTS_ERROR
void update_side_data()
Resets the side configuration according to the scenario data.
std::string is_ip_banned(const std::string &ip)
std::function< void(const std::string &, const std::string &, std::string &, std::ostringstream *)> cmd_handler
void kickban_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
const std::string & name() const
child_itors child_range(config_key_type key)
void start_lan_server_timer()
std::string announcements_
std::unique_ptr< user_handler > user_handler_
node & set_attr_int(const char *key, int value)
void help_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
node & set_attr(const char *key, const char *value)
bool wildcard_string_match(const std::string &str, const std::string &match)
Match using '*' as any number of characters (including none), '+' as one or more characters, and '?' as any one character.
void wml_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
bool to_bool(bool default_value=false) const
void stats_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
#define MP_NAME_TOO_LONG_ERROR
void handle_read_from_fifo(const boost::system::error_code &error, std::size_t bytes_transferred)
void handle_message(player_iterator player, simple_wml::node &message)
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
static bool make_delete_diff(const simple_wml::node &src, const char *gamelist, const char *type, const simple_wml::node *remove, simple_wml::document &out)
std::size_t default_time_period_
void handle_join_game(player_iterator player, simple_wml::node &join)
void requests_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Define the errors the server may send during the login procedure.
std::vector< std::string > accepted_versions_
std::size_t default_max_messages_
void new_scenario(player_iterator player)
when the host sends the new scenario of a mp campaign
node & set_attr_dup(const char *key, const char *value)
bool is_moderator() const
const child_list & children(const char *name) const
void ban_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definitions for the interface to Wesnoth Markup Language (WML).
void handle_sighup(const boost::system::error_code &error, int signal_number)
bool process_turn(simple_wml::document &data, player_iterator user)
Handles [end_turn], repackages [commands] with private [speak]s in them and sends the data...
player_connections::const_iterator player_iterator
bool deny_unregistered_login_
void handle_graceful_timeout(const boost::system::error_code &error)
#define LOG_SERVER
normal events
#define MP_INCORRECT_PASSWORD_ERROR
void gban_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
node * child(const char *name)
const std::string & source() const
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
node * child(const char *name)
std::string recommended_version_
void start_tournaments_timer()
const char * begin() const
simple_wml::document login_response_
#define MP_NAME_UNREGISTERED_ERROR
A class to handle the non-SQL logic for connecting to the phpbb forum database.
void login_client(boost::asio::yield_context yield, socket_ptr socket)
boost::asio::streambuf admin_cmd_
void truncate_message(const simple_wml::string_span &str, simple_wml::node &message)
Function to ensure a text message is within the allowed length.
bool allow_remote_shutdown_
std::map< std::string, config > proxy_versions_
int main(int argc, char **argv)
void unban_group(std::ostringstream &os, const std::string &group)
void msg_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
std::map< socket_ptr::element_type *, std::string > seeds_
void handle_lan_server_shutdown(const boost::system::error_code &error)
bool is_login_allowed(socket_ptr socket, const simple_wml::node *const login, const std::string &username, bool ®istered, bool &is_moderator)
void read(config &cfg, std::istream &in, abstract_validator *validator)
void list_deleted_bans(std::ostringstream &out, const std::string &mask="*") const
bool has_password() const
std::string ban(const std::string &, const std::time_t &, const std::string &, const std::string &, const std::string &, const std::string &="")
void process_whiteboard(simple_wml::document &data, player_iterator user)
Handles incoming [whiteboard] data.
std::string restart_command
void handle_player_in_game(player_iterator player, simple_wml::document &doc)
bool describe_slots()
Set the description to the number of available slots.
void transfer_side_control(player_iterator player, const simple_wml::node &cfg)
Let's a player owning a side give it to another player or observer.
bool player_is_in_game(player_iterator player) const
boost::asio::steady_timer timer_
boost::asio::io_service io_service_
void clones_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
bool ip_exceeds_connection_limit(const std::string &ip) const
std::string input_path_
server socket/fifo.
An interface class to handle nick registration To activate it put a [user_handler] section into the s...
std::unique_ptr< std::istream > scoped_istream
void send_server_message_to_all(const std::string &message, std::optional< player_iterator > exclude={})
void motd_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
void process_message(simple_wml::document &data, player_iterator)
std::vector< std::string > tor_ip_list_
static lg::log_domain log_config("config")
boost::asio::ip::tcp::acceptor acceptor_v6_
void bans_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
std::string get_replay_filename()
std::ostream & requests(std::ostream &out) const
void searchlog_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
void send_password_request(socket_ptr socket, const std::string &msg, const std::string &user, const char *error_code="", bool force_confirmation=false)
void start_game(player_iterator starter)
simple_wml::document version_query_response_
void perform_controller_tweaks()
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
std::string replay_save_path_
void kick_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
bool remove_player(player_iterator player, const bool disconnect=false, const bool destruct=false)
Removes a user from the game.
static void make_add_diff(const simple_wml::node &src, const char *gamelist, const char *type, simple_wml::document &out, int index=-1)
boost::asio::steady_timer dump_stats_timer_
const std::string & name() const
std::optional< player_iterator > kick_member(const simple_wml::node &kick, player_iterator kicker)
Kick a member by name.
void load_next_scenario(player_iterator user)
A user (player only?) asks for the next scenario to advance to.
bool is_owner(player_iterator player) const
const std::string help_msg
void cleanup_game(game *)
std::string server_message
static simple_wml::node * starting_pos(simple_wml::node &data)
void set_moderator(bool moderator)
static std::string stats()
void game_terminated(const std::string &reason)
void send_server_message(const char *message, std::optional< player_iterator > player={}, simple_wml::document *doc=nullptr) const
void sample_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Thrown by operations encountering invalid UTF-8 data.
node & set_attr_dup(const char *key, const char *value)
boost::asio::steady_timer lan_server_timer_
const node::child_list & children(const char *name) const
std::size_t concurrent_connections_
void unban_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
const std::string config_file_
void roll_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
static map_location::DIRECTION s
void delete_game(int, const std::string &reason="")
void remove_child(const char *name, std::size_t index)
std::time_t failed_login_ban_
std::string password(const std::string &server, const std::string &login)
std::vector< node * > child_list
void async_send_doc_queued(socket_ptr socket, simple_wml::document &doc)
High level wrapper for sending a WML document.
std::string client_address(const socket_ptr socket)
bool authenticate(socket_ptr socket, const std::string &username, const std::string &password, bool name_taken, bool ®istered)
std::string admin_passwd_
void reset_last_synced_context_id()
wesnothd::ban_manager ban_manager_
std::deque< login_log > failed_logins_
void adminmsg_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
boost::asio::steady_timer tournaments_timer_
void stopgame(const std::string &, const std::string &, std::string &, std::ostringstream *)
#define ERR_SERVER
fatal and directly server related errors/warnings, ie not caused by erroneous client data ...
std::string & insert(std::string &str, const std::size_t pos, const std::string &insert)
Insert a UTF-8 string at the specified position.
Declarations for File-IO.
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
#define MP_PASSWORD_REQUEST
std::string lowercase(const std::string &s)
Returns a lowercased version of the string.
void set_description(simple_wml::node *desc)
Functions to set/get the address of the game's summary description as sent to players in the lobby...
boost::asio::posix::stream_descriptor input_
#define MP_NAME_INACTIVE_WARNING
std::map< std::string, config > redirected_versions_
const version_info wesnoth_version(VERSION)
void set_password(const std::string &passwd)
void handle_query(player_iterator player, simple_wml::node &query)
bool set_log_domain_severity(const std::string &name, int severity)
Represents version numbers.
const std::shared_ptr< game > get_game() const
void dump_stats(const boost::system::error_code &ec)
void set_game(std::shared_ptr< game > new_game)
#define SETUP_HANDLER(name, function)
int request_sample_frequency
void restart_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
void send_server_message(socket_ptr socket, const std::string &message, const std::string &type)
#define MP_PASSWORD_REQUEST_FOR_LOGGED_IN_NAME
void version_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
#define MP_NAME_TAKEN_ERROR
std::string is_ip_banned(const std::string &ip)
void unban_user(const simple_wml::node &unban, player_iterator unbanner)
void list_bans(std::ostringstream &out, const std::string &mask="*")
server(int port, bool keep_alive, const std::string &config_file, std::size_t, std::size_t)
void coro_send_doc(socket_ptr socket, simple_wml::document &doc, boost::asio::yield_context yield)
Send a WML document from within a coroutine.
std::vector< std::string > split(const config_attribute_value &val)
void handle_create_game(player_iterator player, simple_wml::node &create_game)
void unban(std::ostringstream &os, const std::string &ip, bool immediate_write=true)
bool parse_time(const std::string &duration, std::time_t *time) const
Parses the given duration and adds it to *time except if the duration is '0' or 'permanent' in which ...
std::map< std::string, cmd_handler > cmd_handlers_
const std::string & termination_reason() const
Standard logging facilities (interface).
void dul_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
std::string str() const
Serializes the version number into string form.
std::shared_ptr< boost::asio::ip::tcp::socket > socket_ptr
void trim(std::string_view &s)
player_connections player_connections_
void load_config()
Parse the server config into local variables.
std::deque< std::shared_ptr< game > > games() const
void update_game_in_lobby(const game &g, std::optional< player_iterator > exclude={})
std::deque< connection_log > ip_log_
void handle_new_client(socket_ptr socket)
#define MP_NAME_RESERVED_ERROR
Account email address ban.
void ungban_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
std::string process_command(std::string cmd, std::string issuer_name)
Process commands from admins and users.
config read_config() const
Read the server config from file 'config_file_'.
void copy_into(node &n) const
void metrics_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
static bool read_config(config &src, config &dst)
A config object defines a single node in a WML file, with access to child nodes.
void lobbymsg_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
#define MP_NAME_AUTH_BAN_EMAIL_ERROR
void pm_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
Function to use the WML preprocessor on a file.
void async_send_warning(socket_ptr socket, const std::string &msg, const char *warning_code="", const info_table &info={})
const std::string & get_ban_help() const
static map_location::DIRECTION n
void set_name_bans(const std::vector< std::string > name_bans)
void handle_player_in_lobby(player_iterator player, simple_wml::document &doc)
void abort_lan_server_timer()
std::size_t max_ip_log_size_
void unmute_observer(const simple_wml::node &unmute, player_iterator unmuter)
void mute_all_observers()
void disconnect_player(player_iterator player)
version_info secure_version
void handle_nickserv(player_iterator player, simple_wml::node &nickserv)
void send_server_message_to_lobby(const std::string &message, std::optional< player_iterator > exclude={})
#define WRN_SERVER
clients send wrong/unexpected data
void set_termination_reason(const std::string &reason)
std::vector< std::string > disallowed_names_
std::pair< std::string, unsigned > item
std::string client_ip() const
std::string node_to_string(const node &n)
#define MP_NAME_AUTH_BAN_IP_ERROR
void send_data(simple_wml::document &data, std::optional< player_iterator > exclude={}, std::string packet_type="")
#define MP_INVALID_CHARS_IN_NAME_ERROR
simple_wml::document & level()
The full scenario data.
simple_wml::node * description() const
static bool make_change_diff(const simple_wml::node &src, const char *gamelist, const char *type, const simple_wml::node *item, simple_wml::document &out)