# Pasarela

> Pasarela WebSocket / JSON: las interfaces públicas que puede llamar el SDK de Python también las pueden llamar JS, C#, Go, Rust, páginas web o programas en otra máquina. Tres roles, con cliente JS y página de demostración incluidos; la latencia es la del carril rápido más ~1 ms.

Fuente: https://war3ai.com/es/docs/gateway/

La pasarela envuelve el carril rápido y el estado publicado en **WebSocket / JSON**. Las interfaces públicas del [Catálogo de API](https://war3ai.com/es/api/) que puede llamar el SDK de Python también las pueden llamar JS, C#, Go, Rust, páginas web, programas en otra máquina y LLM, con los mismos nombres de método y los mismos parámetros. La latencia es la del carril rápido más ~1 ms.

**Lo más cómodo: en la página de inicio de Farsight, "Centro de control" → "Pasarela" → "Iniciar"** (en esa misma tarjeta también puedes detenerla, reiniciarla, ver el log y abrir la página de demostración). Desde la línea de comandos:

```bash
python gateway/server.py                 # ws://127.0.0.1:8870/ws (el puerto está en ports.gateway de openwar3.json)
python gateway/server.py --open          # lo mismo, y cuando el puerto ya escucha abre la página de demostración http://127.0.0.1:8870/demo
python gateway/server.py --host 0.0.0.0  # para la LAN: exige token automáticamente (bin/gateway/token.txt)
python gateway/server.py --allow-origin http://localhost:5173   # para que también se conecte tu propia página web
```

## Conexión y roles

Dirección de conexión: `ws://127.0.0.1:8870/ws?inst=9&role=dev` (también puedes usar `pid=` en lugar de `inst=`; si hace falta token, añade `&token=`).

| Rol | Qué puede llamar | Ideal para |
|---|---|---|
| `dev` | Todo: observación, comandos, control del juego, sandbox (cambiar el mundo con JASS), dibujar interfaz | Herramientas locales, [mods de juego](https://war3ai.com/es/docs/mods/), compañeros |
| `player` (con `&player=N`) | Observación, dar órdenes a las unidades del jugador N, dibujar interfaz; **modo justo por defecto**: solo ve lo que está en la visión del jugador N (`&fair=0` lo desactiva) | Bots o LLM que juegan por un jugador |
| `observer` | Solo lectura (el runtime rechaza directamente los comandos que envíe) | Espectadores, comentaristas, recogida de datos |

Lo que `player` no puede usar: el control del juego, como terminar la partida, cambiar la velocidad o pausar; `players` y `enemy_ai_plan`, que dejan ver las cartas de los demás; `canvas.image`, que hace que el proceso del juego abra archivos locales; y JASS. Las consultas que llevan número de jugador, como `resources`, `tech` o `stats`, solo pueden consultar el propio.

Cada conexión es una sesión y ocupa un carril rápido (el runtime tiene 16 en total). La pasarela admite como máximo 12 sesiones a la vez, para dejar algunos carriles a los Bots, los mods y Farsight. Al desconectarse solo se retira lo que dibujó esa sesión y sus atajos de teclado; lo que dibujaron otros programas no se toca.

## Mensajes

Al conectar, lo primero que llega es `hello`: versión del protocolo, rol, ID del proceso del juego y la lista de métodos que ese rol puede llamar. Después, cada petición lleva un `id` y la respuesta lleva el mismo `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 poción"], "kwargs": {"screen": [40, 300]}}
→ {"id": 4, "op": "subscribe", "state": {"hz": 4, "units": "mine"}, "events": true}
← {"type": "state", ...}    {"type": "events", ...}        a partir de aquí se envían continuamente
→ {"id": 5, "op": "overview"}                             la partida en una página: recursos, unidades por tipo, héroes, enemigos visibles, producción
→ {"id": 6, "op": "jass", "code": "call PingMinimapEx(0, 0, 3, 255, 0, 0, false)"}    solo para dev
→ {"id": 7, "op": "api"}                                  catálogo de métodos (también hay ping / unsubscribe)
```

- **Parámetros de unidad**: se escriben `{"unit": dirección}`, donde la dirección es el `addr` del JSON de la unidad; puedes añadir `"handle": [lo, hi]` para comprobar que esa dirección no la ha reutilizado otra unidad.
- **Nombres de método**: son los métodos públicos de Game, más `ui.*` (button / choice / toast / hotkey / mouse / cursor…), `canvas.*` (text / panel / bar / image / circle / path / remove…) y `jass.<nombre de función>` (solo para dev).
- Un cliente remoto no puede aportar funciones de callback: los clics y los atajos de teclado se reciben en los eventos enviados, y el evento `ui.click` lleva la `key`. Ver [Interfaz y entrada](https://war3ai.com/es/docs/ui-input/).
- Si una llamada falla, solo se responde con error a esa llamada (`ok: false` más `error`) y la conexión sigue abierta; lo mismo si lo que se envía no es JSON.
- Los campos del JSON de eventos coinciden con el [Protocolo W3P](https://war3ai.com/es/docs/protocol/), con campos de conveniencia adicionales (`spell`, `key`, `text`, `chat`, `button`, `player`, `mods`).

También funciona por HTTP, útil para llamadas sueltas y para curl: `GET /api?role=player` lista el catálogo de métodos y `POST /call` con `inst`, `role`, `method`, `args` y `kwargs` hace una llamada. `/call` reutiliza la sesión: si el juego se reinicia y cambia de proceso, pasa automáticamente a una nueva, y las que llevan 10 minutos inactivas se cierran.

## Clientes

**JS** (navegador o Node 22+, sin dependencias): `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", "Púlsame", { screen: [40, 300] });   // el último objeto plano = argumentos con nombre
ow.on("event:ui.click", (e) => console.log("Pulsado", e.key));
await ow.subscribe({ state: { hz: 2, units: "mine" }, events: true });
```

Con Node 20 / 21 hay que añadir `--experimental-websocket`. El ejemplo completo está en `gateway/clients/js/example.mjs`.

**Página de demostración en el navegador** `http://127.0.0.1:8870/demo`: la partida, la tabla de unidades propias, poner un botón en el juego y el flujo de eventos, todo en una página.

**Otros lenguajes**: basta con cualquier biblioteca de WebSocket + el JSON de arriba; no hace falta tocar la memoria compartida.

**LLM**: usa directamente el [servidor MCP](https://war3ai.com/es/docs/mcp/), que convierte las tareas habituales en herramientas listas para usar.

## Medido

2026-09-25, verificación punto por punto conectada a una partida real, 16/16 (9 de la pasarela + 7 de MCP): handshake (rol dev, 121 métodos), `units('me')`, la partida en una página, aviso en pantalla, poner un botón; tras suscribirse, clic en ese botón dentro del juego → `ui.click` llega al cliente; JASS; pasar una unidad no válida solo da error en esa llamada; HTTP `/call` (rol observer).

También se probaron el cliente JS (Node) y la página de demostración: un botón puesto desde la web recibe un clic en el juego y el registro de eventos de la web recibe `ui.click`.

## Seguridad

- Por defecto solo escucha en `127.0.0.1` y no pide token (igual que Farsight). Si `--host` no es una dirección local, exige token automáticamente; `--auth` lo exige también en local.
- **Otros sitios web abiertos en el navegador no pueden conectarse**: todas las conexiones que inicia un navegador llevan su origen (`Origin`), y la pasarela solo acepta su propia página de demostración y las direcciones dadas con `--allow-origin`. Programas como Python, Node o curl no envían origen y se conectan con normalidad. Cuando solo escucha en local, además comprueba `Host`, lo que bloquea los ataques que hacen resolver un dominio externo a la máquina local.
- El rol lo declara el propio cliente al conectar: en modo local es una convención, no una frontera de seguridad. En la Arena, el proceso árbitro decidirá qué rol recibe cada uno; ver [Arena](https://war3ai.com/es/arena/).
