Monday, June 7, 2010

Keep-alive in Fab.js

‹prev | My Chain | next›

Up tonight, I need to fix a problem that seems to have popped up recently with my (fab) game in Chrome. Specifically, the comet <iframe> seems to be closing after only a minute. I am reasonably sure that this started in a recent Chrome build, but regardless of the cause, I need to make sure that the <iframe> stays open.

Happily, this is pretty easy to do in my fab.js backend:
function keepalive(id) {
if (players[id]) {
puts("keepalive: " + id);
players[id].listener({body: '<script type="text/javascript">"12345789 "</script>' + "\n"});
setTimeout(function(){keepalive(id);}, 30*1000);
}
}
If a player's ID is in the list of players, call the downstream (closer to the browser) listener with a NOP body. Then, wait another 30 seconds and keepalive again. I then call the keepalive function for the first time when the new player is added in the broadcast_new app:
function broadcast_new (app) {
return function () {
var out = this;

var downstream;
return app.call( function listener(obj) {
if (obj && obj.body && obj.body.id) {
for (var id in players) {
out({body: comet_walk_player(JSON.stringify(players[id].status))});
}

var new_id = obj.body.id;
puts("adding: " + new_id);
players[new_id] = {status: obj.body, listener: out};

idle_watch(new_id);

setTimeout(function(){keepalive(new_id);}, 30*1000);

puts("broadcasting about: " + new_id);
broadcast(comet_walk_player(JSON.stringify(obj.body)));
}
else {
downstream = out(obj);
}
return listener;
});
};
}
That is a good place to call keepalive for the first time because I am sure that the new player in the game is already in the players list. (I do make a TODO to clean-up that app—it's getting a tad large) That keeps the comet connection open seemingly indefinitely—or at least until the idle_watch kicks the player out of the room.

Minor Bug Fix


Much to my chagrin, I found that chat was no longer working in my (fab) game when I tried to demo it at Bohconf today. I take a little time to investigate that and fix it as well. The fix was a simple matter of removing code that seemed to serve no purpose:
Player.prototype.notify = function(evt) {
switch(evt.type) {
case "click":
this.stop();
this.walk_to(evt.x, evt.y);
this.notify_server('move', {id:this.id, x:evt.x, y:evt.y});
break;
case "message":
this.stop();
this.walk_to(this.x, this.y);
this.notify_server('move', {id:this.id, x:evt.x, y:evt.y});

this.notify_server('chat', {id:this.id, say:evt.value});
break;
default:
console.debug("[notify] type: " + evt.type + " value: " + evt.value);
}
};
It is beyond me why, when sending a chat event, I would need to stop the player walking through the room, walk them to the current x-y coordinate, then notify the fab.js backend that the player moved to the coordinates of an event that does not have coordinates. Craziness. What's worse, I was quite pleased with myself when I did it, though didn't say why. Tracer bullets do not prevent all mistakes. Ah well, removing those lines fixes chat and all's well that ends well.

Up tomorrow, my code could use a bit of refactoring, but I'm itching to play with web sockets. Which urge will win out?


Day #127

No comments:

Post a Comment