001/* 002 * Forge Mod Loader 003 * Copyright (c) 2012-2013 cpw. 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser Public License v2.1 006 * which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 008 * 009 * Contributors: 010 * cpw - implementation 011 */ 012 013package cpw.mods.fml.relauncher; 014 015import java.applet.Applet; 016import java.io.File; 017import java.lang.reflect.Method; 018import java.net.URLClassLoader; 019 020import javax.swing.JDialog; 021import javax.swing.JOptionPane; 022 023public class FMLRelauncher 024{ 025 private static FMLRelauncher INSTANCE; 026 public static String logFileNamePattern; 027 private static String side; 028 private RelaunchClassLoader classLoader; 029 private Object newApplet; 030 private Class<? super Object> appletClass; 031 032 JDialog popupWindow; 033 034 public static void handleClientRelaunch(ArgsWrapper wrap) 035 { 036 logFileNamePattern = "ForgeModLoader-client-%g.log"; 037 side = "CLIENT"; 038 instance().relaunchClient(wrap); 039 } 040 041 public static void handleServerRelaunch(ArgsWrapper wrap) 042 { 043 logFileNamePattern = "ForgeModLoader-server-%g.log"; 044 side = "SERVER"; 045 instance().relaunchServer(wrap); 046 } 047 048 static FMLRelauncher instance() 049 { 050 if (INSTANCE == null) 051 { 052 INSTANCE = new FMLRelauncher(); 053 } 054 return INSTANCE; 055 056 } 057 058 private FMLRelauncher() 059 { 060 URLClassLoader ucl = (URLClassLoader) getClass().getClassLoader(); 061 062 classLoader = new RelaunchClassLoader(ucl.getURLs()); 063 064 } 065 066 private void showWindow(boolean showIt) 067 { 068 if (RelaunchLibraryManager.downloadMonitor != null) { return; } 069 try 070 { 071 if (showIt) 072 { 073 RelaunchLibraryManager.downloadMonitor = new Downloader(); 074 popupWindow = (JDialog) RelaunchLibraryManager.downloadMonitor.makeDialog(); 075 } 076 else 077 { 078 RelaunchLibraryManager.downloadMonitor = new DummyDownloader(); 079 } 080 } 081 catch (Throwable e) 082 { 083 if (RelaunchLibraryManager.downloadMonitor == null) 084 { 085 RelaunchLibraryManager.downloadMonitor = new DummyDownloader(); 086 e.printStackTrace(); 087 } 088 else 089 { 090 RelaunchLibraryManager.downloadMonitor.makeHeadless(); 091 } 092 popupWindow = null; 093 } 094 } 095 096 private void relaunchClient(ArgsWrapper wrap) 097 { 098 showWindow(true); 099 // Now we re-inject the home into the "new" minecraft under our control 100 Class<? super Object> client; 101 try 102 { 103 File minecraftHome = computeExistingClientHome(); 104 setupHome(minecraftHome); 105 106 client = setupNewClientHome(minecraftHome); 107 } 108 finally 109 { 110 if (popupWindow != null) 111 { 112 popupWindow.setVisible(false); 113 popupWindow.dispose(); 114 } 115 } 116 117 if (RelaunchLibraryManager.downloadMonitor.shouldStopIt()) 118 { 119 System.exit(1); 120 } 121 try 122 { 123 ReflectionHelper.findMethod(client, null, new String[] { "fmlReentry" }, ArgsWrapper.class).invoke(null, wrap); 124 } 125 catch (Exception e) 126 { 127 e.printStackTrace(); 128 // Hmmm 129 } 130 } 131 132 private Class<? super Object> setupNewClientHome(File minecraftHome) 133 { 134 Class<? super Object> client = ReflectionHelper.getClass(classLoader, "net.minecraft.client.Minecraft"); 135 ReflectionHelper.setPrivateValue(client, null, minecraftHome, 136 "field_" + "71463_am" /*Separate that so that MCP's updatenames does not replace it*/, 137 "an", "minecraftDir"); 138 return client; 139 } 140 141 private void relaunchServer(ArgsWrapper wrap) 142 { 143 showWindow(false); 144 // Now we re-inject the home into the "new" minecraft under our control 145 Class<? super Object> server; 146 File minecraftHome = new File("."); 147 setupHome(minecraftHome); 148 149 server = ReflectionHelper.getClass(classLoader, "net.minecraft.server.MinecraftServer"); 150 try 151 { 152 ReflectionHelper.findMethod(server, null, new String[] { "fmlReentry" }, ArgsWrapper.class).invoke(null, wrap); 153 } 154 catch (Exception e) 155 { 156 e.printStackTrace(); 157 } 158 } 159 160 private void setupHome(File minecraftHome) 161 { 162 FMLInjectionData.build(minecraftHome, classLoader); 163 FMLRelaunchLog.minecraftHome = minecraftHome; 164 FMLRelaunchLog.info("Forge Mod Loader version %s.%s.%s.%s for Minecraft %s loading", FMLInjectionData.major, FMLInjectionData.minor, 165 FMLInjectionData.rev, FMLInjectionData.build, FMLInjectionData.mccversion, FMLInjectionData.mcpversion); 166 167 try 168 { 169 RelaunchLibraryManager.handleLaunch(minecraftHome, classLoader); 170 } 171 catch (Throwable t) 172 { 173 if (popupWindow != null) 174 { 175 try 176 { 177 String logFile = new File(minecraftHome, "ForgeModLoader-client-0.log").getCanonicalPath(); 178 JOptionPane.showMessageDialog(popupWindow, String.format( 179 "<html><div align=\"center\"><font size=\"+1\">There was a fatal error starting up minecraft and FML</font></div><br/>" 180 + "Minecraft cannot launch in it's current configuration<br/>" 181 + "Please consult the file <i><a href=\"file:///%s\">%s</a></i> for further information</html>", logFile, logFile), 182 "Fatal FML error", JOptionPane.ERROR_MESSAGE); 183 } 184 catch (Exception ex) 185 { 186 // ah well, we tried 187 } 188 } 189 throw new RuntimeException(t); 190 } 191 } 192 193 /** 194 * @return the location of the client home 195 */ 196 private File computeExistingClientHome() 197 { 198 Class<? super Object> mcMaster = ReflectionHelper.getClass(getClass().getClassLoader(), "net.minecraft.client.Minecraft"); 199 // If we get the system property we inject into the old MC, setup the 200 // dir, then pull the value 201 String str = System.getProperty("minecraft.applet.TargetDirectory"); 202 if (str != null) 203 { 204 str = str.replace('/', File.separatorChar); 205 ReflectionHelper.setPrivateValue(mcMaster, null, new File(str), "minecraftDir", "an", "minecraftDir"); 206 } 207 // We force minecraft to setup it's homedir very early on so we can 208 // inject stuff into it 209 Method setupHome = ReflectionHelper.findMethod(mcMaster, null, new String[] { "getMinecraftDir", "getMinecraftDir", "b" }); 210 try 211 { 212 setupHome.invoke(null); 213 } 214 catch (Exception e) 215 { 216 // Hmmm 217 } 218 File minecraftHome = ReflectionHelper.getPrivateValue(mcMaster, null, "minecraftDir", "an", "minecraftDir"); 219 return minecraftHome; 220 } 221 222 public static void appletEntry(Applet minecraftApplet) 223 { 224 side = "CLIENT"; 225 logFileNamePattern = "ForgeModLoader-client-%g.log"; 226 instance().relaunchApplet(minecraftApplet); 227 } 228 229 private void relaunchApplet(Applet minecraftApplet) 230 { 231 showWindow(true); 232 233 if (minecraftApplet.getClass().getClassLoader() == classLoader) 234 { 235 if (popupWindow != null) 236 { 237 popupWindow.setVisible(false); 238 popupWindow.dispose(); 239 } 240 try 241 { 242 newApplet = minecraftApplet; 243 appletClass = ReflectionHelper.getClass(classLoader, "net.minecraft.client.MinecraftApplet"); 244 ReflectionHelper.findMethod(appletClass, newApplet, new String[] { "fmlInitReentry" }).invoke(newApplet); 245 return; 246 } 247 catch (Exception e) 248 { 249 System.out.println("FMLRelauncher.relaunchApplet"); 250 e.printStackTrace(); 251 throw new RuntimeException(e); 252 } 253 } 254 255 File mcDir = computeExistingClientHome(); 256 setupHome(mcDir); 257 setupNewClientHome(mcDir); 258 259 Class<? super Object> parentAppletClass = ReflectionHelper.getClass(getClass().getClassLoader(), "java.applet.Applet"); 260 261 try 262 { 263 appletClass = ReflectionHelper.getClass(classLoader, "net.minecraft.client.MinecraftApplet"); 264 newApplet = appletClass.newInstance(); 265 Object appletContainer = ReflectionHelper.getPrivateValue(ReflectionHelper.getClass(getClass().getClassLoader(), "java.awt.Component"), 266 minecraftApplet, "parent"); 267 268 String launcherClassName = System.getProperty("minecraft.applet.WrapperClass", "net.minecraft.Launcher"); 269 Class<? super Object> launcherClass = ReflectionHelper.getClass(getClass().getClassLoader(), launcherClassName); 270 if (launcherClass.isInstance(appletContainer)) 271 { 272 ReflectionHelper.findMethod(ReflectionHelper.getClass(getClass().getClassLoader(), "java.awt.Container"), minecraftApplet, 273 new String[] { "removeAll" }).invoke(appletContainer); 274 ReflectionHelper.findMethod(launcherClass, appletContainer, new String[] { "replace" }, parentAppletClass).invoke(appletContainer, newApplet); 275 } 276 else 277 { 278 FMLRelaunchLog.severe("Found unknown applet parent %s, unable to inject!\n", appletContainer.getClass().getName()); 279 throw new RuntimeException(); 280 } 281 } 282 catch (Exception e) 283 { 284 throw new RuntimeException(e); 285 } 286 finally 287 { 288 if (popupWindow != null) 289 { 290 popupWindow.setVisible(false); 291 popupWindow.dispose(); 292 } 293 } 294 } 295 296 public static void appletStart(Applet applet) 297 { 298 instance().startApplet(applet); 299 } 300 301 private void startApplet(Applet applet) 302 { 303 if (applet.getClass().getClassLoader() == classLoader) 304 { 305 if (popupWindow != null) 306 { 307 popupWindow.setVisible(false); 308 popupWindow.dispose(); 309 } 310 if (RelaunchLibraryManager.downloadMonitor.shouldStopIt()) 311 { 312 System.exit(1); 313 } 314 try 315 { 316 ReflectionHelper.findMethod(appletClass, newApplet, new String[] { "fmlStartReentry" }).invoke(newApplet); 317 } 318 catch (Exception e) 319 { 320 System.out.println("FMLRelauncher.startApplet"); 321 e.printStackTrace(); 322 throw new RuntimeException(e); 323 } 324 } 325 return; 326 } 327 328 public static String side() 329 { 330 return side; 331 } 332}