Deploy and refactor config::optional_child

Previously the config class had an operator bool and
it was a common pattern to use if(const config& = cfg.child(..)).
While this pattern was nice to use. It has severe drawbacks, in
particular it was unclear whether a function that took a config&
parameter allowed "invalid" configs, while most functions
did not, there were some that did. Furtheremore it lead to a few
buggy codes that were unconvered by this change (Not fixed though!),
in particular codes that tested local config objects that were
not references to being invalid, which could never be the case.
This commits replaces those with just `true` in order to not
change behaviour.

Some obvious cases were also removed including for example
things like `assert(config());` There is ony case in the ai code
that i'm not 100% sure of where one implementation of a virtual
function checked for an invalid config and another one that didn't.

With this, all code that check for a config child to be
present now uses config::optional_child which returns an object
that behaves similar to optional<(const) config&>, so it throws
on invalid dereferencing. But it also has operator[string] for
convinience, in particular to make is similary
easy to use the the previous `if (config& = .. child())`.
Also it has a tool DEBUG_CONFIG which tests whether all
optional_config values are checked before they are derefereneced.

Another method manditory_child was
added that throws when the key is not found, which replaces all
occurances of child() that did not check whether the result was
valid. This was neccecary (this= adding a new method instead of
renaming .child) to keep track of converted changes, and be sure
no occurances of child() were accidentally changed to the
throwing version.

We might want to rename one of mandatory_child or optional_child
to just child later. Not sure which one yet. I think it's better
to keep it in the current state (no config::child() ) for a while
though, so that people that currently used child() in their open
prs or other work get an error and not wrongly rely on the previous
behviour of config::child.

The interface of vconfig was not changed in this commit.
This commit is contained in:
gfgtdf 2023-03-30 16:49:41 +02:00
parent 231f9ea905
commit f8bd32eb15
150 changed files with 918 additions and 999 deletions

View file

@ -32,7 +32,7 @@ tprogress_bar_definition::tresolution::tresolution(const config& cfg) /*@ \label
* * state_enabled, the progress bar is enabled. * * state_enabled, the progress bar is enabled.
*/ */
// Note the order should be the same as the enum tstate is progress_bar.hpp. // Note the order should be the same as the enum tstate is progress_bar.hpp.
state.push_back(tstate_definition(cfg.child("state_enabled"))); state.push_back(state_definition(cfg.optional_child("state_enabled")));
} }
} // namespace gui2 } // namespace gui2

View file

@ -86,7 +86,7 @@ undo_action_base * undo_list::create_action(const config & cfg)
else if ( str == "recruit" ) { else if ( str == "recruit" ) {
// Validate the unit type. // Validate the unit type.
const config & child = cfg.child("unit"); const config & child = cfg.mandatory_child("unit");
const unit_type * u_type = unit_types.find(child["type"]); const unit_type * u_type = unit_types.find(child["type"]);
if ( !u_type ) { if ( !u_type ) {
@ -102,7 +102,7 @@ undo_action_base * undo_list::create_action(const config & cfg)
res = new undo::recall_action(cfg, map_location(cfg.child_or_empty("leader"), nullptr)); res = new undo::recall_action(cfg, map_location(cfg.child_or_empty("leader"), nullptr));
else if ( str == "dismiss" ) else if ( str == "dismiss" )
res = new undo::dismiss_action(cfg, cfg.child("unit")); res = new undo::dismiss_action(cfg, cfg.mandatory_child("unit"));
else if ( str == "auto_shroud" ) else if ( str == "auto_shroud" )
res = new undo::auto_shroud_action(cfg["active"].to_bool()); res = new undo::auto_shroud_action(cfg["active"].to_bool());
@ -403,8 +403,7 @@ void undo_list::redo()
auto action = std::move(redos_.back()); auto action = std::move(redos_.back());
redos_.pop_back(); redos_.pop_back();
auto [commandname, data] = action->mandatory_child("command").all_children_range().front();
auto [commandname, data] = action->child("command").all_children_range().front();
// Note that this might add more than one [command] // Note that this might add more than one [command]
resources::recorder->redo(*action); resources::recorder->redo(*action);

View file

@ -111,8 +111,8 @@ namespace {
u2.reset(new scoped_xy_unit("unit", who->get_location(), resources::gameboard->units())); u2.reset(new scoped_xy_unit("unit", who->get_location(), resources::gameboard->units()));
} }
scoped_weapon_info w1("weapon", e.data.child("first")); scoped_weapon_info w1("weapon", e.data.optional_child("first"));
scoped_weapon_info w2("second_weapon", e.data.child("second")); scoped_weapon_info w2("second_weapon", e.data.optional_child("second"));
game_events::queued_event q(tag, "", map_location(x1, y1, wml_loc()), map_location(x2, y2, wml_loc()), e.data); game_events::queued_event q(tag, "", map_location(x1, y1, wml_loc()), map_location(x2, y2, wml_loc()), e.data);
resources::lua_kernel->run_wml_action("command", vconfig(e.commands), q); resources::lua_kernel->run_wml_action("command", vconfig(e.commands), q);

View file

@ -56,7 +56,7 @@ void move_action::write(config & cfg) const
shroud_clearing_action::write(cfg); shroud_clearing_action::write(cfg);
cfg["starting_direction"] = map_location::write_direction(starting_dir); cfg["starting_direction"] = map_location::write_direction(starting_dir);
cfg["starting_moves"] = starting_moves; cfg["starting_moves"] = starting_moves;
config & child = cfg.child("unit"); config & child = cfg.mandatory_child("unit");
child["goto_x"] = goto_hex.wml_x(); child["goto_x"] = goto_hex.wml_x();
child["goto_y"] = goto_hex.wml_y(); child["goto_y"] = goto_hex.wml_y();
} }

View file

@ -60,7 +60,7 @@ void recruit_action::write(config & cfg) const
shroud_clearing_action::write(cfg); shroud_clearing_action::write(cfg);
recruit_from.write(cfg.add_child("leader")); recruit_from.write(cfg.add_child("leader"));
config & child = cfg.child("unit"); config & child = cfg.mandatory_child("unit");
child["type"] = u_type.parent_id(); child["type"] = u_type.parent_id();
} }

View file

@ -88,7 +88,7 @@ void addons_client::connect()
wait_for_transfer_done(msg); wait_for_transfer_done(msg);
if(!update_last_error(response_buf)) { if(!update_last_error(response_buf)) {
if(const auto& info = response_buf.child("server_id")) { if(auto info = response_buf.optional_child("server_id")) {
server_id_ = info["id"].str(); server_id_ = info["id"].str();
server_version_ = info["version"].str(); server_version_ = info["version"].str();
@ -127,7 +127,7 @@ bool addons_client::request_addons_list(config& cfg)
send_simple_request("request_campaign_list", response_buf); send_simple_request("request_campaign_list", response_buf);
wait_for_transfer_done(_("Downloading list of add-ons...")); wait_for_transfer_done(_("Downloading list of add-ons..."));
std::swap(cfg, response_buf.child("campaigns")); std::swap(cfg, response_buf.mandatory_child("campaigns"));
return !update_last_error(response_buf); return !update_last_error(response_buf);
} }
@ -148,7 +148,7 @@ bool addons_client::request_distribution_terms(std::string& terms)
send_simple_request("request_terms", response_buf); send_simple_request("request_terms", response_buf);
wait_for_transfer_done(_("Requesting distribution terms...")); wait_for_transfer_done(_("Requesting distribution terms..."));
if(const config& msg_cfg = response_buf.child("message")) { if(auto msg_cfg = response_buf.optional_child("message")) {
terms = msg_cfg["message"].str(); terms = msg_cfg["message"].str();
} }
@ -224,7 +224,7 @@ bool addons_client::upload_addon(const std::string& id, std::string& response_me
wait_for_transfer_done(_("Requesting file index...")); wait_for_transfer_done(_("Requesting file index..."));
// A silent error check // A silent error check
if(!hashlist.child("error")) { if(!hashlist.has_child("error")) {
if(!contains_hashlist(addon_data, hashlist) || !contains_hashlist(hashlist, addon_data)) { if(!contains_hashlist(addon_data, hashlist) || !contains_hashlist(hashlist, addon_data)) {
LOG_ADDONS << "making an update pack for the add-on " << id; LOG_ADDONS << "making an update pack for the add-on " << id;
config updatepack; config updatepack;
@ -240,7 +240,7 @@ bool addons_client::upload_addon(const std::string& id, std::string& response_me
wait_for_transfer_done(VGETTEXT("Sending an update pack for the add-on <i>$addon_title</i>...", i18n_symbols wait_for_transfer_done(VGETTEXT("Sending an update pack for the add-on <i>$addon_title</i>...", i18n_symbols
), transfer_mode::upload); ), transfer_mode::upload);
if(const config& message_cfg = response_buf.child("message")) { if(auto message_cfg = response_buf.optional_child("message")) {
response_message = message_cfg["message"].str(); response_message = message_cfg["message"].str();
LOG_ADDONS << "server response: " << response_message; LOG_ADDONS << "server response: " << response_message;
} }
@ -261,7 +261,7 @@ bool addons_client::upload_addon(const std::string& id, std::string& response_me
wait_for_transfer_done(VGETTEXT("Sending add-on <i>$addon_title</i>...", i18n_symbols wait_for_transfer_done(VGETTEXT("Sending add-on <i>$addon_title</i>...", i18n_symbols
), transfer_mode::upload); ), transfer_mode::upload);
if(const config& message_cfg = response_buf.child("message")) { if(auto message_cfg = response_buf.optional_child("message")) {
response_message = message_cfg["message"].str(); response_message = message_cfg["message"].str();
LOG_ADDONS << "server response: " << response_message; LOG_ADDONS << "server response: " << response_message;
} }
@ -309,7 +309,7 @@ bool addons_client::delete_remote_addon(const std::string& id, std::string& resp
send_request(request_buf, response_buf); send_request(request_buf, response_buf);
wait_for_transfer_done(VGETTEXT("Removing add-on <i>$addon_title</i> from the server...", i18n_symbols)); wait_for_transfer_done(VGETTEXT("Removing add-on <i>$addon_title</i> from the server...", i18n_symbols));
if(const config& message_cfg = response_buf.child("message")) { if(auto message_cfg = response_buf.optional_child("message")) {
response_message = message_cfg["message"].str(); response_message = message_cfg["message"].str();
LOG_ADDONS << "server response: " << response_message; LOG_ADDONS << "server response: " << response_message;
} }
@ -599,15 +599,15 @@ addons_client::install_result addons_client::install_addon_with_checks(const add
bool addons_client::update_last_error(config& response_cfg) bool addons_client::update_last_error(config& response_cfg)
{ {
if(const config& error = response_cfg.child("error")) { if(auto error = response_cfg.optional_child("error")) {
if(error.has_attribute("status_code")) { if(error->has_attribute("status_code")) {
const auto& status_msg = translated_addon_check_status(error["status_code"].to_unsigned()); const auto& status_msg = translated_addon_check_status(error["status_code"].to_unsigned());
last_error_ = font::escape_text(status_msg); last_error_ = font::escape_text(status_msg);
} else { } else {
last_error_ = font::escape_text(error["message"].str()); last_error_ = font::escape_text(error["message"].str());
} }
last_error_data_ = font::escape_text(error["extra_data"].str()); last_error_data_ = font::escape_text(error["extra_data"].str());
ERR_ADDONS << "server error: " << error; ERR_ADDONS << "server error: " << *error;
return true; return true;
} else { } else {
last_error_.clear(); last_error_.clear();

View file

@ -113,8 +113,8 @@ void get_addon_install_info(const std::string& addon_name, config& cfg)
cfg.clear(); cfg.clear();
config envelope; config envelope;
read(envelope, *stream); read(envelope, *stream);
if(config& info = envelope.child("info")) { if(auto info = envelope.optional_child("info")) {
cfg = std::move(info); cfg = std::move(*info);
} }
} catch(const config::error& e) { } catch(const config::error& e) {
ERR_CFG << "Failed to read add-on installation information for '" ERR_CFG << "Failed to read add-on installation information for '"

View file

@ -57,10 +57,6 @@ bool get_addons_list(addons_client& client, addons_list& list)
config cfg; config cfg;
client.request_addons_list(cfg); client.request_addons_list(cfg);
if(!cfg) {
return false;
}
read_addons_list(cfg, list); read_addons_list(cfg, list);
return true; return true;

View file

@ -301,9 +301,9 @@ bool contains_hashlist(const config& from, const config& to)
} }
for(const config& d : to.child_range("dir")) { for(const config& d : to.child_range("dir")) {
const config& origin_dir = from.find_child("dir", "name", d["name"]); auto origin_dir = from.find_child("dir", "name", d["name"]);
if(origin_dir) { if(origin_dir) {
if(!contains_hashlist(origin_dir, d)) { if(!contains_hashlist(*origin_dir, d)) {
return false; return false;
} }
} else { } else {
@ -343,10 +343,10 @@ static bool write_difference(config& pack, const config& from, const config& to,
} }
for(const config& d : to.child_range("dir")) { for(const config& d : to.child_range("dir")) {
const config& origin_dir = from.find_child("dir", "name", d["name"]); auto origin_dir = from.find_child("dir", "name", d["name"]);
config dir; config dir;
if(origin_dir) { if(origin_dir) {
if(write_difference(dir, origin_dir, d, with_content)) { if(write_difference(dir, *origin_dir, d, with_content)) {
pack.add_child("dir", dir); pack.add_child("dir", dir);
has_changes = true; has_changes = true;
} }

View file

@ -250,8 +250,9 @@ public:
add_facet(-1,cfg_element); add_facet(-1,cfg_element);
} }
config _default = this->cfg_.child("default"); config _default = this->cfg_.mandatory_child("default");
if (_default) { // TODO: this was a faulty invalid config test.
if ((true)) {
_default["id"] = "default_facet"; _default["id"] = "default_facet";
std::vector< aspect_ptr > default_aspects; std::vector< aspect_ptr > default_aspects;
engine::parse_aspect_from_config(*this,_default,parent_id_,std::back_inserter(default_aspects)); engine::parse_aspect_from_config(*this,_default,parent_id_,std::back_inserter(default_aspects));

View file

@ -185,11 +185,11 @@ bool component_manager::add_component(component *root, const std::string &path,
if (c==nullptr) { if (c==nullptr) {
return false; return false;
} }
const config &ch = cfg.child(tail.property); auto ch = cfg.optional_child(tail.property);
if (!ch) { if (!ch) {
return false; return false;
} }
return c->add_child(tail, ch); return c->add_child(tail, *ch);
} }
@ -200,11 +200,11 @@ bool component_manager::change_component(component *root, const std::string &pat
if (c==nullptr) { if (c==nullptr) {
return false; return false;
} }
const config &ch = cfg.child(tail.property); auto ch = cfg.optional_child(tail.property);
if (!ch) { if (!ch) {
return false; return false;
} }
return c->change_child(tail,ch); return c->change_child(tail, *ch);
} }
bool component_manager::delete_component(component *root, const std::string &path) bool component_manager::delete_component(component *root, const std::string &path)

View file

@ -130,11 +130,11 @@ void target_unit_goal::add_targets(std::back_insert_iterator< std::vector< targe
return; return;
} }
const config &criteria = cfg_.child("criteria"); auto criteria = cfg_.optional_child("criteria");
if (!criteria) return; if (!criteria) return;
//find the enemy leaders and explicit targets //find the enemy leaders and explicit targets
const unit_filter ufilt{ vconfig(criteria) }; const unit_filter ufilt{ vconfig(*criteria) };
for (const unit &u : resources::gameboard->units()) { for (const unit &u : resources::gameboard->units()) {
if (ufilt( u )) { if (ufilt( u )) {
LOG_AI_GOAL << "found explicit target unit at ... " << u.get_location() << " with value: " << value(); LOG_AI_GOAL << "found explicit target unit at ... " << u.get_location() << " with value: " << value();
@ -161,9 +161,9 @@ void target_location_goal::on_create()
if (cfg_.has_attribute("value")) { if (cfg_.has_attribute("value")) {
value_ = cfg_["value"].to_double(0); value_ = cfg_["value"].to_double(0);
} }
const config &criteria = cfg_.child("criteria"); auto criteria = cfg_.optional_child("criteria");
if (criteria) { if (criteria) {
filter_ptr_.reset(new terrain_filter(vconfig(criteria),resources::filter_con, false)); filter_ptr_.reset(new terrain_filter(vconfig(*criteria),resources::filter_con, false));
} }
} }
@ -210,9 +210,9 @@ void protect_goal::on_create()
if (radius_<1) { if (radius_<1) {
radius_=20; radius_=20;
} }
const config &criteria = cfg_.child("criteria"); auto criteria = cfg_.optional_child("criteria");
if (criteria) { if (criteria) {
filter_ptr_.reset(new terrain_filter(vconfig(criteria), resources::filter_con, false)); filter_ptr_.reset(new terrain_filter(vconfig(*criteria), resources::filter_con, false));
} }
} }
@ -231,19 +231,19 @@ void protect_goal::add_targets(std::back_insert_iterator< std::vector< target >>
return; return;
} }
const config &criteria = cfg_.child("criteria"); auto criteria = cfg_.optional_child("criteria");
if (!criteria) { if (!criteria) {
LOG_AI_GOAL << "skipping " << goal_type << " goal - no criteria given"; LOG_AI_GOAL << "skipping " << goal_type << " goal - no criteria given";
return; return;
} else { } else {
DBG_AI_GOAL << "side " << get_side() << ": "<< goal_type << " goal with criteria" << std::endl << cfg_.child("criteria"); DBG_AI_GOAL << "side " << get_side() << ": "<< goal_type << " goal with criteria" << std::endl << cfg_.mandatory_child("criteria");
} }
unit_map &units = resources::gameboard->units(); unit_map &units = resources::gameboard->units();
std::set<map_location> items; std::set<map_location> items;
if (protect_unit_) { if (protect_unit_) {
const unit_filter ufilt{ vconfig(criteria) }; const unit_filter ufilt{ vconfig(*criteria) };
for (const unit &u : units) for (const unit &u : units)
{ {
// 'protect_unit' can be set to any unit of any side -> exclude hidden units // 'protect_unit' can be set to any unit of any side -> exclude hidden units

View file

@ -40,8 +40,8 @@ candidate_action::candidate_action(rca_context &context, const config &cfg):
max_score_(cfg["max_score"].to_double(HIGH_SCORE)), max_score_(cfg["max_score"].to_double(HIGH_SCORE)),
id_(cfg["id"]), name_(cfg["name"]), type_(cfg["type"]), to_be_removed_(false) id_(cfg["id"]), name_(cfg["name"]), type_(cfg["type"]), to_be_removed_(false)
{ {
if (const config &filter_own = cfg.child("filter_own")) { if (auto filter_own = cfg.optional_child("filter_own")) {
vconfig vcfg(filter_own); vconfig vcfg(*filter_own);
vcfg.make_safe(); vcfg.make_safe();
filter_own_.reset(new unit_filter(vcfg)); filter_own_.reset(new unit_filter(vcfg));
} }

View file

@ -192,8 +192,8 @@ public:
static void cfg_to_value(const config &cfg, config &value) static void cfg_to_value(const config &cfg, config &value)
{ {
if (const config &v = cfg.child("value")) { if (auto v = cfg.optional_child("value")) {
value = v; value = *v;
} else { } else {
value.clear(); value.clear();
} }
@ -223,8 +223,8 @@ public:
static terrain_filter cfg_to_value(const config &cfg) static terrain_filter cfg_to_value(const config &cfg)
{ {
if (const config &v = cfg.child("value")) { if (auto v = cfg.optional_child("value")) {
return terrain_filter(vconfig(v), resources::filter_con, false); return terrain_filter(vconfig(*v), resources::filter_con, false);
} }
static config c("not"); static config c("not");
return terrain_filter(vconfig(c),resources::filter_con, false); return terrain_filter(vconfig(c),resources::filter_con, false);

View file

@ -48,9 +48,10 @@ void configuration::init(const game_config_view &game_config)
era_ai_configurations_.clear(); era_ai_configurations_.clear();
mod_ai_configurations_.clear(); mod_ai_configurations_.clear();
const config &ais = game_config.child("ais"); const config &ais = game_config.mandatory_child("ais");
default_config_ = ais.child("default_config"); default_config_ = ais.mandatory_child("default_config");
if (!default_config_) { // TODO: this was a faulty invalid config test.
if ((false)) {
ERR_AI_CONFIGURATION << "Missing AI [default_config]. Therefore, default_config_ set to empty."; ERR_AI_CONFIGURATION << "Missing AI [default_config]. Therefore, default_config_ set to empty.";
default_config_.clear(); default_config_.clear();
} }
@ -217,7 +218,8 @@ bool configuration::parse_side_config(side_number side, const config& original_c
DBG_AI_CONFIGURATION << "side " << side << ": config contains:"<< std::endl << cfg; DBG_AI_CONFIGURATION << "side " << side << ": config contains:"<< std::endl << cfg;
//insert default config at the beginning //insert default config at the beginning
if (default_config_) { // TODO: this was a faulty invalid config test.
if ((true)) {
DBG_AI_CONFIGURATION << "side "<< side <<": applying default configuration"; DBG_AI_CONFIGURATION << "side "<< side <<": applying default configuration";
cfg.add_child_at("ai",default_config_,0); cfg.add_child_at("ai",default_config_,0);
} else { } else {
@ -246,12 +248,12 @@ bool configuration::parse_side_config(side_number side, const config& original_c
// No point in warning about Lua or standard aspects lacking [default] // No point in warning about Lua or standard aspects lacking [default]
continue; continue;
} }
if (!aspect_cfg.child("default")) { if (!aspect_cfg.has_child("default")) {
WRN_AI_CONFIGURATION << "side "<< side <<": aspect with id=["<<aspect_cfg["id"]<<"] lacks default config facet!"; WRN_AI_CONFIGURATION << "side "<< side <<": aspect with id=["<<aspect_cfg["id"]<<"] lacks default config facet!";
continue; continue;
} }
aspect_cfg.merge_children("default"); aspect_cfg.merge_children("default");
config& dflt = aspect_cfg.child("default"); config& dflt = aspect_cfg.mandatory_child("default");
if (dflt.has_child("value")) { if (dflt.has_child("value")) {
while (dflt.child_count("value") > 1) { while (dflt.child_count("value") > 1) {
dflt.remove_child("value", 0); dflt.remove_child("value", 0);

View file

@ -53,14 +53,14 @@ aspect_attacks::aspect_attacks(readonly_context& context, const config& cfg, con
, filter_own_() , filter_own_()
, filter_enemy_() , filter_enemy_()
{ {
if(const config& filter_own = cfg.child("filter_own")) { if(auto filter_own = cfg.optional_child("filter_own")) {
vconfig vcfg(filter_own); vconfig vcfg(*filter_own);
vcfg.make_safe(); vcfg.make_safe();
filter_own_.reset(new unit_filter(vcfg)); filter_own_.reset(new unit_filter(vcfg));
} }
if(const config& filter_enemy = cfg.child("filter_enemy")) { if(auto filter_enemy = cfg.optional_child("filter_enemy")) {
vconfig vcfg(filter_enemy); vconfig vcfg(*filter_enemy);
vcfg.make_safe(); vcfg.make_safe();
filter_enemy_.reset(new unit_filter(vcfg)); filter_enemy_.reset(new unit_filter(vcfg));
} }

View file

@ -277,9 +277,10 @@ move_leader_to_goals_phase::~move_leader_to_goals_phase()
double move_leader_to_goals_phase::evaluate() double move_leader_to_goals_phase::evaluate()
{ {
const config &goal = get_leader_goal(); config goal = get_leader_goal();
//passive leader can reach a goal //passive leader can reach a goal
if (!goal) { // TODO: this was a faulty invalid config test.
if ((false)) {
LOG_AI_TESTING_AI_DEFAULT << get_name() << "No goal found"; LOG_AI_TESTING_AI_DEFAULT << get_name() << "No goal found";
return BAD_SCORE; return BAD_SCORE;
} }

View file

@ -1817,7 +1817,7 @@ void recruitment::recruit_situation_change_observer::reset_gamestate_changed() {
recruitment_aspect::recruitment_aspect(readonly_context &context, const config &cfg, const std::string &id) recruitment_aspect::recruitment_aspect(readonly_context &context, const config &cfg, const std::string &id)
: standard_aspect<config>(context, cfg, id) : standard_aspect<config>(context, cfg, id)
{ {
config parsed_cfg(cfg.has_child("value") ? cfg.child("value") : cfg); config parsed_cfg(cfg.has_child("value") ? cfg.mandatory_child("value") : cfg);
// First, transform simplified tags into [recruit] tags. // First, transform simplified tags into [recruit] tags.
for (config pattern : parsed_cfg.child_range("pattern")) { for (config pattern : parsed_cfg.child_range("pattern")) {
parsed_cfg["pattern"] = true; parsed_cfg["pattern"] = true;

View file

@ -755,10 +755,6 @@ bool formula_ai::gamestate_change_observer::continue_check() {
config formula_ai::to_config() const config formula_ai::to_config() const
{ {
if (!cfg_)
{
return config();
}
DBG_AI << "formula_ai::to_config(): "<< cfg_; DBG_AI << "formula_ai::to_config(): "<< cfg_;
config cfg = cfg_; config cfg = cfg_;

View file

@ -62,10 +62,10 @@ candidate_action_with_filters::candidate_action_with_filters(
: base_candidate_action(name, type, cfg, function_table) : base_candidate_action(name, type, cfg, function_table)
, filter_map_() , filter_map_()
{ {
const config & filter_params = cfg.child("filter"); auto filter_params = cfg.optional_child("filter");
if( filter_params ) { if( filter_params ) {
for(const config::attribute& filter_param : filter_params.attribute_range()) for(const config::attribute& filter_param : filter_params->attribute_range())
{ {
const_formula_ptr filter_formula( const_formula_ptr filter_formula(
new formula(filter_param.second, function_table)); new formula(filter_param.second, function_table));

View file

@ -91,9 +91,7 @@ void engine_fai::do_parse_candidate_action_from_config( rca_context &context, co
void engine_fai::do_parse_stage_from_config( ai_context &context, const config &cfg, std::back_insert_iterator<std::vector< stage_ptr >> b ) void engine_fai::do_parse_stage_from_config( ai_context &context, const config &cfg, std::back_insert_iterator<std::vector< stage_ptr >> b )
{ {
if (!cfg) { // This checekd for !cfg but oter implementation of do_parse_stage_from_config didn't.
return;
}
const std::string &name = cfg["name"]; const std::string &name = cfg["name"];
stage_ptr st_ptr; stage_ptr st_ptr;

View file

@ -303,11 +303,8 @@ void engine_lua::push_ai_table()
} }
} }
void engine_lua::do_parse_candidate_action_from_config( rca_context &context, const config &cfg, std::back_insert_iterator<std::vector< candidate_action_ptr > > b ){ void engine_lua::do_parse_candidate_action_from_config( rca_context &context, const config &cfg, std::back_insert_iterator<std::vector< candidate_action_ptr > > b )
if (!cfg) { {
return;
}
if (!lua_ai_context_) { if (!lua_ai_context_) {
return; return;
} }
@ -333,10 +330,6 @@ void engine_lua::do_parse_candidate_action_from_config( rca_context &context, co
void engine_lua::do_parse_stage_from_config( ai_context &context, const config &cfg, std::back_insert_iterator<std::vector< stage_ptr > > b ) void engine_lua::do_parse_stage_from_config( ai_context &context, const config &cfg, std::back_insert_iterator<std::vector< stage_ptr > > b )
{ {
if (!cfg) {
return;
}
if (!lua_ai_context_) { if (!lua_ai_context_) {
return; return;
} }

View file

@ -71,62 +71,10 @@ int map_erase_key(Map& map, Key&& key)
} }
struct config_implementation
{
/**
* Implementation for the wrappers for
* [const] config& child(const std::string& key, const std::string& parent);
*
* @tparam T A pointer to the config.
*/
template<class T>
static utils::const_clone_ref<config, T> child(T config, config_key_type key, const std::string& parent)
{
config->check_valid();
assert(!parent.empty());
assert(parent.front() == '[');
assert(parent.back() == ']');
if(config->has_child(key)) {
return *(config->children_.find(key)->second.front());
}
/**
* @todo Implement a proper wml_exception here.
*
* at the moment there seem to be dependency issues, which i don't want
* to fix right now.
*/
// FAIL(missing_mandatory_wml_section(parent, key));
std::stringstream sstr;
sstr << "Mandatory WML child »[" << key << "]« missing in »" << parent << "«. Please report this bug.";
throw config::error(sstr.str());
}
};
/* ** config implementation ** */ /* ** config implementation ** */
config config::invalid;
const char* config::diff_track_attribute = "__diff_track"; const char* config::diff_track_attribute = "__diff_track";
void config::check_valid() const
{
if(!*this) {
throw error("Mandatory WML child missing yet untested for. Please report.");
}
}
void config::check_valid(const config& cfg) const
{
if(!*this || !cfg) {
throw error("Mandatory WML child missing yet untested for. Please report.");
}
}
config::config() config::config()
: values_() : values_()
, children_() , children_()
@ -210,20 +158,16 @@ bool config::valid_attribute(config_key_type name)
bool config::has_attribute(config_key_type key) const bool config::has_attribute(config_key_type key) const
{ {
check_valid();
return values_.find(key) != values_.end(); return values_.find(key) != values_.end();
} }
void config::remove_attribute(config_key_type key) void config::remove_attribute(config_key_type key)
{ {
check_valid();
map_erase_key(values_, key); map_erase_key(values_, key);
} }
void config::append_children(const config& cfg) void config::append_children(const config& cfg)
{ {
check_valid(cfg);
for(const any_child value : cfg.all_children_range()) { for(const any_child value : cfg.all_children_range()) {
add_child(value.key, value.cfg); add_child(value.key, value.cfg);
} }
@ -231,8 +175,6 @@ void config::append_children(const config& cfg)
void config::append_children(config&& cfg) void config::append_children(config&& cfg)
{ {
check_valid(cfg);
#if 0 #if 0
//For some unknown reason this doesn't compile. //For some unknown reason this doesn't compile.
if(children_.empty()) { if(children_.empty()) {
@ -251,7 +193,6 @@ void config::append_children(config&& cfg)
void config::append_attributes(const config& cfg) void config::append_attributes(const config& cfg)
{ {
check_valid(cfg);
for(const attribute& v : cfg.values_) { for(const attribute& v : cfg.values_) {
values_[v.first] = v.second; values_[v.first] = v.second;
} }
@ -259,8 +200,6 @@ void config::append_attributes(const config& cfg)
void config::append_children(const config& cfg, const std::string& key) void config::append_children(const config& cfg, const std::string& key)
{ {
check_valid(cfg);
for(const config& value : cfg.child_range(key)) { for(const config& value : cfg.child_range(key)) {
add_child(key, value); add_child(key, value);
} }
@ -293,8 +232,6 @@ void config::append(config&& cfg)
void config::append_children_by_move(config& cfg, const std::string& key) void config::append_children_by_move(config& cfg, const std::string& key)
{ {
check_valid(cfg);
// DO note this leaves the tags empty in the source config. Not sure if // DO note this leaves the tags empty in the source config. Not sure if
// that should be changed. // that should be changed.
for(config& value : cfg.child_range(key)) { for(config& value : cfg.child_range(key)) {
@ -306,8 +243,6 @@ void config::append_children_by_move(config& cfg, const std::string& key)
void config::merge_children(const std::string& key) void config::merge_children(const std::string& key)
{ {
check_valid();
if(child_count(key) < 2) { if(child_count(key) < 2) {
return; return;
} }
@ -323,8 +258,6 @@ void config::merge_children(const std::string& key)
void config::merge_children_by_attribute(const std::string& key, const std::string& attribute) void config::merge_children_by_attribute(const std::string& key, const std::string& attribute)
{ {
check_valid();
if(child_count(key) < 2) { if(child_count(key) < 2) {
return; return;
} }
@ -343,8 +276,6 @@ void config::merge_children_by_attribute(const std::string& key, const std::stri
config::child_itors config::child_range(config_key_type key) config::child_itors config::child_range(config_key_type key)
{ {
check_valid();
child_map::iterator i = children_.find(key); child_map::iterator i = children_.find(key);
static child_list dummy; static child_list dummy;
child_list* p = &dummy; child_list* p = &dummy;
@ -357,8 +288,6 @@ config::child_itors config::child_range(config_key_type key)
config::const_child_itors config::child_range(config_key_type key) const config::const_child_itors config::child_range(config_key_type key) const
{ {
check_valid();
child_map::const_iterator i = children_.find(key); child_map::const_iterator i = children_.find(key);
static child_list dummy; static child_list dummy;
const child_list* p = &dummy; const child_list* p = &dummy;
@ -371,8 +300,6 @@ config::const_child_itors config::child_range(config_key_type key) const
unsigned config::child_count(config_key_type key) const unsigned config::child_count(config_key_type key) const
{ {
check_valid();
child_map::const_iterator i = children_.find(key); child_map::const_iterator i = children_.find(key);
if(i != children_.end()) { if(i != children_.end()) {
return i->second.size(); return i->second.size();
@ -393,25 +320,19 @@ unsigned config::attribute_count() const
bool config::has_child(config_key_type key) const bool config::has_child(config_key_type key) const
{ {
check_valid();
child_map::const_iterator i = children_.find(key); child_map::const_iterator i = children_.find(key);
return i != children_.end() && !i->second.empty(); return i != children_.end() && !i->second.empty();
} }
config& config::child(config_key_type key, int n) namespace {
template<class Tchildren>
auto get_child_impl(Tchildren& children, config_key_type key, int n) -> optional_config_impl<std::remove_reference_t<decltype(**(*children.begin()).second.begin())>>
{ {
check_valid();
const child_map::const_iterator i = children_.find(key); auto i = children.find(key);
if(i == children_.end()) { if(i == children.end()) {
DBG_CF << "The config object has no child named »" << key << "«."; DBG_CF << "The config object has no child named »" << key << "«.";
return std::nullopt;
if(throw_when_child_not_found::do_throw()) {
throw error("Child not found");
} else {
return invalid;
}
} }
if(n < 0) { if(n < 0) {
@ -423,50 +344,61 @@ config& config::child(config_key_type key, int n)
} catch(const std::out_of_range&) { } catch(const std::out_of_range&) {
DBG_CF << "The config object has only »" << i->second.size() << "« children named »" << key DBG_CF << "The config object has only »" << i->second.size() << "« children named »" << key
<< "«; request for the index »" << n << "« cannot be honored."; << "«; request for the index »" << n << "« cannot be honored.";
if(throw_when_child_not_found::do_throw()) {
throw error("Child at index not found");
} else {
return invalid;
}
}
}
config& config::child(config_key_type key, const std::string& parent)
{
return config_implementation::child(this, key, parent);
}
const config& config::child(config_key_type key, const std::string& parent) const
{
return config_implementation::child(this, key, parent);
}
utils::optional_reference<config> config::optional_child(config_key_type key, int n)
{
try {
throw_when_child_not_found raii_helper{};
return child(key, n);
} catch(const error&) {
return std::nullopt; return std::nullopt;
} }
} }
utils::optional_reference<const config> config::optional_child(config_key_type key, int n) const }
config& config::mandatory_child(config_key_type key, const std::string& parent)
{ {
try { if(auto res = get_child_impl(children_, key, 0)) {
throw_when_child_not_found raii_helper{}; return *res;
return child(key, n); } else {
} catch(const error&) { throw error("Mandatory WML child »[" + std::string(key) + "]« missing in »" + parent + "«. Please report this bug.");
return std::nullopt;
} }
} }
const config& config::mandatory_child(config_key_type key, const std::string& parent) const
{
if(auto res = get_child_impl(children_, key, 0)) {
return *res;
} else {
throw error("Mandatory WML child »[" + std::string(key) + "]« missing in »" + parent + "«. Please report this bug.");
}
}
config& config::mandatory_child(config_key_type key, int n)
{
if(auto res = get_child_impl(children_, key, n)) {
return *res;
} else {
throw error("Child [" + std::string(key) + "] at index " + std::to_string(n) + " not found");
}
}
const config& config::mandatory_child(config_key_type key, int n) const
{
if(auto res = get_child_impl(children_, key, n)) {
return *res;
} else {
throw error("Child [" + std::string(key) + "] at index " + std::to_string(n) + " not found");
}
}
optional_config config::optional_child(config_key_type key, int n)
{
return get_child_impl(children_, key, n);
}
optional_const_config config::optional_child(config_key_type key, int n) const
{
return get_child_impl(children_, key, n);
}
const config& config::child_or_empty(config_key_type key) const const config& config::child_or_empty(config_key_type key) const
{ {
static const config empty_cfg; static const config empty_cfg;
check_valid();
child_map::const_iterator i = children_.find(key); child_map::const_iterator i = children_.find(key);
if(i != children_.end() && !i->second.empty()) { if(i != children_.end() && !i->second.empty()) {
return *i->second.front(); return *i->second.front();
@ -485,20 +417,19 @@ config& config::child_or_add(config_key_type key)
return add_child(key); return add_child(key);
} }
utils::optional_reference<const config> config::get_deprecated_child(config_key_type old_key, const std::string& in_tag, DEP_LEVEL level, const std::string& message) const { optional_config_impl<const config> config::get_deprecated_child(config_key_type old_key, const std::string& in_tag, DEP_LEVEL level, const std::string& message) const
check_valid(); {
if(auto res = optional_child(old_key)) {
if(auto i = children_.find(old_key); i != children_.end() && !i->second.empty()) {
const std::string what = formatter() << "[" << in_tag << "][" << old_key << "]"; const std::string what = formatter() << "[" << in_tag << "][" << old_key << "]";
deprecated_message(what, level, "", message); deprecated_message(what, level, "", message);
return *i->second.front(); return res;
} }
return std::nullopt; return std::nullopt;
} }
config::const_child_itors config::get_deprecated_child_range(config_key_type old_key, const std::string& in_tag, DEP_LEVEL level, const std::string& message) const { config::const_child_itors config::get_deprecated_child_range(config_key_type old_key, const std::string& in_tag, DEP_LEVEL level, const std::string& message) const
check_valid(); {
static child_list dummy; static child_list dummy;
const child_list* p = &dummy; const child_list* p = &dummy;
@ -513,8 +444,6 @@ config::const_child_itors config::get_deprecated_child_range(config_key_type old
config& config::add_child(config_key_type key) config& config::add_child(config_key_type key)
{ {
check_valid();
child_list& v = map_get(children_, key); child_list& v = map_get(children_, key);
v.emplace_back(new config()); v.emplace_back(new config());
ordered_children.emplace_back(children_.find(key), v.size() - 1); ordered_children.emplace_back(children_.find(key), v.size() - 1);
@ -523,8 +452,6 @@ config& config::add_child(config_key_type key)
config& config::add_child(config_key_type key, const config& val) config& config::add_child(config_key_type key, const config& val)
{ {
check_valid(val);
child_list& v = map_get(children_, key); child_list& v = map_get(children_, key);
v.emplace_back(new config(val)); v.emplace_back(new config(val));
ordered_children.emplace_back(children_.find(key), v.size() - 1); ordered_children.emplace_back(children_.find(key), v.size() - 1);
@ -534,8 +461,6 @@ config& config::add_child(config_key_type key, const config& val)
config& config::add_child(config_key_type key, config&& val) config& config::add_child(config_key_type key, config&& val)
{ {
check_valid(val);
child_list& v = map_get(children_, key); child_list& v = map_get(children_, key);
v.emplace_back(new config(std::move(val))); v.emplace_back(new config(std::move(val)));
ordered_children.emplace_back(children_.find(key), v.size() - 1); ordered_children.emplace_back(children_.find(key), v.size() - 1);
@ -545,8 +470,6 @@ config& config::add_child(config_key_type key, config&& val)
config& config::add_child_at(config_key_type key, const config& val, unsigned index) config& config::add_child_at(config_key_type key, const config& val, unsigned index)
{ {
check_valid(val);
child_list& v = map_get(children_, key); child_list& v = map_get(children_, key);
if(index > v.size()) { if(index > v.size()) {
throw error("illegal index to add child at"); throw error("illegal index to add child at");
@ -648,8 +571,6 @@ private:
void config::clear_children_impl(config_key_type key) void config::clear_children_impl(config_key_type key)
{ {
check_valid();
child_map::iterator i = children_.find(key); child_map::iterator i = children_.find(key);
if(i == children_.end()) if(i == children_.end())
return; return;
@ -663,8 +584,6 @@ void config::clear_children_impl(config_key_type key)
void config::splice_children(config& src, const std::string& key) void config::splice_children(config& src, const std::string& key)
{ {
check_valid(src);
child_map::iterator i_src = src.children_.find(key); child_map::iterator i_src = src.children_.find(key);
if(i_src == src.children_.end()) { if(i_src == src.children_.end()) {
return; return;
@ -689,8 +608,6 @@ void config::splice_children(config& src, const std::string& key)
void config::recursive_clear_value(config_key_type key) void config::recursive_clear_value(config_key_type key)
{ {
check_valid();
map_erase_key(values_, key); map_erase_key(values_, key);
for(std::pair<const std::string, child_list>& p : children_) { for(std::pair<const std::string, child_list>& p : children_) {
@ -731,8 +648,6 @@ config::all_children_iterator config::erase(const config::all_children_iterator&
void config::remove_child(config_key_type key, unsigned index) void config::remove_child(config_key_type key, unsigned index)
{ {
check_valid();
child_map::iterator i = children_.find(key); child_map::iterator i = children_.find(key);
if(i == children_.end() || index >= i->second.size()) { if(i == children_.end() || index >= i->second.size()) {
ERR_CF << "Error: attempting to delete non-existing child: " << key << "[" << index << "]"; ERR_CF << "Error: attempting to delete non-existing child: " << key << "[" << index << "]";
@ -744,8 +659,6 @@ void config::remove_child(config_key_type key, unsigned index)
void config::remove_children(config_key_type key, std::function<bool(const config&)> p) void config::remove_children(config_key_type key, std::function<bool(const config&)> p)
{ {
check_valid();
child_map::iterator pos = children_.find(key); child_map::iterator pos = children_.find(key);
if(pos == children_.end()) { if(pos == children_.end()) {
return; return;
@ -766,8 +679,6 @@ void config::remove_children(config_key_type key, std::function<bool(const confi
const config::attribute_value& config::operator[](config_key_type key) const const config::attribute_value& config::operator[](config_key_type key) const
{ {
check_valid();
const attribute_map::const_iterator i = values_.find(key); const attribute_map::const_iterator i = values_.find(key);
if(i != values_.end()) { if(i != values_.end()) {
return i->second; return i->second;
@ -779,15 +690,12 @@ const config::attribute_value& config::operator[](config_key_type key) const
const config::attribute_value* config::get(config_key_type key) const const config::attribute_value* config::get(config_key_type key) const
{ {
check_valid();
attribute_map::const_iterator i = values_.find(key); attribute_map::const_iterator i = values_.find(key);
return i != values_.end() ? &i->second : nullptr; return i != values_.end() ? &i->second : nullptr;
} }
config::attribute_value& config::operator[](config_key_type key) config::attribute_value& config::operator[](config_key_type key)
{ {
check_valid();
auto res = values_.lower_bound(key); auto res = values_.lower_bound(key);
if(res == values_.end() || key != res->first) { if(res == values_.end() || key != res->first) {
@ -799,8 +707,6 @@ config::attribute_value& config::operator[](config_key_type key)
const config::attribute_value& config::get_old_attribute(config_key_type key, const std::string& old_key, const std::string& in_tag, const std::string& message) const const config::attribute_value& config::get_old_attribute(config_key_type key, const std::string& old_key, const std::string& in_tag, const std::string& message) const
{ {
check_valid();
if(has_attribute(old_key)) { if(has_attribute(old_key)) {
const std::string what = formatter() << "[" << in_tag << "]" << old_key << "="; const std::string what = formatter() << "[" << in_tag << "]" << old_key << "=";
const std::string msg = formatter() << "Use " << key << "= instead. " << message; const std::string msg = formatter() << "Use " << key << "= instead. " << message;
@ -821,9 +727,8 @@ const config::attribute_value& config::get_old_attribute(config_key_type key, co
return empty_attribute; return empty_attribute;
} }
const config::attribute_value& config::get_deprecated_attribute(config_key_type old_key, const std::string& in_tag, DEP_LEVEL level, const std::string& message) const { const config::attribute_value& config::get_deprecated_attribute(config_key_type old_key, const std::string& in_tag, DEP_LEVEL level, const std::string& message) const
check_valid(); {
if(auto i = values_.find(old_key); i != values_.end()) { if(auto i = values_.find(old_key); i != values_.end()) {
const std::string what = formatter() << "[" << in_tag << "]" << old_key << "="; const std::string what = formatter() << "[" << in_tag << "]" << old_key << "=";
deprecated_message(what, level, "", message); deprecated_message(what, level, "", message);
@ -836,8 +741,6 @@ const config::attribute_value& config::get_deprecated_attribute(config_key_type
void config::merge_attributes(const config& cfg) void config::merge_attributes(const config& cfg)
{ {
check_valid(cfg);
assert(this != &cfg); assert(this != &cfg);
for(const attribute& v : cfg.values_) { for(const attribute& v : cfg.values_) {
std::string key = v.first; std::string key = v.first;
@ -857,8 +760,6 @@ void config::merge_attributes(const config& cfg)
config::const_attr_itors config::attribute_range() const config::const_attr_itors config::attribute_range() const
{ {
check_valid();
const_attr_itors range(const_attribute_iterator(values_.begin()), const_attribute_iterator(values_.end())); const_attr_itors range(const_attribute_iterator(values_.begin()), const_attribute_iterator(values_.end()));
// Ensure the first element is not blank, as a few places assume this // Ensure the first element is not blank, as a few places assume this
@ -871,7 +772,6 @@ config::const_attr_itors config::attribute_range() const
config::attr_itors config::attribute_range() config::attr_itors config::attribute_range()
{ {
check_valid();
attr_itors range(attribute_iterator(values_.begin()), attribute_iterator(values_.end())); attr_itors range(attribute_iterator(values_.begin()), attribute_iterator(values_.end()));
// Ensure the first element is not blank, as a few places assume this // Ensure the first element is not blank, as a few places assume this
@ -882,19 +782,14 @@ config::attr_itors config::attribute_range()
return range; return range;
} }
config& config::find_child(config_key_type key, const std::string& name, const std::string& value) optional_config config::find_child(config_key_type key, const std::string& name, const std::string& value)
{ {
check_valid();
const child_map::iterator i = children_.find(key); const child_map::iterator i = children_.find(key);
if(i == children_.end()) { if(i == children_.end()) {
DBG_CF << "Key »" << name << "« value »" << value << "« pair not found as child of key »" << key << "«."; DBG_CF << "Key »" << name << "« value »" << value << "« pair not found as child of key »" << key << "«.";
if(throw_when_child_not_found::do_throw()) {
throw error("Child not found"); return std::nullopt;
} else {
return invalid;
}
} }
const child_list::iterator j = std::find_if(i->second.begin(), i->second.end(), const child_list::iterator j = std::find_if(i->second.begin(), i->second.end(),
@ -910,11 +805,7 @@ config& config::find_child(config_key_type key, const std::string& name, const s
DBG_CF << "Key »" << name << "« value »" << value << "« pair not found as child of key »" << key << "«."; DBG_CF << "Key »" << name << "« value »" << value << "« pair not found as child of key »" << key << "«.";
if(throw_when_child_not_found::do_throw()) { return std::nullopt;
throw error("Child not found");
} else {
return invalid;
}
} }
void config::clear() void config::clear()
@ -940,8 +831,6 @@ void config::clear_attributes()
bool config::empty() const bool config::empty() const
{ {
check_valid();
return children_.empty() && values_.empty(); return children_.empty() && values_.empty();
} }
@ -1003,8 +892,6 @@ config::all_children_itors config::all_children_range()
config config::get_diff(const config& c) const config config::get_diff(const config& c) const
{ {
check_valid(c);
config res; config res;
get_diff(c, res); get_diff(c, res);
return res; return res;
@ -1012,9 +899,6 @@ config config::get_diff(const config& c) const
void config::get_diff(const config& c, config& res) const void config::get_diff(const config& c, config& res) const
{ {
check_valid(c);
check_valid(res);
config* inserts = nullptr; config* inserts = nullptr;
for(const auto& v : values_) { for(const auto& v : values_) {
@ -1124,8 +1008,6 @@ void config::get_diff(const config& c, config& res) const
void config::apply_diff(const config& diff, bool track /* = false */) void config::apply_diff(const config& diff, bool track /* = false */)
{ {
check_valid(diff);
if(track) { if(track) {
values_[diff_track_attribute] = "modified"; values_[diff_track_attribute] = "modified";
} }
@ -1223,8 +1105,6 @@ void config::clear_diff_track(const config& diff)
*/ */
void config::merge_with(const config& c) void config::merge_with(const config& c)
{ {
check_valid(c);
std::vector<child_pos> to_remove; std::vector<child_pos> to_remove;
std::map<std::string, unsigned> visitations; std::map<std::string, unsigned> visitations;
@ -1288,7 +1168,6 @@ void config::inherit_from(const config& c)
*/ */
void config::inherit_attributes(const config& cfg) void config::inherit_attributes(const config& cfg)
{ {
check_valid(cfg);
for(const attribute& v : cfg.values_) { for(const attribute& v : cfg.values_) {
attribute_value& v2 = values_[v.first]; attribute_value& v2 = values_[v.first];
if(v2.blank()) { if(v2.blank()) {
@ -1298,8 +1177,6 @@ void config::inherit_attributes(const config& cfg)
} }
bool config::matches(const config& filter) const bool config::matches(const config& filter) const
{ {
check_valid(filter);
bool result = true; bool result = true;
for(const attribute& i : filter.attribute_range()) { for(const attribute& i : filter.attribute_range()) {
@ -1346,8 +1223,6 @@ bool config::matches(const config& filter) const
std::string config::debug() const std::string config::debug() const
{ {
check_valid();
std::ostringstream outstream; std::ostringstream outstream;
outstream << *this; outstream << *this;
return outstream.str(); return outstream.str();
@ -1391,8 +1266,6 @@ std::ostream& operator<<(std::ostream& outstream, const config& cfg)
std::string config::hash() const std::string config::hash() const
{ {
check_valid();
static const unsigned int hash_length = 128; static const unsigned int hash_length = 128;
static const char hash_string[] = "+-,.<>0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; static const char hash_string[] = "+-,.<>0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
char hash_str[hash_length + 1]; char hash_str[hash_length + 1];
@ -1446,8 +1319,6 @@ std::string config::hash() const
void config::swap(config& cfg) void config::swap(config& cfg)
{ {
check_valid(cfg);
values_.swap(cfg.values_); values_.swap(cfg.values_);
children_.swap(cfg.children_); children_.swap(cfg.children_);
ordered_children.swap(cfg.ordered_children); ordered_children.swap(cfg.ordered_children);
@ -1474,8 +1345,6 @@ bool config::validate_wml() const
bool operator==(const config& a, const config& b) bool operator==(const config& a, const config& b)
{ {
a.check_valid(b);
if(a.values_ != b.values_) { if(a.values_ != b.values_) {
return false; return false;
} }

View file

@ -30,6 +30,7 @@
#include "config_attribute_value.hpp" #include "config_attribute_value.hpp"
#include "exceptions.hpp" #include "exceptions.hpp"
#include "utils/const_clone.hpp"
#include "utils/optional_reference.hpp" #include "utils/optional_reference.hpp"
#include <climits> #include <climits>
@ -52,6 +53,105 @@ enum class DEP_LEVEL : uint8_t;
class config; class config;
template<class T>
class optional_config_impl
{
public:
optional_config_impl() = default;
optional_config_impl(T& ref)
: opt_(&ref)
{
}
optional_config_impl(std::nullopt_t)
: opt_()
{
}
T& value() const
{
if(opt_) {
return *opt_;
} else {
// We're going to drop this codepath once we can use optional::value anyway, but just
// noting we want this function to ultimately throw std::bad_optional_access.
throw std::runtime_error("Optional reference has no value");
}
}
optional_config_impl<T>& operator=(T& new_ref)
{
opt_ = &new_ref;
return *this;
}
bool has_value() const
{
#ifdef DEBUG_CONFIG
tested_ = true;
#endif
return opt_ != nullptr;
}
explicit operator bool() const
{
return has_value();
}
operator optional_config_impl<const T>() const
{
return opt_ ? optional_config_impl<const T>(*opt_) : optional_config_impl<const T>();
}
/** Returns a pointer to the referenced object or nullptr if no reference is held. */
T* ptr() const
{
if(opt_) {
return &value();
} else {
return nullptr;
}
}
T* operator->() const
{
assert(tested());
return &value();
}
T& operator*() const
{
assert(tested());
return value();
}
utils::const_clone_t<config_attribute_value, T>& operator[](config_key_type key)
{
assert(tested());
return value()[key];
}
operator utils::optional_reference<T>() const
{
return has_value() ? utils::optional_reference<T>(value()) : utils::optional_reference<T>();
}
bool tested() const
{
#ifdef DEBUG_CONFIG
return tested_;
#else
return true;
#endif
}
private:
T* opt_;
#ifdef DEBUG_CONFIG
mutable bool tested_;
#endif
};
bool operator==(const config &, const config &); bool operator==(const config &, const config &);
inline bool operator!=(const config &a, const config &b) { return !operator==(a, b); } inline bool operator!=(const config &a, const config &b) { return !operator==(a, b); }
std::ostream &operator << (std::ostream &, const config &); std::ostream &operator << (std::ostream &, const config &);
@ -60,20 +160,6 @@ std::ostream &operator << (std::ostream &, const config &);
class config class config
{ {
friend bool operator==(const config& a, const config& b); friend bool operator==(const config& a, const config& b);
friend struct config_implementation;
static config invalid;
/**
* Raises an exception if @a this is not valid.
*/
void check_valid() const;
/**
* Raises an exception if @a this or @a cfg is not valid.
*/
void check_valid(const config &cfg) const;
public: public:
// Create an empty node. // Create an empty node.
config(); config();
@ -105,12 +191,6 @@ public:
// Verifies that the string can be used as an attribute name // Verifies that the string can be used as an attribute name
static bool valid_attribute(config_key_type name); static bool valid_attribute(config_key_type name);
explicit operator bool() const
{ return this != &invalid; }
static config& get_invalid()
{ return invalid; }
typedef std::vector<std::unique_ptr<config>> child_list; typedef std::vector<std::unique_ptr<config>> child_list;
typedef std::map<std::string, child_list, std::less<>> child_map; typedef std::map<std::string, child_list, std::less<>> child_map;
@ -302,60 +382,27 @@ public:
*/ */
const config & child_or_empty(config_key_type key) const; const config & child_or_empty(config_key_type key) const;
/**
* An object of this type will cause the following functions to throw a config::error instead
* of returning a reference to the invalid config for the duration of its lfetime. If multiple
* instances exist simultaneously, this behavior will persist until all objects are destroyed.
*
* - @c child
* - @c find_child
*/
class throw_when_child_not_found
{
public:
friend class config;
throw_when_child_not_found()
{
instances++;
}
~throw_when_child_not_found()
{
instances--;
}
static bool do_throw()
{
return instances > 0;
}
private:
static inline unsigned instances = 0;
};
/** /**
* Returns the nth child with the given @a key, or * Returns the nth child with the given @a key, or
* a reference to an invalid config if there is none. * throws an error if there is none.
* @note A negative @a n accesses from the end of the object. * @note A negative @a n accesses from the end of the object.
* For instance, -1 is the index of the last child. * For instance, -1 is the index of the last child.
*/ */
config& child(config_key_type key, int n = 0);
config& mandatory_child(config_key_type key, int n = 0);
/** /**
* Returns the nth child with the given @a key, or * Returns the nth child with the given @a key, or
* a reference to an invalid config if there is none. * throws an error if there is none.
* @note A negative @a n accesses from the end of the object. * @note A negative @a n accesses from the end of the object.
* For instance, -1 is the index of the last child. * For instance, -1 is the index of the last child.
*/ */
const config& child(config_key_type key, int n = 0) const const config& mandatory_child(config_key_type key, int n = 0) const;
{ return const_cast<config*>(this)->child(key, n); }
/** Euivalent to @ref child, but returns an empty optional if the nth child was not found. */ /** Euivalent to @ref mandatory_child, but returns an empty optional if the nth child was not found. */
utils::optional_reference<config> optional_child(config_key_type key, int n = 0); optional_config_impl<config> optional_child(config_key_type key, int n = 0);
/** Euivalent to @ref child, but returns an empty optional if the nth child was not found. */ /** Euivalent to @ref mandatory_child, but returns an empty optional if the nth child was not found. */
utils::optional_reference<const config> optional_child(config_key_type key, int n = 0) const; optional_config_impl<const config> optional_child(config_key_type key, int n = 0) const;
/** /**
* Returns a mandatory child node. * Returns a mandatory child node.
@ -371,7 +418,7 @@ public:
* *
* @returns The wanted child node. * @returns The wanted child node.
*/ */
config& child(config_key_type key, const std::string& parent); config& mandatory_child(config_key_type key, const std::string& parent);
/** /**
* Returns a mandatory child node. * Returns a mandatory child node.
@ -387,7 +434,7 @@ public:
* *
* @returns The wanted child node. * @returns The wanted child node.
*/ */
const config& child(config_key_type key, const std::string& parent) const; const config& mandatory_child(config_key_type key, const std::string& parent) const;
/** /**
* Get a deprecated child and log a deprecation message * Get a deprecated child and log a deprecation message
@ -397,7 +444,7 @@ public:
* @param message An explanation of the deprecation, possibly mentioning an alternative * @param message An explanation of the deprecation, possibly mentioning an alternative
* @note The deprecation message will be a level 3 deprecation. * @note The deprecation message will be a level 3 deprecation.
*/ */
utils::optional_reference<const config> get_deprecated_child(config_key_type old_key, const std::string& in_tag, DEP_LEVEL level, const std::string& message) const; optional_config_impl<const config> get_deprecated_child(config_key_type old_key, const std::string& in_tag, DEP_LEVEL level, const std::string& message) const;
/** /**
* Get a deprecated child rangw and log a deprecation message * Get a deprecated child rangw and log a deprecation message
@ -569,10 +616,10 @@ public:
* Returns the first child of tag @a key with a @a name attribute * Returns the first child of tag @a key with a @a name attribute
* containing @a value. * containing @a value.
*/ */
config& find_child(config_key_type key, const std::string &name, optional_config_impl<config> find_child(config_key_type key, const std::string &name,
const std::string &value); const std::string &value);
const config& find_child(config_key_type key, const std::string &name, optional_config_impl<const config> find_child(config_key_type key, const std::string &name,
const std::string &value) const const std::string &value) const
{ return const_cast<config *>(this)->find_child(key, name, value); } { return const_cast<config *>(this)->find_child(key, name, value); }
@ -870,6 +917,9 @@ private:
std::vector<child_pos> ordered_children; std::vector<child_pos> ordered_children;
}; };
using optional_config = optional_config_impl<config>;
using optional_const_config = optional_config_impl<const config>;
/** Implement non-member swap function for std::swap (calls @ref config::swap). */ /** Implement non-member swap function for std::swap (calls @ref config::swap). */
void swap(config& lhs, config& rhs); void swap(config& lhs, config& rhs);

View file

@ -144,8 +144,8 @@ inline std::string pretty_path(const std::string& path)
inline config get_bookmarks_config() inline config get_bookmarks_config()
{ {
const config& cfg = preferences::get_child("dir_bookmarks"); auto cfg = preferences::get_child("dir_bookmarks");
return cfg ? cfg : config{}; return cfg ? *cfg : config{};
} }
inline void commit_bookmarks_config(config& cfg) inline void commit_bookmarks_config(config& cfg)

View file

@ -2924,13 +2924,13 @@ void display::refresh_report(const std::string& report_name, const config * new_
if (!str.empty()) { if (!str.empty()) {
config &e = report.add_child_at("element", config(), 0); config &e = report.add_child_at("element", config(), 0);
e["text"] = str; e["text"] = str;
e["tooltip"] = report.child("element")["tooltip"]; e["tooltip"] = report.mandatory_child("element")["tooltip"];
} }
str = item->postfix(); str = item->postfix();
if (!str.empty()) { if (!str.empty()) {
config &e = report.add_child("element"); config &e = report.add_child("element");
e["text"] = str; e["text"] = str;
e["tooltip"] = report.child("element", -1)["tooltip"]; e["tooltip"] = report.mandatory_child("element", -1)["tooltip"];
} }
// Do a fake run of drawing the report, so tooltips can be determined. // Do a fake run of drawing the report, so tooltips can be determined.

View file

@ -953,27 +953,27 @@ void context_manager::revert_map()
void context_manager::new_map(int width, int height, const t_translation::terrain_code& fill, bool new_context) void context_manager::new_map(int width, int height, const t_translation::terrain_code& fill, bool new_context)
{ {
const config& default_schedule = game_config_.find_child("editor_times", "id", "empty"); auto default_schedule = game_config_.find_child("editor_times", "id", "empty");
editor_map m(width, height, fill); editor_map m(width, height, fill);
if(new_context) { if(new_context) {
int new_id = add_map_context(m, true, default_schedule); int new_id = add_map_context(m, true, *default_schedule);
switch_context(new_id); switch_context(new_id);
} else { } else {
replace_map_context(m, true, default_schedule); replace_map_context(m, true, *default_schedule);
} }
} }
void context_manager::new_scenario(int width, int height, const t_translation::terrain_code& fill, bool new_context) void context_manager::new_scenario(int width, int height, const t_translation::terrain_code& fill, bool new_context)
{ {
const config& default_schedule = game_config_.find_child("editor_times", "id", "empty"); auto default_schedule = game_config_.find_child("editor_times", "id", "empty");
editor_map m(width, height, fill); editor_map m(width, height, fill);
if(new_context) { if(new_context) {
int new_id = add_map_context(m, false, default_schedule); int new_id = add_map_context(m, false, *default_schedule);
switch_context(new_id); switch_context(new_id);
} else { } else {
replace_map_context(m, false, default_schedule); replace_map_context(m, false, *default_schedule);
} }
// Give the new scenario an initial side. // Give the new scenario an initial side.
@ -1019,8 +1019,8 @@ void context_manager::create_default_context()
t_translation::terrain_code default_terrain = t_translation::terrain_code default_terrain =
t_translation::read_terrain_code(game_config::default_terrain); t_translation::read_terrain_code(game_config::default_terrain);
const config& default_schedule = game_config_.find_child("editor_times", "id", "empty"); auto default_schedule = game_config_.find_child("editor_times", "id", "empty");
add_map_context(editor_map(44, 33, default_terrain), true, default_schedule); add_map_context(editor_map(44, 33, default_terrain), true, *default_schedule);
} else { } else {
for(const std::string& filename : saved_windows_) { for(const std::string& filename : saved_windows_) {
add_map_context(game_config_, filename); add_map_context(game_config_, filename);

View file

@ -112,7 +112,7 @@ map_context::map_context(const game_config_view& game_config, const std::string&
, labels_(nullptr) , labels_(nullptr)
, units_() , units_()
, teams_() , teams_()
, tod_manager_(new tod_manager(game_config.find_child("editor_times", "id", "empty"))) , tod_manager_(new tod_manager(*game_config.find_child("editor_times", "id", "empty")))
, mp_settings_() , mp_settings_()
, game_classification_() , game_classification_()
, music_tracks_() , music_tracks_()

View file

@ -103,7 +103,7 @@ bool load_font_config()
return false; return false;
} }
const config &fonts_config = cfg.child("fonts"); auto fonts_config = cfg.optional_child("fonts");
if (!fonts_config) if (!fonts_config)
return false; return false;

View file

@ -970,12 +970,12 @@ variant event_callable::get_value(const std::string &key) const
} }
} else if(key == "weapon") { } else if(key == "weapon") {
if(event_info.data.has_child("first")) { if(event_info.data.has_child("first")) {
first_weapon = std::make_shared<attack_type>(event_info.data.child("first")); first_weapon = std::make_shared<attack_type>(event_info.data.mandatory_child("first"));
return variant(std::make_shared<attack_type_callable>(*first_weapon)); return variant(std::make_shared<attack_type_callable>(*first_weapon));
} }
} else if(key == "second_weapon") { } else if(key == "second_weapon") {
if(event_info.data.has_child("second")) { if(event_info.data.has_child("second")) {
second_weapon = std::make_shared<attack_type>(event_info.data.child("second")); second_weapon = std::make_shared<attack_type>(event_info.data.mandatory_child("second"));
return variant(std::make_shared<attack_type_callable>(*second_weapon)); return variant(std::make_shared<attack_type_callable>(*second_weapon));
} }
} }

View file

@ -142,11 +142,11 @@ std::set<std::string> game_classification::active_addons(const std::string& scen
continue; continue;
} }
} }
if(const config& cfg = game_config_manager::get()->game_config().find_child(current.type, "id", current.id)) { if(auto cfg = game_config_manager::get()->game_config().find_child(current.type, "id", current.id)) {
if(!cfg["addon_id"].empty()) { if(!cfg["addon_id"].empty()) {
res.insert(cfg["addon_id"]); res.insert(cfg["addon_id"]);
} }
for (const config& load_res : cfg.child_range("load_resource")) { for (const config& load_res : cfg->child_range("load_resource")) {
mods.emplace_back("resource", load_res["id"].str()); mods.emplace_back("resource", load_res["id"].str());
} }
} else { } else {

View file

@ -306,7 +306,7 @@ void load_config(const config &v)
default_victory_music = utils::split(v["default_victory_music"].str()); default_victory_music = utils::split(v["default_victory_music"].str());
default_defeat_music = utils::split(v["default_defeat_music"].str()); default_defeat_music = utils::split(v["default_defeat_music"].str());
if(const config& i = v.child("colors")){ if(auto i = v.optional_child("colors")){
using namespace game_config::colors; using namespace game_config::colors;
moved_orb_color = i["moved_orb_color"].str(); moved_orb_color = i["moved_orb_color"].str();
@ -324,7 +324,7 @@ void load_config(const config &v)
show_unmoved_orb = v["show_unmoved_orb"].to_bool(true); show_unmoved_orb = v["show_unmoved_orb"].to_bool(true);
show_disengaged_orb = v["show_disengaged_orb"].to_bool(true); show_disengaged_orb = v["show_disengaged_orb"].to_bool(true);
if(const config& i = v.child("images")){ if(auto i = v.optional_child("images")){
using namespace game_config::images; using namespace game_config::images;
game_title = i["game_title"].str(); game_title = i["game_title"].str();
@ -416,7 +416,7 @@ void load_config(const config &v)
server_list.push_back(sinf); server_list.push_back(sinf);
} }
if(const config& s = v.child("sounds")) { if(auto s = v.optional_child("sounds")) {
using namespace game_config::sounds; using namespace game_config::sounds;
const auto load_attribute = [](const config& c, const std::string& key, std::string& member) { const auto load_attribute = [](const config& c, const std::string& key, std::string& member) {
@ -425,26 +425,26 @@ void load_config(const config &v)
} }
}; };
load_attribute(s, "turn_bell", turn_bell); load_attribute(*s, "turn_bell", turn_bell);
load_attribute(s, "timer_bell", timer_bell); load_attribute(*s, "timer_bell", timer_bell);
load_attribute(s, "public_message", public_message); load_attribute(*s, "public_message", public_message);
load_attribute(s, "private_message", private_message); load_attribute(*s, "private_message", private_message);
load_attribute(s, "friend_message", friend_message); load_attribute(*s, "friend_message", friend_message);
load_attribute(s, "server_message", server_message); load_attribute(*s, "server_message", server_message);
load_attribute(s, "player_joins", player_joins); load_attribute(*s, "player_joins", player_joins);
load_attribute(s, "player_leaves", player_leaves); load_attribute(*s, "player_leaves", player_leaves);
load_attribute(s, "game_created", game_created); load_attribute(*s, "game_created", game_created);
load_attribute(s, "game_user_arrive", game_user_arrive); load_attribute(*s, "game_user_arrive", game_user_arrive);
load_attribute(s, "game_user_leave", game_user_leave); load_attribute(*s, "game_user_leave", game_user_leave);
load_attribute(s, "ready_for_start", ready_for_start); load_attribute(*s, "ready_for_start", ready_for_start);
load_attribute(s, "game_has_begun", game_has_begun); load_attribute(*s, "game_has_begun", game_has_begun);
if(const config & ss = s.child("status")) { if(auto ss = s->optional_child("status")) {
using namespace game_config::sounds::status; using namespace game_config::sounds::status;
load_attribute(ss, "poisoned", poisoned); load_attribute(*ss, "poisoned", poisoned);
load_attribute(ss, "slowed", slowed); load_attribute(*ss, "slowed", slowed);
load_attribute(ss, "petrified", petrified); load_attribute(*ss, "petrified", petrified);
} }
} }
} }

View file

@ -114,7 +114,7 @@ bool game_config_manager::init_game_config(FORCE_RELOAD_CONFIG force_reload)
load_game_config_with_loadscreen(force_reload, nullptr, ""); load_game_config_with_loadscreen(force_reload, nullptr, "");
game_config::load_config(game_config().child("game_config")); game_config::load_config(game_config().mandatory_child("game_config"));
// It's necessary to block the event thread while load_hotkeys() runs, otherwise keyboard input // It's necessary to block the event thread while load_hotkeys() runs, otherwise keyboard input
// can cause a crash by accessing the list of hotkeys while it's being modified. // can cause a crash by accessing the list of hotkeys while it's being modified.
@ -250,7 +250,7 @@ void game_config_manager::load_game_config(bool reload_everything, const game_cl
continue; continue;
} }
if(*&valid_cores.find_child("core", "id", id)) { if(valid_cores.find_child("core", "id", id)) {
events::call_in_main_thread([&]() { events::call_in_main_thread([&]() {
gui2::dialogs::wml_error::display( gui2::dialogs::wml_error::display(
_("Error validating data core."), _("Error validating data core."),

View file

@ -49,27 +49,35 @@ config_array_view game_config_view::child_range(config_key_type key) const
return res; return res;
} }
const config& game_config_view::find_child(config_key_type key, const std::string &name, const std::string &value) const optional_const_config game_config_view::find_child(config_key_type key, const std::string &name, const std::string &value) const
{ {
for(const config& cfg : cfgs_) { for(const config& cfg : cfgs_) {
if(const config& res = cfg.find_child(key, name, value)) { if(optional_const_config res = cfg.find_child(key, name, value)) {
return res; return res;
} }
} }
LOG_CONFIG << "gcv : cannot find [" << key << "] with " << name << "=" << value << ", count = " << cfgs_.size(); LOG_CONFIG << "gcv : cannot find [" << key << "] with " << name << "=" << value << ", count = " << cfgs_.size();
const config cfg; return optional_const_config();
return cfg.child("invalid");
} }
const config& game_config_view::child(config_key_type key) const const config& game_config_view::mandatory_child(config_key_type key) const
{ {
for(const config& cfg : cfgs_) { for(const config& cfg : cfgs_) {
if(const auto res = cfg.optional_child(key)) { if(const auto res = cfg.optional_child(key)) {
return res.value(); return res.value();
} }
} }
const config cfg; throw config::error("missing WML tag [" + std::string(key) + "]");
return cfg.child("invalid"); }
optional_const_config game_config_view::optional_child(config_key_type key) const
{
for(const config& cfg : cfgs_) {
if(const auto res = cfg.optional_child(key)) {
return res.value();
}
}
return optional_const_config();
} }

View file

@ -36,9 +36,11 @@ public:
config_array_view child_range(config_key_type key) const; config_array_view child_range(config_key_type key) const;
const config& find_child(config_key_type key, const std::string &name, const std::string &value) const; optional_const_config find_child(config_key_type key, const std::string &name, const std::string &value) const;
const config& child(config_key_type key) const; // const config& child(config_key_type key) const;
const config& mandatory_child(config_key_type key) const;
optional_const_config optional_child(config_key_type key) const;
const config& child_or_empty(config_key_type key) const; const config& child_or_empty(config_key_type key) const;

View file

@ -212,7 +212,7 @@ bool conditional_passed(const vconfig& cond)
return matches; return matches;
} }
bool matches_special_filter(const config &cfg, const vconfig& filter) bool matches_special_filter(optional_const_config cfg, const vconfig& filter)
{ {
if (!cfg) { if (!cfg) {
WRN_NG << "attempt to filter attack for an event with no attack data."; WRN_NG << "attempt to filter attack for an event with no attack data.";
@ -221,7 +221,7 @@ bool matches_special_filter(const config &cfg, const vconfig& filter)
} }
// Though it may seem wasteful to put this on the heap, it's necessary. // Though it may seem wasteful to put this on the heap, it's necessary.
// matches_filter() could potentially call a WFL formula, which would call shared_from_this(). // matches_filter() could potentially call a WFL formula, which would call shared_from_this().
auto attack = std::make_shared<const attack_type>(cfg); auto attack = std::make_shared<const attack_type>(*cfg);
return attack->matches_filter(filter.get_parsed_config()); return attack->matches_filter(filter.get_parsed_config());
} }

View file

@ -21,14 +21,14 @@
#pragma once #pragma once
class config;
class vconfig; class vconfig;
#include "config.hpp"
namespace game_events namespace game_events
{ {
bool conditional_passed(const vconfig& cond); bool conditional_passed(const vconfig& cond);
bool matches_special_filter(const config &cfg, const vconfig& filter); bool matches_special_filter(optional_const_config cfg, const vconfig& filter);
namespace builtin_conditions { namespace builtin_conditions {
bool have_unit(const vconfig& cfg); bool have_unit(const vconfig& cfg);

View file

@ -243,7 +243,7 @@ struct filter_attack : public event_filter {
const auto& loc = first_ ? event_info.loc1 : event_info.loc2; const auto& loc = first_ ? event_info.loc1 : event_info.loc2;
auto unit = units.find(loc); auto unit = units.find(loc);
if(unit != units.end() && loc.matches_unit(unit)) { if(unit != units.end() && loc.matches_unit(unit)) {
const config& attack = event_info.data.child(first_ ? "first" : "second"); auto attack = event_info.data.optional_child(first_ ? "first" : "second");
return swf_.empty() || matches_special_filter(attack, swf_); return swf_.empty() || matches_special_filter(attack, swf_);
} }
return false; return false;

View file

@ -208,8 +208,8 @@ void wml_event_pump::process_event(handler_ptr& handler_p, const queued_event& e
unit_map& units = resources::gameboard->units(); unit_map& units = resources::gameboard->units();
scoped_xy_unit first_unit("unit", ev.loc1, units); scoped_xy_unit first_unit("unit", ev.loc1, units);
scoped_xy_unit second_unit("second_unit", ev.loc2, units); scoped_xy_unit second_unit("second_unit", ev.loc2, units);
scoped_weapon_info first_weapon("weapon", ev.data.child("first")); scoped_weapon_info first_weapon("weapon", ev.data.optional_child("first"));
scoped_weapon_info second_weapon("second_weapon", ev.data.child("second")); scoped_weapon_info second_weapon("second_weapon", ev.data.optional_child("second"));
if(!handler_p->filter_event(ev)) { if(!handler_p->filter_event(ev)) {
return; return;

View file

@ -92,8 +92,8 @@ connect_engine::connect_engine(saved_game& state, const bool first_scenario, mp_
config::child_itors sides = current_config()->child_range("side"); config::child_itors sides = current_config()->child_range("side");
// AI algorithms. // AI algorithms.
if(const config& era = level_.child("era")) { if(auto era = level_.optional_child("era")) {
ai::configuration::add_era_ai_from_config(era); ai::configuration::add_era_ai_from_config(*era);
} }
ai::configuration::add_mod_ai_from_config(level_.child_range("modification")); ai::configuration::add_mod_ai_from_config(level_.child_range("modification"));
@ -174,7 +174,7 @@ connect_engine::connect_engine(saved_game& state, const bool first_scenario, mp_
} }
// Selected era's factions. // Selected era's factions.
for(const config& era : level_.child("era").child_range("multiplayer_side")) { for(const config& era : level_.mandatory_child("era").child_range("multiplayer_side")) {
era_factions_.push_back(&era); era_factions_.push_back(&era);
} }
@ -225,11 +225,7 @@ connect_engine::connect_engine(saved_game& state, const bool first_scenario, mp_
config* connect_engine::current_config() { config* connect_engine::current_config() {
if(config& s = scenario()) { return &scenario();
return &s;
}
return nullptr;
} }
void connect_engine::import_user(const std::string& name, const bool observer, int side_taken) void connect_engine::import_user(const std::string& name, const bool observer, int side_taken)
@ -422,7 +418,7 @@ void connect_engine::start_game()
// Shuffle sides (check settings and if it is a re-loaded game). // Shuffle sides (check settings and if it is a re-loaded game).
// Must be done after resolve_random() or shuffle sides, or they won't work. // Must be done after resolve_random() or shuffle sides, or they won't work.
if(state_.mp_settings().shuffle_sides && !force_lock_settings_ && !(level_.child("snapshot") && level_.child("snapshot").child("side"))) { if(state_.mp_settings().shuffle_sides && !force_lock_settings_ && !(level_.has_child("snapshot") && level_.mandatory_child("snapshot").has_child("side"))) {
// Only playable sides should be shuffled. // Only playable sides should be shuffled.
std::vector<int> playable_sides; std::vector<int> playable_sides;
@ -506,7 +502,7 @@ void connect_engine::start_game_commandline(const commandline_options& cmdline_o
// Set AI algorithm to default for all sides, // Set AI algorithm to default for all sides,
// then override if commandline option was given. // then override if commandline option was given.
std::string ai_algorithm = game_config.child("ais")["default_ai_algorithm"].str(); std::string ai_algorithm = game_config.mandatory_child("ais")["default_ai_algorithm"].str();
side->set_ai_algorithm(ai_algorithm); side->set_ai_algorithm(ai_algorithm);
if(cmdline_opts.multiplayer_algorithm) { if(cmdline_opts.multiplayer_algorithm) {
@ -578,13 +574,13 @@ std::pair<bool, bool> connect_engine::process_network_data(const config& data)
{ {
std::pair<bool, bool> result(false, true); std::pair<bool, bool> result(false, true);
if(data.child("leave_game")) { if(data.has_child("leave_game")) {
result.first = true; result.first = true;
return result; return result;
} }
// A side has been dropped. // A side has been dropped.
if(const config& side_drop = data.child("side_drop")) { if(auto side_drop = data.optional_child("side_drop")) {
unsigned side_index = side_drop["side_num"].to_int() - 1; unsigned side_index = side_drop["side_num"].to_int() - 1;
if(side_index < side_engines_.size()) { if(side_index < side_engines_.size()) {
@ -691,20 +687,20 @@ std::pair<bool, bool> connect_engine::process_network_data(const config& data)
} }
} }
if(const config& change_faction = data.child("change_faction")) { if(auto change_faction = data.optional_child("change_faction")) {
int side_taken = find_user_side_index_by_id(change_faction["name"]); int side_taken = find_user_side_index_by_id(change_faction["name"]);
if(side_taken != -1 || !first_scenario_) { if(side_taken != -1 || !first_scenario_) {
import_user(change_faction, false, side_taken); import_user(*change_faction, false, side_taken);
update_and_send_diff(); update_and_send_diff();
} }
} }
if(const config& observer = data.child("observer")) { if(auto observer = data.optional_child("observer")) {
import_user(observer, true); import_user(*observer, true);
update_and_send_diff(); update_and_send_diff();
} }
if(const config& observer = data.child("observer_quit")) { if(auto observer = data.optional_child("observer_quit")) {
const std::string& observer_name = observer["name"]; const std::string& observer_name = observer["name"];
if(connected_users().find(observer_name) != connected_users().end()) { if(connected_users().find(observer_name) != connected_users().end()) {
@ -773,12 +769,12 @@ void connect_engine::save_reserved_sides_information()
} }
} }
level_.child("multiplayer")["side_users"] = utils::join_map(side_users); level_.mandatory_child("multiplayer")["side_users"] = utils::join_map(side_users);
} }
void connect_engine::load_previous_sides_users() void connect_engine::load_previous_sides_users()
{ {
std::map<std::string, std::string> side_users = utils::map_split(level_.child("multiplayer")["side_users"]); std::map<std::string, std::string> side_users = utils::map_split(level_.mandatory_child("multiplayer")["side_users"]);
std::set<std::string> names; std::set<std::string> names;
for(side_engine_ptr side : side_engines_) { for(side_engine_ptr side : side_engines_) {
const std::string& save_id = side->previous_save_id(); const std::string& save_id = side->previous_save_id();
@ -942,7 +938,7 @@ side_engine::side_engine(const config& cfg, connect_engine& parent_engine, const
} }
// Initialize ai algorithm. // Initialize ai algorithm.
if(const config& ai = cfg.child("ai")) { if(auto ai = cfg.optional_child("ai")) {
ai_algorithm_ = ai["ai_algorithm"].str(); ai_algorithm_ = ai["ai_algorithm"].str();
} }
} }

View file

@ -82,10 +82,10 @@ public:
const config& level() const { return level_; } const config& level() const { return level_; }
config& scenario() config& scenario()
{ {
if(config& scenario = level_.child("scenario")) if(auto scenario = level_.optional_child("scenario"))
return scenario; return *scenario;
else if(config& snapshot = level_.child("snapshot")) else if(auto snapshot = level_.optional_child("snapshot"))
return snapshot; return *snapshot;
else else
throw "No scenariodata found"; throw "No scenariodata found";
} }

View file

@ -165,7 +165,7 @@ random_map::random_map(const config& data)
data_["description"] = "Error: Random map found with missing generator information. Scenario should have a [generator] child."; data_["description"] = "Error: Random map found with missing generator information. Scenario should have a [generator] child.";
data_["error_message"] = "missing [generator] tag"; data_["error_message"] = "missing [generator] tag";
} else { } else {
generator_data_ = data.child("generator"); generator_data_ = data.mandatory_child("generator");
} }
if(!data.has_attribute("scenario_generation") && !data.has_attribute("map_generation")) { if(!data.has_attribute("scenario_generation") && !data.has_attribute("map_generation")) {
@ -646,8 +646,8 @@ const mp_game_settings& create_engine::get_parameters()
void create_engine::init_all_levels() void create_engine::init_all_levels()
{ {
if(const config& generic_multiplayer = game_config_.child("generic_multiplayer")) { if(auto generic_multiplayer = game_config_.optional_child("generic_multiplayer")) {
config gen_mp_data = generic_multiplayer; config gen_mp_data = *generic_multiplayer;
// User maps. // User maps.
int dep_index_offset = 0; int dep_index_offset = 0;

View file

@ -155,7 +155,7 @@ bool manager::exists(const elem& e) const
std::string manager::find_name_for(const elem& e) const std::string manager::find_name_for(const elem& e) const
{ {
const config& cfg = depinfo_.find_child(e.type, "id", e.id); auto cfg = depinfo_.find_child(e.type, "id", e.id);
return cfg["name"]; return cfg["name"];
} }
@ -182,7 +182,7 @@ std::vector<std::string> manager::get_required(const elem& e) const
return result; return result;
} }
config data = depinfo_.find_child(e.type, "id", e.id); config data = *depinfo_.find_child(e.type, "id", e.id);
if(data.has_attribute("force_modification")) { if(data.has_attribute("force_modification")) {
result = utils::split(data["force_modification"].str(), ','); result = utils::split(data["force_modification"].str(), ',');
@ -230,8 +230,8 @@ bool manager::does_conflict(const elem& elem1, const elem& elem2, bool directonl
return false; return false;
} }
config data1 = depinfo_.find_child(elem1.type, "id", elem1.id); config data1 = *depinfo_.find_child(elem1.type, "id", elem1.id);
config data2 = depinfo_.find_child(elem2.type, "id", elem2.id); config data2 = *depinfo_.find_child(elem2.type, "id", elem2.id);
// Whether we should skip the check entirely // Whether we should skip the check entirely
if(data1.has_attribute("ignore_incompatible_" + elem2.type)) { if(data1.has_attribute("ignore_incompatible_" + elem2.type)) {
@ -323,7 +323,7 @@ bool manager::does_require(const elem& elem1, const elem& elem2) const
return false; return false;
} }
config data = depinfo_.find_child(elem1.type, "id", elem1.id); config data = *depinfo_.find_child(elem1.type, "id", elem1.id);
if(data.has_attribute("force_modification")) { if(data.has_attribute("force_modification")) {
std::vector<std::string> required = utils::split(data["force_modification"]); std::vector<std::string> required = utils::split(data["force_modification"]);
@ -369,7 +369,7 @@ void manager::try_modifications(const std::vector<std::string>& ids, bool force)
void manager::try_modification_by_index(int index, bool activate, bool force) void manager::try_modification_by_index(int index, bool activate, bool force)
{ {
std::string id = depinfo_.child("modification", index)["id"]; std::string id = depinfo_.mandatory_child("modification", index)["id"];
std::vector<std::string> mods_copy = mods_; std::vector<std::string> mods_copy = mods_;
if(activate) { if(activate) {
@ -388,12 +388,12 @@ void manager::try_modification_by_index(int index, bool activate, bool force)
void manager::try_era_by_index(int index, bool force) void manager::try_era_by_index(int index, bool force)
{ {
try_era(depinfo_.child("era", index)["id"], force); try_era(depinfo_.mandatory_child("era", index)["id"], force);
} }
void manager::try_scenario_by_index(int index, bool force) void manager::try_scenario_by_index(int index, bool force)
{ {
try_scenario(depinfo_.child("scenario", index)["id"], force); try_scenario(depinfo_.mandatory_child("scenario", index)["id"], force);
} }
int manager::get_era_index() const int manager::get_era_index() const
@ -427,7 +427,7 @@ int manager::get_scenario_index() const
bool manager::is_modification_active(int index) const bool manager::is_modification_active(int index) const
{ {
std::string id = depinfo_.child("modification", index)["id"]; std::string id = depinfo_.mandatory_child("modification", index)["id"];
return std::find(mods_.begin(), mods_.end(), id) != mods_.end(); return std::find(mods_.begin(), mods_.end(), id) != mods_.end();
} }

View file

@ -62,8 +62,8 @@ flg_manager::flg_manager(const std::vector<const config*>& era_factions,
const std::string& leader_id = side["id"]; const std::string& leader_id = side["id"];
if(!leader_id.empty()) { if(!leader_id.empty()) {
// Check if leader was carried over and now is in [unit] tag. // Check if leader was carried over and now is in [unit] tag.
default_leader_cfg_ = &side.find_child("unit", "id", leader_id); default_leader_cfg_ = side.find_child("unit", "id", leader_id).ptr();
if(*default_leader_cfg_) { if(default_leader_cfg_) {
default_leader_type_ = (*default_leader_cfg_)["type"].str(); default_leader_type_ = (*default_leader_cfg_)["type"].str();
default_leader_gender_ = (*default_leader_cfg_)["gender"].str(); default_leader_gender_ = (*default_leader_cfg_)["gender"].str();
} else { } else {
@ -546,8 +546,8 @@ void flg_manager::set_current_gender(const std::string& gender)
const config& flg_manager::get_default_faction(const config& cfg) const config& flg_manager::get_default_faction(const config& cfg)
{ {
if(const config& df = cfg.child("default_faction")) { if(auto df = cfg.optional_child("default_faction")) {
return df; return *df;
} else { } else {
return cfg; return cfg;
} }

View file

@ -175,13 +175,13 @@ game_info::game_info(const config& game, const std::vector<std::string>& install
} }
if(!game["mp_era"].empty()) { if(!game["mp_era"].empty()) {
const config& era_cfg = game_config.find_child("era", "id", game["mp_era"]); auto era_cfg = game_config.find_child("era", "id", game["mp_era"]);
const bool require = game["require_era"].to_bool(true); const bool require = game["require_era"].to_bool(true);
if(era_cfg) { if(era_cfg) {
era = era_cfg["name"].str(); era = era_cfg["name"].str();
if(require) { if(require) {
addon_req result = check_addon_version_compatibility(era_cfg, game); addon_req result = check_addon_version_compatibility(*era_cfg, game);
addons_outcome = std::max(addons_outcome, result); // Elevate to most severe error level encountered so far addons_outcome = std::max(addons_outcome, result); // Elevate to most severe error level encountered so far
} }
} else { } else {
@ -206,8 +206,8 @@ game_info::game_info(const config& game, const std::vector<std::string>& install
info_stream << ' ' << mod_info.back().first; info_stream << ' ' << mod_info.back().first;
if(cfg["require_modification"].to_bool(false)) { if(cfg["require_modification"].to_bool(false)) {
if(const config& mod = game_config.find_child("modification", "id", cfg["id"])) { if(auto mod = game_config.find_child("modification", "id", cfg["id"])) {
addon_req result = check_addon_version_compatibility(mod, game); addon_req result = check_addon_version_compatibility(*mod, game);
addons_outcome = std::max(addons_outcome, result); // Elevate to most severe error level encountered so far addons_outcome = std::max(addons_outcome, result); // Elevate to most severe error level encountered so far
} else { } else {
have_all_mods = false; have_all_mods = false;
@ -252,15 +252,15 @@ game_info::game_info(const config& game, const std::vector<std::string>& install
// //
if(!game["mp_scenario"].empty() && game["mp_campaign"].empty()) { if(!game["mp_scenario"].empty() && game["mp_campaign"].empty()) {
// Check if it's a multiplayer scenario // Check if it's a multiplayer scenario
const config* level_cfg = &game_config.find_child("multiplayer", "id", game["mp_scenario"]); const config* level_cfg = game_config.find_child("multiplayer", "id", game["mp_scenario"]).ptr();
const bool require = game["require_scenario"].to_bool(false); const bool require = game["require_scenario"].to_bool(false);
// Check if it's a user map // Check if it's a user map
if(!*level_cfg) { if(level_cfg) {
level_cfg = &game_config.find_child("generic_multiplayer", "id", game["mp_scenario"]); level_cfg = game_config.find_child("generic_multiplayer", "id", game["mp_scenario"]).ptr();
} }
if(*level_cfg) { if(level_cfg) {
type_marker = make_game_type_marker(_("scenario_abbreviation^S"), false); type_marker = make_game_type_marker(_("scenario_abbreviation^S"), false);
scenario = (*level_cfg)["name"].str(); scenario = (*level_cfg)["name"].str();
info_stream << scenario; info_stream << scenario;
@ -268,10 +268,10 @@ game_info::game_info(const config& game, const std::vector<std::string>& install
// Reloaded games do not match the original scenario hash, so it makes no sense // Reloaded games do not match the original scenario hash, so it makes no sense
// to test them, since they always would appear as remote scenarios // to test them, since they always would appear as remote scenarios
if(!reloaded) { if(!reloaded) {
if(const config& hashes = game_config.child("multiplayer_hashes")) { if(auto hashes = game_config.optional_child("multiplayer_hashes")) {
std::string hash = game["hash"]; std::string hash = game["hash"];
bool hash_found = false; bool hash_found = false;
for(const auto & i : hashes.attribute_range()) { for(const auto & i : hashes->attribute_range()) {
if(i.first == game["mp_scenario"] && i.second == hash) { if(i.first == game["mp_scenario"] && i.second == hash) {
hash_found = true; hash_found = true;
break; break;
@ -301,7 +301,7 @@ game_info::game_info(const config& game, const std::vector<std::string>& install
verified = false; verified = false;
} }
} else if(!game["mp_campaign"].empty()) { } else if(!game["mp_campaign"].empty()) {
if(const config& campaign_cfg = game_config.find_child("campaign", "id", game["mp_campaign"])) { if(auto campaign_cfg = game_config.find_child("campaign", "id", game["mp_campaign"])) {
type_marker = make_game_type_marker(_("campaign_abbreviation^C"), false); type_marker = make_game_type_marker(_("campaign_abbreviation^C"), false);
std::stringstream campaign_text; std::stringstream campaign_text;
@ -310,7 +310,7 @@ game_info::game_info(const config& game, const std::vector<std::string>& install
<< game["mp_scenario_name"]; << game["mp_scenario_name"];
// Difficulty // Difficulty
config difficulties = gui2::dialogs::generate_difficulty_config(campaign_cfg); config difficulties = gui2::dialogs::generate_difficulty_config(*campaign_cfg);
for(const config& difficulty : difficulties.child_range("difficulty")) { for(const config& difficulty : difficulties.child_range("difficulty")) {
if(difficulty["define"] == game["difficulty_define"]) { if(difficulty["define"] == game["difficulty_define"]) {
campaign_text << spaced_em_dash() << difficulty["description"]; campaign_text << spaced_em_dash() << difficulty["description"];
@ -324,7 +324,7 @@ game_info::game_info(const config& game, const std::vector<std::string>& install
// TODO: should we have this? // TODO: should we have this?
//if(game["require_scenario"].to_bool(false)) { //if(game["require_scenario"].to_bool(false)) {
addon_req result = check_addon_version_compatibility(campaign_cfg, game); addon_req result = check_addon_version_compatibility(*campaign_cfg, game);
addons_outcome = std::max(addons_outcome, result); // Elevate to most severe error level encountered so far addons_outcome = std::max(addons_outcome, result); // Elevate to most severe error level encountered so far
//} //}
} else { } else {
@ -412,7 +412,7 @@ game_info::addon_req game_info::check_addon_version_compatibility(const config&
return addon_req::SATISFIED; return addon_req::SATISFIED;
} }
if(const config& game_req = game.find_child("addon", "id", local_item["addon_id"])) { if(auto game_req = game.find_child("addon", "id", local_item["addon_id"])) {
if(!game_req["require"].to_bool(false)) { if(!game_req["require"].to_bool(false)) {
return addon_req::SATISFIED; return addon_req::SATISFIED;
} }
@ -429,7 +429,7 @@ game_info::addon_req game_info::check_addon_version_compatibility(const config&
// Remote version // Remote version
const version_info remote_ver(game_req["version"].str()); const version_info remote_ver(game_req["version"].str());
version_info remote_min_ver(game_req.has_attribute("min_version") ? game_req["min_version"] : game_req["version"]); version_info remote_min_ver(game_req->has_attribute("min_version") ? game_req["min_version"] : game_req["version"]);
remote_min_ver = std::min(remote_min_ver, remote_ver); remote_min_ver = std::min(remote_min_ver, remote_ver);

View file

@ -126,13 +126,13 @@ void lobby_info::process_gamelist(const config& data)
games_by_id_.clear(); games_by_id_.clear();
for(const auto& c : gamelist_.child("gamelist").child_range("game")) { for(const auto& c : gamelist_.mandatory_child("gamelist").child_range("game")) {
game_info game(c, installed_addons_); game_info game(c, installed_addons_);
games_by_id_.emplace(game.id, std::move(game)); games_by_id_.emplace(game.id, std::move(game));
} }
DBG_LB << dump_games_map(games_by_id_); DBG_LB << dump_games_map(games_by_id_);
DBG_LB << dump_games_config(gamelist_.child("gamelist")); DBG_LB << dump_games_config(gamelist_.mandatory_child("gamelist"));
process_userlist(); process_userlist();
} }
@ -155,7 +155,7 @@ bool lobby_info::process_gamelist_diff_impl(const config& data)
return false; return false;
} }
DBG_LB << "prediff " << dump_games_config(gamelist_.child("gamelist")); DBG_LB << "prediff " << dump_games_config(gamelist_.mandatory_child("gamelist"));
try { try {
gamelist_.apply_diff(data, true); gamelist_.apply_diff(data, true);
@ -164,10 +164,10 @@ bool lobby_info::process_gamelist_diff_impl(const config& data)
return false; return false;
} }
DBG_LB << "postdiff " << dump_games_config(gamelist_.child("gamelist")); DBG_LB << "postdiff " << dump_games_config(gamelist_.mandatory_child("gamelist"));
DBG_LB << dump_games_map(games_by_id_); DBG_LB << dump_games_map(games_by_id_);
for(config& c : gamelist_.child("gamelist").child_range("game")) { for(config& c : gamelist_.mandatory_child("gamelist").child_range("game")) {
DBG_LB << "data process: " << c["id"] << " (" << c[config::diff_track_attribute] << ")"; DBG_LB << "data process: " << c["id"] << " (" << c[config::diff_track_attribute] << ")";
const int game_id = c["id"]; const int game_id = c["id"];
@ -219,7 +219,7 @@ bool lobby_info::process_gamelist_diff_impl(const config& data)
return false; return false;
} }
DBG_LB << "postclean " << dump_games_config(gamelist_.child("gamelist")); DBG_LB << "postclean " << dump_games_config(gamelist_.mandatory_child("gamelist"));
process_userlist(); process_userlist();
return true; return true;

View file

@ -96,7 +96,7 @@ config initial_level_config(saved_game& state)
*/ */
const game_config_view& game_config = game_config_manager::get()->game_config(); const game_config_view& game_config = game_config_manager::get()->game_config();
const config& era_cfg = game_config.find_child("era", "id", era); auto era_cfg = game_config.find_child("era", "id", era);
if(!era_cfg) { if(!era_cfg) {
if(params.saved_game == saved_game_mode::type::no) { if(params.saved_game == saved_game_mode::type::no) {
@ -107,20 +107,20 @@ config initial_level_config(saved_game& state)
WRN_CF << "Missing era in MP load game '" << era << "'"; WRN_CF << "Missing era in MP load game '" << era << "'";
} else { } else {
level.add_child("era", era_cfg); level.add_child("era", *era_cfg);
// Initialize the list of sides available for the current era. // Initialize the list of sides available for the current era.
// We also need this so not to get a segfault in mp_staging for ai configuration. // We also need this so not to get a segfault in mp_staging for ai configuration.
const config& custom_side = game_config.find_child("multiplayer_side", "id", "Custom"); auto custom_side = game_config.find_child("multiplayer_side", "id", "Custom");
level.child("era").add_child_at("multiplayer_side", custom_side, 0); level.mandatory_child("era").add_child_at("multiplayer_side", *custom_side, 0);
} }
// Add modifications, needed for ai algorithms which are applied in mp_staging. // Add modifications, needed for ai algorithms which are applied in mp_staging.
const std::vector<std::string>& mods = state.classification().active_mods; const std::vector<std::string>& mods = state.classification().active_mods;
for(unsigned i = 0; i < mods.size(); ++i) { for(unsigned i = 0; i < mods.size(); ++i) {
if(const config& mod_cfg = game_config.find_child("modification", "id", mods[i])) { if(auto mod_cfg = game_config.find_child("modification", "id", mods[i])) {
level.add_child("modification", mod_cfg); level.add_child("modification", *mod_cfg);
} }
} }

View file

@ -520,8 +520,8 @@ bool mp_manager::enter_lobby_mode()
// We use a loop here to allow returning to the lobby if you, say, cancel game creation. // We use a loop here to allow returning to the lobby if you, say, cancel game creation.
while(true) { while(true) {
if(const config& cfg = game_config_manager::get()->game_config().child("lobby_music")) { if(auto cfg = game_config_manager::get()->game_config().optional_child("lobby_music")) {
for(const config& i : cfg.child_range("music")) { for(const config& i : cfg->child_range("music")) {
sound::play_music_config(i); sound::play_music_config(i);
} }
@ -739,7 +739,7 @@ void start_local_game_commandline(const commandline_options& cmdline_opts)
state.classification().era_id = *cmdline_opts.multiplayer_era; state.classification().era_id = *cmdline_opts.multiplayer_era;
} }
if(const config& cfg_era = game_config.find_child("era", "id", state.classification().era_id)) { if(auto cfg_era = game_config.find_child("era", "id", state.classification().era_id)) {
state.classification().era_define = cfg_era["define"].str(); state.classification().era_define = cfg_era["define"].str();
} else { } else {
PLAIN_LOG << "Could not find era '" << state.classification().era_id << "'"; PLAIN_LOG << "Could not find era '" << state.classification().era_id << "'";
@ -751,7 +751,7 @@ void start_local_game_commandline(const commandline_options& cmdline_opts)
parameters.name = *cmdline_opts.multiplayer_scenario; parameters.name = *cmdline_opts.multiplayer_scenario;
} }
if(const config& cfg_multiplayer = game_config.find_child("multiplayer", "id", parameters.name)) { if(auto cfg_multiplayer = game_config.find_child("multiplayer", "id", parameters.name)) {
state.classification().scenario_define = cfg_multiplayer["define"].str(); state.classification().scenario_define = cfg_multiplayer["define"].str();
} else { } else {
PLAIN_LOG << "Could not find [multiplayer] '" << parameters.name << "'"; PLAIN_LOG << "Could not find [multiplayer] '" << parameters.name << "'";

View file

@ -63,9 +63,9 @@ game_state::game_state(const config& level, play_controller& pc)
, first_human_team_(-1) , first_human_team_(-1)
{ {
lua_kernel_->load_core(); lua_kernel_->load_core();
if(const config& endlevel_cfg = level.child("end_level_data")) { if(auto endlevel_cfg = level.optional_child("end_level_data")) {
end_level_data el_data; end_level_data el_data;
el_data.read(endlevel_cfg); el_data.read(*endlevel_cfg);
el_data.transient.carryover_report = false; el_data.transient.carryover_report = false;
end_level_data_ = el_data; end_level_data_ = el_data;
} }

View file

@ -37,7 +37,7 @@ cave_map_generator::cave_map_generator(const config &cfg) :
village_(t_translation::UNDERGROUND_VILLAGE), village_(t_translation::UNDERGROUND_VILLAGE),
castle_(t_translation::DWARVEN_CASTLE), castle_(t_translation::DWARVEN_CASTLE),
keep_(t_translation::DWARVEN_KEEP), keep_(t_translation::DWARVEN_KEEP),
cfg_(cfg ? cfg : config()), cfg_(cfg),
width_(50), width_(50),
height_(50), height_(50),
village_density_(0), village_density_(0),
@ -193,8 +193,8 @@ void cave_map_generator::cave_map_generator_job::generate_chambers()
new_chamber.center = map_location(x,y); new_chamber.center = map_location(x,y);
build_chamber(new_chamber.center,new_chamber.locs,chamber_size,jagged_edges); build_chamber(new_chamber.center,new_chamber.locs,chamber_size,jagged_edges);
const config &items = ch.child("items"); auto items = ch.optional_child("items");
new_chamber.items = items ? &items : nullptr; new_chamber.items = items ? &*items : nullptr;
const std::string &id = ch["id"]; const std::string &id = ch["id"];
if (!id.empty()) { if (!id.empty()) {
@ -231,11 +231,12 @@ void cave_map_generator::cave_map_generator_job::place_chamber(const chamber& c)
for (const config::any_child it : c.items->all_children_range()) for (const config::any_child it : c.items->all_children_range())
{ {
config cfg = it.cfg; config cfg = it.cfg;
config &filter = cfg.child("filter"); auto filter = cfg.optional_child("filter");
config* object_filter = nullptr; config* object_filter = nullptr;
if (config &object = cfg.child("object")) { if (auto object = cfg.optional_child("object")) {
if (config &of = object.child("filter")) if (auto of = object->optional_child("filter")) {
object_filter = &of; object_filter = &*of;
}
} }
if (!it.cfg["same_location_as_previous"].to_bool()) { if (!it.cfg["same_location_as_previous"].to_bool()) {

View file

@ -67,7 +67,7 @@ std::string default_map_generator::name() const { return "default"; }
std::string default_map_generator::config_name() const std::string default_map_generator::config_name() const
{ {
if (const config &c = cfg_.child("scenario")) if (auto c = cfg_.optional_child("scenario"))
return c["name"]; return c["name"];
return std::string(); return std::string();

View file

@ -109,7 +109,7 @@ namespace {
static std::string terrain; static std::string terrain;
terrain = t_translation::write_terrain_code(c); terrain = t_translation::write_terrain_code(c);
double res = getNoPathValue(); double res = getNoPathValue();
if(const config &child = cfg_.find_child("road_cost", "terrain", terrain)) { if(auto child = cfg_.find_child("road_cost", "terrain", terrain)) {
res = child["cost"].to_double(); res = child["cost"].to_double();
} }
@ -628,7 +628,7 @@ static map_location place_village(const t_translation::ter_map& map,
const t_translation::terrain_code t = map[i.x][i.y]; const t_translation::terrain_code t = map[i.x][i.y];
const std::string str = t_translation::write_terrain_code(t); const std::string str = t_translation::write_terrain_code(t);
if(const config &child = cfg.find_child("village", "terrain", str)) { if(auto child = cfg.find_child("village", "terrain", str)) {
tcode_list_cache::iterator l = adj_liked_cache.find(t); tcode_list_cache::iterator l = adj_liked_cache.find(t);
t_translation::ter_list *adjacent_liked; t_translation::ter_list *adjacent_liked;
if(l != adj_liked_cache.end()) { if(l != adj_liked_cache.end()) {
@ -707,7 +707,7 @@ std::string default_map_generator_job::default_generate_map(generator_data data,
VALIDATE(is_even(data.width), _("Random maps with an odd width aren't supported.")); VALIDATE(is_even(data.width), _("Random maps with an odd width aren't supported."));
// Try to find configuration for castles // Try to find configuration for castles
const config& castle_config = cfg.child("castle"); auto castle_config = cfg.optional_child("castle");
int ticks = SDL_GetTicks(); int ticks = SDL_GetTicks();
@ -721,8 +721,8 @@ std::string default_map_generator_job::default_generate_map(generator_data data,
config naming; config naming;
if(cfg.has_child("naming")) { if(cfg.has_child("naming")) {
naming = game_config_.child("naming"); naming = game_config_.mandatory_child("naming");
naming.append_attributes(cfg.child("naming")); naming.append_attributes(cfg.mandatory_child("naming"));
} }
// If the [naming] child is empty, we cannot provide good names. // If the [naming] child is empty, we cannot provide good names.
@ -1055,8 +1055,8 @@ std::string default_map_generator_job::default_generate_map(generator_data data,
} }
// Find the configuration which tells us what to convert this tile to, to make it into a road. // Find the configuration which tells us what to convert this tile to, to make it into a road.
const config& child = cfg.find_child("road_cost", "terrain", t_translation::write_terrain_code(terrain[x][y])); auto child = cfg.find_child("road_cost", "terrain", t_translation::write_terrain_code(terrain[x][y]));
if(child.empty()){ if(!child || child->empty()){
continue; continue;
} }
@ -1255,10 +1255,10 @@ std::string default_map_generator_job::default_generate_map(generator_data data,
std::set<std::string> used_names; std::set<std::string> used_names;
tcode_list_cache adj_liked_cache; tcode_list_cache adj_liked_cache;
config village_naming = game_config_.child("village_naming"); config village_naming = game_config_.mandatory_child("village_naming");
if(cfg.has_child("village_naming")) { if(cfg.has_child("village_naming")) {
village_naming.append_attributes(cfg.child("village_naming")); village_naming.append_attributes(cfg.mandatory_child("village_naming"));
} }
// If the [village_naming] child is empty, we cannot provide good names. // If the [village_naming] child is empty, we cannot provide good names.

View file

@ -122,7 +122,7 @@ gui_definition::gui_definition(const config& cfg)
* - Override the default and above per instance of the widget, some buttons * - Override the default and above per instance of the widget, some buttons
* can give a different sound. * can give a different sound.
*/ */
const config& settings = cfg.child("settings"); const config& settings = cfg.mandatory_child("settings");
popup_show_delay_ = settings["popup_show_delay"]; popup_show_delay_ = settings["popup_show_delay"];
popup_show_time_ = settings["popup_show_time"]; popup_show_time_ = settings["popup_show_time"];

View file

@ -25,10 +25,10 @@
namespace gui2 namespace gui2
{ {
state_definition::state_definition(const config& cfg) state_definition::state_definition(optional_const_config cfg)
: canvas_cfg_(cfg ? cfg.child("draw") : cfg) : canvas_cfg_(cfg ? cfg->child_or_empty("draw") : config())
{ {
VALIDATE(canvas_cfg_, _("No state or draw section defined.")); VALIDATE(cfg && cfg->has_child("draw"), _("No state or draw section defined."));
} }
resolution_definition::resolution_definition(const config& cfg) resolution_definition::resolution_definition(const config& cfg)

View file

@ -35,7 +35,7 @@ namespace gui2
*/ */
struct state_definition struct state_definition
{ {
explicit state_definition(const config& cfg); explicit state_definition(optional_const_config cfg);
config canvas_cfg_; config canvas_cfg_;
}; };

View file

@ -162,11 +162,11 @@ builder_window::window_resolution::window_resolution(const config& cfg)
wfl::formula(cfg["functions"], &functions).evaluate(); wfl::formula(cfg["functions"], &functions).evaluate();
} }
const config& c = cfg.child("grid"); auto c = cfg.optional_child("grid");
VALIDATE(c, _("No grid defined.")); VALIDATE(c, _("No grid defined."));
grid = std::make_shared<builder_grid>(c); grid = std::make_shared<builder_grid>(*c);
if(!automatic_placement) { if(!automatic_placement) {
VALIDATE(width.has_formula() || width(), missing_mandatory_wml_key("resolution", "width")); VALIDATE(width.has_formula() || width(), missing_mandatory_wml_key("resolution", "width"));

View file

@ -545,7 +545,8 @@ void addon_manager::toggle_details(button& btn, stacked_widget& stk)
void addon_manager::fetch_addons_list() void addon_manager::fetch_addons_list()
{ {
client_.request_addons_list(cfg_); client_.request_addons_list(cfg_);
if(!cfg_) { // TODO: this was a faulty invalid config test.
if((false)) {
gui2::show_error_message(_("An error occurred while downloading the add-ons list from the server.")); gui2::show_error_message(_("An error occurred while downloading the add-ons list from the server."));
get_window()->close(); get_window()->close();
} }

View file

@ -110,7 +110,7 @@ void campaign_difficulty::post_show(window& window)
{ {
if(get_retval() == retval::OK) { if(get_retval() == retval::OK) {
listbox& list = find_widget<listbox>(&window, "listbox", false); listbox& list = find_widget<listbox>(&window, "listbox", false);
selected_difficulty_ = difficulties_.child("difficulty", list.get_selected_row())["define"].str(); selected_difficulty_ = difficulties_.mandatory_child("difficulty", list.get_selected_row())["define"].str();
} }
} }
} // namespace dialogs } // namespace dialogs

View file

@ -370,8 +370,8 @@ void game_load::evaluate_summary_string(std::stringstream& str, const config& cf
case campaign_type::type::scenario: { case campaign_type::type::scenario: {
const config* campaign = nullptr; const config* campaign = nullptr;
if(!campaign_id.empty()) { if(!campaign_id.empty()) {
if(const config& c = cache_config_.find_child("campaign", "id", campaign_id)) { if(auto c = cache_config_.find_child("campaign", "id", campaign_id)) {
campaign = &c; campaign = c.ptr();
} }
} }
@ -421,8 +421,8 @@ void game_load::evaluate_summary_string(std::stringstream& str, const config& cf
case campaign_type::type::multiplayer: { case campaign_type::type::multiplayer: {
const config* campaign = nullptr; const config* campaign = nullptr;
if (!campaign_id.empty()) { if (!campaign_id.empty()) {
if (const config& c = cache_config_.find_child("campaign", "id", campaign_id)) { if (auto c = cache_config_.find_child("campaign", "id", campaign_id)) {
campaign = &c; campaign = c.ptr();
} }
} }
@ -433,7 +433,7 @@ void game_load::evaluate_summary_string(std::stringstream& str, const config& cf
if (campaign != nullptr) { if (campaign != nullptr) {
str << "\n" << _("Difficulty: "); str << "\n" << _("Difficulty: ");
try { try {
const config& difficulty = campaign->find_child("difficulty", "define", cfg_summary["difficulty"]); auto difficulty = campaign->find_child("difficulty", "define", cfg_summary["difficulty"]);
std::ostringstream ss; std::ostringstream ss;
ss << difficulty["label"] << " (" << difficulty["description"] << ")"; ss << difficulty["label"] << " (" << difficulty["description"] << ")";
str << ss.str(); str << ss.str();

View file

@ -550,7 +550,7 @@ void variable_mode_controller::show_array(tree_view_node& node)
std::size_t n_start = var.find_last_of('[') + 1; std::size_t n_start = var.find_last_of('[') + 1;
std::size_t n_len = var.size() - n_start - 1; std::size_t n_len = var.size() - n_start - 1;
int n = std::stoi(var.substr(n_start, n_len)); int n = std::stoi(var.substr(n_start, n_len));
model().set_data(config_to_string(vars().child(var.substr(1, n_start - 3), n))); model().set_data(config_to_string(vars().mandatory_child(var.substr(1, n_start - 3), n)));
} }
} }
@ -585,7 +585,7 @@ void event_mode_controller::show_list(tree_view_node& node, bool is_wmi)
void event_mode_controller::show_event(tree_view_node& node, bool is_wmi) void event_mode_controller::show_event(tree_view_node& node, bool is_wmi)
{ {
int n = node.describe_path().back(); int n = node.describe_path().back();
model().set_data(config_to_string(events.child(is_wmi ? "menu_item" : "event", n))); model().set_data(config_to_string(events.mandatory_child(is_wmi ? "menu_item" : "event", n)));
} }
static stuff_list_adder add_unit_entry(stuff_list_adder& progress, const unit& u, const display_context& dc) static stuff_list_adder add_unit_entry(stuff_list_adder& progress, const unit& u, const display_context& dc)
@ -705,7 +705,7 @@ void unit_mode_controller::show_array(tree_view_node& node)
std::size_t n_start = var.find_last_of('[') + 1; std::size_t n_start = var.find_last_of('[') + 1;
std::size_t n_len = var.size() - n_start - 1; std::size_t n_len = var.size() - n_start - 1;
int n = std::stoi(var.substr(n_start, n_len)); int n = std::stoi(var.substr(n_start, n_len));
model().set_data(config_to_string(u->variables().child(var.substr(1, n_start - 3), n))); model().set_data(config_to_string(u->variables().mandatory_child(var.substr(1, n_start - 3), n)));
} }
} }
@ -907,7 +907,7 @@ void team_mode_controller::show_array(tree_view_node& node, int side)
std::size_t n_start = var.find_last_of('[') + 1; std::size_t n_start = var.find_last_of('[') + 1;
std::size_t n_len = var.size() - n_start - 1; std::size_t n_len = var.size() - n_start - 1;
int n = std::stoi(var.substr(n_start, n_len)); int n = std::stoi(var.substr(n_start, n_len));
model().set_data(config_to_string(t.variables().child(var.substr(1, n_start - 3), n))); model().set_data(config_to_string(t.variables().mandatory_child(var.substr(1, n_start - 3), n)));
} }
} }

View file

@ -46,7 +46,7 @@ REGISTER_DIALOG(help_browser)
help_browser::help_browser() help_browser::help_browser()
: modal_dialog(window_id()) : modal_dialog(window_id())
, initial_topic_("introduction") , initial_topic_("introduction")
, help_cfg_(game_config_manager::get()->game_config().child("help")) , help_cfg_(game_config_manager::get()->game_config().mandatory_child("help"))
{ {
} }

View file

@ -752,13 +752,13 @@ void mp_lobby::network_handler()
void mp_lobby::process_network_data(const config& data) void mp_lobby::process_network_data(const config& data)
{ {
if(const config& error = data.child("error")) { if(auto error = data.optional_child("error")) {
throw wesnothd_error(error["message"]); throw wesnothd_error(error["message"]);
} else if(data.child("gamelist")) { } else if(data.has_child("gamelist")) {
process_gamelist(data); process_gamelist(data);
} else if(const config& gamelist_diff = data.child("gamelist_diff")) { } else if(auto gamelist_diff = data.optional_child("gamelist_diff")) {
process_gamelist_diff(gamelist_diff); process_gamelist_diff(*gamelist_diff);
} else if(const config& info = data.child("message")) { } else if(auto info = data.optional_child("message")) {
if(info["type"] == "server_info") { if(info["type"] == "server_info") {
server_information_ = info["message"].str(); server_information_ = info["message"].str();
return; return;

View file

@ -103,20 +103,20 @@ bool mp_join_game::fetch_game_config()
network_connection_.wait_and_receive_data(revc); network_connection_.wait_and_receive_data(revc);
}); });
if(const config& err = revc.child("error")) { if(auto err = revc.optional_child("error")) {
throw wesnothd_error(err["message"]); throw wesnothd_error(err["message"]);
} else if(revc.child("leave_game")) { } else if(revc.has_child("leave_game")) {
return false; return false;
} else if(config& next_scenario = revc.child("next_scenario")) { } else if(auto next_scenario = revc.optional_child("next_scenario")) {
level_.swap(next_scenario); level_.swap(*next_scenario);
} else if(revc.has_attribute("version")) { } else if(revc.has_attribute("version")) {
level_.swap(revc); level_.swap(revc);
has_scenario_and_controllers = true; has_scenario_and_controllers = true;
} else if(config& controllers = revc.child("controllers")) { } else if(auto controllers = revc.optional_child("controllers")) {
int index = 0; int index = 0;
for(const config& controller : controllers.child_range("controller")) { for(const config& controller : controllers->child_range("controller")) {
if(config& side = get_scenario().child("side", index)) { if(auto side = get_scenario().optional_child("side", index)) {
side["is_local"] = controller["is_local"]; side["is_local"] = controller["is_local"];
} }
++index; ++index;
@ -140,7 +140,7 @@ bool mp_join_game::fetch_game_config()
// since add-ons are now only enabled when used, the scenario ID may still not be known // since add-ons are now only enabled when used, the scenario ID may still not be known
// so check in the MP info sent from the server for the scenario ID if that's the case // so check in the MP info sent from the server for the scenario ID if that's the case
if(scenario_id == "") { if(scenario_id == "") {
for(const auto& addon : level_.child("multiplayer").child_range("addon")) { for(const auto& addon : level_.mandatory_child("multiplayer").child_range("addon")) {
for(const auto& content : addon.child_range("content")) { for(const auto& content : addon.child_range("content")) {
if(content["type"] == "scenario") { if(content["type"] == "scenario") {
scenario_id = content["id"].str(); scenario_id = content["id"].str();
@ -245,7 +245,7 @@ void mp_join_game::pre_show(window& window)
// //
label& title = find_widget<label>(&window, "title", false); label& title = find_widget<label>(&window, "title", false);
// FIXME: very hacky way to get the game name... // FIXME: very hacky way to get the game name...
title.set_label((formatter() << level_.child("multiplayer")["scenario"] << " " << font::unicode_em_dash << " " << get_scenario()["name"].t_str()).str()); title.set_label((formatter() << level_.mandatory_child("multiplayer")["scenario"] << " " << font::unicode_em_dash << " " << get_scenario()["name"].t_str()).str());
// //
// Set up sides list // Set up sides list
@ -283,18 +283,18 @@ void mp_join_game::pre_show(window& window)
bool mp_join_game::show_flg_select(int side_num, bool first_time) bool mp_join_game::show_flg_select(int side_num, bool first_time)
{ {
if(const config& side_choice = get_scenario().child("side", side_num - 1)) { if(auto side_choice = get_scenario().optional_child("side", side_num - 1)) {
if(!side_choice["allow_changes"].to_bool(true)) { if(!side_choice["allow_changes"].to_bool(true)) {
return true; return true;
} }
const config& era = level_.child("era"); auto era = level_.optional_child("era");
if(!era) { if(!era) {
ERR_MP << "no era information"; ERR_MP << "no era information";
return false; return false;
} }
config::const_child_itors possible_sides = era.child_range("multiplayer_side"); config::const_child_itors possible_sides = era->child_range("multiplayer_side");
if(possible_sides.empty()) { if(possible_sides.empty()) {
WRN_MP << "no [multiplayer_side] found in era '" << era["id"] << "'."; WRN_MP << "no [multiplayer_side] found in era '" << era["id"] << "'.";
return false; return false;
@ -312,10 +312,10 @@ bool mp_join_game::show_flg_select(int side_num, bool first_time)
const bool is_mp = state_.classification().is_normal_mp_game(); const bool is_mp = state_.classification().is_normal_mp_game();
const bool lock_settings = get_scenario()["force_lock_settings"].to_bool(!is_mp); const bool lock_settings = get_scenario()["force_lock_settings"].to_bool(!is_mp);
const bool use_map_settings = level_.child("multiplayer")["mp_use_map_settings"].to_bool(); const bool use_map_settings = level_.mandatory_child("multiplayer")["mp_use_map_settings"].to_bool();
const saved_game_mode::type saved_game = saved_game_mode::get_enum(level_.child("multiplayer")["savegame"].str()).value_or(saved_game_mode::type::no); const saved_game_mode::type saved_game = saved_game_mode::get_enum(level_.mandatory_child("multiplayer")["savegame"].str()).value_or(saved_game_mode::type::no);
ng::flg_manager flg(era_factions, side_choice, lock_settings, use_map_settings, saved_game == saved_game_mode::type::midgame); ng::flg_manager flg(era_factions, *side_choice, lock_settings, use_map_settings, saved_game == saved_game_mode::type::midgame);
{ {
gui2::dialogs::faction_select flg_dialog(flg, color, side_num); gui2::dialogs::faction_select flg_dialog(flg, color, side_num);
@ -515,34 +515,34 @@ void mp_join_game::network_handler()
close_faction_select_dialog_if_open(); close_faction_select_dialog_if_open();
set_retval(retval::CANCEL); set_retval(retval::CANCEL);
} else if(data.child("start_game")) { } else if(data.has_child("start_game")) {
close_faction_select_dialog_if_open(); close_faction_select_dialog_if_open();
level_["started"] = true; level_["started"] = true;
set_retval(retval::OK); set_retval(retval::OK);
} else if(data.child("leave_game")) { } else if(data.has_child("leave_game")) {
close_faction_select_dialog_if_open(); close_faction_select_dialog_if_open();
set_retval(retval::CANCEL); set_retval(retval::CANCEL);
} }
if(data.child("stop_updates")) { if(data.has_child("stop_updates")) {
stop_updates_ = true; stop_updates_ = true;
} else if(const config& c = data.child("scenario_diff")) { } else if(auto c = data.optional_child("scenario_diff")) {
// TODO: We should catch config::error and then leave the game. // TODO: We should catch config::error and then leave the game.
level_.apply_diff(c); level_.apply_diff(*c);
generate_side_list(); generate_side_list();
} else if(const config& change = data.child("change_controller")) { } else if(auto change = data.optional_child("change_controller")) {
if(config& side_to_change = get_scenario().find_child("side", "side", change["side"])) { if(auto side_to_change = get_scenario().find_child("side", "side", change["side"])) {
side_to_change.merge_with(change); side_to_change->merge_with(*change);
} }
if(flg_dialog_ && flg_dialog_->get_side_num() == change["side"].to_int()) { if(flg_dialog_ && flg_dialog_->get_side_num() == change["side"].to_int()) {
close_faction_select_dialog_if_open(); close_faction_select_dialog_if_open();
} }
} else if(data.has_child("scenario") || data.has_child("snapshot") || data.child("next_scenario")) { } else if(data.has_child("scenario") || data.has_child("snapshot") || data.has_child("next_scenario")) {
level_ = first_scenario_ ? data : data.child("next_scenario"); level_ = first_scenario_ ? data : data.mandatory_child("next_scenario");
generate_side_list(); generate_side_list();
} }
@ -559,10 +559,10 @@ void mp_join_game::network_handler()
config& mp_join_game::get_scenario() config& mp_join_game::get_scenario()
{ {
if(config& scenario = level_.child("scenario")) { if(auto scenario = level_.optional_child("scenario")) {
return scenario; return *scenario;
} else if(config& snapshot = level_.child("snapshot")) { } else if(auto snapshot = level_.optional_child("snapshot")) {
return snapshot; return *snapshot;
} }
return level_; return level_;
@ -576,9 +576,9 @@ void mp_join_game::post_show(window& window)
} }
if(window.get_retval() == retval::OK) { if(window.get_retval() == retval::OK) {
if(const config& stats = level_.child("statistics")) { if(auto stats = level_.optional_child("statistics")) {
statistics::fresh_stats(); statistics::fresh_stats();
statistics::read_stats(stats); statistics::read_stats(*stats);
} }
mp::level_to_gamestate(level_, state_); mp::level_to_gamestate(level_, state_);

View file

@ -427,10 +427,10 @@ addon_list_definition::resolution::resolution(const config& cfg)
static config dummy("draw"); static config dummy("draw");
state.emplace_back(dummy); state.emplace_back(dummy);
const config& child = cfg.child("grid"); auto child = cfg.optional_child("grid");
VALIDATE(child, _("No grid defined.")); VALIDATE(child, _("No grid defined."));
grid = std::make_shared<builder_grid>(child); grid = std::make_shared<builder_grid>(*child);
} }
namespace implementation namespace implementation

View file

@ -159,10 +159,10 @@ button_definition::resolution::resolution(const config& cfg)
: resolution_definition(cfg) : resolution_definition(cfg)
{ {
// Note the order should be the same as the enum state_t in button.hpp. // Note the order should be the same as the enum state_t in button.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
state.emplace_back(cfg.child("state_disabled")); state.emplace_back(cfg.optional_child("state_disabled"));
state.emplace_back(cfg.child("state_pressed")); state.emplace_back(cfg.optional_child("state_pressed"));
state.emplace_back(cfg.child("state_focused")); state.emplace_back(cfg.optional_child("state_focused"));
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{

View file

@ -653,13 +653,13 @@ chatbox_definition::chatbox_definition(const config& cfg)
chatbox_definition::resolution::resolution(const config& cfg) chatbox_definition::resolution::resolution(const config& cfg)
: resolution_definition(cfg), grid() : resolution_definition(cfg), grid()
{ {
state.emplace_back(cfg.child("background")); state.emplace_back(cfg.optional_child("background"));
state.emplace_back(cfg.child("foreground")); state.emplace_back(cfg.optional_child("foreground"));
const config& child = cfg.child("grid"); auto child = cfg.optional_child("grid");
VALIDATE(child, _("No grid defined.")); VALIDATE(child, _("No grid defined."));
grid = std::make_shared<builder_grid>(child); grid = std::make_shared<builder_grid>(*child);
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{

View file

@ -23,6 +23,9 @@
#include "gui/core/register_widget.hpp" #include "gui/core/register_widget.hpp"
#include "gui/widgets/settings.hpp" #include "gui/widgets/settings.hpp"
#include "gettext.hpp"
#include "wml_exception.hpp"
#include <functional> #include <functional>
namespace gui2 namespace gui2
@ -113,7 +116,7 @@ builder_drawing::builder_drawing(const config& cfg)
: builder_styled_widget(cfg) : builder_styled_widget(cfg)
, width(cfg["width"]) , width(cfg["width"])
, height(cfg["height"]) , height(cfg["height"])
, draw(cfg.child("draw")) , draw(VALIDATE_WML_CHILD(cfg, "draw", _("Missing [draw] in drawing")))
{ {
assert(!draw.empty()); assert(!draw.empty());
} }

View file

@ -129,10 +129,10 @@ horizontal_scrollbar_definition::resolution::resolution(const config& cfg)
"minimum_positioner_length")); "minimum_positioner_length"));
// Note the order should be the same as the enum state_t is scrollbar.hpp. // Note the order should be the same as the enum state_t is scrollbar.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
state.emplace_back(cfg.child("state_disabled")); state.emplace_back(cfg.optional_child("state_disabled"));
state.emplace_back(cfg.child("state_pressed")); state.emplace_back(cfg.optional_child("state_pressed"));
state.emplace_back(cfg.child("state_focused")); state.emplace_back(cfg.optional_child("state_focused"));
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{

View file

@ -110,7 +110,7 @@ image_definition::resolution::resolution(const config& cfg)
: resolution_definition(cfg) : resolution_definition(cfg)
{ {
// Note the order should be the same as the enum state_t in image.hpp. // Note the order should be the same as the enum state_t in image.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{

View file

@ -237,8 +237,8 @@ label_definition::resolution::resolution(const config& cfg)
, link_color(cfg["link_color"].empty() ? color_t::from_hex_string("ffff00") : color_t::from_rgba_string(cfg["link_color"].str())) , link_color(cfg["link_color"].empty() ? color_t::from_hex_string("ffff00") : color_t::from_rgba_string(cfg["link_color"].str()))
{ {
// Note the order should be the same as the enum state_t is label.hpp. // Note the order should be the same as the enum state_t is label.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
state.emplace_back(cfg.child("state_disabled")); state.emplace_back(cfg.optional_child("state_disabled"));
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{

View file

@ -706,11 +706,10 @@ listbox_definition::resolution::resolution(const config& cfg)
, grid(nullptr) , grid(nullptr)
{ {
// Note the order should be the same as the enum state_t in listbox.hpp. // Note the order should be the same as the enum state_t in listbox.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
state.emplace_back(cfg.child("state_disabled")); state.emplace_back(cfg.optional_child("state_disabled"));
const config& child = cfg.child("grid"); auto child = VALIDATE_WML_CHILD(cfg, "grid", _("No grid defined."));
VALIDATE(child, _("No grid defined."));
grid = std::make_shared<builder_grid>(child); grid = std::make_shared<builder_grid>(child);
} }
@ -759,25 +758,25 @@ builder_listbox::builder_listbox(const config& cfg)
, has_minimum_(cfg["has_minimum"].to_bool(true)) , has_minimum_(cfg["has_minimum"].to_bool(true))
, has_maximum_(cfg["has_maximum"].to_bool(true)) , has_maximum_(cfg["has_maximum"].to_bool(true))
{ {
if(const config& h = cfg.child("header")) { if(auto h = cfg.optional_child("header")) {
header = std::make_shared<builder_grid>(h); header = std::make_shared<builder_grid>(*h);
} }
if(const config& f = cfg.child("footer")) { if(auto f = cfg.optional_child("footer")) {
footer = std::make_shared<builder_grid>(f); footer = std::make_shared<builder_grid>(*f);
} }
const config& l = cfg.child("list_definition"); auto l = cfg.optional_child("list_definition");
VALIDATE(l, _("No list defined.")); VALIDATE(l, _("No list defined."));
list_builder = std::make_shared<builder_grid>(l); list_builder = std::make_shared<builder_grid>(*l);
assert(list_builder); assert(list_builder);
VALIDATE(list_builder->rows == 1, _("A 'list_definition' should contain one row.")); VALIDATE(list_builder->rows == 1, _("A 'list_definition' should contain one row."));
if(cfg.has_child("list_data")) { if(cfg.has_child("list_data")) {
list_data = parse_list_data(cfg.child("list_data"), list_builder->cols); list_data = parse_list_data(cfg.mandatory_child("list_data"), list_builder->cols);
} }
} }
@ -810,17 +809,17 @@ builder_horizontal_listbox::builder_horizontal_listbox(const config& cfg)
, has_minimum_(cfg["has_minimum"].to_bool(true)) , has_minimum_(cfg["has_minimum"].to_bool(true))
, has_maximum_(cfg["has_maximum"].to_bool(true)) , has_maximum_(cfg["has_maximum"].to_bool(true))
{ {
const config& l = cfg.child("list_definition"); auto l = cfg.optional_child("list_definition");
VALIDATE(l, _("No list defined.")); VALIDATE(l, _("No list defined."));
list_builder = std::make_shared<builder_grid>(l); list_builder = std::make_shared<builder_grid>(*l);
assert(list_builder); assert(list_builder);
VALIDATE(list_builder->rows == 1, _("A 'list_definition' should contain one row.")); VALIDATE(list_builder->rows == 1, _("A 'list_definition' should contain one row."));
if(cfg.has_child("list_data")) { if(cfg.has_child("list_data")) {
list_data = parse_list_data(cfg.child("list_data"), list_builder->cols); list_data = parse_list_data(cfg.mandatory_child("list_data"), list_builder->cols);
} }
} }
@ -853,17 +852,17 @@ builder_grid_listbox::builder_grid_listbox(const config& cfg)
, has_minimum_(cfg["has_minimum"].to_bool(true)) , has_minimum_(cfg["has_minimum"].to_bool(true))
, has_maximum_(cfg["has_maximum"].to_bool(true)) , has_maximum_(cfg["has_maximum"].to_bool(true))
{ {
const config& l = cfg.child("list_definition"); auto l = cfg.optional_child("list_definition");
VALIDATE(l, _("No list defined.")); VALIDATE(l, _("No list defined."));
list_builder = std::make_shared<builder_grid>(l); list_builder = std::make_shared<builder_grid>(*l);
assert(list_builder); assert(list_builder);
VALIDATE(list_builder->rows == 1, _("A 'list_definition' should contain one row.")); VALIDATE(list_builder->rows == 1, _("A 'list_definition' should contain one row."));
if(cfg.has_child("list_data")) { if(cfg.has_child("list_data")) {
list_data = parse_list_data(cfg.child("list_data"), list_builder->cols); list_data = parse_list_data(cfg.mandatory_child("list_data"), list_builder->cols);
} }
} }

View file

@ -17,6 +17,7 @@
#include "gui/widgets/matrix.hpp" #include "gui/widgets/matrix.hpp"
#include "gettext.hpp"
#include "gui/auxiliary/find_widget.hpp" #include "gui/auxiliary/find_widget.hpp"
#include "gui/auxiliary/iterator/walker.hpp" #include "gui/auxiliary/iterator/walker.hpp"
#include "gui/core/log.hpp" #include "gui/core/log.hpp"
@ -184,11 +185,11 @@ matrix_definition::matrix_definition(const config& cfg)
matrix_definition::resolution::resolution(const config& cfg) matrix_definition::resolution::resolution(const config& cfg)
: resolution_definition(cfg) : resolution_definition(cfg)
, content(new builder_grid(cfg.child("content", "[matrix_definition]"))) , content(new builder_grid(VALIDATE_WML_CHILD(cfg, "content", _("Missing [content] in [matrix_definition]"))))
{ {
// Note the order should be the same as the enum state_t in matrix.hpp. // Note the order should be the same as the enum state_t in matrix.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
state.emplace_back(cfg.child("state_disabled")); state.emplace_back(cfg.optional_child("state_disabled"));
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{
@ -206,22 +207,22 @@ builder_matrix::builder_matrix(const config& cfg)
, builder_bottom(nullptr) , builder_bottom(nullptr)
, builder_left(nullptr) , builder_left(nullptr)
, builder_right(nullptr) , builder_right(nullptr)
, builder_main(create_widget_builder(cfg.child("main", "[matrix]"))) , builder_main(create_widget_builder(VALIDATE_WML_CHILD(cfg, "main", _("Missing [main] in [matrix]"))))
{ {
if(const config& top = cfg.child("top")) { if(auto top = cfg.optional_child("top")) {
builder_top = std::make_shared<builder_grid>(top); builder_top = std::make_shared<builder_grid>(*top);
} }
if(const config& bottom = cfg.child("bottom")) { if(auto bottom = cfg.optional_child("bottom")) {
builder_bottom = std::make_shared<builder_grid>(bottom); builder_bottom = std::make_shared<builder_grid>(*bottom);
} }
if(const config& left = cfg.child("left")) { if(auto left = cfg.optional_child("left")) {
builder_left = std::make_shared<builder_grid>(left); builder_left = std::make_shared<builder_grid>(*left);
} }
if(const config& right = cfg.child("right")) { if(auto right = cfg.optional_child("right")) {
builder_right = std::make_shared<builder_grid>(right); builder_right = std::make_shared<builder_grid>(*right);
} }
} }

View file

@ -229,10 +229,10 @@ menu_button_definition::resolution::resolution(const config& cfg)
: resolution_definition(cfg) : resolution_definition(cfg)
{ {
// Note the order should be the same as the enum state_t in menu_button.hpp. // Note the order should be the same as the enum state_t in menu_button.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
state.emplace_back(cfg.child("state_disabled")); state.emplace_back(cfg.optional_child("state_disabled"));
state.emplace_back(cfg.child("state_pressed")); state.emplace_back(cfg.optional_child("state_pressed"));
state.emplace_back(cfg.child("state_focused")); state.emplace_back(cfg.optional_child("state_focused"));
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{

View file

@ -112,7 +112,7 @@ minimap_definition::resolution::resolution(const config& cfg)
: resolution_definition(cfg) : resolution_definition(cfg)
{ {
// Note the order should be the same as the enum state_t in minimap.hpp. // Note the order should be the same as the enum state_t in minimap.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{

View file

@ -172,10 +172,10 @@ multi_page_definition::multi_page_definition(const config& cfg)
multi_page_definition::resolution::resolution(const config& cfg) multi_page_definition::resolution::resolution(const config& cfg)
: resolution_definition(cfg), grid(nullptr) : resolution_definition(cfg), grid(nullptr)
{ {
const config& child = cfg.child("grid"); auto child = cfg.optional_child("grid");
VALIDATE(child, _("No grid defined.")); VALIDATE(child, _("No grid defined."));
grid = std::make_shared<builder_grid>(child); grid = std::make_shared<builder_grid>(*child);
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{
@ -195,13 +195,13 @@ builder_multi_page::builder_multi_page(const config& cfg)
VALIDATE(!builders.empty(), _("No page defined.")); VALIDATE(!builders.empty(), _("No page defined."));
/** @todo This part is untested. */ /** @todo This part is untested. */
const config& d = cfg.child("page_data"); auto d = cfg.optional_child("page_data");
if(!d) { if(!d) {
return; return;
} }
auto builder = builders.begin()->second; auto builder = builders.begin()->second;
for(const auto & row : d.child_range("row")) for(const auto & row : d->child_range("row"))
{ {
unsigned col = 0; unsigned col = 0;

View file

@ -252,10 +252,10 @@ multimenu_button_definition::resolution::resolution(const config& cfg)
: resolution_definition(cfg) : resolution_definition(cfg)
{ {
// Note the order should be the same as the enum state_t in multimenu_button.hpp. // Note the order should be the same as the enum state_t in multimenu_button.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
state.emplace_back(cfg.child("state_disabled")); state.emplace_back(cfg.optional_child("state_disabled"));
state.emplace_back(cfg.child("state_pressed")); state.emplace_back(cfg.optional_child("state_pressed"));
state.emplace_back(cfg.child("state_focused")); state.emplace_back(cfg.optional_child("state_focused"));
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{

View file

@ -358,7 +358,7 @@ builder_pane::builder_pane(const config& cfg)
: builder_widget(cfg) : builder_widget(cfg)
, grow_dir(*grow_direction::get_enum(cfg["grow_direction"].str())) , grow_dir(*grow_direction::get_enum(cfg["grow_direction"].str()))
, parallel_items(cfg["parallel_items"]) , parallel_items(cfg["parallel_items"])
, item_definition(new builder_grid(cfg.child("item_definition", "[pane]"))) , item_definition(new builder_grid(VALIDATE_WML_CHILD(cfg, "item_definition", _("Missing [item_definition] in [pane]"))))
{ {
VALIDATE(parallel_items > 0, _("Need at least 1 parallel item.")); VALIDATE(parallel_items > 0, _("Need at least 1 parallel item."));
} }

View file

@ -108,8 +108,8 @@ panel_definition::resolution::resolution(const config& cfg)
, right_border(cfg["right_border"]) , right_border(cfg["right_border"])
{ {
// The panel needs to know the order. // The panel needs to know the order.
state.emplace_back(cfg.child("background")); state.emplace_back(cfg.optional_child("background"));
state.emplace_back(cfg.child("foreground")); state.emplace_back(cfg.optional_child("foreground"));
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{
@ -120,11 +120,11 @@ namespace implementation
builder_panel::builder_panel(const config& cfg) builder_panel::builder_panel(const config& cfg)
: builder_styled_widget(cfg), grid(nullptr) : builder_styled_widget(cfg), grid(nullptr)
{ {
const config& c = cfg.child("grid"); auto c = cfg.optional_child("grid");
VALIDATE(c, _("No grid defined.")); VALIDATE(c, _("No grid defined."));
grid = std::make_shared<builder_grid>(c); grid = std::make_shared<builder_grid>(*c);
} }
std::unique_ptr<widget> builder_panel::build() const std::unique_ptr<widget> builder_panel::build() const

View file

@ -91,7 +91,7 @@ progress_bar_definition::resolution::resolution(const config& cfg)
: resolution_definition(cfg) : resolution_definition(cfg)
{ {
// Note the order should be the same as the enum state_t in progress_bar.hpp. // Note the order should be the same as the enum state_t in progress_bar.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{

View file

@ -179,10 +179,10 @@ repeating_button_definition::resolution::resolution(const config& cfg)
{ {
// Note the order should be the same as the enum state_t in // Note the order should be the same as the enum state_t in
// repeating_button.hpp. // repeating_button.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
state.emplace_back(cfg.child("state_disabled")); state.emplace_back(cfg.optional_child("state_disabled"));
state.emplace_back(cfg.child("state_pressed")); state.emplace_back(cfg.optional_child("state_pressed"));
state.emplace_back(cfg.child("state_focused")); state.emplace_back(cfg.optional_child("state_focused"));
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{

View file

@ -176,13 +176,13 @@ scroll_label_definition::resolution::resolution(const config& cfg)
: resolution_definition(cfg), grid(nullptr) : resolution_definition(cfg), grid(nullptr)
{ {
// Note the order should be the same as the enum state_t is scroll_label.hpp. // Note the order should be the same as the enum state_t is scroll_label.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
state.emplace_back(cfg.child("state_disabled")); state.emplace_back(cfg.optional_child("state_disabled"));
const config& child = cfg.child("grid"); auto child = cfg.optional_child("grid");
VALIDATE(child, _("No grid defined.")); VALIDATE(child, _("No grid defined."));
grid = std::make_shared<builder_grid>(child); grid = std::make_shared<builder_grid>(*child);
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{

View file

@ -67,13 +67,13 @@ scrollbar_panel_definition::resolution::resolution(const config& cfg)
: resolution_definition(cfg), grid() : resolution_definition(cfg), grid()
{ {
// The panel needs to know the order. // The panel needs to know the order.
state.emplace_back(cfg.child("background")); state.emplace_back(cfg.optional_child("background"));
state.emplace_back(cfg.child("foreground")); state.emplace_back(cfg.optional_child("foreground"));
const config& child = cfg.child("grid"); auto child = cfg.optional_child("grid");
VALIDATE(child, _("No grid defined.")); VALIDATE(child, _("No grid defined."));
grid = std::make_shared<builder_grid>(child); grid = std::make_shared<builder_grid>(*child);
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{
@ -89,10 +89,10 @@ builder_scrollbar_panel::builder_scrollbar_panel(const config& cfg)
get_scrollbar_mode(cfg["horizontal_scrollbar_mode"])) get_scrollbar_mode(cfg["horizontal_scrollbar_mode"]))
, grid_(nullptr) , grid_(nullptr)
{ {
const config& grid_definition = cfg.child("definition"); auto grid_definition = cfg.optional_child("definition");
VALIDATE(grid_definition, _("No list defined.")); VALIDATE(grid_definition, _("No list defined."));
grid_ = std::make_shared<builder_grid>(grid_definition); grid_ = std::make_shared<builder_grid>(*grid_definition);
assert(grid_); assert(grid_);
} }

View file

@ -107,10 +107,10 @@ size_lock_definition::resolution::resolution(const config& cfg)
static config dummy("draw"); static config dummy("draw");
state.emplace_back(dummy); state.emplace_back(dummy);
const config& child = cfg.child("grid"); auto child = cfg.optional_child("grid");
VALIDATE(child, _("No grid defined.")); VALIDATE(child, _("No grid defined."));
grid = std::make_shared<builder_grid>(child); grid = std::make_shared<builder_grid>(*child);
} }
namespace implementation namespace implementation
@ -122,7 +122,7 @@ builder_size_lock::builder_size_lock(const config& cfg)
, content_(nullptr) , content_(nullptr)
{ {
VALIDATE(cfg.has_child("widget"), _("No widget defined.")); VALIDATE(cfg.has_child("widget"), _("No widget defined."));
content_ = create_widget_builder(cfg.child("widget")); content_ = create_widget_builder(cfg.mandatory_child("widget"));
} }
std::unique_ptr<widget> builder_size_lock::build() const std::unique_ptr<widget> builder_size_lock::build() const

View file

@ -310,10 +310,10 @@ slider_definition::resolution::resolution(const config& cfg)
VALIDATE(positioner_length, missing_mandatory_wml_key("resolution", "minimum_positioner_length")); VALIDATE(positioner_length, missing_mandatory_wml_key("resolution", "minimum_positioner_length"));
// Note the order should be the same as the enum state_t is slider.hpp. // Note the order should be the same as the enum state_t is slider.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
state.emplace_back(cfg.child("state_disabled")); state.emplace_back(cfg.optional_child("state_disabled"));
state.emplace_back(cfg.child("state_pressed")); state.emplace_back(cfg.optional_child("state_pressed"));
state.emplace_back(cfg.child("state_focused")); state.emplace_back(cfg.optional_child("state_focused"));
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{
@ -331,12 +331,12 @@ builder_slider::builder_slider(const config& cfg)
, maximum_value_label_(cfg["maximum_value_label"].t_str()) , maximum_value_label_(cfg["maximum_value_label"].t_str())
, value_labels_() , value_labels_()
{ {
const config& labels = cfg.child("value_labels"); auto labels = cfg.optional_child("value_labels");
if(!labels) { if(!labels) {
return; return;
} }
for(const auto& label : labels.child_range("value")) { for(const auto& label : labels->child_range("value")) {
value_labels_.push_back(label["label"]); value_labels_.push_back(label["label"]);
} }
} }

View file

@ -221,10 +221,10 @@ stacked_widget_definition::resolution::resolution(const config& cfg)
static config dummy("draw"); static config dummy("draw");
state.emplace_back(dummy); state.emplace_back(dummy);
const config& child = cfg.child("grid"); auto child = cfg.optional_child("grid");
VALIDATE(child, _("No grid defined.")); VALIDATE(child, _("No grid defined."));
grid = std::make_shared<builder_grid>(child); grid = std::make_shared<builder_grid>(*child);
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{
@ -235,7 +235,7 @@ namespace implementation
builder_stacked_widget::builder_stacked_widget(const config& real_cfg) builder_stacked_widget::builder_stacked_widget(const config& real_cfg)
: builder_styled_widget(real_cfg), stack() : builder_styled_widget(real_cfg), stack()
{ {
const config& cfg = real_cfg.has_child("stack") ? real_cfg.child("stack") : real_cfg; const config& cfg = real_cfg.has_child("stack") ? real_cfg.mandatory_child("stack") : real_cfg;
if(&cfg != &real_cfg) { if(&cfg != &real_cfg) {
lg::log_to_chat() << "Stacked widgets no longer require a [stack] tag. Instead, place [layer] tags directly in the widget definition.\n"; lg::log_to_chat() << "Stacked widgets no longer require a [stack] tag. Instead, place [layer] tags directly in the widget definition.\n";
ERR_WML << "Stacked widgets no longer require a [stack] tag. Instead, place [layer] tags directly in the widget definition."; ERR_WML << "Stacked widgets no longer require a [stack] tag. Instead, place [layer] tags directly in the widget definition.";

View file

@ -273,8 +273,6 @@ void text_box::handle_mouse_selection(point mouse, const bool start_selection)
void text_box::update_offsets() void text_box::update_offsets()
{ {
assert(config());
const auto conf = cast_config_to<text_box_definition>(); const auto conf = cast_config_to<text_box_definition>();
assert(conf); assert(conf);
@ -408,10 +406,10 @@ text_box_definition::resolution::resolution(const config& cfg)
, text_y_offset(cfg["text_y_offset"]) , text_y_offset(cfg["text_y_offset"])
{ {
// Note the order should be the same as the enum state_t in text_box.hpp. // Note the order should be the same as the enum state_t in text_box.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
state.emplace_back(cfg.child("state_disabled")); state.emplace_back(cfg.optional_child("state_disabled"));
state.emplace_back(cfg.child("state_focused")); state.emplace_back(cfg.optional_child("state_focused"));
state.emplace_back(cfg.child("state_hovered")); state.emplace_back(cfg.optional_child("state_hovered"));
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{

View file

@ -209,9 +209,9 @@ toggle_button_definition::resolution::resolution(const config& cfg)
// toggle_button.hpp. // toggle_button.hpp.
for(const auto& c : cfg.child_range("state")) for(const auto& c : cfg.child_range("state"))
{ {
state.emplace_back(c.child("enabled")); state.emplace_back(c.optional_child("enabled"));
state.emplace_back(c.child("disabled")); state.emplace_back(c.optional_child("disabled"));
state.emplace_back(c.child("focused")); state.emplace_back(c.optional_child("focused"));
} }
} }

View file

@ -299,9 +299,9 @@ toggle_panel_definition::resolution::resolution(const config& cfg)
// Note the order should be the same as the enum state_t in toggle_panel.hpp. // Note the order should be the same as the enum state_t in toggle_panel.hpp.
for(const auto& c : cfg.child_range("state")) for(const auto& c : cfg.child_range("state"))
{ {
state.emplace_back(c.child("enabled")); state.emplace_back(c.optional_child("enabled"));
state.emplace_back(c.child("disabled")); state.emplace_back(c.optional_child("disabled"));
state.emplace_back(c.child("focused")); state.emplace_back(c.optional_child("focused"));
} }
} }
@ -316,11 +316,11 @@ builder_toggle_panel::builder_toggle_panel(const config& cfg)
, retval_id_(cfg["return_value_id"]) , retval_id_(cfg["return_value_id"])
, retval_(cfg["return_value"]) , retval_(cfg["return_value"])
{ {
const config& c = cfg.child("grid"); auto c = cfg.optional_child("grid");
VALIDATE(c, _("No grid defined.")); VALIDATE(c, _("No grid defined."));
grid = std::make_shared<builder_grid>(c); grid = std::make_shared<builder_grid>(*c);
} }
std::unique_ptr<widget> builder_toggle_panel::build() const std::unique_ptr<widget> builder_toggle_panel::build() const

View file

@ -265,13 +265,13 @@ tree_view_definition::resolution::resolution(const config& cfg)
, grid(nullptr) , grid(nullptr)
{ {
// Note the order should be the same as the enum state_t is listbox.hpp. // Note the order should be the same as the enum state_t is listbox.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
state.emplace_back(cfg.child("state_disabled")); state.emplace_back(cfg.optional_child("state_disabled"));
const config& child = cfg.child("grid"); auto child = cfg.optional_child("grid");
VALIDATE(child, _("No grid defined.")); VALIDATE(child, _("No grid defined."));
grid = std::make_shared<builder_grid>(child); grid = std::make_shared<builder_grid>(*child);
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{
@ -326,11 +326,11 @@ tree_node::tree_node(const config& cfg)
// TODO: interpolate this value into the error message // TODO: interpolate this value into the error message
VALIDATE(id != tree_view::root_node_id, _("[node]id 'root' is reserved for the implementation.")); VALIDATE(id != tree_view::root_node_id, _("[node]id 'root' is reserved for the implementation."));
const config& node_definition = cfg.child("node_definition"); auto node_definition = cfg.optional_child("node_definition");
VALIDATE(node_definition, _("No node defined.")); VALIDATE(node_definition, _("No node defined."));
builder = std::make_shared<builder_grid>(node_definition); builder = std::make_shared<builder_grid>(*node_definition);
} }
} // namespace implementation } // namespace implementation

View file

@ -608,13 +608,13 @@ unit_preview_pane_definition::unit_preview_pane_definition(const config& cfg)
unit_preview_pane_definition::resolution::resolution(const config& cfg) unit_preview_pane_definition::resolution::resolution(const config& cfg)
: resolution_definition(cfg), grid() : resolution_definition(cfg), grid()
{ {
state.emplace_back(cfg.child("background")); state.emplace_back(cfg.optional_child("background"));
state.emplace_back(cfg.child("foreground")); state.emplace_back(cfg.optional_child("foreground"));
const config& child = cfg.child("grid"); auto child = cfg.optional_child("grid");
VALIDATE(child, _("No grid defined.")); VALIDATE(child, _("No grid defined."));
grid = std::make_shared<builder_grid>(child); grid = std::make_shared<builder_grid>(*child);
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{

View file

@ -119,10 +119,10 @@ vertical_scrollbar_definition::resolution::resolution(const config& cfg)
"minimum_positioner_length")); "minimum_positioner_length"));
// Note the order should be the same as the enum state_t in scrollbar.hpp. // Note the order should be the same as the enum state_t in scrollbar.hpp.
state.emplace_back(cfg.child("state_enabled")); state.emplace_back(cfg.optional_child("state_enabled"));
state.emplace_back(cfg.child("state_disabled")); state.emplace_back(cfg.optional_child("state_disabled"));
state.emplace_back(cfg.child("state_pressed")); state.emplace_back(cfg.optional_child("state_pressed"));
state.emplace_back(cfg.child("state_focused")); state.emplace_back(cfg.optional_child("state_focused"));
} }
// }---------- BUILDER -----------{ // }---------- BUILDER -----------{

View file

@ -20,7 +20,9 @@
#include "gui/auxiliary/iterator/walker.hpp" #include "gui/auxiliary/iterator/walker.hpp"
#include "gui/core/log.hpp" #include "gui/core/log.hpp"
#include "config.hpp" #include "config.hpp"
#include "gettext.hpp"
#include "utils/const_clone.hpp" #include "utils/const_clone.hpp"
#include "wml_exception.hpp"
#define LOG_SCOPE_HEADER "viewport [" + id() + "] " + __func__ #define LOG_SCOPE_HEADER "viewport [" + id() + "] " + __func__
#define LOG_HEADER LOG_SCOPE_HEADER + ':' #define LOG_HEADER LOG_SCOPE_HEADER + ':'
@ -167,7 +169,7 @@ namespace implementation
builder_viewport::builder_viewport(const config& cfg) builder_viewport::builder_viewport(const config& cfg)
: builder_widget(cfg) : builder_widget(cfg)
, widget_(create_widget_builder(cfg.child("widget", "[viewport]"))) , widget_(create_widget_builder(VALIDATE_WML_CHILD(cfg, "widget", _("Missing [widget] in [viewport]"))))
{ {
} }

View file

@ -1308,12 +1308,12 @@ window_definition::window_definition(const config& cfg)
window_definition::resolution::resolution(const config& cfg) window_definition::resolution::resolution(const config& cfg)
: panel_definition::resolution(cfg), grid(nullptr) : panel_definition::resolution(cfg), grid(nullptr)
{ {
const config& child = cfg.child("grid"); auto child = cfg.optional_child("grid");
// VALIDATE(child, _("No grid defined.")); // VALIDATE(child, _("No grid defined."));
/** @todo Evaluate whether the grid should become mandatory. */ /** @todo Evaluate whether the grid should become mandatory. */
if(child) { if(child) {
grid = std::make_shared<builder_grid>(child); grid = std::make_shared<builder_grid>(*child);
} }
} }

View file

@ -150,7 +150,7 @@ static bool is_cjk_char(const char32_t ch)
bool section_is_referenced(const std::string &section_id, const config &cfg) bool section_is_referenced(const std::string &section_id, const config &cfg)
{ {
if (const config &toplevel = cfg.child("toplevel")) if (auto toplevel = cfg.optional_child("toplevel"))
{ {
const std::vector<std::string> toplevel_refs const std::vector<std::string> toplevel_refs
= utils::quoted_split(toplevel["sections"]); = utils::quoted_split(toplevel["sections"]);
@ -174,7 +174,7 @@ bool section_is_referenced(const std::string &section_id, const config &cfg)
bool topic_is_referenced(const std::string &topic_id, const config &cfg) bool topic_is_referenced(const std::string &topic_id, const config &cfg)
{ {
if (const config &toplevel = cfg.child("toplevel")) if (auto toplevel = cfg.optional_child("toplevel"))
{ {
const std::vector<std::string> toplevel_refs const std::vector<std::string> toplevel_refs
= utils::quoted_split(toplevel["topics"]); = utils::quoted_split(toplevel["topics"]);
@ -218,10 +218,10 @@ void parse_config_internal(const config *help_cfg, const config *section_cfg,
std::vector<std::string>::const_iterator it; std::vector<std::string>::const_iterator it;
// Find all child sections. // Find all child sections.
for (it = sections.begin(); it != sections.end(); ++it) { for (it = sections.begin(); it != sections.end(); ++it) {
if (const config &child_cfg = help_cfg->find_child("section", "id", *it)) if (auto child_cfg = help_cfg->find_child("section", "id", *it))
{ {
section child_section; section child_section;
parse_config_internal(help_cfg, &child_cfg, child_section, level + 1); parse_config_internal(help_cfg, child_cfg.ptr(), child_section, level + 1);
sec.add_section(child_section); sec.add_section(child_section);
} }
else { else {
@ -263,7 +263,7 @@ void parse_config_internal(const config *help_cfg, const config *section_cfg,
// Find all topics in this section. // Find all topics in this section.
for (it = topics_id.begin(); it != topics_id.end(); ++it) { for (it = topics_id.begin(); it != topics_id.end(); ++it) {
if (const config &topic_cfg = help_cfg->find_child("topic", "id", *it)) if (auto topic_cfg = help_cfg->find_child("topic", "id", *it))
{ {
std::string text = topic_cfg["text"]; std::string text = topic_cfg["text"];
text += generate_topic_text(topic_cfg["generator"], help_cfg, sec, generated_topics); text += generate_topic_text(topic_cfg["generator"], help_cfg, sec, generated_topics);
@ -304,8 +304,8 @@ section parse_config(const config *cfg)
{ {
section sec; section sec;
if (cfg != nullptr) { if (cfg != nullptr) {
const config& toplevel_cfg = cfg->child("toplevel"); auto toplevel_cfg = cfg->optional_child("toplevel");
parse_config_internal(cfg, toplevel_cfg ? &toplevel_cfg : nullptr, sec); parse_config_internal(cfg, toplevel_cfg.ptr(), sec);
} }
return sec; return sec;
} }
@ -483,7 +483,7 @@ std::vector<topic> generate_weapon_special_topics(const bool sort_generated)
for(config adv : type.modification_advancements()) { for(config adv : type.modification_advancements()) {
for(config effect : adv.child_range("effect")) { for(config effect : adv.child_range("effect")) {
if(effect["apply_to"] == "new_attack" && effect.has_child("specials")) { if(effect["apply_to"] == "new_attack" && effect.has_child("specials")) {
for(config::any_child spec : effect.child("specials").all_children_range()) { for(config::any_child spec : effect.mandatory_child("specials").all_children_range()) {
if(!spec.cfg["name"].empty()) { if(!spec.cfg["name"].empty()) {
special_description.emplace(spec.cfg["name"].t_str(), spec.cfg["description"].t_str()); special_description.emplace(spec.cfg["name"].t_str(), spec.cfg["description"].t_str());
if(!type.hide_help()) { if(!type.hide_help()) {
@ -500,7 +500,7 @@ std::vector<topic> generate_weapon_special_topics(const bool sort_generated)
} }
} }
} else if(effect["apply_to"] == "attack" && effect.has_child("set_specials")) { } else if(effect["apply_to"] == "attack" && effect.has_child("set_specials")) {
for(config::any_child spec : effect.child("set_specials").all_children_range()) { for(config::any_child spec : effect.mandatory_child("set_specials").all_children_range()) {
if(!spec.cfg["name"].empty()) { if(!spec.cfg["name"].empty()) {
special_description.emplace(spec.cfg["name"].t_str(), spec.cfg["description"].t_str()); special_description.emplace(spec.cfg["name"].t_str(), spec.cfg["description"].t_str());
if(!type.hide_help()) { if(!type.hide_help()) {
@ -611,9 +611,9 @@ std::vector<topic> generate_era_topics(const bool sort_generated, const std::str
{ {
std::vector<topic> topics; std::vector<topic> topics;
const config & era = game_cfg->find_child("era","id", era_id); auto era = game_cfg->find_child("era","id", era_id);
if(era && !era["hide_help"].to_bool()) { if(era && !era["hide_help"].to_bool()) {
topics = generate_faction_topics(era, sort_generated); topics = generate_faction_topics(*era, sort_generated);
std::vector<std::string> faction_links; std::vector<std::string> faction_links;
for (const topic & t : topics) { for (const topic & t : topics) {
@ -1200,7 +1200,7 @@ UNIT_DESCRIPTION_TYPE description_type(const unit_type &type)
std::string generate_contents_links(const std::string& section_name, config const *help_cfg) std::string generate_contents_links(const std::string& section_name, config const *help_cfg)
{ {
const config& section_cfg = help_cfg->find_child("section", "id", section_name); auto section_cfg = help_cfg->find_child("section", "id", section_name);
if (!section_cfg) { if (!section_cfg) {
return std::string(); return std::string();
} }
@ -1216,7 +1216,7 @@ std::string generate_contents_links(const std::string& section_name, config cons
std::vector<std::string>::iterator t; std::vector<std::string>::iterator t;
// Find all topics in this section. // Find all topics in this section.
for (t = topics.begin(); t != topics.end(); ++t) { for (t = topics.begin(); t != topics.end(); ++t) {
if (const config& topic_cfg = help_cfg->find_child("topic", "id", *t)) { if (auto topic_cfg = help_cfg->find_child("topic", "id", *t)) {
std::string id = topic_cfg["id"]; std::string id = topic_cfg["id"];
if (is_visible_id(id)) if (is_visible_id(id))
topics_links.emplace_back(topic_cfg["title"], id); topics_links.emplace_back(topic_cfg["title"], id);

View file

@ -133,8 +133,8 @@ void help_text_area::set_items()
read(cfg, stream); read(cfg, stream);
#define TRY(name) do { \ #define TRY(name) do { \
if (config &child = cfg.child(#name)) \ if (auto child = cfg.optional_child(#name)) \
handle_##name##_cfg(child); \ handle_##name##_cfg(*child); \
} while (0) } while (0)
TRY(ref); TRY(ref);

View file

@ -686,7 +686,7 @@ std::string unit_topic_generator::operator()() const {
for (const config & t : traits) { for (const config & t : traits) {
if (t["availability"].str() == "musthave") { if (t["availability"].str() == "musthave") {
for (const config & effect : t.child_range("effect")) { for (const config & effect : t.child_range("effect")) {
if (!effect.child("filter") // If this is musthave but has a unit filter, it might not always apply, so don't apply it in the help. if (!effect.has_child("filter") // If this is musthave but has a unit filter, it might not always apply, so don't apply it in the help.
&& movetype::effects.find(effect["apply_to"].str()) != movetype::effects.end()) { && movetype::effects.find(effect["apply_to"].str()) != movetype::effects.end()) {
movement_type.merge(effect, effect["replace"].to_bool()); movement_type.merge(effect, effect["replace"].to_bool());
} }

View file

@ -1316,8 +1316,8 @@ protected:
"whiteboard_options", &console_handler::do_whiteboard_options, _("Access whiteboard options dialog.")); "whiteboard_options", &console_handler::do_whiteboard_options, _("Access whiteboard options dialog."));
register_alias("whiteboard_options", "wbo"); register_alias("whiteboard_options", "wbo");
if(const config& alias_list = preferences::get_alias()) { if(auto alias_list = preferences::get_alias()) {
for(const config::attribute& a : alias_list.attribute_range()) { for(const config::attribute& a : alias_list->attribute_range()) {
register_alias(a.second, a.first); register_alias(a.second, a.first);
} }
} }
@ -1817,7 +1817,7 @@ void console_handler::do_choose_level()
// find scenarios of multiplayer campaigns // find scenarios of multiplayer campaigns
// (assumes that scenarios are ordered properly in the game_config) // (assumes that scenarios are ordered properly in the game_config)
std::string scenario_id = menu_handler_.pc_.get_mp_settings().mp_scenario; std::string scenario_id = menu_handler_.pc_.get_mp_settings().mp_scenario;
if(const config& this_scenario = menu_handler_.game_config_.find_child(tag, "id", scenario_id)) { if(auto this_scenario = menu_handler_.game_config_.find_child(tag, "id", scenario_id)) {
std::string addon_id = this_scenario["addon_id"].str(); std::string addon_id = this_scenario["addon_id"].str();
for(const config& sc : menu_handler_.game_config_.child_range(tag)) { for(const config& sc : menu_handler_.game_config_.child_range(tag)) {
if(sc["addon_id"] == addon_id) { if(sc["addon_id"] == addon_id) {

View file

@ -276,7 +276,7 @@ const teleport_map get_teleport_locations(const unit &u,
for (const unit_ability & teleport : u.get_abilities("teleport")) { for (const unit_ability & teleport : u.get_abilities("teleport")) {
const int tunnel_count = (teleport.ability_cfg)->child_count("tunnel"); const int tunnel_count = (teleport.ability_cfg)->child_count("tunnel");
for(int i = 0; i < tunnel_count; ++i) { for(int i = 0; i < tunnel_count; ++i) {
config teleport_group_cfg = (teleport.ability_cfg)->child("tunnel", i); config teleport_group_cfg = (teleport.ability_cfg)->mandatory_child("tunnel", i);
groups.emplace_back(vconfig(teleport_group_cfg, true), false); groups.emplace_back(vconfig(teleport_group_cfg, true), false);
} }
} }
@ -290,7 +290,7 @@ const teleport_map get_teleport_locations(const unit &u,
manager::manager(const config &cfg) : tunnels_(), id_(cfg["next_teleport_group_id"].to_int(0)) { manager::manager(const config &cfg) : tunnels_(), id_(cfg["next_teleport_group_id"].to_int(0)) {
const int tunnel_count = cfg.child_count("tunnel"); const int tunnel_count = cfg.child_count("tunnel");
for(int i = 0; i < tunnel_count; ++i) { for(int i = 0; i < tunnel_count; ++i) {
const config& t = cfg.child("tunnel", i); const config& t = cfg.mandatory_child("tunnel", i);
if(!t["saved"].to_bool()) { if(!t["saved"].to_bool()) {
lg::log_to_chat() << "Do not use [tunnel] directly in a [scenario]. Use it in an [event] or [abilities] tag.\n"; lg::log_to_chat() << "Do not use [tunnel] directly in a [scenario]. Use it in an [event] or [abilities] tag.\n";
ERR_WML << "Do not use [tunnel] directly in a [scenario]. Use it in an [event] or [abilities] tag."; ERR_WML << "Do not use [tunnel] directly in a [scenario]. Use it in an [event] or [abilities] tag.";

View file

@ -70,7 +70,7 @@ bool persist_file_context::clear_var(const std::string &global, bool immediate)
bool ret = active->has_child("variables"); bool ret = active->has_child("variables");
if (ret) { if (ret) {
config &cfg = active->child("variables"); config &cfg = active->mandatory_child("variables");
bool exists = cfg.has_attribute(global); bool exists = cfg.has_attribute(global);
if (!exists) { if (!exists) {
if (cfg.has_child(global)) { if (cfg.has_child(global)) {
@ -100,7 +100,7 @@ bool persist_file_context::clear_var(const std::string &global, bool immediate)
name_space prev = working.prev(); name_space prev = working.prev();
active = get_node(cfg_, prev); active = get_node(cfg_, prev);
active->clear_children(working.node_); active->clear_children(working.node_);
if (active->has_child("variables") && active->child("variables").empty()) { if (active->has_child("variables") && active->mandatory_child("variables").empty()) {
active->clear_children("variables"); active->clear_children("variables");
active->remove_attribute("variables"); active->remove_attribute("variables");
} }
@ -149,7 +149,7 @@ bool persist_file_context::clear_var(const std::string &global, bool immediate)
break; break;
} }
active->clear_children(namespace_.node_); active->clear_children(namespace_.node_);
if (active->has_child("variables") && active->child("variables").empty()) { if (active->has_child("variables") && active->mandatory_child("variables").empty()) {
active->clear_children("variables"); active->clear_children("variables");
active->remove_attribute("variables"); active->remove_attribute("variables");
} }
@ -163,11 +163,11 @@ config persist_file_context::get_var(const std::string &global) const
config ret; config ret;
const config *active = get_node(cfg_, namespace_); const config *active = get_node(cfg_, namespace_);
if (active && (active->has_child("variables"))) { if (active && (active->has_child("variables"))) {
const config &cfg = active->child("variables"); const config &cfg = active->mandatory_child("variables");
std::size_t arrsize = cfg.child_count(global); std::size_t arrsize = cfg.child_count(global);
if (arrsize > 0) { if (arrsize > 0) {
for (std::size_t i = 0; i < arrsize; i++) for (std::size_t i = 0; i < arrsize; i++)
ret.add_child(global,cfg.child(global,i)); ret.add_child(global, cfg.mandatory_child(global,i));
} else { } else {
ret = pack_scalar(global,cfg[global]); ret = pack_scalar(global,cfg[global]);
} }

View file

@ -125,7 +125,7 @@ protected:
if (force) if (force)
return get_node(cfg.child_or_add(next.root_), next, true); return get_node(cfg.child_or_add(next.root_), next, true);
else if (cfg.has_child(next.root_)) else if (cfg.has_child(next.root_))
return get_node(cfg.child(next.root_), next); return get_node(cfg.mandatory_child(next.root_), next);
else else
return nullptr; return nullptr;
} }
@ -137,7 +137,7 @@ protected:
name_space next = ns.next(); name_space next = ns.next();
if (next) { if (next) {
if (cfg.has_child(next.root_)) if (cfg.has_child(next.root_))
return get_node(cfg.child(next.root_), next); return get_node(cfg.mandatory_child(next.root_), next);
else else
return nullptr; return nullptr;
} }

Some files were not shown because too many files have changed in this diff Show more