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