From aa5add485fa5003b443b19a700862c4e4061b617 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Wed, 24 Apr 2024 20:14:51 -0700 Subject: [PATCH] work on adventure --- patches/server/Adventure.patch | 333 ++++++++++++------ .../Fix-upstreams-block-state-factories.patch | 8 +- ...oleAppender-for-console-improvements.patch | 4 +- 3 files changed, 239 insertions(+), 106 deletions(-) diff --git a/patches/server/Adventure.patch b/patches/server/Adventure.patch index 9423b7676b..240670230b 100644 --- a/patches/server/Adventure.patch +++ b/patches/server/Adventure.patch @@ -6,7 +6,7 @@ Subject: [PATCH] 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 tag +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 @@ -28,27 +28,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; -+import com.mojang.serialization.Encoder; +import com.mojang.serialization.JsonOps; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; -+import io.netty.buffer.ByteBuf; -+import io.netty.handler.codec.DecoderException; -+import io.netty.handler.codec.EncoderException; +import java.io.IOException; +import java.util.Collections; +import java.util.List; ++import java.util.Map; +import java.util.Optional; +import java.util.UUID; -+import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import net.kyori.adventure.key.Key; -+import net.kyori.adventure.nbt.api.BinaryTagHolder; +import net.kyori.adventure.text.BlockNBTComponent; +import net.kyori.adventure.text.Component; -+import net.kyori.adventure.text.ComponentLike; +import net.kyori.adventure.text.EntityNBTComponent; +import net.kyori.adventure.text.KeybindComponent; +import net.kyori.adventure.text.NBTComponent; @@ -60,17 +54,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.TranslationArgument; +import net.kyori.adventure.text.event.ClickEvent; ++import net.kyori.adventure.text.event.DataComponentValue; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.format.TextDecoration; -+import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import net.minecraft.core.UUIDUtil; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.CompoundTag; -+import net.minecraft.nbt.NbtAccounter; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.Tag; +import net.minecraft.nbt.TagParser; @@ -86,19 +79,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import net.minecraft.util.StringRepresentable; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; -+import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.intellij.lang.annotations.Subst; + ++import static com.mojang.serialization.Codec.recursive; +import static com.mojang.serialization.codecs.RecordCodecBuilder.mapCodec; +import static java.util.function.Function.identity; +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.TranslationArgument.bool; +import static net.kyori.adventure.text.TranslationArgument.component; +import static net.kyori.adventure.text.TranslationArgument.numeric; -+import static com.mojang.serialization.Codec.recursive; + +@DefaultQualifier(NonNull.class) +public final class AdventureCodecs { @@ -152,16 +144,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + static Codec showItemCodec(final Codec componentCodec) { + return net.minecraft.network.chat.HoverEvent.ItemStackInfo.CODEC.xmap(isi -> { + @Subst("key") final String typeKey = isi.item.unwrapKey().orElseThrow().toString(); -+ return HoverEvent.ShowItem.showItem(Key.key(typeKey), isi.count, PaperAdventure.dataComponents(isi.getItemStack())); ++ return HoverEvent.ShowItem.showItem(Key.key(typeKey), isi.count, PaperAdventure.asAdventure(isi.getItemStack().getComponentsPatch())); + }, si -> { + final Item itemType = BuiltInRegistries.ITEM.get(PaperAdventure.asVanilla(si.item())); -+ final ItemStack stack; -+ try { -+ final @Nullable CompoundTag tag = si.nbt() != null ? si.nbt().get(PaperAdventure.NBT_CODEC) : null; -+ stack = new ItemStack(BuiltInRegistries.ITEM.wrapAsHolder(itemType), si.count(), Optional.ofNullable(tag)); -+ } catch (final IOException e) { -+ throw new RuntimeException(e); -+ } ++ final Map dataComponentsMap = si.dataComponents(); ++ final ItemStack stack = new ItemStack(BuiltInRegistries.ITEM.wrapAsHolder(itemType), si.count(), PaperAdventure.asVanilla(dataComponentsMap)); + return new net.minecraft.network.chat.HoverEvent.ItemStackInfo(stack); + }); + } @@ -190,8 +177,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + final DynamicOps dynamicOps = ops != null ? ops.withParent(NbtOps.INSTANCE) : NbtOps.INSTANCE; + final DataResult stackResult = ItemStack.CODEC.parse(dynamicOps, tag); + return stackResult.map(stack -> { -+ final CraftItemStack craft = CraftItemStack.asCraftMirror(stack); -+ return HoverEvent.ShowItem.showItem(craft.getType().key(), stack.getCount(), PaperAdventure.dataComponents(stack)); ++ @Subst("key:value") final String location = stack.getItemHolder().unwrapKey().orElseThrow().location().toString(); ++ return HoverEvent.ShowItem.showItem(Key.key(location), stack.getCount(), PaperAdventure.asAdventure(stack.getComponentsPatch())); + }); + } catch (final CommandSyntaxException ex) { + return DataResult.error(() -> "Failed to parse item tag: " + ex.getMessage()); @@ -1166,10 +1153,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ +package io.papermc.paper.adventure; + ++import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; ++import com.mojang.serialization.JavaOps; +import io.netty.util.AttributeKey; +import java.io.IOException; +import java.util.ArrayList; ++import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; @@ -1189,6 +1179,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.TranslationArgument; +import net.kyori.adventure.text.event.DataComponentValue; ++import net.kyori.adventure.text.event.DataComponentValueConverterRegistry; +import net.kyori.adventure.text.flattener.ComponentFlattener; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextColor; @@ -1201,16 +1192,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import net.kyori.adventure.translation.Translator; +import net.kyori.adventure.util.Codec; +import net.minecraft.ChatFormatting; -+import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.core.Holder; -+import net.minecraft.core.component.TypedDataComponent; ++import net.minecraft.core.component.DataComponentPatch; ++import net.minecraft.core.component.DataComponentType; ++import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.locale.Language; +import net.minecraft.nbt.CompoundTag; -+import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtOps; -+import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.Tag; +import net.minecraft.nbt.TagParser; +import net.minecraft.network.chat.ComponentUtils; @@ -1218,18 +1208,22 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket; +import net.minecraft.network.protocol.game.ClientboundSoundPacket; +import net.minecraft.resources.ResourceLocation; ++import net.minecraft.server.network.Filterable; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.BossEvent; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; -+import net.minecraft.world.item.WrittenBookItem; ++import net.minecraft.world.item.component.WrittenBookContent; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.command.VanillaCommandWrapper; +import org.bukkit.craftbukkit.entity.CraftEntity; ++import org.intellij.lang.annotations.Subst; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + ++import static java.util.Objects.requireNonNull; ++ +public final class PaperAdventure { + private static final Pattern LOCALIZATION_PATTERN = Pattern.compile("%(?:(\\d+)\\$)?s"); + public static final ComponentFlattener FLATTENER = ComponentFlattener.basic().toBuilder() @@ -1284,18 +1278,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + public static final AttributeKey LOCALE_ATTRIBUTE = AttributeKey.valueOf("adventure:locale"); // init after FLATTENER because classloading triggered here might create a logger + @Deprecated + public static final PlainComponentSerializer PLAIN = PlainComponentSerializer.builder().flattener(FLATTENER).build(); -+ static final Codec NBT_CODEC = new Codec() { ++ public static final Codec NBT_CODEC = new Codec<>() { + @Override -+ public @NotNull CompoundTag decode(final @NotNull String encoded) throws IOException { -+ try { -+ return TagParser.parseTag(encoded); -+ } catch (final CommandSyntaxException e) { -+ throw new IOException(e); -+ } ++ public @NotNull Tag decode(final @NotNull String encoded) throws CommandSyntaxException { ++ return new TagParser(new StringReader(encoded)).readValue(); + } + + @Override -+ public @NotNull String encode(final @NotNull CompoundTag decoded) { ++ public @NotNull String encode(final @NotNull Tag decoded) { + return decoded.toString(); + } + }; @@ -1473,17 +1463,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + public static ItemStack asItemStack(final Book book, final Locale locale) { + final ItemStack item = new ItemStack(net.minecraft.world.item.Items.WRITTEN_BOOK, 1); -+ final CompoundTag tag = item.getOrCreateTag(); -+ tag.putString(WrittenBookItem.TAG_TITLE, validateField(asPlain(book.title(), locale), WrittenBookItem.TITLE_MAX_LENGTH, WrittenBookItem.TAG_TITLE)); -+ tag.putString(WrittenBookItem.TAG_AUTHOR, asPlain(book.author(), locale)); -+ final ListTag pages = new ListTag(); -+ if (book.pages().size() > WrittenBookItem.MAX_PAGES) { -+ throw new IllegalArgumentException("Book provided had " + book.pages().size() + " pages, but is only allowed a maximum of " + WrittenBookItem.MAX_PAGES); -+ } -+ for (final Component page : book.pages()) { -+ pages.add(StringTag.valueOf(validateField(asJsonString(page, locale), WrittenBookItem.PAGE_LENGTH, "page"))); -+ } -+ tag.put(WrittenBookItem.TAG_PAGES, pages); ++ item.set(DataComponents.WRITTEN_BOOK_CONTENT, new WrittenBookContent( ++ Filterable.passThrough(validateField(asPlain(book.title(), locale), WrittenBookContent.TITLE_MAX_LENGTH, "title")), ++ asPlain(book.author(), locale), ++ 0, ++ book.pages().stream().map(c -> Filterable.passThrough(PaperAdventure.asVanilla(c))).toList(), // TODO should we validate legnth? ++ false ++ )); + return item; + } + @@ -1547,34 +1533,57 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + // NBT + -+ public static Map dataComponents( -+ final ItemStack stack ++ @SuppressWarnings({"rawtypes", "unchecked"}) ++ public static Map asAdventure( ++ final DataComponentPatch patch + ) { ++ if (patch.isEmpty()) { ++ return Collections.emptyMap(); ++ } + final Map map = new HashMap<>(); -+ for (final TypedDataComponent component : stack.getComponents()) { -+ final ResourceLocation key = BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(component.type()); -+ final DataComponentValue value = new DataComponentValue.TagSerializable() { -+ @Override -+ public @NotNull BinaryTagHolder asBinaryTag() { -+ return BinaryTagHolder.binaryTagHolder( -+ component.encodeValue(NbtOps.INSTANCE).map(Tag::getAsString).getOrThrow() -+ ); -+ } -+ }; -+ map.put(Key.key(key.toString()), value); ++ for (final Map.Entry, Optional> entry : patch.entrySet()) { ++ if (entry.getKey().isTransient()) continue; ++ @Subst("key:value") final String typeKey = requireNonNull(BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(entry.getKey())).toString(); ++ if (entry.getValue().isEmpty()) { ++ map.put(Key.key(typeKey), DataComponentValue.removed()); ++ } else { ++ map.put(Key.key(typeKey), new DataComponentValueImpl(entry.getKey().codec(), entry.getValue().get())); ++ } + } + return map; + } + ++ @SuppressWarnings({"rawtypes", "unchecked"}) ++ public static DataComponentPatch asVanilla(final Map map) { ++ if (map.isEmpty()) { ++ return DataComponentPatch.EMPTY; ++ } ++ final DataComponentPatch.Builder builder = DataComponentPatch.builder(); ++ map.forEach((key, dataComponentValue) -> { ++ final DataComponentType type = requireNonNull(BuiltInRegistries.DATA_COMPONENT_TYPE.get(asVanilla(key))); ++ if (dataComponentValue instanceof DataComponentValue.Removed) { ++ builder.remove(type); ++ return; ++ } ++ final DataComponentValueImpl converted = DataComponentValueConverterRegistry.convert(DataComponentValueImpl.class, key, dataComponentValue); ++ builder.set((DataComponentType) type, (Object) converted.value()); ++ }); ++ return builder.build(); ++ } ++ ++ public record DataComponentValueImpl(com.mojang.serialization.Codec codec, T value) implements DataComponentValue.TagSerializable { ++ ++ @Override ++ public @NotNull BinaryTagHolder asBinaryTag() { ++ return BinaryTagHolder.encode(this.codec.encodeStart(NbtOps.INSTANCE, this.value).getOrThrow(IllegalArgumentException::new), NBT_CODEC); ++ } ++ } ++ + public static @Nullable BinaryTagHolder asBinaryTagHolder(final @Nullable CompoundTag tag) { + if (tag == null) { + return null; + } -+ try { -+ return BinaryTagHolder.encode(tag, NBT_CODEC); -+ } catch (final IOException e) { -+ return null; -+ } ++ return BinaryTagHolder.encode(tag, NBT_CODEC); + } + + // Colors @@ -1593,20 +1602,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + // Style + -+ public static net.minecraft.network.chat.Style asVanilla(Style style) { -+ Object encoded = Util.getOrThrow(AdventureCodecs.STYLE_MAP_CODEC.codec() -+ .encodeStart(net.minecraft.util.JavaOps.INSTANCE, style), IllegalStateException::new); ++ public static net.minecraft.network.chat.Style asVanilla(final Style style) { ++ final Object encoded = AdventureCodecs.STYLE_MAP_CODEC.codec() ++ .parse(JavaOps.INSTANCE, style).getOrThrow(IllegalStateException::new); + -+ return Util.getOrThrow(net.minecraft.network.chat.Style.Serializer.CODEC -+ .parse(net.minecraft.util.JavaOps.INSTANCE, encoded), IllegalStateException::new); ++ return net.minecraft.network.chat.Style.Serializer.CODEC ++ .parse(JavaOps.INSTANCE, encoded).getOrThrow(IllegalStateException::new); + } + -+ public static Style asAdventure(net.minecraft.network.chat.Style style) { -+ Object encoded = Util.getOrThrow(net.minecraft.network.chat.Style.Serializer.CODEC -+ .encodeStart(net.minecraft.util.JavaOps.INSTANCE, style), IllegalStateException::new); ++ public static Style asAdventure(final net.minecraft.network.chat.Style style) { ++ final Object encoded = net.minecraft.network.chat.Style.Serializer.CODEC ++ .parse(JavaOps.INSTANCE, style).getOrThrow(IllegalStateException::new); + -+ return Util.getOrThrow(AdventureCodecs.STYLE_MAP_CODEC.codec() -+ .parse(net.minecraft.util.JavaOps.INSTANCE, encoded), IllegalStateException::new); ++ return AdventureCodecs.STYLE_MAP_CODEC.codec() ++ .parse(JavaOps.INSTANCE, encoded).getOrThrow(IllegalStateException::new); + } +} diff --git a/src/main/java/io/papermc/paper/adventure/WrapperAwareSerializer.java b/src/main/java/io/papermc/paper/adventure/WrapperAwareSerializer.java @@ -1802,6 +1811,88 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return PaperAdventure.asPlain(message, null); + } +} +diff --git a/src/main/java/io/papermc/paper/adventure/providers/DataComponentValueConverterProviderImpl.java b/src/main/java/io/papermc/paper/adventure/providers/DataComponentValueConverterProviderImpl.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/adventure/providers/DataComponentValueConverterProviderImpl.java +@@ -0,0 +0,0 @@ ++package io.papermc.paper.adventure.providers; ++ ++import com.google.gson.JsonElement; ++import com.mojang.brigadier.exceptions.CommandSyntaxException; ++import com.mojang.serialization.JsonOps; ++import io.papermc.paper.adventure.PaperAdventure; ++import java.util.List; ++import net.kyori.adventure.key.Key; ++import net.kyori.adventure.nbt.api.BinaryTagHolder; ++import net.kyori.adventure.text.event.DataComponentValue; ++import net.kyori.adventure.text.event.DataComponentValueConverterRegistry; ++import net.kyori.adventure.text.serializer.gson.GsonDataComponentValue; ++import net.minecraft.core.component.DataComponentType; ++import net.minecraft.core.registries.BuiltInRegistries; ++import net.minecraft.nbt.NbtOps; ++import net.minecraft.nbt.Tag; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++import static net.kyori.adventure.nbt.api.BinaryTagHolder.binaryTagHolder; ++import static net.kyori.adventure.text.serializer.gson.GsonDataComponentValue.gsonDatacomponentValue; ++ ++@DefaultQualifier(NonNull.class) ++public class DataComponentValueConverterProviderImpl implements DataComponentValueConverterRegistry.Provider { ++ ++ static final Key ID = Key.key("adventure", "platform/paper"); ++ ++ @Override ++ public Key id() { ++ return ID; ++ } ++ ++ @SuppressWarnings({"unchecked", "rawtypes"}) ++ @Override ++ public Iterable> conversions() { ++ return List.of( ++ DataComponentValueConverterRegistry.Conversion.convert( ++ PaperAdventure.DataComponentValueImpl.class, ++ GsonDataComponentValue.class, ++ (key, dataComponentValue) -> gsonDatacomponentValue((JsonElement) dataComponentValue.codec().encodeStart(JsonOps.INSTANCE, dataComponentValue.value()).getOrThrow()) ++ ), ++ DataComponentValueConverterRegistry.Conversion.convert( ++ GsonDataComponentValue.class, ++ PaperAdventure.DataComponentValueImpl.class, ++ (key, dataComponentValue) -> { ++ final @Nullable DataComponentType type = BuiltInRegistries.DATA_COMPONENT_TYPE.get(PaperAdventure.asVanilla(key)); ++ if (type == null) { ++ throw new IllegalArgumentException("Unknown data component type: " + key); ++ } ++ return new PaperAdventure.DataComponentValueImpl(type.codecOrThrow(), type.codecOrThrow().parse(JsonOps.INSTANCE, dataComponentValue.element()).getOrThrow(IllegalArgumentException::new)); ++ } ++ ), ++ DataComponentValueConverterRegistry.Conversion.convert( ++ PaperAdventure.DataComponentValueImpl.class, ++ DataComponentValue.TagSerializable.class, ++ (key, dataComponentValue) -> BinaryTagHolder.encode((Tag) dataComponentValue.codec().encodeStart(NbtOps.INSTANCE, dataComponentValue.value()).getOrThrow(), PaperAdventure.NBT_CODEC) ++ ), ++ DataComponentValueConverterRegistry.Conversion.convert( ++ DataComponentValue.TagSerializable.class, ++ PaperAdventure.DataComponentValueImpl.class, ++ (key, tagSerializable) -> { ++ final @Nullable DataComponentType type = BuiltInRegistries.DATA_COMPONENT_TYPE.get(PaperAdventure.asVanilla(key)); ++ if (type == null) { ++ throw new IllegalArgumentException("Unknown data component type: " + key); ++ } ++ try { ++ return new PaperAdventure.DataComponentValueImpl(type.codecOrThrow(), type.codecOrThrow().parse(NbtOps.INSTANCE, tagSerializable.asBinaryTag().get(PaperAdventure.NBT_CODEC)).getOrThrow(IllegalArgumentException::new)); ++ } catch (final CommandSyntaxException e) { ++ throw new IllegalArgumentException(e); ++ } ++ } ++ ) ++ ); ++ } ++} diff --git a/src/main/java/io/papermc/paper/adventure/providers/GsonComponentSerializerProviderImpl.java b/src/main/java/io/papermc/paper/adventure/providers/GsonComponentSerializerProviderImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 @@ -2182,28 +2273,36 @@ diff --git a/src/main/java/net/minecraft/network/chat/ComponentSerialization.jav index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/network/chat/ComponentSerialization.java +++ b/src/main/java/net/minecraft/network/chat/ComponentSerialization.java -@@ -0,0 +0,0 @@ public class ComponentSerialization { +@@ -0,0 +0,0 @@ import net.minecraft.util.StringRepresentable; + + public class ComponentSerialization { public static final Codec CODEC = Codec.recursive("Component", ComponentSerialization::createCodec); - public static final StreamCodec STREAM_CODEC = ByteBufCodecs.fromCodecWithRegistries(CODEC); +- public static final StreamCodec STREAM_CODEC = ByteBufCodecs.fromCodecWithRegistries(CODEC); ++ public static final StreamCodec STREAM_CODEC = createTranslationAware(() -> net.minecraft.nbt.NbtAccounter.create(net.minecraft.network.FriendlyByteBuf.DEFAULT_NBT_QUOTA)); // Paper - adventure public static final StreamCodec> OPTIONAL_STREAM_CODEC = STREAM_CODEC.apply(ByteBufCodecs::optional); - public static final StreamCodec TRUSTED_STREAM_CODEC = ByteBufCodecs.fromCodecWithRegistriesTrusted(CODEC); + // Paper start - adventure; use locale from bytebuf for translation -+ public static final StreamCodec TRUSTED_STREAM_CODEC = new StreamCodec<>() { -+ final StreamCodec streamCodec = ByteBufCodecs.tagCodec(net.minecraft.nbt.NbtAccounter::unlimitedHeap); -+ @Override -+ public Component decode(RegistryFriendlyByteBuf registryFriendlyByteBuf) { -+ net.minecraft.nbt.Tag tag = this.streamCodec.decode(registryFriendlyByteBuf); -+ RegistryOps 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)); -+ } ++ public static final ThreadLocal DONT_RENDER_TRANSLATABLES = ThreadLocal.withInitial(() -> false); ++ public static final StreamCodec TRUSTED_STREAM_CODEC = createTranslationAware(net.minecraft.nbt.NbtAccounter::unlimitedHeap); ++ private static StreamCodec createTranslationAware(final Supplier sizeTracker) { ++ return new StreamCodec<>() { ++ final StreamCodec streamCodec = ByteBufCodecs.tagCodec(sizeTracker); ++ @Override ++ public Component decode(RegistryFriendlyByteBuf registryFriendlyByteBuf) { ++ net.minecraft.nbt.Tag tag = this.streamCodec.decode(registryFriendlyByteBuf); ++ RegistryOps 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 registryOps = registryFriendlyByteBuf.registryAccess().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE); -+ net.minecraft.nbt.Tag tag = 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); -+ } -+ }; ++ @Override ++ public void encode(RegistryFriendlyByteBuf registryFriendlyByteBuf, Component object) { ++ RegistryOps 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> TRUSTED_OPTIONAL_STREAM_CODEC = TRUSTED_STREAM_CODEC.apply( ByteBufCodecs::optional @@ -3111,6 +3210,28 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 return this.createWorldFog; } +diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/world/item/ItemStack.java ++++ b/src/main/java/net/minecraft/world/item/ItemStack.java +@@ -0,0 +0,0 @@ public final class ItemStack implements DataComponentHolder { + return ItemStack.EMPTY; + } else { + Holder holder = (Holder) ITEM_STREAM_CODEC.decode(registryfriendlybytebuf); // CraftBukkit - decompile error +- DataComponentPatch datacomponentpatch = (DataComponentPatch) DataComponentPatch.STREAM_CODEC.decode(registryfriendlybytebuf); ++ // Paper start - adventure; conditionally render translatable components ++ DataComponentPatch datacomponentpatch; ++ boolean prev = net.minecraft.network.chat.ComponentSerialization.DONT_RENDER_TRANSLATABLES.get(); ++ try { ++ net.minecraft.network.chat.ComponentSerialization.DONT_RENDER_TRANSLATABLES.set(true); ++ datacomponentpatch = (DataComponentPatch) DataComponentPatch.STREAM_CODEC.decode(registryfriendlybytebuf); ++ } finally { ++ net.minecraft.network.chat.ComponentSerialization.DONT_RENDER_TRANSLATABLES.set(prev); ++ } ++ // Paper end - adventure; conditionally render translatable components + + // CraftBukkit start + ItemStack itemstack = new ItemStack(holder, i, datacomponentpatch); diff --git a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java @@ -4645,7 +4766,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper start - Adventure + @Override + public net.kyori.adventure.text.event.HoverEvent asHoverEvent(final ItemStack item, final java.util.function.UnaryOperator op) { -+ return net.kyori.adventure.text.event.HoverEvent.showItem(op.apply(net.kyori.adventure.text.event.HoverEvent.ShowItem.showItem(item.getType().getKey(), item.getAmount(), io.papermc.paper.adventure.PaperAdventure.dataComponents(CraftItemStack.asNMSCopy(item))))); ++ return net.kyori.adventure.text.event.HoverEvent.showItem(op.apply(net.kyori.adventure.text.event.HoverEvent.ShowItem.showItem(item.getType().getKey(), item.getAmount(), io.papermc.paper.adventure.PaperAdventure.asAdventure(CraftItemStack.asNMSCopy(item))))); + } + + @Override @@ -5416,6 +5537,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +++ b/src/main/resources/META-INF/services/net.kyori.adventure.text.event.ClickCallback$Provider @@ -0,0 +1 @@ +io.papermc.paper.adventure.providers.ClickCallbackProviderImpl +diff --git a/src/main/resources/META-INF/services/net.kyori.adventure.text.event.DataComponentValueConverterRegistry$Provider b/src/main/resources/META-INF/services/net.kyori.adventure.text.event.DataComponentValueConverterRegistry$Provider +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/resources/META-INF/services/net.kyori.adventure.text.event.DataComponentValueConverterRegistry$Provider +@@ -0,0 +1 @@ ++io.papermc.paper.adventure.providers.DataComponentValueConverterProviderImpl diff --git a/src/main/resources/META-INF/services/net.kyori.adventure.text.logger.slf4j.ComponentLoggerProvider b/src/main/resources/META-INF/services/net.kyori.adventure.text.logger.slf4j.ComponentLoggerProvider new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 @@ -5483,6 +5611,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; ++import com.mojang.serialization.JavaOps; +import com.mojang.serialization.JsonOps; +import io.papermc.paper.util.MethodParameterSource; +import java.io.IOException; @@ -5494,7 +5623,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import java.util.UUID; +import java.util.function.Function; +import net.kyori.adventure.key.Key; -+import net.kyori.adventure.nbt.api.BinaryTagHolder; +import net.kyori.adventure.text.BlockNBTComponent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; @@ -5503,6 +5631,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.format.TextDecoration; ++import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.ByteTag; +import net.minecraft.nbt.CompoundTag; @@ -5512,7 +5641,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.ComponentSerialization; +import net.minecraft.resources.ResourceLocation; -+import net.minecraft.util.JavaOps; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import org.apache.commons.lang3.RandomStringUtils; @@ -5529,7 +5657,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import static io.papermc.paper.adventure.AdventureCodecs.KEY_CODEC; +import static io.papermc.paper.adventure.AdventureCodecs.STYLE_MAP_CODEC; +import static io.papermc.paper.adventure.AdventureCodecs.TEXT_COLOR_CODEC; -+import static io.papermc.paper.adventure.PaperAdventure.NBT_CODEC; +import static java.util.Objects.requireNonNull; +import static net.kyori.adventure.key.Key.key; +import static net.kyori.adventure.text.Component.blockNBT; @@ -5540,7 +5667,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import static net.kyori.adventure.text.Component.storageNBT; +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.Component.translatable; -+import static net.kyori.adventure.text.TranslationArgument.bool; +import static net.kyori.adventure.text.TranslationArgument.numeric; +import static net.kyori.adventure.text.event.ClickEvent.openUrl; +import static net.kyori.adventure.text.event.ClickEvent.suggestCommand; @@ -5606,8 +5732,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + @Test + void testShowItemHoverEvent() throws IOException { + final ItemStack stack = new ItemStack(Items.PUMPKIN, 3); -+ stack.setHoverName(net.minecraft.network.chat.Component.literal("NAME")); -+ final HoverEvent hoverEvent = HoverEvent.showItem(key("minecraft:pumpkin"), 3, BinaryTagHolder.encode(requireNonNull(stack.getTag()), NBT_CODEC)); ++ stack.set(DataComponents.CUSTOM_NAME, net.minecraft.network.chat.Component.literal("NAME")); ++ final HoverEvent hoverEvent = HoverEvent.showItem(key("minecraft:pumpkin"), 3, PaperAdventure.asAdventure(stack.getComponentsPatch())); + final Tag result = HOVER_EVENT_CODEC.encodeStart(NbtOps.INSTANCE, hoverEvent).result().orElseThrow(); + final DataResult> dataResult = net.minecraft.network.chat.HoverEvent.CODEC.decode(NbtOps.INSTANCE, result); + assertTrue(dataResult.result().isPresent(), () -> dataResult + " result is not present"); @@ -5616,8 +5742,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + final net.minecraft.network.chat.HoverEvent.ItemStackInfo value = nms.getValue(net.minecraft.network.chat.HoverEvent.Action.SHOW_ITEM); + assertNotNull(value); + assertEquals(hoverEvent.value().count(), value.count); -+ assertEquals(hoverEvent.value().item().asString(), BuiltInRegistries.ITEM.getKey(value.item).toString()); -+ assertEquals(stack.getTag(), value.tag.orElse(null)); ++ assertEquals(hoverEvent.value().item().asString(), value.item.unwrapKey().orElseThrow().location().toString()); ++ assertEquals(stack.getComponentsPatch(), value.components); + } + + @Test @@ -5731,9 +5857,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + + static R require(final DataResult result, final Function errorMessage) { -+ return result.get().map(Function.identity(), r -> { -+ throw new RuntimeException(errorMessage.apply(r.message())); -+ }); ++ return switch (result) { ++ case final DataResult.Error error -> throw new RuntimeException(errorMessage.apply(error.message())); ++ case final DataResult.Success success -> success.value(); ++ }; + } + + static List invalidData() { diff --git a/patches/server/Fix-upstreams-block-state-factories.patch b/patches/server/Fix-upstreams-block-state-factories.patch index 7cfe212df0..37db2e106d 100644 --- a/patches/server/Fix-upstreams-block-state-factories.patch +++ b/patches/server/Fix-upstreams-block-state-factories.patch @@ -41,11 +41,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ public class CraftBlockEntityState extends CraftBlockStat } + @Override +- public CraftBlockEntityState copy() { +- return new CraftBlockEntityState<>(this, null); +- } ++ public abstract CraftBlockEntityState copy(); // Paper - make abstract + @Override - public CraftBlockEntityState copy(Location location) { - return new CraftBlockEntityState<>(this, location); - } -+ public abstract CraftBlockEntityState copy(); // Paper - make abstract ++ public abstract CraftBlockEntityState copy(Location location); // Paper - make abstract // Paper start @Override diff --git a/patches/server/Use-TerminalConsoleAppender-for-console-improvements.patch b/patches/server/Use-TerminalConsoleAppender-for-console-improvements.patch index 415c4c0f6b..a8beacefeb 100644 --- a/patches/server/Use-TerminalConsoleAppender-for-console-improvements.patch +++ b/patches/server/Use-TerminalConsoleAppender-for-console-improvements.patch @@ -222,9 +222,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @Deprecated public static final PlainComponentSerializer PLAIN = PlainComponentSerializer.builder().flattener(FLATTENER).build(); + public static final ANSIComponentSerializer ANSI_SERIALIZER = ANSIComponentSerializer.builder().flattener(FLATTENER).build(); - static final Codec NBT_CODEC = new Codec() { + public static final Codec NBT_CODEC = new Codec<>() { @Override - public @NotNull CompoundTag decode(final @NotNull String encoded) throws IOException { + public @NotNull Tag decode(final @NotNull String encoded) throws CommandSyntaxException { diff --git a/src/main/java/io/papermc/paper/adventure/providers/ComponentLoggerProviderImpl.java b/src/main/java/io/papermc/paper/adventure/providers/ComponentLoggerProviderImpl.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/io/papermc/paper/adventure/providers/ComponentLoggerProviderImpl.java