package net.minecraft.resources; 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 java.util.ArrayList; import java.util.List; import java.util.Optional; import net.minecraft.core.Holder; import net.minecraft.core.HolderGetter; import net.minecraft.core.HolderOwner; import net.minecraft.core.HolderSet; import net.minecraft.core.Registry; import net.minecraft.core.Holder.Direct; import net.minecraft.tags.TagKey; import net.minecraft.util.ExtraCodecs; public class HolderSetCodec implements Codec> { private final ResourceKey> registryKey; private final Codec> elementCodec; private final Codec>> homogenousListCodec; private final Codec, List>>> registryAwareCodec; private static Codec>> homogenousList(Codec> holderCodec, boolean disallowInline) { Codec>> codec = holderCodec.listOf().validate(ExtraCodecs.ensureHomogenous(Holder::kind)); return disallowInline ? codec : ExtraCodecs.compactListCodec(holderCodec, codec); } public static Codec> create(ResourceKey> registryKey, Codec> holderCodec, boolean disallowInline) { return new HolderSetCodec<>(registryKey, holderCodec, disallowInline); } private HolderSetCodec(ResourceKey> registryKey, Codec> elementCodec, boolean disallowInline) { this.registryKey = registryKey; this.elementCodec = elementCodec; this.homogenousListCodec = homogenousList(elementCodec, disallowInline); this.registryAwareCodec = Codec.either(TagKey.hashedCodec(registryKey), this.homogenousListCodec); } @Override public DataResult, T>> decode(DynamicOps dynamicOps, T object) { if (dynamicOps instanceof RegistryOps registryOps) { Optional> optional = registryOps.getter(this.registryKey); if (optional.isPresent()) { HolderGetter holderGetter = (HolderGetter)optional.get(); return this.registryAwareCodec .decode(dynamicOps, object) .flatMap( pair -> { DataResult> dataResult = ((Either)pair.getFirst()) .map(tagKey -> lookupTag(holderGetter, tagKey), list -> DataResult.success(HolderSet.direct(list))); return dataResult.map(holderSet -> Pair.of(holderSet, pair.getSecond())); } ); } } return this.decodeWithoutRegistry(dynamicOps, object); } private static DataResult> lookupTag(HolderGetter input, TagKey tagKey) { return (DataResult>)input.get(tagKey) .map(DataResult::success) .orElseGet(() -> DataResult.error(() -> "Missing tag: '" + tagKey.location() + "' in '" + tagKey.registry().location() + "'")); } public DataResult encode(HolderSet input, DynamicOps ops, T prefix) { if (ops instanceof RegistryOps registryOps) { Optional> optional = registryOps.owner(this.registryKey); if (optional.isPresent()) { if (!input.canSerializeIn((HolderOwner)optional.get())) { return DataResult.error(() -> "HolderSet " + input + " is not valid in current registry set"); } return this.registryAwareCodec.encode(input.unwrap().mapRight(List::copyOf), ops, prefix); } } return this.encodeWithoutRegistry(input, ops, prefix); } private DataResult, T>> decodeWithoutRegistry(DynamicOps ops, T input) { return this.elementCodec.listOf().decode(ops, input).flatMap(pair -> { List> list = new ArrayList(); for (Holder holder : (List)pair.getFirst()) { if (!(holder instanceof Direct direct)) { return DataResult.error(() -> "Can't decode element " + holder + " without registry"); } list.add(direct); } return DataResult.success(new Pair<>(HolderSet.direct(list), pair.getSecond())); }); } private DataResult encodeWithoutRegistry(HolderSet input, DynamicOps ops, T prefix) { return this.homogenousListCodec.encode(input.stream().toList(), ops, prefix); } }