Rebuilt slightly to offer bool return val

We fail on unknown general ref types now, without modifying the job at
all yet
develop
Stephen Baynham 2016-11-24 22:36:11 -08:00
parent 8b964ca2dc
commit e490afdf00
3 changed files with 89 additions and 51 deletions

@ -1469,6 +1469,8 @@ static const LuaWrapper::FunctionReg dfhack_job_module[] = {
WRAPM(Job,getName), WRAPM(Job,getName),
WRAPM(Job,linkIntoWorld), WRAPM(Job,linkIntoWorld),
WRAPM(Job,removePostings), WRAPM(Job,removePostings),
WRAPM(Job,disconnectJobItem),
WRAPM(Job,disconnectJobGeneralRef),
WRAPM(Job,removeJob), WRAPM(Job,removeJob),
WRAPN(is_equal, jobEqual), WRAPN(is_equal, jobEqual),
WRAPN(is_item_equal, jobItemEqual), WRAPN(is_item_equal, jobItemEqual),

@ -66,10 +66,20 @@ namespace DFHack
DFHACK_EXPORT void setJobCooldown(df::building *workshop, df::unit *worker, int cooldown = 100); DFHACK_EXPORT void setJobCooldown(df::building *workshop, df::unit *worker, int cooldown = 100);
DFHACK_EXPORT bool removeWorker(df::job *job, int cooldown = 100); DFHACK_EXPORT bool removeWorker(df::job *job, int cooldown = 100);
// This helpful method only removes the backref from the item to the job, but it doesn't
// remove the item ref from the job's vector, or delete it or anything. Think of it as a method
// that does all the needful to make an item ref ready to delete.
DFHACK_EXPORT void disconnectJobItem(df::job_item_ref *item, df::job *job);
// This helpful method only removes the backref from whatever the general_ref points to,
// it doesn't remove the general_ref from the job's vector, or delete it or anything.
// Think of it as a method that does all the needful to make a ref ready to delete.
// If it returns false, you've found a ref that the method doesn't know how to handle. Congratulations!
// You should report that and/or check in a fix.
DFHACK_EXPORT bool disconnectJobGeneralRef(df::general_ref *ref, df::job *job);
// Delete a job & remove all refs from everywhere. // Delete a job & remove all refs from everywhere.
// This method DELETES the job object! Everything related to it will be wiped // This method DELETES the job object! Everything related to it will be wiped
// clean from the earth, so make sure you pull what you need out before calling this! // clean from the earth, so make sure you pull what you need out before calling this!
DFHACK_EXPORT void removeJob(df::job *job); DFHACK_EXPORT bool removeJob(df::job *job);
// Instruct the game to check and assign workers // Instruct the game to check and assign workers
DFHACK_EXPORT void checkBuildingsNow(); DFHACK_EXPORT void checkBuildingsNow();

@ -304,73 +304,98 @@ void DFHack::Job::setJobCooldown(df::building *workshop, df::unit *worker, int c
} }
} }
void DFHack::Job::removeJob(df::job *job) { void DFHack::Job::disconnectJobItem(df::job_item_ref *ref, df::job *job) {
if (!ref) return;
auto item = ref->item;
if (!item) return;
//Work backward through the specific refs & remove/delete all specific refs to this job
int refCount = item->specific_refs.size();
bool stillHasJobs = false;
for(int refIndex = refCount-1; refIndex >= 0; refIndex--) {
auto ref = item->specific_refs[refIndex];
if (ref->type == df::specific_ref_type::JOB) {
if (ref->job == job) {
vector_erase_at(item->specific_refs, refIndex);
delete ref;
} else {
stillHasJobs = true;
}
}
}
if (!stillHasJobs) item->flags.bits.in_job = false;
}
bool DFHack::Job::disconnectJobGeneralRef(df::general_ref *ref, df::job *job) {
if (ref == NULL) return true;
switch (ref->getType()) {
case general_ref_type::BUILDING_HOLDER:
auto building = ref->getBuilding();
if (building != NULL) {
int jobIndex = linear_index(building->jobs, job);
if (jobIndex >= 0) {
vector_erase_at(building->jobs, jobIndex);
}
}
break;
case general_ref_type::UNIT_WORKER:
auto unit = ref->getUnit();
if (unit != NULL) {
if (unit->job.current_job == job) {
unit->job.current_job = NULL;
}
}
break;
default:
return false;
}
return true;
}
bool DFHack::Job::removeJob(df::job *job) {
using df::global::world; using df::global::world;
CHECK_NULL_POINTER(job); CHECK_NULL_POINTER(job);
if (job->flags.bits.special) //I don't think you can cancel these, because DF wasn't build to expect it? if (job->flags.bits.special) //I don't think you can cancel these, because DF wasn't build to expect it?
return; return false;
//As far as I know there are only two general refs jobs have, the unit assigned to work it (if any) //We actually only know how to handle BUILDING_HOLDER and UNIT_WORKER refs- there's probably a great
//and the workshop it was created at (if any). It's possible there are others, so we go ahead and wipe all //way to handle them, but until we have a good example, we'll just fail to remove jobs that have other sorts
//refs, but these two are the only ones that we really handle with any intelligence. If other refs //of refs, or any specific refs
//exist that might have return-references that need to be cleared, that needs to be implemented!!!! if (job->specific_refs.size() > 0)
auto holderRef = getGeneralRef(job, general_ref_type::BUILDING_HOLDER); return false;
auto workerRef = getGeneralRef(job, general_ref_type::UNIT_WORKER);
df::building *holder = NULL;
df::unit *worker = NULL;
if (holderRef) holder = holderRef->getBuilding();
if (workerRef) worker = workerRef->getUnit();
//removeWorker() adds a job cd about right now, but I chose not to do that because I'm pretty sure
//that's only to stop removed workers from immediately reclaiming the job before doing something
//else, and this job is gonna be dead in a second.
//Remove return-refs from the holder & worker
if (holder) {
int jobIndex = linear_index(holder->jobs, job);
if (jobIndex >= 0)
vector_erase_at(holder->jobs, jobIndex);
}
if (worker) { for (auto genRefItr = job->general_refs.begin(); genRefItr != job->general_refs.end(); ++genRefItr) {
if (worker->job.current_job == job) auto ref = *genRefItr;
worker->job.current_job = NULL; if (ref != NULL && (ref->getType() != general_ref_type::BUILDING_HOLDER && ref->getType() != general_ref_type::UNIT_WORKER))
return false;
} }
//Wipe all refs out //Disconnect, delete, and wipe all general refs
while (job->general_refs.size() > 0) { while (job->general_refs.size() > 0) {
auto ref = job->general_refs[0]; auto ref = job->general_refs[0];
//Our code above should have ensured that this won't return false- if it does, there's not
//a great way of recovering since we can't properly destroy the job & we can't leave it
//around. Better to know the moment that becomes a problem.
assert(disconnectJobGeneralRef(ref, job));
vector_erase_at(job->general_refs, 0); vector_erase_at(job->general_refs, 0);
delete ref; if (ref != NULL) delete ref;
} }
//Detach all items from the job //Detach all items from the job
while (job->items.size() > 0) { while (job->items.size() > 0) {
auto itemRef = job->items[0]; auto itemRef = job->items[0];
df::item *item = NULL; disconnectJobItem(itemRef, job);
if (itemRef) {
item = itemRef->item;
if (item) {
item->flags.bits.in_job = false;
//Work backward through the specific refs & remove/delete all specific refs to this job
int refCount = item->specific_refs.size();
for(int refIndex = refCount-1; refIndex >= 0; refIndex--) {
auto ref = item->specific_refs[refIndex];
if (ref->type == df::specific_ref_type::JOB && ref->job == job) {
vector_erase_at(item->specific_refs, refIndex);
delete ref;
}
}
}
delete itemRef;
}
vector_erase_at(job->items, 0); vector_erase_at(job->items, 0);
if (itemRef != NULL) delete itemRef;
} }
//Remove job from job board //Remove job from job board
@ -400,6 +425,7 @@ void DFHack::Job::removeJob(df::job *job) {
} }
delete job; delete job;
return true;
} }
bool DFHack::Job::removeWorker(df::job *job, int cooldown) bool DFHack::Job::removeWorker(df::job *job, int cooldown)