The Battle for Wesnoth  1.19.0-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 
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 bool event_handlers::cmp(const handler_ptr lhs, const handler_ptr rhs)
79 {
80  return lhs->priority() < rhs->priority();
81 }
82 
83 /**
84  * Read-only access to the handlers with fixed event names, by event name.
85  */
86 handler_list& event_handlers::get(const std::string& name)
87 {
88  // Empty list for the "not found" case.
89  static handler_list empty_list;
90 
91  // Look for the name in the name map.
92  auto find_it = by_name_.find(standardize_name(name));
93  return find_it == by_name_.end() ? empty_list : find_it->second;
94 }
95 
96 /**
97  * Adds an event handler.
98  * An event with a nonempty ID will not be added if an event with that
99  * ID already exists.
100  */
101 pending_event_handler event_handlers::add_event_handler(const std::string& name, const std::string& id, bool repeat, double priority, bool is_menu_item)
102 {
103  if(!id.empty()) {
104  // Ignore this handler if there is already one with this ID.
105  auto find_it = id_map_.find(id);
106 
107  if(find_it != id_map_.end() && !find_it->second.expired()) {
108  LOG_EH << "ignoring event handler for name='" << name << "' with id '" << id << "' because an event with that id already exists";
109  return {*this, nullptr};
110  }
111  }
112 
113  if(name.empty() && id.empty()) {
114  static const char* msg = "[event] is missing name or id field";
115  lg::log_to_chat() << msg << "\n";
116  if(lg::info().dont_log(log_event_handler)) {
117  ERR_EH << msg << " (run with --log-info=event_handler for more info)";
118  } else {
119  ERR_EH << msg;
120  }
121  return {*this, nullptr};
122  }
123 
124  // Create a new handler.
125  auto handler = std::make_shared<event_handler>(name, id);
126  handler->set_menu_item(is_menu_item);
127  handler->set_priority(priority);
128  handler->set_repeatable(repeat);
129  return {*this, handler};
130 }
131 
133 {
134  // Someone decided to register an empty event... bail.
135  if(handler->empty()) {
136  return;
137  }
138 
139  const std::string& names = handler->names_raw();
140  const std::string& id = handler->id();
141 
142  // Register the new handler.
143  // Do note active_ holds the main shared_ptr, and the other three containers
144  // construct weak_ptrs from the shared one.
145  DBG_EH << "inserting event handler for name=" << names << " with id=" << id;
146  active_.emplace_back(handler);
147  std::stable_sort(active_.rbegin(), active_.rend(), cmp);
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  log_handlers();
164 }
165 
167 {
169 }
170 
171 /**
172  * Removes an event handler, identified by its ID.
173  * Events with empty IDs cannot be removed.
174  */
175 void event_handlers::remove_event_handler(const std::string& id)
176 {
177  if(id.empty()) {
178  return;
179  }
180 
181  DBG_EH << "removing event handler with id " << id;
182 
183  // Find the existing handler with this ID.
184  auto find_it = id_map_.find(id);
185  if(find_it != id_map_.end()) {
186  handler_ptr handler = find_it->second.lock();
187 
188  if(handler && !handler->disabled()) {
189  handler->disable();
190  }
191 
192  // Do this even if the lock failed.
193  id_map_.erase(find_it);
194 
195  // We don't delete the handler from the other lists just yet. This is to ensure
196  // the main handler list's size doesn't change when iterating over the handlers.
197  // Any disabled handlers don't get executed, and will be removed during the next
198  // cleanup pass.
199  }
200 
201  log_handlers();
202 }
203 
204 void event_handlers::clean_up_expired_handlers(const std::string& event_name)
205 {
206  // First, remove all disabled handlers from the main list.
207  auto to_remove = std::remove_if(active_.begin(), active_.end(),
208  [](handler_ptr p) { return p->disabled(); }
209  );
210 
211  active_.erase(to_remove, active_.end());
212 
213  // Then remove any now-unlockable weak_ptrs from the by-name list.
214  // Might be more than one so we split.
215  for(const std::string& name : utils::split(event_name)) {
216  get(name).remove_if(
217  [](weak_handler_ptr ptr) { return ptr.expired(); }
218  );
219  }
220 
221  // And finally remove any now-unlockable weak_ptrs from the with-variables name list.
222  dynamic_.remove_if(
223  [](weak_handler_ptr ptr) { return ptr.expired(); }
224  );
225 }
226 
228 {
229  auto find_it = id_map_.find(id);
230  if(find_it != id_map_.end() && !find_it->second.expired()) {
231  return find_it->second.lock();
232  }
233 
234  return nullptr;
235 }
236 
237 } // end namespace game_events
std::vector< std::string > names
Definition: build_info.cpp:66
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:207
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:320
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:544
logger & info()
Definition: log.cpp:314
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:109
mock_party p
#define h