Skip to content

Fix state inconsistency in uniform-unstick #1490

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

Jarkami
Copy link

@Jarkami Jarkami commented Aug 13, 2025

@Jarkami
Copy link
Author

Jarkami commented Aug 13, 2025

May need additional handling for items that have been offloaded to save/unit-#.dat files; we could either remove them from assigned items (my preference) or add some handling to differentiate between an item that isn't assigned and an item that is assigned but just currently doesn't exist in the block updating equipment.assigned_items.

Copy link
Contributor

@SilasD SilasD left a comment

Choose a reason for hiding this comment

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

This fixes the bug you're targeting; it's fine.

I still think we should totally construct a copy of the data from scratch, and compare with the existing vector, but I know that's extra work for (probably) no gain.

I just see three possibilities for corruption:

  • values that shouldn't be in this vector. You're fixing that.
  • values that should be in the vector but aren't. I haven't probed for this, but it's probably not happening.
  • the vector itself not being sorted. I did check two forts with extensive military (in 0.47.05 and 0.51.*), and didn't find any cases of this.

I suppose this is just nitpicking. So is my line-by-line; there's nothing that needs changed.

@@ -196,6 +196,22 @@ local function process(unit, args, need_newline)
end
end

-- Make the equipment.assigned_items list consistent with what is present in equipment.uniform
for i=#(squad_position.equipment.assigned_items)-1,0,-1 do
local u_id = squad_position.equipment.assigned_items[i]
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess I'll just say that I don't like the variable name u_id. I would probably use item_id or aitem_id instead.

u_id is worse than meaningless, it's misleading. It makes me think it's a unit.id instead of an item.id.

I know it's used above, but even considering that, you're using it for a slightly different meaning. Above it means that an item is in the assigned_items hash key/value map, and therefore is in the uniform nested structure; here it means each item in the assigned_items vector.

I mean, it works, it's just not as clear as it could be. And this script really needs some clarity.

Copy link
Member

Choose a reason for hiding this comment

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

I agree that u_id is a poor name for this variable. Would suggest uniform_item_id or something along those lines. Terseness is not a virtue. Feel free to break the if statement on line 203 over multiple lines.

Copy link
Contributor

@SilasD SilasD left a comment

Choose a reason for hiding this comment

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

I saw some inconsistencies in API use, but they're harmless.

Really, the abomination that is need_newline = print_line(string, need_newline) needs to be fixed. I'm willing to tackle that, as soon as this PR is accepted.

else
if dfhack.items.moveToGround(item, pos) then
print("Dropped item #" .. id .. " '" .. item_description(item) .. "'")
print("Dropped " .. item_description(item))
Copy link
Contributor

Choose a reason for hiding this comment

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

This should use the need_newline = print_line() abomination, should it not?

EDIT: no, it makes no difference here. That's gonna be a rant by itself.

Comment on lines 207 to 208
need_newline = print_line(unit_name .. " has an improperly assigned item, item # " .. u_id .. " '" .. item_description(item) .. "'; removing it")
need_newline = print_line(unit_name .. " has an improperly assigned item, " .. item_description(item) .. '; removing it')
else
need_newline = print_line(unit_name .. " has a nonexistent item assigned, item # " .. u_id .. "; removing it")
need_newline = print_line(unit_name .. " has a nonexistent item assigned, item # " .. u_id .. '; removing it')
Copy link
Contributor

Choose a reason for hiding this comment

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

You're not passing in need_newline to print_line(). That's harmless; it tests nil instead of false, but it's inconsistent with the API. I'm not sure it's worth fixing.

You changed from double to single quotes; I'm not sure why. Everywhere else uses double quotes. Harmless.

Comment on lines 18 to 20
local function item_description(item)
return dfhack.df2console(dfhack.items.getDescription(item, 0, true))
return "item #" .. item.id .. " '" .. dfhack.df2console(dfhack.items.getDescription(item, 0, true)) .. "'"
end
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider extending item_description() to gracefully handle the case of a nonexistent item.

Copy link
Member

Choose a reason for hiding this comment

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

I'm unclear on what you mean here by the "case of a nonexistent item"

Copy link
Contributor

Choose a reason for hiding this comment

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

The case handled on line 208. Literally df.item.find() returned nil.

Copy link
Member

Choose a reason for hiding this comment

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

there is no "valid" response to being asked to describe a null item; the correct response is for the function to throw, which is what it currently does

callers are expected to check for nil or be prepared to accept a throw if they do not

Copy link
Member

Choose a reason for hiding this comment

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

I did a cursory scan through the code and I think item will never be null on calls to this function, but I am not at all sure of that. If you're confident that this code will never be called with item as null, it's enough to document that item must never be null. Otherwise, add a test for item being null to this code and provide an appropriate alternative. Otherwise, the script will abort with an exception, which will generally be confusing to users (as most people never look at the console and thus won't see the exception).

@Jarkami
Copy link
Author

Jarkami commented Aug 17, 2025

Did a quick readability pass on the script as a whole. print_line()/need_newline is gone, some variable names were changed for clarity, plus a couple more small rearrangements where I felt things were extra confusing. Actual behavior should not be affected by any of these changes.

Ultimately with how many different structures this script is pulling from and comparing I'm not sure it's ever going to reach fantastic readability, but hopefully this is at least a decent step in the right direction.

@Jarkami Jarkami requested a review from SilasD August 17, 2025 06:26
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.

[bug] Military squad positions assigned multiple items after detect/resolve conflicts (uniform-unstick.lua)
3 participants