From 1d7df88192473a2b626be76037fd17b70a72196a Mon Sep 17 00:00:00 2001 From: JRoy <10731363+JRoy@users.noreply.github.com> Date: Sun, 5 Apr 2026 11:52:52 -0700 Subject: [PATCH 1/3] Remove extractUrls from default legacy serializer 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 +++++++- .../com/earth2me/essentials/adventure/AdventureFacet.java | 5 +++++ .../essentials/adventure/PaperAdventureFacet.java | 8 +++++++- 3 files changed, 19 insertions(+), 2 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/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 7ab8c00ede7073e8cde0a91eb23b5e1cfe5c3b73 Mon Sep 17 00:00:00 2001 From: JRoy <10731363+JRoy@users.noreply.github.com> Date: Sun, 5 Apr 2026 12:08:00 -0700 Subject: [PATCH 2/3] Wire up legacyToMiniWithUrls for broadcast and chat spy Broadcast messages and chat spy output should have clickable URLs, so use the URL-extracting serializer for user message content in /broadcast, /broadcastworld, and local chat spy. --- .../com/earth2me/essentials/commands/Commandbroadcast.java | 4 +++- .../earth2me/essentials/commands/Commandbroadcastworld.java | 3 ++- .../essentials/chat/processing/AbstractChatHandler.java | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) 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..488f654c035 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,7 @@ 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..de5737ec080 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,8 @@ 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..b65085e06f7 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,7 @@ 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); From f529480ef1dd4edde54bdacfe128d482b3806ed7 Mon Sep 17 00:00:00 2001 From: JRoy <10731363+JRoy@users.noreply.github.com> Date: Sun, 5 Apr 2026 12:11:52 -0700 Subject: [PATCH 3/3] ure --- .../com/earth2me/essentials/commands/Commandbroadcast.java | 4 +++- .../earth2me/essentials/commands/Commandbroadcastworld.java | 5 ++++- .../essentials/chat/processing/AbstractChatHandler.java | 5 ++++- 3 files changed, 11 insertions(+), 3 deletions(-) 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 488f654c035..e36d72c76a2 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcast.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcast.java @@ -17,6 +17,8 @@ public void run(final Server server, final CommandSource sender, final String co } 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()); + 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 de5737ec080..7ecf52d4f58 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcastworld.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbroadcastworld.java @@ -55,7 +55,10 @@ private void sendBroadcast(final World world, final String name, final String me throw new NotEnoughArgumentsException(); } 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))); + 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 b65085e06f7..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().legacyToMiniWithUrls(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);