Add in the ability for a client to throw a custom exception that displays a custom gui instead of the default error message.

This commit is contained in:
Christian 2012-10-16 12:39:04 -04:00
parent a82285964f
commit 7327ea75f7
10 changed files with 165 additions and 6 deletions

View file

@ -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.
*
* <br/><em>Warning: Minecraft is in a deep error state.</em> <strong>All</strong> 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);
}

View file

@ -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());

View file

@ -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_);
}
}

View file

@ -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);

View file

@ -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);

View file

@ -0,0 +1,6 @@
package cpw.mods.fml.common;
public interface IFMLHandledException
{
}

View file

@ -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<String, Throwable> 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)

View file

@ -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();
}
}

View file

@ -0,0 +1,9 @@
package cpw.mods.mockmod;
public class MockProxy
{
public void throwError()
{
throw new RuntimeException();
}
}

View file

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