diff --git a/build.gradle b/build.gradle index 36df60c..bd90706 100644 --- a/build.gradle +++ b/build.gradle @@ -48,8 +48,8 @@ apply plugin: 'maven-publish' apply plugin: 'eclipse' -sourceCompatibility = '1.6' -targetCompatibility = '1.6' +sourceCompatibility = '1.8' +targetCompatibility = '1.8' compileJava.options.encoding = 'UTF-8' diff --git a/src/main/java/cpw/mods/fml/relauncher/CoreModManager.java b/src/main/java/cpw/mods/fml/relauncher/CoreModManager.java index deb055e..ba2e833 100644 --- a/src/main/java/cpw/mods/fml/relauncher/CoreModManager.java +++ b/src/main/java/cpw/mods/fml/relauncher/CoreModManager.java @@ -113,7 +113,7 @@ Map data = new HashMap(); data.put("mcLocation", mcDir); data.put("coremodList", loadPlugins); - data.put("runtimeDeobfuscationEnabled", true);//!deobfuscatedEnvironment); //Mods should assume that we in obf env + data.put("runtimeDeobfuscationEnabled", !deobfuscatedEnvironment); FMLRelaunchLog.fine("Running coremod plugin %s", name); data.put("coremodLocation", location); coreModInstance.injectData(data); diff --git a/src/main/java/org/ultramine/server/UltraminePlugin.java b/src/main/java/org/ultramine/server/UltraminePlugin.java index 516f3a3..b132229 100644 --- a/src/main/java/org/ultramine/server/UltraminePlugin.java +++ b/src/main/java/org/ultramine/server/UltraminePlugin.java @@ -1,15 +1,18 @@ package org.ultramine.server; import cpw.mods.fml.relauncher.IFMLLoadingPlugin; +import cpw.mods.fml.relauncher.IFMLLoadingPlugin.SortingIndex; import java.io.File; import java.util.Map; import net.minecraft.launchwrapper.LaunchClassLoader; +@SortingIndex(Integer.MAX_VALUE) //Java8ComputeFramesTransformer must be always the last public class UltraminePlugin implements IFMLLoadingPlugin { public static File location; + public static boolean isObfEnv; @Override public String[] getASMTransformerClass() @@ -17,6 +20,8 @@ return new String[]{ "org.ultramine.server.asm.transformers.TrigMathTransformer", "org.ultramine.server.asm.transformers.PrintStackTraceTransformer", + + "org.ultramine.server.asm.transformers.Java8ComputeFramesTransformer", //must be always the last }; } @@ -36,6 +41,7 @@ public void injectData(Map data) { location = (File)data.get("coremodLocation"); + isObfEnv = (Boolean)data.get("runtimeDeobfuscationEnabled"); LaunchClassLoader cl = (LaunchClassLoader)this.getClass().getClassLoader(); cl.addTransformerExclusion("org.ultramine.server.asm."); diff --git a/src/main/java/org/ultramine/server/asm/ComputeFramesClassWriter.java b/src/main/java/org/ultramine/server/asm/ComputeFramesClassWriter.java new file mode 100644 index 0000000..9b53171 --- /dev/null +++ b/src/main/java/org/ultramine/server/asm/ComputeFramesClassWriter.java @@ -0,0 +1,152 @@ +package org.ultramine.server.asm; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; +import org.ultramine.server.UltraminePlugin; + +import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper; +import cpw.mods.fml.common.patcher.ClassPatchManager; +import net.minecraft.launchwrapper.LaunchClassLoader; + +public class ComputeFramesClassWriter extends ClassWriter +{ + private static final LaunchClassLoader CLASSLOADER = (LaunchClassLoader)ComputeFramesClassWriter.class.getClassLoader(); + + public ComputeFramesClassWriter() + { + super(ClassWriter.COMPUTE_FRAMES); + } + + @Override + protected String getCommonSuperClass(String type1, String type2) + { + if(type1.equals(type2)) + return type1; + if(type1.equals("java/lang/Object") || type2.equals("java/lang/Object")) + return "java/lang/Object"; + + ClassReader node1 = getClassNode(type1); + ClassReader node2 = getClassNode(type2); + + if((node1 != null && (node1.getAccess() & Opcodes.ACC_INTERFACE) != 0) || (node2 != null && (node2.getAccess() & Opcodes.ACC_INTERFACE) != 0)) + return "java/lang/Object"; + + List sup1 = getSuperTypesStack(type1, node1); + if(sup1 == null) + return "java/lang/Object"; + List sup2 = getSuperTypesStack(type2, node2); + if(sup2 == null) + return "java/lang/Object"; + + if(sup2.contains(type1)) + return type1; + if(sup1.contains(type2)) + return type2; + + if(sup1.isEmpty() || sup2.isEmpty()) + return "java/lang/Object"; + + for(int i = 0; i < sup1.size(); i++) + { + String s1 = sup1.get(i); + if(sup2.contains(s1)) + return s1; + } + return "java/lang/Object"; + } + + private static ClassReader getClassNode(String name) + { + try + { + byte[] classBytes = ClassPatchManager.INSTANCE.getPatchedResource(UltraminePlugin.isObfEnv ? FMLDeobfuscatingRemapper.INSTANCE.unmap(name) : name, + FMLDeobfuscatingRemapper.INSTANCE.map(name), CLASSLOADER); + if(classBytes != null) + { + ClassReader reader = new ClassReader(classBytes); + return reader; + } + return null; + } + catch(IOException e) + { + throw new RuntimeException(e); + } + } + + private static List getSuperTypesStack(String type, ClassReader node) + { + try + { + if(node == null) + { + Class cls1 = Class.forName(type.replace('/', '.'), false, CLASSLOADER); + if(cls1.isInterface()) + return null; + return getSuperTypesStack(cls1); + } + else + { + return getSuperTypesStack(node); + } + } + catch(ClassNotFoundException e) + { + return Collections.emptyList(); + } + } + + private static List getSuperTypesStack(ClassReader node) + { + String superName = FMLDeobfuscatingRemapper.INSTANCE.map(node.getSuperName()); + if(superName.equals("java/lang/Object")) + return Collections.emptyList(); + List list = new ArrayList(4); + list.add(superName); + getSuperTypesStack(list, superName); + return list; + } + + private static List getSuperTypesStack(Class cls) + { + if(cls.getSuperclass() == Object.class) + return Collections.emptyList(); + List list = new ArrayList(4); + while((cls = cls.getSuperclass()) != Object.class) + list.add(cls.getName().replace('.', '/')); + return list; + } + + private static void getSuperTypesStack(List list, String name) + { + ClassReader node = getClassNode(name); + if(node != null) + { + String superName = FMLDeobfuscatingRemapper.INSTANCE.map(node.getSuperName()); + if(!superName.equals("java/lang/Object")) + { + list.add(superName); + getSuperTypesStack(list, superName); + } + } + else + { + try + { + Class cls = Class.forName(name.replace('/', '.'), false, CLASSLOADER); + for(; cls != Object.class; cls = cls.getSuperclass()) + list.add(cls.getName().replace('.', '/')); + } + catch(ClassNotFoundException ignored) + { + //will be used incomplete hierarchy + } + } + } +} diff --git a/src/main/java/org/ultramine/server/asm/transformers/Java8ComputeFramesTransformer.java b/src/main/java/org/ultramine/server/asm/transformers/Java8ComputeFramesTransformer.java new file mode 100644 index 0000000..7646e19 --- /dev/null +++ b/src/main/java/org/ultramine/server/asm/transformers/Java8ComputeFramesTransformer.java @@ -0,0 +1,25 @@ +package org.ultramine.server.asm.transformers; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; +import org.ultramine.server.asm.ComputeFramesClassWriter; + +import net.minecraft.launchwrapper.IClassTransformer; + +public class Java8ComputeFramesTransformer implements IClassTransformer +{ + @Override + public byte[] transform(String name, String transformedName, byte[] basicClass) + { + if(basicClass == null) + return null; + ClassReader classReader = new ClassReader(basicClass); + if(classReader.readInt(classReader.getItem(1) - 7) <= Opcodes.V1_6) + return basicClass; + + ClassWriter writer = new ComputeFramesClassWriter(); + classReader.accept(writer, 0); + return writer.toByteArray(); + } +}