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();
}