package net.minecraft.core; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterators; import com.google.common.collect.ImmutableMap.Builder; import com.mojang.serialization.Lifecycle; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectList; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.Map.Entry; import java.util.function.BiConsumer; import java.util.stream.Stream; import javax.annotation.Nullable; import net.minecraft.Util; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.TagKey; import net.minecraft.tags.TagLoader; import net.minecraft.util.RandomSource; public class MappedRegistry implements WritableRegistry { private final ResourceKey> key; private final ObjectList> byId = new ObjectArrayList<>(256); private final Reference2IntMap toId = Util.make(new Reference2IntOpenHashMap<>(), p_308420_ -> p_308420_.defaultReturnValue(-1)); private final Map> byLocation = new HashMap<>(); private final Map, Holder.Reference> byKey = new HashMap<>(); private final Map> byValue = new IdentityHashMap<>(); private final Map, RegistrationInfo> registrationInfos = new IdentityHashMap<>(); private Lifecycle registryLifecycle; private final Map, HolderSet.Named> frozenTags = new IdentityHashMap<>(); MappedRegistry.TagSet allTags = MappedRegistry.TagSet.unbound(); private boolean frozen; @Nullable private Map> unregisteredIntrusiveHolders; @Override public Stream> listTags() { return this.getTags(); } public MappedRegistry(ResourceKey> p_249899_, Lifecycle p_252249_) { this(p_249899_, p_252249_, false); } public MappedRegistry(ResourceKey> p_252132_, Lifecycle p_249215_, boolean p_251014_) { this.key = p_252132_; this.registryLifecycle = p_249215_; if (p_251014_) { this.unregisteredIntrusiveHolders = new IdentityHashMap<>(); } } @Override public ResourceKey> key() { return this.key; } @Override public String toString() { return "Registry[" + this.key + " (" + this.registryLifecycle + ")]"; } private void validateWrite() { if (this.frozen) { throw new IllegalStateException("Registry is already frozen"); } } private void validateWrite(ResourceKey p_205922_) { if (this.frozen) { throw new IllegalStateException("Registry is already frozen (trying to add key " + p_205922_ + ")"); } } @Override public Holder.Reference register(ResourceKey p_256252_, T p_256591_, RegistrationInfo p_329661_) { this.validateWrite(p_256252_); Objects.requireNonNull(p_256252_); Objects.requireNonNull(p_256591_); if (this.byLocation.containsKey(p_256252_.location())) { throw (IllegalStateException)Util.pauseInIde(new IllegalStateException("Adding duplicate key '" + p_256252_ + "' to registry")); } else if (this.byValue.containsKey(p_256591_)) { throw (IllegalStateException)Util.pauseInIde(new IllegalStateException("Adding duplicate value '" + p_256591_ + "' to registry")); } else { Holder.Reference reference; if (this.unregisteredIntrusiveHolders != null) { reference = this.unregisteredIntrusiveHolders.remove(p_256591_); if (reference == null) { throw new AssertionError("Missing intrusive holder for " + p_256252_ + ":" + p_256591_); } reference.bindKey(p_256252_); } else { reference = this.byKey.computeIfAbsent(p_256252_, p_358082_ -> Holder.Reference.createStandAlone(this, (ResourceKey)p_358082_)); } this.byKey.put(p_256252_, reference); this.byLocation.put(p_256252_.location(), reference); this.byValue.put(p_256591_, reference); int i = this.byId.size(); this.byId.add(reference); this.toId.put(p_256591_, i); this.registrationInfos.put(p_256252_, p_329661_); this.registryLifecycle = this.registryLifecycle.add(p_329661_.lifecycle()); return reference; } } @Nullable @Override public ResourceLocation getKey(T p_122746_) { Holder.Reference reference = this.byValue.get(p_122746_); return reference != null ? reference.key().location() : null; } @Override public Optional> getResourceKey(T p_122755_) { return Optional.ofNullable(this.byValue.get(p_122755_)).map(Holder.Reference::key); } @Override public int getId(@Nullable T p_122706_) { return this.toId.getInt(p_122706_); } @Nullable @Override public T getValue(@Nullable ResourceKey p_122714_) { return getValueFromNullable(this.byKey.get(p_122714_)); } @Nullable @Override public T byId(int p_122684_) { return p_122684_ >= 0 && p_122684_ < this.byId.size() ? this.byId.get(p_122684_).value() : null; } @Override public Optional> get(int p_205907_) { return p_205907_ >= 0 && p_205907_ < this.byId.size() ? Optional.ofNullable(this.byId.get(p_205907_)) : Optional.empty(); } @Override public Optional> get(ResourceLocation p_333710_) { return Optional.ofNullable(this.byLocation.get(p_333710_)); } @Override public Optional> get(ResourceKey p_205905_) { return Optional.ofNullable(this.byKey.get(p_205905_)); } @Override public Optional> getAny() { return this.byId.isEmpty() ? Optional.empty() : Optional.of(this.byId.getFirst()); } @Override public Holder wrapAsHolder(T p_263356_) { Holder.Reference reference = this.byValue.get(p_263356_); return (Holder)(reference != null ? reference : Holder.direct(p_263356_)); } Holder.Reference getOrCreateHolderOrThrow(ResourceKey p_248831_) { return this.byKey.computeIfAbsent(p_248831_, p_358081_ -> { if (this.unregisteredIntrusiveHolders != null) { throw new IllegalStateException("This registry can't create new holders without value"); } else { this.validateWrite((ResourceKey)p_358081_); return Holder.Reference.createStandAlone(this, (ResourceKey)p_358081_); } }); } @Override public int size() { return this.byKey.size(); } @Override public Optional registrationInfo(ResourceKey p_331530_) { return Optional.ofNullable(this.registrationInfos.get(p_331530_)); } @Override public Lifecycle registryLifecycle() { return this.registryLifecycle; } @Override public Iterator iterator() { return Iterators.transform(this.byId.iterator(), Holder::value); } @Nullable @Override public T getValue(@Nullable ResourceLocation p_122739_) { Holder.Reference reference = this.byLocation.get(p_122739_); return getValueFromNullable(reference); } @Nullable private static T getValueFromNullable(@Nullable Holder.Reference p_205866_) { return p_205866_ != null ? p_205866_.value() : null; } @Override public Set keySet() { return Collections.unmodifiableSet(this.byLocation.keySet()); } @Override public Set> registryKeySet() { return Collections.unmodifiableSet(this.byKey.keySet()); } @Override public Set, T>> entrySet() { return Collections.unmodifiableSet(Util.mapValuesLazy(this.byKey, Holder::value).entrySet()); } @Override public Stream> listElements() { return this.byId.stream(); } @Override public Stream> getTags() { return this.allTags.getTags(); } HolderSet.Named getOrCreateTagForRegistration(TagKey p_363833_) { return this.frozenTags.computeIfAbsent(p_363833_, this::createTag); } private HolderSet.Named createTag(TagKey p_211068_) { return new HolderSet.Named<>(this, p_211068_); } @Override public boolean isEmpty() { return this.byKey.isEmpty(); } @Override public Optional> getRandom(RandomSource p_235716_) { return Util.getRandomSafe(this.byId, p_235716_); } @Override public boolean containsKey(ResourceLocation p_122761_) { return this.byLocation.containsKey(p_122761_); } @Override public boolean containsKey(ResourceKey p_175392_) { return this.byKey.containsKey(p_175392_); } @Override public Registry freeze() { if (this.frozen) { return this; } else { this.frozen = true; this.byValue.forEach((p_247989_, p_247990_) -> p_247990_.bindValue((T)p_247989_)); List list = this.byKey .entrySet() .stream() .filter(p_211055_ -> !p_211055_.getValue().isBound()) .map(p_211794_ -> p_211794_.getKey().location()) .sorted() .toList(); if (!list.isEmpty()) { throw new IllegalStateException("Unbound values in registry " + this.key() + ": " + list); } else { if (this.unregisteredIntrusiveHolders != null) { if (!this.unregisteredIntrusiveHolders.isEmpty()) { throw new IllegalStateException("Some intrusive holders were not registered: " + this.unregisteredIntrusiveHolders.values()); } this.unregisteredIntrusiveHolders = null; } if (this.allTags.isBound()) { throw new IllegalStateException("Tags already present before freezing"); } else { List list1 = this.frozenTags .entrySet() .stream() .filter(p_358086_ -> !p_358086_.getValue().isBound()) .map(p_358087_ -> p_358087_.getKey().location()) .sorted() .toList(); if (!list1.isEmpty()) { throw new IllegalStateException("Unbound tags in registry " + this.key() + ": " + list1); } else { this.allTags = MappedRegistry.TagSet.fromMap(this.frozenTags); this.refreshTagsInHolders(); return this; } } } } } @Override public Holder.Reference createIntrusiveHolder(T p_205915_) { if (this.unregisteredIntrusiveHolders == null) { throw new IllegalStateException("This registry can't create intrusive holders"); } else { this.validateWrite(); return this.unregisteredIntrusiveHolders.computeIfAbsent(p_205915_, p_358092_ -> Holder.Reference.createIntrusive(this, (T)p_358092_)); } } @Override public Optional> get(TagKey p_365729_) { return this.allTags.get(p_365729_); } private Holder.Reference validateAndUnwrapTagElement(TagKey p_363318_, Holder p_362647_) { if (!p_362647_.canSerializeIn(this)) { throw new IllegalStateException("Can't create named set " + p_363318_ + " containing value " + p_362647_ + " from outside registry " + this); } else if (p_362647_ instanceof Holder.Reference reference) { return reference; } else { throw new IllegalStateException("Found direct holder " + p_362647_ + " value in tag " + p_363318_); } } @Override public void bindTag(TagKey p_361998_, List> p_361891_) { this.validateWrite(); this.getOrCreateTagForRegistration(p_361998_).bind(p_361891_); } void refreshTagsInHolders() { Map, List>> map = new IdentityHashMap<>(); this.byKey.values().forEach(p_211801_ -> map.put((Holder.Reference)p_211801_, new ArrayList<>())); this.allTags.forEach((p_358084_, p_358085_) -> { for (Holder holder : p_358085_) { Holder.Reference reference = this.validateAndUnwrapTagElement((TagKey)p_358084_, holder); map.get(reference).add((TagKey)p_358084_); } }); map.forEach(Holder.Reference::bindTags); } public void bindAllTagsToEmpty() { this.validateWrite(); this.frozenTags.values().forEach(p_211792_ -> p_211792_.bind(List.of())); } @Override public HolderGetter createRegistrationLookup() { this.validateWrite(); return new HolderGetter() { @Override public Optional> get(ResourceKey p_255624_) { return Optional.of(this.getOrThrow(p_255624_)); } @Override public Holder.Reference getOrThrow(ResourceKey p_362418_) { return MappedRegistry.this.getOrCreateHolderOrThrow(p_362418_); } @Override public Optional> get(TagKey p_256277_) { return Optional.of(this.getOrThrow(p_256277_)); } @Override public HolderSet.Named getOrThrow(TagKey p_365508_) { return MappedRegistry.this.getOrCreateTagForRegistration(p_365508_); } }; } @Override public Registry.PendingTags prepareTagReload(TagLoader.LoadResult p_368827_) { if (!this.frozen) { throw new IllegalStateException("Invalid method used for tag loading"); } else { Builder, HolderSet.Named> builder = ImmutableMap.builder(); final Map, List>> map = new HashMap<>(); p_368827_.tags().forEach((p_358090_, p_358091_) -> { HolderSet.Named named = this.frozenTags.get(p_358090_); if (named == null) { named = this.createTag((TagKey)p_358090_); } builder.put((TagKey)p_358090_, named); map.put((TagKey)p_358090_, List.copyOf(p_358091_)); }); final ImmutableMap, HolderSet.Named> immutablemap = builder.build(); final HolderLookup.RegistryLookup registrylookup = new HolderLookup.RegistryLookup.Delegate() { @Override public HolderLookup.RegistryLookup parent() { return MappedRegistry.this; } @Override public Optional> get(TagKey p_259486_) { return Optional.ofNullable(immutablemap.get(p_259486_)); } @Override public Stream> listTags() { return immutablemap.values().stream(); } }; return new Registry.PendingTags() { @Override public ResourceKey> key() { return MappedRegistry.this.key(); } @Override public int size() { return map.size(); } @Override public HolderLookup.RegistryLookup lookup() { return registrylookup; } @Override public void apply() { immutablemap.forEach((p_363053_, p_363988_) -> { List> list = map.getOrDefault(p_363053_, List.of()); p_363988_.bind(list); }); MappedRegistry.this.allTags = MappedRegistry.TagSet.fromMap(immutablemap); MappedRegistry.this.refreshTagsInHolders(); } }; } } interface TagSet { static MappedRegistry.TagSet unbound() { return new MappedRegistry.TagSet() { @Override public boolean isBound() { return false; } @Override public Optional> get(TagKey p_369929_) { throw new IllegalStateException("Tags not bound, trying to access " + p_369929_); } @Override public void forEach(BiConsumer, ? super HolderSet.Named> p_367948_) { throw new IllegalStateException("Tags not bound"); } @Override public Stream> getTags() { throw new IllegalStateException("Tags not bound"); } }; } static MappedRegistry.TagSet fromMap(final Map, HolderSet.Named> p_364506_) { return new MappedRegistry.TagSet() { @Override public boolean isBound() { return true; } @Override public Optional> get(TagKey p_364348_) { return Optional.ofNullable(p_364506_.get(p_364348_)); } @Override public void forEach(BiConsumer, ? super HolderSet.Named> p_366521_) { p_364506_.forEach(p_366521_); } @Override public Stream> getTags() { return p_364506_.values().stream(); } }; } boolean isBound(); Optional> get(TagKey p_368240_); void forEach(BiConsumer, ? super HolderSet.Named> p_366969_); Stream> getTags(); } }