Build an online multiplayer game using peer-to-peer and realtime messaging
It is September 2020 and we’re still mid pandemic, which means I’m social distancing and missing my friends. At the start of lockdown I played a lot of online multiplayer party games with friends to try and replace our regular board game hangouts. After consuming so many, I decided it was high time that I try and make a multiplayer game myself! Working at Ably, I have all of their robust pub/sub real-time messaging infrastructure right there, perfect for creating a system that can send data between multiple players!
What is pub/sub and how can I use it to build a game?
Pub/Sub is short for Publish/Subscribe and it describes a pattern of real-time message exchange between ‘publishers’ and ‘subscribers’. Publishers create and send messages to a ‘channel’. Subscribers are then able to receive messages from the channel by subscribing to it. There can be multiple publishers and multiple subscribers, all of which are decoupled - the publishers do not have knowledge of how many subscribers there are. This is great for me, being the “frontendest of frontenders” (a quote from a friend of mine), I have a way to easily get data between players without having to implement it myself. I can use the pub/sub pattern to build a peer-to-peer game, meaning that I don’t have to set up a central server. Even better!
What is Peer-to-Peer and why is it helpful here?
Peer-to-peer (p2p) refers to a network of computers (and/or devices) that share and exchange workloads. These computers, or peers, send messages to each other which control the game state. This differs from client-server architecture where one central machine controls the whole game.
- No need for a central server, making setup easier and cheaper
- Can easily handle a game with up to 10 players
- Easy way to share data between players
- Server error can’t take down the entire game
- Need to manage the loss of a ‘peer’ (due to network outage, rage quit etc) or this could break the game flow
- Possibility for users to cheat by altering the data they’re sending
In order to make state management of the game slightly simpler, I’m going to make one of the peers elect themselves the ‘host’ of the game, the host will initialise a game, invite peers to join and then coordinate the communication between the peers.
What can I make with pub/sub and p2p?
Simple multiplayer games like Pictionary, Codenames, Taboo, Cards Against Humanity, Pass the Pen are all perfect for this architecture —they all have clearly defined rules and steps which players follow to change the state of the game.
I made a game where players illustrate something from a card prompt and other players have to write down a caption for their drawing. The round of the game each player draws from the previous player’s caption and so on. I called it Depict-It. It was based on a game called Eat Poop you Cat.
Defining the rules
Defining the rules and steps of Depict-It makes it easier to build out the gameplay in code, so let’s do that here:
- A host creates a game and receives a share link to invite others.
- Other players join the game.
- Once all players are in, the host starts the game.
- Players are each sent a ‘prompt’ card with a phrase on it.
- Players draw their prompt on an HTML canvas, they are given 180 seconds to do this.
- Players’ drawings are passed along to the person who joined after them, except for the last player, who’s drawing is passed to the host.
- Players then have to enter a ‘caption’ for that picture into a textbox, they are given 180 seconds to do so.
- Steps 6 and 7 repeat until each player has received the ‘stack’ of drawings from their original prompt.
- The stacks of drawings and captions are shown to each player to vote on which submission was their favourite.
- Votes are counted and shown to players.
- Host is offered a prompt to start a new game.
Defining the state from the rules
From the list of rules, it becomes clearer what state the game might need:
There is an initial state which counts the number of players and keeps track of who is host. Then, the game splits into the following phases (which each have an input and a response); dealing, drawing, captioning and voting.
There is a software pattern called a ‘State Machine’ which can be used to model a system that can exist in one of several known states. When a player or the game makes a change or provides an input, the state machine does logical checks on what do do next. The game is built on a collection of ‘handlers’ (which are just blocks of code), that the state machine switches between to execute the entire game.
When players join the game they are connected to an Ably channel, on which they can send and receive messages. As the game phases are moved through, and the state updates, these messages keep all of the players’ browsers in sync and their games up to date with each other. Below is a table of the game phases and the messages that are sent:
Each phase of the game will have a “completion condition”, which will be used to make decisions. For example, ‘When all of the players have finished drawing…” or “When all of the players have voted for their favourite submission…”.
The messages that are sent have both a ‘kind’ and a ‘value’. The ‘kind’ lets our code know to process the message differently from the initial connection messages. The ‘value’ is the payload that the player needs in order for the game to continue, be it a drawing (in the form of a URL) or a caption (a string of text).
The coolest part about peer-to-peer and pub/sub is that we can use promises to pause game execution while waiting on the completion conditions. For example, while players are drawing, the game checks ‘have I had a number of drawings submitted that equals the total number of players?’ Until that statement returns true, the game will not continue. Every time a player submits a drawing, a message is sent on the Ably channel which increments the number of submitted drawings. Once the statement has returned true, the next phase of the game can be executed.
You can read more about how the state machine, handlers and messages work in the README of the project and see the code in the repo: https://github.com/ably/depict-it
Writing the game’s user interface
The game UI is written using Vue.js. This framework allowed me to split the different game phases into separate Vue Components. Components can have Vue Directives which tell the library what to do with the component, for example: whether or not to render it, or to bind data to it.
With Vue I could easily create UI elements which are shown only to the host by using the Vue.js syntax:
v-if:isHost. This checks the state for whether the player is the host and evaluates which UI elements to display. Building up the components in this way enabled me to check each game phase in the state and show the correct UI elements accordingly.
Vue.js also has a methods property, which can be used to handle events, such as implementing click handlers. The syntax looks like this:
v-on:click=function(). This allowed me to bind functions to events that components can raise as the game phases progress.
This ‘componentization’ made the markup of the game nice and neat, since it allowed me to split each component into a separate file and use the browser’s native import module syntax to import them when needed.
You can read more about how the game UI works in the project’s README.
Extending the game
There are still many improvements that could be made to the game and extensions on how the gameplay could work. It might be fun to let the players suggest captions at the start of the game, or give them an interface to print or save the progressions to their machine so that they can laugh at them in the future. It might also be a good idea to limit the number of players in a game and split the overflow players into a separate game so that round doesn’t get too long. The UI could also do with a better explanation for players new to the game. There are lots of ways the game could be improved, and I leave it in your capable hands! If you’d like to add anything, or make the game your own I’d love to see it! You can make a PR on the repo or fork it and make it entirely your own! I am also definitely open to funny prompt suggestions!
Sharing the lols
If you play a game of Depict-It you can share your card progressions to Twitter, and follow other’s silly progressions on our Twitter feed: https://twitter.com/DepictItGame
I hope you’ll enjoy playing the game as much as I enjoyed building it, I also hope that the write up and the project will inspire you to make your own multiplayer game!
I can’t wait to see all your silly drawings! 😂