PHIG: A Platform for Hosting Infinite Generativity
A modular, real-time multiplayer, language-based game platform and bootcamp final project.
Context
By the time I got to the end of my time at Lighthouse Labs’ Web Development bootcamp, I’d been exposed to the exciting possibilities of websockets for multi-user simultaneity in a chat context and was dreaming up silly applications like programmatically enforce participants to write in iambic pentameter with syllable-checking libraries. My educational background was in English literature, my drive was for building teaching tools, and my inclination was to create some sort of game that could combine all of these things. Gratifyingly, I was able to find two cohort-mates who were at least mildly entertained by my vision and were willing to collaborate.
We became The Standard Deviants:
- Emily McMinn (front-end, back-end)
- Alfred Law (database, back-end)
- … and myself (back-end, game modules)
“PHIG” was the acronym for “Platform for Hosting Infinite Generativity”: rather than build a single game, our goal was to build a platform on which rules modules could be hosted, allowing users to mix and match game-rounds for their friends; infinite generativity is the concept of infinite valid combinations arising from finite components (which I’d first learned about with reference to words and language: for example, it’s possible to write an infinite number of different, “valid” novels1 using the same vocabulary).
As we hashed over how this might take shape, we ultimately took our inspiration from BeZerk’s flash-game, Acrophobia and JackBox-style party games. By demo-day we had two working game modes, managed to convince many members of the audience to bring laptops, and hosted a couple of rounds with about twenty participants: seeing the leaderboard shuffle madly while hearing cries of triumph and dismay pepper the viewers was glorious. It kills me that we don’t have footage.
Notable Features
- WebSockets for real-time, simultaneous, interactive play using the
ws
library - Two game modules implemented: Synonyms and Rhymes
- Automated answer-bank generation for nearly limitless replayability
- Both modules use the
random-words
library to generate seed-words that are relatively common in English. - The Rhymes module uses the DataMuse API to populate a bank of valid rhymes
- The Synonyms module originally used the
cheerio
web-scraping library to navigate the DOM of thesaurus.com queries. This was an exciting but brittle approach, and in returning to this project in 2023, it was necessary to switch over to the DataMuse API for better sustainability.
- Both modules use the
- Persistent Daily Scoreboards for each game type using MongoDB.
- Amusing, auto-generated (but consistent-by-name) avatars for players originally using the adorable.io Avatars API (now defunct, alas), now updated to the DiceBear Thumbs API.
- The Tim Johns Confetti Period follows each round with a magnitude of celebration proportional to how well players scored. This feature was added at the request of a beloved Lighthouse Labs mentor who discovered the
react-confetti
library and invited us to use it. - A dynamic real-time scoreboard using the
react-flip-move
library that re-arranges itself during frantic game play for Heightened Emotional Impact, etc.
Operation
⚠️ Warning: This project was written in 2018 and uses dependencies with versions from that time. Installing those dependencies raises several vulnerability alerts in 2023, but updating them, tracking down breaking changes, and getting everything up and running again is currently beyond the scope of this restoration-for-demonstration-purposes. Therefore, this project is not secure. Beyond enjoying it with friends, please do not attempt to host it perpetually, especially on sensitive or personal hardware.
Requirements
- Node.js (here running on
18.12.1
, but could probably run on older) - MongoDB (here running on
4.2.13
, but could probably run on newer)
Installation
Note: the structure of the project is a bit weird in that it contains both the server and client components in
./server
and./client
respectively. Each of these components has their ownpackage.json
and will need to be running during operation. The instructions below will walk you through the process.
Clone the repository:
$ git clone https://github.com/AllegroFox/standard-deviants.git phig $ cd phig
Install dependencies for both the components. It will be necessary to ignore dependency vulnerability warnings.
phig $ cd server
phig/server $ npm install
# ...
phig/server $ cd ../client
phig/client $ npm install
Configuration
Optionally: You can set which game modules will play by uncommenting the desired playstyle in
server/game/Room.js
at the beginning ofstartGetReady()
:// server/game/Room.js // ... // ############################# // ############################# // Round Lifecycle Controllers // ############################# // ############################# async startGetReady() { // Normal, random mode: // this.round = new this.gameModes[Math.floor(Math.random() * this.gameModes.length)](this.messager); // Rhyme-mode only, for n3wbz: // this.round = new RoundRhymes(this.messager); // Synonym-mode only, for l33tz: // this.round = new RoundSynonyms(this.messager); // Alternating modes: this.round = (this.roundNumber % 2) ? new RoundRhymes(this.messager) : new RoundSynonyms(this.messager);
Start
Note: MongoDB must be running during operation so that high scores can be checked and persisted. If it’s not currently running as a background process, it can be started in a terminal with the command
mongod
.
To start the game, both the server and the client need to be running at the same time. The easiest way to do this is to run the startup script in client/package.json
:
phig/client $ npm start
In addition to starting up the game, this will open a browser window on the local machine to the client at localhost:3000
.
Multiplayer and Hosting
The game isn’t much fun alone. If there are players on your local network, they can join you by navigating to your local IP address at port 3000
. (For example, 192.168.2.15:3000
).
At present, playing online is possible, but it takes some doing. Basic tunnelling services like localtunnel don’t support websockets, and while ngrok does, a paid account is necessary to host the two tunnels needed to expose the client and the server. The following is the solution I’ve found (with much gratitude to present-day Tim Johns and Brian Estany for helping me get here).
Additional Requirements for Hosting Online
- ngrok installed (a free account is fine)
- Three terminal windows (for maximum impressiveness)
Procedure
Before starting the server or client, in the first of your three terminal windows, run ngrok in Transmission Control Protocol-mode, pointing at PHIG’s server’s port (default
3001
):$ ngrok tcp 3001 ngrok (Ctrl+C to quit) Visit http://localhost:4040/ to inspect, replay, and modify your req Session Status online Account Some Account Name (Plan: Free) Version 3.0.5 Region United States (us) Latency 32ms Web Interface http://127.0.0.1:4040 Forwarding tcp://2.tcp.ngrok.io:10830 -> localhost Connections ttl opn rt1 rt5 p50 0 0 0.00 0.00 0.00
This will make a websocket-compatible tunnel available for the server, once it starts.
Copy the forwarding address without its protocol prefix shown in the resulting ngrok status readout. In the example above, this would be
2.tcp.ngrok.io:10830
.NOTE: The address will change every time you restart ngrok unless you have a paid account.
In
client/src/config/hosting-config.json
, set theremote_address
key to that forwarding address:{ "remote_address": "2.tcp.ngrok.io:10830" }
In the second of your three terminal windows, start the PHIG application:
phig/client $ npm start
In the third of your three terminal windows, use localtunnel to expose the PHIG client with either the
npx
application runner or a local installation, as desired:NOTE: You will need your local IP address for localtunnel’s
--local-host
parameter. You can grab it quickly withhostname -I
.NOTE: localtunnel allows you to request a
--subdomain
, which will provide a more convenient forwarding address if available. In the example below,phig
is used.$ hostname -I 192.168.X.Y $ npx localtunnel --local-host 192.168.X.Y --port 3000 --subdomain phig your url is: https://phig.loca.lt
It will be necessary to share the insecure
HTTP
version of the provided address (http://phig.loca.lt
, rather thanhttps://phig.loca.lt
in the example above). Testing has shown that it’s often helpful for those who wish to join to be using Chrome (as Firefox seems to resist connecting to the insecure protocol). With all that out of the way, have fun!
“Novel” being a satisfyingly apropos medium for this example, though “reportedly”, I could’ve “essayed” a different one with similar success.
</twee-word-nerdery>
↩︎