HetznerWorkerHost
One Hetzner Cloud VM per worker, created through the Hetzner Cloud API (https://docs.hetzner.cloud).
Nebula.Hosting · Packages/com.1by3.nebula/Runtime/Orchestrator/Hosting/HetznerWorkerHost.cspublic sealed class HetznerWorkerHost : IWorkerHostOne Hetzner Cloud VM per worker, created through the Hetzner Cloud API (https://docs.hetzner.cloud). Each VM boots Ubuntu with a cloud-init script that downloads the Linux server build from the orchestrator over the private network, reads its own private IP from the Hetzner metadata service, and starts the worker role as a systemd unit advertising that private IP. Killing a worker deletes its VM. On Initialize every VM labelled with this mesh id is deleted, so a restarted orchestrator never inherits (and pays for) stale machines.
All HTTP work runs on the thread pool; results are queued and applied in Tick on the main thread, where the JSON is parsed with JsonUtility.
Constructors
HetznerWorkerHost
public HetznerWorkerHost(CloudHostSettings settings);Fields
BillingPeriodSeconds
public const float BillingPeriodSeconds = 3600f;Hetzner charges per started hour, so a machine deleted after 61 minutes has cost two.
BillingSafetyMarginSeconds
public const float BillingSafetyMarginSeconds = 120f;Delete a parked machine this long before the paid hour ends, so the delete call lands inside it.
WorkerEnvFile
public const string WorkerEnvFile = "/etc/nebula/worker.env";The worker's environment file on its VM: the game's variables, applied by systemd before the worker starts.
Properties
Name
public string Name { get; }SupportsParking
public bool SupportsParking { get; }A VM already paid for is worth keeping: a retired worker is parked and unparked instead of deleted and recreated.
TypicalBootSeconds
public float TypicalBootSeconds { get; }Measured: create, Ubuntu boot, build download and Unity start is well over a minute.
IsReady
public bool IsReady { get; }InitializationError
public string InitializationError { get; } = "";Methods
Initialize
public void Initialize(Action<string, string> log);Launch
public IWorkerHandle Launch(WorkerLaunchSpec spec);Kill
public void Kill(IWorkerHandle handle);BilledUntilSeconds
public static float BilledUntilSeconds(float launchedAt, float now, float billingPeriodSeconds = BillingPeriodSeconds);End of the billing period a machine launched at launchedAt has been charged for as of now. Hetzner bills per started hour, so a machine up for five minutes is paid for until minute 60 and one up for 61 minutes until minute 120; keeping it parked until then is free. Pure function.
RetainUntilSeconds
public static float RetainUntilSeconds(float launchedAt, float now, float idlePoolSeconds);When a machine parked at now stops being kept: the end of the hour it has already been billed, or sooner when idlePoolSeconds asks for less. A longer idle pool is not honoured, because the hour after this one is a fresh charge and nobody asked to pay it.
BillingSafetyMarginSeconds is taken off the billed deadline only, so the delete call lands before the next hour starts. It is not taken off a retention the developer asked for: an IdlePoolSeconds under the margin would otherwise mean "delete on the next tick".
Pure function; the result is the wall-clock moment the machine goes.
ParkedInstanceExpired
public static bool ParkedInstanceExpired(float now, float retainUntil);True when a parked machine kept until retainUntil must be deleted now. Pure function.
Park
public void Park(IWorkerHandle handle, float idlePoolSeconds);Keep the machine instead of deleting it: the worker process stays up and keeps heartbeating, the orchestrator simply deals it nothing. It is deleted in Tick once the hour Hetzner has already charged for runs out (or sooner, when idlePoolSeconds is shorter), unless Unpark claims it first.
Unpark
public bool Unpark(IWorkerHandle handle);Take a parked machine back into service. The worker never stopped, so it is ready at once.
Tick
public void Tick();WriteHandleJson
public void WriteHandleJson(IWorkerHandle handle, JsonWriter w);Dispose
public void Dispose();BuildCloudInit
public static string BuildCloudInit(string buildUrl, string workerArgs, System.Collections.Generic.IDictionary<string, string> env = null, Action<string> warn = null);Boot script for a worker VM. Waits for the private interface, fetches the build from the orchestrator, writes the game's variables (env) to WorkerEnvFile (root-only), and runs the worker as a systemd unit that reads it. $PRIVATE_IP in workerArgs expands on the machine; the variables are written literally.
SystemdEnvLine
public static string SystemdEnvLine(string key, string value);One line of a systemd environment file: KEY="value" with \, ", $ and ` escaped. Null when the value has a line break, which the format cannot carry safely.