From 6151fea80487789d56a62452b79b8e23e2583347 Mon Sep 17 00:00:00 2001 From: Charles Dang Date: Fri, 6 Oct 2017 11:38:25 +1100 Subject: [PATCH] Saved Game: formatting cleanup --- src/saved_game.cpp | 493 ++++++++++++++++++++++++--------------------- 1 file changed, 264 insertions(+), 229 deletions(-) diff --git a/src/saved_game.cpp b/src/saved_game.cpp index 3e97772aa7cb4..f34c23031268d 100644 --- a/src/saved_game.cpp +++ b/src/saved_game.cpp @@ -12,53 +12,68 @@ */ /** - Some information about savefiles: - A saveile can contain: - * General information (toplevel atributes, [multiplayer]) - This is present in all savefiles - * [statistics] - This is present in all savefiles but it's not handled by playcampaign/play_controller/saved_game. - It's handled by savegame.cpp - * [snapshot] - If a savegame was saved during a scenario this contains a snapshot of the game at the point when it was saved. - * [carryover_sides_start] - At start-of-scenrio saves this contains data from the previous scenario that was preserved - * [carryover_sides] - In savefile made during the game, this tag contains data from [carryover_sides_start] that was not used in the current scenario but should be saved for a next scenario - * [replay_start] - A snapshot made very early to replay the game from. - * [replay] - A record of game actions that was made between the creation of [replay_start] and [snapshot]. - - - - The following types of savegames are known: - * Start of scenario savefiles - These files only contain general information, statistics, and [carryover_sides_start] - When these saves are loaded, the scenario data is loaded form the game config using the next_scenario attribute from [carryover_sides_start] - * Expanded Start of scenario savefiles - Similar to normal Start-of-scenario savefiles, but the also contain a [scenario] that contains the scenario data. - This type is only used internally and usually doesn't get written to the disk. - * Ingame savefile - These files contain general information, statistics, [snapshot], [replay], [replay_start], [snapshot], [carryover_sides] - These files don't contain a [carryover_sides_start] because both starting points ([replay_start] and [snapshot]) - were made after [carryover_sides_start] was merged into the scenario. - * Replay savefiles - Like a Ingame save made during linger mode, but without the [snapshot] -*/ + * Some information about savefiles: + * + * A savefile can contain: + * + * - General information (toplevel atributes, [multiplayer]) + * This is present in all savefiles + * + * - [statistics] + * This is present in all savefiles but it's not handled by playcampaign/play_controller/saved_game. + * It's handled by savegame.cpp + * + * - [snapshot] + * If a savegame was saved during a scenario this contains a snapshot of the game at the point when + * it was saved. + * + * - [carryover_sides_start] + * At start-of-scenario saves this contains data from the previous scenario that was preserved. + * + * - [carryover_sides] + * In savefile made during the game, this tag contains data from [carryover_sides_start] that was not + * used in the current scenario but should be saved for a next scenario + * + * - [replay_start] + * A snapshot made very early to replay the game from. + * + * - [replay] + * A record of game actions that was made between the creation of [replay_start] and [snapshot]. + * + * + * The following types of savegames are known: + * + * - Start of scenario savefiles + * These files only contain general information, statistics, and [carryover_sides_start]. When these + * saves are loaded, the scenario data is loaded form the game config using the next_scenario attribute + * from [carryover_sides_start]. + * + * - Expanded Start of scenario savefiles + * Similar to normal Start-of-scenario savefiles, but the also contain a [scenario] that contains the + * scenario data. This type is only used internally and usually doesn't get written to the disk. + * + * - In-game savefile + * These files contain general information, statistics, [snapshot], [replay], [replay_start], [snapshot], + * and [carryover_sides]. These files don't contain a [carryover_sides_start] because both starting points + * ([replay_start] and [snapshot]) were made after [carryover_sides_start] was merged into the scenario. + * + * - Replay savefiles + * Like a in-game save made during linger mode, but without the [snapshot]. + */ #include "saved_game.hpp" + #include "carryover.hpp" +#include "config.hpp" #include "cursor.hpp" -#include "log.hpp" +#include "formula/string_utils.hpp" #include "game_config_manager.hpp" #include "generators/map_create.hpp" -#include "statistics.hpp" +#include "log.hpp" #include "serialization/binary_or_text.hpp" -#include "variable_info.hpp" -#include "formula/string_utils.hpp" -#include "config.hpp" +#include "statistics.hpp" #include "variable.hpp" // for config_variable_set +#include "variable_info.hpp" #include #include @@ -80,7 +95,6 @@ saved_game::saved_game() , starting_pos_() , replay_data_() { - } saved_game::saved_game(config cfg) @@ -117,10 +131,10 @@ void saved_game::set_carryover_sides_start(config carryover_sides_start) void saved_game::set_random_seed() { - if(has_carryover_expanded_ || !carryover_["random_seed"].empty()) - { + if(has_carryover_expanded_ || !carryover_["random_seed"].empty()) { return; } + carryover_["random_seed"] = rand(); carryover_["random_calls"] = 0; } @@ -129,34 +143,33 @@ void saved_game::write_config(config_writer& out) const { write_general_info(out); write_starting_pos(out); - if(!this->replay_start_.empty()) - { + + if(!this->replay_start_.empty()) { out.write_child("replay_start", replay_start_); } + out.open_child("replay"); replay_data_.write(out); + out.close_child("replay"); write_carryover(out); } void saved_game::write_starting_pos(config_writer& out) const { - if(starting_pos_type_ == STARTINGPOS_SNAPSHOT) - { + if(starting_pos_type_ == STARTINGPOS_SNAPSHOT) { out.write_child("snapshot", starting_pos_); - } - else if(starting_pos_type_ == STARTINGPOS_SCENARIO) - { + } else if(starting_pos_type_ == STARTINGPOS_SCENARIO) { out.write_child("scenario", starting_pos_); } } + void saved_game::write_carryover(config_writer& out) const { assert(not_corrupt()); out.write_child(has_carryover_expanded_ ? "carryover_sides" : "carryover_sides_start", carryover_); } - void saved_game::write_general_info(config_writer& out) const { out.write(classification_.to_config()); @@ -167,33 +180,36 @@ void saved_game::set_defaults() { const bool is_loaded_game = this->starting_pos_type_ != STARTINGPOS_SCENARIO; const bool is_multiplayer_tag = classification().get_tagname() == "multiplayer"; + static const std::vector team_defaults { "carryover_percentage", "carryover_add", }; - for(config& side : starting_pos_.child_range("side")) - { - // Set save_id default value directly after loading to its default to prevent different default behaviour in mp_connect code and sp code. - if(side["save_id"].empty()) - { + + for(config& side : starting_pos_.child_range("side")) { + // Set save_id default value directly after loading to its default to prevent different default behaviour in + // mp_connect code and sp code. + if(side["save_id"].empty()) { side["save_id"] = side["id"]; } - if(!is_multiplayer_tag && side["side_name"].blank()) - { + + if(!is_multiplayer_tag && side["side_name"].blank()) { side["side_name"] = side["name"]; } - if(!is_loaded_game && !side["current_player"].empty()) - { - ERR_NG << "Removed invalid 'current_player' attribute from [side] while loading a scenario. Consider using 'side_name' instead\n"; + + if(!is_loaded_game && !side["current_player"].empty()) { + ERR_NG << "Removed invalid 'current_player' attribute from [side] while loading a scenario. Consider using " + "'side_name' instead\n"; + side["current_player"] = config::attribute_value(); } + // Set some team specific values to their defaults specified in scenario - for(const std::string& att_name : team_defaults) - { + for(const std::string& att_name : team_defaults) { const config::attribute_value* scenario_value = starting_pos_.get(att_name); config::attribute_value& team_value = side[att_name]; - if(scenario_value && team_value.empty()) - { + + if(scenario_value && team_value.empty()) { team_value = *scenario_value; } } @@ -202,28 +218,32 @@ void saved_game::set_defaults() void saved_game::expand_scenario() { - if(this->starting_pos_type_ == STARTINGPOS_NONE && !has_carryover_expanded_) - { + if(this->starting_pos_type_ == STARTINGPOS_NONE && !has_carryover_expanded_) { game_config_manager::get()->load_game_config_for_game(this->classification()); + const config& game_config = game_config_manager::get()->game_config(); - const config& scenario = game_config.find_child(classification().get_tagname(), "id", carryover_["next_scenario"]); - if(scenario) - { + const config& scenario = + game_config.find_child(classification().get_tagname(), "id", carryover_["next_scenario"]); + + if(scenario) { this->starting_pos_type_ = STARTINGPOS_SCENARIO; this->starting_pos_ = scenario; + // A hash has to be generated using an unmodified scenario data. mp_settings_.hash = scenario.hash(); // Add addon_id information if it exists. - if (!scenario["addon_id"].empty() && scenario["require_scenario"].to_bool(false)) { - mp_settings_.update_addon_requirements(config {"id",scenario["addon_id"], "version", scenario["addon_version"], "min_version", scenario["addon_min_version"]}); + if(!scenario["addon_id"].empty() && scenario["require_scenario"].to_bool(false)) { + mp_settings_.update_addon_requirements(config { + "id", scenario["addon_id"], + "version", scenario["addon_version"], + "min_version", scenario["addon_min_version"] + }); } update_label(); set_defaults(); - } - else - { + } else { this->starting_pos_type_ = STARTINGPOS_INVALID; this->starting_pos_ = config(); } @@ -232,135 +252,152 @@ void saved_game::expand_scenario() namespace { - bool variable_to_bool(const config& vars, const std::string& expression) +bool variable_to_bool(const config& vars, const std::string& expression) +{ + std::string res = utils::interpolate_variables_into_string(expression, config_variable_set(vars)); + return res == "true" || res == "yes" || res == "1"; +} + +// helper objects for saved_game::expand_mp_events() +struct modevents_entry +{ + modevents_entry(const std::string& _type, const std::string& _id) + : type(_type) + , id(_id) { - std::string res = utils::interpolate_variables_into_string(expression, config_variable_set(vars)); - return res == "true" || res == "yes" || res == "1"; } - //helper objects for saved_game::expand_mp_events() - struct modevents_entry + std::string type; + std::string id; +}; + +struct modevents_entry_for +{ + // This typedef is used by boost. + typedef modevents_entry result_type; + + modevents_entry_for(const std::string& type) + : type_(type) { - modevents_entry(const std::string& _type, const std::string& _id) : type(_type), id(_id) {} - std::string type; - std::string id; - }; - struct modevents_entry_for + } + + modevents_entry operator()(const std::string& id) const { - //this typedef is used by boost. - typedef modevents_entry result_type; - modevents_entry_for(const std::string& type ) : type_(type) {} - modevents_entry operator()(const std::string& id) const - { - return modevents_entry(type_, id); - } - private: - std::string type_; - }; -} + return modevents_entry(type_, id); + } + +private: + std::string type_; +}; + +} // end anon namespace void saved_game::load_mod(const std::string& type, const std::string& id) { - if(const config& cfg = game_config_manager::get()-> - game_config().find_child(type, "id", id)) - { - // Note the addon_id if this mod is required to play the game in mp + if(const config& cfg = game_config_manager::get()->game_config().find_child(type, "id", id)) { + // Note the addon_id if this mod is required to play the game in mp. std::string require_attr = "require_" + type; - bool require_default = (type == "era"); // By default, eras have "require_era = true", and mods have "require_modification = false" - if (!cfg["addon_id"].empty() && cfg[require_attr].to_bool(require_default)) { - mp_settings_.update_addon_requirements(config {"id",cfg["addon_id"], "version", cfg["addon_version"], "min_version", cfg["addon_min_version"]}); + + // By default, eras have "require_era = true", and mods have "require_modification = false". + bool require_default = (type == "era"); + + if(!cfg["addon_id"].empty() && cfg[require_attr].to_bool(require_default)) { + mp_settings_.update_addon_requirements(config{ + "id", cfg["addon_id"], "version", cfg["addon_version"], "min_version", cfg["addon_min_version"]}); } // Copy events - for(const config& modevent : cfg.child_range("event")) - { - if(modevent["enable_if"].empty() || variable_to_bool(carryover_.child_or_empty("variables"), modevent["enable_if"])) - { + for(const config& modevent : cfg.child_range("event")) { + if(modevent["enable_if"].empty() + || variable_to_bool(carryover_.child_or_empty("variables"), modevent["enable_if"]) + ) { this->starting_pos_.add_child("event", modevent); } } + // Copy lua - for(const config& modlua : cfg.child_range("lua")) - { + for(const config& modlua : cfg.child_range("lua")) { this->starting_pos_.add_child("lua", modlua); } + // Copy load_resource - for(const config& load_resource : cfg.child_range("load_resource")) - { + for(const config& load_resource : cfg.child_range("load_resource")) { this->starting_pos_.add_child("load_resource", load_resource); } - } - else - { - //TODO: A user message instead? - ERR_NG << "Couldn't find [" << type<< "] with id=" << id <starting_pos_type_ == STARTINGPOS_SCENARIO && !this->starting_pos_["has_mod_events"].to_bool(false)) { std::vector mods; std::set loaded_resources; - boost::copy( mp_settings_.active_mods - | boost::adaptors::transformed(modevents_entry_for("modification")) - , std::back_inserter(mods) ); - if(!mp_settings_.mp_era.empty()) //We don't want the error message below if there is no era (= if this is a sp game) - { mods.emplace_back("era", mp_settings_.mp_era); } - if(!classification_.campaign.empty()) - { mods.emplace_back("campaign", classification_.campaign); } - - // In the first iteration mod contains no [resource]s in all other iterations, mods contains only [resource]s + + boost::copy(mp_settings_.active_mods | boost::adaptors::transformed(modevents_entry_for("modification")), + std::back_inserter(mods)); + + // We don't want the error message below if there is no era (= if this is a sp game). + if(!mp_settings_.mp_era .empty()) { + mods.emplace_back("era", mp_settings_.mp_era); + } + + if(!classification_.campaign.empty()) { + mods.emplace_back("campaign", classification_.campaign); + } + + // In the first iteration mod contains no [resource]s in all other iterations, mods contains only [resource]s. do { - for(modevents_entry& mod : mods) - { + for(modevents_entry& mod : mods) { load_mod(mod.type, mod.id); } + mods.clear(); - for(const config& cfg : starting_pos_.child_range("load_resource")) - { + + for(const config& cfg : starting_pos_.child_range("load_resource")) { if(loaded_resources.find(cfg["id"].str()) == loaded_resources.end()) { mods.emplace_back("resource", cfg["id"].str()); + loaded_resources.insert(cfg["id"].str()); } } + starting_pos_.clear_children("load_resource"); } while(!mods.empty()); + this->starting_pos_["has_mod_events"] = true; } } void saved_game::expand_mp_options() { - if(starting_pos_type_ == STARTINGPOS_SCENARIO && !has_carryover_expanded_) - { + if(starting_pos_type_ == STARTINGPOS_SCENARIO && !has_carryover_expanded_) { std::vector mods; - boost::copy( mp_settings_.active_mods - | boost::adaptors::transformed(modevents_entry_for("modification")) - , std::back_inserter(mods) ); + boost::copy(mp_settings_.active_mods | boost::adaptors::transformed(modevents_entry_for("modification")), + std::back_inserter(mods)); + mods.emplace_back("era", mp_settings_.mp_era); mods.emplace_back("multiplayer", get_scenario_id()); mods.emplace_back("campaign", classification().campaign); config& variables = carryover_.child_or_add("variables"); - for(modevents_entry& mod : mods) - { - if(const config& cfg = this->mp_settings().options.find_child(mod.type, "id", mod.id)) - { + + for(modevents_entry& mod : mods) { + if(const config& cfg = this->mp_settings().options.find_child(mod.type, "id", mod.id)) { // Parse old [option] tag range syntax. - for(const config& option : cfg.child_range("option")) - { - try - { + for(const config& option : cfg.child_range("option")) { + try { variable_access_create(option["id"], variables).as_scalar() = option["value"]; - } - catch(const invalid_variablename_exception&) - { + } catch(const invalid_variablename_exception&) { ERR_NG << "variable " << option["id"] << "cannot be set to " << option["value"] << std::endl; } } @@ -373,10 +410,8 @@ void saved_game::expand_mp_options() ERR_NG << "variable " << option.first << "cannot be set to " << option.second << std::endl; } } - } - else - { - LOG_NG << "Couldn't find [" << mod.type<< "] with id=" << mod.id << " for [option]s" << std::endl; + } else { + LOG_NG << "Couldn't find [" << mod.type << "] with id=" << mod.id << " for [option]s" << std::endl; } } } @@ -385,23 +420,24 @@ void saved_game::expand_mp_options() void saved_game::expand_random_scenario() { expand_scenario(); - if(this->starting_pos_type_ == STARTINGPOS_SCENARIO) - { + + if(this->starting_pos_type_ == STARTINGPOS_SCENARIO) { // If the entire scenario should be randomly generated - if(!starting_pos_["scenario_generation"].empty()) - { + if(!starting_pos_["scenario_generation"].empty()) { LOG_NG << "randomly generating scenario...\n"; const cursor::setter cursor_setter(cursor::WAIT); - config scenario_new = random_generate_scenario(starting_pos_["scenario_generation"], - starting_pos_.child("generator")); - //Preserve "story" form the scenario toplevel. - for(config& story : starting_pos_.child_range("story")) - { + config scenario_new = + random_generate_scenario(starting_pos_["scenario_generation"], starting_pos_.child("generator")); + + // Preserve "story" form the scenario toplevel. + for(config& story : starting_pos_.child_range("story")) { scenario_new.add_child("story", story); } + scenario_new["id"] = starting_pos_["id"]; starting_pos_ = scenario_new; + update_label(); set_defaults(); } @@ -417,8 +453,8 @@ void saved_game::expand_random_scenario() LOG_NG << "randomly generating map...\n"; const cursor::setter cursor_setter(cursor::WAIT); - starting_pos_["map_data"] = random_generate_map( - starting_pos_["map_generation"], starting_pos_.child("generator")); + starting_pos_["map_data"] = + random_generate_map(starting_pos_["map_generation"], starting_pos_.child("generator")); } } } @@ -426,13 +462,11 @@ void saved_game::expand_random_scenario() void saved_game::expand_carryover() { expand_scenario(); - if(this->starting_pos_type_ == STARTINGPOS_SCENARIO && !has_carryover_expanded_) - { + if(this->starting_pos_type_ == STARTINGPOS_SCENARIO && !has_carryover_expanded_) { carryover_info sides(carryover_); sides.transfer_to(get_starting_pos()); - for(config& side_cfg : get_starting_pos().child_range("side")) - { + for(config& side_cfg : get_starting_pos().child_range("side")) { sides.transfer_all_to(side_cfg); } @@ -450,6 +484,7 @@ config& saved_game::set_snapshot(config snapshot) { this->starting_pos_type_ = STARTINGPOS_SNAPSHOT; this->starting_pos_.swap(snapshot); + return this->starting_pos_; } @@ -457,7 +492,9 @@ void saved_game::set_scenario(config scenario) { this->starting_pos_type_ = STARTINGPOS_SCENARIO; this->starting_pos_.swap(scenario); + has_carryover_expanded_ = false; + update_label(); } @@ -472,79 +509,80 @@ config& saved_game::get_starting_pos() return starting_pos_; } - const config& saved_game::get_replay_starting_pos() { - if(!replay_start_.empty()) - { + if(!replay_start_.empty()) { return replay_start_; } - if(!has_carryover_expanded_) - { - //Try to load the scenario form game config or from [scenario] if there is no [replay_start] + + if(!has_carryover_expanded_) { + // Try to load the scenario form game config or from [scenario] if there is no [replay_start] expand_scenario(); expand_carryover(); } - if(starting_pos_type_ == STARTINGPOS_SCENARIO) - { + + if(starting_pos_type_ == STARTINGPOS_SCENARIO) { return starting_pos_; } + return this->replay_start_.child("some_non_existet_invalid"); } void saved_game::convert_to_start_save() { assert(starting_pos_type_ == STARTINGPOS_SNAPSHOT); + carryover_info sides(starting_pos_, true); + sides.merge_old_carryover(carryover_info(carryover_)); sides.rng().rotate_random(); + carryover_ = sides.to_config(); + has_carryover_expanded_ = false; + replay_data_ = replay_recorder_base(); replay_start_ = config(); + remove_snapshot(); } config saved_game::to_config() const { - //TODO: remove this code dublication with write_... functions. + // TODO: remove this code duplication with write_... functions. config r = classification_.to_config(); - if(!this->replay_start_.empty()) - { + + if(!this->replay_start_.empty()) { r.add_child("replay_start", replay_start_); } + replay_data_.write(r.add_child("replay")); - if(starting_pos_type_ == STARTINGPOS_SNAPSHOT) - { + if(starting_pos_type_ == STARTINGPOS_SNAPSHOT) { r.add_child("snapshot", starting_pos_); - } - else if(starting_pos_type_ == STARTINGPOS_SCENARIO) - { + } else if(starting_pos_type_ == STARTINGPOS_SCENARIO) { r.add_child("scenario", starting_pos_); } - r.add_child(has_carryover_expanded_ ? "carryover_sides" : "carryover_sides_start" , carryover_); + + r.add_child(has_carryover_expanded_ ? "carryover_sides" : "carryover_sides_start", carryover_); r.add_child("multiplayer", mp_settings_.to_config()); + return r; } std::string saved_game::get_scenario_id() { std::string scenario_id; - if(this->starting_pos_type_ == STARTINGPOS_SNAPSHOT - || this->starting_pos_type_ == STARTINGPOS_SCENARIO) - { + + if(this->starting_pos_type_ == STARTINGPOS_SNAPSHOT || this->starting_pos_type_ == STARTINGPOS_SCENARIO) { scenario_id = starting_pos_["id"].str(); - } - else if(!has_carryover_expanded_) - { + } else if(!has_carryover_expanded_) { scenario_id = carryover_["next_scenario"].str(); - } - else - { + } else { assert(!"cannot figure out scenario_id"); throw "assertion ingnored"; } + return scenario_id == "null" ? "" : scenario_id; } @@ -555,23 +593,24 @@ bool saved_game::not_corrupt() const void saved_game::update_label() { - if (classification().abbrev.empty()) + if(classification().abbrev.empty()) { classification().label = starting_pos_["name"].str(); - else { + } else { classification().label = classification().abbrev + "-" + starting_pos_["name"]; } } void saved_game::cancel_orders() { - for(config &side : this->starting_pos_.child_range("side")) - { + for(config& side : this->starting_pos_.child_range("side")) { // for humans "goto_x/y" is used for multi-turn-moves // for the ai "goto_x/y" is a way for wml to order the ai to move a unit to a certain place. // we want to cancel human order but not to break wml. - if (side["controller"] != "human" && side["controller"] != "network") continue; - for(config &unit : side.child_range("unit")) - { + if(side["controller"] != "human" && side["controller"] != "network") { + continue; + } + + for(config& unit : side.child_range("unit")) { unit["goto_x"] = -999; unit["goto_y"] = -999; } @@ -580,12 +619,14 @@ void saved_game::cancel_orders() void saved_game::unify_controllers() { - for(config &side : this->starting_pos_.child_range("side")) - { - if (side["controller"] == "network") + for(config& side : this->starting_pos_.child_range("side")) { + if(side["controller"] == "network") { side["controller"] = "human"; - if (side["controller"] == "network_ai") + } + + if(side["controller"] == "network_ai") { side["controller"] = "ai"; + } } } @@ -598,12 +639,15 @@ saved_game& saved_game::operator=(saved_game&& other) void saved_game::swap(saved_game& other) { carryover_.swap(other.carryover_); + std::swap(classification_, other.classification_); std::swap(has_carryover_expanded_, other.has_carryover_expanded_); std::swap(mp_settings_, other.mp_settings_); + replay_data_.swap(other.replay_data_); replay_start_.swap(other.replay_start_); starting_pos_.swap(other.starting_pos_); + std::swap(starting_pos_type_, other.starting_pos_type_); } @@ -611,62 +655,53 @@ void saved_game::set_data(config& cfg) { log_scope("read_game"); - if(config & caryover_sides = cfg.child("carryover_sides")) - { + if(config& caryover_sides = cfg.child("carryover_sides")) { carryover_.swap(caryover_sides); has_carryover_expanded_ = true; - } - else if(config & caryover_sides_start = cfg.child("carryover_sides_start")) - { + } else if(config& caryover_sides_start = cfg.child("carryover_sides_start")) { carryover_.swap(caryover_sides_start); has_carryover_expanded_ = false; - } - else - { + } else { carryover_ = config(); has_carryover_expanded_ = false; } - if(config & replay_start = cfg.child("replay_start")) - { + if(config& replay_start = cfg.child("replay_start")) { replay_start_.swap(replay_start); - } - else - { + } else { replay_start_ = config(); } + replay_data_ = replay_recorder_base(); - //Serversided replays can contain multiple [replay] - for(config& replay : cfg.child_range("replay")) - { + + // Serversided replays can contain multiple [replay] + for(config& replay : cfg.child_range("replay")) { replay_data_.append_config(replay); } + replay_data_.set_to_end(); - if(config& snapshot = cfg.child("snapshot")) - { + + if(config& snapshot = cfg.child("snapshot")) { this->starting_pos_type_ = STARTINGPOS_SNAPSHOT; this->starting_pos_.swap(snapshot); - } - else if(config& scenario = cfg.child("scenario")) - { + } else if(config& scenario = cfg.child("scenario")) { this->starting_pos_type_ = STARTINGPOS_SCENARIO; this->starting_pos_.swap(scenario); - } - else - { + } else { this->starting_pos_type_ = STARTINGPOS_NONE; this->starting_pos_ = config(); } LOG_NG << "scenario: '" << carryover_["next_scenario"].str() << "'\n"; - if (const config &stats = cfg.child("statistics")) { + if(const config& stats = cfg.child("statistics")) { statistics::fresh_stats(); statistics::read_stats(stats); } classification_ = game_classification(cfg); mp_settings_ = mp_game_settings(cfg.child_or_empty("multiplayer")); + cfg.clear(); }