Error Handling

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Error Handling

      dclark brought up a good subject in the outline feedback forum, that I'd like to see discussed at little. When your game code detects an error, do you attempt to recover from the error and continue running OR do you fail out as elegantly as possible?

      Recovering might be good for something simple like a missing sound effect - it doesn't affect the game, right? But if the game recovers and moves on it might be tough for QA to notice a problem occurred.

      Failing out is not a horrible idea either - since most of these failures will happen in QA where they'll be noticed, recorded, and hopefully fixed.

      Also - should there be different strategies used for DEBUG and RELEASE builds?

      Generally, the solution I tend to like is recover if you can in RELEASE, fail in DEBUG. The only time I recover and continue is when I know the problem will likely allow the player to get to a save game point. If it's something catastrophic like running out of memory, I bail at all costs.

      What's everyone else doing?
      Mr.Mike
      Author, Programmer, Brewer, Patriot
    • Assert and offer an ignore forever...

      Certainly some errors should be show stoppers (can't init D3D, etc.) and should alert the user. Lesser errors, however, I think should be recoverable. In debug builds, these should obviously throw an assert. One thing that the original Game Programming Gems book suggests (and a CERTAIN COMPANY I MAY PRESENTLY WORK FOR does) is have a custom assert dialog appear. In it, you can have an "ignore" option so that if you hit it again it won't appear.

      It might even be a neat idea to rate assert levels for severity, and set a certain threshold for when a warning dialog would appear.

      For example:

      PriorityAssert(ASSERTLEVEL_HIGH, (NULL != pDD), "Couldn't instantiate D3D object!");

      and

      PriorityAssert(ASSERTLEVEL_LOW, (NULL != pDSoundObj), "Couldn't create sound effect!");

      You could have a global static called kMinAssertLevel that PriorityAssert (macro or fn) would compare to.

      James
    • RE: Assert and offer an ignore forever...

      Neat idea -

      I'd also add to it - attach a __FILE__ & __LINE__ to the assert call, and put the "have I been called and ignored" flag in a data structure, like a <map>.

      Add another bit of code to an INI file to figure out what the code will do under different assert levels:
      - always ignore and continue
      - ask
      - always fail

      The INI file should distinguish between debug and release builds - I imagine that the error handler will be used heavilly in development by programmers trying to debug though code that asserts every frame. In release, it shouldn't ask the player what to do about an error, unless their option is to attempt to save their game in it's potentially screwed up state instead of exiting.
      Mr.Mike
      Author, Programmer, Brewer, Patriot
    • Nah - that's dumb.

      Then you don't get asserts for anything - even the thing you are looking for!
      Mr.Mike
      Author, Programmer, Brewer, Patriot
    • I think at the end of the day, every situation would call for its own unique judgement that caters to its circumstance. Sometimes, you want to recover and continue (like missing sounds), and sometimes there is no point in going on (like missing background or something). Each situation would call for its own unique method of handling.

      I'd think it's dangerous to say that one mindset covers all situations, but acknowleding categories of situations might be a good thing to do.
    • No, no, it was a *locally declared* static bool, not globally. So it does work. Lemme grab the book here:

      #if defined(_DEBUG)
      extern bool CustomAssertFunction(bool, char*, int, char *, bool*); //Custom assert dialog...

      #define Assert( exp, description ) \
      { static bool ignoreAlways = false; \
      if (!ignoreAlways) { \
      if (CustomAssertFunction((int)(exp), description, \
      __LINE__, __FILE__, &ignoreAlways) ) ) \
      { _asm { int 3 } } \
      } \
      }

      And there you have it. Now get out of my cab, for nowhere on your ticket does it say you can sleep here!
    • I get it! Nice - and very easy too.
      Mr.Mike
      Author, Programmer, Brewer, Patriot
    • There's also the notion of warning messages ( log only do not popup or stop execution - but helpful for post-mortem wierd-behavior diagnosis ), error messages that can be handled ( popups, optional debug-into and optional ignore-further-hits ) and continued thru, and simple die-now problems ( just popup message and die unconditionally ).

      I tend to like a more KISS principle, but having the three gradations has been useful.

      In no conditions should the assertion mechanism "die dirty" - at a worst case it should be hooked in a way to force the application to release hardware resources and provide a message that tells the user why the app is dieing. At the least it cuts down on support issues and at best helps support post-mort the problem so it can be Really Fixed.