1 const express = require("express");
2 const cors = require("cors");
3 const body_parser = require("body-parser");
11 this.next_player_id = 1;
13 this.next_client_id = 1;
16 add_player(name, character) {
17 const new_player = {id: this.next_player_id,
22 this._players.push(new_player);
23 this.next_player_id++;
24 /* The syntax here is using an anonymous function to create a new
25 object from new_player with just the subset of fields that we
27 const player_string = JSON.stringify((({id, name}) => ({id, name}))(new_player));
28 this.broadcast("player-register", player_string);
32 const index = this._players.findIndex(player => player.id === id);
33 this._players.splice(index, 1);
35 this.broadcast("player-deregister", `{"id": ${id}}`);
38 remove_all_players() {
40 this.next_player_id = 1;
43 capture(captor_id, captee_id) {
44 /* TODO: Fix to fail on already-captured players (or to move the
45 * captured player from an old captor to a new—need to clarify in
46 * the API specification which we want here. */
47 let captor = this._players.find(player => player.id === captor_id);
48 captor.captures.push(captee_id);
50 this.broadcast("capture", `{"captor": ${captor_id}, "captee": ${captee_id}}`);
54 let captor = this._players.find(player => player.captures.includes(captee_id));
55 captor.captures.splice(captor.captures.indexOf(captee_id), 1);
59 for (const player of this._players) {
65 return this._players.map(player => player.character);
69 return this._players.map(player => ({id: player.id, captures: player.captures}));
73 return this._players.map(player => ({id: player.id, name: player.name }));
76 add_client(response) {
77 const id = this.next_client_id;
78 this.clients.push({id: id,
80 this.next_client_id++;
86 this.clients = this.clients.filter(client => client.id !== id);
89 /* Send an event to all clients */
90 broadcast(type, data) {
91 this.clients.forEach(client => client.response.write(`event: ${type}\ndata: ${data}\n\n`));
95 const game = new Game();
97 app.use(body_parser.urlencoded({ extended: false }));
98 app.use(body_parser.json());
100 function handle_events(request, response) {
101 /* These headers will keep the connection open so we can stream events. */
103 "Content-type": "text/event-stream",
104 "Connection": "keep-alive",
105 "Cache-Control": "no-cache"
107 response.writeHead(200, headers);
109 /* Now that a client has connected, first we need to stream all of
110 * the existing players (if any). */
111 if (game._players.length > 0) {
112 const players_json = JSON.stringify(game.players);
113 const players_data = `event: players\ndata: ${players_json}\n\n`;
114 response.write(players_data);
117 /* Add this new client to our list of clients. */
118 const id = game.add_client(response);
120 /* And queue up cleanup to be tirggered on client close. */
121 request.on('close', () => {
122 game.remove_client(id);
126 app.get('/', (request, response) => {
127 response.send('Hello World!');
130 app.post('/register', (request, response) => {
131 game.add_player(request.body.name, request.body.character);
135 app.post('/deregister/:id', (request, response) => {
136 game.remove_player(parseInt(request.params.id));
140 app.post('/reset', (request, response) => {
141 game.remove_all_players();
145 app.post('/capture/:captor/:captee', (request, response) => {
146 game.capture(parseInt(request.params.captor), parseInt(request.params.captee));
150 app.post('/liberate/:id', (request, response) => {
151 game.liberate(parseInt(request.params.id));
155 app.post('/restart', (request, response) => {
156 game.restart(parseInt(request.params.id));
160 app.get('/characters', (request, response) => {
161 response.send(game.characters);
164 app.get('/empires', (request, response) => {
165 response.send(game.empires);
168 app.get('/players', (request, response) => {
169 response.send(game.players);
172 app.get('/events', handle_events);
174 app.listen(3000, function () {
175 console.log('Example app listening on port 3000!');