diff --git a/patches/server/0010-Adventure.patch b/patches/server/0010-Adventure.patch index ee701aa325..49b0841e1c 100644 --- a/patches/server/0010-Adventure.patch +++ b/patches/server/0010-Adventure.patch @@ -3,25 +3,425 @@ From: Riley Park Date: Fri, 29 Jan 2021 17:54:03 +0100 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.contents.TranslatableContents filterAllowedArguments(Ljava/lang/Object;)Lcom/mojang/serialization/DataResult; + Co-authored-by: zml Co-authored-by: Jake Potrebic -diff --git a/src/main/java/io/papermc/paper/adventure/AdventureComponent.java b/src/main/java/io/papermc/paper/adventure/AdventureComponent.java +diff --git a/src/main/java/io/papermc/paper/adventure/AdventureCodecs.java b/src/main/java/io/papermc/paper/adventure/AdventureCodecs.java new file mode 100644 -index 0000000000000000000000000000000000000000..fbf7804f8e02fc76cadae661083dbcc5b1c21881 +index 0000000000000000000000000000000000000000..89d24905a91b3844ff95fe1d9252464a896906a7 --- /dev/null -+++ b/src/main/java/io/papermc/paper/adventure/AdventureComponent.java -@@ -0,0 +1,90 @@ ++++ b/src/main/java/io/papermc/paper/adventure/AdventureCodecs.java +@@ -0,0 +1,393 @@ ++package io.papermc.paper.adventure; ++ ++import com.mojang.brigadier.exceptions.CommandSyntaxException; ++import com.mojang.datafixers.util.Either; ++import com.mojang.serialization.Codec; ++import com.mojang.serialization.DataResult; ++import com.mojang.serialization.Encoder; ++import com.mojang.serialization.MapCodec; ++import com.mojang.serialization.codecs.RecordCodecBuilder; ++import java.io.IOException; ++import java.util.Collections; ++import java.util.List; ++import java.util.Optional; ++import java.util.UUID; ++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.EntityNBTComponent; ++import net.kyori.adventure.text.KeybindComponent; ++import net.kyori.adventure.text.NBTComponent; ++import net.kyori.adventure.text.NBTComponentBuilder; ++import net.kyori.adventure.text.ScoreComponent; ++import net.kyori.adventure.text.SelectorComponent; ++import net.kyori.adventure.text.StorageNBTComponent; ++import net.kyori.adventure.text.TextComponent; ++import net.kyori.adventure.text.TranslatableComponent; ++import net.kyori.adventure.text.event.ClickEvent; ++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.TagParser; ++import net.minecraft.network.chat.ComponentSerialization; ++import net.minecraft.network.chat.contents.KeybindContents; ++import net.minecraft.network.chat.contents.ScoreContents; ++import net.minecraft.network.chat.contents.TranslatableContents; ++import net.minecraft.util.ExtraCodecs; ++import net.minecraft.util.StringRepresentable; ++import net.minecraft.world.item.Item; ++import net.minecraft.world.item.ItemStack; ++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.codecs.RecordCodecBuilder.mapCodec; ++import static java.util.function.Function.identity; ++import static net.kyori.adventure.text.Component.text; ++import static net.minecraft.util.ExtraCodecs.recursive; ++import static net.minecraft.util.ExtraCodecs.strictOptionalField; ++ ++@DefaultQualifier(NonNull.class) ++public final class AdventureCodecs { ++ ++ public static final Codec COMPONENT_CODEC = recursive("adventure Component", AdventureCodecs::createCodec); ++ ++ static final Codec TEXT_COLOR_CODEC = Codec.STRING.comapFlatMap(s -> { ++ if (s.startsWith("#")) { ++ @Nullable TextColor value = TextColor.fromHexString(s); ++ return value != null ? DataResult.success(value) : DataResult.error(() -> "Cannot convert " + s + " to adventure TextColor"); ++ } else { ++ final @Nullable NamedTextColor value = NamedTextColor.NAMES.value(s); ++ return value != null ? DataResult.success(value) : DataResult.error(() -> "Cannot convert " + s + " to adventure NamedTextColor"); ++ } ++ }, textColor -> { ++ if (textColor instanceof NamedTextColor named) { ++ return NamedTextColor.NAMES.keyOrThrow(named); ++ } else { ++ return textColor.asHexString(); ++ } ++ }); ++ ++ static final Codec KEY_CODEC = Codec.STRING.comapFlatMap(s -> { ++ return Key.parseable(s) ? DataResult.success(Key.key(s)) : DataResult.error(() -> "Cannot convert " + s + " to adventure Key"); ++ }, Key::asString); ++ ++ static final Codec CLICK_EVENT_ACTION_CODEC = Codec.STRING.comapFlatMap(s -> { ++ final ClickEvent.@Nullable Action value = ClickEvent.Action.NAMES.value(s); ++ return value != null ? DataResult.success(value) : DataResult.error(() -> "Cannot convert " + s + " to adventure ClickEvent$Action"); ++ }, ClickEvent.Action.NAMES::keyOrThrow); ++ static final Codec CLICK_EVENT_CODEC = RecordCodecBuilder.create((instance) -> { ++ return instance.group( ++ CLICK_EVENT_ACTION_CODEC.fieldOf("action").forGetter(ClickEvent::action), ++ Codec.STRING.fieldOf("value").forGetter(ClickEvent::value) ++ ).apply(instance, ClickEvent::clickEvent); ++ }); ++ ++ static Codec showEntityCodec(final Codec componentCodec) { ++ return RecordCodecBuilder.create((instance) -> { ++ return instance.group( ++ KEY_CODEC.fieldOf("type").forGetter(HoverEvent.ShowEntity::type), ++ UUIDUtil.LENIENT_CODEC.fieldOf("id").forGetter(HoverEvent.ShowEntity::id), ++ strictOptionalField(componentCodec, "name").forGetter(he -> Optional.ofNullable(he.name())) ++ ).apply(instance, (key, uuid, component) -> { ++ return HoverEvent.ShowEntity.showEntity(key, uuid, component.orElse(null)); ++ }); ++ }); ++ } ++ ++ static Codec showItemCodec(final Codec componentCodec) { ++ return net.minecraft.network.chat.HoverEvent.ItemStackInfo.CODEC.xmap(isi -> { ++ @Subst("key") final String typeKey = BuiltInRegistries.ITEM.getKey(isi.item).toString(); ++ return HoverEvent.ShowItem.showItem(Key.key(typeKey), isi.count, PaperAdventure.asBinaryTagHolder(isi.tag.orElse(null))); ++ }, 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); ++ } ++ return new net.minecraft.network.chat.HoverEvent.ItemStackInfo(stack); ++ }); ++ } ++ ++ static final HoverEventType SHOW_ENTITY_HOVER_EVENT_TYPE = new HoverEventType<>(AdventureCodecs::showEntityCodec, HoverEvent.Action.SHOW_ENTITY, "show_entity", AdventureCodecs::legacyDeserializeEntity); ++ static final HoverEventType SHOW_ITEM_HOVER_EVENT_TYPE = new HoverEventType<>(AdventureCodecs::showItemCodec, HoverEvent.Action.SHOW_ITEM, "show_item", AdventureCodecs::legacyDeserializeItem); ++ static final HoverEventType SHOW_TEXT_HOVER_EVENT_TYPE = new HoverEventType<>(identity(), HoverEvent.Action.SHOW_TEXT, "show_text", DataResult::success); ++ static final Codec> HOVER_EVENT_TYPE_CODEC = StringRepresentable.fromValues(() -> new HoverEventType[]{ SHOW_ENTITY_HOVER_EVENT_TYPE, SHOW_ITEM_HOVER_EVENT_TYPE, SHOW_TEXT_HOVER_EVENT_TYPE }); ++ ++ static DataResult legacyDeserializeEntity(final Component text) { ++ try { ++ final CompoundTag tag = TagParser.parseTag(PlainTextComponentSerializer.plainText().serialize(text)); ++ final @Nullable Component entityName = GsonComponentSerializer.gson().deserializeOrNull(tag.getString("name")); ++ @Subst("key") final String keyString = tag.getString("type"); ++ final UUID entityUUID = UUID.fromString(tag.getString("id")); ++ return DataResult.success(HoverEvent.ShowEntity.showEntity(Key.key(keyString), entityUUID, entityName)); ++ } catch (final Exception ex) { ++ return DataResult.error(() -> "Failed to parse tooltip: " + ex.getMessage()); ++ } ++ } ++ ++ static DataResult legacyDeserializeItem(final Component text) { ++ try { ++ final ItemStack stack = ItemStack.of(TagParser.parseTag(PlainTextComponentSerializer.plainText().serialize(text))); ++ @Subst("key") final String keyString = BuiltInRegistries.ITEM.getKey(stack.getItem()).toString(); ++ return DataResult.success(HoverEvent.ShowItem.showItem(Key.key(keyString), stack.getCount(), stack.getTag() != null ? BinaryTagHolder.encode(stack.getTag(), PaperAdventure.NBT_CODEC) : null)); ++ } catch (final CommandSyntaxException | IOException ex) { ++ return DataResult.error(() -> "Failed to parse item tag: " + ex.getMessage()); ++ } ++ } ++ ++ record HoverEventType(Function, Codec>> codec, String id, Function, Codec>> legacyCodec) implements StringRepresentable { ++ HoverEventType(final Function, Codec> contentCodec, final HoverEvent.Action action, final String id, final Function> legacyDeserializer) { ++ this(cc -> contentCodec.apply(cc).xmap(v -> HoverEvent.hoverEvent(action, v), HoverEvent::value).fieldOf("contents").codec(), ++ id, ++ codec -> Codec.of( ++ Encoder.error("Can't encode in legacy format"), ++ codec.flatMap(legacyDeserializer).map(text -> HoverEvent.hoverEvent(action, text)) ++ ) ++ ); ++ } ++ @Override ++ public String getSerializedName() { ++ return this.id; ++ } ++ } ++ ++ private static final Function, HoverEventType> GET_HOVER_EVENT_TYPE = he -> { ++ if (he.action() == HoverEvent.Action.SHOW_ENTITY) { ++ return SHOW_ENTITY_HOVER_EVENT_TYPE; ++ } else if (he.action() == HoverEvent.Action.SHOW_ITEM) { ++ return SHOW_ITEM_HOVER_EVENT_TYPE; ++ } else if (he.action() == HoverEvent.Action.SHOW_TEXT) { ++ return SHOW_TEXT_HOVER_EVENT_TYPE; ++ } else { ++ throw new IllegalStateException(); ++ } ++ }; ++ static final Codec> HOVER_EVENT_CODEC = Codec.either( ++ HOVER_EVENT_TYPE_CODEC.>dispatchMap("action", GET_HOVER_EVENT_TYPE, het -> het.codec.apply(COMPONENT_CODEC)).codec(), ++ HOVER_EVENT_TYPE_CODEC.>dispatchMap("action", GET_HOVER_EVENT_TYPE, het -> het.legacyCodec.apply(COMPONENT_CODEC)).codec() ++ ).xmap(either -> either.map(identity(), identity()), Either::left); ++ ++ public static final MapCodec