    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 the location of the client home
    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}