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