-
Notifications
You must be signed in to change notification settings - Fork 207
workorder-detail-fix: Enforce order item details for buggy job types #929
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 27 commits
c1960e0
0ac190c
152fe5b
4e42bd8
323121d
08a56b0
b4b2518
9635fab
5c643b7
69a5e1f
e52adbe
b30668f
fae0027
45f87de
47761b6
e1f170e
ea76f59
727200b
39a4db4
a4ce456
8ad4f77
a5c2cc9
4e950dd
a39ec2e
d6b0775
f6275f0
384625d
3cff781
b0146e7
f504edc
027f410
b7c0ce8
4ca5a42
f961c8f
fa308ab
e584bba
fcc8546
fea88ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
fix/workorder-detail-fix | ||
======================== | ||
|
||
.. dfhack-tool:: | ||
:summary: Fixes a bug with modified work orders creating incorrect jobs. | ||
:tags: fort bugfix workorders | ||
|
||
Some work order jobs have a bug when their input item details have been modified. | ||
|
||
Example 1: a Stud With Iron order, modified to stud a cabinet, instead creates a job to stud any furniture. | ||
|
||
Example 2: a Prepare Meal order, modified to use all plant type ingredients, instead creates a job to use any ingredients. | ||
|
||
This fix forces these jobs to properly inherit the item details from their work order. | ||
|
||
Usage | ||
----- | ||
|
||
``fix/workorder-detail-fix enable`` | ||
enables the fix | ||
``fix/workorder-detail-fix disable`` | ||
disables the fix | ||
``fix/workorder-detail-fix status`` | ||
print fix status | ||
flashy-man marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
--@enable = true | ||
--@module = true | ||
local repeatutil = require 'repeat-util' | ||
local utils = require 'utils' | ||
local script_name = "workorder-detail-fix" | ||
local schedule_key = script_name..":dispatch" | ||
|
||
enabled = enabled or false -- enabled API | ||
function isEnabled() return enabled end | ||
|
||
local managers = df.global.plotinfo.main.fortress_entity.assignments_by_type.MANAGE_PRODUCTION | ||
if not managers then error("NO MANAGERS VECTOR!") end | ||
local last_job_id = -1 | ||
local jobs_corrected = 0 | ||
|
||
-- all jobs with the NONE (-1) type in its default job_items may be a problem | ||
local offending_jobs = { | ||
flashy-man marked this conversation as resolved.
Show resolved
Hide resolved
|
||
[df.job_type.EncrustWithGems] = true, | ||
[df.job_type.EncrustWithGlass] = true, | ||
[df.job_type.StudWith] = true, | ||
[df.job_type.PrepareMeal] = true, | ||
[df.job_type.DecorateWith] = true, | ||
[df.job_type.SewImage] = true, | ||
-- list may be incomplete | ||
} | ||
|
||
-- copy order.item fields/flags over to job's job_item | ||
-- only the essentials: stuff that is editable via gui/job-details | ||
local function correct_item_details(job_item, order_item) | ||
local fields = {'item_type', 'item_subtype', 'mat_type', 'mat_index'} | ||
for _, field in pairs(fields) do | ||
flashy-man marked this conversation as resolved.
Show resolved
Hide resolved
|
||
job_item[field] = order_item[field] | ||
end | ||
|
||
local flags_names = {'flags1', 'flags2', 'flags3', 'flags4', 'flags5'} | ||
for _, flags in pairs(flags_names) do | ||
local order_flags = order_item[flags] | ||
if type(order_flags) == 'number' then | ||
job_item[flags] = order_flags | ||
else | ||
job_item[flags].whole = order_flags.whole | ||
end | ||
end | ||
end | ||
|
||
-- correct job's job_items to match the order's | ||
local function enforce_order_details(job, order) | ||
if not job.job_items then return end -- never happens (error here?) | ||
local modified = false | ||
for idx, job_item in ipairs(job.job_items) do | ||
local order_item = order.items[idx] | ||
if not order_item then break end -- never happens (error here?) | ||
if job_item.item_type ~= order_item.item_type then | ||
-- dfhack's isSuitableItem function will allow the orders we want, | ||
-- but disallow insane combinations like meals made of shoes | ||
local suitable = dfhack.job.isSuitableItem( | ||
job_item, order_item.item_type, order_item.item_subtype ) | ||
if suitable then | ||
correct_item_details(job_item, order_item) | ||
modified = true | ||
else --[[ error on unsuitable item?]] end | ||
end | ||
end | ||
if modified then jobs_corrected = jobs_corrected + 1 end | ||
end | ||
|
||
-- check orders list to see if fix is needed | ||
-- yields id-order table for the orders with issues, or nil when no issues | ||
local PrepareMeal = df.job_type.PrepareMeal | ||
local SewImage = df.job_type.SewImage | ||
local NONE = df.job_type.NONE | ||
local function get_bugged_orders() | ||
local orders = {} | ||
local num_bugged, improve_item_index, item = 0, 1, nil | ||
for _, order in ipairs(df.global.world.manager_orders) do | ||
if not order.items then goto nextorder end | ||
local order_job_type = order.job_type | ||
if not offending_jobs[order_job_type] then goto nextorder end | ||
if #order.items == 0 then goto nextorder end -- doesn't happen | ||
|
||
-- for PrepareMeal jobs, any one of the items could be an issue. | ||
if order_job_type == PrepareMeal then | ||
for _, _item in ipairs(order.items) do | ||
if _item.item_type ~= NONE then goto fix end | ||
end | ||
goto nextorder | ||
end | ||
|
||
-- All other types are improve jobs; only the improved item is checked | ||
-- Only SewImage has the item-to-improve at items[0] | ||
improve_item_index = (order_job_type ~= SewImage) and 1 or 0 | ||
item = order.items[improve_item_index] | ||
flashy-man marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if not item then goto nextorder end -- error here? | ||
if item.item_type == NONE then goto nextorder end | ||
:: fix :: | ||
num_bugged = num_bugged + 1 | ||
orders[order.id] = order | ||
:: nextorder :: | ||
end | ||
if num_bugged == 0 then return nil end | ||
return orders | ||
end | ||
|
||
-- correct newly dispatched work order jobs | ||
local disable | ||
local function on_dispatch_tick() | ||
if df.global.cur_year_tick % 150 ~= 30 then | ||
print(script_name.." desynced from dispatch tick") | ||
repeatutil.cancel(schedule_key) | ||
disable(true) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is a fatal error. we could regularly get desynchronized because of tools like jobs that have already started to gather items can be skipped (and we can document that this script is not infallible) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. got it re-syncing now. Surprisingly the fix works with Also, the already-gathering-items "fix" is gone for now because I got a couple crashes (!), either relating to the general refs or the |
||
end | ||
if #managers == 0 then return end | ||
flashy-man marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if df.global.plotinfo.manager_timer ~= 10 then return end | ||
local orders = get_bugged_orders() | ||
if not orders then return end -- no bugs to fix | ||
local highest = last_job_id | ||
for _, job in utils.listpairs(df.global.world.jobs.list) do | ||
if job.id <= last_job_id then goto nextjob end | ||
flashy-man marked this conversation as resolved.
Show resolved
Hide resolved
|
||
highest = math.max(job.id, highest) | ||
if job.order_id == -1 then goto nextjob end | ||
local order = orders[job.order_id] | ||
if not order then goto nextjob end -- order wasn't bugged | ||
-- job in progress: only happens on the first run. | ||
-- experimental fix: remove the items and un-task them | ||
while #job.items ~= 0 do | ||
job.items[0].item.flags.in_job = false | ||
job.items:erase(0) | ||
end | ||
flashy-man marked this conversation as resolved.
Show resolved
Hide resolved
|
||
enforce_order_details(job, order) | ||
:: nextjob :: | ||
end | ||
last_job_id = highest | ||
end | ||
|
||
timeout_id = timeout_id or nil | ||
local function schedule_handler() | ||
if repeatutil.cancel(schedule_key) then | ||
print(script_name..": canceled old dispatch handler") | ||
end | ||
flashy-man marked this conversation as resolved.
Show resolved
Hide resolved
|
||
repeatutil.scheduleEvery(schedule_key, 150, 'ticks', on_dispatch_tick) | ||
timeout_id = nil | ||
end | ||
|
||
local function enable(yell) | ||
local manager_timer = df.global.plotinfo.manager_timer | ||
local d = df.global.cur_year_tick | ||
-- it's a potential dispatch tick when tick % 150 == 30 | ||
local time_until = (30 - d) % 150 -- + manager_timer * 150 | ||
if time_until == 0 then | ||
schedule_handler() | ||
else | ||
timeout_id = dfhack.timeout(time_until, 'ticks', schedule_handler) | ||
end | ||
enabled = true | ||
if yell then print(script_name.." ENABLED") end | ||
end | ||
|
||
function disable(yell) | ||
if timeout_id then | ||
dfhack.timeout_active(timeout_id, nil) | ||
timeout_id = nil | ||
end | ||
repeatutil.cancel(schedule_key) | ||
enabled = false | ||
if yell then print(script_name.." DISABLED") end | ||
end | ||
|
||
-- (not working with enabled API, probably something to do with module mode) | ||
local function status() | ||
local status = "DISABLED" or enabled and "ENABLED" | ||
flashy-man marked this conversation as resolved.
Show resolved
Hide resolved
|
||
print(script_name.." status: "..status.." # jobs corrected: "..tostring(jobs_corrected)) | ||
end | ||
|
||
-- check if script was called by enable API | ||
if dfhack_flags.enable then | ||
if dfhack_flags.enable_state then | ||
enable(false) | ||
else | ||
disable(false) | ||
end | ||
return | ||
end | ||
|
||
if dfhack_flags.module then return end | ||
|
||
-- check the arguments | ||
local args={...} | ||
|
||
if not args[1] then | ||
flashy-man marked this conversation as resolved.
Show resolved
Hide resolved
|
||
print(script_name.." valid cmds: enable, disable, status") | ||
return | ||
end | ||
|
||
local cmd_table = { ['enable']=enable, ['disable']=disable, ['status']=status } | ||
|
||
local cmd = cmd_table[args[1]:lower()] | ||
if cmd then cmd(true) else | ||
print(script_name.." valid cmds: enable, disable, status") | ||
end |
Uh oh!
There was an error while loading. Please reload this page.