Migration of a Multiplayer Game from Hosted to Serverless

migration-of-a-multiplayer-game-from-hosted-to-serverless

Photo by Sigmund on Unsplash

In the previous article I’ve introduced you to my plan of migrating away from my dedicated server to a fully serverless infrastructure. The previous example was hereby quite a doable task – after all the given website was mostly static and the migration of the API part was rather painless thanks to the sound base of using C# / .NET.

The current task at hand will be a bit more difficult. In this post I’m going to convert my multiplayer spaceshoot game. If you want to know more about this game, check out my “classic” articles:

A bit similar to the previous article the bulk of the work are static web assets. These will not pose any problem. However, this time we need something more than just a bit of API. We need a fully server – or at least full server capabilities.

Quick Recap – What do we want?

Before we go into the technical details let’s see what I expect from this migration:

  • A more clean code base (finally I can clean up my stuff, maybe remove something and modernize some other parts)
  • No more FTP or messy / unclear deployments – everything should be handled by CI/CD pipelines
  • Cost reduction; sounds weird, but the last thing I want to have is a cost increase (today it’s about 30 € per month for the hosting and my goal is to bring this below or close to 10 € – note: I pay much more for domains and these costs are not included here as they will remain the same).

Right now I am on track. Up to this point the new monthly total will be 5 €, which is for a DNS service with full e-mail capabilities incl. 30 GB storage and simple static websites.

Let’s see what we can do about this thing and how much it will cost me monthly.

Legacy Structure

Right now the project is just a Visual Studio solution. It consists of three projects:

  • The client, these are mostly static assets
  • The server, this is a command-line application spawning a WebSocket server on a certain port
  • Unit tests for the server

Initial folder structure

Running this results in the game being loaded – but without the server the multiplayer part is just grayed out.

Single-player only

There are several things that make this structure hard to work with for my desired goal:

  • We don’t want to create a custom server / command line application
  • The web assets should be processed by a web pipeline / bundler, which also includes more optimizations etc.
  • The whole build process should run on a dedicated pipeline within Azure DevOps

Also a bit of an update would be nice – in the end I still need to enter the “Server IP” in the multiplayer options. I’d like to have a DNS here instead (and it should default to the current domain).

The current IP settings

Before we can settle on a basic structure for our new repository we need to find an appropriate serverless replacement.

Choosing the Right Service

While I’ve chosen an Azure service in the last article we also might want to consider something else. However, let’s first see what Azure offers us:

  • Azure Web PubSub (only free for up to 20 connections with a max. of 20k messages; standard paid tier would work – but costs around $45 per month)
  • Azure Functions (durable functions to be specific, the free tier goes up to 400,000 GB-s monthly, assuming 100 MB memory consumption that means we can have 46 days in a month – or a party of 46 users concurrently playing for a whole day in a single month)
  • Azure App Service (the free tier would be sufficient, but it gives us a limit of 60 CPU minutes / day, which – for a larger game – would be insufficient; furthermore it does not allow custom domains)
  • Azure SignalR (only free for up to 20 connections with a max. of 20k messages; otherwise – also from standard plan – comparable to Azure Web PubSub)

So far Azure Functions seems mostly interesting – especially since we could use Azure Static Web App again for our static assets.

Alternatives are:

  • Linode (Nanode with 1 GB could be as cheap as $5 per month)
  • DigitalOcean (a Droplet with 512 MB could be as cheap as $4 per month)
  • Render (a free instance running for 750 instance hours with shutdown if not used)
  • Ably (a free tier contains 6 million monthly messages with 200 concurrent connections)

Ably could be used together with Netlify or other functions as a service.

The free tier of Ably looks interesting and choosing this stack seems to be just the right amount of learning something new (Ably + using Azure Entity Functions) with an established pattern (Azure Static Webs). Also, there is a great blog post by Ably, which discusses a very similar scenario.

In the end we would aim for the architecture as outlined in the article.

Architecture for our game after migration

Personally, I’d love to go with Render, however, it does not have “native” support for C#/.NET. This would mean a complete rewrite into Node.js. Even if I would like to do that I lack time. So, unfortunately, I need to drop that options. Or do I?!

It turns out that Render also supports deployment of Docker images. This way, I could just do the most minimalistic changes to the server code – making it a new and shiny .NET 8 AoT Docker container.

Minimum memory footprint, best performance – least development effort. Sounds too good to be true? Well, let’s give it a try!

Code Changes for the Client

The app itself was quite a mess. This has been created in a time before bundlers. So it featured an HTML file that in the end had something like:


















Then in order to optimize this I ran (from a BAT-file) a little utility called jsmin.exe (!), which received as input all found