The Battle for Wesnoth  1.19.8+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_data_dir, etc
25 #include "game_classification.hpp" // for game_classification, etc
26 #include "game_config.hpp" // for path, 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 "save_index.hpp"
46 #include "sdl/surface.hpp" // for surface
47 #include "serialization/compression.hpp" // for format::NONE
48 #include "tstring.hpp" // for operator==, operator!=
49 #include "video.hpp"
51 #include "wml_exception.hpp" // for wml_exception
52 
53 #include <algorithm> // for copy, max, min, stable_sort
54 #ifdef _WIN32
55 #include <boost/process/windows.hpp>
56 #endif
57 #include <boost/process.hpp>
58 #include <cstdlib> // for system
59 #include <new>
60 #include <thread>
61 #include <utility> // for pair
62 
63 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
64 #include "gui/widgets/debug.hpp"
65 #endif
66 
67 static lg::log_domain log_config("config");
68 #define ERR_CONFIG LOG_STREAM(err, log_config)
69 #define WRN_CONFIG LOG_STREAM(warn, log_config)
70 #define LOG_CONFIG LOG_STREAM(info, log_config)
71 
72 #define ERR_GENERAL LOG_STREAM(err, lg::general())
73 #define LOG_GENERAL LOG_STREAM(info, lg::general())
74 #define WRN_GENERAL LOG_STREAM(warn, lg::general())
75 #define DBG_GENERAL LOG_STREAM(debug, lg::general())
76 
77 #define LOG_TEST FORCE_LOG_TO(lg::general(), log_config)
78 
79 static lg::log_domain log_mp_create("mp/create");
80 #define DBG_MP LOG_STREAM(debug, log_mp_create)
81 
82 static lg::log_domain log_network("network");
83 #define ERR_NET LOG_STREAM(err, log_network)
84 
85 static lg::log_domain log_enginerefac("enginerefac");
86 #define LOG_RG LOG_STREAM(info, log_enginerefac)
87 
88 namespace bp = boost::process;
89 
91  : cmdline_opts_(cmdline_opts)
92  , font_manager_()
93  , image_manager_()
94  , main_event_context_()
95  , hotkey_manager_()
96  , music_thinker_()
97  , music_muter_()
98  , test_scenarios_{"test"}
99  , screenshot_map_()
100  , screenshot_filename_()
101  , state_()
102  , play_replay_(false)
103  , multiplayer_server_()
104  , jump_to_multiplayer_(false)
105  , jump_to_campaign_{}
106  , jump_to_editor_(false)
107  , load_data_()
108 {
109  bool no_music = false;
110  bool no_sound = false;
111 
112  if(cmdline_opts_.core_id) {
113  prefs::get().set_core(*cmdline_opts_.core_id);
114  }
115  if(cmdline_opts_.campaign) {
116  jump_to_campaign_.jump = true;
118  PLAIN_LOG << "selected campaign id: [" << jump_to_campaign_.campaign_id << "]";
119 
122  PLAIN_LOG << "selected difficulty: [" << jump_to_campaign_.difficulty << "]";
123  } else {
124  jump_to_campaign_.difficulty = -1; // let the user choose the difficulty
125  }
126 
129  PLAIN_LOG << "selected scenario id: [" << jump_to_campaign_.scenario_id << "]";
130  }
131 
134  }
135  }
136  if(cmdline_opts_.clock)
138  if(cmdline_opts_.debug) {
140  game_config::mp_debug = true;
141  }
142 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
143  if(cmdline_opts_.debug_dot_domain)
144  gui2::debug_layout_graph::set_domain(*cmdline_opts_.debug_dot_domain);
145  if(cmdline_opts_.debug_dot_level)
146  gui2::debug_layout_graph::set_level(*cmdline_opts_.debug_dot_level);
147 #endif
148  if(cmdline_opts_.editor) {
149  jump_to_editor_ = true;
150  if(!cmdline_opts_.editor->empty()) {
153  }
154  }
155  if(cmdline_opts_.fps)
156  prefs::get().set_show_fps(true);
158  start_in_fullscreen_ = true;
159  if(cmdline_opts_.load)
162  if(cmdline_opts_.max_fps) {
163  prefs::get().set_refresh_rate(std::clamp(*cmdline_opts_.max_fps, 1, 1000));
164  }
166  no_sound = true;
168  }
170  gui2::new_widgets = true;
172  no_music = true;
174  no_sound = true;
176  const int xres = std::get<0>(*cmdline_opts_.resolution);
177  const int yres = std::get<1>(*cmdline_opts_.resolution);
178  if(xres > 0 && yres > 0) {
179  prefs::get().set_resolution(point(xres, yres));
180  prefs::get().set_maximized(false);
181  }
182  }
184  // TODO it could be simplified to use cmdline_opts_ directly if there is no other way to enter screenshot mode
187  no_sound = true;
189  }
190  if (cmdline_opts_.server){
191  jump_to_multiplayer_ = true;
192  // Do we have any server specified ?
193  if(!cmdline_opts_.server->empty()) {
195  } else {
196  // Pick the first server in config
197  if(game_config::server_list.size() > 0) {
199  } else {
200  multiplayer_server_ = "";
201  }
202  }
203  if(cmdline_opts_.username) {
206  if(cmdline_opts_.password) {
208  prefs::get().set_password(*cmdline_opts.server, *cmdline_opts.username, *cmdline_opts_.password);
209  }
210  }
211  }
212  if(cmdline_opts_.test) {
213  if(!cmdline_opts_.test->empty()) {
215  }
216  }
217  if(!cmdline_opts_.unit_test.empty()) {
219  }
221  start_in_fullscreen_ = false;
223  load_data_->show_replay = true;
226 
227  if(!cmdline_opts.nobanner) {
228  PLAIN_LOG
229  << "\nData directory: " << game_config::path
230  << "\nUser data directory: " << filesystem::get_user_data_dir()
231  << "\nCache directory: " << filesystem::get_cache_dir()
232  << "\n\n";
233  }
234 
235  // disable sound in nosound mode, or when sound engine failed to initialize
236  if(no_sound || ((prefs::get().sound() || prefs::get().music_on() ||
238  !sound::init_sound())) {
239  prefs::get().set_sound(false);
240  prefs::get().set_music(false);
241  prefs::get().set_turn_bell(false);
242  prefs::get().set_ui_sound(false);
243  } else if(no_music) { // else disable the music in nomusic mode
244  prefs::get().set_music(false);
245  }
246 }
247 
249 {
250  if(!::load_language_list()) {
251  return false;
252  }
253 
254  language_def locale;
255  if(cmdline_opts_.language) {
256  std::vector<language_def> langs = get_languages(true);
257  for(const language_def& def : langs) {
258  if(def.localename == *cmdline_opts_.language) {
259  locale = def;
260  break;
261  }
262  }
263  if(locale.localename.empty()) {
264  PLAIN_LOG << "Language symbol '" << *cmdline_opts_.language << "' not found.";
265  return false;
266  }
267  } else {
268  locale = get_locale();
269  }
270  ::set_language(locale);
271 
272  return true;
273 }
274 
276 {
277  // Handle special commandline launch flags
282  {
288  {
289  PLAIN_LOG << "--nogui flag is only valid with --multiplayer or --screenshot or --plugin flags";
290  return false;
291  }
293  // Screenshots require a rendering context, and thus a window,
294  // so we create one but hidden.
296  } else {
297  // Other functions don't require a window at all.
299  }
300  return true;
301  }
302 
303  // Initialize video subsystem, and create a new window.
304  video::init();
305 
306  // Set window title and icon
308 
309 #if !(defined(__APPLE__))
310  surface icon(image::get_surface(image::locator{"icons/icon-game.png"}, image::UNSCALED));
311  if(icon != nullptr) {
313  }
314 #endif
315  return true;
316 }
317 
319 {
320  bool error = false;
321 
322  if(!cmdline_opts_.nobanner) {
323  STREAMING_LOG << "Checking lua scripts... ";
324  }
325 
327  // load the "package" package, so that scripts can get what packages they want
329  }
330 
332  std::string filename = *cmdline_opts_.plugin_file;
333 
334  PLAIN_LOG << "Loading a plugin file'" << filename << "'...";
335 
337 
338  try {
339  if(sf->fail()) {
340  throw std::runtime_error("failed to open plugin file");
341  }
342 
343  /* Cancel all "jumps" to editor / campaign / multiplayer */
344  jump_to_multiplayer_ = false;
345  jump_to_editor_ = false;
346  jump_to_campaign_.jump = false;
347 
348  std::string full_plugin((std::istreambuf_iterator<char>(*sf)), std::istreambuf_iterator<char>());
349 
351 
352  std::size_t i = pm.add_plugin(filename, full_plugin);
353 
354  for(std::size_t j = 0; j < pm.size(); ++j) {
355  PLAIN_LOG << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j);
356  }
357 
358  PLAIN_LOG << "Starting a plugin...";
359  pm.start_plugin(i);
360 
361  for(std::size_t j = 0; j < pm.size(); ++j) {
362  PLAIN_LOG << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j);
363  }
364 
365  plugins_context pc("init");
366 
367  for(std::size_t repeat = 0; repeat < 5; ++repeat) {
368  PLAIN_LOG << "Playing a slice...";
369  pc.play_slice();
370 
371  for(std::size_t j = 0; j < pm.size(); ++j) {
372  PLAIN_LOG << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j);
373  }
374  }
375 
376  return true;
377  } catch(const std::exception& e) {
378  gui2::show_error_message(std::string("When loading a plugin, error:\n") + e.what());
379  error = true;
380  }
381  }
382 
383  if(!error && !cmdline_opts_.nobanner) {
384  PLAIN_LOG << "ok";
385  }
386 
387  return !error;
388 }
389 
390 void game_launcher::set_test(const std::string& id)
391 {
392  state_.clear();
393  state_.classification().type = campaign_type::type::test;
395  state_.classification().era_id = "era_default";
396 
397  state_.set_carryover_sides_start(config{"next_scenario", id});
398 }
399 
401 {
402  // This first_time variable was added in 70f3c80a3e2 so that using the GUI
403  // menu to load a game works. That seems to have edge-cases, for example if
404  // you try to load a game a second time then Wesnoth exits.
405  static bool first_time = true;
406 
407  if(!cmdline_opts_.test) {
408  return true;
409  }
410 
411  if(!first_time) {
412  return false;
413  }
414 
415  first_time = false;
416 
417  if(test_scenarios_.size() == 0) {
418  // shouldn't happen, as test_scenarios_ is initialised to {"test"}
419  PLAIN_LOG << "Error in the test handling code";
420  return false;
421  }
422 
423  if(test_scenarios_.size() > 1) {
424  PLAIN_LOG << "You can't run more than one unit test in interactive mode";
425  }
426 
427  set_test(test_scenarios_.at(0));
428 
430 
431  try {
432  campaign_controller ccontroller(state_);
433  ccontroller.play_game();
434  } catch(savegame::load_game_exception& e) {
435  load_data_ = std::move(e.data_);
436  return true;
437  }
438 
439  return false;
440 }
441 
442 /**
443  * Runs unit tests specified on the command line.
444  *
445  * If multiple unit tests were specified, then this will stop at the first test
446  * which returns a non-zero status.
447  */
448 // Same as play_test except that we return the results of play_game.
449 // \todo "same ... except" ... and many other changes, such as testing the replay
451 {
452  // There's no copy of play_test's first_time variable. That seems to be for handling
453  // the player loading a game via the GUI, which makes no sense in a non-interactive test.
454  if(cmdline_opts_.unit_test.empty()) {
456  }
457 
458  auto ret = unit_test_result::TEST_FAIL; // will only be returned if no test is run
459  for(const auto& scenario : test_scenarios_) {
460  set_test(scenario);
461  ret = single_unit_test();
462  const char* describe_result;
463  switch(ret) {
465  describe_result = "PASS TEST";
466  break;
468  describe_result = "FAIL TEST (INVALID REPLAY)";
469  break;
471  describe_result = "FAIL TEST (ERRORED REPLAY)";
472  break;
474  describe_result = "FAIL TEST (WML EXCEPTION)";
475  break;
477  describe_result = "FAIL TEST (DEFEAT)";
478  break;
480  describe_result = "PASS TEST (VICTORY)";
481  break;
483  describe_result = "BROKE STRICT (PASS)";
484  break;
486  describe_result = "BROKE STRICT (FAIL)";
487  break;
489  describe_result = "BROKE STRICT (DEFEAT)";
490  break;
492  describe_result = "BROKE STRICT (VICTORY)";
493  break;
494  default:
495  describe_result = "FAIL TEST (UNKNOWN)";
496  break;
497  }
498 
499  PLAIN_LOG << describe_result << " (" << int(ret) << "): " << scenario;
500  if(ret != unit_test_result::TEST_PASS) {
501  break;
502  }
503  }
504 
505  return ret;
506 }
507 
509 {
511 
512  level_result::type game_res = level_result::type::fail;
513  try {
514  campaign_controller ccontroller(state_, true);
515  game_res = ccontroller.play_game();
516  if(game_res == level_result::type::fail) {
517  if(lg::broke_strict()) {
519  } else {
521  }
522  }
523  } catch(const wml_exception& e) {
524  PLAIN_LOG << "Caught WML Exception:" << e.dev_message;
526  }
527 
529 
531  return pass_victory_or_defeat(game_res);
532  }
533 
535  save.save_game_automatic(false, "unit_test_replay");
536 
538  savegame::save_index_class::default_saves_dir(), save.filename(), "", true, true, false};
539 
540  if(!load_game()) {
541  PLAIN_LOG << "Failed to load the replay!";
542  return unit_test_result::TEST_FAIL_LOADING_REPLAY; // failed to load replay
543  }
544 
545  try {
546  const bool was_strict_broken = lg::broke_strict();
547  campaign_controller ccontroller(state_, true);
548  ccontroller.play_replay();
549  if(!was_strict_broken && lg::broke_strict()) {
550  PLAIN_LOG << "Observed failure on replay";
552  }
553  } catch(const wml_exception& e) {
554  PLAIN_LOG << "WML Exception while playing replay: " << e.dev_message;
556  }
557 
558  return pass_victory_or_defeat(game_res);
559 }
560 
562 {
563  if(res == level_result::type::defeat) {
564  if(lg::broke_strict()) {
566  } else {
568  }
569  } else if(res == level_result::type::victory) {
570  if(lg::broke_strict()) {
572  } else {
574  }
575  }
576 
577  if(lg::broke_strict()) {
579  } else {
581  }
582 }
583 
585 {
587  return true;
588  }
589 
591 
593 
595  return false;
596 }
597 
599 {
601  return true;
602  }
603 
604  state_.classification().type = campaign_type::type::multiplayer;
605  DBG_GENERAL << "Current campaign type: " << campaign_type::get_string(state_.classification().type);
606 
607  try {
609  } catch(const config::error& e) {
610  PLAIN_LOG << "Error loading game config: " << e.what();
611  return false;
612  }
613 
614  // A default output filename
615  std::string outfile = "wesnoth_image.png";
616 
617  // If a output path was given as an argument, use that instead
619  outfile = *cmdline_opts_.render_image_dst;
620  }
621 
623  exit(1);
624  }
625 
626  return false;
627 }
628 
630 {
631  return load_data_.has_value();
632 }
633 
635 {
636  assert(game_config_manager::get());
637 
638  DBG_GENERAL << "Current campaign type: " << campaign_type::get_string(state_.classification().type);
639 
641  if(load_data_) {
642  load.data() = std::move(*load_data_);
644  }
645 
646  try {
647  if(!load.load_game()) {
648  return false;
649  }
650 
651  load.set_gamestate();
652  try {
654  } catch(const config::error&) {
655  return false;
656  }
657 
658  } catch(const config::error& e) {
659  if(e.message.empty()) {
660  gui2::show_error_message(_("The file you have tried to load is corrupt"));
661  } else {
662  gui2::show_error_message(_("The file you have tried to load is corrupt: '") + e.message + '\'');
663  }
664 
665  return false;
666  } catch(const wml_exception& e) {
667  e.show();
668  return false;
669  } catch(const filesystem::io_exception& e) {
670  if(e.message.empty()) {
671  gui2::show_error_message(_("File I/O Error while reading the game"));
672  } else {
673  gui2::show_error_message(_("File I/O Error while reading the game: '") + e.message + '\'');
674  }
675 
676  return false;
677  } catch(const game::error& e) {
678  if(e.message.empty()) {
679  gui2::show_error_message(_("The file you have tried to load is corrupt"));
680  } else {
681  gui2::show_error_message(_("The file you have tried to load is corrupt: '") + e.message + '\'');
682  }
683 
684  return false;
685  }
686 
687  play_replay_ = load.data().show_replay;
688  LOG_CONFIG << "is middle game savefile: " << (state_.is_mid_game_save() ? "yes" : "no");
689  LOG_CONFIG << "show replay: " << (play_replay_ ? "yes" : "no");
690  // in case load.data().show_replay && state_.is_start_of_scenario
691  // there won't be any turns to replay, but the
692  // user gets to watch the intro sequence again ...
693 
694  if(!state_.is_start_of_scenario() && load.data().show_replay) {
696  }
697 
700  }
701 
702  if(load.data().cancel_orders) {
704  }
705 
706  return true;
707 }
708 
710 {
711  state_.clear();
712  state_.classification().type = campaign_type::type::scenario;
713  play_replay_ = false;
714 
716 }
717 
719 {
721 }
722 
724  jump_to_campaign_.jump = false;
725  if(new_campaign()) {
728  return true;
729  }
730  return false;
731 }
732 
734 {
735  if(jump_to_campaign_.jump) {
736  return play_campaign();
737  }
738 
739  return true;
740 }
741 
743 {
744  if(!jump_to_multiplayer_) {
745  return true;
746  }
747 
748  jump_to_multiplayer_ = false;
750 }
751 
753 {
754  if(jump_to_editor_) {
755  jump_to_editor_ = false;
756 
757  const std::string to_open = load_data_ ? filesystem::normalize_path(load_data_->filename) : "";
759 
761  return false;
762  }
763  }
764 
765  return true;
766 }
767 
769 {
770  std::string wesnothd_program = "";
771  if(!prefs::get().get_mp_server_program_name().empty()) {
772  wesnothd_program = prefs::get().get_mp_server_program_name();
773  } else {
774  wesnothd_program = filesystem::get_wesnothd_name();
775  }
776 
777  std::string config = filesystem::get_user_data_dir() + "/lan_server.cfg";
779  // copy file if it isn't created yet
781  }
782 
783  LOG_GENERAL << "Starting wesnothd";
784  try
785  {
786 #ifndef _WIN32
787  bp::child c(wesnothd_program, "-c", config);
788 #else
789  bp::child c(wesnothd_program, "-c", config, bp::windows::create_no_window);
790 #endif
791  c.detach();
792  // Give server a moment to start up
793  using namespace std::chrono_literals;
794  std::this_thread::sleep_for(50ms);
795  return;
796  }
797  catch(const bp::process_error& e)
798  {
800 
801  // Couldn't start server so throw error
802  WRN_GENERAL << "Failed to start server " << wesnothd_program << ":\n" << e.what();
803  throw game::mp_server_error("Starting MP server failed!");
804  }
805 }
806 
808 {
810  try {
811  if(mode == mp_mode::HOST) {
812  try {
813  start_wesnothd();
814  } catch(const game::mp_server_error&) {
816 
817  try {
818  start_wesnothd();
819  } catch(const game::mp_server_error&) {
820  return false;
821  }
822  }
823  }
824 
825  // If a server address wasn't specified, prompt for it now.
826  if(mode != mp_mode::LOCAL && multiplayer_server_.empty()) {
827  if(!gui2::dialogs::mp_connect::execute()) {
828  return false;
829  }
830 
831  // The prompt saves its input to preferences.
833 
834  if(multiplayer_server_ != prefs::get().builtin_servers_list().front().address) {
836  }
837  }
838 
839  // create_engine already calls game_config_manager::get()->load_config but maybe its better to have MULTIPLAYER
840  // defined while we are in the lobby.
842 
843  events::discard_input(); // prevent the "keylogger" effect
845 
846  if(mode == mp_mode::LOCAL) {
848  } else {
850  multiplayer_server_.clear();
851  }
852 
853  } catch(const wesnothd_rejected_client_error& e) {
854  gui2::show_error_message(e.message);
855  } catch(const game::mp_server_error& e) {
856  gui2::show_error_message(_("Error while starting server: ") + e.message);
857  } catch(const game::load_game_failed& e) {
858  gui2::show_error_message(_("The game could not be loaded: ") + e.message);
859  } catch(const game::game_error& e) {
860  gui2::show_error_message(_("Error while playing the game: ") + e.message);
861  } catch(const mapgen_exception& e) {
862  gui2::show_error_message(_("Map generator error: ") + e.message);
863  } catch(const wesnothd_error& e) {
864  if(!e.message.empty()) {
865  ERR_NET << "caught network error: " << e.message;
866 
867  std::string user_msg;
868  auto conn_err = dynamic_cast<const wesnothd_connection_error*>(&e);
869 
870  if(conn_err) {
871  // The wesnothd_connection_error subclass is only thrown with messages
872  // from boost::system::error_code which we can't translate ourselves.
873  // It's also the originator of the infamous EOF error that happens when
874  // the server dies. <https://github.com/wesnoth/wesnoth/issues/3005>. It
875  // will provide a translated string instead of that when it happens.
876  user_msg = !conn_err->user_message.empty()
877  ? conn_err->user_message
878  : _("Connection failed: ") + e.message;
879  } else {
880  // This will be a message from the server itself, which we can
881  // probably translate.
882  user_msg = translation::gettext(e.message.c_str());
883  }
884 
885  gui2::show_error_message(user_msg);
886  } else {
887  ERR_NET << "caught network error";
888  }
889  } catch(const config::error& e) {
890  if(!e.message.empty()) {
891  ERR_CONFIG << "caught config::error: " << e.message;
892  gui2::show_transient_message("", e.message);
893  } else {
894  ERR_CONFIG << "caught config::error";
895  }
896  } catch(const incorrect_map_format_error& e) {
897  gui2::show_error_message(_("The game map could not be loaded: ") + e.message);
898  } catch(savegame::load_game_exception& e) {
899  load_data_ = std::move(e.data_);
900  // this will make it so next time through the title screen loop, this game is loaded
901  } catch(const wml_exception& e) {
902  e.show();
903  } catch(const game::error& e) {
904  PLAIN_LOG << "caught game::error...";
905  gui2::show_error_message(_("Error: ") + e.message);
906  }
907 
908  return true;
909 }
910 
912 {
914  return true;
915  }
916 
917  DBG_MP << "starting multiplayer game from the commandline";
918 
920 
921  events::discard_input(); // prevent the "keylogger" effect
923 
924  try {
926  } catch(savegame::load_game_exception& e) {
927  load_data_ = std::move(e.data_);
928  return true;
929  }
930 
931  return false;
932 }
933 
935 {
936  if(!gui2::dialogs::language_selection::execute()) {
937  return false;
938  }
939 
942  }
943 
948 
949  return true;
950 }
951 
953 {
954  assert(!load_data_);
955  if(play_replay_) {
956  play_replay();
957  return;
958  }
959 
960  gui2::dialogs::loading_screen::display([this, reload]() {
962 
963  if(reload == reload_mode::RELOAD_DATA) {
964  try {
967  } catch(const config::error&) {
968  return;
969  }
970  }
971  });
972 
973  try {
974  campaign_controller ccontroller(state_);
975  ccontroller.play_game();
976  ai::manager::singleton_ = nullptr;
977  } catch(savegame::load_game_exception& e) {
978  load_data_ = std::move(e.data_);
979  // this will make it so next time through the title screen loop, this game is loaded
980  } catch(const wml_exception& e) {
981  e.show();
982  } catch(const mapgen_exception& e) {
983  gui2::show_error_message(_("Map generator error: ") + e.message);
984  }
985 }
986 
988 {
989  assert(!load_data_);
990  try {
991  campaign_controller ccontroller(state_);
992  ccontroller.play_replay();
993  } catch(savegame::load_game_exception& e) {
994  load_data_ = std::move(e.data_);
995  // this will make it so next time through the title screen loop, this game is loaded
996  } catch(const wml_exception& e) {
997  e.show();
998  }
999 }
1000 
1002 {
1004  while(true) {
1006 
1008 
1010 
1011  if(res != editor::EXIT_RELOAD_DATA) {
1012  return res;
1013  }
1014 
1016  }
1017 
1018  return editor::EXIT_ERROR; // not supposed to happen
1019 }
1020 
1022 {
1023  load_data_.reset();
1024 }
1025 
1027 {
1028  try {
1030  video::deinit();
1031  } catch(std::exception& e) {
1032  ERR_GENERAL << "Suppressing exception thrown during ~game_launcher: " << e.what();
1033  } catch(...) {
1034  ERR_GENERAL << "Suppressing exception " << utils::get_unknown_exception_type() << " thrown during ~game_launcher";
1035  }
1036 }
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands.
static manager * singleton_
Definition: manager.hpp:437
level_result::type play_game()
level_result::type play_replay()
bool nogui
True if –nogui was given on the command line.
utils::optional< std::pair< int, int > > resolution
Pair of AxB values specified after –resolution.
bool headless_unit_test
True if –unit is used and –showgui is not present.
utils::optional< std::string > screenshot_map_file
Map file to make a screenshot of.
utils::optional< std::string > test
Non-empty if –test was given on the command line.
bool windowed
True if –windowed was given on the command line.
utils::optional< std::string > language
Non-empty if –language was given on the command line.
bool noreplaycheck
True if –noreplaycheck was given on the command line.
utils::optional< std::string > screenshot_output_file
Output file to put screenshot in.
utils::optional< int > max_fps
Max FPS specified by –max-fps option.
utils::optional< std::string > core_id
Non-empty if –core was given on the command line.
utils::optional< int > campaign_difficulty
Non-empty if –campaign-difficulty 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.
utils::optional< std::string > server
Non-empty if –server was given on the command line.
bool script_unsafe_mode
Whether to load the "package" package for the scripting environment.
utils::optional< unsigned int > translation_percent
Non-empty if –all-translations or –translations-over is given on the command line.
utils::optional< std::string > plugin_file
File to load a lua plugin script from.
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.
utils::optional< std::string > render_image_dst
Output file to put rendered image path in.
utils::optional< std::string > render_image
Image path to render.
utils::optional< std::string > campaign
Non-empty if –campaign was given on the command line.
bool new_widgets
True if –new-widgets was given on the command line.
bool fps
True if –fps was given on the command line.
utils::optional< std::string > username
Non-empty if –username was given on the command line.
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.
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.
utils::optional< std::string > editor
Non-empty if –editor was given on the command line.
utils::optional< std::string > password
Non-empty if –password was given on the command line.
bool fullscreen
True if –fullscreen was given on the command line.
utils::optional< std::string > campaign_scenario
Non-empty if –campaign-scenario was given on the command line.
utils::optional< std::string > load
Non-empty if –load 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:158
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).
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_
utils::optional< savegame::load_game_metadata > load_data_
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(const std::function< void()> &f)
Generic locator abstracting the location of an image.
Definition: picture.hpp:59
void load_package()
Loads the package library into lua environment.
void play_slice()
Definition: context.cpp:103
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
void set_network_host(const std::string &host)
bool set_music(bool ison)
std::string network_host()
bool set_ui_sound(bool ison)
void set_login(const std::string &login)
static prefs & get()
void show_wesnothd_server_search()
void set_password(const std::string &server, const std::string &login, const std::string &key)
bool set_turn_bell(bool ison)
void set_show_fps(bool value)
bool set_sound(bool ison)
void set_resolution(const point &res)
static void disable_preferences_save()
std::string get_mp_server_program_name()
void set_mp_server_program_name(const std::string &)
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:755
void set_carryover_sides_start(config carryover_sides_start)
Definition: saved_game.cpp:165
std::string get_scenario_id() const
Definition: saved_game.cpp:701
void clear()
Definition: saved_game.cpp:836
statistics_record::campaign_stats_t & statistics()
Definition: saved_game.hpp:143
void cancel_orders()
Definition: saved_game.cpp:738
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:209
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:361
const std::string & filename() const
Definition: savegame.hpp:178
static void reset_translations()
Definition: tstring.cpp:665
Definitions for the interface to Wesnoth Markup Language (WML).
Declarations for File-IO.
std::size_t i
Definition: function.cpp:1029
#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:327
void init_textdomains(const game_config_view &cfg)
Initializes the list of textdomains from a configuration object.
Definition: language.cpp:365
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:148
Standard logging facilities (interface).
#define STREAMING_LOG
Definition: log.hpp:298
#define PLAIN_LOG
Definition: log.hpp:297
@ 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:209
@ 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:772
std::string get_cache_dir()
Definition: filesystem.cpp:865
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
std::string get_user_data_dir()
Definition: filesystem.cpp:855
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:327
utils::optional< std::string > get_wml_location(const std::string &path, const utils::optional< std::string > &current_dir)
Returns a translated path to the actual file or directory, if it exists.
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:53
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_wesnothd_name()
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:58
std::string turn_bell
Game configuration data as global variables.
Definition: build_info.cpp:61
std::string path
Definition: filesystem.cpp:92
std::string get_default_title_string()
std::vector< server_info > server_list
Definition: game_config.cpp:76
void set_debug(bool new_debug)
Definition: game_config.cpp:97
bool show_debug_clock_button
Do we wish to show the button for the debug clock.
static bool sound()
static bool ui_sound_on()
static bool music_on()
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:37
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:201
void flush_cache()
Purges all image caches.
Definition: picture.cpp:200
surface get_surface(const image::locator &i_locator, TYPE type, bool skip_cache)
Returns an image surface suitable for software manipulation.
Definition: picture.cpp:653
save_result save_image(const locator &i_locator, const std::string &filename)
Definition: picture.cpp:884
@ UNSCALED
Unmodified original-size image.
Definition: picture.hpp:164
bool broke_strict()
Definition: log.cpp:400
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 clean_saves(const std::string &label)
Delete all autosaves of a certain scenario from the default save directory.
Definition: savegame.cpp:63
bool init_sound()
Definition: sound.cpp:444
void close_sound()
Definition: sound.cpp:496
void flush_cache()
Definition: sound.cpp:196
bool select_campaign(saved_game &state, jump_to_campaign_info jump_to_campaign)
void process(int mousex, int mousey)
Definition: tooltips.cpp:340
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(std::string_view 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:647
void set_window_icon(surface &icon)
Sets the icon of the main window.
Definition: video.cpp:653
void init(fake type)
Initialize the video subsystem.
Definition: video.cpp:81
void deinit()
Deinitialize the video subsystem.
Definition: video.cpp:110
This file contains the settings handling of the widget library.
std::string filename
Filename.
An exception object used when an IO error occurs.
Definition: filesystem.hpp:67
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