The Battle for Wesnoth  1.17.0-dev
game_launcher.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2021
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 "game_launcher.hpp"
16 #include "game_errors.hpp"
17 
18 #include "ai/manager.hpp" // for manager
19 #include "commandline_options.hpp" // for commandline_options
20 #include "config.hpp" // for config, etc
21 #include "cursor.hpp" // for set, CURSOR_TYPE::NORMAL
22 #include "exceptions.hpp" // for error
23 #include "filesystem.hpp" // for get_user_config_dir, etc
24 #include "game_classification.hpp" // for game_classification, etc
25 #include "game_config.hpp" // for path, no_delay, revision, etc
26 #include "game_config_manager.hpp" // for game_config_manager
28 #include "game_initialization/multiplayer.hpp" // for start_client, etc
29 #include "game_initialization/playcampaign.hpp" // for play_game, etc
30 #include "game_initialization/singleplayer.hpp" // for sp_create_mode
31 #include "generators/map_generator.hpp" // for mapgen_exception
32 #include "gettext.hpp" // for _
33 #include "gui/dialogs/language_selection.hpp" // for language_selection
35 #include "gui/dialogs/message.hpp" // for show error message
37 #include "gui/dialogs/multiplayer/mp_host_game_prompt.hpp" // for host game prompt
39 #include "gui/dialogs/title_screen.hpp" // for show_debug_clock_button
40 #include "gui/dialogs/transient_message.hpp" // for show_transient_message
41 #include "gui/widgets/retval.hpp" // for window, etc
42 #include "gui/widgets/settings.hpp" // for new_widgets
43 #include "language.hpp" // for language_def, etc
44 #include "log.hpp" // for LOG_STREAM, logger, general, etc
45 #include "map/exception.hpp"
47 #include "preferences/display.hpp"
48 #include "preferences/general.hpp" // for disable_preferences_save, etc
49 #include "save_index.hpp"
51 #include "sdl/surface.hpp" // for surface
52 #include "serialization/compression.hpp" // for format::NONE
53 #include "serialization/string_utils.hpp" // for split
54 #include "statistics.hpp"
55 #include "tstring.hpp" // for operator==, operator!=
56 #include "video.hpp" // for CVideo
58 #include "wml_exception.hpp" // for wml_exception
59 
60 #include <algorithm> // for copy, max, min, stable_sort
61 #include <cstdlib> // for system
62 #include <iostream> // for operator<<, basic_ostream, etc
63 #include <new>
64 #include <utility> // for pair
65 
66 #include <SDL2/SDL.h> // for SDL_INIT_JOYSTICK, etc
67 
68 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
69 #include "gui/widgets/debug.hpp"
70 #endif
71 
72 // For wesnothd launch code.
73 #ifdef _WIN32
74 
75 #define WIN32_LEAN_AND_MEAN
76 #include <windows.h>
77 
78 #endif // _WIN32
79 
81 
82 static lg::log_domain log_config("config");
83 #define ERR_CONFIG LOG_STREAM(err, log_config)
84 #define WRN_CONFIG LOG_STREAM(warn, log_config)
85 #define LOG_CONFIG LOG_STREAM(info, log_config)
86 
87 #define LOG_GENERAL LOG_STREAM(info, lg::general())
88 #define WRN_GENERAL LOG_STREAM(warn, lg::general())
89 #define DBG_GENERAL LOG_STREAM(debug, lg::general())
90 
91 static lg::log_domain log_mp_create("mp/create");
92 #define DBG_MP LOG_STREAM(debug, log_mp_create)
93 
94 static lg::log_domain log_network("network");
95 #define ERR_NET LOG_STREAM(err, log_network)
96 
97 static lg::log_domain log_enginerefac("enginerefac");
98 #define LOG_RG LOG_STREAM(info, log_enginerefac)
99 
101  : cmdline_opts_(cmdline_opts)
102  , video_(new CVideo())
103  , font_manager_()
104  , prefs_manager_()
105  , image_manager_()
106  , main_event_context_()
107  , hotkey_manager_()
108  , music_thinker_()
109  , music_muter_()
110  , test_scenarios_{"test"}
111  , screenshot_map_()
113  , state_()
114  , play_replay_(false)
116  , jump_to_multiplayer_(false)
118  , jump_to_editor_(false)
119  , load_data_()
120 {
121  bool no_music = false;
122  bool no_sound = false;
123 
124  // The path can be hardcoded and it might be a relative path.
125  if(!game_config::path.empty() &&
126 #ifdef _WIN32
127  // use c_str to ensure that index 1 points to valid element since c_str() returns null-terminated string
128  game_config::path.c_str()[1] != ':'
129 #else
130  game_config::path[0] != '/'
131 #endif
132  )
133  {
135  // font_manager_.update_font_path()
136  // To update the font path, destroy and recreate the manager
138  new (&font_manager_) font::manager();
139  }
140 
141  if(cmdline_opts_.core_id) {
143  }
144  if(cmdline_opts_.campaign) {
145  jump_to_campaign_.jump = true;
147  std::cerr << "selected campaign id: [" << jump_to_campaign_.campaign_id << "]\n";
148 
151  std::cerr << "selected difficulty: [" << jump_to_campaign_.difficulty << "]\n";
152  } else {
153  jump_to_campaign_.difficulty = -1; // let the user choose the difficulty
154  }
155 
158  std::cerr << "selected scenario id: [" << jump_to_campaign_.scenario_id << "]\n";
159  }
160 
163  }
164  }
165  if(cmdline_opts_.clock)
167  if(cmdline_opts_.debug) {
169  game_config::mp_debug = true;
170  }
171 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
172  if(cmdline_opts_.debug_dot_domain)
173  gui2::debug_layout_graph::set_domain(*cmdline_opts_.debug_dot_domain);
174  if(cmdline_opts_.debug_dot_level)
176 #endif
177  if(cmdline_opts_.editor) {
178  jump_to_editor_ = true;
179  if(!cmdline_opts_.editor->empty()) {
182  }
183  }
184  if(cmdline_opts_.fps)
187  video_->set_fullscreen(true);
188  if(cmdline_opts_.load)
191  if(cmdline_opts_.max_fps) {
192  int fps = std::clamp(*cmdline_opts_.max_fps, 1, 1000);
193  fps = 1000 / fps;
194  // increase the delay to avoid going above the maximum
195  if(1000 % fps != 0) {
196  ++fps;
197  }
199  }
201  no_sound = true;
203  }
205  gui2::new_widgets = true;
207  game_config::no_delay = true;
209  no_music = true;
211  no_sound = true;
213  const int xres = std::get<0>(*cmdline_opts_.resolution);
214  const int yres = std::get<1>(*cmdline_opts_.resolution);
215  if(xres > 0 && yres > 0) {
216  preferences::_set_resolution(point(xres, yres));
218  }
219  }
221  // TODO it could be simplified to use cmdline_opts_ directly if there is no other way to enter screenshot mode
224  no_sound = true;
226  }
227  if (cmdline_opts_.server){
228  jump_to_multiplayer_ = true;
229  // Do we have any server specified ?
230  if(!cmdline_opts_.server->empty()) {
232  } else {
233  // Pick the first server in config
234  if(game_config::server_list.size() > 0) {
236  } else {
237  multiplayer_server_ = "";
238  }
239  }
240  if(cmdline_opts_.username) {
243  if(cmdline_opts_.password) {
245  preferences::set_password(*cmdline_opts.server, *cmdline_opts.username, *cmdline_opts_.password);
246  }
247  }
248  }
249  if(cmdline_opts_.test) {
250  if(!cmdline_opts_.test->empty()) {
252  }
253  }
254  if(!cmdline_opts_.unit_test.empty()) {
256  }
258  video_->set_fullscreen(false);
260  load_data_->show_replay = true;
263 
264  if(!cmdline_opts.nobanner) {
265  std::cerr
266  << "\nData directory: " << filesystem::sanitize_path(game_config::path)
267  << "\nUser configuration directory: " << filesystem::sanitize_path(filesystem::get_user_config_dir())
268  << "\nUser data directory: " << filesystem::sanitize_path(filesystem::get_user_data_dir())
269  << "\nCache directory: " << filesystem::sanitize_path(filesystem::get_cache_dir())
270  << '\n';
271  std::cerr << '\n';
272  }
273 
274  // disable sound in nosound mode, or when sound engine failed to initialize
275  if(no_sound || ((preferences::sound_on() || preferences::music_on() ||
277  !sound::init_sound())) {
278  preferences::set_sound(false);
279  preferences::set_music(false);
282  } else if(no_music) { // else disable the music in nomusic mode
283  preferences::set_music(false);
284  }
285 }
286 
288 {
289  if(!::load_language_list()) {
290  return false;
291  }
292 
293  language_def locale;
294  if(cmdline_opts_.language) {
295  std::vector<language_def> langs = get_languages(true);
296  for(const language_def& def : langs) {
297  if(def.localename == *cmdline_opts_.language) {
298  locale = def;
299  break;
300  }
301  }
302  if(locale.localename.empty()) {
303  std::cerr << "Language symbol '" << *cmdline_opts_.language << "' not found.\n";
304  return false;
305  }
306  } else {
307  locale = get_locale();
308  }
309  ::set_language(locale);
310 
311  return true;
312 }
313 
315 {
316  // Handle special commandline launch flags
319  std::cerr << "--nogui flag is only valid with --multiplayer or --screenshot or --plugin flags\n";
320  return false;
321  }
322  video_->make_fake();
323  game_config::no_delay = true;
324  return true;
325  }
326 
327  // Initialize a new window
328  video_->init_window();
329 
330  // Set window title and icon
331  video_->set_window_title(game_config::get_default_title_string());
332 
333 #if !(defined(__APPLE__))
334  surface icon(image::get_image("icons/icon-game.png", image::UNSCALED));
335  if(icon != nullptr) {
336  video_->set_window_icon(icon);
337  }
338 #endif
339  return true;
340 }
341 
343 {
344  bool error = false;
345 
346  if(!cmdline_opts_.nobanner) std::cerr << "Checking lua scripts... ";
347 
349  // load the "package" package, so that scripts can get what packages they want
351  }
352 
353  // get the application lua kernel, load and execute script file, if script file is present
356 
357  if(!sf->fail()) {
358  /* Cancel all "jumps" to editor / campaign / multiplayer */
359  jump_to_multiplayer_ = false;
360  jump_to_editor_ = false;
361  jump_to_campaign_.jump = false;
362 
363  std::string full_script((std::istreambuf_iterator<char>(*sf)), std::istreambuf_iterator<char>());
364 
365  std::cerr << "\nRunning lua script: " << *cmdline_opts_.script_file << std::endl;
366 
368  } else {
369  std::cerr << "Encountered failure when opening script '" << *cmdline_opts_.script_file << "'\n";
370  error = true;
371  }
372  }
373 
375  std::string filename = *cmdline_opts_.plugin_file;
376 
377  std::cerr << "Loading a plugin file'" << filename << "'...\n";
378 
380 
381  try {
382  if(sf->fail()) {
383  throw std::runtime_error("failed to open plugin file");
384  }
385 
386  /* Cancel all "jumps" to editor / campaign / multiplayer */
387  jump_to_multiplayer_ = false;
388  jump_to_editor_ = false;
389  jump_to_campaign_.jump = false;
390 
391  std::string full_plugin((std::istreambuf_iterator<char>(*sf)), std::istreambuf_iterator<char>());
392 
394 
395  std::size_t i = pm.add_plugin(filename, full_plugin);
396 
397  for(std::size_t j = 0; j < pm.size(); ++j) {
398  std::cerr << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j) << std::endl;
399  }
400 
401  std::cerr << "Starting a plugin...\n";
402  pm.start_plugin(i);
403 
404  for(std::size_t j = 0; j < pm.size(); ++j) {
405  std::cerr << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j) << std::endl;
406  }
407 
408  plugins_context pc("init");
409 
410  for(std::size_t repeat = 0; repeat < 5; ++repeat) {
411  std::cerr << "Playing a slice...\n";
412  pc.play_slice();
413 
414  for(std::size_t j = 0; j < pm.size(); ++j) {
415  std::cerr << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j) << std::endl;
416  }
417  }
418 
419  return true;
420  } catch(const std::exception& e) {
421  gui2::show_error_message(std::string("When loading a plugin, error:\n") + e.what());
422  error = true;
423  }
424  }
425 
426  if(!error && !cmdline_opts_.nobanner) {
427  std::cerr << "ok\n";
428  }
429 
430  return !error;
431 }
432 
433 void game_launcher::set_test(const std::string& id)
434 {
435  state_.clear();
436  state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::TEST;
438  state_.classification().era_id = "era_default";
439 
440  state_.set_carryover_sides_start(config{"next_scenario", id});
441 }
442 
444 {
445  // This first_time variable was added in 70f3c80a3e2 so that using the GUI
446  // menu to load a game works. That seems to have edge-cases, for example if
447  // you try to load a game a second time then Wesnoth exits.
448  static bool first_time = true;
449 
450  if(!cmdline_opts_.test) {
451  return true;
452  }
453 
454  if(!first_time) {
455  return false;
456  }
457 
458  first_time = false;
459 
460  if(test_scenarios_.size() == 0) {
461  // shouldn't happen, as test_scenarios_ is initialised to {"test"}
462  std::cerr << "Error in the test handling code" << std::endl;
463  return false;
464  }
465 
466  if(test_scenarios_.size() > 1) {
467  std::cerr << "You can't run more than one unit test in interactive mode" << std::endl;
468  }
469 
470  set_test(test_scenarios_.at(0));
471 
473 
474  try {
475  campaign_controller ccontroller(state_);
476  ccontroller.play_game();
477  } catch(savegame::load_game_exception& e) {
478  load_data_ = std::move(e.data_);
479  return true;
480  }
481 
482  return false;
483 }
484 
485 /**
486  * Runs unit tests specified on the command line.
487  *
488  * If multiple unit tests were specified, then this will stop at the first test
489  * which returns a non-zero status.
490  */
491 // Same as play_test except that we return the results of play_game.
492 // \todo "same ... except" ... and many other changes, such as testing the replay
494 {
495  // There's no copy of play_test's first_time variable. That seems to be for handling
496  // the player loading a game via the GUI, which makes no sense in a non-interactive test.
497  if(cmdline_opts_.unit_test.empty()) {
499  }
500 
501  auto ret = unit_test_result::TEST_FAIL; // will only be returned if no test is run
502  for(const auto& scenario : test_scenarios_) {
503  set_test(scenario);
504  ret = single_unit_test();
505  const char* describe_result;
506  switch(ret) {
508  describe_result = "PASS TEST";
509  break;
511  describe_result = "FAIL TEST (INVALID REPLAY)";
512  break;
514  describe_result = "FAIL TEST (ERRORED REPLAY)";
515  break;
517  describe_result = "FAIL TEST (WML EXCEPTION)";
518  break;
520  describe_result = "FAIL TEST (DEFEAT)";
521  break;
523  describe_result = "PASS TEST (VICTORY)";
524  break;
526  describe_result = "BROKE STRICT (PASS)";
527  break;
529  describe_result = "BROKE STRICT (FAIL)";
530  break;
532  describe_result = "BROKE STRICT (DEFEAT)";
533  break;
535  describe_result = "BROKE STRICT (VICTORY)";
536  break;
537  default:
538  describe_result = "FAIL TEST (UNKNOWN)";
539  break;
540  }
541 
542  std::cerr << describe_result << ": " << scenario << std::endl;
543  if(ret != unit_test_result::TEST_PASS) {
544  break;
545  }
546  }
547 
548  return ret;
549 }
550 
552 {
554 
555  LEVEL_RESULT game_res = LEVEL_RESULT::TEST_FAIL;
556  try {
557  campaign_controller ccontroller(state_, true);
558  game_res = ccontroller.play_game();
559  if(game_res == LEVEL_RESULT::TEST_FAIL) {
560  if(lg::broke_strict()) {
562  } else {
564  }
565  }
566  } catch(const wml_exception& e) {
567  std::cerr << "Caught WML Exception:" << e.dev_message << std::endl;
569  }
570 
572 
574  return pass_victory_or_defeat(game_res);
575  }
576 
578  save.save_game_automatic(false, "unit_test_replay");
579 
581  savegame::save_index_class::default_saves_dir(), save.filename(), "", true, true, false};
582 
583  if(!load_game()) {
584  std::cerr << "Failed to load the replay!" << std::endl;
585  return unit_test_result::TEST_FAIL_LOADING_REPLAY; // failed to load replay
586  }
587 
588  try {
589  const bool was_strict_broken = lg::broke_strict();
590  campaign_controller ccontroller(state_, true);
591  ccontroller.play_replay();
592  if(!was_strict_broken && lg::broke_strict()) {
593  std::cerr << "Observed failure on replay" << std::endl;
595  }
596  } catch(const wml_exception& e) {
597  std::cerr << "WML Exception while playing replay: " << e.dev_message << std::endl;
599  }
600 
601  return pass_victory_or_defeat(game_res);
602 }
603 
605 {
606  if(res == LEVEL_RESULT::DEFEAT) {
607  if(lg::broke_strict()) {
609  } else {
611  }
612  } else if(res == LEVEL_RESULT::VICTORY) {
613  if(lg::broke_strict()) {
615  } else {
617  }
618  }
619 
620  if(lg::broke_strict()) {
622  } else {
624  }
625 }
626 
628 {
630  return true;
631  }
632 
634 
636 
638  return false;
639 }
640 
642 {
644  return true;
645  }
646 
647  state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::MULTIPLAYER;
648  DBG_GENERAL << "Current campaign type: " << state_.classification().campaign_type << std::endl;
649 
650  try {
652  } catch(const config::error& e) {
653  std::cerr << "Error loading game config: " << e.what() << std::endl;
654  return false;
655  }
656 
657  // A default output filename
658  std::string outfile = "wesnoth_image.bmp";
659 
660  // If a output path was given as an argument, use that instead
662  outfile = *cmdline_opts_.render_image_dst;
663  }
664 
666  exit(1);
667  }
668 
669  return false;
670 }
671 
673 {
674  return load_data_.has_value();
675 }
676 
678 {
679  assert(game_config_manager::get());
680 
681  DBG_GENERAL << "Current campaign type: " << state_.classification().campaign_type << std::endl;
682 
684  if(load_data_) {
685  load.data() = std::move(*load_data_);
687  }
688 
689  try {
690  if(!load.load_game()) {
691  return false;
692  }
693 
694  load.set_gamestate();
695  try {
697  } catch(const config::error&) {
698  return false;
699  }
700 
701  } catch(const config::error& e) {
702  if(e.message.empty()) {
703  gui2::show_error_message(_("The file you have tried to load is corrupt"));
704  } else {
705  gui2::show_error_message(_("The file you have tried to load is corrupt: '") + e.message + '\'');
706  }
707 
708  return false;
709  } catch(const wml_exception& e) {
710  e.show();
711  return false;
712  } catch(const filesystem::io_exception& e) {
713  if(e.message.empty()) {
714  gui2::show_error_message(_("File I/O Error while reading the game"));
715  } else {
716  gui2::show_error_message(_("File I/O Error while reading the game: '") + e.message + '\'');
717  }
718 
719  return false;
720  } catch(const game::error& e) {
721  if(e.message.empty()) {
722  gui2::show_error_message(_("The file you have tried to load is corrupt"));
723  } else {
724  gui2::show_error_message(_("The file you have tried to load is corrupt: '") + e.message + '\'');
725  }
726 
727  return false;
728  }
729 
730  play_replay_ = load.data().show_replay;
731  LOG_CONFIG << "is middle game savefile: " << (state_.is_mid_game_save() ? "yes" : "no") << "\n";
732  LOG_CONFIG << "show replay: " << (play_replay_ ? "yes" : "no") << "\n";
733  // in case load.data().show_replay && !state_.is_mid_game_save()
734  // there won't be any turns to replay, but the
735  // user gets to watch the intro sequence again ...
736 
737  if(state_.is_mid_game_save() && load.data().show_replay) {
739  }
740 
743  }
744 
745  if(load.data().cancel_orders) {
747  }
748 
749  return true;
750 }
751 
753 {
754  state_.clear();
756  play_replay_ = false;
757 
759 }
760 
762 {
764 }
765 
767 {
768  if(jump_to_campaign_.jump) {
769  if(new_campaign()) {
771  jump_to_campaign_.jump = false;
773  } else {
774  jump_to_campaign_.jump = false;
775  return false;
776  }
777  }
778 
779  return true;
780 }
781 
783 {
785  jump_to_multiplayer_ = false;
787  ;
788  } else {
789  return false;
790  }
791  }
792 
793  return true;
794 }
795 
797 {
798  if(jump_to_editor_) {
799  jump_to_editor_ = false;
800 
801  const std::string to_open = load_data_ ? filesystem::normalize_path(load_data_->filename) : "";
803 
805  return false;
806  }
807  }
808 
809  return true;
810 }
811 
813 {
814  const std::string wesnothd_program = preferences::get_mp_server_program_name().empty()
817 
818  std::string config = filesystem::get_user_config_dir() + "/lan_server.cfg";
819  if (!filesystem::file_exists(config)) {
820  // copy file if it isn't created yet
822  }
823 
824 #ifndef _WIN32
825  std::string command = "\"" + wesnothd_program +"\" -c \"" + config + "\" -d -t 2 -T 5";
826 #else
827  // start wesnoth as background job
828  std::string command = "cmd /C start \"wesnoth server\" /B \"" + wesnothd_program + "\" -c \"" + config + "\" -t 2 -T 5";
829  // Make sure wesnothd's console output is visible on the console window by
830  // disabling SDL's stdio redirection code for this and future child
831  // processes. No need to bother cleaning this up because it's only
832  // meaningful to SDL applications during pre-main initialization.
833  SetEnvironmentVariableA("SDL_STDIO_REDIRECT", "0");
834 #endif
835  LOG_GENERAL << "Starting wesnothd: "<< command << "\n";
836  if (std::system(command.c_str()) == 0) {
837  // Give server a moment to start up
838  SDL_Delay(50);
839  return;
840  }
842 
843  // Couldn't start server so throw error
844  WRN_GENERAL << "Failed to run server start script" << std::endl;
845  throw game::mp_server_error("Starting MP server failed!");
846 }
847 
849 {
850  try {
851  if(mode == mp_mode::HOST) {
852  try {
853  start_wesnothd();
854  } catch(const game::mp_server_error&) {
856 
857  try {
858  start_wesnothd();
859  } catch(const game::mp_server_error&) {
860  return false;
861  }
862  }
863  }
864 
865  // If a server address wasn't specified, prompt for it now.
866  if(mode != mp_mode::LOCAL && multiplayer_server_.empty()) {
867  if(!gui2::dialogs::mp_connect::execute()) {
868  return false;
869  }
870 
871  // The prompt saves its input to preferences.
873 
874  if(multiplayer_server_ != preferences::builtin_servers_list().front().address) {
876  }
877  }
878 
879  // create_engine already calls game_config_manager::get()->load_config but maybe its better to have MULTIPLAYER
880  // defined while we are in the lobby.
882 
883  events::discard_input(); // prevent the "keylogger" effect
885 
886  if(mode == mp_mode::LOCAL) {
888  } else {
890  multiplayer_server_.clear();
891  }
892 
893  } catch(const wesnothd_rejected_client_error& e) {
895  } catch(const game::mp_server_error& e) {
896  gui2::show_error_message(_("Error while starting server: ") + e.message);
897  } catch(const game::load_game_failed& e) {
898  gui2::show_error_message(_("The game could not be loaded: ") + e.message);
899  } catch(const game::game_error& e) {
900  gui2::show_error_message(_("Error while playing the game: ") + e.message);
901  } catch(const mapgen_exception& e) {
902  gui2::show_error_message(_("Map generator error: ") + e.message);
903  } catch(const wesnothd_error& e) {
904  if(!e.message.empty()) {
905  ERR_NET << "caught network error: " << e.message << std::endl;
906 
907  std::string user_msg;
908  auto conn_err = dynamic_cast<const wesnothd_connection_error*>(&e);
909 
910  if(conn_err) {
911  // The wesnothd_connection_error subclass is only thrown with messages
912  // from boost::system::error_code which we can't translate ourselves.
913  // It's also the originator of the infamous EOF error that happens when
914  // the server dies. <https://github.com/wesnoth/wesnoth/issues/3005>. It
915  // will provide a translated string instead of that when it happens.
916  user_msg = !conn_err->user_message.empty()
917  ? conn_err->user_message
918  : _("Connection failed: ") + e.message;
919  } else {
920  // This will be a message from the server itself, which we can
921  // probably translate.
922  user_msg = translation::gettext(e.message.c_str());
923  }
924 
925  gui2::show_error_message(user_msg);
926  } else {
927  ERR_NET << "caught network error" << std::endl;
928  }
929  } catch(const config::error& e) {
930  if(!e.message.empty()) {
931  ERR_CONFIG << "caught config::error: " << e.message << std::endl;
933  } else {
934  ERR_CONFIG << "caught config::error" << std::endl;
935  }
936  } catch(const incorrect_map_format_error& e) {
937  gui2::show_error_message(_("The game map could not be loaded: ") + e.message);
938  } catch(savegame::load_game_exception& e) {
939  load_data_ = std::move(e.data_);
940  // this will make it so next time through the title screen loop, this game is loaded
941  } catch(const wml_exception& e) {
942  e.show();
943  } catch(const game::error& e) {
944  std::cerr << "caught game::error...\n";
945  gui2::show_error_message(_("Error: ") + e.message);
946  }
947 
948  return true;
949 }
950 
952 {
954  return true;
955  }
956 
957  DBG_MP << "starting multiplayer game from the commandline" << std::endl;
958 
960 
961  events::discard_input(); // prevent the "keylogger" effect
963 
965 
966  return false;
967 }
968 
970 {
971  if(!gui2::dialogs::language_selection::execute()) {
972  return false;
973  }
974 
976  video_->set_window_title(game_config::get_default_title_string());
977  }
978 
983 
984  return true;
985 }
986 
988 {
989  assert(!load_data_);
990  if(play_replay_) {
991  play_replay();
992  return;
993  }
994 
995  gui2::dialogs::loading_screen::display([this, reload]() {
997 
998  if(reload == reload_mode::RELOAD_DATA) {
999  try {
1002  } catch(const config::error&) {
1003  return;
1004  }
1005  }
1006  });
1007 
1008  try {
1009  campaign_controller ccontroller(state_);
1010  ccontroller.play_game();
1011  ai::manager::singleton_ = nullptr;
1012  } catch(savegame::load_game_exception& e) {
1013  load_data_ = std::move(e.data_);
1014  // this will make it so next time through the title screen loop, this game is loaded
1015  } catch(const wml_exception& e) {
1016  e.show();
1017  } catch(const mapgen_exception& e) {
1018  gui2::show_error_message(_("Map generator error: ") + e.message);
1019  }
1020 }
1021 
1023 {
1024  assert(!load_data_);
1025  try {
1026  campaign_controller ccontroller(state_);
1027  ccontroller.play_replay();
1028  } catch(savegame::load_game_exception& e) {
1029  load_data_ = std::move(e.data_);
1030  // this will make it so next time through the title screen loop, this game is loaded
1031  } catch(const wml_exception& e) {
1032  e.show();
1033  }
1034 }
1035 
1037 {
1038  while(true) {
1040 
1042 
1043  editor::EXIT_STATUS res = editor::start(filename);
1044 
1045  if(res != editor::EXIT_RELOAD_DATA) {
1046  return res;
1047  }
1048 
1050  }
1051 
1052  return editor::EXIT_ERROR; // not supposed to happen
1053 }
1054 
1056 {
1057  load_data_.reset();
1058 }
1059 
1061 {
1062  try {
1064  } catch(...) {
1065  }
1066 }
bool has_load_data() const
An error occurred inside the underlying network communication code (boost asio) TODO: find a short na...
An error occurred during when trying to communicate with the wesnothd server.
bool new_widgets
Do we wish to use the new library or not.
Definition: settings.cpp:24
surface get_image(const image::locator &i_locator, TYPE type)
Caches and returns an image.
Definition: picture.cpp:815
bool set_sound(bool ison)
Definition: general.cpp:692
void close_sound()
Definition: sound.cpp:494
game_launcher(const commandline_options &cmdline_opts)
std::string get_program_invocation(const std::string &program_name)
Returns the appropriate invocation for a Wesnoth-related binary, assuming that it is located in the s...
LEVEL_RESULT play_replay()
bool load_game()
Load a game with pre-setting information for the load-game dialog.
Definition: savegame.cpp:164
static lg::log_domain log_mp_create("mp/create")
std::optional< std::string > core_id
Non-empty if –core was given on the command line.
bool turn_bell()
Definition: general.cpp:634
void discard_input()
Discards all input events.
Definition: events.cpp:850
static void reset_translations()
Definition: tstring.cpp:650
Error used when the client is rejected by the MP server.
LEVEL_RESULT play_game()
bool is_mid_game_save() const
Definition: saved_game.hpp:105
bool nogui
True if –nogui was given on the command line.
bool play_multiplayer(mp_mode mode)
std::string scenario_id
The ID of the scenario within the campaign to jump to.
The class for loading a savefile.
Definition: savegame.hpp:98
bool script_unsafe_mode
Whether to load the "package" package for the scripting environment.
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:175
void _set_maximized(bool ison)
Definition: general.cpp:424
std::string label
Name of the game (e.g.
void disable_preferences_save()
Definition: general.cpp:226
std::optional< std::string > test
Non-empty if –test was given on the command line.
void set_gamestate()
Generate the gamestate out of the loaded game config.
Definition: savegame.cpp:281
bool cancel_orders
State of the "cancel_orders" checkbox in the load-game dialog.
Definition: savegame.hpp:66
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:35
Add a special kind of assert to validate whether the input from WML doesn&#39;t contain any problems that...
const std::unique_ptr< CVideo > video_
#define DBG_MP
std::optional< std::string > password
Non-empty if –password was given on the command line.
const std::string & filename() const
Definition: savegame.hpp:176
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:40
Error used when game loading fails.
Definition: game_errors.hpp:29
std::optional< unsigned int > translation_percent
Non-empty if –all-translations or –translations-over is given on the command line.
void show_wesnothd_server_search()
Definition: display.cpp:150
static std::shared_ptr< save_index_class > default_saves_dir()
Returns an instance for managing saves in filesystem::get_saves_dir()
Definition: save_index.cpp:209
Definition: video.hpp:31
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:262
std::string network_host()
Definition: game.cpp:400
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup, const bool restore_background)
Shows a transient message to the user.
static void progress(loading_stage stage=loading_stage::none)
void unify_controllers()
Definition: saved_game.cpp:702
const language_def & get_locale()
Definition: language.cpp:328
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
void load_game_config_for_game(const game_classification &classification, const std::string &scenario_id)
void _set_resolution(const point &res)
Definition: general.cpp:418
void set_language(const std::string &language, const std::vector< std::string > *)
Definition: gettext.cpp:445
save_result save_image(const locator &i_locator, const std::string &filename)
Definition: picture.cpp:1080
std::string get_default_title_string()
void set_min_translation_percent(int percent)
Definition: language.cpp:143
void clear()
Definition: saved_game.cpp:792
bool fps
True if –fps was given on the command line.
std::vector< std::string > unit_test
Non-empty if –unit was given on the command line.
unit_test_result unit_test()
Runs unit tests specified on the command line.
bool change_language()
void set_network_host(const std::string &host)
Definition: game.cpp:410
std::string normalize_path(const std::string &fpath, bool normalize_separators, bool resolve_dot_entries)
Returns the absolute path of a file.
bool noreplaycheck
True if –noreplaycheck was given on the command line.
std::optional< std::string > load
Non-empty if –load was given on the command line.
void init_textdomains(const game_config_view &cfg)
Initializes the list of textdomains from a configuration object.
Definition: language.cpp:366
std::optional< int > max_fps
Max FPS specified by –max-fps option.
static std::string _(const char *str)
Definition: gettext.hpp:92
std::string jump_to_campaign_id() const
Return the ID of the campaign to jump to (skipping the main menu).
Definitions for the interface to Wesnoth Markup Language (WML).
load_game_metadata & data()
Definition: savegame.hpp:117
bool sound_on()
Definition: general.cpp:687
#define DBG_GENERAL
load_game_metadata data_
Definition: savegame.hpp:91
std::string user_message
User-friendly and potentially translated message for use in the UI.
jump_to_campaign_info jump_to_campaign_
void flush_cache()
Purges all image caches.
Definition: picture.cpp:221
bool init_sound()
Definition: sound.cpp:442
std::string get_scenario_id() const
Definition: saved_game.cpp:648
static lg::log_domain log_config("config")
bool broke_strict()
Definition: log.cpp:168
std::vector< std::string > test_scenarios_
std::string get_cwd()
Definition: filesystem.cpp:878
bool with_replay
True if –with-replay was given on the command line.
void set_mp_server_program_name(const std::string &path)
Definition: game.cpp:514
void show() const
Shows the error in a dialog.
bool play_render_image_mode()
bool headless_unit_test
True if –unit is used and –showgui is not present.
static game_config_manager * get()
unit_test_result
Status code after running a unit test, should match the run_wml_tests script and the documentation fo...
#define ERR_CONFIG
font::manager font_manager_
bool skip_story
Whether the story screen should be skipped.
std::optional< std::string > render_image_dst
Output file to put rendered image path in.
const std::vector< game_config::server_info > & builtin_servers_list()
Definition: game.cpp:369
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:84
static lg::log_domain log_enginerefac("enginerefac")
void start_plugin(std::size_t idx)
Definition: manager.cpp:101
std::optional< savegame::load_game_metadata > load_data_
std::string get_name(std::size_t idx)
Definition: manager.cpp:94
This file contains the settings handling of the widget library.
void start_local_game_commandline(const commandline_options &cmdline_opts)
Starts a multiplayer game in single-user mode using command line settings.
bool nodelay
True if –nodelay was given on the command line.
std::string get_user_data_dir()
Definition: filesystem.cpp:791
void write_file(const std::string &fname, const std::string &data)
Throws io_exception if an error occurs.
static lg::log_domain log_network("network")
std::string multiplayer_server_
std::string campaign_define
If there is a define the campaign uses to customize data.
editor::EXIT_STATUS start_editor()
void set_login(const std::string &login)
std::optional< int > campaign_difficulty
Non-empty if –campaign-difficulty was given on the command line.
std::string localename
Definition: language.hpp:54
language_list get_languages(bool all)
Return a list of available translations.
Definition: language.cpp:126
void load_package()
Loads the package library into lua environment.
bool windowed
True if –windowed was given on the command line.
void set_show_fps(bool value)
Definition: general.cpp:859
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:38
void set_test(const std::string &id)
void set_password(const std::string &server, const std::string &login, const std::string &key)
void set_core_id(const std::string &core_id)
Definition: general.cpp:328
std::string path
Definition: game_config.cpp:38
bool multiplayer
True if –multiplayer was given on the command line.
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands...
bool UI_sound_on()
Definition: general.cpp:658
std::string sanitize_path(const std::string &path)
Sanitizes a path to remove references to the user&#39;s name.
const char * what() const noexcept
Definition: exceptions.hpp:35
bool clock
True if –clock was given on the command line.
Error used for any general game error, e.g.
Definition: game_errors.hpp:45
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
Definition: filesystem.cpp:997
bool nobanner
True if –nobanner was given on the command line.
std::string screenshot_map_
#define LOG_CONFIG
void set_draw_delay(int value)
Definition: general.cpp:869
void flush_cache()
Definition: sound.cpp:194
void set_level(const std::string &value)
Definition: game.cpp:709
bool load_font_config()
std::optional< std::string > render_image
Image path to render.
Helper class, don&#39;t construct this directly.
bool nomusic
True if –nomusic was given on the command line.
std::string dev_message
The message for developers telling which problem was triggered, this shouldn&#39;t be translated...
std::optional< std::string > campaign
Non-empty if –campaign was given on the command line.
std::string get_cache_dir()
Definition: filesystem.cpp:796
Exception used to signal that the user has decided to abortt a game, and to load another game instead...
Definition: savegame.hpp:82
void run(char const *prog, const std::string &name, int nArgs=0)
Runs a plain script.
#define WRN_GENERAL
static std::string gettext(const char *str)
Definition: gettext.hpp:59
bool debug
True if –debug was given on the command line.
std::size_t i
Definition: function.cpp:940
std::size_t add_plugin(const std::string &name, const std::string &prog)
Definition: manager.cpp:118
std::string get_mp_server_program_name()
Definition: game.cpp:523
std::string get_wml_location(const std::string &filename, const std::string &current_dir)
Returns a complete path to the actual WML file or directory or an empty string if the file isn&#39;t pres...
unit_test_result pass_victory_or_defeat(LEVEL_RESULT res)
void clean_saves(const std::string &label)
Delete all autosaves of a certain scenario from the default save directory.
Definition: savegame.cpp:68
void load_game_config_for_create(bool is_mp, bool is_test=false)
Game configuration data as global variables.
Definition: build_info.cpp:58
bool new_widgets
True if –new-widgets was given on the command line.
std::optional< std::string > screenshot_output_file
Output file to put screenshot in.
#define ERR_NET
An exception object used when an IO error occurs.
Definition: filesystem.hpp:47
bool music_on()
Definition: general.cpp:710
bool load_language_list()
Definition: language.cpp:103
Holds a 2D point.
Definition: point.hpp:23
bool set_UI_sound(bool ison)
Definition: general.cpp:663
bool goto_multiplayer()
Declarations for File-IO.
std::optional< std::string > script_file
File to load lua script from.
static void save(LexState *ls, int c)
Definition: llex.cpp:57
std::optional< std::string > username
Non-empty if –username was given on the command line.
Class for replay saves (either manually or automatically).
Definition: savegame.hpp:265
bool campaign_skip_story
True if –skip-story was given on the command line.
unit_test_result single_unit_test()
Internal to the implementation of unit_test().
static manager * singleton_
Definition: manager.hpp:440
std::optional< std::string > server
Non-empty if –server was given on the command line.
std::string get_user_config_dir()
Definition: filesystem.cpp:762
bool screenshot
True if –screenshot was given on the command line.
void cancel_orders()
Definition: saved_game.cpp:685
std::string get_detailed_status(std::size_t idx)
Definition: manager.cpp:83
bool show_replay
State of the "show_replay" checkbox in the load-game dialog.
Definition: savegame.hpp:63
static void display(std::function< void()> f)
const commandline_options & cmdline_opts_
bool select_campaign(saved_game &state, jump_to_campaign_info jump_to_campaign)
Base class for all the errors encountered by the engine.
Definition: exceptions.hpp:27
game_classification & classification()
Definition: saved_game.hpp:55
std::string screenshot_filename_
void set_carryover_sides_start(config carryover_sides_start)
Definition: saved_game.cpp:161
saved_game state_
std::optional< std::string > screenshot_map_file
Map file to make a screenshot of.
bool set_music(bool ison)
Definition: general.cpp:715
Standard logging facilities (interface).
void set_skip_story(bool skip_story)
Definition: saved_game.hpp:140
bool set_turn_bell(bool ison)
Definition: general.cpp:639
void clear_loaded_game()
std::string message
Definition: exceptions.hpp:29
EXIT_STATUS start(const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
Definition: editor_main.cpp:29
bool fullscreen
True if –fullscreen was given on the command line.
bool jump
Whether the game should immediately start a campaign.
std::optional< std::string > editor
Non-empty if –editor was given on the command line.
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:205
#define e
std::size_t size()
Definition: manager.cpp:68
void launch_game(reload_mode reload=reload_mode::RELOAD_DATA)
int difficulty
The difficulty at which to launch the campaign.
bool nosound
True if –nosound was given on the command line.
bool play_screenshot_mode()
lua_kernel_base * get_kernel_base()
Definition: manager.cpp:63
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
std::optional< std::string > campaign_scenario
Non-empty if –campaign-scenario was given on the command line.
void clear_current_scenario()
Delete the current scenario from the stats.
Definition: statistics.cpp:788
Unmodified original-size image.
Definition: picture.hpp:231
void start_client(const std::string &host)
Pubic entry points for the MP workflow.
std::optional< std::pair< int, int > > resolution
Pair of AxB values specified after –resolution.
bool init_lua_script()
std::vector< server_info > server_list
Definition: game_config.cpp:94
std::optional< std::string > plugin_file
File to load a lua plugin (similar to a script) from.
bool save_game_automatic(bool ask_for_overwrite=false, const std::string &filename="")
Saves a game without user interaction, unless the file exists and it should be asked to overwrite it...
Definition: savegame.cpp:366
bool play_multiplayer_commandline()
void set_debug(bool new_debug)
static plugins_manager * get()
Definition: manager.cpp:58
void play_slice()
Definition: context.cpp:96
#define LOG_GENERAL
bool show_debug_clock_button
Do we wish to show the button for the debug clock.
std::string campaign_id
The ID of the campaign to launch.
void start_local_game()
Starts a multiplayer game in single-user mode.
std::optional< std::string > language
Non-empty if –language was given on the command line.