DFHack / dfhack

Memory hacking library for Dwarf Fortress and a set of tools that use it
Other
1.87k stars 475 forks source link

Feature: Fix/ownership #4373

Closed LightHardt closed 7 months ago

LightHardt commented 7 months ago

Have started running into an issue where multiple dwarves are claiming the same clothing items.

Dwarf 1 attempting to store item in cabinet that it believes it owns (it is stuck repeatedly attempting this) image

Who the item believes it belongs to (Dwarf 2) image

Dwarf 2 who also claims the item as their own image

Potential idea loop through all dwarves owned_items vector and every time a duplicate is found remove duplicate id. item::setOwner appears to be a good function to use for this. Any thoughts or input is appreciated on this.

myk002 commented 7 months ago

yes, a dfhack.items.setOwner(item, nil) followed by a dfhack.items.setOwner(item, unit) for the unit that should keep ownership looks like it would do it. Maybe an algorithm like:

local owners = {}
for _,unit in ipairs(dfhack.units.getCitizens()) do
    for _,item in ipairs(unit.owned_items) do
        local owner = owners[item.id]
        if owner then
            dfhack.items.setOwner(item)
            dfhack.items.setOwner(item, owner)
        else
            owners[item.id] = unit
        end
    end
end

maybe it could be smarter and specifically assign ownership to the unit that already has the item in inventory.

A fix tool like this could also keep an eye on squad equipment and do the same for multiple squad members who have claimed the same item for their uniform. That code is already written in uniform-unstick, but it could be refactored to be callable by this fixer tool too.

LightHardt commented 7 months ago

Oh wow thanks for the algo ill test it out here soon as I seemingly have a couple dwarves in this predicament right now. I will also look into uniform-unstick.

LightHardt commented 7 months ago

After more exploration it seems the problem (or at least part of it) lies with for some reason when a dwarf unclaims the item it deletes the ref but it doesn't delete the item id from the dwarfs owned_items vector. This allows other dwarfs to just grab it and claim it for themselves now causing the item to have multiple "owners" when in actuality i believe the one that has the ref (sometimes neither do) should be recognized as the real owner. The only thing i can think would cause this is tailor however looking at the code it seems to properly clean up the ref and remove the item id from the dwarf. This leads me to believe it might be buggy vanilla behavior?

With all that out the way with this knowledge i made some modifications and am curious on thought/opinions on code and mentioned behavior.

for _,unit in ipairs(dfhack.units.getCitizens()) do
    for index = #unit.owned_items-1, 0, -1 do
        local item = df.item.find(unit.owned_items[index])
        if not item then goto continue end
        for _, ref in ipairs(item.general_refs) do
            if df.general_ref_unit_itemownerst:is_instance(ref) then
                -- make sure the ref belongs to unit
                if ref.unit_id == unit.id then goto continue end
            end
        end
        print('Erasing ' .. dfhack.TranslateName(unit.name) .. ' claim on item #' .. item.id)
        unit.owned_items:erase(index)
        ::continue::
    end
end