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 
156  /** @return Whether we tracked something that can never be undone. */
157  static bool undo_blocked();
158 
159  static void set_last_unit_id(int id)
160  {
161  last_unit_id_ = id;
162  }
163 
164  static int get_unit_id_diff();
165 
167  {
168  public:
169  virtual ~server_choice()
170  {
171  }
172 
173  /** We are in a game with no mp server and need to do this choice locally. */
174  virtual config local_choice() const = 0;
175 
176  /** The request which is sent to the mp server. */
177  virtual config request() const = 0;
178 
179  virtual const char* name() const = 0;
180 
181  int request_id() const;
182  void send_request() const;
183  };
184 
185  /** If we are in a mp game, ask the server, otherwise generate the answer ourselves. */
186  static config ask_server_choice(const server_choice&);
187 
188  struct event_info {
190  std::optional<int> lua_;
192  event_info(const config& cmds, game_events::queued_event evt) : cmds_(cmds), evt_(evt) {}
193  event_info(int lua, game_events::queued_event evt) : lua_(lua), evt_(evt) {}
194  event_info(int lua, const config& args, game_events::queued_event evt) : cmds_(args), lua_(lua), evt_(evt) {}
195  };
196 
197  typedef std::deque<event_info> event_list;
199  {
200  return undo_commands_;
201  }
202 
203  static void add_undo_commands(const config& commands, const game_events::queued_event& ctx);
204  static void add_undo_commands(int fcn_idx, const game_events::queued_event& ctx);
205  static void add_undo_commands(int fcn_idx, const config& args, const game_events::queued_event& ctx);
206 
207  static void reset_undo_commands()
208  {
209  undo_commands_.clear();
210  }
211 
212 private:
213  /** Weather we are in a synced move, in a user_choice, or none of them. */
215 
216  /**
217  * As soon as get_user_choice is used with side != current_side (for example in generate_random_seed) other sides
218  * execute the command simultaneously and is_simultaneously is set to true. It's impossible to undo data that has
219  * been sent over the network.
220  *
221  * false = we are on a local turn and haven't sent anything yet.
222  *
223  * TODO: it would be better if the following variable were not static.
224  */
225  static inline bool is_simultaneous_ = false;
226 
227  /** Used to restore the unit id manager when undoing. */
228  static inline int last_unit_id_ = 0;
229 
230  /** Actions to be executed when the current action is undone. */
231  static inline event_list undo_commands_ {};
232 };
233 
235 {
236 public:
239 
240 protected:
241  std::shared_ptr<randomness::rng> new_rng_;
243 };
244 
245 /** A RAII object to enter the synced context, cannot be called if we are already in a synced context. */
247 {
248 public:
250 
251  /** Use this constructor if you have multiple synced_context but only one replay entry. */
252  set_scontext_synced(int num);
253 
255 
256  int get_random_calls();
257  void do_final_checkup(bool dont_throw = false);
258 
259 private:
260  // only called by constructors.
261  void init();
262  static checkup* generate_checkup(const std::string& tagname);
264  const std::unique_ptr<checkup> new_checkup_;
267 };
268 
269 /**
270  * A RAII object to temporary leave the synced context like in wesnoth.synchronize_choice.
271  * Can only be used from inside a synced context.
272  */
274 {
275 public:
278 
279 private:
281 };
282 
283 /**
284  * An object to leave the synced context during draw or unsynced wml items when we don’t know whether we are in a
285  * synced context or not. if we are in a synced context we leave the synced context, otherwise it has no effect. we need
286  * This because we might call lua's wesnoth.interface.game_display during draw and we don’t want to have that an effect
287  * on the gamestate in this case.
288  */
290 {
291 public:
293 
294 private:
295  const std::unique_ptr<leave_synced_context> leaver_;
296 };
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 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 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()
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)