Default-on dump keeps every POST body under samples/raw/<matchid>/gsi.jsonl, with CLI overrides to disable when disk use is a concern. Co-authored-by: Cursor <cursoragent@cursor.com>
219 lines
8.5 KiB
Markdown
219 lines
8.5 KiB
Markdown
# 上分帝(Climperor)—— 方案与实施细节
|
||
|
||
`README.md` 是操作手册;本文记录背景、选型与关键决策。协作约束见 `AGENTS.md`。
|
||
|
||
---
|
||
|
||
## 1. 背景与目标
|
||
|
||
### 要解决的问题
|
||
|
||
在 Dota 2 选将阶段(以及进入游戏后)**几秒内自动获取双方 10 个英雄**,服务天梯选将辅助或赛后分析。
|
||
|
||
### 为什么 GSI 做不到
|
||
|
||
| 场景 | GSI 可获得的阵容数据 |
|
||
|------|---------------------|
|
||
| 排位 / 普通 All Pick | 仅自己的 `hero.id`;`draft` 通常为空 |
|
||
| Captains Mode | 历史上有部分 pick/ban,不稳定 |
|
||
| 观战 / 裁判视角 | 阵容字段较全 |
|
||
| 赛后 | 需依赖 OpenDota 等外部 API |
|
||
|
||
Valve 已明确关闭普通玩家视角的实时 draft
|
||
([#9562](https://github.com/ValveSoftware/Dota2-Gameplay/issues/9562)、
|
||
[#7193](https://github.com/ValveSoftware/Dota2-Gameplay/issues/7193),not planned)。
|
||
|
||
### 候选方案
|
||
|
||
| 方案 | 结论 |
|
||
|------|------|
|
||
| 官方 GSI | 拿不到双方 pick |
|
||
| Overwolf GEP | 备选,偏重 |
|
||
| 读游戏内存 | **排除**(合规) |
|
||
| 截屏 + 模板匹配 | **选定** |
|
||
| 整图问多模态大模型 | 排除为主路径(实测整图几乎全错;裁顶栏后仍不如白名单匹配) |
|
||
|
||
社区同类工具共同点:**裁固定 ROI + 模板匹配 / 小模型**,输出约束在英雄白名单内。
|
||
|
||
---
|
||
|
||
## 2. 技术方案
|
||
|
||
### 处理流程
|
||
|
||
```
|
||
截图 → 相对坐标裁 10 格 →(天梯则遮罩段位条)→ CDN NCC 匹配
|
||
→ score + margin 双门控 → hero_key | null
|
||
```
|
||
|
||
### 关键决策
|
||
|
||
**① 相对坐标**
|
||
|
||
横向 `(cx - W/2) / H`,纵向与宽高 `/ H`。同宽高比下分辨率无关;21:9 等需锚点或重标定。
|
||
|
||
**② 单一 CDN 模板库**
|
||
|
||
`templates/cdn/{hero}.png` 来自 Steam 官方头像,按顶栏实际窗口裁切后写入。
|
||
|
||
曾尝试「CDN 兜底 + real 实拍」双层库。实测修正 CDN 裁切后 **CDN-only 即可全对**,
|
||
继续攒 real 库性价比低,已放弃(见 §3 发现二)。皮肤靠会话策略,不堆变体模板。
|
||
|
||
**③ 宁可不认,不可乱认**
|
||
|
||
- `score >= min_score`(默认 0.45)
|
||
- `margin = Top1 − Top2 >= min_margin`(默认 0.04)
|
||
|
||
**④ 会话层处理皮肤与淡入**
|
||
|
||
- 选人阶段:多帧确认、允许更高分改判(`revise_gain`)
|
||
- 决策阶段:只补空槽、禁止改判(避免皮肤顶栏覆盖默认脸结论)
|
||
- best 帧优先选人阶段默认脸(`recognized` 相同时)
|
||
- 本人英雄以 GSI `hero` 覆盖视觉结果(只覆盖自己的槽)
|
||
|
||
**⑤ 截图按对局归档**
|
||
|
||
GSI 会话写入 `samples/raw/{matchid}/`;手动 `capture.py` 仍写 `samples/raw/` 根目录。
|
||
结果 JSON 仍为 `results/draft_<时间戳>.json`(内含 `match_id`)。
|
||
|
||
**⑥ GSI 全量落盘**
|
||
|
||
`gsi.dump_payloads`(默认开)把每包 POST body 追加到 `samples/raw/{matchid}/gsi.jsonl`,
|
||
行格式 `{"t": <unix>, "payload": <原文>}`。内容上限仍是 cfg 订阅字段 + 普通玩家视角;
|
||
不能靠落盘补出双方 pick。CLI:`--dump-gsi` / `--no-dump-gsi`。
|
||
|
||
---
|
||
|
||
## 3. 关键实测(2026-07-25)
|
||
|
||
环境:2560×1440 无边框;人机 + 天梯。
|
||
|
||
### 发现一:CDN 裁切窗口决定上限
|
||
|
||
初版按正方形取中心,比例与顶栏 111×88 不符,均分约 0.65。
|
||
网格搜索得到正确窗口 **x0=38, w=182, 全高**(源 256×144),均分约 0.94。
|
||
写入 `fetch_cdn_templates.py` 的 `CROP_X0/CROP_X1` 后,**CDN-only 30/30 / 40/40**。
|
||
|
||
> 兜底层差时先查素材处理,再考虑堆数据。
|
||
|
||
### 发现二:不必维护 real 库
|
||
|
||
CDN-only 开箱即用后,「打几十局攒默认脸」不再是主路径。项目改为只维护 CDN。
|
||
|
||
后续审计(迁出前原型库)进一步确认:
|
||
|
||
- 标注集上大量 `score=1.000` 往往是「从同帧裁进 real 再评同帧」的自证,夸大了 real 贡献。
|
||
- 剩余 real 与 CDN(天梯遮罩)多数 ≥0.85,近乎 CDN 副本;明显偏离的极少(如军团指挥官一类边缘格)。
|
||
- **近期问题局**(宙斯缺槽、斯拉达 CDN 分低、敌法末位、皮肤帧风行者 0.415)主要靠
|
||
**多帧会话 + 决策禁改判 + GSI 补自己** 凑齐,不是靠 real。
|
||
- 皮肤变体极多,入库皮肤模板不合理;段位条/徽章每人不同,匹配时已用遮罩忽略,
|
||
也不该为不同段位重复存图。
|
||
|
||
### 发现三:自动标定靠玩家颜色条
|
||
|
||
10 条固定玩家色定位槽位;中心用中位数槽距拟合(剔除被身后头像污染的宽条);
|
||
头像下沿用「格内列 vs 格间空隙」亮度差,勿用逐行差分(易误判到名字行)。
|
||
|
||
### 发现四:天梯段位条用遮罩
|
||
|
||
GSI 不含可靠 lobby 类型时,用右下金色勋章检测(`has_ranked_overlay`),
|
||
匹配时屏蔽底部 32% + 右侧 22%。人机无勋章走全图匹配。
|
||
|
||
### 发现五:禁用名单不靠模板
|
||
|
||
网格按主属性分块 + 客户端本地化名行优先排布;禁用/已选卡片对比度塌陷
|
||
(std 约 8–21 vs 正常 ≥33)。`heroes.json` 需含 `attr` / `name_loc`。
|
||
格数对不上时 `ok=False`,不返回残缺名单。
|
||
|
||
### 发现六:位置与「我」
|
||
|
||
定位局位置字用二值 IoU(非 OCR)。「我」优先 GSI `team_slot`;
|
||
否则姓名亮度相对差(最亮比次亮 ≥ 25)兜底。
|
||
|
||
### 发现七:客户端顶栏时机(会话策略前提)
|
||
|
||
这些是实战纠正后的事实,改 `DraftSession` 时不要违背:
|
||
|
||
| 现象 | 含义 |
|
||
|------|------|
|
||
| 顶栏默认脸 → 皮肤 | **全员选完后**才换皮肤立绘,不是一进决策就换 |
|
||
| 本机已进 `STRATEGY_TIME` | 别人可能还在选;**不能**一进决策就永久停视觉 |
|
||
| 空槽红旗倒计时 | 只出现在**未选**槽;揭晓后消失,不是挡脸主因 |
|
||
| GSI | **没有**「皮肤已加载」字段;无法精确卡「人选完、皮肤前」单帧 |
|
||
|
||
因此正确节奏是:选人 + 决策前期持续读,直到确认 10/10 或 `strategy_tail_polls` 用尽;
|
||
凑齐后停视觉,再短等 GSI 补本人英雄。单帧皮肤弱识别(如决策 3D 模型挡顶栏)属预期,
|
||
靠会话回填,不靠降阈值或堆皮肤模板。
|
||
|
||
### 发现八:揭晓淡入需要可改判
|
||
|
||
首帧揭晓常半透明 + 段位条,易误认(例:剑圣揭晓瞬间被认成幽鬼)。
|
||
`confirm_polls` 后若仍禁止改判会锁死错误。选人阶段允许
|
||
`score ≥ was_score + revise_gain` 的稳定新结果覆盖;决策阶段则关闭改判。
|
||
|
||
---
|
||
|
||
## 4. 代码结构
|
||
|
||
```
|
||
climperor/
|
||
├── config.json / heroes.json
|
||
├── common.py / recognize.py / draft_session.py
|
||
├── gsi_watch.py / gsi_setup.py
|
||
├── fetch_cdn_templates.py
|
||
├── autocalibrate.py / calibrate.py / capture.py
|
||
├── evaluate.py / roles.py / grid.py / modes.py
|
||
├── templates/cdn|roles|modes/
|
||
└── samples/labels.json # 评测真值;raw/<matchid>/ 见 .gitignore
|
||
```
|
||
|
||
运行时目录 `preview/`、`results/`、`failures/`、`samples/raw/`(含按对局子目录)不入库。
|
||
|
||
### 主要配置
|
||
|
||
| 字段 | 含义 | 默认 |
|
||
|------|------|------|
|
||
| `match.min_score` / `min_margin` | 识别门控 | 0.45 / 0.04 |
|
||
| `match.ranked_mask` | 天梯遮罩比例 | bottom 0.32, right 0.22 |
|
||
| `gsi.port` | 监听端口 | 3223 |
|
||
| `gsi.confirm_polls` | 连续同结果帧数 | 2 |
|
||
| `gsi.revise_gain` | 选人阶段改判所需分数增益 | 0.15 |
|
||
| `gsi.strategy_tail_polls` | 决策阶段继续视觉轮询 | 8 |
|
||
| `gsi.strategy_gsi_wait` | 视觉结束后等待本人 GSI 英雄 | 3.0 |
|
||
| `gsi.dump_payloads` | 全量 GSI JSONL 落盘 | true |
|
||
|
||
---
|
||
|
||
## 5. GSI 自动化
|
||
|
||
```
|
||
Dota 2 (-gamestateintegration)
|
||
→ POST → gsi_watch.py :3223
|
||
→ samples/raw/{matchid}/gsi.jsonl(可选)
|
||
→ DraftSession 轮询 recognize + roles/grid/modes
|
||
→ results/draft_*.json
|
||
```
|
||
|
||
要点:按 `matchid` 去重;截图进 `samples/raw/{matchid}/`;识别在工作线程;
|
||
未标定时降级为只截图。
|
||
|
||
天梯 AP 选人按 2/2/1 成批揭晓,本轮结束前互不可见——因此必须在
|
||
**HERO_SELECTION** 跟踪,不能只在决策时间截一张终局图;决策前期仍读几帧
|
||
以吃到最后一人揭晓(见发现七)。
|
||
|
||
---
|
||
|
||
## 6. 与 dota2-hex
|
||
|
||
独立验证项目。可选:Rust 重写并入,或本地旁路 HTTP 回传。
|
||
合规同 `dota2-hex`:**屏幕可见信息 only**。
|
||
|
||
---
|
||
|
||
## 7. 参考
|
||
|
||
- [Valve #9562](https://github.com/ValveSoftware/Dota2-Gameplay/issues/9562)
|
||
- [Valve #14915](https://github.com/ValveSoftware/Dota2-Gameplay/issues/14915)(开发者 OCR ≈85%)
|
||
- [Overwolf Dota 2 GEP](https://dev.overwolf.com/ow-native/live-game-data-gep/supported-games/dota-2/)
|
||
- Steam CDN:`https://cdn.cloudflare.steamstatic.com/apps/dota2/images/dota_react/heroes/{key}.png`
|