The Battle for Wesnoth  1.15.5+dev
game_launcher.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "game_launcher.hpp"
16 #include "game_errors.hpp"
17 
18 #include "ai/manager.hpp" // for manager
20 #include "commandline_options.hpp" // for commandline_options
21 #include "config.hpp" // for config, etc
22 #include "cursor.hpp" // for set, CURSOR_TYPE::NORMAL
23 #include "exceptions.hpp" // for error
24 #include "filesystem.hpp" // for get_user_config_dir, etc
25 #include "game_classification.hpp" // for game_classification, etc
26 #include "game_config.hpp" // for path, no_delay, revision, etc
27 #include "game_config_manager.hpp" // for game_config_manager
28 #include "generators/map_generator.hpp" // for mapgen_exception
29 #include "gettext.hpp" // for _
31 #include "gui/dialogs/language_selection.hpp" // for language_selection
33 #include "gui/dialogs/message.hpp" //for show error message
35 #include "gui/dialogs/multiplayer/mp_host_game_prompt.hpp" //for host game prompt
37 #include "gui/dialogs/outro.hpp"
39 #include "gui/dialogs/transient_message.hpp" // for show_transient_message
40 #include "gui/dialogs/title_screen.hpp" // for show_debug_clock_button
41 #include "gui/widgets/settings.hpp" // for new_widgets
42 #include "gui/widgets/retval.hpp" // for window, etc
43 #include "language.hpp" // for language_def, etc
44 #include "log.hpp" // for LOG_STREAM, logger, general, etc
45 #include "map/exception.hpp"
46 #include "game_initialization/multiplayer.hpp" // for start_client, etc
48 #include "game_initialization/playcampaign.hpp" // for play_game, etc
49 #include "preferences/general.hpp" // for disable_preferences_save, etc
50 #include "preferences/display.hpp"
51 #include "savegame.hpp" // for clean_saves, etc
52 #include "save_index.hpp"
54 #include "sdl/surface.hpp" // for surface
55 #include "serialization/compression.hpp" // for format::NONE
56 #include "serialization/string_utils.hpp" // for split
57 #include "game_initialization/singleplayer.hpp" // for sp_create_mode
58 #include "statistics.hpp"
59 #include "tstring.hpp" // for operator==, operator!=
60 #include "utils/general.hpp" // for clamp
61 #include "video.hpp" // for CVideo
63 #include "wml_exception.hpp" // for wml_exception
64 
65 #include <algorithm> // for copy, max, min, stable_sort
66 #include <cstdlib> // for system
67 #include <iostream> // for operator<<, basic_ostream, etc
68 #include <new>
69 #include <utility> // for pair
70 #include <SDL2/SDL.h> // for SDL_INIT_JOYSTICK, etc
71 
72 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
73 #include "gui/widgets/debug.hpp"
74 #endif
75 
76 // For wesnothd launch code.
77 #ifdef _WIN32
78 
79 #define WIN32_LEAN_AND_MEAN
80 #include <windows.h>
81 
82 #endif // _WIN32
83 
85 
86 static lg::log_domain log_config("config");
87 #define ERR_CONFIG LOG_STREAM(err, log_config)
88 #define WRN_CONFIG LOG_STREAM(warn, log_config)
89 #define LOG_CONFIG LOG_STREAM(info, log_config)
90 
91 #define LOG_GENERAL LOG_STREAM(info, lg::general())
92 #define WRN_GENERAL LOG_STREAM(warn, lg::general())
93 #define DBG_GENERAL LOG_STREAM(debug, lg::general())
94 
95 static lg::log_domain log_mp_create("mp/create");
96 #define DBG_MP LOG_STREAM(debug, log_mp_create)
97 
98 static lg::log_domain log_network("network");
99 #define ERR_NET LOG_STREAM(err, log_network)
100 
101 static lg::log_domain log_enginerefac("enginerefac");
102 #define LOG_RG LOG_STREAM(info, log_enginerefac)
103 
104 game_launcher::game_launcher(const commandline_options& cmdline_opts, const char *appname) :
105  cmdline_opts_(cmdline_opts),
106  video_(new CVideo()),
107  font_manager_(),
108  prefs_manager_(),
109  image_manager_(),
110  main_event_context_(),
111  hotkey_manager_(),
112  music_thinker_(),
113  music_muter_(),
114  test_scenarios_{"test"},
115  screenshot_map_(),
117  state_(),
118  play_replay_(false),
120  jump_to_multiplayer_(false),
121  jump_to_campaign_(false, false, -1, "", ""),
122  jump_to_editor_(false),
123  load_data_()
124 {
125  bool no_music = false;
126  bool no_sound = false;
127 
128  // The path can be hardcoded and it might be a relative path.
129  if(!game_config::path.empty() &&
130 #ifdef _WIN32
131  // use c_str to ensure that index 1 points to valid element since c_str() returns null-terminated string
132  game_config::path.c_str()[1] != ':'
133 #else
134  game_config::path[0] != '/'
135 #endif
136  )
137  {
139  // font_manager_.update_font_path()
140  // To update the font path, destroy and recreate the manager
142  new (&font_manager_) font::manager();
143  }
144 
145  const std::string app_basename = filesystem::base_name(appname);
146  jump_to_editor_ = app_basename.find("editor") != std::string::npos;
147 
148  if (cmdline_opts_.core_id) {
150  }
151  if (cmdline_opts_.campaign) {
152  jump_to_campaign_.jump_ = true;
154  std::cerr << "selected campaign id: [" << jump_to_campaign_.campaign_id_ << "]\n";
155 
158  std::cerr << "selected difficulty: [" << jump_to_campaign_.difficulty_ << "]\n";
159  }
160  else
161  jump_to_campaign_.difficulty_ = -1; // let the user choose the difficulty
162 
165  std::cerr << "selected scenario id: [" << jump_to_campaign_.scenario_id_ << "]\n";
166  }
167 
170  }
171  }
172  if (cmdline_opts_.clock)
174  if (cmdline_opts_.debug) {
176  game_config::mp_debug = true;
177  }
178 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
179  if (cmdline_opts_.debug_dot_domain)
180  gui2::debug_layout_graph::set_domain (*cmdline_opts_.debug_dot_domain);
181  if (cmdline_opts_.debug_dot_level)
183 #endif
184  if (cmdline_opts_.editor)
185  {
186  jump_to_editor_ = true;
187  if (!cmdline_opts_.editor->empty())
188  load_data_.reset(new savegame::load_game_metadata{ savegame::save_index_class::default_saves_dir(), *cmdline_opts_.editor });
189  }
190  if (cmdline_opts_.fps)
193  video().set_fullscreen(true);
194  if (cmdline_opts_.load)
196  if (cmdline_opts_.max_fps) {
197  int fps = utils::clamp(*cmdline_opts_.max_fps, 1, 1000);
198  fps = 1000 / fps;
199  // increase the delay to avoid going above the maximum
200  if(1000 % fps != 0) {
201  ++fps;
202  }
204  }
206  no_sound = true;
208  }
210  gui2::new_widgets = true;
212  game_config::no_delay = true;
214  no_music = true;
216  no_sound = true;
218  const int xres = std::get<0>(*cmdline_opts_.resolution);
219  const int yres = std::get<1>(*cmdline_opts_.resolution);
220  if(xres > 0 && yres > 0) {
221  preferences::_set_resolution(point(xres, yres));
223  }
224  }
226  //TODO it could be simplified to use cmdline_opts_ directly if there is no other way to enter screenshot mode
229  no_sound = true;
231  }
232  if (cmdline_opts_.server){
233  jump_to_multiplayer_ = true;
234  //Do we have any server specified ?
235  if (!cmdline_opts_.server->empty())
237  else //Pick the first server in config
238  {
241  else
242  multiplayer_server_ = "";
243  }
244  if (cmdline_opts_.username) {
247  if (cmdline_opts_.password) {
249  preferences::set_password(*cmdline_opts.server, *cmdline_opts.username, *cmdline_opts_.password);
250  }
251  }
252  }
253  if (cmdline_opts_.test)
254  {
255  if (!cmdline_opts_.test->empty())
257  }
258  if (!cmdline_opts_.unit_test.empty())
259  {
261  }
263  video().set_fullscreen(false);
265  load_data_->show_replay = true;
268 
269  std::cerr
270  << "\nData directory: " << filesystem::sanitize_path(game_config::path)
271  << "\nUser configuration directory: " << filesystem::sanitize_path(filesystem::get_user_config_dir())
272  << "\nUser data directory: " << filesystem::sanitize_path(filesystem::get_user_data_dir())
273  << "\nCache directory: " << filesystem::sanitize_path(filesystem::get_cache_dir())
274  << '\n';
275  std::cerr << '\n';
276 
277  // disable sound in nosound mode, or when sound engine failed to initialize
278  if (no_sound || ((preferences::sound_on() || preferences::music_on() ||
280  !sound::init_sound())) {
281  preferences::set_sound(false);
282  preferences::set_music(false);
285  }
286  else if (no_music) { // else disable the music in nomusic mode
287  preferences::set_music(false);
288  }
289 }
290 
292 {
293  if(!::load_language_list())
294  return false;
295 
296  language_def locale;
297  if(cmdline_opts_.language) {
298  std::vector<language_def> langs = get_languages();
299  for(const language_def & def : langs) {
300  if(def.localename == *cmdline_opts_.language) {
301  locale = def;
302  break;
303  }
304  }
305  if(locale.localename.empty()) {
306  std::cerr << "Language symbol '" << *cmdline_opts_.language << "' not found.\n";
307  return false;
308  }
309  } else {
310  locale = get_locale();
311  }
312  ::set_language(locale);
313 
314  return true;
315 }
316 
318 {
319  // Handle special commandline launch flags
322  std::cerr << "--nogui flag is only valid with --multiplayer or --screenshot or --plugin flags\n";
323  return false;
324  }
325  video().make_fake();
326  game_config::no_delay = true;
327  return true;
328  }
329 
330  // Initialize a new window
331  video().init_window();
332 
333  // Set window title and icon
335 
336 #if !(defined(__APPLE__))
337  surface icon(image::get_image("icons/icon-game.png", image::UNSCALED));
338  if(icon != nullptr) {
339 
340  video().set_window_icon(icon);
341  }
342 #endif
343  return true;
344 }
345 
347 {
348  bool error = false;
349 
350  std::cerr << "Checking lua scripts... ";
351 
353  plugins_manager::get()->get_kernel_base()->load_package(); //load the "package" package, so that scripts can get what packages they want
354  }
355 
356  // get the application lua kernel, load and execute script file, if script file is present
358  {
360 
361  if (!sf->fail()) {
362  /* Cancel all "jumps" to editor / campaign / multiplayer */
363  jump_to_multiplayer_ = false;
364  jump_to_editor_ = false;
365  jump_to_campaign_.jump_ = false;
366 
367  std::string full_script((std::istreambuf_iterator<char>(*sf)), std::istreambuf_iterator<char>());
368 
369  std::cerr << "\nRunning lua script: " << *cmdline_opts_.script_file << std::endl;
370 
372  } else {
373  std::cerr << "Encountered failure when opening script '" << *cmdline_opts_.script_file << "'\n";
374  error = true;
375  }
376  }
377 
379  {
381 
382  std::cerr << "Loading a plugin file'" << filename << "'...\n";
383 
385 
386  try {
387  if (sf->fail()) {
388  throw std::runtime_error("failed to open plugin file");
389  }
390 
391  /* Cancel all "jumps" to editor / campaign / multiplayer */
392  jump_to_multiplayer_ = false;
393  jump_to_editor_ = false;
394  jump_to_campaign_.jump_ = false;
395 
396  std::string full_plugin((std::istreambuf_iterator<char>(*sf)), std::istreambuf_iterator<char>());
397 
399 
400  std::size_t i = pm.add_plugin(filename, full_plugin);
401 
402  for (std::size_t j = 0 ; j < pm.size(); ++j) {
403  std::cerr << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j) << std::endl;
404  }
405 
406  std::cerr << "Starting a plugin...\n";
407  pm.start_plugin(i);
408 
409  for (std::size_t j = 0 ; j < pm.size(); ++j) {
410  std::cerr << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j) << std::endl;
411  }
412 
413  plugins_context pc("init");
414 
415  for (std::size_t repeat = 0; repeat < 5; ++repeat) {
416  std::cerr << "Playing a slice...\n";
417  pc.play_slice();
418 
419  for (std::size_t j = 0 ; j < pm.size(); ++j) {
420  std::cerr << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j) << std::endl;
421  }
422  }
423 
424  return true;
425  } catch (const std::exception & e) {
426  gui2::show_error_message(std::string("When loading a plugin, error:\n") + e.what());
427  error = true;
428  }
429  }
430 
431  if (!error) {
432  std::cerr << "ok\n";
433  }
434 
435  return !error;
436 }
437 
439 {
440  state_.clear();
441  state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::TEST;
443  state_.classification().era_id = "era_default";
444 
445 
447  config {"next_scenario", id}
448  );
449 }
450 
452 {
453  // This first_time variable was added in 70f3c80a3e2 so that using the GUI
454  // menu to load a game works. That seems to have edge-cases, for example if
455  // you try to load a game a second time then Wesnoth exits.
456  static bool first_time = true;
457 
458  if(!cmdline_opts_.test) {
459  return true;
460  }
461  if(!first_time)
462  return false;
463 
464  first_time = false;
465 
466  if(test_scenarios_.size() == 0) {
467  // shouldn't happen, as test_scenarios_ is initialised to {"test"}
468  std::cerr << "Error in the test handling code" << std::endl;
469  return false;
470  }
471  if(test_scenarios_.size() > 1) {
472  std::cerr << "You can't run more than one unit test in interactive mode" << std::endl;
473  }
474  set_test(test_scenarios_.at(0));
475 
477  load_game_config_for_game(state_.classification(), state_.get_scenario_id());
478 
479  try {
480  campaign_controller ccontroller(state_, game_config_manager::get()->terrain_types());
481  ccontroller.play_game();
482  } catch(const savegame::load_game_exception &e) {
483  load_data_.reset(new savegame::load_game_metadata(std::move(e.data_)));
484  return true;
485  }
486 
487  return false;
488 }
489 
490 /**
491  * Runs unit tests specified on the command line.
492  *
493  * If multiple unit tests were specified, then this will stop at the first test
494  * which returns a non-zero status.
495  */
496 // Same as play_test except that we return the results of play_game.
497 // \todo "same ... except" ... and many other changes, such as testing the replay
499 {
500  // There's no copy of play_test's first_time variable. That seems to be for handling
501  // the player loading a game via the GUI, which makes no sense in a non-interactive test.
502  if(cmdline_opts_.unit_test.empty()) {
504  }
505 
506  auto ret = unit_test_result::TEST_FAIL; // will only be returned if no test is run
507  for(const auto& scenario : test_scenarios_) {
508  set_test(scenario);
509  ret = single_unit_test();
510  const char* describe_result;
511  switch(ret) {
513  describe_result = "PASS TEST";
514  break;
516  describe_result = "FAIL TEST (INVALID REPLAY)";
517  break;
519  describe_result = "FAIL TEST (ERRORED REPLAY)";
520  break;
522  describe_result = "FAIL TEST (BROKE STRICT)";
523  break;
525  describe_result = "FAIL TEST (WML EXCEPTION)";
526  break;
528  describe_result = "FAIL TEST (DEFEAT)";
529  break;
531  describe_result = "PASS TEST (VICTORY)";
532  break;
533  default:
534  describe_result = "FAIL TEST";
535  break;
536  }
537  std::cerr << describe_result << ": " << scenario << std::endl;
538  if (ret != unit_test_result::TEST_PASS) {
539  break;
540  }
541  }
542  return ret;
543 }
544 
546 {
548  load_game_config_for_game(state_.classification(), state_.get_scenario_id());
549 
550  LEVEL_RESULT game_res = LEVEL_RESULT::TEST_FAIL;
551  try {
552  campaign_controller ccontroller(state_, game_config_manager::get()->terrain_types(), true);
553  game_res = ccontroller.play_game();
554  // TODO: How to handle the case where a unit test scenario ends without an explicit {SUCCEED} or {FAIL}?
555  // ex: check_victory_never_ai_fail results in victory by killing one side's leaders
556  if(game_res == LEVEL_RESULT::TEST_FAIL) {
558  }
559  if (lg::broke_strict()) {
561  }
562  } catch(const wml_exception& e) {
563  std::cerr << "Caught WML Exception:" << e.dev_message << std::endl;
565  }
566 
568 
570  return pass_victory_or_defeat(game_res);
571  }
572 
574  save.save_game_automatic(false, "unit_test_replay");
575 
577 
578  if (!load_game()) {
579  std::cerr << "Failed to load the replay!" << std::endl;
580  return unit_test_result::TEST_FAIL_LOADING_REPLAY; //failed to load replay
581  }
582 
583  try {
584  campaign_controller ccontroller(state_, game_config_manager::get()->terrain_types(), true);
585  ccontroller.play_replay();
586  if (lg::broke_strict()) {
587  std::cerr << "Observed failure on replay" << std::endl;
589  }
590  } catch(const wml_exception& e) {
591  std::cerr << "WML Exception while playing replay: " << e.dev_message << std::endl;
593  }
594 
595  return pass_victory_or_defeat(game_res);
596 }
597 
599  if(res == LEVEL_RESULT::DEFEAT) {
601  } else if(res == LEVEL_RESULT::VICTORY) {
603  }
605 }
606 
608 {
610  return true;
611  }
612 
614 
616 
618  return false;
619 }
620 
622 {
624  return true;
625  }
626 
627  state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::MULTIPLAYER;
628  DBG_GENERAL << "Current campaign type: " << state_.classification().campaign_type << std::endl;
629 
630  try {
632  load_game_config_for_game(state_.classification(), state_.get_scenario_id());
633  } catch(const config::error& e) {
634  std::cerr << "Error loading game config: " << e.what() << std::endl;
635  return false;
636  }
637 
638  // A default output filename
639  std::string outfile = "wesnoth_image.bmp";
640 
641  // If a output path was given as an argument, use that instead
643  outfile = *cmdline_opts_.render_image_dst;
644  }
645 
647  exit(1);
648  }
649 
650  return false;
651 }
652 
654 {
655  return !!load_data_;
656 }
657 
659 {
660  assert(game_config_manager::get());
661 
662  DBG_GENERAL << "Current campaign type: " << state_.classification().campaign_type << std::endl;
663 
665  if (load_data_) {
666  std::unique_ptr<savegame::load_game_metadata> load_data = std::move(load_data_);
667  load.data() = std::move(*load_data);
668  }
669 
670  try {
671  if(!load.load_game()) {
672  return false;
673  }
674 
675  load.set_gamestate();
676  try {
678  load_game_config_for_game(state_.classification(), state_.get_scenario_id());
679  } catch(const config::error&) {
680  return false;
681  }
682 
683 
684  } catch(const config::error& e) {
685  if(e.message.empty()) {
686  gui2::show_error_message(_("The file you have tried to load is corrupt"));
687  }
688  else {
689  gui2::show_error_message(_("The file you have tried to load is corrupt: '") + e.message + '\'');
690  }
691  return false;
692  } catch(const wml_exception& e) {
693  e.show();
694  return false;
695  } catch(const filesystem::io_exception& e) {
696  if(e.message.empty()) {
697  gui2::show_error_message(_("File I/O Error while reading the game"));
698  } else {
699  gui2::show_error_message(_("File I/O Error while reading the game: '") + e.message + '\'');
700  }
701  return false;
702  } catch(const game::error& e) {
703  if(e.message.empty()) {
704  gui2::show_error_message(_("The file you have tried to load is corrupt"));
705  }
706  else {
707  gui2::show_error_message(_("The file you have tried to load is corrupt: '") + e.message + '\'');
708  }
709  return false;
710  }
711 
712  play_replay_ = load.data().show_replay;
713  LOG_CONFIG << "is middle game savefile: " << (state_.is_mid_game_save() ? "yes" : "no") << "\n";
714  LOG_CONFIG << "show replay: " << (play_replay_ ? "yes" : "no") << "\n";
715  // in case load.data().show_replay && !state_.is_mid_game_save()
716  // there won't be any turns to replay, but the
717  // user gets to watch the intro sequence again ...
718 
719  if(state_.is_mid_game_save() && load.data().show_replay)
720  {
722  }
723 
724  if(state_.classification().campaign_type == game_classification::CAMPAIGN_TYPE::MULTIPLAYER) {
726  }
727 
728  if (load.data().cancel_orders) {
730  }
731 
732  return true;
733 }
734 
736 {
737  state_.clear();
738  state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::TUTORIAL;
739  state_.classification().campaign_define = "TUTORIAL";
740  state_.classification().campaign = "Tutorial";
741  state_.classification().era_id = "era_default";
742 
744  config {"next_scenario", "tutorial"}
745  );
746 
747 }
748 
749 void game_launcher::mark_completed_campaigns(std::vector<config> &campaigns)
750 {
751  for (config &campaign : campaigns) {
752  campaign["completed"] = preferences::is_campaign_completed(campaign["id"]);
753  }
754 }
755 
757 {
758  state_.clear();
760  play_replay_ = false;
761 
763 }
764 
766 {
768 }
769 
771 {
773  if(new_campaign()) {
775  jump_to_campaign_.jump_ = false;
777  }else{
778  jump_to_campaign_.jump_ = false;
779  return false;
780  }
781  }
782  return true;
783 }
784 
786 {
788  jump_to_multiplayer_ = false;
790  ;
791  }else{
792  return false;
793  }
794  }
795  return true;
796 }
797 
799 {
800  if(jump_to_editor_){
801  jump_to_editor_ = false;
802  std::unique_ptr<savegame::load_game_metadata> load_data = std::move(load_data_);
803  if (start_editor(filesystem::normalize_path(load_data ? load_data->filename : "")) ==
805  {
806  return false;
807  }
808  }
809  return true;
810 }
811 
813 {
814  const std::string wesnothd_program =
817 
818  std::string config = filesystem::get_user_config_dir() + "/lan_server.cfg";
819  if (!filesystem::file_exists(config)) {
820  // copy file if it isn't created yet
822  }
823 
824 #ifndef _WIN32
825  std::string command = "\"" + wesnothd_program +"\" -c \"" + config + "\" -d -t 2 -T 5";
826 #else
827  // start wesnoth as background job
828  std::string command = "cmd /C start \"wesnoth server\" /B \"" + wesnothd_program + "\" -c \"" + config + "\" -t 2 -T 5";
829  // Make sure wesnothd's console output is visible on the console window by
830  // disabling SDL's stdio redirection code for this and future child
831  // processes. No need to bother cleaning this up because it's only
832  // meaningful to SDL applications during pre-main initialization.
833  SetEnvironmentVariableA("SDL_STDIO_REDIRECT", "0");
834 #endif
835  LOG_GENERAL << "Starting wesnothd: "<< command << "\n";
836  if (std::system(command.c_str()) == 0) {
837  // Give server a moment to start up
838  SDL_Delay(50);
839  return;
840  }
842 
843  // Couldn't start server so throw error
844  WRN_GENERAL << "Failed to run server start script" << std::endl;
845  throw game::mp_server_error("Starting MP server failed!");
846 }
847 
849 {
850  state_.clear();
851  state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::MULTIPLAYER;
852 
853  try {
854  if (res == MP_HOST)
855  {
856  try {
857  start_wesnothd();
858  } catch(const game::mp_server_error&)
859  {
861 
862  try {
863  start_wesnothd();
864  } catch(const game::mp_server_error&)
865  {
866  return false;
867  }
868  }
869 
870 
871  }
872 
873  // If a server address wasn't specified, prompt for it now.
874  if(res != MP_LOCAL && multiplayer_server_.empty()) {
876  return false;
877  }
878 
879  // The prompt saves its input to preferences.
881 
882  if(multiplayer_server_ != preferences::builtin_servers_list().front().address) {
884  }
885  }
886 
887  //create_engine already calls game_config_manager::get()->load_config but maybe its better to have MULTIPLAYER defined while we are in the lobby.
889 
890  events::discard_input(); // prevent the "keylogger" effect
892 
893  if(res == MP_LOCAL) {
896  } else {
899  multiplayer_server_.clear();
900  }
901 
902  } catch(const wesnothd_rejected_client_error& e) {
904  } catch(const game::mp_server_error& e) {
905  gui2::show_error_message(_("Error while starting server: ") + e.message);
906  } catch(const game::load_game_failed& e) {
907  gui2::show_error_message(_("The game could not be loaded: ") + e.message);
908  } catch(const game::game_error& e) {
909  gui2::show_error_message(_("Error while playing the game: ") + e.message);
910  } catch (const mapgen_exception& e) {
911  gui2::show_error_message(_("Map generator error: ") + e.message);
912  } catch(const wesnothd_error& e) {
913  if(!e.message.empty()) {
914  ERR_NET << "caught network error: " << e.message << std::endl;
915 
916  std::string user_msg;
917  auto conn_err = dynamic_cast<const wesnothd_connection_error*>(&e);
918 
919  if(conn_err) {
920  // The wesnothd_connection_error subclass is only thrown with messages
921  // from boost::system::error_code which we can't translate ourselves.
922  // It's also the originator of the infamous EOF error that happens when
923  // the server dies. <https://github.com/wesnoth/wesnoth/issues/3005>. It
924  // will provide a translated string instead of that when it happens.
925  user_msg = !conn_err->user_message.empty()
926  ? conn_err->user_message
927  : _("Connection failed: ") + e.message;
928  } else {
929  // This will be a message from the server itself, which we can
930  // probably translate.
931  user_msg = translation::gettext(e.message.c_str());
932  }
933 
934  gui2::show_error_message(user_msg);
935  } else {
936  ERR_NET << "caught network error" << std::endl;
937  }
938  } catch(const config::error& e) {
939  if(!e.message.empty()) {
940  ERR_CONFIG << "caught config::error: " << e.message << std::endl;
942  } else {
943  ERR_CONFIG << "caught config::error" << std::endl;
944  }
945  } catch(const incorrect_map_format_error& e) {
946  gui2::show_error_message(_("The game map could not be loaded: ") + e.message);
947  } catch (const savegame::load_game_exception & e) {
948  load_data_.reset(new savegame::load_game_metadata(std::move(e.data_)));
949  //this will make it so next time through the title screen loop, this game is loaded
950  } catch(const wml_exception& e) {
951  e.show();
952  } catch (const game::error & e) {
953  std::cerr << "caught game::error...\n";
954  gui2::show_error_message(_("Error: ") + e.message);
955  }
956 
957  return false;
958 }
959 
961 {
963  return true;
964  }
965 
966  DBG_MP << "starting multiplayer game from the commandline" << std::endl;
967 
968  // These are all the relevant lines taken literally from play_multiplayer() above
969  state_.clear();
970  state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::MULTIPLAYER;
971 
973  load_game_config_for_create(true);
974 
975  events::discard_input(); // prevent the "keylogger" effect
977 
980 
981  return false;
982 }
983 
985 {
987  return false;
988  }
989 
992  }
993 
994  return true;
995 }
996 
998 {
1000 }
1001 
1003 {
1004  assert(!load_data_);
1005  if(play_replay_)
1006  {
1007  play_replay();
1008  return;
1009  }
1010 
1011  gui2::dialogs::loading_screen::display([this, reload]() {
1012 
1014  if(reload == RELOAD_DATA) {
1015  try {
1017  load_game_config_for_game(state_.classification(), state_.get_scenario_id());
1018  } catch(const config::error&) {
1019  return;
1020  }
1021  }
1022  });
1023 
1024  try {
1025  campaign_controller ccontroller(state_, game_config_manager::get()->terrain_types());
1026  LEVEL_RESULT result = ccontroller.play_game();
1027  ai::manager::singleton_ = nullptr;
1028  // don't show The End for multiplayer scenario
1029  // change this if MP campaigns are implemented
1030  if(result == LEVEL_RESULT::VICTORY && !state_.classification().is_normal_mp_game()) {
1032 
1033  gui2::dialogs::outro::display(state_.classification().end_text, state_.classification().end_text_duration);
1034 
1036  gui2::dialogs::end_credits::display(state_.classification().campaign);
1037  }
1038  }
1039  } catch (const savegame::load_game_exception &e) {
1040  load_data_.reset(new savegame::load_game_metadata(std::move(e.data_)));
1041  //this will make it so next time through the title screen loop, this game is loaded
1042  } catch(const wml_exception& e) {
1043  e.show();
1044  } catch(const mapgen_exception& e) {
1045  gui2::show_error_message(_("Map generator error: ") + e.message);
1046  }
1047 }
1048 
1050 {
1051  assert(!load_data_);
1052  try {
1053  campaign_controller ccontroller(state_, game_config_manager::get()->terrain_types());
1054  ccontroller.play_replay();
1055  } catch (const savegame::load_game_exception &e) {
1056  load_data_.reset(new savegame::load_game_metadata(std::move(e.data_)));
1057  //this will make it so next time through the title screen loop, this game is loaded
1058  } catch(const wml_exception& e) {
1059  e.show();
1060  }
1061 }
1062 
1064 {
1065  while(true){
1067 
1069 
1070  editor::EXIT_STATUS res = editor::start(filename);
1071 
1072  if(res != editor::EXIT_RELOAD_DATA)
1073  return res;
1074 
1076  }
1077  return editor::EXIT_ERROR; // not supposed to happen
1078 }
1079 
1081 {
1082  load_data_.reset();
1083 }
1084 
1086 {
1087  try {
1089  } catch (...) {}
1090 }
boost::optional< std::string > core_id
Non-empty if –core was given on the command line. Chooses the core to be loaded. ...
constexpr const T & clamp(const T &value, const T &min, const T &max)
Definition: general.hpp:31
an error occurred inside the underlying network communication code (boost asio) TODO: find a short na...
CVideo & video()
boost::optional< std::string > script_file
File to load lua script from.
An error occurred during when trying to coommunicate with the wesnothd server.
bool new_widgets
Do we wish to use the new library or not.
Definition: settings.cpp:24
surface get_image(const image::locator &i_locator, TYPE type)
function to get the surface corresponding to an image.
Definition: picture.cpp:833
unsigned int end_text_duration
for how long the end-of-campaign text is shown
void set_window_icon(surface &icon)
Sets the icon of the main window.
Definition: video.cpp:360
bool set_sound(bool ison)
Definition: general.cpp:674
void close_sound()
Definition: sound.cpp:494
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:167
static lg::log_domain log_mp_create("mp/create")
bool turn_bell()
Definition: general.cpp:616
void discard_input()
Discards all input events.
Definition: events.cpp:853
bool end_credits
whether to show the standard credits at the end
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:95
bool nogui
True if –nogui was given on the command line. Disables GUI.
The class for loading a savefile.
Definition: savegame.hpp:109
bool script_unsafe_mode
Whether to load the "package" package for the scripting environment. (This allows to load arbitrary l...
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:175
void _set_maximized(bool ison)
Definition: general.cpp:406
std::string label
Name of the game (e.g.
void disable_preferences_save()
Definition: general.cpp:247
std::string filename
Name of the savefile to be loaded (not including the directory).
Definition: savegame.hpp:58
void set_gamestate()
Generate the gamestate out of the loaded game config.
Definition: savegame.cpp:276
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:35
Add a special kind of assert to validate whether the input from WML doesn&#39;t contain any problems that...
#define DBG_MP
const std::string & filename() const
Definition: savegame.hpp:187
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
Error used when game loading fails.
Definition: game_errors.hpp:30
void show_wesnothd_server_search()
Definition: display.cpp:150
void show_preferences()
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:188
Definition: video.hpp:31
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:267
bool is_campaign_completed(const std::string &campaign_id)
Definition: game.cpp:332
std::string network_host()
Definition: game.cpp:413
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:703
const language_def & get_locale()
Definition: language.cpp:318
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
Definition: filesystem.cpp:993
void _set_resolution(const point &res)
Definition: general.cpp:400
void set_language(const std::string &language, const std::vector< std::string > *)
Definition: gettext.cpp:448
boost::optional< std::string > username
Non-empty if –username was given on the command line. Forces Wesnoth to use this network username...
void mark_completed_campaigns(std::vector< config > &campaigns)
save_result save_image(const locator &i_locator, const std::string &filename)
Definition: picture.cpp:1101
boost::optional< std::string > campaign_scenario
Non-empty if –campaign-scenario was given on the command line. Chooses starting scenario in the camp...
std::string get_default_title_string()
void set_min_translation_percent(int percent)
Definition: language.cpp:133
void clear()
Definition: saved_game.cpp:793
boost::optional< int > max_fps
Max FPS specified by –max-fps option.
bool fps
True if –fps was given on the command line. Shows number of fps.
std::vector< std::string > unit_test
Non-empty if –unit was given on the command line. Goes directly into unit test mode, into a scenario, if specified.
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:423
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. Dependent on –unit.
void init_textdomains(const game_config_view &cfg)
Initializes the list of textdomains from a configuration object.
Definition: language.cpp:356
std::string jump_to_campaign_id() const
Return the ID of the campaign to jump to (skipping the main menu).
-file util.hpp
Definitions for the interface to Wesnoth Markup Language (WML).
load_game_metadata & data()
Definition: savegame.hpp:128
boost::optional< std::string > plugin_file
File to load a lua plugin (similar to a script) from. Experimental / may replace script.
bool sound_on()
Definition: general.cpp:669
#define DBG_GENERAL
load_game_metadata data_
Definition: savegame.hpp:102
std::string user_message
User-friendly and potentially translated message for use in the UI.
jump_to_campaign_info jump_to_campaign_
boost::optional< std::string > load
Non-empty if –load was given on the command line. Savegame specified to load after start...
bool init_sound()
Definition: sound.cpp:442
static lg::log_domain log_config("config")
bool broke_strict()
Definition: log.cpp:171
bool play_multiplayer(mp_selection res)
std::vector< std::string > test_scenarios_
std::string get_cwd()
Definition: filesystem.cpp:882
bool with_replay
True if –with-replay was given on the command line. Shows replay of the loaded file.
void set_mp_server_program_name(const std::string &path)
Definition: game.cpp:535
void set_window_title(const std::string &title)
Sets the title of the main window.
Definition: video.cpp:354
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()
if(len==0) return stream_ref()
void make_fake()
Definition: video.cpp:177
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:100
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_
const std::vector< game_config::server_info > & builtin_servers_list()
Definition: game.cpp:382
static lg::log_domain log_enginerefac("enginerefac")
void start_plugin(std::size_t idx)
Definition: manager.cpp:101
std::string get_name(std::size_t idx)
Definition: manager.cpp:94
This file contains the settings handling of the widget library.
bool nodelay
True if –nodelay was given on the command line.
std::string get_user_data_dir()
Definition: filesystem.cpp:795
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)
boost::optional< unsigned int > translation_percent
Non-empty if –all-translations or –translations-over is given on the command line.
std::string localename
Definition: language.hpp:54
void load_package()
Loads the package library into lua environment.
bool windowed
True if –windowed was given on the command line. Starts Wesnoth in windowed mode.
boost::optional< std::string > editor
Non-empty if –editor was given on the command line. Goes directly into editor. If string is longer t...
void set_show_fps(bool value)
Definition: general.cpp:841
void start_local_game_commandline(const game_config_view &game_config, saved_game &state, const commandline_options &cmdline_opts)
Starts a multiplayer game in single-user mode.
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:39
void set_test(const std::string &id)
std::string end_text
end-of-campaign text
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:320
std::string path
Definition: game_config.cpp:39
boost::optional< std::string > test
Non-empty if –test was given on the command line. Goes directly into test mode, into a scenario...
bool multiplayer
True if –multiplayer was given on the command line. Goes directly into multiplayer mode...
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands...
bool UI_sound_on()
Definition: general.cpp:640
std::string sanitize_path(const std::string &path)
Sanitizes a path to remove references to the user&#39;s name.
game_launcher(const commandline_options &cmdline_opts, const char *appname)
std::string campaign
The id of the campaign being played.
const char * what() const noexcept
Definition: exceptions.hpp:37
bool clock
True if –clock was given on the command line. Enables.
static UNUSEDNOWARN std::string gettext(const char *str)
Definition: gettext.hpp:67
Error used for any general game error, e.g.
Definition: game_errors.hpp:46
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
Definition: filesystem.cpp:985
std::string screenshot_map_
std::unique_ptr< savegame::load_game_metadata > load_data_
#define LOG_CONFIG
void set_draw_delay(int value)
Definition: general.cpp:851
boost::optional< std::string > render_image
Image path to render. First parameter after –render-image.
void set_level(const std::string &value)
Definition: game.cpp:737
boost::optional< int > campaign_difficulty
Non-empty if –campaign-difficulty was given on the command line. Numerical difficulty of the campaig...
Helper class, don&#39;t construct this directly.
bool nomusic
True if –nomusic was given on the command line. Disables music.
std::string dev_message
The message for developers telling which problem was triggered, this shouldn&#39;t be translated...
std::string get_cache_dir()
Definition: filesystem.cpp:800
Exception used to signal that the user has decided to abortt a game, and to load another game instead...
Definition: savegame.hpp:93
void run(char const *prog, const std::string &name, int nArgs=0)
Runs a plain script.
#define WRN_GENERAL
boost::optional< std::string > password
Non-empty if –password was given on the command line. Forces Wesnoth to use this network password...
bool is_loading() const
bool debug
True if –debug was given on the command line. Enables debug mode.
std::size_t i
Definition: function.cpp:933
std::size_t add_plugin(const std::string &name, const std::string &prog)
Definition: manager.cpp:118
std::string get_mp_server_program_name()
Definition: game.cpp:547
boost::optional< std::string > screenshot_map_file
Map file to make a screenshot of. First parameter given after –screenshot.
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:70
void load_game_config_for_create(bool is_mp, bool is_test=false)
u64 size
Definition: statement.cpp:80
Game configuration data as global variables.
Definition: build_info.cpp:55
bool new_widgets
True if –new-widgets was given on the command line. Hidden option to enable the new widget toolkit...
#define ERR_NET
void init_window()
Initializes a new SDL window instance, taking into account any preiously saved states.
Definition: video.cpp:218
An exception object used when an IO error occurs.
Definition: filesystem.hpp:48
bool music_on()
Definition: general.cpp:692
void add_completed_campaign(const std::string &campaign_id, const std::string &difficulty_level)
Definition: game.cpp:328
bool load_language_list()
Definition: language.cpp:93
std::string get_scenario_id()
Definition: saved_game.cpp:649
language_list get_languages()
Definition: language.cpp:116
void set_fullscreen(bool ison)
Definition: video.cpp:565
Holds a 2D point.
Definition: point.hpp:23
bool set_UI_sound(bool ison)
Definition: general.cpp:645
bool goto_multiplayer()
Declarations for File-IO.
static void save(LexState *ls, int c)
Definition: llex.cpp:57
void launch_game(RELOAD_GAME_DATA reload=RELOAD_DATA)
Class for replay saves (either manually or automatically).
Definition: savegame.hpp:276
-file mapgen.hpp
bool campaign_skip_story
True if –skip-story was given on the command line. Skips [story] and [message]s through the end of t...
std::string difficulty
The difficulty level the game is being played on.
unit_test_result single_unit_test()
Internal to the implementation of unit_test().
boost::optional< std::string > render_image_dst
Output file to put rendered image path in. Optional second parameter after –render-image.
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
void start_client(const game_config_view &game_config, saved_game &state, const std::string &host)
Starts a multiplayer game in client mode.
static manager * singleton_
Definition: manager.hpp:478
std::string get_user_config_dir()
Definition: filesystem.cpp:766
bool screenshot
True if –screenshot was given on the command line. Starts Wesnoth in screenshot mode.
void cancel_orders()
Definition: saved_game.cpp:686
std::string get_detailed_status(std::size_t idx)
Definition: manager.cpp:83
bool show_replay
State of the "show_replay" checkbox in the load-game dialog.
Definition: savegame.hpp:64
static void display(std::function< void()> f)
const commandline_options & cmdline_opts_
Base class for all the errors encountered by the engine.
Definition: exceptions.hpp:29
static void display(const game_config_view &game_cfg, const preferences::PREFERENCE_VIEW initial_view=preferences::VIEW_DEFAULT)
The display function – see modal_dialog for more information.
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_
bool set_music(bool ison)
Definition: general.cpp:697
Standard logging facilities (interface).
void set_skip_story(bool skip_story)
Definition: saved_game.hpp:128
boost::optional< std::string > screenshot_output_file
Output file to put screenshot in. Second parameter given after –screenshot.
bool set_turn_bell(bool ison)
Definition: general.cpp:621
void clear_loaded_game()
std::string message
Definition: exceptions.hpp:31
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:28
bool fullscreen
True if –fullscreen was given on the command line. Starts Wesnoth in fullscreen mode.
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:205
boost::optional< std::string > language
Non-empty if –language was given on the command line. Sets the language for this session...
#define e
bool nosound
True if –nosound was given on the command line. Disables sound.
boost::optional< std::pair< int, int > > resolution
Pair of AxB values specified after –resolution. Changes Wesnoth resolution.
bool play_screenshot_mode()
lua_kernel_base * get_kernel_base()
Definition: manager.cpp:63
boost::optional< std::string > campaign
Non-empty if –campaign was given on the command line. ID of the campaign we want to start...
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
void clear_current_scenario()
Delete the current scenario from the stats.
Definition: statistics.cpp:786
bool enter_create_mode(saved_game &state, jump_to_campaign_info jump_to_campaign)
bool init_lua_script()
std::vector< server_info > server_list
Definition: game_config.cpp:94
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:357
boost::optional< std::string > server
Non-empty if –server was given on the command line. Connects Wesnoth to specified server...
void start_local_game(const game_config_view &game_config, saved_game &state)
Starts a multiplayer game in single-user mode.
bool play_multiplayer_commandline()
void set_debug(bool new_debug)
static plugins_manager * get()
Definition: manager.cpp:58
void play_slice()
Definition: context.cpp:96
#define LOG_GENERAL
bool show_debug_clock_button
Do we wish to show the button for the debug clock.