下层不知道上层存在
接口层里没有一行「该不该打」的逻辑;大脑之间互不 import,只依赖 SDK。这是对战平台能成立的前提:平台只需要「接口层 + 裁判」,谁的大脑都能插上来。
整张地图,每 50 ms 一份
信息由运行时在游戏线程上一次性采好、推进共享内存,客户端直接读,不再往游戏线程排队。一次采集中位 0.5 ~ 0.9 ms(100 ~ 120 个单位),分段耗时一直写在世界块头里。
玩家 ×16
金、木、人口与上限、累计采集量、种族
单位 ×1024
类型、主人、坐标、血蓝、当前订单与目标、实际在打谁、等级经验技能点、每个玩家对它的可见性
单位细节 ×256
12 个技能(等级、剩余冷却)、8 个 buff、6 格背包;英雄优先
生产表 ×128
训练 / 研究 / 建造 / 升级:队列、总时长、已进行时间、是否卡住
地上物品 ×256
类型、位置、耐久;捡走或用掉发事件
树 ×4096
可破坏物的位置和血量,每 2 秒刷新
地图
128 一格的可走 / 可建网格、可玩区、出生点;开局几秒算完
时间
引擎游戏时钟、游戏内时间(昼夜)、倍速、是否在局里
走哪条路,由运行时决定
采集、撤退、施法……同样是「让单位做事」,引擎里走的路各不相同。这些经验全部收进运行时:你只说做什么,它用验证过的配方执行,并在同一帧读回下令前后的订单作为回执。
- 一次提交一批,同一帧执行
- 每条都有回执:状态码 + 引擎原因码 + 执行耗时
- Shift 排队、路径点、连造
- 查询:科技计数、可行性、可见性、金矿余量、电脑对手的进攻目标
慢的从来不是跨进程
共享内存读写是纳秒级的,读一份快照约 0.05 ms。旧通道慢,是因为每个请求都要抢全机共用的一把锁、再等游戏下一次取消息(一帧一次,16 ~ 33 ms)。参考大脑 93% 的墙钟都花在等它上。
- 抢全机共用的互斥量
- 向游戏线程投递一条消息
- 等游戏下一次取消息(一帧一次)
- 等完成事件,放锁
6 个进程一起用时,吞吐被卡在约 88 次/秒。
- 每个客户端独占一条车道:一个写者一个读者,不加锁
- 执行点在游戏线程的事件分发里(每秒数百次),没有提交时开销只有几次整数比较
- 一次提交 16 条,同一次分发里全部执行;每次排空有 4 ms 时间预算
- 每条带截止时间:暂停恢复后,过期的命令不会再执行
每条命令的回执都带执行耗时,车道头记着最慢的一条和最近一次排空用了多久 —— 慢在哪里一目了然。
| 档 | 通道 | 延迟 | 谁用 | 状态 |
|---|---|---|---|---|
| 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)
工程笔记:开局 3 分钟,每条命令慢了 1000 倍
给每条命令加上执行耗时之后,发现开局头几分钟同一批里第二条命令起每条要 4 ~ 10 ms,到游戏时间约 180 秒突然降到几微秒。用运行时内置的游戏线程采样器抓了 1700 个样本,93% 落在运行时自己的日志函数里 —— 每写一行都同步打开、关闭一次日志文件,而开局每条引擎订单都要记一行。调试日志默认关闭、日志改成异步落盘后,开局第 14 秒的命令也只要 4 ~ 8 µs。
我们相信量出来的数字,不相信猜测。
每条车道都有角色
归属校验和视野过滤在运行时里做 —— 引擎的执行层本身不校验单位归属,只能在这里补。两个 AI 对打,就是同一局里开两条 player 车道。
| 角色 | 能看到 | 能做 |
|---|---|---|
| player | 本方全部 + 视野内的敌方和中立(公平模式) | 只能指挥本方单位 选手、你的 Bot |
| observer | 全图 | 不能下令;可查询、控制镜头、读 HUD 导播、解说、复盘 |
不猜,不崩
1.24 ~ 1.28 是同一套引擎结构,适合「一个运行时 + 多份 profile」。1.29 之后和重制版是另一套引擎,不承诺自动兼容。
- 识别 读 Game.dll 的版本资源和文件哈希,选 profile
- 符号表 配方引用符号名,不引用数字;每个符号带调用约定和参数形状
- 特征码兜底 未知版本上按函数开头的字节特征扫描,找到唯一匹配才用
- 开机自检 每个符号用无副作用的方式验证,不过的能力标记为不可用
- 能力清单 SDK 读到某能力不可用,调用时抛清楚的错误,而不是悄悄返回 0
一个 Bot 的 bug,不该带崩整局游戏
这也是每个 AI 都跑在独立进程里的原因:进程内的一个空指针就是整局崩溃;独立进程崩了,只是那一方停了。
| 机制 | 做法 | 状态 |
|---|---|---|
| 不拖垮游戏 | 所有游戏调用包异常保护;每次排空 4 ms 时间预算(高精度计时) | 已有 |
| 客户端互不影响 | 每个客户端一条车道、各自配额;一个卡死不挡别人 | 已有 |
| 过期不执行 | 每条命令带截止时间,过期只标记不执行,暂停恢复后不会重放 | 已有 |
| 可观测 | 每条命令的执行耗时;每次采集的分段耗时写在世界块头里 | 已有 |
| 断线重连 | SDK 跟随换局、换进程自动重连 | 部分 |
| 能力熔断 | 某个能力连续出故障 N 次 → 标记不可用、发事件,别的能力照常 | 计划 |
在游戏里,做你自己的东西
语义命令让 AI 像玩家一样操作;扩展层让你改变玩家看到和经历的东西 —— 画自己能点的界面,调用地图作者的全部函数。两条路按「多人局安不安全」分开。
- 文字框、面板、进度条、图片、贴地形的圈、带箭头的路线
- 跟单位、世界坐标、屏幕位置;中文字体、圆角、半透明、任意颜色
- 运行时自己画,不建游戏对象、不改游戏状态
- Python 一行一个,也可以走 HTTP,或直接写共享内存
- 按钮、选项卡能点,悬停高亮;画在鼠标指针下面,点它的那一下游戏收不到
- 造单位、改属性、特效、面板、对话框、声音、镜头、迷雾、天气……
- 远见控制台、命令行、HTTP、Python,同一套脚本写法
- 常用效果一句话一个:浮动文字、连线、范围圈、头像对白、全屏滤镜
- 多人局里只放行只读函数,避免不同步
| 画板 | JASS 画面函数 | |
|---|---|---|
| 谁画 | 运行时自绘 | 游戏自己 |
| 多人局 | 安全:只画在本机画面上 | 只能单人局 |
| 样式 | 随意:字体、圆角、半透明、图片 | 游戏原生风格 |
| 跟着东西走 | 单位 / 世界坐标 / 屏幕位置 | 看具体函数 |
| 一次开销 | 每帧 0.2 ~ 0.35 ms | 每次调用约 13 ms |
玩家做了什么,直接进事件流
运行时直接报告玩家的操作:点了哪个画出来的按钮、按了哪个热键、点了地面哪里、选中了谁、放了什么技能、聊天框里打了什么字。游戏自己的触发事件(进入区域、对话框按钮、方向键)还可以用空触发器接:只注册事件、不写条件和动作,再数它执行了几次。