The Battle for Wesnoth  1.17.21+dev
manager.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 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 #include "game_events/manager.hpp"
17 
18 #include "game_events/handlers.hpp"
21 #include "game_events/pump.hpp"
22 
23 #include "formula/string_utils.hpp"
24 #include "game_data.hpp"
25 #include "log.hpp"
26 #include "resources.hpp"
28 
29 static lg::log_domain log_engine("engine");
30 #define DBG_NG LOG_STREAM(debug, log_engine)
31 #define LOG_NG LOG_STREAM(info, log_engine)
32 #define WRN_NG LOG_STREAM(warn, log_engine)
33 
34 static lg::log_domain log_event_handler("event_handler");
35 #define LOG_EH LOG_STREAM(info, log_event_handler)
36 #define DBG_EH LOG_STREAM(debug, log_event_handler)
37 
38 namespace
39 {
40  // Event handlers can't be destroyed as long as at least one of these locks exist.
41  class event_handler_list_lock
42  {
43  public:
44  event_handler_list_lock()
45  {
46  ++num_locks_;
47  }
48 
49  ~event_handler_list_lock()
50  {
51  --num_locks_;
52  }
53 
54  static bool none()
55  {
56  return num_locks_ == 0u;
57  }
58  private:
59  static unsigned int num_locks_;
60  };
61 
62  unsigned int event_handler_list_lock::num_locks_ = 0u;
63 }
64 
65 namespace game_events
66 {
67 /** Create an event handler. */
68 void manager::add_event_handler_from_wml(const config& handler, game_lua_kernel& lk, bool is_menu_item)
69 {
70  auto new_handler = event_handlers_->add_event_handler(
71  handler["name"],
72  handler["id"],
73  !handler["first_time_only"].to_bool(true),
74  handler["priority"].to_double(0.),
75  is_menu_item
76  );
77  if(new_handler.valid()) {
78  new_handler->read_filters(handler);
79 
80  // Strip out anything that's used by the event system itself.
81  config args;
82  for(const auto& [attr, val] : handler.attribute_range()) {
83  if(attr == "id" || attr == "name" || attr == "first_time_only" || attr == "priority" || attr.compare(0, 6, "filter") == 0) {
84  continue;
85  }
86  args[attr] = val;
87  }
88  for(auto child : handler.all_children_range()) {
89  if(child.key.compare(0, 6, "filter") != 0) {
90  args.add_child(child.key, child.cfg);
91  }
92  }
93  new_handler->set_arguments(args);
94  new_handler->register_wml_event(lk);
95  DBG_EH << "Registered WML event "
96  << (new_handler->names_raw().empty() ? "" : "'" + new_handler->names_raw() + "'")
97  << (new_handler->id().empty() ? "" : "{id=" + new_handler->id() + "}")
98  << (new_handler->repeatable() ? " (repeating" : " (first time only")
99  << "; priority " + std::to_string(new_handler->priority())
100  << (is_menu_item ? "; menu item)" : ")")
101  << " with the following actions:\n"
102  << args.debug();
103  } else {
104  LOG_EH << "Content of failed event:\n" << handler.debug();
105  }
106 }
107 
108 pending_event_handler manager::add_event_handler_from_lua(const std::string& name, const std::string& id, bool repeat, double priority, bool is_menu_item)
109 {
110  return event_handlers_->add_event_handler(name, id, repeat, priority, is_menu_item);
111 }
112 
113 /** Removes an event handler. */
114 void manager::remove_event_handler(const std::string& id)
115 {
116  event_handlers_->remove_event_handler(id);
117 }
118 
119 /** Gets an event handler by id */
120 const handler_ptr manager::get_event_handler_by_id(const std::string& id)
121 {
122  return event_handlers_->get_event_handler_by_id(id);
123 }
124 
125 /* ** manager ** */
126 
128  : event_handlers_(new event_handlers())
129  , unit_wml_ids_()
130  , pump_(new game_events::wml_event_pump(*this))
131  , wml_menu_items_()
132 {
133 }
134 
135 void manager::read_scenario(const config& scenario_cfg, game_lua_kernel& lk)
136 {
137  for(const config& ev : scenario_cfg.child_range("event")) {
139  }
140 
141  for(const std::string& id : utils::split(scenario_cfg["unit_wml_ids"])) {
142  unit_wml_ids_.insert(id);
143  }
144 
145  wml_menu_items_.set_menu_items(scenario_cfg);
146 
147  // Create the event handlers for menu items.
149 }
150 
152 {
153 }
154 
155 void manager::add_events(const config::const_child_itors& cfgs, game_lua_kernel& lk, const std::string& type)
156 {
157  if(!type.empty()) {
158  if(std::find(unit_wml_ids_.begin(), unit_wml_ids_.end(), type) != unit_wml_ids_.end()) {
159  return;
160  }
161 
162  unit_wml_ids_.insert(type);
163  }
164 
165  for(const config& new_ev : cfgs) {
166  if(type.empty() && new_ev["id"].empty()) {
167  WRN_NG << "attempt to add an [event] with empty id=, ignoring ";
168  continue;
169  }
170 
171  add_event_handler_from_wml(new_ev, lk);
172  }
173 }
174 
175 void manager::write_events(config& cfg, bool include_nonserializable) const
176 {
177  for(const handler_ptr& eh : event_handlers_->get_active()) {
178  if(!eh || eh->is_menu_item()) {
179  continue;
180  }
181 
182  // Silently skip disabled events if this function is invoked mid-event, such as via
183  // [inspect] (the inspector writes the events to a local config) or if an out-of-sync
184  // error occurs in MP. If the event in question is first-time-only, it will already
185  // have been flagged as disabled by this point (such events are disabled before their
186  // actions are run). If a disabled event is encountered outside an event context,
187  // however, assert. That means something went wrong with event list cleanup.
188  // Also silently skip them when including nonserializable events, which can happen
189  // if viewing the inspector with :inspect after removing an event from the Lua console.
190  if(eh->disabled() && (is_event_running() || include_nonserializable)) {
191  continue;
192  } else {
193  assert(!eh->disabled());
194  }
195 
196  config event_cfg;
197  eh->write_config(event_cfg, include_nonserializable);
198  if(!event_cfg.empty()) {
199  cfg.add_child("event", std::move(event_cfg));
200  }
201  }
202 
203  cfg["unit_wml_ids"] = utils::join(unit_wml_ids_);
205 }
206 
207 void manager::execute_on_events(const std::string& event_id, manager::event_func_t func)
208 {
209  const std::string standardized_event_id = event_handlers::standardize_name(event_id);
210  const game_data* gd = resources::gamedata;
211  auto& active_handlers = event_handlers_->get_active();
212 
213  // Save the end outside the loop so the end point remains constant,
214  // even if new events are added to the queue.
215  const unsigned saved_end = active_handlers.size();
216 
217 
218  {
219  // Ensure that event handlers won't be cleaned up while we're iterating them.
220  event_handler_list_lock lock;
221 
222  for (unsigned i = 0; i < saved_end; ++i) {
223  handler_ptr handler = nullptr;
224 
225  try {
226  handler = active_handlers.at(i);
227  }
228  catch (const std::out_of_range&) {
229  continue;
230  }
231 
232  // Shouldn't happen, but we're just being safe.
233  if (!handler || handler->disabled()) {
234  continue;
235  }
236 
237  // Could be more than one.
238  for(const std::string& name : handler->names(gd)) {
239  if(standardized_event_id == name) {
240  func(*this, handler);
241  break;
242  }
243  }
244  }
245  }
246 
247  // Clean up expired ptrs. This saves us effort later since it ensures every ptr is valid.
248  if(event_handler_list_lock::none()) {
249  event_handlers_->clean_up_expired_handlers(standardized_event_id);
250  }
251 }
252 
254 {
255  // If there is an event handler list lock, an event is being processed.
256  return !event_handler_list_lock::none();
257 }
258 
260 {
261  return *pump_;
262 }
263 
264 } // end namespace game_events
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
const_attr_itors attribute_range() const
Definition: config.cpp:767
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:891
child_itors child_range(config_key_type key)
Definition: config.cpp:277
std::string debug() const
Definition: config.cpp:1248
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:285
bool empty() const
Definition: config.cpp:856
config & add_child(config_key_type key)
Definition: config.cpp:445
static std::string standardize_name(const std::string &name)
Utility to standardize the event names used in by_name_.
std::function< void(game_events::manager &, handler_ptr &)> event_func_t
Definition: manager.hpp:81
void execute_on_events(const std::string &event_id, event_func_t func)
Definition: manager.cpp:207
const std::unique_ptr< game_events::wml_event_pump > pump_
Definition: manager.hpp:52
game_events::wml_event_pump & pump()
Definition: manager.cpp:259
void write_events(config &cfg, bool include_nonserializable=false) const
Definition: manager.cpp:175
void remove_event_handler(const std::string &id)
Removes an event handler.
Definition: manager.cpp:114
void read_scenario(const config &scenario_cfg, game_lua_kernel &lk)
Definition: manager.cpp:135
void add_event_handler_from_wml(const config &handler, game_lua_kernel &lk, bool is_menu_item=false)
Create an event handler from an [event] tag.
Definition: manager.cpp:68
void add_events(const config::const_child_itors &cfgs, game_lua_kernel &lk, const std::string &type=std::string())
Definition: manager.cpp:155
const handler_ptr get_event_handler_by_id(const std::string &id)
Gets an event handler by ID.
Definition: manager.cpp:120
bool is_event_running() const
Definition: manager.cpp:253
const std::unique_ptr< event_handlers > event_handlers_
Definition: manager.hpp:49
game_events::wmi_manager wml_menu_items_
Definition: manager.hpp:53
pending_event_handler add_event_handler_from_lua(const std::string &name, const std::string &id, bool repeat=false, double priority=0., bool is_menu_item=false)
Create an empty event handler.
Definition: manager.cpp:108
std::set< std::string > unit_wml_ids_
Definition: manager.hpp:50
Represents a handler that is about to be added to the events manager but is still waiting for some da...
void init_handlers(game_lua_kernel &lk) const
Initializes the implicit event handlers for inlined [command]s.
void to_config(config &cfg) const
void set_menu_items(const config &cfg)
Sets the current menu items to the "menu_item" children of cfg.
std::size_t i
Definition: function.cpp:968
#define WRN_NG
Definition: manager.cpp:32
static lg::log_domain log_engine("engine")
#define LOG_EH
Definition: manager.cpp:35
static lg::log_domain log_event_handler("event_handler")
#define DBG_EH
Definition: manager.cpp:36
Define the handlers for the game's events mechanism.
Standard logging facilities (interface).
Declarations for a class that implements WML-defined (right-click) menu items.
Domain specific events.
std::shared_ptr< event_handler > handler_ptr
Definition: fwd.hpp:25
game_data * gamedata
Definition: resources.cpp:23
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::vector< std::string > split(const config_attribute_value &val)
Define the game's event mechanism.