ひとつのランタイム、ひとつのプロトコルで、 ゲームをプログラム可能な環境に
W3 Runtime はオリジナルのゲームに注入され、ゲームスレッド上でワールド全体を収集し、コマンドを実行し、結果を返します。あなたの AI は「何をするか」を伝えるだけ。バージョン付きの共有メモリプロトコルを通じてランタイムと対話します。
下の層は上の層の存在を知らない
インターフェース層には「攻めるべきか」のロジックは 1 行もありません。ブレイン同士は互いに import せず、SDK だけに依存します。これがアリーナを成立させる前提です。プラットフォームに必要なのは「インターフェース層 + レフェリー」だけで、誰のブレインでも差し込めます。
マップ全体を、50 ms ごとに
情報はランタイムがゲームスレッド上で一度に収集して共有メモリにプッシュし、クライアントはそれを直接読みます。ゲームスレッドにキューイングする必要はもうありません。1 回の収集は中央値 0.5 〜 0.9 ms(ユニット 100 〜 120 体)で、各段階の所要時間は常にワールドブロックのヘッダーに記録されています。
プレイヤー ×16
ゴールド、木材、人口と上限、累計採集量、種族
ユニット ×1024
種類、所有者、座標、HP・マナ、現在のオーダーと目標、実際に攻撃している相手、レベル・経験値・スキルポイント、各プレイヤーからの可視性
ユニット詳細 ×256
スキル 12 個(レベル、残りクールダウン)、バフ 8 個、インベントリ 6 枠。ヒーロー優先
生産表 ×128
訓練 / 研究 / 建設 / アップグレード:キュー、総時間、経過時間、詰まっているかどうか
地面のアイテム ×256
種類、位置、耐久度。拾われたり使われたりするとイベントを発行
樹木 ×4096
破壊可能オブジェクトの位置と HP。2 秒ごとに更新
マップ
1 マス 128 の通行可能 / 建設可能グリッド、プレイ可能領域、スタート地点。ゲーム開始後数秒で計算完了
時間
エンジンのゲーム時計、ゲーム内時刻(昼夜)、ゲーム速度、ゲーム中かどうか
どの経路を通るかはランタイムが決める
採集、撤退、スペル使用……同じ「ユニットに何かをさせる」でも、エンジン内での経路はそれぞれ異なります。そうした知見はすべてランタイムに組み込まれています。あなたは何をするかを伝えるだけで、ランタイムが検証済みのレシピで実行し、同じフレームで命令前後のオーダーを読み戻してレシートとして返します。
- 1 回で 1 バッチを送信し、同じフレームで実行
- すべてにレシート:ステータスコード + エンジンの理由コード + 実行時間
- Shift キュー、ウェイポイント、連続建設
- クエリ:テクノロジー数、実行可否、可視性、金鉱の残量、コンピューター対戦相手の攻撃目標
遅いのはプロセス間通信ではない
共有メモリの読み書きはナノ秒単位で、スナップショット 1 つの読み取りは約 0.05 ms です。旧チャネルが遅かったのは、リクエストごとにマシン全体で共有する 1 つのロックを奪い合い、さらにゲームが次にメッセージを取り出すのを待っていたからです(1 フレームに 1 回、16 〜 33 ms)。リファレンスブレインの実時間の 93% がこの待ちに費やされていました。
- マシン全体で共有するミューテックスを奪い合う
- ゲームスレッドにメッセージを 1 つ投げる
- ゲームが次にメッセージを取り出すのを待つ(1 フレームに 1 回)
- 完了イベントを待ってロックを解放
6 プロセスで同時に使うと、スループットは約 88 回/秒で頭打ちになります。
- 各クライアントが専用のレーンを持つ:書き手 1、読み手 1、ロックなし
- 実行ポイントはゲームスレッドのイベントディスパッチ内(毎秒数百回)。送信がないときのコストは整数比較数回だけ
- 1 回で 16 件を送信し、同じディスパッチ内ですべて実行。1 回の処理には 4 ms の時間予算
- 各コマンドに期限付き:一時停止から再開したあと、期限切れのコマンドは実行されない
各コマンドのレシートには実行時間が含まれ、レーンのヘッダーには最も遅かったコマンドと直近の処理時間が記録されます。どこが遅いのかが一目でわかります。
| 区分 | チャネル | レイテンシ | 利用者 | ステータス |
|---|---|---|---|---|
| 0 | プッシュ型スナップショット + イベントストリーム | 1 回の読み取り約 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)
エンジニアリングノート:序盤の 3 分間、各コマンドが 1000 倍遅かった
各コマンドに実行時間を付けてみたところ、序盤の数分間は同じバッチ内の 2 件目以降のコマンドが 1 件あたり 4 〜 10 ms かかり、ゲーム時間が約 180 秒に達すると突然数マイクロ秒まで下がることがわかりました。ランタイム組み込みのゲームスレッドサンプラーで 1700 サンプルを取ると、93% がランタイム自身のログ関数の中にありました。1 行書くたびにログファイルを同期的に開いて閉じており、しかも序盤はエンジンのオーダーごとに 1 行記録していたのです。デバッグログをデフォルトで無効にし、ログを非同期書き込みに変えたところ、開始 14 秒時点のコマンドも 4 〜 8 µs で済むようになりました。
私たちが信じるのは測った数値であって、推測ではありません。
すべてのレーンにロールがある
所有権チェックと視界フィルタリングはランタイムで行います。エンジンの実行層自体はユニットの所有権をチェックしないため、ここで補うしかありません。2 つの AI を対戦させるには、同じゲームで player レーンを 2 本開きます。
| ロール | 見えるもの | できること |
|---|---|---|
| player | 自軍のすべて + 視界内の敵と中立(フェアモード) | 自軍のユニットだけを指揮できる プレイヤー、あなたの Bot |
| observer | マップ全体 | 命令は出せない。クエリ、カメラ操作、HUD の読み取りは可能 ディレクター、実況、振り返り |
推測しない、落ちない
1.24 〜 1.28 は同じエンジン構造なので、「1 つのランタイム + 複数の profile」が適しています。1.29 以降と Reforged は別のエンジンのため、自動互換は約束しません。
- 識別 Game.dll のバージョンリソースとファイルハッシュを読み、profile を選ぶ
- シンボル表 レシピは数値ではなくシンボル名を参照。各シンボルに呼び出し規約と引数の形が付く
- シグネチャによるフォールバック 未知のバージョンでは関数先頭のバイトパターンでスキャンし、一意に一致した場合のみ使用
- 起動時セルフチェック 各シンボルを副作用のない方法で検証し、通らなかった機能は使用不可としてマーク
- 機能一覧 SDK は使用不可の機能を読み取ると、呼び出し時にわかりやすいエラーを投げる。黙って 0 を返したりしない
1 つの Bot のバグで、ゲーム全体を落としてはならない
これが、各 AI が独立したプロセスで動く理由でもあります。プロセス内では null ポインタ 1 つでゲーム全体がクラッシュしますが、独立したプロセスならクラッシュしてもその陣営が止まるだけです。
| 仕組み | 方法 | ステータス |
|---|---|---|
| ゲームを巻き込まない | すべてのゲーム呼び出しを例外保護。1 回の処理に 4 ms の時間予算(高精度タイマー) | 実装済み |
| クライアント同士が干渉しない | クライアントごとに 1 本のレーンと個別の割り当て。1 つが固まっても他を妨げない | 実装済み |
| 期限切れは実行しない | 各コマンドに期限を付け、期限切れはマークするだけで実行しない。一時停止から再開しても再生しない | 実装済み |
| 可観測性 | 各コマンドの実行時間。各収集の段階別所要時間はワールドブロックのヘッダーに記録 | 実装済み |
| 切断時の再接続 | SDK はゲームやプロセスの切り替えに追従して自動再接続 | 一部 |
| 機能のサーキットブレーカー | ある機能が N 回連続で故障 → 使用不可としてマークしてイベントを発行、他の機能は通常どおり | 計画 |
ゲームの中で、自分だけのものをつくる
セマンティックコマンドは AI にプレイヤーと同じ操作をさせます。拡張層は、プレイヤーが見るもの・体験するものを変えます。クリックできる自分の UI を描き、マップ作者が使える関数をすべて呼び出せます。2 つの経路は「マルチプレイで安全かどうか」で分かれています。
- テキストボックス、パネル、プログレスバー、画像、地形に沿った円、矢印付きのルート
- ユニット、ワールド座標、画面位置に追従。CJK フォント、角丸、半透明、任意の色
- ランタイムが自前で描画。ゲームオブジェクトを作らず、ゲームの状態も変えない
- Python なら 1 行で 1 要素。HTTP 経由や、共有メモリへの直接書き込みも可能
- ボタンや選択カードはクリックでき、ホバーでハイライト。マウスカーソルの下に描かれ、そのクリックはゲームには届かない
- ユニット生成、属性変更、エフェクト、パネル、ダイアログ、サウンド、カメラ、霧、天候……
- Farsight コンソール、コマンドライン、HTTP、Python で、同じスクリプトの書き方
- よく使う効果は 1 行で 1 つ:フローティングテキスト、ライン、範囲円、ポートレート会話、全画面フィルター
- マルチプレイでは読み取り専用の関数だけを許可し、同期ずれを防ぐ
| キャンバス | JASS の描画関数 | |
|---|---|---|
| 描くのは | ランタイムが自前で描画 | ゲーム自身 |
| マルチプレイ | 安全:自分の画面にだけ描く | シングルプレイのみ |
| スタイル | 自由:フォント、角丸、半透明、画像 | ゲーム標準のスタイル |
| 追従対象 | ユニット / ワールド座標 / 画面位置 | 関数による |
| 1 回のコスト | 1 フレーム 0.2 〜 0.35 ms | 1 回の呼び出し約 13 ms |
プレイヤーの操作は、そのままイベントストリームへ
ランタイムがプレイヤーの操作を直接報告します:描いたボタンのどれをクリックしたか、どのホットキーを押したか、地面のどこをクリックしたか、誰を選択したか、どのスキルを使ったか、チャット欄に何を打ったか。ゲーム自身のトリガーイベント(リージョンへの進入、ダイアログのボタン、矢印キー)は、空のトリガーでも受け取れます:イベントだけを登録し、条件もアクションも書かず、その実行回数を数えます。