Each time I get a new computer I convince myself to install Unity, as I dream of building my indie game ideas. Since that will never happen, I decided to take the tools I know and apply it to something I wish I knew. The real inspiration for this post came from a recent conversation with an actual game developer. I wanted to demonstrate how Payload CMS could be used as the Admin tool and API for managing a video game.
With the right CMS built up, we can enable admin users to manage players, games and achievements as well as completing in-app purchases and handle authentication and access control. Since Payload CMS is headless, an online game can call the API to perform all the needed backend functionality.
All the code written for this guide can be found on Github. It only includes the admin portion of the project. As for now the 'game' is as made-up as that really cool kickstarter project you funded back in 2012.
The features for the admin panel of our fictional game are:
First, let's go over the collections at a high level:
Admins - Can log in to the admin UI and do most things Players - User accounts that can also hold player stats and unlock achievements and make in-app purchases Games - Contain teams and players along with scores, when saved player stats are updated Achievements - Admins can manage achievements and set thresholds needed to earn them In-App Purchases - The things players will be able to purchase Player Purchases - Who, what and how much it cost Images - File upload to store images for In-App purchases and Achievements
If you haven't developed with Payload CMS before then you'll be surprised by how little effort it takes to add and change fields in your app. Simply providing the config will build the admin UI as well as the APIs and manage the database.
Now the fields for our game collection are made up of an array of teams, with an array of players so that player scores are tracked independently of the team score. For the computer science nerds, that is what we call a two-dimensional array. We'll later use this scoring to assign experience points and achievements.
You could easily add more interesting stats beyond simple scores for other things happening in game and allow users to unlock more achievements. Adding them here as well as in the achievement collection as new type options would allow admins that ability.
The types that you see in the code are all generated based on the config using Payload's type generation feature. You may also notice in my code there are a few places I like to use Partial<{...}>
which allows me to be a little less strict and get work done.
Another feature that will get you going ultra-fast with Payload is authentication. By setting auth: true
in the collection, we can take the defaults for how user authentication works for our players.
Now that we have an authenticated a user, let's see how we can implement banning a player. It should come as no surprise that we have a field on the players to be managed by admins.
That gives the admin UI a checkbox in the sidebar to ban the player.
It doesn't do anything yet. To make Payload refuse our banned players on login, we can add the after login hook to keep them out!
Payload has an APIError that is useful for returning errors with status codes which we can see in action when we try to login with a banned player.
Another point about authentication is that in the admins collection, we've turned on useAPIKey
. What that allows you to do is generate an API key that your game or any other third party could use to call the API with the full privileges of being an Admin. This is how our fictional game will call the API to store game scores and other actions.
Before we get in to how achievements work, take a look at the admin experience for adding them and assigning the parameters with which they'll be awarded.
An admin can use this interface to create new in-game achievements complete with image uploads to be served on the web or the game interface.
I want to dive in to how our game takes game data, which we saw the structure for above. When a game is created we have an after-change hook which will do the heavy lifting of updating the player stats and awarding achievements. An admin can view a player with some experience in the Admin UI and see their stats and achievements.
Notice that the fields are all disabled since we have it set to read only, that is because we want hooks to manage this for us. Let's review the code for the hook that automates this process.
Each new game will call this code in the hook that updates players with new stats and achievements, all dynamically. Here is the function that to add up the stats, achievements and update the players.
The last delicious detail is in how player achievements are filtered. As long as we have player stats that match the achievement.type
it will just work.
From an admin perspective, the purchasable items look much the same as achievements except that they also have a price field.
The relationships for the purchases are slightly different from achievements in that it has been given its own collection instead of living in the player document. First, we can better segment our data as we won't always want to have it on the player. Second, it gives us a place to add more data like the amount paid and the Stripe charge id which we didn't need for achievements.
When a new player registers we are calling on Stripe to create a customer ID so that we're ready to handle any purchases in the game.
Then as a player purchase request comes in, it must have the token for the payment from stripe in order to complete the request.
The code presented in this article and the accompanying repository was written in a matter of two days and shouldn't be considered production ready without testing.
Give us a shout if you liked this guide on Twitter @payloadcms or in our Discord!
If you want to hire me for game development you should absolutely talk to somebody else.