From 2ebfda2c2ca4b661f2d6143b4eebe0528a921f8f Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Wed, 5 Feb 2025 11:18:08 -0500 Subject: [PATCH 1/3] Add support for Paper's chat events --- .../essentials/chat/EssentialsChat.java | 11 ++- .../chat/processing/AbstractChatHandler.java | 66 +++++++-------- .../chat/processing/ChatHandler.java | 20 +++-- .../chat/processing/PaperChatHandler.java | 36 ++++++++ .../chat/processing/SpigotChatEvent.java | 72 ++++++++++++++++ build-logic/src/main/kotlin/constants.kt | 2 +- .../net/ess3/provider/AbstractChatEvent.java | 30 +++++++ .../provider/providers/PaperChatEvent.java | 83 +++++++++++++++++++ .../providers/PaperChatListenerProvider.java | 76 +++++++++++++++++ 9 files changed, 353 insertions(+), 43 deletions(-) create mode 100644 EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/PaperChatHandler.java create mode 100644 EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/SpigotChatEvent.java create mode 100644 providers/BaseProviders/src/main/java/net/ess3/provider/AbstractChatEvent.java create mode 100644 providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperChatEvent.java create mode 100644 providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperChatListenerProvider.java 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 739957fa663..a12f60f8f4b 100644 --- a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/EssentialsChat.java +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/EssentialsChat.java @@ -3,8 +3,10 @@ import com.earth2me.essentials.Essentials; import com.earth2me.essentials.EssentialsLogger; 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; import org.bukkit.command.CommandSender; @@ -32,8 +34,13 @@ public void onEnable() { return; } - final ChatHandler legacyHandler = new ChatHandler((Essentials) ess, this); - legacyHandler.registerListeners(); + if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_16_5_R01) && VersionUtil.isPaper()) { + final PaperChatHandler paperHandler = new PaperChatHandler((Essentials) ess, this); + paperHandler.registerListeners(); + } else { + final ChatHandler legacyHandler = new ChatHandler((Essentials) ess, this); + legacyHandler.registerListeners(); + } if (metrics == null) { metrics = new MetricsWrapper(this, 3814, 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 8bdd79f7f2d..edc39a503b0 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 @@ -8,6 +8,7 @@ import com.earth2me.essentials.utils.AdventureUtil; import com.earth2me.essentials.utils.FormatUtil; import net.ess3.api.events.LocalChatSpyEvent; +import net.ess3.provider.AbstractChatEvent; import net.essentialsx.api.v2.ChatType; import net.essentialsx.api.v2.events.chat.ChatEvent; import net.essentialsx.api.v2.events.chat.GlobalChatEvent; @@ -22,10 +23,8 @@ import org.bukkit.scoreboard.Team; import java.util.HashSet; -import java.util.Iterator; import java.util.Locale; import java.util.Set; -import java.util.logging.Level; import static com.earth2me.essentials.I18n.tlLiteral; @@ -48,7 +47,7 @@ protected AbstractChatHandler(Essentials ess, EssentialsChat essChat) { *

* Handled at {@link org.bukkit.event.EventPriority#LOWEST} on both preview and chat events. */ - protected void handleChatFormat(AsyncPlayerChatEvent event) { + protected void handleChatFormat(AbstractChatEvent event) { if (isAborted(event)) { return; } @@ -73,8 +72,9 @@ protected void handleChatFormat(AsyncPlayerChatEvent event) { final long configRadius = ess.getSettings().getChatRadius(); chat.setRadius(Math.max(configRadius, 0)); + final String formatted = FormatUtil.formatMessage(user, "essentials.chat", event.getMessage()); // This listener should apply the general chat formatting only...then return control back the event handler - event.setMessage(FormatUtil.formatMessage(user, "essentials.chat", event.getMessage())); + event.setMessage(formatted); if (ChatColor.stripColor(event.getMessage()).isEmpty()) { event.setCancelled(true); @@ -110,6 +110,12 @@ protected void handleChatFormat(AsyncPlayerChatEvent event) { event.setMessage(event.getMessage().substring(1)); } + // Prevent messages like "!&c" or "?&c" from being sent which would cause an empty message + if (ChatColor.stripColor(event.getMessage()).isEmpty()) { + event.setCancelled(true); + return; + } + if (chat.getType() == ChatType.UNKNOWN) { format = AdventureUtil.miniToLegacy(tlLiteral("chatTypeLocal")).concat(format); } else { @@ -128,7 +134,7 @@ protected void handleChatFormat(AsyncPlayerChatEvent event) { *

* Runs at {@link org.bukkit.event.EventPriority#NORMAL} priority on submitted chat events only. */ - protected void handleChatRecipients(AsyncPlayerChatEvent event) { + protected void handleChatRecipients(AbstractChatEvent event) { if (isAborted(event)) { return; } @@ -151,12 +157,12 @@ protected void handleChatRecipients(AsyncPlayerChatEvent event) { return; } - event.getRecipients().removeIf(player -> !ess.getUser(player).isAuthorized("essentials.chat.receive.local")); + event.removeRecipients(player -> !ess.getUser(player).isAuthorized("essentials.chat.receive.local")); } else { final String permission = "essentials.chat." + chat.getType().key(); if (user.isAuthorized(permission)) { - event.getRecipients().removeIf(player -> !ess.getUser(player).isAuthorized("essentials.chat.receive." + chat.getType().key())); + event.removeRecipients(player -> !ess.getUser(player).isAuthorized("essentials.chat.receive." + chat.getType().key())); callChatEvent(event, chat.getType(), null); } else { @@ -171,22 +177,10 @@ protected void handleChatRecipients(AsyncPlayerChatEvent event) { final Location loc = user.getLocation(); final World world = loc.getWorld(); - final Set outList = event.getRecipients(); final Set spyList = new HashSet<>(); - try { - outList.add(event.getPlayer()); - } catch (final UnsupportedOperationException ex) { - if (ess.getSettings().isDebug()) { - essChat.getLogger().log(Level.INFO, "Plugin triggered custom chat event, local chat handling aborted.", ex); - } - return; - } - - final Iterator it = outList.iterator(); - while (it.hasNext()) { - final Player onlinePlayer = it.next(); - final User onlineUser = ess.getUser(onlinePlayer); + event.removeRecipients(player -> { + final User onlineUser = ess.getUser(player); if (!onlineUser.equals(user)) { boolean abort = false; final Location playerLoc = onlineUser.getLocation(); @@ -200,12 +194,13 @@ protected void handleChatRecipients(AsyncPlayerChatEvent event) { } if (abort) { if (onlineUser.isAuthorized("essentials.chat.spy")) { - spyList.add(onlinePlayer); + spyList.add(player); } - it.remove(); + return true; } } - } + return false; + }); callChatEvent(event, ChatType.LOCAL, chat.getRadius()); @@ -213,7 +208,7 @@ protected void handleChatRecipients(AsyncPlayerChatEvent event) { return; } - if (outList.size() < 2) { + if (event.recipients().size() < 2) { user.sendTl("localNoOne"); } @@ -242,17 +237,22 @@ protected void handleChatRecipients(AsyncPlayerChatEvent event) { * @param chatType Chat type which determines which event will be created and called. * @param radius If chat is a local chat, this is a non-squared radius used to calculate recipients, otherwise {@code null}. */ - protected void callChatEvent(final AsyncPlayerChatEvent event, final ChatType chatType, final Long radius) { + protected void callChatEvent(final AbstractChatEvent event, final ChatType chatType, final Long radius) { final ChatEvent chatEvent; if (chatType == ChatType.LOCAL) { - chatEvent = new LocalChatEvent(event.isAsynchronous(), event.getPlayer(), event.getFormat(), event.getMessage(), event.getRecipients(), radius); + chatEvent = new LocalChatEvent(event.isAsynchronous(), event.getPlayer(), event.getFormat(), event.getMessage(), event.recipients(), radius); } else { - chatEvent = new GlobalChatEvent(event.isAsynchronous(), chatType, event.getPlayer(), event.getFormat(), event.getMessage(), event.getRecipients()); + chatEvent = new GlobalChatEvent(event.isAsynchronous(), chatType, event.getPlayer(), event.getFormat(), event.getMessage(), event.recipients()); } server.getPluginManager().callEvent(chatEvent); + event.removeRecipients(player -> !chatEvent.getRecipients().contains(player)); + for (final Player recipient : chatEvent.getRecipients()) { + event.addRecipient(recipient); + } + event.setFormat(chatEvent.getFormat()); event.setMessage(chatEvent.getMessage()); event.setCancelled(chatEvent.isCancelled()); @@ -262,9 +262,9 @@ protected void callChatEvent(final AsyncPlayerChatEvent event, final ChatType ch * Finalise the formatting stage of chat processing. *

* Handled at {@link org.bukkit.event.EventPriority#HIGHEST} during previews, and immediately after - * {@link #handleChatFormat(AsyncPlayerChatEvent)} when previews are not available. + * {@link #handleChatFormat(AbstractChatEvent)} when previews are not available. */ - protected void handleChatPostFormat(AsyncPlayerChatEvent event) { + protected void handleChatPostFormat(AbstractChatEvent event) { if (isAborted(event)) { cache.clearProcessedChat(event.getPlayer()); } @@ -273,7 +273,7 @@ protected void handleChatPostFormat(AsyncPlayerChatEvent event) { /** * Run costs for chat and clean up the cached {@link com.earth2me.essentials.chat.processing.ChatProcessingCache.ProcessedChat} */ - protected void handleChatSubmit(AsyncPlayerChatEvent event) { + protected void handleChatSubmit(AbstractChatEvent event) { if (isAborted(event)) { return; } @@ -284,7 +284,7 @@ protected void handleChatSubmit(AsyncPlayerChatEvent event) { cache.clearProcessedChat(event.getPlayer()); } - boolean isAborted(final AsyncPlayerChatEvent event) { + boolean isAborted(final AbstractChatEvent event) { return event.isCancelled(); } @@ -320,7 +320,7 @@ private void charge(final User user, final Trade charge) throws ChargeException charge.charge(user); } - boolean charge(final AsyncPlayerChatEvent event, final ChatProcessingCache.ProcessedChat chat) { + boolean charge(final AbstractChatEvent event, final ChatProcessingCache.ProcessedChat chat) { try { charge(chat.getUser(), chat.getCharge()); } catch (final ChargeException e) { diff --git a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/ChatHandler.java b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/ChatHandler.java index 8da640bd769..eb536545254 100644 --- a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/ChatHandler.java +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/ChatHandler.java @@ -2,6 +2,7 @@ import com.earth2me.essentials.Essentials; import com.earth2me.essentials.chat.EssentialsChat; +import net.ess3.provider.AbstractChatEvent; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.player.AsyncPlayerChatEvent; @@ -19,28 +20,33 @@ public void registerListeners() { pm.registerEvents(new ChatHighest(), essChat); } - private class ChatLowest implements ChatListener { + private AbstractChatEvent wrap(final AsyncPlayerChatEvent event) { + return new SpigotChatEvent(event); + } + + private final class ChatLowest implements ChatListener { @Override @EventHandler(priority = EventPriority.LOWEST) public void onPlayerChat(AsyncPlayerChatEvent event) { - handleChatFormat(event); + handleChatFormat(wrap(event)); } } - private class ChatNormal implements ChatListener { + private final class ChatNormal implements ChatListener { @Override @EventHandler(priority = EventPriority.NORMAL) public void onPlayerChat(AsyncPlayerChatEvent event) { - handleChatRecipients(event); + handleChatRecipients(wrap(event)); } } - private class ChatHighest implements ChatListener { + private final class ChatHighest implements ChatListener { @Override @EventHandler(priority = EventPriority.HIGHEST) public void onPlayerChat(AsyncPlayerChatEvent event) { - handleChatPostFormat(event); - handleChatSubmit(event); + final AbstractChatEvent absEvent = wrap(event); + handleChatPostFormat(absEvent); + handleChatSubmit(absEvent); } } } diff --git a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/PaperChatHandler.java b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/PaperChatHandler.java new file mode 100644 index 00000000000..989816f069e --- /dev/null +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/PaperChatHandler.java @@ -0,0 +1,36 @@ +package com.earth2me.essentials.chat.processing; + +import com.earth2me.essentials.Essentials; +import com.earth2me.essentials.chat.EssentialsChat; +import net.ess3.provider.AbstractChatEvent; +import net.ess3.provider.providers.PaperChatListenerProvider; +import org.bukkit.plugin.PluginManager; + +public class PaperChatHandler extends AbstractChatHandler { + public PaperChatHandler(Essentials ess, EssentialsChat essChat) { + super(ess, essChat); + } + + public void registerListeners() { + final PluginManager pm = essChat.getServer().getPluginManager(); + pm.registerEvents(new ChatListener(), essChat); + } + + public final class ChatListener extends PaperChatListenerProvider { + @Override + public void onChatLowest(AbstractChatEvent event) { + handleChatFormat(event); + } + + @Override + public void onChatNormal(AbstractChatEvent event) { + handleChatRecipients(event); + } + + @Override + public void onChatHighest(AbstractChatEvent event) { + handleChatPostFormat(event); + handleChatSubmit(event); + } + } +} diff --git a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/SpigotChatEvent.java b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/SpigotChatEvent.java new file mode 100644 index 00000000000..04ee58f3768 --- /dev/null +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/SpigotChatEvent.java @@ -0,0 +1,72 @@ +package com.earth2me.essentials.chat.processing; + +import net.ess3.provider.AbstractChatEvent; +import org.bukkit.entity.Player; +import org.bukkit.event.player.AsyncPlayerChatEvent; + +import java.util.Collections; +import java.util.Set; +import java.util.function.Predicate; + +public class SpigotChatEvent implements AbstractChatEvent { + private final AsyncPlayerChatEvent event; + + public SpigotChatEvent(AsyncPlayerChatEvent event) { + this.event = event; + } + + @Override + public boolean isAsynchronous() { + return event.isAsynchronous(); + } + + @Override + public boolean isCancelled() { + return event.isCancelled(); + } + + @Override + public void setCancelled(boolean toCancel) { + event.setCancelled(toCancel); + } + + @Override + public String getFormat() { + return event.getFormat(); + } + + @Override + public void setFormat(String format) { + event.setFormat(format); + } + + @Override + public String getMessage() { + return event.getMessage(); + } + + @Override + public void setMessage(String message) { + event.setMessage(message); + } + + @Override + public Player getPlayer() { + return event.getPlayer(); + } + + @Override + public Set recipients() { + return Collections.unmodifiableSet(event.getRecipients()); + } + + @Override + public void removeRecipients(Predicate predicate) { + event.getRecipients().removeIf(predicate); + } + + @Override + public void addRecipient(Player player) { + event.getRecipients().add(player); + } +} diff --git a/build-logic/src/main/kotlin/constants.kt b/build-logic/src/main/kotlin/constants.kt index 8fa6e50a655..fd903188969 100644 --- a/build-logic/src/main/kotlin/constants.kt +++ b/build-logic/src/main/kotlin/constants.kt @@ -1 +1 @@ -const val RUN_PAPER_MINECRAFT_VERSION = "1.21.1" +const val RUN_PAPER_MINECRAFT_VERSION = "1.21.4" diff --git a/providers/BaseProviders/src/main/java/net/ess3/provider/AbstractChatEvent.java b/providers/BaseProviders/src/main/java/net/ess3/provider/AbstractChatEvent.java new file mode 100644 index 00000000000..19ff03418c0 --- /dev/null +++ b/providers/BaseProviders/src/main/java/net/ess3/provider/AbstractChatEvent.java @@ -0,0 +1,30 @@ +package net.ess3.provider; + +import org.bukkit.entity.Player; + +import java.util.Set; +import java.util.function.Predicate; + +public interface AbstractChatEvent { + boolean isAsynchronous(); + + boolean isCancelled(); + + void setCancelled(boolean toCancel); + + String getFormat(); + + void setFormat(String format); + + String getMessage(); + + void setMessage(String message); + + Player getPlayer(); + + Set recipients(); + + void removeRecipients(Predicate predicate); + + void addRecipient(Player player); +} diff --git a/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperChatEvent.java b/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperChatEvent.java new file mode 100644 index 00000000000..1bb501b5730 --- /dev/null +++ b/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperChatEvent.java @@ -0,0 +1,83 @@ +package net.ess3.provider.providers; + +import io.papermc.paper.event.player.AsyncChatEvent; +import net.ess3.provider.AbstractChatEvent; +import net.kyori.adventure.audience.Audience; +import org.bukkit.entity.Player; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Predicate; + +public class PaperChatEvent implements AbstractChatEvent { + private final AsyncChatEvent event; + private String fakeFormat; + private String fakeMessage; + + public PaperChatEvent(final AsyncChatEvent event) { + this.event = event; + this.fakeMessage = event.signedMessage().message(); + } + + @Override + public boolean isAsynchronous() { + return event.isAsynchronous(); + } + + @Override + public boolean isCancelled() { + return event.isCancelled(); + } + + @Override + public void setCancelled(boolean toCancel) { + event.setCancelled(toCancel); + } + + @Override + public String getFormat() { + return fakeFormat; + } + + @Override + public void setFormat(String format) { + this.fakeFormat = format; + } + + @Override + public String getMessage() { + return fakeMessage; + } + + @Override + public void setMessage(String message) { + this.fakeMessage = message; + } + + @Override + public Player getPlayer() { + return event.getPlayer(); + } + + @Override + public Set recipients() { + final Set recipients = new HashSet<>(); + for (final Audience recipient : event.viewers()) { + if (recipient instanceof Player) { + recipients.add((Player) recipient); + } + } + return Collections.unmodifiableSet(recipients); + } + + @Override + public void removeRecipients(Predicate predicate) { + event.viewers().removeIf(recipient -> recipient instanceof Player && predicate.test((Player) recipient)); + } + + @Override + public void addRecipient(Player player) { + event.viewers().add(player); + } +} diff --git a/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperChatListenerProvider.java b/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperChatListenerProvider.java new file mode 100644 index 00000000000..f54fac33f9c --- /dev/null +++ b/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperChatListenerProvider.java @@ -0,0 +1,76 @@ +package net.ess3.provider.providers; + +import io.papermc.paper.chat.ChatRenderer; +import io.papermc.paper.event.player.AsyncChatEvent; +import net.ess3.provider.AbstractChatEvent; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.flattener.ComponentFlattener; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; + +import java.util.HashMap; + +public abstract class PaperChatListenerProvider implements Listener { + private final LegacyComponentSerializer serializer; + private final HashMap eventMap = new HashMap<>(); + + public PaperChatListenerProvider() { + this.serializer = LegacyComponentSerializer.builder() + .flattener(ComponentFlattener.basic()) + .extractUrls() + .useUnusualXRepeatedCharacterHexFormat().build(); + } + + public abstract void onChatLowest(final AbstractChatEvent event); + + public abstract void onChatNormal(final AbstractChatEvent event); + + public abstract void onChatHighest(final AbstractChatEvent event); + + @EventHandler(priority = EventPriority.LOWEST) + public void onLowest(final AsyncChatEvent event) { + onChatLowest(wrap(event)); + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onNormal(final AsyncChatEvent event) { + onChatNormal(wrap(event)); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onHighest(final AsyncChatEvent event) { + final PaperChatEvent paperChatEvent = wrap(event); + onChatHighest(paperChatEvent); + + final TextComponent format = serializer.deserialize(paperChatEvent.getFormat()); + final TextComponent eventMessage = serializer.deserialize(paperChatEvent.getMessage()); + + if (!event.isCancelled()) { + event.renderer(ChatRenderer.viewerUnaware((player, displayName, message) -> + format.replaceText(builder -> builder + .match("%(\\d)\\$s").replacement((index, match) -> { + if (index.group(1).equals("1")) { + return displayName; + } + return eventMessage; + }) + ))); + } + + eventMap.remove(event); + } + + private PaperChatEvent wrap(final AsyncChatEvent event) { + PaperChatEvent paperChatEvent = eventMap.get(event); + if (paperChatEvent != null) { + return paperChatEvent; + } + + paperChatEvent = new PaperChatEvent(event); + eventMap.put(event, paperChatEvent); + + return paperChatEvent; + } +} From be72e7adf83da79353c6871444db3a8998e82660 Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Wed, 5 Feb 2025 11:37:08 -0500 Subject: [PATCH 2/3] Fix link clicking in /broadcast --- .../main/java/com/earth2me/essentials/utils/AdventureUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/AdventureUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/AdventureUtil.java index aa5cd68d595..ed377c2b39b 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/AdventureUtil.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/AdventureUtil.java @@ -20,6 +20,7 @@ public final class AdventureUtil { static { final LegacyComponentSerializer.Builder builder = LegacyComponentSerializer.builder() .flattener(ComponentFlattener.basic()) + .extractUrls() .useUnusualXRepeatedCharacterHexFormat(); if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_16_1_R01)) { builder.hexColors(); From 3bd2acbe046b864c5cf716b4a051448e445dc6b0 Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Wed, 5 Feb 2025 11:48:51 -0500 Subject: [PATCH 3/3] use more strict pattern for URL extraction --- .../java/com/earth2me/essentials/utils/AdventureUtil.java | 3 ++- .../java/com/earth2me/essentials/utils/FormatUtil.java | 8 ++++---- .../main/java/net/ess3/provider/AbstractChatEvent.java | 3 +++ .../provider/providers/PaperChatListenerProvider.java | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/AdventureUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/AdventureUtil.java index ed377c2b39b..e71cad80548 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/AdventureUtil.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/AdventureUtil.java @@ -1,6 +1,7 @@ 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; @@ -20,7 +21,7 @@ public final class AdventureUtil { static { final LegacyComponentSerializer.Builder builder = LegacyComponentSerializer.builder() .flattener(ComponentFlattener.basic()) - .extractUrls() + .extractUrls(AbstractChatEvent.URL_PATTERN) .useUnusualXRepeatedCharacterHexFormat(); if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_16_1_R01)) { builder.hexColors(); 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 e911e3a8fda..02c6dc1d817 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/FormatUtil.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/FormatUtil.java @@ -1,6 +1,7 @@ package com.earth2me.essentials.utils; import net.ess3.api.IUser; +import net.ess3.provider.AbstractChatEvent; import org.bukkit.ChatColor; import org.bukkit.Color; @@ -24,7 +25,6 @@ public final class FormatUtil { private static final Pattern REPLACE_ALL_RGB_PATTERN = Pattern.compile("(&)?&#([0-9a-fA-F]{6})"); //Used to prepare xmpp output private static final Pattern LOGCOLOR_PATTERN = Pattern.compile("\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]"); - private static final Pattern URL_PATTERN = Pattern.compile("((?:(?:https?)://)?[\\w-_\\.]{2,})\\.([a-zA-Z]{2,3}(?:/\\S+)?)"); //Used to strip ANSI control codes from console private static final Pattern ANSI_CONTROL_PATTERN = Pattern.compile("[\\x1B\\x9B][\\[\\]()#;?]*(?:(?:(?:;[-a-zA-Z\\d/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d/#&.:=?%@~_]*)*)?\\x07|(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~])"); private static final Pattern PAPER_CONTROL_PATTERN = Pattern.compile("(?i)" + (char) 0x7f + "[0-9A-FK-ORX]"); @@ -297,9 +297,9 @@ static String blockURL(final String input) { if (input == null) { return null; } - String text = URL_PATTERN.matcher(input).replaceAll("$1 $2"); - while (URL_PATTERN.matcher(text).find()) { - text = URL_PATTERN.matcher(text).replaceAll("$1 $2"); + String text = AbstractChatEvent.URL_PATTERN.matcher(input).replaceAll("$1 $2"); + while (AbstractChatEvent.URL_PATTERN.matcher(text).find()) { + text = AbstractChatEvent.URL_PATTERN.matcher(text).replaceAll("$1 $2"); } return text; } diff --git a/providers/BaseProviders/src/main/java/net/ess3/provider/AbstractChatEvent.java b/providers/BaseProviders/src/main/java/net/ess3/provider/AbstractChatEvent.java index 19ff03418c0..9401367443c 100644 --- a/providers/BaseProviders/src/main/java/net/ess3/provider/AbstractChatEvent.java +++ b/providers/BaseProviders/src/main/java/net/ess3/provider/AbstractChatEvent.java @@ -4,8 +4,11 @@ import java.util.Set; import java.util.function.Predicate; +import java.util.regex.Pattern; public interface AbstractChatEvent { + Pattern URL_PATTERN = Pattern.compile("((?:(?:https?)://)?[\\w-_\\.]{2,})\\.([a-zA-Z]{2,3}(?:/\\S+)?)"); + boolean isAsynchronous(); boolean isCancelled(); diff --git a/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperChatListenerProvider.java b/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperChatListenerProvider.java index f54fac33f9c..b624eea268a 100644 --- a/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperChatListenerProvider.java +++ b/providers/PaperProvider/src/main/java/net/ess3/provider/providers/PaperChatListenerProvider.java @@ -19,7 +19,7 @@ public abstract class PaperChatListenerProvider implements Listener { public PaperChatListenerProvider() { this.serializer = LegacyComponentSerializer.builder() .flattener(ComponentFlattener.basic()) - .extractUrls() + .extractUrls(AbstractChatEvent.URL_PATTERN) .useUnusualXRepeatedCharacterHexFormat().build(); }