diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 2d7dd2ee0..5122dfdf2 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -37,10 +37,10 @@ namespace DFHack { 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; } - bool operator!=(EventHandler& handle) const { + bool operator!=(const EventHandler& handle) const { return !( *this == handle); } }; @@ -59,7 +59,7 @@ namespace DFHack { };*/ 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 unregisterAll(Plugin* plugin); void manageEvents(color_ostream& out); @@ -79,6 +79,16 @@ namespace std { return r; } }; + template <> + struct hash { + 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 diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 1a91e2c11..257747871 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -49,7 +49,7 @@ void DFHack::EventManager::registerListener(EventType::EventType e, EventHandler handlers[e].insert(pair(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 ) { df::world* world = df::global::world; if ( world ) { @@ -62,19 +62,40 @@ void DFHack::EventManager::registerTick(EventHandler handler, int32_t when, Plug handler.freq = when; tickQueue.insert(pair(handler.freq, handler)); handlers[EventType::TICK].insert(pair(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) { - for ( multimap::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 ) break; EventHandler handle = (*i).second; - if ( handle == handler ) { - handlers[e].erase(i); - break; + if ( handle != handler ) { + i++; + 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) { @@ -82,21 +103,7 @@ void DFHack::EventManager::unregisterAll(Plugin* plugin) { if ( (*i).first != plugin ) break; - //shenanigans to avoid concurrent modification - 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 + removeFromTickQueue((*i).second); } for ( size_t a = 0; a < (size_t)EventType::EVENT_MAX; a++ ) { handlers[a].erase(plugin); @@ -185,6 +192,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event gameLoaded = false; // equipmentLog.clear(); } else if ( event == DFHack::SC_MAP_LOADED ) { + /* int32_t tick = df::global::world->frame_counter; multimap newTickQueue; 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.insert(newTickQueue.begin(), newTickQueue.end()); //out.print("%s,%d: on load, frame_counter = %d\n", __FILE__, __LINE__, tick); - + */ + //tickQueue.clear(); + nextItem = *df::global::item_next_id; nextBuilding = *df::global::building_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++ ) { if ( handlers[a].empty() ) continue; - int32_t eventFrequency = 1000000000; - for ( auto b = handlers[a].begin(); b != handlers[a].end(); b++ ) { - EventHandler bob = (*b).second; - if ( bob.freq < eventFrequency ) - eventFrequency = bob.freq; - } + int32_t eventFrequency = -100; + if ( a != EventType::TICK ) + for ( auto b = handlers[a].begin(); b != handlers[a].end(); b++ ) { + EventHandler bob = (*b).second; + if ( bob.freq < eventFrequency || eventFrequency == -100 ) + eventFrequency = bob.freq; + } + else eventFrequency = 1; if ( tick - eventLastTick[a] < eventFrequency ) continue; @@ -264,6 +276,7 @@ void DFHack::EventManager::manageEvents(color_ostream& out) { } static void manageTickEvent(color_ostream& out) { + unordered_set toRemove; int32_t tick = df::global::world->frame_counter; while ( !tickQueue.empty() ) { if ( tick < (*tickQueue.begin()).first ) @@ -271,8 +284,20 @@ static void manageTickEvent(color_ostream& out) { EventHandler handle = (*tickQueue.begin()).second; tickQueue.erase(tickQueue.begin()); 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) { diff --git a/plugins/devel/eventExample.cpp b/plugins/devel/eventExample.cpp index f6fa649b8..80570fc27 100644 --- a/plugins/devel/eventExample.cpp +++ b/plugins/devel/eventExample.cpp @@ -46,21 +46,30 @@ command_result eventExample(color_ostream& out, vector& parameters) { EventManager::EventHandler constructionHandler(construction, 100); EventManager::EventHandler syndromeHandler(syndrome, 1); EventManager::EventHandler invasionHandler(invasion, 1000); - Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("eventExample"); - EventManager::unregisterAll(me); - - EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, me); - EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, completeHandler, me); - EventManager::registerTick(timeHandler, 1, me); - EventManager::registerTick(timeHandler, 2, me); - EventManager::registerTick(timeHandler, 4, me); - EventManager::registerTick(timeHandler, 8, me); - EventManager::registerListener(EventManager::EventType::UNIT_DEATH, deathHandler, me); - EventManager::registerListener(EventManager::EventType::ITEM_CREATED, itemHandler, me); - EventManager::registerListener(EventManager::EventType::BUILDING, buildingHandler, me); - EventManager::registerListener(EventManager::EventType::CONSTRUCTION, constructionHandler, me); - EventManager::registerListener(EventManager::EventType::SYNDROME, syndromeHandler, me); - EventManager::registerListener(EventManager::EventType::INVASION, invasionHandler, me); + EventManager::unregisterAll(plugin_self); + + EventManager::registerListener(EventManager::EventType::JOB_INITIATED, initiateHandler, plugin_self); + EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, completeHandler, plugin_self); + EventManager::registerListener(EventManager::EventType::UNIT_DEATH, deathHandler, plugin_self); + EventManager::registerListener(EventManager::EventType::ITEM_CREATED, itemHandler, plugin_self); + EventManager::registerListener(EventManager::EventType::BUILDING, buildingHandler, plugin_self); + EventManager::registerListener(EventManager::EventType::CONSTRUCTION, constructionHandler, plugin_self); + EventManager::registerListener(EventManager::EventType::SYNDROME, syndromeHandler, plugin_self); + EventManager::registerListener(EventManager::EventType::INVASION, invasionHandler, plugin_self); + EventManager::registerTick(timeHandler, 1, plugin_self); + EventManager::registerTick(timeHandler, 2, plugin_self); + EventManager::registerTick(timeHandler, 4, plugin_self); + EventManager::registerTick(timeHandler, 8, plugin_self); + 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"); return CR_OK; }