Impact

This forum is read only and just serves as an archive. If you have any questions, please post them on github.com/phoboslab/impact

6 years ago by IanMac

Afternoon all,

I remember a long time ago that there was an explanation on the exception handling (or lack of it) in ImpactJS. I think that Dominic basically said that any exception throws you out of the current game loop and onto the next iteration. He also posted some code that would allow us to put in the try catch in the right place to catch any errors in the game loop.

I can't find that post anymore and I think I need the code. Can anyone help out?

The basic problem is that I have a deterministic game loop that was all working fine on all platforms and machines.

At some point though, Safari on the iPad or iPhone started giving a whole different result to every other platform - which is very annoying (and difficult to track through the javascript console.

I suspect that it is a missing audio file or something that is triggered and forces the current game loop iteration to exit. At this point any random numbers from a list that should have been used up on this iteration are used up on the next loop instead and the game takes a whole new course.

Any help much appreciated.

Ian.

6 years ago by Joncom

This post is a little confusing, because it seems like you're suggesting that an uncaught exception throws off your deterministic number generator, and that there is no helpful information in the console about it.

However if an exception was indeed thrown then there definitely should be some useful information in the console.

If you're testing on iOS, you can even debug JavaScript remotely (on your computer) with breakpoints (no Windows support unfortunately).

Is it possible that there is nothing wrong with your code, and that the different device implementations of Math.random or whatever number generator you're using is responsible for the different outcomes?

6 years ago by IanMac

Thanks for replying Joncom.

On the number generator point, as the game is deterministic and I need to be able to replay a match, I pass in an array of 100 random integers to be used in the match. This will keep it the same across all platforms.

With regards to the debugging and breakpoints, a match lasts about 10 minutes and I don't know exactly when something goes wrong. It varies from game to game. There is some event that triggers it.

I think I am just going to have to sit with two machines side by side and watch until I see the game diverge, mark the time and set debugger to go through loop just before it.

Cheers,

Ian.

6 years ago by vincentpiel

Two suggestions :
In your game :
- rename update into _update.
- now create a new update() that will call _update within a detailled try/catch.

See code below.
I alerted on error, but you can choose to display it with a small debug Entity that would show some text in a simple way.

... But it might be that it's not an exception, but rather some numerical values that goes to NaN, or undefined (you should never use undefined to set a property value, but rather 0, null, false only).

In that case, setup a watcher for all properties that will throw exception on wrong value encountered.
Obviously, it will slow-down your game, but that's just the time you find the bug.

  // replacement for update, after you renamed your current update to _update
  update : function() {
       try {
          this._update();
       } catch(e) {
           var stackInfo = (e.stack && e.stack.split('\n')[0+!!window.chrome].join('\n')) 
                                                 || 'no stack info' ) ;
           var errMsg = 'exception thrown in game._update \n' + 
                                   'exception : ' + e + '\n' +
                                   'stack : ' + stackInfo ;
          alert(errMsg);
       }
  }



// function that take an instance of an object as argument, 
// and will watch for 'bad' values within its properties.
// add a watcher to any object that might have a property set to a wrong value.
// assumes : - you are using only standard properties (not getters/setters).
//                     - you are not using String, Number, Boolean 
//                              (using string,number,boolean is ok).
// put it in the scope of your game.

function addWatcher(baseObj) {
   for ( var p in baseObj)  {
       var value =  baseObj [p];
       testVal(baseObj, p,value );
       if (typeof value == 'function') continue;
       if (typeof value == 'object' && value !== null ) addWatcher(value);
       else {
                  (function() { 
                      var val = value ;
                      var prop = p;
                      Object.defineProperty(baseObj, prop, { 
                                              get : function () { return val ; } 
                                              set : function(x) {  testVal(baseObj, prop, x) ;
                                                                              val = x;   }        }) 
                   } () );
         }
    }
}

// function that tests a value against a set of 'bad' values.
// throws exception on bad value encountered.
// put it in the scope of your game.
function testVal(obj, prop, val) {
   // test for undefined, NaN, and Infinity
   var err = (val === undefined) || (val !== val) || (Math.abs(val) === Infinity) ;
   if (err)  throw('property ' + prop + ' of object ' +  Obj + ' had wrong value : ' + val);
}



so call addWatcher on any instance you want to have an eye upon, and it will throw exception when a bad value is set.
This code is not tested, so please re-read, hopefully it will give you some ideas to work out your puzzling bug.

Use the watcher in conjunction with update(); exception catcher.


!! Notice that there is a breach of responsability in Impact's Entity handling : when updating pos with the result of the movement trace, it is the whole pos property that will get changed, not its x and y only.
In entity.js, handleMovementTrace, line 172, replace :
this.pos = res.pos;
with
this.pos.x = res.pos.x ; this.pos.y = res.pos.y ;

Otherwise you won't be able to watch pos.x, pos.y.

Last remark : you might want to try/catch the draw() also, since exception could come from here.

6 years ago by ChavSpaniel

Thanks Vincent,

Those are useful tips.

I think I found the problem though.

var angle=45;
console.log("angle: " + angle.toRad()); 
console.log("cos: " + Math.cos(angle.toRad())); 
console.log("sin: " + Math.sin(angle.toRad())); 

Math.cos() and Math.sin() return slightly different values on iOS mobile browsers than on other implementations.

Slightly annoying. I get that there will be rounding errors. I sort of assumed that they would all make the same rounding errors though.

When I do code like this

var x = Math.cos(angle.toRad()) * (player.data.pace + 120);
var y = Math.sin(angle.toRad()) * (player.data.pace + 120);
player.vel = {x: x, y: y};

eventually, some differences are going to be evident in the game.

Anyone got any ideas on how to get round this?

Cheers,

Ian (Chav Spaniel - remembered my password this time)

6 years ago by drhayes

Can you precalc the values and store them in a lookup table instead? That way every platform gets the same values and, hey, it'll probably be marginally faster too. You'll lose some precision on the calculations but at least it'll be predictable.
Page 1 of 1
« first « previous next › last »