No description
  • Go 99.1%
  • Shell 0.9%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
SystemSh0cker 3c1060a88e Tune SQP polling defaults
Use a 20-second health-check interval and a 3-second UDP deadline while retaining the three-failure offline threshold. Update fallback values, tests, and both README language sections.
2026-07-22 21:24:36 +02:00
cmd/masterserver Stabilize SQP health checks and expose runtime limits 2026-07-22 20:54:47 +02:00
internal Tune SQP polling defaults 2026-07-22 21:24:36 +02:00
.gitignore Initial masterserver implementation 2026-07-22 19:24:01 +02:00
build.sh Initial masterserver implementation 2026-07-22 19:24:01 +02:00
go.mod Harden masterserver networking and heartbeat handling 2026-07-22 20:16:05 +02:00
LICENSE Initial masterserver implementation 2026-07-22 19:24:01 +02:00
README.md Tune SQP polling defaults 2026-07-22 21:24:36 +02:00

AUC Masterserver

AUC Masterserver is the lightweight HTTP service behind the All Under Control server browser. Game servers send heartbeats, clients request the public server list, and background workers check SQP reachability over UDP.

Diese README enthält die englische und deutsche Dokumentation.

English

Endpoints

  • POST /heartbeat registers or refreshes a game server.
  • GET /list returns all currently registered servers and their SQP status.
  • GET /health returns uptime, registry and whitelist information.

The service stores live data in memory. A server expires after the configured time without a heartbeat.

Heartbeat

The heartbeat body is JSON matching the GameServer model. The server derives IP, ID, LastSeen and IsOfficial; values submitted for those fields are not trusted.

Important limits:

  • Maximum request body: 16 KiB.
  • Port and SQPPort: 1 through 65535; an omitted SQPPort defaults to 7980.
  • Maximum registered servers: 10,000 globally and 64 per public IP.
  • Application rate limit: 5 heartbeats per second per IP with a burst of 20.
  • Text, player counts and nesting depth are bounded and validated.
  • Unknown JSON fields and multiple JSON objects are rejected. The legacy Host field is accepted but ignored because the address is derived by the masterserver.

Successful response:

{"status":"success"}

Server description BB-Code

Descriptions may use the BB-Code understood by the game client:

  • [LR] and [LF]
  • [COLOR=#RRGGBB]...[/COLOR]
  • [B]...[/B]
  • [S]...[/S]
  • [CENTER]...[/CENTER]
  • [LIST][*]First item[*]Second item[/LIST]

Tags must be properly nested. Raw TextMeshPro tags such as <color=...> and literal control characters are rejected. Use [LR] or [LF] for line breaks. Other metadata fields do not support rich-text markup.

Client IP and nginx

The default listen address is loopback-only:

[SERVER]
port=127.0.0.1:5001
serverTimeoutSeconds=60
whitelistReloadMinutes=5
allowPrivateIPs=false
heartbeatRatePerSecond=5
heartbeatBurst=20
sqpCheckIntervalSeconds=20
sqpTimeoutMilliseconds=3000
sqpFailureThreshold=3
httpReadHeaderTimeoutSeconds=5
httpReadTimeoutSeconds=10
httpWriteTimeoutSeconds=15
httpIdleTimeoutSeconds=60

The heartbeat rate uses a token bucket per public IP. nginx has an independent rate limit; when changing these values, its limit_req_zone rate and burst should be kept in sync or configured slightly more permissively.

Legacy values port=5001 and port=:5001 are also converted to 127.0.0.1:5001. A non-loopback address must be configured explicitly.

X-Real-IP is trusted only when the direct HTTP peer is a loopback address. This matches a local nginx reverse proxy and prevents clients reaching the Go service directly from spoofing their IP. nginx must overwrite, rather than forward, any client-provided header:

# http context
limit_req_zone $binary_remote_addr zone=auc_heartbeat:10m rate=5r/s;

server {
    listen 443 ssl;

    location = /heartbeat {
        client_max_body_size 16k;
        limit_req zone=auc_heartbeat burst=20 nodelay;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_pass http://127.0.0.1:5001;
    }

    location = /list {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_pass http://127.0.0.1:5001;
    }

    location = /health {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_pass http://127.0.0.1:5001;
    }
}

Keep TCP port 5001 blocked from external networks. If nginx runs in a container or on another host, loopback trust does not apply; the proxy/network design must then be adapted explicitly.

With allowPrivateIPs=false, private, loopback, link-local, multicast, documentation, CGNAT and other special-purpose addresses are rejected.

Official-server whitelist

Official servers are identified by API keys in whitelist.json next to the binary:

[
  "official-server-key-1",
  "official-server-key-2"
]

The file is created and enforced with owner-only permissions (0600) on platforms supporting POSIX permission bits. Keys are removed before data enters the in-memory registry or /list response. Use HTTPS between game servers and nginx.

SQP checks

The checker sends UDP ping and expects pong.

  • Default port: 7980
  • Per-check deadline: sqpTimeoutMilliseconds (default: 3 seconds)
  • Periodic interval: sqpCheckIntervalSeconds (default: 20 seconds)
  • Offline threshold: sqpFailureThreshold consecutive failures (default: 3)
  • Worker count: 16
  • Queue capacity: 1,024 checks

Checks are deduplicated per server and use a bounded worker pool. A heartbeat starts an immediate check only for a new server or a changed SQP port. A previously online server remains online during isolated UDP packet loss and changes to offline only after the configured number of consecutive failures. SQP state is deleted when its server expires.

Build and test

Go 1.26.5 or newer is required.

go test -race ./...
go vet ./...
./build.sh

build.sh creates Linux, Windows and macOS binaries below dist/.

Deutsch

Endpunkte

  • POST /heartbeat registriert oder aktualisiert einen Gameserver.
  • GET /list liefert alle aktuell registrierten Server und ihren SQP-Status.
  • GET /health liefert Betriebszeit sowie Informationen über Registry und Whitelist.

Der Dienst hält die aktiven Daten im Arbeitsspeicher. Ein Server wird entfernt, wenn innerhalb des konfigurierten Zeitraums kein Heartbeat eingeht.

Heartbeat

Der Heartbeat enthält JSON entsprechend dem GameServer-Modell. IP, ID, LastSeen und IsOfficial werden vom Masterserver bestimmt; dafür übermittelte Werte werden nicht vertraut.

Wichtige Grenzen:

  • Maximale Request-Größe: 16 KiB.
  • Port und SQPPort: 1 bis 65535; ohne SQPPort wird 7980 verwendet.
  • Maximal 10.000 registrierte Server insgesamt und 64 je öffentlicher IP-Adresse.
  • Anwendungsseitig 5 Heartbeats pro Sekunde und IP-Adresse, kurzzeitig bis zu 20.
  • Texte, Spielerzahlen und Verschachtelungstiefe werden begrenzt und validiert.
  • Unbekannte JSON-Felder und mehrere JSON-Objekte werden abgelehnt. Das alte Feld Host wird aus Kompatibilitätsgründen akzeptiert, aber ignoriert, weil der Masterserver die Adresse selbst bestimmt.

Erfolgreiche Antwort:

{"status":"success"}

BB-Code für die Serverbeschreibung

Die Beschreibung unterstützt den vom Spiel ausgewerteten BB-Code:

  • [LR] und [LF]
  • [COLOR=#RRGGBB]...[/COLOR]
  • [B]...[/B]
  • [S]...[/S]
  • [CENTER]...[/CENTER]
  • [LIST][*]Erster Eintrag[*]Zweiter Eintrag[/LIST]

Tags müssen korrekt verschachtelt sein. Direkte TextMeshPro-Tags wie <color=...> und echte Steuerzeichen werden abgelehnt. Für Zeilenumbrüche sind [LR] oder [LF] zu verwenden. Andere Metadatenfelder unterstützen keine Rich-Text-Formatierung.

Client-IP und nginx

Standardmäßig lauscht der Dienst ausschließlich auf Loopback:

[SERVER]
port=127.0.0.1:5001
serverTimeoutSeconds=60
whitelistReloadMinutes=5
allowPrivateIPs=false
heartbeatRatePerSecond=5
heartbeatBurst=20
sqpCheckIntervalSeconds=20
sqpTimeoutMilliseconds=3000
sqpFailureThreshold=3
httpReadHeaderTimeoutSeconds=5
httpReadTimeoutSeconds=10
httpWriteTimeoutSeconds=15
httpIdleTimeoutSeconds=60

Das Heartbeat-Limit arbeitet als Token-Bucket je öffentlicher IP-Adresse. nginx besitzt ein unabhängiges Rate-Limit. Werden die Werte geändert, müssen limit_req_zone und burst in nginx entsprechend synchronisiert oder dort etwas großzügiger gewählt werden.

Alte Werte wie port=5001 und port=:5001 werden ebenfalls in 127.0.0.1:5001 umgewandelt. Eine andere Bind-Adresse muss ausdrücklich angegeben werden.

X-Real-IP wird ausschließlich akzeptiert, wenn die direkte HTTP-Verbindung von einer Loopback-Adresse kommt. Das entspricht einem lokalen nginx-Reverse-Proxy und verhindert, dass direkte Clients ihre Adresse fälschen. nginx muss einen eingehenden Header überschreiben:

proxy_set_header X-Real-IP $remote_addr;

Der interne TCP-Port 5001 muss zusätzlich durch die Firewall vor externen Verbindungen geschützt werden. Läuft nginx in einem Container oder auf einem anderen Host, muss das Proxy-Vertrauensmodell ausdrücklich dafür angepasst werden.

Mit allowPrivateIPs=false werden private, lokale, Link-local-, Multicast-, Dokumentations-, CGNAT- und weitere Spezialadressen abgelehnt.

Official-Server-Whitelist

Official-Server werden über API-Keys in der Datei whitelist.json neben der Binary erkannt. Auf Systemen mit POSIX-Dateirechten wird die Datei mit 0600 erstellt und beim Laden entsprechend abgesichert. API-Keys werden vor dem Speichern aus den Serverdaten entfernt und niemals über /list ausgegeben. Zwischen Gameservern und nginx sollte ausschließlich HTTPS verwendet werden.

SQP-Prüfungen

Der Checker sendet ein UDP-ping und erwartet pong.

  • Standardport: 7980
  • Zeitlimit pro Prüfung: sqpTimeoutMilliseconds (Standard: 3 Sekunden)
  • Periodisches Intervall: sqpCheckIntervalSeconds (Standard: 20 Sekunden)
  • Offline-Schwelle: sqpFailureThreshold aufeinanderfolgende Fehler (Standard: 3)
  • 16 parallele Worker
  • Warteschlange für 1.024 Prüfungen

Prüfungen werden je Server zusammengefasst und über einen begrenzten Worker-Pool ausgeführt. Ein Heartbeat startet nur für einen neuen Server oder einen geänderten SQP-Port eine sofortige Prüfung. Ein zuvor erreichbarer Server bleibt bei einzelnen verlorenen UDP-Paketen online und wird erst nach der konfigurierten Anzahl aufeinanderfolgender Fehler als offline markiert. Beim Ablauf eines Servers wird auch dessen SQP-Status entfernt.

Bauen und testen

Benötigt wird Go 1.26.5 oder neuer.

go test -race ./...
go vet ./...
./build.sh

Das Build-Skript erzeugt Linux-, Windows- und macOS-Binaries unter dist/.