Merge pull request #3818 from master-spike/eventmanager_cleanup

Eventmanager cleanup
develop
Myk 2023-09-29 09:38:35 -07:00 committed by GitHub
commit 27c85a9af3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 187 additions and 121 deletions

@ -58,11 +58,14 @@ Template for new versions:
## Fixes ## Fixes
- `autolabor`: now unconditionally re-enables vanilla work details when the fort or the plugin is unloaded - `autolabor`: now unconditionally re-enables vanilla work details when the fort or the plugin is unloaded
- ``dfhack.TranslateName()``: fixed crash on certain invalid names, which affected `warn-starving` - ``dfhack.TranslateName()``: fixed crash on certain invalid names, which affected `warn-starving`
- EventManager: Unit death event no longer misfires on units leaving the map
## Misc Improvements ## Misc Improvements
- `dig`: `digtype` command now has options to choose between designating only visible tiles or hidden tiles, as well as "auto" dig mode. Z-level options adjusted to allow choosing z-levels above, below, or the same as the cursor. - `dig`: `digtype` command now has options to choose between designating only visible tiles or hidden tiles, as well as "auto" dig mode. Z-level options adjusted to allow choosing z-levels above, below, or the same as the cursor.
- `hotkeys`: make the DFHack logo brighten on hover in ascii mode to indicate that it is clickable - `hotkeys`: make the DFHack logo brighten on hover in ascii mode to indicate that it is clickable
- `hotkeys`: use vertical bars instead of "!" symbols for the DFHack logo to make it easier to read - `hotkeys`: use vertical bars instead of "!" symbols for the DFHack logo to make it easier to read
- EventManager: guarded against potential iterator invalidation if one of the event listeners modified the global data structure being iterated over
- EventManager: changed firing order of building created and building destroyed events to improve performance in the building location cache.
## Documentation ## Documentation

@ -43,6 +43,7 @@
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <array> #include <array>
#include <utility>
namespace DFHack { namespace DFHack {
DBG_DECLARE(eventmanager, log, DebugCategory::LINFO); DBG_DECLARE(eventmanager, log, DebugCategory::LINFO);
@ -246,6 +247,15 @@ static int32_t reportToRelevantUnitsTime = -1;
//interaction //interaction
static int32_t lastReportInteraction; static int32_t lastReportInteraction;
struct hash_pair {
template<typename A, typename B>
size_t operator()(const std::pair<A,B>& p) const {
auto h1 = std::hash<A>{}(p.first);
auto h2 = std::hash<B>{}(p.second);
return h1 ^ (h2 << 1);
}
};
void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event event) { void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event event) {
static bool doOnce = false; static bool doOnce = false;
// const string eventNames[] = {"world loaded", "world unloaded", "map loaded", "map unloaded", "viewscreen changed", "core initialized", "begin unload", "paused", "unpaused"}; // const string eventNames[] = {"world loaded", "world unloaded", "map loaded", "map unloaded", "viewscreen changed", "core initialized", "begin unload", "paused", "unpaused"};
@ -276,9 +286,9 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event
gameLoaded = false; gameLoaded = false;
multimap<Plugin*,EventHandler> copy(handlers[EventType::UNLOAD].begin(), handlers[EventType::UNLOAD].end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::UNLOAD].begin(), handlers[EventType::UNLOAD].end());
for (auto &key_value : copy) { for (auto &[_,handle] : copy) {
DEBUG(log,out).print("calling handler for map unloaded state change event\n"); DEBUG(log,out).print("calling handler for map unloaded state change event\n");
key_value.second.eventHandler(out, nullptr); handle.eventHandler(out, nullptr);
} }
} else if ( event == DFHack::SC_MAP_LOADED ) { } else if ( event == DFHack::SC_MAP_LOADED ) {
/* /*
@ -375,8 +385,7 @@ void DFHack::EventManager::manageEvents(color_ostream& out) {
continue; continue;
int32_t eventFrequency = -100; int32_t eventFrequency = -100;
if ( a != EventType::TICK ) if ( a != EventType::TICK )
for (auto &key_value : handlers[a]) { for (auto &[_,handle] : handlers[a]) {
EventHandler &handle = key_value.second;
if (handle.freq < eventFrequency || eventFrequency == -100 ) if (handle.freq < eventFrequency || eventFrequency == -100 )
eventFrequency = handle.freq; eventFrequency = handle.freq;
} }
@ -439,8 +448,7 @@ static void manageJobInitiatedEvent(color_ostream& out) {
continue; continue;
if ( link->item->id <= lastJobId ) if ( link->item->id <= lastJobId )
continue; continue;
for (auto &key_value : copy) { for (auto &[_,handle] : copy) {
EventHandler &handle = key_value.second;
DEBUG(log,out).print("calling handler for job initiated event\n"); DEBUG(log,out).print("calling handler for job initiated event\n");
handle.eventHandler(out, (void*)link->item); handle.eventHandler(out, (void*)link->item);
} }
@ -455,17 +463,24 @@ static void manageJobStartedEvent(color_ostream& out) {
static unordered_set<int32_t> startedJobs; static unordered_set<int32_t> startedJobs;
vector<df::job*> new_started_jobs;
// iterate event handler callbacks // iterate event handler callbacks
multimap<Plugin*, EventHandler> copy(handlers[EventType::JOB_STARTED].begin(), handlers[EventType::JOB_STARTED].end()); multimap<Plugin*, EventHandler> copy(handlers[EventType::JOB_STARTED].begin(), handlers[EventType::JOB_STARTED].end());
for (df::job_list_link* link = df::global::world->jobs.list.next; link != nullptr; link = link->next) {
df::job* job = link->item; for (df::job_list_link* link = &df::global::world->jobs.list; link->next != nullptr; link = link->next) {
df::job* job = link->next->item;
int32_t j_id = job->id;
if (job && Job::getWorker(job) && !startedJobs.count(job->id)) { if (job && Job::getWorker(job) && !startedJobs.count(job->id)) {
startedJobs.emplace(job->id); startedJobs.emplace(job->id);
for (auto &key_value : copy) { for (auto &[_,handle] : copy) {
auto &handler = key_value.second;
// the jobs must have a worker to start // the jobs must have a worker to start
DEBUG(log,out).print("calling handler for job started event\n"); DEBUG(log,out).print("calling handler for job started event\n");
handler.eventHandler(out, job); handle.eventHandler(out, job);
}
}
if (link->next == nullptr || link->next->item->id != j_id) {
if ( Once::doOnce("EventManager jobstarted job removed") ) {
out.print("%s,%d: job %u removed from jobs linked list\n", __FILE__, __LINE__, j_id);
} }
} }
} }
@ -487,11 +502,11 @@ static void manageJobCompletedEvent(color_ostream& out) {
int32_t tick1 = df::global::world->frame_counter; int32_t tick1 = df::global::world->frame_counter;
multimap<Plugin*,EventHandler> copy(handlers[EventType::JOB_COMPLETED].begin(), handlers[EventType::JOB_COMPLETED].end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::JOB_COMPLETED].begin(), handlers[EventType::JOB_COMPLETED].end());
map<int32_t, df::job*> nowJobs; unordered_map<int32_t, df::job*> nowJobs;
for ( df::job_list_link* link = &df::global::world->jobs.list; link != nullptr; link = link->next ) { for ( df::job_list_link* link = &df::global::world->jobs.list; link != nullptr; link = link->next ) {
if ( link->item == nullptr ) if ( link->item == nullptr )
continue; continue;
nowJobs[link->item->id] = link->item; nowJobs.emplace(link->item->id, link->item);
} }
#if 0 #if 0
@ -573,10 +588,9 @@ static void manageJobCompletedEvent(color_ostream& out) {
continue; continue;
//still false positive if cancelled at EXACTLY the right time, but experiments show this doesn't happen //still false positive if cancelled at EXACTLY the right time, but experiments show this doesn't happen
for (auto &key_value : copy) { for (auto &[_,handle] : copy) {
EventHandler &handle = key_value.second;
DEBUG(log,out).print("calling handler for repeated job completed event\n"); DEBUG(log,out).print("calling handler for repeated job completed event\n");
handle.eventHandler(out, (void*)&job0); handle.eventHandler(out, (void*) &job0);
} }
continue; continue;
} }
@ -586,28 +600,27 @@ static void manageJobCompletedEvent(color_ostream& out) {
if ( job0.flags.bits.repeat || job0.completion_timer != 0 ) if ( job0.flags.bits.repeat || job0.completion_timer != 0 )
continue; continue;
for (auto &key_value : copy) { for (auto &[_,handle] : copy) {
EventHandler &handle = key_value.second;
DEBUG(log,out).print("calling handler for job completed event\n"); DEBUG(log,out).print("calling handler for job completed event\n");
handle.eventHandler(out, (void*)&job0); handle.eventHandler(out, (void*) &job0);
} }
} }
//erase old jobs, copy over possibly altered jobs //erase old jobs, copy over possibly altered jobs
for (auto &prevJob : prevJobs) { for (auto &[_,prev_job] : prevJobs) {
Job::deleteJobStruct(prevJob.second, true); Job::deleteJobStruct(prev_job, true);
} }
prevJobs.clear(); prevJobs.clear();
//create new jobs //create new jobs
for (auto &nowJob : nowJobs) { for (auto &[_,now_job] : nowJobs) {
/*map<int32_t, df::job*>::iterator i = prevJobs.find((*j).first); /*map<int32_t, df::job*>::iterator i = prevJobs.find((*j).first);
if ( i != prevJobs.end() ) { if ( i != prevJobs.end() ) {
continue; continue;
}*/ }*/
df::job* newJob = Job::cloneJobStruct(nowJob.second, true); df::job* newJob = Job::cloneJobStruct(now_job, true);
prevJobs[newJob->id] = newJob; prevJobs.emplace(newJob->id, newJob);
} }
} }
@ -617,15 +630,17 @@ static void manageNewUnitActiveEvent(color_ostream& out) {
multimap<Plugin*,EventHandler> copy(handlers[EventType::UNIT_NEW_ACTIVE].begin(), handlers[EventType::UNIT_NEW_ACTIVE].end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::UNIT_NEW_ACTIVE].begin(), handlers[EventType::UNIT_NEW_ACTIVE].end());
// iterate event handler callbacks // iterate event handler callbacks
for (auto &key_value : copy) { vector<int32_t> new_active_unit_ids;
auto &handler = key_value.second; for (df::unit* unit : df::global::world->units.active) {
for (df::unit* unit : df::global::world->units.active) { if (!activeUnits.count(unit->id)) {
int32_t id = unit->id; activeUnits.emplace(unit->id);
if (!activeUnits.count(id)) { new_active_unit_ids.emplace_back(unit->id);
activeUnits.emplace(id); }
DEBUG(log,out).print("calling handler for new unit event\n"); }
handler.eventHandler(out, (void*) intptr_t(id)); // intptr_t() avoids cast from smaller type warning for (int32_t unit_id : new_active_unit_ids) {
} for (auto &[_,handle] : copy) {
DEBUG(log,out).print("calling handler for new unit event\n");
handle.eventHandler(out, (void*) intptr_t(unit_id)); // intptr_t() avoids cast from smaller type warning
} }
} }
} }
@ -635,22 +650,26 @@ static void manageUnitDeathEvent(color_ostream& out) {
if (!df::global::world) if (!df::global::world)
return; return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::UNIT_DEATH].begin(), handlers[EventType::UNIT_DEATH].end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::UNIT_DEATH].begin(), handlers[EventType::UNIT_DEATH].end());
vector<int32_t> dead_unit_ids;
for (auto unit : df::global::world->units.all) { for (auto unit : df::global::world->units.all) {
//if ( unit->counters.death_id == -1 ) { //if ( unit->counters.death_id == -1 ) {
if ( Units::isActive(unit) ) { if ( Units::isActive(unit) ) {
livingUnits.insert(unit->id); livingUnits.insert(unit->id);
continue; continue;
} }
if (!Units::isDead(unit)) continue; // for units that have left the map but aren't dead
//dead: if dead since last check, trigger events //dead: if dead since last check, trigger events
if ( livingUnits.find(unit->id) == livingUnits.end() ) if ( livingUnits.find(unit->id) == livingUnits.end() )
continue; continue;
livingUnits.erase(unit->id);
dead_unit_ids.emplace_back(unit->id);
}
for (auto &key_value : copy) { for (int32_t unit_id : dead_unit_ids) {
EventHandler &handle = key_value.second; for (auto &[_,handle] : copy) {
DEBUG(log,out).print("calling handler for unit death event\n"); DEBUG(log,out).print("calling handler for unit death event\n");
handle.eventHandler(out, (void*)intptr_t(unit->id)); handle.eventHandler(out, (void*)intptr_t(unit_id));
} }
livingUnits.erase(unit->id);
} }
} }
@ -666,6 +685,8 @@ static void manageItemCreationEvent(color_ostream& out) {
multimap<Plugin*,EventHandler> copy(handlers[EventType::ITEM_CREATED].begin(), handlers[EventType::ITEM_CREATED].end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::ITEM_CREATED].begin(), handlers[EventType::ITEM_CREATED].end());
size_t index = df::item::binsearch_index(df::global::world->items.all, nextItem, false); size_t index = df::item::binsearch_index(df::global::world->items.all, nextItem, false);
if ( index != 0 ) index--; if ( index != 0 ) index--;
std::vector<int32_t> created_items;
for ( size_t a = index; a < df::global::world->items.all.size(); a++ ) { for ( size_t a = index; a < df::global::world->items.all.size(); a++ ) {
df::item* item = df::global::world->items.all[a]; df::item* item = df::global::world->items.all[a];
//already processed //already processed
@ -683,12 +704,17 @@ static void manageItemCreationEvent(color_ostream& out) {
//spider webs don't count //spider webs don't count
if ( item->flags.bits.spider_web ) if ( item->flags.bits.spider_web )
continue; continue;
for (auto &key_value : copy) { created_items.push_back(item->id);
EventHandler &handle = key_value.second; }
// handle all created items
for (int32_t item_id : created_items) {
for (auto &[_,handle] : copy) {
DEBUG(log,out).print("calling handler for item created event\n"); DEBUG(log,out).print("calling handler for item created event\n");
handle.eventHandler(out, (void*)intptr_t(item->id)); handle.eventHandler(out, (void*)intptr_t(item_id));
} }
} }
nextItem = *df::global::item_next_id; nextItem = *df::global::item_next_id;
} }
@ -703,6 +729,7 @@ static void manageBuildingEvent(color_ostream& out) {
**/ **/
multimap<Plugin*,EventHandler> copy(handlers[EventType::BUILDING].begin(), handlers[EventType::BUILDING].end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::BUILDING].begin(), handlers[EventType::BUILDING].end());
//first alert people about new buildings //first alert people about new buildings
vector<int32_t> new_buildings;
for ( int32_t a = nextBuilding; a < *df::global::building_next_id; a++ ) { 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); int32_t index = df::building::binsearch_index(df::global::world->buildings.all, a);
if ( index == -1 ) { if ( index == -1 ) {
@ -711,30 +738,34 @@ static void manageBuildingEvent(color_ostream& out) {
continue; continue;
} }
buildings.insert(a); buildings.insert(a);
for (auto &key_value : copy) { new_buildings.emplace_back(a);
EventHandler &handle = key_value.second;
DEBUG(log,out).print("calling handler for created building event\n");
handle.eventHandler(out, (void*)intptr_t(a));
}
} }
nextBuilding = *df::global::building_next_id; nextBuilding = *df::global::building_next_id;
//now alert people about destroyed buildings //now alert people about destroyed buildings
for ( auto a = buildings.begin(); a != buildings.end(); ) { for ( auto it = buildings.begin(); it != buildings.end(); ) {
int32_t id = *a; int32_t id = *it;
int32_t index = df::building::binsearch_index(df::global::world->buildings.all,id); int32_t index = df::building::binsearch_index(df::global::world->buildings.all,id);
if ( index != -1 ) { if ( index != -1 ) {
a++; ++it;
continue; continue;
} }
for (auto &key_value : copy) { for (auto &[_,handle] : copy) {
EventHandler &handle = key_value.second;
DEBUG(log,out).print("calling handler for destroyed building event\n"); DEBUG(log,out).print("calling handler for destroyed building event\n");
handle.eventHandler(out, (void*)intptr_t(id)); handle.eventHandler(out, (void*)intptr_t(id));
} }
a = buildings.erase(a); it = buildings.erase(it);
} }
//alert people about newly created buildings
std::for_each(new_buildings.begin(), new_buildings.end(), [&](int32_t building){
for (auto &[_,handle] : copy) {
DEBUG(log,out).print("calling handler for created building event\n");
handle.eventHandler(out, (void*)intptr_t(building));
}
});
} }
static void manageConstructionEvent(color_ostream& out) { static void manageConstructionEvent(color_ostream& out) {
@ -743,35 +774,41 @@ static void manageConstructionEvent(color_ostream& out) {
//unordered_set<df::construction*> constructionsNow(df::global::world->constructions.begin(), df::global::world->constructions.end()); //unordered_set<df::construction*> constructionsNow(df::global::world->constructions.begin(), df::global::world->constructions.end());
multimap<Plugin*, EventHandler> copy(handlers[EventType::CONSTRUCTION].begin(), handlers[EventType::CONSTRUCTION].end()); multimap<Plugin*, EventHandler> copy(handlers[EventType::CONSTRUCTION].begin(), handlers[EventType::CONSTRUCTION].end());
// find & send construction removals
for (auto iter = constructions.begin(); iter != constructions.end();) { unordered_set<df::construction> next_construction_set; // will be swapped with constructions
auto &construction = *iter; next_construction_set.reserve(constructions.bucket_count());
// if we can't find it, it was removed vector<df::construction> new_constructions;
if (df::construction::find(construction.pos) != nullptr) {
++iter; // find new constructions - swapping found constructions over from constructions to next_construction_set
continue; for (auto c : df::global::world->constructions) {
auto &construction = *c;
auto it = constructions.find(construction);
if (it == constructions.end()) {
// handle new construction event later
new_constructions.emplace_back(construction);
}
else {
constructions.erase(it);
} }
// send construction to handlers, because it was removed next_construction_set.emplace(construction);
for (const auto &key_value: copy) { }
EventHandler handle = key_value.second;
constructions.swap(next_construction_set);
// now next_construction_set contains all the constructions that were removed (not found in df::global::world->constructions)
for (auto& construction : next_construction_set) {
// handle construction removed event
for (const auto &[_,handle]: copy) {
DEBUG(log,out).print("calling handler for destroyed construction event\n"); DEBUG(log,out).print("calling handler for destroyed construction event\n");
handle.eventHandler(out, (void*) &construction); handle.eventHandler(out, (void*) &construction);
} }
// erase from existent constructions
iter = constructions.erase(iter);
} }
// find & send construction additions // now handle all the new constructions
for (auto c: df::global::world->constructions) { for (auto& construction : new_constructions) {
auto &construction = *c; for (const auto &[_,handle]: copy) {
// add construction to constructions, if it isn't already present DEBUG(log,out).print("calling handler for created construction event\n");
if (constructions.emplace(construction).second) { handle.eventHandler(out, (void*) &construction);
// send construction to handlers, because it is new
for (const auto &key_value: copy) {
EventHandler handle = key_value.second;
DEBUG(log,out).print("calling handler for created construction event\n");
handle.eventHandler(out, (void*) &construction);
}
} }
} }
} }
@ -781,6 +818,8 @@ static void manageSyndromeEvent(color_ostream& out) {
return; return;
multimap<Plugin*,EventHandler> copy(handlers[EventType::SYNDROME].begin(), handlers[EventType::SYNDROME].end()); multimap<Plugin*,EventHandler> copy(handlers[EventType::SYNDROME].begin(), handlers[EventType::SYNDROME].end());
int32_t highestTime = -1; int32_t highestTime = -1;
std::vector<SyndromeData> new_syndrome_data;
for (auto unit : df::global::world->units.all) { for (auto unit : df::global::world->units.all) {
/* /*
@ -795,14 +834,16 @@ static void manageSyndromeEvent(color_ostream& out) {
if ( startTime <= lastSyndromeTime ) if ( startTime <= lastSyndromeTime )
continue; continue;
SyndromeData data(unit->id, b); new_syndrome_data.emplace_back(unit->id, b);
for (auto &key_value : copy) {
EventHandler &handle = key_value.second;
DEBUG(log,out).print("calling handler for syndrome event\n");
handle.eventHandler(out, (void*)&data);
}
} }
} }
for (auto& data : new_syndrome_data) {
for (auto &[_,handle] : copy) {
DEBUG(log,out).print("calling handler for syndrome event\n");
handle.eventHandler(out, (void*)&data);
}
}
lastSyndromeTime = highestTime; lastSyndromeTime = highestTime;
} }
@ -815,8 +856,7 @@ static void manageInvasionEvent(color_ostream& out) {
return; return;
nextInvasion = df::global::plotinfo->invasions.next_id; nextInvasion = df::global::plotinfo->invasions.next_id;
for (auto &key_value : copy) { for (auto &[_,handle] : copy) {
EventHandler &handle = key_value.second;
DEBUG(log,out).print("calling handler for invasion event\n"); DEBUG(log,out).print("calling handler for invasion event\n");
handle.eventHandler(out, (void*)intptr_t(nextInvasion-1)); handle.eventHandler(out, (void*)intptr_t(nextInvasion-1));
} }
@ -829,6 +869,18 @@ static void manageEquipmentEvent(color_ostream& out) {
unordered_map<int32_t, InventoryItem> itemIdToInventoryItem; unordered_map<int32_t, InventoryItem> itemIdToInventoryItem;
unordered_set<int32_t> currentlyEquipped; unordered_set<int32_t> currentlyEquipped;
vector<InventoryChangeData> equipment_pickups;
vector<InventoryChangeData> equipment_drops;
vector<InventoryChangeData> equipment_changes;
// This vector stores the pointers to newly created changed items
// needed as the stack allocated temporary (in the loop) is lost when we go to
// handle the event calls, so we move that data to the heap if its needed,
// and then once we are done we delete everything.
vector<InventoryItem*> changed_items;
for (auto unit : df::global::world->units.all) { for (auto unit : df::global::world->units.all) {
itemIdToInventoryItem.clear(); itemIdToInventoryItem.clear();
currentlyEquipped.clear(); currentlyEquipped.clear();
@ -856,40 +908,30 @@ static void manageEquipmentEvent(color_ostream& out) {
auto c = itemIdToInventoryItem.find(dfitem_new->item->id); auto c = itemIdToInventoryItem.find(dfitem_new->item->id);
if ( c == itemIdToInventoryItem.end() ) { if ( c == itemIdToInventoryItem.end() ) {
//new item equipped (probably just picked up) //new item equipped (probably just picked up)
InventoryChangeData data(unit->id, nullptr, &item_new); changed_items.emplace_back(new InventoryItem(item_new));
for (auto &key_value : copy) { equipment_pickups.emplace_back(unit->id, nullptr, changed_items.back());
EventHandler &handle = key_value.second;
DEBUG(log,out).print("calling handler for new item equipped inventory change event\n");
handle.eventHandler(out, (void*)&data);
}
continue; continue;
} }
InventoryItem item_old = (*c).second; InventoryItem item_old = c->second;
df::unit_inventory_item& item0 = item_old.item; df::unit_inventory_item& item0 = item_old.item;
df::unit_inventory_item& item1 = item_new.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 ) if ( item0.mode == item1.mode && item0.body_part_id == item1.body_part_id && item0.wound_id == item1.wound_id )
continue; continue;
//some sort of change in how it's equipped //some sort of change in how it's equipped
changed_items.emplace_back(new InventoryItem(item_new));
InventoryChangeData data(unit->id, &item_old, &item_new); InventoryItem* item_new_ptr = changed_items.back();
for (auto &key_value : copy) { changed_items.emplace_back(new InventoryItem(item_old));
EventHandler &handle = key_value.second; InventoryItem* item_old_ptr = changed_items.back();
DEBUG(log,out).print("calling handler for inventory change event\n"); equipment_changes.emplace_back(unit->id, item_old_ptr, item_new_ptr);
handle.eventHandler(out, (void*)&data);
}
} }
//check for dropped items //check for dropped items
for (auto i : v) { for (auto i : v) {
if ( currentlyEquipped.find(i.itemId) != currentlyEquipped.end() ) if ( currentlyEquipped.find(i.itemId) != currentlyEquipped.end() )
continue; continue;
//TODO: delete ptr if invalid //TODO: delete ptr if invalid
InventoryChangeData data(unit->id, &i, nullptr); changed_items.emplace_back(new InventoryItem(i));
for (auto &key_value : copy) { equipment_drops.emplace_back(unit->id, changed_items.back(), nullptr);
EventHandler &handle = key_value.second;
DEBUG(log,out).print("calling handler for dropped item inventory change event\n");
handle.eventHandler(out, (void*)&data);
}
} }
if ( !hadEquipment ) if ( !hadEquipment )
delete temp; delete temp;
@ -902,6 +944,31 @@ static void manageEquipmentEvent(color_ostream& out) {
equipment.push_back(item); equipment.push_back(item);
} }
} }
// now handle events
std::for_each(equipment_pickups.begin(), equipment_pickups.end(), [&](InventoryChangeData& data) {
for (auto &[_, handle] : copy) {
DEBUG(log,out).print("calling handler for new item equipped inventory change event\n");
handle.eventHandler(out, (void*) &data);
}
});
std::for_each(equipment_drops.begin(), equipment_drops.end(), [&](InventoryChangeData& data) {
for (auto &[_, handle] : copy) {
DEBUG(log,out).print("calling handler for dropped item inventory change event\n");
handle.eventHandler(out, (void*) &data);
}
});
std::for_each(equipment_changes.begin(), equipment_changes.end(), [&](InventoryChangeData& data) {
for (auto &[_, handle] : copy) {
DEBUG(log,out).print("calling handler for inventory change event\n");
handle.eventHandler(out, (void*) &data);
}
});
// clean up changed items list
std::for_each(changed_items.begin(), changed_items.end(), [](InventoryItem* p){
delete p;
});
} }
static void updateReportToRelevantUnits() { static void updateReportToRelevantUnits() {
@ -939,8 +1006,7 @@ static void manageReportEvent(color_ostream& out) {
for ( ; idx < reports.size(); idx++ ) { for ( ; idx < reports.size(); idx++ ) {
df::report* report = reports[idx]; df::report* report = reports[idx];
for (auto &key_value : copy) { for (auto &[_,handle] : copy) {
EventHandler &handle = key_value.second;
DEBUG(log,out).print("calling handler for report event\n"); DEBUG(log,out).print("calling handler for report event\n");
handle.eventHandler(out, (void*)intptr_t(report->id)); handle.eventHandler(out, (void*)intptr_t(report->id));
} }
@ -981,7 +1047,7 @@ static void manageUnitAttackEvent(color_ostream& out) {
if ( strikeReports.empty() ) if ( strikeReports.empty() )
return; return;
updateReportToRelevantUnits(); updateReportToRelevantUnits();
map<int32_t, map<int32_t, int32_t> > alreadyDone; unordered_set<std::pair<int32_t, int32_t>, hash_pair> already_done;
for (int reportId : strikeReports) { for (int reportId : strikeReports) {
df::report* report = df::report::find(reportId); df::report* report = df::report::find(reportId);
if ( !report ) if ( !report )
@ -1011,27 +1077,25 @@ static void manageUnitAttackEvent(color_ostream& out) {
UnitAttackData data{}; UnitAttackData data{};
data.report_id = report->id; data.report_id = report->id;
if ( wound1 && !alreadyDone[unit1->id][unit2->id] ) { if ( wound1 && already_done.find(std::make_pair(unit1->id,unit2->id)) == already_done.end() ) {
data.attacker = unit1->id; data.attacker = unit1->id;
data.defender = unit2->id; data.defender = unit2->id;
data.wound = wound1->id; data.wound = wound1->id;
alreadyDone[data.attacker][data.defender] = 1; already_done.emplace(unit1->id, unit2->id);
for (auto &key_value : copy) { for (auto &[_,handle] : copy) {
EventHandler &handle = key_value.second;
DEBUG(log,out).print("calling handler for unit1 attack unit attack event\n"); DEBUG(log,out).print("calling handler for unit1 attack unit attack event\n");
handle.eventHandler(out, (void*)&data); handle.eventHandler(out, (void*)&data);
} }
} }
if ( wound2 && !alreadyDone[unit1->id][unit2->id] ) { if ( wound2 && already_done.find(std::make_pair(unit1->id,unit2->id)) == already_done.end() ) {
data.attacker = unit2->id; data.attacker = unit2->id;
data.defender = unit1->id; data.defender = unit1->id;
data.wound = wound2->id; data.wound = wound2->id;
alreadyDone[data.attacker][data.defender] = 1; already_done.emplace(unit1->id, unit2->id);
for (auto &key_value : copy) { for (auto &[_,handle] : copy) {
EventHandler &handle = key_value.second;
DEBUG(log,out).print("calling handler for unit2 attack unit attack event\n"); DEBUG(log,out).print("calling handler for unit2 attack unit attack event\n");
handle.eventHandler(out, (void*)&data); handle.eventHandler(out, (void*)&data);
} }
@ -1041,9 +1105,9 @@ static void manageUnitAttackEvent(color_ostream& out) {
data.attacker = unit2->id; data.attacker = unit2->id;
data.defender = unit1->id; data.defender = unit1->id;
data.wound = -1; data.wound = -1;
alreadyDone[data.attacker][data.defender] = 1;
for (auto &key_value : copy) { already_done.emplace(unit1->id, unit2->id);
EventHandler &handle = key_value.second; for (auto &[_,handle] : copy) {
DEBUG(log,out).print("calling handler for unit1 killed unit attack event\n"); DEBUG(log,out).print("calling handler for unit1 killed unit attack event\n");
handle.eventHandler(out, (void*)&data); handle.eventHandler(out, (void*)&data);
} }
@ -1053,9 +1117,9 @@ static void manageUnitAttackEvent(color_ostream& out) {
data.attacker = unit1->id; data.attacker = unit1->id;
data.defender = unit2->id; data.defender = unit2->id;
data.wound = -1; data.wound = -1;
alreadyDone[data.attacker][data.defender] = 1;
for (auto &key_value : copy) { already_done.emplace(unit1->id, unit2->id);
EventHandler &handle = key_value.second; for (auto &[_,handle] : copy) {
DEBUG(log,out).print("calling handler for unit2 killed unit attack event\n"); DEBUG(log,out).print("calling handler for unit2 killed unit attack event\n");
handle.eventHandler(out, (void*)&data); handle.eventHandler(out, (void*)&data);
} }
@ -1313,8 +1377,7 @@ static void manageInteractionEvent(color_ostream& out) {
lastAttacker = df::unit::find(data.attacker); lastAttacker = df::unit::find(data.attacker);
//lastDefender = df::unit::find(data.defender); //lastDefender = df::unit::find(data.defender);
//fire event //fire event
for (auto &key_value : copy) { for (auto &[_,handle] : copy) {
EventHandler &handle = key_value.second;
DEBUG(log,out).print("calling handler for interaction event\n"); DEBUG(log,out).print("calling handler for interaction event\n");
handle.eventHandler(out, (void*)&data); handle.eventHandler(out, (void*)&data);
} }