From 3c75c21583a457a9b1695a6a5eeb3506de17112d Mon Sep 17 00:00:00 2001 From: Andrius Silinskas Date: Mon, 12 Aug 2013 16:19:31 +0100 Subject: [PATCH] Refactor users related code (mp::connect). --- src/multiplayer_connect.cpp | 185 ++++------- src/multiplayer_connect.hpp | 17 +- src/multiplayer_connect_engine.cpp | 478 +++++++++++++++++------------ src/multiplayer_connect_engine.hpp | 109 ++++--- src/multiplayer_ui.hpp | 2 - 5 files changed, 410 insertions(+), 381 deletions(-) diff --git a/src/multiplayer_connect.cpp b/src/multiplayer_connect.cpp index 1ffa960c841..acb39ec82ba 100644 --- a/src/multiplayer_connect.cpp +++ b/src/multiplayer_connect.cpp @@ -35,6 +35,17 @@ static lg::log_domain log_mp_connect("mp/connect"); namespace mp { +std::vector controller_options_names( + const std::vector& controller_options) +{ + std::vector names; + BOOST_FOREACH(const controller_option& option, controller_options) { + names.push_back(option.second); + } + + return names; +} + connect::side::side(connect& parent, side_engine_ptr engine) : parent_(&parent), engine_(engine), @@ -50,8 +61,8 @@ connect::side::side(connect& parent, side_engine_ptr engine) : label_gold_(parent.video(), "100", font::SIZE_SMALL, font::LOBBY_COLOR), label_income_(parent.video(), _("Normal"), font::SIZE_SMALL, font::LOBBY_COLOR), - combo_controller_(new gui::combo_drag(parent.disp(), parent.player_types_, - parent.combo_control_group_)), + combo_controller_(new gui::combo_drag(parent.disp(), + std::vector(), parent.combo_control_group_)), combo_ai_algorithm_(parent.disp(), std::vector()), combo_faction_(parent.disp(), std::vector()), combo_leader_(parent.disp(), std::vector()), @@ -85,7 +96,8 @@ connect::side::side(connect& parent, side_engine_ptr engine) : label_gold_.hide(parent_->params_.saved_game); label_income_.hide(parent_->params_.saved_game); - init_ai_algorithm_combo(); + update_controller_combo_list(); + update_ai_algorithm_combo(); if (parent_->params_.use_map_settings) { // Gold, income, team, and color are only suggestions. @@ -155,47 +167,19 @@ void connect::side::process_event() changed_ = true; parent_->sides_[drop_target].changed_ = true; - init_ai_algorithm_combo(); - parent_->sides_[drop_target].init_ai_algorithm_combo(); + update_ai_algorithm_combo(); + parent_->sides_[drop_target].update_ai_algorithm_combo(); update_ui(); parent_->sides_[drop_target].update_ui(); - } else if (combo_controller_->changed() && - combo_controller_->selected() >= 0) { - - const int cntr_last = - (engine_->save_id().empty() ? CNTR_LAST-1 : CNTR_LAST) - - (parent_->engine_.local_players_only() ? 1 : 0); - if (combo_controller_->selected() == cntr_last) { - update_controller_ui(); - } else if (combo_controller_->selected() < cntr_last) { - // Correct entry number if CNTR_NETWORK - // is not allowed for combo_controller_. - engine_-> - set_mp_controller(mp::controller(combo_controller_->selected() + - (parent_->engine_.local_players_only() ? 1 : 0))); - engine_->set_player_id(""); - engine_->set_ready_for_start(false); - changed_ = true; - } else { - // Give user a second side. - size_t user = combo_controller_->selected() - cntr_last - 1; - - const std::string new_id = parent_->engine_.users()[user].name; - if (new_id != engine_->player_id()) { - engine_->set_player_id(new_id); - engine_->set_mp_controller( - parent_->engine_.users()[user].controller); - engine_->set_ready_for_start(true); - changed_ = true; - } - } - hide_ai_algorithm_combo(parent_->hidden()); + } else if (combo_controller_->changed()) { + changed_ = engine_->controller_changed(combo_controller_->selected()); + update_controller_combo(); } - if (combo_controller_->hidden()) { combo_controller_->hide(false); } + if (parent_->params_.saved_game) { return; } @@ -240,7 +224,6 @@ void connect::side::process_event() changed_ = true; } if (slider_income_.value() != engine_->income()) { - engine_->set_income(slider_income_.value()); std::stringstream buf; if (engine_->income() < 0) { @@ -265,11 +248,13 @@ bool connect::side::changed() return false; } -void connect::side::update_user_list( - const std::vector& name_list) { +void connect::side::update_controller_combo_list() +{ + engine_->update_controller_options(); + combo_controller_->set_items(controller_options_names( + engine_->controller_options())); - combo_controller_->set_items(name_list); - update_controller_ui(); + update_controller_combo(); } void connect::side::update_ui() @@ -278,7 +263,7 @@ void connect::side::update_ui() update_leader_combo(); update_gender_combo(); - update_controller_ui(); + update_controller_combo(); combo_team_.set_selected(engine_->team()); combo_color_.set_selected(engine_->color()); @@ -296,13 +281,13 @@ void connect::side::update_ui() label_income_.set_text(buf.str()); } -void connect::side::hide_ai_algorithm_combo(bool invisible) +void connect::side::hide_ai_algorithm_combo(bool force) { - if (!invisible) { - if (engine_->mp_controller() == CNTR_COMPUTER) { + if (!force) { + if (engine_->controller() == CNTR_COMPUTER) { // Computer selected, show AI combo. - label_original_controller_.hide(true); combo_ai_algorithm_.hide(false); + label_original_controller_.hide(true); } else { // Computer de-selected, hide AI combo. combo_ai_algorithm_.hide(true); @@ -333,28 +318,26 @@ void connect::side::add_widgets_to_scrollpane(gui::scrollpane& pane, int pos) pane.add_widget(&label_income_, 475 + slider_gold_.width(), 35 + pos); } -void connect::side::init_ai_algorithm_combo() +void connect::side::update_ai_algorithm_combo() { - assert(parent_->ai_algorithms_.empty() == false); + assert(!parent_->ai_algorithms_.empty()); int sel = 0; - std::vector &ais_list = parent_->ai_algorithms_; - std::vector ais; int i = 0; - BOOST_FOREACH(const ai::description *desc, ais_list){ + std::vector ais; + BOOST_FOREACH(const ai::description* desc, parent_->ai_algorithms_){ ais.push_back(desc->text); - if (desc->id == engine_->ai_algorithm()){ + if (desc->id == engine_->ai_algorithm()) { sel = i; } i++; } combo_ai_algorithm_.set_items(ais); combo_ai_algorithm_.set_selected(sel); - if (!ais_list.empty()) { - // Ensures that the visually selected AI - // is the one that will be loaded. - engine_->set_ai_algorithm(ais_list[sel]->id); - } + + // Ensures that the visually selected AI + // is the one that will be loaded. + engine_->set_ai_algorithm(parent_->ai_algorithms_[sel]->id); } void connect::side::update_faction_combo() @@ -394,26 +377,9 @@ void connect::side::update_gender_combo() engine_->color(), parent_->params_.saved_game); } -void connect::side::update_controller_ui() +void connect::side::update_controller_combo() { - if (engine_->player_id().empty()) { - combo_controller_->set_selected( - engine_->mp_controller() - (parent_->engine_.local_players_only() ? 1 : 0)); - } else { - connected_user_list::iterator player = - parent_->engine_.find_player_by_id(engine_->player_id()); - - if (player != parent_->engine_.users().end()) { - const int no_reserve = engine_->save_id().empty() ? - 1 : 0; - combo_controller_->set_selected( - CNTR_LAST + no_reserve + 1 + - (player - parent_->engine_.users().begin()) - - (parent_->engine_.local_players_only() ? 1 : 0)); - } else { - assert(parent_->engine_.local_players_only() != true); - combo_controller_->set_selected(CNTR_NETWORK); - } - } + combo_controller_->set_selected(engine_->current_controller_index()); hide_ai_algorithm_combo(parent_->hidden()); } @@ -423,7 +389,6 @@ connect::connect(game_display& disp, const config& game_config, bool local_players_only, bool first_scenario) : mp::ui(disp, _("Game Lobby: ") + params.name, game_config, c, gamelist), params_(params), - player_types_(), player_teams_(), player_colors_(), ai_algorithms_(), @@ -468,6 +433,7 @@ connect::connect(game_display& disp, const config& game_config, throw config::error( _("The scenario is invalid because it has no sides.")); } + update_side_controller_combos(); if (first_scenario) { // Send Initial information @@ -480,10 +446,6 @@ connect::connect(game_display& disp, const config& game_config, network::send_data(response, 0); } - update_user_combos(); - - engine_.assign_side_for_host(); - append_to_title(" — " + engine_.level()["name"].t_str()); gold_title_label_.hide(params_.saved_game); income_title_label_.hide(params_.saved_game); @@ -532,7 +494,7 @@ void connect::process_event() engine_.users().push_back(connected_user(d.textbox_text(), CNTR_LOCAL, 0)); update_playerlist_state(); - update_user_combos(); + update_side_controller_combos(); } } } while (already_exists); @@ -648,32 +610,28 @@ void connect::process_network_data(const config& data, const network::connection sock) { ui::process_network_data(data, sock); - network_res_tuple result = engine_.process_network_data(data, sock); + std::pair result = engine_.process_network_data(data, sock); - if (result.get<0>()) { + if (result.first) { set_result(QUIT); - } else { - if (result.get<1>()) { - update_user_combos(); - update_playerlist_state(result.get<2>()); - } + } - BOOST_FOREACH(int side_index, result.get<3>()) { - sides_[side_index].update_ui(); - } + update_side_controller_combos(); + update_playerlist_state(result.second); + BOOST_FOREACH(side& s, sides_) { + s.update_ui(); } } void connect::process_network_error(network::error& error) { - int res = engine_.process_network_error(error); + bool res = engine_.process_network_error(error); - if (res > 0) { - sides_[res - 1].update_ui(); - } - - if (res != 1) { - update_user_combos(); + if (res) { + BOOST_FOREACH(side& s, sides_) { + s.update_ui(); + } + update_side_controller_combos(); update_playerlist_state(); } } @@ -691,14 +649,6 @@ bool connect::accept_connections() void connect::lists_init() { - // Options. - if (!engine_.local_players_only()) { - player_types_.push_back(_("Network Player")); - } - player_types_.push_back(_("Local Player")); - player_types_.push_back(_("Computer Player")); - player_types_.push_back(_("Empty")); - // AI algorithms. const config &era = engine_.level().child("era"); ai::configuration::add_era_ai_from_config(era); @@ -795,6 +745,7 @@ void connect::lists_init() index++; } + engine_.init_after_side_engines_assigned(); // Add side widgets to scroll pane. int side_pos_y_offset = 0; @@ -825,7 +776,7 @@ void connect::update_playerlist_state(bool silent) } else { // Updates the player list std::vector playerlist; - BOOST_FOREACH(const connected_user& user, engine_.users()) { + BOOST_FOREACH(const connected_user& user, engine_.connected_users()) { playerlist.push_back(user.name); } set_user_list(playerlist, silent); @@ -833,22 +784,10 @@ void connect::update_playerlist_state(bool silent) } } -void connect::update_user_combos() +void connect::update_side_controller_combos() { BOOST_FOREACH(side& s, sides_) { - typedef std::vector name_list; - - name_list list = player_types_; - if (!s.engine()->save_id().empty()) { - list.push_back(_("Reserved")); - } - list.push_back(_("--give--")); - - BOOST_FOREACH(const connected_user& user, engine_.users()) { - list.push_back(user.name); - } - - s.update_user_list(list); + s.update_controller_combo_list(); } } diff --git a/src/multiplayer_connect.hpp b/src/multiplayer_connect.hpp index 8d69f28093e..1e9012f8096 100644 --- a/src/multiplayer_connect.hpp +++ b/src/multiplayer_connect.hpp @@ -31,6 +31,10 @@ namespace ai { namespace mp { +// Helper function to retrieve controller names. +std::vector controller_options_names( + const std::vector& controller_options); + class connect : public mp::ui { public: @@ -46,11 +50,10 @@ public: // Returns true if this side changed since last call to this method. bool changed(); - // Adds an user to the user list combo. - void update_user_list(const std::vector& name_list); + void update_controller_combo_list(); void update_ui(); - void hide_ai_algorithm_combo(bool invisible); + void hide_ai_algorithm_combo(bool force); void add_widgets_to_scrollpane(gui::scrollpane& pane, int pos); @@ -58,13 +61,12 @@ public: const side_engine_ptr engine() const { return engine_; } private: - void init_ai_algorithm_combo(); - // Update UI methods and their helper(s). + void update_ai_algorithm_combo(); void update_faction_combo(); void update_leader_combo(); void update_gender_combo(); - void update_controller_ui(); + void update_controller_combo(); // The mp::connect widget owning this mp::connect::side. connect* parent_; @@ -129,12 +131,11 @@ private: // Updates the state of the player list, the launch button and of the start // game label, to reflect the actual state. void update_playerlist_state(bool silent = true); - void update_user_combos(); + void update_side_controller_combos(); const mp_game_settings params_; // Lists used for combos. - std::vector player_types_; std::vector player_teams_; std::vector player_colors_; std::vector ai_algorithms_; diff --git a/src/multiplayer_connect_engine.cpp b/src/multiplayer_connect_engine.cpp index dedf8c3f296..2f73ce68046 100644 --- a/src/multiplayer_connect_engine.cpp +++ b/src/multiplayer_connect_engine.cpp @@ -69,13 +69,14 @@ connect_engine::connect_engine(game_display& disp, level_(), state_(), params_(params), - local_players_only_(local_players_only), + default_controller_(local_players_only ? CNTR_LOCAL: CNTR_NETWORK), first_scenario_(first_scenario), side_engines_(), era_factions_(), team_names_(), user_team_names_(), - users_() + connected_users_(), + default_controller_options_() { level_ = initial_level_config(disp, params, state_); if (level_.empty()) { @@ -88,8 +89,16 @@ connect_engine::connect_engine(game_display& disp, era_factions_.push_back(&era); } - // Adds the current user as default user. - users_.push_back(connected_user(preferences::login(), CNTR_LOCAL, 0)); + if (!local_players_only) { + default_controller_options_.push_back( + std::make_pair(CNTR_NETWORK, _("Network Player"))); + } + default_controller_options_.push_back( + std::make_pair(CNTR_LOCAL, _("Local Player"))); + default_controller_options_.push_back( + std::make_pair(CNTR_COMPUTER, _("Computer Player"))); + default_controller_options_.push_back( + std::make_pair(CNTR_EMPTY, _("Empty"))); } connect_engine::~connect_engine() @@ -123,30 +132,66 @@ void connect_engine::add_side_engine(side_engine_ptr engine) side_engines_.push_back(engine); } -void connect_engine::assign_side_for_host() +void connect_engine::init_after_side_engines_assigned() { - // Take the first available side or available side with id == login. - int side_choice = -1; - int counter = 0; - BOOST_FOREACH(side_engine_ptr side, side_engines_) { - if (side->allow_player()) { - if (side_choice == -1) { - side_choice = counter; - } - if (side->current_player() == preferences::login()) { - side_engines_[counter]->set_player_from_users_list( - preferences::login()); - side_choice = gamemap::MAX_PLAYERS; - } - } + // Add host to the connected users list. + config user_data; + user_data["name"] = preferences::login(); + import_user(HOST, user_data, 0); +} - counter++; +void connect_engine::import_user(USER_TYPE user_type, const config& data, + int sock, int side_taken) +{ + const std::string& username = data["name"]; + assert(!username.empty()); + + // Check if user with such name isn't already connected. + bool already_connected = false; + BOOST_FOREACH(const connected_user& user, connected_users_) { + if (user.name == username) { + already_connected = true; + } + } + // Finally, add user to the connected user list. + if (!already_connected) { + connected_users_.push_back(connected_user(username, sock)); + update_side_controller_options(); } - if (side_choice != -1 && side_choice != gamemap::MAX_PLAYERS) { - if (side_engines_[side_choice]->player_id() == "") { - side_engines_[side_choice]->set_player_from_users_list( - preferences::login()); + if (user_type == OBSERVER) { + return; + } + + bool side_assigned = false; + if (side_taken >= 0) { + side_engines_[side_taken]->place_user(data); + side_assigned = true; + } + + // Check if user has a side(s) reserved for him. + BOOST_FOREACH(side_engine_ptr side, side_engines_) { + if (side->current_player() == username) { + side->place_user(data); + + side_assigned = true; + } + } + + if (side_taken < 0) { + // If no sides were assigned for a user, + // take a first available side. + if (!side_assigned) { + BOOST_FOREACH(side_engine_ptr side, side_engines_) { + if (side->available_for_user(username) || + side->controller() == CNTR_LOCAL) { + + side->place_user(data); + + side_assigned = true; + break; + } + } } } } @@ -154,7 +199,7 @@ void connect_engine::assign_side_for_host() bool connect_engine::sides_available() const { BOOST_FOREACH(side_engine_ptr side, side_engines_) { - if (side->available()) { + if (side->available_for_user()) { return true; } } @@ -196,9 +241,9 @@ bool connect_engine::can_start_game() const // First check if all sides are ready to start the game. BOOST_FOREACH(side_engine_ptr side, side_engines_) { if (!side->ready_for_start()) { + const int side_num = side->index() + 1; DBG_MP << "not all sides are ready, side " << - side->new_config().get("side")->str() << " not ready" << - std::endl; + side_num << " not ready\n"; return false; } @@ -213,10 +258,8 @@ bool connect_engine::can_start_game() const * [1] http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=568029 */ BOOST_FOREACH(side_engine_ptr side, side_engines_) { - if (side->mp_controller() != CNTR_EMPTY) { - if (side->allow_player()) { - return true; - } + if (side->controller() != CNTR_EMPTY && side->allow_player()) { + return true; } } @@ -319,7 +362,7 @@ void connect_engine::start_game_commandline( // Set AI algorithm to RCA AI for all sides, // then override if commandline option was given. - side->set_ai_algorithm_commandline("ai_default_rca"); + side->set_ai_algorithm("ai_default_rca"); if (cmdline_opts.multiplayer_algorithm) { BOOST_FOREACH(const mp_option& option, *cmdline_opts.multiplayer_algorithm) { @@ -328,7 +371,7 @@ void connect_engine::start_game_commandline( DBG_MP << "\tsetting side " << option.get<0>() << "\tfaction: " << option.get<1>() << std::endl; - side->set_ai_algorithm_commandline(option.get<1>()); + side->set_ai_algorithm(option.get<1>()); } } } @@ -394,38 +437,35 @@ void connect_engine::start_game_commandline( network::send_data(config("start_game"), 0); } -network_res_tuple connect_engine::process_network_data(const config& data, +std::pair connect_engine::process_network_data(const config& data, const network::connection sock) { - network_res_tuple result; - result.get<0>() = false; - result.get<1>() = false; - result.get<2>() = true; + std::pair result(std::make_pair(false, true)); if (data.child("leave_game")) { - result.get<0>() = true; + result.first = true; return result; } // A side has been dropped. if (!data["side_drop"].empty()) { unsigned side_drop = data["side_drop"].to_int() - 1; - result.get<3>().push_back(side_drop); if (side_drop < side_engines_.size()) { side_engine_ptr side_to_drop = side_engines_[side_drop]; - connected_user_list::iterator player = - find_player_by_id(side_to_drop->player_id()); - if (player != users_.end()) { - users_.erase(player); + // Remove user, whose side was dropped. + const int user_index = + find_user_index_by_id(side_to_drop->player_id()); + if (user_index != -1) { + connected_users_.erase(connected_users_.begin() + user_index); + update_side_controller_options(); } - side_to_drop->reset(side_to_drop->mp_controller()); + side_to_drop->reset(); update_and_send_diff(); - result.get<1>() = true; return result; } } @@ -446,11 +486,11 @@ network_res_tuple connect_engine::process_network_data(const config& data, return result; } - connected_user_list::iterator player = find_player_by_id(name); - if (player != users().end()) { + const int user_index = find_user_index_by_id(name); + if (user_index != -1) { // TODO: Seems like a needless limitation // to only allow one side per player. - if (find_player_side_index_by_id(name) != -1) { + if (find_user_side_index_by_id(name) != -1) { config response; response["failed"] = true; response["message"] = "The nickname '" + name + @@ -459,23 +499,22 @@ network_res_tuple connect_engine::process_network_data(const config& data, return result; } else { - users_.erase(player); + connected_users_.erase(connected_users_.begin() + user_index); + update_side_controller_options(); config observer_quit; observer_quit.add_child("observer_quit")["name"] = name; network::send_data(observer_quit, 0); - - result.get<1>() = true; } } // Assigns this user to a side. if (side_taken < side_engines_.size()) { - if (!side_engines_[side_taken]->available(name)) { + if (!side_engines_[side_taken]->available_for_user(name)) { // This side is already taken. // Try to reassing the player to a different position. side_taken = 0; BOOST_FOREACH(side_engine_ptr s, side_engines_) { - if (s->available()) { + if (s->available_for_user()) { break; } @@ -492,8 +531,6 @@ network_res_tuple connect_engine::process_network_data(const config& data, kick["username"] = data["name"]; network::send_data(res, 0); - result.get<1>() = true; - update_and_send_diff(); ERR_CF << "ERROR: Couldn't assign a side to '" << @@ -505,30 +542,12 @@ network_res_tuple connect_engine::process_network_data(const config& data, LOG_CF << "client has taken a valid position\n"; - // Adds the name to the list. - users_.push_back(connected_user(name, CNTR_NETWORK, sock)); - - result.get<3>().push_back(side_taken); - side_engines_[side_taken]->import_network_user(data); - - // Check if more sides are reserved for this player. - int side_index = 0; - BOOST_FOREACH(side_engine_ptr& s, side_engines_) { - if (s->available(data["name"]) && - s->current_player() == data["name"]) { - - result.get<3>().push_back(side_index); - s->import_network_user(data); - } - - side_index++; - } - - result.get<1>() = true; - result.get<2>() = false; + import_user(PLAYER, data, sock, side_taken); update_and_send_diff(); + result.second = false; + LOG_NW << "sent player data\n"; } else { ERR_CF << "tried to take illegal side: " << side_taken << "\n"; @@ -539,46 +558,30 @@ network_res_tuple connect_engine::process_network_data(const config& data, } } - if (const config &change_faction = data.child("change_faction")) { - int side_taken = find_player_side_index_by_id(change_faction["name"]); - if (side_taken != -1) { - result.get<3>().push_back(side_taken); - side_engines_[side_taken]->import_network_user(change_faction); - side_engines_[side_taken]->set_ready_for_start(true); - result.get<1>() = true; + if (const config& change_faction = data.child("change_faction")) { + int side_taken = find_user_side_index_by_id(change_faction["name"]); + if (side_taken != -1) { + import_user(PLAYER, change_faction, sock, side_taken); update_and_send_diff(); } } - if (const config &c = data.child("observer")) { - const t_string &observer_name = c["name"]; - if (!observer_name.empty()) { - connected_user_list::iterator player = - find_player_by_id(observer_name); - if (player == users_.end()) { - users_.push_back(connected_user(observer_name, CNTR_NETWORK, - sock)); - - result.get<1>() = true; - - update_and_send_diff(); - } - } + if (const config& observer = data.child("observer")) { + import_user(OBSERVER, observer, sock); + update_and_send_diff(); } - if (const config &c = data.child("observer_quit")) { - const t_string &observer_name = c["name"]; + if (const config& observer = data.child("observer_quit")) { + const t_string& observer_name = observer["name"]; if (!observer_name.empty()) { - connected_user_list::iterator player = - find_player_by_id(observer_name); - if (player != users_.end() && - find_player_side_index_by_id(observer_name) == -1) { + const int user_index = find_user_index_by_id(observer_name); + if (user_index != -1 && + find_user_side_index_by_id(observer_name) == -1) { - users_.erase(player); - - result.get<1>() = true; + connected_users_.erase(connected_users_.begin() + user_index); + update_side_controller_options(); update_and_send_diff(); } @@ -588,7 +591,7 @@ network_res_tuple connect_engine::process_network_data(const config& data, return result; } -int connect_engine::process_network_error(network::error& error) +bool connect_engine::process_network_error(network::error& error) { // If the problem isn't related to any specific connection, // it's a general error and we should just re-throw the error. @@ -599,25 +602,26 @@ int connect_engine::process_network_error(network::error& error) throw network::error(error.message); } - int res = -1; + bool res = false; // A socket has disconnected. Remove it, and resets its side. - connected_user_list::iterator user; - for(user = users_.begin(); user != users_.end(); ++user) { - if (user->connection == error.socket) { - int side_index = find_player_side_index_by_id(user->name); + int user_index = 0; + BOOST_FOREACH(connected_user& user, connected_users_) { + if (user.connection == error.socket) { + int side_index = find_user_side_index_by_id(user.name); if (side_index != -1) { - side_engines_[side_index]-> - reset((local_players_only_) ? CNTR_LOCAL : CNTR_NETWORK); - res = side_index + 1; - } else { - res = 0; + side_engines_[side_index]->reset(); } - users_.erase(user); + res = true; + + connected_users_.erase(connected_users_.begin() + user_index); + update_side_controller_options(); break; } + + user_index++; } // Now disconnect the socket. @@ -625,7 +629,7 @@ int connect_engine::process_network_error(network::error& error) // If there have been changes to the positions taken, // then notify other players. - if (res != -1) { + if (res) { update_and_send_diff(); } @@ -645,20 +649,25 @@ void connect_engine::process_network_connection(const network::connection sock) } } -connected_user_list::iterator - connect_engine::find_player_by_id(const std::string& id) +int connect_engine::find_user_index_by_id(const std::string& id) { - connected_user_list::iterator itor; - for (itor = users_.begin(); itor != users_.end(); ++itor) { - if (itor->name == id) { + size_t i = 0; + BOOST_FOREACH(const connected_user& user, connected_users_) { + if (user.name == id) { break; } + + i++; } - return itor; + if (i >= connected_users_.size()) { + return -1; + } + + return i; } -int connect_engine::find_player_side_index_by_id(const std::string& id) const +int connect_engine::find_user_side_index_by_id(const std::string& id) const { size_t i = 0; BOOST_FOREACH(side_engine_ptr side, side_engines_) { @@ -676,33 +685,43 @@ int connect_engine::find_player_side_index_by_id(const std::string& id) const return i; } +void connect_engine::update_side_controller_options() +{ + BOOST_FOREACH(side_engine_ptr side, side_engines_) { + side->update_controller_options(); + } +} + side_engine::side_engine(const config& cfg, connect_engine& parent_engine, const int index) : cfg_(cfg), parent_(parent_engine), - mp_controller_(CNTR_NETWORK), + controller_(CNTR_NETWORK), + current_controller_index_(0), available_factions_(), choosable_factions_(), choosable_leaders_(), choosable_genders_(), + controller_options_(), current_faction_(NULL), current_leader_("null"), current_gender_("null"), - ready_for_start_(false), allow_player_(cfg["controller"] == "ai" && cfg["allow_player"].empty() ? false : cfg["allow_player"].to_bool(true)), allow_changes_(cfg["allow_changes"].to_bool(true)), + leader_id_(cfg["id"]), + save_id_(cfg["save_id"]), + current_player_(cfg["current_player"]), index_(index), team_(0), color_(index), gold_(cfg["gold"].to_int(100)), income_(cfg["income"]), - id_(cfg["id"]), player_id_(cfg["player_id"]), - save_id_(cfg["save_id"]), - current_player_(cfg["current_player"]), ai_algorithm_() { + update_controller_options(); + // Tweak the controllers. if (cfg["controller"] == "human_ai" || cfg["controller"] == "network_ai") { @@ -710,16 +729,17 @@ side_engine::side_engine(const config& cfg, connect_engine& parent_engine, cfg_["controller"] = "ai"; } if (allow_player_ && !parent_.params_.saved_game) { - mp_controller_ = (parent_.local_players_only_) ? CNTR_LOCAL : - CNTR_NETWORK; + set_controller(parent_.default_controller_); + } else if (!current_player_.empty()) { + set_controller(CNTR_RESERVED); } else { size_t i = CNTR_NETWORK; if (!allow_player_) { if (cfg["controller"] == "null") { - mp_controller_ = CNTR_EMPTY; + set_controller(CNTR_EMPTY); } else { cfg_["controller"] = controller_names[CNTR_COMPUTER]; - mp_controller_ = CNTR_COMPUTER; + set_controller(CNTR_COMPUTER); } } else { if (cfg["controller"] == "network" || @@ -730,7 +750,7 @@ side_engine::side_engine(const config& cfg, connect_engine& parent_engine, for (; i != CNTR_LAST; ++i) { if (cfg["controller"] == controller_names[i]) { - mp_controller_ = static_cast(i); + set_controller(static_cast(i)); break; } } @@ -801,13 +821,13 @@ config side_engine::new_config() const if (!cfg_.has_attribute("side") || cfg_["side"].to_int() != index_ + 1) { res["side"] = index_ + 1; } - res["controller"] = controller_names[mp_controller_]; + res["controller"] = controller_names[controller_]; res["current_player"] = player_id_.empty() ? current_player_ : player_id_; - res["id"] = id_; + res["id"] = leader_id_; if (player_id_.empty()) { std::string description; - switch(mp_controller_) { + switch(controller_) { case CNTR_NETWORK: description = N_("(Vacant slot)"); @@ -921,7 +941,7 @@ config side_engine::new_config() const trimmed.remove_attribute(attribute); } - if (mp_controller_ != CNTR_COMPUTER) { + if (controller_ != CNTR_COMPUTER) { // Only override names for computer controlled players. trimmed.remove_attribute("user_description"); } @@ -934,66 +954,83 @@ config side_engine::new_config() const bool side_engine::ready_for_start() const { - // Sides without players are always ready. if (!allow_player_) { + // Sides without players are always ready. return true; } - // The host and the AI are always ready. - if ((mp_controller_ == mp::CNTR_COMPUTER) || - (mp_controller_ == mp::CNTR_EMPTY) || - (mp_controller_ == mp::CNTR_LOCAL)) { + if ((controller_ == mp::CNTR_COMPUTER) || + (controller_ == mp::CNTR_EMPTY) || + (controller_ == mp::CNTR_LOCAL)) { return true; } - return ready_for_start_; -} - -bool side_engine::available(const std::string& name) const -{ - if (name.empty()) { - return allow_player_ && ((mp_controller_ == CNTR_NETWORK && - player_id_.empty()) || mp_controller_ == CNTR_RESERVED); + if (controller_ == CNTR_NETWORK && !player_id_.empty()) { + // Side is assigned to a network player. + return true; } - return allow_player_ && - ((mp_controller_ == CNTR_NETWORK && player_id_.empty()) || - (mp_controller_ == CNTR_RESERVED && current_player_ == name)); + return false; } -void side_engine::set_player_from_users_list(const std::string& player_id) +bool side_engine::available_for_user(const std::string& name) const { - connected_user_list::iterator i = parent_.find_player_by_id(player_id); - if (i != parent_.users_.end()) { - player_id_ = player_id; - mp_controller_ = i->controller; + if (!allow_player_) { + // Computer sides are never available for players. + return false; } + + if (controller_ == CNTR_NETWORK && player_id_.empty()) { + // Side is free and waiting for user. + return true; + } + if (controller_ == CNTR_RESERVED && name.empty()) { + // Side is still available to someone. + return true; + } + if (controller_ == CNTR_RESERVED && current_player_ == name) { + // Side is available only for the player with specific name. + return true; + } + + return false; } void side_engine::swap_sides_on_drop_target(const int drop_target) { const std::string target_id = parent_.side_engines_[drop_target]->player_id_; const mp::controller target_controller = - parent_.side_engines_[drop_target]->mp_controller_; + parent_.side_engines_[drop_target]->controller_; const std::string target_ai = parent_.side_engines_[drop_target]->ai_algorithm_; parent_.side_engines_[drop_target]->ai_algorithm_ = ai_algorithm_; if (player_id_.empty()) { - parent_.side_engines_[drop_target]->mp_controller_ = mp_controller_; + parent_.side_engines_[drop_target]->set_controller(controller_); } else { - parent_.side_engines_[drop_target]-> - set_player_from_users_list(player_id_); + const int user_index = parent_.find_user_index_by_id(player_id_); + if (user_index != -1) { + const connected_user& user = parent_.connected_users_[user_index]; + parent_.side_engines_[drop_target]->player_id_ = user.name; + parent_.side_engines_[drop_target]->set_controller( + parent_.default_controller_); + } } ai_algorithm_ = target_ai; if (target_id.empty()) { - mp_controller_ = target_controller; + set_controller(target_controller); player_id_ = ""; } else { - set_player_from_users_list(target_id); + const int user_index = parent_.find_user_index_by_id(target_id); + if (user_index != -1) { + const connected_user& user = parent_.connected_users_[user_index]; + parent_.side_engines_[drop_target]->player_id_ = user.name; + parent_.side_engines_[drop_target]->set_controller( + parent_.default_controller_); + } } } @@ -1104,16 +1141,10 @@ void side_engine::resolve_random() } } -void side_engine::reset(mp::controller controller) +void side_engine::reset() { player_id_.clear(); - mp_controller_ = controller; - - if (mp_controller_ == mp::CNTR_NETWORK || - mp_controller_ == mp::CNTR_RESERVED) { - - ready_for_start_ = false; - } + set_controller(parent_.default_controller_); if (!parent_.params_.saved_game) { set_current_faction(choosable_factions_[0]); @@ -1122,22 +1153,58 @@ void side_engine::reset(mp::controller controller) } } -void side_engine::import_network_user(const config& data) +void side_engine::place_user(const config& data) { - if (mp_controller_ == CNTR_RESERVED || parent_.params_.saved_game) { - ready_for_start_ = true; - } - player_id_ = data["name"].str(); - mp_controller_ = CNTR_NETWORK; + set_controller(parent_.default_controller_); - BOOST_FOREACH(const config* faction, choosable_factions_) { - if ((*faction)["id"] == data["faction"]) { - set_current_faction(faction); + if (data.has_attribute("faction")) { + // Network user's data carry information about chosen + // faction, leader and genders. + BOOST_FOREACH(const config* faction, choosable_factions_) { + if ((*faction)["id"] == data["faction"]) { + set_current_faction(faction); + } } + set_current_leader(data["leader"]); + set_current_gender(data["gender"]); } - set_current_leader(data["leader"]); - set_current_gender(data["gender"]); +} + +void side_engine::update_controller_options() +{ + controller_options_ = parent_.default_controller_options_; + if (!save_id_.empty()) { + controller_options_.push_back( + std::make_pair(CNTR_RESERVED, _("Reserved"))); + } + + controller_options_.push_back(std::make_pair(CNTR_LAST, _("--give--"))); + + BOOST_FOREACH(const connected_user& user, parent_.connected_users_) { + controller_options_.push_back(std::make_pair( + parent_.default_controller_, user.name)); + } +} + +bool side_engine::controller_changed(const int selection) +{ + const mp::controller selected_cntr = controller_options_[selection].first; + if (selected_cntr == CNTR_LAST) { + return false; + } + + // Check if user was selected. If so assign a side to him/her. + // If not, make sure that no user is assigned to this side. + if (selected_cntr == parent_.default_controller_ && selection != 0) { + player_id_ = controller_options_[selection].second; + } else { + player_id_.clear(); + } + + set_controller(selected_cntr); + + return true; } void side_engine::set_current_faction(const config* current_faction) @@ -1187,22 +1254,16 @@ void side_engine::set_faction_commandline(const std::string& faction_name) void side_engine::set_controller_commandline(const std::string& controller_name) { - mp_controller_ = CNTR_LOCAL; + set_controller(CNTR_LOCAL); if (controller_name == "ai") { - mp_controller_ = CNTR_COMPUTER; + set_controller(CNTR_COMPUTER); } if (controller_name == "null") { - mp_controller_ = CNTR_EMPTY; + set_controller(CNTR_EMPTY); } - player_id_ = ""; -} - -void side_engine::set_ai_algorithm_commandline( - const std::string& algorithm_name) -{ - ai_algorithm_ = algorithm_name; + player_id_.clear(); } void side_engine::update_choosable_leaders() @@ -1220,4 +1281,27 @@ void side_engine::update_choosable_genders() parent_.params_.use_map_settings, parent_.params_.saved_game); } +void side_engine::set_controller(mp::controller controller) +{ + controller_ = controller; + + // Update current controller index. + int i = 0; + BOOST_FOREACH(const controller_option& option, controller_options_) { + if (option.first == controller) { + current_controller_index_ = i; + + if (player_id_.empty() || player_id_ == option.second) { + // Stop searching if no user is assigned to a side + // or the selected user is found. + break; + } + } + + i++; + } + + assert(current_controller_index_ < controller_options_.size()); +} + } // end namespace mp diff --git a/src/multiplayer_connect_engine.hpp b/src/multiplayer_connect_engine.hpp index 4d1b7e44db0..5a66f67abf3 100644 --- a/src/multiplayer_connect_engine.hpp +++ b/src/multiplayer_connect_engine.hpp @@ -19,28 +19,31 @@ #include "gamestatus.hpp" #include "multiplayer_ui.hpp" -#include - namespace mp { +enum controller { + CNTR_NETWORK = 0, + CNTR_LOCAL, + CNTR_COMPUTER, + CNTR_EMPTY, + CNTR_RESERVED, + CNTR_LAST +}; + class side_engine; struct connected_user { - connected_user(const std::string& name, mp::controller controller, - network::connection connection) : + connected_user(const std::string& name, network::connection connection) : name(name), - controller(controller), connection(connection) {}; std::string name; - mp::controller controller; network::connection connection; }; typedef boost::shared_ptr side_engine_ptr; -typedef std::vector connected_user_list; -typedef boost::tuple > network_res_tuple; +typedef std::pair controller_option; class connect_engine { @@ -49,10 +52,17 @@ public: const bool local_players_only, const bool first_scenario); ~connect_engine(); + enum USER_TYPE { HOST, PLAYER, OBSERVER }; + config* current_config(); void add_side_engine(side_engine_ptr engine); - void assign_side_for_host(); + // Should be called after all calls to 'add_side_engine()' + // have been made, so that everything could be initialized. + void init_after_side_engines_assigned(); + + void import_user(USER_TYPE user_type, const config& data, int sock, + int side_taken = -1); // Returns true if there are still sides available for this game. bool sides_available() const; @@ -66,29 +76,26 @@ public: void start_game(); void start_game_commandline(const commandline_options& cmdline_opts); - // Acts according to the given data and returns tuple - // holding information on what has changed. - // 0th - quit? - // 1st - update UI? - // 2nd - silent UI update? - // 3rd - side UIs to update. - network_res_tuple process_network_data(const config& data, + // Return pair first element specifies whether to leave the game + // and second element whether to silently update UI. + std::pair process_network_data(const config& data, const network::connection sock); - // Returns -1 if UI should not be updated at all, - // 0 if UI should be updated or (side index + 1) - // if some side's UI should be updated as well. - int process_network_error(network::error& error); + // Returns true, if UI should be updated. + bool process_network_error(network::error& error); void process_network_connection(const network::connection sock); - connected_user_list::iterator find_player_by_id(const std::string& id); + int find_user_index_by_id(const std::string& id); + // Returns the side which is taken by a given user, + // or -1 if none was found. + int find_user_side_index_by_id(const std::string& id) const; /* Setters & Getters */ const config& level() const { return level_; } const game_state& state() const { return state_; } - bool local_players_only() const { return local_players_only_; } - connected_user_list& users() { return users_; } + const std::vector& connected_users() const + { return connected_users_; } std::vector& team_names() { return team_names_; } std::vector& user_team_names() { return user_team_names_; } @@ -99,25 +106,24 @@ private: connect_engine(const connect_engine&); void operator=(const connect_engine&); - friend side_engine; + void update_side_controller_options(); - // Returns the side which is taken by a given player, - // or -1 if none was found. - int find_player_side_index_by_id(const std::string& id) const; + friend side_engine; config level_; game_state state_; const mp_game_settings& params_; - const bool local_players_only_; + const mp::controller default_controller_; const bool first_scenario_; std::vector side_engines_; std::vector era_factions_; std::vector team_names_; std::vector user_team_names_; - connected_user_list users_; + std::vector connected_users_; + std::vector default_controller_options_; }; class side_engine @@ -135,19 +141,20 @@ public: bool ready_for_start() const; // Returns true if this side is waiting for a network player and // players are allowed. - bool available(const std::string& name = "") const; - - void set_player_from_users_list(const std::string& player_id); + bool available_for_user(const std::string& name = "") const; void swap_sides_on_drop_target(const int drop_target); void resolve_random(); // Resets this side to its default state. - void reset(mp::controller controller); + void reset(); - // Imports data from the network into this side. - void import_network_user(const config& data); + // Place user into this side. + void place_user(const config& data); + + void update_controller_options(); + bool controller_changed(const int selection); void set_current_faction(const config* current_faction); void set_current_leader(const std::string& current_leader); @@ -158,8 +165,6 @@ public: // Game set up from command line helpers. void set_faction_commandline(const std::string& faction_name); void set_controller_commandline(const std::string& controller_name); - void set_ai_algorithm_commandline(const std::string& algorithm_name); - /* Setters & Getters */ @@ -169,12 +174,14 @@ public: { return choosable_leaders_; } const std::vector& choosable_genders() { return choosable_genders_; } + const std::vector& controller_options() + { return controller_options_; } const config& cfg() const { return cfg_; } const std::string& current_leader() const { return current_leader_; } const std::string& current_gender() const { return current_gender_; } - controller mp_controller() const { return mp_controller_; } - void set_mp_controller(controller mp_controller) - { mp_controller_ = mp_controller; } + mp::controller controller() const { return controller_; } + unsigned current_controller_index() const + { return current_controller_index_; } int index() const { return index_; } void set_index(int index) { index_ = index; } int team() const { return team_; } @@ -186,14 +193,11 @@ public: int income() const { return income_; } void set_income(int income) { income_ = income; } const std::string& player_id() const { return player_id_; } - void set_player_id(const std::string& player_id) { player_id_ = player_id; } const std::string& save_id() const { return save_id_; } const std::string& current_player() const { return current_player_; } const std::string& ai_algorithm() const { return ai_algorithm_; } void set_ai_algorithm(const std::string& ai_algorithm) { ai_algorithm_ = ai_algorithm; } - void set_ready_for_start(const bool ready_for_start) - { ready_for_start_ = ready_for_start; } bool allow_player() const { return allow_player_; } private: @@ -203,10 +207,13 @@ private: void update_choosable_leaders(); void update_choosable_genders(); + void set_controller(mp::controller controller); + config cfg_; connect_engine& parent_; - controller mp_controller_; + mp::controller controller_; + unsigned current_controller_index_; // All factions which could be played by a side (including Random). std::vector available_factions_; @@ -215,24 +222,24 @@ private: std::vector choosable_leaders_; std::vector choosable_genders_; + std::vector controller_options_; + const config* current_faction_; std::string current_leader_; std::string current_gender_; - bool ready_for_start_; - bool allow_player_; - bool allow_changes_; + const bool allow_player_; + const bool allow_changes_; + const std::string leader_id_; + const std::string save_id_; + const std::string current_player_; - // Configurable variables. int index_; int team_; int color_; int gold_; int income_; - std::string id_; std::string player_id_; - std::string save_id_; - std::string current_player_; std::string ai_algorithm_; }; diff --git a/src/multiplayer_ui.hpp b/src/multiplayer_ui.hpp index 54ebd530175..f9f8054672f 100644 --- a/src/multiplayer_ui.hpp +++ b/src/multiplayer_ui.hpp @@ -32,8 +32,6 @@ class game_state; namespace mp { -enum controller { CNTR_NETWORK = 0, CNTR_LOCAL, CNTR_COMPUTER, CNTR_EMPTY, CNTR_RESERVED, CNTR_LAST }; - const std::string random_enemy_picture("units/random-dice.png"); void check_response(network::connection res, const config& data);