Adventure

== AT ==
public net.minecraft.network.chat.HoverEvent$ItemStackInfo item
public net.minecraft.network.chat.HoverEvent$ItemStackInfo count
public net.minecraft.network.chat.HoverEvent$ItemStackInfo components
public net.minecraft.network.chat.contents.TranslatableContents filterAllowedArguments(Ljava/lang/Object;)Lcom/mojang/serialization/DataResult;

Co-authored-by: zml <zml@stellardrift.ca>
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
This commit is contained in:
Riley Park
2021-01-29 17:54:03 +01:00
parent b01c811c2f
commit 66779f5c86
103 changed files with 4975 additions and 392 deletions

View File

@@ -0,0 +1,23 @@
--- a/net/minecraft/network/chat/ChatDecorator.java
+++ b/net/minecraft/network/chat/ChatDecorator.java
@@ -2,10 +2,18 @@
import javax.annotation.Nullable;
import net.minecraft.server.level.ServerPlayer;
+import java.util.concurrent.CompletableFuture; // Paper
@FunctionalInterface
public interface ChatDecorator {
- ChatDecorator PLAIN = (sender, message) -> message;
+ ChatDecorator PLAIN = (sender, message) -> CompletableFuture.completedFuture(message); // Paper - adventure; support async chat decoration events
- Component decorate(@Nullable ServerPlayer sender, Component message);
+ @io.papermc.paper.annotation.DoNotUse @Deprecated // Paper - adventure; support chat decoration events (callers should use the overload with CommandSourceStack)
+ CompletableFuture<Component> decorate(@Nullable ServerPlayer sender, Component message); // Paper - adventure; support async chat decoration events
+
+ // Paper start - adventure; support async chat decoration events
+ default CompletableFuture<Component> decorate(@Nullable ServerPlayer sender, @Nullable net.minecraft.commands.CommandSourceStack commandSourceStack, Component message) {
+ throw new UnsupportedOperationException("Must override this implementation");
+ }
+ // Paper end - adventure; support async chat decoration events
}

View File

@@ -0,0 +1,99 @@
--- a/net/minecraft/network/chat/ComponentSerialization.java
+++ b/net/minecraft/network/chat/ComponentSerialization.java
@@ -37,9 +37,31 @@
public class ComponentSerialization {
public static final Codec<Component> CODEC = Codec.recursive("Component", ComponentSerialization::createCodec);
- public static final StreamCodec<RegistryFriendlyByteBuf, Component> STREAM_CODEC = ByteBufCodecs.fromCodecWithRegistries(CODEC);
+ public static final StreamCodec<RegistryFriendlyByteBuf, Component> STREAM_CODEC = createTranslationAware(() -> net.minecraft.nbt.NbtAccounter.create(net.minecraft.network.FriendlyByteBuf.DEFAULT_NBT_QUOTA)); // Paper - adventure
public static final StreamCodec<RegistryFriendlyByteBuf, Optional<Component>> OPTIONAL_STREAM_CODEC = STREAM_CODEC.apply(ByteBufCodecs::optional);
- public static final StreamCodec<RegistryFriendlyByteBuf, Component> TRUSTED_STREAM_CODEC = ByteBufCodecs.fromCodecWithRegistriesTrusted(CODEC);
+ // Paper start - adventure; use locale from bytebuf for translation
+ public static final ThreadLocal<Boolean> DONT_RENDER_TRANSLATABLES = ThreadLocal.withInitial(() -> false);
+ public static final StreamCodec<RegistryFriendlyByteBuf, Component> TRUSTED_STREAM_CODEC = createTranslationAware(net.minecraft.nbt.NbtAccounter::unlimitedHeap);
+ private static StreamCodec<RegistryFriendlyByteBuf, Component> createTranslationAware(final Supplier<net.minecraft.nbt.NbtAccounter> sizeTracker) {
+ return new StreamCodec<>() {
+ final StreamCodec<ByteBuf, net.minecraft.nbt.Tag> streamCodec = ByteBufCodecs.tagCodec(sizeTracker);
+ @Override
+ public Component decode(RegistryFriendlyByteBuf registryFriendlyByteBuf) {
+ net.minecraft.nbt.Tag tag = this.streamCodec.decode(registryFriendlyByteBuf);
+ RegistryOps<net.minecraft.nbt.Tag> registryOps = registryFriendlyByteBuf.registryAccess().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE);
+ return CODEC.parse(registryOps, tag).getOrThrow(error -> new io.netty.handler.codec.DecoderException("Failed to decode: " + error + " " + tag));
+ }
+
+ @Override
+ public void encode(RegistryFriendlyByteBuf registryFriendlyByteBuf, Component object) {
+ RegistryOps<net.minecraft.nbt.Tag> registryOps = registryFriendlyByteBuf.registryAccess().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE);
+ net.minecraft.nbt.Tag tag = (DONT_RENDER_TRANSLATABLES.get() ? CODEC : ComponentSerialization.localizedCodec(registryFriendlyByteBuf.adventure$locale))
+ .encodeStart(registryOps, object).getOrThrow(error -> new io.netty.handler.codec.EncoderException("Failed to encode: " + error + " " + object));
+ this.streamCodec.encode(registryFriendlyByteBuf, tag);
+ }
+ };
+ }
+ // Paper end - adventure; use locale from bytebuf for translation
public static final StreamCodec<RegistryFriendlyByteBuf, Optional<Component>> TRUSTED_OPTIONAL_STREAM_CODEC = TRUSTED_STREAM_CODEC.apply(
ByteBufCodecs::optional
);
@@ -100,7 +122,27 @@
return ExtraCodecs.orCompressed(mapCodec3, mapCodec2);
}
+ // Paper start - adventure; create separate codec for each locale
+ private static final java.util.Map<java.util.Locale, Codec<Component>> LOCALIZED_CODECS = new java.util.concurrent.ConcurrentHashMap<>();
+
+ public static Codec<Component> localizedCodec(final java.util.@org.checkerframework.checker.nullness.qual.Nullable Locale locale) {
+ if (locale == null) {
+ return CODEC;
+ }
+ return LOCALIZED_CODECS.computeIfAbsent(locale,
+ loc -> Codec.recursive("Component", selfCodec -> createCodec(selfCodec, loc)));
+ }
+
+
+ // Paper end - adventure; create separate codec for each locale
+
private static Codec<Component> createCodec(Codec<Component> selfCodec) {
+ // Paper start - adventure; create separate codec for each locale
+ return createCodec(selfCodec, null);
+ }
+
+ private static Codec<Component> createCodec(Codec<Component> selfCodec, @javax.annotation.Nullable java.util.Locale locale) {
+ // Paper end - adventure; create separate codec for each locale
ComponentContents.Type<?>[] types = new ComponentContents.Type[]{
PlainTextContents.TYPE, TranslatableContents.TYPE, KeybindContents.TYPE, ScoreContents.TYPE, SelectorContents.TYPE, NbtContents.TYPE
};
@@ -113,6 +155,34 @@
)
.apply(instance, MutableComponent::new)
);
+ // Paper start - adventure; create separate codec for each locale
+ final Codec<Component> origCodec = codec;
+ codec = new Codec<>() {
+ @Override
+ public <T> DataResult<com.mojang.datafixers.util.Pair<Component, T>> decode(final DynamicOps<T> ops, final T input) {
+ return origCodec.decode(ops, input);
+ }
+
+ @Override
+ public <T> DataResult<T> encode(final Component input, final DynamicOps<T> ops, final T prefix) {
+ final net.kyori.adventure.text.Component adventureComponent;
+ if (input instanceof io.papermc.paper.adventure.AdventureComponent adv) {
+ adventureComponent = adv.adventure$component();
+ } else if (locale != null && input.getContents() instanceof TranslatableContents && io.papermc.paper.adventure.PaperAdventure.hasAnyTranslations()) {
+ adventureComponent = io.papermc.paper.adventure.PaperAdventure.asAdventure(input);
+ } else {
+ return origCodec.encode(input, ops, prefix);
+ }
+ return io.papermc.paper.adventure.PaperAdventure.localizedCodec(locale)
+ .encode(adventureComponent, ops, prefix);
+ }
+
+ @Override
+ public String toString() {
+ return origCodec.toString() + "[AdventureComponentAware]";
+ }
+ };
+ // Paper end - adventure; create separate codec for each locale
return Codec.either(Codec.either(Codec.STRING, ExtraCodecs.nonEmptyList(selfCodec.listOf())), codec)
.xmap(either -> either.map(either2 -> either2.map(Component::literal, ComponentSerialization::createFromList), text -> (Component)text), text -> {
String string = text.tryCollapseToString();

View File

@@ -0,0 +1,14 @@
--- a/net/minecraft/network/chat/ComponentUtils.java
+++ b/net/minecraft/network/chat/ComponentUtils.java
@@ -41,6 +41,11 @@
if (depth > 100) {
return text.copy();
} else {
+ // Paper start - adventure; pass actual vanilla component
+ if (text instanceof io.papermc.paper.adventure.AdventureComponent adventureComponent) {
+ text = adventureComponent.deepConverted();
+ }
+ // Paper end - adventure; pass actual vanilla component
MutableComponent mutableComponent = text.getContents().resolve(source, sender, depth + 1);
for (Component component : text.getSiblings()) {

View File

@@ -0,0 +1,10 @@
--- a/net/minecraft/network/chat/MessageSignature.java
+++ b/net/minecraft/network/chat/MessageSignature.java
@@ -13,6 +13,7 @@
import net.minecraft.util.SignatureValidator;
public record MessageSignature(byte[] bytes) {
+ public net.kyori.adventure.chat.SignedMessage.Signature adventure() { return () -> this.bytes; } // Paper - adventure; support signed messages
public static final Codec<MessageSignature> CODEC = ExtraCodecs.BASE64_STRING.xmap(MessageSignature::new, MessageSignature::bytes);
public static final int BYTES = 256;

View File

@@ -0,0 +1,14 @@
--- a/net/minecraft/network/chat/MutableComponent.java
+++ b/net/minecraft/network/chat/MutableComponent.java
@@ -94,6 +94,11 @@
@Override
public boolean equals(Object object) {
+ // Paper start - make AdventureComponent equivalent
+ if (object instanceof io.papermc.paper.adventure.AdventureComponent adventureComponent) {
+ object = adventureComponent.deepConverted();
+ }
+ // Paper end - make AdventureComponent equivalent
return this == object
|| object instanceof MutableComponent mutableComponent
&& this.contents.equals(mutableComponent.contents)

View File

@@ -0,0 +1,44 @@
--- a/net/minecraft/network/chat/OutgoingChatMessage.java
+++ b/net/minecraft/network/chat/OutgoingChatMessage.java
@@ -7,6 +7,12 @@
void sendToPlayer(ServerPlayer sender, boolean filterMaskEnabled, ChatType.Bound params);
+ // Paper start
+ default void sendToPlayer(ServerPlayer sender, boolean filterMaskEnabled, ChatType.Bound params, @javax.annotation.Nullable Component unsigned) {
+ this.sendToPlayer(sender, filterMaskEnabled, params);
+ }
+ // Paper end
+
static OutgoingChatMessage create(PlayerChatMessage message) {
return (OutgoingChatMessage)(message.isSystem()
? new OutgoingChatMessage.Disguised(message.decoratedContent())
@@ -16,8 +22,13 @@
public static record Disguised(@Override Component content) implements OutgoingChatMessage {
@Override
public void sendToPlayer(ServerPlayer sender, boolean filterMaskEnabled, ChatType.Bound params) {
- sender.connection.sendDisguisedChatMessage(this.content, params);
+ // Paper start
+ this.sendToPlayer(sender, filterMaskEnabled, params, null);
}
+ public void sendToPlayer(ServerPlayer sender, boolean filterMaskEnabled, ChatType.Bound params, @javax.annotation.Nullable Component unsigned) {
+ sender.connection.sendDisguisedChatMessage(unsigned != null ? unsigned : this.content, params);
+ // Paper end
+ }
}
public static record Player(PlayerChatMessage message) implements OutgoingChatMessage {
@@ -28,7 +39,13 @@
@Override
public void sendToPlayer(ServerPlayer sender, boolean filterMaskEnabled, ChatType.Bound params) {
+ // Paper start
+ this.sendToPlayer(sender, filterMaskEnabled, params, null);
+ }
+ public void sendToPlayer(ServerPlayer sender, boolean filterMaskEnabled, ChatType.Bound params, @javax.annotation.Nullable Component unsigned) {
+ // Paper end
PlayerChatMessage playerChatMessage = this.message.filter(filterMaskEnabled);
+ playerChatMessage = unsigned != null ? playerChatMessage.withUnsignedContent(unsigned) : playerChatMessage; // Paper
if (!playerChatMessage.isFullyFiltered()) {
sender.connection.sendPlayerChatMessage(playerChatMessage, params);
}

View File

@@ -0,0 +1,61 @@
--- a/net/minecraft/network/chat/PlayerChatMessage.java
+++ b/net/minecraft/network/chat/PlayerChatMessage.java
@@ -17,6 +17,42 @@
public record PlayerChatMessage(
SignedMessageLink link, @Nullable MessageSignature signature, SignedMessageBody signedBody, @Nullable Component unsignedContent, FilterMask filterMask
) {
+ // Paper start - adventure; support signed messages
+ 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 == null ? null : PlayerChatMessage.this.signature.adventure();
+ }
+ @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 - adventure; support signed messages
public static final MapCodec<PlayerChatMessage> MAP_CODEC = RecordCodecBuilder.mapCodec(
instance -> instance.group(
SignedMessageLink.CODEC.fieldOf("link").forGetter(PlayerChatMessage::link),
@@ -47,7 +83,14 @@
}
public PlayerChatMessage withUnsignedContent(Component unsignedContent) {
- Component component = !unsignedContent.equals(Component.literal(this.signedContent())) ? unsignedContent : null;
+ // Paper start - adventure
+ final Component component;
+ if (unsignedContent instanceof io.papermc.paper.adventure.AdventureComponent advComponent) {
+ component = this.signedContent().equals(io.papermc.paper.adventure.AdventureCodecs.tryCollapseToString(advComponent.adventure$component())) ? null : unsignedContent;
+ } else {
+ component = !unsignedContent.equals(Component.literal(this.signedContent())) ? unsignedContent : null;
+ }
+ // Paper end - adventure
return new PlayerChatMessage(this.link, this.signature, this.signedBody, component, this.filterMask);
}