Add initial 1.16+ support

- Added initial 1.16+ support. This basically means that all the old functions are supposed to work, but none of the new ones are implemented yet (netherite, crafting in smithy). The '+' part refers to the fact that the NBTEditor has a new 1.16+ implementation that uses the Spigot API, which means that it should work just fine in newer versions.
- Using .equals() instead of == for some Integer/Double comparisons.
This commit is contained in:
Pim van der Loos 2020-06-27 12:59:17 +02:00
parent 1053e17b1d
commit 3fc27ca1fc
No known key found for this signature in database
GPG Key ID: C16F020ADAE6D5A8
11 changed files with 538 additions and 379 deletions

View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>nl.pim16aap2</groupId>
<artifactId>ArmoredElytra</artifactId>
<version>2.4.13</version>
<version>2.4.14</version>
<repositories>
<repository>
@ -30,7 +30,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.15.1-R0.1-SNAPSHOT</version>
<version>1.16.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
@ -38,7 +38,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.15.1-R0.1-SNAPSHOT</version>
<version>1.16.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>

View File

@ -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()

View File

@ -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()));

View File

@ -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<Enchantment, Integer> 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)
{

View File

@ -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);
}
}

View File

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

View File

@ -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);
}

View File

@ -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<String, Integer> 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<AttributeModifier> 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;
}
}

View File

@ -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<String, Integer> 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;
}
}

View File

@ -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<String, ArmorTier> map = new HashMap<>();
private static Map<String, ArmorTier> map = new HashMap<>();
private static Map<Integer, ArmorTier> 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);
}
}
}

View File

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