NATS and game servers

Jonas Lagoni Avatar

Jonas Lagoni

ยท5 min read

I have recently spent some time trying to learn about event sourcing and it happens to fit perfectly for GamingAPI. I want to have a log of events that happened in the game server, to enable server owners to pull those events as needed.

This would enable you to create all kinds of applications but to name a few:

  • Create a historical database of the game server, i.e. leader boards, player profiles, among others.
  • Create a discord bot that notifies your players that a game server has started/stopped, etc.
  • Send any type of commands from anywhere to the game server which can consume them once it is ready to do so. You can read more about their features here: https://docs.nats.io/nats-concepts/overview

In NATS to do event sourcing, you will have to utilize a feature they call JetStream.

Some of the reasons why I chose NATS:

  • It's diverse and easy setup and focus on performance.
  • It's ability to scale servers from a single server to clusters and even super clusters.
  • It has decent support in AsyncAPI code generation in C# and TS (and soon Go).
  • I have been using it for some time now, and are comfortable around it.

Initial setup

I have two dedicated servers within a Swarm cluster, that I am gonna deploy the NATS broker to that everything related to GamingAPI is gonna run on. At least as a start.

I did plan to start off with a cluster of NATS servers, however, I ended up deciding to go with the absolute minimal solution of a single NATS server. Eventually, it will most definitely become a cluster or even a supercluster, however, there is no reason to overcomplicate things when I don't have to at this early stage.

Initial configuration

The initial configuration looks like the following:

1#./configs/nats/nats-1.conf
2server_name = "nats-1"
3listen = 4222
4
5jetstream {
6  store_dir = "/data/gamingapi"
7}
8
9accounts {
10  GA-1: {
11    jetstream: {
12      max_mem: 512M
13      max_file: 1G
14      max_streams: 10
15      max_consumers: 100
16    },
17    users: [
18      {
19        user: UDXU4RCSJNZOIQHZNWXHXORDPRTGNJAHAHFRGZNEEJCPQTT2M7NLCNF4
20      }
21    ]
22  }
23}

My initial idea regarding accounts is that each game server owner gets their own account.

Each account can have x restrictions, such as the maximum number of consumers (max_consumers), etc. At some point in the future, I need to figure out which types of accounts should be present in the system i.e. accounts with certain restrictions that can be upgraded. Cause I will by no means want to pay for your +1TB events ๐Ÿ˜† As everything will be open-source, so you are of course free to set up your own NATS broker and use that instead of the provided one. The account then has x users associated.

A user in this context is anyone who needs to have access to any of the APIs, i.e. either a normal user account, game server, or third-party applications. This means that you can create as many users as you like, each with their own access token.

Accounts also create an excellent segregation between users to provide a solid security model. For example, two accounts cannot, unless explicitly states, have access to each other's events.

Security in the future

I do plan to take advantage of JWT, however, I haven't quite figured out how to actually use them yet or how much they will benefit... So for now that setup is on the back burner. The reason I want to go with JWT is that I have a feeling I will be able to set it up with the REST API as well as giving game owners the power to authorize third parties easily. How? No idea.

Docker compose

Single NATS server is configured with the following docker-compose file.

1#nats.yml
2version: '3.8'
3services:
4  nats-1:
5    restart: unless-stopped
6    image: nats
7    command: "-c /etc/nats/nats.conf"
8    volumes:
9      - "./configs/nats/nats-1.conf:/etc/nats/nats.conf"
10      - "/nats/data/nats-1:/data/gamingapi"
11    ports:
12      - 4222:4222
13    networks:
14      - gamingapi
15networks:
16  gamingapi:
17    external: true

Notice how I already have an overlay network in place called gamingapi. This enables me to independently deploy services and game servers and attach them to the same network to be discoverable.

I can then deploy the NATS server using the command docker stack deploy -c ./nats.yml gaming_api_nats

One of the things I have not quite figured out with docker swarm deployments is the best way to share files across the nodes in the swarm cluster. For example, the NATS configuration, how to best give other nodes access to the underlying configurations. Eventually, I will need to find a solution for this, but it seems complex...

Next

Designing the account system and how to map that to AsyncAPI and NATS is gonna be interesting and probably complex, but that is the next step in the NATS setup.

Once the account system is designed, I need a dynamic way for users to manage their accounts and access to the NATS server in a dynamic way. That would enable me to tie the developer portal together with the NATS server for a dynamic configuration.

However, that is not gonna happen until the underlying communication has been designed with AsyncAPI and the first game server is in production running on the network.

Related resources:

Photo by Billy Huynh on Unsplash