package net.minecraft.world.item.component; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.ImmutableList.Builder; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.util.List; import java.util.Optional; import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.HolderLookup; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.network.chat.ComponentUtils; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.server.network.Filterable; import net.minecraft.util.ExtraCodecs; import net.minecraft.world.entity.player.Player; import org.jetbrains.annotations.Nullable; public record WrittenBookContent(Filterable title, String author, int generation, List> pages, boolean resolved) implements BookContent { public static final WrittenBookContent EMPTY = new WrittenBookContent(Filterable.passThrough(""), "", 0, List.of(), true); public static final int PAGE_LENGTH = 32767; public static final int TITLE_LENGTH = 16; public static final int TITLE_MAX_LENGTH = 32; public static final int MAX_GENERATION = 3; public static final int MAX_CRAFTABLE_GENERATION = 2; public static final Codec CONTENT_CODEC = ComponentSerialization.flatCodec(32767); public static final Codec>> PAGES_CODEC = pagesCodec(CONTENT_CODEC); public static final Codec CODEC = RecordCodecBuilder.create( instance -> instance.group( Filterable.codec(Codec.string(0, 32)).fieldOf("title").forGetter(WrittenBookContent::title), Codec.STRING.fieldOf("author").forGetter(WrittenBookContent::author), ExtraCodecs.intRange(0, 3).optionalFieldOf("generation", 0).forGetter(WrittenBookContent::generation), PAGES_CODEC.optionalFieldOf("pages", List.of()).forGetter(WrittenBookContent::pages), Codec.BOOL.optionalFieldOf("resolved", false).forGetter(WrittenBookContent::resolved) ) .apply(instance, WrittenBookContent::new) ); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( Filterable.streamCodec(ByteBufCodecs.stringUtf8(32)), WrittenBookContent::title, ByteBufCodecs.STRING_UTF8, WrittenBookContent::author, ByteBufCodecs.VAR_INT, WrittenBookContent::generation, Filterable.streamCodec(ComponentSerialization.STREAM_CODEC).apply(ByteBufCodecs.list()), WrittenBookContent::pages, ByteBufCodecs.BOOL, WrittenBookContent::resolved, WrittenBookContent::new ); public WrittenBookContent(Filterable title, String author, int generation, List> pages, boolean resolved) { if (generation >= 0 && generation <= 3) { this.title = title; this.author = author; this.generation = generation; this.pages = pages; this.resolved = resolved; } else { throw new IllegalArgumentException("Generation was " + generation + ", but must be between 0 and 3"); } } private static Codec> pageCodec(Codec codec) { return Filterable.codec(codec); } public static Codec>> pagesCodec(Codec codec) { return pageCodec(codec).listOf(); } @Nullable public WrittenBookContent tryCraftCopy() { return this.generation >= 2 ? null : new WrittenBookContent(this.title, this.author, this.generation + 1, this.pages, this.resolved); } @Nullable public WrittenBookContent resolve(CommandSourceStack source, @Nullable Player player) { if (this.resolved) { return null; } else { Builder> builder = ImmutableList.builderWithExpectedSize(this.pages.size()); for (Filterable filterable : this.pages) { Optional> optional = resolvePage(source, player, filterable); if (optional.isEmpty()) { return null; } builder.add((Filterable)optional.get()); } return new WrittenBookContent(this.title, this.author, this.generation, builder.build(), true); } } public WrittenBookContent markResolved() { return new WrittenBookContent(this.title, this.author, this.generation, this.pages, true); } private static Optional> resolvePage(CommandSourceStack source, @Nullable Player player, Filterable pages) { return pages.resolve(component -> { try { Component component2 = ComponentUtils.updateForEntity(source, component, player, 0); return isPageTooLarge(component2, source.registryAccess()) ? Optional.empty() : Optional.of(component2); } catch (Exception var4) { return Optional.of(component); } }); } private static boolean isPageTooLarge(Component page, HolderLookup.Provider registryAccess) { return Component.Serializer.toJson(page, registryAccess).length() > 32767; } public List getPages(boolean filtered) { return Lists.transform(this.pages, filterable -> (Component)filterable.get(filtered)); } public WrittenBookContent withReplacedPages(List> newPages) { return new WrittenBookContent(this.title, this.author, this.generation, newPages, false); } }