Add in an ItemStackHolder - a way to inject ItemStacks without having to have complex lookup code everywhere.
Example: https://gist.github.com/cpw/9af398451a20459ac263
This commit is contained in:
parent
06398fa259
commit
bd6630810f
4 changed files with 240 additions and 0 deletions
|
@ -75,6 +75,7 @@ import cpw.mods.fml.common.functions.ArtifactVersionNameFunction;
|
||||||
import cpw.mods.fml.common.functions.ModIdFunction;
|
import cpw.mods.fml.common.functions.ModIdFunction;
|
||||||
import cpw.mods.fml.common.registry.GameData;
|
import cpw.mods.fml.common.registry.GameData;
|
||||||
import cpw.mods.fml.common.registry.GameRegistry.Type;
|
import cpw.mods.fml.common.registry.GameRegistry.Type;
|
||||||
|
import cpw.mods.fml.common.registry.ItemStackHolderInjector;
|
||||||
import cpw.mods.fml.common.registry.ObjectHolderRegistry;
|
import cpw.mods.fml.common.registry.ObjectHolderRegistry;
|
||||||
import cpw.mods.fml.common.toposort.ModSorter;
|
import cpw.mods.fml.common.toposort.ModSorter;
|
||||||
import cpw.mods.fml.common.toposort.ModSortingException;
|
import cpw.mods.fml.common.toposort.ModSortingException;
|
||||||
|
@ -530,8 +531,10 @@ public class Loader
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ObjectHolderRegistry.INSTANCE.findObjectHolders(discoverer.getASMTable());
|
ObjectHolderRegistry.INSTANCE.findObjectHolders(discoverer.getASMTable());
|
||||||
|
ItemStackHolderInjector.INSTANCE.findHolders(discoverer.getASMTable());
|
||||||
modController.distributeStateMessage(LoaderState.PREINITIALIZATION, discoverer.getASMTable(), canonicalConfigDir);
|
modController.distributeStateMessage(LoaderState.PREINITIALIZATION, discoverer.getASMTable(), canonicalConfigDir);
|
||||||
ObjectHolderRegistry.INSTANCE.applyObjectHolders();
|
ObjectHolderRegistry.INSTANCE.applyObjectHolders();
|
||||||
|
ItemStackHolderInjector.INSTANCE.inject();
|
||||||
modController.transition(LoaderState.INITIALIZATION, false);
|
modController.transition(LoaderState.INITIALIZATION, false);
|
||||||
progressBar.step("Initializing Minecraft Engine");
|
progressBar.step("Initializing Minecraft Engine");
|
||||||
}
|
}
|
||||||
|
@ -714,6 +717,7 @@ public class Loader
|
||||||
progressBar.step("Initializing mods Phase 3");
|
progressBar.step("Initializing mods Phase 3");
|
||||||
modController.transition(LoaderState.POSTINITIALIZATION, false);
|
modController.transition(LoaderState.POSTINITIALIZATION, false);
|
||||||
modController.distributeStateMessage(FMLInterModComms.IMCEvent.class);
|
modController.distributeStateMessage(FMLInterModComms.IMCEvent.class);
|
||||||
|
ItemStackHolderInjector.INSTANCE.inject();
|
||||||
modController.distributeStateMessage(LoaderState.POSTINITIALIZATION);
|
modController.distributeStateMessage(LoaderState.POSTINITIALIZATION);
|
||||||
progressBar.step("Finishing up");
|
progressBar.step("Finishing up");
|
||||||
modController.transition(LoaderState.AVAILABLE, false);
|
modController.transition(LoaderState.AVAILABLE, false);
|
||||||
|
|
|
@ -32,6 +32,10 @@ import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.item.crafting.CraftingManager;
|
import net.minecraft.item.crafting.CraftingManager;
|
||||||
import net.minecraft.item.crafting.FurnaceRecipes;
|
import net.minecraft.item.crafting.FurnaceRecipes;
|
||||||
import net.minecraft.item.crafting.IRecipe;
|
import net.minecraft.item.crafting.IRecipe;
|
||||||
|
import net.minecraft.nbt.JsonToNBT;
|
||||||
|
import net.minecraft.nbt.NBTBase;
|
||||||
|
import net.minecraft.nbt.NBTException;
|
||||||
|
import net.minecraft.nbt.NBTTagCompound;
|
||||||
import net.minecraft.tileentity.TileEntity;
|
import net.minecraft.tileentity.TileEntity;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraft.world.chunk.IChunkProvider;
|
import net.minecraft.world.chunk.IChunkProvider;
|
||||||
|
@ -39,6 +43,8 @@ import net.minecraft.world.chunk.IChunkProvider;
|
||||||
import org.apache.logging.log4j.Level;
|
import org.apache.logging.log4j.Level;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
@ -476,4 +482,70 @@ public class GameRegistry
|
||||||
*/
|
*/
|
||||||
String value();
|
String value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface ItemStackHolder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The registry name of the item being looked up.
|
||||||
|
* @return The registry name
|
||||||
|
*/
|
||||||
|
public String value();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The metadata or damage value for the itemstack, defaults to 0.
|
||||||
|
* @return the metadata value
|
||||||
|
*/
|
||||||
|
public int meta() default 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The string serialized nbt value for the itemstack. Defaults to empty for no nbt.
|
||||||
|
*
|
||||||
|
* @return a nbt string
|
||||||
|
*/
|
||||||
|
public String nbt() default "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes an {@link ItemStack} based on the itemName reference, with supplied meta, stackSize and nbt, if possible
|
||||||
|
*
|
||||||
|
* Will return null if the item doesn't exist (because it's not from a loaded mod for example)
|
||||||
|
* Will throw a {@link RuntimeException} if the nbtString is invalid for use in an {@link ItemStack}
|
||||||
|
*
|
||||||
|
* @param itemName a registry name reference
|
||||||
|
* @param meta the meta
|
||||||
|
* @param stackSize the stack size
|
||||||
|
* @param nbtString an nbt stack as a string, will be processed by {@link JsonToNBT}
|
||||||
|
* @return a new itemstack
|
||||||
|
*/
|
||||||
|
public static ItemStack makeItemStack(String itemName, int meta, int stackSize, String nbtString)
|
||||||
|
{
|
||||||
|
if (itemName == null) throw new IllegalArgumentException("The itemName cannot be null");
|
||||||
|
Item item = GameData.getItemRegistry().getObject(itemName);
|
||||||
|
if (item == null) {
|
||||||
|
FMLLog.getLogger().log(Level.TRACE, "Unable to find item with name {}", itemName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ItemStack is = new ItemStack(item,1,meta);
|
||||||
|
if (!Strings.isNullOrEmpty(nbtString)) {
|
||||||
|
NBTBase nbttag = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nbttag = JsonToNBT.func_150315_a(nbtString);
|
||||||
|
} catch (NBTException e)
|
||||||
|
{
|
||||||
|
FMLLog.getLogger().log(Level.WARN, "Encountered an exception parsing ItemStack NBT string {}", nbtString, e);
|
||||||
|
throw Throwables.propagate(e);
|
||||||
|
}
|
||||||
|
if (!(nbttag instanceof NBTTagCompound)) {
|
||||||
|
FMLLog.getLogger().log(Level.WARN, "Unexpected NBT string - multiple values {}", nbtString);
|
||||||
|
throw new RuntimeException("Invalid NBT JSON");
|
||||||
|
} else {
|
||||||
|
is.setTagCompound((NBTTagCompound) nbttag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
package cpw.mods.fml.common.registry;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.Level;
|
||||||
|
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.FMLLog;
|
||||||
|
import cpw.mods.fml.common.discovery.ASMDataTable;
|
||||||
|
import cpw.mods.fml.common.discovery.ASMDataTable.ASMData;
|
||||||
|
|
||||||
|
public enum ItemStackHolderInjector
|
||||||
|
{
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
private List<ItemStackHolderRef> itemStackHolders = Lists.newArrayList();
|
||||||
|
|
||||||
|
public void inject() {
|
||||||
|
FMLLog.getLogger().log(Level.INFO, "Injecting itemstacks");
|
||||||
|
for (ItemStackHolderRef ishr: itemStackHolders) {
|
||||||
|
ishr.apply();
|
||||||
|
}
|
||||||
|
FMLLog.getLogger().log(Level.INFO, "Itemstack injection complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void findHolders(ASMDataTable table) {
|
||||||
|
FMLLog.info("Identifying ItemStackHolder annotations");
|
||||||
|
Set<ASMData> allItemStackHolders = table.getAll(GameRegistry.ItemStackHolder.class.getName());
|
||||||
|
Map<String, Class<?>> classCache = Maps.newHashMap();
|
||||||
|
for (ASMData data : allItemStackHolders)
|
||||||
|
{
|
||||||
|
String className = data.getClassName();
|
||||||
|
String annotationTarget = data.getObjectName();
|
||||||
|
String value = (String) data.getAnnotationInfo().get("value");
|
||||||
|
int meta = data.getAnnotationInfo().containsKey("meta") ? (Integer) data.getAnnotationInfo().get("meta") : 0;
|
||||||
|
String nbt = data.getAnnotationInfo().containsKey("nbt") ? (String) data.getAnnotationInfo().get("nbt") : "";
|
||||||
|
addHolder(classCache, className, annotationTarget, value, meta, nbt);
|
||||||
|
}
|
||||||
|
FMLLog.info("Found %d ItemStackHolder annotations", allItemStackHolders.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addHolder(Map<String, Class<?>> classCache, String className, String annotationTarget, String value, Integer meta, String nbt)
|
||||||
|
{
|
||||||
|
Class<?> clazz;
|
||||||
|
if (classCache.containsKey(className))
|
||||||
|
{
|
||||||
|
clazz = classCache.get(className);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
clazz = Class.forName(className, true, getClass().getClassLoader());
|
||||||
|
classCache.put(className, clazz);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// unpossible?
|
||||||
|
throw Throwables.propagate(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Field f = clazz.getField(annotationTarget);
|
||||||
|
itemStackHolders.add(new ItemStackHolderRef(f, value, meta, nbt));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// unpossible?
|
||||||
|
throw Throwables.propagate(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package cpw.mods.fml.common.registry;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.Level;
|
||||||
|
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.FMLLog;
|
||||||
|
import cpw.mods.fml.common.registry.GameRegistry.ItemStackHolder;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal class used in tracking {@link ItemStackHolder} references
|
||||||
|
*
|
||||||
|
* @author cpw
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class ItemStackHolderRef {
|
||||||
|
private Field field;
|
||||||
|
private String itemName;
|
||||||
|
private int meta;
|
||||||
|
private String serializednbt;
|
||||||
|
|
||||||
|
|
||||||
|
ItemStackHolderRef(Field field, String itemName, int meta, String serializednbt)
|
||||||
|
{
|
||||||
|
this.field = field;
|
||||||
|
this.itemName = itemName;
|
||||||
|
this.meta = meta;
|
||||||
|
this.serializednbt = serializednbt;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
throw Throwables.propagate(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void apply()
|
||||||
|
{
|
||||||
|
ItemStack is;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
is = GameRegistry.makeItemStack(itemName, meta, 1, serializednbt);
|
||||||
|
} catch (RuntimeException e)
|
||||||
|
{
|
||||||
|
FMLLog.getLogger().log(Level.ERROR, "Caught exception processing itemstack {},{},{} in annotation at {}.{}", itemName, meta, serializednbt,field.getClass().getName(),field.getName());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, field, false);
|
||||||
|
fieldAccessorSet.invoke(fieldAccessor, null, is);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
FMLLog.getLogger().log(Level.WARN, "Unable to set {} with value {},{},{}", this.field, this.itemName, this.meta, this.serializednbt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue