package cpw.mods.fml.relauncher; import java.applet.Applet; import java.io.File; import java.lang.reflect.Method; import java.net.URLClassLoader; import javax.swing.JDialog; import javax.swing.JOptionPane; public class FMLRelauncher { private static FMLRelauncher INSTANCE; public static String logFileNamePattern; private static String side; private RelaunchClassLoader classLoader; private Object newApplet; private Class<? super Object> appletClass; JDialog popupWindow; public static void handleClientRelaunch(ArgsWrapper wrap) { logFileNamePattern = "ForgeModLoader-client-%g.log"; side = "CLIENT"; instance().relaunchClient(wrap); } public static void handleServerRelaunch(ArgsWrapper wrap) { logFileNamePattern = "ForgeModLoader-server-%g.log"; side = "SERVER"; instance().relaunchServer(wrap); } static FMLRelauncher instance() { if (INSTANCE == null) { INSTANCE = new FMLRelauncher(); } return INSTANCE; } private FMLRelauncher() { URLClassLoader ucl = (URLClassLoader) getClass().getClassLoader(); classLoader = new RelaunchClassLoader(ucl.getURLs()); } private void showWindow(boolean showIt) { if (RelaunchLibraryManager.downloadMonitor != null) { return; } try { if (showIt) { RelaunchLibraryManager.downloadMonitor = new Downloader(); popupWindow = (JDialog) RelaunchLibraryManager.downloadMonitor.makeDialog(); } else { RelaunchLibraryManager.downloadMonitor = new DummyDownloader(); } } catch (Throwable e) { if (RelaunchLibraryManager.downloadMonitor == null) { RelaunchLibraryManager.downloadMonitor = new DummyDownloader(); e.printStackTrace(); } else { RelaunchLibraryManager.downloadMonitor.makeHeadless(); } popupWindow = null; } } private void relaunchClient(ArgsWrapper wrap) { showWindow(true); // Now we re-inject the home into the "new" minecraft under our control Class<? super Object> client; try { File minecraftHome = computeExistingClientHome(); setupHome(minecraftHome); client = setupNewClientHome(minecraftHome); } finally { if (popupWindow != null) { popupWindow.setVisible(false); popupWindow.dispose(); } } if (RelaunchLibraryManager.downloadMonitor.shouldStopIt()) { System.exit(1); } try { ReflectionHelper.findMethod(client, null, new String[] { "fmlReentry" }, ArgsWrapper.class).invoke(null, wrap); } catch (Exception e) { e.printStackTrace(); // Hmmm } } private Class<? super Object> setupNewClientHome(File minecraftHome) { Class<? super Object> client = ReflectionHelper.getClass(classLoader, "net.minecraft.client.Minecraft"); ReflectionHelper.setPrivateValue(client, null, minecraftHome, "minecraftDir", "an", "minecraftDir"); return client; } private void relaunchServer(ArgsWrapper wrap) { showWindow(false); // Now we re-inject the home into the "new" minecraft under our control Class<? super Object> server; File minecraftHome = new File("."); setupHome(minecraftHome); server = ReflectionHelper.getClass(classLoader, "net.minecraft.server.MinecraftServer"); try { ReflectionHelper.findMethod(server, null, new String[] { "fmlReentry" }, ArgsWrapper.class).invoke(null, wrap); } catch (Exception e) { e.printStackTrace(); } } private void setupHome(File minecraftHome) { FMLInjectionData.build(minecraftHome, classLoader); FMLRelaunchLog.minecraftHome = minecraftHome; FMLRelaunchLog.info("Forge Mod Loader version %s.%s.%s.%s for Minecraft %s loading", FMLInjectionData.major, FMLInjectionData.minor, FMLInjectionData.rev, FMLInjectionData.build, FMLInjectionData.mccversion, FMLInjectionData.mcpversion); try { RelaunchLibraryManager.handleLaunch(minecraftHome, classLoader); } catch (Throwable t) { if (popupWindow != null) { try { String logFile = new File(minecraftHome, "ForgeModLoader-client-0.log").getCanonicalPath(); JOptionPane.showMessageDialog(popupWindow, String.format( "<html><div align=\"center\"><font size=\"+1\">There was a fatal error starting up minecraft and FML</font></div><br/>" + "Minecraft cannot launch in it's current configuration<br/>" + "Please consult the file <i><a href=\"file:///%s\">%s</a></i> for further information</html>", logFile, logFile), "Fatal FML error", JOptionPane.ERROR_MESSAGE); } catch (Exception ex) { // ah well, we tried } } throw new RuntimeException(t); } } /** * @return */ private File computeExistingClientHome() { Class<? super Object> mcMaster = ReflectionHelper.getClass(getClass().getClassLoader(), "net.minecraft.client.Minecraft"); // If we get the system property we inject into the old MC, setup the // dir, then pull the value String str = System.getProperty("minecraft.applet.TargetDirectory"); if (str != null) { str = str.replace('/', File.separatorChar); ReflectionHelper.setPrivateValue(mcMaster, null, new File(str), "minecraftDir", "an", "minecraftDir"); } // 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[] { "getMinecraftDir", "getMinecraftDir", "b" }); try { setupHome.invoke(null); } catch (Exception e) { // Hmmm } File minecraftHome = ReflectionHelper.getPrivateValue(mcMaster, null, "minecraftDir", "an", "minecraftDir"); return minecraftHome; } public static void appletEntry(Applet minecraftApplet) { side = "CLIENT"; logFileNamePattern = "ForgeModLoader-client-%g.log"; instance().relaunchApplet(minecraftApplet); } private void relaunchApplet(Applet minecraftApplet) { showWindow(true); appletClass = ReflectionHelper.getClass(classLoader, "net.minecraft.client.MinecraftApplet"); if (minecraftApplet.getClass().getClassLoader() == classLoader) { if (popupWindow != null) { popupWindow.setVisible(false); popupWindow.dispose(); } try { newApplet = minecraftApplet; ReflectionHelper.findMethod(appletClass, newApplet, new String[] { "fmlInitReentry" }).invoke(newApplet); return; } catch (Exception e) { System.out.println("FMLRelauncher.relaunchApplet"); e.printStackTrace(); throw new RuntimeException(e); } } File mcDir = computeExistingClientHome(); setupHome(mcDir); setupNewClientHome(mcDir); Class<? super Object> parentAppletClass = ReflectionHelper.getClass(getClass().getClassLoader(), "java.applet.Applet"); try { newApplet = appletClass.newInstance(); Object appletContainer = ReflectionHelper.getPrivateValue(ReflectionHelper.getClass(getClass().getClassLoader(), "java.awt.Component"), minecraftApplet, "parent"); String launcherClassName = System.getProperty("minecraft.applet.WrapperClass", "net.minecraft.Launcher"); Class<? super Object> launcherClass = ReflectionHelper.getClass(getClass().getClassLoader(), launcherClassName); if (launcherClass.isInstance(appletContainer)) { ReflectionHelper.findMethod(ReflectionHelper.getClass(getClass().getClassLoader(), "java.awt.Container"), minecraftApplet, new String[] { "removeAll" }).invoke(appletContainer); ReflectionHelper.findMethod(launcherClass, appletContainer, new String[] { "replace" }, parentAppletClass).invoke(appletContainer, newApplet); } else { FMLRelaunchLog.severe("Found unknown applet parent %s, unable to inject!\n", appletContainer.getClass().getName()); throw new RuntimeException(); } } catch (Exception e) { throw new RuntimeException(e); } finally { if (popupWindow != null) { popupWindow.setVisible(false); popupWindow.dispose(); } } } public static void appletStart(Applet applet) { instance().startApplet(applet); } private void startApplet(Applet applet) { if (applet.getClass().getClassLoader() == classLoader) { if (popupWindow != null) { popupWindow.setVisible(false); popupWindow.dispose(); } if (RelaunchLibraryManager.downloadMonitor.shouldStopIt()) { System.exit(1); } try { ReflectionHelper.findMethod(appletClass, newApplet, new String[] { "fmlStartReentry" }).invoke(newApplet); } catch (Exception e) { System.out.println("FMLRelauncher.startApplet"); e.printStackTrace(); throw new RuntimeException(e); } } return; } public static String side() { return side; } }