wesnoth/src/game_launcher.cpp
Andreas Löf 77b39190ba Revert "Start Wesnoth maximized"
This reverts commit 0f77464e3641ce8fca567903f8be95fbca012624.
2015-12-20 11:13:57 +13:00

1155 lines
35 KiB
C++

/*
Copyright (C) 2003 - 2015 by David White <dave@whitevine.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include "game_launcher.hpp"
#include "global.hpp" // for false_, bool_
#include "about.hpp" //for show_about
#include "commandline_options.hpp" // for commandline_options
#include "config.hpp" // for config, etc
#include "config_assign.hpp"
#include "construct_dialog.hpp" // for dialog
#include "cursor.hpp" // for set, CURSOR_TYPE::NORMAL
#include "exceptions.hpp" // for error
#include "filesystem.hpp" // for get_user_config_dir, etc
#include "game_classification.hpp" // for game_classification, etc
#include "game_config.hpp" // for path, no_delay, revision, etc
#include "game_config_manager.hpp" // for game_config_manager
#include "game_end_exceptions.hpp" // for LEVEL_RESULT, etc
#include "generators/map_generator.hpp" // for mapgen_exception
#include "gettext.hpp" // for _
#include "gui/dialogs/language_selection.hpp" // for tlanguage_selection
#include "gui/dialogs/message.hpp" //for show error message
#include "gui/dialogs/mp_host_game_prompt.hpp" //for host game prompt
#include "gui/dialogs/mp_method_selection.hpp"
#include "gui/dialogs/transient_message.hpp" // for show_transient_message
#include "gui/dialogs/title_screen.hpp" // for show_debug_clock_button
#include "gui/widgets/settings.hpp" // for new_widgets
#include "gui/widgets/window.hpp" // for twindow, etc
#include "intro.hpp"
#include "language.hpp" // for language_def, etc
#include "loadscreen.hpp" // for loadscreen, etc
#include "log.hpp" // for LOG_STREAM, logger, general, etc
#include "map_exception.hpp"
#include "game_initialization/multiplayer.hpp" // for start_client, etc
#include "game_initialization/create_engine.hpp"
#include "network.hpp"
#include "game_initialization/playcampaign.hpp" // for play_game, etc
#include "preferences.hpp" // for disable_preferences_save, etc
#include "preferences_display.hpp" // for detect_video_settings, etc
#include "savegame.hpp" // for clean_saves, etc
#include "scripting/application_lua_kernel.hpp"
#include "sdl/utils.hpp" // for surface
#include "serialization/compression.hpp" // for format::NONE
#include "serialization/string_utils.hpp" // for split
#include "game_initialization/singleplayer.hpp" // for sp_create_mode
#include "statistics.hpp"
#include "tstring.hpp" // for operator==, operator!=
#include "util.hpp" // for lexical_cast_default
#include "wml_exception.hpp" // for twml_exception
#include <algorithm> // for copy, max, min, stable_sort
#include <boost/foreach.hpp> // for auto_any_base, etc
#include <boost/optional.hpp> // for optional
#include <boost/tuple/tuple.hpp> // for tuple
#include <cstdlib> // for NULL, system
#include <iostream> // for operator<<, basic_ostream, etc
#include <utility> // for pair
#include "SDL.h" // for SDL_INIT_JOYSTICK, etc
#include "SDL_events.h" // for SDL_ENABLE
#include "SDL_joystick.h" // for SDL_JoystickEventState, etc
#include "SDL_timer.h" // for SDL_Delay
#include "SDL_version.h" // for SDL_VERSION_ATLEAST
#include "SDL_video.h" // for SDL_WM_SetCaption, etc
#ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
#include "gui/widgets/debug.hpp"
#endif
// For wesnothd launch code.
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif // _WIN32
struct incorrect_map_format_error;
static lg::log_domain log_config("config");
#define ERR_CONFIG LOG_STREAM(err, log_config)
#define WRN_CONFIG LOG_STREAM(warn, log_config)
#define LOG_CONFIG LOG_STREAM(info, log_config)
#define LOG_GENERAL LOG_STREAM(info, lg::general)
#define WRN_GENERAL LOG_STREAM(warn, lg::general)
#define DBG_GENERAL LOG_STREAM(debug, lg::general)
static lg::log_domain log_mp_create("mp/create");
#define DBG_MP LOG_STREAM(debug, log_mp_create)
static lg::log_domain log_network("network");
#define ERR_NET LOG_STREAM(err, log_network)
static lg::log_domain log_enginerefac("enginerefac");
#define LOG_RG LOG_STREAM(info, log_enginerefac)
game_launcher::game_launcher(const commandline_options& cmdline_opts, const char *appname) :
cmdline_opts_(cmdline_opts),
disp_(NULL),
video_(),
thread_manager(),
font_manager_(),
prefs_manager_(),
image_manager_(),
main_event_context_(),
hotkey_manager_(),
music_thinker_(),
resize_monitor_(),
test_scenario_("test"),
screenshot_map_(),
screenshot_filename_(),
state_(),
play_replay_(false),
multiplayer_server_(),
jump_to_multiplayer_(false),
jump_to_campaign_(false, -1, "", ""),
jump_to_editor_(false)
{
bool no_music = false;
bool no_sound = false;
// The path can be hardcoded and it might be a relative path.
if(!game_config::path.empty() &&
#ifdef _WIN32
// use c_str to ensure that index 1 points to valid element since c_str() returns null-terminated string
game_config::path.c_str()[1] != ':'
#else
game_config::path[0] != '/'
#endif
)
{
game_config::path = filesystem::get_cwd() + '/' + game_config::path;
font_manager_.update_font_path();
}
const std::string app_basename = filesystem::base_name(appname);
jump_to_editor_ = app_basename.find("editor") != std::string::npos;
if (cmdline_opts_.core_id) {
preferences::set_core_id(*cmdline_opts_.core_id);
}
if (cmdline_opts_.campaign) {
jump_to_campaign_.jump_ = true;
jump_to_campaign_.campaign_id_ = *cmdline_opts_.campaign;
std::cerr << "selected campaign id: [" << jump_to_campaign_.campaign_id_ << "]\n";
if (cmdline_opts_.campaign_difficulty) {
jump_to_campaign_.difficulty_ = *cmdline_opts_.campaign_difficulty;
std::cerr << "selected difficulty: [" << jump_to_campaign_.difficulty_ << "]\n";
}
else
jump_to_campaign_.difficulty_ = -1; // let the user choose the difficulty
if (cmdline_opts_.campaign_scenario) {
jump_to_campaign_.scenario_id_ = *cmdline_opts_.campaign_scenario;
std::cerr << "selected scenario id: [" << jump_to_campaign_.scenario_id_ << "]\n";
}
}
if (cmdline_opts_.clock)
gui2::show_debug_clock_button = true;
if (cmdline_opts_.debug) {
game_config::debug = true;
game_config::mp_debug = true;
}
#ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
if (cmdline_opts_.debug_dot_domain)
gui2::tdebug_layout_graph::set_domain (*cmdline_opts_.debug_dot_domain);
if (cmdline_opts_.debug_dot_level)
gui2::tdebug_layout_graph::set_level (*cmdline_opts_.debug_dot_level);
#endif
if (cmdline_opts_.editor)
{
jump_to_editor_ = true;
if(!cmdline_opts_.editor->empty())
game::load_game_exception::game = *cmdline_opts_.editor;
}
if (cmdline_opts_.fps)
preferences::set_show_fps(true);
if (cmdline_opts_.fullscreen)
preferences::set_fullscreen(true);
if (cmdline_opts_.load)
game::load_game_exception::game = *cmdline_opts_.load;
if (cmdline_opts_.max_fps) {
int fps;
//FIXME: remove the next line once the weird util.cpp specialized template lexical_cast_default() linking issue is solved
fps = lexical_cast_default<int>("", 50);
fps = *cmdline_opts_.max_fps;
fps = std::min<int>(fps, 1000);
fps = std::max<int>(fps, 1);
fps = 1000 / fps;
// increase the delay to avoid going above the maximum
if(1000 % fps != 0) {
++fps;
}
preferences::set_draw_delay(fps);
}
if (cmdline_opts_.nogui || cmdline_opts_.headless_unit_test) {
no_sound = true;
preferences::disable_preferences_save();
}
if (cmdline_opts_.new_widgets)
gui2::new_widgets = true;
if (cmdline_opts_.nodelay)
game_config::no_delay = true;
if (cmdline_opts_.nomusic)
no_music = true;
if (cmdline_opts_.nosound)
no_sound = true;
//These commented lines should be used to implement support of connection
//through a proxy via command line options.
//The ANA network module should implement these methods (while the SDL_net won't.)
if (cmdline_opts_.proxy)
network::enable_connection_through_proxy();
if (cmdline_opts_.proxy_address)
{
network::enable_connection_through_proxy();
network::set_proxy_address(*cmdline_opts_.proxy_address);
}
if (cmdline_opts_.proxy_password)
{
network::enable_connection_through_proxy();
network::set_proxy_password(*cmdline_opts_.proxy_password);
}
if (cmdline_opts_.proxy_port)
{
network::enable_connection_through_proxy();
network::set_proxy_port(*cmdline_opts_.proxy_port);
}
if (cmdline_opts_.proxy_user)
{
network::enable_connection_through_proxy();
network::set_proxy_user(*cmdline_opts_.proxy_user);
}
if (cmdline_opts_.resolution) {
const int xres = cmdline_opts_.resolution->get<0>();
const int yres = cmdline_opts_.resolution->get<1>();
if(xres > 0 && yres > 0) {
const std::pair<int,int> resolution(xres,yres);
preferences::set_resolution(resolution);
}
}
if (cmdline_opts_.screenshot) {
//TODO it could be simplified to use cmdline_opts_ directly if there is no other way to enter screenshot mode
screenshot_map_ = *cmdline_opts_.screenshot_map_file;
screenshot_filename_ = *cmdline_opts_.screenshot_output_file;
no_sound = true;
preferences::disable_preferences_save();
}
if (cmdline_opts_.server){
jump_to_multiplayer_ = true;
//Do we have any server specified ?
if (!cmdline_opts_.server->empty())
multiplayer_server_ = *cmdline_opts_.server;
else //Pick the first server in config
{
if (game_config::server_list.size() > 0)
multiplayer_server_ = preferences::network_host();
else
multiplayer_server_ = "";
}
}
if (cmdline_opts_.username) {
preferences::disable_preferences_save();
preferences::set_login(*cmdline_opts_.username);
}
if (cmdline_opts_.password) {
preferences::disable_preferences_save();
preferences::set_password(*cmdline_opts_.password);
}
if (cmdline_opts_.test)
{
if (!cmdline_opts_.test->empty())
test_scenario_ = *cmdline_opts_.test;
}
if (cmdline_opts_.unit_test)
{
if (!cmdline_opts_.unit_test->empty()) {
test_scenario_ = *cmdline_opts_.unit_test;
}
}
if (cmdline_opts_.windowed)
preferences::set_fullscreen(false);
if (cmdline_opts_.with_replay)
game::load_game_exception::show_replay = true;
std::cerr
<< "\nData directory: " << game_config::path
<< "\nUser configuration directory: " << filesystem::get_user_config_dir()
<< "\nUser data directory: " << filesystem::get_user_data_dir()
<< "\nCache directory: " << filesystem::get_cache_dir()
<< '\n';
std::cerr << '\n';
// disable sound in nosound mode, or when sound engine failed to initialize
if (no_sound || ((preferences::sound_on() || preferences::music_on() ||
preferences::turn_bell() || preferences::UI_sound_on()) &&
!sound::init_sound())) {
preferences::set_sound(false);
preferences::set_music(false);
preferences::set_turn_bell(false);
preferences::set_UI_sound(false);
}
else if (no_music) { // else disable the music in nomusic mode
preferences::set_music(false);
}
}
game_display& game_launcher::disp()
{
if(disp_.get() == NULL) {
if(get_video_surface() == NULL) {
throw CVideo::error();
}
disp_.assign(game_display::create_dummy_display(video_));
}
return *disp_.get();
}
bool game_launcher::init_joystick()
{
if (!preferences::joystick_support_enabled())
return false;
if(SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
return false;
int joysticks = SDL_NumJoysticks();
if (joysticks == 0) return false;
SDL_JoystickEventState(SDL_ENABLE);
bool joystick_found = false;
for (int i = 0; i<joysticks; i++) {
if (SDL_JoystickOpen(i))
joystick_found = true;
}
return joystick_found;
}
bool game_launcher::init_language()
{
if(!::load_language_list())
return false;
language_def locale;
if(cmdline_opts_.language) {
std::vector<language_def> langs = get_languages();
BOOST_FOREACH(const language_def & def, langs) {
if(def.localename == *cmdline_opts_.language) {
locale = def;
break;
}
}
if(locale.localename.empty()) {
std::cerr << "Language symbol '" << *cmdline_opts_.language << "' not found.\n";
return false;
}
} else {
locale = get_locale();
}
::set_language(locale);
return true;
}
bool game_launcher::init_video()
{
if(cmdline_opts_.nogui || cmdline_opts_.headless_unit_test) {
if( !(cmdline_opts_.multiplayer || cmdline_opts_.screenshot || cmdline_opts_.plugin_file || cmdline_opts_.headless_unit_test) ) {
std::cerr << "--nogui flag is only valid with --multiplayer or --screenshot or --plugin flags\n";
return false;
}
video_.make_fake();
game_config::no_delay = true;
return true;
}
std::string wm_title_string = _("The Battle for Wesnoth");
wm_title_string += " - " + game_config::revision;
#if !SDL_VERSION_ATLEAST(2, 0, 0)
SDL_WM_SetCaption(wm_title_string.c_str(), NULL);
#endif
#if !(defined(__APPLE__))
surface icon(image::get_image("icons/icon-game.png", image::UNSCALED));
if(icon != NULL) {
///must be called after SDL_Init() and before setting video mode
#if !SDL_VERSION_ATLEAST(2, 0, 0)
SDL_WM_SetIcon(icon,NULL);
#endif
}
#endif
std::pair<int,int> resolution;
int bpp = 0;
int video_flags = 0;
bool found_matching = preferences::detect_video_settings(video_, resolution, bpp, video_flags);
if (cmdline_opts_.bpp) {
bpp = *cmdline_opts_.bpp;
} else if (cmdline_opts_.screenshot) {
bpp = 32;
}
if(!found_matching && (video_flags & FULL_SCREEN)) {
video_flags ^= FULL_SCREEN;
found_matching = preferences::detect_video_settings(video_, resolution, bpp, video_flags);
if (found_matching) {
std::cerr << "Failed to set " << resolution.first << 'x' << resolution.second << 'x' << bpp << " in fullscreen mode. Using windowed instead.\n";
}
}
if(!found_matching) {
std::cerr << "Video mode " << resolution.first << 'x'
<< resolution.second << 'x' << bpp
<< " is not supported.\n";
return false;
}
std::cerr << "Setting mode to " << resolution.first << "x" << resolution.second << "x" << bpp << "\n";
const int res = video_.setMode(resolution.first,resolution.second,bpp,video_flags);
video_.setBpp(bpp);
if(res == 0) {
std::cerr << "Required video mode, " << resolution.first << "x"
<< resolution.second << "x" << bpp << " is not supported\n";
return false;
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
CVideo::set_window_title(wm_title_string);
#if !(defined(__APPLE__))
if(icon != NULL) {
CVideo::set_window_icon(icon);
}
#endif
#endif
return true;
}
bool game_launcher::init_lua_script()
{
bool error = false;
std::cerr << "Checking lua scripts... ";
if (cmdline_opts_.script_unsafe_mode) {
plugins_manager::get()->get_kernel_base()->load_package(); //load the "package" package, so that scripts can get what packages they want
}
// get the application lua kernel, load and execute script file, if script file is present
if (cmdline_opts_.script_file)
{
filesystem::scoped_istream sf = filesystem::istream_file(*cmdline_opts_.script_file);
if (!sf->fail()) {
/* Cancel all "jumps" to editor / campaign / multiplayer */
jump_to_multiplayer_ = false;
jump_to_editor_ = false;
jump_to_campaign_.jump_ = false;
std::string full_script((std::istreambuf_iterator<char>(*sf)), std::istreambuf_iterator<char>());
std::cerr << "\nRunning lua script: " << *cmdline_opts_.script_file << std::endl;
plugins_manager::get()->get_kernel_base()->run(full_script.c_str());
} else {
std::cerr << "Encountered failure when opening script '" << *cmdline_opts_.script_file << "'\n";
error = true;
}
}
if (cmdline_opts_.plugin_file)
{
std::string filename = *cmdline_opts_.plugin_file;
std::cerr << "Loading a plugin file'" << filename << "'...\n";
filesystem::scoped_istream sf = filesystem::istream_file(filename);
try {
if (sf->fail()) {
throw std::runtime_error("failed to open plugin file");
}
/* Cancel all "jumps" to editor / campaign / multiplayer */
jump_to_multiplayer_ = false;
jump_to_editor_ = false;
jump_to_campaign_.jump_ = false;
std::string full_plugin((std::istreambuf_iterator<char>(*sf)), std::istreambuf_iterator<char>());
plugins_manager & pm = *plugins_manager::get();
size_t i = pm.add_plugin(filename, full_plugin);
for (size_t j = 0 ; j < pm.size(); ++j) {
std::cerr << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j) << std::endl;
}
std::cerr << "Starting a plugin...\n";
pm.start_plugin(i);
for (size_t j = 0 ; j < pm.size(); ++j) {
std::cerr << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j) << std::endl;
}
plugins_context pc("init");
for (size_t repeat = 0; repeat < 5; ++repeat) {
std::cerr << "Playing a slice...\n";
pc.play_slice();
for (size_t j = 0 ; j < pm.size(); ++j) {
std::cerr << j << ": " << pm.get_name(j) << " -- " << pm.get_detailed_status(j) << std::endl;
}
}
return true;
} catch (std::exception & e) {
gui2::show_error_message(disp().video(), std::string("When loading a plugin, error:\n") + e.what());
error = true;
}
}
if (!error) {
std::cerr << "ok\n";
}
return !error;
}
bool game_launcher::play_test()
{
static bool first_time = true;
if(!cmdline_opts_.test) {
return true;
}
if(!first_time)
return false;
first_time = false;
state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::TEST;
state_.classification().campaign_define = "TEST";
state_.mp_settings().mp_era = "era_default";
state_.mp_settings().show_connect = false;
state_.set_carryover_sides_start(
config_of("next_scenario", test_scenario_)
);
game_config_manager::get()->
load_game_config_for_game(state_.classification());
try {
campaign_controller ccontroller(disp(), state_, game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types());
ccontroller.play_game();
} catch (game::load_game_exception &) {
return true;
}
return false;
}
// Same as play_test except that we return the results of play_game.
int game_launcher::unit_test()
{
static bool first_time_unit = true;
if(!cmdline_opts_.unit_test) {
return 0;
}
if(!first_time_unit)
return 0;
first_time_unit = false;
state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::TEST;
state_.classification().campaign_define = "TEST";
state_.set_carryover_sides_start(
config_of("next_scenario", test_scenario_)
);
game_config_manager::get()->
load_game_config_for_game(state_.classification());
try {
campaign_controller ccontroller(disp(), state_, game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types(), true);
LEVEL_RESULT res = ccontroller.play_game();
if (!(res == LEVEL_RESULT::VICTORY) || lg::broke_strict()) {
return 1;
}
} catch (game::load_game_exception &) {
std::cerr << "Load_game_exception encountered while loading the unit test!" << std::endl;
return 1; //failed to load the unit test scenario
} catch(twml_exception& e) {
std::cerr << "Caught WML Exception:" << e.dev_message << std::endl; //e.show(disp());
return 1;
}
savegame::clean_saves(state_.classification().label);
if (cmdline_opts_.noreplaycheck)
return 0; //we passed, huzzah!
savegame::replay_savegame save(state_, compression::NONE);
save.save_game_automatic(disp().video(), false, "unit_test_replay"); //false means don't check for overwrite
clear_loaded_game();
//game::load_game_exception::game = *cmdline_opts_.load
game::load_game_exception::game = "unit_test_replay";
// game::load_game_exception::game = "Unit_test_" + test_scenario_ + "_replay";
game::load_game_exception::show_replay = true;
game::load_game_exception::cancel_orders = true;
if (!load_game()) {
std::cerr << "Failed to load the replay!" << std::endl;
return 3; //failed to load replay
}
try {
campaign_controller ccontroller(disp(), state_, game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types(), true);
LEVEL_RESULT res = ccontroller.play_replay();
if (!(res == LEVEL_RESULT::VICTORY)) {
std::cerr << "Observed failure on replay" << std::endl;
return 4;
}
clear_loaded_game();
} catch (game::load_game_exception &) {
std::cerr << "Load_game_exception encountered during play_replay!" << std::endl;
return 3; //failed to load replay
} catch(twml_exception& e) {
std::cerr << "WML Exception while playing replay: " << e.dev_message << std::endl; //e.show(disp());
return 4; //failed with an error during the replay
}
return 0; //we passed, huzzah!
}
bool game_launcher::play_screenshot_mode()
{
if(!cmdline_opts_.screenshot) {
return true;
}
game_config_manager::get()->load_game_config_for_editor();
::init_textdomains(game_config_manager::get()->game_config());
editor::start(game_config_manager::get()->game_config(), video_,
screenshot_map_, true, screenshot_filename_);
return false;
}
bool game_launcher::play_render_image_mode()
{
if(!cmdline_opts_.render_image) {
return true;
}
state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::MULTIPLAYER;
DBG_GENERAL << "Current campaign type: " << state_.classification().campaign_type << std::endl;
try {
game_config_manager::get()->
load_game_config_for_game(state_.classification());
} catch(config::error& e) {
std::cerr << "Error loading game config: " << e.what() << std::endl;
return false;
}
// A default output filename
std::string outfile = "wesnoth_image.bmp";
// If a output path was given as an argument, use that instead
if (cmdline_opts_.render_image_dst) {
outfile = *cmdline_opts_.render_image_dst;
}
if (!image::save_image(*cmdline_opts_.render_image, outfile)) {
exit(1);
}
return false;
}
bool game_launcher::is_loading() const
{
return !game::load_game_exception::game.empty();
}
bool game_launcher::load_game()
{
assert(game_config_manager::get());
DBG_GENERAL << "Current campaign type: " << state_.classification().campaign_type << std::endl;
savegame::loadgame load(disp(), game_config_manager::get()->game_config(),
state_);
try {
if(!load.load_game(game::load_game_exception::game, game::load_game_exception::show_replay, game::load_game_exception::cancel_orders, game::load_game_exception::select_difficulty, game::load_game_exception::difficulty, game::load_game_exception::skip_version_check)) {
clear_loaded_game();
return false;
}
try {
game_config_manager::get()->
load_game_config_for_game(state_.classification());
} catch(config::error&) {
return false;
}
load.set_gamestate();
} catch(config::error& e) {
if(e.message.empty()) {
gui2::show_error_message(disp().video(), _("The file you have tried to load is corrupt"));
}
else {
gui2::show_error_message(disp().video(), _("The file you have tried to load is corrupt: '") + e.message + '\'');
}
return false;
} catch(twml_exception& e) {
e.show(disp());
return false;
} catch(filesystem::io_exception& e) {
if(e.message.empty()) {
gui2::show_error_message(disp().video(), _("File I/O Error while reading the game"));
} else {
gui2::show_error_message(disp().video(), _("File I/O Error while reading the game: '") + e.message + '\'');
}
return false;
} catch(game::error& e) {
if(e.message.empty()) {
gui2::show_error_message(disp().video(), _("The file you have tried to load is corrupt"));
}
else {
gui2::show_error_message(disp().video(), _("The file you have tried to load is corrupt: '") + e.message + '\'');
}
return false;
}
play_replay_ = load.show_replay();
LOG_CONFIG << "is middle game savefile: " << (state_.is_mid_game_save() ? "yes" : "no") << "\n";
LOG_CONFIG << "show replay: " << (play_replay_ ? "yes" : "no") << "\n";
// in case load.show_replay() && !state_.is_mid_game_save()
// there won't be any turns to replay, but the
// user gets to watch the intro sequence again ...
if(state_.is_mid_game_save() && load.show_replay())
{
statistics::clear_current_scenario();
}
if(state_.classification().campaign_type == game_classification::CAMPAIGN_TYPE::MULTIPLAYER) {
state_.unify_controllers();
gui2::show_message(disp().video(), _("Warning") , _("This is a multiplayer scenario. Some parts of it may not work properly in single-player. It is recommended to load this scenario through the <b>Multiplayer</b> → <b>Load Game</b> dialog instead."), "", true, true);
}
if (load.cancel_orders()) {
state_.cancel_orders();
}
return true;
}
void game_launcher::set_tutorial()
{
state_ = saved_game();
state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::TUTORIAL;
state_.classification().campaign_define = "TUTORIAL";
state_.mp_settings().mp_era = "era_default";
state_.mp_settings().show_connect = false;
state_.set_carryover_sides_start(
config_of("next_scenario", "tutorial")
);
}
void game_launcher::mark_completed_campaigns(std::vector<config> &campaigns)
{
BOOST_FOREACH(config &campaign, campaigns) {
campaign["completed"] = preferences::is_campaign_completed(campaign["id"]);
}
}
bool game_launcher::new_campaign()
{
state_ = saved_game();
state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::SCENARIO;
state_.mp_settings().show_connect = false;
play_replay_ = false;
return sp::enter_create_mode(disp(), game_config_manager::get()->game_config(),
state_, jump_to_campaign_, true);
}
std::string game_launcher::jump_to_campaign_id() const
{
return jump_to_campaign_.campaign_id_;
}
bool game_launcher::goto_campaign()
{
if(jump_to_campaign_.jump_){
if(new_campaign()) {
jump_to_campaign_.jump_ = false;
launch_game(NO_RELOAD_DATA);
}else{
jump_to_campaign_.jump_ = false;
return false;
}
}
return true;
}
bool game_launcher::goto_multiplayer()
{
if(jump_to_multiplayer_){
jump_to_multiplayer_ = false;
if(play_multiplayer()){
;
}else{
return false;
}
}
return true;
}
bool game_launcher::goto_editor()
{
if(jump_to_editor_){
jump_to_editor_ = false;
if (start_editor(filesystem::normalize_path(game::load_game_exception::game)) ==
editor::EXIT_QUIT_TO_DESKTOP)
{
return false;
}
clear_loaded_game();
}
return true;
}
void game_launcher::start_wesnothd()
{
const std::string wesnothd_program =
preferences::get_mp_server_program_name().empty() ?
filesystem::get_program_invocation("wesnothd") : preferences::get_mp_server_program_name();
std::string config = filesystem::get_user_config_dir() + "/lan_server.cfg";
if (!filesystem::file_exists(config)) {
// copy file if it isn't created yet
filesystem::write_file(config, filesystem::read_file(filesystem::get_wml_location("lan_server.cfg")));
}
#ifndef _WIN32
std::string command = "\"" + wesnothd_program +"\" -c \"" + config + "\" -d -t 2 -T 5";
#else
// start wesnoth as background job
std::string command = "cmd /C start \"wesnoth server\" /B \"" + wesnothd_program + "\" -c \"" + config + "\" -t 2 -T 5";
// Make sure wesnothd's console output is visible on the console window by
// disabling SDL's stdio redirection code for this and future child
// processes. No need to bother cleaning this up because it's only
// meaningful to SDL applications during pre-main initialization.
SetEnvironmentVariableA("SDL_STDIO_REDIRECT", "0");
#endif
LOG_GENERAL << "Starting wesnothd: "<< command << "\n";
if (std::system(command.c_str()) == 0) {
// Give server a moment to start up
SDL_Delay(50);
return;
}
preferences::set_mp_server_program_name("");
// Couldn't start server so throw error
WRN_GENERAL << "Failed to run server start script" << std::endl;
throw game::mp_server_error("Starting MP server failed!");
}
bool game_launcher::play_multiplayer()
{
int res;
state_ = saved_game();
state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::MULTIPLAYER;
//Print Gui only if the user hasn't specified any server
if( multiplayer_server_.empty() ){
int start_server;
do {
start_server = 0;
gui2::tmp_method_selection dlg;
dlg.show(disp().video());
if(dlg.get_retval() == gui2::twindow::OK) {
res = dlg.get_choice();
} else {
return false;
}
if (res == 2 && preferences::mp_server_warning_disabled() < 2) {
start_server = !gui2::tmp_host_game_prompt::execute(disp().video());
}
} while (start_server);
if (res < 0) {
return false;
}
}else{
res = 4;
}
try {
if (res == 2)
{
try {
start_wesnothd();
} catch(game::mp_server_error&)
{
std::string path = preferences::show_wesnothd_server_search(disp());
if (!path.empty())
{
preferences::set_mp_server_program_name(path);
start_wesnothd();
}
else
{
return false;
}
}
}
//create_engine already calls game_config_manager::get()->load_config but maybe its better to have MULTIPLAYER defined while we are in the lobby.
game_config_manager::get()->load_game_config_for_create(true);
events::discard_input(); // prevent the "keylogger" effect
cursor::set(cursor::NORMAL);
if(res == 3) {
mp::start_local_game(disp(),
game_config_manager::get()->game_config(), state_);
} else if((res >= 0 && res <= 2) || res == 4) {
std::string host;
if(res == 0) {
host = preferences::server_list().front().address;
}else if(res == 2) {
host = "localhost";
}else if(res == 4){
host = multiplayer_server_;
multiplayer_server_ = "";
}
mp::start_client(disp(), game_config_manager::get()->game_config(),
state_, host);
}
} catch(game::mp_server_error& e) {
gui2::show_error_message(disp().video(), _("Error while starting server: ") + e.message);
} catch(game::load_game_failed& e) {
gui2::show_error_message(disp().video(), _("The game could not be loaded: ") + e.message);
} catch(game::game_error& e) {
gui2::show_error_message(disp().video(), _("Error while playing the game: ") + e.message);
} catch (mapgen_exception& e) {
gui2::show_error_message(disp().video(), std::string(_("Map generator error: ") + e.message));
} catch(network::error& e) {
if(e.message != "") {
ERR_NET << "caught network::error: " << e.message << std::endl;
gui2::show_transient_message(disp().video()
, ""
, translation::gettext(e.message.c_str()));
} else {
ERR_NET << "caught network::error" << std::endl;
}
} catch(config::error& e) {
if(e.message != "") {
ERR_CONFIG << "caught config::error: " << e.message << std::endl;
gui2::show_transient_message(disp().video(), "", e.message);
} else {
ERR_CONFIG << "caught config::error" << std::endl;
}
} catch(incorrect_map_format_error& e) {
gui2::show_error_message(disp().video(), std::string(_("The game map could not be loaded: ")) + e.message);
} catch (game::load_game_exception &) {
//this will make it so next time through the title screen loop, this game is loaded
} catch(twml_exception& e) {
e.show(disp());
} catch (game::error & e) {
std::cerr << "caught game::error...\n";
gui2::show_error_message(disp().video(), _("Error: ") + e.message);
}
return false;
}
bool game_launcher::play_multiplayer_commandline()
{
if(!cmdline_opts_.multiplayer) {
return true;
}
DBG_MP << "starting multiplayer game from the commandline" << std::endl;
// These are all the relevant lines taken literally from play_multiplayer() above
state_ = saved_game();
state_.classification().campaign_type = game_classification::CAMPAIGN_TYPE::MULTIPLAYER;
game_config_manager::get()->
load_game_config_for_game(state_.classification());
events::discard_input(); // prevent the "keylogger" effect
cursor::set(cursor::NORMAL);
mp::start_local_game_commandline(disp(),
game_config_manager::get()->game_config(), state_, cmdline_opts_);
return false;
}
bool game_launcher::change_language()
{
gui2::tlanguage_selection dlg;
dlg.show(disp().video());
if (dlg.get_retval() != gui2::twindow::OK) return false;
if (!(cmdline_opts_.nogui || cmdline_opts_.headless_unit_test)) {
std::string wm_title_string = _("The Battle for Wesnoth");
wm_title_string += " - " + game_config::revision;
#if SDL_VERSION_ATLEAST(2, 0, 0)
CVideo::set_window_title(wm_title_string);
#else
SDL_WM_SetCaption(wm_title_string.c_str(), NULL);
#endif
}
return true;
}
void game_launcher::show_preferences()
{
const preferences::display_manager disp_manager(&disp());
preferences::show_preferences_dialog(disp(),
game_config_manager::get()->game_config());
disp().redraw_everything();
}
void game_launcher::launch_game(RELOAD_GAME_DATA reload)
{
if(play_replay_)
{
play_replay();
return;
}
loadscreen::global_loadscreen_manager loadscreen_manager(disp().video());
loadscreen::start_stage("load data");
if(reload == RELOAD_DATA) {
try {
game_config_manager::get()->
load_game_config_for_game(state_.classification());
} catch(config::error&) {
return;
}
}
try {
campaign_controller ccontroller(disp(), state_, game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types());
LEVEL_RESULT result = ccontroller.play_game();
// don't show The End for multiplayer scenario
// change this if MP campaigns are implemented
if(result == LEVEL_RESULT::VICTORY && !state_.classification().is_normal_mp_game()) {
preferences::add_completed_campaign(state_.classification().campaign, state_.classification().difficulty);
the_end(disp(), state_.classification().end_text, state_.classification().end_text_duration);
if(state_.classification().end_credits) {
about::show_about(disp(),state_.classification().campaign);
}
}
clear_loaded_game();
} catch (game::load_game_exception &) {
//this will make it so next time through the title screen loop, this game is loaded
} catch(twml_exception& e) {
e.show(disp());
}
}
void game_launcher::play_replay()
{
try {
campaign_controller ccontroller(disp(), state_, game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types());
ccontroller.play_replay();
clear_loaded_game();
} catch (game::load_game_exception &) {
//this will make it so next time through the title screen loop, this game is loaded
} catch(twml_exception& e) {
e.show(disp());
}
}
editor::EXIT_STATUS game_launcher::start_editor(const std::string& filename)
{
while(true){
game_config_manager::get()->load_game_config_for_editor();
::init_textdomains(game_config_manager::get()->game_config());
editor::EXIT_STATUS res = editor::start(
game_config_manager::get()->game_config(), video_, filename);
if(res != editor::EXIT_RELOAD_DATA)
return res;
game_config_manager::get()->reload_changed_game_config();
image::flush_cache();
}
return editor::EXIT_ERROR; // not supposed to happen
}
game_launcher::~game_launcher()
{
try {
gui::dialog::delete_empty_menu();
sound::close_sound();
} catch (...) {}
}