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    }