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