diff --git a/src/main/java/net/minecraftforge/fml/common/registry/FMLControlledNamespacedRegistry.java b/src/main/java/net/minecraftforge/fml/common/registry/FMLControlledNamespacedRegistry.java index 5ccad594f..c2884f65e 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/FMLControlledNamespacedRegistry.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/FMLControlledNamespacedRegistry.java @@ -19,7 +19,6 @@ package net.minecraftforge.fml.common.registry; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; @@ -31,7 +30,6 @@ import java.util.Map.Entry; import java.util.Set; import com.google.common.base.Joiner; -import com.google.common.base.Throwables; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.Iterators; @@ -90,7 +88,9 @@ public class FMLControlledNamespacedRegistry> e private final CreateCallback createCallback; - FMLControlledNamespacedRegistry(ResourceLocation defaultKey, int minIdValue, int maxIdValue, Class type, AddCallback addCallback, ClearCallback clearCallback, CreateCallback createCallback) + private final SubstitutionCallback substitutionCallback; + + FMLControlledNamespacedRegistry(ResourceLocation defaultKey, int minIdValue, int maxIdValue, Class type, BiMap> registries, AddCallback addCallback, ClearCallback clearCallback, CreateCallback createCallback, SubstitutionCallback substitutionCallback) { super(defaultKey); this.superType = type; @@ -102,9 +102,10 @@ public class FMLControlledNamespacedRegistry> e this.addCallback = addCallback; this.clearCallback = clearCallback; this.createCallback = createCallback; + this.substitutionCallback = substitutionCallback; if (createCallback != null) { - createCallback.onCreate(slaves); + createCallback.onCreate(slaves, registries); } } @@ -168,7 +169,9 @@ public class FMLControlledNamespacedRegistry> e { throw new IllegalArgumentException("incompatible registry"); } - + final Map slaves = Maps.newHashMap(this.slaves); + slaves.put(PersistentRegistryManager.SUBSTITUTION_ORIGINALS, substitutionOriginals); + if (this.clearCallback!=null) this.clearCallback.onClear(this, slaves); this.optionalDefaultKey = otherRegistry.optionalDefaultKey; this.maxId = otherRegistry.maxId; this.minId = otherRegistry.minId; @@ -187,7 +190,10 @@ public class FMLControlledNamespacedRegistry> e { addObjectRaw(otherRegistry.getId(thing), otherRegistry.getNameForObject(thing), thing); } - this.activeSubstitutions.putAll(otherRegistry.activeSubstitutions); + for (ResourceLocation resloc : otherRegistry.activeSubstitutions.keySet()) + { + activateSubstitution(resloc); + } } // public api @@ -611,6 +617,7 @@ public class FMLControlledNamespacedRegistry> e addObjectRaw(id, nameToReplace, sub); // Track the original in the substitution originals collection substitutionOriginals.put(nameToReplace, original); + if (substitutionCallback!=null) substitutionCallback.onSubstituteActivated(slaves, original, sub, nameToReplace); return original; } return null; @@ -668,9 +675,9 @@ public class FMLControlledNamespacedRegistry> e } - FMLControlledNamespacedRegistry makeShallowCopy() + FMLControlledNamespacedRegistry makeShallowCopy(BiMap> registries) { - return new FMLControlledNamespacedRegistry(optionalDefaultKey, minId, maxId, superType, addCallback, clearCallback, createCallback); + return new FMLControlledNamespacedRegistry(optionalDefaultKey, minId, maxId, superType, registries, addCallback, clearCallback, createCallback, substitutionCallback); } void resetSubstitutionDelegates() @@ -756,12 +763,17 @@ public class FMLControlledNamespacedRegistry> e remappedIds.put(itemName, new Integer[] {currId, newId}); } I obj = currentRegistry.getRaw(itemName); + I sub = obj; // If we have an object in the originals set, we use that for initial adding - substitute activation will readd the substitute if neceessary later if (currentRegistry.substitutionOriginals.containsKey(itemName)) { obj = currentRegistry.substitutionOriginals.get(itemName); } add(newId, itemName, obj); + if (currentRegistry.substitutionOriginals.containsKey(itemName) && substitutionCallback != null) + { + substitutionCallback.onSubstituteActivated(slaves, sub, obj, itemName); + } } } diff --git a/src/main/java/net/minecraftforge/fml/common/registry/FinalFieldHelper.java b/src/main/java/net/minecraftforge/fml/common/registry/FinalFieldHelper.java new file mode 100644 index 000000000..b952ac659 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/common/registry/FinalFieldHelper.java @@ -0,0 +1,58 @@ +/* + * Minecraft Forge + * Copyright (c) 2016. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.fml.common.registry; + +import com.google.common.base.Throwables; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * Helper class for setting final fields. + */ +class FinalFieldHelper +{ + private static Field modifiersField; + private static Object reflectionFactory; + private static Method newFieldAccessor; + private static Method fieldAccessorSet; + static Field makeWritable(Field f) throws Exception + { + if (modifiersField == null) + { + Method getReflectionFactory = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("getReflectionFactory"); + reflectionFactory = getReflectionFactory.invoke(null); + newFieldAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newFieldAccessor", Field.class, boolean.class); + fieldAccessorSet = Class.forName("sun.reflect.FieldAccessor").getDeclaredMethod("set", Object.class, Object.class); + modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + } + modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL); + return f; + } + + + static void setField(Field field, Object instance, Object thing) throws Exception + { + Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, field, false); + fieldAccessorSet.invoke(fieldAccessor, instance, thing); + } +} diff --git a/src/main/java/net/minecraftforge/fml/common/registry/GameData.java b/src/main/java/net/minecraftforge/fml/common/registry/GameData.java index 3ed1790f4..7d36b9fec 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/GameData.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/GameData.java @@ -19,8 +19,10 @@ package net.minecraftforge.fml.common.registry; +import java.lang.reflect.Field; import java.util.Map; +import com.google.common.base.Throwables; import com.google.common.collect.HashBiMap; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; @@ -32,13 +34,13 @@ import net.minecraft.potion.PotionType; import net.minecraft.util.ObjectIntIdentityMap; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundEvent; -import net.minecraft.util.registry.RegistryNamespaced; import net.minecraft.world.biome.Biome; import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.ModContainer; import com.google.common.collect.BiMap; +import net.minecraftforge.fml.relauncher.ReflectionHelper; import org.apache.logging.log4j.Level; public class GameData @@ -63,16 +65,28 @@ public class GameData private static final GameData mainData = new GameData(); + private static Field blockField; + public GameData() { - iBlockRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.BLOCKS, Block.class, new ResourceLocation("minecraft:air"), MIN_BLOCK_ID, MAX_BLOCK_ID, true, BlockCallbacks.INSTANCE, BlockCallbacks.INSTANCE, BlockCallbacks.INSTANCE); - iItemRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.ITEMS, Item.class, null, MIN_ITEM_ID, MAX_ITEM_ID, true, ItemCallbacks.INSTANCE, ItemCallbacks.INSTANCE, ItemCallbacks.INSTANCE); - iPotionRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.POTIONS, Potion.class, null, MIN_POTION_ID, MAX_POTION_ID, false, PotionCallbacks.INSTANCE, PotionCallbacks.INSTANCE, PotionCallbacks.INSTANCE); - iBiomeRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.BIOMES, Biome.class, null, MIN_BIOME_ID, MAX_BIOME_ID, false, BiomeCallbacks.INSTANCE, BiomeCallbacks.INSTANCE, BiomeCallbacks.INSTANCE); - iSoundEventRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.SOUNDEVENTS, SoundEvent.class, null, MIN_SOUND_ID, MAX_SOUND_ID, false, null, null, null); + iBlockRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.BLOCKS, Block.class, new ResourceLocation("minecraft:air"), MIN_BLOCK_ID, MAX_BLOCK_ID, true, BlockCallbacks.INSTANCE, BlockCallbacks.INSTANCE, BlockCallbacks.INSTANCE, BlockCallbacks.INSTANCE); + iItemRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.ITEMS, Item.class, null, MIN_ITEM_ID, MAX_ITEM_ID, true, ItemCallbacks.INSTANCE, ItemCallbacks.INSTANCE, ItemCallbacks.INSTANCE, ItemCallbacks.INSTANCE); + iPotionRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.POTIONS, Potion.class, null, MIN_POTION_ID, MAX_POTION_ID, false, PotionCallbacks.INSTANCE, PotionCallbacks.INSTANCE, PotionCallbacks.INSTANCE, null); + iBiomeRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.BIOMES, Biome.class, null, MIN_BIOME_ID, MAX_BIOME_ID, false, BiomeCallbacks.INSTANCE, BiomeCallbacks.INSTANCE, BiomeCallbacks.INSTANCE, null); + iSoundEventRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.SOUNDEVENTS, SoundEvent.class, null, MIN_SOUND_ID, MAX_SOUND_ID, false, null, null, null, null); ResourceLocation WATER = new ResourceLocation("water"); - iPotionTypeRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.POTIONTYPES, PotionType.class, WATER, MIN_POTIONTYPE_ID, MAX_POTIONTYPE_ID, false, null, null, null); - iEnchantmentRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.ENCHANTMENTS, Enchantment.class, null, MIN_ENCHANTMENT_ID, MAX_ENCHANTMENT_ID, false, null, null, null); + iPotionTypeRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.POTIONTYPES, PotionType.class, WATER, MIN_POTIONTYPE_ID, MAX_POTIONTYPE_ID, false, null, null, null, null); + iEnchantmentRegistry = PersistentRegistryManager.createRegistry(PersistentRegistryManager.ENCHANTMENTS, Enchantment.class, null, MIN_ENCHANTMENT_ID, MAX_ENCHANTMENT_ID, false, null, null, null, null); + + try + { + blockField = FinalFieldHelper.makeWritable(ReflectionHelper.findField(ItemBlock.class, "block", "field_150939" + "_a")); + } + catch (Exception e) + { + FMLLog.log(Level.FATAL, e, "Cannot access the 'block' field from ItemBlock, this is fatal!"); + throw Throwables.propagate(e); + } } // internal registry objects private final FMLControlledNamespacedRegistry iBlockRegistry; @@ -193,22 +207,12 @@ public class GameData if (type == GameRegistry.Type.BLOCK) { iBlockRegistry.addSubstitutionAlias(Loader.instance().activeModContainer().getModId(), nameToSubstitute, (Block)toReplace); - Block orig = iBlockRegistry.activateSubstitution(nameToSubstitute); - if (blockItemMap.containsKey(orig)) - { - Item i = blockItemMap.get(orig); - blockItemMap.forcePut((Block)toReplace,i); - } + iBlockRegistry.activateSubstitution(nameToSubstitute); } else if (type == GameRegistry.Type.ITEM) { iItemRegistry.addSubstitutionAlias(Loader.instance().activeModContainer().getModId(), nameToSubstitute, (Item)toReplace); - Item orig = iItemRegistry.activateSubstitution(nameToSubstitute); - if (blockItemMap.containsValue(orig)) - { - Block b = blockItemMap.inverse().get(orig); - blockItemMap.forcePut(b, (Item)toReplace); - } + iItemRegistry.activateSubstitution(nameToSubstitute); } } @@ -277,7 +281,7 @@ public class GameData return PersistentRegistryManager.makeDelegate(obj, rootClass); } - private static class BlockCallbacks implements IForgeRegistry.AddCallback,IForgeRegistry.ClearCallback,IForgeRegistry.CreateCallback + private static class BlockCallbacks implements IForgeRegistry.AddCallback,IForgeRegistry.ClearCallback,IForgeRegistry.CreateCallback, IForgeRegistry.SubstitutionCallback { static final BlockCallbacks INSTANCE = new BlockCallbacks(); @@ -297,23 +301,64 @@ public class GameData @SuppressWarnings("unchecked") @Override - public void onClear(Map slaveset) + public void onClear(IForgeRegistry registry, Map slaveset) { ClearableObjectIntIdentityMap blockstateMap = (ClearableObjectIntIdentityMap)slaveset.get(BLOCKSTATE_TO_ID); blockstateMap.clear(); + final Map originals = (Map)slaveset.get(PersistentRegistryManager.SUBSTITUTION_ORIGINALS); + final BiMap blockItemMap = (BiMap)slaveset.get(BLOCK_TO_ITEM); + for (Item it : blockItemMap.values()) + { + if (it instanceof ItemBlock) { + ItemBlock itemBlock = (ItemBlock)it; + final ResourceLocation registryKey = registry.getKey(itemBlock.block); + if (!originals.containsKey(registryKey)) continue; + try + { + FinalFieldHelper.setField(blockField, itemBlock, originals.get(registryKey)); + } + catch (Exception e) + { + throw Throwables.propagate(e); + } + } + } } @SuppressWarnings("unchecked") @Override - public void onCreate(Map slaveset) + public void onCreate(Map slaveset, BiMap> registries) { final ClearableObjectIntIdentityMap idMap = new ClearableObjectIntIdentityMap(); ((Map)slaveset).put(BLOCKSTATE_TO_ID, idMap); + final HashBiMap map = HashBiMap.create(); + ((Map)slaveset).put(BLOCK_TO_ITEM, map); + } + @Override + public void onSubstituteActivated(Map slaveset, Block original, Block replacement, ResourceLocation name) + { + final BiMap blockItemMap = (BiMap)slaveset.get(BLOCK_TO_ITEM); + if (blockItemMap.containsKey(original)) + { + Item i = blockItemMap.get(original); + if (i instanceof ItemBlock) + { + try + { + FinalFieldHelper.setField(blockField, i, replacement); + } + catch (Exception e) + { + throw Throwables.propagate(e); + } + } + blockItemMap.forcePut(replacement,i); + } } } - private static class ItemCallbacks implements IForgeRegistry.AddCallback,IForgeRegistry.ClearCallback,IForgeRegistry.CreateCallback + private static class ItemCallbacks implements IForgeRegistry.AddCallback,IForgeRegistry.ClearCallback,IForgeRegistry.CreateCallback, IForgeRegistry.SubstitutionCallback { static final ItemCallbacks INSTANCE = new ItemCallbacks(); @@ -324,13 +369,14 @@ public class GameData { ItemBlock itemBlock = (ItemBlock)item; @SuppressWarnings("unchecked") BiMap blockToItem = (BiMap)slaves.get(BLOCK_TO_ITEM); - blockToItem.forcePut(itemBlock.getBlock().delegate.get(), item); + final Block block = itemBlock.getBlock().delegate.get(); + blockToItem.forcePut(block, item); } } @SuppressWarnings("unchecked") @Override - public void onClear(Map slaveset) + public void onClear(IForgeRegistry registry, Map slaveset) { Map map = (Map)slaveset.get(BLOCK_TO_ITEM); map.clear(); @@ -338,10 +384,17 @@ public class GameData @SuppressWarnings("unchecked") @Override - public void onCreate(Map slaveset) + public void onCreate(Map slaveset, BiMap> registries) { - final HashBiMap map = HashBiMap.create(); - ((Map)slaveset).put(BLOCK_TO_ITEM, map); + // We share the blockItem map between items and blocks registries + final BiMap blockItemMap = (BiMap)registries.get(PersistentRegistryManager.BLOCKS).getSlaveMap(BLOCK_TO_ITEM, BiMap.class); + ((Map)slaveset).put(BLOCK_TO_ITEM, blockItemMap); + } + + @Override + public void onSubstituteActivated(Map slaveset, Item original, Item replacement, ResourceLocation name) + { + final BiMap blockItemMap = (BiMap)slaveset.get(BLOCK_TO_ITEM); } } @@ -355,13 +408,13 @@ public class GameData } @Override - public void onClear(Map slaveset) + public void onClear(IForgeRegistry registry, Map slaveset) { // no op for the minute? } @Override - public void onCreate(Map slaveset) + public void onCreate(Map slaveset, BiMap> registries) { // no op for the minute? } @@ -371,18 +424,18 @@ public class GameData static final BiomeCallbacks INSTANCE = new BiomeCallbacks(); @Override - public void onAdd(Biome potion, int id, Map slaves) { + public void onAdd(Biome biome, int id, Map slaves) { // no op for the minute? } @Override - public void onClear(Map slaveset) + public void onClear(IForgeRegistry registry, Map slaveset) { // no op for the minute? } @Override - public void onCreate(Map slaveset) + public void onCreate(Map slaveset, BiMap> registries) { // no op for the minute? } diff --git a/src/main/java/net/minecraftforge/fml/common/registry/IForgeRegistry.java b/src/main/java/net/minecraftforge/fml/common/registry/IForgeRegistry.java index a39bc7351..54863649b 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/IForgeRegistry.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/IForgeRegistry.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import com.google.common.collect.BiMap; import net.minecraft.util.ResourceLocation; /** @@ -62,7 +63,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. */ - interface AddCallback + interface AddCallback> { void onAdd(V obj, int id, Map slaveset); } @@ -71,16 +72,21 @@ public interface IForgeRegistry> extends Iterab * Callback fired when the registry is cleared. This is done before a registry is reloaded from client * or server. */ - interface ClearCallback + interface ClearCallback> { - void onClear(Map slaveset); + void onClear(IForgeRegistry is, Map slaveset); } /** * Callback fired when a registry instance is created. Populate slave maps here. */ - interface CreateCallback + interface CreateCallback> { - void onCreate(Map slaveset); + void onCreate(Map slaveset, BiMap> registries); + } + + interface SubstitutionCallback> + { + void onSubstituteActivated(Map slaveset, V original, V replacement, ResourceLocation name); } } 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 ed6a88110..a04d9ff98 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/ObjectHolderRef.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/ObjectHolderRef.java @@ -87,28 +87,11 @@ class ObjectHolderRef { { 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())); } - makeWritable(field); - } - - private static Field modifiersField; - private static Object reflectionFactory; - private static Method newFieldAccessor; - private static Method fieldAccessorSet; - private static void makeWritable(Field f) - { try { - if (modifiersField == null) - { - Method getReflectionFactory = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("getReflectionFactory"); - reflectionFactory = getReflectionFactory.invoke(null); - newFieldAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newFieldAccessor", Field.class, boolean.class); - fieldAccessorSet = Class.forName("sun.reflect.FieldAccessor").getDeclaredMethod("set", Object.class, Object.class); - modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - } - modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL); - } catch (Exception e) + FinalFieldHelper.makeWritable(field); + } + catch (Exception e) { throw Throwables.propagate(e); } @@ -145,8 +128,7 @@ class ObjectHolderRef { } try { - Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, field, false); - fieldAccessorSet.invoke(fieldAccessor, null, thing); + FinalFieldHelper.setField(field, null, thing); } catch (Exception e) { 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 e38e6e0df..2ab4869d2 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/PersistentRegistryManager.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/PersistentRegistryManager.java @@ -84,13 +84,13 @@ public class PersistentRegistryManager { if (!registries.containsKey(key)) { - registries.put(key, other.makeShallowCopy()); + registries.put(key, other.makeShallowCopy(registries)); registrySuperTypes.put(regType, key); } return getRegistry(key, regType); } - private > FMLControlledNamespacedRegistry createRegistry(ResourceLocation registryName, Class type, ResourceLocation defaultObjectKey, int minId, int maxId, IForgeRegistry.AddCallback addCallback, IForgeRegistry.ClearCallback clearCallback, IForgeRegistry.CreateCallback createCallback) + private > FMLControlledNamespacedRegistry createRegistry(ResourceLocation registryName, Class type, ResourceLocation defaultObjectKey, int minId, int maxId, IForgeRegistry.AddCallback addCallback, IForgeRegistry.ClearCallback clearCallback, IForgeRegistry.CreateCallback createCallback, IForgeRegistry.SubstitutionCallback substitutionCallback) { Set> parents = Sets.newHashSet(); findSuperTypes(type, parents); @@ -101,7 +101,7 @@ public class PersistentRegistryManager FMLLog.severe("Found existing registry of type %1s named %2s, you cannot create a new registry (%3s) with type %4s, as %4s has a parent of that type", foundType, registrySuperTypes.get(foundType), registryName, type); throw new IllegalArgumentException("Duplicate registry parent type found - you can only have one registry for a particular super type"); } - FMLControlledNamespacedRegistry fmlControlledNamespacedRegistry = new FMLControlledNamespacedRegistry(defaultObjectKey, minId, maxId, type, addCallback, clearCallback, createCallback); + FMLControlledNamespacedRegistry fmlControlledNamespacedRegistry = new FMLControlledNamespacedRegistry(defaultObjectKey, minId, maxId, type, registries, addCallback, clearCallback, createCallback, substitutionCallback); registries.put(registryName, fmlControlledNamespacedRegistry); registrySuperTypes.put(type, registryName); return getRegistry(registryName, type); @@ -152,10 +152,11 @@ public class PersistentRegistryManager public static final ResourceLocation SOUNDEVENTS = new ResourceLocation("minecraft:soundevents"); public static final ResourceLocation POTIONTYPES = new ResourceLocation("minecraft:potiontypes"); public static final ResourceLocation ENCHANTMENTS = new ResourceLocation("minecraft:enchantments"); + static final ResourceLocation SUBSTITUTION_ORIGINALS = new ResourceLocation("fml:suboriginals"); - public static > FMLControlledNamespacedRegistry createRegistry(ResourceLocation registryName, Class registryType, ResourceLocation optionalDefaultKey, int minId, int maxId, boolean hasDelegates, IForgeRegistry.AddCallback addCallback, IForgeRegistry.ClearCallback clearCallback, IForgeRegistry.CreateCallback createCallback) + public static > FMLControlledNamespacedRegistry createRegistry(ResourceLocation registryName, Class registryType, ResourceLocation optionalDefaultKey, int minId, int maxId, boolean hasDelegates, IForgeRegistry.AddCallback addCallback, IForgeRegistry.ClearCallback clearCallback, IForgeRegistry.CreateCallback createCallback, IForgeRegistry.SubstitutionCallback substitutionCallback) { - return PersistentRegistry.ACTIVE.createRegistry(registryName, registryType, optionalDefaultKey, minId, maxId, addCallback, clearCallback, createCallback); + return PersistentRegistry.ACTIVE.createRegistry(registryName, registryType, optionalDefaultKey, minId, maxId, addCallback, clearCallback, createCallback, substitutionCallback); } @SuppressWarnings({ "unchecked", "rawtypes" }) diff --git a/src/main/java/net/minecraftforge/fml/common/registry/VillagerRegistry.java b/src/main/java/net/minecraftforge/fml/common/registry/VillagerRegistry.java index e0efec9a2..874d71121 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/VillagerRegistry.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/VillagerRegistry.java @@ -132,7 +132,7 @@ public class VillagerRegistry } private boolean hasInit = false; - private FMLControlledNamespacedRegistry professions = PersistentRegistryManager.createRegistry(PROFESSIONS, VillagerProfession.class, null, 0, 1024, true, null, null, null); + private FMLControlledNamespacedRegistry professions = PersistentRegistryManager.createRegistry(PROFESSIONS, VillagerProfession.class, null, 0, 1024, true, null, null, null, null); public IForgeRegistry getRegistry() { return this.professions; } diff --git a/src/test/java/net/minecraftforge/fml/common/registry/FreezingTests.java b/src/test/java/net/minecraftforge/fml/common/registry/FreezingTests.java index 6cdd8101c..05b0a4dfd 100644 --- a/src/test/java/net/minecraftforge/fml/common/registry/FreezingTests.java +++ b/src/test/java/net/minecraftforge/fml/common/registry/FreezingTests.java @@ -39,9 +39,9 @@ public class FreezingTests { Loader.instance(); System.setProperty("fml.queryResult", "confirm"); - registry = PersistentRegistryManager.createRegistry(resloc, RTest.class, null, 0, 255, false, null, null, null); - PersistentRegistryManager.createRegistry(PersistentRegistryManager.BLOCKS, Block.class, null, 0, 255, false, null, null, null); - PersistentRegistryManager.createRegistry(PersistentRegistryManager.ITEMS, Item.class, null, 0, 255, false, null, null, null); + registry = PersistentRegistryManager.createRegistry(resloc, RTest.class, null, 0, 255, false, null, null, null, null); + PersistentRegistryManager.createRegistry(PersistentRegistryManager.BLOCKS, Block.class, null, 0, 255, false, null, null, null, null); + PersistentRegistryManager.createRegistry(PersistentRegistryManager.ITEMS, Item.class, null, 0, 255, false, null, null, null, null); r1 = new RTest("test1"); r2 = new RTest("test2"); r3 = new RTest("test3"); @@ -58,9 +58,9 @@ public class FreezingTests ss.entries.put(resloc, new PersistentRegistryManager.GameDataSnapshot.Entry(PersistentRegistryManager.PersistentRegistry.ACTIVE.getRegistry(RTest.class))); PersistentRegistryManager.PersistentRegistry.ACTIVE.clean(); PersistentRegistryManager.PersistentRegistry.FROZEN.clean(); - registry = PersistentRegistryManager.createRegistry(resloc, RTest.class, null, 0, 255, false, null, null, null); - PersistentRegistryManager.createRegistry(PersistentRegistryManager.BLOCKS, Block.class, null, 0, 255, false, null, null, null); - PersistentRegistryManager.createRegistry(PersistentRegistryManager.ITEMS, Item.class, null, 0, 255, false, null, null, null); + registry = PersistentRegistryManager.createRegistry(resloc, RTest.class, null, 0, 255, false, null, null, null, null); + PersistentRegistryManager.createRegistry(PersistentRegistryManager.BLOCKS, Block.class, null, 0, 255, false, null, null, null, null); + PersistentRegistryManager.createRegistry(PersistentRegistryManager.ITEMS, Item.class, null, 0, 255, false, null, null, null, null); } @Test diff --git a/src/test/java/net/minecraftforge/fml/common/registry/ItemBlockSubstitutionRemoveRestoreTest.java b/src/test/java/net/minecraftforge/fml/common/registry/ItemBlockSubstitutionRemoveRestoreTest.java new file mode 100644 index 000000000..ba1da0b1d --- /dev/null +++ b/src/test/java/net/minecraftforge/fml/common/registry/ItemBlockSubstitutionRemoveRestoreTest.java @@ -0,0 +1,88 @@ +package net.minecraftforge.fml.common.registry; + +import com.google.common.base.Function; +import net.minecraft.block.Block; +import net.minecraft.block.BlockDirt; +import net.minecraft.init.Blocks; +import net.minecraft.init.Bootstrap; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemMultiTexture; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.DummyModContainer; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.ModMetadata; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.annotation.Nullable; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +/** + * Substitution test harness - tests that substitutions behave correctly + */ +@RunWith(ForgeTestRunner.class) +public class ItemBlockSubstitutionRemoveRestoreTest +{ + private ResourceLocation myDirt = new ResourceLocation("minecraft:dirt"); + private static class ItemMyDirt extends ItemMultiTexture + { + public ItemMyDirt() { + super(Blocks.DIRT, Blocks.DIRT, new Function() + { + @Nullable + public String apply(@Nullable ItemStack p_apply_1_) + { + return BlockDirt.DirtType.byMetadata(p_apply_1_.getMetadata()).getUnlocalizedName(); + } + }); + } + } + private static ItemMyDirt myDirtInstance; + private static Item originalDirt; + @BeforeClass + public static void setup() + { + Loader.instance(); + Bootstrap.register(); + myDirtInstance = new ItemMyDirt(); + Loader.instance().setupTestHarness(new DummyModContainer(new ModMetadata() {{ + modId = "test"; + }})); + originalDirt = new ItemStack(Blocks.DIRT).getItem(); + } + + @Test + public void testSubstitutionRemovalAndRestore() throws Exception + { + GameRegistry.addSubstitutionAlias("minecraft:dirt", GameRegistry.Type.ITEM, myDirtInstance); + PersistentRegistryManager.freezeData(); + ObjectHolderRegistry.INSTANCE.applyObjectHolders(); + + final FMLControlledNamespacedRegistry itemRegistry = (FMLControlledNamespacedRegistry)PersistentRegistryManager.findRegistryByType(Item.class); + + // TEST 1: Does my substitute take effect? The substitute should be found in the registry + ItemBlock dirtitem = (ItemBlock)itemRegistry.getValue(myDirt); + assertEquals("ItemBlock points at my block", myDirtInstance, dirtitem); + + // TEST 2: Does the substitute get removed when told by remote operation? The substitute should NOT be found in the registry + final PersistentRegistryManager.GameDataSnapshot snapshot = PersistentRegistryManager.takeSnapshot(); + snapshot.entries.get(PersistentRegistryManager.ITEMS).substitutions.clear(); + PersistentRegistryManager.injectSnapshot(snapshot, false, false); + ObjectHolderRegistry.INSTANCE.applyObjectHolders(); + + dirtitem = (ItemBlock)itemRegistry.getValue(myDirt); + assertEquals("ItemBlock points at vanilla block", originalDirt, dirtitem); + assertNotEquals("ItemBlock points at my block", myDirtInstance, dirtitem); + + // TEST 3: Does the substitute get restored when reverting to frozen state? The substitute should be found in the registry again + PersistentRegistryManager.revertToFrozen(); + ObjectHolderRegistry.INSTANCE.applyObjectHolders(); + dirtitem = (ItemBlock)itemRegistry.getValue(myDirt); + assertEquals("ItemBlock points at my block", myDirtInstance, dirtitem); + } +} diff --git a/src/test/java/net/minecraftforge/fml/common/registry/RegistryTestSuite.java b/src/test/java/net/minecraftforge/fml/common/registry/RegistryTestSuite.java index ffc5e3933..c17b467f8 100644 --- a/src/test/java/net/minecraftforge/fml/common/registry/RegistryTestSuite.java +++ b/src/test/java/net/minecraftforge/fml/common/registry/RegistryTestSuite.java @@ -7,7 +7,7 @@ import org.junit.runners.Suite; * Run the full suite of tests */ @RunWith(Suite.class) -@Suite.SuiteClasses({VanillaRegistryTests.class, FreezingTests.class, SubstitutionRemoveRestoreTest.class, SubstitutionInjectionTest.class, DummyBlockReplacementTest.class}) +@Suite.SuiteClasses({VanillaRegistryTests.class, FreezingTests.class, SubstitutionRemoveRestoreTest.class, SubstitutionInjectionTest.class, DummyBlockReplacementTest.class, BlockStateMappingsTest.class}) public class RegistryTestSuite { } diff --git a/src/test/java/net/minecraftforge/fml/common/registry/SubstitutionInjectionTest.java b/src/test/java/net/minecraftforge/fml/common/registry/SubstitutionInjectionTest.java index 55030c20b..84bc10a4a 100644 --- a/src/test/java/net/minecraftforge/fml/common/registry/SubstitutionInjectionTest.java +++ b/src/test/java/net/minecraftforge/fml/common/registry/SubstitutionInjectionTest.java @@ -4,6 +4,8 @@ import net.minecraft.block.Block; import net.minecraft.block.BlockDirt; import net.minecraft.init.Blocks; import net.minecraft.init.Bootstrap; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; import net.minecraft.stats.StatList; import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.common.DummyModContainer; @@ -15,6 +17,7 @@ import org.junit.runner.RunWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.fail; /** * Substitution test harness - tests that substitutions behave correctly @@ -40,24 +43,44 @@ public class SubstitutionInjectionTest @Test public void testSubstitutionInjection() throws Exception { + final FMLControlledNamespacedRegistry blockRegistry = (FMLControlledNamespacedRegistry)PersistentRegistryManager.findRegistryByType(Block.class); + final FMLControlledNamespacedRegistry itemRegistry = (FMLControlledNamespacedRegistry)PersistentRegistryManager.findRegistryByType(Item.class); + // Capture snapshot prior to registering the substitution - this is a world state "pre-substitute" final PersistentRegistryManager.GameDataSnapshot snapshot = PersistentRegistryManager.takeSnapshot(); + Block fnd = blockRegistry.getValue(myDirt); + Block currDirt = Blocks.DIRT; + // TEST 0: Verify that input state is correct + assertEquals("Got vanilla dirt ", currDirt, fnd); + + // TEST 0a: Validate that the ItemBlock for Dirt points at vanilla dirt + ItemBlock dirtitem = (ItemBlock)itemRegistry.getValue(myDirt); + assertEquals("ItemBlock points at my block", currDirt, dirtitem.block); + GameRegistry.addSubstitutionAlias("minecraft:dirt", GameRegistry.Type.BLOCK, toSub); PersistentRegistryManager.freezeData(); ObjectHolderRegistry.INSTANCE.applyObjectHolders(); // This should not throw an exception - StatList.reinit(); - - final FMLControlledNamespacedRegistry blockRegistry = (FMLControlledNamespacedRegistry)PersistentRegistryManager.findRegistryByType(Block.class); + try + { + StatList.reinit(); + } catch (Exception e) + { + fail("Caught exception"); + } // TEST 1: Does my substitute take effect? The substitute should be found in the registry - Block fnd = blockRegistry.getValue(myDirt); - Block currDirt = Blocks.DIRT; + fnd = blockRegistry.getValue(myDirt); + currDirt = Blocks.DIRT; assertEquals("Got my dirt substitute - Blocks", toSub, currDirt); assertEquals("Got my dirt substitute - Blocks and registry", currDirt, fnd); assertEquals("Got my dirt substitute - registry", toSub, fnd); + // TEST 1a: Validate that the ItemBlock for Dirt now points at my dirt + dirtitem = (ItemBlock)itemRegistry.getValue(myDirt); + assertEquals("ItemBlock points at my block", toSub, dirtitem.block); + // TEST 2: Does the substitute get injected when told by loading operation? The substitute should be found in the registry PersistentRegistryManager.injectSnapshot(snapshot, true, true); ObjectHolderRegistry.INSTANCE.applyObjectHolders(); @@ -67,6 +90,9 @@ public class SubstitutionInjectionTest assertEquals("Got my dirt substitute - Blocks and registry", currDirt, fnd); assertEquals("Got my dirt substitute - registry", toSub, fnd); + dirtitem = (ItemBlock)itemRegistry.getValue(myDirt); + assertEquals("ItemBlock points at my block", toSub, dirtitem.block); + // TEST 3: Does the substitute get restored when reverting to frozen state? The substitute should be found in the registry again PersistentRegistryManager.revertToFrozen(); ObjectHolderRegistry.INSTANCE.applyObjectHolders(); @@ -75,5 +101,7 @@ public class SubstitutionInjectionTest assertEquals("Got my dirt substitute - Blocks", toSub, currDirt); assertEquals("Got my dirt substitute - Blocks and registry", currDirt, fnd); assertEquals("Got my dirt substitute - registry", toSub, fnd); + dirtitem = (ItemBlock)itemRegistry.getValue(myDirt); + assertEquals("ItemBlock points at my block", toSub, dirtitem.block); } } diff --git a/src/test/java/net/minecraftforge/fml/common/registry/SubstitutionRemoveRestoreTest.java b/src/test/java/net/minecraftforge/fml/common/registry/SubstitutionRemoveRestoreTest.java index 41c9450b5..97766005c 100644 --- a/src/test/java/net/minecraftforge/fml/common/registry/SubstitutionRemoveRestoreTest.java +++ b/src/test/java/net/minecraftforge/fml/common/registry/SubstitutionRemoveRestoreTest.java @@ -4,6 +4,8 @@ import net.minecraft.block.Block; import net.minecraft.block.BlockDirt; import net.minecraft.init.Blocks; import net.minecraft.init.Bootstrap; +import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.common.DummyModContainer; import net.minecraftforge.fml.common.Loader; @@ -44,6 +46,7 @@ public class SubstitutionRemoveRestoreTest ObjectHolderRegistry.INSTANCE.applyObjectHolders(); final FMLControlledNamespacedRegistry blockRegistry = (FMLControlledNamespacedRegistry)PersistentRegistryManager.findRegistryByType(Block.class); + final FMLControlledNamespacedRegistry itemRegistry = (FMLControlledNamespacedRegistry)PersistentRegistryManager.findRegistryByType(Item.class); // TEST 1: Does my substitute take effect? The substitute should be found in the registry Block fnd = blockRegistry.getValue(myDirt); @@ -52,6 +55,9 @@ public class SubstitutionRemoveRestoreTest assertEquals("Got my dirt substitute - Blocks and registry", currDirt, fnd); assertEquals("Got my dirt substitute - registry", toSub, fnd); + ItemBlock dirtitem = (ItemBlock)itemRegistry.getValue(myDirt); + assertEquals("ItemBlock points at my block", toSub, dirtitem.block); + // TEST 2: Does the substitute get removed when told by remote operation? The substitute should NOT be found in the registry final PersistentRegistryManager.GameDataSnapshot snapshot = PersistentRegistryManager.takeSnapshot(); snapshot.entries.get(PersistentRegistryManager.BLOCKS).substitutions.clear(); @@ -63,6 +69,10 @@ public class SubstitutionRemoveRestoreTest assertEquals("Got my dirt substitute - Blocks and registry", currDirt, fnd); assertNotEquals("Got my dirt substitute - registry", toSub, fnd); + dirtitem = (ItemBlock)itemRegistry.getValue(myDirt); + assertEquals("ItemBlock points at vanilla block", currDirt, dirtitem.block); + assertNotEquals("ItemBlock points at my block", toSub, dirtitem.block); + // TEST 3: Does the substitute get restored when reverting to frozen state? The substitute should be found in the registry again PersistentRegistryManager.revertToFrozen(); ObjectHolderRegistry.INSTANCE.applyObjectHolders(); @@ -71,5 +81,8 @@ public class SubstitutionRemoveRestoreTest assertEquals("Got my dirt substitute - Blocks", toSub, currDirt); assertEquals("Got my dirt substitute - Blocks and registry", currDirt, fnd); assertEquals("Got my dirt substitute - registry", toSub, fnd); + + dirtitem = (ItemBlock)itemRegistry.getValue(myDirt); + assertEquals("ItemBlock points at my block", toSub, dirtitem.block); } }