00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "global.hpp"
00024
00025 #include "variable.hpp"
00026
00027 #include "foreach.hpp"
00028 #include "formula_string_utils.hpp"
00029 #include "gamestatus.hpp"
00030 #include "log.hpp"
00031 #include "resources.hpp"
00032 #include "unit.hpp"
00033 #include "unit_map.hpp"
00034 #include "team.hpp"
00035
00036 #include <boost/variant.hpp>
00037
00038 static lg::log_domain log_engine("engine");
00039 #define LOG_NG LOG_STREAM(info, log_engine)
00040 #define WRN_NG LOG_STREAM(warn, log_engine)
00041 #define ERR_NG LOG_STREAM(err, log_engine)
00042
00043 namespace
00044 {
00045
00046
00047
00048
00049 #define repos (resources::state_of_game)
00050
00051
00052 std::set<std::string> vconfig_recursion;
00053
00054
00055
00056
00057
00058
00059 std::map<config const *, int> config_cache;
00060
00061
00062 std::map<std::string const *, config const *> hash_to_cache;
00063
00064
00065 std::map<config const *, std::string const *> config_hashes;
00066
00067 config empty_config;
00068
00069 struct compare_str_ptr {
00070 bool operator()(const std::string* s1, const std::string* s2) const
00071 {
00072 return (*s1) < (*s2);
00073 }
00074 };
00075
00076 class hash_memory_manager {
00077 public:
00078 hash_memory_manager() :
00079 mem_()
00080 {
00081 }
00082
00083 const std::string *find(const std::string& str) const {
00084 std::set<std::string const*, compare_str_ptr>::const_iterator itor = mem_.lower_bound(&str);
00085 if(itor == mem_.end() || **itor != str) {
00086 return NULL;
00087 }
00088 return *itor;
00089 }
00090 void insert(const std::string *newhash) {
00091 mem_.insert(newhash);
00092 }
00093 void clear() {
00094 hash_to_cache.clear();
00095 config_hashes.clear();
00096 std::set<std::string const*, compare_str_ptr>::iterator mem_it,
00097 mem_end = mem_.end();
00098 for(mem_it = mem_.begin(); mem_it != mem_end; ++mem_it) {
00099 delete *mem_it;
00100 }
00101 mem_.clear();
00102 }
00103 ~hash_memory_manager() {
00104 clear();
00105 }
00106 private:
00107 std::set<std::string const*, compare_str_ptr> mem_;
00108 };
00109 hash_memory_manager hash_memory;
00110 }
00111
00112 static const std::string* get_hash_of(const config* cp) {
00113
00114 std::map<config const *, std::string const *>::iterator ch_it = config_hashes.find(cp);
00115 if(ch_it != config_hashes.end()) {
00116 return ch_it->second;
00117 }
00118
00119 const std::string & temp_hash = cp->hash();
00120 std::string const* find_hash = hash_memory.find(temp_hash);
00121 if(find_hash != NULL) {
00122 return find_hash;
00123 }
00124
00125 std::string* new_hash = new std::string(temp_hash);
00126 hash_memory.insert(new_hash);
00127
00128 return new_hash;
00129 }
00130
00131 static void increment_config_usage(const config*& key) {
00132 if(key == NULL) return;
00133 std::map<config const *, int>::iterator this_usage = config_cache.find(key);
00134 if(this_usage != config_cache.end()) {
00135 ++this_usage->second;
00136 return;
00137 }
00138 const std::string *hash = get_hash_of(key);
00139 const config *& cfg_store = hash_to_cache[hash];
00140 if(cfg_store == NULL || (key != cfg_store && *key != *cfg_store)) {
00141
00142 key = new config(*key);
00143
00144 cfg_store = key;
00145
00146 config_hashes[key] = hash;
00147 } else {
00148
00149 key = cfg_store;
00150 }
00151 ++(config_cache[key]);
00152 }
00153
00154 static void decrement_config_usage(const config* key) {
00155 if(key == NULL) return;
00156 std::map<config const *, int>::iterator this_usage = config_cache.find(key);
00157 assert(this_usage != config_cache.end());
00158 if(--(this_usage->second) == 0) {
00159 config_cache.erase(this_usage);
00160 if(config_cache.empty()) {
00161 hash_memory.clear();
00162 } else {
00163 if(!hash_to_cache.empty()) {
00164 hash_to_cache.erase(get_hash_of(key));
00165 }
00166 config_hashes.erase(key);
00167 }
00168 delete key;
00169 }
00170 }
00171
00172 vconfig::vconfig() :
00173 cfg_(NULL), cache_key_(NULL)
00174 {
00175 }
00176
00177 vconfig::vconfig(const config* cfg, const config * cache_key) :
00178 cfg_(cfg), cache_key_(cache_key)
00179 {
00180 increment_config_usage(cache_key_);
00181 if(cache_key_ != cache_key) {
00182
00183 cfg_ = cache_key_;
00184 }
00185 }
00186
00187 vconfig::vconfig(const config &cfg, bool is_volatile) :
00188 cfg_(&cfg), cache_key_(&cfg)
00189 {
00190 if(is_volatile) {
00191 increment_config_usage(cache_key_);
00192 if (cache_key_ != &cfg) {
00193
00194 cfg_ = cache_key_;
00195 }
00196 } else {
00197 cache_key_ = NULL;
00198 }
00199 }
00200
00201 vconfig::vconfig(const vconfig& v) :
00202 cfg_(v.cfg_), cache_key_(v.cache_key_)
00203 {
00204 increment_config_usage(cache_key_);
00205 }
00206
00207 vconfig::~vconfig()
00208 {
00209 decrement_config_usage(cache_key_);
00210 }
00211
00212 vconfig vconfig::empty_vconfig()
00213 {
00214 return vconfig(config(), true);
00215 }
00216
00217 vconfig vconfig::unconstructed_vconfig()
00218 {
00219 return vconfig();
00220 }
00221
00222 vconfig& vconfig::operator=(const vconfig& cfg)
00223 {
00224 const config* prev_key = cache_key_;
00225 cfg_ = cfg.cfg_;
00226 cache_key_ = cfg.cache_key_;
00227 increment_config_usage(cache_key_);
00228 decrement_config_usage(prev_key);
00229 return *this;
00230 }
00231
00232 const config vconfig::get_parsed_config() const
00233 {
00234 config res;
00235
00236 foreach (const config::attribute &i, cfg_->attribute_range()) {
00237 res[i.first] = expand(i.first);
00238 }
00239
00240 foreach (const config::any_child &child, cfg_->all_children_range())
00241 {
00242 if (child.key == "insert_tag") {
00243 vconfig insert_cfg(child.cfg);
00244 const t_string& name = insert_cfg["name"];
00245 const t_string& vname = insert_cfg["variable"];
00246 if(!vconfig_recursion.insert(vname).second) {
00247 throw recursion_error("vconfig::get_parsed_config() infinite recursion detected, aborting");
00248 }
00249 try {
00250 variable_info vinfo(vname, false, variable_info::TYPE_CONTAINER);
00251 if(!vinfo.is_valid) {
00252 res.add_child(name);
00253 } else if(vinfo.explicit_index) {
00254 res.add_child(name, vconfig(vinfo.as_container()).get_parsed_config());
00255 } else {
00256 variable_info::array_range range = vinfo.as_array();
00257 if(range.first == range.second) {
00258 res.add_child(name);
00259 }
00260 while(range.first != range.second) {
00261 res.add_child(name, vconfig(*range.first++).get_parsed_config());
00262 }
00263 }
00264 vconfig_recursion.erase(vname);
00265 } catch(recursion_error &err) {
00266 vconfig_recursion.erase(vname);
00267 WRN_NG << err.message << std::endl;
00268 if(vconfig_recursion.empty()) {
00269 res.add_child("insert_tag", insert_cfg.get_config());
00270 } else {
00271
00272 throw;
00273 }
00274 }
00275 } else {
00276 res.add_child(child.key, vconfig(child.cfg).get_parsed_config());
00277 }
00278 }
00279 return res;
00280 }
00281
00282 vconfig::child_list vconfig::get_children(const std::string& key) const
00283 {
00284 vconfig::child_list res;
00285
00286 foreach (const config::any_child &child, cfg_->all_children_range())
00287 {
00288 if (child.key == key) {
00289 res.push_back(vconfig(&child.cfg, cache_key_));
00290 } else if (child.key == "insert_tag") {
00291 vconfig insert_cfg(child.cfg);
00292 if(insert_cfg["name"] == key) {
00293 variable_info vinfo(insert_cfg["variable"], false, variable_info::TYPE_CONTAINER);
00294 if(!vinfo.is_valid) {
00295
00296 res.push_back(vconfig(empty_config));
00297 } else if(vinfo.explicit_index) {
00298 config * cp = &(vinfo.as_container());
00299 res.push_back(vconfig(cp, cp));
00300 } else {
00301 variable_info::array_range range = vinfo.as_array();
00302 if(range.first == range.second) {
00303
00304 res.push_back(vconfig(empty_config));
00305 }
00306 while(range.first != range.second) {
00307 config *cp = &*range.first++;
00308 res.push_back(vconfig(cp, cp));
00309 }
00310 }
00311 }
00312 }
00313 }
00314 return res;
00315 }
00316
00317 vconfig vconfig::child(const std::string& key) const
00318 {
00319 if (const config &natural = cfg_->child(key)) {
00320 return vconfig(&natural, cache_key_);
00321 }
00322 foreach (const config &ins, cfg_->child_range("insert_tag"))
00323 {
00324 vconfig insert_cfg(ins);
00325 if(insert_cfg["name"] == key) {
00326 variable_info vinfo(insert_cfg["variable"], false, variable_info::TYPE_CONTAINER);
00327 if(!vinfo.is_valid) {
00328 return vconfig(empty_config);
00329 }
00330 config * cp = &(vinfo.as_container());
00331 return vconfig(cp, cp);
00332 }
00333 }
00334 return unconstructed_vconfig();
00335 }
00336
00337 bool vconfig::has_child(const std::string& key) const
00338 {
00339 if (cfg_->child(key)) {
00340 return true;
00341 }
00342 foreach (const config &ins, cfg_->child_range("insert_tag"))
00343 {
00344 vconfig insert_cfg(ins);
00345 if(insert_cfg["name"] == key) {
00346 return true;
00347 }
00348 }
00349 return false;
00350 }
00351
00352 struct vconfig_expand_visitor : boost::static_visitor<void>
00353 {
00354 config::attribute_value &result;
00355 vconfig_expand_visitor(config::attribute_value &r): result(r) {}
00356 template<typename T> void operator()(T const &) const {}
00357 void operator()(const std::string &s) const
00358 {
00359 result = utils::interpolate_variables_into_string(s, *repos);
00360 }
00361 void operator()(const t_string &s) const
00362 {
00363 result = utils::interpolate_variables_into_tstring(s, *repos);
00364 }
00365 };
00366
00367 config::attribute_value vconfig::expand(const std::string &key) const
00368 {
00369 config::attribute_value val = (*cfg_)[key];
00370 if (repos)
00371 boost::apply_visitor(vconfig_expand_visitor(val), val.value);
00372 return val;
00373 }
00374
00375 vconfig::all_children_iterator::all_children_iterator(const Itor &i, const config *cache_key)
00376 : i_(i), inner_index_(0), cache_key_(cache_key)
00377 {
00378 }
00379
00380 vconfig::all_children_iterator& vconfig::all_children_iterator::operator++()
00381 {
00382 if (inner_index_ >= 0 && i_->key == "insert_tag")
00383 {
00384 variable_info vinfo(vconfig(i_->cfg)["variable"], false, variable_info::TYPE_CONTAINER);
00385 if(vinfo.is_valid && !vinfo.explicit_index) {
00386 variable_info::array_range range = vinfo.as_array();
00387 if (++inner_index_ < std::distance(range.first, range.second)) {
00388 return *this;
00389 }
00390 inner_index_ = 0;
00391 }
00392 }
00393 ++i_;
00394 return *this;
00395 }
00396
00397 vconfig::all_children_iterator vconfig::all_children_iterator::operator++(int)
00398 {
00399 vconfig::all_children_iterator i = *this;
00400 this->operator++();
00401 return i;
00402 }
00403
00404 vconfig::all_children_iterator::reference vconfig::all_children_iterator::operator*() const
00405 {
00406 return value_type(get_key(), get_child());
00407 }
00408
00409 vconfig::all_children_iterator::pointer vconfig::all_children_iterator::operator->() const
00410 {
00411 pointer_proxy p = { value_type(get_key(), get_child()) };
00412 return p;
00413 }
00414
00415
00416 std::string vconfig::all_children_iterator::get_key() const
00417 {
00418 const std::string &key = i_->key;
00419 if (inner_index_ >= 0 && key == "insert_tag") {
00420 return vconfig(i_->cfg)["name"];
00421 }
00422 return key;
00423 }
00424
00425 vconfig vconfig::all_children_iterator::get_child() const
00426 {
00427 if (inner_index_ >= 0 && i_->key == "insert_tag")
00428 {
00429 config * cp;
00430 variable_info vinfo(vconfig(i_->cfg)["variable"], false, variable_info::TYPE_CONTAINER);
00431 if(!vinfo.is_valid) {
00432 return vconfig(empty_config);
00433 } else if(inner_index_ == 0) {
00434 cp = &(vinfo.as_container());
00435 return vconfig(cp, cp);
00436 }
00437 variable_info::array_range r = vinfo.as_array();
00438 std::advance(r.first, inner_index_);
00439 cp = &*r.first;
00440 return vconfig(cp, cp);
00441 }
00442 return vconfig(&i_->cfg, cache_key_);
00443 }
00444
00445 bool vconfig::all_children_iterator::operator==(const all_children_iterator &i) const
00446 {
00447 return i_ == i.i_ && inner_index_ == i.inner_index_;
00448 }
00449
00450 vconfig::all_children_iterator vconfig::ordered_begin() const
00451 {
00452 return all_children_iterator(cfg_->ordered_begin(), cache_key_);
00453 }
00454
00455 vconfig::all_children_iterator vconfig::ordered_end() const
00456 {
00457 return all_children_iterator(cfg_->ordered_end(), cache_key_);
00458 }
00459
00460 namespace variable
00461 {
00462 manager::~manager()
00463 {
00464 hash_memory.clear();
00465 }
00466 }
00467
00468 scoped_wml_variable::scoped_wml_variable(const std::string& var_name) :
00469 previous_val_(),
00470 var_name_(var_name),
00471 activated_(false)
00472 {
00473 repos->scoped_variables.push_back(this);
00474 }
00475
00476 config &scoped_wml_variable::store(const config &var_value)
00477 {
00478 foreach (const config &i, repos->get_variables().child_range(var_name_)) {
00479 previous_val_.add_child(var_name_, i);
00480 }
00481 repos->clear_variable_cfg(var_name_);
00482 config &res = repos->add_variable_cfg(var_name_, var_value);
00483 LOG_NG << "scoped_wml_variable: var_name \"" << var_name_ << "\" has been auto-stored.\n";
00484 activated_ = true;
00485 return res;
00486 }
00487
00488 scoped_wml_variable::~scoped_wml_variable()
00489 {
00490 if(activated_) {
00491 repos->clear_variable_cfg(var_name_);
00492 foreach (const config &i, previous_val_.child_range(var_name_)) {
00493 repos->add_variable_cfg(var_name_, i);
00494 }
00495 LOG_NG << "scoped_wml_variable: var_name \"" << var_name_ << "\" has been reverted.\n";
00496 }
00497 assert(repos->scoped_variables.back() == this);
00498 repos->scoped_variables.pop_back();
00499 }
00500
00501 void scoped_xy_unit::activate()
00502 {
00503 map_location loc = map_location(x_, y_);
00504 unit_map::const_iterator itor = umap_.find(loc);
00505 if(itor != umap_.end()) {
00506 config &tmp_cfg = store();
00507 itor->write(tmp_cfg);
00508 tmp_cfg["x"] = x_ + 1;
00509 tmp_cfg["y"] = y_ + 1;
00510 LOG_NG << "auto-storing $" << name() << " at (" << loc << ")\n";
00511 } else {
00512 ERR_NG << "failed to auto-store $" << name() << " at (" << loc << ")\n";
00513 }
00514 }
00515
00516 void scoped_weapon_info::activate()
00517 {
00518 if (data_) {
00519 store(data_);
00520 }
00521 }
00522
00523 void scoped_recall_unit::activate()
00524 {
00525 const std::vector<team>& teams = teams_manager::get_teams();
00526 std::vector<team>::const_iterator team_it;
00527 for (team_it = teams.begin(); team_it != teams.end(); ++team_it) {
00528 if (team_it->save_id() == player_ )
00529 break;
00530 }
00531
00532 if(team_it != teams.end()) {
00533 if(team_it->recall_list().size() > recall_index_) {
00534 config &tmp_cfg = store();
00535 team_it->recall_list()[recall_index_].write(tmp_cfg);
00536 tmp_cfg["x"] = "recall";
00537 tmp_cfg["y"] = "recall";
00538 LOG_NG << "auto-storing $" << name() << " for player: " << player_
00539 << " at recall index: " << recall_index_ << '\n';
00540 } else {
00541 ERR_NG << "failed to auto-store $" << name() << " for player: " << player_
00542 << " at recall index: " << recall_index_ << '\n';
00543 }
00544 } else {
00545 ERR_NG << "failed to auto-store $" << name() << " for player: " << player_ << '\n';
00546 }
00547 }
00548
00549 namespace {
00550 bool recursive_activation = false;
00551
00552
00553 void activate_scope_variable(std::string var_name)
00554 {
00555 if(recursive_activation)
00556 return;
00557 const std::string::iterator itor = std::find(var_name.begin(),var_name.end(),'.');
00558 if(itor != var_name.end()) {
00559 var_name.erase(itor, var_name.end());
00560 }
00561 std::vector<scoped_wml_variable*>::reverse_iterator rit;
00562 for(rit = repos->scoped_variables.rbegin(); rit != repos->scoped_variables.rend(); ++rit) {
00563 if((**rit).name() == var_name) {
00564 recursive_activation = true;
00565 if(!(**rit).activated()) {
00566 (**rit).activate();
00567 }
00568 recursive_activation = false;
00569 break;
00570 }
00571 }
00572 }
00573 }
00574
00575 variable_info::variable_info(const std::string& varname,
00576 bool force_valid, TYPE validation_type) :
00577 vartype(validation_type),
00578 is_valid(false),
00579 key(),
00580 explicit_index(false),
00581 index(0),
00582 vars(NULL)
00583 {
00584 assert(repos != NULL);
00585 activate_scope_variable(varname);
00586
00587 vars = &repos->variables_;
00588 key = varname;
00589 std::string::const_iterator itor = std::find(key.begin(),key.end(),'.');
00590 int dot_index = key.find('.');
00591
00592 bool force_length = false;
00593
00594 while(itor != key.end()) {
00595 std::string element=key.substr(0,dot_index);
00596 key = key.substr(dot_index+1);
00597
00598 size_t inner_index = 0;
00599 const std::string::iterator index_start = std::find(element.begin(),element.end(),'[');
00600 const bool inner_explicit_index = index_start != element.end();
00601 if(inner_explicit_index) {
00602 const std::string::iterator index_end = std::find(index_start,element.end(),']');
00603 const std::string index_str(index_start+1,index_end);
00604 inner_index = static_cast<size_t>(lexical_cast_default<int>(index_str));
00605 if(inner_index > game_config::max_loop) {
00606 ERR_NG << "variable_info: index greater than " << game_config::max_loop
00607 << ", truncated\n";
00608 inner_index = game_config::max_loop;
00609 }
00610 element = std::string(element.begin(),index_start);
00611 }
00612
00613 size_t size = vars->child_count(element);
00614 if(size <= inner_index) {
00615 if(force_valid) {
00616
00617 if(inner_explicit_index || key != "length") {
00618 for(; size <= inner_index; ++size) {
00619 vars->add_child(element);
00620 }
00621 }
00622 } else if(inner_explicit_index) {
00623 WRN_NG << "variable_info: invalid WML array index, "
00624 << varname << std::endl;
00625 return;
00626 } else if(varname.length() >= 7 && varname.substr(varname.length()-7) == ".length") {
00627
00628
00629 force_length = true;
00630 } else {
00631 WRN_NG << "variable_info: retrieving member of non-existent WML container, "
00632 << varname << std::endl;
00633 return;
00634 }
00635 }
00636 if((!inner_explicit_index && key == "length") || force_length) {
00637 switch(vartype) {
00638 case variable_info::TYPE_ARRAY:
00639 case variable_info::TYPE_CONTAINER:
00640 WRN_NG << "variable_info: using reserved WML variable as wrong type, "
00641 << varname << std::endl;
00642 is_valid = force_valid || repos->temporaries_.child(varname);
00643 break;
00644 case variable_info::TYPE_SCALAR:
00645 default:
00646
00647 repos->temporaries_[varname] = int(size);
00648 is_valid = true;
00649 break;
00650 }
00651 key = varname;
00652 vars = &repos->temporaries_;
00653 return;
00654 }
00655
00656 vars = &vars->child(element, inner_index);
00657 itor = std::find(key.begin(),key.end(),'.');
00658 dot_index = key.find('.');
00659 }
00660
00661 const std::string::iterator index_start = std::find(key.begin(),key.end(),'[');
00662 explicit_index = index_start != key.end();
00663 if(explicit_index) {
00664 const std::string::iterator index_end = std::find(index_start,key.end(),']');
00665 const std::string index_str(index_start+1,index_end);
00666 index = static_cast<size_t>(lexical_cast_default<int>(index_str));
00667 if(index > game_config::max_loop) {
00668 ERR_NG << "variable_info: index greater than " << game_config::max_loop
00669 << ", truncated\n";
00670 index = game_config::max_loop;
00671 }
00672 key = std::string(key.begin(),index_start);
00673 size_t size = vars->child_count(key);
00674 if(size <= index) {
00675 if(!force_valid) {
00676 WRN_NG << "variable_info: invalid WML array index, " << varname << std::endl;
00677 return;
00678 }
00679 for(; size <= index; ++size) {
00680 vars->add_child(key);
00681 }
00682 }
00683 switch(vartype) {
00684 case variable_info::TYPE_ARRAY:
00685 vars = &vars->child(key, index);
00686 key = "__array";
00687 is_valid = force_valid || vars->child(key);
00688 break;
00689 case variable_info::TYPE_SCALAR:
00690 vars = &vars->child(key, index);
00691 key = "__value";
00692 is_valid = force_valid || vars->has_attribute(key);
00693 break;
00694 case variable_info::TYPE_CONTAINER:
00695 case variable_info::TYPE_UNSPECIFIED:
00696 default:
00697 is_valid = true;
00698 return;
00699 }
00700 if (force_valid) {
00701 WRN_NG << "variable_info: using explicitly indexed "
00702 "container as wrong WML type, " << varname << '\n';
00703 }
00704 explicit_index = false;
00705 index = 0;
00706 } else {
00707
00708 switch(vartype) {
00709 case variable_info::TYPE_ARRAY:
00710 case variable_info::TYPE_CONTAINER:
00711 is_valid = force_valid || vars->child(key);
00712 break;
00713 case variable_info::TYPE_SCALAR:
00714 is_valid = force_valid || vars->has_attribute(key);
00715 break;
00716 case variable_info::TYPE_UNSPECIFIED:
00717 default:
00718 is_valid = true;
00719 break;
00720 }
00721 }
00722 }
00723
00724 config::attribute_value &variable_info::as_scalar()
00725 {
00726 assert(is_valid);
00727 return (*vars)[key];
00728 }
00729
00730 config& variable_info::as_container() {
00731 assert(is_valid);
00732 if(explicit_index) {
00733
00734 return vars->child(key, index);
00735 }
00736 if (config &temp = vars->child(key)) {
00737
00738 return temp;
00739 }
00740
00741 return vars->add_child(key);
00742 }
00743
00744 variable_info::array_range variable_info::as_array() {
00745 assert(is_valid);
00746 return vars->child_range(key);
00747 }