Monday, May 31, 2010

Refactoring a Fab.js Stack

‹prev | My Chain | next›

Yesterday, I did not quite finish with my fab.js cleanup. Previously, I had refactored the code in my (fab) game to something along the lines of:
  ( /^\/comet_view/ )
( broadcast_new )
( init_comet )
( add_player )
( player_from_querystring )
The init_comet and player_from_querystring (fab) apps got the refactoring love. The broadcast_new and add_player apps are the leftovers that did not get refactored, but they are quite important to the game. They add the new player to the list of players and broadcast the new player to that list of players respectively.

The trouble I ended with last night was getting them both to play nicely with each other and getting the init_comet app to pass along the player information to broadcast_new. It turns out that the latter is fairly easy to resolve. Previously, init_comet only sent comet initialization back to the client, ending with something like:
         ({body: "<script type=\"text/javascript\">\"123456789 123456789 123456789 123456789 123456789 12345\";</script>\n"})
({body: "<script type=\"text/javascript\">\"123456789 123456789 123456789 123456789 123456789 12345\";</script>\n"})
It did not send along the player information because the browser did not need to know it (the browser had added the new player in the first place). The trick is to pass along the player information, but to have broadcast_new use it and not pass it along to the browser. In init_comet, I pass along the player information by passing the object encapsulating it back downstream:
         ({body: "<script type=\"text/javascript\">\"123456789 123456789 123456789 123456789 123456789 12345\";</script>\n"})
({body: "<script type=\"text/javascript\">\"123456789 123456789 123456789 123456789 123456789 12345\";</script>\n"})
(obj);
Then, in broadcast_new, I add a condition to broadcast the new player to the existing players, but send back the comet_initialization to the new player's browser:
function broadcast_new (app) {
return function () {
var out = this;

return app.call( function listener(obj) {
if (obj && obj.body && obj.body.id) {
broadcast(comet_walk_player(JSON.stringify(obj.body)));
}

else {
out(obj);
}
return listener;
});
};
}
The add_player app turns out to be a bit trickier. So tricky in fact that I have to eliminate it. It had added the player along with its comet channel to a list of players. The problem I ran into was the comet channel. The comet channel is further upstream than the init_comet binary app:
  ( /^\/comet_view/ )
( broadcast_new )
( init_comet )
( add_player )
( player_from_querystring )
This means that any subsequent communication over the channel needed to go through the init_comet channel. Initializing the comet channel (sending new headers and new opening HTML tags) each time a new player is added turns out to be a brilliant way to close all previous comet channels. So clearly I need something as close downstream as possible to handle this.

For now, I add the code that previously went in the add_player app into broadcast_new, making the add-player stack a bit smaller:
  ( /^\/comet_view/ )
( broadcast_new )
( init_comet )
( player_from_querystring )
Smaller and with a broadcast_new app with too many responsibilities. Still, I am in a better place than when I started. The init_comet and player_from_querystring apps are now tested and all of my untested code is working (in that I can play the game again) and in one place for future refactoring.

And that is a good place to stop for the night.

Day #120

No comments:

Post a Comment