From cb00783ede30e15ee5f93b28edfb01dff2f23669 Mon Sep 17 00:00:00 2001 From: Josh Roy <10731363+JRoy@users.noreply.github.com> Date: Fri, 14 Feb 2025 15:14:44 -0500 Subject: [PATCH] Add support for Paper's chat events (#6033) Fixes #4970 Fixes #5887 --- .../essentials/utils/AdventureUtil.java | 2 + .../earth2me/essentials/utils/FormatUtil.java | 8 +- .../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 | 33 ++++++++ .../provider/providers/PaperChatEvent.java | 83 +++++++++++++++++++ .../providers/PaperChatListenerProvider.java | 76 +++++++++++++++++ 11 files changed, 362 insertions(+), 47 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/Essentials/src/main/java/com/earth2me/essentials/utils/AdventureUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/AdventureUtil.java index aa5cd68d595..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,6 +21,7 @@ public final class AdventureUtil { 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(); 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/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
* 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