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