SongodaCore/Core/src/main/java/com/songoda/core/utils/ItemUtils.java

1192 lines
45 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.songoda.core.utils;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import com.songoda.core.compatibility.ClassMapping;
import com.songoda.core.compatibility.CompatibleHand;
import com.songoda.core.compatibility.CompatibleMaterial;
import com.songoda.core.compatibility.MethodMapping;
import com.songoda.core.compatibility.ServerVersion;
import org.apache.commons.lang3.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import org.bukkit.util.io.BukkitObjectInputStream;
import org.bukkit.util.io.BukkitObjectOutputStream;
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Base64;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
/**
* This class uses some Minecraft code and also Paper API
*/
public class ItemUtils {
static boolean can_getI18NDisplayName = true;
static {
try {
ItemStack.class.getMethod("getI18NDisplayName");
} catch (NoSuchMethodException | SecurityException ex) {
can_getI18NDisplayName = false;
}
}
public static String getItemName(ItemStack it) {
if (it == null) {
return null;
}
return itemName(it.getType());
}
static String itemName(Material mat) {
String matName = mat.name().replace("_", " ");
StringBuilder titleCase = new StringBuilder(matName.length());
Stream.of(matName.split(" ")).forEach(s -> {
s = s.toLowerCase();
if (s.equals("of")) {
titleCase.append(s).append(" ");
} else {
char[] str = s.toCharArray();
str[0] = Character.toUpperCase(str[0]);
titleCase.append(new String(str)).append(" ");
}
});
return titleCase.toString().trim();
}
private static Method methodAsBukkitCopy, methodAsNMSCopy, methodA;
static {
try {
Class<?> clazzEnchantmentManager = ClassMapping.ENCHANTMENT_MANAGER.getClazz();
Class<?> clazzItemStack = ClassMapping.ITEM_STACK.getClazz();
Class<?> clazzCraftItemStack = ClassMapping.CRAFT_ITEM_STACK.getClazz();
methodAsBukkitCopy = clazzCraftItemStack.getMethod("asBukkitCopy", clazzItemStack);
methodAsNMSCopy = clazzCraftItemStack.getMethod("asNMSCopy", ItemStack.class);
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_19)) {
Class<?> clazzRandomSource = ClassMapping.RANDOM_SOURCE.getClazz();
methodA = clazzEnchantmentManager.getMethod("a", clazzRandomSource.getMethod("c").getReturnType(), clazzItemStack, int.class, boolean.class);
}else if (ServerVersion.isServerVersion(ServerVersion.V1_8)) {
methodA = clazzEnchantmentManager.getMethod("a", Random.class, clazzItemStack, int.class);
} else {
methodA = clazzEnchantmentManager.getMethod("a", Random.class, clazzItemStack, int.class, boolean.class);
}
} catch (NoSuchMethodException ex) {
ex.printStackTrace();
}
}
public static ItemStack applyRandomEnchants(ItemStack item, int level) {
try {
Object nmsItemStack = methodAsNMSCopy.invoke(null, item);
if (ServerVersion.isServerVersion(ServerVersion.V1_8)) {
nmsItemStack = methodA.invoke(null, new Random(), nmsItemStack, level);
} else {
nmsItemStack = methodA.invoke(null, new Random(), nmsItemStack, level, false);
}
item = (ItemStack) methodAsBukkitCopy.invoke(null, nmsItemStack);
} catch (IllegalAccessException | InvocationTargetException ex) {
ex.printStackTrace();
}
return item;
}
public static String itemStackArrayToBase64(ItemStack[] items) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BukkitObjectOutputStream dataOutput = new BukkitObjectOutputStream(outputStream);
dataOutput.writeInt(items.length);
for (ItemStack item : items) {
dataOutput.writeObject(item);
}
dataOutput.close();
return Base64Coder.encodeLines(outputStream.toByteArray());
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static ItemStack[] itemStackArrayFromBase64(String data) {
try {
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data));
BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);
ItemStack[] items = new ItemStack[dataInput.readInt()];
for (int i = 0; i < items.length; i++) {
items[i] = (ItemStack) dataInput.readObject();
}
dataInput.close();
return items;
} catch (ClassNotFoundException | IOException ex) {
ex.printStackTrace();
}
return null;
}
/**
* Clone of org.bukkit.inventory.ItemStack.asQuantity, since it is a paper-only function
*
* @param item item to copy
* @param qty amount the new ItemStack should have
*
* @return a copy of the original item
*/
public static ItemStack getAsCopy(ItemStack item, int qty) {
ItemStack clone = item.clone();
clone.setAmount(qty);
return clone;
}
public static boolean hasEnoughDurability(ItemStack tool, int requiredAmount) {
if (tool.getType().getMaxDurability() <= 1) {
return true;
}
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)) {
if (!tool.hasItemMeta() || !(tool.getItemMeta() instanceof Damageable)) {
return true;
}
Damageable damageable = (Damageable) tool.getItemMeta();
int durabilityRemaining = tool.getType().getMaxDurability() - damageable.getDamage();
return durabilityRemaining > requiredAmount;
}
return tool.getDurability() + requiredAmount <= tool.getType().getMaxDurability();
}
static Class<?> cb_ItemStack = ClassMapping.CRAFT_ITEM_STACK.getClazz();
static Class<?> mc_ItemStack = ClassMapping.ITEM_STACK.getClazz();
static Class<?> mc_NBTTagCompound = ClassMapping.NBT_TAG_COMPOUND.getClazz();
static Class<?> mc_NBTTagList = ClassMapping.NBT_TAG_LIST.getClazz();
static Method mc_ItemStack_getTag;
static Method mc_ItemStack_setTag;
static Method mc_NBTTagCompound_set;
static Method mc_NBTTagCompound_remove;
// static Method mc_NBTTagCompound_setShort;
// static Method mc_NBTTagCompound_setString;
// static Method mc_NBTTagList_add;
static Method cb_CraftItemStack_asNMSCopy;
static Method cb_CraftItemStack_asCraftMirror;
static {
if (cb_ItemStack != null) {
try {
mc_ItemStack_getTag = MethodMapping.MC_ITEM_STACK__GET_TAG.getMethod(mc_ItemStack);
mc_ItemStack_setTag = MethodMapping.MC_ITEM_STACK__SET_TAG.getMethod(mc_ItemStack);
mc_NBTTagCompound_set = MethodMapping.MC_NBT_TAG_COMPOUND__SET.getMethod(mc_NBTTagCompound);
mc_NBTTagCompound_remove = MethodMapping.MC_NBT_TAG_COMPOUND__REMOVE.getMethod(mc_NBTTagCompound);
// mc_NBTTagCompound_setShort = MethodMapping.MC_NBT_TAG_COMPOUND__SET_SHORT.getMethod(mc_NBTTagCompound);
// mc_NBTTagCompound_setString = MethodMapping.MC_NBT_TAG_COMPOUND__SET_STRING.getMethod(mc_NBTTagCompound);
cb_CraftItemStack_asNMSCopy = MethodMapping.CB_ITEM_STACK__AS_NMS_COPY.getMethod(cb_ItemStack);
cb_CraftItemStack_asCraftMirror = MethodMapping.CB_ITEM_STACK__AS_CRAFT_MIRROR.getMethod(cb_ItemStack);
// mc_NBTTagList_add = MethodMapping.MC_NBT_TAG_LIST__ADD.getMethod(mc_NBTTagList);
} catch (Exception ex) {
Logger.getLogger(ItemUtils.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
/**
* Make an item glow as if it contained an enchantment. <br>
* Tested working 1.8-1.14
*
* @param item itemstack to create a glowing copy of
*
* @return copy of item with a blank enchantment nbt tag
*/
public static ItemStack addGlow(ItemStack item) {
// from 1.11 up, fake enchantments don't work without more steps
// creating a new Enchantment involves some very involved reflection,
// as the namespace is the same but until 1.12 requires an int, but versions after require a String
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_11)) {
item.addUnsafeEnchantment(Enchantment.DURABILITY, 1);
// you can at least hide the enchantment, though
ItemMeta m = item.getItemMeta();
m.addItemFlags(ItemFlag.HIDE_ENCHANTS);
item.setItemMeta(m);
return item;
}
// hack a fake enchant onto the item
// Confirmed works on 1.8, 1.9, 1.10
// Does not work 1.11+ (minecraft ignores the glitched enchantment)
if (item != null && item.getType() != Material.AIR && cb_CraftItemStack_asCraftMirror != null) {
try {
Object nmsStack = cb_CraftItemStack_asNMSCopy.invoke(null, item);
Object tag = mc_ItemStack_getTag.invoke(nmsStack);
if (tag == null) {
tag = mc_NBTTagCompound.newInstance();
}
// set to have a fake enchantment
Object enchantmentList = mc_NBTTagList.newInstance();
/*
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)) {
// Servers from 1.13 and up change the id to a string
Object fakeEnchantment = mc_NBTTagCompound.newInstance();
mc_NBTTagCompound_setString.invoke(fakeEnchantment, "id", "glow:glow");
mc_NBTTagCompound_setShort.invoke(fakeEnchantment, "lvl", (short) 0);
mc_NBTTagList_add.invoke(enchantmentList, fakeEnchantment);
} else if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_11)) {
// Servers from 1.11 and up require *something* in the enchantment field
Object fakeEnchantment = mc_NBTTagCompound.newInstance();
mc_NBTTagCompound_setShort.invoke(fakeEnchantment, "id", (short) 245);
mc_NBTTagCompound_setShort.invoke(fakeEnchantment, "lvl", (short) 1);
mc_NBTTagList_add.invoke(enchantmentList, fakeEnchantment);
}//*/
mc_NBTTagCompound_set.invoke(tag, "ench", enchantmentList);
mc_ItemStack_setTag.invoke(nmsStack, tag);
item = (ItemStack) cb_CraftItemStack_asCraftMirror.invoke(null, nmsStack);
} catch (Exception ex) {
Bukkit.getLogger().log(Level.SEVERE, "Failed to set glow enchantment on item: " + item, ex);
}
}
return item;
}
/**
* Remove all enchantments, including hidden enchantments
*
* @param item item to clear enchants from
*
* @return copy of the item without any enchantment tag
*/
public static ItemStack removeGlow(ItemStack item) {
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_11)) {
item.removeEnchantment(Enchantment.DURABILITY);
return item;
} else {
if (item != null && item.getType() != Material.AIR && cb_CraftItemStack_asCraftMirror != null) {
try {
Object nmsStack = cb_CraftItemStack_asNMSCopy.invoke(null, item);
Object tag = mc_ItemStack_getTag.invoke(nmsStack);
if (tag != null) {
// remove enchantment list
mc_NBTTagCompound_remove.invoke(tag, "ench");
mc_ItemStack_setTag.invoke(nmsStack, tag);
item = (ItemStack) cb_CraftItemStack_asCraftMirror.invoke(null, nmsStack);
}
} catch (Exception ex) {
Bukkit.getLogger().log(Level.SEVERE, "Failed to set glow enchantment on item: " + item, ex);
}
}
}
return item;
}
public static ItemStack getPlayerSkull(OfflinePlayer player) {
ItemStack head = CompatibleMaterial.PLAYER_HEAD.getItem();
if (ServerVersion.isServerVersionBelow(ServerVersion.V1_8)) {
return head;
}
SkullMeta meta = (SkullMeta) head.getItemMeta();
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)) {
meta.setOwningPlayer(player);
} else {
meta.setOwner(player.getName());
}
head.setItemMeta(meta);
return head;
}
public static void setHeadOwner(ItemStack head, OfflinePlayer player) {
if (ServerVersion.isServerVersionBelow(ServerVersion.V1_8) || !CompatibleMaterial.PLAYER_HEAD.matches(head)) {
return;
}
SkullMeta meta = (SkullMeta) head.getItemMeta();
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)) {
meta.setOwningPlayer(player);
} else {
meta.setOwner(player.getName());
}
}
public static ItemStack getCustomHead(String texture) {
return getCustomHead(null, texture);
}
public static ItemStack getCustomHead(String signature, String texture) {
ItemStack skullItem = CompatibleMaterial.PLAYER_HEAD.getItem();
if (ServerVersion.isServerVersionBelow(ServerVersion.V1_8)) {
return skullItem;
}
SkullMeta sm = (SkullMeta) skullItem.getItemMeta();
GameProfile gm;
if (texture.endsWith("=")) {
gm = new GameProfile(UUID.nameUUIDFromBytes(texture.getBytes()), "CustomHead");
if (signature == null) {
gm.getProperties().put("textures", new Property("texture", texture.replaceAll("=", "")));
} else {
gm.getProperties().put("textures", new Property("textures", texture, signature));
}
} else {
gm = new GameProfile(UUID.nameUUIDFromBytes(texture.getBytes()), "CustomHead");
byte[] encodedData = Base64.getEncoder().encode(String.format("{textures:{SKIN:{url:\"http://textures.minecraft.net/texture/%s\"}}}", texture).getBytes());
gm.getProperties().put("textures", new Property("textures", new String(encodedData)));
}
try {
Field profileField;
profileField = sm.getClass().getDeclaredField("profile");
profileField.setAccessible(true);
profileField.set(sm, gm);
skullItem.setItemMeta(sm);
return skullItem;
} catch (NoSuchFieldException | IllegalAccessException | SecurityException ex) {
throw new RuntimeException("Reflection error while setting head texture", ex);
}
}
static Class cb_CraftPlayer = NMSUtils.getCraftClass("entity.CraftPlayer");
static Method cb_CraftPlayer_getProfile;
static {
try {
cb_CraftPlayer_getProfile = cb_CraftPlayer.getMethod("getProfile");
} catch (Exception ignore) {
}
}
public static String getSkullTexture(Player player) {
if (player == null || ServerVersion.isServerVersionBelow(ServerVersion.V1_8)) {
return null;
}
try {
Object craftPlayer = cb_CraftPlayer.cast(player);
Iterator<Property> iterator = ((GameProfile) cb_CraftPlayer_getProfile.invoke(craftPlayer)).getProperties().get("textures").iterator();
return iterator.hasNext() ? iterator.next().getValue() : null;
} catch (IllegalAccessException | InvocationTargetException ex) {
ex.printStackTrace();
}
return null;
}
public static String getSkullTexture(ItemStack item) {
if (!CompatibleMaterial.PLAYER_HEAD.matches(item) || ServerVersion.isServerVersionBelow(ServerVersion.V1_8)) {
return null;
}
try {
SkullMeta localSkullMeta = (SkullMeta) item.getItemMeta();
Field cb_SkullMeta_profile = localSkullMeta.getClass().getDeclaredField("profile");
cb_SkullMeta_profile.setAccessible(true);
GameProfile profile = (GameProfile) cb_SkullMeta_profile.get(localSkullMeta);
Iterator<Property> iterator = profile.getProperties().get("textures").iterator();
return iterator.hasNext() ? iterator.next().getValue() : null;
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ignore) {
}
return null;
}
public static String getDecodedTexture(String encoded) {
return encoded != null ? StringUtils.substringBetween(new String(Base64.getDecoder().decode(encoded)), "texture/", "\"") : null;
}
/**
* Use up whatever item the player is holding in their main hand
*
* @param player player to grab item from
* @param hand the hand to take the item from.
*/
@Deprecated
public static void takeActiveItem(Player player, CompatibleHand hand) {
takeActiveItem(player, hand, 1);
}
/**
* Use up whatever item the player is holding in their main hand
*
* @param player player to grab item from
* @param hand the hand to take the item from.
* @param amount number of items to use up
*/
@Deprecated
public static void takeActiveItem(Player player, CompatibleHand hand, int amount) {
hand.takeItem(player, amount);
}
/**
* Quickly check to see if the two items use the same material. <br />
* NOTE: Does not check metadata; only checks the item material.
*
* @param is1 first item to compare
* @param is2 item to compare against
*
* @return true if both items are of the same material
*/
public static boolean isSimilarMaterial(ItemStack is1, ItemStack is2) {
CompatibleMaterial mat1 = CompatibleMaterial.getMaterial(is1);
return mat1 != null && mat1 == CompatibleMaterial.getMaterial(is2);
}
/**
* Check to see if this item can be moved into a single slot in this
* inventory. <br>
* This returns true if there is a free slot or a slot with a matching item
* where adding this item's amount to that item's amount will not violate
* the maximum stack size for that item.
*
* @param inventory inventory to check
* @param item item to check against
*
* @return true if a free slot or single receiver slot is available
*/
public static boolean canMove(Inventory inventory, ItemStack item) {
if (inventory.firstEmpty() != -1) {
return true;
}
final ItemMeta itemMeta = item.getItemMeta();
for (ItemStack stack : inventory) {
final ItemMeta stackMeta;
if (isSimilarMaterial(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
&& ((itemMeta == null) == ((stackMeta = stack.getItemMeta()) == null))
&& (itemMeta == null || Bukkit.getItemFactory().equals(itemMeta, stackMeta))) {
return true;
}
}
return false;
}
/**
* Check to see if this item can be moved into a single slot in this
* inventory. <br>
* This returns true if there is a free slot or a slot with a matching item
* where adding this item's amount to that item's amount will not violate
* the maximum stack size for that item.
*
* @param contents inventory to check
* @param item item to check against
*
* @return true if a free slot or single receiver slot is available
*/
public static boolean canMove(ItemStack[] contents, ItemStack item) {
final ItemMeta itemMeta = item.getItemMeta();
for (final ItemStack stack : contents) {
if (stack == null || stack.getAmount() == 0) {
return true;
}
final ItemMeta stackMeta;
if (isSimilarMaterial(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
&& ((itemMeta == null) == ((stackMeta = stack.getItemMeta()) == null))
&& (itemMeta == null || Bukkit.getItemFactory().equals(itemMeta, stackMeta))) {
return true;
}
}
return false;
}
/**
* Check to see if this item can be moved into a single slot in this
* inventory while also reserving one of the slots.<br>
* This returns true if there is a free slot or a slot with a matching item
* where adding this item's amount to that item's amount will not violate
* the maximum stack size for that item.
*
* @param inventory inventory to check
* @param item item to check against
* @param reserved which slot should be reserved
*
* @return true if a free slot or single receiver slot is available
*/
public static boolean canMoveReserved(Inventory inventory, ItemStack item, int reserved) {
final ItemMeta itemMeta = item.getItemMeta();
final ItemStack[] contents = inventory.getContents();
for (int i = 0; i < contents.length; ++i) {
if (i == reserved) {
continue;
}
final ItemStack stack = contents[i];
final ItemMeta stackMeta;
if (stack == null || stack.getAmount() == 0
|| (isSimilarMaterial(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
&& ((itemMeta == null) == ((stackMeta = stack.getItemMeta()) == null))
&& (itemMeta == null || Bukkit.getItemFactory().equals(itemMeta, stackMeta)))) {
return true;
}
}
return false;
}
/**
* Check to see if this item can be moved into a single slot in this
* inventory while also reserving one of the slots.<br>
* This returns true if there is a free slot or a slot with a matching item
* where adding this item's amount to that item's amount will not violate
* the maximum stack size for that item.
*
* @param contents inventory to check
* @param item item to check against
* @param reserved which slot should be reserved
*
* @return true if a free slot or single receiver slot is available
*/
public static boolean canMoveReserved(ItemStack[] contents, ItemStack item, int reserved) {
final ItemMeta itemMeta = item.getItemMeta();
for (int i = 0; i < contents.length; ++i) {
if (i == reserved) {
continue;
}
final ItemStack stack = contents[i];
if (stack == null || stack.getAmount() == 0) {
return true;
}
final ItemMeta stackMeta;
if (isSimilarMaterial(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
&& ((itemMeta == null) == ((stackMeta = stack.getItemMeta()) == null))
&& (itemMeta == null || Bukkit.getItemFactory().equals(itemMeta, stackMeta))) {
return true;
}
}
return false;
}
/**
* Add up to a number of items to this inventory.
*
* @param item item to add
* @param amountToAdd how many of this item to attempt to add
* @param inventory a list that represents the inventory
* @param maxSize maximum number of different items this container can hold
*
* @return how many items were added
*/
public static int addAny(ItemStack item, int amountToAdd, List<ItemStack> inventory, int maxSize) {
return addAny(item, amountToAdd, inventory, maxSize, -1);
}
/**
* Add up to a number of items to this inventory.
*
* @param item item to add
* @param amountToAdd how many of this item to attempt to add
* @param inventory a list that represents the inventory
* @param maxSize maximum number of different items this container can hold
* @param reserved slot to reserve - will not fill this slot
*
* @return how many items were added
*/
public static int addAny(ItemStack item, int amountToAdd, List<ItemStack> inventory, int maxSize, int reserved) {
int totalAdded = 0;
if (inventory != null && item != null && amountToAdd > 0) {
final int maxStack = item.getMaxStackSize();
for (int i = 0; amountToAdd > 0 && i < maxSize; ++i) {
if (i == reserved) {
continue;
}
final ItemStack cacheItem = i >= inventory.size() ? null : inventory.get(i);
if (cacheItem == null || cacheItem.getAmount() == 0) {
// free slot!
int toAdd = Math.min(maxStack, amountToAdd);
ItemStack item2 = item.clone();
item2.setAmount(toAdd);
if (i >= inventory.size()) {
inventory.add(item2);
} else {
inventory.set(i, item2);
}
totalAdded += toAdd;
amountToAdd -= toAdd;
} else if (maxStack > cacheItem.getAmount() && item.isSimilar(cacheItem)) {
// free space!
int toAdd = Math.min(maxStack - cacheItem.getAmount(), amountToAdd);
inventory.get(i).setAmount(toAdd + cacheItem.getAmount());
totalAdded += toAdd;
amountToAdd -= toAdd;
}
}
}
return totalAdded;
}
/**
* Add an item to this inventory, but only if it can be added completely.
*
* @param item item to add
* @param inventory a list that represents the inventory
* @param containerSize maximum number of different items this container can
* hold
*
* @return true if the item was added
*/
public static boolean addItem(ItemStack item, List<ItemStack> inventory, int containerSize) {
if (inventory == null || item == null || item.getAmount() <= 0 || containerSize <= 0) {
return false;
}
return addItem(item, item.getAmount(), inventory, containerSize);
}
/**
* Add an item to this inventory, but only if it can be added completely.
*
* @param item item to add
* @param inventory a list that represents the inventory
* @param containerSize maximum number of different items this container can
* hold
* @param reserved slot to reserve - will not fill this slot
*
* @return true if the item was added
*/
public static boolean addItem(ItemStack item, List<ItemStack> inventory, int containerSize, int reserved) {
if (inventory == null || item == null || item.getAmount() <= 0 || containerSize <= 0) {
return false;
}
return addItem(item, item.getAmount(), inventory, containerSize, reserved);
}
/**
* Add an item to this inventory.
*
* @param item item to add
* @param amount how many of this item should be added
* @param inventory a list that represents the inventory
* @param containerSize maximum number of different items this container can
* @param reserved slot to reserve - will not fill this slot hold
*
* @return true if the item was added
*/
public static boolean addItem(ItemStack item, int amount, List<ItemStack> inventory, int containerSize, int reserved) {
return addItem(item, amount, inventory, containerSize, reserved, null);
}
/**
* Add an item to this inventory, but only if it can be added completely.
*
* @param item item to add
* @param amount how many of this item should be added
* @param inventory a list that represents the inventory
* @param containerSize maximum number of different items this container can
* hold
* @param reserved slot to reserve - will not fill this slot
* @param inventorySource Material of the container
*
* @return true if the item was added
*/
public static boolean addItem(ItemStack item, int amount, List<ItemStack> inventory, int containerSize, int reserved, Material inventorySource) {
if (inventory == null || item == null || amount <= 0 || inventorySource == null) {
return false;
}
boolean[] check = null;
if (inventorySource != Material.AIR) {
// Don't transfer shulker boxes into other shulker boxes, that's a bad idea.
if (inventorySource.name().contains("SHULKER_BOX") && item.getType().name().contains("SHULKER_BOX")) {
return false;
}
// some destination containers have special conditions
switch (inventorySource.name()) {
case "BREWING_STAND": {
// first compile a list of what slots to check
check = new boolean[5];
String typeStr = item.getType().name().toUpperCase();
if (typeStr.contains("POTION") || typeStr.contains("BOTTLE")) {
// potion bottles are the first three slots
check[0] = check[1] = check[2] = true;
}
// fuel in 5th position, input in 4th
if (item.getType() == Material.BLAZE_POWDER) {
check[4] = true;
} else {
check[3] = true;
}
}
case "SMOKER":
case "BLAST_FURNACE":
case "BURNING_FURNACE":
case "FURNACE": {
check = new boolean[3];
boolean isFuel = !item.getType().name().contains("LOG") && CompatibleMaterial.getMaterial(item.getType()).isFuel();
// fuel is 2nd slot, input is first
if (isFuel) {
check[1] = true;
} else {
check[0] = true;
}
}
}
}
// grab the amount to move and the max item stack size
int toAdd = item.getAmount();
final int maxStack = item.getMaxStackSize();
// we can reduce calls to ItemStack.isSimilar() by caching what cells to look at
if (check == null) {
check = new boolean[containerSize];
for (int i = 0; toAdd > 0 && i < check.length; ++i) {
check[i] = true;
}
}
if (reserved >= 0 && check.length < reserved) {
check[reserved] = false;
}
// first verify that we can add this item
for (int i = 0; toAdd > 0 && i < containerSize; ++i) {
if (check[i]) {
final ItemStack cacheItem = i >= inventory.size() ? null : inventory.get(i);
if (cacheItem == null || cacheItem.getAmount() == 0) {
// free slot!
toAdd -= Math.min(maxStack, toAdd);
check[i] = true;
} else if (maxStack > cacheItem.getAmount() && item.isSimilar(cacheItem)) {
// free space!
toAdd -= Math.min(maxStack - cacheItem.getAmount(), toAdd);
check[i] = true;
} else {
check[i] = false;
}
}
}
if (toAdd <= 0) {
// all good to add!
toAdd = item.getAmount();
for (int i = 0; toAdd > 0 && i < containerSize; i++) {
if (!check[i]) {
continue;
}
final ItemStack cacheItem = i >= inventory.size() ? null : inventory.get(i);
if (cacheItem == null || cacheItem.getAmount() == 0) {
// free slot!
int adding = Math.min(maxStack, toAdd);
ItemStack item2 = item.clone();
item2.setAmount(adding);
if (i >= inventory.size()) {
inventory.add(item2);
} else {
inventory.set(i, item2);
}
toAdd -= adding;
} else if (maxStack > cacheItem.getAmount()) {
// free space!
// (no need to check item.isSimilar(cacheItem), since we have that cached in check[])
int adding = Math.min(maxStack - cacheItem.getAmount(), toAdd);
inventory.get(i).setAmount(adding + cacheItem.getAmount());
toAdd -= adding;
}
}
return true;
}
return false;
}
/**
* Add up to a number of items to this inventory.
*
* @param item item to add
* @param amountToAdd how many of this item to attempt to add
* @param inventory a list that represents the inventory
*
* @return how many items were added
*/
public static int addAny(ItemStack item, int amountToAdd, Inventory inventory) {
int totalAdded = 0;
if (inventory != null && item != null && amountToAdd > 0) {
final int containerSize = inventory.getSize();
final int maxStack = item.getMaxStackSize();
for (int i = 0; amountToAdd > 0 && i < containerSize; ++i) {
final ItemStack cacheItem = inventory.getItem(i);
if (cacheItem == null || cacheItem.getAmount() == 0) {
// free slot!
int toAdd = Math.min(maxStack, amountToAdd);
ItemStack item2 = item.clone();
item2.setAmount(toAdd);
inventory.setItem(i, item2);
totalAdded += toAdd;
amountToAdd -= toAdd;
} else if (maxStack > cacheItem.getAmount() && item.isSimilar(cacheItem)) {
// free space!
int toAdd = Math.min(maxStack - cacheItem.getAmount(), amountToAdd);
cacheItem.setAmount(toAdd + cacheItem.getAmount());
totalAdded += toAdd;
amountToAdd -= toAdd;
}
}
}
return totalAdded;
}
/**
* Add an item to this inventory, but only if it can be added completely.
*
* @param item item to add
* @param inventory a list that represents the inventory hold
*
* @return true if the item was added
*/
public static boolean addItem(ItemStack item, Inventory inventory) {
if (inventory == null || item == null || item.getAmount() <= 0) {
return false;
}
return addItem(item, item.getAmount(), inventory, -1, null);
}
/**
* Add an item to this inventory.
*
* @param item item to add
* @param amount how many of this item should be added
* @param inventory a list that represents the inventory
* @param reserved slot to reserve - will not fill this slot
*
* @return true if the item was added
*/
public static boolean addItem(ItemStack item, int amount, Inventory inventory, int reserved) {
return addItem(item, amount, inventory, reserved, null);
}
/**
* Add an item to this inventory, but only if it can be added completely.
*
* @param item item to add
* @param amount how many of this item should be added
* @param inventory a list that represents the inventory
* @param reserved slot to reserve - will not fill this slot
* @param inventorySource Material of the container
*
* @return true if the item was added
*/
public static boolean addItem(ItemStack item, int amount, Inventory inventory, int reserved, Material inventorySource) {
if (inventory == null || item == null || amount <= 0 || inventorySource == null) {
return false;
}
boolean[] check = null;
if (inventorySource != Material.AIR) {
// Don't transfer shulker boxes into other shulker boxes, that's a bad idea.
if (inventorySource.name().contains("SHULKER_BOX") && item.getType().name().contains("SHULKER_BOX")) {
return false;
}
// some destination containers have special conditions
switch (inventorySource.name()) {
case "BREWING_STAND": {
// first compile a list of what slots to check
check = new boolean[5];
String typeStr = item.getType().name().toUpperCase();
if (typeStr.contains("POTION") || typeStr.contains("BOTTLE")) {
// potion bottles are the first three slots
check[0] = check[1] = check[2] = true;
}
// fuel in 5th position, input in 4th
if (item.getType() == Material.BLAZE_POWDER) {
check[4] = true;
} else {
check[3] = true;
}
}
case "SMOKER":
case "BLAST_FURNACE":
case "BURNING_FURNACE":
case "FURNACE": {
check = new boolean[3];
boolean isFuel = !item.getType().name().contains("LOG") && CompatibleMaterial.getMaterial(item.getType()).isFuel();
// fuel is 2nd slot, input is first
if (isFuel) {
check[1] = true;
} else {
check[0] = true;
}
}
}
}
// grab the amount to move and the max item stack size
int toAdd = item.getAmount();
final int maxStack = item.getMaxStackSize();
final int containerSize = inventory.getSize();
// we can reduce calls to ItemStack.isSimilar() by caching what cells to look at
if (check == null) {
check = new boolean[containerSize];
for (int i = 0; toAdd > 0 && i < check.length; ++i) {
check[i] = true;
}
}
// first verify that we can add this item
for (int i = 0; toAdd > 0 && i < containerSize; i++) {
if (check[i]) {
final ItemStack cacheItem = inventory.getItem(i);
if (cacheItem == null || cacheItem.getAmount() == 0) {
// free slot!
toAdd -= Math.min(maxStack, toAdd);
check[i] = true;
} else if (maxStack > cacheItem.getAmount() && item.isSimilar(cacheItem)) {
// free space!
toAdd -= Math.min(maxStack - cacheItem.getAmount(), toAdd);
check[i] = true;
} else {
check[i] = false;
}
}
}
if (toAdd <= 0) {
// all good to add!
toAdd = item.getAmount();
for (int i = 0; toAdd > 0 && i < containerSize; ++i) {
if (!check[i]) {
continue;
}
final ItemStack cacheItem = inventory.getItem(i);
if (cacheItem == null || cacheItem.getAmount() == 0) {
// free slot!
int adding = Math.min(maxStack, toAdd);
ItemStack item2 = item.clone();
item2.setAmount(adding);
inventory.setItem(i, item2);
toAdd -= adding;
} else if (maxStack > cacheItem.getAmount()) {
// free space!
// (no need to check item.isSimilar(cacheItem), since we have that cached in check[])
int adding = Math.min(maxStack - cacheItem.getAmount(), toAdd);
cacheItem.setAmount(adding + cacheItem.getAmount());
toAdd -= adding;
}
}
return true;
}
return false;
}
public static CompatibleMaterial getDyeColor(char color) {
switch (color) {
case '0':
return CompatibleMaterial.BLACK_DYE;
case '1':
return CompatibleMaterial.BLUE_DYE;
case '2':
return CompatibleMaterial.GREEN_DYE;
case '3':
return CompatibleMaterial.CYAN_DYE;
case '4':
return CompatibleMaterial.BROWN_DYE;
case '5':
return CompatibleMaterial.PURPLE_DYE;
case '6':
return CompatibleMaterial.ORANGE_DYE;
case '7':
return CompatibleMaterial.LIGHT_GRAY_DYE;
case '8':
return CompatibleMaterial.GRAY_DYE;
case 'a':
return CompatibleMaterial.LIME_DYE;
case 'b':
return CompatibleMaterial.LIGHT_BLUE_DYE;
case 'c':
return CompatibleMaterial.RED_DYE;
case 'd':
return CompatibleMaterial.MAGENTA_DYE;
case 'e':
return CompatibleMaterial.YELLOW_DYE;
case 'f':
return CompatibleMaterial.WHITE_DYE;
}
return CompatibleMaterial.STONE;
}
/**
* Add an item to this inventory.
*
* @param item item to add
* @param amount how many of this item should be added
* @param inventory a list that represents the inventory
* @param containerSize maximum number of different items this container can
* hold
*
* @return true if the item was added
*/
public static boolean addItem(ItemStack item, int amount, List<ItemStack> inventory, int containerSize) {
if (inventory == null || item == null || amount <= 0 || containerSize <= 0) {
return false;
}
// grab the amount to move and the max item stack size
int toAdd = amount;
final int maxStack = item.getMaxStackSize();
// we can reduce calls to ItemStack.isSimilar() by caching what cells to look at
boolean[] check = new boolean[containerSize];
Arrays.fill(check, true);
// first verify that we can add this item
for (int i = 0; toAdd > 0 && i < containerSize; i++) {
if (check[i]) {
final ItemStack cacheItem = i >= inventory.size() ? null : inventory.get(i);
if (cacheItem == null || cacheItem.getAmount() == 0) {
// free slot!
toAdd -= Math.min(maxStack, toAdd);
check[i] = true;
} else if (maxStack > cacheItem.getAmount() && item.isSimilar(cacheItem)) {
// free space!
toAdd -= Math.min(maxStack - cacheItem.getAmount(), toAdd);
check[i] = true;
} else {
check[i] = false;
}
}
}
if (toAdd <= 0) {
// all good to add!
toAdd = item.getAmount();
for (int i = 0; toAdd > 0 && i < containerSize; i++) {
if (!check[i]) {
continue;
}
final ItemStack cacheItem = i >= inventory.size() ? null : inventory.get(i);
if (cacheItem == null || cacheItem.getAmount() == 0) {
// free slot!
int adding = Math.min(maxStack, toAdd);
ItemStack item2 = item.clone();
item2.setAmount(adding);
if (i >= inventory.size()) {
inventory.add(item2);
} else {
inventory.set(i, item2);
}
toAdd -= adding;
} else if (maxStack > cacheItem.getAmount()) {
// free space!
// (no need to check item.isSimilar(cacheItem), since we have that cached in check[])
int adding = Math.min(maxStack - cacheItem.getAmount(), toAdd);
inventory.get(i).setAmount(adding + cacheItem.getAmount());
toAdd -= adding;
}
}
return true;
}
return false;
}
}