fixup wml menu items: track the lifetime of the manager
testing revealed that assertions may fail regarding the lifetime of the manager of wml menu items in some cases. this commit adds a pointer to each which tracks its manager's life time and prevents dangling pointers.
This commit is contained in:
parent
d4440b34cd
commit
6fc1ac1bb2
6 changed files with 60 additions and 41 deletions
|
@ -32,6 +32,8 @@
|
|||
#include "util.hpp"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <iostream>
|
||||
|
||||
|
@ -99,6 +101,7 @@ manager::manager(const config& cfg, const boost::shared_ptr<t_context> & res)
|
|||
, used_items_()
|
||||
, pump_(new game_events::t_pump(*this, res))
|
||||
, resources_(res)
|
||||
, me_(boost::make_shared<manager * const>(this))
|
||||
{
|
||||
BOOST_FOREACH(const config &ev, cfg.child_range("event")) {
|
||||
add_event_handler(ev);
|
||||
|
@ -126,7 +129,7 @@ manager::manager(const config& cfg, const boost::shared_ptr<t_context> & res)
|
|||
}
|
||||
|
||||
// Create the event handlers for menu items.
|
||||
resources_->gamedata->get_wml_menu_items().init_handlers();
|
||||
resources_->gamedata->get_wml_menu_items().init_handlers(me_);
|
||||
}
|
||||
|
||||
manager::~manager() {}
|
||||
|
|
|
@ -115,6 +115,7 @@ namespace game_events {
|
|||
|
||||
boost::scoped_ptr<game_events::t_pump> pump_;
|
||||
boost::shared_ptr<t_context> resources_;
|
||||
boost::shared_ptr<manager* const> me_;
|
||||
|
||||
public:
|
||||
/// Note that references will be maintained,
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
#include "../preferences.hpp"
|
||||
#include "../replay.hpp"
|
||||
#include "../replay_helper.hpp"
|
||||
#include "../resources.hpp"
|
||||
#include "../synced_context.hpp"
|
||||
#include "../terrain_filter.hpp"
|
||||
|
||||
|
@ -92,7 +91,8 @@ wml_menu_item::wml_menu_item(const std::string& id, const config & cfg) :
|
|||
command_(cfg.child_or_empty("command")),
|
||||
default_hotkey_(cfg.child_or_empty("default_hotkey")),
|
||||
use_hotkey_(cfg["use_hotkey"].to_bool(true)),
|
||||
use_wml_menu_(cfg["use_hotkey"].str() != "only")
|
||||
use_wml_menu_(cfg["use_hotkey"].str() != "only"),
|
||||
my_manager_()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,8 @@ wml_menu_item::wml_menu_item(const std::string& id, const vconfig & definition,
|
|||
command_(original.command_),
|
||||
default_hotkey_(original.default_hotkey_),
|
||||
use_hotkey_(original.use_hotkey_),
|
||||
use_wml_menu_(original.use_wml_menu_)
|
||||
use_wml_menu_(original.use_wml_menu_),
|
||||
my_manager_()
|
||||
{
|
||||
// Apply WML.
|
||||
update(definition);
|
||||
|
@ -221,24 +222,29 @@ void wml_menu_item::fire_event(const map_location & event_hex, const game_data &
|
|||
void wml_menu_item::finish_handler() const
|
||||
{
|
||||
if ( !command_.empty() ) {
|
||||
assert(resources::game_events);
|
||||
resources::game_events->remove_event_handler(command_["id"]);
|
||||
if (boost::shared_ptr<manager * const> man = my_manager_.lock()) {
|
||||
(**man).remove_event_handler(command_["id"]);
|
||||
} else {
|
||||
LOG_NG << "Tried to finish handler for a wml menu item, but the manager could not be found.\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Hotkey support
|
||||
if ( use_hotkey_ )
|
||||
if ( use_hotkey_ ) {
|
||||
hotkey::remove_wml_hotkey(hotkey_id_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the implicit event handler for an inlined [command].
|
||||
*/
|
||||
void wml_menu_item::init_handler() const
|
||||
void wml_menu_item::init_handler(const boost::shared_ptr<manager * const> & man) const
|
||||
{
|
||||
// If this menu item has a [command], add a handler for it.
|
||||
if ( !command_.empty() ) {
|
||||
assert(resources::game_events);
|
||||
resources::game_events->add_event_handler(command_, true);
|
||||
assert(man);
|
||||
my_manager_ = man;
|
||||
(**man).add_event_handler(command_, true);
|
||||
}
|
||||
|
||||
// Hotkey support
|
||||
|
@ -349,37 +355,39 @@ void wml_menu_item::update(const vconfig & vcfg)
|
|||
*/
|
||||
void wml_menu_item::update_command(const config & new_command)
|
||||
{
|
||||
// If there is an old command, remove it from the event handlers.
|
||||
if ( !command_.empty() ) {
|
||||
assert(resources::game_events);
|
||||
manager::iteration iter(event_name_, *resources::game_events);
|
||||
while ( handler_ptr hand = *iter ) {
|
||||
if ( hand->is_menu_item() ) {
|
||||
LOG_NG << "Removing command for " << event_name_ << ".\n";
|
||||
resources::game_events->remove_event_handler(command_["id"].str());
|
||||
if (boost::shared_ptr<manager * const> man = my_manager_.lock()) {
|
||||
// If there is an old command, remove it from the event handlers.
|
||||
if ( !command_.empty() ) {
|
||||
manager::iteration iter(event_name_, **man);
|
||||
while ( handler_ptr hand = *iter ) {
|
||||
if ( hand->is_menu_item() ) {
|
||||
LOG_NG << "Removing command for " << event_name_ << ".\n";
|
||||
(**man).remove_event_handler(command_["id"].str());
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
// Update our stored command.
|
||||
if ( new_command.empty() )
|
||||
command_.clear();
|
||||
else {
|
||||
command_ = new_command;
|
||||
// Update our stored command.
|
||||
if ( new_command.empty() )
|
||||
command_.clear();
|
||||
else {
|
||||
command_ = new_command;
|
||||
|
||||
// Set some fields required by event processing.
|
||||
config::attribute_value & event_id = command_["id"];
|
||||
if ( event_id.empty() && !item_id_.empty() ) {
|
||||
event_id = item_id_;
|
||||
// Set some fields required by event processing.
|
||||
config::attribute_value & event_id = command_["id"];
|
||||
if ( event_id.empty() && !item_id_.empty() ) {
|
||||
event_id = item_id_;
|
||||
}
|
||||
command_["name"] = event_name_;
|
||||
command_["first_time_only"] = false;
|
||||
|
||||
// Register the event.
|
||||
LOG_NG << "Setting command for " << event_name_ << " to:\n" << command_;
|
||||
(**man).add_event_handler(command_, true);
|
||||
}
|
||||
command_["name"] = event_name_;
|
||||
command_["first_time_only"] = false;
|
||||
|
||||
// Register the event.
|
||||
LOG_NG << "Setting command for " << event_name_ << " to:\n" << command_;
|
||||
assert(resources::game_events);
|
||||
resources::game_events->add_event_handler(command_, true);
|
||||
} else {
|
||||
ERR_NG << "Tried to set a command for a menu item, but the manager could not be found\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
#include "../tstring.hpp"
|
||||
#include "../variable.hpp"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/weak_ptr.hpp>
|
||||
|
||||
class filter_context;
|
||||
class game_data;
|
||||
struct map_location;
|
||||
|
@ -31,6 +34,7 @@ class unit_map;
|
|||
|
||||
namespace game_events
|
||||
{
|
||||
class manager;
|
||||
|
||||
class wml_menu_item
|
||||
{
|
||||
|
@ -57,7 +61,7 @@ public:
|
|||
/// Removes the implicit event handler for an inlined [command].
|
||||
void finish_handler() const;
|
||||
/// Initializes the implicit event handler for an inlined [command].
|
||||
void init_handler() const;
|
||||
void init_handler(const boost::shared_ptr<manager * const> &) const;
|
||||
/// The text to put in a menu for this item.
|
||||
/// This will be either translated text or a hotkey identifier.
|
||||
std::string menu_text() const
|
||||
|
@ -98,6 +102,9 @@ private: // Data
|
|||
bool use_hotkey_;
|
||||
/// If true, allow using the menu to trigger this item.
|
||||
bool use_wml_menu_;
|
||||
|
||||
/// A link back to my manager
|
||||
mutable boost::weak_ptr<manager * const> my_manager_;
|
||||
};
|
||||
|
||||
} // end namespace game_events
|
||||
|
|
|
@ -133,7 +133,7 @@ std::vector<std::pair<boost::shared_ptr<const wml_menu_item>, std::string> > wmi
|
|||
/**
|
||||
* Initializes the implicit event handlers for inlined [command]s.
|
||||
*/
|
||||
void wmi_container::init_handlers() const
|
||||
void wmi_container::init_handlers(const boost::shared_ptr<manager * const> & man) const
|
||||
{
|
||||
// Applying default hotkeys here currently does not work because
|
||||
// the hotkeys are reset by play_controler::init_managers() ->
|
||||
|
@ -149,7 +149,7 @@ void wmi_container::init_handlers() const
|
|||
// Loop through each menu item.
|
||||
BOOST_FOREACH( const item_ptr & wmi, *this ) {
|
||||
// If this menu item has a [command], add a handler for it.
|
||||
wmi->init_handler();
|
||||
wmi->init_handler(man);
|
||||
// Count the menu items (for the diagnostic message).
|
||||
++wmi_count;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ class vconfig;
|
|||
namespace game_events
|
||||
{
|
||||
class wml_menu_item;
|
||||
|
||||
class manager;
|
||||
|
||||
/// A container of wml_menu_item.
|
||||
class wmi_container{
|
||||
|
@ -85,7 +85,7 @@ public:
|
|||
return get_items(hex, gamedata, fc, units, begin(), end());
|
||||
}
|
||||
/// Initializes the implicit event handlers for inlined [command]s.
|
||||
void init_handlers() const;
|
||||
void init_handlers(const boost::shared_ptr<manager * const> &) const;
|
||||
void to_config(config& cfg) const;
|
||||
/// Updates or creates (as appropriate) the menu item with the given @a id.
|
||||
void set_item(const std::string& id, const vconfig& menu_item);
|
||||
|
|
Loading…
Add table
Reference in a new issue