Game Events: more formatting and doc cleanup

This commit is contained in:
Charles Dang 2017-05-21 05:33:23 +11:00
parent d41396ac32
commit c04e289c73
4 changed files with 343 additions and 260 deletions

View file

@ -35,7 +35,6 @@
#include <iostream>
static lg::log_domain log_engine("engine");
#define DBG_NG LOG_STREAM(debug, log_engine)
#define LOG_NG LOG_STREAM(info, log_engine)
@ -44,10 +43,9 @@ static lg::log_domain log_engine("engine");
static lg::log_domain log_event_handler("event_handler");
#define DBG_EH LOG_STREAM(debug, log_event_handler)
// This file is in the game_events namespace.
namespace game_events {
namespace game_events
{
/* ** handler_list::iterator ** */
/**
@ -57,29 +55,30 @@ namespace game_events {
handler_ptr handler_list::iterator::operator*()
{
// Check for an available handler.
while ( iter_.derefable() ) {
while(iter_.derefable()) {
// Handler still accessible?
if ( handler_ptr lock = iter_->lock() )
if(handler_ptr lock = iter_->lock()) {
return lock;
else
} else {
// Remove the now-defunct entry.
iter_ = list_t::erase(iter_);
}
}
// End of the list.
return handler_ptr();
}
/* ** event_handler ** */
event_handler::event_handler(const config &cfg, bool imi, handler_vec::size_type index, manager & man)
event_handler::event_handler(const config& cfg, bool imi, handler_vec::size_type index, manager& man)
: first_time_only_(cfg["first_time_only"].to_bool(true))
, is_menu_item_(imi)
, index_(index)
, man_(&man)
, cfg_(cfg)
{}
{
}
/**
* Disables *this, removing it from the game.
@ -93,8 +92,10 @@ void event_handler::disable()
{
assert(man_);
assert(man_->event_handlers_);
// Handlers must have an index after they're created.
assert ( index_ < man_->event_handlers_->size() );
assert(index_ < man_->event_handlers_->size());
// Disable this handler.
(*man_->event_handlers_)[index_].reset();
}
@ -108,20 +109,21 @@ void event_handler::disable()
* @param[in,out] handler_p The caller's smart pointer to *this. It may be
* reset() during processing.
*/
void event_handler::handle_event(const queued_event& event_info, handler_ptr& handler_p, game_lua_kernel & lk)
void event_handler::handle_event(const queued_event& event_info, handler_ptr& handler_p, game_lua_kernel& lk)
{
// We will need our config after possibly self-destructing. Make a copy now.
// TODO: instead of copying possibly huge config objects we should use shared things and only increase a refcount here.
// TODO: instead of copying possibly huge config objects we should use shared things and only increase a refcount
// here.
vconfig vcfg(cfg_, true);
if (is_menu_item_) {
if(is_menu_item_) {
DBG_NG << cfg_["name"] << " will now invoke the following command(s):\n" << cfg_;
}
if (first_time_only_)
{
if(first_time_only_) {
// Disable this handler.
disable();
// Also remove our caller's hold on us.
handler_p.reset();
}
@ -131,20 +133,23 @@ void event_handler::handle_event(const queued_event& event_info, handler_ptr& ha
sound::commit_music_changes();
}
bool event_handler::matches_name(const std::string &name, const game_data * gd) const
bool event_handler::matches_name(const std::string& name, const game_data* gd) const
{
const std::string my_names = !gd ? cfg_["name"].str() :
utils::interpolate_variables_into_string(cfg_["name"], *gd);
std::string::const_iterator itor,
it_begin = my_names.begin(),
it_end = my_names.end(),
match_it = name.begin(),
match_begin = name.begin(),
match_end = name.end();
const std::string my_names = !gd
? cfg_["name"].str()
: utils::interpolate_variables_into_string(cfg_["name"], *gd);
std::string::const_iterator
itor, it_begin = my_names.begin(),
it_end = my_names.end(),
match_it = name.begin(),
match_begin = name.begin(),
match_end = name.end();
int skip_count = 0;
for(itor = it_begin; itor != it_end; ++itor) {
bool do_eat = false,
do_skip = false;
bool do_eat = false, do_skip = false;
switch(*itor) {
case ',':
if(itor - it_begin - skip_count == match_it - match_begin && match_it == match_end) {
@ -171,6 +176,7 @@ bool event_handler::matches_name(const std::string &name, const game_data * gd)
do_eat = (match_it != match_end && *match_it == *itor);
break;
}
if(do_eat) {
++match_it;
} else if(do_skip) {
@ -185,11 +191,12 @@ bool event_handler::matches_name(const std::string &name, const game_data * gd)
skip_count = 0;
}
}
if(itor - it_begin - skip_count == match_it - match_begin && match_it == match_end) {
return true;
}
return false;
}
} // end namespace game_events

View file

@ -35,110 +35,149 @@ class game_lua_kernel;
namespace game_events
{
struct queued_event;
class event_handler; // Defined a few lines down.
class manager;
struct queued_event;
class event_handler; // Defined a few lines down.
class manager;
/// Shared pointer to handler objects.
typedef std::shared_ptr<event_handler> handler_ptr;
/// Storage of event handlers.
typedef std::vector<handler_ptr> handler_vec;
/// Shared pointer to handler objects.
typedef std::shared_ptr<event_handler> handler_ptr;
/// Storage of event handlers.
typedef std::vector<handler_ptr> handler_vec;
class event_handler
class event_handler
{
public:
event_handler(const config& cfg, bool is_menu_item, handler_vec::size_type index, manager&);
/** The index of *this should only be of interest when controlling iterations. */
handler_vec::size_type index() const
{
public:
event_handler(const config &cfg, bool is_menu_item,
handler_vec::size_type index, manager &);
return index_;
}
/// The index of *this should only be of interest when controlling iterations.
handler_vec::size_type index() const { return index_; }
bool matches_name(const std::string& name, const game_data* data) const;
bool matches_name(const std::string& name, const game_data * data) const;
bool is_menu_item() const { return is_menu_item_; }
/// Disables *this, removing it from the game.
void disable();
void handle_event(const queued_event& event_info, handler_ptr& handler_p, game_lua_kernel &);
const config &get_config() const { return cfg_; }
private:
bool first_time_only_;
bool is_menu_item_;
handler_vec::size_type index_;
manager * man_;
config cfg_;
};
/// This is a wrapper for a list of weak pointers to handlers. It allows forward
/// iterations of the list, with each element returned as a shared pointer.
/// (Weak pointers that fail to lock are silently removed from the list.) These
/// iterations can be used recursively, even when the innermost iteration might
/// erase arbitrary elements from the list.
///
/// The interface is not the standard list interface because that would be
/// inconvenient. The functionality implemented is that required by Wesnoth.
class handler_list
bool is_menu_item() const
{
/// The weak pointers that are used internally.
typedef std::weak_ptr<event_handler> internal_ptr;
/// The underlying list.
typedef utils::smart_list<internal_ptr> list_t;
return is_menu_item_;
}
public: // types
/// Handler list iterators are rather limited. They can be constructed
/// from a reference iterator (not default constructed), incremented,
/// and dereferenced. Consecutive dereferences are not guaranteed to
/// return the same element (if the list mutates between them, the next
/// element might be returned). An increment guarantees that the next
/// dereference will differ from the previous (unless at the end of the
/// list). The end of the list is indicated by dereferencing to a null
/// pointer.
class iterator
/** Disables *this, removing it from the game. */
void disable();
void handle_event(const queued_event& event_info, handler_ptr& handler_p, game_lua_kernel&);
const config& get_config() const
{
return cfg_;
}
private:
bool first_time_only_;
bool is_menu_item_;
handler_vec::size_type index_;
manager* man_;
config cfg_;
};
/**
* This is a wrapper for a list of weak pointers to handlers. It allows forward
* iterations of the list, with each element returned as a shared pointer.
* (Weak pointers that fail to lock are silently removed from the list.) These
* iterations can be used recursively, even when the innermost iteration might
* erase arbitrary elements from the list.
*
* The interface is not the standard list interface because that would be
* inconvenient. The functionality implemented is that required by Wesnoth.
*/
class handler_list
{
/// The weak pointers that are used internally.
typedef std::weak_ptr<event_handler> internal_ptr;
/// The underlying list.
typedef utils::smart_list<internal_ptr> list_t;
public:
/**
* Handler list iterators are rather limited. They can be constructed
* from a reference iterator (not default constructed), incremented,
* and dereferenced. Consecutive dereferences are not guaranteed to
* return the same element (if the list mutates between them, the next
* element might be returned). An increment guarantees that the next
* dereference will differ from the previous (unless at the end of the
* list). The end of the list is indicated by dereferencing to a null
* pointer.
*/
class iterator
{
/// The current element.
list_t::iterator iter_;
public:
/// Initialized constructor (to be called by handler_list).
explicit iterator(const list_t::iterator& base_iter)
: iter_(base_iter)
{
/// The current element.
list_t::iterator iter_;
}
public:
/// Initialized constructor (to be called by handler_list).
explicit iterator(const list_t::iterator & base_iter) :
iter_(base_iter)
{}
/// Increment.
iterator & operator++() { ++iter_; return *this; }
/// Dereference.
handler_ptr operator*();
};
friend class iterator;
typedef iterator const_iterator;
public: // functions
/// Default constructor.
/// Note: This explicit definition is required (by the more pedantic
/// compilers) in order to declare a default-constructed, static,
/// and const variable in event_handlers::get(), in handlers.cpp.
handler_list() : data_() {}
const_iterator begin() const { return iterator(const_cast<list_t &>(data_).begin()); }
// The above const_cast is so the iterator can remove obsolete entries.
const_iterator end() const { return iterator(const_cast<list_t &>(data_).end()); }
// push_front() is probably unneeded, but I'll leave the code here, just in case.
// (These lists must be maintained in index order, which means pushing to the back.)
void push_front(const handler_ptr & p) { data_.push_front(internal_ptr(p)); }
void push_back(const handler_ptr & p) { data_.push_back(internal_ptr(p)); }
void clear() { data_.clear(); }
private:
/// No implementation of operator=() since smart_list does not support it.
handler_list & operator=(const handler_list &);
/// The actual list.
list_t data_;
/// Increment.
iterator& operator++()
{
++iter_;
return *this;
}
/// Dereference.
handler_ptr operator*();
};
friend class iterator;
typedef iterator const_iterator;
public:
/**
* Default constructor.
* Note: This explicit definition is required (by the more pedantic
* compilers) in order to declare a default-constructed, static,
* and const variable in event_handlers::get(), in handlers.cpp.
*/
handler_list()
: data_()
{
}
const_iterator begin() const
{
return iterator(const_cast<list_t&>(data_).begin());
}
// The above const_cast is so the iterator can remove obsolete entries.
const_iterator end() const
{
return iterator(const_cast<list_t&>(data_).end());
}
// push_front() is probably unneeded, but I'll leave the code here, just in case.
// (These lists must be maintained in index order, which means pushing to the back.)
void push_front(const handler_ptr& p)
{
data_.push_front(internal_ptr(p));
}
void push_back(const handler_ptr& p)
{
data_.push_back(internal_ptr(p));
}
void clear()
{
data_.clear();
}
private:
/// No implementation of operator=() since smart_list does not support it.
handler_list& operator=(const handler_list&);
/// The actual list.
list_t data_;
};
}

View file

@ -27,108 +27,116 @@ class game_display;
class game_data;
class unit_map;
namespace game_events {
class wml_event_pump;
class event_handlers;
/// The game event manager loads the scenario configuration object,
/// and ensures that events are handled according to the
/// scenario configuration for its lifetime.
///
/// Thus, a manager object should be created when a scenario is played,
/// and destroyed at the end of the scenario.
/// If a second manager object is created before destroying the previous
/// one, the game will crash with an assertion failure.
class manager {
private:
/// This class is similar to an input iterator through event handlers,
/// except each instance knows its own end (determined when constructed).
/// Subsequent dereferences are not guaranteed to return the same element,
/// so it is important to assign a dereference to a variable if you want
/// to use it more than once. On the other hand, a dereference will not
/// return a null pointer until the end of the iteration is reached (and
/// this is how to detect the end of the iteration).
///
/// For simplicity, this class is neither assignable nor equality
/// comparable nor default constructable, and there is no postincrement.
/// Typedefs are also skipped.
class iteration
{
public:
/// Event-specific constructor.
explicit iteration(const std::string & event_name, manager &);
// Increment:
iteration & operator++();
// Dereference:
handler_ptr operator*();
private: // functions
/// Gets the index from a pointer, capped at end_.
handler_vec::size_type ptr_index(const handler_ptr & ptr) const
{ return !bool(ptr) ? end_ : std::min(ptr->index(), end_); }
private: // data
/// The fixed-name event handlers for this iteration.
const handler_list & main_list_;
/// The varying-name event handlers for this iteration.
const handler_list & var_list_;
/// The event name for this iteration.
const std::string event_name_;
/// The end of this iteration. We intentionally exclude handlers
/// added after *this is constructed.
const handler_vec::size_type end_;
/// Set to true upon dereferencing.
bool current_is_known_;
/// true if the most recent dereference was taken from main_list_.
bool main_is_current_;
/// The current (or next) element from main_list_.
handler_list::iterator main_it_;
/// The current (or next) element from var_list_.
handler_list::iterator var_it_;
game_data * gamedata_;
};
// Performs an assertion check to ensure these members are not null.
friend void event_handler::disable();
const std::unique_ptr<event_handlers> event_handlers_;
std::set<std::string> unit_wml_ids_;
const std::unique_ptr<game_events::wml_event_pump> pump_;
game_events::wmi_manager wml_menu_items_;
namespace game_events
{
class wml_event_pump;
class event_handlers;
/**
* The game event manager loads the scenario configuration object,
* and ensures that events are handled according to the
* scenario configuration for its lifetime.
*
* Thus, a manager object should be created when a scenario is played,
* and destroyed at the end of the scenario.
* If a second manager object is created before destroying the previous
* one, the game will crash with an assertion failure.
*/
class manager
{
private:
/**
* This class is similar to an input iterator through event handlers,
* except each instance knows its own end (determined when constructed).
* Subsequent dereferences are not guaranteed to return the same element,
* so it is important to assign a dereference to a variable if you want
* to use it more than once. On the other hand, a dereference will not
* return a null pointer until the end of the iteration is reached (and
* this is how to detect the end of the iteration).
*
* For simplicity, this class is neither assignable nor equality
* comparable nor default constructable, and there is no postincrement.
* Typedefs are also skipped.
*/
class iteration
{
public:
manager(const manager&) = delete;
manager& operator=(const manager&) = delete;
/// Event-specific constructor.
explicit iteration(const std::string& event_name, manager&);
explicit manager();
void read_scenario(const config& scenario_cfg);
~manager();
// Increment:
iteration& operator++();
// Dereference:
handler_ptr operator*();
/// Create an event handler.
void add_event_handler(const config & handler, bool is_menu_item=false);
/// Removes an event handler.
void remove_event_handler(const std::string & id);
/// Gets an event handler by ID
const handler_ptr get_event_handler_by_id(const std::string & id);
void add_events(const config::const_child_itors &cfgs,
const std::string& type = std::string());
void write_events(config& cfg) const;
using event_func_t = std::function<void(game_events::manager&, handler_ptr)>;
void execute_on_events(const std::string& event_id, event_func_t func);
game_events::wml_event_pump & pump();
game_events::wmi_manager& wml_menu_items()
private:
/// Gets the index from a pointer, capped at end_.
handler_vec::size_type ptr_index(const handler_ptr& ptr) const
{
return wml_menu_items_;
return !bool(ptr) ? end_ : std::min(ptr->index(), end_);
}
private:
/// The fixed-name event handlers for this iteration.
const handler_list& main_list_;
/// The varying-name event handlers for this iteration.
const handler_list& var_list_;
/// The event name for this iteration.
const std::string event_name_;
/// The end of this iteration. We intentionally exclude handlers
/// added after *this is constructed.
const handler_vec::size_type end_;
/// Set to true upon dereferencing.
bool current_is_known_;
/// true if the most recent dereference was taken from main_list_.
bool main_is_current_;
/// The current (or next) element from main_list_.
handler_list::iterator main_it_;
/// The current (or next) element from var_list_.
handler_list::iterator var_it_;
game_data* gamedata_;
};
// Performs an assertion check to ensure these members are not null.
friend void event_handler::disable();
const std::unique_ptr<event_handlers> event_handlers_;
std::set<std::string> unit_wml_ids_;
const std::unique_ptr<game_events::wml_event_pump> pump_;
game_events::wmi_manager wml_menu_items_;
public:
manager(const manager&) = delete;
manager& operator=(const manager&) = delete;
explicit manager();
void read_scenario(const config& scenario_cfg);
~manager();
/** Create an event handler. */
void add_event_handler(const config& handler, bool is_menu_item = false);
/** Removes an event handler. */
void remove_event_handler(const std::string& id);
/** Gets an event handler by ID */
const handler_ptr get_event_handler_by_id(const std::string& id);
void add_events(const config::const_child_itors& cfgs, const std::string& type = std::string());
void write_events(config& cfg) const;
using event_func_t = std::function<void(game_events::manager&, handler_ptr)>;
void execute_on_events(const std::string& event_id, event_func_t func);
game_events::wml_event_pump& pump();
game_events::wmi_manager& wml_menu_items()
{
return wml_menu_items_;
}
};
}

View file

@ -19,54 +19,83 @@
#include <memory>
#include <unordered_map>
namespace game_events {
namespace game_events
{
// event_handlers is essentially the implementation details of the manager
class event_handlers
{
private:
typedef std::unordered_map<std::string, handler_list> map_t;
typedef std::unordered_map<std::string, std::weak_ptr<event_handler>> id_map_t;
//event_handlers is essentially the implementation details of the manager
class event_handlers {
private:
typedef std::unordered_map<std::string, handler_list> map_t;
typedef std::unordered_map<std::string, std::weak_ptr<event_handler> > id_map_t;
/**
* Active event handlers. Will not have elements removed unless the event_handlers is clear()ed.
* This is the only container that actually 'owns' any events in the form of shared_ptrs. The other
* three storage methods own weak_ptrs.
*/
handler_vec active_;
handler_vec active_; /// Active event handlers. Will not have elements removed unless the event_handlers is clear()ed.
map_t by_name_; /// Active event handlers with fixed event names, organized by event name.
handler_list dynamic_; /// Active event handlers with variables in their event names.
id_map_t id_map_; /// Allows quick locating of handlers by id.
/** Active event handlers with fixed event names, organized by event name. */
map_t by_name_;
void log_handlers();
/// Utility to standardize the event names used in by_name_.
static std::string standardize_name(const std::string & name);
/** Active event handlers with variables in their event names. */
handler_list dynamic_;
public:
// TODO: remove
typedef handler_vec::size_type size_type;
/** Allows quick locating of handlers by id. */
id_map_t id_map_;
event_handlers()
: active_()
, by_name_()
, dynamic_()
, id_map_()
{}
void log_handlers();
/// Read-only access to the handlers with varying event names.
const handler_list & get_dynamic() const { return dynamic_; }
/** Utility to standardize the event names used in by_name_. */
static std::string standardize_name(const std::string& name);
/// Read-only access to the active event handlers.
const handler_vec& get_active() const { return active_; }
public:
// TODO: remove
typedef handler_vec::size_type size_type;
/// Read-only access to the handlers with fixed event names, by event name.
const handler_list & get(const std::string & name) const;
event_handlers()
: active_()
, by_name_()
, dynamic_()
, id_map_()
{
}
/// Adds an event handler.
void add_event_handler(const config & cfg, manager & man, bool is_menu_item=false);
/// Removes an event handler, identified by its ID.
void remove_event_handler(const std::string& id);
/// Gets an event handler, identified by its ID.
const handler_ptr get_event_handler_by_id(const std::string & id);
/** Read-only access to the handlers with varying event names. */
const handler_list& get_dynamic() const
{
return dynamic_;
}
/// The number of active event handlers.
size_type size() const { return active_.size(); }
/// Access to active event handlers by index.
handler_ptr & operator[](size_type index) { return active_[index]; }
};//event_handlers
/** Read-only access to the active event handlers. Essentially gives all events. */
const handler_vec& get_active() const
{
return active_;
}
} //end namespace game_events
/** Read-only access to the handlers with fixed event names, by event name. */
const handler_list& get(const std::string& name) const;
/** Adds an event handler. */
void add_event_handler(const config& cfg, manager& man, bool is_menu_item = false);
/** Removes an event handler, identified by its ID. */
void remove_event_handler(const std::string& id);
/** Gets an event handler, identified by its ID. */
const handler_ptr get_event_handler_by_id(const std::string& id);
/** The number of active event handlers. */
size_type size() const
{
return active_.size();
}
/** Access to active event handlers by index. */
handler_ptr& operator[](size_type index)
{
return active_[index];
}
};
} // end namespace game_events