29 #include <boost/coroutine/exceptions.hpp>
32 #define ERR_GAME LOG_STREAM(err, log_server)
33 #define WRN_GAME LOG_STREAM(warn, log_server)
34 #define LOG_GAME LOG_STREAM(info, log_server)
35 #define DBG_GAME LOG_STREAM(debug, log_server)
38 #define WRN_CONFIG LOG_STREAM(warn, log_config)
45 res.push_back(span.
to_int());
49 template<
typename TResult,
typename TConvert>
52 std::vector<TResult> res;
57 while(i2 != val.
end()) {
81 const std::string& name,
83 const std::string& replay_save_path)
101 , description_(nullptr)
102 , description_updated_(false)
104 , current_side_index_(0)
105 , next_side_index_(0)
107 , all_observers_muted_(false)
110 , players_not_advanced_()
112 , save_replays_(save_replays)
113 , replay_save_path_(replay_save_path)
115 , last_choice_request_id_(-1)
116 , is_queue_game_(is_queue_game)
135 }
catch(
const boost::coroutines::detail::forced_unwind&) {
136 ERR_GAME <<
"Caught forced_unwind in game destructor!";
148 ERR_GAME <<
"no [multiplayer] found. Returning root";
188 return iter->info().name();
195 for(
auto user : users) {
200 list += user->info().name();
210 DBG_GAME <<
"****\n Performing controller tweaks. sides = ";
216 for(
unsigned side_index = 0; side_index < sides.size(); ++side_index) {
219 if(side[
"controller"] != side_controller::none) {
222 std::stringstream
msg;
223 msg <<
"Side " << side_index + 1
224 <<
" had no controller during controller tweaks! The host was assigned control.";
245 std::stringstream
msg;
246 msg <<
"Side " << side_index + 1 <<
" had no controller AFTER controller tweaks! Ruh Roh!";
264 DBG_GAME <<
"****\n Starting game. sides = ";
274 const bool save = multiplayer[
"savegame"].to_bool();
276 << starter->client_ip() <<
"\t" << starter->name() <<
"\t"
277 << (advance ?
"advanced" :
"started") << (save ?
" reloaded" :
"") <<
" game:\t\"" <<
name_ <<
"\" (" <<
id_
279 <<
". Settings: map: " << multiplayer[
"mp_scenario"]
280 <<
"\tera: " << multiplayer[
"mp_era"]
281 <<
"\tXP: " << multiplayer[
"experience_modifier"]
282 <<
"\tGPV: " << multiplayer[
"mp_village_gold"]
283 <<
"\tfog: " << multiplayer[
"mp_fog"]
284 <<
"\tshroud: " << multiplayer[
"mp_shroud"]
285 <<
"\tobservers: " << multiplayer[
"observer"]
286 <<
"\tshuffle: " << multiplayer[
"shuffle_sides"]
287 <<
"\ttimer: " << multiplayer[
"mp_countdown"]
288 << (multiplayer[
"mp_countdown"].to_bool()
289 ?
"\treservoir time: " + multiplayer[
"mp_countdown_reservoir_time"].to_string()
290 +
"\tinit time: " + multiplayer[
"mp_countdown_init_time"].to_string()
291 +
"\taction bonus: " + multiplayer[
"mp_countdown_action_bonus"].to_string()
292 +
"\tturn bonus: " + multiplayer[
"mp_countdown_turn_bonus"].to_string()
296 for(
unsigned side_index = 0; side_index < sides.size(); ++side_index) {
299 if(side[
"controller"] != side_controller::none) {
300 if(side_index >=
sides_.size()) {
305 std::stringstream
msg;
306 msg <<
"Side " << side_index + 1
307 <<
" has no controller but should! The host needs to assign control for the game to proceed past "
323 turn = lexical_cast_default<int>((*snapshot)[
"turn_at"], 1);
324 side = lexical_cast_default<int>((*snapshot)[
"playing_team"], 0);
325 if((*snapshot)[
"init_side_done"].to_bool(
false)) {
330 LOG_GAME <<
"Reload from turn: " << turn <<
". Current side is: " << side + 1 <<
". Next side is: " << next_side + 1;
349 const std::size_t side_index = (*side)[
"side"].to_int() - 1;
352 if(side_index >=
sides_.size()) {
385 if(((*side)[
"controller"] == side_controller::human || (*side)[
"controller"] == side_controller::reserved)
386 && (*side)[
"current_player"] == user->name().c_str()) {
396 if((*side)[
"controller"] == side_controller::human) {
403 DBG_GAME <<
"take_side: there are no more sides available";
446 for(
auto iter : users) {
448 bool side_found =
false;
449 for(
unsigned side_index = 0; side_index < level_sides.size(); ++side_index) {
460 if(player_id == iter->info().name().c_str()) {
469 sides_[side_index] = iter;
494 DBG_GAME <<
"transfer_side_control...";
502 const unsigned int side_num = cfg[
"side"].to_int();
503 if(side_num < 1 || side_num >
sides_.size()) {
504 std::ostringstream
msg;
505 msg <<
"The side number has to be between 1 and " <<
sides_.size() <<
".";
516 auto old_player =
sides_[side_num - 1];
517 const std::string& controller_type = cfg[
"to"].to_string();
519 const std::string old_player_name = old_player ?
username(*old_player) :
"null";
522 if(newplayer_name.
empty()) {
523 std::stringstream
msg;
524 msg <<
"Received invalid [change_controller] with no player= attribute specified";
532 std::stringstream
msg;
533 msg <<
"You can't give away side " << side_num <<
". It's controlled by '" << old_player_name <<
"' not you.";
540 auto newplayer {
find_user(newplayer_name) };
543 if(!newplayer || !
is_member(*newplayer)) {
548 if(newplayer == old_player) {
554 std::stringstream
msg;
555 msg <<
"Side " << side_num <<
" is already controlled by " << newplayer_name <<
".";
565 sides_[side_num - 1].reset();
596 const std::size_t side_index,
player_iterator player,
const std::string& player_name,
const bool player_left)
600 const std::string& side = lexical_cast_default<std::string, std::size_t>(side_index + 1);
622 response->root().child(
"change_controller")->set_attr(
"is_local",
"yes");
629 const std::string& side = std::to_string(side_index + 1);
640 return response.
clone();
649 std::string message = owner_name +
" has been chosen as the new host.";
660 int available_slots = 0;
665 if(((*side)[
"allow_player"].to_bool(
true) ==
false) || (*side)[
"controller"] == side_controller::none) {
745 << (*user)->client_ip() <<
")\tin game:\t\"" <<
name_ <<
"\" (" <<
id_ <<
", " <<
db_id_ <<
")";
777 << (*user)->client_ip() <<
")\tin game:\t\"" <<
name_ <<
"\" (" <<
id_ <<
", " <<
db_id_ <<
")";
802 }
else if(user == kicker) {
805 }
else if((*user)->info().is_moderator()) {
811 << (*user)->client_ip() <<
")\tfrom game:\t\"" <<
name_ <<
"\" (" <<
id_ <<
", " <<
db_id_ <<
")";
834 }
else if(user == banner) {
840 }
else if((*user)->info().is_moderator()) {
846 << (*user)->client_ip() <<
")\tfrom game:\t\"" <<
name_ <<
"\" (" <<
id_ <<
", " <<
db_id_ <<
")";
848 bans_.push_back((*user)->client_ip());
884 << unbanner->client_ip() <<
"\t" << unbanner->info().name()
885 <<
"\tunbanned: " <<
username <<
" (" << (*user)->client_ip() <<
")\tfrom game:\t\"" <<
name_ <<
"\" ("
897 message->set_attr_dup(
"sender", user->info().name().c_str());
908 const bool is_host = user ==
owner_;
912 const std::size_t from_side_index = command[
"from_side"].to_int() - 1;
915 if(command[
"from_side"] ==
"server") {
924 if(command.
child(
"init_side")) {
941 if(command.
child(
"speak")) {
944 if(command.
child(
"surrender")) {
964 command.
child(
"label") ||
965 command.
child(
"clear_labels") ||
966 command.
child(
"rename") ||
967 command.
child(
"countdown_update")
983 bool turn_ended =
false;
988 bool repackage =
false;
990 std::vector<int> marked;
995 DBG_GAME <<
"game " <<
id_ <<
", " <<
db_id_ <<
" received [" << (*command).first_child() <<
"] from player '" <<
username(user)
1001 std::stringstream
msg;
1002 msg <<
"Removing illegal command '" << (*command).first_child().to_string() <<
"' from: " <<
username(user)
1008 marked.push_back(
index - marked.size());
1009 }
else if((*command).child(
"speak")) {
1025 const std::size_t side_index = speak[
"side"].to_int() - 1;
1027 if(side_index >=
sides_.size() ||
sides_[side_index] != user) {
1032 speak.
set_attr_dup(
"side", lexical_cast_default<std::string>(
s -
sides_.begin() + 1).c_str());
1036 }
else if (command->child(
"surrender")) {
1037 std::size_t side_index = 0;
1046 if(side_index <
sides_.size()) {
1048 std::string playername;
1053 auto new_side_index = (side_index + 1) %
sides_.size();
1054 auto new_owner =
sides_[new_side_index];
1056 new_side_index = (new_side_index + 1) %
sides_.size();
1057 if(new_side_index == side_index) {
1058 ERR_GAME <<
"Ran out of sides to surrender to.";
1061 new_owner =
sides_[new_side_index];
1074 end_turn(endturn[
"next_player_number"].to_int());
1075 }
else if(command->child(
"init_side")) {
1083 for(
const int j : marked) {
1099 if(speak ==
nullptr) {
1100 auto mdata = std::make_unique<simple_wml::document>();
1116 auto message = std::make_unique<simple_wml::document>();
1119 message_turn_command.
set_attr(
"undo",
"no");
1122 if(to_sides.
empty()) {
1140 std::stringstream stream;
1141 stream << std::setfill(
'0') << std::setw(
sizeof(uint32_t) * 2) << std::hex << seed;
1143 auto mdata = std::make_unique<simple_wml::document>();
1148 random_seed.
set_attr_dup(
"new_seed", stream.str().c_str());
1151 command.
set_attr(
"from_side",
"server");
1152 command.
set_attr(
"dependent",
"yes");
1167 const std::size_t side_index = req[
"side"].to_int() - 1;
1171 if(!new_controller) {
1173 "Could not handle [request_choice] [change_controller] with invalid controller '" + req[
"new_controller"].to_string() +
"'");
1177 if(!old_controller) {
1179 "Could not handle [request_choice] [change_controller] with invalid controller '" + req[
"old_controller"].to_string() +
"'");
1185 "Found unexpected old_controller= '" +
side_controller::get_string(*old_controller) +
"' in [request_choice] [change_controller]");
1188 if(side_index >=
sides_.size()) {
1190 "Could not handle [request_choice] [change_controller] with invalid side '" + req[
"side"].to_string() +
"'");
1194 const bool was_null = this->
side_controllers_[side_index] == side_controller::type::none;
1195 const bool becomes_null = new_controller == side_controller::type::none;
1198 assert(!
sides_[side_index]);
1203 sides_[side_index].reset();
1208 auto mdata = std::make_unique<simple_wml::document>();
1214 change_controller_wml.
set_attr(
"is_local",
"yes");
1217 command.
set_attr(
"from_side",
"server");
1218 command.
set_attr(
"dependent",
"yes");
1224 change_controller_wml.
set_attr(
"is_local",
"no");
1253 int request_id = lexical_cast_default<int>(
data[
"request_id"], -10);
1259 DBG_GAME <<
"answering choice request " << request_id <<
" by player "
1260 << user->info().name();
1263 if(
data.child(
"random_seed")) {
1267 }
else if(
data.child(
"add_side_wml")) {
1284 std::size_t
const side_index = wb_node[
"side"].to_int() - 1;
1286 if(side_index >=
sides_.size() ||
sides_[side_index] != user) {
1287 std::ostringstream
msg;
1288 msg <<
"Ignoring illegal whiteboard data, sent from user '" << user->info().name()
1289 <<
"' which had an invalid side '" << side_index + 1 <<
"' specified" << std::endl;
1291 const std::string& msg_str =
msg.str();
1309 const int num_turns = ctw_node[
"max"].to_int();
1318 assert(
static_cast<int>(this->
current_turn()) == current_turn);
1365 ERR_GAME <<
"ERROR: Player is already in this game.";
1373 bool became_observer =
false;
1385 became_observer =
true;
1393 user->info().name() +
" is now observing the game.",
player);
1397 observer_join.
root()
1406 <<
player->client_ip() <<
"\t" << user->info().
name() <<
"\tjoined game:\t\""
1409 user->info().mark_available(
id_,
name_);
1434 if(!clones.empty()) {
1436 user->info().name() +
" has the same IP as: " + clones);
1439 if(became_observer) {
1450 ERR_GAME <<
"ERROR: User is not in this game.";
1469 << user->client_ip()
1470 <<
"\t" << user->info().name()
1471 << ((game_ended && !(
observer && destruct)) ? (
started_ ?
"\tended" :
"\taborted") :
"\thas left")
1472 <<
" game:\t\"" <<
name_ <<
"\" (" <<
id_ <<
", " <<
db_id_ <<
")"
1474 ?
" at turn: " + lexical_cast_default<std::string, std::size_t>(
current_turn())
1477 << (
observer ?
" as an observer" :
"") << (disconnect ?
" and disconnected" :
"") <<
".";
1483 if(game_ended || destruct) {
1491 user->info().mark_available();
1498 + (disconnect ?
" has disconnected." :
" has left the game."),
player);
1507 bool ai_transfer =
false;
1511 for(
unsigned side_index = 0; side_index <
sides_.size(); ++side_index) {
1512 auto side =
sides_[side_index];
1526 DBG_GAME <<
"making the owner a player...";
1534 const std::string side_drop = lexical_cast_default<std::string, std::size_t>(side_index + 1);
1539 node_side_drop.
set_attr_dup(
"side_num", side_drop.c_str());
1582 assert(sender ==
owner_);
1585 if(user_ptr != sender) {
1601 DBG_GAME <<
"****\n loading next scenario for a client. sides info = ";
1627 for(
const auto& side_user :
sides_) {
1629 cfg_controller.
set_attr(
"is_local", side_user == user ?
"yes" :
"no");
1644 template<
typename Container>
1647 for(
const auto&
player : players) {
1661 utils::optional<player_iterator> exclude)
1663 std::vector<int> sides_vec = ::split<int>(sides, ::split_conv_impl);
1667 decltype(
players_) filtered_players;
1669 std::copy_if(
players_.begin(),
players_.end(), std::back_inserter(filtered_players),
1670 [
this, &sides_vec](
player_iterator user) { return controls_side(sides_vec, user); });
1677 for(
int side : sides) {
1678 std::size_t side_index = side - 1;
1691 const std::string ip = user->client_ip();
1694 for(
auto u : users) {
1695 if(ip == u->client_ip() && user != u) {
1696 clones += (clones.empty() ?
"" :
", ") + u->info().name();
1728 observer_quit.
root()
1756 history_.push_back(std::move(doc));
1758 WRN_CONFIG << __func__ <<
": simple_wml error: " <<
e.message;
1775 return !(isalnum(
c) ||
1795 std::stringstream
name;
1809 std::string replay_commands;
1820 std::stringstream replay_data;
1827 const bool has_old_replay =
level_.
child(
"replay") !=
nullptr;
1839 << (has_old_replay ?
"" :
"\t[command]\n\t\t[start]\n\t\t[/start]\n\t[/command]\n")
1840 << replay_commands <<
"[/replay]\n";
1842 std::string replay_data_str = replay_data.str();
1849 (*os) <<
replay.output_compressed(
true);
1855 WRN_CONFIG << __func__ <<
": simple_wml error: " <<
e.message;
1909 std::stringstream result;
1910 result <<
"game id: " <<
id_ <<
", " <<
db_id_ <<
"\n";
1913 result <<
"player: " << user->info().name().c_str() <<
"\n";
1917 result <<
"observer: " << user->info().name().c_str() <<
"\n";
1920 return result.str();
1925 std::stringstream result;
1926 result <<
"game id: " <<
id_ <<
", " <<
db_id_ <<
"\n";
1929 result <<
"\t\t level, server\n";
1933 <<
"side " << (*s)[
"side"].to_int()
1934 <<
" :\t" << (*s)[
"controller"].to_string()
1936 <<
"\t( " << (*s)[
"current_player"].to_string() <<
" )\n";
1939 return result.str();
1954 auto doc = std::make_unique<simple_wml::document>();
1973 if(docptr ==
nullptr) {
1985 msg.set_attr(
"id",
"server");
1986 msg.set_attr_dup(
"message", message);
1987 std::stringstream ss;
1989 msg.set_attr_dup(
"time", ss.str().c_str());
1993 msg.set_attr(
"sender",
"server");
1994 msg.set_attr_dup(
"message", message);
2005 return multiplayer.
has_attr(
"savegame") && multiplayer[
"savegame"].to_bool();
2015 if(side_index == -1) {
uint32_t get_next_random()
Get a new random number.
node & set_attr_dup(const char *key, const char *value)
std::unique_ptr< document > clone()
node * child(const char *name)
const string_span & attr(const char *key) const
void remove_child(const char *name, std::size_t index)
const child_list & children(const char *name) const
bool has_attr(const char *key) const
node & set_attr_int(const char *key, int value)
node * child(const char *name)
std::vector< node * > child_list
node & add_child(const char *name)
node & child_or_add(const char *name)
node & set_attr(const char *key, const char *value)
void copy_into(node &n) const
node & set_attr_dup(const char *key, const char *value)
std::string to_string() const
const char * begin() const
const char * const_iterator
void handle_controller_choice(const simple_wml::node &data)
Handle a request to change a side's controller.
utils::optional< player_iterator > ban_user(const simple_wml::node &ban, player_iterator banner)
Ban a user by name.
std::string debug_player_info() const
Helps debugging player and observer lists.
void send_user_list(utils::optional< player_iterator > exclude={})
Function to send a list of users to all clients.
void mute_all_observers()
Toggles whether all observers are muted or not.
utils::optional< player_iterator > get_side_player(size_t index) const
int current_side_index_
The index of the current side.
std::vector< std::unique_ptr< simple_wml::document > > history_
Replay data.
bool is_legal_command(const simple_wml::node &command, player_iterator user)
void clear_chat_history()
Clears the history of recorded chat WML documents.
std::vector< std::string > bans_
List of banned IPs.
bool is_owner(player_iterator player) const
void init_turn()
Function which should be called every time a player starts their turn (i.e.
void send_history(player_iterator sock) const
void process_change_turns_wml(simple_wml::document &data, player_iterator user)
Handles incoming [change_turns_wml] data.
bool is_current_player(player_iterator player) const
randomness::mt_rng rng_
A wrapper for mersenne twister rng which generates randomness for this game.
std::string has_same_ip(player_iterator user) const
Checks whether a user has the same IP as any other members of this game.
std::string name_
The name of the game.
void notify_new_host()
In case of a host transfer, notify the new host about its status.
void send_chat_history(player_iterator sock) const
void update_side_data()
Resets the side configuration according to the scenario data.
std::string replay_save_path_
Where to save the replay of this game.
void send_leave_game(player_iterator user) const
Tells a player to leave the game.
void handle_random_choice()
Send a randomly generated number to the requestor.
void set_termination_reason(const std::string &reason)
Sets the termination reason for this game.
void unban_user(const simple_wml::node &unban, player_iterator unbanner)
Unban a user by name.
std::vector< std::unique_ptr< simple_wml::document > > chat_history_
Replay chat history data.
void perform_controller_tweaks()
This is performed just before starting and before the [start_game] signal.
void record_data(std::unique_ptr< simple_wml::document > data)
Records a WML document in the game's history.
std::vector< side_controller::type > side_controllers_
A vector containiner the controller type for each side.
void send_server_message_to_all(const char *message, utils::optional< player_iterator > exclude={})
Sends a message to all players in this game that aren't excluded.
void handle_add_side_wml()
Adds a new, empty side owned by no one.
void clear_history()
Clears the history of recorded WML documents.
int current_turn_
The game's current turn.
bool save_replays_
Whether to save a replay of this game.
void send_muted_observers(player_iterator user) const
Sends a message either stating that all observers are muted or listing the observers that are muted.
void load_next_scenario(player_iterator user)
A user asks for the next scenario to advance to.
bool add_player(player_iterator player, bool observer=false)
Add a user to the game.
utils::optional< player_iterator > current_player() const
void new_scenario(player_iterator sender)
When the host sends the new scenario of a mp campaign.
std::string list_users(const user_vector &users) const
game(wesnothd::server &server, player_connections &player_connections, player_iterator host, bool is_queue_game, const std::string &name="", bool save_replays=false, const std::string &replay_save_path="")
const user_vector all_game_users() const
void send_data(simple_wml::document &data, utils::optional< player_iterator > exclude={})
Send data to all players and observers except those excluded.
void describe_slots()
Set the description to the number of available slots.
void start_game(player_iterator starter)
Starts the game (if a new game) or starts the next scenario of an MP campaign.
static simple_wml::node * starting_pos(simple_wml::node &data)
The non-const version.
void send_and_record_server_message(const char *message, utils::optional< player_iterator > exclude={})
Send data to all players in this game except 'exclude'.
std::vector< std::string > name_bans_
List of banned usernames.
bool remove_player(player_iterator player, const bool disconnect=false, const bool destruct=false)
Removes a user from the game.
int id_
This game's ID within wesnothd.
std::size_t current_side() const
utils::optional< player_iterator > find_user(const simple_wml::string_span &name)
Shortcut to a convenience function for finding a user by name.
bool process_turn(simple_wml::document &data, player_iterator user)
Handles [end_turn], repackages [commands] with private [speak]s in them and sends the data.
std::size_t current_turn() const
std::string username(player_iterator pl) const
void process_whiteboard(simple_wml::document &data, player_iterator user)
Handles incoming [whiteboard] data.
player_connections & player_connections_
int get_next_nonempty(int side_index) const
finds the first side starting at side_index that is non empty.
bool is_member(player_iterator player) const
int db_id_
Used for unique identification of games played in the database.
void reset_sides()
calculates the initial value for sides_, side_controllerds_, nsides_
void handle_choice(const simple_wml::node &data, player_iterator user)
Handle a choice requested by a client, such as changing a side's controller, if initiated by WML/lua.
user_vector muted_observers_
A vector of muted observers.
bool is_muted_observer(player_iterator player) const
std::set< const player_record * > players_not_advanced_
in multiplayer campaigns it can happen that some players are still in the previous scenario keep trac...
void save_replay()
Move the level information and recorded history into a replay file and save it.
void end_turn(int new_side)
Function which should be called every time a player ends their turn (i.e.
void transfer_side_control(player_iterator player, const simple_wml::node &cfg)
Lets a player owning a side give it to another player or observer.
void unmute_observer(const simple_wml::node &unmute, player_iterator unmuter)
Unmute an observer or unmute all currently muted observers if no name is given.
int next_side_index_
after [end_turn] was received, this contains the side for who we accept [init_side].
bool started_
Whether the game has been started or not.
utils::optional< player_iterator > kick_member(const simple_wml::node &kick, player_iterator kicker)
Kick a user from this game by name.
std::string debug_sides_info() const
Helps debugging controller tweaks.
std::string termination_
The reason the game ended.
const std::string & name() const
void send_data_sides(simple_wml::document &data, const simple_wml::string_span &sides, utils::optional< player_iterator > exclude={})
Sends a document to the provided list of sides.
const simple_wml::node::child_list & get_sides_list() const
simple_wml::node * description_
Pointer to the game's description in the games_and_users_list_.
user_vector observers_
A vector of observers (members not owning a side).
static int db_id_num
Incremented to retrieve a unique ID per wesnothd instance for game instances within the database.
const std::string get_scenario_id() const
void set_description(simple_wml::node *desc)
Set the game's description.
int num_turns_
The maximum number of turns before the game ends.
bool all_observers_muted_
Whether all observers should be treated as muted.
side_vector sides_
A vector of side owners.
int nsides_
Number of sides in the current scenario.
simple_wml::document level_
The current scenario data.
bool take_side(player_iterator user)
Figures out which side to take and tells that side to the game owner.
void change_controller(const std::size_t side_index, player_iterator player, const std::string &player_name, const bool player_left=true)
Send [change_controller] message to tell all clients the new controller's name or controller type (hu...
bool allow_observers() const
user_vector players_
A vector of players (members owning a side).
void send_observerquit(player_iterator observer)
bool send_taken_side(simple_wml::document &cfg, const simple_wml::node *side) const
Tell the host who owns a side.
int get_next_side_index() const
void send_server_message(const char *message, utils::optional< player_iterator > player={}, simple_wml::document *doc=nullptr) const
Send a server message to the specified player.
std::string password_
The password needed to join the game.
bool is_observer(player_iterator player) const
bool is_player(player_iterator player) const
int last_choice_request_id_
The ID of the last request received from a client.
const std::string & termination_reason() const
Provides the reason the game was ended.
bool controls_side(const std::vector< int > &sides, player_iterator player) const
Function which returns true if 'player' controls any of the sides specified in 'sides'.
void send_observerjoins(utils::optional< player_iterator > player={})
Send a document per observer in the game.
static int id_num
Incremented to retrieve a unique ID for game instances within wesnothd.
bool player_is_banned(player_iterator player, const std::string &name) const
std::string get_replay_filename()
void send_to_players(simple_wml::document &data, const Container &players, utils::optional< player_iterator > exclude={})
Send data to all players except those excluded.
void mute_observer(const simple_wml::node &mute, player_iterator muter)
Mute an observer or give a message of all currently muted observers if no name is given.
std::unique_ptr< simple_wml::document > change_controller_type(const std::size_t side_index, player_iterator player, const std::string &player_name)
Tell everyone else but the source player that the controller type changed.
void update_turn_data()
Set or update the current and max turn values in the game's description.
simple_wml::node * description_for_writing()
player_iterator owner_
The game host or later owner (if the host left).
void process_message(simple_wml::document &data, player_iterator user)
Sends an ingame message to all other players.
const std::string & name() const
void send_to_player(player_iterator player, simple_wml::document &data)
Declarations for File-IO.
static lg::log_domain log_server("server")
static lg::log_domain log_config("config")
New lexcical_cast header.
Standard logging facilities (interface).
General math utility functions.
constexpr T modulo(T num, int mod, T min=0)
auto serialize_timestamp(const std::chrono::system_clock::time_point &time)
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
std::unique_ptr< std::ostream > scoped_ostream
const std::string observer_team_name
observer team name used for observer team chat
std::string node_to_string(const node &n)
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
std::size_t erase(Container &container, const Value &value)
Convenience wrapper for using std::remove on a container.
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
void erase_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::remove_if on a container.
std::vector< std::string > split(const config_attribute_value &val)
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
void truncate_message(const simple_wml::string_span &str, simple_wml::node &message)
Function to ensure a text message is within the allowed length.
static bool is_invalid_filename_char(char c)
std::vector< player_iterator > user_vector
player_connections::const_iterator player_iterator
static const simple_wml::node & get_multiplayer(const simple_wml::node &root)
returns const so that operator [] won't create empty keys if not existent
bmi::multi_index_container< player_record, bmi::indexed_by< bmi::ordered_unique< bmi::tag< socket_t >, bmi::const_mem_fun< player_record, const any_socket_ptr, &player_record::socket > >, bmi::hashed_unique< bmi::tag< name_t >, bmi::const_mem_fun< player_record, const std::string &, &player_record::name > >, bmi::ordered_non_unique< bmi::tag< game_t >, bmi::const_mem_fun< player_record, int, &player_record::game_id > > > > player_connections
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
std::string filename
Filename.
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
static constexpr utils::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
static map_location::direction s