The Battle for Wesnoth  1.19.0+dev
playmp_controller.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2024
3  by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
4  Copyright (C) 2003 by David White <dave@whitevine.net>
5  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY.
13 
14  See the COPYING file for more details.
15 */
16 
17 #include "playmp_controller.hpp"
18 
19 #include "actions/undo.hpp"
20 #include "countdown_clock.hpp"
21 #include "display_chat_manager.hpp"
22 #include "floating_label.hpp"
23 #include "formula/string_utils.hpp" // for VGETTEXT
24 #include "game_end_exceptions.hpp"
26 #include "gettext.hpp"
30 #include "log.hpp"
31 #include "map/label.hpp"
32 #include "mp_ui_alerts.hpp"
34 #include "replay_helper.hpp"
35 #include "resources.hpp"
36 #include "savegame.hpp"
38 #include "synced_context.hpp"
39 #include "video.hpp" // only for faked
40 #include "wesnothd_connection.hpp"
41 #include "whiteboard/manager.hpp"
42 
43 static lg::log_domain log_engine("engine");
44 static lg::log_domain log_network("network");
45 
46 #define LOG_NG LOG_STREAM(info, log_engine)
47 #define DBG_NG LOG_STREAM(debug, log_engine)
48 #define ERR_NW LOG_STREAM(err, log_network)
49 
51  : playsingle_controller(level, state_of_game)
52  , network_processing_stopped_(false)
53  , next_scenario_notified_(false)
54  , blindfold_(*gui_, mp_info && mp_info->skip_replay_blindfolded)
55  , network_reader_([this](config& cfg) { return receive_from_wesnothd(cfg); })
56  , mp_info_(mp_info)
57 {
58  // upgrade hotkey handler to the mp (network enabled) version
59  hotkey_handler_.reset(new hotkey_handler(*this, saved_game_));
60 
61  skip_replay_ = mp_info && mp_info->skip_replay && mp_info->current_turn > turn();
62 
63  if(gui_->is_blindfolded() && !is_observer()) {
65  }
66 }
67 
69 {
70 }
71 
73 {
75  LOG_NG << "network processing activated again";
76 }
77 
79 {
81  LOG_NG << "network processing stopped";
82 }
83 
85 {
87 }
88 
90 {
91  if(gui_->is_blindfolded()) {
93  LOG_NG << "Taking off the blindfold now";
94  gui_->queue_rerender();
95  }
96 }
97 
99 {
100  LOG_NG << "playmp::play_human_turn...";
101  assert(gamestate().in_phase(game_data::TURN_PLAYING));
102 
103  mp::ui_alerts::turn_changed(current_team().current_player());
104 
105  LOG_NG << "events::commands_disabled=" << events::commands_disabled;
106 
108 
109  const std::unique_ptr<countdown_clock> timer(saved_game_.mp_settings().mp_countdown
111  : nullptr);
112 
114 
115  if(undo_stack().can_undo()) {
116  // If we reload a networked mp game we cannot undo moves made before the save
117  // because other players already received them
118  if(!current_team().auto_shroud_updates()) {
120  }
121  undo_stack().clear();
122  }
123 
124  if(!prefs::get().disable_auto_moves()) {
125  execute_gotos();
126  }
127 
128  end_turn_enable(true);
129 
130  while(!should_return_to_play_side()) {
131  try {
136  // Clean undo stack if turn has to be restarted (losing control)
137  if(undo_stack().can_undo()) {
138  gui_->announce(_("Undoing moves not yet transmitted to the server."), font::NORMAL_COLOR);
139  }
140 
141  while(undo_stack().can_undo()) {
142  undo_stack().undo();
143  }
144  }
145 
146  if(timer) {
147  bool time_left = timer->update();
148  if(!time_left) {
149  end_turn_requested_ = true;
150  }
151  }
152  } catch(...) {
153  DBG_NG << "Caught exception while playing a side: " << utils::get_unknown_exception_type();
154  throw;
155  }
156  }
157 }
158 
160 {
161  LOG_NG << "playmp::play_human_turn...";
162 
164 
165  while(!should_return_to_play_side()) {
166  try {
169  SDL_Delay(1);
170  } catch(...) {
171  DBG_NG << "Caught exception while playing idle loop: " << utils::get_unknown_exception_type();
172  throw;
173  }
174  }
175 }
176 
178 {
179  send_actions();
180 
183  while(!next_scenario_notified_ && !is_host()) {
185  SDL_Delay(10);
187  }
188  });
189 }
190 
192 {
194  // time_left + turn_bonus + (action_bonus * number of actions done)
195  const int new_time_in_secs = (current_team().countdown_time() / 1000)
198 
199  const int new_time
200  = 1000 * std::min<int>(new_time_in_secs, saved_game_.mp_settings().mp_countdown_reservoir_time);
201 
203  current_team().set_countdown_time(new_time);
204 
206  }
207 
208  LOG_NG << "playmp::after_human_turn...";
209 
210  // Normal post-processing for human turns (clear undos, end the turn, etc.)
212 }
213 
215 {
216  LOG_NG << "is networked...";
217 
218  end_turn_enable(false);
219 
220  while(!should_return_to_play_side()) {
223  if(!mp_info_ || mp_info_->current_turn == turn()) {
224  skip_replay_ = false;
225  }
226  }
227 
229  }
230 
231  LOG_NG << "finished networked...";
232 }
233 
234 void playmp_controller::process_oos(const std::string& err_msg) const
235 {
236  // Notify the server of the oos error.
237  config cfg;
238  config& info = cfg.add_child("info");
239  info["type"] = "termination";
240  info["condition"] = "out of sync";
241  send_to_wesnothd(cfg);
242 
243  std::stringstream temp_buf;
244  std::vector<std::string> err_lines = utils::split(err_msg, '\n');
245  temp_buf << _("The game is out of sync, and cannot continue. There are a number of reasons this could happen: this "
246  "can occur if you or another player have modified their game settings. This may mean one of the "
247  "players is attempting to cheat. It could also be due to a bug in the game, but this is less "
248  "likely.\n\nDo you want to save an error log of your game?");
249 
250  if(!err_msg.empty()) {
251  temp_buf << " \n \n"; // and now the "Details:"
252  for(std::vector<std::string>::iterator i = err_lines.begin(); i != err_lines.end(); ++i) {
253  temp_buf << *i << '\n';
254  }
255  temp_buf << " \n";
256  }
257 
258  scoped_savegame_snapshot snapshot(*this);
260 
262 }
263 
264 void playmp_controller::handle_generic_event(const std::string& name)
265 {
267 }
269 {
270  return !mp_info_ || mp_info_->is_host;
271 }
272 
274 {
275  gui_->get_chat_manager().add_chat_message(std::time(nullptr), "", 0,
276  _("This side is in an idle state. To proceed with the game, it must be assigned to another controller. You may "
277  "use :droid, :control or :give_control for example."),
279 }
280 
282 {
283  if(replay_controller_.get() != nullptr) {
284  // We have probably been using the mp "back to turn" feature
285  // We continue play since we have reached the end of the replay.
286  replay_controller_.reset();
287  }
288 
289  // mouse_handler expects at least one team for linger mode to work.
290  send_actions();
291  assert(is_regular_game_end());
292  if(!get_end_level_data().transient.linger_mode || get_teams().empty() || video::headless()) {
293  if(!is_host() && gamestate().has_next_scenario()) {
294  // If we continue without lingering we need to
295  // make sure the host uploads the next scenario
296  // before we attempt to download it.
297  wait_for_upload();
298  }
299  } else {
300  linger();
301  }
302  end_turn_requested_ = true;
303 }
304 
306 {
307  undo_stack().clear();
309  send_actions();
310 }
311 
313 {
315  send_actions();
316 }
317 
318 
319 void playmp_controller::play_slice(bool is_delay_enabled)
320 {
322  // receive chat during animations and delay
323  // But don't execute turn data during animations etc.
324  process_network_data(true);
325  // send_actions() makes sure that no actions that can
326  // still be undone is sent.
327  send_actions();
328  }
329 
330  playsingle_controller::play_slice(is_delay_enabled);
331 }
332 
334 {
335  return mp_info_ != nullptr;
336 }
337 
338 void playmp_controller::send_to_wesnothd(const config& cfg, const std::string&) const
339 {
340  if(mp_info_ != nullptr) {
342  }
343 }
344 
346 {
347  if(mp_info_ != nullptr) {
348  return mp_info_->connection.receive_data(cfg);
349  } else {
350  return false;
351  }
352 }
353 
355 {
356  if(!recorder().at_end()) {
357  auto commandtype = get_replay_action_type(*recorder().peek_next_action());
358  // the only cases where process_network_data_impl puts something on the recorder
359  // without immidiately exceuting it are user choices
360  if(commandtype != REPLAY_ACTION_TYPE::DEPENDENT) {
361  ERR_NW << "attempting to process network data while still having data on the replay.";
362  }
363  return;
364  } else if (next_scenario_notified_) {
365  //Do nothing, Otherwise we might risk getting data that belongs to the next scenario.
366  return;
367  }
368 
369  config cfg;
370  // we could replace this "if" with a "while" to process multiple actions without delay between them
371  if(network_reader_.read(cfg)) {
372 
373  //Don't exceute the next turns actions.
374  unsync_only |= gamestate().in_phase(game_data::TURN_ENDED);
375  unsync_only |= is_regular_game_end();
376  unsync_only |= player_type_changed_;
377 
378  auto res = process_network_data_impl(cfg, unsync_only);
380  // chat_only=true, but we received a non-chat command, probably belonging to the next
381  // action when we are still exceuting the previous one.
382  // save the incoming data for later.
383  // Note: With this approach, incoming [turn] that we cannot handle also blocks other
384  // incoming data, like [change_controller].
385  network_reader_.push_front(std::move(cfg));
386  return;
387  }
389  end_turn();
390  }
391  }
392 }
393 
395 {
396  // the simple wesnothserver implementation in wesnoth was removed years ago.
397  assert(cfg.all_children_count() == 1);
398  assert(cfg.attribute_range().empty());
399  if(!recorder().at_end())
400  {
401  ERR_NW << "processing network data while still having data on the replay.";
402  }
403 
404  if (const auto message = cfg.optional_child("message"))
405  {
406  game_display::get_singleton()->get_chat_manager().add_chat_message(std::time(nullptr), message.value()["sender"], message.value()["side"],
407  message.value()["message"], events::chat_handler::MESSAGE_PUBLIC,
409  }
410  else if (auto whisper = cfg.optional_child("whisper") /*&& is_observer()*/)
411  {
412  game_display::get_singleton()->get_chat_manager().add_chat_message(std::time(nullptr), "whisper: " + whisper["sender"].str(), 0,
413  whisper["message"], events::chat_handler::MESSAGE_PRIVATE,
414  prefs::get().message_bell());
415  }
416  else if (auto observer = cfg.optional_child("observer") )
417  {
419  }
420  else if (auto observer_quit = cfg.optional_child("observer_quit"))
421  {
423  }
424  else if (cfg.has_child("leave_game")) {
425  const bool has_reason = cfg.mandatory_child("leave_game").has_attribute("reason");
426  throw leavegame_wesnothd_error(has_reason ? cfg.mandatory_child("leave_game")["reason"].str() : "");
427  }
428  else if (auto turn = cfg.optional_child("turn"))
429  {
430  return process_network_turn_impl(*turn, chat_only);
431  }
432  else if (cfg.has_child("whiteboard"))
433  {
434  set_scontext_unsynced scontext;
435  get_whiteboard()->process_network_data(cfg);
436  }
437  else if (auto change = cfg.optional_child("change_controller"))
438  {
440  }
441  else if (auto side_drop_c = cfg.optional_child("side_drop"))
442  {
443  process_network_side_drop_impl(*side_drop_c);
444  }
445 
446  // The host has ended linger mode in a campaign -> enable the "End scenario" button
447  // and tell we did get the notification.
448  else if (cfg.has_child("notify_next_scenario")) {
450  }
451 
452  //If this client becomes the new host, notify the play_controller object about it
453  else if (cfg.has_child("host_transfer")) {
454  mp_info_->is_host = true;
455  if(is_linger_mode()) {
456  end_turn_enable(true);
457  }
458  }
459  else
460  {
461  ERR_NW << "found unknown command:\n" << cfg.debug();
462  }
463 
465 }
466 
468 {
469  //t can contain a [command] or a [upload_log]
470  assert(t.all_children_count() == 1);
471 
472  if(auto command = t.optional_child("command")) {
473  auto commandtype = get_replay_action_type(*command);
474  if(chat_only && (commandtype == REPLAY_ACTION_TYPE::SYNCED || commandtype == REPLAY_ACTION_TYPE::INVALID) ) {
476  }
477  if (commandtype == REPLAY_ACTION_TYPE::SYNCED && current_team().is_local()) {
478  // Executing those is better than OOS, also the server checks that other players don't send actions while it's not their turn.
479  ERR_NW << "Received a synced remote user action during our own turn";
480  }
481  }
482 
483  //note, that this function might call itself recursively: do_replay -> ... -> get_user_choice -> ... -> receive_actions -> ... -> handle_turn
485  do_replay();
487 }
488 
490 {
491  // Only the host receives this message when a player leaves/disconnects.
492  const int side_drop = side_drop_c["side_num"].to_int(0);
493  std::size_t index = side_drop -1;
494 
496 
497  if (index >= gamestate().board_.teams().size()) {
498  ERR_NW << "unknown side " << side_drop << " is dropping game";
499  throw ingame_wesnothd_error("");
500  }
501 
502  auto ctrl = side_controller::get_enum(side_drop_c["controller"].str());
503  if(!ctrl) {
504  ERR_NW << "unknown controller type issued from server on side drop: " << side_drop_c["controller"];
505  throw ingame_wesnothd_error("");
506  }
507 
508  if (ctrl == side_controller::type::ai) {
509  gamestate().board_.side_drop_to(side_drop, *ctrl);
510  return;
511  }
512  //null controlled side cannot be dropped because they aren't controlled by anyone.
513  else if (ctrl != side_controller::type::human) {
514  ERR_NW << "unknown controller type issued from server on side drop: " << side_controller::get_string(*ctrl);
515  throw ingame_wesnothd_error("");
516  }
517 
518  int action = 0;
519  int first_observer_option_idx = 0;
520  int control_change_options = 0;
521  bool has_next_scenario = gamestate().has_next_scenario();
522 
523  std::vector<std::string> observers;
524  std::vector<const team *> allies;
525  std::vector<std::string> options;
526 
527  const team &tm = gamestate().board_.teams()[index];
528 
529  for (const team &t : gamestate().board_.teams()) {
530  if (!t.is_enemy(side_drop) && !t.is_local_human() && !t.is_local_ai() && !t.is_network_ai() && !t.is_empty()
531  && t.current_player() != tm.current_player()) {
532  allies.push_back(&t);
533  }
534  }
535 
536  // We want to give host chance to decide what to do for side
537  if (!is_linger_mode() || has_next_scenario) {
538  utils::string_map t_vars;
539 
540  //get all allies in as options to transfer control
541  for (const team *t : allies) {
542  //if this is an ally of the dropping side and it is not us (choose local player
543  //if you want that) and not ai or empty and if it is not the dropping side itself,
544  //get this team in as well
545  t_vars["player"] = t->current_player();
546  options.emplace_back(VGETTEXT("Give control to their ally $player", t_vars));
547  control_change_options++;
548  }
549 
550  first_observer_option_idx = options.size();
551 
552  //get all observers in as options to transfer control
553  for (const std::string &screen_observers : game_display::get_singleton()->observers()) {
554  t_vars["player"] = screen_observers;
555  options.emplace_back(VGETTEXT("Give control to observer $player", t_vars));
556  observers.push_back(screen_observers);
557  control_change_options++;
558  }
559 
560  options.emplace_back(_("Replace with AI"));
561  options.emplace_back(_("Replace with local player"));
562  options.emplace_back(_("Set side to idle"));
563  options.emplace_back(_("Save and abort game"));
564 
565  t_vars["player"] = tm.current_player();
566  t_vars["side_drop"] = std::to_string(side_drop);
567  const std::string gettext_message = VGETTEXT("$player who controlled side $side_drop has left the game. What do you want to do?", t_vars);
568  gui2::dialogs::simple_item_selector dlg("", gettext_message, options);
569  dlg.set_single_button(true);
570  dlg.show();
571  action = dlg.selected_index();
572 
573  // If esc was pressed, default to setting side to idle
574  if (action == -1) {
575  action = control_change_options + 2;
576  }
577  } else {
578  // Always set leaving side to idle if in linger mode and there is no next scenario
579  action = 2;
580  }
581 
582  if (action < control_change_options) {
583  // Grant control to selected ally
584  // Server thinks this side is ours now so in case of error transferring side we have to make local state to same as what server thinks it is.
585  gamestate().board_.side_drop_to(side_drop, side_controller::type::human, side_proxy_controller::type::idle);
586 
587  if (action < first_observer_option_idx) {
588  send_change_side_controller(side_drop, allies[action]->current_player());
589  } else {
590  send_change_side_controller(side_drop, observers[action - first_observer_option_idx]);
591  }
592  } else {
593  action -= control_change_options;
594 
595  //make the player an AI, and redo this turn, in case
596  //it was the current player's team who has just changed into
597  //an AI.
598  switch(action) {
599  case 0:
600  on_not_observer();
601  gamestate().board_.side_drop_to(side_drop, side_controller::type::human, side_proxy_controller::type::ai);
602 
603  return;
604 
605  case 1:
606  on_not_observer();
607  gamestate().board_.side_drop_to(side_drop, side_controller::type::human, side_proxy_controller::type::human);
608 
609  return;
610  case 2:
611  gamestate().board_.side_drop_to(side_drop, side_controller::type::human, side_proxy_controller::type::idle);
612 
613  return;
614 
615  case 3:
616  //The user pressed "end game". Don't throw a network error here or he will get
617  //thrown back to the title screen.
618  do_autosave();
620  default:
621  break;
622  }
623  }
624 }
625 
627 {
628 
629  if(change.empty()) {
630  ERR_NW << "Bad [change_controller] signal from server, [change_controller] tag was empty.";
631  return;
632  }
633 
634  const int side = change["side"].to_int();
635  const bool is_local = change["is_local"].to_bool();
636  const std::string player = change["player"];
637  const std::string controller_type = change["controller"];
638  const std::size_t index = side - 1;
639  if(index >= gamestate().board_.teams().size()) {
640  ERR_NW << "Bad [change_controller] signal from server, side out of bounds: " << change.debug();
641  return;
642  }
643 
644  const team & tm = gamestate().board_.teams().at(index);
645  const bool was_local = tm.is_local();
646 
647  gamestate().board_.side_change_controller(side, is_local, player, controller_type);
648 
649  if (!was_local && tm.is_local()) {
650  on_not_observer();
651  }
652 
654 
655  get_whiteboard()->on_change_controller(side,tm);
656 
657  player_type_changed_ |= game_display::get_singleton()->playing_side() == side && (was_local || tm.is_local());
658 }
659 
661 {
662  if(!is_networked_mp()) {
663  return;
664  }
665 
666  get_whiteboard()->send_network_data();
667 
668  const bool send_everything = synced_context::is_unsynced() ? !undo_stack().can_undo() : synced_context::undo_blocked();
669  const replay::DATA_TYPE data_type = send_everything ? replay::ALL_DATA : replay::NON_UNDO_DATA;
670 
671  config data = recorder().get_unsent_commands(data_type);
672  if (!data.empty()) {
673  send_to_wesnothd(config{ "turn", data});
674  }
675 }
676 
677 void playmp_controller::send_change_side_controller(int side, const std::string& player)
678 {
679  config cfg;
680  config& change = cfg.add_child("change_controller");
681  change["side"] = side;
682  change["player"] = player;
683  send_to_wesnothd(cfg);
684 }
double t
Definition: astarsearch.cpp:63
bool can_undo() const
True if there are actions that can be undone.
Definition: undo.hpp:99
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:201
void undo()
Undoes the top action on the undo stack.
Definition: undo.cpp:330
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:367
const_attr_itors attribute_range() const
Definition: config.cpp:763
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:317
bool has_attribute(config_key_type key) const
Definition: config.cpp:155
std::size_t all_children_count() const
Definition: config.cpp:307
std::string debug() const
Definition: config.cpp:1244
bool empty() const
Definition: config.cpp:852
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Euivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:385
config & add_child(config_key_type key)
Definition: config.cpp:441
virtual void play_slice(bool is_delay_enabled=true)
void remove_observer(const std::string &name)
void add_observer(const std::string &name)
void add_chat_message(const std::time_t &time, const std::string &speaker, int side, const std::string &msg, events::chat_handler::MESSAGE_TYPE type, bool bell)
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:79
void side_drop_to(int side_num, side_controller::type ctrl, side_proxy_controller::type proxy=side_proxy_controller::type::human)
Definition: game_board.cpp:223
void side_change_controller(int side_num, bool is_local, const std::string &pname, const std::string &controller_type)
Definition: game_board.cpp:239
@ TURN_PLAYING
The User is controlling the game and invoking actions The game can be saved here.
Definition: game_data.hpp:93
@ TURN_ENDED
The turn_end, side_turn_end etc [events] are fired next phase: TURN_STARTING_WAITING (default),...
Definition: game_data.hpp:96
virtual int playing_side() const override
The playing team is the team whose turn it is.
static game_display * get_singleton()
display_chat_manager & get_chat_manager()
bool has_next_scenario() const
Checks whether this is not the last scenario (usually of a campaign)
Definition: game_state.cpp:395
bool in_phase(game_data::PHASE phase) const
Definition: game_state.hpp:109
game_board board_
Definition: game_state.hpp:44
static void progress(loading_stage stage=loading_stage::none)
Report what is being loaded to the loading screen.
static void display(std::function< void()> f)
static void spin()
Indicate to the player that loading is progressing.
bool show(const unsigned auto_close_time=0)
Shows the window.
void set_single_button(bool value)
Sets whether the Cancel button should be hidden or not.
int selected_index() const
Returns the selected item index after displaying.
std::vector< team > & get_teams()
std::unique_ptr< hotkey_handler > hotkey_handler_
bool is_linger_mode() const
actions::undo_list & undo_stack()
bool is_observer() const
bool is_regular_game_end() const
saved_game & saved_game_
game_state & gamestate()
std::unique_ptr< game_display > gui_
const end_level_data & get_end_level_data() const
int current_side() const
Returns the number of the side whose turn it is.
std::shared_ptr< wb::manager > get_whiteboard() const
bool is_replay() const
bool player_type_changed_
true when the controller of the currently playing side has changed.
std::size_t turn() const
bool can_undo() const
replay & recorder() const
void process_network_data(bool unsync_only=false)
void send_actions() override
Sends replay [command]s to the server.
void process_oos(const std::string &err_msg) const override
Asks the user whether to continue on an OOS error.
void process_network_side_drop_impl(const config &t)
Handle incoming [side_drop] from the multiplayer server.
void send_change_side_controller(int side, const std::string &player)
Send [change_controller] to the multiplayer server.
mp_game_metadata * mp_info_
Information about our connection to the multiplayer server.
virtual bool is_host() const override
void process_network_change_controller_impl(const config &)
Handle incoming [change_controller] from the multiplayer server.
virtual void do_idle_notification() override
Will handle sending a networked notification in descendent classes.
void maybe_linger() override
playturn_network_adapter network_reader_
Helper to preprocess infoming network data.
bool receive_from_wesnothd(config &cfg) const override
virtual void play_idle_loop() override
virtual void handle_generic_event(const std::string &name) override
virtual void play_human_turn() override
void send_to_wesnothd(const config &cfg, const std::string &packet_type="unknown") const override
PROCESS_DATA_RESULT process_network_data_impl(const config &cfg, bool chat_only=false)
Check for and Handle incoming data from the multiplayer server.
virtual void on_not_observer() override
virtual void after_human_turn() override
bool is_networked_mp() const override
PROCESS_DATA_RESULT process_network_turn_impl(const config &t, bool chat_only=false)
Handle incoming [turn] from the multiplayer server.
@ CANNOT_HANDLE
when we couldn't handle the given action currently.
virtual void play_network_turn() override
Will handle networked turns in descendent classes.
void receive_actions() override
Reads and executes replay [command]s from the server.
void surrender(int side_number)
void wait_for_upload()
Wait for the host to upload the next scenario.
playmp_controller(const config &level, saved_game &state_of_game, mp_game_metadata *mp_info)
void play_slice(bool is_delay_enabled=true) override
void end_turn_enable(bool enable)
std::unique_ptr< replay_controller > replay_controller_
non-null when replay mode in active, is used in singleplayer and for the "back to turn" feature in mu...
virtual void check_objectives() override
virtual bool should_return_to_play_side() const override
void update_viewing_player() override
virtual void handle_generic_event(const std::string &name) override
bool end_turn_requested_
true iff the user has pressed the end turn button this turn.
static prefs & get()
bool message_bell()
static config get_update_shroud()
Records that the player has manually updated fog/shroud.
void add_config(const config &cfg, MARK_SENT mark=MARK_AS_UNSENT)
Definition: replay.cpp:661
@ MARK_AS_SENT
Definition: replay.hpp:132
DATA_TYPE
Definition: replay.hpp:107
@ ALL_DATA
Definition: replay.hpp:107
@ NON_UNDO_DATA
Definition: replay.hpp:107
void add_surrender(int side_number)
Definition: replay.cpp:234
config get_unsent_commands(DATA_TYPE data_type)
Definition: replay.cpp:397
void add_countdown_update(int value, int team)
Definition: replay.cpp:240
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
Definition: saved_game.hpp:60
bool save_game_interactive(const std::string &message, DIALOG_TYPE dialog_type)
Save a game interactively through the savegame dialog.
Definition: savegame.cpp:377
An object to leave the synced context during draw or unsynced wml items when we don’t know whether we...
static bool undo_blocked()
static bool run_and_store(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
static bool is_unsynced()
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:74
void set_action_bonus_count(const int count)
Definition: team.hpp:200
const std::string & current_player() const
Definition: team.hpp:220
bool is_local() const
Definition: team.hpp:247
int action_bonus_count() const
Definition: team.hpp:199
void set_countdown_time(const int amount) const
Definition: team.hpp:197
int countdown_time() const
Definition: team.hpp:196
bool receive_data(config &result)
Receives the next pending data pack from the server, if available.
void send_data(const configr_of &request)
Queues the given data to be sent to the server.
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
std::size_t i
Definition: function.cpp:968
Contains the exception interfaces used to signal completion of a scenario, campaign or turn.
void throw_quit_game_exception()
static std::string _(const char *str)
Definition: gettext.hpp:93
An extension of playsingle_controller::hotkey_handler, which has support for MP wesnoth features like...
Standard logging facilities (interface).
int side_number
Definition: game_info.hpp:40
const color_t NORMAL_COLOR
std::string observer
logger & info()
Definition: log.cpp:314
void turn_changed(const std::string &player_name)
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
std::map< std::string, t_string > string_map
std::vector< std::string > split(const config_attribute_value &val)
bool headless()
The game is running headless.
Definition: video.cpp:141
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
std::string_view data
Definition: picture.cpp:194
static lg::log_domain log_engine("engine")
#define ERR_NW
static lg::log_domain log_network("network")
#define DBG_NG
#define LOG_NG
REPLAY_ACTION_TYPE get_replay_action_type(const config &command)
Definition: replay.cpp:696
REPLAY_RETURN do_replay(bool one_move)
Definition: replay.cpp:711
void unblind()
Definition: display.hpp:1040
We received invalid data from wesnothd during a game This means we cannot continue with the game but ...
wesnothd_connection & connection
unsigned current_turn
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
static constexpr std::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
Definition: enum_base.hpp:57
Various functions that implement the undoing (and redoing) of in-game commands.