The Battle for Wesnoth  1.15.0-dev
manager_impl.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
16 
17 #include "game_events/handlers.hpp"
18 #include "formula/string_utils.hpp"
19 #include "log.hpp"
20 
21 #include <boost/algorithm/string.hpp>
22 
23 #include <iostream>
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 DBG_EH LOG_STREAM(debug, log_event_handler)
32 
33 namespace game_events
34 {
36 {
37  if(lg::debug().dont_log("event_handler")) {
38  return;
39  }
40 
41  std::stringstream ss;
42 
43  for(const handler_ptr& h : active_) {
44  if(!h) {
45  continue;
46  }
47 
48  const config& cfg = h->get_config();
49  ss << "name=" << cfg["name"] << ", with id=" << cfg["id"] << "; ";
50  }
51 
52  DBG_EH << "active handlers are now " << ss.str() << "\n";
53 }
54 
55 /**
56  * Utility to standardize the event names used in by_name_.
57  * This means stripping leading and trailing spaces, and converting internal
58  * spaces to underscores.
59  */
60 std::string event_handlers::standardize_name(const std::string& name)
61 {
62  std::string retval = name;
63 
64  // Trim leading and trailing spaces.
65  boost::trim(retval);
66 
67  // Replace internal spaces with underscores.
68  boost::replace_all(retval, " ", "_");
69 
70  return retval;
71 }
72 
73 /**
74  * Read-only access to the handlers with fixed event names, by event name.
75  */
76 handler_list& event_handlers::get(const std::string& name)
77 {
78  // Empty list for the "not found" case.
79  static handler_list empty_list;
80 
81  // Look for the name in the name map.
82  auto find_it = by_name_.find(standardize_name(name));
83  return find_it == by_name_.end() ? empty_list : find_it->second;
84 }
85 
86 /**
87  * Adds an event handler.
88  * An event with a nonempty ID will not be added if an event with that
89  * ID already exists.
90  */
91 void event_handlers::add_event_handler(const config& cfg, bool is_menu_item)
92 {
93  // Someone decided to register an empty event... bail.
94  if(cfg.empty()) {
95  return;
96  }
97 
98  std::string name = cfg["name"];
99  std::string id = cfg["id"];
100 
101  if(!id.empty()) {
102  // Ignore this handler if there is already one with this ID.
103  auto find_it = id_map_.find(id);
104 
105  if(find_it != id_map_.end() && !find_it->second.expired()) {
106  DBG_EH << "ignoring event handler for name='" << name << "' with id '" << id << "'\n";
107  return;
108  }
109  }
110 
111  if(name.empty() && id.empty()) {
112  lg::wml_error() << "[event] is missing name or id field\n";
113  return;
114  }
115 
116  // Make a copy of the event cfg here in order to do some standardization on the
117  // name field. Will be moved into the handler.
118  config event_cfg = cfg;
119 
120  // Split the name field...
121  std::vector<std::string> standardized_names = utils::split(name);
122 
123  if(!name.empty()) {
124  // ...and standardize each one individually. This ensures they're all valid for by-name lookup.
125  for(std::string& single_name : standardized_names) {
126  if(!utils::might_contain_variables(single_name)) {
127  single_name = standardize_name(single_name);
128  }
129  }
130 
131  assert(!standardized_names.empty());
132 
133  // Write the new name back to the config.
134  name = utils::join(standardized_names);
135  event_cfg["name"] = name;
136  }
137 
138  // Create a new handler.
139  // Do note active_ holds the main shared_ptr, and the other three containers
140  // construct weak_ptrs from the shared one.
141  DBG_EH << "inserting event handler for name=" << name << " with id=" << id << "\n";
142  active_.emplace_back(new event_handler(std::move(event_cfg), is_menu_item, standardized_names));
143 
144  //
145  // !! event_cfg is invalid past this point! DO NOT USE!
146  //
147 
148  // File by name.
150  dynamic_.emplace_back(active_.back());
151  } else {
152  for(const std::string& single_name : standardized_names) {
153  by_name_[single_name].emplace_back(active_.back());
154  }
155  }
156 
157  // File by ID.
158  if(!id.empty()) {
159  id_map_[id] = active_.back();
160  }
161 
162  log_handlers();
163 }
164 
165 /**
166  * Removes an event handler, identified by its ID.
167  * Events with empty IDs cannot be removed.
168  */
169 void event_handlers::remove_event_handler(const std::string& id)
170 {
171  if(id.empty()) {
172  return;
173  }
174 
175  DBG_EH << "removing event handler with id " << id << "\n";
176 
177  // Find the existing handler with this ID.
178  auto find_it = id_map_.find(id);
179  if(find_it != id_map_.end()) {
180  handler_ptr handler = find_it->second.lock();
181 
182  if(handler && !handler->disabled()) {
183  handler->disable();
184  }
185 
186  // Do this even if the lock failed.
187  id_map_.erase(find_it);
188 
189  // We don't delete the handler from the other lists just yet. This is to ensure
190  // the main handler list's size doesn't change when iterating over the handlers.
191  // Any disabled handlers don't get executed, and will be removed during the next
192  // cleanup pass.
193  }
194 
195  log_handlers();
196 }
197 
198 void event_handlers::clean_up_expired_handlers(const std::string& event_name)
199 {
200  // First, remove all disabled handlers from the main list.
201  auto to_remove = std::remove_if(active_.begin(), active_.end(),
202  [](handler_ptr p) { return p->disabled(); }
203  );
204 
205  active_.erase(to_remove, active_.end());
206 
207  // Then remove any now-unlockable weak_ptrs from the by-name list.
208  // Might be more than one so we split.
209  for(const std::string& name : utils::split(event_name)) {
210  get(name).remove_if(
211  [](weak_handler_ptr ptr) { return ptr.expired(); }
212  );
213  }
214 
215  // And finally remove any now-unlockable weak_ptrs from the with-variables name list.
216  dynamic_.remove_if(
217  [](weak_handler_ptr ptr) { return ptr.expired(); }
218  );
219 }
220 
222 {
223  auto find_it = id_map_.find(id);
224  if(find_it != id_map_.end() && !find_it->second.expired()) {
225  return find_it->second.lock();
226  }
227 
228  return nullptr;
229 }
230 
231 } // 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.
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
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:25
handler_queue_t active_
Active event handlers.
logger & debug()
Definition: log.cpp:96
Domain specific events.
Definition: action_wml.cpp:88
void add_event_handler(const config &cfg, bool is_menu_item=false)
Adds an event handler.
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:269
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:26
Define the handlers for the game&#39;s events mechanism.
Standard logging facilities (interface).
static lg::log_domain log_event_handler("event_handler")
retval
Default window/dialog return values.
Definition: retval.hpp:28
std::shared_ptr< event_handler > handler_ptr
Definition: fwd.hpp:24
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:68
bool empty() const
Definition: config.cpp:837
#define DBG_EH