Most tutorials I’ve seen for making games with PubNub simply transfer messages between clients. This means the game is putting complete trust in each client. This works and is a great way to learn, but in practice (especially with HTML5 games) it’s super easy for users to hack your game! All they have to do is change the code that broadcasts their position (or any other important data) and suddenly every other client thinks they can fly.

PubNub’s Functions and data store features allow us to turn a channel into a completely authoritative server.

I made a simple battle royale game and iOS client to demo the functionality. The code for that can be found at the bottom of this post in the Source Code section. I’ll be focusing on the PubNub side of things.

Players can join the game, wait until the game starts, and then attack each other. Here’s what it looks like.

Now for the code!

Function 1

The first thing we need to do is create a function that happens BEFORE a publish or fire. Here’s a quick tutorial on creating functions. We need this to happen before the messages publish so we can make sure the game state actually exists.

Creating the Game State and Adding Player States

For imports, by default all we’re given is kvstore and xhr . kvstore is what we use to store data and xhr handles http requests, which we don’t really need for this. We need to add pubnub so we can publish messages to the players.

The main concept for this authoritative strategy is to handle messages AFTER we get the gameState from the database. In every function, once we get the game state, we will use it to decide what to do and then update it at the end of the function.

Line 6 gets the message object from the response. We will do this in every function for convenience.

Line 8 will retrieve an object with the key “gameState” from the database and the .then callback will give us the data it retrieved as gameState . Now that we have the data, we need to make sure it exists.

Line 11 checks to see if the game state doesn’t exist (or if we sent a “reset-gameState” message which is really helpful for debugging).

Lines 13–18 will update the gameState variable with our default game state

Lines 26–29 will then push our new game state to the database.

Adding Players to the Game State

Now that we have our game state, we need to add players to it when they join. This will replace the TODO comment.

First we make sure that the message is a “join” message. If the player’s uuid doesn’t already exist in our playerStates object, we create a playerState for them. The player state can have anything about the player, like their username, stats etc. For this example, I just need to know if they are ready and what their health is.

Because the example game is a battle royale, I need a count of the total players. Later on, as people ready up, we will compare the ready players with this to tell when the game should start.

We then publish this new game state to all the players. We need to send the entire game state here to make sure this new player is up to date. This will then also update everyone else’s game state with this new player!

Function 2

We now need to create a function that happens AFTER a publish. This function will be the core of the game. It will handle player interactions and updating the game state. I’ll break this one into smaller chunks.

Setup

Let’s first setup the core of the function.

A lot of this looks the same as the last function.

Line 14 get’s the playerState for the player from the gameState

Lines 16–25 handle stray messages. A user might send a message at the wrong time if their client is a little behind or they might be dead trying to attack people.

return request.okay() will return a response and exit out of the function.

Lines 28–32 are the meat of our server. They check the status of the game and then handle messages accordingly. I’ll explain the meat of each of these in a second.

Lines 34–41 send the updated game state to all the players. This is an expensive and inefficient way to do this and can be improved. You should just send small state changes to the players and only send a complete game state when a player joins. If you have 100 players in your game, this game state could get pretty huge. I did this to speed up the development of the client. I only have to update my state for this message rather than for a bunch of messages.

TODO 1 (below) has an example of updating client’s states for small state changes.

We then update the state in our database and now everyone is up to speed!

TODO 1: If the game state is waiting

If the game state is waiting, we only need to worry about player’s sending “ready” messages. We will also start the game if all the players are ready. Any other messages are just players trying to be sneaky.

Lines 2–7 If we get a ready message, simply update this player’s state and add to the active players.

Lines 10–19 will check to see if all the players are ready. If so, it will update the game state and send a message to all the players that the game has started. I put this here as an example of updating client’s game states without sending the entire game state. For every action that changes the game state, a small message should be sent to tell the players what happened.

TODO 2: If the Game is in Progress

If the game is in progress, we need to handle player interactions.

The only interaction in my example game is attacking, so that’s the only message we need to check for.

Lines 2–3 will get the target player’s state and decrement their health. I set a DEFAULT_DAMAGE constant to make things easier, but for a more complicated game this could be retrieved from the sender’s player state!

Lines 7–10 check to see if the target player died. If they did, decrement the active players. This is basically saying if they died, there is one less player left in the game.

Lines 13–27 check to see if the game should end. Due to the fact that we don’t have a running game loop, we need to do these checks inside important messages. We check to see if there is only 1 active player left, and if so we reset the game state and send a “win” message to all the players.

NOTE: On my client I made all the users unsubscribe from the channel when someone wins, meaning they have to join back to play again. If you want to keep the users in the game, don’t reset the game state and just reset the playerStates.

Conclusion

That’s it! As long as all game-changing messages update the game state, and the server sends updates to the clients, PubNub can be used as an authoritative game server for simple games.

There’s a bit of code I didn’t show here, like handling presence and player’s leaving, but here’s the source code for the PubNub functions!

Source Code:

Swift Client

PubNub Functions