Skip to content

Fix: quickfort UI freeze on large blueprints and strip undocumented ghost-features#1597

Open
sizzlins wants to merge 1 commit into
DFHack:masterfrom
sizzlins:fix/quickfort-lag-and-ghost-code
Open

Fix: quickfort UI freeze on large blueprints and strip undocumented ghost-features#1597
sizzlins wants to merge 1 commit into
DFHack:masterfrom
sizzlins:fix/quickfort-lag-and-ghost-code

Conversation

@sizzlins

@sizzlins sizzlins commented Jun 26, 2026

Copy link
Copy Markdown

Title: Fix: quickfort UI freeze on massive blueprints and strip undocumented ghost-features

This PR addresses severe UI lag when selecting massive blueprints (e.g. industry2) in the gui/quickfort overlay, and removes broken, undocumented ghost-code from the core quickfort.lua parser.

Fixed massive UI render lag
onRenderFrame was synchronously re-parsing the blueprint CSV and reevaluating thousands of matrix tiles 60 times a second whenever the mouse moved. This completely froze the game engine.

  • Decoupled the rendering loop from the CSV parsing logic.
  • Implemented a true 50ms backend throttle to ensure the DF engine always gets processing time between blueprint recalculations.
  • Added a visual coordinate shift (dx, dy, dz) to the cached preview tiles. The blueprint now perfectly tracks the mouse at a smooth 60 FPS while the heavy collision math runs asynchronously in the background.

Removed argparse.stringList
The action argument was previously being run through argparse.stringList(action). This undocumented feature bypassed the safety mechanism (not dfhack.isMapLoaded()) because the literal action string check (action == 'run') fails if the string is "run,orders".

  • Removed the implementation to enforce single-action processing, fixing the safety bypass and adhering to standard quickfort usage documentation (YAGNI).

Removed reduce_transform()
The UI had a 25-line mathematical reduce_transform() block dedicated purely to minimizing the length of a UI display text label when a user hit the rotate hotkey multiple times.

  • Deleted the function to simplify the UI loop. transformations now behaves as a standard array.

  • Updated SELECT_ALL to SEC_SELECT as the 51.01 transition has been stable for multiple major releases.

@ab9rf

ab9rf commented Jun 26, 2026

Copy link
Copy Markdown
Member

This PR seems to combine two unrelated changes, one related to quickfort and one implementing some sort of manual-save feature, possibly the same as the one in #1583.

Please don't bundle unrelated changes into a single PR.

@sizzlins sizzlins force-pushed the fix/quickfort-lag-and-ghost-code branch 5 times, most recently from 292b40a to e6ab51d Compare June 26, 2026 16:46
@sizzlins sizzlins force-pushed the fix/quickfort-lag-and-ghost-code branch from e6ab51d to ad9eac2 Compare June 26, 2026 17:35

@SilasD SilasD left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the code for for the main feature of the PR, the delayed-refresh looks kind of reasonable, I guess? some odd idioms.

thing is, I don't see much difference between refreshing the preview every time the mouse cursor enters a different tile or waiting 50ms and then refreshing. yes, the mouse cursor can move multiple tiles in 50ms, but only if the blueprint is dragged around very quickly.

my gut feeling is that it doesn't really matter whether the CSV is fully reparsed now or fully reparsed in 50 ms, it's going to be fully reparsed anyway. and probably this happens on every tile the mouse cursor moves onto.

if you want to speed up dragging around a blueprint, targetting that parsing seems like the path to take. something as simple as saving an intermediate representation could do a lot of good.

but it feels like a tempest in a teapot.

for the other changes, they seem to be a TODO implemented without understanding the consequences, deletion of beneficial code, and a "fix" of "undocumented ghost-features" which actually breaks a documented and working feature.

recommend reject, of course.

and I gave this well over an hour of my time. now I see why professionals are auto-rejecting anything that smacks of "AI slop".

Comment thread gui/quickfort.lua
widgets.HotkeyLabel{
frame={b=0, l=15},
key='SELECT_ALL', -- TODO: change to SEC_SELECT once 51.01 is stable
key='SEC_SELECT',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has this been tested? can you explain what key sequence or mouse button presses this would change?

it's not enough to blithely say, oh yeah, we're way past 51.01 so let's just perform the TODO while we're in here.

SEC_SELECT showed up as a keybinding/mousebinding between 50.15 and 51.02. what is it? is SELECT_ALL deprecated now? what is being changed here?

should this be merged? I'm honestly not sure. I don't think it would hurt, but I don't know that there's a reason to do it.

Comment thread quickfort.lua
args.commands = {action}

local action_fn = action_switch[args.commands[1]]
local action_fn = action_switch[action]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the issue this purports to fix is real, although I don't know if it's severe enough to warrant a fix.

this proposed fix is wrong; it removes a documented capability for no good reason.

"quickfort <command>[,<command>...] <list_id>[,<list_id>...] [<options>]"
   Applies the blueprint(s) with the id number(s) reported from the "list" command.

"quickfort <command>[,<command>...] <filename> [-n|--name <name>[,<name>...]] [<options>]"
   Applies a blueprint in the specified file.   {{ details removed, not relevant }}

quickfort is designed to accept multiple comma-separated commands. this proposed fix removes that capability.

I recommend against merging this particular change.

Edit: the fact that this capability is intentional and documented speaks against the title of this PR. "strip undocumented ghost-features". no.

Comment thread gui/quickfort.lua
Comment on lines 547 to 550

local origin, test_point = {x=0, y=0}, {x=1, y=-2}
local minimal_sequence = {
['x=1, y=-2'] = {},
['x=2, y=-1'] = {'cw', 'flipv'},
['x=2, y=1'] = {'cw'},
['x=1, y=2'] = {'flipv'},
['x=-1, y=2'] = {'cw', 'cw'},
['x=-2, y=1'] = {'ccw', 'flipv'},
['x=-2, y=-1'] = {'ccw'},
['x=-1, y=-2'] = {'fliph'}
}

-- reduces the list of transformations to a minimal sequence
local function reduce_transform(elements)
local pos = test_point
for _,elem in ipairs(elements) do
pos = quickfort_transform.make_transform_fn_from_name(elem)(pos, origin)
end
local ret = quickfort_transform.resolve_vector(pos, minimal_sequence)
if #ret == #elements then
-- if we're not making the sequence any shorter, prefer the existing set
return elements
end
return copyall(ret)
end

function Quickfort:on_transform(val)
table.insert(transformations, val)
transformations = reduce_transform(transformations)
self:updateLayout()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any reason to remove this code. without it, the UI Label would grow without bound as the user applies multiple transformations.

this code is beneficial, and it only runs when the user applies a transformation, so it doesn't impact the performance per the stated intent of the PR.

Comment thread gui/quickfort.lua
Comment on lines +668 to +670
local now = os.clock()
-- wait at least 0.05s (50ms) between heavy recalculations
if not self.last_refresh_time or (now - self.last_refresh_time > 0.05) then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd prefer this be based on frames instead of wall clock. ticks are of course not relevant, because ZScreen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants