Add config option, improve multi-version support
- Added config option to allow multiple types of protection enchantments to be applied to a single armored elytra. - Added XMaterial class that was accidentally omitted in the last commit.
This commit is contained in:
		@@ -117,32 +117,35 @@ public class EventHandlers implements Listener
 | 
			
		||||
                combined.put(entry.getKey(), entry.getValue());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Get the protection enchantment rating for both enchantment sets.
 | 
			
		||||
        int protVal0 = Util.getProtectionEnchantmentsVal(enchantments0);
 | 
			
		||||
        int protVal1 = Util.getProtectionEnchantmentsVal(enchantments1);
 | 
			
		||||
        if (!plugin.getConfigLoader().allowMultipleProtectionEnchantments())
 | 
			
		||||
        {
 | 
			
		||||
            // Get the protection enchantment rating for both enchantment sets.
 | 
			
		||||
            int protVal0 = Util.getProtectionEnchantmentsVal(enchantments0);
 | 
			
		||||
            int protVal1 = Util.getProtectionEnchantmentsVal(enchantments1);
 | 
			
		||||
 | 
			
		||||
        // If they have different protection enchantments, keep enchantment1's enchantments
 | 
			
		||||
        // And remove the protection enchantment from enchantments0. Yes, this system only works
 | 
			
		||||
        // If there is 1 protection enchantment on
 | 
			
		||||
        if (protVal0 != 0 && protVal1 != 0 && protVal0 != protVal1)
 | 
			
		||||
            switch(protVal0)
 | 
			
		||||
            {
 | 
			
		||||
            case 1:
 | 
			
		||||
                combined.remove(Enchantment.PROTECTION_ENVIRONMENTAL);
 | 
			
		||||
                break;
 | 
			
		||||
            case 2:
 | 
			
		||||
                combined.remove(Enchantment.PROTECTION_EXPLOSIONS);
 | 
			
		||||
                break;
 | 
			
		||||
            case 4:
 | 
			
		||||
                combined.remove(Enchantment.PROTECTION_FALL);
 | 
			
		||||
                break;
 | 
			
		||||
            case 8:
 | 
			
		||||
                combined.remove(Enchantment.PROTECTION_FIRE);
 | 
			
		||||
                break;
 | 
			
		||||
            case 16:
 | 
			
		||||
                combined.remove(Enchantment.PROTECTION_PROJECTILE);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            // If they have different protection enchantments, keep enchantment1's enchantments
 | 
			
		||||
            // And remove the protection enchantment from enchantments0. Yes, this system only works
 | 
			
		||||
            // If there is 1 protection enchantment on
 | 
			
		||||
            if (protVal0 != 0 && protVal1 != 0 && protVal0 != protVal1)
 | 
			
		||||
                switch(protVal0)
 | 
			
		||||
                {
 | 
			
		||||
                case 1:
 | 
			
		||||
                    combined.remove(Enchantment.PROTECTION_ENVIRONMENTAL);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 2:
 | 
			
		||||
                    combined.remove(Enchantment.PROTECTION_EXPLOSIONS);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 4:
 | 
			
		||||
                    combined.remove(Enchantment.PROTECTION_FALL);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 8:
 | 
			
		||||
                    combined.remove(Enchantment.PROTECTION_FIRE);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 16:
 | 
			
		||||
                    combined.remove(Enchantment.PROTECTION_PROJECTILE);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
        return combined;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@ public class ConfigLoader
 | 
			
		||||
    private int DIAMONDS_TO_FULL;
 | 
			
		||||
    private boolean noFlightDurability;
 | 
			
		||||
    private List<String> allowedEnchantments;
 | 
			
		||||
    private boolean allowMultipleProtectionEnchantments;
 | 
			
		||||
 | 
			
		||||
    private ArrayList<ConfigOption<?>> configOptionsList;
 | 
			
		||||
    private ArmoredElytra plugin;
 | 
			
		||||
@@ -93,6 +94,14 @@ public class ConfigLoader
 | 
			
		||||
            {
 | 
			
		||||
                "Specify a language file to be used. Note that en_US.txt will get regenerated!"
 | 
			
		||||
            };
 | 
			
		||||
        String[] allowMultipleProtectionEnchantmentsComment =
 | 
			
		||||
            {
 | 
			
		||||
                "Allow more than 1 type of protection enchantment on a single armored elytra. ",
 | 
			
		||||
                "If true, you could have both blast protection and environmental protection at the same time.",
 | 
			
		||||
                "If false, the second enchantment (while crafting) will override the first. So combining an armored",
 | 
			
		||||
                "elytra that has the protection enchantment with an enchanted book that has the blast protection enchantment",
 | 
			
		||||
                "would result in removal of the protection enchantment and addition of the blast protection enchantment."
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        // Set default list of allowed enchantments.
 | 
			
		||||
        allowedEnchantments = new ArrayList<>(Arrays.asList("DURABILITY", "PROTECTION_FIRE", "PROTECTION_EXPLOSIONS",
 | 
			
		||||
@@ -108,6 +117,7 @@ public class ConfigLoader
 | 
			
		||||
        IRON_TO_FULL = addNewConfigOption(config, "ironRepair", 4, null);
 | 
			
		||||
        DIAMONDS_TO_FULL = addNewConfigOption(config, "diamondsRepair", 3, null);
 | 
			
		||||
        allowedEnchantments = addNewConfigOption(config, "allowedEnchantments", allowedEnchantments, enchantmentsComment);
 | 
			
		||||
        allowMultipleProtectionEnchantments = addNewConfigOption(config, "allowMultipleProtectionEnchantments", false, allowMultipleProtectionEnchantmentsComment);
 | 
			
		||||
        checkForUpdates = addNewConfigOption(config, "checkForUpdates", true, updateComment);
 | 
			
		||||
        allowStats = addNewConfigOption(config, "allowStats", true, bStatsComment);
 | 
			
		||||
        enableDebug = addNewConfigOption(config, "enableDebug", false, debugComment);
 | 
			
		||||
@@ -205,6 +215,11 @@ public class ConfigLoader
 | 
			
		||||
        return DIAMONDS_TO_FULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean allowMultipleProtectionEnchantments()
 | 
			
		||||
    {
 | 
			
		||||
        return allowMultipleProtectionEnchantments;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean uninstallMode()
 | 
			
		||||
    {
 | 
			
		||||
        return uninstallMode;
 | 
			
		||||
 
 | 
			
		||||
@@ -76,6 +76,7 @@ public class Util
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 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;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										656
									
								
								src/main/java/nl/pim16aap2/armoredElytra/util/XMaterial.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										656
									
								
								src/main/java/nl/pim16aap2/armoredElytra/util/XMaterial.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,656 @@
 | 
			
		||||
package nl.pim16aap2.armoredElytra.util;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The MIT License (MIT)
 | 
			
		||||
 *
 | 
			
		||||
 * Original work Copyright (c) 2018 Hex_27
 | 
			
		||||
 * v2.0 Copyright (c) 2019 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 org.apache.commons.lang.Validate;
 | 
			
		||||
import org.bukkit.Bukkit;
 | 
			
		||||
import org.bukkit.Material;
 | 
			
		||||
import org.bukkit.inventory.ItemStack;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * References
 | 
			
		||||
 *
 | 
			
		||||
 * * * GitHub: https://github.com/CryptoMorin/XMaterial/blob/master/XMaterial.java
 | 
			
		||||
 * * Thread: https://www.spigotmc.org/threads/378136/
 | 
			
		||||
 * https://minecraft.gamepedia.com/Java_Edition_data_values/Pre-flattening
 | 
			
		||||
 * https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html
 | 
			
		||||
 * http://docs.codelanx.com/Bukkit/1.8/org/bukkit/Material.html
 | 
			
		||||
 * https://www.spigotmc.org/threads/1-8-to-1-13-itemstack-material-version-support.329630/
 | 
			
		||||
 * https://minecraft-ids.grahamedgecombe.com/
 | 
			
		||||
 * v1: https://pastebin.com/Fe65HZnN
 | 
			
		||||
 * v2: 6/15/2019
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * XMaterial v2.2 - Data Values/Pre-flattening Supports 1.8-1.14 1.13 and above
 | 
			
		||||
 * as priority.
 | 
			
		||||
 */
 | 
			
		||||
public enum XMaterial
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    CHAINMAIL_CHESTPLATE(0, ""),
 | 
			
		||||
    DIAMOND_CHESTPLATE(0, ""),
 | 
			
		||||
    GOLDEN_CHESTPLATE(0, "GOLD_CHESTPLATE"),
 | 
			
		||||
    IRON_CHESTPLATE(0, ""),
 | 
			
		||||
    LEATHER_CHESTPLATE(0, ""),
 | 
			
		||||
 | 
			
		||||
    AIR(0, ""),
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A list of material names that can be damaged. Some names are not complete as
 | 
			
		||||
     * this list needs to be checked with {@link String#contains}.
 | 
			
		||||
     */
 | 
			
		||||
    public static final String[] DAMAGEABLE = { "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" };
 | 
			
		||||
 | 
			
		||||
    public static final XMaterial[] VALUES = XMaterial.values();
 | 
			
		||||
    private static final HashMap<String, XMaterial> CACHED_SEARCH = new HashMap<>();
 | 
			
		||||
    private static MinecraftVersion version;
 | 
			
		||||
    private static Boolean isNewVersion;
 | 
			
		||||
    private final byte data;
 | 
			
		||||
    private final String[] legacy;
 | 
			
		||||
 | 
			
		||||
    XMaterial(int data, String... legacy)
 | 
			
		||||
    {
 | 
			
		||||
        this.data = (byte) data;
 | 
			
		||||
        this.legacy = legacy;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the version is 1.13 (Aquatic Update) or higher.
 | 
			
		||||
     *
 | 
			
		||||
     * @return true if 1.13 or higher.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isNewVersion()
 | 
			
		||||
    {
 | 
			
		||||
        if (isNewVersion != null)
 | 
			
		||||
            return isNewVersion;
 | 
			
		||||
        return isNewVersion = isVersionOrHigher(MinecraftVersion.VERSION_1_13);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static boolean isOneEight()
 | 
			
		||||
    {
 | 
			
		||||
        return getVersion() == MinecraftVersion.VERSION_1_8;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Uses newly added materials to minecraft to detect the server version.
 | 
			
		||||
     *
 | 
			
		||||
     * @return the current server version.
 | 
			
		||||
     */
 | 
			
		||||
    public static MinecraftVersion getVersion()
 | 
			
		||||
    {
 | 
			
		||||
        if (version != null)
 | 
			
		||||
            return version;
 | 
			
		||||
        return version = valueOfVersion(Bukkit.getVersion());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * When using newer versions of Minecraft {@link #isNewVersion()} this helps to
 | 
			
		||||
     * find the old material name with its data using a cached search for
 | 
			
		||||
     * optimization.
 | 
			
		||||
     *
 | 
			
		||||
     * @see #matchXMaterial(String, byte)
 | 
			
		||||
     */
 | 
			
		||||
    private static XMaterial requestOldXMaterial(String name, byte data)
 | 
			
		||||
    {
 | 
			
		||||
        XMaterial cached = CACHED_SEARCH.get(name + "," + data);
 | 
			
		||||
 | 
			
		||||
        if (cached != null)
 | 
			
		||||
            return cached;
 | 
			
		||||
        Optional<XMaterial> search = data == -1 ?
 | 
			
		||||
            Arrays.stream(XMaterial.VALUES).filter(mat -> mat.matchAnyLegacy(name)).findFirst() :
 | 
			
		||||
            Arrays.stream(XMaterial.VALUES).filter(mat -> mat.matchAnyLegacy(name) && mat.data == data).findFirst();
 | 
			
		||||
 | 
			
		||||
        if (search.isPresent())
 | 
			
		||||
        {
 | 
			
		||||
            XMaterial found = search.get();
 | 
			
		||||
            CACHED_SEARCH.put(found.legacy[0] + "," + found.getData(), found);
 | 
			
		||||
            return found;
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if XMaterial enum contains a material with this name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name name of the material
 | 
			
		||||
     * @return true if XMaterial enum has this material.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean contains(String name)
 | 
			
		||||
    {
 | 
			
		||||
        String formatted = format(name);
 | 
			
		||||
        return Arrays.stream(XMaterial.VALUES).anyMatch(mat -> mat.name().equals(formatted));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the given material matches any of the legacy names.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name the material name.
 | 
			
		||||
     * @return true if it's a legacy name.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean containsLegacy(String name)
 | 
			
		||||
    {
 | 
			
		||||
        String formatted = format(name);
 | 
			
		||||
        return Arrays.stream(Arrays.stream(XMaterial.VALUES).map(m -> m.legacy).toArray(String[]::new))
 | 
			
		||||
            .anyMatch(mat -> parseLegacyVersionMaterialName(mat).equals(formatted));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @see #matchXMaterial(String, byte)
 | 
			
		||||
     */
 | 
			
		||||
    public static XMaterial matchXMaterial(Material material)
 | 
			
		||||
    {
 | 
			
		||||
        return matchXMaterial(material.name());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @see #matchXMaterial(String, byte)
 | 
			
		||||
     */
 | 
			
		||||
    public static XMaterial matchXMaterial(String name)
 | 
			
		||||
    {
 | 
			
		||||
        // -1 Determines whether the item's data is unknown and only the name is given.
 | 
			
		||||
        // Checking if the item is damageable won't do anything as the data is not going
 | 
			
		||||
        // to be checked in requestOldMaterial anyway.
 | 
			
		||||
        return matchXMaterial(name, (byte) -1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parses the material name and data argument as a {@link Material}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name name of the material
 | 
			
		||||
     * @param data data of the material
 | 
			
		||||
     */
 | 
			
		||||
    public static Material parseMaterial(String name, byte data)
 | 
			
		||||
    {
 | 
			
		||||
        return matchXMaterial(name, data).parseMaterial();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param item the ItemStack to match its material and data.
 | 
			
		||||
     * @see #matchXMaterial(String, byte)
 | 
			
		||||
     */
 | 
			
		||||
    @SuppressWarnings("deprecation")
 | 
			
		||||
    public static XMaterial matchXMaterial(ItemStack item)
 | 
			
		||||
    {
 | 
			
		||||
        return isDamageable(item.getType().name()) ? matchXMaterial(item.getType().name(), (byte) 0) :
 | 
			
		||||
            matchXMaterial(item.getType().name(), (byte) item.getDurability());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Matches the argument string and its data with a XMaterial.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name the name of the material
 | 
			
		||||
     * @param data the data byte of the material
 | 
			
		||||
     * @return a XMaterial from the enum (with the same legacy name and data if in
 | 
			
		||||
     *         older versions.)
 | 
			
		||||
     */
 | 
			
		||||
    public static XMaterial matchXMaterial(String name, byte data)
 | 
			
		||||
    {
 | 
			
		||||
        Validate.notEmpty(name, "Material name cannot be null or empty");
 | 
			
		||||
        name = format(name);
 | 
			
		||||
 | 
			
		||||
        if ((contains(name) && data <= 0))
 | 
			
		||||
            return valueOf(name);
 | 
			
		||||
        return requestOldXMaterial(name, data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the XMaterial based on the Material's ID and data. You should avoid
 | 
			
		||||
     * using this for performance reasons.
 | 
			
		||||
     *
 | 
			
		||||
     * @param id   the ID (Magic value) of the material.
 | 
			
		||||
     * @param data the data of the material.
 | 
			
		||||
     * @return some XMaterial, or null.
 | 
			
		||||
     */
 | 
			
		||||
    public static XMaterial matchXMaterial(int id, byte data)
 | 
			
		||||
    {
 | 
			
		||||
        // Looping through Material.values() will take longer.
 | 
			
		||||
        return Arrays.stream(XMaterial.VALUES).filter(mat -> mat.getId() == id && mat.data == data).findFirst()
 | 
			
		||||
            .orElse(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Attempts to build the string like an enum name.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name the material name to modify.
 | 
			
		||||
     * @return a Material enum name.
 | 
			
		||||
     */
 | 
			
		||||
    private static String format(String name)
 | 
			
		||||
    {
 | 
			
		||||
        return name.toUpperCase().replace("MINECRAFT:", "").replace('-', '_').replaceAll("\\s+", "_").replaceAll("\\W",
 | 
			
		||||
                                                                                                                 "");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parses the material name if the legacy name has a version attached to it.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name the material name to parse.
 | 
			
		||||
     * @return the material name with the version removed.
 | 
			
		||||
     */
 | 
			
		||||
    private static String parseLegacyVersionMaterialName(String name)
 | 
			
		||||
    {
 | 
			
		||||
        if (!name.contains("/"))
 | 
			
		||||
            return name;
 | 
			
		||||
        return name.substring(0, name.indexOf('/'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the version argument is the same or higher than the current server
 | 
			
		||||
     * version.
 | 
			
		||||
     *
 | 
			
		||||
     * @param version the version to be checked.
 | 
			
		||||
     * @return true of the version is equal or higher than the current version.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isVersionOrHigher(MinecraftVersion version)
 | 
			
		||||
    {
 | 
			
		||||
        MinecraftVersion current = getVersion();
 | 
			
		||||
 | 
			
		||||
        if (version == current)
 | 
			
		||||
            return true;
 | 
			
		||||
        if (version == MinecraftVersion.UNKNOWN)
 | 
			
		||||
            return false;
 | 
			
		||||
        if (current == MinecraftVersion.UNKNOWN)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        int ver = Integer.parseInt(version.name().replace("VERSION_", "").replace("_", ""));
 | 
			
		||||
        int currentVer = Integer.parseInt(current.name().replace("VERSION_", "").replace("_", ""));
 | 
			
		||||
 | 
			
		||||
        return currentVer >= ver;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Converts the material's name to a string with the first letter uppercase.
 | 
			
		||||
     *
 | 
			
		||||
     * @return a converted string.
 | 
			
		||||
     */
 | 
			
		||||
    public static String toWord(Material material)
 | 
			
		||||
    {
 | 
			
		||||
        return material.name().charAt(0) + material.name().substring(1).toLowerCase();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Compares two major versions. The current server version and the given
 | 
			
		||||
     * version.
 | 
			
		||||
     *
 | 
			
		||||
     * @param version the version to check.
 | 
			
		||||
     * @return true if is the same as the current version or higher.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isVersionOrHigher(String version)
 | 
			
		||||
    {
 | 
			
		||||
        int currentVer = Integer.parseInt(getExactMajorVersion(Bukkit.getVersion()).replace(".", ""));
 | 
			
		||||
        int versionNumber = Integer.parseInt(version.replace(".", ""));
 | 
			
		||||
 | 
			
		||||
        return currentVer >= versionNumber;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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.
 | 
			
		||||
     */
 | 
			
		||||
    public static String getExactMajorVersion(String version)
 | 
			
		||||
    {
 | 
			
		||||
        // getBukkitVersion()
 | 
			
		||||
        if (version.contains("SNAPSHOT") || version.contains("-R"))
 | 
			
		||||
            version = version.substring(0, version.indexOf("-"));
 | 
			
		||||
        // getVersion()
 | 
			
		||||
        if (version.contains("git"))
 | 
			
		||||
            version = version.substring(version.indexOf("MC:") + 4).replace(")", "");
 | 
			
		||||
        if (version.split(Pattern.quote(".")).length > 2)
 | 
			
		||||
            version = version.substring(0, version.lastIndexOf("."));
 | 
			
		||||
        return version;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parses the string arugment to a version. Supports
 | 
			
		||||
     * {@link Bukkit#getVersion()}, {@link Bukkit#getBukkitVersion()} and normal
 | 
			
		||||
     * formats such as "1.14"
 | 
			
		||||
     *
 | 
			
		||||
     * @param version the server version.
 | 
			
		||||
     * @return the Minecraft version represented by the string.
 | 
			
		||||
     */
 | 
			
		||||
    private static MinecraftVersion valueOfVersion(String version)
 | 
			
		||||
    {
 | 
			
		||||
        version = getExactMajorVersion(version);
 | 
			
		||||
        if (version.equals("1.10") || version.equals("1.11") || version.equals("1.12"))
 | 
			
		||||
            return MinecraftVersion.VERSION_1_9;
 | 
			
		||||
        version = version.replace(".", "_");
 | 
			
		||||
        if (!version.startsWith("VERSION_"))
 | 
			
		||||
            version = "VERSION_" + version;
 | 
			
		||||
        String check = version;
 | 
			
		||||
        return Arrays.stream(MinecraftVersion.VALUES).anyMatch(v -> v.name().equals(check)) ?
 | 
			
		||||
            MinecraftVersion.valueOf(version) : MinecraftVersion.UNKNOWN;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the material can be damaged from {@link #DAMAGEABLE}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name the name of the material.
 | 
			
		||||
     * @return true of the material can be damaged.
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isDamageable(String name)
 | 
			
		||||
    {
 | 
			
		||||
        return Arrays.stream(DAMAGEABLE).anyMatch(name::contains);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the ID (Magic value) of the material. If an
 | 
			
		||||
     * {@link IllegalArgumentException} was thrown from this method, you should
 | 
			
		||||
     * definitely report it.
 | 
			
		||||
     *
 | 
			
		||||
     * @return the ID of the material. -1 if it's a new block.
 | 
			
		||||
     */
 | 
			
		||||
    @SuppressWarnings("deprecation")
 | 
			
		||||
    public int getId()
 | 
			
		||||
    {
 | 
			
		||||
        return isNew() ? -1 : this.parseMaterial().getId();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the given string matches any of this material's legacy material
 | 
			
		||||
     * names.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name the name to check
 | 
			
		||||
     * @return true if it's one of the legacy names.
 | 
			
		||||
     */
 | 
			
		||||
    public boolean matchAnyLegacy(String name)
 | 
			
		||||
    {
 | 
			
		||||
        String formatted = format(name);
 | 
			
		||||
        return Arrays.asList(legacy).contains(formatted);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Converts the material's name to a string with the first letter uppercase.
 | 
			
		||||
     *
 | 
			
		||||
     * @return a converted string.
 | 
			
		||||
     */
 | 
			
		||||
    public String toWord()
 | 
			
		||||
    {
 | 
			
		||||
        return name().charAt(0) + name().substring(1).toLowerCase();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return true if the item can be damaged.
 | 
			
		||||
     * @see #isDamageable(String)
 | 
			
		||||
     */
 | 
			
		||||
    public boolean isDamageable()
 | 
			
		||||
    {
 | 
			
		||||
        return isDamageable(name());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the {@link ItemStack} data of this material in older versions. Which can
 | 
			
		||||
     * be accessed with {@link ItemStack#getData()} then MaterialData#getData() or
 | 
			
		||||
     * {@link ItemStack#getDurability()} if not damageable.
 | 
			
		||||
     *
 | 
			
		||||
     * @return data of this material.
 | 
			
		||||
     */
 | 
			
		||||
    public int getData()
 | 
			
		||||
    {
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a list of materials names that was previously used by older versions.
 | 
			
		||||
     *
 | 
			
		||||
     * @return a list of string of legacy material names.
 | 
			
		||||
     */
 | 
			
		||||
    public String[] getLegacy()
 | 
			
		||||
    {
 | 
			
		||||
        return legacy;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parses the XMaterial as an {@link ItemStack}.
 | 
			
		||||
     *
 | 
			
		||||
     * @return an ItemStack with the same material (and data if in older versions.)
 | 
			
		||||
     */
 | 
			
		||||
    public ItemStack parseItem()
 | 
			
		||||
    {
 | 
			
		||||
        return parseItem(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parses the XMaterial as an {@link ItemStack}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param suggest if true {@link #parseMaterial(boolean)}
 | 
			
		||||
     * @return an ItemStack with the same material (and data if in older versions.)
 | 
			
		||||
     */
 | 
			
		||||
    @SuppressWarnings("deprecation")
 | 
			
		||||
    public ItemStack parseItem(boolean suggest)
 | 
			
		||||
    {
 | 
			
		||||
        Material material = this.parseMaterial(suggest);
 | 
			
		||||
        return isNewVersion() ? new ItemStack(material) : new ItemStack(material, 1, data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parses the XMaterial as a {@link Material}.
 | 
			
		||||
     *
 | 
			
		||||
     * @return the Material related to this XMaterial based on the server version.
 | 
			
		||||
     */
 | 
			
		||||
    public Material parseMaterial()
 | 
			
		||||
    {
 | 
			
		||||
        return parseMaterial(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parses the XMaterial as a {@link Material}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param suggest Use a suggested material if the material is added in the new
 | 
			
		||||
     *                version.
 | 
			
		||||
     * @return the Material related to this XMaterial based on the server version.
 | 
			
		||||
     * @see #matchXMaterial(String, byte)
 | 
			
		||||
     */
 | 
			
		||||
    public Material parseMaterial(boolean suggest)
 | 
			
		||||
    {
 | 
			
		||||
        Material newMat = Material.getMaterial(name());
 | 
			
		||||
 | 
			
		||||
        // If the name is not null it's probably the new version.
 | 
			
		||||
        // So you can still use this name even if it's a duplicated name.
 | 
			
		||||
        // Since duplicated names only apply to older versions.
 | 
			
		||||
        if (newMat != null && (isNewVersion()))
 | 
			
		||||
            return newMat;
 | 
			
		||||
        return requestOldMaterial(suggest);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parses from old material names and can accept suggestions.
 | 
			
		||||
     *
 | 
			
		||||
     * @param suggest Accept suggestions for newly added blocks
 | 
			
		||||
     * @return A parsed Material suitable for this minecraft version.
 | 
			
		||||
     */
 | 
			
		||||
    private Material requestOldMaterial(boolean suggest)
 | 
			
		||||
    {
 | 
			
		||||
        Material oldMat;
 | 
			
		||||
        boolean isNew = getVersionIfNew() != MinecraftVersion.UNKNOWN;
 | 
			
		||||
        for (int i = legacy.length - 1; i >= 0; i--)
 | 
			
		||||
        {
 | 
			
		||||
            String legacyName = legacy[i];
 | 
			
		||||
            // Slash means it's just another name for the material in another version.
 | 
			
		||||
            if (legacyName.contains("/"))
 | 
			
		||||
            {
 | 
			
		||||
                oldMat = Material.getMaterial(parseLegacyVersionMaterialName(legacyName));
 | 
			
		||||
 | 
			
		||||
                if (oldMat != null)
 | 
			
		||||
                    return oldMat;
 | 
			
		||||
                else
 | 
			
		||||
                    continue;
 | 
			
		||||
            }
 | 
			
		||||
            if (isNew)
 | 
			
		||||
            {
 | 
			
		||||
                if (suggest)
 | 
			
		||||
                {
 | 
			
		||||
                    oldMat = Material.getMaterial(legacyName);
 | 
			
		||||
                    if (oldMat != null)
 | 
			
		||||
                        return oldMat;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                    return null;
 | 
			
		||||
                // According to the suggestion format list, all the other names continuing
 | 
			
		||||
                // from here are considered as a "suggestion" if there's no slash anymore.
 | 
			
		||||
            }
 | 
			
		||||
            oldMat = Material.getMaterial(legacyName);
 | 
			
		||||
            if (oldMat != null)
 | 
			
		||||
                return oldMat;
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if an item is similar to the material and its data (if in older
 | 
			
		||||
     * versions.)
 | 
			
		||||
     *
 | 
			
		||||
     * @param item item to check.
 | 
			
		||||
     * @return true if the material is the same as the item's material (and data if
 | 
			
		||||
     *         in older versions.)
 | 
			
		||||
     */
 | 
			
		||||
    @SuppressWarnings("deprecation")
 | 
			
		||||
    public boolean isSimilar(ItemStack item)
 | 
			
		||||
    {
 | 
			
		||||
        Objects.requireNonNull(item, "ItemStack cannot be null");
 | 
			
		||||
        Objects.requireNonNull(item.getType(), "ItemStack's material cannot be null");
 | 
			
		||||
        return (isNewVersion() || this.isDamageable()) ? item.getType() == this.parseMaterial() :
 | 
			
		||||
            item.getType() == this.parseMaterial() && item.getDurability() == data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the suggested material names that can be used instead of this material.
 | 
			
		||||
     *
 | 
			
		||||
     * @return a list of suggested material names.
 | 
			
		||||
     */
 | 
			
		||||
    public String[] getSuggestions()
 | 
			
		||||
    {
 | 
			
		||||
        if (!legacy[0].contains("."))
 | 
			
		||||
            return new String[0];
 | 
			
		||||
        return Arrays.stream(legacy).filter(mat -> !mat.contains(".")).toArray(String[]::new);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if this material is supported in the current version. It'll check both
 | 
			
		||||
     * the newest matetrial name and for legacy names.
 | 
			
		||||
     *
 | 
			
		||||
     * @return true if the material exists in {@link Material} list.
 | 
			
		||||
     */
 | 
			
		||||
    public boolean isSupported()
 | 
			
		||||
    {
 | 
			
		||||
        return Arrays.stream(Material.values())
 | 
			
		||||
            .anyMatch(mat -> mat.name().equals(name()) || matchAnyLegacy(mat.name()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the added version if the material is newly added after the 1.13 Aquatic
 | 
			
		||||
     * Update and higher.
 | 
			
		||||
     *
 | 
			
		||||
     * @return the version which the material was added in.
 | 
			
		||||
     *         {@link MinecraftVersion#UNKNOWN} if not new.
 | 
			
		||||
     * @see #isNew()
 | 
			
		||||
     */
 | 
			
		||||
    public MinecraftVersion getVersionIfNew()
 | 
			
		||||
    {
 | 
			
		||||
        return isNew() ? valueOfVersion(legacy[0]) : MinecraftVersion.UNKNOWN;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks if the material is newly added after the 1.13 Aquatic Update.
 | 
			
		||||
     *
 | 
			
		||||
     * @return true if it was newly added.
 | 
			
		||||
     */
 | 
			
		||||
    public boolean isNew()
 | 
			
		||||
    {
 | 
			
		||||
        return legacy[0].contains(".");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the suggested material instead of returning null for unsupported
 | 
			
		||||
     * versions. This is somehow similar to what ProtcolSupport and ViaVersion are
 | 
			
		||||
     * doing to new materials. Don't use this if you want to parse to a
 | 
			
		||||
     * {@link Material}
 | 
			
		||||
     *
 | 
			
		||||
     * @return The suggested material that is similar.
 | 
			
		||||
     * @see #parseMaterial()
 | 
			
		||||
     */
 | 
			
		||||
    public XMaterial suggestOldMaterialIfNew()
 | 
			
		||||
    {
 | 
			
		||||
        if (getVersionIfNew() == MinecraftVersion.UNKNOWN || legacy.length == 1)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        // We need a loop because: Newest -> Oldest
 | 
			
		||||
        for (int i = legacy.length - 1; i >= 0; i--)
 | 
			
		||||
        {
 | 
			
		||||
            String legacyName = legacy[i];
 | 
			
		||||
 | 
			
		||||
            if (legacyName.contains("/"))
 | 
			
		||||
                continue;
 | 
			
		||||
            XMaterial mat = matchXMaterial(parseLegacyVersionMaterialName(legacyName), data);
 | 
			
		||||
            if (mat != null && this != mat)
 | 
			
		||||
                return mat;
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Only major versions related to material changes.
 | 
			
		||||
     */
 | 
			
		||||
    public enum MinecraftVersion
 | 
			
		||||
    {
 | 
			
		||||
        /**
 | 
			
		||||
         * Bountiful Update
 | 
			
		||||
         */
 | 
			
		||||
        VERSION_1_8,
 | 
			
		||||
        /**
 | 
			
		||||
         * Combat Update (Pitiful Update?)
 | 
			
		||||
         */
 | 
			
		||||
        VERSION_1_9,
 | 
			
		||||
        /**
 | 
			
		||||
         * Aquatic Update
 | 
			
		||||
         */
 | 
			
		||||
        VERSION_1_13,
 | 
			
		||||
        /**
 | 
			
		||||
         * Village Pillage Update
 | 
			
		||||
         */
 | 
			
		||||
        VERSION_1_14,
 | 
			
		||||
        /**
 | 
			
		||||
         * 1.7 or below. Using {@link #getVersionIfNew()} it means 1.12 or below.
 | 
			
		||||
         */
 | 
			
		||||
        UNKNOWN;
 | 
			
		||||
 | 
			
		||||
        public static final MinecraftVersion[] VALUES = MinecraftVersion.values();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user