The Battle for Wesnoth  1.19.5+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, 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 "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 <utility> // for pair
61 
62 
63 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
64 #include "gui/widgets/debug.hpp"
65 #endif
66 
67 
68 static lg::log_domain log_config("config");
69 #define ERR_CONFIG LOG_STREAM(err, log_config)
70 #define WRN_CONFIG LOG_STREAM(warn, log_config)
71 #define LOG_CONFIG LOG_STREAM(info, log_config)
72 
73 #define ERR_GENERAL LOG_STREAM(err, lg::general())
74 #define LOG_GENERAL LOG_STREAM(info, lg::general())
75 #define WRN_GENERAL LOG_STREAM(warn, lg::general())
76 #define DBG_GENERAL LOG_STREAM(debug, lg::general())
77 
78 #define LOG_TEST FORCE_LOG_TO(lg::general(), log_config)
79 
80 static lg::log_domain log_mp_create("mp/create");
81 #define DBG_MP LOG_STREAM(debug, log_mp_create)
82 
83 static lg::log_domain log_network("network");
84 #define ERR_NET LOG_STREAM(err, log_network)
85 
86 static lg::log_domain log_enginerefac("enginerefac");
87 #define LOG_RG LOG_STREAM(info, log_enginerefac)
88 
89 namespace bp = boost::process;
90 
92  : cmdline_opts_(cmdline_opts)
93  , font_manager_()
94  , image_manager_()
95  , main_event_context_()
96  , hotkey_manager_()
97  , music_thinker_()
98  , music_muter_()
99  , test_scenarios_{"test"}
100  , screenshot_map_()
101  , screenshot_filename_()
102  , state_()
103  , play_replay_(false)
104  , multiplayer_server_()
105  , jump_to_multiplayer_(false)
106  , jump_to_campaign_{}
107  , jump_to_editor_(false)
108  , load_data_()
109 {
110  bool no_music = false;
111  bool no_sound = false;
112 
113  if(cmdline_opts_.core_id) {
114  prefs::get().set_core(*cmdline_opts_.core_id);
115  }
116  if(cmdline_opts_.campaign) {
117  jump_to_campaign_.jump = true;
119  PLAIN_LOG << "selected campaign id: [" << jump_to_campaign_.campaign_id << "]";
120 
123  PLAIN_LOG << "selected difficulty: [" << jump_to_campaign_.difficulty << "]";
124  } else {
125  jump_to_campaign_.difficulty = -1; // let the user choose the difficulty
126  }
127 
130  PLAIN_LOG << "selected scenario id: [" << jump_to_campaign_.scenario_id << "]";
131  }
132 
135  }
136  }
137  if(cmdline_opts_.clock)
139  if(cmdline_opts_.debug) {
141  game_config::mp_debug = true;
142  }
143 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
144  if(cmdline_opts_.debug_dot_domain)
145  gui2::debug_layout_graph::set_domain(*cmdline_opts_.debug_dot_domain);
146  if(cmdline_opts_.debug_dot_level)
147  gui2::debug_layout_graph::set_level(*cmdline_opts_.debug_dot_level);
148 #endif
149  if(cmdline_opts_.editor) {
150  jump_to_editor_ = true;
151  if(!cmdline_opts_.editor->empty()) {
154  }
155  }
156  if(cmdline_opts_.fps)
157  prefs::get().set_show_fps(true);
159  start_in_fullscreen_ = true;
160  if(cmdline_opts_.load)
163  if(cmdline_opts_.max_fps) {
164  int fps = std::clamp(*cmdline_opts_.max_fps, 1, 1000);
165  fps = 1000 / fps;
166  // increase the delay to avoid going above the maximum
167  if(1000 % fps != 0) {
168  ++fps;
169  }
170  prefs::get().set_draw_delay(fps);
171  }
173  no_sound = true;
175  }
177  gui2::new_widgets = true;
179  game_config::no_delay = true;
181  no_music = true;
183  no_sound = true;
185  const int xres = std::get<0>(*cmdline_opts_.resolution);
186  const int yres = std::get<1>(*cmdline_opts_.resolution);
187  if(xres > 0 && yres > 0) {
188  prefs::get().set_resolution(point(xres, yres));
189  prefs::get().set_maximized(false);
190  }
191  }
193  // TODO it could be simplified to use cmdline_opts_ directly if there is no other way to enter screenshot mode
196  no_sound = true;
198  }
199  if (cmdline_opts_.server){
200  jump_to_multiplayer_ = true;
201  // Do we have any server specified ?
202  if(!cmdline_opts_.server->empty()) {
204  } else {
205  // Pick the first server in config
206  if(game_config::server_list.size() > 0) {
208  } else {
209  multiplayer_server_ = "";
210  }
211  }
212  if(cmdline_opts_.username) {
215  if(cmdline_opts_.password) {
217  prefs::get().set_password(*cmdline_opts.server, *cmdline_opts.username, *cmdline_opts_.password);
218  }
219  }
220  }
221  if(cmdline_opts_.test) {
222  if(!cmdline_opts_.test->empty()) {
224  }
225  }
226  if(!cmdline_opts_.unit_test.empty()) {
228  }
230  start_in_fullscreen_ = false;
232  load_data_->show_replay = true;
235 
236  if(!cmdline_opts.nobanner) {
237  PLAIN_LOG
238  << "\nData directory: " << game_config::path
239  << "\nUser data directory: " << filesystem::get_user_data_dir()
240  << "\nCache directory: " << filesystem::get_cache_dir()
241  << "\n\n";
242  }
243 
244  // disable sound in nosound mode, or when sound engine failed to initialize
245  if(no_sound || ((prefs::get().sound() || prefs::get().music_on() ||
247  !sound::init_sound())) {
248  prefs::get().set_sound(false);
249  prefs::get().set_music(false);
250  prefs::get().set_turn_bell(false);
251  prefs::get().set_ui_sound(false);
252  } else if(no_music) { // else disable the music in nomusic mode
253  prefs::get().set_music(false);
254  }
255 }
256 
258 {
259  if(!::load_language_list()) {
260  return false;
261  }
262 
263  language_def locale;
264  if(cmdline_opts_.language) {
265  std::vector<language_def> langs = get_languages(true);
266  for(const language_def& def : langs) {
267  if(def.localename == *cmdline_opts_.language) {
268  locale = def;
269  break;
270  }
271  }
272  if(locale.localename.empty()) {
273  PLAIN_LOG << "Language symbol '" << *cmdline_opts_.language << "' not found.";
274  return false;
275  }
276  } else {
277  locale = get_locale();
278  }
279  ::set_language(locale);
280 
281  return true;
282 }
283 
285 {
286  // Handle special commandline launch flags
291  {
297  {
298  PLAIN_LOG << "--nogui flag is only valid with --multiplayer or --screenshot or --plugin flags";
299  return false;
300  }
302  // Screenshots require a rendering context, and thus a window,
303  // so we create one but hidden.
305  } else {
306  // Other functions don't require a window at all.
308  }
309  game_config::no_delay = true;
310  return true;
311  }
312 
313  // Initialize video subsystem, and create a new window.
314  video::init();
315 
316  // Set window title and icon
318 
319 #if !(defined(__APPLE__))
320  surface icon(image::get_surface(image::locator{"icons/icon-game.png"}, image::UNSCALED));
321  if(icon != nullptr) {
323  }
324 #endif
325  return true;
326 }
327 
329 {
330  bool error = false;
331 
332  if(!cmdline_opts_.nobanner) {
333  STREAMING_LOG << "Checking lua scripts... ";
334  }
335 
337  // load the "package" package, so that scripts can get what packages they want
339  }
340 
341  // get the application lua kernel, load and execute script file, if script file is present
344 
345  if(!sf->fail()) {
346  /* Cancel all "jumps" to editor / campaign / multiplayer */
347  jump_to_multiplayer_ = false;
348  jump_to_editor_ = false;
349  jump_to_campaign_.jump = false;
350 
351  std::string full_script((std::istreambuf_iterator<char>(*sf)), std::istreambuf_iterator<char>());
352 
353  PLAIN_LOG << "\nRunning lua script: " << *cmdline_opts_.script_file;
354 
356  } else {
357  PLAIN_LOG << "Encountered failure when opening script '" << *cmdline_opts_.script_file << '\'';
358  error = true;
359  }
360  }
361 
363  std::string filename = *cmdline_opts_.plugin_file;
364 
365  PLAIN_LOG << "Loading a plugin file'" << filename << "'...";
366 
368 
369  try {
370  if(sf->fail()) {
371  throw std::runtime_error("failed to open plugin file");
372  }
373 
374  /* Cancel all "jumps" to editor / campaign / multiplayer */
375  jump_to_multiplayer_ = false;
376  jump_to_editor_ = false;
377  jump_to_campaign_.jump = false;
378 
379  std::string full_plugin((std::istreambuf_iterator<char>(*sf)), std::istreambuf_iterator<char>());
380 
382 
383  std::size_t i = pm.add_plugin(filename, full_plugin);
384 
385  for(std::size_t j = 0; j < pm.size(); ++j) {
386  PLAIN_LOG << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j);
387  }
388 
389  PLAIN_LOG << "Starting a plugin...";
390  pm.start_plugin(i);
391 
392  for(std::size_t j = 0; j < pm.size(); ++j) {
393  PLAIN_LOG << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j);
394  }
395 
396  plugins_context pc("init");
397 
398  for(std::size_t repeat = 0; repeat < 5; ++repeat) {
399  PLAIN_LOG << "Playing a slice...";
400  pc.play_slice();
401 
402  for(std::size_t j = 0; j < pm.size(); ++j) {
403  PLAIN_LOG << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j);
404  }
405  }
406 
407  return true;
408  } catch(const std::exception& e) {
409  gui2::show_error_message(std::string("When loading a plugin, error:\n") + e.what());
410  error = true;
411  }
412  }
413 
414  if(!error && !cmdline_opts_.nobanner) {
415  PLAIN_LOG << "ok";
416  }
417 
418  return !error;
419 }
420 
421 void game_launcher::set_test(const std::string& id)
422 {
423  state_.clear();
424  state_.classification().type = campaign_type::type::test;
426  state_.classification().era_id = "era_default";
427 
428  state_.set_carryover_sides_start(config{"next_scenario", id});
429 }
430 
432 {
433  // This first_time variable was added in 70f3c80a3e2 so that using the GUI
434  // menu to load a game works. That seems to have edge-cases, for example if
435  // you try to load a game a second time then Wesnoth exits.
436  static bool first_time = true;
437 
438  if(!cmdline_opts_.test) {
439  return true;
440  }
441 
442  if(!first_time) {
443  return false;
444  }
445 
446  first_time = false;
447 
448  if(test_scenarios_.size() == 0) {
449  // shouldn't happen, as test_scenarios_ is initialised to {"test"}
450  PLAIN_LOG << "Error in the test handling code";
451  return false;
452  }
453 
454  if(test_scenarios_.size() > 1) {
455  PLAIN_LOG << "You can't run more than one unit test in interactive mode";
456  }
457 
458  set_test(test_scenarios_.at(0));
459 
461 
462  try {
463  campaign_controller ccontroller(state_);
464  ccontroller.play_game();
465  } catch(savegame::load_game_exception& e) {
466  load_data_ = std::move(e.data_);
467  return true;
468  }
469 
470  return false;
471 }
472 
473 /**
474  * Runs unit tests specified on the command line.
475  *
476  * If multiple unit tests were specified, then this will stop at the first test
477  * which returns a non-zero status.
478  */
479 // Same as play_test except that we return the results of play_game.
480 // \todo "same ... except" ... and many other changes, such as testing the replay
482 {
483  // There's no copy of play_test's first_time variable. That seems to be for handling
484  // the player loading a game via the GUI, which makes no sense in a non-interactive test.
485  if(cmdline_opts_.unit_test.empty()) {
487  }
488 
489  auto ret = unit_test_result::TEST_FAIL; // will only be returned if no test is run
490  for(const auto& scenario : test_scenarios_) {
491  set_test(scenario);
492  ret = single_unit_test();
493  const char* describe_result;
494  switch(ret) {
496  describe_result = "PASS TEST";
497  break;
499  describe_result = "FAIL TEST (INVALID REPLAY)";
500  break;
502  describe_result = "FAIL TEST (ERRORED REPLAY)";
503  break;
505  describe_result = "FAIL TEST (WML EXCEPTION)";
506  break;
508  describe_result = "FAIL TEST (DEFEAT)";
509  break;
511  describe_result = "PASS TEST (VICTORY)";
512  break;
514  describe_result = "BROKE STRICT (PASS)";
515  break;
517  describe_result = "BROKE STRICT (FAIL)";
518  break;
520  describe_result = "BROKE STRICT (DEFEAT)";
521  break;
523  describe_result = "BROKE STRICT (VICTORY)";
524  break;
525  default:
526  describe_result = "FAIL TEST (UNKNOWN)";
527  break;
528  }
529 
530  PLAIN_LOG << describe_result << " (" << int(ret) << "): " << scenario;
531  if(ret != unit_test_result::TEST_PASS) {
532  break;
533  }
534  }
535 
536  return ret;
537 }
538 
540 {
542 
543  level_result::type game_res = level_result::type::fail;
544  try {
545  campaign_controller ccontroller(state_, true);
546  game_res = ccontroller.play_game();
547  if(game_res == level_result::type::fail) {
548  if(lg::broke_strict()) {
550  } else {
552  }
553  }
554  } catch(const wml_exception& e) {
555  PLAIN_LOG << "Caught WML Exception:" << e.dev_message;
557  }
558 
560 
562  return pass_victory_or_defeat(game_res);
563  }
564 
566  save.save_game_automatic(false, "unit_test_replay");
567 
569  savegame::save_index_class::default_saves_dir(), save.filename(), "", true, true, false};
570 
571  if(!load_game()) {
572  PLAIN_LOG << "Failed to load the replay!";
573  return unit_test_result::TEST_FAIL_LOADING_REPLAY; // failed to load replay
574  }
575 
576  try {
577  const bool was_strict_broken = lg::broke_strict();
578  campaign_controller ccontroller(state_, true);
579  ccontroller.play_replay();
580  if(!was_strict_broken && lg::broke_strict()) {
581  PLAIN_LOG << "Observed failure on replay";
583  }
584  } catch(const wml_exception& e) {
585  PLAIN_LOG << "WML Exception while playing replay: " << e.dev_message;
587  }
588 
589  return pass_victory_or_defeat(game_res);
590 }
591 
593 {
594  if(res == level_result::type::defeat) {
595  if(lg::broke_strict()) {
597  } else {
599  }
600  } else if(res == level_result::type::victory) {
601  if(lg::broke_strict()) {
603  } else {
605  }
606  }
607 
608  if(lg::broke_strict()) {
610  } else {
612  }
613 }
614 
616 {
618  return true;
619  }
620 
622 
624 
626  return false;
627 }
628 
630 {
632  return true;
633  }
634 
635  state_.classification().type = campaign_type::type::multiplayer;
636  DBG_GENERAL << "Current campaign type: " << campaign_type::get_string(state_.classification().type);
637 
638  try {
640  } catch(const config::error& e) {
641  PLAIN_LOG << "Error loading game config: " << e.what();
642  return false;
643  }
644 
645  // A default output filename
646  std::string outfile = "wesnoth_image.png";
647 
648  // If a output path was given as an argument, use that instead
650  outfile = *cmdline_opts_.render_image_dst;
651  }
652 
654  exit(1);
655  }
656 
657  return false;
658 }
659 
661 {
662  return load_data_.has_value();
663 }
664 
666 {
667  assert(game_config_manager::get());
668 
669  DBG_GENERAL << "Current campaign type: " << campaign_type::get_string(state_.classification().type);
670 
672  if(load_data_) {
673  load.data() = std::move(*load_data_);
675  }
676 
677  try {
678  if(!load.load_game()) {
679  return false;
680  }
681 
682  load.set_gamestate();
683  try {
685  } catch(const config::error&) {
686  return false;
687  }
688 
689  } catch(const config::error& e) {
690  if(e.message.empty()) {
691  gui2::show_error_message(_("The file you have tried to load is corrupt"));
692  } else {
693  gui2::show_error_message(_("The file you have tried to load is corrupt: '") + e.message + '\'');
694  }
695 
696  return false;
697  } catch(const wml_exception& e) {
698  e.show();
699  return false;
700  } catch(const filesystem::io_exception& e) {
701  if(e.message.empty()) {
702  gui2::show_error_message(_("File I/O Error while reading the game"));
703  } else {
704  gui2::show_error_message(_("File I/O Error while reading the game: '") + e.message + '\'');
705  }
706 
707  return false;
708  } catch(const game::error& e) {
709  if(e.message.empty()) {
710  gui2::show_error_message(_("The file you have tried to load is corrupt"));
711  } else {
712  gui2::show_error_message(_("The file you have tried to load is corrupt: '") + e.message + '\'');
713  }
714 
715  return false;
716  }
717 
718  play_replay_ = load.data().show_replay;
719  LOG_CONFIG << "is middle game savefile: " << (state_.is_mid_game_save() ? "yes" : "no");
720  LOG_CONFIG << "show replay: " << (play_replay_ ? "yes" : "no");
721  // in case load.data().show_replay && state_.is_start_of_scenario
722  // there won't be any turns to replay, but the
723  // user gets to watch the intro sequence again ...
724 
725  if(!state_.is_start_of_scenario() && load.data().show_replay) {
727  }
728 
731  }
732 
733  if(load.data().cancel_orders) {
735  }
736 
737  return true;
738 }
739 
741 {
742  state_.clear();
743  state_.classification().type = campaign_type::type::scenario;
744  play_replay_ = false;
745 
747 }
748 
750 {
752 }
753 
755 {
756  if(jump_to_campaign_.jump) {
757  jump_to_campaign_.jump = false;
758  if(new_campaign()) {
761  } else {
762  return false;
763  }
764  }
765 
766  return true;
767 }
768 
770 {
771  if(!jump_to_multiplayer_) {
772  return true;
773  }
774 
775  jump_to_multiplayer_ = false;
777 }
778 
780 {
781  if(jump_to_editor_) {
782  jump_to_editor_ = false;
783 
784  const std::string to_open = load_data_ ? filesystem::normalize_path(load_data_->filename) : "";
786 
788  return false;
789  }
790  }
791 
792  return true;
793 }
794 
796 {
797  std::string wesnothd_program = "";
798  if(!prefs::get().get_mp_server_program_name().empty()) {
799  wesnothd_program = prefs::get().get_mp_server_program_name();
800  } else {
801  wesnothd_program = filesystem::get_wesnothd_name();
802  }
803 
804  std::string config = filesystem::get_user_data_dir() + "/lan_server.cfg";
806  // copy file if it isn't created yet
808  }
809 
810  LOG_GENERAL << "Starting wesnothd";
811  try
812  {
813 #ifndef _WIN32
814  bp::child c(wesnothd_program, "-c", config);
815 #else
816  bp::child c(wesnothd_program, "-c", config, bp::windows::create_no_window);
817 #endif
818  c.detach();
819  // Give server a moment to start up
820  SDL_Delay(50);
821  return;
822  }
823  catch(const bp::process_error& e)
824  {
826 
827  // Couldn't start server so throw error
828  WRN_GENERAL << "Failed to start server " << wesnothd_program << ":\n" << e.what();
829  throw game::mp_server_error("Starting MP server failed!");
830  }
831 }
832 
834 {
835  try {
836  if(mode == mp_mode::HOST) {
837  try {
838  start_wesnothd();
839  } catch(const game::mp_server_error&) {
841 
842  try {
843  start_wesnothd();
844  } catch(const game::mp_server_error&) {
845  return false;
846  }
847  }
848  }
849 
850  // If a server address wasn't specified, prompt for it now.
851  if(mode != mp_mode::LOCAL && multiplayer_server_.empty()) {
852  if(!gui2::dialogs::mp_connect::execute()) {
853  return false;
854  }
855 
856  // The prompt saves its input to preferences.
858 
859  if(multiplayer_server_ != prefs::get().builtin_servers_list().front().address) {
861  }
862  }
863 
864  // create_engine already calls game_config_manager::get()->load_config but maybe its better to have MULTIPLAYER
865  // defined while we are in the lobby.
867 
868  events::discard_input(); // prevent the "keylogger" effect
870 
871  if(mode == mp_mode::LOCAL) {
873  } else {
875  multiplayer_server_.clear();
876  }
877 
878  } catch(const wesnothd_rejected_client_error& e) {
879  gui2::show_error_message(e.message);
880  } catch(const game::mp_server_error& e) {
881  gui2::show_error_message(_("Error while starting server: ") + e.message);
882  } catch(const game::load_game_failed& e) {
883  gui2::show_error_message(_("The game could not be loaded: ") + e.message);
884  } catch(const game::game_error& e) {
885  gui2::show_error_message(_("Error while playing the game: ") + e.message);
886  } catch(const mapgen_exception& e) {
887  gui2::show_error_message(_("Map generator error: ") + e.message);
888  } catch(const wesnothd_error& e) {
889  if(!e.message.empty()) {
890  ERR_NET << "caught network error: " << e.message;
891 
892  std::string user_msg;
893  auto conn_err = dynamic_cast<const wesnothd_connection_error*>(&e);
894 
895  if(conn_err) {
896  // The wesnothd_connection_error subclass is only thrown with messages
897  // from boost::system::error_code which we can't translate ourselves.
898  // It's also the originator of the infamous EOF error that happens when
899  // the server dies. <https://github.com/wesnoth/wesnoth/issues/3005>. It
900  // will provide a translated string instead of that when it happens.
901  user_msg = !conn_err->user_message.empty()
902  ? conn_err->user_message
903  : _("Connection failed: ") + e.message;
904  } else {
905  // This will be a message from the server itself, which we can
906  // probably translate.
907  user_msg = translation::gettext(e.message.c_str());
908  }
909 
910  gui2::show_error_message(user_msg);
911  } else {
912  ERR_NET << "caught network error";
913  }
914  } catch(const config::error& e) {
915  if(!e.message.empty()) {
916  ERR_CONFIG << "caught config::error: " << e.message;
917  gui2::show_transient_message("", e.message);
918  } else {
919  ERR_CONFIG << "caught config::error";
920  }
921  } catch(const incorrect_map_format_error& e) {
922  gui2::show_error_message(_("The game map could not be loaded: ") + e.message);
923  } catch(savegame::load_game_exception& e) {
924  load_data_ = std::move(e.data_);
925  // this will make it so next time through the title screen loop, this game is loaded
926  } catch(const wml_exception& e) {
927  e.show();
928  } catch(const game::error& e) {
929  PLAIN_LOG << "caught game::error...";
930  gui2::show_error_message(_("Error: ") + e.message);
931  }
932 
933  return true;
934 }
935 
937 {
939  return true;
940  }
941 
942  DBG_MP << "starting multiplayer game from the commandline";
943 
945 
946  events::discard_input(); // prevent the "keylogger" effect
948 
949  try {
951  } catch(savegame::load_game_exception& e) {
952  load_data_ = std::move(e.data_);
953  return true;
954  }
955 
956  return false;
957 }
958 
960 {
961  if(!gui2::dialogs::language_selection::execute()) {
962  return false;
963  }
964 
967  }
968 
973 
974  return true;
975 }
976 
978 {
979  assert(!load_data_);
980  if(play_replay_) {
981  play_replay();
982  return;
983  }
984 
985  gui2::dialogs::loading_screen::display([this, reload]() {
987 
988  if(reload == reload_mode::RELOAD_DATA) {
989  try {
992  } catch(const config::error&) {
993  return;
994  }
995  }
996  });
997 
998  try {
999  campaign_controller ccontroller(state_);
1000  ccontroller.play_game();
1001  ai::manager::singleton_ = nullptr;
1002  } catch(savegame::load_game_exception& e) {
1003  load_data_ = std::move(e.data_);
1004  // this will make it so next time through the title screen loop, this game is loaded
1005  } catch(const wml_exception& e) {
1006  e.show();
1007  } catch(const mapgen_exception& e) {
1008  gui2::show_error_message(_("Map generator error: ") + e.message);
1009  }
1010 }
1011 
1013 {
1014  assert(!load_data_);
1015  try {
1016  campaign_controller ccontroller(state_);
1017  ccontroller.play_replay();
1018  } catch(savegame::load_game_exception& e) {
1019  load_data_ = std::move(e.data_);
1020  // this will make it so next time through the title screen loop, this game is loaded
1021  } catch(const wml_exception& e) {
1022  e.show();
1023  }
1024 }
1025 
1027 {
1029  while(true) {
1031 
1033 
1035 
1036  if(res != editor::EXIT_RELOAD_DATA) {
1037  return res;
1038  }
1039 
1041  }
1042 
1043  return editor::EXIT_ERROR; // not supposed to happen
1044 }
1045 
1047 {
1048  load_data_.reset();
1049 }
1050 
1052 {
1053  try {
1055  video::deinit();
1056  } catch(std::exception& e) {
1057  ERR_GENERAL << "Suppressing exception thrown during ~game_launcher: " << e.what();
1058  } catch(...) {
1059  ERR_GENERAL << "Suppressing exception " << utils::get_unknown_exception_type() << " thrown during ~game_launcher";
1060  }
1061 }
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()
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 (similar to a 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.
bool nodelay
True if –nodelay 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 > script_file
File to load lua script from.
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:172
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(std::function< void()> f)
Generic locator abstracting the location of an image.
Definition: picture.hpp:59
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
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:733
void set_carryover_sides_start(config carryover_sides_start)
Definition: saved_game.cpp:164
std::string get_scenario_id() const
Definition: saved_game.cpp:679
void clear()
Definition: saved_game.cpp:814
statistics_record::campaign_stats_t & statistics()
Definition: saved_game.hpp:143
void cancel_orders()
Definition: saved_game.cpp:716
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:360
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:1023
#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:300
#define PLAIN_LOG
Definition: log.hpp:299
@ 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:837
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
std::string get_user_data_dir()
Definition: filesystem.cpp:827
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:325
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:90
std::string get_default_title_string()
std::vector< server_info > server_list
Definition: game_config.cpp:73
void set_debug(bool new_debug)
Definition: game_config.cpp:94
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:398
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: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: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(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:632
void set_window_icon(surface &icon)
Sets the icon of the main window.
Definition: video.cpp:638
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