Files
MinecraftForge/src/main/java/net/minecraftforge/registries/NamespacedWrapper.java
T

510 lines
17 KiB
Java
Raw Normal View History

/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
2017-06-19 15:02:18 -07:00
package net.minecraftforge.registries;
2022-11-22 13:32:47 -06:00
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import com.mojang.datafixers.util.Pair;
import com.mojang.logging.LogUtils;
import com.mojang.serialization.Lifecycle;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectList;
import net.minecraft.Util;
2022-03-01 14:59:27 -06:00
import net.minecraft.core.Holder;
2022-11-22 13:32:47 -06:00
import net.minecraft.core.HolderGetter;
import net.minecraft.core.HolderSet;
2022-11-22 13:32:47 -06:00
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.Registry;
2021-07-17 00:08:49 -05:00
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
2022-06-07 15:31:26 -05:00
import net.minecraft.util.RandomSource;
2019-05-23 16:02:15 -07:00
import org.apache.commons.lang3.Validate;
2022-11-22 13:32:47 -06:00
import org.jetbrains.annotations.NotNull;
2022-06-07 15:31:26 -05:00
import org.jetbrains.annotations.Nullable;
2022-11-22 13:32:47 -06:00
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
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.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
2020-06-19 10:58:45 -04:00
2022-11-22 13:32:47 -06:00
class NamespacedWrapper<T> extends MappedRegistry<T> implements ILockableRegistry
2017-06-19 15:02:18 -07:00
{
2022-11-22 13:32:47 -06:00
static final Logger LOGGER = LogUtils.getLogger();
private final ForgeRegistry<T> delegate;
2022-11-22 13:32:47 -06:00
@Nullable
private final Function<T, Holder.Reference<T>> intrusiveHolderCallback;
private final Multimap<TagKey<T>, Supplier<T>> optionalTags = Multimaps.newSetMultimap(new IdentityHashMap<>(), HashSet::new);
boolean locked = false;
Lifecycle registryLifecycle = Lifecycle.stable();
2022-11-22 13:32:47 -06:00
private boolean frozen = false; // Frozen is vanilla's variant of locked, but it can be unfrozen
private List<Holder.Reference<T>> holdersSorted;
private ObjectList<Holder.Reference<T>> holdersById = new ObjectArrayList<>(256);
private Map<ResourceLocation, Holder.Reference<T>> holdersByName = new HashMap<>();
private Map<T, Holder.Reference<T>> holders = new IdentityHashMap<>();
private RegistryManager stage;
private volatile Map<TagKey<T>, HolderSet.Named<T>> tags = new IdentityHashMap<>();
NamespacedWrapper(ForgeRegistry<T> fowner, Function<T, Holder.Reference<T>> intrusiveHolderCallback, RegistryManager stage)
2017-06-19 15:02:18 -07:00
{
super(fowner.getRegistryKey(), Lifecycle.stable(), intrusiveHolderCallback != null);
2022-11-22 13:32:47 -06:00
this.delegate = fowner;
this.intrusiveHolderCallback = intrusiveHolderCallback;
this.stage = stage;
2017-06-19 15:02:18 -07:00
}
@Override
2022-11-22 13:32:47 -06:00
public Holder.Reference<T> registerMapping(int id, ResourceKey<T> key, T value, Lifecycle lifecycle)
2017-06-19 15:02:18 -07:00
{
if (locked)
throw new IllegalStateException("Can not register to a locked registry. Modder should use Forge Register methods.");
Validate.notNull(value);
markKnown();
2022-11-22 13:32:47 -06:00
this.registryLifecycle = this.registryLifecycle.add(lifecycle);
2022-06-07 15:31:26 -05:00
int realId = this.delegate.add(id, key.location(), value);
2017-06-19 15:02:18 -07:00
if (realId != id && id != -1)
2022-06-07 15:31:26 -05:00
LOGGER.warn("Registered object did not get ID it asked for. Name: {} Expected: {} Got: {}", key, id, realId);
2022-11-22 13:32:47 -06:00
return getHolder(key, value);
2017-06-19 15:02:18 -07:00
}
@Override
2022-11-22 13:32:47 -06:00
public Holder.Reference<T> register(ResourceKey<T> key, T value, Lifecycle lifecycle)
2017-06-19 15:02:18 -07:00
{
return registerMapping(-1, key, value, lifecycle);
2020-08-13 00:13:48 -07:00
}
2017-06-19 15:02:18 -07:00
// Reading Functions
@Override
@Nullable
public T get(@Nullable ResourceLocation name)
2017-06-19 15:02:18 -07:00
{
return this.delegate.getRaw(name); //get without default
2017-06-19 15:02:18 -07:00
}
@Override
public Optional<T> getOptional(@Nullable ResourceLocation name)
{
2022-11-22 13:32:47 -06:00
return Optional.ofNullable(this.delegate.getRaw(name)); //get without default
}
2020-09-23 23:35:34 -07:00
@Override
@Nullable
2021-07-17 00:08:49 -05:00
public T get(@Nullable ResourceKey<T> name)
2020-09-23 23:35:34 -07:00
{
return name == null ? null : this.delegate.getRaw(name.location()); //get without default
2020-09-23 23:35:34 -07:00
}
2017-06-19 15:02:18 -07:00
@Override
@Nullable
2019-05-23 16:02:15 -07:00
public ResourceLocation getKey(T value)
2017-06-19 15:02:18 -07:00
{
return this.delegate.getKey(value);
}
2021-11-26 18:57:41 +00:00
@Override
public Optional<ResourceKey<T>> getResourceKey(T p_122755_)
{
return this.delegate.getResourceKey(p_122755_);
}
2017-06-19 15:02:18 -07:00
@Override
2019-05-23 16:02:15 -07:00
public boolean containsKey(ResourceLocation key)
2017-06-19 15:02:18 -07:00
{
return this.delegate.containsKey(key);
}
@Override
public boolean containsKey(ResourceKey<T> key)
{
2022-11-22 13:32:47 -06:00
return this.delegate.getRegistryName().equals(key.registry()) && containsKey(key.location());
}
2017-06-19 15:02:18 -07:00
@Override
2019-05-23 16:02:15 -07:00
public int getId(@Nullable T value)
2017-06-19 15:02:18 -07:00
{
return this.delegate.getID(value);
}
@Override
@Nullable
public T byId(int id)
2017-06-19 15:02:18 -07:00
{
return this.delegate.getValue(id);
}
2021-11-26 18:57:41 +00:00
@Override
public Lifecycle lifecycle(T value)
{
2021-11-26 18:57:41 +00:00
return Lifecycle.stable();
}
@Override
2022-11-22 13:32:47 -06:00
public Lifecycle registryLifecycle()
{
2022-11-22 13:32:47 -06:00
return this.registryLifecycle;
}
2017-06-19 15:02:18 -07:00
@Override
2019-05-23 16:02:15 -07:00
public Iterator<T> iterator()
2017-06-19 15:02:18 -07:00
{
return this.delegate.iterator();
}
@Override
2019-05-23 16:02:15 -07:00
public Set<ResourceLocation> keySet()
2017-06-19 15:02:18 -07:00
{
return this.delegate.getKeys();
}
@Override
public Set<ResourceKey<T>> registryKeySet()
{
return this.delegate.getResourceKeys();
}
@Override
2021-07-17 00:08:49 -05:00
public Set<Map.Entry<ResourceKey<T>, T>> entrySet()
{
return this.delegate.getEntries();
}
@Override
public boolean isEmpty()
{
return this.delegate.isEmpty();
}
2017-06-19 15:02:18 -07:00
@Override
public int size()
{
return this.delegate.size();
}
2022-11-22 13:32:47 -06:00
/**
* @deprecated Forge: For internal use only. Use the Register events when registering values.
*/
@Deprecated
@Override
2022-11-22 13:32:47 -06:00
public void lock()
{
2022-11-22 13:32:47 -06:00
this.locked = true;
}
2022-11-22 13:32:47 -06:00
@Override
public Optional<Holder.Reference<T>> getHolder(int id)
{
return id >= 0 && id < this.holdersById.size() ? Optional.ofNullable(this.holdersById.get(id)) : Optional.empty();
}
2017-06-19 15:02:18 -07:00
2022-11-22 13:32:47 -06:00
@Override
public Optional<Holder.Reference<T>> getHolder(ResourceKey<T> key)
{
return Optional.ofNullable(this.holdersByName.get(key.location()));
}
2023-05-05 02:42:02 +03:00
@Override
public @NotNull Holder<T> wrapAsHolder(@NotNull T value)
{
final Holder<T> holder = this.holders.get(value);
return holder == null ? Holder.direct(value) : holder;
}
2022-11-22 13:32:47 -06:00
Optional<Holder<T>> getHolder(ResourceLocation location)
{
return Optional.ofNullable(this.holdersByName.get(location));
}
Optional<Holder<T>> getHolder(T value)
{
return Optional.ofNullable(this.holders.get(value));
}
@Override
public HolderGetter<T> createRegistrationLookup()
{
this.validateWrite();
return new HolderGetter<T>()
{
public Optional<Holder.Reference<T>> get(ResourceKey<T> p_259097_)
{
return Optional.of(this.getOrThrow(p_259097_));
}
public Holder.Reference<T> getOrThrow(ResourceKey<T> p_259750_)
{
return NamespacedWrapper.this.getOrCreateHolderOrThrow(p_259750_);
}
public Optional<HolderSet.Named<T>> get(TagKey<T> p_259486_)
{
return Optional.of(this.getOrThrow(p_259486_));
}
public HolderSet.Named<T> getOrThrow(TagKey<T> p_260298_)
{
return NamespacedWrapper.this.getOrCreateTag(p_260298_);
}
};
}
void validateWrite()
{
if (this.frozen)
throw new IllegalStateException("Registry is already frozen");
}
void validateWrite(ResourceKey<T> key)
{
if (this.frozen)
throw new IllegalStateException("Registry is already frozen (trying to add key " + key + ")");
}
2022-03-01 14:59:27 -06:00
2022-11-22 13:32:47 -06:00
Holder.Reference<T> getOrCreateHolderOrThrow(ResourceKey<T> key)
{
return this.holdersByName.computeIfAbsent(key.location(), k -> {
if (this.intrusiveHolderCallback != null)
{
throw new IllegalStateException("This registry can't create new holders without value");
}
else
{
this.validateWrite(key);
return Holder.Reference.createStandAlone(this.holderOwner(), key);
}
});
}
@Override
public Optional<Holder.Reference<T>> getRandom(RandomSource rand)
{
return Util.getRandomSafe(this.getSortedHolders(), rand);
}
@Override
public Stream<Holder.Reference<T>> holders()
{
return this.getSortedHolders().stream();
}
@Override
public Stream<Pair<TagKey<T>, HolderSet.Named<T>>> getTags()
{
return this.tags.entrySet().stream().map(e -> Pair.of(e.getKey(), e.getValue()));
}
@Override
public HolderSet.Named<T> getOrCreateTag(TagKey<T> name)
{
HolderSet.Named<T> named = this.tags.get(name);
if (named == null)
{
named = createTag(name);
// They use volatile and set it this way to not have the performance penalties of synced read access. But this makes a lot of new maps.. We need to look into performance alternatives.
Map<TagKey<T>, HolderSet.Named<T>> map = new IdentityHashMap<>(this.tags);
map.put(name, named);
this.tags = map;
}
return named;
}
void addOptionalTag(TagKey<T> name, @NotNull Set<? extends Supplier<T>> defaults)
{
this.optionalTags.putAll(name, defaults);
}
@Override
public Stream<TagKey<T>> getTagNames()
{
return this.tags.keySet().stream();
}
//TODO: Move this to ValidateCallback?
@Override
public Registry<T> freeze()
{
this.frozen = true;
List<ResourceLocation> unregistered = this.holdersByName.entrySet().stream()
.filter(e -> !e.getValue().isBound())
.map(Map.Entry::getKey).sorted().toList();
if (!unregistered.isEmpty())
throw new IllegalStateException("Unbound values in registry " + this.key() + ": " + unregistered.stream().map(ResourceLocation::toString).collect(Collectors.joining(", \n\t")));
if (this.unregisteredIntrusiveHolders != null && this.unregisteredIntrusiveHolders.values().stream().anyMatch(r -> !r.isBound() && r.getType() == Holder.Reference.Type.INTRUSIVE)) {
throw new IllegalStateException("Some intrusive holders were not registered: " + this.unregisteredIntrusiveHolders.values() + " Hint: Did you register all your registry objects? Registry stage: " + stage.getName());
}
return this;
}
@Override
public Holder.Reference<T> createIntrusiveHolder(T value)
{
if (this.intrusiveHolderCallback == null)
throw new IllegalStateException("This registry can't create intrusive holders");
this.validateWrite();
return super.createIntrusiveHolder(value);
}
@Override
public Optional<HolderSet.Named<T>> getTag(TagKey<T> name)
{
return Optional.ofNullable(this.tags.get(name));
}
@Override
public void bindTags(Map<TagKey<T>, List<Holder<T>>> newTags)
{
Map<Holder.Reference<T>, List<TagKey<T>>> holderToTag = new IdentityHashMap<>();
this.holdersByName.values().forEach(v -> holderToTag.put(v, new ArrayList<>()));
newTags.forEach((name, values) -> values.forEach(holder -> addTagToHolder(holderToTag, name, holder)));
Set<TagKey<T>> set = new HashSet<>(Sets.difference(this.tags.keySet(), newTags.keySet()));
set.removeAll(this.optionalTags.keySet());
if (!set.isEmpty())
LOGGER.warn("Not all defined tags for registry {} are present in data pack: {}", this.key(), set.stream().map(k -> k.location().toString()).sorted()
.collect(Collectors.joining(", \n\t")));
Map<TagKey<T>, HolderSet.Named<T>> tmpTags = new IdentityHashMap<>(this.tags);
newTags.forEach((k, v) -> tmpTags.computeIfAbsent(k, this::createTag).bind(v));
Set<TagKey<T>> defaultedTags = Sets.difference(this.optionalTags.keySet(), newTags.keySet());
defaultedTags.forEach(name -> {
List<Holder<T>> defaults = this.optionalTags.get(name).stream()
.map(valueSupplier -> getHolder(valueSupplier.get()).orElse(null))
.filter(Objects::nonNull)
.distinct()
.toList();
defaults.forEach(holder -> addTagToHolder(holderToTag, name, holder));
tmpTags.computeIfAbsent(name, this::createTag).bind(defaults);
});
holderToTag.forEach(Holder.Reference::bindTags);
this.tags = tmpTags;
this.delegate.onBindTags(this.tags, defaultedTags);
}
private void addTagToHolder(Map<Holder.Reference<T>, List<TagKey<T>>> holderToTag, TagKey<T> name, Holder<T> holder)
{
if (!holder.canSerializeIn(this.holderOwner()))
throw new IllegalStateException("Can't create named set " + name + " containing value " + holder + " from outside registry " + this);
if (!(holder instanceof Holder.Reference<T>))
throw new IllegalStateException("Found direct holder " + holder + " value in tag " + name);
holderToTag.get((Holder.Reference<T>) holder).add(name);
}
@Override
public void resetTags()
{
this.tags.values().forEach(t -> t.bind(List.of()));
this.holders.values().forEach(v -> v.bindTags(Set.of()));
}
@Override
public void unfreeze()
{
this.frozen = false;
}
boolean isFrozen()
{
return this.frozen;
}
boolean isIntrusive()
{
return this.intrusiveHolderCallback != null;
}
@Nullable
Holder.Reference<T> onAdded(RegistryManager stage, int id, ResourceKey<T> key, T newValue, T oldValue)
{
if (stage != RegistryManager.ACTIVE && (this.intrusiveHolderCallback == null || !stage.isStaging())) // Intrusive handlers need updating in staging.
return null; // Only do holder shit on the active registries, not pending or snapshots.
//Holder.Reference<T> oldHolder = oldValue == null ? null : getHolder(key, oldValue);
// Do we need to do anything with the old holder? We cant update its pointer unless its non-intrusive...
// And if thats the case, then we *should* get its reference in newHolder
Holder.Reference<T> newHolder = getHolder(key, newValue);
this.holdersById.size(Math.max(this.holdersById.size(), id + 1));
this.holdersById.set(id, newHolder);
this.holdersByName.put(key.location(), newHolder);
this.holders.put(newValue, newHolder);
if (this.unregisteredIntrusiveHolders != null)
{
this.unregisteredIntrusiveHolders.remove(newValue);
newHolder.bindKey(key);
}
newHolder.bindValue(newValue);
this.holdersSorted = null;
return newHolder;
}
@SuppressWarnings("deprecation")
private HolderSet.Named<T> createTag(TagKey<T> name)
{
return HolderSet.emptyNamed(this.holderOwner(), name);
}
private Holder.Reference<T> getHolder(ResourceKey<T> key, T value)
{
if (this.intrusiveHolderCallback != null)
return this.intrusiveHolderCallback.apply(value);
return this.holdersByName.computeIfAbsent(key.location(), k -> Holder.Reference.createStandAlone(this.holderOwner(), key));
}
private List<Holder.Reference<T>> getSortedHolders()
{
if (this.holdersSorted == null)
this.holdersSorted = this.holdersById.stream().filter(Objects::nonNull).toList();
return this.holdersSorted;
}
2022-06-07 15:31:26 -05:00
public static class Factory<V> implements IForgeRegistry.CreateCallback<V>, IForgeRegistry.AddCallback<V>
2017-06-19 15:02:18 -07:00
{
public static final ResourceLocation ID = new ResourceLocation("forge", "registry_defaulted_wrapper");
2017-06-19 15:02:18 -07:00
@Override
public void onCreate(IForgeRegistryInternal<V> owner, RegistryManager stage)
{
2022-11-22 13:32:47 -06:00
ForgeRegistry<V> fowner = (ForgeRegistry<V>) owner;
owner.setSlaveMap(ID, new NamespacedWrapper<V>(fowner, fowner.getBuilder().getIntrusiveHolderCallback(), stage));
2017-06-19 15:02:18 -07:00
}
@Override
@SuppressWarnings("unchecked")
2022-06-07 15:31:26 -05:00
public void onAdd(IForgeRegistryInternal<V> owner, RegistryManager stage, int id, ResourceKey<V> key, V value, V oldValue)
{
2022-11-22 13:32:47 -06:00
owner.getSlaveMap(ID, NamespacedWrapper.class).onAdded(stage, id, key, value, oldValue);
}
2017-06-19 15:02:18 -07:00
}
}