From 23b0c5343f0d8444635586cf3318e7221e815841 Mon Sep 17 00:00:00 2001 From: gfgtdf Date: Mon, 30 Jun 2014 00:33:04 +0200 Subject: [PATCH] catch invalid variablename exception when using these exceptiosn are not thrown yet in all cases. This patch prepares a patch taht makes use of it. --- src/game_data.cpp | 33 ++- src/game_events/action_wml.cpp | 484 ++++++++++++++++++--------------- src/menu_events.cpp | 12 +- src/persist_var.cpp | 35 ++- src/scripting/lua.cpp | 53 ++-- src/synced_commands.cpp | 11 +- src/variable.cpp | 145 ++++++---- 7 files changed, 469 insertions(+), 304 deletions(-) diff --git a/src/game_data.cpp b/src/game_data.cpp index 385306b612eb..ede92557784f 100644 --- a/src/game_data.cpp +++ b/src/game_data.cpp @@ -107,7 +107,7 @@ game_data::game_data(const game_data& data) , scenario_(data.scenario_) , next_scenario_(data.next_scenario_) {} - +//throws config::attribute_value &game_data::get_variable(const std::string& key) { return get_variable_access(key, variable_info::TYPE_SCALAR).as_scalar(); @@ -115,9 +115,16 @@ config::attribute_value &game_data::get_variable(const std::string& key) config::attribute_value game_data::get_variable_const(const std::string &key) const { - return get_variable_access_readonly(key, variable_info::TYPE_SCALAR).as_scalar_const(); + try + { + return get_variable_access_readonly(key, variable_info::TYPE_SCALAR).as_scalar_const(); + } + catch(const invalid_variable_info_exception&) + { + return config::attribute_value(); + } } - +//throws config& game_data::get_variable_cfg(const std::string& key) { return get_variable_access(key, variable_info::TYPE_CONTAINER).as_container(); @@ -125,9 +132,16 @@ config& game_data::get_variable_cfg(const std::string& key) void game_data::set_variable(const std::string& key, const t_string& value) { - get_variable(key) = value; + try + { + get_variable(key) = value; + } + catch(const invalid_variable_info_exception&) + { + ERR_NG << "variable " << key << "cannot be set to " << value << std::endl; + } } - +//throws config& game_data::add_variable_cfg(const std::string& key, const config& value) { return get_variable_access(key, variable_info::TYPE_ARRAY).add_child(value); @@ -135,7 +149,14 @@ config& game_data::add_variable_cfg(const std::string& key, const config& value) void game_data::clear_variable_cfg(const std::string& varname) { - get_variable_access_noadd(varname, variable_info::TYPE_CONTAINER).clear(true); + try + { + get_variable_access_noadd(varname, variable_info::TYPE_CONTAINER).clear(true); + } + catch(const invalid_variable_info_exception&) + { + //variable doesn't exist, nothing to delete + } } void game_data::clear_variable(const std::string& varname) diff --git a/src/game_events/action_wml.cpp b/src/game_events/action_wml.cpp index 7073d3de64f6..a7d27ee78485 100644 --- a/src/game_events/action_wml.cpp +++ b/src/game_events/action_wml.cpp @@ -1827,238 +1827,247 @@ WML_HANDLER_FUNCTION(set_menu_item, /*event_info*/, cfg) WML_HANDLER_FUNCTION(set_variable, /*event_info*/, cfg) { game_data *gameinfo = resources::gamedata; + try + { + const std::string name = cfg["name"]; + if(name.empty()) { + ERR_NG << "trying to set a variable with an empty name:\n" << cfg.get_config().debug(); + return; + } + config::attribute_value &var = gameinfo->get_variable(name); - const std::string name = cfg["name"]; - if(name.empty()) { - ERR_NG << "trying to set a variable with an empty name:\n" << cfg.get_config().debug(); - return; - } - config::attribute_value &var = gameinfo->get_variable(name); - - config::attribute_value literal = cfg.get_config()["literal"]; // no $var substitution - if (!literal.blank()) { - var = literal; - } - - config::attribute_value value = cfg["value"]; - if (!value.blank()) { - var = value; - } + config::attribute_value literal = cfg.get_config()["literal"]; // no $var substitution + if (!literal.blank()) { + var = literal; + } - const std::string to_variable = cfg["to_variable"]; - if(to_variable.empty() == false) { - var = gameinfo->get_variable(to_variable); - } + config::attribute_value value = cfg["value"]; + if (!value.blank()) { + var = value; + } - config::attribute_value add = cfg["add"]; - if (!add.empty()) { - var = var.to_double() + add.to_double(); - } + const std::string to_variable = cfg["to_variable"]; + if(to_variable.empty() == false) { + var = gameinfo->get_variable(to_variable); + } - config::attribute_value sub = cfg["sub"]; - if (!sub.empty()) { - var = var.to_double() - sub.to_double(); - } + config::attribute_value add = cfg["add"]; + if (!add.empty()) { + var = var.to_double() + add.to_double(); + } - config::attribute_value multiply = cfg["multiply"]; - if (!multiply.empty()) { - var = var.to_double() * multiply.to_double(); - } + config::attribute_value sub = cfg["sub"]; + if (!sub.empty()) { + var = var.to_double() - sub.to_double(); + } - config::attribute_value divide = cfg["divide"]; - if (!divide.empty()) { - if (divide.to_double() == 0) { - ERR_NG << "division by zero on variable " << name << std::endl; - return; + config::attribute_value multiply = cfg["multiply"]; + if (!multiply.empty()) { + var = var.to_double() * multiply.to_double(); } - var = var.to_double() / divide.to_double(); - } - config::attribute_value modulo = cfg["modulo"]; - if (!modulo.empty()) { - if (modulo.to_double() == 0) { - ERR_NG << "division by zero on variable " << name << std::endl; - return; + config::attribute_value divide = cfg["divide"]; + if (!divide.empty()) { + if (divide.to_double() == 0) { + ERR_NG << "division by zero on variable " << name << std::endl; + return; + } + var = var.to_double() / divide.to_double(); } - var = std::fmod(var.to_double(), modulo.to_double()); - } - config::attribute_value round_val = cfg["round"]; - if (!round_val.empty()) { - double value = var.to_double(); - if (round_val == "ceil") { - var = int(std::ceil(value)); - } else if (round_val == "floor") { - var = int(std::floor(value)); - } else { - // We assume the value is an integer. - // Any non-numerical values will be interpreted as 0 - // Which is probably what was intended anyway - int decimals = round_val.to_int(); - value *= std::pow(10.0, decimals); //add $decimals zeroes - value = round_portable(value); // round() isn't implemented everywhere - value *= std::pow(10.0, -decimals); //and remove them - var = value; + config::attribute_value modulo = cfg["modulo"]; + if (!modulo.empty()) { + if (modulo.to_double() == 0) { + ERR_NG << "division by zero on variable " << name << std::endl; + return; + } + var = std::fmod(var.to_double(), modulo.to_double()); } - } - config::attribute_value ipart = cfg["ipart"]; - if (!ipart.empty()) { - double result; - std::modf(ipart.to_double(), &result); - var = int(result); - } + config::attribute_value round_val = cfg["round"]; + if (!round_val.empty()) { + double value = var.to_double(); + if (round_val == "ceil") { + var = int(std::ceil(value)); + } else if (round_val == "floor") { + var = int(std::floor(value)); + } else { + // We assume the value is an integer. + // Any non-numerical values will be interpreted as 0 + // Which is probably what was intended anyway + int decimals = round_val.to_int(); + value *= std::pow(10.0, decimals); //add $decimals zeroes + value = round_portable(value); // round() isn't implemented everywhere + value *= std::pow(10.0, -decimals); //and remove them + var = value; + } + } - config::attribute_value fpart = cfg["fpart"]; - if (!fpart.empty()) { - double ignore; - var = std::modf(fpart.to_double(), &ignore); - } + config::attribute_value ipart = cfg["ipart"]; + if (!ipart.empty()) { + double result; + std::modf(ipart.to_double(), &result); + var = int(result); + } - config::attribute_value string_length_target = cfg["string_length"]; - if (!string_length_target.blank()) { - var = int(string_length_target.str().size()); - } + config::attribute_value fpart = cfg["fpart"]; + if (!fpart.empty()) { + double ignore; + var = std::modf(fpart.to_double(), &ignore); + } - // Note: maybe we add more options later, eg. strftime formatting. - // For now make the stamp mandatory. - const std::string time = cfg["time"]; - if(time == "stamp") { - var = int(SDL_GetTicks()); - } + config::attribute_value string_length_target = cfg["string_length"]; + if (!string_length_target.blank()) { + var = int(string_length_target.str().size()); + } - // Random generation works as follows: - // rand=[comma delimited list] - // Each element in the list will be considered a separate choice, - // unless it contains "..". In this case, it must be a numerical - // range (i.e. -1..-10, 0..100, -10..10, etc). - const std::string rand = cfg["rand"]; + // Note: maybe we add more options later, eg. strftime formatting. + // For now make the stamp mandatory. + const std::string time = cfg["time"]; + if(time == "stamp") { + var = int(SDL_GetTicks()); + } - // The new random generator, the logic is a copy paste of the old random. - if(rand.empty() == false) { - assert(gameinfo); + // Random generation works as follows: + // rand=[comma delimited list] + // Each element in the list will be considered a separate choice, + // unless it contains "..". In this case, it must be a numerical + // range (i.e. -1..-10, 0..100, -10..10, etc). + const std::string rand = cfg["rand"]; - // A default value in case something goes really wrong. - var = ""; + // The new random generator, the logic is a copy paste of the old random. + if(rand.empty() == false) { + assert(gameinfo); - std::string word; - std::vector words; - std::vector > ranges; - long num_choices = 0; - std::string::size_type pos = 0, pos2 = std::string::npos; - std::stringstream ss(std::stringstream::in|std::stringstream::out); - while (pos2 != rand.length()) { - pos = pos2+1; - pos2 = rand.find(",", pos); + // A default value in case something goes really wrong. + var = ""; - if (pos2 == std::string::npos) - pos2 = rand.length(); + std::string word; + std::vector words; + std::vector > ranges; + long num_choices = 0; + std::string::size_type pos = 0, pos2 = std::string::npos; + std::stringstream ss(std::stringstream::in|std::stringstream::out); + while (pos2 != rand.length()) { + pos = pos2+1; + pos2 = rand.find(",", pos); - word = rand.substr(pos, pos2-pos); - words.push_back(word); - std::string::size_type tmp = word.find(".."); + if (pos2 == std::string::npos) + pos2 = rand.length(); + word = rand.substr(pos, pos2-pos); + words.push_back(word); + std::string::size_type tmp = word.find(".."); - if (tmp == std::string::npos) { - // Treat this element as a string - ranges.push_back(std::pair(0,0)); - num_choices += 1; - } - else { - // Treat as a numerical range - const std::string first = word.substr(0, tmp); - const std::string second = word.substr(tmp+2, - rand.length()); - long low, high; - ss << first + " " + second; - if ( !(ss >> low) || !(ss >> high) ) { - ERR_NG << "Malformed range: rand = \"" << rand << "\"" << std::endl; - // Treat this element as a string? + if (tmp == std::string::npos) { + // Treat this element as a string ranges.push_back(std::pair(0,0)); num_choices += 1; } else { - if (low > high) { - std::swap(low, high); + // Treat as a numerical range + const std::string first = word.substr(0, tmp); + const std::string second = word.substr(tmp+2, + rand.length()); + + long low, high; + ss << first + " " + second; + if ( !(ss >> low) || !(ss >> high) ) { + ERR_NG << "Malformed range: rand = \"" << rand << "\"" << std::endl; + // Treat this element as a string? + ranges.push_back(std::pair(0,0)); + num_choices += 1; } - ranges.push_back(std::pair(low,high)); - num_choices += (high - low) + 1; + else { + if (low > high) { + std::swap(low, high); + } + ranges.push_back(std::pair(low,high)); + num_choices += (high - low) + 1; - // Make 0..0 ranges work - if (high == 0 && low == 0) { - words.pop_back(); - words.push_back("0"); + // Make 0..0 ranges work + if (high == 0 && low == 0) { + words.pop_back(); + words.push_back("0"); + } } + ss.clear(); } - ss.clear(); } - } - /* - * Choice gets a value in the range [0..32768). - * So need to add a second set of random values when a value - * outside the range is requested. - */ - if(num_choices > 0x3fffffff) { - WRN_NG << "Requested random number with an upper bound of " + /* + * Choice gets a value in the range [0..32768). + * So need to add a second set of random values when a value + * outside the range is requested. + */ + if(num_choices > 0x3fffffff) { + WRN_NG << "Requested random number with an upper bound of " << num_choices << " however the maximum number generated will be " << 0x3fffffff << ".\n"; - } - long choice = random_new::generator->next_random();// gameinfo->rng().get_next_random(); - if(num_choices >= 32768) { - choice <<= 15; - choice += random_new::generator->next_random();//gameinfo->rng().get_next_random(); - } - choice %= num_choices; - long tmp = 0; - for(size_t i = 0; i < ranges.size(); ++i) { - tmp += (ranges[i].second - ranges[i].first) + 1; - if (tmp > choice) { - if (ranges[i].first == 0 && ranges[i].second == 0) { - var = words[i]; - } - else { - var = (ranges[i].second - (tmp - choice)) + 1; + } + long choice = random_new::generator->next_random();// gameinfo->rng().get_next_random(); + if(num_choices >= 32768) { + choice <<= 15; + choice += random_new::generator->next_random();//gameinfo->rng().get_next_random(); + } + choice %= num_choices; + long tmp = 0; + for(size_t i = 0; i < ranges.size(); ++i) { + tmp += (ranges[i].second - ranges[i].first) + 1; + if (tmp > choice) { + if (ranges[i].first == 0 && ranges[i].second == 0) { + var = words[i]; + } + else { + var = (ranges[i].second - (tmp - choice)) + 1; + } + break; } - break; } } - } - const vconfig::child_list join_elements = cfg.get_children("join"); - if(!join_elements.empty()) - { - const vconfig & join_element = join_elements.front(); + const vconfig::child_list join_elements = cfg.get_children("join"); + if(!join_elements.empty()) + { + const vconfig & join_element = join_elements.front(); - std::string array_name=join_element["variable"]; - std::string separator=join_element["separator"]; - std::string key_name=join_element["key"]; + std::string array_name=join_element["variable"]; + std::string separator=join_element["separator"]; + std::string key_name=join_element["key"]; - if(key_name.empty()) - { - key_name="value"; - } + if(key_name.empty()) + { + key_name="value"; + } - bool remove_empty = join_element["remove_empty"].to_bool(); + bool remove_empty = join_element["remove_empty"].to_bool(); - std::string joined_string; + std::string joined_string; - variable_info vi = resources::gamedata->get_variable_access(array_name, variable_info::TYPE_ARRAY); - bool first = true; - BOOST_FOREACH(const config &cfg, vi.as_array()) - { - std::string current_string = cfg[key_name]; - if (remove_empty && current_string.empty()) continue; - if (first) first = false; - else joined_string += separator; - joined_string += current_string; - } - var=joined_string; + + + variable_info vi = resources::gamedata->get_variable_access(array_name, variable_info::TYPE_ARRAY); + bool first = true; + BOOST_FOREACH(const config &cfg, vi.as_array()) + { + std::string current_string = cfg[key_name]; + if (remove_empty && current_string.empty()) continue; + if (first) first = false; + else joined_string += separator; + joined_string += current_string; + } + + var = joined_string; + } + } + catch(const invalid_variable_info_exception&) + { + ERR_NG << "Found invalid variablename in [set_variable] with " << cfg.get_config().debug() << "\n"; } } @@ -2080,17 +2089,29 @@ WML_HANDLER_FUNCTION(set_variables, /*event_info*/, cfg) if(cfg.has_attribute("to_variable")) { - variable_info tovar = resources::gamedata->get_variable_access_readonly(cfg["to_variable"], variable_info::TYPE_CONTAINER); - if(tovar.get_is_valid()) { - if(tovar.is_explicit_index()) { - data.add_child(dest.get_final_key(), tovar.as_container()); - } else { - variable_info::array_range range = tovar.as_array(); - while(range.first != range.second) - { - data.add_child(dest.get_final_key(), *range.first++); - } + try + { + variable_info tovar = resources::gamedata->get_variable_access_readonly(cfg["to_variable"], variable_info::TYPE_CONTAINER); + BOOST_FOREACH(const config& c, tovar.as_array_throw()) + { + data.add_child(dest.get_final_key(), c); } + /* + if(tovar.get_is_valid()) { + if(tovar.is_explicit_index()) { + data.add_child(dest.get_final_key(), tovar.as_container()); + } else { + variable_info::array_range range = tovar.as_array(); + while(range.first != range.second) + { + data.add_child(dest.get_final_key(), *range.first++); + } + } + }*/ + } + catch(const invalid_variable_info_exception&) + { + ERR_NG << "Cannot do [set_variables] with invalid to_variable variable: " << cfg["to_variable"] << " with " << cfg.get_config().debug() << std::endl; } } else if(!values.empty()) { for(vconfig::child_list::const_iterator i=values.begin(); i!=values.end(); ++i) @@ -2136,8 +2157,14 @@ WML_HANDLER_FUNCTION(set_variables, /*event_info*/, cfg) data.add_child(dest.get_final_key())[key_name]=*i; } } - - dest.set_range(data, cfg["mode"]);// replace, append, merge, or insert + try + { + dest.set_range(data, cfg["mode"]);// replace, append, merge, or insert + } + catch(const invalid_variable_info_exception&) + { + ERR_NG << "Cannot do [set_variables] with invalid destination variable: " << name << " with " << cfg.get_config().debug() << std::endl; + } } WML_HANDLER_FUNCTION(sound_source, /*event_info*/, cfg) @@ -2176,10 +2203,16 @@ WML_HANDLER_FUNCTION(store_relative_dir, /*event_info*/, cfg) std::string variable = cfg["variable"]; map_location::RELATIVE_DIR_MODE mode = static_cast (cfg["mode"].to_int(0)); + try + { + variable_info store = resources::gamedata->get_variable_access(variable, variable_info::TYPE_SCALAR ); - variable_info store = resources::gamedata->get_variable_access(variable, variable_info::TYPE_SCALAR ); - - store.as_scalar() = map_location::write_direction(src.get_relative_dir(dst,mode)); + store.as_scalar() = map_location::write_direction(src.get_relative_dir(dst,mode)); + } + catch(const invalid_variable_info_exception&) + { + ERR_NG << "Cannot do [store_relative_dir] with invalid destination variable: " << variable << " with " << cfg.get_config().debug() << std::endl; + } } /// Store the rotation of one hex around another in a WML variable. @@ -2206,10 +2239,17 @@ WML_HANDLER_FUNCTION(store_rotate_map_location, /*event_info*/, cfg) std::string variable = cfg["variable"]; int angle = cfg["angle"].to_int(1); + + try + { + variable_info store = resources::gamedata->get_variable_access(variable, variable_info::TYPE_CONTAINER ); - variable_info store = resources::gamedata->get_variable_access(variable, variable_info::TYPE_CONTAINER ); - - dst.rotate_right_around_center(src,angle).write(store.as_container()); + dst.rotate_right_around_center(src,angle).write(store.as_container()); + } + catch(const invalid_variable_info_exception&) + { + ERR_NG << "Cannot do [store_rotate_map_location] with invalid destination variable: " << variable << " with " << cfg.get_config().debug() << std::endl; + } } @@ -2227,10 +2267,15 @@ WML_HANDLER_FUNCTION(store_time_of_day, /*event_info*/, cfg) if(variable.empty()) { variable = "time_of_day"; } - - variable_info store = resources::gamedata->get_variable_access(variable, variable_info::TYPE_CONTAINER); - - tod.write(store.as_container()); + try + { + variable_info store = resources::gamedata->get_variable_access(variable, variable_info::TYPE_CONTAINER); + tod.write(store.as_container()); + } + catch(const invalid_variable_info_exception&) + { + ERR_NG << "Found invalid variablename " << variable << " in [store_time_of_day] with " << cfg.get_config().debug() << "\n"; + } } WML_HANDLER_FUNCTION(teleport, event_info, cfg) @@ -2394,12 +2439,20 @@ WML_HANDLER_FUNCTION(unit, /*event_info*/, cfg) { parsed_cfg.remove_attribute("to_variable"); unit new_unit(parsed_cfg, true); - config &var = resources::gamedata->get_variable_cfg(to_variable); - var.clear(); - new_unit.write(var); - if (const config::attribute_value *v = parsed_cfg.get("x")) var["x"] = *v; - if (const config::attribute_value *v = parsed_cfg.get("y")) var["y"] = *v; + try + { + config &var = resources::gamedata->get_variable_cfg(to_variable); + var.clear(); + new_unit.write(var); + if (const config::attribute_value *v = parsed_cfg.get("x")) var["x"] = *v; + if (const config::attribute_value *v = parsed_cfg.get("y")) var["y"] = *v; + } + catch(const invalid_variable_info_exception&) + { + ERR_NG << "Cannot do [unit] with invalid to_variable: " << to_variable << " with " << cfg.get_config().debug() << std::endl; + } return; + } int side = parsed_cfg["side"].to_int(1); @@ -2429,9 +2482,9 @@ WML_HANDLER_FUNCTION(unit, /*event_info*/, cfg) /// Unit serialization from variables WML_HANDLER_FUNCTION(unstore_unit, /*event_info*/, cfg) { - const config &var = resources::gamedata->get_variable_cfg(cfg["variable"]); - try { + const config &var = resources::gamedata->get_variable_cfg(cfg["variable"]); + config tmp_cfg(var); const unit_ptr u = unit_ptr( new unit(tmp_cfg, false)); @@ -2520,7 +2573,12 @@ WML_HANDLER_FUNCTION(unstore_unit, /*event_info*/, cfg) (*resources::teams)[u->side() - 1].have_leader(); } - } catch (game::game_error &e) { + } + catch (const invalid_variable_info_exception&) + { + ERR_NG << "invlid variable name in unstore_unit" << std::endl; + } + catch (game::game_error &e) { ERR_NG << "could not de-serialize unit: '" << e.message << "'" << std::endl; } } diff --git a/src/menu_events.cpp b/src/menu_events.cpp index fc818d41ffa4..71cac89bfd2b 100644 --- a/src/menu_events.cpp +++ b/src/menu_events.cpp @@ -3091,8 +3091,16 @@ void console_handler::do_set_var() { if(j != data.end()) { const std::string name(data.begin(),j); const std::string value(j+1,data.end()); - resources::gamedata->set_variable(name,value); - } else { + try + { + resources::gamedata->set_variable(name,value); + } + catch(const invalid_variable_info_exception&) + { + command_failed(_("Variable not found")); + } + } + else { command_failed(_("Variable not found")); } } diff --git a/src/persist_var.cpp b/src/persist_var.cpp index 4806c9963445..3ba8053d4ac4 100644 --- a/src/persist_var.cpp +++ b/src/persist_var.cpp @@ -31,6 +31,7 @@ //TODO: remove LOG_PERSIST, ERR_PERSIST from persist_context.hpp to .cpp files. #define DBG_PERSIST LOG_STREAM(debug, log_persist) +#define ERR_PERSIST LOG_STREAM(err, log_persist) struct persist_choice: mp_sync::user_choice { const persist_context &ctx; @@ -62,17 +63,24 @@ static void get_global_variable(persist_context &ctx, const vconfig &pcfg) int side = pcfg_side.str() == "global" ? resources::controller->current_side() : pcfg_side.to_int(); persist_choice choice(ctx,global,side); config cfg = mp_sync::get_user_choice("global_variable",choice,side).child("variables"); - if (cfg) { - size_t arrsize = cfg.child_count(global); - if (arrsize == 0) { - resources::gamedata->set_variable(local,cfg[global]); + try + { + if (cfg) { + size_t arrsize = cfg.child_count(global); + if (arrsize == 0) { + resources::gamedata->set_variable(local,cfg[global]); + } else { + resources::gamedata->clear_variable(local); + for (size_t i = 0; i < arrsize; i++) + resources::gamedata->add_variable_cfg(local,cfg.child(global,i)); + } } else { - resources::gamedata->clear_variable(local); - for (size_t i = 0; i < arrsize; i++) - resources::gamedata->add_variable_cfg(local,cfg.child(global,i)); + resources::gamedata->set_variable(local,""); } - } else { - resources::gamedata->set_variable(local,""); + } + catch(const invalid_variable_info_exception&) + { + ERR_PERSIST << "cannot store global variable into invalid variablename " << local << std::endl; } } @@ -93,7 +101,14 @@ static void set_global_variable(persist_context &ctx, const vconfig &pcfg) const config &vars = resources::gamedata->get_variables(); size_t arraylen = vars.child_count(local); if (arraylen == 0) { - val = pack_scalar(global,resources::gamedata->get_variable(local)); + try + { + val = pack_scalar(global,resources::gamedata->get_variable(local)); + } + catch(const invalid_variable_info_exception&) + { + val = config(); + } } else { for (size_t i = 0; i < arraylen; i++) val.add_child(global,vars.child(local,i)); diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index 02d2d3435bfc..ea46c9953f61 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -990,19 +990,26 @@ static int intf_fire_event(lua_State *L) static int intf_get_variable(lua_State *L) { char const *m = luaL_checkstring(L, 1); - variable_info v = resources::gamedata->get_variable_access_readonly(m, variable_info::TYPE_SCALAR); - if (v.get_is_valid()) { - luaW_pushscalar(L, v.as_scalar()); - return 1; - } else { - variable_info w = resources::gamedata->get_variable_access_readonly(m, variable_info::TYPE_CONTAINER); - if (w.get_is_valid()) { - lua_newtable(L); - if (lua_toboolean(L, 2)) - luaW_filltable(L, w.as_container()); + try + { + variable_info v = resources::gamedata->get_variable_access_readonly(m, variable_info::TYPE_SCALAR); + if (v.get_is_valid()) { + luaW_pushscalar(L, v.as_scalar()); return 1; + } else { + variable_info w = resources::gamedata->get_variable_access_readonly(m, variable_info::TYPE_CONTAINER); + if (w.get_is_valid()) { + lua_newtable(L); + if (lua_toboolean(L, 2)) + luaW_filltable(L, w.as_container()); + return 1; + } } } + catch (const invalid_variable_info_exception&) + { + ERR_LUA << "invlid variable name in wesnoth.get_veriable" << std::endl; + } return 0; } @@ -1019,9 +1026,10 @@ static int intf_set_variable(lua_State *L) resources::gamedata->clear_variable(m); return 0; } - - variable_info v = resources::gamedata->get_variable_access(m); - switch (lua_type(L, 2)) { + try + { + variable_info v = resources::gamedata->get_variable_access(m); + switch (lua_type(L, 2)) { case LUA_TBOOLEAN: v.as_scalar() = luaW_toboolean(L, 2); break; @@ -1038,15 +1046,20 @@ static int intf_set_variable(lua_State *L) } // no break case LUA_TTABLE: - { - config &cfg = v.as_container(); - cfg.clear(); - if (luaW_toconfig(L, 2, cfg)) - break; - // no break - } + { + config &cfg = v.as_container(); + cfg.clear(); + if (luaW_toconfig(L, 2, cfg)) + break; + // no break + } default: return luaL_typerror(L, 2, "WML table or scalar"); + } + } + catch (const invalid_variable_info_exception&) + { + ERR_LUA << "invlid variable name in wesnoth.set_veriable" << std::endl; } return 0; } diff --git a/src/synced_commands.cpp b/src/synced_commands.cpp index 08f0b87a8aec..e899358aac42 100644 --- a/src/synced_commands.cpp +++ b/src/synced_commands.cpp @@ -300,12 +300,19 @@ SYNCED_COMMAND_HANDLER_FUNCTION(move, child, use_undo, show, error_handler) return true; } -SYNCED_COMMAND_HANDLER_FUNCTION(fire_event, child, /*use_undo*/, /*show*/, /*error_handler*/) +SYNCED_COMMAND_HANDLER_FUNCTION(fire_event, child, /*use_undo*/, /*show*/, error_handler) { //i don't know the reason for the following three lines. //TODO: find out wheter we can delete them. I think this code was introduced in bbfdfcf9ed6ca44f01da32bf74c39d5fa9a75c37 BOOST_FOREACH(const config &v, child.child_range("set_variable")) { - resources::gamedata->set_variable(v["name"], v["value"]); + try + { + resources::gamedata->set_variable(v["name"], v["value"]); + } + catch(const invalid_variable_info_exception&) + { + error_handler("invalid variable name", false); + } } bool undoable = true; diff --git a/src/variable.cpp b/src/variable.cpp index 54bb9183aa6b..cd40234153c9 100644 --- a/src/variable.cpp +++ b/src/variable.cpp @@ -39,16 +39,6 @@ static lg::log_domain log_engine("engine"); #define WRN_NG LOG_STREAM(warn, log_engine) #define ERR_NG LOG_STREAM(err, log_engine) -namespace -{ - /** - * @todo FIXME: the variable repository should be - * a class of variable.hpp, and not the game_state. - */ - #define repos (resources::gamedata) -} - - vconfig::vconfig() : cache_(), cfg_(NULL) { @@ -153,7 +143,12 @@ config vconfig::get_parsed_config() const } } vconfig_recursion.erase(vname); - } catch(recursion_error &err) { + } + catch(const invalid_variable_info_exception&) + { + res.add_child(name); + } + catch(recursion_error &err) { vconfig_recursion.erase(vname); WRN_NG << err.message << std::endl; if(vconfig_recursion.empty()) { @@ -180,23 +175,32 @@ vconfig::child_list vconfig::get_children(const std::string& key) const res.push_back(vconfig(child.cfg, cache_)); } else if (child.key == "insert_tag") { vconfig insert_cfg(child.cfg); - if(insert_cfg["name"] == key) { - variable_info vinfo = resources::gamedata->get_variable_access_readonly(insert_cfg["variable"], variable_info::TYPE_CONTAINER); - if(!vinfo.get_is_valid()) { - //push back an empty tag - res.push_back(empty_vconfig()); - } else if(vinfo.is_explicit_index()) { - res.push_back(vconfig(vinfo.as_container(), true)); - } else { - variable_info::array_range range = vinfo.as_array(); - if(range.first == range.second) { + if(insert_cfg["name"] == key) + { + try + { + variable_info vinfo = resources::gamedata->get_variable_access_readonly(insert_cfg["variable"], variable_info::TYPE_CONTAINER); + if(!vinfo.get_is_valid()) { //push back an empty tag res.push_back(empty_vconfig()); - } - while(range.first != range.second) { - res.push_back(vconfig(*range.first++, true)); + } else if(vinfo.is_explicit_index()) { + res.push_back(vconfig(vinfo.as_container(), true)); + } else { + variable_info::array_range range = vinfo.as_array(); + if(range.first == range.second) { + //push back an empty tag + res.push_back(empty_vconfig()); + } + while(range.first != range.second) + { + res.push_back(vconfig(*range.first++, true)); + } } } + catch(const invalid_variable_info_exception&) + { + res.push_back(empty_vconfig()); + } } } } @@ -216,12 +220,21 @@ vconfig vconfig::child(const std::string& key) const BOOST_FOREACH(const config &ins, cfg_->child_range("insert_tag")) { vconfig insert_cfg(ins); - if(insert_cfg["name"] == key) { - variable_info vinfo = resources::gamedata->get_variable_access_readonly(insert_cfg["variable"], variable_info::TYPE_CONTAINER); - if(!vinfo.get_is_valid()) { + if(insert_cfg["name"] == key) + { + try + { + variable_info vinfo = resources::gamedata->get_variable_access_readonly(insert_cfg["variable"], variable_info::TYPE_CONTAINER); + if(!vinfo.get_is_valid()) + { + return empty_vconfig(); + } + return vconfig(vinfo.as_container(), true); + } + catch(const invalid_variable_info_exception&) + { return empty_vconfig(); } - return vconfig(vinfo.as_container(), true); } } return unconstructed_vconfig(); @@ -266,7 +279,7 @@ namespace { config::attribute_value vconfig::expand(const std::string &key) const { config::attribute_value val = (*cfg_)[key]; - if (repos) + if (resources::gamedata) val.apply_visitor(vconfig_expand_visitor(val)); return val; } @@ -285,13 +298,19 @@ vconfig::all_children_iterator& vconfig::all_children_iterator::operator++() { if (inner_index_ >= 0 && i_->key == "insert_tag") { - variable_info vinfo = resources::gamedata->get_variable_access_readonly(vconfig(i_->cfg)["variable"], variable_info::TYPE_CONTAINER); - if(vinfo.get_is_valid() && !vinfo.is_explicit_index()) { - variable_info::array_range range = vinfo.as_array(); - if (++inner_index_ < std::distance(range.first, range.second)) { - return *this; + try + { + variable_info vinfo = resources::gamedata->get_variable_access_readonly(vconfig(i_->cfg)["variable"], variable_info::TYPE_CONTAINER); + if(vinfo.get_is_valid() && !vinfo.is_explicit_index()) { + variable_info::array_range range = vinfo.as_array(); + if (++inner_index_ < std::distance(range.first, range.second)) { + return *this; + } + inner_index_ = 0; } - inner_index_ = 0; + } + catch(const invalid_variable_info_exception&) + { } } ++i_; @@ -330,15 +349,23 @@ vconfig vconfig::all_children_iterator::get_child() const { if (inner_index_ >= 0 && i_->key == "insert_tag") { - variable_info vinfo = resources::gamedata->get_variable_access_readonly(vconfig(i_->cfg)["variable"], variable_info::TYPE_CONTAINER); - if(!vinfo.get_is_valid()) { + try + { + variable_info vinfo = resources::gamedata->get_variable_access_readonly(vconfig(i_->cfg)["variable"], variable_info::TYPE_CONTAINER); + if(!vinfo.get_is_valid()) { + return empty_vconfig(); + } else if(inner_index_ == 0) { + return vconfig(vinfo.as_container(), true); + } else { + variable_info::array_range r = vinfo.as_array(); + std::advance(r.first, inner_index_); + return vconfig(*r.first, true); + } + } + catch(const invalid_variable_info_exception&) + { return empty_vconfig(); - } else if(inner_index_ == 0) { - return vconfig(vinfo.as_container(), true); } - variable_info::array_range r = vinfo.as_array(); - std::advance(r.first, inner_index_); - return vconfig(*r.first, true); } return vconfig(i_->cfg, cache_); } @@ -369,22 +396,38 @@ scoped_wml_variable::scoped_wml_variable(const std::string& var_name) : config &scoped_wml_variable::store(const config &var_value) { - BOOST_FOREACH(const config &i, resources::gamedata->get_variables().child_range(var_name_)) { - previous_val_.add_child(var_name_, i); + try + { + BOOST_FOREACH(const config &i, resources::gamedata->get_variables().child_range(var_name_)) { + previous_val_.add_child(var_name_, i); + } + resources::gamedata->clear_variable_cfg(var_name_); + config &res = resources::gamedata->add_variable_cfg(var_name_, var_value); + LOG_NG << "scoped_wml_variable: var_name \"" << var_name_ << "\" has been auto-stored.\n"; + activated_ = true; + return res; } - resources::gamedata->clear_variable_cfg(var_name_); - config &res = resources::gamedata->add_variable_cfg(var_name_, var_value); - LOG_NG << "scoped_wml_variable: var_name \"" << var_name_ << "\" has been auto-stored.\n"; - activated_ = true; - return res; + catch(const invalid_variable_info_exception&) + { + assert(false && "invalid variable name of autostored varaible"); + throw "assertion ignored"; + } + } scoped_wml_variable::~scoped_wml_variable() { if(activated_) { resources::gamedata->clear_variable_cfg(var_name_); - BOOST_FOREACH(const config &i, previous_val_.child_range(var_name_)) { - resources::gamedata->add_variable_cfg(var_name_, i); + BOOST_FOREACH(const config &i, previous_val_.child_range(var_name_)) + { + try + { + resources::gamedata->add_variable_cfg(var_name_, i); + } + catch(const invalid_variable_info_exception&) + { + } } LOG_NG << "scoped_wml_variable: var_name \"" << var_name_ << "\" has been reverted.\n"; }