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