Merge branch 'master' into mc179

This commit is contained in:
Christian committed 2014-06-03 13:04:49 -04:00
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)
{
@@ -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)
{
@@ -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");
}
}