00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "../global.hpp"
00019
00020 #include "../config.hpp"
00021 #include "../game_config.hpp"
00022 #include "../log.hpp"
00023 #include "../map.hpp"
00024 #include "../network.hpp"
00025 #include "../filesystem.hpp"
00026 #include "../serialization/parser.hpp"
00027 #include "../serialization/preprocessor.hpp"
00028 #include "../serialization/string_utils.hpp"
00029
00030 #include "game.hpp"
00031 #include "input_stream.hpp"
00032 #include "metrics.hpp"
00033 #include "player.hpp"
00034 #include "proxy.hpp"
00035 #include "simple_wml.hpp"
00036 #include "ban.hpp"
00037
00038 #include <algorithm>
00039 #include <cassert>
00040 #include <cerrno>
00041 #include <cstdlib>
00042 #include <ctime>
00043 #include <iostream>
00044 #include <map>
00045 #include <set>
00046 #include <sstream>
00047 #include <vector>
00048 #include <queue>
00049
00050 #include <csignal>
00051
00052 #ifndef _WIN32
00053 #include <sys/times.h>
00054
00055 namespace {
00056
00057 clock_t get_cpu_time(bool active) {
00058 if(!active) {
00059 return 0;
00060 }
00061 struct tms buf;
00062 times(&buf);
00063 return buf.tms_utime + buf.tms_stime;
00064 }
00065
00066 }
00067
00068 #else
00069
00070
00071 clock_t get_cpu_time(bool active) {
00072 return 0;
00073 }
00074
00075 #endif
00076
00077
00078
00079 #define ERR_SERVER LOG_STREAM(err, mp_server)
00080
00081 #define WRN_SERVER LOG_STREAM(warn, mp_server)
00082
00083 #define LOG_SERVER LOG_STREAM(info, mp_server)
00084 #define DBG_SERVER LOG_STREAM(debug, mp_server)
00085 #define ERR_CONFIG LOG_STREAM(err, config)
00086 #define WRN_CONFIG LOG_STREAM(warn, config)
00087
00088
00089 #ifndef SIGHUP
00090 #define SIGHUP 20
00091 #endif
00092
00093
00094 sig_atomic_t config_reload = 0;
00095
00096 static void reload_config(int signal) {
00097 assert(signal == SIGHUP);
00098 config_reload = 1;
00099 }
00100
00101 static void exit_sigint(int signal) {
00102 assert(signal == SIGINT);
00103 LOG_SERVER << "SIGINT caught, exiting without cleanup immediately.\n";
00104 exit(1);
00105 }
00106
00107 static void exit_sigterm(int signal) {
00108 assert(signal == SIGTERM);
00109 LOG_SERVER << "SIGTERM caught, exiting without cleanup immediately.\n";
00110 exit(1);
00111 }
00112
00113 namespace {
00114
00115
00116 int request_sample_frequency = 1;
00117
00118 void send_doc(simple_wml::document& doc, network::connection connection)
00119 {
00120 simple_wml::string_span s = doc.output_compressed();
00121 network::send_raw_data(s.begin(), s.size(), connection);
00122 }
00123
00124 void make_add_diff(const simple_wml::node& src, const char* gamelist,
00125 const char* type,
00126 simple_wml::document& out, int index=-1)
00127 {
00128 if(out.root().child("gamelist_diff") == NULL) {
00129 out.root().add_child("gamelist_diff");
00130 }
00131
00132 simple_wml::node* top = out.root().child("gamelist_diff");
00133 if(gamelist) {
00134 top = &top->add_child("change_child");
00135 top->set_attr_int("index", 0);
00136 top = &top->add_child("gamelist");
00137 }
00138
00139 simple_wml::node& insert = top->add_child("insert_child");
00140 const simple_wml::node::child_list& children = src.children(type);
00141 assert(!children.empty());
00142 if(index < 0) {
00143 index = children.size() - 1;
00144 }
00145
00146 assert(index < children.size());
00147 insert.set_attr_int("index", index);
00148 children[index]->copy_into(insert.add_child(type));
00149 }
00150
00151 bool make_delete_diff(const simple_wml::node& src,
00152 const char* gamelist,
00153 const char* type,
00154 const simple_wml::node* remove,
00155 simple_wml::document& out)
00156 {
00157 if(out.root().child("gamelist_diff") == NULL) {
00158 out.root().add_child("gamelist_diff");
00159 }
00160
00161 simple_wml::node* top = out.root().child("gamelist_diff");
00162 if(gamelist) {
00163 top = &top->add_child("change_child");
00164 top->set_attr_int("index", 0);
00165 top = &top->add_child("gamelist");
00166 }
00167
00168 const simple_wml::node::child_list& children = src.children(type);
00169 const simple_wml::node::child_list::const_iterator itor =
00170 std::find(children.begin(), children.end(), remove);
00171 if(itor == children.end()) {
00172 return false;
00173 }
00174 const int index = itor - children.begin();
00175 simple_wml::node& del = top->add_child("delete_child");
00176 del.set_attr_int("index", index);
00177 del.add_child(type);
00178 return true;
00179 }
00180
00181 bool make_change_diff(const simple_wml::node& src,
00182 const char* gamelist,
00183 const char* type,
00184 const simple_wml::node* item,
00185 simple_wml::document& out)
00186 {
00187 if(out.root().child("gamelist_diff") == NULL) {
00188 out.root().add_child("gamelist_diff");
00189 }
00190
00191 simple_wml::node* top = out.root().child("gamelist_diff");
00192 if(gamelist) {
00193 top = &top->add_child("change_child");
00194 top->set_attr_int("index", 0);
00195 top = &top->add_child("gamelist");
00196 }
00197 const simple_wml::node::child_list& children = src.children(type);
00198 const simple_wml::node::child_list::const_iterator itor =
00199 std::find(children.begin(), children.end(), item);
00200 if(itor == children.end()) {
00201 return false;
00202 }
00203
00204 simple_wml::node& diff = *top;
00205 simple_wml::node& del = diff.add_child("delete_child");
00206 const int index = itor - children.begin();
00207 del.set_attr_int("index", index);
00208 del.add_child(type);
00209
00210
00211
00212 simple_wml::node& insert = diff.add_child("insert_child");
00213 insert.set_attr_int("index", index+1);
00214 children[index]->copy_into(insert.add_child(type));
00215 return true;
00216 }
00217
00218 }
00219 class fps_limiter {
00220 size_t start_ticks_;
00221 size_t ms_per_frame_;
00222 public:
00223 fps_limiter(size_t ms_per_frame = 20) : start_ticks_(0),ms_per_frame_(ms_per_frame)
00224 {}
00225
00226 void limit() {
00227 size_t current_ticks = SDL_GetTicks();
00228 if (current_ticks - start_ticks_ < ms_per_frame_) {
00229 SDL_Delay(ms_per_frame_ - (current_ticks - start_ticks_));
00230 start_ticks_ += ms_per_frame_;
00231 } else {
00232 start_ticks_ = current_ticks;
00233 }
00234 }
00235
00236 void set_ms_per_frame(size_t ms_per_frame)
00237 {
00238 ms_per_frame_ = ms_per_frame;
00239 }
00240
00241 void set_fps(size_t fps)
00242 {
00243 ms_per_frame_ = 1000 / fps;
00244 }
00245 };
00246
00247
00248 class server
00249 {
00250 public:
00251 server(int port, input_stream& input, const std::string& config_file, size_t min_threads,size_t max_threads);
00252 void run();
00253 private:
00254 void send_error(network::connection sock, const char* msg) const;
00255 void send_error_dup(network::connection sock, const std::string& msg) const;
00256 const network::manager net_manager_;
00257 network::server_manager server_;
00258 wesnothd::ban_manager ban_manager_;
00259
00260
00261 player_map players_;
00262 std::vector<game*> games_;
00263 game not_logged_in_;
00264
00265 game lobby_;
00266
00267
00268 input_stream& input_;
00269
00270 const std::string config_file_;
00271 config cfg_;
00272
00273 config read_config() const;
00274
00275
00276 std::set<std::string> accepted_versions_;
00277 std::map<std::string,config> redirected_versions_;
00278 std::map<std::string,config> proxy_versions_;
00279 std::vector<std::string> disallowed_names_;
00280 std::string admin_passwd_;
00281 std::set<network::connection> admins_;
00282 std::string motd_;
00283 size_t default_max_messages_;
00284 size_t default_time_period_;
00285 size_t concurrent_connections_;
00286 bool graceful_restart;
00287 std::string restart_command;
00288
00289 void load_config();
00290
00291 bool ip_exceeds_connection_limit(const std::string& ip) const;
00292 bool is_ip_banned(const std::string& ip) const;
00293
00294 simple_wml::document version_query_response_;
00295 simple_wml::document login_response_;
00296 simple_wml::document join_lobby_response_;
00297 simple_wml::document games_and_users_list_;
00298
00299 metrics metrics_;
00300 fps_limiter fps_limit_;
00301
00302 time_t last_ping_;
00303 time_t last_stats_;
00304 void dump_stats(const time_t& now);
00305
00306 void process_data(const network::connection sock,
00307 simple_wml::document& data);
00308 void process_login(const network::connection sock,
00309 simple_wml::document& data);
00310
00311 void process_query(const network::connection sock,
00312 simple_wml::node& query);
00313
00314 std::string process_command(const std::string& cmd);
00315
00316 void process_whisper(const network::connection sock,
00317 simple_wml::node& whisper) const;
00318 void process_data_lobby(const network::connection sock,
00319 simple_wml::document& data);
00320 void process_data_game(const network::connection sock,
00321 simple_wml::document& data);
00322 void delete_game(std::vector<game*>::iterator game_it);
00323
00324 void update_game_in_lobby(const game* g, network::connection exclude=0);
00325
00326 void start_new_server();
00327 };
00328
00329 server::server(int port, input_stream& input, const std::string& config_file, size_t min_threads,size_t max_threads)
00330 : net_manager_(min_threads,max_threads),
00331 server_(port),
00332 not_logged_in_(players_),
00333 lobby_(players_),
00334 input_(input),
00335 config_file_(config_file),
00336 cfg_(read_config()),
00337 graceful_restart(false),
00338 version_query_response_("[version]\n[/version]\n", simple_wml::INIT_COMPRESSED),
00339 login_response_("[mustlogin]\n[/mustlogin]\n", simple_wml::INIT_COMPRESSED),
00340 join_lobby_response_("[join_lobby]\n[/join_lobby]\n", simple_wml::INIT_COMPRESSED),
00341 games_and_users_list_("[gamelist]\n[/gamelist]\n", simple_wml::INIT_STATIC),
00342 last_ping_(time(NULL)),
00343 last_stats_(last_ping_)
00344 {
00345 load_config();
00346 signal(SIGHUP, reload_config);
00347 signal(SIGINT, exit_sigint);
00348 signal(SIGTERM, exit_sigterm);
00349 }
00350
00351 void server::send_error(network::connection sock, const char* msg) const
00352 {
00353 simple_wml::document doc;
00354 doc.root().add_child("error").set_attr("message", msg);
00355 simple_wml::string_span output = doc.output_compressed();
00356 network::send_raw_data(output.begin(), output.size(), sock);
00357 }
00358
00359 void server::send_error_dup(network::connection sock, const std::string& msg) const
00360 {
00361 simple_wml::document doc;
00362 doc.root().add_child("error").set_attr_dup("message", msg.c_str());
00363 simple_wml::string_span output = doc.output_compressed();
00364 network::send_raw_data(output.begin(), output.size(), sock);
00365 }
00366
00367 config server::read_config() const {
00368 config configuration;
00369 if (config_file_ == "") return configuration;
00370 scoped_istream stream = preprocess_file(config_file_);
00371 std::string errors;
00372 try {
00373 read(configuration, *stream, &errors);
00374 if (errors.empty()) {
00375 LOG_SERVER << "Server configuration from file: '" << config_file_
00376 << "' read.\n";
00377 } else {
00378 ERR_CONFIG << "ERROR: Errors reading configuration file: '"
00379 << errors << "'.\n";
00380 }
00381 } catch(config::error& e) {
00382 ERR_CONFIG << "ERROR: Could not read configuration file: '"
00383 << config_file_ << "': '" << e.message << "'.\n";
00384 }
00385 return configuration;
00386 }
00387
00388 void server::load_config() {
00389 admin_passwd_ = cfg_["passwd"];
00390 motd_ = cfg_["motd"];
00391
00392 disallowed_names_.clear();
00393 if (cfg_["disallow_names"] == "") {
00394 disallowed_names_.push_back("*admin*");
00395 disallowed_names_.push_back("*admln*");
00396 disallowed_names_.push_back("*server*");
00397 disallowed_names_.push_back("player");
00398 disallowed_names_.push_back("network");
00399 disallowed_names_.push_back("human");
00400 disallowed_names_.push_back("computer");
00401 disallowed_names_.push_back("ai");
00402 disallowed_names_.push_back("ai?");
00403 } else {
00404 disallowed_names_ = utils::split(cfg_["disallow_names"]);
00405 }
00406 default_max_messages_ =
00407 lexical_cast_default<size_t>(cfg_["max_messages"],4);
00408 default_time_period_ =
00409 lexical_cast_default<size_t>(cfg_["messages_time_period"],10);
00410 concurrent_connections_ =
00411 lexical_cast_default<size_t>(cfg_["connections_allowed"],5);
00412
00413
00414
00415 restart_command = cfg_["restart_command"];
00416
00417 fps_limit_.set_ms_per_frame(lexical_cast_default<size_t>(cfg_["ms_per_frame"],20));
00418
00419 accepted_versions_.clear();
00420 const std::string& versions = cfg_["versions_accepted"];
00421 if (versions.empty() == false) {
00422 const std::vector<std::string> accepted(utils::split(versions));
00423 for (std::vector<std::string>::const_iterator i = accepted.begin(); i != accepted.end(); ++i) {
00424 accepted_versions_.insert(*i);
00425 }
00426 } else {
00427 accepted_versions_.insert(game_config::version);
00428 accepted_versions_.insert("test");
00429 }
00430
00431 redirected_versions_.clear();
00432 const config::child_list& redirects = cfg_.get_children("redirect");
00433 for (config::child_list::const_iterator i = redirects.begin(); i != redirects.end(); ++i) {
00434 const std::vector<std::string> versions(utils::split((**i)["version"]));
00435 for (std::vector<std::string>::const_iterator j = versions.begin(); j != versions.end(); ++j) {
00436 redirected_versions_[*j] = **i;
00437 }
00438 }
00439
00440 proxy_versions_.clear();
00441 const config::child_list& proxies = cfg_.get_children("proxy");
00442 for (config::child_list::const_iterator p = proxies.begin(); p != proxies.end(); ++p) {
00443 const std::vector<std::string> versions(utils::split((**p)["version"]));
00444 for (std::vector<std::string>::const_iterator j = versions.begin(); j != versions.end(); ++j) {
00445 proxy_versions_[*j] = **p;
00446 }
00447 }
00448 ban_manager_.set_default_ban_times(cfg_);
00449 }
00450
00451 bool server::ip_exceeds_connection_limit(const std::string& ip) const {
00452 if (concurrent_connections_ == 0) return false;
00453 size_t connections = 0;
00454 for (player_map::const_iterator i = players_.begin(); i != players_.end(); ++i) {
00455 if (network::ip_address(i->first) == ip) {
00456 ++connections;
00457 }
00458 }
00459
00460 return connections >= concurrent_connections_;
00461 }
00462
00463 bool server::is_ip_banned(const std::string& ip) const {
00464 return ban_manager_.is_ip_banned(ip);
00465 }
00466
00467 void server::dump_stats(const time_t& now) {
00468 last_stats_ = now;
00469 LOG_SERVER << "Statistics:"
00470 << "\tnumber_of_games = " << games_.size()
00471 << "\tnumber_of_users = " << players_.size()
00472 << "\tlobby_users = " << lobby_.nobservers() << "\n";
00473 }
00474
00475 void server::run() {
00476 int graceful_counter = 0;
00477 for (int loop = 0;; ++loop) {
00478
00479
00480 fps_limit_.limit();
00481 try {
00482
00483 if (graceful_restart && games_.size() == 0 && ++graceful_counter > 500 )
00484 {
00485
00486
00487
00488
00489
00490
00491 process_command("msg All games ended. Shutting down now. Reconnect to the new server.");
00492 throw network::error("shut down");
00493 }
00494 if (config_reload == 1) {
00495 cfg_ = read_config();
00496 load_config();
00497 config_reload = 0;
00498 }
00499
00500 std::string admin_cmd;
00501 if (input_.read_line(admin_cmd)) {
00502 process_command(admin_cmd);
00503 }
00504
00505 time_t now = time(NULL);
00506 if (last_ping_ + 15 <= now) {
00507
00508 ban_manager_.check_ban_times(now);
00509
00510 if (last_stats_ + 5*60 <= now) {
00511 dump_stats(now);
00512 }
00513
00514 config ping;
00515 ping["ping"] = lexical_cast<std::string>(now);
00516 for (player_map::const_iterator i = players_.begin();
00517 i != players_.end(); ++i)
00518 {
00519 network::send_data(ping, i->first, true);
00520 }
00521 last_ping_ = now;
00522 }
00523
00524 network::process_send_queue();
00525
00526 network::connection sock = network::accept_connection();
00527 if (sock) {
00528 const std::string& ip = network::ip_address(sock);
00529 if (is_ip_banned(ip)) {
00530 LOG_SERVER << ip << "\trejected banned user.\n";
00531 send_error(sock, "You are banned.");
00532 network::disconnect(sock);
00533 } else if (ip_exceeds_connection_limit(ip)) {
00534 LOG_SERVER << ip << "\trejected ip due to excessive connections\n";
00535 send_error(sock, "Too many connections from your IP.");
00536 network::disconnect(sock);
00537 } else {
00538 DBG_SERVER << ip << "\tnew connection accepted. (socket: "
00539 << sock << ")\n";
00540 send_doc(version_query_response_, sock);
00541 not_logged_in_.add_player(sock, true);
00542 }
00543 }
00544
00545 static int sample_counter = 0;
00546
00547 std::vector<char> buf;
00548 while ((sock = network::receive_data(buf)) != network::null_connection) {
00549 metrics_.service_request();
00550
00551 if(buf.empty()) {
00552 std::cerr << "received empty packet\n";
00553 continue;
00554 }
00555
00556
00557
00558
00559
00560 if(buf.front() < 5) {
00561 static simple_wml::document leave_game_doc(
00562 "[leave_game]\n[/leave_game]\n",
00563 simple_wml::INIT_COMPRESSED);
00564 process_data(sock, leave_game_doc);
00565 continue;
00566 }
00567
00568 const bool sample = request_sample_frequency >= 1 && (sample_counter++ % request_sample_frequency) == 0;
00569
00570 const clock_t before_parsing = get_cpu_time(sample);
00571
00572 char* buf_ptr = new char [buf.size()];
00573 memcpy(buf_ptr, &buf[0], buf.size());
00574 simple_wml::string_span compressed_buf(buf_ptr, buf.size());
00575 simple_wml::document data(compressed_buf);
00576 data.take_ownership_of_buffer(buf_ptr);
00577 std::vector<char>().swap(buf);
00578
00579 const clock_t after_parsing = get_cpu_time(sample);
00580
00581 process_data(sock, data);
00582
00583 if(sample) {
00584 const clock_t after_processing = get_cpu_time(sample);
00585 metrics_.record_sample(data.root().first_child(),
00586 after_parsing - before_parsing,
00587 after_processing - after_parsing);
00588 }
00589 }
00590
00591 metrics_.no_requests();
00592
00593 } catch(config::error& e) {
00594 WRN_CONFIG << "Warning: error in received data: " << e.message << "\n";
00595 } catch(simple_wml::error& e) {
00596 WRN_CONFIG << "Warning: error in received data\n";
00597 } catch(network::error& e) {
00598 if (e.message == "shut down") {
00599 LOG_SERVER << "Try to disconnect all users...\n";
00600 for (player_map::const_iterator pl = players_.begin();
00601 pl != players_.end(); ++pl)
00602 {
00603 network::disconnect(pl->first);
00604 }
00605 LOG_SERVER << "Shutting server down.\n";
00606 break;
00607 }
00608 if (!e.socket) {
00609 ERR_SERVER << "network error: " << e.message << "\n";
00610 e.disconnect();
00611 continue;
00612 }
00613 DBG_SERVER << "socket closed: " << e.message << "\n";
00614 const std::string ip = network::ip_address(e.socket);
00615 if (proxy::is_proxy(e.socket)) {
00616 LOG_SERVER << ip << "\tProxy user disconnected.\n";
00617 proxy::disconnect(e.socket);
00618 e.disconnect();
00619 DBG_SERVER << "done closing socket...\n";
00620 continue;
00621 }
00622
00623 const player_map::iterator pl_it = players_.find(e.socket);
00624 if (pl_it == players_.end()) {
00625 if (not_logged_in_.is_observer(e.socket)) {
00626 DBG_SERVER << ip << "\tNot logged in user disconnected.\n";
00627 not_logged_in_.remove_player(e.socket);
00628 } else {
00629 WRN_SERVER << ip << "\tWarning: User disconnected right after the connection was accepted.\n";
00630 }
00631 e.disconnect();
00632 DBG_SERVER << "done closing socket...\n";
00633 continue;
00634 }
00635 const simple_wml::node::child_list& users = games_and_users_list_.root().children("user");
00636 const size_t index = std::find(users.begin(), users.end(), pl_it->second.config_address()) - users.begin();
00637 if (index < users.size()) {
00638 simple_wml::document diff;
00639 if(make_delete_diff(games_and_users_list_.root(), NULL, "user",
00640 pl_it->second.config_address(), diff)) {
00641 lobby_.send_data(diff, e.socket);
00642 }
00643
00644 games_and_users_list_.root().remove_child("user",index);
00645 } else {
00646 ERR_SERVER << ip << "ERROR: Could not find user to remove: "
00647 << pl_it->second.name() << " in games_and_users_list_.\n";
00648 }
00649
00650 if (lobby_.is_member(e.socket)) {
00651 lobby_.remove_player(e.socket);
00652 LOG_SERVER << ip << "\t" << pl_it->second.name()
00653 << "\thas logged off. (socket: " << e.socket << ")\n";
00654
00655 } else {
00656 for (std::vector<game*>::iterator g = games_.begin();
00657 g != games_.end(); ++g)
00658 {
00659 if (!(*g)->is_member(e.socket)) {
00660 continue;
00661 }
00662
00663 if ((*g)->remove_player(e.socket, true)) {
00664 delete_game(g);
00665 break;
00666 } else {
00667 (*g)->describe_slots();
00668
00669 update_game_in_lobby(*g, e.socket);
00670 }
00671 break;
00672 }
00673 }
00674 players_.erase(pl_it);
00675 e.disconnect();
00676 DBG_SERVER << "done closing socket...\n";
00677 }
00678 }
00679 }
00680
00681 void server::process_data(const network::connection sock,
00682 simple_wml::document& data) {
00683 if (proxy::is_proxy(sock)) {
00684 proxy::received_data(sock, data);
00685 }
00686
00687 simple_wml::node& root = data.root();
00688 if(root.has_attr("ping")) {
00689
00690 return;
00691 } else if(not_logged_in_.is_observer(sock)) {
00692
00693 process_login(sock, data);
00694 } else if (simple_wml::node* query = root.child("query")) {
00695 process_query(sock, *query);
00696 } else if (simple_wml::node* whisper = root.child("whisper")) {
00697 process_whisper(sock, *whisper);
00698 } else if (lobby_.is_observer(sock)) {
00699 process_data_lobby(sock, data);
00700 } else {
00701 process_data_game(sock, data);
00702 }
00703 }
00704
00705
00706 void server::process_login(const network::connection sock,
00707 simple_wml::document& data) {
00708
00709 if (const simple_wml::node* const version = data.child("version")) {
00710 const simple_wml::string_span& version_str_span = (*version)["version"];
00711 const std::string version_str(version_str_span.begin(),
00712 version_str_span.end());
00713 std::set<std::string>::const_iterator accepted_it;
00714
00715 for (accepted_it = accepted_versions_.begin();
00716 accepted_it != accepted_versions_.end(); ++accepted_it) {
00717 if (utils::wildcard_string_match(version_str,*accepted_it)) break;
00718 }
00719 if (accepted_it != accepted_versions_.end()) {
00720 LOG_SERVER << network::ip_address(sock)
00721 << "\tplayer joined using accepted version " << version_str
00722 << ":\ttelling them to log in.\n";
00723 send_doc(login_response_, sock);
00724 return;
00725 }
00726 std::map<std::string,config>::const_iterator config_it;
00727
00728 for (config_it = redirected_versions_.begin();
00729 config_it != redirected_versions_.end(); ++config_it)
00730 {
00731 if (utils::wildcard_string_match(version_str,config_it->first))
00732 break;
00733 }
00734 if (config_it != redirected_versions_.end()) {
00735 LOG_SERVER << network::ip_address(sock)
00736 << "\tplayer joined using version " << version_str
00737 << ":\tredirecting them to " << config_it->second["host"]
00738 << ":" << config_it->second["port"] << "\n";
00739 config response;
00740 response.add_child("redirect",config_it->second);
00741 network::send_data(response, sock, true);
00742 return;
00743 }
00744
00745 for (config_it = proxy_versions_.begin();
00746 config_it != proxy_versions_.end(); ++config_it)
00747 {
00748 if (utils::wildcard_string_match(version_str,config_it->first))
00749 break;
00750 }
00751 if (config_it != proxy_versions_.end()) {
00752 LOG_SERVER << network::ip_address(sock)
00753 << "\tplayer joined using version " << version_str
00754 << ":\tconnecting them by proxy to " << config_it->second["host"]
00755 << ":" << config_it->second["port"] << "\n";
00756 proxy::create_proxy(sock,config_it->second["host"],
00757 lexical_cast_default<int>(config_it->second["port"],15000));
00758 return;
00759 }
00760
00761 LOG_SERVER << network::ip_address(sock)
00762 << "\tplayer joined using unknown version " << version_str
00763 << ":\trejecting them\n";
00764 config response;
00765 if (!accepted_versions_.empty()) {
00766 response["version"] = *accepted_versions_.begin();
00767 } else if (redirected_versions_.empty() == false) {
00768 response["version"] = redirected_versions_.begin()->first;
00769 } else {
00770 ERR_SERVER << "ERROR: This server doesn't accept any versions at all.\n";
00771 response["version"] = "null";
00772 }
00773 network::send_data(response, sock, true);
00774 return;
00775 }
00776
00777 const simple_wml::node* const login = data.child("login");
00778
00779 if (login == NULL) {
00780 send_error(sock, "You must login first.");
00781 return;
00782 }
00783
00784
00785 std::string username = (*login)["username"].to_string();
00786 if (!utils::isvalid_username(username)) {
00787 send_error(sock, "This username contains invalid "
00788 "characters. Only alpha-numeric characters, underscores and hyphens"
00789 "are allowed.");
00790 return;
00791 }
00792 if (username.size() > 18) {
00793 send_error(sock, "This username is too long. Usernames must be 18 characers or less.");
00794 return;
00795 }
00796
00797 for (std::vector<std::string>::const_iterator d_it = disallowed_names_.begin();
00798 d_it != disallowed_names_.end(); ++d_it)
00799 {
00800 if (utils::wildcard_string_match(utils::lowercase(username),
00801 utils::lowercase(*d_it)))
00802 {
00803 send_error(sock, "The nick you chose is reserved and cannot be used by players");
00804 return;
00805 }
00806 }
00807
00808 player_map::const_iterator p;
00809 for (p = players_.begin(); p != players_.end(); ++p) {
00810 if (p->second.name() == username) {
00811 send_error(sock, "The username you chose is already taken.");
00812 return;
00813 }
00814 }
00815
00816 send_doc(join_lobby_response_, sock);
00817
00818 simple_wml::node& player_cfg = games_and_users_list_.root().add_child("user");
00819 const player new_player(username, player_cfg, default_max_messages_,
00820 default_time_period_);
00821 players_.insert(std::pair<network::connection,player>(sock, new_player));
00822
00823 not_logged_in_.remove_player(sock);
00824 lobby_.add_player(sock, true);
00825
00826
00827 send_doc(games_and_users_list_, sock);
00828
00829 if (motd_ != "") {
00830 lobby_.send_server_message(motd_.c_str(), sock);
00831 }
00832
00833
00834 simple_wml::document diff;
00835 make_add_diff(games_and_users_list_.root(), NULL, "user", diff);
00836 lobby_.send_data(diff, sock);
00837
00838 LOG_SERVER << network::ip_address(sock) << "\t" << username
00839 << "\thas logged on. (socket: " << sock << ")\n";
00840
00841 for (std::vector<game*>::const_iterator g = games_.begin(); g != games_.end(); ++g) {
00842
00843 (*g)->send_server_message_to_all((username + " has logged into the lobby").c_str());
00844 }
00845 }
00846
00847 void server::process_query(const network::connection sock,
00848 simple_wml::node& query) {
00849 const player_map::const_iterator pl = players_.find(sock);
00850 if (pl == players_.end()) {
00851 DBG_SERVER << "ERROR: process_query(): Could not find player with socket: " << sock << "\n";
00852 return;
00853 }
00854 const simple_wml::string_span& command(query["type"]);
00855 std::ostringstream response;
00856 const std::string& help_msg = "Available commands are: help, metrics,"
00857 " motd, status, wml.";
00858 if (admins_.count(sock) != 0) {
00859 LOG_SERVER << "Admin Command:" << "\ttype: " << command
00860 << "\tIP: "<< network::ip_address(sock)
00861 << "\tnick: "<< pl->second.name() << std::endl;
00862 response << process_command(command.to_string());
00863
00864 } else if (command == "help") {
00865 response << help_msg;
00866 } else if (command == "status") {
00867 response << process_command(command.to_string() + " " + pl->second.name());
00868 } else if (command == "status " + pl->second.name() || command == "metrics"
00869 || command == "motd" || command == "wml" || command == "netstats") {
00870 response << process_command(command.to_string());
00871 } else if (command == admin_passwd_) {
00872 LOG_SERVER << "New Admin recognized:" << "\tIP: "
00873 << network::ip_address(sock) << "\tnick: "
00874 << pl->second.name() << std::endl;
00875 admins_.insert(sock);
00876 response << "You are now recognized as an administrator.";
00877 } else if (admin_passwd_.empty() == false) {
00878 WRN_SERVER << "FAILED Admin attempt: '" << command << "'\tIP: "
00879 << network::ip_address(sock) << "\tnick: "
00880 << pl->second.name() << std::endl;
00881 response << "Error: unrecognized query: '" << command << "'\n" << help_msg;
00882 } else {
00883 response << "Error: unrecognized query: '" << command << "'\n" << help_msg;
00884 }
00885 lobby_.send_server_message(response.str().c_str(), sock);
00886 }
00887
00888 void server::start_new_server() {
00889 if (restart_command.empty())
00890 return;
00891
00892
00893
00894
00895 std::system(restart_command.c_str());
00896
00897 LOG_SERVER << "New server started with command: " << restart_command << "\n";
00898 }
00899
00900 std::string server::process_command(const std::string& query) {
00901 std::ostringstream out;
00902 const std::string::const_iterator i = std::find(query.begin(),query.end(),' ');
00903 const std::string command(query.begin(),i);
00904 std::string parameters = (i == query.end() ? "" : std::string(i+1,query.end()));
00905 utils::strip(parameters);
00906 const std::string& help_msg = "Available commands are: ban(s) [<mask>] [<reason>],"
00907 "kick <mask>, k(ick)ban [<mask>] [<reason>], help, metrics, netstats,"
00908 " (lobby)msg <message>, motd [<message>], status [<mask>],"
00909 " unban <ipmask>, shut_down [now], restart";
00910 if (command == "shut_down") {
00911 if (parameters == "now") {
00912 throw network::error("shut down");
00913 } else {
00914
00915 server_.stop();
00916 input_.stop();
00917 graceful_restart = true;
00918 process_command("msg The server is shutting down. You may finish your games but can't start new ones. Once all games have ended the server will exit.");
00919 out << "Server is doing graceful shut down.";
00920 }
00921
00922 #ifndef _WIN32 // Not sure if this works on windows
00923
00924 } else if (command == "restart") {
00925 if (restart_command.empty()) {
00926 out << "No restart_command configured! Not restarting.";
00927 } else {
00928 LOG_SERVER << "Graceful restart requested.";
00929 graceful_restart = true;
00930
00931 server_.stop();
00932 input_.stop();
00933
00934 start_new_server();
00935 process_command("msg The server has been restarted. You may finish your games but can't start new ones and new players can't join this server.");
00936 out << "New server started.";
00937 }
00938 #endif
00939 } else if (command == "help") {
00940 out << help_msg;
00941 } else if (command == "metrics") {
00942 out << metrics_ << "Current number of games = " << games_.size() << "\n"
00943 "Total number of users = " << players_.size() << "\n"
00944 "Number of users in the lobby = " << lobby_.nobservers() << "\n";
00945 } else if (command == "wml") {
00946 out << simple_wml::document::stats();
00947 } else if (command == "netstats") {
00948 network::pending_statistics stats = network::get_pending_stats();
00949 out << "Network stats:\nPending send buffers: "
00950 << stats.npending_sends << "\nBytes in buffers: "
00951 << stats.nbytes_pending_sends << "\n";
00952 } else if (command == "msg" || command == "lobbymsg") {
00953 if (parameters == "") {
00954 return "You must type a message.";
00955 }
00956 lobby_.send_server_message_to_all(parameters.c_str());
00957 if (command == "msg") {
00958 for (std::vector<game*>::const_iterator g = games_.begin(); g != games_.end(); ++g) {
00959 (*g)->send_server_message_to_all(parameters.c_str());
00960 }
00961 }
00962 LOG_SERVER << "<server> " + parameters + "\n";
00963 out << "message '" << parameters << "' relayed to players\n";
00964 } else if (command == "status") {
00965 out << "STATUS REPORT\n";
00966 for (player_map::const_iterator pl = players_.begin(); pl != players_.end(); ++pl) {
00967 if (parameters == ""
00968 || utils::wildcard_string_match(pl->second.name(), parameters)
00969 || utils::wildcard_string_match(network::ip_address(pl->first), parameters)) {
00970 const network::connection_stats& stats = network::get_connection_stats(pl->first);
00971 const int time_connected = stats.time_connected/1000;
00972 const int seconds = time_connected%60;
00973 const int minutes = (time_connected/60)%60;
00974 const int hours = time_connected/(60*60);
00975 out << "'" << pl->second.name() << "' @ " << network::ip_address(pl->first)
00976 << " connected for " << hours << ":" << minutes << ":" << seconds
00977 << " sent " << stats.bytes_sent << " bytes, received "
00978 << stats.bytes_received << " bytes\n";
00979 }
00980 }
00981 } else if (command == "ban" || command == "bans" || command == "kban" || command == "kickban") {
00982 if (parameters == "") {
00983 ban_manager_.list_bans(out);
00984 } else {
00985 bool banned_ = false;
00986 const bool kick = (command == "kban" || command == "kickban");
00987 const std::string::iterator first_space = std::find(parameters.begin(), parameters.end(), ' ');
00988 if (first_space == parameters.end())
00989 {
00990 return ban_manager_.get_ban_help();
00991 }
00992 std::string::iterator second_space = std::find(first_space+1, parameters.end(), ' ');
00993 const std::string target(parameters.begin(), first_space);
00994 const std::string time(first_space+1,second_space);
00995 if (second_space == parameters.end())
00996 {
00997 --second_space;
00998 }
00999 std::string reason(second_space + 1, parameters.end());
01000 utils::strip(reason);
01001
01002
01003 if (std::count(target.begin(), target.end(), '.') >= 1) {
01004 banned_ = true;
01005 out << "Set ban on '" << target << "' with time '" << time << "' with reason: '" << reason << "'.\n";
01006
01007 ban_manager_.ban(target, ban_manager_.parse_time(time), reason);
01008
01009 if (kick) {
01010 for (player_map::const_iterator pl = players_.begin();
01011 pl != players_.end(); ++pl)
01012 {
01013 if (utils::wildcard_string_match(network::ip_address(pl->first), target)) {
01014 out << "Kicked " << pl->second.name() << ".\n";
01015 network::queue_disconnect(pl->first);
01016 }
01017 }
01018 }
01019 } else {
01020 for (player_map::const_iterator pl = players_.begin();
01021 pl != players_.end(); ++pl)
01022 {
01023 if (utils::wildcard_string_match(pl->second.name(), target)) {
01024 banned_ = true;
01025 const std::string& ip = network::ip_address(pl->first);
01026 if (!is_ip_banned(ip)) {
01027 ban_manager_.ban(ip,ban_manager_.parse_time(time), reason);
01028 out << "Set ban on '" << ip << "' with time '" << time << "' with reason: '"
01029 << reason << "'.\n";
01030 }
01031 if (kick) {
01032 out << "Kicked " << pl->second.name() << ".\n";
01033 network::queue_disconnect(pl->first);
01034 }
01035 }
01036 }
01037 if (!banned_) {
01038 out << "Nickmask '" << target << "' did not match, no bans set.";
01039 }
01040 }
01041 }
01042 } else if (command == "unban") {
01043 if (parameters == "") {
01044 return "You must enter an ipmask to unban.";
01045 }
01046 ban_manager_.unban(out, parameters);
01047 } else if (command == "kick") {
01048 if (parameters == "") {
01049 return "You must enter a mask to kick.";
01050 }
01051 bool kicked = false;
01052
01053 if (std::count(parameters.begin(), parameters.end(), '.') >= 1) {
01054 for (player_map::const_iterator pl = players_.begin();
01055 pl != players_.end(); ++pl)
01056 {
01057 if (utils::wildcard_string_match(network::ip_address(pl->first), parameters)) {
01058 kicked = true;
01059 out << "Kicked " << pl->second.name() << ".\n";
01060 network::queue_disconnect(pl->first);
01061 }
01062 }
01063 } else {
01064 for (player_map::const_iterator pl = players_.begin();
01065 pl != players_.end(); ++pl)
01066 {
01067 if (utils::wildcard_string_match(pl->second.name(), parameters)) {
01068 kicked = true;
01069 out << "Kicked " << pl->second.name() << " ("
01070 << network::ip_address(pl->first) << ").\n";
01071 network::queue_disconnect(pl->first);
01072 }
01073 }
01074 }
01075 if (!kicked) out << "No user matched '" << parameters << "'.\n";
01076 } else if(command == "sample") {
01077 request_sample_frequency = atoi(parameters.c_str());
01078 if(request_sample_frequency <= 0) {
01079 out << "Sampling turned off";
01080 } else {
01081 out << "Sampling every " << request_sample_frequency << " requests\n";
01082 }
01083 } else if (command == "motd") {
01084 if (parameters == "") {
01085 if (motd_ != "") {
01086 out << "Message of the day: " << motd_;
01087 return out.str();
01088 } else {
01089 return "No message of the day set.";
01090 }
01091 }
01092 motd_ = parameters;
01093 out << "Message of the day set to: " << motd_;
01094 } else {
01095 out << "Command '" << command << "' is not recognized.\n" << help_msg;
01096 }
01097
01098 LOG_SERVER << out.str() << "\n";
01099 return out.str();
01100 }
01101
01102 void server::process_whisper(const network::connection sock,
01103 simple_wml::node& whisper) const {
01104 if ((whisper["receiver"] == "") || (whisper["message"] == "")) {
01105 static simple_wml::document data(
01106 "[message]\n"
01107 "message=\"Invalid number of arguments\"\n"
01108 "sender=\"server\"\n"
01109 "[/message]\n", simple_wml::INIT_COMPRESSED);
01110 send_doc(data, sock);
01111 return;
01112 }
01113 const player_map::const_iterator pl = players_.find(sock);
01114 if (pl == players_.end()) {
01115 ERR_SERVER << "ERROR: Could not find whispering player. (socket: "
01116 << sock << ")\n";
01117 return;
01118 }
01119 whisper.set_attr_dup("sender", pl->second.name().c_str());
01120 bool dont_send = false;
01121 const simple_wml::string_span& whisper_receiver = whisper["receiver"];
01122 for (player_map::const_iterator i = players_.begin(); i != players_.end(); ++i) {
01123 if (whisper_receiver != i->second.name().c_str()) {
01124 continue;
01125 }
01126
01127 std::vector<game*>::const_iterator g;
01128 for (g = games_.begin(); g != games_.end(); ++g) {
01129 if (!(*g)->is_member(i->first)) continue;
01130
01131 dont_send = ((*g)->started() && (*g)->is_player(i->first) && (*g)->is_member(sock));
01132 break;
01133 }
01134 if (dont_send) {
01135 break;
01136 }
01137
01138 simple_wml::document cwhisper;
01139 whisper.copy_into(cwhisper.root().add_child("whisper"));
01140 send_doc(cwhisper, i->first);
01141 return;
01142 }
01143
01144 simple_wml::document data;
01145 simple_wml::node& msg = data.root().add_child("message");
01146
01147 if (dont_send) {
01148 msg.set_attr("message", "You cannot send private messages to players in a running game you observe.");
01149 } else {
01150 msg.set_attr_dup("message", ("Can't find '" + whisper["receiver"].to_string() + "'.").c_str());
01151 }
01152
01153 msg.set_attr("sender", "server");
01154 send_doc(data, sock);
01155 }
01156
01157 void server::process_data_lobby(const network::connection sock,
01158 simple_wml::document& data) {
01159 DBG_SERVER << "in process_data_lobby...\n";
01160
01161 const player_map::iterator pl = players_.find(sock);
01162 if (pl == players_.end()) {
01163 ERR_SERVER << "ERROR: Could not find player in players_. (socket: "
01164 << sock << ")\n";
01165 return;
01166 }
01167
01168 if (data.root().child("create_game")) {
01169 if (graceful_restart) {
01170 static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED);
01171 send_doc(leave_game_doc, sock);
01172 lobby_.send_server_message("This server is shutting down. You aren't allowed to make new games. Please reconnect to the new server.", sock);
01173 send_doc(games_and_users_list_, sock);
01174 return;
01175 }
01176 const std::string game_name = (*data.root().child("create_game"))["name"].to_string();
01177 const std::string game_password = (*data.root().child("create_game"))["password"].to_string();
01178 DBG_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
01179 << "\tcreates a new game: \"" << game_name << "\".\n";
01180
01181
01182 games_.push_back(new game(players_, sock, game_name));
01183 game& g = *games_.back();
01184 if(game_password.empty() == false) {
01185 g.set_password(game_password);
01186 }
01187
01188 data.root().child("create_game")->copy_into(g.level().root());
01189 lobby_.remove_player(sock);
01190 simple_wml::document diff;
01191 if(make_change_diff(games_and_users_list_.root(), NULL,
01192 "user", pl->second.config_address(), diff)) {
01193 lobby_.send_data(diff);
01194 }
01195 return;
01196 }
01197
01198
01199 if (data.root().child("join")) {
01200 const bool observer = data.root().child("join")->attr("observe").to_bool();
01201 const std::string& password = (*data.root().child("join"))["password"].to_string();
01202 int game_id = (*data.root().child("join"))["id"].to_int();
01203
01204 const std::vector<game*>::iterator g =
01205 std::find_if(games_.begin(),games_.end(), game_id_matches(game_id));
01206
01207 static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED);
01208 if (g == games_.end()) {
01209 WRN_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
01210 << "\tattempted to join unknown game:\t" << game_id << ".\n";
01211 send_doc(leave_game_doc, sock);
01212 lobby_.send_server_message("Attempt to join unknown game.", sock);
01213 send_doc(games_and_users_list_, sock);
01214 return;
01215 } else if ((*g)->player_is_banned(sock)) {
01216 DBG_SERVER << network::ip_address(sock) << "\tReject banned player: "
01217 << pl->second.name() << "\tfrom game:\t\"" << (*g)->name()
01218 << "\" (" << game_id << ").\n";
01219 send_doc(leave_game_doc, sock);
01220 lobby_.send_server_message("You are banned from this game.", sock);
01221 send_doc(games_and_users_list_, sock);
01222 return;
01223 } else if(!observer && !(*g)->password_matches(password)) {
01224 WRN_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
01225 << "\tattempted to join game:\t\"" << (*g)->name() << "\" ("
01226 << game_id << ") with bad password\n";
01227 send_doc(leave_game_doc, sock);
01228 lobby_.send_server_message("Incorrect password.", sock);
01229 send_doc(games_and_users_list_, sock);
01230 return;
01231 } else if (observer && !(*g)->allow_observers()) {
01232 WRN_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
01233 << "\tattempted to observe game:\t\"" << (*g)->name() << "\" ("
01234 << game_id << ") which doesn't allow observers.\n";
01235 send_doc(leave_game_doc, sock);
01236 lobby_.send_server_message("Attempt to observe a game that doesn't allow observers.", sock);
01237 send_doc(games_and_users_list_, sock);
01238 return;
01239 } else if (!(*g)->level_init()) {
01240 WRN_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
01241 << "\tattempted to join uninitialized game:\t\"" << (*g)->name()
01242 << "\" (" << game_id << ").\n";
01243 send_doc(leave_game_doc, sock);
01244 lobby_.send_server_message("Attempt to observe a game that doesn't allow observers.", sock);
01245 send_doc(games_and_users_list_, sock);
01246 return;
01247 }
01248 LOG_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
01249 << "\tjoined game:\t\"" << (*g)->name()
01250 << "\" (" << game_id << (observer ? ") as an observer.\n" : ").\n");
01251 lobby_.remove_player(sock);
01252 (*g)->add_player(sock, observer);
01253 (*g)->describe_slots();
01254
01255
01256 simple_wml::document diff;
01257 bool diff1 = make_change_diff(*games_and_users_list_.root().child("gamelist"),
01258 "gamelist", "game", (*g)->description(), diff);
01259 bool diff2 = make_change_diff(games_and_users_list_.root(), NULL,
01260 "user", pl->second.config_address(), diff);
01261 if (diff1 || diff2) {
01262 lobby_.send_data(diff);
01263 }
01264 }
01265
01266
01267
01268 if (data.child("message")) {
01269 lobby_.process_message(data, pl);
01270 }
01271
01272
01273
01274 if (data.child("refresh_lobby")) {
01275 send_doc(games_and_users_list_, sock);
01276 }
01277 }
01278
01279
01280
01281 void server::process_data_game(const network::connection sock,
01282 simple_wml::document& data) {
01283 DBG_SERVER << "in process_data_game...\n";
01284
01285 const player_map::iterator pl = players_.find(sock);
01286 if (pl == players_.end()) {
01287 ERR_SERVER << "ERROR: Could not find player in players_. (socket: "
01288 << sock << ")\n";
01289 return;
01290 }
01291
01292 std::vector<game*>::iterator itor;
01293 for (itor = games_.begin(); itor != games_.end(); ++itor) {
01294 if ((*itor)->is_owner(sock) || (*itor)->is_member(sock))
01295 break;
01296 }
01297 if (itor == games_.end()) {
01298 ERR_SERVER << "ERROR: Could not find game for player: "
01299 << pl->second.name() << ". (socket: " << sock << ")\n";
01300 return;
01301 }
01302
01303 game* g = *itor;
01304
01305
01306 if (data.root().child("side")) {
01307 if (!g->is_owner(sock)) {
01308 return;
01309 }
01310 size_t nsides = 0;
01311 const simple_wml::node::child_list& sides = data.root().children("side");
01312 for (simple_wml::node::child_list::const_iterator s = sides.begin(); s != sides.end(); ++s) {
01313 ++nsides;
01314 }
01315 if (nsides > gamemap::MAX_PLAYERS) {
01316 delete_game(itor);
01317 std::stringstream msg;
01318 msg << "This server does not support games with more than "
01319 << gamemap::MAX_PLAYERS << " sides.";
01320 lobby_.send_server_message(msg.str().c_str(), sock);
01321 return;
01322 }
01323
01324
01325
01326
01327
01328
01329
01330 if (!g->level_init()) {
01331 LOG_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
01332 << "\tcreated game:\t\"" << g->name() << "\" ("
01333 << g->id() << ").\n";
01334
01335
01336 simple_wml::node* const gamelist = games_and_users_list_.child("gamelist");
01337 assert(gamelist != NULL);
01338 simple_wml::node& desc = gamelist->add_child("game");
01339 g->level().root().copy_into(desc);
01340 g->set_description(&desc);
01341 desc.set_attr_dup("id", lexical_cast<std::string>(g->id()).c_str());
01342 } else {
01343 WRN_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
01344 << "\tsent scenario data in game:\t\"" << g->name() << "\" ("
01345 << g->id() << ") although it's already initialized.\n";
01346 return;
01347 }
01348
01349 assert(games_and_users_list_.child("gamelist")->children("game").empty() == false);
01350
01351 simple_wml::node& desc = *g->description();
01352
01353
01354
01355 if (!data["mp_shroud"].to_bool()) {
01356 desc.set_attr_dup("map_data", data["map_data"]);
01357 }
01358 if(data.child("era")) {
01359 desc.set_attr_dup("mp_era", data.child("era")->attr("id"));
01360 if(data.child("era")->attr("require_era").to_bool()) {
01361 desc.set_attr("require_era", "yes");
01362 }
01363 } else {
01364 desc.set_attr("mp_era", "");
01365 }
01366
01367
01368 desc.set_attr_dup("mp_scenario", data["id"]);
01369 desc.set_attr_dup("observer", data["observer"]);
01370 desc.set_attr_dup("mp_village_gold", data["mp_village_gold"]);
01371 desc.set_attr_dup("experience_modifier", data["experience_modifier"]);
01372 desc.set_attr_dup("mp_fog", data["mp_fog"]);
01373 desc.set_attr_dup("mp_shroud", data["mp_shroud"]);
01374 desc.set_attr_dup("mp_use_map_settings", data["mp_use_map_settings"]);
01375 desc.set_attr_dup("mp_countdown", data["mp_countdown"]);
01376 desc.set_attr_dup("mp_countdown_init_time", data["mp_countdown_init_time"]);
01377 desc.set_attr_dup("mp_countdown_turn_bonus", data["mp_countdown_turn_bonus"]);
01378 desc.set_attr_dup("mp_countdown_reservoir_time", data["mp_countdown_reservoir_time"]);
01379 desc.set_attr_dup("mp_countdown_action_bonus", data["mp_countdown_action_bonus"]);
01380 desc.set_attr_dup("savegame", data["savegame"]);
01381 desc.set_attr_dup("hash", data["hash"]);
01382
01383
01384
01385
01386
01387
01388
01389
01390
01391 g->level().swap(data);
01392
01393
01394
01395 g->update_side_data();
01396 g->describe_slots();
01397
01398 assert(games_and_users_list_.child("gamelist")->children("game").empty() == false);
01399
01400
01401 simple_wml::document diff;
01402 make_add_diff(*games_and_users_list_.child("gamelist"), "gamelist", "game", diff);
01403 lobby_.send_data(diff);
01404
01405
01406 return;
01407
01408 } else if (!g->level_init()) {
01409 WRN_SERVER << "Received unknown data from: " << pl->second.name()
01410 << " (socket:" << sock
01411 << ") while the scenario wasn't yet initialized.\n" << data.output();
01412 return;
01413
01414 } else if (data.child("store_next_scenario")) {
01415 if (!g->is_owner(sock)) return;
01416 if (!g->level_init()) {
01417 WRN_SERVER << network::ip_address(sock) << "\tWarning: "
01418 << pl->second.name() << "\tsent [store_next_scenario] in game:\t\""
01419 << g->name() << "\" (" << g->id()
01420 << ") while the scenario is not yet initialized.";
01421 return;
01422 }
01423 size_t nsides = 0;
01424 const simple_wml::node::child_list& sides = data.root().children("side");
01425 for (simple_wml::node::child_list::const_iterator s = sides.begin(); s != sides.end(); ++s) {
01426 ++nsides;
01427 }
01428 if (nsides > gamemap::MAX_PLAYERS) {
01429 delete_game(itor);
01430 std::stringstream msg;
01431 msg << "This server does not support games with more than "
01432 << gamemap::MAX_PLAYERS << " sides.";
01433 lobby_.send_server_message(msg.str().c_str(), sock);
01434 return;
01435 }
01436 const simple_wml::node& s = *data.child("store_next_scenario");
01437
01438 g->level().clear();
01439 s.copy_into(g->level().root());
01440 g->start_game(pl);
01441 if (g->description() == NULL) {
01442 ERR_SERVER << network::ip_address(sock) << "\tERROR: \""
01443 << g->name() << "\" (" << g->id()
01444 << ") is initialized but has no description_.\n";
01445 return;
01446 }
01447 simple_wml::node& desc = *g->description();
01448
01449
01450
01451 if (s["mp_shroud"].to_bool()) {
01452 desc.set_attr_dup("map_data", s["map_data"]);
01453 }
01454
01455 if(s.child("era")) {
01456 desc.set_attr_dup("mp_era", s.child("era")->attr("id"));
01457 } else {
01458 desc.set_attr("mp_era", "");
01459 }
01460
01461
01462 desc.set_attr_dup("mp_scenario", s["id"]);
01463 desc.set_attr_dup("observer", s["observer"]);
01464 desc.set_attr_dup("mp_village_gold", s["mp_village_gold"]);
01465 desc.set_attr_dup("experience_modifier", s["experience_modifier"]);
01466 desc.set_attr_dup("mp_fog", s["mp_fog"]);
01467 desc.set_attr_dup("mp_shroud", s["mp_shroud"]);
01468 desc.set_attr_dup("mp_use_map_settings", s["mp_use_map_settings"]);
01469 desc.set_attr_dup("mp_countdown", s["mp_countdown"]);
01470 desc.set_attr_dup("mp_countdown_init_time", s["mp_countdown_init_time"]);
01471 desc.set_attr_dup("mp_countdown_turn_bonus", s["mp_countdown_turn_bonus"]);
01472 desc.set_attr_dup("mp_countdown_reservoir_time", s["mp_countdown_reservoir_time"]);
01473 desc.set_attr_dup("mp_countdown_action_bonus", s["mp_countdown_action_bonus"]);
01474 desc.set_attr_dup("hash", s["hash"]);
01475
01476
01477
01478
01479
01480
01481
01482
01483
01484 update_game_in_lobby(g);
01485 return;
01486
01487 } else if(data.child("notify_next_scenario")) {
01488
01489
01490 return;
01491
01492 } else if (data.child("load_next_scenario")) {
01493 g->load_next_scenario(pl);
01494 return;
01495 } else if (data.child("start_game")) {
01496 if (!g->is_owner(sock)) return;
01497
01498
01499
01500 g->send_data(data, sock);
01501 g->start_game(pl);
01502
01503
01504 update_game_in_lobby(g);
01505 return;
01506 } else if (data.child("leave_game")) {
01507 if ((g->is_player(sock) && g->nplayers() == 1)
01508 || (g->is_owner(sock) && !g->started())) {
01509 LOG_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
01510 << (g->started() ? "\tended game:\t\"" : "\taborted game:\t\"")
01511 << g->name() << "\" (" << g->id() << ")"
01512 << (g->started() ? " at turn: "
01513 + lexical_cast_default<std::string,size_t>(g->current_turn())
01514 + " with reason: '" + g->termination_reason() + "'" : "")
01515 << ".\n";
01516 g->send_server_message_to_all((pl->second.name() + " ended the game.").c_str(), pl->first);
01517
01518
01519 delete_game(itor);
01520 } else {
01521 g->remove_player(sock);
01522 lobby_.add_player(sock, true);
01523 g->describe_slots();
01524
01525
01526 simple_wml::document diff;
01527 bool diff1 = make_change_diff(*games_and_users_list_.root().child("gamelist"),
01528 "gamelist", "game", g->description(), diff);
01529 bool diff2 = make_change_diff(games_and_users_list_.root(), NULL,
01530 "user", pl->second.config_address(), diff);
01531 if (diff1 || diff2) {
01532 lobby_.send_data(diff, sock);
01533 }
01534
01535
01536 send_doc(games_and_users_list_, sock);
01537 }
01538 return;
01539
01540 } else if (data.child("scenario_diff")) {
01541 if (!g->is_owner(sock)) return;
01542 g->level().root().apply_diff(*data.child("scenario_diff"));
01543 const simple_wml::node* cfg_change = data.child("scenario_diff")->child("change_child");
01544 if ((cfg_change != NULL) && (cfg_change->child("side") != NULL)) {
01545 g->update_side_data();
01546 }
01547 if (g->describe_slots()) {
01548 update_game_in_lobby(g);
01549 }
01550 g->send_data(data, sock);
01551 return;
01552
01553 } else if (data.child("change_faction")) {
01554 g->send_data(data, sock);
01555 return;
01556
01557 } else if (data.child("change_controller")) {
01558 const simple_wml::node& change = *data.child("change_controller");
01559 g->transfer_side_control(sock, change);
01560 if (g->describe_slots()) {
01561 update_game_in_lobby(g);
01562 }
01563
01564 return;
01565
01566 } else if (data.child("muteall")) {
01567 if (!g->is_owner(sock)) {
01568 g->send_server_message("You cannot mute: not the game host.", sock);
01569 return;
01570 }
01571 g->mute_all_observers();
01572 return;
01573
01574 } else if (data.child("mute")) {
01575 g->mute_observer(*data.child("mute"), pl);
01576 return;
01577
01578 } else if (data.child("kick") || data.child("ban")) {
01579 bool ban = (data.child("ban") != NULL);
01580 const network::connection user =
01581 (ban ? g->ban_user(*data.child("ban"), pl)
01582 : g->kick_member(*data.child("kick"), pl));
01583 if (user) {
01584 lobby_.add_player(user, true);
01585 if (g->describe_slots()) {
01586 update_game_in_lobby(g, user);
01587 }
01588
01589 send_doc(games_and_users_list_, user);
01590
01591
01592 }
01593 return;
01594
01595 } else if (data.child("info")) {
01596 if (!g->is_player(sock)) return;
01597 const simple_wml::node& info = *data.child("info");
01598 if (info["type"] == "termination") {
01599 g->set_termination_reason(info["condition"].to_string());
01600 }
01601 return;
01602 } else if (data.child("turn")) {
01603
01604
01605
01606 if (g->process_turn(data, pl)) {
01607 update_game_in_lobby(g);
01608 }
01609 return;
01610 } else if (data.child("message")) {
01611 g->process_message(data, pl);
01612 return;
01613
01614 } else if (data.child("stop_updates")) {
01615
01616 g->send_data(data, sock);
01617 return;
01618
01619 } else if (data.child("error")
01620 || data.child("side_secured")
01621 || data.root().has_attr("failed")
01622 || data.root().has_attr("side_drop")
01623 || data.root().has_attr("side")) {
01624 return;
01625 }
01626
01627 WRN_SERVER << "Received unknown data from: " << pl->second.name()
01628 << ". (socket:" << sock << ")\n" << data.output();
01629 }
01630
01631 void server::delete_game(std::vector<game*>::iterator game_it) {
01632 metrics_.game_terminated((*game_it)->termination_reason());
01633
01634 simple_wml::node* const gamelist = games_and_users_list_.child("gamelist");
01635 assert(gamelist != NULL);
01636
01637
01638 simple_wml::document diff;
01639 bool send_diff = false;
01640 if(make_delete_diff(*gamelist, "gamelist", "game",
01641 (*game_it)->description(), diff)) {
01642 send_diff = true;
01643 }
01644
01645
01646 const simple_wml::node::child_list& games = gamelist->children("game");
01647 const simple_wml::node::child_list::const_iterator g =
01648 std::find(games.begin(), games.end(), (*game_it)->description());
01649 if (g != games.end()) {
01650 const size_t index = g - games.begin();
01651 gamelist->remove_child("game", index);
01652 } else {
01653
01654 LOG_SERVER << "Could not find game (" << (*game_it)->id()
01655 << ") to delete in games_and_users_list_.\n";
01656 }
01657 const user_vector& users = (*game_it)->all_game_users();
01658
01659 for (user_vector::const_iterator user = users.begin();
01660 user != users.end(); user++)
01661 {
01662 const player_map::iterator pl = players_.find(*user);
01663 if (pl != players_.end()) {
01664 pl->second.mark_available();
01665 if (make_change_diff(games_and_users_list_.root(), NULL,
01666 "user", pl->second.config_address(), diff)) {
01667 send_diff = true;
01668 }
01669 } else {
01670 ERR_SERVER << "ERROR: delete_game(): Could not find user in players_. (socket: "
01671 << *user << ")\n";
01672 }
01673 }
01674 if (send_diff) {
01675 lobby_.send_data(diff);
01676 }
01677
01678
01679 static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED);
01680 (*game_it)->send_data(leave_game_doc);
01681
01682 lobby_.add_players(**game_it, true);
01683
01684 (*game_it)->send_data(games_and_users_list_);
01685
01686 delete *game_it;
01687 games_.erase(game_it);
01688 }
01689
01690 void server::update_game_in_lobby(const game* g, network::connection exclude)
01691 {
01692 simple_wml::document diff;
01693 if(make_change_diff(*games_and_users_list_.root().child("gamelist"), "gamelist", "game", g->description(), diff)) {
01694 lobby_.send_data(diff, exclude);
01695 }
01696 }
01697
01698 #include <sys/types.h>
01699 #include <sys/stat.h>
01700 #include <fcntl.h>
01701 #include <unistd.h>
01702
01703 int main(int argc, char** argv) {
01704 int port = 15000;
01705 size_t min_threads = 5;
01706 size_t max_threads = 0;
01707
01708 std::string config_file;
01709
01710 #ifndef FIFODIR
01711 # define FIFODIR "/var/run/wesnothd"
01712 #endif
01713 std::string fifo_path = std::string(FIFODIR) + "/socket";
01714
01715
01716 lg::set_log_domain_severity("server", 2);
01717 lg::timestamps(true);
01718
01719 for (int arg = 1; arg != argc; ++arg) {
01720 const std::string val(argv[arg]);
01721 if (val.empty()) {
01722 continue;
01723 }
01724
01725 if ((val == "--config" || val == "-c") && arg+1 != argc) {
01726 config_file = argv[++arg];
01727 } else if (val == "--verbose" || val == "-v") {
01728 lg::set_log_domain_severity("all",3);
01729 } else if (val.substr(0, 6) == "--log-") {
01730 size_t p = val.find('=');
01731 if (p == std::string::npos) {
01732 std::cerr << "unknown option: " << val << '\n';
01733 return 0;
01734 }
01735 std::string s = val.substr(6, p - 6);
01736 int severity;
01737 if (s == "error") severity = 0;
01738 else if (s == "warning") severity = 1;
01739 else if (s == "info") severity = 2;
01740 else if (s == "debug") severity = 3;
01741 else {
01742 std::cerr << "unknown debug level: " << s << '\n';
01743 return 0;
01744 }
01745 while (p != std::string::npos) {
01746 size_t q = val.find(',', p + 1);
01747 s = val.substr(p + 1, q == std::string::npos ? q : q - (p + 1));
01748 if (!lg::set_log_domain_severity(s, severity)) {
01749 std::cerr << "unknown debug domain: " << s << '\n';
01750 return 0;
01751 }
01752 p = q;
01753 }
01754 } else if ((val == "--port" || val == "-p") && arg+1 != argc) {
01755 port = atoi(argv[++arg]);
01756 } else if (val == "--help" || val == "-h") {
01757 std::cout << "usage: " << argv[0]
01758 << " [-dvV] [-c path] [-m n] [-p port] [-t n]\n"
01759 << " -c, --config <path> Tells wesnothd where to find the config file to use.\n"
01760 << " -d, --daemon Runs wesnothd as a daemon.\n"
01761 << " -h, --help Shows this usage message.\n"
01762 << " --log-<level>=<domain1>,<domain2>,...\n"
01763 << " sets the severity level of the debug domains.\n"
01764 << " 'all' can be used to match any debug domain.\n"
01765 << " Available levels: error, warning, info, debug.\n"
01766 << " -p, --port <port> Binds the server to the specified port.\n"
01767 << " -t, --threads <n> Uses n worker threads for network I/O (default: 5).\n"
01768 << " -v --verbose Turns on more verbose logging.\n"
01769 << " -V, --version Returns the server version.\n";
01770 return 0;
01771 } else if (val == "--version" || val == "-V") {
01772 std::cout << "Battle for Wesnoth server " << game_config::version
01773 << "\n";
01774 return 0;
01775 } else if (val == "--daemon" || val == "-d") {
01776 #ifdef WIN32
01777 ERR_SERVER << "Running as a daemon is not supported on this platform\n";
01778 return -1;
01779 #else
01780 const pid_t pid = fork();
01781 if (pid < 0) {
01782 ERR_SERVER << "Could not fork and run as a daemon\n";
01783 return -1;
01784 } else if (pid > 0) {
01785 std::cout << "Started wesnothd as a daemon with process id "
01786 << pid << "\n";
01787 return 0;
01788 }
01789
01790 setsid();
01791 #endif
01792 } else if ((val == "--threads" || val == "-t") && arg+1 != argc) {
01793 min_threads = atoi(argv[++arg]);
01794 if (min_threads > 30) {
01795 min_threads = 30;
01796 }
01797 } else if ((val == "--max-threads" || val == "-T") && arg+1 != argc) {
01798 max_threads = atoi(argv[++arg]);
01799 } else if(val == "--request_sample_frequency" && arg+1 != argc) {
01800 request_sample_frequency = atoi(argv[++arg]);
01801 } else if (val[0] == '-') {
01802 ERR_SERVER << "unknown option: " << val << "\n";
01803 return 0;
01804 } else {
01805 port = atoi(argv[arg]);
01806 }
01807 }
01808 input_stream input(fifo_path);
01809
01810 network::set_raw_data_only();
01811
01812 try {
01813 server(port, input, config_file, min_threads, max_threads).run();
01814 } catch(network::error& e) {
01815 ERR_SERVER << "Caught network error while server was running. Aborting.: "
01816 << e.message << "\n";
01817
01818
01819 return errno;
01820 } catch(std::bad_alloc&) {
01821 ERR_SERVER << "Ran out of memory. Aborting.\n";
01822 return ENOMEM;
01823 } catch(...) {
01824 ERR_SERVER << "Caught unknown error while server was running. Aborting.\n";
01825 return -1;
01826 }
01827
01828 return 0;
01829 }