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