package net.minecraft.resources; 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.Lifecycle; import java.util.Optional; import net.minecraft.core.Holder; import net.minecraft.core.HolderGetter; import net.minecraft.core.HolderOwner; import net.minecraft.core.Registry; /** * A codec that wraps a single element, or "file", within a registry. Possibly allows inline definitions, and always falls back to the element codec (and thus writing the registry element inline) if it fails to decode from the registry. */ public final class RegistryFileCodec implements Codec> { private final ResourceKey> registryKey; private final Codec elementCodec; private final boolean allowInline; /** * Creates a codec for a single registry element, which is held as an un-resolved {@code Supplier}. Both inline definitions of the object, and references to an existing registry element id are allowed. * * @param registryKey The registry which elements may belong to. * @param elementCodec The codec used to decode either inline definitions, or elements before entering them into the registry. */ public static RegistryFileCodec create(ResourceKey> registryKey, Codec elementCodec) { return create(registryKey, elementCodec, true); } public static RegistryFileCodec create(ResourceKey> registryKey, Codec elementCodec, boolean allowInline) { return new RegistryFileCodec<>(registryKey, elementCodec, allowInline); } private RegistryFileCodec(ResourceKey> registryKey, Codec elementCodec, boolean allowInline) { this.registryKey = registryKey; this.elementCodec = elementCodec; this.allowInline = allowInline; } public DataResult encode(Holder 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(() -> "Element " + input + " is not valid in current registry set"); } return input.unwrap() .map(resourceKey -> ResourceLocation.CODEC.encode(resourceKey.location(), ops, prefix), object2 -> this.elementCodec.encode((E)object2, ops, prefix)); } } return this.elementCodec.encode(input.value(), ops, prefix); } @Override public DataResult, T>> decode(DynamicOps dynamicOps, T object) { if (dynamicOps instanceof RegistryOps registryOps) { Optional> optional = registryOps.getter(this.registryKey); if (optional.isEmpty()) { return DataResult.error(() -> "Registry does not exist: " + this.registryKey); } else { HolderGetter holderGetter = (HolderGetter)optional.get(); DataResult> dataResult = ResourceLocation.CODEC.decode(dynamicOps, object); if (dataResult.result().isEmpty()) { return !this.allowInline ? DataResult.error(() -> "Inline definitions not allowed here") : this.elementCodec.decode(dynamicOps, object).map(pairx -> pairx.mapFirst(Holder::direct)); } else { Pair pair = (Pair)dataResult.result().get(); ResourceKey resourceKey = ResourceKey.create(this.registryKey, pair.getFirst()); return ((DataResult)holderGetter.get(resourceKey).map(DataResult::success).orElseGet(() -> DataResult.error(() -> "Failed to get element " + resourceKey))) ., T>>map(reference -> Pair.of(reference, pair.getSecond())) .setLifecycle(Lifecycle.stable()); } } } else { return this.elementCodec.decode(dynamicOps, object).map(pairx -> pairx.mapFirst(Holder::direct)); } } public String toString() { return "RegistryFileCodec[" + this.registryKey + " " + this.elementCodec + "]"; } }