Multiplayer gaming remains colossal in the gaming industry. And why wouldn’t it be? To settle old scores, solve disputes, or even satisfy that competitive itch , battling it out online against other users is just as cathartic as it is entertaining.
This is why this Launchpad app has created its own real time strategy game, Pizza Tribes, that involves…wait for it… mice! The gameplay involves training a population of mice to bake and sell pizzas for coins, with the overarching objective being to generate more coins than any other player.
For all its creativity, this application wouldn’t be able to provide users with real time gameplay without Redis’ ability to transmit data between components efficiently. Any delays would have made real time gameplay impossible.
Let’s take a look at how this application was created. But before we go any further, we’d like to point out that we have an excellent range of applications that are having an impact on everyday life for you to check out on the Redis Launchpad.
You’ll build a multiplayer browser-based real time strategy game using Redis. Below we’ll go through each step in chronological order and outline all of the components that you’ll need to create this application.
Ready to get started? Ok, let’s dive straight in.
The application has an unconventional approach when it comes to client-server communication. This is because it relies heavily on web sockets to carry out responsibilities that would normally be fulfilled by an HTTP request/response.
Below is an example of a typical web-socket flow:
Since this application is reliant on Web-Sockets, we’re going to spend a bit more time going over the role that they play. Firstly, web socket communication relies heavily on Redis to send and retrieve messages.
The API does not do any game logic but simply validates client messages before pushing them to Redis. Meanwhile, the workers can be horizontally scaled whilst relying on Redis to push messages efficiently through the system.
Since Web Socket bidirectional lightweight communication protocol over a single TCP connection, it is not easy to scale the Web API (holding the sockets).
This solution attempts to minimize load on the Web API so that it can focus on shoveling data to the clients.
Note: messages are not sent between the API and workers using Redis Pub/Sub. Instead, Redis lists (RPush and BLPop) are used. One of the advantages of this is that the workers or the API can be restarted without losing messages, whereas with Redis Pub/Sub everything will be forgotten.
The worker will need to delay some tasks (e.g., finish construction of the building after 5 minutes). This is accomplished by carrying out updates to the Sorted set user_updates. The updater then pulls the top record of the sorted set which is determined by time. If the time has passed, it will remove the record from the set and will then update the game state of that user.
Below is a simplified typical flow:
i. Validates the command
ii. Updates the user game state:
iii. Discovers the next time the user game state needs to be updated (e.g. when the construction is completed)
iv. Sets the next updated time of the user game state:
3. An updater updates the user game state at the next update time by carrying out a number of commands:
i) Runs the following command to fetch the next user that needs to be updated (and at what time)
ii) If the score (timestamp) has been passed
a) Remove the next update time:
b) Perform game state update
c) Find next time the user game state needs to be updated again
d) Set next update time:
Protocol Buffers are used to define the messages that are sent between the client/server and the server/client.
Below is the full definition.
Below is the full definition.
Prerequisite:
There are two ways you can run the application locally. You can either run everything (Redis, services, web app) in a Docker container Or, you can pick and choose for faster development.
Clone the repository
git clone https://github.com/redis-developer/pizza-tribes
This is arguably the easiest way to run the project locally. To get started, you need to execute the docker-compose command as shown below:
This command will achieve the following:
When this command is carried out, the following is achieved:
If you want to make changes then you’ll benefit from Hot Module Replacement (HMR) in the web app. This will allow you to build the Go apps more efficiently. To achieve this, run Redis using docker-compose, and then run the services and web app on your host OS:
Once carried out, this command should give you:
Note: The web app will proxy calls to /api to http://localhost:8080 (see webapp/vite.config.ts).
The users are stored as a hash set in key user:{user_id} containing fields. These include:
The user_id can be looked up using the username via username:{username}.
Registration is achieved through the following commands:
Authentication is done like so:
Note: If the hashes match, create JWT
Using RedisJSON, the user game state is stored as a JSON value in the key user.
It does this with the following structure:
The game state is accessed in different ways depending on the use case. But for a complete retrieval, the following is used:
In other cases, a path is used to retrieve only a subset of the data:
The game state update is what makes the game tick and it’s one of the most important processes in the game. Its purpose is to:
In addition, the game state update will also:
The updater runs in a loop that queries a sorted set that’s called user_updates. It retrieves the top record in the sorted set by running the following command:
By utilizing WITHSCORES we also retrieve the timestamp of when that user needs a game state. As such, the updater can check if timestamp < now. If so, then the user can carry out the following commands:
2. Proceed to update the game state
Note: there is some level of risk involved because if the game state update fails, the user will no longer have a record in the user_updates sorted set. When this happens, no game state update will be scheduled.
To avoid this, the game will ensure that the user is scheduled for game state updates when logging in.
The update is executed with a check-and-set approach (WATCH, MULTI, EXEC). This is achieved through the following steps:
For more details of a simple game state update, see the trace below:
Note: Extrapolating resources, completing buildings, trainings and complete travels are all implemented using the flow described above.
When the game state has been updated, you’ll have to schedule the next one. Here’s how to do it:
The RedisTimeseries module is used to track the changes in user resources. The resources are tracked using the following keys:
When every game state update is carried out, a new data point is inserted into each key. Below is an example:
When the user wants to look at their resource history, the following command is used to retrieve the aggregated data points from the last 24 hours.
The game state update will change the number of coins a user has which is why we need to update the leaderboard. The leaderboard is a sorted set with the key leaderboard. It’s updated by running the following command:
When any user wants to have access to the leaderboard, the data is retrieved with the following command:
From a performance perspective, achieving real-time gameplay is one of the most important objectives behind creating a successful online multiplayer game. Failing to achieve this will drastically hamper the user’s experience, irrespective of how advanced other qualities of the game are.
Despite having a complicated architecture, Redis removed this obstacle through its ability to zip data between different components with ease. Having no lags, no delays, and no data setbacks whatsoever allowed this Launchpad App to create a complex yet engaging online multiplayer strategy game where users from around the world can battle it out against each other for the top spot.
If you want to get more of an insight into how this application was made then you may want to check out this YouTube video.
We should also let you know that we have an exciting range of game-changing applications (excuse the pun) on the Redis Launchpad. Here you’ll discover a collection of apps that are having an impact on everyday life by everyday programmers.
So make sure to check it out!
Matteus Hemström
From Matteus is a highly innovative software engineer who currently plies his trade at Nuway.
If you want to keep up to date with all of the projects he’s been involved with, then head over to his GitHub page here.