Skip to content

Commit

Permalink
Another pass at substitutions - now capable of fixing up ItemBlock to…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
cpw committed Jul 29, 2016
1 parent 53b43a6 commit 4dcee24
Show file tree
Hide file tree
Showing 12 changed files with 328 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -31,7 +30,6 @@
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;
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -168,7 +169,9 @@ void set(FMLControlledNamespacedRegistry<I> otherRegistry)
{
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;
Expand All @@ -187,7 +190,10 @@ void set(FMLControlledNamespacedRegistry<I> otherRegistry)
{
addObjectRaw(otherRegistry.getId(thing), otherRegistry.getNameForObject(thing), thing);
}
this.activeSubstitutions.putAll(otherRegistry.activeSubstitutions);
for (ResourceLocation resloc : otherRegistry.activeSubstitutions.keySet())
{
activateSubstitution(resloc);
}
}

// public api
Expand Down Expand Up @@ -611,6 +617,7 @@ I activateSubstitution(ResourceLocation nameToReplace)
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;
Expand Down Expand Up @@ -668,9 +675,9 @@ public Iterator<I> iterator()
}


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()
Expand Down Expand Up @@ -756,12 +763,17 @@ else if (currId != newId)
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);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
121 changes: 87 additions & 34 deletions src/main/java/net/minecraftforge/fml/common/registry/GameData.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -32,13 +34,13 @@
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
Expand All @@ -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;
Expand Down Expand Up @@ -193,22 +207,12 @@ void registerSubstitutionAlias(String name, GameRegistry.Type type, Object toRep
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);
}
}

Expand Down Expand Up @@ -277,7 +281,7 @@ public <T extends IForgeRegistryEntry<T>> RegistryDelegate<T> makeDelegate(T obj
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();

Expand All @@ -297,23 +301,64 @@ public void onAdd(Block block, int blockId, Map<ResourceLocation,?> slaves)

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

Expand All @@ -324,24 +369,32 @@ public void onAdd(Item item, int blockId, Map<ResourceLocation, ?> slaves)
{
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();
}

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

Expand All @@ -355,13 +408,13 @@ public void onAdd(Potion potion, int id, Map<ResourceLocation, ?> slaves) {
}

@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?
}
Expand All @@ -371,18 +424,18 @@ private static class BiomeCallbacks implements IForgeRegistry.AddCallback<Biome>
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?
}
Expand Down
Loading

0 comments on commit 4dcee24

Please sign in to comment.