The Battle for Wesnoth  1.17.0-dev
manager_impl.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2021
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 #include <iostream>
26 
27 static lg::log_domain log_engine("engine");
28 #define DBG_NG LOG_STREAM(debug, log_engine)
29 #define LOG_NG LOG_STREAM(info, log_engine)
30 #define WRN_NG LOG_STREAM(warn, log_engine)
31 
32 static lg::log_domain log_event_handler("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 
38 namespace game_events
39 {
41 {
42  if(lg::debug().dont_log(log_event_handler)) {
43  return;
44  }
45 
46  std::stringstream ss;
47 
48  for(const handler_ptr& h : active_) {
49  if(!h) {
50  continue;
51  }
52 
53  const config& cfg = h->get_config();
54  ss << "name=" << cfg["name"] << ", with id=" << cfg["id"] << "; ";
55  }
56 
57  DBG_EH << "active handlers are now " << ss.str() << "\n";
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.
70  boost::trim(retval);
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 void event_handlers::add_event_handler(const config& cfg, bool is_menu_item)
97 {
98  // Someone decided to register an empty event... bail.
99  if(cfg.empty()) {
100  return;
101  }
102 
103  std::string name = cfg["name"];
104  std::string id = cfg["id"];
105 
106  if(!id.empty()) {
107  // Ignore this handler if there is already one with this ID.
108  auto find_it = id_map_.find(id);
109 
110  if(find_it != id_map_.end() && !find_it->second.expired()) {
111  DBG_EH << "ignoring event handler for name='" << name << "' with id '" << id << "'\n";
112  return;
113  }
114  }
115 
116  if(name.empty() && id.empty()) {
117  lg::log_to_chat() << "[event] is missing name or id field\n";
118  ERR_WML << "[event] is missing name or id field";
119  return;
120  }
121 
122  // Make a copy of the event cfg here in order to do some standardization on the
123  // name field. Will be moved into the handler.
124  config event_cfg = cfg;
125 
126  // Split the name field...
127  std::vector<std::string> standardized_names = utils::split(name);
128 
129  if(!name.empty()) {
130  // ...and standardize each one individually. This ensures they're all valid for by-name lookup.
131  for(std::string& single_name : standardized_names) {
132  if(!utils::might_contain_variables(single_name)) {
133  single_name = standardize_name(single_name);
134  }
135  }
136 
137  assert(!standardized_names.empty());
138 
139  // Write the new name back to the config.
140  name = utils::join(standardized_names);
141  event_cfg["name"] = name;
142  }
143 
144  // Create a new handler.
145  // Do note active_ holds the main shared_ptr, and the other three containers
146  // construct weak_ptrs from the shared one.
147  DBG_EH << "inserting event handler for name=" << name << " with id=" << id << "\n";
148  active_.emplace_back(new event_handler(std::move(event_cfg), is_menu_item, standardized_names));
149 
150  //
151  // !! event_cfg is invalid past this point! DO NOT USE!
152  //
153 
154  // File by name.
156  dynamic_.emplace_back(active_.back());
157  } else {
158  for(const std::string& single_name : standardized_names) {
159  by_name_[single_name].emplace_back(active_.back());
160  }
161  }
162 
163  // File by ID.
164  if(!id.empty()) {
165  id_map_[id] = active_.back();
166  }
167 
168  log_handlers();
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 << "\n";
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::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
static std::string standardize_name(const std::string &name)
Utility to standardize the event names used in by_name_.
handler_list & get(const std::string &name)
Access to the handlers with fixed event names, by event name.
void clean_up_expired_handlers(const std::string &event_name)
Removes all expired event handlers and any weak_ptrs to them.
#define h
id_map_t id_map_
Allows quick locating of handlers by id.
static lg::log_domain log_wml("wml")
void remove_event_handler(const std::string &id)
Removes an event handler, identified by its ID.
handler_list dynamic_
Active event handlers with variables in their event names.
static lg::log_domain log_engine("engine")
std::weak_ptr< event_handler > weak_handler_ptr
Definition: fwd.hpp:26
handler_queue_t active_
Active event handlers.
logger & debug()
Definition: log.cpp:95
Domain specific events.
Definition: action_wml.cpp:87
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
void add_event_handler(const config &cfg, bool is_menu_item=false)
Adds an event handler.
mock_party p
map_t by_name_
Active event handlers with fixed event names, organized by event name.
bool might_contain_variables(const std::string &str)
Determines if a string might contain variables to interpolate.
std::list< weak_handler_ptr > handler_list
Definition: fwd.hpp:27
Define the handlers for the game&#39;s events mechanism.
std::vector< std::string > split(const config_attribute_value &val)
Standard logging facilities (interface).
void trim(std::string_view &s)
#define ERR_WML
static lg::log_domain log_event_handler("event_handler")
retval
Default window/dialog return values.
Definition: retval.hpp:29
std::shared_ptr< event_handler > handler_ptr
Definition: fwd.hpp:25
const handler_ptr get_event_handler_by_id(const std::string &id)
Gets an event handler, identified by its ID.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
bool empty() const
Definition: config.cpp:941
#define DBG_EH
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:289