ChatProcessor fixes (#8690)

Fixes handling for `ForwardingAudience.Single` and passes the signed message to non-native `Audience` types
This commit is contained in:
Jason Penilla 2022-12-16 23:05:48 -07:00
parent 968a3ee539
commit 1de7f28233
2 changed files with 117 additions and 24 deletions

View file

@ -257,22 +257,27 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
@@ -0,0 +0,0 @@
+package io.papermc.paper.adventure;
+
+import com.google.common.base.Suppliers;
+import io.papermc.paper.chat.ChatRenderer;
+import io.papermc.paper.event.player.AbstractChatEvent;
+import io.papermc.paper.event.player.AsyncChatEvent;
+import io.papermc.paper.event.player.ChatEvent;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.audience.MessageType;
+import net.kyori.adventure.audience.ForwardingAudience;
+import net.kyori.adventure.key.Key;
+import net.kyori.adventure.text.Component;
+import net.minecraft.Util;
+import net.minecraft.core.registries.Registries;
+import net.minecraft.network.chat.ChatDecorator;
+import net.minecraft.network.chat.ChatType;
+import net.minecraft.network.chat.OutgoingChatMessage;
@ -280,7 +285,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerPlayer;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.ConsoleCommandSender;
+import org.bukkit.craftbukkit.entity.CraftPlayer;
+import org.bukkit.craftbukkit.util.LazyPlayerSet;
@ -524,39 +528,79 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ final class ViewersOutgoingChat implements OutgoingChat {
+ @Override
+ public void sendFormatChangedViewerAware(CraftPlayer player, Component displayName, Component message, ChatRenderer renderer, Set<Audience> viewers, ChatType.Bound chatType) {
+ this.broadcastToViewers(viewers, player, chatType, v -> PaperAdventure.asVanilla(renderer.render(player, displayName, message, v)));
+ this.broadcastToViewers(viewers, chatType, v -> PaperAdventure.asVanilla(renderer.render(player, displayName, message, v)));
+ }
+
+ @Override
+ public void sendMessageChanged(CraftPlayer player, net.minecraft.network.chat.Component renderedMessage, Set<Audience> viewers, ChatType.Bound chatType) {
+ this.broadcastToViewers(viewers, player, chatType, new ConstantFunction(renderedMessage));
+ this.broadcastToViewers(viewers, chatType, $ -> renderedMessage);
+ }
+
+ @Override
+ public void sendOriginal(CraftPlayer player, Set<Audience> viewers, ChatType.Bound chatType) {
+ this.broadcastToViewers(viewers, player, chatType, null);
+ this.broadcastToViewers(viewers, chatType, null);
+ }
+
+ private void broadcastToViewers(Collection<Audience> viewers, final Player source, final ChatType.Bound chatType, final @Nullable Function<Audience, net.minecraft.network.chat.Component> msgFunction) {
+ final Supplier<Component> fallbackSupplier = Suppliers.memoize(() -> PaperAdventure.asAdventure(msgFunction instanceof ConstantFunction constantFunction ? constantFunction.component : ChatProcessor.this.message.decoratedContent()));
+ final Function<Audience, Component> audienceMsgFunction = !(msgFunction instanceof ConstantFunction || msgFunction == null) ? msgFunction.andThen(PaperAdventure::asAdventure) : viewer -> fallbackSupplier.get();
+ private void broadcastToViewers(Collection<Audience> viewers, final ChatType.Bound chatType, final @Nullable Function<Audience, net.minecraft.network.chat.Component> msgFunction) {
+ for (Audience viewer : viewers) {
+ if (viewer instanceof Player || viewer instanceof ConsoleCommandSender) {
+ // players and console have builtin PlayerChatMessage sending support while other audiences do not
+ this.sendToViewer((CommandSender) viewer, chatType, msgFunction);
+ if (acceptsNative(viewer)) {
+ this.sendNative(viewer, chatType, msgFunction);
+ } else {
+ viewer.sendMessage(source, audienceMsgFunction.apply(viewer), MessageType.CHAT);
+ final net.minecraft.network.chat.@Nullable Component unsigned = Util.mapNullable(msgFunction, f -> f.apply(viewer));
+ final PlayerChatMessage msg = unsigned == null ? ChatProcessor.this.message : ChatProcessor.this.message.withUnsignedContent(unsigned);
+ viewer.sendMessage(msg.adventureView(), this.adventure(chatType));
+ }
+ }
+ }
+
+ private void sendToViewer(final CommandSender viewer, final ChatType.Bound chatType, final @Nullable Function<Audience, net.minecraft.network.chat.Component> msgFunction) {
+ private static final Map<String, net.kyori.adventure.chat.ChatType> BUILT_IN_CHAT_TYPES = Util.make(() -> {
+ final Map<String, net.kyori.adventure.chat.ChatType> map = new HashMap<>();
+ for (final Field declaredField : net.kyori.adventure.chat.ChatType.class.getDeclaredFields()) {
+ if (Modifier.isStatic(declaredField.getModifiers()) && declaredField.getType().equals(ChatType.class)) {
+ try {
+ final net.kyori.adventure.chat.ChatType type = (net.kyori.adventure.chat.ChatType) declaredField.get(null);
+ map.put(type.key().asString(), type);
+ } catch (final ReflectiveOperationException ignore) {
+ }
+ }
+ }
+ return map;
+ });
+
+ private net.kyori.adventure.chat.ChatType.Bound adventure(ChatType.Bound chatType) {
+ final String stringKey = Objects.requireNonNull(
+ ChatProcessor.this.server.registryAccess().registryOrThrow(Registries.CHAT_TYPE).getKey(chatType.chatType()),
+ () -> "No key for '%s' in CHAT_TYPE registry.".formatted(chatType)
+ ).toString();
+ net.kyori.adventure.chat.@Nullable ChatType adventure = BUILT_IN_CHAT_TYPES.get(stringKey);
+ if (adventure == null) {
+ adventure = net.kyori.adventure.chat.ChatType.chatType(Key.key(stringKey));
+ }
+ return adventure.bind(
+ PaperAdventure.asAdventure(chatType.name()),
+ PaperAdventure.asAdventure(chatType.targetName())
+ );
+ }
+
+ private static boolean acceptsNative(final Audience viewer) {
+ if (viewer instanceof Player || viewer instanceof ConsoleCommandSender) {
+ return true;
+ }
+ if (viewer instanceof ForwardingAudience.Single single) {
+ return acceptsNative(single.audience());
+ }
+ return false;
+ }
+
+ private void sendNative(final Audience viewer, final ChatType.Bound chatType, final @Nullable Function<Audience, net.minecraft.network.chat.Component> msgFunction) {
+ if (viewer instanceof ConsoleCommandSender) {
+ this.sendToServer(chatType, msgFunction);
+ } else if (viewer instanceof CraftPlayer craftPlayer) {
+ craftPlayer.getHandle().sendChatMessage(ChatProcessor.this.outgoing, ChatProcessor.this.player.shouldFilterMessageTo(craftPlayer.getHandle()), chatType, Util.mapNullable(msgFunction, f -> f.apply(viewer)));
+ } else if (viewer instanceof ForwardingAudience.Single single) {
+ this.sendNative(single.audience(), chatType, msgFunction);
+ } else {
+ throw new IllegalStateException("Should only be a Player or Console");
+ throw new IllegalStateException("Should only be a Player or Console or ForwardingAudience.Single pointing to one!");
+ }
+ }
+
@ -564,13 +608,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ final PlayerChatMessage toConsoleMessage = msgFunction == null ? ChatProcessor.this.message : ChatProcessor.this.message.withUnsignedContent(msgFunction.apply(ChatProcessor.this.server.console));
+ ChatProcessor.this.server.logChatMessage(toConsoleMessage.decoratedContent(), chatType, ChatProcessor.this.server.getPlayerList().verifyChatTrusted(toConsoleMessage) ? null : "Not Secure");
+ }
+
+ record ConstantFunction(net.minecraft.network.chat.Component component) implements Function<Audience, net.minecraft.network.chat.Component> {
+ @Override
+ public net.minecraft.network.chat.Component apply(Audience audience) {
+ return this.component;
+ }
+ }
+ }
+
+ private Set<Audience> viewersFromLegacy(final Set<Player> recipients) {
@ -1669,6 +1706,25 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
MutableComponent mutableComponent = text.getContents().resolve(source, sender, depth + 1);
for(Component component : text.getSiblings()) {
diff --git a/src/main/java/net/minecraft/network/chat/MessageSignature.java b/src/main/java/net/minecraft/network/chat/MessageSignature.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/chat/MessageSignature.java
+++ b/src/main/java/net/minecraft/network/chat/MessageSignature.java
@@ -0,0 +0,0 @@ import net.minecraft.util.ExtraCodecs;
import net.minecraft.util.SignatureUpdater;
import net.minecraft.util.SignatureValidator;
-public record MessageSignature(byte[] bytes) {
+public record MessageSignature(byte[] bytes) implements net.kyori.adventure.chat.SignedMessage.Signature { // Paper
public static final Codec<MessageSignature> CODEC = ExtraCodecs.BASE64_STRING.xmap(MessageSignature::new, MessageSignature::bytes);
public static final int BYTES = 256;
public MessageSignature {
- Preconditions.checkState(bs.length == 256, "Invalid message signature size");
+ Preconditions.checkState(bytes.length == 256, "Invalid message signature size"); // Paper - decompile fix
}
public static MessageSignature read(FriendlyByteBuf buf) {
diff --git a/src/main/java/net/minecraft/network/chat/OutgoingChatMessage.java b/src/main/java/net/minecraft/network/chat/OutgoingChatMessage.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/chat/OutgoingChatMessage.java
@ -1735,6 +1791,40 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ public net.minecraft.network.chat.ChatDecorator.Result requireResult() {
+ return Objects.requireNonNull(this.result, "Requires a decoration result to be set here");
+ }
+ public final class AdventureView implements net.kyori.adventure.chat.SignedMessage {
+ private AdventureView() {
+ }
+ @Override
+ public @org.jetbrains.annotations.NotNull Instant timestamp() {
+ return PlayerChatMessage.this.timeStamp();
+ }
+ @Override
+ public long salt() {
+ return PlayerChatMessage.this.salt();
+ }
+ @Override
+ public @org.jetbrains.annotations.Nullable Signature signature() {
+ return PlayerChatMessage.this.signature();
+ }
+ @Override
+ public net.kyori.adventure.text.@org.jetbrains.annotations.Nullable Component unsignedContent() {
+ return PlayerChatMessage.this.unsignedContent() == null ? null : io.papermc.paper.adventure.PaperAdventure.asAdventure(PlayerChatMessage.this.unsignedContent());
+ }
+ @Override
+ public @org.jetbrains.annotations.NotNull String message() {
+ return PlayerChatMessage.this.signedContent();
+ }
+ @Override
+ public @org.jetbrains.annotations.NotNull net.kyori.adventure.identity.Identity identity() {
+ return net.kyori.adventure.identity.Identity.identity(PlayerChatMessage.this.sender());
+ }
+ public PlayerChatMessage playerChatMessage() {
+ return PlayerChatMessage.this;
+ }
+ }
+ public AdventureView adventureView() {
+ return new AdventureView();
+ }
+ // Paper end
public static final MapCodec<PlayerChatMessage> MAP_CODEC = RecordCodecBuilder.mapCodec((instance) -> {
return instance.group(SignedMessageLink.CODEC.fieldOf("link").forGetter(PlayerChatMessage::link), MessageSignature.CODEC.optionalFieldOf("signature").forGetter((message) -> {
@ -3456,6 +3546,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ public void sendMessage(net.kyori.adventure.chat.SignedMessage signedMessage, net.kyori.adventure.chat.ChatType.Bound boundChatType) {
+ if (getHandle().connection == null) return;
+
+ if (signedMessage instanceof PlayerChatMessage.AdventureView view) {
+ this.getHandle().sendChatMessage(net.minecraft.network.chat.OutgoingChatMessage.create(view.playerChatMessage()), this.getHandle().isTextFilteringEnabled(), this.toHandle(boundChatType));
+ return;
+ }
+ net.kyori.adventure.text.Component message = signedMessage.unsignedContent() == null ? net.kyori.adventure.text.Component.text(signedMessage.message()) : signedMessage.unsignedContent();
+ if (signedMessage.isSystem()) {
+ this.sendMessage(message, boundChatType);

View file

@ -20,7 +20,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
+import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
+import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.entity.CraftPlayer;