From e5c9074b7c5ff77f6bf7a6b938bda46feb8e332d Mon Sep 17 00:00:00 2001 From: Ville Blixt <25557150+md5nake@users.noreply.github.com> Date: Sat, 7 Feb 2026 06:14:10 +0100 Subject: [PATCH 01/37] Fix preventing joins when server isn't actually full (#6426) Fixes #6424 Caused by https://github.com/PaperMC/Paper/pull/13601 --- .../java/com/earth2me/essentials/EssentialsPlayerListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java index 84e4baaf96c..8fc09fdcb20 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java @@ -666,7 +666,7 @@ public void onPlayerListFull(final PlayerServerFullCheckEvent event) { return; } - if (ess.getSettings().isCustomServerFullMessage()) { + if (!event.isAllowed() && ess.getSettings().isCustomServerFullMessage()) { PaperAdventureSmuggler.smugglePlayerServerFullCheckEvent(event, AdventureUtil.miniToLegacy(tlLiteral("serverFull"))); } } From 6ab56d2fe8546c01f30b6cadba68291a8246a45e Mon Sep 17 00:00:00 2001 From: TiagoFar78 <42421954+TiagoFar78@users.noreply.github.com> Date: Sat, 7 Feb 2026 05:50:39 +0000 Subject: [PATCH 02/37] Implement kits placing items in specific slots (#5794) Closes #5535 --------- Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com> --- .../java/com/earth2me/essentials/Kit.java | 39 +++++++++++++++---- .../essentials/commands/Commandcreatekit.java | 7 ++-- .../essentials/craftbukkit/Inventories.java | 29 ++++++++++++++ 3 files changed, 65 insertions(+), 10 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/Kit.java b/Essentials/src/main/java/com/earth2me/essentials/Kit.java index e32ade33e33..9213890a633 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Kit.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Kit.java @@ -26,6 +26,7 @@ import java.util.GregorianCalendar; import java.util.List; import java.util.Map; +import java.util.HashMap; import java.util.logging.Level; import static com.earth2me.essentials.I18n.tlLiteral; @@ -174,10 +175,11 @@ public boolean expandItems(final User user, final List items) throws Exc final boolean allowUnsafe = ess.getSettings().allowUnsafeEnchantments(); final boolean autoEquip = ess.getSettings().isKitAutoEquip(); final List itemList = new ArrayList<>(); + final Map itemsWithSlot = new HashMap<>(); final List commandQueue = new ArrayList<>(); final List moneyQueue = new ArrayList<>(); final String currencySymbol = ess.getSettings().getCurrencySymbol().isEmpty() ? "$" : ess.getSettings().getCurrencySymbol(); - for (final String kitItem : output.getLines()) { + for (String kitItem : output.getLines()) { if (kitItem.startsWith("$") || kitItem.startsWith(currencySymbol)) { moneyQueue.add(NumberUtil.sanitizeCurrencyString(kitItem, ess)); continue; @@ -191,6 +193,16 @@ public boolean expandItems(final User user, final List items) throws Exc continue; } + int itemSlot = -1; + if (kitItem.startsWith("slot:")) { + final int spaceIndex = kitItem.indexOf(" "); + if (spaceIndex != -1) { + final String slotStr = kitItem.substring("slot:".length(), spaceIndex); + itemSlot = NumberUtil.isInt(slotStr) ? Integer.parseInt(slotStr) : -1; + kitItem = kitItem.substring(spaceIndex + 1); + } + } + final ItemStack stack; final SerializationProvider serializationProvider = ess.provider(SerializationProvider.class); @@ -218,22 +230,35 @@ public boolean expandItems(final User user, final List items) throws Exc stack = metaStack.getItemStack(); } - itemList.add(stack); + if (itemSlot == -1 || itemsWithSlot.containsKey(itemSlot)) { + itemList.add(stack); + } else { + itemsWithSlot.put(itemSlot, stack); + } } final int maxStackSize = user.isAuthorized("essentials.oversizedstacks") ? ess.getSettings().getOversizedStackSize() : 0; final boolean isDropItemsIfFull = ess.getSettings().isDropItemsIfFull(); - final KitPreExpandItemsEvent itemsEvent = new KitPreExpandItemsEvent(user, kitName, itemList); - Bukkit.getPluginManager().callEvent(itemsEvent); + final List totalItems = new ArrayList<>(itemList); + totalItems.addAll(itemsWithSlot.values()); - final ItemStack[] itemArray = itemList.toArray(new ItemStack[0]); - - if (!isDropItemsIfFull && !Inventories.hasSpace(user.getBase(), maxStackSize, autoEquip, itemArray)) { + final KitPreExpandItemsEvent itemsEvent = new KitPreExpandItemsEvent(user, kitName, totalItems); + Bukkit.getPluginManager().callEvent(itemsEvent); + final ItemStack[] totalItemsArray = totalItems.toArray(new ItemStack[0]); + if (!isDropItemsIfFull && !Inventories.hasSpace(user.getBase(), maxStackSize, autoEquip, totalItemsArray)) { user.sendTl("kitInvFullNoDrop"); return false; } + for (Map.Entry itemWithSlot : itemsWithSlot.entrySet()) { + final ItemStack leftover = Inventories.addItem(user.getBase(), maxStackSize, itemWithSlot.getValue(), itemWithSlot.getKey()); + if (leftover != null) { + itemList.add(leftover); + } + } + + final ItemStack[] itemArray = itemList.toArray(new ItemStack[0]); final Map leftover = Inventories.addItem(user.getBase(), maxStackSize, autoEquip, itemArray); if (!isDropItemsIfFull && !leftover.isEmpty()) { // Inventories#hasSpace should prevent this state from EVER being reached; If it does, something has gone terribly wrong, and we should just give up and hope people report it :( diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandcreatekit.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandcreatekit.java index 7325a75b770..0c5189f82ba 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandcreatekit.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandcreatekit.java @@ -51,13 +51,14 @@ public void run(final Server server, final User user, final String commandLabel, useSerializationProvider = false; } - for (ItemStack is : items) { + for (int i = 0; i < items.length; i++) { + final ItemStack is = items[i]; if (is != null && is.getType() != null && is.getType() != Material.AIR) { final String serialized; if (useSerializationProvider) { - serialized = "@" + Base64Coder.encodeLines(serializationProvider.serializeItem(is)); + serialized = "slot:" + i + " @" + Base64Coder.encodeLines(serializationProvider.serializeItem(is)); } else { - serialized = ess.getItemDb().serialize(is); + serialized = "slot:" + i + " " + ess.getItemDb().serialize(is); } list.add(serialized); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/craftbukkit/Inventories.java b/Essentials/src/main/java/com/earth2me/essentials/craftbukkit/Inventories.java index db754a19426..fe25564f23a 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/craftbukkit/Inventories.java +++ b/Essentials/src/main/java/com/earth2me/essentials/craftbukkit/Inventories.java @@ -142,6 +142,35 @@ public static Map addItem(final Player player, final int max return addItem(player, maxStack, false, items); } + public static ItemStack addItem(final Player player, final int maxStack, ItemStack item, int slot) { + final int itemMax = Math.max(maxStack, item.getMaxStackSize()); + + ItemStack existing = player.getInventory().getItem(slot); + final int existingAmount; + if (isEmpty(existing)) { + existing = item.clone(); + existingAmount = 0; + } else { + existingAmount = existing.getAmount(); + } + + if (!item.isSimilar(existing)) { + return item; + } + + final int amount = item.getAmount(); + if (amount + existingAmount <= itemMax) { + existing.setAmount(amount + existingAmount); + player.getInventory().setItem(slot, existing); + return null; + } + + existing.setAmount(itemMax); + player.getInventory().setItem(slot, existing); + item.setAmount(amount + existingAmount - itemMax); + return item; + } + public static Map addItem(final Player player, final int maxStack, final boolean allowArmor, ItemStack... items) { items = normalizeItems(cloneItems(items)); final Map leftover = new HashMap<>(); From 787f539921ac4a573c5362973e1093b7cc051091 Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Mon, 9 Feb 2026 20:38:47 +0100 Subject: [PATCH 03/37] Prevent NPE in getPlayerLocale for offline players (#6435) I hope this message finds you well. This pull requests prevents an NPE in getPlayerLocale for offline players. Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com> --- Essentials/src/main/java/com/earth2me/essentials/User.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/User.java b/Essentials/src/main/java/com/earth2me/essentials/User.java index e0b7cd40276..2a34edc3bca 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/User.java +++ b/Essentials/src/main/java/com/earth2me/essentials/User.java @@ -841,7 +841,7 @@ public void checkActivity() { && !isAuthorized("essentials.afk.kickexempt")) { lastActivity = 0; final double kickTime = autoafktimeout / 60.0; - + // If `afk-timeout-command` in config.yml is empty, use default Essentials kicking behaviour instead of executing a command. if (ess.getSettings().getAfkTimeoutCommands().isEmpty()) { this.getBase().kickPlayer(AdventureUtil.miniToLegacy(playerTl("autoAfkKickReason", kickTime))); @@ -1112,7 +1112,7 @@ public String tlSender(String tlKey, Object... args) { } public Locale getPlayerLocale(final String locale) { - if (locale.equals(lastLocaleString)) { + if (locale == null || locale.equals(lastLocaleString)) { return playerLocale; } lastLocaleString = locale; From d16aec152594038d4913676002861fae33e83e8a Mon Sep 17 00:00:00 2001 From: Vishwas Kumar Date: Sat, 21 Feb 2026 14:15:17 +0530 Subject: [PATCH 04/37] Fixed unreachable nickNoMore Message in /nick command. Not a huge change. (#6442) The display name was comparing getDisplayName() to itself, making it always true which made the nickNoMore message unreachable. Now it uses FormatUtil.stripFormat() to strip color codes before comparing against getName(), also has a null-safety guard. I have built it locally and tested it - it works well. Verified: "Nick set normally", "Nick equal to Username", "Colored nick equal to Username". Had no Errors. --- .../java/com/earth2me/essentials/commands/Commandnick.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnick.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnick.java index 9716816e126..a2c3f364ded 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnick.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnick.java @@ -48,7 +48,8 @@ protected void updatePlayer(final Server server, final CommandSource sender, fin target.sendTl("nickNoMore"); } else if (target.getName().equalsIgnoreCase(nick)) { setNickname(server, sender, target, nick); - if (!target.getDisplayName().equalsIgnoreCase(target.getDisplayName())) { + final String strippedDisplay = FormatUtil.stripFormat(target.getDisplayName()); + if (strippedDisplay != null && !strippedDisplay.equalsIgnoreCase(target.getName())) { target.sendTl("nickNoMore"); } target.sendTl("nickSet", ess.getSettings().changeDisplayName() ? target.getDisplayName() : nick); From bc6acfe1dc33d3bf0d06b2cdaf1db2b4a411fb38 Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Sat, 21 Feb 2026 13:13:48 -0800 Subject: [PATCH 05/37] Command Trees v2 (#6230) My take on command trees which fit the vibe of our codebase better than #5384 imo. Closes #5384 --- .../essentials/commands/Commandafk.java | 2 +- .../essentials/commands/Commandback.java | 2 +- .../essentials/commands/Commandbalance.java | 2 +- .../essentials/commands/Commandban.java | 2 +- .../essentials/commands/Commandbanip.java | 2 +- .../essentials/commands/Commandbook.java | 2 +- .../essentials/commands/Commandburn.java | 2 +- .../commands/Commandclearinventory.java | 4 +- .../essentials/commands/Commanddelhome.java | 2 +- .../essentials/commands/Commandeco.java | 2 +- .../commands/Commandenderchest.java | 2 +- .../commands/Commandessentials.java | 975 +----------------- .../essentials/commands/Commandexp.java | 6 +- .../essentials/commands/Commandext.java | 2 +- .../essentials/commands/Commandfeed.java | 2 +- .../essentials/commands/Commandgamemode.java | 8 +- .../essentials/commands/Commandgetpos.java | 2 +- .../essentials/commands/Commandgive.java | 2 +- .../essentials/commands/Commandheal.java | 2 +- .../essentials/commands/Commandhome.java | 2 +- .../essentials/commands/Commandice.java | 2 +- .../essentials/commands/Commandignore.java | 2 +- .../essentials/commands/Commandinvsee.java | 2 +- .../essentials/commands/Commandkick.java | 2 +- .../essentials/commands/Commandkill.java | 2 +- .../essentials/commands/Commandkit.java | 4 +- .../essentials/commands/Commandkitreset.java | 2 +- .../essentials/commands/Commandlightning.java | 2 +- .../essentials/commands/Commandmail.java | 4 +- .../essentials/commands/Commandmsg.java | 2 +- .../essentials/commands/Commandmute.java | 2 +- .../essentials/commands/Commandnear.java | 4 +- .../essentials/commands/Commandnick.java | 2 +- .../essentials/commands/Commandnuke.java | 2 +- .../essentials/commands/Commandpay.java | 2 +- .../essentials/commands/Commandplaytime.java | 8 +- .../essentials/commands/Commandpowertool.java | 2 +- .../essentials/commands/Commandptime.java | 2 +- .../essentials/commands/Commandpweather.java | 2 +- .../commands/Commandrenamehome.java | 2 +- .../essentials/commands/Commandrest.java | 2 +- .../essentials/commands/Commandseen.java | 2 +- .../essentials/commands/Commandskull.java | 4 +- .../essentials/commands/Commandspeed.java | 2 +- .../essentials/commands/Commandtempban.java | 2 +- .../essentials/commands/Commandtempbanip.java | 2 +- .../commands/Commandtogglejail.java | 2 +- .../essentials/commands/Commandtp.java | 4 +- .../essentials/commands/Commandtpa.java | 2 +- .../essentials/commands/Commandtpaall.java | 2 +- .../essentials/commands/Commandtpahere.java | 2 +- .../essentials/commands/Commandtpall.java | 2 +- .../essentials/commands/Commandtphere.java | 2 +- .../essentials/commands/Commandtpo.java | 2 +- .../essentials/commands/Commandtpohere.java | 2 +- .../essentials/commands/Commandtppos.java | 2 +- .../essentials/commands/Commandtpr.java | 2 +- .../essentials/commands/Commandwarp.java | 4 +- .../essentials/commands/Commandwhois.java | 2 +- .../commands/EssentialsCommand.java | 16 +- .../commands/EssentialsLoopCommand.java | 8 +- .../commands/EssentialsToggleCommand.java | 4 +- .../commands/EssentialsTreeCommand.java | 94 ++ .../commands/EssentialsTreeNode.java | 62 ++ .../commands/essentials/CleanupCommand.java | 86 ++ .../essentials/CommandMapCommand.java | 25 + .../commands/essentials/DebugCommand.java | 42 + .../commands/essentials/DumpCommand.java | 289 ++++++ .../commands/essentials/HomesCommand.java | 106 ++ .../commands/essentials/ItemTestCommand.java | 65 ++ .../commands/essentials/MooCommand.java | 51 + .../commands/essentials/NyanCommand.java | 90 ++ .../commands/essentials/ReloadCommand.java | 17 + .../commands/essentials/UsermapCommand.java | 107 ++ .../commands/essentials/VersionCommand.java | 155 +++ .../essentials/utils/VersionUtil.java | 22 + .../essentials/spawn/Commandspawn.java | 2 +- 77 files changed, 1317 insertions(+), 1045 deletions(-) create mode 100644 Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsTreeCommand.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsTreeNode.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/commands/essentials/CleanupCommand.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/commands/essentials/CommandMapCommand.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DebugCommand.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DumpCommand.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/commands/essentials/HomesCommand.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/commands/essentials/ItemTestCommand.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/commands/essentials/MooCommand.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/commands/essentials/NyanCommand.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/commands/essentials/ReloadCommand.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/commands/essentials/UsermapCommand.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandafk.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandafk.java index 51ec11841f8..95ac1809169 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandafk.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandafk.java @@ -106,7 +106,7 @@ private void toggleAfk(final User sender, final User user, final String message) @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1 && sender.isAuthorized("essentials.afk.others")) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandback.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandback.java index a8894912746..32345b48eba 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandback.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandback.java @@ -77,7 +77,7 @@ private void teleportBack(final CommandSource sender, final User user, final Str @Override protected List getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) { if (user.isAuthorized("essentials.back.others") && args.length == 1) { - return getPlayers(server, user); + return getPlayers(user); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalance.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalance.java index 75834c345a7..ab597c640de 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalance.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalance.java @@ -39,7 +39,7 @@ public void run(final Server server, final User user, final String commandLabel, @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1 && sender.isAuthorized("essentials.balance.others")) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandban.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandban.java index 633140c6340..c0c16d397d5 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandban.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandban.java @@ -68,7 +68,7 @@ public void run(final Server server, final CommandSource sender, final String co @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbanip.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbanip.java index a9cc79ed1ad..f0eef7be0f6 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbanip.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbanip.java @@ -70,7 +70,7 @@ public void run(final Server server, final CommandSource sender, final String co protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { // TODO: Also list IP addresses? - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbook.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbook.java index d7fd13ec040..1df69caf58d 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbook.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbook.java @@ -106,7 +106,7 @@ protected List getTabCompleteOptions(final Server server, final User use } return options; } else if (args.length == 2 && args[0].equalsIgnoreCase("author") && user.isAuthorized("essentials.book.author")) { - final List options = getPlayers(server, user); + final List options = getPlayers(user); options.add("Herobrine"); // #EasterEgg return options; } else { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandburn.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandburn.java index 0455e2dad36..e3f90303189 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandburn.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandburn.java @@ -26,7 +26,7 @@ protected void run(final Server server, final CommandSource sender, final String @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else if (args.length == 2) { return COMMON_DURATIONS; } else { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandclearinventory.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandclearinventory.java index c3b19adb8ff..abe54678e39 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandclearinventory.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandclearinventory.java @@ -153,7 +153,7 @@ protected void clearHandler(final CommandSource sender, final Player player, fin protected List getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) { if (user.isAuthorized("essentials.clearinventory.others")) { if (args.length == 1) { - final List options = getPlayers(server, user); + final List options = getPlayers(user); if (user.isAuthorized("essentials.clearinventory.all") || user.isAuthorized("essentials.clearinventory.multiple")) { // Assume that nobody will have the 'all' permission without the 'others' permission options.add("*"); @@ -182,7 +182,7 @@ protected List getTabCompleteOptions(final Server server, final User use @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - final List options = getPlayers(server, sender); + final List options = getPlayers(sender); options.add("*"); return options; } else if (args.length == 2) { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commanddelhome.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commanddelhome.java index 20500ef45d2..e1b9dab0077 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commanddelhome.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commanddelhome.java @@ -87,7 +87,7 @@ protected List getTabCompleteOptions(final Server server, final CommandS if (canDelOthers) { final int sepIndex = args[0].indexOf(':'); if (sepIndex < 0) { - getPlayers(server, sender).forEach(player -> homes.add(player + ":")); + getPlayers(sender).forEach(player -> homes.add(player + ":")); } else { final String namePart = args[0].substring(0, sepIndex); final User otherUser; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandeco.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandeco.java index d8576a475dd..3517f69937f 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandeco.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandeco.java @@ -87,7 +87,7 @@ protected List getTabCompleteOptions(final Server server, final CommandS } return options; } else if (args.length == 2) { - return getPlayers(server, sender); + return getPlayers(sender); } else if (args.length == 3 && !args[0].equalsIgnoreCase(EcoCommands.RESET.name())) { if (args[0].equalsIgnoreCase(EcoCommands.SET.name())) { return Lists.newArrayList("0", ess.getSettings().getStartingBalance().toString()); diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandenderchest.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandenderchest.java index 478e7209236..51c46a394d3 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandenderchest.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandenderchest.java @@ -26,7 +26,7 @@ protected void run(final Server server, final User user, final String commandLab @Override protected List getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) { if (args.length == 1 && user.isAuthorized("essentials.enderchest.others")) { - return getPlayers(server, user); + return getPlayers(user); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandessentials.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandessentials.java index 1eeb6d20b27..74070bb43c6 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandessentials.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandessentials.java @@ -1,962 +1,29 @@ package com.earth2me.essentials.commands; -import com.earth2me.essentials.CommandSource; -import com.earth2me.essentials.EssentialsUpgrade; -import com.earth2me.essentials.User; -import com.earth2me.essentials.craftbukkit.Inventories; -import com.earth2me.essentials.economy.EconomyLayer; -import com.earth2me.essentials.economy.EconomyLayers; -import com.earth2me.essentials.userstorage.ModernUserMap; -import com.earth2me.essentials.utils.AdventureUtil; -import com.earth2me.essentials.utils.CommandMapUtil; -import com.earth2me.essentials.utils.DateUtil; -import com.earth2me.essentials.utils.FloatUtil; -import com.earth2me.essentials.utils.NumberUtil; -import com.earth2me.essentials.utils.PasteUtil; -import com.earth2me.essentials.utils.RegistryUtil; -import com.earth2me.essentials.utils.VersionUtil; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; -import com.google.gson.JsonArray; -import com.google.gson.JsonNull; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import net.ess3.api.TranslatableException; -import net.ess3.provider.KnownCommandsProvider; -import net.ess3.provider.OnlineModeProvider; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Server; -import org.bukkit.Sound; -import org.bukkit.World; -import org.bukkit.command.Command; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.PluginDescriptionFile; -import org.bukkit.plugin.PluginManager; -import org.bukkit.scheduler.BukkitRunnable; - -import java.io.File; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.function.Supplier; -import java.util.logging.Level; -import java.util.stream.Collectors; - -import static com.earth2me.essentials.I18n.tlLiteral; +import com.earth2me.essentials.commands.essentials.CleanupCommand; +import com.earth2me.essentials.commands.essentials.CommandMapCommand; +import com.earth2me.essentials.commands.essentials.DebugCommand; +import com.earth2me.essentials.commands.essentials.DumpCommand; +import com.earth2me.essentials.commands.essentials.HomesCommand; +import com.earth2me.essentials.commands.essentials.ItemTestCommand; +import com.earth2me.essentials.commands.essentials.MooCommand; +import com.earth2me.essentials.commands.essentials.NyanCommand; +import com.earth2me.essentials.commands.essentials.UsermapCommand; +import com.earth2me.essentials.commands.essentials.VersionCommand; // This command has 4 undocumented behaviours #EasterEgg -public class Commandessentials extends EssentialsCommand { - - private static final Sound NOTE_HARP = RegistryUtil.valueOf(Sound.class, "BLOCK_NOTE_BLOCK_HARP", "BLOCK_NOTE_HARP", "NOTE_PIANO"); - private static final Sound MOO_SOUND = RegistryUtil.valueOf(Sound.class, "COW_IDLE", "ENTITY_COW_MILK"); - - private static final String HOMES_USAGE = "/ homes (fix | delete [world])"; - - private static final String NYAN_TUNE = "1D#,1E,2F#,,2A#,1E,1D#,1E,2F#,2B,2D#,2E,2D#,2A#,2B,,2F#,,1D#,1E,2F#,2B,2C#,2A#,2B,2C#,2E,2D#,2E,2C#,,2F#,,2G#,,1D,1D#,,1C#,1D,1C#,1B,,1B,,1C#,,1D,,1D,1C#,1B,1C#,1D#,2F#,2G#,1D#,2F#,1C#,1D#,1B,1C#,1B,1D#,,2F#,,2G#,1D#,2F#,1C#,1D#,1B,1D,1D#,1D,1C#,1B,1C#,1D,,1B,1C#,1D#,2F#,1C#,1D,1C#,1B,1C#,,1B,,1C#,,2F#,,2G#,,1D,1D#,,1C#,1D,1C#,1B,,1B,,1C#,,1D,,1D,1C#,1B,1C#,1D#,2F#,2G#,1D#,2F#,1C#,1D#,1B,1C#,1B,1D#,,2F#,,2G#,1D#,2F#,1C#,1D#,1B,1D,1D#,1D,1C#,1B,1C#,1D,,1B,1C#,1D#,2F#,1C#,1D,1C#,1B,1C#,,1B,,1B,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1B,,"; - private static final String[] CONSOLE_MOO = new String[] {" (__)", " (oo)", " /------\\/", " / | ||", " * /\\---/\\", " ~~ ~~", "....\"Have you mooed today?\"..."}; - private static final String[] PLAYER_MOO = new String[] {" (__)", " (oo)", " /------\\/", " / | | |", " * /\\---/\\", " ~~ ~~", "....\"Have you mooed today?\"..."}; - private static final List versionPlugins = Arrays.asList( - "Vault", // API - "Reserve", // API - "PlaceholderAPI", // API - "CMI", // potential for issues - "Towny", // past issues; admins should ensure latest - "ChestShop", // past issues; admins should ensure latest - "Citizens", // fires player events - "LuckPerms", // permissions (recommended) - "UltraPermissions", - "PermissionsEx", // permissions (unsupported) - "GroupManager", // permissions (unsupported) - "bPermissions", // permissions (unsupported) - "DiscordSRV", // potential for issues if EssentialsXDiscord is installed - - // Chat signing bypass plugins that can potentially break EssentialsChat - "AntiPopup", - "NoChatReports", - "NoEncryption" - ); - private static final List officialPlugins = Arrays.asList( - "EssentialsAntiBuild", - "EssentialsChat", - "EssentialsDiscord", - "EssentialsDiscordLink", - "EssentialsGeoIP", - "EssentialsProtect", - "EssentialsSpawn", - "EssentialsXMPP" - ); - private static final List warnPlugins = Arrays.asList( - "PermissionsEx", - "GroupManager", - "bPermissions", - - // Brain-dead chat signing bypass that break EssentialsChat - "NoChatReports", - "NoEncryption" - ); - private transient TuneRunnable currentTune = null; - +public class Commandessentials extends EssentialsTreeCommand { public Commandessentials() { super("essentials"); - } - - @Override - public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception { - if (args.length == 0) { - showUsage(sender); - } - - switch (args[0]) { - // Info commands - case "debug": - case "verbose": - runDebug(server, sender, commandLabel, args); - break; - case "ver": - case "version": - runVersion(server, sender, commandLabel, args); - break; - case "cmd": - case "commands": - runCommands(server, sender, commandLabel, args); - break; - case "dump": - runDump(server, sender, commandLabel, args); - break; - - // Data commands - case "reload": - runReload(server, sender, commandLabel, args); - break; - case "reset": - runReset(server, sender, commandLabel, args); - break; - case "cleanup": - runCleanup(server, sender, commandLabel, args); - break; - case "homes": - runHomes(server, sender, commandLabel, args); - break; - case "usermap": - runUserMap(sender, args); - break; - - case "itemtest": - runItemTest(server, sender, commandLabel, args); - break; - - // "#EasterEgg" - case "nya": - case "nyan": - runNya(server, sender, commandLabel, args); - break; - case "moo": - runMoo(server, sender, commandLabel, args); - break; - default: - showUsage(sender); - break; - } - } - - public void runItemTest(Server server, CommandSource sender, String commandLabel, String[] args) { - if (!sender.isAuthorized("essentials.itemtest") || args.length < 2 || !sender.isPlayer()) { - return; - } - - final Player player = sender.getPlayer(); - assert player != null; - - switch (args[1]) { - case "slot": { - if (args.length < 3) { - return; - } - player.getInventory().setItem(Integer.parseInt(args[2]), new ItemStack(Material.DIRT)); - break; - } - case "overfill": { - sender.sendMessage(Inventories.addItem(player, 42, false, new ItemStack(Material.DIAMOND_SWORD, 1), new ItemStack(Material.DIRT, 32), new ItemStack(Material.DIRT, 32)).toString()); - break; - } - case "overfill2": { - if (args.length < 4) { - return; - } - final boolean armor = Boolean.parseBoolean(args[2]); - final boolean add = Boolean.parseBoolean(args[3]); - final ItemStack[] items = new ItemStack[]{new ItemStack(Material.DIAMOND_SWORD, 1), new ItemStack(Material.DIRT, 32), new ItemStack(Material.DIRT, 32), new ItemStack(Material.DIAMOND_HELMET, 4), new ItemStack(Material.CHAINMAIL_LEGGINGS, 1)}; - if (Inventories.hasSpace(player, 0, armor, items)) { - if (add) { - sender.sendMessage(Inventories.addItem(player, 0, armor, items).toString()); - } - sender.sendMessage("SO MUCH SPACE!"); - } else { - sender.sendMessage("No space!"); - } - break; - } - case "remove": { - if (args.length < 3) { - return; - } - Inventories.removeItemExact(player, new ItemStack(Material.PUMPKIN, 1), Boolean.parseBoolean(args[2])); - break; - } - default: { - break; - } - } - } - - // Displays the command's usage. - private void showUsage(final CommandSource sender) throws Exception { - throw new NotEnoughArgumentsException(); - } - - // Lists commands that are being handed over to other plugins. - private void runCommands(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { - if (ess.getAlternativeCommandsHandler().disabledCommands().size() == 0) { - sender.sendTl("blockListEmpty"); - return; - } - - sender.sendTl("blockList"); - for (final Map.Entry entry : ess.getAlternativeCommandsHandler().disabledCommands().entrySet()) { - sender.sendMessage(entry.getKey() + " => " + entry.getValue()); - } - } - - // Generates a paste of useful information - private void runDump(Server server, CommandSource sender, String commandLabel, String[] args) { - sender.sendTl("dumpCreating"); - - final JsonObject dump = new JsonObject(); - - final JsonObject meta = new JsonObject(); - meta.addProperty("timestamp", Instant.now().toEpochMilli()); - meta.addProperty("sender", sender.getPlayer() != null ? sender.getPlayer().getName() : null); - meta.addProperty("senderUuid", sender.getPlayer() != null ? sender.getPlayer().getUniqueId().toString() : null); - dump.add("meta", meta); - - final JsonObject serverData = new JsonObject(); - serverData.addProperty("bukkit-version", Bukkit.getBukkitVersion()); - serverData.addProperty("server-version", Bukkit.getVersion()); - serverData.addProperty("server-brand", Bukkit.getName()); - serverData.addProperty("online-mode", ess.provider(OnlineModeProvider.class).getOnlineModeString()); - final JsonObject supportStatus = new JsonObject(); - final VersionUtil.SupportStatus status = VersionUtil.getServerSupportStatus(); - supportStatus.addProperty("status", status.name()); - supportStatus.addProperty("supported", status.isSupported()); - supportStatus.addProperty("trigger", VersionUtil.getSupportStatusClass()); - serverData.add("support-status", supportStatus); - dump.add("server-data", serverData); - - final JsonObject environment = new JsonObject(); - environment.addProperty("java-version", System.getProperty("java.version")); - environment.addProperty("operating-system", System.getProperty("os.name")); - environment.addProperty("uptime", DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime())); - environment.addProperty("allocated-memory", (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB"); - dump.add("environment", environment); - - final JsonObject essData = new JsonObject(); - essData.addProperty("version", ess.getDescription().getVersion()); - final JsonObject updateData = new JsonObject(); - updateData.addProperty("id", ess.getUpdateChecker().getVersionIdentifier()); - updateData.addProperty("branch", ess.getUpdateChecker().getVersionBranch()); - updateData.addProperty("dev", ess.getUpdateChecker().isDevBuild()); - essData.add("update-data", updateData); - final JsonObject econLayer = new JsonObject(); - econLayer.addProperty("enabled", !ess.getSettings().isEcoDisabled()); - econLayer.addProperty("selected-layer", EconomyLayers.isLayerSelected()); - final EconomyLayer layer = EconomyLayers.getSelectedLayer(); - econLayer.addProperty("name", layer == null ? "null" : layer.getName()); - econLayer.addProperty("layer-version", layer == null ? "null" : layer.getPluginVersion()); - econLayer.addProperty("backend-name", layer == null ? "null" : layer.getBackendName()); - essData.add("economy-layer", econLayer); - final JsonArray addons = new JsonArray(); - final JsonArray plugins = new JsonArray(); - final ArrayList alphabetical = new ArrayList<>(); - Collections.addAll(alphabetical, Bukkit.getPluginManager().getPlugins()); - alphabetical.sort(Comparator.comparing(o -> o.getName().toUpperCase(Locale.ENGLISH))); - for (final Plugin plugin : alphabetical) { - final JsonObject pluginData = new JsonObject(); - final PluginDescriptionFile info = plugin.getDescription(); - final String name = info.getName(); - - pluginData.addProperty("name", name); - pluginData.addProperty("version", info.getVersion()); - pluginData.addProperty("description", info.getDescription()); - pluginData.addProperty("main", info.getMain()); - pluginData.addProperty("enabled", plugin.isEnabled()); - pluginData.addProperty("official", plugin == ess || officialPlugins.contains(name)); - pluginData.addProperty("unsupported", warnPlugins.contains(name)); - - final JsonArray authors = new JsonArray(); - for (final String author : info.getAuthors()) { - authors.add(author == null ? JsonNull.INSTANCE : new JsonPrimitive(author)); - } - pluginData.add("authors", authors); - - if (name.startsWith("Essentials") && !name.equals("Essentials")) { - addons.add(pluginData); - } - plugins.add(pluginData); - } - essData.add("addons", addons); - dump.add("ess-data", essData); - dump.add("plugins", plugins); - - final List files = new ArrayList<>(); - files.add(new PasteUtil.PasteFile("dump.json", dump.toString())); - - final Plugin essDiscord = Bukkit.getPluginManager().getPlugin("EssentialsDiscord"); - final Plugin essDiscordLink = Bukkit.getPluginManager().getPlugin("EssentialsDiscordLink"); - final Plugin essSpawn = Bukkit.getPluginManager().getPlugin("EssentialsSpawn"); - - final Map knownCommandsCopy = new HashMap<>(ess.provider(KnownCommandsProvider.class).getKnownCommands()); - final Map disabledCommandsCopy = new HashMap<>(ess.getAlternativeCommandsHandler().disabledCommands()); - - // Further operations will be heavy IO - ess.runTaskAsynchronously(() -> { - boolean config = false; - boolean discord = false; - boolean kits = false; - boolean log = false; - boolean worth = false; - boolean tpr = false; - boolean spawns = false; - boolean commands = false; - for (final String arg : args) { - if (arg.equals("*") || arg.equalsIgnoreCase("all")) { - config = true; - discord = true; - kits = true; - log = true; - worth = true; - tpr = true; - spawns = true; - commands = true; - break; - } else if (arg.equalsIgnoreCase("config")) { - config = true; - } else if (arg.equalsIgnoreCase("discord")) { - discord = true; - } else if (arg.equalsIgnoreCase("kits")) { - kits = true; - } else if (arg.equalsIgnoreCase("log")) { - log = true; - } else if (arg.equalsIgnoreCase("worth")) { - worth = true; - } else if (arg.equalsIgnoreCase("tpr")) { - tpr = true; - } else if (arg.equalsIgnoreCase("spawns")) { - spawns = true; - } else if (arg.equalsIgnoreCase("commands")) { - commands = true; - } - } - - if (config) { - try { - files.add(new PasteUtil.PasteFile("config.yml", new String(Files.readAllBytes(ess.getSettings().getConfigFile().toPath()), StandardCharsets.UTF_8))); - } catch (IOException e) { - sender.sendTl("dumpErrorUpload", "config.yml", e.getMessage()); - } - } - - if (discord && essDiscord != null) { - try { - files.add(new PasteUtil.PasteFile("discord-config.yml", - new String(Files.readAllBytes(essDiscord.getDataFolder().toPath().resolve("config.yml")), StandardCharsets.UTF_8) - .replaceAll("[A-Za-z\\d]{24}\\.[\\w-]{6}\\.[\\w-]{27}", ""))); - } catch (IOException e) { - sender.sendTl("dumpErrorUpload", "discord-config.yml", e.getMessage()); - } - - if (essDiscordLink != null) { - try { - files.add(new PasteUtil.PasteFile("discord-link-config.yml", - new String(Files.readAllBytes(essDiscordLink.getDataFolder().toPath().resolve("config.yml")), StandardCharsets.UTF_8))); - } catch (IOException e) { - sender.sendTl("dumpErrorUpload", "discord-link-config.yml", e.getMessage()); - } - } - } - - if (kits) { - try { - files.add(new PasteUtil.PasteFile("kits.yml", new String(Files.readAllBytes(ess.getKits().getFile().toPath()), StandardCharsets.UTF_8))); - } catch (IOException e) { - sender.sendTl("dumpErrorUpload", "kits.yml", e.getMessage()); - } - } - - if (log) { - try { - files.add(new PasteUtil.PasteFile("latest.log", new String(Files.readAllBytes(Paths.get("logs", "latest.log")), StandardCharsets.UTF_8) - .replaceAll("(?m)^\\[\\d\\d:\\d\\d:\\d\\d] \\[.+/(?:DEBUG|TRACE)]: .+\\s(?:[A-Za-z.]+:.+\\s(?:\\t.+\\s)*)?\\s*(?:\"[A-Za-z]+\" : .+[\\s}\\]]+)*", "") - .replaceAll("(?:[0-9]{1,3}\\.){3}[0-9]{1,3}", ""))); - } catch (IOException e) { - sender.sendTl("dumpErrorUpload", "latest.log", e.getMessage()); - } - } - - if (worth) { - try { - files.add(new PasteUtil.PasteFile("worth.yml", new String(Files.readAllBytes(ess.getWorth().getFile().toPath()), StandardCharsets.UTF_8))); - } catch (IOException e) { - sender.sendTl("dumpErrorUpload", "worth.yml", e.getMessage()); - } - } - - if (tpr) { - try { - files.add(new PasteUtil.PasteFile("tpr.yml", new String(Files.readAllBytes(ess.getRandomTeleport().getFile().toPath()), StandardCharsets.UTF_8))); - } catch (IOException e) { - sender.sendTl("dumpErrorUpload", "tpr.yml", e.getMessage()); - } - } - - if (spawns && essSpawn != null) { - try { - files.add(new PasteUtil.PasteFile("spawn.yml", new String(Files.readAllBytes(ess.getDataFolder().toPath().resolve("spawn.yml")), StandardCharsets.UTF_8))); - } catch (IOException e) { - sender.sendTl("dumpErrorUpload", "spawn.yml", e.getMessage()); - } - } - - if (commands) { - try { - files.add(new PasteUtil.PasteFile("commands.yml", new String(Files.readAllBytes(Paths.get("commands.yml")), StandardCharsets.UTF_8))); - files.add(new PasteUtil.PasteFile("commandmap.json", CommandMapUtil.toJsonPretty(ess, knownCommandsCopy))); - files.add(new PasteUtil.PasteFile("commandoverride.json", disabledCommandsCopy.toString())); - } catch (IOException e) { - sender.sendTl("dumpErrorUpload", "commands.yml", e.getMessage()); - } - } - - final CompletableFuture future = PasteUtil.createPaste(files); - future.thenAccept(result -> { - if (result != null) { - final String dumpUrl = "https://essentialsx.net/dump?bytebin=" + result.getPasteId(); - sender.sendTl("dumpUrl", dumpUrl); - // pastes.dev doesn't support deletion keys - //sender.sendTl("dumpDeleteKey", result.getDeletionKey()); - if (sender.isPlayer()) { - ess.getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("dumpConsoleUrl", dumpUrl))); - // pastes.dev doesn't support deletion keys - //ess.getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("dumpDeleteKey", result.getDeletionKey()))); - } - } - files.clear(); - }); - future.exceptionally(throwable -> { - sender.sendTl("dumpError", throwable.getMessage()); - return null; - }); - }); - } - - // Resets the given player's user data. - private void runReset(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception { - if (args.length < 2) { - throw new Exception("/ reset "); - } - final User user = getPlayer(server, args, 1, true, true); - user.reset(); - sender.sendMessage("Reset Essentials userdata for player: " + user.getDisplayName()); - } - - // Toggles debug mode. - private void runDebug(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception { - ess.getSettings().setDebug(!ess.getSettings().isDebug()); - sender.sendMessage("Essentials " + ess.getDescription().getVersion() + " debug mode " + (ess.getSettings().isDebug() ? "enabled" : "disabled")); - } - - // Reloads all reloadable configs. - private void runReload(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception { - ess.reload(); - sender.sendTl("essentialsReload", ess.getDescription().getVersion()); - } - - // Pop tarts. - private void runNya(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception { - if (currentTune != null) { - currentTune.cancel(); - } - - currentTune = new TuneRunnable(NYAN_TUNE, NOTE_HARP, ess::getOnlinePlayers); - currentTune.runTaskTimer(ess, 20, 2); - } - - // Cow farts. - private void runMoo(final Server server, final CommandSource sender, final String command, final String[] args) { - if (args.length == 2 && args[1].equals("moo")) { - for (final String s : CONSOLE_MOO) { - ess.getLogger().info(s); - } - for (final Player player : ess.getOnlinePlayers()) { - player.sendMessage(PLAYER_MOO); - player.playSound(player.getLocation(), MOO_SOUND, 1, 1.0f); - } - } else { - if (sender.isPlayer()) { - sender.getSender().sendMessage(PLAYER_MOO); - final Player player = sender.getPlayer(); - player.playSound(player.getLocation(), MOO_SOUND, 1, 1.0f); - - } else { - sender.getSender().sendMessage(CONSOLE_MOO); - } - } - } - - // Cleans up inactive users. - private void runCleanup(final Server server, final CommandSource sender, final String command, final String[] args) throws Exception { - if (args.length < 2 || !NumberUtil.isInt(args[1])) { - sender.sendMessage("This sub-command will delete users who haven't logged in in the last days."); - sender.sendMessage("Optional parameters define the minimum amount required to prevent deletion."); - sender.sendMessage("Unless you define larger default values, this command will ignore people who have more than 0 money/homes."); - throw new Exception("/ cleanup [money] [homes]"); - } - - sender.sendTl("cleaning"); - - final long daysArg = Long.parseLong(args[1]); - final double moneyArg = args.length >= 3 ? FloatUtil.parseDouble(args[2].replaceAll("[^0-9.]", "")) : 0; - final int homesArg = args.length >= 4 && NumberUtil.isInt(args[3]) ? Integer.parseInt(args[3]) : 0; - - ess.runTaskAsynchronously(() -> { - final long currTime = System.currentTimeMillis(); - for (final UUID u : ess.getUsers().getAllUserUUIDs()) { - final User user = ess.getUsers().loadUncachedUser(u); - if (user == null) { - continue; - } - - long lastLog = user.getLastLogout(); - if (lastLog == 0) { - lastLog = user.getLastLogin(); - } - if (lastLog == 0) { - user.setLastLogin(currTime); - } - - if (user.isNPC()) { - continue; - } - - final long timeDiff = currTime - lastLog; - final long milliDays = daysArg * 24L * 60L * 60L * 1000L; - final int homeCount = user.getHomes().size(); - final double moneyCount = user.getMoney().doubleValue(); - - if ((lastLog == 0) || (timeDiff < milliDays) || (homeCount > homesArg) || (moneyCount > moneyArg)) { - continue; - } - - if (ess.getSettings().isDebug()) { - ess.getLogger().info("Deleting user: " + user.getName() + " Money: " + moneyCount + " Homes: " + homeCount + " Last seen: " + DateUtil.formatDateDiff(lastLog)); - } - - user.reset(); - } - sender.sendTl("cleaned"); - }); - } - - private void runHomes(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception { - if (args.length < 2) { - sender.sendMessage("This sub-command provides a utility to mass-delete homes based on user options:"); - sender.sendMessage("Use \"fix\" to delete all homes inside non-existent or unloaded worlds."); - sender.sendMessage("Use \"delete\" to delete all existing homes."); - sender.sendMessage("Use \"delete \" to delete all homes inside a specific world."); - throw new Exception(HOMES_USAGE); - } - - switch (args[1]) { - case "fix": - sender.sendTl("fixingHomes"); - ess.runTaskAsynchronously(() -> { - for (final UUID u : ess.getUsers().getAllUserUUIDs()) { - final User user = ess.getUsers().loadUncachedUser(u); - if (user == null) { - continue; - } - for (String homeName : user.getHomes()) { - try { - if (user.getHome(homeName) == null) { - user.delHome(homeName); - } - } catch (Exception e) { - ess.getLogger().info("Unable to delete home " + homeName + " for " + user.getName()); - } - } - } - sender.sendTl("fixedHomes"); - }); - break; - case "delete": - final boolean filterByWorld = args.length >= 3; - if (filterByWorld && server.getWorld(args[2]) == null) { - throw new TranslatableException("invalidWorld"); - } - if (filterByWorld) { - sender.sendTl("deletingHomesWorld", args[2]); - } else { - sender.sendTl("deletingHomes"); - } - ess.runTaskAsynchronously(() -> { - for (final UUID u : ess.getUsers().getAllUserUUIDs()) { - final User user = ess.getUsers().loadUncachedUser(u); - if (user == null) { - continue; - } - for (String homeName : user.getHomes()) { - try { - final Location home = user.getHome(homeName); - if (!filterByWorld || (home != null && home.getWorld() != null && home.getWorld().getName().equals(args[2]))) { - user.delHome(homeName); - } - } catch (Exception e) { - ess.getLogger().info("Unable to delete home " + homeName + " for " + user.getName()); - } - } - } - - if (filterByWorld) { - sender.sendTl("deletedHomesWorld", args[2]); - } else { - sender.sendTl("deletedHomes"); - } - }); - break; - default: - throw new Exception(HOMES_USAGE); - } - } - - // Gets information about cached users - private void runUserMap(final CommandSource sender, final String[] args) { - if (!sender.isAuthorized("essentials.usermap")) { - return; - } - - final ModernUserMap userMap = (ModernUserMap) ess.getUsers(); - sender.sendTl("usermapSize", userMap.getCachedCount(), userMap.getUserCount(), ess.getSettings().getMaxUserCacheCount()); - if (args.length > 1) { - if (args[1].equals("full")) { - for (final Map.Entry entry : userMap.getNameCache().entrySet()) { - sender.sendTl("usermapEntry", entry.getKey(), entry.getValue().toString()); - } - } else if (args[1].equals("purge")) { - final boolean seppuku = args.length > 2 && args[2].equals("iknowwhatimdoing"); - - sender.sendTl("usermapPurge", String.valueOf(seppuku)); - - final Set uuids = new HashSet<>(ess.getUsers().getAllUserUUIDs()); - ess.runTaskAsynchronously(() -> { - final File userdataFolder = new File(ess.getDataFolder(), "userdata"); - final File backupFolder = new File(ess.getDataFolder(), "userdata-npc-backup-boogaloo-" + System.currentTimeMillis()); - - if (!userdataFolder.isDirectory()) { - ess.getLogger().warning("Missing userdata folder, aborting usermap purge."); - return; - } - - if (seppuku && !backupFolder.mkdir()) { - ess.getLogger().warning("Unable to create backup folder, aborting usermap purge."); - return; - } - - int total = 0; - final File[] files = userdataFolder.listFiles(EssentialsUpgrade.YML_FILTER); - if (files != null) { - for (final File file : files) { - try { - final String fileName = file.getName(); - final UUID uuid = UUID.fromString(fileName.substring(0, fileName.length() - 4)); - if (!uuids.contains(uuid)) { - total++; - ess.getLogger().warning("Found orphaned userdata file: " + file.getName()); - if (seppuku) { - try { - com.google.common.io.Files.move(file, new File(backupFolder, file.getName())); - } catch (IOException e) { - ess.getLogger().log(Level.WARNING, "Unable to move orphaned userdata file: " + file.getName(), e); - } - } - } - } catch (IllegalArgumentException ignored) { - } - } - } - ess.getLogger().info("Found " + total + " orphaned userdata files."); - }); - } else if (args[1].equalsIgnoreCase("cache")) { - sender.sendTl("usermapKnown", ess.getUsers().getAllUserUUIDs().size(), ess.getUsers().getNameCache().size()); - } else { - try { - final UUID uuid = UUID.fromString(args[1]); - for (final Map.Entry entry : userMap.getNameCache().entrySet()) { - if (entry.getValue().equals(uuid)) { - sender.sendTl("usermapEntry", entry.getKey(), args[1]); - } - } - } catch (IllegalArgumentException ignored) { - final String sanitizedName = userMap.getSanitizedName(args[1]); - sender.sendTl("usermapEntry", sanitizedName, userMap.getNameCache().get(sanitizedName).toString()); - } - } - } - } - - // Displays versions of EssentialsX and related plugins. - private void runVersion(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception { - if (sender.isPlayer() && !ess.getUser(sender.getPlayer()).isAuthorized("essentials.version")) return; - - boolean isMismatched = false; - boolean isVaultInstalled = false; - boolean isUnsupported = false; - final VersionUtil.SupportStatus supportStatus = VersionUtil.getServerSupportStatus(); - final PluginManager pm = server.getPluginManager(); - final String essVer = pm.getPlugin("Essentials").getDescription().getVersion(); - - final String serverMessageKey; - if (supportStatus.isSupported()) { - serverMessageKey = "versionOutputFine"; - } else if (supportStatus == VersionUtil.SupportStatus.UNSTABLE) { - serverMessageKey = "versionOutputUnsupported"; - } else { - serverMessageKey = "versionOutputWarn"; - } - - sender.sendTl(serverMessageKey, "Server", server.getBukkitVersion() + " " + server.getVersion()); - sender.sendTl(serverMessageKey, "Brand", server.getName()); - sender.sendTl("versionOutputFine", "EssentialsX", essVer); - - for (final Plugin plugin : pm.getPlugins()) { - final PluginDescriptionFile desc = plugin.getDescription(); - String name = desc.getName(); - final String version = desc.getVersion(); - - if (name.startsWith("Essentials") && !name.equalsIgnoreCase("Essentials")) { - if (officialPlugins.contains(name)) { - name = name.replace("Essentials", "EssentialsX"); - - if (!version.equalsIgnoreCase(essVer)) { - isMismatched = true; - sender.sendTl("versionOutputWarn", name, version); - } else { - sender.sendTl("versionOutputFine", name, version); - } - } else { - sender.sendTl("versionOutputUnsupported", name, version); - isUnsupported = true; - } - } - - if (versionPlugins.contains(name)) { - if (warnPlugins.contains(name)) { - sender.sendTl("versionOutputUnsupported", name, version); - isUnsupported = true; - } else { - sender.sendTl("versionOutputFine", name, version); - } - } - - if (name.equals("Vault")) isVaultInstalled = true; - } - - final String layer; - if (ess.getSettings().isEcoDisabled()) { - layer = "Disabled"; - } else if (EconomyLayers.isLayerSelected()) { - final EconomyLayer economyLayer = EconomyLayers.getSelectedLayer(); - layer = economyLayer.getName() + " (" + economyLayer.getBackendName() + ")"; - } else { - layer = "None"; - } - sender.sendTl("versionOutputEconLayer", layer); - - if (isMismatched) { - sender.sendTl("versionMismatchAll"); - } - - if (!isVaultInstalled) { - sender.sendTl("versionOutputVaultMissing"); - } - - if (isUnsupported) { - sender.sendTl("versionOutputUnsupportedPlugins"); - } - - switch (supportStatus) { - case NMS_CLEANROOM: - sender.sendComponent(sender.tlComponent("serverUnsupportedCleanroom").color(NamedTextColor.DARK_RED)); - break; - case DANGEROUS_FORK: - sender.sendComponent(sender.tlComponent("serverUnsupportedDangerous").color(NamedTextColor.DARK_RED)); - break; - case STUPID_PLUGIN: - sender.sendComponent(sender.tlComponent("serverUnsupportedDumbPlugins").color(NamedTextColor.DARK_RED)); - break; - case UNSTABLE: - sender.sendComponent(sender.tlComponent("serverUnsupportedMods").color(NamedTextColor.DARK_RED)); - break; - case OUTDATED: - sender.sendComponent(sender.tlComponent("serverUnsupported").color(NamedTextColor.RED)); - break; - case LIMITED: - sender.sendComponent(sender.tlComponent("serverUnsupportedLimitedApi").color(NamedTextColor.RED)); - break; - } - if (VersionUtil.getSupportStatusClass() != null) { - sender.sendComponent(sender.tlComponent("serverUnsupportedClass").color(NamedTextColor.RED)); - } - - sender.sendTl("versionFetching"); - ess.runTaskAsynchronously(() -> { - for (final Component component : ess.getUpdateChecker().getVersionMessages(true, true, sender)) { - sender.sendComponent(component); - } - }); - } - - @Override - protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { - if (args.length == 1) { - final List options = Lists.newArrayList(); - options.add("reload"); - options.add("version"); - options.add("dump"); - options.add("commands"); - options.add("debug"); - options.add("reset"); - options.add("cleanup"); - options.add("homes"); - //options.add("uuidconvert"); - //options.add("nya"); - //options.add("moo"); - return options; - } - - switch (args[0]) { - case "moo": - if (args.length == 2) { - return Lists.newArrayList("moo"); - } - break; - case "reset": - if (args.length == 2) { - return getPlayers(server, sender); - } - break; - case "cleanup": - if (args.length == 2) { - return COMMON_DURATIONS; - } else if (args.length == 3 || args.length == 4) { - return Lists.newArrayList("-1", "0"); - } - break; - case "homes": - if (args.length == 2) { - return Lists.newArrayList("fix", "delete"); - } else if (args.length == 3 && args[1].equalsIgnoreCase("delete")) { - return server.getWorlds().stream().map(World::getName).collect(Collectors.toList()); - } - break; - case "dump": - final List list = Lists.newArrayList("config", "kits", "log", "discord", "worth", "tpr", "spawns", "commands", "all"); - for (String arg : args) { - if (arg.equals("*") || arg.equalsIgnoreCase("all")) { - list.clear(); - return list; - } - list.remove(arg.toLowerCase(Locale.ENGLISH)); - } - return list; - } - - return Collections.emptyList(); - } - - private static class TuneRunnable extends BukkitRunnable { - private static final Map noteMap = ImmutableMap.builder() - .put("1F#", 0.5f) - .put("1G", 0.53f) - .put("1G#", 0.56f) - .put("1A", 0.6f) - .put("1A#", 0.63f) - .put("1B", 0.67f) - .put("1C", 0.7f) - .put("1C#", 0.76f) - .put("1D", 0.8f) - .put("1D#", 0.84f) - .put("1E", 0.9f) - .put("1F", 0.94f) - .put("2F#", 1.0f) - .put("2G", 1.06f) - .put("2G#", 1.12f) - .put("2A", 1.18f) - .put("2A#", 1.26f) - .put("2B", 1.34f) - .put("2C", 1.42f) - .put("2C#", 1.5f) - .put("2D", 1.6f) - .put("2D#", 1.68f) - .put("2E", 1.78f) - .put("2F", 1.88f) - .build(); - - private final String[] tune; - private final Sound sound; - private final Supplier> players; - private int i = 0; - - TuneRunnable(final String tuneStr, final Sound sound, final Supplier> players) { - this.tune = tuneStr.split(","); - this.sound = sound; - this.players = players; - } - - @Override - public void run() { - final String note = tune[i]; - i++; - if (i >= tune.length) { - cancel(); - } - if (note == null || note.isEmpty()) { - return; - } - - for (final Player onlinePlayer : players.get()) { - onlinePlayer.playSound(onlinePlayer.getLocation(), sound, 1, noteMap.get(note)); - } - } + registerNode(new VersionCommand()); + registerNode(new DebugCommand()); + registerNode(new CommandMapCommand()); + registerNode(new DumpCommand()); + registerNode(new CleanupCommand()); + registerNode(new HomesCommand()); + registerNode(new UsermapCommand()); + registerNode(new ItemTestCommand()); + registerNode(new NyanCommand()); + registerNode(new MooCommand()); } } 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 9f4270aa0f1..c104c43646d 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandexp.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandexp.java @@ -154,7 +154,7 @@ protected List getTabCompleteOptions(final Server server, final User use } } if (user.isAuthorized("essentials.exp.others")) { - return getPlayers(server, user); + 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", ""); @@ -182,10 +182,10 @@ protected List getTabCompleteOptions(final Server server, final CommandS return Collections.emptyList(); } } else { // even without 'show' - return getPlayers(server, sender); + return getPlayers(sender); } } else if (args.length == 3 && (args[0].equalsIgnoreCase("set") || args[0].equalsIgnoreCase("give"))) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandext.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandext.java index ba3df39f4f0..0b9601f94b3 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandext.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandext.java @@ -46,7 +46,7 @@ private void extPlayer(final Player player) { @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandfeed.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandfeed.java index 72efa62ac85..fcf97889cc9 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandfeed.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandfeed.java @@ -65,7 +65,7 @@ private void feedPlayer(final Player player) throws QuietAbortException { @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1 && sender.isAuthorized("essentials.feed.others")) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgamemode.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgamemode.java index b5c6d05d77c..bb9860c80c5 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgamemode.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgamemode.java @@ -105,12 +105,12 @@ protected List getTabCompleteOptions(final Server server, final CommandS try { // Direct command? Don't ask for the mode matchGameMode(commandLabel); - return getPlayers(server, sender); + return getPlayers(sender); } catch (final NotEnoughArgumentsException e) { return STANDARD_OPTIONS; } } else if (args.length == 2) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } @@ -128,12 +128,12 @@ protected List getTabCompleteOptions(final Server server, final User use } if (args.length == 1) { if (user.isAuthorized("essentials.gamemode.others") && isDirectGamemodeCommand) { - return getPlayers(server, user); + return getPlayers(user); } else { return STANDARD_OPTIONS; } } else if (args.length == 2 && user.isAuthorized("essentials.gamemode.others") && !isDirectGamemodeCommand) { - return getPlayers(server, user); + return getPlayers(user); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgetpos.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgetpos.java index 388bab68cd7..7b45607a8af 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgetpos.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgetpos.java @@ -47,7 +47,7 @@ private void outputPosition(final CommandSource sender, final Location coords, f @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1 && sender.isAuthorized("essentials.getpos.others")) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgive.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgive.java index 9991e826a46..14cfa7e0fe2 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgive.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgive.java @@ -99,7 +99,7 @@ public void run(final Server server, final CommandSource sender, final String co @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else if (args.length == 2) { return getItems(); } else if (args.length == 3) { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandheal.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandheal.java index 1eebd4e2240..7f72526660e 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandheal.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandheal.java @@ -81,7 +81,7 @@ protected void updatePlayer(final Server server, final CommandSource sender, fin @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1 && sender.isAuthorized("essentials.heal.others")) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java index a8017dcbf8a..e147bdcfdb3 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java @@ -155,7 +155,7 @@ protected List getTabCompleteOptions(final Server server, final User use if (canVisitOthers) { final int sepIndex = args[0].indexOf(':'); if (sepIndex < 0) { - getPlayers(server, user).forEach(player -> homes.add(player + ":")); + getPlayers(user).forEach(player -> homes.add(player + ":")); } else { final String namePart = args[0].substring(0, sepIndex); final User otherUser; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandice.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandice.java index 4c802a79f94..be8f9a6d35a 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandice.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandice.java @@ -49,7 +49,7 @@ private void freezePlayer(final IUser user) { @Override protected List getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) { if (args.length == 1 && sender.isAuthorized("essentials.ice.others")) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandignore.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandignore.java index 488c2a0ee7d..c8a492403c3 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandignore.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandignore.java @@ -45,7 +45,7 @@ protected void run(final Server server, final User user, final String commandLab @Override protected List getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, user); + return getPlayers(user); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandinvsee.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandinvsee.java index f4666dd90a9..d1fe45b1a01 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandinvsee.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandinvsee.java @@ -45,7 +45,7 @@ protected void run(final Server server, final User user, final String commandLab @Override protected List getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) { if (args.length == 1) { - final List suggestions = getPlayers(server, user); + final List suggestions = getPlayers(user); suggestions.remove(user.getName()); return suggestions; } else { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkick.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkick.java index 0efb0fd0754..c0b2a3c176f 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkick.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkick.java @@ -62,7 +62,7 @@ public void run(final Server server, final CommandSource sender, final String co @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkill.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkill.java index e5145852616..80b9983bba2 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkill.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkill.java @@ -44,7 +44,7 @@ protected void updatePlayer(final Server server, final CommandSource sender, fin @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkit.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkit.java index 1394a3c5e86..adba5434be3 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkit.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkit.java @@ -108,7 +108,7 @@ protected List getTabCompleteOptions(final Server server, final User use } return options; } else if (args.length == 2 && user.isAuthorized("essentials.kit.others")) { - return getPlayers(server, user); + return getPlayers(user); } else { return Collections.emptyList(); } @@ -119,7 +119,7 @@ protected List getTabCompleteOptions(final Server server, final CommandS if (args.length == 1) { return new ArrayList<>(ess.getKits().getKitKeys()); // TODO: Move this to its own method } else if (args.length == 2) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkitreset.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkitreset.java index 70c8873f7b9..fafed4dc3e4 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkitreset.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkitreset.java @@ -59,7 +59,7 @@ protected List getTabCompleteOptions(Server server, CommandSource sender if (args.length == 1) { return new ArrayList<>(ess.getKits().getKitKeys()); } else if (args.length == 2 && sender.isAuthorized("essentials.kitreset.others")) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandlightning.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandlightning.java index a8a53db6242..046923c684a 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandlightning.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandlightning.java @@ -63,7 +63,7 @@ protected List getTabCompleteOptions(final Server server, final User use @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else if (args.length == 2) { return Lists.newArrayList("5"); } else { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmail.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmail.java index 7dad7306325..683fce75332 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmail.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmail.java @@ -334,7 +334,7 @@ protected List getTabCompleteOptions(final Server server, final User use return options; } else if (args.length == 2) { if ((args[0].equalsIgnoreCase("send") && user.isAuthorized("essentials.mail.send")) || (args[0].equalsIgnoreCase("sendtemp") && user.isAuthorized("essentials.mail.sendtemp")) || ((args[0].equalsIgnoreCase("clear"))&& user.isAuthorized("essentials.mail.clear.others"))) { - return getPlayers(server, user); + return getPlayers(user); } else if (args[0].equalsIgnoreCase("sendtempall") && user.isAuthorized("essentials.mail.sendtempall")) { return COMMON_DATE_DIFFS; } else if (args[0].equalsIgnoreCase("read")) { @@ -374,7 +374,7 @@ protected List getTabCompleteOptions(final Server server, final CommandS return Lists.newArrayList("send", "sendall", "sendtemp", "sendtempall", "clearall", "clear"); } else if (args.length == 2) { if (args[0].equalsIgnoreCase("send") || args[0].equalsIgnoreCase("sendtemp") || args[0].equalsIgnoreCase("clear")) { - return getPlayers(server, sender); + return getPlayers(sender); } else if (args[0].equalsIgnoreCase("sendtempall")) { return COMMON_DATE_DIFFS; } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmsg.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmsg.java index ad662ca0437..9b15254c964 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmsg.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmsg.java @@ -58,7 +58,7 @@ protected void updatePlayer(final Server server, final CommandSource sender, fin @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); // It's a chat message, send an empty list. } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmute.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmute.java index 53d1b21755e..e8cf37fbe31 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmute.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmute.java @@ -129,7 +129,7 @@ public void run(final Server server, final CommandSource sender, final String co @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return COMMON_DATE_DIFFS; // Date diff can span multiple words } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnear.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnear.java index 34bc3aaa9c0..c17ff324923 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnear.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnear.java @@ -117,7 +117,7 @@ private String getLocal(final CommandSource source, final User user, final long protected List getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) { if (user.isAuthorized("essentials.near.others")) { if (args.length == 1) { - return getPlayers(server, user); + return getPlayers(user); } else if (args.length == 2) { return Lists.newArrayList(Integer.toString(ess.getSettings().getNearRadius())); } else { @@ -135,7 +135,7 @@ protected List getTabCompleteOptions(final Server server, final User use @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else if (args.length == 2) { return Lists.newArrayList(Integer.toString(ess.getSettings().getNearRadius())); } else { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnick.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnick.java index a2c3f364ded..92ed2a88f4b 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnick.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnick.java @@ -121,7 +121,7 @@ private void setNickname(final Server server, final CommandSource sender, final @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1 && sender.isAuthorized("essentials.nick.others")) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnuke.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnuke.java index c06d2f16eb0..58ae8da0f71 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnuke.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnuke.java @@ -52,7 +52,7 @@ protected void run(final Server server, final CommandSource sender, final String @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpay.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpay.java index 3f7d353af6f..daa227391a6 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpay.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpay.java @@ -109,7 +109,7 @@ protected void updatePlayer(final Server server, final CommandSource sender, fin @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else if (args.length == 2) { return Lists.newArrayList(ess.getSettings().getMinimumPayAmount().toString()); } else { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandplaytime.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandplaytime.java index 7ef841f3e8a..cd424207a4d 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandplaytime.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandplaytime.java @@ -28,7 +28,7 @@ protected void run(Server server, CommandSource sender, String commandLabel, Str String displayName; long playtime; final String key; - + if (args.length > 0 && sender.isAuthorized("essentials.playtime.others")) { try { final IUser user = getPlayer(server, sender, args, 0); @@ -59,14 +59,14 @@ protected void run(Server server, CommandSource sender, String commandLabel, Str final long playtimeMs = System.currentTimeMillis() - (playtime * 50L); sender.sendTl(key, DateUtil.formatDateDiff(playtimeMs), AdventureUtil.parsed(AdventureUtil.legacyToMini(displayName))); } - + @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1 && sender.isAuthorized("essentials.playtime.others")) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } } - + } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpowertool.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpowertool.java index cd6808b639e..5d0b47030c0 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpowertool.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpowertool.java @@ -129,7 +129,7 @@ protected List getTabCompleteOptions(final Server server, final User use @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else if (args.length == 2) { return getItems(); } else if (args.length == 3) { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandptime.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandptime.java index 9e66b0e2a5f..92ed2bce0e1 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandptime.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandptime.java @@ -122,7 +122,7 @@ protected List getTabCompleteOptions(final Server server, final CommandS if (args.length == 1) { return Lists.newArrayList("get", "reset", "sunrise", "day", "morning", "noon", "afternoon", "sunset", "night", "midnight"); } else if (args.length == 2 && (getAliases.contains(args[0]) || user == null || user.isAuthorized("essentials.ptime.others"))) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpweather.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpweather.java index 962dd445168..c9eb4c2a425 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpweather.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpweather.java @@ -107,7 +107,7 @@ protected List getTabCompleteOptions(final Server server, final User use if (args.length == 1) { return Lists.newArrayList("get", "reset", "storm", "sun"); } else if (args.length == 2 && (getAliases.contains(args[0]) || user == null || user.isAuthorized("essentials.pweather.others"))) { - return getPlayers(server, user); + return getPlayers(user); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrenamehome.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrenamehome.java index 4bf8e3cbc45..753fdc87a0f 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrenamehome.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrenamehome.java @@ -89,7 +89,7 @@ protected List getTabCompleteOptions(final Server server, final CommandS if (canRenameOthers) { final int sepIndex = args[0].indexOf(':'); if (sepIndex < 0) { - getPlayers(server, sender).forEach(player -> homes.add(player + ":")); + getPlayers(sender).forEach(player -> homes.add(player + ":")); } else { final String namePart = args[0].substring(0, sepIndex); final User otherUser; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrest.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrest.java index 82946b4f008..3ff41da670b 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrest.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrest.java @@ -45,7 +45,7 @@ private void restPlayer(final IUser user) { @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1 && sender.isAuthorized("essentials.rest.others")) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandseen.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandseen.java index 0c1065bef18..d0a452b49c9 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandseen.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandseen.java @@ -232,7 +232,7 @@ private void seenIP(final CommandSource sender, final String ipAddress, final St @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandskull.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandskull.java index 1adcefe8f2e..b279b477c5c 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandskull.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandskull.java @@ -178,13 +178,13 @@ private void editSkull(final User user, final User receive, final ItemStack stac protected List getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) { if (args.length == 1) { if (user.isAuthorized("essentials.skull.others")) { - return getPlayers(server, user); + return getPlayers(user); } else { return Lists.newArrayList(user.getName()); } } else if (args.length == 2){ if (user.isAuthorized("essentials.skull.others")) { - return getPlayers(server, user); + return getPlayers(user); } else { return Lists.newArrayList(user.getName()); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandspeed.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandspeed.java index 6eb6975ad46..69f9c195d5a 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandspeed.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandspeed.java @@ -140,7 +140,7 @@ protected List getTabCompleteOptions(final Server server, final CommandS } else if (args.length == 2) { return speeds; } else if (args.length == 3 && sender.isAuthorized("essentials.speed.others")) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempban.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempban.java index fde30d244ce..8ce0b996dab 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempban.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempban.java @@ -64,7 +64,7 @@ public void run(final Server server, final CommandSource sender, final String co @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else { // Note: following args are both date diffs _and_ messages; ideally we'd mix with the vanilla handler return COMMON_DATE_DIFFS; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempbanip.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempbanip.java index e3105dfa94f..51897dfbb9e 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempbanip.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempbanip.java @@ -80,7 +80,7 @@ public void run(final Server server, final CommandSource sender, final String co protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { // TODO: Also list IP addresses? - return getPlayers(server, sender); + return getPlayers(sender); } else { // Note: following args are both date diffs _and_ messages; ideally we'd mix with the vanilla handler return COMMON_DATE_DIFFS; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtogglejail.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtogglejail.java index ce777f67f72..d77a55b51da 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtogglejail.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtogglejail.java @@ -171,7 +171,7 @@ public void run(final Server server, final CommandSource sender, final String co @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else if (args.length == 2) { try { return new ArrayList<>(ess.getJails().getList()); diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtp.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtp.java index 747775d4afe..1fc8a9df1ec 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtp.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtp.java @@ -147,7 +147,7 @@ public void run(final Server server, final CommandSource sender, final String co protected List getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) { // Don't handle coords if (args.length == 1 || (args.length == 2 && user.isAuthorized("essentials.tp.others"))) { - return getPlayers(server, user); + return getPlayers(user); } else { return Collections.emptyList(); } @@ -157,7 +157,7 @@ protected List getTabCompleteOptions(final Server server, final User use protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { // Don't handle coords if (args.length == 1 || args.length == 2) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpa.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpa.java index f33e8324638..151d5654130 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpa.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpa.java @@ -81,7 +81,7 @@ public void run(final Server server, final User user, final String commandLabel, @Override protected List getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, user); + return getPlayers(user); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpaall.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpaall.java index 9cc135294c2..722fd11083d 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpaall.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpaall.java @@ -62,7 +62,7 @@ private void tpaAll(final CommandSource sender, final User target) { @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpahere.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpahere.java index 54383495c28..71b35889e79 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpahere.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpahere.java @@ -59,7 +59,7 @@ public void run(final Server server, final User user, final String commandLabel, @Override protected List getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, user); + return getPlayers(user); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpall.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpall.java index 5a007165dcc..5d18cb2becc 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpall.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpall.java @@ -45,7 +45,7 @@ private void teleportAllPlayers(final Server server, final CommandSource sender, @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtphere.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtphere.java index c18609c5dc9..895bad58c27 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtphere.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtphere.java @@ -30,7 +30,7 @@ public void run(final Server server, final User user, final String commandLabel, @Override protected List getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, user); + return getPlayers(user); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpo.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpo.java index 448628187b9..8ff98843485 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpo.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpo.java @@ -54,7 +54,7 @@ public void run(final Server server, final User user, final String commandLabel, protected List getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) { // Don't handle coords if (args.length == 1 || (args.length == 2 && user.isAuthorized("essentials.tp.others"))) { - return getPlayers(server, user); + return getPlayers(user); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpohere.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpohere.java index f7321855fc5..5937a5b908a 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpohere.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpohere.java @@ -32,7 +32,7 @@ public void run(final Server server, final User user, final String commandLabel, @Override protected List getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, user); + return getPlayers(user); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtppos.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtppos.java index 288eeaafb4e..48a73cfe054 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtppos.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtppos.java @@ -107,7 +107,7 @@ protected List getTabCompleteOptions(final Server server, final User use @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else if (args.length == 2 || args.length == 3 || args.length == 4) { return Lists.newArrayList("~0"); } else if (args.length == 5 || args.length == 6) { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpr.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpr.java index fd524bb99c9..e1aa96c071a 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpr.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpr.java @@ -118,7 +118,7 @@ protected List getTabCompleteOptions(final Server server, final CommandS return randomTeleport.listLocations(); } } else if (args.length == 2 && sender.isAuthorized("essentials.tpr.others")) { - return getPlayers(server, sender); + return getPlayers(sender); } return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandwarp.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandwarp.java index 79f54f8f76e..ce0860156e4 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandwarp.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandwarp.java @@ -114,7 +114,7 @@ protected List getTabCompleteOptions(final Server server, final User use return getAvailableWarpsFor(user); } else if (args.length == 2 && (user.isAuthorized("essentials.warp.otherplayers") || user.isAuthorized("essentials.warp.others"))) { //TODO: Remove 'otherplayers' permission. - return getPlayers(server, user); + return getPlayers(user); } else { return Collections.emptyList(); } @@ -125,7 +125,7 @@ protected List getTabCompleteOptions(final Server server, final CommandS if (args.length == 1) { return new ArrayList<>(ess.getWarps().getList()); } else if (args.length == 2) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandwhois.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandwhois.java index b3eb0ca1877..d7869e38fe7 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandwhois.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandwhois.java @@ -77,7 +77,7 @@ public void run(final Server server, final CommandSource sender, final String co @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else { return Collections.emptyList(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsCommand.java index 897c3d98101..6f7091d90db 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsCommand.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsCommand.java @@ -36,7 +36,7 @@ public abstract class EssentialsCommand implements IEssentialsCommand { /** * Common time durations (in seconds), for use in tab completion. */ - protected static final List COMMON_DURATIONS = ImmutableList.of("1", "60", "600", "3600", "86400"); + public static final List COMMON_DURATIONS = ImmutableList.of("1", "60", "600", "3600", "86400"); /** * Common date diffs, for use in tab completion */ @@ -219,7 +219,7 @@ public final List tabComplete(final Server server, final CommandSource s // Doesn't need to do any starts-with checks protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { // No tab completion results - return getPlayers(server, sender); + return getPlayers(sender); } boolean canInteractWith(final CommandSource interactor, final User interactee) { @@ -230,7 +230,7 @@ boolean canInteractWith(final CommandSource interactor, final User interactee) { * Gets a list of all player names that can be seen with by the given CommandSource, * for tab completion. */ - protected List getPlayers(final Server server, final CommandSource interactor) { + protected List getPlayers(final CommandSource interactor) { final List players = Lists.newArrayList(); for (final User user : ess.getOnlineUsers()) { if (canInteractWith(interactor, user)) { @@ -244,14 +244,8 @@ protected List getPlayers(final Server server, final CommandSource inter * Gets a list of all player names that can be seen with by the given User, * for tab completion. */ - protected List getPlayers(final Server server, final User interactor) { - final List players = Lists.newArrayList(); - for (final User user : ess.getOnlineUsers()) { - if (canInteractWith(interactor, user)) { - players.add(ess.getSettings().changeTabCompleteName() ? FormatUtil.stripFormat(user.getDisplayName()) : user.getName()); - } - } - return players; + protected List getPlayers(final User interactor) { + return getPlayers(interactor.getSource()); } /** diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsLoopCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsLoopCommand.java index 5aca1d9eac3..1a4505a7cb4 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsLoopCommand.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsLoopCommand.java @@ -138,16 +138,16 @@ protected void loopOnlinePlayersConsumer(final Server server, final CommandSourc protected abstract void updatePlayer(Server server, CommandSource sender, User user, String[] args) throws NotEnoughArgumentsException, PlayerExemptException, ChargeException, MaxMoneyException; @Override - protected List getPlayers(final Server server, final CommandSource interactor) { - final List players = super.getPlayers(server, interactor); + protected List getPlayers(final CommandSource interactor) { + final List players = super.getPlayers(interactor); players.add("**"); players.add("*"); return players; } @Override - protected List getPlayers(final Server server, final User interactor) { - final List players = super.getPlayers(server, interactor); + protected List getPlayers(final User interactor) { + final List players = super.getPlayers(interactor); players.add("**"); players.add("*"); return players; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsToggleCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsToggleCommand.java index 5d8ff2a89dd..617664024c8 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsToggleCommand.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsToggleCommand.java @@ -74,7 +74,7 @@ protected void toggleOtherPlayers(final Server server, final CommandSource sende protected List getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) { if (args.length == 1) { if (user.isAuthorized(othersPermission)) { - return getPlayers(server, user); + return getPlayers(user); } else { return Lists.newArrayList("enable", "disable"); } @@ -88,7 +88,7 @@ protected List getTabCompleteOptions(final Server server, final User use @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1) { - return getPlayers(server, sender); + return getPlayers(sender); } else if (args.length == 2) { return Lists.newArrayList("enable", "disable"); } else { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsTreeCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsTreeCommand.java new file mode 100644 index 00000000000..acac81251b5 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsTreeCommand.java @@ -0,0 +1,94 @@ +package com.earth2me.essentials.commands; + +import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.User; +import org.bukkit.Server; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class EssentialsTreeCommand extends EssentialsCommand { + private final Map nodes = new HashMap<>(); + private final List publicNodes = new ArrayList<>(); + + public EssentialsTreeCommand(final String command) { + super(command); + } + + protected void registerNode(final EssentialsTreeNode node) { + for (final String name : node.names()) { + nodes.put(name, node); + if (!node.hidden()) { + publicNodes.add(name); + } + } + + node.setEssentials(ess); + node.setEssentialsModule(module); + node.setParent(this); + } + + public void runDefault(final User user, final String commandLabel) throws Exception { + runDefault(user.getSource(), commandLabel); + } + + public void runDefault(final CommandSource sender, final String commandLabel) throws Exception { + throw new NotEnoughArgumentsException(); + } + + @Override + protected final void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception { + run(server, user.getSource(), commandLabel, args); + } + + @Override + protected final void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception { + if (args.length == 0) { + if (sender.isPlayer() && sender.getUser() != null) { + runDefault((User) sender.getUser(), commandLabel); + } else { + runDefault(sender, commandLabel); + } + } else { + final EssentialsTreeNode node = nodes.get(args[0]); + if (node != null) { + final String[] newArgs = Arrays.copyOfRange(args, 1, args.length); + if (sender.isPlayer() && sender.getUser() != null) { + node.run((User) sender.getUser(), commandLabel, newArgs); + } else { + node.run(sender, commandLabel, newArgs); + } + } else { + throw new NotEnoughArgumentsException(); + } + } + } + + @Override + protected final List getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) { + return getTabCompleteOptions(server, user.getSource(), commandLabel, args); + } + + @Override + protected final List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { + if (args.length == 1) { + return publicNodes; + } + + final EssentialsTreeNode node = nodes.get(args[0]); + if (node != null) { + final String[] newArgs = Arrays.copyOfRange(args, 1, args.length); + if (sender.isPlayer() && sender.getUser() != null) { + return node.tabComplete((User) sender.getUser(), commandLabel, newArgs); + } else { + return node.tabComplete(sender, commandLabel, newArgs); + } + } + + return Collections.emptyList(); + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsTreeNode.java b/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsTreeNode.java new file mode 100644 index 00000000000..17247f3fdf4 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsTreeNode.java @@ -0,0 +1,62 @@ +package com.earth2me.essentials.commands; + +import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.IEssentialsModule; +import com.earth2me.essentials.User; +import net.ess3.api.IEssentials; +import net.ess3.api.TranslatableException; + +import java.util.List; + +public class EssentialsTreeNode { + private final String[] names; + private final boolean hidden; + protected transient IEssentials ess; + protected transient IEssentialsModule module; + protected transient EssentialsTreeCommand parent; + + public EssentialsTreeNode(final String... names) { + this(names, false); + } + + public EssentialsTreeNode(final String[] names, final boolean hidden) { + this.names = names; + this.hidden = hidden; + } + + protected void setEssentials(final IEssentials ess) { + this.ess = ess; + } + + protected void setEssentialsModule(final IEssentialsModule module) { + this.module = module; + } + + protected void setParent(final EssentialsTreeCommand parent) { + this.parent = parent; + } + + public String[] names() { + return names; + } + + public boolean hidden() { + return hidden; + } + + protected void run(final User user, final String commandLabel, final String[] args) throws Exception { + run(user.getSource(), commandLabel, args); + } + + protected void run(final CommandSource sender, final String commandLabel, final String[] args) throws Exception { + throw new TranslatableException("onlyPlayers", commandLabel); + } + + protected List tabComplete(final User user, final String commandLabel, final String[] args) { + return tabComplete(user.getSource(), commandLabel, args); + } + + protected List tabComplete(final CommandSource sender, final String commandLabel, final String[] args) { + return parent.getPlayers(sender); + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/CleanupCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/CleanupCommand.java new file mode 100644 index 00000000000..b7eba8ff058 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/CleanupCommand.java @@ -0,0 +1,86 @@ +package com.earth2me.essentials.commands.essentials; + +import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.User; +import com.earth2me.essentials.commands.EssentialsCommand; +import com.earth2me.essentials.commands.EssentialsTreeNode; +import com.earth2me.essentials.utils.DateUtil; +import com.earth2me.essentials.utils.FloatUtil; +import com.earth2me.essentials.utils.NumberUtil; +import com.google.common.collect.Lists; + +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +public class CleanupCommand extends EssentialsTreeNode { + + public CleanupCommand() { + super("cleanup"); + } + + @Override + protected void run(final CommandSource sender, final String commandLabel, final String[] args) throws Exception { + if (args.length < 1 || !NumberUtil.isInt(args[0])) { + sender.sendMessage("This sub-command will delete users who haven't logged in in the last days."); + sender.sendMessage("Optional parameters define the minimum amount required to prevent deletion."); + sender.sendMessage("Unless you define larger default values, this command will ignore people who have more than 0 money/homes."); + throw new Exception("/" + commandLabel + " cleanup [money] [homes]"); + } + + sender.sendTl("cleaning"); + + final long daysArg = Long.parseLong(args[0]); + final double moneyArg = args.length >= 2 ? FloatUtil.parseDouble(args[1].replaceAll("[^0-9.]", "")) : 0; + final int homesArg = args.length >= 3 && NumberUtil.isInt(args[2]) ? Integer.parseInt(args[2]) : 0; + + ess.runTaskAsynchronously(() -> { + final long currTime = System.currentTimeMillis(); + for (final UUID u : ess.getUsers().getAllUserUUIDs()) { + final User user = ess.getUsers().loadUncachedUser(u); + if (user == null) { + continue; + } + + long lastLog = user.getLastLogout(); + if (lastLog == 0) { + lastLog = user.getLastLogin(); + } + if (lastLog == 0) { + user.setLastLogin(currTime); + } + + if (user.isNPC()) { + continue; + } + + final long timeDiff = currTime - lastLog; + final long milliDays = daysArg * 24L * 60L * 60L * 1000L; + final int homeCount = user.getHomes().size(); + final double moneyCount = user.getMoney().doubleValue(); + + if (lastLog == 0 || timeDiff < milliDays || homeCount > homesArg || moneyCount > moneyArg) { + continue; + } + + if (ess.getSettings().isDebug()) { + ess.getLogger().info("Deleting user: " + user.getName() + " Money: " + moneyCount + " Homes: " + homeCount + " Last seen: " + DateUtil.formatDateDiff(lastLog)); + } + + user.reset(); + } + sender.sendTl("cleaned"); + }); + } + + @Override + protected List tabComplete(CommandSource sender, String commandLabel, String[] args) { + if (args.length == 1) { + return EssentialsCommand.COMMON_DURATIONS; + } else if (args.length == 2 || args.length == 3) { + return Lists.newArrayList("-1", "0"); + } + + return Collections.emptyList(); + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/CommandMapCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/CommandMapCommand.java new file mode 100644 index 00000000000..806bc501c88 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/CommandMapCommand.java @@ -0,0 +1,25 @@ +package com.earth2me.essentials.commands.essentials; + +import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.commands.EssentialsTreeNode; + +import java.util.Map; + +public class CommandMapCommand extends EssentialsTreeNode { + public CommandMapCommand() { + super("cmd", "commands"); + } + + @Override + protected void run(CommandSource sender, String commandLabel, String[] args) throws Exception { + if (ess.getAlternativeCommandsHandler().disabledCommands().size() == 0) { + sender.sendTl("blockListEmpty"); + return; + } + + sender.sendTl("blockList"); + for (final Map.Entry entry : ess.getAlternativeCommandsHandler().disabledCommands().entrySet()) { + sender.sendMessage(entry.getKey() + " => " + entry.getValue()); + } + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DebugCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DebugCommand.java new file mode 100644 index 00000000000..c5f568b0586 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DebugCommand.java @@ -0,0 +1,42 @@ +package com.earth2me.essentials.commands.essentials; + +import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.commands.EssentialsTreeNode; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class DebugCommand extends EssentialsTreeNode { + public DebugCommand() { + super("debug", "verbose"); + } + + @Override + protected void run(CommandSource sender, String commandLabel, String[] args) throws Exception { + final boolean newDebugState; + + if (args.length > 0) { + if (args[0].equalsIgnoreCase("on") || args[0].equalsIgnoreCase("true")) { + newDebugState = true; + } else if (args[0].equalsIgnoreCase("off") || args[0].equalsIgnoreCase("false")) { + newDebugState = false; + } else { + newDebugState = !ess.getSettings().isDebug(); + } + } else { + newDebugState = !ess.getSettings().isDebug(); + } + + ess.getSettings().setDebug(newDebugState); + sender.sendMessage("Essentials " + ess.getDescription().getVersion() + " debug mode " + (ess.getSettings().isDebug() ? "enabled" : "disabled")); + } + + @Override + protected List tabComplete(CommandSource sender, String commandLabel, String[] args) { + if (args.length == 1) { + return Arrays.asList("on", "off"); + } + return Collections.emptyList(); + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DumpCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DumpCommand.java new file mode 100644 index 00000000000..f4fd2b81c73 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DumpCommand.java @@ -0,0 +1,289 @@ +package com.earth2me.essentials.commands.essentials; + +import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.commands.EssentialsTreeNode; +import com.earth2me.essentials.economy.EconomyLayer; +import com.earth2me.essentials.economy.EconomyLayers; +import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.utils.CommandMapUtil; +import com.earth2me.essentials.utils.DateUtil; +import com.earth2me.essentials.utils.PasteUtil; +import com.earth2me.essentials.utils.VersionUtil; +import com.google.common.collect.Lists; +import com.google.gson.JsonArray; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.ess3.provider.KnownCommandsProvider; +import net.ess3.provider.OnlineModeProvider; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import static com.earth2me.essentials.I18n.tlLiteral; + +public class DumpCommand extends EssentialsTreeNode { + public DumpCommand() { + super("dump"); + } + + @Override + protected void run(CommandSource sender, String commandLabel, String[] args) throws Exception { + sender.sendTl("dumpCreating"); + + final JsonObject dump = new JsonObject(); + + final JsonObject meta = new JsonObject(); + meta.addProperty("timestamp", Instant.now().toEpochMilli()); + meta.addProperty("sender", sender.getPlayer() != null ? sender.getPlayer().getName() : null); + meta.addProperty("senderUuid", sender.getPlayer() != null ? sender.getPlayer().getUniqueId().toString() : null); + dump.add("meta", meta); + + final JsonObject serverData = new JsonObject(); + serverData.addProperty("bukkit-version", Bukkit.getBukkitVersion()); + serverData.addProperty("server-version", Bukkit.getVersion()); + serverData.addProperty("server-brand", Bukkit.getName()); + serverData.addProperty("online-mode", ess.provider(OnlineModeProvider.class).getOnlineModeString()); + final JsonObject supportStatus = new JsonObject(); + final VersionUtil.SupportStatus status = VersionUtil.getServerSupportStatus(); + supportStatus.addProperty("status", status.name()); + supportStatus.addProperty("supported", status.isSupported()); + supportStatus.addProperty("trigger", VersionUtil.getSupportStatusClass()); + serverData.add("support-status", supportStatus); + dump.add("server-data", serverData); + + final JsonObject environment = new JsonObject(); + environment.addProperty("java-version", System.getProperty("java.version")); + environment.addProperty("operating-system", System.getProperty("os.name")); + environment.addProperty("uptime", DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime())); + environment.addProperty("allocated-memory", (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB"); + dump.add("environment", environment); + + final JsonObject essData = new JsonObject(); + essData.addProperty("version", ess.getDescription().getVersion()); + final JsonObject updateData = new JsonObject(); + updateData.addProperty("id", ess.getUpdateChecker().getVersionIdentifier()); + updateData.addProperty("branch", ess.getUpdateChecker().getVersionBranch()); + updateData.addProperty("dev", ess.getUpdateChecker().isDevBuild()); + essData.add("update-data", updateData); + final JsonObject econLayer = new JsonObject(); + econLayer.addProperty("enabled", !ess.getSettings().isEcoDisabled()); + econLayer.addProperty("selected-layer", EconomyLayers.isLayerSelected()); + final EconomyLayer layer = EconomyLayers.getSelectedLayer(); + econLayer.addProperty("name", layer == null ? "null" : layer.getName()); + econLayer.addProperty("layer-version", layer == null ? "null" : layer.getPluginVersion()); + econLayer.addProperty("backend-name", layer == null ? "null" : layer.getBackendName()); + essData.add("economy-layer", econLayer); + final JsonArray addons = new JsonArray(); + final JsonArray plugins = new JsonArray(); + final ArrayList alphabetical = new ArrayList<>(); + Collections.addAll(alphabetical, Bukkit.getPluginManager().getPlugins()); + alphabetical.sort(Comparator.comparing(o -> o.getName().toUpperCase(Locale.ENGLISH))); + for (final Plugin plugin : alphabetical) { + final JsonObject pluginData = new JsonObject(); + final PluginDescriptionFile info = plugin.getDescription(); + final String name = info.getName(); + + pluginData.addProperty("name", name); + pluginData.addProperty("version", info.getVersion()); + pluginData.addProperty("description", info.getDescription()); + pluginData.addProperty("main", info.getMain()); + pluginData.addProperty("enabled", plugin.isEnabled()); + pluginData.addProperty("official", plugin == ess || VersionUtil.officialPlugins.contains(name)); + pluginData.addProperty("unsupported", VersionUtil.warnPlugins.contains(name)); + + final JsonArray authors = new JsonArray(); + for (final String author : info.getAuthors()) { + authors.add(author == null ? JsonNull.INSTANCE : new JsonPrimitive(author)); + } + pluginData.add("authors", authors); + + if (name.startsWith("Essentials") && !name.equals("Essentials")) { + addons.add(pluginData); + } + plugins.add(pluginData); + } + essData.add("addons", addons); + dump.add("ess-data", essData); + dump.add("plugins", plugins); + + final List files = new ArrayList<>(); + files.add(new PasteUtil.PasteFile("dump.json", dump.toString())); + + final Plugin essDiscord = Bukkit.getPluginManager().getPlugin("EssentialsDiscord"); + final Plugin essDiscordLink = Bukkit.getPluginManager().getPlugin("EssentialsDiscordLink"); + final Plugin essSpawn = Bukkit.getPluginManager().getPlugin("EssentialsSpawn"); + + final Map knownCommandsCopy = new HashMap<>(ess.provider(KnownCommandsProvider.class).getKnownCommands()); + final Map disabledCommandsCopy = new HashMap<>(ess.getAlternativeCommandsHandler().disabledCommands()); + + // Further operations will be heavy IO + ess.runTaskAsynchronously(() -> { + boolean config = false; + boolean discord = false; + boolean kits = false; + boolean log = false; + boolean worth = false; + boolean tpr = false; + boolean spawns = false; + boolean commands = false; + for (final String arg : args) { + if (arg.equals("*") || arg.equalsIgnoreCase("all")) { + config = true; + discord = true; + kits = true; + log = true; + worth = true; + tpr = true; + spawns = true; + commands = true; + break; + } else if (arg.equalsIgnoreCase("config")) { + config = true; + } else if (arg.equalsIgnoreCase("discord")) { + discord = true; + } else if (arg.equalsIgnoreCase("kits")) { + kits = true; + } else if (arg.equalsIgnoreCase("log")) { + log = true; + } else if (arg.equalsIgnoreCase("worth")) { + worth = true; + } else if (arg.equalsIgnoreCase("tpr")) { + tpr = true; + } else if (arg.equalsIgnoreCase("spawns")) { + spawns = true; + } else if (arg.equalsIgnoreCase("commands")) { + commands = true; + } + } + + if (config) { + try { + files.add(new PasteUtil.PasteFile("config.yml", new String(Files.readAllBytes(ess.getSettings().getConfigFile().toPath()), StandardCharsets.UTF_8))); + } catch (IOException e) { + sender.sendTl("dumpErrorUpload", "config.yml", e.getMessage()); + } + } + + if (discord && essDiscord != null) { + try { + files.add(new PasteUtil.PasteFile("discord-config.yml", + new String(Files.readAllBytes(essDiscord.getDataFolder().toPath().resolve("config.yml")), StandardCharsets.UTF_8) + .replaceAll("[A-Za-z\\d]{24}\\.[\\w-]{6}\\.[\\w-]{27}", ""))); + } catch (IOException e) { + sender.sendTl("dumpErrorUpload", "discord-config.yml", e.getMessage()); + } + + if (essDiscordLink != null) { + try { + files.add(new PasteUtil.PasteFile("discord-link-config.yml", + new String(Files.readAllBytes(essDiscordLink.getDataFolder().toPath().resolve("config.yml")), StandardCharsets.UTF_8))); + } catch (IOException e) { + sender.sendTl("dumpErrorUpload", "discord-link-config.yml", e.getMessage()); + } + } + } + + if (kits) { + try { + files.add(new PasteUtil.PasteFile("kits.yml", new String(Files.readAllBytes(ess.getKits().getFile().toPath()), StandardCharsets.UTF_8))); + } catch (IOException e) { + sender.sendTl("dumpErrorUpload", "kits.yml", e.getMessage()); + } + } + + if (log) { + try { + files.add(new PasteUtil.PasteFile("latest.log", new String(Files.readAllBytes(Paths.get("logs", "latest.log")), StandardCharsets.UTF_8) + .replaceAll("(?m)^\\[\\d\\d:\\d\\d:\\d\\d] \\[.+/(?:DEBUG|TRACE)]: .+\\s(?:[A-Za-z.]+:.+\\s(?:\\t.+\\s)*)?\\s*(?:\"[A-Za-z]+\" : .+[\\s}\\]]+)*", "") + .replaceAll("(?:[0-9]{1,3}\\.){3}[0-9]{1,3}", ""))); + } catch (IOException e) { + sender.sendTl("dumpErrorUpload", "latest.log", e.getMessage()); + } + } + + if (worth) { + try { + files.add(new PasteUtil.PasteFile("worth.yml", new String(Files.readAllBytes(ess.getWorth().getFile().toPath()), StandardCharsets.UTF_8))); + } catch (IOException e) { + sender.sendTl("dumpErrorUpload", "worth.yml", e.getMessage()); + } + } + + if (tpr) { + try { + files.add(new PasteUtil.PasteFile("tpr.yml", new String(Files.readAllBytes(ess.getRandomTeleport().getFile().toPath()), StandardCharsets.UTF_8))); + } catch (IOException e) { + sender.sendTl("dumpErrorUpload", "tpr.yml", e.getMessage()); + } + } + + if (spawns && essSpawn != null) { + try { + files.add(new PasteUtil.PasteFile("spawn.yml", new String(Files.readAllBytes(ess.getDataFolder().toPath().resolve("spawn.yml")), StandardCharsets.UTF_8))); + } catch (IOException e) { + sender.sendTl("dumpErrorUpload", "spawn.yml", e.getMessage()); + } + } + + if (commands) { + try { + files.add(new PasteUtil.PasteFile("commands.yml", new String(Files.readAllBytes(Paths.get("commands.yml")), StandardCharsets.UTF_8))); + files.add(new PasteUtil.PasteFile("commandmap.json", CommandMapUtil.toJsonPretty(ess, knownCommandsCopy))); + files.add(new PasteUtil.PasteFile("commandoverride.json", disabledCommandsCopy.toString())); + } catch (IOException e) { + sender.sendTl("dumpErrorUpload", "commands.yml", e.getMessage()); + } + } + + final CompletableFuture future = PasteUtil.createPaste(files); + future.thenAccept(result -> { + if (result != null) { + final String dumpUrl = "https://essentialsx.net/dump?bytebin=" + result.getPasteId(); + sender.sendTl("dumpUrl", dumpUrl); + // pastes.dev doesn't support deletion keys + //sender.sendTl("dumpDeleteKey", result.getDeletionKey()); + if (sender.isPlayer()) { + ess.getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("dumpConsoleUrl", dumpUrl))); + // pastes.dev doesn't support deletion keys + //ess.getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("dumpDeleteKey", result.getDeletionKey()))); + } + } + files.clear(); + }); + future.exceptionally(throwable -> { + sender.sendTl("dumpError", throwable.getMessage()); + return null; + }); + }); + } + + @Override + protected List tabComplete(CommandSource sender, String commandLabel, String[] args) { + final List list = Lists.newArrayList("config", "kits", "log", "discord", "worth", "tpr", "spawns", "commands", "all"); + for (String arg : args) { + if (arg.equals("*") || arg.equalsIgnoreCase("all")) { + list.clear(); + return list; + } + list.remove(arg.toLowerCase(Locale.ENGLISH)); + } + return list; + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/HomesCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/HomesCommand.java new file mode 100644 index 00000000000..5a84c401c3f --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/HomesCommand.java @@ -0,0 +1,106 @@ +package com.earth2me.essentials.commands.essentials; + +import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.User; +import com.earth2me.essentials.commands.EssentialsTreeNode; +import com.google.common.collect.Lists; +import net.ess3.api.TranslatableException; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; + +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +public class HomesCommand extends EssentialsTreeNode { + private static final String HOMES_USAGE = "/ homes (fix | delete [world])"; + + public HomesCommand() { + super("homes"); + } + + @Override + protected void run(final CommandSource sender, final String commandLabel, final String[] args) throws Exception { + if (args.length < 1) { + sender.sendMessage("This sub-command provides a utility to mass-delete homes based on user options:"); + sender.sendMessage("Use \"fix\" to delete all homes inside non-existent or unloaded worlds."); + sender.sendMessage("Use \"delete\" to delete all existing homes."); + sender.sendMessage("Use \"delete \" to delete all homes inside a specific world."); + throw new Exception(HOMES_USAGE); + } + + switch (args[0]) { + case "fix": + sender.sendTl("fixingHomes"); + ess.runTaskAsynchronously(() -> { + for (final UUID u : ess.getUsers().getAllUserUUIDs()) { + final User user = ess.getUsers().loadUncachedUser(u); + if (user == null) { + continue; + } + for (String homeName : user.getHomes()) { + try { + if (user.getHome(homeName) == null) { + user.delHome(homeName); + } + } catch (Exception e) { + ess.getLogger().info("Unable to delete home " + homeName + " for " + user.getName()); + } + } + } + sender.sendTl("fixedHomes"); + }); + break; + case "delete": + final boolean filterByWorld = args.length >= 2; + if (filterByWorld && Bukkit.getWorld(args[1]) == null) { + throw new TranslatableException("invalidWorld"); + } + if (filterByWorld) { + sender.sendTl("deletingHomesWorld", args[1]); + } else { + sender.sendTl("deletingHomes"); + } + ess.runTaskAsynchronously(() -> { + for (final UUID u : ess.getUsers().getAllUserUUIDs()) { + final User user = ess.getUsers().loadUncachedUser(u); + if (user == null) { + continue; + } + for (String homeName : user.getHomes()) { + try { + final Location home = user.getHome(homeName); + if (!filterByWorld || home != null && home.getWorld() != null && home.getWorld().getName().equals(args[1])) { + user.delHome(homeName); + } + } catch (Exception e) { + ess.getLogger().info("Unable to delete home " + homeName + " for " + user.getName()); + } + } + } + + if (filterByWorld) { + sender.sendTl("deletedHomesWorld", args[1]); + } else { + sender.sendTl("deletedHomes"); + } + }); + break; + default: + throw new Exception(HOMES_USAGE); + } + } + + @Override + protected List tabComplete(CommandSource sender, String commandLabel, String[] args) { + if (args.length == 1) { + return Lists.newArrayList("fix", "delete"); + } else if (args.length == 2 && args[0].equalsIgnoreCase("delete")) { + return Bukkit.getWorlds().stream().map(World::getName).collect(Collectors.toList()); + } + + return Collections.emptyList(); + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/ItemTestCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/ItemTestCommand.java new file mode 100644 index 00000000000..1b876265bc9 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/ItemTestCommand.java @@ -0,0 +1,65 @@ +package com.earth2me.essentials.commands.essentials; + +import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.commands.EssentialsTreeNode; +import com.earth2me.essentials.craftbukkit.Inventories; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public class ItemTestCommand extends EssentialsTreeNode { + public ItemTestCommand() { + super(new String[]{"itemtest"}, true); + } + + @Override + protected void run(final CommandSource sender, final String commandLabel, final String[] args) throws Exception { + if (!sender.isAuthorized("essentials.itemtest") || args.length < 1 || !sender.isPlayer()) { + return; + } + + final Player player = sender.getPlayer(); + assert player != null; + + switch (args[0]) { + case "slot": { + if (args.length < 2) { + return; + } + player.getInventory().setItem(Integer.parseInt(args[1]), new ItemStack(Material.DIRT)); + break; + } + case "overfill": { + sender.sendMessage(Inventories.addItem(player, 42, false, new ItemStack(Material.DIAMOND_SWORD, 1), new ItemStack(Material.DIRT, 32), new ItemStack(Material.DIRT, 32)).toString()); + break; + } + case "overfill2": { + if (args.length < 3) { + return; + } + final boolean armor = Boolean.parseBoolean(args[1]); + final boolean add = Boolean.parseBoolean(args[2]); + final ItemStack[] items = new ItemStack[]{new ItemStack(Material.DIAMOND_SWORD, 1), new ItemStack(Material.DIRT, 32), new ItemStack(Material.DIRT, 32), new ItemStack(Material.DIAMOND_HELMET, 4), new ItemStack(Material.CHAINMAIL_LEGGINGS, 1)}; + if (Inventories.hasSpace(player, 0, armor, items)) { + if (add) { + sender.sendMessage(Inventories.addItem(player, 0, armor, items).toString()); + } + sender.sendMessage("SO MUCH SPACE!"); + } else { + sender.sendMessage("No space!"); + } + break; + } + case "remove": { + if (args.length < 2) { + return; + } + Inventories.removeItemExact(player, new ItemStack(Material.PUMPKIN, 1), Boolean.parseBoolean(args[1])); + break; + } + default: { + break; + } + } + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/MooCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/MooCommand.java new file mode 100644 index 00000000000..2908edc058c --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/MooCommand.java @@ -0,0 +1,51 @@ +package com.earth2me.essentials.commands.essentials; + +import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.commands.EssentialsTreeNode; +import com.earth2me.essentials.utils.RegistryUtil; +import com.google.common.collect.Lists; +import org.bukkit.Sound; +import org.bukkit.entity.Player; + +import java.util.Collections; +import java.util.List; + +public class MooCommand extends EssentialsTreeNode { + private static final Sound MOO_SOUND = RegistryUtil.valueOf(Sound.class, "COW_IDLE", "ENTITY_COW_MILK"); + private static final String[] CONSOLE_MOO = new String[] {" (__)", " (oo)", " /------\\/", " / | ||", " * /\\---/\\", " ~~ ~~", "....\"Have you mooed today?\"..."}; + private static final String[] PLAYER_MOO = new String[] {" (__)", " (oo)", " /------\\/", " / | | |", " * /\\---/\\", " ~~ ~~", "....\"Have you mooed today?\"..."}; + + public MooCommand() { + super(new String[]{"moo"}, true); + } + + @Override + protected void run(final CommandSource sender, final String commandLabel, final String[] args) { + if (args.length == 1 && args[0].equals("moo")) { + for (final String s : CONSOLE_MOO) { + ess.getLogger().info(s); + } + for (final Player player : ess.getOnlinePlayers()) { + player.sendMessage(PLAYER_MOO); + player.playSound(player.getLocation(), MOO_SOUND, 1, 1.0f); + } + } else { + if (sender.isPlayer()) { + sender.getSender().sendMessage(PLAYER_MOO); + final Player player = sender.getPlayer(); + player.playSound(player.getLocation(), MOO_SOUND, 1, 1.0f); + + } else { + sender.getSender().sendMessage(CONSOLE_MOO); + } + } + } + + @Override + protected List tabComplete(CommandSource sender, String commandLabel, String[] args) { + if (args.length == 1) { + return Lists.newArrayList("moo"); + } + return Collections.emptyList(); + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/NyanCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/NyanCommand.java new file mode 100644 index 00000000000..9e42fc503e9 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/NyanCommand.java @@ -0,0 +1,90 @@ +package com.earth2me.essentials.commands.essentials; + +import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.commands.EssentialsTreeNode; +import com.earth2me.essentials.utils.RegistryUtil; +import com.google.common.collect.ImmutableMap; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.Collection; +import java.util.Map; +import java.util.function.Supplier; + +public class NyanCommand extends EssentialsTreeNode { + private static final Sound NOTE_HARP = RegistryUtil.valueOf(Sound.class, "BLOCK_NOTE_BLOCK_HARP", "BLOCK_NOTE_HARP", "NOTE_PIANO"); + private static final String NYAN_TUNE = "1D#,1E,2F#,,2A#,1E,1D#,1E,2F#,2B,2D#,2E,2D#,2A#,2B,,2F#,,1D#,1E,2F#,2B,2C#,2A#,2B,2C#,2E,2D#,2E,2C#,,2F#,,2G#,,1D,1D#,,1C#,1D,1C#,1B,,1B,,1C#,,1D,,1D,1C#,1B,1C#,1D#,2F#,2G#,1D#,2F#,1C#,1D#,1B,1C#,1B,1D#,,2F#,,2G#,1D#,2F#,1C#,1D#,1B,1D,1D#,1D,1C#,1B,1C#,1D,,1B,1C#,1D#,2F#,1C#,1D,1C#,1B,1C#,,1B,,1C#,,2F#,,2G#,,1D,1D#,,1C#,1D,1C#,1B,,1B,,1C#,,1D,,1D,1C#,1B,1C#,1D#,2F#,2G#,1D#,2F#,1C#,1D#,1B,1C#,1B,1D#,,2F#,,2G#,1D#,2F#,1C#,1D#,1B,1D,1D#,1D,1C#,1B,1C#,1D,,1B,1C#,1D#,2F#,1C#,1D,1C#,1B,1C#,,1B,,1B,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1B,,"; + + private transient TuneRunnable currentTune = null; + + public NyanCommand() { + super(new String[]{"nyan", "nya"}, true); + } + + @Override + protected void run(final CommandSource sender, final String commandLabel, final String[] args) throws Exception { + if (currentTune != null) { + currentTune.cancel(); + } + + currentTune = new TuneRunnable(NYAN_TUNE, NOTE_HARP, ess::getOnlinePlayers); + currentTune.runTaskTimer(ess, 20, 2); + } + + private static class TuneRunnable extends BukkitRunnable { + private static final Map noteMap = ImmutableMap.builder() + .put("1F#", 0.5f) + .put("1G", 0.53f) + .put("1G#", 0.56f) + .put("1A", 0.6f) + .put("1A#", 0.63f) + .put("1B", 0.67f) + .put("1C", 0.7f) + .put("1C#", 0.76f) + .put("1D", 0.8f) + .put("1D#", 0.84f) + .put("1E", 0.9f) + .put("1F", 0.94f) + .put("2F#", 1.0f) + .put("2G", 1.06f) + .put("2G#", 1.12f) + .put("2A", 1.18f) + .put("2A#", 1.26f) + .put("2B", 1.34f) + .put("2C", 1.42f) + .put("2C#", 1.5f) + .put("2D", 1.6f) + .put("2D#", 1.68f) + .put("2E", 1.78f) + .put("2F", 1.88f) + .build(); + + private final String[] tune; + private final Sound sound; + private final Supplier> players; + private int i = 0; + + TuneRunnable(final String tuneStr, final Sound sound, final Supplier> players) { + this.tune = tuneStr.split(","); + this.sound = sound; + this.players = players; + } + + @Override + public void run() { + final String note = tune[i]; + i++; + if (i >= tune.length) { + cancel(); + } + if (note == null || note.isEmpty()) { + return; + } + + for (final Player onlinePlayer : players.get()) { + onlinePlayer.playSound(onlinePlayer.getLocation(), sound, 1, noteMap.get(note)); + } + } + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/ReloadCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/ReloadCommand.java new file mode 100644 index 00000000000..999ad8ac2b5 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/ReloadCommand.java @@ -0,0 +1,17 @@ +package com.earth2me.essentials.commands.essentials; + +import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.commands.EssentialsTreeNode; + +public class ReloadCommand extends EssentialsTreeNode { + + public ReloadCommand() { + super("reload"); + } + + @Override + protected void run(final CommandSource sender, final String commandLabel, final String[] args) throws Exception { + ess.reload(); + sender.sendTl("essentialsReload", ess.getDescription().getVersion()); + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/UsermapCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/UsermapCommand.java new file mode 100644 index 00000000000..a7f80ec8c1c --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/UsermapCommand.java @@ -0,0 +1,107 @@ +package com.earth2me.essentials.commands.essentials; + +import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.EssentialsUpgrade; +import com.earth2me.essentials.commands.EssentialsTreeNode; +import com.earth2me.essentials.userstorage.ModernUserMap; +import com.google.common.collect.Lists; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Level; + +public class UsermapCommand extends EssentialsTreeNode { + + public UsermapCommand() { + super("usermap"); + } + + @Override + protected void run(final CommandSource sender, final String commandLabel, final String[] args) throws Exception { + if (!sender.isAuthorized("essentials.usermap")) { + return; + } + + final ModernUserMap userMap = (ModernUserMap) ess.getUsers(); + sender.sendTl("usermapSize", userMap.getCachedCount(), userMap.getUserCount(), ess.getSettings().getMaxUserCacheCount()); + if (args.length > 0) { + if (args[0].equals("full")) { + for (final Map.Entry entry : userMap.getNameCache().entrySet()) { + sender.sendTl("usermapEntry", entry.getKey(), entry.getValue().toString()); + } + } else if (args[0].equals("purge")) { + final boolean seppuku = args.length > 1 && args[1].equals("iknowwhatimdoing"); + + sender.sendTl("usermapPurge", String.valueOf(seppuku)); + + final Set uuids = new HashSet<>(ess.getUsers().getAllUserUUIDs()); + ess.runTaskAsynchronously(() -> { + final File userdataFolder = new File(ess.getDataFolder(), "userdata"); + final File backupFolder = new File(ess.getDataFolder(), "userdata-npc-backup-boogaloo-" + System.currentTimeMillis()); + + if (!userdataFolder.isDirectory()) { + ess.getLogger().warning("Missing userdata folder, aborting usermap purge."); + return; + } + + if (seppuku && !backupFolder.mkdir()) { + ess.getLogger().warning("Unable to create backup folder, aborting usermap purge."); + return; + } + + int total = 0; + final File[] files = userdataFolder.listFiles(EssentialsUpgrade.YML_FILTER); + if (files != null) { + for (final File file : files) { + try { + final String fileName = file.getName(); + final UUID uuid = UUID.fromString(fileName.substring(0, fileName.length() - 4)); + if (!uuids.contains(uuid)) { + total++; + ess.getLogger().warning("Found orphaned userdata file: " + file.getName()); + if (seppuku) { + try { + com.google.common.io.Files.move(file, new File(backupFolder, file.getName())); + } catch (IOException e) { + ess.getLogger().log(Level.WARNING, "Unable to move orphaned userdata file: " + file.getName(), e); + } + } + } + } catch (IllegalArgumentException ignored) { + } + } + } + ess.getLogger().info("Found " + total + " orphaned userdata files."); + }); + } else if (args[0].equalsIgnoreCase("cache")) { + sender.sendTl("usermapKnown", ess.getUsers().getAllUserUUIDs().size(), ess.getUsers().getNameCache().size()); + } else { + try { + final UUID uuid = UUID.fromString(args[0]); + for (final Map.Entry entry : userMap.getNameCache().entrySet()) { + if (entry.getValue().equals(uuid)) { + sender.sendTl("usermapEntry", entry.getKey(), args[0]); + } + } + } catch (IllegalArgumentException ignored) { + final String sanitizedName = userMap.getSanitizedName(args[0]); + sender.sendTl("usermapEntry", sanitizedName, userMap.getNameCache().get(sanitizedName).toString()); + } + } + } + } + + @Override + protected List tabComplete(CommandSource sender, String commandLabel, String[] args) { + if (args.length == 1) { + return Lists.newArrayList("full", "purge", "cache"); + } + return Collections.emptyList(); + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java new file mode 100644 index 00000000000..8287691ad63 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java @@ -0,0 +1,155 @@ +package com.earth2me.essentials.commands.essentials; + +import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.commands.EssentialsTreeNode; +import com.earth2me.essentials.economy.EconomyLayer; +import com.earth2me.essentials.economy.EconomyLayers; +import com.earth2me.essentials.utils.VersionUtil; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginManager; + +import java.util.Arrays; +import java.util.List; + +public class VersionCommand extends EssentialsTreeNode { + private static final List versionPlugins = Arrays.asList( + "Vault", // API + "Reserve", // API + "PlaceholderAPI", // API + "CMI", // potential for issues + "Towny", // past issues; admins should ensure latest + "ChestShop", // past issues; admins should ensure latest + "Citizens", // fires player events + "LuckPerms", // permissions (recommended) + "UltraPermissions", + "PermissionsEx", // permissions (unsupported) + "GroupManager", // permissions (unsupported) + "bPermissions", // permissions (unsupported) + "DiscordSRV", // potential for issues if EssentialsXDiscord is installed + + // Chat signing bypass plugins that can potentially break EssentialsChat + "AntiPopup", + "NoChatReports", + "NoEncryption" + ); + + public VersionCommand() { + super("version"); + } + + @Override + protected void run(CommandSource sender, String commandLabel, String[] args) throws Exception { + if (sender.isPlayer() && !ess.getUser(sender.getPlayer()).isAuthorized("essentials.version")) return; + + boolean isMismatched = false; + boolean isVaultInstalled = false; + boolean isUnsupported = false; + final VersionUtil.SupportStatus supportStatus = VersionUtil.getServerSupportStatus(); + final PluginManager pm = Bukkit.getPluginManager(); + final String essVer = pm.getPlugin("Essentials").getDescription().getVersion(); + + final String serverMessageKey; + if (supportStatus.isSupported()) { + serverMessageKey = "versionOutputFine"; + } else if (supportStatus == VersionUtil.SupportStatus.UNSTABLE) { + serverMessageKey = "versionOutputUnsupported"; + } else { + serverMessageKey = "versionOutputWarn"; + } + + sender.sendTl(serverMessageKey, "Server", Bukkit.getBukkitVersion() + " " + Bukkit.getVersion()); + sender.sendTl(serverMessageKey, "Brand", Bukkit.getName()); + sender.sendTl("versionOutputFine", "EssentialsX", essVer); + + for (final Plugin plugin : pm.getPlugins()) { + final PluginDescriptionFile desc = plugin.getDescription(); + String name = desc.getName(); + final String version = desc.getVersion(); + + if (name.startsWith("Essentials") && !name.equalsIgnoreCase("Essentials")) { + if (VersionUtil.officialPlugins.contains(name)) { + name = name.replace("Essentials", "EssentialsX"); + + if (!version.equalsIgnoreCase(essVer)) { + isMismatched = true; + sender.sendTl("versionOutputWarn", name, version); + } else { + sender.sendTl("versionOutputFine", name, version); + } + } else { + sender.sendTl("versionOutputUnsupported", name, version); + isUnsupported = true; + } + } + + if (versionPlugins.contains(name)) { + if (VersionUtil.warnPlugins.contains(name)) { + sender.sendTl("versionOutputUnsupported", name, version); + isUnsupported = true; + } else { + sender.sendTl("versionOutputFine", name, version); + } + } + + if (name.equals("Vault")) isVaultInstalled = true; + } + + final String layer; + if (ess.getSettings().isEcoDisabled()) { + layer = "Disabled"; + } else if (EconomyLayers.isLayerSelected()) { + final EconomyLayer economyLayer = EconomyLayers.getSelectedLayer(); + layer = economyLayer.getName() + " (" + economyLayer.getBackendName() + ")"; + } else { + layer = "None"; + } + sender.sendTl("versionOutputEconLayer", layer); + + if (isMismatched) { + sender.sendTl("versionMismatchAll"); + } + + if (!isVaultInstalled) { + sender.sendTl("versionOutputVaultMissing"); + } + + if (isUnsupported) { + sender.sendTl("versionOutputUnsupportedPlugins"); + } + + switch (supportStatus) { + case NMS_CLEANROOM: + sender.sendComponent(sender.tlComponent("serverUnsupportedCleanroom").color(NamedTextColor.DARK_RED)); + break; + case DANGEROUS_FORK: + sender.sendComponent(sender.tlComponent("serverUnsupportedDangerous").color(NamedTextColor.DARK_RED)); + break; + case STUPID_PLUGIN: + sender.sendComponent(sender.tlComponent("serverUnsupportedDumbPlugins").color(NamedTextColor.DARK_RED)); + break; + case UNSTABLE: + sender.sendComponent(sender.tlComponent("serverUnsupportedMods").color(NamedTextColor.DARK_RED)); + break; + case OUTDATED: + sender.sendComponent(sender.tlComponent("serverUnsupported").color(NamedTextColor.RED)); + break; + case LIMITED: + sender.sendComponent(sender.tlComponent("serverUnsupportedLimitedApi").color(NamedTextColor.RED)); + break; + } + if (VersionUtil.getSupportStatusClass() != null) { + sender.sendComponent(sender.tlComponent("serverUnsupportedClass").color(NamedTextColor.RED)); + } + + sender.sendTl("versionFetching"); + ess.runTaskAsynchronously(() -> { + for (final Component component : ess.getUpdateChecker().getVersionMessages(true, true, sender)) { + sender.sendComponent(component); + } + }); + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java index b821320f891..0caf7ea6659 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java @@ -9,6 +9,8 @@ import org.bukkit.Bukkit; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; @@ -49,6 +51,26 @@ public final class VersionUtil { public static final boolean PRE_FLATTENING = VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_13_0_R01); + public static final List officialPlugins = Arrays.asList( + "EssentialsAntiBuild", + "EssentialsChat", + "EssentialsDiscord", + "EssentialsDiscordLink", + "EssentialsGeoIP", + "EssentialsProtect", + "EssentialsSpawn", + "EssentialsXMPP" + ); + public static final List warnPlugins = Arrays.asList( + "PermissionsEx", + "GroupManager", + "bPermissions", + + // Brain-dead chat signing bypass that break EssentialsChat + "NoChatReports", + "NoEncryption" + ); + private static final Map unsupportedServerClasses; private static final String PFX = make("8(;4>`"); diff --git a/EssentialsSpawn/src/main/java/com/earth2me/essentials/spawn/Commandspawn.java b/EssentialsSpawn/src/main/java/com/earth2me/essentials/spawn/Commandspawn.java index 8b2ebcab9e6..561180daa4f 100644 --- a/EssentialsSpawn/src/main/java/com/earth2me/essentials/spawn/Commandspawn.java +++ b/EssentialsSpawn/src/main/java/com/earth2me/essentials/spawn/Commandspawn.java @@ -61,7 +61,7 @@ protected void run(final Server server, final CommandSource sender, final String @Override protected List getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) { if (args.length == 1 && sender.isAuthorized("essentials.spawn.others")) { - return getPlayers(server, sender); + return getPlayers(sender); } return Collections.emptyList(); } From 99f7a49f28d7d5912645214e6f19817a459d0fea Mon Sep 17 00:00:00 2001 From: RVSkeLe <75245854+RVSkeLe@users.noreply.github.com> Date: Sun, 22 Feb 2026 04:40:39 +0100 Subject: [PATCH 06/37] Improve gc command performance (#6439) Updates /gc to use the new provider methods, allowing Paper to use getTileEntities(false) and avoid unnecessary snapshots while maintaining spigot compatibility. "Benchmarks:" Before, Paper: https://spark.lucko.me/kf2MVRQ5td - 76mspt After, Paper: https://spark.lucko.me/NhZQXtLqvh - 1mspt Spigot: https://spark.lucko.me/toXyvx0yq0 --- .../com/earth2me/essentials/Essentials.java | 5 ++++ .../essentials/commands/Commandgc.java | 4 ++- .../net/ess3/provider/TileEntityProvider.java | 8 ++++++ .../providers/BukkitTileEntityProvider.java | 14 +++++++++++ .../providers/PaperTileEntityProvider.java | 25 +++++++++++++++++++ 5 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 providers/BaseProviders/src/main/java/net/ess3/provider/TileEntityProvider.java create mode 100644 providers/BaseProviders/src/main/java/net/ess3/provider/providers/BukkitTileEntityProvider.java create mode 100644 providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperTileEntityProvider.java diff --git a/Essentials/src/main/java/com/earth2me/essentials/Essentials.java b/Essentials/src/main/java/com/earth2me/essentials/Essentials.java index 8a72fb26991..8c4c25413f7 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Essentials.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Essentials.java @@ -70,6 +70,7 @@ import net.ess3.provider.providers.BlockMetaSpawnerItemProvider; import net.ess3.provider.providers.BukkitMaterialTagProvider; import net.ess3.provider.providers.BukkitSpawnerBlockProvider; +import net.ess3.provider.providers.BukkitTileEntityProvider; import net.ess3.provider.providers.FixedHeightWorldInfoProvider; import net.ess3.provider.providers.FlatSpawnEggProvider; import net.ess3.provider.providers.LegacyBannerDataProvider; @@ -96,6 +97,7 @@ import net.ess3.provider.providers.PaperSerializationProvider; import net.ess3.provider.providers.PaperServerStateProvider; import net.ess3.provider.providers.PaperTickCountProvider; +import net.ess3.provider.providers.PaperTileEntityProvider; import net.ess3.provider.providers.PrehistoricPotionMetaProvider; import net.essentialsx.api.v2.services.BalanceTop; import net.essentialsx.api.v2.services.mail.MailService; @@ -385,6 +387,9 @@ public void onEnable() { // Biome Key Provider providerFactory.registerProvider(PaperBiomeKeyProvider.class); + // Tile Entity Provider + providerFactory.registerProvider(BukkitTileEntityProvider.class, PaperTileEntityProvider.class); + // Tick Count Provider providerFactory.registerProvider(PaperTickCountProvider.class); diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgc.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgc.java index 16082f3c98d..b35449507e7 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgc.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgc.java @@ -3,6 +3,7 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.utils.DateUtil; import com.earth2me.essentials.utils.NumberUtil; +import net.ess3.provider.TileEntityProvider; import org.bukkit.ChatColor; import org.bukkit.Chunk; import org.bukkit.Server; @@ -36,6 +37,7 @@ protected void run(final Server server, final CommandSource sender, final String sender.sendTl("gcfree", Runtime.getRuntime().freeMemory() / 1024 / 1024); final List worlds = server.getWorlds(); + final TileEntityProvider tileEntityProvider = ess.provider(TileEntityProvider.class); for (final World w : worlds) { String worldType = "World"; switch (w.getEnvironment()) { @@ -51,7 +53,7 @@ protected void run(final Server server, final CommandSource sender, final String try { for (final Chunk chunk : w.getLoadedChunks()) { - tileEntities += chunk.getTileEntities().length; + tileEntities += tileEntityProvider.getTileEntities(chunk).length; } } catch (final java.lang.ClassCastException ex) { ess.getLogger().log(Level.SEVERE, "Corrupted chunk data on world " + w, ex); diff --git a/providers/BaseProviders/src/main/java/net/ess3/provider/TileEntityProvider.java b/providers/BaseProviders/src/main/java/net/ess3/provider/TileEntityProvider.java new file mode 100644 index 00000000000..f3cbd5f7234 --- /dev/null +++ b/providers/BaseProviders/src/main/java/net/ess3/provider/TileEntityProvider.java @@ -0,0 +1,8 @@ +package net.ess3.provider; + +import org.bukkit.Chunk; +import org.bukkit.block.BlockState; + +public interface TileEntityProvider extends Provider { + BlockState[] getTileEntities(Chunk chunk); +} diff --git a/providers/BaseProviders/src/main/java/net/ess3/provider/providers/BukkitTileEntityProvider.java b/providers/BaseProviders/src/main/java/net/ess3/provider/providers/BukkitTileEntityProvider.java new file mode 100644 index 00000000000..f350de53acf --- /dev/null +++ b/providers/BaseProviders/src/main/java/net/ess3/provider/providers/BukkitTileEntityProvider.java @@ -0,0 +1,14 @@ +package net.ess3.provider.providers; + +import net.ess3.provider.TileEntityProvider; +import net.essentialsx.providers.ProviderData; +import org.bukkit.Chunk; +import org.bukkit.block.BlockState; + +@ProviderData(description = "Bukkit Tile Entity Provider") +public class BukkitTileEntityProvider implements TileEntityProvider { + @Override + public BlockState[] getTileEntities(Chunk chunk) { + return chunk.getTileEntities(); + } +} diff --git a/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperTileEntityProvider.java b/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperTileEntityProvider.java new file mode 100644 index 00000000000..8f142bf3810 --- /dev/null +++ b/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperTileEntityProvider.java @@ -0,0 +1,25 @@ +package net.ess3.provider.providers; + +import net.ess3.provider.TileEntityProvider; +import net.essentialsx.providers.ProviderData; +import net.essentialsx.providers.ProviderTest; +import org.bukkit.Chunk; +import org.bukkit.block.BlockState; + +@ProviderData(description = "Paper 1.13+ Tile Entity Provider", weight = 1) +public class PaperTileEntityProvider implements TileEntityProvider { + @Override + public BlockState[] getTileEntities(Chunk chunk) { + return chunk.getTileEntities(false); + } + + @ProviderTest + public static boolean test() { + try { + Chunk.class.getDeclaredMethod("getTileEntities", boolean.class); + return true; + } catch (final NoSuchMethodException ignored) { + return false; + } + } +} From e9b83437510d9801b7eb0663b1ca192a9baed319 Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Sun, 22 Feb 2026 11:53:38 -0800 Subject: [PATCH 07/37] Move away from adventure-platform for Paper (#6220) Makes us no longer dependent on adventure-platform for Paper servers as we will use their native adventure apis. Few hacks needed to be done to not remap things in places but this is the best we can do The PaperAdventureFacet has to be in PaperProvider to avoid the kyori remapping that happens on the entire EssentialsX module (we already did this for PaperChatListenerProvider) Fixes #6215 --- Essentials/build.gradle | 2 +- .../java/com/earth2me/essentials/Backup.java | 5 +- .../earth2me/essentials/CommandSource.java | 15 +- .../java/com/earth2me/essentials/Console.java | 10 +- .../com/earth2me/essentials/Essentials.java | 97 +++++++----- .../essentials/EssentialsPlayerListener.java | 42 ++--- .../essentials/EssentialsUpgrade.java | 46 ++++-- .../java/com/earth2me/essentials/I18n.java | 4 +- .../com/earth2me/essentials/IEssentials.java | 3 + .../com/earth2me/essentials/ISettings.java | 5 +- .../java/com/earth2me/essentials/IUser.java | 7 +- .../java/com/earth2me/essentials/Jails.java | 13 +- .../java/com/earth2me/essentials/Kit.java | 3 +- .../com/earth2me/essentials/ManagedFile.java | 3 +- .../java/com/earth2me/essentials/Mob.java | 4 +- .../com/earth2me/essentials/PlayerList.java | 3 +- .../com/earth2me/essentials/Settings.java | 48 +++--- .../java/com/earth2me/essentials/Trade.java | 2 +- .../java/com/earth2me/essentials/User.java | 19 ++- .../java/com/earth2me/essentials/Warps.java | 4 +- .../essentials/adventure/AdventureUtil.java | 62 ++++++++ .../adventure/SpigotAdventureFacet.java | 149 ++++++++++++++++++ .../essentials/commands/Commandbalance.java | 2 +- .../commands/Commandbalancetop.java | 6 +- .../essentials/commands/Commandban.java | 5 +- .../essentials/commands/Commandbanip.java | 5 +- .../commands/Commandbroadcastworld.java | 4 +- .../essentials/commands/Commandeco.java | 2 +- .../essentials/commands/Commandhelp.java | 2 +- .../essentials/commands/Commandhelpop.java | 3 +- .../essentials/commands/Commandhome.java | 2 +- .../essentials/commands/Commandkick.java | 5 +- .../essentials/commands/Commandkickall.java | 3 +- .../essentials/commands/Commandkit.java | 2 +- .../essentials/commands/Commandlist.java | 7 +- .../essentials/commands/Commandmute.java | 3 +- .../essentials/commands/Commandnear.java | 2 +- .../essentials/commands/Commandpay.java | 2 +- .../essentials/commands/Commandplaytime.java | 4 +- .../essentials/commands/Commandptime.java | 2 +- .../essentials/commands/Commandrecipe.java | 2 +- .../essentials/commands/Commandseen.java | 4 +- .../essentials/commands/Commandsell.java | 4 +- .../essentials/commands/Commandspeed.java | 2 +- .../essentials/commands/Commandtempban.java | 5 +- .../essentials/commands/Commandtempbanip.java | 5 +- .../essentials/commands/Commandtime.java | 4 +- .../commands/Commandtogglejail.java | 5 +- .../essentials/commands/Commandunban.java | 3 +- .../essentials/commands/Commandunbanip.java | 3 +- .../essentials/commands/Commandunlimited.java | 3 +- .../essentials/commands/Commandwhois.java | 2 +- .../essentials/commands/Commandworth.java | 2 +- .../commands/essentials/DumpCommand.java | 3 +- .../commands/essentials/VersionCommand.java | 19 ++- .../config/EssentialsConfiguration.java | 8 +- .../economy/vault/VaultEconomyProvider.java | 3 +- .../messaging/SimpleMessageRecipient.java | 6 +- .../essentials/signs/EssentialsSign.java | 8 +- .../essentials/signs/SignBalance.java | 2 +- .../essentials/textreader/HelpInput.java | 17 +- .../textreader/KeywordReplacer.java | 3 +- .../essentials/updatecheck/UpdateChecker.java | 32 ++-- .../essentials/utils/AdventureUtil.java | 146 ----------------- .../essentials/utils/CommonPlaceholders.java | 1 + .../earth2me/essentials/utils/FormatUtil.java | 3 +- .../net/ess3/api/TranslatableException.java | 4 +- .../src/main/resources/messages.properties | 16 +- .../com/earth2me/essentials/EconomyTest.java | 3 +- .../antibuild/EssentialsConnect.java | 6 +- .../essentials/chat/EssentialsChat.java | 3 +- .../chat/processing/AbstractChatHandler.java | 9 +- .../essentialsx/discord/DiscordSettings.java | 3 +- .../discord/EssentialsDiscord.java | 5 +- .../interactions/InteractionEventImpl.java | 4 +- .../interactions/commands/MessageCommand.java | 2 +- .../discordlink/EssentialsDiscordLink.java | 3 +- .../listeners/LinkBukkitListener.java | 5 +- .../discordlink/rolesync/RoleSyncManager.java | 13 +- .../essentials/geoip/EssentialsGeoIP.java | 3 +- .../geoip/EssentialsGeoIPPlayerListener.java | 21 ++- .../essentials/protect/EssentialsConnect.java | 4 +- .../essentials/spawn/EssentialsSpawn.java | 3 +- .../essentials/xmpp/EssentialsXMPP.java | 3 +- .../earth2me/essentials/xmpp/XMPPManager.java | 3 +- .../essentials/adventure/AdventureFacet.java | 58 +++++++ .../essentials/adventure/ComponentHolder.java | 17 ++ .../adventure/PaperAdventureFacet.java | 138 ++++++++++++++++ 88 files changed, 747 insertions(+), 481 deletions(-) create mode 100644 Essentials/src/main/java/com/earth2me/essentials/adventure/AdventureUtil.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/adventure/SpigotAdventureFacet.java delete mode 100644 Essentials/src/main/java/com/earth2me/essentials/utils/AdventureUtil.java create mode 100644 providers/BaseProviders/src/main/java/com/earth2me/essentials/adventure/AdventureFacet.java create mode 100644 providers/BaseProviders/src/main/java/com/earth2me/essentials/adventure/ComponentHolder.java create mode 100644 providers/PaperProvider/src/main/java/com/earth2me/essentials/adventure/PaperAdventureFacet.java diff --git a/Essentials/build.gradle b/Essentials/build.gradle index c33de421815..088ae5f5505 100644 --- a/Essentials/build.gradle +++ b/Essentials/build.gradle @@ -95,7 +95,7 @@ shadowJar { relocate 'io.leangen.geantyref', 'com.earth2me.essentials.libs.geantyref' relocate 'org.checkerframework', 'com.earth2me.essentials.libs.checkerframework' relocate 'net.kyori', 'com.earth2me.essentials.libs.kyori' - relocate 'net.essentialsx.temp.adventure', 'net.kyori.adventure' + relocate 'net.essentialsx.temp.adventure', 'net.kyori.adventure' // Undo temporary relocation for Paper Provider minimize { include(dependency('org.checkerframework:checker-qual')) diff --git a/Essentials/src/main/java/com/earth2me/essentials/Backup.java b/Essentials/src/main/java/com/earth2me/essentials/Backup.java index 7ef4a5f1097..a6831ab3bb1 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Backup.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Backup.java @@ -1,6 +1,5 @@ package com.earth2me.essentials; -import com.earth2me.essentials.utils.AdventureUtil; import net.ess3.api.IEssentials; import org.bukkit.Server; import org.bukkit.command.CommandSender; @@ -80,7 +79,7 @@ public void run() { taskLock.complete(new Object()); return; } - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("backupStarted"))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("backupStarted"))); final CommandSender cs = server.getConsoleSender(); server.dispatchCommand(cs, "save-all"); server.dispatchCommand(cs, "save-off"); @@ -119,7 +118,7 @@ public void run() { } active = false; taskLock.complete(new Object()); - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("backupFinished"))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("backupFinished"))); } } diff --git a/Essentials/src/main/java/com/earth2me/essentials/CommandSource.java b/Essentials/src/main/java/com/earth2me/essentials/CommandSource.java index ed8d5bdb68d..a29d4d3f8ff 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/CommandSource.java +++ b/Essentials/src/main/java/com/earth2me/essentials/CommandSource.java @@ -1,8 +1,6 @@ package com.earth2me.essentials; -import com.earth2me.essentials.utils.AdventureUtil; -import net.kyori.adventure.platform.bukkit.BukkitAudiences; -import net.kyori.adventure.text.Component; +import com.earth2me.essentials.adventure.ComponentHolder; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -37,7 +35,7 @@ public void sendTl(final String tlKey, final Object... args) { final String translation = tlLiteral(tlKey, args); if (!translation.isEmpty()) { - sendComponent(AdventureUtil.miniMessage().deserialize(translation)); + sendComponent(ess.getAdventureFacet().deserializeMiniMessage(translation)); } } @@ -49,18 +47,17 @@ public String tl(final String tlKey, final Object... args) { return tlLiteral(tlKey, args); } - public Component tlComponent(final String tlKey, final Object... args) { + public ComponentHolder tlComponent(final String tlKey, final Object... args) { if (isPlayer()) { //noinspection ConstantConditions return getUser().tlComponent(tlKey, args); } final String translation = tlLiteral(tlKey, args); - return AdventureUtil.miniMessage().deserialize(translation); + return ess.getAdventureFacet().deserializeMiniMessage(translation); } - public void sendComponent(final Component component) { - final BukkitAudiences audiences = ess.getBukkitAudience(); - audiences.sender(sender).sendMessage(component); + public void sendComponent(final ComponentHolder component) { + ess.getAdventureFacet().send(sender, component); } public final net.ess3.api.IUser getUser() { diff --git a/Essentials/src/main/java/com/earth2me/essentials/Console.java b/Essentials/src/main/java/com/earth2me/essentials/Console.java index 6414f3cefc5..d1d76c53844 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Console.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Console.java @@ -1,10 +1,8 @@ package com.earth2me.essentials; +import com.earth2me.essentials.adventure.AdventureFacet; import com.earth2me.essentials.messaging.IMessageRecipient; import com.earth2me.essentials.messaging.SimpleMessageRecipient; -import com.earth2me.essentials.utils.AdventureUtil; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.text.Component; import org.bukkit.Server; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -73,10 +71,8 @@ public void sendTl(String tlKey, Object... args) { return; } - final Audience consoleAudience = ((Essentials) ess).getBukkitAudience().sender(getCommandSender()); - final Component component = AdventureUtil.miniMessage() - .deserialize(translation); - consoleAudience.sendMessage(component); + final AdventureFacet adventureFacet = ess.getAdventureFacet(); + adventureFacet.send(getCommandSender(), adventureFacet.deserializeMiniMessage(translation)); } @Override diff --git a/Essentials/src/main/java/com/earth2me/essentials/Essentials.java b/Essentials/src/main/java/com/earth2me/essentials/Essentials.java index 8c4c25413f7..2241b53d5d9 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Essentials.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Essentials.java @@ -17,6 +17,11 @@ */ package com.earth2me.essentials; +import com.earth2me.essentials.adventure.AdventureFacet; +import com.earth2me.essentials.adventure.AdventureUtil; +import com.earth2me.essentials.adventure.ComponentHolder; +import com.earth2me.essentials.adventure.PaperAdventureFacet; +import com.earth2me.essentials.adventure.SpigotAdventureFacet; import com.earth2me.essentials.commands.EssentialsCommand; import com.earth2me.essentials.commands.IEssentialsCommand; import com.earth2me.essentials.commands.NoChargeException; @@ -40,7 +45,6 @@ import com.earth2me.essentials.textreader.SimpleTextInput; import com.earth2me.essentials.updatecheck.UpdateChecker; import com.earth2me.essentials.userstorage.ModernUserMap; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import com.earth2me.essentials.utils.VersionUtil; import io.papermc.lib.PaperLib; @@ -101,8 +105,6 @@ import net.ess3.provider.providers.PrehistoricPotionMetaProvider; import net.essentialsx.api.v2.services.BalanceTop; import net.essentialsx.api.v2.services.mail.MailService; -import net.kyori.adventure.platform.bukkit.BukkitAudiences; -import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; import org.bukkit.Server; import org.bukkit.World; @@ -179,7 +181,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials { private transient Kits kits; private transient RandomTeleport randomTeleport; private transient UpdateChecker updateChecker; - private transient BukkitAudiences bukkitAudience; + private transient AdventureFacet adventureFacet; static { EconomyLayers.init(); @@ -210,6 +212,8 @@ public void onEnable() { LOGGER = EssentialsLogger.getLoggerProvider(this); EssentialsLogger.updatePluginLogger(this); + initAdventureFacet(); + execTimer = new ExecuteTimer(); execTimer.start(); @@ -225,37 +229,37 @@ public void onEnable() { switch (VersionUtil.getServerSupportStatus()) { case NMS_CLEANROOM: - getLogger().severe(AdventureUtil.miniToLegacy(tlLiteral("serverUnsupportedCleanroom"))); + getLogger().severe(getAdventureFacet().miniToLegacy(tlLiteral("serverUnsupportedCleanroom"))); break; case DANGEROUS_FORK: - getLogger().severe(AdventureUtil.miniToLegacy(tlLiteral("serverUnsupportedDangerous"))); + getLogger().severe(getAdventureFacet().miniToLegacy(tlLiteral("serverUnsupportedDangerous"))); break; case STUPID_PLUGIN: - getLogger().severe(AdventureUtil.miniToLegacy(tlLiteral("serverUnsupportedDumbPlugins"))); + getLogger().severe(getAdventureFacet().miniToLegacy(tlLiteral("serverUnsupportedDumbPlugins"))); break; case UNSTABLE: - getLogger().severe(AdventureUtil.miniToLegacy(tlLiteral("serverUnsupportedMods"))); + getLogger().severe(getAdventureFacet().miniToLegacy(tlLiteral("serverUnsupportedMods"))); break; case OUTDATED: - getLogger().severe(AdventureUtil.miniToLegacy(tlLiteral("serverUnsupported"))); + getLogger().severe(getAdventureFacet().miniToLegacy(tlLiteral("serverUnsupported"))); break; case LIMITED: - getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("serverUnsupportedLimitedApi"))); + getLogger().info(getAdventureFacet().miniToLegacy(tlLiteral("serverUnsupportedLimitedApi"))); break; } if (VersionUtil.getSupportStatusClass() != null) { - getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("serverUnsupportedClass", VersionUtil.getSupportStatusClass()))); + getLogger().info(getAdventureFacet().miniToLegacy(tlLiteral("serverUnsupportedClass", VersionUtil.getSupportStatusClass()))); } if (VersionUtil.getServerBukkitVersion().isSnapshot()) { - getLogger().severe(AdventureUtil.miniToLegacy(tlLiteral("serverSnapshot"))); + getLogger().severe(getAdventureFacet().miniToLegacy(tlLiteral("serverSnapshot"))); } final PluginManager pm = getServer().getPluginManager(); for (final Plugin plugin : pm.getPlugins()) { if (plugin.getDescription().getName().startsWith("Essentials") && !plugin.getDescription().getVersion().equals(this.getDescription().getVersion()) && !plugin.getDescription().getName().equals("EssentialsAntiCheat")) { - getLogger().warning(AdventureUtil.miniToLegacy(tlLiteral("versionMismatch", plugin.getDescription().getName()))); + getLogger().warning(getAdventureFacet().miniToLegacy(tlLiteral("versionMismatch", plugin.getDescription().getName()))); } } @@ -436,9 +440,9 @@ public void onEnable() { if (!TESTING) { updateChecker = new UpdateChecker(this); runTaskAsynchronously(() -> { - getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("versionFetching"))); - for (final Component component : updateChecker.getVersionMessages(false, true, new CommandSource(this, Bukkit.getConsoleSender()))) { - getLogger().log(getSettings().isUpdateCheckEnabled() ? Level.WARNING : Level.INFO, AdventureUtil.adventureToLegacy(component)); + getLogger().log(Level.INFO, getAdventureFacet().miniToLegacy(tlLiteral("versionFetching"))); + for (final ComponentHolder component : updateChecker.getVersionMessages(false, true, new CommandSource(this, Bukkit.getConsoleSender()))) { + getLogger().log(getSettings().isUpdateCheckEnabled() ? Level.WARNING : Level.INFO, getAdventureFacet().adventureToLegacy(component)); } }); @@ -527,13 +531,13 @@ public ProviderFactory getProviders() { @Override public void onDisable() { - if (bukkitAudience != null) { - bukkitAudience.close(); + if (adventureFacet != null) { + adventureFacet.close(); } final boolean stopping = TESTING || provider(ServerStateProvider.class).isStopping(); if (!stopping) { - LOGGER.log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("serverReloading"))); + LOGGER.log(Level.SEVERE, getAdventureFacet().miniToLegacy(tlLiteral("serverReloading"))); } if (!TESTING) { @@ -557,7 +561,7 @@ public void onDisable() { } cleanupOpenInventories(); if (!TESTING && getBackup().getTaskLock() != null && !getBackup().getTaskLock().isDone()) { - LOGGER.log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("backupInProgress"))); + LOGGER.log(Level.SEVERE, getAdventureFacet().miniToLegacy(tlLiteral("backupInProgress"))); getBackup().getTaskLock().join(); } if (i18n != null) { @@ -572,7 +576,6 @@ public void onDisable() { } Economy.setEss(null); - AdventureUtil.setEss(null); Trade.closeLog(); getUsers().shutdown(); @@ -585,11 +588,6 @@ public void onDisable() { public void reload() { Trade.closeLog(); - if (bukkitAudience != null) { - bukkitAudience.close(); - bukkitAudience = null; - } - for (final IConf iConf : confList) { iConf.reloadConfig(); execTimer.mark("Reload(" + iConf.getClass().getSimpleName() + ")"); @@ -599,16 +597,29 @@ public void reload() { for (final String commandName : this.getDescription().getCommands().keySet()) { final Command command = this.getCommand(commandName); if (command != null) { - command.setDescription(AdventureUtil.miniToLegacy(tlLiteral(commandName + "CommandDescription"))); - command.setUsage(AdventureUtil.miniToLegacy(tlLiteral(commandName + "CommandUsage"))); + command.setDescription(getAdventureFacet().miniToLegacy(tlLiteral(commandName + "CommandDescription"))); + command.setUsage(getAdventureFacet().miniToLegacy(tlLiteral(commandName + "CommandUsage"))); } } final PluginManager pm = getServer().getPluginManager(); registerListeners(pm); - AdventureUtil.setEss(this); - bukkitAudience = BukkitAudiences.create(this); + initAdventureFacet(); + } + + private void initAdventureFacet() { + if (adventureFacet != null) { + adventureFacet.close(); + } + + if (VersionUtil.isPaper() && VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_16_5_R01)) { + adventureFacet = new PaperAdventureFacet(getSettings() != null ? getSettings().getPrimaryColor() : null, getSettings() != null ? getSettings().getSecondaryColor() : null); + } else { + adventureFacet = new SpigotAdventureFacet(this); + } + + AdventureUtil.setAdventureFacet(adventureFacet); } private IEssentialsCommand loadCommand(final String path, final String name, final IEssentialsModule module, final ClassLoader classLoader) throws Exception { @@ -673,7 +684,7 @@ public List onTabCompleteEssentials(final CommandSender cSender, final C cmd = loadCommand(commandPath, command.getName(), module, classLoader); } catch (final Exception ex) { sender.sendTl("commandNotLoaded", commandLabel); - LOGGER.log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("commandNotLoaded", commandLabel)), ex); + LOGGER.log(Level.SEVERE, getAdventureFacet().miniToLegacy(tlLiteral("commandNotLoaded", commandLabel)), ex); return Collections.emptyList(); } @@ -696,11 +707,11 @@ public List onTabCompleteEssentials(final CommandSender cSender, final C } catch (final Exception ex) { showError(sender, ex, commandLabel); // Tab completion shouldn't fail - LOGGER.log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("commandFailed", commandLabel)), ex); + LOGGER.log(Level.SEVERE, getAdventureFacet().miniToLegacy(tlLiteral("commandFailed", commandLabel)), ex); return Collections.emptyList(); } } catch (final Throwable ex) { - LOGGER.log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("commandFailed", commandLabel)), ex); + LOGGER.log(Level.SEVERE, getAdventureFacet().miniToLegacy(tlLiteral("commandFailed", commandLabel)), ex); return Collections.emptyList(); } } @@ -727,7 +738,8 @@ public boolean onCommandEssentials(final CommandSender cSender, final Command co LOGGER.log(Level.SEVERE, ex.getMessage(), ex); if (cSender instanceof Player) { final PlayerLocaleProvider localeProvider = provider(PlayerLocaleProvider.class); - getBukkitAudience().sender(cSender).sendMessage(AdventureUtil.miniMessage().deserialize(tlLocale(I18n.getLocale(localeProvider.getLocale((Player) cSender)), "internalError"))); + final String miniMessageStr = tlLocale(I18n.getLocale(localeProvider.getLocale((Player) cSender)), "internalError"); + getAdventureFacet().send(cSender, getAdventureFacet().deserializeMiniMessage(miniMessageStr)); } else { cSender.sendMessage(tlLiteral("internalError")); } @@ -785,13 +797,13 @@ public boolean onCommandEssentials(final CommandSender cSender, final Command co cmd = loadCommand(commandPath, command.getName(), module, classLoader); } catch (final Exception ex) { sender.sendTl("commandNotLoaded", commandLabel); - LOGGER.log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("commandNotLoaded", commandLabel)), ex); + LOGGER.log(Level.SEVERE, getAdventureFacet().miniToLegacy(tlLiteral("commandNotLoaded", commandLabel)), ex); return true; } // Check authorization if (user != null && !user.isAuthorized(cmd, permissionPrefix)) { - LOGGER.log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("deniedAccessCommand", user.getName()))); + LOGGER.log(Level.INFO, getAdventureFacet().miniToLegacy(tlLiteral("deniedAccessCommand", user.getName()))); user.sendTl("noAccessCommand"); return true; } @@ -832,7 +844,7 @@ public boolean onCommandEssentials(final CommandSender cSender, final Command co sender.sendMessage(command.getUsage().replace("", commandLabel)); } if (!ex.getMessage().isEmpty()) { - sender.sendComponent(AdventureUtil.miniMessage().deserialize(ex.getMessage())); + sender.sendComponent(getAdventureFacet().deserializeMiniMessage(ex.getMessage())); } if (ex.getCause() != null && settings.isDebug()) { ex.getCause().printStackTrace(); @@ -846,7 +858,7 @@ public boolean onCommandEssentials(final CommandSender cSender, final Command co return true; } } catch (final Throwable ex) { - LOGGER.log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("commandFailed", commandLabel)), ex); + LOGGER.log(Level.SEVERE, getAdventureFacet().miniToLegacy(tlLiteral("commandFailed", commandLabel)), ex); return true; } } @@ -882,7 +894,7 @@ public void showError(final CommandSource sender, final Throwable exception, fin sender.sendTl("errorWithMessage", exception.getMessage()); } if (getSettings().isDebug()) { - LOGGER.log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("errorCallingCommand", commandLabel)), exception); + LOGGER.log(Level.INFO, getAdventureFacet().miniToLegacy(tlLiteral("errorCallingCommand", commandLabel)), exception); } } @@ -1151,7 +1163,7 @@ private int broadcastMessage(final IUser sender, final String permission, final final Collection players = getOnlinePlayers(); for (final Player player : players) { final User user = getUser(player); - if ((permission == null && (sender == null || !user.isIgnoredPlayer(sender))) || (permission != null && user.isAuthorized(permission))) { + if (permission == null && (sender == null || !user.isIgnoredPlayer(sender)) || permission != null && user.isAuthorized(permission)) { if (shouldExclude != null && shouldExclude.test(user)) { continue; } @@ -1323,8 +1335,9 @@ public PluginCommand getPluginCommand(final String cmd) { return this.getCommand(cmd); } - public BukkitAudiences getBukkitAudience() { - return bukkitAudience; + @Override + public AdventureFacet getAdventureFacet() { + return adventureFacet; } private AbstractItemDb getItemDbFromConfig() { diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java index 8fc09fdcb20..c1fb775d7b4 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java @@ -1,6 +1,7 @@ package com.earth2me.essentials; import com.destroystokyo.paper.ClientOption; +import com.earth2me.essentials.adventure.ComponentHolder; import com.earth2me.essentials.commands.Commandfireball; import com.earth2me.essentials.craftbukkit.Inventories; import com.earth2me.essentials.textreader.IText; @@ -8,7 +9,6 @@ import com.earth2me.essentials.textreader.TextInput; import com.earth2me.essentials.textreader.TextPager; import com.earth2me.essentials.userstorage.ModernUserMap; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.CommonPlaceholders; import com.earth2me.essentials.utils.DateUtil; import com.earth2me.essentials.utils.FormatUtil; @@ -30,7 +30,6 @@ import net.ess3.provider.providers.PaperCommandSendListenerProvider; import net.essentialsx.PaperAdventureSmuggler; import net.essentialsx.api.v2.events.AsyncUserDataLoadEvent; -import net.kyori.adventure.text.Component; import org.bukkit.BanEntry; import org.bukkit.BanList; import org.bukkit.GameMode; @@ -210,7 +209,7 @@ public void onPlayerChat(final AsyncPlayerChatEvent event) { } } - ess.getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("mutedUserSpeaks", user.getName(), event.getMessage()))); + ess.getLogger().info(ess.getAdventureFacet().miniToLegacy(tlLiteral("mutedUserSpeaks", user.getName(), event.getMessage()))); } try { final Iterator it = event.getRecipients().iterator(); @@ -295,7 +294,7 @@ public void onPlayerQuit(final PlayerQuitEvent event) { ess.getScheduler().cancelTask(pendingId); } - if (hideJoinQuitMessages() || (ess.getSettings().allowSilentJoinQuit() && user.isAuthorized("essentials.silentquit"))) { + if (hideJoinQuitMessages() || ess.getSettings().allowSilentJoinQuit() && user.isAuthorized("essentials.silentquit")) { event.setQuitMessage(null); } else if (ess.getSettings().isCustomQuitMessage() && event.getQuitMessage() != null) { final Player player = event.getPlayer(); @@ -498,7 +497,7 @@ private void joinFlow(final User user, final long currentTime, final String mess if (user.isAuthorized("essentials.updatecheck")) { ess.runTaskAsynchronously(() -> { - for (final Component component : ess.getUpdateChecker().getVersionMessages(false, false, user.getSource())) { + for (final ComponentHolder component : ess.getUpdateChecker().getVersionMessages(false, false, user.getSource())) { user.sendComponent(component); } }); @@ -623,9 +622,9 @@ public void onPlayerLoginBanned(final PlayerLoginEvent event) { final Date banExpiry = banEntry.getExpiration(); if (banExpiry != null) { final String expiry = DateUtil.formatDateDiff(banExpiry.getTime()); - event.setKickMessage(AdventureUtil.miniToLegacy(tlLiteral("tempbanJoin", expiry, banEntry.getReason()))); + event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("tempbanJoin", expiry, banEntry.getReason()))); } else { - event.setKickMessage(AdventureUtil.miniToLegacy(tlLiteral("banJoin", banEntry.getReason()))); + event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("banJoin", banEntry.getReason()))); } } else { banEntry = ess.getServer().getBanList(BanList.Type.IP).getBanEntry(event.getAddress().getHostAddress()); @@ -633,9 +632,9 @@ public void onPlayerLoginBanned(final PlayerLoginEvent event) { final Date banExpiry = banEntry.getExpiration(); if (banExpiry != null) { final String expiry = DateUtil.formatDateDiff(banExpiry.getTime()); - event.setKickMessage(AdventureUtil.miniToLegacy(tlLiteral("tempbanIpJoin", expiry, banEntry.getReason()))); + event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("tempbanIpJoin", expiry, banEntry.getReason()))); } else { - event.setKickMessage(AdventureUtil.miniToLegacy(tlLiteral("banIpJoin", banEntry.getReason()))); + event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("banIpJoin", banEntry.getReason()))); } } } @@ -667,7 +666,7 @@ public void onPlayerListFull(final PlayerServerFullCheckEvent event) { } if (!event.isAllowed() && ess.getSettings().isCustomServerFullMessage()) { - PaperAdventureSmuggler.smugglePlayerServerFullCheckEvent(event, AdventureUtil.miniToLegacy(tlLiteral("serverFull"))); + PaperAdventureSmuggler.smugglePlayerServerFullCheckEvent(event, ess.getAdventureFacet().miniToLegacy(tlLiteral("serverFull"))); } } @@ -679,9 +678,9 @@ public void onPlayerKickBanned(final AsyncPlayerPreLoginEvent event) { final Date banExpiry = banEntry.getExpiration(); if (banExpiry != null) { final String expiry = DateUtil.formatDateDiff(banExpiry.getTime()); - event.setKickMessage(AdventureUtil.miniToLegacy(tlLiteral("tempbanJoin", expiry, banEntry.getReason()))); + event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("tempbanJoin", expiry, banEntry.getReason()))); } else { - event.setKickMessage(AdventureUtil.miniToLegacy(tlLiteral("banJoin", banEntry.getReason()))); + event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("banJoin", banEntry.getReason()))); } } else { banEntry = ess.getServer().getBanList(BanListType.IP).getBanEntry(event.getAddress()); @@ -689,9 +688,9 @@ public void onPlayerKickBanned(final AsyncPlayerPreLoginEvent event) { final Date banExpiry = banEntry.getExpiration(); if (banExpiry != null) { final String expiry = DateUtil.formatDateDiff(banExpiry.getTime()); - event.setKickMessage(AdventureUtil.miniToLegacy(tlLiteral("tempbanIpJoin", expiry, banEntry.getReason()))); + event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("tempbanIpJoin", expiry, banEntry.getReason()))); } else { - event.setKickMessage(AdventureUtil.miniToLegacy(tlLiteral("banIpJoin", banEntry.getReason()))); + event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("banIpJoin", banEntry.getReason()))); } } } @@ -701,7 +700,7 @@ public void onPlayerKickBanned(final AsyncPlayerPreLoginEvent event) { return; } if (ess.getSettings().isCustomWhitelistMessage()) { - event.setKickMessage(AdventureUtil.miniToLegacy(tlLiteral("whitelistKick"))); + event.setKickMessage(ess.getAdventureFacet().miniToLegacy(tlLiteral("whitelistKick"))); } } } @@ -786,16 +785,19 @@ public void handlePlayerCommandPreprocess(final PlayerCommandPreprocessEvent eve if (ess.getSettings().getSocialSpyCommands().contains(cmd) || ess.getSettings().getSocialSpyCommands().contains("*")) { if (pluginCommand == null - || (!pluginCommand.getName().equals("msg") && !pluginCommand.getName().equals("r"))) { // /msg and /r are handled in SimpleMessageRecipient + || !pluginCommand.getName().equals("msg") && !pluginCommand.getName().equals("r")) { // /msg and /r are handled in SimpleMessageRecipient final User user = ess.getUser(player); if (!user.isAuthorized("essentials.chat.spy.exempt")) { final String playerName = ess.getSettings().isSocialSpyDisplayNames() ? player.getDisplayName() : player.getName(); for (final User spyer : ess.getOnlineUsers()) { if (spyer.isSocialSpyEnabled() && !player.equals(spyer.getBase())) { - final Component base = (user.isMuted() && ess.getSettings().getSocialSpyListenMutedPlayers()) + final ComponentHolder base = (user.isMuted() && ess.getSettings().getSocialSpyListenMutedPlayers()) ? spyer.tlComponent("socialSpyMutedPrefix") : spyer.tlComponent("socialSpyPrefix"); - spyer.sendComponent(base.append(AdventureUtil.legacyToAdventure(playerName)).append(Component.text(": " + event.getMessage()))); + final ComponentHolder nameComponent = ess.getAdventureFacet().legacyToAdventure(playerName); + final ComponentHolder messageComponent = ess.getAdventureFacet().text(": " + event.getMessage()); + + spyer.sendComponent(ess.getAdventureFacet().append(base, nameComponent, messageComponent)); } } } @@ -819,7 +821,7 @@ public void handlePlayerCommandPreprocess(final PlayerCommandPreprocessEvent eve user.sendTl("voiceSilencedTime", dateDiff); } } - ess.getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("mutedUserSpeaks", player.getName(), event.getMessage()))); + ess.getLogger().info(ess.getAdventureFacet().miniToLegacy(tlLiteral("mutedUserSpeaks", player.getName(), event.getMessage()))); return; } @@ -1272,7 +1274,7 @@ private boolean isEssentialsCommand(final String label) { return command != null && (command.getPlugin() == ess || command.getPlugin().getClass().getName().startsWith("com.earth2me.essentials") || command.getPlugin().getClass().getName().startsWith("net.essentialsx")) - && (ess.getSettings().isCommandOverridden(label) || (ess.getAlternativeCommandsHandler().getAlternative(label) == null)); + && (ess.getSettings().isCommandOverridden(label) || ess.getAlternativeCommandsHandler().getAlternative(label) == null); } } } diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsUpgrade.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsUpgrade.java index 43ee9ad8643..984daabf7b9 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsUpgrade.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsUpgrade.java @@ -6,7 +6,6 @@ import com.earth2me.essentials.config.entities.LazyLocation; import com.earth2me.essentials.craftbukkit.BanLookup; import com.earth2me.essentials.userstorage.ModernUUIDCache; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.StringUtil; import com.google.common.base.Charsets; import com.google.common.io.Files; @@ -72,7 +71,7 @@ public class EssentialsUpgrade { doneFile.load(); } - public static void uuidFileConvert(final IEssentials ess, final Boolean ignoreUFCache) { + private static void uuidFileConvert(final IEssentials ess, final Boolean ignoreUFCache) { ess.getLogger().info("Starting Essentials UUID userdata conversion"); final File userdir = new File(ess.getDataFolder(), "userdata"); @@ -157,7 +156,7 @@ public static void uuidFileConvert(final IEssentials ess, final Boolean ignoreUF ess.getLogger().info("To rerun the conversion type /essentials uuidconvert"); } - public void updateRandomTeleport() { + private void updateRandomTeleport() { if (doneFile.getBoolean("updateRandomTeleport", false)) { return; } @@ -192,7 +191,7 @@ public void updateRandomTeleport() { ess.getLogger().info("Done converting random teleport config."); } - public void convertMailList() { + private void convertMailList() { if (doneFile.getBoolean("updateUsersMailList", false)) { return; } @@ -232,7 +231,7 @@ public void convertMailList() { ess.getLogger().info("Done converting mail list."); } - public void convertStupidCamelCaseUserdataKeys() { + private void convertStupidCamelCaseUserdataKeys() { if (doneFile.getBoolean("updateUsersStupidLegacyPathNames", false)) { return; } @@ -312,7 +311,7 @@ public void convertStupidCamelCaseUserdataKeys() { * horrible economy code, as any operation which loads all user data into memory will load all these NPC accounts * and spam the console with warnings." */ - public void purgeBrokenNpcAccounts() { + private void purgeBrokenNpcAccounts() { if (doneFile.getBoolean("updatePurgeBrokenNpcAccounts", false)) { return; } @@ -423,7 +422,7 @@ public void purgeBrokenNpcAccounts() { ess.getLogger().info("#===========================================================================#"); } - public void convertIgnoreList() { + private void convertIgnoreList() { final Pattern pattern = Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); if (doneFile.getBoolean("updateUsersIgnoreListUUID", false)) { return; @@ -474,6 +473,10 @@ public void convertIgnoreList() { } public void convertKits() { + if (Essentials.TESTING) { + return; + } + final Kits kits = ess.getKits(); final EssentialsConfiguration config = kits.getRootConfig(); if (doneFile.getBoolean("kitsyml", false)) { @@ -531,7 +534,7 @@ private void moveMotdRulesToFile(final String name) { doneFile.setProperty("move" + name + "ToFile", true); doneFile.save(); } catch (final IOException e) { - ess.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("upgradingFilesError")), e); + ess.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("upgradingFilesError")), e); } } @@ -694,15 +697,15 @@ private void sanitizeAllUserFilenames() { final File tmpFile = new File(listOfFile.getParentFile(), sanitizedFilename + ".tmp"); final File newFile = new File(listOfFile.getParentFile(), sanitizedFilename); if (!listOfFile.renameTo(tmpFile)) { - ess.getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("userdataMoveError", filename, sanitizedFilename))); + ess.getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(tlLiteral("userdataMoveError", filename, sanitizedFilename))); continue; } if (newFile.exists()) { - ess.getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("duplicatedUserdata", filename, sanitizedFilename))); + ess.getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(tlLiteral("duplicatedUserdata", filename, sanitizedFilename))); continue; } if (!tmpFile.renameTo(newFile)) { - ess.getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("userdataMoveBackError", sanitizedFilename, sanitizedFilename))); + ess.getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(tlLiteral("userdataMoveBackError", sanitizedFilename, sanitizedFilename))); } } doneFile.setProperty("sanitizeAllUserFilenames", true); @@ -718,7 +721,7 @@ private World getFakeWorld(final String name) { return null; } - public Location getFakeLocation(final CommentedConfigurationNode config, final String path) { + private Location getFakeLocation(final CommentedConfigurationNode config, final String path) { final String worldName = config.getString((path != null ? path + "." : "") + "world"); if (worldName == null || worldName.isEmpty()) { return null; @@ -882,7 +885,7 @@ private void uuidFileChange() { doneFile.save(); } - public void banFormatChange() { + private void banFormatChange() { if (doneFile.getBoolean("banFormatChange", false)) { return; } @@ -952,7 +955,7 @@ private void updateBan(final String playerName, final String banReason, final Lo Bukkit.getBanList(BanList.Type.NAME).addBan(playerName, banReason, banTimeout == 0 ? null : new Date(banTimeout), Console.NAME); } - public void generateUidCache() { + private void generateUidCache() { if (doneFile.getBoolean("newUidCacheBuilt", false)) { return; } @@ -992,7 +995,7 @@ public void generateUidCache() { if (name != null) { if (nameToUuidMap.containsKey(name)) { final UUID oldUuid = nameToUuidMap.get(name); - if (oldUuid.version() < uuid.version() || (oldUuid.version() == uuid.version() && uuids.get(oldUuid) < time)) { + if (oldUuid.version() < uuid.version() || oldUuid.version() == uuid.version() && uuids.get(oldUuid) < time) { ess.getLogger().warning("New UUID found for " + name + ": " + uuid + " (old: " + oldUuid + "). Replacing."); uuids.remove(oldUuid); } else { @@ -1060,7 +1063,7 @@ public void upgradeLang() { properties.load(Files.newReader(backup, Charsets.UTF_8)); for (final String key : properties.stringPropertyNames()) { final String value = properties.getProperty(key); - properties.setProperty(key, AdventureUtil.legacyToMini(AdventureUtil.miniMessage().escapeTags(value), true)); + properties.setProperty(key, ess.getAdventureFacet().legacyToMini(ess.getAdventureFacet().escapeTags(value), true)); } properties.store(Files.newWriter(newFile, Charsets.UTF_8), null); } @@ -1079,6 +1082,10 @@ public void upgradeLang() { } public void beforeSettings() { + if (Essentials.TESTING) { + return; + } + if (!ess.getDataFolder().exists()) { ess.getDataFolder().mkdirs(); } @@ -1087,10 +1094,17 @@ public void beforeSettings() { } public void preModules() { + if (Essentials.TESTING) { + return; + } generateUidCache(); } public void afterSettings() { + if (Essentials.TESTING) { + return; + } + sanitizeAllUserFilenames(); updateUsersPowerToolsFormat(); updateUsersHomesFormat(); diff --git a/Essentials/src/main/java/com/earth2me/essentials/I18n.java b/Essentials/src/main/java/com/earth2me/essentials/I18n.java index 4592e3f0a38..c7ae82eaada 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/I18n.java +++ b/Essentials/src/main/java/com/earth2me/essentials/I18n.java @@ -1,6 +1,6 @@ package com.earth2me.essentials; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import net.ess3.api.IEssentials; import org.jetbrains.annotations.NotNull; @@ -187,7 +187,7 @@ private String format(final Locale locale, final String string, final Object... if (arg instanceof AdventureUtil.ParsedPlaceholder) { return arg.toString(); } - return AdventureUtil.legacyToMini(AdventureUtil.miniMessage().escapeTags(arg.toString())); + return ess.getAdventureFacet().legacyToMini(ess.getAdventureFacet().escapeTags(arg.toString())); }); return messageFormat.format(processedArgs).replace(' ', ' '); // replace nbsp with a space diff --git a/Essentials/src/main/java/com/earth2me/essentials/IEssentials.java b/Essentials/src/main/java/com/earth2me/essentials/IEssentials.java index bdcaa1dcae1..6e69b938bdb 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/IEssentials.java +++ b/Essentials/src/main/java/com/earth2me/essentials/IEssentials.java @@ -1,5 +1,6 @@ package com.earth2me.essentials; +import com.earth2me.essentials.adventure.AdventureFacet; import com.earth2me.essentials.api.IItemDb; import com.earth2me.essentials.api.IJails; import com.earth2me.essentials.api.IWarps; @@ -146,4 +147,6 @@ public interface IEssentials extends Plugin { default

P provider(final Class

providerClass) { return getProviders().get(providerClass); } + + AdventureFacet getAdventureFacet(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/ISettings.java b/Essentials/src/main/java/com/earth2me/essentials/ISettings.java index ae6dbc600d9..0ba2d7d9c14 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/ISettings.java +++ b/Essentials/src/main/java/com/earth2me/essentials/ISettings.java @@ -4,7 +4,6 @@ import com.earth2me.essentials.signs.EssentialsSign; import com.earth2me.essentials.textreader.IText; import net.essentialsx.api.v2.ChatType; -import net.kyori.adventure.text.minimessage.tag.Tag; import org.bukkit.Material; import org.bukkit.event.EventPriority; import org.spongepowered.configurate.CommentedConfigurationNode; @@ -440,9 +439,9 @@ public interface ISettings extends IConf { int getMaxItemLore(); - Tag getPrimaryColor(); + String getPrimaryColor(); - Tag getSecondaryColor(); + String getSecondaryColor(); BigDecimal getBaltopMinBalance(); diff --git a/Essentials/src/main/java/com/earth2me/essentials/IUser.java b/Essentials/src/main/java/com/earth2me/essentials/IUser.java index 8fce2df96d0..a701eb5610f 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/IUser.java +++ b/Essentials/src/main/java/com/earth2me/essentials/IUser.java @@ -1,5 +1,6 @@ package com.earth2me.essentials; +import com.earth2me.essentials.adventure.ComponentHolder; import com.earth2me.essentials.api.IAsyncTeleport; import com.earth2me.essentials.commands.IEssentialsCommand; import com.earth2me.essentials.config.entities.CommandCooldown; @@ -7,8 +8,6 @@ import net.ess3.api.events.AfkStatusChangeEvent; import net.essentialsx.api.v2.services.mail.MailMessage; import net.essentialsx.api.v2.services.mail.MailSender; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.ComponentLike; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; @@ -138,9 +137,9 @@ default boolean hasOutstandingTeleportRequest() { void sendMessage(String message); - void sendComponent(ComponentLike component); + void sendComponent(ComponentHolder component); - Component tlComponent(String tlKey, Object... args); + ComponentHolder tlComponent(String tlKey, Object... args); String playerTl(String tlKey, Object... args); diff --git a/Essentials/src/main/java/com/earth2me/essentials/Jails.java b/Essentials/src/main/java/com/earth2me/essentials/Jails.java index f4e31f09692..223a4b0cf0d 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Jails.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Jails.java @@ -3,7 +3,6 @@ import com.earth2me.essentials.config.ConfigurateUtil; import com.earth2me.essentials.config.EssentialsConfiguration; import com.earth2me.essentials.config.entities.LazyLocation; -import com.earth2me.essentials.utils.AdventureUtil; import net.ess3.api.IEssentials; import net.ess3.api.IUser; import net.ess3.api.TranslatableException; @@ -273,9 +272,9 @@ public void onJailPlayerRespawn(final PlayerRespawnEvent event) { event.setRespawnLocation(getJail(user.getJail())); } catch (final Exception ex) { if (ess.getSettings().isDebug()) { - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())), ex); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())), ex); } else { - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()))); } } } @@ -295,9 +294,9 @@ public void onJailPlayerTeleport(final PlayerTeleportEvent event) { event.setTo(getJail(user.getJail())); } catch (final Exception ex) { if (ess.getSettings().isDebug()) { - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())), ex); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())), ex); } else { - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()))); } } user.sendTl("jailMessage"); @@ -319,9 +318,9 @@ public void onJailPlayerJoin(final PlayerJoinEvent event) { final CompletableFuture future = new CompletableFuture<>(); future.exceptionally(ex -> { if (ess.getSettings().isDebug()) { - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())), ex); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage())), ex); } else { - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("returnPlayerToJailError", user.getName(), ex.getLocalizedMessage()))); } return false; }); diff --git a/Essentials/src/main/java/com/earth2me/essentials/Kit.java b/Essentials/src/main/java/com/earth2me/essentials/Kit.java index 9213890a633..895b13d8220 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Kit.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Kit.java @@ -6,7 +6,6 @@ import com.earth2me.essentials.textreader.IText; import com.earth2me.essentials.textreader.KeywordReplacer; import com.earth2me.essentials.textreader.SimpleTextInput; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.DateUtil; import com.earth2me.essentials.utils.NumberUtil; import net.ess3.api.IEssentials; @@ -208,7 +207,7 @@ public boolean expandItems(final User user, final List items) throws Exc if (kitItem.startsWith("@")) { if (serializationProvider == null) { - ess.getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("kitError3", kitName, user.getName()))); + ess.getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(tlLiteral("kitError3", kitName, user.getName()))); continue; } stack = serializationProvider.deserializeItem(Base64Coder.decodeLines(kitItem.substring(1))); diff --git a/Essentials/src/main/java/com/earth2me/essentials/ManagedFile.java b/Essentials/src/main/java/com/earth2me/essentials/ManagedFile.java index 20991cb503d..ba88395d6a1 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/ManagedFile.java +++ b/Essentials/src/main/java/com/earth2me/essentials/ManagedFile.java @@ -1,6 +1,5 @@ package com.earth2me.essentials; -import com.earth2me.essentials.utils.AdventureUtil; import net.ess3.api.IEssentials; import java.io.BufferedInputStream; @@ -46,7 +45,7 @@ public ManagedFile(final String filename, final IEssentials ess) { try { copyResourceAscii("/" + filename, file); } catch (final IOException ex) { - Essentials.getWrappedLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("itemsCsvNotLoaded", filename)), ex); + Essentials.getWrappedLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("itemsCsvNotLoaded", filename)), ex); } } } diff --git a/Essentials/src/main/java/com/earth2me/essentials/Mob.java b/Essentials/src/main/java/com/earth2me/essentials/Mob.java index 61486003d7d..4385b2b929e 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Mob.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Mob.java @@ -1,6 +1,6 @@ package com.earth2me.essentials; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.EnumUtil; import org.bukkit.Location; import org.bukkit.Server; @@ -191,7 +191,7 @@ public static Mob fromBukkitType(final EntityType type) { public Entity spawn(final World world, final Server server, final Location loc) throws MobException { final Entity entity = world.spawn(loc, this.bukkitType.getEntityClass()); if (entity == null) { - Essentials.getWrappedLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("unableToSpawnMob"))); + Essentials.getWrappedLogger().log(Level.WARNING, AdventureUtil.getAdventureFacet().miniToLegacy(tlLiteral("unableToSpawnMob"))); throw new MobException(); } return entity; diff --git a/Essentials/src/main/java/com/earth2me/essentials/PlayerList.java b/Essentials/src/main/java/com/earth2me/essentials/PlayerList.java index 0570fe2a384..f807af73f51 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/PlayerList.java +++ b/Essentials/src/main/java/com/earth2me/essentials/PlayerList.java @@ -1,6 +1,5 @@ package com.earth2me.essentials; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import com.earth2me.essentials.utils.NumberUtil; import net.ess3.api.TranslatableException; @@ -39,7 +38,7 @@ public static String listUsers(final IEssentials ess, final List users, fi groupString.append(tlLiteral("listHiddenTag")); } user.setDisplayNick(); - groupString.append(AdventureUtil.legacyToMini(user.getDisplayName())); + groupString.append(ess.getAdventureFacet().legacyToMini(user.getDisplayName())); final String strippedNick = FormatUtil.stripFormat(user.getNickname()); if (ess.getSettings().realNamesOnList() && strippedNick != null && !strippedNick.equals(user.getName())) { diff --git a/Essentials/src/main/java/com/earth2me/essentials/Settings.java b/Essentials/src/main/java/com/earth2me/essentials/Settings.java index b1a694a1c99..8d41da405e7 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Settings.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Settings.java @@ -1,5 +1,6 @@ package com.earth2me.essentials; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.api.IItemDb; import com.earth2me.essentials.commands.IEssentialsCommand; import com.earth2me.essentials.config.ConfigurateUtil; @@ -8,7 +9,6 @@ import com.earth2me.essentials.signs.Signs; import com.earth2me.essentials.textreader.IText; import com.earth2me.essentials.textreader.SimpleTextInput; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.EnumUtil; import com.earth2me.essentials.utils.FormatUtil; import com.earth2me.essentials.utils.LocationUtil; @@ -18,8 +18,6 @@ import net.ess3.provider.SyncCommandsProvider; import net.essentialsx.api.v2.ChatType; import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.format.TextColor; -import net.kyori.adventure.text.minimessage.tag.Tag; import org.bukkit.ChatColor; import org.bukkit.Color; import org.bukkit.Material; @@ -46,6 +44,7 @@ import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; @@ -59,8 +58,8 @@ public class Settings implements net.ess3.api.ISettings { private static final BigDecimal DEFAULT_MAX_MONEY = new BigDecimal("10000000000000"); private static final BigDecimal DEFAULT_MIN_MONEY = new BigDecimal("-10000000000000"); - private static final Tag DEFAULT_PRIMARY_COLOR = Tag.styling(NamedTextColor.GOLD); - private static final Tag DEFAULT_SECONDARY_COLOR = Tag.styling(NamedTextColor.RED); + private static final String DEFAULT_PRIMARY_COLOR = NamedTextColor.GOLD.toString(); + private static final String DEFAULT_SECONDARY_COLOR = NamedTextColor.RED.toString(); private final transient EssentialsConfiguration config; private final transient IEssentials ess; private final transient AtomicInteger reloadCount = new AtomicInteger(0); @@ -151,8 +150,8 @@ public class Settings implements net.ess3.api.ISettings { private double maxProjectileSpeed; private boolean removeEffectsOnHeal; private Map worldAliases; - private Tag primaryColor = DEFAULT_PRIMARY_COLOR; - private Tag secondaryColor = DEFAULT_SECONDARY_COLOR; + private String primaryColor = DEFAULT_PRIMARY_COLOR; + private String secondaryColor = DEFAULT_SECONDARY_COLOR; private Set multiplierPerms; private BigDecimal defaultMultiplier; private List afkTimeoutCommands = Collections.emptyList(); @@ -974,7 +973,7 @@ private List _getItemSpawnBlacklist() { final ItemStack iStack = itemDb.get(itemName); epItemSpwn.add(iStack.getType()); } catch (final Exception ex) { - ess.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("unknownItemInList", itemName, "item-spawn-blacklist")), ex); + ess.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("unknownItemInList", itemName, "item-spawn-blacklist")), ex); } } return epItemSpwn; @@ -1002,7 +1001,7 @@ private List _getEnabledSigns() { try { newSigns.add(Signs.valueOf(signName).getSign()); } catch (final Exception ex) { - ess.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("unknownItemInList", signName, "enabledSigns"))); + ess.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("unknownItemInList", signName, "enabledSigns"))); continue; } signsEnabled = true; @@ -1130,7 +1129,7 @@ public List getProtectList(final String configName) { } if (mat == null) { - ess.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("unknownItemInList", itemName, configName))); + ess.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("unknownItemInList", itemName, configName))); } else { list.add(mat); } @@ -1900,7 +1899,7 @@ private List _getUnprotectedSign() { try { newSigns.add(Signs.valueOf(signName).getSign()); } catch (final Exception ex) { - ess.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("unknownItemInList", signName, "unprotected-sign-names"))); + ess.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("unknownItemInList", signName, "unprotected-sign-names"))); } } return newSigns; @@ -2200,39 +2199,40 @@ public int getMaxItemLore() { } @Override - public Tag getPrimaryColor() { + public String getPrimaryColor() { return primaryColor; } - private Tag _getPrimaryColor() { + private String _getPrimaryColor() { final String color = config.getString("message-colors.primary", "#ffaa00"); - final TextColor textColor = _getTagColor(color); - return textColor != null ? Tag.styling(textColor) : DEFAULT_PRIMARY_COLOR; + final String textColor = _getTagColor(color); + return textColor != null ? textColor : DEFAULT_PRIMARY_COLOR; } @Override - public Tag getSecondaryColor() { + public String getSecondaryColor() { return secondaryColor; } - private Tag _getSecondaryColor() { + private String _getSecondaryColor() { final String color = config.getString("message-colors.secondary", "#ff5555"); - final TextColor textColor = _getTagColor(color); - return textColor != null ? Tag.styling(textColor) : DEFAULT_SECONDARY_COLOR; + final String textColor = _getTagColor(color); + return textColor != null ? textColor : DEFAULT_SECONDARY_COLOR; } - private TextColor _getTagColor(final String color) { + private String _getTagColor(final String color) { try { if (color.startsWith("#") && color.length() == 7 && NumberUtil.isHexadecimal(color.substring(1))) { - return TextColor.color(Color.fromRGB(Integer.decode(color)).asRGB()); + Color.fromRGB(Integer.decode(color)); + return color; } if (color.length() == 1) { - return AdventureUtil.fromChar(color.charAt(0)); + return Objects.requireNonNull(AdventureUtil.fromChar(color.charAt(0))).toString(); } - return NamedTextColor.NAMES.value(color.toLowerCase(Locale.ENGLISH)); - } catch (IllegalArgumentException ignored) { + return Objects.requireNonNull(NamedTextColor.NAMES.value(color.toLowerCase(Locale.ENGLISH))).toString(); + } catch (NullPointerException | IllegalArgumentException ignored) { } return null; } diff --git a/Essentials/src/main/java/com/earth2me/essentials/Trade.java b/Essentials/src/main/java/com/earth2me/essentials/Trade.java index aab142ab6b9..2575b0f23a9 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Trade.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Trade.java @@ -2,7 +2,7 @@ import com.earth2me.essentials.craftbukkit.Inventories; import com.earth2me.essentials.craftbukkit.SetExpFix; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.NumberUtil; import com.earth2me.essentials.utils.VersionUtil; import net.ess3.api.IEssentials; diff --git a/Essentials/src/main/java/com/earth2me/essentials/User.java b/Essentials/src/main/java/com/earth2me/essentials/User.java index 2a34edc3bca..a261880720b 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/User.java +++ b/Essentials/src/main/java/com/earth2me/essentials/User.java @@ -1,12 +1,13 @@ package com.earth2me.essentials; +import com.earth2me.essentials.adventure.AdventureUtil; +import com.earth2me.essentials.adventure.ComponentHolder; import com.earth2me.essentials.commands.IEssentialsCommand; import com.earth2me.essentials.craftbukkit.Inventories; import com.earth2me.essentials.economy.EconomyLayer; import com.earth2me.essentials.economy.EconomyLayers; import com.earth2me.essentials.messaging.IMessageRecipient; import com.earth2me.essentials.messaging.SimpleMessageRecipient; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.DateUtil; import com.earth2me.essentials.utils.EnumUtil; import com.earth2me.essentials.utils.FormatUtil; @@ -24,8 +25,6 @@ import net.ess3.provider.PlayerLocaleProvider; import net.essentialsx.api.v2.events.TransactionEvent; import net.essentialsx.api.v2.services.mail.MailSender; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.ComponentLike; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Statistic; @@ -648,7 +647,7 @@ public void setAfk(final boolean set, final AfkStatusChangeEvent.Cause cause) { return; } - this.getBase().setSleepingIgnored(this.isAuthorized("essentials.sleepingignored") || (set && ess.getSettings().sleepIgnoresAfkPlayers())); + this.getBase().setSleepingIgnored(this.isAuthorized("essentials.sleepingignored") || set && ess.getSettings().sleepIgnoresAfkPlayers()); if (set && !isAfk()) { afkPosition = this.getLocation(); this.afkSince = System.currentTimeMillis(); @@ -844,7 +843,7 @@ public void checkActivity() { // If `afk-timeout-command` in config.yml is empty, use default Essentials kicking behaviour instead of executing a command. if (ess.getSettings().getAfkTimeoutCommands().isEmpty()) { - this.getBase().kickPlayer(AdventureUtil.miniToLegacy(playerTl("autoAfkKickReason", kickTime))); + this.getBase().kickPlayer(ess.getAdventureFacet().miniToLegacy(playerTl("autoAfkKickReason", kickTime))); for (final User user : ess.getOnlineUsers()) { if (user.isAuthorized("essentials.kick.notify")) { @@ -1074,14 +1073,14 @@ public void sendMessage(final String message) { } @Override - public void sendComponent(ComponentLike component) { - ess.getBukkitAudience().player(base).sendMessage(component); + public void sendComponent(ComponentHolder component) { + ess.getAdventureFacet().send(base, component); } @Override - public Component tlComponent(String tlKey, Object... args) { + public ComponentHolder tlComponent(String tlKey, Object... args) { final String translation = playerTl(tlKey, args); - return AdventureUtil.miniMessage().deserialize(translation); + return ess.getAdventureFacet().deserializeMiniMessage(translation); } @Override @@ -1091,7 +1090,7 @@ public void sendTl(String tlKey, Object... args) { return; } - sendComponent(AdventureUtil.miniMessage().deserialize(translation)); + sendComponent(ess.getAdventureFacet().deserializeMiniMessage(translation)); } @Override diff --git a/Essentials/src/main/java/com/earth2me/essentials/Warps.java b/Essentials/src/main/java/com/earth2me/essentials/Warps.java index 37b8cc752c8..ea188382bed 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Warps.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Warps.java @@ -1,8 +1,8 @@ package com.earth2me.essentials; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.commands.WarpNotFoundException; import com.earth2me.essentials.config.EssentialsConfiguration; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.StringUtil; import net.ess3.api.InvalidNameException; import net.ess3.api.TranslatableException; @@ -134,7 +134,7 @@ public final void reloadConfig() { warpPoints.put(new StringIgnoreCase(name), conf); } } catch (final Exception ex) { - Essentials.getWrappedLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("loadWarpError", filename)), ex); + Essentials.getWrappedLogger().log(Level.WARNING, AdventureUtil.getAdventureFacet().miniToLegacy(tlLiteral("loadWarpError", filename)), ex); } } } diff --git a/Essentials/src/main/java/com/earth2me/essentials/adventure/AdventureUtil.java b/Essentials/src/main/java/com/earth2me/essentials/adventure/AdventureUtil.java new file mode 100644 index 00000000000..aaa5e559b77 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/adventure/AdventureUtil.java @@ -0,0 +1,62 @@ +package com.earth2me.essentials.adventure; + +import net.kyori.adventure.text.format.NamedTextColor; + +public final class AdventureUtil { + private static final String LOOKUP = "0123456789abcdefklmnor"; + private static final NamedTextColor[] COLORS = new NamedTextColor[]{NamedTextColor.BLACK, NamedTextColor.DARK_BLUE, NamedTextColor.DARK_GREEN, NamedTextColor.DARK_AQUA, NamedTextColor.DARK_RED, NamedTextColor.DARK_PURPLE, NamedTextColor.GOLD, NamedTextColor.GRAY, NamedTextColor.DARK_GRAY, NamedTextColor.BLUE, NamedTextColor.GREEN, NamedTextColor.AQUA, NamedTextColor.RED, NamedTextColor.LIGHT_PURPLE, NamedTextColor.YELLOW, NamedTextColor.WHITE}; + + private static AdventureFacet ADVENTURE_FACET_INSTANCE = null; + + private AdventureUtil() { + } + + public static void setAdventureFacet(AdventureFacet adventureFacetInstance) { + ADVENTURE_FACET_INSTANCE = adventureFacetInstance; + } + + public static AdventureFacet getAdventureFacet() { + return ADVENTURE_FACET_INSTANCE; + } + + /** + * Get the {@link NamedTextColor} from its associated section sign char. + */ + public static NamedTextColor fromChar(final char c) { + final int index = LOOKUP.indexOf(c); + if (index == -1 || index > 15) { + return null; + } + return COLORS[index]; + } + + public static String fromCharName(final char c) { + final NamedTextColor namedTextColor = fromChar(c); + if (namedTextColor == null) { + return null; + } + + return namedTextColor.toString(); + } + + /** + * Parameters for a translation message are not parsed for MiniMessage by default to avoid injection. If you want + * a parameter to be parsed for MiniMessage you must wrap it in a ParsedPlaceholder by using this method. + */ + public static ParsedPlaceholder parsed(final String literal) { + return new ParsedPlaceholder(literal); + } + + public static class ParsedPlaceholder { + private final String value; + + protected ParsedPlaceholder(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/adventure/SpigotAdventureFacet.java b/Essentials/src/main/java/com/earth2me/essentials/adventure/SpigotAdventureFacet.java new file mode 100644 index 00000000000..fbf0b8020ae --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/adventure/SpigotAdventureFacet.java @@ -0,0 +1,149 @@ +package com.earth2me.essentials.adventure; + +import com.earth2me.essentials.Essentials; +import com.earth2me.essentials.utils.NumberUtil; +import com.earth2me.essentials.utils.VersionUtil; +import net.ess3.provider.AbstractChatEvent; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.flattener.ComponentFlattener; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.Tag; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.Color; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.Locale; + +public class SpigotAdventureFacet implements AdventureFacet { + private static final LegacyComponentSerializer LEGACY_SERIALIZER; + private static final MiniMessage MINI_MESSAGE_NO_TAGS; + + static { + final LegacyComponentSerializer.Builder builder = LegacyComponentSerializer.builder() + .flattener(ComponentFlattener.basic()) + .extractUrls(AbstractChatEvent.URL_PATTERN) + .useUnusualXRepeatedCharacterHexFormat(); + if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_16_1_R01)) { + builder.hexColors(); + } + LEGACY_SERIALIZER = builder.build(); + + MINI_MESSAGE_NO_TAGS = MiniMessage.builder().strict(true).build(); + } + + private final Essentials ess; + private final BukkitAudiences bukkitAudiences; + private final MiniMessage miniMessageInstance; + + public SpigotAdventureFacet(final Essentials ess) { + this.ess = ess.getSettings() == null ? null : ess; + bukkitAudiences = BukkitAudiences.create(ess); + + miniMessageInstance = MiniMessage.builder() + .tags(TagResolver.builder() + .resolvers(TagResolver.standard()) + .resolver(TagResolver.resolver("primary", supplyTag(true))) + .resolver(TagResolver.resolver("secondary", supplyTag(false))) + .build()) + .build(); + } + + private Tag supplyTag(final boolean primary) { + final String color = primary ? (ess != null ? ess.getSettings().getPrimaryColor() : "gold") : (ess != null ? ess.getSettings().getSecondaryColor() : "red"); + + final TextColor textColor; + if (color.startsWith("#") && color.length() == 7 && NumberUtil.isHexadecimal(color.substring(1))) { + textColor = TextColor.color(Color.fromRGB(Integer.decode(color)).asRGB()); + } else if (color.length() == 1) { + textColor = AdventureUtil.fromChar(color.charAt(0)); + } else { + textColor = NamedTextColor.NAMES.value(color.toLowerCase(Locale.ENGLISH)); + } + + return ess != null && textColor != null ? Tag.styling(textColor) : Tag.styling(primary ? NamedTextColor.GOLD : NamedTextColor.RED); + } + + @Override + public ComponentHolder deserializeMiniMessage(String message) { + return new ComponentHolder(miniMessageInstance.deserialize(message)); + } + + @Override + public String legacyToMini(String message) { + return legacyToMini(message, true); + } + + @Override + public void send(CommandSender sender, ComponentHolder component) { + bukkitAudiences.sender(sender).sendMessage((Component) component.getComponent()); + } + + @Override + public void send(Player player, ComponentHolder component) { + bukkitAudiences.player(player).sendMessage((Component) component.getComponent()); + } + + @Override + public String adventureToLegacy(ComponentHolder component) { + return adventureToLegacy((Component) component.getComponent()); + } + + private String adventureToLegacy(Component component) { + return LEGACY_SERIALIZER.serialize(component); + } + + @Override + public ComponentHolder legacyToAdventure(String message) { + return new ComponentHolder(LEGACY_SERIALIZER.deserialize(message)); + } + + @Override + public ComponentHolder text(String message) { + return new ComponentHolder(Component.text(message)); + } + + @Override + public String legacyToMini(String message, boolean useCustomTags) { + final Component deserializedText = LEGACY_SERIALIZER.deserialize(message); + if (useCustomTags) { + return miniMessageInstance.serialize(deserializedText); + } else { + return MINI_MESSAGE_NO_TAGS.serialize(deserializedText); + } + } + + @Override + public String miniToLegacy(String message) { + return adventureToLegacy(miniMessageInstance.deserialize(message)); + } + + @Override + public String stripTags(String input) { + return miniMessageInstance.stripTags(input); + } + + @Override + public String escapeTags(String input) { + return miniMessageInstance.escapeTags(input); + } + + @Override + public ComponentHolder append(ComponentHolder base, ComponentHolder... addition) { + Component baseComponent = (Component) base.getComponent(); + for (ComponentHolder holder : addition) { + final Component additionComponent = (Component) holder.getComponent(); + baseComponent = baseComponent.append(additionComponent); + } + return new ComponentHolder(baseComponent); + } + + @Override + public void close() { + bukkitAudiences.close(); + } +} diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalance.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalance.java index ab597c640de..2241b26174e 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalance.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalance.java @@ -2,7 +2,7 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.NumberUtil; import org.bukkit.Server; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalancetop.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalancetop.java index f5915467fdc..e11f8a17abf 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalancetop.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalancetop.java @@ -4,7 +4,7 @@ import com.earth2me.essentials.User; import com.earth2me.essentials.textreader.SimpleTextInput; import com.earth2me.essentials.textreader.TextPager; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.EnumUtil; import com.earth2me.essentials.utils.NumberUtil; import com.earth2me.essentials.utils.VersionUtil; @@ -115,7 +115,7 @@ public void run() { future.thenRun(() -> { if (fresh) { final SimpleTextInput newCache = new SimpleTextInput(); - newCache.getLines().add(AdventureUtil.miniToLegacy(tlLiteral("serverTotal", AdventureUtil.parsed(NumberUtil.displayCurrency(ess.getBalanceTop().getBalanceTopTotal(), ess))))); + newCache.getLines().add(ess.getAdventureFacet().miniToLegacy(tlLiteral("serverTotal", AdventureUtil.parsed(NumberUtil.displayCurrency(ess.getBalanceTop().getBalanceTopTotal(), ess))))); int pos = 1; for (final Map.Entry entry : ess.getBalanceTop().getBalanceTopCache().entrySet()) { final BigDecimal balance = entry.getValue().getBalance(); @@ -141,7 +141,7 @@ public void run() { && balance.compareTo(ess.getSettings().getBaltopMinBalance()) >= 0 && // Skip playtime check for offline players on versions below 1.15.2 (playtime == -1 || playTimeSecs >= ess.getSettings().getBaltopMinPlaytime())) { - newCache.getLines().add(AdventureUtil.miniToLegacy(tlLiteral("balanceTopLine", pos, entry.getValue().getDisplayName(), AdventureUtil.parsed(NumberUtil.displayCurrency(balance, ess))))); + newCache.getLines().add(ess.getAdventureFacet().miniToLegacy(tlLiteral("balanceTopLine", pos, entry.getValue().getDisplayName(), AdventureUtil.parsed(NumberUtil.displayCurrency(balance, ess))))); } pos++; } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandban.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandban.java index c0c16d397d5..78a8a69b1c2 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandban.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandban.java @@ -4,7 +4,6 @@ import com.earth2me.essentials.Console; import com.earth2me.essentials.OfflinePlayerStub; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import net.ess3.api.TranslatableException; import org.bukkit.BanList; @@ -55,8 +54,8 @@ public void run(final Server server, final CommandSource sender, final String co final String banDisplay = tlLiteral("banFormat", banReason, senderDisplayName); - user.getBase().kickPlayer(AdventureUtil.miniToLegacy(banDisplay)); - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("playerBanned", senderDisplayName, user.getName(), banDisplay))); + user.getBase().kickPlayer(ess.getAdventureFacet().miniToLegacy(banDisplay)); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("playerBanned", senderDisplayName, user.getName(), banDisplay))); if (nomatch) { sender.sendTl("userUnknown", user.getName()); diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbanip.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbanip.java index f0eef7be0f6..2fdf954dcd4 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbanip.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbanip.java @@ -3,7 +3,6 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.Console; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import org.bukkit.BanList; import org.bukkit.Server; @@ -52,10 +51,10 @@ public void run(final Server server, final CommandSource sender, final String co banReason = tlLiteral("defaultBanReason"); } - final String banDisplay = AdventureUtil.miniToLegacy(tlLiteral("banFormat", banReason, senderDisplayName)); + final String banDisplay = ess.getAdventureFacet().miniToLegacy(tlLiteral("banFormat", banReason, senderDisplayName)); ess.getServer().getBanList(BanList.Type.IP).addBan(ipAddress, banReason, null, senderName); - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("playerBanIpAddress", senderDisplayName, ipAddress, banReason))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("playerBanIpAddress", senderDisplayName, ipAddress, banReason))); for (final Player player : ess.getServer().getOnlinePlayers()) { if (player.getAddress().getAddress().getHostAddress().equalsIgnoreCase(ipAddress)) { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcastworld.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcastworld.java index 6d44ba2ecb5..bc8e31c8d95 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcastworld.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcastworld.java @@ -2,7 +2,7 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import com.google.common.collect.Lists; import net.ess3.api.TranslatableException; @@ -54,7 +54,7 @@ private void sendBroadcast(final World world, final String name, final String me if (message.isEmpty()) { throw new NotEnoughArgumentsException(); } - ess.broadcastTl(null, u -> !u.getBase().getWorld().equals(world), true, "broadcast", FormatUtil.replaceFormat(message).replace("\\n", "\n"), AdventureUtil.parsed(AdventureUtil.legacyToMini(name))); + ess.broadcastTl(null, u -> !u.getBase().getWorld().equals(world), true, "broadcast", FormatUtil.replaceFormat(message).replace("\\n", "\n"), AdventureUtil.parsed(ess.getAdventureFacet().legacyToMini(name))); } @Override diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandeco.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandeco.java index 3517f69937f..367dccf7235 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandeco.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandeco.java @@ -3,7 +3,7 @@ import com.earth2me.essentials.ChargeException; import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.NumberUtil; import com.google.common.collect.Lists; import net.ess3.api.MaxMoneyException; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhelp.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhelp.java index d3cb65a827b..9c2cd2baf8a 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhelp.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhelp.java @@ -7,7 +7,7 @@ import com.earth2me.essentials.textreader.KeywordReplacer; import com.earth2me.essentials.textreader.TextInput; import com.earth2me.essentials.textreader.TextPager; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.NumberUtil; import net.ess3.provider.KnownCommandsProvider; import org.bukkit.Server; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhelpop.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhelpop.java index 2d6606fd705..99456ffbc6c 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhelpop.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhelpop.java @@ -4,7 +4,6 @@ import com.earth2me.essentials.Console; import com.earth2me.essentials.User; import com.earth2me.essentials.messaging.IMessageRecipient; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import net.ess3.api.IUser; import net.essentialsx.api.v2.events.HelpopMessageSendEvent; @@ -39,7 +38,7 @@ private void sendMessage(final IMessageRecipient from, final String[] args) thro } final String message = FormatUtil.stripFormat(getFinalArg(args, 0)); - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("helpOp", from.getDisplayName(), message))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("helpOp", from.getDisplayName(), message))); final List recipients = new ArrayList<>(); for (IUser user : ess.getOnlineUsers()) { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java index e147bdcfdb3..34b81e265b9 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java @@ -3,7 +3,7 @@ import com.earth2me.essentials.OfflinePlayerStub; import com.earth2me.essentials.Trade; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.StringUtil; import io.papermc.lib.PaperLib; import net.ess3.api.TranslatableException; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkick.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkick.java index c0b2a3c176f..f7d1f3cdba0 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkick.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkick.java @@ -3,7 +3,6 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.Console; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import net.ess3.api.TranslatableException; import net.essentialsx.api.v2.events.UserKickEvent; @@ -39,7 +38,7 @@ public void run(final Server server, final CommandSource sender, final String co } } - String kickReason = args.length > 1 ? getFinalArg(args, 1) : AdventureUtil.miniToLegacy(tlLiteral("kickDefault")); + String kickReason = args.length > 1 ? getFinalArg(args, 1) : ess.getAdventureFacet().miniToLegacy(tlLiteral("kickDefault")); kickReason = FormatUtil.replaceFormat(kickReason.replace("\\n", "\n").replace("|", "\n")); final UserKickEvent event = new UserKickEvent(user, target, kickReason); @@ -55,7 +54,7 @@ public void run(final Server server, final CommandSource sender, final String co final String tlKey = "playerKicked"; final Object[] objects = {senderDisplayName, target.getName(), kickReason}; - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral(tlKey, objects))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral(tlKey, objects))); ess.broadcastTl(null, "essentials.kick.notify", tlKey, objects); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkickall.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkickall.java index 6d53141afb8..7db1c9f3b74 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkickall.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkickall.java @@ -1,7 +1,6 @@ package com.earth2me.essentials.commands; import com.earth2me.essentials.CommandSource; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import org.bukkit.Server; import org.bukkit.entity.Player; @@ -15,7 +14,7 @@ public Commandkickall() { @Override public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception { - String kickReason = args.length > 0 ? getFinalArg(args, 0) : AdventureUtil.miniToLegacy(tlLiteral("kickDefault")); + String kickReason = args.length > 0 ? getFinalArg(args, 0) : ess.getAdventureFacet().miniToLegacy(tlLiteral("kickDefault")); kickReason = FormatUtil.replaceFormat(kickReason.replace("\\n", "\n").replace("|", "\n")); for (final Player onlinePlayer : ess.getOnlinePlayers()) { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkit.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkit.java index adba5434be3..606e58ec607 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkit.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkit.java @@ -3,7 +3,7 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.Kit; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.StringUtil; import net.ess3.api.TranslatableException; import org.bukkit.Server; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandlist.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandlist.java index 254f48ec240..b4cd4659a3c 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandlist.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandlist.java @@ -3,7 +3,6 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.PlayerList; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; import org.bukkit.Server; import java.util.ArrayList; @@ -24,11 +23,11 @@ public void run(final Server server, final CommandSource sender, final String co user = ess.getUser(sender.getPlayer()); showHidden = user.isAuthorized("essentials.list.hidden") || user.canInteractVanished(); } - sender.sendComponent(AdventureUtil.miniMessage().deserialize(PlayerList.listSummary(ess, user, showHidden))); + sender.sendComponent(ess.getAdventureFacet().deserializeMiniMessage(PlayerList.listSummary(ess, user, showHidden))); final Map> playerList = PlayerList.getPlayerLists(ess, user, showHidden); if (args.length > 0) { - sender.sendComponent(AdventureUtil.miniMessage().deserialize(PlayerList.listGroupUsers(ess, playerList, args[0].toLowerCase()))); + sender.sendComponent(ess.getAdventureFacet().deserializeMiniMessage(PlayerList.listGroupUsers(ess, playerList, args[0].toLowerCase()))); } else { sendGroupedList(sender, commandLabel, playerList); } @@ -37,7 +36,7 @@ public void run(final Server server, final CommandSource sender, final String co // Output the standard /list output, when no group is specified private void sendGroupedList(final CommandSource sender, final String commandLabel, final Map> playerList) { for (final String str : PlayerList.prepareGroupedList(ess, sender, commandLabel, playerList)) { - sender.sendComponent(AdventureUtil.miniMessage().deserialize(str)); + sender.sendComponent(ess.getAdventureFacet().deserializeMiniMessage(str)); } } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmute.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmute.java index e8cf37fbe31..b5c7302b5a7 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmute.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmute.java @@ -3,7 +3,6 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.OfflinePlayerStub; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.DateUtil; import net.ess3.api.TranslatableException; import net.ess3.api.events.MuteStatusChangeEvent; @@ -117,7 +116,7 @@ public void run(final Server server, final CommandSource sender, final String co objects = new Object[]{sender.getSender().getName(), user.getName(), muteTime}; } - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral(tlKey, objects))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral(tlKey, objects))); ess.broadcastTl(null, "essentials.mute.notify", tlKey, objects); } else { sender.sendTl("unmutedPlayer", user.getDisplayName()); diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnear.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnear.java index c17ff324923..51e37107304 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnear.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandnear.java @@ -2,7 +2,7 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.google.common.collect.Lists; import org.bukkit.Location; import org.bukkit.Server; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpay.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpay.java index daa227391a6..84c2722b7a1 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpay.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandpay.java @@ -3,7 +3,7 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.Trade; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.NumberUtil; import com.earth2me.essentials.utils.StringUtil; import com.google.common.collect.Lists; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandplaytime.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandplaytime.java index cd424207a4d..4c644a1e4f1 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandplaytime.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandplaytime.java @@ -2,7 +2,7 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.DateUtil; import com.earth2me.essentials.utils.EnumUtil; import com.earth2me.essentials.utils.VersionUtil; @@ -57,7 +57,7 @@ protected void run(Server server, CommandSource sender, String commandLabel, Str } final long playtimeMs = System.currentTimeMillis() - (playtime * 50L); - sender.sendTl(key, DateUtil.formatDateDiff(playtimeMs), AdventureUtil.parsed(AdventureUtil.legacyToMini(displayName))); + sender.sendTl(key, DateUtil.formatDateDiff(playtimeMs), AdventureUtil.parsed(ess.getAdventureFacet().legacyToMini(displayName))); } @Override diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandptime.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandptime.java index 92ed2bce0e1..313e9369076 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandptime.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandptime.java @@ -3,7 +3,7 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.IUser; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.DescParseTickFormat; import com.google.common.collect.Lists; import org.bukkit.Server; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrecipe.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrecipe.java index 0d5a8ef58f0..e63222ab9bb 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrecipe.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrecipe.java @@ -2,8 +2,8 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.User; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.craftbukkit.Inventories; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.EnumUtil; import com.earth2me.essentials.utils.NumberUtil; import com.earth2me.essentials.utils.VersionUtil; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandseen.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandseen.java index d0a452b49c9..a9a851ad6a3 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandseen.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandseen.java @@ -3,7 +3,7 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.User; import com.earth2me.essentials.craftbukkit.BanLookup; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.CommonPlaceholders; import com.earth2me.essentials.utils.DateUtil; import com.earth2me.essentials.utils.FormatUtil; @@ -201,7 +201,7 @@ private void seenOffline(final CommandSource sender, final User user, final bool } private void seenIP(final CommandSource sender, final String ipAddress, final String display) { - sender.sendTl("runningPlayerMatch", AdventureUtil.parsed(AdventureUtil.legacyToMini(display))); + sender.sendTl("runningPlayerMatch", AdventureUtil.parsed(ess.getAdventureFacet().legacyToMini(display))); ess.runTaskAsynchronously(() -> { final List matches = new ArrayList<>(); diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsell.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsell.java index 9a9ca1cf603..c378f139b2a 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsell.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsell.java @@ -3,7 +3,7 @@ import com.earth2me.essentials.Trade; import com.earth2me.essentials.User; import com.earth2me.essentials.craftbukkit.Inventories; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.NumberUtil; import com.google.common.collect.Lists; import net.ess3.api.TranslatableException; @@ -123,7 +123,7 @@ private BigDecimal sellItem(final User user, final ItemStack is, final String[] final String typeName = is.getType().toString().toLowerCase(Locale.ENGLISH); final AdventureUtil.ParsedPlaceholder worthDisplay = AdventureUtil.parsed(NumberUtil.displayCurrency(worth, ess)); user.sendTl("itemSold", AdventureUtil.parsed(NumberUtil.displayCurrency(result, ess)), amount, typeName, worthDisplay); - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("itemSoldConsole", user.getName(), typeName, AdventureUtil.miniToLegacy(NumberUtil.displayCurrency(result, ess)), amount, AdventureUtil.miniToLegacy(worthDisplay.toString()), user.getDisplayName()))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("itemSoldConsole", user.getName(), typeName, ess.getAdventureFacet().miniToLegacy(NumberUtil.displayCurrency(result, ess)), amount, ess.getAdventureFacet().miniToLegacy(worthDisplay.toString()), user.getDisplayName()))); return result; } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandspeed.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandspeed.java index 69f9c195d5a..35c6d20cfa7 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandspeed.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandspeed.java @@ -2,7 +2,7 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.FloatUtil; import org.bukkit.Server; import org.bukkit.entity.Player; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempban.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempban.java index 8ce0b996dab..105f0b1c2e1 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempban.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempban.java @@ -4,7 +4,6 @@ import com.earth2me.essentials.Console; import com.earth2me.essentials.IUser; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.DateUtil; import com.earth2me.essentials.utils.FormatUtil; import org.bukkit.BanList; @@ -55,9 +54,9 @@ public void run(final Server server, final CommandSource sender, final String co final String expiry = DateUtil.formatDateDiff(banTimestamp); final String banDisplay = user.playerTl("tempBanned", expiry, senderDisplayName, banReason); - user.getBase().kickPlayer(AdventureUtil.miniToLegacy(banDisplay)); + user.getBase().kickPlayer(ess.getAdventureFacet().miniToLegacy(banDisplay)); - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("playerTempBanned", senderDisplayName, user.getName(), expiry, banReason))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("playerTempBanned", senderDisplayName, user.getName(), expiry, banReason))); ess.broadcastTl((IUser) null, "essentials.ban.notify", "playerTempBanned", senderDisplayName, user.getName(), expiry, banReason); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempbanip.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempbanip.java index 51897dfbb9e..c8201527f86 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempbanip.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtempbanip.java @@ -3,7 +3,6 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.Console; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.DateUtil; import com.earth2me.essentials.utils.FormatUtil; import org.bukkit.BanList; @@ -63,7 +62,7 @@ public void run(final Server server, final CommandSource sender, final String co ess.getServer().getBanList(BanList.Type.IP).addBan(ipAddress, banReason, new Date(banTimestamp), senderName); - final String banDisplay = AdventureUtil.miniToLegacy(tlLiteral("banFormat", banReason, senderDisplayName)); + final String banDisplay = ess.getAdventureFacet().miniToLegacy(tlLiteral("banFormat", banReason, senderDisplayName)); for (final Player player : ess.getServer().getOnlinePlayers()) { if (player.getAddress().getAddress().getHostAddress().equalsIgnoreCase(ipAddress)) { player.kickPlayer(banDisplay); @@ -72,7 +71,7 @@ public void run(final Server server, final CommandSource sender, final String co final String tlKey = "playerTempBanIpAddress"; final Object[] objects = {senderDisplayName, ipAddress, banReason, DateUtil.formatDateDiff(banTimestamp), banReason}; - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral(tlKey, objects))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral(tlKey, objects))); ess.broadcastTl(null, "essentials.banip.notify", tlKey, objects); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtime.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtime.java index 4e85d7b4c22..f376bdb8719 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtime.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtime.java @@ -1,7 +1,7 @@ package com.earth2me.essentials.commands; import com.earth2me.essentials.CommandSource; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.DescParseTickFormat; import com.earth2me.essentials.utils.NumberUtil; import com.google.common.collect.Lists; @@ -96,7 +96,7 @@ public void run(final Server server, final CommandSource sender, final String co private void getWorldsTime(final CommandSource sender, final Collection worlds) { if (worlds.size() == 1) { final Iterator iter = worlds.iterator(); - sender.sendComponent(AdventureUtil.miniMessage().deserialize(DescParseTickFormat.format(iter.next().getTime()))); + sender.sendComponent(ess.getAdventureFacet().deserializeMiniMessage(DescParseTickFormat.format(iter.next().getTime()))); return; } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtogglejail.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtogglejail.java index d77a55b51da..345602eab0e 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtogglejail.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtogglejail.java @@ -3,7 +3,6 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.ISettings; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.DateUtil; import com.earth2me.essentials.utils.EnumUtil; import com.google.common.collect.Iterables; @@ -98,7 +97,7 @@ public void run(final Server server, final CommandSource sender, final String co sender.sendTl("playerJailed", player.getName()); } - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral(tlKey, objects))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral(tlKey, objects))); ess.broadcastTl(null, "essentials.jail.notify", tlKey, objects); } }); @@ -126,7 +125,7 @@ public void run(final Server server, final CommandSource sender, final String co final String tlKey = "jailNotifySentenceExtended"; final Object[] objects = new Object[]{player.getName(), DateUtil.formatDateDiff(displayTimeDiff), sender.getSender().getName()}; - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral(tlKey, objects))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral(tlKey, objects))); ess.broadcastTl(null, "essentials.jail.notify", tlKey, objects); return; } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandunban.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandunban.java index fb5af288832..90e6c5185cc 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandunban.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandunban.java @@ -4,7 +4,6 @@ import com.earth2me.essentials.Console; import com.earth2me.essentials.IUser; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; import net.ess3.api.TranslatableException; import org.bukkit.BanList; import org.bukkit.OfflinePlayer; @@ -40,7 +39,7 @@ public void run(final Server server, final CommandSource sender, final String co } final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.DISPLAY_NAME; - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("playerUnbanned", senderDisplayName, name))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("playerUnbanned", senderDisplayName, name))); ess.broadcastTl((IUser) null, "essentials.ban.notify", "playerUnbanned", senderDisplayName, name); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandunbanip.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandunbanip.java index 4316d013a1b..443c5831b5a 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandunbanip.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandunbanip.java @@ -4,7 +4,6 @@ import com.earth2me.essentials.Console; import com.earth2me.essentials.IUser; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import org.bukkit.BanList; import org.bukkit.Server; @@ -42,7 +41,7 @@ public void run(final Server server, final CommandSource sender, final String co ess.getServer().getBanList(BanList.Type.IP).pardon(ipAddress); final String senderDisplayName = sender.isPlayer() ? sender.getPlayer().getDisplayName() : Console.DISPLAY_NAME; - ess.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("playerUnbanIpAddress", senderDisplayName, ipAddress))); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("playerUnbanIpAddress", senderDisplayName, ipAddress))); ess.broadcastTl((IUser) null, "essentials.banip.notify", "playerUnbanIpAddress", senderDisplayName, ipAddress); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandunlimited.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandunlimited.java index 448934aa841..c47c0f91ffe 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandunlimited.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandunlimited.java @@ -2,7 +2,6 @@ import com.earth2me.essentials.User; import com.earth2me.essentials.craftbukkit.Inventories; -import com.earth2me.essentials.utils.AdventureUtil; import net.ess3.api.TranslatableException; import org.bukkit.Material; import org.bukkit.Server; @@ -30,7 +29,7 @@ public void run(final Server server, final User user, final String commandLabel, } if (args[0].equalsIgnoreCase("list")) { - user.sendComponent(AdventureUtil.miniMessage().deserialize(getList(user, target))); + user.sendComponent(ess.getAdventureFacet().deserializeMiniMessage(getList(user, target))); } else if (args[0].equalsIgnoreCase("clear")) { for (final Material m : new HashSet<>(target.getUnlimited())) { if (m == null) { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandwhois.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandwhois.java index d7869e38fe7..36565746ad7 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandwhois.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandwhois.java @@ -3,7 +3,7 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.User; import com.earth2me.essentials.craftbukkit.SetExpFix; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.CommonPlaceholders; import com.earth2me.essentials.utils.DateUtil; import com.earth2me.essentials.utils.EnumUtil; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandworth.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandworth.java index f9f982269b4..32cf4ec9e10 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandworth.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandworth.java @@ -2,7 +2,7 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.MaterialUtil; import com.earth2me.essentials.utils.NumberUtil; import com.google.common.collect.Lists; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DumpCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DumpCommand.java index f4fd2b81c73..ba5f078e3ed 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DumpCommand.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DumpCommand.java @@ -4,7 +4,6 @@ import com.earth2me.essentials.commands.EssentialsTreeNode; import com.earth2me.essentials.economy.EconomyLayer; import com.earth2me.essentials.economy.EconomyLayers; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.CommandMapUtil; import com.earth2me.essentials.utils.DateUtil; import com.earth2me.essentials.utils.PasteUtil; @@ -260,7 +259,7 @@ protected void run(CommandSource sender, String commandLabel, String[] args) thr // pastes.dev doesn't support deletion keys //sender.sendTl("dumpDeleteKey", result.getDeletionKey()); if (sender.isPlayer()) { - ess.getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("dumpConsoleUrl", dumpUrl))); + ess.getLogger().info(ess.getAdventureFacet().miniToLegacy(tlLiteral("dumpConsoleUrl", dumpUrl))); // pastes.dev doesn't support deletion keys //ess.getLogger().info(AdventureUtil.miniToLegacy(tlLiteral("dumpDeleteKey", result.getDeletionKey()))); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java index 8287691ad63..82796f9802f 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java @@ -1,12 +1,11 @@ package com.earth2me.essentials.commands.essentials; import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.adventure.ComponentHolder; import com.earth2me.essentials.commands.EssentialsTreeNode; import com.earth2me.essentials.economy.EconomyLayer; import com.earth2me.essentials.economy.EconomyLayers; import com.earth2me.essentials.utils.VersionUtil; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; @@ -123,31 +122,31 @@ protected void run(CommandSource sender, String commandLabel, String[] args) thr switch (supportStatus) { case NMS_CLEANROOM: - sender.sendComponent(sender.tlComponent("serverUnsupportedCleanroom").color(NamedTextColor.DARK_RED)); + sender.sendTl("serverUnsupportedCleanroom"); break; case DANGEROUS_FORK: - sender.sendComponent(sender.tlComponent("serverUnsupportedDangerous").color(NamedTextColor.DARK_RED)); + sender.sendTl("serverUnsupportedDangerous"); break; case STUPID_PLUGIN: - sender.sendComponent(sender.tlComponent("serverUnsupportedDumbPlugins").color(NamedTextColor.DARK_RED)); + sender.sendTl("serverUnsupportedDumbPlugins"); break; case UNSTABLE: - sender.sendComponent(sender.tlComponent("serverUnsupportedMods").color(NamedTextColor.DARK_RED)); + sender.sendTl("serverUnsupportedMods"); break; case OUTDATED: - sender.sendComponent(sender.tlComponent("serverUnsupported").color(NamedTextColor.RED)); + sender.sendTl("serverUnsupported"); break; case LIMITED: - sender.sendComponent(sender.tlComponent("serverUnsupportedLimitedApi").color(NamedTextColor.RED)); + sender.sendTl("serverUnsupportedLimitedApi"); break; } if (VersionUtil.getSupportStatusClass() != null) { - sender.sendComponent(sender.tlComponent("serverUnsupportedClass").color(NamedTextColor.RED)); + sender.sendTl("serverUnsupportedClass"); } sender.sendTl("versionFetching"); ess.runTaskAsynchronously(() -> { - for (final Component component : ess.getUpdateChecker().getVersionMessages(true, true, sender)) { + for (final ComponentHolder component : ess.getUpdateChecker().getVersionMessages(true, true, sender)) { sender.sendComponent(component); } }); diff --git a/Essentials/src/main/java/com/earth2me/essentials/config/EssentialsConfiguration.java b/Essentials/src/main/java/com/earth2me/essentials/config/EssentialsConfiguration.java index 875683cb823..555420ddc39 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/config/EssentialsConfiguration.java +++ b/Essentials/src/main/java/com/earth2me/essentials/config/EssentialsConfiguration.java @@ -1,6 +1,7 @@ package com.earth2me.essentials.config; import com.earth2me.essentials.Essentials; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.config.annotations.DeleteIfIncomplete; import com.earth2me.essentials.config.annotations.DeleteOnEmpty; import com.earth2me.essentials.config.entities.CommandCooldown; @@ -12,7 +13,6 @@ import com.earth2me.essentials.config.serializers.LocationTypeSerializer; import com.earth2me.essentials.config.serializers.MailMessageSerializer; import com.earth2me.essentials.config.serializers.MaterialTypeSerializer; -import com.earth2me.essentials.utils.AdventureUtil; import net.essentialsx.api.v2.services.mail.MailMessage; import org.bukkit.Location; import org.bukkit.Material; @@ -367,7 +367,7 @@ public synchronized void load() { if (configFile.getParentFile() != null && !configFile.getParentFile().exists()) { if (!configFile.getParentFile().mkdirs()) { - Essentials.getWrappedLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("failedToCreateConfig", configFile.toString()))); + Essentials.getWrappedLogger().log(Level.SEVERE, AdventureUtil.getAdventureFacet().miniToLegacy(tlLiteral("failedToCreateConfig", configFile.toString()))); return; } } @@ -379,10 +379,10 @@ public synchronized void load() { convertAltFile(); } else if (templateName != null) { try (final InputStream is = resourceClass.getResourceAsStream(templateName)) { - Essentials.getWrappedLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("creatingConfigFromTemplate", configFile.toString()))); + Essentials.getWrappedLogger().log(Level.INFO, AdventureUtil.getAdventureFacet().miniToLegacy(tlLiteral("creatingConfigFromTemplate", configFile.toString()))); Files.copy(is, configFile.toPath()); } catch (IOException e) { - Essentials.getWrappedLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("failedToWriteConfig", configFile.toString())), e); + Essentials.getWrappedLogger().log(Level.SEVERE, AdventureUtil.getAdventureFacet().miniToLegacy(tlLiteral("failedToWriteConfig", configFile.toString())), e); } } } diff --git a/Essentials/src/main/java/com/earth2me/essentials/economy/vault/VaultEconomyProvider.java b/Essentials/src/main/java/com/earth2me/essentials/economy/vault/VaultEconomyProvider.java index 091c9a2f320..f6a0e40b7d0 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/economy/vault/VaultEconomyProvider.java +++ b/Essentials/src/main/java/com/earth2me/essentials/economy/vault/VaultEconomyProvider.java @@ -5,7 +5,6 @@ import com.earth2me.essentials.api.NoLoanPermittedException; import com.earth2me.essentials.api.UserDoesNotExistException; import com.earth2me.essentials.config.EssentialsUserConfiguration; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.NumberUtil; import com.earth2me.essentials.utils.StringUtil; import com.google.common.base.Charsets; @@ -62,7 +61,7 @@ public int fractionalDigits() { @Override public String format(double amount) { - return AdventureUtil.miniToLegacy(NumberUtil.displayCurrency(BigDecimal.valueOf(amount), ess)); + return ess.getAdventureFacet().miniToLegacy(NumberUtil.displayCurrency(BigDecimal.valueOf(amount), ess)); } @Override diff --git a/Essentials/src/main/java/com/earth2me/essentials/messaging/SimpleMessageRecipient.java b/Essentials/src/main/java/com/earth2me/essentials/messaging/SimpleMessageRecipient.java index 6c73641089b..53b0b4bec52 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/messaging/SimpleMessageRecipient.java +++ b/Essentials/src/main/java/com/earth2me/essentials/messaging/SimpleMessageRecipient.java @@ -3,7 +3,7 @@ import com.earth2me.essentials.IEssentials; import com.earth2me.essentials.IUser; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import net.ess3.api.events.PrivateMessagePreSendEvent; import net.ess3.api.events.PrivateMessageSentEvent; import org.bukkit.entity.Player; @@ -129,9 +129,9 @@ public MessageResponse sendMessage(final IMessageRecipient recipient, String mes && !onlineUser.equals(senderUser) && !onlineUser.equals(recipient)) { if (senderUser.isMuted() && ess.getSettings().getSocialSpyListenMutedPlayers()) { - onlineUser.sendComponent(AdventureUtil.miniMessage().deserialize(tlSender("socialSpyMutedPrefix") + tlLiteral("socialSpyMsgFormat", senderName, recipientName, message))); + onlineUser.sendComponent(ess.getAdventureFacet().deserializeMiniMessage(tlSender("socialSpyMutedPrefix") + tlLiteral("socialSpyMsgFormat", senderName, recipientName, message))); } else { - onlineUser.sendComponent(AdventureUtil.miniMessage().deserialize(tlLiteral("socialSpyPrefix") + tlLiteral("socialSpyMsgFormat", senderName, recipientName, message))); + onlineUser.sendComponent(ess.getAdventureFacet().deserializeMiniMessage(tlLiteral("socialSpyPrefix") + tlLiteral("socialSpyMsgFormat", senderName, recipientName, message))); } } } diff --git a/Essentials/src/main/java/com/earth2me/essentials/signs/EssentialsSign.java b/Essentials/src/main/java/com/earth2me/essentials/signs/EssentialsSign.java index 7e6e1bc3cd0..4301930bf0e 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/signs/EssentialsSign.java +++ b/Essentials/src/main/java/com/earth2me/essentials/signs/EssentialsSign.java @@ -5,7 +5,7 @@ import com.earth2me.essentials.MetaItemStack; import com.earth2me.essentials.Trade; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import com.earth2me.essentials.utils.MaterialUtil; import com.earth2me.essentials.utils.NumberUtil; @@ -106,7 +106,7 @@ protected final boolean onSignCreate(final SignChangeEvent event, final IEssenti // they won't change it to §1[Signname] return true; } - sign.setLine(0, AdventureUtil.miniToLegacy(tlLiteral("signFormatFail", this.signName))); + sign.setLine(0, ess.getAdventureFacet().miniToLegacy(tlLiteral("signFormatFail", this.signName))); final SignCreateEvent signEvent = new SignCreateEvent(sign, this, user); ess.getServer().getPluginManager().callEvent(signEvent); @@ -140,7 +140,7 @@ public String getSuccessName(final IEssentials ess) { } public String getSuccessName() { - String successName = AdventureUtil.miniToLegacy(tlLiteral("signFormatSuccess", this.signName)); + String successName = AdventureUtil.getAdventureFacet().miniToLegacy(tlLiteral("signFormatSuccess", this.signName)); if (successName.isEmpty() || !successName.contains(this.signName)) { // Set to null to cause an error in place of no functionality. This makes an error obvious as opposed to leaving users baffled by lack of // functionality. @@ -150,7 +150,7 @@ public String getSuccessName() { } public String getTemplateName() { - return AdventureUtil.miniToLegacy(tlLiteral("signFormatTemplate", this.signName)); + return AdventureUtil.getAdventureFacet().miniToLegacy(tlLiteral("signFormatTemplate", this.signName)); } public String getName() { diff --git a/Essentials/src/main/java/com/earth2me/essentials/signs/SignBalance.java b/Essentials/src/main/java/com/earth2me/essentials/signs/SignBalance.java index aac87448fd4..b905afe2e9b 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/signs/SignBalance.java +++ b/Essentials/src/main/java/com/earth2me/essentials/signs/SignBalance.java @@ -1,7 +1,7 @@ package com.earth2me.essentials.signs; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.NumberUtil; import net.ess3.api.IEssentials; diff --git a/Essentials/src/main/java/com/earth2me/essentials/textreader/HelpInput.java b/Essentials/src/main/java/com/earth2me/essentials/textreader/HelpInput.java index d37a0a636fa..da61b8e3fd0 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/textreader/HelpInput.java +++ b/Essentials/src/main/java/com/earth2me/essentials/textreader/HelpInput.java @@ -1,7 +1,6 @@ package com.earth2me.essentials.textreader; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import net.ess3.api.IEssentials; @@ -29,7 +28,7 @@ public HelpInput(final User user, final String match, final IEssentials ess) { String pluginName = ""; String pluginNameLow = ""; if (!match.equalsIgnoreCase("")) { - lines.add(AdventureUtil.miniToLegacy(user.playerTl("helpMatching", match))); + lines.add(ess.getAdventureFacet().miniToLegacy(user.playerTl("helpMatching", match))); } final Multimap pluginCommands = HashMultimap.create(); @@ -51,7 +50,7 @@ public HelpInput(final User user, final String match, final IEssentials ess) { if (pluginNameLow.equals(match)) { lines.clear(); newLines.clear(); - lines.add(AdventureUtil.miniToLegacy(user.playerTl("helpFrom", p.getDescription().getName()))); + lines.add(ess.getAdventureFacet().miniToLegacy(user.playerTl("helpFrom", p.getDescription().getName()))); } final boolean isOnWhitelist = user.isAuthorized("essentials.help." + pluginNameLow); @@ -70,7 +69,7 @@ public HelpInput(final User user, final String match, final IEssentials ess) { if (pluginNameLow.contains("essentials")) { final String node = "essentials." + commandName; if (!ess.getSettings().isCommandDisabled(commandName) && user.isAuthorized(node)) { - pluginLines.add(AdventureUtil.miniToLegacy(user.playerTl("helpLine", commandName, commandDescription))); + pluginLines.add(ess.getAdventureFacet().miniToLegacy(user.playerTl("helpLine", commandName, commandDescription))); } } else { if (ess.getSettings().showNonEssCommandsInHelp()) { @@ -83,7 +82,7 @@ public HelpInput(final User user, final String match, final IEssentials ess) { } if (isOnWhitelist || user.isAuthorized("essentials.help." + pluginNameLow + "." + commandName)) { - pluginLines.add(AdventureUtil.miniToLegacy(user.playerTl("helpLine", commandName, commandDescription))); + pluginLines.add(ess.getAdventureFacet().miniToLegacy(user.playerTl("helpLine", commandName, commandDescription))); } else if (permissions.length != 0) { boolean enabled = false; @@ -95,11 +94,11 @@ public HelpInput(final User user, final String match, final IEssentials ess) { } if (enabled) { - pluginLines.add(AdventureUtil.miniToLegacy(user.playerTl("helpLine", commandName, commandDescription))); + pluginLines.add(ess.getAdventureFacet().miniToLegacy(user.playerTl("helpLine", commandName, commandDescription))); } } else { if (!ess.getSettings().hidePermissionlessHelp()) { - pluginLines.add(AdventureUtil.miniToLegacy(user.playerTl("helpLine", commandName, commandDescription))); + pluginLines.add(ess.getAdventureFacet().miniToLegacy(user.playerTl("helpLine", commandName, commandDescription))); } } } @@ -113,13 +112,13 @@ public HelpInput(final User user, final String match, final IEssentials ess) { break; } if (match.equalsIgnoreCase("")) { - lines.add(AdventureUtil.miniToLegacy(user.playerTl("helpPlugin", pluginName, pluginNameLow))); + lines.add(ess.getAdventureFacet().miniToLegacy(user.playerTl("helpPlugin", pluginName, pluginNameLow))); } } } catch (final NullPointerException ignored) { } catch (final Exception ex) { if (!reported) { - ess.getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(user.playerTl("commandHelpFailedForPlugin", pluginNameLow)), ex); + ess.getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(user.playerTl("commandHelpFailedForPlugin", pluginNameLow)), ex); } reported = true; } diff --git a/Essentials/src/main/java/com/earth2me/essentials/textreader/KeywordReplacer.java b/Essentials/src/main/java/com/earth2me/essentials/textreader/KeywordReplacer.java index e61c5ced543..34896e6e71f 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/textreader/KeywordReplacer.java +++ b/Essentials/src/main/java/com/earth2me/essentials/textreader/KeywordReplacer.java @@ -4,7 +4,6 @@ import com.earth2me.essentials.ExecuteTimer; import com.earth2me.essentials.PlayerList; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.DateUtil; import com.earth2me.essentials.utils.DescParseTickFormat; import com.earth2me.essentials.utils.EnumUtil; @@ -228,7 +227,7 @@ private String replaceLine(String line, final String fullMatch, final String[] m break; case BALANCE: if (user != null) { - replacer = AdventureUtil.miniToLegacy(NumberUtil.displayCurrency(user.getMoney(), ess)); + replacer = ess.getAdventureFacet().miniToLegacy(NumberUtil.displayCurrency(user.getMoney(), ess)); } break; case MAILS: diff --git a/Essentials/src/main/java/com/earth2me/essentials/updatecheck/UpdateChecker.java b/Essentials/src/main/java/com/earth2me/essentials/updatecheck/UpdateChecker.java index 7476356bbc8..0f596a1e3f6 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/updatecheck/UpdateChecker.java +++ b/Essentials/src/main/java/com/earth2me/essentials/updatecheck/UpdateChecker.java @@ -2,10 +2,10 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.Essentials; +import com.earth2me.essentials.adventure.ComponentHolder; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonSyntaxException; -import net.kyori.adventure.text.Component; import java.io.BufferedReader; import java.io.IOException; @@ -71,7 +71,7 @@ public boolean isDevBuild() { } public CompletableFuture fetchLatestDev() { - if (cachedDev == null || ((System.currentTimeMillis() - lastFetchTime) > 1800000L)) { + if (cachedDev == null || System.currentTimeMillis() - lastFetchTime > 1800000L) { if (pendingDevFuture != null) { return pendingDevFuture; } @@ -87,7 +87,7 @@ public CompletableFuture fetchLatestDev() { } public CompletableFuture fetchLatestRelease() { - if (cachedRelease == null || ((System.currentTimeMillis() - lastFetchTime) > 1800000L)) { + if (cachedRelease == null || System.currentTimeMillis() - lastFetchTime > 1800000L) { if (pendingReleaseFuture != null) { return pendingReleaseFuture; } @@ -206,56 +206,56 @@ private RemoteVersion fetchDistance(final String head, final String hash) { } } - public Component[] getVersionMessages(final boolean sendLatestMessage, final boolean verboseErrors, final CommandSource source) { + public ComponentHolder[] getVersionMessages(final boolean sendLatestMessage, final boolean verboseErrors, final CommandSource source) { if (!ess.getSettings().isUpdateCheckEnabled()) { - return new Component[] {source.tlComponent("versionCheckDisabled")}; + return new ComponentHolder[] {source.tlComponent("versionCheckDisabled")}; } if (this.isDevBuild()) { final RemoteVersion latestDev = this.fetchLatestDev().join(); switch (latestDev.getBranchStatus()) { case IDENTICAL: { - return sendLatestMessage ? new Component[] {source.tlComponent("versionDevLatest")} : new Component[] {}; + return sendLatestMessage ? new ComponentHolder[] {source.tlComponent("versionDevLatest")} : new ComponentHolder[] {}; } case BEHIND: { - return new Component[] {source.tlComponent("versionDevBehind", latestDev.getDistance()), + return new ComponentHolder[] {source.tlComponent("versionDevBehind", latestDev.getDistance()), source.tlComponent("versionReleaseNewLink", "https://essentialsx.net/downloads.html")}; } case AHEAD: case DIVERGED: { - return new Component[] {source.tlComponent(latestDev.getDistance() == 0 ? "versionDevDivergedLatest" : "versionDevDiverged", latestDev.getDistance()), + return new ComponentHolder[] {source.tlComponent(latestDev.getDistance() == 0 ? "versionDevDivergedLatest" : "versionDevDiverged", latestDev.getDistance()), source.tlComponent("versionDevDivergedBranch", this.getVersionBranch()) }; } case UNKNOWN: { - return verboseErrors ? new Component[] {source.tlComponent("versionCustom", this.getBuildInfo())} : new Component[] {}; + return verboseErrors ? new ComponentHolder[] {source.tlComponent("versionCustom", this.getBuildInfo())} : new ComponentHolder[] {}; } case ERROR: { - return new Component[] {source.tlComponent(verboseErrors ? "versionError" : "versionErrorPlayer", this.getBuildInfo())}; + return new ComponentHolder[] {source.tlComponent(verboseErrors ? "versionError" : "versionErrorPlayer", this.getBuildInfo())}; } default: { - return new Component[] {}; + return new ComponentHolder[] {}; } } } else { final RemoteVersion latestRelease = this.fetchLatestRelease().join(); switch (latestRelease.getBranchStatus()) { case IDENTICAL: { - return sendLatestMessage ? new Component[] {source.tlComponent("versionReleaseLatest")} : new Component[] {}; + return sendLatestMessage ? new ComponentHolder[] {source.tlComponent("versionReleaseLatest")} : new ComponentHolder[] {}; } case BEHIND: { - return new Component[] {source.tlComponent("versionReleaseNew", this.getLatestRelease()), + return new ComponentHolder[] {source.tlComponent("versionReleaseNew", this.getLatestRelease()), source.tlComponent("versionReleaseNewLink", "https://essentialsx.net/downloads.html?branch=stable")}; } case DIVERGED: //WhatChamp case AHEAD: //monkaW? case UNKNOWN: { - return verboseErrors ? new Component[] {source.tlComponent("versionCustom", this.getBuildInfo())} : new Component[] {}; + return verboseErrors ? new ComponentHolder[] {source.tlComponent("versionCustom", this.getBuildInfo())} : new ComponentHolder[] {}; } case ERROR: { - return new Component[] {source.tlComponent(verboseErrors ? "versionError" : "versionErrorPlayer", this.getBuildInfo())}; + return new ComponentHolder[] {source.tlComponent(verboseErrors ? "versionError" : "versionErrorPlayer", this.getBuildInfo())}; } default: { - return new Component[] {}; + return new ComponentHolder[] {}; } } } diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/AdventureUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/AdventureUtil.java deleted file mode 100644 index 37046bd9225..00000000000 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/AdventureUtil.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.earth2me.essentials.utils; - -import net.ess3.api.IEssentials; -import net.ess3.provider.AbstractChatEvent; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.flattener.ComponentFlattener; -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.minimessage.tag.Tag; -import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; - -public final class AdventureUtil { - private static final LegacyComponentSerializer LEGACY_SERIALIZER; - private static final MiniMessage MINI_MESSAGE_NO_TAGS; - private static final String LOOKUP = "0123456789abcdefklmnor"; - private static final NamedTextColor[] COLORS = new NamedTextColor[]{NamedTextColor.BLACK, NamedTextColor.DARK_BLUE, NamedTextColor.DARK_GREEN, NamedTextColor.DARK_AQUA, NamedTextColor.DARK_RED, NamedTextColor.DARK_PURPLE, NamedTextColor.GOLD, NamedTextColor.GRAY, NamedTextColor.DARK_GRAY, NamedTextColor.BLUE, NamedTextColor.GREEN, NamedTextColor.AQUA, NamedTextColor.RED, NamedTextColor.LIGHT_PURPLE, NamedTextColor.YELLOW, NamedTextColor.WHITE}; - private static IEssentials ess; - private static MiniMessage miniMessageInstance; - - static { - final LegacyComponentSerializer.Builder builder = LegacyComponentSerializer.builder() - .flattener(ComponentFlattener.basic()) - .extractUrls(AbstractChatEvent.URL_PATTERN) - .hexColors() - .useUnusualXRepeatedCharacterHexFormat(); - if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_16_1_R01)) { - builder.hexColors(); - } - LEGACY_SERIALIZER = builder.build(); - - MINI_MESSAGE_NO_TAGS = MiniMessage.builder().strict(true).build(); - - miniMessageInstance = createMiniMessageInstance(); - } - - private AdventureUtil() { - } - - public static void setEss(final IEssentials ess) { - AdventureUtil.ess = ess; - miniMessageInstance = createMiniMessageInstance(); - } - - private static MiniMessage createMiniMessageInstance() { - return MiniMessage.builder() - .tags(TagResolver.builder() - .resolvers(TagResolver.standard()) - .resolver(TagResolver.resolver("primary", supplyTag(true))) - .resolver(TagResolver.resolver("secondary", supplyTag(false))) - .build()) - .build(); - } - - public static MiniMessage miniMessage() { - return miniMessageInstance; - } - - /** - * Converts a section sign legacy string to an adventure component. - */ - public static Component legacyToAdventure(final String text) { - return LEGACY_SERIALIZER.deserialize(text); - } - - /** - * Converts an adventure component to a section sign legacy string. - */ - public static String adventureToLegacy(final Component component) { - return LEGACY_SERIALIZER.serialize(component); - } - - /** - * Converts a MiniMessage string to a section sign legacy string. - */ - public static String miniToLegacy(final String format) { - return adventureToLegacy(miniMessage().deserialize(format)); - } - - /** - * Converts a section sign legacy string to a MiniMessage string. - */ - public static String legacyToMini(String text) { - return legacyToMini(text, false); - } - - /** - * Converts a section sign legacy string to a MiniMessage string. - * - * @param useCustomTags true if gold and red colors should use primary and secondary tags instead. - */ - public static String legacyToMini(String text, boolean useCustomTags) { - final Component deserializedText = LEGACY_SERIALIZER.deserialize(text); - if (useCustomTags) { - return miniMessageInstance.serialize(deserializedText); - } else { - return MINI_MESSAGE_NO_TAGS.serialize(deserializedText); - } - } - - /** - * Get the {@link NamedTextColor} from its associated section sign char. - */ - public static NamedTextColor fromChar(final char c) { - final int index = LOOKUP.indexOf(c); - if (index == -1 || index > 15) { - return null; - } - return COLORS[index]; - } - - /** - * Convenience method for submodules to escape MiniMessage tags. - */ - public static String escapeTags(final String input) { - return miniMessage().escapeTags(input); - } - - /** - * Parameters for a translation message are not parsed for MiniMessage by default to avoid injection. If you want - * a parameter to be parsed for MiniMessage you must wrap it in a ParsedPlaceholder by using this method. - */ - public static ParsedPlaceholder parsed(final String literal) { - return new ParsedPlaceholder(literal); - } - - private static Tag supplyTag(final boolean primary) { - if (primary) { - return ess != null ? ess.getSettings().getPrimaryColor() : Tag.styling(NamedTextColor.GOLD); - } - return ess != null ? ess.getSettings().getSecondaryColor() : Tag.styling(NamedTextColor.RED); - } - - public static class ParsedPlaceholder { - private final String value; - - protected ParsedPlaceholder(String value) { - this.value = value; - } - - @Override - public String toString() { - return value; - } - } -} diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/CommonPlaceholders.java b/Essentials/src/main/java/com/earth2me/essentials/utils/CommonPlaceholders.java index 9910940ae4e..03a2eaff3aa 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/CommonPlaceholders.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/CommonPlaceholders.java @@ -1,6 +1,7 @@ package com.earth2me.essentials.utils; import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.adventure.AdventureUtil; public final class CommonPlaceholders { private CommonPlaceholders() { diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/FormatUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/FormatUtil.java index 02c6dc1d817..747034fac56 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/FormatUtil.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/FormatUtil.java @@ -1,5 +1,6 @@ package com.earth2me.essentials.utils; +import com.earth2me.essentials.adventure.AdventureUtil; import net.ess3.api.IUser; import net.ess3.provider.AbstractChatEvent; import org.bukkit.ChatColor; @@ -44,7 +45,7 @@ public static String stripMiniFormat(final String input) { if (input == null) { return null; } - return AdventureUtil.miniMessage().stripTags(input); + return AdventureUtil.getAdventureFacet().stripTags(input); } //This method is used to simply strip the & convention colour codes diff --git a/Essentials/src/main/java/net/ess3/api/TranslatableException.java b/Essentials/src/main/java/net/ess3/api/TranslatableException.java index 87ac813a3f5..704c3111d17 100644 --- a/Essentials/src/main/java/net/ess3/api/TranslatableException.java +++ b/Essentials/src/main/java/net/ess3/api/TranslatableException.java @@ -1,6 +1,6 @@ package net.ess3.api; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import static com.earth2me.essentials.I18n.tlLiteral; @@ -42,6 +42,6 @@ public Object[] getArgs() { @Override public String getMessage() { final String literal = tlLiteral(tlKey, args); - return AdventureUtil.miniToLegacy(literal); + return AdventureUtil.getAdventureFacet().miniToLegacy(literal); } } diff --git a/Essentials/src/main/resources/messages.properties b/Essentials/src/main/resources/messages.properties index 38983e3efd8..2eb9381f265 100644 --- a/Essentials/src/main/resources/messages.properties +++ b/Essentials/src/main/resources/messages.properties @@ -1136,14 +1136,14 @@ serverFull=Server is full\! whitelistKick=Whitelist enabled\! serverReloading=There's a good chance you're reloading your server right now. If that's the case, why do you hate yourself? Expect no support from the EssentialsX team when using /reload. serverTotal=Server Total\: {0} -serverSnapshot=You are running a development snapshot build of Minecraft\! EssentialsX may not work correctly on this version, and it is not supported. -serverUnsupported=You are running an unsupported server version\! -serverUnsupportedClass=Status determining class\: {0} -serverUnsupportedCleanroom=You are running a server that does not properly support Bukkit plugins that rely on internal Mojang code. Consider using an Essentials replacement for your server software. -serverUnsupportedDangerous=You are running a server fork that is known to be extremely dangerous and lead to data loss. It is strongly recommended you switch to a more stable server software like Paper. -serverUnsupportedLimitedApi=You are running a server with limited API functionality. EssentialsX will still work, but certain features may be disabled. -serverUnsupportedDumbPlugins=You are using plugins known to cause severe issues with EssentialsX and other plugins. -serverUnsupportedMods=You are running a server that does not properly support Bukkit plugins. Bukkit plugins should not be used with Forge/Fabric mods\! For Forge\: Consider using ForgeEssentials, or SpongeForge + Nucleus. +serverSnapshot=You are running a development snapshot build of Minecraft\! EssentialsX may not work correctly on this version, and it is not supported. +serverUnsupported=You are running an unsupported server version\! +serverUnsupportedClass=Status determining class\: {0} +serverUnsupportedCleanroom=You are running a server that does not properly support Bukkit plugins that rely on internal Mojang code. Consider using an Essentials replacement for your server software. +serverUnsupportedDangerous=You are running a server fork that is known to be extremely dangerous and lead to data loss. It is strongly recommended you switch to a more stable server software like Paper. +serverUnsupportedLimitedApi=You are running a server with limited API functionality. EssentialsX will still work, but certain features may be disabled. +serverUnsupportedDumbPlugins=You are using plugins known to cause severe issues with EssentialsX and other plugins. +serverUnsupportedMods=You are running a server that does not properly support Bukkit plugins. Bukkit plugins should not be used with Forge/Fabric mods\! Remove your hybrid server jar, and use the FTB Essentials mod for similar features to EssentialsX. setBal=Your balance was set to {0}. setBalOthers=You set {0}''s balance to {1}. setSpawner=Changed spawner type to {0}. diff --git a/Essentials/src/test/java/com/earth2me/essentials/EconomyTest.java b/Essentials/src/test/java/com/earth2me/essentials/EconomyTest.java index cfc5729cd03..401d29e14d1 100644 --- a/Essentials/src/test/java/com/earth2me/essentials/EconomyTest.java +++ b/Essentials/src/test/java/com/earth2me/essentials/EconomyTest.java @@ -4,7 +4,6 @@ import com.earth2me.essentials.api.UserDoesNotExistException; import com.earth2me.essentials.commands.IEssentialsCommand; import com.earth2me.essentials.commands.NoChargeException; -import com.earth2me.essentials.utils.AdventureUtil; import net.ess3.api.Economy; import net.ess3.api.MaxMoneyException; import org.bukkit.command.CommandSender; @@ -149,7 +148,7 @@ public void testNegativePayCommand() { try { runCommand("pay", user1, PLAYERNAME2 + " -123"); } catch (final Exception e) { - Assertions.assertEquals(AdventureUtil.miniToLegacy(I18n.tlLiteral("payMustBePositive")), e.getMessage()); + Assertions.assertEquals(ess.getAdventureFacet().miniToLegacy(I18n.tlLiteral("payMustBePositive")), e.getMessage()); } } } diff --git a/EssentialsAntiBuild/src/main/java/com/earth2me/essentials/antibuild/EssentialsConnect.java b/EssentialsAntiBuild/src/main/java/com/earth2me/essentials/antibuild/EssentialsConnect.java index fffece6367c..89433503bb0 100644 --- a/EssentialsAntiBuild/src/main/java/com/earth2me/essentials/antibuild/EssentialsConnect.java +++ b/EssentialsAntiBuild/src/main/java/com/earth2me/essentials/antibuild/EssentialsConnect.java @@ -2,7 +2,7 @@ import com.earth2me.essentials.IConf; import com.earth2me.essentials.User; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import net.ess3.api.IEssentials; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -18,7 +18,7 @@ class EssentialsConnect { EssentialsConnect(final Plugin essPlugin, final Plugin essProtect) { if (!essProtect.getDescription().getVersion().equals(essPlugin.getDescription().getVersion())) { - essProtect.getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("versionMismatchAll"))); + essProtect.getLogger().log(Level.WARNING, AdventureUtil.getAdventureFacet().miniToLegacy(tlLiteral("versionMismatchAll"))); } ess = (IEssentials) essPlugin; protect = (IAntiBuild) essProtect; @@ -33,7 +33,7 @@ IEssentials getEssentials() { void alert(final User user, final String item, final String tlKey) { final Location loc = user.getLocation(); - protect.getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("alertFormat", user.getName(), tlLiteral(tlKey), item, loc.getWorld().getName() + "," + loc.getBlockX() + "," + loc.getBlockY() + "," + loc.getBlockZ()))); + protect.getLogger().log(Level.WARNING, AdventureUtil.getAdventureFacet().miniToLegacy(tlLiteral("alertFormat", user.getName(), tlLiteral(tlKey), item, loc.getWorld().getName() + "," + loc.getBlockX() + "," + loc.getBlockY() + "," + loc.getBlockZ()))); for (final Player p : ess.getServer().getOnlinePlayers()) { final User alertUser = ess.getUser(p); if (alertUser.isAuthorized("essentials.protect.alerts")) { diff --git a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/EssentialsChat.java b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/EssentialsChat.java index 8db4743a03e..f7db3796d28 100644 --- a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/EssentialsChat.java +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/EssentialsChat.java @@ -5,7 +5,6 @@ import com.earth2me.essentials.chat.processing.ChatHandler; import com.earth2me.essentials.chat.processing.PaperChatHandler; import com.earth2me.essentials.metrics.MetricsWrapper; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.VersionUtil; import net.ess3.api.IEssentials; import org.bukkit.command.Command; @@ -27,7 +26,7 @@ public void onEnable() { final PluginManager pluginManager = getServer().getPluginManager(); ess = (IEssentials) pluginManager.getPlugin("Essentials"); if (!this.getDescription().getVersion().equals(ess.getDescription().getVersion())) { - getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("versionMismatchAll"))); + getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(tlLiteral("versionMismatchAll"))); } if (!ess.isEnabled()) { this.setEnabled(false); diff --git a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AbstractChatHandler.java b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AbstractChatHandler.java index 3c1c4716bf3..1b28e1e6561 100644 --- a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AbstractChatHandler.java +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AbstractChatHandler.java @@ -5,7 +5,6 @@ import com.earth2me.essentials.Trade; import com.earth2me.essentials.User; import com.earth2me.essentials.chat.EssentialsChat; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import net.ess3.api.events.LocalChatSpyEvent; import net.ess3.provider.AbstractChatEvent; @@ -117,9 +116,9 @@ protected void handleChatFormat(AbstractChatEvent event) { } if (chat.getType() == ChatType.UNKNOWN) { - format = AdventureUtil.miniToLegacy(tlLiteral("chatTypeLocal")).concat(format); + format = ess.getAdventureFacet().miniToLegacy(tlLiteral("chatTypeLocal")).concat(format); } else { - format = AdventureUtil.miniToLegacy(tlLiteral(chat.getType().key() + "Format", format)); + format = ess.getAdventureFacet().miniToLegacy(tlLiteral(chat.getType().key() + "Format", format)); } } @@ -215,7 +214,7 @@ protected void handleChatRecipients(AbstractChatEvent event) { // Strip local chat prefix to preserve API behaviour final String localPrefix = tlLiteral("chatTypeLocal"); - String baseFormat = AdventureUtil.legacyToMini(event.getFormat()); + String baseFormat = ess.getAdventureFacet().legacyToMini(event.getFormat()); if (baseFormat.startsWith(localPrefix)) { baseFormat = baseFormat.substring(localPrefix.length()); } @@ -224,7 +223,7 @@ protected void handleChatRecipients(AbstractChatEvent event) { server.getPluginManager().callEvent(spyEvent); if (!spyEvent.isCancelled()) { - final String legacyString = AdventureUtil.miniToLegacy(String.format(spyEvent.getFormat(), AdventureUtil.legacyToMini(user.getDisplayName()), AdventureUtil.legacyToMini(AdventureUtil.escapeTags(spyEvent.getMessage())))); + final String legacyString = ess.getAdventureFacet().miniToLegacy(String.format(spyEvent.getFormat(), ess.getAdventureFacet().legacyToMini(user.getDisplayName()), ess.getAdventureFacet().legacyToMini(ess.getAdventureFacet().escapeTags(spyEvent.getMessage())))); for (final Player onlinePlayer : spyEvent.getRecipients()) { onlinePlayer.sendMessage(legacyString); diff --git a/EssentialsDiscord/src/main/java/net/essentialsx/discord/DiscordSettings.java b/EssentialsDiscord/src/main/java/net/essentialsx/discord/DiscordSettings.java index 3aed831ad40..f2526a08cde 100644 --- a/EssentialsDiscord/src/main/java/net/essentialsx/discord/DiscordSettings.java +++ b/EssentialsDiscord/src/main/java/net/essentialsx/discord/DiscordSettings.java @@ -3,7 +3,6 @@ import com.earth2me.essentials.IConf; import com.earth2me.essentials.config.ConfigurateUtil; import com.earth2me.essentials.config.EssentialsConfiguration; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.entities.Activity; @@ -485,7 +484,7 @@ private MessageFormat generateMessageFormat(String content, String defaultStr, b @Override public void reloadConfig() { if (plugin.isInvalidStartup()) { - plugin.getLogger().warning(AdventureUtil.miniToLegacy(tlLiteral("discordReloadInvalid"))); + plugin.getLogger().warning(plugin.getEss().getAdventureFacet().miniToLegacy(tlLiteral("discordReloadInvalid"))); return; } diff --git a/EssentialsDiscord/src/main/java/net/essentialsx/discord/EssentialsDiscord.java b/EssentialsDiscord/src/main/java/net/essentialsx/discord/EssentialsDiscord.java index e47e25cdaad..47c19f411bd 100644 --- a/EssentialsDiscord/src/main/java/net/essentialsx/discord/EssentialsDiscord.java +++ b/EssentialsDiscord/src/main/java/net/essentialsx/discord/EssentialsDiscord.java @@ -4,7 +4,6 @@ import com.earth2me.essentials.IEssentials; import com.earth2me.essentials.IEssentialsModule; import com.earth2me.essentials.metrics.MetricsWrapper; -import com.earth2me.essentials.utils.AdventureUtil; import net.essentialsx.discord.interactions.InteractionControllerImpl; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -36,7 +35,7 @@ public void onEnable() { return; } if (!getDescription().getVersion().equals(ess.getDescription().getVersion())) { - getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("versionMismatchAll"))); + getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(tlLiteral("versionMismatchAll"))); } // JDK-8274349 - Mitigation for a regression in Java 17 on 1 core systems which was fixed in 17.0.2 @@ -63,7 +62,7 @@ public void onEnable() { jda.startup(); ess.scheduleSyncDelayedTask(() -> ((InteractionControllerImpl) jda.getInteractionController()).processBatchRegistration()); } catch (Exception e) { - getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("discordErrorLogin", e.getMessage()))); + getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("discordErrorLogin", e.getMessage()))); if (ess.getSettings().isDebug()) { e.printStackTrace(); } diff --git a/EssentialsDiscord/src/main/java/net/essentialsx/discord/interactions/InteractionEventImpl.java b/EssentialsDiscord/src/main/java/net/essentialsx/discord/interactions/InteractionEventImpl.java index ca008c37ff5..7bf82e853dc 100644 --- a/EssentialsDiscord/src/main/java/net/essentialsx/discord/interactions/InteractionEventImpl.java +++ b/EssentialsDiscord/src/main/java/net/essentialsx/discord/interactions/InteractionEventImpl.java @@ -1,6 +1,6 @@ package net.essentialsx.discord.interactions; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import com.google.common.base.Joiner; import net.dv8tion.jda.api.entities.Message; @@ -50,7 +50,7 @@ public void reply(String message) { @Override public void replyTl(String tlKey, Object... args) { - reply(AdventureUtil.miniToLegacy(tlLiteral(tlKey, args))); + reply(AdventureUtil.getAdventureFacet().miniToLegacy(tlLiteral(tlKey, args))); } @Override diff --git a/EssentialsDiscord/src/main/java/net/essentialsx/discord/interactions/commands/MessageCommand.java b/EssentialsDiscord/src/main/java/net/essentialsx/discord/interactions/commands/MessageCommand.java index 28a6ee0d5b4..f0e7ceec73d 100644 --- a/EssentialsDiscord/src/main/java/net/essentialsx/discord/interactions/commands/MessageCommand.java +++ b/EssentialsDiscord/src/main/java/net/essentialsx/discord/interactions/commands/MessageCommand.java @@ -2,7 +2,7 @@ import com.earth2me.essentials.User; import com.earth2me.essentials.commands.PlayerNotFoundException; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import net.essentialsx.api.v2.services.discord.InteractionCommandArgument; import net.essentialsx.api.v2.services.discord.InteractionCommandArgumentType; diff --git a/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/EssentialsDiscordLink.java b/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/EssentialsDiscordLink.java index 8e8165bea89..0d30cd92bf9 100644 --- a/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/EssentialsDiscordLink.java +++ b/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/EssentialsDiscordLink.java @@ -3,7 +3,6 @@ import com.earth2me.essentials.EssentialsLogger; import com.earth2me.essentials.IEssentials; import com.earth2me.essentials.metrics.MetricsWrapper; -import com.earth2me.essentials.utils.AdventureUtil; import com.google.common.collect.ImmutableSet; import net.essentialsx.api.v2.services.discord.DiscordService; import net.essentialsx.api.v2.services.discord.InteractionException; @@ -45,7 +44,7 @@ public void onEnable() { return; } if (!getDescription().getVersion().equals(ess.getDescription().getVersion())) { - getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("versionMismatchAll"))); + getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(tlLiteral("versionMismatchAll"))); } api = getServer().getServicesManager().load(DiscordService.class); diff --git a/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/listeners/LinkBukkitListener.java b/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/listeners/LinkBukkitListener.java index 8991633a7fa..959a30adf33 100644 --- a/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/listeners/LinkBukkitListener.java +++ b/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/listeners/LinkBukkitListener.java @@ -1,6 +1,5 @@ package net.essentialsx.discordlink.listeners; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import net.essentialsx.api.v2.events.AsyncUserDataLoadEvent; import net.essentialsx.api.v2.events.UserMailEvent; @@ -60,7 +59,7 @@ public void onConnect(final AsyncPlayerPreLoginEvent event) { } catch (IllegalArgumentException e) { code = e.getMessage(); } - event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, AdventureUtil.miniToLegacy(tlLiteral("discordLinkLoginKick", "/link " + code, ess.getApi().getInviteUrl()))); + event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, ess.getEss().getAdventureFacet().miniToLegacy(tlLiteral("discordLinkLoginKick", "/link " + code, ess.getApi().getInviteUrl()))); } } @@ -160,7 +159,7 @@ public void onUserLinkStatusChange(final DiscordLinkStatusChangeEvent event) { switch (ess.getSettings().getLinkPolicy()) { case KICK: { - final Runnable kickTask = () -> event.getUser().getBase().kickPlayer(AdventureUtil.miniToLegacy(event.getUser().playerTl("discordLinkLoginKick", "/link " + finalCode, ess.getApi().getInviteUrl()))); + final Runnable kickTask = () -> event.getUser().getBase().kickPlayer(ess.getEss().getAdventureFacet().miniToLegacy(event.getUser().playerTl("discordLinkLoginKick", "/link " + finalCode, ess.getApi().getInviteUrl()))); if (Bukkit.isPrimaryThread()) { kickTask.run(); } else { diff --git a/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/rolesync/RoleSyncManager.java b/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/rolesync/RoleSyncManager.java index 4545b691c06..8e9c31d74f4 100644 --- a/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/rolesync/RoleSyncManager.java +++ b/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/rolesync/RoleSyncManager.java @@ -1,7 +1,6 @@ package net.essentialsx.discordlink.rolesync; import com.earth2me.essentials.UUIDPlayer; -import com.earth2me.essentials.utils.AdventureUtil; import com.google.common.collect.BiMap; import net.essentialsx.api.v2.events.discordlink.DiscordLinkStatusChangeEvent; import net.essentialsx.api.v2.services.discord.InteractionMember; @@ -146,21 +145,21 @@ public void onReload() { final String group = entry.getKey(); final InteractionRole role = ess.getApi().getRole(entry.getValue()); if (!groups.contains(group)) { - ess.getLogger().warning(AdventureUtil.miniToLegacy(tlLiteral("discordLinkInvalidGroup", group, entry.getValue(), groups))); + ess.getLogger().warning(ess.getEss().getAdventureFacet().miniToLegacy(tlLiteral("discordLinkInvalidGroup", group, entry.getValue(), groups))); continue; } if (role == null) { - ess.getLogger().warning(AdventureUtil.miniToLegacy(tlLiteral("discordLinkInvalidRole", entry.getValue(), group))); + ess.getLogger().warning(ess.getEss().getAdventureFacet().miniToLegacy(tlLiteral("discordLinkInvalidRole", entry.getValue(), group))); continue; } if (role.isManaged() || role.isPublicRole()) { - ess.getLogger().warning(AdventureUtil.miniToLegacy(tlLiteral("discordLinkInvalidRoleManaged", role.getName(), role.getId()))); + ess.getLogger().warning(ess.getEss().getAdventureFacet().miniToLegacy(tlLiteral("discordLinkInvalidRoleManaged", role.getName(), role.getId()))); continue; } if (!role.canInteract()) { - ess.getLogger().warning(AdventureUtil.miniToLegacy(tlLiteral("discordLinkInvalidRoleInteract", role.getName(), role.getId()))); + ess.getLogger().warning(ess.getEss().getAdventureFacet().miniToLegacy(tlLiteral("discordLinkInvalidRoleInteract", role.getName(), role.getId()))); continue; } @@ -175,11 +174,11 @@ public void onReload() { final InteractionRole role = ess.getApi().getRole(entry.getKey()); final String group = entry.getValue(); if (role == null) { - ess.getLogger().warning(AdventureUtil.miniToLegacy(tlLiteral("discordLinkInvalidRole", entry.getKey(), group))); + ess.getLogger().warning(ess.getEss().getAdventureFacet().miniToLegacy(tlLiteral("discordLinkInvalidRole", entry.getKey(), group))); continue; } if (!groups.contains(group)) { - ess.getLogger().warning(AdventureUtil.miniToLegacy(tlLiteral("discordLinkInvalidGroup", group, entry.getKey(), groups))); + ess.getLogger().warning(ess.getEss().getAdventureFacet().miniToLegacy(tlLiteral("discordLinkInvalidGroup", group, entry.getKey(), groups))); continue; } diff --git a/EssentialsGeoIP/src/main/java/com/earth2me/essentials/geoip/EssentialsGeoIP.java b/EssentialsGeoIP/src/main/java/com/earth2me/essentials/geoip/EssentialsGeoIP.java index cdb2d5ed004..7613a34f7e4 100644 --- a/EssentialsGeoIP/src/main/java/com/earth2me/essentials/geoip/EssentialsGeoIP.java +++ b/EssentialsGeoIP/src/main/java/com/earth2me/essentials/geoip/EssentialsGeoIP.java @@ -2,7 +2,6 @@ import com.earth2me.essentials.EssentialsLogger; import com.earth2me.essentials.metrics.MetricsWrapper; -import com.earth2me.essentials.utils.AdventureUtil; import net.ess3.api.IEssentials; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; @@ -22,7 +21,7 @@ public void onEnable() { final PluginManager pm = getServer().getPluginManager(); final IEssentials ess = (IEssentials) pm.getPlugin("Essentials"); if (!this.getDescription().getVersion().equals(ess.getDescription().getVersion())) { - getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("versionMismatchAll"))); + getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(tlLiteral("versionMismatchAll"))); } if (!ess.isEnabled()) { this.setEnabled(false); diff --git a/EssentialsGeoIP/src/main/java/com/earth2me/essentials/geoip/EssentialsGeoIPPlayerListener.java b/EssentialsGeoIP/src/main/java/com/earth2me/essentials/geoip/EssentialsGeoIPPlayerListener.java index b8594e267bf..eb75eb5d68a 100644 --- a/EssentialsGeoIP/src/main/java/com/earth2me/essentials/geoip/EssentialsGeoIPPlayerListener.java +++ b/EssentialsGeoIP/src/main/java/com/earth2me/essentials/geoip/EssentialsGeoIPPlayerListener.java @@ -3,7 +3,6 @@ import com.earth2me.essentials.IConf; import com.earth2me.essentials.User; import com.earth2me.essentials.config.EssentialsConfiguration; -import com.earth2me.essentials.utils.AdventureUtil; import com.ice.tar.TarEntry; import com.ice.tar.TarInputStream; import com.maxmind.geoip2.DatabaseReader; @@ -67,7 +66,7 @@ private void delayedJoin(final Player player) { final StringBuilder sb = new StringBuilder(); if (mmreader == null) { - essGeo.getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("geoIpErrorOnJoin", u.getName()))); + essGeo.getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(tlLiteral("geoIpErrorOnJoin", u.getName()))); return; } @@ -107,10 +106,10 @@ private void delayedJoin(final Player player) { } // GeoIP2 API forced this when address not found in their DB. jar will not complied without this. // TODO: Maybe, we can set a new custom msg about addr-not-found in messages.properties. - essGeo.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("cantReadGeoIpDB")) + " " + ex.getLocalizedMessage()); + essGeo.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("cantReadGeoIpDB")) + " " + ex.getLocalizedMessage()); } catch (final IOException | GeoIp2Exception ex) { // GeoIP2 API forced this when address not found in their DB. jar will not complied without this. - essGeo.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("cantReadGeoIpDB")) + " " + ex.getLocalizedMessage()); + essGeo.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("cantReadGeoIpDB")) + " " + ex.getLocalizedMessage()); } if (config.getBoolean("show-on-whois", true)) { u.setGeoLocation(sb.toString()); @@ -154,7 +153,7 @@ public final void reloadConfig() { if (config.getBoolean("database.download-if-missing", true)) { downloadDatabase(); } else { - essGeo.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("cantFindGeoIpDB"))); + essGeo.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("cantFindGeoIpDB"))); return; } } else if (config.getBoolean("database.update.enable", true)) { @@ -178,7 +177,7 @@ public final void reloadConfig() { mmreader = new DatabaseReader.Builder(databaseFile).build(); } } catch (final IOException ex) { - essGeo.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("cantReadGeoIpDB")), ex); + essGeo.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("cantReadGeoIpDB")), ex); } } @@ -191,16 +190,16 @@ private void downloadDatabase() { url = config.getString("database.download-url", null); } if (url == null || url.isEmpty()) { - essGeo.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("geoIpUrlEmpty"))); + essGeo.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("geoIpUrlEmpty"))); return; } final String licenseKey = config.getString("database.license-key", ""); if (licenseKey == null || licenseKey.isEmpty()) { - essGeo.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("geoIpLicenseMissing"))); + essGeo.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("geoIpLicenseMissing"))); return; } url = url.replace("{LICENSEKEY}", licenseKey); - essGeo.getLogger().log(Level.INFO, AdventureUtil.miniToLegacy(tlLiteral("downloadingGeoIp"))); + essGeo.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("downloadingGeoIp"))); final URL downloadUrl = new URL(url); final URLConnection conn = downloadUrl.openConnection(); conn.setConnectTimeout(10000); @@ -234,9 +233,9 @@ private void downloadDatabase() { output.close(); input.close(); } catch (final MalformedURLException ex) { - essGeo.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("geoIpUrlInvalid")), ex); + essGeo.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("geoIpUrlInvalid")), ex); } catch (final IOException ex) { - essGeo.getLogger().log(Level.SEVERE, AdventureUtil.miniToLegacy(tlLiteral("connectionFailed")), ex); + essGeo.getLogger().log(Level.SEVERE, ess.getAdventureFacet().miniToLegacy(tlLiteral("connectionFailed")), ex); } } diff --git a/EssentialsProtect/src/main/java/com/earth2me/essentials/protect/EssentialsConnect.java b/EssentialsProtect/src/main/java/com/earth2me/essentials/protect/EssentialsConnect.java index 75b85772b82..bc625747150 100644 --- a/EssentialsProtect/src/main/java/com/earth2me/essentials/protect/EssentialsConnect.java +++ b/EssentialsProtect/src/main/java/com/earth2me/essentials/protect/EssentialsConnect.java @@ -1,7 +1,7 @@ package com.earth2me.essentials.protect; import com.earth2me.essentials.IConf; -import com.earth2me.essentials.utils.AdventureUtil; +import com.earth2me.essentials.adventure.AdventureUtil; import net.ess3.api.IEssentials; import org.bukkit.plugin.Plugin; @@ -15,7 +15,7 @@ class EssentialsConnect { EssentialsConnect(final Plugin essPlugin, final Plugin essProtect) { if (!essProtect.getDescription().getVersion().equals(essPlugin.getDescription().getVersion())) { - essProtect.getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("versionMismatchAll"))); + essProtect.getLogger().log(Level.WARNING, AdventureUtil.getAdventureFacet().miniToLegacy(tlLiteral("versionMismatchAll"))); } ess = (IEssentials) essPlugin; protect = (IProtect) essProtect; diff --git a/EssentialsSpawn/src/main/java/com/earth2me/essentials/spawn/EssentialsSpawn.java b/EssentialsSpawn/src/main/java/com/earth2me/essentials/spawn/EssentialsSpawn.java index b6faffbf7fc..0d8f80e6d6e 100644 --- a/EssentialsSpawn/src/main/java/com/earth2me/essentials/spawn/EssentialsSpawn.java +++ b/EssentialsSpawn/src/main/java/com/earth2me/essentials/spawn/EssentialsSpawn.java @@ -2,7 +2,6 @@ import com.earth2me.essentials.EssentialsLogger; import com.earth2me.essentials.metrics.MetricsWrapper; -import com.earth2me.essentials.utils.AdventureUtil; import net.ess3.api.IEssentials; import org.bukkit.Location; import org.bukkit.command.Command; @@ -29,7 +28,7 @@ public void onEnable() { final PluginManager pluginManager = getServer().getPluginManager(); ess = (IEssentials) pluginManager.getPlugin("Essentials"); if (!this.getDescription().getVersion().equals(ess.getDescription().getVersion())) { - getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("versionMismatchAll"))); + getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(tlLiteral("versionMismatchAll"))); } if (!ess.isEnabled()) { this.setEnabled(false); diff --git a/EssentialsXMPP/src/main/java/com/earth2me/essentials/xmpp/EssentialsXMPP.java b/EssentialsXMPP/src/main/java/com/earth2me/essentials/xmpp/EssentialsXMPP.java index e3c99061105..e9a6216e7b5 100644 --- a/EssentialsXMPP/src/main/java/com/earth2me/essentials/xmpp/EssentialsXMPP.java +++ b/EssentialsXMPP/src/main/java/com/earth2me/essentials/xmpp/EssentialsXMPP.java @@ -3,7 +3,6 @@ import com.earth2me.essentials.EssentialsLogger; import com.earth2me.essentials.IEssentials; import com.earth2me.essentials.metrics.MetricsWrapper; -import com.earth2me.essentials.utils.AdventureUtil; import net.ess3.api.IUser; import org.bstats.charts.SimplePie; import org.bukkit.command.Command; @@ -42,7 +41,7 @@ public void onEnable() { final PluginManager pluginManager = getServer().getPluginManager(); ess = (IEssentials) pluginManager.getPlugin("Essentials"); if (!this.getDescription().getVersion().equals(ess.getDescription().getVersion())) { - getLogger().log(Level.WARNING, AdventureUtil.miniToLegacy(tlLiteral("versionMismatchAll"))); + getLogger().log(Level.WARNING, ess.getAdventureFacet().miniToLegacy(tlLiteral("versionMismatchAll"))); } if (!ess.isEnabled()) { this.setEnabled(false); diff --git a/EssentialsXMPP/src/main/java/com/earth2me/essentials/xmpp/XMPPManager.java b/EssentialsXMPP/src/main/java/com/earth2me/essentials/xmpp/XMPPManager.java index a5199f9216a..0530d4974c2 100644 --- a/EssentialsXMPP/src/main/java/com/earth2me/essentials/xmpp/XMPPManager.java +++ b/EssentialsXMPP/src/main/java/com/earth2me/essentials/xmpp/XMPPManager.java @@ -3,7 +3,6 @@ import com.earth2me.essentials.Console; import com.earth2me.essentials.IConf; import com.earth2me.essentials.config.EssentialsConfiguration; -import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import net.ess3.api.IUser; import org.bukkit.entity.Player; @@ -159,7 +158,7 @@ final void disconnect() { final void updatePresence() { if (connection == null) { - parent.getEss().getLogger().warning(AdventureUtil.miniToLegacy(tlLiteral("xmppNotConfigured"))); + parent.getEss().getLogger().warning(parent.getEss().getAdventureFacet().miniToLegacy(tlLiteral("xmppNotConfigured"))); return; } diff --git a/providers/BaseProviders/src/main/java/com/earth2me/essentials/adventure/AdventureFacet.java b/providers/BaseProviders/src/main/java/com/earth2me/essentials/adventure/AdventureFacet.java new file mode 100644 index 00000000000..90a581d1e63 --- /dev/null +++ b/providers/BaseProviders/src/main/java/com/earth2me/essentials/adventure/AdventureFacet.java @@ -0,0 +1,58 @@ +package com.earth2me.essentials.adventure; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public interface AdventureFacet { + void close(); + + ComponentHolder deserializeMiniMessage(String message); + + void send(CommandSender sender, ComponentHolder component); + + void send(Player player, ComponentHolder component); + + /** + * Converts a section sign legacy string to a MiniMessage string. + */ + String legacyToMini(String message); + + /** + * Converts a section sign legacy string to a MiniMessage string. + * + * @param useCustomTags true if gold and red colors should use primary and secondary tags instead. + */ + String legacyToMini(String message, boolean useCustomTags); + + /** + * Convenience method for submodules to escape MiniMessage tags. + */ + String escapeTags(String input); + + /** + * Strips all MiniMessage tags from the given input, returning plain text. + */ + String stripTags(String input); + + /** + * Converts a section sign legacy string to an adventure component. + */ + ComponentHolder legacyToAdventure(String message); + + /** + * Converts a plain text string to an adventure component. + */ + ComponentHolder text(String message); + + /** + * Converts a MiniMessage string to a section sign legacy string. + */ + String miniToLegacy(String message); + + /** + * Converts an adventure component to a section sign legacy string. + */ + String adventureToLegacy(ComponentHolder component); + + ComponentHolder append(ComponentHolder base, ComponentHolder... addition); +} diff --git a/providers/BaseProviders/src/main/java/com/earth2me/essentials/adventure/ComponentHolder.java b/providers/BaseProviders/src/main/java/com/earth2me/essentials/adventure/ComponentHolder.java new file mode 100644 index 00000000000..1829ff48cc2 --- /dev/null +++ b/providers/BaseProviders/src/main/java/com/earth2me/essentials/adventure/ComponentHolder.java @@ -0,0 +1,17 @@ +package com.earth2me.essentials.adventure; + +public class ComponentHolder { + private final Object component; + + protected ComponentHolder(final Object component) { + if (component == null) { + throw new IllegalArgumentException("Component cannot be null"); + } + + this.component = component; + } + + public Object getComponent() { + return component; + } +} diff --git a/providers/PaperProvider/src/main/java/com/earth2me/essentials/adventure/PaperAdventureFacet.java b/providers/PaperProvider/src/main/java/com/earth2me/essentials/adventure/PaperAdventureFacet.java new file mode 100644 index 00000000000..fbc7ab39496 --- /dev/null +++ b/providers/PaperProvider/src/main/java/com/earth2me/essentials/adventure/PaperAdventureFacet.java @@ -0,0 +1,138 @@ +package com.earth2me.essentials.adventure; + +import net.ess3.provider.AbstractChatEvent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.flattener.ComponentFlattener; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.Tag; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.Color; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.Locale; + +public class PaperAdventureFacet implements AdventureFacet { + private final LegacyComponentSerializer legacySerializer; + private final MiniMessage miniMessageNoTags; + private final MiniMessage miniMessageInstance; + + private final String primaryColor; + private final String secondaryColor; + + public PaperAdventureFacet(final String primaryColor, final String secondaryColor) { + this.primaryColor = primaryColor != null ? primaryColor : "gold"; + this.secondaryColor = secondaryColor != null ? secondaryColor : "red"; + + final LegacyComponentSerializer.Builder builder = LegacyComponentSerializer.builder() + .flattener(ComponentFlattener.basic()) + .extractUrls(AbstractChatEvent.URL_PATTERN) + .hexColors() + .useUnusualXRepeatedCharacterHexFormat(); + legacySerializer = builder.build(); + + miniMessageNoTags = MiniMessage.builder().strict(true).build(); + + miniMessageInstance = MiniMessage.builder() + .tags(TagResolver.builder() + .resolvers(TagResolver.standard()) + .resolver(TagResolver.resolver("primary", supplyTag(true))) + .resolver(TagResolver.resolver("secondary", supplyTag(false))) + .build()) + .build(); + } + + private Tag supplyTag(final boolean primary) { + final String color = primary ? primaryColor : secondaryColor; + + final TextColor textColor; + if (color.startsWith("#")) { + textColor = TextColor.color(Color.fromRGB(Integer.decode(color)).asRGB()); + } else { + textColor = NamedTextColor.NAMES.value(color.toLowerCase(Locale.ENGLISH)); + } + + return textColor != null ? Tag.styling(textColor) : Tag.styling(primary ? NamedTextColor.GOLD : NamedTextColor.RED); + } + + @Override + public ComponentHolder deserializeMiniMessage(String message) { + return new ComponentHolder(miniMessageInstance.deserialize(message)); + } + + @Override + public void send(CommandSender sender, ComponentHolder component) { + sender.sendMessage((Component) component.getComponent()); + } + + @Override + public void send(Player player, ComponentHolder component) { + player.sendMessage((Component) component.getComponent()); + } + + @Override + public String legacyToMini(String message) { + return legacyToMini(message, true); + } + + @Override + public String legacyToMini(String message, boolean useCustomTags) { + final Component deserializedText = legacySerializer.deserialize(message); + if (useCustomTags) { + return miniMessageInstance.serialize(deserializedText); + } else { + return miniMessageNoTags.serialize(deserializedText); + } + } + + @Override + public String escapeTags(String input) { + return miniMessageInstance.escapeTags(input); + } + + @Override + public String stripTags(String input) { + return miniMessageInstance.stripTags(input); + } + + @Override + public ComponentHolder legacyToAdventure(String message) { + return new ComponentHolder(legacySerializer.deserialize(message)); + } + + @Override + public ComponentHolder text(String message) { + return new ComponentHolder(Component.text(message)); + } + + @Override + public String miniToLegacy(String message) { + return adventureToLegacy(miniMessageInstance.deserialize(message)); + } + + @Override + public String adventureToLegacy(ComponentHolder component) { + return adventureToLegacy((Component) component.getComponent()); + } + + private String adventureToLegacy(Component component) { + return legacySerializer.serialize(component); + } + + @Override + public ComponentHolder append(ComponentHolder base, ComponentHolder... addition) { + Component baseComponent = (Component) base.getComponent(); + for (ComponentHolder holder : addition) { + final Component additionComponent = (Component) holder.getComponent(); + baseComponent = baseComponent.append(additionComponent); + } + return new ComponentHolder(baseComponent); + } + + @Override + public void close() { + } +} From 18549a718afd48f2c7ce141ad44c187ec46855c3 Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Sun, 22 Feb 2026 13:36:39 -0800 Subject: [PATCH 08/37] Fix /ess reload (#6444) --- .../com/earth2me/essentials/commands/Commandessentials.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandessentials.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandessentials.java index 74070bb43c6..8bb55af72e0 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandessentials.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandessentials.java @@ -8,6 +8,7 @@ import com.earth2me.essentials.commands.essentials.ItemTestCommand; import com.earth2me.essentials.commands.essentials.MooCommand; import com.earth2me.essentials.commands.essentials.NyanCommand; +import com.earth2me.essentials.commands.essentials.ReloadCommand; import com.earth2me.essentials.commands.essentials.UsermapCommand; import com.earth2me.essentials.commands.essentials.VersionCommand; @@ -19,6 +20,7 @@ public Commandessentials() { registerNode(new DebugCommand()); registerNode(new CommandMapCommand()); registerNode(new DumpCommand()); + registerNode(new ReloadCommand()); registerNode(new CleanupCommand()); registerNode(new HomesCommand()); registerNode(new UsermapCommand()); From f080f815faf7b850ef01b3d8ae4100519bcc1263 Mon Sep 17 00:00:00 2001 From: BestZige <113977019+bestzige@users.noreply.github.com> Date: Tue, 24 Feb 2026 00:14:49 +0700 Subject: [PATCH 09/37] fix(commands): propagate Essentials and module to tree nodes (#6445) ### What does this PR do? Fixes an issue where `EssentialsTreeNode` instances did not receive updated `Essentials` and `IEssentialsModule` references after registration. This could lead to null references during command execution, especially in test environments. ### Changes made - Override `setEssentials` in `EssentialsTreeCommand` - Override `setEssentialsModule` in `EssentialsTreeCommand` - Propagate updated references to all registered nodes ### Why is this needed? Nodes only received references during registration. If the parent command updated its references later, the nodes would retain outdated (null) values. ### Does this PR break compatibility? No. --- .../commands/EssentialsTreeCommand.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsTreeCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsTreeCommand.java index acac81251b5..27641844d75 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsTreeCommand.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/EssentialsTreeCommand.java @@ -1,7 +1,9 @@ package com.earth2me.essentials.commands; import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.IEssentialsModule; import com.earth2me.essentials.User; +import net.ess3.api.IEssentials; import org.bukkit.Server; import java.util.ArrayList; @@ -10,6 +12,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.HashSet; public class EssentialsTreeCommand extends EssentialsCommand { private final Map nodes = new HashMap<>(); @@ -32,6 +35,22 @@ protected void registerNode(final EssentialsTreeNode node) { node.setParent(this); } + @Override + public void setEssentials(final IEssentials ess) { + super.setEssentials(ess); + for (final EssentialsTreeNode node : new HashSet<>(nodes.values())) { + node.setEssentials(ess); + } + } + + @Override + public void setEssentialsModule(final IEssentialsModule module) { + super.setEssentialsModule(module); + for (final EssentialsTreeNode node : new HashSet<>(nodes.values())) { + node.setEssentialsModule(module); + } + } + public void runDefault(final User user, final String commandLabel) throws Exception { runDefault(user.getSource(), commandLabel); } From b8acd9c3ab381564adbdf1629a78b4b1cbcc4201 Mon Sep 17 00:00:00 2001 From: Gavin Heatherington <91021615+gheatherington@users.noreply.github.com> Date: Sat, 28 Feb 2026 16:22:50 -0500 Subject: [PATCH 10/37] Allow discord console relay to be filtered by logger name (#6448) Rearranged logger addition to message entry for filtering by logger name. --------- Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com> --- .../essentialsx/discord/util/ConsoleInjector.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/ConsoleInjector.java b/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/ConsoleInjector.java index 52ea96c64a8..4794ddce786 100644 --- a/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/ConsoleInjector.java +++ b/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/ConsoleInjector.java @@ -124,6 +124,13 @@ public void append(LogEvent event) { return; } + final String[] loggerNameSplit = event.getLoggerName().split("\\."); + final String loggerName = loggerNameSplit[loggerNameSplit.length - 1].trim(); + + if (!loggerName.isEmpty()) { + entry = "[" + loggerName + "] " + entry; + } + if (!jda.getSettings().getConsoleFilters().isEmpty()) { for (final Pattern pattern : jda.getSettings().getConsoleFilters()) { if (pattern.matcher(entry).find()) { @@ -132,13 +139,6 @@ public void append(LogEvent event) { } } - final String[] loggerNameSplit = event.getLoggerName().split("\\."); - final String loggerName = loggerNameSplit[loggerNameSplit.length - 1].trim(); - - if (!loggerName.isEmpty()) { - entry = "[" + loggerName + "] " + entry; - } - messageQueue.addAll(Splitter.fixedLength(Message.MAX_CONTENT_LENGTH - 50).splitToList( MessageUtil.formatMessage(jda.getSettings().getConsoleFormat(), TimeFormat.TIME_LONG.format(Instant.now()), From 5abe8b61efe1180e63581d5fc5a5c53e70d00345 Mon Sep 17 00:00:00 2001 From: UDPSendToFailed Date: Sun, 1 Mar 2026 00:03:48 +0100 Subject: [PATCH 11/37] Fix server freezing with /give (#6449) ### Information This PR fixes a bug where anyone with access to the /give command could freeze the server. ### Details Currently, if a user has permission to spawn items with metadata, they can freeze or completely crash the server by supplying a massive integer for a book's page number (e.g., `/give written_book 1 page2147483647:payload`). Because there is no upper bound check in `MetaItemStack.java`, the internal `for` loop tries to allocate an `ArrayList` with billions of empty strings to reach that requested page index. This instantly exhausts the JVM heap and triggers an OutOfMemoryError, hard-crashing the server thread, or at least freezing it entirely for seconds. This PR fixes the issue by introducing a hard limit of 100 pages for book metadata parsing. If the requested page exceeds this limit, the loop doesn't execute and it instead throws a newly added `pageLimitExceeded` translatable exception. **Environments tested:** OS: Windows 11 25H2 26200.7922 Java version: Java 21 (OpenJDK 64-Bit Server VM 21.0.10+7-LTS; Eclipse Adoptium Temurin-21.0.10+7) Purpur version 1.21.11-2545 **Demonstration:** Before: image After: image Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com> --- .../java/com/earth2me/essentials/MetaItemStack.java | 10 ++++++++-- Essentials/src/main/resources/messages.properties | 1 + Essentials/src/main/resources/messages_en.properties | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/MetaItemStack.java b/Essentials/src/main/java/com/earth2me/essentials/MetaItemStack.java index 43e6c6f026e..48c6984a32f 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/MetaItemStack.java +++ b/Essentials/src/main/java/com/earth2me/essentials/MetaItemStack.java @@ -308,8 +308,14 @@ public void addStringMeta(final CommandSource sender, final boolean allowUnsafe, final BookMeta meta = (BookMeta) stack.getItemMeta(); meta.setTitle(title); stack.setItemMeta(meta); - } else if (split.length > 1 && split[0].startsWith("page") && split[0].length() > 4 && MaterialUtil.isEditableBook(stack.getType()) && hasMetaPermission(sender, "page", false, true, ess)) { - final int page = NumberUtil.isInt(split[0].substring(4)) ? (Integer.parseInt(split[0].substring(4)) - 1) : 0; + } else if (split.length > 1 && split[0].startsWith("page") && split[0].length() > 4 + && MaterialUtil.isEditableBook(stack.getType()) + && hasMetaPermission(sender, "page", false, true, ess)) { + final int page = NumberUtil.isInt(split[0].substring(4)) ? (Integer.parseInt(split[0].substring(4)) - 1) + : 0; + if (page > 100) { + throw new TranslatableException("pageLimitExceeded"); + } final BookMeta meta = (BookMeta) stack.getItemMeta(); final List pages = meta.hasPages() ? new ArrayList<>(meta.getPages()) : new ArrayList<>(); final List lines = new ArrayList<>(); diff --git a/Essentials/src/main/resources/messages.properties b/Essentials/src/main/resources/messages.properties index 2eb9381f265..50e945d886f 100644 --- a/Essentials/src/main/resources/messages.properties +++ b/Essentials/src/main/resources/messages.properties @@ -909,6 +909,7 @@ openingDisposal=Opening disposal menu... orderBalances=Ordering balances of {0} users, please wait... oversizedMute=You may not mute a player for this period of time. oversizedTempban=You may not ban a player for this period of time. +pageLimitExceeded=Page number exceeds the maximum limit (100). passengerTeleportFail=You cannot be teleported while carrying passengers. payCommandDescription=Pays another player from your balance. payCommandUsage=/ diff --git a/Essentials/src/main/resources/messages_en.properties b/Essentials/src/main/resources/messages_en.properties index 7aa38dabc01..0689d5b2948 100644 --- a/Essentials/src/main/resources/messages_en.properties +++ b/Essentials/src/main/resources/messages_en.properties @@ -904,6 +904,7 @@ openingDisposal=Opening disposal menu... orderBalances=Ordering balances of {0} users, please wait... oversizedMute=You may not mute a player for this period of time. oversizedTempban=You may not ban a player for this period of time. +pageLimitExceeded=Page number exceeds the maximum limit (100). passengerTeleportFail=You cannot be teleported while carrying passengers. payCommandDescription=Pays another player from your balance. payCommandUsage=/ From 9c833336fbc01a595f7dd2cba25c2bf8e665113b Mon Sep 17 00:00:00 2001 From: UDPSendToFailed Date: Sat, 7 Mar 2026 21:09:13 +0100 Subject: [PATCH 12/37] Fix integer overflow with /burn + /more & fix % in chat formats (#6452) Before: - Using `/burn 107374183` extinguished the player. - `/burn` accepted negative values. - Using `/more 2147483647` deleted the held item(s) entirely. After: - Using `/burn 107374183` sets the player on fire for 107374183 seconds correctly. - `/burn` doesn't accept negative values. - Using `/more 2147483647` correctly increases the held item stack to 64. --- .../essentials/commands/Commandburn.java | 8 ++++++-- .../essentials/commands/Commandmore.java | 12 ++++++----- .../chat/processing/AbstractChatHandler.java | 20 +++++++++---------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandburn.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandburn.java index e3f90303189..ba088eb8cec 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandburn.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandburn.java @@ -19,8 +19,12 @@ protected void run(final Server server, final CommandSource sender, final String } final User user = getPlayer(server, sender, args, 0); - user.getBase().setFireTicks(Integer.parseInt(args[1]) * 20); - sender.sendTl("burnMsg", user.getDisplayName(), Integer.parseInt(args[1])); + final int seconds = Integer.parseInt(args[1]); + + final int fireTicks = (int) Math.max(0L, Math.min((long) seconds * 20, Integer.MAX_VALUE)); + + user.getBase().setFireTicks(fireTicks); + sender.sendTl("burnMsg", user.getDisplayName(), Math.max(0, seconds)); } @Override diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmore.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmore.java index ef10cfc5cca..4e544d9c56f 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmore.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmore.java @@ -36,11 +36,13 @@ public void run(final Server server, final User user, final String commandLabel, if (!NumberUtil.isPositiveInt(args[0])) { throw new TranslatableException("nonZeroPosNumber"); } - newStackSize += Integer.parseInt(args[0]); - - if (newStackSize > (canOversized ? ess.getSettings().getOversizedStackSize() : stack.getMaxStackSize())) { - user.sendTl(canOversized ? "fullStackDefaultOversize" : "fullStackDefault", canOversized ? ess.getSettings().getOversizedStackSize() : stack.getMaxStackSize()); - newStackSize = canOversized ? ess.getSettings().getOversizedStackSize() : stack.getMaxStackSize(); + final int cap = canOversized ? ess.getSettings().getOversizedStackSize() : stack.getMaxStackSize(); + final long newSizeLong = (long) stack.getAmount() + Integer.parseInt(args[0]); + if (newSizeLong > cap) { + user.sendTl(canOversized ? "fullStackDefaultOversize" : "fullStackDefault", cap); + newStackSize = cap; + } else { + newStackSize = (int) newSizeLong; } } else if (canOversized) { newStackSize = ess.getSettings().getOversizedStackSize(); diff --git a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AbstractChatHandler.java b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AbstractChatHandler.java index 1b28e1e6561..4189a5b374d 100644 --- a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AbstractChatHandler.java +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AbstractChatHandler.java @@ -92,16 +92,16 @@ protected void handleChatFormat(AbstractChatEvent event) { final ChatType chatType = chat.getType(); String format = ess.getSettings().getChatFormat(group, chat.getRadius() > 0 && chatType == ChatType.UNKNOWN ? ChatType.LOCAL : chatType); - format = format.replace("{0}", group); - format = format.replace("{1}", ess.getSettings().getWorldAlias(world)); - format = format.replace("{2}", world.substring(0, 1).toUpperCase(Locale.ENGLISH)); - format = format.replace("{3}", team == null ? "" : team.getPrefix()); - format = format.replace("{4}", team == null ? "" : team.getSuffix()); - format = format.replace("{5}", team == null ? "" : team.getDisplayName()); - format = format.replace("{6}", prefix); - format = format.replace("{7}", suffix); - format = format.replace("{8}", username); - format = format.replace("{9}", nickname == null ? username : nickname); + format = format.replace("{0}", group.replace("%", "%%")); + format = format.replace("{1}", ess.getSettings().getWorldAlias(world).replace("%", "%%")); + format = format.replace("{2}", world.substring(0, 1).toUpperCase(Locale.ENGLISH).replace("%", "%%")); + format = format.replace("{3}", team == null ? "" : team.getPrefix().replace("%", "%%")); + format = format.replace("{4}", team == null ? "" : team.getSuffix().replace("%", "%%")); + format = format.replace("{5}", team == null ? "" : team.getDisplayName().replace("%", "%%")); + format = format.replace("{6}", prefix.replace("%", "%%")); + format = format.replace("{7}", suffix.replace("%", "%%")); + format = format.replace("{8}", username.replace("%", "%%")); + format = format.replace("{9}", (nickname == null ? username : nickname).replace("%", "%%")); // Local, shout and question chat types are only enabled when there's a valid radius if (chat.getRadius() > 0 && !event.getMessage().isEmpty()) { From 297d0e460605efec9db2ca824ad20945838a1ff3 Mon Sep 17 00:00:00 2001 From: MD <1917406+mdcfe@users.noreply.github.com> Date: Sun, 8 Mar 2026 21:44:39 +0000 Subject: [PATCH 13/37] Migrate Vault and emoji-java dependencies away from Jitpack (#6458) Jitpack is down. Move dependencies to our own repo to remove the build-time dependency on Jitpack. --- Essentials/build.gradle | 2 +- EssentialsDiscord/build.gradle | 2 +- settings.gradle.kts | 11 ++--------- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/Essentials/build.gradle b/Essentials/build.gradle index 088ae5f5505..5134e7daa02 100644 --- a/Essentials/build.gradle +++ b/Essentials/build.gradle @@ -3,7 +3,7 @@ plugins { } dependencies { - compileOnly('com.github.milkbowl:VaultAPI:1.7') { + compileOnly('net.essentialsx.deps:VaultAPI:1.7') { exclude group: "org.bukkit", module: "bukkit" } compileOnly 'net.luckperms:api:5.3' diff --git a/EssentialsDiscord/build.gradle b/EssentialsDiscord/build.gradle index fcc70014579..1f4eecfb794 100644 --- a/EssentialsDiscord/build.gradle +++ b/EssentialsDiscord/build.gradle @@ -7,7 +7,7 @@ dependencies { implementation('net.dv8tion:JDA:6.0.0') { exclude(module: 'opus-java') } - implementation 'com.github.MinnDevelopment:emoji-java:v6.1.0' + implementation 'net.essentialsx.deps:emoji-java:v6.1.0' implementation('club.minnced:discord-webhooks:0.8.4') { exclude(module: 'okhttp') } diff --git a/settings.gradle.kts b/settings.gradle.kts index 14f8b050a71..8c7168356ae 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,11 +1,5 @@ dependencyResolutionManagement { repositories { - maven("https://maven-prs.papermc.io/Paper/pr13194") { - name = "Maven for PR #13194" // https://github.com/PaperMC/Paper/pull/13194 - mavenContent { - includeModule("io.papermc.paper", "paper-api") - } - } maven("https://repo.papermc.io/repository/maven-public/") maven("https://hub.spigotmc.org/nexus/content/groups/public/") { content { @@ -13,10 +7,9 @@ dependencyResolutionManagement { includeGroup("net.md_5") } } - maven("https://jitpack.io") { + maven("https://repo.essentialsx.net/releases") { content { - includeGroup("com.github.milkbowl") - includeGroup("com.github.MinnDevelopment") + includeGroup("net.essentialsx.deps") } } maven("https://repo.codemc.org/repository/maven-public") { From 9d70fdb263d93a64577b8c33800bb0a373cd8efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Dufrene?= <43671109+itsme-to@users.noreply.github.com> Date: Mon, 9 Mar 2026 16:03:43 +0100 Subject: [PATCH 14/37] Add MiniMessage support for thunder enabled / disabled (#6454) ### Information This PR fixes missing MiniMessage support for the enabled / disabled thunder message ### Details **Proposed fix:** Added parsing **Environments tested:** OS: Linux Java version: 21 - [x] 1.21.8 Paper version - [ ] CraftBukkit/Spigot/Paper 1.12.2 - [ ] CraftBukkit 1.8.8 **Demonstration:** --- .../com/earth2me/essentials/commands/Commandthunder.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandthunder.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandthunder.java index ce678d0540a..09a93328d11 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandthunder.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandthunder.java @@ -1,6 +1,7 @@ package com.earth2me.essentials.commands; import com.earth2me.essentials.User; +import com.earth2me.essentials.utils.CommonPlaceholders; import com.google.common.collect.Lists; import org.bukkit.Server; import org.bukkit.World; @@ -21,15 +22,16 @@ public void run(final Server server, final User user, final String commandLabel, final World world = user.getWorld(); final boolean setThunder = args[0].equalsIgnoreCase("true"); + if (args.length == 1) { world.setThundering(setThunder); - user.sendTl("thunder", setThunder ? user.playerTl("enabled") : user.playerTl("disabled")); + user.sendTl("thunder", CommonPlaceholders.enableDisable(user.getSource(), setThunder)); return; } world.setThundering(setThunder); world.setThunderDuration(Integer.parseInt(args[1]) * 20); - user.sendTl("thunderDuration", setThunder ? user.playerTl("enabled") : user.playerTl("disabled"), Integer.parseInt(args[1])); + user.sendTl("thunderDuration", CommonPlaceholders.enableDisable(user.getSource(), setThunder), Integer.parseInt(args[1])); } @Override From 302653a77cc8bf56dcdf61fe7273e0bb7f54bbf9 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 00:33:35 +0000 Subject: [PATCH 15/37] Fix workflow artifact upload paths missing "X" in EssentialsX addon jar names (#6462) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Information Workflow artifact upload steps referenced incorrect jar filenames — all addon jars are built as `EssentialsX{Addon}-*.jar` (e.g. `EssentialsXChat`, `EssentialsXProtect`) because project names in `settings.gradle.kts` are all prefixed with `EssentialsX`, and `archivesName` is set to `project.name`. The upload globs were missing the `X`, causing every per-module direct upload to fail to find its target file. ### Details **Proposed fix:** Update all per-module `archive: false` upload step paths in both `build-pr.yml` and `build-master.yml` to use the correct `EssentialsX`-prefixed names: | Before | After | |---|---| | `jars/EssentialsAntiBuild-*.jar` | `jars/EssentialsXAntiBuild-*.jar` | | `jars/EssentialsChat-*.jar` | `jars/EssentialsXChat-*.jar` | | `jars/EssentialsDiscord-*.jar` | `jars/EssentialsXDiscord-*.jar` | | `jars/EssentialsDiscordLink-*.jar` | `jars/EssentialsXDiscordLink-*.jar` | | `jars/EssentialsGeoIP-*.jar` | `jars/EssentialsXGeoIP-*.jar` | | `jars/EssentialsProtect-*.jar` | `jars/EssentialsXProtect-*.jar` | | `jars/EssentialsSpawn-*.jar` | `jars/EssentialsXSpawn-*.jar` | | `jars/EssentialsXMPP-*.jar` | `jars/EssentialsXXMPP-*.jar` | The Javadoc `cp` paths in `build-master.yml` reference filesystem directory names (which don't include `X`) and remain correct as-is. **Environments tested:** OS: N/A (CI workflow change only) Java version: N/A - ~~Most recent Paper version (1.XX.Y, git-Paper-BUILD)~~ - ~~CraftBukkit/Spigot/Paper 1.12.2~~ - ~~CraftBukkit 1.8.8~~ **Demonstration:** N/A — the incorrect globs would silently produce no artifact (or an error) for each addon module on every workflow run using `archive: false`.

Original prompt > https://github.blog/changelog/2026-02-26-github-actions-now-supports-uploading-and-downloading-non-zipped-artifacts/ > > update our workflows to use this rather than zipping (still provide the zip but also individually upload
--- ✨ Let Copilot coding agent [set things up for you](https://github.com/EssentialsX/Essentials/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com> --- .github/workflows/build-master.yml | 60 ++++++++++++++++++++++++++++-- .github/workflows/build-pr.yml | 56 +++++++++++++++++++++++++++- 2 files changed, 112 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-master.yml b/.github/workflows/build-master.yml index dff69a15f53..4abe80ed260 100644 --- a/.github/workflows/build-master.yml +++ b/.github/workflows/build-master.yml @@ -41,11 +41,65 @@ jobs: report_paths: '**/build/test-results/test*/TEST-*.xml' - name: Archive plugin jars on GitHub - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: EssentialsX plugin jars path: jars/ + - name: Upload EssentialsX jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsX-*.jar + archive: false + + - name: Upload EssentialsXAntiBuild jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXAntiBuild-*.jar + archive: false + + - name: Upload EssentialsXChat jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXChat-*.jar + archive: false + + - name: Upload EssentialsXDiscord jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXDiscord-*.jar + archive: false + + - name: Upload EssentialsXDiscordLink jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXDiscordLink-*.jar + archive: false + + - name: Upload EssentialsXGeoIP jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXGeoIP-*.jar + archive: false + + - name: Upload EssentialsXProtect jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXProtect-*.jar + archive: false + + - name: Upload EssentialsXSpawn jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXSpawn-*.jar + archive: false + + - name: Upload EssentialsXXMPP jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXXMPP-*.jar + archive: false + - name: Deploy to Maven repo if: ${{ success() && github.event_name == 'push' && github.repository == 'EssentialsX/Essentials' && github.ref == 'refs/heads/2.x' }} env: @@ -67,7 +121,7 @@ jobs: cp -r EssentialsXMPP/build/docs/javadoc/ javadocs/EssentialsXMPP/ - name: Archive Javadocs - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: javadocs path: javadocs/ @@ -85,7 +139,7 @@ jobs: node-version: 22 - name: Download Javadocs - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: javadocs path: javadocs/ diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index a5508d14400..7a91e356dc9 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -48,7 +48,61 @@ jobs: report_paths: '**/build/test-results/test*/TEST-*.xml' - name: Archive plugin jars on GitHub - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: EssentialsX plugin jars path: jars/ + + - name: Upload EssentialsX jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsX-*.jar + archive: false + + - name: Upload EssentialsXAntiBuild jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXAntiBuild-*.jar + archive: false + + - name: Upload EssentialsXChat jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXChat-*.jar + archive: false + + - name: Upload EssentialsXDiscord jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXDiscord-*.jar + archive: false + + - name: Upload EssentialsXDiscordLink jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXDiscordLink-*.jar + archive: false + + - name: Upload EssentialsXGeoIP jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXGeoIP-*.jar + archive: false + + - name: Upload EssentialsXProtect jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXProtect-*.jar + archive: false + + - name: Upload EssentialsXSpawn jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXSpawn-*.jar + archive: false + + - name: Upload EssentialsXXMPP jar + uses: actions/upload-artifact@v7 + with: + path: jars/EssentialsXXMPP-*.jar + archive: false From 482175e6c288afcdc17732d7a757e6e0d4311110 Mon Sep 17 00:00:00 2001 From: Golfing7 <51512085+Golfing7@users.noreply.github.com> Date: Sat, 14 Mar 2026 11:23:54 -0400 Subject: [PATCH 16/37] Prevent running recipe command while sleeping (#6455) ### Information This PR fixes #6450 . ### Details **Proposed fix:** I added a check to make sure that items are only placed into the inventory when it successfully opens. **Environments tested:** OS: Linux Mint Java version: 21 - [x] Most recent Paper version (1.XX.Y, git-Paper-BUILD) - [ ] CraftBukkit/Spigot/Paper 1.12.2 - [ ] CraftBukkit 1.8.8 **Demonstration:** In order to replicate the issue, I used the following code to test: ```java @EventHandler public void onRecipeStop(InventoryOpenEvent event) { if (event.getPlayer().isSleeping()) { event.setCancelled(true); } } ``` --------- Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com> Co-authored-by: Benedikt Johannes --- .../essentials/commands/Commandrecipe.java | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrecipe.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrecipe.java index e63222ab9bb..9a0a0f0ca87 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrecipe.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandrecipe.java @@ -13,8 +13,10 @@ import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.Server; +import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.FurnaceRecipe; import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.Recipe; import org.bukkit.inventory.RecipeChoice; @@ -158,7 +160,13 @@ public void shapedRecipe(final CommandSource sender, final ShapedRecipe recipe, final User user = ess.getUser(sender.getPlayer()); user.getBase().closeInventory(); user.setRecipeSee(true); - final InventoryView view = user.getBase().openWorkbench(null, true); + final InventoryView view = openWorkbench(user); + if (view == null) { + user.setRecipeSee(false); + return; + } + final Inventory topInventory = ess.provider(InventoryViewProvider.class).getTopInventory(view); + final String[] recipeShape = recipe.getShape(); final Map ingredientMap = recipe.getIngredientMap(); for (int j = 0; j < recipeShape.length; j++) { @@ -170,7 +178,7 @@ public void shapedRecipe(final CommandSource sender, final ShapedRecipe recipe, if (VersionUtil.PRE_FLATTENING && item.getDurability() == Short.MAX_VALUE) { item.setDurability((short) 0); } - ess.provider(InventoryViewProvider.class).getTopInventory(view).setItem(j * 3 + k + 1, item); + topInventory.setItem(j * 3 + k + 1, item); } } } else { @@ -217,7 +225,11 @@ public void shapelessRecipe(final CommandSource sender, final ShapelessRecipe re final User user = ess.getUser(sender.getPlayer()); user.getBase().closeInventory(); user.setRecipeSee(true); - final InventoryView view = user.getBase().openWorkbench(null, true); + final InventoryView view = openWorkbench(user); + if (view == null) { + user.setRecipeSee(false); + return; + } for (int i = 0; i < ingredients.size(); i++) { final ItemStack item = ingredients.get(i); if (VersionUtil.PRE_FLATTENING && item.getDurability() == Short.MAX_VALUE) { @@ -239,6 +251,17 @@ public void shapelessRecipe(final CommandSource sender, final ShapelessRecipe re } } + private InventoryView openWorkbench(final User user) { + final InventoryView view = user.getBase().openWorkbench(null, true); + // If InventoryOpenEvent is canceled, the items can end up in the player's own crafting grid + // which allows players to extract counterfeit items. + if (view == null) + return null; + + final Inventory inventory = ess.provider(InventoryViewProvider.class).getTopInventory(view); + return inventory.getType() == InventoryType.WORKBENCH ? view : null; + } + public String getMaterialName(final CommandSource sender, final ItemStack stack) { if (stack == null) { return sender.tl("recipeNothing"); From d7452bfcc7af414930849827c736eb0f20e2f083 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 15:49:56 +0000 Subject: [PATCH 17/37] Fix stale shadow include group ID for emoji-java in EssentialsDiscord (#6467) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 297d0e4 migrated `emoji-java` from Jitpack (`com.github.MinnDevelopment`) to EssentialsX's own Maven repo (`net.essentialsx.deps`), but the `include(dependency(...))` in the `shadowJar` block was not updated to match, leaving the shadow JAR unable to locate and bundle the dependency. ### Details **Proposed fix:** - Updated `EssentialsDiscord/build.gradle` shadowJar include to use the correct group ID: ```groovy // Before include(dependency('com.github.MinnDevelopment:emoji-java')) // After include(dependency('net.essentialsx.deps:emoji-java')) ``` **Environments tested:** OS: Java version: - [ ] Most recent Paper version (1.XX.Y, git-Paper-BUILD) - [ ] CraftBukkit/Spigot/Paper 1.12.2 - [ ] CraftBukkit 1.8.8 **Demonstration:**
Original prompt > never updated the shadow include group id here pls fix > > Reference: https://github.com/EssentialsX/Essentials/commit/297d0e460605efec9db2ca824ad20945838a1ff3
--- 📱 Kick off Copilot coding agent tasks wherever you are with [GitHub Mobile](https://gh.io/cca-mobile-docs), available on iOS and Android. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com> --- EssentialsDiscord/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EssentialsDiscord/build.gradle b/EssentialsDiscord/build.gradle index 1f4eecfb794..b35c633c7a1 100644 --- a/EssentialsDiscord/build.gradle +++ b/EssentialsDiscord/build.gradle @@ -32,7 +32,7 @@ shadowJar { include(dependency('org.jetbrains.kotlin:kotlin-stdlib')) // Emoji - include(dependency('com.github.MinnDevelopment:emoji-java')) + include(dependency('net.essentialsx.deps:emoji-java')) include(dependency('org.json:json')) // discord-webhooks From 8e3b24fdba7b98f96e18f8fefe8dd93759add3f5 Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Mon, 23 Mar 2026 11:07:48 -0700 Subject: [PATCH 18/37] Minecraft 26.1 (#6473) --- .../essentials/utils/VersionUtil.java | 59 +++++++++++++------ Essentials/src/main/resources/items.json | 8 ++- .../com/earth2me/essentials/UtilTest.java | 52 ++++++++++++++++ README.md | 2 +- 4 files changed, 100 insertions(+), 21 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java index 0caf7ea6659..47be345e824 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java @@ -46,8 +46,9 @@ public final class VersionUtil { public static final BukkitVersion v1_21_5_R01 = BukkitVersion.fromString("1.21.5-R0.1-SNAPSHOT"); public static final BukkitVersion v1_21_8_R01 = BukkitVersion.fromString("1.21.8-R0.1-SNAPSHOT"); public static final BukkitVersion v1_21_11_R01 = BukkitVersion.fromString("1.21.11-R0.1-SNAPSHOT"); + public static final BukkitVersion v26_1_R01 = BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"); - private static final Set supportedVersions = ImmutableSet.of(v1_8_8_R01, v1_9_4_R01, v1_10_2_R01, v1_11_2_R01, v1_12_2_R01, v1_13_2_R01, v1_14_4_R01, v1_15_2_R01, v1_16_5_R01, v1_17_1_R01, v1_18_2_R01, v1_19_4_R01, v1_20_6_R01, v1_21_11_R01); + private static final Set supportedVersions = ImmutableSet.of(v1_8_8_R01, v1_9_4_R01, v1_10_2_R01, v1_11_2_R01, v1_12_2_R01, v1_13_2_R01, v1_14_4_R01, v1_15_2_R01, v1_16_5_R01, v1_17_1_R01, v1_18_2_R01, v1_19_4_R01, v1_20_6_R01, v1_21_11_R01, v26_1_R01); public static final boolean PRE_FLATTENING = VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_13_0_R01); @@ -189,11 +190,12 @@ public static String getSupportStatusClass() { } public static final class BukkitVersion implements Comparable { - private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)\\.?([0-9]*)?(?:-pre(\\d))?(?:-rc(\\d+))?(?:-?R?([\\d.]+))?(?:-SNAPSHOT)?"); - private static final Pattern SNAPSHOT_PATTERN = Pattern.compile("^(\\d{2})w(\\d{2})([a-z])(?:-?R?([\\d.]+))?(?:-SNAPSHOT)?"); + private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)\\.?([0-9]*)?(?:-snapshot-(\\d+))?(?:-pre-?(\\d+))?(?:-rc-?(\\d+))?(?:-?R?([\\d.]+))?(?:-SNAPSHOT)?"); + private static final Pattern LEGACY_SNAPSHOT_PATTERN = Pattern.compile("^(\\d{2})w(\\d{2})([a-z])(?:-?R?([\\d.]+))?(?:-SNAPSHOT)?"); private final int major; private final int minor; + private final int snapshotRelease; private final int preRelease; private final int releaseCandidate; private final int patch; @@ -204,11 +206,12 @@ public static final class BukkitVersion implements Comparable { private final int snapshotWeek; private final char snapshotLetter; - private BukkitVersion(final int major, final int minor, final int patch, final double revision, final int preRelease, final int releaseCandidate) { + private BukkitVersion(final int major, final int minor, final int patch, final double revision, final int snapshotRelease, final int preRelease, final int releaseCandidate) { this.major = major; this.minor = minor; this.patch = patch; this.revision = revision; + this.snapshotRelease = snapshotRelease; this.preRelease = preRelease; this.releaseCandidate = releaseCandidate; this.snapshot = false; @@ -217,12 +220,13 @@ private BukkitVersion(final int major, final int minor, final int patch, final d this.snapshotLetter = '\0'; } - private BukkitVersion(final int major, final int minor, final int patch, final double revision, final int preRelease, final int releaseCandidate, + private BukkitVersion(final int major, final int minor, final int patch, final double revision, final int snapshotRelease, final int preRelease, final int releaseCandidate, final boolean snapshot, final int snapshotYear, final int snapshotWeek, final char snapshotLetter) { this.major = major; this.minor = minor; this.patch = patch; this.revision = revision; + this.snapshotRelease = snapshotRelease; this.preRelease = preRelease; this.releaseCandidate = releaseCandidate; this.snapshot = snapshot; @@ -237,11 +241,11 @@ public static BukkitVersion fromString(final String string) { // Try standard release format first Matcher matcher = VERSION_PATTERN.matcher(string); if (matcher.matches()) { - return from(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(6), matcher.group(4), matcher.group(5)); + return from(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(7), matcher.group(4), matcher.group(5), matcher.group(6)); } // Try snapshot format (e.g., 25w32a-R0.1-SNAPSHOT) - final Matcher snapshotMatcher = SNAPSHOT_PATTERN.matcher(string); + final Matcher snapshotMatcher = LEGACY_SNAPSHOT_PATTERN.matcher(string); if (snapshotMatcher.matches()) { final int year = Integer.parseInt(snapshotMatcher.group(1)); final int week = Integer.parseInt(snapshotMatcher.group(2)); @@ -259,24 +263,26 @@ public static BukkitVersion fromString(final String string) { } matcher = VERSION_PATTERN.matcher(v1_16_1_R01.toString()); Preconditions.checkArgument(matcher.matches(), string + " is not in valid version format. e.g. 1.8.8-R0.1"); - return from(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(6), matcher.group(4), matcher.group(5)); + return from(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(7), matcher.group(4), matcher.group(5), matcher.group(6)); } - private static BukkitVersion from(final String major, final String minor, String patch, String revision, String preRelease, String releaseCandidate) { + private static BukkitVersion from(final String major, final String minor, String patch, String revision, String snapshotRelease, String preRelease, String releaseCandidate) { if (patch == null || patch.isEmpty()) patch = "0"; if (revision == null || revision.isEmpty()) revision = "0"; + if (snapshotRelease == null || snapshotRelease.isEmpty()) snapshotRelease = "-1"; if (preRelease == null || preRelease.isEmpty()) preRelease = "-1"; if (releaseCandidate == null || releaseCandidate.isEmpty()) releaseCandidate = "-1"; return new BukkitVersion(Integer.parseInt(major), Integer.parseInt(minor), Integer.parseInt(patch), Double.parseDouble(revision), + Integer.parseInt(snapshotRelease), Integer.parseInt(preRelease), Integer.parseInt(releaseCandidate)); } private static BukkitVersion fromSnapshot(final int year, final int week, final char letter, final double revision) { - return new BukkitVersion(-1, -1, -1, revision, -1, -1, true, year, week, letter); + return new BukkitVersion(-1, -1, -1, revision, -1, -1, -1, true, year, week, letter); } public boolean isHigherThan(final BukkitVersion o) { @@ -319,6 +325,10 @@ public int getReleaseCandidate() { return releaseCandidate; } + public int getSnapshotRelease() { + return snapshotRelease; + } + public boolean isSnapshot() { return snapshot; } @@ -343,7 +353,9 @@ public boolean equals(final Object o) { minor == that.minor && patch == that.patch && revision == that.revision && - preRelease == that.preRelease; + snapshotRelease == that.snapshotRelease && + preRelease == that.preRelease && + releaseCandidate == that.releaseCandidate; } @Override @@ -351,7 +363,7 @@ public int hashCode() { if (snapshot) { return Objects.hashCode("snapshot", snapshotYear, snapshotWeek, snapshotLetter, revision); } - return Objects.hashCode(major, minor, patch, revision, preRelease, releaseCandidate); + return Objects.hashCode(major, minor, patch, revision, snapshotRelease, preRelease, releaseCandidate); } @Override @@ -363,6 +375,9 @@ public String toString() { if (patch != 0) { sb.append(".").append(patch); } + if (snapshotRelease != -1) { + sb.append("-snapshot-").append(snapshotRelease); + } if (preRelease != -1) { sb.append("-pre").append(preRelease); } @@ -407,17 +422,23 @@ public int compareTo(final BukkitVersion o) { } else if (patch > o.patch) { return 1; } else { // equal patch - if (preRelease < o.preRelease) { + if (snapshotRelease < o.snapshotRelease) { return -1; - } else if (preRelease > o.preRelease) { + } else if (snapshotRelease > o.snapshotRelease) { return 1; - } else { // equal prerelease - if (releaseCandidate < o.releaseCandidate) { + } else { // equal snapshot release + if (preRelease < o.preRelease) { return -1; - } else if (releaseCandidate > o.releaseCandidate) { + } else if (preRelease > o.preRelease) { return 1; - } else { // equal release candidate - return Double.compare(revision, o.revision); + } else { // equal prerelease + if (releaseCandidate < o.releaseCandidate) { + return -1; + } else if (releaseCandidate > o.releaseCandidate) { + return 1; + } else { // equal release candidate + return Double.compare(revision, o.revision); + } } } } diff --git a/Essentials/src/main/resources/items.json b/Essentials/src/main/resources/items.json index c6a31fd7597..cf725a74331 100644 --- a/Essentials/src/main/resources/items.json +++ b/Essentials/src/main/resources/items.json @@ -8620,6 +8620,12 @@ "gshirt": "golden_chestplate", "gtunic": "golden_chestplate", "minecraft:golden_chestplate": "golden_chestplate", + "golden_dandelion": { + "name": "golden_dandelion", + "material": "GOLDEN_DANDELION" + }, + "goldendandelion": "golden_dandelion", + "minecraft:golden_dandelion": "golden_dandelion", "golden_helmet": { "material": "GOLDEN_HELMET" }, @@ -50980,4 +50986,4 @@ "zpmanmonsterspawner": "zombified_piglin_spawner", "zpmanmspawner": "zombified_piglin_spawner", "zpmanspawner": "zombified_piglin_spawner" -} \ No newline at end of file +} diff --git a/Essentials/src/test/java/com/earth2me/essentials/UtilTest.java b/Essentials/src/test/java/com/earth2me/essentials/UtilTest.java index 62c64500380..336b02df5aa 100644 --- a/Essentials/src/test/java/com/earth2me/essentials/UtilTest.java +++ b/Essentials/src/test/java/com/earth2me/essentials/UtilTest.java @@ -249,5 +249,57 @@ public void testVer() { assertEquals(v.getRevision(), 0.1); assertEquals(v.getPrerelease(), -1); assertEquals(v.getReleaseCandidate(), 3); + // New versioning format (26.x) + v = VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"); + assertEquals(v.getMajor(), 26); + assertEquals(v.getMinor(), 1); + assertEquals(v.getPatch(), 0); + assertEquals(v.getRevision(), 0.1); + assertEquals(v.getSnapshotRelease(), -1); + assertEquals(v.getPrerelease(), -1); + assertEquals(v.getReleaseCandidate(), -1); + v = VersionUtil.BukkitVersion.fromString("26.1.1-R0.1-SNAPSHOT"); + assertEquals(v.getMajor(), 26); + assertEquals(v.getMinor(), 1); + assertEquals(v.getPatch(), 1); + assertEquals(v.getRevision(), 0.1); + assertEquals(v.getSnapshotRelease(), -1); + assertEquals(v.getPrerelease(), -1); + assertEquals(v.getReleaseCandidate(), -1); + v = VersionUtil.BukkitVersion.fromString("26.1-snapshot-11-R0.1-SNAPSHOT"); + assertEquals(v.getMajor(), 26); + assertEquals(v.getMinor(), 1); + assertEquals(v.getPatch(), 0); + assertEquals(v.getRevision(), 0.1); + assertEquals(v.getSnapshotRelease(), 11); + assertEquals(v.getPrerelease(), -1); + assertEquals(v.getReleaseCandidate(), -1); + v = VersionUtil.BukkitVersion.fromString("26.1-pre-3-R0.1-SNAPSHOT"); + assertEquals(v.getMajor(), 26); + assertEquals(v.getMinor(), 1); + assertEquals(v.getPatch(), 0); + assertEquals(v.getRevision(), 0.1); + assertEquals(v.getSnapshotRelease(), -1); + assertEquals(v.getPrerelease(), 3); + assertEquals(v.getReleaseCandidate(), -1); + v = VersionUtil.BukkitVersion.fromString("26.1-rc-2-R0.1-SNAPSHOT"); + assertEquals(v.getMajor(), 26); + assertEquals(v.getMinor(), 1); + assertEquals(v.getPatch(), 0); + assertEquals(v.getRevision(), 0.1); + assertEquals(v.getSnapshotRelease(), -1); + assertEquals(v.getPrerelease(), -1); + assertEquals(v.getReleaseCandidate(), 2); + // Old format versions are always lower than new format versions + assertTrue(VersionUtil.BukkitVersion.fromString("1.21.11-R0.1-SNAPSHOT") + .isLowerThan(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT") + .isHigherThan(VersionUtil.BukkitVersion.fromString("1.21.11-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("1.21.11-R0.1-SNAPSHOT") + .isLowerThan(VersionUtil.BukkitVersion.fromString("26.1-snapshot-1-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("1.21.11-R0.1-SNAPSHOT") + .isLowerThan(VersionUtil.BukkitVersion.fromString("26.1-pre-1-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("1.21.11-R0.1-SNAPSHOT") + .isLowerThan(VersionUtil.BukkitVersion.fromString("26.1-rc-1-R0.1-SNAPSHOT"))); } } diff --git a/README.md b/README.md index 1266ffbf005..37aff5f1822 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ however, have some new requirements: * **EssentialsX requires CraftBukkit, Spigot or Paper to run.** Other server software may work, but these are not tested by the team and we may not be able to help with any issues that occur. * **EssentialsX currently supports Minecraft versions 1.8.8, 1.9.4, 1.10.2, 1.11.2, 1.12.2, 1.13.2, 1.14.4, 1.15.2, - 1.16.5, 1.17.1, 1.18.2, 1.19.4, 1.20.6, and 1.21.11.** + 1.16.5, 1.17.1, 1.18.2, 1.19.4, 1.20.6, 1.21.11, and 26.1.** * **EssentialsX currently requires Java 8 or higher.** We recommend using the latest Java version supported by your server software. * **EssentialsX requires [Vault](http://dev.bukkit.org/bukkit-plugins/vault/) to enable using chat prefix/suffixes and From 5d535dcbe423ce711e9469b4c2d32f9268a9a3c5 Mon Sep 17 00:00:00 2001 From: Jason <51760372+JasonHorkles@users.noreply.github.com> Date: Fri, 3 Apr 2026 22:11:15 -0600 Subject: [PATCH 19/37] Support new PaperMC versioning scheme in VersionUtil (#6480) ThioJoe is waiting! Re #6479 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com> --- .../essentials/utils/VersionUtil.java | 92 ++++++++++-- .../com/earth2me/essentials/UtilTest.java | 140 ++++++++++++++++++ 2 files changed, 220 insertions(+), 12 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java index 47be345e824..55627219eec 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java @@ -176,7 +176,7 @@ public static SupportStatus getServerSupportStatus() { } } - if (!supportedVersions.contains(getServerBukkitVersion())) { + if (!isSupportedVersion(getServerBukkitVersion())) { return supportStatus = SupportStatus.OUTDATED; } @@ -189,8 +189,30 @@ public static String getSupportStatusClass() { return supportStatusClass; } + /** + * Checks if a version is considered supported, either by exact match in the + * supported versions set, or by matching the base version (major.minor.patch) + * of a supported version. This handles both the new PaperMC versioning scheme + * (which omits the Bukkit revision suffix) and development builds that report + * versions like {@code 26.1-rc-3.build.8-alpha}. + */ + private static boolean isSupportedVersion(final BukkitVersion version) { + if (supportedVersions.contains(version)) { + return true; + } + // Check if this version matches a supported base version. + // This covers Paper versions (no -R0.1-SNAPSHOT suffix) and + // dev variants (snapshot/pre-release/RC/Paper builds). + for (final BukkitVersion supported : supportedVersions) { + if (version.equalsBaseVersion(supported)) { + return true; + } + } + return false; + } + public static final class BukkitVersion implements Comparable { - private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)\\.?([0-9]*)?(?:-snapshot-(\\d+))?(?:-pre-?(\\d+))?(?:-rc-?(\\d+))?(?:-?R?([\\d.]+))?(?:-SNAPSHOT)?"); + private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)\\.?([0-9]*)?(?:-snapshot-(\\d+))?(?:-pre-?(\\d+))?(?:-rc-?(\\d+))?(?:\\.build\\.(\\d+)(?:-([a-z]+))?)?(?:-?R?([\\d.]+))?(?:-SNAPSHOT)?"); private static final Pattern LEGACY_SNAPSHOT_PATTERN = Pattern.compile("^(\\d{2})w(\\d{2})([a-z])(?:-?R?([\\d.]+))?(?:-SNAPSHOT)?"); private final int major; @@ -198,6 +220,8 @@ public static final class BukkitVersion implements Comparable { private final int snapshotRelease; private final int preRelease; private final int releaseCandidate; + private final int paperBuild; + private final String releaseChannel; private final int patch; private final double revision; @@ -206,7 +230,7 @@ public static final class BukkitVersion implements Comparable { private final int snapshotWeek; private final char snapshotLetter; - private BukkitVersion(final int major, final int minor, final int patch, final double revision, final int snapshotRelease, final int preRelease, final int releaseCandidate) { + private BukkitVersion(final int major, final int minor, final int patch, final double revision, final int snapshotRelease, final int preRelease, final int releaseCandidate, final int paperBuild, final String releaseChannel) { this.major = major; this.minor = minor; this.patch = patch; @@ -214,13 +238,15 @@ private BukkitVersion(final int major, final int minor, final int patch, final d this.snapshotRelease = snapshotRelease; this.preRelease = preRelease; this.releaseCandidate = releaseCandidate; + this.paperBuild = paperBuild; + this.releaseChannel = releaseChannel; this.snapshot = false; this.snapshotYear = -1; this.snapshotWeek = -1; this.snapshotLetter = '\0'; } - private BukkitVersion(final int major, final int minor, final int patch, final double revision, final int snapshotRelease, final int preRelease, final int releaseCandidate, + private BukkitVersion(final int major, final int minor, final int patch, final double revision, final int snapshotRelease, final int preRelease, final int releaseCandidate, final int paperBuild, final String releaseChannel, final boolean snapshot, final int snapshotYear, final int snapshotWeek, final char snapshotLetter) { this.major = major; this.minor = minor; @@ -229,6 +255,8 @@ private BukkitVersion(final int major, final int minor, final int patch, final d this.snapshotRelease = snapshotRelease; this.preRelease = preRelease; this.releaseCandidate = releaseCandidate; + this.paperBuild = paperBuild; + this.releaseChannel = releaseChannel; this.snapshot = snapshot; this.snapshotYear = snapshotYear; this.snapshotWeek = snapshotWeek; @@ -241,7 +269,7 @@ public static BukkitVersion fromString(final String string) { // Try standard release format first Matcher matcher = VERSION_PATTERN.matcher(string); if (matcher.matches()) { - return from(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(7), matcher.group(4), matcher.group(5), matcher.group(6)); + return from(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(9), matcher.group(4), matcher.group(5), matcher.group(6), matcher.group(7), matcher.group(8)); } // Try snapshot format (e.g., 25w32a-R0.1-SNAPSHOT) @@ -263,26 +291,29 @@ public static BukkitVersion fromString(final String string) { } matcher = VERSION_PATTERN.matcher(v1_16_1_R01.toString()); Preconditions.checkArgument(matcher.matches(), string + " is not in valid version format. e.g. 1.8.8-R0.1"); - return from(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(7), matcher.group(4), matcher.group(5), matcher.group(6)); + return from(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(9), matcher.group(4), matcher.group(5), matcher.group(6), matcher.group(7), matcher.group(8)); } - private static BukkitVersion from(final String major, final String minor, String patch, String revision, String snapshotRelease, String preRelease, String releaseCandidate) { + private static BukkitVersion from(final String major, final String minor, String patch, String revision, String snapshotRelease, String preRelease, String releaseCandidate, String paperBuild, final String releaseChannel) { if (patch == null || patch.isEmpty()) patch = "0"; if (revision == null || revision.isEmpty()) revision = "0"; if (snapshotRelease == null || snapshotRelease.isEmpty()) snapshotRelease = "-1"; if (preRelease == null || preRelease.isEmpty()) preRelease = "-1"; if (releaseCandidate == null || releaseCandidate.isEmpty()) releaseCandidate = "-1"; + if (paperBuild == null || paperBuild.isEmpty()) paperBuild = "-1"; return new BukkitVersion(Integer.parseInt(major), Integer.parseInt(minor), Integer.parseInt(patch), Double.parseDouble(revision), Integer.parseInt(snapshotRelease), Integer.parseInt(preRelease), - Integer.parseInt(releaseCandidate)); + Integer.parseInt(releaseCandidate), + Integer.parseInt(paperBuild), + releaseChannel); } private static BukkitVersion fromSnapshot(final int year, final int week, final char letter, final double revision) { - return new BukkitVersion(-1, -1, -1, revision, -1, -1, -1, true, year, week, letter); + return new BukkitVersion(-1, -1, -1, revision, -1, -1, -1, -1, null, true, year, week, letter); } public boolean isHigherThan(final BukkitVersion o) { @@ -329,10 +360,35 @@ public int getSnapshotRelease() { return snapshotRelease; } + public int getPaperBuild() { + return paperBuild; + } + + public String getReleaseChannel() { + return releaseChannel; + } + public boolean isSnapshot() { return snapshot; } + /** + * Checks if this version has the same base version (major, minor, and patch) + * as the given version, ignoring development specifiers (snapshot release, pre-release, + * release candidate, paper build numbers) and Bukkit metadata (revision). + * Revision is ignored because Paper versions do not include the Bukkit revision + * suffix (e.g. {@code -R0.1-SNAPSHOT}). + * This is used to match development builds against their target release version. + */ + public boolean equalsBaseVersion(final BukkitVersion other) { + if (this.snapshot || other.snapshot) { + return false; + } + return this.major == other.major && + this.minor == other.minor && + this.patch == other.patch; + } + @Override public boolean equals(final Object o) { if (this == o) { @@ -379,10 +435,16 @@ public String toString() { sb.append("-snapshot-").append(snapshotRelease); } if (preRelease != -1) { - sb.append("-pre").append(preRelease); + sb.append("-pre-").append(preRelease); } if (releaseCandidate != -1) { - sb.append("-rc").append(releaseCandidate); + sb.append("-rc-").append(releaseCandidate); + } + if (paperBuild != -1) { + sb.append(".build.").append(paperBuild); + if (releaseChannel != null) { + sb.append("-").append(releaseChannel); + } } return sb.append("-R").append(revision).toString(); } @@ -437,7 +499,13 @@ public int compareTo(final BukkitVersion o) { } else if (releaseCandidate > o.releaseCandidate) { return 1; } else { // equal release candidate - return Double.compare(revision, o.revision); + if (paperBuild < o.paperBuild) { + return -1; + } else if (paperBuild > o.paperBuild) { + return 1; + } else { // equal paper build + return Double.compare(revision, o.revision); + } } } } diff --git a/Essentials/src/test/java/com/earth2me/essentials/UtilTest.java b/Essentials/src/test/java/com/earth2me/essentials/UtilTest.java index 336b02df5aa..221de015505 100644 --- a/Essentials/src/test/java/com/earth2me/essentials/UtilTest.java +++ b/Essentials/src/test/java/com/earth2me/essentials/UtilTest.java @@ -15,6 +15,8 @@ import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class UtilTest { @@ -301,5 +303,143 @@ public void testVer() { .isLowerThan(VersionUtil.BukkitVersion.fromString("26.1-pre-1-R0.1-SNAPSHOT"))); assertTrue(VersionUtil.BukkitVersion.fromString("1.21.11-R0.1-SNAPSHOT") .isLowerThan(VersionUtil.BukkitVersion.fromString("26.1-rc-1-R0.1-SNAPSHOT"))); + // Dev variants are considered higher than their base version (for feature checks) + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-snapshot-1-R0.1-SNAPSHOT") + .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-pre-1-R0.1-SNAPSHOT") + .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-rc-1-R0.1-SNAPSHOT") + .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); + // Dev variants of 26.1 are lower than 26.2 + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-snapshot-99-R0.1-SNAPSHOT") + .isLowerThan(VersionUtil.BukkitVersion.fromString("26.2-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-pre-99-R0.1-SNAPSHOT") + .isLowerThan(VersionUtil.BukkitVersion.fromString("26.2-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-rc-99-R0.1-SNAPSHOT") + .isLowerThan(VersionUtil.BukkitVersion.fromString("26.2-R0.1-SNAPSHOT"))); + // equalsBaseVersion: dev variants match their base version + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-snapshot-11-R0.1-SNAPSHOT") + .equalsBaseVersion(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-pre-3-R0.1-SNAPSHOT") + .equalsBaseVersion(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-rc-2-R0.1-SNAPSHOT") + .equalsBaseVersion(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); + // equalsBaseVersion: different minor versions don't match + assertFalse(VersionUtil.BukkitVersion.fromString("26.2-snapshot-1-R0.1-SNAPSHOT") + .equalsBaseVersion(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); + // equalsBaseVersion: different patch versions don't match + assertFalse(VersionUtil.BukkitVersion.fromString("26.1.1-R0.1-SNAPSHOT") + .equalsBaseVersion(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); + // toString roundtrip with new PaperMC format + assertEquals("26.1-snapshot-11-R0.1", + VersionUtil.BukkitVersion.fromString("26.1-snapshot-11-R0.1-SNAPSHOT").toString()); + assertEquals("26.1-pre-3-R0.1", + VersionUtil.BukkitVersion.fromString("26.1-pre-3-R0.1-SNAPSHOT").toString()); + assertEquals("26.1-rc-2-R0.1", + VersionUtil.BukkitVersion.fromString("26.1-rc-2-R0.1-SNAPSHOT").toString()); + // Paper build metadata format (e.g. 26.1-rc-3.build.8-alpha) + // Paper versions do NOT include -R0.1-SNAPSHOT suffix + v = VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.8-alpha"); + assertEquals(v.getMajor(), 26); + assertEquals(v.getMinor(), 1); + assertEquals(v.getPatch(), 0); + assertEquals(v.getRevision(), 0.0); + assertEquals(v.getSnapshotRelease(), -1); + assertEquals(v.getPrerelease(), -1); + assertEquals(v.getReleaseCandidate(), 3); + assertEquals(v.getPaperBuild(), 8); + assertEquals(v.getReleaseChannel(), "alpha"); + v = VersionUtil.BukkitVersion.fromString("26.1-pre-3.build.5-alpha"); + assertEquals(v.getMajor(), 26); + assertEquals(v.getMinor(), 1); + assertEquals(v.getPatch(), 0); + assertEquals(v.getRevision(), 0.0); + assertEquals(v.getSnapshotRelease(), -1); + assertEquals(v.getPrerelease(), 3); + assertEquals(v.getReleaseCandidate(), -1); + assertEquals(v.getPaperBuild(), 5); + assertEquals(v.getReleaseChannel(), "alpha"); + v = VersionUtil.BukkitVersion.fromString("26.1-snapshot-11.build.3-alpha"); + assertEquals(v.getMajor(), 26); + assertEquals(v.getMinor(), 1); + assertEquals(v.getPatch(), 0); + assertEquals(v.getRevision(), 0.0); + assertEquals(v.getSnapshotRelease(), 11); + assertEquals(v.getPrerelease(), -1); + assertEquals(v.getReleaseCandidate(), -1); + assertEquals(v.getPaperBuild(), 3); + assertEquals(v.getReleaseChannel(), "alpha"); + // Paper build of a base release (no Mojang specifier) + v = VersionUtil.BukkitVersion.fromString("26.1.build.5-alpha"); + assertEquals(v.getMajor(), 26); + assertEquals(v.getMinor(), 1); + assertEquals(v.getPatch(), 0); + assertEquals(v.getRevision(), 0.0); + assertEquals(v.getSnapshotRelease(), -1); + assertEquals(v.getPrerelease(), -1); + assertEquals(v.getReleaseCandidate(), -1); + assertEquals(v.getPaperBuild(), 5); + assertEquals(v.getReleaseChannel(), "alpha"); + // Paper builds with same Mojang version: equalsBaseVersion matches + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.8-alpha") + .equalsBaseVersion(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1.build.5-alpha") + .equalsBaseVersion(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1") + .equalsBaseVersion(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); + // Paper build ordering: higher build number is higher + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.12-alpha") + .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.8-alpha"))); + // Paper build is higher than bare Mojang version (paperBuild -1 < 1) + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.1-alpha") + .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-rc-3-R0.1-SNAPSHOT"))); + // Paper versions: ordering still works against Bukkit versions + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.8-alpha") + .isHigherThan(VersionUtil.BukkitVersion.fromString("1.21.11-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("1.21.11-R0.1-SNAPSHOT") + .isLowerThan(VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.8-alpha"))); + // Release channel: different channels parsed correctly + v = VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.8-beta"); + assertEquals(v.getReleaseCandidate(), 3); + assertEquals(v.getPaperBuild(), 8); + assertEquals(v.getReleaseChannel(), "beta"); + v = VersionUtil.BukkitVersion.fromString("26.1.build.10-recommended"); + assertEquals(v.getMajor(), 26); + assertEquals(v.getMinor(), 1); + assertEquals(v.getPatch(), 0); + assertEquals(v.getPaperBuild(), 10); + assertEquals(v.getReleaseChannel(), "recommended"); + // Release channel: Bukkit/Mojang versions have no release channel + assertNull(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT").getReleaseChannel()); + assertNull(VersionUtil.BukkitVersion.fromString("26.1-rc-3-R0.1-SNAPSHOT").getReleaseChannel()); + assertNull(VersionUtil.BukkitVersion.fromString("1.21.11-R0.1-SNAPSHOT").getReleaseChannel()); + // Release channel: Paper build without channel suffix + v = VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.8"); + assertEquals(v.getPaperBuild(), 8); + assertNull(v.getReleaseChannel()); + // toString roundtrip preserves release channel + assertEquals("26.1-rc-3.build.8-alpha-R0.0", + VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.8-alpha").toString()); + assertEquals("26.1.build.5-recommended-R0.0", + VersionUtil.BukkitVersion.fromString("26.1.build.5-recommended").toString()); + assertEquals("26.1-pre-1.build.3-beta-R0.0", + VersionUtil.BukkitVersion.fromString("26.1-pre-1.build.3-beta").toString()); + assertEquals("26.1-snapshot-11.build.3-alpha-R0.0", + VersionUtil.BukkitVersion.fromString("26.1-snapshot-11.build.3-alpha").toString()); + // toString: paper build without channel omits channel suffix + assertEquals("26.1-rc-3.build.8-R0.0", + VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.8").toString()); + // Release channel does not affect equalsBaseVersion + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.8-alpha") + .equalsBaseVersion(VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.8-beta"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1.build.5-recommended") + .equalsBaseVersion(VersionUtil.BukkitVersion.fromString("26.1.build.5-alpha"))); + // Paper build metadata does not affect equality + assertEquals(VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.8-alpha"), + VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.12-beta")); + assertEquals(VersionUtil.BukkitVersion.fromString("26.1.build.5-alpha"), + VersionUtil.BukkitVersion.fromString("26.1.build.99-recommended")); + assertEquals(VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.8-alpha"), + VersionUtil.BukkitVersion.fromString("26.1-rc-3-R0.0")); } } From c26909e2b802c965eb3a2c38f114f24c5f0ef6dd Mon Sep 17 00:00:00 2001 From: Jason <51760372+JasonHorkles@users.noreply.github.com> Date: Fri, 3 Apr 2026 22:48:36 -0600 Subject: [PATCH 20/37] Add alias "ver" for the "version" subcommand (#6481) Fixin what u broke :) --- .../earth2me/essentials/commands/essentials/VersionCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java index 82796f9802f..7be23713a5f 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java @@ -37,7 +37,7 @@ public class VersionCommand extends EssentialsTreeNode { ); public VersionCommand() { - super("version"); + super("version", "ver"); } @Override From 218e004108518afbd39f9c80ca56076942ac0b62 Mon Sep 17 00:00:00 2001 From: Jason <51760372+JasonHorkles@users.noreply.github.com> Date: Fri, 3 Apr 2026 23:28:07 -0600 Subject: [PATCH 21/37] Bump supported versions to 26.1.1 (#6482) Yay! --- .../main/java/com/earth2me/essentials/utils/VersionUtil.java | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java index 55627219eec..83537998ca0 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java @@ -46,7 +46,7 @@ public final class VersionUtil { public static final BukkitVersion v1_21_5_R01 = BukkitVersion.fromString("1.21.5-R0.1-SNAPSHOT"); public static final BukkitVersion v1_21_8_R01 = BukkitVersion.fromString("1.21.8-R0.1-SNAPSHOT"); public static final BukkitVersion v1_21_11_R01 = BukkitVersion.fromString("1.21.11-R0.1-SNAPSHOT"); - public static final BukkitVersion v26_1_R01 = BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"); + public static final BukkitVersion v26_1_R01 = BukkitVersion.fromString("26.1.1-R0.1-SNAPSHOT"); private static final Set supportedVersions = ImmutableSet.of(v1_8_8_R01, v1_9_4_R01, v1_10_2_R01, v1_11_2_R01, v1_12_2_R01, v1_13_2_R01, v1_14_4_R01, v1_15_2_R01, v1_16_5_R01, v1_17_1_R01, v1_18_2_R01, v1_19_4_R01, v1_20_6_R01, v1_21_11_R01, v26_1_R01); diff --git a/README.md b/README.md index 37aff5f1822..ace9c1c78c2 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ however, have some new requirements: * **EssentialsX requires CraftBukkit, Spigot or Paper to run.** Other server software may work, but these are not tested by the team and we may not be able to help with any issues that occur. * **EssentialsX currently supports Minecraft versions 1.8.8, 1.9.4, 1.10.2, 1.11.2, 1.12.2, 1.13.2, 1.14.4, 1.15.2, - 1.16.5, 1.17.1, 1.18.2, 1.19.4, 1.20.6, 1.21.11, and 26.1.** + 1.16.5, 1.17.1, 1.18.2, 1.19.4, 1.20.6, 1.21.11, and 26.1.1.** * **EssentialsX currently requires Java 8 or higher.** We recommend using the latest Java version supported by your server software. * **EssentialsX requires [Vault](http://dev.bukkit.org/bukkit-plugins/vault/) to enable using chat prefix/suffixes and From 637ac8f4ee0d391b853559d49c85715c7744c622 Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Sat, 4 Apr 2026 11:58:55 -0700 Subject: [PATCH 22/37] Fix version comparison logic for pre-release/snapshot/rc variants (#6483) Pre-release, snapshot, and RC variants should be considered lower than the base release version. Updated test assertions and comparison logic to reflect that release versions are higher than their dev variants (snapshot < pre < rc < release). --- .../essentials/utils/VersionUtil.java | 49 +++++++++---------- .../com/earth2me/essentials/UtilTest.java | 28 ++++++++--- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java index 83537998ca0..17449d91c54 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java @@ -374,11 +374,9 @@ public boolean isSnapshot() { /** * Checks if this version has the same base version (major, minor, and patch) - * as the given version, ignoring development specifiers (snapshot release, pre-release, - * release candidate, paper build numbers) and Bukkit metadata (revision). - * Revision is ignored because Paper versions do not include the Bukkit revision - * suffix (e.g. {@code -R0.1-SNAPSHOT}). - * This is used to match development builds against their target release version. + * as the given version, ignoring development specifiers and Bukkit metadata. + * Revision is ignored because Paper versions do not include the Bukkit + * revision suffix (e.g. {@code -R0.1-SNAPSHOT}). */ public boolean equalsBaseVersion(final BukkitVersion other) { if (this.snapshot || other.snapshot) { @@ -449,6 +447,13 @@ public String toString() { return sb.append("-R").append(revision).toString(); } + private static int compareDevSpecifier(final int a, final int b) { + if (a == b) return 0; + if (a == -1) return 1; + if (b == -1) return -1; + return Integer.compare(a, b); + } + @Override public int compareTo(final BukkitVersion o) { // Snapshots are always considered the most recent @@ -484,30 +489,20 @@ public int compareTo(final BukkitVersion o) { } else if (patch > o.patch) { return 1; } else { // equal patch - if (snapshotRelease < o.snapshotRelease) { + // For dev specifiers, -1 means absent (final release) and + // sorts HIGHER than any present value: snapshot < pre < rc < release + int cmp = compareDevSpecifier(snapshotRelease, o.snapshotRelease); + if (cmp != 0) return cmp; + cmp = compareDevSpecifier(preRelease, o.preRelease); + if (cmp != 0) return cmp; + cmp = compareDevSpecifier(releaseCandidate, o.releaseCandidate); + if (cmp != 0) return cmp; + if (paperBuild < o.paperBuild) { return -1; - } else if (snapshotRelease > o.snapshotRelease) { + } else if (paperBuild > o.paperBuild) { return 1; - } else { // equal snapshot release - if (preRelease < o.preRelease) { - return -1; - } else if (preRelease > o.preRelease) { - return 1; - } else { // equal prerelease - if (releaseCandidate < o.releaseCandidate) { - return -1; - } else if (releaseCandidate > o.releaseCandidate) { - return 1; - } else { // equal release candidate - if (paperBuild < o.paperBuild) { - return -1; - } else if (paperBuild > o.paperBuild) { - return 1; - } else { // equal paper build - return Double.compare(revision, o.revision); - } - } - } + } else { + return Double.compare(revision, o.revision); } } } diff --git a/Essentials/src/test/java/com/earth2me/essentials/UtilTest.java b/Essentials/src/test/java/com/earth2me/essentials/UtilTest.java index 221de015505..33c88ea4485 100644 --- a/Essentials/src/test/java/com/earth2me/essentials/UtilTest.java +++ b/Essentials/src/test/java/com/earth2me/essentials/UtilTest.java @@ -303,13 +303,17 @@ public void testVer() { .isLowerThan(VersionUtil.BukkitVersion.fromString("26.1-pre-1-R0.1-SNAPSHOT"))); assertTrue(VersionUtil.BukkitVersion.fromString("1.21.11-R0.1-SNAPSHOT") .isLowerThan(VersionUtil.BukkitVersion.fromString("26.1-rc-1-R0.1-SNAPSHOT"))); - // Dev variants are considered higher than their base version (for feature checks) - assertTrue(VersionUtil.BukkitVersion.fromString("26.1-snapshot-1-R0.1-SNAPSHOT") - .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); - assertTrue(VersionUtil.BukkitVersion.fromString("26.1-pre-1-R0.1-SNAPSHOT") - .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); + // Base release is higher than dev variants: snapshot < pre < rc < release + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT") + .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-snapshot-1-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT") + .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-pre-1-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT") + .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-rc-1-R0.1-SNAPSHOT"))); assertTrue(VersionUtil.BukkitVersion.fromString("26.1-rc-1-R0.1-SNAPSHOT") - .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); + .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-pre-1-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1-pre-1-R0.1-SNAPSHOT") + .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-snapshot-1-R0.1-SNAPSHOT"))); // Dev variants of 26.1 are lower than 26.2 assertTrue(VersionUtil.BukkitVersion.fromString("26.1-snapshot-99-R0.1-SNAPSHOT") .isLowerThan(VersionUtil.BukkitVersion.fromString("26.2-R0.1-SNAPSHOT"))); @@ -441,5 +445,17 @@ public void testVer() { VersionUtil.BukkitVersion.fromString("26.1.build.99-recommended")); assertEquals(VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.8-alpha"), VersionUtil.BukkitVersion.fromString("26.1-rc-3-R0.0")); + // Patch release with Paper build does not match different patch via equalsBaseVersion + assertFalse(VersionUtil.BukkitVersion.fromString("26.1.1.build.14-alpha") + .equalsBaseVersion(VersionUtil.BukkitVersion.fromString("26.1-R0.1-SNAPSHOT"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1.1.build.14-alpha") + .equalsBaseVersion(VersionUtil.BukkitVersion.fromString("26.1.1-R0.1-SNAPSHOT"))); + // Base release paper build is higher than rc/pre/snapshot paper builds + assertTrue(VersionUtil.BukkitVersion.fromString("26.1.build.5-alpha") + .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-rc-3.build.8-alpha"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1.build.1-alpha") + .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-pre-1.build.99-alpha"))); + assertTrue(VersionUtil.BukkitVersion.fromString("26.1.build.1-alpha") + .isHigherThan(VersionUtil.BukkitVersion.fromString("26.1-snapshot-99.build.99-alpha"))); } } From 716a61eb1645b9c5602b406f539a8656efd0257f Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Sat, 4 Apr 2026 12:17:36 -0700 Subject: [PATCH 23/37] Mitigate Discord 429s (#6461) --- .../discord/JDADiscordService.java | 37 ++-- .../discord/util/ConsoleInjector.java | 76 ++----- .../essentialsx/discord/util/DiscordUtil.java | 17 +- .../discord/util/WebhookDispatcher.java | 186 ++++++++++++++++++ .../discord/util/WrappedWebhookClient.java | 4 + .../discordlink/rolesync/RoleSyncManager.java | 128 ++++++++---- 6 files changed, 325 insertions(+), 123 deletions(-) create mode 100644 EssentialsDiscord/src/main/java/net/essentialsx/discord/util/WebhookDispatcher.java diff --git a/EssentialsDiscord/src/main/java/net/essentialsx/discord/JDADiscordService.java b/EssentialsDiscord/src/main/java/net/essentialsx/discord/JDADiscordService.java index cdaa1ceec06..dc8bdd2aba9 100644 --- a/EssentialsDiscord/src/main/java/net/essentialsx/discord/JDADiscordService.java +++ b/EssentialsDiscord/src/main/java/net/essentialsx/discord/JDADiscordService.java @@ -50,7 +50,7 @@ import net.essentialsx.discord.util.ConsoleInjector; import net.essentialsx.discord.util.DiscordUtil; import net.essentialsx.discord.util.MessageUtil; -import net.essentialsx.discord.util.WrappedWebhookClient; +import net.essentialsx.discord.util.WebhookDispatcher; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.HandlerList; @@ -84,11 +84,11 @@ public class JDADiscordService implements DiscordService, IEssentialsModule { private JDA jda; private Guild guild; private TextChannel primaryChannel; - private WrappedWebhookClient consoleWebhook; + private WebhookDispatcher consoleWebhook; private String lastConsoleId; private final Map registeredTypes = new HashMap<>(); private final Map typeToChannelId = new HashMap<>(); - private final Map channelIdToWebhook = new HashMap<>(); + private final Map channelIdToWebhook = new HashMap<>(); private ConsoleInjector injector; private DiscordCommandDispatcher commandDispatcher; private InteractionControllerImpl interactionController; @@ -145,11 +145,11 @@ public void sendMessage(DiscordMessageEvent event, String message, boolean group final String webhookChannelId = typeToChannelId.get(event.getType()); if (webhookChannelId != null) { - final WrappedWebhookClient client = channelIdToWebhook.get(webhookChannelId); - if (client != null) { + final WebhookDispatcher dispatcher = channelIdToWebhook.get(webhookChannelId); + if (dispatcher != null) { final String avatarUrl = event.getAvatarUrl() != null ? event.getAvatarUrl() : jda.getSelfUser().getAvatarUrl(); final String name = event.getName() != null ? event.getName() : guild.getSelfMember().getEffectiveName(); - client.send(getWebhookMessage(strippedContent, avatarUrl, name, groupMentions)); + dispatcher.send(getWebhookMessage(strippedContent, avatarUrl, name, groupMentions)); return; } } @@ -160,7 +160,7 @@ public void sendMessage(DiscordMessageEvent event, String message, boolean group } channel.sendMessage(strippedContent) .setAllowedMentions(groupMentions ? null : DiscordUtil.NO_GROUP_MENTIONS) - .queue(); + .queue(null, error -> logger.log(Level.WARNING, "Failed to send message to channel " + channel.getName(), error)); } public void startup() throws LoginException, InterruptedException { @@ -229,7 +229,7 @@ public void startup() throws LoginException, InterruptedException { } // Load emotes into cache, JDA will handle updates from here on out. - guild.retrieveEmojis().queue(); + guild.retrieveEmojis().queue(null, error -> logger.log(Level.WARNING, "Failed to retrieve emojis from guild", error)); updatePrimaryChannel(); @@ -390,8 +390,8 @@ public void updatePresence() { public void updateTypesRelay() { if (!getSettings().isShowAvatar() && !getSettings().isCustomBotName()) { - for (WrappedWebhookClient webhook : channelIdToWebhook.values()) { - webhook.close(); + for (WebhookDispatcher dispatcher : channelIdToWebhook.values()) { + dispatcher.close(); } typeToChannelId.clear(); channelIdToWebhook.clear(); @@ -410,14 +410,14 @@ public void updateTypesRelay() { final Webhook webhook = DiscordUtil.getOrCreateWebhook(channel, DiscordUtil.ADVANCED_RELAY_NAME).join(); if (webhook == null) { - final WrappedWebhookClient current = channelIdToWebhook.remove(channel.getId()); + final WebhookDispatcher current = channelIdToWebhook.remove(channel.getId()); if (current != null) { current.close(); } continue; } typeToChannelId.put(type, channel.getId()); - channelIdToWebhook.put(channel.getId(), DiscordUtil.getWebhookClient(webhook.getIdLong(), webhook.getToken(), jda.getHttpClient())); + channelIdToWebhook.put(channel.getId(), new WebhookDispatcher(DiscordUtil.getWebhookClient(webhook.getIdLong(), webhook.getToken(), jda.getHttpClient()))); } } @@ -471,7 +471,7 @@ public void updateConsoleRelay() { } shutdownConsoleRelay(false); - consoleWebhook = DiscordUtil.getWebhookClient(webhookId, webhookToken, jda.getHttpClient()); + consoleWebhook = new WebhookDispatcher(DiscordUtil.getWebhookClient(webhookId, webhookToken, jda.getHttpClient()), 50); if (injector == null || injector.isRemoved()) { injector = new ConsoleInjector(this); injector.start(); @@ -510,8 +510,8 @@ public void shutdown() { shutdownConsoleRelay(true); - for (WrappedWebhookClient webhook : channelIdToWebhook.values()) { - webhook.close(); + for (WebhookDispatcher dispatcher : channelIdToWebhook.values()) { + dispatcher.close(); } // Unregister leftover jda listeners @@ -583,7 +583,10 @@ public CompletableFuture modifyMemberRoles(InteractionMember member, Colle } final CompletableFuture future = new CompletableFuture<>(); - guild.modifyMemberRoles(((InteractionMemberImpl) member).getJdaObject(), add, remove).queue(future::complete); + guild.modifyMemberRoles(((InteractionMemberImpl) member).getJdaObject(), add, remove).queue(future::complete, error -> { + logger.log(Level.WARNING, "Failed to modify member roles", error); + future.complete(null); + }); return future; } @@ -613,7 +616,7 @@ public DiscordSettings getSettings() { return plugin.getSettings(); } - public WrappedWebhookClient getConsoleWebhook() { + public WebhookDispatcher getConsoleWebhook() { return consoleWebhook; } diff --git a/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/ConsoleInjector.java b/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/ConsoleInjector.java index 4794ddce786..266fee02937 100644 --- a/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/ConsoleInjector.java +++ b/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/ConsoleInjector.java @@ -14,10 +14,8 @@ import org.bukkit.Bukkit; import java.time.Instant; +import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Pattern; import static com.earth2me.essentials.I18n.tlLiteral; @@ -27,40 +25,18 @@ public class ConsoleInjector extends AbstractAppender { private final static java.util.logging.Logger logger = EssentialsDiscord.getWrappedLogger(); private final static long QUEUE_PROCESS_PERIOD_SECONDS = 2; + private final static int QUEUE_CAPACITY = 500; private final JDADiscordService jda; - private final BlockingQueue messageQueue = new LinkedBlockingQueue<>(); + private final BlockingQueue messageQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY); private final int taskId; private boolean removed = false; - private final AtomicLong lastRateLimitTime = new AtomicLong(0); - private final AtomicInteger recentRateLimit = new AtomicInteger(0); - private final AtomicInteger totalBackoffEvents = new AtomicInteger(); - public ConsoleInjector(JDADiscordService jda) { super("EssentialsX-ConsoleInjector", null, null, false); this.jda = jda; ((Logger) LogManager.getRootLogger()).addAppender(this); taskId = Bukkit.getScheduler().runTaskTimerAsynchronously(jda.getPlugin(), () -> { - // Check to see if we're supposed to be backing off, preform backoff if the case. - if (recentRateLimit.get() < 0) { - if (totalBackoffEvents.get() * 20 >= jda.getSettings().getConsoleSkipDelay() * 60) { - logger.warning("EssXBackoff: Reached console skip delay, attempt to skip"); - jda.getConsoleWebhook().abandonRequests(); - messageQueue.clear(); - totalBackoffEvents.set(0); - recentRateLimit.set(0); - lastRateLimitTime.set(0); - return; - } - - final int backoff = recentRateLimit.incrementAndGet(); - if (jda.isDebug()) { - logger.warning("EssXBackoff: Webhook backoff in progress, skipping queue processing. Resuming in " + Math.abs(backoff) + " cycles."); - } - return; - } - final StringBuilder buffer = new StringBuilder(); String curLine; while ((curLine = messageQueue.peek()) != null) { @@ -78,7 +54,11 @@ public ConsoleInjector(JDADiscordService jda) { } private void sendMessage(String content) { - jda.getConsoleWebhook().send(jda.getWebhookMessage(content)).exceptionally(e -> { + final WebhookDispatcher webhook = jda.getConsoleWebhook(); + if (webhook == null || webhook.isShutdown()) { + return; + } + webhook.send(jda.getWebhookMessage(content)).exceptionally(e -> { logger.severe(tlLiteral("discordErrorWebhook")); remove(); return null; @@ -97,40 +77,13 @@ public void append(LogEvent event) { return; } - if (entry.startsWith("EssXBackoff: ")) { - return; - } - - if (event.getLoggerName().contains("club.minnced.discord.webhook.WebhookClient") && entry.startsWith("Encountered 429, retrying after ")) { - if (recentRateLimit.get() >= 0) { - recentRateLimit.incrementAndGet(); - } - - if (lastRateLimitTime.get() == 0 || System.currentTimeMillis() - lastRateLimitTime.get() > 5000) { - lastRateLimitTime.set(System.currentTimeMillis()); - - // A negative value would mean the timer is current preforming a backoff, don't stop it. - if (recentRateLimit.get() >= 0) { - recentRateLimit.set(0); - } - } else if (recentRateLimit.get() >= 2) { - // Start the webhook backoff, defaulting to 20s, which should reset our bucket. - if (jda.isDebug()) { - totalBackoffEvents.getAndIncrement(); - logger.warning("EssXBackoff: Beginning Webhook Backoff"); - } - recentRateLimit.set(-20); - } - return; - } - final String[] loggerNameSplit = event.getLoggerName().split("\\."); final String loggerName = loggerNameSplit[loggerNameSplit.length - 1].trim(); if (!loggerName.isEmpty()) { entry = "[" + loggerName + "] " + entry; } - + if (!jda.getSettings().getConsoleFilters().isEmpty()) { for (final Pattern pattern : jda.getSettings().getConsoleFilters()) { if (pattern.matcher(entry).find()) { @@ -139,11 +92,18 @@ public void append(LogEvent event) { } } - messageQueue.addAll(Splitter.fixedLength(Message.MAX_CONTENT_LENGTH - 50).splitToList( + for (final String line : Splitter.fixedLength(Message.MAX_CONTENT_LENGTH - 50).splitToList( MessageUtil.formatMessage(jda.getSettings().getConsoleFormat(), TimeFormat.TIME_LONG.format(Instant.now()), event.getLevel().name(), - MessageUtil.sanitizeDiscordMarkdown(entry)))); + MessageUtil.sanitizeDiscordMarkdown(entry)))) { + + if (!messageQueue.offer(line)) { + if (jda.isDebug()) { + logger.fine("Console relay queue full, dropping message."); + } + } + } } public void remove() { diff --git a/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/DiscordUtil.java b/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/DiscordUtil.java index 4911e91689a..8686e65c4cd 100644 --- a/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/DiscordUtil.java +++ b/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/DiscordUtil.java @@ -15,6 +15,7 @@ import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.essentialsx.api.v2.events.discord.DiscordMessageEvent; import net.essentialsx.api.v2.services.discord.MessageType; +import net.essentialsx.discord.EssentialsDiscord; import net.essentialsx.discord.JDADiscordService; import okhttp3.OkHttpClient; import org.bukkit.Bukkit; @@ -26,8 +27,11 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; +import java.util.logging.Level; +import java.util.logging.Logger; public final class DiscordUtil { + private static final Logger logger = EssentialsDiscord.getWrappedLogger(); public final static String ADVANCED_RELAY_NAME = "EssX Advanced Relay"; public final static String CONSOLE_RELAY_NAME = "EssX Console Relay"; public final static List NO_GROUP_MENTIONS; @@ -63,7 +67,6 @@ public static WrappedWebhookClient getWebhookClient(long id, String token, OkHtt * * @param channel The channel to search for/create webhooks in. * @param webhookName The name of the webhook to search for/create. - * * @return A future which completes with the webhook by the given name in the given channel, or null * if the bot lacks the proper permissions. */ @@ -82,7 +85,7 @@ public static CompletableFuture getOrCreateWebhook(final TextChannel ch } } createWebhook(channel, webhookName).thenAccept(future::complete); - }); + }, error -> logger.log(Level.WARNING, "Failed to retrieve webhooks from channel " + channel.getName(), error)); return future; } @@ -101,17 +104,17 @@ private static void cleanWebhooks(final Guild guild, String webhookName) { guild.retrieveWebhooks().queue(webhooks -> { for (final Webhook webhook : webhooks) { if (webhook.getName().equalsIgnoreCase(webhookName) && !ACTIVE_WEBHOOKS.contains(webhook.getId())) { - webhook.delete().reason("EssentialsX Discord: webhook cleanup").queue(); + webhook.delete().reason("EssentialsX Discord: webhook cleanup").queue(null, error -> logger.log(Level.WARNING, "Failed to delete webhook " + webhook.getName(), error)); } } - }); + }, error -> logger.log(Level.WARNING, "Failed to retrieve webhooks from guild " + guild.getName(), error)); } /** * Creates a webhook with the given name in the given channel. * - * @param channel The channel to search for webhooks in. - * @param webhookName The name of the webhook to look for. + * @param channel The channel to search for webhooks in. + * @param webhookName The name of the webhook to look for. * @return A future which completes with the webhook by the given name in the given channel or null if no permissions. */ public static CompletableFuture createWebhook(TextChannel channel, String webhookName) { @@ -123,7 +126,7 @@ public static CompletableFuture createWebhook(TextChannel channel, Stri channel.createWebhook(webhookName).queue(webhook -> { future.complete(webhook); ACTIVE_WEBHOOKS.addIfAbsent(webhook.getId()); - }); + }, error -> logger.log(Level.WARNING, "Failed to create webhook " + webhookName + " in channel " + channel.getName(), error)); return future; } diff --git a/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/WebhookDispatcher.java b/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/WebhookDispatcher.java new file mode 100644 index 00000000000..9a0d14dbb65 --- /dev/null +++ b/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/WebhookDispatcher.java @@ -0,0 +1,186 @@ +package net.essentialsx.discord.util; + +import club.minnced.discord.webhook.receive.ReadonlyMessage; +import club.minnced.discord.webhook.send.WebhookMessage; +import net.essentialsx.discord.EssentialsDiscord; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Rate-limited webhook dispatcher that sends at most one message per drain cycle + * and backs off exponentially when Discord returns 429 (rate limited). + *

+ * Discord webhook rate limit: ~5 requests per 2 seconds per webhook. + * We send 1 message every 500ms (= 4/2s), staying under the limit with headroom. + * On 429, we pause all sends and back off exponentially. + */ +public class WebhookDispatcher { + private static final Logger logger = EssentialsDiscord.getWrappedLogger(); + + private static final long DRAIN_INTERVAL_MS = 500; + private static final int DEFAULT_QUEUE_CAPACITY = 30; + private static final long INITIAL_BACKOFF_MS = 4000; + private static final long MAX_BACKOFF_MS = 30000; + + private final WrappedWebhookClient client; + private final BlockingQueue queue; + private final ScheduledExecutorService scheduler; + private final ScheduledFuture drainTask; + + private final AtomicInteger totalDropped = new AtomicInteger(0); + private final AtomicInteger total429s = new AtomicInteger(0); + + private volatile long backoffUntil = 0; + private volatile long currentBackoffMs = INITIAL_BACKOFF_MS; + private volatile boolean shutdown = false; + + public WebhookDispatcher(WrappedWebhookClient client) { + this(client, DEFAULT_QUEUE_CAPACITY); + } + + public WebhookDispatcher(WrappedWebhookClient client, int queueCapacity) { + this.client = client; + this.queue = new LinkedBlockingQueue<>(queueCapacity); + this.scheduler = Executors.newSingleThreadScheduledExecutor(r -> { + final Thread t = new Thread(r, "EssX-WebhookDispatcher-" + client.getId()); + t.setDaemon(true); + return t; + }); + this.drainTask = scheduler.scheduleAtFixedRate(this::drain, DRAIN_INTERVAL_MS, DRAIN_INTERVAL_MS, TimeUnit.MILLISECONDS); + } + + public CompletableFuture send(WebhookMessage message) { + if (shutdown) { + return CompletableFuture.completedFuture(null); + } + + final CompletableFuture future = new CompletableFuture<>(); + final PendingMessage pending = new PendingMessage(message, future); + + if (!queue.offer(pending)) { + final PendingMessage dropped = queue.poll(); + if (dropped != null) { + dropped.future.complete(null); + totalDropped.incrementAndGet(); + } + + if (!queue.offer(pending)) { + future.complete(null); + totalDropped.incrementAndGet(); + } + } + + return future; + } + + private void drain() { + if (shutdown) { + return; + } + + final long now = System.currentTimeMillis(); + if (now < backoffUntil) { + return; + } + + final PendingMessage pending = queue.poll(); + if (pending == null) { + return; + } + + try { + client.send(pending.message).whenComplete((result, error) -> { + if (error != null) { + final String errorMsg = error.getMessage() != null ? error.getMessage() : error.getClass().getSimpleName(); + if (errorMsg.contains("429") || errorMsg.contains("rate limit") || errorMsg.toLowerCase().contains("ratelimit")) { + final int count429 = total429s.incrementAndGet(); + backoffUntil = System.currentTimeMillis() + currentBackoffMs; + logger.warning("Webhook rate limited (429). Backing off " + currentBackoffMs + "ms. Queue: " + queue.size() + ", 429 count: " + count429 + ", dropped: " + totalDropped.get()); + currentBackoffMs = Math.min(currentBackoffMs * 2, MAX_BACKOFF_MS); + } else { + logger.log(Level.WARNING, "Webhook send failed", error); + } + pending.future.completeExceptionally(error); + } else { + currentBackoffMs = INITIAL_BACKOFF_MS; + pending.future.complete(result); + } + }); + } catch (Exception e) { + logger.log(Level.WARNING, "Error dispatching webhook message", e); + pending.future.completeExceptionally(e); + } + } + + public void abandonRequests() { + PendingMessage pending; + int count = 0; + while ((pending = queue.poll()) != null) { + pending.future.complete(null); + count++; + } + client.abandonRequests(); + if (count > 0) { + logger.info("WebhookDispatcher: Abandoned " + count + " pending message(s)."); + } + } + + public void close() { + shutdown = true; + drainTask.cancel(false); + scheduler.shutdown(); + try { + if (!scheduler.awaitTermination(3, TimeUnit.SECONDS)) { + scheduler.shutdownNow(); + } + } catch (InterruptedException e) { + scheduler.shutdownNow(); + } + + PendingMessage pending; + while ((pending = queue.poll()) != null) { + pending.future.complete(null); + } + + client.close(); + } + + public boolean isShutdown() { + return shutdown || client.isShutdown(); + } + + public WrappedWebhookClient getClient() { + return client; + } + + public int getQueueSize() { + return queue.size(); + } + + public int getTotalDropped() { + return totalDropped.get(); + } + + public int getTotal429s() { + return total429s.get(); + } + + private static class PendingMessage { + final WebhookMessage message; + final CompletableFuture future; + + PendingMessage(WebhookMessage message, CompletableFuture future) { + this.message = message; + this.future = future; + } + } +} diff --git a/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/WrappedWebhookClient.java b/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/WrappedWebhookClient.java index 7a8a4db46fc..0b358321a80 100644 --- a/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/WrappedWebhookClient.java +++ b/EssentialsDiscord/src/main/java/net/essentialsx/discord/util/WrappedWebhookClient.java @@ -57,6 +57,10 @@ public WrappedWebhookClient(final long id, final String token, final OkHttpClien webhookQueue = queue; } + public long getId() { + return webhookClient.getId(); + } + public CompletableFuture send(WebhookMessage message) { return webhookClient.send(message); } diff --git a/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/rolesync/RoleSyncManager.java b/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/rolesync/RoleSyncManager.java index 8e9c31d74f4..5a72ae4345a 100644 --- a/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/rolesync/RoleSyncManager.java +++ b/EssentialsDiscordLink/src/main/java/net/essentialsx/discordlink/rolesync/RoleSyncManager.java @@ -3,7 +3,6 @@ import com.earth2me.essentials.UUIDPlayer; import com.google.common.collect.BiMap; import net.essentialsx.api.v2.events.discordlink.DiscordLinkStatusChangeEvent; -import net.essentialsx.api.v2.services.discord.InteractionMember; import net.essentialsx.api.v2.services.discord.InteractionRole; import net.essentialsx.discordlink.EssentialsDiscordLink; import org.bukkit.Bukkit; @@ -18,6 +17,9 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Semaphore; +import java.util.logging.Level; import static com.earth2me.essentials.I18n.tlLiteral; @@ -25,6 +27,8 @@ public class RoleSyncManager implements Listener { private final EssentialsDiscordLink ess; private final Map groupToRoleMap = new HashMap<>(); private final Map roleIdToGroupMap = new HashMap<>(); + private final Semaphore syncSemaphore = new Semaphore(5); + private int syncCursor = 0; public RoleSyncManager(final EssentialsDiscordLink ess) { this.ess = ess; @@ -36,12 +40,28 @@ public RoleSyncManager(final EssentialsDiscordLink ess) { } final BiMap uuidToDiscordCopy = ess.getAccountStorage().getRawStorageMap(); + if (uuidToDiscordCopy.isEmpty()) { + syncCursor = 0; + return; + } + + final List> entries = new ArrayList<>(uuidToDiscordCopy.entrySet()); + final int size = entries.size(); + if (syncCursor >= size) { + syncCursor = 0; + } + + final int start = syncCursor; + final int end = Math.min(start + 50, size); + syncCursor = end >= size ? 0 : end; + final Map groupToRoleMapCopy = new HashMap<>(groupToRoleMap); final Map roleIdToGroupMapCopy = new HashMap<>(roleIdToGroupMap); final boolean primaryOnly = ess.getSettings().isRoleSyncPrimaryGroupOnly(); final boolean removeGroups = ess.getSettings().isRoleSyncRemoveGroups(); final boolean removeRoles = ess.getSettings().isRoleSyncRemoveRoles(); - for (final Map.Entry entry : uuidToDiscordCopy.entrySet()) { + for (int i = start; i < end; i++) { + final Map.Entry entry = entries.get(i); sync(new UUIDPlayer(UUID.fromString(entry.getKey())), entry.getValue(), groupToRoleMapCopy, roleIdToGroupMapCopy, primaryOnly, removeGroups, removeRoles); } }, 0, ess.getSettings().getRoleSyncResyncDelay() * 1200L); @@ -60,41 +80,52 @@ public void sync(final Player player, final String discordId, final Map groups = primaryOnly ? Collections.singletonList(ess.getEss().getPermissionsHandler().getGroup(player)) : ess.getEss().getPermissionsHandler().getGroups(player); - final InteractionMember member = ess.getApi().getMemberById(discordId).join(); - - if (member == null) { - if (ess.getSettings().isUnlinkOnLeave()) { - ess.getLinkManager().removeAccount(ess.getEss().getUser(player), DiscordLinkStatusChangeEvent.Cause.UNSYNC_LEAVE); - } else { - unSync(player.getUniqueId(), discordId); - } - return; - } - - final List toAdd = new ArrayList<>(); - final List toRemove = new ArrayList<>(); - - for (final Map.Entry entry : groupToRoleMap.entrySet()) { - if (groups.contains(entry.getKey()) && !member.hasRole(entry.getValue())) { - toAdd.add(entry.getValue()); - } else if (removeRoles && !groups.contains(entry.getKey()) && member.hasRole(entry.getValue())) { - toRemove.add(entry.getValue()); - } - } - - for (final Map.Entry entry : roleIdToGroupMap.entrySet()) { - if (member.hasRole(entry.getKey()) && !groups.contains(entry.getValue())) { - ess.getEss().getPermissionsHandler().addToGroup(player, entry.getValue()); - } else if (removeGroups && !member.hasRole(entry.getKey()) && groups.contains(entry.getValue())) { - ess.getEss().getPermissionsHandler().removeFromGroup(player, entry.getValue()); - } - } - - if (toAdd.isEmpty() && toRemove.isEmpty()) { - return; - } - - ess.getApi().modifyMemberRoles(member, toAdd, toRemove); + ess.getEss().runTaskAsynchronously(() -> { + syncSemaphore.acquireUninterruptibly(); + ess.getApi().getMemberById(discordId).thenCompose(member -> { + if (member == null) { + if (ess.getSettings().isUnlinkOnLeave()) { + ess.getLinkManager().removeAccount(ess.getEss().getUser(player), DiscordLinkStatusChangeEvent.Cause.UNSYNC_LEAVE); + } else { + ess.getEss().runTaskAsynchronously(() -> unSync(player.getUniqueId(), discordId)); + } + return CompletableFuture.completedFuture(null); + } + + final List toAdd = new ArrayList<>(); + final List toRemove = new ArrayList<>(); + + for (final Map.Entry entry : groupToRoleMap.entrySet()) { + if (groups.contains(entry.getKey()) && !member.hasRole(entry.getValue())) { + toAdd.add(entry.getValue()); + } else if (removeRoles && !groups.contains(entry.getKey()) && member.hasRole(entry.getValue())) { + toRemove.add(entry.getValue()); + } + } + + for (final Map.Entry entry : roleIdToGroupMap.entrySet()) { + if (member.hasRole(entry.getKey()) && !groups.contains(entry.getValue())) { + ess.getEss().getPermissionsHandler().addToGroup(player, entry.getValue()); + } else if (removeGroups && !member.hasRole(entry.getKey()) && groups.contains(entry.getValue())) { + ess.getEss().getPermissionsHandler().removeFromGroup(player, entry.getValue()); + } + } + + if (toAdd.isEmpty() && toRemove.isEmpty()) { + return CompletableFuture.completedFuture(null); + } + + return ess.getApi() + .modifyMemberRoles(member, toAdd, toRemove) + .exceptionally(e -> { + ess.getLogger().log(Level.WARNING, "Failed to modify Discord roles for " + player.getUniqueId() + " / " + discordId, e); + return null; + }); + }).exceptionally(e -> { + ess.getLogger().log(Level.WARNING, "Failed to fetch Discord member for " + player.getUniqueId() + " / " + discordId, e); + return null; + }).whenComplete((unused, throwable) -> syncSemaphore.release()); + }); } public void unSync(final UUID uuid, final String discordId) { @@ -108,7 +139,6 @@ public void unSync(final UUID uuid, final String discordId) { final Map roleIdToGroupMapCopy = new HashMap<>(roleIdToGroupMap); final Player player = new UUIDPlayer(uuid); - final InteractionMember member = ess.getApi().getMemberById(discordId).join(); if (removeGroups) { for (final String group : roleIdToGroupMapCopy.values()) { @@ -116,10 +146,26 @@ public void unSync(final UUID uuid, final String discordId) { } } - // Check if the member is no longer in the guild (null), they don't have any roles anyway. - if (removeRoles && member != null) { - ess.getApi().modifyMemberRoles(member, null, groupToRoleMapCopy.values()); + if (!removeRoles) { + return; } + + ess.getEss().runTaskAsynchronously(() -> { + syncSemaphore.acquireUninterruptibly(); + ess.getApi().getMemberById(discordId).thenCompose(member -> { + // Check if the member is no longer in the guild (null), they don't have any roles anyway. + if (member == null) { + return CompletableFuture.completedFuture(null); + } + return ess.getApi().modifyMemberRoles(member, null, groupToRoleMapCopy.values()).exceptionally(e -> { + ess.getLogger().log(Level.WARNING, "Failed to remove Discord roles for " + uuid + " / " + discordId, e); + return null; + }); + }).exceptionally(e -> { + ess.getLogger().log(Level.WARNING, "Failed to fetch Discord member for unsync " + uuid + " / " + discordId, e); + return null; + }).whenComplete((unused, throwable) -> syncSemaphore.release()); + }); } @EventHandler From 5a78df5494991aec6a2a7ce34bf23f716ee0d6ea Mon Sep 17 00:00:00 2001 From: vbojovic Date: Sun, 5 Apr 2026 04:36:13 +0200 Subject: [PATCH 24/37] Add option to allow fishing when afk (#6465) Fixes #6443 Fixes #6323 - Prevent AFK timer reset when fishing rod bobber is idle - Fix incorrect teleportOfflineUnknown message showing sender instead of target --------- Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com> --- .../earth2me/essentials/EssentialsPlayerListener.java | 4 ++++ .../src/main/java/com/earth2me/essentials/ISettings.java | 2 ++ .../src/main/java/com/earth2me/essentials/Settings.java | 5 +++++ .../earth2me/essentials/commands/Commandtpoffline.java | 2 +- Essentials/src/main/resources/config.yml | 9 ++++++--- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java index c1fb775d7b4..efd64c56b02 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java @@ -1174,6 +1174,10 @@ public void onInventoryCloseEvent(final InventoryCloseEvent event) { @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) public void onPlayerFishEvent(final PlayerFishEvent event) { + if (!ess.getSettings().cancelAfkOnFish()) { + return; + } + final User user = ess.getUser(event.getPlayer()); user.updateActivityOnInteract(true); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/ISettings.java b/Essentials/src/main/java/com/earth2me/essentials/ISettings.java index 0ba2d7d9c14..857dd0ce226 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/ISettings.java +++ b/Essentials/src/main/java/com/earth2me/essentials/ISettings.java @@ -221,6 +221,8 @@ public interface ISettings extends IConf { boolean cancelAfkOnChat(); + boolean cancelAfkOnFish(); + boolean sleepIgnoresAfkPlayers(); boolean sleepIgnoresVanishedPlayers(); diff --git a/Essentials/src/main/java/com/earth2me/essentials/Settings.java b/Essentials/src/main/java/com/earth2me/essentials/Settings.java index 8d41da405e7..136a448ba5d 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Settings.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Settings.java @@ -1320,6 +1320,11 @@ public boolean cancelAfkOnChat() { return config.getBoolean("cancel-afk-on-chat", true); } + @Override + public boolean cancelAfkOnFish() { + return config.getBoolean("cancel-afk-on-fish", true); + } + @Override public boolean sleepIgnoresAfkPlayers() { return sleepIgnoresAfkPlayers; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpoffline.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpoffline.java index 5448aa7cfeb..cc4f31c0e32 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpoffline.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtpoffline.java @@ -21,7 +21,7 @@ public void run(final Server server, final User user, final String label, final final Location logout = target.getLogoutLocation(); if (logout == null) { - user.sendTl("teleportOfflineUnknown", user.getDisplayName()); + user.sendTl("teleportOfflineUnknown", target.getDisplayName()); throw new NoChargeException(); } diff --git a/Essentials/src/main/resources/config.yml b/Essentials/src/main/resources/config.yml index 5c87444d9fb..25bbf68d716 100644 --- a/Essentials/src/main/resources/config.yml +++ b/Essentials/src/main/resources/config.yml @@ -491,7 +491,7 @@ auto-afk: 300 auto-afk-timeout: -1 # A list of commands to be executed instead of kicking the player once the -# threshold defined above in 'afk-auto-timeout' is reached. If this list is empty +# threshold defined above in 'afk-auto-timeout' is reached. If this list is empty # and 'afk-auto-timeout' is not set to -1, Essentials will default to # kicking the player once they reach the timeout threshold. # @@ -527,6 +527,9 @@ cancel-afk-on-move: true # Should Essentials automatically remove AFK status when a player sends a chat message? cancel-afk-on-chat: true +# Should Essentials automatically remove AFK status when a player uses a fishing rod? +cancel-afk-on-fish: true + # Should AFK players be ignored when other players are trying to sleep? # When this setting is false, players won't be able to skip the night if some players are AFK. # Players with the permission 'essentials.sleepingignored' will always be ignored. @@ -897,8 +900,8 @@ baltop-requirements: minimum-balance: 0 minimum-playtime: 0 -# Limit the number of cached balance top entries. -# Recommended for servers with a large number of players, as it reduces memory usage. +# Limit the number of cached balance top entries. +# Recommended for servers with a large number of players, as it reduces memory usage. # Set to -1 to disable the limit. baltop-entry-limit: -1 From 0845ea6f08d24e01766c2fcae4bda449d31512a9 Mon Sep 17 00:00:00 2001 From: CrystalD5 Date: Sun, 5 Apr 2026 04:57:29 +0200 Subject: [PATCH 25/37] Fixed NoSuchMethodException Player.getRespawnLocation() (#6460) Fixes #6459. Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com> --- .../main/java/com/earth2me/essentials/utils/VersionUtil.java | 1 + .../essentials/spawn/EssentialsSpawnPlayerListener.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java index 17449d91c54..5872e5f3219 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java @@ -40,6 +40,7 @@ public final class VersionUtil { public static final BukkitVersion v1_19_R01 = BukkitVersion.fromString("1.19-R0.1-SNAPSHOT"); public static final BukkitVersion v1_19_4_R01 = BukkitVersion.fromString("1.19.4-R0.1-SNAPSHOT"); public static final BukkitVersion v1_20_1_R01 = BukkitVersion.fromString("1.20.1-R0.1-SNAPSHOT"); + public static final BukkitVersion v1_20_4_R01 = BukkitVersion.fromString("1.20.4-R0.1-SNAPSHOT"); public static final BukkitVersion v1_20_6_R01 = BukkitVersion.fromString("1.20.6-R0.1-SNAPSHOT"); public static final BukkitVersion v1_21_R01 = BukkitVersion.fromString("1.21-R0.1-SNAPSHOT"); public static final BukkitVersion v1_21_3_R01 = BukkitVersion.fromString("1.21.3-R0.1-SNAPSHOT"); diff --git a/EssentialsSpawn/src/main/java/com/earth2me/essentials/spawn/EssentialsSpawnPlayerListener.java b/EssentialsSpawn/src/main/java/com/earth2me/essentials/spawn/EssentialsSpawnPlayerListener.java index 6f2b8902af7..bf6e19492d3 100644 --- a/EssentialsSpawn/src/main/java/com/earth2me/essentials/spawn/EssentialsSpawnPlayerListener.java +++ b/EssentialsSpawn/src/main/java/com/earth2me/essentials/spawn/EssentialsSpawnPlayerListener.java @@ -49,9 +49,9 @@ void onPlayerRespawn(final PlayerRespawnEvent event) { (!VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_16_1_R01) || (!event.isAnchorSpawn() || ess.getSettings().isRespawnAtAnchor()))) { // cannot nuke this sync load due to the event being sync so it would hand either way - if(VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_16_1_R01)) { + if(VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_20_4_R01)) { respawnLocation = user.getBase().getRespawnLocation(); - } else { // For versions prior to 1.16. + } else { // For versions prior to 1.20.4. respawnLocation = user.getBase().getBedSpawnLocation(); } } From 08b7fe3bb8b5dfb66fa1a0f792dd88b27272905f Mon Sep 17 00:00:00 2001 From: Golfing7 <51512085+Golfing7@users.noreply.github.com> Date: Sat, 4 Apr 2026 23:46:06 -0400 Subject: [PATCH 26/37] Add PreTransactionEvent (#6370) ### Information I added a PreTransactionEvent for cancelling transactions between users. ### Details **Proposed feature:** Adding a PreTransactionEvent. **Environments tested:** OS: Linux Mint Java version: 23 - [x] Most recent Paper version (1.XX.Y, git-Paper-BUILD) - [ ] CraftBukkit/Spigot/Paper 1.12.2 - [ ] CraftBukkit 1.8.8 **Demonstration:** ```java @EventHandler public void onPay(PreTransactionEvent event) { // Prevent money from being sent outside their own faction. IUser userSource = event.getRequester().getUser(); if (userSource == null) return; FPlayer fplayerSource = FPlayers.getInstance().getByPlayer(userSource.getBase()); if (fplayerSource == null || fplayerSource.isAdminBypassing()) return; Faction faction = fplayerSource.getFaction(); if (faction == null || !isFactionVoluntaryExcluded(faction.getId())) return; // Who are they sending it to? FPlayer target = FPlayers.getInstance().getById(event.getTarget().getUUID().toString()); Faction factionTarget = target.getFaction(); if (faction != factionTarget) { event.setCancelled(true); msg(userSource.getBase(), cantPayOutsideExcludedFactionMessage); } } ``` This code prevents transactions between two users when two factions can't interact. --------- Co-authored-by: Josh Roy <10731363+JRoy@users.noreply.github.com> --- .../java/com/earth2me/essentials/User.java | 11 +++- .../api/v2/events/PreTransactionEvent.java | 59 +++++++++++++++++++ .../api/v2/events/TransactionEvent.java | 10 +++- 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 Essentials/src/main/java/net/essentialsx/api/v2/events/PreTransactionEvent.java diff --git a/Essentials/src/main/java/com/earth2me/essentials/User.java b/Essentials/src/main/java/com/earth2me/essentials/User.java index a261880720b..36a2df5ffc1 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/User.java +++ b/Essentials/src/main/java/com/earth2me/essentials/User.java @@ -23,6 +23,7 @@ import net.ess3.api.events.MuteStatusChangeEvent; import net.ess3.api.events.UserBalanceUpdateEvent; import net.ess3.provider.PlayerLocaleProvider; +import net.essentialsx.api.v2.events.PreTransactionEvent; import net.essentialsx.api.v2.events.TransactionEvent; import net.essentialsx.api.v2.services.mail.MailSender; import org.bukkit.Location; @@ -263,12 +264,20 @@ public void payUser(final User reciever, final BigDecimal value) throws Exceptio payUser(reciever, value, UserBalanceUpdateEvent.Cause.UNKNOWN); } - public void payUser(final User reciever, final BigDecimal value, final UserBalanceUpdateEvent.Cause cause) throws Exception { + public void payUser(final User reciever, BigDecimal value, final UserBalanceUpdateEvent.Cause cause) throws Exception { if (value.compareTo(BigDecimal.ZERO) < 1) { throw new Exception(tlLocale(playerLocale, "payMustBePositive")); } if (canAfford(value)) { + // Call an event for pre-transaction + final PreTransactionEvent preTransactionEvent = new PreTransactionEvent(this.getSource(), reciever, value); + ess.getServer().getPluginManager().callEvent(preTransactionEvent); + if (preTransactionEvent.isCancelled()) { + return; + } + value = preTransactionEvent.getAmount(); + setMoney(getMoney().subtract(value), cause); reciever.setMoney(reciever.getMoney().add(value), cause); sendTl("moneySentTo", AdventureUtil.parsed(NumberUtil.displayCurrency(value, ess)), reciever.getDisplayName()); diff --git a/Essentials/src/main/java/net/essentialsx/api/v2/events/PreTransactionEvent.java b/Essentials/src/main/java/net/essentialsx/api/v2/events/PreTransactionEvent.java new file mode 100644 index 00000000000..e772a7239e8 --- /dev/null +++ b/Essentials/src/main/java/net/essentialsx/api/v2/events/PreTransactionEvent.java @@ -0,0 +1,59 @@ +package net.essentialsx.api.v2.events; + +import com.earth2me.essentials.CommandSource; +import com.google.common.base.Preconditions; +import net.ess3.api.IUser; +import org.bukkit.Bukkit; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; + +import java.math.BigDecimal; + +/** + * Fired when a transaction (e.g. /pay) is about to be handled. + */ +public class PreTransactionEvent extends TransactionEvent implements Cancellable { + private static final HandlerList handlers = new HandlerList(); + + private boolean cancelled; + + public PreTransactionEvent(final CommandSource requester, final IUser target, final BigDecimal amount) { + super(!Bukkit.isPrimaryThread(), requester, target, amount); + } + + /** + * Sets the amount to be subtracted from the requester's balance. + *

+ * Note: Changing this amount will not verify the requester actually has enough balance to complete the transaction. + * @param decimal the new amount + */ + public void setAmount(final BigDecimal decimal) { + Preconditions.checkNotNull(decimal, "decimal cannot be null"); + Preconditions.checkArgument(decimal.compareTo(BigDecimal.ZERO) >= 0, "decimal cannot be negative"); + + this.amount = decimal; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + /** + * If this event should be cancelled. If cancelled, no messages will be displayed to the users involved. + * @param cancelled whether this event should be cancelled + */ + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/Essentials/src/main/java/net/essentialsx/api/v2/events/TransactionEvent.java b/Essentials/src/main/java/net/essentialsx/api/v2/events/TransactionEvent.java index 9e09c4f58bb..1a26946d6bd 100644 --- a/Essentials/src/main/java/net/essentialsx/api/v2/events/TransactionEvent.java +++ b/Essentials/src/main/java/net/essentialsx/api/v2/events/TransactionEvent.java @@ -16,15 +16,19 @@ public class TransactionEvent extends Event { private final CommandSource requester; private final IUser target; - private final BigDecimal amount; + protected BigDecimal amount; - public TransactionEvent(CommandSource requester, IUser target, BigDecimal amount) { - super(!Bukkit.isPrimaryThread()); + protected TransactionEvent(boolean async, CommandSource requester, IUser target, BigDecimal amount) { + super(async); this.requester = requester; this.target = target; this.amount = amount; } + public TransactionEvent(CommandSource requester, IUser target, BigDecimal amount) { + this(!Bukkit.isPrimaryThread(), requester, target, amount); + } + /** * @return the user who initiated the transaction */ From f5fc4305c91f93349cb0697ceb3dc1a6a7249d5f Mon Sep 17 00:00:00 2001 From: Touchie Date: Sun, 5 Apr 2026 07:49:23 +0300 Subject: [PATCH 27/37] fix(fly): persist fly mode and restore on join (#6403) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Persist /fly state in userdata and re-enable allowFlight on login so flight isn’t lost after relogging on ground (upstream #6344). ### Information This PR fixes #6344 ### Details **Proposed fix:** Persist the /fly state to userdata and restore allowFlight on login so flight isn’t lost after relogging while on the ground. **Environments tested:** OS: Ubuntu 24.04 Java version: 21 - [x] Most recent Paper version (1.XX.Y, git-Paper-BUILD) - [x] Paper 1.21.8 - [ ] CraftBukkit/Spigot/Paper 1.12.2 - [ ] CraftBukkit 1.8.8 --------- Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com> --- .../essentials/EssentialsPlayerListener.java | 15 ++++++++++++++- .../java/com/earth2me/essentials/UserData.java | 9 +++++++++ .../earth2me/essentials/commands/Commandfly.java | 2 ++ .../config/holders/UserConfigHolder.java | 10 ++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java index efd64c56b02..ade93e5940e 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java @@ -503,12 +503,20 @@ private void joinFlow(final User user, final long currentTime, final String mess }); } + final boolean restoreFly = user.isFlyModeEnabled() && user.isAuthorized("essentials.fly"); + if (restoreFly) { + user.getBase().setAllowFlight(true); + if (ess.getSettings().isSendFlyEnableOnJoin()) { + user.sendTl("flyMode", CommonPlaceholders.enableDisable(user.getSource(), true), user.getDisplayName()); + } + } + if (user.isAuthorized("essentials.fly.safelogin")) { user.getBase().setFallDistance(0); if (LocationUtil.shouldFly(ess, user.getLocation())) { user.getBase().setAllowFlight(true); user.getBase().setFlying(true); - if (ess.getSettings().isSendFlyEnableOnJoin()) { + if (!restoreFly && ess.getSettings().isSendFlyEnableOnJoin()) { user.sendTl("flyMode", CommonPlaceholders.enableDisable(user.getSource(), true), user.getDisplayName()); } } @@ -524,6 +532,11 @@ private void joinFlow(final User user, final long currentTime, final String mess ess.getLogger().log(Level.INFO, "Set socialspy to false for {0} because they had it enabled without permission.", user.getName()); } + if (user.isFlyModeEnabled() && !user.isAuthorized("essentials.fly")) { + user.setFlyModeEnabled(false); + ess.getLogger().log(Level.INFO, "Set fly mode to false for {0} because they had it enabled without permission.", user.getName()); + } + if (user.isGodModeEnabled() && !user.isAuthorized("essentials.god")) { user.setGodModeEnabled(false); ess.getLogger().log(Level.INFO, "Set god mode to false for {0} because they had it enabled without permission.", user.getName()); diff --git a/Essentials/src/main/java/com/earth2me/essentials/UserData.java b/Essentials/src/main/java/com/earth2me/essentials/UserData.java index 6e280b8942e..2f2a2c8e87b 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/UserData.java +++ b/Essentials/src/main/java/com/earth2me/essentials/UserData.java @@ -469,6 +469,15 @@ public void setGodModeEnabled(final boolean set) { config.save(); } + public boolean isFlyModeEnabled() { + return holder.flyMode(); + } + + public void setFlyModeEnabled(final boolean set) { + holder.flyMode(set); + config.save(); + } + public boolean getMuted() { return holder.muted(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandfly.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandfly.java index c6a38fa4771..c56eaef1588 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandfly.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandfly.java @@ -34,6 +34,8 @@ protected void togglePlayer(final CommandSource sender, final User user, Boolean user.getBase().setFallDistance(0f); user.getBase().setAllowFlight(enabled); + user.setFlyModeEnabled(enabled); + if (!user.getBase().getAllowFlight()) { user.getBase().setFlying(false); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/config/holders/UserConfigHolder.java b/Essentials/src/main/java/com/earth2me/essentials/config/holders/UserConfigHolder.java index 5455376675a..2e278667390 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/config/holders/UserConfigHolder.java +++ b/Essentials/src/main/java/com/earth2me/essentials/config/holders/UserConfigHolder.java @@ -171,6 +171,16 @@ public void godMode(final boolean value) { this.godmode = value; } + private boolean flymode = false; + + public boolean flyMode() { + return this.flymode; + } + + public void flyMode(final boolean value) { + this.flymode = value; + } + private boolean muted = false; public boolean muted() { From 236ce8837ea576077b1ced78681048440c60940c Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Sun, 5 Apr 2026 10:41:29 -0700 Subject: [PATCH 28/37] Add /unmute command and broadcast on unmute (#6491) Fixes #6419 --- .../earth2me/essentials/commands/Commandmute.java | 15 ++++++++++++--- Essentials/src/main/resources/messages.properties | 2 ++ .../src/main/resources/messages_en.properties | 2 ++ Essentials/src/main/resources/plugin.yml | 2 +- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmute.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmute.java index b5c7302b5a7..b028742bf5d 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmute.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmute.java @@ -21,6 +21,7 @@ public Commandmute() { @Override public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception { + final boolean isUnmute = commandLabel.toLowerCase(java.util.Locale.ENGLISH).contains("unmute"); boolean nomatch = false; if (args.length < 1) { throw new NotEnoughArgumentsException(); @@ -32,11 +33,17 @@ public void run(final Server server, final CommandSource sender, final String co nomatch = true; user = ess.getUser(new OfflinePlayerStub(args[0], ess.getServer())); } + + if (isUnmute && !user.getMuted()) { + sender.sendTl("playerNotMuted", user.getDisplayName()); + return; + } + if (!user.getBase().isOnline() && sender.isPlayer()) { if (!sender.isAuthorized("essentials.mute.offline")) { throw new TranslatableException("muteExemptOffline"); } - } else if (user.isAuthorized("essentials.mute.exempt")) { + } else if (!isUnmute && user.isAuthorized("essentials.mute.exempt")) { throw new TranslatableException("muteExempt"); } @@ -44,7 +51,7 @@ public void run(final Server server, final CommandSource sender, final String co final String time; String muteReason = null; - if (args.length > 1) { + if (!isUnmute && args.length > 1) { time = args[1]; try { muteTimestamp = DateUtil.parseDateDiff(time, true); @@ -59,7 +66,7 @@ public void run(final Server server, final CommandSource sender, final String co } } - final boolean willMute = (args.length > 1) || !user.getMuted(); + final boolean willMute = !isUnmute && ((args.length > 1) || !user.getMuted()); final User controller = sender.isPlayer() ? ess.getUser(sender.getPlayer()) : null; final MuteStatusChangeEvent event = new MuteStatusChangeEvent(user, controller, willMute, muteTimestamp, muteReason); ess.getServer().getPluginManager().callEvent(event); @@ -121,6 +128,8 @@ public void run(final Server server, final CommandSource sender, final String co } else { sender.sendTl("unmutedPlayer", user.getDisplayName()); user.sendTl("playerUnmuted"); + ess.getLogger().log(Level.INFO, ess.getAdventureFacet().miniToLegacy(tlLiteral("unmuteNotify", sender.getSender().getName(), user.getName()))); + ess.broadcastTl(null, "essentials.mute.notify", "unmuteNotify", new Object[]{sender.getSender().getName(), user.getName()}); } } } diff --git a/Essentials/src/main/resources/messages.properties b/Essentials/src/main/resources/messages.properties index 50e945d886f..fe2fd99baee 100644 --- a/Essentials/src/main/resources/messages.properties +++ b/Essentials/src/main/resources/messages.properties @@ -1480,7 +1480,9 @@ unlinkCommandDescription=Unlinks your Minecraft account from the currently linke unlinkCommandUsage=/ unlinkCommandUsage1=/ unlinkCommandUsage1Description=Unlinks your Minecraft account from the currently linked Discord account. +unmuteNotify={0} unmuted {1}. unmutedPlayer=Player {0} unmuted. +playerNotMuted={0} is not muted. unsafeTeleportDestination=The teleport destination is unsafe and teleport-safety is disabled. unsupportedBrand=The server platform you are currently running does not provide the capabilities for this feature. unsupportedFeature=This feature is not supported on the current server version. diff --git a/Essentials/src/main/resources/messages_en.properties b/Essentials/src/main/resources/messages_en.properties index 0689d5b2948..67fe0229f8e 100644 --- a/Essentials/src/main/resources/messages_en.properties +++ b/Essentials/src/main/resources/messages_en.properties @@ -1463,7 +1463,9 @@ unlinkCommandDescription=Unlinks your Minecraft account from the currently linke unlinkCommandUsage=/ unlinkCommandUsage1=/ unlinkCommandUsage1Description=Unlinks your Minecraft account from the currently linked Discord account. +unmuteNotify={0} unmuted {1}. unmutedPlayer=Player {0} unmuted. +playerNotMuted={0} is not muted. unsafeTeleportDestination=The teleport destination is unsafe and teleport-safety is disabled. unsupportedBrand=The server platform you are currently running does not provide the capabilities for this feature. unsupportedFeature=This feature is not supported on the current server version. diff --git a/Essentials/src/main/resources/plugin.yml b/Essentials/src/main/resources/plugin.yml index 844f9231926..4c578304f3e 100644 --- a/Essentials/src/main/resources/plugin.yml +++ b/Essentials/src/main/resources/plugin.yml @@ -314,7 +314,7 @@ commands: mute: description: Mutes or unmutes a player. usage: / [datediff] [reason] - aliases: [emute,silence,esilence] + aliases: [emute,silence,esilence,unmute,eunmute] near: description: Lists the players near by or around a player. usage: / [playername] [radius] From 86621cd90ee619bff132497976a94cae82d7f1bd Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Sun, 5 Apr 2026 10:41:33 -0700 Subject: [PATCH 29/37] Don't enable fly mode on login when player is in water (#6490) Fixes #6198 --- .../java/com/earth2me/essentials/EssentialsPlayerListener.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java index ade93e5940e..4f9ddcd7b77 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java @@ -513,7 +513,8 @@ private void joinFlow(final User user, final long currentTime, final String mess if (user.isAuthorized("essentials.fly.safelogin")) { user.getBase().setFallDistance(0); - if (LocationUtil.shouldFly(ess, user.getLocation())) { + final boolean inWater = VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_17_R01) && user.getBase().isInWater(); + if (!inWater && LocationUtil.shouldFly(ess, user.getLocation())) { user.getBase().setAllowFlight(true); user.getBase().setFlying(true); if (!restoreFly && ess.getSettings().isSendFlyEnableOnJoin()) { From 73aa65733ed7ce82d4cd921cfe60ba4dddc49f09 Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Sun, 5 Apr 2026 10:41:34 -0700 Subject: [PATCH 30/37] Reject Windows reserved filenames in /setwarp (#6487) Fixes #5430 stop gap for before we do #5267 --- .../earth2me/essentials/commands/Commandsetwarp.java | 4 ++++ .../java/com/earth2me/essentials/utils/StringUtil.java | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsetwarp.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsetwarp.java index f5adff26d03..a8b470a68fe 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsetwarp.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsetwarp.java @@ -25,6 +25,10 @@ public void run(final Server server, final User user, final String commandLabel, throw new TranslatableException("invalidWarpName"); } + if (StringUtil.isReservedFileName(args[0])) { + throw new TranslatableException("invalidWarpName"); + } + final IWarps warps = ess.getWarps(); Location warpLoc = null; diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/StringUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/StringUtil.java index d9d6602d7b1..a3d949b83b5 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/StringUtil.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/StringUtil.java @@ -18,11 +18,21 @@ public final class StringUtil { private StringUtil() { } + private static final Pattern WINDOWS_RESERVED = Pattern.compile("^(con|prn|aux|nul|com[0-9]|lpt[0-9])$"); + //Used to clean file names before saving to disk public static String sanitizeFileName(final String name) { return INVALIDFILECHARS.matcher(name.toLowerCase(Locale.ENGLISH)).replaceAll("_"); } + /** + * Returns true if the given name is a Windows reserved filename (e.g. CON, NUL, AUX). + * Using these as file names on Windows will hang or crash the server. + */ + public static boolean isReservedFileName(final String name) { + return WINDOWS_RESERVED.matcher(name.toLowerCase(Locale.ENGLISH)).matches(); + } + //Used to clean strings/names before saving as filenames/permissions public static String safeString(final String string) { if (string == null) { From 00b4a5b3bd7e709ea4d0bc239bbe3b83189c3649 Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Sun, 5 Apr 2026 10:41:47 -0700 Subject: [PATCH 31/37] Make /flyspeed and /walkspeed force the correct speed type (#6485) Fixes #4883 --- .../earth2me/essentials/commands/Commandspeed.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandspeed.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandspeed.java index 35c6d20cfa7..e636c53e609 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandspeed.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandspeed.java @@ -37,7 +37,8 @@ protected void run(final Server server, final User user, final String commandLab final float speed; final boolean isBypass = user.isAuthorized("essentials.speed.bypass"); if (args.length == 1) { - isFly = flyPermCheck(user, user.getBase().isFlying()); + final boolean inferredFly = isFlyAlias(commandLabel) ? true : isWalkAlias(commandLabel) ? false : user.getBase().isFlying(); + isFly = flyPermCheck(user, inferredFly); speed = getMoveSpeed(args[0]); } else { isFly = flyPermCheck(user, isFlyMode(args[0])); @@ -91,6 +92,14 @@ private Boolean flyPermCheck(final User user, final boolean input) { } else return !canWalk; } + private boolean isFlyAlias(final String label) { + return label.contains("fly") || label.equalsIgnoreCase("fspeed") || label.equalsIgnoreCase("efspeed"); + } + + private boolean isWalkAlias(final String label) { + return label.contains("walk") || label.equalsIgnoreCase("wspeed") || label.equalsIgnoreCase("ewspeed"); + } + private boolean isFlyMode(final String modeString) throws NotEnoughArgumentsException { final boolean isFlyMode; if (modeString.contains("fly") || modeString.equalsIgnoreCase("f")) { From 4db09f2a2e5098ddb458d77ce63ec9aa49fe45a8 Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Sun, 5 Apr 2026 10:41:51 -0700 Subject: [PATCH 32/37] Change default max-nick-length to 16 (#6484) Fixes #6028 --- Essentials/src/main/resources/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Essentials/src/main/resources/config.yml b/Essentials/src/main/resources/config.yml index 25bbf68d716..beb886dd867 100644 --- a/Essentials/src/main/resources/config.yml +++ b/Essentials/src/main/resources/config.yml @@ -34,7 +34,7 @@ ops-name-color: '4' nickname-prefix: '~' # The maximum length allowed in nicknames. The nickname prefix is not included in this. -max-nick-length: 15 +max-nick-length: 16 # The regex pattern used to determine if a requested nickname should be allowed for use. # If the requested nickname does not match this pattern, the nickname will be rejected. From ddaadf209936dba2d6ec71eef973d41c8cf3cc3a Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Sun, 5 Apr 2026 10:41:54 -0700 Subject: [PATCH 33/37] Add socialSpyCmdFormat message key for command social spy (#6489) Fixes #6049 --- .../com/earth2me/essentials/EssentialsPlayerListener.java | 5 ++--- Essentials/src/main/resources/messages.properties | 1 + Essentials/src/main/resources/messages_en.properties | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java index 4f9ddcd7b77..25a817993f4 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java @@ -808,10 +808,9 @@ public void handlePlayerCommandPreprocess(final PlayerCommandPreprocessEvent eve final ComponentHolder base = (user.isMuted() && ess.getSettings().getSocialSpyListenMutedPlayers()) ? spyer.tlComponent("socialSpyMutedPrefix") : spyer.tlComponent("socialSpyPrefix"); - final ComponentHolder nameComponent = ess.getAdventureFacet().legacyToAdventure(playerName); - final ComponentHolder messageComponent = ess.getAdventureFacet().text(": " + event.getMessage()); + final ComponentHolder formatted = ess.getAdventureFacet().deserializeMiniMessage(spyer.playerTl("socialSpyCmdFormat", playerName, event.getMessage())); - spyer.sendComponent(ess.getAdventureFacet().append(base, nameComponent, messageComponent)); + spyer.sendComponent(ess.getAdventureFacet().append(base, formatted)); } } } diff --git a/Essentials/src/main/resources/messages.properties b/Essentials/src/main/resources/messages.properties index fe2fd99baee..665f639f8a5 100644 --- a/Essentials/src/main/resources/messages.properties +++ b/Essentials/src/main/resources/messages.properties @@ -1234,6 +1234,7 @@ slimeMalformedSize=Malformed size. smithingtableCommandDescription=Opens up a smithing table. smithingtableCommandUsage=/ socialSpy=SocialSpy for {0}\: {1} +socialSpyCmdFormat={0}\: {1} socialSpyMsgFormat=[{0} -> {1}] {2} socialSpyMutedPrefix=[SS] (muted) socialspyCommandDescription=Toggles if you can see msg/mail commands in chat. diff --git a/Essentials/src/main/resources/messages_en.properties b/Essentials/src/main/resources/messages_en.properties index 67fe0229f8e..7180af28742 100644 --- a/Essentials/src/main/resources/messages_en.properties +++ b/Essentials/src/main/resources/messages_en.properties @@ -1225,6 +1225,7 @@ slimeMalformedSize=Malformed size. smithingtableCommandDescription=Opens up a smithing table. smithingtableCommandUsage=/ socialSpy=SocialSpy for {0}\: {1} +socialSpyCmdFormat={0}\: {1} socialSpyMsgFormat=[{0} -> {1}] {2} socialSpyMutedPrefix=[SS] (muted) socialspyCommandDescription=Toggles if you can see msg/mail commands in chat. From add5eb7cbfd4f297e4826cedd4930cdf0cc3c4c1 Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Sun, 5 Apr 2026 13:55:36 -0700 Subject: [PATCH 34/37] Add permission cache system + permission for vanish item pickup (#6488) Fixes #6270 --- .../essentials/EssentialsPlayerListener.java | 20 +++++++---- .../java/com/earth2me/essentials/IUser.java | 2 ++ .../java/com/earth2me/essentials/User.java | 33 ++++++++++++++++- .../essentials/perm/IPermissionsHandler.java | 7 ++++ .../essentials/perm/PermissionsHandler.java | 10 ++++++ .../perm/impl/LuckPermsHandler.java | 36 +++++++++++++++++++ Essentials/src/main/resources/plugin.yml | 3 ++ 7 files changed, 104 insertions(+), 7 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java index 25a817993f4..c0c46d909dc 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java @@ -1230,10 +1230,12 @@ private final class PickupListenerPre1_12 implements Listener { public void onPlayerPickupItem(final org.bukkit.event.player.PlayerPickupItemEvent event) { if (event.getItem().hasMetadata(Commandfireball.FIREBALL_META_KEY)) { event.setCancelled(true); - } else if (ess.getSettings().getDisableItemPickupWhileAfk()) { - if (ess.getUser(event.getPlayer()).isAfk()) { - event.setCancelled(true); - } + return; + } + final User user = ess.getUser(event.getPlayer()); + if ((ess.getSettings().getDisableItemPickupWhileAfk() && user.isAfk()) + || (user.isVanished() && !user.isAuthorizedCached("essentials.vanish.pickup"))) { + event.setCancelled(true); } } } @@ -1241,8 +1243,10 @@ public void onPlayerPickupItem(final org.bukkit.event.player.PlayerPickupItemEve private final class PickupListener1_12 implements Listener { @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) public void onPlayerPickupItem(final org.bukkit.event.entity.EntityPickupItemEvent event) { - if (ess.getSettings().getDisableItemPickupWhileAfk() && event.getEntity() instanceof Player) { - if (ess.getUser((Player) event.getEntity()).isAfk()) { + if (event.getEntity() instanceof Player) { + final User user = ess.getUser((Player) event.getEntity()); + if ((ess.getSettings().getDisableItemPickupWhileAfk() && user.isAfk()) + || (user.isVanished() && !user.isAuthorizedCached("essentials.vanish.pickup"))) { event.setCancelled(true); } } @@ -1261,6 +1265,10 @@ public void onGameEvent(final org.bukkit.event.block.BlockReceiveGameEvent event private final class CommandSendFilter implements CommandSendListenerProvider.Filter { @Override public Predicate apply(Player player) { + // There is no event for op status changes, but the command list is resent when + // a player is opped/deopped, so we invalidate cached permissions here. + ess.getPermissionsHandler().invalidatePermissionCache(player.getUniqueId()); + final User user = ess.getUser(player); final Set checked = new HashSet<>(); final Set toRemove = new HashSet<>(); diff --git a/Essentials/src/main/java/com/earth2me/essentials/IUser.java b/Essentials/src/main/java/com/earth2me/essentials/IUser.java index a701eb5610f..bb427dcba16 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/IUser.java +++ b/Essentials/src/main/java/com/earth2me/essentials/IUser.java @@ -32,6 +32,8 @@ public interface IUser { boolean isAuthorized(String node); + boolean isAuthorizedCached(String node); + boolean isAuthorized(IEssentialsCommand cmd); boolean isAuthorized(IEssentialsCommand cmd, String permissionPrefix); diff --git a/Essentials/src/main/java/com/earth2me/essentials/User.java b/Essentials/src/main/java/com/earth2me/essentials/User.java index 36a2df5ffc1..9b039bf21e6 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/User.java +++ b/Essentials/src/main/java/com/earth2me/essentials/User.java @@ -147,6 +147,19 @@ public boolean isAuthorized(final String node) { return result; } + @Override + public boolean isAuthorizedCached(final String node) { + if (Essentials.TESTING) { + return false; + } + + final boolean result = isAuthorizedCachedCheck(node); + if (ess.getSettings().isDebug()) { + ess.getLogger().log(Level.INFO, "checking if " + base.getName() + " has " + node + " (cached) - " + result); + } + return result; + } + @Override public boolean isPermissionSet(final String node) { if (Essentials.TESTING) { @@ -186,6 +199,24 @@ private boolean isAuthorizedCheck(final String node) { } } + private boolean isAuthorizedCachedCheck(final String node) { + if (base instanceof OfflinePlayerStub) { + return false; + } + + try { + return ess.getPermissionsHandler().hasPermissionCached(base, node); + } catch (final Exception ex) { + if (ess.getSettings().isDebug()) { + ess.getLogger().log(Level.SEVERE, "Permission System Error: " + ess.getPermissionsHandler().getName() + " returned: " + ex.getMessage(), ex); + } else { + ess.getLogger().log(Level.SEVERE, "Permission System Error: " + ess.getPermissionsHandler().getName() + " returned: " + ex.getMessage()); + } + + return false; + } + } + private boolean isPermSetCheck(final String node) { if (base instanceof OfflinePlayerStub) { return false; @@ -872,7 +903,7 @@ public void checkActivity() { } } final long autoafk = ess.getSettings().getAutoAfk(); - if (!isAfk() && autoafk > 0 && lastActivity + autoafk * 1000 < System.currentTimeMillis() && isAuthorized("essentials.afk.auto")) { + if (!isAfk() && autoafk > 0 && lastActivity + autoafk * 1000 < System.currentTimeMillis() && isAuthorizedCached("essentials.afk.auto")) { setAfk(true, AfkStatusChangeEvent.Cause.ACTIVITY); if (isAfk() && !isHidden()) { setDisplayNick(); diff --git a/Essentials/src/main/java/com/earth2me/essentials/perm/IPermissionsHandler.java b/Essentials/src/main/java/com/earth2me/essentials/perm/IPermissionsHandler.java index 76d47a2e160..6b440918050 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/perm/IPermissionsHandler.java +++ b/Essentials/src/main/java/com/earth2me/essentials/perm/IPermissionsHandler.java @@ -28,6 +28,13 @@ public interface IPermissionsHandler { boolean hasPermission(Player base, String node); + default boolean hasPermissionCached(Player base, String node) { + return hasPermission(base, node); + } + + default void invalidatePermissionCache(UUID uuid) { + } + // Does not check for * permissions boolean isPermissionSet(Player base, String node); diff --git a/Essentials/src/main/java/com/earth2me/essentials/perm/PermissionsHandler.java b/Essentials/src/main/java/com/earth2me/essentials/perm/PermissionsHandler.java index 0cbad14775b..b5c15cb051a 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/perm/PermissionsHandler.java +++ b/Essentials/src/main/java/com/earth2me/essentials/perm/PermissionsHandler.java @@ -105,6 +105,16 @@ public boolean hasPermission(final Player base, final String node) { return handler.hasPermission(base, node); } + @Override + public boolean hasPermissionCached(final Player base, final String node) { + return handler.hasPermissionCached(base, node); + } + + @Override + public void invalidatePermissionCache(final UUID uuid) { + handler.invalidatePermissionCache(uuid); + } + @Override public boolean isPermissionSet(final Player base, final String node) { return handler.isPermissionSet(base, node); diff --git a/Essentials/src/main/java/com/earth2me/essentials/perm/impl/LuckPermsHandler.java b/Essentials/src/main/java/com/earth2me/essentials/perm/impl/LuckPermsHandler.java index 1dc9418e75b..a3d1d0143ca 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/perm/impl/LuckPermsHandler.java +++ b/Essentials/src/main/java/com/earth2me/essentials/perm/impl/LuckPermsHandler.java @@ -7,17 +7,22 @@ import net.luckperms.api.context.ContextConsumer; import net.luckperms.api.context.ContextSet; import net.luckperms.api.context.ImmutableContextSet; +import net.luckperms.api.event.EventSubscription; +import net.luckperms.api.event.user.UserDataRecalculateEvent; import net.luckperms.api.model.group.Group; import net.luckperms.api.query.QueryOptions; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.plugin.RegisteredServiceProvider; +import java.util.logging.Level; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.function.Supplier; @@ -25,6 +30,8 @@ public class LuckPermsHandler extends ModernVaultHandler { private LuckPerms luckPerms; private Essentials ess; private CombinedCalculator calculator; + private final Map> permissionCache = new ConcurrentHashMap<>(); + private EventSubscription recalculateSubscription; @Override public void registerContext(final String context, final Function> calculator, final Supplier> suggestions) { @@ -41,6 +48,30 @@ public void unregisterContexts() { this.luckPerms.getContextManager().unregisterCalculator(this.calculator); this.calculator = null; } + if (this.recalculateSubscription != null) { + this.recalculateSubscription.close(); + this.recalculateSubscription = null; + } + this.permissionCache.clear(); + } + + @Override + public boolean hasPermissionCached(final Player base, final String node) { + final UUID uuid = base.getUniqueId(); + final Map userCache = permissionCache.computeIfAbsent(uuid, k -> new ConcurrentHashMap<>()); + return userCache.computeIfAbsent(node, k -> hasPermission(base, node)); + } + + @Override + public void invalidatePermissionCache(final UUID uuid) { + invalidateCache(uuid); + } + + public void invalidateCache(final UUID uuid) { + if (ess.getSettings().isDebug()) { + ess.getLogger().log(Level.INFO, "Invalidating permission cache for " + uuid); + } + permissionCache.remove(uuid); } @Override @@ -77,6 +108,11 @@ public boolean tryProvider(Essentials ess) { if (provider != null) { this.luckPerms = provider.getProvider(); this.ess = ess; + this.recalculateSubscription = this.luckPerms.getEventBus().subscribe( + ess, + UserDataRecalculateEvent.class, + event -> invalidateCache(event.getUser().getUniqueId()) + ); } return luckPerms != null && super.tryProvider(ess); } diff --git a/Essentials/src/main/resources/plugin.yml b/Essentials/src/main/resources/plugin.yml index 4c578304f3e..b18e7f06f6c 100644 --- a/Essentials/src/main/resources/plugin.yml +++ b/Essentials/src/main/resources/plugin.yml @@ -1374,6 +1374,9 @@ permissions: description: Applies invisibility effects to the player when they are in vanish mode essentials.vanish.interact: description: Allows the bearer to interact with players in vanish mode + essentials.vanish.pickup: + description: Allows the bearer to pick up items while in vanish mode + default: false essentials.version: description: Allows access to the /version command essentials.warp: From fb669aa9fd0fb8b33b25c0845903b3688e1dc31e Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Sun, 5 Apr 2026 13:55:38 -0700 Subject: [PATCH 35/37] Remove extractUrls from default legacy serializer (#6492) extractUrls on the default LegacyComponentSerializer caused legacyToMini to auto-wrap URLs in tags. When translation templates like discordCommandLink already wrapped {0} in their own click tag, this produced nested click events that Paper 1.21.11+ rejected. The default serializer no longer extracts URLs. A new legacyToMiniWithUrls method is available for cases that explicitly need clickable URL extraction (chat, broadcast). Fixes #6463 --- .../essentials/adventure/SpigotAdventureFacet.java | 8 +++++++- .../earth2me/essentials/commands/Commandbroadcast.java | 6 +++++- .../essentials/commands/Commandbroadcastworld.java | 6 +++++- .../essentials/chat/processing/AbstractChatHandler.java | 5 ++++- .../com/earth2me/essentials/adventure/AdventureFacet.java | 5 +++++ .../essentials/adventure/PaperAdventureFacet.java | 8 +++++++- 6 files changed, 33 insertions(+), 5 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/adventure/SpigotAdventureFacet.java b/Essentials/src/main/java/com/earth2me/essentials/adventure/SpigotAdventureFacet.java index fbf0b8020ae..1a15512d27d 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/adventure/SpigotAdventureFacet.java +++ b/Essentials/src/main/java/com/earth2me/essentials/adventure/SpigotAdventureFacet.java @@ -21,17 +21,18 @@ public class SpigotAdventureFacet implements AdventureFacet { private static final LegacyComponentSerializer LEGACY_SERIALIZER; + private static final LegacyComponentSerializer LEGACY_SERIALIZER_URLS; private static final MiniMessage MINI_MESSAGE_NO_TAGS; static { final LegacyComponentSerializer.Builder builder = LegacyComponentSerializer.builder() .flattener(ComponentFlattener.basic()) - .extractUrls(AbstractChatEvent.URL_PATTERN) .useUnusualXRepeatedCharacterHexFormat(); if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_16_1_R01)) { builder.hexColors(); } LEGACY_SERIALIZER = builder.build(); + LEGACY_SERIALIZER_URLS = builder.extractUrls(AbstractChatEvent.URL_PATTERN).build(); MINI_MESSAGE_NO_TAGS = MiniMessage.builder().strict(true).build(); } @@ -127,6 +128,11 @@ public String stripTags(String input) { return miniMessageInstance.stripTags(input); } + @Override + public String legacyToMiniWithUrls(String message) { + return miniMessageInstance.serialize(LEGACY_SERIALIZER_URLS.deserialize(message)); + } + @Override public String escapeTags(String input) { return miniMessageInstance.escapeTags(input); diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcast.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcast.java index 029fe9f13b7..e36d72c76a2 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcast.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcast.java @@ -1,6 +1,7 @@ package com.earth2me.essentials.commands; import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.adventure.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import org.bukkit.Server; @@ -15,6 +16,9 @@ public void run(final Server server, final CommandSource sender, final String co throw new NotEnoughArgumentsException(); } - ess.broadcastTl("broadcast", FormatUtil.replaceFormat(getFinalArg(args, 0)).replace("\\n", "\n"), sender.getDisplayName()); + final String message = FormatUtil.replaceFormat(getFinalArg(args, 0)).replace("\\n", "\n"); + ess.broadcastTl("broadcast", + AdventureUtil.parsed(ess.getAdventureFacet().legacyToMiniWithUrls(ess.getAdventureFacet().escapeTags(message))), + sender.getDisplayName()); } } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcastworld.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcastworld.java index bc8e31c8d95..7ecf52d4f58 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcastworld.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcastworld.java @@ -54,7 +54,11 @@ private void sendBroadcast(final World world, final String name, final String me if (message.isEmpty()) { throw new NotEnoughArgumentsException(); } - ess.broadcastTl(null, u -> !u.getBase().getWorld().equals(world), true, "broadcast", FormatUtil.replaceFormat(message).replace("\\n", "\n"), AdventureUtil.parsed(ess.getAdventureFacet().legacyToMini(name))); + final String formatted = FormatUtil.replaceFormat(message).replace("\\n", "\n"); + ess.broadcastTl(null, u -> !u.getBase().getWorld().equals(world), true, "broadcast", + AdventureUtil.parsed( + ess.getAdventureFacet().legacyToMiniWithUrls(ess.getAdventureFacet().escapeTags(formatted))), + AdventureUtil.parsed(ess.getAdventureFacet().legacyToMini(name))); } @Override diff --git a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AbstractChatHandler.java b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AbstractChatHandler.java index 4189a5b374d..00903a631b6 100644 --- a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AbstractChatHandler.java +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AbstractChatHandler.java @@ -223,7 +223,10 @@ protected void handleChatRecipients(AbstractChatEvent event) { server.getPluginManager().callEvent(spyEvent); if (!spyEvent.isCancelled()) { - final String legacyString = ess.getAdventureFacet().miniToLegacy(String.format(spyEvent.getFormat(), ess.getAdventureFacet().legacyToMini(user.getDisplayName()), ess.getAdventureFacet().legacyToMini(ess.getAdventureFacet().escapeTags(spyEvent.getMessage())))); + final String legacyString = ess.getAdventureFacet().miniToLegacy( + String.format(spyEvent.getFormat(), + ess.getAdventureFacet().legacyToMini(user.getDisplayName()), + ess.getAdventureFacet().legacyToMiniWithUrls(ess.getAdventureFacet().escapeTags(spyEvent.getMessage())))); for (final Player onlinePlayer : spyEvent.getRecipients()) { onlinePlayer.sendMessage(legacyString); diff --git a/providers/BaseProviders/src/main/java/com/earth2me/essentials/adventure/AdventureFacet.java b/providers/BaseProviders/src/main/java/com/earth2me/essentials/adventure/AdventureFacet.java index 90a581d1e63..dcab735fe1b 100644 --- a/providers/BaseProviders/src/main/java/com/earth2me/essentials/adventure/AdventureFacet.java +++ b/providers/BaseProviders/src/main/java/com/earth2me/essentials/adventure/AdventureFacet.java @@ -24,6 +24,11 @@ public interface AdventureFacet { */ String legacyToMini(String message, boolean useCustomTags); + /** + * Converts a section sign legacy string to a MiniMessage string, auto-linking detected URLs as click events. + */ + String legacyToMiniWithUrls(String message); + /** * Convenience method for submodules to escape MiniMessage tags. */ diff --git a/providers/PaperProvider/src/main/java/com/earth2me/essentials/adventure/PaperAdventureFacet.java b/providers/PaperProvider/src/main/java/com/earth2me/essentials/adventure/PaperAdventureFacet.java index fbc7ab39496..d5de801ce96 100644 --- a/providers/PaperProvider/src/main/java/com/earth2me/essentials/adventure/PaperAdventureFacet.java +++ b/providers/PaperProvider/src/main/java/com/earth2me/essentials/adventure/PaperAdventureFacet.java @@ -17,6 +17,7 @@ public class PaperAdventureFacet implements AdventureFacet { private final LegacyComponentSerializer legacySerializer; + private final LegacyComponentSerializer legacySerializerUrls; private final MiniMessage miniMessageNoTags; private final MiniMessage miniMessageInstance; @@ -29,10 +30,10 @@ public PaperAdventureFacet(final String primaryColor, final String secondaryColo final LegacyComponentSerializer.Builder builder = LegacyComponentSerializer.builder() .flattener(ComponentFlattener.basic()) - .extractUrls(AbstractChatEvent.URL_PATTERN) .hexColors() .useUnusualXRepeatedCharacterHexFormat(); legacySerializer = builder.build(); + legacySerializerUrls = builder.extractUrls(AbstractChatEvent.URL_PATTERN).build(); miniMessageNoTags = MiniMessage.builder().strict(true).build(); @@ -88,6 +89,11 @@ public String legacyToMini(String message, boolean useCustomTags) { } } + @Override + public String legacyToMiniWithUrls(String message) { + return miniMessageInstance.serialize(legacySerializerUrls.deserialize(message)); + } + @Override public String escapeTags(String input) { return miniMessageInstance.escapeTags(input); From 1038b3a5978075b3124c7e8b9d06648f31bf1bc1 Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Sun, 5 Apr 2026 13:55:41 -0700 Subject: [PATCH 36/37] Fix incorrect teleport safety message (#6486) old message seems needlessly technical anyway Fixes #5088 --- Essentials/src/main/resources/messages.properties | 2 +- Essentials/src/main/resources/messages_en.properties | 2 +- Essentials/src/main/resources/messages_en_GB.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Essentials/src/main/resources/messages.properties b/Essentials/src/main/resources/messages.properties index 665f639f8a5..0c8b6bc05f3 100644 --- a/Essentials/src/main/resources/messages.properties +++ b/Essentials/src/main/resources/messages.properties @@ -1483,8 +1483,8 @@ unlinkCommandUsage1=/ unlinkCommandUsage1Description=Unlinks your Minecraft account from the currently linked Discord account. unmuteNotify={0} unmuted {1}. unmutedPlayer=Player {0} unmuted. +unsafeTeleportDestination=The teleport destination is unsafe. playerNotMuted={0} is not muted. -unsafeTeleportDestination=The teleport destination is unsafe and teleport-safety is disabled. unsupportedBrand=The server platform you are currently running does not provide the capabilities for this feature. unsupportedFeature=This feature is not supported on the current server version. unvanishedReload=A reload has forced you to become visible. diff --git a/Essentials/src/main/resources/messages_en.properties b/Essentials/src/main/resources/messages_en.properties index 7180af28742..20889526a70 100644 --- a/Essentials/src/main/resources/messages_en.properties +++ b/Essentials/src/main/resources/messages_en.properties @@ -1466,8 +1466,8 @@ unlinkCommandUsage1=/ unlinkCommandUsage1Description=Unlinks your Minecraft account from the currently linked Discord account. unmuteNotify={0} unmuted {1}. unmutedPlayer=Player {0} unmuted. +unsafeTeleportDestination=The teleport destination is unsafe. playerNotMuted={0} is not muted. -unsafeTeleportDestination=The teleport destination is unsafe and teleport-safety is disabled. unsupportedBrand=The server platform you are currently running does not provide the capabilities for this feature. unsupportedFeature=This feature is not supported on the current server version. unvanishedReload=A reload has forced you to become visible. diff --git a/Essentials/src/main/resources/messages_en_GB.properties b/Essentials/src/main/resources/messages_en_GB.properties index 40d438034f1..9b577877d3a 100644 --- a/Essentials/src/main/resources/messages_en_GB.properties +++ b/Essentials/src/main/resources/messages_en_GB.properties @@ -1474,7 +1474,7 @@ unlinkCommandUsage=/ unlinkCommandUsage1=/ unlinkCommandUsage1Description=Unlinks your Minecraft account from the currently linked Discord account. unmutedPlayer=Player {0} unmuted. -unsafeTeleportDestination=The teleport destination is unsafe and teleport-safety is disabled. +unsafeTeleportDestination=The teleport destination is unsafe. unsupportedBrand=The server platform you are currently running does not provide the capabilities for this feature. unsupportedFeature=This feature is not supported on the current server version. unvanishedReload=A reload has forced you to become visible. From 63e7c4d259132f8bfc816c9eac7855cc486146c4 Mon Sep 17 00:00:00 2001 From: Jason <51760372+JasonHorkles@users.noreply.github.com> Date: Sat, 11 Apr 2026 13:06:18 -0600 Subject: [PATCH 37/37] Bump supported versions to 26.1.2 (#6500) --- .../main/java/com/earth2me/essentials/utils/VersionUtil.java | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java index 5872e5f3219..e606b6a67ba 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java @@ -47,7 +47,7 @@ public final class VersionUtil { public static final BukkitVersion v1_21_5_R01 = BukkitVersion.fromString("1.21.5-R0.1-SNAPSHOT"); public static final BukkitVersion v1_21_8_R01 = BukkitVersion.fromString("1.21.8-R0.1-SNAPSHOT"); public static final BukkitVersion v1_21_11_R01 = BukkitVersion.fromString("1.21.11-R0.1-SNAPSHOT"); - public static final BukkitVersion v26_1_R01 = BukkitVersion.fromString("26.1.1-R0.1-SNAPSHOT"); + public static final BukkitVersion v26_1_R01 = BukkitVersion.fromString("26.1.2-R0.1-SNAPSHOT"); private static final Set supportedVersions = ImmutableSet.of(v1_8_8_R01, v1_9_4_R01, v1_10_2_R01, v1_11_2_R01, v1_12_2_R01, v1_13_2_R01, v1_14_4_R01, v1_15_2_R01, v1_16_5_R01, v1_17_1_R01, v1_18_2_R01, v1_19_4_R01, v1_20_6_R01, v1_21_11_R01, v26_1_R01); diff --git a/README.md b/README.md index ace9c1c78c2..21b0a04ceea 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ however, have some new requirements: * **EssentialsX requires CraftBukkit, Spigot or Paper to run.** Other server software may work, but these are not tested by the team and we may not be able to help with any issues that occur. * **EssentialsX currently supports Minecraft versions 1.8.8, 1.9.4, 1.10.2, 1.11.2, 1.12.2, 1.13.2, 1.14.4, 1.15.2, - 1.16.5, 1.17.1, 1.18.2, 1.19.4, 1.20.6, 1.21.11, and 26.1.1.** + 1.16.5, 1.17.1, 1.18.2, 1.19.4, 1.20.6, 1.21.11, and 26.1.2.** * **EssentialsX currently requires Java 8 or higher.** We recommend using the latest Java version supported by your server software. * **EssentialsX requires [Vault](http://dev.bukkit.org/bukkit-plugins/vault/) to enable using chat prefix/suffixes and