Setting up Robust.Cdn
Robust.Cdn is the dedicated server for providing manifest-based delta updates without server ACZ.
The TL;DR of how Robust.Cdn
operates is that it ingests directories of client zip files whenever you push a new build, then generates manifests and exposes the manifest and endpoint to do delta downloads. The games files are cached in an SQLite DB similar to how the launcher stores it.
Prerequisites
You will need the ASP.NET Core 6 runtime installed.
Building
Clone the code, then compile it with dotnet publish -c Release -r linux-x64 --no-self-contained
The files will be dropped in Robust.Cdn/bin/Release/net6.0/linux-x64/publish
. Robust.Cdn
is the executable to run, but obviously copy everything else too.
Server File Layout
You’re going to want to put the server files somewhere on your uhhh, server. We put them at /opt/robust_cdn
, with the following directory structure:
/opt/robust_cdn/
├── appsettings.json
├── run.sh
├── bin
│ ├── Robust.Cdn
│ ├── Robust.Cdn.dll
.
The run.sh
looks like this by the way, yes it’s just that simple:
#!/bin/sh
exec bin/Robust.Cdn
The actual program files go in a subfolder, and we run it from the parent directory so you don’t bulldoze the config files with updates or something.
Configuration
The configuration file is appsettings.json
in the current working directory you run the server program from. You need to change at least two configuration settings: the disk path of your game versions, and the update token to trigger updates. Maybe port too.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
// change port for the server to bind to here
"Urls": "http://localhost:27690/",
"Cdn": {
// See https://github.com/space-wizards/Robust.Cdn/blob/master/Robust.Cdn/CdnOptions.cs for all options.
// Yes you can have comments in here by the way.
// Change this update token so people don't uh... spam update checks on your server.
"UpdateToken": "change this",
// Increase this to reduce bandwidth usage for large downloads. Higher numbers need more CPU.
"StreamCompressLevel": 5,
// The file path of the SQLite database used by the server.
// You can change this to put it on another drive or something if you want.
// Personally we put it on our server's HDD instead of the SSD, but that's up to you.
"DatabaseFileName": "content.db",
// The directory path containing the versioned client builds to load and serve.
// See below for explanation
"VersionDiskPath": "/var/lib/wizards-builds/builds"
}
}
Version disk
Robust.Cdn
needs a directory containing builds to load, with the following layout:
/var/lib/wizards-builds/builds
├── 02030cfa0ed6511ec5527c5b7d1f8bcd46fe1435
│ ├── SS14.Client.zip
│ ├── SS14.Server_linux-arm64.zip
│ ├── SS14.Server_linux-x64.zip
│ ├── SS14.Server_osx-x64.zip
│ └── SS14.Server_win-x64.zip
├── 021d39be2876f991c5fd6e663760a921d29ac694
│ ├── SS14.Client.zip
│ ├── SS14.Server_linux-arm64.zip
It will look for SS14.Client.zip
(configurable) in each subdirectory to the directory to specify. The subdirectory’s name is the “version” of content. (Note: these are hashes because we use the Git commit hash as version “number”. They are not tied to manifest or zip hashes in any way, and can be any string, really).
Automatic detection of new versions
You can use something like the following to trigger Robust.Cdn
to scan for new versions in the version directory:
curl -X POST -d "" -H 'Authorization: Bearer <token from the config file, change this>' "http://localhost:27690/control/update"
API endpoints / build metadata
Robust.Cdn
exposes the /version/{version}/manifest
and /version/{version}/download
endpoints that do the actual updating. These need to be specified in the build.json
in your server files, which you probably want to just specify by modifying gen_build_info.py
or something. The new metadata required is as follows:
// (example from Wizard's Den servers)
{
"download": "https://cdn.centcomm.spacestation14.com/builds/wizards/builds/{FORK_VERSION}/SS14.Client.zip",
"hash": "49a7f54eb7e848c0a438bcfd3a198454d862e7d58d3e11c7ce60e281ddbd205d",
"version": "e769ad27256300cfbbf10d641930d43990bff309",
"fork_id": "wizards",
"engine_version": "0.12.1.0",
// These ones are new
"manifest_url": "https://cdn.centcomm.spacestation14.com/cdn/version/{FORK_VERSION}/manifest",
"manifest_download_url": "https://cdn.centcomm.spacestation14.com/cdn/version/{FORK_VERSION}/download",
"manifest_hash": "8B175E9D944F54C1444E93E19C39EB255353392B316118B660F21DF68D56DC2D"
}
Note the ability to specify {FORK_VERSION}
in the build.json
file. This gets replaced by version
from the JSON at runtime. This way the URL does not need to duplicate the version string, and the URL template can be changed via CVar while the entry in build.json
stays the same. Other possible keys are {FORK_ID}
, {MANIFEST_HASH}
and {ZIP_HASH}
.
manifest_url
is the/manifest
endpoint onRobust.Cdn
. The version must be included in the URL forRobust.Cdn
to work.manifest_download_url
is the/download
endpoint. Same rules as above.manifest_hash
must be the manifest hash. This hash is automatically generated bygen_build_info.py
now and should just work.
Systemd Unit
We personally use systemd to start the server:
[Unit]
Description=Robust.Cdn
[Service]
Type=notify
WorkingDirectory=/opt/robust_cdn/
ExecStart=/opt/robust_cdn/run.sh
User=robust_cdn
Group=robust_cdn
[Install]
WantedBy=multi-user.target