diff --git a/src/main/java/net/minecraftforge/fml/RegistryObject.java b/src/main/java/net/minecraftforge/fml/RegistryObject.java index af1bb0cde..cdf22d76c 100644 --- a/src/main/java/net/minecraftforge/fml/RegistryObject.java +++ b/src/main/java/net/minecraftforge/fml/RegistryObject.java @@ -50,6 +50,10 @@ public final class RegistryObject> impl return new RegistryObject<>(name, registry); } + public static , U extends T> RegistryObject of(final ResourceLocation name, final Class baseType, String modid) { + return new RegistryObject<>(name, baseType, modid); + } + private static RegistryObject EMPTY = new RegistryObject<>(); private static > RegistryObject empty() { @@ -80,6 +84,30 @@ public final class RegistryObject> impl }); } + @SuppressWarnings("unchecked") + private > RegistryObject(final ResourceLocation name, final Class baseType, final String modid) + { + this.name = name; + final Throwable callerStack = new Throwable("Calling Site from mod: " + modid); + ObjectHolderRegistry.addHandler(new Consumer>() + { + private IForgeRegistry registry; + + @Override + public void accept(Predicate pred) + { + if (registry == null) + { + this.registry = RegistryManager.ACTIVE.getRegistry(baseType); + if (registry == null) + throw new IllegalStateException("Unable to find registry for type " + baseType.getName() + " for mod \"" + modid + "\". Check the 'caused by' to see futher stack.", callerStack); + } + if (pred.test(registry.getRegistryName())) + RegistryObject.this.value = registry.containsKey(RegistryObject.this.name) ? (T)registry.getValue(RegistryObject.this.name) : null; + } + }); + } + /** * Directly retrieves the wrapped Registry Object. This value will automatically be updated when the backing registry is updated. * Will throw NPE if the value is null, use isPresent to check first. Or use any of the other guarded functions. diff --git a/src/main/java/net/minecraftforge/registries/DeferredRegister.java b/src/main/java/net/minecraftforge/registries/DeferredRegister.java index 0405fb402..277720470 100644 --- a/src/main/java/net/minecraftforge/registries/DeferredRegister.java +++ b/src/main/java/net/minecraftforge/registries/DeferredRegister.java @@ -21,6 +21,7 @@ package net.minecraftforge.registries; import net.minecraft.util.ResourceLocation; import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.RegistryObject; @@ -33,6 +34,8 @@ import java.util.Objects; import java.util.Set; import java.util.function.Supplier; +import com.google.common.reflect.TypeToken; + /** * Utility class to help with managing registry entries. * Maintains a list of all suppliers for entries and registers them during the proper Register event. @@ -56,15 +59,41 @@ import java.util.function.Supplier; */ public class DeferredRegister> { - private final IForgeRegistry type; + /** + * Use for vanilla/forge registries. See example above. + */ + public static > DeferredRegister create(IForgeRegistry reg, String modid) + { + return new DeferredRegister(reg, modid); + } + + /** + * Use for custom registries that are made during the NewRegistry event. + */ + public static > DeferredRegister create(Class base, String modid) + { + return new DeferredRegister(base, modid); + } + + private final Class superType; private final String modid; private final Map, Supplier> entries = new LinkedHashMap<>(); private final Set> entriesView = Collections.unmodifiableSet(entries.keySet()); + private IForgeRegistry type; + private Supplier> registryFactory; + + private DeferredRegister(Class base, String modid) + { + this.superType = base; + this.modid = modid; + } + + @Deprecated // Make private in 1.16, Use create. Constructors are an implementation detail. public DeferredRegister(IForgeRegistry reg, String modid) { + this(reg.getRegistrySuperType(), modid); this.type = reg; - this.modid = modid; } /** @@ -80,13 +109,41 @@ public class DeferredRegister> Objects.requireNonNull(name); Objects.requireNonNull(sup); final ResourceLocation key = new ResourceLocation(modid, name); - RegistryObject ret = RegistryObject.of(key, this.type); + + RegistryObject ret; + if (this.type != null) + ret = RegistryObject.of(key, this.type); + else if (this.superType != null) + ret = RegistryObject.of(key, this.superType, this.modid); + else + throw new IllegalStateException("Could not create RegistryObject in DeferredRegister"); + if (entries.putIfAbsent((RegistryObject) ret, () -> sup.get().setRegistryName(key)) != null) { throw new IllegalArgumentException("Duplicate registration " + name); } + return ret; } + /** + * For custom registries only, fills the {@link #registryFactory} to be called later see {@link #register(IEventBus)} + * + * Calls {@link RegistryBuilder#setName} and {@link RegistryBuilder#setType} automatically. + * + * @param name Path of the registry's {@link ResourceLocation} + * @param sup Supplier of the RegistryBuilder that is called to fill {@link #type} during the NewRegistry event + * @return A supplier of the {@link IForgeRegistry} created by the builder. + */ + public Supplier> makeRegistry(final String name, final Supplier> sup) { + if (this.superType == null) + throw new IllegalStateException("Cannot create a registry without specifying a base type"); + if (this.type != null || this.registryFactory != null) + throw new IllegalStateException("Cannot create a registry for a type that already exists"); + + this.registryFactory = () -> sup.get().setName(new ResourceLocation(modid, name)).setType(this.superType); + return () -> this.type; + } + /** * Adds our event handler to the specified event bus, this MUST be called in order for this class to function. * See the example usage. @@ -96,6 +153,12 @@ public class DeferredRegister> public void register(IEventBus bus) { bus.addListener(this::addEntries); + if (this.type == null) { + if (this.registryFactory != null) + bus.addListener(this::createRegistry); + else + bus.addListener(EventPriority.LOWEST, this::captureRegistry); + } } /** @@ -108,7 +171,7 @@ public class DeferredRegister> private void addEntries(RegistryEvent.Register event) { - if (event.getGenericType() == this.type.getRegistrySuperType()) + if (this.type != null && event.getGenericType() == this.type.getRegistrySuperType()) { @SuppressWarnings("unchecked") IForgeRegistry reg = (IForgeRegistry)event.getRegistry(); @@ -119,4 +182,21 @@ public class DeferredRegister> } } } + + private void createRegistry(RegistryEvent.NewRegistry event) + { + this.type = this.registryFactory.get().create(); + } + + private void captureRegistry(RegistryEvent.NewRegistry event) + { + 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"); + } + else + throw new IllegalStateException("Unable to find registry for mod \"" + modid + "\" No lookup criteria specified."); + } } diff --git a/src/main/java/net/minecraftforge/registries/IForgeRegistry.java b/src/main/java/net/minecraftforge/registries/IForgeRegistry.java index 77b4c9102..6db38d650 100644 --- a/src/main/java/net/minecraftforge/registries/IForgeRegistry.java +++ b/src/main/java/net/minecraftforge/registries/IForgeRegistry.java @@ -26,12 +26,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import net.minecraft.util.ResourceLocation; - -import net.minecraft.util.ResourceLocation; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - /** * Main interface for the registry system. Use this to query the registry system. * @@ -72,6 +66,7 @@ public interface IForgeRegistry> extends Iterab * Callback fired when objects are added to the registry. This will fire when the registry is rebuilt * on the client side from a server side synchronization, or when a world is loaded. */ + @FunctionalInterface interface AddCallback> { void onAdd(IForgeRegistryInternal owner, RegistryManager stage, int id, V obj, @Nullable V oldObj); @@ -81,6 +76,7 @@ public interface IForgeRegistry> extends Iterab * Callback fired when the registry is cleared. This is done before a registry is reloaded from client * or server. */ + @FunctionalInterface interface ClearCallback> { void onClear(IForgeRegistryInternal owner, RegistryManager stage); @@ -89,6 +85,7 @@ public interface IForgeRegistry> extends Iterab /** * Callback fired when a registry instance is created. Populate slave maps here. */ + @FunctionalInterface interface CreateCallback> { void onCreate(IForgeRegistryInternal owner, RegistryManager stage); @@ -97,6 +94,7 @@ public interface IForgeRegistry> extends Iterab /** * Callback fired when the registry contents are validated. */ + @FunctionalInterface interface ValidateCallback> { void onValidate(IForgeRegistryInternal owner, RegistryManager stage, int id, ResourceLocation key, V obj); @@ -105,6 +103,7 @@ public interface IForgeRegistry> extends Iterab /** * Callback fired when the registry is done processing. Used to calculate state ID maps. */ + @FunctionalInterface interface BakeCallback> { void onBake(IForgeRegistryInternal owner, RegistryManager stage); @@ -113,6 +112,7 @@ public interface IForgeRegistry> extends Iterab /** * Factory for creating dummy entries, allowing worlds to be loaded and keep the missing block references. */ + @FunctionalInterface interface DummyFactory> { V createDummy(ResourceLocation key); @@ -121,6 +121,7 @@ public interface IForgeRegistry> extends Iterab /** * */ + @FunctionalInterface interface MissingFactory> { V createMissing(ResourceLocation key, boolean isNetwork); diff --git a/src/main/java/net/minecraftforge/registries/ObjectHolderRegistry.java b/src/main/java/net/minecraftforge/registries/ObjectHolderRegistry.java index de4d315b2..70891b0b6 100644 --- a/src/main/java/net/minecraftforge/registries/ObjectHolderRegistry.java +++ b/src/main/java/net/minecraftforge/registries/ObjectHolderRegistry.java @@ -45,7 +45,6 @@ import org.apache.logging.log4j.Logger; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; -import static net.minecraftforge.fml.Logging.CORE; import static net.minecraftforge.registries.ForgeRegistry.REGISTRIES; /** diff --git a/src/main/java/net/minecraftforge/registries/RegistryBuilder.java b/src/main/java/net/minecraftforge/registries/RegistryBuilder.java index 17bb0e533..92a17397e 100644 --- a/src/main/java/net/minecraftforge/registries/RegistryBuilder.java +++ b/src/main/java/net/minecraftforge/registries/RegistryBuilder.java @@ -108,48 +108,83 @@ public class RegistryBuilder> return this; } + public RegistryBuilder onAdd(AddCallback add) + { + return this.add(add); + } + public RegistryBuilder add(ClearCallback clear) { this.clearCallback.add(clear); return this; } + public RegistryBuilder onClear(ClearCallback clear) + { + return this.add(clear); + } + public RegistryBuilder add(CreateCallback create) { this.createCallback.add(create); return this; } + public RegistryBuilder onCreate(CreateCallback create) + { + return this.add(create); + } + public RegistryBuilder add(ValidateCallback validate) { this.validateCallback.add(validate); return this; } + public RegistryBuilder onValidate(ValidateCallback validate) + { + return this.add(validate); + } + public RegistryBuilder add(BakeCallback bake) { this.bakeCallback.add(bake); return this; } + public RegistryBuilder onBake(BakeCallback bake) + { + return this.add(bake); + } + public RegistryBuilder set(DummyFactory factory) { this.dummyFactory = factory; return this; } + public RegistryBuilder dummy(DummyFactory factory) + { + return this.set(factory); + } + public RegistryBuilder set(MissingFactory missing) { this.missingFactory = missing; return this; } + public RegistryBuilder missing(MissingFactory missing) + { + return this.set(missing); + } + public RegistryBuilder disableSaving() { this.saveToDisc = false; return this; } - + public RegistryBuilder disableSync() { this.sync = false; @@ -167,12 +202,12 @@ public class RegistryBuilder> this.allowModifications = true; return this; } - + public RegistryBuilder legacyName(String name) { return legacyName(new ResourceLocation(name)); } - + public RegistryBuilder legacyName(ResourceLocation name) { this.legacyNames.add(name); @@ -306,12 +341,12 @@ public class RegistryBuilder> { return saveToDisc; } - + public boolean getSync() { return sync; } - + public Set getLegacyNames() { return legacyNames; diff --git a/src/test/java/net/minecraftforge/debug/DeferredRegistryTest.java b/src/test/java/net/minecraftforge/debug/DeferredRegistryTest.java new file mode 100644 index 000000000..c5971ae42 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/DeferredRegistryTest.java @@ -0,0 +1,75 @@ +package net.minecraftforge.debug; + +import java.util.function.Supplier; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.data.DataGenerator; +import net.minecraft.item.BlockItem; +import net.minecraft.item.Item; +import net.minecraft.item.ItemGroup; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.generators.BlockStateProvider; +import net.minecraftforge.client.model.generators.ModelFile; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.RegistryObject; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.GatherDataEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.ForgeRegistryEntry; +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.RegistryBuilder; + +@SuppressWarnings("unused") +@Mod(DeferredRegistryTest.MODID) +public class DeferredRegistryTest { + static final String MODID = "deferred_registry_test"; + private static final Logger LOGGER = LogManager.getLogger(); + + 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(Custom.class, MODID); + + private static final RegistryObject BLOCK = BLOCKS.register("test", () -> new Block(Block.Properties.create(Material.ROCK))); + private static final RegistryObject ITEM = ITEMS .register("test", () -> new BlockItem(BLOCK.get(), new Item.Properties().group(ItemGroup.BUILDING_BLOCKS))); + private static final RegistryObject CUSTOM = CUSTOMS.register("test", () -> new Custom(){}); + + private static final Supplier> CUSTOM_REG = CUSTOMS.makeRegistry("test_registry", () -> + new RegistryBuilder().disableSaving().setMaxID(Integer.MAX_VALUE - 1) + .onAdd((owner, stage, id, obj, old) -> LOGGER.info("Custom Added: " + id + " " + obj.foo())) + ); + + public DeferredRegistryTest() { + IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus(); + BLOCKS.register(modBus); + ITEMS.register(modBus); + CUSTOMS.register(modBus); + modBus.addListener(this::gatherData); + } + + public void gatherData(GatherDataEvent event) { + DataGenerator gen = event.getGenerator(); + + if (event.includeClient()) { + gen.addProvider(new BlockStateProvider(gen, MODID, event.getExistingFileHelper()) { + @Override + protected void registerStatesAndModels() { + ModelFile model = models().cubeAll(BLOCK.get().getRegistryName().getPath(), mcLoc("block/furnace_top")); + simpleBlock(BLOCK.get(), model); + simpleBlockItem(BLOCK.get(), model); + } + }); + } + } + + public static class Custom extends ForgeRegistryEntry { + public String foo() { + return this.getClass().getName(); + } + } +} diff --git a/src/test/java/net/minecraftforge/debug/block/PistonEventTest.java b/src/test/java/net/minecraftforge/debug/block/PistonEventTest.java index 9d535ebb8..fc6b75fe7 100644 --- a/src/test/java/net/minecraftforge/debug/block/PistonEventTest.java +++ b/src/test/java/net/minecraftforge/debug/block/PistonEventTest.java @@ -40,7 +40,6 @@ import net.minecraft.world.World; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.model.generators.BlockStateProvider; import net.minecraftforge.client.model.generators.ExistingFileHelper; -import net.minecraftforge.client.model.generators.ItemModelProvider; import net.minecraftforge.client.model.generators.ModelFile; import net.minecraftforge.event.world.PistonEvent; import net.minecraftforge.event.world.PistonEvent.PistonMoveType; @@ -66,8 +65,8 @@ public class PistonEventTest { public static final String MODID = "piston_event_test"; public static String blockName = "shiftonmove"; - private static DeferredRegister BLOCKS = new DeferredRegister(ForgeRegistries.BLOCKS, MODID); - private static DeferredRegister ITEMS = new DeferredRegister (ForgeRegistries.ITEMS, MODID); + private static DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID); + private static DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID); private static RegistryObject shiftOnMove = BLOCKS.register(blockName, () -> new Block(Block.Properties.create(Material.ROCK))); static { diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml index cbfd420e2..ca66e67f8 100644 --- a/src/test/resources/META-INF/mods.toml +++ b/src/test/resources/META-INF/mods.toml @@ -47,6 +47,12 @@ loaderVersion="[28,)" modId="nameplate_render_test" [[mods]] modId="global_loot_test" +[[dependencies.global_loot_test]] + modId="forge" + mandatory=true + versionRange="[1,)" + ordering="AFTER" + side="BOTH" [[mods]] modId="custom_plant_type_test" [[mods]] @@ -57,9 +63,5 @@ loaderVersion="[28,)" modId="music_disc_test" [[mods]] modId="stencil_enable_test" -[[dependencies.global_loot_test]] - modId="forge" - mandatory=true - versionRange="[1,)" - ordering="AFTER" - side="BOTH" +[[mods]] + modId="deferred_registry_test"