I started my game programming journey about a year and a half ago. I am somehow confident in programming Kotlin and liked the idea of a complete game in my favorite language. I planned to make a multiplayer game where you fight together to kill a boss. I played a lot of World of Warcraft in the old days but didn’t like the amount of work I had to put in to be able to play my favorite part of the game. So to build this game, I needed technology for a game server and frontend.
My professional tech stack always evolved around Spring-Boot backend programming, so I was very quick to choose it for this project as well. Some said it would be too bloated and slow for this purpose, but I didn’t have any problems getting the wanted performance. The server has a tick rate of 33 updates per second, and this loads just one of the 8 CPUs on my machine.
I have to say I love Kotlins Coroutines.
Concurrency is hard, and concurrency that should be real-time in the best case is a complete mess if you just work with threads.
I know that coroutines are not real-time and the delay()
method provides no guarantees that it waits for the wanted amount.
Despite all this, it works better than I expected as long as the whole system doesn’t have much load the Kotlin coroutine timing seems almost magically correct.
I decided to use Websockets for communication. The reasons behind this were primarily to be able to host the game server with just an HTTP load balancer. My game should also be runnable in a browser, to allow a quick and uncomplicated entry into the game. Later I learned that because of WebSockets it was much easier to port to android too. (You don’t need any elevated permissions)
The frontend was somehow a more difficult story. There is a Kotlin game engine: Korge. I have to say, it was awesome to prototype and try out things, while I was new to game programming. The problems appear if you want to implement more sophisticated features like complex particles, z-indexing, camera, or performance tuning. That,was when I decided to rewrite my complete frontend in Unity to get more options and build a more compelling experience.
That created a dear problem. I had to rewrite the whole code in C# and had to find a possibility to not duplicate the multitude of data structures. I love the Kotlin annotation processor kapt and I used it extensively in other projects beforehand. At first, I only exported the raw data structures and used them in the c# code. One major advantage is, that if you ignore Kotlin’s nullability it is quite compatible with c#. Over time, I also generated rest-clients and json-deserializers with the annotation processor and am satisfied with the result. I will write another blog post detailing the technology and code used for this.
Performance was always the main consideration of my game development journey. I aim to make my game run smoothly on any system that has 2 CPU cores. To accomplish this graphically was a fairly easy task because I don’t have any talent when it comes to digital art. The game doesn’t have any ray tracing or overly extensive physical calculations. All the relevant heavy computations are made on the game server.
The main performance challenge was to get the game state to the clients. I started with whole game state updates and transported all data 30 times per second. But as the development processed further and the game state got more complex the deserialization would take 3 client CPU cores to keep up with the updates. This is unacceptable because I cannot assume everyone has an 8-core system with 4ghz each core.
That’s why I moved to delta compression. I evaluated JSON delta compression, but neither Spring-Boot nor Json.Net in Unity has sophisticated Support for this. So I chose semantic delta updates, and I would suggest it to anyone who stands to make this choice. There are per definition only a certain number of different updates a game can make. In the end, I have only 11 different payloads, and they are all a lot smaller than the initial game state which would take up to 3MB. There is still potential if I wanted to reduce sizes further, but right now the game runs smoothly at >500fps on my machine where 60 would be enough.