Skip to content

Commit

Permalink
simplified state transmission and authoritative server
Browse files Browse the repository at this point in the history
  • Loading branch information
masylum committed Dec 20, 2011
1 parent 6baf536 commit f9eeb93
Show file tree
Hide file tree
Showing 7 changed files with 415 additions and 94 deletions.
38 changes: 19 additions & 19 deletions app.js
Expand Up @@ -194,24 +194,6 @@ http.post('/room', authorize, function (req, res, next) {
} }
}); });


http.get('/game/single', authorize, function (req, res, next) {
var js = ['tile', 'client', 'mouse', 'confirm']
, options = { user: req.user
, room: null
, css: asereje.css()
, js: asereje.js(js)
};

require('./game').spawn({
io: io
, single: true
, user: req.user
, db: db
});

res.render('room', options);
});

http.get('/scores', authorize, function (req, res, next) { http.get('/scores', authorize, function (req, res, next) {
var funk = require('funk')('parallel'); var funk = require('funk')('parallel');


Expand Down Expand Up @@ -292,9 +274,27 @@ http.get('/scores', authorize, function (req, res, next) {
}, next); }, next);
}); });


http.get('/game/single', authorize, function (req, res, next) {
var js = ['vendor/uuid', 'tile', 'client', 'mouse', 'confirm']
, options = { user: req.user
, room: null
, css: asereje.css()
, js: asereje.js(js)
};

require('./game').spawn({
io: io
, single: true
, user: req.user
, db: db
});

res.render('room', options);
});

http.get('/game/:room_id', authorize, function (req, res, next) { http.get('/game/:room_id', authorize, function (req, res, next) {
var query = {_id: require('mongojs').ObjectId(req.param('room_id'))} var query = {_id: require('mongojs').ObjectId(req.param('room_id'))}
, js = ['tile', 'client', 'mouse', 'confirm']; , js = ['vendor/uuid', 'tile', 'client', 'mouse', 'confirm'];


db.rooms.findOne(query, function (error, room) { db.rooms.findOne(query, function (error, room) {
if (error) return next(error); if (error) return next(error);
Expand Down
43 changes: 29 additions & 14 deletions game.js
Expand Up @@ -8,6 +8,7 @@ module.exports.spawn = function (options) {


function _defaultState() { function _defaultState() {
return { tiles: [] return { tiles: []
, events: []
, time: 0 , time: 0
, points: 250 , points: 250
, players: {} , players: {}
Expand Down Expand Up @@ -53,6 +54,7 @@ module.exports.spawn = function (options) {


function _cleanState() { function _cleanState() {
clearInterval(STATE.interval); clearInterval(STATE.interval);
clearInterval(STATE.sync_interval);
STATE = _defaultState(); STATE = _defaultState();
room.removeAllListeners(); room.removeAllListeners();
} }
Expand All @@ -70,6 +72,14 @@ module.exports.spawn = function (options) {
room_socket.emit('tick', {time: STATE.time, points: STATE.points}); room_socket.emit('tick', {time: STATE.time, points: STATE.points});
} }


function _sync() {
console.log('sync', STATE.events);
room_socket.emit('sync', STATE.events);

// reset the event stack
STATE.events = [];
}

// for each game // for each game
function _initState() { function _initState() {
STATE.tiles = _shuffle(_getDeck()); STATE.tiles = _shuffle(_getDeck());
Expand All @@ -83,23 +93,29 @@ module.exports.spawn = function (options) {
STATE.num_pairs = Tile.getNumPairs(STATE.tiles); STATE.num_pairs = Tile.getNumPairs(STATE.tiles);
STATE.remaining_tiles = Tile.TOTAL_TILES; STATE.remaining_tiles = Tile.TOTAL_TILES;
STATE.interval = setInterval(_eachSecond, 1000); STATE.interval = setInterval(_eachSecond, 1000);
STATE.sync_interval = setInterval(_sync, 1000);
STATE.started = true; STATE.started = true;
_.each(STATE.players, function (player) { _.each(STATE.players, function (player) {
player.selected_tile = null; player.selected_tile = null;
player.num_pairs = 0; player.num_pairs = 0;
}); });
} }


// when a tile is being clicked // when two tiles are matched
socket.on('tile.clicked', function (data) { socket.on('tile.matched', function (data) {
var tile = data.tile var tiles = data.tiles
, player_id = data.player_id; , uuid = data.uuid
, player_id = data.player_id
, points;

if (STATE.tiles[tiles[0].i].is_deleted || STATE.tiles[tiles[1].i].is_deleted) {
STATE.events.push({type: 'error', uuid: uuid});

// are matching
} else if (Tile.areMatching(tiles[0], tiles[1])) {

_.each(tiles, Tile['delete']);


Tile.onClicked(
function (tile) {
socket.broadcast.emit('tiles.updated', STATE.tiles);
}
, function secondSelection(tile, selected_tile, points) {
STATE.remaining_tiles -= 2; STATE.remaining_tiles -= 2;
STATE.num_pairs = Tile.getNumPairs(STATE.tiles); STATE.num_pairs = Tile.getNumPairs(STATE.tiles);
points = (Tile.POINTS_PER_SECOND * 3) + Math.ceil( points = (Tile.POINTS_PER_SECOND * 3) + Math.ceil(
Expand All @@ -113,11 +129,14 @@ module.exports.spawn = function (options) {
, num_pairs: STATE.num_pairs , num_pairs: STATE.num_pairs
, player_num_pairs: STATE.players[player_id].num_pairs , player_num_pairs: STATE.players[player_id].num_pairs
}); });
socket.broadcast.emit('tiles.updated', STATE.tiles);
STATE.events.push(data);


if (!STATE.num_pairs || !STATE.remaining_tiles) { if (!STATE.num_pairs || !STATE.remaining_tiles) {
STATE.finished = true; STATE.finished = true;
clearInterval(STATE.interval); clearInterval(STATE.interval);
clearInterval(STATE.sync_interval);
_sync();


// loose // loose
if (!STATE.remaining_tiles) { if (!STATE.remaining_tiles) {
Expand All @@ -138,10 +157,6 @@ module.exports.spawn = function (options) {
} }
} }
} }
, function noMatching(tile, selected_tile) {
socket.broadcast.emit('tiles.updated', STATE.tiles);
}
)(tile, player_id);
}); });


// mouse.js // mouse.js
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -12,6 +12,7 @@
"jade": "0.19.0", "jade": "0.19.0",
"mongojs": "0.2.6", "mongojs": "0.2.6",
"asereje": "0.1.0", "asereje": "0.1.0",
"moment": "1.2.0" "moment": "1.2.0",
"node-uuid": "1.3.1"
} }
} }
98 changes: 67 additions & 31 deletions public/js/client.js
@@ -1,4 +1,4 @@
/*global Raphael, io, Tile*/ /*global Raphael, io, Tile, uuid*/


var socket; var socket;


Expand All @@ -13,6 +13,7 @@ $(function () {
, ALPHA_HIDE = 0.25 , ALPHA_HIDE = 0.25
, CANVAS_WIDTH = (ROWS * TILE_WIDTH) + (2 * SIDE_SIZE) , CANVAS_WIDTH = (ROWS * TILE_WIDTH) + (2 * SIDE_SIZE)
, CANVAS_HEIGHT = (COLUMNS * TILE_HEIGHT) + (2 * SIDE_SIZE) , CANVAS_HEIGHT = (COLUMNS * TILE_HEIGHT) + (2 * SIDE_SIZE)
, UUID = uuid.noConflict()
, paper = Raphael($('#canvas').get(0), CANVAS_WIDTH, CANVAS_HEIGHT) , paper = Raphael($('#canvas').get(0), CANVAS_WIDTH, CANVAS_HEIGHT)
, room_host_id = $('#room_host_id').val() , room_host_id = $('#room_host_id').val()
, namespace_id = $('#namespace_id').val() , namespace_id = $('#namespace_id').val()
Expand All @@ -25,14 +26,14 @@ $(function () {
socket = io.connect('/' + namespace_id); socket = io.connect('/' + namespace_id);


emit = _.bind(socket.emit, socket); emit = _.bind(socket.emit, socket);
//if (document.location.hostname === 'localhost') { if (document.location.hostname === 'localhost') {
// emit = function (ev) { emit = function (ev) {
// var args = arguments; var args = arguments;
// setTimeout(function () { setTimeout(function () {
// socket.emit.apply(socket, args); socket.emit.apply(socket, args);
// }, 5000); }, 5000);
// }; };
//} }


/** /**
* Adds the new player to the room view * Adds the new player to the room view
Expand Down Expand Up @@ -174,7 +175,7 @@ $(function () {
*/ */
function initGame(STATE) { function initGame(STATE) {
var TILE = Tile(STATE) var TILE = Tile(STATE)
, svgs = [], images = [], shapes = [], shadows = []; , events = [], svgs = [], images = [], shapes = [], shadows = [];


function setShadows(tile) { function setShadows(tile) {
var shadow = shadows[tile.i] var shadow = shadows[tile.i]
Expand Down Expand Up @@ -280,7 +281,10 @@ $(function () {


function paintAsSelected(tile, color) { function paintAsSelected(tile, color) {
if (!shapes[tile.i].removed) { if (!shapes[tile.i].removed) {
shapes[tile.i].attr({fill: color || STATE.players[tile.player_ids[0]].color, 'fill-opacity': 0.5}); shapes[tile.i].attr({
fill: color || STATE.players[tile.player_ids[tile.player_ids.length - 1]].color
, 'fill-opacity': 0.5
});
} }
} }


Expand Down Expand Up @@ -332,7 +336,9 @@ $(function () {
} }


shadowing(tile); shadowing(tile);
svgs[tile.i].remove(); svgs[tile.i].attr({opacity: tile.is_deleted ? 0 : 1});
// svgs[tile.i].remove();
// delete svgs[tile.i];
makeBetterVisibility(tile, false); makeBetterVisibility(tile, false);
} }


Expand Down Expand Up @@ -410,16 +416,22 @@ $(function () {
function firstSelection(tile) { function firstSelection(tile) {
document.getElementById('s_click').play(); document.getElementById('s_click').play();
paintAsSelected(tile, STATE.players[user_data._id].color); paintAsSelected(tile, STATE.players[user_data._id].color);
emit('tile.clicked', {tile: tile, player_id: user_data._id});
} }
, function onMatching(tile, selected_tile, points) { , function onMatching(tile, selected_tile, points) {
var event = { uuid: UUID.v4()
, tiles: [_.clone(tile), _.clone(selected_tile)]
, player_id: user_data._id
};

document.getElementById('s_gling').play(); document.getElementById('s_gling').play();
_.each([tile, selected_tile], function (tile) { _.each([tile, selected_tile], function (tile) {
svgs[tile.i].animate({opacity: 0}, 100, '>', function () { svgs[tile.i].animate({opacity: 0}, 100, '>', function () {
onDelete(tile); onDelete(tile);
}); });
}); });
emit('tile.clicked', {tile: tile, player_id: user_data._id});
events.push(event);
emit('tile.matched', event);
} }
, function notMatching(tile, selected_tile) { , function notMatching(tile, selected_tile) {
document.getElementById('s_grunt').play(); document.getElementById('s_grunt').play();
Expand All @@ -428,18 +440,20 @@ $(function () {
onUnselected(selected_tile); onUnselected(selected_tile);
} }
onUnselected(tile); onUnselected(tile);
emit('tile.clicked', {tile: tile, player_id: user_data._id});
} }
, function onError() {}
)(tile, user_data._id); )(tile, user_data._id);
}); });


shape.hover(function () { shape.hover(function () {
if (!STATE.tiles[tile.i].selected && TILE.isFree(STATE.tiles[tile.i]) && !STATE.tiles[tile.i].is_deleted) { if (!TILE.isSelected(STATE.tiles[tile.i])
&& TILE.isFree(STATE.tiles[tile.i])
&& !STATE.tiles[tile.i].is_deleted) {
this.attr({'fill-opacity': 0.3}); this.attr({'fill-opacity': 0.3});
} }
makeBetterVisibility(STATE.tiles[tile.i], true); makeBetterVisibility(STATE.tiles[tile.i], true);
}, function () { }, function () {
if (!STATE.tiles[tile.i].selected && !STATE.tiles[tile.i].is_deleted) { if (!TILE.isSelected(STATE.tiles[tile.i]) && !STATE.tiles[tile.i].is_deleted) {
this.attr({'fill-opacity': 0}); this.attr({'fill-opacity': 0});
} }
makeBetterVisibility(STATE.tiles[tile.i], false); makeBetterVisibility(STATE.tiles[tile.i], false);
Expand All @@ -453,16 +467,12 @@ $(function () {
*/ */
function drawTile(tiles, i) { function drawTile(tiles, i) {
if (tiles[i].is_deleted) { if (tiles[i].is_deleted) {
if (svgs[i]) { onDelete(tiles[i]);
onDelete(tiles[i]);
}
} else { } else {
if (!svgs[i]) { renderTile(tiles[i]);
renderTile(tiles[i]); setShadows(tiles[i]);
setShadows(tiles[i]);
}


if (tiles[i].selected) { if (TILE.isSelected(tiles[i])) {
paintAsSelected(tiles[i]); paintAsSelected(tiles[i]);
} else { } else {
paintAsUnselected(tiles[i]); paintAsUnselected(tiles[i]);
Expand Down Expand Up @@ -505,12 +515,38 @@ $(function () {
$('.sidebar .player_' + data.player_id + ' .points').html(data.player_num_pairs); $('.sidebar .player_' + data.player_id + ' .points').html(data.player_num_pairs);
}); });


socket.removeAllListeners('tiles.updated'); socket.removeAllListeners('sync');
socket.on('tiles.updated', function (tiles) { socket.on('sync', function (server_events) {
_.each(tiles, function (tile) { console.log('sync', Object.keys(server_events).length, _.clone(server_events), _.clone(events));
if (tile && !_.isEqual(tile, STATE.tiles[tile.i])) {
STATE.tiles[tile.i] = tile; _.each(server_events, function (server_event) {
drawTile(STATE.tiles, tile.i); var event = _.detect(events, function (e) {
return e.uuid === server_event.uuid;
});

if (event) {
// rollback
if (server_event.type === 'error') {
_.each(server_event.tiles, function (tile) {
if (!tile.is_deleted) {
STATE.tiles[tile.i].is_deleted = false;
onDelete(tile);
}
});
}
events = _.without(events, event);
} else {
if (server_event.type !== 'error') {
_.each(server_event.tiles, function (tile) {
if (STATE.players[user_data._id].selected_tile.i === tile.i) {
STATE.players[user_data._id].selected_tile = null;
}
TILE['delete'](STATE.tiles[tile.i]);
svgs[tile.i].animate({opacity: 0}, 100, '>', function () {
onDelete(tile);
});
});
}
} }
}); });
}); });
Expand Down

0 comments on commit f9eeb93

Please sign in to comment.