平台

一个运行时,一份协议, 把游戏变成可编程的环境

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 一份

信息由运行时在游戏线程上一次性采好、推进共享内存,客户端直接读,不再往游戏线程排队。一次采集中位 0.5 ~ 0.9 ms(100 ~ 120 个单位),分段耗时一直写在世界块头里。

玩家 ×16

金、木、人口与上限、累计采集量、种族

单位 ×1024

类型、主人、坐标、血蓝、当前订单与目标、实际在打谁、等级经验技能点、每个玩家对它的可见性

单位细节 ×256

12 个技能(等级、剩余冷却)、8 个 buff、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 兜底、少数界面类操作 实机验证
3 网关(WebSocket / JSON) 档 1 + 约 1 ms 任何语言、浏览器页面、大模型(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()
世界状态一次采集
原来 11.8 ms
0.58 ms
游戏线程上;复用已确认可读的内存区
开局时单条命令
原来 4~10 ms
4~8 µs
采样器定位到同步日志,改成异步
参考大脑一轮
原来 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 的 bug,不该带崩整局游戏

这也是每个 AI 都跑在独立进程里的原因:进程内的一个空指针就是整局崩溃;独立进程崩了,只是那一方停了。

机制做法状态
不拖垮游戏 所有游戏调用包异常保护;每次排空 4 ms 时间预算(高精度计时) 已有
客户端互不影响 每个客户端一条车道、各自配额;一个卡死不挡别人 已有
过期不执行 每条命令带截止时间,过期只标记不执行,暂停恢复后不会重放 已有
可观测 每条命令的执行耗时;每次采集的分段耗时写在世界块头里 已有
断线重连 SDK 跟随换局、换进程自动重连 部分
能力熔断 某个能力连续出故障 N 次 → 标记不可用、发事件,别的能力照常 计划
扩展层

在游戏里,做你自己的东西

语义命令让 AI 像玩家一样操作;扩展层让你改变玩家看到和经历的东西 —— 画自己能点的界面,调用地图作者的全部函数。两条路按「多人局安不安全」分开。

画板 多人局安全
0.27 ~ 0.34 ms 每帧开销(9 个元素,约 63 帧 / 秒)
  • 文字框、面板、进度条、图片、贴地形的圈、带箭头的路线
  • 跟单位、世界坐标、屏幕位置;中文字体、圆角、半透明、任意颜色
  • 运行时自己画,不建游戏对象、不改游戏状态
  • Python 一行一个,也可以走 HTTP,或直接写共享内存
  • 按钮、选项卡能点,悬停高亮;画在鼠标指针下面,点它的那一下游戏收不到
画板文档
JASS 通道 单人局 · 本机工具
1291 个 JASS 函数,按名字直接调用
  • 造单位、改属性、特效、面板、对话框、声音、镜头、迷雾、天气……
  • 远见控制台、命令行、HTTP、Python,同一套脚本写法
  • 常用效果一句话一个:浮动文字、连线、范围圈、头像对白、全屏滤镜
  • 多人局里只放行只读函数,避免不同步
JASS 通道文档
画板JASS 画面函数
谁画 运行时自绘 游戏自己
多人局 安全:只画在本机画面上 只能单人局
样式 随意:字体、圆角、半透明、图片 游戏原生风格
跟着东西走 单位 / 世界坐标 / 屏幕位置 看具体函数
一次开销 每帧 0.2 ~ 0.35 ms 每次调用约 13 ms
1291 个函数,按用途分
画面效果 80 界面面板 146 镜头 44 声音音乐 50 迷雾视野 25 单位 161 物品 63 英雄 32 玩家 / 同盟 / 资源 71 触发器 / 计时器 62 地形 / 天气 45 游戏流程 57

玩家做了什么,直接进事件流

运行时直接报告玩家的操作:点了哪个画出来的按钮、按了哪个热键、点了地面哪里、选中了谁、放了什么技能、聊天框里打了什么字。游戏自己的触发事件(进入区域、对话框按钮、方向键)还可以用空触发器接:只注册事件、不写条件和动作,再数它执行了几次。