The Battle for Wesnoth  1.17.0-dev
persist_var.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2021
3  by Jody Northup
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "game_data.hpp"
17 #include "gettext.hpp"
18 #include "log.hpp"
19 #include "persist_context.hpp"
20 #include "persist_manager.hpp"
21 #include "persist_var.hpp"
22 #include "play_controller.hpp"
23 #include "synced_user_choice.hpp"
24 #include "resources.hpp"
25 #include "variable.hpp"
26 
27 #include <cassert>
28 
29 //TODO: remove LOG_PERSIST, ERR_PERSIST from persist_context.hpp to .cpp files.
30 #define DBG_PERSIST LOG_STREAM(debug, log_persist)
31 #define ERR_PERSIST LOG_STREAM(err, log_persist)
32 
35  std::string var_name;
36  int side;
37  persist_choice(const persist_context &context,const std::string &name, int side_num)
38  : ctx(context)
39  , var_name(name)
40  , side(side_num) {
41  }
42  virtual config query_user(int /*side_for*/) const {
43  //side can be different from side_for: if side was null-controlled
44  //then get_user_choice will use the next non-null-controlled side instead
45  config ret;
46  ret["side"] = side;
47  ret.add_child("variables",ctx.get_var(var_name));
48  return ret;
49  }
50  virtual config random_choice(int /*side_for*/) const {
51  return config();
52  }
53 
54  virtual std::string description() const
55  {
56  // TRANSLATORS: In networked games, this text is shown for other
57  // clients, while they wait to receive the content of a global variable
58  // from another player. This text will be embedded into a sentence.
59  return _("waiting for^a global variable");
60  }
61  virtual bool is_visible() const { return false; }
62 };
63 
64 static void get_global_variable(persist_context &ctx, const vconfig &pcfg)
65 {
66  std::string global = pcfg["from_global"];
67  std::string local = pcfg["to_local"];
68  config::attribute_value pcfg_side = pcfg["side"];
69  const int side = pcfg_side.to_int(resources::controller->current_side());
70  persist_choice choice(ctx, global, side);
71  config cfg = mp_sync::get_user_choice("global_variable",choice,side).child("variables");
72  try
73  {
74  if (cfg) {
75  std::size_t arrsize = cfg.child_count(global);
76  if (arrsize == 0) {
77  resources::gamedata->set_variable(local,cfg[global]);
78  } else {
80  for (std::size_t i = 0; i < arrsize; i++)
81  resources::gamedata->add_variable_cfg(local,cfg.child(global,i));
82  }
83  } else {
85  }
86  }
87  catch(const invalid_variablename_exception&)
88  {
89  ERR_PERSIST << "cannot store global variable into invalid variablename " << local << std::endl;
90  }
91 }
92 
94 {
95  std::string global = pcfg["global"];
96  ctx.clear_var(global, pcfg["immediate"].to_bool());
97 }
98 
99 static void set_global_variable(persist_context &ctx, const vconfig &pcfg)
100 {
101  if (pcfg["from_local"].empty()) {
102  clear_global_variable(ctx, pcfg);
103  } else {
104  std::string global = pcfg["to_global"];
105  std::string local = pcfg["from_local"];
106  config val;
107  const config &vars = resources::gamedata->get_variables();
108  std::size_t arraylen = vars.child_count(local);
109  if (arraylen == 0) {
110  try
111  {
112  val = pack_scalar(global,resources::gamedata->get_variable(local));
113  }
114  catch(const invalid_variablename_exception&)
115  {
116  val.clear();
117  }
118  } else {
119  for (std::size_t i = 0; i < arraylen; i++)
120  val.add_child(global,vars.child(local,i));
121  }
122  ctx.set_var(global, val, pcfg["immediate"].to_bool());
123  }
124 }
126 {
127  bool valid = true;
128  if (!pcfg.has_attribute("from_global")) {
129  ERR_PERSIST << "[get_global_variable] missing required attribute \"from_global\"";
130  valid = false;
131  }
132  if (!pcfg.has_attribute("to_local")) {
133  ERR_PERSIST << "[get_global_variable] missing required attribute \"to_local\"";
134  valid = false;
135  }
136  // TODO: allow for global namespace.
137  if (!pcfg.has_attribute("namespace")) {
138  ERR_PERSIST << "[get_global_variable] missing attribute \"namespace\"";
139  valid = false;
140  }
141  if (resources::controller->is_networked_mp()) {
142  DBG_PERSIST << "verify_and_get_global_variable with from_global=" << pcfg["from_global"] << " from side " << pcfg["side"] << "\n";
143  config::attribute_value pcfg_side = pcfg["side"];
144  int side = (pcfg_side.str() == "global" || pcfg_side.empty()) ? resources::controller->current_side() : pcfg_side.to_int();
145  if (!resources::gameboard->has_team(side)) {
146  ERR_PERSIST << "[get_global_variable] attribute \"side\" specifies invalid side number." << "\n";
147  valid = false;
148  }
149  DBG_PERSIST << "end verify_and_get_global_variable with from_global=" << pcfg["from_global"] << " from side " << pcfg["side"] << "\n";
150  }
151  if (valid)
152  {
153  persist_context &ctx = resources::persist->get_context((pcfg["namespace"]));
154  if (ctx.valid()) {
155  get_global_variable(ctx,pcfg);
156  } else {
157  LOG_PERSIST << "Error: [get_global_variable] attribute \"namespace\" is not valid.";
158  }
159  }
160 }
162 {
163  bool valid = true;
164  if (!pcfg.has_attribute("to_global")) {
165  ERR_PERSIST << "[set_global_variable] missing required attribute \"to_global\"";
166  valid = false;
167  }
168  if (!pcfg.has_attribute("from_local")) {
169  LOG_PERSIST << "Warning: [set_global_variable] missing attribute \"from_local\", global variable will be cleared";
170  }
171  // TODO: allow for global namespace.
172  if (!pcfg.has_attribute("namespace")) {
173  ERR_PERSIST << "[set_global_variable] missing attribute \"namespace\" and no global namespace provided.";
174  valid = false;
175  }
176  if (resources::controller->is_networked_mp()) {
177  config::attribute_value pcfg_side = pcfg["side"];
178  int side = pcfg_side;
179  //Check side matching only if the side is not "global" or empty.
180  if (pcfg_side.str() != "global" && !pcfg_side.empty()) {
181  //Ensure that the side is valid.
182  if (!resources::gameboard->has_team(side)) {
183  ERR_PERSIST << "[set_global_variable] attribute \"side\" specifies invalid side number.";
184  valid = false;
185  } else if (resources::gameboard->get_team(side).is_empty()) {
186  LOG_PERSIST << "[set_global_variable] attribute \"side\" specifies a null-controlled side number.";
187  valid = false;
188  } else {
189  //Set the variable only if it is meant for a side we control
190  valid = resources::gameboard->get_team(side).is_local();
191  }
192  }
193  }
194  if (valid)
195  {
196  persist_context &ctx = resources::persist->get_context((pcfg["namespace"]));
197  if (ctx.valid()) {
198  set_global_variable(ctx,pcfg);
199  } else {
200  LOG_PERSIST << "Error: [set_global_variable] attribute \"namespace\" is not valid.";
201  }
202  }
203 }
205 {
206  bool valid = true;
207  if (!pcfg.has_attribute("global")) {
208  ERR_PERSIST << "[clear_global_variable] missing required attribute \"global\"";
209  valid = false;
210  }
211  if (!pcfg.has_attribute("namespace")) {
212  ERR_PERSIST << "[clear_global_variable] missing attribute \"namespace\" and no global namespace provided.";
213  valid = false;
214  }
215  if (resources::controller->is_networked_mp()) {
216  config::attribute_value pcfg_side = pcfg["side"];
217  const int side = pcfg_side.to_int();
218  //Check side matching only if the side is not "global" or empty.
219  if (pcfg_side.str() != "global" && !pcfg_side.empty()) {
220  //Ensure that the side is valid.
221  if (!resources::gameboard->has_team(side)) {
222  ERR_PERSIST << "[clear_global_variable] attribute \"side\" specifies invalid side number.";
223  valid = false;
224  } else if (resources::gameboard->get_team(side).is_empty()) {
225  LOG_PERSIST << "[clear_global_variable] attribute \"side\" specifies a null-controlled side number.";
226  valid = false;
227  } else {
228  //Clear the variable only if it is meant for a side we control
229  valid = resources::gameboard->get_team(side).is_local();
230  }
231  }
232  }
233  if (valid)
234  {
235  persist_context &ctx = resources::persist->get_context((pcfg["namespace"]));
236  if (ctx.valid()) {
237  clear_global_variable(ctx,pcfg);
238  } else {
239  LOG_PERSIST << "Error: [clear_global_variable] attribute \"namespace\" is not valid.";
240  }
241  }
242 }
void verify_and_get_global_variable(const vconfig &pcfg)
play_controller * controller
Definition: resources.cpp:22
#define DBG_PERSIST
Definition: persist_var.cpp:30
bool empty() const
Tests for an attribute that either was never set or was set to "".
config get_user_choice(const std::string &name, const user_choice &uch, int side=0)
static void get_global_variable(persist_context &ctx, const vconfig &pcfg)
Definition: persist_var.cpp:64
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:402
std::string var_name
Definition: persist_var.cpp:35
static void set_global_variable(persist_context &ctx, const vconfig &pcfg)
Definition: persist_var.cpp:99
Variant for storing WML attributes.
unsigned child_count(config_key_type key) const
Definition: config.cpp:372
bool has_attribute(const std::string &key) const
< Synonym for operator[]
Definition: variable.hpp:99
virtual config query_user(int) const
Definition: persist_var.cpp:42
persist_manager * persist
Definition: resources.cpp:27
void verify_and_set_global_variable(const vconfig &pcfg)
config pack_scalar(const std::string &name, const t_string &val)
void clear()
Definition: config.cpp:920
const config & get_variables() const
Definition: game_data.hpp:34
virtual bool set_var(const std::string &, const config &, bool immediate=false)=0
static std::string _(const char *str)
Definition: gettext.hpp:93
game_data * gamedata
Definition: resources.cpp:23
#define LOG_PERSIST
bool valid() const
team & get_team(int i)
Definition: game_board.hpp:97
virtual std::string description() const
Definition: persist_var.cpp:54
virtual config get_var(const std::string &) const =0
persist_choice(const persist_context &context, const std::string &name, int side_num)
Definition: persist_var.cpp:37
game_board * gameboard
Definition: resources.cpp:21
Interface for querying local choices.
#define ERR_PERSIST
Definition: persist_var.cpp:31
virtual config random_choice(int) const
Definition: persist_var.cpp:50
void set_variable(const std::string &varname, const t_string &value)
does nothing if varname is no valid variable name.
Definition: game_data.cpp:85
virtual bool clear_var(const std::string &, bool immediate=false)=0
const persist_context & ctx
Definition: persist_var.cpp:34
std::size_t i
Definition: function.cpp:967
persist_context & get_context(const std::string &ns)
bool is_local() const
Definition: team.hpp:273
config & add_child(config_key_type key)
Definition: config.cpp:514
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
Standard logging facilities (interface).
static void clear_global_variable(persist_context &ctx, const vconfig &pcfg)
Definition: persist_var.cpp:93
void clear_variable(const std::string &varname)
Clears attributes config children does nothing if varname is no valid variable name.
Definition: game_data.cpp:115
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
virtual bool is_visible() const
whether the choice is visible for the user like an advancement choice a non-visible choice is for exa...
Definition: persist_var.cpp:61
void verify_and_clear_global_variable(const vconfig &pcfg)
std::string str(const std::string &fallback="") const