플랫폼

하나의 런타임, 하나의 프로토콜로 게임을 프로그래밍 가능한 환경으로

W3 Runtime은 원본 게임에 주입되어 게임 스레드에서 월드 전체를 수집하고, 명령을 실행하고, 결과를 보고합니다. 여러분의 AI는 “무엇을 할지”만 말하면 되며, 버전이 붙은 공유 메모리 프로토콜로 런타임과 대화합니다.

계층 구조

하위 계층은 상위 계층의 존재를 모릅니다

인터페이스 계층에는 “싸울지 말지”를 판단하는 로직이 한 줄도 없습니다. 브레인끼리는 서로 import하지 않고 SDK에만 의존합니다. 이것이 아레나가 성립하는 전제입니다. 플랫폼에는 “인터페이스 계층 + 심판”만 있으면 되고, 누구의 브레인이든 꽂을 수 있습니다.

여러분의 Agent / Bot
어떤 모델이든, 어떤 언어든
Claude CodeCursorChatGPTQwen 로컬 모델Python Bot레퍼런스 AI
의사결정
import openwar3 · 또는 WebSocket / JSON 게이트웨이 · MCP
OpenWar3 SDK
Python · openwar3
Game / Bot스냅샷 w3world고속 레인 w3fastcombat 전투 계산pathing 경로 탐색w3claim 점유 중재canvas 캔버스jass 채널schemes 스킴
인터페이스
50 ms마다 월드 푸시 · 명령은 약 1프레임 만에 반영 · 모두 회신 포함
W3P 프로토콜 v2
공유 메모리 · 제로 카피 · 버전 관리
War3WorldWar3EventsWar3MapWar3TreesWar3Fast 명령 레인War3Canvas 캔버스
프로토콜
게임 스레드에서 배치 실행 · 건당 4~8 µs · 한 번 비울 때 4 ms 예산
W3 Runtime
게임 스레드에서 실행
월드 상태 발행기이벤트 스트림시맨틱 명령 실행기조회권한과 시점자체 드로잉 캔버스JASS 호출버전 지원상태 점검과 차단기
런타임
War3.exe 1.27 · 원본 게임, 디스크의 파일은 전혀 수정하지 않음
실시간 정보 계층

맵 전체를 50 ms마다 한 번씩

정보는 런타임이 게임 스레드에서 한 번에 수집해 공유 메모리에 푸시하고, 클라이언트는 바로 읽기만 하므로 더 이상 게임 스레드에 줄을 서지 않습니다. 수집 1회는 중앙값 0.5 ~ 0.9 ms(유닛 100 ~ 120개)이며, 구간별 소요 시간은 항상 월드 블록 헤더에 기록됩니다.

플레이어 ×16

금, 목재, 식량과 상한, 누적 채집량, 종족

유닛 ×1024

유형, 소유자, 좌표, 체력/마나, 현재 오더와 대상, 실제 공격 대상, 레벨·경험치·스킬 포인트, 플레이어별 가시성

유닛 세부 정보 ×256

스킬 12개(레벨, 남은 쿨다운), 버프 8개, 인벤토리 6칸. 영웅 우선

생산 테이블 ×128

훈련 / 연구 / 건설 / 업그레이드. 대기열, 총 소요 시간, 경과 시간, 막힘 여부

바닥 아이템 ×256

유형, 위치, 내구도. 줍거나 사용하면 이벤트 발생

나무 ×4096

파괴 가능한 오브젝트의 위치와 체력, 2초마다 갱신

맵

128 단위 한 칸의 이동 가능 / 건설 가능 그리드, 플레이 영역, 시작 위치. 게임 시작 후 몇 초 안에 계산 완료

시간

엔진 게임 시계, 게임 내 시각(낮과 밤), 배속, 게임 중 여부

이벤트 스트림 링 버퍼 8192건. 건마다 순번이 있어, 너무 느리게 읽다가 덮어쓰이면 SDK가 감지
unit.appearedunit.diedunit.removedunit.damagedorder.changedowner.changedhero.levelupitem.appeareditem.removedspell.castselection.changedmessageplayer.leftgame.startedgame.ended damage · 엔진 수준, 타격마다killed · 엔진 수준, 타격마다 production.done · 상대 것도 포함
시맨틱 명령

어떤 경로로 갈지는 런타임이 정합니다

채집, 후퇴, 시전…… 똑같이 “유닛에게 일을 시키는” 것이라도 엔진 안에서 거치는 경로는 제각각입니다. 이 노하우는 모두 런타임에 담겨 있습니다. 여러분은 무엇을 할지만 말하면, 런타임이 검증된 레시피로 실행하고 같은 프레임에 명령 전후의 오더를 읽어 회신으로 돌려줍니다.

  • 한 번에 한 배치를 제출해 같은 프레임에 실행
  • 모든 명령에 회신: 상태 코드 + 엔진 사유 코드 + 실행 시간
  • Shift 대기열, 경유점, 연속 건설
  • 조회: 기술 카운트, 실행 가능성, 가시성, 금광 잔량, 컴퓨터 상대의 공격 목표
회신과 사유 코드
moveattack_moveattackstopholdpatrolattack_groundgatherrepairbuildbuild_nearbuild_queuetraincancellearncastrallyrevivepick_upuse_itemdrop_itemgive_itemsell_itembuypathcall_to_armspausebatch
저지연

느린 원인은 프로세스 간 통신이 아니었습니다

공유 메모리 읽기/쓰기는 나노초 단위이며, 스냅샷 하나를 읽는 데 약 0.05 ms입니다. 기존 채널이 느렸던 것은 요청마다 시스템 전체가 공유하는 락 하나를 잡고, 게임이 다음에 메시지를 꺼낼 때까지(프레임당 한 번, 16 ~ 33 ms) 기다려야 했기 때문입니다. 레퍼런스 브레인의 실제 소요 시간 중 93%가 이 대기에 쓰였습니다.

기존 · 제어 채널20 ~ 40 ms / 회
  1. 시스템 전체가 공유하는 뮤텍스 획득
  2. 게임 스레드에 메시지 하나 전달
  3. 게임이 다음에 메시지를 꺼낼 때까지 대기(프레임당 한 번)
  4. 완료 이벤트를 기다린 뒤 락 해제

6개 프로세스가 함께 쓰면 처리량이 약 88회/초에서 막힙니다.

신규 · 고속 레인약 1프레임, 배치 처리
  1. 클라이언트마다 레인 하나를 독점: 쓰는 쪽 하나, 읽는 쪽 하나, 락 없음
  2. 실행 지점은 게임 스레드의 이벤트 디스패치 안(초당 수백 회)이며, 제출이 없으면 오버헤드는 정수 비교 몇 번뿐
  3. 한 번에 16개를 제출하면 같은 디스패치에서 모두 실행. 한 번 비울 때 4 ms 시간 예산
  4. 명령마다 마감 시간: 일시 정지 후 재개해도 만료된 명령은 다시 실행되지 않음

모든 명령의 회신에 실행 시간이 담기고, 레인 헤더에는 가장 느렸던 명령과 최근 비우기에 걸린 시간이 기록됩니다. 어디가 느린지 한눈에 보입니다.

등급채널지연사용 주체상태
0 푸시 스냅샷 + 이벤트 스트림 한 번 읽는 데 약 0.4 ms. 50 ms마다 새로 발행(16 ms까지 조정 가능) 모든 Bot 실게임 검증
1 고속 레인 약 1프레임. 6개 프로세스 동시 실행 시 중앙값 0.06 ms, 처리량 약 3000회/초 SDK 기본값 실게임 검증
2 제어 채널 20 ~ 40 ms 폴백, 일부 UI 관련 조작 실게임 검증
3 게이트웨이(WebSocket / JSON) 1등급 + 약 1 ms 모든 언어, 브라우저 페이지, LLM(MCP), 다른 컴퓨터 실게임 검증

실측(1.27 테스트 인스턴스, 2026-09-23 / 24)

명령 처리량
이전 88
3,000 회/초
6개 프로세스 동시 실행
동시 실행 시 대기 시간 중앙값
이전 67 ms
0.06 ms
기존 제어 채널 → 고속 레인
명령 16개
이전 121 ms
13 ms
하나씩 전송 → 한 배치로 제출
이동 명령 8개
이전 68~99 ms
6.5~10 ms
with g.batch()
월드 상태 수집 1회
이전 11.8 ms
0.58 ms
게임 스레드에서 실행, 읽기 가능이 확인된 메모리 영역 재사용
게임 초반 명령 1개
이전 4~10 ms
4~8 µs
샘플러로 동기 로그를 찾아내 비동기로 변경
레퍼런스 브레인 1라운드
이전 0.15~0.56 s
0.02~0.07 s
39분 풀게임, 작업 오류 0건
레퍼런스 브레인 최악 라운드
이전 1.3~3.2 s
0.24~0.42 s
게임 전체에서 2초 이상 걸린 라운드 없음

엔지니어링 노트: 게임 초반 3분간 명령이 1000배 느렸던 이유

명령마다 실행 시간을 기록하기 시작하자, 게임 초반 몇 분 동안 같은 배치의 두 번째 명령부터 건당 4 ~ 10 ms가 걸리다가 게임 시간 약 180초에 갑자기 몇 마이크로초로 떨어지는 현상이 보였습니다. 런타임에 내장된 게임 스레드 샘플러로 샘플 1700개를 수집해 보니 93%가 런타임 자체의 로그 함수에 있었습니다. 한 줄을 쓸 때마다 로그 파일을 동기적으로 열고 닫았는데, 게임 초반에는 엔진 오더마다 한 줄씩 기록했던 것입니다. 디버그 로그를 기본으로 끄고 로그를 비동기로 기록하도록 바꾸자, 게임 시작 14초째의 명령도 4 ~ 8 µs면 충분해졌습니다.

우리는 추측이 아니라 측정한 숫자를 믿습니다.

권한과 시점

모든 레인에는 역할이 있습니다

소유권 검증과 시야 필터링은 런타임에서 합니다. 엔진의 실행 계층 자체는 유닛 소유권을 검증하지 않으므로 여기서 보완할 수밖에 없습니다. 두 AI를 대결시키려면 같은 게임에서 player 레인을 두 개 열면 됩니다.

역할볼 수 있는 것할 수 있는 것
player 아군 전체 + 시야 안의 적과 중립(공정 모드) 아군 유닛만 지휘 가능
선수, 여러분의 Bot
observer 맵 전체 명령 불가. 조회, 카메라 제어, HUD 읽기 가능
중계 연출, 해설, 복기
다중 버전 지원 · P4

추측하지 않고, 죽지 않는다

1.24 ~ 1.28은 같은 엔진 구조라 “런타임 하나 + profile 여러 개” 방식에 적합합니다. 1.29 이후 버전과 리포지드는 다른 엔진이므로 자동 호환을 약속하지 않습니다.

  1. 식별 Game.dll의 버전 리소스와 파일 해시를 읽어 profile을 선택
  2. 심볼 테이블 레시피는 숫자가 아니라 심볼 이름을 참조하며, 심볼마다 호출 규약과 인수 형태를 명시
  3. 시그니처 스캔 폴백 알 수 없는 버전에서는 함수 시작 부분의 바이트 패턴으로 스캔하고, 유일하게 일치할 때만 사용
  4. 시작 시 자가 점검 부작용 없는 방법으로 심볼마다 검증하고, 통과하지 못한 기능은 사용 불가로 표시
  5. 기능 목록 SDK가 어떤 기능이 사용 불가임을 읽으면, 조용히 0을 반환하는 대신 호출 시 명확한 오류를 던짐
고가용성

Bot 하나의 버그가 게임 전체를 무너뜨려서는 안 됩니다

모든 AI가 독립 프로세스에서 실행되는 이유이기도 합니다. 같은 프로세스 안의 널 포인터 하나는 게임 전체의 크래시로 이어지지만, 독립 프로세스가 죽으면 그쪽만 멈출 뿐입니다.

메커니즘방식상태
게임을 무너뜨리지 않음 모든 게임 호출을 예외 보호로 감쌈. 한 번 비울 때 4 ms 시간 예산(고정밀 타이머) 구현됨
클라이언트 간 격리 클라이언트마다 레인 하나와 개별 할당량. 하나가 멈춰도 다른 클라이언트를 막지 않음 구현됨
만료되면 실행 안 함 모든 명령에 마감 시간이 있고, 만료되면 표시만 하고 실행하지 않음. 일시 정지 후 재개해도 재실행되지 않음 구현됨
관측 가능성 명령별 실행 시간, 수집 1회의 구간별 소요 시간을 월드 블록 헤더에 기록 구현됨
자동 재연결 SDK가 게임 전환, 프로세스 전환을 따라가며 자동 재연결 일부
기능 차단기 어떤 기능이 연속 N회 실패하면 → 사용 불가로 표시하고 이벤트 발생, 다른 기능은 그대로 동작 계획
확장 레이어

게임 안에서 나만의 것을 만드세요

시맨틱 명령은 AI가 플레이어처럼 조작하게 하고, 확장 레이어는 플레이어가 보고 겪는 것을 바꿉니다. 클릭할 수 있는 나만의 UI를 그리고, 맵 제작자가 쓰는 함수 전부를 호출할 수 있습니다. 두 경로는 “멀티플레이 게임에서 안전한가”를 기준으로 나뉩니다.

캔버스 멀티플레이 안전
0.27 ~ 0.34 ms 프레임당 오버헤드(요소 9개, 약 63 fps)
  • 텍스트 상자, 패널, 진행 바, 이미지, 지형에 붙는 원, 화살표가 달린 경로
  • 유닛, 월드 좌표, 화면 위치에 고정. CJK 폰트, 둥근 모서리, 반투명, 원하는 색 모두 가능
  • 런타임이 직접 그리므로 게임 오브젝트를 만들지 않고 게임 상태도 바꾸지 않음
  • Python에서는 요소 하나에 한 줄. HTTP를 쓰거나 공유 메모리에 직접 써도 됨
  • 버튼과 선택 카드를 클릭할 수 있고, 마우스를 올리면 강조됨. 마우스 포인터 아래에 그려지며, 버튼을 누른 클릭은 게임에 전달되지 않음
캔버스 문서
JASS 채널 싱글플레이 · 로컬 도구
1291 JASS 함수를 이름으로 직접 호출
  • 유닛 생성, 속성 변경, 이펙트, 패널, 대화 상자, 사운드, 카메라, 전장의 안개, 날씨……
  • Farsight 콘솔, 명령줄, HTTP, Python에서 같은 스크립트 문법
  • 자주 쓰는 효과는 한 줄에 하나: 떠오르는 텍스트, 연결선, 범위 원, 초상화 대사, 전체 화면 필터
  • 멀티플레이 게임에서는 읽기 전용 함수만 허용해 동기화 어긋남(desync)을 방지
JASS 채널 문서
캔버스JASS 화면 함수
그리는 주체 런타임 자체 드로잉 게임 자체
멀티플레이 안전: 내 화면에만 그림 싱글플레이 전용
스타일 자유: 폰트, 둥근 모서리, 반투명, 이미지 게임 기본 스타일
따라다니기 유닛 / 월드 좌표 / 화면 위치 함수마다 다름
1회 비용 프레임당 0.2 ~ 0.35 ms 호출당 약 13 ms
용도별로 나눈 함수 1291개
화면 효과 80 UI 패널 146 카메라 44 사운드와 음악 50 안개와 시야 25 유닛 161 아이템 63 영웅 32 플레이어 / 동맹 / 자원 71 트리거 / 타이머 62 지형 / 날씨 45 게임 흐름 57

플레이어가 한 일이 바로 이벤트 스트림에 들어옵니다

런타임이 플레이어의 조작을 직접 보고합니다. 화면에 그린 어떤 버튼을 클릭했는지, 어떤 단축키를 눌렀는지, 지면의 어디를 클릭했는지, 무엇을 선택했는지, 어떤 스킬을 썼는지, 채팅창에 무엇을 입력했는지. 게임 자체의 트리거 이벤트(지역 진입, 대화 상자 버튼, 방향키)는 빈 트리거로도 받을 수 있습니다. 이벤트만 등록하고 조건과 액션은 쓰지 않은 채, 몇 번 실행됐는지 세면 됩니다.