The Battle for Wesnoth  1.19.14+dev
playmp_controller.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2025
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"
37 #include "serialization/chrono.hpp"
39 #include "synced_context.hpp"
40 #include "video.hpp" // only for faked
41 #include "wesnothd_connection.hpp"
42 #include "whiteboard/manager.hpp"
43 
44 static lg::log_domain log_engine("engine");
45 static lg::log_domain log_network("network");
46 
47 #define LOG_NG LOG_STREAM(info, log_engine)
48 #define DBG_NG LOG_STREAM(debug, log_engine)
49 #define ERR_NW LOG_STREAM(err, log_network)
50 
52  : playsingle_controller(level, state_of_game)
53  , network_processing_stopped_(false)
54  , next_scenario_notified_(false)
55  , blindfold_(*gui_, mp_info && mp_info->skip_replay_blindfolded)
56  , network_reader_([this](config& cfg) { return receive_from_wesnothd(cfg); })
57  , mp_info_(mp_info)
58 {
59  // upgrade hotkey handler to the mp (network enabled) version
60  hotkey_handler_.reset(new hotkey_handler(*this, saved_game_));
61 
62  skip_replay_ = mp_info && mp_info->skip_replay && mp_info->current_turn > turn();
63 
64  if(gui_->is_blindfolded() && !is_observer()) {
66  }
67 }
68 
70 {
71 }
72 
74 {
76  LOG_NG << "network processing activated again";
77 }
78 
80 {
82  LOG_NG << "network processing stopped";
83 }
84 
86 {
88 }
89 
91 {
92  if(gui_->is_blindfolded()) {
94  LOG_NG << "Taking off the blindfold now";
95  gui_->queue_rerender();
96  }
97 }
98 
100 {
101  LOG_NG << "playmp::play_human_turn...";
102  assert(gamestate().in_phase(game_data::TURN_PLAYING));
103 
104  mp::ui_alerts::turn_changed(current_team().current_player());
105 
106  LOG_NG << "events::commands_disabled=" << events::commands_disabled;
107 
109 
110  utils::optional<countdown_clock> timer;
112  timer.emplace(current_team());
113  }
114 
116 
117  if(undo_stack().can_undo()) {
118  // If we reload a networked mp game we cannot undo moves made before the save
119  // because other players already received them
120  if(!current_team().auto_shroud_updates()) {
122  }
123  undo_stack().clear();
124  }
125 
126  if(!prefs::get().disable_auto_moves()) {
127  execute_gotos();
128  }
129 
130  end_turn_enable(true);
131 
132  while(!should_return_to_play_side()) {
137  // Clean undo stack if turn has to be restarted (losing control)
138  if(undo_stack().can_undo()) {
139  gui_->announce(_("Undoing moves not yet transmitted to the server."), font::NORMAL_COLOR);
140  }
141 
142  while(undo_stack().can_undo()) {
143  undo_stack().undo();
144  }
145  }
146 
147  if(timer) {
148  bool time_left = timer->update();
149  if(!time_left) {
150  end_turn_requested_ = true;
151  }
152  }
153  }
154 }
155 
157 {
158  LOG_NG << "playmp::play_idle_loop...";
159 
161 
162  while(!should_return_to_play_side()) {
165  using namespace std::chrono_literals;
166  std::this_thread::sleep_for(1ms); // TODO: why?
167  }
168 }
169 
171 {
172  send_actions();
173 
176  while(!next_scenario_notified_ && !is_host()) {
178  using namespace std::chrono_literals;
179  std::this_thread::sleep_for(10ms);
181  }
182  });
183 }
184 
186 {
188  // time_left + turn_bonus + (action_bonus * number of actions done)
189  auto new_time = current_team().countdown_time()
192 
193  new_time
194  = std::min<std::chrono::milliseconds>(new_time, saved_game_.mp_settings().mp_countdown_reservoir_time);
195 
197  current_team().set_countdown_time(new_time);
198 
199  recorder().add_countdown_update(new_time.count(), current_side());
200  }
201 
202  LOG_NG << "playmp::after_human_turn...";
203 
204  // Normal post-processing for human turns (clear undos, end the turn, etc.)
206 }
207 
209 {
210  LOG_NG << "is networked...";
211 
212  end_turn_enable(false);
213 
214  while(!should_return_to_play_side()) {
217  if(!mp_info_ || mp_info_->current_turn == turn()) {
218  skip_replay_ = false;
219  }
220  }
221 
223  }
224 
225  LOG_NG << "finished networked...";
226 }
227 
228 void playmp_controller::process_oos(const std::string& err_msg) const
229 {
230  // Notify the server of the oos error.
231  config cfg;
232  config& info = cfg.add_child("info");
233  info["type"] = "termination";
234  info["condition"] = "out of sync";
236 
237  std::stringstream temp_buf;
238  std::vector<std::string> err_lines = utils::split(err_msg, '\n');
239  temp_buf << _("The game is out of sync, and cannot continue. There are a number of reasons this could happen: this "
240  "can occur if you or another player have modified their game settings. This may mean one of the "
241  "players is attempting to cheat. It could also be due to a bug in the game, but this is less "
242  "likely.\n\nDo you want to save an error log of your game?");
243 
244  if(!err_msg.empty()) {
245  temp_buf << " \n \n"; // and now the "Details:"
246  for(std::vector<std::string>::iterator i = err_lines.begin(); i != err_lines.end(); ++i) {
247  temp_buf << *i << '\n';
248  }
249  temp_buf << " \n";
250  }
251 
252  scoped_savegame_snapshot snapshot(*this);
254 
256 }
257 
258 void playmp_controller::handle_generic_event(const std::string& name)
259 {
261 }
263 {
264  return !mp_info_ || mp_info_->is_host;
265 }
266 
268 {
269  gui_->get_chat_manager().add_chat_message(std::chrono::system_clock::now(), "", 0,
270  _("This side is in an idle state. To proceed with the game, it must be assigned to another controller. You may "
271  "use :droid, :control or :give_control for example."),
273 }
274 
276 {
277  if(replay_controller_.get() != nullptr) {
278  // We have probably been using the mp "back to turn" feature
279  // We continue play since we have reached the end of the replay.
280  replay_controller_.reset();
281  }
282 
283  // mouse_handler expects at least one team for linger mode to work.
284  send_actions();
285  assert(is_regular_game_end());
286  if(!get_end_level_data().transient.linger_mode || get_teams().empty() || video::headless()) {
287  if(!is_host() && gamestate().has_next_scenario()) {
288  // If we continue without lingering we need to
289  // make sure the host uploads the next scenario
290  // before we attempt to download it.
291  wait_for_upload();
292  }
293  } else {
294  linger();
295  }
296  end_turn_requested_ = true;
297 }
298 
300 {
301  bool need_wait = !is_host() && gamestate().has_next_scenario();
303 }
304 
306 {
307  undo_stack().clear();
309  send_actions();
310 }
311 
313 {
315  send_actions();
316 }
317 
318 
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 
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) {
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  }
388  }
389 }
390 
391 namespace
392 {
393 /** Data sent by the server in response to the ping command. */
394 struct ping_response
395 {
396  explicit ping_response(const config& cfg)
397  : requested_at(std::chrono::steady_clock::duration{cfg["requested_at"].to_long_long()})
398  , processed_at(chrono::parse_timestamp(cfg["processed_at"]))
399  {
400  }
401 
402  std::chrono::steady_clock::time_point requested_at;
403  std::chrono::system_clock::time_point processed_at;
404 };
405 
406 } // namespace
407 
409 {
410  // the simple wesnothserver implementation in wesnoth was removed years ago.
411  assert(cfg.all_children_count() == 1);
412  assert(cfg.attribute_range().empty());
413  if(!recorder().at_end())
414  {
415  ERR_NW << "processing network data while still having data on the replay.";
416  }
417 
418  if (const auto message = cfg.optional_child("message"))
419  {
421  std::chrono::system_clock::now(),
422  message.value()["sender"],
423  message.value()["side"].to_int(),
424  message.value()["message"],
427  }
428  else if (auto whisper = cfg.optional_child("whisper") /*&& is_observer()*/)
429  {
431  std::chrono::system_clock::now(),
432  "whisper: " + whisper["sender"].str(),
433  0,
434  whisper["message"],
436  prefs::get().message_bell());
437  }
438  else if (auto observer = cfg.optional_child("observer") )
439  {
441  }
442  else if (auto observer_quit = cfg.optional_child("observer_quit"))
443  {
445  }
446  else if (cfg.has_child("leave_game")) {
447  const bool has_reason = cfg.mandatory_child("leave_game").has_attribute("reason");
448  throw leavegame_wesnothd_error(has_reason ? cfg.mandatory_child("leave_game")["reason"].str() : "");
449  }
450  else if (auto turn = cfg.optional_child("turn"))
451  {
452  return process_network_turn_impl(*turn, chat_only);
453  }
454  else if (cfg.has_child("whiteboard"))
455  {
456  set_scontext_unsynced scontext;
457  get_whiteboard()->process_network_data(cfg);
458  }
459  else if (auto change = cfg.optional_child("change_controller"))
460  {
462  }
463  else if (auto side_drop_c = cfg.optional_child("side_drop"))
464  {
465  process_network_side_drop_impl(*side_drop_c);
466  }
467 
468  // The host has ended linger mode in a campaign -> enable the "End scenario" button
469  // and tell we did get the notification.
470  else if (cfg.has_child("notify_next_scenario")) {
472  }
473 
474  //If this client becomes the new host, notify the play_controller object about it
475  else if (cfg.has_child("host_transfer")) {
476  mp_info_->is_host = true;
477  if(is_linger_mode()) {
478  end_turn_enable(true);
479  }
480  }
481  else if(auto ping = cfg.optional_child("ping")) {
482  auto now = std::chrono::steady_clock::now();
483  ping_response packet{*ping};
484 
485  auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(now - packet.requested_at);
486  std::string msg = formatter() << "Packet roundtrip took " << delta.count() << "ms";
487 
489  std::chrono::system_clock::now(), "ping", 0, msg, events::chat_handler::MESSAGE_PUBLIC, prefs::get().message_bell());
490  }
491  else
492  {
493  ERR_NW << "found unknown command:\n" << cfg.debug();
494  }
495 
497 }
498 
500 {
501  //t can contain a [command] or a [upload_log]
502  assert(t.all_children_count() == 1);
503 
504  if(auto command = t.optional_child("command")) {
505  auto commandtype = get_replay_action_type(*command);
506  if(chat_only && (commandtype == REPLAY_ACTION_TYPE::SYNCED || commandtype == REPLAY_ACTION_TYPE::INVALID) ) {
508  }
509  if (commandtype == REPLAY_ACTION_TYPE::SYNCED && current_team().is_local()) {
510  // Executing those is better than OOS, also the server checks that other players don't send actions while it's not their turn.
511  ERR_NW << "Received a synced remote user action during our own turn";
512  }
513  }
514 
515  //note, that this function might call itself recursively: do_replay -> ... -> get_user_choice -> ... -> receive_actions -> ... -> handle_turn
517  do_replay();
519 }
520 
522 {
523  // Only the host receives this message when a player leaves/disconnects.
524  const int side_drop = side_drop_c["side_num"].to_int(0);
525  std::size_t index = side_drop -1;
526 
528 
529  if (index >= gamestate().board_.teams().size()) {
530  ERR_NW << "unknown side " << side_drop << " is dropping game";
531  throw ingame_wesnothd_error("");
532  }
533 
534  auto ctrl = side_controller::get_enum(side_drop_c["controller"].str());
535  if(!ctrl) {
536  ERR_NW << "unknown controller type issued from server on side drop: " << side_drop_c["controller"];
537  throw ingame_wesnothd_error("");
538  }
539 
540  if (ctrl == side_controller::type::ai) {
541  gamestate().board_.side_drop_to(side_drop, *ctrl);
542  return;
543  }
544  //null controlled side cannot be dropped because they aren't controlled by anyone.
545  else if (ctrl != side_controller::type::human) {
546  ERR_NW << "unknown controller type issued from server on side drop: " << side_controller::get_string(*ctrl);
547  throw ingame_wesnothd_error("");
548  }
549 
550  int action = 0;
551  int first_observer_option_idx = 0;
552  int control_change_options = 0;
553  bool has_next_scenario = gamestate().has_next_scenario();
554 
555  std::vector<std::string> observers;
556  std::vector<const team *> allies;
557  std::vector<std::string> options;
558 
559  const team &tm = gamestate().board_.teams()[index];
560 
561  for (const team &t : gamestate().board_.teams()) {
562  if (!t.is_enemy(side_drop) && !t.is_local_human() && !t.is_local_ai() && !t.is_network_ai() && !t.is_empty()
563  && t.current_player() != tm.current_player()) {
564  allies.push_back(&t);
565  }
566  }
567 
568  // We want to give host chance to decide what to do for side
569  if (!is_linger_mode() || has_next_scenario) {
570  utils::string_map t_vars;
571 
572  //get all allies in as options to transfer control
573  for (const team *t : allies) {
574  //if this is an ally of the dropping side and it is not us (choose local player
575  //if you want that) and not ai or empty and if it is not the dropping side itself,
576  //get this team in as well
577  t_vars["player"] = t->current_player();
578  options.emplace_back(VGETTEXT("Give control to their ally $player", t_vars));
579  control_change_options++;
580  }
581 
582  first_observer_option_idx = options.size();
583 
584  //get all observers in as options to transfer control
585  for (const std::string &screen_observers : game_display::get_singleton()->observers()) {
586  t_vars["player"] = screen_observers;
587  options.emplace_back(VGETTEXT("Give control to observer $player", t_vars));
588  observers.push_back(screen_observers);
589  control_change_options++;
590  }
591 
592  options.emplace_back(_("Replace with AI"));
593  options.emplace_back(_("Replace with local player"));
594  options.emplace_back(_("Set side to idle"));
595  options.emplace_back(_("Save and abort game"));
596 
597  t_vars["player"] = tm.current_player();
598  t_vars["side_drop"] = std::to_string(side_drop);
599  const std::string gettext_message = VGETTEXT("$player who controlled side $side_drop has left the game. What do you want to do?", t_vars);
600  gui2::dialogs::simple_item_selector dlg("", gettext_message, options);
601  dlg.set_single_button(true);
602  dlg.show();
603  action = dlg.selected_index();
604 
605  // If esc was pressed, default to setting side to idle
606  if (action == -1) {
607  action = control_change_options + 2;
608  }
609  } else {
610  // Always set leaving side to idle if in linger mode and there is no next scenario
611  action = 2;
612  }
613 
614  if (action < control_change_options) {
615  // Grant control to selected ally
616  // 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.
617  gamestate().board_.side_drop_to(side_drop, side_controller::type::human, side_proxy_controller::type::idle);
618 
619  if (action < first_observer_option_idx) {
620  send_change_side_controller(side_drop, allies[action]->current_player());
621  } else {
622  send_change_side_controller(side_drop, observers[action - first_observer_option_idx]);
623  }
624  } else {
625  action -= control_change_options;
626 
627  //make the player an AI, and redo this turn, in case
628  //it was the current player's team who has just changed into
629  //an AI.
630  switch(action) {
631  case 0:
632  on_not_observer();
633  gamestate().board_.side_drop_to(side_drop, side_controller::type::human, side_proxy_controller::type::ai);
634 
635  return;
636 
637  case 1:
638  on_not_observer();
639  gamestate().board_.side_drop_to(side_drop, side_controller::type::human, side_proxy_controller::type::human);
640 
641  return;
642  case 2:
643  gamestate().board_.side_drop_to(side_drop, side_controller::type::human, side_proxy_controller::type::idle);
644 
645  return;
646 
647  case 3:
648  //The user pressed "end game". Don't throw a network error here or he will get
649  //thrown back to the title screen.
650  do_autosave();
652  default:
653  break;
654  }
655  }
656 }
657 
659 {
660 
661  if(change.empty()) {
662  ERR_NW << "Bad [change_controller] signal from server, [change_controller] tag was empty.";
663  return;
664  }
665 
666  const int side = change["side"].to_int();
667  const bool is_local = change["is_local"].to_bool();
668  const std::string player = change["player"];
669  const std::string controller_type = change["controller"];
670  const std::size_t index = side - 1;
671  if(index >= gamestate().board_.teams().size()) {
672  ERR_NW << "Bad [change_controller] signal from server, side out of bounds: " << change.debug();
673  return;
674  }
675 
676  const team & tm = gamestate().board_.teams().at(index);
677  const bool was_local = tm.is_local();
678 
679  gamestate().board_.side_change_controller(side, is_local, player, controller_type);
680 
681  if (!was_local && tm.is_local()) {
682  on_not_observer();
683  }
684 
686 
687  get_whiteboard()->on_change_controller(side,tm);
688 
689  player_type_changed_ |= game_display::get_singleton()->playing_team().side() == side && (was_local || tm.is_local());
690 }
691 
693 {
694  if(!is_networked_mp()) {
695  return;
696  }
697 
698  get_whiteboard()->send_network_data();
699 
700  const bool send_everything = synced_context::is_unsynced() ? !undo_stack().can_undo() : synced_context::undo_blocked();
701  const replay::DATA_TYPE data_type = send_everything ? replay::ALL_DATA : replay::NON_UNDO_DATA;
702 
703  config data = recorder().get_unsent_commands(data_type);
704  if (!data.empty()) {
705  send_to_wesnothd(config{ "turn", data});
706  }
707 }
708 
709 void playmp_controller::send_change_side_controller(int side, const std::string& player)
710 {
711  config cfg;
712  config& change = cfg.add_child("change_controller");
713  change["side"] = side;
714  change["player"] = player;
716 }
double t
Definition: astarsearch.cpp:63
bool can_undo() const
True if there are actions that can be undone.
Definition: undo.hpp:95
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:131
void undo()
Undoes the top action on the undo stack.
Definition: undo.cpp:280
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
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:362
const_attr_itors attribute_range() const
Definition: config.cpp:756
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:312
bool has_attribute(config_key_type key) const
Definition: config.cpp:157
std::size_t all_children_count() const
Definition: config.cpp:302
std::string debug() const
Definition: config.cpp:1230
bool empty() const
Definition: config.cpp:839
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:380
config & add_child(config_key_type key)
Definition: config.cpp:436
virtual void play_slice()
void add_chat_message(const std::chrono::system_clock::time_point &time, const std::string &speaker, int side, const std::string &msg, events::chat_handler::MESSAGE_TYPE type, bool bell)
void remove_observer(const std::string &name)
void add_observer(const std::string &name)
const team & playing_team() const
Definition: display.cpp:326
std::ostringstream wrapper.
Definition: formatter.hpp:40
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:80
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:213
void side_change_controller(int side_num, bool is_local, const std::string &pname, const std::string &controller_type)
Definition: game_board.cpp:229
@ 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
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:393
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(const 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
const auto & timer() 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
void play_slice() 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
bool end_linger() 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 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:647
@ 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:220
config get_unsent_commands(DATA_TYPE data_type)
Definition: replay.cpp:383
void add_countdown_update(int value, int team)
Definition: replay.cpp:226
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:379
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 is_unsynced()
static bool run_and_store(const std::string &commandname, const config &data, action_spectator &spectator=get_default_spectator())
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:236
int side() const
Definition: team.hpp:179
void set_countdown_time(const std::chrono::milliseconds &amount) const
Definition: team.hpp:234
const std::string & current_player() const
Definition: team.hpp:258
bool is_local() const
Definition: team.hpp:285
int action_bonus_count() const
Definition: team.hpp:235
std::chrono::milliseconds countdown_time() const
Definition: team.hpp:233
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.
const config * cfg
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
std::size_t i
Definition: function.cpp:1032
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:97
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
auto parse_timestamp(long long val)
Definition: chrono.hpp:47
const color_t NORMAL_COLOR
std::string observer
logger & info()
Definition: log.cpp:351
void turn_changed(const std::string &player_name)
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
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:147
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
std::string_view data
Definition: picture.cpp:188
static lg::log_domain log_engine("engine")
#define ERR_NW
static lg::log_domain log_network("network")
#define LOG_NG
REPLAY_ACTION_TYPE get_replay_action_type(const config &command)
Definition: replay.cpp:677
REPLAY_RETURN do_replay(bool one_move)
Definition: replay.cpp:692
void unblind()
Definition: display.hpp:957
We received invalid data from wesnothd during a game This means we cannot continue with the game but ...
wesnothd_connection & connection
unsigned current_turn
std::chrono::seconds mp_countdown_reservoir_time
std::chrono::seconds mp_countdown_action_bonus
std::chrono::seconds mp_countdown_turn_bonus
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
static constexpr utils::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
Definition: enum_base.hpp:57
Various functions that implement the undoing (and redoing) of in-game commands.