Шлюз
Шлюз WebSocket / JSON: публичный API, доступный из Python SDK, можно вызывать из JS, C#, Go, Rust, со страницы в браузере или из программы на другой машине. Три роли, в комплекте JS-клиент и демо-страница для браузера; задержка — быстрая полоса плюс около 1 ms.
Шлюз оборачивает быструю полосу и публикуемое состояние в WebSocket / JSON. Публичные методы Python SDK из каталога API можно вызывать из JS, C#, Go, Rust, со страницы в браузере, из программы на другой машине и из LLM — с теми же именами методов и параметрами. Задержка — быстрая полоса плюс около 1 ms.
Проще всего: главная страница Farsight «Центр управления» → Шлюз → «Запустить» (остановка, перезапуск, логи и открытие демо-страницы — на той же карточке). Из командной строки:
python gateway/server.py # ws://127.0.0.1:8870/ws (порт — ports.gateway в openwar3.json)
python gateway/server.py --open # то же самое, а когда порт начнёт слушать, открывает демо-страницу http://127.0.0.1:8870/demo
python gateway/server.py --host 0.0.0.0 # для локальной сети: токен требуется автоматически (bin/gateway/token.txt)
python gateway/server.py --allow-origin http://localhost:5173 # чтобы подключаться могла и ваша собственная веб-страница
Подключение и роли
Адрес подключения: ws://127.0.0.1:8870/ws?inst=9&role=dev (вместо inst= можно указать pid=; если нужен токен, добавьте &token=).
| Роль | Что можно вызывать | Для чего |
|---|---|---|
dev | Всё: наблюдение, команды, управление игрой, песочница (JASS, меняющий мир), отрисовка интерфейса | Локальные инструменты, игровые моды, компаньоны |
player (с &player=N) | Наблюдение, команды юнитам игрока N, отрисовка интерфейса; по умолчанию честный режим — видно только то, что в обзоре игрока N (&fair=0 — выключить) | Бот или LLM, играющие за конкретного игрока |
observer | Только чтение (рантайм сразу отклоняет его команды) | Наблюдение за матчем, комментирование, сбор данных |
Роли player недоступны: управление игрой — завершение игры, смена скорости, пауза; players и enemy_ai_plan, которые раскрывают чужие карты; canvas.image, из-за которого процесс игры открыл бы локальный файл; и JASS. Запросы с номером игрока, такие как resources, tech и stats, работают только для своего игрока.
Одно подключение — один сеанс, он занимает одну быструю полосу (всего в рантайме их 16). Одновременно шлюз держит не больше 12 сеансов, чтобы несколько полос оставалось ботам, модам и Farsight. При отключении убирается только то, что нарисовал сам этот сеанс, и его горячие клавиши; нарисованное другими программами остаётся.
Сообщения
Сразу после подключения приходит hello: версия протокола, роль, идентификатор процесса игры и список методов, доступных этой роли. Дальше в каждом запросе передаётся id, а ответ приходит с тем же 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", "Купить зелье"], "kwargs": {"screen": [40, 300]}}
→ {"id": 4, "op": "subscribe", "state": {"hz": 4, "units": "mine"}, "events": true}
← {"type": "state", ...} {"type": "events", ...} дальше приходят постоянно
→ {"id": 5, "op": "overview"} обстановка на одной странице: ресурсы, число юнитов по типам, герои, видимые враги, производство
→ {"id": 6, "op": "jass", "code": "call PingMinimapEx(0, 0, 3, 255, 0, 0, false)"} только для dev
→ {"id": 7, "op": "api"} каталог методов (есть ещё ping / unsubscribe)
- Юнит в аргументах задаётся как
{"unit": адрес}, где адрес — этоaddrиз JSON юнита; можно добавить"handle": [lo, hi], чтобы проверить, что этот адрес не занял уже другой юнит. - Имена методов — это публичные методы Game, а также
ui.*(button / choice / toast / hotkey / mouse / cursor…),canvas.*(text / panel / bar / image / circle / path / remove…) иjass.<имя_функции>(только для dev). - Передать функцию-обработчик удалённо нельзя: клики и горячие клавиши приходят в потоке событий, событие
ui.clickсодержитkey. См. Интерфейс и ввод. - Ошибка в одном вызове возвращается только для этого вызова (
ok: falseиerror), соединение не рвётся; то же самое, если прислали не JSON. - Поля событий в JSON совпадают с протоколом W3P, плюс удобные поля (
spell,key,text,chat,button,player,mods).
Работает и HTTP — удобно для разовых вызовов и curl: GET /api?role=player выдаёт каталог методов, POST /call с inst, role, method, args, kwargs выполняет один вызов. /call переиспользует сеансы: если игру перезапустили и сменился процесс, автоматически открывается новый сеанс, а сеансы, простаивающие 10 минут, закрываются.
Клиенты
JS (браузер или Node 22+, без зависимостей): 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", "Нажми меня", { screen: [40, 300] }); // последний простой объект = именованные аргументы
ow.on("event:ui.click", (e) => console.log("Нажато:", e.key));
await ow.subscribe({ state: { hz: 2, units: "mine" }, events: true });
В Node 20 / 21 нужен флаг --experimental-websocket. Полный пример — в gateway/clients/js/example.mjs.
Демо-страница в браузере http://127.0.0.1:8870/demo: обстановка, таблица наших юнитов, кнопка, которую можно поставить в игру, поток событий — всё на одной странице.
Другие языки: хватит любой библиотеки WebSocket и JSON, показанного выше, — трогать общую память не нужно.
LLM: используйте сразу MCP-сервер — частые задачи в нём уже оформлены как готовые инструменты.
Замеры
2026-09-25, по пунктам на живом матче, 16/16 (шлюз — 9 пунктов, MCP — 7): рукопожатие (у роли dev — 121 метод), units('me'), обстановка на одной странице, подсказка на экране, кнопка; после подписки клик по этой кнопке в игре → ui.click доходит до клиента; JASS; неверный юнит даёт ошибку только для этого вызова; HTTP /call (роль observer).
JS-клиент (Node) и демо-страница в браузере тоже проверены: по кнопке, поставленной с веб-страницы, кликнули в игре, и журнал событий страницы получил ui.click.
Безопасность
- По умолчанию шлюз слушает только локальный
127.0.0.1и токен не требует (как и Farsight). Если--host— не локальный адрес, токен требуется автоматически; с--authтокен нужен и локально. - Другие сайты в браузере подключиться не могут: у любого подключения из браузера есть источник (
Origin), а шлюз принимает только свою демо-страницу и адреса, переданные через--allow-origin; программы вроде Python, Node или curl источник не передают и подключаются как обычно. Когда шлюз слушает только локально, он ещё проверяетHostи так блокирует атаки, при которых внешний домен резолвится на локальную машину. - Роль клиент объявляет сам при подключении: в локальном режиме это соглашение, а не граница безопасности. На Арене роли будет раздавать процесс-судья, см. Арена.