The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
manager_impl.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 "log.hpp"
22#include "utils/general.hpp"
23
24#include <boost/algorithm/string.hpp>
25
26static lg::log_domain log_engine("engine");
27#define DBG_NG LOG_STREAM(debug, log_engine)
28#define LOG_NG LOG_STREAM(info, log_engine)
29#define WRN_NG LOG_STREAM(warn, log_engine)
30
31static lg::log_domain log_event_handler("event_handler");
32#define ERR_EH LOG_STREAM(err, log_event_handler)
33#define LOG_EH LOG_STREAM(info, log_event_handler)
34#define DBG_EH LOG_STREAM(debug, log_event_handler)
35
36static lg::log_domain log_wml("wml");
37#define ERR_WML LOG_STREAM(err, log_wml)
38#define DBG_WML LOG_STREAM(debug, log_wml)
39
40namespace game_events
41{
43{
44 if(lg::debug().dont_log(log_event_handler)) {
45 return;
46 }
47
48 std::stringstream ss;
49
50 for(const handler_ptr& h : active_) {
51 if(!h) {
52 continue;
53 }
54
55 ss << "name=" << h->names_raw() << ", with id=" << h->id() << "; ";
56 }
57
58 DBG_EH << "active handlers are now " << ss.str();
59}
60
61/**
62 * Utility to standardize the event names used in by_name_.
63 * This means stripping leading and trailing spaces, and converting internal
64 * spaces to underscores.
65 */
66std::string event_handlers::standardize_name(const std::string& name)
67{
68 std::string retval = name;
69
70 // Trim leading and trailing spaces.
71 boost::trim(retval);
72
73 // Replace internal spaces with underscores.
74 boost::replace_all(retval, " ", "_");
75
76 return retval;
77}
78
79bool event_handlers::cmp(const handler_ptr& lhs, const handler_ptr& rhs)
80{
81 return lhs->priority() < rhs->priority();
82}
83
84/**
85 * Adds an event handler.
86 * An event with a nonempty ID will not be added if an event with that
87 * ID already exists.
88 */
89pending_event_handler event_handlers::add_event_handler(const std::string& name, const std::string& id, bool repeat, double priority, bool is_menu_item)
90{
91 if(!id.empty()) {
92 // Ignore this handler if there is already one with this ID.
93 auto find_it = id_map_.find(id);
94
95 if(find_it != id_map_.end() && !find_it->second.expired()) {
96 LOG_EH << "ignoring event handler for name='" << name << "' with id '" << id << "' because an event with that id already exists";
97 return {*this, nullptr};
98 }
99 }
100
101 if(name.empty() && id.empty()) {
102 static const char* msg = "[event] is missing name or id field";
103 lg::log_to_chat() << msg << "\n";
104 if(lg::info().dont_log(log_event_handler)) {
105 ERR_EH << msg << " (run with --log-info=event_handler for more info)";
106 } else {
107 ERR_EH << msg;
108 }
109 return {*this, nullptr};
110 }
111
112 // Create a new handler.
113 auto handler = std::make_shared<event_handler>(name, id);
114 handler->set_menu_item(is_menu_item);
115 handler->set_priority(priority);
116 handler->set_repeatable(repeat);
117 return {*this, handler};
118}
119
121{
122 // Someone decided to register an empty event... bail.
123 if(handler->empty()) {
124 return;
125 }
126
127 const std::string& names = handler->names_raw();
128 const std::string& id = handler->id();
129
130 // Register the new handler.
131 // Do note active_ holds the main shared_ptr, and the other three containers
132 // construct weak_ptrs from the shared one.
133 DBG_EH << "inserting event handler for name=" << names << " with id=" << id;
134 active_.emplace_back(handler);
135
136 // File by name.
138 dynamic_.emplace_back(active_.back());
139 } else {
140 for(const std::string& single_name : handler->names(nullptr)) {
141 by_name_[single_name].emplace_back(active_.back());
142 }
143 }
144
145 // File by ID.
146 if(!id.empty()) {
147 id_map_[id] = active_.back();
148 }
149
150 std::stable_sort(active_.rbegin(), active_.rend(), cmp);
151 log_handlers();
152}
153
158
159/**
160 * Removes an event handler, identified by its ID.
161 * Events with empty IDs cannot be removed.
162 */
163void event_handlers::remove_event_handler(const std::string& id)
164{
165 if(id.empty()) {
166 return;
167 }
168
169 DBG_EH << "removing event handler with id " << id;
170
171 // Find the existing handler with this ID.
172 auto find_it = id_map_.find(id);
173 if(find_it != id_map_.end()) {
174 handler_ptr handler = find_it->second.lock();
175
176 if(handler && !handler->disabled()) {
177 handler->disable();
178 }
179
180 // Do this even if the lock failed.
181 id_map_.erase(find_it);
182
183 // We don't delete the handler from the other lists just yet. This is to ensure
184 // the main handler list's size doesn't change when iterating over the handlers.
185 // Any disabled handlers don't get executed, and will be removed during the next
186 // cleanup pass.
187 }
188
189 log_handlers();
190}
191
192void event_handlers::clean_up_expired_handlers(const std::string& event_name)
193{
194 // First, remove all disabled handlers from the main list.
195 utils::erase_if(active_, [](const handler_ptr& p) { return p->disabled(); });
196
197 // Then remove any now-unlockable weak_ptrs from the by-name list.
198 // Might be more than one so we split.
199 for(const std::string& name : utils::split(event_name)) {
200 by_name_[standardize_name(name)].remove_if(
201 [](const weak_handler_ptr& ptr) { return ptr.expired(); }
202 );
203 }
204
205 // And finally remove any now-unlockable weak_ptrs from the with-variables name list.
206 dynamic_.remove_if(
207 [](const weak_handler_ptr& ptr) { return ptr.expired(); }
208 );
209}
210
212{
213 auto find_it = id_map_.find(id);
214 if(find_it != id_map_.end() && !find_it->second.expired()) {
215 return find_it->second.lock();
216 }
217
218 return nullptr;
219}
220
221} // end namespace game_events
std::vector< std::string > names
map_t by_name_
Active event handlers with fixed event names, organized by event name.
void finish_adding_event_handler(const handler_ptr &new_handler)
void clean_up_expired_handlers(const std::string &event_name)
Removes all expired event handlers and any weak_ptrs to them.
static bool cmp(const handler_ptr &lhs, const handler_ptr &rhs)
Compare function to sort event handlers by priority.
void remove_event_handler(const std::string &id)
Removes an event handler, identified by its ID.
pending_event_handler add_event_handler(const std::string &name, const std::string &id, bool repeat, double priority=0., bool is_menu_item=false)
Adds an event handler.
handler_list dynamic_
Active event handlers with variables in their event names.
id_map_t id_map_
Allows quick locating of handlers by id.
const handler_ptr get_event_handler_by_id(const std::string &id)
Gets an event handler, identified by its ID.
handler_queue_t active_
Active event handlers.
static std::string standardize_name(const std::string &name)
Utility to standardize the event names used in by_name_.
Represents a handler that is about to be added to the events manager but is still waiting for some da...
bool valid() const
Check if this handler is valid.
#define LOG_EH
Definition manager.cpp:35
std::string id
Text to match against addon_info.tags()
Definition manager.cpp:199
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).
#define ERR_EH
static lg::log_domain log_engine("engine")
static lg::log_domain log_event_handler("event_handler")
static lg::log_domain log_wml("wml")
Domain specific events.
std::shared_ptr< event_handler > handler_ptr
Definition fwd.hpp:25
std::weak_ptr< event_handler > weak_handler_ptr
Definition fwd.hpp:26
logger & debug()
Definition log.cpp:357
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition log.cpp:550
logger & info()
Definition log.cpp:351
void erase_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::remove_if on a container.
Definition general.hpp:108
bool might_contain_variables(const std::string &str)
Determines if a string might contain variables to interpolate.
std::vector< std::string > split(const config_attribute_value &val)
mock_party p
#define h