The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
game.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "global.hpp"
16 
17 #include "filesystem.hpp"
18 #include "game_config.hpp" // game_config::observer_team_name
19 #include "log.hpp"
20 
21 #include "game.hpp"
22 #include "player_network.hpp"
24 #include "util.hpp"
25 
26 #include <sstream>
27 #include <iomanip>
28 
29 #include <cstdio>
30 
31 static lg::log_domain log_server("server");
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)
36 static lg::log_domain log_config("config");
37 #define WRN_CONFIG LOG_STREAM(warn, log_config)
38 
39 namespace
40 {
41  struct split_conv_impl
42  {
43  void operator()(std::vector<int>& res, const simple_wml::string_span& span)
44  {
45  if(!span.empty()) {
46  res.push_back(span.to_int());
47  }
48  }
49  };
50  template<typename TResult, typename TConvert>
51  std::vector<TResult> split(const simple_wml::string_span& val, TConvert conv, const char c = ',')
52  {
53  std::vector<TResult> res;
56 
57  while (i2 != val.end()) {
58  if (*i2 == c) {
59  conv(res, simple_wml::string_span(i1, i2));
60  ++i2;
61  i1 = i2;
62  } else {
63  ++i2;
64  }
65  }
66  conv(res, simple_wml::string_span(i1, i2));
67  return res;
68  }
69 }
70 
71 namespace wesnothd {
72 int game::id_num = 1;
73 
74 void game::missing_user(socket_ptr /*socket*/, const std::string& func) const
75 {
76  WRN_GAME << func << "(): Could not find user (socket:\t<some C++ pointer>"
77  << ") in player_info_ in game:\t\"" << name_ << "\" (" << id_ << ")\n";
78 }
79 
81  const std::string& name, bool save_replays,
82  const std::string& replay_save_path) :
83  player_connections_(player_connections),
84  id_(id_num++),
85  name_(name),
86  password_(),
87  owner_(host),
88  players_(),
89  observers_(),
90  muted_observers_(),
91  sides_(),
92  side_controllers_(),
93  nsides_(0),
94  started_(false),
95  level_(),
96  history_(),
97  description_(nullptr),
98  end_turn_(0),
99  num_turns_(0),
100  all_observers_muted_(false),
101  bans_(),
102  termination_(),
103  save_replays_(save_replays),
104  replay_save_path_(replay_save_path),
105  rng_(),
106  last_choice_request_id_(-1) /* or maybe 0 ? it shouldn't matter*/
107 {
108  assert(owner_);
109  players_.push_back(owner_);
111  if (iter == player_connections_.end()) {
112  missing_user(owner_, __func__);
113  return;
114  }
115  // Mark the host as unavailable in the lobby.
116  iter->info().mark_available(id_, name_);
117  iter->info().set_status(player::PLAYING);
118 }
119 
121 {
122  try {
123  save_replay();
124 
125  user_vector users = all_game_users();
126  for (user_vector::const_iterator u = users.begin(); u != users.end(); ++u) {
127  remove_player(*u, false, true);
128  }
129  clear_history();
130  } catch (...) {}
131 }
132 
133 /// returns const so that operator [] won't create empty keys if not existent
135 {
136  if(const simple_wml::node* multiplayer = root.child("multiplayer"))
137  return *multiplayer;
138  else
139  {
140  ERR_GAME << "no [multiplayer] found. Returning root\n";
141  return root;
142  }
143 }
144 
145 bool game::allow_observers() const {
146  return get_multiplayer(level_.root())["observer"].to_bool(true);
147 }
148 
150  return get_multiplayer(level_.root())["registered_users_only"].to_bool(true);
151 }
152 
154  return std::find(observers_.begin(),observers_.end(),player) != observers_.end();
155 }
156 
158  if (!is_observer(player)) {
159  return false;
160  }
161  if (all_observers_muted_) {
162  return true;
163  }
164  return std::find(muted_observers_.begin(), muted_observers_.end(), player)
165  != muted_observers_.end();
166 }
167 
168 bool game::is_player(const socket_ptr player) const {
169  return std::find(players_.begin(),players_.end(),player) != players_.end();
170 }
171 
172 namespace {
173 std::string describe_turns(int turn, int num_turns)
174 {
175  char buf[100];
176 
177  if(num_turns == -1) {
178  snprintf(buf, sizeof(buf), "%d/-", turn);
179  } else {
180  snprintf(buf, sizeof(buf), "%d/%d", turn, num_turns);
181  }
182  return buf;
183 }
184 
185 }//anon namespace
186 
188 {
189  const auto iter = player_connections_.find(player);
190  if(iter != player_connections_.end()) {
191  return iter->info().name();
192  }
193 
194  return "(unknown)";
195 }
196 
198 {
199  std::string list;
200 
201  for(const user_vector::value_type& user : users) {
202  const auto iter = player_connections_.find(user);
203  if (iter != player_connections_.end()) {
204  if (!list.empty()) list += ", ";
205  list += iter->info().name();
206  } else missing_user(user, func);
207  }
208 
209  return list;
210 }
211 
214 
215  DBG_GAME << "****\n Performing controller tweaks. sides = " << std::endl;
216  DBG_GAME << debug_sides_info() << std::endl;
217  DBG_GAME << "****" << std::endl;
218 
219  update_side_data(); // Necessary to read the level_ and get sides_, etc. updated to match
220 
221  for(simple_wml::node::child_list::const_iterator s = sides.begin(); s != sides.end(); ++s) {
222  if ((**s)["controller"] != "null") {
223  const size_t side_index = s - sides.begin();
224  if (sides_[side_index] == 0) {
225  sides_[side_index] = owner_;
226  std::stringstream msg;
227  msg << "Side " << side_index + 1 << " had no controller during controller tweaks! The host was assigned control.";
228  LOG_GAME << msg.str() << " (game id: " << id_ << ")\n";
230  }
231 
232  const auto user = player_connections_.find(sides_[side_index]);
233  std::string user_name = "null (server missing user)";
234  if (user == player_connections_.end()) {
235  missing_user(user->socket(), __func__);
236  } else {
237  user_name = username(user->socket());
238  }
239 
240  // Issue change_controller command, transfering this side to its owner with proper name and controller.
241  // Ensures that what the server now thinks is true is effected on all of the clients.
242  //
243  // We use the "player_left" field as follows. Normally change_controller sends one message to the owner,
244  // and one message to everyone else. In case that a player drops, the owner is gone and should not get
245  // a message, instead the host gets a [side_drop] message.
246  //
247  // In the server controller tweaks, we want to avoid sending controller change messages to the host.
248  // Doing this has the negative consequence that all of the AI side names are given the owners name.
249  // Therefore, if the side belongs to the host, we pass player_left = true, otherwise player_left = false.
250  change_controller(side_index, sides_[side_index], user_name , sides_[side_index] == owner_);
251 
252  //next line change controller types found in level_ to be what is appropriate for an observer at game start.
253  (*s)->set_attr("is_local", "no");
254 
255  if (sides_[side_index] == 0) {
256  std::stringstream msg;
257  msg << "Side " << side_index + 1 << " had no controller AFTER controller tweaks! Ruh Roh!";
258  LOG_GAME << msg.str() << " (game id: " << id_ << ")\n";
259  }
260  }
261  }
262 
263  update_side_data(); // this is the last time that update_side_data will actually run, as now the game will start and started_ will be true.
264 
265  //TODO: Does it matter that the server is telling the host to change a bunch of sides?
266  //According to playturn.cpp, the host should ignore all such messages. Still might be better
267  //not to send them at all, although not if it complicates the server code.
268 }
269 
270 
271 void game::start_game(const socket_ptr starter) {
273  DBG_GAME << "****\n Starting game. sides = " << std::endl;
274  DBG_GAME << debug_sides_info() << std::endl;
275  DBG_GAME << "****" << std::endl;
276 
277 
278  started_ = true;
279  // Prevent inserting empty keys when reading.
280  const simple_wml::node& multiplayer = get_multiplayer(level_.root());
281 
282  const bool save = multiplayer["savegame"].to_bool();
283  LOG_GAME << client_address(starter) << "\t"
284  << player_connections_.find(starter)->name() << "\t" << "started"
285  << (save ? " reloaded" : "") << " game:\t\"" << name_ << "\" (" << id_
286  // << ") with: " << list_users(players_, __func__) << ". Settings: map: " << s["id"]
287  << ") with: " << list_users(players_, __func__) << ". Settings: map: " << multiplayer["mp_scenario"]
288  // << "\tera: " << (s.child("era") ? (*s.child("era"))["id"] : "")
289  << "\tera: " << multiplayer["mp_era"]
290  << "\tXP: " << multiplayer["experience_modifier"]
291  << "\tGPV: " << multiplayer["mp_village_gold"]
292  << "\tfog: " << multiplayer["mp_fog"]
293  << "\tshroud: " << multiplayer["mp_shroud"]
294  << "\tobservers: " << multiplayer["observer"]
295  << "\tshuffle: " << multiplayer["shuffle_sides"]
296  << "\ttimer: " << multiplayer["mp_countdown"]
297  << (multiplayer["mp_countdown"].to_bool() ?
298  "\treservoir time: " + multiplayer["mp_countdown_reservoir_time"].to_string() +
299  "\tinit time: " + multiplayer["mp_countdown_init_time"].to_string() +
300  "\taction bonus: " + multiplayer["mp_countdown_action_bonus"].to_string() +
301  "\tturn bonus: " + multiplayer["mp_countdown_turn_bonus"].to_string() : "")
302  << "\n";
303 
304  for(simple_wml::node::child_list::const_iterator s = sides.begin(); s != sides.end(); ++s) {
305  if ((**s)["controller"] != "null") {
306  const size_t side_index = s - sides.begin();
307  if(side_index >= sides_.size()) {
308  continue;
309  }
310  if (sides_[side_index] == 0) {
311  std::stringstream msg;
312  msg << "Side " << side_index + 1 << " has no controller but should! The host needs to assign control for the game to proceed past that side's turn.";
313  LOG_GAME << msg.str() << " (game id: " << id_ << ")\n";
315  }
316  }
317  }
318 
319  DBG_GAME << "Number of sides: " << nsides_ << "\n";
320  int turn = 1;
321  int side = 0;
322  // Savegames have a snapshot that tells us which side starts.
323  if (const simple_wml::node* snapshot = level_.root().child("snapshot")) {
324  turn = lexical_cast_default<int>((*snapshot)["turn_at"], 1);
325  side = lexical_cast_default<int>((*snapshot)["playing_team"], 0);
326  LOG_GAME << "Reload from turn: " << turn
327  << ". Current side is: " << side + 1 << ".\n";
328  }
329  end_turn_ = (turn - 1) * nsides_ + side - 1;
330  num_turns_ = lexical_cast_default<int>((*starting_pos( level_.root()))["turns"], -1);
331 
332  end_turn();
333  clear_history();
334  // Send [observer] tags for all observers that are already in the game.
336 }
337 
339 {
340  started_ = false;
341  description_->set_attr("turn", "");
342 
344  describe_slots();
345 }
346 
347 bool game::send_taken_side(simple_wml::document& cfg, const simple_wml::node::child_list::const_iterator side) const
348 {
349  const size_t side_index = (**side)["side"].to_int() - 1;
350  //negative values are casted (int -> size_t) to very high values to this check will fail for them too.
351  if (side_index >= sides_.size()) return false;
352  if (sides_[side_index] != 0) return false;
353  // We expect that the host will really use our proposed side number. (He could do different...)
354  cfg.root().set_attr_dup("side", (**side)["side"]);
355 
356  // Tell the host which side the new player should take.
357  send_to_player(owner_, cfg);
358  return true;
359 }
360 
361 bool game::take_side(const socket_ptr user)
362 {
363  DBG_GAME << "take_side...\n";
364 
365  if (started_) return false;
366 
368  cfg.root().set_attr_dup("name", player_connections_.find(user)->name().c_str());
369 
370  //FIXME: It the client code (multiplayer.wait.cpp) the host code (connect_engine.cpp) and the server code (this file)
371  // Has this code to figure out a fitting side for new players, this is clearly too much.
372  // Check if we can figure out a fitting side.
374  for(simple_wml::node::child_list::const_iterator side = sides.begin(); side != sides.end(); ++side) {
375  if(((**side)["controller"] == "human" || (**side)["controller"] == "reserved")
376  && (**side)["current_player"] == player_connections_.find(user)->name().c_str())
377  {
378  if (send_taken_side(cfg, side)) return true;
379  }
380  }
381  // If there was no fitting side just take the first available.
382  for(simple_wml::node::child_list::const_iterator side = sides.begin(); side != sides.end(); ++side) {
383  if((**side)["controller"] == "human") {
384  if (send_taken_side(cfg, side)) return true;
385  }
386  }
387  DBG_GAME << "take_side: there are no more sides available\n";
388  //if we get here we couldn't find a side to take
389  return false;
390 }
392 {
393  side_controllers_.clear();
394  sides_.clear();
395  nsides_ = get_sides_list().size();
396  side_controllers_.resize(nsides_);
397  sides_.resize(nsides_);
398 }
399 
401 {
402  //added by iceiceice: since level_ will now reflect how an observer
403  //views the replay start position and not the current position, the sides_, side_controllers_,
404  //players_ info should not be updated from the level_ after the game has started.
405  //controller changes are now stored in the history, so an observer that joins will get up to
406  //date that way.
407  if (started_) return;
408 
409  DBG_GAME << "update_side_data...\n";
411  // Remember everyone that is in the game.
412  const user_vector users = all_game_users();
413  players_.clear();
414  observers_.clear();
415  reset_sides();
416  const simple_wml::node::child_list& level_sides = get_sides_list();
417 
418  // For each user:
419  // * Find the username.
420  // * Find the side this username corresponds to.
421  for (user_vector::const_iterator user = users.begin(); user != users.end(); ++user) {
422  auto iter = player_connections_.find(*user);
423  if (iter == player_connections_.end()) {
424  missing_user(*user, __func__);
425  continue;
426  }
427 
428  bool side_found = false;
429  for (simple_wml::node::child_list::const_iterator side = level_sides.begin();
430  side != level_sides.end(); ++side)
431  {
432  const size_t side_index = side - level_sides.begin();
433  if (side_index >= sides_.size()
434  || sides_[side_index] != 0) continue;
435 
436  const simple_wml::string_span& player_id = (**side)["player_id"];
437  const simple_wml::string_span& controller = (**side)["controller"];
438  if ( player_id == iter->info().name().c_str()) {
439  if(controller != "human" && controller != "ai") {
440  //we found invalid [side] data. Some message would be cool.
441  continue;
442  }
443  side_controllers_[side_index].parse(controller);
444  sides_[side_index] = *user;
445  side_found = true;
446  }
447  else if (*user == owner_ && (controller == "null")) {
448  //the *user == owner_ check has no effect,
449  //it's just an optimisation so that we only do this once.
450  side_controllers_[side_index].parse(controller);
451  }
452  }
453  if (side_found) {
454  players_.push_back(*user);
455  iter->info().set_status(player::PLAYING);
456  } else {
457  observers_.push_back(*user);
458  iter->info().set_status(player::OBSERVING);
459  }
460  }
462 }
463 
465  DBG_GAME << "transfer_side_control...\n";
466  if (!is_player(sock) && sock != owner_) {
467  send_server_message("You cannot change controllers: not a player.", sock);
468  return;
469  }
470 
471  // Check the side number.
472  const unsigned int side_num = cfg["side"].to_int();
473  if(side_num < 1 || side_num > sides_.size()) {
474  std::ostringstream msg;
475  msg << "The side number has to be between 1 and "
476  << sides_.size() << ".";
477  send_server_message(msg.str(), sock);
478  return;
479  }
480 
481  if (side_num > get_sides_list().size()) {
482  send_server_message("Invalid side number.", sock);
483  return;
484  }
485 
486  const simple_wml::string_span& newplayer_name = cfg["player"];
487  const socket_ptr old_player = sides_[side_num - 1];
488  const auto oldplayer = player_connections_.find(old_player);
489  if (oldplayer == player_connections_.end()) missing_user(old_player, __func__);
490  const std::string old_player_name = username(old_player);
491 
492  // Not supported anymore.
493  if (newplayer_name.empty()) {
494  std::stringstream msg;
495  msg << "Recived invalid [change_controller] with no player= attribute specified";
496  DBG_GAME << msg.str() << "\n";
497  send_server_message(msg.str(), sock);
498  return;
499  }
500 
501  // Check if the sender actually owns the side he gives away or is the host.
502  if (!(sock == old_player || sock == owner_)) {
503  std::stringstream msg;
504  msg << "You can't give away side " << side_num << ". It's controlled by '"
505  << old_player_name << "' not you.";
506  DBG_GAME << msg.str() << "\n";
507  send_server_message(msg.str(), sock);
508  return;
509  }
510  //find the player that is passed control
511  socket_ptr newplayer = find_user(newplayer_name);
512 
513  // Is he in this game?
514  if (player_connections_.find(newplayer) == player_connections_.end() || !is_member(newplayer)) {
515  send_server_message(newplayer_name.to_string() + " is not in this game", sock);
516  return;
517  }
518 
519  if (newplayer == old_player) {
520  std::stringstream msg;
521  msg << "That's already " << newplayer_name << "'s side, silly.";
522  send_server_message(msg.str(), sock);
523  return;
524  }
525  sides_[side_num - 1] = socket_ptr();
526  // If the old player lost his last side, make him an observer.
527  if (std::find(sides_.begin(), sides_.end(), old_player) == sides_.end()
528  && is_player(old_player)) {
529  observers_.push_back(old_player);
530  player_connections_.find(old_player)->info().set_status(player::OBSERVING);
531  players_.erase(std::remove(players_.begin(), players_.end(), old_player), players_.end());
532  // Tell others that the player becomes an observer.
533  send_and_record_server_message(old_player_name + " becomes an observer.");
534  // Update the client side observer list for everyone except old player.
535  simple_wml::document observer_join;
536  observer_join.root().add_child("observer").set_attr_dup("name", old_player_name.c_str());
537  send_data(observer_join, old_player);
538  }
539  change_controller(side_num - 1, newplayer, player_connections_.find(newplayer)->info().name(), false);
540 
541  // If we gave the new side to an observer add him to players_.
542  if (is_observer(newplayer)) {
543  players_.push_back(newplayer);
544  player_connections_.find(newplayer)->info().set_status(player::PLAYING);
545  observers_.erase(std::remove(observers_.begin(), observers_.end(), newplayer), observers_.end());
546  // Send everyone but the new player the observer_quit message.
547  send_observerquit(newplayer);
548  }
549 }
550 
551 void game::change_controller(const size_t side_index,
552  const socket_ptr sock,
553  const std::string& player_name,
554  const bool player_left)
555 {
556  DBG_GAME << __func__ << "...\n";
557 
558  const std::string& side = lexical_cast_default<std::string, size_t>(side_index + 1);
559  sides_[side_index] = sock;
560 
561  if (player_left && side_controllers_[side_index] == CONTROLLER::AI) {
562  // Automatic AI side transfer.
563  } else {
564  if (started_) {
565  send_and_record_server_message(player_name + " takes control of side " + side + ".");
566  }
567  }
568 
569  simple_wml::document response;
570  simple_wml::node& change = response.root().add_child("change_controller");
571 
572  change.set_attr("side", side.c_str());
573  change.set_attr("player", player_name.c_str());
574 
575  // Tell everyone but the new player that this side's controller changed.
576  change.set_attr("controller", side_controllers_[side_index].to_cstring());
577  change.set_attr("is_local", "no");
578 
579  send_data(response, sock);
580  if (started_) { //this is added instead of the if (started_) {...} below
581  //the purpose of these records is so that observers, replay viewers, get controller updates correctly
582  record_data(response.clone());
583  }
584 
585  // Tell the new player that he controls this side now.
586  // Just don't send it when the player left the game. (The host gets the
587  // side_drop already.)
588  if (!player_left) {
589  change.set_attr("is_local", "yes");
590  send_to_player(sock, response);
591  }
592 }
593 
595  const std::string owner_name = username(owner_);
597  cfg.root().add_child("host_transfer");
598 
599  std::string message = owner_name + " has been chosen as the new host.";
600  send_to_player(owner_, cfg);
602 }
603 
605  if(started_ || description_ == nullptr)
606  return false;
607 
608  int available_slots = 0;
609  int num_sides = get_sides_list().size();
610  int i = 0;
611  const simple_wml::node::child_list& side_list = get_sides_list();
612  for(simple_wml::node::child_list::const_iterator it = side_list.begin(); it != side_list.end(); ++it, ++i) {
613  if (((**it)["allow_player"].to_bool(true) == false) || (**it)["controller"] == "null") {
614  num_sides--;
615  } else if (sides_[i] == 0) {
616  ++available_slots;
617  }
618  }
619  char buf[50];
620  snprintf(buf,sizeof(buf), "%d/%d", available_slots, num_sides);
621 
622  if ((*description_)["slots"] != buf) {
623  description_->set_attr_dup("slots", buf);
624  return true;
625  } else {
626  return false;
627  }
628 }
629 
630 bool game::player_is_banned(const socket_ptr sock) const {
631  std::vector<std::string>::const_iterator ban =
632  std::find(bans_.begin(), bans_.end(), client_address(sock));
633  return ban != bans_.end();
634 }
635 
638  if (all_observers_muted_) {
639  send_and_record_server_message("All observers have been muted.");
640  } else {
641  send_and_record_server_message("Muting of all observers has been removed.");
642  }
643 }
644 
646 {
647  if (all_observers_muted_) {
648  send_server_message("All observers are muted.", user);
649  return;
650  }
651  std::string muted_nicks = list_users(muted_observers_, __func__);
652 
653  send_server_message("Muted observers: " + muted_nicks, user);
654 }
655 
657  const socket_ptr muter)
658 {
659  if (muter != owner_) {
660  send_server_message("You cannot mute: not the game host.", muter);
661  return;
662  }
663  const simple_wml::string_span& username = mute["username"];
664  if (username.empty()) {
665  send_muted_observers(muter);
666  return;
667  }
668 
669  const socket_ptr user = find_user(username);
670  /**
671  * @todo FIXME: Maybe rather save muted nicks as a set of strings and
672  * also allow muting of usernames not in the game.
673  */
674  if (!user || !is_observer(user)) {
675  send_server_message("Observer '" + username.to_string() + "' not found.", muter);
676  return;
677  }
678 
679  // Prevent muting ourselves.
680  if (user == muter) {
681  send_server_message("Don't mute yourself, silly.", muter);
682  return;
683  }
684  if (is_muted_observer(user)) {
685  send_server_message(username.to_string() + " is already muted.", muter);
686  return;
687  }
688  LOG_GAME << client_address(muter) << "\t"
689  << game::username(muter) << " muted: " << username << " ("
690  << client_address(user) << ")\tin game:\t\""
691  << name_ << "\" (" << id_ << ")\n";
692  muted_observers_.push_back(user);
693  send_and_record_server_message(username.to_string() + " has been muted.");
694 }
695 
697  const socket_ptr unmuter)
698 {
699  if (unmuter != owner_) {
700  send_server_message("You cannot unmute: not the game host.", unmuter);
701  return;
702  }
703  const simple_wml::string_span& username = unmute["username"];
704  if (username.empty()) {
705  muted_observers_.clear();
706  send_and_record_server_message("Everyone has been unmuted.");
707  return;
708  }
709 
710  const socket_ptr user = find_user(username);
711  if (!user || !is_observer(user)) {
712  send_server_message("Observer '" + username.to_string() + "' not found.", unmuter);
713  return;
714  }
715 
716  if (!is_muted_observer(user)) {
717  send_server_message(username.to_string() + " is not muted.", unmuter);
718  return;
719  }
720 
721  LOG_GAME << client_address(unmuter) << "\t"
722  << game::username(unmuter) << " unmuted: " << username << " ("
723  << client_address(user) << ")\tin game:\t\""
724  << name_ << "\" (" << id_ << ")\n";
726  muted_observers_.end(), user), muted_observers_.end());
727  send_and_record_server_message(username.to_string() + " has been unmuted.");
728 }
729 
731 {
732  static simple_wml::document leave_game("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED);
733  send_to_player(user, leave_game);
734 }
735 
737  const socket_ptr kicker)
738 {
739  if (kicker != owner_) {
740  send_server_message("You cannot kick: not the game host", kicker);
741  return socket_ptr();
742  }
743  const simple_wml::string_span& username = kick["username"];
744  const socket_ptr user = find_user(username);
745  if (!user || !is_member(user)) {
746  send_server_message("'" + username.to_string() + "' is not a member of this game.", kicker);
747  return socket_ptr();
748  } else if (user == kicker) {
749  send_server_message("Don't kick yourself, silly.", kicker);
750  return socket_ptr();
751  } else if (player_connections_.find(user)->info().is_moderator()) {
752  send_server_message("You're not allowed to kick a moderator.", kicker);
753  return socket_ptr();
754  }
755  LOG_GAME << client_address(kicker) << "\t"
756  << game::username(kicker) << "\tkicked: " << username << " ("
757  << client_address(user) << ")\tfrom game:\t\""
758  << name_ << "\" (" << id_ << ")\n";
759  send_and_record_server_message(username.to_string() + " has been kicked.");
760 
761  // Tell the user to leave the game.
762  send_leave_game(user);
763  remove_player(user);
764  return user;
765 }
766 
768  const socket_ptr banner)
769 {
770  if (banner != owner_) {
771  send_server_message("You cannot ban: not the game host", banner);
772  return socket_ptr();
773  }
774  const simple_wml::string_span& username = ban["username"];
775  const socket_ptr user = find_user(username);
776  if (!user) {
777  send_server_message("User '" + username.to_string() + "' not found.", banner);
778  return socket_ptr();
779  } else if (user == banner) {
780  send_server_message("Don't ban yourself, silly.", banner);
781  return socket_ptr();
782  } else if (player_is_banned(user)) {
783  send_server_message("'" + username.to_string() + "' is already banned.", banner);
784  return socket_ptr();
785  } else if (player_connections_.find(user)->info().is_moderator()) {
786  send_server_message("You're not allowed to ban a moderator.", banner);
787  return socket_ptr();
788  }
789  LOG_GAME << client_address(banner) << "\t"
790  << game::username(banner) << "\tbanned: " << username << " ("
791  << client_address(user) << ")\tfrom game:\t\""
792  << name_ << "\" (" << id_ << ")\n";
793  bans_.push_back(client_address(user));
794  send_and_record_server_message(username.to_string() + " has been banned.");
795  if (is_member(user)) {
796  //tell the user to leave the game.
797  send_leave_game(user);
798  remove_player(user);
799  return user;
800  }
801  // Don't return the user if he wasn't in this game.
802  return socket_ptr();
803 }
804 
806  const socket_ptr unbanner)
807 {
808  if (unbanner != owner_) {
809  send_server_message("You cannot unban: not the game host.", unbanner);
810  return;
811  }
812  const simple_wml::string_span& username = unban["username"];
813  const socket_ptr user = find_user(username);
814  if (!user) {
815  send_server_message("User '" + username.to_string() + "' not found.", unbanner);
816  return;
817  }
818  if (!player_is_banned(user)) {
819  send_server_message("'" + username.to_string() + "' is not banned.", unbanner);
820  return;
821  }
822  LOG_GAME << client_address(unbanner) << "\t"
823  << player_connections_.find(unbanner)->info().name() << "\tunbanned: " << username << " ("
824  << client_address(user) << ")\tfrom game:\t\""
825  << name_ << "\" (" << id_ << ")\n";
826  bans_.erase(std::remove(bans_.begin(), bans_.end(), client_address(user)), bans_.end());
827  send_and_record_server_message(username.to_string() + " has been unbanned.");
828 }
829 
831  if (!owner_) {
832  ERR_GAME << "No owner in game::process_message" << std::endl;
833  }
834 
835  simple_wml::node* const message = data.root().child("message");
836  assert(message);
837  message->set_attr_dup("sender", player_connections_.find(user)->info().name().c_str());
838 
839  const simple_wml::string_span& msg = (*message)["message"];
840  chat_message::truncate_message(msg, *message);
841 
842  send_data(data, user, "game message");
843 }
844 
845 bool game::is_legal_command(const simple_wml::node& command, const socket_ptr user)
846 {
847  const bool is_player = this->is_player(user);
848  const bool is_host = user == owner_;
849  const bool is_current = is_current_player(user);
850 
851  if(command.has_attr("from_side")) {
852  const size_t from_side_index = command["from_side"].to_int() - 1;
853  if(command["from_side"] == "server") {
854  //Someone pretends to be the server...
855  return false;
856  }
857  if(from_side_index >= sides_.size() || sides_[from_side_index] != user) {
858  return false;
859  }
860  }
861 
862  if(is_current) return true;
863  // Only single commands allowed.
864  // NOTE: some non-dependent commands like move,attack.. might contain a [checkup] tag after their first data.
865  // But those packages are only sended by the currently active player which we check above.
866  if (!command.one_child()) return false;
867  // Chatting is never an illegal command.
868  if (command.child("speak")) return true;
869  if (is_player && command.has_attr("dependent") && command.has_attr("from_side"))
870  //AKA it's generated by get_user_input for example [global_variable]
871  {
872  return true;
873  }
874  if ((is_player || is_host)
875  && (command.child("label")
876  || command.child("clear_labels")
877  || command.child("rename")
878  || command.child("countdown_update")
879  ))
880  {
881  return true;
882  }
883  return false;
884 }
885 
887  //DBG_GAME << "processing commands: '" << cfg << "'\n";
888  if (!started_) return false;
889  simple_wml::node* const turn = data.root().child("turn");
890  bool turn_ended = false;
891  // Any private 'speak' commands must be repackaged separate
892  // to other commands, and re-sent, since they should only go
893  // to some clients.
894  bool repackage = false;
895  int index = 0;
896  std::vector<int> marked;
897  const simple_wml::node::child_list& commands = turn->children("command");
898  simple_wml::node::child_list::const_iterator command;
899  for (command = commands.begin(); command != commands.end(); ++command) {
900  DBG_GAME << "game " << id_ << " recieved [" << (**command).first_child() << "] from player '" << username(user) << "'(" << user << ") during turn " << end_turn_ << "\n";
901  if (!is_legal_command(**command, user)) {
902  LOG_GAME << "ILLEGAL COMMAND in game: " << id_ << " ((("
903  << simple_wml::node_to_string(**command) << ")))\n";
904  std::stringstream msg;
905  msg << "Removing illegal command '" << (**command).first_child().to_string()
906  << "' from: " << username(user)
907  << ". Current player is: "
909  << " (" << end_turn_ + 1 << "/" << nsides_ << ").";
910  LOG_GAME << msg.str() << " (socket: " << current_player()
911  << ") (game id: " << id_ << ")\n";
913 
914  marked.push_back(index - marked.size());
915  } else if ((**command).child("speak")) {
916  simple_wml::node& speak = *(**command).child("speak");
917  if (speak["to_sides"] != "" || is_muted_observer(user)) {
918  DBG_GAME << "repackaging..." << std::endl;
919  repackage = true;
920  }
921 
922  const simple_wml::string_span& msg = speak["message"];
923  chat_message::truncate_message(msg, speak);
924 
925  // Force the description to be correct,
926  // to prevent spoofing of messages.
927  speak.set_attr_dup("id", player_connections_.find(user)->info().name().c_str());
928  // Also check the side for players.
929  if (is_player(user)) {
930  const size_t side_index = speak["side"].to_int() - 1;
931  if (side_index >= sides_.size()
932  || sides_[side_index] != user) {
933  if (user == current_player()) {
934  speak.set_attr_dup("side", lexical_cast_default<std::string>(current_side() + 1).c_str());
935  } else {
936  const side_vector::const_iterator s =
937  std::find(sides_.begin(), sides_.end(), user);
938  speak.set_attr_dup("side", lexical_cast_default<std::string>(s - sides_.begin() + 1).c_str());
939  }
940  }
941  }
942  }
943  else if (is_current_player(user) && (**command).child("end_turn")) {
944  turn_ended = end_turn();
945  }
946  ++index;
947  }
948  for(std::vector<int>::const_iterator j = marked.begin(); j != marked.end(); ++j) {
949  turn->remove_child("command",*j);
950  }
951  if (turn->no_children()) {
952  return false;
953  }
954  if (!repackage) {
955  record_data(data.clone());
956  send_data(data, user, "game replay");
957  return turn_ended;
958  }
959  for (command = commands.begin(); command != commands.end(); ++command) {
960  simple_wml::node* const speak = (**command).child("speak");
961  if (speak == nullptr) {
963  simple_wml::node& turn = mdata->root().add_child("turn");
964  (**command).copy_into(turn.add_child("command"));
965  send_data(*mdata, user, "game replay");
966  record_data(mdata);
967  continue;
968  }
969  const simple_wml::string_span& to_sides = (*speak)["to_sides"];
970  // Anyone can send to the observer team.
971  if (is_muted_observer(user) && to_sides != game_config::observer_team_name.c_str()) {
972  send_server_message("You have been muted, others can't see your message!", user);
973  continue;
974  }
975 
976  std::unique_ptr<simple_wml::document> message(new simple_wml::document);
977  simple_wml::node& turn = message->root().add_child("turn");
978  simple_wml::node& command = turn.add_child("command");
979  speak->copy_into(command.add_child("speak"));
980  if (to_sides == "") {
981  send_data(*message, user, "game message");
982  record_data(message.release());
983  } else if (to_sides == game_config::observer_team_name) {
984  send_to_players(*message, observers_, user);
985  record_data(message.release());
986  } else {
987  send_data_sides(*message, to_sides, user, "game message");
988  }
989  }
990  return turn_ended;
991 }
992 
994 {
995  uint32_t seed = rng_.get_next_random();
996 
997  std::stringstream stream;
998  stream << std::setfill('0') << std::setw(sizeof(uint32_t)*2) << std::hex << seed;
999 
1001  simple_wml::node& turn = mdata->root().add_child("turn");
1002  simple_wml::node& command = turn.add_child("command");
1003  simple_wml::node& random_seed = command.add_child("random_seed");
1004  random_seed.set_attr_dup("new_seed",stream.str().c_str());
1005  command.set_attr("from_side", "server");
1006  command.set_attr("dependent", "yes");
1007 
1008  send_data(*mdata, socket_ptr(), "game replay");
1009  record_data(mdata);
1010 }
1012 {
1013  const size_t side_index = req["side"].to_int() - 1;
1014  CONTROLLER new_controller;
1015  CONTROLLER old_controller;
1016  if(!new_controller.parse(req["new_controller"])) {
1017  send_and_record_server_message("Could not handle [request_choice] [change_controller] with invalid controller '" + req["new_controller"].to_string() + "'");
1018  return;
1019  }
1020  if(!old_controller.parse(req["old_controller"])) {
1021  send_and_record_server_message("Could not handle [request_choice] [change_controller] with invalid controller '" + req["old_controller"].to_string() + "'");
1022  return;
1023  }
1024  if(old_controller != this->side_controllers_[side_index]) {
1025  send_and_record_server_message("Found unexpected old_controller= '" + old_controller.to_string() + "' in [request_choice] [change_controller]");
1026  }
1027  if(side_index >= sides_.size()) {
1028  send_and_record_server_message("Could not handle [request_choice] [change_controller] with invalid side '" + req["side"].to_string() + "'");
1029  return;
1030  }
1031  const bool was_null = this->side_controllers_[side_index] == CONTROLLER::EMPTY;
1032  const bool becomes_null = new_controller == CONTROLLER::EMPTY;
1033  if(was_null) {
1034  assert(sides_[side_index] == 0);
1035  sides_[side_index] = current_player();
1036  }
1037  if(becomes_null) {
1038  sides_[side_index] = socket_ptr();
1039  }
1040  side_controllers_[side_index] = new_controller;
1041 
1043  simple_wml::node& turn = mdata->root().add_child("turn");
1044  simple_wml::node& command = turn.add_child("command");
1045  simple_wml::node& change_controller_wml = command.add_child("change_controller_wml");
1046  change_controller_wml.set_attr("controller", new_controller.to_cstring());
1047  change_controller_wml.set_attr("is_local", "yes");
1048  command.set_attr("from_side", "server");
1049  command.set_attr("dependent", "yes");
1050  if(sides_[side_index] != 0) {
1051  //calling send_to_one to 0 connect causes the package to be sended to all clients.
1052  send_to_player(sides_[side_index], *mdata);
1053  }
1054  change_controller_wml.set_attr("is_local", "no");
1055  send_data(*mdata, sides_[side_index], "game replay");
1056  record_data(mdata);
1057 }
1058 
1060 {
1061  // note, that during end turn events, it's side=1 for the server but side= side_count() on the clients.
1062 
1063  // Otherwise we allow observers to cause OOS for the playing clients by sending
1064  // server choice requests based on incompatible local changes. To solve this we block
1065  // server choice requests from observers.
1066  if(!started_) {
1067  return;
1068  }
1069  if (user != owner_ && !is_player(user)) {
1070  return;
1071  }
1072  int request_id = lexical_cast_default<int>(data["request_id"], -10);
1073 
1074  if(request_id <= last_choice_request_id_) {
1075  // We gave already an anwer to this request.
1076  return;
1077  }
1078  DBG_GAME << "answering seed request " << request_id << " by player " << player_connections_.find(user)->info().name() << "(" << user << ")" << std::endl;
1079  last_choice_request_id_ = request_id;
1080 
1081  if(const simple_wml::node* rand = data.child("random_seed")) {
1082  handle_random_choice(*rand);
1083  }
1084  else if(const simple_wml::node* ccw = data.child("change_controller_wml")) {
1086  }
1087  else {
1088  send_and_record_server_message("Found unknown server choice request: [" + data.first_child().to_string() + "]");
1089  }
1090 
1091 }
1092 
1094 {
1095  if(!started_ || !is_player(user))
1096  return;
1097 
1098  simple_wml::node const& wb_node = *data.child("whiteboard");
1099 
1100  // Ensure "side" attribute match with user
1101  simple_wml::string_span const& to_sides = wb_node["to_sides"];
1102  size_t const side_index = wb_node["side"].to_int() - 1;
1103  if(side_index >= sides_.size()
1104  || sides_[side_index] != user)
1105  {
1106  std::ostringstream msg;
1107  msg << "Ignoring illegal whiteboard data, sent from user '" << player_connections_.find(user)->info().name()
1108  << "' which had an invalid side '" << side_index + 1 << "' specified" << std::endl;
1109  const std::string& msg_str = msg.str();
1110  LOG_GAME << msg_str << std::endl;
1112  return;
1113  }
1114 
1115  send_data_sides(data, to_sides, user, "whiteboard");
1116 }
1117 
1119 {
1120  if(!started_ || !is_player(user))
1121  return;
1122 
1123  simple_wml::node const& ctw_node = *data.child("change_turns_wml");
1124  const int current_turn = ctw_node["current"].to_int();
1125  const int num_turns = ctw_node["max"].to_int();
1126  if(num_turns > 10000 || current_turn > 10000) {
1127  //ignore this to prevent errors related to integer overflow.
1128  return;
1129  }
1130  set_current_turn(current_turn);
1131  num_turns_ = num_turns;
1132 
1133  assert(static_cast<int>(this->current_turn()) == current_turn);
1134  description_->set_attr_dup("turn", describe_turns(current_turn, num_turns_).c_str());
1135  //Dont send or store this change, all players should have gotten it by wml.
1136 }
1137 
1139  // It's a new turn every time each side in the game ends their turn.
1140  ++end_turn_;
1141  bool turn_ended = false;
1142  if ((current_side()) == 0) {
1143  turn_ended = true;
1144  }
1145  // Skip over empty sides.
1146  for (int i = 0; i < nsides_ && side_controllers_[current_side()] == CONTROLLER::EMPTY; ++i) {
1147  ++end_turn_;
1148  if (current_side() == 0) {
1149  turn_ended = true;
1150  }
1151  }
1152  if (!turn_ended) return false;
1153 
1154  if (description_ == nullptr) {
1155  return false;
1156  }
1157 
1158  description_->set_attr_dup("turn", describe_turns(current_turn(), num_turns_).c_str());
1159 
1160  return true;
1161 }
1162 
1163 ///@todo differentiate between "observers not allowed" and "player already in the game" errors.
1164 // maybe return a string with an error message.
1166  if(is_member(player)) {
1167  ERR_GAME << "ERROR: Player is already in this game. (socket: "
1168  << player << ")\n";
1169  return false;
1170  }
1171 
1172  socket_ptr user = player;
1173 
1175  bool became_observer = false;
1176  if (!started_ && !observer && take_side(user)) {
1177  DBG_GAME << "adding player...\n";
1178  players_.push_back(player);
1179  player_connections_.find(user)->info().set_status(player::PLAYING);
1180  send_and_record_server_message(player_connections_.find(user)->info().name() + " has joined the game.", player);
1181  } else if (!allow_observers() && !player_connections_.find(user)->info().is_moderator()) {
1182  return false;
1183  } else {
1184  if (!observer) {
1185  became_observer = true;
1186  observer = true;
1187  }
1188  DBG_GAME << "adding observer...\n";
1189  observers_.push_back(player);
1190  if (!allow_observers()) send_and_record_server_message(player_connections_.find(user)->info().name() + " is now observing the game.", player);
1191 
1192  simple_wml::document observer_join;
1193  observer_join.root().add_child("observer").set_attr_dup("name", player_connections_.find(user)->info().name().c_str());
1194 
1195  // Send observer join to everyone except the new observer.
1196  send_data(observer_join, player);
1197  }
1198  LOG_GAME << client_address(player) << "\t" << player_connections_.find(user)->info().name()
1199  << "\tjoined game:\t\"" << name_ << "\" (" << id_ << ")"
1200  << (observer ? " as an observer" : "")
1201  << ". (socket: " << player << ")\n";
1202  player_connections_.find(user)->info().mark_available(id_, name_);
1203  player_connections_.find(user)->info().set_status((observer) ? player::OBSERVING : player::PLAYING);
1205  // Send the user the game data.
1206  send_to_player(player, level_);
1207 
1208  if(started_) {
1209  //tell this player that the game has started
1210  static simple_wml::document start_game_doc("[start_game]\n[/start_game]\n", simple_wml::INIT_COMPRESSED);
1211  send_to_player(player, start_game_doc);
1212  // Send observer join of all the observers in the game to the new player
1213  // only once the game started. The client forgets about it anyway
1214  // otherwise.
1215  send_observerjoins(player);
1216  // Send the player the history of the game to-date.
1217  send_history(player);
1218  } else {
1219  send_user_list();
1220  }
1221 
1222  const std::string clones = has_same_ip(player, observer);
1223  if (!clones.empty()) {
1224  send_and_record_server_message(player_connections_.find(user)->info().name() + " has the same IP as: " + clones);
1225  }
1226 
1227  if (became_observer) {
1228  // in case someone took the last slot right before this player
1229  send_server_message("You are an observer.", player);
1230  }
1231  return true;
1232 }
1233 
1234 bool game::remove_player(const socket_ptr player, const bool disconnect, const bool destruct) {
1235  if (!is_member(player)) {
1236  ERR_GAME << "ERROR: User is not in this game. (socket: "
1237  << player << ")\n";
1238  return false;
1239  }
1241  DBG_GAME << "removing player...\n";
1242 
1243  const bool host = (player == owner_);
1244  const bool observer = is_observer(player);
1245  players_.erase(std::remove(players_.begin(), players_.end(), player), players_.end());
1246  observers_.erase(std::remove(observers_.begin(), observers_.end(), player), observers_.end());
1247  const bool game_ended = players_.empty() || (host && !started_);
1248 
1249  socket_ptr user = player;
1250 
1251  LOG_GAME << client_address(user) << "\t" << player_connections_.find(user)->info().name()
1252  << ((game_ended && !(observer && destruct))
1253  ? (started_ ? "\tended" : "\taborted") : "\thas left")
1254  << " game:\t\"" << name_ << "\" (" << id_ << ")"
1255  << (game_ended && started_ && !(observer && destruct) ? " at turn: "
1256  + lexical_cast_default<std::string,size_t>(current_turn())
1257  + " with reason: '" + termination_reason() + "'" : "")
1258  << (observer ? " as an observer" : "")
1259  << (disconnect ? " and disconnected" : "")
1260  << ". (socket: " << user << ")\n";
1261  if (game_ended && started_ && !(observer && destruct)) {
1262  send_server_message_to_all(player_connections_.find(user)->info().name() + " ended the game.", player);
1263  }
1264  if (game_ended || destruct) return game_ended;
1265 
1266  // Don't mark_available() since the player got already removed from the
1267  // games_and_users_list_.
1268  if (!disconnect) {
1269  player_connections_.find(user)->info().mark_available();
1270  }
1271  if (observer) {
1272  send_observerquit(user);
1273  } else {
1274  send_and_record_server_message(player_connections_.find(user)->info().name()
1275  + (disconnect ? " has disconnected." : " has left the game."), player);
1276  }
1277  // If the player was host choose a new one.
1278  if (host) {
1279  owner_ = players_.front();
1280  notify_new_host();
1281  }
1282 
1283  bool ai_transfer = false;
1284  // Look for all sides the player controlled and drop them.
1285  // (Give them to the host.)
1286  for (side_vector::iterator side = sides_.begin(); side != sides_.end(); ++side) {
1287  size_t side_index = side - sides_.begin();
1288  if (*side != player) continue;
1289  if (side_controllers_[side_index] == CONTROLLER::AI) ai_transfer = true;
1290 
1291  change_controller(side_index, owner_, username(owner_));
1292  // Check whether the host is actually a player and make him one if not.
1293  if (!is_player(owner_)) {
1294  DBG_GAME << "making the owner a player...\n";
1295  player_connections_.find(owner_)->info().set_status(player::PLAYING);
1296  observers_.erase(std::remove(observers_.begin(), observers_.end(), owner_), observers_.end());
1297  players_.push_back(owner_);
1299  }
1300 
1301  //send the host a notification of removal of this side
1302  const std::string side_drop = lexical_cast_default<std::string, size_t>(side_index + 1);
1303  simple_wml::document drop;
1304  drop.root().set_attr("side_drop", side_drop.c_str());
1305  drop.root().set_attr("controller", side_controllers_[side_index].to_cstring());
1306 
1307  DBG_GAME << "*** sending side drop: \n" << drop.output() << std::endl;
1308 
1309  send_to_player(owner_, drop);
1310  }
1311  if (ai_transfer) send_and_record_server_message("AI sides transferred to host.");
1312 
1314 
1315  send_user_list(player);
1316  return false;
1317 }
1318 
1319 void game::send_user_list(const socket_ptr exclude) const {
1320  //if the game hasn't started yet, then send all players a list
1321  //of the users in the game
1322  if (started_ || description_ == nullptr) return;
1323  /** @todo Should be renamed to userlist. */
1325  cfg.root().add_child("gamelist");
1326  user_vector users = all_game_users();
1327  for(user_vector::const_iterator p = users.begin(); p != users.end(); ++p) {
1328  const auto pl = player_connections_.find(*p);
1329  if (pl != player_connections_.end()) {
1330  //don't need to duplicate pl->second.name().c_str() because the
1331  //document will be destroyed by the end of the function
1332  cfg.root().add_child("user").set_attr("name", pl->info().name().c_str());
1333  }
1334  }
1335  send_data(cfg, exclude);
1336 }
1337 
1339  send_server_message_to_all(player_connections_.find(user)->info().name() + " advances to the next scenario", user);
1340  simple_wml::document cfg_scenario;
1341  simple_wml::node & next_scen = cfg_scenario.root().add_child("next_scenario");
1342  level_.root().copy_into(next_scen);
1343  next_scen.set_attr("started", started_ ? "yes" : "no");
1344 
1345  DBG_GAME << "****\n loading next scenario for a client. sides info = " << std::endl;
1346  DBG_GAME << debug_sides_info() << std::endl;
1347  DBG_GAME << "****" << std::endl;
1348 
1349  //
1350  // Change the controller to match that client.
1351  //
1352  // FIXME: This breaks scenario transitions with mp connect screen shown.
1353  //
1354  // FIXME: This causes bugs, esp if controller have changed since the
1355  // beginning of the next scenario
1356  //
1357  // There are currently 2 possible ideas to fix this issue:
1358  //
1359  // 1) When the scenario starts, we store the controllers at that
1360  // point and use that data when a client loads the the next
1361  // scenario (here)
1362  //
1363  // 2) When a client loads the next scenario we send it the
1364  // observers' starting point (meaning we don't change sides
1365  // here), and then we send that side an automatic controller
1366  // change later.
1367  //
1368  simple_wml::document doc_controllers;
1369  simple_wml::node & cfg_controllers = doc_controllers.root().add_child("controllers");
1370 
1371  for (const auto& side_user : sides_) {
1372  simple_wml::node & cfg_controller = cfg_controllers.add_child("controller");
1373  cfg_controller.set_attr("is_local", side_user == user ? "yes" : "no");
1374  }
1375 
1376  send_to_player(user, cfg_scenario);
1377  send_to_player(user, doc_controllers);
1378  // Send the player the history of the game to-date.
1379  send_history(user);
1380  // Send observer join of all the observers in the game to the user.
1381  send_observerjoins(user);
1382 }
1383 
1385  const socket_ptr exclude,
1386  std::string /*packet_type*/) const
1387 {
1388  send_to_players(data, all_game_users(), exclude);
1389 }
1390 namespace {
1391  struct controls_side_helper
1392  {
1394  const std::vector<int>& sides_;
1395  controls_side_helper(const wesnothd::game& game, const std::vector<int>& sides)
1396  : game_(game)
1397  , sides_(sides)
1398  {
1399 
1400  }
1401  bool operator ()(socket_ptr user) const
1402  {
1403  return game_.controls_side(sides_, user);
1404  }
1405  };
1406 }
1407 
1409  const simple_wml::string_span& sides,
1410  const socket_ptr exclude,
1411  std::string /*packet_type*/) const
1412 {
1413  std::vector<int> sides_vec = ::split<int>(sides, ::split_conv_impl());
1414  DBG_GAME << __func__ << "...\n";
1415  decltype(players_) filtered_players;
1416  std::copy_if(players_.begin(), players_.end(), filtered_players.begin(), controls_side_helper(*this, sides_vec));
1417  send_to_players(data, filtered_players, exclude);
1418 }
1419 
1420 bool game::controls_side(const std::vector<int>& sides, const socket_ptr player) const
1421 {
1422  for (int side : sides)
1423  {
1424  size_t side_index = side - 1;
1425  if(side_index < sides_.size() && sides_[side_index] == player) {
1426  return true;
1427  }
1428  }
1429  return false;
1430 }
1431 
1433  const user_vector users = observer ? players_ : all_game_users();
1434  const std::string ip = client_address(user);
1435  std::string clones;
1436  for (user_vector::const_iterator i = users.begin(); i != users.end(); ++i) {
1437  if (ip == client_address(*i) && user != *i) {
1438  const auto pl = player_connections_.find(*i);
1439  if (pl != player_connections_.end()) {
1440  clones += (clones.empty() ? "" : ", ") + pl->info().name();
1441  }
1442  }
1443  }
1444  return clones;
1445 }
1446 
1447 void game::send_observerjoins(const socket_ptr sock) const {
1448  for (user_vector::const_iterator ob = observers_.begin(); ob != observers_.end(); ++ob) {
1449  if (*ob == sock) continue;
1450 
1452  cfg.root().add_child("observer").set_attr_dup("name", player_connections_.find(*ob)->info().name().c_str());
1453  if (sock == socket_ptr()) {
1454  // Send to everyone except the observer in question.
1455  send_data(cfg, *ob);
1456  } else {
1457  // Send to the (new) user.
1458  send_to_player(sock, cfg);
1459  }
1460  }
1461 }
1462 
1464  simple_wml::document observer_quit;
1465 
1466  //don't need to dup the attribute because this document is
1467  //short-lived.
1468  observer_quit.root().add_child("observer_quit").set_attr("name", player_connections_.find(observer)->info().name().c_str());
1469  send_data(observer_quit, observer);
1470 }
1471 
1472 void game::send_history(const socket_ptr socket) const
1473 {
1474  if(history_.empty()) {
1475  return;
1476  }
1477 
1478  //we make a new document based on converting to plain text and
1479  //concatenating the buffers.
1480  //TODO: Work out how to concentate buffers without decompressing.
1481  std::string buf;
1482  for(t_history::iterator i = history_.begin(); i != history_.end(); ++i) {
1483  buf += i->output();
1484  }
1485 
1486  try {
1488  doc->compress();
1489  send_to_player(socket, *doc);
1490  history_.clear();
1491  history_.push_back(doc);
1492  } catch (simple_wml::error& e) {
1493  WRN_CONFIG << __func__ << ": simple_wml error: " << e.message << std::endl;
1494  }
1495 
1496 }
1497 
1498 static bool is_invalid_filename_char(char c) {
1499  return !(isalnum(c) || (c == '_') || (c == '-') || (c == '.')
1500  || (c == '(') || (c == ')') || (c == '#') || (c == ',')
1501  || (c == '!') || (c == '?') || (c == '^') || (c == '+')
1502  || (c == '*') || (c == ':') || (c == '=') || (c == '@')
1503  || (c == '%') || (c == '\''));
1504 }
1505 
1507  if (!save_replays_ || !started_ || history_.empty()) return;
1508 
1509  std::string replay_commands;
1510  for(t_history::iterator i = history_.begin(); i != history_.end(); ++i) {
1511  const simple_wml::node::child_list& turn_list = i->root().children("turn");
1512  for (simple_wml::node::child_list::const_iterator turn = turn_list.begin(); turn != turn_list.end(); ++turn) {
1513  replay_commands += simple_wml::node_to_string(**turn);
1514  }
1515  }
1516  history_.clear();
1517 
1518  std::stringstream name;
1519  name << (*starting_pos( level_.root()))["name"] << " Turn " << current_turn();
1520 
1521  std::stringstream replay_data;
1522  try {
1523  //level_.set_attr_dup("label", name.str().c_str());
1524  //TODO: comment where mp_game_title= is used.
1525  level_.set_attr_dup("mp_game_title", name_.c_str());
1526  const bool has_old_replay = level_.child("replay") != nullptr;
1527  //If there is already a replay in the level_, which means this is a reloaded game,
1528  //then we dont need to add the [start] in the replay.
1529  replay_data << level_.output()
1530  //This can result in having 2 [replay] at toplevel since level_ can contain one already. But the client can handle this (simply merges them).
1531  << "[replay]\n"
1532  //The [start] is generated at the clients and not sended over the network so we add it here.
1533  //It usualy contains some checkup data that is used to check whether the calculated results
1534  //match the ones calculated in the replay. But thats not necessary
1535  << (has_old_replay ? "" : "\t[command]\n\t\t[start]\n\t\t[/start]\n\t[/command]\n")
1536  << replay_commands
1537  << "[/replay]\n";
1538  name << " (" << id_ << ").bz2";
1539 
1540  std::string replay_data_str = replay_data.str();
1541  simple_wml::document replay(replay_data_str.c_str(), simple_wml::INIT_STATIC);
1542 
1543  std::string filename(name.str());
1544  std::replace(filename.begin(), filename.end(), ' ', '_');
1545  filename.erase(std::remove_if(filename.begin(), filename.end(), is_invalid_filename_char), filename.end());
1546  DBG_GAME << "saving replay: " << filename << std::endl;
1548  (*os) << replay.output_compressed(true);
1549 
1550  if (!os->good()) {
1551  ERR_GAME << "Could not save replay! (" << filename << ")" << std::endl;
1552  }
1553  } catch (simple_wml::error& e) {
1554  WRN_CONFIG << __func__ << ": simple_wml error: " << e.message << std::endl;
1555  }
1556 }
1557 
1559  data->compress();
1560  history_.push_back(data);
1561 }
1562 
1564  if (history_.empty()) return;
1565  history_.clear();
1566 }
1567 
1569  description_ = desc;
1570  if(!password_.empty()) {
1571  description_->set_attr("password", "yes");
1572  }
1573 }
1574 
1576 /* if (reason == "out of sync") {
1577  simple_wml::string_span era;
1578  if (level_.child("era")) {
1579  era = level_.child("era")->attr("id");
1580  }
1581  termination_ = "out of sync - " + era.to_string();
1582  }*/
1583  if (termination_.empty()) { termination_ = reason; }
1584 }
1585 
1587  user_vector res;
1588 
1589  res.insert(res.end(), players_.begin(), players_.end());
1590  res.insert(res.end(), observers_.begin(), observers_.end());
1591 
1592  return res;
1593 }
1594 
1596  std::stringstream result;
1597  result << "game id: " << id_ << "\n";
1598 // result << "players_.size: " << players_.size() << "\n";
1599  for (user_vector::const_iterator p = players_.begin(); p != players_.end(); ++p){
1600  const auto user = player_connections_.find(*p);
1601  if (user != player_connections_.end()){
1602  result << "player: " << user->info().name().c_str() << "\n";
1603  }
1604  else{
1605  result << "player: '" << *p << "' not found\n";
1606  }
1607  }
1608 // result << "observers_.size: " << observers_.size() << "\n";
1609  for (user_vector::const_iterator o = observers_.begin(); o != observers_.end(); ++o){
1610  const auto user = player_connections_.find(*o);
1611  if (user != player_connections_.end()){
1612  result << "observer: " << user->info().name().c_str() << "\n";
1613  }
1614  else{
1615  result << "observer: '" << *o << "' not found\n";
1616  }
1617  }
1618 /* result << "player_info_: begin\n";
1619  for (player_map::const_iterator info = player_info_->begin(); info != player_info_->end(); info++){
1620  result << info->second.name().c_str() << "\n";
1621  }
1622  result << "player_info_: end\n";*/
1623  return result.str();
1624 }
1625 
1627  std::stringstream result;
1628  result << "game id: " << id_ << "\n";
1629  const simple_wml::node::child_list & sides = get_sides_list();
1630 
1631  result << "\t\t level, server\n";
1632  for(simple_wml::node::child_list::const_iterator s = sides.begin(); s != sides.end(); ++s) {
1633  result << "side " << (**s)["side"].to_int() << " :\t" << (**s)["controller"].to_string()
1634  << "\t, " << side_controllers_[(**s)["side"].to_int() - 1].to_cstring()
1635  << "\t( " << sides_[(**s)["side"].to_int()-1] << ",\t"
1636  << (**s)["current_player"].to_string() << " )\n";
1637  }
1638 
1639  return result.str();
1640 }
1641 
1643 {
1644  const auto iter = player_connections_.get<name_t>().find(name.to_string());
1645  if(iter != player_connections_.get<name_t>().end())
1646  return iter->socket();
1647  else
1648  return socket_ptr();
1649 }
1650 
1652 {
1654  send_server_message(message, socket_ptr(), doc);
1655  send_data(*doc, exclude, "message");
1656  if (started_) record_data(doc);
1657  else delete doc;
1658 }
1659 
1660 void game::send_server_message_to_all(const char* message, socket_ptr exclude) const
1661 {
1663  send_server_message(message, socket_ptr(), &doc);
1664  send_data(doc, exclude, "message");
1665 }
1666 
1668 {
1669  simple_wml::document docbuf;
1670  if(docptr == nullptr) {
1671  docptr = &docbuf;
1672  }
1673 
1674  simple_wml::document& doc = *docptr;
1675  if (started_) {
1676  simple_wml::node& cmd = doc.root().add_child("turn");
1677  simple_wml::node& cfg = cmd.add_child("command");
1678  simple_wml::node& msg = cfg.add_child("speak");
1679  msg.set_attr("id", "server");
1680  msg.set_attr_dup("message", message);
1681  } else {
1682  simple_wml::node& msg = doc.root().add_child("message");
1683  msg.set_attr("sender", "server");
1684  msg.set_attr_dup("message", message);
1685  }
1686 
1687 
1688  if(sock) {
1689  send_to_player(sock, doc);
1690  }
1691 }
1692 } // namespace wesnothd
bool process_turn(simple_wml::document &data, const socket_ptr user)
Handles [end_turn], repackages [commands] with private [speak]s in them and sends the data...
Definition: game.cpp:886
node & add_child(const char *name)
Definition: simple_wml.cpp:465
const char * const_iterator
Definition: simple_wml.hpp:46
bool player_is_banned(const socket_ptr player) const
Checks whether the connection's ip address is banned.
Definition: game.cpp:630
bool save_replays_
Definition: game.hpp:409
void start_game(const socket_ptr starter)
Definition: game.cpp:271
rand_rng::mt_rng rng_
A wrapper for mersenne twister rng which generates randomness for this game.
Definition: game.hpp:413
bool allow_observers() const
Definition: game.cpp:145
int end_turn_
Definition: game.hpp:401
void set_current_turn(int turn)
Definition: game.hpp:92
void update_game()
Definition: game.cpp:338
std::string debug_sides_info() const
Helps debugging controller tweaks.
Definition: game.cpp:1626
std::string to_string() const
Definition: simple_wml.cpp:181
GLenum GLenum GLvoid GLvoid GLvoid * span
Definition: glew.h:3805
void unmute_observer(const simple_wml::node &unmute, const socket_ptr unmuter)
Definition: game.cpp:696
std::string id_
Definition: formula.cpp:636
void send_observerquit(const socket_ptr observer) const
Definition: game.cpp:1463
void save_replay()
Definition: game.cpp:1506
const wesnothd::game & game_
Definition: game.cpp:1393
player_connections & player_connections_
Definition: game.hpp:348
const GLfloat * c
Definition: glew.h:12741
const std::string & name() const
Definition: game.hpp:52
void send_server_message(const char *message, socket_ptr sock=socket_ptr(), simple_wml::document *doc=nullptr) const
Definition: game.cpp:1667
void handle_random_choice(const simple_wml::node &data)
Definition: game.cpp:993
void change_controller(const size_t side_num, const socket_ptr sock, 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...
Definition: game.cpp:551
user_vector muted_observers_
Definition: game.hpp:365
boost::uint32_t uint32_t
Definition: xbrz.hpp:45
uint32_t get_next_random()
Get a new random number.
Definition: mt_rng.cpp:63
void update_side_data()
Resets the side configuration according to the scenario data.
Definition: game.cpp:400
std::string has_same_ip(const socket_ptr user, bool observer) const
Checks whether a user has the same IP as members of this game.
Definition: game.cpp:1432
node & set_attr(const char *key, const char *value)
Definition: simple_wml.cpp:411
void transfer_side_control(const socket_ptr sock, const simple_wml::node &cfg)
Let's a player owning a side give it to another player or observer.
Definition: game.cpp:464
GLuint const GLfloat * val
Definition: glew.h:2614
int nsides_
Number of sides in the current scenario.
Definition: game.hpp:373
user_vector observers_
A vector of observers (members not owning a side).
Definition: game.hpp:364
void process_message(simple_wml::document &data, const socket_ptr user)
Definition: game.cpp:830
const user_vector all_game_users() const
Adds players and observers into one vector and returns that.
Definition: game.cpp:1586
socket_ptr find_user(const simple_wml::string_span &name)
Shortcut to a convenience function for finding a user by name.
Definition: game.cpp:1642
bool is_member(const socket_ptr player) const
Definition: game.hpp:55
bool all_observers_muted_
Definition: game.hpp:403
size_t current_side() const
Definition: game.hpp:254
const simple_wml::node::child_list & get_sides_list() const
Definition: game.hpp:83
#define ERR_GAME
Definition: game.cpp:32
std::string termination_
Definition: game.hpp:407
socket_ptr host
Definition: game.hpp:46
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definition: glew.h:1347
node & set_attr_dup(const char *key, const char *value)
Definition: simple_wml.hpp:273
#define DBG_GAME
Definition: game.cpp:35
std::vector< std::string > bans_
Definition: game.hpp:405
bool take_side(const socket_ptr user)
Figures out which side to take and tells that side to the game owner.
Definition: game.cpp:361
bool is_player(const socket_ptr player) const
Definition: game.cpp:168
GLuint GLuint stream
Definition: glew.h:5239
node * child(const char *name)
Definition: simple_wml.hpp:257
const char * output()
node * child(const char *name)
Definition: simple_wml.cpp:607
socket_ptr kick_member(const simple_wml::node &kick, const socket_ptr kicker)
Kick a member by name.
Definition: game.cpp:736
socket_ptr current_player() const
Definition: game.hpp:255
bool send_taken_side(simple_wml::document &cfg, const simple_wml::node::child_list::const_iterator side) const
Definition: game.cpp:347
void handle_choice(const simple_wml::node &data, const socket_ptr user)
Definition: game.cpp:1059
int last_choice_request_id_
Definition: game.hpp:414
GLuint64EXT * result
Definition: glew.h:10727
void truncate_message(const simple_wml::string_span &str, simple_wml::node &message)
Function to ensure a text message is within the allowed length.
void send_and_record_server_message(const char *message, const socket_ptr exclude=socket_ptr())
Send data to all players in this game except 'exclude'.
Definition: game.cpp:1651
std::string username(const socket_ptr pl) const
Returns the name of the user or "(unfound)".
Definition: game.cpp:187
static lg::log_domain log_server("server")
void reset_sides()
calculates the initial value for sides_, side_controllerds_, nsides_
Definition: game.cpp:391
static lg::log_domain log_config("config")
void send_data(simple_wml::document &data, const socket_ptr exclude=socket_ptr(), std::string packet_type="") const
Definition: game.cpp:1384
std::vector< CONTROLLER > side_controllers_
Definition: game.hpp:370
bool one_child() const
Definition: simple_wml.hpp:167
int num_turns_
Definition: game.hpp:402
void remove_child(const char *name, size_t index)
Definition: simple_wml.cpp:602
const std::string & termination_reason() const
Definition: game.hpp:230
void handle_controller_choice(const simple_wml::node &data)
Definition: game.cpp:1011
bool describe_slots()
Set the description to the number of available slots.
Definition: game.cpp:604
socket_ptr ban_user(const simple_wml::node &ban, const socket_ptr banner)
Ban and kick a user by name.
Definition: game.cpp:767
static bool is_invalid_filename_char(char c)
Definition: game.cpp:1498
bool controls_side(const std::vector< int > &sides, const socket_ptr player) const
Function which returns true iff 'player' controls any of the sides spcified in 'sides'.
Definition: game.cpp:1420
std::ostream * ostream_file(std::string const &fname, bool create_directory=true)
bool registered_users_only() const
Definition: game.cpp:149
bool has_attr(const char *key) const
Definition: simple_wml.cpp:403
GLenum GLuint GLsizei const char * buf
Definition: glew.h:2498
const char * end() const
Definition: simple_wml.hpp:91
void missing_user(socket_ptr socket, const std::string &func) const
Function to log when we don't find a connection in player_info_.
Definition: game.cpp:74
bool is_legal_command(const simple_wml::node &command, const socket_ptr user)
Definition: game.cpp:845
GLfloat GLfloat p
Definition: glew.h:12766
Templates and utility-routines for strings and numbers.
void perform_controller_tweaks()
Definition: game.cpp:212
void send_to_player(socket_ptr socket, simple_wml::document &doc)
Definition: server.cpp:1846
std::string client_address(socket_ptr socket)
Definition: server.cpp:108
bool add_player(const socket_ptr player, bool observer=false)
Add a user to the game.
Definition: game.cpp:1165
void send_leave_game(socket_ptr user) const
Definition: game.cpp:730
void send_history(const socket_ptr sock) const
Definition: game.cpp:1472
socket_ptr owner_
The game host or later owner (if the host left).
Definition: game.hpp:358
GLuint res
Definition: glew.h:9258
static simple_wml::node * starting_pos(simple_wml::node &data)
Definition: game.hpp:65
bool is_observer(const socket_ptr player) const
Definition: game.cpp:153
node & set_attr_dup(const char *key, const char *value)
Definition: simple_wml.cpp:427
void process_whiteboard(simple_wml::document &data, const socket_ptr user)
Handles incoming [whiteboard] data.
Definition: game.cpp:1093
std::string list_users(user_vector users, const std::string &func) const
Returns a comma separated list of user names.
Definition: game.cpp:197
GLuint index
Definition: glew.h:1782
multi_index_container< player_record, indexed_by< ordered_unique< tag< socket_t >, BOOST_MULTI_INDEX_CONST_MEM_FUN(player_record, const socket_ptr, socket)>, hashed_unique< tag< name_t >, BOOST_MULTI_INDEX_CONST_MEM_FUN(player_record, const std::string &, name)>, ordered_non_unique< tag< game_t >, BOOST_MULTI_INDEX_CONST_MEM_FUN(player_record, int, game_id)> >> player_connections
std::vector< node * > child_list
Definition: simple_wml.hpp:121
size_t i
Definition: function.cpp:1057
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:112
void copy_into(node &n) const
Definition: simple_wml.cpp:806
game(const game &)
Definition: game.cpp:80
const child_list & children(const char *name) const
Definition: simple_wml.cpp:634
Declarations for File-IO.
void record_data(simple_wml::document *data)
Definition: game.cpp:1558
side_vector sides_
A vector of side owners.
Definition: game.hpp:368
void notify_new_host()
In case of a host transfer, notify the new host about its status.
Definition: game.cpp:594
bool disconnect(connection s)
Function to disconnect from a certain host, or close all connections if connection_num is 0...
Definition: network.cpp:646
static void save(LexState *ls, int c)
Definition: llex.cpp:51
void load_next_scenario(const socket_ptr user)
A user (player only?) asks for the next scenario to advance to.
Definition: game.cpp:1338
void set_description(simple_wml::node *desc)
Functions to set/get the address of the game's summary description as sent to players in the lobby...
Definition: game.cpp:1568
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
Definition: game.cpp:134
std::string observer
Definition: game_config.cpp:84
void send_server_message_to_all(const char *message, socket_ptr exclude=socket_ptr()) const
Definition: game.cpp:1660
size_t current_turn() const
Definition: game.hpp:91
std::string replace(std::string str, const std::string &src, const std::string &dst)
Replace all instances of src in str with dst.
const char * begin() const
Definition: simple_wml.hpp:90
GLuint const GLchar * name
Definition: glew.h:1782
simple_wml::node * description_
Pointer to the game's description in the games_and_users_list_.
Definition: game.hpp:399
std::string name_
The name of the game.
Definition: game.hpp:354
bool is_current_player(const socket_ptr player) const
Definition: game.hpp:257
bool is_muted_observer(const socket_ptr player) const
Definition: game.cpp:157
-file actions.hpp
static int id_num
Definition: game.hpp:350
#define LOG_GAME
Definition: game.cpp:34
bool find(E event, F functor)
Tests whether an event handler is available.
Definition: ban.cpp:28
std::string replay_save_path_
Definition: game.hpp:410
void send_data_sides(simple_wml::document &data, const simple_wml::string_span &sides, const socket_ptr exclude=socket_ptr(), std::string packet_type="") const
Definition: game.cpp:1408
Standard logging facilities (interface).
user_vector players_
A vector of players (members owning a side).
Definition: game.hpp:361
std::vector< socket_ptr > user_vector
Definition: game.hpp:34
std::string message
Definition: exceptions.hpp:29
GLsizei GLenum GLuint GLuint GLsizei char * message
Definition: glew.h:2499
void send_observerjoins(const socket_ptr sock=socket_ptr()) const
Send [observer] tags of all the observers in the game to the user or everyone if none given...
Definition: game.cpp:1447
#define WRN_CONFIG
Definition: game.cpp:37
std::string password_
Definition: game.hpp:355
const std::string remove
remove directive
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
#define e
void process_change_turns_wml(simple_wml::document &data, const socket_ptr user)
Handles incoming [change_turns_wml] data.
Definition: game.cpp:1118
boost::shared_ptr< boost::asio::ip::tcp::socket > socket_ptr
void mute_observer(const simple_wml::node &mute, const socket_ptr muter)
Mute an observer or give a message of all currently muted observers if no name is given...
Definition: game.cpp:656
void send_user_list(const socket_ptr exclude=socket_ptr()) const
Function to send a list of users to all clients.
Definition: game.cpp:1319
std::string debug_player_info() const
Helps debugging player and observer lists.
Definition: game.cpp:1595
bool started_
Definition: game.hpp:374
bool end_turn()
Function which should be called every time a player ends their turn (i.e.
Definition: game.cpp:1138
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
void unban_user(const simple_wml::node &unban, const socket_ptr unbanner)
Definition: game.cpp:805
GLdouble s
Definition: glew.h:1358
t_history history_
Definition: game.hpp:396
simple_wml::document level_
The current scenario data.
Definition: game.hpp:392
GLsizei const GLcharARB ** string
Definition: glew.h:4503
void clear_history()
Definition: game.cpp:1563
void send_muted_observers(const socket_ptr user) const
Definition: game.cpp:645
void mute_all_observers()
Definition: game.cpp:636
bool remove_player(const socket_ptr player, const bool disconnect=false, const bool destruct=false)
Removes a user from the game.
Definition: game.cpp:1234
const std::vector< int > & sides_
Definition: game.cpp:1394
void send_to_players(simple_wml::document &data, const Container &players, socket_ptr exclude=socket_ptr())
void set_termination_reason(const std::string &reason)
Definition: game.cpp:1575
#define WRN_GAME
Definition: game.cpp:33
bool no_children() const
Definition: simple_wml.hpp:166
std::string node_to_string(const node &n)
Definition: simple_wml.cpp:794
const std::string observer_team_name
observer team name used for observer team chat