Skip to content

Commit

Permalink
Game manager
Browse files Browse the repository at this point in the history
The game states are saved in JSON files, in the data/games/active
directory.
  • Loading branch information
bpierre committed Aug 3, 2014
1 parent 4a607ce commit c4983b0
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 58 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules/
/.env
/data/
28 changes: 15 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,26 @@ function env(name, defaultValue) {
}

var TPL_PATH = env('TPL_PATH', __dirname + '/templates');
var DATA_PATH = env('DATA_PATH', __dirname + '/data');
var PUBLIC_URL = env('PUBLIC_URL');
var WEBSERVER_PORT = env('WEBSERVER_PORT', 3000);

var inspect = require('./lib/utils2').inspect;
var makeGame = require('./lib/game').create;
var makeTerrain = require('./lib/terrain').create;
var makeTurn = require('./lib/turn').createFirst;
var makeGameManager = require('./lib/game-manager');
var webserver = require('./lib/webserver');

var players = {
p1: { email: 'john@example.com' },
p2: { email: 'dave@example.com' }
};

var terrain = makeTerrain(GAME_WIDTH, GAME_HEIGHT, SKY_HEIGHT);
var firstTurn = makeTurn(players, terrain);
var game = makeGame(players, terrain, firstTurn);

webserver(game, TPL_PATH, PUBLIC_URL, 3000);
var gameManager = makeGameManager(DATA_PATH, {
gameWidth: GAME_WIDTH,
gameHeight: GAME_HEIGHT,
skyHeight: SKY_HEIGHT
});

webserver({
tplPath: TPL_PATH,
publicUrl: PUBLIC_URL,
port: WEBSERVER_PORT,
gameManager: gameManager
});

// inspect(game);
// console.log(renderHtml(game));
57 changes: 57 additions & 0 deletions lib/game-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
var Promise = require('bluebird');
var path = require('path');
var fs = Promise.promisifyAll(require('fs'));
var mkdirp = Promise.promisify(require('mkdirp'));
var glob = Promise.promisify(require('glob'));
var makeGame = require('./game').create;
var loadGameFromData = require('./game').loadFromData;
var makeTerrain = require('./terrain').create;
var makeTurn = require('./turn').createFirst;

function loadGameByPath(gamePath) {
return fs.readFileAsync(gamePath, 'utf8').then(function(content) {
return loadGameFromData(JSON.parse(content));
});
}

module.exports = function init(dataPath, settings) {
var gameWidth = settings.gameWidth;
var gameHeight = settings.gameHeight;
var skyHeight = settings.skyHeight;
var activeGamesPath = dataPath + '/games/active';
var gameManager = {
createGame: function create(email1, email2) {
return new Promise(function(resolve, reject) {
var players = {
p1: { email: email1 },
p2: { email: email2 }
};
var terrain = makeTerrain(gameWidth, gameHeight, skyHeight);
var firstTurn = makeTurn(players, terrain);
var game = makeGame(players, terrain, firstTurn);
resolve(gameManager.saveGame(game).thenReturn(game));
});
},
saveGame: function saveGame(game) {
var filename = path.join(activeGamesPath, game.id + '.json');
return mkdirp(activeGamesPath)
.then(function() {
fs.writeFileAsync(filename, game.toString());
});
},
loadGameById: function load(id) {
return mkdirp(activeGamesPath)
.then(function() {
return loadGameByPath(path.join(activeGamesPath, id + '.json'));
});
},
loadByEmails: function load(email1, email2) {
// TODO
},
list: function list() {
return glob(path.join(activeGamesPath, '*.json'))
.map(loadGameByPath);
}
};
return gameManager;
};
23 changes: 11 additions & 12 deletions lib/game.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ var uuid = require('node-uuid');

var Game = {};

Game.save = function save(filename, cb) {
fs.writeFile(filename, this.toString(), cb);
};

Game.currentTurn = function currentTurn() {
return this.turns[this.turns.length-1];
};
Expand All @@ -29,7 +25,7 @@ Game.toString = function toString() {

function create(players, terrain, turn) {
var game = Object.create(Game);
game.id = uuid();
game.id = uuid().replace(/-/g, '');
game.width = terrain.width;
game.height = terrain.height;
game.terrain = terrain;
Expand All @@ -38,13 +34,16 @@ function create(players, terrain, turn) {
return game;
}

function load(email1, email2) {
}

function save(cb) {
}

module.exports = {
create: create,
load: load
loadFromData: function(gameData) {
var game = Object.create(Game);
game.id = gameData.id;
game.width = gameData.width;
game.height = gameData.height;
game.terrain = gameData.terrain;
game.players = gameData.players;
game.turns = gameData.turns;
return game;
}
};
1 change: 0 additions & 1 deletion lib/renderers/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ module.exports = function init(templatesPath, publicUrl) {
var jrender = jade.compileFile(templatesPath + '/game.jade', {
pretty: true
});

return function render(game) {
return jrender({
game: game,
Expand Down
99 changes: 70 additions & 29 deletions lib/webserver.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@ var express = require('express');
var bodyParser = require('body-parser');
var jade = require('jade');
var createTurn = require('./turn').create;
var gameManager = require('./game-manager');

var app = express();
app.use(bodyParser.urlencoded({ extended: false }))

function init(game, templatesPath, publicUrl, port) {
function init(settings) {

var templatesPath = settings.tplPath;
var publicUrl = settings.publicUrl;
var gameManager = settings.gameManager;

var renderHome = jade.compileFile(templatesPath + '/webserver.jade', {
pretty: true
Expand All @@ -16,43 +21,79 @@ function init(game, templatesPath, publicUrl, port) {
var renderGame = makeHtmlRenderer(templatesPath, publicUrl);

app.get('/', function(req, res) {
res.send(renderHome({
gameView: renderGame(game)
}));
gameManager.list().then(function(games) {
var html = '<body>\n';
html += '<style>body,a{background:black;color:white;}</style>\n';
html += '<form action="/new" method="post">\n';
html += '<p><button>New game</button></p>\n';
html += '</form>\n';
html += '<h2>Active games:</h2>\n';
html += '<ul>\n';
html += games.map(function(game) {
var humanTitle = game.players.p1.email + ' vs. ' + game.players.p2.email;
return '<li><a href="/game/' + game.id + '">'+ humanTitle + '</a></li>';
}).join('\n');
html += '</ul>\n';
html += '</body>\n';
res.send(html);
});
});

app.get('/game/:id', function(req, res) {
gameManager.loadGameById(req.params.id).then(function(game) {
res.send(renderHome({
game: game,
gameView: renderGame(game)
}));
});
});

app.post('/command', function(req, res) {
app.post('/new', function(req, res) {
gameManager.createGame('bob@example.com', 'alice@example.com')
.then(function(game) {
res.redirect('/game/' + game.id);
});
});


app.post('/game/:id/command', function(req, res) {
var command = req.body.command;
if (!command) return;

var player = command.split('-')[0];
var direction = command.split('-')[1];

var turn = game.currentTurn();
var p1x = turn.char1.position[0];
var p1y = turn.char1.position[1];
var p2x = turn.char2.position[0];
var p2y = turn.char2.position[1];

if (player === 'p1') {
if (direction === 'left') p1x--;
if (direction === 'right') p1x++;
if (direction === 'top') p1y--;
if (direction === 'bottom') p1y++;
}

if (player === 'p2') {
if (direction === 'left') p2x--;
if (direction === 'right') p2x++;
if (direction === 'top') p2y--;
if (direction === 'bottom') p2y++;
}

game.turns.push(createTurn([p1x, p1y], [p2x, p2y]));

res.redirect('/');
gameManager.loadGameById(req.params.id).then(function(game) {

var turn = game.currentTurn();
var p1x = turn.char1.position[0];
var p1y = turn.char1.position[1];
var p2x = turn.char2.position[0];
var p2y = turn.char2.position[1];

if (player === 'p1') {
if (direction === 'left') p1x--;
if (direction === 'right') p1x++;
if (direction === 'top') p1y--;
if (direction === 'bottom') p1y++;
}

if (player === 'p2') {
if (direction === 'left') p2x--;
if (direction === 'right') p2x++;
if (direction === 'top') p2y--;
if (direction === 'bottom') p2y++;
}

game.turns.push(createTurn([p1x, p1y], [p2x, p2y]));

gameManager.saveGame(game).then(function() {
res.redirect('/game/' + game.id);
});
});
});

app.listen(3000);
app.listen(settings.port);
}

module.exports = init;
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@
"license": "AGPL-3.0",
"main": "index.js",
"dependencies": {
"bluebird": "^2.2.2",
"body-parser": "^1.5.2",
"dotenv": "^0.4.0",
"express": "^4.7.2",
"glob": "^4.0.5",
"jade": "^1.5.0",
"mkdirp": "^0.5.0",
"node-uuid": "^1.4.1"
},
"devDependencies": {},
Expand Down
16 changes: 13 additions & 3 deletions templates/webserver.jade
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,32 @@ style
| .debug h2 {
| font-size: 16px;
| }
| .debug .infos {
| margin-top: 20px;
| }

mixin command(value, label)
button(type='submit', name='command', value=value)
= label

body(style='width:600px;margin:50px auto;background:black')
form(action='/command', method='post')
form(action='/game/'+ game.id +'/command', method='post')
div.debug
div.infos
| Game ID:
=game.id
div.p1
h2 Amthyst
h2
| Amthyst
=' ('+game.players.p1.email+')'
+command('p1-left', '←')
+command('p1-bottom', '↓')
+command('p1-top', '↑')
+command('p1-right', '→')
div.p2
h2 Dr. Black
h2
| Dr. Black
=' ('+game.players.p2.email+')'
+command('p2-left', '←')
+command('p2-bottom', '↓')
+command('p2-top', '↑')
Expand Down

0 comments on commit c4983b0

Please sign in to comment.