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