Sunday, August 1, 2010

Real-World Faye+Fab.js

‹prev | My Chain | next›

Up today, I would like to see if I can get my (fab) game working with the faye / fab.js backend that I got working last night.

I will not try to get the entire game converted from pure comet over to faye tonight. If I can get it so that player movement is communicated entirely via faye, I will be thrilled.

I start in the fab.js backend. Happily the change there is relatively easy. I change my listen call to use the listen_with_faye app that I wrote a few nights back:
...
with ( require( "fab" ) )

( fab )

( listen_with_faye, 0xFAB )

( /move/ )
( if_body(
function () {
update_player_status(JSON.parse(""+this));
broadcast(comet_walk_player(this));
} ) )
...
In the old comet version, the /move resource listened for POSTs from clients and, if there was POST data in the request body, it would update the player status and then broadcast the news of the player's new location to all clients. Thanks to faye, I believe that I can do away with this resource entirely.

First thing I do in the front-end is to include the faye client-side library which faye makes available simply by attaching it to the running fabjs server:
...
<script type="text/javascript" src="/faye.js"></script>

<script src="/javascript/raphael-min.js" type="application/javascript"></script>
<script src="/javascript/player.js" type="application/javascript">"></script>
<script src="/javascript/player_list.js" type="application/javascript"></script>
...
Next, in the Player class initialization, I add a faye client to the object's attributes:
var Player = function(id, options) {
//...

this.faye = new Faye.Client('/faye');
};
And I use that in the notification code. Instead of POSTing data to the /move resource, I publish it to the faye /move channel:
Player.prototype.notify = function(evt) {
switch(evt.type) {
case "click":
this.faye.publish("/move", {id:this.id, x:evt.x, y:evt.y});
break;
// ....
default:
console.debug("[notify] type: " + evt.type + " value: " + evt.value);
}
};
Lastly, I need the PlayerList class to subscribe to the /move channel so that all attached clients can react when a player moves in the room:
var PlayerList = function(me, room, options) {
// ...

var self = this;
var client = new Faye.Client('/faye');
client.subscribe('/move', function(message) {
self.walk_player(message);
});
};
And that works great! My backend no longer needs to expect that the frontend will defined a player_list global variable to be called via comet. I just publish messages and interested clients know what to do with them. A nice, elegant solution to avoid the harsh realities of comet.

Last up, I need to replace the call to update_player in the game backend. That turns out to be a bit tricky as the backend client was not responding to the messages as expected. Eventually I added a timeout before the message listener starts to resolve that problem:
setTimeout(function(){
var client = new faye.Client('http://localhost:4011/faye');
client.subscribe("/move", function(message) {
update_player_status(message);
});
}, 1000);
I am not sure why that timeout is needed or how large it needs to be, so I will pick up investigating that tomorrow. For now, I have completed the switch from comet to faye for player movement. I have also done away with a lot of comet-inspired complexity in both my front-end and back-end. Not a bad night's work :)


Day #182

No comments:

Post a Comment