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