EventManager: completely overhauled JOB_COMPLETED event detection, and tweaked Job::cloneJobStruct.

develop
expwnent 2013-05-25 09:44:17 -04:00
parent 01c311cf13
commit b44cd86dc9
4 changed files with 175 additions and 55 deletions

@ -49,10 +49,10 @@ namespace DFHack
{ {
namespace Job { namespace Job {
// Duplicate the job structure. It is not linked into any DF lists. // 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. // 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 printItemDetails(color_ostream &out, df::job_item *item, int idx);
DFHACK_EXPORT void printJobDetails(color_ostream &out, df::job *job); DFHACK_EXPORT void printJobDetails(color_ostream &out, df::job *job);

@ -8,6 +8,9 @@
#include "df/building.h" #include "df/building.h"
#include "df/construction.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/global_objects.h"
#include "df/item.h" #include "df/item.h"
#include "df/job.h" #include "df/job.h"
@ -150,7 +153,7 @@ void DFHack::EventManager::onStateChange(color_ostream& out, state_change_event
lastTick = 0; lastTick = 0;
lastJobId = -1; lastJobId = -1;
for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) { for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) {
Job::deleteJobStruct((*i).second); Job::deleteJobStruct((*i).second, true);
} }
prevJobs.clear(); prevJobs.clear();
tickQueue.clear(); tickQueue.clear();
@ -188,9 +191,9 @@ void DFHack::EventManager::manageEvents(color_ostream& out) {
} }
uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear uint32_t tick = DFHack::World::ReadCurrentYear()*ticksPerYear
+ DFHack::World::ReadCurrentTick(); + DFHack::World::ReadCurrentTick();
/*if ( tick - lastTick > 1 ) {
if ( tick <= lastTick ) out.print("EventManager missed tick: %d, %d, (%d)\n", lastTick, tick, tick - lastTick);
return; }*/
lastTick = tick; lastTick = tick;
int32_t eventFrequency[EventType::EVENT_MAX]; int32_t eventFrequency[EventType::EVENT_MAX];
@ -237,8 +240,6 @@ void DFHack::EventManager::manageEvents(color_ostream& out) {
manageInvasionEvent(out); manageInvasionEvent(out);
eventLastTick[EventType::INVASION] = tick; eventLastTick[EventType::INVASION] = tick;
} }
return;
} }
static void manageTickEvent(color_ostream& out) { static void manageTickEvent(color_ostream& out) {
@ -281,11 +282,25 @@ static void manageJobInitiatedEvent(color_ostream& out) {
lastJobId = *df::global::job_next_id - 1; 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) { static void manageJobCompletedEvent(color_ostream& out) {
if ( handlers[EventType::JOB_COMPLETED].empty() ) { if ( handlers[EventType::JOB_COMPLETED].empty() ) {
return; return;
} }
uint32_t tick0 = eventLastTick[EventType::JOB_COMPLETED];
uint32_t tick1 = DFHack::World::ReadCurrentYear()*ticksPerYear
+ DFHack::World::ReadCurrentTick();
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; map<int32_t, df::job*> nowJobs;
for ( df::job_list_link* link = &df::global::world->job_list; link != NULL; link = link->next ) { for ( df::job_list_link* link = &df::global::world->job_list; link != NULL; link = link->next ) {
@ -294,11 +309,96 @@ static void manageJobCompletedEvent(color_ostream& out) {
nowJobs[link->item->id] = link->item; 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++ ) { 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;
}
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; continue;
//recently finished or cancelled job!
for ( auto j = copy.begin(); j != copy.end(); j++ ) { for ( auto j = copy.begin(); j != copy.end(); j++ ) {
(*j).second.eventHandler(out, (void*)(*i).second); (*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 //erase old jobs, copy over possibly altered jobs
for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) { for ( auto i = prevJobs.begin(); i != prevJobs.end(); i++ ) {
Job::deleteJobStruct((*i).second); Job::deleteJobStruct((*i).second, true);
} }
prevJobs.clear(); prevJobs.clear();
@ -320,11 +420,6 @@ static void manageJobCompletedEvent(color_ostream& out) {
df::job* newJob = Job::cloneJobStruct((*j).second, true); df::job* newJob = Job::cloneJobStruct((*j).second, true);
prevJobs[newJob->id] = newJob; 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) { static void manageUnitDeathEvent(color_ostream& out) {

@ -55,53 +55,68 @@ using namespace std;
using namespace DFHack; using namespace DFHack;
using namespace df::enums; 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); CHECK_NULL_POINTER(job);
df::job *pnew = new df::job(*job); df::job *pnew = new df::job(*job);
if ( !keepEverything ) {
// Clean out transient fields // Clean out transient fields
pnew->flags.whole = 0; pnew->flags.whole = 0;
pnew->flags.bits.repeat = job->flags.bits.repeat; pnew->flags.bits.repeat = job->flags.bits.repeat;
pnew->flags.bits.suspend = job->flags.bits.suspend; pnew->flags.bits.suspend = job->flags.bits.suspend;
pnew->list_link = NULL;
pnew->completion_timer = -1; pnew->completion_timer = -1;
pnew->items.clear(); }
pnew->specific_refs.clear(); pnew->list_link = NULL;
// Clone refs //pnew->items.clear();
for (int i = pnew->general_refs.size()-1; i >= 0; i--) //pnew->specific_refs.clear();
{ pnew->general_refs.clear();
df::general_ref *ref = pnew->general_refs[i]; //pnew->job_items.clear();
if (!keepWorkerData && virtual_cast<df::general_ref_unit_workerst>(ref)) if ( keepEverything ) {
vector_erase_at(pnew->general_refs, i); for ( int a = 0; a < pnew->items.size(); a++ )
else pnew->items[a] = new df::job_item_ref(*pnew->items[a]);
pnew->general_refs[i] = ref->clone(); 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 a = 0; a < pnew->job_items.size(); a++ )
for (int i = pnew->job_items.size()-1; i >= 0; i--) pnew->job_items[a] = new df::job_item(*pnew->job_items[a]);
pnew->job_items[i] = new df::job_item(*pnew->job_items[i]);
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; return pnew;
} }
void DFHack::Job::deleteJobStruct(df::job *job) void DFHack::Job::deleteJobStruct(df::job *job, bool keptEverything)
{ {
if (!job) if (!job)
return; return;
// Only allow free-floating job structs // Only allow free-floating job structs
if ( !keptEverything )
assert(!job->list_link && job->items.empty() && job->specific_refs.empty()); assert(!job->list_link && job->items.empty() && job->specific_refs.empty());
else
assert(!job->list_link);
for (int i = job->general_refs.size()-1; i >= 0; i--) if ( keptEverything ) {
delete job->general_refs[i]; for ( int a = 0; a < job->items.size(); a++ )
delete job->items[a];
for (int i = job->job_items.size()-1; i >= 0; i--) for ( int a = 0; a < job->specific_refs.size(); a++ )
delete job->job_items[i]; 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; delete job;
} }

@ -7,6 +7,7 @@
#include "DataDefs.h" #include "DataDefs.h"
#include "df/item.h" #include "df/item.h"
#include "df/job.h"
#include "df/world.h" #include "df/world.h"
#include <vector> #include <vector>
@ -34,11 +35,11 @@ DFhackCExport command_result plugin_init(color_ostream &out, std::vector<PluginC
} }
command_result eventExample(color_ostream& out, vector<string>& parameters) { command_result eventExample(color_ostream& out, vector<string>& parameters) {
EventManager::EventHandler initiateHandler(jobInitiated, 10); EventManager::EventHandler initiateHandler(jobInitiated, 1);
EventManager::EventHandler completeHandler(jobCompleted, 5); EventManager::EventHandler completeHandler(jobCompleted, 0);
EventManager::EventHandler timeHandler(timePassed, 1); EventManager::EventHandler timeHandler(timePassed, 1);
EventManager::EventHandler deathHandler(unitDeath, 500); EventManager::EventHandler deathHandler(unitDeath, 500);
EventManager::EventHandler itemHandler(itemCreate, 1000); EventManager::EventHandler itemHandler(itemCreate, 1);
EventManager::EventHandler buildingHandler(building, 500); EventManager::EventHandler buildingHandler(building, 500);
EventManager::EventHandler constructionHandler(construction, 100); EventManager::EventHandler constructionHandler(construction, 100);
EventManager::EventHandler syndromeHandler(syndrome, 1); EventManager::EventHandler syndromeHandler(syndrome, 1);
@ -62,8 +63,17 @@ command_result eventExample(color_ostream& out, vector<string>& parameters) {
return CR_OK; return CR_OK;
} }
void jobInitiated(color_ostream& out, void* job) { //static int timerCount=0;
out.print("Job initiated! 0x%X\n", job); //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) { void jobCompleted(color_ostream& out, void* job) {