Discussion: Global error handling


(Jacob Albano) #1

Continuing the discussion from Collision that would reset the world?:

Our friend @betkowski has been having some serious trouble with getting his Debugger to play nicely, which results in his whole SWF crashing when an error gets thrown. I don’t think I have to tell anyone how frustrating that must be. While trying to help him out, @Ultima2876 suggested an alternate way of displaying error traces in situations where a debugger isn’t available, and that got me to thinking…

Flashpunk has a debugging console already. What if it was possible to automatically catch any errors, print them to the debug console, and pause the game without allowing it to crash? It would be immensely helpful in scenarios when you can’t access a native debugger; I’m thinking mobile development specifically.

Flash doesn’t have global error handling built in, but since Flashpunk runs everything from one top-level engine function, it would be trivial to set up a try...catch block that snags every error the game could throw from within the update() hierarchy. Naturally it would be optional, since exception handling comes with a certain amount of overhead, but it would be great to have available.

Does anyone have any thoughts on the matter?


(Zachary Lewis) #2

That sounds like it could work. If the console is enabled, any exception could be passed to the console’s log.


(Jacob Albano) #3

That’s exactly what I was thinking.


(Draknek) #4

I don’t think you could easily make this optional? You can’t enable or disable try/catch blocks at runtime, they’re either there or they’re not.


(Jacob Albano) #5

It could be done fairly easily with two update functions in the Engine class; one as it is now, the other wrapping the existing update() in a try/catch block. When the engine is updating, either in onEnterFrame or onTimer, it would run the appropriate variant.


(Zachary Lewis) #6

You’re proposing something like:

/** A delegate for the update function. */
protected var _onTick:Function;

public function update():void
{
  // The actual update function.
}

public function debugUpdate():void
{
  try
  {
    update();
  }
  catch (err:Error)
  {
    // Print error to console.
  }
}

public function setDebug(value:Boolean):void
{
  _onTick = value ? debugUpdate : update;
}

(Jacob Albano) #7

Yeah, pretty much. Maybe even add in some conditional compilation to remove the overhead from the function object in Release mode:

// in Engine.as

CONFIG::debug { _onTick(); }
CONFIG::release { update(); }

That may be premature optimization and I don’t know for sure that editors besides Flashdevelop define those by default.


(Zachary Lewis) #8

Oh, shit. I didn’t realize that the mxml compiler had conditional statements in it. That makes the whole thing that much easier.

Engine.as

/**
 * Updates the game, updating the World and Entities.
 */
public function update():void
{
  CONFIG::debug {
  try {
  }

  FP._world.updateLists();
  if (FP._goto) checkWorld();
  if (FP.tweener.active && FP.tweener._tween) FP.tweener.updateTweens();
  if (FP._world.active)
  {
    if (FP._world._tween) FP._world.updateTweens();
    FP._world.update();
  }
  FP._world.updateLists(false);

  CONFIG::debug {
  }
  catch (caughtError:Error)
  {
    FP.console.log("[Error]", caughtError.message);
  }
  }
}

Even if the IDE doesn’t auto-support setting debug, they always have a place for you to add command-line arguments. You’d just need to use

-define=CONFIG::debug,true

We might even want to force the console on in the Engine constructor, so you wouldn’t even need to turn it on in code. That would allow your releases and debugging to be fully build-target-based.


(Jacob Albano) #9

Does it work that way? I think the code between blocks might need to be complete snippets. The bottom block would probably terminate at the first } because it looks like it matches the opening one for the condition, even though it’s supposed to finish out the try block…

We should also consider cases where the user wants to be in Debug compilation mode but still use the usual debugging features.


(Zachary Lewis) #10

I thought they worked like #ifdef. Turns out, they don’t.


(Zachary Lewis) #11

Okay, problem and possible solution.

This fix will work just fine for people who are linking against the source code, but not for people using the .swc. Since the .swc is built, it’ll have whatever compiler directives set “baked in” to the library.

This can be solved by providing two .swc files on the page, one being the debugging library and one being the release library.

A better solution might just be to have a function to switch over (like in my first example).


(Jacob Albano) #12

Good call. It’s very likely that delegates don’t have any overhead at all, and even if they do it’s probably far too small to matter.


(Ultima2876) #13

Delegates have a miniscule overhead; I wouldn’t worry especially if it’s just one call per update (it’s not much more than a regular function call at all). I would probably suggest against using the compiler directives; people might actually want their exceptions hidden in release mode as well. Particularly on mobile devices, debugging a release-mode SWF with adobe scout is pretty common. So I’d say keep the option open for having exception catching in release compiles and ignore the compiler directive stuff altogether, clever as it is :slight_smile:

Also situations where you want debug mode but no debug console, or debug console in release mode (it can happen! shhh)


(Jacob Albano) #14

It’s certainly a useful trick for end-users. I might write up a small tutorial showing how to enable and disable the console automatically depending on the build.

Should I test out the implementation and submit a pull request? I’ve never used Github for actual source control so I’ll probably get it wrong. :sweat_smile:


(Zachary Lewis) #15

I beat you to it.

Take a look at those changes. You can also see it in action in the below .swf. (It seems the full error names might not show when running from a non-debug player, but YMMV.)

flashpunkruntimedebugging.swf (87.3 KB)

You can check the source code, too. It’s really just a single Boolean set in the constructor (or anywhere you fancy).