Add support for Netherite, improve tier retrieval

- Added support for creation and general usage of NETHERITE ArmoredElytras.
- Improved ArmorTier retrieval on 1.16+. A PersistentDataContainer is now stored in the item meta to keep track of the armortier of an item. Instead of using the armorvalue of an item to figure out which armortier an item is (which is how it was done before), this container is used instead. This is not only simpler (and probably more reliable), but it also solves the issue where armor values caused collisions between two types (e.g. gold and chain or diamond and netherite).
- Added Netherite tier to translation system.
This commit is contained in:
Pim van der Loos 2020-06-27 15:40:41 +02:00
parent 3fc27ca1fc
commit 9ddc4f9f61
No known key found for this signature in database
GPG Key ID: C16F020ADAE6D5A8
11 changed files with 979 additions and 493 deletions

View File

@ -57,7 +57,7 @@ public class ArmoredElytra extends JavaPlugin implements Listener
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);
@ -82,7 +82,8 @@ public class ArmoredElytra extends JavaPlugin implements Listener
// Load the files for the correct version of Minecraft.
if (compatibleMCVer())
{
Bukkit.getPluginManager().registerEvents(new EventHandlers(this, is1_9), this);
Bukkit.getPluginManager()
.registerEvents(new EventHandlers(this, is1_9, config.craftingInSmithingTable()), this);
getCommand("ArmoredElytra").setExecutor(new CommandHandler(this));
}
else
@ -142,6 +143,8 @@ public class ArmoredElytra extends JavaPlugin implements Listener
messages.getString(Message.TIER_SHORT_IRON)));
armorTierNames.put(ArmorTier.DIAMOND, new ArmorTierName(messages.getString(Message.TIER_DIAMOND),
messages.getString(Message.TIER_SHORT_DIAMOND)));
armorTierNames.put(ArmorTier.NETHERITE, new ArmorTierName(messages.getString(Message.TIER_NETHERITE),
messages.getString(Message.TIER_SHORT_NETHERITE)));
}
public boolean playerHasCraftPerm(Player player, ArmorTier armorTier)

View File

@ -39,10 +39,12 @@ public class EventHandlers implements Listener
private final Consumer<AnvilInventory> cleanAnvilInventory;
private final Consumer<Player> moveChestplateToInventory;
private final boolean creationEnabled;
public EventHandlers(ArmoredElytra plugin, boolean is1_9)
public EventHandlers(ArmoredElytra plugin, boolean is1_9, boolean creationEnabled)
{
this.plugin = plugin;
this.creationEnabled = creationEnabled;
initializeArmorEquipEvent();
if (is1_9)
{
@ -197,6 +199,9 @@ public class EventHandlers implements Listener
else if (repairItem.getType().equals(Material.DIAMOND))
mult *= (100.0f / plugin.getConfigLoader().DIAMONDS_TO_FULL());
else if (repairItem.getType().equals(XMaterial.NETHERITE_INGOT.parseMaterial()))
mult *= (100.0f / plugin.getConfigLoader().NETHERITE_TO_FULL());
int maxDurability = Material.ELYTRA.getMaxDurability();
int newDurability = (int) (curDur - (maxDurability * mult));
return (short) (newDurability <= 0 ? 0 : newDurability);
@ -264,12 +269,13 @@ public class EventHandlers implements Listener
// If the armored elytra is to be repaired using its repair item...
if (ArmorTier.getRepairItem(tier) == matTwo)
return Action.REPAIR;
return itemOne.getDurability() == 0 ? Action.NONE : Action.REPAIR;
// If the armored elytra is to be combined with another armored elytra of the
// same tier...
// TODO: Should this also be disabled by "creationEnabled"?
if (ArmoredElytra.getInstance().getNbtEditor().getArmorTier(itemTwo) == tier)
return Action.COMBINE;
return creationEnabled ? Action.COMBINE : Action.NONE;
// If the armored elytra is not of the leather tier, but itemTwo is leather,
// Pick the block action, as that would repair the elytra by default (vanilla).
@ -290,6 +296,11 @@ public class EventHandlers implements Listener
ItemStack itemB = event.getInventory().getItem(1);
ItemStack result = null;
// if (itemA != null && itemB == null)
// {
//
// }
if (itemA != null && itemB != null)
// If itemB is the elytra, while itemA isn't, switch itemA and itemB.
if (itemB.getType() == Material.ELYTRA && itemA.getType() != Material.ELYTRA)
@ -361,11 +372,11 @@ public class EventHandlers implements Listener
}
}
// Check if either itemA or itemB is unoccupied.
if ((itemA == null || itemB == null) &&
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)/
// If one of the input items is null and the other an armored elytra, remove the result.
// This prevent some naming issues.
// TODO: Allow renaming armored elytras.
if ((itemA == null ^ itemB == null) &&
ArmoredElytra.getInstance().getNbtEditor().getArmorTier(itemA == null ? itemB : itemA) != ArmorTier.NONE)
event.setResult(null);
player.updateInventory();
}

View File

@ -6,7 +6,8 @@ import org.bukkit.inventory.ItemStack;
public interface INBTEditor
{
/**
* Adds a given {@link ArmorTier} to an item. The item will be cloned.
* Adds a given {@link ArmorTier} to an item. The item will be cloned. Note that setting the armor tier to {@link
* ArmorTier#NONE} has no effect (besides making a copy of the item).
*
* @param item The item.
* @param armorTier The {@link ArmorTier} that will be added to it.

View File

@ -3,17 +3,33 @@ package nl.pim16aap2.armoredElytra.nbtEditor;
import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.util.ArmorTier;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
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 org.bukkit.persistence.PersistentDataType;
import java.util.Collection;
import java.util.UUID;
// TODO: Consider using static UUIDs, to ensure attributes aren't stacked.
public class NBTEditor implements INBTEditor
{
// private static final Map<ArmorTier, NamespacedKey> namespaceKeys;
//
// static
// {
// final Map<ArmorTier, NamespacedKey> namespaceKeysTmp = new EnumMap<ArmorTier, NamespacedKey>(ArmorTier.class);
// for (final ArmorTier tier : ArmorTier.values())
// namespaceKeysTmp.put(tier, new NamespacedKey(ArmoredElytra.getInstance(), "ARMORTIER_" + tier.name()));
// namespaceKeys = Collections.unmodifiableMap(namespaceKeysTmp);
// }
private static final NamespacedKey armorTierKey = new NamespacedKey(ArmoredElytra.getInstance(),
"ARMOR_TIER_LEVEL");
public NBTEditor()
{
}
@ -24,16 +40,23 @@ public class NBTEditor implements INBTEditor
@Override
public ItemStack addArmorNBTTags(ItemStack item, ArmorTier armorTier, boolean unbreakable)
{
if (armorTier == null || armorTier == ArmorTier.NONE)
return new ItemStack(item);
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);
meta.getPersistentDataContainer().set(armorTierKey, PersistentDataType.INTEGER, ArmorTier.getTierID(armorTier));
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");
"generic.armor_toughness");
if (ArmorTier.getKnockbackResistance(armorTier) > 0)
overwriteNBTValue(meta, Attribute.GENERIC_KNOCKBACK_RESISTANCE, ArmorTier.getKnockbackResistance(armorTier),
"generic.knockback_resistance");
meta.setUnbreakable(unbreakable);
meta.setDisplayName(ArmoredElytra.getInstance().getArmoredElytraName(armorTier));
@ -48,7 +71,8 @@ public class NBTEditor implements INBTEditor
meta.removeAttributeModifier(attribute);
AttributeModifier attributeModifier = new AttributeModifier(UUID.randomUUID(), modifierName, value,
AttributeModifier.Operation.ADD_NUMBER, EquipmentSlot.CHEST);
AttributeModifier.Operation.ADD_NUMBER,
EquipmentSlot.CHEST);
meta.addAttributeModifier(attribute, attributeModifier);
}
@ -62,16 +86,20 @@ public class NBTEditor implements INBTEditor
return ArmorTier.NONE;
ItemMeta meta = item.getItemMeta();
if (meta == null)
if (meta == null || !meta.hasAttributeModifiers())
return ArmorTier.NONE;
Integer tierID = meta.getPersistentDataContainer().get(armorTierKey, PersistentDataType.INTEGER);
if (tierID != null)
return ArmorTier.getArmorTierFromID(tierID);
Collection<AttributeModifier> attributeModifiers = meta.getAttributeModifiers(Attribute.GENERIC_ARMOR);
if (attributeModifiers == null)
return ArmorTier.NONE;
for (final AttributeModifier attributeModifier : attributeModifiers)
{
ArmorTier armorTier = ArmorTier.getArmorTier((int) attributeModifier.getAmount());
ArmorTier armorTier = ArmorTier.getArmorTierFromArmor((int) attributeModifier.getAmount());
if (armorTier != ArmorTier.NONE)
return armorTier;
}

View File

@ -7,54 +7,100 @@ import java.util.Map;
public enum ArmorTier
{
// Tier: armor-value, armor-toughness, repair
NONE (0 , 0 , null , ""),
LEATHER (3 , 0 , Material.LEATHER , "leather"),
GOLD (5 , 0 , Material.GOLD_INGOT, "gold"),
CHAIN (5 , 0 , Material.IRON_INGOT, "chain"),
IRON (6 , 0 , Material.IRON_INGOT, "iron"),
DIAMOND (8 , 2 , Material.DIAMOND , "diamond");
// Tier: TierID, armor-value, armor-toughness, knockbackResistance, repair
NONE(0, 0, 0, 0, null, ""),
LEATHER(1, 3, 0, 0, Material.LEATHER, "leather"),
GOLD(2, 5, 0, 0, Material.GOLD_INGOT, "gold"),
CHAIN(3, 5, 0, 0, Material.IRON_INGOT, "chain"),
IRON(4, 6, 0, 0, Material.IRON_INGOT, "iron"),
DIAMOND(5, 8, 2, 0, Material.DIAMOND, "diamond"),
NETHERITE(6, 8, 2, 0.1, XMaterial.NETHERITE_INGOT.parseMaterial(), "netherite"),
;
private final int armor;
private final int toughness;
private final Material repair;
private final String name;
private static Map<String, ArmorTier> map = new HashMap<>();
private final int tierID;
private final int armor;
private final int toughness;
private final double knockbackResistance;
private final Material repair;
private final String name;
private static Map<String, ArmorTier> map = new HashMap<>();
private static Map<Integer, ArmorTier> armorValueMap = new HashMap<>();
private static Map<Integer, ArmorTier> armorIDMap = new HashMap<>();
private ArmorTier (int armor, int toughness, Material repair, String name)
ArmorTier(int tierID, int armor, int toughness, double knockbackResistance, Material repair, String name)
{
this.armor = armor;
this.toughness = toughness;
this.repair = repair;
this.name = name;
this.tierID = tierID;
this.armor = armor;
this.toughness = toughness;
this.knockbackResistance = knockbackResistance;
this.repair = repair;
this.name = name;
}
// return the armor value of a tier.
public static int getArmor (ArmorTier tier) { return tier.armor; }
public static int getArmor(ArmorTier tier)
{
return tier.armor;
}
// return the armor value of a tier.
public static int getTierID(ArmorTier tier)
{
return tier.tierID;
}
// return the armor toughness of a tier.
public static int getToughness (ArmorTier tier) { return tier.toughness; }
public static int getToughness(ArmorTier tier)
{
return tier.toughness;
}
// return the armor toughness of a tier.
public static double getKnockbackResistance(ArmorTier tier)
{
return tier.knockbackResistance;
}
// return the repair item of a tier
public static Material getRepairItem (ArmorTier tier) { return tier.repair; }
public static Material getRepairItem(ArmorTier tier)
{
return tier.repair;
}
public static String getName (ArmorTier tier) { return tier.name; }
public static String getName(ArmorTier tier)
{
return tier.name;
}
public static ArmorTier valueOfName (String name) { return map.get(name); }
public static ArmorTier valueOfName(String name)
{
return map.get(name);
}
public static ArmorTier getArmorTier(int armor)
public static ArmorTier getArmorTierFromArmor(int armor)
{
ArmorTier tier = armorValueMap.get(armor);
return tier == null ? ArmorTier.NONE : tier;
}
public static ArmorTier getArmorTierFromID(int tierID)
{
ArmorTier tier = armorIDMap.get(tierID);
return tier == null ? ArmorTier.NONE : tier;
}
static
{
for (ArmorTier tier : ArmorTier.values())
{
map.put(tier.name, tier);
armorValueMap.put(tier.armor, tier);
armorIDMap.put(tier.tierID, tier);
}
// Overwrite the index for diamond-tier armor value.
// This value is the same as netherite's tier. However, with the introduction of the NETHERITE armor tier,
// a new system was introduced that doesn't rely on the armor value for determining the armortier.
// Therefore, when using the old backup system, it is always going to be the diamond tier instead.
armorValueMap.put(ArmorTier.DIAMOND.armor, ArmorTier.DIAMOND);
}
}

View File

@ -27,14 +27,16 @@ public class ConfigLoader
private boolean autoDLUpdate;
private int LEATHER_TO_FULL;
private int DIAMONDS_TO_FULL;
private int NETHERITE_TO_FULL;
private boolean noFlightDurability;
private List<String> allowedEnchantments;
private boolean allowMultipleProtectionEnchantments;
public boolean bypassWearPerm;
public boolean bypassCraftPerm;
private boolean craftingInSmithingTable;
private boolean bypassWearPerm;
private boolean bypassCraftPerm;
private ArrayList<nl.pim16aap2.armoredElytra.util.ConfigOption<?>> configOptionsList;
private ArmoredElytra plugin;
private final ArrayList<nl.pim16aap2.armoredElytra.util.ConfigOption<?>> configOptionsList;
private final ArmoredElytra plugin;
public ConfigLoader(ArmoredElytra plugin)
{
@ -113,14 +115,17 @@ public class ConfigLoader
{
"Globally bypass permissions for wearing and/or crafting amored elytras. Useful if permissions are unavailable."
};
String[] craftingInSmithingTableComment =
{
"This option only works on 1.16+! When enabled, armored elytra creation in anvils is disabled. ",
"Instead, you will have to craft them in a smithy. Enchanting/repairing them still works via the anvil."
};
// Set default list of allowed enchantments.
allowedEnchantments = new ArrayList<>(Arrays.asList("DURABILITY", "PROTECTION_FIRE", "PROTECTION_EXPLOSIONS",
"PROTECTION_PROJECTILE", "PROTECTION_ENVIRONMENTAL",
"THORNS",
"BINDING_CURSE", "VANISHING_CURSE", "MENDING"));
"THORNS", "BINDING_CURSE", "VANISHING_CURSE", "MENDING"));
FileConfiguration config = plugin.getConfig();
unbreakable = addNewConfigOption(config, "unbreakable", false, unbreakableComment);
@ -129,6 +134,11 @@ public class ConfigLoader
GOLD_TO_FULL = addNewConfigOption(config, "goldRepair", 5, null);
IRON_TO_FULL = addNewConfigOption(config, "ironRepair", 4, null);
DIAMONDS_TO_FULL = addNewConfigOption(config, "diamondsRepair", 3, null);
NETHERITE_TO_FULL = addNewConfigOption(config, "netheriteIngotsRepair", 3, null);
// craftingInSmithingTable = addNewConfigOption(config, "craftingInSmithingTable", true, craftingInSmithingTableComment);
craftingInSmithingTable = false;
allowedEnchantments = addNewConfigOption(config, "allowedEnchantments", allowedEnchantments,
enchantmentsComment);
allowMultipleProtectionEnchantments = addNewConfigOption(config, "allowMultipleProtectionEnchantments", false,
@ -200,6 +210,12 @@ public class ConfigLoader
return allowStats;
}
public boolean craftingInSmithingTable()
{
return ArmoredElytra.getInstance().getMinecraftVersion().isNewerThan(MinecraftVersion.v1_15) &&
craftingInSmithingTable;
}
public boolean unbreakable()
{
return unbreakable;
@ -235,6 +251,11 @@ public class ConfigLoader
return DIAMONDS_TO_FULL;
}
public int NETHERITE_TO_FULL()
{
return NETHERITE_TO_FULL;
}
public boolean allowMultipleProtectionEnchantments()
{
return allowMultipleProtectionEnchantments;

View File

@ -1,13 +1,13 @@
package nl.pim16aap2.armoredElytra.util;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Map;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Map;
public class Util
{
public static String errorToString(Error e)
@ -40,23 +40,26 @@ public class Util
switch (xmat)
{
case LEATHER_CHESTPLATE:
ret = ArmorTier.LEATHER;
break;
case GOLDEN_CHESTPLATE:
ret = ArmorTier.GOLD;
break;
case CHAINMAIL_CHESTPLATE:
ret = ArmorTier.CHAIN;
break;
case IRON_CHESTPLATE:
ret = ArmorTier.IRON;
break;
case DIAMOND_CHESTPLATE:
ret = ArmorTier.DIAMOND;
break;
default:
break;
case LEATHER_CHESTPLATE:
ret = ArmorTier.LEATHER;
break;
case GOLDEN_CHESTPLATE:
ret = ArmorTier.GOLD;
break;
case CHAINMAIL_CHESTPLATE:
ret = ArmorTier.CHAIN;
break;
case IRON_CHESTPLATE:
ret = ArmorTier.IRON;
break;
case DIAMOND_CHESTPLATE:
ret = ArmorTier.DIAMOND;
break;
case NETHERITE_CHESTPLATE:
ret = ArmorTier.NETHERITE;
break;
default:
break;
}
return ret;
}
@ -64,29 +67,37 @@ public class Util
// Check if mat is a chest plate.
public static boolean isChestPlate(Material mat)
{
XMaterial xmat = XMaterial.matchXMaterial(mat);
if (xmat == null)
try
{
XMaterial xmat = XMaterial.matchXMaterial(mat);
if (xmat == null)
return false;
if (xmat == XMaterial.LEATHER_CHESTPLATE || xmat == XMaterial.GOLDEN_CHESTPLATE ||
xmat == XMaterial.CHAINMAIL_CHESTPLATE || xmat == XMaterial.IRON_CHESTPLATE ||
xmat == XMaterial.DIAMOND_CHESTPLATE || xmat == XMaterial.NETHERITE_CHESTPLATE)
return true;
return false;
if (xmat == XMaterial.LEATHER_CHESTPLATE || xmat == XMaterial.GOLDEN_CHESTPLATE ||
xmat == XMaterial.CHAINMAIL_CHESTPLATE || xmat == XMaterial.IRON_CHESTPLATE ||
xmat == XMaterial.DIAMOND_CHESTPLATE)
return true;
return false;
}
catch (IllegalArgumentException e)
{
// No need to handle this, this is just XMaterial complaining the material doesn't exist.
return false;
}
}
// Function that returns which/how many protection enchantments there are.
// TODO: Use bit flags for this.
public static int getProtectionEnchantmentsVal(Map<Enchantment, Integer> enchantments)
{
int ret = 0;
int ret = 0;
if (enchantments.containsKey(Enchantment.PROTECTION_ENVIRONMENTAL))
ret += 1;
ret += 1;
if (enchantments.containsKey(Enchantment.PROTECTION_EXPLOSIONS))
ret += 2;
ret += 2;
if (enchantments.containsKey(Enchantment.PROTECTION_FALL))
ret += 4;
ret += 4;
if (enchantments.containsKey(Enchantment.PROTECTION_FIRE))
ret += 8;
ret += 8;
if (enchantments.containsKey(Enchantment.PROTECTION_PROJECTILE))
ret += 16;
return ret;

File diff suppressed because it is too large Load Diff

View File

@ -14,12 +14,14 @@ public enum Message implements IMessageVariable
TIER_CHAIN(),
TIER_IRON(),
TIER_DIAMOND(),
TIER_NETHERITE(),
TIER_SHORT_LEATHER(),
TIER_SHORT_GOLD(),
TIER_SHORT_CHAIN(),
TIER_SHORT_IRON(),
TIER_SHORT_DIAMOND(),
TIER_SHORT_NETHERITE(),
MESSAGES_UNINSTALLMODE(),
MESSAGES_UNSUPPORTEDTIER(),

View File

@ -42,6 +42,7 @@ public class Messages
public Messages(final ArmoredElytra plugin)
{
this.plugin = plugin;
writeDefaultFile();
final String fileName = plugin.getConfigLoader().languageFile();
// Only append .txt if the provided name doesn't already have it.
textFile = new File(plugin.getDataFolder(), fileName.endsWith(".txt") ? fileName : (fileName + ".txt"));
@ -49,7 +50,7 @@ public class Messages
if (!textFile.exists())
{
plugin.myLogger(Level.WARNING, "Failed to load language file: \"" + textFile +
"\": File not found! Using default file instead!");
"\": File not found! Using default file (\"" + DEFAULTFILENAME + "\") instead!");
textFile = new File(plugin.getDataFolder(), DEFAULTFILENAME);
}
populateMessageMap();

View File

@ -9,11 +9,13 @@ TIER.Gold=&EGolden Armored Elytra
TIER.Chain=&8Chain Armored Elytra
TIER.Iron=&7Iron Armored Elytra
TIER.Diamond=&BDiamond Armored Elytra
TIER.Netherite=&4Netherite Armored Elytra
TIER.SHORT.Leather=&2Leather
TIER.SHORT.Gold=&EGold
TIER.SHORT.Chain=&8Chain
TIER.SHORT.Iron=&7Iron
TIER.SHORT.Diamond=&BDiamond
TIER.SHORT.Netherite=&4Netherite
MESSAGES.UninstallMode=&cPlugin in uninstall mode! New Armored Elytras are not allowed!
MESSAGES.UnsupportedTier=&cNot a supported armor tier! Try one of these: leather, gold, chain, iron, diamond.
MESSAGES.RepairNeeded=&cYou cannot equip this elytra! Please repair it in an anvil first.