Refactor users related code (mp::connect).

This commit is contained in:
Andrius Silinskas 2013-08-12 16:19:31 +01:00
parent 055caebcf2
commit 3c75c21583
5 changed files with 410 additions and 381 deletions

View file

@ -35,6 +35,17 @@ static lg::log_domain log_mp_connect("mp/connect");
namespace mp {
std::vector<std::string> controller_options_names(
const std::vector<controller_option>& controller_options)
{
std::vector<std::string> 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<std::string>(), parent.combo_control_group_)),
combo_ai_algorithm_(parent.disp(), std::vector<std::string>()),
combo_faction_(parent.disp(), std::vector<std::string>()),
combo_leader_(parent.disp(), std::vector<std::string>()),
@ -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<std::string>& 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<ai::description*> &ais_list = parent_->ai_algorithms_;
std::vector<std::string> ais;
int i = 0;
BOOST_FOREACH(const ai::description *desc, ais_list){
std::vector<std::string> 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<bool, bool> 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<std::string> 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<std::string> 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();
}
}

View file

@ -31,6 +31,10 @@ namespace ai {
namespace mp {
// Helper function to retrieve controller names.
std::vector<std::string> controller_options_names(
const std::vector<controller_option>& 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<std::string>& 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<std::string> player_types_;
std::vector<std::string> player_teams_;
std::vector<std::string> player_colors_;
std::vector<ai::description*> ai_algorithms_;

View file

@ -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<bool, bool> 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<bool, bool> 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<mp::controller>(i);
set_controller(static_cast<mp::controller>(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

View file

@ -19,28 +19,31 @@
#include "gamestatus.hpp"
#include "multiplayer_ui.hpp"
#include <boost/tuple/tuple.hpp>
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> side_engine_ptr;
typedef std::vector<connected_user> connected_user_list;
typedef boost::tuple<bool, bool, bool, std::vector<int> > network_res_tuple;
typedef std::pair<mp::controller, std::string> 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<bool, bool> 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_user>& connected_users() const
{ return connected_users_; }
std::vector<std::string>& team_names() { return team_names_; }
std::vector<std::string>& 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_engine_ptr> side_engines_;
std::vector<const config*> era_factions_;
std::vector<std::string> team_names_;
std::vector<std::string> user_team_names_;
connected_user_list users_;
std::vector<connected_user> connected_users_;
std::vector<controller_option> 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<std::string>& choosable_genders()
{ return choosable_genders_; }
const std::vector<controller_option>& 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<const config*> available_factions_;
@ -215,24 +222,24 @@ private:
std::vector<std::string> choosable_leaders_;
std::vector<std::string> choosable_genders_;
std::vector<controller_option> 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_;
};

View file

@ -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);