I have stumpled upon a really weird bug in the Lua API: If you use contains_item
to count tools in an inventory, you can not reliably count the number of tools of the same kind.
I give you an example:
Get yourselves 4 water buckets (which have max. stack size 1) and 4 wooden shovels (also max. stack size 1). Use the luacmd
mod to test the contains_item
method:
/lua print(me:get_inventory():contains_item("main", "bucket:bucket_water"))
→ true
. OK.
/lua print(me:get_inventory():contains_item("main", "bucket:bucket_water 4"))
→ true
. OK.
/lua print(me:get_inventory():contains_item("main", "bucket:bucket_water 5"))
→ false
. OK.
/lua print(me:get_inventory():contains_item("main", "default:shovel_wood"))
→ true
. OK.
/lua print(me:get_inventory():contains_item("main", "default:shovel_wood 4"))
→ true
. OK.
/lua print(me:get_inventory():contains_item("main", "default:shovel_wood 5"))
→ true
. Unexpected!
/lua print(me:get_inventory():contains_item("main", "default:shovel_wood 5000"))
→ true
. Unexpected!
So, contains_item
is able to count the water buckets correctly in the inventory, but not the wooden shovels. In otther words, contains_item
is broken.
In my example, the difference between water bucket and wooden shovel seems to be related to the fact that the wooden shovel is created with register_tool
but the water bucket is created with register_craftitem
.
Seen in Minetest 0.4.14 and revision 986d70ccecb09fcbd247758643a4d623c9c4e1b8.
Bug traceback:
inv:contains_item
: https://github.com/minetest/minetest/blob/master/src/script/lua_api/l_inventory.cpp#L335
read_item
: https://github.com/minetest/minetest/blob/master/src/script/common/c_content.cpp#L797
ItemStack::deSerialize
: https://github.com/minetest/minetest/blob/master/src/inventory.cpp#L305
The problem here is quite complex because the deserialize function is also the one that disallows you from having 5 shovels in one slot, where 5000 cobble is no problem (using /giveme ..
).
It was I who originally wrote the ItemStack
deserialization, but now I think that the engine (and builtin) should not enforce a stack_max
of 1 for tools at all. Instead it should allow mods and subgames to have stackable tools if they want to simply by overriding stack_max
.
Stackable tools could work like in e.g. Junk Jack: if a tool with item count greater than 1 wears out, the count is decremented by one and wear is reset to 0, basically the worn out tool is replaced by a full-durability tool from the stack. So, a tool with item count C can be thought of as one currently active tool plus (C-1) full-durability backup tools.
Of course there would have to be other considerations, such as how add_item
would deal with such stacks.
Most helpful comment
It was I who originally wrote the
ItemStack
deserialization, but now I think that the engine (and builtin) should not enforce astack_max
of 1 for tools at all. Instead it should allow mods and subgames to have stackable tools if they want to simply by overridingstack_max
.Stackable tools could work like in e.g. Junk Jack: if a tool with item count greater than 1 wears out, the count is decremented by one and wear is reset to 0, basically the worn out tool is replaced by a full-durability tool from the stack. So, a tool with item count C can be thought of as one currently active tool plus (C-1) full-durability backup tools.
Of course there would have to be other considerations, such as how
add_item
would deal with such stacks.