diff --git a/fml/client/cpw/mods/fml/client/CustomModLoadingErrorDisplayException.java b/fml/client/cpw/mods/fml/client/CustomModLoadingErrorDisplayException.java new file mode 100644 index 000000000..cbc5f6e16 --- /dev/null +++ b/fml/client/cpw/mods/fml/client/CustomModLoadingErrorDisplayException.java @@ -0,0 +1,43 @@ +package cpw.mods.fml.client; + +import cpw.mods.fml.common.IFMLHandledException; +import cpw.mods.fml.common.Side; +import cpw.mods.fml.common.asm.SideOnly; +import net.minecraft.src.FontRenderer; +import net.minecraft.src.GuiErrorScreen; + +/** + * If a mod throws this exception during loading, it will be called back to render + * the error screen through the methods below. This error will not be cleared, and will + * not allow the game to carry on, but might be useful if your mod wishes to report + * a fatal configuration error in a pretty way. + * + * Throw this through a proxy. It won't work on the dedicated server environment. + * @author cpw + * + */ +@SideOnly(Side.CLIENT) +public abstract class CustomModLoadingErrorDisplayException extends RuntimeException implements IFMLHandledException +{ + /** + * Called after the GUI is inited by the parent code. You can do extra stuff here, maybe? + * + * @param errorScreen The error screen we're painting + * @param fontRenderer A font renderer for you + */ + public abstract void initGui(GuiErrorScreen errorScreen, FontRenderer fontRenderer); + + /** + * Draw your error to the screen. + * + *
Warning: Minecraft is in a deep error state. All it can do is stop. + * Do not try and do anything involving complex user interaction here. + * + * @param errorScreen The error screen to draw to + * @param fontRenderer A font renderer for you + * @param mouseRelX Mouse X + * @param mouseRelY Mouse Y + * @param tickTime tick time + */ + public abstract void drawScreen(GuiErrorScreen errorScreen, FontRenderer fontRenderer, int mouseRelX, int mouseRelY, float tickTime); +} diff --git a/fml/client/cpw/mods/fml/client/FMLClientHandler.java b/fml/client/cpw/mods/fml/client/FMLClientHandler.java index 678c7b979..6e67d31c0 100644 --- a/fml/client/cpw/mods/fml/client/FMLClientHandler.java +++ b/fml/client/cpw/mods/fml/client/FMLClientHandler.java @@ -107,6 +107,8 @@ public class FMLClientHandler implements IFMLSidedHandler private WrongMinecraftVersionException wrongMC; + private CustomModLoadingErrorDisplayException customError; + /** * Called to start the whole game off from * {@link MinecraftServer#startServer} @@ -153,6 +155,11 @@ public class FMLClientHandler implements IFMLSidedHandler { modsMissing = missing; } + catch (CustomModLoadingErrorDisplayException custom) + { + FMLLog.log(Level.SEVERE, custom, "A custom exception was thrown by a mod, the game will now halt"); + customError = custom; + } catch (LoaderException le) { haltGame("There was a severe problem during mod loading that has caused the game to fail", le); @@ -182,6 +189,12 @@ public class FMLClientHandler implements IFMLSidedHandler { Loader.instance().initializeMods(); } + catch (CustomModLoadingErrorDisplayException custom) + { + FMLLog.log(Level.SEVERE, custom, "A custom exception was thrown by a mod, the game will now halt"); + customError = custom; + return; + } catch (LoaderException le) { haltGame("There was a severe problem during mod loading that has caused the game to fail", le); @@ -204,6 +217,10 @@ public class FMLClientHandler implements IFMLSidedHandler { client.func_71373_a(new GuiModsMissing(modsMissing)); } + else if (customError != null) + { + client.func_71373_a(new GuiCustomModLoadingErrorScreen(customError)); + } else { TextureFXManager.instance().loadTextures(client.field_71418_C.func_77292_e()); diff --git a/fml/client/cpw/mods/fml/client/GuiCustomModLoadingErrorScreen.java b/fml/client/cpw/mods/fml/client/GuiCustomModLoadingErrorScreen.java new file mode 100644 index 000000000..6f6e60b8c --- /dev/null +++ b/fml/client/cpw/mods/fml/client/GuiCustomModLoadingErrorScreen.java @@ -0,0 +1,26 @@ +package cpw.mods.fml.client; + +import cpw.mods.fml.common.Loader; +import cpw.mods.fml.common.WrongMinecraftVersionException; +import net.minecraft.src.GuiErrorScreen; + +public class GuiCustomModLoadingErrorScreen extends GuiErrorScreen +{ + private CustomModLoadingErrorDisplayException customException; + public GuiCustomModLoadingErrorScreen(CustomModLoadingErrorDisplayException customException) + { + this.customException = customException; + } + @Override + public void func_73866_w_() + { + super.func_73866_w_(); + this.customException.initGui(this, field_73886_k); + } + @Override + public void func_73863_a(int p_73863_1_, int p_73863_2_, float p_73863_3_) + { + this.func_73873_v_(); + this.customException.drawScreen(this, field_73886_k, p_73863_1_, p_73863_2_, p_73863_3_); + } +} diff --git a/fml/client/cpw/mods/fml/client/GuiModsMissing.java b/fml/client/cpw/mods/fml/client/GuiModsMissing.java index 5398cf282..0383a25d0 100644 --- a/fml/client/cpw/mods/fml/client/GuiModsMissing.java +++ b/fml/client/cpw/mods/fml/client/GuiModsMissing.java @@ -23,7 +23,7 @@ public class GuiModsMissing extends GuiErrorScreen public void func_73863_a(int p_73863_1_, int p_73863_2_, float p_73863_3_) { this.func_73873_v_(); - int offset = 85 - modsMissing.missingMods.size() * 10; + int offset = Math.max(85 - modsMissing.missingMods.size() * 10, 10); this.func_73732_a(this.field_73886_k, "Forge Mod Loader has found a problem with your minecraft installation", this.field_73880_f / 2, offset, 0xFFFFFF); offset+=10; this.func_73732_a(this.field_73886_k, "The mods and versions listed below could not be found", this.field_73880_f / 2, offset, 0xFFFFFF); diff --git a/fml/client/cpw/mods/fml/client/GuiModsMissingForServer.java b/fml/client/cpw/mods/fml/client/GuiModsMissingForServer.java index 67f739870..14e297e15 100644 --- a/fml/client/cpw/mods/fml/client/GuiModsMissingForServer.java +++ b/fml/client/cpw/mods/fml/client/GuiModsMissingForServer.java @@ -35,7 +35,7 @@ public class GuiModsMissingForServer extends GuiScreen public void func_73863_a(int p_73863_1_, int p_73863_2_, float p_73863_3_) { this.func_73873_v_(); - int offset = 85 - modsMissing.getModList().size() * 10; + int offset = Math.max(85 - modsMissing.getModList().size() * 10, 10); this.func_73732_a(this.field_73886_k, "Forge Mod Loader could not connect to this server", this.field_73880_f / 2, offset, 0xFFFFFF); offset += 10; this.func_73732_a(this.field_73886_k, "The mods and versions listed below could not be found", this.field_73880_f / 2, offset, 0xFFFFFF); diff --git a/fml/common/cpw/mods/fml/common/IFMLHandledException.java b/fml/common/cpw/mods/fml/common/IFMLHandledException.java new file mode 100644 index 000000000..a9f52bf3e --- /dev/null +++ b/fml/common/cpw/mods/fml/common/IFMLHandledException.java @@ -0,0 +1,6 @@ +package cpw.mods.fml.common; + +public interface IFMLHandledException +{ + +} diff --git a/fml/common/cpw/mods/fml/common/LoadController.java b/fml/common/cpw/mods/fml/common/LoadController.java index ca4850929..c4dbb5132 100644 --- a/fml/common/cpw/mods/fml/common/LoadController.java +++ b/fml/common/cpw/mods/fml/common/LoadController.java @@ -1,5 +1,6 @@ package cpw.mods.fml.common; +import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -88,6 +89,7 @@ public class LoadController state = state.transition(!errors.isEmpty()); if (state != desiredState) { + Throwable toThrow = null; FMLLog.severe("Fatal errors were detected during the transition from %s to %s. Loading cannot continue", oldState, desiredState); StringBuilder sb = new StringBuilder(); printModStates(sb); @@ -96,10 +98,23 @@ public class LoadController for (Entry error : errors.entries()) { FMLLog.log(Level.SEVERE, error.getValue(), "Caught exception from %s", error.getKey()); + if (error.getValue() instanceof IFMLHandledException) + { + toThrow = error.getValue(); + } + else if (toThrow == null) + { + toThrow = error.getValue(); + } + } + if (toThrow != null && toThrow instanceof RuntimeException) + { + throw (RuntimeException)toThrow; + } + else + { + throw new LoaderException(toThrow); } - - // Throw embedding the first error (usually the only one) - throw new LoaderException(errors.values().iterator().next()); } } @@ -158,7 +173,14 @@ public class LoadController public void errorOccurred(ModContainer modContainer, Throwable exception) { - errors.put(modContainer.getModId(), exception); + if (exception instanceof InvocationTargetException) + { + errors.put(modContainer.getModId(), ((InvocationTargetException)exception).getCause()); + } + else + { + errors.put(modContainer.getModId(), exception); + } } public void printModStates(StringBuilder ret) diff --git a/fml/eclipse/FML-MockMod/src/cpw/mods/mockmod/MockMod.java b/fml/eclipse/FML-MockMod/src/cpw/mods/mockmod/MockMod.java index 3980506a1..3ebffec58 100644 --- a/fml/eclipse/FML-MockMod/src/cpw/mods/mockmod/MockMod.java +++ b/fml/eclipse/FML-MockMod/src/cpw/mods/mockmod/MockMod.java @@ -1,10 +1,13 @@ package cpw.mods.mockmod; +import net.minecraft.src.GuiErrorScreen; import net.minecraft.src.ItemBlock; import net.minecraft.src.NetHandler; import net.minecraft.src.NetworkManager; import net.minecraft.src.Packet131MapData; import net.minecraft.src.Packet250CustomPayload; +import net.minecraft.src.FontRenderer; +import cpw.mods.fml.client.CustomModLoadingErrorDisplayException; import cpw.mods.fml.common.Mod; import cpw.mods.fml.common.Mod.Block; import cpw.mods.fml.common.Mod.Init; @@ -12,6 +15,7 @@ import cpw.mods.fml.common.Mod.Instance; import cpw.mods.fml.common.Mod.Metadata; import cpw.mods.fml.common.Mod.PreInit; import cpw.mods.fml.common.Side; +import cpw.mods.fml.common.SidedProxy; import cpw.mods.fml.common.asm.SideOnly; import cpw.mods.fml.common.event.FMLInitializationEvent; import cpw.mods.fml.common.event.FMLPreInitializationEvent; @@ -56,6 +60,8 @@ public class MockMod @Metadata private ModMetadata meta; + @SidedProxy(clientSide="cpw.mods.mockmod.MockProxyClient", serverSide="cpw.mods.mockmod.MockProxy") + public static MockProxy proxy; // @Block(name="MyBlock", itemTypeClass=TestItem.class) // private MockBlock myBlock; @@ -70,5 +76,6 @@ public class MockMod public void init(FMLInitializationEvent evt) { System.out.printf("Hello from mockmod init : %s %s %s\n", myInstance, meta, MockBlock.tstInstance); + proxy.throwError(); } } diff --git a/fml/eclipse/FML-MockMod/src/cpw/mods/mockmod/MockProxy.java b/fml/eclipse/FML-MockMod/src/cpw/mods/mockmod/MockProxy.java new file mode 100644 index 000000000..e9fdb3f7d --- /dev/null +++ b/fml/eclipse/FML-MockMod/src/cpw/mods/mockmod/MockProxy.java @@ -0,0 +1,9 @@ +package cpw.mods.mockmod; + +public class MockProxy +{ + public void throwError() + { + throw new RuntimeException(); + } +} diff --git a/fml/eclipse/FML-MockMod/src/cpw/mods/mockmod/MockProxyClient.java b/fml/eclipse/FML-MockMod/src/cpw/mods/mockmod/MockProxyClient.java new file mode 100644 index 000000000..145235425 --- /dev/null +++ b/fml/eclipse/FML-MockMod/src/cpw/mods/mockmod/MockProxyClient.java @@ -0,0 +1,29 @@ +package cpw.mods.mockmod; + +import net.minecraft.src.FontRenderer; +import net.minecraft.src.GuiErrorScreen; +import cpw.mods.fml.client.CustomModLoadingErrorDisplayException; + +public class MockProxyClient extends MockProxy +{ + @Override + public void throwError() + { + throw new CustomModLoadingErrorDisplayException() + { + + @Override + public void drawScreen(GuiErrorScreen errorScreen, FontRenderer fontRenderer, int par1, int par2, float par3) + { + errorScreen.func_73732_a(fontRenderer, "MockMod doesn't like your installation", errorScreen.field_73880_f / 2, 75, 0xFFFFFF); + + } + + @Override + public void initGui(GuiErrorScreen errorScreen, FontRenderer renderer) + { + // noop + } + }; + } +}