# Gateway

> Gateway WebSocket / JSON: as APIs públicas que o SDK em Python chama também podem ser chamadas de JS, C#, Go, Rust, páginas de navegador e programas em outra máquina. Três papéis, com cliente JS e página de demonstração no navegador incluídos; a latência é a da via rápida mais cerca de 1 ms.

Fonte: https://war3ai.com/pt/docs/gateway/

O gateway empacota a via rápida e o estado enviado como **WebSocket / JSON**. As APIs públicas do [catálogo da API](https://war3ai.com/pt/api/) que o SDK em Python chama podem ser chamadas de JS, C#, Go, Rust, páginas de navegador, programas em outra máquina e LLMs, com os mesmos nomes de método e parâmetros. A latência é a da via rápida mais cerca de 1 ms.

**O jeito mais fácil: página inicial do Farsight, “Central de controle” → Gateway → Iniciar** (parar, reiniciar, ver os logs e abrir a página de demonstração também ficam nesse cartão). Pela linha de comando:

```bash
python gateway/server.py                 # ws://127.0.0.1:8870/ws (a porta fica em ports.gateway no openwar3.json)
python gateway/server.py --open          # o mesmo, e quando a porta já está escutando abre a página de demonstração http://127.0.0.1:8870/demo
python gateway/server.py --host 0.0.0.0  # para a rede local: exige token automaticamente (bin/gateway/token.txt)
python gateway/server.py --allow-origin http://localhost:5173   # para a sua própria página web também conseguir conectar
```

## Conexão e papéis

Endereço de conexão: `ws://127.0.0.1:8870/ws?inst=9&role=dev` (também dá para usar `pid=` em vez de `inst=`; quando for preciso token, acrescente `&token=`).

| Papel | O que pode chamar | Indicado para |
|---|---|---|
| `dev` | Tudo: observação, comandos, controle do jogo, sandbox (alterar o mundo via JASS), desenhar interface | Ferramentas locais, [mods de jogabilidade](https://war3ai.com/pt/docs/mods/), companheiros |
| `player` (com `&player=N`) | Observação, comandar as unidades do jogador N, desenhar interface; **modo justo por padrão**, só enxerga o que está na visão do jogador N (`&fair=0` desliga) | Bots ou LLMs que jogam no lugar de um jogador |
| `observer` | Só leitura (o runtime rejeita direto os comandos que ele enviar) | Assistir, narrar, coletar dados |

O que `player` não recebe: controle do jogo, como encerrar a partida, mudar a velocidade ou pausar; `players` e `enemy_ai_plan`, que mostram as cartas dos outros; `canvas.image`, que faz o processo do jogo abrir arquivos locais; e JASS. Consultas que levam número de jogador, como `resources`, `tech` e `stats`, só podem consultar o próprio jogador.

Uma conexão é uma sessão e ocupa uma via rápida (o runtime tem 16 no total). O gateway aceita no máximo 12 sessões ao mesmo tempo, para deixar algumas vias para Bots, mods e o Farsight. Ao desconectar, só é removido o que essa sessão desenhou e as teclas de atalho dela; o que outros programas desenharam fica intacto.

## Mensagens

Ao conectar, a primeira mensagem recebida é `hello`: versão do protocolo, papel, PID do processo do jogo e a lista de métodos que esse papel pode chamar. Depois disso, cada requisição leva um `id`, e a resposta volta com o mesmo `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", "Comprar poção"], "kwargs": {"screen": [40, 300]}}
→ {"id": 4, "op": "subscribe", "state": {"hz": 4, "units": "mine"}, "events": true}
← {"type": "state", ...}    {"type": "events", ...}        daí em diante, envio contínuo
→ {"id": 5, "op": "overview"}                             resumo da partida numa página: recursos, contagem por tipo de unidade, heróis, inimigos visíveis, produção
→ {"id": 6, "op": "jass", "code": "call PingMinimapEx(0, 0, 3, 255, 0, 0, false)"}    só para dev
→ {"id": 7, "op": "api"}                                  catálogo de métodos (há também ping / unsubscribe)
```

- **Parâmetros de unidade** são escritos como `{"unit": endereço}`, em que o endereço é o `addr` do JSON da unidade; dá para incluir `"handle": [lo, hi]` para conferir que esse endereço não foi reaproveitado por outra unidade.
- **Nomes de método** são os métodos públicos de Game, mais `ui.*` (button / choice / toast / hotkey / mouse / cursor…), `canvas.*` (text / panel / bar / image / circle / path / remove…) e `jass.<nome da função>` (só para dev).
- Um cliente remoto não pode passar funções de callback: cliques e teclas de atalho chegam pelos eventos enviados, e o evento `ui.click` traz a `key`. Veja [Interface e entrada](https://war3ai.com/pt/docs/ui-input/).
- Se uma chamada der erro, só aquela chamada responde com erro (`ok: false` mais `error`); a conexão continua. O mesmo vale quando o que chega não é JSON.
- Os campos do JSON de eventos são os mesmos do [Protocolo W3P](https://war3ai.com/pt/docs/protocol/), com campos de conveniência adicionais (`spell`, `key`, `text`, `chat`, `button`, `player`, `mods`).

HTTP também funciona, bom para chamadas avulsas e curl: `GET /api?role=player` lista o catálogo de métodos, e `POST /call` com `inst`, `role`, `method`, `args` e `kwargs` faz uma chamada. `/call` reaproveita a sessão: se o jogo for reaberto e o processo mudar, ele troca para uma nova automaticamente, e sessões paradas há 10 minutos são encerradas.

## Clientes

**JS** (navegador ou Node 22+, zero dependências): `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", "Clique aqui", { screen: [40, 300] });     // o último objeto simples = argumentos nomeados
ow.on("event:ui.click", (e) => console.log("clicou em", e.key));
await ow.subscribe({ state: { hz: 2, units: "mine" }, events: true });
```

No Node 20 / 21, acrescente `--experimental-websocket`. O exemplo completo está em `gateway/clients/js/example.mjs`.

**Página de demonstração no navegador** `http://127.0.0.1:8870/demo`: a situação da partida, a tabela das nossas unidades, um botão posto dentro do jogo e o fluxo de eventos, tudo numa página.

**Outras linguagens**: qualquer biblioteca de WebSocket + o JSON acima bastam, sem tocar na memória compartilhada.

**LLMs**: use direto o [servidor MCP](https://war3ai.com/pt/docs/mcp/), que transforma as tarefas comuns em ferramentas prontas.

## Medições

2026-09-25, conectado a uma partida real, verificação item por item 16/16 (9 do gateway + 7 do MCP): handshake (121 métodos no papel dev), `units('me')`, resumo da partida, aviso na tela, pôr um botão; depois de assinar, clicar nesse botão dentro do jogo → `ui.click` enviado ao cliente; JASS; passar uma unidade inválida só gera erro naquela chamada; HTTP `/call` (papel observer).

O cliente JS (Node) e a página de demonstração no navegador também foram testados: o botão posto pela página foi clicado dentro do jogo, e o log de eventos da página recebeu `ui.click`.

## Segurança

- Por padrão, só escuta na máquina local `127.0.0.1` e não exige token (igual ao Farsight). Quando `--host` não é um endereço local, o token passa a ser exigido automaticamente; `--auth` exige token também na máquina local.
- **Outros sites abertos no navegador não conseguem conectar**: toda conexão iniciada por um navegador leva a origem (`Origin`), e o gateway só aceita a própria página de demonstração e os endereços passados em `--allow-origin`; programas como Python, Node e curl não enviam origem e conectam normalmente. Quando escuta só na máquina local, ele também confere o `Host`, bloqueando ataques que fazem um domínio externo resolver para a máquina local.
- O papel é declarado pelo próprio cliente ao conectar: no modo local, ele é uma convenção, não uma fronteira de segurança. Na Arena, quem decide o papel de cada um é o processo árbitro; veja [Arena](https://war3ai.com/pt/arena/).
