mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-13 00:37:52 +00:00
Merge branch 'master' into mc179
This commit is contained in:
commit
c27f4b34f8
12 files changed
+381
-44
No files matched your search
@@ -716,7 +716,7 @@ public class FMLClientHandler implements IFMLSidedHandler
|
||||
}
|
||||
|
||||
Map<String,String> modListMap = modListBldr.build();
|
||||
serverDataTag.put(data, new ExtendedServerListData(type, FMLNetworkHandler.checkModList(modListMap, Side.CLIENT) == null, modListMap, !moddedClientAllowed));
|
||||
serverDataTag.put(data, new ExtendedServerListData(type, FMLNetworkHandler.checkModList(modListMap, Side.SERVER) == null, modListMap, !moddedClientAllowed));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -62,6 +62,7 @@ import cpw.mods.fml.common.functions.ArtifactVersionNameFunction;
|
||||
import cpw.mods.fml.common.functions.ModIdFunction;
|
||||
import cpw.mods.fml.common.registry.GameData;
|
||||
import cpw.mods.fml.common.registry.GameRegistry.Type;
|
||||
import cpw.mods.fml.common.registry.ObjectHolderRegistry;
|
||||
import cpw.mods.fml.common.toposort.ModSorter;
|
||||
import cpw.mods.fml.common.toposort.ModSortingException;
|
||||
import cpw.mods.fml.common.toposort.ModSortingException.SortingExceptionData;
|
||||
@@ -502,12 +503,14 @@ public class Loader
|
||||
|
||||
public void preinitializeMods()
|
||||
{
|
||||
ObjectHolderRegistry.INSTANCE.findObjectHolders(discoverer.getASMTable());
|
||||
if (!modController.isInState(LoaderState.PREINITIALIZATION))
|
||||
{
|
||||
FMLLog.warning("There were errors previously. Not beginning mod initialization phase");
|
||||
return;
|
||||
}
|
||||
modController.distributeStateMessage(LoaderState.PREINITIALIZATION, discoverer.getASMTable(), canonicalConfigDir);
|
||||
ObjectHolderRegistry.INSTANCE.applyObjectHolders();
|
||||
modController.transition(LoaderState.INITIALIZATION, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ import static org.objectweb.asm.Opcodes.ACC_FINAL;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
|
||||
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
@@ -27,6 +29,7 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
@@ -36,8 +39,10 @@ import net.minecraft.launchwrapper.IClassTransformer;
|
||||
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.FieldNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
@@ -206,11 +211,28 @@ public class AccessTransformer implements IClassTransformer
|
||||
}
|
||||
else
|
||||
{
|
||||
List<MethodNode> nowOverridable = Lists.newArrayList();
|
||||
for (MethodNode n : classNode.methods)
|
||||
{
|
||||
if ((n.name.equals(m.name) && n.desc.equals(m.desc)) || m.name.equals("*"))
|
||||
{
|
||||
n.access = getFixedAccess(n.access, m);
|
||||
|
||||
// constructors always use INVOKESPECIAL
|
||||
if (!n.name.equals("<init>"))
|
||||
{
|
||||
// if we changed from private to something else we need to replace all INVOKESPECIAL calls to this method with INVOKEVIRTUAL
|
||||
// so that overridden methods will be called. Only need to scan this class, because obviously the method was private.
|
||||
boolean wasPrivate = (m.oldAccess & ACC_PRIVATE) == ACC_PRIVATE;
|
||||
boolean isNowPrivate = (m.newAccess & ACC_PRIVATE) == ACC_PRIVATE;
|
||||
|
||||
if (wasPrivate && !isNowPrivate)
|
||||
{
|
||||
nowOverridable.add(n);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println(String.format("Method: %s.%s%s %s -> %s", name, n.name, n.desc, toBinary(m.oldAccess), toBinary(m.newAccess)));
|
||||
@@ -222,6 +244,8 @@ public class AccessTransformer implements IClassTransformer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
replaceInvokeSpecial(classNode, nowOverridable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,6 +253,29 @@ public class AccessTransformer implements IClassTransformer
|
||||
classNode.accept(writer);
|
||||
return writer.toByteArray();
|
||||
}
|
||||
|
||||
private void replaceInvokeSpecial(ClassNode clazz, List<MethodNode> toReplace)
|
||||
{
|
||||
for (MethodNode method : clazz.methods)
|
||||
{
|
||||
for (Iterator<AbstractInsnNode> it = method.instructions.iterator(); it.hasNext();)
|
||||
{
|
||||
AbstractInsnNode insn = it.next();
|
||||
if (insn.getOpcode() == INVOKESPECIAL)
|
||||
{
|
||||
MethodInsnNode mInsn = (MethodInsnNode) insn;
|
||||
for (MethodNode n : toReplace)
|
||||
{
|
||||
if (n.name.equals(mInsn.name) && n.desc.equals(mInsn.desc))
|
||||
{
|
||||
mInsn.setOpcode(INVOKEVIRTUAL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String toBinary(int num)
|
||||
{
|
||||
|
||||
+13
-37
@@ -53,7 +53,6 @@ public class FMLDeobfuscatingRemapper extends Remapper {
|
||||
public static final FMLDeobfuscatingRemapper INSTANCE = new FMLDeobfuscatingRemapper();
|
||||
|
||||
private BiMap<String, String> classNameBiMap;
|
||||
private BiMap<String, String> mcpNameBiMap;
|
||||
|
||||
private Map<String,Map<String,String>> rawFieldMaps;
|
||||
private Map<String,Map<String,String>> rawMethodMaps;
|
||||
@@ -71,7 +70,6 @@ public class FMLDeobfuscatingRemapper extends Remapper {
|
||||
private FMLDeobfuscatingRemapper()
|
||||
{
|
||||
classNameBiMap=ImmutableBiMap.of();
|
||||
mcpNameBiMap=ImmutableBiMap.of();
|
||||
}
|
||||
|
||||
public void setupLoadOnly(String deobfFileName, boolean loadAll)
|
||||
@@ -85,7 +83,6 @@ public class FMLDeobfuscatingRemapper extends Remapper {
|
||||
rawMethodMaps = Maps.newHashMap();
|
||||
rawFieldMaps = Maps.newHashMap();
|
||||
Builder<String, String> builder = ImmutableBiMap.<String,String>builder();
|
||||
Builder<String, String> mcpBuilder = ImmutableBiMap.<String,String>builder();
|
||||
Splitter splitter = Splitter.on(CharMatcher.anyOf(": ")).omitEmptyStrings().trimResults();
|
||||
for (String line : srgList)
|
||||
{
|
||||
@@ -94,7 +91,6 @@ public class FMLDeobfuscatingRemapper extends Remapper {
|
||||
if ("CL".equals(typ))
|
||||
{
|
||||
parseClass(builder, parts);
|
||||
parseMCPClass(mcpBuilder,parts);
|
||||
}
|
||||
else if ("MD".equals(typ) && loadAll)
|
||||
{
|
||||
@@ -106,7 +102,6 @@ public class FMLDeobfuscatingRemapper extends Remapper {
|
||||
}
|
||||
}
|
||||
classNameBiMap = builder.build();
|
||||
mcpNameBiMap = mcpBuilder.build();
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
@@ -128,7 +123,6 @@ public class FMLDeobfuscatingRemapper extends Remapper {
|
||||
rawMethodMaps = Maps.newHashMap();
|
||||
rawFieldMaps = Maps.newHashMap();
|
||||
Builder<String, String> builder = ImmutableBiMap.<String,String>builder();
|
||||
Builder<String, String> mcpBuilder = ImmutableBiMap.<String,String>builder();
|
||||
Splitter splitter = Splitter.on(CharMatcher.anyOf(": ")).omitEmptyStrings().trimResults();
|
||||
for (String line : srgList)
|
||||
{
|
||||
@@ -137,7 +131,6 @@ public class FMLDeobfuscatingRemapper extends Remapper {
|
||||
if ("CL".equals(typ))
|
||||
{
|
||||
parseClass(builder, parts);
|
||||
parseMCPClass(mcpBuilder,parts);
|
||||
}
|
||||
else if ("MD".equals(typ))
|
||||
{
|
||||
@@ -149,13 +142,6 @@ public class FMLDeobfuscatingRemapper extends Remapper {
|
||||
}
|
||||
}
|
||||
classNameBiMap = builder.build();
|
||||
// Special case some mappings for modloader mods
|
||||
mcpBuilder.put("BaseMod","net/minecraft/src/BaseMod");
|
||||
mcpBuilder.put("ModLoader","net/minecraft/src/ModLoader");
|
||||
mcpBuilder.put("EntityRendererProxy","net/minecraft/src/EntityRendererProxy");
|
||||
mcpBuilder.put("MLProp","net/minecraft/src/MLProp");
|
||||
mcpBuilder.put("TradeEntry","net/minecraft/src/TradeEntry");
|
||||
mcpNameBiMap = mcpBuilder.build();
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
@@ -167,8 +153,7 @@ public class FMLDeobfuscatingRemapper extends Remapper {
|
||||
|
||||
public boolean isRemappedClass(String className)
|
||||
{
|
||||
className = className.replace('.', '/');
|
||||
return classNameBiMap.containsKey(className) || mcpNameBiMap.containsKey(className) || (!classNameBiMap.isEmpty() && className.indexOf('/') == -1);
|
||||
return !map(className).equals(className);
|
||||
}
|
||||
|
||||
private void parseField(String[] parts)
|
||||
@@ -235,12 +220,6 @@ public class FMLDeobfuscatingRemapper extends Remapper {
|
||||
builder.put(parts[1],parts[2]);
|
||||
}
|
||||
|
||||
private void parseMCPClass(Builder<String, String> builder, String[] parts)
|
||||
{
|
||||
int clIdx = parts[2].lastIndexOf('/');
|
||||
builder.put("net/minecraft/src/"+parts[2].substring(clIdx+1),parts[2]);
|
||||
}
|
||||
|
||||
private void parseMethod(String[] parts)
|
||||
{
|
||||
String oldSrg = parts[1];
|
||||
@@ -280,13 +259,12 @@ public class FMLDeobfuscatingRemapper extends Remapper {
|
||||
{
|
||||
return classNameBiMap.get(typeName);
|
||||
}
|
||||
int dollarIdx = typeName.indexOf('$');
|
||||
String realType = dollarIdx > -1 ? typeName.substring(0, dollarIdx) : typeName;
|
||||
String subType = dollarIdx > -1 ? typeName.substring(dollarIdx+1) : "";
|
||||
|
||||
String result = classNameBiMap.containsKey(realType) ? classNameBiMap.get(realType) : mcpNameBiMap.containsKey(realType) ? mcpNameBiMap.get(realType) : realType;
|
||||
result = dollarIdx > -1 ? result+"$"+subType : result;
|
||||
return result;
|
||||
int dollarIdx = typeName.lastIndexOf('$');
|
||||
if (dollarIdx > -1)
|
||||
{
|
||||
return map(typeName.substring(0, dollarIdx)) + "$" + typeName.substring(dollarIdx + 1);
|
||||
}
|
||||
return typeName;
|
||||
}
|
||||
|
||||
public String unmap(String typeName)
|
||||
@@ -300,14 +278,12 @@ public class FMLDeobfuscatingRemapper extends Remapper {
|
||||
{
|
||||
return classNameBiMap.inverse().get(typeName);
|
||||
}
|
||||
int dollarIdx = typeName.indexOf('$');
|
||||
String realType = dollarIdx > -1 ? typeName.substring(0, dollarIdx) : typeName;
|
||||
String subType = dollarIdx > -1 ? typeName.substring(dollarIdx+1) : "";
|
||||
|
||||
|
||||
String result = classNameBiMap.containsValue(realType) ? classNameBiMap.inverse().get(realType) : mcpNameBiMap.containsValue(realType) ? mcpNameBiMap.inverse().get(realType) : realType;
|
||||
result = dollarIdx > -1 ? result+"$"+subType : result;
|
||||
return result;
|
||||
int dollarIdx = typeName.lastIndexOf('$');
|
||||
if (dollarIdx > -1)
|
||||
{
|
||||
return unmap(typeName.substring(0, dollarIdx)) + "$" + typeName.substring(dollarIdx + 1);
|
||||
}
|
||||
return typeName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -106,8 +106,8 @@ public class FMLOutboundHandler extends ChannelOutboundHandlerAdapter {
|
||||
public List<NetworkDispatcher> selectNetworks(Object args, ChannelHandlerContext context, FMLProxyPacket packet)
|
||||
{
|
||||
EntityPlayerMP player = (EntityPlayerMP) args;
|
||||
NetworkDispatcher dispatcher = player.playerNetServerHandler.netManager.channel().attr(NetworkDispatcher.FML_DISPATCHER).get();
|
||||
return ImmutableList.of(dispatcher);
|
||||
NetworkDispatcher dispatcher = player == null ? null : player.playerNetServerHandler.netManager.channel().attr(NetworkDispatcher.FML_DISPATCHER).get();
|
||||
return dispatcher == null ? ImmutableList.<NetworkDispatcher>of() : ImmutableList.of(dispatcher);
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -223,7 +223,7 @@ public class FMLOutboundHandler extends ChannelOutboundHandlerAdapter {
|
||||
public List<NetworkDispatcher> selectNetworks(Object args, ChannelHandlerContext context, FMLProxyPacket packet)
|
||||
{
|
||||
NetworkManager clientConnection = FMLCommonHandler.instance().getClientToServerNetworkManager();
|
||||
return clientConnection == null ? ImmutableList.<NetworkDispatcher>of() : ImmutableList.of(clientConnection.channel().attr(NetworkDispatcher.FML_DISPATCHER).get());
|
||||
return clientConnection == null || clientConnection.channel().attr(NetworkDispatcher.FML_DISPATCHER).get() == null ? ImmutableList.<NetworkDispatcher>of() : ImmutableList.of(clientConnection.channel().attr(NetworkDispatcher.FML_DISPATCHER).get());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import net.minecraft.network.play.client.C17PacketCustomPayload;
|
||||
import net.minecraft.network.play.server.S3FPacketCustomPayload;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
import cpw.mods.fml.common.network.ByteBufUtils;
|
||||
import cpw.mods.fml.common.network.FMLNetworkException;
|
||||
import cpw.mods.fml.common.network.NetworkRegistry;
|
||||
import cpw.mods.fml.common.network.handshake.NetworkDispatcher;
|
||||
@@ -68,7 +69,11 @@ public class FMLProxyPacket extends Packet {
|
||||
internalChannel.attr(NetworkRegistry.NET_HANDLER).set(this.netHandler);
|
||||
try
|
||||
{
|
||||
internalChannel.writeInbound(this);
|
||||
if (internalChannel.writeInbound(this))
|
||||
{
|
||||
FMLLog.severe("Messages for channel %s for side %s were not processed by the embedded channel %s.\n They have been dropped.\n%s", this.channel, this.target, internalChannel.inboundMessages(), ByteBufUtils.getContentDump(this.payload));
|
||||
}
|
||||
internalChannel.inboundMessages().clear();
|
||||
}
|
||||
catch (FMLNetworkException ne)
|
||||
{
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ public class SimpleChannelHandlerWrapper<REQ extends IMessage, REPLY extends IMe
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, REQ msg) throws Exception
|
||||
{
|
||||
INetHandler iNetHandler = ctx.attr(NetworkRegistry.NET_HANDLER).get();
|
||||
INetHandler iNetHandler = ctx.channel().attr(NetworkRegistry.NET_HANDLER).get();
|
||||
MessageContext context = new MessageContext(iNetHandler, side);
|
||||
REPLY result = messageHandler.onMessage(msg, context);
|
||||
if (result != null)
|
||||
|
||||
@@ -330,7 +330,7 @@ public class FMLControlledNamespacedRegistry<I> extends RegistryNamespaced {
|
||||
if (name.isEmpty()) throw new IllegalArgumentException(String.format("Can't use an empty name for the registry, object %s.", thing));
|
||||
if (name.indexOf(':') == -1) throw new IllegalArgumentException(String.format("Can't add the name (%s) without a prefix, object %s", name, thing));
|
||||
if (thing == null) throw new NullPointerException(String.format("Can't add null-object to the registry, name %s.", name));
|
||||
if (name.equals(optionalDefaultName))
|
||||
if (name.equals(optionalDefaultName) && this.optionalDefaultObject == null)
|
||||
{
|
||||
this.optionalDefaultObject = thing;
|
||||
}
|
||||
@@ -420,4 +420,9 @@ public class FMLControlledNamespacedRegistry<I> extends RegistryNamespaced {
|
||||
underlyingIntegerMap.func_148746_a(thing, id); // obj <-> id
|
||||
super.putObject(name, thing); // name <-> obj
|
||||
}
|
||||
|
||||
public I getDefaultValue()
|
||||
{
|
||||
return optionalDefaultObject;
|
||||
}
|
||||
}
|
||||
@@ -530,6 +530,8 @@ public class GameData {
|
||||
getMain().iBlockRegistry.dump();
|
||||
getMain().iItemRegistry.dump();
|
||||
Loader.instance().fireRemapEvent(remaps);
|
||||
// The id map changed, ensure we apply object holders
|
||||
ObjectHolderRegistry.INSTANCE.applyObjectHolders();
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@@ -664,6 +666,8 @@ public class GameData {
|
||||
|
||||
getMain().set(frozen);
|
||||
}
|
||||
// the id mapping has reverted, ensure we sync up the object holders
|
||||
ObjectHolderRegistry.INSTANCE.applyObjectHolders();
|
||||
}
|
||||
|
||||
protected static boolean isFrozen(FMLControlledNamespacedRegistry<?> registry)
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
package cpw.mods.fml.common.registry;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -424,4 +428,24 @@ public class GameRegistry
|
||||
return GameData.getUniqueName(item);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This will cause runtime injection of public static final fields to occur at various points
|
||||
* where mod blocks and items <em>could</em> be subject to change. This allows for dynamic
|
||||
* substitution to occur.
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.FIELD})
|
||||
public @interface ObjectHolder {
|
||||
/**
|
||||
* If used on a class, this represents a modid only.
|
||||
* If used on a field, it represents a name, which can be abbreviated or complete.
|
||||
* Abbreviated names derive their modid from an enclosing ObjectHolder at the class level.
|
||||
*
|
||||
* @return either a modid or a name based on the rules above
|
||||
*/
|
||||
String value();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package cpw.mods.fml.common.registry;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
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.ObjectHolder;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.Item;
|
||||
|
||||
|
||||
/**
|
||||
* Internal class used in tracking {@link ObjectHolder} references
|
||||
*
|
||||
* @author cpw
|
||||
*
|
||||
*/
|
||||
class ObjectHolderRef {
|
||||
private Field field;
|
||||
private String injectedObject;
|
||||
private boolean isBlock;
|
||||
private boolean isItem;
|
||||
|
||||
|
||||
ObjectHolderRef(Field field, String injectedObject, boolean extractFromExistingValues)
|
||||
{
|
||||
this.field = field;
|
||||
this.isBlock = Block.class.isAssignableFrom(field.getType());
|
||||
this.isItem = Item.class.isAssignableFrom(field.getType());
|
||||
if (extractFromExistingValues)
|
||||
{
|
||||
try
|
||||
{
|
||||
Object existing = field.get(null);
|
||||
// nothing is ever allowed to replace AIR
|
||||
if (existing == null || existing == GameData.getBlockRegistry().getDefaultValue())
|
||||
{
|
||||
this.injectedObject = null;
|
||||
this.field = null;
|
||||
this.isBlock = false;
|
||||
this.isItem = false;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.injectedObject = isBlock ? GameData.getBlockRegistry().getNameForObject(existing) :
|
||||
isItem ? GameData.getItemRegistry().getNameForObject(existing) : null;
|
||||
}
|
||||
} catch (Exception e)
|
||||
{
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.injectedObject = injectedObject;
|
||||
}
|
||||
|
||||
if (this.injectedObject == null || !isValid())
|
||||
{
|
||||
throw new IllegalStateException("The ObjectHolder annotation cannot apply to a field that is not an Item or Block");
|
||||
}
|
||||
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 boolean isValid()
|
||||
{
|
||||
return isBlock || isItem;
|
||||
}
|
||||
public void apply()
|
||||
{
|
||||
Object thing;
|
||||
if (isBlock)
|
||||
{
|
||||
thing = GameData.getBlockRegistry().getObject(injectedObject);
|
||||
if (thing == Blocks.air)
|
||||
{
|
||||
thing = null;
|
||||
}
|
||||
}
|
||||
else if (isItem)
|
||||
{
|
||||
thing = GameData.getItemRegistry().getObject(injectedObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
thing = null;
|
||||
}
|
||||
|
||||
if (thing == null)
|
||||
{
|
||||
FMLLog.warning("Unable to lookup %s for %s. Is there something wrong with the registry?", injectedObject, field);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, field, false);
|
||||
fieldAccessorSet.invoke(fieldAccessor, null, thing);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
FMLLog.log(Level.WARN, e, "Unable to set %s with value %s (%s)", this.field, thing, this.injectedObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package cpw.mods.fml.common.registry;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
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;
|
||||
import cpw.mods.fml.common.registry.GameRegistry.ObjectHolder;
|
||||
|
||||
/**
|
||||
* Internal registry for tracking {@link ObjectHolder} references
|
||||
* @author cpw
|
||||
*
|
||||
*/
|
||||
public enum ObjectHolderRegistry {
|
||||
INSTANCE;
|
||||
private List<ObjectHolderRef> objectHolders = Lists.newArrayList();
|
||||
|
||||
public void findObjectHolders(ASMDataTable table)
|
||||
{
|
||||
FMLLog.info("Processing ObjectHolder annotations");
|
||||
Set<ASMData> allObjectHolders = table.getAll(GameRegistry.ObjectHolder.class.getName());
|
||||
Map<String, String> classModIds = Maps.newHashMap();
|
||||
Map<String, Class<?>> classCache = Maps.newHashMap();
|
||||
for (ASMData data : allObjectHolders)
|
||||
{
|
||||
String className = data.getClassName();
|
||||
String annotationTarget = data.getObjectName();
|
||||
String value = (String) data.getAnnotationInfo().get("value");
|
||||
boolean isClass = className.equals(annotationTarget);
|
||||
if (isClass)
|
||||
{
|
||||
scanTarget(classModIds, classCache, className, annotationTarget, value, isClass, false);
|
||||
}
|
||||
}
|
||||
// double pass - get all the class level annotations first, then the field level annotations
|
||||
for (ASMData data : allObjectHolders)
|
||||
{
|
||||
String className = data.getClassName();
|
||||
String annotationTarget = data.getObjectName();
|
||||
String value = (String) data.getAnnotationInfo().get("value");
|
||||
boolean isClass = className.equals(annotationTarget);
|
||||
if (!isClass)
|
||||
{
|
||||
scanTarget(classModIds, classCache, className, annotationTarget, value, isClass, false);
|
||||
}
|
||||
}
|
||||
scanTarget(classModIds, classCache, "net.minecraft.init.Blocks", null, "minecraft", true, true);
|
||||
scanTarget(classModIds, classCache, "net.minecraft.init.Items", null, "minecraft", true, true);
|
||||
FMLLog.info("Found %d ObjectHolder annotations", objectHolders.size());
|
||||
}
|
||||
|
||||
private void scanTarget(Map<String, String> classModIds, Map<String, Class<?>> classCache, String className, String annotationTarget, String value, boolean isClass, boolean extractFromValue)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (isClass)
|
||||
{
|
||||
scanClassForFields(classModIds, className, value, clazz, extractFromValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value.indexOf(':') == -1)
|
||||
{
|
||||
String prefix = classModIds.get(className);
|
||||
if (prefix == null)
|
||||
{
|
||||
FMLLog.warning("Found an unqualified ObjectHolder annotation (%s) without a modid context at %s.%s, ignoring", value, className, annotationTarget);
|
||||
throw new IllegalStateException("Unqualified reference to ObjectHolder");
|
||||
}
|
||||
value = prefix + ":" + value;
|
||||
}
|
||||
try
|
||||
{
|
||||
Field f = clazz.getField(annotationTarget);
|
||||
addHolderReference(new ObjectHolderRef(f, value, extractFromValue));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// unpossible?
|
||||
throw Throwables.propagate(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void scanClassForFields(Map<String, String> classModIds, String className, String value, Class<?> clazz, boolean extractFromExistingValues)
|
||||
{
|
||||
classModIds.put(className, value);
|
||||
for (Field f : clazz.getFields())
|
||||
{
|
||||
int mods = f.getModifiers();
|
||||
boolean isMatch = Modifier.isPublic(mods) && Modifier.isStatic(mods) && Modifier.isFinal(mods);
|
||||
if (!isMatch || f.isAnnotationPresent(ObjectHolder.class))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
addHolderReference(new ObjectHolderRef(f, value + ":"+ f.getName(), extractFromExistingValues));
|
||||
}
|
||||
}
|
||||
|
||||
private void addHolderReference(ObjectHolderRef ref)
|
||||
{
|
||||
if (ref.isValid())
|
||||
{
|
||||
objectHolders.add(ref);
|
||||
}
|
||||
}
|
||||
|
||||
public void applyObjectHolders()
|
||||
{
|
||||
FMLLog.info("Applying holder lookups");
|
||||
for (ObjectHolderRef ohr : objectHolders)
|
||||
{
|
||||
ohr.apply();
|
||||
}
|
||||
FMLLog.info("Holder lookups applied");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user