NOISSUE redo the launcher part

This commit is contained in:
Alex 2015-05-21 17:23:20 -04:00 committed by Petr Mrázek
parent 678c4793f9
commit c1f7dda8fe
6 changed files with 114 additions and 336 deletions

View File

@ -18,17 +18,20 @@ set(SRC
# The launcher has to be there for silly FML/Forge relauncher. # The launcher has to be there for silly FML/Forge relauncher.
net/minecraft/Launcher.java net/minecraft/Launcher.java
org/multimc/legacy/LegacyLauncher.java org/multimc/legacy/LegacyLauncher.java
org/multimc/LegacyFrame.java
# onesix launcher # onesix launcher
org/multimc/onesix/OneSixLauncher.java org/multimc/onesix/OneSixLauncher.java
org/multimc/onesix/MMCClassLoader.java
# generic launcher # generic launcher
org/multimc/EntryPoint.java org/multimc/EntryPoint.java
org/multimc/Launcher.java org/multimc/Launcher.java
org/multimc/LegacyFrame.java
org/multimc/NotFoundException.java
org/multimc/ParamBucket.java
org/multimc/ParseException.java org/multimc/ParseException.java
org/multimc/Utils.java org/multimc/Utils.java
org/multimc/IconLoader.java
) )
add_jar(NewLaunch ${SRC}) add_jar(NewLaunch ${SRC})

View File

@ -102,7 +102,6 @@ public class EntryPoint
} }
m_params.add(command, param); m_params.add(command, param);
//System.out.println(command + " : " + param);
return Action.Proceed; return Action.Proceed;
} }

View File

@ -1,132 +0,0 @@
package org.multimc;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
/*****************************************************************************
* A convenience class for loading icons from images.
*
* Icons loaded from this class are formatted to fit within the required
* dimension (16x16, 32x32, or 128x128). If the source image is larger than the
* target dimension, it is shrunk down to the minimum size that will fit. If it
* is smaller, then it is only scaled up if the new scale can be a per-pixel
* linear scale (i.e., x2, x3, x4, etc). In both cases, the image's width/height
* ratio is kept the same as the source image.
*
* @author Chris Molini
*****************************************************************************/
public class IconLoader
{
/*************************************************************************
* Loads an icon in ByteBuffer form.
*
* @param filepath
* The location of the Image to use as an icon.
*
* @return An array of ByteBuffers containing the pixel data for the icon in
* various sizes (as recommended by the OS).
*************************************************************************/
public static ByteBuffer[] load(String filepath)
{
BufferedImage image;
try {
image = ImageIO.read ( new File( filepath ) );
} catch ( IOException e ) {
e.printStackTrace();
return new ByteBuffer[0];
}
ByteBuffer[] buffers;
buffers = new ByteBuffer[1];
buffers[0] = loadInstance(image, 128);
return buffers;
}
/*************************************************************************
* Copies the supplied image into a square icon at the indicated size.
*
* @param image
* The image to place onto the icon.
* @param dimension
* The desired size of the icon.
*
* @return A ByteBuffer of pixel data at the indicated size.
*************************************************************************/
private static ByteBuffer loadInstance(BufferedImage image, int dimension)
{
BufferedImage scaledIcon = new BufferedImage(dimension, dimension,
BufferedImage.TYPE_INT_ARGB_PRE);
Graphics2D g = scaledIcon.createGraphics();
double ratio = getIconRatio(image, scaledIcon);
double width = image.getWidth() * ratio;
double height = image.getHeight() * ratio;
g.drawImage(image, (int) ((scaledIcon.getWidth() - width) / 2),
(int) ((scaledIcon.getHeight() - height) / 2), (int) (width),
(int) (height), null);
g.dispose();
return convertToByteBuffer(scaledIcon);
}
/*************************************************************************
* Gets the width/height ratio of the icon. This is meant to simplify
* scaling the icon to a new dimension.
*
* @param src
* The base image that will be placed onto the icon.
* @param icon
* The icon that will have the image placed on it.
*
* @return The amount to scale the source image to fit it onto the icon
* appropriately.
*************************************************************************/
private static double getIconRatio(BufferedImage src, BufferedImage icon)
{
double ratio = 1;
if (src.getWidth() > icon.getWidth())
ratio = (double) (icon.getWidth()) / src.getWidth();
else
ratio = (int) (icon.getWidth() / src.getWidth());
if (src.getHeight() > icon.getHeight())
{
double r2 = (double) (icon.getHeight()) / src.getHeight();
if (r2 < ratio)
ratio = r2;
}
else
{
double r2 = (int) (icon.getHeight() / src.getHeight());
if (r2 < ratio)
ratio = r2;
}
return ratio;
}
/*************************************************************************
* Converts a BufferedImage into a ByteBuffer of pixel data.
*
* @param image
* The image to convert.
*
* @return A ByteBuffer that contains the pixel data of the supplied image.
*************************************************************************/
public static ByteBuffer convertToByteBuffer(BufferedImage image)
{
byte[] buffer = new byte[image.getWidth() * image.getHeight() * 4];
int counter = 0;
for (int i = 0; i < image.getHeight(); i++)
for (int j = 0; j < image.getWidth(); j++)
{
int colorSpace = image.getRGB(j, i);
buffer[counter + 0] = (byte) ((colorSpace << 8) >> 24);
buffer[counter + 1] = (byte) ((colorSpace << 16) >> 24);
buffer[counter + 2] = (byte) ((colorSpace << 24) >> 24);
buffer[counter + 3] = (byte) (colorSpace >> 24);
counter += 4;
}
return ByteBuffer.wrap(buffer);
}
}

View File

@ -34,110 +34,6 @@ import java.util.zip.ZipFile;
public class Utils public class Utils
{ {
/**
* Combine two parts of a path.
*
* @param path1
* @param path2
* @return the paths, combined
*/
public static String combine(String path1, String path2)
{
File file1 = new File(path1);
File file2 = new File(file1, path2);
return file2.getPath();
}
/**
* Join a list of strings into a string using a separator!
*
* @param strings the string list to join
* @param separator the glue
* @return the result.
*/
public static String join(List<String> strings, String separator)
{
StringBuilder sb = new StringBuilder();
String sep = "";
for (String s : strings)
{
sb.append(sep).append(s);
sep = separator;
}
return sb.toString();
}
/**
* Adds the specified library to the classpath
*
* @param s the path to add
* @throws Exception
*/
public static void addToClassPath(String s) throws Exception
{
File f = new File(s);
URL u = f.toURI().toURL();
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class urlClass = URLClassLoader.class;
Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});
method.setAccessible(true);
method.invoke(urlClassLoader, new Object[]{u});
}
/**
* Adds many libraries to the classpath
*
* @param jars the paths to add
*/
public static boolean addToClassPath(List<String> jars)
{
boolean pure = true;
// initialize the class path
for (String jar : jars)
{
try
{
Utils.addToClassPath(jar);
} catch (Exception e)
{
System.err.println("Unable to load: " + jar);
e.printStackTrace(System.err);
pure = false;
}
}
return pure;
}
/**
* Adds the specified path to the java library path
*
* @param pathToAdd the path to add
* @throws Exception
*/
@Deprecated
public static void addLibraryPath(String pathToAdd) throws Exception
{
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
usrPathsField.setAccessible(true);
//get array of paths
final String[] paths = (String[]) usrPathsField.get(null);
//check if the path to add is already present
for (String path : paths)
{
if (path.equals(pathToAdd))
{
return;
}
}
//add the new path
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
newPaths[newPaths.length - 1] = pathToAdd;
usrPathsField.set(null, newPaths);
}
/** /**
* Finds a field that looks like a Minecraft base folder in a supplied class * Finds a field that looks like a Minecraft base folder in a supplied class
* *

View File

@ -0,0 +1,42 @@
package org.multimc.onesix;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.List;
public class MMCClassLoader extends URLClassLoader
{
public MMCClassLoader(String natives, List<String> allJars)
throws MalformedURLException, ClassNotFoundException, NoSuchMethodException,
InvocationTargetException, IllegalAccessException, NoSuchFieldException
{
super(process(allJars));
Method setProperty = loadClass("java.lang.System").getMethod("setProperty", String.class, String.class);
setProperty.invoke(null, "java.library.path", natives);
setProperty.invoke(null, "org.lwjgl.librarypath", natives);
setProperty.invoke(null, "net.java.games.input.librarypath", natives);
}
private static URL[] process(List<String> allJars) throws MalformedURLException
{
URL[] urls = new URL[allJars.size()];
for (int i = 0; i < allJars.size(); i++)
{
String jar = allJars.get(i);
urls[i] = new File(jar).toURI().toURL();
}
return urls;
}
// TODO: use this method to use custom log configs
// @Override
// public URL findResource(String name)
// {
// return super.findResource(name);
// }
}

View File

@ -18,8 +18,8 @@ package org.multimc.onesix;
import org.multimc.*; import org.multimc.*;
import java.applet.Applet; import java.applet.Applet;
import java.io.File;
import java.awt.*; import java.awt.*;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@ -49,7 +49,7 @@ public class OneSixLauncher implements Launcher
private String cwd; private String cwd;
// the much abused system classloader, for convenience (for further abuse) // the much abused system classloader, for convenience (for further abuse)
private ClassLoader cl; private MMCClassLoader cl;
private void processParams(ParamBucket params) throws NotFoundException private void processParams(ParamBucket params) throws NotFoundException
{ {
@ -84,7 +84,10 @@ public class OneSixLauncher implements Launcher
try try
{ {
winSize = new Dimension(Integer.parseInt(dimStrings[0]), Integer.parseInt(dimStrings[1])); winSize = new Dimension(Integer.parseInt(dimStrings[0]), Integer.parseInt(dimStrings[1]));
} catch (NumberFormatException ignored) {} }
catch (NumberFormatException ignored)
{
}
} }
} }
@ -151,9 +154,13 @@ public class OneSixLauncher implements Launcher
Utils.log(" " + mcparams.toString()); Utils.log(" " + mcparams.toString());
Utils.log(); Utils.log();
if (maximize) if (maximize)
{
Utils.log("Window size: max (if available)"); Utils.log("Window size: max (if available)");
}
else else
{
Utils.log("Window size: " + Integer.toString(winSize.width) + " x " + Integer.toString(winSize.height)); Utils.log("Window size: " + Integer.toString(winSize.width) + " x " + Integer.toString(winSize.height));
}
Utils.log(); Utils.log();
} }
@ -176,7 +183,8 @@ public class OneSixLauncher implements Launcher
f.setAccessible(true); f.setAccessible(true);
f.set(null, new File(cwd)); f.set(null, new File(cwd));
} }
} catch (Exception e) }
catch (Exception e)
{ {
System.err.println("Could not set base folder. Failed to find/access Minecraft main class:"); System.err.println("Could not set base folder. Failed to find/access Minecraft main class:");
e.printStackTrace(System.err); e.printStackTrace(System.err);
@ -196,7 +204,8 @@ public class OneSixLauncher implements Launcher
Applet mcappl = (Applet)MCAppletClass.newInstance(); Applet mcappl = (Applet)MCAppletClass.newInstance();
LegacyFrame mcWindow = new LegacyFrame(windowTitle); LegacyFrame mcWindow = new LegacyFrame(windowTitle);
mcWindow.start(mcappl, userName, sessionId, winSize, maximize); mcWindow.start(mcappl, userName, sessionId, winSize, maximize);
} catch (Exception e) }
catch (Exception e)
{ {
Utils.log("Applet wrapper failed:", "Error"); Utils.log("Applet wrapper failed:", "Error");
e.printStackTrace(System.err); e.printStackTrace(System.err);
@ -205,7 +214,8 @@ public class OneSixLauncher implements Launcher
try try
{ {
mc.getMethod("main", String[].class).invoke(null, (Object)mcArgs); mc.getMethod("main", String[].class).invoke(null, (Object)mcArgs);
} catch (Exception e1) }
catch (Exception e1)
{ {
Utils.log("Failed to invoke the Minecraft main class:", "Fatal"); Utils.log("Failed to invoke the Minecraft main class:", "Fatal");
e1.printStackTrace(System.err); e1.printStackTrace(System.err);
@ -237,7 +247,8 @@ public class OneSixLauncher implements Launcher
try try
{ {
mc = cl.loadClass(mainClass); mc = cl.loadClass(mainClass);
} catch (ClassNotFoundException e) }
catch (ClassNotFoundException e)
{ {
System.err.println("Failed to find Minecraft main class:"); System.err.println("Failed to find Minecraft main class:");
e.printStackTrace(System.err); e.printStackTrace(System.err);
@ -249,66 +260,22 @@ public class OneSixLauncher implements Launcher
try try
{ {
meth = mc.getMethod("main", String[].class); meth = mc.getMethod("main", String[].class);
} catch (NoSuchMethodException e) }
catch (NoSuchMethodException e)
{ {
System.err.println("Failed to acquire the main method:"); System.err.println("Failed to acquire the main method:");
e.printStackTrace(System.err); e.printStackTrace(System.err);
return -1; return -1;
} }
/*
final java.nio.ByteBuffer[] icons = IconLoader.load("icon.png");
new Thread() {
public void run() {
ClassLoader cl = ClassLoader.getSystemClassLoader();
try
{
Class<?> Display;
Method isCreated;
Method setTitle;
Method setIcon;
Field fieldWindowCreated;
Boolean created = false;
Display = cl.loadClass("org.lwjgl.opengl.Display");
fieldWindowCreated = Display.getDeclaredField("window_created");
fieldWindowCreated.setAccessible( true );
setTitle = Display.getMethod("setTitle", String.class);
setIcon = Display.getMethod("setIcon", java.nio.ByteBuffer[].class);
created = (Boolean) fieldWindowCreated.get( null );
// set the window title? Maybe?
while(!created)
{
try
{
Thread.sleep(150);
created = (Boolean) fieldWindowCreated.get( null );
} catch (InterruptedException ignored) {}
}
// Give it a bit more time ;)
Thread.sleep(150);
// set the title
setTitle.invoke(null,windowTitle);
// only set icon when there's actually something to set...
if(icons.length > 0)
{
setIcon.invoke(null,(Object)icons);
}
}
catch (Exception e)
{
System.err.println("Couldn't set window icon or title.");
e.printStackTrace(System.err);
}
}
}
.start();
*/
// init params for the main method to chomp on. // init params for the main method to chomp on.
String[] paramsArray = mcparams.toArray(new String[mcparams.size()]); String[] paramsArray = mcparams.toArray(new String[mcparams.size()]);
try try
{ {
// static method doesn't have an instance // static method doesn't have an instance
meth.invoke(null, (Object)paramsArray); meth.invoke(null, (Object)paramsArray);
} catch (Exception e) }
catch (Exception e)
{ {
System.err.println("Failed to start Minecraft:"); System.err.println("Failed to start Minecraft:");
e.printStackTrace(System.err); e.printStackTrace(System.err);
@ -317,34 +284,28 @@ public class OneSixLauncher implements Launcher
return 0; return 0;
} }
@Override @Override public int launch(ParamBucket params)
public int launch(ParamBucket params)
{ {
// get and process the launch script params // get and process the launch script params
try try
{ {
processParams(params); processParams(params);
} catch (NotFoundException e) }
catch (NotFoundException e)
{ {
System.err.println("Not enough arguments."); System.err.println("Not enough arguments.");
e.printStackTrace(System.err); e.printStackTrace(System.err);
return -1; return -1;
} }
// add libraries to classpath
if(!Utils.addToClassPath(libraries))
{
System.err.println("Halting launch due to previous errors.");
return -1;
}
// print the pretty things // print the pretty things
printStats(); printStats();
// extract native libs (depending on platform here... java!) // extract native libs (depending on platform here... java!)
Utils.log("Preparing native libraries..."); Utils.log("Preparing native libraries...");
String property = System.getProperty("os.arch"); String property = System.getProperty("os.arch");
boolean is_64 = property.equalsIgnoreCase("x86_64") || property.equalsIgnoreCase("amd64"); boolean is_64 =
property.equalsIgnoreCase("x86_64") || property.equalsIgnoreCase("amd64");
for (String extlib : extlibs) for (String extlib : extlibs)
{ {
try try
@ -353,7 +314,8 @@ public class OneSixLauncher implements Launcher
File cleanlibf = new File(cleanlib); File cleanlibf = new File(cleanlib);
Utils.log("Extracting " + cleanlibf.getName()); Utils.log("Extracting " + cleanlibf.getName());
Utils.unzipNatives(cleanlibf, new File(natives)); Utils.unzipNatives(cleanlibf, new File(natives));
} catch (IOException e) }
catch (IOException e)
{ {
System.err.println("Failed to extract native library:"); System.err.println("Failed to extract native library:");
e.printStackTrace(System.err); e.printStackTrace(System.err);
@ -362,36 +324,44 @@ public class OneSixLauncher implements Launcher
} }
Utils.log(); Utils.log();
// set the native libs path... the brute force way
try try
{ {
System.setProperty("java.library.path", natives); cl = new MMCClassLoader(natives, libraries);
System.setProperty("org.lwjgl.librarypath", natives); }
System.setProperty("net.java.games.input.librarypath", natives); catch (Exception e)
// by the power of reflection, initialize native libs again. DIRTY!
// this is SO BAD. imagine doing that to ld
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null );
} catch (Exception e)
{ {
System.err.println("Failed to set the native library path:"); e.printStackTrace();
e.printStackTrace(System.err);
return -1;
} }
// grab the system classloader and ... final int[] result = {-1};
cl = ClassLoader.getSystemClassLoader();
// fix log4j by sticking it in a thread with custom contextclassloader
Thread t = new Thread("main")
{
@Override public void run()
{
if (traits.contains("legacyLaunch") || traits.contains("alphaLaunch")) if (traits.contains("legacyLaunch") || traits.contains("alphaLaunch"))
{ {
// legacy launch uses the applet wrapper // legacy launch uses the applet wrapper
return legacyLaunch(); result[0] = legacyLaunch();
} }
else else
{ {
// normal launch just calls main() // normal launch just calls main()
return launchWithMainClass(); result[0] = launchWithMainClass();
} }
} }
};
t.setContextClassLoader(cl);
t.start();
try
{
t.join();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
return result[0];
}
} }