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                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    }