The Battle for Wesnoth  1.19.0-dev
game_launcher.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "game_launcher.hpp"
17 #include "game_errors.hpp"
18 
19 #include "ai/manager.hpp" // for manager
20 #include "commandline_options.hpp" // for commandline_options
21 #include "config.hpp" // for config, etc
22 #include "cursor.hpp" // for set, CURSOR_TYPE::NORMAL
23 #include "exceptions.hpp" // for error
24 #include "filesystem.hpp" // for get_user_config_dir, etc
25 #include "game_classification.hpp" // for game_classification, etc
26 #include "game_config.hpp" // for path, no_delay, revision, etc
27 #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/title_screen.hpp" // for show_debug_clock_button
38 #include "gui/dialogs/transient_message.hpp" // for show_transient_message
39 #include "gui/widgets/settings.hpp" // for new_widgets
40 #include "language.hpp" // for language_def, etc
41 #include "log.hpp" // for LOG_STREAM, logger, general, etc
42 #include "map/exception.hpp"
44 #include "preferences/display.hpp"
45 #include "preferences/general.hpp" // for disable_preferences_save, etc
46 #include "save_index.hpp"
48 #include "sdl/surface.hpp" // for surface
49 #include "serialization/compression.hpp" // for format::NONE
50 #include "tstring.hpp" // for operator==, operator!=
51 #include "video.hpp"
53 #include "wml_exception.hpp" // for wml_exception
54 
55 #include <algorithm> // for copy, max, min, stable_sort
56 #ifdef _WIN32
57 #include <boost/process/windows.hpp>
58 #endif
59 #include <boost/process.hpp>
60 #include <cstdlib> // for system
61 #include <new>
62 #include <utility> // for pair
63 
64 
65 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
66 #include "gui/widgets/debug.hpp"
67 #endif
68 
69 
70 static lg::log_domain log_config("config");
71 #define ERR_CONFIG LOG_STREAM(err, log_config)
72 #define WRN_CONFIG LOG_STREAM(warn, log_config)
73 #define LOG_CONFIG LOG_STREAM(info, log_config)
74 
75 #define ERR_GENERAL LOG_STREAM(err, lg::general())
76 #define LOG_GENERAL LOG_STREAM(info, lg::general())
77 #define WRN_GENERAL LOG_STREAM(warn, lg::general())
78 #define DBG_GENERAL LOG_STREAM(debug, lg::general())
79 
80 #define LOG_TEST FORCE_LOG_TO(lg::general(), log_config)
81 
82 static lg::log_domain log_mp_create("mp/create");
83 #define DBG_MP LOG_STREAM(debug, log_mp_create)
84 
85 static lg::log_domain log_network("network");
86 #define ERR_NET LOG_STREAM(err, log_network)
87 
88 static lg::log_domain log_enginerefac("enginerefac");
89 #define LOG_RG LOG_STREAM(info, log_enginerefac)
90 
91 namespace bp = boost::process;
92 
94  : cmdline_opts_(cmdline_opts)
95  , font_manager_()
96  , prefs_manager_()
97  , image_manager_()
98  , main_event_context_()
99  , hotkey_manager_()
100  , music_thinker_()
101  , music_muter_()
102  , test_scenarios_{"test"}
103  , screenshot_map_()
104  , screenshot_filename_()
105  , state_()
106  , play_replay_(false)
107  , multiplayer_server_()
108  , jump_to_multiplayer_(false)
109  , jump_to_campaign_{}
110  , jump_to_editor_(false)
111  , load_data_()
112 {
113  bool no_music = false;
114  bool no_sound = false;
115 
116  // The path can be hardcoded and it might be a relative path.
117  if(!game_config::path.empty() &&
118 #ifdef _WIN32
119  // use c_str to ensure that index 1 points to valid element since c_str() returns null-terminated string
120  game_config::path.c_str()[1] != ':'
121 #else
122  game_config::path[0] != '/'
123 #endif
124  )
125  {
127  // font_manager_.update_font_path()
128  // To update the font path, destroy and recreate the manager
130  new (&font_manager_) font::manager();
131  }
132 
133  if(cmdline_opts_.core_id) {
135  }
136  if(cmdline_opts_.campaign) {
137  jump_to_campaign_.jump = true;
139  PLAIN_LOG << "selected campaign id: [" << jump_to_campaign_.campaign_id << "]";
140 
143  PLAIN_LOG << "selected difficulty: [" << jump_to_campaign_.difficulty << "]";
144  } else {
145  jump_to_campaign_.difficulty = -1; // let the user choose the difficulty
146  }
147 
150  PLAIN_LOG << "selected scenario id: [" << jump_to_campaign_.scenario_id << "]";
151  }
152 
155  }
156  }
157  if(cmdline_opts_.clock)
159  if(cmdline_opts_.debug) {
161  game_config::mp_debug = true;
162  }
163 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
164  if(cmdline_opts_.debug_dot_domain)
165  gui2::debug_layout_graph::set_domain(*cmdline_opts_.debug_dot_domain);
166  if(cmdline_opts_.debug_dot_level)
168 #endif
169  if(cmdline_opts_.editor) {
170  jump_to_editor_ = true;
171  if(!cmdline_opts_.editor->empty()) {
174  }
175  }
176  if(cmdline_opts_.fps)
179  start_in_fullscreen_ = true;
180  if(cmdline_opts_.load)
183  if(cmdline_opts_.max_fps) {
184  int fps = std::clamp(*cmdline_opts_.max_fps, 1, 1000);
185  fps = 1000 / fps;
186  // increase the delay to avoid going above the maximum
187  if(1000 % fps != 0) {
188  ++fps;
189  }
191  }
193  no_sound = true;
195  }
197  gui2::new_widgets = true;
199  game_config::no_delay = true;
201  no_music = true;
203  no_sound = true;
205  const int xres = std::get<0>(*cmdline_opts_.resolution);
206  const int yres = std::get<1>(*cmdline_opts_.resolution);
207  if(xres > 0 && yres > 0) {
208  preferences::_set_resolution(point(xres, yres));
210  }
211  }
213  // TODO it could be simplified to use cmdline_opts_ directly if there is no other way to enter screenshot mode
216  no_sound = true;
218  }
219  if (cmdline_opts_.server){
220  jump_to_multiplayer_ = true;
221  // Do we have any server specified ?
222  if(!cmdline_opts_.server->empty()) {
224  } else {
225  // Pick the first server in config
226  if(game_config::server_list.size() > 0) {
228  } else {
229  multiplayer_server_ = "";
230  }
231  }
232  if(cmdline_opts_.username) {
235  if(cmdline_opts_.password) {
237  preferences::set_password(*cmdline_opts.server, *cmdline_opts.username, *cmdline_opts_.password);
238  }
239  }
240  }
241  if(cmdline_opts_.test) {
242  if(!cmdline_opts_.test->empty()) {
244  }
245  }
246  if(!cmdline_opts_.unit_test.empty()) {
248  }
250  start_in_fullscreen_ = false;
252  load_data_->show_replay = true;
255 
256  if(!cmdline_opts.nobanner) {
257  PLAIN_LOG
258  << "\nData directory: " << game_config::path
259  << "\nUser configuration directory: " << filesystem::get_user_config_dir()
260  << "\nUser data directory: " << filesystem::get_user_data_dir()
261  << "\nCache directory: " << filesystem::get_cache_dir()
262  << "\n\n";
263  }
264 
265  // disable sound in nosound mode, or when sound engine failed to initialize
266  if(no_sound || ((preferences::sound_on() || preferences::music_on() ||
268  !sound::init_sound())) {
269  preferences::set_sound(false);
270  preferences::set_music(false);
273  } else if(no_music) { // else disable the music in nomusic mode
274  preferences::set_music(false);
275  }
276 }
277 
279 {
280  if(!::load_language_list()) {
281  return false;
282  }
283 
284  language_def locale;
285  if(cmdline_opts_.language) {
286  std::vector<language_def> langs = get_languages(true);
287  for(const language_def& def : langs) {
288  if(def.localename == *cmdline_opts_.language) {
289  locale = def;
290  break;
291  }
292  }
293  if(locale.localename.empty()) {
294  PLAIN_LOG << "Language symbol '" << *cmdline_opts_.language << "' not found.";
295  return false;
296  }
297  } else {
298  locale = get_locale();
299  }
300  ::set_language(locale);
301 
302  return true;
303 }
304 
306 {
307  // Handle special commandline launch flags
312  {
318  {
319  PLAIN_LOG << "--nogui flag is only valid with --multiplayer or --screenshot or --plugin flags";
320  return false;
321  }
323  // Screenshots require a rendering context, and thus a window,
324  // so we create one but hidden.
326  } else {
327  // Other functions don't require a window at all.
329  }
330  game_config::no_delay = true;
331  return true;
332  }
333 
334  // Initialize video subsystem, and create a new window.
335  video::init();
336 
337  // Set window title and icon
339 
340 #if !(defined(__APPLE__))
341  surface icon(image::get_surface(image::locator{"icons/icon-game.png"}, image::UNSCALED));
342  if(icon != nullptr) {
344  }
345 #endif
346  return true;
347 }
348 
350 {
351  bool error = false;
352 
353  if(!cmdline_opts_.nobanner) {
354  STREAMING_LOG << "Checking lua scripts... ";
355  }
356 
358  // load the "package" package, so that scripts can get what packages they want
360  }
361 
362  // get the application lua kernel, load and execute script file, if script file is present
365 
366  if(!sf->fail()) {
367  /* Cancel all "jumps" to editor / campaign / multiplayer */
368  jump_to_multiplayer_ = false;
369  jump_to_editor_ = false;
370  jump_to_campaign_.jump = false;
371 
372  std::string full_script((std::istreambuf_iterator<char>(*sf)), std::istreambuf_iterator<char>());
373 
374  PLAIN_LOG << "\nRunning lua script: " << *cmdline_opts_.script_file;
375 
377  } else {
378  PLAIN_LOG << "Encountered failure when opening script '" << *cmdline_opts_.script_file << '\'';
379  error = true;
380  }
381  }
382 
384  std::string filename = *cmdline_opts_.plugin_file;
385 
386  PLAIN_LOG << "Loading a plugin file'" << filename << "'...";
387 
389 
390  try {
391  if(sf->fail()) {
392  throw std::runtime_error("failed to open plugin file");
393  }
394 
395  /* Cancel all "jumps" to editor / campaign / multiplayer */
396  jump_to_multiplayer_ = false;
397  jump_to_editor_ = false;
398  jump_to_campaign_.jump = false;
399 
400  std::string full_plugin((std::istreambuf_iterator<char>(*sf)), std::istreambuf_iterator<char>());
401 
403 
404  std::size_t i = pm.add_plugin(filename, full_plugin);
405 
406  for(std::size_t j = 0; j < pm.size(); ++j) {
407  PLAIN_LOG << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j);
408  }
409 
410  PLAIN_LOG << "Starting a plugin...";
411  pm.start_plugin(i);
412 
413  for(std::size_t j = 0; j < pm.size(); ++j) {
414  PLAIN_LOG << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j);
415  }
416 
417  plugins_context pc("init");
418 
419  for(std::size_t repeat = 0; repeat < 5; ++repeat) {
420  PLAIN_LOG << "Playing a slice...";
421  pc.play_slice();
422 
423  for(std::size_t j = 0; j < pm.size(); ++j) {
424  PLAIN_LOG << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j);
425  }
426  }
427 
428  return true;
429  } catch(const std::exception& e) {
430  gui2::show_error_message(std::string("When loading a plugin, error:\n") + e.what());
431  error = true;
432  }
433  }
434 
435  if(!error && !cmdline_opts_.nobanner) {
436  PLAIN_LOG << "ok";
437  }
438 
439  return !error;
440 }
441 
442 void game_launcher::set_test(const std::string& id)
443 {
444  state_.clear();
445  state_.classification().type = campaign_type::type::test;
447  state_.classification().era_id = "era_default";
448 
449  state_.set_carryover_sides_start(config{"next_scenario", id});
450 }
451 
453 {
454  // This first_time variable was added in 70f3c80a3e2 so that using the GUI
455  // menu to load a game works. That seems to have edge-cases, for example if
456  // you try to load a game a second time then Wesnoth exits.
457  static bool first_time = true;
458 
459  if(!cmdline_opts_.test) {
460  return true;
461  }
462 
463  if(!first_time) {
464  return false;
465  }
466 
467  first_time = false;
468 
469  if(test_scenarios_.size() == 0) {
470  // shouldn't happen, as test_scenarios_ is initialised to {"test"}
471  PLAIN_LOG << "Error in the test handling code";
472  return false;
473  }
474 
475  if(test_scenarios_.size() > 1) {
476  PLAIN_LOG << "You can't run more than one unit test in interactive mode";
477  }
478 
479  set_test(test_scenarios_.at(0));
480 
482 
483  try {
484  campaign_controller ccontroller(state_);
485  ccontroller.play_game();
486  } catch(savegame::load_game_exception& e) {
487  load_data_ = std::move(e.data_);
488  return true;
489  }
490 
491  return false;
492 }
493 
494 /**
495  * Runs unit tests specified on the command line.
496  *
497  * If multiple unit tests were specified, then this will stop at the first test
498  * which returns a non-zero status.
499  */
500 // Same as play_test except that we return the results of play_game.
501 // \todo "same ... except" ... and many other changes, such as testing the replay
503 {
504  // There's no copy of play_test's first_time variable. That seems to be for handling
505  // the player loading a game via the GUI, which makes no sense in a non-interactive test.
506  if(cmdline_opts_.unit_test.empty()) {
508  }
509 
510  auto ret = unit_test_result::TEST_FAIL; // will only be returned if no test is run
511  for(const auto& scenario : test_scenarios_) {
512  set_test(scenario);
513  ret = single_unit_test();
514  const char* describe_result;
515  switch(ret) {
517  describe_result = "PASS TEST";
518  break;
520  describe_result = "FAIL TEST (INVALID REPLAY)";
521  break;
523  describe_result = "FAIL TEST (ERRORED REPLAY)";
524  break;
526  describe_result = "FAIL TEST (WML EXCEPTION)";
527  break;
529  describe_result = "FAIL TEST (DEFEAT)";
530  break;
532  describe_result = "PASS TEST (VICTORY)";
533  break;
535  describe_result = "BROKE STRICT (PASS)";
536  break;
538  describe_result = "BROKE STRICT (FAIL)";
539  break;
541  describe_result = "BROKE STRICT (DEFEAT)";
542  break;
544  describe_result = "BROKE STRICT (VICTORY)";
545  break;
546  default:
547  describe_result = "FAIL TEST (UNKNOWN)";
548  break;
549  }
550 
551  PLAIN_LOG << describe_result << " (" << int(ret) << "): " << scenario;
552  if(ret != unit_test_result::TEST_PASS) {
553  break;
554  }
555  }
556 
557  return ret;
558 }
559 
561 {
563 
564  level_result::type game_res = level_result::type::fail;
565  try {
566  campaign_controller ccontroller(state_, true);
567  game_res = ccontroller.play_game();
568  if(game_res == level_result::type::fail) {
569  if(lg::broke_strict()) {
571  } else {
573  }
574  }
575  } catch(const wml_exception& e) {
576  PLAIN_LOG << "Caught WML Exception:" << e.dev_message;
578  }
579 
581 
583  return pass_victory_or_defeat(game_res);
584  }
585 
587  save.save_game_automatic(false, "unit_test_replay");
588 
590  savegame::save_index_class::default_saves_dir(), save.filename(), "", true, true, false};
591 
592  if(!load_game()) {
593  PLAIN_LOG << "Failed to load the replay!";
594  return unit_test_result::TEST_FAIL_LOADING_REPLAY; // failed to load replay
595  }
596 
597  try {
598  const bool was_strict_broken = lg::broke_strict();
599  campaign_controller ccontroller(state_, true);
600  ccontroller.play_replay();
601  if(!was_strict_broken && lg::broke_strict()) {
602  PLAIN_LOG << "Observed failure on replay";
604  }
605  } catch(const wml_exception& e) {
606  PLAIN_LOG << "WML Exception while playing replay: " << e.dev_message;
608  }
609 
610  return pass_victory_or_defeat(game_res);
611 }
612 
614 {
615  if(res == level_result::type::defeat) {
616  if(lg::broke_strict()) {
618  } else {
620  }
621  } else if(res == level_result::type::victory) {
622  if(lg::broke_strict()) {
624  } else {
626  }
627  }
628 
629  if(lg::broke_strict()) {
631  } else {
633  }
634 }
635 
637 {
639  return true;
640  }
641 
643 
645 
647  return false;
648 }
649 
651 {
653  return true;
654  }
655 
656  state_.classification().type = campaign_type::type::multiplayer;
657  DBG_GENERAL << "Current campaign type: " << campaign_type::get_string(state_.classification().type);
658 
659  try {
661  } catch(const config::error& e) {
662  PLAIN_LOG << "Error loading game config: " << e.what();
663  return false;
664  }
665 
666  // A default output filename
667  std::string outfile = "wesnoth_image.png";
668 
669  // If a output path was given as an argument, use that instead
671  outfile = *cmdline_opts_.render_image_dst;
672  }
673 
675  exit(1);
676  }
677 
678  return false;
679 }
680 
682 {
683  return load_data_.has_value();
684 }
685 
687 {
688  assert(game_config_manager::get());
689 
690  DBG_GENERAL << "Current campaign type: " << campaign_type::get_string(state_.classification().type);
691 
693  if(load_data_) {
694  load.data() = std::move(*load_data_);
696  }
697 
698  try {
699  if(!load.load_game()) {
700  return false;
701  }
702 
703  load.set_gamestate();
704  try {
706  } catch(const config::error&) {
707  return false;
708  }
709 
710  } catch(const config::error& e) {
711  if(e.message.empty()) {
712  gui2::show_error_message(_("The file you have tried to load is corrupt"));
713  } else {
714  gui2::show_error_message(_("The file you have tried to load is corrupt: '") + e.message + '\'');
715  }
716 
717  return false;
718  } catch(const wml_exception& e) {
719  e.show();
720  return false;
721  } catch(const filesystem::io_exception& e) {
722  if(e.message.empty()) {
723  gui2::show_error_message(_("File I/O Error while reading the game"));
724  } else {
725  gui2::show_error_message(_("File I/O Error while reading the game: '") + e.message + '\'');
726  }
727 
728  return false;
729  } catch(const game::error& e) {
730  if(e.message.empty()) {
731  gui2::show_error_message(_("The file you have tried to load is corrupt"));
732  } else {
733  gui2::show_error_message(_("The file you have tried to load is corrupt: '") + e.message + '\'');
734  }
735 
736  return false;
737  }
738 
739  play_replay_ = load.data().show_replay;
740  LOG_CONFIG << "is middle game savefile: " << (state_.is_mid_game_save() ? "yes" : "no");
741  LOG_CONFIG << "show replay: " << (play_replay_ ? "yes" : "no");
742  // in case load.data().show_replay && state_.is_start_of_scenario
743  // there won't be any turns to replay, but the
744  // user gets to watch the intro sequence again ...
745 
746  if(!state_.is_start_of_scenario() && load.data().show_replay) {
748  }
749 
752  }
753 
754  if(load.data().cancel_orders) {
756  }
757 
758  return true;
759 }
760 
762 {
763  state_.clear();
764  state_.classification().type = campaign_type::type::scenario;
765  play_replay_ = false;
766 
768 }
769 
771 {
773 }
774 
776 {
777  if(jump_to_campaign_.jump) {
778  if(new_campaign()) {
780  jump_to_campaign_.jump = false;
782  } else {
783  jump_to_campaign_.jump = false;
784  return false;
785  }
786  }
787 
788  return true;
789 }
790 
792 {
794  jump_to_multiplayer_ = false;
796  ;
797  } else {
798  return false;
799  }
800  }
801 
802  return true;
803 }
804 
806 {
807  if(jump_to_editor_) {
808  jump_to_editor_ = false;
809 
810  const std::string to_open = load_data_ ? filesystem::normalize_path(load_data_->filename) : "";
812 
814  return false;
815  }
816  }
817 
818  return true;
819 }
820 
822 {
823  std::string wesnothd_program = preferences::get_mp_server_program_name().empty()
826 
827  std::string config = filesystem::get_user_config_dir() + "/lan_server.cfg";
829  // copy file if it isn't created yet
831  }
832 
833  LOG_GENERAL << "Starting wesnothd";
834  try
835  {
836 #ifndef _WIN32
837  bp::child c(wesnothd_program, "-c", config);
838 #else
839  bp::child c(wesnothd_program, "-c", config, bp::windows::create_no_window);
840 #endif
841  c.detach();
842  // Give server a moment to start up
843  SDL_Delay(50);
844  return;
845  }
846  catch(const bp::process_error& e)
847  {
849 
850  // Couldn't start server so throw error
851  WRN_GENERAL << "Failed to start server " << wesnothd_program << ":\n" << e.what();
852  throw game::mp_server_error("Starting MP server failed!");
853  }
854 }
855 
857 {
858  try {
859  if(mode == mp_mode::HOST) {
860  try {
861  start_wesnothd();
862  } catch(const game::mp_server_error&) {
864 
865  try {
866  start_wesnothd();
867  } catch(const game::mp_server_error&) {
868  return false;
869  }
870  }
871  }
872 
873  // If a server address wasn't specified, prompt for it now.
874  if(mode != mp_mode::LOCAL && multiplayer_server_.empty()) {
875  if(!gui2::dialogs::mp_connect::execute()) {
876  return false;
877  }
878 
879  // The prompt saves its input to preferences.
881 
882  if(multiplayer_server_ != preferences::builtin_servers_list().front().address) {
884  }
885  }
886 
887  // create_engine already calls game_config_manager::get()->load_config but maybe its better to have MULTIPLAYER
888  // defined while we are in the lobby.
890 
891  events::discard_input(); // prevent the "keylogger" effect
893 
894  if(mode == mp_mode::LOCAL) {
896  } else {
898  multiplayer_server_.clear();
899  }
900 
901  } catch(const wesnothd_rejected_client_error& e) {
902  gui2::show_error_message(e.message);
903  } catch(const game::mp_server_error& e) {
904  gui2::show_error_message(_("Error while starting server: ") + e.message);
905  } catch(const game::load_game_failed& e) {
906  gui2::show_error_message(_("The game could not be loaded: ") + e.message);
907  } catch(const game::game_error& e) {
908  gui2::show_error_message(_("Error while playing the game: ") + e.message);
909  } catch(const mapgen_exception& e) {
910  gui2::show_error_message(_("Map generator error: ") + e.message);
911  } catch(const wesnothd_error& e) {
912  if(!e.message.empty()) {
913  ERR_NET << "caught network error: " << e.message;
914 
915  std::string user_msg;
916  auto conn_err = dynamic_cast<const wesnothd_connection_error*>(&e);
917 
918  if(conn_err) {
919  // The wesnothd_connection_error subclass is only thrown with messages
920  // from boost::system::error_code which we can't translate ourselves.
921  // It's also the originator of the infamous EOF error that happens when
922  // the server dies. <https://github.com/wesnoth/wesnoth/issues/3005>. It
923  // will provide a translated string instead of that when it happens.
924  user_msg = !conn_err->user_message.empty()
925  ? conn_err->user_message
926  : _("Connection failed: ") + e.message;
927  } else {
928  // This will be a message from the server itself, which we can
929  // probably translate.
930  user_msg = translation::gettext(e.message.c_str());
931  }
932 
933  gui2::show_error_message(user_msg);
934  } else {
935  ERR_NET << "caught network error";
936  }
937  } catch(const config::error& e) {
938  if(!e.message.empty()) {
939  ERR_CONFIG << "caught config::error: " << e.message;
940  gui2::show_transient_message("", e.message);
941  } else {
942  ERR_CONFIG << "caught config::error";
943  }
944  } catch(const incorrect_map_format_error& e) {
945  gui2::show_error_message(_("The game map could not be loaded: ") + e.message);
946  } catch(savegame::load_game_exception& e) {
947  load_data_ = std::move(e.data_);
948  // this will make it so next time through the title screen loop, this game is loaded
949  } catch(const wml_exception& e) {
950  e.show();
951  } catch(const game::error& e) {
952  PLAIN_LOG << "caught game::error...";
953  gui2::show_error_message(_("Error: ") + e.message);
954  }
955 
956  return true;
957 }
958 
960 {
962  return true;
963  }
964 
965  DBG_MP << "starting multiplayer game from the commandline";
966 
968 
969  events::discard_input(); // prevent the "keylogger" effect
971 
972  try {
974  } catch(savegame::load_game_exception& e) {
975  load_data_ = std::move(e.data_);
976  return true;
977  }
978 
979  return false;
980 }
981 
983 {
984  if(!gui2::dialogs::language_selection::execute()) {
985  return false;
986  }
987 
990  }
991 
996 
997  return true;
998 }
999 
1001 {
1002  assert(!load_data_);
1003  if(play_replay_) {
1004  play_replay();
1005  return;
1006  }
1007 
1008  gui2::dialogs::loading_screen::display([this, reload]() {
1010 
1011  if(reload == reload_mode::RELOAD_DATA) {
1012  try {
1015  } catch(const config::error&) {
1016  return;
1017  }
1018  }
1019  });
1020 
1021  try {
1022  campaign_controller ccontroller(state_);
1023  ccontroller.play_game();
1024  ai::manager::singleton_ = nullptr;
1025  } catch(savegame::load_game_exception& e) {
1026  load_data_ = std::move(e.data_);
1027  // this will make it so next time through the title screen loop, this game is loaded
1028  } catch(const wml_exception& e) {
1029  e.show();
1030  } catch(const mapgen_exception& e) {
1031  gui2::show_error_message(_("Map generator error: ") + e.message);
1032  }
1033 }
1034 
1036 {
1037  assert(!load_data_);
1038  try {
1039  campaign_controller ccontroller(state_);
1040  ccontroller.play_replay();
1041  } catch(savegame::load_game_exception& e) {
1042  load_data_ = std::move(e.data_);
1043  // this will make it so next time through the title screen loop, this game is loaded
1044  } catch(const wml_exception& e) {
1045  e.show();
1046  }
1047 }
1048 
1050 {
1052  while(true) {
1054 
1056 
1057  res = editor::start(res != editor::EXIT_RELOAD_DATA, filename);
1058 
1059  if(res != editor::EXIT_RELOAD_DATA) {
1060  return res;
1061  }
1062 
1064  }
1065 
1066  return editor::EXIT_ERROR; // not supposed to happen
1067 }
1068 
1070 {
1071  load_data_.reset();
1072 }
1073 
1075 {
1076  try {
1078  video::deinit();
1079  } catch(std::exception& e) {
1080  ERR_GENERAL << "Suppressing exception thrown during ~game_launcher: " << e.what();
1081  } catch(...) {
1082  ERR_GENERAL << "Suppressing exception " << utils::get_unknown_exception_type() << " thrown during ~game_launcher";
1083  }
1084 }
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands.
static manager * singleton_
Definition: manager.hpp:439
level_result::type play_game()
level_result::type play_replay()
std::optional< std::string > render_image_dst
Output file to put rendered image path in.
bool nogui
True if –nogui was given on the command line.
std::optional< std::string > server
Non-empty if –server was given on the command line.
std::optional< std::string > plugin_file
File to load a lua plugin (similar to a script) from.
bool headless_unit_test
True if –unit is used and –showgui is not present.
bool windowed
True if –windowed was given on the command line.
bool noreplaycheck
True if –noreplaycheck was given on the command line.
std::optional< std::string > render_image
Image path to render.
std::optional< int > campaign_difficulty
Non-empty if –campaign-difficulty was given on the command line.
std::optional< unsigned int > translation_percent
Non-empty if –all-translations or –translations-over is given on the command line.
std::optional< std::string > campaign_scenario
Non-empty if –campaign-scenario was given on the command line.
std::optional< std::string > password
Non-empty if –password was given on the command line.
bool debug
True if –debug was given on the command line.
bool nosound
True if –nosound was given on the command line.
bool multiplayer
True if –multiplayer was given on the command line.
bool script_unsafe_mode
Whether to load the "package" package for the scripting environment.
bool nobanner
True if –nobanner was given on the command line.
bool nomusic
True if –nomusic was given on the command line.
bool clock
True if –clock was given on the command line.
bool nodelay
True if –nodelay was given on the command line.
std::optional< std::string > username
Non-empty if –username was given on the command line.
bool new_widgets
True if –new-widgets was given on the command line.
std::optional< std::string > load
Non-empty if –load was given on the command line.
std::optional< std::string > campaign
Non-empty if –campaign was given on the command line.
bool fps
True if –fps was given on the command line.
std::optional< std::string > script_file
File to load lua script from.
bool campaign_skip_story
True if –skip-story was given on the command line.
bool with_replay
True if –with-replay was given on the command line.
std::optional< std::string > core_id
Non-empty if –core was given on the command line.
bool screenshot
True if –screenshot was given on the command line.
std::vector< std::string > unit_test
Non-empty if –unit was given on the command line.
bool fullscreen
True if –fullscreen was given on the command line.
std::optional< std::string > test
Non-empty if –test was given on the command line.
std::optional< std::string > language
Non-empty if –language was given on the command line.
std::optional< int > max_fps
Max FPS specified by –max-fps option.
std::optional< std::pair< int, int > > resolution
Pair of AxB values specified after –resolution.
std::optional< std::string > screenshot_map_file
Map file to make a screenshot of.
std::optional< std::string > screenshot_output_file
Output file to put screenshot in.
std::optional< std::string > editor
Non-empty if –editor was given on the command line.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
campaign_type::type type
std::string label
Name of the game (e.g.
std::string campaign_define
If there is a define the campaign uses to customize data.
static game_config_manager * get()
void load_game_config_for_game(const game_classification &classification, const std::string &scenario_id)
void load_game_config_for_create(bool is_mp, bool is_test=false)
game_launcher(const commandline_options &cmdline_opts)
unit_test_result single_unit_test()
Internal to the implementation of unit_test().
std::string multiplayer_server_
std::string jump_to_campaign_id() const
Return the ID of the campaign to jump to (skipping the main menu).
std::optional< savegame::load_game_metadata > load_data_
bool play_screenshot_mode()
jump_to_campaign_info jump_to_campaign_
std::string screenshot_filename_
void launch_game(reload_mode reload=reload_mode::RELOAD_DATA)
bool goto_multiplayer()
bool change_language()
saved_game state_
unit_test_result pass_victory_or_defeat(level_result::type res)
bool has_load_data() const
bool play_render_image_mode()
void clear_loaded_game()
bool init_lua_script()
std::vector< std::string > test_scenarios_
unit_test_result
Status code after running a unit test, should match the run_wml_tests script and the documentation fo...
std::string screenshot_map_
const commandline_options & cmdline_opts_
font::manager font_manager_
void set_test(const std::string &id)
bool play_multiplayer_commandline()
unit_test_result unit_test()
Runs unit tests specified on the command line.
bool play_multiplayer(mp_mode mode)
editor::EXIT_STATUS start_editor()
static void progress(loading_stage stage=loading_stage::none)
Report what is being loaded to the loading screen.
static void display(std::function< void()> f)
Generic locator abstracting the location of an image.
Definition: picture.hpp:63
void run(char const *prog, const std::string &name, int nArgs=0)
Runs a plain script.
void load_package()
Loads the package library into lua environment.
void play_slice()
Definition: context.cpp:96
std::size_t size()
Definition: manager.cpp:68
std::string get_name(std::size_t idx)
Definition: manager.cpp:94
static plugins_manager * get()
Definition: manager.cpp:58
void start_plugin(std::size_t idx)
Definition: manager.cpp:101
std::string get_detailed_status(std::size_t idx)
Definition: manager.cpp:83
lua_kernel_base * get_kernel_base()
Definition: manager.cpp:63
std::size_t add_plugin(const std::string &name, const std::string &prog)
Definition: manager.cpp:118
game_classification & classification()
Definition: saved_game.hpp:56
void set_skip_story(bool skip_story)
Definition: saved_game.hpp:147
bool is_mid_game_save() const
Definition: saved_game.hpp:106
bool is_start_of_scenario() const
Definition: saved_game.hpp:110
void unify_controllers()
Definition: saved_game.cpp:732
void set_carryover_sides_start(config carryover_sides_start)
Definition: saved_game.cpp:163
std::string get_scenario_id() const
Definition: saved_game.cpp:678
void clear()
Definition: saved_game.cpp:813
statistics_record::campaign_stats_t & statistics()
Definition: saved_game.hpp:143
void cancel_orders()
Definition: saved_game.cpp:715
Exception used to signal that the user has decided to abortt a game, and to load another game instead...
Definition: savegame.hpp:85
The class for loading a savefile.
Definition: savegame.hpp:101
Class for replay saves (either manually or automatically).
Definition: savegame.hpp:268
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:208
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:360
const std::string & filename() const
Definition: savegame.hpp:178
static void reset_translations()
Definition: tstring.cpp:651
Declarations for File-IO.
std::size_t i
Definition: function.cpp:968
#define LOG_CONFIG
#define ERR_GENERAL
#define DBG_GENERAL
static lg::log_domain log_enginerefac("enginerefac")
#define DBG_MP
static lg::log_domain log_network("network")
static lg::log_domain log_mp_create("mp/create")
#define ERR_CONFIG
#define WRN_GENERAL
#define ERR_NET
#define LOG_GENERAL
static lg::log_domain log_config("config")
static std::string _(const char *str)
Definition: gettext.hpp:93
const language_def & get_locale()
Definition: language.cpp:328
void init_textdomains(const game_config_view &cfg)
Initializes the list of textdomains from a configuration object.
Definition: language.cpp:366
bool load_language_list()
Definition: language.cpp:103
language_list get_languages(bool all)
Return a list of available translations.
Definition: language.cpp:126
void set_min_translation_percent(int percent)
Definition: language.cpp:143
Standard logging facilities (interface).
#define STREAMING_LOG
Definition: log.hpp:296
#define PLAIN_LOG
Definition: log.hpp:295
@ NORMAL
Definition: cursor.hpp:28
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:176
void point(int x, int y)
Draw a single point.
Definition: draw.cpp:202
@ EXIT_ERROR
Definition: editor_main.hpp:27
@ EXIT_NORMAL
Definition: editor_main.hpp:24
@ EXIT_QUIT_TO_DESKTOP
Definition: editor_main.hpp:25
@ EXIT_RELOAD_DATA
Definition: editor_main.hpp:26
EXIT_STATUS start(bool clear_id, const std::string &filename, bool take_screenshot, const std::string &screenshot_filename)
Main interface for launching the editor from the title screen.
void discard_input()
Discards all input events.
Definition: events.cpp:799
std::string get_cache_dir()
Definition: filesystem.cpp:880
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
std::string get_user_config_dir()
Definition: filesystem.cpp:841
std::string get_user_data_dir()
Definition: filesystem.cpp:870
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:318
std::string get_exe_dir()
Definition: filesystem.cpp:990
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't pres...
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...
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:50
void write_file(const std::string &fname, const std::string &data, std::ios_base::openmode mode)
Throws io_exception if an error occurs.
std::string get_cwd()
Definition: filesystem.cpp:962
std::string normalize_path(const std::string &fpath, bool normalize_separators, bool resolve_dot_entries)
Returns the absolute path of a file.
bool load_font_config()
Definition: font_config.cpp:79
Game configuration data as global variables.
Definition: build_info.cpp:60
std::string path
Definition: filesystem.cpp:83
std::string get_default_title_string()
std::vector< server_info > server_list
Definition: game_config.cpp:72
void set_debug(bool new_debug)
Definition: game_config.cpp:93
bool show_debug_clock_button
Do we wish to show the button for the debug clock.
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:36
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)
Shows a transient message to the user.
bool new_widgets
Do we wish to use the new library or not.
Definition: settings.cpp:23
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:203
void flush_cache()
Purges all image caches.
Definition: picture.cpp:216
surface get_surface(const image::locator &i_locator, TYPE type, bool skip_cache)
Returns an image surface suitable for software manipulation.
Definition: picture.cpp:672
save_result save_image(const locator &i_locator, const std::string &filename)
Definition: picture.cpp:923
@ UNSCALED
Unmodified original-size image.
Definition: picture.hpp:226
bool broke_strict()
Definition: log.cpp:395
void start_local_game()
Starts a multiplayer game in single-user mode.
void start_local_game_commandline(const commandline_options &cmdline_opts)
Starts a multiplayer game in single-user mode using command line settings.
void start_client(const std::string &host)
Pubic entry points for the MP workflow.
void disable_preferences_save()
Definition: general.cpp:231
std::string get_mp_server_program_name()
Definition: game.cpp:507
void set_core_id(const std::string &core_id)
Definition: general.cpp:338
void set_mp_server_program_name(const std::string &path)
Definition: game.cpp:498
bool set_turn_bell(bool ison)
Definition: general.cpp:680
bool set_sound(bool ison)
Definition: general.cpp:733
bool music_on()
Definition: general.cpp:751
void set_draw_delay(int value)
Definition: general.cpp:902
void set_show_fps(bool value)
Definition: general.cpp:892
bool sound_on()
Definition: general.cpp:728
std::string network_host()
Definition: game.cpp:384
bool set_music(bool ison)
Definition: general.cpp:756
void set_network_host(const std::string &host)
Definition: game.cpp:394
bool UI_sound_on()
Definition: general.cpp:699
void _set_maximized(bool ison)
Definition: general.cpp:450
const std::vector< game_config::server_info > & builtin_servers_list()
Definition: game.cpp:353
bool set_UI_sound(bool ison)
Definition: general.cpp:704
void set_level(const std::string &value)
Definition: game.cpp:693
void set_login(const std::string &login)
bool turn_bell()
Definition: general.cpp:675
void set_password(const std::string &server, const std::string &login, const std::string &key)
void _set_resolution(const point &res)
Definition: general.cpp:444
void show_wesnothd_server_search()
Definition: display.cpp:95
void clean_saves(const std::string &label)
Delete all autosaves of a certain scenario from the default save directory.
Definition: savegame.cpp:62
bool init_sound()
Definition: sound.cpp:441
void close_sound()
Definition: sound.cpp:493
void flush_cache()
Definition: sound.cpp:193
bool select_campaign(saved_game &state, jump_to_campaign_info jump_to_campaign)
void process(int mousex, int mousey)
Definition: tooltips.cpp:278
void set_language(const std::string &language, const std::vector< std::string > *)
Definition: gettext.cpp:494
static std::string gettext(const char *str)
Definition: gettext.hpp:60
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
void set_window_title(const std::string &title)
Sets the title of the main window.
Definition: video.cpp:634
void set_window_icon(surface &icon)
Sets the icon of the main window.
Definition: video.cpp:640
void init(fake type)
Initialize the video subsystem.
Definition: video.cpp:82
void deinit()
Deinitialize the video subsystem.
Definition: video.cpp:111
This file contains the settings handling of the widget library.
An exception object used when an IO error occurs.
Definition: filesystem.hpp:64
Base class for all the errors encountered by the engine.
Definition: exceptions.hpp:29
Error used for any general game error, e.g.
Definition: game_errors.hpp:47
Error used when game loading fails.
Definition: game_errors.hpp:31
std::string campaign_id
The ID of the campaign to launch.
bool jump
Whether the game should immediately start a campaign.
std::string scenario_id
The ID of the scenario within the campaign to jump to.
bool skip_story
Whether the story screen should be skipped.
int difficulty
The difficulty at which to launch the campaign.
std::string localename
Definition: language.hpp:53
void clear_current_scenario()
Delete the current scenario from the stats.
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
An error occurred inside the underlying network communication code (boost asio) TODO: find a short na...
std::string user_message
User-friendly and potentially translated message for use in the UI.
An error occurred during when trying to communicate with the wesnothd server.
Error used when the client is rejected by the MP server.
Helper class, don't construct this directly.
mock_char c
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define e