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