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