X-Git-Url: https://git.cworth.org/git?p=empires-server;a=blobdiff_plain;f=empires.js;h=839f5f8be8dc56460b4c6165d4378d99de56f095;hp=7907ce0c80a7711d4c92044f7391298156c2a664;hb=HEAD;hpb=eec2fd5c7db6f39a9994fa620619ebf005d26064 diff --git a/empires.js b/empires.js index 7907ce0..839f5f8 100644 --- a/empires.js +++ b/empires.js @@ -1,8 +1,7 @@ const express = require("express"); +const Game = require("./game.js"); -const router = express.Router(); - -const GameState = { +const GamePhase = { JOIN: 1, REVEAL: 2, CAPTURE: 3, @@ -30,19 +29,15 @@ function shuffle(a) { } } -class Game { - constructor() { +class Empires extends Game { + constructor(id) { + super(id); this._spectators = []; this.next_spectator_id = 1; this._players = []; this.next_player_id = 1; this.characters_to_reveal = null; - this.clients = []; - this.next_client_id = 1; - this.state = GameState.JOIN; - - /* Send a comment to every connected client every 15 seconds. */ - setInterval(() => {this.broadcast_string(":");}, 15000); + this.phase = GamePhase.JOIN; } add_spectator(name, session_id) { @@ -70,7 +65,7 @@ class Game { this.broadcast_event("spectator-leave", `{"id": ${id}}`); } - add_player(name, character) { + register_player(name, character) { const new_player = {id: this.next_player_id, name: name, character: character, @@ -83,6 +78,8 @@ class Game { want. */ const player_data = JSON.stringify((({id, name}) => ({id, name}))(new_player)); this.broadcast_event("player-join", player_data); + + return new_player; } remove_player(id) { @@ -97,15 +94,15 @@ class Game { this.characters_to_reveal = null; this.next_player_id = 1; - this.change_state(GameState.JOIN); + this.change_phase(GamePhase.JOIN); this.broadcast_event("spectators", "{}"); this.broadcast_event("players", "{}"); } reveal_next() { - /* Don't try to reveal anything if we aren't in the reveal state. */ - if (this.state != GameState.REVEAL) { + /* Don't try to reveal anything if we aren't in the reveal phase. */ + if (this.phase != GamePhase.REVEAL) { clearInterval(this.reveal_interval); return; } @@ -122,7 +119,7 @@ class Game { } reveal() { - this.change_state(GameState.REVEAL); + this.change_phase(GamePhase.REVEAL); if (this.characters_to_reveal === null) { this.characters_to_reveal = []; @@ -139,7 +136,7 @@ class Game { } start() { - this.change_state(GameState.CAPTURE); + this.change_phase(GamePhase.CAPTURE); } capture(captor_id, captee_id) { @@ -179,118 +176,81 @@ class Game { })); } - get players() { + /* The base class recently acquired Game.players which works like + * Empires.spectators, (and meanwhile the Empires._players + * functionality could perhaps be reworked into + * Game.players[].team). Until we do that rework, lets use + * .registered_players as the getter for the Empires-specific + * ._players property to avoid mixing it up with the distinct + * Game.players property. */ + get registered_players() { return this._players.map(player => ({id: player.id, name: player.name })); } - add_client(response) { - const id = this.next_client_id; - this.clients.push({id: id, - response: response}); - this.next_client_id++; - - return id; - } - - remove_client(id) { - this.clients = this.clients.filter(client => client.id !== id); - } - - /* Send a string to all clients */ - broadcast_string(str) { - this.clients.forEach(client => client.response.write(str + '\n')); - } - - /* Send an event to all clients. - * - * An event has both a declared type and a separate data block. - * It also ends with two newlines (to mark the end of the event). - */ - broadcast_event(type, data) { - this.broadcast_string(`event: ${type}\ndata: ${data}\n`); - } - - game_state_event_data(old_state, new_state) { - var old_state_name; - if (old_state) - old_state_name = GameState.properties[old_state].name; + game_phase_event_data(old_phase, new_phase) { + var old_phase_name; + if (old_phase) + old_phase_name = GamePhase.properties[old_phase].name; else - old_state_name = "none"; - const new_state_name = GameState.properties[new_state].name; + old_phase_name = "none"; + const new_phase_name = GamePhase.properties[new_phase].name; - return `{"old_state":"${old_state_name}","new_state":"${new_state_name}"}`; + return `{"old_phase":"${old_phase_name}","new_phase":"${new_phase_name}"}`; } - /* Inform clients about a state change. */ - broadcast_state_change() { - const event_data = this.game_state_event_data(this.old_state, this.state); - this.broadcast_event("game-state", event_data); + /* Inform clients about a phase change. */ + broadcast_phase_change() { + const event_data = this.game_phase_event_data(this.old_phase, this.phase); + this.broadcast_event("game-phase", event_data); } - /* Change game state and broadcast the change to all clients. */ - change_state(state) { + /* Change game phase and broadcast the change to all clients. */ + change_phase(phase) { /* Do nothing if there is no actual change happening. */ - if (state === this.state) + if (phase === this.phase) return; - this.old_state = this.state; - this.state = state; + this.old_phase = this.phase; + this.phase = phase; - this.broadcast_state_change(); + this.broadcast_phase_change(); } -} -function handle_events(request, response) { - const game = request.game; - /* These headers will keep the connection open so we can stream events. */ - const headers = { - "Content-type": "text/event-stream", - "Connection": "keep-alive", - "Cache-Control": "no-cache" - }; - response.writeHead(200, headers); - - /* Now that a client has connected, first we need to stream all of - * the existing spectators and players (if any). */ - if (game._spectators.length > 0) { - const spectators_json = JSON.stringify(game.spectators); - const spectators_data = `event: spectators\ndata: ${spectators_json}\n\n`; - response.write(spectators_data); - } + handle_events(request, response) { - if (game._players.length > 0) { - const players_json = JSON.stringify(game.players); - const players_data = `event: players\ndata: ${players_json}\n\n`; - response.write(players_data); - } + super.handle_events(request, response); - /* And we need to inform the client of the current game state. - * - * In fact, we need to cycle through each state transition from the - * beginning so the client can see each. - */ - var old_state = null; - for (var state = GameState.JOIN; state <= game.state; state++) { - var event_data = game.game_state_event_data(old_state, state); - response.write("event: game-state\n" + "data: " + event_data + "\n\n"); - old_state = state; - } + /* Now that a client has connected, first we need to stream all of + * the existing spectators and players (if any). */ + if (this._spectators.length > 0) { + const spectators_json = JSON.stringify(this.spectators); + const spectators_data = `event: spectators\ndata: ${spectators_json}\n\n`; + response.write(spectators_data); + } + + if (this._players.length > 0) { + const players_json = JSON.stringify(this.registered_players); + const players_data = `event: players\ndata: ${players_json}\n\n`; + response.write(players_data); + } - /* Add this new client to our list of clients. */ - const id = game.add_client(response); + /* And we need to inform the client of the current game phase. + * + * In fact, we need to cycle through each phase transition from the + * beginning so the client can see each. + */ + var old_phase = null; + for (var phase = GamePhase.JOIN; phase <= this.phase; phase++) { + var event_data = this.game_phase_event_data(old_phase, phase); + response.write("event: game-phase\n" + "data: " + event_data + "\n\n"); + old_phase = phase; + } + } - /* And queue up cleanup to be triggered on client close. */ - request.on('close', () => { - game.remove_client(id); - }); } -router.get('/', (request, response) => { - if (! request.session.nickname) - response.render('choose-nickname.html', { game_name: "Empires" }); - else - response.render('empires-game.html'); -}); +Empires.router = express.Router(); +const router = Empires.router; router.post('/spectator', (request, response) => { const game = request.game; @@ -318,8 +278,8 @@ router.post('/register', (request, response) => { if (request.body.name) name = request.body.name; - game.add_player(name, request.body.character); - response.send(); + const player = game.register_player(name, request.body.character); + response.send(JSON.stringify(player.id)); }); router.post('/deregister/:id', (request, response) => { @@ -381,11 +341,12 @@ router.get('/spectators', (request, response) => { router.get('/players', (request, response) => { const game = request.game; - response.send(game.players); + response.send(game.registered_players); }); -router.get('/events', handle_events); +Empires.meta = { + name: "Empires", + identifier: "empires" +}; -exports.router = router; -exports.name = "empires"; -exports.Game = Game; +exports.Game = Empires;