Update ItemMap

This commit is contained in:
Alexander
2021-06-26 21:58:42 +01:00
parent 44244e2070
commit 27df8fed62
2 changed files with 321 additions and 185 deletions

View File

@@ -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<ItemStack, Integer> items;
private static final CivLogger LOGGER = CivLogger.getLogger(ItemMap.class);
private final Object2IntMap<ItemStack> 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<ItemStack> stacks) {
items = new HashMap<>();
public ItemMap(final Collection<ItemStack> 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<ItemStack, Integer> 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<Enchantment, Integer> 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<String> 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<String> 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<Entry<ItemStack, Integer>> getEntrySet() {
return ((HashMap<ItemStack, Integer>) 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<String, Object> 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<String, Object> 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<String, Object> map) {
return TagManager.mapToNBT(base, map);
LOGGER.fine("Representing map --> NBTTagCompound");
if (map == null || base == null) {
return base;
}
for (Map.Entry<String, Object> 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<String, Object>) 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>) 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<Object> 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<String, Object>) 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>) 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;
}
}

View File

@@ -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;
}
/**