The Battle for Wesnoth  1.19.5+dev
variable.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2005 - 2024
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 "team.hpp"
32 
33 static lg::log_domain log_engine("engine");
34 #define LOG_NG LOG_STREAM(info, log_engine)
35 #define WRN_NG LOG_STREAM(warn, log_engine)
36 #define ERR_NG LOG_STREAM(err, log_engine)
37 namespace
38 {
39  const config as_nonempty_range_default("_");
40  config::const_child_itors as_nonempty_range(const std::string& varname, const variable_set& vars)
41  {
43 
44  if(!range.empty()) {
45  return range;
46  }
47 
48  return as_nonempty_range_default.child_range("_");
49  }
50 
51  // doxygen didn't like this as an anonymous struct
52  struct anon : public variable_set
53  {
54  config::attribute_value get_variable_const(const std::string&) const override
55  {
56  return config::attribute_value();
57  }
58  variable_access_const get_variable_access_read(const std::string& varname) const override
59  {
60  return variable_access_const(varname, config());
61  }
62  } null_variable_set;
63 }
64 
66  try {
68  return variable.as_scalar();
69  } catch(const invalid_variablename_exception&) {
70  ERR_NG << "invalid variablename " << id;
71  return config::attribute_value();
72  }
73 }
74 
76  return variable_access_const(id, cfg_);
77 }
78 
80 
82 {
84  return resources::gamedata;
85  }
86  return &null_variable_set;
87 }
88 
90  cache_(), cfg_(&default_empty_config), variables_(try_get_gamedata())
91 {
92 }
93 
94 vconfig::vconfig(const config & cfg, const std::shared_ptr<const config> & cache) :
95  cache_(cache), cfg_(&cfg), variables_(try_get_gamedata())
96 {
97 }
98 
99 /**
100  * Constructor from a config, with an option to manage memory.
101  * @param[in] cfg The "WML source" of the vconfig being constructed.
102  * @param[in] manage_memory If true, a copy of @a cfg will be made, allowing the
103  * vconfig to safely persist after @a cfg is destroyed.
104  * If false, no copy is made, so @a cfg must be
105  * guaranteed to persist as long as the vconfig will.
106  * If in doubt, set to true; it is less efficient, but safe.
107  * @param[in] vars
108  * See also make_safe().
109  */
110 vconfig::vconfig(const config &cfg, bool manage_memory, const variable_set* vars)
111  : cache_(manage_memory ? new config(cfg) : nullptr)
112  , cfg_(manage_memory ? cache_.get() : &cfg)
113  , variables_(vars ? vars : try_get_gamedata())
114 {
115 }
116 
118  : cache_(), cfg_(&cfg), variables_(try_get_gamedata())
119 {}
120 
122  : cache_(new config(std::move(cfg))), cfg_(cache_.get()), variables_(try_get_gamedata())
123 {}
124 
125 vconfig::vconfig(const config& cfg, const std::shared_ptr<const config> & cache, const variable_set& variables)
126  : cache_(cache), cfg_(&cfg), variables_(&variables)
127 {}
128 
129 vconfig::vconfig(const config& cfg, const variable_set& variables)
130  : cache_(), cfg_(&cfg), variables_(&variables)
131 {}
132 
133 /**
134  * Default destructor, but defined here for possibly faster compiles
135  * (templates sometimes can be rough on the compiler).
136  */
138 {
139 }
140 
142 {
143  static const config empty_config;
144  return vconfig(empty_config, false);
145 }
146 
147 /**
148  * This is just a wrapper for the default constructor; it exists for historical
149  * reasons and to make it clear that default construction cannot be dereferenced
150  * (in contrast to an empty vconfig).
151  */
153 {
154  return vconfig();
155 }
156 
157 /**
158  * Ensures that *this manages its own memory, making it safe for *this to
159  * outlive the config it was ultimately constructed from.
160  * It is perfectly safe to call this for a vconfig that already manages its memory.
161  * This does not work on a null() vconfig.
162  */
164 {
165  // Nothing to do if we already manage our own memory.
166  if ( memory_managed() )
167  return *this;
168 
169  // Make a copy of our config.
170  cache_.reset(new config(*cfg_));
171  // Use our copy instead of the original.
172  cfg_ = cache_.get();
173  return *this;
174 }
175 
177 {
178  // Keeps track of insert_tag variables.
179  static std::set<std::string> vconfig_recursion;
180 
181  config res;
182 
183  for(const auto& [key, _] : cfg_->attribute_range()) {
184  res[key] = expand(key);
185  }
186 
187  for(const auto [key, cfg] : cfg_->all_children_view())
188  {
189  if (key == "insert_tag") {
190  vconfig insert_cfg(cfg, *variables_);
191  std::string name = insert_cfg["name"];
192  std::string vname = insert_cfg["variable"];
193  if(!vconfig_recursion.insert(vname).second) {
194  throw recursion_error("vconfig::get_parsed_config() infinite recursion detected, aborting");
195  }
196  try
197  {
198  config::const_child_itors range = as_nonempty_range(vname, *variables_);
199  for (const config& ch : range)
200  {
201  res.add_child(name, vconfig(ch, *variables_).get_parsed_config());
202  }
203  }
204  catch(const invalid_variablename_exception&)
205  {
206  res.add_child(name);
207  }
208  catch(const recursion_error &err) {
209  vconfig_recursion.erase(vname);
210  WRN_NG << err.message;
211  if(vconfig_recursion.empty()) {
212  res.add_child("insert_tag", insert_cfg.get_config());
213  } else {
214  // throw to the top [insert_tag] which started the recursion
215  throw;
216  }
217  }
218  vconfig_recursion.erase(vname);
219  } else {
220  res.add_child(key, vconfig(cfg, *variables_).get_parsed_config());
221  }
222  }
223  return res;
224 }
225 
226 vconfig::child_list vconfig::get_children(const std::string& key_to_get) const
227 {
229 
230  for(const auto [key, cfg] : cfg_->all_children_view())
231  {
232  if (key == key_to_get) {
233  res.push_back(vconfig(cfg, cache_, *variables_));
234  } else if (key == "insert_tag") {
235  vconfig insert_cfg(cfg, *variables_);
236  if(insert_cfg["name"] == key_to_get)
237  {
238  try
239  {
240  config::const_child_itors range = as_nonempty_range(insert_cfg["variable"], *variables_);
241  for (const config& ch : range)
242  {
243  res.push_back(vconfig(ch, true, variables_));
244  }
245  }
246  catch(const invalid_variablename_exception&)
247  {
248  res.push_back(empty_vconfig());
249  }
250  }
251  }
252  }
253  return res;
254 }
255 
256 std::size_t vconfig::count_children(const std::string& key_to_count) const
257 {
258  std::size_t n = 0;
259 
260  for(const auto [key, cfg] : cfg_->all_children_view())
261  {
262  if (key == key_to_count) {
263  n++;
264  } else if (key == "insert_tag") {
265  vconfig insert_cfg(cfg, *variables_);
266  if(insert_cfg["name"] == key_to_count)
267  {
268  try
269  {
270  config::const_child_itors range = as_nonempty_range(insert_cfg["variable"], *variables_);
271  n += range.size();
272  }
273  catch(const invalid_variablename_exception&)
274  {
275  n++;
276  }
277  }
278  }
279  }
280  return n;
281 }
282 
283 /**
284  * Returns a child of *this whose key is @a key.
285  * If no such child exists, returns an unconstructed vconfig (use null() to test
286  * for this).
287  */
288 vconfig vconfig::child(const std::string& key) const
289 {
290  if (auto natural = cfg_->optional_child(key)) {
291  return vconfig(*natural, cache_, *variables_);
292  }
293  for (const config &ins : cfg_->child_range("insert_tag"))
294  {
295  vconfig insert_cfg(ins, *variables_);
296  if(insert_cfg["name"] == key)
297  {
298  try
299  {
300  config::const_child_itors range = as_nonempty_range(insert_cfg["variable"], *variables_);
301  return vconfig(range.front(), true, variables_);
302  }
303  catch(const invalid_variablename_exception&)
304  {
305  return empty_vconfig();
306  }
307  }
308  }
309  return unconstructed_vconfig();
310 }
311 
312 /**
313  * Returns whether or not *this has a child whose key is @a key.
314  */
315 bool vconfig::has_child(const std::string& key) const
316 {
317  if (cfg_->has_child(key)) {
318  return true;
319  }
320  for (const config &ins : cfg_->child_range("insert_tag"))
321  {
322  vconfig insert_cfg(ins, *variables_);
323  if(insert_cfg["name"] == key) {
324  return true;
325  }
326  }
327  return false;
328 }
329 
330 namespace {
331  struct vconfig_expand_visitor
332 #ifdef USING_BOOST_VARIANT
333  : boost::static_visitor<void>
334 #endif
335  {
336  config::attribute_value &result;
337  const variable_set& vars;
338 
339  vconfig_expand_visitor(config::attribute_value &r, const variable_set& vars): result(r), vars(vars) {}
340  template<typename T> void operator()(const T&) const {}
341  void operator()(const std::string &s) const
342  {
344  }
345  void operator()(const t_string &s) const
346  {
348  }
349  };
350 }//unnamed namespace
351 
352 config::attribute_value vconfig::expand(const std::string &key) const
353 {
354  config::attribute_value val = (*cfg_)[key];
355  val.apply_visitor(vconfig_expand_visitor(val, *variables_));
356  return val;
357 }
358 
360 {
361  config::attribute val = *i_;
362  val.second.apply_visitor(vconfig_expand_visitor(val.second, *variables_));
363  return val;
364 }
365 
367 {
368  config::attribute val = *i_;
369  val.second.apply_visitor(vconfig_expand_visitor(val.second, *variables_));
370  pointer_proxy p {val};
371  return p;
372 }
373 
375  i_(i), inner_index_(0), cache_(), variables_(&vars)
376 {
377 }
378 
379 vconfig::all_children_iterator::all_children_iterator(const Itor &i, const variable_set& vars, const std::shared_ptr<const config> & cache) :
380  i_(i), inner_index_(0), cache_(cache), variables_(&vars)
381 {
382 }
383 
385 {
386  if (inner_index_ >= 0 && i_->key == "insert_tag")
387  {
388  try
389  {
391 
392  config::const_child_itors range = vinfo.as_array();
393 
394  if (++inner_index_ < static_cast<int>(range.size()))
395  {
396  return *this;
397  }
398 
399  }
400  catch(const invalid_variablename_exception&)
401  {
402  }
403  inner_index_ = 0;
404  }
405  ++i_;
406  return *this;
407 }
408 
410 {
412  this->operator++();
413  return i;
414 }
415 
417 {
418  if(inner_index_ >= 0 && i_->key == "insert_tag") {
419  if(--inner_index_ >= 0) {
420  return *this;
421  }
422  inner_index_ = 0;
423  }
424  --i_;
425  return *this;
426 }
427 
429 {
431  this->operator--();
432  return i;
433 }
434 
436 {
437  return value_type(get_key(), get_child());
438 }
439 
441 {
442  pointer_proxy p { value_type(get_key(), get_child()) };
443  return p;
444 }
445 
446 
448 {
449  const std::string &key = i_->key;
450  if (inner_index_ >= 0 && key == "insert_tag") {
451  return vconfig(i_->cfg, *variables_)["name"];
452  }
453  return key;
454 }
455 
457 {
458  if (inner_index_ >= 0 && i_->key == "insert_tag")
459  {
460  try
461  {
462  config::const_child_itors range = as_nonempty_range(vconfig(i_->cfg, *variables_)["variable"], *variables_);
463 
464  range.advance_begin(inner_index_);
465  return vconfig(range.front(), true, variables_);
466  }
467  catch(const invalid_variablename_exception&)
468  {
469  return empty_vconfig();
470  }
471  }
472  return vconfig(i_->cfg, cache_, *variables_);
473 }
474 
476 {
477  return i_ == i.i_ && inner_index_ == i.inner_index_;
478 }
479 
481 {
483 }
484 
486 {
488 }
489 
490 scoped_wml_variable::scoped_wml_variable(const std::string& var_name) :
491  previous_val_(),
492  var_name_(var_name),
493  activated_(false)
494 {
496  resources::gamedata->scoped_variables.push_back(this);
497 }
498 
500 {
501  try
502  {
503  for (const config &i : resources::gamedata->get_variables().child_range(var_name_)) {
505  }
508  LOG_NG << "scoped_wml_variable: var_name \"" << var_name_ << "\" has been auto-stored.";
509  activated_ = true;
510  return res;
511  }
512  catch(const invalid_variablename_exception&)
513  {
514  assert(false && "invalid variable name of autostored variable");
515  throw "assertion ignored";
516  }
517 
518 }
519 
521 {
522  if(!resources::gamedata) {
523  return;
524  }
525 
526  if(activated_) {
529  {
530  try
531  {
533  }
534  catch(const invalid_variablename_exception&)
535  {
536  }
537  }
538  LOG_NG << "scoped_wml_variable: var_name \"" << var_name_ << "\" has been reverted.";
539  }
540 
541  assert(resources::gamedata->scoped_variables.back() == this);
543 }
544 
546 {
548  if(itor != umap_.end()) {
549  config &tmp_cfg = store();
550  itor->write(tmp_cfg);
551  tmp_cfg["x"] = loc_.wml_x();
552  tmp_cfg["y"] = loc_.wml_y();
553  LOG_NG << "auto-storing $" << name() << " at (" << loc_ << ")";
554  } else {
555  ERR_NG << "failed to auto-store $" << name() << " at (" << loc_ << ")";
556  }
557 }
558 
560 {
561  if (data_) {
562  store(*data_);
563  }
564 }
565 
567 {
568  assert(resources::gameboard);
569 
570  const std::vector<team>& teams = resources::gameboard->teams();
571 
572  std::vector<team>::const_iterator team_it = std::find_if(teams.begin(), teams.end(), [&](const team& t) { return t.save_id_or_number() == player_; });
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 }
double t
Definition: astarsearch.cpp:63
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:65
const config & cfg_
Definition: variable.hpp:28
virtual variable_access_const get_variable_access_read(const std::string &varname) const
Definition: variable.cpp:75
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
all_children_iterator erase(const all_children_iterator &i)
Definition: config.cpp:638
const_all_children_iterator ordered_begin() const
Definition: config.cpp:864
const_attr_itors attribute_range() const
Definition: config.cpp:760
config_attribute_value attribute_value
Variant for storing WML attributes.
Definition: config.hpp:305
auto all_children_view() const
In-order iteration over all children.
Definition: config.hpp:810
const_all_children_iterator ordered_end() const
Definition: config.cpp:874
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:316
child_itors child_range(config_key_type key)
Definition: config.cpp:272
attribute_map::value_type attribute
Definition: config.hpp:312
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:296
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:384
config & add_child(config_key_type key)
Definition: config.cpp:440
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:490
config & store(const config &var_value=config())
Definition: variable.cpp:499
virtual ~scoped_wml_variable()
Definition: variable.cpp:520
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:545
const unit_map & umap_
Definition: variable.hpp:257
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:75
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:137
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:480
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:152
static vconfig empty_vconfig()
Definition: variable.cpp:141
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
Definition: variable.cpp:288
config::attribute_value expand(const std::string &) const
Definition: variable.cpp:352
const config & get_config() const
Definition: variable.hpp:75
config get_parsed_config() const
Definition: variable.cpp:176
all_children_iterator ordered_end() const
Definition: variable.cpp:485
const vconfig & make_safe() const
instruct the vconfig to make a private copy of its underlying data.
Definition: variable.cpp:163
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:89
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:256
child_list get_children(const std::string &key) const
Definition: variable.cpp:226
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:315
std::size_t i
Definition: function.cpp:1028
static std::string _(const char *str)
Definition: gettext.hpp:93
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:198
Standard logging facilities (interface).
CURSOR_TYPE get()
Definition: cursor.cpp:216
logger & err()
Definition: log.cpp:307
game_board * gameboard
Definition: resources.cpp:20
game_data * gamedata
Definition: resources.cpp:22
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:184
int wml_x() const
Definition: location.hpp:183
all_children_iterator(const Itor &i, const variable_set &vars)
Definition: variable.cpp:374
const value_type reference
Definition: variable.hpp:144
all_children_iterator & operator++()
Definition: variable.cpp:384
const std::pair< std::string, vconfig > value_type
Definition: variable.hpp:138
reference operator*() const
Definition: variable.cpp:435
bool operator==(const all_children_iterator &i) const
Definition: variable.cpp:475
std::string get_key() const
Definition: variable.cpp:447
all_children_iterator & operator--()
Definition: variable.cpp:416
pointer operator->() const
Definition: variable.cpp:366
reference operator*() const
Definition: variable.cpp:359
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:35
static lg::log_domain log_engine("engine")
#define ERR_NG
Definition: variable.cpp:36
static const variable_set * try_get_gamedata()
Definition: variable.cpp:81
#define LOG_NG
Definition: variable.cpp:34
variable_info< const variable_info_implementation::vi_policy_const > variable_access_const
Read-only access.