A More Robust Exception System
One thing I’ve been refining in my free time is my system for debugging games on the Xbox 360. Sometimes you have a debugger and want all uncaught exceptions to bubble up so you can handle them in Visual Studio. Sometimes your game is in the hands of playtesters and reviewers where having them know where the problem occurred is crucial to you fixing a bug before releasing the game. And other times your game is in the hands of the public and something happens to slip through the cracks.
To this end, I’ve come up with a nifty little system that I think works pretty well at addressing all of these concerns in the best way possible, given what we have to work with. To start, I wanted to launch the game differently based on whether or not we were attached to the debugger. If we had a debugger attached, we’d run without a try/catch so that all uncaught exceptions hit Visual Studio. If we don’t have a debugger, we’d use a try/catch and use another “game” to display the error.
First thing I wanted was an easy way to encapsulate this functionality so I didn’t have to write it for each game. Since I have a shared library that all my games use, it was pretty easy to make a static method that did this for me:
public static void Run<T>() where T : Game, new()
{
if (Debugger.IsAttached)
{
using (var g = new T())
g.Run();
}
else
{
try
{
using (var g = new T())
g.Run();
}
catch (Exception e)
{
using (var g = new ExceptionGame(e))
g.Run();
}
}
}
With that out of the way, I was ready to write the ExceptionGame class. What I wanted was for it to use a nice Guide message (well, as nice as a message box can be when telling you the game broke) to inform the user that something bad happened, giving them the option to exit to the dashboard or view the error details (i.e. stack trace). This is what I came up with:
public class ExceptionGame : Game
{
private const string errorTitle = "Unexpected Error";
private const string errorMessage =
"The game had an unexpected error and had to shut down. " +
"We're sorry for the inconvenience.";
private static readonly string[] errorButtons = new[]
{
"Exit to Dashboard",
"View Error Details"
};
private readonly Exception exception;
private bool shownMessage;
private bool displayException;
private SpriteBatch batch;
private SpriteFont font;
public ExceptionGame(Exception e)
{
new GraphicsDeviceManager(this)
{
PreferredBackBufferWidth = 1280,
PreferredBackBufferHeight = 720
};
exception = e;
Components.Add(new GamerServicesComponent(this));
Content.RootDirectory = "Content";
}
protected override void LoadContent()
{
batch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>("Fonts/Debug");
}
protected override void Update(GameTime gameTime)
{
if (!shownMessage)
{
try
{
if (!Guide.IsVisible)
{
Guide.BeginShowMessageBox(
PlayerIndex.One,
errorTitle,
errorMessage,
errorButtons,
0,
MessageBoxIcon.Error,
result =>
{
int? choice = Guide.EndShowMessageBox(result);
if (choice.HasValue && choice.Value == 1)
displayException = true;
else
Exit();
},
null);
shownMessage = true;
}
}
catch { }
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
if (displayException)
{
Vector2 p = new Vector2(
GraphicsDevice.Viewport.TitleSafeArea.X,
GraphicsDevice.Viewport.TitleSafeArea.Y);
batch.Begin();
batch.DrawString(font, exception.ToString(), p, Color.White);
batch.End();
}
base.Draw(gameTime);
}
}
You can see I make an assumption about where a specific font is. Maybe not the wisest choice in the long term, but since I’m the only one using this code, it’s easy enough for me to remember to add that font to my project.
With this in place I’m all set. I set up a little test project that would throw an exception after a few seconds. When in Visual Studio, it breaks right on the throw call like it should. If I double click the exe in Windows, the first window will close down and a new one pops up to show me the Guide message and, optionally, the stack trace.
Hopefully this helps extend my previous post on showing exceptions on the 360 into a system that is suitable to leave in your game for release. Seeing a message box with text you wrote is always nicer than your user getting a strange Code 4 error (which we all hope they shouldn’t, but this is just in case
).
I think there is a problem with this if your game uses the GamerServicesComponent. I get the the following exception in ExceptionGame:
An unhandled exception of type ‘System.InvalidOperationException’ occurred in Microsoft.Xna.Framework.dll
Additional information: The Gamer Services functionality is already initialized.
I’ve tried using GamerServicesDispatcher instead of creating a new GamerServicesComponent, but I just end up with a black screen in the ExceptionGame instead of the MessageBox. Any ideas on this one?
I think there is a problem with this if your game uses the GamerServicesComponent. I get the the following exception in ExceptionGame:
An unhandled exception of type ‘System.InvalidOperationException’ occurred in Microsoft.Xna.Framework.dll
Additional information: The Gamer Services functionality is already initialized.
I’ve tried using GamerServicesDispatcher instead of creating a new GamerServicesComponent, but I just end up with a black screen in the ExceptionGame instead of the MessageBox. Any ideas on this one?
I’m a few months late responding, but I recently hit this issue myself. I figured out there is a way to use the GamerServicesComponent with this setup and have it work. Check out the results here: http://blog.nickgravelyn.com/2009/11/exception-handling-and-gamerservicescomponent/
I’m a few months late responding, but I recently hit this issue myself. I figured out there is a way to use the GamerServicesComponent with this setup and have it work. Check out the results here: http://blog.nickgravelyn.com/2009/11/exception-handling-and-gamerservicescomponent/