diff --git a/bukkit/build.gradle b/bukkit/build.gradle index 49355f55d..42ff68c81 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -52,7 +52,7 @@ repositories { dependencies { compileOnly project(':common') // Paper - compileOnly('io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT') { + compileOnly('io.papermc.paper:paper-api:1.20.2-R0.1-SNAPSHOT') { exclude(group: 'it.unimi.dsi', module: 'fastutil') // exclude fastutil just to don't use it (for 1.8 support) } // Folia diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java index 8cb3652bc..49c24b75f 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java @@ -15,13 +15,18 @@ import com.leonardobishop.quests.bukkit.hook.essentials.AbstractEssentialsHook; import com.leonardobishop.quests.bukkit.hook.essentials.EssentialsHook; import com.leonardobishop.quests.bukkit.hook.itemgetter.ItemGetter; -import com.leonardobishop.quests.bukkit.hook.itemgetter.ItemGetterLatest; -import com.leonardobishop.quests.bukkit.hook.itemgetter.ItemGetter_1_13; -import com.leonardobishop.quests.bukkit.hook.itemgetter.ItemGetter_Late_1_8; +import com.leonardobishop.quests.bukkit.hook.itemgetter.ItemGetter13; +import com.leonardobishop.quests.bukkit.hook.itemgetter.ItemGetter14; +import com.leonardobishop.quests.bukkit.hook.itemgetter.ItemGetter8; import com.leonardobishop.quests.bukkit.hook.papi.AbstractPlaceholderAPIHook; import com.leonardobishop.quests.bukkit.hook.papi.PlaceholderAPIHook; import com.leonardobishop.quests.bukkit.hook.playerblocktracker.AbstractPlayerBlockTrackerHook; import com.leonardobishop.quests.bukkit.hook.playerblocktracker.PlayerBlockTrackerHook; +import com.leonardobishop.quests.bukkit.hook.skullgetter.BukkitSkullGetter; +import com.leonardobishop.quests.bukkit.hook.skullgetter.LegacySkullGetter; +import com.leonardobishop.quests.bukkit.hook.skullgetter.ModernSkullGetter; +import com.leonardobishop.quests.bukkit.hook.skullgetter.PaperSkullGetter; +import com.leonardobishop.quests.bukkit.hook.skullgetter.SkullGetter; import com.leonardobishop.quests.bukkit.hook.title.QuestsTitle; import com.leonardobishop.quests.bukkit.hook.title.Title_Bukkit; import com.leonardobishop.quests.bukkit.hook.title.Title_BukkitNoTimings; @@ -116,6 +121,7 @@ import com.leonardobishop.quests.common.tasktype.TaskType; import com.leonardobishop.quests.common.tasktype.TaskTypeManager; import com.leonardobishop.quests.common.updater.Updater; +import com.mojang.authlib.GameProfile; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.chat.BaseComponent; import org.bstats.bukkit.MetricsLite; @@ -169,6 +175,7 @@ public class BukkitQuestsPlugin extends JavaPlugin implements Quests { private AbstractEssentialsHook essentialsHook; private AbstractPlayerBlockTrackerHook playerBlockTrackerHook; private ItemGetter itemGetter; + private SkullGetter skullGetter; private QuestsTitle titleHandle; private QuestsBossBar bossBarHandle; private QuestsActionBar actionBarHandle; @@ -293,13 +300,10 @@ public void onEnable() { setActionBarHandle(); // (itemstacks) - if (version <= 12) { - itemGetter = new ItemGetter_Late_1_8(); - } else if (version == 13) { - itemGetter = new ItemGetter_1_13(); - } else { - itemGetter = new ItemGetterLatest(); - } + setItemGetter(); + + // (skulls) + setSkullGetter(); // (version specific handler) // TODO move above to version specific handlers @@ -530,7 +534,6 @@ public QuestItem getConfiguredQuestItem(String path, ConfigurationSection config return new ParsedQuestItem("defined", null, getConfiguredItemStack(path, config, excludes)); } - public ItemStack getConfiguredItemStack(String path, ConfigurationSection config, ItemGetter.Filter... excludes) { return itemGetter.getItem(path, config, excludes); } @@ -678,6 +681,46 @@ private void setActionBarHandle() { actionBarHandle = new ActionBar_Nothing(); } + private void setItemGetter() { + // Spigot 1.14+ + if (CompatUtils.classWithMethodExists("org.bukkit.inventory.meta.ItemMeta", "setCustomModelData", Integer.class)) { + itemGetter = new ItemGetter14(this); + return; + } + + // Spigot 1.13+ + if (CompatUtils.classWithMethodExists("org.bukkit.inventory.meta.ItemMeta", "getAttributeModifiers")) { + itemGetter = new ItemGetter13(this); + return; + } + + // Spigot 1.8+ + itemGetter = new ItemGetter8(this); + } + + private void setSkullGetter() { + // Paper 1.12+ + if (CompatUtils.classExists("com.destroystokyo.paper.profile.PlayerProfile")) { + skullGetter = new PaperSkullGetter(this); + return; + } + + if (CompatUtils.classWithMethodExists("org.bukkit.craftbukkit.{}.inventory.CraftMetaSkull", "setProfile", GameProfile.class)) { + // Spigot 1.18.1+ + if (CompatUtils.classExists("org.bukkit.profile.PlayerProfile")) { + skullGetter = new ModernSkullGetter(this); + return; + } + + // Spigot 1.15.1+ + skullGetter = new BukkitSkullGetter(this); + return; + } + + // Spigot 1.8+ + skullGetter = new LegacySkullGetter(this); + } + public boolean isValidConfiguration() { return validConfiguration; } @@ -710,6 +753,10 @@ public ItemGetter getItemGetter() { return itemGetter; } + public SkullGetter getSkullGetter() { + return skullGetter; + } + public QuestsTitle getTitleHandle() { return titleHandle; } diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter.java index efbd1a369..999a14610 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter.java @@ -1,46 +1,55 @@ package com.leonardobishop.quests.bukkit.hook.itemgetter; +import com.leonardobishop.quests.bukkit.BukkitQuestsPlugin; +import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.inventory.ItemStack; -public interface ItemGetter { +public abstract class ItemGetter { + + protected static final ItemStack invalidItemStack = new ItemStack(Material.STONE, 1); + protected final BukkitQuestsPlugin plugin; + + public ItemGetter(BukkitQuestsPlugin plugin) { + this.plugin = plugin; + } /** * Gets an ItemStack from a configuration. * Implementations should specific to the server version. * - * @param path the path to where the item is defined in the config (null if item is defined in second param) - * @param config the configuration file + * @param path the path to where the item is defined in the config (null if item is defined in second param) + * @param config the configuration file * @param excludes exclude certain fields in the configuration * @return {@link ItemStack} */ - ItemStack getItem(String path, ConfigurationSection config, Filter... excludes); + public abstract ItemStack getItem(String path, ConfigurationSection config, Filter... excludes); /** * Gets an ItemStack from a given string (which represents a material). * For pre-1.13 server implementations, the string may use a data code. * - * @param material the string + * @param typeString the string * @return {@link ItemStack} */ - ItemStack getItemStack(String material); + public abstract ItemStack getItemStack(String typeString); /** * Validates a material from a string. * For pre-1.13 server implementations, the string may use a data code. * - * @param material the string + * @param typeString the string * @return true if it a material */ - boolean isValidMaterial(String material); + public abstract boolean isValidMaterial(String typeString); - enum Filter { + public enum Filter { DISPLAY_NAME, LORE, ENCHANTMENTS, ITEM_FLAGS, UNBREAKABLE, ATTRIBUTE_MODIFIER, - CUSTOM_MODEL_DATA; + CUSTOM_MODEL_DATA } -} \ No newline at end of file +} diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter13.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter13.java new file mode 100644 index 000000000..57e980ca8 --- /dev/null +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter13.java @@ -0,0 +1,231 @@ +package com.leonardobishop.quests.bukkit.hook.itemgetter; + +import com.leonardobishop.quests.bukkit.BukkitQuestsPlugin; +import com.leonardobishop.quests.bukkit.util.chat.Chat; +import org.bukkit.Material; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeModifier; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +/** + * Reads the following: + * + * Requires at least API version 1.13. + */ +@SuppressWarnings("DuplicatedCode") +public class ItemGetter13 extends ItemGetter { + + public ItemGetter13(BukkitQuestsPlugin plugin) { + super(plugin); + } + + @Override + public ItemStack getItem(String path, ConfigurationSection config, Filter... excludes) { + config = config.getConfigurationSection(path); + if (config == null) { + return invalidItemStack; + } + + List filters = Arrays.asList(excludes); + + // type (without data) + String typeString = config.getString("item", config.getString("type")); + ItemStack item = getItemStack(typeString); + ItemMeta meta = item.getItemMeta(); + + // skull + if (meta instanceof SkullMeta skullMeta) { + String ownerName = config.getString("owner-username"); + String ownerUniqueIdString = config.getString("owner-uuid"); + String ownerBase64 = config.getString("owner-base64"); + + plugin.getSkullGetter().apply(skullMeta, ownerName, ownerUniqueIdString, ownerBase64); + } + + // name + String nameString = config.getString("name"); + if (nameString != null && !filters.contains(Filter.DISPLAY_NAME)) { + nameString = Chat.legacyColor(nameString); + + meta.setDisplayName(nameString); + } + + // lore + List loreStrings = config.getStringList("lore"); + if (!loreStrings.isEmpty() && !filters.contains(Filter.LORE)) { + loreStrings = Chat.legacyColor(loreStrings); + + meta.setLore(loreStrings); + } + + // enchantments + List enchantmentStrings = config.getStringList("enchantments"); + if (!enchantmentStrings.isEmpty() && !filters.contains(Filter.ENCHANTMENTS)) { + for (String enchantmentString : enchantmentStrings) { + String[] parts = enchantmentString.split(":"); + if (parts.length == 0) { + continue; + } + + Enchantment enchantment = Enchantment.getByName(parts[0]); + if (enchantment == null) { + continue; + } + + int level; + if (parts.length == 2) { + try { + level = Integer.parseUnsignedInt(parts[1]); + } catch (NumberFormatException e) { + continue; + } + } else if (parts.length == 1) { + level = 1; + } else { + continue; + } + + meta.addEnchant(enchantment, level, true); + } + } + + // item flags + List itemFlagStrings = config.getStringList("itemflags"); + if (!itemFlagStrings.isEmpty() && !filters.contains(Filter.ITEM_FLAGS)) { + for (String itemFlagString : itemFlagStrings) { + ItemFlag itemFlag; + try { + itemFlag = ItemFlag.valueOf(itemFlagString); + } catch (IllegalArgumentException e) { + continue; + } + + meta.addItemFlags(itemFlag); + } + } + + // unbreakability + boolean unbreakable = config.getBoolean("unbreakable", false); + if (unbreakable && !filters.contains(Filter.UNBREAKABLE)) { + meta.setUnbreakable(true); + } + + // attribute modifiers + List> attributeModifierMaps = config.getMapList("attributemodifiers"); + if (!attributeModifierMaps.isEmpty() && !filters.contains(Filter.ATTRIBUTE_MODIFIER)) { + for (Map attributeModifierMap : attributeModifierMaps) { + // attribute + String attributeString = (String) attributeModifierMap.get("attribute"); + Attribute attribute; + try { + attribute = Attribute.valueOf(attributeString); + } catch (IllegalArgumentException e) { + continue; + } + + // modifier (map) + Map modifierMap = (Map) attributeModifierMap.get("modifier"); + if (modifierMap == null) { + continue; + } + + // modifier unique id + String modifierUniqueIdString = (String) modifierMap.get("uuid"); + UUID modifierUniqueId; + try { + modifierUniqueId = UUID.fromString(modifierUniqueIdString); + } catch (IllegalArgumentException e) { + modifierUniqueId = null; + } + + // modifier name + String modifierName = (String) modifierMap.get("name"); + if (modifierName == null) { + continue; + } + + // modifier amount + Object modifierAmountObject = modifierMap.get("amount"); + double modifierAmount; + if (modifierAmountObject instanceof Number modifierAmountNumber) { + modifierAmount = modifierAmountNumber.doubleValue(); + } else { + continue; + } + + // modifier operation + String modifierOperationString = (String) modifierMap.get("operation"); + AttributeModifier.Operation modifierOperation; + try { + modifierOperation = AttributeModifier.Operation.valueOf(modifierOperationString); + } catch (IllegalArgumentException e) { + continue; + } + + // modifier equipment slot + String equipmentSlotString = (String) modifierMap.get("equipmentslot"); + EquipmentSlot equipmentSlot; + try { + equipmentSlot = EquipmentSlot.valueOf(equipmentSlotString); + } catch (IllegalArgumentException e) { + equipmentSlot = null; + } + + // modifier (ctor) + AttributeModifier modifier; + if (modifierUniqueId != null) { + if (equipmentSlot != null) { + modifier = new AttributeModifier(modifierUniqueId, modifierName, modifierAmount, modifierOperation, equipmentSlot); + } else { + modifier = new AttributeModifier(modifierUniqueId, modifierName, modifierAmount, modifierOperation); + } + } else { + modifier = new AttributeModifier(modifierName, modifierAmount, modifierOperation); + } + + meta.addAttributeModifier(attribute, modifier); + } + } + + item.setItemMeta(meta); + return item; + } + + @Override + public ItemStack getItemStack(String typeString) { + if (typeString == null) { + return invalidItemStack; + } + + Material type = Material.getMaterial(typeString); + if (type == null) { + return invalidItemStack; + } + + return new ItemStack(type, 1); + } + + @Override + public boolean isValidMaterial(String typeString) { + return Material.getMaterial(typeString) != null; + } +} diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter14.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter14.java new file mode 100644 index 000000000..bdab3ad43 --- /dev/null +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter14.java @@ -0,0 +1,238 @@ +package com.leonardobishop.quests.bukkit.hook.itemgetter; + +import com.leonardobishop.quests.bukkit.BukkitQuestsPlugin; +import com.leonardobishop.quests.bukkit.util.chat.Chat; +import org.bukkit.Material; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeModifier; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +/** + * Reads the following: + *
    + *
  • type (without data support, without namespace support)
  • + *
  • name
  • + *
  • lore
  • + *
  • enchantments (without namespace support)
  • + *
  • item flags
  • + *
  • unbreakability
  • + *
  • attribute modifiers
  • + *
  • custom model data
  • + *
+ * Requires at least API version 1.14. + */ +@SuppressWarnings("DuplicatedCode") +public class ItemGetter14 extends ItemGetter { + + public ItemGetter14(BukkitQuestsPlugin plugin) { + super(plugin); + } + + @Override + public ItemStack getItem(String path, ConfigurationSection config, Filter... excludes) { + config = config.getConfigurationSection(path); + if (config == null) { + return invalidItemStack; + } + + List filters = Arrays.asList(excludes); + + // type (without data) + String typeString = config.getString("item", config.getString("type")); + ItemStack item = getItemStack(typeString); + ItemMeta meta = item.getItemMeta(); + + // skull + if (meta instanceof SkullMeta skullMeta) { + String ownerName = config.getString("owner-username"); + String ownerUniqueIdString = config.getString("owner-uuid"); + String ownerBase64 = config.getString("owner-base64"); + + plugin.getSkullGetter().apply(skullMeta, ownerName, ownerUniqueIdString, ownerBase64); + } + + // name + String nameString = config.getString("name"); + if (nameString != null && !filters.contains(Filter.DISPLAY_NAME)) { + nameString = Chat.legacyColor(nameString); + + meta.setDisplayName(nameString); + } + + // lore + List loreStrings = config.getStringList("lore"); + if (!loreStrings.isEmpty() && !filters.contains(Filter.LORE)) { + loreStrings = Chat.legacyColor(loreStrings); + + meta.setLore(loreStrings); + } + + // enchantments + List enchantmentStrings = config.getStringList("enchantments"); + if (!enchantmentStrings.isEmpty() && !filters.contains(Filter.ENCHANTMENTS)) { + for (String enchantmentString : enchantmentStrings) { + String[] parts = enchantmentString.split(":"); + if (parts.length == 0) { + continue; + } + + Enchantment enchantment = Enchantment.getByName(parts[0]); + if (enchantment == null) { + continue; + } + + int level; + if (parts.length == 2) { + try { + level = Integer.parseUnsignedInt(parts[1]); + } catch (NumberFormatException e) { + continue; + } + } else if (parts.length == 1) { + level = 1; + } else { + continue; + } + + meta.addEnchant(enchantment, level, true); + } + } + + // item flags + List itemFlagStrings = config.getStringList("itemflags"); + if (!itemFlagStrings.isEmpty() && !filters.contains(Filter.ITEM_FLAGS)) { + for (String itemFlagString : itemFlagStrings) { + ItemFlag itemFlag; + try { + itemFlag = ItemFlag.valueOf(itemFlagString); + } catch (IllegalArgumentException e) { + continue; + } + + meta.addItemFlags(itemFlag); + } + } + + // unbreakability + boolean unbreakable = config.getBoolean("unbreakable", false); + if (unbreakable && !filters.contains(Filter.UNBREAKABLE)) { + meta.setUnbreakable(true); + } + + // attribute modifiers + List> attributeModifierMaps = config.getMapList("attributemodifiers"); + if (!attributeModifierMaps.isEmpty() && !filters.contains(Filter.ATTRIBUTE_MODIFIER)) { + for (Map attributeModifierMap : attributeModifierMaps) { + // attribute + String attributeString = (String) attributeModifierMap.get("attribute"); + Attribute attribute; + try { + attribute = Attribute.valueOf(attributeString); + } catch (IllegalArgumentException e) { + continue; + } + + // modifier (map) + Map modifierMap = (Map) attributeModifierMap.get("modifier"); + if (modifierMap == null) { + continue; + } + + // modifier unique id + String modifierUniqueIdString = (String) modifierMap.get("uuid"); + UUID modifierUniqueId; + try { + modifierUniqueId = UUID.fromString(modifierUniqueIdString); + } catch (IllegalArgumentException e) { + modifierUniqueId = null; + } + + // modifier name + String modifierName = (String) modifierMap.get("name"); + if (modifierName == null) { + continue; + } + + // modifier amount + Object modifierAmountObject = modifierMap.get("amount"); + double modifierAmount; + if (modifierAmountObject instanceof Number modifierAmountNumber) { + modifierAmount = modifierAmountNumber.doubleValue(); + } else { + continue; + } + + // modifier operation + String modifierOperationString = (String) modifierMap.get("operation"); + AttributeModifier.Operation modifierOperation; + try { + modifierOperation = AttributeModifier.Operation.valueOf(modifierOperationString); + } catch (IllegalArgumentException e) { + continue; + } + + // modifier equipment slot + String equipmentSlotString = (String) modifierMap.get("equipmentslot"); + EquipmentSlot equipmentSlot; + try { + equipmentSlot = EquipmentSlot.valueOf(equipmentSlotString); + } catch (IllegalArgumentException e) { + equipmentSlot = null; + } + + // modifier (ctor) + AttributeModifier modifier; + if (modifierUniqueId != null) { + if (equipmentSlot != null) { + modifier = new AttributeModifier(modifierUniqueId, modifierName, modifierAmount, modifierOperation, equipmentSlot); + } else { + modifier = new AttributeModifier(modifierUniqueId, modifierName, modifierAmount, modifierOperation); + } + } else { + modifier = new AttributeModifier(modifierName, modifierAmount, modifierOperation); + } + + meta.addAttributeModifier(attribute, modifier); + } + } + + // custom model data + Integer customModelData = (Integer) config.get("custommodeldata"); + if (customModelData != null && !filters.contains(Filter.CUSTOM_MODEL_DATA)) { + meta.setCustomModelData(customModelData); + } + + item.setItemMeta(meta); + return item; + } + + @Override + public ItemStack getItemStack(String typeString) { + if (typeString == null) { + return invalidItemStack; + } + + Material type = Material.getMaterial(typeString); + if (type == null) { + return invalidItemStack; + } + + return new ItemStack(type, 1); + } + + @Override + public boolean isValidMaterial(String typeString) { + return Material.getMaterial(typeString) != null; + } +} diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter8.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter8.java new file mode 100644 index 000000000..5f3746f46 --- /dev/null +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter8.java @@ -0,0 +1,189 @@ +package com.leonardobishop.quests.bukkit.hook.itemgetter; + +import com.leonardobishop.quests.bukkit.BukkitQuestsPlugin; +import com.leonardobishop.quests.bukkit.util.chat.Chat; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; + +/** + * Reads the following: + *
    + *
  • type (with data support, without namespace support)
  • + *
  • name
  • + *
  • lore
  • + *
  • enchantments (without namespace support)
  • + *
  • item flags
  • + *
  • unbreakability
  • + *
+ * Requires at least API version 1.8. + */ +@SuppressWarnings({"deprecation", "DuplicatedCode", "JavaReflectionMemberAccess"}) +public class ItemGetter8 extends ItemGetter { + + private static final Method spigotMethod; + private static final Method setUnbreakableMethod; + + static { + try { + spigotMethod = ItemMeta.class.getMethod("spigot"); // removed in 1.15 + + Class spigotClass = Class.forName("org.bukkit.inventory.meta.ItemMeta.Spigot"); + setUnbreakableMethod = spigotClass.getMethod("setUnbreakable", boolean.class); + } catch (ClassNotFoundException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public ItemGetter8(BukkitQuestsPlugin plugin) { + super(plugin); + } + + @Override + public ItemStack getItem(String path, ConfigurationSection config, Filter... excludes) { + config = config.getConfigurationSection(path); + if (config == null) { + return invalidItemStack; + } + + List filters = Arrays.asList(excludes); + + // type (with data) + String typeString = config.getString("item", config.getString("type")); + ItemStack item = getItemStack(typeString); + ItemMeta meta = item.getItemMeta(); + + // skull + if (meta instanceof SkullMeta skullMeta) { + String ownerName = config.getString("owner-username"); + String ownerUniqueIdString = config.getString("owner-uuid"); + String ownerBase64 = config.getString("owner-base64"); + + plugin.getSkullGetter().apply(skullMeta, ownerName, ownerUniqueIdString, ownerBase64); + } + + // name + String nameString = config.getString("name"); + if (nameString != null && !filters.contains(Filter.DISPLAY_NAME)) { + nameString = Chat.legacyColor(nameString); + + meta.setDisplayName(nameString); + } + + // lore + List loreStrings = config.getStringList("lore"); + if (!loreStrings.isEmpty() && !filters.contains(Filter.LORE)) { + loreStrings = Chat.legacyColor(loreStrings); + + meta.setLore(loreStrings); + } + + // enchantments + List enchantmentStrings = config.getStringList("enchantments"); + if (!enchantmentStrings.isEmpty() && !filters.contains(Filter.ENCHANTMENTS)) { + for (String enchantmentString : enchantmentStrings) { + String[] parts = enchantmentString.split(":"); + if (parts.length == 0) { + continue; + } + + Enchantment enchantment = Enchantment.getByName(parts[0]); + if (enchantment == null) { + continue; + } + + int level; + if (parts.length == 2) { + try { + level = Integer.parseUnsignedInt(parts[1]); + } catch (NumberFormatException e) { + continue; + } + } else if (parts.length == 1) { + level = 1; + } else { + continue; + } + + meta.addEnchant(enchantment, level, true); + } + } + + // item flags + List itemFlagStrings = config.getStringList("itemflags"); + if (!itemFlagStrings.isEmpty() && !filters.contains(Filter.ITEM_FLAGS)) { + for (String itemFlagString : itemFlagStrings) { + ItemFlag itemFlag; + try { + itemFlag = ItemFlag.valueOf(itemFlagString); + } catch (IllegalArgumentException e) { + continue; + } + + meta.addItemFlags(itemFlag); + } + } + + // unbreakability + boolean unbreakable = config.getBoolean("unbreakable", false); + if (unbreakable && !filters.contains(Filter.UNBREAKABLE)) { + try { + setUnbreakableMethod.invoke(spigotMethod.invoke(meta), true); + } catch (IllegalAccessException | InvocationTargetException ignored) { + } + } + + item.setItemMeta(meta); + return item; + } + + @Override + public ItemStack getItemStack(String typeString) { + if (typeString == null) { + return invalidItemStack; + } + + Material type = Material.getMaterial(typeString); + if (type != null) { + return new ItemStack(type, 1); + } + + String[] parts = typeString.split(":"); + if (parts.length != 2) { + return invalidItemStack; + } + + type = Material.getMaterial(parts[0]); + if (type == null) { + return invalidItemStack; + } + + byte data; + try { + data = Byte.parseByte(parts[1]); + } catch (NumberFormatException ignored) { + return invalidItemStack; + } + + return new ItemStack(type, 1, (short) 0, data); + } + + @Override + public boolean isValidMaterial(String typeString) { + if (Material.getMaterial(typeString) != null) { + return true; + } + + String[] parts = typeString.split(":"); + return parts.length == 2 && Material.getMaterial(parts[0]) != null; + } +} diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetterLatest.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetterLatest.java deleted file mode 100644 index a7b4e6a70..000000000 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetterLatest.java +++ /dev/null @@ -1,270 +0,0 @@ -package com.leonardobishop.quests.bukkit.hook.itemgetter; - -import com.leonardobishop.quests.bukkit.util.chat.Chat; -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.properties.Property; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.attribute.Attribute; -import org.bukkit.attribute.AttributeModifier; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.inventory.EquipmentSlot; -import org.bukkit.inventory.ItemFlag; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.SkullMeta; - -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -public class ItemGetterLatest implements ItemGetter { - - private Field profileField; - - /* - supporting: - - name - - material - - lore - - enchantments (NamespacedKey) - - itemflags - - unbreakable - - attribute modifier - - custom model data - - requires at least API version 1.14 - */ - @Override - public ItemStack getItem(String path, ConfigurationSection config, ItemGetter.Filter... excludes) { - if (path != null && !path.equals("")) { - path = path + "."; - } - List filters = Arrays.asList(excludes); - - String cName = config.getString(path + "name"); - String cType = config.getString(path + "item", config.getString(path + "type", path + "item")); - boolean hasCustomModelData = config.contains(path + "custommodeldata"); - int customModelData = config.getInt(path + "custommodeldata", 0); - boolean unbreakable = config.getBoolean(path + "unbreakable", false); - List cLore = config.getStringList(path + "lore"); - List cItemFlags = config.getStringList(path + "itemflags"); - boolean hasAttributeModifiers = config.contains(path + "attributemodifiers"); - List> cAttributeModifiers = config.getMapList(path + "attributemodifiers"); - - // material - ItemStack is = getItemStack(cType); - ItemMeta ism = is.getItemMeta(); - - // skull - if (is.getType() == Material.PLAYER_HEAD) { - SkullMeta sm = (SkullMeta) ism; - String cOwnerBase64 = config.getString(path + "owner-base64"); - String cOwnerUsername = config.getString(path + "owner-username"); - String cOwnerUuid = config.getString(path + "owner-uuid"); - if (cOwnerBase64 != null || cOwnerUsername != null || cOwnerUuid != null) { - if (cOwnerUsername != null) { - sm.setOwner(cOwnerUsername); - } else if (cOwnerUuid != null) { - try { - sm.setOwningPlayer(Bukkit.getOfflinePlayer(UUID.fromString(cOwnerUuid))); - } catch (IllegalArgumentException ignored) { } - } else { - GameProfile profile = new GameProfile(UUID.randomUUID(), ""); - profile.getProperties().put("textures", new Property("textures", cOwnerBase64)); - if (profileField == null) { - try { - profileField = sm.getClass().getDeclaredField("profile"); - profileField.setAccessible(true); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } - } - try { - profileField.set(sm, profile); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - } - } - - // name - if (!filters.contains(Filter.DISPLAY_NAME)) { - if (cName != null) { - ism.setDisplayName(Chat.legacyColor(cName)); - } - } - - // lore - if (!filters.contains(Filter.LORE)) { - ism.setLore(Chat.legacyColor(cLore)); - } - - // attribute modifiers - if (!filters.contains(Filter.ATTRIBUTE_MODIFIER)) { - if (hasAttributeModifiers) { - for (Map attr : cAttributeModifiers) { - String cAttribute = (String) attr.get("attribute"); - Attribute attribute = null; - for (Attribute enumattr : Attribute.values()) { - if (enumattr.toString().equals(cAttribute)) { - attribute = enumattr; - break; - } - } - - if (attribute == null) continue; - - Map configurationSection = (Map) attr.get("modifier"); - - String cUUID = (String) configurationSection.get("uuid"); - String cModifierName = (String) configurationSection.get("name"); - String cModifierOperation = (String) configurationSection.get("operation"); - double cAmount; - try { - Object cAmountObj = configurationSection.get("amount"); - if (cAmountObj instanceof Integer) { - cAmount = ((Integer) cAmountObj).doubleValue(); - } else { - cAmount = (Double) cAmountObj; - } - } catch (Exception e) { - cAmount = 1; - } - String cEquipmentSlot = (String) configurationSection.get("equipmentslot"); - - UUID uuid = null; - if (cUUID != null) { - try { - uuid = UUID.fromString(cUUID); - } catch (Exception ignored) { - // ignored - } - } - EquipmentSlot equipmentSlot = null; - if (cEquipmentSlot != null) { - try { - equipmentSlot = EquipmentSlot.valueOf(cEquipmentSlot); - } catch (Exception ignored) { - // ignored - } - } - AttributeModifier.Operation operation = AttributeModifier.Operation.ADD_NUMBER; - try { - operation = AttributeModifier.Operation.valueOf(cModifierOperation); - } catch (Exception ignored) { - // ignored - } - - AttributeModifier modifier; - if (uuid == null) { - modifier = new AttributeModifier(cModifierName, cAmount, operation); - } else if (equipmentSlot == null) { - modifier = new AttributeModifier(uuid, cModifierName, cAmount, operation); - } else { - modifier = new AttributeModifier(uuid, cModifierName, cAmount, operation, equipmentSlot); - } - - ism.addAttributeModifier(attribute, modifier); - } - } - } - - // item flags - if (!filters.contains(Filter.ITEM_FLAGS)) { - if (config.isSet(path + "itemflags")) { - for (String flag : cItemFlags) { - for (ItemFlag iflag : ItemFlag.values()) { - if (iflag.toString().equals(flag)) { - ism.addItemFlags(iflag); - break; - } - } - } - } - } - - // custom model data - if (!filters.contains(Filter.CUSTOM_MODEL_DATA)) { - if (hasCustomModelData) { - ism.setCustomModelData(customModelData); - } - } - - // unbreakable - if (!filters.contains(Filter.UNBREAKABLE)) { - ism.setUnbreakable(unbreakable); - } - - // enchantments - if (!filters.contains(Filter.ENCHANTMENTS)) { - if (config.isSet(path + "enchantments")) { - for (String key : config.getStringList(path + "enchantments")) { - String[] split = key.split(":"); - if (split.length < 2) { - continue; - } - String namespace = split[0]; - String ench = split[1]; - String levelName; - if (split.length >= 3) { - levelName = split[2]; - } else { - levelName = "1"; - } - - // TODO i don't know how these namespaces work -// NamespacedKey namespacedKey; -// try { -// namespacedKey = new NamespacedKey(namespace, ench); -// } catch (Exception e) { -// plugin.getQuestsLogger().debug("Unrecognised namespace: " + namespace); -// e.printStackTrace(); -// continue; -// } - Enchantment enchantment; - if ((enchantment = Enchantment.getByName(ench)) == null) { - continue; - } - - int level; - try { - level = Integer.parseInt(levelName); - } catch (NumberFormatException e) { - level = 1; - } - - ism.addEnchant(enchantment, level, true); - } - } - } - - is.setItemMeta(ism); - return is; - } - - @Override - public ItemStack getItemStack(String material) { - Material type; - try { - type = Material.valueOf(material); - } catch (Exception e) { - type = Material.STONE; - } - return new ItemStack(type, 1); - } - - @Override - public boolean isValidMaterial(String material) { - try { - Material.valueOf(material); - return true; - } catch (IllegalArgumentException ex) { - return false; - } - } -} diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter_1_13.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter_1_13.java deleted file mode 100644 index 4d77427c8..000000000 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter_1_13.java +++ /dev/null @@ -1,260 +0,0 @@ -package com.leonardobishop.quests.bukkit.hook.itemgetter; - -import com.leonardobishop.quests.bukkit.util.chat.Chat; -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.properties.Property; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.attribute.Attribute; -import org.bukkit.attribute.AttributeModifier; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.inventory.EquipmentSlot; -import org.bukkit.inventory.ItemFlag; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.SkullMeta; - -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -public class ItemGetter_1_13 implements ItemGetter { - - private Field profileField; - - /* - reads the following: - - name - - material - - lore - - enchantments (NamespacedKey) - - itemflags - - unbreakable - - attribute modifier - - requires at least API version 1.13 - */ - @Override - public ItemStack getItem(String path, ConfigurationSection config, ItemGetter.Filter... excludes) { - if (path != null && !path.equals("")) { - path = path + "."; - } - List filters = Arrays.asList(excludes); - - String cName = config.getString(path + "name"); - String cType = config.getString(path + "item", config.getString(path + "type", path + "item")); - boolean unbreakable = config.getBoolean(path + "unbreakable", false); - List cLore = config.getStringList(path + "lore"); - List cItemFlags = config.getStringList(path + "itemflags"); - boolean hasAttributeModifiers = config.contains(path + "attributemodifiers"); - List> cAttributeModifiers = config.getMapList(path + "attributemodifiers"); - - // material - ItemStack is = getItemStack(cType); - ItemMeta ism = is.getItemMeta(); - - // skull - if (is.getType() == Material.PLAYER_HEAD) { - SkullMeta sm = (SkullMeta) ism; - String cOwnerBase64 = config.getString(path + "owner-base64"); - String cOwnerUsername = config.getString(path + "owner-username"); - String cOwnerUuid = config.getString(path + "owner-uuid"); - if (cOwnerBase64 != null || cOwnerUsername != null || cOwnerUuid != null) { - if (cOwnerUsername != null) { - sm.setOwner(cOwnerUsername); - } else if (cOwnerUuid != null) { - try { - sm.setOwningPlayer(Bukkit.getOfflinePlayer(UUID.fromString(cOwnerUuid))); - } catch (IllegalArgumentException ignored) { } - } else { - GameProfile profile = new GameProfile(UUID.randomUUID(), ""); - profile.getProperties().put("textures", new Property("textures", cOwnerBase64)); - if (profileField == null) { - try { - profileField = sm.getClass().getDeclaredField("profile"); - profileField.setAccessible(true); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } - } - try { - profileField.set(sm, profile); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - } - } - - // name - if (!filters.contains(Filter.DISPLAY_NAME)) { - if (cName != null) { - ism.setDisplayName(Chat.legacyColor(cName)); - } - } - - // lore - if (!filters.contains(Filter.LORE)) { - ism.setLore(Chat.legacyColor(cLore)); - } - - // attribute modifiers - if (!filters.contains(Filter.ATTRIBUTE_MODIFIER)) { - if (hasAttributeModifiers) { - for (Map attr : cAttributeModifiers) { - String cAttribute = (String) attr.get("attribute"); - Attribute attribute = null; - for (Attribute enumattr : Attribute.values()) { - if (enumattr.toString().equals(cAttribute)) { - attribute = enumattr; - break; - } - } - - if (attribute == null) continue; - - Map configurationSection = (Map) attr.get("modifier"); - - String cUUID = (String) configurationSection.get("uuid"); - String cModifierName = (String) configurationSection.get("name"); - String cModifierOperation = (String) configurationSection.get("operation"); - double cAmount; - try { - Object cAmountObj = configurationSection.get("amount"); - if (cAmountObj instanceof Integer) { - cAmount = ((Integer) cAmountObj).doubleValue(); - } else { - cAmount = (Double) cAmountObj; - } - } catch (Exception e) { - cAmount = 1; - } - String cEquipmentSlot = (String) configurationSection.get("equipmentslot"); - - UUID uuid = null; - if (cUUID != null) { - try { - uuid = UUID.fromString(cUUID); - } catch (Exception ignored) { - // ignored - } - } - EquipmentSlot equipmentSlot = null; - if (cEquipmentSlot != null) { - try { - equipmentSlot = EquipmentSlot.valueOf(cEquipmentSlot); - } catch (Exception ignored) { - // ignored - } - } - AttributeModifier.Operation operation = AttributeModifier.Operation.ADD_NUMBER; - try { - operation = AttributeModifier.Operation.valueOf(cModifierOperation); - } catch (Exception ignored) { - // ignored - } - - AttributeModifier modifier; - if (uuid == null) { - modifier = new AttributeModifier(cModifierName, cAmount, operation); - } else if (equipmentSlot == null) { - modifier = new AttributeModifier(uuid, cModifierName, cAmount, operation); - } else { - modifier = new AttributeModifier(uuid, cModifierName, cAmount, operation, equipmentSlot); - } - - ism.addAttributeModifier(attribute, modifier); - } - } - } - - // item flags - if (!filters.contains(Filter.ITEM_FLAGS)) { - if (config.isSet(path + "itemflags")) { - for (String flag : cItemFlags) { - for (ItemFlag iflag : ItemFlag.values()) { - if (iflag.toString().equals(flag)) { - ism.addItemFlags(iflag); - break; - } - } - } - } - } - - // unbreakable - if (!filters.contains(Filter.UNBREAKABLE)) { - ism.setUnbreakable(unbreakable); - } - - // enchantments - if (!filters.contains(Filter.ENCHANTMENTS)) { - if (config.isSet(path + "enchantments")) { - for (String key : config.getStringList(path + "enchantments")) { - String[] split = key.split(":"); - if (split.length < 2) { - continue; - } - String namespace = split[0]; - String ench = split[1]; - String levelName; - if (split.length >= 3) { - levelName = split[2]; - } else { - levelName = "1"; - } - - // TODO i don't know how these namespaces work -// NamespacedKey namespacedKey; -// try { -// namespacedKey = new NamespacedKey(namespace, ench); -// } catch (Exception e) { -// plugin.getQuestsLogger().debug("Unrecognised namespace: " + namespace); -// e.printStackTrace(); -// continue; -// } - Enchantment enchantment; - if ((enchantment = Enchantment.getByName(ench)) == null) { - continue; - } - - int level; - try { - level = Integer.parseInt(levelName); - } catch (NumberFormatException e) { - level = 1; - } - - is.addUnsafeEnchantment(enchantment, level); - } - } - } - - is.setItemMeta(ism); - return is; - } - - @Override - public ItemStack getItemStack(String material) { - Material type; - try { - type = Material.valueOf(material); - } catch (Exception e) { - type = Material.STONE; - } - return new ItemStack(type, 1); - } - - @Override - public boolean isValidMaterial(String material) { - try { - Material.valueOf(material); - return true; - } catch (IllegalArgumentException ex) { - return false; - } - } -} diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter_Late_1_8.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter_Late_1_8.java deleted file mode 100644 index fbef12d8f..000000000 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/itemgetter/ItemGetter_Late_1_8.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.leonardobishop.quests.bukkit.hook.itemgetter; - -import com.leonardobishop.quests.bukkit.util.StringUtils; -import com.leonardobishop.quests.bukkit.util.chat.Chat; -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.properties.Property; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.inventory.ItemFlag; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.SkullMeta; - -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; -import java.util.regex.Pattern; - -public class ItemGetter_Late_1_8 implements ItemGetter { - - private Field profileField; - - /* - reads the following: - - name - - material (+ DATA) - - lore - - enchantments (NOT NamespacedKey) - - itemflags - - requires at least API version 1.8 (?) - */ - @Override - public ItemStack getItem(String path, ConfigurationSection config, Filter... excludes) { - if (path != null && !path.equals("")) { - path = path + "."; - } - List filters = Arrays.asList(excludes); - - - String cName = config.getString(path + "name"); - String cType = config.getString(path + "item", config.getString(path + "type", path + "item")); - List cLore = config.getStringList(path + "lore"); - List cItemFlags = config.getStringList(path + "itemflags"); - - // material - ItemStack is = getItemStack(cType); - ItemMeta ism = is.getItemMeta(); - - // skull - - // skull - if (is.getType().toString().equals("SKULL_ITEM")) { - SkullMeta sm = (SkullMeta) ism; - String cOwnerBase64 = config.getString(path + "owner-base64"); - String cOwnerUsername = config.getString(path + "owner-username"); - String cOwnerUuid = config.getString(path + "owner-uuid"); - if (cOwnerBase64 != null || cOwnerUsername != null || cOwnerUuid != null) { - if (cOwnerUsername != null) { - sm.setOwner(cOwnerUsername); - } else if (cOwnerUuid != null) { - try { - sm.setOwningPlayer(Bukkit.getOfflinePlayer(UUID.fromString(cOwnerUuid))); - } catch (IllegalArgumentException ignored) { } - } else { - GameProfile profile = new GameProfile(UUID.randomUUID(), ""); - profile.getProperties().put("textures", new Property("textures", cOwnerBase64)); - if (profileField == null) { - try { - profileField = sm.getClass().getDeclaredField("profile"); - profileField.setAccessible(true); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } - } - try { - profileField.set(sm, profile); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - } - } - - // lore - if (!filters.contains(Filter.LORE)) { - ism.setLore(Chat.legacyColor(cLore)); - } - - // name - if (!filters.contains(Filter.DISPLAY_NAME)) { - if (cName != null) { - ism.setDisplayName(Chat.legacyColor(cName)); - } - } - - - // item flags - if (!filters.contains(Filter.ITEM_FLAGS)) { - if (config.isSet(path + "itemflags")) { - for (String flag : cItemFlags) { - for (ItemFlag iflag : ItemFlag.values()) { - if (iflag.toString().equals(flag)) { - ism.addItemFlags(iflag); - break; - } - } - } - } - } - - // enchantments - if (!filters.contains(Filter.ENCHANTMENTS)) { - if (config.isSet(path + "enchantments")) { - for (String key : config.getStringList(path + "enchantments")) { - String[] split = key.split(":"); - String ench = split[0]; - String levelName; - if (split.length >= 2) { - levelName = split[1]; - } else { - levelName = "1"; - } - - Enchantment enchantment; - if ((enchantment = Enchantment.getByName(ench)) == null) { - continue; - } - - int level; - try { - level = Integer.parseInt(levelName); - } catch (NumberFormatException e) { - level = 1; - } - - ism.addEnchant(enchantment, level, true); - } - } - } - - is.setItemMeta(ism); - return is; - } - - - @Override - public ItemStack getItemStack(String material) { - Material type = null; - int data = 0; - - if (Material.getMaterial(material) != null) { - type = Material.getMaterial(material); - } else if (material.contains(":")) { - String[] parts = material.split(Pattern.quote(":")); - if (parts.length > 1) { - if (Material.getMaterial(parts[0]) != null) { - type = Material.getMaterial(parts[0]); - } - if (StringUtils.isNumeric(parts[1])) { - data = Integer.parseInt(parts[1]); - } - } - } - - if (type == null) { - type = Material.STONE; - } - return new ItemStack(type, 1, (short) data); - } - - @Override - public boolean isValidMaterial(String material) { - Material type = null; - - if (Material.getMaterial(material) != null) { - type = Material.getMaterial(material); - } else if (material.contains(":")) { - String[] parts = material.split(Pattern.quote(":")); - if (parts.length > 1) { - if (Material.getMaterial(parts[0]) != null) { - type = Material.getMaterial(parts[0]); - } - } - } - - return !(type == null); - } -} diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/skullgetter/BukkitSkullGetter.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/skullgetter/BukkitSkullGetter.java new file mode 100644 index 000000000..1390c5d50 --- /dev/null +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/skullgetter/BukkitSkullGetter.java @@ -0,0 +1,54 @@ +package com.leonardobishop.quests.bukkit.hook.skullgetter; + +import com.leonardobishop.quests.bukkit.BukkitQuestsPlugin; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import org.bukkit.Bukkit; +import org.bukkit.inventory.meta.SkullMeta; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.UUID; + +/** + * Utilises reflection to set {@link SkullMeta} {@code profile} field value using a dedicated method. + */ +@SuppressWarnings("deprecation") +public class BukkitSkullGetter extends SkullGetter { + + private static Method setProfileMethod; // introduced by Bukkit in 1.15.1 + + public BukkitSkullGetter(BukkitQuestsPlugin plugin) { + super(plugin); + } + + @Override + void applyName(SkullMeta meta, String name) { + meta.setOwner(name); + } + + @Override + void applyUniqueId(SkullMeta meta, UUID uniqueId) { + meta.setOwningPlayer(Bukkit.getOfflinePlayer(uniqueId)); + } + + @Override + void applyBase64(SkullMeta meta, String base64) { + if (setProfileMethod == null) { + try { + setProfileMethod = meta.getClass().getDeclaredMethod("setProfile", GameProfile.class); + setProfileMethod.setAccessible(true); + } catch (NoSuchMethodException ignored) { + return; + } + } + + GameProfile profile = new GameProfile(UUID.randomUUID(), ""); + profile.getProperties().put("textures", new Property("textures", base64)); + + try { + setProfileMethod.invoke(meta, profile); + } catch (IllegalAccessException | InvocationTargetException ignored) { + } + } +} diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/skullgetter/LegacySkullGetter.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/skullgetter/LegacySkullGetter.java new file mode 100644 index 000000000..6e7de8d01 --- /dev/null +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/skullgetter/LegacySkullGetter.java @@ -0,0 +1,71 @@ +package com.leonardobishop.quests.bukkit.hook.skullgetter; + +import com.leonardobishop.quests.bukkit.BukkitQuestsPlugin; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.inventory.meta.SkullMeta; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.UUID; + +/** + * Utilises reflection to set {@link SkullMeta} {@code profile} field value. + */ +public class LegacySkullGetter extends SkullGetter { + + private static Field profileField; + private static Method setOwningPlayerMethod; // introduced in 1.12.1 + + static { + try { + setOwningPlayerMethod = SkullMeta.class.getDeclaredMethod("setOwningPlayer", OfflinePlayer.class); + } catch (NoSuchMethodException ignored) { + } + } + + public LegacySkullGetter(BukkitQuestsPlugin plugin) { + super(plugin); + } + + @SuppressWarnings("deprecation") + @Override + void applyName(SkullMeta meta, String name) { + meta.setOwner(name); + } + + @Override + void applyUniqueId(SkullMeta meta, UUID uniqueId) { + if (setOwningPlayerMethod == null) { + return; + } + + try { + setOwningPlayerMethod.invoke(meta, Bukkit.getOfflinePlayer(uniqueId)); + } catch (IllegalAccessException | InvocationTargetException ignored) { + } + } + + @Override + void applyBase64(SkullMeta meta, String base64) { + if (profileField == null) { + try { + profileField = meta.getClass().getDeclaredField("profile"); + profileField.setAccessible(true); + } catch (NoSuchFieldException e) { + return; + } + } + + GameProfile profile = new GameProfile(UUID.randomUUID(), ""); + profile.getProperties().put("textures", new Property("textures", base64)); + + try { + profileField.set(meta, profile); + } catch (IllegalAccessException ignored) { + } + } +} diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/skullgetter/ModernSkullGetter.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/skullgetter/ModernSkullGetter.java new file mode 100644 index 000000000..fc02f836e --- /dev/null +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/skullgetter/ModernSkullGetter.java @@ -0,0 +1,31 @@ +package com.leonardobishop.quests.bukkit.hook.skullgetter; + +import com.leonardobishop.quests.bukkit.BukkitQuestsPlugin; +import org.bukkit.Bukkit; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.profile.PlayerProfile; + +import java.util.UUID; + +/** + * Utilises {@link PlayerProfile} and {@link Bukkit#createPlayerProfile(UUID, String)} introduced by Bukkit in 1.18.1 + */ +@SuppressWarnings("deprecation") +public class ModernSkullGetter extends BukkitSkullGetter { + + public ModernSkullGetter(BukkitQuestsPlugin plugin) { + super(plugin); + } + + @Override + void applyName(SkullMeta meta, String name) { + PlayerProfile profile = Bukkit.createPlayerProfile(null, name); + meta.setOwnerProfile(profile); + } + + @Override + void applyUniqueId(SkullMeta meta, UUID uniqueId) { + PlayerProfile profile = Bukkit.createPlayerProfile(uniqueId, null); + meta.setOwnerProfile(profile); + } +} diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/skullgetter/PaperSkullGetter.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/skullgetter/PaperSkullGetter.java new file mode 100644 index 000000000..ed5746627 --- /dev/null +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/skullgetter/PaperSkullGetter.java @@ -0,0 +1,38 @@ +package com.leonardobishop.quests.bukkit.hook.skullgetter; + +import com.destroystokyo.paper.profile.PlayerProfile; +import com.destroystokyo.paper.profile.ProfileProperty; +import com.leonardobishop.quests.bukkit.BukkitQuestsPlugin; +import org.bukkit.Bukkit; +import org.bukkit.inventory.meta.SkullMeta; + +import java.util.UUID; + +/** + * Utilises {@link PlayerProfile} and {@link Bukkit#createProfile(UUID, String)} introduced by Paper in 1.12 + */ +public class PaperSkullGetter extends SkullGetter { + + public PaperSkullGetter(BukkitQuestsPlugin plugin) { + super(plugin); + } + + @Override + void applyName(SkullMeta meta, String name) { + PlayerProfile profile = Bukkit.getServer().createProfile(null, name); + meta.setPlayerProfile(profile); + } + + @Override + void applyUniqueId(SkullMeta meta, UUID uniqueId) { + PlayerProfile profile = Bukkit.getServer().createProfile(uniqueId, null); + meta.setPlayerProfile(profile); + } + + @Override + void applyBase64(SkullMeta meta, String base64) { + PlayerProfile profile = Bukkit.getServer().createProfile(UUID.randomUUID(), null); + profile.setProperty(new ProfileProperty("textures", base64)); + meta.setPlayerProfile(profile); + } +} diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/skullgetter/SkullGetter.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/skullgetter/SkullGetter.java new file mode 100644 index 000000000..82958ddcd --- /dev/null +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/hook/skullgetter/SkullGetter.java @@ -0,0 +1,49 @@ +package com.leonardobishop.quests.bukkit.hook.skullgetter; + +import com.leonardobishop.quests.bukkit.BukkitQuestsPlugin; +import org.bukkit.inventory.meta.SkullMeta; + +import java.util.UUID; + +public abstract class SkullGetter { + + protected final BukkitQuestsPlugin plugin; + + public SkullGetter(BukkitQuestsPlugin plugin) { + this.plugin = plugin; + } + + public final boolean apply(SkullMeta meta, String name, String uniqueIdString, String base64) { + if (name != null) { + applyName(meta, name); + return true; + } + + if (uniqueIdString != null) { + UUID uniqueId; + try { + uniqueId = UUID.fromString(uniqueIdString); + } catch (IllegalArgumentException e) { + uniqueId = null; + } + + if (uniqueId != null) { + applyUniqueId(meta, uniqueId); + return true; + } + } + + if (base64 != null) { + applyBase64(meta, base64); + return true; + } + + return false; + } + + abstract void applyName(SkullMeta meta, String name); + + abstract void applyUniqueId(SkullMeta meta, UUID uniqueId); + + abstract void applyBase64(SkullMeta meta, String base64); +} diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/util/CompatUtils.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/util/CompatUtils.java index e550c509b..e6609b44a 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/util/CompatUtils.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/util/CompatUtils.java @@ -5,6 +5,8 @@ public class CompatUtils { + private static final String version = Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3]; + public static boolean classExists(String className) { try { Class.forName(className); @@ -15,8 +17,10 @@ public static boolean classExists(String className) { } public static boolean classWithMethodExists(String className, String methodName, Class... methodParameterTypes) { + className = className.replace("{}", version); + try { - Class.forName(className).getMethod(methodName, methodParameterTypes); + Class.forName(className).getDeclaredMethod(methodName, methodParameterTypes); return true; } catch (ClassNotFoundException | NoSuchMethodException e) { return false;