The Battle for Wesnoth  1.17.23+dev
synced_context.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2023
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 "generic_event.hpp"
20 #include "mouse_handler_base.hpp"
21 #include "random.hpp"
22 #include "random_synced.hpp"
23 #include "replay.hpp"
24 #include "synced_checkup.hpp"
25 #include "synced_commands.hpp"
26 
27 #include <deque>
28 #include <functional>
29 
30 class config;
31 
32 // only static methods.
34 {
35 public:
37 
38  /**
39  * Sets the context to 'synced', initialises random context, and calls the given function.
40  * The plan is that in replays and in real game the same function is called.
41  * However, if you cannot call this function you can also use set_scontext_synced directly
42  * (use it like it's used in this method).
43  *
44  * Movement commands are currently treated specially because actions::move_unit returns a
45  * value and some function use that value. Maybe I should add a way to return a value here.
46  *
47  * AI attacks are also treated special because the ai wants to pass advancement_aspects.
48  *
49  * Redoing does normally not take place in a synced context, because we saved the dependent=true
50  * replay commands in the replay stack data. There are also no events of similar fired when
51  * redoing an action (in most cases).
52  *
53  * @param commandname The command to run.
54  * @param data The data to use with the command.
55  * @param use_undo This parameter is used to ignore undos during an ai move to optimize.
56  * @param show
57  * @param error_handler An error handler for the case that data contains invalid data.
58  *
59  * @return True if the action was successful.
60  */
61  static bool run(const std::string& commandname,
62  const config& data,
63  bool use_undo = true,
64  bool show = true,
66 
67  static bool run_and_store(const std::string& commandname,
68  const config& data,
69  bool use_undo = true,
70  bool show = true,
72 
73  static bool run_and_throw(const std::string& commandname,
74  const config& data,
75  bool use_undo = true,
76  bool show = true,
78 
79  /**
80  * Checks whether we are currently running in a synced context, and if not we enters it.
81  * This is never called from so_replay_handle.
82  */
83  static bool run_in_synced_context_if_not_already(const std::string& commandname,
84  const config& data,
85  bool use_undo = true,
86  bool show = true,
88 
89  /**
90  * @return Whether we are currently executing a synced action like recruit, start, recall, disband, movement,
91  * attack, init_side, end_turn, fire_event, lua_ai, auto_shroud or similar.
92  */
94  {
95  return state_;
96  }
97 
98  /**
99  * @return Whether we are currently executing a synced action like recruit, start, recall, disband, movement,
100  * attack, init_side, end_turn, fire_event, lua_ai, auto_shroud or similar.
101  */
102  static bool is_synced()
103  {
104  return get_synced_state() == SYNCED;
105  }
106 
107  /**
108  * @return Whether we are not currently executing a synced action like recruit, start, recall, disband, movement,
109  * attack, init_side, end_turn, fire_event, lua_ai, auto_shroud or similar. and not doing a local choice.
110  */
111  static bool is_unsynced()
112  {
113  return get_synced_state() == UNSYNCED;
114  }
115 
116  /** Should only be called form set_scontext_synced, set_scontext_local_choice */
117  static void set_synced_state(synced_state newstate)
118  {
119  state_ = newstate;
120  }
121 
122  /** Generates a new seed for a synced event, by asking the 'server' */
123  static std::string generate_random_seed();
124 
125  /** called from get_user_choice while waiting for a remove user choice. */
126  static void pull_remote_user_input();
127 
128  /**
129  * called from get_user_choice to send a recently made choice to the other clients.
130  * Does not receive any data from the network any sends data.
131  */
132  static void send_user_choice();
133 
134  /** A function to be passed to run_in_synced_context to assert false on error (the default). */
135  static void default_error_function(const std::string& message);
136 
137  /** A function to be passed to run_in_synced_context to log the error. */
138  static void just_log_error_function(const std::string& message);
139 
140  /** A function to be passed to run_in_synced_context to ignore the error. */
141  static void ignore_error_function(const std::string& message);
142 
143  /** @return A rng_deterministic if in determinsic mode otherwise a rng_synced. */
144  static std::shared_ptr<randomness::rng> get_rng_for_action();
145 
146  /** @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. */
147  static bool is_simultaneous()
148  {
149  return is_simultaneous_;
150  }
151 
152  /** Sets is_simultaneous_ = false, called when entering the synced context. */
153  static void reset_is_simultaneous()
154  {
155  is_simultaneous_ = false;
156  }
157 
158  /** Sets is_simultaneous_ = true, called using a user choice that is not the currently playing side. */
159  static void set_is_simultaneous();
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 private:
218  /** Weather we are in a synced move, in a user_choice, or none of them. */
220 
221  /**
222  * As soon as get_user_choice is used with side != current_side (for example in generate_random_seed) other sides
223  * execute the command simultaneously and is_simultaneously is set to true. It's impossible to undo data that has
224  * been sent over the network.
225  *
226  * false = we are on a local turn and haven't sent anything yet.
227  *
228  * TODO: it would be better if the following variable were not static.
229  */
230  static inline bool is_simultaneous_ = false;
231 
232  /** Used to restore the unit id manager when undoing. */
233  static inline int last_unit_id_ = 0;
234 
235  /** Actions to be executed when the current action is undone. */
236  static inline event_list undo_commands_ {};
237 };
238 
240 {
241 public:
244 
245 protected:
246  std::shared_ptr<randomness::rng> new_rng_;
248 };
249 
250 /** A RAII object to enter the synced context, cannot be called if we are already in a synced context. */
252 {
253 public:
255 
256  /** Use this constructor if you have multiple synced_context but only one replay entry. */
257  set_scontext_synced(int num);
258 
260 
261  int get_random_calls();
262  void do_final_checkup(bool dont_throw = false);
263 
264 private:
265  // only called by constructors.
266  void init();
267  static checkup* generate_checkup(const std::string& tagname);
269  const std::unique_ptr<checkup> new_checkup_;
272 };
273 
274 /**
275  * A RAII object to temporary leave the synced context like in wesnoth.synchronize_choice.
276  * Can only be used from inside a synced context.
277  */
279 {
280 public:
283 
284 private:
286 };
287 
288 /**
289  * An object to leave the synced context during draw or unsynced wml items when we don’t know whether we are in a
290  * synced context or not. if we are in a synced context we leave the synced context, otherwise it has no effect. we need
291  * This because we might call lua's wesnoth.interface.game_display during draw and we don’t want to have that an effect
292  * on the gamestate in this case.
293  */
295 {
296 public:
298 
299 private:
300  const std::unique_ptr<leave_synced_context> leaver_;
301 };
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:161
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:29
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:215
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:81
std::string_view data
Definition: picture.cpp:199
Define the game's event mechanism.
Replay control code.
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)