Merge pull request #597 from RainWarrior/blame
Added blaming of coremods and mods not targetting Java 6
This commit is contained in:
commit
c85aa9351a
|
@ -8,3 +8,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public String func_71501_a()
|
public String func_71501_a()
|
||||||
|
@@ -220,6 +221,8 @@
|
||||||
|
{
|
||||||
|
StringBuilder stringbuilder = new StringBuilder();
|
||||||
|
stringbuilder.append("---- Minecraft Crash Report ----\n");
|
||||||
|
+ net.minecraftforge.fml.common.asm.transformers.BlamingTransformer.onCrash(stringbuilder);
|
||||||
|
+ net.minecraftforge.fml.relauncher.CoreModManager.onCrash(stringbuilder);
|
||||||
|
stringbuilder.append("// ");
|
||||||
|
stringbuilder.append(func_71503_h());
|
||||||
|
stringbuilder.append("\n\n");
|
||||||
|
|
|
@ -55,6 +55,7 @@ import net.minecraftforge.fml.common.gameevent.PlayerEvent;
|
||||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||||
import net.minecraftforge.fml.common.gameevent.TickEvent.Phase;
|
import net.minecraftforge.fml.common.gameevent.TickEvent.Phase;
|
||||||
import net.minecraftforge.fml.common.network.NetworkRegistry;
|
import net.minecraftforge.fml.common.network.NetworkRegistry;
|
||||||
|
import net.minecraftforge.fml.relauncher.CoreModManager;
|
||||||
import net.minecraftforge.fml.relauncher.Side;
|
import net.minecraftforge.fml.relauncher.Side;
|
||||||
import net.minecraftforge.fml.server.FMLServerHandler;
|
import net.minecraftforge.fml.server.FMLServerHandler;
|
||||||
|
|
||||||
|
@ -107,6 +108,28 @@ public class FMLCommonHandler
|
||||||
private WeakReference<SaveHandler> handlerToCheck;
|
private WeakReference<SaveHandler> handlerToCheck;
|
||||||
private EventBus eventBus = new EventBus();
|
private EventBus eventBus = new EventBus();
|
||||||
private volatile CountDownLatch exitLatch = null;
|
private volatile CountDownLatch exitLatch = null;
|
||||||
|
|
||||||
|
private FMLCommonHandler()
|
||||||
|
{
|
||||||
|
registerCrashCallable(new ICrashCallable()
|
||||||
|
{
|
||||||
|
public String call() throws Exception
|
||||||
|
{
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
Joiner joiner = Joiner.on("\n ");
|
||||||
|
for(String coreMod : CoreModManager.getTransformers().keySet())
|
||||||
|
{
|
||||||
|
builder.append("\n" + coreMod + "\n ").append(joiner.join(CoreModManager.getTransformers().get(coreMod)));
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLabel()
|
||||||
|
{
|
||||||
|
return "Loaded coremods (and transformers)";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* The FML event bus. Subscribe here for FML related events
|
* The FML event bus. Subscribe here for FML related events
|
||||||
*
|
*
|
||||||
|
|
|
@ -27,6 +27,7 @@ import java.util.Set;
|
||||||
|
|
||||||
import net.minecraftforge.fml.common.Mod.Instance;
|
import net.minecraftforge.fml.common.Mod.Instance;
|
||||||
import net.minecraftforge.fml.common.Mod.Metadata;
|
import net.minecraftforge.fml.common.Mod.Metadata;
|
||||||
|
import net.minecraftforge.fml.common.asm.transformers.BlamingTransformer;
|
||||||
import net.minecraftforge.fml.common.discovery.ASMDataTable;
|
import net.minecraftforge.fml.common.discovery.ASMDataTable;
|
||||||
import net.minecraftforge.fml.common.discovery.ModCandidate;
|
import net.minecraftforge.fml.common.discovery.ModCandidate;
|
||||||
import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData;
|
import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData;
|
||||||
|
@ -416,6 +417,7 @@ public class FMLModContainer implements ModContainer
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
BlamingTransformer.addClasses(getModId(), candidate.getClassList());
|
||||||
ModClassLoader modClassLoader = event.getModClassLoader();
|
ModClassLoader modClassLoader = event.getModClassLoader();
|
||||||
modClassLoader.addFile(source);
|
modClassLoader.addFile(source);
|
||||||
modClassLoader.clearNegativeCacheFor(candidate.getClassList());
|
modClassLoader.clearNegativeCacheFor(candidate.getClassList());
|
||||||
|
|
|
@ -14,19 +14,21 @@ package net.minecraftforge.fml.common;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.logging.log4j.Level;
|
|
||||||
|
|
||||||
import net.minecraft.launchwrapper.IClassTransformer;
|
import net.minecraft.launchwrapper.IClassTransformer;
|
||||||
import net.minecraft.launchwrapper.LaunchClassLoader;
|
import net.minecraft.launchwrapper.LaunchClassLoader;
|
||||||
import net.minecraftforge.fml.common.asm.transformers.ModAPITransformer;
|
import net.minecraftforge.fml.common.asm.transformers.ModAPITransformer;
|
||||||
import net.minecraftforge.fml.common.discovery.ASMDataTable;
|
import net.minecraftforge.fml.common.discovery.ASMDataTable;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.Level;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,15 +61,18 @@ public class ModClassLoader extends URLClassLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
public File[] getParentSources() {
|
public File[] getParentSources() {
|
||||||
List<URL> urls=mainClassLoader.getSources();
|
|
||||||
File[] sources=new File[urls.size()];
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
for (int i = 0; i<urls.size(); i++)
|
List<File> files=new ArrayList<File>();
|
||||||
|
for(URL url : mainClassLoader.getSources())
|
||||||
{
|
{
|
||||||
sources[i]=new File(urls.get(i).toURI());
|
URI uri = url.toURI();
|
||||||
|
if(uri.getScheme() == "file")
|
||||||
|
{
|
||||||
|
files.add(new File(uri));
|
||||||
}
|
}
|
||||||
return sources;
|
}
|
||||||
|
return files.toArray(new File[]{});
|
||||||
}
|
}
|
||||||
catch (URISyntaxException e)
|
catch (URISyntaxException e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,263 @@
|
||||||
|
|
||||||
|
|
||||||
|
package net.minecraftforge.fml.common.asm;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.net.URLStreamHandler;
|
||||||
|
import java.security.Permission;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
import net.minecraft.launchwrapper.IClassTransformer;
|
||||||
|
import net.minecraft.launchwrapper.LaunchClassLoader;
|
||||||
|
|
||||||
|
import org.objectweb.asm.ClassVisitor;
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.FieldVisitor;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.Type;
|
||||||
|
import org.objectweb.asm.commons.GeneratorAdapter;
|
||||||
|
import org.objectweb.asm.commons.Method;
|
||||||
|
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
public class ASMTransformerWrapper
|
||||||
|
{
|
||||||
|
private static final Map<String, String> wrapperModMap = Maps.newHashMap();
|
||||||
|
private static final Map<String, String> wrapperParentMap = Maps.newHashMap();
|
||||||
|
|
||||||
|
private static final LoadingCache<String, byte[]> wrapperCache = CacheBuilder.newBuilder()
|
||||||
|
.maximumSize(30)
|
||||||
|
.weakValues()
|
||||||
|
.build(new CacheLoader<String, byte[]>()
|
||||||
|
{
|
||||||
|
public byte[] load(String file) throws Exception
|
||||||
|
{
|
||||||
|
return makeWrapper(file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
private static final URL asmGenRoot;
|
||||||
|
private static boolean injected = false;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
asmGenRoot = new URL("asmgen", null, -1, "/", new ASMGenHandler());
|
||||||
|
}
|
||||||
|
catch(MalformedURLException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ASMGenHandler extends URLStreamHandler
|
||||||
|
{
|
||||||
|
protected URLConnection openConnection(URL url) throws IOException
|
||||||
|
{
|
||||||
|
String file = url.getFile();
|
||||||
|
if(file.equals("/"))
|
||||||
|
{
|
||||||
|
return new URLConnection(url)
|
||||||
|
{
|
||||||
|
public void connect() throws IOException
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if(!file.startsWith("/")) throw new RuntimeException("Malformed URL: " + url);
|
||||||
|
file = file.substring(1);
|
||||||
|
if(wrapperModMap.containsKey(file))
|
||||||
|
{
|
||||||
|
return new ASMGenConnection(url, file);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ASMGenConnection extends URLConnection
|
||||||
|
{
|
||||||
|
private final String file;
|
||||||
|
|
||||||
|
protected ASMGenConnection(URL url, String file)
|
||||||
|
{
|
||||||
|
super(url);
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connect() throws IOException
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return new ByteArrayInputStream(wrapperCache.get(file));
|
||||||
|
}
|
||||||
|
catch (ExecutionException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Permission getPermission()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getTransformerWrapper(LaunchClassLoader launchLoader, String parentClass, String coreMod)
|
||||||
|
{
|
||||||
|
if(!injected)
|
||||||
|
{
|
||||||
|
injected = true;
|
||||||
|
launchLoader.addURL(asmGenRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = getWrapperName(parentClass);
|
||||||
|
String fileName = name.replace('.', '/') + ".class";
|
||||||
|
wrapperModMap.put(fileName, coreMod);
|
||||||
|
wrapperParentMap.put(fileName, parentClass);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] makeWrapper(String fileName)
|
||||||
|
{
|
||||||
|
if(!wrapperModMap.containsKey(fileName) || !wrapperParentMap.containsKey(fileName) || !fileName.endsWith(".class"))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("makeWrapper called with strange argument: " + fileName);
|
||||||
|
}
|
||||||
|
String name = fileName.substring(0, fileName.length() - ".class".length());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Type wrapper = Type.getType(TransformerWrapper.class);
|
||||||
|
|
||||||
|
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
|
||||||
|
|
||||||
|
writer.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, name, null, wrapper.getInternalName(), null);
|
||||||
|
|
||||||
|
Method m = Method.getMethod("void <init> ()");
|
||||||
|
GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PUBLIC, m, null, null, writer);
|
||||||
|
mg.loadThis();
|
||||||
|
mg.invokeConstructor(wrapper, m);
|
||||||
|
mg.returnValue();
|
||||||
|
mg.endMethod();
|
||||||
|
|
||||||
|
m = Method.getMethod("java.lang.String getParentClass ()");
|
||||||
|
mg = new GeneratorAdapter(Opcodes.ACC_PROTECTED, m, null, null, writer);
|
||||||
|
mg.push(wrapperParentMap.get(fileName));
|
||||||
|
mg.returnValue();
|
||||||
|
mg.endMethod();
|
||||||
|
|
||||||
|
m = Method.getMethod("java.lang.String getCoreMod ()");
|
||||||
|
mg = new GeneratorAdapter(Opcodes.ACC_PROTECTED, m, null, null, writer);
|
||||||
|
mg.push(wrapperModMap.get(fileName));
|
||||||
|
mg.returnValue();
|
||||||
|
mg.endMethod();
|
||||||
|
|
||||||
|
writer.visitEnd();
|
||||||
|
|
||||||
|
return writer.toByteArray();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getWrapperName(String parentClass)
|
||||||
|
{
|
||||||
|
return parentClass + "_$Wrapper";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class WrapperVisitor extends ClassVisitor
|
||||||
|
{
|
||||||
|
private final String name;
|
||||||
|
private final String parentClass;
|
||||||
|
|
||||||
|
public WrapperVisitor(ClassVisitor cv, String name, String parentClass)
|
||||||
|
{
|
||||||
|
super(Opcodes.ASM5, cv);
|
||||||
|
this.name = name.replace('.', '/');
|
||||||
|
this.parentClass = parentClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
|
||||||
|
{
|
||||||
|
super.visit(version, access, this.name, signature, superName, interfaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value)
|
||||||
|
{
|
||||||
|
if(name.equals("parentClass"))
|
||||||
|
{
|
||||||
|
return super.visitField(access, name, desc, signature, parentClass);
|
||||||
|
}
|
||||||
|
return super.visitField(access, name, desc, signature, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class TransformerWrapper implements IClassTransformer
|
||||||
|
{
|
||||||
|
private final IClassTransformer parent;
|
||||||
|
|
||||||
|
public TransformerWrapper()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.parent = (IClassTransformer)this.getClass().getClassLoader().loadClass(getParentClass()).newInstance();
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] transform(String name, String transformedName, byte[] basicClass)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return parent.transform(name, transformedName, basicClass);
|
||||||
|
}
|
||||||
|
catch(Throwable e)
|
||||||
|
{
|
||||||
|
throw new TransformerException("Exception in class transformer " + parent + " from coremod " + getCoreMod(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "TransformerWrapper(" + getParentClass() + ", " + getCoreMod() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String getParentClass();
|
||||||
|
|
||||||
|
protected abstract String getCoreMod();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TransformerException extends RuntimeException
|
||||||
|
{
|
||||||
|
public TransformerException(String message, Throwable cause)
|
||||||
|
{
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
package net.minecraftforge.fml.common.asm.transformers;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import net.minecraft.launchwrapper.IClassTransformer;
|
||||||
|
import net.minecraftforge.fml.common.FMLLog;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.JavaVersion;
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.objectweb.asm.ClassReader;
|
||||||
|
import org.objectweb.asm.ClassVisitor;
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
|
public class BlamingTransformer implements IClassTransformer
|
||||||
|
{
|
||||||
|
private static final Map<String, String> classMap = new HashMap<String, String>();
|
||||||
|
private static final Set<String> naughtyMods = new HashSet<String>();
|
||||||
|
private static final Set<String> naughtyClasses = new TreeSet<String>();
|
||||||
|
private static final Set<String> orphanNaughtyClasses = new HashSet();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] transform(String name, String transformedName, byte[] bytes)
|
||||||
|
{
|
||||||
|
if (bytes == null) { return null; }
|
||||||
|
|
||||||
|
ClassReader classReader = new ClassReader(bytes);
|
||||||
|
VersionVisitor visitor = new VersionVisitor();
|
||||||
|
classReader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void blame(String modId, String cls)
|
||||||
|
{
|
||||||
|
naughtyClasses.add(cls);
|
||||||
|
naughtyMods.add(modId);
|
||||||
|
FMLLog.severe("Unsupported class format in mod %s: class %s", modId, cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class VersionVisitor extends ClassVisitor
|
||||||
|
{
|
||||||
|
public VersionVisitor()
|
||||||
|
{
|
||||||
|
super(Opcodes.ASM5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
|
||||||
|
{
|
||||||
|
if( (version == Opcodes.V1_8 && !SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_8)) ||
|
||||||
|
(version == Opcodes.V1_7 && !SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_7)) )
|
||||||
|
{
|
||||||
|
if(classMap.containsKey(name)) blame(classMap.get(name), name);
|
||||||
|
else orphanNaughtyClasses.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkPendingNaughty()
|
||||||
|
{
|
||||||
|
ImmutableSet.Builder<String> toRemove = ImmutableSet.builder();
|
||||||
|
for(String cls : orphanNaughtyClasses)
|
||||||
|
{
|
||||||
|
if(classMap.containsKey(cls))
|
||||||
|
{
|
||||||
|
String modId = classMap.get(cls);
|
||||||
|
blame(modId, cls);
|
||||||
|
toRemove.add(cls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
orphanNaughtyClasses.removeAll(toRemove.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addClasses(String modId, Set<String> classList)
|
||||||
|
{
|
||||||
|
for(String cls : classList)
|
||||||
|
{
|
||||||
|
classMap.put(cls, modId);
|
||||||
|
}
|
||||||
|
checkPendingNaughty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onCrash(StringBuilder builder)
|
||||||
|
{
|
||||||
|
checkPendingNaughty();
|
||||||
|
if(!naughtyClasses.isEmpty())
|
||||||
|
{
|
||||||
|
builder.append("\n*** ATTENTION: detected classes with unsupported format ***\n");
|
||||||
|
builder.append("*** DO NOT SUBMIT THIS CRASH REPORT TO FORGE ***\n\n");
|
||||||
|
if(!naughtyMods.isEmpty())
|
||||||
|
{
|
||||||
|
builder.append("Contact authors of the following mods: \n");
|
||||||
|
for(String modId : naughtyMods)
|
||||||
|
{
|
||||||
|
builder.append(" ").append(modId).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!orphanNaughtyClasses.isEmpty())
|
||||||
|
{
|
||||||
|
builder.append("Unidentified unsupported classes: \n");
|
||||||
|
for(String cls : orphanNaughtyClasses)
|
||||||
|
{
|
||||||
|
builder.append(" ").append(cls).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.append('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.jar.Attributes;
|
import java.util.jar.Attributes;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ import net.minecraft.launchwrapper.ITweaker;
|
||||||
import net.minecraft.launchwrapper.Launch;
|
import net.minecraft.launchwrapper.Launch;
|
||||||
import net.minecraft.launchwrapper.LaunchClassLoader;
|
import net.minecraft.launchwrapper.LaunchClassLoader;
|
||||||
import net.minecraftforge.fml.common.FMLLog;
|
import net.minecraftforge.fml.common.FMLLog;
|
||||||
|
import net.minecraftforge.fml.common.asm.ASMTransformerWrapper;
|
||||||
import net.minecraftforge.fml.common.asm.transformers.ModAccessTransformer;
|
import net.minecraftforge.fml.common.asm.transformers.ModAccessTransformer;
|
||||||
import net.minecraftforge.fml.common.launcher.FMLInjectionAndSortingTweaker;
|
import net.minecraftforge.fml.common.launcher.FMLInjectionAndSortingTweaker;
|
||||||
import net.minecraftforge.fml.common.launcher.FMLTweaker;
|
import net.minecraftforge.fml.common.launcher.FMLTweaker;
|
||||||
|
@ -50,6 +52,7 @@ 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;
|
||||||
import com.google.common.collect.ObjectArrays;
|
import com.google.common.collect.ObjectArrays;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
|
|
||||||
public class CoreModManager {
|
public class CoreModManager {
|
||||||
|
@ -58,12 +61,22 @@ public class CoreModManager {
|
||||||
private static final Attributes.Name MODSIDE = new Attributes.Name("ModSide");
|
private static final Attributes.Name MODSIDE = new Attributes.Name("ModSide");
|
||||||
private static String[] rootPlugins = { "net.minecraftforge.fml.relauncher.FMLCorePlugin", "net.minecraftforge.classloading.FMLForgePlugin" };
|
private static String[] rootPlugins = { "net.minecraftforge.fml.relauncher.FMLCorePlugin", "net.minecraftforge.classloading.FMLForgePlugin" };
|
||||||
private static List<String> loadedCoremods = Lists.newArrayList();
|
private static List<String> loadedCoremods = Lists.newArrayList();
|
||||||
|
private static Map<String, List<String>> transformers = Maps.newHashMap();
|
||||||
private static List<FMLPluginWrapper> loadPlugins;
|
private static List<FMLPluginWrapper> loadPlugins;
|
||||||
private static boolean deobfuscatedEnvironment;
|
private static boolean deobfuscatedEnvironment;
|
||||||
private static FMLTweaker tweaker;
|
private static FMLTweaker tweaker;
|
||||||
private static File mcDir;
|
private static File mcDir;
|
||||||
private static List<String> reparsedCoremods = Lists.newArrayList();
|
private static List<String> reparsedCoremods = Lists.newArrayList();
|
||||||
private static List<String> accessTransformers = Lists.newArrayList();
|
private static List<String> accessTransformers = Lists.newArrayList();
|
||||||
|
private static Set<String> rootNames = Sets.newHashSet();
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
for(String cls : rootPlugins)
|
||||||
|
{
|
||||||
|
rootNames.add(cls.substring(cls.lastIndexOf('.') + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class FMLPluginWrapper implements ITweaker {
|
private static class FMLPluginWrapper implements ITweaker {
|
||||||
public final String name;
|
public final String name;
|
||||||
|
@ -98,10 +111,19 @@ public class CoreModManager {
|
||||||
public void injectIntoClassLoader(LaunchClassLoader classLoader)
|
public void injectIntoClassLoader(LaunchClassLoader classLoader)
|
||||||
{
|
{
|
||||||
FMLRelaunchLog.fine("Injecting coremod %s {%s} class transformers", name, coreModInstance.getClass().getName());
|
FMLRelaunchLog.fine("Injecting coremod %s {%s} class transformers", name, coreModInstance.getClass().getName());
|
||||||
|
List<String> ts = Lists.newArrayList();
|
||||||
if (coreModInstance.getASMTransformerClass() != null) for (String transformer : coreModInstance.getASMTransformerClass())
|
if (coreModInstance.getASMTransformerClass() != null) for (String transformer : coreModInstance.getASMTransformerClass())
|
||||||
{
|
{
|
||||||
FMLRelaunchLog.finer("Registering transformer %s", transformer);
|
FMLRelaunchLog.finer("Registering transformer %s", transformer);
|
||||||
classLoader.registerTransformer(transformer);
|
classLoader.registerTransformer(ASMTransformerWrapper.getTransformerWrapper(classLoader, transformer, name));
|
||||||
|
ts.add(transformer);
|
||||||
|
}
|
||||||
|
if(!rootNames.contains(name))
|
||||||
|
{
|
||||||
|
String loc;
|
||||||
|
if(location == null) loc = "unknown";
|
||||||
|
else loc = location.getName();
|
||||||
|
transformers.put(name + " (" + loc + ")", ts);
|
||||||
}
|
}
|
||||||
FMLRelaunchLog.fine("Injection complete");
|
FMLRelaunchLog.fine("Injection complete");
|
||||||
|
|
||||||
|
@ -405,6 +427,11 @@ public class CoreModManager {
|
||||||
return loadedCoremods;
|
return loadedCoremods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Map<String, List<String>> getTransformers()
|
||||||
|
{
|
||||||
|
return transformers;
|
||||||
|
}
|
||||||
|
|
||||||
public static List<String> getReparseableCoremods()
|
public static List<String> getReparseableCoremods()
|
||||||
{
|
{
|
||||||
return reparsedCoremods;
|
return reparsedCoremods;
|
||||||
|
@ -609,4 +636,17 @@ public class CoreModManager {
|
||||||
{
|
{
|
||||||
return accessTransformers;
|
return accessTransformers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void onCrash(StringBuilder builder)
|
||||||
|
{
|
||||||
|
if(!loadedCoremods.isEmpty() || !reparsedCoremods.isEmpty())
|
||||||
|
{
|
||||||
|
builder.append("\nWARNING: coremods are present:\n");
|
||||||
|
for(String coreMod : transformers.keySet())
|
||||||
|
{
|
||||||
|
builder.append(" ").append(coreMod).append('\n');
|
||||||
|
}
|
||||||
|
builder.append("Contact their authors BEFORE contacting forge\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ public class FMLCorePlugin implements IFMLLoadingPlugin
|
||||||
public String[] getASMTransformerClass()
|
public String[] getASMTransformerClass()
|
||||||
{
|
{
|
||||||
return new String[] {
|
return new String[] {
|
||||||
|
"net.minecraftforge.fml.common.asm.transformers.BlamingTransformer",
|
||||||
"net.minecraftforge.fml.common.asm.transformers.MarkerTransformer",
|
"net.minecraftforge.fml.common.asm.transformers.MarkerTransformer",
|
||||||
"net.minecraftforge.fml.common.asm.transformers.SideTransformer",
|
"net.minecraftforge.fml.common.asm.transformers.SideTransformer",
|
||||||
"net.minecraftforge.fml.common.asm.transformers.EventSubscriptionTransformer",
|
"net.minecraftforge.fml.common.asm.transformers.EventSubscriptionTransformer",
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package net.minecraftforge.fml.debug;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.minecraft.launchwrapper.IClassTransformer;
|
||||||
|
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
|
||||||
|
|
||||||
|
public class FaultyCoreMod implements IFMLLoadingPlugin {
|
||||||
|
public static boolean enabled = false;
|
||||||
|
|
||||||
|
public String[] getASMTransformerClass()
|
||||||
|
{
|
||||||
|
return new String[] { FaultyTransformer.class.getName() };
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModContainerClass() { return null; }
|
||||||
|
public String getSetupClass() { return null; }
|
||||||
|
public void injectData(Map<String, Object> data) {}
|
||||||
|
public String getAccessTransformerClass() { return null; }
|
||||||
|
|
||||||
|
public static class FaultyTransformer implements IClassTransformer {
|
||||||
|
|
||||||
|
public byte[] transform(String name, String transformedName, byte[] basicClass)
|
||||||
|
{
|
||||||
|
if(enabled && name.equals("net.minecraft.client.gui.GuiMainMenu")) throw new RuntimeException("Faulty transformer test exception");
|
||||||
|
return basicClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*package net.minecraftforge.fml.debug;
|
||||||
|
|
||||||
|
import net.minecraftforge.fml.common.Mod;
|
||||||
|
import net.minecraftforge.fml.common.Mod.EventHandler;
|
||||||
|
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
|
||||||
|
|
||||||
|
@Mod(modid = Java8Debug.MODID, version = Java8Debug.VERSION)
|
||||||
|
public class Java8Debug implements ITest
|
||||||
|
{
|
||||||
|
public static final String MODID = "Java8Debug";
|
||||||
|
public static final String VERSION = "1.0";
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void init(FMLInitializationEvent event)
|
||||||
|
{
|
||||||
|
System.out.println("Java8Debug mod loaded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ITest
|
||||||
|
{
|
||||||
|
default int test()
|
||||||
|
{
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
}*/
|
Loading…
Reference in New Issue