From 1f031ad2c3fd344837226fbb126cd3c7638ed08a Mon Sep 17 00:00:00 2001 From: SizableShrimp Date: Fri, 1 Apr 2022 15:49:46 -0500 Subject: [PATCH] Allow using DeferredRegisters for vanilla registries (#8527) Catch and aggregate exceptions when applying object holders --- .../minecraft/core/RegistryAccess.java.patch | 11 - .../net/minecraftforge/common/ForgeMod.java | 6 +- .../java/net/minecraftforge/common/Tags.java | 12 +- .../registries/DeferredRegister.java | 238 +++++++++++------ .../registries/ForgeRegistries.java | 2 +- .../minecraftforge/registries/GameData.java | 30 ++- .../registries/ObjectHolderRegistry.java | 23 +- .../registries/RegistryObject.java | 240 +++++++++++++++--- .../registries/VanillaRegisterEvent.java | 29 +++ src/main/resources/META-INF/coremods.json | 3 +- .../coremods/registry_object_binary_compat.js | 61 +++++ .../debug/DeferredRegistryTest.java | 33 +++ .../debug/RegistryObjectTest.java | 4 +- 13 files changed, 550 insertions(+), 142 deletions(-) delete mode 100644 patches/minecraft/net/minecraft/core/RegistryAccess.java.patch create mode 100644 src/main/java/net/minecraftforge/registries/VanillaRegisterEvent.java create mode 100644 src/main/resources/coremods/registry_object_binary_compat.js diff --git a/patches/minecraft/net/minecraft/core/RegistryAccess.java.patch b/patches/minecraft/net/minecraft/core/RegistryAccess.java.patch deleted file mode 100644 index 54e8728c62..0000000000 --- a/patches/minecraft/net/minecraft/core/RegistryAccess.java.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/net/minecraft/core/RegistryAccess.java -+++ b/net/minecraft/core/RegistryAccess.java -@@ -185,6 +_,8 @@ - private static void m_211081_(RegistryResourceAccess.InMemoryStorage p_211082_, RegistryAccess.RegistryData p_211083_) { - ResourceKey> resourcekey = p_211083_.f_123101_(); - Registry registry = BuiltinRegistries.f_206379_.m_175515_(resourcekey); -+ if (!resourcekey.equals(Registry.f_122818_)) -+ registry = ((Registry>)BuiltinRegistries.f_123858_).m_6246_((ResourceKey>)resourcekey); - - for(Entry, E> entry : registry.m_6579_()) { - ResourceKey resourcekey1 = entry.getKey(); diff --git a/src/main/java/net/minecraftforge/common/ForgeMod.java b/src/main/java/net/minecraftforge/common/ForgeMod.java index 4ec5fabc3e..71eee1be9f 100644 --- a/src/main/java/net/minecraftforge/common/ForgeMod.java +++ b/src/main/java/net/minecraftforge/common/ForgeMod.java @@ -95,8 +95,8 @@ public class ForgeMod public static final RegistryObject 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 MILK = RegistryObject.of(new ResourceLocation("milk"), ForgeRegistries.FLUIDS); - public static final RegistryObject FLOWING_MILK = RegistryObject.of(new ResourceLocation("flowing_milk"), ForgeRegistries.FLUIDS); + public static final RegistryObject MILK = RegistryObject.create(new ResourceLocation("milk"), ForgeRegistries.FLUIDS); + public static final RegistryObject 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) diff --git a/src/main/java/net/minecraftforge/common/Tags.java b/src/main/java/net/minecraftforge/common/Tags.java index ed4f1bd2da..764e4923ec 100644 --- a/src/main/java/net/minecraftforge/common/Tags.java +++ b/src/main/java/net/minecraftforge/common/Tags.java @@ -226,7 +226,10 @@ public class Tags public static final TagKey DYES_WHITE = DyeColor.WHITE.getTag(); public static final TagKey EGGS = tag("eggs"); - public static final TagKey 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 ENCHANTING_FUELS = tag("enchanting_fuels"); public static final TagKey END_STONES = tag("end_stones"); public static final TagKey ENDER_PEARLS = tag("ender_pearls"); public static final TagKey FEATHERS = tag("feathers"); @@ -391,14 +394,9 @@ public class Tags public static final TagKey STORAGE_BLOCKS_REDSTONE = tag("storage_blocks/redstone"); public static final TagKey STRING = tag("string"); - private static TagKey tag(String name, @Nullable Set> defaults) - { - return ItemTags.create(new ResourceLocation("forge", name)); - } - private static TagKey tag(String name) { - return tag(name, null); + return ItemTags.create(new ResourceLocation("forge", name)); } } diff --git a/src/main/java/net/minecraftforge/registries/DeferredRegister.java b/src/main/java/net/minecraftforge/registries/DeferredRegister.java index 2112c47cda..99f4796bd4 100644 --- a/src/main/java/net/minecraftforge/registries/DeferredRegister.java +++ b/src/main/java/net/minecraftforge/registries/DeferredRegister.java @@ -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; * } *} * - * @param The base registry type, must be a concrete base class, do not use subclasses or wild cards. + * @param The base registry type */ -public class DeferredRegister> +public class DeferredRegister { /** - * Use for vanilla/forge registries. See example above. + * DeferredRegister factory for forge registries that exist before this DeferredRegister is created. + *

+ * If you have a supplier, do not use this method. + * 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 > DeferredRegister create(IForgeRegistry reg, String modid) { @@ -68,56 +77,111 @@ public class DeferredRegister> @Deprecated(forRemoval = true, since = "1.18.2") public static > DeferredRegister create(Class 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. + *

+ * 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 > DeferredRegister create(ResourceKey> key, String modid) + public static DeferredRegister create(ResourceKey> 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. + *

+ * 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 DeferredRegister createOptional(ResourceKey> 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. + *

+ * 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 > DeferredRegister create(ResourceLocation registryName, String modid) + public static DeferredRegister 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 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. + *

+ * 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 DeferredRegister createOptional(ResourceLocation registryName, String modid) + { + return new DeferredRegister<>(ResourceKey.createRegistryKey(registryName), null, modid, true); + } + + @Nullable // Nullable when superType is not null + private final ResourceKey> registryKey; + @Nullable // Nullable when registryKey is not null + private final Class> superType; private final String modid; + private final boolean optionalRegistry; private final Map, Supplier> entries = new LinkedHashMap<>(); private final Set> entriesView = Collections.unmodifiableSet(entries.keySet()); - private IForgeRegistry type; - private Supplier> typeSupplier; - private Supplier> registryFactory; + @Nullable + private Supplier> registryFactory; + @Nullable private SetMultimap, Supplier> optionalTags; private boolean seenRegisterEvent = false; - private DeferredRegister(@Nullable ResourceLocation registryName, @Nullable Class base, String modid) + private > DeferredRegister(@Nullable ResourceKey> registryKey, @Nullable Class base, + String modid, boolean optionalRegistry) { - this.registryName = registryName; + this.registryKey = registryKey; this.superType = base; this.modid = modid; + this.optionalRegistry = optionalRegistry; } - private DeferredRegister(IForgeRegistry reg, String modid) + @SuppressWarnings("unchecked") + private > DeferredRegister(IForgeRegistry reg, String modid) { - this(reg.getRegistryName(), reg.getRegistrySuperType(), modid); - this.type = reg; + this((ResourceKey>) (ResourceKey) reg.getRegistryKey(), reg.getRegistrySuperType(), modid, false); } /** @@ -127,7 +191,7 @@ public class DeferredRegister> * @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 RegistryObject register(final String name, final Supplier sup) { if (seenRegisterEvent) @@ -137,19 +201,23 @@ public class DeferredRegister> final ResourceLocation key = new ResourceLocation(modid, name); RegistryObject ret; - IForgeRegistry 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) ret, () -> sup.get().setRegistryName(key)) != null) { + Supplier prevValue = entries.putIfAbsent((RegistryObject) 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> * * @deprecated Use {@link #create(ResourceLocation, String)} and {@link #makeRegistry(Class, Supplier)} instead */ + @SuppressWarnings("unchecked") @Deprecated(forRemoval = true, since = "1.18.2") - public Supplier> makeRegistry(final String name, final Supplier> sup) + public > Supplier> makeRegistry(final String name, final Supplier> sup) { - return makeRegistry(new ResourceLocation(modid, name), this.superType, sup); + return makeRegistry(new ResourceLocation(modid, name), (Class) this.superType, sup); } /** @@ -181,9 +250,9 @@ public class DeferredRegister> * @return A supplier of the {@link IForgeRegistry} created by the builder. * Will always return null until after the {@link NewRegistryEvent} event fires. */ - public Supplier> makeRegistry(final Class base, final Supplier> sup) + public > Supplier> makeRegistry(final Class base, final Supplier> sup) { - return makeRegistry(this.registryName, base, sup); + return makeRegistry(this.registryKey.location(), base, sup); } /** @@ -214,10 +283,10 @@ public class DeferredRegister> @NotNull public TagKey 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> 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> @Nullable public ResourceLocation getRegistryName() { - return this.registryName; + return this.registryKey == null ? null : this.registryKey.location(); } - private Supplier> makeRegistry(final ResourceLocation registryName, final Class superType, final Supplier> 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 > Supplier> makeRegistry(final ResourceLocation registryName, final Class superType, final Supplier> 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 registry; + + @Override + public IForgeRegistry get() + { + // Keep trying to capture until it's not null + if (registry == null) + registry = RegistryManager.ACTIVE.getRegistry(registryName); + + return registry; + } + }; } - private IForgeRegistry getForgeRegistry() - { - if (this.type == null && this.typeSupplier != null) - this.type = this.typeSupplier.get(); - - return this.type; - } - - private void onFill(IForgeRegistry registry) + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void onFill(IForgeRegistry registry) { if (this.optionalTags == null) return; - ITagManager tagManager = registry.tags(); + // TODO 1.19: Put back the 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 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 reg = (IForgeRegistry)event.getRegistry(); + IForgeRegistry reg = (IForgeRegistry) event.getRegistry(); for (Entry, Supplier> 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> 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, Supplier> e : entries.entrySet()) + { + Registry.register((Registry) event.vanillaRegistry, e.getKey().getId(), e.getValue().get()); + e.getKey().updateReference((Registry) 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; } } diff --git a/src/main/java/net/minecraftforge/registries/ForgeRegistries.java b/src/main/java/net/minecraftforge/registries/ForgeRegistries.java index da9bd99be0..e50e5f1fa4 100644 --- a/src/main/java/net/minecraftforge/registries/ForgeRegistries.java +++ b/src/main/java/net/minecraftforge/registries/ForgeRegistries.java @@ -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() { diff --git a/src/main/java/net/minecraftforge/registries/GameData.java b/src/main/java/net/minecraftforge/registries/GameData.java index a487a7a748..8c811c87a2 100644 --- a/src/main/java/net/minecraftforge/registries/GameData.java +++ b/src/main/java/net/minecraftforge/registries/GameData.java @@ -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> checkForRevertToVanilla(final Executor executor, final CompletableFuture> 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 extends IdMapper { diff --git a/src/main/java/net/minecraftforge/registries/ObjectHolderRegistry.java b/src/main/java/net/minecraftforge/registries/ObjectHolderRegistry.java index 8dc98d4f31..57c3bc0576 100644 --- a/src/main/java/net/minecraftforge/registries/ObjectHolderRegistry.java +++ b/src/main/java/net/minecraftforge/registries/ObjectHolderRegistry.java @@ -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> ref) + public static synchronized void addHandler(Consumer> ref) { objectHolders.add(ref); } @@ -59,7 +59,7 @@ public class ObjectHolderRegistry * * @return true if handler was matched and removed. */ - public static boolean removeHandler(Consumer> ref) + public static synchronized boolean removeHandler(Consumer> ref) { return objectHolders.remove(ref); } @@ -171,7 +171,24 @@ public class ObjectHolderRegistry public static void applyObjectHolders(Predicate 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; + } } } diff --git a/src/main/java/net/minecraftforge/registries/RegistryObject.java b/src/main/java/net/minecraftforge/registries/RegistryObject.java index 006a3fbc90..60fab715cc 100644 --- a/src/main/java/net/minecraftforge/registries/RegistryObject.java +++ b/src/main/java/net/minecraftforge/registries/RegistryObject.java @@ -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> implements Supplier +public final class RegistryObject implements Supplier { 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 , U extends T> RegistryObject of(final ResourceLocation name, Supplier> registryType) { return new RegistryObject<>(name, registryType); } - public static , U extends T> RegistryObject of(final ResourceLocation name, IForgeRegistry 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 , U extends T> RegistryObject of(final ResourceLocation name, IForgeRegistry 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 , U extends T> RegistryObject create(final ResourceLocation name, IForgeRegistry 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 , U extends T> RegistryObject of(final ResourceLocation name, final Class baseType, String modid) { @@ -47,32 +73,129 @@ public final class RegistryObject> 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. + *

+ * 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 , U extends T> RegistryObject of(final ResourceLocation name, final ResourceKey> key, String modid) { - return new RegistryObject<>(name, key.location(), modid); + @Deprecated(forRemoval = true, since = "1.18.2") + public static RegistryObject of(final ResourceLocation name, final ResourceKey> 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. + *

+ * 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 , U extends T> RegistryObject of(final ResourceLocation name, final ResourceLocation registryName, String modid) { - return new RegistryObject<>(name, registryName, modid); + public static RegistryObject create(final ResourceLocation name, final ResourceKey> 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. + *

+ * 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 RegistryObject createOptional(final ResourceLocation name, final ResourceKey> 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. + *

+ * 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 RegistryObject 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. + *

+ * 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 RegistryObject 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. + *

+ * 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 RegistryObject createOptional(final ResourceLocation name, final ResourceLocation registryName, String modid) + { + return new RegistryObject<>(name, registryName, modid, true); } private static RegistryObject EMPTY = new RegistryObject<>(); - private static > RegistryObject empty() { + private static RegistryObject empty() { @SuppressWarnings("unchecked") RegistryObject t = (RegistryObject) EMPTY; return t; @@ -80,6 +203,7 @@ public final class RegistryObject> impl private RegistryObject() { this.name = null; + this.optionalRegistry = false; } @Deprecated(forRemoval = true, since = "1.18.2") @@ -94,6 +218,7 @@ public final class RegistryObject> 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> impl private > RegistryObject(final ResourceLocation name, final Class baseType, final String modid) { this.name = name; + this.optionalRegistry = false; final Throwable callerStack = new Throwable("Calling Site from mod: " + modid); ObjectHolderRegistry.addHandler(new Consumer>() { private IForgeRegistry registry; + private boolean invalidRegistry = false; @Override public void accept(Predicate 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) registry); @@ -133,34 +265,35 @@ public final class RegistryObject> impl } } - @SuppressWarnings("unchecked") - private > 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 registry; + private boolean registryExists = false; + private boolean invalidRegistry = false; @Override public void accept(Predicate 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) registry); + if (pred.test(registryName)) + RegistryObject.this.updateReference(registryName); } }); - IForgeRegistry 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) registry)); - } + this.updateReference(registryName); } /** @@ -179,7 +312,46 @@ public final class RegistryObject> impl @Deprecated(since = "1.18.1") // TODO: make package-private public void updateReference(IForgeRegistry 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 registry) + { + this.value = registry.containsKey(this.name) ? registry.get(this.name) : null; + } + + @SuppressWarnings("unchecked") + void updateReference(ResourceLocation registryName) + { + IForgeRegistry forgeRegistry = RegistryManager.ACTIVE.getRegistry(registryName); + if (forgeRegistry != null) + { + updateReference(forgeRegistry); + return; + } + + Registry vanillaRegistry = (Registry) Registry.REGISTRY.get(registryName); + if (vanillaRegistry != null) + { + updateReference(vanillaRegistry); + return; + } + + Registry builtinRegistry = (Registry) 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() diff --git a/src/main/java/net/minecraftforge/registries/VanillaRegisterEvent.java b/src/main/java/net/minecraftforge/registries/VanillaRegisterEvent.java new file mode 100644 index 0000000000..5a8fedf9ae --- /dev/null +++ b/src/main/java/net/minecraftforge/registries/VanillaRegisterEvent.java @@ -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; + } +} diff --git a/src/main/resources/META-INF/coremods.json b/src/main/resources/META-INF/coremods.json index bc26fe0f09..cdba3b4887 100644 --- a/src/main/resources/META-INF/coremods.json +++ b/src/main/resources/META-INF/coremods.json @@ -1,5 +1,6 @@ { "field_to_method": "coremods/field_to_method.js", "field_to_instanceof": "coremods/field_to_instanceof.js", - "add_bouncer_method": "coremods/add_bouncer_method.js" + "add_bouncer_method": "coremods/add_bouncer_method.js", + "registry_object_binary_compat": "coremods/registry_object_binary_compat.js" } diff --git a/src/main/resources/coremods/registry_object_binary_compat.js b/src/main/resources/coremods/registry_object_binary_compat.js new file mode 100644 index 0000000000..c73e118241 --- /dev/null +++ b/src/main/resources/coremods/registry_object_binary_compat.js @@ -0,0 +1,61 @@ +var Opcodes = Java.type('org.objectweb.asm.Opcodes') +var MethodNode = Java.type('org.objectweb.asm.tree.MethodNode') +var VarInsnNode = Java.type('org.objectweb.asm.tree.VarInsnNode') +var MethodInsnNode = Java.type('org.objectweb.asm.tree.MethodInsnNode') +var TypeInsnNode = Java.type('org.objectweb.asm.tree.TypeInsnNode') +var InsnNode = Java.type('org.objectweb.asm.tree.InsnNode') + +// TODO 1.19: Delete this whole coremod. We won't need to maintain binary compatibility then. +// This coremod exists purely because of Java limitations - 2 methods with the same parameters and name but different return types are not allowed. +// However, this restriction does not exist in the JVM. +// The bounds of RegistryObject's T generic parameter were changed to no longer require IForgeRegistryEntry. +// This changes the erased type of the T parameter to just Object instead of IForgeRegistryEntry. +// This coremod was made to maintain compatibility with mods compiled on old forge in the least invasive way. +// It adds runtime methods for the 4 methods in RegistryObject that return type T to allow calling them with the erased bound of IForgeRegistryEntry. +// The ASM methods just delegate to the original methods and return with a cast. +function initializeCoreMod() { + return { + 'registry_object_binary_compat': { + 'target': { + 'type': 'CLASS', + 'name': 'net.minecraftforge.registries.RegistryObject' + }, + 'transformer': function (classNode) { + var getNode = new MethodNode(Opcodes.ACC_PUBLIC, 'get', '()Lnet/minecraftforge/registries/IForgeRegistryEntry;', null, null) + getNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)) + getNode.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, 'net/minecraftforge/registries/RegistryObject', 'get', '()Ljava/lang/Object;')) + getNode.instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, 'net/minecraftforge/registries/IForgeRegistryEntry')) + getNode.instructions.add(new InsnNode(Opcodes.ARETURN)) + + var orElseNode = new MethodNode(Opcodes.ACC_PUBLIC, 'orElse', '(Lnet/minecraftforge/registries/IForgeRegistryEntry;)Lnet/minecraftforge/registries/IForgeRegistryEntry;', null, null) + orElseNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)) + orElseNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)) + orElseNode.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, 'net/minecraftforge/registries/RegistryObject', 'orElse', '(Ljava/lang/Object;)Ljava/lang/Object;')) + orElseNode.instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, 'net/minecraftforge/registries/IForgeRegistryEntry')) + orElseNode.instructions.add(new InsnNode(Opcodes.ARETURN)) + + var orElseGetNode = new MethodNode(Opcodes.ACC_PUBLIC, 'orElseGet', '(Ljava/util/function/Supplier;)Lnet/minecraftforge/registries/IForgeRegistryEntry;', '(Ljava/util/function/Supplier<+TT;>;)Lnet/minecraftforge/registries/IForgeRegistryEntry;', null) + orElseGetNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)) + orElseGetNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)) + orElseGetNode.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, 'net/minecraftforge/registries/RegistryObject', 'orElseGet', '(Ljava/util/function/Supplier;)Ljava/lang/Object;')) + orElseGetNode.instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, 'net/minecraftforge/registries/IForgeRegistryEntry')) + orElseGetNode.instructions.add(new InsnNode(Opcodes.ARETURN)) + + // orElseThrow + var orElseThrowNode = new MethodNode(Opcodes.ACC_PUBLIC, 'orElseThrow', '(Ljava/util/function/Supplier;)Lnet/minecraftforge/registries/IForgeRegistryEntry;', '\<\X:Ljava/lang/Throwable;>(Ljava/util/function/Supplier<+TX;>;)Lnet/minecraftforge/registries/IForgeRegistryEntry;^TX;', ['java/lang/Throwable']) + orElseThrowNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)) + orElseThrowNode.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)) + orElseThrowNode.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, 'net/minecraftforge/registries/RegistryObject', 'orElseThrow', '(Ljava/util/function/Supplier;)Ljava/lang/Object;')) + orElseThrowNode.instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, 'net/minecraftforge/registries/IForgeRegistryEntry')) + orElseThrowNode.instructions.add(new InsnNode(Opcodes.ARETURN)) + + classNode.methods.add(getNode) + classNode.methods.add(orElseNode) + classNode.methods.add(orElseGetNode) + classNode.methods.add(orElseThrowNode) + + return classNode + } + } + } +} \ No newline at end of file diff --git a/src/test/java/net/minecraftforge/debug/DeferredRegistryTest.java b/src/test/java/net/minecraftforge/debug/DeferredRegistryTest.java index 5dfc403eea..512a5f4039 100644 --- a/src/test/java/net/minecraftforge/debug/DeferredRegistryTest.java +++ b/src/test/java/net/minecraftforge/debug/DeferredRegistryTest.java @@ -5,11 +5,19 @@ package net.minecraftforge.debug; +import java.util.List; import java.util.Set; import java.util.function.Supplier; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.data.worldgen.features.OreFeatures; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.TagKey; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.server.ServerStartedEvent; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -41,10 +49,19 @@ public class DeferredRegistryTest { private static final DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID); private static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID); private static final DeferredRegister CUSTOMS = DeferredRegister.create(new ResourceLocation(MODID, "test_registry"), MODID); + private static final DeferredRegister DOESNT_EXIST_REG = DeferredRegister.createOptional(new ResourceLocation(MODID, "doesnt_exist"), MODID); + // Vanilla Registry - filled directly after all RegistryEvent.Register events are fired + private static final DeferredRegister> RECIPE_TYPES = DeferredRegister.create(Registry.RECIPE_TYPE_REGISTRY, MODID); + // Vanilla Builtin Registry - filled directly after all RegistryEvent.Register events are fired + private static final DeferredRegister PLACED_FEATURES = DeferredRegister.create(Registry.PLACED_FEATURE_REGISTRY, MODID); private static final RegistryObject BLOCK = BLOCKS.register("test", () -> new Block(Block.Properties.of(Material.STONE))); private static final RegistryObject ITEM = ITEMS .register("test", () -> new BlockItem(BLOCK.get(), new Item.Properties().tab(CreativeModeTab.TAB_BUILDING_BLOCKS))); private static final RegistryObject CUSTOM = CUSTOMS.register("test", () -> new Custom(){}); + // Should never be created as the registry doesn't exist - this should silently fail and remain empty + private static final RegistryObject DOESNT_EXIST = DOESNT_EXIST_REG.register("test", Object::new); + private static final RegistryObject> RECIPE_TYPE = RECIPE_TYPES.register("test", () -> new RecipeType<>() {}); + private static final RegistryObject PLACED_FEATURE = PLACED_FEATURES.register("test", () -> new PlacedFeature(Holder.hackyErase(OreFeatures.ORE_DIORITE), List.of())); private static final TagKey CUSTOM_TAG_KEY = CUSTOMS.createOptionalTagKey("test_tag", Set.of(CUSTOM)); private static final Supplier> CUSTOM_REG = CUSTOMS.makeRegistry(Custom.class, () -> @@ -57,7 +74,23 @@ public class DeferredRegistryTest { BLOCKS.register(modBus); ITEMS.register(modBus); CUSTOMS.register(modBus); + RECIPE_TYPES.register(modBus); + PLACED_FEATURES.register(modBus); modBus.addListener(this::gatherData); + + MinecraftForge.EVENT_BUS.addListener(this::serverStarted); + } + + public void serverStarted(ServerStartedEvent event) + { + // Validate all the RegistryObjects are filled / not filled + BLOCK.get(); + ITEM.get(); + CUSTOM.get(); + if (DOESNT_EXIST.isPresent()) + throw new IllegalStateException("DeferredRegistryTest#DOESNT_EXIST should not be present!"); + RECIPE_TYPE.get(); + PLACED_FEATURE.get(); } public void gatherData(GatherDataEvent event) { diff --git a/src/test/java/net/minecraftforge/debug/RegistryObjectTest.java b/src/test/java/net/minecraftforge/debug/RegistryObjectTest.java index 28afe77ff2..cf091a1d57 100644 --- a/src/test/java/net/minecraftforge/debug/RegistryObjectTest.java +++ b/src/test/java/net/minecraftforge/debug/RegistryObjectTest.java @@ -40,7 +40,7 @@ public class RegistryObjectTest public void commonSetup(FMLCommonSetupEvent event) { - LOGGER.info("Stone 1: {}", RegistryObject.of(new ResourceLocation("minecraft", "stone"), ForgeRegistries.BLOCKS).get()); - LOGGER.info("Stone 2: {}", RegistryObject.of(new ResourceLocation("minecraft", "stone"), ForgeRegistries.BLOCKS.getRegistryKey(), MODID).get()); + LOGGER.info("Stone 1: {}", RegistryObject.create(new ResourceLocation("minecraft", "stone"), ForgeRegistries.BLOCKS).get()); + LOGGER.info("Stone 2: {}", RegistryObject.create(new ResourceLocation("minecraft", "stone"), ForgeRegistries.BLOCKS.getRegistryKey(), MODID).get()); } }