mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-12 16:27:47 +00:00
Created a CreativeModeTabEvent to register creative mode tabs and populate entries per tab Moved datapack registries to DataPackRegistryEvent.NewRegistry event instead of tying them to ForgeRegistry Made it easier for mods to datagen datapack builtin entries with DatapackBuiltinEntriesProvider Provided access to lookupProvider for datagen Updated dependencies to match versions used by vanilla and update JarJar to 0.3.18 Added a test mod for the new CreativeModeTabEvent Throws better error message for Forge registries in tag datagen Deleted ForgeRegistryTagsProvider Updated ClientChatReceivedEvent and ServerChatEvent for Mojang changes Added patches for both sign related methods in ModelLayers Changed RegisterShadersEvent to use ResourceProvider Migrated old Mojang math types to JOML Co-authored-by: Marc Hermans <marc.hermans@ldtteam.com> Co-authored-by: LexManos <LexManos@gmail.com> Co-authored-by: sciwhiz12 <arnoldnunag12@gmail.com> Co-authored-by: coehlrich <coehlrich@users.noreply.github.com>
504 lines
16 KiB
Java
504 lines
16 KiB
Java
/*
|
|
* Copyright (c) Forge Development LLC and contributors
|
|
* SPDX-License-Identifier: LGPL-2.1-only
|
|
*/
|
|
|
|
package net.minecraftforge.registries;
|
|
|
|
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;
|
|
import net.minecraft.core.Holder;
|
|
import net.minecraft.core.HolderGetter;
|
|
import net.minecraft.core.HolderSet;
|
|
import net.minecraft.core.MappedRegistry;
|
|
import net.minecraft.core.Registry;
|
|
import net.minecraft.resources.ResourceKey;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.tags.TagKey;
|
|
import net.minecraft.util.RandomSource;
|
|
import org.apache.commons.lang3.Validate;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.jetbrains.annotations.Nullable;
|
|
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;
|
|
|
|
class NamespacedWrapper<T> extends MappedRegistry<T> implements ILockableRegistry
|
|
{
|
|
static final Logger LOGGER = LogUtils.getLogger();
|
|
private final ForgeRegistry<T> delegate;
|
|
@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.experimental();
|
|
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)
|
|
{
|
|
super(fowner.getRegistryKey(), Lifecycle.experimental(), intrusiveHolderCallback != null);
|
|
this.delegate = fowner;
|
|
this.intrusiveHolderCallback = intrusiveHolderCallback;
|
|
this.stage = stage;
|
|
}
|
|
|
|
@Override
|
|
public Holder.Reference<T> registerMapping(int id, ResourceKey<T> key, T value, Lifecycle lifecycle)
|
|
{
|
|
if (locked)
|
|
throw new IllegalStateException("Can not register to a locked registry. Modder should use Forge Register methods.");
|
|
|
|
Validate.notNull(value);
|
|
markKnown();
|
|
this.registryLifecycle = this.registryLifecycle.add(lifecycle);
|
|
|
|
int realId = this.delegate.add(id, key.location(), value);
|
|
if (realId != id && id != -1)
|
|
LOGGER.warn("Registered object did not get ID it asked for. Name: {} Expected: {} Got: {}", key, id, realId);
|
|
|
|
return getHolder(key, value);
|
|
}
|
|
|
|
@Override
|
|
public Holder.Reference<T> register(ResourceKey<T> key, T value, Lifecycle lifecycle)
|
|
{
|
|
return registerMapping(-1, key, value, lifecycle);
|
|
}
|
|
|
|
// Reading Functions
|
|
@Override
|
|
@Nullable
|
|
public T get(@Nullable ResourceLocation name)
|
|
{
|
|
return this.delegate.getRaw(name); //get without default
|
|
}
|
|
|
|
@Override
|
|
public Optional<T> getOptional(@Nullable ResourceLocation name)
|
|
{
|
|
return Optional.ofNullable(this.delegate.getRaw(name)); //get without default
|
|
}
|
|
|
|
@Override
|
|
@Nullable
|
|
public T get(@Nullable ResourceKey<T> name)
|
|
{
|
|
return name == null ? null : this.delegate.getRaw(name.location()); //get without default
|
|
}
|
|
|
|
@Override
|
|
@Nullable
|
|
public ResourceLocation getKey(T value)
|
|
{
|
|
return this.delegate.getKey(value);
|
|
}
|
|
|
|
@Override
|
|
public Optional<ResourceKey<T>> getResourceKey(T p_122755_)
|
|
{
|
|
return this.delegate.getResourceKey(p_122755_);
|
|
}
|
|
|
|
@Override
|
|
public boolean containsKey(ResourceLocation key)
|
|
{
|
|
return this.delegate.containsKey(key);
|
|
}
|
|
|
|
@Override
|
|
public boolean containsKey(ResourceKey<T> key)
|
|
{
|
|
return this.delegate.getRegistryName().equals(key.registry()) && containsKey(key.location());
|
|
}
|
|
|
|
@Override
|
|
public int getId(@Nullable T value)
|
|
{
|
|
return this.delegate.getID(value);
|
|
}
|
|
|
|
@Override
|
|
@Nullable
|
|
public T byId(int id)
|
|
{
|
|
return this.delegate.getValue(id);
|
|
}
|
|
|
|
@Override
|
|
public Lifecycle lifecycle(T value)
|
|
{
|
|
return Lifecycle.stable();
|
|
}
|
|
|
|
@Override
|
|
public Lifecycle registryLifecycle()
|
|
{
|
|
return this.registryLifecycle;
|
|
}
|
|
|
|
@Override
|
|
public Iterator<T> iterator()
|
|
{
|
|
return this.delegate.iterator();
|
|
}
|
|
|
|
@Override
|
|
public Set<ResourceLocation> keySet()
|
|
{
|
|
return this.delegate.getKeys();
|
|
}
|
|
|
|
@Override
|
|
public Set<ResourceKey<T>> registryKeySet()
|
|
{
|
|
return this.delegate.getResourceKeys();
|
|
}
|
|
|
|
@Override
|
|
public Set<Map.Entry<ResourceKey<T>, T>> entrySet()
|
|
{
|
|
return this.delegate.getEntries();
|
|
}
|
|
|
|
@Override
|
|
public boolean isEmpty()
|
|
{
|
|
return this.delegate.isEmpty();
|
|
}
|
|
|
|
@Override
|
|
public int size()
|
|
{
|
|
return this.delegate.size();
|
|
}
|
|
|
|
/**
|
|
* @deprecated Forge: For internal use only. Use the Register events when registering values.
|
|
*/
|
|
@Deprecated
|
|
@Override
|
|
public void lock()
|
|
{
|
|
this.locked = true;
|
|
}
|
|
|
|
@Override
|
|
public Optional<Holder.Reference<T>> getHolder(int id)
|
|
{
|
|
return id >= 0 && id < this.holdersById.size() ? Optional.ofNullable(this.holdersById.get(id)) : Optional.empty();
|
|
}
|
|
|
|
@Override
|
|
public Optional<Holder.Reference<T>> getHolder(ResourceKey<T> key)
|
|
{
|
|
return Optional.ofNullable(this.holdersByName.get(key.location()));
|
|
}
|
|
|
|
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 + ")");
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
public static class Factory<V> implements IForgeRegistry.CreateCallback<V>, IForgeRegistry.AddCallback<V>
|
|
{
|
|
public static final ResourceLocation ID = new ResourceLocation("forge", "registry_defaulted_wrapper");
|
|
|
|
@Override
|
|
public void onCreate(IForgeRegistryInternal<V> owner, RegistryManager stage)
|
|
{
|
|
ForgeRegistry<V> fowner = (ForgeRegistry<V>) owner;
|
|
owner.setSlaveMap(ID, new NamespacedWrapper<V>(fowner, fowner.getBuilder().getIntrusiveHolderCallback(), stage));
|
|
}
|
|
|
|
@Override
|
|
@SuppressWarnings("unchecked")
|
|
public void onAdd(IForgeRegistryInternal<V> owner, RegistryManager stage, int id, ResourceKey<V> key, V value, V oldValue)
|
|
{
|
|
owner.getSlaveMap(ID, NamespacedWrapper.class).onAdded(stage, id, key, value, oldValue);
|
|
}
|
|
}
|
|
}
|