Friday, August 6, 2010

All for Disgust at Globals

‹prev | My Chain | next›

Up tonight, a simple enough task: restore idle timeouts in my (fab) game.

I spent the bulk of last night deleting code that provided functionality better implemented in faye. The fab.js backend is now little more than a skeleton app serving up html, javascript, CSS and a place for the faye serve to hang its hat:
with ( require( "fab" ) )

( fab )

// Listen on the FAB port and establish the faye server
( listen_with_faye, 0xFAB )

// resource to query player status -- debugging
( /^\/status/ )
( player_status )

// serve javascript and CSS
(/^\/(javascript|stylesheets)/)
(/^\/([-_\w]+)\.(js|css)$/)
(fab.nodejs.fs)
( fab.tmpl, "<%= this[0] %>/<%= this[1] %>.<%= this[2] %>" )
( fab.capture )
(404)

// serve static HTML
(/^\/([_\w]+)$/)
(fab.nodejs.fs)
( fab.tmpl, "html/<%= this %>.html" )
( fab.capture.at, 0 )

// anything else is 404 / Not Found
( 404 );
Aside from that, I have a local player store:
var players = {};
And several functions to interact with that local store:
function add_player(player) { ... }

function update_player_status(status) { ... }

function idle_watch(id) { ... }

function drop_player(id) { ... }
Lastly, I have a faye client that listens for messages and calls the appropriate player function. I had to put that client inside a 0.5 second timeout to allow the TCP/IP server establish itself at startup:
// Ensure that the faye server has fully established by waiting
// half a second before subscribing to channels
setTimeout(function(){
var client = new faye.Client('http://localhost:4011/faye');
client.subscribe("/move", function(message) {
update_player_status(message);
});

// other faye subscriptions
}, 500);
While surveying this, I recognize that idle timeout is going to be a bit of a challenge. The actual timeout code (idle_watch()) will work fine. Calling the function to drop the players (drop_player) will also work. The problem I am facing is how to broadcast the "drop player" message to my clients.

The client object is closed inside the setTimeout function. There is no way for me to get access to it from inside the drop_player() function. I could declare a global client variable and assign it inside the the setTimeout:
var client;

setTimeout(function(){
client = new faye.Client('http://localhost:4011/faye');
client.subscribe("/move", function(message) {
update_player_status(message);
});

// other faye subscriptions
}, 500);


function drop_player(id) {
client.publish("/drop", id);
}
Am I really going to let disgust with global variables prevent me from stopping there, calling it a night and getting a decent night's sleep? Of course I am.

This global variable trying to pop into existence is trying to tell me that I need an object. The functions that are interacting with the global store, players, ought to be methods on that object. I can then refactor the local store as:
// Player local store
var players = {
_: { },

all: function() {
return this._;
},

add_player: function(player) { ... },

update_player_status: function(status) { ... },

idle_watch: function(id) { ... },

drop_player: function(id) { ... },

init: function() { ... }
};
Inside the the init() method, I can establish my faye server and assign the client to an attribute of the player store object (nicely encapsulated, non-global):
  init: function() {
var self = this;

var faye = require("faye");
this.faye = new faye.Client('http://localhost:4011/faye');

this.faye.subscribe("/move", function(message) {
self.update_player_status(message);
});

// other faye interaction
}
I can then use that attribute to broadcast "drop player" messages via faye:
  drop_player: function(id) {
puts("Dropping player \""+ id +"\"");
this.faye.publish("/players/drop", id);
delete this._[id];
}
I rather like that approach. Everything having to do with the player store, including pub-sub, is now nicely encapsulated in an object. Initial interaction in the game seems to indicate that it all just works.

I have removed much over the past few days. Tomorrow, I will play around with the game a bit to make sure that everything is still OK before moving on to investigate some speed issues that I noted yesterday.


Day #187

No comments:

Post a Comment