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