Atlas Game Distribution System
Atlas is a game-oriented Application Distribution System. It is fully developed in C# with .NET Core and ASP.NET Core.
The goal of Atlas is to provide an optimal way to deliver updates. For this, Atlas must meet certain criteria:
- The shipping process must be fast.
- The download process should be fast, use as little bandwidth as possible and have a small memory footprint.
How to deliver updates ?
With an archive
Providing an installer or an archive containing the program and replacing the files. Although simple, this way suffers from a huge flaw. The user has to download the whole program even if only a small part of the new version is different.
With binary deltas
By calculating the binary deltas. This reduces the size of an update but it is a more complex and extremely slow system.
Binary patches can be very small, which helps deliver updates quickly. But you end up needing a lot of them when users try to update from very old versions. For each new version, you need to generate multiple binary patchs, which increases the processing time.
Binaries deltas also need two full versions to be generated, which makes the process more complex.
With file chunking
This is the method used by Atlas and is summarized below. This method offers many advantages over the other two methods:
- There is no need for several versions of an application, only the one you want to deploy, which greatly reduces the time of deployment.
- We send and download only the modified data. Updates are therefore lighter than with an archive but slightly heavier than with binary deltas.
How does Atlas work ?
Atlas use the FastCDC algorithm for chunking. Its a content-defined chunking algorithm which provides a very fast rolling hash.
With content-defined chunking, Atlas first split the file into a series of variable-size chunks. You then compare the list of chunks that you have with the list of chunks in the new file and download the ones that you’re missing. You can then reconstruct the new file by writing chunks in the appropriate order, reusing some of the data you already had.
The main benefit of content-defined chunking is that chunks barely change when data shifts within a file. If you blindly split at 64KB boundaries, for example, then a single byte added at the beginning would change all the chunks throughout the file, which would be terrible for patch sizes.
A patching system based on content-defined chunking gives you a very important advantage over binary deltas: you can update any old version of the game to the latest one efficiently and in a single step.
To reduce download times, each chunk is compressed. Atlas use Zstandard which provides very good compression ratios while keeping decompression speeds high. Other algorithms would have a better compression ratio but CPU usage during decompression would be much higher. Zstd is a good compromise.
Atlas groups compressed chunks into bundles (60 chunks per bundle). Bundling is important for improving download efficiency by reducing the number of HTTP requests. If Atlas stored each chunk separately, players would have to make many requests, each of them for a tiny amount of data. This would slow down updates due to the overhead of the protocol and may cause server saturation.
Generating the manifest
Once bundling is completed, the next step is to write the release manifest. The manifest stores information about all the files, chunks, and bundles that are part of a release. The manifest initially used the JSON format to store this information but Atlas now uses the FlatBuffers format, which load very quickly on the client, is very compact and have a small memory footprint.
The manifest is also compressed with Zstandard.
The fundamental job of the patcher is to do whatever is necessary to transform the existing installation to the desired state. This may involve downloading new chunks, updating existing files, deleting old files, and so on.
Atlas Patcher uses a small SQLite database to track files. This database stores information about all the files and all the chunks that we have locally. The manifest is downloaded and the patcher use it to build a plan. This plan includes the missing chunks (which will be downloaded) and the chunks already present(which will be transferred or copied from one file to others files).
The patcher sends multipart range requests to download the missing chunks. If it need all the chunks in a given bundle then it download the whole thing, but if it only need some of them then it send a range request to retrieve the appropriate byte ranges. Asking for multiple ranges helps reduce the number of HTTP requests. This is why it is important to use a CDN.
With a small Unreal project of 500 Mb, I get excellent results. I can deploy the first version in less than 30 seconds.. The second version, 95% identical to the first, deploys in less than 4 seconds..
Once finalized, Atlas will be open-sourced and available for free on GitHub under MIT license. It will not be designed to be deployed in production but can be used as a basis for other custom distribution systems. It will still be distributed with instructions to run a demo with Docker and Kubernetes.