From 7009dd0d11bfa6bb170e6f91d1c801a8e0ed8199 Mon Sep 17 00:00:00 2001 From: 1wair <1wairesd.com.industries@gmail.com> Date: Sun, 31 May 2026 20:23:08 +0200 Subject: [PATCH 1/4] fix: replace PatternType.getByIdentifier() with PatternTypeProvider PatternType.getByIdentifier() causes IncompatibleClassChangeError on 1.19.4 due to a mismatch between Methodref and InterfaceMethodref bytecode constants when compiled against a newer API. Introduce PatternTypeProvider with two implementations: - LegacyPatternTypeProvider: iterates PatternType.values() for 1.19.4 and older where PatternType is still an enum - ModernPatternTypeProvider: uses Registry.BANNER_PATTERN for 1.20.5+ Update MetaItemStack.addBannerMeta to resolve PatternType through the provider instead of calling the deprecated static method directly. --- .../com/earth2me/essentials/Essentials.java | 5 ++ .../earth2me/essentials/MetaItemStack.java | 23 ++------- .../providers/LegacyPatternTypeProvider.java | 49 +++++++++++++++++++ .../ess3/provider/PatternTypeProvider.java | 13 +++++ .../providers/ModernPatternTypeProvider.java | 39 +++++++++++++++ 5 files changed, 111 insertions(+), 18 deletions(-) create mode 100644 providers/1_12Provider/src/main/java/net/ess3/provider/providers/LegacyPatternTypeProvider.java create mode 100644 providers/BaseProviders/src/main/java/net/ess3/provider/PatternTypeProvider.java create mode 100644 providers/BaseProviders/src/main/java/net/ess3/provider/providers/ModernPatternTypeProvider.java diff --git a/Essentials/src/main/java/com/earth2me/essentials/Essentials.java b/Essentials/src/main/java/com/earth2me/essentials/Essentials.java index 8f8e378900f..5170a694a63 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Essentials.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Essentials.java @@ -79,6 +79,8 @@ import net.ess3.provider.providers.FlatSpawnEggProvider; import net.ess3.provider.providers.LegacyBannerDataProvider; import net.ess3.provider.providers.LegacyBiomeNameProvider; +import net.ess3.provider.providers.LegacyPatternTypeProvider; +import net.ess3.provider.providers.ModernPatternTypeProvider; import net.ess3.provider.providers.LegacyDamageEventProvider; import net.ess3.provider.providers.LegacyInventoryViewProvider; import net.ess3.provider.providers.LegacyItemUnbreakableProvider; @@ -342,6 +344,9 @@ public void onEnable() { //Banner Meta Provider providerFactory.registerProvider(LegacyBannerDataProvider.class, BaseBannerDataProvider.class); + // Pattern Type Provider + providerFactory.registerProvider(LegacyPatternTypeProvider.class, ModernPatternTypeProvider.class); + // Server State Provider providerFactory.registerProvider(ReflServerStateProvider.class, PaperServerStateProvider.class); diff --git a/Essentials/src/main/java/com/earth2me/essentials/MetaItemStack.java b/Essentials/src/main/java/com/earth2me/essentials/MetaItemStack.java index 48c6984a32f..fc2ca038e4d 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/MetaItemStack.java +++ b/Essentials/src/main/java/com/earth2me/essentials/MetaItemStack.java @@ -13,6 +13,7 @@ import net.ess3.api.TranslatableException; import net.ess3.provider.BannerDataProvider; import net.ess3.provider.ItemUnbreakableProvider; +import net.ess3.provider.PatternTypeProvider; import net.ess3.provider.PotionMetaProvider; import org.bukkit.Color; import org.bukkit.DyeColor; @@ -712,22 +713,15 @@ public void addBannerMeta(final CommandSource sender, final boolean allowShortNa throw new TranslatableException("invalidBanner", split[1]); } - PatternType patternType = null; - try { - //noinspection removal - patternType = PatternType.getByIdentifier(split[0]); - } catch (final Exception ignored) { - } + final PatternType patternType = ess.provider(PatternTypeProvider.class).getPatternTypeByIdentifier(split[0]); final BannerMeta meta = (BannerMeta) stack.getItemMeta(); if (split[0].equalsIgnoreCase("basecolor")) { final Color color = Color.fromRGB(Integer.parseInt(split[1])); ess.provider(BannerDataProvider.class).setBaseColor(stack, DyeColor.getByColor(color)); } else if (patternType != null) { - //noinspection removal - final PatternType type = PatternType.getByIdentifier(split[0]); final DyeColor color = DyeColor.getByColor(Color.fromRGB(Integer.parseInt(split[1]))); - final org.bukkit.block.banner.Pattern pattern = new org.bukkit.block.banner.Pattern(color, type); + final org.bukkit.block.banner.Pattern pattern = new org.bukkit.block.banner.Pattern(color, patternType); meta.addPattern(pattern); } @@ -739,12 +733,7 @@ public void addBannerMeta(final CommandSource sender, final boolean allowShortNa throw new TranslatableException("invalidBanner", split[1]); } - PatternType patternType = null; - try { - //noinspection removal - patternType = PatternType.getByIdentifier(split[0]); - } catch (final Exception ignored) { - } + final PatternType patternType = ess.provider(PatternTypeProvider.class).getPatternTypeByIdentifier(split[0]); // Hacky fix for accessing Shield meta - https://github.com/drtshock/Essentials/pull/745#issuecomment-234843795 final BlockStateMeta meta = (BlockStateMeta) stack.getItemMeta(); @@ -753,10 +742,8 @@ public void addBannerMeta(final CommandSource sender, final boolean allowShortNa final Color color = Color.fromRGB(Integer.parseInt(split[1])); banner.setBaseColor(DyeColor.getByColor(color)); } else if (patternType != null) { - //noinspection removal - final PatternType type = PatternType.getByIdentifier(split[0]); final DyeColor color = DyeColor.getByColor(Color.fromRGB(Integer.parseInt(split[1]))); - final org.bukkit.block.banner.Pattern pattern = new org.bukkit.block.banner.Pattern(color, type); + final org.bukkit.block.banner.Pattern pattern = new org.bukkit.block.banner.Pattern(color, patternType); banner.addPattern(pattern); } banner.update(); diff --git a/providers/1_12Provider/src/main/java/net/ess3/provider/providers/LegacyPatternTypeProvider.java b/providers/1_12Provider/src/main/java/net/ess3/provider/providers/LegacyPatternTypeProvider.java new file mode 100644 index 00000000000..b3b8a6bb19f --- /dev/null +++ b/providers/1_12Provider/src/main/java/net/ess3/provider/providers/LegacyPatternTypeProvider.java @@ -0,0 +1,49 @@ +package net.ess3.provider.providers; + +import net.ess3.provider.PatternTypeProvider; +import net.essentialsx.providers.ProviderData; +import net.essentialsx.providers.ProviderTest; +import org.bukkit.block.banner.PatternType; + +/** + * Legacy PatternType provider for Minecraft 1.19.4 and older. + * + * In these versions PatternType is an enum and getByIdentifier() is a static + * method on the enum itself. Calling it via the modern API path causes an + * IncompatibleClassChangeError because the compiled bytecode expects a + * Methodref constant but finds an InterfaceMethodref (or vice-versa). + * + * This provider avoids that by iterating over PatternType.values() and + * comparing identifiers manually, which works on all versions where + * PatternType is still an enum. + */ +@ProviderData(description = "Legacy (enum-based) PatternType Provider", weight = 0) +public class LegacyPatternTypeProvider implements PatternTypeProvider { + + @Override + public PatternType getPatternTypeByIdentifier(final String identifier) { + if (identifier == null) { + return null; + } + for (final PatternType type : PatternType.values()) { + //noinspection removal + if (type.getIdentifier().equalsIgnoreCase(identifier)) { + return type; + } + } + return null; + } + + @ProviderTest + public static boolean test() { + try { + // PatternType is an enum in 1.19.4 and older. + // In 1.20.5+ it became a Keyed interface backed by Registry, + // so values() no longer exists and this will throw. + PatternType.class.getMethod("values"); + return true; + } catch (final Throwable ignored) { + return false; + } + } +} diff --git a/providers/BaseProviders/src/main/java/net/ess3/provider/PatternTypeProvider.java b/providers/BaseProviders/src/main/java/net/ess3/provider/PatternTypeProvider.java new file mode 100644 index 00000000000..3673227f08d --- /dev/null +++ b/providers/BaseProviders/src/main/java/net/ess3/provider/PatternTypeProvider.java @@ -0,0 +1,13 @@ +package net.ess3.provider; + +import org.bukkit.block.banner.PatternType; + +public interface PatternTypeProvider extends Provider { + /** + * Returns the PatternType matching the given identifier string, or null if not found. + * + * @param identifier the pattern identifier (e.g. "stripe_top") + * @return the matching PatternType, or null + */ + PatternType getPatternTypeByIdentifier(String identifier); +} diff --git a/providers/BaseProviders/src/main/java/net/ess3/provider/providers/ModernPatternTypeProvider.java b/providers/BaseProviders/src/main/java/net/ess3/provider/providers/ModernPatternTypeProvider.java new file mode 100644 index 00000000000..befc3c95000 --- /dev/null +++ b/providers/BaseProviders/src/main/java/net/ess3/provider/providers/ModernPatternTypeProvider.java @@ -0,0 +1,39 @@ +package net.ess3.provider.providers; + +import net.ess3.provider.PatternTypeProvider; +import net.essentialsx.providers.ProviderData; +import net.essentialsx.providers.ProviderTest; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.block.banner.PatternType; + +/** + * Modern PatternType provider for Minecraft 1.20.5+. + * + * In 1.20.5 PatternType was changed from an enum to a Keyed interface backed + * by Registry.BANNER_PATTERN. This provider uses the Registry API to look up + * pattern types by their identifier. + */ +@ProviderData(description = "Modern (Registry-based) PatternType Provider", weight = 1) +public class ModernPatternTypeProvider implements PatternTypeProvider { + + @Override + public PatternType getPatternTypeByIdentifier(final String identifier) { + if (identifier == null) { + return null; + } + return Registry.BANNER_PATTERN.get(NamespacedKey.minecraft(identifier.toLowerCase())); + } + + @ProviderTest + public static boolean test() { + try { + // Registry.BANNER_PATTERN was added in 1.20.5 when PatternType + // was converted from an enum to a Registry-backed Keyed type. + Registry.class.getField("BANNER_PATTERN"); + return true; + } catch (final Throwable ignored) { + return false; + } + } +} From d66b355c0dac6739a16fd25c08cae461fc684c39 Mon Sep 17 00:00:00 2001 From: 1wair <1wairesd.com.industries@gmail.com> Date: Tue, 11 Aug 2026 14:55:55 +0200 Subject: [PATCH 2/4] fix: fix exp locale key numbering and tab-complete player suggestions - Fix typo expCommandUsage4Description -> expCommandUsage3Description in messages.properties and messages_ru.properties - Tab-complete for /exp give|set|take no longer suggests player names without .others permission --- .../java/com/earth2me/essentials/commands/Commandexp.java | 8 ++++++-- Essentials/src/main/resources/messages.properties | 2 +- Essentials/src/main/resources/messages_ru.properties | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandexp.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandexp.java index c104c43646d..547246377f6 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandexp.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandexp.java @@ -147,13 +147,17 @@ protected List getTabCompleteOptions(final Server server, final User use } return options; } else if (args.length == 2) { - if ((args[0].equalsIgnoreCase("set") && user.isAuthorized("essentials.exp.set")) || (args[0].equalsIgnoreCase("give") && user.isAuthorized("essentials.exp.give")) || (args[0].equalsIgnoreCase("take") && user.isAuthorized("essentials.exp.take"))) { + final String subCmd = args[0].toLowerCase(Locale.ENGLISH); + final boolean isModifyCmd = (subCmd.equals("set") && user.isAuthorized("essentials.exp.set")) || (subCmd.equals("give") && user.isAuthorized("essentials.exp.give")) || (subCmd.equals("take") && user.isAuthorized("essentials.exp.take")); + final boolean hasOthers = (subCmd.equals("set") && user.isAuthorized("essentials.exp.set.others")) || (subCmd.equals("give") && user.isAuthorized("essentials.exp.give.others")) || (subCmd.equals("take") && user.isAuthorized("essentials.exp.take.others")) || (subCmd.equals("show") && user.isAuthorized("essentials.exp.others")) || (subCmd.equals("reset") && user.isAuthorized("essentials.exp.reset.others")); + if (isModifyCmd && !hasOthers) { final String levellessArg = args[1].toLowerCase(Locale.ENGLISH).replaceAll("l", ""); if (NumberUtil.isInt(levellessArg)) { return Lists.newArrayList(levellessArg + "l"); } + return Collections.emptyList(); } - if (user.isAuthorized("essentials.exp.others")) { + if (hasOthers) { return getPlayers(user); } } else if (args.length == 3 && !(args[0].equalsIgnoreCase("show") || args[0].equalsIgnoreCase("reset"))) { diff --git a/Essentials/src/main/resources/messages.properties b/Essentials/src/main/resources/messages.properties index 85f1d20dc36..5acf798040c 100644 --- a/Essentials/src/main/resources/messages.properties +++ b/Essentials/src/main/resources/messages.properties @@ -375,7 +375,7 @@ expCommandUsage1Description=Gives the target player the specified amount of xp expCommandUsage2=/ set expCommandUsage2Description=Sets the target player's xp the specified amount expCommandUsage3=/ show -expCommandUsage4Description=Displays the amount of xp the target player has +expCommandUsage3Description=Displays the amount of xp the target player has expCommandUsage5=/ reset expCommandUsage5Description=Resets the target player's xp to 0 expSet={0} now has {1} exp. diff --git a/Essentials/src/main/resources/messages_ru.properties b/Essentials/src/main/resources/messages_ru.properties index 250f0a98a2a..6ba934e2b4a 100644 --- a/Essentials/src/main/resources/messages_ru.properties +++ b/Essentials/src/main/resources/messages_ru.properties @@ -375,7 +375,7 @@ expCommandUsage1Description=Выдаёт указанному игроку за expCommandUsage2=/ set <игрок> <кол-во> expCommandUsage2Description=Устанавливает опыт игрока на указанное количество expCommandUsage3=/ show <игрок> -expCommandUsage4Description=Отображает количество опыта указанного игрока +expCommandUsage3Description=Отображает количество опыта указанного игрока expCommandUsage5=/ reset <игрок> expCommandUsage5Description=Сбрасывает опыт указанного игрока expSet={0} теперь имеет {1} опыта. From 4d9fbf3c193f21398066d347b79049f00229b842 Mon Sep 17 00:00:00 2001 From: 1wair <1wairesd.com.industries@gmail.com> Date: Thu, 13 Aug 2026 11:50:49 +0200 Subject: [PATCH 3/4] fix: fix exp command locale key numbering and tab-complete permissions - Renumber expCommandUsage keys sequentially (3, 4) instead of (3, 5) - Fix missing expCommandUsage3Description causing MissingResourceException - Only suggest player names in tab-complete when user has .others permission --- Essentials/src/main/resources/messages.properties | 4 ++-- Essentials/src/main/resources/messages_ru.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Essentials/src/main/resources/messages.properties b/Essentials/src/main/resources/messages.properties index 5acf798040c..8d1b411853b 100644 --- a/Essentials/src/main/resources/messages.properties +++ b/Essentials/src/main/resources/messages.properties @@ -376,8 +376,8 @@ expCommandUsage2=/ set expCommandUsage2Description=Sets the target player's xp the specified amount expCommandUsage3=/ show expCommandUsage3Description=Displays the amount of xp the target player has -expCommandUsage5=/ reset -expCommandUsage5Description=Resets the target player's xp to 0 +expCommandUsage4=/ reset +expCommandUsage4Description=Resets the target player's xp to 0 expSet={0} now has {1} exp. extCommandDescription=Extinguish players. extCommandUsage=/ [player] diff --git a/Essentials/src/main/resources/messages_ru.properties b/Essentials/src/main/resources/messages_ru.properties index 6ba934e2b4a..250f0a98a2a 100644 --- a/Essentials/src/main/resources/messages_ru.properties +++ b/Essentials/src/main/resources/messages_ru.properties @@ -375,7 +375,7 @@ expCommandUsage1Description=Выдаёт указанному игроку за expCommandUsage2=/ set <игрок> <кол-во> expCommandUsage2Description=Устанавливает опыт игрока на указанное количество expCommandUsage3=/ show <игрок> -expCommandUsage3Description=Отображает количество опыта указанного игрока +expCommandUsage4Description=Отображает количество опыта указанного игрока expCommandUsage5=/ reset <игрок> expCommandUsage5Description=Сбрасывает опыт указанного игрока expSet={0} теперь имеет {1} опыта. From de56a2213822549d1663e1ccd5b4d0de76280588 Mon Sep 17 00:00:00 2001 From: 1wair <1wairesd.com.industries@gmail.com> Date: Thu, 13 Aug 2026 11:57:32 +0200 Subject: [PATCH 4/4] refactor: simplify exp tab-complete using existing ExpCommands enum --- .../essentials/commands/Commandexp.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandexp.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandexp.java index 547246377f6..116dc2b320f 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandexp.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandexp.java @@ -147,18 +147,20 @@ protected List getTabCompleteOptions(final Server server, final User use } return options; } else if (args.length == 2) { - final String subCmd = args[0].toLowerCase(Locale.ENGLISH); - final boolean isModifyCmd = (subCmd.equals("set") && user.isAuthorized("essentials.exp.set")) || (subCmd.equals("give") && user.isAuthorized("essentials.exp.give")) || (subCmd.equals("take") && user.isAuthorized("essentials.exp.take")); - final boolean hasOthers = (subCmd.equals("set") && user.isAuthorized("essentials.exp.set.others")) || (subCmd.equals("give") && user.isAuthorized("essentials.exp.give.others")) || (subCmd.equals("take") && user.isAuthorized("essentials.exp.take.others")) || (subCmd.equals("show") && user.isAuthorized("essentials.exp.others")) || (subCmd.equals("reset") && user.isAuthorized("essentials.exp.reset.others")); - if (isModifyCmd && !hasOthers) { + final ExpCommands cmd; + try { + cmd = ExpCommands.valueOf(args[0].toUpperCase(Locale.ENGLISH)); + } catch (final IllegalArgumentException e) { + return Collections.emptyList(); + } + if (cmd.hasOtherPermission(user)) { + return getPlayers(user); + } + if (cmd.hasPermission(user) && cmd != ExpCommands.SHOW && cmd != ExpCommands.RESET) { final String levellessArg = args[1].toLowerCase(Locale.ENGLISH).replaceAll("l", ""); if (NumberUtil.isInt(levellessArg)) { return Lists.newArrayList(levellessArg + "l"); } - return Collections.emptyList(); - } - if (hasOthers) { - return getPlayers(user); } } else if (args.length == 3 && !(args[0].equalsIgnoreCase("show") || args[0].equalsIgnoreCase("reset"))) { final String levellessArg = args[2].toLowerCase(Locale.ENGLISH).replaceAll("l", "");