Improves various things

Gets rid of XMaterial
Updates API version to 16
Sets custom_model_data values to allow custom textures for each armored elytra
Extracts and renames interfaces and classes used for the armored elytra builder
This commit is contained in:
Kristian Knarvik 2023-03-15 13:48:06 +01:00
parent 80ed7838d9
commit 5730d41bc6
19 changed files with 728 additions and 1599 deletions

View File

@ -7,7 +7,6 @@ import nl.pim16aap2.armoredElytra.util.Action;
import nl.pim16aap2.armoredElytra.util.ArmorTier;
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
import nl.pim16aap2.armoredElytra.util.Util;
import nl.pim16aap2.armoredElytra.util.XMaterial;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;
@ -81,7 +80,7 @@ public class AnvilHandler extends ArmoredElytraHandler implements Listener {
// Pick the block action, as that would repair the elytra by default (vanilla).
// Also block Armored Elytra + Elytra and Elytra + Membrane
if (itemOneTier != ArmorTier.LEATHER && matTwo == Material.LEATHER || matTwo == Material.ELYTRA ||
matTwo.equals(XMaterial.PHANTOM_MEMBRANE.parseMaterial())) {
matTwo.equals(Material.PHANTOM_MEMBRANE)) {
return Action.BLOCK;
}
}

View File

@ -1,7 +1,7 @@
package nl.pim16aap2.armoredElytra.handlers;
import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder;
import nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder.ArmoredElytraBuilder;
import nl.pim16aap2.armoredElytra.nbtEditor.DurabilityManager;
import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor;
import nl.pim16aap2.armoredElytra.util.ConfigLoader;

View File

@ -1,7 +1,7 @@
package nl.pim16aap2.armoredElytra.handlers;
import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder;
import nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder.ArmoredElytraBuilder;
import nl.pim16aap2.armoredElytra.nbtEditor.DurabilityManager;
import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor;
import nl.pim16aap2.armoredElytra.util.ArmorTier;

View File

@ -140,7 +140,7 @@ public class EventHandlers implements Listener {
plugin.usageDeniedMessage(e.getPlayer(), armorTier);
e.setCancelled(true);
}
case ALLOWED, default -> {
default -> {
}
}
}

View File

@ -1,451 +0,0 @@
package nl.pim16aap2.armoredElytra.nbtEditor;
import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.util.ArmorTier;
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
import nl.pim16aap2.armoredElytra.util.EnchantmentContainer;
import nl.pim16aap2.armoredElytra.util.Util;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.LeatherArmorMeta;
import javax.annotation.Nullable;
import java.util.List;
@SuppressWarnings({"unused", "UnusedReturnValue"})
public class ArmoredElytraBuilder {
private final NBTEditor nbtEditor;
private final DurabilityManager durabilityManager;
private final ConfigLoader config;
private final ArmoredElytra plugin;
public ArmoredElytraBuilder(NBTEditor nbtEditor, DurabilityManager durabilityManager,
ConfigLoader config, ArmoredElytra plugin) {
this.nbtEditor = nbtEditor;
this.durabilityManager = durabilityManager;
this.config = config;
this.plugin = plugin;
}
/**
* Creates a new builder for an armored elytra.
*
* @return The first step of the new builder.
*/
public IStep0 newBuilder() {
return new Builder(nbtEditor, durabilityManager, config, plugin);
}
/**
* Shortcut for repairing an armored elytra.
*
* @param armoredElytra The armored elytra to repair.
* @param repairItems The repair item(s) to use for repairing the armored elytra. It is assumed that they are of
* the correct type.
* @param name The new name of the output armored elytra. When this is null, {@link
* ArmoredElytra#getArmoredElytraName(ArmorTier)} is used to set the name.
* @return The new armored elytra.
*/
public @Nullable ItemStack repair(ItemStack armoredElytra, ItemStack repairItems, @Nullable String name) {
return newBuilder().ofElytra(armoredElytra).repair(repairItems.getAmount()).withName(name).build();
}
/**
* Shortcut for enchanting an armored elytra.
*
* @param armoredElytra The armored elytra to repair.
* @param sourceItem The source item from which to copy the enchantments from.
* @param name The new name of the output armored elytra. When this is null, {@link
* ArmoredElytra#getArmoredElytraName(ArmorTier)} is used to set the name.
* @return The new armored elytra.
*/
public @Nullable ItemStack enchant(ItemStack armoredElytra, ItemStack sourceItem, @Nullable String name) {
final EnchantmentContainer enchantments = EnchantmentContainer.getEnchantmentsOf(sourceItem, plugin);
if (enchantments.isEmpty()) {
return null;
}
return newBuilder().ofElytra(armoredElytra).addEnchantments(enchantments).withName(name).build();
}
/**
* Shortcut for creating a new armored elytra from two items.
*
* @param elytra The input item. This should be an (armored) elytra.
* @param combiner The item to combine with the elytra. This should either be an armored elytra of the same
* non-NONE tier as the input elytra or a chestplate.
* @param armorTier The armor tier of the input item. If this is not known, use {@link #combine(ItemStack,
* ItemStack, String)} instead.
* @param name The new name of the output armored elytra. When this is null, {@link
* ArmoredElytra#getArmoredElytraName(ArmorTier)} is used to set the name.
* @return The new armored elytra.
*/
public ItemStack combine(ItemStack elytra, ItemStack combiner, ArmorTier armorTier, @Nullable String name) {
return newBuilder().ofElytra(elytra).combineWith(combiner, armorTier).withName(name).build();
}
/**
* See {@link #combine(ItemStack, ItemStack, ArmorTier, String)} for unknown armor tiers.
*/
public ItemStack combine(ItemStack elytra, ItemStack combiner, @Nullable String name) {
return newBuilder().ofElytra(elytra).combineWith(combiner).withName(name).build();
}
/**
* Creates a new armored elytra of a specific tier.
*
* @param armorTier The tier of the new armored elytra.
* @return The new armored elytra.
*/
public ItemStack newArmoredElytra(ArmorTier armorTier) {
return newBuilder().newItem(armorTier).build();
}
/**
* Represents the third and last step of the armored elytra build process.
*/
public interface IStep2 {
/**
* Specifies the new name of the armored elytra.
*
* @param name The new name of the armored elytra. When this is null (default), the default name for the new
* tier will be used.
* @return The current builder step.
*/
IStep2 withName(@Nullable String name);
/**
* Specifies the new color of the armored elytra.
*
* @param color The new color of the armored elytra. When this is null (default), the color to use is inferred
* from the creation process.
* @return The current builder step.
*/
IStep2 withColor(@Nullable Color color);
/**
* Specifies the new lore of the armored elytra.
*
* @param lore The new lore of the armored elytra. When this is null (default), the default lore for the new
* tier will be used.
* @return The current builder step.
*/
IStep2 withLore(@Nullable List<String> lore);
/**
* Specifies whether the armored elytra should be unbreakable.
* <p>
* By default, this value is read from {@link ConfigLoader#unbreakable()}.
*
* @param isUnbreakable True if the armored elytra should be unbreakable.
* @return The current builder step.
*/
IStep2 unbreakable(boolean isUnbreakable);
/**
* Constructs the armored elytra from the provided configuration.
*
* @return The new armored elytra.
*/
ItemStack build();
}
/**
* Represents the second and last step of the armored elytra build process.
*/
public interface IStep1 {
/**
* Repairs the armored elytra provided as input.
*
* @param count The amount of repair items to process.
* @return The next step of the builder process.
*/
IStep2 repair(int count);
/**
* Adds a set of enchantments to the armored elytra.
*
* @param enchantmentContainer The enchantments to add.
* @return The next step of the builder process.
*/
IStep2 addEnchantments(EnchantmentContainer enchantmentContainer);
/**
* Adds a set of enchantments to the armored elytra.
*
* @param sourceItem The source item from which to take the enchantments.
* @return The next step of the builder process.
*/
IStep2 addEnchantments(ItemStack sourceItem);
/**
* Combines the input elytra with another item.
*
* @param item The item to combine with the input elytra. This can either be a chestplate or, if the input
* elytra is an armored one, another armored elytra.
* @param armorTier The armor tier of the input item. If this is not known, use {@link #combineWith(ItemStack)}
* instead.
* @return The next step of the builder process.
*/
IStep2 combineWith(ItemStack item, ArmorTier armorTier);
/**
* See {@link #combineWith(ItemStack, ArmorTier)}. Used when the armor tier of the input item is not known.
*
* @return The next step of the builder process.
*/
IStep2 combineWith(ItemStack item);
/**
* Upgrades the elytra to a specific armor tier.
*
* @param armorTier The new armor tier.
* @return The next step of the builder process.
*/
IStep2 upgradeToTier(ArmorTier armorTier);
}
/**
* Represents the first and last step of the armored elytra build process.
*/
public interface IStep0 {
/**
* Use an elytra as base item to create the new armored elytra from.
*
* @param elytra An itemstack that represents an elytra. It does not matter whether the elytra is armored or
* not.
* @return The next step of the builder process.
*/
IStep1 ofElytra(ItemStack elytra);
/**
* Creates a fresh new armored elytra of a specific tier.
*
* @param armorTier The tier of the new armored elytra.
* @return The next step of the builder process.
*/
IStep2 newItem(ArmorTier armorTier);
}
private static final class Builder implements IStep0, IStep1, IStep2 {
private static final Color DEFAULT_LEATHER_COLOR = Bukkit.getServer().getItemFactory().getDefaultLeatherColor();
private final NBTEditor nbtEditor;
private final DurabilityManager durabilityManager;
private final ConfigLoader config;
private final ArmoredElytra plugin;
// These aren't nullable, as they are set by the only entry points.
/**
* The new armored elytra that will be returned at the end of the build process.
*/
private ItemStack newArmoredElytra;
/**
* The combined enchantments of the input items.
*/
private EnchantmentContainer combinedEnchantments;
/**
* The current armor tier of the input elytra.
*/
private ArmorTier currentArmorTier;
/**
* The durability of the output armored elytra.
*/
private int durability;
/**
* The armor tier of the output armored elytra. This defaults to {@link #currentArmorTier} if this isn't set.
*/
private @Nullable ArmorTier newArmorTier;
/**
* The name of the output armored elytra. This defaults to {@link ArmoredElytra#getArmoredElytraName(ArmorTier)}
* when not overridden.
*/
private @Nullable String name;
/**
* The lore of the output armored elytra. This defaults to {@link ArmoredElytra#getElytraLore(ArmorTier)} when
* not overridden.
*/
private @Nullable List<String> lore;
/**
* The color of the output armored elytra. By default, the existing color (if any is used). When combined with
* another item, the color is inferred using {@link #getItemColor(ItemStack, ItemStack)}.
*/
private @Nullable Color color;
/**
* Whether the output armored elytra should be unbreakable. This defaults to {@link ConfigLoader#unbreakable()}
* when not overridden.
*/
private @Nullable Boolean isUnbreakable = null;
private Builder(NBTEditor nbtEditor, DurabilityManager durabilityManager,
ConfigLoader config, ArmoredElytra plugin) {
this.nbtEditor = nbtEditor;
this.durabilityManager = durabilityManager;
this.config = config;
this.plugin = plugin;
}
@Override
public ItemStack build() {
// Get default values if unset.
newArmorTier = newArmorTier == null ? currentArmorTier : newArmorTier;
name = name == null ? plugin.getArmoredElytraName(newArmorTier) : name;
lore = lore == null ? plugin.getElytraLore(newArmorTier) : lore;
isUnbreakable = isUnbreakable == null ? config.unbreakable() : isUnbreakable;
final ItemStack output = nbtEditor.addArmorNBTTags(newArmoredElytra, newArmorTier,
isUnbreakable, name, lore, color);
durabilityManager.setDurability(output, durability, newArmorTier);
combinedEnchantments.applyEnchantments(output);
return output;
}
@Override
public IStep2 withName(@Nullable String name) {
this.name = name;
return this;
}
@Override
public IStep2 withColor(@Nullable Color color) {
this.color = color;
return this;
}
@Override
public IStep2 withLore(@Nullable List<String> lore) {
this.lore = lore;
return this;
}
@Override
public IStep2 unbreakable(boolean isUnbreakable) {
this.isUnbreakable = isUnbreakable;
return this;
}
@Override
public IStep2 repair(int count) {
if (currentArmorTier == ArmorTier.NONE) {
throw new IllegalArgumentException("Non-armored elytras cannot be repaired!");
}
durability = durabilityManager.getRepairedDurability(newArmoredElytra, count, currentArmorTier);
return this;
}
@Override
public IStep2 addEnchantments(EnchantmentContainer enchantmentContainer) {
combinedEnchantments.merge(enchantmentContainer);
return this;
}
@Override
public IStep2 addEnchantments(ItemStack sourceItem) {
return addEnchantments(EnchantmentContainer.getEnchantmentsOf(sourceItem, plugin));
}
@Override
public IStep2 combineWith(ItemStack item, ArmorTier armorTier) {
if (armorTier == ArmorTier.NONE && !Util.isChestPlate(item)) {
throw new IllegalArgumentException("Non-armored elytras can only be combined with chest plates!");
}
newArmorTier = armorTier;
if (currentArmorTier == ArmorTier.NONE &&
item.getType().equals(Material.ELYTRA) && newArmorTier != ArmorTier.NONE) {
throw new IllegalArgumentException("A regular elytra cannot be combined with an armored one!");
}
withColor(getItemColor(newArmoredElytra, item));
addEnchantments(item);
durability = durabilityManager.getCombinedDurability(newArmoredElytra, item,
currentArmorTier, newArmorTier);
return this;
}
@Override
public IStep2 combineWith(ItemStack item) {
final ArmorTier armorTier = item.getType().equals(Material.ELYTRA) ?
nbtEditor.getArmorTier(item) : Util.armorToTier(item.getType());
return combineWith(item, armorTier);
}
@Override
public IStep2 upgradeToTier(ArmorTier armorTier) {
newArmorTier = armorTier;
return this;
}
@Override
public IStep1 ofElytra(ItemStack elytra) {
if (!elytra.getType().equals(Material.ELYTRA)) {
throw new IllegalArgumentException("Expected elytra as input, but got: " + elytra);
}
newArmoredElytra = new ItemStack(elytra);
if (currentArmorTier == null) {
currentArmorTier = nbtEditor.getArmorTier(elytra);
}
combinedEnchantments = EnchantmentContainer.getEnchantmentsOf(newArmoredElytra, plugin);
durability = durabilityManager.getRealDurability(newArmoredElytra, currentArmorTier);
return this;
}
@Override
public IStep2 newItem(ArmorTier armorTier) {
currentArmorTier = newArmorTier = armorTier;
ofElytra(new ItemStack(Material.ELYTRA));
return this;
}
/**
* Gets the color of the item if the item has a color.
* <p>
* See {@link LeatherArmorMeta#getColor()}.
*
* @param itemA The first {@link ItemStack} to check.
* @param itemB The second {@link ItemStack} to check.
* @return The color of the item, if it has a color, otherwise null.
*/
private @Nullable Color getItemColor(final ItemStack itemA, final ItemStack itemB) {
final @Nullable Color colorA = getItemColor(itemA);
if (colorA != null && !colorA.equals(DEFAULT_LEATHER_COLOR)) {
return colorA;
}
final @Nullable Color colorB = getItemColor(itemB);
return colorB != null ? colorB : colorA;
}
/**
* Gets the colors of an item if available.
* <p>
* This currently only applies to leather armor(ed elytras).
*
* @param itemStack The item to analyze.
* @return The color of the item, if available, otherwise null.
*/
private @Nullable Color getItemColor(final ItemStack itemStack) {
if (itemStack.getType() == Material.ELYTRA) {
return nbtEditor.getColorOfArmoredElytra(itemStack);
}
if (!itemStack.hasItemMeta() || !(itemStack.getItemMeta() instanceof LeatherArmorMeta)) {
return null;
}
return ((LeatherArmorMeta) itemStack.getItemMeta()).getColor();
}
}
}

View File

@ -0,0 +1,102 @@
package nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder;
import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.nbtEditor.DurabilityManager;
import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor;
import nl.pim16aap2.armoredElytra.util.ArmorTier;
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
import nl.pim16aap2.armoredElytra.util.EnchantmentContainer;
import org.bukkit.inventory.ItemStack;
import javax.annotation.Nullable;
@SuppressWarnings({"unused", "UnusedReturnValue"})
public class ArmoredElytraBuilder {
private final NBTEditor nbtEditor;
private final DurabilityManager durabilityManager;
private final ConfigLoader config;
private final ArmoredElytra plugin;
public ArmoredElytraBuilder(NBTEditor nbtEditor, DurabilityManager durabilityManager,
ConfigLoader config, ArmoredElytra plugin) {
this.nbtEditor = nbtEditor;
this.durabilityManager = durabilityManager;
this.config = config;
this.plugin = plugin;
}
/**
* Creates a new builder for an armored elytra.
*
* @return The first step of the new builder.
*/
public ItemCreator newBuilder() {
return new Builder(nbtEditor, durabilityManager, config, plugin);
}
/**
* Shortcut for repairing an armored elytra.
*
* @param armoredElytra The armored elytra to repair.
* @param repairItems The repair item(s) to use for repairing the armored elytra. It is assumed that they are of
* the correct type.
* @param name The new name of the output armored elytra. When this is null, {@link
* ArmoredElytra#getArmoredElytraName(ArmorTier)} is used to set the name.
* @return The new armored elytra.
*/
public @Nullable ItemStack repair(ItemStack armoredElytra, ItemStack repairItems, @Nullable String name) {
return newBuilder().ofElytra(armoredElytra).repair(repairItems.getAmount()).withName(name).build();
}
/**
* Shortcut for enchanting an armored elytra.
*
* @param armoredElytra The armored elytra to repair.
* @param sourceItem The source item from which to copy the enchantments from.
* @param name The new name of the output armored elytra. When this is null, {@link
* ArmoredElytra#getArmoredElytraName(ArmorTier)} is used to set the name.
* @return The new armored elytra.
*/
public @Nullable ItemStack enchant(ItemStack armoredElytra, ItemStack sourceItem, @Nullable String name) {
final EnchantmentContainer enchantments = EnchantmentContainer.getEnchantmentsOf(sourceItem, plugin);
if (enchantments.isEmpty()) {
return null;
}
return newBuilder().ofElytra(armoredElytra).addEnchantments(enchantments).withName(name).build();
}
/**
* Shortcut for creating a new armored elytra from two items.
*
* @param elytra The input item. This should be an (armored) elytra.
* @param combiner The item to combine with the elytra. This should either be an armored elytra of the same
* non-NONE tier as the input elytra or a chestplate.
* @param armorTier The armor tier of the input item. If this is not known, use {@link #combine(ItemStack,
* ItemStack, String)} instead.
* @param name The new name of the output armored elytra. When this is null, {@link
* ArmoredElytra#getArmoredElytraName(ArmorTier)} is used to set the name.
* @return The new armored elytra.
*/
public ItemStack combine(ItemStack elytra, ItemStack combiner, ArmorTier armorTier, @Nullable String name) {
return newBuilder().ofElytra(elytra).combineWith(combiner, armorTier).withName(name).build();
}
/**
* See {@link #combine(ItemStack, ItemStack, ArmorTier, String)} for unknown armor tiers.
*/
public ItemStack combine(ItemStack elytra, ItemStack combiner, @Nullable String name) {
return newBuilder().ofElytra(elytra).combineWith(combiner).withName(name).build();
}
/**
* Creates a new armored elytra of a specific tier.
*
* @param armorTier The tier of the new armored elytra.
* @return The new armored elytra.
*/
public ItemStack newArmoredElytra(ArmorTier armorTier) {
return newBuilder().newItem(armorTier).build();
}
}

View File

@ -0,0 +1,258 @@
package nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder;
import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.nbtEditor.DurabilityManager;
import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor;
import nl.pim16aap2.armoredElytra.util.ArmorTier;
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
import nl.pim16aap2.armoredElytra.util.EnchantmentContainer;
import nl.pim16aap2.armoredElytra.util.Util;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.LeatherArmorMeta;
import javax.annotation.Nullable;
import java.util.List;
import static nl.pim16aap2.armoredElytra.util.Util.getTextureIdFromArmorTier;
final class Builder implements ItemCreator, ItemCombinator, ItemMetadataSetter {
private static final Color DEFAULT_LEATHER_COLOR = Bukkit.getServer().getItemFactory().getDefaultLeatherColor();
private final NBTEditor nbtEditor;
private final DurabilityManager durabilityManager;
private final ConfigLoader config;
private final ArmoredElytra plugin;
/**
* The new armored elytra that will be returned at the end of the build process.
*/
private ItemStack newArmoredElytra;
/**
* The combined enchantments of the input items.
*/
private EnchantmentContainer combinedEnchantments;
/**
* The current armor tier of the input elytra.
*/
private ArmorTier currentArmorTier;
/**
* The durability of the output armored elytra.
*/
private int durability;
/**
* The armor tier of the output armored elytra. This defaults to {@link #currentArmorTier} if this isn't set.
*/
private @Nullable ArmorTier newArmorTier;
/**
* The name of the output armored elytra. This defaults to {@link ArmoredElytra#getArmoredElytraName(ArmorTier)}
* when not overridden.
*/
private @Nullable String name;
/**
* The lore of the output armored elytra. This defaults to {@link ArmoredElytra#getElytraLore(ArmorTier)} when
* not overridden.
*/
private @Nullable List<String> lore;
/**
* The color of the output armored elytra. By default, the existing color (if any is used). When combined with
* another item, the color is inferred using {@link #getItemColor(ItemStack, ItemStack)}.
*/
private @Nullable Color color;
/**
* Whether the output armored elytra should be unbreakable. This defaults to {@link ConfigLoader#unbreakable()}
* when not overridden.
*/
private @Nullable Boolean isUnbreakable = null;
/**
* The texture id (custom_model_data) to be applied to the item. If null, no custom texture id will be set.
*/
private @Nullable Integer textureId = null;
Builder(NBTEditor nbtEditor, DurabilityManager durabilityManager,
ConfigLoader config, ArmoredElytra plugin) {
this.nbtEditor = nbtEditor;
this.durabilityManager = durabilityManager;
this.config = config;
this.plugin = plugin;
}
@Override
public ItemStack build() {
// Get default values if unset.
newArmorTier = newArmorTier == null ? currentArmorTier : newArmorTier;
name = name == null ? plugin.getArmoredElytraName(newArmorTier) : name;
lore = lore == null ? plugin.getElytraLore(newArmorTier) : lore;
isUnbreakable = isUnbreakable == null ? config.unbreakable() : isUnbreakable;
final ItemStack output = nbtEditor.addArmorNBTTags(newArmoredElytra, newArmorTier,
isUnbreakable, name, lore, color, textureId);
durabilityManager.setDurability(output, durability, newArmorTier);
combinedEnchantments.applyEnchantments(output);
return output;
}
@Override
public ItemMetadataSetter withName(@Nullable String name) {
this.name = name;
return this;
}
@Override
public ItemMetadataSetter withColor(@Nullable Color color) {
this.color = color;
return this;
}
@Override
public ItemMetadataSetter withTexture(@Nullable Integer textureId) {
if (textureId != null) {
this.textureId = textureId;
} else if (newArmorTier != null) {
this.textureId = getTextureIdFromArmorTier(newArmorTier);
} else {
this.textureId = null;
}
return this;
}
@Override
public ItemMetadataSetter withLore(@Nullable List<String> lore) {
this.lore = lore;
return this;
}
@Override
public ItemMetadataSetter unbreakable(boolean isUnbreakable) {
this.isUnbreakable = isUnbreakable;
return this;
}
@Override
public ItemMetadataSetter repair(int count) {
if (currentArmorTier == ArmorTier.NONE) {
throw new IllegalArgumentException("Non-armored elytras cannot be repaired!");
}
durability = durabilityManager.getRepairedDurability(newArmoredElytra, count, currentArmorTier);
return this;
}
@Override
public ItemMetadataSetter addEnchantments(EnchantmentContainer enchantmentContainer) {
combinedEnchantments.merge(enchantmentContainer);
return this;
}
@Override
public ItemMetadataSetter addEnchantments(ItemStack sourceItem) {
return addEnchantments(EnchantmentContainer.getEnchantmentsOf(sourceItem, plugin));
}
@Override
public ItemMetadataSetter combineWith(ItemStack item, ArmorTier armorTier) {
// Note: This method is almost always run. Important building steps should happen here.
if (armorTier == ArmorTier.NONE && !Util.isChestPlate(item)) {
throw new IllegalArgumentException("Non-armored elytras can only be combined with chest plates!");
}
newArmorTier = armorTier;
if (currentArmorTier == ArmorTier.NONE &&
item.getType().equals(Material.ELYTRA) && newArmorTier != ArmorTier.NONE) {
throw new IllegalArgumentException("A regular elytra cannot be combined with an armored one!");
}
withColor(getItemColor(newArmoredElytra, item)).withTexture(null);
addEnchantments(item);
durability = durabilityManager.getCombinedDurability(newArmoredElytra, item,
currentArmorTier, newArmorTier);
return this;
}
@Override
public ItemMetadataSetter combineWith(ItemStack item) {
final ArmorTier armorTier = item.getType().equals(Material.ELYTRA) ?
nbtEditor.getArmorTier(item) : Util.armorToTier(item.getType());
return combineWith(item, armorTier);
}
@Override
public ItemMetadataSetter upgradeToTier(ArmorTier armorTier) {
newArmorTier = armorTier;
return this;
}
@Override
public ItemCombinator ofElytra(ItemStack elytra) {
if (!elytra.getType().equals(Material.ELYTRA)) {
throw new IllegalArgumentException("Expected elytra as input, but got: " + elytra);
}
newArmoredElytra = new ItemStack(elytra);
if (currentArmorTier == null) {
currentArmorTier = nbtEditor.getArmorTier(elytra);
}
combinedEnchantments = EnchantmentContainer.getEnchantmentsOf(newArmoredElytra, plugin);
durability = durabilityManager.getRealDurability(newArmoredElytra, currentArmorTier);
return this;
}
@Override
public ItemMetadataSetter newItem(ArmorTier armorTier) {
currentArmorTier = newArmorTier = armorTier;
ofElytra(new ItemStack(Material.ELYTRA));
return this;
}
/**
* Gets the color of the item if the item has a color.
* <p>
* See {@link LeatherArmorMeta#getColor()}.
*
* @param itemA The first {@link ItemStack} to check.
* @param itemB The second {@link ItemStack} to check.
* @return The color of the item, if it has a color, otherwise null.
*/
private @Nullable Color getItemColor(final ItemStack itemA, final ItemStack itemB) {
final @Nullable Color colorA = getItemColor(itemA);
if (colorA != null && !colorA.equals(DEFAULT_LEATHER_COLOR)) {
return colorA;
}
final @Nullable Color colorB = getItemColor(itemB);
return colorB != null ? colorB : colorA;
}
/**
* Gets the colors of an item if available.
* <p>
* This currently only applies to leather armor(ed elytras).
*
* @param itemStack The item to analyze.
* @return The color of the item, if available, otherwise null.
*/
private @Nullable Color getItemColor(final ItemStack itemStack) {
if (itemStack.getType() == Material.ELYTRA) {
return nbtEditor.getColorOfArmoredElytra(itemStack);
}
if (!itemStack.hasItemMeta() || !(itemStack.getItemMeta() instanceof LeatherArmorMeta)) {
return null;
}
return ((LeatherArmorMeta) itemStack.getItemMeta()).getColor();
}
}

View File

@ -0,0 +1,62 @@
package nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder;
import nl.pim16aap2.armoredElytra.util.ArmorTier;
import nl.pim16aap2.armoredElytra.util.EnchantmentContainer;
import org.bukkit.inventory.ItemStack;
/**
* An interface describing the combination step of building an armored elytra
*/
public interface ItemCombinator {
/**
* Repairs the armored elytra provided as input.
*
* @param count The amount of repair items to process.
* @return The next step of the builder process.
*/
ItemMetadataSetter repair(int count);
/**
* Adds a set of enchantments to the armored elytra.
*
* @param enchantmentContainer The enchantments to add.
* @return The next step of the builder process.
*/
ItemMetadataSetter addEnchantments(EnchantmentContainer enchantmentContainer);
/**
* Adds a set of enchantments to the armored elytra.
*
* @param sourceItem The source item from which to take the enchantments.
* @return The next step of the builder process.
*/
ItemMetadataSetter addEnchantments(ItemStack sourceItem);
/**
* Combines the input elytra with another item.
*
* @param item The item to combine with the input elytra. This can either be a chestplate or, if the input
* elytra is an armored one, another armored elytra.
* @param armorTier The armor tier of the input item. If this is not known, use {@link #combineWith(ItemStack)}
* instead.
* @return The next step of the builder process.
*/
ItemMetadataSetter combineWith(ItemStack item, ArmorTier armorTier);
/**
* See {@link #combineWith(ItemStack, ArmorTier)}. Used when the armor tier of the input item is not known.
*
* @return The next step of the builder process.
*/
ItemMetadataSetter combineWith(ItemStack item);
/**
* Upgrades the elytra to a specific armor tier.
*
* @param armorTier The new armor tier.
* @return The next step of the builder process.
*/
ItemMetadataSetter upgradeToTier(ArmorTier armorTier);
}

View File

@ -0,0 +1,27 @@
package nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder;
import nl.pim16aap2.armoredElytra.util.ArmorTier;
import org.bukkit.inventory.ItemStack;
/**
* An interface describing the first step of building an armored elytra
*/
public interface ItemCreator {
/**
* Use an elytra as base item to create the new armored elytra from.
*
* @param elytra <p>An item-stack that represents an elytra. It does not matter whether the elytra is armored or not.</p>
* @return <p>The next step of the builder process.</p>
*/
ItemCombinator ofElytra(ItemStack elytra);
/**
* Creates a fresh new armored elytra of a specific tier.
*
* @param armorTier <p>The tier of the new armored elytra.</p>
* @return <p>The next step of the builder process.</p>
*/
ItemMetadataSetter newItem(ArmorTier armorTier);
}

View File

@ -0,0 +1,69 @@
package nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder;
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
import org.bukkit.Color;
import org.bukkit.inventory.ItemStack;
import javax.annotation.Nullable;
import java.util.List;
/**
* An interface describing the last step of building an armored elytra: updating metadata
*/
public interface ItemMetadataSetter {
/**
* Specifies the new name of the armored elytra.
*
* @param name The new name of the armored elytra. When this is null (default), the default name for the new
* tier will be used.
* @return <p>The current metadata setter.</p>
*/
ItemMetadataSetter withName(@Nullable String name);
/**
* Specifies the new color of the armored elytra.
*
* @param color <p>The new color of the armored elytra. When this is null (default), the color to use is inferred
* from the creation process.</p>
* @return <p>The current metadata setter.</p>
*/
ItemMetadataSetter withColor(@Nullable Color color);
/**
* Specifies the texture identifier (custom_model_data) for the armored elytra
*
* @param textureId <p>The texture id to set for the armored elytra. If null, the default values will be used.</p>
* @return <p>The current metadata setter.</p>
*/
ItemMetadataSetter withTexture(@Nullable Integer textureId);
/**
* Specifies the new lore of the armored elytra.
*
* @param lore The new lore of the armored elytra. When this is null (default), the default lore for the new
* tier will be used.
* @return <p>The current metadata setter.</p>
*/
//TODO: UNUSED!
ItemMetadataSetter withLore(@Nullable List<String> lore);
/**
* Specifies whether the armored elytra should be unbreakable.
* <p>
* By default, this value is read from {@link ConfigLoader#unbreakable()}.
*
* @param isUnbreakable True if the armored elytra should be unbreakable.
* @return <p>The current metadata setter.</p>
*/
//TODO: UNUSED!
ItemMetadataSetter unbreakable(boolean isUnbreakable);
/**
* Constructs the armored elytra from the provided configuration.
*
* @return <p>The new armored elytra.</p>
*/
ItemStack build();
}

View File

@ -84,42 +84,47 @@ public class NBTEditor {
* @param name The name of the item.
* @param lore The lore of the item.
* @param color The color of the armor to store. May be null.
* @param textureId <p>The id of the texture to use (custom_model_data)</p>
* @return The NEW item.
*/
public ItemStack addArmorNBTTags(ItemStack item, ArmorTier armorTier, boolean unbreakable, String name,
@Nullable List<String> lore, @Nullable Color color) {
@Nullable List<String> lore, @Nullable Color color, @Nullable Integer textureId) {
if (armorTier == null || armorTier == ArmorTier.NONE) {
return new ItemStack(item);
}
final ItemStack ret = new ItemStack(item);
final ItemMeta meta = getOrCreateItemMeta(ret);
meta.getPersistentDataContainer().set(ARMOR_TIER_KEY, PersistentDataType.INTEGER,
final ItemStack returnedItem = new ItemStack(item);
final ItemMeta itemMeta = getOrCreateItemMeta(returnedItem);
itemMeta.getPersistentDataContainer().set(ARMOR_TIER_KEY, PersistentDataType.INTEGER,
ArmorTier.getTierID(armorTier));
if (color != null && armorTier == ArmorTier.LEATHER) {
meta.getPersistentDataContainer().set(ARMOR_COLOR_KEY, PersistentDataType.INTEGER, color.asRGB());
itemMeta.getPersistentDataContainer().set(ARMOR_COLOR_KEY, PersistentDataType.INTEGER, color.asRGB());
}
overwriteNBTValue(meta, Attribute.GENERIC_ARMOR, ArmorTier.getArmor(armorTier), "generic.armor");
overwriteNBTValue(itemMeta, Attribute.GENERIC_ARMOR, ArmorTier.getArmor(armorTier), "generic.armor");
if (ArmorTier.getToughness(armorTier) > 0) {
overwriteNBTValue(meta, Attribute.GENERIC_ARMOR_TOUGHNESS, ArmorTier.getToughness(armorTier),
overwriteNBTValue(itemMeta, Attribute.GENERIC_ARMOR_TOUGHNESS, ArmorTier.getToughness(armorTier),
"generic.armor_toughness");
}
if (ArmorTier.getKnockbackResistance(armorTier) > 0) {
overwriteNBTValue(meta, Attribute.GENERIC_KNOCKBACK_RESISTANCE, ArmorTier.getKnockbackResistance(armorTier),
overwriteNBTValue(itemMeta, Attribute.GENERIC_KNOCKBACK_RESISTANCE, ArmorTier.getKnockbackResistance(armorTier),
"generic.knockback_resistance");
}
meta.setUnbreakable(unbreakable);
meta.setDisplayName(name);
itemMeta.setUnbreakable(unbreakable);
itemMeta.setDisplayName(name);
if (lore != null) {
meta.setLore(lore);
itemMeta.setLore(lore);
}
ret.setItemMeta(meta);
return ret;
if (textureId != null) {
itemMeta.setCustomModelData(textureId);
}
returnedItem.setItemMeta(itemMeta);
return returnedItem;
}
void overwriteNBTValue(ItemMeta meta, Attribute attribute, double value, String modifierName) {

View File

@ -6,6 +6,7 @@ import java.util.HashMap;
import java.util.Map;
public enum ArmorTier {
// Tier: TierID, armor-value, armor-toughness, knockbackResistance, repair, defaultRepairCount, name, durability
NONE(0, 0, 0, 0, null, 0, "", 0),
LEATHER(1, 3, 0, 0, Material.LEATHER, 6, "leather", 80),
@ -13,7 +14,7 @@ public enum ArmorTier {
CHAIN(3, 5, 0, 0, Material.IRON_INGOT, 4, "chain", 240),
IRON(4, 6, 0, 0, Material.IRON_INGOT, 4, "iron", 240),
DIAMOND(5, 8, 2, 0, Material.DIAMOND, 3, "diamond", 528),
NETHERITE(6, 8, 3, 0.1, XMaterial.NETHERITE_INGOT.parseMaterial(), 3, "netherite", 592),
NETHERITE(6, 8, 3, 0.1, Material.NETHERITE_INGOT, 3, "netherite", 592),
;
private final int tierID;
@ -92,7 +93,7 @@ public enum ArmorTier {
}
// 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.
// a new system was introduced that doesn't rely on the armor value for determining the armor-tier.
// Therefore, when using the old backup system, it is always going to be the diamond tier instead.
// While no new elytras can be created using the old system, some may still
// be around from when it was still used.
@ -102,4 +103,5 @@ public enum ArmorTier {
public static int getDefaultRepairCount(ArmorTier armorTier) {
return armorTier.defaultRepairCount;
}
}

View File

@ -16,9 +16,11 @@ import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
public class ConfigLoader {
private static final String HEADER =
"Config file for ArmoredElytra. Don't forget to make a backup before making changes!";
@ -36,12 +38,12 @@ public class ConfigLoader {
private boolean noFlightDurability;
private boolean useTierDurability;
private boolean dropNetheriteAsChestplate;
private LinkedHashSet<Enchantment> allowedEnchantments;
private Set<Enchantment> allowedEnchantments;
private List<List<Enchantment>> mutuallyExclusiveEnchantments;
private boolean craftingInSmithingTable;
private boolean allowUpgradeToNetherite;
private boolean bypassWearPerm;
private boolean bypassCraftPerm;
private boolean bypassWearPermissions;
private boolean bypassCraftPermissions;
private boolean allowRenaming;
private boolean allowAddingEnchantments;
@ -53,112 +55,95 @@ public class ConfigLoader {
// Read the current config, the make a new one based on the old one or default values, whichever is applicable.
private void makeConfig() {
// All the comments for the various config options.
String[] unbreakableComment =
{
"Setting this to true will cause armored elytras to be unbreakable.",
"Changing this to false will NOT make unbreakable elytras breakable again!"
};
String[] flyDurabilityComment =
{
"Setting this to true will cause armored elytras to not lose any durability while flying.",
"This is not a permanent option and will affect ALL elytras."
};
String[] useTierDurabilityComment =
{
"Use the maximum durability of the armor tier of armored elytras.",
"For example, when this is true, a diamond armored elytra would have a durability of 528.",
"When this is false, all armored elytras have the maximum durability of a regular elytra."
};
String[] repairComment =
{
"Amount of items it takes to fully repair an armored elytra",
"Repair cost for every tier of armored elytra in number of items to repair 100%.",
"Note that this value cannot be less than 1."
};
String[] enchantmentsComment =
{
"List of enchantments that are allowed to be put on an armored elytra.",
"If you do not want to allow any enchantments at all, remove them all and add \"NONE\"",
"You can find supported enchantments by running the command:",
"\"armoredelytra listAvailableEnchantments\" in console",
"If you install additional enchantment plugins, you can add their enchantments as well.",
"Just add their 'NamespacedKey'. Ask the enchantment plugin dev for more info if you need it."
};
String[] mutuallyExclusiveEnchantmentsComment =
{
"The lists of enchantments that are mutually exclusive.",
"Each group [] on this list is treated as mutually exclusive, " +
"so only one of them can be on an ArmoredElytra.",
"The default follows modern vanilla rules by making the different " +
"types of protection mutually exclusive.",
"If you do not want any enchantments to be mutually exclusive, " +
"replace all the entries in this list with \"[]\"",
"You can find supported enchantments by running the command:",
"\"armoredelytra listAvailableEnchantments\" in console",
"If you install additional enchant plugins, " +
"you can make their enchantments mutually exclusive as well.",
"Just add their 'NamespacedKey'. Ask the enchantment plugin dev for more info if you need it."
};
String[] dropNetheriteAsChestplateComment =
{
"Whether to drop Netherite Armored Elytras as netherite chestplates when they are dropped",
"This means that they won't burn in lava etc.",
"When you pick them up, they will turn into Netherite Armored Elytras again."
};
String[] updateComment =
{
"Allow this plugin to check for updates on startup. It will not download new versions on its own!"
};
String[] bStatsComment =
{
"Allow this plugin to send (anonymous) stats using bStats. Please consider keeping it enabled.",
"It has a negligible impact on performance and more users on stats keeps me more motivated " +
"to support this plugin!"
};
String[] debugComment =
{
"Print debug messages to console. You will most likely never need this."
};
String[] uninstallComment =
{
"Setting this to true will disable this plugin and remove any armored elytras it can find.",
"It will check player's inventories and their end chest upon login and any regular" +
" chest when it is opened.",
"This means it will take a while for all armored elytras to be removed from your server, " +
"but it doesn't take up ",
"a lot of resources, so you can just leave the plugin enabled and ignore it.",
"Please do not forget to MAKE A BACKUP before enabling this option!"
};
String[] languageFileComment =
{
"Specify a language file to be used."
};
String[] permissionsComment =
{
"Globally bypass permissions for wearing and/or crafting armored elytras.",
"Useful if permissions are unavailable."
};
String[] craftingInSmithingTableComment =
{
"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."
};
String[] allowUpgradeToNetheriteComment =
{
"Whether or not to allow upgrading diamond armored elytras to netherite ones is possible.",
"When allowed, you can combine a diamond one with a netherite ingot in a smithing table",
"and you'll receive a netherite one."
};
String[] allowRenamingComment =
{
"Whether or not to allow renaming of armored elytras in anvils."
};
String[] allowAddingEnchantmentsComment =
{
"Allow new enchantments to be added to armored elytras.",
"When false, no enchantments can be added to armored elytras, even those on the allowed list.",
"When true, only enchantments from the allowed list can be added."
};
String[] unbreakableComment = {
"Setting this to true will cause armored elytras to be unbreakable.",
"Changing this to false will NOT make unbreakable elytras breakable again!"
};
String[] flyDurabilityComment = {
"Setting this to true will cause armored elytras to not lose any durability while flying.",
"This is not a permanent option and will affect ALL elytras."
};
String[] useTierDurabilityComment = {
"Use the maximum durability of the armor tier of armored elytras.",
"For example, when this is true, a diamond armored elytra would have a durability of 528.",
"When this is false, all armored elytras have the maximum durability of a regular elytra."
};
String[] repairComment = {
"Amount of items it takes to fully repair an armored elytra",
"Repair cost for every tier of armored elytra in number of items to repair 100%.",
"Note that this value cannot be less than 1."
};
String[] enchantmentsComment = {
"List of enchantments that are allowed to be put on an armored elytra.",
"If you do not want to allow any enchantments at all, remove them all and add \"NONE\"",
"You can find supported enchantments by running the command:",
"\"armoredelytra listAvailableEnchantments\" in console",
"If you install additional enchantment plugins, you can add their enchantments as well.",
"Just add their 'NamespacedKey'. Ask the enchantment plugin dev for more info if you need it."
};
String[] mutuallyExclusiveEnchantmentsComment = {
"The lists of enchantments that are mutually exclusive.",
"Each group [] on this list is treated as mutually exclusive, " +
"so only one of them can be on an ArmoredElytra.",
"The default follows modern vanilla rules by making the different " +
"types of protection mutually exclusive.",
"If you do not want any enchantments to be mutually exclusive, " +
"replace all the entries in this list with \"[]\"",
"You can find supported enchantments by running the command:",
"\"armoredelytra listAvailableEnchantments\" in console",
"If you install additional enchant plugins, " +
"you can make their enchantments mutually exclusive as well.",
"Just add their 'NamespacedKey'. Ask the enchantment plugin dev for more info if you need it."
};
String[] dropNetheriteAsChestplateComment = {
"Whether to drop Netherite Armored Elytras as netherite chestplates when they are dropped",
"This means that they won't burn in lava etc.",
"When you pick them up, they will turn into Netherite Armored Elytras again."
};
String[] updateComment = {
"Allow this plugin to check for updates on startup. It will not download new versions on its own!"
};
String[] bStatsComment = {
"Allow this plugin to send (anonymous) stats using bStats. Please consider keeping it enabled.",
"It has a negligible impact on performance and more users on stats keeps me more motivated " +
"to support this plugin!"
};
String[] debugComment = {
"Print debug messages to console. You will most likely never need this."
};
String[] uninstallComment = {
"Setting this to true will disable this plugin and remove any armored elytras it can find.",
"It will check player's inventories and their end chest upon login and any regular" +
" chest when it is opened.",
"This means it will take a while for all armored elytras to be removed from your server, " +
"but it doesn't take up ",
"a lot of resources, so you can just leave the plugin enabled and ignore it.",
"Please do not forget to MAKE A BACKUP before enabling this option!"
};
String[] languageFileComment = {
"Specify a language file to be used."
};
String[] permissionsComment = {
"Globally bypass permissions for wearing and/or crafting armored elytras.",
"Useful if permissions are unavailable."
};
String[] craftingInSmithingTableComment = {
"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."
};
String[] allowUpgradeToNetheriteComment = {
"Whether or not to allow upgrading diamond armored elytras to netherite ones is possible.",
"When allowed, you can combine a diamond one with a netherite ingot in a smithing table",
"and you'll receive a netherite one."
};
String[] allowRenamingComment = {
"Whether or not to allow renaming of armored elytras in anvils."
};
String[] allowAddingEnchantmentsComment = {
"Allow new enchantments to be added to armored elytras.",
"When false, no enchantments can be added to armored elytras, even those on the allowed list.",
"When true, only enchantments from the allowed list can be added."
};
// Set default list of allowed enchantments.
List<String> defaultAllowedEnchantments = new ArrayList<>(
@ -177,21 +162,20 @@ public class ConfigLoader {
FileConfiguration config = plugin.getConfig();
unbreakable = addNewConfigOption(config, "unbreakable", false, unbreakableComment);
noFlightDurability = addNewConfigOption(config, "noFlightDurability", false, flyDurabilityComment);
useTierDurability = addNewConfigOption(config, "useTierDurability", true, useTierDurabilityComment);
final ArmorTier[] armorTiers = ArmorTier.values();
for (int idx = 1; idx < armorTiers.length; ++idx) {
final ArmorTier armorTier = armorTiers[idx];
for (int armorTierId = 1; armorTierId < armorTiers.length; ++armorTierId) {
final ArmorTier armorTier = armorTiers[armorTierId];
// Only the first one should have the comment.
final @Nullable String[] comment = idx == 1 ? repairComment : null;
final @Nullable String[] comment = armorTierId == 1 ? repairComment : null;
final String name = Util.snakeToCamelCase(ArmorTier.getRepairItem(armorTier).name());
final int defaultRepairCount = ArmorTier.getDefaultRepairCount(armorTier);
repairCounts[idx] = addNewConfigOption(config, name, defaultRepairCount, comment);
repairCounts[armorTierId] = addNewConfigOption(config, name, defaultRepairCount, comment);
}
final int armorTierCount = ArmorTier.values().length;
@ -227,8 +211,9 @@ public class ConfigLoader {
enableDebug = addNewConfigOption(config, "enableDebug", false, debugComment);
uninstallMode = addNewConfigOption(config, "uninstallMode", false, uninstallComment);
languageFile = addNewConfigOption(config, "languageFile", "en_US", languageFileComment);
bypassWearPerm = addNewConfigOption(config, "bypassWearPermissions", true, permissionsComment);
bypassCraftPerm = addNewConfigOption(config, "bypassCraftPermissions", true, null);
bypassWearPermissions = addNewConfigOption(config, "bypassWearPermissions", true, permissionsComment);
bypassCraftPermissions = addNewConfigOption(config, "bypassCraftPermissions", true, null);
writeConfig();
}
@ -359,7 +344,7 @@ public class ConfigLoader {
return dropNetheriteAsChestplate;
}
public LinkedHashSet<Enchantment> allowedEnchantments() {
public Set<Enchantment> allowedEnchantments() {
return allowedEnchantments;
}
@ -368,14 +353,15 @@ public class ConfigLoader {
}
public boolean bypassWearPerm() {
return bypassWearPerm;
return bypassWearPermissions;
}
public boolean bypassCraftPerm() {
return bypassCraftPerm;
return bypassCraftPermissions;
}
public boolean useTierDurability() {
return useTierDurability;
}
}

View File

@ -36,21 +36,20 @@ public class Util {
}
// Get the armor tier from a chest plate.
public static ArmorTier armorToTier(Material mat) {
ArmorTier ret = ArmorTier.NONE;
XMaterial xmat = XMaterial.matchXMaterial(mat);
public static ArmorTier armorToTier(Material material) {
ArmorTier armorTier = ArmorTier.NONE;
switch (xmat) {
case LEATHER_CHESTPLATE -> ret = ArmorTier.LEATHER;
case GOLDEN_CHESTPLATE -> ret = ArmorTier.GOLD;
case CHAINMAIL_CHESTPLATE -> ret = ArmorTier.CHAIN;
case IRON_CHESTPLATE -> ret = ArmorTier.IRON;
case DIAMOND_CHESTPLATE -> ret = ArmorTier.DIAMOND;
case NETHERITE_CHESTPLATE -> ret = ArmorTier.NETHERITE;
switch (material) {
case LEATHER_CHESTPLATE -> armorTier = ArmorTier.LEATHER;
case GOLDEN_CHESTPLATE -> armorTier = ArmorTier.GOLD;
case CHAINMAIL_CHESTPLATE -> armorTier = ArmorTier.CHAIN;
case IRON_CHESTPLATE -> armorTier = ArmorTier.IRON;
case DIAMOND_CHESTPLATE -> armorTier = ArmorTier.DIAMOND;
case NETHERITE_CHESTPLATE -> armorTier = ArmorTier.NETHERITE;
default -> {
}
}
return ret;
return armorTier;
}
public static boolean isChestPlate(ItemStack itemStack) {
@ -58,17 +57,10 @@ public class Util {
}
// Check if mat is a chest plate.
public static boolean isChestPlate(Material mat) {
try {
XMaterial xmat = XMaterial.matchXMaterial(mat);
return xmat == XMaterial.LEATHER_CHESTPLATE || xmat == XMaterial.GOLDEN_CHESTPLATE ||
xmat == XMaterial.CHAINMAIL_CHESTPLATE || xmat == XMaterial.IRON_CHESTPLATE ||
xmat == XMaterial.DIAMOND_CHESTPLATE || xmat == XMaterial.NETHERITE_CHESTPLATE;
} catch (IllegalArgumentException e) {
// No need to handle this, this is just XMaterial complaining the material doesn't exist.
return false;
}
public static boolean isChestPlate(Material material) {
return material == Material.LEATHER_CHESTPLATE || material == Material.GOLDEN_CHESTPLATE ||
material == Material.CHAINMAIL_CHESTPLATE || material == Material.IRON_CHESTPLATE ||
material == Material.DIAMOND_CHESTPLATE || material == Material.NETHERITE_CHESTPLATE;
}
public static String snakeToCamelCase(String input) {
@ -147,4 +139,23 @@ public class Util {
public static int between(int val, int min, int max) {
return Math.max(min, Math.min(max, val));
}
/**
* Gets the correct texture id from the given armor tier
*
* @param armorTier <p>The armor tier to get texture for</p>
* @return <p>The texture to be used for the given armor tier</p>
*/
public static Integer getTextureIdFromArmorTier(ArmorTier armorTier) {
return switch (armorTier) {
case NONE -> null;
case LEATHER -> 471301;
case GOLD -> 471302;
case CHAIN -> 471303;
case IRON -> 471304;
case DIAMOND -> 471305;
case NETHERITE -> 471306;
};
}
}

View File

@ -1,970 +0,0 @@
package nl.pim16aap2.armoredElytra.util;
/*
* The MIT License (MIT)
*
* Copyright (c) 2018 Hex_27
* Copyright (c) 2020 Crypto Morin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import com.google.common.base.Enums;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.WordUtils;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
/**
* <b>XMaterial</b> - Data Values/Pre-flattening<br>
* 1.13 and above as priority.
* <p>
* This class is mainly designed to support ItemStacks. If you want to use it on blocks you'll have to use <a
* href="https://github.com/CryptoMorin/XSeries/blob/master/XBlock.java">XBlock</a>
* <p>
* Pre-flattening: https://minecraft.gamepedia.com/Java_Edition_data_values/Pre-flattening Materials:
* https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html Materials (1.12):
* https://helpch.at/docs/1.12.2/index.html?org/bukkit/Material.html Material IDs:
* https://minecraft-ids.grahamedgecombe.com/ Material Source Code:
* <p>
* https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/browse/src/main/java/org/bukkit/Material.java XMaterial
* v1: https://www.spigotmc.org/threads/329630/
*
* @author Crypto Morin
* @version 5.0.0
* @see Material
* @see ItemStack
*/
public enum XMaterial {
CHAINMAIL_CHESTPLATE,
DIAMOND_CHESTPLATE,
GOLDEN_CHESTPLATE("GOLD_CHESTPLATE"),
IRON_CHESTPLATE,
LEATHER_CHESTPLATE,
NETHERITE_CHESTPLATE("1.16"),
PHANTOM_MEMBRANE("1.13"),
AIR,
NETHERITE_INGOT("1.16"),
;
/**
* An immutable cached set of {@link XMaterial#values()} to avoid allocating memory for calling the method every
* time.
*
* @since 2.0.0
*/
public static final EnumSet<XMaterial> VALUES = EnumSet.allOf(XMaterial.class);
/**
* A set of material names that can be damaged.
* <p>
* Most of the names are not complete as this list is intended to be checked with {@link String#contains} for memory
* usage.
*
* @since 1.0.0
*/
private static final ImmutableSet<String> DAMAGEABLE = ImmutableSet.of(
"HELMET", "CHESTPLATE", "LEGGINGS", "BOOTS",
"SWORD", "AXE", "PICKAXE", "SHOVEL", "HOE",
"ELYTRA", "TRIDENT", "HORSE_ARMOR", "BARDING",
"SHEARS", "FLINT_AND_STEEL", "BOW", "FISHING_ROD",
"CARROT_ON_A_STICK", "CARROT_STICK", "SPADE", "SHIELD"
);
/*
* A set of all the legacy names without duplicates.
* <p>
* It'll help to free up a lot of memory if it's not used.
* Add it back if you need it.
*
* @see #containsLegacy(String)
* @since 2.2.0
*
private static final ImmutableSet<String> LEGACY_VALUES = VALUES.stream().map(XMaterial::getLegacy)
.flatMap(Arrays::stream)
.filter(m -> m.charAt(1) == '.')
.collect(Collectors.collectingAndThen(Collectors.toSet(), ImmutableSet::copyOf));
*/
/**
* Guava (Google Core Libraries for Java)'s cache for performance and timed caches. For strings that match a certain
* XMaterial. Mostly cached for configs.
*
* @since 1.0.0
*/
private static final Cache<String, XMaterial> NAME_CACHE
= CacheBuilder.newBuilder()
.softValues()
.expireAfterAccess(15, TimeUnit.MINUTES)
.build();
/**
* Guava (Google Core Libraries for Java)'s cache for performance and timed caches. For XMaterials that are already
* parsed once.
*
* @since 3.0.0
*/
private static final Cache<XMaterial, Optional<Material>> PARSED_CACHE
= CacheBuilder.newBuilder()
.softValues()
.expireAfterAccess(10, TimeUnit.MINUTES)
.concurrencyLevel(Runtime.getRuntime().availableProcessors())
.build();
/**
* Pre-compiled RegEx pattern. Include both replacements to avoid recreating string multiple times with multiple
* RegEx checks.
*
* @since 3.0.0
*/
private static final Pattern FORMAT_PATTERN = Pattern.compile("\\W+");
/**
* The current version of the server in the a form of a major version.
*
* @since 1.0.0
*/
private static final int VERSION = Integer.parseInt(getMajorVersion(Bukkit.getVersion()).substring(2));
/**
* Cached result if the server version is after the v1.13 flattening update. Please don't mistake this with
* flat-chested people. It happened.
*
* @since 3.0.0
*/
private static final boolean ISFLAT = supports(13);
/**
* The data value of this material https://minecraft.gamepedia.com/Java_Edition_data_values/Pre-flattening
*
* @see #getData()
*/
private final byte data;
/**
* A list of material names that was being used for older verions.
*
* @see #getLegacy()
*/
private final String[] legacy;
XMaterial(int data, String... legacy) {
this.data = (byte) data;
this.legacy = legacy;
}
XMaterial() {
this(0);
}
XMaterial(String... legacy) {
this(0, legacy);
}
/**
* Checks if the version is 1.13 Aquatic Update or higher. An invocation of this method yields the cached result
* from the expression:
* <p>
* <blockquote>
* {@link #supports(int) 13}}
* </blockquote>
*
* @return true if 1.13 or higher.
* @see #getVersion()
* @see #supports(int)
* @since 1.0.0
*/
public static boolean isNewVersion() {
return ISFLAT;
}
/**
* This is just an extra method that method that can be used for many cases. It can be used in {@link
* org.bukkit.event.player.PlayerInteractEvent} or when accessing {@link org.bukkit.entity.Player#getMainHand()}, or
* other compatibility related methods.
* <p>
* An invocation of this method yields exactly the same result as the expression:
* <p>
* <blockquote>
* {@link #getVersion()} == 1.8
* </blockquote>
*
* @since 2.0.0
*/
public static boolean isOneEight() {
return !supports(9);
}
/**
* The current version of the server.
*
* @return the current server version or 0.0 if unknown.
* @see #isNewVersion()
* @since 2.0.0
*/
public static double getVersion() {
return VERSION;
}
/**
* When using newer versions of Minecraft ({@link #isNewVersion()}), helps to find the old material name with its
* data value using a cached search for optimization.
*
* @see #matchDefinedXMaterial(String, byte)
* @since 1.0.0
*/
@Nullable
private static XMaterial requestOldXMaterial(@Nonnull String name, byte data) {
String holder = name + data;
XMaterial cache = NAME_CACHE.getIfPresent(holder);
if (cache != null) {
return cache;
}
for (XMaterial material : VALUES) {
// Not using material.name().equals(name) check is intended.
if ((data == -1 || data == material.data) && material.anyMatchLegacy(name)) {
NAME_CACHE.put(holder, material);
return material;
}
}
return null;
}
/**
* Checks if XMaterial enum contains a material with the given name.
* <p>
* You should use {@link #matchXMaterial(String)} instead if you're going to get the XMaterial object after checking
* if it's available in the list by doing a simple {@link Optional#isPresent()} check. This is just to avoid
* multiple loops for maximum performance.
*
* @param name name of the material.
* @return true if XMaterial enum has this material.
* @since 1.0.0
*/
public static boolean contains(@Nonnull String name) {
Validate.notEmpty(name, "Cannot check for null or empty material name");
name = format(name);
for (XMaterial materials : VALUES)
if (materials.name().equals(name)) {
return true;
}
return false;
}
/**
* Parses the given material name as an XMaterial with unspecified data value.
*
* @see #matchXMaterialWithData(String)
* @since 2.0.0
*/
@Nonnull
public static Optional<XMaterial> matchXMaterial(@Nonnull String name) {
Validate.notEmpty(name, "Cannot match a material with null or empty material name");
Optional<XMaterial> oldMatch = matchXMaterialWithData(name);
if (oldMatch.isPresent()) {
return oldMatch;
}
return matchDefinedXMaterial(format(name), (byte) -1);
}
/**
* Parses material name and data value from the specified string. The seperators are: <b>, or :</b> Spaces are
* allowed. Mostly used when getting materials from config for old school minecrafters.
* <p>
* <b>Examples</b>
* <p><pre>
* {@code INK_SACK:1 -> RED_DYE}
* {@code WOOL, 14 -> RED_WOOL}
* </pre>
*
* @param name the material string that consists of the material name, data and separator character.
* @return the parsed XMaterial.
* @see #matchXMaterial(String)
* @since 3.0.0
*/
private static Optional<XMaterial> matchXMaterialWithData(String name) {
for (char separator : new char[]{',', ':'}) {
int index = name.indexOf(separator);
if (index == -1) {
continue;
}
String mat = format(name.substring(0, index));
byte data = Byte.parseByte(StringUtils.deleteWhitespace(name.substring(index + 1)));
return matchDefinedXMaterial(mat, data);
}
return Optional.empty();
}
/**
* Parses the given material as an XMaterial.
*
* @throws IllegalArgumentException may be thrown as an unexpected exception.
* @see #matchDefinedXMaterial(String, byte)
* @see #matchXMaterial(ItemStack)
* @since 2.0.0
*/
@Nonnull
public static XMaterial matchXMaterial(@Nonnull Material material) {
Objects.requireNonNull(material, "Cannot match null material");
return matchDefinedXMaterial(material.name(), (byte) -1)
.orElseThrow(() -> new IllegalArgumentException("Unsupported Material With No Bytes: " + material.name()));
}
/**
* Parses the given item as an XMaterial using its material and data value (durability).
*
* @param item the ItemStack to match.
* @return an XMaterial if matched any.
* @throws IllegalArgumentException may be thrown as an unexpected exception.
* @see #matchDefinedXMaterial(String, byte)
* @since 2.0.0
*/
@Nonnull
@SuppressWarnings("deprecation")
public static XMaterial matchXMaterial(@Nonnull ItemStack item) {
Objects.requireNonNull(item, "Cannot match null ItemStack");
String material = item.getType().name();
byte data = (byte) (ISFLAT || isDamageable(material) ? 0 : item.getDurability());
return matchDefinedXMaterial(material, data)
.orElseThrow(() -> new IllegalArgumentException("Unsupported Material: " + material + " (" + data + ')'));
}
/**
* Parses the given material name and data value as an XMaterial. All the values passed to this method will not be
* null or empty and are formatted correctly.
*
* @param name the formatted name of the material.
* @param data the data value of the material.
* @return an XMaterial (with the same data value if specified)
* @see #matchXMaterial(Material)
* @see #matchXMaterial(int, byte)
* @see #matchXMaterial(ItemStack)
* @since 3.0.0
*/
@Nonnull
private static Optional<XMaterial> matchDefinedXMaterial(@Nonnull String name, byte data) {
boolean duplicated = isDuplicated(name);
// Do basic number and boolean checks before accessing more complex enum stuff.
// Maybe we can simplify (ISFLAT || !duplicated) with the (!ISFLAT && duplicated)
// under it to save a few nanoseconds?
if (data <= 0 && !duplicated) {
// Apparently the transform method is more efficient than toJavaUtil()
// toJavaUtil isn't even supported in older versions.
Optional<XMaterial> xMat =
Enums.getIfPresent(XMaterial.class, name).transform(Optional::of).or(Optional.empty());
if (xMat.isPresent()) {
return xMat;
}
}
// XMaterial Paradox (Duplication Check)
// I've concluded that this is just an infinite loop that keeps
// going around the Singular Form and the Plural Form materials. A waste of brain cells and a waste of time.
// This solution works just fine anyway.
XMaterial xMat = requestOldXMaterial(name, data);
if (xMat == null) {
return Optional.empty();
}
if (!ISFLAT && duplicated && xMat.name().charAt(xMat.name().length() - 1) == 'S') {
// A solution for XMaterial Paradox.
// Manually parses the duplicated materials to find the exact material based on the server version.
// If ends with "S" -> Plural Form Material
return Enums.getIfPresent(XMaterial.class, name).transform(Optional::of).or(Optional.empty());
}
return Optional.ofNullable(xMat);
}
/**
* <b>XMaterial Paradox (Duplication Check)</b>
* Checks if the material has any duplicates.
* <p>
* <b>Example:</b>
* <p>{@code MELON, CARROT, POTATO, BEETROOT -> true}
*
* @param name the name of the material to check.
* @return true if there's a duplicated material for this material, otherwise false.
* @see #isDuplicated()
* @since 2.0.0
*/
private static boolean isDuplicated(@Nonnull String name) {
return false;
}
/**
* Gets the XMaterial based on the material's ID (Magic Value) and data value.<br> You should avoid using this for
* performance issues.
*
* @param id the ID (Magic value) of the material.
* @param data the data value of the material.
* @return a parsed XMaterial with the same ID and data value.
* @see #matchXMaterial(ItemStack)
* @since 2.0.0
*/
@Nonnull
public static Optional<XMaterial> matchXMaterial(int id, byte data) {
if (id < 0 || data < 0) {
return Optional.empty();
}
// Looping through Material.values() will take longer.
for (XMaterial materials : VALUES)
if (materials.data == data && materials.getId() == id) {
return Optional.of(materials);
}
return Optional.empty();
}
/**
* Attempts to build the string like an enum name. Removes all the spaces, numbers and extra non-English characters.
* Also removes some config/in-game based strings.
*
* @param name the material name to modify.
* @return a Material enum name.
* @since 2.0.0
*/
@Nonnull
private static String format(@Nonnull String name) {
return FORMAT_PATTERN.matcher(
name.trim().replace('-', '_').replace(' ', '_')).replaceAll("").toUpperCase(Locale.ENGLISH);
}
/**
* Checks if the specified version is the same version or higher than the current server version.
*
* @param version the major version to be checked. "1." is ignored. E.g. 1.12 = 12 | 1.9 = 9
* @return true of the version is equal or higher than the current version.
* @since 2.0.0
*/
public static boolean supports(int version) {
return VERSION >= version;
}
/**
* Converts the enum names to a more friendly and readable string.
*
* @return a formatted string.
* @see #toWord(String)
* @since 2.1.0
*/
@Nonnull
public static String toWord(@Nonnull Material material) {
Objects.requireNonNull(material, "Cannot translate a null material to a word");
return toWord(material.name());
}
/**
* Parses an enum name to a normal word. Normal names have underlines removed and each word capitalized.
* <p>
* <b>Examples:</b>
* <pre>
* EMERALD -> Emerald
* EMERALD_BLOCK -> Emerald Block
* ENCHANTED_GOLDEN_APPLE -> Enchanted Golden Apple
* </pre>
*
* @param name the name of the enum.
* @return a cleaned more readable enum name.
* @since 2.1.0
*/
@Nonnull
private static String toWord(@Nonnull String name) {
return WordUtils.capitalize(name.replace('_', ' ').toLowerCase(Locale.ENGLISH));
}
/**
* Gets the exact major version (..., 1.9, 1.10, ..., 1.14)
*
* @param version Supports {@link Bukkit#getVersion()}, {@link Bukkit#getBukkitVersion()} and normal formats such as
* "1.14"
* @return the exact major version.
* @since 2.0.0
*/
@Nonnull
public static String getMajorVersion(@Nonnull String version) {
Validate.notEmpty(version, "Cannot get major Minecraft version from null or empty string");
// getVersion()
int index = version.lastIndexOf("MC:");
if (index != -1) {
version = version.substring(index + 4, version.length() - 1);
} else if (version.endsWith("SNAPSHOT")) {
// getBukkitVersion()
index = version.indexOf('-');
version = version.substring(0, index);
}
// 1.13.2, 1.14.4, etc...
int lastDot = version.lastIndexOf('.');
if (version.indexOf('.') != lastDot) {
version = version.substring(0, lastDot);
}
return version;
}
/**
* Checks if the material can be damaged by using it. Names going through this method are not formatted.
*
* @param name the name of the material.
* @return true of the material can be damaged.
* @see #isDamageable()
* @since 1.0.0
*/
public static boolean isDamageable(@Nonnull String name) {
Objects.requireNonNull(name, "Material name cannot be null");
for (String damageable : DAMAGEABLE)
if (name.contains(damageable)) {
return true;
}
return false;
}
/**
* Checks if the list of given material names matches the given base material. Mostly used for configs.
* <p>
* Supports {@link String#contains} {@code CONTAINS:NAME} and Regular Expression {@code REGEX:PATTERN} formats.
* <p>
* <b>Example:</b>
* <blockquote><pre>
* XMaterial material = {@link #matchXMaterial(ItemStack)};
* if (material.isOneOf(plugin.getConfig().getStringList("disabled-items")) return;
* </pre></blockquote>
* <br>
* <b>{@code CONTAINS} Examples:</b>
* <pre>
* {@code "CONTAINS:CHEST" -> CHEST, ENDERCHEST, TRAPPED_CHEST -> true}
* {@code "cOnTaINS:dYe" -> GREEN_DYE, YELLOW_DYE, BLUE_DYE, INK_SACK -> true}
* </pre>
* <p>
* <b>{@code REGEX} Examples</b>
* <pre>
* {@code "REGEX:^.+_.+_.+$" -> Every Material with 3 underlines or more: SHULKER_SPAWN_EGG,
* SILVERFISH_SPAWN_EGG, SKELETON_HORSE_SPAWN_EGG}
* {@code "REGEX:^.{1,3}$" -> Material names that have 3 letters only: BED, MAP, AIR}
* </pre>
* <p>
* The reason that there are tags for {@code CONTAINS} and {@code REGEX} is for the performance. Please avoid using
* the {@code REGEX} tag if you can use the {@code CONTAINS} tag. It'll have a huge impact on performance. Please
* avoid using {@code (capturing groups)} there's no use for them in this case. If you want to use groups, use
* {@code (?: non-capturing groups)}. It's faster.
* <p>
* You can make a cache for pre-compiled RegEx patterns from your config. It's better, but not much faster since
* these patterns are not that complex.
* <p>
* Want to learn RegEx? You can mess around in <a href="https://regexr.com/">RegExr</a> website.
*
* @param material the base material to match other materials with.
* @param materials the material names to check base material on.
* @return true if one of the given material names is similar to the base material.
* @since 3.1.1
*/
public static boolean isOneOf(@Nonnull Material material, @Nullable List<String> materials) {
if (materials == null || materials.isEmpty()) {
return false;
}
Objects.requireNonNull(material, "Cannot match materials with a null material");
String name = material.name();
for (String comp : materials) {
comp = comp.toUpperCase();
if (comp.startsWith("CONTAINS:")) {
comp = format(comp.substring(9));
if (name.contains(comp)) {
return true;
}
continue;
}
if (comp.startsWith("REGEX:")) {
comp = comp.substring(6);
if (name.matches(comp)) {
return true;
}
continue;
}
// Direct Object Equals
Optional<XMaterial> mat = matchXMaterial(comp);
if (mat.isPresent() && mat.get().parseMaterial() == material) {
return true;
}
}
return false;
}
/**
* Gets the version which this material was added in. If the material doesn't have a version it'll return 0;
*
* @return the Minecraft version which tihs material was added in.
* @since 3.0.0
*/
public int getMaterialVersion() {
if (this.legacy.length == 0) {
return 0;
}
String version = this.legacy[0];
if (version.charAt(1) != '.') {
return 0;
}
return Integer.parseInt(version.substring(2));
}
/**
* Sets the {@link Material} (and data value on older versions) of an item. Damageable materials will not have their
* durability changed.
* <p>
* Use {@link #parseItem()} instead when creating new ItemStacks.
*
* @param item the item to change its type.
* @see #parseItem()
* @since 3.0.0
*/
@Nonnull
@SuppressWarnings("deprecation")
public ItemStack setType(@Nonnull ItemStack item) {
Objects.requireNonNull(item, "Cannot set material for null ItemStack");
item.setType(this.parseMaterial());
if (!ISFLAT && !this.isDamageable()) {
item.setDurability(this.data);
}
return item;
}
/**
* Checks if the list of given material names matches the given base material. Mostly used for configs.
*
* @param materials the material names to check base material on.
* @return true if one of the given material names is similar to the base material.
* @see #isOneOf(Material, List)
* @since 3.0.0
*/
public boolean isOneOf(@Nullable List<String> materials) {
Material material = this.parseMaterial();
if (material == null) {
return false;
}
return isOneOf(material, materials);
}
/**
* Checks if the given string matches any of this material's legacy material names. All the values passed to this
* method will not be null or empty and are formatted correctly.
*
* @param name the name to check
* @return true if it's one of the legacy names.
* @since 2.0.0
*/
private boolean anyMatchLegacy(@Nonnull String name) {
for (String legacy : this.legacy) {
if (legacy.isEmpty())
// Left-side suggestion list
{
break;
}
if (name.equals(legacy)) {
return true;
}
}
return false;
}
/**
* User-friendly readable name for this material In most cases you should be using {@link #name()} instead.
*
* @return string of this object.
* @see #toWord(String)
* @since 3.0.0
*/
@Override
public String toString() {
return toWord(this.name());
}
/**
* Gets the ID (Magic value) of the material.
*
* @return the ID of the material or <b>-1</b> if it's a new block or the material is not supported.
* @see #matchXMaterial(int, byte)
* @since 2.2.0
*/
@SuppressWarnings("deprecation")
public int getId() {
if (this.data != 0 || (this.legacy.length != 0 && Integer.parseInt(this.legacy[0].substring(2)) >= 13)) {
return -1;
}
Material material = this.parseMaterial();
return material == null ? -1 : material.getId();
}
/**
* Checks if the material has any duplicates.
*
* @return true if there is a duplicated name for this material, otherwise false.
* @see #isDuplicated(String)
* @since 2.0.0
*/
public boolean isDuplicated() {
return false;
}
/**
* Checks if the material can be damaged by using it. Names going through this method are not formatted.
*
* @return true if the item can be damaged (have its durability changed), otherwise false.
* @see #isDamageable(String)
* @since 1.0.0
*/
public boolean isDamageable() {
return isDamageable(this.name());
}
/**
* The data value of this material
* <a href="https://minecraft.gamepedia.com/Java_Edition_data_values/Pre-flattening">pre-flattening</a>.
* <p>
* Can be accessed with {@link ItemStack#getData()} then {@code MaterialData#getData()} or {@link
* ItemStack#getDurability()} if not damageable.
*
* @return data of this material, or 0 if none.
* @since 1.0.0
*/
@SuppressWarnings("deprecation")
public byte getData() {
return data;
}
/**
* Get a list of materials names that was previously used by older versions. If the material was added in a new
* version {@link #isNewVersion()}, then the first element will indicate which version the material was added in.
*
* @return a list of legacy material names and the first element as the version the material was added in if new.
* @since 1.0.0
*/
@Nonnull
public String[] getLegacy() {
return legacy;
}
/**
* Parses an item from this XMaterial. Uses data values on older versions.
*
* @return an ItemStack with the same material (and data value if in older versions.)
* @see #parseItem(boolean)
* @see #setType(ItemStack)
* @since 1.0.0
*/
@Nullable
public ItemStack parseItem() {
return parseItem(false);
}
/**
* Parses an item from this XMaterial. Uses data values on older versions.
*
* @param suggest if true {@link #parseMaterial(boolean)} true will be used.
* @return an ItemStack with the same material (and data value if in older versions.)
* @see #setType(ItemStack)
* @since 2.0.0
*/
@Nullable
@SuppressWarnings("deprecation")
public ItemStack parseItem(boolean suggest) {
Material material = this.parseMaterial(suggest);
if (material == null) {
return null;
}
return ISFLAT ? new ItemStack(material) : new ItemStack(material, 1, this.data);
}
/**
* Parses the material of this XMaterial.
*
* @return the material related to this XMaterial based on the server version.
* @see #parseMaterial(boolean)
* @since 1.0.0
*/
@Nullable
public Material parseMaterial() {
return parseMaterial(false);
}
/**
* Parses the material of this XMaterial and accepts suggestions.
*
* @param suggest use a suggested material (from older materials) if the material is added in a later version of
* Minecraft.
* @return the material related to this XMaterial based on the server version.
* @since 2.0.0
*/
@SuppressWarnings("OptionalAssignedToNull")
@Nullable
public Material parseMaterial(boolean suggest) {
Optional<Material> cache = PARSED_CACHE.getIfPresent(this);
if (cache != null) {
return cache.orElse(null);
}
Material mat;
if (!ISFLAT && this.isDuplicated()) {
mat = requestOldMaterial(suggest);
} else {
mat = Material.getMaterial(this.name());
if (mat == null) {
mat = requestOldMaterial(suggest);
}
}
if (mat != null) {
PARSED_CACHE.put(this, Optional.ofNullable(mat));
}
return mat;
}
/**
* Parses a material for older versions of Minecraft. Accepts suggestions if specified.
*
* @param suggest if true suggested materials will be considered for old versions.
* @return a parsed material suitable for the current Minecraft version.
* @see #parseMaterial(boolean)
* @since 2.0.0
*/
@Nullable
private Material requestOldMaterial(boolean suggest) {
for (int i = this.legacy.length - 1; i >= 0; i--) {
String legacy = this.legacy[i];
// Check if we've reached the end and the last string is our
// material version.
if (i == 0 && legacy.charAt(1) == '.') {
return null;
}
// According to the suggestion list format, all the other names continuing
// from here are considered as a "suggestion"
// The empty string is an indicator for suggestion list on the left side.
if (legacy.isEmpty()) {
if (suggest) {
continue;
}
break;
}
Material material = Material.getMaterial(legacy);
if (material != null) {
return material;
}
}
return null;
}
/**
* Checks if an item has the same material (and data value on older versions).
*
* @param item item to check.
* @return true if the material is the same as the item's material (and data value if on older versions), otherwise
* false.
* @since 1.0.0
*/
@SuppressWarnings("deprecation")
public boolean isSimilar(@Nonnull ItemStack item) {
Objects.requireNonNull(item, "Cannot compare with null ItemStack");
if (item.getType() != this.parseMaterial()) {
return false;
}
return ISFLAT || this.isDamageable() || item.getDurability() == this.data;
}
/**
* Gets the suggested material names that can be used if the material is not supported in the current version.
*
* @return a list of suggested material names.
* @see #parseMaterial(boolean)
* @since 2.0.0
*/
@Nonnull
public List<String> getSuggestions() {
if (this.legacy.length == 0 || this.legacy[0].charAt(1) != '.') {
return new ArrayList<>();
}
List<String> suggestions = new ArrayList<>();
for (String legacy : this.legacy) {
if (legacy.isEmpty()) {
break;
}
suggestions.add(legacy);
}
return suggestions;
}
/**
* Checks if this material is supported in the current version. Suggested materials will be ignored.
* <p>
* Note that you should use {@link #parseMaterial()} and check if it's null if you're going to parse and use the
* material later.
*
* @return true if the material exists in {@link Material} list.
* @since 2.0.0
*/
public boolean isSupported() {
int version = this.getMaterialVersion();
if (version != 0) {
return supports(version);
}
Material material = Material.getMaterial(this.name());
if (material != null) {
return true;
}
return requestOldMaterial(false) != null;
}
/**
* Checks if the material is newly added after the 1.13 Aquatic Update.
*
* @return true if the material was newly added, otherwise false.
* @see #getMaterialVersion()
* @since 2.0.0
*/
public boolean isFromNewSystem() {
return this.legacy.length != 0 && Integer.parseInt(this.legacy[0].substring(2)) > 13;
}
}

View File

@ -1,11 +0,0 @@
package nl.pim16aap2.armoredElytra.util.messages;
/**
* Represents supported variables in localizable messages.
*
* @author Pim
*/
public interface IMessageVariable {
String VAR_TIER_NAME = "%ARMOR_TIER%";
String VAR_TIER_NAME_SHORT = "%ARMOR_TIER_SHORT%";
}

View File

@ -5,7 +5,7 @@ package nl.pim16aap2.armoredElytra.util.messages;
*
* @author Pim
*/
public enum Message implements IMessageVariable {
public enum Message implements MessageVariable {
EMPTY(),
TIER_LEATHER(),
@ -26,10 +26,10 @@ public enum Message implements IMessageVariable {
MESSAGES_UNSUPPORTEDTIER(),
MESSAGES_REPAIRNEEDED(),
MESSAGES_LORE(VAR_TIER_NAME, VAR_TIER_NAME_SHORT),
MESSAGES_NOGIVEPERMISSION(VAR_TIER_NAME, VAR_TIER_NAME_SHORT),
MESSAGES_USAGEDENIED(VAR_TIER_NAME, VAR_TIER_NAME_SHORT),
MESSAGES_ELYTRARECEIVED(VAR_TIER_NAME, VAR_TIER_NAME_SHORT),
MESSAGES_LORE(VARIABLE_TIER_NAME, VARIABLE_TIER_NAME_SHORT),
MESSAGES_NOGIVEPERMISSION(VARIABLE_TIER_NAME, VARIABLE_TIER_NAME_SHORT),
MESSAGES_USAGEDENIED(VARIABLE_TIER_NAME, VARIABLE_TIER_NAME_SHORT),
MESSAGES_ELYTRARECEIVED(VARIABLE_TIER_NAME, VARIABLE_TIER_NAME_SHORT),
;

View File

@ -0,0 +1,13 @@
package nl.pim16aap2.armoredElytra.util.messages;
/**
* Represents supported variables in localizable messages.
*
* @author Pim
*/
public interface MessageVariable {
String VARIABLE_TIER_NAME = "%ARMOR_TIER%";
String VARIABLE_TIER_NAME_SHORT = "%ARMOR_TIER_SHORT%";
}

View File

@ -3,12 +3,39 @@ main: nl.pim16aap2.armoredElytra.ArmoredElytra
version: ${project.version}
author: pim16aap2
softdepend: [ AdvancedEnchantments ]
api-version: 1.13
api-version: 1.16
commands:
ArmoredElytra:
description: Give an armored elytra of the specified tier.
usage: /ArmoredElytra [receiver] <tier>
permissions:
armoredelytra.wear:
description: Allows the player to wear all armored elytras.
children:
- armoredelytra.wear.leather
- armoredelytra.wear.gold
- armoredelytra.wear.chain
- armoredelytra.wear.iron
- armoredelytra.wear.diamond
- armoredelytra.wear.netherite
armoredelytra.craft:
description: Allows the player to craft all armored elytras.
children:
- armoredelytra.craft.leather
- armoredelytra.craft.gold
- armoredelytra.craft.chain
- armoredelytra.craft.iron
- armoredelytra.craft.diamond
- armoredelytra.craft.netherite
armoredelytra.give:
description: Allows the player to give all armored elytras.
children:
- armoredelytra.give.leather
- armoredelytra.give.gold
- armoredelytra.give.chain
- armoredelytra.give.iron
- armoredelytra.give.diamond
- armoredelytra.give.netherite
armoredelytra.wear.leather:
description: Allow the player to wear leather tier armored elytras.
armoredelytra.wear.gold: