The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
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"
22#include "floating_label.hpp"
23#include "formula/string_utils.hpp" // for VGETTEXT
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"
39#include "synced_context.hpp"
40#include "video.hpp" // only for faked
43
44static lg::log_domain log_engine("engine");
45static 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
72
74{
76 LOG_NG << "network processing activated again";
77}
78
80{
82 LOG_NG << "network processing stopped";
83}
84
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()) {
128 }
129
130 end_turn_enable(true);
131
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
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
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
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
228void 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
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{
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.
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
305void playmp_controller::surrender(int side_number)
306{
307 undo_stack().clear();
308 recorder().add_surrender(side_number);
309 send_actions();
310}
311
317
318
320{
322 // receive chat during animations and delay
323 // But don't execute turn data during animations etc.
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
338void 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
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
391namespace
392{
393/** Data sent by the server in response to the ping command. */
394struct 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:
633 gamestate().board_.side_drop_to(side_drop, side_controller::type::human, side_proxy_controller::type::ai);
634
635 return;
636
637 case 1:
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()) {
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
704 if (!data.empty()) {
705 send_to_wesnothd(config{ "turn", data});
706 }
707}
708
709void 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}
#define LOG_NG
double t
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:281
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
config & add_child(std::string_view key)
Definition config.cpp:436
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition config.cpp:380
const_attr_itors attribute_range() const
Definition config.cpp:740
std::size_t all_children_count() const
Definition config.cpp:302
bool has_attribute(std::string_view key) const
Definition config.cpp:157
std::string debug() const
Definition config.cpp:1214
bool has_child(std::string_view key) const
Determine whether a config has a child or not.
Definition config.cpp:312
bool empty() const
Definition config.cpp:823
config & mandatory_child(std::string_view key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition config.cpp:362
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:332
std::ostringstream wrapper.
Definition formatter.hpp:40
virtual const std::vector< team > & teams() const override
void side_drop_to(int side_num, side_controller::type ctrl, side_proxy_controller::type proxy=side_proxy_controller::type::human)
void side_change_controller(int side_num, bool is_local, const std::string &pname, const std::string &controller_type)
@ 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 const std::set< std::string > & observers() const override
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)
bool in_phase(game_data::PHASE phase) const
game_board board_
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.
actions::undo_list & undo_stack()
const end_level_data & get_end_level_data() const
std::unique_ptr< hotkey_handler > hotkey_handler_
bool is_linger_mode() const
bool is_observer() const
bool is_regular_game_end() const
saved_game & saved_game_
std::unique_ptr< game_display > gui_
std::vector< team > & get_teams()
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
replay & recorder() const
bool can_undo() const
const auto & timer() const
game_state & gamestate()
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)
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
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:134
@ ALL_DATA
Definition replay.hpp:109
@ NON_UNDO_DATA
Definition replay.hpp:109
void add_surrender(int side_number)
Definition replay.cpp:218
config get_unsent_commands(DATA_TYPE data_type)
Definition replay.cpp:383
void add_countdown_update(int value, int team)
Definition replay.cpp:224
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
bool save_game_interactive(const std::string &message, DIALOG_TYPE dialog_type)
Save a game interactively through the savegame dialog.
Definition savegame.cpp:291
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:239
int side() const
Definition team.hpp:182
void set_countdown_time(const std::chrono::milliseconds &amount) const
Definition team.hpp:237
bool is_local() const
Definition team.hpp:288
int action_bonus_count() const
Definition team.hpp:238
const std::string & current_player() const
Definition team.hpp:261
std::chrono::milliseconds countdown_time() const
Definition team.hpp:236
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.
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:100
An extension of playsingle_controller::hotkey_handler, which has support for MP wesnoth features like...
Standard logging facilities (interface).
#define ERR_NW
const color_t NORMAL_COLOR
void turn_changed(const std::string &player_name)
STL namespace.
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:146
std::string_view data
Definition picture.cpp:188
static lg::log_domain log_engine("engine")
static lg::log_domain log_network("network")
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:956
We received invalid data from wesnothd during a game This means we cannot continue with the game but ...
wesnothd_connection & connection
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.
#define i