server.cpp

Go to the documentation of this file.
00001 /* $Id: server.cpp 26797 2008-05-23 17:37:37Z suokko $ */
00002 /*
00003    Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 //! @file server/server.cpp
00016 //! Wesnoth-Server, for multiplayer-games.
00017 
00018 #include "../global.hpp"
00019 
00020 #include "../config.hpp"
00021 #include "../game_config.hpp"
00022 #include "../log.hpp"
00023 #include "../map.hpp" // gamemap::MAX_PLAYERS
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 // on Windows we don't calculate CPU time
00071 clock_t get_cpu_time(bool active) {
00072     return 0;
00073 }
00074 
00075 #endif
00076 
00077 //! fatal and directly server related errors/warnings,
00078 //! ie not caused by erroneous client data
00079 #define ERR_SERVER LOG_STREAM(err, mp_server)
00080 //! clients send wrong/unexpected data
00081 #define WRN_SERVER LOG_STREAM(warn, mp_server)
00082 //! normal events
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 //compatibility code for MS compilers
00089 #ifndef SIGHUP
00090 #define SIGHUP 20
00091 #endif
00092 // FIXME: should define SIGINT here too, but to what?
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 // we take profiling info on every n requests
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     //inserts will be processed first by the client, so insert at index+1,
00211     //and then when the delete is processed we'll slide into the right position
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     //! std::map<network::connection,player>
00261     player_map players_;
00262     std::vector<game*> games_;
00263     game not_logged_in_;
00264     //! The lobby is implemented as a game.
00265     game lobby_;
00266 
00267     //! server socket/fifo
00268     input_stream& input_;
00269 
00270     const std::string config_file_;
00271     config cfg_;
00272     //! Read the server config from file 'config_file_'.
00273     config read_config() const;
00274     
00275     // settings from the server config
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     //! Parse the server config into local variables.
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     //! Handle queries from clients.
00311     void process_query(const network::connection sock,
00312                        simple_wml::node& query);
00313     //! Process commands from admins and users.
00314     std::string process_command(const std::string& cmd);
00315     //! Handle private messages between players.
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     // Example config line: 
00413     // restart_command="./wesnothd-debug -d -c ~/.wesnoth1.5/server.cfg"
00414     // remember to make new one as a daemon or it will block old one
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         // Try to run with 50 FPS all the time
00479         // Server will respond a bit faster under heavy load
00480         fps_limit_.limit();
00481         try {
00482             // We are going to waith 10 seconds before shutting down so users can get out of game.
00483             if (graceful_restart && games_.size() == 0 && ++graceful_counter > 500 )
00484             {
00485                 // TODO: We should implement client side autoreconnect.
00486                 // Idea:
00487                 // server should send [reconnect]host=host,port=number[/reconnect]
00488                 // Then client would reconnect to new server automaticaly.
00489                 // This would also allow server to move to new port or address if there is need
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             // Process commands from the server socket/fifo
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                 // and check if bans have expired
00508                 ban_manager_.check_ban_times(now);
00509                 // Make sure we log stats every 5 minutes
00510                 if (last_stats_ + 5*60 <= now) {
00511                     dump_stats(now);
00512                 }
00513                 // send a 'ping' to all players to detect ghosts
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                 //TODO: this is a HUGE HACK. There was a bug in Wesnoth 1.4
00557                 //that caused it to still use binary WML for the leave game
00558                 //message. (ugh). We will see if this looks like binary WML
00559                 //and if it does, assume it's a leave_game message
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             // Was the user already logged in?
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             // Was the player in the lobby or a game?
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                     // Did the last player leave?
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         // Ignore client side pings for now.
00690         return;
00691     } else if(not_logged_in_.is_observer(sock)) {
00692         // Someone who is not yet logged in is sending login details.
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     // See if the client is sending their version number.
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         // Check if it is an accepted version.
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         // Check if it is a redirected version
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         // Check if it's a version we should start a proxy for.
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         // No match, send a response and reject them.
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     // Client must send a login first.
00779     if (login == NULL) {
00780         send_error(sock, "You must login first.");
00781         return;
00782     }
00783 
00784     // Check if the username is valid (all alpha-numeric plus underscore and hyphen)
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     // Check if the username is allowed.
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     // Check the username isn't already taken
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     // Send the new player the entire list of games and players
00827     send_doc(games_and_users_list_, sock);
00828 
00829     if (motd_ != "") {
00830         lobby_.send_server_message(motd_.c_str(), sock);
00831     }
00832 
00833     // Send other players in the lobby the update that the player has joined
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         // Note: This string is parsed by the client to identify lobby join messages!
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     // Commands a player may issue.
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     // Example config line: 
00893     // restart_command="./wesnothd-debug -d -c ~/.wesnoth1.5/server.cfg"
00894     // remember to make new one as a daemon or it will block old one
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             // Graceful shut down.
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         // TODO: check if this works in windows.
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             // stop listening socket
00931             server_.stop();
00932             input_.stop();
00933             // start new server
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             // if we find a '.' consider it an ip mask 
01002             //! @todo  FIXME: should also check for only numbers
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         // if we find a '.' consider it an ip mask
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             // Don't send to players in a running game the sender is part of.
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         // Create the new game, remove the player from the lobby
01181         // and set the player as the host/owner.
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     // See if the player is joining a game
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         //send notification of changes to the game and user
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     // See if it's a message, in which case we add the name of the sender,
01267     // and forward it to all players in the lobby.
01268     if (data.child("message")) {
01269         lobby_.process_message(data, pl);
01270     }
01271 
01272     // Player requests update of lobby content,
01273     // for example when cancelling the create game dialog
01274     if (data.child("refresh_lobby")) {
01275         send_doc(games_and_users_list_, sock);
01276     }
01277 }
01278 
01279 //! Process data sent by a player in a game. Note that 'data' by default gets
01280 //! broadcasted and saved in the replay.
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     // If this is data describing the level for a game.
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         // If this game is having its level data initialized
01325         // for the first time, and is ready for players to join.
01326         // We should currently have a summary of the game in g->level().
01327         // We want to move this summary to the games_and_users_list_, and
01328         // place a pointer to that summary in the game's description.
01329         // g->level() should then receive the full data for the game.
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             // Update our config object which describes the open games,
01335             // and save a pointer to the description in the new game.
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         // Update the game's description.
01353         // If there is no shroud, then tell players in the lobby
01354         // what the map looks like
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         // map id
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         //desc["map_name"] = data["name"];
01383         //desc["map_description"] = data["description"];
01384         //desc[""] = data["objectives"];
01385         //desc[""] = data["random_start_time"];
01386         //desc[""] = data["turns"];
01387         //desc["client_version"] = data["version"];
01388 
01389         // Record the full scenario in g->level()
01390 
01391         g->level().swap(data);
01392         // The host already put himself in the scenario so we just need
01393         // to update_side_data().
01394         //g->take_side(sock);
01395         g->update_side_data();
01396         g->describe_slots();
01397 
01398         assert(games_and_users_list_.child("gamelist")->children("game").empty() == false);
01399 
01400         // Send the update of the game description to the lobby.
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         //! @todo FIXME: Why not save the level data in the history_?
01406         return;
01407 // Everything below should only be processed if the game is already intialized.
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     // If the host is sending the next scenario data.
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         // Record the full scenario in g->level()
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         // Update the game's description.
01449         // If there is no shroud, then tell players in the lobby
01450         // what the map looks like.
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         // map id
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         //desc["map_name"] = s["name"];
01476         //desc["map_description"] = s["description"];
01477         //desc[""] = s["objectives"];
01478         //desc[""] = s["random_start_time"];
01479         //desc[""] = s["turns"];
01480         //desc["client_version"] = s["version"];
01481         // Send the update of the game description to the lobby.
01482 
01483         //update the game having changed in the lobby
01484         update_game_in_lobby(g);
01485         return;
01486     // If a player advances to the next scenario of a mp campaign. (deprecated)
01487     } else if(data.child("notify_next_scenario")) {
01488         //g->send_data(g->construct_server_message(pl->second.name()
01489         //      + " advanced to the next scenario."), sock);
01490         return;
01491     // A mp client sends a request for the next scenario of a mp campaign.
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         // Send notification of the game starting immediately.
01498         // g->start_game() will send data that assumes
01499         // the [start_game] message has been sent
01500         g->send_data(data, sock);
01501         g->start_game(pl);
01502 
01503         //update the game having changed in the lobby
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             // Remove the player in delete_game() with all other remaining
01518             // ones so he gets the updated gamelist.
01519             delete_game(itor);
01520         } else {
01521             g->remove_player(sock);
01522             lobby_.add_player(sock, true);
01523             g->describe_slots();
01524 
01525             // Send all other players in the lobby the update to the gamelist.
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             // Send the player who has quit the gamelist.
01536             send_doc(games_and_users_list_, sock);
01537         }
01538         return;
01539     // If this is data describing side changes by the host.
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     // If a player changes his faction.
01553     } else if (data.child("change_faction")) {
01554         g->send_data(data, sock);
01555         return;
01556     // If the owner of a side is changing the controller.
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         // FIXME: Why not save it in the history_? (if successful)
01564         return;
01565     // If all observers should be muted. (toggles)
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     // If an observer should be muted.
01574     } else if (data.child("mute")) {
01575         g->mute_observer(*data.child("mute"), pl);
01576         return;
01577     // The owner is kicking/banning someone from the game.
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             // Send the removed user the lobby game list.
01589             send_doc(games_and_users_list_, user);
01590             // FIXME: should also send a user diff to the lobby
01591             //        to mark this player as available for others
01592         }
01593         return;
01594     // If info is being provided about the game state.
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         // Notify the game of the commands, and if it changes
01604         // the description, then sync the new description
01605         // to players in the lobby.
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     // Data to store and broadcast.
01614     } else if (data.child("stop_updates")) {
01615 //      if (g->started()) g->record_data(data);
01616         g->send_data(data, sock);
01617         return;
01618     // Data to ignore.
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     // Send a diff of the gamelist with the game deleted to players in the lobby
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     // Delete the game from the games_and_users_list_.
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         // Can happen when the game ends before the scenario was transfered.
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     // Set the availability status for all quitting users.
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     //send users in the game a notification to leave the game since it has ended
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     // Put the remaining users back in the lobby.
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     // show 'info' by default
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         //! @todo errno should be passed here with the error or it might not be
01818         //! the true errno anymore. Seems to work good enough for now though.
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 }

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs