Up tonight, I want to kick players out of my (fab) game. I am not trying to drop unruly players, just unresponsive players.
I start with the client code. When a
Player
quits, I need to remove the raphaël.js drawable object:Player.prototype.quit = function() {("
this.drawable.remove();
}
drawable
" refers to a raphaël.js circle and remove()
is a raphaël.js method).For good measure, I delete the attributes from the player object:
Player.prototype.quit = function() {I am not trying to save memory or anything of the sort. I remove these attributes so that the
this.drawable.remove();
delete this.x;
delete this.y;
delete this.id;
}
Player
no longer quacks like a Player
. If anything tries to treat it as such, errors will happen.Now that a
Player
can quit a room, I need to expose an interface for fab.js to be able to communicate with browsers to inform them of a departure. The interface for this is the PlayerList
in the browser. The PlayerList
holds the current player (me
) as well as all the other current players. I will handle removing these other players first:PlayerList.prototype.remove_player = function(id) {Actually, that is quite easy. I call the
this.get_player(id).quit();
delete this.other_players[id];
};
quit()
method that I just defined on the Player
leaving the room. Then I delete the player from the player list.If the player quitting the room is me (e.g. I have been idle too long), then I need to quit the room as do all other players. From my perspective, if I leave, I no longer care about the other players. So:
PlayerList.prototype.remove_player = function(id) {With that, I am ready to get started in the fab.js backend. First up, I do a little refactoring of the code so that the comet communication method is a bit more general. It had been specific to walking players in the rooms of each client. Now the
if (this.me.id == id) {
// I am leaving and so is everyone else (as far as I'm concerned)
this.me.quit();
for (var oid in this.other_players) {
this.other_players[oid].quit();
}
}
else {
this.get_player(id).quit();
delete this.other_players[id];
}
};
broadcast()
function can send any command to the PlayerList
in the connected browsers:function broadcast(comet_command) {It can send any command, such as the new
var num = 0;
for (var id in players) {
var player = players[id];
player.listener({body: comet_command});
num++;
}
puts("broadcasting to "+num+" players");
}
remove_player()
method. To get started with timing out players, I add a simple setTimeout()
immediately after a player is added in fab.js:// ...With that, 5 seconds after connecting, the
puts("adding: " + obj.body.id);
players[obj.body.id] = {status: obj.body, listener: out};
setTimeout(function() {
drop_player(obj.body.id);
}, 5000);
broadcast(comet_walk_player(JSON.stringify(obj.body)));
...
drop_player()
method will be called which, in turn, broadcasts to all connected clients that said player should be removed:function drop_player(id) {Trying this out, and after moving the player "bob" about the room, I am dropped:
puts("Dropping player \""+ id +"\"");
broadcast(comet_quit_player(id));
}
function comet_quit_player(id) {
return comet_wrap('player_list.remove_player("'+ id +'")');
}
cstrom@whitefall:~/repos/my_fab_game$ node game.jsThe last step is to move this into per-player idle watching inside the fab.js. In the
adding: bob
broadcasting to 1 players
broadcasting to 1 players
updating player status: bob
Dropping player "bob"
broadcasting to 1 players
idle_watch()
function, I clear any existing timeout, then set a new one. If 60 seconds pass, then the Player will be removed:function idle_watch(id) {Calling this method anytime a player updates its status will ensure that active players remain in the room as long as they like:
if (players[id].idle_timeout) {
clearTimeout(players[id].idle_timeout);
}
players[id].idle_timeout = setTimeout(function() {
drop_player(id);
}, 60*1000);
}
function update_player_status(status) {That is a good stopping point for tonight. Up tomorrow, more raphaël.js work, I think. I would like to include player names next to the player icons. Then it'll be onto the next killer feature (in my son's eyes): chatting.
puts("updating player status: " + status.id);
players[status.id].status = status;
idle_watch(status.id);
}
Day #108
No comments:
Post a Comment