server/game.cpp

Go to the documentation of this file.
00001 /* $Id: game.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2003 - 2012 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 as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 #include "../global.hpp"
00017 
00018 #include "../filesystem.hpp"
00019 #include "../game_config.hpp" // game_config::observer_team_name
00020 #include "../log.hpp"
00021 #include "../map.hpp" // gamemap::MAX_PLAYERS
00022 
00023 #include "game.hpp"
00024 #include "player_network.hpp"
00025 #include "serialization/string_utils.hpp"
00026 #include "util.hpp"
00027 
00028 #include <boost/bind.hpp>
00029 
00030 static lg::log_domain log_server("server");
00031 #define ERR_GAME LOG_STREAM(err, log_server)
00032 #define WRN_GAME LOG_STREAM(warn, log_server)
00033 #define LOG_GAME LOG_STREAM(info, log_server)
00034 #define DBG_GAME LOG_STREAM(debug, log_server)
00035 static lg::log_domain log_config("config");
00036 #define WRN_CONFIG LOG_STREAM(warn, log_config)
00037 
00038 namespace wesnothd {
00039 int game::id_num = 1;
00040 
00041 void game::missing_user(network::connection socket, const std::string& func) const
00042 {
00043     WRN_GAME << func << "(): Could not find user (socket:\t" << socket
00044         << ") in player_info_ in game:\t\"" << name_ << "\" (" << id_ << ")\n";
00045 }
00046 
00047 game::game(player_map& players, const network::connection host,
00048         const std::string& name, bool save_replays,
00049         const std::string& replay_save_path) :
00050     player_info_(&players),
00051     id_(id_num++),
00052     name_(name),
00053     password_(),
00054     owner_(host),
00055     players_(),
00056     observers_(),
00057     muted_observers_(),
00058     sides_(gamemap::MAX_PLAYERS),
00059     side_controllers_(gamemap::MAX_PLAYERS),
00060     nsides_(0),
00061     started_(false),
00062     level_(),
00063     history_(),
00064     description_(NULL),
00065     end_turn_(0),
00066     all_observers_muted_(false),
00067     bans_(),
00068     termination_(),
00069     save_replays_(save_replays),
00070     replay_save_path_(replay_save_path),
00071     global_wait_side_(0)
00072 {
00073     assert(owner_);
00074     players_.push_back(owner_);
00075     const player_map::iterator pl = player_info_->find(owner_);
00076     if (pl == player_info_->end()) {
00077         missing_user(owner_, __func__);
00078         return;
00079     }
00080     // Mark the host as unavailable in the lobby.
00081     pl->second.mark_available(id_, name_);
00082     pl->second.set_status(player::PLAYING);
00083 }
00084 
00085 game::~game()
00086 {
00087     save_replay();
00088 
00089     user_vector users = all_game_users();
00090     for (user_vector::const_iterator u = users.begin(); u != users.end(); ++u) {
00091         remove_player(*u, false, true);
00092     }
00093     clear_history();
00094 }
00095 
00096 bool game::allow_observers() const {
00097     return level_["observer"].to_bool(true);
00098 }
00099 
00100 bool game::is_observer(const network::connection player) const {
00101     return std::find(observers_.begin(),observers_.end(),player) != observers_.end();
00102 }
00103 
00104 bool game::is_muted_observer(const network::connection player) const {
00105     if (is_observer(player)) {
00106         if (all_observers_muted_) return true;
00107     } else {
00108         return false;
00109     }
00110     return std::find(muted_observers_.begin(), muted_observers_.end(), player)
00111         != muted_observers_.end();
00112 }
00113 
00114 bool game::is_player(const network::connection player) const {
00115     return std::find(players_.begin(),players_.end(),player) != players_.end();
00116 }
00117 
00118 namespace {
00119 std::string describe_turns(int turn, const simple_wml::string_span& num_turns)
00120 {
00121     char buf[50];
00122     snprintf(buf,sizeof(buf),"%d/",int(turn));
00123 
00124     if(num_turns == "-1") {
00125         return buf + std::string("-");
00126     } else {
00127         return buf + std::string(num_turns.begin(), num_turns.end());
00128     }
00129 }
00130 
00131 }//anon namespace
00132 
00133 std::string game::username(const player_map::const_iterator pl) const
00134 {
00135     if (pl != player_info_->end()) {
00136         return pl->second.name();
00137     }
00138 
00139     return "(unknown)";
00140 }
00141 
00142 std::string game::list_users(user_vector users, const std::string& func) const
00143 {
00144     std::string list;
00145 
00146     for (user_vector::const_iterator user = users.begin(); user != users.end(); ++user) {
00147         const player_map::const_iterator pl = player_info_->find(*user);
00148         if (pl != player_info_->end()) {
00149             if (!list.empty()) list += ", ";
00150             list += pl->second.name();
00151         } else missing_user(*user, func);
00152     }
00153 
00154     return list;
00155 }
00156 
00157 void game::start_game(const player_map::const_iterator starter) {
00158     // If the game was already started we're actually advancing.
00159     const bool advance = started_;
00160     started_ = true;
00161     // Prevent inserting empty keys when reading.
00162     const simple_wml::node& s = level_.root();
00163     const bool save = s["savegame"].to_bool();
00164     LOG_GAME << network::ip_address(starter->first) << "\t"
00165         << starter->second.name() << "\t" << (advance ? "advanced" : "started")
00166         << (save ? " reloaded" : "") << " game:\t\"" << name_ << "\" (" << id_
00167         << ") with: " << list_users(players_, __func__) << ". Settings: map: " << s["id"]
00168         << "\tera: "       << (s.child("era") ? (*s.child("era"))["id"] : "")
00169         << "\tXP: "        << s["experience_modifier"]
00170         << "\tGPV: "       << s["mp_village_gold"]
00171         << "\tfog: "       << s["mp_fog"]
00172         << "\tshroud: "    << s["mp_shroud"]
00173         << "\tobservers: " << s["observer"]
00174         << "\tshuffle: "   << s["shuffle_sides"]
00175         << "\ttimer: "     << s["mp_countdown"]
00176         << (s["mp_countdown"].to_bool() ?
00177             "\treservoir time: " + s["mp_countdown_reservoir_time"].to_string() +
00178             "\tinit time: "      + s["mp_countdown_init_time"].to_string() +
00179             "\taction bonus: "   + s["mp_countdown_action_bonus"].to_string() +
00180             "\tturn bonus: "     + s["mp_countdown_turn_bonus"].to_string() : "")
00181         << "\n";
00182 
00183     update_side_data();
00184 
00185     nsides_ = 0;
00186     // Set all side controllers to 'human' so that observers will understand
00187     // that they can't take control of any sides if they happen to have the
00188     // same name as one of the descriptions.
00189     const simple_wml::node::child_list& sides = level_.root().children("side");
00190     for(simple_wml::node::child_list::const_iterator s = sides.begin(); s != sides.end(); ++s) {
00191         nsides_++;
00192         if ((**s)["controller"] != "null") {
00193             int side_num = (**s)["side"].to_int() - 1;
00194             if (sides_[side_num] == 0) {
00195                 std::stringstream msg;
00196                 msg << "Side "  << side_num + 1 << " has no controller but should! The host needs to assign control for the game to proceed past that side's turn.";
00197                 LOG_GAME << msg.str() << " (game id: " << id_ << ")\n";
00198                 send_and_record_server_message(msg.str());
00199             }
00200             (*s)->set_attr("controller", "human");
00201         }
00202     }
00203 
00204     DBG_GAME << "Number of sides: " << nsides_ << "\n";
00205     int turn = 1;
00206     int side = 0;
00207     // Savegames have a snapshot that tells us which side starts.
00208     if (const simple_wml::node* snapshot = s.child("snapshot")) {
00209         turn = lexical_cast_default<int>((*snapshot)["turn_at"], 1);
00210         side = lexical_cast_default<int>((*snapshot)["playing_team"], 0);
00211         LOG_GAME << "Reload from turn: " << turn
00212             << ". Current side is: " << side + 1 << ".\n";
00213     }
00214     end_turn_ = (turn - 1) * nsides_ + side - 1;
00215     end_turn();
00216     clear_history();
00217     if (advance) {
00218         // When the host advances tell everyone that the next scenario data is
00219         // available.
00220         static simple_wml::document notify_next_scenario("[notify_next_scenario]\n[/notify_next_scenario]\n", simple_wml::INIT_COMPRESSED);
00221         send_data(notify_next_scenario, starter->first);
00222     }
00223     // Send [observer] tags for all observers that are already in the game.
00224     send_observerjoins();
00225 }
00226 
00227 bool game::send_taken_side(simple_wml::document& cfg, const simple_wml::node::child_list::const_iterator side) const
00228 {
00229     const size_t side_num = (**side)["side"].to_int();
00230     if (side_num < 1 || side_num > gamemap::MAX_PLAYERS) return false;
00231     if (sides_[side_num - 1] != 0) return false;
00232     // We expect that the host will really use our proposed side number. (He could do different...)
00233     cfg.root().set_attr_dup("side", (**side)["side"]);
00234 
00235     // Tell the host which side the new player should take.
00236     return wesnothd::send_to_one(cfg, owner_);
00237 }
00238 
00239 bool game::take_side(const player_map::const_iterator user)
00240 {
00241     DBG_GAME << "take_side...\n";
00242 
00243     if (started_) return false;
00244 
00245     simple_wml::document cfg;
00246     cfg.root().set_attr_dup("name", user->second.name().c_str());
00247     cfg.root().set_attr("faction", "random");
00248     cfg.root().set_attr("leader", "random");
00249     cfg.root().set_attr("gender", "random");
00250 
00251     // Check if we can figure out a fitting side.
00252     const simple_wml::node::child_list& sides = level_.root().children("side");
00253     for(simple_wml::node::child_list::const_iterator side = sides.begin(); side != sides.end(); ++side) {
00254         if(((**side)["controller"] == "network" || (**side)["controller"] == "reserved")
00255                 && ((**side)["save_id"] == user->second.name().c_str()
00256                 || (**side)["current_player"] == user->second.name().c_str()))
00257         {
00258             if (send_taken_side(cfg, side)) return true;
00259         }
00260     }
00261     // If there was no fitting side just take the first available.
00262     for(simple_wml::node::child_list::const_iterator side = sides.begin(); side != sides.end(); ++side) {
00263         if((**side)["controller"] == "network") {
00264             if (send_taken_side(cfg, side)) return true;
00265         }
00266     }
00267     DBG_GAME << "take_side: there are no more sides available\n";
00268     //if we get here we couldn't find a side to take
00269     return false;
00270 }
00271 
00272 void game::update_side_data() {
00273     DBG_GAME << "update_side_data...\n";
00274     DBG_GAME << debug_player_info();
00275     // Remember everyone that is in the game.
00276     const user_vector users = all_game_users();
00277 
00278     side_controllers_.clear();
00279     side_controllers_.resize(gamemap::MAX_PLAYERS);
00280     sides_.clear();
00281     sides_.resize(gamemap::MAX_PLAYERS);
00282     players_.clear();
00283     observers_.clear();
00284 
00285     const simple_wml::node::child_list& level_sides = level_.root().children("side");
00286     /* This causes data corruption for some reason
00287     if (!lg::debug.dont_log(log_server)) {
00288         for (simple_wml::node::child_list::const_iterator side = level_sides.begin();
00289                 side != level_sides.end(); ++side)
00290             DBG_GAME << "[side]\n" << simple_wml::node_to_string(**side) << "[/side]\n";
00291     }*/
00292     // For each user:
00293     // * Find the username.
00294     // * Find the side this username corresponds to.
00295     for (user_vector::const_iterator user = users.begin(); user != users.end(); ++user) {
00296         player_map::iterator info = player_info_->find(*user);
00297         if (info == player_info_->end()) {
00298             missing_user(*user, __func__);
00299             continue;
00300         }
00301 
00302         bool side_found = false;
00303         for (simple_wml::node::child_list::const_iterator side = level_sides.begin();
00304                 side != level_sides.end(); ++side)
00305         {
00306             int side_num = (**side)["side"].to_int() - 1;
00307             if (side_num < 0 || side_num >= gamemap::MAX_PLAYERS
00308                     || sides_[side_num] != 0) continue;
00309 
00310             if ((**side)["controller"] == "network") {
00311                 if ((**side)["current_player"] == info->second.name().c_str()) {
00312                     side_controllers_[side_num] = "human";
00313                     sides_[side_num] = *user;
00314                     side_found = true;
00315                 }
00316             } else if (*user == owner_
00317             && ((**side)["controller"] == "ai" || (**side)["controller"] == "human")) {
00318                 side_controllers_[side_num] = (**side)["controller"].to_string();
00319                 sides_[side_num] = owner_;
00320                 side_found = true;
00321             } else {
00322                 // "null", "reserved"
00323                 side_controllers_[side_num] = (**side)["controller"].to_string();
00324             }
00325         }
00326         if (side_found) {
00327             players_.push_back(*user);
00328             info->second.set_status(player::PLAYING);
00329         } else {
00330             observers_.push_back(*user);
00331             info->second.set_status(player::OBSERVING);
00332         }
00333     }
00334     DBG_GAME << debug_player_info();
00335 }
00336 
00337 void game::transfer_side_control(const network::connection sock, const simple_wml::node& cfg) {
00338     DBG_GAME << "transfer_side_control...\n";
00339     if (!is_player(sock) && sock != owner_) {
00340         send_server_message("You cannot change controllers: not a player.", sock);
00341         return;
00342     }
00343 
00344     // Check the side number.
00345     const unsigned int side_num = cfg["side"].to_int();
00346     if(side_num < 1 || side_num > gamemap::MAX_PLAYERS) {
00347         std::ostringstream msg;
00348         msg << "The side number has to be between 1 and "
00349             << gamemap::MAX_PLAYERS << ".";
00350         send_server_message(msg.str(), sock);
00351         return;
00352     }
00353 
00354     if (side_num > level_.root().children("side").size()) {
00355         send_server_message("Invalid side number.", sock);
00356         return;
00357     }
00358 
00359     const simple_wml::string_span& newplayer_name = cfg["player"];
00360     const network::connection old_player = sides_[side_num - 1];
00361     const player_map::iterator oldplayer = player_info_->find(old_player);
00362     if (oldplayer == player_info_->end()) missing_user(old_player, __func__);
00363     const std::string old_player_name = username(oldplayer);
00364 
00365     // A player (un)droids his side.
00366     if (newplayer_name.empty()) {
00367         if (sock != old_player) {
00368             if (cfg["controller"].empty()) {
00369                 send_server_message("No player name or controller type given.", sock);
00370             } else {
00371                 send_server_message("You can only (un)droid your own sides!", sock);
00372             }
00373             return;
00374         } else if (cfg["controller"] != "human_ai" && cfg["controller"] != "human") {
00375             std::stringstream msg;
00376             msg << "Wrong controller type received: '" << cfg["controller"] << "'";
00377             DBG_GAME << msg.str() << "\n";
00378             send_server_message(msg.str(), sock);
00379             return;
00380         }
00381         change_controller(side_num - 1, old_player, old_player_name, false, cfg["controller"].to_string());
00382         return;
00383     }
00384 
00385     // Check if the sender actually owns the side he gives away or is the host.
00386     if (!(sock == old_player || sock == owner_)) {
00387         std::stringstream msg;
00388         msg << "You can't give away side " << side_num << ". It's controlled by '"
00389             << old_player_name << "' not you.";
00390         DBG_GAME << msg.str() << "\n";
00391         send_server_message(msg.str(), sock);
00392         return;
00393     }
00394     //find the player that is passed control
00395     player_map::iterator newplayer = find_user(newplayer_name);
00396 
00397     // Is he in this game?
00398     if (newplayer == player_info_->end() || !is_member(newplayer->first)) {
00399         send_server_message(newplayer_name.to_string() + " is not in this game", sock);
00400         return;
00401     }
00402 
00403     if (newplayer->first == old_player) {
00404         std::stringstream msg;
00405         msg << "That's already " << newplayer_name << "'s side, silly.";
00406         send_server_message(msg.str(), sock);
00407         return;
00408     }
00409     sides_[side_num - 1] = 0;
00410     // If the old player lost his last side, make him an observer.
00411     if (std::find(sides_.begin(), sides_.end(), old_player) == sides_.end()
00412     && is_player(old_player)) {
00413         observers_.push_back(old_player);
00414         oldplayer->second.set_status(player::OBSERVING);
00415         players_.erase(std::remove(players_.begin(), players_.end(), old_player), players_.end());
00416         // Tell others that the player becomes an observer.
00417         send_and_record_server_message(old_player_name + " becomes an observer.");
00418         // Update the client side observer list for everyone except old player.
00419         simple_wml::document observer_join;
00420         observer_join.root().add_child("observer").set_attr_dup("name", old_player_name.c_str());
00421         send_data(observer_join, old_player);
00422     }
00423     change_controller(side_num - 1, newplayer->first, newplayer->second.name(), false);
00424 
00425     // If we gave the new side to an observer add him to players_.
00426     if (is_observer(newplayer->first)) {
00427         players_.push_back(newplayer->first);
00428         newplayer->second.set_status(player::PLAYING);
00429         observers_.erase(std::remove(observers_.begin(), observers_.end(), newplayer->first), observers_.end());
00430         // Send everyone but the new player the observer_quit message.
00431         send_observerquit(newplayer);
00432     }
00433 }
00434 
00435 void game::change_controller(const size_t side_num,
00436         const network::connection sock,
00437         const std::string& player_name,
00438         const bool player_left,
00439         const std::string& controller)
00440 {
00441     DBG_GAME << __func__ << "...\n";
00442 
00443     const std::string& side = lexical_cast<std::string, size_t>(side_num + 1);
00444     sides_[side_num] = sock;
00445 
00446     if (player_left && side_controllers_[side_num] == "ai") {
00447         // Automatic AI side transfer.
00448     } else if (controller.empty()) {
00449         send_and_record_server_message(player_name + " takes control of side " + side + ".");
00450         side_controllers_[side_num] = "human";
00451     } else {
00452         send_and_record_server_message(player_name + (controller == "human_ai" ? " " : " un")
00453                 + "droids side " + side + ".");
00454         side_controllers_[side_num] = (controller == "human_ai" ? "ai" : "human");
00455     }
00456 
00457     simple_wml::document response;
00458     simple_wml::node& change = response.root().add_child("change_controller");
00459 
00460     change.set_attr("side", side.c_str());
00461     change.set_attr("player", player_name.c_str());
00462 
00463     // Tell everyone but the new player that this side's controller changed.
00464     change.set_attr("controller", (side_controllers_[side_num] == "ai" ? "network_ai" : "network"));
00465     send_data(response, sock);
00466 
00467     // Tell the new player that he controls this side now.
00468     // Just don't send it when the player left the game. (The host gets the
00469     // side_drop already.)
00470     if (!player_left) {
00471         change.set_attr("controller", (side_controllers_[side_num] == "ai" ? "human_ai" : "human"));
00472         wesnothd::send_to_one(response, sock);
00473     }
00474 
00475     // Update the level so observers who join get the new name. (The host handles level changes before game start.)
00476     if (started_) {
00477         const simple_wml::node::child_list& side_list = level_.root().children("side");
00478         assert(side_num < side_list.size());
00479         side_list[side_num]->set_attr_dup("current_player", player_name.c_str());
00480         // Also update controller type (so savegames of observers have proper controllers)
00481         side_list[side_num]->set_attr_dup("controller", side_controllers_[side_num].c_str());
00482     }
00483 }
00484 
00485 void game::notify_new_host(){
00486     const std::string owner_name = username(player_info_->find(owner_));
00487     simple_wml::document cfg;
00488     simple_wml::node& cfg_host_transfer = cfg.root().add_child("host_transfer");
00489 
00490     // Why do we send the new host his own name?
00491     cfg_host_transfer.set_attr("name", owner_name.c_str());
00492     cfg_host_transfer.set_attr("value", "1");
00493     std::string message = owner_name + " has been chosen as the new host.";
00494     if (!wesnothd::send_to_one(cfg, owner_)) {
00495         message += " But an internal error occurred. You probably have to abandon this game.";
00496     }
00497     send_and_record_server_message(message);
00498 }
00499 
00500 bool game::describe_slots() {
00501     if(started_ || description_ == NULL)
00502         return false;
00503 
00504     int available_slots = 0;
00505     int num_sides = level_.root().children("side").size();
00506     int i = 0;
00507     const simple_wml::node::child_list& side_list = level_.root().children("side");
00508     for(simple_wml::node::child_list::const_iterator it = side_list.begin(); it != side_list.end(); ++it, ++i) {
00509         if (((**it)["allow_player"].to_bool(true) == false) || (**it)["controller"] == "null") {
00510             num_sides--;
00511         } else {
00512             if (sides_[i] == 0) ++available_slots;
00513         }
00514     }
00515     char buf[50];
00516     snprintf(buf,sizeof(buf), "%d/%d", available_slots, num_sides);
00517 
00518     if ((*description_)["slots"] != buf) {
00519         description_->set_attr_dup("slots", buf);
00520         return true;
00521     } else {
00522         return false;
00523     }
00524 }
00525 
00526 bool game::player_is_banned(const network::connection sock) const {
00527     std::vector<std::string>::const_iterator ban =
00528         std::find(bans_.begin(), bans_.end(), network::ip_address(sock));
00529     return ban != bans_.end();
00530 }
00531 
00532 void game::mute_all_observers() {
00533     all_observers_muted_ = !all_observers_muted_;
00534     if (all_observers_muted_) {
00535         send_and_record_server_message("All observers have been muted.");
00536     } else {
00537         send_and_record_server_message("Muting of all observers has been removed.");
00538     }
00539 }
00540 
00541 void game::send_muted_observers(const player_map::const_iterator user) const
00542 {
00543     if (all_observers_muted_) {
00544         send_server_message("All observers are muted.", user->first);
00545         return;
00546     }
00547     std::string muted_nicks = list_users(muted_observers_, __func__);
00548 
00549     send_server_message("Muted observers: " + muted_nicks, user->first);
00550 }
00551 
00552 void game::mute_observer(const simple_wml::node& mute,
00553         const player_map::const_iterator muter)
00554 {
00555     if (muter->first != owner_) {
00556         send_server_message("You cannot mute: not the game host.", muter->first);
00557         return;
00558     }
00559     const simple_wml::string_span& username = mute["username"];
00560     if (username.empty()) {
00561         send_muted_observers(muter);
00562         return;
00563     }
00564 
00565     const player_map::const_iterator user = find_user(username);
00566     /**
00567      * @todo FIXME: Maybe rather save muted nicks as a set of strings and
00568      * also allow muting of usernames not in the game.
00569      */
00570     if (user == player_info_->end() || !is_observer(user->first)) {
00571         send_server_message("Observer '" + username.to_string() + "' not found.", muter->first);
00572         return;
00573     }
00574 
00575     // Prevent muting ourselves.
00576     if (user->first == muter->first) {
00577         send_server_message("Don't mute yourself, silly.", muter->first);
00578         return;
00579     }
00580     if (is_muted_observer(user->first)) {
00581         send_server_message(username.to_string() + " is already muted.", muter->first);
00582         return;
00583     }
00584     LOG_GAME << network::ip_address(muter->first) << "\t"
00585         << muter->second.name() << " muted: " << username << " ("
00586         << network::ip_address(user->first) << ")\tin game:\t\""
00587         << name_ << "\" (" << id_ << ")\n";
00588     muted_observers_.push_back(user->first);
00589     send_and_record_server_message(username.to_string() + " has been muted.");
00590 }
00591 
00592 void game::unmute_observer(const simple_wml::node& unmute,
00593         const player_map::const_iterator unmuter)
00594 {
00595     if (unmuter->first != owner_) {
00596         send_server_message("You cannot unmute: not the game host.", unmuter->first);
00597         return;
00598     }
00599     const simple_wml::string_span& username = unmute["username"];
00600     if (username.empty()) {
00601         muted_observers_.clear();
00602         send_and_record_server_message("Everyone has been unmuted.");
00603         return;
00604     }
00605 
00606     const player_map::const_iterator user = find_user(username);
00607     if (user == player_info_->end() || !is_observer(user->first)) {
00608         send_server_message("Observer '" + username.to_string() + "' not found.", unmuter->first);
00609         return;
00610     }
00611 
00612     if (!is_muted_observer(user->first)) {
00613         send_server_message(username.to_string() + " is not muted.", unmuter->first);
00614         return;
00615     }
00616 
00617     LOG_GAME << network::ip_address(unmuter->first) << "\t"
00618         << unmuter->second.name() << " unmuted: " << username << " ("
00619         << network::ip_address(user->first) << ")\tin game:\t\""
00620         << name_ << "\" (" << id_ << ")\n";
00621     muted_observers_.erase(std::remove(muted_observers_.begin(),
00622                 muted_observers_.end(), user->first), muted_observers_.end());
00623     send_and_record_server_message(username.to_string() + " has been unmuted.");
00624 }
00625 
00626 void game::send_leave_game(network::connection user) const
00627 {
00628     static simple_wml::document leave_game("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED);
00629     wesnothd::send_to_one(leave_game, user);
00630 }
00631 
00632 network::connection game::kick_member(const simple_wml::node& kick,
00633         const player_map::const_iterator kicker)
00634 {
00635     if (kicker->first != owner_) {
00636         send_server_message("You cannot kick: not the game host", kicker->first);
00637         return 0;
00638     }
00639     const simple_wml::string_span& username = kick["username"];
00640     const player_map::const_iterator user = find_user(username);
00641     if (user == player_info_->end() || !is_member(user->first)) {
00642         send_server_message("'" + username.to_string() + "' is not a member of this game.", kicker->first);
00643         return 0;
00644     } else if (user->first == kicker->first) {
00645         send_server_message("Don't kick yourself, silly.", kicker->first);
00646         return 0;
00647     } else if (user->second.is_moderator()) {
00648         send_server_message("You're not allowed to kick a moderator.", kicker->first);
00649         return 0;
00650     }
00651     LOG_GAME << network::ip_address(kicker->first) << "\t"
00652         << kicker->second.name() << "\tkicked: " << username << " ("
00653         << network::ip_address(user->first) << ")\tfrom game:\t\""
00654         << name_ << "\" (" << id_ << ")\n";
00655     send_and_record_server_message(username.to_string() + " has been kicked.");
00656 
00657     // Tell the user to leave the game.
00658     send_leave_game(user->first);
00659     remove_player(user->first);
00660     return user->first;
00661 }
00662 
00663 network::connection game::ban_user(const simple_wml::node& ban,
00664         const player_map::const_iterator banner)
00665 {
00666     if (banner->first != owner_) {
00667         send_server_message("You cannot ban: not the game host", banner->first);
00668         return 0;
00669     }
00670     const simple_wml::string_span& username = ban["username"];
00671     const player_map::const_iterator user = find_user(username);
00672     if (user == player_info_->end()) {
00673         send_server_message("User '" + username.to_string() + "' not found.", banner->first);
00674         return 0;
00675     } else if (user->first == banner->first) {
00676         send_server_message("Don't ban yourself, silly.", banner->first);
00677         return 0;
00678     } else if (player_is_banned(user->first)) {
00679         send_server_message("'" + username.to_string() + "' is already banned.", banner->first);
00680         return 0;
00681     } else if (user->second.is_moderator()) {
00682         send_server_message("You're not allowed to ban a moderator.", banner->first);
00683         return 0;
00684     }
00685     LOG_GAME << network::ip_address(banner->first) << "\t"
00686         << banner->second.name() << "\tbanned: " << username << " ("
00687         << network::ip_address(user->first) << ")\tfrom game:\t\""
00688         << name_ << "\" (" << id_ << ")\n";
00689     bans_.push_back(network::ip_address(user->first));
00690     send_and_record_server_message(username.to_string() + " has been banned.");
00691     if (is_member(user->first)) {
00692         //tell the user to leave the game.
00693         send_leave_game(user->first);
00694         remove_player(user->first);
00695         return user->first;
00696     }
00697     // Don't return the user if he wasn't in this game.
00698     return 0;
00699 }
00700 
00701 void game::unban_user(const simple_wml::node& unban,
00702         const player_map::const_iterator unbanner)
00703 {
00704     if (unbanner->first != owner_) {
00705         send_server_message("You cannot unban: not the game host.", unbanner->first);
00706         return;
00707     }
00708     const simple_wml::string_span& username = unban["username"];
00709     const player_map::const_iterator user = find_user(username);
00710     if (user == player_info_->end()) {
00711         send_server_message("User '" + username.to_string() + "' not found.", unbanner->first);
00712         return;
00713     }
00714     if (!player_is_banned(user->first)) {
00715         send_server_message("'" + username.to_string() + "' is not banned.", unbanner->first);
00716         return;
00717     }
00718     LOG_GAME << network::ip_address(unbanner->first) << "\t"
00719         << unbanner->second.name() << "\tunbanned: " << username << " ("
00720         << network::ip_address(user->first) << ")\tfrom game:\t\""
00721         << name_ << "\" (" << id_ << ")\n";
00722     bans_.erase(std::remove(bans_.begin(), bans_.end(), network::ip_address(user->first)), bans_.end());
00723     send_and_record_server_message(username.to_string() + " has been unbanned.");
00724 }
00725 
00726 void game::process_message(simple_wml::document& data, const player_map::iterator user) {
00727     if (owner_ == 0) {
00728         ERR_GAME << "No owner in game::process_message\n";
00729     }
00730 
00731     simple_wml::node* const message = data.root().child("message");
00732     assert(message);
00733     message->set_attr_dup("sender", user->second.name().c_str());
00734 
00735     const simple_wml::string_span& msg = (*message)["message"];
00736     chat_message::truncate_message(msg, *message);
00737 
00738     send_data(data, user->first, "game message");
00739 }
00740 
00741 bool game::is_legal_command(const simple_wml::node& command, bool is_player) {
00742     // Only single commands allowed.
00743     if (!command.one_child()) return false;
00744     // Chatting is never an illegal command.
00745     if (command.child("speak") || command.child("global_variable")) return true;
00746     if (is_player && command.child("global_variable")) {
00747         const simple_wml::node *gvar = (command.child("global_variable"));
00748         if ((*gvar)["side"].to_int() == global_wait_side_) {
00749             global_wait_side_ = 0;
00750             return true;
00751         }
00752         return false;
00753     }
00754     if (is_player
00755     && (command.child("label")
00756         || command.child("clear_labels")
00757         || command.child("rename")
00758         || command.child("countdown_update")
00759         ))
00760     {
00761         return true;
00762     }
00763     return false;
00764 }
00765 
00766 bool game::process_turn(simple_wml::document& data, const player_map::const_iterator user) {
00767     //DBG_GAME << "processing commands: '" << cfg << "'\n";
00768     if (!started_) return false;
00769     simple_wml::node* const turn = data.root().child("turn");
00770     bool turn_ended = false;
00771     // Any private 'speak' commands must be repackaged separate
00772     // to other commands, and re-sent, since they should only go
00773     // to some clients.
00774     bool repackage = false;
00775     int index = 0;
00776     std::vector<int> marked;
00777     const simple_wml::node::child_list& commands = turn->children("command");
00778     simple_wml::node::child_list::const_iterator command;
00779     const bool player = (is_player(user->first) || user->first == owner_);
00780     for (command = commands.begin(); command != commands.end(); ++command) {
00781         if (!is_current_player(user->first)
00782         && !is_legal_command(**command, player)) {
00783             LOG_GAME << "ILLEGAL COMMAND in game: " << id_ << " ((("
00784                 << simple_wml::node_to_string(**command) << ")))\n";
00785             std::stringstream msg;
00786             msg << "Removing illegal command '" << (**command).first_child().to_string()
00787                 << "' from: " << user->second.name()
00788                 << ". Current player is: "
00789                 << username(player_info_->find(current_player()))
00790                 << " (" << end_turn_ + 1 << "/" << nsides_ << ").";
00791             LOG_GAME << msg.str() << " (socket: " << current_player()
00792                 << ") (game id: " << id_ << ")\n";
00793             send_and_record_server_message(msg.str());
00794 
00795             marked.push_back(index - marked.size());
00796         } else if ((**command).child("speak")) {
00797             simple_wml::node& speak = *(**command).child("speak");
00798             if (speak["team_name"] != "" || is_muted_observer(user->first)) {
00799                 DBG_GAME << "repackaging..." << std::endl;
00800                 repackage = true;
00801             }
00802 
00803             const simple_wml::string_span& msg = speak["message"];
00804             chat_message::truncate_message(msg, speak);
00805 
00806             // Force the description to be correct,
00807             // to prevent spoofing of messages.
00808             speak.set_attr_dup("id", user->second.name().c_str());
00809             // Also check the side for players.
00810             if (is_player(user->first)) {
00811                 const size_t side_num = speak["side"].to_int();
00812                 if (side_num < 1 || side_num > gamemap::MAX_PLAYERS
00813                 || sides_[side_num - 1] != user->first) {
00814                     if (user->first == current_player()) {
00815                         speak.set_attr_dup("side", lexical_cast<std::string>(current_side() + 1).c_str());
00816                     } else {
00817                         const side_vector::const_iterator s =
00818                                 std::find(sides_.begin(), sides_.end(), user->first);
00819                         speak.set_attr_dup("side", lexical_cast<std::string>(s - sides_.begin() + 1).c_str());
00820                     }
00821                 }
00822             }
00823         } else if (is_current_player(user->first) && (**command).child("end_turn")) {
00824             turn_ended = end_turn();
00825         }
00826         ++index;
00827     }
00828     for(std::vector<int>::const_iterator j = marked.begin(); j != marked.end(); ++j) {
00829         turn->remove_child("command",*j);
00830     }
00831     for (command = commands.begin(); command != commands.end(); ++command) {
00832         if (simple_wml::node* attack = (**command).child("attack")) {
00833             int seed = rand() & 0x7FFFFFFF;
00834             attack->set_attr_int("seed", seed);
00835             simple_wml::document doc;
00836             simple_wml::node& rs = doc.root().add_child("random_seed");
00837             rs.set_attr_int("seed", seed);
00838             wesnothd::send_to_one(doc, user->first, "game replay");
00839         }
00840     }
00841     if (turn->no_children()) {
00842         return false;
00843     }
00844     if (!repackage) {
00845         record_data(data.clone());
00846         send_data(data, user->first, "game replay");
00847         return turn_ended;
00848     }
00849     for (command = commands.begin(); command != commands.end(); ++command) {
00850         simple_wml::node* const speak = (**command).child("speak");
00851         if (speak == NULL) {
00852             simple_wml::document* mdata = new simple_wml::document;
00853             simple_wml::node& turn = mdata->root().add_child("turn");
00854             (**command).copy_into(turn.add_child("command"));
00855             send_data(*mdata, user->first, "game replay");
00856             record_data(mdata);
00857             continue;
00858         }
00859         const simple_wml::string_span& team_name = (*speak)["team_name"];
00860         // Anyone can send to the observer team.
00861         if (team_name == game_config::observer_team_name.c_str()) {
00862         // Don't send if the member is muted.
00863         } else if (is_muted_observer(user->first)) {
00864             send_server_message("You have been muted, others can't see your message!", user->first);
00865             continue;
00866         // Don't send if the player addresses a different team.
00867         } else if (!is_on_team(team_name, user->first)) {
00868             std::ostringstream msg;
00869             msg << "Removing illegal message from " << user->second.name() << " to " << std::string(team_name.begin(), team_name.end()) << ".";
00870             const std::string& msg_str = msg.str();
00871             LOG_GAME << msg_str << std::endl;
00872             send_and_record_server_message(msg_str);
00873             continue;
00874         }
00875 
00876         std::auto_ptr<simple_wml::document> message(new simple_wml::document);
00877         simple_wml::node& turn = message->root().add_child("turn");
00878         simple_wml::node& command = turn.add_child("command");
00879         speak->copy_into(command.add_child("speak"));
00880         if (team_name == "") {
00881             send_data(*message, user->first, "game message");
00882             record_data(message.release());
00883         } else if (team_name == game_config::observer_team_name) {
00884             wesnothd::send_to_many(*message, observers_, user->first, "game message");
00885             record_data(message.release());
00886         } else {
00887             send_data_team(*message, team_name, user->first, "game message");
00888         }
00889     }
00890     return turn_ended;
00891 }
00892 
00893 void game::process_whiteboard(simple_wml::document& data, const player_map::const_iterator user)
00894 {
00895     if(!started_ || !is_player(user->first))
00896         return;
00897 
00898     simple_wml::node const& wb_node = *data.child("whiteboard");
00899 
00900     // Ensure "side" and "team_name" attributes match with user
00901     simple_wml::string_span const& team_name = wb_node["team_name"];
00902     size_t const side_num = wb_node["side"].to_int();
00903     if(!is_on_team(team_name,user->first)
00904             || side_num < 1
00905             || side_num > gamemap::MAX_PLAYERS
00906             || sides_[side_num-1] != user->first)
00907     {
00908         std::ostringstream msg;
00909         msg << "Ignoring illegal whiteboard data, sent from user '" << user->second.name()
00910                 << "' to team '" << std::string(team_name.begin(), team_name.end()) << "'." << std::endl;
00911         const std::string& msg_str = msg.str();
00912         LOG_GAME << msg_str << std::endl;
00913         send_and_record_server_message(msg_str);
00914         return;
00915     }
00916 
00917     send_data_team(data,team_name,user->first,"whiteboard");
00918 }
00919 
00920 bool game::end_turn() {
00921     // It's a new turn every time each side in the game ends their turn.
00922     ++end_turn_;
00923     bool turn_ended = false;
00924     if ((current_side()) == 0) {
00925         turn_ended = true;
00926     }
00927     // Skip over empty sides.
00928     for (int i = 0; i < nsides_ && nsides_ <= gamemap::MAX_PLAYERS && side_controllers_[current_side()] == "null"; ++i) {
00929         ++end_turn_;
00930         if (current_side() == 0) {
00931             turn_ended = true;
00932         }
00933     }
00934     if (!turn_ended) return false;
00935 
00936     if (description_ == NULL) {
00937         return false;
00938     }
00939 
00940     description_->set_attr_dup("turn", describe_turns(current_turn(), level_["turns"]).c_str());
00941 
00942     return true;
00943 }
00944 
00945 ///@todo differentiate between "observers not allowed" and "player already in the game" errors.
00946 //      maybe return a string with an error message.
00947 bool game::add_player(const network::connection player, bool observer) {
00948     if(is_member(player)) {
00949         ERR_GAME << "ERROR: Player is already in this game. (socket: "
00950             << player << ")\n";
00951         return false;
00952     }
00953     const player_map::iterator user = player_info_->find(player);
00954     if (user == player_info_->end()) {
00955         missing_user(player, __func__);
00956         return false;
00957     }
00958     DBG_GAME << debug_player_info();
00959     bool became_observer = false;
00960     if (!started_ && !observer && take_side(user)) {
00961         DBG_GAME << "adding player...\n";
00962         players_.push_back(player);
00963         user->second.set_status(player::PLAYING);
00964         send_and_record_server_message(user->second.name() + " has joined the game.", player);
00965     } else if (!allow_observers() && !user->second.is_moderator()) {
00966         return false;
00967     } else {
00968         if (!observer) {
00969             became_observer = true;
00970             observer = true;
00971         }
00972         DBG_GAME << "adding observer...\n";
00973         observers_.push_back(player);
00974         if (!allow_observers()) send_and_record_server_message(user->second.name() + " is now observing the game.", player);
00975 
00976         simple_wml::document observer_join;
00977         observer_join.root().add_child("observer").set_attr_dup("name", user->second.name().c_str());
00978 
00979         // Send observer join to everyone except the new observer.
00980         send_data(observer_join, player);
00981     }
00982     LOG_GAME << network::ip_address(player) << "\t" << user->second.name()
00983         << "\tjoined game:\t\"" << name_ << "\" (" << id_ << ")"
00984         << (observer ? " as an observer" : "")
00985         << ". (socket: " << player << ")\n";
00986     user->second.mark_available(id_, name_);
00987     user->second.set_status((observer) ? player::OBSERVING : player::PLAYING);
00988     DBG_GAME << debug_player_info();
00989     // Send the user the game data.
00990     if (!wesnothd::send_to_one(level_, player)) return false;
00991 
00992     if(started_) {
00993         //tell this player that the game has started
00994         static simple_wml::document start_game_doc("[start_game]\n[/start_game]\n", simple_wml::INIT_COMPRESSED);
00995         if (!wesnothd::send_to_one(start_game_doc, player)) return false;
00996         // Send observer join of all the observers in the game to the new player
00997         // only once the game started. The client forgets about it anyway
00998         // otherwise.
00999         send_observerjoins(player);
01000         // Send the player the history of the game to-date.
01001         send_history(player);
01002     } else {
01003         send_user_list();
01004     }
01005 
01006     const std::string clones = has_same_ip(player, observer);
01007     if (!clones.empty()) {
01008         send_and_record_server_message(user->second.name() + " has the same IP as: " + clones);
01009     }
01010 
01011     if (became_observer) {
01012         // in case someone took the last slot right before this player
01013         send_server_message("You are an observer.", player);
01014     }
01015     return true;
01016 }
01017 
01018 bool game::remove_player(const network::connection player, const bool disconnect, const bool destruct) {
01019     if (!is_member(player)) {
01020         ERR_GAME << "ERROR: User is not in this game. (socket: "
01021             << player << ")\n";
01022         return false;
01023     }
01024     DBG_GAME << debug_player_info();
01025     DBG_GAME << "removing player...\n";
01026 
01027     const bool host = (player == owner_);
01028     const bool observer = is_observer(player);
01029     players_.erase(std::remove(players_.begin(), players_.end(), player), players_.end());
01030     observers_.erase(std::remove(observers_.begin(), observers_.end(), player), observers_.end());
01031     const bool game_ended = players_.empty() || (host && !started_);
01032     const player_map::iterator user = player_info_->find(player);
01033     if (user == player_info_->end()) {
01034         missing_user(player, __func__);
01035         return game_ended;
01036     }
01037     LOG_GAME << network::ip_address(user->first) << "\t" << user->second.name()
01038         << ((game_ended && !(observer && destruct))
01039             ? (started_ ? "\tended" : "\taborted") : "\thas left")
01040         << " game:\t\"" << name_ << "\" (" << id_ << ")"
01041         << (game_ended && started_ && !(observer && destruct) ? " at turn: "
01042             + lexical_cast_default<std::string,size_t>(current_turn())
01043             + " with reason: '" + termination_reason() + "'" : "")
01044         << (observer ? " as an observer" : "")
01045         << (disconnect ? " and disconnected" : "")
01046         << ". (socket: " << user->first << ")\n";
01047     if (game_ended && started_ && !(observer && destruct)) {
01048         send_server_message_to_all(user->second.name() + " ended the game.", player);
01049     }
01050     if (game_ended || destruct) return game_ended;
01051 
01052     // Don't mark_available() since the player got already removed from the
01053     // games_and_users_list_.
01054     if (!disconnect) {
01055         user->second.mark_available();
01056     }
01057     if (observer) {
01058         send_observerquit(user);
01059     } else {
01060         send_and_record_server_message(user->second.name()
01061                 + (disconnect ? " has disconnected." : " has left the game."), player);
01062     }
01063     // If the player was host choose a new one.
01064     if (host) {
01065         owner_ = players_.front();
01066         notify_new_host();
01067     }
01068 
01069     bool ai_transfer = false;
01070     // Look for all sides the player controlled and drop them.
01071     // (Give them to the host.)
01072     for (side_vector::iterator side = sides_.begin(); side != sides_.end(); ++side) {
01073         size_t side_num = side - sides_.begin();
01074         if (*side != player) continue;
01075         if (side_controllers_[side_num] == "ai") ai_transfer = true;
01076 
01077         player_map::iterator o = player_info_->find(owner_);
01078         change_controller(side_num, owner_, username(o));
01079         // Check whether the host is actually a player and make him one if not.
01080         if (!is_player(owner_)) {
01081             DBG_GAME << "making the owner a player...\n";
01082             o->second.set_status(player::PLAYING);
01083             observers_.erase(std::remove(observers_.begin(), observers_.end(), owner_), observers_.end());
01084             players_.push_back(owner_);
01085             send_observerquit(o);
01086         }
01087 
01088         //send the host a notification of removal of this side
01089         const std::string side_drop = lexical_cast<std::string, size_t>(side_num + 1);
01090         simple_wml::document drop;
01091         drop.root().set_attr("side_drop", side_drop.c_str());
01092         drop.root().set_attr("controller", side_controllers_[side_num].c_str());
01093 
01094         wesnothd::send_to_one(drop, owner_);
01095     }
01096     if (ai_transfer) send_and_record_server_message("AI sides transferred to host.");
01097 
01098     DBG_GAME << debug_player_info();
01099 
01100     send_user_list(player);
01101     return false;
01102 }
01103 
01104 void game::send_user_list(const network::connection exclude) const {
01105     //if the game hasn't started yet, then send all players a list
01106     //of the users in the game
01107     if (started_ || description_ == NULL) return;
01108     /** @todo Should be renamed to userlist. */
01109     simple_wml::document cfg;
01110     cfg.root().add_child("gamelist");
01111     user_vector users = all_game_users();
01112     for(user_vector::const_iterator p = users.begin(); p != users.end(); ++p) {
01113         const player_map::const_iterator pl = player_info_->find(*p);
01114         if (pl != player_info_->end()) {
01115             //don't need to duplicate pl->second.name().c_str() because the
01116             //document will be destroyed by the end of the function
01117             cfg.root().add_child("user").set_attr("name", pl->second.name().c_str());
01118         }
01119     }
01120     send_data(cfg, exclude);
01121 }
01122 
01123 void game::load_next_scenario(const player_map::const_iterator user) const {
01124     send_server_message_to_all(user->second.name() + " advances to the next scenario", user->first);
01125     simple_wml::document cfg_scenario;
01126     level_.root().copy_into(cfg_scenario.root().add_child("next_scenario"));
01127     if (!wesnothd::send_to_one(cfg_scenario, user->first)) return;
01128     // Send the player the history of the game to-date.
01129     send_history(user->first);
01130     // Send observer join of all the observers in the game to the user.
01131     send_observerjoins(user->first);
01132 }
01133 
01134 void game::send_data(simple_wml::document& data,
01135                           const network::connection exclude,
01136                           std::string packet_type) const
01137 {
01138     wesnothd::send_to_many(data, all_game_users(), exclude, packet_type);
01139 }
01140 
01141 void game::send_data_team(simple_wml::document& data,
01142                           const simple_wml::string_span& team,
01143                           const network::connection exclude,
01144                           std::string packet_type) const
01145 {
01146     DBG_GAME << __func__ << "...\n";
01147     wesnothd::send_to_many(data, players_,
01148         boost::bind(&game::is_on_team, this, boost::ref(team), _1),
01149         exclude, packet_type);
01150 }
01151 
01152 
01153 bool game::is_on_team(const simple_wml::string_span& team, const network::connection player) const {
01154     const simple_wml::node::child_list& side_list = level_.root().children("side");
01155     for (side_vector::const_iterator side = sides_.begin(); side != sides_.end(); ++side) {
01156         if (*side != player) continue;
01157         for (simple_wml::node::child_list::const_iterator i = side_list.begin();
01158                 i != side_list.end(); ++i) {
01159             if ((**i)["side"].to_int() != side - sides_.begin() + 1) continue;
01160             if ((**i)["team_name"] != team) continue;
01161             // Don't consider ai sides on a team.
01162             if ((**i)["controller"] == "ai") continue;
01163             if (side_controllers_[side - sides_.begin()] == "ai") continue;
01164             DBG_GAME << "side: " << (**i)["side"].to_int() << " with team_name: " << (**i)["team_name"]
01165             << " belongs to player: " << player << std::endl;
01166             return true;
01167         }
01168     }
01169 
01170     return false;
01171 }
01172 
01173 std::string game::has_same_ip(const network::connection& user, bool observer) const {
01174     const user_vector users = observer ? players_ : all_game_users();
01175     const std::string ip = network::ip_address(user);
01176     std::string clones;
01177     for (user_vector::const_iterator i = users.begin(); i != users.end(); ++i) {
01178         if (ip == network::ip_address(*i) && user != *i) {
01179             const player_map::const_iterator pl = player_info_->find(*i);
01180             if (pl != player_info_->end()) {
01181                 clones += (clones.empty() ? "" : ", ") + pl->second.name();
01182             }
01183         }
01184     }
01185     return clones;
01186 }
01187 
01188 void game::send_observerjoins(const network::connection sock) const {
01189     for (user_vector::const_iterator ob = observers_.begin(); ob != observers_.end(); ++ob) {
01190         if (*ob == sock) continue;
01191         const player_map::const_iterator obs = player_info_->find(*ob);
01192         if (obs == player_info_->end()) {
01193             missing_user(*ob, __func__);
01194             continue;
01195         }
01196 
01197         simple_wml::document cfg;
01198         cfg.root().add_child("observer").set_attr_dup("name", obs->second.name().c_str());
01199         if (sock == 0) {
01200             // Send to everyone except the observer in question.
01201             send_data(cfg, *ob);
01202         } else {
01203             // Send to the (new) user.
01204             wesnothd::send_to_one(cfg, sock);
01205         }
01206     }
01207 }
01208 
01209 void game::send_observerquit(const player_map::const_iterator observer) const {
01210     if (observer == player_info_->end()) {
01211         return;
01212     }
01213     simple_wml::document observer_quit;
01214 
01215     //don't need to dup the attribute because this document is
01216     //short-lived.
01217     observer_quit.root().add_child("observer_quit").set_attr("name", observer->second.name().c_str());
01218     send_data(observer_quit, observer->first);
01219 }
01220 
01221 void game::send_history(const network::connection sock) const
01222 {
01223     if(history_.empty()) {
01224         return;
01225     }
01226 
01227     //we make a new document based on converting to plain text and
01228     //concatenating the buffers.
01229     //TODO: Work out how to concentate buffers without decompressing.
01230     std::string buf;
01231     for(std::vector<simple_wml::document*>::iterator i = history_.begin();
01232         i != history_.end(); ++i) {
01233         buf += (*i)->output();
01234         delete *i;
01235     }
01236 
01237     try {
01238         simple_wml::document* doc = new simple_wml::document(buf.c_str(), simple_wml::INIT_STATIC);
01239         const simple_wml::string_span& data = doc->output_compressed();
01240         doc->compress();
01241         network::send_raw_data(data.begin(), data.size(), sock,"game_history");
01242         history_.clear();
01243         history_.push_back(doc);
01244     } catch (simple_wml::error& e) {
01245         WRN_CONFIG << __func__ << ": simple_wml error: " << e.message << std::endl;
01246     }
01247 
01248 }
01249 
01250 static bool is_invalid_filename_char(char c) {
01251     return !(isalnum(c) || (c == '_') || (c == '-') || (c == '.')
01252             || (c == '(') || (c == ')') || (c == '#') || (c == ',')
01253             || (c == '!') || (c == '?') || (c == '^') || (c == '+')
01254             || (c == '*') || (c == ':') || (c == '=') || (c == '@')
01255             || (c == '%') || (c == '\''));
01256 }
01257 
01258 void game::save_replay() {
01259     if (!save_replays_ || !started_ || history_.empty()) return;
01260 
01261     std::string replay_commands;
01262     for(std::vector<simple_wml::document*>::iterator i = history_.begin();
01263             i != history_.end(); ++i) {
01264         const simple_wml::node::child_list& turn_list = (*i)->root().children("turn");
01265         for (simple_wml::node::child_list::const_iterator turn = turn_list.begin();
01266                 turn != turn_list.end(); ++turn) {
01267             replay_commands += simple_wml::node_to_string(**turn);
01268         }
01269         delete *i;
01270     }
01271     history_.clear();
01272 
01273     std::stringstream name;
01274     name << level_["name"] << " Turn " << current_turn();
01275 
01276     std::stringstream replay_data;
01277     try {
01278         replay_data << "campaign_type=\"multiplayer\"\n"
01279         << "difficulty=\"NORMAL\"\n"
01280         << "label=\"" << name.str() << "\"\n"
01281         << "mp_game_title=\"" << name_ << "\"\n"
01282         << "random_seed=\"" << level_["random_seed"] << "\"\n"
01283         << "version=\"" << level_["version"] << "\"\n"
01284         << "[replay]\n" << replay_commands << "[/replay]\n"
01285         << "[replay_start]\n" << level_.output() << "[/replay_start]\n";
01286 
01287         name << " (" << id_ << ").gz";
01288 
01289         std::string replay_data_str = replay_data.str();
01290         simple_wml::document replay(replay_data_str.c_str(), simple_wml::INIT_STATIC);
01291 
01292         std::string filename(name.str());
01293         std::replace(filename.begin(), filename.end(), ' ', '_');
01294         filename.erase(std::remove_if(filename.begin(), filename.end(), is_invalid_filename_char), filename.end());
01295         DBG_GAME << "saving replay: " << filename << std::endl;
01296         scoped_ostream os(ostream_file(replay_save_path_ + filename));
01297         (*os) << replay.output_compressed();
01298 
01299         if (!os->good()) {
01300             ERR_GAME << "Could not save replay! (" << filename << ")\n";
01301         }
01302     } catch (simple_wml::error& e) {
01303         WRN_CONFIG << __func__ << ": simple_wml error: " << e.message << std::endl;
01304     }
01305 }
01306 
01307 void game::record_data(simple_wml::document* data) {
01308     data->compress();
01309     history_.push_back(data);
01310 }
01311 
01312 void game::clear_history() {
01313     if (history_.empty()) return;
01314     for(std::vector<simple_wml::document*>::iterator i = history_.begin(); i != history_.end(); ++i) {
01315         delete *i;
01316     }
01317     history_.clear();
01318 }
01319 
01320 void game::set_description(simple_wml::node* desc) {
01321     description_ = desc;
01322     if(!password_.empty()) {
01323         description_->set_attr("password", "yes");
01324     }
01325 }
01326 
01327 void game::set_termination_reason(const std::string& reason) {
01328 /*  if (reason == "out of sync") {
01329         simple_wml::string_span era;
01330         if (level_.child("era")) {
01331             era = level_.child("era")->attr("id");
01332         }
01333         termination_ = "out of sync - " + era.to_string();
01334     }*/
01335     if (termination_.empty()) { termination_ = reason; }
01336 }
01337 
01338 void game::allow_global(const simple_wml::document &data) {
01339     const simple_wml::node *cfg = data.root().child("wait_global");
01340     int side = (*cfg)["side"].to_int();
01341     if ((side < 0) || (side > nsides_)) side = 0;
01342     global_wait_side_ = side;
01343 }
01344 
01345 const user_vector game::all_game_users() const {
01346     user_vector res;
01347 
01348     res.insert(res.end(), players_.begin(), players_.end());
01349     res.insert(res.end(), observers_.begin(), observers_.end());
01350 
01351     return res;
01352 }
01353 
01354 std::string game::debug_player_info() const {
01355     std::stringstream result;
01356     result << "game id: " << id_ << "\n";
01357 //  result << "players_.size: " << players_.size() << "\n";
01358     for (user_vector::const_iterator p = players_.begin(); p != players_.end(); ++p){
01359         const player_map::const_iterator user = player_info_->find(*p);
01360         if (user != player_info_->end()){
01361             result << "player: " << user->second.name().c_str() << "\n";
01362         }
01363         else{
01364             result << "player: '" << *p << "' not found\n";
01365         }
01366     }
01367 //  result << "observers_.size: " << observers_.size() << "\n";
01368     for (user_vector::const_iterator o = observers_.begin(); o != observers_.end(); ++o){
01369         const player_map::const_iterator user = player_info_->find(*o);
01370         if (user != player_info_->end()){
01371             result << "observer: " << user->second.name().c_str() << "\n";
01372         }
01373         else{
01374             result << "observer: '" << *o << "' not found\n";
01375         }
01376     }
01377 /*  result << "player_info_: begin\n";
01378     for (player_map::const_iterator info = player_info_->begin(); info != player_info_->end(); info++){
01379         result << info->second.name().c_str() << "\n";
01380     }
01381     result << "player_info_: end\n";*/
01382     return result.str();
01383 }
01384 
01385 player_map::iterator game::find_user(const simple_wml::string_span& name)
01386 {
01387     player_map::iterator pl;
01388     for (pl = player_info_->begin(); pl != player_info_->end(); ++pl) {
01389         if (name == pl->second.name().c_str()) {
01390             break;
01391         }
01392     }
01393     return pl;
01394 }
01395 
01396 void game::send_and_record_server_message(const char* message, const network::connection exclude)
01397 {
01398     simple_wml::document* doc = new simple_wml::document;
01399     send_server_message(message, 0, doc);
01400     send_data(*doc, exclude, "message");
01401     if (started_) record_data(doc);
01402 }
01403 
01404 void game::send_server_message_to_all(const char* message, network::connection exclude) const
01405 {
01406     simple_wml::document doc;
01407     send_server_message(message, 0, &doc);
01408     send_data(doc, exclude, "message");
01409 }
01410 
01411 void game::send_server_message(const char* message, network::connection sock, simple_wml::document* docptr) const
01412 {
01413     simple_wml::document docbuf;
01414     if(docptr == NULL) {
01415         docptr = &docbuf;
01416     }
01417 
01418     simple_wml::document& doc = *docptr;
01419     if (started_) {
01420         simple_wml::node& cmd = doc.root().add_child("turn");
01421         simple_wml::node& cfg = cmd.add_child("command");
01422         simple_wml::node& msg = cfg.add_child("speak");
01423         msg.set_attr("id", "server");
01424         msg.set_attr_dup("message", message);
01425     } else {
01426         simple_wml::node& msg = doc.root().add_child("message");
01427         msg.set_attr("sender", "server");
01428         msg.set_attr_dup("message", message);
01429     }
01430 
01431 
01432     if(sock) {
01433         wesnothd::send_to_one(doc, sock, "message");
01434     }
01435 }
01436 } // namespace wesnothd
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:02:51 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs