From 3e147fe9024c656ab4929630ba9292b18df86203 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 9 Jan 2012 17:29:28 +0400 Subject: [PATCH] Modify the workflow plugin to protect all repeat jobs when enabled. --- plugins/workflow.cpp | 341 ++++++++++++++++++------------------------- 1 file changed, 143 insertions(+), 198 deletions(-) diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 0a1319d2c..32ca50a82 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -36,7 +36,7 @@ using df::global::job_next_id; /* Plugin registration */ -static command_result protect_job(Core *c, vector & parameters); +static command_result workflow_cmd(Core *c, vector & parameters); static void init_state(Core *c); static void cleanup_state(Core *c); @@ -55,14 +55,17 @@ DFhackCExport command_result plugin_init (Core *c, std::vector & if (ui_workshop_job_cursor && job_next_id) { commands.push_back( PluginCommand( - "protect-job", "Manage protection of workshop jobs from removal.", - protect_job, false, - " protect-job list\n" - " List protected jobs. If a workshop is selected, filters by it.\n" - " protect-job add [all]\n" - " Protect the selected job, or any repeat jobs (possibly in the workshop).\n" - " protect-job remove [all]\n" - " Unprotect the selected job, or any repeat jobs (possibly in the workshop).\n" + "workflow", "Manage control of repeat jobs.", + workflow_cmd, false, + " workflow enable\n" + " workflow disable\n" + " Enable or disable the plugin.\n" + " workflow list-jobs\n" + " List workflow-controlled jobs (if in a workshop, filtered by it).\n" + "Function:\n" + " When the plugin is enabled, it protects all repeat jobs from removal.\n" + " If they do disappear due to any cause, they are immediately re-added\n" + " to their workshop and suspended.\n" ) ); } @@ -130,20 +133,35 @@ struct ProtectedJob { } }; -PersistentDataItem config; -static std::vector protected_cfg; +/*******************************/ + +static bool enabled = false; +static PersistentDataItem config; + +enum ConfigFlags { + CF_ENABLED = 1 +}; typedef std::map TKnownJobs; static TKnownJobs known_jobs; static std::vector pending_recover; +/*******************************/ + static ProtectedJob *get_known(int id) { TKnownJobs::iterator it = known_jobs.find(id); return (it != known_jobs.end()) ? it->second : NULL; } +static bool isSupportedJob(df::job *job) +{ + return job->misc_links.empty() && + !job->job_items.empty() && + getJobHolder(job); +} + static void enumLiveJobs(std::map &rv) { df::job_list_link *p = world->job_list.next; @@ -151,129 +169,74 @@ static void enumLiveJobs(std::map &rv) rv[p->item->id] = p->item; } -static void cleanup_state(Core *) -{ - config = PersistentDataItem(); - protected_cfg.clear(); +/*******************************/ +static void stop_protect(Core *c) +{ pending_recover.clear(); + if (!known_jobs.empty()) + c->con.print("Unprotecting %d jobs.\n", known_jobs.size()); + for (TKnownJobs::iterator it = known_jobs.begin(); it != known_jobs.end(); ++it) delete it->second; known_jobs.clear(); } -static void init_state(Core *c) +static void cleanup_state(Core *c) { - config = c->getWorld()->GetPersistentData("workflow/config"); - c->getWorld()->GetPersistentData(&protected_cfg, "workflow/protected-jobs"); - - std::map jobs; - enumLiveJobs(jobs); + config = PersistentDataItem(); - for (unsigned i = 0; i < protected_cfg.size(); i++) - { - PersistentDataItem &item = protected_cfg[i]; - for (int j = 0; j < PersistentDataItem::NumInts; j++) - { - int id = item.ival(j); - if (id <= 0) - continue; + stop_protect(c); +} - if (get_known(id)) // duplicate - { - item.ival(j) = -1; - continue; - } +static bool check_lost_jobs(Core *c); - df::job *job = jobs[id]; - if (!job) - { - c->con.printerr("Protected job lost: %d\n", id); - item.ival(j) = -1; - continue; - } +static void start_protect(Core *c) +{ + check_lost_jobs(c); - if (!job->misc_links.empty() || job->job_items.empty()) - { - c->con.printerr("Protected job unsupported: %d (%s)\n", - id, ENUM_KEY_STR(job_type, job->job_type)); - item.ival(j) = -1; - continue; - } + if (!known_jobs.empty()) + c->con.print("Protecting %d jobs.\n", known_jobs.size()); +} - ProtectedJob *pj = new ProtectedJob(job); - if (!pj->holder) - { - c->con.printerr("Protected job not in building: %d (%s)\n", - id, ENUM_KEY_STR(job_type, job->job_type)); - delete pj; - item.ival(j) = -1; - continue; - } +static void init_state(Core *c) +{ + config = c->getWorld()->GetPersistentData("workflow/config"); - known_jobs[id] = pj; + enabled = config.isValid() && config.ival(0) != -1 && + (config.ival(0) & CF_ENABLED); - if (!job->flags.bits.repeat) { - c->con.printerr("Protected job not repeating: %d\n", id); - job->flags.bits.repeat = true; - } - } - } + if (!enabled) + return; - if (!known_jobs.empty()) - c->con.print("Protecting %d jobs.\n", known_jobs.size()); + start_protect(c); } -static int *find_protected_id_slot(Core *c, int key) +static void enable_plugin(Core *c) { - for (unsigned i = 0; i < protected_cfg.size(); i++) + if (!config.isValid()) { - PersistentDataItem &item = protected_cfg[i]; - for (int j = 0; j < PersistentDataItem::NumInts; j++) - { - if (item.ival(j) == key) - return &item.ival(j); - } + config = c->getWorld()->AddPersistentData("workflow/config"); + config.ival(0) = 0; } - if (key == -1) { - protected_cfg.push_back(c->getWorld()->AddPersistentData("workflow/protected-jobs")); - PersistentDataItem &item = protected_cfg.back(); - return &item.ival(0); - } + config.ival(0) |= CF_ENABLED; + enabled = true; + c->con << "Enabling the plugin." << endl; - return NULL; + start_protect(c); } +/*******************************/ + static void forget_job(Core *c, ProtectedJob *pj) { known_jobs.erase(pj->id); - - if (int *p = find_protected_id_slot(c, pj->id)) - *p = -1; - delete pj; } -static void remember_job(Core *c, df::job *job) -{ - if (get_known(job->id)) - return; - - if (!job->misc_links.empty() || job->job_items.empty()) - { - c->con.printerr("Unsupported job type: %d (%s)\n", - job->id, ENUM_KEY_STR(job_type, job->job_type)); - return; - } - - known_jobs[job->id] = new ProtectedJob(job); - - *find_protected_id_slot(c, -1) = job->id; -} - static bool recover_job(Core *c, ProtectedJob *pj) { // Check that the building exists @@ -305,37 +268,22 @@ static bool recover_job(Core *c, ProtectedJob *pj) return false; } - // Find the position in the job list - df::job_list_link *ins_pos = &world->job_list; - while (ins_pos->next && ins_pos->next->item->id < pj->id) - ins_pos = ins_pos->next; + // Create and link in the actual job structure + df::job *recovered = cloneJobStruct(pj->job_copy); + + recovered->flags.bits.repeat = true; + recovered->flags.bits.suspend = true; - if (ins_pos->next && ins_pos->next->item->id == pj->id) + if (!linkJobIntoWorld(recovered, false)) // reuse same id { + deleteJobStruct(recovered); + c->con.printerr("Inconsistency: job %d (%s) already in list.", pj->id, ENUM_KEY_STR(job_type, pj->job_copy->job_type)); pj->live = true; return true; } - // Create the actual job structure - df::job *recovered = cloneJobStruct(pj->job_copy); - - recovered->flags.bits.repeat = true; - recovered->flags.bits.suspend = true; - - // Link the job into the global list - df::job_list_link *link = new df::job_list_link(); - recovered->list_link = link; - - link->item = recovered; - link->next = ins_pos->next; - if (ins_pos->next) - ins_pos->next->prev = link; - link->prev = ins_pos; - ins_pos->next = link; - - // Add to building jobs pj->holder->jobs.push_back(recovered); // Done @@ -343,7 +291,7 @@ static bool recover_job(Core *c, ProtectedJob *pj) return true; } -static void check_lost_jobs(Core *c) +static bool check_lost_jobs(Core *c) { static int check = 1; check++; @@ -351,15 +299,27 @@ static void check_lost_jobs(Core *c) df::job_list_link *p = world->job_list.next; for (; p; p = p->next) { - ProtectedJob *pj = get_known(p->item->id); - if (!pj) - continue; - pj->check_idx = check; + df::job *job = p->item; - // force repeat - p->item->flags.bits.repeat = true; + ProtectedJob *pj = get_known(job->id); + if (pj) + { + if (!job->flags.bits.repeat) + forget_job(c, pj); + else + pj->check_idx = check; + } + else if (job->flags.bits.repeat && isSupportedJob(job)) + { + pj = new ProtectedJob(job); + assert(pj->holder); + known_jobs[pj->id] = pj; + pj->check_idx = check; + } } + bool any_lost = false; + for (TKnownJobs::const_iterator it = known_jobs.begin(); it != known_jobs.end(); ++it) { if (it->second->check_idx == check || !it->second->live) @@ -367,7 +327,10 @@ static void check_lost_jobs(Core *c) it->second->live = false; pending_recover.push_back(it->second); + any_lost = true; } + + return any_lost; } static void update_job_data(Core *c) @@ -382,21 +345,30 @@ static void update_job_data(Core *c) } } +static void recover_jobs(Core *c) +{ + for (int i = pending_recover.size()-1; i >= 0; i--) + if (recover_job(c, pending_recover[i])) + vector_erase_at(pending_recover, i); +} + DFhackCExport command_result plugin_onupdate(Core* c) { - if (known_jobs.empty()) + if (!enabled) return CR_OK; static unsigned cnt = 0; cnt++; - if ((cnt % 10) == 0) + bool force_recover = false; + if ((cnt % 5) == 0) { - for (int i = pending_recover.size()-1; i >= 0; i--) - if (recover_job(c, pending_recover[i])) - vector_erase_at(pending_recover, i); + force_recover = check_lost_jobs(c); + } - check_lost_jobs(c); + if (force_recover || (cnt % 50) == 0) + { + recover_jobs(c); } if ((cnt % 500) == 0) @@ -405,10 +377,12 @@ DFhackCExport command_result plugin_onupdate(Core* c) return CR_OK; } -static command_result protect_job(Core *c, vector & parameters) +static command_result workflow_cmd(Core *c, vector & parameters) { CoreSuspender suspend(c); + update_job_data(c); + if (parameters.empty()) return CR_WRONG_USAGE; @@ -424,10 +398,38 @@ static command_result protect_job(Core *c, vector & parameters) std::map jobs; enumLiveJobs(jobs); - update_job_data(c); std::string cmd = parameters[0]; - if (cmd == "list") + + if (cmd == "enable") + { + if (enabled) + { + c->con << "The plugin is already enabled." << endl; + return CR_OK; + } + + enable_plugin(c); + return CR_OK; + } + else if (cmd == "disable") + { + if (!enabled) + { + c->con << "The plugin is already disabled." << endl; + return CR_OK; + } + + enabled = false; + config.ival(0) &= ~CF_ENABLED; + stop_protect(c); + return CR_OK; + } + + if (!enabled) + c->con << "Note: the plugin is not enabled." << endl; + + if (cmd == "list-jobs") { if (workshop) { @@ -457,65 +459,8 @@ static command_result protect_job(Core *c, vector & parameters) printJobDetails(c, pending_recover[i]->job_copy); } } - } - else if (cmd == "add" || cmd == "remove") - { - bool add = (cmd == "add"); - bool all = (parameters.size() >= 2 && parameters[1] == "all"); - if (parameters.size() >= 2 && !all) - return CR_WRONG_USAGE; - - if (workshop && all) - { - for (unsigned i = 0; i < workshop->jobs.size(); i++) - { - df::job *job = workshop->jobs[i]; - if (add) - { - if (!job->flags.bits.repeat) - continue; - remember_job(c, job); - } - else - { - if (ProtectedJob *pj = get_known(job->id)) - forget_job(c, pj); - } - } - } - else if (workshop) - { - if (!job) { - c->con.printerr("No job is selected in the current building.\n"); - return CR_FAILURE; - } - - if (add) - remember_job(c, job); - else if (ProtectedJob *pj = get_known(job->id)) - forget_job(c, pj); - } - else - { - if (!all) { - c->con.printerr("Please either select a job, or specify 'all'.\n"); - return CR_WRONG_USAGE; - } - if (add) - { - for (std::map::iterator it = jobs.begin(); it != jobs.end(); it++) - if (it->second->flags.bits.repeat) - remember_job(c, it->second); - } - else - { - pending_recover.clear(); - - while (!known_jobs.empty()) - forget_job(c, known_jobs.begin()->second); - } - } + return CR_OK; } else return CR_WRONG_USAGE;