diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0d61f2d8..78b673a3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] # Compile only -spigot = "1.21.4-R0.1-SNAPSHOT" +spigot = "1.21.5-R0.1-SNAPSHOT" vault = "1.7.1" authlib = "1.5.25" headdb = "1.3.2" diff --git a/src/main/java/com/extendedclip/deluxemenus/DeluxeMenus.java b/src/main/java/com/extendedclip/deluxemenus/DeluxeMenus.java index 642076f8..f812f937 100644 --- a/src/main/java/com/extendedclip/deluxemenus/DeluxeMenus.java +++ b/src/main/java/com/extendedclip/deluxemenus/DeluxeMenus.java @@ -66,7 +66,11 @@ public void onLoad() { return; } - this.debug(DebugLevel.HIGHEST, Level.WARNING, "Could not setup a NMS hook for your server version!"); + this.debug( + DebugLevel.HIGHEST, + Level.WARNING, + "Could not setup a NMS hook for your server version! The following Item options will not work: nbt_int, nbt_ints, nbt_string and nbt_strings." + ); } @Override diff --git a/src/main/java/com/extendedclip/deluxemenus/config/DeluxeMenusConfig.java b/src/main/java/com/extendedclip/deluxemenus/config/DeluxeMenusConfig.java index e2bdfa6c..5232c535 100644 --- a/src/main/java/com/extendedclip/deluxemenus/config/DeluxeMenusConfig.java +++ b/src/main/java/com/extendedclip/deluxemenus/config/DeluxeMenusConfig.java @@ -6,10 +6,11 @@ import com.extendedclip.deluxemenus.action.ClickActionTask; import com.extendedclip.deluxemenus.action.ClickHandler; import com.extendedclip.deluxemenus.hooks.ItemHook; -import com.extendedclip.deluxemenus.menu.options.LoreAppendMode; import com.extendedclip.deluxemenus.menu.Menu; import com.extendedclip.deluxemenus.menu.MenuHolder; import com.extendedclip.deluxemenus.menu.MenuItem; +import com.extendedclip.deluxemenus.menu.options.CustomModelDataComponent; +import com.extendedclip.deluxemenus.menu.options.LoreAppendMode; import com.extendedclip.deluxemenus.menu.options.MenuItemOptions; import com.extendedclip.deluxemenus.menu.options.MenuOptions; import com.extendedclip.deluxemenus.requirement.HasExpRequirement; @@ -632,6 +633,13 @@ private Map> loadMenuItems(FileConfiguration .tooltipStyle(c.getString(currentPath + "tooltip_style", null)) .itemModel(c.getString(currentPath + "item_model", null)); + if (c.contains(currentPath + "model_data_component") && c.isConfigurationSection(currentPath + "model_data_component")) { + final ConfigurationSection modelDataComponent = c.getConfigurationSection(currentPath + "model_data_component"); + if (modelDataComponent != null) { + builder.customModelDataComponent(CustomModelDataComponent.builder().colors(modelDataComponent.getStringList("colors")).flags(modelDataComponent.getStringList("flags")).floats(modelDataComponent.getStringList("floats")).strings(modelDataComponent.getStringList("strings"))); + } + } + // Lore Append Mode if (c.contains(currentPath + "lore_append_mode")) { String loreAppendMode = c.getString(currentPath + "lore_append_mode", "OVERRIDE").toUpperCase(); @@ -907,6 +915,18 @@ private RequirementList getRequirements(FileConfiguration c, String path) { wrapper.setCustomData(c.getInt(rPath + ".modeldata", 0)); } + if (c.contains(rPath + ".model_data_component") && c.isConfigurationSection(rPath + ".model_data_component")) { + final ConfigurationSection modelDataComponent = c.getConfigurationSection(rPath + ".model_data_component"); + if (modelDataComponent != null) { + wrapper.setCustomModelDataComponent( + CustomModelDataComponent.builder() + .colors(modelDataComponent.getStringList("colors")) + .flags(modelDataComponent.getStringList("flags")) + .floats(modelDataComponent.getStringList("floats")) + .strings(modelDataComponent.getStringList("strings"))); + } + } + if (c.contains(rPath + ".name_contains")) { wrapper.setNameContains(c.getBoolean(rPath + ".name_contains")); } else { @@ -968,7 +988,7 @@ private RequirementList getRequirements(FileConfiguration c, String path) { plugin.debug( DebugLevel.HIGHEST, Level.WARNING, - "Has Permissions requirement at path: " + rPath + " has a minimum higher than the amount of permissions. Using "+permissions.size()+" instead" + "Has Permissions requirement at path: " + rPath + " has a minimum higher than the amount of permissions. Using " + permissions.size() + " instead" ); minimum = permissions.size(); } @@ -1252,9 +1272,14 @@ public void debug(String... messages) { public File getMenuDirector() { return menuDirectory; } - public void addEnchantmentsOptionToBuilder(final FileConfiguration c, final String currentPath, - final String itemKey, final String menuName, - final MenuItemOptions.MenuItemOptionsBuilder builder) { + + public void addEnchantmentsOptionToBuilder( + final FileConfiguration c, + final String currentPath, + final String itemKey, + final String menuName, + final MenuItemOptions.MenuItemOptionsBuilder builder + ) { if (!c.contains(currentPath + "enchantments")) { return; } @@ -1267,9 +1292,7 @@ public void addEnchantmentsOptionToBuilder(final FileConfiguration c, final Stri plugin.debug( DebugLevel.HIGHEST, Level.WARNING, - "Enchantment format '" + configEnchantment + "' is incorrect for item " + itemKey + - " in GUI " + menuName + "!", - "Correct format: - ';" + "Enchantment format '" + configEnchantment + "' is incorrect for item " + itemKey + " in GUI " + menuName + "!", "Correct format: - ';" ); continue; } @@ -1279,9 +1302,7 @@ public void addEnchantmentsOptionToBuilder(final FileConfiguration c, final Stri plugin.debug( DebugLevel.HIGHEST, Level.WARNING, - "Enchantment format '" + configEnchantment + "' is incorrect for item " + itemKey + - " in GUI " + menuName + "!", - "Correct format: - ';" + "Enchantment format '" + configEnchantment + "' is incorrect for item " + itemKey + " in GUI " + menuName + "!", "Correct format: - ';" ); continue; } @@ -1291,8 +1312,7 @@ public void addEnchantmentsOptionToBuilder(final FileConfiguration c, final Stri plugin.debug( DebugLevel.HIGHEST, Level.WARNING, - "Enchantment '" + parts[0].strip() + "' for item " + itemKey + - " in menu " + menuName + " is not a valid enchantment name!" + "Enchantment '" + parts[0].strip() + "' for item " + itemKey + " in menu " + menuName + " is not a valid enchantment name!" ); } @@ -1302,8 +1322,7 @@ public void addEnchantmentsOptionToBuilder(final FileConfiguration c, final Stri plugin.debug( DebugLevel.HIGHEST, Level.WARNING, - "Enchantment level '" + parts[1].strip() + "' is incorrect for item " + itemKey + - " in menu " + menuName + "!" + "Enchantment level '" + parts[1].strip() + "' is incorrect for item " + itemKey + " in menu " + menuName + "!" ); } parsedEnchantments.put(enchantment, level); @@ -1311,8 +1330,13 @@ public void addEnchantmentsOptionToBuilder(final FileConfiguration c, final Stri builder.enchantments(parsedEnchantments); } - public void addDamageOptionToBuilder(final FileConfiguration c, final String currentPath, final String itemKey, - final String menuName, final MenuItemOptions.MenuItemOptionsBuilder builder) { + public void addDamageOptionToBuilder( + final FileConfiguration c, + final String currentPath, + final String itemKey, + final String menuName, + final MenuItemOptions.MenuItemOptionsBuilder builder + ) { boolean damageOptionIsPresent = false; String damageValue = null; diff --git a/src/main/java/com/extendedclip/deluxemenus/menu/MenuItem.java b/src/main/java/com/extendedclip/deluxemenus/menu/MenuItem.java index 544d712e..55b216e5 100644 --- a/src/main/java/com/extendedclip/deluxemenus/menu/MenuItem.java +++ b/src/main/java/com/extendedclip/deluxemenus/menu/MenuItem.java @@ -5,6 +5,7 @@ import com.extendedclip.deluxemenus.menu.options.HeadType; import com.extendedclip.deluxemenus.menu.options.LoreAppendMode; import com.extendedclip.deluxemenus.menu.options.MenuItemOptions; +import com.extendedclip.deluxemenus.menu.options.CustomModelDataComponent; import com.extendedclip.deluxemenus.nbt.NbtProvider; import com.extendedclip.deluxemenus.utils.DebugLevel; import com.extendedclip.deluxemenus.utils.ItemUtils; @@ -39,6 +40,7 @@ import org.bukkit.inventory.meta.trim.TrimPattern; import org.bukkit.potion.PotionEffect; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.ArrayList; @@ -158,13 +160,9 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { if (meta != null) { if (this.options.rgb().isPresent()) { - final String rgbString = holder.setPlaceholdersAndArguments(this.options.rgb().get()); - final String[] parts = rgbString.split(","); - - try { - meta.setColor(Color.fromRGB(Integer.parseInt(parts[0].trim()), Integer.parseInt(parts[1].trim()), - Integer.parseInt(parts[2].trim()))); - } catch (Exception ignored) { + final Color color = parseRGBColor(holder.setPlaceholdersAndArguments(this.options.rgb().get())); + if (color != null) { + meta.setColor(color); } } @@ -224,7 +222,7 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { return itemStack; } - if (this.options.customModelData().isPresent() && VersionHelper.IS_CUSTOM_MODEL_DATA) { + if (VersionHelper.IS_CUSTOM_MODEL_DATA && this.options.customModelData().isPresent()) { try { final int modelData = Integer.parseInt(holder.setPlaceholdersAndArguments(this.options.customModelData().get())); itemMeta.setCustomModelData(modelData); @@ -232,6 +230,10 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { } } + if (VersionHelper.IS_CUSTOM_MODEL_DATA_COMPONENT && this.options.customModelDataComponent().isPresent()) { + itemMeta.setCustomModelDataComponent(parseCustomModelDataComponent(this.options.customModelDataComponent().get(), itemMeta.getCustomModelDataComponent(), holder)); + } + if (this.options.displayName().isPresent()) { final String displayName = holder.setPlaceholdersAndArguments(this.options.displayName().get()); itemMeta.setDisplayName(StringUtils.color(displayName)); @@ -346,37 +348,33 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { } if (itemMeta instanceof LeatherArmorMeta && this.options.rgb().isPresent()) { - final String rgbString = holder.setPlaceholdersAndArguments(this.options.rgb().get()); - final String[] parts = rgbString.split(","); final LeatherArmorMeta leatherArmorMeta = (LeatherArmorMeta) itemMeta; - try { - leatherArmorMeta.setColor(Color.fromRGB(Integer.parseInt(parts[0].trim()), Integer.parseInt(parts[1].trim()), - Integer.parseInt(parts[2].trim()))); - itemStack.setItemMeta(leatherArmorMeta); - } catch (final Exception exception) { - plugin.printStacktrace( - "Invalid rgb colors found for leather armor: " + parts[0].trim() + ", " + parts[1].trim() + ", " + - parts[2].trim(), - exception + final Color color = parseRGBColor(holder.setPlaceholdersAndArguments(this.options.rgb().get())); + if (color != null) { + leatherArmorMeta.setColor(color); + } else { + plugin.debug( + DebugLevel.HIGHEST, + Level.WARNING, + "Invalid rgb colors found for leather armor: " + this.options.rgb().get() ); } + + itemStack.setItemMeta(leatherArmorMeta); } else if (itemMeta instanceof FireworkEffectMeta && this.options.rgb().isPresent()) { - final String rgbString = holder.setPlaceholdersAndArguments(this.options.rgb().get()); - final String[] parts = rgbString.split(","); final FireworkEffectMeta fireworkEffectMeta = (FireworkEffectMeta) itemMeta; - - try { - fireworkEffectMeta.setEffect(FireworkEffect.builder().withColor(Color.fromRGB(Integer.parseInt(parts[0].trim()), - Integer.parseInt(parts[1].trim()), Integer.parseInt(parts[2].trim()))).build()); - itemStack.setItemMeta(fireworkEffectMeta); - } catch (final Exception exception) { - plugin.printStacktrace( - "Invalid rgb colors found for firework or firework star: " + parts[0].trim() + ", " - + parts[1].trim() + ", " + parts[2].trim(), - exception + final Color color = parseRGBColor(holder.setPlaceholdersAndArguments(this.options.rgb().get())); + if (color != null) { + fireworkEffectMeta.setEffect(FireworkEffect.builder().withColor(color).build()); + } else { + plugin.debug( + DebugLevel.HIGHEST, + Level.WARNING, + "Invalid RGB color found for firework or firework star: " + this.options.rgb().get() ); } + itemStack.setItemMeta(fireworkEffectMeta); } else if (itemMeta instanceof EnchantmentStorageMeta && !this.options.enchantments().isEmpty()) { final EnchantmentStorageMeta enchantmentStorageMeta = (EnchantmentStorageMeta) itemMeta; for (final Map.Entry entry : this.options.enchantments().entrySet()) { @@ -547,6 +545,62 @@ protected List getMenuItemLore(@NotNull final MenuHolder holder, @NotNul .collect(Collectors.toList()); } + private @NotNull org.bukkit.inventory.meta.components.CustomModelDataComponent parseCustomModelDataComponent( + @NotNull final CustomModelDataComponent unparsedComponent, + @NotNull final org.bukkit.inventory.meta.components.CustomModelDataComponent component, + @NotNull final MenuHolder holder + ) { + if (!unparsedComponent.colors().isEmpty()) { + final List colors = unparsedComponent.colors() + .stream() + .map(holder::setPlaceholdersAndArguments) + .map(this::parseRGBColor) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + component.setColors(colors); + } + + if (!unparsedComponent.flags().isEmpty()) { + final List flags = unparsedComponent.flags() + .stream() + .map(holder::setPlaceholdersAndArguments) + .map(Boolean::parseBoolean) + .collect(Collectors.toList()); + component.setFlags(flags); + } + + if (!unparsedComponent.floats().isEmpty()) { + final List floats = unparsedComponent.floats() + .stream() + .map(holder::setPlaceholdersAndArguments) + .map(Float::parseFloat) + .collect(Collectors.toList()); + component.setFloats(floats); + } + + if (!unparsedComponent.strings().isEmpty()) { + final List strings = unparsedComponent.strings() + .stream() + .map(holder::setPlaceholdersAndArguments) + .collect(Collectors.toList()); + component.setStrings(strings); + } + + return component; + } + + private @Nullable Color parseRGBColor(@NotNull final String input) { + final Color color = StringUtils.parseRGBColor(input); + if (color == null) { + plugin.debug( + DebugLevel.HIGHEST, + Level.WARNING, + "Invalid RGB color found: " + input + ); + } + return color; + } + public @NotNull MenuItemOptions options() { return options; } diff --git a/src/main/java/com/extendedclip/deluxemenus/menu/options/CustomModelDataComponent.java b/src/main/java/com/extendedclip/deluxemenus/menu/options/CustomModelDataComponent.java new file mode 100644 index 00000000..c4bbdbbf --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/menu/options/CustomModelDataComponent.java @@ -0,0 +1,65 @@ +package com.extendedclip.deluxemenus.menu.options; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class CustomModelDataComponent { + private List colors = new ArrayList<>(); + private List flags = new ArrayList<>(); + private List floats = new ArrayList<>(); + private List strings = new ArrayList<>(); + + private CustomModelDataComponent() { + // Private constructor to force builder usage + } + + @NotNull + public CustomModelDataComponent colors(@NotNull final List<@NotNull String> colors) { + this.colors = colors; + return this; + } + + @NotNull + public List<@NotNull String> colors() { + return colors; + } + + @NotNull + public CustomModelDataComponent flags(@NotNull final List<@NotNull String> flags) { + this.flags = flags; + return this; + } + + @NotNull + public List<@NotNull String> flags() { + return flags; + } + + @NotNull + public CustomModelDataComponent floats(@NotNull final List<@NotNull String> floats) { + this.floats = floats; + return this; + } + + @NotNull + public List<@NotNull String> floats() { + return floats; + } + + @NotNull + public CustomModelDataComponent strings(@NotNull final List<@NotNull String> strings) { + this.strings = strings; + return this; + } + + @NotNull + public List<@NotNull String> strings() { + return strings; + } + + public static @NotNull CustomModelDataComponent builder() { + return new CustomModelDataComponent(); + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/menu/options/MenuItemOptions.java b/src/main/java/com/extendedclip/deluxemenus/menu/options/MenuItemOptions.java index 3377bec1..f0b4bbd8 100644 --- a/src/main/java/com/extendedclip/deluxemenus/menu/options/MenuItemOptions.java +++ b/src/main/java/com/extendedclip/deluxemenus/menu/options/MenuItemOptions.java @@ -25,6 +25,7 @@ public class MenuItemOptions { private final String damage; private final int amount; private final String customModelData; + private final CustomModelDataComponent customModelDataComponent; private final String dynamicAmount; private final String lightLevel; private final String displayName; @@ -87,6 +88,7 @@ private MenuItemOptions(final @NotNull MenuItemOptionsBuilder builder) { this.damage = builder.damage; this.amount = builder.amount; this.customModelData = builder.customModelData; + this.customModelDataComponent = builder.customModelDataComponent; this.dynamicAmount = builder.dynamicAmount; this.lightLevel = builder.lightLevel; this.displayName = builder.displayName; @@ -156,6 +158,10 @@ public int amount() { return Optional.ofNullable(customModelData); } + public @NotNull Optional customModelDataComponent() { + return Optional.ofNullable(customModelDataComponent); + } + public @NotNull Optional dynamicAmount() { return Optional.ofNullable(dynamicAmount); } @@ -354,6 +360,7 @@ public boolean updatePlaceholders() { .damage(this.damage) .amount(this.amount) .customModelData(this.customModelData) + .customModelDataComponent(this.customModelDataComponent) .dynamicAmount(this.dynamicAmount) .lightLevel(this.lightLevel) .displayName(this.displayName) @@ -407,6 +414,7 @@ public static class MenuItemOptionsBuilder { private String damage; private int amount; private String customModelData; + private CustomModelDataComponent customModelDataComponent; private String dynamicAmount; private String lightLevel; private String displayName; @@ -487,6 +495,11 @@ public MenuItemOptionsBuilder customModelData(final @Nullable String customModel return this; } + public MenuItemOptionsBuilder customModelDataComponent(final @Nullable CustomModelDataComponent customModelDataComponent) { + this.customModelDataComponent = customModelDataComponent; + return this; + } + public MenuItemOptionsBuilder dynamicAmount(final @Nullable String configDynamicAmount) { this.dynamicAmount = configDynamicAmount; return this; diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/HasItemRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/HasItemRequirement.java index 97b3d965..9c1a1442 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/HasItemRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/HasItemRequirement.java @@ -6,157 +6,237 @@ import com.extendedclip.deluxemenus.requirement.wrappers.ItemWrapper; import com.extendedclip.deluxemenus.utils.StringUtils; import com.extendedclip.deluxemenus.utils.VersionHelper; -import java.util.List; -import java.util.stream.Collectors; +import org.bukkit.Color; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.components.CustomModelDataComponent; +import org.jetbrains.annotations.NotNull; -public class HasItemRequirement extends Requirement { - - private final DeluxeMenus plugin; - private final ItemWrapper wrapper; - private final boolean invert; - - public HasItemRequirement(final DeluxeMenus plugin, final ItemWrapper wrapper, final boolean invert) { - this.plugin = plugin; - this.wrapper = wrapper; - this.invert = invert; - } - - @Override - public boolean evaluate(MenuHolder holder) { - String materialName = holder.setPlaceholdersAndArguments(wrapper.getMaterial()); - Material material = DeluxeMenus.MATERIALS.get(materialName.toUpperCase()); - ItemHook pluginHook = null; - if (material == null) { - pluginHook = plugin.getItemHooks().values() - .stream() - .filter(x -> materialName.toLowerCase().startsWith(x.getPrefix())) - .findFirst() - .orElse(null); - if (pluginHook == null) return invert; - } - - if (material == Material.AIR) return invert == (holder.getViewer().getInventory().firstEmpty() == -1); +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; - ItemStack[] armor = wrapper.checkArmor() ? holder.getViewer().getInventory().getArmorContents() : null; - ItemStack[] offHand = wrapper.checkOffhand() ? holder.getViewer().getInventory().getExtraContents() : null; - ItemStack[] inventory = holder.getViewer().getInventory().getStorageContents(); +@SuppressWarnings("UnstableApiUsage") +public class HasItemRequirement extends Requirement { - int total = 0; - for (ItemStack itemToCheck: inventory) { - if (!isRequiredItem(itemToCheck, holder, material, pluginHook)) continue; - total += itemToCheck.getAmount(); - } + private final DeluxeMenus plugin; + private final ItemWrapper wrapper; + private final boolean invert; - if (offHand != null) { - for (ItemStack itemToCheck: offHand) { - if (!isRequiredItem(itemToCheck, holder, material, pluginHook)) continue; - total += itemToCheck.getAmount(); - } + public HasItemRequirement(final DeluxeMenus plugin, final ItemWrapper wrapper, final boolean invert) { + this.plugin = plugin; + this.wrapper = wrapper; + this.invert = invert; } - if (armor != null) { - for (ItemStack itemToCheck: armor) { - if (!isRequiredItem(itemToCheck, holder, material, pluginHook)) continue; - total += itemToCheck.getAmount(); - } - } + @Override + public boolean evaluate(MenuHolder holder) { + String materialName = holder.setPlaceholdersAndArguments(wrapper.getMaterial()); + Material material = DeluxeMenus.MATERIALS.get(materialName.toUpperCase()); + ItemHook pluginHook = null; + if (material == null) { + pluginHook = plugin.getItemHooks().values() + .stream() + .filter(x -> materialName.toLowerCase().startsWith(x.getPrefix())) + .findFirst() + .orElse(null); + if (pluginHook == null) return invert; + } - return invert == (total < wrapper.getAmount()); - } + if (material == Material.AIR) return invert == (holder.getViewer().getInventory().firstEmpty() == -1); - private boolean isRequiredItem(ItemStack itemToCheck, MenuHolder holder, Material material, ItemHook pluginHook) { - if (itemToCheck == null || itemToCheck.getType() == Material.AIR) return false; + ItemStack[] armor = wrapper.checkArmor() ? holder.getViewer().getInventory().getArmorContents() : null; + ItemStack[] offHand = wrapper.checkOffhand() ? holder.getViewer().getInventory().getExtraContents() : null; + ItemStack[] inventory = holder.getViewer().getInventory().getStorageContents(); - if (pluginHook != null) { - if (!pluginHook.itemMatchesIdentifiers(itemToCheck, holder.setPlaceholdersAndArguments(wrapper.getMaterial().substring(pluginHook.getPrefix().length())))) return false; - } - else if (wrapper.getMaterial() != null && itemToCheck.getType() != material) return false; - if (wrapper.hasData() && itemToCheck.getDurability() != wrapper.getData()) return false; - - ItemMeta metaToCheck = itemToCheck.getItemMeta(); - if (wrapper.isStrict()) { - if (metaToCheck != null) { - if (VersionHelper.IS_CUSTOM_MODEL_DATA) { - if (metaToCheck.hasCustomModelData()) return false; + int total = 0; + for (ItemStack itemToCheck : inventory) { + if (!isRequiredItem(itemToCheck, holder, material, pluginHook)) continue; + total += itemToCheck.getAmount(); } - if (metaToCheck.hasLore()) return false; - return !metaToCheck.hasDisplayName(); - } - - } else { - if ((wrapper.getCustomData() != 0 || wrapper.getName() != null || wrapper.getLore() != null) && metaToCheck == null) - return false; - - if (wrapper.getCustomData() != 0) { - if (VersionHelper.IS_CUSTOM_MODEL_DATA) { - if (!metaToCheck.hasCustomModelData()) return false; - if (metaToCheck.getCustomModelData() != wrapper.getCustomData()) return false; + + if (offHand != null) { + for (ItemStack itemToCheck : offHand) { + if (!isRequiredItem(itemToCheck, holder, material, pluginHook)) continue; + total += itemToCheck.getAmount(); + } } - } - if (wrapper.getName() != null) { - if (!metaToCheck.hasDisplayName()) return false; + if (armor != null) { + for (ItemStack itemToCheck : armor) { + if (!isRequiredItem(itemToCheck, holder, material, pluginHook)) continue; + total += itemToCheck.getAmount(); + } + } - String name = StringUtils.color(holder.setPlaceholdersAndArguments(wrapper.getName())); - String nameToCheck = StringUtils.color(holder.setPlaceholdersAndArguments(metaToCheck.getDisplayName())); + return invert == (total < wrapper.getAmount()); + } - if (wrapper.checkNameContains() && wrapper.checkNameIgnoreCase()) { - if (!org.apache.commons.lang3.StringUtils.containsIgnoreCase(nameToCheck, name)) return false; - } - else if (wrapper.checkNameContains()) { - if (!nameToCheck.contains(name)) return false; - } - else if (wrapper.checkNameIgnoreCase()) { - if (!nameToCheck.equalsIgnoreCase(name)) return false; - } - else if (!nameToCheck.equals(name)) { - return false; + private boolean isRequiredItem(ItemStack itemToCheck, MenuHolder holder, Material material, ItemHook pluginHook) { + if (itemToCheck == null || itemToCheck.getType() == Material.AIR) return false; + + if (pluginHook != null) { + if (!pluginHook.itemMatchesIdentifiers(itemToCheck, holder.setPlaceholdersAndArguments(wrapper.getMaterial().substring(pluginHook.getPrefix().length())))) + return false; + } else if (wrapper.getMaterial() != null && itemToCheck.getType() != material) return false; + if (wrapper.hasData() && itemToCheck.getDurability() != wrapper.getData()) return false; + + ItemMeta metaToCheck = itemToCheck.getItemMeta(); + if (wrapper.isStrict()) { + if (metaToCheck != null) { + if (VersionHelper.IS_CUSTOM_MODEL_DATA && metaToCheck.hasCustomModelData()) { + return false; + } + if (VersionHelper.IS_CUSTOM_MODEL_DATA_COMPONENT && !isEmptyModelData(metaToCheck.getCustomModelDataComponent())) { + return false; + } + if (metaToCheck.hasLore()) return false; + return !metaToCheck.hasDisplayName(); + } + } else { + if (metaToCheck == null) { + if (wrapper.getCustomData() != 0 || wrapper.getName() != null || wrapper.getLore() != null || wrapper.getLoreList() != null) { + return false; + } + + if (VersionHelper.IS_CUSTOM_MODEL_DATA_COMPONENT) { + if (!isEmptyModelData(wrapper.getCustomModelDataComponent())) { + return false; + } + } + } + + if (wrapper.getCustomData() != 0) { + if (VersionHelper.IS_CUSTOM_MODEL_DATA) { + if (!metaToCheck.hasCustomModelData()) return false; + if (metaToCheck.getCustomModelData() != wrapper.getCustomData()) return false; + } + } + + if (VersionHelper.IS_CUSTOM_MODEL_DATA_COMPONENT && !isEmptyModelData(wrapper.getCustomModelDataComponent()) && !itemModelComponentContains(holder, metaToCheck.getCustomModelDataComponent(), wrapper.getCustomModelDataComponent())) { + return false; + } + + if (wrapper.getName() != null) { + if (!metaToCheck.hasDisplayName()) return false; + + String name = StringUtils.color(holder.setPlaceholdersAndArguments(wrapper.getName())); + String nameToCheck = StringUtils.color(holder.setPlaceholdersAndArguments(metaToCheck.getDisplayName())); + + if (wrapper.checkNameContains() && wrapper.checkNameIgnoreCase()) { + if (!org.apache.commons.lang3.StringUtils.containsIgnoreCase(nameToCheck, name)) return false; + } else if (wrapper.checkNameContains()) { + if (!nameToCheck.contains(name)) return false; + } else if (wrapper.checkNameIgnoreCase()) { + if (!nameToCheck.equalsIgnoreCase(name)) return false; + } else if (!nameToCheck.equals(name)) { + return false; + } + } + + if (wrapper.getLoreList() != null) { + List loreX = metaToCheck.getLore(); + if (loreX == null) return false; + + String lore = wrapper.getLoreList().stream().map(holder::setPlaceholdersAndArguments).map(StringUtils::color).collect(Collectors.joining("&&")); + String loreToCheck = loreX.stream().map(holder::setPlaceholdersAndArguments).map(StringUtils::color).collect(Collectors.joining("&&")); + + if (wrapper.checkLoreContains() && wrapper.checkLoreIgnoreCase()) { + if (!org.apache.commons.lang3.StringUtils.containsIgnoreCase(loreToCheck, lore)) return false; + } else if (wrapper.checkLoreContains()) { + if (!loreToCheck.contains(lore)) return false; + } else if (wrapper.checkLoreIgnoreCase()) { + if (!loreToCheck.equalsIgnoreCase(lore)) return false; + } else if (!loreToCheck.equals(lore)) { + return false; + } + } + + if (wrapper.getLore() != null) { + List loreX = metaToCheck.getLore(); + if (loreX == null) return false; + + String lore = StringUtils.color(holder.setPlaceholdersAndArguments(wrapper.getLore())); + String loreToCheck = loreX.stream().map(holder::setPlaceholdersAndArguments).map(StringUtils::color).collect(Collectors.joining("&&")); + + if (wrapper.checkLoreContains() && wrapper.checkLoreIgnoreCase()) { + return org.apache.commons.lang3.StringUtils.containsIgnoreCase(loreToCheck, lore); + } else if (wrapper.checkLoreContains()) { + return loreToCheck.contains(lore); + } else if (wrapper.checkLoreIgnoreCase()) { + return loreToCheck.equalsIgnoreCase(lore); + } else return loreToCheck.equals(lore); + } } - } + return true; + } - if (wrapper.getLoreList() != null) { - List loreX = metaToCheck.getLore(); - if (loreX == null) return false; + private boolean isEmptyModelData(@NotNull final CustomModelDataComponent modelData) { + return modelData.getColors().isEmpty() && modelData.getFlags().isEmpty() && modelData.getFloats().isEmpty() && modelData.getStrings().isEmpty(); + } - String lore = wrapper.getLoreList().stream().map(holder::setPlaceholdersAndArguments).map(StringUtils::color).collect(Collectors.joining("&&")); - String loreToCheck = loreX.stream().map(holder::setPlaceholdersAndArguments).map(StringUtils::color).collect(Collectors.joining("&&")); + private boolean isEmptyModelData(@NotNull final com.extendedclip.deluxemenus.menu.options.CustomModelDataComponent modelData) { + return modelData.colors().isEmpty() && modelData.flags().isEmpty() && modelData.floats().isEmpty() && modelData.strings().isEmpty(); + } - if (wrapper.checkLoreContains() && wrapper.checkLoreIgnoreCase()) { - if (!org.apache.commons.lang3.StringUtils.containsIgnoreCase(loreToCheck, lore)) return false; - } - else if (wrapper.checkLoreContains()) { - if (!loreToCheck.contains(lore)) return false; + private boolean itemModelComponentContains(MenuHolder holder, @NotNull final CustomModelDataComponent modelData, @NotNull final com.extendedclip.deluxemenus.menu.options.CustomModelDataComponent wrapper) { + if (!wrapper.colors().isEmpty()) { + final List colors = wrapper.colors() + .stream() + .map(holder::setPlaceholdersAndArguments) + .map(StringUtils::parseRGBColor) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + for (Color color : colors) { + if (!modelData.getColors().contains(color)) { + return false; + } + } } - else if (wrapper.checkLoreIgnoreCase()) { - if (!loreToCheck.equalsIgnoreCase(lore)) return false; - } - else if (!loreToCheck.equals(lore)) { - return false; - } - } - - if (wrapper.getLore() != null) { - List loreX = metaToCheck.getLore(); - if (loreX == null) return false; - - String lore = StringUtils.color(holder.setPlaceholdersAndArguments(wrapper.getLore())); - String loreToCheck = loreX.stream().map(holder::setPlaceholdersAndArguments).map(StringUtils::color).collect(Collectors.joining("&&")); - if (wrapper.checkLoreContains() && wrapper.checkLoreIgnoreCase()) { - return org.apache.commons.lang3.StringUtils.containsIgnoreCase(loreToCheck, lore); + if (!wrapper.flags().isEmpty()) { + final List flags = wrapper.flags() + .stream() + .map(holder::setPlaceholdersAndArguments) + .map(Boolean::parseBoolean) + .collect(Collectors.toList()); + + for (Boolean flag : flags) { + if (!modelData.getFlags().contains(flag)) { + return false; + } + } } - else if (wrapper.checkLoreContains()) { - return loreToCheck.contains(lore); + + if (!wrapper.floats().isEmpty()) { + final List floats = wrapper.floats() + .stream() + .map(holder::setPlaceholdersAndArguments) + .map(Float::parseFloat) + .collect(Collectors.toList()); + + for (Float floatValue : floats) { + if (!modelData.getFloats().contains(floatValue)) { + return false; + } + } } - else if (wrapper.checkLoreIgnoreCase()) { - return loreToCheck.equalsIgnoreCase(lore); + + if (!wrapper.strings().isEmpty()) { + final List strings = wrapper.strings() + .stream() + .map(holder::setPlaceholdersAndArguments) + .collect(Collectors.toList()); + + for (String string : strings) { + if (!modelData.getStrings().contains(string)) { + return false; + } + } } - else return loreToCheck.equals(lore); - } + + return true; } - return true; - } } diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/wrappers/ItemWrapper.java b/src/main/java/com/extendedclip/deluxemenus/requirement/wrappers/ItemWrapper.java index f3bf7a8f..760a44c9 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/wrappers/ItemWrapper.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/wrappers/ItemWrapper.java @@ -1,5 +1,8 @@ package com.extendedclip.deluxemenus.requirement.wrappers; +import com.extendedclip.deluxemenus.menu.options.CustomModelDataComponent; +import org.jetbrains.annotations.NotNull; + import java.util.List; public class ItemWrapper { @@ -11,6 +14,7 @@ public class ItemWrapper { private short data = 0; private boolean hasData = false; + private CustomModelDataComponent customDataComponent = CustomModelDataComponent.builder(); private int customData = 0; private int amount = 1; @@ -80,6 +84,14 @@ public void setCustomData(int customData) { this.customData = customData; } + public @NotNull CustomModelDataComponent getCustomModelDataComponent() { + return this.customDataComponent; + } + + public void setCustomModelDataComponent(@NotNull final CustomModelDataComponent customDataComponent) { + this.customDataComponent = customDataComponent; + } + public int getAmount() { return amount; } diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/StringUtils.java b/src/main/java/com/extendedclip/deluxemenus/utils/StringUtils.java index 0304c7eb..6ae95804 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/StringUtils.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/StringUtils.java @@ -6,6 +6,7 @@ import me.clip.placeholderapi.PlaceholderAPI; import net.md_5.bungee.api.ChatColor; +import org.bukkit.Color; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -73,4 +74,18 @@ public static String replaceArguments(@NotNull String input, final @Nullable Map return input; } + + @Nullable + public static Color parseRGBColor(@NotNull final String input) { + final String[] parts = input.split(","); + try { + return Color.fromRGB( + Integer.parseInt(parts[0].trim()), + Integer.parseInt(parts[1].trim()), + Integer.parseInt(parts[2].trim()) + ); + } catch (final Exception exception) { + return null; + } + } } diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/VersionHelper.java b/src/main/java/com/extendedclip/deluxemenus/utils/VersionHelper.java index 08e0d68f..87e9a943 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/VersionHelper.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/VersionHelper.java @@ -21,6 +21,8 @@ public final class VersionHelper { private static final String PACKAGE_NAME = Bukkit.getServer().getClass().getPackage().getName(); public static final String NMS_VERSION = PACKAGE_NAME.substring(PACKAGE_NAME.lastIndexOf('.') + 1); + // Custom Model Data Component + private static final int V1_21_4 = 1_21_4; // Tooltip Style & Item Model private static final int V1_21_2 = 1_21_2; // Data components @@ -98,6 +100,8 @@ public final class VersionHelper { */ public static final boolean IS_CUSTOM_MODEL_DATA = CURRENT_VERSION >= V1_14; + public static final boolean IS_CUSTOM_MODEL_DATA_COMPONENT = CURRENT_VERSION >= V1_21_4; + public static final boolean IS_HEX_VERSION = CURRENT_VERSION >= V1_16; private static List CHEST_INVENTORY_TYPES = null;