게이트웨이
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가 더해지는 정도입니다.
가장 간단한 방법: Farsight 첫 화면 “컨트롤 센터” → 게이트웨이 → 시작(중지, 재시작, 로그 보기, 데모 페이지 열기도 그 카드에서 합니다). 명령줄:
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 # 직접 만든 웹 페이지에서도 연결 가능
연결과 역할
연결 주소: ws://127.0.0.1:8870/ws?inst=9&role=dev(inst= 대신 pid=를 써도 됩니다. 토큰이 필요하면 &token=을 붙입니다).
| 역할 | 호출할 수 있는 것 | 적합한 용도 |
|---|---|---|
dev | 전부: 관찰, 명령, 게임 제어, 샌드박스(JASS로 월드 변경), UI 그리기 | 로컬 도구, 게임플레이 모드, 동료 |
player(&player=N 추가) | 관찰, N번 플레이어의 유닛 지휘, UI 그리기. 기본값은 공정 모드로, N번 플레이어의 시야 안에 있는 것만 보임(&fair=0으로 끔) | 특정 플레이어 대신 출전하는 Bot이나 LLM |
observer | 읽기 전용(내린 명령은 런타임이 바로 거부) | 관전, 해설, 데이터 수집 |
player가 쓸 수 없는 것: 게임 종료, 속도 변경, 일시 정지 같은 게임 제어, 다른 플레이어의 패를 볼 수 있는 players와 enemy_ai_plan, 게임 프로세스가 로컬 파일을 열게 하는 canvas.image, 그리고 JASS. resources, tech, stats처럼 플레이어 번호를 받는 조회는 자기 자신만 조회할 수 있습니다.
연결 하나가 세션 하나이며, 고속 레인 하나를 차지합니다(런타임에는 모두 16개). 게이트웨이는 동시에 최대 12개 세션까지 받아 Bot, 모드, Farsight 몫으로 몇 개를 남겨 둡니다. 연결이 끊기면 그 세션이 직접 그린 것과 단축키만 정리하고, 다른 프로그램이 그린 것은 건드리지 않습니다.
메시지
연결되면 먼저 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가 들어 있습니다. UI와 입력을 참고하세요. - 호출 하나에서 오류가 나면 그 호출에만 오류를 돌려주며(
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에서만 수신 대기하며, 토큰이 필요 없습니다(Farsight와 같음).--host가 로컬 주소가 아니면 자동으로 토큰을 요구하고,--auth를 쓰면 로컬에서도 요구합니다. - 브라우저 안의 다른 웹사이트는 연결할 수 없습니다: 브라우저가 여는 연결에는 모두 출처(
Origin)가 붙으며, 게이트웨이는 자체 데모 페이지와--allow-origin으로 지정한 주소만 받아들입니다. Python, Node, curl 같은 프로그램은 출처를 붙이지 않으므로 평소처럼 연결됩니다. 로컬에서만 수신 대기할 때는Host도 확인해, 외부 도메인을 로컬로 해석시키는 공격을 막습니다. - 역할은 연결할 때 클라이언트가 스스로 선언합니다. 로컬 모드에서는 약속일 뿐 보안 경계가 아닙니다. 아레나에서는 누가 어떤 역할을 받을지 심판 프로세스가 정합니다. 아레나를 참고하세요.