package net.minecraft.network.chat; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import com.google.gson.JsonParser; import com.mojang.datafixers.util.Either; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.JsonOps; import com.mojang.serialization.MapCodec; import com.mojang.serialization.MapDecoder; import com.mojang.serialization.MapEncoder; import com.mojang.serialization.MapLike; import com.mojang.serialization.RecordBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder; import io.netty.buffer.ByteBuf; import java.util.List; import java.util.Optional; import java.util.function.Function; import java.util.stream.Stream; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.contents.KeybindContents; import net.minecraft.network.chat.contents.NbtContents; import net.minecraft.network.chat.contents.PlainTextContents; import net.minecraft.network.chat.contents.ScoreContents; import net.minecraft.network.chat.contents.SelectorContents; import net.minecraft.network.chat.contents.TranslatableContents; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.RegistryOps; import net.minecraft.util.ExtraCodecs; import net.minecraft.util.GsonHelper; 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> OPTIONAL_STREAM_CODEC = STREAM_CODEC.apply(ByteBufCodecs::optional); public static final StreamCodec TRUSTED_STREAM_CODEC = ByteBufCodecs.fromCodecWithRegistriesTrusted(CODEC); public static final StreamCodec> TRUSTED_OPTIONAL_STREAM_CODEC = TRUSTED_STREAM_CODEC.apply( ByteBufCodecs::optional ); public static final StreamCodec TRUSTED_CONTEXT_FREE_STREAM_CODEC = ByteBufCodecs.fromCodecTrusted(CODEC); public static final Codec FLAT_CODEC = flatCodec(Integer.MAX_VALUE); public static Codec flatCodec(int maxSize) { final Codec codec = Codec.string(0, maxSize); return new Codec() { @Override public DataResult> decode(DynamicOps dynamicOps, T object) { DynamicOps dynamicOps2 = asJsonOps(dynamicOps); return codec.decode(dynamicOps, object).flatMap(pair -> { try { JsonElement jsonElement = JsonParser.parseString((String)pair.getFirst()); return ComponentSerialization.CODEC.parse(dynamicOps2, jsonElement).map(component -> Pair.of(component, pair.getSecond())); } catch (JsonParseException var3x) { return DataResult.error(var3x::getMessage); } }); } public DataResult encode(Component input, DynamicOps ops, T value) { DynamicOps dynamicOps = asJsonOps(ops); return ComponentSerialization.CODEC.encodeStart(dynamicOps, input).flatMap(jsonElement -> { try { return codec.encodeStart(ops, GsonHelper.toStableString(jsonElement)); } catch (IllegalArgumentException var4x) { return DataResult.error(var4x::getMessage); } }); } private static DynamicOps asJsonOps(DynamicOps ops) { return (DynamicOps)(ops instanceof RegistryOps registryOps ? registryOps.withParent(JsonOps.INSTANCE) : JsonOps.INSTANCE); } }; } private static MutableComponent createFromList(List components) { MutableComponent mutableComponent = ((Component)components.get(0)).copy(); for (int i = 1; i < components.size(); i++) { mutableComponent.append((Component)components.get(i)); } return mutableComponent; } public static MapCodec createLegacyComponentMatcher( T[] types, Function> codecGetter, Function typeGetter, String typeFieldName ) { MapCodec mapCodec = new ComponentSerialization.FuzzyCodec( Stream.of(types).map(codecGetter).toList(), object -> (MapEncoder)codecGetter.apply((StringRepresentable)typeGetter.apply(object)) ); Codec codec = StringRepresentable.fromValues(() -> types); MapCodec mapCodec2 = codec.dispatchMap(typeFieldName, typeGetter, codecGetter); MapCodec mapCodec3 = new ComponentSerialization.StrictEither<>(typeFieldName, mapCodec2, mapCodec); return ExtraCodecs.orCompressed(mapCodec3, mapCodec2); } private static Codec createCodec(Codec codec) { ComponentContents.Type[] types = new ComponentContents.Type[]{ PlainTextContents.TYPE, TranslatableContents.TYPE, KeybindContents.TYPE, ScoreContents.TYPE, SelectorContents.TYPE, NbtContents.TYPE }; MapCodec mapCodec = createLegacyComponentMatcher(types, ComponentContents.Type::codec, ComponentContents::type, "type"); Codec codec2 = RecordCodecBuilder.create( instance -> instance.group( mapCodec.forGetter(Component::getContents), ExtraCodecs.nonEmptyList(codec.listOf()).optionalFieldOf("extra", List.of()).forGetter(Component::getSiblings), Style.Serializer.MAP_CODEC.forGetter(Component::getStyle) ) .apply(instance, MutableComponent::new) ); return Codec.either(Codec.either(Codec.STRING, ExtraCodecs.nonEmptyList(codec.listOf())), codec2) .xmap(either -> either.map(eitherx -> eitherx.map(Component::literal, ComponentSerialization::createFromList), component -> component), component -> { String string = component.tryCollapseToString(); return string != null ? Either.left(Either.left(string)) : Either.right(component); }); } static class FuzzyCodec extends MapCodec { private final List> codecs; private final Function> encoderGetter; public FuzzyCodec(List> codecs, Function> encoderGetter) { this.codecs = codecs; this.encoderGetter = encoderGetter; } @Override public DataResult decode(DynamicOps dynamicOps, MapLike mapLike) { for (MapDecoder mapDecoder : this.codecs) { DataResult dataResult = mapDecoder.decode(dynamicOps, mapLike); if (dataResult.result().isPresent()) { return (DataResult)dataResult; } } return DataResult.error(() -> "No matching codec found"); } @Override public RecordBuilder encode(T object, DynamicOps dynamicOps, RecordBuilder recordBuilder) { MapEncoder mapEncoder = (MapEncoder)this.encoderGetter.apply(object); return mapEncoder.encode(object, dynamicOps, recordBuilder); } @Override public Stream keys(DynamicOps dynamicOps) { return this.codecs.stream().flatMap(mapCodec -> mapCodec.keys(dynamicOps)).distinct(); } public String toString() { return "FuzzyCodec[" + this.codecs + "]"; } } static class StrictEither extends MapCodec { private final String typeFieldName; private final MapCodec typed; private final MapCodec fuzzy; public StrictEither(String typeFieldName, MapCodec typed, MapCodec fuzzy) { this.typeFieldName = typeFieldName; this.typed = typed; this.fuzzy = fuzzy; } @Override public DataResult decode(DynamicOps dynamicOps, MapLike mapLike) { return mapLike.get(this.typeFieldName) != null ? this.typed.decode(dynamicOps, mapLike) : this.fuzzy.decode(dynamicOps, mapLike); } @Override public RecordBuilder encode(T object, DynamicOps dynamicOps, RecordBuilder recordBuilder) { return this.fuzzy.encode(object, dynamicOps, recordBuilder); } @Override public Stream keys(DynamicOps dynamicOps) { return Stream.concat(this.typed.keys(dynamicOps), this.fuzzy.keys(dynamicOps)).distinct(); } } }