From de94f64850fa9f5915c976ab65c6fd9baee749f2 Mon Sep 17 00:00:00 2001 From: Riley Park Date: Sun, 21 Mar 2021 13:47:13 -0700 Subject: [PATCH] Refactor chat message composition (#5396) fixes #5395 --- Spigot-API-Patches/0005-Adventure.patch | 129 +++++++++++++++--- Spigot-Server-Patches/0010-Adventure.patch | 35 +++-- ...nilla-per-world-scoreboard-coloring-.patch | 6 +- 3 files changed, 133 insertions(+), 37 deletions(-) diff --git a/Spigot-API-Patches/0005-Adventure.patch b/Spigot-API-Patches/0005-Adventure.patch index 59c83d572..00f17036b 100644 --- a/Spigot-API-Patches/0005-Adventure.patch +++ b/Spigot-API-Patches/0005-Adventure.patch @@ -7,7 +7,7 @@ Co-authored-by: zml Co-authored-by: Jake Potrebic diff --git a/pom.xml b/pom.xml -index 12306d830c6889c2c9b12699abebe0411262aef6..9e9aa8efe9f2d74f32c22dbd3cc5ed6dedf2ad1a 100644 +index e3744ee042e0426a513c4fdf4abedafe85e31cd2..75b2830340051deb0fa39149e80872d2b88ed6f0 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,39 @@ @@ -94,11 +94,11 @@ index ef58a6c00f444bd498a2d8fc4e457236f393954f..ecd149157d4fb80444f34bf5633d74bc + } + // Paper end } -diff --git a/src/main/java/io/papermc/paper/chat/ChatFormatter.java b/src/main/java/io/papermc/paper/chat/ChatFormatter.java +diff --git a/src/main/java/io/papermc/paper/chat/ChatComposer.java b/src/main/java/io/papermc/paper/chat/ChatComposer.java new file mode 100644 -index 0000000000000000000000000000000000000000..9873a97d472e000d2d996fc2b6a46db25cdd397b +index 0000000000000000000000000000000000000000..1f03ce9ff40ed12a1825c8e24dabddbbef44d6af --- /dev/null -+++ b/src/main/java/io/papermc/paper/chat/ChatFormatter.java ++++ b/src/main/java/io/papermc/paper/chat/ChatComposer.java @@ -0,0 +1,24 @@ +package io.papermc.paper.chat; + @@ -107,11 +107,44 @@ index 0000000000000000000000000000000000000000..9873a97d472e000d2d996fc2b6a46db2 +import org.jetbrains.annotations.NotNull; + +/** -+ * A chat formatter is responsible for the formatting of chat messages sent by {@link Player}s to the server. ++ * A chat composer is responsible for composing chat messages sent by {@link Player}s to the server. + */ +@FunctionalInterface ++public interface ChatComposer { ++ ChatComposer DEFAULT = (player, displayName, message) -> Component.translatable("chat.type.text", displayName, message); ++ ++ /** ++ * Composes a chat message. ++ * ++ * @param source the message source ++ * @param displayName the display name of the {@link Player} sending the message ++ * @param message the chat message ++ * @return a composed chat message ++ */ ++ @NotNull ++ Component composeChat(final @NotNull Player source, final @NotNull Component displayName, final @NotNull Component message); ++} +diff --git a/src/main/java/io/papermc/paper/chat/ChatFormatter.java b/src/main/java/io/papermc/paper/chat/ChatFormatter.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ae811175089009be6b1db6941e9c5a24b2b1f027 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/chat/ChatFormatter.java +@@ -0,0 +1,28 @@ ++package io.papermc.paper.chat; ++ ++import net.kyori.adventure.text.Component; ++import org.bukkit.entity.Player; ++import org.jetbrains.annotations.NotNull; ++ ++/** ++ * A chat formatter is responsible for the formatting of chat messages sent by {@link Player}s to the server. ++ * ++ * @deprecated in favour of {@link ChatComposer} ++ */ ++@Deprecated ++@FunctionalInterface +public interface ChatFormatter { -+ // This format might be different than the CraftBukkit one? ++ @Deprecated + ChatFormatter DEFAULT = (displayName, message) -> Component.translatable("chat.type.text", displayName, message); + + /** @@ -121,35 +154,48 @@ index 0000000000000000000000000000000000000000..9873a97d472e000d2d996fc2b6a46db2 + * @param message the chat message + * @return a formatted chat message + */ ++ @Deprecated + @NotNull + Component chat(final @NotNull Component displayName, final @NotNull Component message); +} diff --git a/src/main/java/io/papermc/paper/event/player/AbstractChatEvent.java b/src/main/java/io/papermc/paper/event/player/AbstractChatEvent.java new file mode 100644 -index 0000000000000000000000000000000000000000..9cac60a004a71963221dc4e5fc595eeba3055be7 +index 0000000000000000000000000000000000000000..f77597786f201b57ac18e14099f7b84f1e4e4cf3 --- /dev/null +++ b/src/main/java/io/papermc/paper/event/player/AbstractChatEvent.java -@@ -0,0 +1,95 @@ +@@ -0,0 +1,140 @@ +package io.papermc.paper.event.player; + ++import io.papermc.paper.chat.ChatComposer; +import io.papermc.paper.chat.ChatFormatter; -+import java.util.Objects; +import java.util.Set; +import net.kyori.adventure.text.Component; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.player.PlayerEvent; ++import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; + ++import static java.util.Objects.requireNonNull; ++ +/** + * An abstract implementation of a chat event, handling shared logic. + */ +public abstract class AbstractChatEvent extends PlayerEvent implements Cancellable { + private final Set recipients; + private boolean cancelled = false; -+ private ChatFormatter formatter; ++ private ChatComposer composer; ++ @Deprecated private @Nullable ChatFormatter formatter; + private Component message; + ++ AbstractChatEvent(final boolean async, final @NotNull Player player, final @NotNull Set recipients, final @NotNull ChatComposer composer, final @NotNull Component message) { ++ super(player, async); ++ this.recipients = recipients; ++ this.composer = composer; ++ this.message = message; ++ } ++ ++ @Deprecated + AbstractChatEvent(final boolean async, final @NotNull Player player, final @NotNull Set recipients, final @NotNull ChatFormatter formatter, final @NotNull Component message) { + super(player, async); + this.recipients = recipients; @@ -176,12 +222,42 @@ index 0000000000000000000000000000000000000000..9cac60a004a71963221dc4e5fc595eeb + } + + /** ++ * Gets the chat composer. ++ * ++ * @return the chat composer ++ */ ++ @NotNull ++ public final ChatComposer composer() { ++ if(this.composer == null) { ++ requireNonNull(this.formatter, "composer and formatter"); ++ this.composer = (source, displayName, message) -> this.formatter.chat(displayName, message); ++ } ++ return this.composer; ++ } ++ ++ /** ++ * Sets the chat composer. ++ * ++ * @param composer the chat composer ++ * @throws NullPointerException if {@code composer} is {@code null} ++ */ ++ public final void composer(final @NotNull ChatComposer composer) { ++ this.composer = requireNonNull(composer, "composer"); ++ this.formatter = null; ++ } ++ ++ /** + * Gets the chat formatter. + * + * @return the chat formatter ++ * @deprecated in favour of {@link #composer()} + */ ++ @Deprecated + @NotNull + public final ChatFormatter formatter() { ++ if(this.formatter == null) { ++ this.formatter = (displayName, message) -> this.composer.composeChat(this.player, displayName, message); ++ } + return this.formatter; + } + @@ -190,9 +266,12 @@ index 0000000000000000000000000000000000000000..9cac60a004a71963221dc4e5fc595eeb + * + * @param formatter the chat formatter + * @throws NullPointerException if {@code formatter} is {@code null} ++ * @deprecated in favour of {@link #composer(ChatComposer)} + */ ++ @Deprecated + public final void formatter(final @NotNull ChatFormatter formatter) { -+ this.formatter = Objects.requireNonNull(formatter, "formatter"); ++ this.formatter = requireNonNull(formatter, "formatter"); ++ this.composer = (source, displayName, message) -> formatter.chat(displayName, message); + } + + /** @@ -212,7 +291,7 @@ index 0000000000000000000000000000000000000000..9cac60a004a71963221dc4e5fc595eeb + * @throws NullPointerException if {@code message} is {@code null} + */ + public final void message(final @NotNull Component message) { -+ this.message = Objects.requireNonNull(message, "message"); ++ this.message = requireNonNull(message, "message"); + } + + @Override @@ -227,12 +306,13 @@ index 0000000000000000000000000000000000000000..9cac60a004a71963221dc4e5fc595eeb +} diff --git a/src/main/java/io/papermc/paper/event/player/AsyncChatEvent.java b/src/main/java/io/papermc/paper/event/player/AsyncChatEvent.java new file mode 100644 -index 0000000000000000000000000000000000000000..baaa0451fec6b2d9a601f355984adce6c74d11f6 +index 0000000000000000000000000000000000000000..a0f748957f4472103dd27fc95a711a42de7fae89 --- /dev/null +++ b/src/main/java/io/papermc/paper/event/player/AsyncChatEvent.java -@@ -0,0 +1,30 @@ +@@ -0,0 +1,39 @@ +package io.papermc.paper.event.player; + ++import io.papermc.paper.chat.ChatComposer; +import io.papermc.paper.chat.ChatFormatter; +import java.util.Set; +import net.kyori.adventure.text.Component; @@ -246,6 +326,14 @@ index 0000000000000000000000000000000000000000..baaa0451fec6b2d9a601f355984adce6 +public final class AsyncChatEvent extends AbstractChatEvent { + private static final HandlerList HANDLERS = new HandlerList(); + ++ public AsyncChatEvent(final boolean async, final @NotNull Player player, final @NotNull Set recipients, final @NotNull ChatComposer composer, final @NotNull Component message) { ++ super(async, player, recipients, composer, message); ++ } ++ ++ /** ++ * @deprecated use {@link #AsyncChatEvent(boolean, Player, Set, ChatComposer, Component)} ++ */ ++ @Deprecated + public AsyncChatEvent(final boolean async, final @NotNull Player player, final @NotNull Set recipients, final @NotNull ChatFormatter formatter, final @NotNull Component message) { + super(async, player, recipients, formatter, message); + } @@ -263,12 +351,13 @@ index 0000000000000000000000000000000000000000..baaa0451fec6b2d9a601f355984adce6 +} diff --git a/src/main/java/io/papermc/paper/event/player/ChatEvent.java b/src/main/java/io/papermc/paper/event/player/ChatEvent.java new file mode 100644 -index 0000000000000000000000000000000000000000..04ee20906e104e15d730899477ae983d3255cb18 +index 0000000000000000000000000000000000000000..13c5df5fb8ce1d0203d99e88dd691019146a8f52 --- /dev/null +++ b/src/main/java/io/papermc/paper/event/player/ChatEvent.java -@@ -0,0 +1,35 @@ +@@ -0,0 +1,44 @@ +package io.papermc.paper.event.player; + ++import io.papermc.paper.chat.ChatComposer; +import io.papermc.paper.chat.ChatFormatter; +import java.util.Set; +import net.kyori.adventure.text.Component; @@ -287,6 +376,14 @@ index 0000000000000000000000000000000000000000..04ee20906e104e15d730899477ae983d +public final class ChatEvent extends AbstractChatEvent { + private static final HandlerList HANDLERS = new HandlerList(); + ++ public ChatEvent(final @NotNull Player player, final @NotNull Set recipients, final @NotNull ChatComposer composer, final @NotNull Component message) { ++ super(false, player, recipients, composer, message); ++ } ++ ++ /** ++ * @deprecated use {@link #ChatEvent(Player, Set, ChatComposer, Component)} ++ */ ++ @Deprecated + public ChatEvent(final @NotNull Player player, final @NotNull Set recipients, final @NotNull ChatFormatter formatter, final @NotNull Component message) { + super(false, player, recipients, formatter, message); + } diff --git a/Spigot-Server-Patches/0010-Adventure.patch b/Spigot-Server-Patches/0010-Adventure.patch index f8a378d6c..df04b0db8 100644 --- a/Spigot-Server-Patches/0010-Adventure.patch +++ b/Spigot-Server-Patches/0010-Adventure.patch @@ -106,13 +106,13 @@ index 0000000000000000000000000000000000000000..dd018bb835e1b9fb2496404e1e5b1038 +} diff --git a/src/main/java/io/papermc/paper/adventure/ChatProcessor.java b/src/main/java/io/papermc/paper/adventure/ChatProcessor.java new file mode 100644 -index 0000000000000000000000000000000000000000..e1c5376efccbb1432b17cc60797a7bbe2871a470 +index 0000000000000000000000000000000000000000..ed00e5e825856e790da467f429d026772e6303bd --- /dev/null +++ b/src/main/java/io/papermc/paper/adventure/ChatProcessor.java -@@ -0,0 +1,213 @@ +@@ -0,0 +1,212 @@ +package io.papermc.paper.adventure; + -+import io.papermc.paper.chat.ChatFormatter; ++import io.papermc.paper.chat.ChatComposer; +import io.papermc.paper.event.player.AbstractChatEvent; +import io.papermc.paper.event.player.AsyncChatEvent; +import io.papermc.paper.event.player.ChatEvent; @@ -123,8 +123,6 @@ index 0000000000000000000000000000000000000000..e1c5376efccbb1432b17cc60797a7bbe +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextReplacementConfig; +import net.kyori.adventure.text.event.ClickEvent; -+import net.kyori.adventure.text.format.Style; -+import net.kyori.adventure.text.format.TextDecoration; +import net.minecraft.network.chat.IChatBaseComponent; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.EntityPlayer; @@ -171,7 +169,7 @@ index 0000000000000000000000000000000000000000..e1c5376efccbb1432b17cc60797a7bbe + // continuing from AsyncPlayerChatEvent (without PlayerChatEvent) + event -> { + this.processModern( -+ legacyFormatter(event.getFormat(), legacyDisplayName((CraftPlayer) event.getPlayer()), event.getMessage()), ++ legacyComposer(event.getFormat(), legacyDisplayName((CraftPlayer) event.getPlayer()), event.getMessage()), + event.getRecipients(), + PaperAdventure.LEGACY_SECTION_UXRC.deserialize(event.getMessage()), + event.isCancelled() @@ -180,7 +178,7 @@ index 0000000000000000000000000000000000000000..e1c5376efccbb1432b17cc60797a7bbe + // continuing from AsyncPlayerChatEvent and PlayerChatEvent + event -> { + this.processModern( -+ legacyFormatter(event.getFormat(), legacyDisplayName((CraftPlayer) event.getPlayer()), event.getMessage()), ++ legacyComposer(event.getFormat(), legacyDisplayName((CraftPlayer) event.getPlayer()), event.getMessage()), + event.getRecipients(), + PaperAdventure.LEGACY_SECTION_UXRC.deserialize(event.getMessage()), + event.isCancelled() @@ -189,7 +187,7 @@ index 0000000000000000000000000000000000000000..e1c5376efccbb1432b17cc60797a7bbe + // no legacy events called, all nice and fresh! + () -> { + this.processModern( -+ ChatFormatter.DEFAULT, ++ ChatComposer.DEFAULT, + new LazyPlayerSet(this.server), + Component.text(this.message).replaceText(URL_REPLACEMENT_CONFIG), + false @@ -229,8 +227,8 @@ index 0000000000000000000000000000000000000000..e1c5376efccbb1432b17cc60797a7bbe + } + } + -+ private void processModern(final ChatFormatter formatter, final Set recipients, final Component message, final boolean cancelled) { -+ final AsyncChatEvent ae = this.createAsync(formatter, recipients, message); ++ private void processModern(final ChatComposer composer, final Set recipients, final Component message, final boolean cancelled) { ++ final AsyncChatEvent ae = this.createAsync(composer, recipients, message); + ae.setCancelled(cancelled); // propagate cancelled state + post(ae); + final boolean listenersOnSyncEvent = anyListeners(ChatEvent.getHandlerList()); @@ -245,7 +243,7 @@ index 0000000000000000000000000000000000000000..e1c5376efccbb1432b17cc60797a7bbe + this.queueIfAsyncOrRunImmediately(new Waitable() { + @Override + protected Void evaluate() { -+ final ChatEvent se = ChatProcessor.this.createSync(ae.formatter(), ae.recipients(), ae.message()); ++ final ChatEvent se = ChatProcessor.this.createSync(ae.composer(), ae.recipients(), ae.message()); + se.setCancelled(ae.isCancelled()); // propagate cancelled state + post(se); + ChatProcessor.this.complete(se); @@ -261,7 +259,8 @@ index 0000000000000000000000000000000000000000..e1c5376efccbb1432b17cc60797a7bbe + + final CraftPlayer player = this.player.getBukkitEntity(); + -+ final Component message = event.formatter().chat( ++ final Component message = event.composer().composeChat( ++ event.getPlayer(), + displayName(player), + event.message() + ); @@ -280,12 +279,12 @@ index 0000000000000000000000000000000000000000..e1c5376efccbb1432b17cc60797a7bbe + } + } + -+ private AsyncChatEvent createAsync(final ChatFormatter formatter, final Set recipients, final Component message) { -+ return new AsyncChatEvent(this.async, this.player.getBukkitEntity(), recipients, formatter, message); ++ private AsyncChatEvent createAsync(final ChatComposer composer, final Set recipients, final Component message) { ++ return new AsyncChatEvent(this.async, this.player.getBukkitEntity(), recipients, composer, message); + } + -+ private ChatEvent createSync(final ChatFormatter formatter, final Set recipients, final Component message) { -+ return new ChatEvent(this.player.getBukkitEntity(), recipients, formatter, message); ++ private ChatEvent createSync(final ChatComposer composer, final Set recipients, final Component message) { ++ return new ChatEvent(this.player.getBukkitEntity(), recipients, composer, message); + } + + private static String legacyDisplayName(final CraftPlayer player) { @@ -296,8 +295,8 @@ index 0000000000000000000000000000000000000000..e1c5376efccbb1432b17cc60797a7bbe + return player.displayName(); + } + -+ private static ChatFormatter legacyFormatter(final String format, final String legacyDisplayName, final String legacyMessage) { -+ return (displayName, message) -> PaperAdventure.LEGACY_SECTION_UXRC.deserialize(String.format(format, legacyDisplayName, legacyMessage)).replaceText(URL_REPLACEMENT_CONFIG); ++ private static ChatComposer legacyComposer(final String format, final String legacyDisplayName, final String legacyMessage) { ++ return (player, displayName, message) -> PaperAdventure.LEGACY_SECTION_UXRC.deserialize(String.format(format, legacyDisplayName, legacyMessage)).replaceText(URL_REPLACEMENT_CONFIG); + } + + private void queueIfAsyncOrRunImmediately(final Waitable waitable) { diff --git a/Spigot-Server-Patches/0083-Option-to-use-vanilla-per-world-scoreboard-coloring-.patch b/Spigot-Server-Patches/0083-Option-to-use-vanilla-per-world-scoreboard-coloring-.patch index d2234cc77..6e96267ac 100644 --- a/Spigot-Server-Patches/0083-Option-to-use-vanilla-per-world-scoreboard-coloring-.patch +++ b/Spigot-Server-Patches/0083-Option-to-use-vanilla-per-world-scoreboard-coloring-.patch @@ -26,10 +26,10 @@ index db2dddd12f54e6d15916c4cee623676541de37fb..1942f5224aaebb18adb591d6f70a419c + } } diff --git a/src/main/java/io/papermc/paper/adventure/ChatProcessor.java b/src/main/java/io/papermc/paper/adventure/ChatProcessor.java -index ee377d5bd8646733968f4085f60150f8ff6a4a5c..f68b8c921cc07764ce78547b78689e8b5b68320c 100644 +index ed00e5e825856e790da467f429d026772e6303bd..472adbd2aa2a9e1d8d481749ba92c3a290a5fa36 100644 --- a/src/main/java/io/papermc/paper/adventure/ChatProcessor.java +++ b/src/main/java/io/papermc/paper/adventure/ChatProcessor.java -@@ -16,7 +16,11 @@ import net.kyori.adventure.text.format.TextDecoration; +@@ -14,7 +14,11 @@ import net.kyori.adventure.text.event.ClickEvent; import net.minecraft.network.chat.IChatBaseComponent; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.EntityPlayer; @@ -41,7 +41,7 @@ index ee377d5bd8646733968f4085f60150f8ff6a4a5c..f68b8c921cc07764ce78547b78689e8b import org.bukkit.craftbukkit.entity.CraftPlayer; import org.bukkit.craftbukkit.util.LazyPlayerSet; import org.bukkit.craftbukkit.util.Waitable; -@@ -177,10 +181,22 @@ public final class ChatProcessor { +@@ -176,10 +180,22 @@ public final class ChatProcessor { } private static String legacyDisplayName(final CraftPlayer player) {