multiplayer_connect.cpp

Go to the documentation of this file.
00001 /* $Id: multiplayer_connect.cpp 54111 2012-05-06 21:53:09Z anonymissimus $ */
00002 /*
00003    Copyright (C) 2007 - 2012
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 /**
00017  * @file
00018  * Prepare to join a multiplayer-game.
00019  */
00020 
00021 #include "global.hpp"
00022 
00023 
00024 #include "ai/configuration.hpp"
00025 #include "dialogs.hpp"
00026 #include "foreach.hpp"
00027 #include "game_display.hpp"
00028 #include "game_preferences.hpp"
00029 #include "gettext.hpp"
00030 #include "log.hpp"
00031 #include "map.hpp"
00032 #include "multiplayer_connect.hpp"
00033 #include "savegame.hpp"
00034 #include "statistics.hpp"
00035 #include "unit_id.hpp"
00036 #include "wml_separators.hpp"
00037 #include "formula_string_utils.hpp"
00038 #include "tod_manager.hpp"
00039 #include "wml_exception.hpp"
00040 
00041 #include <boost/bind.hpp>
00042 
00043 static lg::log_domain log_network("network");
00044 #define LOG_NW LOG_STREAM(info, log_network)
00045 
00046 static lg::log_domain log_config("config");
00047 #define LOG_CF LOG_STREAM(info, log_config)
00048 #define WRN_CF LOG_STREAM(warn, log_config)
00049 #define ERR_CF LOG_STREAM(err, log_config)
00050 
00051 static lg::log_domain log_mp_connect("mp/connect");
00052 #define DBG_MP LOG_STREAM(debug, log_mp_connect)
00053 
00054 namespace {
00055     const char* controller_names[] = {
00056         "network",
00057         "human",
00058         "ai",
00059         "null",
00060         "reserved"
00061     };
00062 }
00063 
00064 namespace mp {
00065 
00066 connect::side::side(connect& parent, const config& cfg, int index) :
00067     parent_(&parent),
00068     cfg_(cfg),
00069     index_(index),
00070     id_(cfg["id"]),
00071     player_id_(cfg["player_id"]),
00072     save_id_(cfg["save_id"]),
00073     current_player_(cfg["current_player"]),
00074     controller_(CNTR_NETWORK),
00075     faction_(cfg["faction"]),
00076     team_(0),
00077     color_(index),
00078     gold_(cfg["gold"].to_int(100)),
00079     income_(cfg["income"]),
00080     leader_(),
00081     gender_(),
00082     ai_algorithm_(),
00083     ready_for_start_(false),
00084     gold_lock_(cfg["gold_lock"].to_bool()),
00085     income_lock_(cfg["income_lock"].to_bool()),
00086     team_lock_(cfg["team_lock"].to_bool()),
00087     color_lock_(cfg["color_lock"].to_bool()),
00088     player_number_(parent.video(), str_cast(index + 1), font::SIZE_LARGE, font::LOBBY_COLOR),
00089     combo_controller_(new gui::combo_drag(parent.disp(), parent.player_types_, parent.combo_control_group_)),
00090     orig_controller_(parent.video(), current_player_, font::SIZE_SMALL),
00091     combo_ai_algorithm_(parent.disp(), std::vector<std::string>()),
00092     combo_faction_(parent.disp(), std::vector<std::string>()),
00093     combo_leader_(parent.disp(), std::vector<std::string>()),
00094     combo_gender_(parent.disp(), std::vector<std::string>()),
00095     combo_team_(parent.disp(), parent.player_teams_),
00096     combo_color_(parent.disp(), parent.player_colors_),
00097     slider_gold_(parent.video()),
00098     slider_income_(parent.video()),
00099     label_gold_(parent.video(), "100", font::SIZE_SMALL, font::LOBBY_COLOR),
00100     label_income_(parent.video(), _("Normal"), font::SIZE_SMALL, font::LOBBY_COLOR),
00101     allow_player_(cfg["allow_player"].to_bool(true)),
00102     allow_changes_(cfg["allow_changes"].to_bool(true)),
00103     enabled_(!parent_->params_.saved_game), changed_(false),
00104     llm_(parent.era_sides_, enabled_ ? &combo_leader_ : NULL, enabled_ ? &combo_gender_ : NULL)
00105 {
00106     DBG_MP << "initializing side" << std::endl;
00107     // convert ai controllers
00108     if (cfg_["controller"] == "human_ai"
00109             || cfg_["controller"] == "network_ai")
00110         cfg_["controller"] = "ai";
00111     if(allow_player_ && enabled_) {
00112         controller_ = parent_->default_controller_;
00113     } else {
00114         size_t i = CNTR_NETWORK;
00115         if(!allow_player_)
00116         {
00117             if(cfg_["controller"] == "null") {
00118                 controller_ = CNTR_EMPTY;
00119             } else {
00120                 cfg_["controller"] = controller_names[CNTR_COMPUTER];
00121                 controller_ = static_cast<mp::controller>(CNTR_COMPUTER);
00122             }
00123         } else {
00124             if (cfg_["controller"] == "network"
00125                     || cfg_["controller"] == "human")
00126             {
00127                 cfg_["controller"] = "reserved";
00128 
00129             }
00130             for(; i != CNTR_LAST; ++i) {
00131                 if(cfg_["controller"] == controller_names[i]) {
00132                     controller_ = static_cast<mp::controller>(i);
00133                     break;
00134                 }
00135             }
00136         }
00137     }
00138 
00139     slider_gold_.set_min(20);
00140     slider_gold_.set_max(800);
00141     slider_gold_.set_increment(25);
00142     slider_gold_.set_value(cfg["gold"].to_int(100));
00143     slider_gold_.set_measurements(80, 16);
00144 
00145     slider_income_.set_min(-2);
00146     slider_income_.set_max(18);
00147     slider_income_.set_increment(1);
00148     slider_income_.set_value(cfg["income"]);
00149     slider_income_.set_measurements(50, 16);
00150 
00151     combo_faction_.enable(enabled_);
00152     combo_leader_.enable(enabled_);
00153     combo_gender_.enable(enabled_);
00154     combo_team_.enable(enabled_);
00155     combo_color_.enable(enabled_);
00156     slider_gold_.hide(!enabled_);
00157     slider_income_.hide(!enabled_);
00158     label_gold_.hide(!enabled_);
00159     label_income_.hide(!enabled_);
00160 
00161     std::vector<std::string>::const_iterator itor =
00162             std::find(parent_->team_names_.begin(), parent_->team_names_.end(),
00163             cfg["team_name"].str());
00164     if(itor == parent_->team_names_.end()) {
00165         assert(!parent_->team_names_.empty());
00166         team_ = 0;
00167     } else {
00168         team_ = itor - parent_->team_names_.begin();
00169     }
00170     if (cfg.has_attribute("color")) {
00171         color_ = game_config::color_info(cfg["color"]).index() - 1;
00172     }
00173     llm_.set_color(color_);
00174 
00175     update_faction_combo();
00176 
00177     if (const config &ai = cfg.child("ai"))
00178         ai_algorithm_ = ai["ai_algorithm"].str();
00179     init_ai_algorithm_combo();
00180 
00181     // "Faction name" hack
00182     if (!enabled_) {
00183         faction_ = 0;
00184         std::vector<std::string> pseudo_factions;
00185         pseudo_factions.push_back(cfg["name"]);
00186         combo_faction_.set_items(pseudo_factions);
00187         combo_faction_.set_selected(0);
00188 
00189         // Hack: if there is a unit which can recruit, use it as a leader.
00190         // Necessary to display leader information when loading saves.
00191         std::string leader_type;
00192         foreach (const config &side_unit, cfg.child_range("unit"))
00193         {
00194             if (side_unit["canrecruit"].to_bool()) {
00195                 leader_type = side_unit["type"].str();
00196                 gender_ = side_unit["gender"].str();
00197                 break;
00198             }
00199         }
00200         std::vector<std::string> leader_name_pseudolist;
00201         if(leader_type.empty()) {
00202             leader_name_pseudolist.push_back(utils::unicode_em_dash);
00203         } else {
00204             const unit_type *leader_name = unit_types.find(leader_type);
00205             if (!leader_name) {
00206                 leader_name_pseudolist.push_back(utils::unicode_em_dash);
00207             } else {
00208                 leader_name_pseudolist.push_back(leader_name->get_gender_unit_type(gender_).type_name());
00209             }
00210         }
00211         combo_leader_.set_items(leader_name_pseudolist);
00212         combo_leader_.set_selected(0);
00213         std::vector<std::string> gender_name_pseudolist;
00214 
00215         if (!gender_.empty()) {
00216             if (leader_type.empty() || !unit_types.find(leader_type)) {
00217                 gender_name_pseudolist.push_back(utils::unicode_em_dash);
00218             } else {
00219                 if (gender_ == "female")
00220                     gender_name_pseudolist.push_back( _("Female ♀") );
00221                 else if (gender_ == "male")
00222                     gender_name_pseudolist.push_back( _("Male ♂") );
00223                 else if (gender_ == "random")
00224                     gender_name_pseudolist.push_back( _("gender^Random") );
00225                 else gender_name_pseudolist.push_back("?");
00226             }
00227         } else {
00228             gender_name_pseudolist.push_back(utils::unicode_em_dash);
00229         }
00230         combo_gender_.set_items(gender_name_pseudolist);
00231         combo_gender_.set_selected(0);
00232     } else if(parent_->params_.use_map_settings) {
00233         // gold, income, team, and color are only suggestions unless explicitly locked
00234         slider_gold_.enable(!gold_lock_);
00235         label_gold_.enable(!gold_lock_);
00236         slider_income_.enable(!income_lock_);
00237         label_income_.enable(!income_lock_);
00238         combo_team_.enable(!team_lock_);
00239         combo_color_.enable(!color_lock_);
00240 
00241         // Set the leader and gender
00242         leader_ = cfg["type"].str();
00243         gender_ = cfg["gender"].str();
00244         if(!leader_.empty()) {
00245             combo_leader_.enable(false);
00246             combo_gender_.enable(false);
00247             llm_.set_leader_combo(NULL);
00248             llm_.set_gender_combo(NULL);
00249             std::vector<std::string> leader_name_pseudolist;
00250             const unit_type *leader_name = unit_types.find(leader_);
00251             if (!leader_name) {
00252                 leader_name_pseudolist.push_back("?");
00253             } else {
00254                 leader_name_pseudolist.push_back(leader_name->type_name());
00255             }
00256             combo_leader_.set_items(leader_name_pseudolist);
00257             combo_leader_.set_selected(0);
00258             std::vector<std::string> gender_name_pseudolist;
00259             if (!gender_.empty()) {
00260                 if (!leader_name) {
00261                     gender_name_pseudolist.push_back("?");
00262                 } else {
00263                     if (gender_ == "female")
00264                         gender_name_pseudolist.push_back( _("Female ♀") );
00265                     else if (gender_ == "male")
00266                         gender_name_pseudolist.push_back( _("Male ♂") );
00267                     else if (gender_ == "random")
00268                         gender_name_pseudolist.push_back( _("gender^Random") );
00269                     else gender_name_pseudolist.push_back("?");
00270                 }
00271             } else gender_name_pseudolist.push_back("?");
00272             combo_gender_.set_items(gender_name_pseudolist);
00273             combo_gender_.set_selected(0);
00274         }
00275 
00276         // Try to pick a faction for the sake of appearance
00277         // and for filling in the blanks
00278         if(faction_ == 0) {
00279             faction_ = find_suitable_faction(parent.era_sides_, cfg);
00280             if (faction_ < 0) faction_ = 0;
00281             if (faction_) {
00282                 llm_.update_leader_list(faction_);
00283                 llm_.update_gender_list(llm_.get_leader());
00284                 combo_faction_.enable(false);
00285             }
00286         } else {
00287             combo_faction_.enable(false);
00288         }
00289     }
00290 
00291     update_ui();
00292 }
00293 
00294 connect::side::side(const side& a) :
00295     parent_(a.parent_), cfg_(a.cfg_),
00296     index_(a.index_), id_(a.id_), player_id_(a.player_id_),  save_id_(a.save_id_),
00297     current_player_(a.current_player_),
00298     controller_(a.controller_),
00299     faction_(a.faction_), team_(a.team_), color_(a.color_),
00300     gold_(a.gold_), income_(a.income_), leader_(a.leader_),
00301     gender_(a.gender_),
00302     ai_algorithm_(a.ai_algorithm_),
00303     ready_for_start_(a.ready_for_start_),
00304     gold_lock_(a.gold_lock_),
00305     income_lock_(a.income_lock_),
00306     team_lock_(a.team_lock_),
00307     color_lock_(a.color_lock_),
00308     player_number_(a.player_number_), combo_controller_(a.combo_controller_),
00309     orig_controller_(a.orig_controller_),
00310     combo_ai_algorithm_(a.combo_ai_algorithm_),
00311     combo_faction_(a.combo_faction_), combo_leader_(a.combo_leader_), combo_gender_(a.combo_gender_),
00312     combo_team_(a.combo_team_), combo_color_(a.combo_color_),
00313     slider_gold_(a.slider_gold_), slider_income_(a.slider_income_),
00314     label_gold_(a.label_gold_), label_income_(a.label_income_),
00315     allow_player_(a.allow_player_), allow_changes_(a.allow_changes_),
00316     enabled_(a.enabled_), changed_(a.changed_), llm_(a.llm_)
00317 {
00318     llm_.set_color(color_);
00319     llm_.set_leader_combo((enabled_ && leader_.empty()) ? &combo_leader_ : NULL);
00320     llm_.set_gender_combo((enabled_ && leader_.empty()) ? &combo_gender_ : NULL);
00321     // FIXME: this is an ugly hack to force updating the gender list when the side
00322     // widget is initialized. Need an optimal way. -- shadowmaster
00323     llm_.update_gender_list(llm_.get_leader());
00324 }
00325 
00326 void connect::side::add_widgets_to_scrollpane(gui::scrollpane& pane, int pos)
00327 {
00328     pane.add_widget(&player_number_,     0, 5 + pos);
00329     pane.add_widget(combo_controller_.get(), 20, 5 + pos);
00330     pane.add_widget(&orig_controller_,  20 + (combo_controller_->width() - orig_controller_.width()) / 2,
00331                                         35 + pos + (combo_leader_.height() - orig_controller_.height()) / 2);
00332     pane.add_widget(&combo_ai_algorithm_, 20, 35 + pos);
00333     pane.add_widget(&combo_faction_, 135, 5 + pos);
00334     pane.add_widget(&combo_leader_,  135, 35 + pos);
00335     pane.add_widget(&combo_gender_,  250, 35 + pos);
00336     pane.add_widget(&combo_team_,    250, 5 + pos);
00337     pane.add_widget(&combo_color_,  365, 5 + pos);
00338     pane.add_widget(&slider_gold_,   475, 5 + pos);
00339     pane.add_widget(&label_gold_,    475, 35 + pos);
00340     pane.add_widget(&slider_income_, 475 + slider_gold_.width(),  5 + pos);
00341     pane.add_widget(&label_income_,  475 + slider_gold_.width(), 35 + pos);
00342 }
00343 
00344 void connect::side::process_event()
00345 {
00346     int drop_target;
00347     if ( ( drop_target = combo_controller_->get_drop_target() )> -1)
00348     {
00349         const std::string target_id = parent_->sides_[drop_target].get_player_id();
00350         const mp::controller target_controller = parent_->sides_[drop_target].get_controller();
00351         const std::string target_ai = parent_->sides_[drop_target].ai_algorithm_;
00352 
00353         parent_->sides_[drop_target].ai_algorithm_ = ai_algorithm_;
00354         if (player_id_.empty())
00355         {
00356             parent_->sides_[drop_target].set_controller(controller_);
00357         } else {
00358             parent_->sides_[drop_target].set_player_id(player_id_);
00359         }
00360 
00361         ai_algorithm_ = target_ai;
00362         if (target_id.empty())
00363         {
00364             set_controller(target_controller);
00365         } else {
00366             set_player_id(target_id);
00367         }
00368         changed_ = true;
00369         parent_->sides_[drop_target].changed_ = true;
00370         init_ai_algorithm_combo();
00371         update_ui();
00372         parent_->sides_[drop_target].init_ai_algorithm_combo();
00373         parent_->sides_[drop_target].update_ui();
00374     }
00375     else if(combo_controller_->changed() && combo_controller_->selected() >= 0) {
00376         const int cntr_last = (save_id_.empty() ? CNTR_LAST-1 : CNTR_LAST) - (parent_->local_only_ ? 1 : 0);
00377         if (combo_controller_->selected() == cntr_last) {
00378             update_controller_ui();
00379         } else if (combo_controller_->selected() < cntr_last) {
00380             // Correct entry number if CNTR_NETWORK is not allowed for combo_controller_
00381             controller_ = mp::controller(combo_controller_->selected() + (parent_->local_only_ ? 1 : 0));
00382             player_id_ = "";
00383             ready_for_start_ = false;
00384             changed_ = true;
00385         } else {
00386             // give user second side
00387             size_t user = combo_controller_->selected() - cntr_last - 1;
00388 
00389             const std::string new_id = parent_->users_[user].name;
00390             if (new_id != player_id_) {
00391                 player_id_ = new_id;
00392                 controller_ = parent_->users_[user].controller;
00393                 ready_for_start_ = true;
00394                 changed_ = true;
00395             }
00396         }
00397         update_ai_algorithm_combo();
00398     }
00399 
00400     if (combo_controller_->hidden())
00401         combo_controller_->hide(false);
00402     if(!enabled_)
00403         return;
00404 
00405     if (combo_color_.changed() && combo_color_.selected() >= 0) {
00406         color_ = combo_color_.selected();
00407         llm_.set_color(color_);
00408         update_faction_combo();
00409         llm_.set_leader_combo(&combo_leader_);
00410         llm_.set_gender_combo(&combo_gender_);
00411         changed_ = true;
00412     }
00413     if (combo_faction_.changed() && combo_faction_.selected() >= 0) {
00414         faction_ = combo_faction_.selected();
00415         llm_.update_leader_list(faction_);
00416         llm_.update_gender_list(llm_.get_leader());
00417         changed_ = true;
00418     }
00419     if (combo_ai_algorithm_.changed() && combo_ai_algorithm_.selected() >= 0) {
00420         ai_algorithm_ = parent_->ai_algorithms_[combo_ai_algorithm_.selected()]->id;
00421         changed_ = true;
00422     }
00423     if (combo_leader_.changed() && combo_leader_.selected() >= 0) {
00424         llm_.update_gender_list(llm_.get_leader());
00425         changed_ = true;
00426     }
00427     if (combo_gender_.changed() && combo_gender_.selected() >= 0) {
00428         llm_.set_leader_combo(&combo_leader_);
00429         changed_ = true;
00430     }
00431     if (combo_team_.changed() && combo_team_.selected() >= 0) {
00432         team_ = combo_team_.selected();
00433         changed_ = true;
00434     }
00435     if (slider_gold_.value() != gold_) {
00436         gold_ = slider_gold_.value();
00437         label_gold_.set_text(str_cast(gold_));
00438         changed_ = true;
00439     }
00440     if (slider_income_.value() != income_) {
00441 
00442         income_ = slider_income_.value();
00443         std::stringstream buf;
00444         if(income_ < 0) {
00445             buf << _("(") << income_ << _(")");
00446         } else if(income_ > 0) {
00447             buf << _("+") << income_;
00448         } else {
00449             buf << _("Normal");
00450         }
00451         label_income_.set_text(buf.str());
00452         changed_ = true;
00453     }
00454 }
00455 
00456 bool connect::side::changed()
00457 {
00458     bool res = changed_;
00459     changed_ = false;
00460     return res;
00461 }
00462 
00463 bool connect::side::ready_for_start() const
00464 {
00465     //sides without players are always ready
00466     if (!allow_player_)
00467         return true;
00468 
00469     //the host and the AI are always ready
00470     if ((controller_ == mp::CNTR_COMPUTER) ||
00471         (controller_ == mp::CNTR_EMPTY) ||
00472         (controller_ == mp::CNTR_LOCAL))
00473         return true;
00474 
00475     return ready_for_start_;
00476 }
00477 
00478 bool connect::side::available(const std::string& name) const
00479 {
00480     if (name.empty())
00481     {
00482         return allow_player_
00483             && ((controller_ == CNTR_NETWORK && player_id_.empty())
00484                     || controller_ == CNTR_RESERVED);
00485     }
00486     return allow_player_
00487         && ((controller_ == CNTR_NETWORK && player_id_.empty())
00488             || (controller_ == CNTR_RESERVED && current_player_ == name));
00489 }
00490 
00491 bool connect::side::allow_player() const
00492 {
00493     return allow_player_;
00494 }
00495 
00496 void connect::side::update_controller_ui()
00497 {
00498     if (player_id_.empty()) {
00499         combo_controller_->set_selected(controller_ - (parent_->local_only_ ? 1 : 0));
00500     } else {
00501         connected_user_list::iterator player = parent_->find_player(player_id_);
00502 
00503         if (player != parent_->users_.end()) {
00504             const int no_reserve = save_id_.empty()?-1:0;
00505             combo_controller_->set_selected(CNTR_LAST + no_reserve + 1 + (player - parent_->users_.begin()) - (parent_->local_only_ ? 1 : 0));
00506         } else {
00507             assert(parent_->local_only_ != true);
00508             combo_controller_->set_selected(CNTR_NETWORK);
00509         }
00510     }
00511 
00512     update_ai_algorithm_combo();
00513 }
00514 
00515 void connect::side::hide_ai_algorithm_combo(bool invis)
00516 {
00517     if(!invis)
00518     {
00519         if(controller_ == CNTR_COMPUTER)
00520         {
00521             // Computer selected, show AI combo
00522             orig_controller_.hide(true);
00523             combo_ai_algorithm_.hide(false);
00524         } else {
00525             // Computer de-selected, hide AI combo
00526             combo_ai_algorithm_.hide(true);
00527             orig_controller_.hide(false);
00528         }
00529     } else {
00530         combo_ai_algorithm_.hide(true);
00531     }
00532 }
00533 
00534 void connect::side::init_ai_algorithm_combo()
00535 {
00536     assert(parent_->ai_algorithms_.empty() == false);
00537 
00538     int sel = 0;
00539     std::vector<ai::description*> &ais_list = parent_->ai_algorithms_;
00540     std::vector<std::string> ais;
00541     int i = 0;
00542     foreach (const ai::description *desc,  ais_list){
00543         ais.push_back(desc->text);
00544         if (desc->id==ai_algorithm_){
00545             sel = i;
00546         }
00547         i++;
00548     }
00549     combo_ai_algorithm_.set_items(ais);
00550     combo_ai_algorithm_.set_selected(sel);
00551     if (!ais_list.empty()) {
00552         // Ensures that the visually selected AI is the one that will be loaded.
00553         ai_algorithm_ = ais_list[sel]->id;
00554     }
00555 }
00556 
00557 void connect::side::update_faction_combo()
00558 {
00559     std::vector<std::string> factions;
00560     foreach (const config *faction, parent_->era_sides_)
00561     {
00562         const std::string& name = (*faction)["name"];
00563         const std::string& icon = (*faction)["image"];
00564         if (!icon.empty()) {
00565             std::string rgb = (*faction)["flag_rgb"];
00566             if (rgb.empty())
00567                 rgb = "magenta";
00568 
00569             factions.push_back(IMAGE_PREFIX + icon + "~RC(" + rgb + ">" + lexical_cast<std::string>(color_+1) + ")" + COLUMN_SEPARATOR + name);
00570         } else {
00571             factions.push_back(name);
00572         }
00573     }
00574     combo_faction_.set_items(factions);
00575     combo_faction_.set_selected(faction_);
00576 }
00577 
00578 void connect::side::update_ui()
00579 {
00580     update_controller_ui();
00581 
00582     if (combo_faction_.selected() != faction_ && combo_faction_.selected() >= 0) {
00583         combo_faction_.set_selected(faction_);
00584     }
00585 
00586     combo_team_.set_selected(team_);
00587     combo_color_.set_selected(color_);
00588     slider_gold_.set_value(gold_);
00589     label_gold_.set_text(str_cast(gold_));
00590     slider_income_.set_value(income_);
00591     std::stringstream buf;
00592     if(income_ < 0) {
00593         buf << _("(") << income_ << _(")");
00594     } else if(income_ > 0) {
00595         buf << _("+") << income_;
00596     } else {
00597         buf << _("Normal");
00598     }
00599     label_income_.set_text(buf.str());
00600 }
00601 
00602 config connect::side::get_config() const
00603 {
00604     config res;
00605 
00606     // If the user is allowed to change type, faction, leader etc,
00607     // then import their new values in the config.
00608     if(enabled_ && !parent_->era_sides_.empty()) {
00609         // Merge the faction data to res
00610         res.append(*(parent_->era_sides_[faction_]));
00611         res["faction_name"] = res["name"];
00612     }
00613     res.append(cfg_);
00614     if (!cfg_.has_attribute("side") || cfg_["side"].to_int() != index_ + 1) {
00615         res["side"] = index_ + 1;
00616     }
00617     res["controller"] = controller_names[controller_];
00618     res["current_player"] = player_id_.empty() ? current_player_ : player_id_;
00619     res["id"] = id_;
00620 
00621     if (player_id_.empty()) {
00622         std::string description;
00623         switch(controller_) {
00624         case CNTR_NETWORK:
00625             description = N_("(Vacant slot)");
00626             break;
00627         case CNTR_LOCAL:
00628             if (enabled_ && !cfg_.has_attribute("save_id")) {
00629                 res["save_id"] = preferences::login() + res["side"].str();
00630             }
00631             res["player_id"] = preferences::login() + res["side"].str();
00632             res["current_player"] = preferences::login();
00633             description = N_("Anonymous local player");
00634             break;
00635         case CNTR_COMPUTER:
00636             if (enabled_ && !cfg_.has_attribute("save_id")) {
00637                 res["save_id"] = "ai" + res["side"].str();
00638             }
00639             {
00640                 utils::string_map symbols;
00641                 if (allow_player_) {
00642                     const config &ai_cfg = ai::configuration::get_ai_config_for(ai_algorithm_);
00643                     res.add_child("ai",ai_cfg);
00644                     symbols["playername"] = ai_cfg["description"];
00645                 } else { // do not import default ai cfg here - all is set by scenario config
00646                     symbols["playername"] = _("Computer Player");
00647                 }
00648                 symbols["side"] = res["side"].str();
00649                 description = vgettext("$playername $side",symbols);
00650             }
00651             break;
00652         case CNTR_EMPTY:
00653             description = N_("(Empty slot)");
00654             res["no_leader"] = true;
00655             break;
00656         case CNTR_RESERVED:
00657             {
00658                 utils::string_map symbols;
00659                 symbols["playername"] = current_player_;
00660                 description = vgettext("(Reserved for $playername)",symbols);
00661             }
00662             break;
00663         case CNTR_LAST:
00664         default:
00665             description = N_("(empty)");
00666             assert(false);
00667             break;
00668         }
00669         res["user_description"] = t_string(description, "wesnoth");
00670     } else {
00671         res["player_id"] = player_id_ + res["side"];
00672         if (enabled_ && !cfg_.has_attribute("save_id")) {
00673             res["save_id"] = player_id_ + res["side"];
00674         }
00675 
00676         res["user_description"] = player_id_;
00677     }
00678 
00679     res["name"] = res["user_description"];
00680     res["allow_changes"] = enabled_ && allow_changes_;
00681 
00682     if(enabled_) {
00683         if (leader_.empty()) {
00684             res["type"] = llm_.get_leader();
00685         } else {
00686             res["type"] = leader_;
00687         }
00688         if (gender_.empty()) {
00689             std::string dummy = llm_.get_gender();
00690             if (!dummy.empty() && dummy != "null" && dummy != "?")
00691                 res["gender"] = dummy;
00692         } else {
00693             // If no genders could be resolved, let the unit engine use
00694             // the default gender
00695             if (gender_ != "null")
00696                 res["gender"] = gender_;
00697         }
00698         // res["team"] = lexical_cast<std::string>(team_);
00699         res["team_name"] = parent_->team_names_[team_];
00700         res["user_team_name"] = parent_->user_team_names_[team_];
00701         res["allow_player"] = allow_player_;
00702         res["color"] = color_ + 1;
00703         res["gold"] = gold_;
00704         res["income"] = income_;
00705 
00706         if(!parent_->params_.use_map_settings || res["fog"].empty() || (res["fog"] != "yes" && res["fog"] != "no")) {
00707             res["fog"] = parent_->params_.fog_game;
00708         }
00709 
00710         if(!parent_->params_.use_map_settings || res["shroud"].empty() || (res["shroud"] != "yes" && res["shroud"] != "no")) {
00711             res["shroud"] = parent_->params_.shroud_game;
00712         }
00713 
00714         res["share_maps"] = parent_->params_.share_maps;
00715         res["share_view"] =  parent_->params_.share_view;
00716         if(!parent_->params_.use_map_settings || res["village_gold"].empty())
00717             res["village_gold"] = parent_->params_.village_gold;
00718         if(!parent_->params_.use_map_settings || res["village_support"].empty())
00719             res["village_support"] = lexical_cast<std::string>(parent_->params_.village_support);
00720 
00721     }
00722 
00723     if(parent_->params_.use_map_settings && enabled_) {
00724         config trimmed = cfg_;
00725         static char const *attrs[] = { "side", "controller", "id",
00726             "team_name", "user_team_name", "color", "gold",
00727             "income", "allow_changes" };
00728         foreach (const char *attr, attrs) {
00729             trimmed.remove_attribute(attr);
00730         }
00731 
00732         if(controller_ != CNTR_COMPUTER) {
00733             // Only override names for computer controlled players
00734             trimmed.remove_attribute("user_description");
00735         }
00736         res.merge_with(trimmed);
00737     }
00738     return res;
00739 }
00740 
00741 void connect::side::set_controller(mp::controller controller)
00742 {
00743     controller_ = controller;
00744     player_id_ = "";
00745 
00746     update_ui();
00747 }
00748 
00749 mp::controller connect::side::get_controller() const
00750 {
00751     return controller_;
00752 }
00753 
00754 void connect::side::update_user_list()
00755 {
00756     bool name_present = false;
00757 
00758     typedef std::vector<std::string> name_list;
00759 
00760     name_list list = parent_->player_types_;
00761     if (!save_id_.empty())
00762         list.push_back(_("Reserved"));
00763     list.push_back(_("--give--"));
00764 
00765     connected_user_list::const_iterator itor;
00766     for (itor = parent_->users_.begin(); itor != parent_->users_.end();
00767             ++itor) {
00768         list.push_back(itor->name);
00769         if (itor->name == player_id_)
00770             name_present = true;
00771     }
00772 
00773     if (name_present == false) {
00774         player_id_ = "";
00775     }
00776 
00777     combo_controller_->set_items(list);
00778     update_controller_ui();
00779 }
00780 
00781 int connect::side::get_index()
00782 {
00783     return index_;
00784 }
00785 
00786 void connect::side::set_index(int index)
00787 {
00788     index_ = index;
00789 }
00790 
00791 const std::string& connect::side::get_player_id() const
00792 {
00793     return player_id_;
00794 }
00795 
00796 void connect::side::set_player_id(const std::string& player_id)
00797 {
00798     connected_user_list::iterator i = parent_->find_player(player_id);
00799     if (i != parent_->users_.end()) {
00800         player_id_ = player_id;
00801         controller_ = i->controller;
00802     }
00803     update_ui();
00804 }
00805 
00806 int connect::side::get_team()
00807 {
00808     return team_;
00809 }
00810 
00811 void connect::side::set_team(int team)
00812 {
00813     team_ = team;
00814 }
00815 
00816 std::vector<std::string> connect::side::get_children_to_swap()
00817 {
00818     std::vector<std::string> children;
00819 
00820     children.push_back("village");
00821     children.push_back("unit");
00822     children.push_back("ai");
00823 
00824     return children;
00825 }
00826 
00827 std::map<std::string, config> connect::side::get_side_children()
00828 {
00829     std::map<std::string, config> children;
00830 
00831     foreach(const std::string& children_to_swap, get_children_to_swap())
00832         foreach(const config& child, cfg_.child_range(children_to_swap))
00833             children.insert(std::pair<std::string, config>(children_to_swap, child));
00834 
00835     return children;
00836 }
00837 
00838 void connect::side::set_side_children(std::map<std::string, config> children)
00839 {
00840     foreach(const std::string& children_to_remove, get_children_to_swap())
00841         cfg_.clear_children(children_to_remove);
00842 
00843     std::pair<std::string, config> child_map;
00844 
00845     foreach(child_map, children)
00846         cfg_.add_child(child_map.first, child_map.second);
00847 }
00848 
00849 void connect::side::set_ready_for_start(bool ready_for_start)
00850 {
00851     ready_for_start_ = ready_for_start;
00852 }
00853 
00854 void connect::side::import_network_user(const config& data)
00855 {
00856     if (controller_ == CNTR_RESERVED || !enabled_)
00857         set_ready_for_start(true);
00858 
00859     player_id_ = data["name"].str();
00860     controller_ = CNTR_NETWORK;
00861 
00862     if(enabled_ && !parent_->era_sides_.empty()) {
00863         if(combo_faction_.enabled()) {
00864             faction_ = data["faction"];
00865             if(faction_ > int(parent_->era_sides_.size()))
00866                 faction_ = 0;
00867             llm_.update_leader_list(faction_);
00868             llm_.update_gender_list(llm_.get_leader());
00869         }
00870         if(combo_leader_.enabled()) {
00871             llm_.set_leader(data["leader"]);
00872             // FIXME: not optimal, but this hack is necessary to do after updating
00873             // the leader selection. Otherwise, gender gets always forced to "male".
00874             llm_.update_gender_list(llm_.get_leader());
00875         }
00876         if (combo_gender_.enabled()) {
00877             llm_.set_gender(data["gender"]);
00878         }
00879     }
00880 
00881     update_ui();
00882 }
00883 
00884 void connect::side::reset(mp::controller controller)
00885 {
00886     player_id_ = "";
00887     controller_ = controller;
00888 
00889     if ((controller == mp::CNTR_NETWORK) || (controller == mp::CNTR_RESERVED))
00890         ready_for_start_ = false;
00891 
00892     if(enabled_ && !parent_->era_sides_.empty()) {
00893         if(combo_faction_.enabled())
00894             faction_ = 0;
00895         if(combo_leader_.enabled())
00896             llm_.update_leader_list(faction_);
00897         if (combo_gender_.enabled())
00898             llm_.update_gender_list(llm_.get_leader());
00899     }
00900 
00901     update_ui();
00902 }
00903 
00904 void connect::side::resolve_random()
00905 {
00906     if(!enabled_ || parent_->era_sides_.empty())
00907         return;
00908 
00909     if ((*parent_->era_sides_[faction_])["random_faction"].to_bool())
00910     {
00911         std::vector<std::string> faction_choices, faction_excepts;
00912         faction_choices = utils::split((*parent_->era_sides_[faction_])["choices"]);
00913         if(faction_choices.size() == 1 && faction_choices.front() == "") {
00914             faction_choices.clear();
00915         }
00916         faction_excepts = utils::split((*parent_->era_sides_[faction_])["except"]);
00917         if(faction_excepts.size() == 1 && faction_excepts.front() == "") {
00918             faction_excepts.clear();
00919         }
00920 
00921         // Builds the list of sides eligible for choice (nonrandom factions)
00922         std::vector<int> nonrandom_sides;
00923         int num = -1;
00924         foreach (const config *i, parent_->era_sides_)
00925         {
00926             ++num;
00927             if (!(*i)["random_faction"].to_bool()) {
00928                 const std::string& faction_id = (*i)["id"];
00929                 if (
00930                     !faction_choices.empty() &&
00931                     std::find(faction_choices.begin(),faction_choices.end(),faction_id) == faction_choices.end()
00932                 )
00933                     continue;
00934                 if (
00935                     !faction_excepts.empty() &&
00936                     std::find(faction_excepts.begin(),faction_excepts.end(),faction_id) != faction_excepts.end()
00937                 )
00938                     continue;
00939                 nonrandom_sides.push_back(num);
00940             }
00941         }
00942 
00943         if (nonrandom_sides.empty()) {
00944             throw config::error(_("Only random sides in the current era."));
00945         }
00946 
00947         faction_ = nonrandom_sides[rand() % nonrandom_sides.size()];
00948     }
00949     bool solved_random_leader = false;
00950 
00951     if (llm_.get_leader() == "random") {
00952         // Choose a random leader type, and force gender to be random
00953         llm_.set_gender("random");
00954         const config& fact = *parent_->era_sides_[faction_];
00955         std::vector<std::string> types = utils::split(fact["random_leader"]);
00956         if (!types.empty()) {
00957             const int lchoice = rand() % types.size();
00958             leader_ = types[lchoice];
00959         } else {
00960             // If random_leader= doesn't exists, we use leader=
00961             types = utils::split(fact["leader"]);
00962             if (!types.empty()) {
00963                 const int lchoice = rand() % types.size();
00964                 leader_ = types[lchoice];
00965             } else {
00966                 utils::string_map i18n_symbols;
00967                 i18n_symbols["faction"] = fact["name"];
00968                 throw config::error(vgettext("Unable to find a leader type for faction $faction", i18n_symbols));
00969             }
00970         }
00971         solved_random_leader = true;
00972     }
00973     // Resolve random genders "very much" like standard unit code
00974     if (llm_.get_gender() == "random" || solved_random_leader) {
00975         const unit_type *ut = unit_types.find(leader_.empty() ? llm_.get_leader() : leader_);
00976 
00977         if (ut) {
00978             const std::vector<unit_race::GENDER> glist = ut->genders();
00979             if (!glist.empty()) {
00980                 const int gchoice = rand() % glist.size();
00981                 // Pick up a gender, using the random 'gchoice' index
00982                 unit_race::GENDER sgender = glist[gchoice];
00983                 switch (sgender)
00984                 {
00985                     case unit_race::FEMALE:
00986                         gender_ = "female";
00987                         break;
00988                     case unit_race::MALE:
00989                         gender_ = "male";
00990                         break;
00991                     default:
00992                         gender_ = "null";
00993                 }
00994             } else gender_ = "null";
00995         } else {
00996             ERR_CF << "cannot obtain genders for invalid leader '" << (leader_.empty() ? llm_.get_leader() : leader_) << "'.\n";
00997             gender_ = "null";
00998         }
00999     }
01000 }
01001 
01002 connect::connect(game_display& disp, const config& game_config,
01003         chat& c, config& gamelist, const mp_game_settings& params, const int num_turns,
01004         mp::controller default_controller, bool local_players_only) :
01005     mp::ui(disp, _("Game Lobby: ") + params.name, game_config, c, gamelist),
01006     disp_(&disp),
01007     local_only_(local_players_only),
01008     level_(),
01009     state_(),
01010     params_(params),
01011     num_turns_(num_turns),
01012     era_sides_(),
01013     player_types_(),
01014     player_factions_(),
01015     player_teams_(),
01016     player_colors_(),
01017     ai_algorithms_(),
01018     team_names_(),
01019     user_team_names_(),
01020     team_prefix_(std::string(_("Team")) + " "),
01021     sides_(),
01022     users_(),
01023     waiting_label_(video(), "", font::SIZE_SMALL, font::LOBBY_COLOR),
01024     message_full_(false),
01025     default_controller_(default_controller),
01026     scroll_pane_(video()),
01027     type_title_label_(video(), _("Player/Type"), font::SIZE_SMALL, font::LOBBY_COLOR),
01028     faction_title_label_(video(), _("Faction"), font::SIZE_SMALL, font::LOBBY_COLOR),
01029     team_title_label_(video(), _("Team/Gender"), font::SIZE_SMALL, font::LOBBY_COLOR),
01030     color_title_label_(video(), _("Color"), font::SIZE_SMALL, font::LOBBY_COLOR),
01031     gold_title_label_(video(), _("Gold"), font::SIZE_SMALL, font::LOBBY_COLOR),
01032     income_title_label_(video(), _("Income"), font::SIZE_SMALL, font::LOBBY_COLOR),
01033 
01034     launch_(video(), _("I’m Ready")),
01035     cancel_(video(), _("Cancel")),
01036     add_local_player_(video(), _("Add named local player")),
01037     combo_control_group_(new gui::drop_group_manager())
01038 {
01039     DBG_MP << "setting up connect dialog" << std::endl;
01040     load_game();
01041 
01042     if(get_result() == QUIT
01043         || get_result() == CREATE)
01044         return;
01045     if (level_["id"].empty()) {
01046         throw config::error(_("The scenario is invalid because it has no id."));
01047     }
01048     lists_init();
01049     if(sides_.empty()) {
01050         throw config::error(_("The scenario is invalid because it has no sides."));
01051     }
01052     // Send Initial information
01053     config response;
01054     config& create_game = response.add_child("create_game");
01055     create_game["name"] = params.name;
01056     if(params.password.empty() == false) {
01057         create_game["password"] = params.password;
01058     }
01059 /*
01060     // The number of human-controlled sides is important to know
01061     // to let the server decide how many players can join this game
01062     int human_sides = 0;
01063     config::child_list cfg_sides = current_config()->get_children("side");
01064     for (config::child_list::const_iterator side = cfg_sides.begin(); side != cfg_sides.end(); side++){
01065         if ((**side)["controller"] == "human"){
01066             human_sides++;
01067         }
01068     }
01069     create_game["human_sides"] = lexical_cast<std::string>(human_sides);*/
01070     network::send_data(response, 0);
01071 
01072     // Adds the current user as default user.
01073     users_.push_back(connected_user(preferences::login(), CNTR_LOCAL, 0));
01074     update_user_combos();
01075     // Take the first available side or available side with id == login
01076     int side_choice = -1;
01077     for(side_list::const_iterator s = sides_.begin(); s != sides_.end(); ++s) {
01078         if (s->allow_player()) {
01079             if (side_choice == -1)
01080                 side_choice = s - sides_.begin();
01081             if(s->get_current_player() == preferences::login()) {
01082                 sides_[s - sides_.begin()].set_player_id(preferences::login());
01083                 side_choice = gamemap::MAX_PLAYERS;
01084             }
01085         }
01086     }
01087 
01088     if (side_choice != -1
01089             && side_choice != gamemap::MAX_PLAYERS)
01090     {
01091         if (sides_[side_choice].get_player_id() == "")
01092             sides_[side_choice].set_player_id(preferences::login());
01093     }
01094 
01095     // Updates the "level_" variable, now that sides are loaded
01096     update_level();
01097     update_playerlist_state(true);
01098 
01099     // If we are connected, send data to the connected host
01100     network::send_data(level_, 0);
01101 }
01102 
01103 void connect::process_event()
01104 {
01105     bool changed = false;
01106 
01107     /*
01108      * If the Add Local Player button is pressed, display corresponding dialog box.
01109      * Dialog box is shown again if an already existing player name is entered.
01110      * If the name is valid, add a new user with that name to the list of connected users,
01111      * and refresh the UI.
01112      */
01113     if (add_local_player_.pressed()) {
01114         bool alreadyExists = false;
01115         do {
01116             alreadyExists = false;
01117             gui::dialog d(*disp_, _("Enter a name for the new player"), "", gui::OK_CANCEL);
01118             d.set_textbox(_("Name: "));
01119             d.show();
01120             if(d.result() != gui::CLOSE_DIALOG && !d.textbox_text().empty())
01121             {
01122                 for(connected_user_list::iterator it = users_.begin(); it != users_.end(); ++it) {
01123                     if( (*it).name == d.textbox_text() ) alreadyExists = true;
01124                 }
01125                 if (!alreadyExists) {
01126                     users_.push_back(connected_user(d.textbox_text(), CNTR_LOCAL, 0));
01127                     update_playerlist_state();
01128                     update_user_combos();
01129                 }
01130             }
01131         } while (alreadyExists);
01132     }
01133 
01134     for(size_t n = 0; n != sides_.size(); ++n) {
01135         sides_[n].process_event();
01136         if (sides_[n].changed())
01137             changed = true;
01138     }
01139 
01140     if (cancel_.pressed()) {
01141         if(network::nconnections() > 0) {
01142             config cfg;
01143             cfg.add_child("leave_game");
01144             network::send_data(cfg, 0);
01145         }
01146 
01147         set_result(QUIT);
01148         return;
01149     }
01150 
01151     if (launch_.pressed()) {
01152         if (can_start_game())
01153             set_result(mp::ui::PLAY);
01154     }
01155 
01156     // If something has changed in the level config,
01157     // send it to the network:
01158     if (changed) {
01159         update_playerlist_state();
01160         update_and_send_diff();
01161     }
01162 }
01163 
01164 const game_state& connect::get_state()
01165 {
01166     return state_;
01167 }
01168 
01169 void connect::start_game()
01170 {
01171     DBG_MP << "starting a new game" << std::endl;
01172 
01173     // Resolves the "random faction", "random gender" and "random message"
01174     // Must be done before shuffle sides, or some cases will cause errors
01175     for (side_list::iterator itor = sides_.begin(); itor != sides_.end();
01176             ++itor) {
01177 
01178         itor->resolve_random();
01179     }
01180 
01181     // Shuffle sides (check preferences and if it is a re-loaded game)
01182     // Must be done after resolve_random() or shuffle sides, or they won't work.
01183     if (preferences::shuffle_sides() && !(level_.child("snapshot") && level_.child("snapshot").child("side")))
01184     {
01185         // Only playable sides should be shuffled
01186         std::vector<int> playable_sides;
01187 
01188         // Find ids of playable sides
01189         for (side_list::iterator itor = sides_.begin(); itor != sides_.end(); ++itor)
01190             if (itor->allow_player()) playable_sides.push_back(itor->get_index());
01191 
01192         // Now do Fisher-Yates shuffle
01193         for (int i = playable_sides.size(); i > 1; i--)
01194         {
01195             int j_side = playable_sides[get_random() % i];
01196             int i_side = playable_sides[i - 1];
01197 
01198             int tmp_index = sides_[j_side].get_index();
01199             sides_[j_side].set_index(sides_[i_side].get_index());
01200             sides_[i_side].set_index(tmp_index);
01201 
01202             int tmp_team = sides_[j_side].get_team();
01203             sides_[j_side].set_team(sides_[i_side].get_team());
01204             sides_[i_side].set_team(tmp_team);
01205 
01206             std::map<std::string, config> tmp_side_children = sides_[j_side].get_side_children();
01207             sides_[j_side].set_side_children(sides_[i_side].get_side_children());
01208             sides_[i_side].set_side_children(tmp_side_children);
01209 
01210             // This is needed otherwise fog bugs will apear
01211             side tmp_side = sides_[j_side];
01212             sides_[j_side] = sides_[i_side];
01213             sides_[i_side] = tmp_side;
01214         }
01215     }
01216 
01217     // Make other clients not show the results of resolve_random().
01218     config lock;
01219     lock.add_child("stop_updates");
01220     network::send_data(lock, 0);
01221     update_and_send_diff(true);
01222 
01223     // Build the gamestate object after updating the level
01224 
01225     level_to_gamestate(level_, state_);
01226 
01227     network::send_data(config("start_game"), 0);
01228 }
01229 
01230 void connect::hide_children(bool hide)
01231 {
01232     DBG_MP << (hide ? "hiding" : "showing" ) << " children widgets" << std::endl;
01233 
01234     ui::hide_children(hide);
01235 
01236     waiting_label_.hide(hide);
01237     // Hiding the scrollpane automatically hides its contents
01238     scroll_pane_.hide(hide);
01239     for (side_list::iterator itor = sides_.begin(); itor != sides_.end(); ++itor) {
01240         itor->hide_ai_algorithm_combo(hide);
01241     }
01242     faction_title_label_.hide(hide);
01243     team_title_label_.hide(hide);
01244     color_title_label_.hide(hide);
01245     if (!params_.saved_game) {
01246         gold_title_label_.hide(hide);
01247         income_title_label_.hide(hide);
01248     }
01249 
01250     launch_.hide(hide);
01251     cancel_.hide(hide);
01252 }
01253 
01254 void connect::process_network_data(const config& data, const network::connection sock)
01255 {
01256     ui::process_network_data(data, sock);
01257 
01258     if(data.child("leave_game")) {
01259         set_result(QUIT);
01260         return;
01261     }
01262 
01263     if (!data["side_drop"].empty()) {
01264         unsigned side_drop = data["side_drop"].to_int() - 1;
01265         if (side_drop < sides_.size())
01266         {
01267             connected_user_list::iterator player = find_player(sides_[side_drop].get_player_id());
01268             sides_[side_drop].reset(sides_[side_drop].get_controller());
01269             if (player != users_.end()) {
01270                 users_.erase(player);
01271                 update_user_combos();
01272             }
01273             update_and_send_diff();
01274             update_playerlist_state(true);
01275             return;
01276         }
01277     }
01278 
01279     if (!data["side"].empty()) {
01280         unsigned side_taken = data["side"].to_int() - 1;
01281 
01282         // Checks if the connecting user has a valid and unique name.
01283         const std::string name = data["name"];
01284         if(name.empty()) {
01285             config response;
01286             response["failed"] = true;
01287             network::send_data(response, sock);
01288             ERR_CF << "ERROR: No username provided with the side.\n";
01289             return;
01290         }
01291 
01292         connected_user_list::iterator player = find_player(name);
01293         if(player != users_.end()) {
01294             /**
01295              * @todo Seems like a needless limitation to only allow one side
01296              * per player.
01297              */
01298             if(find_player_side(name) != -1) {
01299                 config response;
01300                 response["failed"] = true;
01301                 response["message"] = "The nickname '" + name + "' is already in use.";
01302                 network::send_data(response, sock);
01303                 return;
01304             } else {
01305                 users_.erase(player);
01306                 config observer_quit;
01307                 observer_quit.add_child("observer_quit")["name"] = name;
01308                 network::send_data(observer_quit, 0);
01309                 update_user_combos();
01310             }
01311         }
01312 
01313         // Assigns this user to a side
01314         if (side_taken < sides_.size())
01315         {
01316             if(!sides_[side_taken].available(name)) {
01317                 // This side is already taken.
01318                 // Try to reassing the player to a different position.
01319                 side_list::const_iterator itor;
01320                 side_taken = 0;
01321                 for (itor = sides_.begin(); itor != sides_.end();
01322                         ++itor, ++side_taken) {
01323                     if(itor->available())
01324                         break;
01325                 }
01326 
01327                 if(itor == sides_.end()) {
01328                     config response;
01329                     response["failed"] = true;
01330                     network::send_data(response, sock);
01331                     config kick;
01332                     kick["username"] = data["name"];
01333                     config res;
01334                     res.add_child("kick", kick);
01335                     network::send_data(res, 0);
01336                     update_user_combos();
01337                     update_and_send_diff();
01338                     ERR_CF << "ERROR: Couldn't assign a side to '" << name << "'\n";
01339                     return;
01340                 }
01341             }
01342 
01343             LOG_CF << "client has taken a valid position\n";
01344 
01345             // Adds the name to the list
01346             users_.push_back(connected_user(name, CNTR_NETWORK, sock));
01347             update_user_combos();
01348 
01349             // sides_[side_taken].set_connection(sock);
01350             sides_[side_taken].import_network_user(data);
01351 
01352             // Go thought and check if more sides are reserved
01353             // For this player
01354             std::for_each(sides_.begin(), sides_.end(), boost::bind(&connect::take_reserved_side, this,_1, data));
01355             update_playerlist_state(false);
01356             update_and_send_diff();
01357 
01358             LOG_NW << "sent player data\n";
01359 
01360         } else {
01361             ERR_CF << "tried to take illegal side: " << side_taken << '\n';
01362             config response;
01363             response["failed"] = true;
01364             network::send_data(response, sock);
01365         }
01366     }
01367 
01368     if (const config &change_faction = data.child("change_faction")) {
01369         int side_taken = find_player_side(change_faction["name"]);
01370         if(side_taken != -1) {
01371             sides_[side_taken].import_network_user(change_faction);
01372             sides_[side_taken].set_ready_for_start(true);
01373             update_playerlist_state();
01374             update_and_send_diff();
01375         }
01376     }
01377 
01378     if (const config &c = data.child("observer"))
01379     {
01380         const t_string &observer_name = c["name"];
01381         if(!observer_name.empty()) {
01382             connected_user_list::iterator player = find_player(observer_name);
01383             if(player == users_.end()) {
01384                 users_.push_back(connected_user(observer_name, CNTR_NETWORK, sock));
01385                 update_user_combos();
01386                 update_playerlist_state();
01387                 update_and_send_diff();
01388             }
01389         }
01390     }
01391     if (const config &c = data.child("observer_quit"))
01392     {
01393         const t_string &observer_name = c["name"];
01394         if(!observer_name.empty()) {
01395             connected_user_list::iterator player = find_player(observer_name);
01396             if(player != users_.end() && find_player_side(observer_name) == -1) {
01397                 users_.erase(player);
01398                 update_user_combos();
01399                 update_playerlist_state();
01400                 update_and_send_diff();
01401             }
01402         }
01403     }
01404 }
01405 
01406 void connect::take_reserved_side(connect::side& side, const config& data)
01407 {
01408     if (side.available(data["name"])
01409             && side.get_current_player() == data["name"])
01410     {
01411         side.import_network_user(data);
01412     }
01413 }
01414 
01415 void connect::process_network_error(network::error& error)
01416 {
01417     // If the problem isn't related to any specific connection,
01418     // it's a general error and we should just re-throw the error.
01419     // Likewise if we are not a server, we cannot afford any connection
01420     // to go down, so also re-throw the error.
01421     if(!error.socket || !network::is_server()) {
01422         error.disconnect();
01423         throw network::error(error.message);
01424     }
01425 
01426     bool changes = false;
01427 
01428     // A socket has disconnected. Remove it, and resets its side
01429     connected_user_list::iterator user;
01430     for(user = users_.begin(); user != users_.end(); ++user) {
01431         if(user->connection == error.socket) {
01432             changes = true;
01433 
01434             int i = find_player_side(user->name);
01435             if (i != -1)
01436                 sides_[i].reset(default_controller_);
01437 
01438             break;
01439         }
01440     }
01441     if(user != users_.end()) {
01442         users_.erase(user);
01443         update_user_combos();
01444     }
01445 
01446     // Now disconnect the socket
01447     error.disconnect();
01448 
01449     // If there have been changes to the positions taken,
01450     // then notify other players
01451     if(changes) {
01452         update_and_send_diff();
01453         update_playerlist_state();
01454     }
01455 }
01456 
01457 bool connect::accept_connections()
01458 {
01459     if (sides_available())
01460         return true;
01461     return false;
01462 }
01463 
01464 void connect::process_network_connection(const network::connection sock)
01465 {
01466     ui::process_network_connection(sock);
01467 
01468     network::send_data(config("join_game"), 0);
01469 
01470     network::send_data(level_, sock);
01471 }
01472 
01473 void connect::layout_children(const SDL_Rect& rect)
01474 {
01475     ui::layout_children(rect);
01476 
01477     SDL_Rect ca = client_area();
01478 
01479     gui::button* left_button = &launch_;
01480     gui::button* right_button = &cancel_;
01481 #ifdef OK_BUTTON_ON_RIGHT
01482     std::swap(left_button,right_button);
01483 #endif
01484     size_t left = ca.x;
01485     size_t right = ca.x + ca.w;
01486     size_t top = ca.y;
01487     size_t bottom = ca.y + ca.h;
01488 
01489     // Buttons
01490     right_button->set_location(right  - right_button->width(),
01491                                bottom - right_button->height());
01492     left_button->set_location(right  - right_button->width() - left_button->width() - gui::ButtonHPadding,
01493                               bottom - left_button->height());
01494 
01495     type_title_label_.set_location(left+30, top+35);
01496     faction_title_label_.set_location((left+145), top+35);
01497     team_title_label_.set_location((left+260), top+35);
01498     color_title_label_.set_location((left+375), top+35);
01499     gold_title_label_.set_location((left+493), top+35);
01500     income_title_label_.set_location((left+560), top+35);
01501 
01502     add_local_player_.set_help_string(("Feature currently disabled."));
01503     add_local_player_.set_active(false);
01504     add_local_player_.hide(true);
01505     add_local_player_.set_location(left, bottom - add_local_player_.height());
01506     waiting_label_.set_location(left + gui::ButtonHPadding +
01507         add_local_player_.width(), bottom - left_button->height() + 4);
01508 
01509     SDL_Rect scroll_pane_rect;
01510     scroll_pane_rect.x = ca.x;
01511     scroll_pane_rect.y = ca.y + 50;
01512     scroll_pane_rect.w = ca.w;
01513     scroll_pane_rect.h = launch_.location().y - scroll_pane_rect.y - gui::ButtonVPadding;
01514 
01515     scroll_pane_.set_location(scroll_pane_rect);
01516 }
01517 
01518 void connect::lists_init()
01519 {
01520     // Options
01521     if(!local_only_) {
01522         player_types_.push_back(_("Network Player"));
01523     }
01524     player_types_.push_back(_("Local Player"));
01525     player_types_.push_back(_("Computer Player"));
01526     player_types_.push_back(_("Empty"));
01527 
01528     foreach (const config *faction, era_sides_) {
01529         player_factions_.push_back((*faction)["name"]);
01530     }
01531 
01532     // AI algorithms
01533     const config &era = level_.child("era");
01534     ai::configuration::add_era_ai_from_config(era);
01535     ai_algorithms_ = ai::configuration::get_available_ais();
01536 
01537     // Factions
01538     config::child_itors sides = current_config()->child_range("side");
01539 
01540     // Teams
01541     if(params_.use_map_settings) {
01542         int side_num = 1;
01543         foreach (config &side, sides)
01544         {
01545             config::attribute_value &team_name = side["team_name"];
01546             config::attribute_value &user_team_name = side["user_team_name"];
01547 
01548             if(team_name.empty())
01549                 team_name = side_num;
01550 
01551             if(user_team_name.empty())
01552             {
01553                 user_team_name = team_name;
01554             }
01555 
01556             std::vector<std::string>::const_iterator itor = std::find(team_names_.begin(),
01557                 team_names_.end(), team_name.str());
01558             if(itor == team_names_.end()) {
01559                 team_names_.push_back(team_name);
01560                 user_team_names_.push_back(user_team_name.t_str().to_serialized());
01561                 if (side["allow_player"].to_bool(true)) {
01562                     player_teams_.push_back(user_team_name.str());
01563                 }
01564             }
01565             ++side_num;
01566         }
01567     } else {
01568         std::vector<std::string> map_team_names;
01569         int _side_num = 1;
01570         foreach (config &side, sides)
01571         {
01572             const std::string side_num = lexical_cast<std::string>(_side_num);
01573             config::attribute_value &team_name = side["team_name"];
01574 
01575             if(team_name.empty())
01576                 team_name = side_num;
01577 
01578             std::vector<std::string>::const_iterator itor = std::find(map_team_names.begin(),
01579                 map_team_names.end(), team_name.str());
01580             if(itor == map_team_names.end()) {
01581                 map_team_names.push_back(team_name);
01582                 team_name = lexical_cast<std::string>(map_team_names.size());
01583             } else {
01584                 team_name = lexical_cast<std::string>(itor - map_team_names.begin() + 1);
01585             }
01586 
01587             team_names_.push_back(side_num);
01588             user_team_names_.push_back(team_prefix_ + side_num);
01589             if (side["allow_player"].to_bool(true)) {
01590                 player_teams_.push_back(team_prefix_ + side_num);
01591             }
01592             ++_side_num;
01593         }
01594     }
01595 
01596     // Colors
01597     for(int i = 0; i < gamemap::MAX_PLAYERS; ++i) {
01598         player_colors_.push_back(get_color_string(i));
01599     }
01600 
01601     // Populates "sides_" from the level configuration
01602     int index = 0;
01603     foreach (const config &s, sides) {
01604         sides_.push_back(side(*this, s, index++));
01605     }
01606     int offset=0;
01607     // This function must be called after the sides_ vector is fully populated.
01608     for(side_list::iterator s = sides_.begin(); s != sides_.end(); ++s) {
01609         const int side_num = s - sides_.begin();
01610         const int spos = 60 * (side_num-offset);
01611         if(!s->allow_player()) {
01612             offset++;
01613             continue;
01614         }
01615 
01616         s->add_widgets_to_scrollpane(scroll_pane_, spos);
01617     }
01618 }
01619 
01620 void connect::load_game()
01621 {
01622     DBG_MP << "loading game parameters" << std::endl;
01623 
01624     if(params_.saved_game) {
01625         try{
01626             savegame::loadgame load(disp(), game_config(), state_);
01627             load.load_multiplayer_game();
01628             load.fill_mplevel_config(level_);
01629         }
01630         catch (load_game_cancelled_exception){
01631             set_result(CREATE);
01632             return;
01633         }
01634     } else {
01635         level_.clear();
01636         params_.saved_game = false;
01637         params_.mp_scenario = params_.scenario_data["id"].str();
01638         level_.merge_with(params_.scenario_data);
01639         level_["turns"] = num_turns_;
01640         level_.add_child("multiplayer", params_.to_config());
01641 
01642         params_.hash = level_.hash();
01643         level_["next_underlying_unit_id"] = 0;
01644         n_unit::id_manager::instance().clear();
01645 
01646         if (params_.random_start_time)
01647         {
01648             if (!tod_manager::is_start_ToD(level_["random_start_time"]))
01649             {
01650                 level_["random_start_time"] = true;
01651             }
01652         }
01653         else
01654         {
01655             level_["random_start_time"] = false;
01656         }
01657 
01658         level_["experience_modifier"] = params_.xp_modifier;
01659         level_["random_seed"] = state_.rng().get_random_seed();
01660     }
01661 
01662     // Add the map name to the title.
01663     append_to_title(" — " + level_["name"].t_str());
01664 
01665 
01666     std::string era = params_.mp_era;
01667     if (params_.saved_game) {
01668         if (const config &c = level_.child("snapshot").child("era"))
01669             era = c["id"].str();
01670     }
01671 
01672     // Initialize the list of sides available for the current era.
01673     const config &era_cfg = game_config().find_child("era", "id", era);
01674     if (!era_cfg) {
01675         if (!params_.saved_game)
01676         {
01677             utils::string_map i18n_symbols;
01678             i18n_symbols["era"] = era;
01679             throw config::error(vgettext("Cannot find era $era", i18n_symbols));
01680         }
01681         // FIXME: @todo We should tell user about missing era but still load game
01682         WRN_CF << "Missing era in MP load game " << era << "\n";
01683     }
01684     else
01685     {
01686         era_sides_.clear();
01687         foreach (const config &e, era_cfg.child_range("multiplayer_side")) {
01688             era_sides_.push_back(&e);
01689         }
01690         level_.add_child("era", era_cfg);
01691     }
01692 
01693     gold_title_label_.hide(params_.saved_game);
01694     income_title_label_.hide(params_.saved_game);
01695 
01696     // This will force connecting clients to be using the same version number as us.
01697     level_["version"] = game_config::version;
01698 
01699     level_["observer"] = params_.allow_observers;
01700     level_["shuffle_sides"] = params_.shuffle_sides;
01701 
01702     if(level_["objectives"].empty()) {
01703         level_["objectives"] = "<big>" + t_string(N_("Victory:"), "wesnoth") +
01704             "</big>\n<span foreground=\"#00ff00\">&#8226; " +
01705             t_string(N_("Defeat enemy leader(s)"), "wesnoth") + "</span>";
01706     }
01707 }
01708 
01709 config* connect::current_config(){
01710     config* cfg_level = NULL;
01711 
01712     //It might make sense to invent a mechanism of some sort to check whether a config node contains information
01713     //that you can load from(side information, specifically)
01714     config &snapshot = level_.child("snapshot");
01715     if (snapshot && snapshot.child("side")) {
01716         // Savegame
01717         cfg_level = &snapshot;
01718     } else if (!level_.child("side")) {
01719         // Start-of-scenario save, the info has to be taken from the starting_pos
01720         cfg_level = &state_.starting_pos;
01721     } else {
01722         // Fresh game, no snapshot available
01723         cfg_level = &level_;
01724     }
01725 
01726     return cfg_level;
01727 }
01728 
01729 void connect::update_level()
01730 {
01731     DBG_MP << "updating level" << std::endl;
01732     // Import all sides into the level
01733     level_.clear_children("side");
01734     for(side_list::const_iterator itor = sides_.begin(); itor != sides_.end();
01735             ++itor) {
01736 
01737         level_.add_child("side", itor->get_config());
01738     }
01739 }
01740 
01741 void connect::update_and_send_diff(bool update_time_of_day)
01742 {
01743 
01744     config old_level = level_;
01745     update_level();
01746     if (update_time_of_day)
01747     {
01748         // Set random start ToD
01749         tod_manager tod_mng(level_, level_["turns"]);
01750     }
01751 
01752     config diff = level_.get_diff(old_level);
01753     if (!diff.empty()) {
01754         config scenario_diff;
01755         scenario_diff.add_child("scenario_diff", diff);
01756         network::send_data(scenario_diff, 0);
01757     }
01758 }
01759 
01760 bool connect::sides_ready() const
01761 {
01762     for(side_list::const_iterator itor = sides_.begin(); itor != sides_.end(); ++itor) {
01763         if (!itor->ready_for_start()) {
01764             DBG_MP << "not all sides are ready, side " << itor->get_config().get("side")->str() << " not ready" << std::endl;
01765             return false;
01766         }
01767     }
01768     DBG_MP << "all sides are ready" << std::endl;
01769     return true;
01770 }
01771 
01772 bool connect::sides_available() const
01773 {
01774     for(side_list::const_iterator itor = sides_.begin(); itor != sides_.end(); ++itor) {
01775         if (itor->available())
01776             return true;
01777     }
01778     return false;
01779 }
01780 
01781 bool connect::can_start_game() const
01782 {
01783     if(!sides_ready()) {
01784         return false;
01785     }
01786 
01787     /*
01788      * If at least one human player is slotted with a player/ai we're allowed
01789      * to start. Before used a more advanced test but it seems people are
01790      * creative in what is used in multiplayer [1] so use a simpler test now.
01791      * [1] http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=568029
01792      */
01793     foreach(const side& s, sides_) {
01794         if(s.get_controller() != CNTR_EMPTY) {
01795             if(s.allow_player()) {
01796                 return true;
01797             }
01798         }
01799     }
01800 
01801     return false;
01802 }
01803 
01804 void connect::update_playerlist_state(bool silent)
01805 {
01806     DBG_MP << "updating player list state" << std::endl;
01807 
01808     waiting_label_.set_text(can_start_game() ? ""
01809             : sides_available()
01810                 ? _("Waiting for players to join...")
01811                 : _("Waiting for players to choose factions..."));
01812     launch_.enable(can_start_game());
01813 
01814     // If the "gamelist_" variable has users, use it.
01815     // Else, extracts the user list from the actual player list.
01816     if (gamelist().child("user")) {
01817         ui::gamelist_updated(silent);
01818     } else {
01819         // Updates the player list
01820         std::vector<std::string> playerlist;
01821         for(connected_user_list::const_iterator itor = users_.begin();
01822                                                        itor != users_.end();
01823                                                        ++itor) {
01824             playerlist.push_back(itor->name);
01825         }
01826         set_user_list(playerlist, silent);
01827         set_user_menu_items(playerlist);
01828     }
01829 }
01830 
01831 connect::connected_user_list::iterator connect::find_player(const std::string& id)
01832 {
01833     connected_user_list::iterator itor;
01834     for (itor = users_.begin(); itor != users_.end(); ++itor) {
01835         if (itor->name == id)
01836             break;
01837     }
01838     return itor;
01839 }
01840 
01841 int connect::find_player_side(const std::string& id) const
01842 {
01843     side_list::const_iterator itor;
01844     for (itor = sides_.begin(); itor != sides_.end(); ++itor) {
01845         if (itor->get_player_id() == id)
01846             break;
01847     }
01848 
01849     if (itor == sides_.end())
01850         return -1;
01851 
01852     return itor - sides_.begin();
01853 }
01854 
01855 void connect::update_user_combos()
01856 {
01857     for (side_list::iterator itor = sides_.begin();
01858                                     itor != sides_.end(); ++itor) {
01859         itor->update_user_list();
01860     }
01861 }
01862 
01863 } // end namespace mp
01864 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

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