From 43a6e9e19cec7cc8198675dafbf969fbb4051bb5 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/Adventure.patch | 107 +++++++++++++++++- Spigot-Server-Patches/Adventure.patch | 31 +++-- ...nilla-per-world-scoreboard-coloring-.patch | 2 +- 3 files changed, 118 insertions(+), 22 deletions(-) diff --git a/Spigot-API-Patches/Adventure.patch b/Spigot-API-Patches/Adventure.patch index cc36348f01..c7929797a0 100644 --- a/Spigot-API-Patches/Adventure.patch +++ b/Spigot-API-Patches/Adventure.patch @@ -94,6 +94,36 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + // Paper end } +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..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/chat/ChatComposer.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.chat; ++ ++import net.kyori.adventure.text.Component; ++import org.bukkit.entity.Player; ++import org.jetbrains.annotations.NotNull; ++ ++/** ++ * 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..0000000000000000000000000000000000000000 @@ -108,10 +138,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + +/** + * 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,6 +154,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + * @param message the chat message + * @return a formatted chat message + */ ++ @Deprecated + @NotNull + Component chat(final @NotNull Component displayName, final @NotNull Component message); +} @@ -132,24 +166,36 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ +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..00000000000000000000000000000000 + } + + /** ++ * 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..00000000000000000000000000000000 + * + * @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..00000000000000000000000000000000 + * @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 @@ -233,6 +312,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ +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..00000000000000000000000000000000 +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); + } @@ -269,6 +357,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ +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..00000000000000000000000000000000 +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/Adventure.patch b/Spigot-Server-Patches/Adventure.patch index 84d29a978e..572c4fe2d3 100644 --- a/Spigot-Server-Patches/Adventure.patch +++ b/Spigot-Server-Patches/Adventure.patch @@ -112,7 +112,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ +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..00000000000000000000000000000000 +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..00000000000000000000000000000000 + // 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..00000000000000000000000000000000 + // 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..00000000000000000000000000000000 + // 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..00000000000000000000000000000000 + } + } + -+ 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..00000000000000000000000000000000 + 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..00000000000000000000000000000000 + + 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..00000000000000000000000000000000 + } + } + -+ 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..00000000000000000000000000000000 + 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/Option-to-use-vanilla-per-world-scoreboard-coloring-.patch b/Spigot-Server-Patches/Option-to-use-vanilla-per-world-scoreboard-coloring-.patch index a4398b4e27..f32e2e0813 100644 --- a/Spigot-Server-Patches/Option-to-use-vanilla-per-world-scoreboard-coloring-.patch +++ b/Spigot-Server-Patches/Option-to-use-vanilla-per-world-scoreboard-coloring-.patch @@ -29,7 +29,7 @@ diff --git a/src/main/java/io/papermc/paper/adventure/ChatProcessor.java b/src/m index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/io/papermc/paper/adventure/ChatProcessor.java +++ b/src/main/java/io/papermc/paper/adventure/ChatProcessor.java -@@ -0,0 +0,0 @@ import net.kyori.adventure.text.format.TextDecoration; +@@ -0,0 +0,0 @@ import net.kyori.adventure.text.event.ClickEvent; import net.minecraft.network.chat.IChatBaseComponent; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.EntityPlayer;