Gateway
WebSocket-/JSON-Gateway: Die öffentlichen APIs, die das Python-SDK aufrufen kann, stehen auch JS, C#, Go, Rust, Browserseiten und Programmen auf anderen Rechnern offen. Drei Rollen, mit JS-Client und Demoseite für den Browser; die Latenz ist die der Schnellspur plus ca. 1 ms.
Das Gateway verpackt Schnellspur und gepushten Zustand als WebSocket / JSON. Die öffentlichen APIs aus dem API-Katalog, die das Python-SDK aufrufen kann, stehen damit auch JS, C#, Go, Rust, Browserseiten, Programmen auf anderen Rechnern und LLMs offen – mit denselben Methodennamen und Parametern. Die Latenz ist die der Schnellspur plus ca. 1 ms.
Am einfachsten: „Kontrollzentrum“ auf der Startseite von Farsight → Gateway → Starten (Stoppen, Neu starten, Logs ansehen und die Demoseite öffnen gehen ebenfalls über diese Karte). Über die Kommandozeile:
python gateway/server.py # ws://127.0.0.1:8870/ws (Port unter ports.gateway in openwar3.json)
python gateway/server.py --open # wie oben, öffnet die Demoseite http://127.0.0.1:8870/demo, sobald der Port lauscht
python gateway/server.py --host 0.0.0.0 # fürs LAN: verlangt automatisch ein Token (bin/gateway/token.txt)
python gateway/server.py --allow-origin http://localhost:5173 # damit sich auch deine eigene Webseite verbinden kann
Verbindung und Rollen
Verbindungsadresse: ws://127.0.0.1:8870/ws?inst=9&role=dev (statt inst= geht auch pid=; wird ein Token verlangt, &token= anhängen).
| Rolle | Darf aufrufen | Geeignet für |
|---|---|---|
dev | alles: Beobachten, Befehle, Spielsteuerung, Sandbox (Welt per JASS verändern), Oberfläche zeichnen | lokale Tools, Gameplay-Mods, Begleiter |
player (mit &player=N) | Beobachten, Einheiten von Spieler N befehligen, Oberfläche zeichnen; standardmäßig im Fair-Modus, sieht nur, was in der Sicht von Spieler N liegt (&fair=0 schaltet das ab) | Bots oder LLMs, die für einen bestimmten Spieler antreten |
observer | nur lesen (die Runtime lehnt seine Befehle direkt ab) | Zuschauen, Kommentar, Datenerfassung |
player bekommt nicht: Spielsteuerung wie Spiel beenden, Tempo ändern oder Pausieren, players und enemy_ai_plan, die die verdeckten Karten der anderen zeigen, canvas.image, das den Spielprozess eine lokale Datei öffnen ließe, und JASS. Abfragen mit Spielernummer wie resources, tech und stats gehen nur für den eigenen Spieler.
Eine Verbindung ist eine Sitzung und belegt eine Schnellspur (die Runtime hat insgesamt 16). Das Gateway erlaubt höchstens 12 Sitzungen gleichzeitig, damit ein paar für Bots, Mods und Farsight frei bleiben. Beim Trennen wird nur entfernt, was diese Sitzung selbst gezeichnet hat, samt ihren Hotkeys; was andere Programme gezeichnet haben, bleibt.
Nachrichten
Nach dem Verbinden kommt zuerst hello: Protokollversion, Rolle, Prozess-ID des Spiels und die Liste der Methoden, die diese Rolle aufrufen darf. Danach trägt jede Anfrage eine id, und die Antwort trägt dieselbe id:
→ {"id": 1, "op": "call", "method": "units", "args": ["me"]}
← {"id": 1, "ok": true, "result": [{"addr": 123456, "type": "hpea", "owner": 0, "x": -4480, "y": 2120, "hp": 220, "hp_max": 220}]}
→ {"id": 2, "op": "call", "method": "move", "args": [[{"unit": 123456}], 100, 200]}
→ {"id": 3, "op": "call", "method": "ui.button", "args": ["shop", "Trank kaufen"], "kwargs": {"screen": [40, 300]}}
→ {"id": 4, "op": "subscribe", "state": {"hz": 4, "units": "mine"}, "events": true}
← {"type": "state", ...} {"type": "events", ...} danach laufend gepusht
→ {"id": 5, "op": "overview"} Lage auf einer Seite: Ressourcen, Einheitenzahlen, Helden, sichtbare Gegner, Produktion
→ {"id": 6, "op": "jass", "code": "call PingMinimapEx(0, 0, 3, 255, 0, 0, false)"} nur für dev
→ {"id": 7, "op": "api"} Methodenkatalog (außerdem ping / unsubscribe)
- Einheitenparameter schreibst du als
{"unit": Adresse}; die Adresse istaddraus dem Einheiten-JSON. Optional mit"handle": [lo, hi], um zu prüfen, dass die Adresse nicht von einer anderen Einheit wiederverwendet wurde. - Methodennamen sind die öffentlichen Methoden von Game, dazu
ui.*(button / choice / toast / hotkey / mouse / cursor …),canvas.*(text / panel / bar / image / circle / path / remove …) undjass.<Funktionsname>(nur für dev). - Callback-Funktionen lassen sich von entfernt nicht übergeben: Klicks und Hotkeys kommen über die Event-Pushes, das
ui.click-Event trägt denkey. Siehe Oberfläche & Eingabe. - Schlägt ein Aufruf fehl, betrifft die Antwort nur diesen einen (
ok: falsepluserror); die Verbindung bleibt bestehen. Das gilt auch, wenn das Gesendete kein JSON ist. - Die Felder im Event-JSON entsprechen dem W3P-Protokoll, dazu kommen Komfortfelder (
spell,key,text,chat,button,player,mods).
HTTP geht auch, praktisch für einzelne Aufrufe und curl: GET /api?role=player listet den Methodenkatalog, POST /call mit inst, role, method, args, kwargs ruft einmal auf. /call verwendet Sitzungen wieder: Startet das Spiel neu und läuft in einem neuen Prozess, wird automatisch eine neue Sitzung geöffnet; Sitzungen, die 10 Minuten untätig waren, werden geschlossen.
Clients
JS (Browser oder Node 22+, ohne Abhängigkeiten): gateway/clients/js/openwar3.mjs
import { OpenWar3, unit } from "./openwar3.mjs";
const ow = new OpenWar3("ws://127.0.0.1:8870/ws", { inst: 9, role: "dev" });
await ow.connect();
const mine = await ow.api.units("me");
await ow.api.move(mine.slice(0, 3).map(unit), 100, 200);
await ow.api.ui.button("hi", "Klick mich", { screen: [40, 300] }); // letztes einfaches Objekt = Keyword-Argumente
ow.on("event:ui.click", (e) => console.log("Geklickt:", e.key));
await ow.subscribe({ state: { hz: 2, units: "mine" }, events: true });
Node 20 / 21 braucht --experimental-websocket. Ein vollständiges Beispiel liegt in gateway/clients/js/example.mjs.
Demoseite für den Browser http://127.0.0.1:8870/demo: Lage, Tabelle der eigenen Einheiten, einen Button ins Spiel setzen, Event-Stream – alles auf einer Seite.
Andere Sprachen: Eine beliebige WebSocket-Bibliothek + das JSON oben genügt; Shared Memory musst du nicht anfassen.
LLMs: Nimm direkt den MCP-Server – er stellt die häufigen Aufgaben als fertige Tools bereit.
Gemessen
2026-09-25, Punkt für Punkt an einer echten Partie geprüft: 16/16 (Gateway 9 + MCP 7): Handshake (Rolle dev mit 121 Methoden), units('me'), Lage auf einer Seite, Bildschirmhinweis, Button setzen; nach dem Abonnieren den Button im Spiel angeklickt → ui.click wird zum Client gepusht; JASS; eine ungültige Einheit führt nur bei diesem einen Aufruf zu einem Fehler; HTTP /call (Rolle observer).
Auch der JS-Client (Node) und die Demoseite wurden getestet: Der von der Webseite gesetzte Button wurde im Spiel angeklickt, und das Event-Log der Webseite hat ui.click empfangen.
Sicherheit
- Standardmäßig lauscht es nur lokal auf
127.0.0.1, ohne Token (wie Farsight). Ist--hostkeine lokale Adresse, wird automatisch ein Token verlangt;--authverlangt es auch lokal. - Andere Websites im Browser kommen nicht rein: Verbindungen aus dem Browser tragen immer eine Herkunft (
Origin); das Gateway akzeptiert nur seine eigene Demoseite und die mit--allow-originangegebenen Adressen. Programme wie Python, Node oder curl senden keine Herkunft und verbinden sich ganz normal. Lauscht es nur lokal, prüft es zusätzlichHostund blockt so Angriffe, die eine externe Domain auf den lokalen Rechner auflösen lassen. - Die Rolle deklariert der Client beim Verbinden selbst: Im lokalen Modus ist sie eine Konvention, keine Sicherheitsgrenze. In der Arena muss der Schiedsrichter-Prozess entscheiden, wer welche Rolle bekommt, siehe Arena.