manageJobStartedEvent handle moved to inner loop and iterates from previous in linked list in case it is removed. fix a bug with item change events and using invalidated stack pointer as item data. swapped order of destroyed and created building event calls, and a couple other improvements

develop
Najeeb Al-Shabibi 2023-09-28 16:19:32 +01:00
parent 7203e148ea
commit 70ff728fba
1 changed files with 56 additions and 42 deletions

@ -466,18 +466,22 @@ static void manageJobStartedEvent(color_ostream& out) {
vector<df::job*> new_started_jobs; 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);
new_started_jobs.emplace_back(job); for (auto &[_,handle] : copy) {
// the jobs must have a worker to start
DEBUG(log,out).print("calling handler for job started event\n");
handle.eventHandler(out, job);
}
} }
} if (link->next == nullptr || link->next->item->id != j_id) {
for (df::job* job : new_started_jobs) { if ( Once::doOnce("EventManager jobstarted job removed") ) {
for (auto &[_,handle] : copy) { out.print("%s,%d: job %u removed from jobs linked list\n", __FILE__, __LINE__, j_id);
// the jobs must have a worker to start }
DEBUG(log,out).print("calling handler for job started event\n");
handle.eventHandler(out, job);
} }
} }
} }
@ -498,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
@ -567,8 +571,6 @@ static void manageJobCompletedEvent(color_ostream& out) {
} }
#endif #endif
vector<df::job*> new_jobs_completed;
vector<df::job*> new_jobs_completed_repeats;
for (auto &prevJob : prevJobs) { for (auto &prevJob : prevJobs) {
//if it happened within a tick, must have been cancelled by the user or a plugin: not completed //if it happened within a tick, must have been cancelled by the user or a plugin: not completed
if ( tick1 <= tick0 ) if ( tick1 <= tick0 )
@ -586,7 +588,10 @@ 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
new_jobs_completed_repeats.emplace_back(&job0); for (auto &[_,handle] : copy) {
DEBUG(log,out).print("calling handler for repeated job completed event\n");
handle.eventHandler(out, (void*) &job0);
}
continue; continue;
} }
@ -595,37 +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;
new_jobs_completed.emplace_back(&job0);
}
for (df::job* job : new_jobs_completed_repeats) {
for (auto &[_,handle] : copy) {
DEBUG(log,out).print("calling handler for repeated job completed event\n");
handle.eventHandler(out, (void*) job);
}
}
for (df::job* job : new_jobs_completed) {
for (auto &[_,handle] : copy) { for (auto &[_,handle] : copy) {
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*) job); 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);
} }
} }
@ -662,6 +657,7 @@ static void manageUnitDeathEvent(color_ostream& out) {
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;
@ -747,13 +743,6 @@ static void manageBuildingEvent(color_ostream& out) {
} }
nextBuilding = *df::global::building_next_id; nextBuilding = *df::global::building_next_id;
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));
}
});
//now alert people about destroyed buildings //now alert people about destroyed buildings
for ( auto it = buildings.begin(); it != buildings.end(); ) { for ( auto it = buildings.begin(); it != buildings.end(); ) {
int32_t id = *it; int32_t id = *it;
@ -769,6 +758,14 @@ static void manageBuildingEvent(color_ostream& out) {
} }
it = buildings.erase(it); 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) {
@ -877,6 +874,13 @@ static void manageEquipmentEvent(color_ostream& out) {
vector<InventoryChangeData> equipment_drops; vector<InventoryChangeData> equipment_drops;
vector<InventoryChangeData> equipment_changes; 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();
@ -904,25 +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)
equipment_pickups.emplace_back(unit->id, nullptr, &item_new); changed_items.emplace_back(new InventoryItem(item_new));
equipment_pickups.emplace_back(unit->id, nullptr, changed_items.back());
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));
equipment_changes.emplace_back(unit->id, nullptr, &item_new); InventoryItem* item_new_ptr = changed_items.back();
changed_items.emplace_back(new InventoryItem(item_old));
InventoryItem* item_old_ptr = changed_items.back();
equipment_changes.emplace_back(unit->id, item_old_ptr, item_new_ptr);
} }
//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
equipment_drops.emplace_back(unit->id, &i, nullptr); changed_items.emplace_back(new InventoryItem(i));
equipment_drops.emplace_back(unit->id, changed_items.back(), nullptr);
} }
if ( !hadEquipment ) if ( !hadEquipment )
delete temp; delete temp;
@ -955,6 +964,11 @@ static void manageEquipmentEvent(color_ostream& out) {
handle.eventHandler(out, (void*) &data); 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() {