이 페이지의 목차
문서 도구

게이트웨이

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도 확인해, 외부 도메인을 로컬로 해석시키는 공격을 막습니다.
  • 역할은 연결할 때 클라이언트가 스스로 선언합니다. 로컬 모드에서는 약속일 뿐 보안 경계가 아닙니다. 아레나에서는 누가 어떤 역할을 받을지 심판 프로세스가 정합니다. 아레나를 참고하세요.