35 #define LOG_CF LOG_STREAM(info, log_config) 36 #define ERR_CF LOG_STREAM(err, log_config) 39 #define DBG_MP LOG_STREAM(debug, log_mp_connect_engine) 40 #define LOG_MP LOG_STREAM(info, log_mp_connect_engine) 41 #define WRN_MP LOG_STREAM(warn, log_mp_connect_engine) 42 #define ERR_MP LOG_STREAM(err, log_mp_connect_engine) 45 #define LOG_NW LOG_STREAM(info, log_network) 60 , params_(state.mp_settings())
62 , campaign_info_(campaign_info)
63 , first_scenario_(first_scenario)
64 , force_lock_settings_()
86 std::vector<std::string> original_team_names;
87 std::string team_prefix(
_(
"Team") +
" ");
90 for(
config& side : sides) {
91 const std::string side_str = std::to_string(side_count);
97 if(team_name.
empty()) {
102 user_team_name = team_name;
105 bool add_team =
true;
109 return data.team_name == team_name.
str();
115 auto name_itor =
std::find(original_team_names.begin(), original_team_names.end(), team_name.
str());
119 if(name_itor == original_team_names.end()) {
120 original_team_names.push_back(team_name);
122 team_name =
"Team " + std::to_string(original_team_names.size());
124 team_name =
"Team " + std::to_string(std::distance(original_team_names.begin(), name_itor) + 1);
127 user_team_name = team_prefix + side_str;
174 if(lhs[
"random_faction"].to_bool() && !rhs[
"random_faction"].to_bool()) {
178 if(!lhs[
"random_faction"].to_bool() && rhs[
"random_faction"].to_bool()) {
225 user_data[
"name"] = name;
231 const std::string& username = data[
"name"];
232 assert(!username.empty());
243 bool side_assigned =
false;
244 if(side_taken >= 0) {
246 side_assigned =
true;
251 if(side->reserved_for() == username && side->player_id().empty() && side->controller() !=
CNTR_COMPUTER) {
252 side->place_user(data);
254 side_assigned =
true;
260 if(side_taken < 0 && !side_assigned) {
262 if(side->available_for_user(username) ||
264 side->place_user(data);
266 side_assigned =
true;
275 if(user_side->player_id() == username && !user_side->previous_save_id().empty()) {
277 if(side->player_id().empty() && side->previous_save_id() == user_side->previous_save_id()) {
278 side->place_user(data);
288 if(side->available_for_user()) {
298 DBG_MP <<
"updating level" << std::endl;
315 scenario_diff.
add_child(
"scenario_diff", std::move(diff));
328 if(!side->ready_for_start()) {
329 const int side_num = side->index() + 1;
330 DBG_MP <<
"not all sides are ready, side " <<
331 side_num <<
" not ready\n";
337 DBG_MP <<
"all sides are ready" << std::endl;
346 if(side->controller() !=
CNTR_EMPTY && side->allow_player()) {
373 std::vector<std::string> children;
375 children.push_back(
"village");
376 children.push_back(
"unit");
377 children.push_back(
"ai");
384 std::multimap<std::string, config> children;
386 for(
const std::string& children_to_swap : get_children_to_swap()) {
388 children.emplace(children_to_swap, child);
397 for(
const std::string& children_to_remove : get_children_to_swap()) {
398 cfg_.clear_children(children_to_remove);
401 for(std::pair<std::string, config> child_map : children) {
402 cfg_.add_child(child_map.first, child_map.second);
408 DBG_MP <<
"starting a new game" << std::endl;
414 std::vector<std::string> avoid_faction_ids;
417 if(
params_.random_faction_mode != mp_game_settings::RANDOM_FACTION_MODE::DEFAULT) {
419 if(!side2->flg().is_random_faction()) {
420 switch(
params_.random_faction_mode.v) {
421 case mp_game_settings::RANDOM_FACTION_MODE::NO_MIRROR:
422 avoid_faction_ids.push_back(side2->flg().current_faction()[
"id"].str());
424 case mp_game_settings::RANDOM_FACTION_MODE::NO_ALLY_MIRROR:
425 if(side2->team() == side->team()) {
426 avoid_faction_ids.push_back(side2->flg().current_faction()[
"id"].str());
435 side->resolve_random(rng, avoid_faction_ids);
443 std::vector<int> playable_sides;
445 if(side->allow_player() && side->allow_shuffle()) {
446 playable_sides.push_back(side->index());
451 for(
int i = playable_sides.size();
i > 1;
i--) {
453 int i_side = playable_sides[
i - 1];
455 if(i_side == j_side)
continue;
459 side_engines_[j_side] = side_engines_[i_side];
460 side_engines_[i_side] = tmp_side;
464 std::multimap<std::string, config> tmp_side_children = side_engines_[j_side]->get_side_children();
465 side_engines_[j_side]->set_side_children(side_engines_[i_side]->get_side_children());
466 side_engines_[i_side]->set_side_children(tmp_side_children);
470 int tmp_index = side_engines_[j_side]->index();
471 side_engines_[j_side]->set_index(side_engines_[i_side]->
index());
472 side_engines_[i_side]->set_index(tmp_index);
474 int tmp_team = side_engines_[j_side]->team();
475 side_engines_[j_side]->set_team(side_engines_[i_side]->
team());
476 side_engines_[i_side]->set_team(tmp_team);
481 config lock(
"stop_updates");
496 DBG_MP <<
"starting a new game in commandline mode" << std::endl;
498 typedef std::tuple<unsigned int, std::string> mp_option;
510 if(std::get<0>(option) == num) {
512 DBG_MP <<
"\tsetting side " << std::get<0>(option) <<
"\tfaction: " << std::get<1>(option) << std::endl;
514 side->set_faction_commandline(std::get<1>(option));
517 ERR_MP <<
"failed to set side " << std::get<0>(option) <<
" to faction " << std::get<1>(option) << std::endl;
527 if(std::get<0>(option) == num) {
528 DBG_MP <<
"\tsetting side " << std::get<0>(option) <<
529 "\tfaction: " << std::get<1>(option) << std::endl;
531 side->set_controller_commandline(std::get<1>(option));
538 std::string ai_algorithm = game_config.
child(
"ais")[
"default_ai_algorithm"].str();
539 side->set_ai_algorithm(ai_algorithm);
543 if(std::get<0>(option) == num) {
544 DBG_MP <<
"\tsetting side " << std::get<0>(option) <<
545 "\tfaction: " << std::get<1>(option) << std::endl;
547 side->set_ai_algorithm(std::get<1>(option));
554 side->resolve_random(rng);
570 if(std::get<0>(option) == side[
"side"].to_unsigned()) {
571 DBG_MP <<
"\tsetting side " << side[
"side"] <<
572 "\tai_config: " << std::get<1>(option) << std::endl;
574 side[
"ai_config"] = std::get<1>(option);
588 typedef std::tuple<unsigned int, std::string, std::string> mp_parameter;
593 if(std::get<0>(parameter) == side[
"side"].to_unsigned()) {
594 DBG_MP <<
"\tsetting side " << side[
"side"] <<
" " <<
595 std::get<1>(parameter) <<
": " << std::get<2>(parameter) << std::endl;
597 side[std::get<1>(parameter)] = std::get<2>(parameter);
612 DBG_MP <<
"leaving the game" << std::endl;
619 std::pair<bool, bool> result(std::make_pair(
false,
true));
621 if(data.
child(
"leave_game")) {
627 if(
const config& side_drop = data.
child(
"side_drop")) {
628 unsigned side_index = side_drop[
"side_num"].to_int() - 1;
637 side_to_drop->reset();
646 if(!data[
"side"].empty()) {
647 unsigned side_taken = data[
"side"].to_int() - 1;
650 const std::string name = data[
"name"];
653 response[
"failed"] =
true;
656 ERR_CF <<
"ERROR: No username provided with the side." << std::endl;
666 response[
"failed"] =
true;
667 response[
"message"] =
"The nickname '" + name +
668 "' is already in use.";
676 observer_quit.
add_child(
"observer_quit")[
"name"] = name;
688 if(
s->available_for_user()) {
695 if(side_taken >= side_engines_.size()) {
697 response[
"failed"] =
true;
702 kick[
"username"] = data[
"name"];
707 ERR_CF <<
"ERROR: Couldn't assign a side to '" <<
714 LOG_CF <<
"client has taken a valid position\n";
721 LOG_MP <<
"waiting to choose status = " <<
side_engines_[side_taken]->allow_changes() << std::endl;
722 result.second =
false;
724 LOG_NW <<
"sent player data\n";
726 ERR_CF <<
"tried to take illegal side: " << side_taken << std::endl;
729 response[
"failed"] =
true;
734 if(
const config& change_faction = data.
child(
"change_faction")) {
748 const std::string& observer_name =
observer[
"name"];
769 if(side->player_id() == id) {
776 if(i >= side_engines_.size()) {
808 const std::string& save_id = side->save_id();
809 const std::string& player_id = side->player_id();
810 if(!save_id.empty() && !player_id.empty()) {
811 side_users[save_id] = player_id;
821 std::set<std::string>
names;
823 const std::string& save_id = side->previous_save_id();
824 if(side_users.find(save_id) != side_users.end()) {
825 side->set_reserved_for(side_users[save_id]);
829 names.insert(side_users[save_id]);
832 side->update_controller_options();
837 for(
const std::string& name : names)
848 side->update_controller_options();
858 static std::set<std::string> empty;
870 , parent_(parent_engine)
872 , current_controller_index_(0)
873 , controller_options_()
874 , allow_player_(cfg[
"allow_player"].to_bool(true))
878 , color_(
std::min(index,
gamemap::MAX_PLAYERS - 1))
879 , gold_(cfg[
"gold"].to_int(100))
880 , income_(cfg[
"income"])
881 , reserved_for_(cfg[
"current_player"])
884 , chose_random_(cfg[
"chose_random"].to_bool(false))
885 , disallow_shuffle_(cfg[
"disallow_shuffle"].to_bool(false))
888 , waiting_to_choose_faction_(allow_changes_)
890 , color_id_(color_options_[color_])
895 "type",
cfg_[
"type"],
896 "gender",
cfg_[
"gender"],
897 "faction", cfg_[
"faction"],
898 "recruit", cfg_[
"recruit"],
902 ERR_CF <<
"found invalid side=" <<
cfg_[
"side"].to_int(
index_ + 1) <<
" in definition of side number " <<
index_ + 1 << std::endl;
908 const std::size_t side_cntr_index =
cfg_[
"controller"].to_int(-1) - 1;
915 ERR_MP <<
"controller=<number> is deperecated\n";
918 if(
cfg_[
"controller"] !=
"human" &&
cfg_[
"controller"] !=
"ai" &&
cfg_[
"controller"] !=
"null") {
927 cfg_[
"controller"] =
"ai";
930 if(
cfg_[
"controller"] ==
"null") {
932 }
else if(
cfg_[
"controller"] ==
"ai") {
946 unsigned team_name_index = 0;
958 WRN_MP <<
"In side_engine constructor: Could not find my team_name " << cfg[
"team_name"] <<
" among the mp connect engine's list of team names. I am being assigned to the first team. This may indicate a bug!" << std::endl;
960 team_ = team_name_index;
966 if(!given_color.empty()) {
991 return N_(
"Anonymous player");
996 return N_(
"Computer Player");
1008 res[
"side"] =
index_ + 1;
1011 if(
parent_.
params_.saved_game != mp_game_settings::SAVED_GAME_MODE::MIDGAME) {
1014 LOG_MP <<
"side_engine::new_config: side=" <<
index_ + 1 <<
" faction=" << faction[
"id"] <<
" recruit=" << faction[
"recruit"] <<
"\n";
1015 res[
"faction_name"] = faction[
"name"];
1016 res[
"faction"] = faction[
"id"];
1017 faction.
remove_attributes(
"id",
"name",
"image",
"gender",
"type",
"description");
1036 res[
"user_description"] =
t_string(desc,
"wesnoth");
1038 desc =
VGETTEXT(
"$playername $side", {
1039 {
"playername",
_(desc.c_str())},
1040 {
"side", res[
"side"].str()}
1046 if(res[
"name"].str().
empty() && !desc.empty()) {
1056 if(
parent_.
params_.saved_game == mp_game_settings::SAVED_GAME_MODE::MIDGAME) {
1078 res[
"current_player"] = desc;
1088 if(
parent_.
params_.saved_game != mp_game_settings::SAVED_GAME_MODE::MIDGAME) {
1098 leader = &side_unit;
1102 std::string leader_id = (*leader)[
"id"];
1105 if(!leader_id.empty()) {
1106 (*leader)[
"id"] = leader_id;
1118 LOG_MP <<
"side_engine::new_config: side=" <<
index_ + 1 <<
" type=" << (*leader)[
"type"] <<
" gender=" << (*leader)[
"gender"] <<
"\n";
1121 (*leader)[
"type"] =
"null";
1122 (*leader)[
"gender"] =
"null";
1167 res[
"gold"] =
gold_;
1174 res[
"name"] =
cfg_[
"name"];
1177 res[
"user_description"] =
cfg_[
"user_description"];
1236 if(
parent_.
params_.saved_game == mp_game_settings::SAVED_GAME_MODE::MIDGAME) {
1255 if(
parent_.
params_.saved_game != mp_game_settings::SAVED_GAME_MODE::MIDGAME) {
1263 data[
"name"] = name;
1273 if(data[
"change_faction"].to_bool() && contains_selection) {
1363 if(controller_name ==
"ai") {
1367 if(controller_name ==
"null") {
1375 const std::string& name,
const std::string& controller_value)
1378 cfg_[
"controller"] != controller_value) {
bool ready_for_start() const
void send_data(const configr_of &request)
bool empty() const
Tests for an attribute that either was never set or was set to "".
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
std::string join_map(const T &v, const std::string &major=",", const std::string &minor=":")
void save_reserved_sides_information()
void set_waiting_to_choose_status(bool status)
boost::optional< std::string > multiplayer_turns
Non-empty if –turns was given on the command line. Dependent on –multiplayer.
void clear_children(T... keys)
void append(const config &cfg)
Append data from another config object to this one.
const std::string & current_gender() const
static std::string get_side_color_id_from_config(const config &cfg)
void import_user(const std::string &name, const bool observer, int side_taken=-1)
std::vector< side_engine_ptr > side_engines_
Variant for storing WML attributes.
void load_previous_sides_users()
bool has_attribute(config_key_type key) const
void add_controller_option(ng::controller controller, const std::string &name, const std::string &controller_value)
std::set< std::string > connected_players
players and observers
void resolve_random(randomness::mt_rng &rng, const std::vector< std::string > &avoid)
int compare(const std::string &s1, const std::string &s2)
Case-sensitive lexicographical comparison.
static lg::log_domain log_config("config")
config initial_level_config(saved_game &state)
const std::string get_ignored_delim()
child_itors child_range(config_key_type key)
void place_user(const std::string &name)
void remove_attributes(T... keys)
void set_faction_commandline(const std::string &faction_name)
uint32_t get_next_random()
Get a new random number.
bool multiplayer_ignore_map_settings
True if –ignore-map-settings was given at the command line. Do not use map settings.
void set_controller_commandline(const std::string &controller_name)
bool is_normal_mp_game() const
std::string ai_algorithm_
static void add_mod_ai_from_config(config::const_child_itors configs)
void set_current_leader(const unsigned index)
std::vector< team_data_pod > team_data_
bool receive_data(config &result)
void set_current_gender(const unsigned index)
std::map< std::string, std::string > map_split(const std::string &val, char major, char minor, int flags, const std::string &default_value)
Splits a string based on two separators into a map.
void level_to_gamestate(const config &level, saved_game &state)
const bool allow_changes_
config new_config() const
void remove_attribute(config_key_type key)
std::vector< controller_option > controller_options_
boost::optional< std::vector< std::pair< unsigned int, std::string > > > multiplayer_side
Non-empty if –side was given on the command line. Vector of pairs (side number, faction id)...
void resolve_random(randomness::mt_rng &rng, const std::vector< std::string > &avoid_faction_ids=std::vector< std::string >())
static void add_color_info(const config &v, bool build_defaults)
void update_and_send_diff(bool update_time_of_day=false)
bool available_for_user(const std::string &name="") const
config get_diff(const config &c) const
A function to get the differences between this object, and 'c', as another config object...
bool waiting_to_choose_faction_
void send_to_server(const config &cfg) const
std::string user_description() const
This class stores all the data for a single 'side' (in game nomenclature).
A small explanation about what's going on here: Each action has access to two game_info objects First...
static UNUSEDNOWARN std::string _(const char *str)
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
int find_user_side_index_by_id(const std::string &id) const
std::vector< side_engine_ptr > & side_engines()
boost::optional< std::vector< std::pair< unsigned int, std::string > > > multiplayer_ai_config
Non-empty if –ai-config was given on the command line. Vector of pairs (side number, value). Dependent on –multiplayer.
const std::string & current_leader() const
config & add_child_at(config_key_type key, const config &val, unsigned index)
void set_controller(ng::controller controller)
const config * default_leader_cfg() const
Encapsulates the map of the game.
std::vector< std::string > color_options_
void send_level_data() const
const bool first_scenario_
ng::controller controller_
std::vector< const config * > era_factions_
boost::optional< std::vector< std::pair< unsigned int, std::string > > > multiplayer_algorithm
Non-empty if –algorithm was given on the command line. Vector of pairs (side number, value). Dependent on –multiplayer.
mp_campaign_info * campaign_info_
const ng::controller default_controller_
bool receive_from_server(config &dst) const
std::vector< std::string > get_children_to_swap()
void set_current_faction(const unsigned index)
const config & current_faction() const
config * current_config()
static void add_era_ai_from_config(const config &game_config)
void update_controller_options()
Game configuration data as global variables.
static map_location::DIRECTION s
std::vector< std::string > names
std::string reserved_for_
static lg::log_domain log_network("network")
connect_engine(saved_game &state, const bool first_scenario, mp_campaign_info *campaign_info)
boost::optional< std::vector< std::tuple< unsigned int, std::string, std::string > > > multiplayer_parm
Non-empty if –parm was given on the command line. Vector of pairs (side number, parm name...
std::string to_serialized() const
void update_current_controller_index()
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
static int sort(lua_State *L)
unsigned current_controller_index_
std::shared_ptr< side_engine > side_engine_ptr
config & add_child(config_key_type key)
std::pair< ng::controller, std::string > controller_option
const std::set< std::string > & connected_users() const
std::set< std::string > & connected_users_rw()
std::string user_team_name
wesnothd_connection & connection
bool find(E event, F functor)
Tests whether an event handler is available.
game_classification & classification()
boost::iterator_range< child_iterator > child_itors
Managing the AIs configuration - headers.
const bool controller_lock_
Standard logging facilities (interface).
bool controller_changed(const int selection)
void set_side_children(std::multimap< std::string, config > children)
static lg::log_domain log_mp_connect_engine("mp/connect/engine")
const mp_game_settings & params_
const config & child_or_empty(config_key_type key) const
Returns the first child with the given key, or an empty config if there is none.
std::vector< std::string > default_colors
ng::controller controller() const
bool can_start_game() const
A config object defines a single node in a WML file, with access to child nodes.
void start_game_commandline(const commandline_options &cmdline_opts, const config &game_config)
static const std::array< std::string, 5 > controller_names
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
static const config & get_ai_config_for(const std::string &id)
Return the config for a specified ai.
boost::optional< std::vector< std::pair< unsigned int, std::string > > > multiplayer_controller
Non-empty if –controller was given on the command line. Vector of pairs (side number, controller). Dependent on –multiplayer.
side_engine(const config &cfg, connect_engine &parent_engine, const int index)
std::multimap< std::string, config > get_side_children()
bool sides_available() const
static std::string controller_name(const team &t)
std::pair< bool, bool > process_network_data(const config &data)
std::string str(const std::string &fallback="") const
bool force_lock_settings_
void update_side_controller_options()