Written by: Agustinus Agri Ardyan
Edited by: Monic Andyani
Game update is one of the ways to keep our gaming experience smooth — that’s just how important it is. The challenge is that the size of the game update today can go up to GBs, especially when we talk about AAA games.While this is not a problem for some countries with high internet speed like Singapore and Baltic countries, it’s not quite the same for the rest of the world. In that case, one of the solutions is to reduce the update size. So how do you do that?
To reduce the update size for a game or application, we reuse bytes from old files. This concept is implemented in AccelByte’s BuildInfo, and as a result, BuildInfo V2 was born. Think of it like we recycle the old file and require users to download only the necessary parts from remote to patch the old files to the latest version. Previously in BuildInfo V1, we had an updated algorithm to chunk each file into smaller chunks. For a small number of users updating simultaneously, I think the performance is alright and everyone is happy.
However, when the number of users rose high, the server load increases significantly. We make it so that in every game update request, the BuildInfo service will compare every chunk from the user’s version with the latest version and then respond by returning only chunks that don’t match. This process is called diffing. However, there is no guarantee that the update size will be smaller than the competitors’ nor that the update size will be in a small amount. In a case, the V1 update size is two times bigger than the one on Steam.
To figure out a way for an efficient update, our team did some research by implementing several algorithms. We were thinking to use the widely-known Rsync algorithm initially, however, we found out that Rsync is one of the main causes why our server load is severely heavy, we decided to switch to Zsync (Kudos to the team Rafi, Stradley, and Windio!).
There are several open-sourced MIT-licensed library for Zsync available in public. After a thorough examination of those libraries, we need to make sure that it doesn’t implement another proprietary library or any licensed library that might hinder us to fully alter and use it commercially. The next step is to alter some of the code in it and adjust it accordingly so that it will suit our case. In this case, we add a splitter feature to make things run smoother and I also created a wrapper and removed some unused features, so that it is easier to be integrated to the Launcher.
To ensure that everything will run without fail, I helped the Quality Assurance team by creating a script to check the download size of game update via BuildInfo V2. I personally believe that the script is helpful for the QA team to use as a tool to cross-check and to ensure that the download size in the Launcher is correct. I also assisted the Launcher team to integrate the new build into their code. I will not claim a full credit here since I am not working alone, I am grateful that the teams are backing up one another. For instance, just right after I finished the integration task, it turns out there are some defects along the way, and the Launcher team is there to help me to fix the issue.
The technology that we use is similar to BuildUtil, which is Golang. What’s nice about Golang is that it gives us a future maintenance advantage, since there are a lot of our engineers that are able to speak Golang. Can I confidently say that the technology we use is good? Well, good is a word with a broad meaning, since every algorithm or any tech has its own tradeoff. The BuildInfo V2 tradeoff is that it will take more time before downloading, especially when there are lots of files because it needs to calculate which bytes to reuse and which ones to be downloaded. However, it’s very effective to reduce update size as much as possible to solve the current issue. In the future, we plan to make it even faster and more efficient by using a faster hash generator combined with another algorithm and/or something else.
Every project has its own obstacles. As for this project, the integration process to our Launcher was one of the toughest obstacles that the team managed to tackle. Our Launcher is an Electron application written in Typescript, and while it’s good for front-end tasks general purpose, it’s not meant for intensive computation. We spent a good amount of time trying to implement the algorithm using Typescript and finally conclude that we need to use another way because it was too slow.
To solve it, we went through a series of research. After browsing for some NodeAPI documentations, we decided to build it as an executable instead, so that the Launcher can call it by using one of the NodeAPIs. Other than making it as an executable, I also preserve the code that is exported to the DLL to be used for our upcoming MiniLauncher.
I was surprised that the BuildInfo V2 is able to save so much by reusing the existing bytes from the old file. When our QA Team released a report for one of the AAA games we handle, they said that our update is 7GB smaller compared to the Steam updater. I genuinely thought that I had a bug, even one of our QA raised his eyebrow when he acknowledged the size difference. Only after the QA tested it by pressing Play on the Launcher, and is sure that the game was working as expected, I felt relieved and was also super excited at the same time. The same story happened with another game we tested, The Steam updater calculated 1.3GB of update size while our Launcher required only about 450MB!
It’s awesome what we can do, don’t you think? Got any question? Or wanting to learn about our technologies at AccelByte?