The Battle for Wesnoth  1.19.14+dev
variable.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2005 - 2025
3  by Philippe Plantier <ayin@anathas.org>
4  Copyright (C) 2003 by David White <dave@whitevine.net>
5  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY.
13 
14  See the COPYING file for more details.
15 */
16 
17 /**
18  * @file
19  * Manage WML-variables.
20  */
21 
22 #include "variable.hpp"
23 
24 #include "formula/string_utils.hpp"
25 #include "game_board.hpp"
26 #include "game_data.hpp"
27 #include "log.hpp"
28 #include "resources.hpp"
29 #include "units/unit.hpp"
30 #include "units/map.hpp"
31 #include "utils/general.hpp"
32 #include "team.hpp"
33 
34 static lg::log_domain log_engine("engine");
35 #define LOG_NG LOG_STREAM(info, log_engine)
36 #define WRN_NG LOG_STREAM(warn, log_engine)
37 #define ERR_NG LOG_STREAM(err, log_engine)
38 namespace
39 {
40  const config as_nonempty_range_default("_");
41  config::const_child_itors as_nonempty_range(const std::string& varname, const variable_set& vars)
42  {
44 
45  if(!range.empty()) {
46  return range;
47  }
48 
49  return as_nonempty_range_default.child_range("_");
50  }
51 
52  // doxygen didn't like this as an anonymous struct
53  struct anon : public variable_set
54  {
55  config::attribute_value get_variable_const(const std::string&) const override
56  {
57  return config::attribute_value();
58  }
59  variable_access_const get_variable_access_read(const std::string& varname) const override
60  {
61  return variable_access_const(varname, config());
62  }
63  } null_variable_set;
64 }
65 
67  try {
69  return variable.as_scalar();
70  } catch(const invalid_variablename_exception&) {
71  ERR_NG << "invalid variablename " << id;
72  return config::attribute_value();
73  }
74 }
75 
77  return variable_access_const(id, cfg_);
78 }
79 
81 
83 {
85  return resources::gamedata;
86  }
87  return &null_variable_set;
88 }
89 
91  cache_(), cfg_(&default_empty_config), variables_(try_get_gamedata())
92 {
93 }
94 
95 vconfig::vconfig(const config & cfg, const std::shared_ptr<const config> & cache) :
96  cache_(cache), cfg_(&cfg), variables_(try_get_gamedata())
97 {
98 }
99 
100 /**
101  * Constructor from a config, with an option to manage memory.
102  * @param[in] cfg The "WML source" of the vconfig being constructed.
103  * @param[in] manage_memory If true, a copy of @a cfg will be made, allowing the
104  * vconfig to safely persist after @a cfg is destroyed.
105  * If false, no copy is made, so @a cfg must be
106  * guaranteed to persist as long as the vconfig will.
107  * If in doubt, set to true; it is less efficient, but safe.
108  * @param[in] vars
109  * See also make_safe().
110  */
111 vconfig::vconfig(const config &cfg, bool manage_memory, const variable_set* vars)
112  : cache_(manage_memory ? new config(cfg) : nullptr)
113  , cfg_(manage_memory ? cache_.get() : &cfg)
114  , variables_(vars ? vars : try_get_gamedata())
115 {
116 }
117 
119  : cache_(), cfg_(&cfg), variables_(try_get_gamedata())
120 {}
121 
123  : cache_(new config(std::move(cfg))), cfg_(cache_.get()), variables_(try_get_gamedata())
124 {}
125 
126 vconfig::vconfig(const config& cfg, const std::shared_ptr<const config> & cache, const variable_set& variables)
127  : cache_(cache), cfg_(&cfg), variables_(&variables)
128 {}
129 
130 vconfig::vconfig(const config& cfg, const variable_set& variables)
131  : cache_(), cfg_(&cfg), variables_(&variables)
132 {}
133 
134 /**
135  * Default destructor, but defined here for possibly faster compiles
136  * (templates sometimes can be rough on the compiler).
137  */
139 {
140 }
141 
143 {
144  static const config empty_config;
145  return vconfig(empty_config, false);
146 }
147 
148 /**
149  * This is just a wrapper for the default constructor; it exists for historical
150  * reasons and to make it clear that default construction cannot be dereferenced
151  * (in contrast to an empty vconfig).
152  */
154 {
155  return vconfig();
156 }
157 
158 /**
159  * Ensures that *this manages its own memory, making it safe for *this to
160  * outlive the config it was ultimately constructed from.
161  * It is perfectly safe to call this for a vconfig that already manages its memory.
162  * This does not work on a null() vconfig.
163  */
165 {
166  // Nothing to do if we already manage our own memory.
167  if ( memory_managed() )
168  return *this;
169 
170  // Make a copy of our config.
171  cache_.reset(new config(*cfg_));
172  // Use our copy instead of the original.
173  cfg_ = cache_.get();
174  return *this;
175 }
176 
178 {
179  // Keeps track of insert_tag variables.
180  static std::set<std::string> vconfig_recursion;
181 
182  config res;
183 
184  for(const auto& [key, _] : cfg_->attribute_range()) {
185  res[key] = expand(key);
186  }
187 
188  for(const auto [key, cfg] : cfg_->all_children_view())
189  {
190  if (key == "insert_tag") {
191  vconfig insert_cfg(cfg, *variables_);
192  std::string name = insert_cfg["name"];
193  std::string vname = insert_cfg["variable"];
194  if(!vconfig_recursion.insert(vname).second) {
195  throw recursion_error("vconfig::get_parsed_config() infinite recursion detected, aborting");
196  }
197  try
198  {
199  config::const_child_itors range = as_nonempty_range(vname, *variables_);
200  for (const config& ch : range)
201  {
202  res.add_child(name, vconfig(ch, *variables_).get_parsed_config());
203  }
204  }
205  catch(const invalid_variablename_exception&)
206  {
207  res.add_child(name);
208  }
209  catch(const recursion_error &err) {
210  vconfig_recursion.erase(vname);
211  WRN_NG << err.message;
212  if(vconfig_recursion.empty()) {
213  res.add_child("insert_tag", insert_cfg.get_config());
214  } else {
215  // throw to the top [insert_tag] which started the recursion
216  throw;
217  }
218  }
219  vconfig_recursion.erase(vname);
220  } else {
222  }
223  }
224  return res;
225 }
226 
227 vconfig::child_list vconfig::get_children(const std::string& key_to_get) const
228 {
230 
231  for(const auto [key, cfg] : cfg_->all_children_view())
232  {
233  if (key == key_to_get) {
234  res.push_back(vconfig(cfg, cache_, *variables_));
235  } else if (key == "insert_tag") {
236  vconfig insert_cfg(cfg, *variables_);
237  if(insert_cfg["name"] == key_to_get)
238  {
239  try
240  {
241  config::const_child_itors range = as_nonempty_range(insert_cfg["variable"], *variables_);
242  for (const config& ch : range)
243  {
244  res.push_back(vconfig(ch, true, variables_));
245  }
246  }
247  catch(const invalid_variablename_exception&)
248  {
249  res.push_back(empty_vconfig());
250  }
251  }
252  }
253  }
254  return res;
255 }
256 
257 std::size_t vconfig::count_children(const std::string& key_to_count) const
258 {
259  std::size_t n = 0;
260 
261  for(const auto [key, cfg] : cfg_->all_children_view())
262  {
263  if (key == key_to_count) {
264  n++;
265  } else if (key == "insert_tag") {
266  vconfig insert_cfg(cfg, *variables_);
267  if(insert_cfg["name"] == key_to_count)
268  {
269  try
270  {
271  config::const_child_itors range = as_nonempty_range(insert_cfg["variable"], *variables_);
272  n += range.size();
273  }
274  catch(const invalid_variablename_exception&)
275  {
276  n++;
277  }
278  }
279  }
280  }
281  return n;
282 }
283 
284 /**
285  * Returns a child of *this whose key is @a key.
286  * If no such child exists, returns an unconstructed vconfig (use null() to test
287  * for this).
288  */
289 vconfig vconfig::child(const std::string& key) const
290 {
291  if (auto natural = cfg_->optional_child(key)) {
292  return vconfig(*natural, cache_, *variables_);
293  }
294  for (const config &ins : cfg_->child_range("insert_tag"))
295  {
296  vconfig insert_cfg(ins, *variables_);
297  if(insert_cfg["name"] == key)
298  {
299  try
300  {
301  config::const_child_itors range = as_nonempty_range(insert_cfg["variable"], *variables_);
302  return vconfig(range.front(), true, variables_);
303  }
304  catch(const invalid_variablename_exception&)
305  {
306  return empty_vconfig();
307  }
308  }
309  }
310  return unconstructed_vconfig();
311 }
312 
313 /**
314  * Returns whether or not *this has a child whose key is @a key.
315  */
316 bool vconfig::has_child(const std::string& key) const
317 {
318  if (cfg_->has_child(key)) {
319  return true;
320  }
321  for (const config &ins : cfg_->child_range("insert_tag"))
322  {
323  vconfig insert_cfg(ins, *variables_);
324  if(insert_cfg["name"] == key) {
325  return true;
326  }
327  }
328  return false;
329 }
330 
331 namespace {
332  struct vconfig_expand_visitor
333 #ifdef USING_BOOST_VARIANT
334  : boost::static_visitor<void>
335 #endif
336  {
337  config::attribute_value &result;
338  const variable_set& vars;
339 
340  vconfig_expand_visitor(config::attribute_value &r, const variable_set& vars): result(r), vars(vars) {}
341  template<typename T> void operator()(const T&) const {}
342  void operator()(const std::string &s) const
343  {
345  }
346  void operator()(const t_string &s) const
347  {
349  }
350  };
351 }//unnamed namespace
352 
353 config::attribute_value vconfig::expand(const std::string &key) const
354 {
355  config::attribute_value val = (*cfg_)[key];
356  val.apply_visitor(vconfig_expand_visitor(val, *variables_));
357  return val;
358 }
359 
361 {
362  config::attribute val = *i_;
363  val.second.apply_visitor(vconfig_expand_visitor(val.second, *variables_));
364  return val;
365 }
366 
368 {
369  config::attribute val = *i_;
370  val.second.apply_visitor(vconfig_expand_visitor(val.second, *variables_));
371  pointer_proxy p {val};
372  return p;
373 }
374 
376  i_(i), inner_index_(0), cache_(), variables_(&vars)
377 {
378 }
379 
380 vconfig::all_children_iterator::all_children_iterator(const Itor &i, const variable_set& vars, const std::shared_ptr<const config> & cache) :
381  i_(i), inner_index_(0), cache_(cache), variables_(&vars)
382 {
383 }
384 
386 {
387  if (inner_index_ >= 0 && i_->key == "insert_tag")
388  {
389  try
390  {
392 
393  config::const_child_itors range = vinfo.as_array();
394 
395  if (++inner_index_ < static_cast<int>(range.size()))
396  {
397  return *this;
398  }
399 
400  }
401  catch(const invalid_variablename_exception&)
402  {
403  }
404  inner_index_ = 0;
405  }
406  ++i_;
407  return *this;
408 }
409 
411 {
413  this->operator++();
414  return i;
415 }
416 
418 {
419  if(inner_index_ >= 0 && i_->key == "insert_tag") {
420  if(--inner_index_ >= 0) {
421  return *this;
422  }
423  inner_index_ = 0;
424  }
425  --i_;
426  return *this;
427 }
428 
430 {
432  this->operator--();
433  return i;
434 }
435 
437 {
438  return value_type(get_key(), get_child());
439 }
440 
442 {
443  pointer_proxy p { value_type(get_key(), get_child()) };
444  return p;
445 }
446 
447 
449 {
450  const std::string &key = i_->key;
451  if (inner_index_ >= 0 && key == "insert_tag") {
452  return vconfig(i_->cfg, *variables_)["name"];
453  }
454  return key;
455 }
456 
458 {
459  if (inner_index_ >= 0 && i_->key == "insert_tag")
460  {
461  try
462  {
463  config::const_child_itors range = as_nonempty_range(vconfig(i_->cfg, *variables_)["variable"], *variables_);
464 
465  range.advance_begin(inner_index_);
466  return vconfig(range.front(), true, variables_);
467  }
468  catch(const invalid_variablename_exception&)
469  {
470  return empty_vconfig();
471  }
472  }
473  return vconfig(i_->cfg, cache_, *variables_);
474 }
475 
477 {
478  return i_ == i.i_ && inner_index_ == i.inner_index_;
479 }
480 
482 {
484 }
485 
487 {
489 }
490 
491 scoped_wml_variable::scoped_wml_variable(const std::string& var_name) :
492  previous_val_(),
493  var_name_(var_name),
494  activated_(false)
495 {
497  resources::gamedata->scoped_variables.push_back(this);
498 }
499 
501 {
502  try
503  {
504  for (const config &i : resources::gamedata->get_variables().child_range(var_name_)) {
506  }
509  LOG_NG << "scoped_wml_variable: var_name \"" << var_name_ << "\" has been auto-stored.";
510  activated_ = true;
511  return res;
512  }
513  catch(const invalid_variablename_exception&)
514  {
515  assert(false && "invalid variable name of autostored variable");
516  throw "assertion ignored";
517  }
518 
519 }
520 
522 {
523  if(!resources::gamedata) {
524  return;
525  }
526 
527  if(activated_) {
530  {
531  try
532  {
534  }
535  catch(const invalid_variablename_exception&)
536  {
537  }
538  }
539  LOG_NG << "scoped_wml_variable: var_name \"" << var_name_ << "\" has been reverted.";
540  }
541 
542  assert(resources::gamedata->scoped_variables.back() == this);
544 }
545 
547 {
549  if(itor != umap_.end()) {
550  config &tmp_cfg = store();
551  itor->write(tmp_cfg);
552  tmp_cfg["x"] = loc_.wml_x();
553  tmp_cfg["y"] = loc_.wml_y();
554  LOG_NG << "auto-storing $" << name() << " at (" << loc_ << ")";
555  } else {
556  ERR_NG << "failed to auto-store $" << name() << " at (" << loc_ << ")";
557  }
558 }
559 
561 {
562  if (data_) {
563  store(*data_);
564  }
565 }
566 
568 {
569  assert(resources::gameboard);
570 
571  const std::vector<team>& teams = resources::gameboard->teams();
572  auto team_it = utils::ranges::find(teams, player_, &team::save_id_or_number);
573 
574  if(team_it != teams.end()) {
575  if(team_it->recall_list().size() > recall_index_) {
576  config &tmp_cfg = store();
577  team_it->recall_list()[recall_index_]->write(tmp_cfg);
578  tmp_cfg["x"] = "recall";
579  tmp_cfg["y"] = "recall";
580  LOG_NG << "auto-storing $" << name() << " for player: " << player_
581  << " at recall index: " << recall_index_;
582  } else {
583  ERR_NG << "failed to auto-store $" << name() << " for player: " << player_
584  << " at recall index: " << recall_index_;
585  }
586  } else {
587  ERR_NG << "failed to auto-store $" << name() << " for player: " << player_;
588  }
589 }
Variant for storing WML attributes.
auto apply_visitor(const V &visitor) const
Visitor support: Applies a visitor to the underlying variant.
virtual config::attribute_value get_variable_const(const std::string &id) const
Definition: variable.cpp:66
const config & cfg_
Definition: variable.hpp:28
virtual variable_access_const get_variable_access_read(const std::string &varname) const
Definition: variable.cpp:76
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
all_children_iterator erase(const all_children_iterator &i)
Definition: config.cpp:634
const_all_children_iterator ordered_begin() const
Definition: config.cpp:854
const_attr_itors attribute_range() const
Definition: config.cpp:756
config_attribute_value attribute_value
Variant for storing WML attributes.
Definition: config.hpp:291
auto all_children_view() const
In-order iteration over all children.
Definition: config.hpp:796
const_all_children_iterator ordered_end() const
Definition: config.cpp:864
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:312
child_itors child_range(config_key_type key)
Definition: config.cpp:268
attribute_map::value_type attribute
Definition: config.hpp:298
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:282
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:380
config & add_child(config_key_type key)
Definition: config.cpp:436
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:80
std::vector< scoped_wml_variable * > scoped_variables
Definition: game_data.hpp:31
void clear_variable_cfg(const std::string &varname)
Clears only the config children does nothing if varname is no valid variable name.
Definition: game_data.cpp:106
config & add_variable_cfg(const std::string &varname, const config &value=config())
throws invalid_variablename_exception if varname is no valid variable name.
Definition: game_data.cpp:100
unsigned int recall_index_
Definition: variable.hpp:269
const std::string player_
Definition: variable.hpp:268
optional_const_config data_
Definition: variable.hpp:246
scoped_wml_variable(const std::string &var_name)
Definition: variable.cpp:491
config & store(const config &var_value=config())
Definition: variable.cpp:500
virtual ~scoped_wml_variable()
Definition: variable.cpp:521
const std::string var_name_
Definition: variable.hpp:235
const std::string & name() const
Definition: variable.hpp:229
const map_location loc_
Definition: variable.hpp:256
void activate()
Definition: variable.cpp:546
const unit_map & umap_
Definition: variable.hpp:257
std::string save_id_or_number() const
Definition: team.hpp:256
unit_iterator end()
Definition: map.hpp:428
unit_iterator find(std::size_t id)
Definition: map.cpp:302
Information on a WML variable.
maybe_const_t< config::child_itors, V > as_array() const
If instantiated with vi_policy_const, the lifetime of the returned const attribute_value reference mi...
maybe_const_t< config::attribute_value, V > & as_scalar() const
If instantiated with vi_policy_const, the lifetime of the returned const attribute_value reference mi...
virtual config::attribute_value get_variable_const(const std::string &id) const =0
virtual variable_access_const get_variable_access_read(const std::string &varname) const =0
A variable-expanding proxy for the config class.
Definition: variable.hpp:45
~vconfig()
Default destructor, but defined here for possibly faster compiles (templates sometimes can be rough o...
Definition: variable.cpp:138
bool memory_managed() const
Returns true if *this has made a copy of its config.
Definition: variable.hpp:196
all_children_iterator ordered_begin() const
In-order iteration over all children.
Definition: variable.cpp:481
std::vector< vconfig > child_list
Definition: variable.hpp:78
static vconfig unconstructed_vconfig()
This is just a wrapper for the default constructor; it exists for historical reasons and to make it c...
Definition: variable.cpp:153
static vconfig empty_vconfig()
Definition: variable.cpp:142
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
Definition: variable.cpp:289
config::attribute_value expand(const std::string &) const
Definition: variable.cpp:353
const config & get_config() const
Definition: variable.hpp:75
config get_parsed_config() const
Definition: variable.cpp:177
all_children_iterator ordered_end() const
Definition: variable.cpp:486
const vconfig & make_safe() const
instruct the vconfig to make a private copy of its underlying data.
Definition: variable.cpp:164
std::shared_ptr< const config > cache_
Keeps a copy of our config alive when we manage our own memory.
Definition: variable.hpp:202
vconfig()
Definition: variable.cpp:90
static const config default_empty_config
Definition: variable.hpp:206
const config * cfg_
Used to access our config (original or copy, as appropriate).
Definition: variable.hpp:204
std::size_t count_children(const std::string &key) const
Definition: variable.cpp:257
child_list get_children(const std::string &key) const
Definition: variable.cpp:227
const variable_set * variables_
Definition: variable.hpp:205
bool has_child(const std::string &key) const
Returns whether or not *this has a child whose key is key.
Definition: variable.cpp:316
const config * cfg
std::size_t i
Definition: function.cpp:1032
static std::string _(const char *str)
Definition: gettext.hpp:97
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:199
Standard logging facilities (interface).
CURSOR_TYPE get()
Definition: cursor.cpp:218
logger & err()
Definition: log.cpp:339
game_board * gameboard
Definition: resources.cpp:20
game_data * gamedata
Definition: resources.cpp:22
auto find(Container &container, const Value &value, const Projection &projection={})
Definition: general.hpp:179
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with '$' in the string 'str' with the equivalent ...
t_string interpolate_variables_into_tstring(const t_string &tstr, const variable_set &variables)
Function that does the same as the above, for t_stringS.
int wml_y() const
Definition: location.hpp:187
int wml_x() const
Definition: location.hpp:186
all_children_iterator(const Itor &i, const variable_set &vars)
Definition: variable.cpp:375
const value_type reference
Definition: variable.hpp:144
all_children_iterator & operator++()
Definition: variable.cpp:385
const std::pair< std::string, vconfig > value_type
Definition: variable.hpp:138
reference operator*() const
Definition: variable.cpp:436
bool operator==(const all_children_iterator &i) const
Definition: variable.cpp:476
std::string get_key() const
Definition: variable.cpp:448
all_children_iterator & operator--()
Definition: variable.cpp:417
pointer operator->() const
Definition: variable.cpp:367
reference operator*() const
Definition: variable.cpp:360
const config::attribute reference
Definition: variable.hpp:110
const variable_set * variables_
Definition: variable.hpp:128
mock_party p
static map_location::direction n
static map_location::direction s
#define WRN_NG
Definition: variable.cpp:36
static lg::log_domain log_engine("engine")
#define ERR_NG
Definition: variable.cpp:37
static const variable_set * try_get_gamedata()
Definition: variable.cpp:82
#define LOG_NG
Definition: variable.cpp:35
variable_info< const variable_info_implementation::vi_policy_const > variable_access_const
Read-only access.