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