Got the relauncher downloading and injecting library files

This commit is contained in:
Christian 2012-07-05 15:23:37 -04:00
parent d1f791f94e
commit e8b09f4692
10 changed files with 416 additions and 61 deletions

View File

@ -266,14 +266,17 @@
<target name="repatch" depends="buildenvsetup">
<echo>Moving old patched sources at ${mcp.home}/src-work out of the way</echo>
<move todir="${mcp.home}/src-work${timestamp}" failonerror="false" verbose="true">
<fileset dir="${mcp.home}/src-work"/>
<fileset dir="${patch.mcp.srcdir}"/>
</move>
<delete dir="${mcp.srcdir}"/>
<copy todir="${mcp.srcdir}">
<fileset dir="${mcp.home}/src-base"/>
<fileset dir="${clean.mcp.srcdir}"/>
</copy>
<antcall target="cleanargo"/>
<antcall target="patch"/>
<copy todir="${mcp.home}/src-work">
<copy todir="${patch.mcp.srcdir}">
<fileset dir="${mcp.srcdir}"/>
</copy>
</target>

View File

@ -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;
/**

View File

@ -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 <T, E> void setPrivateValue(Class<? super T> 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 <T, E> void setPrivateValue(Class<? super T> instanceclass, T instance, String field, E value)
{
ReflectionHelper.setPrivateValue(instanceclass, instance, field, value);
ReflectionHelper.setPrivateValue(instanceclass, instance, value, field);
}
/**

View File

@ -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, E> T getPrivateValue(Class <? super E > classToAccess, E instance, String fieldName)
public static <T, E> T getPrivateValue(Class <? super E > 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 <T, E> void setPrivateValue(Class <? super T > 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 <T, E> void setPrivateValue(Class <? super T > classToAccess, T instance, String fieldName, E value)
public static <T, E> void setPrivateValue(Class <? super T > 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 <T, E> void setPrivateValue(Class <? super T > classToAccess, T instance, String fieldName, E value)
{
setPrivateValue(classToAccess, instance, value, fieldName);
}
public static <T, E> void setPrivateValue(Class <? super T > 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)
{

View File

@ -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<? super Object> 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<? super Object> 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
}
}
}

View File

@ -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, E> T getPrivateValue(Class <? super E > 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, E> T getPrivateValue(Class <? super E > classToAccess, E instance, String... fieldNames)
{
try
{
return (T) findField(classToAccess, fieldNames).get(instance);
}
catch (Exception e)
{
throw new UnableToAccessFieldException(fieldNames, e);
}
}
public static <T, E> void setPrivateValue(Class <? super T > 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 <T, E> void setPrivateValue(Class <? super T > 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<? super Object> getClass(ClassLoader loader, String... classNames)
{
Exception err = null;
for (String className : classNames)
{
try
{
return (Class<? super Object>) Class.forName(className, false, loader);
}
catch (Exception e)
{
err = e;
}
}
throw new UnableToFindClassException(classNames, err);
}
public static <E> Method findMethod(Class<? super E> 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);
}
}

View File

@ -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()));
}
}

View File

@ -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<libraries.length; i++)
{
String libName = libraries[i];
String checksum = checksums[i];
File libFile = new File(libDir, libName);
if (!libFile.exists())
{
downloadFile(libFile);
}
if (libFile.exists() && !libFile.isFile())
{
throw new RuntimeException(String.format("Found a file %s that is not a normal file", libName));
}
String fileChecksum = generateChecksum(libFile);
if (!checksum.equals(fileChecksum))
{
throw new RuntimeException(String.format("The file %s has an invalid checksum %s (expecting %s)", libName, fileChecksum, checksum));
}
try
{
actualClassLoader.addURL(libFile.toURI().toURL());
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
}
}
private static String generateChecksum(File file)
{
try
{
FileInputStream fis = new FileInputStream(file);
FileChannel chan = fis.getChannel();
MessageDigest digest = MessageDigest.getInstance("SHA-1");
MappedByteBuffer mappedFile = chan.map(MapMode.READ_ONLY, 0, file.length());
digest.update(mappedFile);
chan.close();
fis.close();
byte[] chksum = digest.digest();
final StringBuilder hex = new StringBuilder( 2 * chksum.length );
for ( final byte b : chksum ) {
hex.append(HEXES.charAt((b & 0xF0) >> 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)
{
}
}
}

View File

@ -18,5 +18,6 @@
</attributes>
</classpathentry>
<classpathentry kind="lib" path="jars/bin/minecraft.jar"/>
<classpathentry kind="lib" path="/home/cpw/projects/FML/mcsnapshot/lib/argo-2.25.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="common"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="client"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="lib" path="jars/bin/jinput.jar">