Tuesday, August 3, 2010

Don't Publish Real Objects to Faye

‹prev | My Chain | next›

Today, I continue working on the faye branch of my (fab) game. Having solved a minor mystery last night, I would like remove comet entirely and get through as much replacement with faye as possible.

The best laid plans...

Removing the comet stuff is easy. I start with the client and will rip out the server stuff later. It is easy to identify comet in the client—it is the <iframe>:
  <script type="application/javascript">
$(function() {
// Other initialization...

$('#iframe')[0].src = '/comet_view?player=' + me.id + '&x=' + me.x + '&y=' + me.y;
});
</script>
</head>
<body>
<form id="login" method="get">
<label>Name
<input type="text" name="player">
</label>
<input type="submit" value="Play">
</form>
<div id="room-container"></div>
<iframe id="iframe"></iframe>
</body>
</html>
With that removed what breaks?

Not too surprisingly, moving the player about the room does not break. I got that converted over to faye two days ago. What does break is getting other players in the room to show up on my screen. Prior to converting to faye, all of the comet communication went through the PlayerList class on the client side. Now that I am on faye, I do not necessarily have to limit myself to a single pipe for communication. Still, until I know for sure that I have good reason to do otherwise, I will start faye integration in that same class.

To build up the player list in the backend, I need to listen for new player announcements in my fab.js backend:
// Ensure that the faye server has fully established by waiting half a
// second before subscribing to channels
setTimeout(function(){
client.subscribe("/players/create", function(player) {
puts("adding player:" + player.id);
add_player(player);
});
}, 500);
Easy-peasy: I can make use of the add_player backend function to add players to the data store.

Similarly, in the client side, I need to broadcast the new player to the "/players/create" channel. Doing this in the PlayerList initialization seems reasonable:
var PlayerList = function(me, room, options) {
// other initialization...

this.faye = new Faye.Client('/faye');
this.faye.publish('/players/create', me);
};
Here is where I run into problems. That looks to be a perfectly legitimate faye publish() call, but... nothing happens in the backend. Specifically, that puts statement never fires:
// Ensure that the faye server has fully established by waiting half a
// second before subscribing to channels
setTimeout(function(){
client.subscribe("/players/create", function(player) {
puts("adding player:" + player.id);
add_player(player);
});
}, 500);
That never fires and querying my local store produces empty results. Ugh. I had such high hopes for progress tonight.

My troubleshooting steps include:
  • adding a timeout around the publish (it worked on the backend)
  • adding console.debug statements around the publish to ensure that statement and beyond was reached (it was)
  • publishing to that same channel with a simple string
None of those worked until I commented out the publish of the player object. With that commented out, then the simple string publish did work.

So what gives?

The me object being published is a full-blown object (with a prototype and all), not a simple object literal:
  this.faye.publish('/players/create', me);
There is no way to convert a prototyped object into JSON to be transferred via faye, so things silently fail. Not great, but understandable.

To resolve, I add an attrs() method to the Player class and then publish that:
var PlayerList = function(me, room, options) {
// Other class initialization

this.faye = new Faye.Client('/faye');
this.faye.publish('/players/create', me.attrs());
};
With that, I finally see activity in the backend. Better still, it is the right kind of activity—the player really is added to the local store. Tomorrow, I will continue replacing comet communication with faye. Hopefully things will go a bit smoother.

Day #184

No comments:

Post a Comment