The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
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
17
20#include "game_events/pump.hpp"
21
22#include "game_data.hpp"
23#include "log.hpp"
24#include "resources.hpp"
26#include "utils/general.hpp"
27
28static lg::log_domain log_engine("engine");
29#define DBG_NG LOG_STREAM(debug, log_engine)
30#define LOG_NG LOG_STREAM(info, log_engine)
31#define WRN_NG LOG_STREAM(warn, log_engine)
32#define ERR_NG LOG_STREAM(err, log_engine)
33
34static 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
38namespace
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
65namespace game_events
66{
67/** Create an event handler. */
68void 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 [key, cfg] : handler.all_children_view()) {
89 if(key.compare(0, 6, "filter") != 0) {
90 args.add_child(key, 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
108pending_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. */
114void 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 */
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
135void 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
154
155void manager::add_events(const config::const_child_itors& cfgs, game_lua_kernel& lk, const std::string& type, const std::string& origin)
156{
157 if(!type.empty()) {
158 if(utils::contains(unit_wml_ids_, type)) {
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= from " << origin << ", ignoring ";
168 continue;
169 }
170
171 add_event_handler_from_wml(new_ev, lk);
172 }
173}
174
175void 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, show an error. 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()) {
191 if(!is_event_running() && !include_nonserializable) {
192 //This could in theory happen if an exception was thrown during execute_on_events.
193 ERR_NG << "found disabled event with no event running, seems like event cleanup has failed";
194 }
195 continue;
196 }
197
198 config event_cfg;
199 eh->write_config(event_cfg, include_nonserializable);
200 if(!event_cfg.empty()) {
201 cfg.add_child("event", std::move(event_cfg));
202 }
203 }
204
205 cfg["unit_wml_ids"] = utils::join(unit_wml_ids_);
207}
208
209void manager::execute_on_events(const std::string& event_id, const manager::event_func_t& func)
210{
211 const std::string standardized_event_id = event_handlers::standardize_name(event_id);
212 const game_data* gd = resources::gamedata;
213 // Copy the list so that new events added during processing are not executed.
214 auto active_handlers = event_handlers_->get_active();
215
216 {
217 // Ensure that event handlers won't be cleaned up while we're iterating them.
218 event_handler_list_lock lock;
219
220 for (unsigned i = 0; i < active_handlers.size(); ++i) {
221 handler_ptr handler = nullptr;
222
223 try {
224 handler = active_handlers.at(i);
225 }
226 catch (const std::out_of_range&) {
227 continue;
228 }
229
230 // Shouldn't happen, but we're just being safe.
231 if (!handler || handler->disabled()) {
232 continue;
233 }
234
235 // Could be more than one.
236 for(const std::string& name : handler->names(gd)) {
237 if(standardized_event_id == name) {
238 func(*this, handler);
239 break;
240 }
241 }
242 }
243 }
244
245 // Clean up expired ptrs. This saves us effort later since it ensures every ptr is valid.
246 if(event_handler_list_lock::none()) {
247 event_handlers_->clean_up_expired_handlers(standardized_event_id);
248 }
249}
250
252{
253 // If there is an event handler list lock, an event is being processed.
254 return !event_handler_list_lock::none();
255}
256
261
262} // end namespace game_events
#define WRN_NG
#define ERR_NG
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
config & add_child(std::string_view key)
Definition config.cpp:436
const_attr_itors attribute_range() const
Definition config.cpp:740
auto all_children_view() const
In-order iteration over all children.
Definition config.hpp:795
child_itors child_range(std::string_view key)
Definition config.cpp:268
std::string debug() const
Definition config.cpp:1214
boost::iterator_range< const_child_iterator > const_child_itors
Definition config.hpp:281
bool empty() const
Definition config.cpp:823
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:88
void execute_on_events(const std::string &event_id, const event_func_t &func)
Definition manager.cpp:209
const std::unique_ptr< game_events::wml_event_pump > pump_
Definition manager.hpp:50
game_events::wml_event_pump & pump()
Definition manager.cpp:257
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
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:251
const std::unique_ptr< event_handlers > event_handlers_
Definition manager.hpp:47
game_events::wmi_manager wml_menu_items_
Definition manager.hpp:51
void add_events(const config::const_child_itors &cfgs, game_lua_kernel &lk, const std::string &type, const std::string &origin)
Registers a batch of [event] tags found elsewhere in WML.
Definition manager.cpp:155
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: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.
const config * cfg
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 handlers.cpp:48
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
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition general.hpp:88
std::string join(const Range &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.
#define i