The Battle for Wesnoth  1.19.0-dev
persist_var.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2024
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 
28 //TODO: remove LOG_PERSIST, ERR_PERSIST from persist_context.hpp to .cpp files.
29 #define DBG_PERSIST LOG_STREAM(debug, log_persist)
30 #define ERR_PERSIST LOG_STREAM(err, log_persist)
31 
34  std::string var_name;
35  int side;
36  persist_choice(const persist_context &context,const std::string &name, int side_num)
37  : ctx(context)
38  , var_name(name)
39  , side(side_num) {
40  }
41  virtual config query_user(int /*side_for*/) const {
42  //side can be different from side_for: if side was null-controlled
43  //then get_user_choice will use the next non-null-controlled side instead
44  config ret;
45  ret["side"] = side;
46  ret.add_child("variables",ctx.get_var(var_name));
47  return ret;
48  }
49  virtual config random_choice(int /*side_for*/) const {
50  return config();
51  }
52 
53  virtual std::string description() const
54  {
55  // TRANSLATORS: In networked games, this text is shown for other
56  // clients, while they wait to receive the content of a global variable
57  // from another player. This text will be embedded into a sentence.
58  return _("waiting for^a global variable");
59  }
60  virtual bool is_visible() const { return false; }
61 };
62 
63 static void get_global_variable(persist_context &ctx, const vconfig &pcfg)
64 {
65  std::string global = pcfg["from_global"];
66  std::string local = pcfg["to_local"];
67  config::attribute_value pcfg_side = pcfg["side"];
68  const int side = pcfg_side.to_int(resources::controller->current_side());
69  persist_choice choice(ctx, global, side);
70  config cfg = mp_sync::get_user_choice("global_variable",choice,side).mandatory_child("variables");
71  try
72  {
73  std::size_t arrsize = cfg.child_count(global);
74  if (arrsize == 0) {
75  resources::gamedata->set_variable(local,cfg[global]);
76  } else {
78  for (std::size_t i = 0; i < arrsize; i++)
79  resources::gamedata->add_variable_cfg(local, cfg.mandatory_child(global,i));
80  }
81  }
82  catch(const invalid_variablename_exception&)
83  {
84  ERR_PERSIST << "cannot store global variable into invalid variablename " << local;
85  }
86 }
87 
88 static void clear_global_variable(persist_context &ctx, const vconfig &pcfg)
89 {
90  std::string global = pcfg["global"];
91  ctx.clear_var(global, pcfg["immediate"].to_bool());
92 }
93 
94 static void set_global_variable(persist_context &ctx, const vconfig &pcfg)
95 {
96  if (pcfg["from_local"].empty()) {
97  clear_global_variable(ctx, pcfg);
98  } else {
99  std::string global = pcfg["to_global"];
100  std::string local = pcfg["from_local"];
101  config val;
102  const config &vars = resources::gamedata->get_variables();
103  std::size_t arraylen = vars.child_count(local);
104  if (arraylen == 0) {
105  try
106  {
107  val = pack_scalar(global,resources::gamedata->get_variable(local));
108  }
109  catch(const invalid_variablename_exception&)
110  {
111  val.clear();
112  }
113  } else {
114  for (std::size_t i = 0; i < arraylen; i++)
115  val.add_child(global, vars.mandatory_child(local,i));
116  }
117  ctx.set_var(global, val, pcfg["immediate"].to_bool());
118  }
119 }
121 {
122  bool valid = true;
123  if (!pcfg.has_attribute("from_global")) {
124  ERR_PERSIST << "[get_global_variable] missing required attribute \"from_global\"";
125  valid = false;
126  }
127  if (!pcfg.has_attribute("to_local")) {
128  ERR_PERSIST << "[get_global_variable] missing required attribute \"to_local\"";
129  valid = false;
130  }
131  // TODO: allow for global namespace.
132  if (!pcfg.has_attribute("namespace")) {
133  ERR_PERSIST << "[get_global_variable] missing attribute \"namespace\"";
134  valid = false;
135  }
136  if (resources::controller->is_networked_mp()) {
137  DBG_PERSIST << "verify_and_get_global_variable with from_global=" << pcfg["from_global"] << " from side " << pcfg["side"];
138  config::attribute_value pcfg_side = pcfg["side"];
139  int side = (pcfg_side.str() == "global" || pcfg_side.empty()) ? resources::controller->current_side() : pcfg_side.to_int();
140  if (!resources::gameboard->has_team(side)) {
141  ERR_PERSIST << "[get_global_variable] attribute \"side\" specifies invalid side number.";
142  valid = false;
143  }
144  DBG_PERSIST << "end verify_and_get_global_variable with from_global=" << pcfg["from_global"] << " from side " << pcfg["side"];
145  }
146  if (valid)
147  {
148  persist_context &ctx = resources::persist->get_context((pcfg["namespace"]));
149  if (ctx.valid()) {
150  get_global_variable(ctx,pcfg);
151  } else {
152  LOG_PERSIST << "Error: [get_global_variable] attribute \"namespace\" is not valid.";
153  }
154  }
155 }
157 {
158  bool valid = true;
159  if (!pcfg.has_attribute("to_global")) {
160  ERR_PERSIST << "[set_global_variable] missing required attribute \"to_global\"";
161  valid = false;
162  }
163  if (!pcfg.has_attribute("from_local")) {
164  LOG_PERSIST << "Warning: [set_global_variable] missing attribute \"from_local\", global variable will be cleared";
165  }
166  // TODO: allow for global namespace.
167  if (!pcfg.has_attribute("namespace")) {
168  ERR_PERSIST << "[set_global_variable] missing attribute \"namespace\" and no global namespace provided.";
169  valid = false;
170  }
171  if (resources::controller->is_networked_mp()) {
172  config::attribute_value pcfg_side = pcfg["side"];
173  int side = pcfg_side;
174  //Check side matching only if the side is not "global" or empty.
175  if (pcfg_side.str() != "global" && !pcfg_side.empty()) {
176  //Ensure that the side is valid.
177  if (!resources::gameboard->has_team(side)) {
178  ERR_PERSIST << "[set_global_variable] attribute \"side\" specifies invalid side number.";
179  valid = false;
180  } else if (resources::gameboard->get_team(side).is_empty()) {
181  LOG_PERSIST << "[set_global_variable] attribute \"side\" specifies a null-controlled side number.";
182  valid = false;
183  } else {
184  //Set the variable only if it is meant for a side we control
185  valid = resources::gameboard->get_team(side).is_local();
186  }
187  }
188  }
189  if (valid)
190  {
191  persist_context &ctx = resources::persist->get_context((pcfg["namespace"]));
192  if (ctx.valid()) {
193  set_global_variable(ctx,pcfg);
194  } else {
195  LOG_PERSIST << "Error: [set_global_variable] attribute \"namespace\" is not valid.";
196  }
197  }
198 }
200 {
201  bool valid = true;
202  if (!pcfg.has_attribute("global")) {
203  ERR_PERSIST << "[clear_global_variable] missing required attribute \"global\"";
204  valid = false;
205  }
206  if (!pcfg.has_attribute("namespace")) {
207  ERR_PERSIST << "[clear_global_variable] missing attribute \"namespace\" and no global namespace provided.";
208  valid = false;
209  }
210  if (resources::controller->is_networked_mp()) {
211  config::attribute_value pcfg_side = pcfg["side"];
212  const int side = pcfg_side.to_int();
213  //Check side matching only if the side is not "global" or empty.
214  if (pcfg_side.str() != "global" && !pcfg_side.empty()) {
215  //Ensure that the side is valid.
216  if (!resources::gameboard->has_team(side)) {
217  ERR_PERSIST << "[clear_global_variable] attribute \"side\" specifies invalid side number.";
218  valid = false;
219  } else if (resources::gameboard->get_team(side).is_empty()) {
220  LOG_PERSIST << "[clear_global_variable] attribute \"side\" specifies a null-controlled side number.";
221  valid = false;
222  } else {
223  //Clear the variable only if it is meant for a side we control
224  valid = resources::gameboard->get_team(side).is_local();
225  }
226  }
227  }
228  if (valid)
229  {
230  persist_context &ctx = resources::persist->get_context((pcfg["namespace"]));
231  if (ctx.valid()) {
232  clear_global_variable(ctx,pcfg);
233  } else {
234  LOG_PERSIST << "Error: [clear_global_variable] attribute \"namespace\" is not valid.";
235  }
236  }
237 }
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:159
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:367
std::size_t child_count(config_key_type key) const
Definition: config.cpp:297
void clear()
Definition: config.cpp:831
config & add_child(config_key_type key)
Definition: config.cpp:441
team & get_team(int i)
Definition: game_board.hpp:91
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:33
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:247
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:20
persist_manager * persist
Definition: resources.cpp:26
game_data * gamedata
Definition: resources.cpp:22
play_controller * controller
Definition: resources.cpp:21
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:88
static void set_global_variable(persist_context &ctx, const vconfig &pcfg)
Definition: persist_var.cpp:94
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:30
static void get_global_variable(persist_context &ctx, const vconfig &pcfg)
Definition: persist_var.cpp:63
#define DBG_PERSIST
Definition: persist_var.cpp:29
void verify_and_get_global_variable(const vconfig &pcfg)
Interface for querying local choices.
virtual config random_choice(int) const
Definition: persist_var.cpp:49
const persist_context & ctx
Definition: persist_var.cpp:33
persist_choice(const persist_context &context, const std::string &name, int side_num)
Definition: persist_var.cpp:36
virtual std::string description() const
Definition: persist_var.cpp:53
std::string var_name
Definition: persist_var.cpp:34
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:60
virtual config query_user(int) const
Definition: persist_var.cpp:41