# 网关

> WebSocket / JSON 网关：Python SDK 能调的公开接口，JS、C#、Go、Rust、浏览器页面、另一台机器上的程序都能调。三种角色，自带 JS 客户端和浏览器演示页；延迟是快车道再加约 1 ms。

来源: https://war3ai.com/docs/gateway/

网关把快车道和推送状态包成 **WebSocket / JSON**。[接口目录](https://war3ai.com/api/) 里 Python SDK 能调的公开接口，JS、C#、Go、Rust、浏览器页面、另一台机器上的程序、大模型都能调，方法名和参数都一样。延迟是快车道再加约 1 ms。

**最省事：远见首页「控制中心」→ 网关 → 启动**（停止、重启、看日志、打开演示页也在那张卡片上）。命令行：

```bash
python gateway/server.py                 # ws://127.0.0.1:8870/ws（端口在 openwar3.json 的 ports.gateway）
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`（也可以用 `pid=` 代替 `inst=`；要令牌时加 `&token=`）。

| 角色 | 能调 | 适合 |
|---|---|---|
| `dev` | 全部：观察、命令、游戏控制、沙盒（JASS 改世界）、画界面 | 本机工具、[玩法模组](https://war3ai.com/docs/mods/)、玩伴 |
| `player`（加 `&player=N`） | 观察、指挥 N 号玩家的单位、画界面；**默认公平模式**，只看得见 N 号玩家视野里的东西（`&fair=0` 关掉） | 替某个玩家下场的 Bot 或大模型 |
| `observer` | 只读（运行时直接拒绝它下的命令） | 观战、解说、数据采集 |

`player` 拿不到：结束游戏、改速度、暂停这类游戏控制，看得见别人底牌的 `players`、`enemy_ai_plan`，会让游戏进程打开本机文件的 `canvas.image`，还有 JASS。`resources`、`tech`、`stats` 这类带玩家号的查询只能查自己。

一个连接就是一个会话，占一条快车道（运行时一共 16 条）。网关同时最多 12 个会话，给 Bot、模组和远见留几条。断开时只收掉这个会话自己画的东西和热键，别的程序画的不动。

## 消息

连上之后先收到 `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": 地址}`，地址就是单位 JSON 里的 `addr`；可以带上 `"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/docs/ui-input/)。
- 一条调用出错只回这一条（`ok: false` 加 `error`），连接不断；发来的不是 JSON 也一样。
- 事件 JSON 的字段和 [W3P 协议](https://war3ai.com/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 就够了，不用碰共享内存。

**大模型**：直接用 [MCP 服务器](https://war3ai.com/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`，不要令牌（和远见一样）。`--host` 不是本机地址时自动要求令牌；`--auth` 让本机也要。
- **浏览器里别的网站连不上**：浏览器发起的连接都带来源（`Origin`），网关只认自己的演示页和 `--allow-origin` 给的网址；Python、Node、curl 这类程序不带来源，照常连。只听本机时还会核对 `Host`，挡住把外部域名解析到本机的攻击。
- 角色是连接时自己声明的：本机模式下它是约定，不是安全边界。对战平台要由裁判进程决定谁拿什么角色，见 [对战平台](https://war3ai.com/arena/)。
