diff --git a/plugins/civmodcore-paper/src/main/java/vg/civcraft/mc/civmodcore/inventory/items/ItemMap.java b/plugins/civmodcore-paper/src/main/java/vg/civcraft/mc/civmodcore/inventory/items/ItemMap.java index 6a5d79bbd..f596aacf7 100644 --- a/plugins/civmodcore-paper/src/main/java/vg/civcraft/mc/civmodcore/inventory/items/ItemMap.java +++ b/plugins/civmodcore-paper/src/main/java/vg/civcraft/mc/civmodcore/inventory/items/ItemMap.java @@ -1,73 +1,83 @@ package vg.civcraft.mc.civmodcore.inventory.items; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; -import java.util.logging.Logger; +import java.util.UUID; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagByte; +import net.minecraft.nbt.NBTTagByteArray; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagDouble; +import net.minecraft.nbt.NBTTagFloat; +import net.minecraft.nbt.NBTTagInt; +import net.minecraft.nbt.NBTTagIntArray; import net.minecraft.nbt.NBTTagList; -import org.bukkit.Bukkit; +import net.minecraft.nbt.NBTTagLong; +import net.minecraft.nbt.NBTTagShort; +import net.minecraft.nbt.NBTTagString; +import org.apache.commons.collections4.CollectionUtils; import org.bukkit.ChatColor; import org.bukkit.Material; +import org.bukkit.configuration.MemorySection; import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.Repairable; +import vg.civcraft.mc.civmodcore.nbt.NBTSerialization; +import vg.civcraft.mc.civmodcore.utilities.CivLogger; +import vg.civcraft.mc.civmodcore.utilities.MoreMath; /** - * Allows the storage and comparison of itemstacks while ignoring their maximum - * possible stack sizes. This offers various tools to compare inventories, to - * store recipe costs or to specify setupcosts. Take great care when dealing - * with itemstacks with negative amounnts, while this implementation should be - * consistent even with negative values, they create possibly unexpected - * results. For example an empty inventory/itemmap will seem to contain items - * when compared to a map containing negative values. Additionally this - * implementation allows durability "wild cards", if you specify -1 as - * durability it will count as any given durability. When working with multiple - * ItemMaps this will only work if all methods are executed on the instance - * containing items with a durability of -1. + * Allows the storage and comparison of item stacks while ignoring their maximum possible stack sizes. This offers + * various tools to compare inventories, to store recipe costs or to specify setup costs. Take great care when dealing + * with item stacks with negative amounts, while this implementation should be consistent even with negative values, + * they create possibly unexpected results. For example an empty inventory/item map will seem to contain items when + * compared to a map containing negative values. Additionally this implementation allows durability "wild cards", if + * you specify -1 as durability it will count as any given durability. When working with multiple ItemMaps this will + * only work if all methods are executed on the instance containing items with a durability of -1. + * + * TODO: ItemMap is troubling because it manipulates and searches for items in ways that are not friendly to modern + * ways of cataloging items. For example, Bastion materials are all custom items, but they're only custom in the + * sense that they are named and lored.. but an Energizer will match with any other emerald unless you use any of + * the weirdly specific methods to weed out custom items. I think it would be better to refactor ItemMap to + * specifically support custom items, but I definitely need help in that regard. Or we can just keep it around + * for a little longer since they work fine enough and aren't in critical updates. */ public class ItemMap { - private static final Logger log = Bukkit.getLogger(); - - private HashMap items; + private static final CivLogger LOGGER = CivLogger.getLogger(ItemMap.class); + private final Object2IntMap items; private int totalItems; /** * Empty constructor to create empty item map */ public ItemMap() { - items = new HashMap<>(); - totalItems = 0; - } - - /** - * Constructor to create an item map based on the content of an inventory. The - * ItemMap will not be in sync with the inventory, it will only update if it's - * explicitly told to do so - * - * @param inv Inventory to base the item map on - */ - public ItemMap(Inventory inv) { - totalItems = 0; - update(inv); + this.items = new Object2IntOpenHashMap<>(0); + this.items.defaultReturnValue(0); + this.totalItems = 0; } /** * Constructor to create an ItemMap based on a single ItemStack * - * @param is ItemStack to start with + * @param item ItemStack to start with */ - public ItemMap(ItemStack is) { - items = new HashMap<>(); - totalItems = 0; - addItemStack(is); + public ItemMap(final ItemStack item) { + this.items = new Object2IntOpenHashMap<>(1); + this.items.defaultReturnValue(0); + this.totalItems = 0; + addItemStack(item); } /** @@ -75,41 +85,91 @@ public class ItemMap { * * @param stacks Stacks to add to the map */ - public ItemMap(Collection stacks) { - items = new HashMap<>(); + public ItemMap(final Collection stacks) { + this.items = new Object2IntOpenHashMap<>(stacks.size()); + this.items.defaultReturnValue(0); addAll(stacks); } /** - * Clones the given itemstack, sets its amount to one and checks whether a stack - * equaling the created one exists in the item map. If yes the amount of the - * given stack (before the amount was set to 1) will be added to the current - * amount in the item map, if not a new entry in the map with the correct amount - * will be created + * Constructor to create an item map based on the content of an inventory. The ItemMap will not be in sync with the + * inventory, it will only update if it's explicitly told to do so. + * + * @param inventory Inventory to base the item map on + */ + public ItemMap(final Inventory inventory) { + this.items = new Object2IntOpenHashMap<>(inventory.getSize()); + this.items.defaultReturnValue(0); + this.totalItems = 0; + update(inventory); + } + + private static ItemStack INTERNAL_createKey(ItemStack item) { + item = item.asOne(); // this also clones the stack + ItemUtils.handleItemMeta(item, (Repairable meta) -> { + meta.setRepairCost(0); + return true; + }); + return item; + } + + + + + //getAmount + //getTotalItemAmount + //getTotalUniqueItemAmount + //getStacksByMaterial + //getStacksByMaterialEnchant + //getStacksByLore + //getEntrySet + //getItemStackRepresentation + //getLoredItemCountRepresentation + + //addAll + //addItemStack + //addItemAmount + //addToInventory + //addToEntrySet + + //removeItemStack + //removeItemStackCompletely + //removeSafelyFrom + + //fitsIn + //isContainedIn + //containedExactlyIn + //getMultiplesContainedIn + //multiplyContent + + //createMapConformCopy + //enrichWithNBT + //clone + //merge + //update + + + + + /** + * Clones the given item stack, sets its amount to one and checks whether a stack equaling the created one exists + * in the item map. If yes the amount of the given stack (before the amount was set to 1) will be added to the + * current amount in the item map, if not a new entry in the map with the correct amount will be created. * * @param input ItemStack to insert */ - public void addItemStack(ItemStack input) { - if (input != null) { - // log().info("Adding {0} as ItemStack", input.toString()); - ItemStack is = createMapConformCopy(input); - // log().info(" Conform Copy: {0}", is.toString()); - if (is == null) { - return; - } - Integer i; - if ((i = items.get(is)) != null) { - items.put(is, i + input.getAmount()); - } else { - items.put(is, input.getAmount()); - } - totalItems += input.getAmount(); + public void addItemStack(final ItemStack input) { + if (!ItemUtils.isValidItemIgnoringAmount(input)) { + return; } + this.items.computeInt(INTERNAL_createKey(input), (key, amount) -> + amount == null ? input.getAmount() : amount + input.getAmount()); + this.totalItems += input.getAmount(); } /** * Adds all the items contained in this instance to the given inventory - * + * * @param inventory Inventory to add items to */ public void addToInventory(Inventory inventory) { @@ -119,49 +179,38 @@ public class ItemMap { } /** - * Removes the given ItemStack from this map. Only the amount of the given - * ItemStack will be removed, not all of them. If the amount of the given - * itemstack is bigger than the existing ones in this map, not more than the + * Removes the given ItemStack from this map. Only the amount of the given ItemStack will be removed, not all of + * them. If the amount of the given item stack is bigger than the existing ones in this map, not more than the * amount in this map will be removed * * @param input ItemStack to remove */ - public void removeItemStack(ItemStack input) { - ItemStack is = createMapConformCopy(input); - if (is == null) { + public void removeItemStack(final ItemStack input) { + if (input.getAmount() <= 0) { return; } - Integer value = items.get(is); - if (value != null) { - int newVal = value - input.getAmount(); - if (newVal > 0) { - items.put(is, newVal); - } else { - items.remove(is); - } + final ItemStack key = INTERNAL_createKey(input); + if (key == null) { + return; } + this.items.computeIntIfPresent(key, (_key, amount) -> (amount -= input.getAmount()) <= 0 ? null : amount); } /** - * Completly removes the given itemstack of this item map, completly independent - * of its amount + * Completely removes the given item stack of this item map, completely independent of its amount. * * @param input ItemStack to remove */ - public void removeItemStackCompletly(ItemStack input) { - ItemStack is = createMapConformCopy(input); - if (is != null) { - items.remove(is); + public void removeItemStackCompletely(final ItemStack input) { + final ItemStack key = INTERNAL_createKey(input); + if (key != null) { + this.items.removeInt(key); } } @Override public int hashCode() { - int res = 0; - for (Entry entry : items.entrySet()) { - res += entry.hashCode(); - } - return res; + return this.items.hashCode(); } /** @@ -188,13 +237,13 @@ public class ItemMap { } } - public void update(Inventory inv) { - items = new HashMap<>(); - totalItems = 0; - for (int i = 0; i < inv.getSize(); i++) { - ItemStack is = inv.getItem(i); - if (is != null) { - addItemStack(is); + public void update(final Inventory inventory) { + this.items.clear(); + this.totalItems = 0; + for (int i = 0; i < inventory.getSize(); i++) { + final ItemStack item = inventory.getItem(i); + if (item != null) { + addItemStack(item); } } } @@ -212,7 +261,7 @@ public class ItemMap { * @param amount Amount associated with the given ItemStack */ public void addItemAmount(ItemStack input, int amount) { - ItemStack copy = createMapConformCopy(input); + ItemStack copy = INTERNAL_createKey(input); if (copy == null) { return; } @@ -221,8 +270,8 @@ public class ItemMap { } /** - * Gets a submap of this instance which contains all stacks with the same - * material as the given one and their respective amounts + * Gets a submap of this instance which contains all stacks with the same material as the given one and their + * respective amounts. * * @param m Material to search for * @return New ItemMap with all ItemStack and their amount whose material @@ -243,13 +292,12 @@ public class ItemMap { } /** - * Gets a submap of this instance which contains all stacks with the same - * material and enchants as the given one and their respective amounts + * Gets a submap of this instance which contains all stacks with the same material and enchants as the given one + * and their respective amounts. * - * @param m Material to search for + * @param m Material to search for * @param enchants Enchants to search for - * @return New ItemMap with all ItemStack and their amount whose material and - * enchants matches the given one + * @return New ItemMap with all ItemStack and their amount whose material and enchants matches the given one */ public ItemMap getStacksByMaterialEnchants(Material m, Map enchants) { ItemMap result = new ItemMap(); @@ -270,27 +318,35 @@ public class ItemMap { } /** - * Gets a submap of this instance which contains all stacks with the same lore - * as the given and their respective amount + * Gets a submap of this instance which contains all stacks with the same lore as the given and their respective + * amount. * * @param lore Lore to search for - * @return New ItemMap with all ItemStacks and their amount whose lore matches - * the given one + * @return New ItemMap with all ItemStacks and their amount whose lore matches the given one */ - public ItemMap getStacksByLore(List lore) { - ItemMap result = new ItemMap(); - for (ItemStack is : items.keySet()) { - if (is.getItemMeta() != null && is.getItemMeta().getLore().equals(lore)) { - result.addItemAmount(is.clone(), items.get(is)); + public ItemMap getStacksByLore(final List lore) { + final boolean gaveLore = CollectionUtils.isNotEmpty(lore); + final ItemMap result = new ItemMap(); + for (final ItemStack key : this.items.keySet()) { + if (!key.hasItemMeta()) { + continue; } + final var keyMeta = key.getItemMeta(); + if (gaveLore != keyMeta.hasLore()) { + continue; + } + final var keyLore = keyMeta.getLore(); + if (!Objects.equals(lore, keyLore)) { + continue; + } + result.addItemAmount(key.clone(), this.items.getInt(key)); } return result; } /** - * Gets how many items of the given stack are in this map. Be aware that if a - * stack doesnt equal with the given one, for example because of mismatched NBT - * tags, it wont be included in the result + * Gets how many items of the given stack are in this map. Be aware that if a stack doesnt equal with the given one, + * for example because of mismatched NBT tags, it wont be included in the result * * @param is Exact ItemStack to search for * @return amount of items like the given stack in this map @@ -321,9 +377,9 @@ public class ItemMap { return items.keySet().size(); } - @SuppressWarnings("unchecked") + @SuppressWarnings("deprecation") public Set> getEntrySet() { - return ((HashMap) items.clone()).entrySet(); + return this.items.entrySet(); } /** @@ -351,14 +407,12 @@ public class ItemMap { } /** - * Checks whether this instance is completly contained in the given inventory, - * which means every stack in this instance is also in the given inventory and - * the amount in the given inventory is either the same or bigger as in this - * instance + * Checks whether this instance is completely contained in the given inventory, which means every stack in this + * instance is also in the given inventory and the amount in the given inventory is either the same or bigger as in + * this instance * * @param i inventory to check - * @return true if this instance is completly contained in the given inventory, - * false if not + * @return true if this instance is completely contained in the given inventory, false if not */ public boolean isContainedIn(Inventory i) { ItemMap invMap = new ItemMap(i); @@ -374,19 +428,17 @@ public class ItemMap { public String toString() { StringBuilder res = new StringBuilder(); for (ItemStack is : getItemStackRepresentation()) { - res.append(is.toString() + ";"); + res.append(is.toString()).append(";"); } return res.toString(); } /** - * Checks how often this ItemMap is contained in the given ItemMap or how often - * this ItemMap could be removed from the given one before creating negative - * stacks + * Checks how often this ItemMap is contained in the given ItemMap or how often this ItemMap could be removed from + * the given one before creating negative stacks * * @param i ItemMap to check - * @return How often this map is contained in the given one or Integer.MAX_VALUE - * if this instance is empty + * @return How often this map is contained in the given one or Integer.MAX_VALUE if this instance is empty */ public int getMultiplesContainedIn(Inventory i) { ItemMap invMap = new ItemMap(i); @@ -413,8 +465,8 @@ public class ItemMap { } /** - * Turns this item map into a list of ItemStacks, with amounts that do not - * surpass the maximum allowed stack size for each ItemStack + * Turns this item map into a list of ItemStacks, with amounts that do not surpass the maximum allowed stack size + * for each ItemStack * * @return List of stacksize conform ItemStacks */ @@ -451,8 +503,7 @@ public class ItemMap { * Checks whether this instance would completly fit into the given inventory * * @param i Inventory to check - * @return True if this ItemMap's item representation would completly fit in the - * inventory, false if not + * @return True if this ItemMap's item representation would completly fit in the inventory, false if not */ public boolean fitsIn(Inventory i) { int size; @@ -471,9 +522,9 @@ public class ItemMap { } /** - * Instead of converting into many stacks of maximum size, this creates a stack - * with an amount of one for each entry and adds the total item amount and stack - * count as lore, which is needed to display larger ItemMaps in inventories + * Instead of converting into many stacks of maximum size, this creates a stack with an amount of one for each + * entry and adds the total item amount and stack count as lore, which is needed to display larger ItemMaps in + * inventories * * @return UI representation of large ItemMap */ @@ -488,11 +539,11 @@ public class ItemMap { int extra = entry.getValue() % is.getType().getMaxStackSize(); StringBuilder out = new StringBuilder(ChatColor.GOLD.toString()); if (stacks != 0) { - out.append(stacks + " stack" + (stacks == 1 ? "" : "s")); + out.append(stacks).append(" stack").append(stacks == 1 ? "" : "s"); } if (extra != 0) { - out.append(" and " + extra); - out.append(" item" + (extra == 1 ? "" : "s")); + out.append(" and ").append(extra); + out.append(" item").append(extra == 1 ? "" : "s"); } ItemUtils.addLore(is, out.toString()); } @@ -502,8 +553,8 @@ public class ItemMap { } /** - * Attempts to remove the content of this ItemMap from the given inventory. If - * it fails to find all the required items it will stop and return false + * Attempts to remove the content of this ItemMap from the given inventory. If it fails to find all the required + * items it will stop and return false * * @param i Inventory to remove from * @return True if everything was successfully removed, false if not @@ -534,8 +585,7 @@ public class ItemMap { } } if (amountToRemove > 0) { - if (i instanceof PlayerInventory) { - PlayerInventory pInv = (PlayerInventory) i; + if (i instanceof PlayerInventory pInv) { ItemStack offHand = pInv.getItemInOffHand(); if (offHand == null) { return false; @@ -563,8 +613,7 @@ public class ItemMap { @Override public boolean equals(Object o) { - if (o instanceof ItemMap) { - ItemMap im = (ItemMap) o; + if (o instanceof ItemMap im) { if (im.getTotalItemAmount() == getTotalItemAmount()) { return im.getEntrySet().equals(getEntrySet()); } @@ -572,62 +621,133 @@ public class ItemMap { return false; } - /** - * Utility to not mess with stacks directly taken from inventories - * - * @param is Template ItemStack - * @return Cloned ItemStack with its amount set to 1 - */ - private static ItemStack createMapConformCopy(ItemStack is) { - ItemStack copy = is.clone(); - copy.setAmount(1); - final var s = ItemUtils.getNMSItemStack(copy); - if (s == null) { - log.info("Attempted to create map conform copy of " + copy.toString() - + ", but couldn't because this item can't be held in inventories since Minecraft 1.8"); - return null; - } - s.setRepairCost(0); - copy = s.getBukkitStack(); - return copy; - } - /** * Utility to add NBT tags to an item and produce a custom stack size * - * @param is Template Bukkit ItemStack - * @param amt Output Stack Size + * @param item Template Bukkit ItemStack + * @param amount Output Stack Size * @param map Java Maps and Lists representing NBT data * @return Cloned ItemStack with amount set to amt and NBT set to map. */ - public static ItemStack enrichWithNBT(ItemStack is, int amt, Map map) { - log.fine("Received request to enrich " + is.toString()); - ItemStack copy = is.clone(); - amt = (amt < 1 ? 1 : amt > is.getMaxStackSize() ? is.getMaxStackSize() : amt); - copy.setAmount(amt); - final var s = ItemUtils.getNMSItemStack(copy); - if (s == null) { - log.severe("Failed to create enriched copy of " + copy.toString()); - return null; - } - - NBTTagCompound nbt = s.getTag(); - if (nbt == null) { - nbt = new NBTTagCompound(); - } - - TagManager.mapToNBT(nbt, map); - s.setTag(nbt); - copy = s.getBukkitStack(); - return copy; + public static ItemStack enrichWithNBT(ItemStack item, int amount, Map map) { + LOGGER.fine("Received request to enrich " + item.toString()); + item = item.clone(); + item.setAmount(MoreMath.clamp(amount, 1, item.getMaxStackSize())); + item = NBTSerialization.processItem(item, (nbt) -> mapToNBT(nbt, map)); + return item; } + @SuppressWarnings("unchecked") public static NBTTagCompound mapToNBT(NBTTagCompound base, Map map) { - return TagManager.mapToNBT(base, map); + LOGGER.fine("Representing map --> NBTTagCompound"); + if (map == null || base == null) { + return base; + } + for (Map.Entry entry : map.entrySet()) { + Object object = entry.getValue(); + if (object instanceof Map) { + LOGGER.fine("Adding map at key " + entry.getKey()); + base.set(entry.getKey(), mapToNBT(new NBTTagCompound(), (Map) object)); + } else if (object instanceof MemorySection) { + LOGGER.fine("Adding map from MemorySection at key " + entry.getKey()); + base.set(entry.getKey(), mapToNBT(new NBTTagCompound(), ((MemorySection) object).getValues(true))); + } else if (object instanceof List) { + LOGGER.fine("Adding list at key " + entry.getKey()); + base.set(entry.getKey(), listToNBT(new NBTTagList(), (List) object)); + } else if (object instanceof String) { + LOGGER.fine("Adding String " + object + " at key " + entry.getKey()); + base.setString(entry.getKey(), (String) object); + } else if (object instanceof Double) { + LOGGER.fine("Adding Double " + object + " at key " + entry.getKey()); + base.setDouble(entry.getKey(), (Double) object); + } else if (object instanceof Float) { + LOGGER.fine("Adding Float " + object + " at key " + entry.getKey()); + base.setFloat(entry.getKey(), (Float) object); + } else if (object instanceof Boolean) { + LOGGER.fine("Adding Boolean " + object + " at key " + entry.getKey()); + base.setBoolean(entry.getKey(), (Boolean) object); + } else if (object instanceof Byte) { + LOGGER.fine("Adding Byte " + object + " at key " + entry.getKey()); + base.setByte(entry.getKey(), (Byte) object); + } else if (object instanceof Short) { + LOGGER.fine("Adding Byte " + object + " at key " + entry.getKey()); + base.setShort(entry.getKey(), (Short) object); + } else if (object instanceof Integer) { + LOGGER.fine("Adding Integer " + object + " at key " + entry.getKey()); + base.setInt(entry.getKey(), (Integer) object); + } else if (object instanceof Long) { + LOGGER.fine("Adding Long " + object + " at key " + entry.getKey()); + base.setLong(entry.getKey(), (Long) object); + } else if (object instanceof byte[]) { + LOGGER.fine("Adding bytearray at key " + entry.getKey()); + base.setByteArray(entry.getKey(), (byte[]) object); + } else if (object instanceof int[]) { + LOGGER.fine("Adding intarray at key " + entry.getKey()); + base.setIntArray(entry.getKey(), (int[]) object); + } else if (object instanceof UUID) { + LOGGER.fine("Adding UUID " + object + " at key " + entry.getKey()); + base.a(entry.getKey(), (UUID) object); + } else if (object instanceof NBTBase) { + LOGGER.fine("Adding nbtobject at key " + entry.getKey()); + base.set(entry.getKey(), (NBTBase) object); + } else { + LOGGER.warning("Unrecognized entry in map-->NBT: " + object.toString()); + } + } + return base; } + @SuppressWarnings("unchecked") public static NBTTagList listToNBT(NBTTagList base, List list) { - return TagManager.listToNBT(base, list); + LOGGER.fine("Representing list --> NBTTagList"); + if (list == null || base == null) { + return base; + } + for (Object object : list) { + if (object instanceof Map) { + LOGGER.fine("Adding map to list"); + base.add(mapToNBT(new NBTTagCompound(), (Map) object)); + } else if (object instanceof MemorySection) { + LOGGER.fine("Adding map from MemorySection to list"); + base.add(mapToNBT(new NBTTagCompound(), ((MemorySection) object).getValues(true))); + } else if (object instanceof List) { + LOGGER.fine("Adding list to list"); + base.add(listToNBT(new NBTTagList(), (List) object)); + } else if (object instanceof String) { + LOGGER.fine("Adding string " + object + " to list"); + base.add(NBTTagString.a((String) object)); + } else if (object instanceof Double) { + LOGGER.fine("Adding double " + object + " to list"); + base.add(NBTTagDouble.a((Double) object)); + } else if (object instanceof Float) { + LOGGER.fine("Adding float " + object + " to list"); + base.add(NBTTagFloat.a((Float) object)); + } else if (object instanceof Byte) { + LOGGER.fine("Adding byte " + object + " to list"); + base.add(NBTTagByte.a((Byte) object)); + } else if (object instanceof Short) { + LOGGER.fine("Adding short " + object + " to list"); + base.add(NBTTagShort.a((Short) object)); + } else if (object instanceof Integer) { + LOGGER.fine("Adding integer " + object + " to list"); + base.add(NBTTagInt.a((Integer) object)); + } else if (object instanceof Long) { + LOGGER.fine("Adding long " + object + " to list"); + base.add(NBTTagLong.a((Long) object)); + } else if (object instanceof byte[]) { + LOGGER.fine("Adding byte array to list"); + base.add(new NBTTagByteArray((byte[]) object)); + } else if (object instanceof int[]) { + LOGGER.fine("Adding int array to list"); + base.add(new NBTTagIntArray((int[]) object)); + } else if (object instanceof NBTBase) { + LOGGER.fine("Adding nbt object to list"); + base.add((NBTBase) object); + } else { + LOGGER.warning("Unrecognized entry in list-->NBT: " + base); + } + } + return base; } } diff --git a/plugins/civmodcore-paper/src/main/java/vg/civcraft/mc/civmodcore/inventory/items/ItemUtils.java b/plugins/civmodcore-paper/src/main/java/vg/civcraft/mc/civmodcore/inventory/items/ItemUtils.java index 16a45219f..fc41adc3d 100644 --- a/plugins/civmodcore-paper/src/main/java/vg/civcraft/mc/civmodcore/inventory/items/ItemUtils.java +++ b/plugins/civmodcore-paper/src/main/java/vg/civcraft/mc/civmodcore/inventory/items/ItemUtils.java @@ -106,7 +106,23 @@ public final class ItemUtils { public static boolean isValidItem(final ItemStack item) { return item != null && isValidItemMaterial(item.getType()) - && isValidItemAmount(item); + && item.getAmount() > 0 + && item.getAmount() <= item.getMaxStackSize(); + } + + /** + * Checks if an ItemStack is valid instance in that it's non-null, has a valid item material, and has a positive + * item amount. This differs from {@link #isValidItem(ItemStack)} in that the maximum stack size isn't considered, + * so an item considered valid by this method may not be considered valid by {@link #isValidItem(ItemStack)}, thus + * may not appear correctly in inventories. + * + * @param item The item to validate. + * @return Returns true if the item is valid. + */ + public static boolean isValidItemIgnoringAmount(final ItemStack item) { + return item != null + && isValidItemMaterial(item.getType()) + && item.getAmount() > 0; } /**