The Battle for Wesnoth  1.19.7+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  * 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  */
89 pending_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 
155 {
157 }
158 
159 /**
160  * Removes an event handler, identified by its ID.
161  * Events with empty IDs cannot be removed.
162  */
163 void 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 
192 void 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
Definition: build_info.cpp:67
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.
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::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:520
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:106
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