From b44cd86dc957818a9ea170a58c1d46bf0d8263ab Mon Sep 17 00:00:00 2001 From: expwnent Date: Sat, 25 May 2013 09:44:17 -0400 Subject: [PATCH] EventManager: completely overhauled JOB_COMPLETED event detection, and tweaked Job::cloneJobStruct. --- library/include/modules/Job.h | 4 +- library/modules/EventManager.cpp | 127 +++++++++++++++++++++++++++---- library/modules/Job.cpp | 79 +++++++++++-------- plugins/devel/eventExample.cpp | 20 +++-- 4 files changed, 175 insertions(+), 55 deletions(-) diff --git a/library/include/modules/Job.h b/library/include/modules/Job.h index e4d7dc36b..a7b107b18 100644 --- a/library/include/modules/Job.h +++ b/library/include/modules/Job.h @@ -49,10 +49,10 @@ namespace DFHack { namespace Job { // Duplicate the job structure. It is not linked into any DF lists. - DFHACK_EXPORT df::job *cloneJobStruct(df::job *job, bool keepWorkerData=false); + DFHACK_EXPORT df::job *cloneJobStruct(df::job *job, bool keepEverything=false); // Delete a cloned structure. - DFHACK_EXPORT void deleteJobStruct(df::job *job); + DFHACK_EXPORT void deleteJobStruct(df::job *job, bool keptEverything=false); DFHACK_EXPORT void printItemDetails(color_ostream &out, df::job_item *item, int idx); DFHACK_EXPORT void printJobDetails(color_ostream &out, df::job *job); diff --git a/library/modules/EventManager.cpp b/library/modules/EventManager.cpp index d6f40e4a2..2718fee3f 100644 --- a/library/modules/EventManager.cpp +++ b/library/modules/EventManager.cpp @@ -8,6 +8,9 @@ #include "df/building.h" #include "df/construction.h" +#include "df/general_ref.h" +#include "df/general_ref_type.h" +#include "df/general_ref_unit_workerst.h" #include "df/global_objects.h" #include "df/item.h" #include "df/job.h" @@ -150,7 +153,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event lastTick = 0; lastJobId = -1; for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) { - Job::deleteJobStruct((*i).second); + Job::deleteJobStruct((*i).second, true); } prevJobs.clear(); tickQueue.clear(); @@ -188,9 +191,9 @@ void DFHack::EventManager::manageEvents(color_ostream& out) { } uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear + DFHack::World::ReadCurrentTick(); - - if ( tick <= lastTick ) - return; + /*if ( tick - lastTick > 1 ) { + out.print("EventManager missed tick: %d, %d, (%d)\n", lastTick, tick, tick - lastTick); + }*/ lastTick = tick; int32_t eventFrequency[EventType::EVENT_MAX]; @@ -237,8 +240,6 @@ void DFHack::EventManager::manageEvents(color_ostream& out) { manageInvasionEvent(out); eventLastTick[EventType::INVASION] = tick; } - - return; } static void manageTickEvent(color_ostream& out) { @@ -281,11 +282,25 @@ static void manageJobInitiatedEvent(color_ostream& out) { lastJobId = *df::global::job_next_id - 1; } +//helper function for manageJobCompletedEvent +static int32_t getWorkerID(df::job* job) { + for ( size_t a = 0; a < job->general_refs.size(); a++ ) { + if ( job->general_refs[a]->getType() != df::enums::general_ref_type::UNIT_WORKER ) + continue; + return ((df::general_ref_unit_workerst*)job->general_refs[a])->unit_id; + } + return -1; +} + static void manageJobCompletedEvent(color_ostream& out) { if ( handlers[EventType::JOB_COMPLETED].empty() ) { return; } + uint32_t tick0 = eventLastTick[EventType::JOB_COMPLETED]; + uint32_t tick1 = DFHack::World::ReadCurrentYear()*ticksPerYear + + DFHack::World::ReadCurrentTick(); + multimap copy(handlers[EventType::JOB_COMPLETED].begin(), handlers[EventType::JOB_COMPLETED].end()); map nowJobs; for ( df::job_list_link* link = &df::global::world->job_list; link != NULL; link = link->next ) { @@ -293,12 +308,97 @@ static void manageJobCompletedEvent(color_ostream& out) { continue; nowJobs[link->item->id] = link->item; } - + +#if 0 + //testing info on job initiation/completion + //newly allocated jobs + for ( auto j = nowJobs.begin(); j != nowJobs.end(); j++ ) { + if ( prevJobs.find((*j).first) != prevJobs.end() ) + continue; + + df::job& job1 = *(*j).second; + out.print("new job\n" + " location : 0x%X\n" + " id : %d\n" + " type : %d %s\n" + " working : %d\n" + " completion_timer : %d\n" + " workerID : %d\n" + " time : %d -> %d\n" + "\n", job1.list_link->item, job1.id, job1.job_type, ENUM_ATTR(job_type, caption, job1.job_type), job1.flags.bits.working, job1.completion_timer, getWorkerID(&job1), tick0, tick1); + } for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) { - if ( nowJobs.find((*i).first) != nowJobs.end() ) + df::job& job0 = *(*i).second; + auto j = nowJobs.find((*i).first); + if ( j == nowJobs.end() ) { + out.print("job deallocated\n" + " location : 0x%X\n" + " id : %d\n" + " type : %d %s\n" + " working : %d\n" + " completion_timer : %d\n" + " workerID : %d\n" + " time : %d -> %d\n" + ,job0.list_link == NULL ? 0 : job0.list_link->item, job0.id, job0.job_type, ENUM_ATTR(job_type, caption, job0.job_type), job0.flags.bits.working, job0.completion_timer, getWorkerID(&job0), tick0, tick1); continue; - - //recently finished or cancelled job! + } + df::job& job1 = *(*j).second; + + if ( job0.flags.bits.working == job1.flags.bits.working && + (job0.completion_timer == job1.completion_timer || (job1.completion_timer > 0 && job0.completion_timer-1 == job1.completion_timer)) && + getWorkerID(&job0) == getWorkerID(&job1) ) + continue; + + out.print("job change\n" + " location : 0x%X -> 0x%X\n" + " id : %d -> %d\n" + " type : %d -> %d\n" + " type : %s -> %s\n" + " working : %d -> %d\n" + " completion timer : %d -> %d\n" + " workerID : %d -> %d\n" + " time : %d -> %d\n" + "\n", + job0.list_link->item, job1.list_link->item, + job0.id, job1.id, + job0.job_type, job1.job_type, + ENUM_ATTR(job_type, caption, job0.job_type), ENUM_ATTR(job_type, caption, job1.job_type), + job0.flags.bits.working, job1.flags.bits.working, + job0.completion_timer, job1.completion_timer, + getWorkerID(&job0), getWorkerID(&job1), + tick0, tick1 + ); + } +#endif + + for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) { + //if it happened within a tick, must have been cancelled by the user or a plugin: not completed + if ( tick1 <= tick0 ) + continue; + + if ( nowJobs.find((*i).first) != nowJobs.end() ) { + //could have just finished if it's a repeat job + df::job& job0 = *(*i).second; + if ( !job0.flags.bits.repeat ) + continue; + df::job& job1 = *nowJobs[(*i).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 + for ( auto j = copy.begin(); j != copy.end(); j++ ) { + (*j).second.eventHandler(out, (void*)&job0); + } + continue; + } + + //recently finished or cancelled job + df::job& job0 = *(*i).second; + if ( job0.flags.bits.repeat || job0.completion_timer != 0 ) + continue; + for ( auto j = copy.begin(); j != copy.end(); j++ ) { (*j).second.eventHandler(out, (void*)(*i).second); } @@ -306,7 +406,7 @@ static void manageJobCompletedEvent(color_ostream& out) { //erase old jobs, copy over possibly altered jobs for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) { - Job::deleteJobStruct((*i).second); + Job::deleteJobStruct((*i).second, true); } prevJobs.clear(); @@ -320,11 +420,6 @@ static void manageJobCompletedEvent(color_ostream& out) { df::job* newJob = Job::cloneJobStruct((*j).second, true); prevJobs[newJob->id] = newJob; } - - /*//get rid of old pointers to deallocated jobs - for ( size_t a = 0; a < toDelete.size(); a++ ) { - prevJobs.erase(a); - }*/ } static void manageUnitDeathEvent(color_ostream& out) { diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index c6469657c..feb0f33a7 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -55,53 +55,68 @@ using namespace std; using namespace DFHack; using namespace df::enums; -df::job *DFHack::Job::cloneJobStruct(df::job *job, bool keepWorkerData) +df::job *DFHack::Job::cloneJobStruct(df::job *job, bool keepEverything) { CHECK_NULL_POINTER(job); df::job *pnew = new df::job(*job); - - // Clean out transient fields - pnew->flags.whole = 0; - pnew->flags.bits.repeat = job->flags.bits.repeat; - pnew->flags.bits.suspend = job->flags.bits.suspend; - + + if ( !keepEverything ) { + // Clean out transient fields + pnew->flags.whole = 0; + pnew->flags.bits.repeat = job->flags.bits.repeat; + pnew->flags.bits.suspend = job->flags.bits.suspend; + + pnew->completion_timer = -1; + } pnew->list_link = NULL; - pnew->completion_timer = -1; - pnew->items.clear(); - pnew->specific_refs.clear(); - // Clone refs - for (int i = pnew->general_refs.size()-1; i >= 0; i--) - { - df::general_ref *ref = pnew->general_refs[i]; - - if (!keepWorkerData && virtual_cast(ref)) - vector_erase_at(pnew->general_refs, i); - else - pnew->general_refs[i] = ref->clone(); + //pnew->items.clear(); + //pnew->specific_refs.clear(); + pnew->general_refs.clear(); + //pnew->job_items.clear(); + + if ( keepEverything ) { + for ( int a = 0; a < pnew->items.size(); a++ ) + pnew->items[a] = new df::job_item_ref(*pnew->items[a]); + for ( int a = 0; a < pnew->specific_refs.size(); a++ ) + pnew->specific_refs[a] = new df::specific_ref(*pnew->specific_refs[a]); + } else { + pnew->items.clear(); + pnew->specific_refs.clear(); } - - // Clone items - for (int i = pnew->job_items.size()-1; i >= 0; i--) - pnew->job_items[i] = new df::job_item(*pnew->job_items[i]); - + + for ( int a = 0; a < pnew->job_items.size(); a++ ) + pnew->job_items[a] = new df::job_item(*pnew->job_items[a]); + + for ( int a = 0; a < job->general_refs.size(); a++ ) + if ( keepEverything || job->general_refs[a]->getType() != df::enums::general_ref_type::UNIT_WORKER ) + pnew->general_refs.push_back(job->general_refs[a]->clone()); + return pnew; } -void DFHack::Job::deleteJobStruct(df::job *job) +void DFHack::Job::deleteJobStruct(df::job *job, bool keptEverything) { if (!job) return; // Only allow free-floating job structs - assert(!job->list_link && job->items.empty() && job->specific_refs.empty()); - - for (int i = job->general_refs.size()-1; i >= 0; i--) - delete job->general_refs[i]; - - for (int i = job->job_items.size()-1; i >= 0; i--) - delete job->job_items[i]; + if ( !keptEverything ) + assert(!job->list_link && job->items.empty() && job->specific_refs.empty()); + else + assert(!job->list_link); + + if ( keptEverything ) { + for ( int a = 0; a < job->items.size(); a++ ) + delete job->items[a]; + for ( int a = 0; a < job->specific_refs.size(); a++ ) + delete job->specific_refs[a]; + } + for ( int a = 0; a < job->job_items.size(); a++ ) + delete job->job_items[a]; + for ( int a = 0; a < job->general_refs.size(); a++ ) + delete job->general_refs[a]; delete job; } diff --git a/plugins/devel/eventExample.cpp b/plugins/devel/eventExample.cpp index 3a2970c5b..49cdac4f9 100644 --- a/plugins/devel/eventExample.cpp +++ b/plugins/devel/eventExample.cpp @@ -7,6 +7,7 @@ #include "DataDefs.h" #include "df/item.h" +#include "df/job.h" #include "df/world.h" #include @@ -34,11 +35,11 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector& parameters) { - EventManager::EventHandler initiateHandler(jobInitiated, 10); - EventManager::EventHandler completeHandler(jobCompleted, 5); + EventManager::EventHandler initiateHandler(jobInitiated, 1); + EventManager::EventHandler completeHandler(jobCompleted, 0); EventManager::EventHandler timeHandler(timePassed, 1); EventManager::EventHandler deathHandler(unitDeath, 500); - EventManager::EventHandler itemHandler(itemCreate, 1000); + EventManager::EventHandler itemHandler(itemCreate, 1); EventManager::EventHandler buildingHandler(building, 500); EventManager::EventHandler constructionHandler(construction, 100); EventManager::EventHandler syndromeHandler(syndrome, 1); @@ -62,8 +63,17 @@ command_result eventExample(color_ostream& out, vector& parameters) { return CR_OK; } -void jobInitiated(color_ostream& out, void* job) { - out.print("Job initiated! 0x%X\n", job); +//static int timerCount=0; +//static int timerDenom=0; +void jobInitiated(color_ostream& out, void* job_) { + out.print("Job initiated! 0x%X\n", job_); +/* + df::job* job = (df::job*)job_; + out.print(" completion_timer = %d\n", job->completion_timer); + if ( job->completion_timer != -1 ) timerCount++; + timerDenom++; + out.print(" frac = %d / %d\n", timerCount, timerDenom); +*/ } void jobCompleted(color_ostream& out, void* job) {