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