The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
game_launcher.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2003 - 2025
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, etc
27#include "game_initialization/multiplayer.hpp" // for start_client, etc
28#include "game_initialization/playcampaign.hpp" // for play_game, etc
29#include "game_initialization/singleplayer.hpp" // for sp_create_mode
30#include "generators/map_generator.hpp" // for mapgen_exception
31#include "gettext.hpp" // for _
32#include "gui/dialogs/language_selection.hpp" // for language_selection
34#include "gui/dialogs/message.hpp" // for show error message
36#include "gui/dialogs/title_screen.hpp" // for show_debug_clock_button
37#include "gui/dialogs/transient_message.hpp" // for show_transient_message
38#include "gui/widgets/settings.hpp" // for new_widgets
39#include "language.hpp" // for language_def, etc
40#include "log.hpp" // for LOG_STREAM, logger, general, etc
41#include "map/exception.hpp"
43#include "save_index.hpp"
45#include "sdl/surface.hpp" // for surface
46#include "serialization/compression.hpp" // for format::NONE
47#include "tstring.hpp" // for operator==, operator!=
48#include "video.hpp"
50#include "wml_exception.hpp" // for wml_exception
51
52#ifdef __APPLE__
53#include <TargetConditionals.h>
54#endif
55
56#if BOOST_VERSION >= 108600
57
58// boost::asio (via boost::process) complains about winsock.h otherwise
59#ifdef _WIN32
60#define WIN32_LEAN_AND_MEAN
61#endif
62#include <boost/process/v2/process.hpp>
63
64#else
65
66// process::v1 only. The v1 folders do not exist until 1.86
67#ifdef _WIN32
68#include <boost/process/windows.hpp>
69#endif
70#include <boost/process/child.hpp>
71
72#endif
73
74#include <algorithm> // for copy, max, min, stable_sort
75#include <new>
76#include <thread>
77#include <utility> // for pair
78
79#ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
80#include "gui/widgets/debug.hpp"
81#endif
82
83static 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 ERR_GENERAL LOG_STREAM(err, lg::general())
89#define LOG_GENERAL LOG_STREAM(info, lg::general())
90#define WRN_GENERAL LOG_STREAM(warn, lg::general())
91#define DBG_GENERAL LOG_STREAM(debug, lg::general())
92
93#define LOG_TEST FORCE_LOG_TO(lg::general(), log_config)
94
95static lg::log_domain log_mp_create("mp/create");
96#define DBG_MP LOG_STREAM(debug, log_mp_create)
97
98static lg::log_domain log_network("network");
99#define ERR_NET LOG_STREAM(err, log_network)
100
101static lg::log_domain log_enginerefac("enginerefac");
102#define LOG_RG LOG_STREAM(info, log_enginerefac)
103
105 : cmdline_opts_(cmdline_opts)
106 , config_manager_(cmdline_opts)
107 , font_manager_()
108 , image_manager_()
110 , hotkey_manager_()
111 , music_thinker_()
112 , music_muter_()
113 , test_scenarios_{"test"}
114 , screenshot_map_()
115 , screenshot_filename_()
116 , state_()
117 , play_replay_(false)
118 , multiplayer_server_()
119 , jump_to_multiplayer_(false)
120 , jump_to_campaign_{}
121 , jump_to_editor_()
122 , load_data_()
123{
124 bool no_music = false;
125 bool no_sound = false;
126
128 prefs::get().set_core(*cmdline_opts_.core_id);
129 }
131 jump_to_campaign_.jump = true;
133 PLAIN_LOG << "selected campaign id: [" << jump_to_campaign_.campaign_id << "]";
134
137 PLAIN_LOG << "selected difficulty: [" << jump_to_campaign_.difficulty << "]";
138 } else {
139 jump_to_campaign_.difficulty = -1; // let the user choose the difficulty
140 }
141
144 PLAIN_LOG << "selected scenario id: [" << jump_to_campaign_.scenario_id << "]";
145 }
146
149 }
150 }
153 if(cmdline_opts_.debug) {
156 }
157#ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
158 if(cmdline_opts_.debug_dot_domain)
159 gui2::debug_layout_graph::set_domain(*cmdline_opts_.debug_dot_domain);
160 if(cmdline_opts_.debug_dot_level)
161 gui2::debug_layout_graph::set_level(*cmdline_opts_.debug_dot_level);
162#endif
166 prefs::get().set_show_fps(true);
168 prefs::get().set_fullscreen(true);
169 if(cmdline_opts_.load) {
170#ifdef __cpp_aggregate_paren_init
171 load_data_.emplace(
173#else
176#endif
177 try {
178 load_data_->read_file();
179 } catch(const game::load_game_failed&) {
180 load_data_.reset();
181 }
182 }
184 prefs::get().set_refresh_rate(std::clamp(*cmdline_opts_.max_fps, 1, 1000));
185 }
187 no_sound = true;
189 }
191 gui2::new_widgets = true;
193 no_music = true;
195 no_sound = true;
197 const int xres = std::get<0>(*cmdline_opts_.resolution);
198 const int yres = std::get<1>(*cmdline_opts_.resolution);
199 if(xres > 0 && yres > 0) {
200 prefs::get().set_resolution(point(xres, yres));
201 prefs::get().set_maximized(false);
202 }
203 }
205 // TODO it could be simplified to use cmdline_opts_ directly if there is no other way to enter screenshot mode
208 no_sound = true;
210 }
213 // Do we have any server specified ?
214 if(!cmdline_opts_.server->empty()) {
216 } else {
217 // Pick the first server in config
218 if(game_config::server_list.size() > 0) {
220 } else {
222 }
223 }
229 prefs::get().set_password(*cmdline_opts.server, *cmdline_opts.username, *cmdline_opts_.password);
230 }
231 }
232 }
233 if(cmdline_opts_.test) {
234 if(!cmdline_opts_.test->empty()) {
236 }
237 }
238 if(!cmdline_opts_.unit_test.empty()) {
240 }
242 prefs::get().set_fullscreen(false);
244 load_data_->show_replay = true;
247
248 if(!cmdline_opts.nobanner) {
250 << "\nGame data: " << game_config::path
251 << "\nUser data: " << filesystem::get_user_data_dir()
252 << "\nCache: " << filesystem::get_cache_dir()
253 << "\n";
254 }
255
256 // disable sound in nosound mode, or when sound engine failed to initialize
257 if(no_sound || !sound::init_sound()) {
258 prefs::get().set_sound(false);
259 prefs::get().set_music(false);
260 prefs::get().set_turn_bell(false);
261 prefs::get().set_ui_sound(false);
262 } else if(no_music) { // else disable the music in nomusic mode
263 prefs::get().set_music(false);
264 }
265}
266
268{
269 if(!::load_language_list()) {
270 return false;
271 }
272
273 language_def locale;
275 std::vector<language_def> langs = get_languages(true);
276 for(const language_def& def : langs) {
277 if(def.localename == *cmdline_opts_.language) {
278 locale = def;
279 break;
280 }
281 }
282 if(locale.localename.empty()) {
283 PLAIN_LOG << "Language symbol '" << *cmdline_opts_.language << "' not found.";
284 return false;
285 }
286 } else {
287 locale = get_locale();
288 }
289 ::set_language(locale);
290
291 return true;
292}
293
295{
296 // Handle special commandline launch flags
301 {
307 {
308 PLAIN_LOG << "--nogui flag is only valid with --multiplayer or --screenshot or --plugin flags";
309 return false;
310 }
312 // Screenshots require a rendering context, and thus a window,
313 // so we create one but hidden.
315 } else {
316 // Other functions don't require a window at all.
318 }
319 return true;
320 }
321
322 // Initialize video subsystem, and create a new window.
323 video::init();
324
325 // Set window title and icon
327
328#if !(defined(__APPLE__))
329 surface icon(image::get_surface(image::locator{"icons/icon-game.png"}, image::UNSCALED));
330 if(icon != nullptr) {
332 }
333#endif
334
335#ifndef __ANDROID__
336 SDL_StartTextInput(video::get_window());
337#endif
338
339 return true;
340}
341
343{
344 bool error = false;
345
347 STREAMING_LOG << "Checking lua scripts... ";
348 }
349
351 // load the "package" package, so that scripts can get what packages they want
353 }
354
356 std::string filename = *cmdline_opts_.plugin_file;
357
358 PLAIN_LOG << "Loading a plugin file'" << filename << "'...";
359
361
362 try {
363 if(sf->fail()) {
364 throw std::runtime_error("failed to open plugin file");
365 }
366
367 /* Cancel all "jumps" to editor / campaign / multiplayer */
368 jump_to_multiplayer_ = false;
369 jump_to_editor_ = utils::nullopt;
370 jump_to_campaign_.jump = false;
371
372 std::string full_plugin((std::istreambuf_iterator<char>(*sf)), std::istreambuf_iterator<char>());
373
375
376 std::size_t i = pm.add_plugin(filename, full_plugin);
377
378 for(std::size_t j = 0; j < pm.size(); ++j) {
379 PLAIN_LOG << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j);
380 }
381
382 PLAIN_LOG << "Starting a plugin...";
383 pm.start_plugin(i);
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 plugins_context pc("init");
390
391 for(std::size_t repeat = 0; repeat < 5; ++repeat) {
392 PLAIN_LOG << "Playing a slice...";
393 pc.play_slice();
394
395 for(std::size_t j = 0; j < pm.size(); ++j) {
396 PLAIN_LOG << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j);
397 }
398 }
399
400 return true;
401 } catch(const std::exception& e) {
402 gui2::show_error_message(std::string("When loading a plugin, error:\n") + e.what());
403 error = true;
404 }
405 }
406
407 if(!error && !cmdline_opts_.nobanner) {
408 PLAIN_LOG << "ok";
409 }
410
411 return !error;
412}
413
414void game_launcher::set_test(const std::string& id)
415{
416 state_.clear();
417 state_.classification().type = campaign_type::type::test;
419 state_.classification().era_id = "era_default";
420
421 state_.set_carryover_sides_start(config{"next_scenario", id});
422}
423
425{
426 // This first_time variable was added in 70f3c80a3e2 so that using the GUI
427 // menu to load a game works. That seems to have edge-cases, for example if
428 // you try to load a game a second time then Wesnoth exits.
429 static bool first_time = true;
430
431 if(!cmdline_opts_.test) {
432 return true;
433 }
434
435 if(!first_time) {
436 return false;
437 }
438
439 first_time = false;
440
441 if(test_scenarios_.size() == 0) {
442 // shouldn't happen, as test_scenarios_ is initialised to {"test"}
443 PLAIN_LOG << "Error in the test handling code";
444 return false;
445 }
446
447 if(test_scenarios_.size() > 1) {
448 PLAIN_LOG << "You can't run more than one unit test in interactive mode";
449 }
450
452
454
455 try {
456 campaign_controller ccontroller(state_);
457 ccontroller.play_game();
459 load_data_ = std::move(e.data_);
460 return true;
461 }
462
463 return false;
464}
465
466/**
467 * Runs unit tests specified on the command line.
468 *
469 * If multiple unit tests were specified, then this will stop at the first test
470 * which returns a non-zero status.
471 */
472// Same as play_test except that we return the results of play_game.
473// \todo "same ... except" ... and many other changes, such as testing the replay
475{
476 // There's no copy of play_test's first_time variable. That seems to be for handling
477 // the player loading a game via the GUI, which makes no sense in a non-interactive test.
478 if(cmdline_opts_.unit_test.empty()) {
480 }
481
482 auto ret = unit_test_result::TEST_FAIL; // will only be returned if no test is run
483 for(const auto& scenario : test_scenarios_) {
484 set_test(scenario);
485 ret = single_unit_test();
486 const char* describe_result;
487 switch(ret) {
489 describe_result = "PASS TEST";
490 break;
492 describe_result = "FAIL TEST (INVALID REPLAY)";
493 break;
495 describe_result = "FAIL TEST (ERRORED REPLAY)";
496 break;
498 describe_result = "FAIL TEST (WML EXCEPTION)";
499 break;
501 describe_result = "FAIL TEST (DEFEAT)";
502 break;
504 describe_result = "PASS TEST (VICTORY)";
505 break;
507 describe_result = "BROKE STRICT (PASS)";
508 break;
510 describe_result = "BROKE STRICT (FAIL)";
511 break;
513 describe_result = "BROKE STRICT (DEFEAT)";
514 break;
516 describe_result = "BROKE STRICT (VICTORY)";
517 break;
518 default:
519 describe_result = "FAIL TEST (UNKNOWN)";
520 break;
521 }
522
523 PLAIN_LOG << describe_result << " (" << int(ret) << "): " << scenario;
524 if(ret != unit_test_result::TEST_PASS) {
525 break;
526 }
527 }
528
529 return ret;
530}
531
533{
535
536 level_result::type game_res = level_result::type::fail;
537 try {
538 campaign_controller ccontroller(state_, true);
539 game_res = ccontroller.play_game();
540 if(game_res == level_result::type::fail) {
541 if(lg::broke_strict()) {
543 } else {
545 }
546 }
547 } catch(const wml_exception& e) {
548 PLAIN_LOG << "Caught WML Exception:" << e.dev_message;
550 }
551
553
555 return pass_victory_or_defeat(game_res);
556 }
557
559 save.save_game_automatic(false, "unit_test_replay");
560
561#ifdef __cpp_aggregate_paren_init
562 load_data_.emplace(
563 savegame::save_index_class::default_saves_dir(), save.filename(), "", true, true, false);
564#else
566 savegame::save_index_class::default_saves_dir(), save.filename(), "", true, true, false};
567#endif
568 load_data_->read_file();
569
570 if(!load_prepared_game()) {
571 PLAIN_LOG << "Failed to load the replay!";
572 return unit_test_result::TEST_FAIL_LOADING_REPLAY; // failed to load replay
573 }
574
575 try {
576 const bool was_strict_broken = lg::broke_strict();
577 campaign_controller ccontroller(state_, true);
578 ccontroller.play_replay();
579 if(!was_strict_broken && lg::broke_strict()) {
580 PLAIN_LOG << "Observed failure on replay";
582 }
583 } catch(const wml_exception& e) {
584 PLAIN_LOG << "WML Exception while playing replay: " << e.dev_message;
586 }
587
588 return pass_victory_or_defeat(game_res);
589}
590
592{
593 if(res == level_result::type::defeat) {
594 if(lg::broke_strict()) {
596 } else {
598 }
599 } else if(res == level_result::type::victory) {
600 if(lg::broke_strict()) {
602 } else {
604 }
605 }
606
607 if(lg::broke_strict()) {
609 } else {
611 }
612}
613
615{
617 return true;
618 }
619
621
623
625 if (res != editor::EXIT_NORMAL) {
626 PLAIN_LOG << "Error while taking screenshot of map: " << screenshot_map_;
627 }
628 return false;
629}
630
632{
634 return true;
635 }
636
637 state_.classification().type = campaign_type::type::multiplayer;
638 DBG_GENERAL << "Current campaign type: " << campaign_type::get_string(state_.classification().type);
639
640 try {
642 } catch(const config::error& e) {
643 PLAIN_LOG << "Error loading game config: " << e.what();
644 return false;
645 }
646
647 // A default output filename
648 std::string outfile = "wesnoth_image.png";
649
650 // If a output path was given as an argument, use that instead
653 }
654
656 exit(1);
657 }
658
659 return false;
660}
661
663{
664 return load_data_.has_value();
665}
666
668{
669 return std::exchange(load_data_, utils::nullopt).value();
670}
671
673{
674 DBG_GENERAL << "Current campaign type: " << campaign_type::get_string(state_.classification().type);
675 assert(!load_data_);
676
677 // FIXME: evaluate which of these damn catch blocks are still necessary
678 try {
680
681 } catch(const config::error& e) {
682 if(e.message.empty()) {
683 gui2::show_error_message(_("The file you have tried to load is corrupt"));
684 } else {
685 gui2::show_error_message(_("The file you have tried to load is corrupt: '") + e.message + '\'');
686 }
687
688 return false;
689 } catch(const wml_exception& e) {
690 e.show();
691 return false;
692 } catch(const filesystem::io_exception& e) {
693 if(e.message.empty()) {
694 gui2::show_error_message(_("File I/O Error while reading the game"));
695 } else {
696 gui2::show_error_message(_("File I/O Error while reading the game: '") + e.message + '\'');
697 }
698
699 return false;
700 } catch(const game::error& e) {
701 if(e.message.empty()) {
702 gui2::show_error_message(_("The file you have tried to load is corrupt"));
703 } else {
704 gui2::show_error_message(_("The file you have tried to load is corrupt: '") + e.message + '\'');
705 }
706
707 return false;
708 }
709
710 // If the player canceled loading, we won't have any load data at this point
711 return has_load_data();
712}
713
715{
718
719 play_replay_ = load_data.show_replay;
720
721 // in case show_replay && 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 if(load_data.show_replay && !state_.is_start_of_scenario()) {
726 }
727
728 if(load_data.cancel_orders) {
730 }
731
732 try {
734 } catch(const config::error&) {
735 return false;
736 }
737
738 return true;
739}
740
742{
743 state_.clear();
744 state_.classification().type = campaign_type::type::scenario;
745 play_replay_ = false;
746
748}
749
751{
753}
754
756 jump_to_campaign_.jump = false;
757 if(new_campaign()) {
760 return true;
761 }
762 return false;
763}
764
766{
768 return play_campaign();
769 }
770
771 return true;
772}
773
775{
777 return true;
778 }
779
780 jump_to_multiplayer_ = false;
782}
783
785{
786 if(jump_to_editor_) {
787 // If no filename was specified, this will be an empty string.
788 const std::string to_open = std::exchange(jump_to_editor_, utils::nullopt).value();
789
791 return false;
792 }
793 }
794
795 return true;
796}
797
799{
800#if defined(__APPLE__) && TARGET_OS_IPHONE
801 throw game::mp_server_error("Starting MP server is not supported on iOS builds.");
802#else
803 std::string wesnothd_program = "";
804 if(!prefs::get().get_mp_server_program_name().empty()) {
805 wesnothd_program = prefs::get().get_mp_server_program_name();
806 } else {
807 wesnothd_program = filesystem::get_wesnothd_name();
808 }
809
810 std::string config = filesystem::get_user_data_dir() + "/lan_server.cfg";
812 // copy file if it isn't created yet
814 }
815
816 LOG_GENERAL << "Starting wesnothd";
817 try
818 {
819#if BOOST_VERSION >= 108600
820 boost::asio::io_context io_context;
821 auto c = boost::process::v2::process{io_context, wesnothd_program, { "-c", config }};
822#else
823#ifndef _WIN32
824 boost::process::child c(wesnothd_program, "-c", config);
825#else
826 boost::process::child c(wesnothd_program, "-c", config, boost::process::windows::create_no_window);
827#endif
828#endif
829 c.detach();
830 // Give server a moment to start up
831 using namespace std::chrono_literals;
832 std::this_thread::sleep_for(50ms);
833 return;
834 }
835#if BOOST_VERSION >= 108600
836 catch(const std::exception& e)
837#else
838 catch(const boost::process::process_error& e)
839#endif
840 {
842
843 // Couldn't start server so throw error
844 WRN_GENERAL << "Failed to start server " << wesnothd_program << ":\n" << e.what();
845 throw game::mp_server_error("Starting MP server failed!");
846 }
847#endif
848}
849
851{
853 try {
854 if(mode == mp_mode::HOST) {
855 try {
857 } catch(const game::mp_server_error&) {
859
860 try {
862 } catch(const game::mp_server_error&) {
863 return false;
864 }
865 }
866 }
867
868 // If a server address wasn't specified, prompt for it now.
869 if(mode != mp_mode::LOCAL && multiplayer_server_.empty()) {
870 if(!gui2::dialogs::mp_connect::execute()) {
871 return false;
872 }
873
874 // The prompt saves its input to preferences.
876
877 if(multiplayer_server_ != prefs::get().builtin_servers_list().front().address) {
879 }
880 }
881
882 // create_engine already calls config_manager_.load_config but maybe its better to have MULTIPLAYER
883 // defined while we are in the lobby.
885
886 events::discard_input(); // prevent the "keylogger" effect
888
889 if(mode == mp_mode::LOCAL) {
891 } else {
893 multiplayer_server_.clear();
894 }
895
896 } catch(const wesnothd_rejected_client_error& e) {
898 } catch(const game::mp_server_error& e) {
899 gui2::show_error_message(_("Error while starting server: ") + e.message);
900 } catch(const game::load_game_failed& e) {
901 gui2::show_error_message(_("The game could not be loaded: ") + e.message);
902 } catch(const game::game_error& e) {
903 gui2::show_error_message(_("Error while playing the game: ") + e.message);
904 } catch(const mapgen_exception& e) {
905 gui2::show_error_message(_("Map generator error: ") + e.message);
906 } catch(const wesnothd_error& e) {
907 if(!e.message.empty()) {
908 ERR_NET << "caught network error: " << e.message;
909
910 std::string user_msg;
911 auto conn_err = dynamic_cast<const wesnothd_connection_error*>(&e);
912
913 if(conn_err) {
914 // The wesnothd_connection_error subclass is only thrown with messages
915 // from boost::system::error_code which we can't translate ourselves.
916 // It's also the originator of the infamous EOF error that happens when
917 // the server dies. <https://github.com/wesnoth/wesnoth/issues/3005>. It
918 // will provide a translated string instead of that when it happens.
919 user_msg = !conn_err->user_message.empty()
920 ? conn_err->user_message
921 : _("Connection failed: ") + e.message;
922 } else {
923 // This will be a message from the server itself, which we can
924 // probably translate.
925 user_msg = translation::gettext(e.message.c_str());
926 }
927
928 gui2::show_error_message(user_msg);
929 } else {
930 ERR_NET << "caught network error";
931 }
932 } catch(const config::error& e) {
933 if(!e.message.empty()) {
934 ERR_CONFIG << "caught config::error: " << e.message;
935 gui2::show_transient_message("", e.message);
936 } else {
937 ERR_CONFIG << "caught config::error";
938 }
939 } catch(const incorrect_map_format_error& e) {
940 gui2::show_error_message(_("The game map could not be loaded: ") + e.message);
942 load_data_ = std::move(e.data_);
943 // this will make it so next time through the title screen loop, this game is loaded
944 } catch(const wml_exception& e) {
945 e.show();
946 } catch(const game::error& e) {
947 PLAIN_LOG << "caught game::error...";
948 gui2::show_error_message(_("Error: ") + e.message);
949 }
950
951 return true;
952}
953
955{
957 return true;
958 }
959
960 DBG_MP << "starting multiplayer game from the commandline";
961
963
964 events::discard_input(); // prevent the "keylogger" effect
966
967 try {
970 load_data_ = std::move(e.data_);
971 return true;
972 }
973
974 return false;
975}
976
978{
979 if(!gui2::dialogs::language_selection::execute()) {
980 return false;
981 }
982
985 }
986
991
992 return true;
993}
994
996{
997 assert(!load_data_);
998 if(play_replay_) {
999 play_replay();
1000 return;
1001 }
1002
1005
1006 if(reload == reload_mode::RELOAD_DATA) {
1007 try {
1010 } catch(const config::error&) {
1011 return;
1012 }
1013 }
1014 });
1015
1016 try {
1017 campaign_controller ccontroller(state_);
1018 ccontroller.play_game();
1019 ai::manager::singleton_ = nullptr;
1021 load_data_ = std::move(e.data_);
1022 // this will make it so next time through the title screen loop, this game is loaded
1023 } catch(const wml_exception& e) {
1024 e.show();
1025 } catch(const mapgen_exception& e) {
1026 gui2::show_error_message(_("Map generator error: ") + e.message);
1027 }
1028}
1029
1031{
1032 assert(!load_data_);
1033 try {
1034 campaign_controller ccontroller(state_);
1035 ccontroller.play_replay();
1037 load_data_ = std::move(e.data_);
1038 // this will make it so next time through the title screen loop, this game is loaded
1039 } catch(const wml_exception& e) {
1040 e.show();
1041 }
1042}
1043
1045{
1047 while(true) {
1049
1051
1053
1054 if(res != editor::EXIT_RELOAD_DATA) {
1055 return res;
1056 }
1057
1059 }
1060
1061 return editor::EXIT_ERROR; // not supposed to happen
1062}
1063
1065{
1066 load_data_.reset();
1067}
1068
1070{
1071 try {
1073 video::deinit();
1074 } catch(std::exception& e) {
1075 ERR_GENERAL << "Suppressing exception thrown during ~game_launcher: " << e.what();
1076 } catch(...) {
1077 ERR_GENERAL << "Suppressing exception " << utils::get_unknown_exception_type() << " thrown during ~game_launcher";
1078 }
1079}
#define ERR_CONFIG
#define ERR_NET
Definition manager.cpp:38
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands.
static manager * singleton_
Definition manager.hpp:422
level_result::type play_game()
level_result::type play_replay()
bool nogui
True if –nogui was given on the command line.
utils::optional< std::pair< int, int > > resolution
Pair of AxB values specified after –resolution.
bool headless_unit_test
True if –unit is used and –showgui is not present.
utils::optional< std::string > screenshot_map_file
Map file to make a screenshot of.
utils::optional< std::string > test
Non-empty if –test was given on the command line.
bool windowed
True if –windowed was given on the command line.
utils::optional< std::string > language
Non-empty if –language was given on the command line.
bool noreplaycheck
True if –noreplaycheck was given on the command line.
utils::optional< std::string > screenshot_output_file
Output file to put screenshot in.
utils::optional< int > max_fps
Max FPS specified by –max-fps option.
utils::optional< std::string > core_id
Non-empty if –core was given on the command line.
utils::optional< int > campaign_difficulty
Non-empty if –campaign-difficulty was given on the command line.
bool debug
True if –debug was given on the command line.
bool nosound
True if –nosound was given on the command line.
bool multiplayer
True if –multiplayer was given on the command line.
utils::optional< std::string > server
Non-empty if –server was given on the command line.
bool script_unsafe_mode
Whether to load the "package" package for the scripting environment.
utils::optional< unsigned int > translation_percent
Non-empty if –all-translations or –translations-over is given on the command line.
utils::optional< std::string > plugin_file
File to load a lua plugin script from.
bool nobanner
True if –nobanner was given on the command line.
bool nomusic
True if –nomusic was given on the command line.
bool clock
True if –clock was given on the command line.
utils::optional< std::string > render_image_dst
Output file to put rendered image path in.
utils::optional< std::string > render_image
Image path to render.
utils::optional< std::string > campaign
Non-empty if –campaign was given on the command line.
bool new_widgets
True if –new-widgets was given on the command line.
bool fps
True if –fps was given on the command line.
utils::optional< std::string > username
Non-empty if –username was given on the command line.
bool campaign_skip_story
True if –skip-story was given on the command line.
bool with_replay
True if –with-replay was given on the command line.
bool screenshot
True if –screenshot was given on the command line.
std::vector< std::string > unit_test
Non-empty if –unit was given on the command line.
utils::optional< std::string > editor
Non-empty if –editor was given on the command line.
utils::optional< std::string > password
Non-empty if –password was given on the command line.
bool fullscreen
True if –fullscreen was given on the command line.
utils::optional< std::string > campaign_scenario
Non-empty if –campaign-scenario was given on the command line.
utils::optional< std::string > load
Non-empty if –load was given on the command line.
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
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.
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)
const game_config_view & game_config() const
game_launcher(const commandline_options &cmdline_opts)
bool load_prepared_game()
utils::optional< std::string > jump_to_editor_
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)
saved_game state_
savegame::load_game_metadata extract_load_data()
Returns the load_game_metadata object stored in load_data_.
unit_test_result pass_victory_or_defeat(level_result::type res)
game_config_manager config_manager_
bool has_load_data() const
bool play_render_image_mode()
std::vector< std::string > test_scenarios_
unit_test_result
Status code after running a unit test, should match the run_wml_tests script and the documentation fo...
std::string screenshot_map_
const commandline_options & cmdline_opts_
utils::optional< savegame::load_game_metadata > load_data_
void set_test(const std::string &id)
bool play_multiplayer_commandline()
unit_test_result unit_test()
Runs unit tests specified on the command line.
bool play_multiplayer(mp_mode mode)
editor::EXIT_STATUS start_editor()
static void progress(loading_stage stage=loading_stage::none)
Report what is being loaded to the loading screen.
static void display(const std::function< void()> &f)
Generic locator abstracting the location of an image.
Definition picture.hpp:59
void load_package()
Loads the package library into lua environment.
void play_slice()
Definition context.cpp:103
std::size_t size()
Definition manager.cpp:68
std::string get_name(std::size_t idx)
Definition manager.cpp:94
static plugins_manager * get()
Definition manager.cpp:58
void start_plugin(std::size_t idx)
Definition manager.cpp:101
std::string get_detailed_status(std::size_t idx)
Definition manager.cpp:83
lua_kernel_base * get_kernel_base()
Definition manager.cpp:63
std::size_t add_plugin(const std::string &name, const std::string &prog)
Definition manager.cpp:118
void set_network_host(const std::string &host)
bool set_music(bool ison)
static prefs & get()
std::string network_host()
bool set_ui_sound(bool ison)
void set_login(const std::string &login)
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 &)
void set_skip_story(bool skip_story)
bool is_start_of_scenario() const
void set_carryover_sides_start(config carryover_sides_start)
std::string get_scenario_id() const
game_classification & classification()
void cancel_orders()
statistics_record::campaign_stats_t & statistics()
Exception used to signal that the user has decided to abort a game, and to load another game instead.
Definition savegame.hpp:88
Class for replay saves (either manually or automatically).
Definition savegame.hpp:244
static std::shared_ptr< save_index_class > default_saves_dir()
Returns an instance for managing saves in filesystem::get_saves_dir()
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:274
const std::string & filename() const
Definition savegame.hpp:154
static void reset_translations()
Definition tstring.cpp:665
Definitions for the interface to Wesnoth Markup Language (WML).
#define DBG_MP
const events::event_context main_event_context_
Declarations for File-IO.
#define ERR_GENERAL
#define DBG_GENERAL
static lg::log_domain log_enginerefac("enginerefac")
static lg::log_domain log_network("network")
static lg::log_domain log_mp_create("mp/create")
#define WRN_GENERAL
#define LOG_GENERAL
static lg::log_domain log_config("config")
static std::string _(const char *str)
Definition gettext.hpp:100
std::vector< language_def > get_languages(bool all)
Return a list of available translations.
Definition language.cpp:159
void init_textdomains(const game_config_view &cfg)
Initializes the list of textdomains from a configuration object.
Definition language.cpp:339
bool load_language_list()
Definition language.cpp:138
const language_def & get_locale()
Definition language.cpp:319
void set_language(const language_def &locale)
Definition language.cpp:259
void set_min_translation_percent(int percent)
Definition language.cpp:189
Standard logging facilities (interface).
#define STREAMING_LOG
Definition log.hpp:297
#define PLAIN_LOG
Definition log.hpp:296
@ NORMAL
Definition cursor.hpp:28
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition cursor.cpp:151
@ EXIT_QUIT_TO_DESKTOP
@ EXIT_RELOAD_DATA
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:758
std::string get_cache_dir()
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
std::string get_user_data_dir()
utils::optional< std::string > get_wml_location(const std::string &path, const utils::optional< std::string > &current_dir=utils::nullopt)
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
void write_file(const std::string &fname, const std::string &data, std::ios_base::openmode mode=std::ios_base::binary)
Throws io_exception if an error occurs.
std::string get_wesnothd_name()
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error=true)
std::string normalize_path(const std::string &path, bool normalize_separators=false, bool resolve_dot_entries=false)
Returns the absolute path of a file.
bool load_font_config()
std::string path
std::string get_default_title_string()
std::vector< server_info > server_list
void set_debug(bool new_debug)
bool show_debug_clock_button
Do we wish to show the button for the debug clock.
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:210
surface get_surface(const image::locator &i_locator, TYPE type, bool skip_cache)
Returns an image surface suitable for software manipulation.
Definition picture.cpp:691
save_result save_image(const locator &i_locator, const std::string &filename)
Definition picture.cpp:921
@ UNSCALED
Unmodified original-size image.
Definition picture.hpp:173
bool broke_strict()
Definition log.cpp:430
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:64
void set_gamestate(saved_game &gamestate, load_game_metadata &load_data)
Definition savegame.cpp:257
utils::optional< load_game_metadata > load_interactive()
Definition savegame.cpp:185
bool init_sound()
Definition sound.cpp:444
void close_sound()
Definition sound.cpp:507
void flush_cache()
Definition sound.cpp:1069
bool select_campaign(saved_game &state, jump_to_campaign_info jump_to_campaign)
static std::string gettext(const char *str)
Definition gettext.hpp:62
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:691
void set_window_icon(surface &icon)
Sets the icon of the main window.
Definition video.cpp:697
SDL_Window * get_window()
Definition video.cpp:712
void init(fake type)
Initialize the video subsystem.
Definition video.cpp:93
void deinit()
Deinitialize the video subsystem.
Definition video.cpp:122
This file contains the settings handling of the widget library.
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
std::string filename
Filename.
An exception object used when an IO error occurs.
Base class for all the errors encountered by the engine.
Error used for any general game error, e.g.
Error used when game loading fails.
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:32
Holds a 2D point.
Definition point.hpp:25
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.
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define i
#define e
#define c