mirror of
https://github.com/CivMC/Civ.git
synced 2026-07-18 00:20:44 +00:00
Update ItemMap
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user