From d3da111c0804881400fb229a828c6311ed086783 Mon Sep 17 00:00:00 2001 From: Steven Blom Date: Mon, 10 Oct 2016 07:34:38 +1100 Subject: [PATCH] Allow all Forge Registries to work with ObjectHolder (#3220) --- .../net/minecraftforge/fml/common/Loader.java | 1 + .../fml/common/registry/ObjectHolderRef.java | 63 +++++---- .../registry/PersistentRegistryManager.java | 6 +- .../debug/ObjectHolderTest.java | 131 ++++++++++++++++++ 4 files changed, 174 insertions(+), 27 deletions(-) create mode 100644 src/test/java/net/minecraftforge/debug/ObjectHolderTest.java diff --git a/src/main/java/net/minecraftforge/fml/common/Loader.java b/src/main/java/net/minecraftforge/fml/common/Loader.java index 763dffeaf5..da69d59c59 100644 --- a/src/main/java/net/minecraftforge/fml/common/Loader.java +++ b/src/main/java/net/minecraftforge/fml/common/Loader.java @@ -601,6 +601,7 @@ public class Loader FMLLog.warning("There were errors previously. Not beginning mod initialization phase"); return; } + PersistentRegistryManager.fireCreateRegistryEvents(); ObjectHolderRegistry.INSTANCE.findObjectHolders(discoverer.getASMTable()); ItemStackHolderInjector.INSTANCE.findHolders(discoverer.getASMTable()); CapabilityManager.INSTANCE.injectCapabilities(discoverer.getASMTable()); diff --git a/src/main/java/net/minecraftforge/fml/common/registry/ObjectHolderRef.java b/src/main/java/net/minecraftforge/fml/common/registry/ObjectHolderRef.java index a04d9ff981..b9e1ac7c7d 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/ObjectHolderRef.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/ObjectHolderRef.java @@ -20,17 +20,15 @@ package net.minecraftforge.fml.common.registry; import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Queue; import net.minecraft.util.ResourceLocation; import org.apache.logging.log4j.Level; import com.google.common.base.Throwables; -import net.minecraft.block.Block; -import net.minecraft.init.Blocks; -import net.minecraft.item.Item; import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.registry.GameRegistry.ObjectHolder; @@ -44,15 +42,16 @@ import net.minecraftforge.fml.common.registry.GameRegistry.ObjectHolder; class ObjectHolderRef { private Field field; private ResourceLocation injectedObject; - private boolean isBlock; - private boolean isItem; + private boolean isValid; + private IForgeRegistry registry; ObjectHolderRef(Field field, ResourceLocation injectedObject, boolean extractFromExistingValues) { + registry = getRegistryForType(field); + this.field = field; - this.isBlock = Block.class.isAssignableFrom(field.getType()); - this.isItem = Item.class.isAssignableFrom(field.getType()); + this.isValid = registry != null; if (extractFromExistingValues) { try @@ -63,14 +62,15 @@ class ObjectHolderRef { { this.injectedObject = null; this.field = null; - this.isBlock = false; - this.isItem = false; + this.isValid = false; return; } else { - ResourceLocation tmp = isBlock ? ForgeRegistries.BLOCKS.getKey((Block)existing) : - isItem ? ForgeRegistries.ITEMS.getKey((Item)existing) : null; + ResourceLocation tmp = null; + if (registry != null) { + tmp = registry.getKey((IForgeRegistryEntry) existing); + } this.injectedObject = tmp; } } catch (Exception e) @@ -85,7 +85,7 @@ class ObjectHolderRef { if (this.injectedObject == null || !isValid()) { - throw new IllegalStateException(String.format("The ObjectHolder annotation cannot apply to a field that is not an Item or Block (found : %s at %s.%s)", field.getType().getName(), field.getClass().getName(), field.getName())); + throw new IllegalStateException(String.format("The ObjectHolder annotation cannot apply to a field that does not map to a registry. Ensure the registry was created during the RegistryEvent.NewRegistry event. (found : %s at %s.%s)", field.getType().getName(), field.getClass().getName(), field.getName())); } try { @@ -97,24 +97,37 @@ class ObjectHolderRef { } } + private IForgeRegistry getRegistryForType(Field field) + { + Queue> typesToExamine = new LinkedList>(); + typesToExamine.add(field.getType()); + IForgeRegistry registry = null; + while (!typesToExamine.isEmpty() && registry == null) { + Class type = typesToExamine.remove(); + Collections.addAll(typesToExamine, type.getInterfaces()); + if (IForgeRegistryEntry.class.isAssignableFrom(type)) + { + registry = PersistentRegistryManager.findRegistryByType((Class) type); + final Class parentType = type.getSuperclass(); + if (parentType != null) + { + typesToExamine.add(parentType); + } + } + } + return registry; + } + public boolean isValid() { - return isBlock || isItem; + return isValid; } public void apply() { Object thing; - if (isBlock) + if (isValid && registry.containsKey(injectedObject)) { - thing = ForgeRegistries.BLOCKS.getValue(injectedObject); - if (thing == Blocks.AIR) - { - thing = null; - } - } - else if (isItem) - { - thing = ForgeRegistries.ITEMS.getValue(injectedObject); + thing = registry.getValue(injectedObject); } else { diff --git a/src/main/java/net/minecraftforge/fml/common/registry/PersistentRegistryManager.java b/src/main/java/net/minecraftforge/fml/common/registry/PersistentRegistryManager.java index 71010a3480..545dcb233b 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/PersistentRegistryManager.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/PersistentRegistryManager.java @@ -67,10 +67,12 @@ import com.google.common.collect.Sets.SetView; @SuppressWarnings("WeakerAccess") public class PersistentRegistryManager { + public static void fireCreateRegistryEvents() { + MinecraftForge.EVENT_BUS.post(new RegistryEvent.NewRegistry()); + } + public static void fireRegistryEvents() { - MinecraftForge.EVENT_BUS.post(new RegistryEvent.NewRegistry()); - List registryKeys = Lists.newArrayList(PersistentRegistry.ACTIVE.registries.keySet()); Collections.sort(registryKeys, new Comparator() { diff --git a/src/test/java/net/minecraftforge/debug/ObjectHolderTest.java b/src/test/java/net/minecraftforge/debug/ObjectHolderTest.java new file mode 100644 index 0000000000..67ea19b2f3 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/ObjectHolderTest.java @@ -0,0 +1,131 @@ +package net.minecraftforge.debug; + +import net.minecraft.block.Block; +import net.minecraft.potion.Potion; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundEvent; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.registry.GameRegistry; +import net.minecraftforge.fml.common.registry.IForgeRegistry; +import net.minecraftforge.fml.common.registry.IForgeRegistryEntry; +import net.minecraftforge.fml.common.registry.RegistryBuilder; + +@Mod(modid= ObjectHolderTest.MODID, name = "ObjectHolderTests", version = "1.0") +public class ObjectHolderTest +{ + public static final String MODID = "objectholdertest"; + public static IForgeRegistry registry; + + @Mod.EventHandler + public void init(FMLInitializationEvent event) { + //verifies @ObjectHolder with custom id + assert VanillaObjectHolder.uiButtonClick != null; + //verifies modded items work + assert ForgeObjectHolder.forgePotion != null; + //verifies minecraft:air is now resolvable + assert VanillaObjectHolder.air != null; + //verifies unexpected name should not have defaulted to AIR. + assert VanillaObjectHolder.nonExistentBlock == null; + //verifies custom registries + assert CustomRegistryObjectHolder.customEntry != null; + //verifies interfaces are supported + assert CustomRegistryObjectHolder.customEntryByInterface != null; + + } + + protected static class PotionForge extends Potion { + protected PotionForge(ResourceLocation location, boolean badEffect, int potionColor) { + super(badEffect, potionColor); + setPotionName("potion." + location.getResourcePath()); + setRegistryName(location); + } + } + + @Mod.EventBusSubscriber + public static class Registration { + @SubscribeEvent + public static void newRegistry(RegistryEvent.NewRegistry event) { + new RegistryBuilder() + .setType(ICustomRegistryEntry.class) + .setName(new ResourceLocation("ObjectHolderTestCustomRegistry")) + .setIDRange(0, 255) + .create(); + } + + @SubscribeEvent + public static void registerPotions(RegistryEvent.Register event) { + event.getRegistry().register( + new ObjectHolderTest.PotionForge(new ResourceLocation(ObjectHolderTest.MODID, "forgePotion"), false, 0xff00ff) // test automatic id distribution + ); + } + + @SubscribeEvent + public static void registerInterfaceRegistryForge(RegistryEvent.Register event) { + event.getRegistry().register( + new CustomRegistryEntry().setRegistryName(new ResourceLocation(MODID, "customEntryByInterface")) + ); + + event.getRegistry().register( + new CustomRegistryEntry().setRegistryName(new ResourceLocation(MODID, "customEntry")) + ); + } + } +} + +class CustomRegistryEntry implements ICustomRegistryEntry +{ + private ResourceLocation name; + + @Override + public ICustomRegistryEntry setRegistryName(ResourceLocation name) + { + this.name = name; + return this; + } + + @Override + public ResourceLocation getRegistryName() + { + return name; + } + + @Override + public Class getRegistryType() + { + return ICustomRegistryEntry.class; + } +} + +interface ICustomRegistryEntry extends IForgeRegistryEntry { + +} + +@GameRegistry.ObjectHolder("minecraft") +class VanillaObjectHolder +{ + //Tests importing vanilla objects that need the @ObjectHolder annotation to get a valid ResourceLocation + @GameRegistry.ObjectHolder("ui.button.click") + public static final SoundEvent uiButtonClick = null; + public static final Block air = null; + public static final Block nonExistentBlock = null; +} + +@GameRegistry.ObjectHolder(ObjectHolderTest.MODID) +class ForgeObjectHolder +{ + //Tests using subclasses for injections + public static final ObjectHolderTest.PotionForge forgePotion = null; +} + +@GameRegistry.ObjectHolder(ObjectHolderTest.MODID) +class CustomRegistryObjectHolder +{ + //Tests whether custom registries can be used + public static final ICustomRegistryEntry customEntry = null; + + //Tests whether interfaces can be used + public static final ICustomRegistryEntry customEntryByInterface = null; +}