package net.minecraft.core; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.Lifecycle; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import net.minecraft.core.Cloner.Factory; import net.minecraft.core.Holder.Reference; import net.minecraft.core.Holder.Reference.Type; import net.minecraft.core.HolderLookup.RegistryLookup.Delegate; import net.minecraft.core.HolderSet.Named; import net.minecraft.core.RegistrySetBuilder.3.1; import net.minecraft.data.worldgen.BootstrapContext; import net.minecraft.resources.RegistryOps; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.TagKey; import org.apache.commons.lang3.mutable.MutableObject; import org.jetbrains.annotations.Nullable; public class RegistrySetBuilder { private final List> entries = new ArrayList(); static HolderGetter wrapContextLookup(HolderLookup.RegistryLookup owner) { return new RegistrySetBuilder.EmptyTagLookup(owner) { @Override public Optional> get(ResourceKey resourceKey) { return owner.get(resourceKey); } }; } static HolderLookup.RegistryLookup lookupFromMap( ResourceKey> registryKey, Lifecycle registryLifecycle, HolderOwner owner, Map, Reference> elements ) { return new RegistrySetBuilder.EmptyTagRegistryLookup(owner) { @Override public ResourceKey> key() { return registryKey; } @Override public Lifecycle registryLifecycle() { return registryLifecycle; } @Override public Optional> get(ResourceKey resourceKey) { return Optional.ofNullable((Reference)elements.get(resourceKey)); } @Override public Stream> listElements() { return elements.values().stream(); } }; } public RegistrySetBuilder add(ResourceKey> key, Lifecycle lifecycle, RegistrySetBuilder.RegistryBootstrap bootstrap) { this.entries.add(new RegistrySetBuilder.RegistryStub<>(key, lifecycle, bootstrap)); return this; } public RegistrySetBuilder add(ResourceKey> key, RegistrySetBuilder.RegistryBootstrap bootstrap) { return this.add(key, Lifecycle.stable(), bootstrap); } private RegistrySetBuilder.BuildState createState(RegistryAccess registryAccess) { RegistrySetBuilder.BuildState buildState = RegistrySetBuilder.BuildState.create( registryAccess, this.entries.stream().map(RegistrySetBuilder.RegistryStub::key) ); this.entries.forEach(registryStub -> registryStub.apply(buildState)); return buildState; } private static HolderLookup.Provider buildProviderWithContext( RegistrySetBuilder.UniversalOwner owner, RegistryAccess registryAccess, Stream> lookups ) { record Entry(HolderLookup.RegistryLookup lookup, RegistryOps.RegistryInfo opsInfo) { public static Entry createForContextRegistry(HolderLookup.RegistryLookup lookup) { return new Entry<>(new RegistrySetBuilder.EmptyTagLookupWrapper<>(lookup, lookup), RegistryOps.RegistryInfo.fromRegistryLookup(lookup)); } public static Entry createForNewRegistry(RegistrySetBuilder.UniversalOwner owner, HolderLookup.RegistryLookup lookup) { return new Entry<>( new RegistrySetBuilder.EmptyTagLookupWrapper<>(owner.cast(), lookup), new RegistryOps.RegistryInfo<>(owner.cast(), lookup, lookup.registryLifecycle()) ); } } final Map>, Entry> map = new HashMap(); registryAccess.registries().forEach(registryEntry -> map.put(registryEntry.key(), Entry.createForContextRegistry(registryEntry.value()))); lookups.forEach(registryLookup -> map.put(registryLookup.key(), Entry.createForNewRegistry(owner, registryLookup))); return new HolderLookup.Provider() { @Override public Stream>> listRegistryKeys() { return map.keySet().stream(); } Optional> getEntry(ResourceKey> registryKey) { return Optional.ofNullable((Entry)map.get(registryKey)); } @Override public Optional> lookup(ResourceKey> registryKey) { return this.getEntry(registryKey).map(Entry::lookup); } @Override public RegistryOps createSerializationContext(DynamicOps ops) { return RegistryOps.create(ops, new 1(this)); } }; } public HolderLookup.Provider build(RegistryAccess registryAccess) { RegistrySetBuilder.BuildState buildState = this.createState(registryAccess); Stream> stream = this.entries .stream() .map(registryStub -> registryStub.collectRegisteredValues(buildState).buildAsLookup(buildState.owner)); HolderLookup.Provider provider = buildProviderWithContext(buildState.owner, registryAccess, stream); buildState.reportNotCollectedHolders(); buildState.reportUnclaimedRegisteredValues(); buildState.throwOnError(); return provider; } private HolderLookup.Provider createLazyFullPatchedRegistries( RegistryAccess registry, HolderLookup.Provider lookupProvider, Factory clonerFactory, Map>, RegistrySetBuilder.RegistryContents> registries, HolderLookup.Provider registryLookupProvider ) { RegistrySetBuilder.UniversalOwner universalOwner = new RegistrySetBuilder.UniversalOwner(); MutableObject mutableObject = new MutableObject<>(); List> list = (List>)registries.keySet() .stream() .map(resourceKey -> this.createLazyFullPatchedRegistries(universalOwner, clonerFactory, resourceKey, registryLookupProvider, lookupProvider, mutableObject)) .collect(Collectors.toUnmodifiableList()); HolderLookup.Provider provider = buildProviderWithContext(universalOwner, registry, list.stream()); mutableObject.setValue(provider); return provider; } private HolderLookup.RegistryLookup createLazyFullPatchedRegistries( HolderOwner owner, Factory clonerFactory, ResourceKey> registryKey, HolderLookup.Provider registryLookupProvider, HolderLookup.Provider lookupProvider, MutableObject object ) { Cloner cloner = clonerFactory.cloner(registryKey); if (cloner == null) { throw new NullPointerException("No cloner for " + registryKey.location()); } else { Map, Reference> map = new HashMap(); HolderLookup.RegistryLookup registryLookup = registryLookupProvider.lookupOrThrow(registryKey); registryLookup.listElements().forEach(reference -> { ResourceKey resourceKey = reference.key(); RegistrySetBuilder.LazyHolder lazyHolder = new RegistrySetBuilder.LazyHolder<>(owner, resourceKey); lazyHolder.supplier = () -> cloner.clone((T)reference.value(), registryLookupProvider, object.getValue()); map.put(resourceKey, lazyHolder); }); HolderLookup.RegistryLookup registryLookup2 = lookupProvider.lookupOrThrow(registryKey); registryLookup2.listElements().forEach(reference -> { ResourceKey resourceKey = reference.key(); map.computeIfAbsent(resourceKey, resourceKey2 -> { RegistrySetBuilder.LazyHolder lazyHolder = new RegistrySetBuilder.LazyHolder<>(owner, resourceKey); lazyHolder.supplier = () -> cloner.clone((T)reference.value(), lookupProvider, object.getValue()); return lazyHolder; }); }); Lifecycle lifecycle = registryLookup.registryLifecycle().add(registryLookup2.registryLifecycle()); return lookupFromMap(registryKey, lifecycle, owner, map); } } public RegistrySetBuilder.PatchedRegistries buildPatch(RegistryAccess registryAccess, HolderLookup.Provider lookupProvider, Factory clonerFactory) { RegistrySetBuilder.BuildState buildState = this.createState(registryAccess); Map>, RegistrySetBuilder.RegistryContents> map = new HashMap(); this.entries .stream() .map(registryStub -> registryStub.collectRegisteredValues(buildState)) .forEach(registryContents -> map.put(registryContents.key, registryContents)); Set>> set = (Set>>)registryAccess.listRegistryKeys() .collect(Collectors.toUnmodifiableSet()); lookupProvider.listRegistryKeys() .filter(resourceKey -> !set.contains(resourceKey)) .forEach(resourceKey -> map.putIfAbsent(resourceKey, new RegistrySetBuilder.RegistryContents(resourceKey, Lifecycle.stable(), Map.of()))); Stream> stream = map.values().stream().map(registryContents -> registryContents.buildAsLookup(buildState.owner)); HolderLookup.Provider provider = buildProviderWithContext(buildState.owner, registryAccess, stream); buildState.reportUnclaimedRegisteredValues(); buildState.throwOnError(); HolderLookup.Provider provider2 = this.createLazyFullPatchedRegistries(registryAccess, lookupProvider, clonerFactory, map, provider); return new RegistrySetBuilder.PatchedRegistries(provider2, provider); } record BuildState( RegistrySetBuilder.UniversalOwner owner, RegistrySetBuilder.UniversalLookup lookup, Map> registries, Map, RegistrySetBuilder.RegisteredValue> registeredValues, List errors ) { public static RegistrySetBuilder.BuildState create(RegistryAccess registryAccess, Stream>> registries) { RegistrySetBuilder.UniversalOwner universalOwner = new RegistrySetBuilder.UniversalOwner(); List list = new ArrayList(); RegistrySetBuilder.UniversalLookup universalLookup = new RegistrySetBuilder.UniversalLookup(universalOwner); Builder> builder = ImmutableMap.builder(); registryAccess.registries() .forEach(registryEntry -> builder.put(registryEntry.key().location(), RegistrySetBuilder.wrapContextLookup(registryEntry.value()))); registries.forEach(resourceKey -> builder.put(resourceKey.location(), universalLookup)); return new RegistrySetBuilder.BuildState(universalOwner, universalLookup, builder.build(), new HashMap(), list); } public BootstrapContext bootstrapContext() { return new net.minecraft.core.RegistrySetBuilder.BuildState.1(this); } public void reportUnclaimedRegisteredValues() { this.registeredValues .forEach((resourceKey, registeredValue) -> this.errors.add(new IllegalStateException("Orpaned value " + registeredValue.value + " for key " + resourceKey))); } public void reportNotCollectedHolders() { for (ResourceKey resourceKey : this.lookup.holders.keySet()) { this.errors.add(new IllegalStateException("Unreferenced key: " + resourceKey)); } } public void throwOnError() { if (!this.errors.isEmpty()) { IllegalStateException illegalStateException = new IllegalStateException("Errors during registry creation"); for (RuntimeException runtimeException : this.errors) { illegalStateException.addSuppressed(runtimeException); } throw illegalStateException; } } } abstract static class EmptyTagLookup implements HolderGetter { protected final HolderOwner owner; protected EmptyTagLookup(HolderOwner owner) { this.owner = owner; } @Override public Optional> get(TagKey tagKey) { return Optional.of(HolderSet.emptyNamed(this.owner, tagKey)); } } static class EmptyTagLookupWrapper extends RegistrySetBuilder.EmptyTagRegistryLookup implements Delegate { private final HolderLookup.RegistryLookup parent; EmptyTagLookupWrapper(HolderOwner owner, HolderLookup.RegistryLookup parent) { super(owner); this.parent = parent; } public HolderLookup.RegistryLookup parent() { return this.parent; } } abstract static class EmptyTagRegistryLookup extends RegistrySetBuilder.EmptyTagLookup implements HolderLookup.RegistryLookup { protected EmptyTagRegistryLookup(HolderOwner holderOwner) { super(holderOwner); } @Override public Stream> listTags() { throw new UnsupportedOperationException("Tags are not available in datagen"); } } static class LazyHolder extends Reference { @Nullable Supplier supplier; protected LazyHolder(HolderOwner owner, @Nullable ResourceKey key) { super(Type.STAND_ALONE, owner, key, null); } @Override protected void bindValue(T value) { super.bindValue(value); this.supplier = null; } @Override public T value() { if (this.supplier != null) { this.bindValue((T)this.supplier.get()); } return super.value(); } } public record PatchedRegistries(HolderLookup.Provider full, HolderLookup.Provider patches) { } record RegisteredValue(T value, Lifecycle lifecycle) { } @FunctionalInterface public interface RegistryBootstrap { void run(BootstrapContext bootstrapContext); } record RegistryContents( ResourceKey> key, Lifecycle lifecycle, Map, RegistrySetBuilder.ValueAndHolder> values ) { public HolderLookup.RegistryLookup buildAsLookup(RegistrySetBuilder.UniversalOwner owner) { Map, Reference> map = (Map, Reference>)this.values .entrySet() .stream() .collect(Collectors.toUnmodifiableMap(java.util.Map.Entry::getKey, entry -> { RegistrySetBuilder.ValueAndHolder valueAndHolder = (RegistrySetBuilder.ValueAndHolder)entry.getValue(); Reference reference = (Reference)valueAndHolder.holder().orElseGet(() -> Reference.createStandAlone(owner.cast(), (ResourceKey)entry.getKey())); reference.bindValue(valueAndHolder.value().value()); return reference; })); return RegistrySetBuilder.lookupFromMap(this.key, this.lifecycle, owner.cast(), map); } } record RegistryStub(ResourceKey> key, Lifecycle lifecycle, RegistrySetBuilder.RegistryBootstrap bootstrap) { void apply(RegistrySetBuilder.BuildState state) { this.bootstrap.run(state.bootstrapContext()); } public RegistrySetBuilder.RegistryContents collectRegisteredValues(RegistrySetBuilder.BuildState buildState) { Map, RegistrySetBuilder.ValueAndHolder> map = new HashMap(); Iterator, RegistrySetBuilder.RegisteredValue>> iterator = buildState.registeredValues.entrySet().iterator(); while (iterator.hasNext()) { java.util.Map.Entry, RegistrySetBuilder.RegisteredValue> entry = (java.util.Map.Entry, RegistrySetBuilder.RegisteredValue>)iterator.next(); ResourceKey resourceKey = (ResourceKey)entry.getKey(); if (resourceKey.isFor(this.key)) { RegistrySetBuilder.RegisteredValue registeredValue = (RegistrySetBuilder.RegisteredValue)entry.getValue(); Reference reference = (Reference)buildState.lookup.holders.remove(resourceKey); map.put(resourceKey, new RegistrySetBuilder.ValueAndHolder<>(registeredValue, Optional.ofNullable(reference))); iterator.remove(); } } return new RegistrySetBuilder.RegistryContents<>(this.key, this.lifecycle, map); } } static class UniversalLookup extends RegistrySetBuilder.EmptyTagLookup { final Map, Reference> holders = new HashMap(); public UniversalLookup(HolderOwner holderOwner) { super(holderOwner); } @Override public Optional> get(ResourceKey resourceKey) { return Optional.of(this.getOrCreate(resourceKey)); } Reference getOrCreate(ResourceKey key) { return (Reference)this.holders.computeIfAbsent(key, resourceKey -> Reference.createStandAlone(this.owner, resourceKey)); } } static class UniversalOwner implements HolderOwner { public HolderOwner cast() { return this; } } record ValueAndHolder(RegistrySetBuilder.RegisteredValue value, Optional> holder) { } }