]> git.cworth.org Git - empires-server/blobdiff - game.js
game: Rename Game.clients to Game.players, combining multiple connections
[empires-server] / game.js
diff --git a/game.js b/game.js
index 56ee56c8fa5e013991f676abfbbd8b51889994ff..baf1e557df502cc28f775f3540fa5f14d55102c8 100644 (file)
--- a/game.js
+++ b/game.js
@@ -1,9 +1,48 @@
+/* A single player can have multiple connections, (think, multiple
+ * browser windows with a common session cookie). */
+class Player {
+  constructor(id, session_id, name, connection) {
+    this.id = id;
+    this.session_id = session_id;
+    this.name = name;
+    this.connections = [connection];
+  }
+
+  add_connection(connection) {
+    /* Don't add a duplicate connection if this player already has it. */
+    for (let c of this.connections) {
+      if (c === connection)
+        return;
+    }
+
+    this.connections.push(connection);
+  }
+
+  /* Returns the number of remaining connections after this one is removed. */
+  remove_connection(connection) {
+    this.connections.filter(c => c !== connection);
+    return this.connections.length;
+  }
+
+  /* Send a string to all connections for this player. */
+  send(data) {
+    this.connections.forEach(connection => connection.write(data));
+  }
+
+  info_json() {
+    return JSON.stringify({
+      id: this.id,
+      name: this.name,
+    });
+  }
+}
+
 /* Base class providing common code for game engine implementations. */
 class Game {
   constructor(id) {
     this.id = id;
-    this.clients = [];
-    this.next_client_id = 1;
+    this.players = [];
+    this.next_player_id = 1;
 
     /* Send a comment to every connected client every 15 seconds. */
     setInterval(() => {this.broadcast_string(":");}, 15000);
@@ -29,25 +68,57 @@ class Game {
     return this._meta;
   }
 
-  add_client(response) {
-    const id = this.next_client_id;
-    this.clients.push({id: id,
-                       response: response});
-    this.next_client_id++;
+  add_player(session, connection) {
+    /* First see if we already have a player object for this session. */
+    const existing_index = this.players.findIndex(
+      player => player.session_id === session.id);
+    if (existing_index >= 0) {
+      const player = this.players[existing_index];
+      player.add_connection(connection);
+      return player;
+    }
+
+    /* No existing player. Add a new one. */
+    const id = this.next_player_id;
+    const player = new Player(id, session.id, session.nickname, connection);
+
+    /* Broadcast before adding player to list (to avoid announcing the
+     * new player to itself). */
+    const player_data = JSON.stringify({ id: player.id, name: player.name });
+    this.broadcast_event("player-enter", player_data);
+
+    this.players.push(player);
+    this.next_player_id++;
+
+    return player;
+  }
+
+  find_player(session) {
+    const existing_index = this.players.findIndex(
+      player => player.session_id === session.id);
+    if (existing_index >= 0)
+      return this.players[existing_index];
 
-    return id;
+    return null;
   }
 
-  remove_client(id) {
-    this.clients = this.clients.filter(client => client.id !== id);
+  /* Drop a connection object from a player, and if it's the last one,
+   * then drop that player from the game's list of players. */
+  remove_player_connection(player, connection) {
+    const remaining = player.remove_connection(connection);
+    if (remaining === 0) {
+      const player_data = JSON.stringify({ id: player.id });
+      this.players.filter(p => p !== player);
+      this.broadcast_event("player-exit", player_data);
+    }
   }
 
-  /* Send a string to all clients */
+  /* Send a string to all players */
   broadcast_string(str) {
-    this.clients.forEach(client => client.response.write(str + '\n'));
+    this.players.forEach(player => player.send(str + '\n'));
   }
 
-  /* Send an event to all clients.
+  /* Send an event to all players.
    *
    * 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).
@@ -65,12 +136,12 @@ class Game {
     };
     response.writeHead(200, headers);
 
-    /* Add this new client to our list of clients. */
-    const id = this.add_client(response);
+    /* Add this new player. */
+    const player = this.add_player(request.session, response);
 
     /* And queue up cleanup to be triggered on client close. */
     request.on('close', () => {
-      this.remove_client(id);
+      this.remove_player_connection(player, response);
     });
 
     /* Give the client the game-info event. */