The Battle for Wesnoth  1.17.17+dev
manager_impl.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2023
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 
23 #include <boost/algorithm/string.hpp>
24 
25 static lg::log_domain log_engine("engine");
26 #define DBG_NG LOG_STREAM(debug, log_engine)
27 #define LOG_NG LOG_STREAM(info, log_engine)
28 #define WRN_NG LOG_STREAM(warn, log_engine)
29 
30 static lg::log_domain log_event_handler("event_handler");
31 #define ERR_EH LOG_STREAM(err, log_event_handler)
32 #define LOG_EH LOG_STREAM(info, log_event_handler)
33 #define DBG_EH LOG_STREAM(debug, log_event_handler)
34 
35 static lg::log_domain log_wml("wml");
36 #define ERR_WML LOG_STREAM(err, log_wml)
37 #define DBG_WML LOG_STREAM(debug, log_wml)
38 
39 namespace game_events
40 {
42 {
43  if(lg::debug().dont_log(log_event_handler)) {
44  return;
45  }
46 
47  std::stringstream ss;
48 
49  for(const handler_ptr& h : active_) {
50  if(!h) {
51  continue;
52  }
53 
54  ss << "name=" << h->names_raw() << ", with id=" << h->id() << "; ";
55  }
56 
57  DBG_EH << "active handlers are now " << ss.str();
58 }
59 
60 /**
61  * Utility to standardize the event names used in by_name_.
62  * This means stripping leading and trailing spaces, and converting internal
63  * spaces to underscores.
64  */
65 std::string event_handlers::standardize_name(const std::string& name)
66 {
67  std::string retval = name;
68 
69  // Trim leading and trailing spaces.
71 
72  // Replace internal spaces with underscores.
73  boost::replace_all(retval, " ", "_");
74 
75  return retval;
76 }
77 
78 /**
79  * Read-only access to the handlers with fixed event names, by event name.
80  */
81 handler_list& event_handlers::get(const std::string& name)
82 {
83  // Empty list for the "not found" case.
84  static handler_list empty_list;
85 
86  // Look for the name in the name map.
87  auto find_it = by_name_.find(standardize_name(name));
88  return find_it == by_name_.end() ? empty_list : find_it->second;
89 }
90 
91 /**
92  * Adds an event handler.
93  * An event with a nonempty ID will not be added if an event with that
94  * ID already exists.
95  */
96 pending_event_handler event_handlers::add_event_handler(const std::string& name, const std::string& id, bool repeat, bool is_menu_item)
97 {
98  if(!id.empty()) {
99  // Ignore this handler if there is already one with this ID.
100  auto find_it = id_map_.find(id);
101 
102  if(find_it != id_map_.end() && !find_it->second.expired()) {
103  LOG_EH << "ignoring event handler for name='" << name << "' with id '" << id << "' because an event with that id already exists";
104  return {*this, nullptr};
105  }
106  }
107 
108  if(name.empty() && id.empty()) {
109  static const char* msg = "[event] is missing name or id field";
110  lg::log_to_chat() << msg << "\n";
111  if(lg::info().dont_log(log_event_handler)) {
112  ERR_EH << msg << " (run with --log-info=event_handler for more info)";
113  } else {
114  ERR_EH << msg;
115  }
116  return {*this, nullptr};
117  }
118 
119  // Create a new handler.
120  auto handler = std::make_shared<event_handler>(name, id);
121  handler->set_menu_item(is_menu_item);
122  handler->set_repeatable(repeat);
123  return {*this, handler};
124 }
125 
127 {
128  // Someone decided to register an empty event... bail.
129  if(handler->empty()) {
130  return;
131  }
132 
133  const std::string& names = handler->names_raw();
134  const std::string& id = handler->id();
135 
136  // Register the new handler.
137  // Do note active_ holds the main shared_ptr, and the other three containers
138  // construct weak_ptrs from the shared one.
139  DBG_EH << "inserting event handler for name=" << names << " with id=" << id;
140  active_.emplace_back(handler);
141 
142  // File by name.
144  dynamic_.emplace_back(active_.back());
145  } else {
146  for(const std::string& single_name : handler->names(nullptr)) {
147  by_name_[single_name].emplace_back(active_.back());
148  }
149  }
150 
151  // File by ID.
152  if(!id.empty()) {
153  id_map_[id] = active_.back();
154  }
155 
156  log_handlers();
157 }
158 
160 {
162 }
163 
164 /**
165  * Removes an event handler, identified by its ID.
166  * Events with empty IDs cannot be removed.
167  */
168 void event_handlers::remove_event_handler(const std::string& id)
169 {
170  if(id.empty()) {
171  return;
172  }
173 
174  DBG_EH << "removing event handler with id " << id;
175 
176  // Find the existing handler with this ID.
177  auto find_it = id_map_.find(id);
178  if(find_it != id_map_.end()) {
179  handler_ptr handler = find_it->second.lock();
180 
181  if(handler && !handler->disabled()) {
182  handler->disable();
183  }
184 
185  // Do this even if the lock failed.
186  id_map_.erase(find_it);
187 
188  // We don't delete the handler from the other lists just yet. This is to ensure
189  // the main handler list's size doesn't change when iterating over the handlers.
190  // Any disabled handlers don't get executed, and will be removed during the next
191  // cleanup pass.
192  }
193 
194  log_handlers();
195 }
196 
197 void event_handlers::clean_up_expired_handlers(const std::string& event_name)
198 {
199  // First, remove all disabled handlers from the main list.
200  auto to_remove = std::remove_if(active_.begin(), active_.end(),
201  [](handler_ptr p) { return p->disabled(); }
202  );
203 
204  active_.erase(to_remove, active_.end());
205 
206  // Then remove any now-unlockable weak_ptrs from the by-name list.
207  // Might be more than one so we split.
208  for(const std::string& name : utils::split(event_name)) {
209  get(name).remove_if(
210  [](weak_handler_ptr ptr) { return ptr.expired(); }
211  );
212  }
213 
214  // And finally remove any now-unlockable weak_ptrs from the with-variables name list.
215  dynamic_.remove_if(
216  [](weak_handler_ptr ptr) { return ptr.expired(); }
217  );
218 }
219 
221 {
222  auto find_it = id_map_.find(id);
223  if(find_it != id_map_.end() && !find_it->second.expired()) {
224  return find_it->second.lock();
225  }
226 
227  return nullptr;
228 }
229 
230 } // end namespace game_events
std::vector< std::string > names
Definition: build_info.cpp:69
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, 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.
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:215
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:238
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:462
logger & info()
Definition: log.cpp:232
void trim(std::string_view &s)
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:110
mock_party p
#define h