From e8b09f46929dc37dbf5158060e4baeb33607ca49 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 5 Jul 2012 15:23:37 -0400 Subject: [PATCH] Got the relauncher downloading and injecting library files --- fml/build.xml | 9 +- .../cpw/mods/fml/client/FMLClientHandler.java | 2 +- fml/client/net/minecraft/src/ModLoader.java | 6 +- .../cpw/mods/fml/common/ReflectionHelper.java | 94 +++++---- .../relauncher/FMLEmbeddingRelauncher.java | 36 ++-- .../mods/fml/relauncher/ReflectionHelper.java | 183 ++++++++++++++++++ .../fml/relauncher/RelaunchClassLoader.java | 7 + .../relauncher/RelaunchLibraryManager.java | 137 +++++++++++++ fml/eclipse/Clean-Client/.classpath | 1 + fml/eclipse/FML-Client/.classpath | 2 +- 10 files changed, 416 insertions(+), 61 deletions(-) create mode 100644 fml/common/cpw/mods/fml/relauncher/ReflectionHelper.java create mode 100644 fml/common/cpw/mods/fml/relauncher/RelaunchLibraryManager.java diff --git a/fml/build.xml b/fml/build.xml index 587bb5a053..eff40159e7 100644 --- a/fml/build.xml +++ b/fml/build.xml @@ -266,14 +266,17 @@ Moving old patched sources at ${mcp.home}/src-work out of the way - + - + + + + - + diff --git a/fml/client/cpw/mods/fml/client/FMLClientHandler.java b/fml/client/cpw/mods/fml/client/FMLClientHandler.java index 4ce22755bb..eea61f0d4c 100644 --- a/fml/client/cpw/mods/fml/client/FMLClientHandler.java +++ b/fml/client/cpw/mods/fml/client/FMLClientHandler.java @@ -88,13 +88,13 @@ import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.ModMetadata; import cpw.mods.fml.common.ProxyInjector; -import cpw.mods.fml.common.ReflectionHelper; import cpw.mods.fml.common.Side; import cpw.mods.fml.common.TickType; import cpw.mods.fml.common.modloader.ModLoaderHelper; import cpw.mods.fml.common.modloader.ModLoaderModContainer; import cpw.mods.fml.common.modloader.ModProperty; import cpw.mods.fml.common.registry.FMLRegistry; +import cpw.mods.fml.common.ReflectionHelper; /** diff --git a/fml/client/net/minecraft/src/ModLoader.java b/fml/client/net/minecraft/src/ModLoader.java index 91be235c7d..502caebd03 100644 --- a/fml/client/net/minecraft/src/ModLoader.java +++ b/fml/client/net/minecraft/src/ModLoader.java @@ -26,10 +26,10 @@ import cpw.mods.fml.client.FMLClientHandler; import cpw.mods.fml.client.SpriteHelper; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.Loader; -import cpw.mods.fml.common.ReflectionHelper; import cpw.mods.fml.common.modloader.ModLoaderHelper; import cpw.mods.fml.common.modloader.ModLoaderModContainer; import cpw.mods.fml.common.registry.FMLRegistry; +import cpw.mods.fml.common.ReflectionHelper; public class ModLoader { @@ -733,7 +733,7 @@ public class ModLoader */ public static void setPrivateValue(Class instanceclass, T instance, int fieldindex, E value) { - ReflectionHelper.setPrivateValue(instanceclass, instance, fieldindex, value); + ReflectionHelper.setPrivateValue(instanceclass, instance, value, fieldindex); } /** @@ -747,7 +747,7 @@ public class ModLoader */ public static void setPrivateValue(Class instanceclass, T instance, String field, E value) { - ReflectionHelper.setPrivateValue(instanceclass, instance, field, value); + ReflectionHelper.setPrivateValue(instanceclass, instance, value, field); } /** diff --git a/fml/common/cpw/mods/fml/common/ReflectionHelper.java b/fml/common/cpw/mods/fml/common/ReflectionHelper.java index 30bc523448..251da11835 100644 --- a/fml/common/cpw/mods/fml/common/ReflectionHelper.java +++ b/fml/common/cpw/mods/fml/common/ReflectionHelper.java @@ -14,9 +14,15 @@ package cpw.mods.fml.common; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.logging.Level; + +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.relauncher.ReflectionHelper.UnableToAccessFieldException; +import cpw.mods.fml.relauncher.ReflectionHelper.UnableToFindFieldException; /** * Some reflection helper code. - * + * * @author cpw * */ @@ -29,73 +35,79 @@ public class ReflectionHelper { try { - Field f = classToAccess.getDeclaredFields()[fieldIndex]; - f.setAccessible(true); - return (T) f.get(instance); + return cpw.mods.fml.relauncher.ReflectionHelper.getPrivateValue(classToAccess, instance, fieldIndex); } - catch (Exception e) + catch (UnableToAccessFieldException e) { - FMLCommonHandler.instance().getFMLLogger().severe(String.format("There was a problem getting field %d from %s", fieldIndex, classToAccess.getName())); - FMLCommonHandler.instance().getFMLLogger().throwing("ReflectionHelper", "getPrivateValue", e); - throw new RuntimeException(e); + FMLCommonHandler.instance().getFMLLogger().log(Level.SEVERE, String.format("There was a problem getting field index %d from %s", fieldIndex, classToAccess.getName()), e); + throw e; } } @SuppressWarnings("unchecked") - public static T getPrivateValue(Class classToAccess, E instance, String fieldName) + public static T getPrivateValue(Class classToAccess, E instance, String... fieldNames) { try { - Field f = classToAccess.getDeclaredField(fieldName); - f.setAccessible(true); - return (T) f.get(instance); + return cpw.mods.fml.relauncher.ReflectionHelper.getPrivateValue(classToAccess, instance, fieldNames); } - catch (Exception e) + catch (UnableToFindFieldException e) { - if ((fieldName.length() > 3 && !obfuscation) || (fieldName.length() <= 3 && obfuscation)) { - FMLCommonHandler.instance().getFMLLogger().severe(String.format("There was a problem getting field %s from %s", fieldName, classToAccess.getName())); - FMLCommonHandler.instance().getFMLLogger().throwing("ReflectionHelper", "getPrivateValue", e); - } - throw new RuntimeException(e); + FMLCommonHandler.instance().getFMLLogger().log(Level.SEVERE, String.format("Unable to locate any field %s on type %s", Arrays.toString(fieldNames), classToAccess.getName()), e); + throw e; + } + catch (UnableToAccessFieldException e) + { + FMLCommonHandler.instance().getFMLLogger().log(Level.SEVERE, String.format("Unable to access any field %s on type %s", Arrays.toString(fieldNames), classToAccess.getName()), e); + throw e; } } + @Deprecated public static void setPrivateValue(Class classToAccess, T instance, int fieldIndex, E value) { - try - { - Field f = classToAccess.getDeclaredFields()[fieldIndex]; - f.setAccessible(true); - f.set(instance, value); - } - catch (Exception e) - { - FMLCommonHandler.instance().getFMLLogger().severe(String.format("There was a problem setting field %d from %s", fieldIndex, classToAccess.getName())); - FMLCommonHandler.instance().getFMLLogger().throwing("ReflectionHelper", "getPrivateValue", e); - throw new RuntimeException(e); - } + setPrivateValue(classToAccess, instance, value, fieldIndex); } - public static void setPrivateValue(Class classToAccess, T instance, String fieldName, E value) + public static void setPrivateValue(Class classToAccess, T instance, E value, int fieldIndex) { try { - Field f = classToAccess.getDeclaredField(fieldName); - f.setAccessible(true); - f.set(instance, value); + cpw.mods.fml.relauncher.ReflectionHelper.setPrivateValue(classToAccess, instance, value, fieldIndex); } - catch (Exception e) + catch (UnableToAccessFieldException e) { - if ((fieldName.length() > 3 && !obfuscation) || (fieldName.length() <= 3 && obfuscation)) { - FMLCommonHandler.instance().getFMLLogger().severe(String.format("There was a problem setting field %s from %s", fieldName, classToAccess.getName())); - FMLCommonHandler.instance().getFMLLogger().throwing("ReflectionHelper", "getPrivateValue", e); - } - throw new RuntimeException(e); + FMLCommonHandler.instance().getFMLLogger().log(Level.SEVERE, String.format("There was a problem setting field index %d on type %s", fieldIndex, classToAccess.getName())); + throw e; + } + } + + @Deprecated + public static void setPrivateValue(Class classToAccess, T instance, String fieldName, E value) + { + setPrivateValue(classToAccess, instance, value, fieldName); + } + + public static void setPrivateValue(Class classToAccess, T instance, E value, String... fieldNames) + { + try + { + cpw.mods.fml.relauncher.ReflectionHelper.setPrivateValue(classToAccess, instance, value, fieldNames); + } + catch (UnableToFindFieldException e) + { + FMLCommonHandler.instance().getFMLLogger().log(Level.SEVERE, String.format("Unable to locate any field %s on type %s", Arrays.toString(fieldNames), classToAccess.getName()), e); + throw e; + } + catch (UnableToAccessFieldException e) + { + FMLCommonHandler.instance().getFMLLogger().log(Level.SEVERE, String.format("Unable to set any field %s on type %s", Arrays.toString(fieldNames), classToAccess.getName()), e); + throw e; } } /** - * + * */ public static void detectObfuscation(Class clazz) { diff --git a/fml/common/cpw/mods/fml/relauncher/FMLEmbeddingRelauncher.java b/fml/common/cpw/mods/fml/relauncher/FMLEmbeddingRelauncher.java index d924f816f0..f905984268 100644 --- a/fml/common/cpw/mods/fml/relauncher/FMLEmbeddingRelauncher.java +++ b/fml/common/cpw/mods/fml/relauncher/FMLEmbeddingRelauncher.java @@ -1,17 +1,18 @@ package cpw.mods.fml.relauncher; +import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.net.URLClassLoader; +import java.util.Arrays; -import net.minecraft.client.Minecraft; import net.minecraft.src.WorldSettings; public class FMLEmbeddingRelauncher { private static FMLEmbeddingRelauncher INSTANCE; private RelaunchClassLoader clientLoader; - private RelaunchClassLoader serverLoader; public static void relaunch(ArgsWrapper wrap) { @@ -24,25 +25,36 @@ public class FMLEmbeddingRelauncher URLClassLoader ucl = (URLClassLoader)getClass().getClassLoader(); clientLoader = new RelaunchClassLoader(ucl.getURLs()); - serverLoader = new RelaunchClassLoader(ucl.getURLs()); } private void relaunchClient(ArgsWrapper wrap) { + Class mcMaster = ReflectionHelper.getClass(getClass().getClassLoader(), "net.minecraft.client.Minecraft"); + // We force minecraft to setup it's homedir very early on so we can inject stuff into it + Method setupHome = ReflectionHelper.findMethod(mcMaster, null, new String[] { "func_6240_b", "getMinecraftDir", "b"} ); try { - Class original = Class.forName("net.minecraft.client.Minecraft", false, getClass().getClassLoader()); - Field origDir = original.getDeclaredField("field_6275_Z"); - origDir.setAccessible(true); - Class client = Class.forName("net.minecraft.client.Minecraft", false, clientLoader); - Field homeDir = client.getDeclaredField("field_6275_Z"); - homeDir.setAccessible(true); - homeDir.set(null, origDir.get(null)); - client.getMethod("fmlReentry", ArgsWrapper.class).invoke(null, wrap); + setupHome.invoke(null); } catch (Exception e) { - e.printStackTrace(); + // Hmmm + } + File minecraftHome = ReflectionHelper.getPrivateValue(mcMaster, null, "field_6275_Z", "ap", "minecraftDir"); + + RelaunchLibraryManager.handleLaunch(minecraftHome, clientLoader); + + // Now we re-inject the home into the "new" minecraft under our control + Class client = ReflectionHelper.getClass(clientLoader, "net.minecraft.client.Minecraft"); + ReflectionHelper.setPrivateValue(client, null, minecraftHome, "field_6275_Z", "ap", "minecraftDir"); + + try + { + ReflectionHelper.findMethod(client, null, new String[] { "fmlReentry" }, ArgsWrapper.class).invoke(null, wrap); + } + catch (Exception e) + { + // Hmmm } } } diff --git a/fml/common/cpw/mods/fml/relauncher/ReflectionHelper.java b/fml/common/cpw/mods/fml/relauncher/ReflectionHelper.java new file mode 100644 index 0000000000..f441197feb --- /dev/null +++ b/fml/common/cpw/mods/fml/relauncher/ReflectionHelper.java @@ -0,0 +1,183 @@ +/* + * The FML Forge Mod Loader suite. Copyright (C) 2012 cpw + * + * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +package cpw.mods.fml.relauncher; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import cpw.mods.fml.common.FMLCommonHandler; +/** + * Some reflection helper code. + * + * @author cpw + * + */ +public class ReflectionHelper +{ + public static class UnableToFindMethodException extends RuntimeException + { + private String[] methodNames; + + public UnableToFindMethodException(String[] methodNames, Exception failed) + { + super(failed); + this.methodNames = methodNames; + } + + } + + public static class UnableToFindClassException extends RuntimeException + { + private String[] classNames; + + public UnableToFindClassException(String[] classNames, Exception err) + { + super(err); + this.classNames = classNames; + } + + } + + public static class UnableToAccessFieldException extends RuntimeException + { + + private String[] fieldNameList; + + public UnableToAccessFieldException(String[] fieldNames, Exception e) + { + super(e); + this.fieldNameList = fieldNames; + } + } + + public static class UnableToFindFieldException extends RuntimeException + { + private String[] fieldNameList; + public UnableToFindFieldException(String[] fieldNameList, Exception e) + { + super(e); + this.fieldNameList = fieldNameList; + } + } + + public static Field findField(Class clazz, String... fieldNames) + { + Exception failed = null; + for (String fieldName : fieldNames) + { + try + { + Field f = clazz.getDeclaredField(fieldName); + f.setAccessible(true); + return f; + } + catch (Exception e) + { + failed = e; + } + } + throw new UnableToFindFieldException(fieldNames, failed); + } + + @SuppressWarnings("unchecked") + public static T getPrivateValue(Class classToAccess, E instance, int fieldIndex) + { + try + { + Field f = classToAccess.getDeclaredFields()[fieldIndex]; + f.setAccessible(true); + return (T) f.get(instance); + } + catch (Exception e) + { + throw new UnableToAccessFieldException(new String[0], e); + } + } + + @SuppressWarnings("unchecked") + public static T getPrivateValue(Class classToAccess, E instance, String... fieldNames) + { + try + { + return (T) findField(classToAccess, fieldNames).get(instance); + } + catch (Exception e) + { + throw new UnableToAccessFieldException(fieldNames, e); + } + } + + public static void setPrivateValue(Class classToAccess, T instance, E value, int fieldIndex) + { + try + { + Field f = classToAccess.getDeclaredFields()[fieldIndex]; + f.setAccessible(true); + f.set(instance, value); + } + catch (Exception e) + { + throw new UnableToAccessFieldException(new String[0] , e); + } + } + + public static void setPrivateValue(Class classToAccess, T instance, E value, String... fieldNames) + { + try + { + findField(classToAccess, fieldNames).set(instance, value); + } + catch (Exception e) + { + throw new UnableToAccessFieldException(fieldNames, e); + } + } + + public static Class getClass(ClassLoader loader, String... classNames) + { + Exception err = null; + for (String className : classNames) + { + try + { + return (Class) Class.forName(className, false, loader); + } + catch (Exception e) + { + err = e; + } + } + + throw new UnableToFindClassException(classNames, err); + } + + + public static Method findMethod(Class clazz, E instance, String[] methodNames, Class... methodTypes) + { + Exception failed = null; + for (String methodName : methodNames) + { + try + { + Method m = clazz.getDeclaredMethod(methodName, methodTypes); + m.setAccessible(true); + return m; + } + catch (Exception e) + { + failed = e; + } + } + throw new UnableToFindMethodException(methodNames, failed); + } +} diff --git a/fml/common/cpw/mods/fml/relauncher/RelaunchClassLoader.java b/fml/common/cpw/mods/fml/relauncher/RelaunchClassLoader.java index 5a4e720349..78546e91cc 100644 --- a/fml/common/cpw/mods/fml/relauncher/RelaunchClassLoader.java +++ b/fml/common/cpw/mods/fml/relauncher/RelaunchClassLoader.java @@ -28,4 +28,11 @@ public class RelaunchClassLoader extends URLClassLoader return super.loadClass(name); } } + + @Override + protected void addURL(URL url) + { + super.addURL(url); + System.out.println(Arrays.toString(getURLs())); + } } diff --git a/fml/common/cpw/mods/fml/relauncher/RelaunchLibraryManager.java b/fml/common/cpw/mods/fml/relauncher/RelaunchLibraryManager.java new file mode 100644 index 0000000000..383e3682c9 --- /dev/null +++ b/fml/common/cpw/mods/fml/relauncher/RelaunchLibraryManager.java @@ -0,0 +1,137 @@ +package cpw.mods.fml.relauncher; + +import java.awt.HeadlessException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.net.URLConnection; +import java.nio.MappedByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.FileChannel.MapMode; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import javax.swing.JOptionPane; + +public class RelaunchLibraryManager +{ + private static String[] libraries = { "argo-2.25.jar","guava-12.0.jar","asm-all-4.0.jar" }; + private static String[] checksums = { "bb672829fde76cb163004752b86b0484bd0a7f4b", "5bc66dd95b79db1e437eb08adba124a3e4088dc0", "98308890597acb64047f7e896638e0d98753ae82" }; + private static final String HEXES = "0123456789abcdef"; + public static void handleLaunch(File mcDir, RelaunchClassLoader actualClassLoader) + { + File libDir = new File(mcDir,"lib"); + try + { + libDir = libDir.getCanonicalFile(); + } + catch (IOException e) + { + throw new RuntimeException(String.format("Unable to canonicalize the lib dir at %s", mcDir.getName()),e); + } + if (!libDir.exists()) + { + libDir.mkdir(); + } + else if (libDir.exists() && !libDir.isDirectory()) + { + throw new RuntimeException(String.format("Found a lib file in %s that's not a directory", mcDir.getName())); + } + + for (int i=0; i> 4)) + .append(HEXES.charAt((b & 0x0F))); + } + return hex.toString(); + } + catch (Exception e) + { + return null; + } + } + private static void downloadFile(File libFile) + { + // try +// { +// JOptionPane.showMessageDialog(null, String.format("Downloading required FML library %s from github.com",libName)); +// } +// catch (HeadlessException he) +// { +// // Ignore +// } + try + { + URL libDownload = new URL(String.format("http://cloud.github.com/downloads/cpw/FML/%s",libFile.getName())); + System.out.printf("Downloading %s..", libDownload.toString()); + InputStream urlConn = libDownload.openStream(); + ReadableByteChannel urlChannel = Channels.newChannel(urlConn); + FileOutputStream libFileStream = new FileOutputStream(libFile); + FileChannel libFileChannel = libFileStream.getChannel(); + libFileChannel.transferFrom(urlChannel, 0, 1<<24); + libFileChannel.close(); + libFileStream.close(); + urlChannel.close(); + urlConn.close(); + System.out.println("download successful"); + } + catch (Exception e) + { + + } + } +} diff --git a/fml/eclipse/Clean-Client/.classpath b/fml/eclipse/Clean-Client/.classpath index 6bf817e741..2660e24ec7 100644 --- a/fml/eclipse/Clean-Client/.classpath +++ b/fml/eclipse/Clean-Client/.classpath @@ -18,5 +18,6 @@ + diff --git a/fml/eclipse/FML-Client/.classpath b/fml/eclipse/FML-Client/.classpath index d750167506..ea960b8032 100644 --- a/fml/eclipse/FML-Client/.classpath +++ b/fml/eclipse/FML-Client/.classpath @@ -1,7 +1,7 @@ - +