From 728b29dc61de885105c82350574fc3c411c18c70 Mon Sep 17 00:00:00 2001 From: gfgtdf Date: Thu, 12 Jun 2014 17:13:43 +0200 Subject: [PATCH] make wesnothd use savefile format for new games This patch changes the mp wesnothd protocol. multiplayer connect now works as following: multiplayer_create/configure creates saved_game object which is then loaded by mp_connect and sended to the server in the usual savefile format, this especially implies that [side] are no longer at toplevel but instead nested into [scenario] or [snapshot]. mp options are saved into state (the saved_game object) (state_.mp_settings()) previously we had a separate mp_settings object for that now we only have one mp_setting object. This commit still not fixes mp modification/era events/options. The reason is, that i want to move the expansion of those to saved_game.cpp for better compatibility to Sp (Especially make era events work in mp games reloaded in sp), but if that won't work i'll just enable them how they were before. There are still some things to do. Especially the server generated replay-saves will be completely broken. (will fix in another commit) we don't use the [replay_start] as starting pos in mp anymore so i removed 2 lines in playcampaign that assumed that. I also removed soem code in mo_game_utils that seemed redundant, bugged or useless since we now send the [carryover_sides_start] over the network too there is no reason to apply [carryover_sides_start] before getting into mp_connect engine (when advanging the the next scenario), even more, since the new code expects only ONE of [carryover_sides_start] or [carryover_sides] to be present, we cannot expand carryover_sides_start in playcampaign before going to mp_connect because we might wan to modify [carryover_sides_start] in mp_connect still TODO: * rename mp::configure::get_parameters * reimplement mp modification/era s events/options * remove fields from the mp_game_settings object * remove disabled/outcommented code --- src/mp_game_utils.cpp | 166 ++++++++++++----------------- src/mp_game_utils.hpp | 5 +- src/multiplayer.cpp | 28 +++-- src/multiplayer_configure.cpp | 22 ++-- src/multiplayer_configure.hpp | 9 +- src/multiplayer_connect.cpp | 4 +- src/multiplayer_connect_engine.cpp | 49 +++------ src/multiplayer_connect_engine.hpp | 13 ++- src/multiplayer_create.cpp | 22 +++- src/multiplayer_create_engine.cpp | 39 +++---- src/multiplayer_create_engine.hpp | 2 +- src/multiplayer_wait.cpp | 43 ++++++-- src/multiplayer_wait.hpp | 2 + src/playcampaign.cpp | 35 +++--- src/saved_game.cpp | 48 +++++++++ src/saved_game.hpp | 2 + 16 files changed, 260 insertions(+), 229 deletions(-) diff --git a/src/mp_game_utils.cpp b/src/mp_game_utils.cpp index 25841d0533a6..2f3843fbf253 100644 --- a/src/mp_game_utils.cpp +++ b/src/mp_game_utils.cpp @@ -45,63 +45,68 @@ static lg::log_domain log_network("network"); namespace mp { -config initial_level_config(game_display& disp, const mp_game_settings& params, - saved_game& state) +// To remove radundant informaion in the clientside internal programmflow +// I want to remove these values from mp_settings so i need to readd them here +static void add_multiplayer_classification(config& multiplayer, saved_game& state) { - config level; - - if (params.saved_game) { - try { - savegame::loadgame load(disp, - resources::config_manager->game_config(), state); - load.load_multiplayer_game(); - load.fill_mplevel_config(level); + multiplayer["mp_scenario"] = state.get_scenario_id(); + multiplayer["mp_scenario_name"] = state.get_starting_pos()["name"]; + multiplayer["difficulty_define"] = state.classification().difficulty; + multiplayer["mp_campaign"] = state.classification().campaign; +} - resources::config_manager-> - load_game_config_for_game(state.classification()); - } - catch (load_game_cancelled_exception){ - return config(); - } catch(config::error&) { - return config(); +config initial_level_config(saved_game& state) +{ + const mp_game_settings& params = state.mp_settings(); + //Shall we do this here or in create_engine ? Idk so just call it twice. + state.expand_scenario(); + + config& scenario = state.get_starting_pos(); + if(!state.mp_settings().saved_game) + { + if(state.carryover_sides_start["random_seed"].str() == "") + { + state.carryover_sides_start["random_seed"] = rand(); + state.carryover_sides_start["random_calls"] = 0; } - } else { - level.merge_with(params.scenario_data); - level["turns"] = params.num_turns; - level["difficulty"] = params.difficulty_define; - level.add_child("multiplayer", params.to_config()); + scenario["turns"] = params.num_turns; // Convert options to events - level.add_child_at("event", options::to_event(params.options.find_child( + scenario.add_child_at("event", options::to_event(params.options.find_child( "multiplayer", "id", params.mp_scenario)), 0); - if(!level.has_attribute("next_underlying_unit_id")) - { - level["next_underlying_unit_id"] = 0; - } - n_unit::id_manager::instance().clear(); if (params.random_start_time) { - if (!tod_manager::is_start_ToD(level["random_start_time"])) + if (!tod_manager::is_start_ToD(scenario["random_start_time"])) { - level["random_start_time"] = true; + scenario["random_start_time"] = true; } } else { - level["random_start_time"] = false; + scenario["random_start_time"] = false; } - level["experience_modifier"] = params.xp_modifier; - level["random_seed"] = state.carryover_sides_start["random_seed"]; + scenario["experience_modifier"] = params.xp_modifier; + } + + if (scenario["objectives"].empty()) { + scenario["objectives"] = "" + t_string(N_("Victory:"), "wesnoth") + + "\n• " + + t_string(N_("Defeat enemy leader(s)"), "wesnoth") + ""; } + config level = state.to_config(); + add_multiplayer_classification(level.child_or_add("multiplayer"), state); + std::string era = params.mp_era; if (params.saved_game) { - if (const config &c = level.child("snapshot").child("era")) + //if (const config &c = level.child("snapshot").child("era")) + // era = c["id"].str(); + if (const config &c = level.child("era")) era = c["id"].str(); } - + //[era] as a toplevel tag ? fine for me. // Initialize the list of sides available for the current era. const config &era_cfg = resources::config_manager->game_config().find_child("era", "id", era); @@ -127,7 +132,8 @@ config initial_level_config(game_display& disp, const mp_game_settings& params, cfg.add_child_at("event", options::to_event( params.options.find_child("era", "id", era)), 0); } - + + //TODO: move to saved_game. // Add modifications const std::vector& mods = params.active_mods; for (unsigned i = 0; i < mods.size(); i++) { @@ -140,9 +146,9 @@ config initial_level_config(game_display& disp, const mp_game_settings& params, params.options.find_child("modification", "id", mods[i])), 0); } - // This will force connecting clients to be using the same version number as us. - level["version"] = game_config::version; - + +#if 0 + // we have this alredy in [multiplayer]. If removing this causes a bug than that's most likley bacause some is searchin for this information at the wrng place (not in [multiplayer]) // If game was reloaded, params won't contain all required information and so we // need to take it from the actual level config. if (params.saved_game) { @@ -152,24 +158,22 @@ config initial_level_config(game_display& disp, const mp_game_settings& params, level["observer"] = params.allow_observers; level["shuffle_sides"] = params.shuffle_sides; } - - if (level["objectives"].empty()) { - level["objectives"] = "" + t_string(N_("Victory:"), "wesnoth") + - "\n• " + - t_string(N_("Defeat enemy leader(s)"), "wesnoth") + ""; - } - +#endif + + // This will force connecting clients to be using the same version number as us. + level["version"] = game_config::version; return level; } -void level_to_gamestate(config& level, saved_game& state) +void level_to_gamestate(const config& level, saved_game& state) { + state = saved_game(level); + state.classification().campaign_type = game_classification::MULTIPLAYER; // Any replay data is only temporary and should be removed from // the level data in case we want to save the game later. if (const config& replay_data = level.child("replay")) { LOG_NW << "setting replay\n"; - state.replay_data = replay_data; recorder = replay(replay_data); if (!recorder.empty()) { recorder.set_skip(false); @@ -177,66 +181,35 @@ void level_to_gamestate(config& level, saved_game& state) } } - carryover_info sides = carryover_info(state.carryover_sides_start); - - n_unit::id_manager::instance().set_save_id(level["next_underlying_unit_id"]); - - // Set random. - const config::attribute_value& seed = level["random_seed"]; - if (!seed.empty()) { - const unsigned calls = level["random_calls"].to_unsigned(); - sides.rng().seed_random(seed.to_int(42), calls); - } else { - ERR_NG << "No random seed found, random " - "events will probably be out of sync.\n"; - } - + + //save id setting was moved to play_controller. + // Adds the starting pos to the level. - if (!level.child("replay_start")) { - level.add_child("replay_start", level); - level.child("replay_start").remove_child("multiplayer", 0); - } + // This is important, if it does not happen, the starting position is // missing and will be drawn from the snapshot instead // (which is not what we want since we have // all needed information here already). - state.replay_start() = level.child("replay_start"); - - level["campaign_type"] = lexical_cast (game_classification::MULTIPLAYER); - state.classification().campaign_type = game_classification::MULTIPLAYER; - state.classification().completion = level["completion"].str(); - state.classification().version = level["version"].str(); + //state.replay_start() = level.child("replay_start"); - if (const config& vars = level.child("variables")) { - sides.set_variables(vars); - } - sides.get_wml_menu_items().set_menu_items(level); - state.mp_settings().set_from_config(level); // Check whether it is a save-game by looking for snapshot data. - const config& snapshot = level.child("snapshot"); - const bool saved_game = snapshot && snapshot.child("side"); + //const config& snapshot = level.child("snapshot"); + //const bool saved_game = state.mp_settings().saved_game; // It might be a MP campaign start-of-scenario save. // In this case, it's not entirely a new game, but not a save, either. // Check whether it is no savegame and the starting_pos // contains [player] information. - bool start_of_scenario = - !saved_game && state.replay_start().child("player"); - - // If we start a fresh game, there won't be any snapshot information. - // If however this is a savegame, we got a valid snapshot here. - if (saved_game) { - state.set_snapshot(snapshot); - if (const config& v = snapshot.child("variables")) { - sides.set_variables(v); - } - sides.get_wml_menu_items().set_menu_items(snapshot); - } + // Edit: idk what this code did before, but i most liley didn't work because [replay_start] never contains [player] + //bool start_of_scenario = !saved_game && state.replay_start().child("player"); // In any type of reload (normal save or start-of-scenario) the players // could have changed and need to be replaced. - if (saved_game || start_of_scenario){ + // EDIT: We directy use the starting_pos() sides now, so no need to so this anymore. +#if 0 + if (saved_game) + { config::child_itors saved_sides = saved_game ? state.get_starting_pos().child_range("side") : state.replay_start().child_range("side"); @@ -257,14 +230,7 @@ void level_to_gamestate(config& level, saved_game& state) } } } - if (sides.get_variables().empty()) { - LOG_NG << "No variables were found for the saved_game." << std::endl; - } else { - LOG_NG << "Variables found and loaded into saved_game:" << std::endl; - LOG_NG << sides.get_variables(); - } - - state.carryover_sides_start = sides.to_config(); +#endif } void check_response(network::connection res, const config& data) diff --git a/src/mp_game_utils.hpp b/src/mp_game_utils.hpp index 302311eba1a6..33295cfb34c2 100644 --- a/src/mp_game_utils.hpp +++ b/src/mp_game_utils.hpp @@ -23,10 +23,9 @@ class saved_game; namespace mp { -config initial_level_config(game_display& disp, const mp_game_settings& params, - saved_game& state); +config initial_level_config(saved_game& state); -void level_to_gamestate(config& level, saved_game& state); +void level_to_gamestate(const config& level, saved_game& state); void check_response(network::connection res, const config& data); diff --git a/src/multiplayer.cpp b/src/multiplayer.cpp index 4aae86304788..7d381cb883fb 100644 --- a/src/multiplayer.cpp +++ b/src/multiplayer.cpp @@ -474,7 +474,7 @@ static void enter_create_mode(game_display& disp, const config& game_config, saved_game& state, bool local_players_only = false); static bool enter_connect_mode(game_display& disp, const config& game_config, - saved_game& state, const mp_game_settings& params, + saved_game& state, bool local_players_only = false) { DBG_MP << "entering connect mode" << std::endl; @@ -487,9 +487,8 @@ static bool enter_connect_mode(game_display& disp, const config& game_config, statistics::fresh_stats(); { - mp::connect_engine_ptr connect_engine(new mp::connect_engine(disp, - state, params, local_players_only, true)); - mp::connect ui(disp, params.name, game_config, gamechat, gamelist, + mp::connect_engine_ptr connect_engine(new mp::connect_engine(state, local_players_only, true)); + mp::connect ui(disp, state.mp_settings().name, game_config, gamechat, gamelist, *connect_engine); run_lobby_loop(disp, ui); @@ -522,7 +521,7 @@ static bool enter_connect_mode(game_display& disp, const config& game_config, } static bool enter_configure_mode(game_display& disp, const config& game_config, - saved_game& state, const mp_game_settings& params, + saved_game& state, bool local_players_only = false); static void enter_create_mode(game_display& disp, const config& game_config, @@ -547,23 +546,22 @@ static void enter_create_mode(game_display& disp, const config& game_config, } else { mp::ui::result res; - mp_game_settings new_params; { mp::create ui(disp, game_config, state, gamechat, gamelist); run_lobby_loop(disp, ui); res = ui.get_result(); - new_params = ui.get_parameters(); + ui.get_parameters(); } switch (res) { case mp::ui::CREATE: configure_canceled = !enter_configure_mode(disp, game_config, - state, new_params, local_players_only); + state, local_players_only); break; case mp::ui::LOAD_GAME: connect_canceled = !enter_connect_mode(disp, game_config, - state, new_params, local_players_only); + state, local_players_only); break; case mp::ui::QUIT: default: @@ -576,7 +574,7 @@ static void enter_create_mode(game_display& disp, const config& game_config, } static bool enter_configure_mode(game_display& disp, const config& game_config, - saved_game& state, const mp_game_settings& params, bool local_players_only) + saved_game& state, bool local_players_only) { DBG_MP << "entering configure mode" << std::endl; @@ -586,20 +584,19 @@ static bool enter_configure_mode(game_display& disp, const config& game_config, connect_canceled = false; mp::ui::result res; - mp_game_settings new_params; { - mp::configure ui(disp, game_config, gamechat, gamelist, params, + mp::configure ui(disp, game_config, gamechat, gamelist, state, local_players_only); run_lobby_loop(disp, ui); res = ui.get_result(); - new_params = ui.get_parameters(); + ui.get_parameters(); } switch (res) { case mp::ui::CREATE: connect_canceled = !enter_connect_mode(disp, game_config, - state, new_params, local_players_only); + state, local_players_only); break; case mp::ui::QUIT: default: @@ -856,8 +853,7 @@ void start_local_game_commandline(game_display& disp, const config& game_config, statistics::fresh_stats(); { - mp::connect_engine_ptr connect_engine(new mp::connect_engine(disp, - state, parameters, true, true)); + mp::connect_engine_ptr connect_engine(new mp::connect_engine(state, true, true)); mp::connect ui(disp, parameters.name, game_config, gamechat, gamelist, *connect_engine); diff --git a/src/multiplayer_configure.cpp b/src/multiplayer_configure.cpp index f49ca3ef9dbb..70d54117d664 100644 --- a/src/multiplayer_configure.cpp +++ b/src/multiplayer_configure.cpp @@ -29,6 +29,7 @@ #include "multiplayer_configure.hpp" #include "filesystem.hpp" #include "log.hpp" +#include "saved_game.hpp" #include "wml_exception.hpp" #include "wml_separators.hpp" #include "formula_string_utils.hpp" @@ -43,7 +44,7 @@ static lg::log_domain log_mp_configure("mp/configure"); namespace mp { -configure::configure(game_display& disp, const config &cfg, chat& c, config& gamelist, const mp_game_settings& params, bool local_players_only) : +configure::configure(game_display& disp, const config &cfg, chat& c, config& gamelist, saved_game& game, bool local_players_only) : ui(disp, _("Configure Game"), cfg, c, gamelist), local_players_only_(local_players_only), @@ -90,7 +91,8 @@ configure::configure(game_display& disp, const config &cfg, chat& c, config& gam entry_points_(), show_entry_points_(false), force_use_map_settings_check_(true), - parameters_(params), + state_(game), + parameters_(state_.mp_settings()), options_manager_(cfg, disp, &options_pane_right_, preferences::options()) { // Build the list of scenarios to play @@ -146,7 +148,7 @@ configure::configure(game_display& disp, const config &cfg, chat& c, config& gam xp_modifier_slider_.set_increment(10); xp_modifier_slider_.set_help_string(_("The amount of experience a unit needs to advance")); - if (parameters_.scenario_data["force_lock_settings"].to_bool()) { + if (state_.get_starting_pos()["force_lock_settings"].to_bool()) { use_map_settings_.enable(false); use_map_settings_.set_check(true); } else { @@ -202,7 +204,7 @@ configure::configure(game_display& disp, const config &cfg, chat& c, config& gam } options_manager_.set_era(parameters_.mp_era); - options_manager_.set_scenario(parameters_.mp_scenario); + options_manager_.set_scenario(state_.get_scenario_id()/*parameters_.mp_scenario*/); options_manager_.set_modifications(parameters_.active_mods); options_manager_.init_widgets(); @@ -325,9 +327,7 @@ void configure::process_event() const config& scenario = *entry_points_[entry_points_combo_.selected()]; parameters_.hash = scenario.hash(); - parameters_.scenario_data = scenario; - parameters_.mp_scenario = scenario["id"].str(); - parameters_.mp_scenario_name = scenario["name"].str(); + state_.set_scenario(scenario); force_use_map_settings_check_ = true; } @@ -412,15 +412,15 @@ void configure::process_event() // If the map settings are wanted use them, // if not properly defined fall back to the default settings turns_slider_.set_value(map_settings ? - settings::get_turns(parameters_.scenario_data["turns"]) : + settings::get_turns(state_.get_starting_pos()["turns"]) : preferences::turns()); xp_modifier_slider_.set_value(map_settings ? - settings::get_xp_modifier(parameters_.scenario_data["experience_modifier"]) : + settings::get_xp_modifier(state_.get_starting_pos()["experience_modifier"]) : preferences::xp_modifier()); random_start_time_.set_check(map_settings ? - parameters_.scenario_data["random_start_time"].to_bool(true) : + state_.get_starting_pos()["random_start_time"].to_bool(true) : preferences::random_start_time()); // These are per player, always show values of player 1. @@ -430,7 +430,7 @@ void configure::process_event() * This might change in the future. * NOTE when 'load game' is selected there are no sides. */ - config::const_child_itors sides = parameters_.scenario_data.child_range("side"); + config::const_child_itors sides = state_.get_starting_pos().child_range("side"); if (sides.first != sides.second) { const config &cfg = *sides.first; diff --git a/src/multiplayer_configure.hpp b/src/multiplayer_configure.hpp index c2eb5a2c7450..302cd7e5d2af 100644 --- a/src/multiplayer_configure.hpp +++ b/src/multiplayer_configure.hpp @@ -25,12 +25,15 @@ #include "tooltips.hpp" #include "mp_options.hpp" +class saved_game; namespace mp { class configure : public mp::ui { public: - configure(game_display& dist, const config& game_config, chat& c, config& gamelist, const mp_game_settings& params, bool local_players_only); + ///gives the user the option to adjust the passed saved_game + ///Call get_parameters to finalize; + configure(game_display& dist, const config& game_config, chat& c, config& gamelist, saved_game& game, bool local_players_only); ~configure(); const mp_game_settings& get_parameters(); @@ -92,8 +95,8 @@ class configure : public mp::ui bool show_entry_points_; bool force_use_map_settings_check_; - mp_game_settings parameters_; - + saved_game& state_; + mp_game_settings& parameters_; options::manager options_manager_; }; diff --git a/src/multiplayer_connect.cpp b/src/multiplayer_connect.cpp index a6a067d34135..2cfdb3623313 100644 --- a/src/multiplayer_connect.cpp +++ b/src/multiplayer_connect.cpp @@ -395,7 +395,7 @@ connect::connect(game_display& disp, const std::string& game_name, if (get_result() == QUIT || get_result() == CREATE) { return; } - if (engine_.level()["id"].empty()) { + if (engine_.scenario()["id"].empty()) { throw config::error(_("The scenario is invalid because it has no id.")); } @@ -426,7 +426,7 @@ connect::connect(game_display& disp, const std::string& game_name, side_pos_y_offset += 60; } - append_to_title(" — " + engine_.level()["name"].t_str()); + append_to_title(" — " + engine_.scenario()["name"].t_str()); gold_title_label_.hide(params().saved_game); income_title_label_.hide(params().saved_game); diff --git a/src/multiplayer_connect_engine.cpp b/src/multiplayer_connect_engine.cpp index 12fc0256fad6..d824cc67b22a 100644 --- a/src/multiplayer_connect_engine.cpp +++ b/src/multiplayer_connect_engine.cpp @@ -69,12 +69,11 @@ const std::string attributes_to_trim[] = { namespace mp { -connect_engine::connect_engine(game_display& disp, saved_game& state, - const mp_game_settings& params, const bool local_players_only, - const bool first_scenario) : +connect_engine::connect_engine(saved_game& state, + const bool local_players_only, const bool first_scenario) : level_(), state_(state), - params_(params), + params_(state.mp_settings()), default_controller_(local_players_only ? CNTR_LOCAL: CNTR_NETWORK), local_players_only_(local_players_only), first_scenario_(first_scenario), @@ -87,12 +86,12 @@ connect_engine::connect_engine(game_display& disp, saved_game& state, connected_users_() { // Initial level config from the mp_game_settings. - level_ = initial_level_config(disp, params_, state_); + level_ = initial_level_config(state_); if (level_.empty()) { return; } - force_lock_settings_ = level_["force_lock_settings"].to_bool(); + force_lock_settings_ = scenario()["force_lock_settings"].to_bool(); // Original level sides. config::child_itors sides = current_config()->child_range("side"); @@ -208,25 +207,10 @@ connect_engine::~connect_engine() } config* connect_engine::current_config() { - config* cfg_level = NULL; - - // It might make sense to invent a mechanism of some sort to check - // whether a config node contains information - // that you can load from(side information, specifically). - config &snapshot = level_.child("snapshot"); - if (snapshot && snapshot.child("side")) { - // Savegame. - cfg_level = &snapshot; - } else if (!level_.child("side")) { - // Start-of-scenario save, - // the info has to be taken from the starting_pos. - cfg_level = &state_.replay_start(); - } else { - // Fresh game, no snapshot available. - cfg_level = &level_; - } - - return cfg_level; + if(config& s = scenario()) + return &s; + else + return NULL; } void connect_engine::import_user(const std::string& name, const bool observer, @@ -312,25 +296,18 @@ void connect_engine::update_level() { DBG_MP << "updating level" << std::endl; - level_.clear_children("side"); + scenario().clear_children("side"); BOOST_FOREACH(side_engine_ptr side, side_engines_) { - level_.add_child("side", side->new_config()); + scenario().add_child("side", side->new_config()); } } -void connect_engine::update_and_send_diff(bool update_time_of_day) +void connect_engine::update_and_send_diff(bool /*update_time_of_day*/) { config old_level = level_; update_level(); - if (update_time_of_day) { - // Set random start ToD. - // This doesn't do anything since the "const" parameter is now really a const. - // We currently resolve the random tod on all clients seperately with the synced rng. - tod_manager tod_mng(level_); - } - config diff = level_.get_diff(old_level); if (!diff.empty()) { config scenario_diff; @@ -543,7 +520,7 @@ void connect_engine::start_game_commandline( if (cmdline_opts.multiplayer_turns) { DBG_MP << "\tsetting turns: " << cmdline_opts.multiplayer_turns << std::endl; - level_["turns"] = *cmdline_opts.multiplayer_turns; + scenario()["turns"] = *cmdline_opts.multiplayer_turns; } BOOST_FOREACH(config &side, level_.child_range("side")) diff --git a/src/multiplayer_connect_engine.hpp b/src/multiplayer_connect_engine.hpp index cd958ee97814..d2049b09e6aa 100644 --- a/src/multiplayer_connect_engine.hpp +++ b/src/multiplayer_connect_engine.hpp @@ -43,8 +43,8 @@ typedef std::pair controller_option; class connect_engine { public: - connect_engine(game_display& disp, saved_game& state, - const mp_game_settings& params, const bool local_players_only, + connect_engine(saved_game& state, + const bool local_players_only, const bool first_scenario); ~connect_engine(); @@ -84,6 +84,15 @@ class connect_engine /* Setters & Getters */ const config& level() const { return level_; } + config& scenario() + { + if(config& scenario = level_.child("scenario")) + return scenario; + else if(config& snapshot = level_.child("snapshot")) + return snapshot; + else + throw "No scenariodata found"; + } const std::set& connected_users() const { return connected_users_; } const std::vector& user_team_names() diff --git a/src/multiplayer_create.cpp b/src/multiplayer_create.cpp index 5187094b4bbf..cf28ef3f2339 100644 --- a/src/multiplayer_create.cpp +++ b/src/multiplayer_create.cpp @@ -36,6 +36,8 @@ #include "minimap.hpp" #include "multiplayer_create.hpp" #include "filesystem.hpp" +#include "resources.hpp" +#include "savegame.hpp" #include "log.hpp" #include "wml_exception.hpp" #include "wml_separators.hpp" @@ -253,11 +255,25 @@ void create::process_event() } if (load_game_.pressed()) { - engine_.prepare_for_saved_game(); + try + { + savegame::loadgame load(disp_, + resources::config_manager->game_config(), engine_.get_state()); + load.load_multiplayer_game(); - set_result(LOAD_GAME); + engine_.prepare_for_saved_game(); + + set_result(LOAD_GAME); + + return; + } + catch (load_game_cancelled_exception) + { + } + catch(config::error&) + { + } - return; } bool update_mod_button_label = mod_selection_ != mods_menu_.selection(); diff --git a/src/multiplayer_create_engine.cpp b/src/multiplayer_create_engine.cpp index 2fd70989d717..2a49db7339a7 100644 --- a/src/multiplayer_create_engine.cpp +++ b/src/multiplayer_create_engine.cpp @@ -344,7 +344,6 @@ create_engine::create_engine(game_display& disp, saved_game& state) : eras_(), mods_(), state_(state), - parameters_(), dependency_manager_(resources::config_manager->game_config(), disp.video()), generator_(NULL) { @@ -369,17 +368,17 @@ create_engine::create_engine(game_display& disp, saved_game& state) : init_extras(ERA); init_extras(MOD); - parameters_.saved_game = false; + state_.mp_settings().saved_game = false; BOOST_FOREACH (const std::string& str, preferences::modifications()) { if (resources::config_manager-> game_config().find_child("modification", "id", str)) - parameters_.active_mods.push_back(str); + state_.mp_settings().active_mods.push_back(str); } if (current_level_type_ != level::CAMPAIGN && current_level_type_ != level::SP_CAMPAIGN) { - dependency_manager_.try_modifications(parameters_.active_mods, true); + dependency_manager_.try_modifications(state_.mp_settings().active_mods, true); } reset_level_filters(); @@ -409,10 +408,8 @@ void create_engine::prepare_for_new_level() { DBG_MP << "preparing mp_game_settings for new level\n"; - parameters_.scenario_data = current_level().data(); - parameters_.hash = parameters_.scenario_data.hash(); - parameters_.mp_scenario = parameters_.scenario_data["id"].str(); - parameters_.mp_scenario_name = parameters_.scenario_data["name"].str(); + state_.set_scenario(current_level().data()); + state_.mp_settings().hash = current_level().data().hash(); } void create_engine::prepare_for_campaign(const std::string& difficulty) @@ -421,7 +418,6 @@ void create_engine::prepare_for_campaign(const std::string& difficulty) if (difficulty != "") { state_.classification().difficulty = difficulty; - parameters_.difficulty_define = difficulty; } state_.classification().campaign = current_level().data()["id"].str(); @@ -443,20 +439,20 @@ void create_engine::prepare_for_campaign(const std::string& difficulty) resources::config_manager->game_config().find_child( lexical_cast (game_classification::MULTIPLAYER), "id", current_level().data()["first_scenario"])); - - parameters_.mp_campaign = current_level().id(); } void create_engine::prepare_for_saved_game() { DBG_MP << "preparing mp_game_settings for saved game\n"; - parameters_.saved_game = true; - parameters_.scenario_data.clear(); + resources::config_manager->load_game_config_for_game(state_.classification()); + //The save migh be a start-of-scenario save so make sure we have the scenario data loaded. + state_.expand_scenario(); + state_.mp_settings().saved_game = true; utils::string_map i18n_symbols; i18n_symbols["login"] = preferences::login(); - parameters_.name = vgettext("$login|’s game", i18n_symbols); + state_.mp_settings().name = vgettext("$login|’s game", i18n_symbols); } void create_engine::apply_level_filter(const std::string &name) @@ -642,7 +638,7 @@ bool create_engine::toggle_current_mod() bool is_active = dependency_manager_.is_modification_active(current_mod_index_); dependency_manager_.try_modification_by_index(current_mod_index_, !is_active); - parameters_.active_mods = dependency_manager_.get_modifications(); + state_.mp_settings().active_mods = dependency_manager_.get_modifications(); return !is_active; } @@ -762,12 +758,12 @@ const depcheck::manager& create_engine::dependency_manager() const void create_engine::init_active_mods() { - parameters_.active_mods = dependency_manager_.get_modifications(); + state_.mp_settings().active_mods = dependency_manager_.get_modifications(); } std::vector& create_engine::active_mods() { - return parameters_.active_mods; + return state_.mp_settings().active_mods; } const mp_game_settings& create_engine::get_parameters() @@ -775,9 +771,9 @@ const mp_game_settings& create_engine::get_parameters() DBG_MP << "getting parameter values" << std::endl; int era_index = current_level().allow_era_choice() ? current_era_index_ : 0; - parameters_.mp_era = eras_[era_index]->id; + state_.mp_settings().mp_era = eras_[era_index]->id; - return parameters_; + return state_.mp_settings(); } void create_engine::init_all_levels() @@ -1061,4 +1057,9 @@ std::vector& return (extra_type == ERA) ? eras_ : mods_; } +saved_game& create_engine::get_state() +{ + return state_; +} + } // end namespace mp diff --git a/src/multiplayer_create_engine.hpp b/src/multiplayer_create_engine.hpp index 814a37fb2ad2..6f0af30289b6 100644 --- a/src/multiplayer_create_engine.hpp +++ b/src/multiplayer_create_engine.hpp @@ -223,6 +223,7 @@ class create_engine const mp_game_settings& get_parameters(); + saved_game& get_state(); private: create_engine(const create_engine&); void operator=(const create_engine&); @@ -268,7 +269,6 @@ class create_engine std::vector mods_; saved_game& state_; - mp_game_settings parameters_; depcheck::manager dependency_manager_; diff --git a/src/multiplayer_wait.cpp b/src/multiplayer_wait.cpp index 6486ac72a102..abfe716f8f22 100644 --- a/src/multiplayer_wait.cpp +++ b/src/multiplayer_wait.cpp @@ -224,7 +224,7 @@ void wait::join_game(bool observe) if (!download_res) { set_result(QUIT); return; - } else if (!level_["allow_new_game"].to_bool(true)) { + } else if (!get_scenario()["allow_new_game"].to_bool(true)) { set_result(PLAY); return; } @@ -235,10 +235,10 @@ void wait::join_game(bool observe) const config* campaign = &resources::config_manager-> game_config().find_child("campaign", "id", - level_.child(lexical_cast(game_classification::MULTIPLAYER))["mp_campaign"]); + level_.child("multiplayer")["mp_campaign"]); if (*campaign) { state_.classification().difficulty = - level_.child(lexical_cast(game_classification::MULTIPLAYER))["difficulty_define"].str(); + level_.child("multiplayer")["difficulty_define"].str(); state_.classification().campaign_define = (*campaign)["define"].str(); state_.classification().campaign_xtra_defines = @@ -251,7 +251,7 @@ void wait::join_game(bool observe) } // Add the map name to the title. - append_to_title(": " + level_["name"].t_str()); + append_to_title(": " + get_scenario()["name"].t_str()); if (!observe) { //search for an appropriate vacant slot. If a description is set @@ -260,7 +260,7 @@ void wait::join_game(bool observe) //available side. const config *side_choice = NULL; int side_num = -1, nb_sides = 0; - BOOST_FOREACH(const config &sd, level_.child_range("side")) + BOOST_FOREACH(const config &sd, get_scenario().child_range("side")) { if (sd["controller"] == "reserved" && sd["current_player"] == preferences::login()) { @@ -315,7 +315,7 @@ void wait::join_game(bool observe) } const bool lock_settings = - level_["force_lock_settings"].to_bool(); + get_scenario()["force_lock_settings"].to_bool(); const bool saved_game = level_.child("multiplayer")["savegame"].to_bool(); @@ -462,17 +462,17 @@ void wait::process_network_data(const config& data, const network::connection so LOG_RG << data.debug() << std::endl; //const int side = lexical_cast(change["side"]); - if (config & sidetochange = level_.find_child("side", "side", change["side"])) { + if (config & sidetochange = get_scenario().find_child("side", "side", change["side"])) { LOG_RG << "found side : " << sidetochange.debug() << std::endl; sidetochange.merge_with(change); LOG_RG << "changed to : " << sidetochange.debug() << std::endl; } else { LOG_RG << "change_controller didn't find any side!" << std::endl; } - } else if(data.child("side") || data.child("next_scenario")) { + } else if(data.has_child("scenario") || data.has_child("snapshot") || data.child("next_scenario")) { level_ = first_scenario_ ? data : data.child("next_scenario"); LOG_NW << "got some sides. Current number of sides = " - << level_.child_count("side") << ',' + << get_scenario().child_count("side") << ',' << data.child_count("side") << '\n'; generate_menu(); } @@ -486,7 +486,7 @@ void wait::generate_menu() std::vector details; std::vector playerlist; - BOOST_FOREACH(const config &sd, level_.child_range("side")) + BOOST_FOREACH(const config &sd, get_scenario().child_range("side")) { if (!sd["allow_player"].to_bool(true)) { continue; @@ -600,7 +600,7 @@ void wait::generate_menu() bool wait::has_level_data() const { if (first_scenario_) { - return level_.has_attribute("version") && level_.has_child("side"); + return level_.has_attribute("version") && get_scenario().has_child("side"); } else { return level_.has_child("next_scenario"); } @@ -634,5 +634,26 @@ bool wait::download_level_data() return true; } +config& wait::get_scenario() +{ + + if(config& scenario = level_.child("scenario")) + return scenario; + else if(config& snapshot = level_.child("snapshot")) + return snapshot; + else + return level_; +} + +const config& wait::get_scenario() const +{ + if(const config& scenario = level_.child("scenario")) + return scenario; + else if(const config& snapshot = level_.child("snapshot")) + return snapshot; + else + return level_; +} + } // namespace mp diff --git a/src/multiplayer_wait.hpp b/src/multiplayer_wait.hpp index fe39ba24310a..066166f9e91f 100644 --- a/src/multiplayer_wait.hpp +++ b/src/multiplayer_wait.hpp @@ -68,6 +68,8 @@ class wait : public ui void generate_menu(); bool has_level_data() const; bool download_level_data(); + config& get_scenario(); + const config& get_scenario() const; gui::button cancel_button_; gui::label start_label_; diff --git a/src/playcampaign.cpp b/src/playcampaign.cpp index 445681e3b6b9..3fbe718c3bd6 100644 --- a/src/playcampaign.cpp +++ b/src/playcampaign.cpp @@ -435,8 +435,9 @@ LEVEL_RESULT play_game(game_display& disp, saved_game& gamestate, return QUIT; } - gamestate.set_scenario(gamestate.replay_start()); - gamestate.replay_start() = config(); + //The host shoudl send teh complete savegame now that also contains teh carryvoer seides start- + //gamestate.set_scenario(gamestate.replay_start()); + //gamestate.replay_start() = config(); // Retain carryover_sides_start, as the config from the server // doesn't contain it. //TODO: enable this again or make mp_wait not change carryover sides start. @@ -446,44 +447,37 @@ LEVEL_RESULT play_game(game_display& disp, saved_game& gamestate, gamestate.expand_scenario(); if (io_type == IO_SERVER && gamestate.valid()) { - config * scenario = &gamestate.get_starting_pos(); - mp_game_settings& params = gamestate.mp_settings(); - // A hash have to be generated using an unmodified // scenario data. - params.hash = scenario->hash(); + gamestate.mp_settings().hash = starting_pos.hash(); // Apply carryover before passing a scenario data to the // mp::connect_engine. - gamestate.expand_carryover(); + //WHY??? + //gamestate.expand_carryover(); //We don't merge WML until start of next scenario, but if we want to allow user to disable MP ui in transition, //then we have to move "allow_new_game" attribute over now. - bool allow_new_game_flag = (*scenario)["allow_new_game"].to_bool(true); + bool allow_new_game_flag = starting_pos["allow_new_game"].to_bool(true); if (gamestate.carryover_sides_start.child_or_empty("end_level_data").child_or_empty("next_scenario_settings").has_attribute("allow_new_game")) { allow_new_game_flag = gamestate.carryover_sides_start.child_or_empty("end_level_data").child("next_scenario_settings")["allow_new_game"].to_bool(); } - params.scenario_data = *scenario; - params.scenario_data["next_underlying_unit_id"] = n_unit::id_manager::instance().get_save_id(); - params.mp_scenario = (*scenario)["id"].str(); - params.mp_scenario_name = (*scenario)["name"].str(); - params.num_turns = (*scenario)["turns"].to_int(-1); - params.saved_game = false; - params.use_map_settings = - (*scenario)["force_lock_settings"].to_bool(); + gamestate.mp_settings().num_turns = starting_pos["turns"].to_int(-1); + gamestate.mp_settings().saved_game = false; + gamestate.mp_settings().use_map_settings = starting_pos["force_lock_settings"].to_bool(); mp::connect_engine_ptr - connect_engine(new mp::connect_engine(disp, gamestate, - params, !network_game, false)); + connect_engine(new mp::connect_engine(gamestate, + !network_game, false)); if (allow_new_game_flag || (game_config::debug && network::nconnections() == 0)) { // Opens mp::connect dialog to allow users to // make an adjustments for scenario. // TODO: Fix this so that it works when network::nconnections() > 0 as well. mp::ui::result connect_res = mp::goto_mp_connect(disp, - *connect_engine, game_config, params.name); + *connect_engine, game_config, gamestate.mp_settings().name); if (connect_res == mp::ui::QUIT) { return QUIT; } @@ -493,9 +487,6 @@ LEVEL_RESULT play_game(game_display& disp, saved_game& gamestate, start_game(mp::connect_engine::FORCE_IMPORT_USERS); } - starting_pos = gamestate.replay_start(); - scenario = &starting_pos; - // TODO: move this code to mp::connect_engine // in order to send generated data to the network // before starting the game. diff --git a/src/saved_game.cpp b/src/saved_game.cpp index 2ae2f8c00b3f..15f31f7d60c3 100644 --- a/src/saved_game.cpp +++ b/src/saved_game.cpp @@ -287,3 +287,51 @@ void saved_game::convert_to_start_save() carryover_sides = config(); remove_snapshot(); } + +config saved_game::to_config() +{ + //TODO: remove this code dublication with write_... functions. + config r = classification_.to_config(); + if(!this->replay_start_.empty()) + { + r.add_child("replay_start", replay_start_); + } + if(!this->replay_data.empty()) + { + r.add_child("replay", replay_data); + } + if(starting_pos_type_ == STARTINGPOS_SNAPSHOT) + { + r.add_child("snapshot", starting_pos_); + } + else if(starting_pos_type_ == STARTINGPOS_SCENARIO) + { + r.add_child("scenario", starting_pos_); + } + if(!this->carryover_sides.empty()) + { + r.add_child("carryover_sides", carryover_sides); + } + if(!this->carryover_sides_start.empty()) + { + r.add_child("carryover_sides_start", carryover_sides_start); + } + + if (classification_.campaign_type == game_classification::MULTIPLAYER) { + r.add_child("multiplayer", mp_settings_.to_config()); + } + return r; +} + +std::string saved_game::get_scenario_id() +{ + if(this->starting_pos_type_ == STARTINGPOS_SNAPSHOT + || this->starting_pos_type_ == STARTINGPOS_SCENARIO) + { + return starting_pos_["id"]; + } + else + { + return carryover_sides_start["next_scenario"]; + } +} diff --git a/src/saved_game.hpp b/src/saved_game.hpp index d20c6ef8ffb1..4dfea9ace590 100644 --- a/src/saved_game.hpp +++ b/src/saved_game.hpp @@ -29,6 +29,7 @@ class saved_game void write_general_info(config_writer& out) const; void write_carryover(config_writer& out) const; void write_starting_pos(config_writer& out) const; + config to_config(); void remove_old_scenario(); game_classification& classification() { return classification_; } const game_classification& classification() const { return classification_; } @@ -53,6 +54,7 @@ class saved_game void convert_to_start_save(); const config& get_replay_starting_pos(); + std::string get_scenario_id(); config& get_starting_pos(); config& replay_start() { return replay_start_; } const config& replay_start() const { return replay_start_; }