The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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"
21 
22 #include <boost/algorithm/string.hpp>
23 
24 #include <iostream>
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 DBG_EH LOG_STREAM(debug, log_event_handler)
33 
34 namespace game_events
35 {
37 {
38  if(lg::debug().dont_log("event_handler")) {
39  return;
40  }
41 
42  std::stringstream ss;
43 
44  for(const handler_ptr& h : active_) {
45  if(!h) {
46  continue;
47  }
48 
49  const config& cfg = h->get_config();
50  ss << "name=" << cfg["name"] << ", with id=" << cfg["id"] << "; ";
51  }
52 
53  DBG_EH << "active handlers are now " << ss.str() << "\n";
54 }
55 
56 /**
57  * Utility to standardize the event names used in by_name_.
58  * This means stripping leading and trailing spaces, and converting internal
59  * spaces to underscores.
60  */
62 {
63  std::string retval = name;
64 
65  // Trim leading and trailing spaces.
66  boost::trim(retval);
67 
68  // Replace internal spaces with underscores.
69  boost::replace_all(retval, " ", "_");
70 
71  return retval;
72 }
73 
74 /**
75  * Read-only access to the handlers with fixed event names, by event name.
76  */
78 {
79  // Empty list for the "not found" case.
80  static handler_list empty_list;
81 
82  // Look for the name in the name map.
83  auto find_it = by_name_.find(standardize_name(name));
84  return find_it == by_name_.end() ? empty_list : find_it->second;
85 }
86 
87 /**
88  * Adds an event handler.
89  * An event with a nonempty ID will not be added if an event with that
90  * ID already exists.
91  */
92 void event_handlers::add_event_handler(const config& cfg, bool is_menu_item)
93 {
94  // Someone decided to register an empty event... bail.
95  if(cfg.empty()) {
96  return;
97  }
98 
99  std::string name = cfg["name"];
100  std::string id = cfg["id"];
101 
102  if(!id.empty()) {
103  // Ignore this handler if there is already one with this ID.
104  auto find_it = id_map_.find(id);
105 
106  if(find_it != id_map_.end() && !find_it->second.expired()) {
107  DBG_EH << "ignoring event handler for name='" << name << "' with id '" << id << "'\n";
108  return;
109  }
110  }
111 
112  if(name.empty()) {
113  lg::wml_error() << "[event] is missing name field\n";
114  return;
115  }
116 
117  // Make a copy of the event cfg here in order to do some standardization on the
118  // name field. Will be moved into the handler.
119  config event_cfg = cfg;
120 
121  // Split the name field...
122  std::vector<std::string> standardized_names = utils::split(name);
123 
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  // Create a new handler.
138  // Do note active_ holds the main shared_ptr, and the other three containers
139  // construct weak_ptrs from the shared one.
140  DBG_EH << "inserting event handler for name=" << name << " with id=" << id << "\n";
141  active_.emplace_back(new event_handler(std::move(event_cfg), is_menu_item, standardized_names));
142 
143  //
144  // !! event_cfg is invalid past this point! DO NOT USE!
145  //
146 
147  // File by name.
149  dynamic_.emplace_back(active_.back());
150  } else {
151  for(const std::string& single_name : standardized_names) {
152  by_name_[single_name].emplace_back(active_.back());
153  }
154  }
155 
156  // File by ID.
157  if(!id.empty()) {
158  id_map_[id] = active_.back();
159  }
160 
161  log_handlers();
162 }
163 
164 /**
165  * Removes an event handler, identified by its ID.
166  * Events with empty IDs cannot be removed.
167  */
169 {
170  if(id.empty()) {
171  return;
172  }
173 
174  DBG_EH << "removing event handler with id " << id << "\n";
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 
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< char_t > string
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
bool empty() const
Definition: config.cpp:830
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:97
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's events mechanism.
Standard logging facilities (interface).
static const char * name(const std::vector< SDL_Joystick * > &joysticks, const size_t index)
Definition: joystick.cpp:48
static lg::log_domain log_event_handler("event_handler")
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:93
#define DBG_EH