Allow using DeferredRegisters for vanilla registries (#8527)

Catch and aggregate exceptions when applying object holders
This commit is contained in:
SizableShrimp authored and GitHub committed 2022-04-01 13:49:46 -07:00
1 parent 8f0d3da1e4
commit 1f031ad2c3
13 files changed
+550 -142

No files matched your search

@@ -95,8 +95,8 @@ public class ForgeMod
public static final RegistryObject<Attribute> REACH_DISTANCE = ATTRIBUTES.register("reach_distance", () -> new RangedAttribute("generic.reachDistance", 5.0D, 0.0D, 1024.0D).setSyncable(true));
private static boolean enableMilkFluid = false;
public static final RegistryObject<Fluid> MILK = RegistryObject.of(new ResourceLocation("milk"), ForgeRegistries.FLUIDS);
public static final RegistryObject<Fluid> FLOWING_MILK = RegistryObject.of(new ResourceLocation("flowing_milk"), ForgeRegistries.FLUIDS);
public static final RegistryObject<Fluid> MILK = RegistryObject.create(new ResourceLocation("milk"), ForgeRegistries.FLUIDS);
public static final RegistryObject<Fluid> FLOWING_MILK = RegistryObject.create(new ResourceLocation("flowing_milk"), ForgeRegistries.FLUIDS);
private static ForgeMod INSTANCE;
public static ForgeMod getInstance()
@@ -150,6 +150,8 @@ public class ForgeMod
MinecraftForge.EVENT_BUS.register(MinecraftForge.INTERNAL_HANDLER);
MinecraftForge.EVENT_BUS.addListener(this::mappingChanged);
BiomeDictionary.init();
ForgeRegistries.ITEMS.tags().addOptionalTagDefaults(Tags.Items.ENCHANTING_FUELS, Set.of(Items.LAPIS_LAZULI.delegate));
}
public void registerCapabilities(RegisterCapabilitiesEvent event)
@@ -226,7 +226,10 @@ public class Tags
public static final TagKey<Item> DYES_WHITE = DyeColor.WHITE.getTag();
public static final TagKey<Item> EGGS = tag("eggs");
public static final TagKey<Item> ENCHANTING_FUELS = tag("enchanting_fuels", Set.of(() -> net.minecraft.world.item.Items.LAPIS_LAZULI));
/**
* This tag defaults to {@link net.minecraft.world.item.Items#LAPIS_LAZULI} when not present in any datapacks, including forge client on vanilla server
*/
public static final TagKey<Item> ENCHANTING_FUELS = tag("enchanting_fuels");
public static final TagKey<Item> END_STONES = tag("end_stones");
public static final TagKey<Item> ENDER_PEARLS = tag("ender_pearls");
public static final TagKey<Item> FEATHERS = tag("feathers");
@@ -391,14 +394,9 @@ public class Tags
public static final TagKey<Item> STORAGE_BLOCKS_REDSTONE = tag("storage_blocks/redstone");
public static final TagKey<Item> STRING = tag("string");
private static TagKey<Item> tag(String name, @Nullable Set<Supplier<Item>> defaults)
{
return ItemTags.create(new ResourceLocation("forge", name));
}
private static TagKey<Item> tag(String name)
{
return tag(name, null);
return ItemTags.create(new ResourceLocation("forge", name));
}
}
@@ -8,6 +8,7 @@ package net.minecraftforge.registries;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
import net.minecraft.core.Registry;
import net.minecraft.data.BuiltinRegistries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
@@ -48,12 +49,20 @@ import java.util.function.Supplier;
* }
*}</pre>
*
* @param <T> The base registry type, must be a concrete base class, do not use subclasses or wild cards.
* @param <T> The base registry type
*/
public class DeferredRegister<T extends IForgeRegistryEntry<T>>
public class DeferredRegister<T>
{
/**
* Use for vanilla/forge registries. See example above.
* DeferredRegister factory for forge registries that exist <i>before</i> this DeferredRegister is created.
* <p>
* If you have a supplier, <u>do not use this method.</u>
* Instead, use one of the other factories that takes in a registry key or registry name.
*
* @param reg the forge registry to wrap
* @param modid the namespace for all objects registered to this DeferredRegister
* @see #create(ResourceKey, String)
* @see #create(ResourceLocation, String)
*/
public static <B extends IForgeRegistryEntry<B>> DeferredRegister<B> create(IForgeRegistry<B> reg, String modid)
{
@@ -68,56 +77,111 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
@Deprecated(forRemoval = true, since = "1.18.2")
public static <B extends IForgeRegistryEntry<B>> DeferredRegister<B> create(Class<B> base, String modid)
{
return new DeferredRegister<>(null, base, modid);
return new DeferredRegister<>(null, base, modid, false);
}
/**
* DeferredRegister factory for custom registries that are made during the {@link NewRegistryEvent} event
* or with {@link #makeRegistry(Class, Supplier)}.
* DeferredRegister factory for custom forge registries, {@link Registry vanilla registries},
* or {@link BuiltinRegistries built-in registries} to lookup based on the provided registry key.
* Supports both registries that already exist or do not exist yet.
* <p>
* If the registry is never created, any {@link RegistryObject}s made from this DeferredRegister will throw an exception.
* To allow the optional existence of a registry without error, use {@link #createOptional(ResourceKey, String)}.
*
* @param key The key of the registry to reference
* @param modid The namespace for all objects registered to this DeferredRegister
* @param key the key of the registry to reference
* @param modid the namespace for all objects registered to this DeferredRegister
* @see #createOptional(ResourceKey, String)
* @see #create(IForgeRegistry, String)
* @see #create(ResourceLocation, String)
*/
public static <B extends IForgeRegistryEntry<B>> DeferredRegister<B> create(ResourceKey<? extends Registry<B>> key, String modid)
public static <B> DeferredRegister<B> create(ResourceKey<? extends Registry<B>> key, String modid)
{
return new DeferredRegister<>(key.location(), null, modid);
return new DeferredRegister<>(key, null, modid, false);
}
/**
* DeferredRegister factory for custom registries that are made during the {@link NewRegistryEvent} event
* or with {@link #makeRegistry(Class, Supplier)}.
* DeferredRegister factory for the optional existence of custom forge registries, {@link Registry vanilla registries},
* or {@link BuiltinRegistries built-in registries} to lookup based on the provided registry key.
* Supports both registries that already exist or do not exist yet.
* <p>
* If the registry is never created, any {@link RegistryObject}s made from this DeferredRegister will never be filled but will not throw an exception.
*
* @param key the key of the registry to reference
* @param modid the namespace for all objects registered to this DeferredRegister
* @see #create(ResourceKey, String)
* @see #create(IForgeRegistry, String)
* @see #create(ResourceLocation, String)
*/
public static <B> DeferredRegister<B> createOptional(ResourceKey<? extends Registry<B>> key, String modid)
{
return new DeferredRegister<>(key, null, modid, true);
}
/**
* DeferredRegister factory for custom forge registries, {@link Registry vanilla registries},
* or {@link BuiltinRegistries built-in registries} to lookup based on the provided registry name.
* Supports both registries that already exist or do not exist yet.
* <p>
* If the registry is never created, any {@link RegistryObject}s made from this DeferredRegister will throw an exception.
* To allow the optional existence of a registry without error, use {@link #createOptional(ResourceLocation, String)}.
*
* @param registryName The name of the registry, should include namespace. May come from another DeferredRegister through {@link #getRegistryName()}.
* @param modid The namespace for all objects registered to this DeferredRegister
* @see #createOptional(ResourceLocation, String)
* @see #create(IForgeRegistry, String)
* @see #create(ResourceKey, String)
*/
public static <B extends IForgeRegistryEntry<B>> DeferredRegister<B> create(ResourceLocation registryName, String modid)
public static <B> DeferredRegister<B> create(ResourceLocation registryName, String modid)
{
return new DeferredRegister<>(registryName, null, modid);
return new DeferredRegister<>(ResourceKey.createRegistryKey(registryName), null, modid, false);
}
private final ResourceLocation registryName;
private final Class<T> superType;
/**
* DeferredRegister factory for the optional existence of custom forge registries, {@link Registry vanilla registries},
* or {@link BuiltinRegistries built-in registries} to lookup based on the provided registry name.
* <p>
* The registry may not exist at the time this DeferredRegister is created.
* If the registry is never created, any {@link RegistryObject}s made from this DeferredRegister will never be filled but will not throw an exception.
*
* @param registryName The name of the registry, should include namespace. May come from another DeferredRegister through {@link #getRegistryName()}.
* @param modid The namespace for all objects registered to this DeferredRegister
* @see #create(ResourceLocation, String)
* @see #create(IForgeRegistry, String)
* @see #create(ResourceKey, String)
*/
public static <B> DeferredRegister<B> createOptional(ResourceLocation registryName, String modid)
{
return new DeferredRegister<>(ResourceKey.createRegistryKey(registryName), null, modid, true);
}
@Nullable // Nullable when superType is not null
private final ResourceKey<? extends Registry<T>> registryKey;
@Nullable // Nullable when registryKey is not null
private final Class<? extends IForgeRegistryEntry<?>> superType;
private final String modid;
private final boolean optionalRegistry;
private final Map<RegistryObject<T>, Supplier<? extends T>> entries = new LinkedHashMap<>();
private final Set<RegistryObject<T>> entriesView = Collections.unmodifiableSet(entries.keySet());
private IForgeRegistry<T> type;
private Supplier<IForgeRegistry<T>> typeSupplier;
private Supplier<RegistryBuilder<T>> registryFactory;
@Nullable
private Supplier<RegistryBuilder<?>> registryFactory;
@Nullable
private SetMultimap<TagKey<T>, Supplier<T>> optionalTags;
private boolean seenRegisterEvent = false;
private DeferredRegister(@Nullable ResourceLocation registryName, @Nullable Class<T> base, String modid)
private <E extends IForgeRegistryEntry<E>> DeferredRegister(@Nullable ResourceKey<? extends Registry<T>> registryKey, @Nullable Class<E> base,
String modid, boolean optionalRegistry)
{
this.registryName = registryName;
this.registryKey = registryKey;
this.superType = base;
this.modid = modid;
this.optionalRegistry = optionalRegistry;
}
private DeferredRegister(IForgeRegistry<T> reg, String modid)
@SuppressWarnings("unchecked")
private <E extends IForgeRegistryEntry<E>> DeferredRegister(IForgeRegistry<E> reg, String modid)
{
this(reg.getRegistryName(), reg.getRegistrySuperType(), modid);
this.type = reg;
this((ResourceKey<? extends Registry<T>>) (ResourceKey) reg.getRegistryKey(), reg.getRegistrySuperType(), modid, false);
}
/**
@@ -127,7 +191,7 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
* @param sup A factory for the new entry, it should return a new instance every time it is called.
* @return A RegistryObject that will be updated with when the entries in the registry change.
*/
@SuppressWarnings("unchecked")
@SuppressWarnings({ "rawtypes", "unchecked" })
public <I extends T> RegistryObject<I> register(final String name, final Supplier<? extends I> sup)
{
if (seenRegisterEvent)
@@ -137,19 +201,23 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
final ResourceLocation key = new ResourceLocation(modid, name);
RegistryObject<I> ret;
IForgeRegistry<T> reg = getForgeRegistry();
if (reg != null)
ret = RegistryObject.of(key, reg);
else if (this.registryName != null)
ret = RegistryObject.of(key, this.registryName, this.modid);
if (this.registryKey != null)
ret = this.optionalRegistry
? RegistryObject.createOptional(key, this.registryKey, this.modid)
: RegistryObject.create(key, this.registryKey, this.modid);
else if (this.superType != null)
ret = RegistryObject.of(key, this.superType, this.modid);
ret = RegistryObject.of(key, (Class) this.superType, this.modid);
else
throw new IllegalStateException("Could not create RegistryObject in DeferredRegister");
if (entries.putIfAbsent((RegistryObject<T>) ret, () -> sup.get().setRegistryName(key)) != null) {
Supplier<? extends T> prevValue = entries.putIfAbsent((RegistryObject<T>) ret, () -> {
I value = sup.get();
if (value instanceof IForgeRegistryEntry<?> regEntry)
regEntry.setRegistryName(key);
return value;
});
if (prevValue != null)
throw new IllegalArgumentException("Duplicate registration " + name);
}
return ret;
}
@@ -165,10 +233,11 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
*
* @deprecated Use {@link #create(ResourceLocation, String)} and {@link #makeRegistry(Class, Supplier)} instead
*/
@SuppressWarnings("unchecked")
@Deprecated(forRemoval = true, since = "1.18.2")
public Supplier<IForgeRegistry<T>> makeRegistry(final String name, final Supplier<RegistryBuilder<T>> sup)
public <E extends IForgeRegistryEntry<E>> Supplier<IForgeRegistry<E>> makeRegistry(final String name, final Supplier<RegistryBuilder<E>> sup)
{
return makeRegistry(new ResourceLocation(modid, name), this.superType, sup);
return makeRegistry(new ResourceLocation(modid, name), (Class<E>) this.superType, sup);
}
/**
@@ -181,9 +250,9 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
* @return A supplier of the {@link IForgeRegistry} created by the builder.
* Will always return null until after the {@link NewRegistryEvent} event fires.
*/
public Supplier<IForgeRegistry<T>> makeRegistry(final Class<T> base, final Supplier<RegistryBuilder<T>> sup)
public <E extends IForgeRegistryEntry<E>> Supplier<IForgeRegistry<E>> makeRegistry(final Class<E> base, final Supplier<RegistryBuilder<E>> sup)
{
return makeRegistry(this.registryName, base, sup);
return makeRegistry(this.registryKey.location(), base, sup);
}
/**
@@ -214,10 +283,10 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
@NotNull
public TagKey<T> createTagKey(@NotNull ResourceLocation location)
{
if (this.registryName == null)
if (this.registryKey == null)
throw new IllegalStateException("The registry name was not set, cannot create a tag key");
Objects.requireNonNull(location);
return TagKey.create(ResourceKey.createRegistryKey(this.registryName), location);
return TagKey.create(this.registryKey, location);
}
/**
@@ -289,7 +358,9 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
public void register(IEventBus bus)
{
bus.register(new EventDispatcher(this));
if (this.type == null && this.registryFactory != null) {
if (this.registryKey != null && this.findForgeRegistry() == null)
bus.addListener(this::vanillaRegister);
if (this.registryFactory != null) {
bus.addListener(this::createRegistry);
}
}
@@ -319,61 +390,60 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
@Nullable
public ResourceLocation getRegistryName()
{
return this.registryName;
return this.registryKey == null ? null : this.registryKey.location();
}
private Supplier<IForgeRegistry<T>> makeRegistry(final ResourceLocation registryName, final Class<T> superType, final Supplier<RegistryBuilder<T>> sup) {
// TODO 1.19: Remove this E method generic in favor of the class generic T when IForgeRegistry and RegistryBuilder no longer require IForgeRegistryEntry
private <E extends IForgeRegistryEntry<E>> Supplier<IForgeRegistry<E>> makeRegistry(final ResourceLocation registryName, final Class<E> superType, final Supplier<RegistryBuilder<E>> sup) {
if (registryName == null)
throw new IllegalStateException("Cannot create a registry without specifying a registry name");
if (superType == null)
throw new IllegalStateException("Cannot create a registry without specifying a base type");
if (this.type != null || this.registryFactory != null)
if (RegistryManager.ACTIVE.getRegistry(registryName) != null || this.registryFactory != null)
throw new IllegalStateException("Cannot create a registry for a type that already exists");
this.registryFactory = () -> sup.get().setName(registryName).setType(superType);
return this::getForgeRegistry;
return new Supplier<>()
{
private IForgeRegistry<E> registry;
@Override
public IForgeRegistry<E> get()
{
// Keep trying to capture until it's not null
if (registry == null)
registry = RegistryManager.ACTIVE.getRegistry(registryName);
return registry;
}
};
}
private IForgeRegistry<T> getForgeRegistry()
{
if (this.type == null && this.typeSupplier != null)
this.type = this.typeSupplier.get();
return this.type;
}
private void onFill(IForgeRegistry<T> registry)
@SuppressWarnings({ "rawtypes", "unchecked" })
private void onFill(IForgeRegistry<?> registry)
{
if (this.optionalTags == null)
return;
ITagManager<T> tagManager = registry.tags();
// TODO 1.19: Put back the <T> here and fix the unsafe generic operations
ITagManager tagManager = registry.tags();
if (tagManager == null)
throw new IllegalStateException("The forge registry " + registry.getRegistryName() + " does not support tags, but optional tags were registered!");
Multimaps.asMap(this.optionalTags).forEach(tagManager::addOptionalTagDefaults);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void addEntries(RegistryEvent.Register<?> event)
{
IForgeRegistry<T> storedType = this.getForgeRegistry();
if (storedType == null && this.registryFactory == null)
{
//If there is no type yet and we don't have a registry factory, attempt to capture the registry
//Note: This will only ever get run on the first registry event, as after that time,
// the type will no longer be null. This is needed here rather than during the NewRegistry event
// to ensure that mods can properly use deferred registers for custom registries added by other mods
captureRegistry();
storedType = this.type;
}
IForgeRegistry<?> storedType = findForgeRegistry();
if (storedType != null && event.getGenericType() == storedType.getRegistrySuperType())
{
this.seenRegisterEvent = true;
@SuppressWarnings("unchecked")
IForgeRegistry<T> reg = (IForgeRegistry<T>)event.getRegistry();
IForgeRegistry<? extends T> reg = (IForgeRegistry<? extends T>) event.getRegistry();
for (Entry<RegistryObject<T>, Supplier<? extends T>> e : entries.entrySet())
{
reg.register(e.getValue().get());
((IForgeRegistry) reg).register((IForgeRegistryEntry) e.getValue().get());
e.getKey().updateReference(reg);
}
}
@@ -381,24 +451,36 @@ public class DeferredRegister<T extends IForgeRegistryEntry<T>>
private void createRegistry(NewRegistryEvent event)
{
this.typeSupplier = event.create(this.registryFactory.get(), this::onFill);
event.create(this.registryFactory.get(), this::onFill);
}
private void captureRegistry()
@SuppressWarnings({ "unchecked", "removal" })
private void vanillaRegister(VanillaRegisterEvent event)
{
if (this.registryName != null)
if (this.registryKey != null && event.vanillaRegistry.key() == this.registryKey)
{
this.type = RegistryManager.ACTIVE.getRegistry(this.registryName);
if (this.type == null)
throw new IllegalStateException("Unable to find registry with key " + this.registryName + " for modid \"" + modid + "\" after NewRegistry event");
this.seenRegisterEvent = true;
for (Entry<RegistryObject<T>, Supplier<? extends T>> e : entries.entrySet())
{
Registry.register((Registry<T>) event.vanillaRegistry, e.getKey().getId(), e.getValue().get());
e.getKey().updateReference((Registry<T>) event.vanillaRegistry);
}
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Nullable
private IForgeRegistry<?> findForgeRegistry()
{
if (this.registryKey != null)
{
return RegistryManager.ACTIVE.getRegistry((ResourceKey) this.registryKey);
}
else if (this.superType != null)
{
this.type = RegistryManager.ACTIVE.getRegistry(this.superType);
if (this.type == null)
throw new IllegalStateException("Unable to find registry for type " + this.superType.getName() + " for modid \"" + modid + "\" after NewRegistry event");
return RegistryManager.ACTIVE.getRegistry((Class) this.superType);
}
else
throw new IllegalStateException("Unable to find registry for mod \"" + modid + "\" No lookup criteria specified.");
return null;
}
}
@@ -145,7 +145,7 @@ public class ForgeRegistries
}
/**
* This function is just to make sure static inializers in other classes have run and setup their registries before we query them.
* This function is just to make sure static initializers in other classes have run and setup their registries before we query them.
*/
private static void init()
{
@@ -10,6 +10,7 @@ import com.mojang.serialization.Lifecycle;
import java.util.*;
import net.minecraft.data.BuiltinRegistries;
import net.minecraft.world.level.block.AirBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
@@ -55,6 +56,7 @@ import net.minecraftforge.common.util.LogMessageAdapter;
import net.minecraftforge.common.world.ForgeWorldPreset;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.event.RegistryEvent.MissingMappings;
import net.minecraftforge.fml.ModLoader;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.IModStateTransition;
import net.minecraftforge.fml.StartupMessageManager;
@@ -372,12 +374,17 @@ public class GameData
final ResourceLocation rl = event.getName();
ForgeRegistry<?> fr = (ForgeRegistry<?>) event.getRegistry();
fr.freeze();
LOGGER.debug(REGISTRIES, "Applying holder lookups: {}", rl.toString());
ObjectHolderRegistry.applyObjectHolders(rl::equals);
LOGGER.debug(REGISTRIES, "Holder lookups applied: {}", rl.toString());
applyHolderLookups(rl);
}, executor).handle((v, t)->t != null ? Collections.singletonList(t): Collections.emptyList());
}
private static void applyHolderLookups(ResourceLocation registryName)
{
LOGGER.debug(REGISTRIES, "Applying holder lookups: {}", registryName);
ObjectHolderRegistry.applyObjectHolders(registryName::equals);
LOGGER.debug(REGISTRIES, "Holder lookups applied: {}", registryName);
}
@SuppressWarnings("deprecation")
public static CompletableFuture<List<Throwable>> checkForRevertToVanilla(final Executor executor, final CompletableFuture<List<Throwable>> listCompletableFuture) {
return listCompletableFuture.whenCompleteAsync((errors, except) -> {
@@ -386,11 +393,28 @@ public class GameData
revertTo(RegistryManager.VANILLA, false);
LOGGER.fatal("Detected errors during registry event dispatch, roll back to VANILLA complete");
} else {
RegistryManager.getVanillaRegistryKeys().forEach(registryName -> {
Registry<?> registry = Registry.REGISTRY.get(registryName);
if (registry == null)
return;
postVanillaRegisterEvent(registry);
});
BuiltinRegistries.REGISTRY.forEach(GameData::postVanillaRegisterEvent);
net.minecraftforge.common.ForgeHooks.modifyAttributes();
}
}, executor);
}
@SuppressWarnings("removal")
private static void postVanillaRegisterEvent(Registry<?> registry)
{
if (RegistryManager.ACTIVE.getRegistry(registry.key().location()) != null)
return;
ModLoader.get().postEvent(new VanillaRegisterEvent(registry));
applyHolderLookups(registry.key().location());
}
//Lets us clear the map so we can rebuild it.
private static class ClearableObjectIntIdentityMap<I> extends IdMapper<I>
{
@@ -45,7 +45,7 @@ public class ObjectHolderRegistry
* and hashCode function to de-duplicate callers here.
* The default @ObjectHolder implementation uses the hashCode/equals for the field the annotation is on.
*/
public static void addHandler(Consumer<Predicate<ResourceLocation>> ref)
public static synchronized void addHandler(Consumer<Predicate<ResourceLocation>> ref)
{
objectHolders.add(ref);
}
@@ -59,7 +59,7 @@ public class ObjectHolderRegistry
*
* @return true if handler was matched and removed.
*/
public static boolean removeHandler(Consumer<Predicate<ResourceLocation>> ref)
public static synchronized boolean removeHandler(Consumer<Predicate<ResourceLocation>> ref)
{
return objectHolders.remove(ref);
}
@@ -171,7 +171,24 @@ public class ObjectHolderRegistry
public static void applyObjectHolders(Predicate<ResourceLocation> filter)
{
objectHolders.forEach(e -> e.accept(filter));
RuntimeException aggregate = new RuntimeException("Failed to apply some object holders, see suppressed exceptions for details");
objectHolders.forEach(objectHolder -> {
try
{
objectHolder.accept(filter);
}
catch (Exception e)
{
aggregate.addSuppressed(e);
}
});
if (aggregate.getSuppressed().length > 0)
{
// Something had an exception, log and propagate with a throw for when FML eventually logs this too
LOGGER.error("", aggregate);
throw aggregate;
}
}
}
@@ -6,6 +6,7 @@
package net.minecraftforge.registries;
import net.minecraft.core.Registry;
import net.minecraft.data.BuiltinRegistries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
@@ -20,26 +21,51 @@ import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
public final class RegistryObject<T extends IForgeRegistryEntry<? super T>> implements Supplier<T>
public final class RegistryObject<T> implements Supplier<T>
{
private final ResourceLocation name;
private final boolean optionalRegistry;
@Nullable
private T value;
/**
* @deprecated The uniqueness of registry super types will not be guaranteed starting in 1.19. Use {@link #of(ResourceLocation, ResourceLocation, String)}.
* @deprecated The uniqueness of registry super types will not be guaranteed starting in 1.19.
* Use {@link #create(ResourceLocation, ResourceLocation, String)}.
*/
@Deprecated(forRemoval = true, since = "1.18.2")
public static <T extends IForgeRegistryEntry<T>, U extends T> RegistryObject<U> of(final ResourceLocation name, Supplier<Class<? super T>> registryType) {
return new RegistryObject<>(name, registryType);
}
public static <T extends IForgeRegistryEntry<T>, U extends T> RegistryObject<U> of(final ResourceLocation name, IForgeRegistry<T> registry) {
/**
* Factory for a {@link RegistryObject} that stores the value of an object from the provided forge registry once it is ready.
*
* @param name the name of the object to look up in the forge registry
* @param registry the forge registry
* @return a {@link RegistryObject} that stores the value of an object from the provided forge registry once it is ready
* @deprecated Use {@link #create(ResourceLocation, IForgeRegistry)} instead.
*/
@Deprecated(forRemoval = true, since = "1.18.2")
public static <T extends IForgeRegistryEntry<T>, U extends T> RegistryObject<U> of(final ResourceLocation name, IForgeRegistry<T> registry)
{
return new RegistryObject<>(name, registry);
}
/**
* @deprecated The uniqueness of registry super types will not be guaranteed starting in 1.19. Use {@link #of(ResourceLocation, ResourceLocation, String)}.
* Factory for a {@link RegistryObject} that stores the value of an object from the provided forge registry once it is ready.
*
* @param name the name of the object to look up in the forge registry
* @param registry the forge registry
* @return a {@link RegistryObject} that stores the value of an object from the provided forge registry once it is ready
*/
public static <T extends IForgeRegistryEntry<T>, U extends T> RegistryObject<U> create(final ResourceLocation name, IForgeRegistry<T> registry)
{
return new RegistryObject<>(name, registry);
}
/**
* @deprecated The uniqueness of registry super types will not be guaranteed starting in 1.19.
* Use {@link #create(ResourceLocation, ResourceLocation, String)}.
*/
@Deprecated(forRemoval = true, since = "1.18.2")
public static <T extends IForgeRegistryEntry<T>, U extends T> RegistryObject<U> of(final ResourceLocation name, final Class<T> baseType, String modid) {
@@ -47,32 +73,129 @@ public final class RegistryObject<T extends IForgeRegistryEntry<? super T>> impl
}
/**
* Factory for a {@link RegistryObject} that stores the value of an object in a registry once it is ready.
* Factory for a {@link RegistryObject} that stores the value of an object from a registry once it is ready based on a lookup of the provided registry key.
* <p>
* If a registry with the given key cannot be found, an exception will be thrown when trying to fill this RegistryObject.
* Use {@link #createOptional(ResourceLocation, ResourceKey, String)} for RegistryObjects of optional registries.
*
* @param name The name of the object to look up in a forge registry
* @param key The key of the forge registry to grab from {@link RegistryManager#ACTIVE}
* @param modid The mod id calling context
* @return A {@link RegistryObject} that stores the value of an object in a registry once it is ready
* @param name the name of the object to look up in a registry
* @param registryKey the key of the registry. Supports lookups on {@link BuiltinRegistries}, {@link Registry}, and {@link RegistryManager#ACTIVE}.
* @param modid the mod id calling context
* @return a {@link RegistryObject} that stores the value of an object from a registry once it is ready
* @see #createOptional(ResourceLocation, ResourceKey, String)
* @see #create(ResourceLocation, IForgeRegistry)
* @see #create(ResourceLocation, ResourceLocation, String)
* @deprecated Use {@link #create(ResourceLocation, ResourceKey, String)} instead.
*/
public static <T extends IForgeRegistryEntry<T>, U extends T> RegistryObject<U> of(final ResourceLocation name, final ResourceKey<? extends Registry<T>> key, String modid) {
return new RegistryObject<>(name, key.location(), modid);
@Deprecated(forRemoval = true, since = "1.18.2")
public static <T, U extends T> RegistryObject<U> of(final ResourceLocation name, final ResourceKey<? extends Registry<T>> registryKey, String modid)
{
return new RegistryObject<>(name, registryKey.location(), modid, false);
}
/**
* Factory for a {@link RegistryObject} that stores the value of an object in a registry once it is ready.
* Factory for a {@link RegistryObject} that stores the value of an object from a registry once it is ready based on a lookup of the provided registry key.
* <p>
* If a registry with the given key cannot be found, an exception will be thrown when trying to fill this RegistryObject.
* Use {@link #createOptional(ResourceLocation, ResourceKey, String)} for RegistryObjects of optional registries.
*
* @param name The name of the object to look up in a forge registry
* @param registryName The name of the forge registry to grab from {@link RegistryManager#ACTIVE}
* @param modid The mod id calling context
* @return A {@link RegistryObject} that stores the value of an object in a registry once it is ready
* @param name the name of the object to look up in a registry
* @param registryKey the key of the registry. Supports lookups on {@link BuiltinRegistries}, {@link Registry}, and {@link RegistryManager#ACTIVE}.
* @param modid the mod id calling context
* @return a {@link RegistryObject} that stores the value of an object from a registry once it is ready
* @see #createOptional(ResourceLocation, ResourceKey, String)
* @see #create(ResourceLocation, IForgeRegistry)
* @see #create(ResourceLocation, ResourceLocation, String)
*/
public static <T extends IForgeRegistryEntry<T>, U extends T> RegistryObject<U> of(final ResourceLocation name, final ResourceLocation registryName, String modid) {
return new RegistryObject<>(name, registryName, modid);
public static <T, U extends T> RegistryObject<U> create(final ResourceLocation name, final ResourceKey<? extends Registry<T>> registryKey, String modid)
{
return new RegistryObject<>(name, registryKey.location(), modid, false);
}
/**
* Factory for a {@link RegistryObject} that optionally stores the value of an object from a registry once it is ready if the registry exists
* based on a lookup of the provided registry key.
* <p>
* If a registry with the given key cannot be found, it will be silently ignored and this RegistryObject will not be filled.
* Use {@link #create(ResourceLocation, ResourceKey, String)} for RegistryObjects that should throw exceptions on missing registry.
*
* @param name the name of the object to look up in a registry
* @param registryKey the key of the registry. Supports lookups on {@link BuiltinRegistries}, {@link Registry}, and {@link RegistryManager#ACTIVE}.
* @param modid the mod id calling context
* @return a {@link RegistryObject} that stores the value of an object from a registry once it is ready
* @see #create(ResourceLocation, ResourceKey, String)
* @see #create(ResourceLocation, IForgeRegistry)
* @see #create(ResourceLocation, ResourceLocation, String)
*/
public static <T, U extends T> RegistryObject<U> createOptional(final ResourceLocation name, final ResourceKey<? extends Registry<T>> registryKey,
String modid)
{
return new RegistryObject<>(name, registryKey.location(), modid, true);
}
/**
* Factory for a {@link RegistryObject} that stores the value of an object from a registry once it is ready based on a lookup of the provided registry name.
* <p>
* If a registry with the given name cannot be found, an exception will be thrown when trying to fill this RegistryObject.
* Use {@link #createOptional(ResourceLocation, ResourceLocation, String)} for RegistryObjects of optional registries.
*
* @param name the name of the object to look up in a registry
* @param registryName the name of the registry. Supports lookups on {@link BuiltinRegistries}, {@link Registry}, and {@link RegistryManager#ACTIVE}.
* @param modid the mod id calling context
* @return a {@link RegistryObject} that stores the value of an object from a registry once it is ready
* @see #createOptional(ResourceLocation, ResourceLocation, String)
* @see #create(ResourceLocation, IForgeRegistry)
* @see #create(ResourceLocation, ResourceKey, String)
* @deprecated Use {@link #create(ResourceLocation, ResourceLocation, String)} instead.
*/
@Deprecated(forRemoval = true, since = "1.18.2")
public static <T, U extends T> RegistryObject<U> of(final ResourceLocation name, final ResourceLocation registryName, String modid)
{
return new RegistryObject<>(name, registryName, modid, false);
}
/**
* Factory for a {@link RegistryObject} that stores the value of an object from a registry once it is ready based on a lookup of the provided registry name.
* <p>
* If a registry with the given name cannot be found, an exception will be thrown when trying to fill this RegistryObject.
* Use {@link #createOptional(ResourceLocation, ResourceLocation, String)} for RegistryObjects of optional registries.
*
* @param name the name of the object to look up in a registry
* @param registryName the name of the registry. Supports lookups on {@link BuiltinRegistries}, {@link Registry}, and {@link RegistryManager#ACTIVE}.
* @param modid the mod id calling context
* @return a {@link RegistryObject} that stores the value of an object from a registry once it is ready
* @see #createOptional(ResourceLocation, ResourceLocation, String)
* @see #create(ResourceLocation, IForgeRegistry)
* @see #create(ResourceLocation, ResourceKey, String)
*/
public static <T, U extends T> RegistryObject<U> create(final ResourceLocation name, final ResourceLocation registryName, String modid)
{
return new RegistryObject<>(name, registryName, modid, false);
}
/**
* Factory for a {@link RegistryObject} that optionally stores the value of an object from a registry once it is ready if the registry exists
* based on a lookup of the provided registry name.
* <p>
* If a registry with the given name cannot be found, it will be silently ignored and this RegistryObject will not be filled.
* Use {@link #create(ResourceLocation, ResourceLocation, String)} for RegistryObjects that should throw exceptions on missing registry.
*
* @param name the name of the object to look up in a registry
* @param registryName the name of the registry. Supports lookups on {@link BuiltinRegistries}, {@link Registry}, and {@link RegistryManager#ACTIVE}.
* @param modid the mod id calling context
* @return a {@link RegistryObject} that stores the value of an object from a registry once it is ready
* @see #create(ResourceLocation, ResourceLocation, String)
* @see #create(ResourceLocation, IForgeRegistry)
* @see #create(ResourceLocation, ResourceKey, String)
*/
public static <T, U extends T> RegistryObject<U> createOptional(final ResourceLocation name, final ResourceLocation registryName, String modid)
{
return new RegistryObject<>(name, registryName, modid, true);
}
private static RegistryObject<?> EMPTY = new RegistryObject<>();
private static <T extends IForgeRegistryEntry<? super T>> RegistryObject<T> empty() {
private static <T> RegistryObject<T> empty() {
@SuppressWarnings("unchecked")
RegistryObject<T> t = (RegistryObject<T>) EMPTY;
return t;
@@ -80,6 +203,7 @@ public final class RegistryObject<T extends IForgeRegistryEntry<? super T>> impl
private RegistryObject() {
this.name = null;
this.optionalRegistry = false;
}
@Deprecated(forRemoval = true, since = "1.18.2")
@@ -94,6 +218,7 @@ public final class RegistryObject<T extends IForgeRegistryEntry<? super T>> impl
if (registry == null)
throw new IllegalArgumentException("Invalid registry argument, must not be null");
this.name = name;
this.optionalRegistry = false;
ObjectHolderRegistry.addHandler(pred ->
{
if (pred.test(registry.getRegistryName()))
@@ -107,19 +232,26 @@ public final class RegistryObject<T extends IForgeRegistryEntry<? super T>> impl
private <V extends IForgeRegistryEntry<V>> RegistryObject(final ResourceLocation name, final Class<V> baseType, final String modid)
{
this.name = name;
this.optionalRegistry = false;
final Throwable callerStack = new Throwable("Calling Site from mod: " + modid);
ObjectHolderRegistry.addHandler(new Consumer<Predicate<ResourceLocation>>()
{
private IForgeRegistry<V> registry;
private boolean invalidRegistry = false;
@Override
public void accept(Predicate<ResourceLocation> pred)
{
if (invalidRegistry)
return;
if (registry == null)
{
this.registry = RegistryManager.ACTIVE.getRegistry(baseType);
if (registry == null)
{
invalidRegistry = true;
throw new IllegalStateException("Unable to find registry for type " + baseType.getName() + " for mod \"" + modid + "\". Check the 'caused by' to see further stack.", callerStack);
}
}
if (pred.test(registry.getRegistryName()))
RegistryObject.this.updateReference((IForgeRegistry<? extends T>) registry);
@@ -133,34 +265,35 @@ public final class RegistryObject<T extends IForgeRegistryEntry<? super T>> impl
}
}
@SuppressWarnings("unchecked")
private <V extends IForgeRegistryEntry<V>> RegistryObject(final ResourceLocation name, final ResourceLocation registryName, final String modid)
private RegistryObject(final ResourceLocation name, final ResourceLocation registryName, final String modid, boolean optionalRegistry)
{
this.name = name;
this.optionalRegistry = optionalRegistry;
final Throwable callerStack = new Throwable("Calling Site from mod: " + modid);
ObjectHolderRegistry.addHandler(new Consumer<>()
{
private IForgeRegistry<V> registry;
private boolean registryExists = false;
private boolean invalidRegistry = false;
@Override
public void accept(Predicate<ResourceLocation> pred)
{
if (registry == null)
if (invalidRegistry)
return;
if (!RegistryObject.this.optionalRegistry && !registryExists)
{
this.registry = RegistryManager.ACTIVE.getRegistry(registryName);
if (registry == null)
if (!registryExists(registryName))
{
invalidRegistry = true;
throw new IllegalStateException("Unable to find registry with key " + registryName + " for mod \"" + modid + "\". Check the 'caused by' to see further stack.", callerStack);
}
registryExists = true;
}
if (pred.test(registry.getRegistryName()))
RegistryObject.this.updateReference((IForgeRegistry<? extends T>) registry);
if (pred.test(registryName))
RegistryObject.this.updateReference(registryName);
}
});
IForgeRegistry<V> registry = RegistryManager.ACTIVE.getRegistry(registryName);
// allow registry to be null, this might be for a custom registry that does not exist yet
if (registry != null)
{
this.updateReference(((IForgeRegistry<? extends T>) registry));
}
this.updateReference(registryName);
}
/**
@@ -179,7 +312,46 @@ public final class RegistryObject<T extends IForgeRegistryEntry<? super T>> impl
@Deprecated(since = "1.18.1") // TODO: make package-private
public void updateReference(IForgeRegistry<? extends T> registry)
{
this.value = registry.containsKey(this.name) ? registry.getValue(getId()) : null;
this.value = registry.containsKey(this.name) ? registry.getValue(this.name) : null;
}
void updateReference(Registry<? extends T> registry)
{
this.value = registry.containsKey(this.name) ? registry.get(this.name) : null;
}
@SuppressWarnings("unchecked")
void updateReference(ResourceLocation registryName)
{
IForgeRegistry<? extends T> forgeRegistry = RegistryManager.ACTIVE.getRegistry(registryName);
if (forgeRegistry != null)
{
updateReference(forgeRegistry);
return;
}
Registry<? extends T> vanillaRegistry = (Registry<? extends T>) Registry.REGISTRY.get(registryName);
if (vanillaRegistry != null)
{
updateReference(vanillaRegistry);
return;
}
Registry<? extends T> builtinRegistry = (Registry<? extends T>) BuiltinRegistries.REGISTRY.get(registryName);
if (builtinRegistry != null)
{
updateReference(builtinRegistry);
return;
}
this.value = null;
}
private static boolean registryExists(ResourceLocation registryName)
{
return RegistryManager.ACTIVE.getRegistry(registryName) != null
|| Registry.REGISTRY.containsKey(registryName)
|| BuiltinRegistries.REGISTRY.containsKey(registryName);
}
public ResourceLocation getId()
@@ -0,0 +1,29 @@
/*
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.registries;
import net.minecraft.core.Registry;
import net.minecraft.data.BuiltinRegistries;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.event.IModBusEvent;
import org.jetbrains.annotations.NotNull;
/**
* Intermediary event for registering to vanilla registries from both {@link Registry} and {@link BuiltinRegistries}.
* This is a stopgap measure until 1.19 comes with the rework of forge registries, at which point it will be deleted.
* Internal for forge-uses only, see {@link DeferredRegister#vanillaRegister(VanillaRegisterEvent)} for how it is used.
*/
@Deprecated(forRemoval = true, since = "1.18.2")
class VanillaRegisterEvent extends Event implements IModBusEvent
{
@NotNull
final Registry<?> vanillaRegistry;
VanillaRegisterEvent(@NotNull Registry<?> vanillaRegistry)
{
this.vanillaRegistry = vanillaRegistry;
}
}