The Battle for Wesnoth  1.19.5+dev
manager_impl.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
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 
18 #include "game_events/handlers.hpp"
19 #include "formula/string_utils.hpp"
20 #include "log.hpp"
22 #include "utils/general.hpp"
23 
24 #include <boost/algorithm/string.hpp>
25 
26 static 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 
31 static 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 
36 static 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 
40 namespace 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  */
66 std::string event_handlers::standardize_name(const std::string& name)
67 {
68  std::string retval = name;
69 
70  // Trim leading and trailing spaces.
72 
73  // Replace internal spaces with underscores.
74  boost::replace_all(retval, " ", "_");
75 
76  return retval;
77 }
78 
79 bool event_handlers::cmp(const handler_ptr lhs, const handler_ptr rhs)
80 {
81  return lhs->priority() < rhs->priority();
82 }
83 
84 /**
85  * Read-only access to the handlers with fixed event names, by event name.
86  */
87 handler_list& event_handlers::get(const std::string& name)
88 {
89  // Empty list for the "not found" case.
90  static handler_list empty_list;
91 
92  // Look for the name in the name map.
93  auto find_it = by_name_.find(standardize_name(name));
94  return find_it == by_name_.end() ? empty_list : find_it->second;
95 }
96 
97 /**
98  * Adds an event handler.
99  * An event with a nonempty ID will not be added if an event with that
100  * ID already exists.
101  */
102 pending_event_handler event_handlers::add_event_handler(const std::string& name, const std::string& id, bool repeat, double priority, bool is_menu_item)
103 {
104  if(!id.empty()) {
105  // Ignore this handler if there is already one with this ID.
106  auto find_it = id_map_.find(id);
107 
108  if(find_it != id_map_.end() && !find_it->second.expired()) {
109  LOG_EH << "ignoring event handler for name='" << name << "' with id '" << id << "' because an event with that id already exists";
110  return {*this, nullptr};
111  }
112  }
113 
114  if(name.empty() && id.empty()) {
115  static const char* msg = "[event] is missing name or id field";
116  lg::log_to_chat() << msg << "\n";
117  if(lg::info().dont_log(log_event_handler)) {
118  ERR_EH << msg << " (run with --log-info=event_handler for more info)";
119  } else {
120  ERR_EH << msg;
121  }
122  return {*this, nullptr};
123  }
124 
125  // Create a new handler.
126  auto handler = std::make_shared<event_handler>(name, id);
127  handler->set_menu_item(is_menu_item);
128  handler->set_priority(priority);
129  handler->set_repeatable(repeat);
130  return {*this, handler};
131 }
132 
134 {
135  // Someone decided to register an empty event... bail.
136  if(handler->empty()) {
137  return;
138  }
139 
140  const std::string& names = handler->names_raw();
141  const std::string& id = handler->id();
142 
143  // Register the new handler.
144  // Do note active_ holds the main shared_ptr, and the other three containers
145  // construct weak_ptrs from the shared one.
146  DBG_EH << "inserting event handler for name=" << names << " with id=" << id;
147  active_.emplace_back(handler);
148 
149  // File by name.
151  dynamic_.emplace_back(active_.back());
152  } else {
153  for(const std::string& single_name : handler->names(nullptr)) {
154  by_name_[single_name].emplace_back(active_.back());
155  }
156  }
157 
158  // File by ID.
159  if(!id.empty()) {
160  id_map_[id] = active_.back();
161  }
162 
163  std::stable_sort(active_.rbegin(), active_.rend(), cmp);
164  log_handlers();
165 }
166 
168 {
170 }
171 
172 /**
173  * Removes an event handler, identified by its ID.
174  * Events with empty IDs cannot be removed.
175  */
176 void event_handlers::remove_event_handler(const std::string& id)
177 {
178  if(id.empty()) {
179  return;
180  }
181 
182  DBG_EH << "removing event handler with id " << id;
183 
184  // Find the existing handler with this ID.
185  auto find_it = id_map_.find(id);
186  if(find_it != id_map_.end()) {
187  handler_ptr handler = find_it->second.lock();
188 
189  if(handler && !handler->disabled()) {
190  handler->disable();
191  }
192 
193  // Do this even if the lock failed.
194  id_map_.erase(find_it);
195 
196  // We don't delete the handler from the other lists just yet. This is to ensure
197  // the main handler list's size doesn't change when iterating over the handlers.
198  // Any disabled handlers don't get executed, and will be removed during the next
199  // cleanup pass.
200  }
201 
202  log_handlers();
203 }
204 
205 void event_handlers::clean_up_expired_handlers(const std::string& event_name)
206 {
207  // First, remove all disabled handlers from the main list.
208  utils::erase_if(active_, [](const handler_ptr& p) { return p->disabled(); });
209 
210  // Then remove any now-unlockable weak_ptrs from the by-name list.
211  // Might be more than one so we split.
212  for(const std::string& name : utils::split(event_name)) {
213  get(name).remove_if(
214  [](weak_handler_ptr ptr) { return ptr.expired(); }
215  );
216  }
217 
218  // And finally remove any now-unlockable weak_ptrs from the with-variables name list.
219  dynamic_.remove_if(
220  [](weak_handler_ptr ptr) { return ptr.expired(); }
221  );
222 }
223 
225 {
226  auto find_it = id_map_.find(id);
227  if(find_it != id_map_.end() && !find_it->second.expired()) {
228  return find_it->second.lock();
229  }
230 
231  return nullptr;
232 }
233 
234 } // end namespace game_events
std::vector< std::string > names
Definition: build_info.cpp:67
map_t by_name_
Active event handlers with fixed event names, organized by event name.
void clean_up_expired_handlers(const std::string &event_name)
Removes all expired event handlers and any weak_ptrs to them.
handler_list & get(const std::string &name)
Access to the handlers with fixed event names, by event name.
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.
void finish_adding_event_handler(handler_ptr new_handler)
handler_list dynamic_
Active event handlers with variables in their event names.
static bool cmp(const handler_ptr lhs, const handler_ptr rhs)
Compare function to sort event handlers by priority.
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.
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:198
Define the handlers for the game's events mechanism.
Standard logging facilities (interface).
#define ERR_EH
static lg::log_domain log_engine("engine")
#define LOG_EH
static lg::log_domain log_event_handler("event_handler")
#define DBG_EH
static lg::log_domain log_wml("wml")
Domain specific events.
std::list< weak_handler_ptr > handler_list
Definition: fwd.hpp:27
std::shared_ptr< event_handler > handler_ptr
Definition: fwd.hpp:25
std::weak_ptr< event_handler > weak_handler_ptr
Definition: fwd.hpp:26
retval
Default window/dialog return values.
Definition: retval.hpp:30
logger & debug()
Definition: log.cpp:325
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:543
logger & info()
Definition: log.cpp:319
void trim(std::string_view &s)
void erase_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::remove_if on a container.
Definition: general.hpp:100
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)
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
mock_party p
#define h