Another pass at substitutions - now capable of fixing up ItemBlock to point at the right block when a block is substituted, should also handle substituting itemblocks. There's some hackery in the way we have to change the value of ItemBlock.block, but it's not too egregious in my opinion.

This commit is contained in:
cpw 2016-07-29 17:08:52 -04:00
parent 53b43a6e1f
commit 4dcee246d5
12 changed files with 328 additions and 87 deletions

View file

@ -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<I extends IForgeRegistryEntry<I>> e
private final CreateCallback<I> createCallback;
FMLControlledNamespacedRegistry(ResourceLocation defaultKey, int minIdValue, int maxIdValue, Class<I> type, AddCallback<I> addCallback, ClearCallback<I> clearCallback, CreateCallback<I> createCallback)
private final SubstitutionCallback<I> substitutionCallback;
FMLControlledNamespacedRegistry(ResourceLocation defaultKey, int minIdValue, int maxIdValue, Class<I> type, BiMap<ResourceLocation, ? extends IForgeRegistry<?>> registries, AddCallback<I> addCallback, ClearCallback<I> clearCallback, CreateCallback<I> createCallback, SubstitutionCallback<I> substitutionCallback)
{
super(defaultKey);
this.superType = type;
@ -102,9 +102,10 @@ public class FMLControlledNamespacedRegistry<I extends IForgeRegistryEntry<I>> 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<I extends IForgeRegistryEntry<I>> e
{
throw new IllegalArgumentException("incompatible registry");
}
final Map<ResourceLocation, Object> 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<I extends IForgeRegistryEntry<I>> 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<I extends IForgeRegistryEntry<I>> 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<I extends IForgeRegistryEntry<I>> e
}
FMLControlledNamespacedRegistry<I> makeShallowCopy()
FMLControlledNamespacedRegistry<I> makeShallowCopy(BiMap<ResourceLocation, ? extends IForgeRegistry<?>> registries)
{
return new FMLControlledNamespacedRegistry<I>(optionalDefaultKey, minId, maxId, superType, addCallback, clearCallback, createCallback);
return new FMLControlledNamespacedRegistry<I>(optionalDefaultKey, minId, maxId, superType, registries, addCallback, clearCallback, createCallback, substitutionCallback);
}
void resetSubstitutionDelegates()
@ -756,12 +763,17 @@ public class FMLControlledNamespacedRegistry<I extends IForgeRegistryEntry<I>> 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);
}
}
}

View file

@ -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);
}
}

View file

@ -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<Block> 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<Block>,IForgeRegistry.ClearCallback<Block>,IForgeRegistry.CreateCallback<Block>
private static class BlockCallbacks implements IForgeRegistry.AddCallback<Block>,IForgeRegistry.ClearCallback<Block>,IForgeRegistry.CreateCallback<Block>, IForgeRegistry.SubstitutionCallback<Block>
{
static final BlockCallbacks INSTANCE = new BlockCallbacks();
@ -297,23 +301,64 @@ public class GameData
@SuppressWarnings("unchecked")
@Override
public void onClear(Map<ResourceLocation, ?> slaveset)
public void onClear(IForgeRegistry<Block> registry, Map<ResourceLocation, ?> slaveset)
{
ClearableObjectIntIdentityMap<IBlockState> blockstateMap = (ClearableObjectIntIdentityMap<IBlockState>)slaveset.get(BLOCKSTATE_TO_ID);
blockstateMap.clear();
final Map<ResourceLocation, Block> originals = (Map<ResourceLocation, Block>)slaveset.get(PersistentRegistryManager.SUBSTITUTION_ORIGINALS);
final BiMap<Block, Item> blockItemMap = (BiMap<Block, Item>)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<ResourceLocation, ?> slaveset)
public void onCreate(Map<ResourceLocation, ?> slaveset, BiMap<ResourceLocation, ? extends IForgeRegistry<?>> registries)
{
final ClearableObjectIntIdentityMap<Block> idMap = new ClearableObjectIntIdentityMap<Block>();
((Map<ResourceLocation,Object>)slaveset).put(BLOCKSTATE_TO_ID, idMap);
final HashBiMap<Block, Item> map = HashBiMap.create();
((Map<ResourceLocation,Object>)slaveset).put(BLOCK_TO_ITEM, map);
}
@Override
public void onSubstituteActivated(Map<ResourceLocation, ?> slaveset, Block original, Block replacement, ResourceLocation name)
{
final BiMap<Block, Item> blockItemMap = (BiMap<Block, Item>)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<Item>,IForgeRegistry.ClearCallback<Item>,IForgeRegistry.CreateCallback<Item>
private static class ItemCallbacks implements IForgeRegistry.AddCallback<Item>,IForgeRegistry.ClearCallback<Item>,IForgeRegistry.CreateCallback<Item>, IForgeRegistry.SubstitutionCallback<Item>
{
static final ItemCallbacks INSTANCE = new ItemCallbacks();
@ -324,13 +369,14 @@ public class GameData
{
ItemBlock itemBlock = (ItemBlock)item;
@SuppressWarnings("unchecked") BiMap<Block, Item> blockToItem = (BiMap<Block, Item>)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<ResourceLocation, ?> slaveset)
public void onClear(IForgeRegistry<Item> registry, Map<ResourceLocation, ?> slaveset)
{
Map<Block,Item> map = (Map<Block, Item>)slaveset.get(BLOCK_TO_ITEM);
map.clear();
@ -338,10 +384,17 @@ public class GameData
@SuppressWarnings("unchecked")
@Override
public void onCreate(Map<ResourceLocation, ?> slaveset)
public void onCreate(Map<ResourceLocation, ?> slaveset, BiMap<ResourceLocation, ? extends IForgeRegistry<?>> registries)
{
final HashBiMap<Block, Item> map = HashBiMap.create();
((Map<ResourceLocation,Object>)slaveset).put(BLOCK_TO_ITEM, map);
// We share the blockItem map between items and blocks registries
final BiMap blockItemMap = (BiMap<Block, Item>)registries.get(PersistentRegistryManager.BLOCKS).getSlaveMap(BLOCK_TO_ITEM, BiMap.class);
((Map<ResourceLocation,Object>)slaveset).put(BLOCK_TO_ITEM, blockItemMap);
}
@Override
public void onSubstituteActivated(Map<ResourceLocation, ?> slaveset, Item original, Item replacement, ResourceLocation name)
{
final BiMap<Block, Item> blockItemMap = (BiMap<Block, Item>)slaveset.get(BLOCK_TO_ITEM);
}
}
@ -355,13 +408,13 @@ public class GameData
}
@Override
public void onClear(Map<ResourceLocation, ?> slaveset)
public void onClear(IForgeRegistry<Potion> registry, Map<ResourceLocation, ?> slaveset)
{
// no op for the minute?
}
@Override
public void onCreate(Map<ResourceLocation, ?> slaveset)
public void onCreate(Map<ResourceLocation, ?> slaveset, BiMap<ResourceLocation, ? extends IForgeRegistry<?>> 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<ResourceLocation, ?> slaves) {
public void onAdd(Biome biome, int id, Map<ResourceLocation, ?> slaves) {
// no op for the minute?
}
@Override
public void onClear(Map<ResourceLocation, ?> slaveset)
public void onClear(IForgeRegistry<Biome> registry, Map<ResourceLocation, ?> slaveset)
{
// no op for the minute?
}
@Override
public void onCreate(Map<ResourceLocation, ?> slaveset)
public void onCreate(Map<ResourceLocation, ?> slaveset, BiMap<ResourceLocation, ? extends IForgeRegistry<?>> registries)
{
// no op for the minute?
}

View file

@ -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<V extends IForgeRegistryEntry<V>> 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<V>
interface AddCallback<V extends IForgeRegistryEntry<V>>
{
void onAdd(V obj, int id, Map<ResourceLocation, ?> slaveset);
}
@ -71,16 +72,21 @@ public interface IForgeRegistry<V extends IForgeRegistryEntry<V>> extends Iterab
* Callback fired when the registry is cleared. This is done before a registry is reloaded from client
* or server.
*/
interface ClearCallback<V>
interface ClearCallback<V extends IForgeRegistryEntry<V>>
{
void onClear(Map<ResourceLocation, ?> slaveset);
void onClear(IForgeRegistry<V> is, Map<ResourceLocation, ?> slaveset);
}
/**
* Callback fired when a registry instance is created. Populate slave maps here.
*/
interface CreateCallback<V>
interface CreateCallback<V extends IForgeRegistryEntry<V>>
{
void onCreate(Map<ResourceLocation, ?> slaveset);
void onCreate(Map<ResourceLocation, ?> slaveset, BiMap<ResourceLocation, ? extends IForgeRegistry<?>> registries);
}
interface SubstitutionCallback<V extends IForgeRegistryEntry<V>>
{
void onSubstituteActivated(Map<ResourceLocation, ?> slaveset, V original, V replacement, ResourceLocation name);
}
}

View file

@ -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)
{

View file

@ -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 <T extends IForgeRegistryEntry<T>> FMLControlledNamespacedRegistry<T> createRegistry(ResourceLocation registryName, Class<T> type, ResourceLocation defaultObjectKey, int minId, int maxId, IForgeRegistry.AddCallback<T> addCallback, IForgeRegistry.ClearCallback<T> clearCallback, IForgeRegistry.CreateCallback<T> createCallback)
private <T extends IForgeRegistryEntry<T>> FMLControlledNamespacedRegistry<T> createRegistry(ResourceLocation registryName, Class<T> type, ResourceLocation defaultObjectKey, int minId, int maxId, IForgeRegistry.AddCallback<T> addCallback, IForgeRegistry.ClearCallback<T> clearCallback, IForgeRegistry.CreateCallback<T> createCallback, IForgeRegistry.SubstitutionCallback<T> substitutionCallback)
{
Set<Class<?>> 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<T> fmlControlledNamespacedRegistry = new FMLControlledNamespacedRegistry<T>(defaultObjectKey, minId, maxId, type, addCallback, clearCallback, createCallback);
FMLControlledNamespacedRegistry<T> fmlControlledNamespacedRegistry = new FMLControlledNamespacedRegistry<T>(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 <T extends IForgeRegistryEntry<T>> FMLControlledNamespacedRegistry<T> createRegistry(ResourceLocation registryName, Class<T> registryType, ResourceLocation optionalDefaultKey, int minId, int maxId, boolean hasDelegates, IForgeRegistry.AddCallback<T> addCallback, IForgeRegistry.ClearCallback<T> clearCallback, IForgeRegistry.CreateCallback<T> createCallback)
public static <T extends IForgeRegistryEntry<T>> FMLControlledNamespacedRegistry<T> createRegistry(ResourceLocation registryName, Class<T> registryType, ResourceLocation optionalDefaultKey, int minId, int maxId, boolean hasDelegates, IForgeRegistry.AddCallback<T> addCallback, IForgeRegistry.ClearCallback<T> clearCallback, IForgeRegistry.CreateCallback<T> createCallback, IForgeRegistry.SubstitutionCallback<T> 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" })

View file

@ -132,7 +132,7 @@ public class VillagerRegistry
}
private boolean hasInit = false;
private FMLControlledNamespacedRegistry<VillagerProfession> professions = PersistentRegistryManager.createRegistry(PROFESSIONS, VillagerProfession.class, null, 0, 1024, true, null, null, null);
private FMLControlledNamespacedRegistry<VillagerProfession> professions = PersistentRegistryManager.createRegistry(PROFESSIONS, VillagerProfession.class, null, 0, 1024, true, null, null, null, null);
public IForgeRegistry<VillagerProfession> getRegistry() { return this.professions; }

View file

@ -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

View file

@ -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<ItemStack, String>()
{
@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<Item> itemRegistry = (FMLControlledNamespacedRegistry<Item>)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);
}
}

View file

@ -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
{
}

View file

@ -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<Block> blockRegistry = (FMLControlledNamespacedRegistry<Block>)PersistentRegistryManager.findRegistryByType(Block.class);
final FMLControlledNamespacedRegistry<Item> itemRegistry = (FMLControlledNamespacedRegistry<Item>)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<Block> blockRegistry = (FMLControlledNamespacedRegistry<Block>)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);
}
}

View file

@ -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<Block> blockRegistry = (FMLControlledNamespacedRegistry<Block>)PersistentRegistryManager.findRegistryByType(Block.class);
final FMLControlledNamespacedRegistry<Item> itemRegistry = (FMLControlledNamespacedRegistry<Item>)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);
}
}