文件 工具

閘道

WebSocket / JSON 閘道:Python SDK 能呼叫的公開 API,JS、C#、Go、Rust、瀏覽器頁面、另一台電腦上的程式都能呼叫。三種角色,附 JS 用戶端和瀏覽器示範頁;延遲是快車道再加約 1 ms。

閘道把快車道和推送狀態包裝成 WebSocket / JSON。API 目錄 裡 Python SDK 能呼叫的公開 API,JS、C#、Go、Rust、瀏覽器頁面、另一台電腦上的程式、LLM 都能呼叫,方法名稱和參數都一樣。延遲是快車道再加約 1 ms。

最省事:遠見首頁「控制中心」→ 網關 → 啟動(停止、重新啟動、看日誌、開啟示範頁也在那張卡片上)。命令列:

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 改動世界)、畫介面本機工具、玩法模組、玩伴
player(加上 &player=N)觀察、指揮 N 號玩家的單位、畫介面;預設為公平模式,只看得見 N 號玩家視野內的東西(&fair=0 關閉)替某個玩家上場的 Bot 或 LLM
observer唯讀(執行環境直接拒絕它下的命令)觀戰、解說、資料蒐集

player 拿不到:結束遊戲、調整速度、暫停這類遊戲控制,看得見別人底牌的 players、enemy_ai_plan,會讓遊戲行程開啟本機檔案的 canvas.image,還有 JASS。resources、tech、stats 這類帶玩家編號的查詢只能查自己。

一個連線就是一個工作階段,占用一條快車道(執行環境總共 16 條)。閘道同時最多 12 個工作階段,給 Bot、模組和遠見留幾條。中斷連線時只收掉這個工作階段自己畫的東西和熱鍵,別的程式畫的不動。

訊息

連上之後會先收到 hello:協定版本、角色、遊戲行程 ID、這個角色能呼叫的方法清單。之後每個請求帶一個 id,回應帶同一個 id:

→ {"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。見 介面與輸入。
  • 一個呼叫出錯只會回報這一個(ok: false 加上 error),連線不會中斷;送來的不是 JSON 也一樣。
  • 事件 JSON 的欄位和 W3P 協定 一致,另外帶有便捷欄位(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

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 伺服器,它把常用的事情做成了現成的工具。

實測

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,擋住把外部網域解析到本機的攻擊。
  • 角色是連線時自行宣告的:在本機模式下它是約定,不是安全邊界。對戰平台要由裁判行程決定誰拿到什麼角色,見 對戰平台。