From c4983b0fa460c7dfa13a26ca9692b0eec53cb2a1 Mon Sep 17 00:00:00 2001 From: Pierre Bertet Date: Sun, 3 Aug 2014 23:11:47 +0100 Subject: [PATCH] Game manager The game states are saved in JSON files, in the data/games/active directory. --- .gitignore | 1 + index.js | 28 ++++++------ lib/game-manager.js | 57 +++++++++++++++++++++++ lib/game.js | 23 +++++----- lib/renderers/html.js | 1 - lib/webserver.js | 99 ++++++++++++++++++++++++++++------------ package.json | 3 ++ templates/webserver.jade | 16 +++++-- 8 files changed, 170 insertions(+), 58 deletions(-) create mode 100644 lib/game-manager.js diff --git a/.gitignore b/.gitignore index 2fad9d5..6f110de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ /.env +/data/ diff --git a/index.js b/index.js index 469106e..924ccc1 100644 --- a/index.js +++ b/index.js @@ -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)); diff --git a/lib/game-manager.js b/lib/game-manager.js new file mode 100644 index 0000000..2adeee0 --- /dev/null +++ b/lib/game-manager.js @@ -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; +}; diff --git a/lib/game.js b/lib/game.js index 465d364..c7b019e 100644 --- a/lib/game.js +++ b/lib/game.js @@ -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]; }; @@ -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; @@ -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; + } }; diff --git a/lib/renderers/html.js b/lib/renderers/html.js index 094987e..e2161bd 100644 --- a/lib/renderers/html.js +++ b/lib/renderers/html.js @@ -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, diff --git a/lib/webserver.js b/lib/webserver.js index 6eeccc7..690609d 100644 --- a/lib/webserver.js +++ b/lib/webserver.js @@ -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 @@ -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 = '\n'; + html += '\n'; + html += '
\n'; + html += '

\n'; + html += '
\n'; + html += '

Active games:

\n'; + html += '\n'; + html += '\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; diff --git a/package.json b/package.json index d2bf01c..4654396 100644 --- a/package.json +++ b/package.json @@ -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": {}, diff --git a/templates/webserver.jade b/templates/webserver.jade index d650ac3..d6f5637 100644 --- a/templates/webserver.jade +++ b/templates/webserver.jade @@ -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', '↑')