diff --git a/pom.xml b/pom.xml index 03b6623..9004cc2 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 nl.pim16aap2 ArmoredElytra - 2.4.13 + 2.4.14 @@ -30,7 +30,7 @@ org.spigotmc spigot-api - 1.15.1-R0.1-SNAPSHOT + 1.16.1-R0.1-SNAPSHOT provided @@ -38,7 +38,7 @@ org.spigotmc spigot - 1.15.1-R0.1-SNAPSHOT + 1.16.1-R0.1-SNAPSHOT provided diff --git a/src/main/java/nl/pim16aap2/armoredElytra/ArmoredElytra.java b/src/main/java/nl/pim16aap2/armoredElytra/ArmoredElytra.java index 7361b69..6c03aa5 100644 --- a/src/main/java/nl/pim16aap2/armoredElytra/ArmoredElytra.java +++ b/src/main/java/nl/pim16aap2/armoredElytra/ArmoredElytra.java @@ -5,10 +5,13 @@ import nl.pim16aap2.armoredElytra.handlers.EventHandlers; import nl.pim16aap2.armoredElytra.handlers.FlyDurabilityHandler; import nl.pim16aap2.armoredElytra.handlers.LoginHandler; import nl.pim16aap2.armoredElytra.handlers.Uninstaller; +import nl.pim16aap2.armoredElytra.nbtEditor.INBTEditor; import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor; +import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor_legacy; import nl.pim16aap2.armoredElytra.util.ArmorTier; import nl.pim16aap2.armoredElytra.util.ArmorTierName; import nl.pim16aap2.armoredElytra.util.ConfigLoader; +import nl.pim16aap2.armoredElytra.util.MinecraftVersion; import nl.pim16aap2.armoredElytra.util.UpdateManager; import nl.pim16aap2.armoredElytra.util.messages.Message; import nl.pim16aap2.armoredElytra.util.messages.Messages; @@ -31,6 +34,9 @@ import java.util.logging.Level; public class ArmoredElytra extends JavaPlugin implements Listener { + private static final MinecraftVersion minecraftVersion = MinecraftVersion + .get(Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]); + private static ArmoredElytra instance; private Messages messages; private ConfigLoader config; @@ -40,10 +46,20 @@ public class ArmoredElytra extends JavaPlugin implements Listener private boolean is1_9; private UpdateManager updateManager; + private INBTEditor nbtEditor; + @Override public void onEnable() { instance = this; + if (minecraftVersion.isOlderThan(MinecraftVersion.v1_8)) + { + myLogger(Level.SEVERE, "Trying to run this plugin on an unsupported version... ABORT!"); + return; + } + + nbtEditor = minecraftVersion.isNewerThan(MinecraftVersion.v1_15) ? new NBTEditor() : new NBTEditor_legacy(); + config = new ConfigLoader(this); messages = new Messages(this); readMessages(); @@ -108,6 +124,11 @@ public class ArmoredElytra extends JavaPlugin implements Listener return messages; } + public INBTEditor getNbtEditor() + { + return nbtEditor; + } + private void readMessages() { armorTierNames.put(ArmorTier.NONE, new ArmorTierName("NONE", "NONE")); // Shouldn't be used. @@ -129,6 +150,11 @@ public class ArmoredElytra extends JavaPlugin implements Listener player.hasPermission("armoredelytra.craft." + ArmorTier.getName(armorTier)); } + public static MinecraftVersion getMinecraftVersion() + { + return minecraftVersion; + } + public boolean playerHasWearPerm(Player player, ArmorTier armorTier) { return getConfigLoader().bypassWearPerm() || @@ -229,7 +255,7 @@ public class ArmoredElytra extends JavaPlugin implements Listener // Check + initialize for the correct version of Minecraft. public boolean compatibleMCVer() { - return NBTEditor.success(); + return minecraftVersion.isNewerThan(MinecraftVersion.v1_8); } public static ArmoredElytra getInstance() diff --git a/src/main/java/nl/pim16aap2/armoredElytra/handlers/CommandHandler.java b/src/main/java/nl/pim16aap2/armoredElytra/handlers/CommandHandler.java index f75f3d2..37cc248 100644 --- a/src/main/java/nl/pim16aap2/armoredElytra/handlers/CommandHandler.java +++ b/src/main/java/nl/pim16aap2/armoredElytra/handlers/CommandHandler.java @@ -1,7 +1,6 @@ package nl.pim16aap2.armoredElytra.handlers; import nl.pim16aap2.armoredElytra.ArmoredElytra; -import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor; import nl.pim16aap2.armoredElytra.util.ArmorTier; import nl.pim16aap2.armoredElytra.util.messages.Message; import org.bukkit.Bukkit; @@ -76,8 +75,9 @@ public class CommandHandler implements CommandExecutor if (allowed) { plugin.elytraReceivedMessage(receiver, armorTier); - newElytra = NBTEditor.addArmorNBTTags(new ItemStack(Material.ELYTRA, 1), armorTier, - plugin.getConfigLoader().unbreakable()); + newElytra = ArmoredElytra.getInstance().getNbtEditor() + .addArmorNBTTags(new ItemStack(Material.ELYTRA, 1), armorTier, + plugin.getConfigLoader().unbreakable()); plugin.giveArmoredElytraToPlayer(receiver, newElytra); } else @@ -106,8 +106,9 @@ public class CommandHandler implements CommandExecutor return false; plugin.elytraReceivedMessage(player, armorTier); - newElytra = NBTEditor.addArmorNBTTags(new ItemStack(Material.ELYTRA, 1), armorTier, - plugin.getConfigLoader().unbreakable()); + newElytra = ArmoredElytra.getInstance().getNbtEditor() + .addArmorNBTTags(new ItemStack(Material.ELYTRA, 1), armorTier, + plugin.getConfigLoader().unbreakable()); plugin.giveArmoredElytraToPlayer(player, newElytra); plugin.myLogger(Level.INFO, ("Giving an armored elytra of the " + ArmorTier.getArmor(armorTier) + " armor tier to player " + player.getName())); diff --git a/src/main/java/nl/pim16aap2/armoredElytra/handlers/EventHandlers.java b/src/main/java/nl/pim16aap2/armoredElytra/handlers/EventHandlers.java index cb56134..4c94b88 100644 --- a/src/main/java/nl/pim16aap2/armoredElytra/handlers/EventHandlers.java +++ b/src/main/java/nl/pim16aap2/armoredElytra/handlers/EventHandlers.java @@ -5,7 +5,6 @@ import com.codingforcookies.armorequip.ArmorListener; import com.codingforcookies.armorequip.ArmorType; import com.codingforcookies.armorequip.DispenserArmorListener; import nl.pim16aap2.armoredElytra.ArmoredElytra; -import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor; import nl.pim16aap2.armoredElytra.util.Action; import nl.pim16aap2.armoredElytra.util.AllowedToWearEnum; import nl.pim16aap2.armoredElytra.util.ArmorTier; @@ -131,13 +130,13 @@ public class EventHandlers implements Listener Integer enchantLevel = enchantments0.get(entry.getKey()); if (enchantLevel != null) { - if (entry.getValue() == enchantLevel && entry.getValue() < entry.getKey().getMaxLevel()) + if (entry.getValue().equals(enchantLevel) && entry.getValue() < entry.getKey().getMaxLevel()) enchantLevel = entry.getValue() + 1; else if (entry.getValue() > enchantLevel) enchantLevel = entry.getValue(); // If the enchantment level has changed, - if (enchantLevel != enchantments0.get(entry.getKey())) + if (!enchantLevel.equals(enchantments0.get(entry.getKey()))) { combined.remove(entry.getKey()); combined.put(entry.getKey(), enchantLevel); @@ -255,7 +254,7 @@ public class EventHandlers implements Listener if (Util.isChestPlate(matTwo)) return Action.CREATE; - ArmorTier tier = NBTEditor.getArmorTier(itemOne); + ArmorTier tier = ArmoredElytra.getInstance().getNbtEditor().getArmorTier(itemOne); if (tier != ArmorTier.NONE) { @@ -269,7 +268,7 @@ public class EventHandlers implements Listener // If the armored elytra is to be combined with another armored elytra of the // same tier... - if (NBTEditor.getArmorTier(itemTwo) == tier) + if (ArmoredElytra.getInstance().getNbtEditor().getArmorTier(itemTwo) == tier) return Action.COMBINE; // If the armored elytra is not of the leather tier, but itemTwo is leather, @@ -306,7 +305,7 @@ public class EventHandlers implements Listener { Action action = isValidInput(itemA, itemB); ArmorTier newTier = ArmorTier.NONE; - ArmorTier curTier = NBTEditor.getArmorTier(itemA); + ArmorTier curTier = ArmoredElytra.getInstance().getNbtEditor().getArmorTier(itemA); short durability = 0; Map enchantments = itemA.getEnchantments(); enchantments = fixEnchantments(enchantments); @@ -356,14 +355,15 @@ public class EventHandlers implements Listener result.addUnsafeEnchantments(enchantments); result.setDurability(durability); - result = NBTEditor.addArmorNBTTags(result, newTier, plugin.getConfigLoader().unbreakable()); + result = ArmoredElytra.getInstance().getNbtEditor() + .addArmorNBTTags(result, newTier, plugin.getConfigLoader().unbreakable()); event.setResult(result); } } // Check if either itemA or itemB is unoccupied. if ((itemA == null || itemB == null) && - NBTEditor.getArmorTier(event.getInventory().getItem(2)) != ArmorTier.NONE) + ArmoredElytra.getInstance().getNbtEditor().getArmorTier(event.getInventory().getItem(2)) != ArmorTier.NONE) // If Item2 is occupied despite itemA or itemB not being occupied. (only for // armored elytra)/ event.setResult(null); @@ -404,7 +404,7 @@ public class EventHandlers implements Listener if (slot == 2 && anvilInventory.getItem(0) != null && anvilInventory.getItem(1) != null && anvilInventory.getItem(2) != null) { - ArmorTier armortier = NBTEditor.getArmorTier(anvilInventory.getItem(2)); + ArmorTier armortier = ArmoredElytra.getInstance().getNbtEditor().getArmorTier(anvilInventory.getItem(2)); // If there's an armored elytra in the final slot... if (armortier != ArmorTier.NONE && plugin.playerHasCraftPerm(player, armortier)) @@ -413,8 +413,9 @@ public class EventHandlers implements Listener // result. // This is done because after putting item0 in AFTER item1, the first letter of // the color code shows up, this gets rid of that problem. - ItemStack result = NBTEditor.addArmorNBTTags(anvilInventory.getItem(2), armortier, - plugin.getConfigLoader().unbreakable()); + ItemStack result = ArmoredElytra.getInstance().getNbtEditor() + .addArmorNBTTags(anvilInventory.getItem(2), armortier, + plugin.getConfigLoader().unbreakable()); // Give the result to the player and clear the anvil's inventory. if (e.isShiftClick()) @@ -465,7 +466,8 @@ public class EventHandlers implements Listener if (p.getInventory().getChestplate() == null) return; - if (NBTEditor.getArmorTier(p.getInventory().getChestplate()) == ArmorTier.NONE) + if (ArmoredElytra.getInstance().getNbtEditor().getArmorTier(p.getInventory().getChestplate()) == + ArmorTier.NONE) return; ItemStack elytra = p.getInventory().getChestplate(); @@ -519,7 +521,7 @@ public class EventHandlers implements Listener !e.getNewArmorPiece().getType().equals(Material.ELYTRA)) return; - ArmorTier armorTier = NBTEditor.getArmorTier(e.getNewArmorPiece()); + ArmorTier armorTier = ArmoredElytra.getInstance().getNbtEditor().getArmorTier(e.getNewArmorPiece()); AllowedToWearEnum allowed = isAllowedToWear(e.getNewArmorPiece(), e.getPlayer(), armorTier); switch (allowed) { diff --git a/src/main/java/nl/pim16aap2/armoredElytra/handlers/FlyDurabilityHandler.java b/src/main/java/nl/pim16aap2/armoredElytra/handlers/FlyDurabilityHandler.java index 8f67210..79fe0f8 100644 --- a/src/main/java/nl/pim16aap2/armoredElytra/handlers/FlyDurabilityHandler.java +++ b/src/main/java/nl/pim16aap2/armoredElytra/handlers/FlyDurabilityHandler.java @@ -1,6 +1,6 @@ package nl.pim16aap2.armoredElytra.handlers; -import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor; +import nl.pim16aap2.armoredElytra.ArmoredElytra; import nl.pim16aap2.armoredElytra.util.ArmorTier; import org.bukkit.Material; import org.bukkit.event.EventHandler; @@ -24,7 +24,7 @@ public class FlyDurabilityHandler implements Listener if (!e.getPlayer().isGliding()) return; - if (NBTEditor.getArmorTier(e.getItem()) != ArmorTier.NONE) + if (ArmoredElytra.getInstance().getNbtEditor().getArmorTier(e.getItem()) != ArmorTier.NONE) e.setCancelled(true); } } diff --git a/src/main/java/nl/pim16aap2/armoredElytra/handlers/Uninstaller.java b/src/main/java/nl/pim16aap2/armoredElytra/handlers/Uninstaller.java index ec7ec53..b745f58 100644 --- a/src/main/java/nl/pim16aap2/armoredElytra/handlers/Uninstaller.java +++ b/src/main/java/nl/pim16aap2/armoredElytra/handlers/Uninstaller.java @@ -1,7 +1,6 @@ package nl.pim16aap2.armoredElytra.handlers; import nl.pim16aap2.armoredElytra.ArmoredElytra; -import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor; import nl.pim16aap2.armoredElytra.util.ArmorTier; import org.bukkit.ChatColor; import org.bukkit.Material; @@ -28,13 +27,12 @@ public class Uninstaller implements Listener { int count = 0; for (ItemStack is : inv) - if (is != null) - if (is.getType() == Material.ELYTRA) - if (NBTEditor.getArmorTier(is) != ArmorTier.NONE) - { - inv.remove(is); - ++count; - } + if (is != null && is.getType() == Material.ELYTRA && + ArmoredElytra.getInstance().getNbtEditor().getArmorTier(is) != ArmorTier.NONE) + { + inv.remove(is); + ++count; + } return count; } diff --git a/src/main/java/nl/pim16aap2/armoredElytra/nbtEditor/INBTEditor.java b/src/main/java/nl/pim16aap2/armoredElytra/nbtEditor/INBTEditor.java new file mode 100644 index 0000000..3b91929 --- /dev/null +++ b/src/main/java/nl/pim16aap2/armoredElytra/nbtEditor/INBTEditor.java @@ -0,0 +1,25 @@ +package nl.pim16aap2.armoredElytra.nbtEditor; + +import nl.pim16aap2.armoredElytra.util.ArmorTier; +import org.bukkit.inventory.ItemStack; + +public interface INBTEditor +{ + /** + * Adds a given {@link ArmorTier} to an item. The item will be cloned. + * + * @param item The item. + * @param armorTier The {@link ArmorTier} that will be added to it. + * @param unbreakable Whether the resulting item should be unbreakable. + * @return The NEW item. + */ + ItemStack addArmorNBTTags(ItemStack item, ArmorTier armorTier, boolean unbreakable); + + /** + * Checks which {@link ArmorTier} is on an item. + * + * @param item The item to check. + * @return The {@link ArmorTier} that is on the item. If none is found, {@link ArmorTier#NONE} is returned. + */ + ArmorTier getArmorTier(ItemStack item); +} diff --git a/src/main/java/nl/pim16aap2/armoredElytra/nbtEditor/NBTEditor.java b/src/main/java/nl/pim16aap2/armoredElytra/nbtEditor/NBTEditor.java index dab6f46..eef9402 100644 --- a/src/main/java/nl/pim16aap2/armoredElytra/nbtEditor/NBTEditor.java +++ b/src/main/java/nl/pim16aap2/armoredElytra/nbtEditor/NBTEditor.java @@ -3,373 +3,79 @@ package nl.pim16aap2.armoredElytra.nbtEditor; import nl.pim16aap2.armoredElytra.ArmoredElytra; import nl.pim16aap2.armoredElytra.util.ArmorTier; import org.bukkit.Bukkit; -import org.bukkit.ChatColor; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeModifier; +import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.function.Function; -import java.util.logging.Level; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.Collection; +import java.util.UUID; -public class NBTEditor +public class NBTEditor implements INBTEditor { - private static final String versionString = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; - private static final MinecraftVersion minecraftVersion = MinecraftVersion.get(versionString); - private static final String NMSbase = "net.minecraft.server." + versionString + "."; - private static final String CraftBase = "org.bukkit.craftbukkit." + versionString + "."; - - private static Method asNMSCopy; - private static Method asBukkitCopy; - private static Class NMSItemStack; - private static Class CraftItemStack; - - private static Class NBTTagCompound; - private static Class NBTTagList; - private static Class NBTBase; - private static Class NBTTagString; - private static Class NBTTagByte; - private static Class NBTTagInt; - - private static Method hasTag; - private static Method getTag; - - private static Method addCompound; - - private static Method setTag; - - private static Method setCompoundByte; - private static Method setCompoundTagList; - - private static Method getCompoundTagList; - private static Method getTagListSize; - private static Method getTagListAtIndex; - - private static Constructor NBTTagStringCtor; - private static Constructor NBTTagByteCtor; - private static Constructor NBTTagIntCtor; - - private static boolean success; - - private static Function getArmorValue; - - - private static final Pattern pattern_findAmount_double = Pattern.compile("Amount:[0-9]+.[0-9]+d[,}]*"); - private static final Pattern pattern_findAmount_int = Pattern.compile("Amount:[0-9]+[,}]*"); - private static final Pattern pattern_getDouble = Pattern.compile("[0-9]+.[0-9]+"); - private static final Pattern pattern_getInt = Pattern.compile("[0-9]+"); - private static final Pattern pattern_isArmor = Pattern.compile("\"generic.armor\""); - - - static + public NBTEditor() { - if (minecraftVersion == null) - success = false; - else - try - { - // 1.13 and lower use integer armor values while 1.14 and newer use double armor values. - getArmorValue = minecraftVersion.isNewerThan(MinecraftVersion.v1_13) ? - NBTEditor::getArmorValueDouble : NBTEditor::getArmorValueInt; - - NMSItemStack = getNMSClass("ItemStack"); - hasTag = NMSItemStack.getMethod("hasTag"); - getTag = NMSItemStack.getMethod("getTag"); - - CraftItemStack = getCraftClass("inventory.CraftItemStack"); - asNMSCopy = CraftItemStack.getMethod("asNMSCopy", ItemStack.class); - asBukkitCopy = CraftItemStack.getMethod("asBukkitCopy", NMSItemStack); - - NBTBase = getNMSClass("NBTBase"); - - NBTTagString = getNMSClass("NBTTagString"); - NBTTagStringCtor = NBTTagString.getDeclaredConstructor(String.class); - NBTTagStringCtor.setAccessible(true); - - NBTTagByte = getNMSClass("NBTTagByte"); - NBTTagByteCtor = NBTTagByte.getDeclaredConstructor(byte.class); - NBTTagByteCtor.setAccessible(true); - - NBTTagInt = getNMSClass("NBTTagInt"); - NBTTagIntCtor = NBTTagInt.getDeclaredConstructor(int.class); - NBTTagIntCtor.setAccessible(true); - - NBTTagCompound = getNMSClass("NBTTagCompound"); - setTag = NBTTagCompound.getMethod("set", String.class, NBTBase); - - NBTTagList = getNMSClass("NBTTagList"); - getTagListSize = NBTTagList.getMethod("size"); - getTagListAtIndex = NBTTagList.getMethod("get", int.class); - // Starting in 1.14, you also need to provide an int value when adding nbt tags. - try - { - addCompound = NBTTagList.getMethod("add", NBTBase); - } - catch (Exception e) - { - addCompound = NBTTagList.getMethod("add", int.class, NBTBase); - } - - setCompoundTagList = NBTTagCompound.getMethod("set", String.class, NBTBase); - setCompoundByte = NBTTagCompound.getMethod("set", String.class, NBTBase); - getCompoundTagList = NBTTagCompound.getMethod("getList", String.class, int.class); - - success = true; - } - catch (NoSuchMethodException | SecurityException | ClassNotFoundException e) - { - e.printStackTrace(); - success = false; - } - } - - private static void addCompound(Object instance, Object nbtbase) - throws IllegalAccessException, IllegalArgumentException, InvocationTargetException - { - if (addCompound.getParameterCount() == 2) - addCompound.invoke(instance, 0, nbtbase); - else - addCompound.invoke(instance, nbtbase); - } - - // Add armor to the supplied item, based on the armorTier. - public static ItemStack addArmorNBTTags(ItemStack item, ArmorTier armorTier, boolean unbreakable) - { - try - { - ItemMeta itemmeta = item.getItemMeta(); - int armorProtection = ArmorTier.getArmor(armorTier); - int armorToughness = ArmorTier.getToughness(armorTier); - - itemmeta.setDisplayName(ArmoredElytra.getInstance().getArmoredElytraName(armorTier)); - final String message = ChatColor.stripColor(ArmoredElytra.getInstance().getElytraLore(armorTier)); - if (message != null) - itemmeta.setLore(Arrays.asList(message)); - - item.setItemMeta(itemmeta); - - Object nmsStack = asNMSCopy.invoke(null, item); - Object compound = ((boolean) hasTag.invoke(nmsStack) ? getTag.invoke(nmsStack) : - NBTTagCompound.newInstance()); - Object modifiers = NBTTagList.newInstance(); - Object armor = NBTTagCompound.newInstance(); // I should be able to simply add custom tags here! - setTag.invoke(armor, "AttributeName", NBTTagStringCtor.newInstance("generic.armor")); - setTag.invoke(armor, "Name", NBTTagStringCtor.newInstance("generic.armor")); - setTag.invoke(armor, "Amount", NBTTagIntCtor.newInstance(armorProtection)); - setTag.invoke(armor, "Operation", NBTTagIntCtor.newInstance(0)); - setTag.invoke(armor, "UUIDLeast", NBTTagIntCtor.newInstance(894654)); - setTag.invoke(armor, "UUIDMost", NBTTagIntCtor.newInstance(2872)); - setTag.invoke(armor, "Slot", NBTTagStringCtor.newInstance("chest")); - addCompound(modifiers, armor); - - Object armorTough = NBTTagCompound.newInstance(); - setTag.invoke(armorTough, "AttributeName", NBTTagStringCtor.newInstance("generic.armorToughness")); - setTag.invoke(armorTough, "Name", NBTTagStringCtor.newInstance("generic.armorToughness")); - setTag.invoke(armorTough, "Amount", NBTTagIntCtor.newInstance(armorToughness)); - setTag.invoke(armorTough, "Operation", NBTTagIntCtor.newInstance(0)); - setTag.invoke(armorTough, "UUIDLeast", NBTTagIntCtor.newInstance(894654)); - setTag.invoke(armorTough, "UUIDMost", NBTTagIntCtor.newInstance(2872)); - setTag.invoke(armorTough, "Slot", NBTTagStringCtor.newInstance("chest")); - addCompound(modifiers, armorTough); - - if (unbreakable) - setCompoundByte.invoke(compound, "Unbreakable", NBTTagByteCtor.newInstance((byte) 1)); - - setCompoundTagList.invoke(compound, "AttributeModifiers", modifiers); - item = (ItemStack) asBukkitCopy.invoke(null, nmsStack); - } - catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e) - { - e.printStackTrace(); - } - return item; } /** - * Gets the armor amount from an NBT attribute. - * - * @param string The NBT attribute as a String. - * @param findAmount The {@link Pattern} that finds the amount in a String. - * @param parseAmount The {@link Pattern} that extracts the amount from the String found by "findAmount". - * @return The String containing the armor value. This can either be an integer or a double value. + * {@inheritDoc} */ - private static String getArmorAmount(final String string, final Pattern findAmount, final Pattern parseAmount) + @Override + public ItemStack addArmorNBTTags(ItemStack item, ArmorTier armorTier, boolean unbreakable) { - final Matcher amountMatcher = findAmount.matcher(string); - if (!amountMatcher.find()) - { - ArmoredElytra.getInstance() - .myLogger(Level.SEVERE, - "Failed to obtain armor value from NBT! No armor amount found: " + string); - return "0"; - } + ItemStack ret = new ItemStack(item); + ItemMeta meta = ret.hasItemMeta() ? ret.getItemMeta() : Bukkit.getItemFactory().getItemMeta(ret.getType()); + if (meta == null) + throw new IllegalArgumentException("Tried to add armor to invalid item: " + item); - final String amountName = amountMatcher.group(); - final Matcher amountString = parseAmount.matcher(amountName); - if (!amountString.find()) - { - ArmoredElytra.getInstance() - .myLogger(Level.SEVERE, - "Failed to obtain armor value from NBT! Could not parse value: " + amountName); - return "0"; - } - return amountString.group(); + overwriteNBTValue(meta, Attribute.GENERIC_ARMOR, ArmorTier.getArmor(armorTier), "generic.armor"); + + if (ArmorTier.getToughness(armorTier) > 0) + overwriteNBTValue(meta, Attribute.GENERIC_ARMOR_TOUGHNESS, ArmorTier.getToughness(armorTier), + "generic.armorToughness"); + + meta.setUnbreakable(unbreakable); + meta.setDisplayName(ArmoredElytra.getInstance().getArmoredElytraName(armorTier)); + + ret.setItemMeta(meta); + return ret; + } + + private void overwriteNBTValue(ItemMeta meta, Attribute attribute, double value, String modifierName) + { + if (meta.hasAttributeModifiers()) + meta.removeAttributeModifier(attribute); + + AttributeModifier attributeModifier = new AttributeModifier(UUID.randomUUID(), modifierName, value, + AttributeModifier.Operation.ADD_NUMBER, EquipmentSlot.CHEST); + meta.addAttributeModifier(attribute, attributeModifier); } /** - * Gets the amount of an attribute in the format of: "something something, amount:2.0d,". The amount is cast to and - * returned as an integer value. - * - * @param string The nbt attribute as String. - * @return The integer value of the amount. + * {@inheritDoc} */ - private static int getArmorValueDouble(final String string) + @Override + public ArmorTier getArmorTier(ItemStack item) { - try - { - return (int) Double.parseDouble(getArmorAmount(string, pattern_findAmount_double, pattern_getDouble)); - } - catch (NumberFormatException e) - { - e.printStackTrace(); - } - return 0; - } - - /** - * Gets the amount of an attribute in the format of: "something something, amount:2.0d,". The amount is cast to and - * returned as an integer value. - * - * @param string The nbt attribute as String. - * @return The integer value of the amount. - */ - private static int getArmorValueInt(final String string) - { - try - { - return Integer.parseInt(getArmorAmount(string, pattern_findAmount_int, pattern_getInt)); - } - catch (NumberFormatException e) - { - e.printStackTrace(); - } - return 0; - } - - public static ArmorTier getArmorTier(ItemStack item) - { -// { -// double armorValue_2 = 0; -// net.minecraft.server.v1_15_R1.ItemStack nmsStack_2 = org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack -// .asNMSCopy(item); -// -// net.minecraft.server.v1_15_R1.NBTTagCompound compound_2 = -// nmsStack_2.hasTag() ? nmsStack_2.getTag() : new net.minecraft.server.v1_15_R1.NBTTagCompound(); -// -// net.minecraft.server.v1_15_R1.NBTTagList modifiers_2 = compound_2.getList("AttributeModifiers", 10); -// } - - try - { - Object compound = getTag.invoke(asNMSCopy.invoke(null, item)); - if (compound == null) - return ArmorTier.NONE; - - Object modifiers = getCompoundTagList.invoke(compound, "AttributeModifiers", 10); // Number 10 = Compound. - int size = (int) getTagListSize.invoke(modifiers); - - - for (int idx = 0; idx < size; ++idx) - { -// final String result = modifiers.get(idx).asString(); - final String result = getTagListAtIndex.invoke(modifiers, idx).toString(); - - if (!pattern_isArmor.matcher(result).find()) - continue; - - int armorValue = getArmorValue.apply(result); - switch (armorValue) - { - case 3: - return ArmorTier.LEATHER; - case 5: - return ArmorTier.GOLD; - case 6: - return ArmorTier.IRON; - case 8: - return ArmorTier.DIAMOND; - default: - return ArmorTier.NONE; - } - } + if (item == null) return ArmorTier.NONE; - } - catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) - { - e.printStackTrace(); + + ItemMeta meta = item.getItemMeta(); + if (meta == null) return ArmorTier.NONE; - } - } - private static Class getNMSClass(String name) - throws ClassNotFoundException - { - return Class.forName(NMSbase + name); - } + Collection attributeModifiers = meta.getAttributeModifiers(Attribute.GENERIC_ARMOR); + if (attributeModifiers == null) + return ArmorTier.NONE; - private static Class getCraftClass(String name) - throws ClassNotFoundException - { - return Class.forName(CraftBase + name); - } - - public static boolean success() - { - return success; - } - - private enum MinecraftVersion - { - v1_9("1_9", 0), - v1_10("1_10", 1), - v1_11("1_11", 2), - v1_12("1_12", 3), - v1_13("1_13", 4), - v1_14("1_14", 5), - v1_15("1_15", 6); - - private int index; - private String name; - - MinecraftVersion(String name, int index) + for (final AttributeModifier attributeModifier : attributeModifiers) { - this.name = name; - this.index = index; + ArmorTier armorTier = ArmorTier.getArmorTier((int) attributeModifier.getAmount()); + if (armorTier != ArmorTier.NONE) + return armorTier; } - /** - * Checks if this version is newer than the other version. - * - * @param other The other version to check against. - * @return True if this version is newer than the other version. - */ - public boolean isNewerThan(final MinecraftVersion other) - { - return this.index > other.index; - } - - public static MinecraftVersion get(final String versionName) - { - if (versionName == null) - return null; - for (final MinecraftVersion mcVersion : MinecraftVersion.values()) - if (versionName.contains(mcVersion.name)) - return mcVersion; - return null; - } + return ArmorTier.NONE; } } diff --git a/src/main/java/nl/pim16aap2/armoredElytra/nbtEditor/NBTEditor_legacy.java b/src/main/java/nl/pim16aap2/armoredElytra/nbtEditor/NBTEditor_legacy.java new file mode 100644 index 0000000..1d40d72 --- /dev/null +++ b/src/main/java/nl/pim16aap2/armoredElytra/nbtEditor/NBTEditor_legacy.java @@ -0,0 +1,330 @@ +package nl.pim16aap2.armoredElytra.nbtEditor; + +import nl.pim16aap2.armoredElytra.ArmoredElytra; +import nl.pim16aap2.armoredElytra.util.ArmorTier; +import nl.pim16aap2.armoredElytra.util.MinecraftVersion; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class NBTEditor_legacy implements INBTEditor +{ + private static final String versionString = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; + private static final MinecraftVersion minecraftVersion = ArmoredElytra.getMinecraftVersion(); + private static final String NMSbase = "net.minecraft.server." + versionString + "."; + private static final String CraftBase = "org.bukkit.craftbukkit." + versionString + "."; + + private static Method asNMSCopy; + private static Method asBukkitCopy; + private static Class NMSItemStack; + private static Class CraftItemStack; + + private static Class NBTTagCompound; + private static Class NBTTagList; + private static Class NBTBase; + private static Class NBTTagString; + private static Class NBTTagByte; + private static Class NBTTagInt; + + private static Method hasTag; + private static Method getTag; + + private static Method addCompound; + + private static Method setTag; + + private static Method setCompoundByte; + private static Method setCompoundTagList; + + private static Method getCompoundTagList; + private static Method getTagListSize; + private static Method getTagListAtIndex; + + private static Constructor NBTTagStringCtor; + private static Constructor NBTTagByteCtor; + private static Constructor NBTTagIntCtor; + + private static boolean success; + + private static Function getArmorValue; + + + private static final Pattern pattern_findAmount_double = Pattern.compile("Amount:[0-9]+.[0-9]+d[,}]*"); + private static final Pattern pattern_findAmount_int = Pattern.compile("Amount:[0-9]+[,}]*"); + private static final Pattern pattern_getDouble = Pattern.compile("[0-9]+.[0-9]+"); + private static final Pattern pattern_getInt = Pattern.compile("[0-9]+"); + private static final Pattern pattern_isArmor = Pattern.compile("\"generic.armor\""); + + + static + { + if (minecraftVersion == null) + success = false; + else + try + { + // 1.13 and lower use integer armor values while 1.14 and newer use double armor values. + getArmorValue = minecraftVersion.isNewerThan(MinecraftVersion.v1_13) ? + NBTEditor_legacy::getArmorValueDouble : NBTEditor_legacy::getArmorValueInt; + + NMSItemStack = getNMSClass("ItemStack"); + hasTag = NMSItemStack.getMethod("hasTag"); + getTag = NMSItemStack.getMethod("getTag"); + + CraftItemStack = getCraftClass("inventory.CraftItemStack"); + asNMSCopy = CraftItemStack.getMethod("asNMSCopy", ItemStack.class); + asBukkitCopy = CraftItemStack.getMethod("asBukkitCopy", NMSItemStack); + + NBTBase = getNMSClass("NBTBase"); + + NBTTagString = getNMSClass("NBTTagString"); + NBTTagStringCtor = NBTTagString.getDeclaredConstructor(String.class); + NBTTagStringCtor.setAccessible(true); + + NBTTagByte = getNMSClass("NBTTagByte"); + NBTTagByteCtor = NBTTagByte.getDeclaredConstructor(byte.class); + NBTTagByteCtor.setAccessible(true); + + NBTTagInt = getNMSClass("NBTTagInt"); + NBTTagIntCtor = NBTTagInt.getDeclaredConstructor(int.class); + NBTTagIntCtor.setAccessible(true); + + NBTTagCompound = getNMSClass("NBTTagCompound"); + setTag = NBTTagCompound.getMethod("set", String.class, NBTBase); + + NBTTagList = getNMSClass("NBTTagList"); + getTagListSize = NBTTagList.getMethod("size"); + getTagListAtIndex = NBTTagList.getMethod("get", int.class); + // Starting in 1.14, you also need to provide an int value when adding nbt tags. + try + { + addCompound = NBTTagList.getMethod("add", NBTBase); + } + catch (Exception e) + { + addCompound = NBTTagList.getMethod("add", int.class, NBTBase); + } + + setCompoundTagList = NBTTagCompound.getMethod("set", String.class, NBTBase); + setCompoundByte = NBTTagCompound.getMethod("set", String.class, NBTBase); + getCompoundTagList = NBTTagCompound.getMethod("getList", String.class, int.class); + + success = true; + } + catch (NoSuchMethodException | SecurityException | ClassNotFoundException e) + { + e.printStackTrace(); + success = false; + } + } + + /** + * {@inheritDoc} + */ + @Override + public ArmorTier getArmorTier(ItemStack item) + { + try + { + Object compound = getTag.invoke(asNMSCopy.invoke(null, item)); + if (compound == null) + return ArmorTier.NONE; + + Object modifiers = getCompoundTagList.invoke(compound, "AttributeModifiers", 10); // Number 10 = Compound. + int size = (int) getTagListSize.invoke(modifiers); + + + for (int idx = 0; idx < size; ++idx) + { + final String result = getTagListAtIndex.invoke(modifiers, idx).toString(); + + if (!pattern_isArmor.matcher(result).find()) + continue; + + int armorValue = getArmorValue.apply(result); + switch (armorValue) + { + case 3: + return ArmorTier.LEATHER; + case 5: + return ArmorTier.GOLD; + case 6: + return ArmorTier.IRON; + case 8: + return ArmorTier.DIAMOND; + default: + return ArmorTier.NONE; + } + } + return ArmorTier.NONE; + } + catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) + { + e.printStackTrace(); + return ArmorTier.NONE; + } + } + + /** + * {@inheritDoc} + */ + @Override + public ItemStack addArmorNBTTags(ItemStack item, ArmorTier armorTier, boolean unbreakable) + { + try + { + ItemMeta itemmeta = item.getItemMeta(); + int armorProtection = ArmorTier.getArmor(armorTier); + int armorToughness = ArmorTier.getToughness(armorTier); + + itemmeta.setDisplayName(ArmoredElytra.getInstance().getArmoredElytraName(armorTier)); + final String message = ChatColor.stripColor(ArmoredElytra.getInstance().getElytraLore(armorTier)); + if (message != null) + itemmeta.setLore(Arrays.asList(message)); + + item.setItemMeta(itemmeta); + + Object nmsStack = asNMSCopy.invoke(null, item); + Object compound = ((boolean) hasTag.invoke(nmsStack) ? getTag.invoke(nmsStack) : + NBTTagCompound.newInstance()); + Object modifiers = NBTTagList.newInstance(); + Object armor = NBTTagCompound.newInstance(); // I should be able to simply add custom tags here! + setTag.invoke(armor, "AttributeName", NBTTagStringCtor.newInstance("generic.armor")); + setTag.invoke(armor, "Name", NBTTagStringCtor.newInstance("generic.armor")); + setTag.invoke(armor, "Amount", NBTTagIntCtor.newInstance(armorProtection)); + setTag.invoke(armor, "Operation", NBTTagIntCtor.newInstance(0)); + setTag.invoke(armor, "UUIDLeast", NBTTagIntCtor.newInstance(894654)); + setTag.invoke(armor, "UUIDMost", NBTTagIntCtor.newInstance(2872)); + setTag.invoke(armor, "Slot", NBTTagStringCtor.newInstance("chest")); + addCompound(modifiers, armor); + + Object armorTough = NBTTagCompound.newInstance(); + setTag.invoke(armorTough, "AttributeName", NBTTagStringCtor.newInstance("generic.armorToughness")); + setTag.invoke(armorTough, "Name", NBTTagStringCtor.newInstance("generic.armorToughness")); + setTag.invoke(armorTough, "Amount", NBTTagIntCtor.newInstance(armorToughness)); + setTag.invoke(armorTough, "Operation", NBTTagIntCtor.newInstance(0)); + setTag.invoke(armorTough, "UUIDLeast", NBTTagIntCtor.newInstance(894654)); + setTag.invoke(armorTough, "UUIDMost", NBTTagIntCtor.newInstance(2872)); + setTag.invoke(armorTough, "Slot", NBTTagStringCtor.newInstance("chest")); + addCompound(modifiers, armorTough); + + if (unbreakable) + setCompoundByte.invoke(compound, "Unbreakable", NBTTagByteCtor.newInstance((byte) 1)); + + setCompoundTagList.invoke(compound, "AttributeModifiers", modifiers); + item = (ItemStack) asBukkitCopy.invoke(null, nmsStack); + } + catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e) + { + e.printStackTrace(); + } + return item; + } + + private static void addCompound(Object instance, Object nbtbase) + throws IllegalAccessException, IllegalArgumentException, InvocationTargetException + { + if (addCompound.getParameterCount() == 2) + addCompound.invoke(instance, 0, nbtbase); + else + addCompound.invoke(instance, nbtbase); + } + + /** + * Gets the armor amount from an NBT attribute. + * + * @param string The NBT attribute as a String. + * @param findAmount The {@link Pattern} that finds the amount in a String. + * @param parseAmount The {@link Pattern} that extracts the amount from the String found by "findAmount". + * @return The String containing the armor value. This can either be an integer or a double value. + */ + private static String getArmorAmount(final String string, final Pattern findAmount, final Pattern parseAmount) + { + final Matcher amountMatcher = findAmount.matcher(string); + if (!amountMatcher.find()) + { + ArmoredElytra.getInstance() + .myLogger(Level.SEVERE, + "Failed to obtain armor value from NBT! No armor amount found: " + string); + return "0"; + } + + final String amountName = amountMatcher.group(); + final Matcher amountString = parseAmount.matcher(amountName); + if (!amountString.find()) + { + ArmoredElytra.getInstance() + .myLogger(Level.SEVERE, + "Failed to obtain armor value from NBT! Could not parse value: " + amountName); + return "0"; + } + return amountString.group(); + } + + /** + * Gets the amount of an attribute in the format of: "something something, amount:2.0d,". The amount is cast to and + * returned as an integer value. + * + * @param string The nbt attribute as String. + * @return The integer value of the amount. + */ + private static int getArmorValueDouble(final String string) + { + try + { + return (int) Double.parseDouble(getArmorAmount(string, pattern_findAmount_double, pattern_getDouble)); + } + catch (NumberFormatException e) + { + e.printStackTrace(); + } + return 0; + } + + /** + * Gets the amount of an attribute in the format of: "something something, amount:2.0d,". The amount is cast to and + * returned as an integer value. + * + * @param string The nbt attribute as String. + * @return The integer value of the amount. + */ + private static int getArmorValueInt(final String string) + { + try + { + return Integer.parseInt(getArmorAmount(string, pattern_findAmount_int, pattern_getInt)); + } + catch (NumberFormatException e) + { + e.printStackTrace(); + } + return 0; + } + + private static Class getNMSClass(String name) + throws ClassNotFoundException + { + return Class.forName(NMSbase + name); + } + + private static Class getCraftClass(String name) + throws ClassNotFoundException + { + return Class.forName(CraftBase + name); + } + + public static boolean success() + { + return success; + } +} diff --git a/src/main/java/nl/pim16aap2/armoredElytra/util/ArmorTier.java b/src/main/java/nl/pim16aap2/armoredElytra/util/ArmorTier.java index 8b0dfe4..042a2a0 100644 --- a/src/main/java/nl/pim16aap2/armoredElytra/util/ArmorTier.java +++ b/src/main/java/nl/pim16aap2/armoredElytra/util/ArmorTier.java @@ -1,10 +1,10 @@ package nl.pim16aap2.armoredElytra.util; +import org.bukkit.Material; + import java.util.HashMap; import java.util.Map; -import org.bukkit.Material; - public enum ArmorTier { // Tier: armor-value, armor-toughness, repair @@ -19,7 +19,8 @@ public enum ArmorTier private final int toughness; private final Material repair; private final String name; - private static Map map = new HashMap<>(); + private static Map map = new HashMap<>(); + private static Map armorValueMap = new HashMap<>(); private ArmorTier (int armor, int toughness, Material repair, String name) { @@ -42,9 +43,18 @@ public enum ArmorTier public static ArmorTier valueOfName (String name) { return map.get(name); } + public static ArmorTier getArmorTier(int armor) + { + ArmorTier tier = armorValueMap.get(armor); + return tier == null ? ArmorTier.NONE : tier; + } + static { for (ArmorTier tier : ArmorTier.values()) + { map.put(tier.name, tier); + armorValueMap.put(tier.armor, tier); + } } } diff --git a/src/main/java/nl/pim16aap2/armoredElytra/util/MinecraftVersion.java b/src/main/java/nl/pim16aap2/armoredElytra/util/MinecraftVersion.java new file mode 100644 index 0000000..fff4e0b --- /dev/null +++ b/src/main/java/nl/pim16aap2/armoredElytra/util/MinecraftVersion.java @@ -0,0 +1,61 @@ +package nl.pim16aap2.armoredElytra.util; + +public enum MinecraftVersion +{ + v1_6("1_6", 0), + v1_7("1_7", 1), + v1_8("1_8", 2), + v1_9("1_9", 3), + v1_10("1_10", 4), + v1_11("1_11", 5), + v1_12("1_12", 6), + v1_13("1_13", 7), + v1_14("1_14", 8), + v1_15("1_15", 9), + v1_16("1_16", 10), + v1_17("1_17", 11), + v1_18("1_18", 12), + UNKNOWN("UNKNOWN", 99999), + ; + + private int index; + private String name; + + MinecraftVersion(String name, int index) + { + this.name = name; + this.index = index; + } + + /** + * Checks if this version is newer than the other version. + * + * @param other The other version to check against. + * @return True if this version is newer than the other version. + */ + public boolean isNewerThan(final MinecraftVersion other) + { + return this.index > other.index; + } + + /** + * Checks if this version is older than the other version. + * + * @param other The other version to check against. + * @return True if this version is older than the other version. + */ + public boolean isOlderThan(final MinecraftVersion other) + { + return this.index < other.index; + } + + public static MinecraftVersion get(final String versionName) + { + if (versionName == null) + return null; + for (final MinecraftVersion mcVersion : MinecraftVersion.values()) + if (versionName.contains(mcVersion.name)) + return mcVersion; + return MinecraftVersion.UNKNOWN; + } +}