The Battle for Wesnoth  1.19.0-dev
synced_context.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2024
3  by David White <dave@whitevine.net>
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 #pragma once
17 
18 #include "game_events/pump.hpp" // for queued_event
19 #include "mouse_handler_base.hpp"
20 #include "random.hpp"
21 #include "synced_checkup.hpp"
22 #include "synced_commands.hpp"
23 
24 #include <deque>
25 
26 
27 // only static methods.
29 {
30 public:
32 
33  /**
34  * Sets the context to 'synced', initialises random context, and calls the given function.
35  * The plan is that in replays and in real game the same function is called.
36  * However, if you cannot call this function you can also use set_scontext_synced directly
37  * (use it like it's used in this method).
38  *
39  * Movement commands are currently treated specially because actions::move_unit returns a
40  * value and some function use that value. Maybe I should add a way to return a value here.
41  *
42  * AI attacks are also treated special because the ai wants to pass advancement_aspects.
43  *
44  * Redoing does normally not take place in a synced context, because we saved the dependent=true
45  * replay commands in the replay stack data. There are also no events of similar fired when
46  * redoing an action (in most cases).
47  *
48  * @param commandname The command to run.
49  * @param data The data to use with the command.
50  * @param use_undo This parameter is used to ignore undos during an ai move to optimize.
51  * @param show
52  * @param error_handler An error handler for the case that data contains invalid data.
53  *
54  * @return True if the action was successful.
55  */
56  static bool run(const std::string& commandname,
57  const config& data,
58  bool use_undo = true,
59  bool show = true,
61 
62  static bool run_and_store(const std::string& commandname,
63  const config& data,
64  bool use_undo = true,
65  bool show = true,
67 
68  static bool run_and_throw(const std::string& commandname,
69  const config& data,
70  bool use_undo = true,
71  bool show = true,
73 
74  /**
75  * Checks whether we are currently running in a synced context, and if not we enters it.
76  * This is never called from so_replay_handle.
77  */
78  static bool run_in_synced_context_if_not_already(const std::string& commandname,
79  const config& data,
80  bool use_undo = true,
81  bool show = true,
83 
84  /**
85  * @return Whether we are currently executing a synced action like recruit, start, recall, disband, movement,
86  * attack, init_side, end_turn, fire_event, lua_ai, auto_shroud or similar.
87  */
89  {
90  return state_;
91  }
92 
93  /**
94  * @return Whether we are currently executing a synced action like recruit, start, recall, disband, movement,
95  * attack, init_side, end_turn, fire_event, lua_ai, auto_shroud or similar.
96  */
97  static bool is_synced()
98  {
99  return get_synced_state() == SYNCED;
100  }
101 
102  /**
103  * @return Whether we are not currently executing a synced action like recruit, start, recall, disband, movement,
104  * attack, init_side, end_turn, fire_event, lua_ai, auto_shroud or similar. and not doing a local choice.
105  */
106  static bool is_unsynced()
107  {
108  return get_synced_state() == UNSYNCED;
109  }
110 
111  /** Should only be called form set_scontext_synced, set_scontext_local_choice */
112  static void set_synced_state(synced_state newstate)
113  {
114  state_ = newstate;
115  }
116 
117  /** Generates a new seed for a synced event, by asking the 'server' */
118  static std::string generate_random_seed();
119 
120  /** called from get_user_choice while waiting for a remove user choice. */
121  static void pull_remote_user_input();
122 
123  /**
124  * called from get_user_choice to send a recently made choice to the other clients.
125  * Does not receive any data from the network any sends data.
126  */
127  static void send_user_choice();
128 
129  /** A function to be passed to run_in_synced_context to assert false on error (the default). */
130  static void default_error_function(const std::string& message);
131 
132  /** A function to be passed to run_in_synced_context to log the error. */
133  static void just_log_error_function(const std::string& message);
134 
135  /** A function to be passed to run_in_synced_context to ignore the error. */
136  static void ignore_error_function(const std::string& message);
137 
138  /** @return A rng_deterministic if in determinsic mode otherwise a rng_synced. */
139  static std::shared_ptr<randomness::rng> get_rng_for_action();
140 
141  /** @return whether we needed data from other clients about the action, in this case we need to send data about the current action to other clients. which means we cannot undo it. */
142  static bool is_simultaneous()
143  {
144  return is_simultaneous_;
145  }
146 
147  /** Sets is_simultaneous_ = false, called when entering the synced context. */
148  static void reset_is_simultaneous()
149  {
150  is_simultaneous_ = false;
151  }
152 
153  /** Sets is_simultaneous_ = true, called using a user choice that is not the currently playing side. */
154  static void set_is_simultaneous();
155  static void block_undo(bool do_block = true);
156  static void reset_block_undo()
157  {
158  is_undo_blocked_ = false;
159  }
160 
161  /** @return Whether we tracked something that can never be undone. */
162  static bool undo_blocked();
163 
164  static void set_last_unit_id(int id)
165  {
166  last_unit_id_ = id;
167  }
168 
169  static int get_unit_id_diff();
170 
172  {
173  public:
174  virtual ~server_choice()
175  {
176  }
177 
178  /** We are in a game with no mp server and need to do this choice locally. */
179  virtual config local_choice() const = 0;
180 
181  /** The request which is sent to the mp server. */
182  virtual config request() const = 0;
183 
184  virtual const char* name() const = 0;
185 
186  int request_id() const;
187  void send_request() const;
188  };
189 
190  /** If we are in a mp game, ask the server, otherwise generate the answer ourselves. */
191  static config ask_server_choice(const server_choice&);
192 
193  struct event_info {
195  std::optional<int> lua_;
197  event_info(const config& cmds, game_events::queued_event evt) : cmds_(cmds), evt_(evt) {}
198  event_info(int lua, game_events::queued_event evt) : lua_(lua), evt_(evt) {}
199  event_info(int lua, const config& args, game_events::queued_event evt) : cmds_(args), lua_(lua), evt_(evt) {}
200  };
201 
202  typedef std::deque<event_info> event_list;
204  {
205  return undo_commands_;
206  }
207 
208  static void add_undo_commands(const config& commands, const game_events::queued_event& ctx);
209  static void add_undo_commands(int fcn_idx, const game_events::queued_event& ctx);
210  static void add_undo_commands(int fcn_idx, const config& args, const game_events::queued_event& ctx);
211 
212  static void reset_undo_commands()
213  {
214  undo_commands_.clear();
215  }
216 
217  static bool ignore_undo();
218 private:
219  /** Weather we are in a synced move, in a user_choice, or none of them. */
221 
222  /**
223  * As soon as get_user_choice is used with side != current_side (for example in generate_random_seed) other sides
224  * execute the command simultaneously and is_simultaneously is set to true. It's impossible to undo data that has
225  * been sent over the network.
226  *
227  * false = we are on a local turn and haven't sent anything yet.
228  *
229  * TODO: it would be better if the following variable were not static.
230  */
231  static inline bool is_simultaneous_ = false;
232  static inline bool is_undo_blocked_ = false;
233 
234  /** Used to restore the unit id manager when undoing. */
235  static inline int last_unit_id_ = 0;
236 
237  /** Actions to be executed when the current action is undone. */
238  static inline event_list undo_commands_ {};
239 };
240 
242 {
243 public:
246 
247 protected:
248  std::shared_ptr<randomness::rng> new_rng_;
250 };
251 
252 /** A RAII object to enter the synced context, cannot be called if we are already in a synced context. */
254 {
255 public:
257 
258  /** Use this constructor if you have multiple synced_context but only one replay entry. */
259  set_scontext_synced(int num);
260 
262 
263  int get_random_calls();
264  void do_final_checkup(bool dont_throw = false);
265 
266 private:
267  // only called by constructors.
268  void init();
269  static checkup* generate_checkup(const std::string& tagname);
271  const std::unique_ptr<checkup> new_checkup_;
274 };
275 
276 /**
277  * A RAII object to temporary leave the synced context like in wesnoth.synchronize_choice.
278  * Can only be used from inside a synced context.
279  */
281 {
282 public:
285 
286 private:
288 };
289 
290 /**
291  * An object to leave the synced context during draw or unsynced wml items when we don’t know whether we are in a
292  * synced context or not. if we are in a synced context we leave the synced context, otherwise it has no effect. we need
293  * This because we might call lua's wesnoth.interface.game_display during draw and we don’t want to have that an effect
294  * on the gamestate in this case.
295  */
297 {
298 public:
300 
301 private:
302  const std::unique_ptr<leave_synced_context> leaver_;
303 };
A class to check whether the results that were calculated in the replay match the results calculated ...
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
A RAII object to temporary leave the synced context like in wesnoth.synchronize_choice.
randomness::rng * old_rng_
this class does not give synced random results derived classes might do.
Definition: random.hpp:28
std::shared_ptr< randomness::rng > new_rng_
randomness::rng * old_rng_
A RAII object to enter the synced context, cannot be called if we are already in a synced context.
void do_final_checkup(bool dont_throw=false)
static checkup * generate_checkup(const std::string &tagname)
events::command_disabler disabler_
const std::unique_ptr< checkup > new_checkup_
An object to leave the synced context during draw or unsynced wml items when we don’t know whether we...
const std::unique_ptr< leave_synced_context > leaver_
std::function< void(const std::string &)> error_handler_function
virtual config request() const =0
The request which is sent to the mp server.
virtual const char * name() const =0
virtual config local_choice() const =0
We are in a game with no mp server and need to do this choice locally.
static void set_last_unit_id(int id)
static bool undo_blocked()
static std::shared_ptr< randomness::rng > get_rng_for_action()
static config ask_server_choice(const server_choice &)
If we are in a mp game, ask the server, otherwise generate the answer ourselves.
static bool is_undo_blocked_
static void reset_is_simultaneous()
Sets is_simultaneous_ = false, called when entering the synced context.
static void add_undo_commands(const config &commands, const game_events::queued_event &ctx)
static event_list undo_commands_
Actions to be executed when the current action is undone.
static void reset_undo_commands()
static void ignore_error_function(const std::string &message)
A function to be passed to run_in_synced_context to ignore the error.
static std::string generate_random_seed()
Generates a new seed for a synced event, by asking the 'server'.
static bool run_and_throw(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
static synced_state state_
Weather we are in a synced move, in a user_choice, or none of them.
static int get_unit_id_diff()
static void default_error_function(const std::string &message)
A function to be passed to run_in_synced_context to assert false on error (the default).
static void just_log_error_function(const std::string &message)
A function to be passed to run_in_synced_context to log the error.
static synced_state get_synced_state()
static void pull_remote_user_input()
called from get_user_choice while waiting for a remove user choice.
static void reset_block_undo()
static bool run(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
Sets the context to 'synced', initialises random context, and calls the given function.
static bool is_simultaneous_
As soon as get_user_choice is used with side != current_side (for example in generate_random_seed) ot...
static bool run_and_store(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
static bool is_unsynced()
static int last_unit_id_
Used to restore the unit id manager when undoing.
std::deque< event_info > event_list
static bool is_synced()
static event_list & get_undo_commands()
static void set_synced_state(synced_state newstate)
Should only be called form set_scontext_synced, set_scontext_local_choice.
static void send_user_choice()
called from get_user_choice to send a recently made choice to the other clients.
static bool run_in_synced_context_if_not_already(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
Checks whether we are currently running in a synced context, and if not we enters it.
static void set_is_simultaneous()
Sets is_simultaneous_ = true, called using a user choice that is not the currently playing side.
static bool is_simultaneous()
static void block_undo(bool do_block=true)
static bool ignore_undo()
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:207
void show(const std::string &window_id, const t_string &message, const point &mouse, const SDL_Rect &source_rect)
Shows a tip.
Definition: tooltip.cpp:79
std::string_view data
Definition: picture.cpp:194
Define the game's event mechanism.
event_info(int lua, game_events::queued_event evt)
event_info(int lua, const config &args, game_events::queued_event evt)
std::optional< int > lua_
game_events::queued_event evt_
event_info(const config &cmds, game_events::queued_event evt)