# ゲートウェイ

> WebSocket / JSON ゲートウェイ：Python SDK で呼べる公開 API を、JS、C#、Go、Rust、ブラウザのページ、別のマシン上のプログラムからも呼べます。3 つのロールがあり、JS クライアントとブラウザ用デモページが付属します。レイテンシはファストレーンに約 1 ms 加わるだけです。

出典: https://war3ai.com/ja/docs/gateway/

ゲートウェイは、ファストレーンとプッシュされる状態を **WebSocket / JSON** で包みます。[API カタログ](https://war3ai.com/ja/api/) にある、Python SDK で呼べる公開 API は、JS、C#、Go、Rust、ブラウザのページ、別のマシン上のプログラム、LLM からも呼べます。メソッド名も引数も同じです。レイテンシはファストレーンに約 1 ms 加わるだけです。

**いちばん手軽なのは Farsight のトップページ「コントロールセンター」→ ゲートウェイ → 起動です**（停止、再起動、ログの確認、デモページを開く操作も同じカードにあります）。コマンドラインでは次のとおりです。

```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  # LAN 向け：自動的にトークンを要求（bin/gateway/token.txt）
python gateway/server.py --allow-origin http://localhost:5173   # 自作の Web ページからも接続できるようにする
```

## 接続とロール

接続先：`ws://127.0.0.1:8870/ws?inst=9&role=dev`（`inst=` の代わりに `pid=` も使えます。トークンが必要なときは `&token=` を付けます）。

| ロール | 呼べるもの | 向いている用途 |
|---|---|---|
| `dev` | すべて：観測、コマンド、ゲーム制御、サンドボックス（JASS でワールドを変更）、UI の描画 | ローカルツール、[ゲームプレイ MOD](https://war3ai.com/ja/docs/mods/)、コンパニオン |
| `player`（`&player=N` を付ける） | 観測、N 番プレイヤーのユニットの指揮、UI の描画。**デフォルトでフェアモード**で、N 番プレイヤーの視界内のものしか見えません（`&fair=0` で無効化） | あるプレイヤーの代わりに戦う Bot や LLM |
| `observer` | 読み取り専用（下したコマンドはランタイムがそのまま拒否） | 観戦、実況、データ収集 |

`player` が使えないもの：ゲームの終了、速度変更、一時停止といったゲーム制御。他プレイヤーの手の内が見える `players`、`enemy_ai_plan`。ゲームプロセスにローカルファイルを開かせる `canvas.image`。そして JASS。`resources`、`tech`、`stats` のようにプレイヤー番号を取るクエリは、自分の分しか照会できません。

1 つの接続が 1 つのセッションで、ファストレーンを 1 本占有します（ランタイム全体で 16 本）。ゲートウェイの同時セッションは最大 12 個で、Bot、MOD、Farsight のために何本か残しておきます。切断時に片付けるのは、そのセッション自身が描いたものとホットキーだけで、ほかのプログラムが描いたものには触れません。

## メッセージ

接続すると、まず `hello` が届きます：プロトコルのバージョン、ロール、ゲームのプロセス ID、そのロールで呼べるメソッドの一覧。以降、各リクエストに `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` が付きます。[UI と入力](https://war3ai.com/ja/docs/ui-input/) を参照してください。
- 1 件の呼び出しでエラーが起きても、返るのはその 1 件のエラーだけで（`ok: false` と `error`）、接続は切れません。送られてきたものが JSON でない場合も同じです。
- イベント JSON のフィールドは [W3P プロトコル](https://war3ai.com/ja/docs/protocol/) と同じで、さらに便利フィールド（`spell`、`key`、`text`、`chat`、`button`、`player`、`mods`）が付きます。

HTTP でも使えます。単発の呼び出しや curl に向いています：`GET /api?role=player` でメソッド一覧を取得し、`POST /call` に `inst`、`role`、`method`、`args`、`kwargs` を付けて 1 回呼び出します。`/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`：局面、自軍ユニットの表、ゲームにボタンを 1 つ置く操作、イベントストリームを 1 ページで確認できます。

**その他の言語**：任意の WebSocket ライブラリ + 上の JSON だけで十分です。共有メモリに触れる必要はありません。

**LLM**：[MCP サーバー](https://war3ai.com/ja/docs/mcp/) をそのまま使ってください。よく使う操作が既成のツールになっています。

## 実測

2026-09-25、実際の試合につないで 1 項目ずつ検証し 16/16（ゲートウェイ 9 項目 + MCP 7 項目）：ハンドシェイク（dev ロールで 121 個のメソッド）、`units('me')`、局面の要約、画面へのヒント表示、ボタンの配置。購読した状態でゲーム内のそのボタンをクリック → `ui.click` がクライアントにプッシュされる。JASS。不正なユニットを渡すとその 1 件だけがエラーになる。HTTP `/call`（observer ロール）。

JS クライアント（Node）とブラウザ用デモページも動作を確認しました：Web ページから置いたボタンをゲーム内でクリックすると、Web ページのイベントログに `ui.click` が届きます。

## セキュリティ

- デフォルトではローカルの `127.0.0.1` だけで待ち受け、トークンは不要です（Farsight と同じ）。`--host` にローカル以外のアドレスを指定すると、自動的にトークンを要求します。`--auth` を付けるとローカルでもトークンが必要になります。
- **ブラウザ上のほかのサイトからは接続できません**：ブラウザが開く接続には必ずオリジン（`Origin`）が付きます。ゲートウェイが受け付けるのは自身のデモページと `--allow-origin` で指定した URL だけです。Python、Node、curl のようなプログラムはオリジンを送らないので、通常どおり接続できます。ローカルだけで待ち受けているときは `Host` も照合し、外部のドメイン名をローカルに解決させる攻撃を防ぎます。
- ロールは接続時にクライアントが自己申告します。ローカルモードではこれは取り決めであって、セキュリティ境界ではありません。アリーナでは、誰にどのロールを与えるかをレフェリープロセスが決めます。[アリーナ](https://war3ai.com/ja/arena/) を参照してください。
