# Шлюз

> Шлюз WebSocket / JSON: публичный API, доступный из Python SDK, можно вызывать из JS, C#, Go, Rust, со страницы в браузере или из программы на другой машине. Три роли, в комплекте JS-клиент и демо-страница для браузера; задержка — быстрая полоса плюс около 1 ms.

Источник: https://war3ai.com/ru/docs/gateway/

Шлюз оборачивает быструю полосу и публикуемое состояние в **WebSocket / JSON**. Публичные методы Python SDK из [каталога API](https://war3ai.com/ru/api/) можно вызывать из JS, C#, Go, Rust, со страницы в браузере, из программы на другой машине и из LLM — с теми же именами методов и параметрами. Задержка — быстрая полоса плюс около 1 ms.

**Проще всего: главная страница Farsight «Центр управления» → Шлюз → «Запустить»** (остановка, перезапуск, логи и открытие демо-страницы — на той же карточке). Из командной строки:

```bash
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, меняющий мир), отрисовка интерфейса | Локальные инструменты, [игровые моды](https://war3ai.com/ru/docs/mods/), компаньоны |
| `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`:

```json
→ {"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`. См. [Интерфейс и ввод](https://war3ai.com/ru/docs/ui-input/).
- Ошибка в одном вызове возвращается только для этого вызова (`ok: false` и `error`), соединение не рвётся; то же самое, если прислали не JSON.
- Поля событий в JSON совпадают с [протоколом W3P](https://war3ai.com/ru/docs/protocol/), плюс удобные поля (`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`

```js
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-сервер](https://war3ai.com/ru/docs/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` и так блокирует атаки, при которых внешний домен резолвится на локальную машину.
- Роль клиент объявляет сам при подключении: в локальном режиме это соглашение, а не граница безопасности. На Арене роли будет раздавать процесс-судья, см. [Арена](https://war3ai.com/ru/arena/).
