From 440d966b939059dfa51604eb68d61eecb12baeb4 Mon Sep 17 00:00:00 2001 From: fluxionary <25628292+fluxionary@users.noreply.github.com> Date: Sun, 9 Oct 2022 05:06:09 -0700 Subject: [PATCH] add an 'equals' method to ItemStack and compatibility w/ lua '==' (#12771) Co-authored-by: rubenwardy Co-authored-by: sfan5 --- doc/lua_api.txt | 17 +++++ games/devtest/mods/unittests/init.lua | 3 +- .../mods/unittests/itemstack_equals.lua | 74 +++++++++++++++++++ src/script/lua_api/l_item.cpp | 34 +++++++++ src/script/lua_api/l_item.h | 3 + 5 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 games/devtest/mods/unittests/itemstack_equals.lua diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 202f66262..46b4ed642 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6811,6 +6811,23 @@ an itemstring, a table or `nil`. * `peek_item(n)`: returns taken `ItemStack` * Copy (don't remove) up to `n` items from this stack * `n`: number, default: `1` +* `equals(other)`: + * returns `true` if this stack is identical to `other`. + * Note: `stack1:to_string() == stack2:to_string()` is not reliable, + as stack metadata can be serialized in arbitrary order. + * Note: if `other` is an itemstring or table representation of an + ItemStack, this will always return false, even if it is + "equivalent". + +### Operators + +* `stack1 == stack2`: + * Returns whether `stack1` and `stack2` are identical. + * Note: `stack1:to_string() == stack2:to_string()` is not reliable, + as stack metadata can be serialized in arbitrary order. + * Note: if `stack2` is an itemstring or table representation of an + ItemStack, this will always return false, even if it is + "equivalent". `ItemStackMetaRef` ------------------ diff --git a/games/devtest/mods/unittests/init.lua b/games/devtest/mods/unittests/init.lua index 936f05c6d..d8237e767 100644 --- a/games/devtest/mods/unittests/init.lua +++ b/games/devtest/mods/unittests/init.lua @@ -91,7 +91,7 @@ function unittests.run_one(idx, counters, out_callback, player, pos) done(status, err) out_callback(true) end - + return true end @@ -178,6 +178,7 @@ dofile(modpath .. "/crafting.lua") dofile(modpath .. "/itemdescription.lua") dofile(modpath .. "/async_env.lua") dofile(modpath .. "/entity.lua") +dofile(modpath .. "/itemstack_equals.lua") dofile(modpath .. "/content_ids.lua") dofile(modpath .. "/metadata.lua") diff --git a/games/devtest/mods/unittests/itemstack_equals.lua b/games/devtest/mods/unittests/itemstack_equals.lua new file mode 100644 index 000000000..561e612c4 --- /dev/null +++ b/games/devtest/mods/unittests/itemstack_equals.lua @@ -0,0 +1,74 @@ +local function test_itemstack_equals_non_stack() + local i1 = ItemStack("basenodes:stone") + local i2 = { foo = "bar" } + + assert(not i1:equals(i2)) + assert(i1 ~= i2) + assert(i2 ~= i1) +end + +unittests.register("test_itemstack_equals_non_stack", test_itemstack_equals_non_stack) + +local function test_itemstack_equals_name() + local i1 = ItemStack("basenodes:stone") + local i2 = ItemStack("basenodes:desert_stone") + + assert(not i1:equals(i2)) + assert(i1 ~= i2) +end + +unittests.register("test_itemstack_equals_name", test_itemstack_equals_name) + +local function test_itemstack_equals_count() + local i1 = ItemStack("basenodes:stone") + local i2 = ItemStack("basenodes:stone 2") + + assert(not i1:equals(i2)) + assert(i1 ~= i2) +end + +unittests.register("test_itemstack_equals_count", test_itemstack_equals_count) + +local function test_itemstack_equals_wear() + local i1 = ItemStack("basetools:axe_stone") + local i2 = ItemStack("basetools:axe_stone") + + i2:add_wear(1) + + assert(not i1:equals(i2)) + assert(i1 ~= i2) +end + +unittests.register("test_itemstack_equals_wear", test_itemstack_equals_wear) + +local function test_itemstack_equals_metadata() + local i1 = ItemStack("basenodes:stone") + local i2 = ItemStack("basenodes:stone") + local i3 = ItemStack("basenodes:stone") + + local m1 = i1:get_meta() + local m2 = i2:get_meta() + local m3 = i3:get_meta() + + local keys = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p"} + local values = {} + + for i, key in pairs(keys) do + m1:set_int(key, i) + m3:set_int(key, i) + values[key] = i + end + + m3:set_int("a", 999) + + for key, i in pairs(values) do + m2:set_int(key, i) + end + + assert(i1:equals(i2)) + assert(i1 == i2) + assert(not i1:equals(i3)) + assert(i1 ~= i3) +end + +unittests.register("test_itemstack_equals_metadata", test_itemstack_equals_metadata) diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index c555e90fd..f33dd1fef 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -433,6 +433,38 @@ int LuaItemStack::l_peek_item(lua_State *L) return 1; } +// equals(self, other) -> bool +int LuaItemStack::l_equals(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o1 = checkObject(L, 1); + + // checks for non-userdata argument + if (!lua_isuserdata(L, 2)) { + lua_pushboolean(L, false); + return 1; + } + + // check that the argument is an ItemStack + if (!lua_getmetatable(L, 2)) { + lua_pushboolean(L, false); + return 1; + } + lua_getfield(L, LUA_REGISTRYINDEX, className); + if (!lua_rawequal(L, -1, -2)) { + lua_pushboolean(L, false); + return 1; + } + + LuaItemStack *o2 = checkObject(L, 2); + + ItemStack &item1 = o1->m_stack; + ItemStack &item2 = o2->m_stack; + + lua_pushboolean(L, item1 == item2); + return 1; +} + LuaItemStack::LuaItemStack(const ItemStack &item): m_stack(item) { @@ -483,6 +515,7 @@ void LuaItemStack::Register(lua_State *L) static const luaL_Reg metamethods[] = { {"__tostring", mt_tostring}, {"__gc", gc_object}, + {"__eq", l_equals}, {0, 0} }; registerClass(L, className, methods, metamethods); @@ -522,6 +555,7 @@ const luaL_Reg LuaItemStack::methods[] = { luamethod(LuaItemStack, item_fits), luamethod(LuaItemStack, take_item), luamethod(LuaItemStack, peek_item), + luamethod(LuaItemStack, equals), {0,0} }; diff --git a/src/script/lua_api/l_item.h b/src/script/lua_api/l_item.h index 8f4b25cf4..a4cc706d8 100644 --- a/src/script/lua_api/l_item.h +++ b/src/script/lua_api/l_item.h @@ -140,6 +140,9 @@ private: // peek_item(self, peekcount=1) -> itemstack static int l_peek_item(lua_State *L); + // equals(self, other) -> bool + static int l_equals(lua_State *L); + public: DISABLE_CLASS_COPY(LuaItemStack)