EventManager: TICK events should unregister correctly when requested and after they fire, other tweaks.

- fixed unregister to allow unregistering individual TICK events
 - make registerTick return the resulting freq you have to put back in to the handler in order for it to be unregisterable
- when TICK events fire, they now automatically remove themselves from handlers[TICK], as they should
- make sure manageTickEvent fires once per tick
develop
expwnent 2013-10-20 22:49:22 -04:00
parent 3594dc2eb8
commit c409289ee4
3 changed files with 91 additions and 47 deletions

@ -37,10 +37,10 @@ namespace DFHack {
EventHandler(callback_t eventHandlerIn, int32_t freqIn): eventHandler(eventHandlerIn), freq(freqIn) { EventHandler(callback_t eventHandlerIn, int32_t freqIn): eventHandler(eventHandlerIn), freq(freqIn) {
} }
bool operator==(EventHandler& handle) const { bool operator==(const EventHandler& handle) const {
return eventHandler == handle.eventHandler && freq == handle.freq; return eventHandler == handle.eventHandler && freq == handle.freq;
} }
bool operator!=(EventHandler& handle) const { bool operator!=(const EventHandler& handle) const {
return !( *this == handle); return !( *this == handle);
} }
}; };
@ -59,7 +59,7 @@ namespace DFHack {
};*/ };*/
DFHACK_EXPORT void registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin); DFHACK_EXPORT void registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin);
DFHACK_EXPORT void registerTick(EventHandler handler, int32_t when, Plugin* plugin, bool absolute=false); DFHACK_EXPORT int32_t registerTick(EventHandler handler, int32_t when, Plugin* plugin, bool absolute=false);
DFHACK_EXPORT void unregister(EventType::EventType e, EventHandler handler, Plugin* plugin); DFHACK_EXPORT void unregister(EventType::EventType e, EventHandler handler, Plugin* plugin);
DFHACK_EXPORT void unregisterAll(Plugin* plugin); DFHACK_EXPORT void unregisterAll(Plugin* plugin);
void manageEvents(color_ostream& out); void manageEvents(color_ostream& out);
@ -79,6 +79,16 @@ namespace std {
return r; return r;
} }
}; };
template <>
struct hash<DFHack::EventManager::EventHandler> {
std::size_t operator()(const DFHack::EventManager::EventHandler& h) const {
size_t r = 17;
const size_t m = 65537;
r = m*(r+(int32_t)h.eventHandler);
r = m*(r+h.freq);
return r;
}
};
} }
#endif #endif

@ -49,7 +49,7 @@ void DFHack::EventManager::registerListener(EventType::EventType e, EventHandler
handlers[e].insert(pair<Plugin*, EventHandler>(plugin, handler)); handlers[e].insert(pair<Plugin*, EventHandler>(plugin, handler));
} }
void DFHack::EventManager::registerTick(EventHandler handler, int32_t when, Plugin* plugin, bool absolute) { int32_t DFHack::EventManager::registerTick(EventHandler handler, int32_t when, Plugin* plugin, bool absolute) {
if ( !absolute ) { if ( !absolute ) {
df::world* world = df::global::world; df::world* world = df::global::world;
if ( world ) { if ( world ) {
@ -62,19 +62,40 @@ void DFHack::EventManager::registerTick(EventHandler handler, int32_t when, Plug
handler.freq = when; handler.freq = when;
tickQueue.insert(pair<int32_t, EventHandler>(handler.freq, handler)); tickQueue.insert(pair<int32_t, EventHandler>(handler.freq, handler));
handlers[EventType::TICK].insert(pair<Plugin*,EventHandler>(plugin,handler)); handlers[EventType::TICK].insert(pair<Plugin*,EventHandler>(plugin,handler));
return when;
}
static void removeFromTickQueue(EventHandler getRidOf) {
//shenanigans to avoid concurrent modification
bool didSomething;
do {
didSomething = false;
for ( auto j = tickQueue.find(getRidOf.freq); j != tickQueue.end(); j++ ) {
if ( (*j).first > getRidOf.freq )
break;
if ( (*j).second != getRidOf )
continue;
tickQueue.erase(j);
didSomething = true;
break;
}
} while (didSomething);
} }
void DFHack::EventManager::unregister(EventType::EventType e, EventHandler handler, Plugin* plugin) { void DFHack::EventManager::unregister(EventType::EventType e, EventHandler handler, Plugin* plugin) {
for ( multimap<Plugin*, EventHandler>::iterator i = handlers[e].find(plugin); i != handlers[e].end(); i++ ) { for ( auto i = handlers[e].find(plugin); i != handlers[e].end(); ) {
if ( (*i).first != plugin ) if ( (*i).first != plugin )
break; break;
EventHandler handle = (*i).second; EventHandler handle = (*i).second;
if ( handle == handler ) { if ( handle != handler ) {
handlers[e].erase(i); i++;
break; continue;
} }
handlers[e].erase(i);
if ( e == EventType::TICK )
removeFromTickQueue(handler);
i = handlers[e].find(plugin); //loop in case the same handler is in there multiple times
} }
return;
} }
void DFHack::EventManager::unregisterAll(Plugin* plugin) { void DFHack::EventManager::unregisterAll(Plugin* plugin) {
@ -82,21 +103,7 @@ void DFHack::EventManager::unregisterAll(Plugin* plugin) {
if ( (*i).first != plugin ) if ( (*i).first != plugin )
break; break;
//shenanigans to avoid concurrent modification removeFromTickQueue((*i).second);
EventHandler getRidOf = (*i).second;
bool didSomething;
do {
didSomething = false;
for ( auto j = tickQueue.find(getRidOf.freq); j != tickQueue.end(); j++ ) {
if ( (*j).first > getRidOf.freq )
break;
if ( (*j).second != getRidOf )
continue;
tickQueue.erase(j);
didSomething = true;
break;
}
} while (didSomething); //this loop is here in case the same EventHandler was added more than once
} }
for ( size_t a = 0; a < (size_t)EventType::EVENT_MAX; a++ ) { for ( size_t a = 0; a < (size_t)EventType::EVENT_MAX; a++ ) {
handlers[a].erase(plugin); handlers[a].erase(plugin);
@ -185,6 +192,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event
gameLoaded = false; gameLoaded = false;
// equipmentLog.clear(); // equipmentLog.clear();
} else if ( event == DFHack::SC_MAP_LOADED ) { } else if ( event == DFHack::SC_MAP_LOADED ) {
/*
int32_t tick = df::global::world->frame_counter; int32_t tick = df::global::world->frame_counter;
multimap<int32_t,EventHandler> newTickQueue; multimap<int32_t,EventHandler> newTickQueue;
for ( auto i = tickQueue.begin(); i != tickQueue.end(); i++ ) for ( auto i = tickQueue.begin(); i != tickQueue.end(); i++ )
@ -192,7 +200,9 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event
tickQueue.clear(); tickQueue.clear();
tickQueue.insert(newTickQueue.begin(), newTickQueue.end()); tickQueue.insert(newTickQueue.begin(), newTickQueue.end());
//out.print("%s,%d: on load, frame_counter = %d\n", __FILE__, __LINE__, tick); //out.print("%s,%d: on load, frame_counter = %d\n", __FILE__, __LINE__, tick);
*/
//tickQueue.clear();
nextItem = *df::global::item_next_id; nextItem = *df::global::item_next_id;
nextBuilding = *df::global::building_next_id; nextBuilding = *df::global::building_next_id;
nextInvasion = df::global::ui->invasions.next_id; nextInvasion = df::global::ui->invasions.next_id;
@ -248,12 +258,14 @@ void DFHack::EventManager::manageEvents(color_ostream& out) {
for ( size_t a = 0; a < EventType::EVENT_MAX; a++ ) { for ( size_t a = 0; a < EventType::EVENT_MAX; a++ ) {
if ( handlers[a].empty() ) if ( handlers[a].empty() )
continue; continue;
int32_t eventFrequency = 1000000000; int32_t eventFrequency = -100;
for ( auto b = handlers[a].begin(); b != handlers[a].end(); b++ ) { if ( a != EventType::TICK )
EventHandler bob = (*b).second; for ( auto b = handlers[a].begin(); b != handlers[a].end(); b++ ) {
if ( bob.freq < eventFrequency ) EventHandler bob = (*b).second;
eventFrequency = bob.freq; if ( bob.freq < eventFrequency || eventFrequency == -100 )
} eventFrequency = bob.freq;
}
else eventFrequency = 1;
if ( tick - eventLastTick[a] < eventFrequency ) if ( tick - eventLastTick[a] < eventFrequency )
continue; continue;
@ -264,6 +276,7 @@ void DFHack::EventManager::manageEvents(color_ostream& out) {
} }
static void manageTickEvent(color_ostream& out) { static void manageTickEvent(color_ostream& out) {
unordered_set<EventHandler> toRemove;
int32_t tick = df::global::world->frame_counter; int32_t tick = df::global::world->frame_counter;
while ( !tickQueue.empty() ) { while ( !tickQueue.empty() ) {
if ( tick < (*tickQueue.begin()).first ) if ( tick < (*tickQueue.begin()).first )
@ -271,8 +284,20 @@ static void manageTickEvent(color_ostream& out) {
EventHandler handle = (*tickQueue.begin()).second; EventHandler handle = (*tickQueue.begin()).second;
tickQueue.erase(tickQueue.begin()); tickQueue.erase(tickQueue.begin());
handle.eventHandler(out, (void*)tick); handle.eventHandler(out, (void*)tick);
toRemove.insert(handle);
}
if ( toRemove.empty() )
return;
for ( auto a = handlers[EventType::TICK].begin(); a != handlers[EventType::TICK].end(); ) {
//handlerToPlugin[(*a).second] = (*a).first;
if ( toRemove.find((*a).second) == toRemove.end() ) {
a++;
continue;
}
Plugin* plug = (*a).first;
handlers[EventType::TICK].erase(a);
a = handlers[EventType::TICK].find(plug);
} }
} }
static void manageJobInitiatedEvent(color_ostream& out) { static void manageJobInitiatedEvent(color_ostream& out) {

@ -46,21 +46,30 @@ command_result eventExample(color_ostream& out, vector<string>& parameters) {
EventManager::EventHandler constructionHandler(construction, 100); EventManager::EventHandler constructionHandler(construction, 100);
EventManager::EventHandler syndromeHandler(syndrome, 1); EventManager::EventHandler syndromeHandler(syndrome, 1);
EventManager::EventHandler invasionHandler(invasion, 1000); EventManager::EventHandler invasionHandler(invasion, 1000);
Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("eventExample"); EventManager::unregisterAll(plugin_self);
EventManager::unregisterAll(me);
EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, plugin_self);
EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, me); EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, completeHandler, plugin_self);
EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, completeHandler, me); EventManager::registerListener(EventManager::EventType::UNIT_DEATH, deathHandler, plugin_self);
EventManager::registerTick(timeHandler, 1, me); EventManager::registerListener(EventManager::EventType::ITEM_CREATED, itemHandler, plugin_self);
EventManager::registerTick(timeHandler, 2, me); EventManager::registerListener(EventManager::EventType::BUILDING, buildingHandler, plugin_self);
EventManager::registerTick(timeHandler, 4, me); EventManager::registerListener(EventManager::EventType::CONSTRUCTION, constructionHandler, plugin_self);
EventManager::registerTick(timeHandler, 8, me); EventManager::registerListener(EventManager::EventType::SYNDROME, syndromeHandler, plugin_self);
EventManager::registerListener(EventManager::EventType::UNIT_DEATH, deathHandler, me); EventManager::registerListener(EventManager::EventType::INVASION, invasionHandler, plugin_self);
EventManager::registerListener(EventManager::EventType::ITEM_CREATED, itemHandler, me); EventManager::registerTick(timeHandler, 1, plugin_self);
EventManager::registerListener(EventManager::EventType::BUILDING, buildingHandler, me); EventManager::registerTick(timeHandler, 2, plugin_self);
EventManager::registerListener(EventManager::EventType::CONSTRUCTION, constructionHandler, me); EventManager::registerTick(timeHandler, 4, plugin_self);
EventManager::registerListener(EventManager::EventType::SYNDROME, syndromeHandler, me); EventManager::registerTick(timeHandler, 8, plugin_self);
EventManager::registerListener(EventManager::EventType::INVASION, invasionHandler, me); int32_t t = EventManager::registerTick(timeHandler, 16, plugin_self);
timeHandler.freq = t;
EventManager::unregister(EventManager::EventType::TICK, timeHandler, plugin_self);
t = EventManager::registerTick(timeHandler, 32, plugin_self);
t = EventManager::registerTick(timeHandler, 32, plugin_self);
t = EventManager::registerTick(timeHandler, 32, plugin_self);
timeHandler.freq = t;
EventManager::unregister(EventManager::EventType::TICK, timeHandler, plugin_self);
EventManager::unregister(EventManager::EventType::TICK, timeHandler, plugin_self);
out.print("Events registered.\n"); out.print("Events registered.\n");
return CR_OK; return CR_OK;
} }