From 735264d7c2e75b44929977045f3d7be4f3a76a3b Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Tue, 22 Mar 2022 21:59:31 -0700 Subject: [PATCH 01/61] Adds event_tracker class for helping enforce event frequencies --- library/modules/EventManager.cpp | 63 ++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 1f70bc1fb1..30a02ae335 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -183,6 +184,68 @@ static const eventManager_t eventManager[] = { manageInteractionEvent, }; +template +class event_tracker { //todo: use inheritance? stl seems to use variadics, so it's unclear how well that would actually work +private: + std::unordered_map seen; + std::multimap history; +public: + void clear() { + seen.clear(); + history.clear(); + } + bool contains(const T &event_data) { + return seen.find(event_data) != seen.end(); + } + typename std::multimap::iterator find(const T &event_data) { + auto iter = seen.find(event_data); + if(iter != seen.end()){ + auto tick = iter->second; + for(auto jter = history.lower_bound(tick); jter != history.end(); ++jter) { + if (jter->second == event_data) { + return jter; + } + } + } + return history.end(); + } + typename std::multimap::iterator begin() { + return history.begin(); + } + typename std::multimap::iterator end() { + return history.end(); + } + typename std::multimap::iterator upper_bound(const int32_t &tick) { + return history.upper_bound(tick); + } + std::pair::iterator, bool> emplace(const int32_t &tick, const T &event_data) { + // data structure is replacing uses of std::unordered_set, so we can do this if(!contains) + if (!contains(event_data)) { + seen.emplace(event_data, tick); + return std::make_pair(history.emplace(tick, event_data), true); + } + return std::make_pair(history.end(), false); + } + typename std::multimap::iterator erase(typename std::multimap::iterator &pos) { + if (pos != history.end()) { + seen.erase(pos->second); + return history.erase(pos); + } + } + typename std::multimap::iterator erase(const T& event_data) { + auto iter = seen.find(event_data); + if(iter != seen.end()){ + auto tick = iter->second; + seen.erase(iter); + for(auto jter = history.lower_bound(tick); jter != history.end(); ++jter) { + if(jter->second == event_data) { + return history.erase(jter); + } + } + } + return history.end(); + } +}; //job initiated static int32_t lastJobId = -1; From 267df4f55bd4d6a64ce206c27d5808bbebeee667 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Tue, 22 Mar 2022 22:58:57 -0700 Subject: [PATCH 02/61] Updates 4 event managers to send all events to handlers Since adding code to enforce each handler's callback frequency handlers would not receive all events Now these 4 do --- library/modules/EventManager.cpp | 211 ++++++++++++++++++++----------- 1 file changed, 137 insertions(+), 74 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 30a02ae335..e2c14d4d0c 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -250,22 +250,22 @@ class event_tracker { //todo: use inheritance? stl seems to use variadics, so it static int32_t lastJobId = -1; //job started -static unordered_set startedJobs; +static event_tracker startedJobs; //job completed static unordered_map prevJobs; //new unit active -static unordered_set activeUnits; +static event_tracker activeUnits; //unit death -static unordered_set livingUnits; +static event_tracker deadUnits; //item creation static int32_t nextItem; //building static int32_t nextBuilding; -static unordered_set buildings; +static event_tracker buildings; //construction static unordered_map constructions; @@ -308,9 +308,10 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) { Job::deleteJobStruct((*i).second, true); } + startedJobs.clear(); prevJobs.clear(); - tickQueue.clear(); - livingUnits.clear(); + activeUnits.clear(); + deadUnits.clear(); buildings.clear(); constructions.clear(); equipmentLog.clear(); @@ -351,6 +352,37 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event nextInvasion = df::global::ui->invasions.next_id; lastJobId = -1 + *df::global::job_next_id; + // initialize our started jobs list + for ( df::job_list_link* link = df::global::world->jobs.list.next; link != nullptr; link = link->next ) { + df::job* job = link->item; + if (job && Job::getWorker(job)) { + // the job was already started, so emplace to a non-iterable area + startedJobs.emplace(-1, job); + } + } + // initialize our active units list + for (df::unit* unit : df::global::world->units.active) { + int32_t id = unit->id; + if (!activeUnits.contains(id)) { + // unit is already active, so emplace to a non-iterable area + activeUnits.emplace(-1, id); + } + } + // initialize our dead units list + for (df::unit* unit: df::global::world->units.all) { + //if ( unit->counters.death_id == -1 ) { + if (!Units::isActive(unit)) { + // unit is already dead, so emplace to a non-iterable area + deadUnits.emplace(-1, unit->id); + } + } + // initialize our buildings list + for (df::building* building : df::global::world->buildings.all) { + Buildings::updateBuildings(out, (void*)intptr_t(building->id)); + // building already existed, so emplace to a non-iterable area + buildings.emplace(-1, building->id); + } + constructions.clear(); for ( auto i = df::global::world->constructions.begin(); i != df::global::world->constructions.end(); i++ ) { df::construction* constr = *i; @@ -497,35 +529,33 @@ static void manageJobInitiatedEvent(color_ostream& out) { lastJobId = *df::global::job_next_id - 1; } -static void manageJobStartedEvent(color_ostream& out){ +static void manageJobStartedEvent(color_ostream& out) { if (!df::global::world) return; - - multimap copy(handlers[EventType::JOB_STARTED].begin(), handlers[EventType::JOB_STARTED].end()); + multimap copy(handlers[EventType::JOB_STARTED].begin(), handlers[EventType::JOB_STARTED].end()); int32_t tick = df::global::world->frame_counter; - // compile a list of newly started jobs - std::vector newly_started_jobs; - for ( df::job_list_link* link = df::global::world->jobs.list.next; link != NULL; link = link->next ) { + // update the started jobs list for the current tick + for (df::job_list_link* link = df::global::world->jobs.list.next; link != NULL; link = link->next) { df::job* job = link->item; // the jobs must have a worker to start if (job && Job::getWorker(job)) { - // cross-reference against existing list of jobs with workers - if (startedJobs.emplace(job).second) { - // must be new - newly_started_jobs.push_back(job); - } + // the job won't be added if it already exists + startedJobs.emplace(tick, job); } } // iterate the event handlers - for(auto &iter : copy) { + for (auto &iter: copy) { auto &handler = iter.second; // make sure the frequency of this handler is obeyed if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; eventLastTick[handler.eventHandler] = tick; - // send the handler the new jobs - for(auto job : newly_started_jobs){ - handler.eventHandler(out, (void*)job); + // send the handler all the new jobs since it last fired + auto jter = startedJobs.upper_bound(last_tick); + for (; jter != startedJobs.end(); ++jter) { + // todo: entity check? before/inside handler loop? + handler.eventHandler(out, (void*) jter->second); } } } @@ -671,46 +701,61 @@ static void manageJobCompletedEvent(color_ostream& out) { static void manageNewUnitActiveEvent(color_ostream& out) { if (!df::global::world) return; - unordered_set activeUnits_replacement; multimap copy(handlers[EventType::NEW_UNIT_ACTIVE].begin(), handlers[EventType::NEW_UNIT_ACTIVE].end()); int32_t tick = df::global::world->frame_counter; - for (auto unit : df::global::world->units.active) { + // update active units list for the current tick + for (df::unit* unit : df::global::world->units.active) { int32_t id = unit->id; - activeUnits_replacement.emplace(id); - if(activeUnits.find(id) == activeUnits.end()){ - for (auto &iter : copy) { - auto &handler = iter.second; - if(tick - eventLastTick[handler.eventHandler] >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; - handler.eventHandler(out, (void*) intptr_t(id)); // intptr_t() avoids cast from smaller type warning - } + if (!activeUnits.contains(id)) { + activeUnits.emplace(tick, id); + } + } + // iterate event handler callbacks + for (auto &iter : copy) { + auto &handler = iter.second; + // enforce handler's callback frequency + if(tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; + eventLastTick[handler.eventHandler] = tick; + // send the handler all the new active unit id's since it last fired + auto jter = activeUnits.upper_bound(last_tick); + for(;jter != activeUnits.end(); ++jter){ + // todo: entity check? before/inside handler loop? + handler.eventHandler(out, (void*) intptr_t(jter->second)); // intptr_t() avoids cast from smaller type warning } } } - activeUnits.swap(activeUnits_replacement); } static void manageUnitDeathEvent(color_ostream& out) { if (!df::global::world) return; - multimap copy(handlers[EventType::UNIT_DEATH].begin(), handlers[EventType::UNIT_DEATH].end()); + multimap copy(handlers[EventType::UNIT_DEATH].begin(), handlers[EventType::UNIT_DEATH].end()); int32_t tick = df::global::world->frame_counter; - for (auto &iter : copy) { + // update dead units list for the current tick + for (df::unit* unit: df::global::world->units.all) { + //if ( unit->counters.death_id == -1 ) { + if (!Units::isActive(unit)) { + if (!deadUnits.contains(unit->id)) { + deadUnits.emplace(tick, unit->id); + activeUnits.erase(unit->id); + } + } else if (deadUnits.contains(unit->id)) { + // unit must have been revived + deadUnits.erase(unit->id); + } + } + // iterate event handler callbacks + for (auto &iter: copy) { auto &handler = iter.second; - if(tick - eventLastTick[handler.eventHandler] >= handler.freq) { + // enforce handler's callback frequency + if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; eventLastTick[handler.eventHandler] = tick; - for ( df::unit *unit : df::global::world->units.all ) { - //if ( unit->counters.death_id == -1 ) { - if ( Units::isActive(unit) ) { - livingUnits.insert(unit->id); - continue; - } - //dead: if dead since last check, trigger events - if ( livingUnits.find(unit->id) == livingUnits.end() ) - continue; - - handler.eventHandler(out, (void*) intptr_t(unit->id)); // intptr_t() avoids cast from smaller type warning - livingUnits.erase(unit->id); + // send the handler all the new dead unit id's since it last fired + auto jter = deadUnits.upper_bound(last_tick); + for(; jter != deadUnits.end(); ++jter) { + handler.eventHandler(out, (void*) intptr_t(jter->second)); // intptr_t() avoids cast from smaller type warning } } } @@ -766,39 +811,57 @@ static void manageBuildingEvent(color_ostream& out) { * TODO: could be faster * consider looking at jobs: building creation / destruction **/ - multimap copy(handlers[EventType::BUILDING].begin(), handlers[EventType::BUILDING].end()); int32_t tick = df::global::world->frame_counter; - //no more separation between new and destroyed events - for (auto &iter : copy) { - auto &handler = iter.second; - if(tick - eventLastTick[handler.eventHandler] >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; - for ( int32_t a = nextBuilding; a < *df::global::building_next_id; ++a ) { - int32_t index = df::building::binsearch_index(df::global::world->buildings.all, a); - if ( index == -1 ) { - //out.print("%s, line %d: Couldn't find new building with id %d.\n", __FILE__, __LINE__, a); - //the tricky thing is that when the game first starts, it's ok to skip buildings, but otherwise, if you skip buildings, something is probably wrong. TODO: make this smarter - continue; - } - buildings.insert(a); - handler.eventHandler(out, (void*)intptr_t(a)); - } + // find destroyed buildings + std::vector destroyed; + for (auto &iter: buildings) { + int32_t id = iter.second; + // continue if the id represents a previously destroyed building + if (id < 0) { + continue; + } + int32_t index = df::building::binsearch_index(df::global::world->buildings.all, id); + // continue if we found the id in world->buildings.all + if (index != -1) { + continue; + } + // pretty sure we'd invalidate our loop if we added to buildings here, so we just save the id in an intermediary for now + destroyed.push_back(id); + } + // update building list with destroyed buildings + for (auto id: destroyed) { + // we're using negative id values to communicate destruction since we need to save knowledge for future ticks + buildings.emplace(tick, -id); + } + // update building list with new buildings + for (int32_t id = nextBuilding; id < *df::global::building_next_id; ++id) { + int32_t index = df::building::binsearch_index(df::global::world->buildings.all, id); + if (index == -1) { + //out.print("%s, line %d: Couldn't find new building with id %d.\n", __FILE__, __LINE__, a); + //the tricky thing is that when the game first starts, it's ok to skip buildings, but otherwise, if you skip buildings, something is probably wrong. TODO: make this smarter + continue; + } + buildings.emplace(tick, id); + } + nextBuilding = *df::global::building_next_id; - //now alert people about destroyed buildings - for (auto building_iter = buildings.begin(); building_iter != buildings.end(); ) { - int32_t id = *building_iter; - int32_t index = df::building::binsearch_index(df::global::world->buildings.all,id); - if ( index != -1 ) { - ++building_iter; - continue; - } - handler.eventHandler(out, (void*)intptr_t(id)); - building_iter = buildings.erase(building_iter); + // todo: maybe we should create static lists, and compare sizes and only copy if they don't match, otherwise every manager function is copying their handlers every single they execute + multimap copy(handlers[EventType::BUILDING].begin(), handlers[EventType::BUILDING].end()); + // iterate event handler callbacks + for (auto &iter: copy) { + auto &handler = iter.second; + // enforce handler's callback frequency + if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; + eventLastTick[handler.eventHandler] = tick; + // send the handler all the new & destroyed buildings since it last fired + auto jter = buildings.upper_bound(last_tick); + for (; jter != buildings.end(); ++jter) { + handler.eventHandler(out, (void*) intptr_t(jter->second)); } } } - nextBuilding = *df::global::building_next_id; } static void manageConstructionEvent(color_ostream& out) { From 3df78486dbc05d894abb1273c3042ead99b2a42c Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 23 Mar 2022 09:30:59 -0700 Subject: [PATCH 03/61] Removes second buildings list initializer --- library/modules/EventManager.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index e2c14d4d0c..f907d6e02e 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -399,11 +399,6 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event } constructions[constr->pos] = *constr; } - for ( size_t a = 0; a < df::global::world->buildings.all.size(); ++a ) { - df::building* b = df::global::world->buildings.all[a]; - Buildings::updateBuildings(out, (void*)intptr_t(b->id)); - buildings.insert(b->id); - } lastSyndromeTime = -1; for ( df::unit* unit : df::global::world->units.all ) { for ( size_t b = 0; b < unit->syndromes.active.size(); ++b ) { From d2bfae2e90347723a795122601716c473835235b Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 23 Mar 2022 10:09:13 -0700 Subject: [PATCH 04/61] Separates buildings list into created/destroyed lists --- library/modules/EventManager.cpp | 62 ++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index f907d6e02e..ee0764b7a0 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -265,7 +265,8 @@ static event_tracker deadUnits; static int32_t nextItem; //building static int32_t nextBuilding; -static event_tracker buildings; +static event_tracker createdBuildings; +static event_tracker destroyedBuildings; //construction static unordered_map constructions; @@ -312,9 +313,12 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event prevJobs.clear(); activeUnits.clear(); deadUnits.clear(); - buildings.clear(); + createdBuildings.clear(); + destroyedBuildings.clear(); constructions.clear(); equipmentLog.clear(); + //todo: clear reportToRelevantUnits? + tickQueue.clear(); Buildings::clearBuildings(out); lastReport = -1; @@ -380,7 +384,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event for (df::building* building : df::global::world->buildings.all) { Buildings::updateBuildings(out, (void*)intptr_t(building->id)); // building already existed, so emplace to a non-iterable area - buildings.emplace(-1, building->id); + createdBuildings.emplace(-1, building->id); } constructions.clear(); @@ -807,29 +811,19 @@ static void manageBuildingEvent(color_ostream& out) { * consider looking at jobs: building creation / destruction **/ int32_t tick = df::global::world->frame_counter; - - // find destroyed buildings - std::vector destroyed; - for (auto &iter: buildings) { + // update destroyed building list + for (auto &iter: createdBuildings) { int32_t id = iter.second; - // continue if the id represents a previously destroyed building - if (id < 0) { - continue; - } int32_t index = df::building::binsearch_index(df::global::world->buildings.all, id); // continue if we found the id in world->buildings.all if (index != -1) { continue; } // pretty sure we'd invalidate our loop if we added to buildings here, so we just save the id in an intermediary for now - destroyed.push_back(id); + destroyedBuildings.emplace(tick, id); + createdBuildings.erase(id); } - // update building list with destroyed buildings - for (auto id: destroyed) { - // we're using negative id values to communicate destruction since we need to save knowledge for future ticks - buildings.emplace(tick, -id); - } - // update building list with new buildings + // update created building list for (int32_t id = nextBuilding; id < *df::global::building_next_id; ++id) { int32_t index = df::building::binsearch_index(df::global::world->buildings.all, id); if (index == -1) { @@ -837,12 +831,42 @@ static void manageBuildingEvent(color_ostream& out) { //the tricky thing is that when the game first starts, it's ok to skip buildings, but otherwise, if you skip buildings, something is probably wrong. TODO: make this smarter continue; } - buildings.emplace(tick, id); + createdBuildings.emplace(tick, id); + destroyedBuildings.erase(id); } nextBuilding = *df::global::building_next_id; // todo: maybe we should create static lists, and compare sizes and only copy if they don't match, otherwise every manager function is copying their handlers every single they execute multimap copy(handlers[EventType::BUILDING].begin(), handlers[EventType::BUILDING].end()); + // iterate event handler callbacks (send handlers CREATED buildings) + for (auto &iter: copy) { + auto &handler = iter.second; + // enforce handler's callback frequency + if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; + eventLastTick[handler.eventHandler] = tick; + // send the handler all the new buildings since it last fired + auto jter = createdBuildings.upper_bound(last_tick); + for (; jter != createdBuildings.end(); ++jter) { + handler.eventHandler(out, (void*) intptr_t(jter->second)); + } + } + } + // iterate event handler callbacks (send handlers DESTROYED buildings + for (auto &iter: copy) { + auto &handler = iter.second; + // enforce handler's callback frequency + if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; + eventLastTick[handler.eventHandler] = tick; + // send the handler all the destroyed buildings since it last fired + auto jter = destroyedBuildings.upper_bound(last_tick); + for (; jter != destroyedBuildings.end(); ++jter) { + handler.eventHandler(out, (void*) intptr_t(jter->second)); + } + } + } +} // iterate event handler callbacks for (auto &iter: copy) { auto &handler = iter.second; From 7b7d46bfdc6223a01a71c7f8292f8a8acab9080f Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 23 Mar 2022 10:10:43 -0700 Subject: [PATCH 05/61] Adds CREATED_/DESTROYED_ building events while retaining BUILDING event --- library/include/modules/EventManager.h | 2 + library/modules/EventManager.cpp | 57 +++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 80a9f6a4a9..7288f9f651 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -26,6 +26,8 @@ namespace DFHack { UNIT_DEATH, ITEM_CREATED, BUILDING, + CREATED_BUILDING, + DESTROYED_BUILDING, CONSTRUCTION, SYNDROME, INVASION, diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index ee0764b7a0..e66bd81d15 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -867,6 +867,26 @@ static void manageBuildingEvent(color_ostream& out) { } } } + +static void manageCreatedBuildingEvent(color_ostream& out) { + if (!df::global::world) + return; + if (!df::global::building_next_id) + return; + int32_t tick = df::global::world->frame_counter; + // update created building list + for (int32_t id = nextBuilding; id < *df::global::building_next_id; ++id) { + int32_t index = df::building::binsearch_index(df::global::world->buildings.all, id); + if (index == -1) { + //out.print("%s, line %d: Couldn't find new building with id %d.\n", __FILE__, __LINE__, a); + //the tricky thing is that when the game first starts, it's ok to skip buildings, but otherwise, if you skip buildings, something is probably wrong. TODO: make this smarter + continue; + } + createdBuildings.emplace(tick, id); + destroyedBuildings.erase(id); + } + nextBuilding = *df::global::building_next_id; + multimap copy(handlers[EventType::CREATED_BUILDING].begin(), handlers[EventType::CREATED_BUILDING].end()); // iterate event handler callbacks for (auto &iter: copy) { auto &handler = iter.second; @@ -875,8 +895,41 @@ static void manageBuildingEvent(color_ostream& out) { auto last_tick = eventLastTick[handler.eventHandler]; eventLastTick[handler.eventHandler] = tick; // send the handler all the new & destroyed buildings since it last fired - auto jter = buildings.upper_bound(last_tick); - for (; jter != buildings.end(); ++jter) { + auto jter = createdBuildings.upper_bound(last_tick); + for (; jter != createdBuildings.end(); ++jter) { + handler.eventHandler(out, (void*) intptr_t(jter->second)); + } + } + } +} + +static void manageDestroyedBuildingEvent(color_ostream& out) { + if (!df::global::world) + return; + int32_t tick = df::global::world->frame_counter; + // update destroyed building list + for (auto &iter: createdBuildings) { + int32_t id = iter.second; + int32_t index = df::building::binsearch_index(df::global::world->buildings.all, id); + // continue if we found the id in world->buildings.all + if (index != -1) { + continue; + } + // pretty sure we'd invalidate our loop if we added to buildings here, so we just save the id in an intermediary for now + destroyedBuildings.emplace(tick, id); + createdBuildings.erase(id); + } + multimap copy(handlers[EventType::DESTROYED_BUILDING].begin(), handlers[EventType::DESTROYED_BUILDING].end()); + // iterate event handler callbacks + for (auto &iter: copy) { + auto &handler = iter.second; + // enforce handler's callback frequency + if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; + eventLastTick[handler.eventHandler] = tick; + // send the handler all the new & destroyed buildings since it last fired + auto jter = destroyedBuildings.upper_bound(last_tick); + for (; jter != destroyedBuildings.end(); ++jter) { handler.eventHandler(out, (void*) intptr_t(jter->second)); } } From b57a48aa7f6ee61f587343139124c52526494cfc Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 23 Mar 2022 10:12:27 -0700 Subject: [PATCH 06/61] Adds missing event manager prototypes --- library/modules/EventManager.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index e66bd81d15..4334fb3cb0 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -154,6 +154,8 @@ static void manageNewUnitActiveEvent(color_ostream& out); static void manageUnitDeathEvent(color_ostream& out); static void manageItemCreationEvent(color_ostream& out); static void manageBuildingEvent(color_ostream& out); +static void manageCreatedBuildingEvent(color_ostream& out); +static void manageDestroyedBuildingEvent(color_ostream& out); static void manageConstructionEvent(color_ostream& out); static void manageSyndromeEvent(color_ostream& out); static void manageInvasionEvent(color_ostream& out); @@ -174,6 +176,8 @@ static const eventManager_t eventManager[] = { manageUnitDeathEvent, manageItemCreationEvent, manageBuildingEvent, + manageCreatedBuildingEvent, + manageDestroyedBuildingEvent, manageConstructionEvent, manageSyndromeEvent, manageInvasionEvent, From 8ea5c579b01f127b5dbecf1133720e9866023db9 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 23 Mar 2022 18:40:44 -0700 Subject: [PATCH 07/61] Adds plugin devel/event-testing --- plugins/devel/CMakeLists.txt | 1 + plugins/devel/event-testing.cpp | 75 +++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 plugins/devel/event-testing.cpp diff --git a/plugins/devel/CMakeLists.txt b/plugins/devel/CMakeLists.txt index 6d15f09dd9..814a9c0ead 100644 --- a/plugins/devel/CMakeLists.txt +++ b/plugins/devel/CMakeLists.txt @@ -22,6 +22,7 @@ dfhack_plugin(stockcheck stockcheck.cpp) dfhack_plugin(stripcaged stripcaged.cpp) dfhack_plugin(tilesieve tilesieve.cpp) dfhack_plugin(zoom zoom.cpp) +dfhack_plugin(event-testing event-testing.cpp) if(UNIX) dfhack_plugin(ref-index ref-index.cpp) diff --git a/plugins/devel/event-testing.cpp b/plugins/devel/event-testing.cpp new file mode 100644 index 0000000000..c5aef98dd8 --- /dev/null +++ b/plugins/devel/event-testing.cpp @@ -0,0 +1,75 @@ +#include "Core.h" +#include +#include +#include +#include +#include +#include +#include + +//#include "df/world.h" + +using namespace DFHack; +using namespace df::enums; + +DFHACK_PLUGIN("event-testing"); +DFHACK_PLUGIN_IS_ENABLED(enabled); +//REQUIRE_GLOBAL(world); + +void onTick(color_ostream& out, void* tick); +void onJobStart(color_ostream &out, void* job); +void onJobCompletion(color_ostream &out, void* job); + +command_result skeleton2 (color_ostream &out, std::vector & parameters); + +DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { + commands.push_back(PluginCommand("event-testing", + "~54 character description of plugin", //to use one line in the ``[DFHack]# ls`` output + skeleton2, + false, + "example usage")); + return CR_OK; +} + +command_result skeleton2 (color_ostream &out, std::vector & parameters) { + if (!parameters.empty()) + return CR_WRONG_USAGE; + CoreSuspender suspend; + out.print("blah"); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown (color_ostream &out) { + namespace EM = EventManager; + EM::unregisterAll(plugin_self); + return CR_OK; +} + +void enable_job_completed_events() { + namespace EM = EventManager; + using namespace EM::EventType; + EM::EventHandler eventHandler1(onJobCompletion, 0); // constantly + EM::EventHandler eventHandler2(onJobCompletion, 50); // every dwarf-hour + EM::registerListener(EventType::JOB_COMPLETED, eventHandler1, plugin_self); + EM::registerListener(EventType::JOB_COMPLETED, eventHandler2, plugin_self); +} + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { + namespace EM = EventManager; + if (enable && !enabled) { + enable_job_completed_events(); + out.print("plugin enabled!\n"); + } else if (!enable && enabled) { + EM::unregisterAll(plugin_self); + out.print("plugin disabled!\n"); + } + enabled = enable; + return CR_OK; +} + +void onJobCompletion(color_ostream &out, void* job) { + auto j = (df::job*)job; + std::string type = ENUM_KEY_STR(job_type, j->job_type); + out.print("onJobCompletion: (id: %d) (type: %s) (expire: %d) (completion: %d) (wait: %d)\n", j->id, type.c_str(), j->expire_timer, j->completion_timer, j->wait_timer); +} + From 950854f3bf9f3b5f4c3c1df834e319a6e477b38a Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 23 Mar 2022 18:41:31 -0700 Subject: [PATCH 08/61] Adds todo comment for BUILDING event (needs to be deprecated) --- library/include/modules/EventManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 7288f9f651..6f1362b1d3 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -25,7 +25,7 @@ namespace DFHack { NEW_UNIT_ACTIVE, UNIT_DEATH, ITEM_CREATED, - BUILDING, + BUILDING, // todo: deprecate this event CREATED_BUILDING, DESTROYED_BUILDING, CONSTRUCTION, From b9ba42e11092789f2276b96a1bdb4c89cc6c532d Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 23 Mar 2022 19:24:09 -0700 Subject: [PATCH 09/61] Adds missing return to event_tracker::erase(iterator) --- library/modules/EventManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 4334fb3cb0..ad04017ba4 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -235,6 +235,7 @@ class event_tracker { //todo: use inheritance? stl seems to use variadics, so it seen.erase(pos->second); return history.erase(pos); } + return history.end(); } typename std::multimap::iterator erase(const T& event_data) { auto iter = seen.find(event_data); From a0109145daefdde84cb0415569d660fda5bf8141 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 23 Mar 2022 20:34:28 -0700 Subject: [PATCH 10/61] Implements several clion suggestions (foreach loops mostly) --- library/modules/EventManager.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index ad04017ba4..023366b355 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -331,8 +331,8 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event gameLoaded = false; multimap copy(handlers[EventType::UNLOAD].begin(), handlers[EventType::UNLOAD].end()); - for (auto a = copy.begin(); a != copy.end(); ++a ) { - (*a).second.eventHandler(out, NULL); + for (auto & plugin_handler_pair : copy) { + plugin_handler_pair.second.eventHandler(out, NULL); } } else if ( event == DFHack::SC_MAP_LOADED ) { /* @@ -393,8 +393,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event } constructions.clear(); - for ( auto i = df::global::world->constructions.begin(); i != df::global::world->constructions.end(); i++ ) { - df::construction* constr = *i; + for (auto constr : df::global::world->constructions) { if ( !constr ) { if ( Once::doOnce("EventManager.onLoad null constr") ) { out.print("EventManager.onLoad: null construction.\n"); @@ -402,23 +401,23 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event continue; } if ( constr->pos == df::coord() ) { - if ( Once::doOnce("EventManager.onLoad null position of construction.\n") ) + if ( Once::doOnce("EventManager.onLoad null position of construction.\n") ) { out.print("EventManager.onLoad null position of construction.\n"); + } continue; } constructions[constr->pos] = *constr; } lastSyndromeTime = -1; for ( df::unit* unit : df::global::world->units.all ) { - for ( size_t b = 0; b < unit->syndromes.active.size(); ++b ) { - df::unit_syndrome* syndrome = unit->syndromes.active[b]; + for (auto syndrome : unit->syndromes.active) { int32_t startTime = syndrome->year*ticksPerYear + syndrome->year_time; if ( startTime > lastSyndromeTime ) lastSyndromeTime = startTime; } } lastReport = -1; - if ( df::global::world->status.reports.size() > 0 ) { + if ( !df::global::world->status.reports.empty() ) { lastReport = df::global::world->status.reports[df::global::world->status.reports.size()-1]->id; } lastReportUnitAttack = -1; @@ -428,8 +427,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event // for ( size_t a = 0; a < EventType::EVENT_MAX; ++a ) { // eventLastTick[a] = -1;//-1000000; // } - for ( size_t a = 0; a < df::global::world->history.figures.size(); ++a ) { - df::historical_figure* unit = df::global::world->history.figures[a]; + for (auto unit : df::global::world->history.figures) { if ( unit->id < 0 && unit->name.language < 0 ) unit->name.language = 0; } From 311ba9c9f48a2e8a37b0fc4d6621668cee42c278 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 23 Mar 2022 20:49:09 -0700 Subject: [PATCH 11/61] Updates job event managers --- library/modules/EventManager.cpp | 176 ++++++++++++++++++------------- 1 file changed, 105 insertions(+), 71 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 023366b355..6b3ebcb5b0 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -251,14 +251,17 @@ class event_tracker { //todo: use inheritance? stl seems to use variadics, so it return history.end(); } }; + //job initiated static int32_t lastJobId = -1; //job started +static std::unordered_set startedJobIDs; static event_tracker startedJobs; //job completed -static unordered_map prevJobs; +//static std::unordered_set completedJobIDs; // disabled because it makes sense to allow duplicate IDs since we test for repeat jobs +static event_tracker completedJobs; //new unit active static event_tracker activeUnits; @@ -311,11 +314,14 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event } if ( event == DFHack::SC_MAP_UNLOADED ) { lastJobId = -1; - for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) { - Job::deleteJobStruct((*i).second, true); + for (auto &iter : completedJobs) { + Job::deleteJobStruct(iter.second, true); + } + for (auto &iter : startedJobs) { + Job::deleteJobStruct(iter.second, true); } startedJobs.clear(); - prevJobs.clear(); + completedJobs.clear(); activeUnits.clear(); deadUnits.clear(); createdBuildings.clear(); @@ -366,7 +372,8 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event df::job* job = link->item; if (job && Job::getWorker(job)) { // the job was already started, so emplace to a non-iterable area - startedJobs.emplace(-1, job); + startedJobIDs.emplace(job->id); + startedJobs.emplace(-1, Job::cloneJobStruct(job, true)); } } // initialize our active units list @@ -534,8 +541,8 @@ static void manageJobInitiatedEvent(color_ostream& out) { static void manageJobStartedEvent(color_ostream& out) { if (!df::global::world) return; - multimap copy(handlers[EventType::JOB_STARTED].begin(), handlers[EventType::JOB_STARTED].end()); int32_t tick = df::global::world->frame_counter; + int32_t oldest_last_tick = (uint16_t)-1; // update the started jobs list for the current tick for (df::job_list_link* link = df::global::world->jobs.list.next; link != NULL; link = link->next) { @@ -543,15 +550,20 @@ static void manageJobStartedEvent(color_ostream& out) { // the jobs must have a worker to start if (job && Job::getWorker(job)) { // the job won't be added if it already exists - startedJobs.emplace(tick, job); + if (startedJobIDs.emplace(job->id).second) { + // DF is going to delete completed jobs, and we might not send these before they're completed + startedJobs.emplace(tick, Job::cloneJobStruct(job, true)); + } } } // iterate the event handlers + multimap copy(handlers[EventType::JOB_STARTED].begin(), handlers[EventType::JOB_STARTED].end()); for (auto &iter: copy) { auto &handler = iter.second; + auto last_tick = eventLastTick[handler.eventHandler]; + oldest_last_tick = oldest_last_tick < last_tick ? oldest_last_tick : last_tick; // make sure the frequency of this handler is obeyed - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { - auto last_tick = eventLastTick[handler.eventHandler]; + if (tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; // send the handler all the new jobs since it last fired auto jter = startedJobs.upper_bound(last_tick); @@ -561,6 +573,15 @@ static void manageJobStartedEvent(color_ostream& out) { } } } + // clean up memory we no longer need + auto iter = startedJobs.begin(); + for(; iter != startedJobs.end() && iter->first != oldest_last_tick;) { + startedJobIDs.erase(iter->second->id); + // we cloned it we delete it + Job::deleteJobStruct(iter->second, true); + // if we delete it, we best not reference it + iter = startedJobs.erase(iter); + } } //helper function for manageJobCompletedEvent //static int32_t getWorkerID(df::job* job) { @@ -574,17 +595,82 @@ TODO: consider checking item creation / experience gain just in case static void manageJobCompletedEvent(color_ostream& out) { if (!df::global::world) return; - static int32_t last_tick = -1; + static int32_t fn_last_tick = -1; int32_t tick = df::global::world->frame_counter; + int32_t oldest_last_tick = (uint16_t)-1; - multimap copy(handlers[EventType::JOB_COMPLETED].begin(), handlers[EventType::JOB_COMPLETED].end()); - map nowJobs; - for ( df::job_list_link* link = df::global::world->jobs.list.next; link != NULL; link = link->next ) { - if ( link->item == NULL ) - continue; - nowJobs[link->item->id] = link->item; + // update the completed jobs list (this apparently isn't as straight forward as one might think) + // todo: verify the need for this check + //if it happened within a tick, must have been cancelled by the user or a plugin: not completed + if(tick > fn_last_tick) { + // we're going to need to check if started jobs are still on this list, so we copy the IDs to avoid a linear search into this list for each started_job + std::unordered_map current_jobs; + for (df::job_list_link* link = df::global::world->jobs.list.next; link != NULL; link = link->next) { + df::job* job = link->item; + current_jobs.emplace(job->id, job); + } + // iterate the started jobs list + for (auto &iter: startedJobs) { + df::job* started_job = iter.second; + int32_t id = started_job->id; + // check for the started job in the current jobs (the jobs we just copied above) + auto cter = current_jobs.find(id); + if (cter != current_jobs.end()) { + // if we found it, that doesn't mean it didn't complete.. it may be a repeat job + df::job* valid_df_job = cter->second; + if (!started_job->flags.bits.repeat) + continue; + // I'd comment these, but I don't know what we're checking exactly.. gl + if (started_job->completion_timer != 0) + continue; + if (valid_df_job->completion_timer != -1) + continue; + + //still false positive if cancelled at EXACTLY the right time, but experiments show this doesn't happen + + // the job won't be added if it already exists - NOTE: this means a repeat job can't be seen as completed again until it gets cleaned from the list + // This is probably going to be a huge problem as it means there is room for an interaction issue between plugins/scripts + if (completedJobIDs.emplace(id).second) { + completedJobs.emplace(tick, Job::cloneJobStruct(started_job, true)); + } + continue; + } + // we didn't find it, so it's a recently finished or cancelled job + if (started_job->flags.bits.repeat || started_job->completion_timer != 0) + continue; + if (completedJobIDs.emplace(id).second) { + completedJobs.emplace(tick, Job::cloneJobStruct(started_job, true)); + } + } } + multimap copy(handlers[EventType::JOB_COMPLETED].begin(), handlers[EventType::JOB_COMPLETED].end()); + // iterate event handler callbacks + for (auto &iter : copy) { + auto &handler = iter.second; + auto last_tick = eventLastTick[handler.eventHandler]; + oldest_last_tick = oldest_last_tick < last_tick ? oldest_last_tick : last_tick; + // enforce handler's callback frequency + if (tick - last_tick >= handler.freq) { + eventLastTick[handler.eventHandler] = tick; + fn_last_tick = tick; + // send the handler all the newly completed jobs since it last fired + auto jter = completedJobs.upper_bound(last_tick); + for (; jter != completedJobs.end(); ++jter) { + handler.eventHandler(out, jter->second); + } + } + } + // clean up memory we no longer need + auto iter = completedJobs.begin(); + for(; iter != completedJobs.end() && iter->first != oldest_last_tick;) { + completedJobIDs.erase(iter->second->id); + // we cloned it we delete it + Job::deleteJobStruct(iter->second, true); + // if we delete it, we best not reference it + iter = completedJobs.erase(iter); + } + // moved to the bottom to be out of the way, but also all the structures used herein no longer exist #if 0 //testing info on job initiation/completion //newly allocated jobs @@ -646,64 +732,11 @@ static void manageJobCompletedEvent(color_ostream& out) { ); } #endif - - //if it happened within a tick, must have been cancelled by the user or a plugin: not completed - if (last_tick < tick ) { - last_tick = tick; - for (auto &iter : copy) { - auto &handler = iter.second; - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; - for (auto job_iter = prevJobs.begin(); job_iter != prevJobs.end(); ++job_iter) { - if (nowJobs.find((*job_iter).first) != nowJobs.end()) { - //could have just finished if it's a repeat job - df::job &job0 = *(*job_iter).second; - if (!job0.flags.bits.repeat) - continue; - df::job &job1 = *nowJobs[(*job_iter).first]; - if (job0.completion_timer != 0) - continue; - if (job1.completion_timer != -1) - continue; - - //still false positive if cancelled at EXACTLY the right time, but experiments show this doesn't happen - handler.eventHandler(out, (void*) &job0); - continue; - } - - //recently finished or cancelled job - df::job &job0 = *(*job_iter).second; - if (job0.flags.bits.repeat || job0.completion_timer != 0) - continue; - handler.eventHandler(out, (void*) &job0); - } - } - } - } - - //erase old jobs, copy over possibly altered jobs - for (auto job_iter = prevJobs.begin(); job_iter != prevJobs.end(); ++job_iter ) { - Job::deleteJobStruct((*job_iter).second, true); - startedJobs.erase(job_iter->second); - } - prevJobs.clear(); - - //create new jobs - for (auto job_iter = nowJobs.begin(); job_iter != nowJobs.end(); ++job_iter ) { - /*map::iterator i = prevJobs.find((*job_iter).first); - if ( i != prevJobs.end() ) { - continue; - }*/ - - df::job* newJob = Job::cloneJobStruct((*job_iter).second, true); - prevJobs[newJob->id] = newJob; - } } static void manageNewUnitActiveEvent(color_ostream& out) { if (!df::global::world) return; - multimap copy(handlers[EventType::NEW_UNIT_ACTIVE].begin(), handlers[EventType::NEW_UNIT_ACTIVE].end()); int32_t tick = df::global::world->frame_counter; // update active units list for the current tick for (df::unit* unit : df::global::world->units.active) { @@ -712,12 +745,13 @@ static void manageNewUnitActiveEvent(color_ostream& out) { activeUnits.emplace(tick, id); } } + multimap copy(handlers[EventType::NEW_UNIT_ACTIVE].begin(), handlers[EventType::NEW_UNIT_ACTIVE].end()); // iterate event handler callbacks for (auto &iter : copy) { auto &handler = iter.second; + auto last_tick = eventLastTick[handler.eventHandler]; // enforce handler's callback frequency - if(tick - eventLastTick[handler.eventHandler] >= handler.freq) { - auto last_tick = eventLastTick[handler.eventHandler]; + if(tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; // send the handler all the new active unit id's since it last fired auto jter = activeUnits.upper_bound(last_tick); From 2e1f42abcfb779db3033b9dc7e5554b3b013e1c5 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 23 Mar 2022 20:50:16 -0700 Subject: [PATCH 12/61] Updates all event managers with boiler plate frequency related code --- library/modules/EventManager.cpp | 55 +++++++++++++++++++------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 6b3ebcb5b0..075ee99b7d 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -260,7 +260,7 @@ static std::unordered_set startedJobIDs; static event_tracker startedJobs; //job completed -//static std::unordered_set completedJobIDs; // disabled because it makes sense to allow duplicate IDs since we test for repeat jobs +static std::unordered_set completedJobIDs; static event_tracker completedJobs; //new unit active @@ -522,10 +522,11 @@ static void manageJobInitiatedEvent(color_ostream& out) { for(auto &iter : copy) { auto &handler = iter.second; - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; + if (tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; - for ( df::job_list_link* link = df::global::world->jobs.list.next; link != NULL; link = link->next ) { - if ( link->item == NULL ) + for ( df::job_list_link* link = df::global::world->jobs.list.next; link != nullptr; link = link->next ) { + if ( link->item == nullptr ) continue; if ( link->item->id <= lastJobId ) continue; @@ -766,7 +767,6 @@ static void manageNewUnitActiveEvent(color_ostream& out) { static void manageUnitDeathEvent(color_ostream& out) { if (!df::global::world) return; - multimap copy(handlers[EventType::UNIT_DEATH].begin(), handlers[EventType::UNIT_DEATH].end()); int32_t tick = df::global::world->frame_counter; // update dead units list for the current tick for (df::unit* unit: df::global::world->units.all) { @@ -782,11 +782,12 @@ static void manageUnitDeathEvent(color_ostream& out) { } } // iterate event handler callbacks + multimap copy(handlers[EventType::UNIT_DEATH].begin(), handlers[EventType::UNIT_DEATH].end()); for (auto &iter: copy) { auto &handler = iter.second; + auto last_tick = eventLastTick[handler.eventHandler]; // enforce handler's callback frequency - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { - auto last_tick = eventLastTick[handler.eventHandler]; + if (tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; // send the handler all the new dead unit id's since it last fired auto jter = deadUnits.upper_bound(last_tick); @@ -812,7 +813,8 @@ static void manageItemCreationEvent(color_ostream& out) { if ( index != 0 ) index--; for (auto &iter : copy) { auto &handler = iter.second; - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; + if (tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; for (size_t a = index; a < df::global::world->items.all.size(); ++a) { df::item* item = df::global::world->items.all[a]; @@ -878,9 +880,9 @@ static void manageBuildingEvent(color_ostream& out) { // iterate event handler callbacks (send handlers CREATED buildings) for (auto &iter: copy) { auto &handler = iter.second; + auto last_tick = eventLastTick[handler.eventHandler]; // enforce handler's callback frequency - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { - auto last_tick = eventLastTick[handler.eventHandler]; + if (tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; // send the handler all the new buildings since it last fired auto jter = createdBuildings.upper_bound(last_tick); @@ -892,9 +894,9 @@ static void manageBuildingEvent(color_ostream& out) { // iterate event handler callbacks (send handlers DESTROYED buildings for (auto &iter: copy) { auto &handler = iter.second; + auto last_tick = eventLastTick[handler.eventHandler]; // enforce handler's callback frequency - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { - auto last_tick = eventLastTick[handler.eventHandler]; + if (tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; // send the handler all the destroyed buildings since it last fired auto jter = destroyedBuildings.upper_bound(last_tick); @@ -927,9 +929,9 @@ static void manageCreatedBuildingEvent(color_ostream& out) { // iterate event handler callbacks for (auto &iter: copy) { auto &handler = iter.second; + auto last_tick = eventLastTick[handler.eventHandler]; // enforce handler's callback frequency - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { - auto last_tick = eventLastTick[handler.eventHandler]; + if (tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; // send the handler all the new & destroyed buildings since it last fired auto jter = createdBuildings.upper_bound(last_tick); @@ -960,9 +962,9 @@ static void manageDestroyedBuildingEvent(color_ostream& out) { // iterate event handler callbacks for (auto &iter: copy) { auto &handler = iter.second; + auto last_tick = eventLastTick[handler.eventHandler]; // enforce handler's callback frequency - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { - auto last_tick = eventLastTick[handler.eventHandler]; + if (tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; // send the handler all the new & destroyed buildings since it last fired auto jter = destroyedBuildings.upper_bound(last_tick); @@ -982,7 +984,8 @@ static void manageConstructionEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &iter : copy) { auto &handler = iter.second; - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; + if (tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; for (auto constru_iter = constructions.begin(); constru_iter != constructions.end();) { df::construction &construction = (*constru_iter).second; @@ -1016,7 +1019,8 @@ static void manageSyndromeEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &iter : copy) { auto &handler = iter.second; - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; + if (tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; for ( df::unit *unit : df::global::world->units.all ) { /* @@ -1052,7 +1056,8 @@ static void manageInvasionEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &iter : copy) { auto &handler = iter.second; - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; + if (tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; handler.eventHandler(out, (void*) intptr_t(nextInvasion - 1)); } @@ -1069,7 +1074,8 @@ static void manageEquipmentEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &iter : copy) { auto &handler = iter.second; - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; + if (tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; for (auto unit : df::global::world->units.all) { itemIdToInventoryItem.clear(); @@ -1173,7 +1179,8 @@ static void manageReportEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &iter : copy) { auto &handler = iter.second; - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; + if (tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; for (; a < reports.size(); ++a) { df::report* report = reports[a]; @@ -1223,7 +1230,8 @@ static void manageUnitAttackEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &iter : copy) { auto &handler = iter.second; - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; + if (tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; for (int reportId : strikeReports) { df::report* report = df::report::find(reportId); @@ -1496,7 +1504,8 @@ static void manageInteractionEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &iter : copy) { auto &handler = iter.second; - if (tick - eventLastTick[handler.eventHandler] >= handler.freq) { + auto last_tick = eventLastTick[handler.eventHandler]; + if (tick - last_tick >= handler.freq) { eventLastTick[handler.eventHandler] = tick; for ( ; a < reports.size(); ++a ) { df::report* report = reports[a]; From 07c2c3ebb625694d5cfcef2663dd66a0bb7bb57c Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 25 Mar 2022 19:25:16 -0700 Subject: [PATCH 13/61] Increases event-testing delay to ensure multiple events should send --- plugins/devel/event-testing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/devel/event-testing.cpp b/plugins/devel/event-testing.cpp index c5aef98dd8..5ac775d649 100644 --- a/plugins/devel/event-testing.cpp +++ b/plugins/devel/event-testing.cpp @@ -49,7 +49,7 @@ void enable_job_completed_events() { namespace EM = EventManager; using namespace EM::EventType; EM::EventHandler eventHandler1(onJobCompletion, 0); // constantly - EM::EventHandler eventHandler2(onJobCompletion, 50); // every dwarf-hour + EM::EventHandler eventHandler2(onJobCompletion, 500); // every dwarf-hour EM::registerListener(EventType::JOB_COMPLETED, eventHandler1, plugin_self); EM::registerListener(EventType::JOB_COMPLETED, eventHandler2, plugin_self); } From 3495c738608e2d9838b0d3a80f5115edab0f42b8 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 25 Mar 2022 19:29:34 -0700 Subject: [PATCH 14/61] Fixes bug in storing last tick values for event handlers --- library/modules/EventManager.cpp | 80 ++++++++++++++++---------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 075ee99b7d..add778e30c 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -58,7 +58,7 @@ static multimap tickQueue; //TODO: consider unordered_map of pairs, or unordered_map of unordered_set, or whatever static multimap handlers[EventType::EVENT_MAX]; -static map eventLastTick; +static std::unordered_map eventLastTick; static const int32_t ticksPerYear = 403200; @@ -75,7 +75,7 @@ void DFHack::EventManager::registerListener(EventType::EventType e, EventHandler handler.when = when; tickQueue.insert(pair(handler.when, handler)); } - eventLastTick[handler.eventHandler] = -1; + eventLastTick[handler] = -1; handlers[e].insert(pair(plugin, handler)); } @@ -91,7 +91,7 @@ int32_t DFHack::EventManager::registerTick(EventHandler handler, int32_t when, P } handler.when = when; tickQueue.insert(pair(handler.when, handler)); - eventLastTick[handler.eventHandler] = -1; + eventLastTick[handler] = -1; //this commented line ensures "Registered Ticks" are not added back to the queue after execution //handlers[EventType::TICK].insert(pair(plugin,handler)); @@ -121,7 +121,7 @@ void DFHack::EventManager::unregister(EventType::EventType e, EventHandler handl continue; } i = handlers[e].erase(i); - eventLastTick.erase(handler.eventHandler); + eventLastTick.erase(handler); if ( e == EventType::TICK ) removeFromTickQueue(handler); } @@ -132,7 +132,7 @@ void DFHack::EventManager::unregisterAll(Plugin* plugin) { if ( (*i).first != plugin ) break; - eventLastTick.erase(i->second.eventHandler); + eventLastTick.erase(i->second); removeFromTickQueue((*i).second); } for (auto &event_type : handlers) { @@ -140,7 +140,7 @@ void DFHack::EventManager::unregisterAll(Plugin* plugin) { if ( (*i).first != plugin ) break; - eventLastTick.erase(i->second.eventHandler); + eventLastTick.erase(i->second); } event_type.erase(plugin); } @@ -460,10 +460,10 @@ void DFHack::EventManager::manageEvents(color_ostream& out) { bool call_events = false; for (auto &iter : handlers[type]) { EventHandler handler = iter.second; - int32_t last_tick = eventLastTick[handler.eventHandler]; + int32_t last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq){ //todo: integrate into every sub-function - //eventLastTick[handler.eventHandler] = tick; + //eventLastTick[handler] = tick; call_events = true; } } @@ -561,11 +561,11 @@ static void manageJobStartedEvent(color_ostream& out) { multimap copy(handlers[EventType::JOB_STARTED].begin(), handlers[EventType::JOB_STARTED].end()); for (auto &iter: copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; oldest_last_tick = oldest_last_tick < last_tick ? oldest_last_tick : last_tick; // make sure the frequency of this handler is obeyed if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; // send the handler all the new jobs since it last fired auto jter = startedJobs.upper_bound(last_tick); for (; jter != startedJobs.end(); ++jter) { @@ -649,11 +649,11 @@ static void manageJobCompletedEvent(color_ostream& out) { // iterate event handler callbacks for (auto &iter : copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; oldest_last_tick = oldest_last_tick < last_tick ? oldest_last_tick : last_tick; // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; fn_last_tick = tick; // send the handler all the newly completed jobs since it last fired auto jter = completedJobs.upper_bound(last_tick); @@ -750,10 +750,10 @@ static void manageNewUnitActiveEvent(color_ostream& out) { // iterate event handler callbacks for (auto &iter : copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; // enforce handler's callback frequency if(tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; // send the handler all the new active unit id's since it last fired auto jter = activeUnits.upper_bound(last_tick); for(;jter != activeUnits.end(); ++jter){ @@ -785,10 +785,10 @@ static void manageUnitDeathEvent(color_ostream& out) { multimap copy(handlers[EventType::UNIT_DEATH].begin(), handlers[EventType::UNIT_DEATH].end()); for (auto &iter: copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; // send the handler all the new dead unit id's since it last fired auto jter = deadUnits.upper_bound(last_tick); for(; jter != deadUnits.end(); ++jter) { @@ -813,9 +813,9 @@ static void manageItemCreationEvent(color_ostream& out) { if ( index != 0 ) index--; for (auto &iter : copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; for (size_t a = index; a < df::global::world->items.all.size(); ++a) { df::item* item = df::global::world->items.all[a]; //already processed @@ -880,10 +880,10 @@ static void manageBuildingEvent(color_ostream& out) { // iterate event handler callbacks (send handlers CREATED buildings) for (auto &iter: copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; // send the handler all the new buildings since it last fired auto jter = createdBuildings.upper_bound(last_tick); for (; jter != createdBuildings.end(); ++jter) { @@ -894,10 +894,10 @@ static void manageBuildingEvent(color_ostream& out) { // iterate event handler callbacks (send handlers DESTROYED buildings for (auto &iter: copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; // send the handler all the destroyed buildings since it last fired auto jter = destroyedBuildings.upper_bound(last_tick); for (; jter != destroyedBuildings.end(); ++jter) { @@ -929,10 +929,10 @@ static void manageCreatedBuildingEvent(color_ostream& out) { // iterate event handler callbacks for (auto &iter: copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; // send the handler all the new & destroyed buildings since it last fired auto jter = createdBuildings.upper_bound(last_tick); for (; jter != createdBuildings.end(); ++jter) { @@ -962,10 +962,10 @@ static void manageDestroyedBuildingEvent(color_ostream& out) { // iterate event handler callbacks for (auto &iter: copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; // send the handler all the new & destroyed buildings since it last fired auto jter = destroyedBuildings.upper_bound(last_tick); for (; jter != destroyedBuildings.end(); ++jter) { @@ -984,9 +984,9 @@ static void manageConstructionEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &iter : copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; for (auto constru_iter = constructions.begin(); constru_iter != constructions.end();) { df::construction &construction = (*constru_iter).second; if (df::construction::find(construction.pos) != NULL) { @@ -1019,9 +1019,9 @@ static void manageSyndromeEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &iter : copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; for ( df::unit *unit : df::global::world->units.all ) { /* if ( unit->flags1.bits.inactive ) @@ -1056,9 +1056,9 @@ static void manageInvasionEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &iter : copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; handler.eventHandler(out, (void*) intptr_t(nextInvasion - 1)); } } @@ -1074,9 +1074,9 @@ static void manageEquipmentEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &iter : copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; for (auto unit : df::global::world->units.all) { itemIdToInventoryItem.clear(); currentlyEquipped.clear(); @@ -1179,9 +1179,9 @@ static void manageReportEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &iter : copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; for (; a < reports.size(); ++a) { df::report* report = reports[a]; handler.eventHandler(out, (void*) intptr_t(report->id)); @@ -1230,9 +1230,9 @@ static void manageUnitAttackEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &iter : copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; for (int reportId : strikeReports) { df::report* report = df::report::find(reportId); if ( !report ) @@ -1504,9 +1504,9 @@ static void manageInteractionEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &iter : copy) { auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; for ( ; a < reports.size(); ++a ) { df::report* report = reports[a]; lastReportInteraction = report->id; From 47b5754a16c9fefcc4cf7d20b1d68d73c6ba98eb Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 25 Mar 2022 19:31:06 -0700 Subject: [PATCH 15/61] Revises foreach naming --- library/modules/EventManager.cpp | 84 ++++++++++++++++---------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index add778e30c..2c30d9c126 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -314,11 +314,11 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event } if ( event == DFHack::SC_MAP_UNLOADED ) { lastJobId = -1; - for (auto &iter : completedJobs) { - Job::deleteJobStruct(iter.second, true); + for (auto &key_value : completedJobs) { + Job::deleteJobStruct(key_value.second, true); } - for (auto &iter : startedJobs) { - Job::deleteJobStruct(iter.second, true); + for (auto &key_value : startedJobs) { + Job::deleteJobStruct(key_value.second, true); } startedJobs.clear(); completedJobs.clear(); @@ -337,8 +337,8 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event gameLoaded = false; multimap copy(handlers[EventType::UNLOAD].begin(), handlers[EventType::UNLOAD].end()); - for (auto & plugin_handler_pair : copy) { - plugin_handler_pair.second.eventHandler(out, NULL); + for (auto &key_value : copy) { + key_value.second.eventHandler(out, NULL); } } else if ( event == DFHack::SC_MAP_LOADED ) { /* @@ -520,11 +520,11 @@ static void manageJobInitiatedEvent(color_ostream& out) { multimap copy(handlers[EventType::JOB_INITIATED].begin(), handlers[EventType::JOB_INITIATED].end()); int32_t tick = df::global::world->frame_counter; - for(auto &iter : copy) { - auto &handler = iter.second; - auto last_tick = eventLastTick[handler.eventHandler]; + for (auto &key_value : copy) { + auto &handler = key_value.second; + auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { - eventLastTick[handler.eventHandler] = tick; + eventLastTick[handler] = tick; for ( df::job_list_link* link = df::global::world->jobs.list.next; link != nullptr; link = link->next ) { if ( link->item == nullptr ) continue; @@ -559,8 +559,8 @@ static void manageJobStartedEvent(color_ostream& out) { } // iterate the event handlers multimap copy(handlers[EventType::JOB_STARTED].begin(), handlers[EventType::JOB_STARTED].end()); - for (auto &iter: copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; oldest_last_tick = oldest_last_tick < last_tick ? oldest_last_tick : last_tick; // make sure the frequency of this handler is obeyed @@ -647,8 +647,8 @@ static void manageJobCompletedEvent(color_ostream& out) { multimap copy(handlers[EventType::JOB_COMPLETED].begin(), handlers[EventType::JOB_COMPLETED].end()); // iterate event handler callbacks - for (auto &iter : copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; oldest_last_tick = oldest_last_tick < last_tick ? oldest_last_tick : last_tick; // enforce handler's callback frequency @@ -748,8 +748,8 @@ static void manageNewUnitActiveEvent(color_ostream& out) { } multimap copy(handlers[EventType::NEW_UNIT_ACTIVE].begin(), handlers[EventType::NEW_UNIT_ACTIVE].end()); // iterate event handler callbacks - for (auto &iter : copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; // enforce handler's callback frequency if(tick - last_tick >= handler.freq) { @@ -783,8 +783,8 @@ static void manageUnitDeathEvent(color_ostream& out) { } // iterate event handler callbacks multimap copy(handlers[EventType::UNIT_DEATH].begin(), handlers[EventType::UNIT_DEATH].end()); - for (auto &iter: copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { @@ -811,8 +811,8 @@ static void manageItemCreationEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; size_t index = df::item::binsearch_index(df::global::world->items.all, nextItem, false); if ( index != 0 ) index--; - for (auto &iter : copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; @@ -878,8 +878,8 @@ static void manageBuildingEvent(color_ostream& out) { // todo: maybe we should create static lists, and compare sizes and only copy if they don't match, otherwise every manager function is copying their handlers every single they execute multimap copy(handlers[EventType::BUILDING].begin(), handlers[EventType::BUILDING].end()); // iterate event handler callbacks (send handlers CREATED buildings) - for (auto &iter: copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { @@ -892,8 +892,8 @@ static void manageBuildingEvent(color_ostream& out) { } } // iterate event handler callbacks (send handlers DESTROYED buildings - for (auto &iter: copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { @@ -927,8 +927,8 @@ static void manageCreatedBuildingEvent(color_ostream& out) { nextBuilding = *df::global::building_next_id; multimap copy(handlers[EventType::CREATED_BUILDING].begin(), handlers[EventType::CREATED_BUILDING].end()); // iterate event handler callbacks - for (auto &iter: copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { @@ -960,8 +960,8 @@ static void manageDestroyedBuildingEvent(color_ostream& out) { } multimap copy(handlers[EventType::DESTROYED_BUILDING].begin(), handlers[EventType::DESTROYED_BUILDING].end()); // iterate event handler callbacks - for (auto &iter: copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { @@ -982,8 +982,8 @@ static void manageConstructionEvent(color_ostream& out) { multimap copy(handlers[EventType::CONSTRUCTION].begin(), handlers[EventType::CONSTRUCTION].end()); int32_t tick = df::global::world->frame_counter; - for (auto &iter : copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; @@ -1017,8 +1017,8 @@ static void manageSyndromeEvent(color_ostream& out) { multimap copy(handlers[EventType::SYNDROME].begin(), handlers[EventType::SYNDROME].end()); int32_t highestTime = -1; int32_t tick = df::global::world->frame_counter; - for (auto &iter : copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; @@ -1054,8 +1054,8 @@ static void manageInvasionEvent(color_ostream& out) { nextInvasion = df::global::ui->invasions.next_id; int32_t tick = df::global::world->frame_counter; - for (auto &iter : copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; @@ -1072,8 +1072,8 @@ static void manageEquipmentEvent(color_ostream& out) { unordered_map itemIdToInventoryItem; unordered_set currentlyEquipped; int32_t tick = df::global::world->frame_counter; - for (auto &iter : copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; @@ -1177,8 +1177,8 @@ static void manageReportEvent(color_ostream& out) { ++a; } int32_t tick = df::global::world->frame_counter; - for (auto &iter : copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; @@ -1228,8 +1228,8 @@ static void manageUnitAttackEvent(color_ostream& out) { updateReportToRelevantUnits(); map > alreadyDone; int32_t tick = df::global::world->frame_counter; - for (auto &iter : copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; @@ -1502,8 +1502,8 @@ static void manageInteractionEvent(color_ostream& out) { //df::unit* lastDefender = NULL; unordered_map > history; int32_t tick = df::global::world->frame_counter; - for (auto &iter : copy) { - auto &handler = iter.second; + for (auto &key_value : copy) { + auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; From ab47f2777cc6962eaa6e02afdd860907b66789b6 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 25 Mar 2022 19:32:06 -0700 Subject: [PATCH 16/61] Moves tickQueue insertion/logic into a separate TU function --- library/modules/EventManager.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 2c30d9c126..f3348ab437 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -62,18 +62,22 @@ static std::unordered_map eventLastTick; static const int32_t ticksPerYear = 403200; +void enqueueTickEvent(EventHandler &handler){ + int32_t when = 0; + df::world* world = df::global::world; + if ( world ) { + when = world->frame_counter + handler.freq; + } else { + if ( Once::doOnce("EventManager registerListener unhonored absolute=false") ) + Core::getInstance().getConsole().print("EventManager::registerTick: warning! absolute flag=false not honored.\n"); + } + handler.when = when; + tickQueue.insert(pair(handler.when, handler)); +} + void DFHack::EventManager::registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin) { if(e == EventType::TICK){ - int32_t when = 0; - df::world* world = df::global::world; - if ( world ) { - when = world->frame_counter + handler.freq; - } else { - if ( Once::doOnce("EventManager registerListener unhonored absolute=false") ) - Core::getInstance().getConsole().print("EventManager::registerTick: warning! absolute flag=false not honored.\n"); - } - handler.when = when; - tickQueue.insert(pair(handler.when, handler)); + enqueueTickEvent(handler); } eventLastTick[handler] = -1; handlers[e].insert(pair(plugin, handler)); From 5c52088bd89bb09f9acdb580a8878ff641f86809 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 25 Mar 2022 19:33:28 -0700 Subject: [PATCH 17/61] Rewrites manageTickEvent, simplifies, adds comments --- library/modules/EventManager.cpp | 44 +++++++++++++++----------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index f3348ab437..2a127b4fdb 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -479,32 +479,30 @@ void DFHack::EventManager::manageEvents(color_ostream& out) { static void manageTickEvent(color_ostream& out) { if (!df::global::world) return; - unordered_set toRemove; int32_t tick = df::global::world->frame_counter; - while ( !tickQueue.empty() ) { - if ( tick < (*tickQueue.begin()).first ) + unordered_set requeue; + // call due tick events + for (auto iter = tickQueue.begin(); iter != tickQueue.end() || iter->first > tick;) { + EventHandler &handler = iter->second; + handler.eventHandler(out, (void*) intptr_t(tick)); + requeue.emplace(handler); + iter = tickQueue.erase(iter); + } + // re-register tick events (only registered tick events though) + for (auto &key_value : handlers[EventType::TICK]) { + if (requeue.empty()) { break; - EventHandler handle = (*tickQueue.begin()).second; - tickQueue.erase(tickQueue.begin()); - handle.eventHandler(out, (void*)intptr_t(tick)); - toRemove.insert(handle); - } - if ( toRemove.empty() ) - return; - for (auto iter = handlers[EventType::TICK].begin(); iter != handlers[EventType::TICK].end(); ) { - EventHandler &handle = iter->second; - //check if we find a handler registered - if ( toRemove.find(handle) == toRemove.end() ) { - //the event is from registerTick, so things are already cleaned up - ++iter; - continue; } - iter = handlers[EventType::TICK].erase(iter); - toRemove.erase(handle); - //the handler is registered with the listeners, so we want it to keep listening - registerListener(DFHack::EventManager::EventType::TICK, handle, iter->first); - if ( toRemove.empty() ) - break; + // check that this handler was fired + auto &handler = key_value.second; + if (requeue.erase(handler) != 0){ + /** + * todo(1): move re-queue to loop above (would mean ALL tick events get re-queued) + * todo(2): don't even use a queue, just follow the same pattern as other managers (fire handler according to freq, removes need for `when` member of handlers) + */ + // any registered handler that was fired must be re-queued + enqueueTickEvent(handler); + } } } From 3fb19543f13ae80857471cc7b29dc3fb811afb93 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 25 Mar 2022 19:33:54 -0700 Subject: [PATCH 18/61] Fixes manageJobCompletedEvent --- library/modules/EventManager.cpp | 50 ++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 2a127b4fdb..362f5b3b6e 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -598,52 +598,52 @@ TODO: consider checking item creation / experience gain just in case static void manageJobCompletedEvent(color_ostream& out) { if (!df::global::world) return; + static std::unordered_map last_tick_jobs; static int32_t fn_last_tick = -1; int32_t tick = df::global::world->frame_counter; int32_t oldest_last_tick = (uint16_t)-1; + std::unordered_map current_jobs; // update the completed jobs list (this apparently isn't as straight forward as one might think) // todo: verify the need for this check //if it happened within a tick, must have been cancelled by the user or a plugin: not completed if(tick > fn_last_tick) { // we're going to need to check if started jobs are still on this list, so we copy the IDs to avoid a linear search into this list for each started_job - std::unordered_map current_jobs; for (df::job_list_link* link = df::global::world->jobs.list.next; link != NULL; link = link->next) { df::job* job = link->item; - current_jobs.emplace(job->id, job); + //can't really minimize the list, because maybe somehow a non-started job will be complete next tick (hax) + //if(job && Job::getWorker(job)) { + current_jobs.emplace(job->id, Job::cloneJobStruct(job, true)); // clone now so we can swap into last_tick_jobs later + //} } // iterate the started jobs list - for (auto &iter: startedJobs) { - df::job* started_job = iter.second; - int32_t id = started_job->id; + for (auto &key_value: last_tick_jobs) { + df::job* lt_job = key_value.second; + int32_t id = lt_job->id; // check for the started job in the current jobs (the jobs we just copied above) auto cter = current_jobs.find(id); if (cter != current_jobs.end()) { // if we found it, that doesn't mean it didn't complete.. it may be a repeat job - df::job* valid_df_job = cter->second; - if (!started_job->flags.bits.repeat) + df::job* ct_job = cter->second; //ct: current tick + if (!lt_job->flags.bits.repeat) continue; // I'd comment these, but I don't know what we're checking exactly.. gl - if (started_job->completion_timer != 0) + if (lt_job->completion_timer != 0) continue; - if (valid_df_job->completion_timer != -1) + if (ct_job->completion_timer != -1) continue; //still false positive if cancelled at EXACTLY the right time, but experiments show this doesn't happen // the job won't be added if it already exists - NOTE: this means a repeat job can't be seen as completed again until it gets cleaned from the list // This is probably going to be a huge problem as it means there is room for an interaction issue between plugins/scripts - if (completedJobIDs.emplace(id).second) { - completedJobs.emplace(tick, Job::cloneJobStruct(started_job, true)); - } + completedJobs.emplace(tick, Job::cloneJobStruct(lt_job, true)); continue; } // we didn't find it, so it's a recently finished or cancelled job - if (started_job->flags.bits.repeat || started_job->completion_timer != 0) + if (lt_job->flags.bits.repeat || lt_job->completion_timer != 0) continue; - if (completedJobIDs.emplace(id).second) { - completedJobs.emplace(tick, Job::cloneJobStruct(started_job, true)); - } + completedJobs.emplace(tick, Job::cloneJobStruct(lt_job, true)); } } @@ -658,21 +658,29 @@ static void manageJobCompletedEvent(color_ostream& out) { eventLastTick[handler] = tick; fn_last_tick = tick; // send the handler all the newly completed jobs since it last fired - auto jter = completedJobs.upper_bound(last_tick); - for (; jter != completedJobs.end(); ++jter) { - handler.eventHandler(out, jter->second); + auto iter = completedJobs.upper_bound(last_tick); + for (; iter != completedJobs.end(); ++iter) { + out.print("callback: %zu, freq: %d, last tick: %d\n", (size_t)handler.eventHandler, handler.freq, last_tick); + handler.eventHandler(out, iter->second); } } } - // clean up memory we no longer need + // clean up memory of job clones which have been sent to all handlers auto iter = completedJobs.begin(); - for(; iter != completedJobs.end() && iter->first != oldest_last_tick;) { + for(; iter != completedJobs.end() && iter->first < oldest_last_tick;) { completedJobIDs.erase(iter->second->id); // we cloned it we delete it Job::deleteJobStruct(iter->second, true); // if we delete it, we best not reference it iter = completedJobs.erase(iter); } + // clean up memory of job clones from last tick + for(auto &key_value : last_tick_jobs){ + Job::deleteJobStruct(key_value.second, true); + } + // last tick jobs takes on the current tick's jobs + last_tick_jobs.swap(current_jobs); + // moved to the bottom to be out of the way, but also all the structures used herein no longer exist #if 0 //testing info on job initiation/completion From d858837ee1780f20641437d5d5c0dc351b0f0812 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 25 Mar 2022 20:15:05 -0700 Subject: [PATCH 19/61] Sets the eventLastTick for new registrants to the `current tick - 1` --- library/modules/EventManager.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 362f5b3b6e..f10a893ce4 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -79,7 +79,11 @@ void DFHack::EventManager::registerListener(EventType::EventType e, EventHandler if(e == EventType::TICK){ enqueueTickEvent(handler); } - eventLastTick[handler] = -1; + int32_t tick = 0; + if (df::global::world) { + tick = df::global::world->frame_counter; + } + eventLastTick[handler] = tick - 1; handlers[e].insert(pair(plugin, handler)); } @@ -95,7 +99,11 @@ int32_t DFHack::EventManager::registerTick(EventHandler handler, int32_t when, P } handler.when = when; tickQueue.insert(pair(handler.when, handler)); - eventLastTick[handler] = -1; + int32_t tick = 0; + if (df::global::world) { + tick = df::global::world->frame_counter; + } + eventLastTick[handler] = tick - 1; //this commented line ensures "Registered Ticks" are not added back to the queue after execution //handlers[EventType::TICK].insert(pair(plugin,handler)); From 04f102bd8a15f18afadec9e1f98a08e1c4075d44 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 25 Mar 2022 22:13:17 -0700 Subject: [PATCH 20/61] Removes debug print line from manageJobCompletedEvent --- library/modules/EventManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index f10a893ce4..da68e76015 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -668,7 +668,6 @@ static void manageJobCompletedEvent(color_ostream& out) { // send the handler all the newly completed jobs since it last fired auto iter = completedJobs.upper_bound(last_tick); for (; iter != completedJobs.end(); ++iter) { - out.print("callback: %zu, freq: %d, last tick: %d\n", (size_t)handler.eventHandler, handler.freq, last_tick); handler.eventHandler(out, iter->second); } } From 52f6e930ad904e8a84a0e2dbbb939828afc0c32f Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Fri, 25 Mar 2022 22:13:38 -0700 Subject: [PATCH 21/61] Fixes bug in manageBuildingEvent --- library/modules/EventManager.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index da68e76015..24ed3563d5 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -868,16 +868,17 @@ static void manageBuildingEvent(color_ostream& out) { **/ int32_t tick = df::global::world->frame_counter; // update destroyed building list - for (auto &iter: createdBuildings) { - int32_t id = iter.second; + for (auto iter = createdBuildings.begin(); iter != createdBuildings.end();) { + int32_t id = iter->second; int32_t index = df::building::binsearch_index(df::global::world->buildings.all, id); // continue if we found the id in world->buildings.all if (index != -1) { + ++iter; continue; } // pretty sure we'd invalidate our loop if we added to buildings here, so we just save the id in an intermediary for now destroyedBuildings.emplace(tick, id); - createdBuildings.erase(id); + iter = createdBuildings.erase(iter); } // update created building list for (int32_t id = nextBuilding; id < *df::global::building_next_id; ++id) { From 4c55d2dfc9d7f994a589aaad99640b513aed67ed Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 00:11:07 -0700 Subject: [PATCH 22/61] Updates manageJobInitiatedEvent to send all events to handlers also fixes a bug in the started job manager --- library/modules/EventManager.cpp | 44 ++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 24ed3563d5..4cf20b6683 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -266,13 +266,14 @@ class event_tracker { //todo: use inheritance? stl seems to use variadics, so it //job initiated static int32_t lastJobId = -1; +static event_tracker newJobs; //job started static std::unordered_set startedJobIDs; static event_tracker startedJobs; //job completed -static std::unordered_set completedJobIDs; +//static std::unordered_set completedJobIDs; static event_tracker completedJobs; //new unit active @@ -523,30 +524,48 @@ static void manageJobInitiatedEvent(color_ostream& out) { lastJobId = *df::global::job_next_id - 1; return; } - if ( lastJobId+1 == *df::global::job_next_id ) { return; //no new jobs } - multimap copy(handlers[EventType::JOB_INITIATED].begin(), handlers[EventType::JOB_INITIATED].end()); int32_t tick = df::global::world->frame_counter; + int32_t oldest_last_tick = (uint16_t)-1; + + // update new jobs list + for ( df::job_list_link* link = df::global::world->jobs.list.next; link != nullptr; link = link->next ) { + df::job* job = link->item; + // the jobs must come after the last known job id + if (job && job->id > lastJobId) { + newJobs.emplace(tick, Job::cloneJobStruct(link->item, true)); + } + } + lastJobId = *df::global::job_next_id - 1; + // iterate the event handlers + multimap copy(handlers[EventType::JOB_INITIATED].begin(), handlers[EventType::JOB_INITIATED].end()); for (auto &key_value : copy) { auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; + oldest_last_tick = oldest_last_tick < last_tick ? oldest_last_tick : last_tick; + // make sure the frequency of this handler is obeyed if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; - for ( df::job_list_link* link = df::global::world->jobs.list.next; link != nullptr; link = link->next ) { - if ( link->item == nullptr ) - continue; - if ( link->item->id <= lastJobId ) - continue; - - handler.eventHandler(out, (void*)link->item); + // send the handler all the new jobs since it last fired + auto jter = newJobs.upper_bound(last_tick); + for(;jter != newJobs.end(); ++jter){ + handler.eventHandler(out, (void*) jter->second); } } } + // clean up memory we no longer need + auto iter = newJobs.begin(); + for(; iter != newJobs.end() && iter->first < oldest_last_tick;) { + // we cloned it we delete it + Job::deleteJobStruct(iter->second, true); + // if we delete it, we best not reference it + iter = newJobs.erase(iter); + } + - lastJobId = *df::global::job_next_id - 1; } static void manageJobStartedEvent(color_ostream& out) { @@ -586,7 +605,7 @@ static void manageJobStartedEvent(color_ostream& out) { } // clean up memory we no longer need auto iter = startedJobs.begin(); - for(; iter != startedJobs.end() && iter->first != oldest_last_tick;) { + for(; iter != startedJobs.end() && iter->first < oldest_last_tick;) { startedJobIDs.erase(iter->second->id); // we cloned it we delete it Job::deleteJobStruct(iter->second, true); @@ -675,7 +694,6 @@ static void manageJobCompletedEvent(color_ostream& out) { // clean up memory of job clones which have been sent to all handlers auto iter = completedJobs.begin(); for(; iter != completedJobs.end() && iter->first < oldest_last_tick;) { - completedJobIDs.erase(iter->second->id); // we cloned it we delete it Job::deleteJobStruct(iter->second, true); // if we delete it, we best not reference it From 3af9892c532867284a1df4ef253b8dd832155de9 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 00:18:06 -0700 Subject: [PATCH 23/61] Adds missing unload clean up for the newJobs list --- library/modules/EventManager.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 4cf20b6683..487928e610 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -327,12 +327,16 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event } if ( event == DFHack::SC_MAP_UNLOADED ) { lastJobId = -1; - for (auto &key_value : completedJobs) { + for (auto &key_value : newJobs) { Job::deleteJobStruct(key_value.second, true); } for (auto &key_value : startedJobs) { Job::deleteJobStruct(key_value.second, true); } + for (auto &key_value : completedJobs) { + Job::deleteJobStruct(key_value.second, true); + } + newJobs.clear(); startedJobs.clear(); completedJobs.clear(); activeUnits.clear(); From 7104f169bc17ae7db695d392ca7414aedcb77ba6 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 00:25:26 -0700 Subject: [PATCH 24/61] Updates manageItemCreationEvent to send all events to handlers --- library/modules/EventManager.cpp | 52 +++++++++++++++++++------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 487928e610..b9a9494066 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -284,6 +284,8 @@ static event_tracker deadUnits; //item creation static int32_t nextItem; +static event_tracker newItems; + //building static int32_t nextBuilding; static event_tracker createdBuildings; @@ -845,38 +847,46 @@ static void manageItemCreationEvent(color_ostream& out) { if ( nextItem >= *df::global::item_next_id ) { return; } - - multimap copy(handlers[EventType::ITEM_CREATED].begin(), handlers[EventType::ITEM_CREATED].end()); int32_t tick = df::global::world->frame_counter; size_t index = df::item::binsearch_index(df::global::world->items.all, nextItem, false); if ( index != 0 ) index--; + for (size_t a = index; a < df::global::world->items.all.size(); ++a) { + df::item* item = df::global::world->items.all[a]; + //already processed + if (item->id < nextItem) + continue; + //invaders + if (item->flags.bits.foreign) + continue; + //traders who bring back your items? + if (item->flags.bits.trader) + continue; + //migrants + if (item->flags.bits.owned) + continue; + //spider webs don't count + if (item->flags.bits.spider_web) + continue; + newItems.emplace(tick, item->id); + } + nextItem = *df::global::item_next_id; + + // iterate event handlers + multimap copy(handlers[EventType::ITEM_CREATED].begin(), handlers[EventType::ITEM_CREATED].end()); for (auto &key_value : copy) { auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; + // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; - for (size_t a = index; a < df::global::world->items.all.size(); ++a) { - df::item* item = df::global::world->items.all[a]; - //already processed - if (item->id < nextItem) - continue; - //invaders - if (item->flags.bits.foreign) - continue; - //traders who bring back your items? - if (item->flags.bits.trader) - continue; - //migrants - if (item->flags.bits.owned) - continue; - //spider webs don't count - if (item->flags.bits.spider_web) - continue; - handler.eventHandler(out, (void*) intptr_t(item->id)); + // send the handler all the new item id's since it last fired + auto iter = newItems.upper_bound(last_tick); + for (; iter != newItems.end(); ++iter) { + handler.eventHandler(out, (void*) intptr_t(iter->second)); } } } - nextItem = *df::global::item_next_id; + } static void manageBuildingEvent(color_ostream& out) { From 93e71936078f63c67d2d7507bdf4bb465442b646 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 00:27:04 -0700 Subject: [PATCH 25/61] Updates comments to match others --- library/modules/EventManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index b9a9494066..486320602e 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -546,7 +546,7 @@ static void manageJobInitiatedEvent(color_ostream& out) { } lastJobId = *df::global::job_next_id - 1; - // iterate the event handlers + // iterate event handler callbacks multimap copy(handlers[EventType::JOB_INITIATED].begin(), handlers[EventType::JOB_INITIATED].end()); for (auto &key_value : copy) { auto &handler = key_value.second; @@ -592,7 +592,7 @@ static void manageJobStartedEvent(color_ostream& out) { } } } - // iterate the event handlers + // iterate event handler callbacks multimap copy(handlers[EventType::JOB_STARTED].begin(), handlers[EventType::JOB_STARTED].end()); for (auto &key_value : copy) { auto &handler = key_value.second; From 9209833fa4769b110debcf371e4fb04f244e03ff Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 00:35:12 -0700 Subject: [PATCH 26/61] Begins modifying manageConstructionEvent --- library/modules/EventManager.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 486320602e..ab84e52dc0 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -1025,10 +1025,24 @@ static void manageDestroyedBuildingEvent(color_ostream& out) { } } + + static void manageConstructionEvent(color_ostream& out) { if (!df::global::world) return; + // todo: finish the job //unordered_set constructionsNow(df::global::world->constructions.begin(), df::global::world->constructions.end()); + for (auto constru_iter = constructions.begin(); constru_iter != constructions.end();) { + df::construction &construction = (*constru_iter).second; + if (df::construction::find(construction.pos) != NULL) { + ++constru_iter; + continue; + } + //construction removed + //out.print("Removed construction (%d,%d,%d)\n", construction.pos.x,construction.pos.y,construction.pos.z); + handler.eventHandler(out, (void*) construction); + constru_iter = constructions.erase(constru_iter); + } multimap copy(handlers[EventType::CONSTRUCTION].begin(), handlers[EventType::CONSTRUCTION].end()); int32_t tick = df::global::world->frame_counter; @@ -1037,17 +1051,7 @@ static void manageConstructionEvent(color_ostream& out) { auto last_tick = eventLastTick[handler]; if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; - for (auto constru_iter = constructions.begin(); constru_iter != constructions.end();) { - df::construction &construction = (*constru_iter).second; - if (df::construction::find(construction.pos) != NULL) { - ++constru_iter; - continue; - } - //construction removed - //out.print("Removed construction (%d,%d,%d)\n", construction.pos.x,construction.pos.y,construction.pos.z); - handler.eventHandler(out, (void*) &construction); - constru_iter = constructions.erase(constru_iter); - } + //for ( auto a = constructionsNow.begin(); a != constructionsNow.end(); ++a ) { for (auto constru_iter = df::global::world->constructions.begin(); constru_iter != df::global::world->constructions.end(); ++constru_iter) { df::construction* construction = *constru_iter; From 32f83fb831456f6256890d2e5ab3876a1c0c4d39 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 03:45:21 -0700 Subject: [PATCH 27/61] Adds std::hash --- library/include/modules/EventManager.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 6f1362b1d3..9111a012ae 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -126,6 +126,21 @@ namespace std { return r; } }; + template <> + struct hash { + std::size_t operator()(const df::construction& construct) const { + auto &c = construct.pos; + size_t r = 17; + const size_t m = 65537; + r = m*(r+c.x); + r = m*(r+c.y); + r = m*(r+c.z); + return r; + } + }; + bool operator==(const df::construction &A, const df::construction &B){ + return A.pos == B.pos; + } } #endif From caec9da9fcf908dbded786246d3d67f43cfdd883 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 03:49:40 -0700 Subject: [PATCH 28/61] Adds more events to devel/event-testing --- plugins/devel/event-testing.cpp | 44 +++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/plugins/devel/event-testing.cpp b/plugins/devel/event-testing.cpp index 5ac775d649..1798cd3610 100644 --- a/plugins/devel/event-testing.cpp +++ b/plugins/devel/event-testing.cpp @@ -6,6 +6,7 @@ #include #include #include +#include //#include "df/world.h" @@ -17,8 +18,8 @@ DFHACK_PLUGIN_IS_ENABLED(enabled); //REQUIRE_GLOBAL(world); void onTick(color_ostream& out, void* tick); -void onJobStart(color_ostream &out, void* job); -void onJobCompletion(color_ostream &out, void* job); +void onJob(color_ostream &out, void* job); +void onConstruction(color_ostream &out, void* construction); command_result skeleton2 (color_ostream &out, std::vector & parameters); @@ -44,20 +45,38 @@ DFhackCExport command_result plugin_shutdown (color_ostream &out) { EM::unregisterAll(plugin_self); return CR_OK; } +const int32_t interval = 500; // 5 dwarf-hours -void enable_job_completed_events() { +void enable_job_events() { namespace EM = EventManager; using namespace EM::EventType; - EM::EventHandler eventHandler1(onJobCompletion, 0); // constantly - EM::EventHandler eventHandler2(onJobCompletion, 500); // every dwarf-hour - EM::registerListener(EventType::JOB_COMPLETED, eventHandler1, plugin_self); - EM::registerListener(EventType::JOB_COMPLETED, eventHandler2, plugin_self); + EM::EventHandler e1(onJob, 0); // constantly + EM::EventHandler e2(onJob, interval); // every interval + EM::registerListener(EventType::JOB_INITIATED, e1, plugin_self); + EM::registerListener(EventType::JOB_INITIATED, e2, plugin_self); + EM::EventHandler e3(onJob, 0); // constantly + EM::EventHandler e4(onJob, interval); // every interval + EM::registerListener(EventType::JOB_STARTED, e3, plugin_self); + EM::registerListener(EventType::JOB_STARTED, e4, plugin_self); + EM::EventHandler e5(onJob, 0); // constantly + EM::EventHandler e6(onJob, interval); // every interval + EM::registerListener(EventType::JOB_COMPLETED, e5, plugin_self); + EM::registerListener(EventType::JOB_COMPLETED, e6, plugin_self); +} +void enable_construction_events() { + namespace EM = EventManager; + using namespace EM::EventType; + EM::EventHandler e1(onConstruction, 0); // constantly + EM::EventHandler e2(onConstruction, interval); // every interval + EM::registerListener(EventType::CONSTRUCTION_REMOVED, e1, plugin_self); + EM::registerListener(EventType::CONSTRUCTION_REMOVED, e2, plugin_self); } DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { namespace EM = EventManager; if (enable && !enabled) { - enable_job_completed_events(); + enable_job_events(); + enable_construction_events(); out.print("plugin enabled!\n"); } else if (!enable && enabled) { EM::unregisterAll(plugin_self); @@ -67,9 +86,14 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { return CR_OK; } -void onJobCompletion(color_ostream &out, void* job) { +void onJob(color_ostream &out, void* job) { auto j = (df::job*)job; std::string type = ENUM_KEY_STR(job_type, j->job_type); - out.print("onJobCompletion: (id: %d) (type: %s) (expire: %d) (completion: %d) (wait: %d)\n", j->id, type.c_str(), j->expire_timer, j->completion_timer, j->wait_timer); + out.print("onJob: (id: %d) (type: %s) (expire: %d) (completion: %d) (wait: %d)\n", j->id, type.c_str(), j->expire_timer, j->completion_timer, j->wait_timer); } +void onConstruction(color_ostream &out, void* construction) { + auto c = (df::construction*)construction; + std::string type = ENUM_KEY_STR(item_type, c->item_type); + out.print("onConstruction: (type: %s)\n", type.c_str()); +} From 2a6b310e0c33dca7f968cd982a9ac7234dde1019 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 03:51:37 -0700 Subject: [PATCH 29/61] Revises BUILDING events, adds CONSTRUCTION (added/removed) events --- library/include/modules/EventManager.h | 8 +- library/modules/EventManager.cpp | 183 ++++++++++++++++--------- 2 files changed, 124 insertions(+), 67 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 9111a012ae..958a2aba75 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -26,9 +26,11 @@ namespace DFHack { UNIT_DEATH, ITEM_CREATED, BUILDING, // todo: deprecate this event - CREATED_BUILDING, - DESTROYED_BUILDING, - CONSTRUCTION, + BUILDING_CREATED, + BUILDING_DESTROYED, + CONSTRUCTION, // todo: deprecate this event + CONSTRUCTION_ADDED, + CONSTRUCTION_REMOVED, SYNDROME, INVASION, INVENTORY_CHANGE, diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index ab84e52dc0..7830246ef3 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -166,9 +166,11 @@ static void manageNewUnitActiveEvent(color_ostream& out); static void manageUnitDeathEvent(color_ostream& out); static void manageItemCreationEvent(color_ostream& out); static void manageBuildingEvent(color_ostream& out); -static void manageCreatedBuildingEvent(color_ostream& out); -static void manageDestroyedBuildingEvent(color_ostream& out); +static void manageBuildingCreatedEvent(color_ostream& out); +static void manageBuildingDestroyedEvent(color_ostream& out); static void manageConstructionEvent(color_ostream& out); +static void manageConstructionAddedEvent(color_ostream& out); +static void manageConstructionRemovedEvent(color_ostream& out); static void manageSyndromeEvent(color_ostream& out); static void manageInvasionEvent(color_ostream& out); static void manageEquipmentEvent(color_ostream& out); @@ -180,24 +182,26 @@ static void manageInteractionEvent(color_ostream& out); typedef void (*eventManager_t)(color_ostream&); static const eventManager_t eventManager[] = { - manageTickEvent, - manageJobInitiatedEvent, - manageJobStartedEvent, - manageJobCompletedEvent, - manageNewUnitActiveEvent, - manageUnitDeathEvent, - manageItemCreationEvent, - manageBuildingEvent, - manageCreatedBuildingEvent, - manageDestroyedBuildingEvent, - manageConstructionEvent, - manageSyndromeEvent, - manageInvasionEvent, - manageEquipmentEvent, - manageReportEvent, - manageUnitAttackEvent, - manageUnloadEvent, - manageInteractionEvent, + manageTickEvent, + manageJobInitiatedEvent, + manageJobStartedEvent, + manageJobCompletedEvent, + manageNewUnitActiveEvent, + manageUnitDeathEvent, + manageItemCreationEvent, + manageBuildingEvent, + manageBuildingCreatedEvent, + manageBuildingDestroyedEvent, + manageConstructionEvent, + manageConstructionAddedEvent, + manageConstructionRemovedEvent, + manageSyndromeEvent, + manageInvasionEvent, + manageEquipmentEvent, + manageReportEvent, + manageUnitAttackEvent, + manageUnloadEvent, + manageInteractionEvent, }; template @@ -266,6 +270,7 @@ class event_tracker { //todo: use inheritance? stl seems to use variadics, so it //job initiated static int32_t lastJobId = -1; +static std::unordered_set newJobIDs; static event_tracker newJobs; //job started @@ -292,7 +297,8 @@ static event_tracker createdBuildings; static event_tracker destroyedBuildings; //construction -static unordered_map constructions; +static event_tracker createdConstructions; +static event_tracker destroyedConstructions; static bool gameLoaded; //syndrome @@ -345,7 +351,8 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event deadUnits.clear(); createdBuildings.clear(); destroyedBuildings.clear(); - constructions.clear(); + createdConstructions.clear(); + destroyedConstructions.clear(); equipmentLog.clear(); //todo: clear reportToRelevantUnits? tickQueue.clear(); @@ -417,22 +424,21 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event // building already existed, so emplace to a non-iterable area createdBuildings.emplace(-1, building->id); } - - constructions.clear(); - for (auto constr : df::global::world->constructions) { - if ( !constr ) { + // initialize our constructions list + for (auto construction : df::global::world->constructions) { + if ( !construction ) { if ( Once::doOnce("EventManager.onLoad null constr") ) { out.print("EventManager.onLoad: null construction.\n"); } continue; } - if ( constr->pos == df::coord() ) { + if (construction->pos == df::coord() ) { if ( Once::doOnce("EventManager.onLoad null position of construction.\n") ) { out.print("EventManager.onLoad null position of construction.\n"); } continue; } - constructions[constr->pos] = *constr; + createdConstructions.emplace(-1, *construction); } lastSyndromeTime = -1; for ( df::unit* unit : df::global::world->units.all ) { @@ -927,7 +933,7 @@ static void manageBuildingEvent(color_ostream& out) { // todo: maybe we should create static lists, and compare sizes and only copy if they don't match, otherwise every manager function is copying their handlers every single they execute multimap copy(handlers[EventType::BUILDING].begin(), handlers[EventType::BUILDING].end()); - // iterate event handler callbacks (send handlers CREATED buildings) + // iterate event handler callbacks for (auto &key_value : copy) { auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; @@ -935,29 +941,18 @@ static void manageBuildingEvent(color_ostream& out) { if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; // send the handler all the new buildings since it last fired - auto jter = createdBuildings.upper_bound(last_tick); - for (; jter != createdBuildings.end(); ++jter) { + for (auto jter = createdBuildings.upper_bound(last_tick); jter != createdBuildings.end(); ++jter) { handler.eventHandler(out, (void*) intptr_t(jter->second)); } - } - } - // iterate event handler callbacks (send handlers DESTROYED buildings - for (auto &key_value : copy) { - auto &handler = key_value.second; - auto last_tick = eventLastTick[handler]; - // enforce handler's callback frequency - if (tick - last_tick >= handler.freq) { - eventLastTick[handler] = tick; // send the handler all the destroyed buildings since it last fired - auto jter = destroyedBuildings.upper_bound(last_tick); - for (; jter != destroyedBuildings.end(); ++jter) { + for (auto jter = destroyedBuildings.upper_bound(last_tick); jter != destroyedBuildings.end(); ++jter) { handler.eventHandler(out, (void*) intptr_t(jter->second)); } } } } -static void manageCreatedBuildingEvent(color_ostream& out) { +static void manageBuildingCreatedEvent(color_ostream& out) { if (!df::global::world) return; if (!df::global::building_next_id) @@ -975,7 +970,7 @@ static void manageCreatedBuildingEvent(color_ostream& out) { destroyedBuildings.erase(id); } nextBuilding = *df::global::building_next_id; - multimap copy(handlers[EventType::CREATED_BUILDING].begin(), handlers[EventType::CREATED_BUILDING].end()); + multimap copy(handlers[EventType::BUILDING_CREATED].begin(), handlers[EventType::BUILDING_CREATED].end()); // iterate event handler callbacks for (auto &key_value : copy) { auto &handler = key_value.second; @@ -992,7 +987,7 @@ static void manageCreatedBuildingEvent(color_ostream& out) { } } -static void manageDestroyedBuildingEvent(color_ostream& out) { +static void manageBuildingDestroyedEvent(color_ostream& out) { if (!df::global::world) return; int32_t tick = df::global::world->frame_counter; @@ -1008,7 +1003,7 @@ static void manageDestroyedBuildingEvent(color_ostream& out) { destroyedBuildings.emplace(tick, id); createdBuildings.erase(id); } - multimap copy(handlers[EventType::DESTROYED_BUILDING].begin(), handlers[EventType::DESTROYED_BUILDING].end()); + multimap copy(handlers[EventType::BUILDING_DESTROYED].begin(), handlers[EventType::BUILDING_DESTROYED].end()); // iterate event handler callbacks for (auto &key_value : copy) { auto &handler = key_value.second; @@ -1030,36 +1025,96 @@ static void manageDestroyedBuildingEvent(color_ostream& out) { static void manageConstructionEvent(color_ostream& out) { if (!df::global::world) return; - // todo: finish the job - //unordered_set constructionsNow(df::global::world->constructions.begin(), df::global::world->constructions.end()); - for (auto constru_iter = constructions.begin(); constru_iter != constructions.end();) { - df::construction &construction = (*constru_iter).second; - if (df::construction::find(construction.pos) != NULL) { - ++constru_iter; + + int32_t tick = df::global::world->frame_counter; + for (auto &c : df::global::world->constructions) { + // anything on the global list, obviously exists.. so we ensure that coord is on the created list and isn't on the destroyed list + createdConstructions.emplace(tick, *c); // hashes based on c->pos (coord) + destroyedConstructions.erase(*c); + } + for (auto iter = createdConstructions.begin(); iter != createdConstructions.end();) { + // if we can't find it, it was removed + if (!df::construction::find(iter->second.pos)) { + destroyedConstructions.emplace(tick, iter->second); + iter = createdConstructions.erase(iter); continue; } - //construction removed - //out.print("Removed construction (%d,%d,%d)\n", construction.pos.x,construction.pos.y,construction.pos.z); - handler.eventHandler(out, (void*) construction); - constru_iter = constructions.erase(constru_iter); + ++iter; } + // iterate event handler callbacks multimap copy(handlers[EventType::CONSTRUCTION].begin(), handlers[EventType::CONSTRUCTION].end()); + for (auto &key_value : copy) { + auto &handler = key_value.second; + auto last_tick = eventLastTick[handler]; + // enforce handler's callback frequency + if (tick - last_tick >= handler.freq) { + eventLastTick[handler] = tick; + // send the handler all the added constructions since it last fired + for(auto iter = createdConstructions.upper_bound(last_tick); iter != createdConstructions.end(); ++iter) { + handler.eventHandler(out, &iter->second); + } + // send the handler all the removed constructions since it last fired + for(auto iter = destroyedConstructions.upper_bound(last_tick); iter != destroyedConstructions.end(); ++iter) { + handler.eventHandler(out, &iter->second); + } + } + } +} + +static void manageConstructionAddedEvent(color_ostream& out) { + if (!df::global::world) + return; + int32_t tick = df::global::world->frame_counter; + // update created construction list + for (auto &c : df::global::world->constructions) { + // anything on the global list, obviously exists.. so we ensure that coord is on the created list and isn't on the destroyed list + createdConstructions.emplace(tick, *c); // hashes based on c->pos (coord) + destroyedConstructions.erase(*c); + } + // iterate event handler callbacks + multimap copy(handlers[EventType::CONSTRUCTION_ADDED].begin(), handlers[EventType::CONSTRUCTION_ADDED].end()); for (auto &key_value : copy) { auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; + // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; + // send the handler all the added constructions since it last fired + for(auto iter = createdConstructions.upper_bound(last_tick); iter != createdConstructions.end(); ++iter) { + handler.eventHandler(out, &iter->second); + } + } + } +} + +static void manageConstructionRemovedEvent(color_ostream& out) { + if (!df::global::world) + return; - //for ( auto a = constructionsNow.begin(); a != constructionsNow.end(); ++a ) { - for (auto constru_iter = df::global::world->constructions.begin(); constru_iter != df::global::world->constructions.end(); ++constru_iter) { - df::construction* construction = *constru_iter; - if (!constructions.emplace(construction->pos, *construction).second) - continue; //not a new insertion, skip - //construction created - //out.print("Created construction (%d,%d,%d)\n", construction->pos.x,construction->pos.y,construction->pos.z); - handler.eventHandler(out, (void*) construction); + int32_t tick = df::global::world->frame_counter; + // update destroyed constructions list + for (auto iter = createdConstructions.begin(); iter != createdConstructions.end();) { + // if we can't find it, it was removed + if (!df::construction::find(iter->second.pos)) { + destroyedConstructions.emplace(tick, iter->second); + iter = createdConstructions.erase(iter); + continue; + } + ++iter; + } + // iterate event handler callbacks + multimap copy(handlers[EventType::CONSTRUCTION_REMOVED].begin(), handlers[EventType::CONSTRUCTION_REMOVED].end()); + for (auto &key_value : copy) { + auto &handler = key_value.second; + auto last_tick = eventLastTick[handler]; + // enforce handler's callback frequency + if (tick - last_tick >= handler.freq) { + eventLastTick[handler] = tick; + // send the handler all the removed constructions since it last fired + for(auto iter = destroyedConstructions.upper_bound(last_tick); iter != destroyedConstructions.end(); ++iter) { + handler.eventHandler(out, &iter->second); } } } From 6a52e22d59fe572839585f9b0d64cf8e6a7cc852 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 04:22:02 -0700 Subject: [PATCH 30/61] Moves operator== from header to TU in which it is used --- library/include/modules/EventManager.h | 3 --- library/modules/EventManager.cpp | 6 +++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 958a2aba75..eedff2379e 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -140,9 +140,6 @@ namespace std { return r; } }; - bool operator==(const df::construction &A, const df::construction &B){ - return A.pos == B.pos; - } } #endif diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 7830246ef3..5381cfbed0 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -203,7 +203,11 @@ static const eventManager_t eventManager[] = { manageUnloadEvent, manageInteractionEvent, }; - +namespace std{ + bool operator==(const df::construction &A, const df::construction &B){ + return A.pos == B.pos; + } +} template class event_tracker { //todo: use inheritance? stl seems to use variadics, so it's unclear how well that would actually work private: From b34f0238ac07b9298db6ffb16cc3a301caa7b29e Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 05:22:27 -0700 Subject: [PATCH 31/61] Adds more events to devel/event-testing --- docs/changelog.txt | 2 +- library/include/modules/EventManager.h | 2 +- plugins/devel/event-testing.cpp | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 0dbfcbc975..5964eb7623 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -45,7 +45,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `dig-now`: handle fortification carving - `EventManager`: add new event type ``JOB_STARTED``, triggered when a job first gains a worker -- `EventManager`: add new event type ``NEW_UNIT_ACTIVE``, triggered when a new unit appears on the active list +- `EventManager`: add new event type ``UNIT_NEW_ACTIVE``, triggered when a new unit appears on the active list - `EventManager`: now each registered handler for an event can have its own frequency instead of all handlers using the lowest frequency of all handlers - `stocks`: allow search terms to match the full item label, even when the label is truncated for length - `dfhack-examples-guide`: add mugs to ``basic`` manager orders diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index eedff2379e..958a5ddabc 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -22,7 +22,7 @@ namespace DFHack { JOB_INITIATED, JOB_STARTED, //has a worker JOB_COMPLETED, - NEW_UNIT_ACTIVE, + UNIT_NEW_ACTIVE, UNIT_DEATH, ITEM_CREATED, BUILDING, // todo: deprecate this event diff --git a/plugins/devel/event-testing.cpp b/plugins/devel/event-testing.cpp index 1798cd3610..f63f8c8b28 100644 --- a/plugins/devel/event-testing.cpp +++ b/plugins/devel/event-testing.cpp @@ -20,6 +20,9 @@ DFHACK_PLUGIN_IS_ENABLED(enabled); void onTick(color_ostream& out, void* tick); void onJob(color_ostream &out, void* job); void onConstruction(color_ostream &out, void* construction); +void onSyndrome(color_ostream &out, void* syndrome); +void onDeath(color_ostream &out, void* unit_id); +void onNewActive(color_ostream &out, void* unit_id); command_result skeleton2 (color_ostream &out, std::vector & parameters); @@ -72,11 +75,23 @@ void enable_construction_events() { EM::registerListener(EventType::CONSTRUCTION_REMOVED, e2, plugin_self); } +void enable_unit_events() { + namespace EM = EventManager; + using namespace EM::EventType; + EM::EventHandler e1(onSyndrome, 0); // constantly + EM::EventHandler e2(onDeath, 0); // constantly + EM::EventHandler e3(onNewActive, 0); // constantly + EM::registerListener(EventType::SYNDROME, e1, plugin_self); + EM::registerListener(EventType::UNIT_DEATH, e2, plugin_self); + EM::registerListener(EventType::UNIT_NEW_ACTIVE, e3, plugin_self); +} + DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { namespace EM = EventManager; if (enable && !enabled) { enable_job_events(); enable_construction_events(); + enable_unit_events(); out.print("plugin enabled!\n"); } else if (!enable && enabled) { EM::unregisterAll(plugin_self); From 642e79ee3220ce45912d6160aa3a9cf57019aede Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 12:51:45 -0700 Subject: [PATCH 32/61] Adds missing include --- library/include/modules/EventManager.h | 1 + 1 file changed, 1 insertion(+) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 958a5ddabc..f61f9acc40 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -13,6 +13,7 @@ #include "df/unit.h" #include "df/unit_inventory_item.h" #include "df/unit_wound.h" +#include "df/construction.h" namespace DFHack { namespace EventManager { From ad433477846b1ae0551e043ac3f1cc4cd553805d Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 12:58:32 -0700 Subject: [PATCH 33/61] Fixes build errors --- library/modules/EventManager.cpp | 2 +- plugins/devel/event-testing.cpp | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 5381cfbed0..5f6a6f65c3 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -797,7 +797,7 @@ static void manageNewUnitActiveEvent(color_ostream& out) { activeUnits.emplace(tick, id); } } - multimap copy(handlers[EventType::NEW_UNIT_ACTIVE].begin(), handlers[EventType::NEW_UNIT_ACTIVE].end()); + multimap copy(handlers[EventType::UNIT_NEW_ACTIVE].begin(), handlers[EventType::UNIT_NEW_ACTIVE].end()); // iterate event handler callbacks for (auto &key_value : copy) { auto &handler = key_value.second; diff --git a/plugins/devel/event-testing.cpp b/plugins/devel/event-testing.cpp index f63f8c8b28..d10e9bb575 100644 --- a/plugins/devel/event-testing.cpp +++ b/plugins/devel/event-testing.cpp @@ -112,3 +112,19 @@ void onConstruction(color_ostream &out, void* construction) { std::string type = ENUM_KEY_STR(item_type, c->item_type); out.print("onConstruction: (type: %s)\n", type.c_str()); } + +void onSyndrome(color_ostream &out, void* syndrome) { + using EventManager::SyndromeData; + auto s = (SyndromeData*)syndrome; + out.print("onSyndrome: (unit: %d) (syndrome: %d)\n", s->unitId, s->syndromeIndex); +} + +void onDeath(color_ostream &out, void* unit_id){ + auto id = (int32_t)(intptr_t)unit_id; + out.print("onDeath: (unit: %d)\n", id); +} + +void onNewActive(color_ostream &out, void* unit_id){ + auto id = (int32_t)(intptr_t)unit_id; + out.print("onNewActive: (unit: %d)\n", id); +} From b22c1c1376b4953dc743af45b455608c4800b322 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 14:18:29 -0700 Subject: [PATCH 34/61] Adds hasher for SyndromeData --- library/include/modules/EventManager.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index f61f9acc40..12f719db19 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -141,6 +141,16 @@ namespace std { return r; } }; + template <> + struct hash { + std::size_t operator()(const DFHack::EventManager::SyndromeData& syndrome){ + size_t r = 43; + const size_t m = 65537; + r = m*(r+syndrome.unitId); + r = m*(r+syndrome.syndromeIndex); + return r; + } + }; } #endif From fff9826618464beaf67a856e17a9673bbef83deb Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 15:10:29 -0700 Subject: [PATCH 35/61] Updates manageSyndromeEvent --- library/include/modules/EventManager.h | 2 +- library/modules/EventManager.cpp | 69 ++++++++++++++++---------- 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 12f719db19..f9711ad753 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -143,7 +143,7 @@ namespace std { }; template <> struct hash { - std::size_t operator()(const DFHack::EventManager::SyndromeData& syndrome){ + std::size_t operator()(const DFHack::EventManager::SyndromeData& syndrome) const { size_t r = 43; const size_t m = 65537; r = m*(r+syndrome.unitId); diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 5f6a6f65c3..7f70fff329 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -207,6 +207,9 @@ namespace std{ bool operator==(const df::construction &A, const df::construction &B){ return A.pos == B.pos; } + bool operator==(const DFHack::EventManager::SyndromeData &A, const DFHack::EventManager::SyndromeData &B){ + return A.unitId == B.unitId && A.syndromeIndex == B.syndromeIndex; + } } template class event_tracker { //todo: use inheritance? stl seems to use variadics, so it's unclear how well that would actually work @@ -272,6 +275,13 @@ class event_tracker { //todo: use inheritance? stl seems to use variadics, so it } }; +static int32_t getTime(){ + if(df::global::world) { + return (*df::global::cur_year) * ticksPerYear + (*df::global::cur_year_tick); + } + return -1; +} + //job initiated static int32_t lastJobId = -1; static std::unordered_set newJobIDs; @@ -306,7 +316,7 @@ static event_tracker destroyedConstructions; static bool gameLoaded; //syndrome -static int32_t lastSyndromeTime; +static event_tracker syndromes; //invasion static int32_t nextInvasion; @@ -444,12 +454,17 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event } createdConstructions.emplace(-1, *construction); } - lastSyndromeTime = -1; - for ( df::unit* unit : df::global::world->units.all ) { - for (auto syndrome : unit->syndromes.active) { - int32_t startTime = syndrome->year*ticksPerYear + syndrome->year_time; - if ( startTime > lastSyndromeTime ) - lastSyndromeTime = startTime; + int32_t current_time = getTime(); + // initialize our syndromes list + for ( df::unit *unit : df::global::world->units.all ) { + for (size_t idx = 0; idx < unit->syndromes.active.size(); ++idx) { + auto &syndrome = unit->syndromes.active[idx]; + int32_t startTime = syndrome->year * ticksPerYear + syndrome->year_time; + // add the syndrome if it started now or in the past + if (startTime <= current_time) { + SyndromeData data(unit->id, (int32_t)idx); + syndromes.emplace(-1, data); + } } } lastReport = -1; @@ -1127,34 +1142,36 @@ static void manageConstructionRemovedEvent(color_ostream& out) { static void manageSyndromeEvent(color_ostream& out) { if (!df::global::world) return; - multimap copy(handlers[EventType::SYNDROME].begin(), handlers[EventType::SYNDROME].end()); - int32_t highestTime = -1; int32_t tick = df::global::world->frame_counter; + + int32_t current_time = getTime(); + // update syndromes list + for ( df::unit *unit : df::global::world->units.all ) { + for (size_t idx = 0; idx < unit->syndromes.active.size(); ++idx) { + auto &syndrome = unit->syndromes.active[idx]; + int32_t startTime = syndrome->year * ticksPerYear + syndrome->year_time; + // add the syndrome if it started now or in the past + if (startTime <= current_time) { + SyndromeData data(unit->id, (int32_t)idx); + syndromes.emplace(tick, data); + } + } + } + + // iterate event handler callbacks + multimap copy(handlers[EventType::SYNDROME].begin(), handlers[EventType::SYNDROME].end()); for (auto &key_value : copy) { auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; + // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; - for ( df::unit *unit : df::global::world->units.all ) { -/* - if ( unit->flags1.bits.inactive ) - continue; -*/ - for ( size_t b = 0; b < unit->syndromes.active.size(); ++b ) { - df::unit_syndrome* syndrome = unit->syndromes.active[b]; - int32_t startTime = syndrome->year*ticksPerYear + syndrome->year_time; - if ( startTime > highestTime ) - highestTime = startTime; - if ( startTime <= lastSyndromeTime ) - continue; - - SyndromeData data(unit->id, b); - handler.eventHandler(out, (void*)&data); - } + // send all new syndromes since it last fired + for(auto iter = syndromes.upper_bound(last_tick); iter != syndromes.end(); ++iter){ + handler.eventHandler(out, &iter->second); } } } - lastSyndromeTime = highestTime; } static void manageInvasionEvent(color_ostream& out) { From aefa5f091e18d3d2522e375f8be66a231c302c13 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 15:20:08 -0700 Subject: [PATCH 36/61] Updates manageInvasionEvent --- library/modules/EventManager.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 7f70fff329..a7291308e6 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -320,6 +320,7 @@ static event_tracker syndromes; //invasion static int32_t nextInvasion; +static event_tracker invasions; //equipment change //static unordered_map > equipmentLog; @@ -1039,8 +1040,6 @@ static void manageBuildingDestroyedEvent(color_ostream& out) { } } - - static void manageConstructionEvent(color_ostream& out) { if (!df::global::world) return; @@ -1174,22 +1173,29 @@ static void manageSyndromeEvent(color_ostream& out) { } } + static void manageInvasionEvent(color_ostream& out) { + if (!df::global::world) + return; if (!df::global::ui) return; - multimap copy(handlers[EventType::INVASION].begin(), handlers[EventType::INVASION].end()); - if ( df::global::ui->invasions.next_id <= nextInvasion ) - return; + int32_t tick = df::global::world->frame_counter; + invasions.emplace(tick, nextInvasion); nextInvasion = df::global::ui->invasions.next_id; - int32_t tick = df::global::world->frame_counter; + // iterate event handler callbacks + multimap copy(handlers[EventType::INVASION].begin(), handlers[EventType::INVASION].end()); for (auto &key_value : copy) { auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; + // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; - handler.eventHandler(out, (void*) intptr_t(nextInvasion - 1)); + // send all new invasions since it last fired + for(auto iter = invasions.upper_bound(last_tick); iter != invasions.end(); ++iter){ + handler.eventHandler(out, (void*)(intptr_t)iter->second); + } } } } From 8a7c2ca30c5ca7472b6497d3f510d0704d189cae Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 21:38:20 -0700 Subject: [PATCH 37/61] Updates manageEquipmentEvent (now manageInventoryChangeEvent) --- library/include/modules/EventManager.h | 25 +++- library/modules/EventManager.cpp | 195 +++++++++++-------------- 2 files changed, 111 insertions(+), 109 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index f9711ad753..8c32f3d5f5 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -77,10 +77,14 @@ namespace DFHack { }; struct InventoryChangeData { int32_t unitId; - InventoryItem* item_old; - InventoryItem* item_new; + // todo: don't use pointers + std::shared_ptr item_old; + std::shared_ptr item_new; InventoryChangeData() {} - InventoryChangeData(int32_t id_in, InventoryItem* old_in, InventoryItem* new_in): unitId(id_in), item_old(old_in), item_new(new_in) {} + InventoryChangeData(int32_t id_in, InventoryItem* old_in, InventoryItem* new_in) : + unitId(id_in), + item_old(old_in), + item_new(new_in) {} }; struct UnitAttackData { @@ -151,6 +155,21 @@ namespace std { return r; } }; + template <> + struct hash { + std::size_t operator()(const DFHack::EventManager::InventoryChangeData& icd) const { + size_t r = 43; + const size_t m = 65537; + r = m*(r+icd.unitId); + if (icd.item_new) { + r=m*(r+icd.item_new->itemId); + } + if (icd.item_old) { + r=m*(r+(2*icd.item_old->itemId)); + } + return r; + } + }; } #endif diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index a7291308e6..79028146e6 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -42,6 +42,7 @@ #include #include #include +#include using namespace std; using namespace DFHack; @@ -173,7 +174,7 @@ static void manageConstructionAddedEvent(color_ostream& out); static void manageConstructionRemovedEvent(color_ostream& out); static void manageSyndromeEvent(color_ostream& out); static void manageInvasionEvent(color_ostream& out); -static void manageEquipmentEvent(color_ostream& out); +static void manageInventoryChangeEvent(color_ostream& out); static void manageReportEvent(color_ostream& out); static void manageUnitAttackEvent(color_ostream& out); static void manageUnloadEvent(color_ostream& out){}; @@ -197,7 +198,7 @@ static const eventManager_t eventManager[] = { manageConstructionRemovedEvent, manageSyndromeEvent, manageInvasionEvent, - manageEquipmentEvent, + manageInventoryChangeEvent, manageReportEvent, manageUnitAttackEvent, manageUnloadEvent, @@ -210,6 +211,12 @@ namespace std{ bool operator==(const DFHack::EventManager::SyndromeData &A, const DFHack::EventManager::SyndromeData &B){ return A.unitId == B.unitId && A.syndromeIndex == B.syndromeIndex; } + bool operator==(const DFHack::EventManager::InventoryChangeData &A, const DFHack::EventManager::InventoryChangeData &B) { + bool unit = A.unitId == B.unitId; + bool newItem = (A.item_new && B.item_new && A.item_new->itemId == B.item_new->itemId) || (!A.item_new && A.item_new == B.item_new); + bool oldItem = (A.item_old && B.item_old && A.item_old->itemId == B.item_old->itemId) || (!A.item_old && A.item_old == B.item_old); + return unit && newItem && oldItem; + } } template class event_tracker { //todo: use inheritance? stl seems to use variadics, so it's unclear how well that would actually work @@ -217,6 +224,7 @@ class event_tracker { //todo: use inheritance? stl seems to use variadics, so it std::unordered_map seen; std::multimap history; public: + event_tracker()= default; void clear() { seen.clear(); history.clear(); @@ -284,7 +292,7 @@ static int32_t getTime(){ //job initiated static int32_t lastJobId = -1; -static std::unordered_set newJobIDs; +// static std::unordered_set newJobIDs; // function fulfilled by lastJobId static event_tracker newJobs; //job started @@ -323,8 +331,8 @@ static int32_t nextInvasion; static event_tracker invasions; //equipment change -//static unordered_map > equipmentLog; -static unordered_map > equipmentLog; +static unordered_map > inventoryLog; +event_tracker equipmentChanges; //report static int32_t lastReport; @@ -359,6 +367,9 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event for (auto &key_value : completedJobs) { Job::deleteJobStruct(key_value.second, true); } +// for (auto &key_value : inventoryLog) { +// //key_value +// } newJobs.clear(); startedJobs.clear(); completedJobs.clear(); @@ -368,7 +379,8 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event destroyedBuildings.clear(); createdConstructions.clear(); destroyedConstructions.clear(); - equipmentLog.clear(); + inventoryLog.clear(); + equipmentChanges.clear(); //todo: clear reportToRelevantUnits? tickQueue.clear(); @@ -382,15 +394,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event key_value.second.eventHandler(out, NULL); } } 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++ ) - newTickQueue.insert(pair(tick+(*i).first, (*i).second)); - tickQueue.clear(); - tickQueue.insert(newTickQueue.begin(), newTickQueue.end()); - //out.print("%s,%d: on load, frame_counter = %d\n", __FILE__, __LINE__, tick); - */ + //tickQueue.clear(); if (!df::global::item_next_id) return; @@ -417,22 +421,6 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event startedJobs.emplace(-1, Job::cloneJobStruct(job, true)); } } - // initialize our active units list - for (df::unit* unit : df::global::world->units.active) { - int32_t id = unit->id; - if (!activeUnits.contains(id)) { - // unit is already active, so emplace to a non-iterable area - activeUnits.emplace(-1, id); - } - } - // initialize our dead units list - for (df::unit* unit: df::global::world->units.all) { - //if ( unit->counters.death_id == -1 ) { - if (!Units::isActive(unit)) { - // unit is already dead, so emplace to a non-iterable area - deadUnits.emplace(-1, unit->id); - } - } // initialize our buildings list for (df::building* building : df::global::world->buildings.all) { Buildings::updateBuildings(out, (void*)intptr_t(building->id)); @@ -456,8 +444,17 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event createdConstructions.emplace(-1, *construction); } int32_t current_time = getTime(); + // initialize our active units list + // initialize our dead units list + // initialize our inventory lists // initialize our syndromes list for ( df::unit *unit : df::global::world->units.all ) { + // add to either the active or dead list + if(Units::isActive(unit)) { + activeUnits.emplace(-1, unit->id); + } else { + deadUnits.emplace(-1, unit->id); + } for (size_t idx = 0; idx < unit->syndromes.active.size(); ++idx) { auto &syndrome = unit->syndromes.active[idx]; int32_t startTime = syndrome->year * ticksPerYear + syndrome->year_time; @@ -467,6 +464,14 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event syndromes.emplace(-1, data); } } + //update equipment + unordered_map &last_tick_inventory = inventoryLog[unit->id]; + vector ¤t_tick_inventory = unit->inventory; + last_tick_inventory.clear(); + for (auto ct_item : current_tick_inventory) { + auto itemId = ct_item->item->id; + last_tick_inventory.emplace(itemId, InventoryItem(itemId, *ct_item)); + } } lastReport = -1; if ( !df::global::world->status.reports.empty() ) { @@ -552,13 +557,6 @@ static void manageJobInitiatedEvent(color_ostream& out) { return; if (!df::global::job_next_id) return; - if ( lastJobId == -1 ) { - lastJobId = *df::global::job_next_id - 1; - return; - } - if ( lastJobId+1 == *df::global::job_next_id ) { - return; //no new jobs - } int32_t tick = df::global::world->frame_counter; int32_t oldest_last_tick = (uint16_t)-1; @@ -1173,7 +1171,6 @@ static void manageSyndromeEvent(color_ostream& out) { } } - static void manageInvasionEvent(color_ostream& out) { if (!df::global::world) return; @@ -1200,82 +1197,68 @@ static void manageInvasionEvent(color_ostream& out) { } } -static void manageEquipmentEvent(color_ostream& out) { + +static void manageInventoryChangeEvent(color_ostream& out) { if (!df::global::world) return; - multimap copy(handlers[EventType::INVENTORY_CHANGE].begin(), handlers[EventType::INVENTORY_CHANGE].end()); - unordered_map itemIdToInventoryItem; - unordered_set currentlyEquipped; int32_t tick = df::global::world->frame_counter; + for (auto unit : df::global::world->units.all) { + auto unitId = unit->id; + unordered_map &last_tick_inventory = inventoryLog[unitId]; + vector ¤t_tick_inventory = unit->inventory; + unordered_set currentlyEquipped; + + // iterate current tick's inventory for unit (std::vector) + for (auto ct_item : current_tick_inventory) { + auto itemId = ct_item->item->id; + currentlyEquipped.emplace(itemId); + + // detect new + auto lti_iter = last_tick_inventory.find(itemId); + if (lti_iter == last_tick_inventory.end()) { + // add to list because the item didn't exist before + equipmentChanges.emplace(tick, InventoryChangeData(unitId, nullptr, new InventoryItem(itemId, *ct_item))); + continue; + } + + // detect change - (in how item is equipped) + InventoryItem item_old = lti_iter->second; + df::unit_inventory_item &item0 = item_old.item; + df::unit_inventory_item &item1 = *ct_item; + if (item0.mode == item1.mode && item0.body_part_id == item1.body_part_id && + item0.wound_id == item1.wound_id) + continue; + // add to list because item is not equipped in the same way + equipmentChanges.emplace(tick, InventoryChangeData(unit->id, new InventoryItem(item_old.itemId, item_old.item), new InventoryItem(itemId, *ct_item))); + } + //check for dropped items + for (auto &key_value : last_tick_inventory) { + auto &item = key_value.second; + // add to list if unit doesn't have the item + if (currentlyEquipped.find(item.itemId) == currentlyEquipped.end()) { + equipmentChanges.emplace(tick, InventoryChangeData(unit->id, new InventoryItem(item.itemId, item.item), nullptr)); + } + } + + //update equipment + last_tick_inventory.clear(); + for (auto ct_item : current_tick_inventory) { + auto itemId = ct_item->item->id; + last_tick_inventory.emplace(itemId, InventoryItem(itemId, *ct_item)); + } + } + // iterate event handler callbacks + multimap copy(handlers[EventType::INVENTORY_CHANGE].begin(), handlers[EventType::INVENTORY_CHANGE].end()); for (auto &key_value : copy) { auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; + // ensure handler's callback frequency if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; - for (auto unit : df::global::world->units.all) { - itemIdToInventoryItem.clear(); - currentlyEquipped.clear(); - /*if ( unit->flags1.bits.inactive ) - continue; - */ - - auto oldEquipment = equipmentLog.find(unit->id); - bool hadEquipment = oldEquipment != equipmentLog.end(); - vector* temp; - if (hadEquipment) { - temp = &((*oldEquipment).second); - } else { - temp = new vector; - } - //vector& v = (*oldEquipment).second; - vector &v = *temp; - for (auto &item : v) { - itemIdToInventoryItem[item.itemId] = item; - } - for (size_t b = 0; b < unit->inventory.size(); ++b) { - df::unit_inventory_item* dfitem_new = unit->inventory[b]; - currentlyEquipped.insert(dfitem_new->item->id); - InventoryItem item_new(dfitem_new->item->id, *dfitem_new); - auto c = itemIdToInventoryItem.find(dfitem_new->item->id); - if (c == itemIdToInventoryItem.end()) { - //new item equipped (probably just picked up) - InventoryChangeData data(unit->id, NULL, &item_new); - handler.eventHandler(out, (void*) &data); - continue; - } - InventoryItem item_old = (*c).second; - - df::unit_inventory_item &item0 = item_old.item; - df::unit_inventory_item &item1 = item_new.item; - if (item0.mode == item1.mode && item0.body_part_id == item1.body_part_id && - item0.wound_id == item1.wound_id) - continue; - //some sort of change in how it's equipped - - InventoryChangeData data(unit->id, &item_old, &item_new); - handler.eventHandler(out, (void*) &data); - } - //check for dropped items - for (auto b = v.begin(); b != v.end(); ++b) { - InventoryItem i = *b; - if (currentlyEquipped.find(i.itemId) != currentlyEquipped.end()) - continue; - //TODO: delete ptr if invalid - InventoryChangeData data(unit->id, &i, NULL); - handler.eventHandler(out, (void*) &data); - } - if (!hadEquipment) - delete temp; - - //update equipment - vector &equipment = equipmentLog[unit->id]; - equipment.clear(); - for (size_t b = 0; b < unit->inventory.size(); ++b) { - df::unit_inventory_item* dfitem = unit->inventory[b]; - InventoryItem item(dfitem->item->id, *dfitem); - equipment.push_back(item); - } + // send all new equipment changes since it last fired + for(auto iter = equipmentChanges.upper_bound(last_tick); iter != equipmentChanges.end(); ++iter){ + handler.eventHandler(out, &iter->second); } } } From dd8bc105e362bb3dcb10d578f12657b0c544cfa4 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 21:39:44 -0700 Subject: [PATCH 38/61] Adds to devel/event-testing --- plugins/devel/event-testing.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/plugins/devel/event-testing.cpp b/plugins/devel/event-testing.cpp index d10e9bb575..e53f5af730 100644 --- a/plugins/devel/event-testing.cpp +++ b/plugins/devel/event-testing.cpp @@ -23,6 +23,7 @@ void onConstruction(color_ostream &out, void* construction); void onSyndrome(color_ostream &out, void* syndrome); void onDeath(color_ostream &out, void* unit_id); void onNewActive(color_ostream &out, void* unit_id); +void onItem(color_ostream &out, void* inventory_change_data); command_result skeleton2 (color_ostream &out, std::vector & parameters); @@ -89,9 +90,15 @@ void enable_unit_events() { DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { namespace EM = EventManager; if (enable && !enabled) { - enable_job_events(); - enable_construction_events(); - enable_unit_events(); + //enable_job_events(); + //enable_construction_events(); + //enable_unit_events(); + namespace EM = EventManager; + using namespace EM::EventType; + EM::EventHandler e1(onItem, 0); + EM::EventHandler e2(onItem, interval); + EM::registerListener(EventType::INVENTORY_CHANGE, e1, plugin_self); + EM::registerListener(EventType::INVENTORY_CHANGE, e2, plugin_self); out.print("plugin enabled!\n"); } else if (!enable && enabled) { EM::unregisterAll(plugin_self); @@ -128,3 +135,12 @@ void onNewActive(color_ostream &out, void* unit_id){ auto id = (int32_t)(intptr_t)unit_id; out.print("onNewActive: (unit: %d)\n", id); } + +void onItem(color_ostream &out, void* inventory_change_data) { + namespace EM = EventManager; + auto data = (EM::InventoryChangeData*)inventory_change_data; + int32_t new_id = data->item_new ? data->item_new->itemId : -1; + int32_t old_id = data->item_old ? data->item_old->itemId : -1; + out.print("onItem: (old id: %d) (new id: %d)\n", old_id, new_id); +} + From fb71e213f06b424860347e655679fcda831eead2 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sat, 26 Mar 2022 21:56:14 -0700 Subject: [PATCH 39/61] Replaces dead unit check with the appropriate Units::isDead() --- library/modules/EventManager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 79028146e6..4df3be89d2 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -449,10 +449,10 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event // initialize our inventory lists // initialize our syndromes list for ( df::unit *unit : df::global::world->units.all ) { - // add to either the active or dead list if(Units::isActive(unit)) { activeUnits.emplace(-1, unit->id); - } else { + } + if (Units::isDead(unit)) { deadUnits.emplace(-1, unit->id); } for (size_t idx = 0; idx < unit->syndromes.active.size(); ++idx) { @@ -836,7 +836,7 @@ static void manageUnitDeathEvent(color_ostream& out) { // update dead units list for the current tick for (df::unit* unit: df::global::world->units.all) { //if ( unit->counters.death_id == -1 ) { - if (!Units::isActive(unit)) { + if (Units::isDead(unit)) { if (!deadUnits.contains(unit->id)) { deadUnits.emplace(tick, unit->id); activeUnits.erase(unit->id); @@ -1197,7 +1197,6 @@ static void manageInvasionEvent(color_ostream& out) { } } - static void manageInventoryChangeEvent(color_ostream& out) { if (!df::global::world) return; @@ -1264,6 +1263,7 @@ static void manageInventoryChangeEvent(color_ostream& out) { } } + static void updateReportToRelevantUnits() { if (!df::global::world) return; From 4a812439c27feb181cc6f25b1a503fdede8b748c Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sun, 27 Mar 2022 21:26:06 -0700 Subject: [PATCH 40/61] Updates manageReportEvent --- library/modules/EventManager.cpp | 34 +++++++++++++++++--------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 4df3be89d2..33e2163407 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -335,7 +335,7 @@ static unordered_map > inventoryL event_tracker equipmentChanges; //report -static int32_t lastReport; +static event_tracker newReports; //unit attack static int32_t lastReportUnitAttack; @@ -381,11 +381,11 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event destroyedConstructions.clear(); inventoryLog.clear(); equipmentChanges.clear(); + newReports.clear(); //todo: clear reportToRelevantUnits? tickQueue.clear(); Buildings::clearBuildings(out); - lastReport = -1; lastReportUnitAttack = -1; gameLoaded = false; @@ -473,9 +473,9 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event last_tick_inventory.emplace(itemId, InventoryItem(itemId, *ct_item)); } } - lastReport = -1; - if ( !df::global::world->status.reports.empty() ) { - lastReport = df::global::world->status.reports[df::global::world->status.reports.size()-1]->id; + // initialize our reports list + for(auto &r : df::global::world->status.reports){ + newReports.emplace(-1, r->id); } lastReportUnitAttack = -1; lastReportInteraction = -1; @@ -1288,23 +1288,25 @@ static void updateReportToRelevantUnits() { static void manageReportEvent(color_ostream& out) { if (!df::global::world) return; - multimap copy(handlers[EventType::REPORT].begin(), handlers[EventType::REPORT].end()); - std::vector& reports = df::global::world->status.reports; - size_t a = df::report::binsearch_index(reports, lastReport, false); - //this may or may not be needed: I don't know if binsearch_index goes earlier or later if it can't hit the target exactly - while (a < reports.size() && reports[a]->id <= lastReport) { - ++a; - } + int32_t tick = df::global::world->frame_counter; + // update reports list + for(auto &r : df::global::world->status.reports){ + newReports.emplace(tick, r->id); + } + + // iterate event handler callbacks + multimap copy(handlers[EventType::REPORT].begin(), handlers[EventType::REPORT].end()); for (auto &key_value : copy) { auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; + // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; - for (; a < reports.size(); ++a) { - df::report* report = reports[a]; - handler.eventHandler(out, (void*) intptr_t(report->id)); - lastReport = report->id; + // send all new reports since it last fired + auto iter = newReports.upper_bound(last_tick); + for(;iter != newReports.end(); ++iter) { + handler.eventHandler(out, (void*) intptr_t(iter->second)); } } } From 8843bf5a095fa5a135fdec8ea9ce728fa3a5e51b Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sun, 27 Mar 2022 21:28:33 -0700 Subject: [PATCH 41/61] Fixes segfault waiting to happen --- library/modules/EventManager.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 33e2163407..b9629dc3b0 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -1010,16 +1010,18 @@ static void manageBuildingDestroyedEvent(color_ostream& out) { return; int32_t tick = df::global::world->frame_counter; // update destroyed building list - for (auto &iter: createdBuildings) { - int32_t id = iter.second; + for (auto iter = createdBuildings.begin(); iter != createdBuildings.end();) { + int32_t id = iter->second; int32_t index = df::building::binsearch_index(df::global::world->buildings.all, id); // continue if we found the id in world->buildings.all if (index != -1) { + ++iter; continue; } // pretty sure we'd invalidate our loop if we added to buildings here, so we just save the id in an intermediary for now destroyedBuildings.emplace(tick, id); - createdBuildings.erase(id); + // handlers which haven't handled this building yet aren't going to (it would be very tricky to make this work) + iter = createdBuildings.erase(id); } multimap copy(handlers[EventType::BUILDING_DESTROYED].begin(), handlers[EventType::BUILDING_DESTROYED].end()); // iterate event handler callbacks From 14ece2745d7d714b36c3c08243831907dccd616d Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sun, 27 Mar 2022 21:30:23 -0700 Subject: [PATCH 42/61] Adds comments about intentionally lost events created/destroyed events need to erase from each other to ensure new events are seen --- library/modules/EventManager.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index b9629dc3b0..508f106ba2 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -934,6 +934,7 @@ static void manageBuildingEvent(color_ostream& out) { } // pretty sure we'd invalidate our loop if we added to buildings here, so we just save the id in an intermediary for now destroyedBuildings.emplace(tick, id); + // handlers which haven't handled this building yet aren't going to (it would be very tricky to make this work) iter = createdBuildings.erase(iter); } // update created building list @@ -945,6 +946,7 @@ static void manageBuildingEvent(color_ostream& out) { continue; } createdBuildings.emplace(tick, id); + // handlers which haven't handled this building yet aren't going to (it would be very tricky to make this work) destroyedBuildings.erase(id); } nextBuilding = *df::global::building_next_id; @@ -985,6 +987,7 @@ static void manageBuildingCreatedEvent(color_ostream& out) { continue; } createdBuildings.emplace(tick, id); + // handlers which haven't handled this building yet aren't going to (it would be very tricky to make this work) destroyedBuildings.erase(id); } nextBuilding = *df::global::building_next_id; @@ -1047,13 +1050,15 @@ static void manageConstructionEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &c : df::global::world->constructions) { // anything on the global list, obviously exists.. so we ensure that coord is on the created list and isn't on the destroyed list - createdConstructions.emplace(tick, *c); // hashes based on c->pos (coord) + createdConstructions.emplace(tick, *c); // hashes based on c->pos (coord)\ + // handlers which haven't handled this construction yet aren't going to (it would be very tricky to make this work) destroyedConstructions.erase(*c); } for (auto iter = createdConstructions.begin(); iter != createdConstructions.end();) { // if we can't find it, it was removed if (!df::construction::find(iter->second.pos)) { destroyedConstructions.emplace(tick, iter->second); + // handlers which haven't handled this construction yet aren't going to (it would be very tricky to make this work) iter = createdConstructions.erase(iter); continue; } @@ -1089,6 +1094,7 @@ static void manageConstructionAddedEvent(color_ostream& out) { for (auto &c : df::global::world->constructions) { // anything on the global list, obviously exists.. so we ensure that coord is on the created list and isn't on the destroyed list createdConstructions.emplace(tick, *c); // hashes based on c->pos (coord) + // handlers which haven't handled this construction yet aren't going to (it would be very tricky to make this work) destroyedConstructions.erase(*c); } // iterate event handler callbacks @@ -1117,6 +1123,7 @@ static void manageConstructionRemovedEvent(color_ostream& out) { // if we can't find it, it was removed if (!df::construction::find(iter->second.pos)) { destroyedConstructions.emplace(tick, iter->second); + // handlers which haven't handled this construction yet aren't going to (it would be very tricky to make this work) iter = createdConstructions.erase(iter); continue; } From d3d3dfe69e767004500828a410b7fb2e44a37931 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sun, 27 Mar 2022 21:31:07 -0700 Subject: [PATCH 43/61] Revises manageSyndromEvent --- library/modules/EventManager.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 508f106ba2..f66e891d57 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -1158,8 +1158,7 @@ static void manageSyndromeEvent(color_ostream& out) { int32_t startTime = syndrome->year * ticksPerYear + syndrome->year_time; // add the syndrome if it started now or in the past if (startTime <= current_time) { - SyndromeData data(unit->id, (int32_t)idx); - syndromes.emplace(tick, data); + syndromes.emplace(tick, SyndromeData(unit->id, (int32_t)idx)); } } } From b65a18144cc576d5599724d6cba77da41c1340a7 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sun, 27 Mar 2022 21:31:56 -0700 Subject: [PATCH 44/61] Reorders functions in file to keep all event managers together --- library/include/modules/EventManager.h | 1 + library/modules/EventManager.cpp | 290 +++++++++++++------------ 2 files changed, 150 insertions(+), 141 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 8c32f3d5f5..d7986728f1 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -75,6 +75,7 @@ namespace DFHack { InventoryItem() {} InventoryItem(int32_t id_in, df::unit_inventory_item item_in): itemId(id_in), item(item_in) {} }; + struct InventoryChangeData { int32_t unitId; // todo: don't use pointers diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index f66e891d57..09263ee69d 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -1271,28 +1271,6 @@ static void manageInventoryChangeEvent(color_ostream& out) { } } - -static void updateReportToRelevantUnits() { - if (!df::global::world) - return; - if ( df::global::world->frame_counter <= reportToRelevantUnitsTime ) - return; - reportToRelevantUnitsTime = df::global::world->frame_counter; - - for ( df::unit *unit : df::global::world->units.all ) { - for ( int16_t b = df::enum_traits::first_item_value; b <= df::enum_traits::last_item_value; ++b ) { - if ( b == df::unit_report_type::Sparring ) - continue; - for ( size_t c = 0; c < unit->reports.log[b].size(); c++ ) { - int32_t report = unit->reports.log[b][c]; - if ( std::find(reportToRelevantUnits[report].begin(), reportToRelevantUnits[report].end(), unit->id) != reportToRelevantUnits[report].end() ) - continue; - reportToRelevantUnits[unit->reports.log[b][c]].push_back(unit->id); - } - } - } -} - static void manageReportEvent(color_ostream& out) { if (!df::global::world) return; @@ -1320,20 +1298,13 @@ static void manageReportEvent(color_ostream& out) { } } -static df::unit_wound* getWound(df::unit* attacker, df::unit* defender) { - for ( size_t a = 0; a < defender->body.wounds.size(); ++a ) { - df::unit_wound* wound = defender->body.wounds[a]; - if ( wound->age <= 1 && wound->attacker_unit_id == attacker->id ) { - return wound; - } - } - return NULL; -} +static void updateReportToRelevantUnits(); +static df::unit_wound* getWound(df::unit* attacker, df::unit* defender); static void manageUnitAttackEvent(color_ostream& out) { if (!df::global::world) return; - multimap copy(handlers[EventType::UNIT_ATTACK].begin(), handlers[EventType::UNIT_ATTACK].end()); + int32_t tick = df::global::world->frame_counter; std::vector& reports = df::global::world->status.reports; size_t a = df::report::binsearch_index(reports, lastReportUnitAttack, false); //this may or may not be needed: I don't know if binsearch_index goes earlier or later if it can't hit the target exactly @@ -1344,19 +1315,21 @@ static void manageUnitAttackEvent(color_ostream& out) { for ( ; a < reports.size(); ++a ) { df::report* report = reports[a]; lastReportUnitAttack = report->id; - if ( report->flags.bits.continuation ) + if ( report->flags.bits.continuation ) { continue; + } df::announcement_type type = report->type; if ( type == df::announcement_type::COMBAT_STRIKE_DETAILS ) { strikeReports.insert(report->id); } } - if ( strikeReports.empty() ) + if ( strikeReports.empty() ) { return; + } updateReportToRelevantUnits(); map > alreadyDone; - int32_t tick = df::global::world->frame_counter; + multimap copy(handlers[EventType::UNIT_ATTACK].begin(), handlers[EventType::UNIT_ATTACK].end()); for (auto &key_value : copy) { auto &handler = key_value.second; auto last_tick = eventLastTick[handler]; @@ -1441,6 +1414,147 @@ static void manageUnitAttackEvent(color_ostream& out) { } } +static vector gatherRelevantUnits(color_ostream& out, df::report* r1, df::report* r2); +static InteractionData getAttacker(color_ostream& out, df::report* attackEvent, df::unit* lastAttacker, df::report* defendEvent, vector& relevantUnits); +static void manageInteractionEvent(color_ostream& out) { + if (!df::global::world) + return; + multimap copy(handlers[EventType::INTERACTION].begin(), handlers[EventType::INTERACTION].end()); + std::vector& reports = df::global::world->status.reports; + size_t a = df::report::binsearch_index(reports, lastReportInteraction, false); + while (a < reports.size() && reports[a]->id <= lastReportInteraction) { + ++a; + } + if ( a < reports.size() ) + updateReportToRelevantUnits(); + + df::report* lastAttackEvent = NULL; + df::unit* lastAttacker = NULL; + //df::unit* lastDefender = NULL; + unordered_map > history; + int32_t tick = df::global::world->frame_counter; + for (auto &key_value : copy) { + auto &handler = key_value.second; + auto last_tick = eventLastTick[handler]; + if (tick - last_tick >= handler.freq) { + eventLastTick[handler] = tick; + for ( ; a < reports.size(); ++a ) { + df::report* report = reports[a]; + lastReportInteraction = report->id; + df::announcement_type type = report->type; + if ( type != df::announcement_type::INTERACTION_ACTOR && type != df::announcement_type::INTERACTION_TARGET ) + continue; + if ( report->flags.bits.continuation ) + continue; + bool attack = type == df::announcement_type::INTERACTION_ACTOR; + if ( attack ) { + lastAttackEvent = report; + lastAttacker = NULL; + //lastDefender = NULL; + } + vector relevantUnits = gatherRelevantUnits(out, lastAttackEvent, report); + InteractionData data = getAttacker(out, lastAttackEvent, lastAttacker, attack ? NULL : report, relevantUnits); + if ( data.attacker < 0 ) + continue; +//out.print("%s,%d\n",__FILE__,__LINE__); + //if ( !attack && lastAttacker && data.attacker == lastAttacker->id && lastDefender && data.defender == lastDefender->id ) + // continue; //lazy way of preventing duplicates + if ( attack && a+1 < reports.size() && reports[a+1]->type == df::announcement_type::INTERACTION_TARGET ) { +//out.print("%s,%d\n",__FILE__,__LINE__); + vector relevants = gatherRelevantUnits(out, lastAttackEvent, reports[a+1]); + InteractionData data2 = getAttacker(out, lastAttackEvent, lastAttacker, reports[a+1], relevants); + if ( data.attacker == data2.attacker && (data.defender == -1 || data.defender == data2.defender) ) { +//out.print("%s,%d\n",__FILE__,__LINE__); + data = data2; + ++a; + } + } + { +#define HISTORY_ITEM 1 +#if HISTORY_ITEM + unordered_set& b = history[data.attacker]; + if ( b.find(data.defender) != b.end() ) + continue; + history[data.attacker].insert(data.defender); + //b.insert(data.defender); +#else + unordered_set& b = history[data.attackReport]; + if ( b.find(data.defendReport) != b.end() ) + continue; + history[data.attackReport].insert(data.defendReport); + //b.insert(data.defendReport); +#endif + } +//out.print("%s,%d\n",__FILE__,__LINE__); + lastAttacker = df::unit::find(data.attacker); + //lastDefender = df::unit::find(data.defender); + //fire event + handler.eventHandler(out, (void*)&data); + //TODO: deduce attacker from latest defend event first + } + } + } +} + + + +static void updateReportToRelevantUnits() { + if (!df::global::world) + return; + if ( df::global::world->frame_counter <= reportToRelevantUnitsTime ) + return; + reportToRelevantUnitsTime = df::global::world->frame_counter; + + for ( df::unit *unit : df::global::world->units.all ) { + for ( int16_t b = df::enum_traits::first_item_value; b <= df::enum_traits::last_item_value; ++b ) { + if ( b == df::unit_report_type::Sparring ) + continue; + for ( size_t c = 0; c < unit->reports.log[b].size(); c++ ) { + int32_t report = unit->reports.log[b][c]; + if ( std::find(reportToRelevantUnits[report].begin(), reportToRelevantUnits[report].end(), unit->id) != reportToRelevantUnits[report].end() ) + continue; + reportToRelevantUnits[unit->reports.log[b][c]].push_back(unit->id); + } + } + } +} + +static df::unit_wound* getWound(df::unit* attacker, df::unit* defender) { + for ( size_t a = 0; a < defender->body.wounds.size(); ++a ) { + df::unit_wound* wound = defender->body.wounds[a]; + if ( wound->age <= 1 && wound->attacker_unit_id == attacker->id ) { + return wound; + } + } + return NULL; +} + +static vector gatherRelevantUnits(color_ostream& out, df::report* r1, df::report* r2) { + vector reports; + if ( r1 == r2 ) r2 = NULL; + if ( r1 ) reports.push_back(r1); + if ( r2 ) reports.push_back(r2); + vector result; + unordered_set ids; +//out.print("%s,%d\n",__FILE__,__LINE__); + for ( size_t a = 0; a < reports.size(); ++a ) { +//out.print("%s,%d\n",__FILE__,__LINE__); + vector& units = reportToRelevantUnits[reports[a]->id]; + if ( units.size() > 2 ) { + if ( Once::doOnce("EventManager interaction too many relevant units") ) { + out.print("%s,%d: too many relevant units. On report\n \'%s\'\n", __FILE__, __LINE__, reports[a]->text.c_str()); + } + } + for ( size_t b = 0; b < units.size(); ++b ) + if ( ids.find(units[b]) == ids.end() ) { + ids.insert(units[b]); + result.push_back(df::unit::find(units[b])); + } + } +//out.print("%s,%d\n",__FILE__,__LINE__); + return result; +} + static std::string getVerb(df::unit* unit, std::string reportStr) { std::string result(reportStr); std::string name = unit->name.first_name + " "; @@ -1588,109 +1702,3 @@ static InteractionData getAttacker(color_ostream& out, df::report* attackEvent, return result; } -static vector gatherRelevantUnits(color_ostream& out, df::report* r1, df::report* r2) { - vector reports; - if ( r1 == r2 ) r2 = NULL; - if ( r1 ) reports.push_back(r1); - if ( r2 ) reports.push_back(r2); - vector result; - unordered_set ids; -//out.print("%s,%d\n",__FILE__,__LINE__); - for ( size_t a = 0; a < reports.size(); ++a ) { -//out.print("%s,%d\n",__FILE__,__LINE__); - vector& units = reportToRelevantUnits[reports[a]->id]; - if ( units.size() > 2 ) { - if ( Once::doOnce("EventManager interaction too many relevant units") ) { - out.print("%s,%d: too many relevant units. On report\n \'%s\'\n", __FILE__, __LINE__, reports[a]->text.c_str()); - } - } - for ( size_t b = 0; b < units.size(); ++b ) - if ( ids.find(units[b]) == ids.end() ) { - ids.insert(units[b]); - result.push_back(df::unit::find(units[b])); - } - } -//out.print("%s,%d\n",__FILE__,__LINE__); - return result; -} - -static void manageInteractionEvent(color_ostream& out) { - if (!df::global::world) - return; - multimap copy(handlers[EventType::INTERACTION].begin(), handlers[EventType::INTERACTION].end()); - std::vector& reports = df::global::world->status.reports; - size_t a = df::report::binsearch_index(reports, lastReportInteraction, false); - while (a < reports.size() && reports[a]->id <= lastReportInteraction) { - ++a; - } - if ( a < reports.size() ) - updateReportToRelevantUnits(); - - df::report* lastAttackEvent = NULL; - df::unit* lastAttacker = NULL; - //df::unit* lastDefender = NULL; - unordered_map > history; - int32_t tick = df::global::world->frame_counter; - for (auto &key_value : copy) { - auto &handler = key_value.second; - auto last_tick = eventLastTick[handler]; - if (tick - last_tick >= handler.freq) { - eventLastTick[handler] = tick; - for ( ; a < reports.size(); ++a ) { - df::report* report = reports[a]; - lastReportInteraction = report->id; - df::announcement_type type = report->type; - if ( type != df::announcement_type::INTERACTION_ACTOR && type != df::announcement_type::INTERACTION_TARGET ) - continue; - if ( report->flags.bits.continuation ) - continue; - bool attack = type == df::announcement_type::INTERACTION_ACTOR; - if ( attack ) { - lastAttackEvent = report; - lastAttacker = NULL; - //lastDefender = NULL; - } - vector relevantUnits = gatherRelevantUnits(out, lastAttackEvent, report); - InteractionData data = getAttacker(out, lastAttackEvent, lastAttacker, attack ? NULL : report, relevantUnits); - if ( data.attacker < 0 ) - continue; -//out.print("%s,%d\n",__FILE__,__LINE__); - //if ( !attack && lastAttacker && data.attacker == lastAttacker->id && lastDefender && data.defender == lastDefender->id ) - // continue; //lazy way of preventing duplicates - if ( attack && a+1 < reports.size() && reports[a+1]->type == df::announcement_type::INTERACTION_TARGET ) { -//out.print("%s,%d\n",__FILE__,__LINE__); - vector relevants = gatherRelevantUnits(out, lastAttackEvent, reports[a+1]); - InteractionData data2 = getAttacker(out, lastAttackEvent, lastAttacker, reports[a+1], relevants); - if ( data.attacker == data2.attacker && (data.defender == -1 || data.defender == data2.defender) ) { -//out.print("%s,%d\n",__FILE__,__LINE__); - data = data2; - ++a; - } - } - { -#define HISTORY_ITEM 1 -#if HISTORY_ITEM - unordered_set& b = history[data.attacker]; - if ( b.find(data.defender) != b.end() ) - continue; - history[data.attacker].insert(data.defender); - //b.insert(data.defender); -#else - unordered_set& b = history[data.attackReport]; - if ( b.find(data.defendReport) != b.end() ) - continue; - history[data.attackReport].insert(data.defendReport); - //b.insert(data.defendReport); -#endif - } -//out.print("%s,%d\n",__FILE__,__LINE__); - lastAttacker = df::unit::find(data.attacker); - //lastDefender = df::unit::find(data.defender); - //fire event - handler.eventHandler(out, (void*)&data); - //TODO: deduce attacker from latest defend event first - } - } - } -} - From 2faa3e8b7a572e12f75c696a7b3578ec8b884958 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sun, 27 Mar 2022 22:11:09 -0700 Subject: [PATCH 45/61] Removes errant \ --- library/modules/EventManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 09263ee69d..8aa747f8bb 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -1050,7 +1050,7 @@ static void manageConstructionEvent(color_ostream& out) { int32_t tick = df::global::world->frame_counter; for (auto &c : df::global::world->constructions) { // anything on the global list, obviously exists.. so we ensure that coord is on the created list and isn't on the destroyed list - createdConstructions.emplace(tick, *c); // hashes based on c->pos (coord)\ + createdConstructions.emplace(tick, *c); // hashes based on c->pos (coord) // handlers which haven't handled this construction yet aren't going to (it would be very tricky to make this work) destroyedConstructions.erase(*c); } From 88eb135d26a5b919e41bf11609bafad8f9d3da04 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sun, 27 Mar 2022 23:08:58 -0700 Subject: [PATCH 46/61] Moves operator== for EventManager types to header/class defs --- library/include/modules/EventManager.h | 29 ++++++++++++++++++++++++-- library/modules/EventManager.cpp | 9 -------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index d7986728f1..3310f35474 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -63,8 +63,11 @@ namespace DFHack { struct SyndromeData { int32_t unitId; int32_t syndromeIndex; - SyndromeData(int32_t unitId_in, int32_t syndromeIndex_in): unitId(unitId_in), syndromeIndex(syndromeIndex_in) { - + SyndromeData(int32_t unitId_in, int32_t syndromeIndex_in) + : unitId(unitId_in), syndromeIndex(syndromeIndex_in) { + } + bool operator==(const SyndromeData &other) const { + return unitId == other.unitId && syndromeIndex == other.syndromeIndex; } }; @@ -86,12 +89,23 @@ namespace DFHack { unitId(id_in), item_old(old_in), item_new(new_in) {} + bool operator==(const InventoryChangeData &other) const { + bool unit = unitId == other.unitId; + bool newItem = (item_new && other.item_new && item_new->itemId == other.item_new->itemId) || + (!item_new && item_new == other.item_new); + bool oldItem = (item_old && other.item_old && item_old->itemId == other.item_old->itemId) || + (!item_old && item_old == other.item_old); + return unit && newItem && oldItem; + } }; struct UnitAttackData { int32_t attacker; int32_t defender; int32_t wound; + bool operator==(const UnitAttackData &other) const { + return attacker == other.attacker && defender == other.defender && wound == other.wound; + } }; struct InteractionData { @@ -171,6 +185,17 @@ namespace std { return r; } }; + template <> + struct hash { + std::size_t operator()(const DFHack::EventManager::UnitAttackData& uad) const { + size_t r = 43; + const size_t m = 65537; + r = m*(r+uad.attacker); + r = m*(r+uad.defender); + r = m*(r+uad.wound); + return r; + } + }; } #endif diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 8aa747f8bb..9308a2b9f4 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -208,15 +208,6 @@ namespace std{ bool operator==(const df::construction &A, const df::construction &B){ return A.pos == B.pos; } - bool operator==(const DFHack::EventManager::SyndromeData &A, const DFHack::EventManager::SyndromeData &B){ - return A.unitId == B.unitId && A.syndromeIndex == B.syndromeIndex; - } - bool operator==(const DFHack::EventManager::InventoryChangeData &A, const DFHack::EventManager::InventoryChangeData &B) { - bool unit = A.unitId == B.unitId; - bool newItem = (A.item_new && B.item_new && A.item_new->itemId == B.item_new->itemId) || (!A.item_new && A.item_new == B.item_new); - bool oldItem = (A.item_old && B.item_old && A.item_old->itemId == B.item_old->itemId) || (!A.item_old && A.item_old == B.item_old); - return unit && newItem && oldItem; - } } template class event_tracker { //todo: use inheritance? stl seems to use variadics, so it's unclear how well that would actually work From 12bfad094f572c53a82b9ddfa395d16e5d1141b7 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sun, 27 Mar 2022 23:09:40 -0700 Subject: [PATCH 47/61] Adds UNIT_ATTACK event to devel/event-testing --- plugins/devel/event-testing.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/plugins/devel/event-testing.cpp b/plugins/devel/event-testing.cpp index e53f5af730..9e9879a95f 100644 --- a/plugins/devel/event-testing.cpp +++ b/plugins/devel/event-testing.cpp @@ -7,6 +7,7 @@ #include #include #include +#include //#include "df/world.h" @@ -24,6 +25,7 @@ void onSyndrome(color_ostream &out, void* syndrome); void onDeath(color_ostream &out, void* unit_id); void onNewActive(color_ostream &out, void* unit_id); void onItem(color_ostream &out, void* inventory_change_data); +void onAttack(color_ostream &out, void* attack_data); command_result skeleton2 (color_ostream &out, std::vector & parameters); @@ -95,10 +97,14 @@ DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { //enable_unit_events(); namespace EM = EventManager; using namespace EM::EventType; - EM::EventHandler e1(onItem, 0); - EM::EventHandler e2(onItem, interval); - EM::registerListener(EventType::INVENTORY_CHANGE, e1, plugin_self); - EM::registerListener(EventType::INVENTORY_CHANGE, e2, plugin_self); +// EM::EventHandler e1(onItem, 0); +// EM::EventHandler e2(onItem, interval); +// EM::registerListener(EventType::INVENTORY_CHANGE, e1, plugin_self); +// EM::registerListener(EventType::INVENTORY_CHANGE, e2, plugin_self); + EM::EventHandler e3(onAttack, 0); + EM::EventHandler e4(onAttack, interval); + EM::registerListener(EventType::UNIT_ATTACK, e3, plugin_self); + EM::registerListener(EventType::UNIT_ATTACK, e4, plugin_self); out.print("plugin enabled!\n"); } else if (!enable && enabled) { EM::unregisterAll(plugin_self); @@ -144,3 +150,14 @@ void onItem(color_ostream &out, void* inventory_change_data) { out.print("onItem: (old id: %d) (new id: %d)\n", old_id, new_id); } +void onAttack(color_ostream &out, void* attack_data){ + auto* data = (EventManager::UnitAttackData*)attack_data; + static std::unordered_set seen; + out.print("onAttack: (attacker: %d) (defender: %d) (wound: %d)\n", data->attacker, data->defender, data->wound); + if(!seen.emplace(*data).second){ + out.print("onAttack: duplicate event"); + } +} + + + From 121a6c32a045b18cfbbdd8561964d1cb370677b1 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Sun, 27 Mar 2022 23:27:58 -0700 Subject: [PATCH 48/61] Updates onAttack --- plugins/devel/event-testing.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/devel/event-testing.cpp b/plugins/devel/event-testing.cpp index 9e9879a95f..40cf6e0feb 100644 --- a/plugins/devel/event-testing.cpp +++ b/plugins/devel/event-testing.cpp @@ -153,9 +153,9 @@ void onItem(color_ostream &out, void* inventory_change_data) { void onAttack(color_ostream &out, void* attack_data){ auto* data = (EventManager::UnitAttackData*)attack_data; static std::unordered_set seen; - out.print("onAttack: (attacker: %d) (defender: %d) (wound: %d)\n", data->attacker, data->defender, data->wound); + out.print(" onAttack: (attacker: %d) (defender: %d) (wound: %d)\n", data->attacker, data->defender, data->wound); if(!seen.emplace(*data).second){ - out.print("onAttack: duplicate event"); + out.print(" onAttack: duplicate event\n"); } } From a6567e52cfbe4cde752f0cc83bffb7eecf8e249e Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 28 Mar 2022 14:00:53 -0700 Subject: [PATCH 49/61] Adds validity check to manageReportsEvent --- library/modules/EventManager.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 9308a2b9f4..dca01a4a96 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -1267,8 +1267,10 @@ static void manageReportEvent(color_ostream& out) { return; int32_t tick = df::global::world->frame_counter; + std::unordered_set valid_reports; // update reports list for(auto &r : df::global::world->status.reports){ + valid_reports.emplace(r->id); newReports.emplace(tick, r->id); } @@ -1282,8 +1284,13 @@ static void manageReportEvent(color_ostream& out) { eventLastTick[handler] = tick; // send all new reports since it last fired auto iter = newReports.upper_bound(last_tick); - for(;iter != newReports.end(); ++iter) { - handler.eventHandler(out, (void*) intptr_t(iter->second)); + for(;iter != newReports.end();) { + if (valid_reports.count(iter->second)) { + handler.eventHandler(out, (void*) intptr_t(iter->second)); + ++iter; + } else { + iter = newReports.erase(iter->second); + } } } } From bee450b23531a85dc841e0ed39e6fd7e004c4abb Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 28 Mar 2022 14:57:02 -0700 Subject: [PATCH 50/61] Updates manageUnitAttackEvent --- library/include/modules/EventManager.h | 6 +- library/modules/EventManager.cpp | 157 +++++++++++++------------ plugins/devel/event-testing.cpp | 9 +- 3 files changed, 92 insertions(+), 80 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 3310f35474..136d0777e4 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -100,11 +100,14 @@ namespace DFHack { }; struct UnitAttackData { + int32_t report_id; int32_t attacker; int32_t defender; int32_t wound; + bool operator==(const UnitAttackData &other) const { - return attacker == other.attacker && defender == other.defender && wound == other.wound; + // fairly sure the report_id is the only thing that matters + return report_id == other.report_id && wound == other.wound; } }; @@ -190,6 +193,7 @@ namespace std { std::size_t operator()(const DFHack::EventManager::UnitAttackData& uad) const { size_t r = 43; const size_t m = 65537; + r = m*(r+uad.report_id); r = m*(r+uad.attacker); r = m*(r+uad.defender); r = m*(r+uad.wound); diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index dca01a4a96..e5205da2dc 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -323,13 +323,14 @@ static event_tracker invasions; //equipment change static unordered_map > inventoryLog; -event_tracker equipmentChanges; +static event_tracker equipmentChanges; //report static event_tracker newReports; //unit attack static int32_t lastReportUnitAttack; +static event_tracker attackEvents; static std::map > reportToRelevantUnits; static int32_t reportToRelevantUnitsTime = -1; @@ -1321,92 +1322,98 @@ static void manageUnitAttackEvent(color_ostream& out) { strikeReports.insert(report->id); } } - - if ( strikeReports.empty() ) { - return; - } updateReportToRelevantUnits(); map > alreadyDone; - multimap copy(handlers[EventType::UNIT_ATTACK].begin(), handlers[EventType::UNIT_ATTACK].end()); - for (auto &key_value : copy) { - auto &handler = key_value.second; - auto last_tick = eventLastTick[handler]; - if (tick - last_tick >= handler.freq) { - eventLastTick[handler] = tick; - for (int reportId : strikeReports) { - df::report* report = df::report::find(reportId); - if ( !report ) - continue; //TODO: error - std::string reportStr = report->text; - for ( int32_t b = reportId+1; ; ++b ) { - df::report* report2 = df::report::find(b); - if ( !report2 ) - break; - if ( report2->type != df::announcement_type::COMBAT_STRIKE_DETAILS ) - break; - if ( !report2->flags.bits.continuation ) - break; - reportStr = reportStr + report2->text; - } + for (int reportId : strikeReports) { + df::report* report = df::report::find(reportId); + if ( !report ) + continue; //TODO: error + std::string reportStr = report->text; + for ( int32_t b = reportId+1; ; ++b ) { + df::report* report2 = df::report::find(b); + if ( !report2 ) + break; + if ( report2->type != df::announcement_type::COMBAT_STRIKE_DETAILS ) + break; + if ( !report2->flags.bits.continuation ) + break; + reportStr += report2->text; + } - std::vector& relevantUnits = reportToRelevantUnits[report->id]; - if ( relevantUnits.size() != 2 ) { - continue; - } + std::vector& relevantUnits = reportToRelevantUnits[report->id]; + if ( relevantUnits.size() != 2 ) { + continue; + } - df::unit* unit1 = df::unit::find(relevantUnits[0]); - df::unit* unit2 = df::unit::find(relevantUnits[1]); + df::unit* unit1 = df::unit::find(relevantUnits[0]); + df::unit* unit2 = df::unit::find(relevantUnits[1]); - df::unit_wound* wound1 = getWound(unit1,unit2); - df::unit_wound* wound2 = getWound(unit2,unit1); + df::unit_wound* wound1 = getWound(unit1,unit2); + df::unit_wound* wound2 = getWound(unit2,unit1); - if ( wound1 && !alreadyDone[unit1->id][unit2->id] ) { - UnitAttackData data; - data.attacker = unit1->id; - data.defender = unit2->id; - data.wound = wound1->id; + if ( wound1 && !alreadyDone[unit1->id][unit2->id] ) { + UnitAttackData data{}; + data.report_id = report->id; + data.attacker = unit1->id; + data.defender = unit2->id; + data.wound = wound1->id; - alreadyDone[data.attacker][data.defender] = 1; - handler.eventHandler(out, (void*)&data); - } + alreadyDone[data.attacker][data.defender] = 1; + attackEvents.emplace(tick, data); + } - if ( wound2 && !alreadyDone[unit1->id][unit2->id] ) { - UnitAttackData data; - data.attacker = unit2->id; - data.defender = unit1->id; - data.wound = wound2->id; + if ( wound2 && !alreadyDone[unit1->id][unit2->id] ) { + UnitAttackData data{}; + data.report_id = report->id; + data.attacker = unit2->id; + data.defender = unit1->id; + data.wound = wound2->id; - alreadyDone[data.attacker][data.defender] = 1; - handler.eventHandler(out, (void*)&data); - } + alreadyDone[data.attacker][data.defender] = 1; + attackEvents.emplace(tick, data); + } - if ( Units::isKilled(unit1) ) { - UnitAttackData data; - data.attacker = unit2->id; - data.defender = unit1->id; - data.wound = -1; - alreadyDone[data.attacker][data.defender] = 1; - handler.eventHandler(out, (void*)&data); - } + if ( Units::isKilled(unit1) ) { + UnitAttackData data{}; + data.report_id = report->id; + data.attacker = unit2->id; + data.defender = unit1->id; + data.wound = -1; - if ( Units::isKilled(unit2) ) { - UnitAttackData data; - data.attacker = unit1->id; - data.defender = unit2->id; - data.wound = -1; - alreadyDone[data.attacker][data.defender] = 1; - handler.eventHandler(out, (void*)&data); - } + alreadyDone[data.attacker][data.defender] = 1; + attackEvents.emplace(tick, data); + } - if ( !wound1 && !wound2 ) { - //if ( unit1->flags1.bits.inactive || unit2->flags1.bits.inactive ) - // continue; - if ( reportStr.find("severed part") ) - continue; - if ( Once::doOnce("EventManager neither wound") ) { - out.print("%s, %d: neither wound: %s\n", __FILE__, __LINE__, reportStr.c_str()); - } - } + if ( Units::isKilled(unit2) ) { + UnitAttackData data{}; + data.report_id = report->id; + data.attacker = unit1->id; + data.defender = unit2->id; + data.wound = -1; + + alreadyDone[data.attacker][data.defender] = 1; + attackEvents.emplace(tick, data); + } + + if ( !wound1 && !wound2 ) { + //if ( unit1->flags1.bits.inactive || unit2->flags1.bits.inactive ) + // continue; + if ( reportStr.find("severed part") ) + continue; + if ( Once::doOnce("EventManager neither wound") ) { + out.print("%s, %d: neither wound: %s\n", __FILE__, __LINE__, reportStr.c_str()); + } + } + } + + multimap copy(handlers[EventType::UNIT_ATTACK].begin(), handlers[EventType::UNIT_ATTACK].end()); + for (auto &key_value : copy) { + auto &handler = key_value.second; + auto last_tick = eventLastTick[handler]; + if (tick - last_tick >= handler.freq) { + eventLastTick[handler] = tick; + for(auto iter = attackEvents.upper_bound(last_tick); iter != attackEvents.end(); ++iter){ + handler.eventHandler(out, &iter->second); } } } diff --git a/plugins/devel/event-testing.cpp b/plugins/devel/event-testing.cpp index 40cf6e0feb..b1a6ade482 100644 --- a/plugins/devel/event-testing.cpp +++ b/plugins/devel/event-testing.cpp @@ -150,11 +150,12 @@ void onItem(color_ostream &out, void* inventory_change_data) { out.print("onItem: (old id: %d) (new id: %d)\n", old_id, new_id); } -void onAttack(color_ostream &out, void* attack_data){ - auto* data = (EventManager::UnitAttackData*)attack_data; +void onAttack(color_ostream &out, void* attack_data) { + auto* data = (EventManager::UnitAttackData*) attack_data; static std::unordered_set seen; - out.print(" onAttack: (attacker: %d) (defender: %d) (wound: %d)\n", data->attacker, data->defender, data->wound); - if(!seen.emplace(*data).second){ + out.print(" onAttack: (report id: %d) (attacker: %d) (defender: %d) (wound: %d)\n", + data->report_id, data->attacker, data->defender, data->wound); + if (!seen.emplace(*data).second) { out.print(" onAttack: duplicate event\n"); } } From e4518b2e9d4d5ecbe22f72be62c8ca06e57622fc Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 28 Mar 2022 15:08:03 -0700 Subject: [PATCH 51/61] Updates manageInteractionEvent --- library/include/modules/EventManager.h | 18 +++++ library/modules/EventManager.cpp | 108 +++++++++++++------------ 2 files changed, 74 insertions(+), 52 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 136d0777e4..5c4d202d0d 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -118,6 +118,12 @@ namespace DFHack { int32_t defender; int32_t attackReport; int32_t defendReport; + bool operator==(const InteractionData &other) const { + bool units = attacker == other.attacker && defender == other.defender; + bool reports = attackReport == other.attackReport && defendReport == other.defendReport; + // probably doesn't need the verbs I bet + return units && reports && attackVerb == other.attackVerb && defendVerb == other.defendVerb; + } }; DFHACK_EXPORT void registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin); @@ -200,6 +206,18 @@ namespace std { return r; } }; + template <> + struct hash { + std::size_t operator()(const DFHack::EventManager::InteractionData& interactionData) const { + size_t r = 43; + const size_t m = 65537; + r = m*(r+interactionData.attackReport); + r = m*(r+interactionData.defendReport); + r = m*(r+interactionData.attacker); + r = m*(r+interactionData.defender); + return r; + } + }; } #endif diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index e5205da2dc..e95bd5e61e 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -336,6 +336,7 @@ static int32_t reportToRelevantUnitsTime = -1; //interaction static int32_t lastReportInteraction; +static event_tracker interactionEvents; void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event event) { static bool doOnce = false; @@ -1424,78 +1425,81 @@ static InteractionData getAttacker(color_ostream& out, df::report* attackEvent, static void manageInteractionEvent(color_ostream& out) { if (!df::global::world) return; - multimap copy(handlers[EventType::INTERACTION].begin(), handlers[EventType::INTERACTION].end()); + + int32_t tick = df::global::world->frame_counter; std::vector& reports = df::global::world->status.reports; size_t a = df::report::binsearch_index(reports, lastReportInteraction, false); - while (a < reports.size() && reports[a]->id <= lastReportInteraction) { - ++a; - } - if ( a < reports.size() ) + if ( a < reports.size() ) { updateReportToRelevantUnits(); + } df::report* lastAttackEvent = NULL; df::unit* lastAttacker = NULL; //df::unit* lastDefender = NULL; unordered_map > history; - int32_t tick = df::global::world->frame_counter; - for (auto &key_value : copy) { - auto &handler = key_value.second; - auto last_tick = eventLastTick[handler]; - if (tick - last_tick >= handler.freq) { - eventLastTick[handler] = tick; - for ( ; a < reports.size(); ++a ) { - df::report* report = reports[a]; - lastReportInteraction = report->id; - df::announcement_type type = report->type; - if ( type != df::announcement_type::INTERACTION_ACTOR && type != df::announcement_type::INTERACTION_TARGET ) - continue; - if ( report->flags.bits.continuation ) - continue; - bool attack = type == df::announcement_type::INTERACTION_ACTOR; - if ( attack ) { - lastAttackEvent = report; - lastAttacker = NULL; - //lastDefender = NULL; - } - vector relevantUnits = gatherRelevantUnits(out, lastAttackEvent, report); - InteractionData data = getAttacker(out, lastAttackEvent, lastAttacker, attack ? NULL : report, relevantUnits); - if ( data.attacker < 0 ) - continue; + for ( ; a < reports.size(); ++a ) { + df::report* report = reports[a]; + lastReportInteraction = report->id; + df::announcement_type type = report->type; + if ( type != df::announcement_type::INTERACTION_ACTOR && type != df::announcement_type::INTERACTION_TARGET ) + continue; + if ( report->flags.bits.continuation ) + continue; + bool attack = type == df::announcement_type::INTERACTION_ACTOR; + if ( attack ) { + lastAttackEvent = report; + lastAttacker = NULL; + //lastDefender = NULL; + } + vector relevantUnits = gatherRelevantUnits(out, lastAttackEvent, report); + InteractionData data = getAttacker(out, lastAttackEvent, lastAttacker, attack ? NULL : report, relevantUnits); + if ( data.attacker < 0 ) + continue; //out.print("%s,%d\n",__FILE__,__LINE__); - //if ( !attack && lastAttacker && data.attacker == lastAttacker->id && lastDefender && data.defender == lastDefender->id ) - // continue; //lazy way of preventing duplicates - if ( attack && a+1 < reports.size() && reports[a+1]->type == df::announcement_type::INTERACTION_TARGET ) { + //if ( !attack && lastAttacker && data.attacker == lastAttacker->id && lastDefender && data.defender == lastDefender->id ) + // continue; //lazy way of preventing duplicates + if ( attack && a+1 < reports.size() && reports[a+1]->type == df::announcement_type::INTERACTION_TARGET ) { //out.print("%s,%d\n",__FILE__,__LINE__); - vector relevants = gatherRelevantUnits(out, lastAttackEvent, reports[a+1]); - InteractionData data2 = getAttacker(out, lastAttackEvent, lastAttacker, reports[a+1], relevants); - if ( data.attacker == data2.attacker && (data.defender == -1 || data.defender == data2.defender) ) { + vector relevants = gatherRelevantUnits(out, lastAttackEvent, reports[a+1]); + InteractionData data2 = getAttacker(out, lastAttackEvent, lastAttacker, reports[a+1], relevants); + if ( data.attacker == data2.attacker && (data.defender == -1 || data.defender == data2.defender) ) { //out.print("%s,%d\n",__FILE__,__LINE__); - data = data2; - ++a; - } - } - { + data = data2; + ++a; + } + } + { #define HISTORY_ITEM 1 #if HISTORY_ITEM - unordered_set& b = history[data.attacker]; - if ( b.find(data.defender) != b.end() ) - continue; - history[data.attacker].insert(data.defender); - //b.insert(data.defender); + unordered_set& b = history[data.attacker]; + if ( b.find(data.defender) != b.end() ) + continue; + history[data.attacker].insert(data.defender); + //b.insert(data.defender); #else - unordered_set& b = history[data.attackReport]; + unordered_set& b = history[data.attackReport]; if ( b.find(data.defendReport) != b.end() ) continue; history[data.attackReport].insert(data.defendReport); //b.insert(data.defendReport); #endif - } + } //out.print("%s,%d\n",__FILE__,__LINE__); - lastAttacker = df::unit::find(data.attacker); - //lastDefender = df::unit::find(data.defender); - //fire event - handler.eventHandler(out, (void*)&data); - //TODO: deduce attacker from latest defend event first + lastAttacker = df::unit::find(data.attacker); + //lastDefender = df::unit::find(data.defender); + //fire event + interactionEvents.emplace(tick, data); + //TODO: deduce attacker from latest defend event first + } + + multimap copy(handlers[EventType::INTERACTION].begin(), handlers[EventType::INTERACTION].end()); + for (auto &key_value : copy) { + auto &handler = key_value.second; + auto last_tick = eventLastTick[handler]; + if (tick - last_tick >= handler.freq) { + eventLastTick[handler] = tick; + for(auto iter = interactionEvents.upper_bound(last_tick); iter != interactionEvents.end(); ++iter){ + handler.eventHandler(out, &iter->second); } } } From a8ed873ba4647d6147bde939209458aaae9e6385 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 28 Mar 2022 17:28:01 -0700 Subject: [PATCH 52/61] Revises InteractionData::operator== --- library/include/modules/EventManager.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 5c4d202d0d..92c1b51e44 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -119,10 +119,10 @@ namespace DFHack { int32_t attackReport; int32_t defendReport; bool operator==(const InteractionData &other) const { - bool units = attacker == other.attacker && defender == other.defender; bool reports = attackReport == other.attackReport && defendReport == other.defendReport; - // probably doesn't need the verbs I bet - return units && reports && attackVerb == other.attackVerb && defendVerb == other.defendVerb; + // based on code in the manager it doesn't need reports or verbs checked. + // since the units are deduced (it seems) from the reports... this + return reports; } }; From b8f77a3f6f57e055fff20dcc12adbc895ba452ca Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Tue, 29 Mar 2022 18:59:09 -0700 Subject: [PATCH 53/61] Adds backlog option for new event registrations --- library/include/modules/EventManager.h | 2 +- library/modules/EventManager.cpp | 17 ++++------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 080b5de29d..49e70dbf89 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -129,7 +129,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, bool backlog = 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); diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index e95bd5e61e..b88729c580 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -76,15 +76,12 @@ void enqueueTickEvent(EventHandler &handler){ tickQueue.insert(pair(handler.when, handler)); } -void DFHack::EventManager::registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin) { +void DFHack::EventManager::registerListener(EventType::EventType e, EventHandler handler, Plugin* plugin, bool backlog) { if(e == EventType::TICK){ enqueueTickEvent(handler); } - int32_t tick = 0; - if (df::global::world) { - tick = df::global::world->frame_counter; - } - eventLastTick[handler] = tick - 1; + int32_t tick = backlog ? -2 : (df::global::world ? df::global::world->frame_counter : -1); + eventLastTick[handler] = tick; // received ranges.. // backlog: -1,n // !world: 0,n // world: tick,n handlers[e].insert(pair(plugin, handler)); } @@ -100,14 +97,8 @@ int32_t DFHack::EventManager::registerTick(EventHandler handler, int32_t when, P } handler.when = when; tickQueue.insert(pair(handler.when, handler)); - int32_t tick = 0; - if (df::global::world) { - tick = df::global::world->frame_counter; - } - eventLastTick[handler] = tick - 1; - //this commented line ensures "Registered Ticks" are not added back to the queue after execution + // we don't track this handler, this allows registerTick to retain the old behaviour of needing to re-register the tick event //handlers[EventType::TICK].insert(pair(plugin,handler)); - // since the event isn't added to the handlers, we don't need to unregister these events return when; } From 7ed0dc6bb42543419bfba566a6dda4586b1f097c Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 6 Apr 2022 17:33:03 -0700 Subject: [PATCH 54/61] Reorders created/destroyed events to send destroyed first --- library/include/modules/EventManager.h | 4 ++-- library/modules/EventManager.cpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 49e70dbf89..6a7e436d2e 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -30,11 +30,11 @@ namespace DFHack { UNIT_DEATH, ITEM_CREATED, BUILDING, // todo: deprecate this event - BUILDING_CREATED, BUILDING_DESTROYED, + BUILDING_CREATED, CONSTRUCTION, // todo: deprecate this event - CONSTRUCTION_ADDED, CONSTRUCTION_REMOVED, + CONSTRUCTION_ADDED, SYNDROME, INVASION, INVENTORY_CHANGE, diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index b88729c580..050e020c0b 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -944,14 +944,14 @@ static void manageBuildingEvent(color_ostream& out) { // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; - // send the handler all the new buildings since it last fired - for (auto jter = createdBuildings.upper_bound(last_tick); jter != createdBuildings.end(); ++jter) { - handler.eventHandler(out, (void*) intptr_t(jter->second)); - } // send the handler all the destroyed buildings since it last fired for (auto jter = destroyedBuildings.upper_bound(last_tick); jter != destroyedBuildings.end(); ++jter) { handler.eventHandler(out, (void*) intptr_t(jter->second)); } + // send the handler all the new buildings since it last fired + for (auto jter = createdBuildings.upper_bound(last_tick); jter != createdBuildings.end(); ++jter) { + handler.eventHandler(out, (void*) intptr_t(jter->second)); + } } } } @@ -1057,14 +1057,14 @@ static void manageConstructionEvent(color_ostream& out) { // enforce handler's callback frequency if (tick - last_tick >= handler.freq) { eventLastTick[handler] = tick; - // send the handler all the added constructions since it last fired - for(auto iter = createdConstructions.upper_bound(last_tick); iter != createdConstructions.end(); ++iter) { - handler.eventHandler(out, &iter->second); - } // send the handler all the removed constructions since it last fired for(auto iter = destroyedConstructions.upper_bound(last_tick); iter != destroyedConstructions.end(); ++iter) { handler.eventHandler(out, &iter->second); } + // send the handler all the added constructions since it last fired + for(auto iter = createdConstructions.upper_bound(last_tick); iter != createdConstructions.end(); ++iter) { + handler.eventHandler(out, &iter->second); + } } } } From 4a89b80113c5595786ec4f448fac21a3bbc9f117 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 6 Apr 2022 23:29:17 -0700 Subject: [PATCH 55/61] Reorders events, and dynamically builds eventManager array --- library/include/modules/EventManager.h | 4 +- library/modules/EventManager.cpp | 125 +++++++++++++++++++------ 2 files changed, 96 insertions(+), 33 deletions(-) diff --git a/library/include/modules/EventManager.h b/library/include/modules/EventManager.h index 6a7e436d2e..775629fba4 100644 --- a/library/include/modules/EventManager.h +++ b/library/include/modules/EventManager.h @@ -29,12 +29,12 @@ namespace DFHack { UNIT_NEW_ACTIVE, UNIT_DEATH, ITEM_CREATED, - BUILDING, // todo: deprecate this event BUILDING_DESTROYED, BUILDING_CREATED, - CONSTRUCTION, // todo: deprecate this event + BUILDING, // todo: deprecate this event CONSTRUCTION_REMOVED, CONSTRUCTION_ADDED, + CONSTRUCTION, // todo: deprecate this event SYNDROME, INVASION, INVENTORY_CHANGE, diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 050e020c0b..2d0ee91db7 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -154,7 +154,7 @@ static void manageTickEvent(color_ostream& out); static void manageJobInitiatedEvent(color_ostream& out); static void manageJobStartedEvent(color_ostream& out); static void manageJobCompletedEvent(color_ostream& out); -static void manageNewUnitActiveEvent(color_ostream& out); +static void manageUnitNewActiveEvent(color_ostream& out); static void manageUnitDeathEvent(color_ostream& out); static void manageItemCreationEvent(color_ostream& out); static void manageBuildingEvent(color_ostream& out); @@ -173,28 +173,87 @@ static void manageInteractionEvent(color_ostream& out); typedef void (*eventManager_t)(color_ostream&); -static const eventManager_t eventManager[] = { - manageTickEvent, - manageJobInitiatedEvent, - manageJobStartedEvent, - manageJobCompletedEvent, - manageNewUnitActiveEvent, - manageUnitDeathEvent, - manageItemCreationEvent, - manageBuildingEvent, - manageBuildingCreatedEvent, - manageBuildingDestroyedEvent, - manageConstructionEvent, - manageConstructionAddedEvent, - manageConstructionRemovedEvent, - manageSyndromeEvent, - manageInvasionEvent, - manageInventoryChangeEvent, - manageReportEvent, - manageUnitAttackEvent, - manageUnloadEvent, - manageInteractionEvent, -}; +// integrate new events into this function, and no longer worry about syncing the enum list with the `eventManager` array +eventManager_t getManager(EventType::EventType t) { + switch (t) { + case EventType::TICK: + return manageTickEvent; + case EventType::JOB_INITIATED: + return manageJobInitiatedEvent; + case EventType::JOB_STARTED: + return manageJobStartedEvent; + case EventType::JOB_COMPLETED: + return manageJobCompletedEvent; + case EventType::UNIT_NEW_ACTIVE: + return manageUnitNewActiveEvent; + case EventType::UNIT_DEATH: + return manageUnitDeathEvent; + case EventType::ITEM_CREATED: + return manageItemCreationEvent; + case EventType::BUILDING_DESTROYED: + return manageBuildingDestroyedEvent; + case EventType::BUILDING_CREATED: + return manageBuildingCreatedEvent; + case EventType::BUILDING: + return manageBuildingEvent; + case EventType::CONSTRUCTION_REMOVED: + return manageConstructionRemovedEvent; + case EventType::CONSTRUCTION_ADDED: + return manageConstructionAddedEvent; + case EventType::CONSTRUCTION: + return manageConstructionEvent; + case EventType::SYNDROME: + return manageSyndromeEvent; + case EventType::INVASION: + return manageInvasionEvent; + case EventType::INVENTORY_CHANGE: + return manageInventoryChangeEvent; + case EventType::REPORT: + return manageReportEvent; + case EventType::UNIT_ATTACK: + return manageUnitAttackEvent; + case EventType::UNLOAD: + return manageUnloadEvent; + case EventType::INTERACTION: + return manageInteractionEvent; + case EventType::EVENT_MAX: + return nullptr; + } +} + +std::array compileManagerArray() { + std::array managers{}; + auto t = (EventType::EventType) 0; + while (t < EventType::EVENT_MAX) { + managers[t] = getManager(t); + t = (EventType::EventType) int(t + 1); + } + return managers; +} + +// declaration moved to inside manageEvents +//static const eventManager_t eventManager[] = { +// manageTickEvent, +// manageJobInitiatedEvent, +// manageJobStartedEvent, +// manageJobCompletedEvent, +// manageUnitNewActiveEvent, +// manageUnitDeathEvent, +// manageItemCreationEvent, +// manageBuildingEvent, +// manageBuildingCreatedEvent, +// manageBuildingDestroyedEvent, +// manageConstructionEvent, +// manageConstructionAddedEvent, +// manageConstructionRemovedEvent, +// manageSyndromeEvent, +// manageInvasionEvent, +// manageInventoryChangeEvent, +// manageReportEvent, +// manageUnitAttackEvent, +// manageUnloadEvent, +// manageInteractionEvent, +//}; namespace std{ bool operator==(const df::construction &A, const df::construction &B){ return A.pos == B.pos; @@ -478,6 +537,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event } void DFHack::EventManager::manageEvents(color_ostream& out) { + static const std::array eventManager = compileManagerArray(); if ( !gameLoaded ) { return; } @@ -488,21 +548,24 @@ void DFHack::EventManager::manageEvents(color_ostream& out) { int32_t tick = df::global::world->frame_counter; + // iterate the event types for (size_t type = 0; type < EventType::EVENT_MAX; type++ ) { if (handlers[type].empty()) continue; - bool call_events = false; - for (auto &iter : handlers[type]) { - EventHandler handler = iter.second; + // events might be missed if we only do the processing when a listener wants to receive the events + eventManager[type](out); + /*bool call_events = false; + // iterate event type's handlers + for (auto &key_value : handlers[type]) { + const EventHandler &handler = key_value.second; int32_t last_tick = eventLastTick[handler]; + // see if there is at least one handler ready to fire if (tick - last_tick >= handler.freq){ - //todo: integrate into every sub-function - //eventLastTick[handler] = tick; call_events = true; + break; } } - if(call_events) - eventManager[type](out); + if(call_events)*/ } } @@ -784,7 +847,7 @@ static void manageJobCompletedEvent(color_ostream& out) { #endif } -static void manageNewUnitActiveEvent(color_ostream& out) { +static void manageUnitNewActiveEvent(color_ostream& out) { if (!df::global::world) return; int32_t tick = df::global::world->frame_counter; From d0a79049a26bd00db71da62395f0cf8033fba694 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Wed, 6 Apr 2022 23:34:34 -0700 Subject: [PATCH 56/61] Fixes build errors --- library/modules/EventManager.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 2d0ee91db7..17995183b4 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -216,7 +216,7 @@ eventManager_t getManager(EventType::EventType t) { return manageUnloadEvent; case EventType::INTERACTION: return manageInteractionEvent; - case EventType::EVENT_MAX: + default: return nullptr; } } @@ -546,8 +546,6 @@ void DFHack::EventManager::manageEvents(color_ostream& out) { CoreSuspender suspender; - int32_t tick = df::global::world->frame_counter; - // iterate the event types for (size_t type = 0; type < EventType::EVENT_MAX; type++ ) { if (handlers[type].empty()) From 51e89802d95d603c3e4fb526c969c4cbe9f81dd8 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Thu, 7 Apr 2022 00:03:12 -0700 Subject: [PATCH 57/61] Revises getManager so that missing Events cause a compiler error --- library/modules/EventManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 17995183b4..c30112dad2 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -216,9 +216,10 @@ eventManager_t getManager(EventType::EventType t) { return manageUnloadEvent; case EventType::INTERACTION: return manageInteractionEvent; - default: + case EventType::EVENT_MAX: return nullptr; } + return nullptr; } std::array compileManagerArray() { From 8b30cf426d3c65b192aba7ae0b69033eb6bdaebf Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Thu, 7 Apr 2022 00:22:04 -0700 Subject: [PATCH 58/61] Revises eventful.cpp to generate correct order of handlers in array --- plugins/eventful.cpp | 80 +++++++++++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/plugins/eventful.cpp b/plugins/eventful.cpp index 094b251987..625b20d94b 100644 --- a/plugins/eventful.cpp +++ b/plugins/eventful.cpp @@ -223,26 +223,66 @@ static void ev_mng_interaction(color_ostream& out, void* ptr) { std::vector enabledEventManagerEvents(EventManager::EventType::EVENT_MAX,-1); typedef void (*handler_t) (color_ostream&,void*); -// NOTICE: keep this list synchronized with the EventManager::EventType enum or -// else the wrong event handlers will get called. -static const handler_t eventHandlers[] = { - NULL, - ev_mng_jobInitiated, - ev_mng_jobStarted, - ev_mng_jobCompleted, - ev_mng_unitNewActive, - ev_mng_unitDeath, - ev_mng_itemCreate, - ev_mng_building, - ev_mng_construction, - ev_mng_syndrome, - ev_mng_invasion, - ev_mng_inventory, - ev_mng_report, - ev_mng_unitAttack, - ev_mng_unload, - ev_mng_interaction, -}; +// integrate new events into this function, and no longer worry about syncing with the enum list +handler_t getManager(EventManager::EventType::EventType t) { + switch (t) { + case EventManager::EventType::TICK: + return nullptr; + case EventManager::EventType::JOB_INITIATED: + return ev_mng_jobInitiated; + case EventManager::EventType::JOB_STARTED: + return ev_mng_jobStarted; + case EventManager::EventType::JOB_COMPLETED: + return ev_mng_jobCompleted; + case EventManager::EventType::UNIT_NEW_ACTIVE: + return ev_mng_unitNewActive; + case EventManager::EventType::UNIT_DEATH: + return ev_mng_unitDeath; + case EventManager::EventType::ITEM_CREATED: + return ev_mng_itemCreate; + case EventManager::EventType::BUILDING_DESTROYED: + return nullptr; + case EventManager::EventType::BUILDING_CREATED: + return nullptr; + case EventManager::EventType::BUILDING: + return ev_mng_building; + case EventManager::EventType::CONSTRUCTION_REMOVED: + return nullptr; + case EventManager::EventType::CONSTRUCTION_ADDED: + return nullptr; + case EventManager::EventType::CONSTRUCTION: + return ev_mng_construction; + case EventManager::EventType::SYNDROME: + return ev_mng_syndrome; + case EventManager::EventType::INVASION: + return ev_mng_invasion; + case EventManager::EventType::INVENTORY_CHANGE: + return ev_mng_inventory; + case EventManager::EventType::REPORT: + return ev_mng_report; + case EventManager::EventType::UNIT_ATTACK: + return ev_mng_unitAttack; + case EventManager::EventType::UNLOAD: + return ev_mng_unload; + case EventManager::EventType::INTERACTION: + return ev_mng_interaction; + case EventManager::EventType::EVENT_MAX: + return nullptr; + } + return nullptr; +} + +std::array compileEventHandlerArray() { + std::array managers{}; + auto t = (EventManager::EventType::EventType) 0; + while (t < EventManager::EventType::EVENT_MAX) { + managers[t] = getManager(t); + t = (EventManager::EventType::EventType) int(t + 1); + } + return managers; +} +static const std::array eventHandlers = compileEventHandlerArray(); + static void enableEvent(int evType,int freq) { if (freq < 0) From 4957523176478236a1b16ff6b0a92a49c623cae1 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Thu, 7 Apr 2022 00:31:49 -0700 Subject: [PATCH 59/61] Revises array generation --- plugins/eventful.cpp | 54 ++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/plugins/eventful.cpp b/plugins/eventful.cpp index 625b20d94b..ae54c28bff 100644 --- a/plugins/eventful.cpp +++ b/plugins/eventful.cpp @@ -223,50 +223,53 @@ static void ev_mng_interaction(color_ostream& out, void* ptr) { std::vector enabledEventManagerEvents(EventManager::EventType::EVENT_MAX,-1); typedef void (*handler_t) (color_ostream&,void*); +using namespace EventManager::EventType; // integrate new events into this function, and no longer worry about syncing with the enum list -handler_t getManager(EventManager::EventType::EventType t) { +handler_t getManager(EventType t) { switch (t) { - case EventManager::EventType::TICK: + case TICK: return nullptr; - case EventManager::EventType::JOB_INITIATED: + case JOB_INITIATED: return ev_mng_jobInitiated; - case EventManager::EventType::JOB_STARTED: + case JOB_STARTED: return ev_mng_jobStarted; - case EventManager::EventType::JOB_COMPLETED: + case JOB_COMPLETED: return ev_mng_jobCompleted; - case EventManager::EventType::UNIT_NEW_ACTIVE: + case UNIT_NEW_ACTIVE: return ev_mng_unitNewActive; - case EventManager::EventType::UNIT_DEATH: + case UNIT_DEATH: return ev_mng_unitDeath; - case EventManager::EventType::ITEM_CREATED: + case ITEM_CREATED: return ev_mng_itemCreate; - case EventManager::EventType::BUILDING_DESTROYED: + //todo: implement these new events + case BUILDING_DESTROYED: + case BUILDING_CREATED: return nullptr; - case EventManager::EventType::BUILDING_CREATED: - return nullptr; - case EventManager::EventType::BUILDING: + //todo: deprecate this event + case BUILDING: return ev_mng_building; - case EventManager::EventType::CONSTRUCTION_REMOVED: - return nullptr; - case EventManager::EventType::CONSTRUCTION_ADDED: + //todo: implement these new events + case CONSTRUCTION_REMOVED: + case CONSTRUCTION_ADDED: return nullptr; - case EventManager::EventType::CONSTRUCTION: + //todo: deprecate this event + case CONSTRUCTION: return ev_mng_construction; - case EventManager::EventType::SYNDROME: + case SYNDROME: return ev_mng_syndrome; - case EventManager::EventType::INVASION: + case INVASION: return ev_mng_invasion; - case EventManager::EventType::INVENTORY_CHANGE: + case INVENTORY_CHANGE: return ev_mng_inventory; - case EventManager::EventType::REPORT: + case REPORT: return ev_mng_report; - case EventManager::EventType::UNIT_ATTACK: + case UNIT_ATTACK: return ev_mng_unitAttack; - case EventManager::EventType::UNLOAD: + case UNLOAD: return ev_mng_unload; - case EventManager::EventType::INTERACTION: + case INTERACTION: return ev_mng_interaction; - case EventManager::EventType::EVENT_MAX: + case EVENT_MAX: return nullptr; } return nullptr; @@ -281,7 +284,7 @@ std::array compileEventHandlerArra } return managers; } -static const std::array eventHandlers = compileEventHandlerArray(); +static std::array eventHandlers; static void enableEvent(int evType,int freq) { @@ -523,6 +526,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { + eventHandlers = compileEventHandlerArray(); if (Core::getInstance().isWorldLoaded()) plugin_onstatechange(out, SC_WORLD_LOADED); enable_hooks(true); From c84dc5e2c7823fcfc7572455d3219fcf62f517f0 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Thu, 7 Apr 2022 20:51:46 -0700 Subject: [PATCH 60/61] Adds comments about the omitted default cases --- library/modules/EventManager.cpp | 2 ++ plugins/eventful.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index c30112dad2..e042231582 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -218,6 +218,8 @@ eventManager_t getManager(EventType::EventType t) { return manageInteractionEvent; case EventType::EVENT_MAX: return nullptr; + //default: + //we don't do this... because then the compiler wouldn't error for missing cases in the enum } return nullptr; } diff --git a/plugins/eventful.cpp b/plugins/eventful.cpp index ae54c28bff..585bb479b9 100644 --- a/plugins/eventful.cpp +++ b/plugins/eventful.cpp @@ -271,6 +271,8 @@ handler_t getManager(EventType t) { return ev_mng_interaction; case EVENT_MAX: return nullptr; + //default: + //we don't do this... because then the compiler wouldn't error for missing cases in the enum } return nullptr; } From d25946271729d192296040bd5effb7c939856f58 Mon Sep 17 00:00:00 2001 From: Josh Cooper Date: Mon, 18 Apr 2022 13:00:46 -0700 Subject: [PATCH 61/61] Moves operator== to df namespace instead of std --- library/modules/EventManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index 6bedbf9e28..9711afa13c 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -233,7 +233,7 @@ std::array compileManagerArray() { return managers; } -namespace std{ +namespace df{ bool operator==(const df::construction &A, const df::construction &B){ return A.pos == B.pos; }