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