+ constructor(id) {
+ this.id = id;
+ this.players = [];
+ this.next_player_id = 1;
+
+ /* Send a comment to every connected client every 15 seconds. */
+ setInterval(() => {this.broadcast_string(":");}, 15000);
+ }
+
+ /* Suport for game meta-data.
+ *
+ * What we want here is an effectively static field that is
+ * accessible through either the class name (SomeGame.meta) or an
+ * instance (some_game.meta). To pull this off we do keep two copies
+ * of the data. But the game classes can just set SomeGame.meta once
+ * and then reference it either way.
+ */
+ static set meta(data) {
+ /* This allows class access (SomeGame.meta) via the get method below. */
+ this._meta = data;
+
+ /* While this allows access via an instance (some_game.meta). */
+ this.prototype.meta = data;
+ }
+
+ static get meta() {
+ return this._meta;
+ }
+
+ 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 null;
+ }
+
+ /* 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 players */
+ broadcast_string(str) {
+ this.players.forEach(player => player.send(str + '\n'));