The Battle for Wesnoth  1.17.23+dev
persist_var.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2023
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).mandatory_child("variables");
72  try
73  {
74  std::size_t arrsize = cfg.child_count(global);
75  if (arrsize == 0) {
76  resources::gamedata->set_variable(local,cfg[global]);
77  } else {
79  for (std::size_t i = 0; i < arrsize; i++)
80  resources::gamedata->add_variable_cfg(local, cfg.mandatory_child(global,i));
81  }
82  }
83  catch(const invalid_variablename_exception&)
84  {
85  ERR_PERSIST << "cannot store global variable into invalid variablename " << local;
86  }
87 }
88 
89 static void clear_global_variable(persist_context &ctx, const vconfig &pcfg)
90 {
91  std::string global = pcfg["global"];
92  ctx.clear_var(global, pcfg["immediate"].to_bool());
93 }
94 
95 static void set_global_variable(persist_context &ctx, const vconfig &pcfg)
96 {
97  if (pcfg["from_local"].empty()) {
98  clear_global_variable(ctx, pcfg);
99  } else {
100  std::string global = pcfg["to_global"];
101  std::string local = pcfg["from_local"];
102  config val;
103  const config &vars = resources::gamedata->get_variables();
104  std::size_t arraylen = vars.child_count(local);
105  if (arraylen == 0) {
106  try
107  {
108  val = pack_scalar(global,resources::gamedata->get_variable(local));
109  }
110  catch(const invalid_variablename_exception&)
111  {
112  val.clear();
113  }
114  } else {
115  for (std::size_t i = 0; i < arraylen; i++)
116  val.add_child(global, vars.mandatory_child(local,i));
117  }
118  ctx.set_var(global, val, pcfg["immediate"].to_bool());
119  }
120 }
122 {
123  bool valid = true;
124  if (!pcfg.has_attribute("from_global")) {
125  ERR_PERSIST << "[get_global_variable] missing required attribute \"from_global\"";
126  valid = false;
127  }
128  if (!pcfg.has_attribute("to_local")) {
129  ERR_PERSIST << "[get_global_variable] missing required attribute \"to_local\"";
130  valid = false;
131  }
132  // TODO: allow for global namespace.
133  if (!pcfg.has_attribute("namespace")) {
134  ERR_PERSIST << "[get_global_variable] missing attribute \"namespace\"";
135  valid = false;
136  }
137  if (resources::controller->is_networked_mp()) {
138  DBG_PERSIST << "verify_and_get_global_variable with from_global=" << pcfg["from_global"] << " from side " << pcfg["side"];
139  config::attribute_value pcfg_side = pcfg["side"];
140  int side = (pcfg_side.str() == "global" || pcfg_side.empty()) ? resources::controller->current_side() : pcfg_side.to_int();
141  if (!resources::gameboard->has_team(side)) {
142  ERR_PERSIST << "[get_global_variable] attribute \"side\" specifies invalid side number.";
143  valid = false;
144  }
145  DBG_PERSIST << "end verify_and_get_global_variable with from_global=" << pcfg["from_global"] << " from side " << pcfg["side"];
146  }
147  if (valid)
148  {
149  persist_context &ctx = resources::persist->get_context((pcfg["namespace"]));
150  if (ctx.valid()) {
151  get_global_variable(ctx,pcfg);
152  } else {
153  LOG_PERSIST << "Error: [get_global_variable] attribute \"namespace\" is not valid.";
154  }
155  }
156 }
158 {
159  bool valid = true;
160  if (!pcfg.has_attribute("to_global")) {
161  ERR_PERSIST << "[set_global_variable] missing required attribute \"to_global\"";
162  valid = false;
163  }
164  if (!pcfg.has_attribute("from_local")) {
165  LOG_PERSIST << "Warning: [set_global_variable] missing attribute \"from_local\", global variable will be cleared";
166  }
167  // TODO: allow for global namespace.
168  if (!pcfg.has_attribute("namespace")) {
169  ERR_PERSIST << "[set_global_variable] missing attribute \"namespace\" and no global namespace provided.";
170  valid = false;
171  }
172  if (resources::controller->is_networked_mp()) {
173  config::attribute_value pcfg_side = pcfg["side"];
174  int side = pcfg_side;
175  //Check side matching only if the side is not "global" or empty.
176  if (pcfg_side.str() != "global" && !pcfg_side.empty()) {
177  //Ensure that the side is valid.
178  if (!resources::gameboard->has_team(side)) {
179  ERR_PERSIST << "[set_global_variable] attribute \"side\" specifies invalid side number.";
180  valid = false;
181  } else if (resources::gameboard->get_team(side).is_empty()) {
182  LOG_PERSIST << "[set_global_variable] attribute \"side\" specifies a null-controlled side number.";
183  valid = false;
184  } else {
185  //Set the variable only if it is meant for a side we control
186  valid = resources::gameboard->get_team(side).is_local();
187  }
188  }
189  }
190  if (valid)
191  {
192  persist_context &ctx = resources::persist->get_context((pcfg["namespace"]));
193  if (ctx.valid()) {
194  set_global_variable(ctx,pcfg);
195  } else {
196  LOG_PERSIST << "Error: [set_global_variable] attribute \"namespace\" is not valid.";
197  }
198  }
199 }
201 {
202  bool valid = true;
203  if (!pcfg.has_attribute("global")) {
204  ERR_PERSIST << "[clear_global_variable] missing required attribute \"global\"";
205  valid = false;
206  }
207  if (!pcfg.has_attribute("namespace")) {
208  ERR_PERSIST << "[clear_global_variable] missing attribute \"namespace\" and no global namespace provided.";
209  valid = false;
210  }
211  if (resources::controller->is_networked_mp()) {
212  config::attribute_value pcfg_side = pcfg["side"];
213  const int side = pcfg_side.to_int();
214  //Check side matching only if the side is not "global" or empty.
215  if (pcfg_side.str() != "global" && !pcfg_side.empty()) {
216  //Ensure that the side is valid.
217  if (!resources::gameboard->has_team(side)) {
218  ERR_PERSIST << "[clear_global_variable] attribute \"side\" specifies invalid side number.";
219  valid = false;
220  } else if (resources::gameboard->get_team(side).is_empty()) {
221  LOG_PERSIST << "[clear_global_variable] attribute \"side\" specifies a null-controlled side number.";
222  valid = false;
223  } else {
224  //Clear the variable only if it is meant for a side we control
225  valid = resources::gameboard->get_team(side).is_local();
226  }
227  }
228  }
229  if (valid)
230  {
231  persist_context &ctx = resources::persist->get_context((pcfg["namespace"]));
232  if (ctx.valid()) {
233  clear_global_variable(ctx,pcfg);
234  } else {
235  LOG_PERSIST << "Error: [clear_global_variable] attribute \"namespace\" is not valid.";
236  }
237  }
238 }
Variant for storing WML attributes.
std::string str(const std::string &fallback="") const
bool empty() const
Tests for an attribute that either was never set or was set to "".
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:371
std::size_t child_count(config_key_type key) const
Definition: config.cpp:301
void clear()
Definition: config.cpp:835
config & add_child(config_key_type key)
Definition: config.cpp:445
team & get_team(int i)
Definition: game_board.hpp:98
void clear_variable(const std::string &varname)
Clears attributes config children does nothing if varname is no valid variable name.
Definition: game_data.cpp:118
const config & get_variables() const
Definition: game_data.hpp:34
void set_variable(const std::string &varname, const t_string &value)
does nothing if varname is no valid variable name.
Definition: game_data.cpp:88
virtual config get_var(const std::string &) const =0
bool valid() const
virtual bool set_var(const std::string &, const config &, bool immediate=false)=0
virtual bool clear_var(const std::string &, bool immediate=false)=0
persist_context & get_context(const std::string &ns)
bool is_local() const
Definition: team.hpp:249
A variable-expanding proxy for the config class.
Definition: variable.hpp:45
bool has_attribute(const std::string &key) const
< Synonym for operator[]
Definition: variable.hpp:99
std::size_t i
Definition: function.cpp:968
static std::string _(const char *str)
Definition: gettext.hpp:93
Standard logging facilities (interface).
config get_user_choice(const std::string &name, const user_choice &uch, int side=0)
game_board * gameboard
Definition: resources.cpp:21
persist_manager * persist
Definition: resources.cpp:27
game_data * gamedata
Definition: resources.cpp:23
play_controller * controller
Definition: resources.cpp:22
config pack_scalar(const std::string &name, const t_string &val)
#define LOG_PERSIST
static void clear_global_variable(persist_context &ctx, const vconfig &pcfg)
Definition: persist_var.cpp:89
static void set_global_variable(persist_context &ctx, const vconfig &pcfg)
Definition: persist_var.cpp:95
void verify_and_set_global_variable(const vconfig &pcfg)
void verify_and_clear_global_variable(const vconfig &pcfg)
#define ERR_PERSIST
Definition: persist_var.cpp:31
static void get_global_variable(persist_context &ctx, const vconfig &pcfg)
Definition: persist_var.cpp:64
#define DBG_PERSIST
Definition: persist_var.cpp:30
void verify_and_get_global_variable(const vconfig &pcfg)
Interface for querying local choices.
virtual config random_choice(int) const
Definition: persist_var.cpp:50
const persist_context & ctx
Definition: persist_var.cpp:34
persist_choice(const persist_context &context, const std::string &name, int side_num)
Definition: persist_var.cpp:37
virtual std::string description() const
Definition: persist_var.cpp:54
std::string var_name
Definition: persist_var.cpp:35
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
virtual config query_user(int) const
Definition: persist_var.cpp:42