feat: 支持多机器人挂机、开拓日志、公屏完整日志与大地图

多机器人(src/bot/botPool.js、src/config/botsConfig.js)
- .env 用 MC_BOTS 一次配置多个账号(JSON 数组或 用户名:密码 逗号分隔),未配置时回退单机器人
- 每个机器人独立连接 / 登录 / 扩地状态机,共享一份领土数据服务与运行日志
- 密码只从 .env 读取;data/bots.json 只存启用状态、备注名、自动开始与各自的配置覆盖
- 每个机器人可覆盖全局配置(策略 / 目标邦国 / 目标数量等),未覆盖字段回退全局默认

开拓日志(src/core/expansionLog.js)
- 逐条记录开拓玩家、邦国、区块、方块坐标、时间与判定来源,追加写入 data/expansion-log.jsonl
- 支持按机器人 / 邦国 / 时间筛选、统计、导出带 BOM 的 CSV,超过 16MB 自动轮转

游戏公屏完整日志与聊天框(src/core/chatLog.js)
- 每个机器人的全部公屏 / 系统 / 动作栏消息按机器人分文件留档,自身发出的聊天与指令同样留档
- 控制台新增公屏聊天页签与消息发送框

登录验证门禁
- 游戏侧:未完成登录验证时拒绝发送指令 / 聊天,isReady() 也不放行扩地任务;
  新增 server.loginConfirmTimeoutMs 兜底,避免服务器不回提示导致永远卡在未登录
- 控制台侧:新增 WEB_PASSWORD 与 /api/login、/api/session,页面登录框验证通过才允许操作

大地图(src/core/bigMapService.js)
- 爬取服务器地图站点(squaremap)的瓦片图片与领土标记,一张图上看全部机器人坐标
- 坐标系对源站实测标定并用 27 个王城交叉验证;瓦片磁盘缓存,层级 0 启动预爬取、更高层级按需穿透
- 领土按 markers.json 原始多边形绘制(预编译 Path2D,even-odd 保留孔洞),叠加王城与机器人标签

配套改造
- TerritoryModel 新增 flyingChunksOf / ownChunksOf,邦国归属改为按名称显式传入,
  不再依赖全局 selfStateName(多机器人会互相覆盖)
- Web 接口以 /api/bots/:id/... 为主,原单机器人接口保留为默认机器人的等价别名
- 新增 scripts/check-web-ids.js 校验前端元素引用一致性

测试从 154 增至 242,全部通过。
这个提交包含在:
lyizai
2026-09-27 21:22:57 +08:00
父节点 223cf719b6
当前提交 39624da2d5
共修改 30 个文件,包含 5480 行新增和 362 行删除
+220 -37
查看文件
@@ -4,10 +4,11 @@ Minecraft 自动扩地机器人 —— 由原 Fabric 客户端模组 [AutoStore]
**Node.js + mineflayer 实现,UI 从游戏内 GUI 改为网页控制台,并针对装有 Grim 反作弊的服务器做合规适配。**
不需要开游戏客户端,一条命令启动,浏览器打开控制台即可开始自动扩地。
支持**同时挂多个玩家(机器人)**、**开拓日志**、**游戏公屏完整日志**与**爬取服务器地图的大地图**。
```
npm install
cp .env.example .env # 填入服务器地址与账号密码
cp .env.example .env # 填入服务器地址与账号密码(支持多个账号)
npm start # 打开 http://127.0.0.1:3081
```
@@ -35,6 +36,143 @@ npm start # 打开 http://127.0.0.1:3081
| 日志导出 | ✅ | 网页按钮导出到 `data/` |
| SimpCore 开放 API 第四数据源 | ✅ | 低频轮询 + 手动全服拉取 |
| Grim 反作弊适配 | ✅ 新增 | 见下方专章 |
| **多机器人同时挂机** | ✅ 新增 | 一个进程挂多个账号,各自独立连接 / 登录 / 扩地,配置可独立覆盖 |
| **开拓日志** | ✅ 新增 | 逐条记录开拓玩家、邦国、区块、方块坐标、时间、判定来源,落盘可导出 CSV |
| **游戏公屏完整日志 + 聊天框** | ✅ 新增 | 全部公屏 / 系统消息按机器人分文件留档,控制台可直接发公屏消息 |
| **大地图** | ✅ 新增 | 爬取服务器地图站点瓦片与领土标记,一张图上看全部机器人坐标 |
| **登录验证门禁** | ✅ 新增 | 游戏内登录 + 控制台登录双重校验,通过后才允许操作机器人与发送消息 |
---
## 多机器人同时挂机
### 配置
在 `.env` 里一次配好所有账号(**密码只从这里读,绝不落代码与配置文件**):
```bash
# 写法一:用户名:密码,逗号分隔
MC_BOTS=开拓一号:password1,开拓二号:password2
# 写法二:JSON 数组(用户名/密码含逗号冒号时用这个)
MC_BOTS=[{"username":"bot1","password":"p1"},{"username":"bot2","password":"p2","label":"二号机"}]
# 不设置 MC_BOTS 时回退到单机器人
MC_USERNAME=AutoStoreBot
MC_PASSWORD=xxxxxx
```
启动后控制台左栏会列出全部机器人(连接状态 / 坐标 / 任务状态 / 已开拓数),
顶栏下拉框切换当前操作的机器人。
### 每个机器人独立的配置
控制台右侧配置表单勾选「**仅对当前机器人生效**」后保存,即为该机器人写入专属配置覆盖
(存 `data/bots.json`,**不含密码**);不勾选则写入全局默认配置。未覆盖的字段一律回退到全局默认。
典型用法:给每个账号指定不同的 `目标邦国` 与 `扩地策略`,让它们分工扩张不同方向。
### 设计取舍
- **共享一份领土模型**:`markers.json` 是全服数据(约 160KB / 11.5 万区块),
没必要每个机器人各拉一遍,因此数据服务是单例、共享给全部控制器。
- **邦国归属按机器人解析**:`ExpansionController` 在 `start()` 时把识别到的邦国固化到
`controller.stateName` 并写回本次任务配置,规划器不再读全局 `selfStateName`
(否则会被最后启动的机器人覆盖,导致 A 机器人按 B 的邦国规划)。
- **「会话新占领」区块是全局共享的**:控制台地图的「己方领土」显示的是最近启动的那个机器人的邦国。
---
## 开拓日志
`src/core/expansionLog.js` 逐条记录每一次成功开拓:
| 字段 | 说明 |
| --- | --- |
| `t` | 时间戳(毫秒),界面按 `YYYY-MM-DD HH:mm:ss` 展示 |
| `bot` / `botId` | 开拓玩家(机器人游戏名 / id) |
| `state` | 开拓归属的邦国 |
| `cx` / `cz` | 区块坐标 |
| `bx` / `bz` | 方块坐标(服务器回报的精确位置,通用成功文案时为空) |
| `source` | 判定来源:`chat`(带坐标的聊天消息)/ `chat-generic`(无坐标的通用文案) |
| `sessionCount` | 该机器人本轮任务的第几个 |
- 落盘:`data/expansion-log.jsonl`(追加写入,超过 16MB 轮转为 `.1`)
- 内存:保留最近 5000 条供实时查看,磁盘上的历史不受此限制
- 控制台「开拓日志」页签支持按机器人筛选、清屏、导出带 BOM 的 CSV(Excel 打开中文不乱码)
---
## 游戏公屏完整日志与聊天框
`src/core/chatLog.js` 把每个机器人收到的**全部**消息留档,不只留开垦相关的:
- 公屏聊天(其他玩家发言,含发言人)、系统公告、动作栏消息
- 本机器人发出的聊天与指令(便于对照「指令发出后服务器回了什么」)
- 落盘:`data/chat/<机器人名>.jsonl`(每机器人一份,超过 16MB 轮转)
- 控制台「公屏聊天」页签可按当前机器人或全部机器人查看、按关键字过滤、导出为纯文本
控制台右下角的「控制台 / 聊天框」可以选择**公屏聊天**或**游戏指令**模式发送内容。
---
## 大地图
`src/core/bigMapService.js` 爬取服务器地图站点(本项目部署的是
[squaremap](https://github.com/jpenilla/squaremap))的瓦片图片与领土标记,
把所有机器人画在同一张图上。
### 坐标系(已对源站实测标定)
squaremap 前端用 `L.CRS.Simple` + `tileSize: 512`,瓦片路径为
`tiles/<world>/<z>/<x>_<y>.png`,并且:
- 瓦片图片固定 **512×512** 像素;
- 最大原生层级 `zoom.max`(本站为 3)下 **1 方块 = 1 像素**;
- 因此层级 `z` 每张瓦片覆盖 `512 × 2^(maxZoom − z)` 个方块,
瓦片 `(x, y)` 覆盖方块区间 `[x·B, (x+1)·B) × [y·B, (y+1)·B)`。
实测标定:本站层级 0 每张瓦片覆盖 4096 方块,全世界(±20480)为 **10×10 = 100 张**。
用 27 个王城坐标交叉验证——按上式换算,王城所在瓦片的平均海洋像素占比为 **28.7%**,
而错误假设(256 / 1024 方块每瓦片、整体偏移半张瓦片)分别为 76.0% / 47.3% / 41.6%,
即只有正确公式能让王城稳定落在陆地上。
### 缓存与爬取
| 层级 | 每瓦片方块数 | 全世界瓦片数 | 策略 |
| --- | --- | --- | --- |
| 0 | 4096 | 10×10 = 100 | 启动时自动预爬取 |
| 1 | 2048 | 20×20 = 400 | 按需拉取 + 永久缓存 |
| 2 | 1024 | 40×40 = 1600 | 按需拉取 + 永久缓存 |
| 3 | 512 | 80×80 = 6400 | 按需拉取 + 永久缓存 |
- 磁盘缓存:`data/mapcache/<world>/<z>/<x>_<y>.png`,命中即不再回源
- 瓦片由后端代理(`/api/bigmap/tile/:z/:x/:y`),避免浏览器直连地图站点的跨域问题
- 领土以 `markers.json` 的**原始多边形**绘制(预编译成 `Path2D`,按 even-odd 填充保留国中孔洞),
叠加本次会话新占领区块、王城与**全部机器人坐标(含名字标签)**
- 操作:滚轮缩放、拖拽平移、「全世界」/「回到机器人」一键定位、「爬取地图」手动触发预爬取
---
## 登录验证门禁
「登录验证后才允许操作」在两侧都做了强制:
**游戏侧**(`src/bot/botManager.js`)
- `canOperate()` = 已连接 + 已进入世界 + 已完成登录验证
- 未通过时 `sendCommand` / `sendChat` 直接拒绝并记警告,`isReady()` 也不放行扩地任务
- 登录 / 注册流程自身用 `{ internal: true }` 绕过门禁
- **登录确认兜底**:部分离线服登录成功后不回明确提示,`server.loginConfirmTimeoutMs`
(默认 10 秒)内既没收到成功也没收到失败提示时按「已登录」放行,避免机器人永远卡死;
收到「密码错误 / 登录失败」则标记 `loginFailed` 且不放行。设为 `0` 可禁用兜底
**控制台侧**(`src/web/server.js`)
- 配置 `WEB_PASSWORD`(或 `WEB_TOKEN`)后,页面弹出登录框,`POST /api/login` 校验通过才解锁
- 所有 `/api` 与 WebSocket 握手都要求凭证(`X-Auth-Token` 头 / `?token=` / Cookie)
- `/api/session` 与 `/api/login` 本身免鉴权,否则用户无法登录
- 控制台顶栏显示登录状态,可随时退出登录
---
@@ -161,12 +299,13 @@ npm start
| --- | --- |
| `MC_HOST` / `MC_PORT` | 服务器地址与端口 |
| `MC_VERSION` | 协议版本,默认 `1.21.4` |
| `MC_USERNAME` | 机器人游戏名 |
| `MC_BOTS` | **多机器人**账号列表:`用户名:密码` 逗号分隔,或 JSON 数组写法 |
| `MC_USERNAME` / `MC_PASSWORD` | 单机器人兜底(未设置 `MC_BOTS` 时生效) |
| `MC_AUTH` | `offline`(离线服)或 `microsoft`(正版) |
| `MC_PASSWORD` | 服务器登录 / 注册密码,供 `{password}` 占位符使用 |
| `WEB_HOST` / `WEB_PORT` | 控制台监听地址与端口 |
| `WEB_TOKEN` | 控制台访问令牌;非本机访问**必须**设置(页面静态资源不校验,`/api` 与 WebSocket 握手校验) |
| `DYNMAP_BASE` / `DYNMAP_WORLD` | 地图数据源根地址与世界目录名 |
| `WEB_TOKEN` | 控制台令牌;非本机访问**必须**设置(页面静态资源不校验,`/api` 与 WebSocket 握手校验) |
| `WEB_PASSWORD` | 控制台登录密码:配置后页面弹出登录框,验证通过才允许操作机器人的控制台 / 聊天框 |
| `DYNMAP_BASE` / `DYNMAP_WORLD` | 地图站点根地址与世界目录名(领土数据 + 大地图瓦片) |
| `SIMP_API_KEY` | SimpCore 开放 API Key(可选) |
---
@@ -175,37 +314,57 @@ npm start
打开控制台后:
- **左栏**:连接状态、任务状态机、领土数据源、Grim 合规面板
- **中栏**:领土地图(Canvas,滚轮缩放 / 拖拽平移 / 图层开关)+ 实时运行日志
- **右栏**:分 7 个标签页的配置表单 + 手动指令输入
- **顶栏**:机器人下拉选择、连接 / 任务徽标、控制台登录状态、开始扩地 / 停止 / 重连
- **左栏**:机器人列表(状态 / 坐标 / 任务 / 已开拓数)、连接、任务、领土数据源、Grim 合规
- **中栏**:
- 地图页签 —— **大地图**(瓦片 + 领土多边形 + 全部机器人坐标)与 **领土地图**(区块级)
- 日志页签 —— **运行日志** / **公屏聊天** / **开拓日志**
- **右栏**:8 个标签页的配置表单(可切换「仅对当前机器人生效」)+ 控制台 / 聊天框
顶栏按钮:开始扩地 / 停止 / 重连。
地图图层:己方领土(蓝)、本次占领(绿)、飞地疏漏(红)、当前目标(黄)、机器人(白点)、王城(紫)。
地图图层:己方领土(蓝)、本次占领(绿)、飞地疏漏(红)、当前目标(黄)、机器人(白点 / 黄点)、王城(紫)。
### REST / WebSocket 接口
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| GET | `/api/status` | 机器人 / 任务 / 数据源完整状态 |
| GET | `/api/config` | 当前配置 |
| POST | `/api/config` | 保存并立即应用配置 |
| GET | `/api/map?full=1` | 地图数据(`full=1` 附带全部邦国区块) |
| GET | `/api/session` | 控制台登录状态(免鉴权,用于决定是否弹登录框) |
| POST | `/api/login` | 控制台登录(免鉴权) |
| GET | `/api/status` | 机器人池 / 任务 / 数据源 / 日志 / 大地图完整状态 |
| GET | `/api/bots` | 全部机器人状态列表 |
| GET | `/api/bots/:id/status` | 单个机器人状态 |
| GET | `/api/bots/:id/settings` | 单个机器人的设置与生效配置 |
| POST | `/api/bots/:id/settings` | 保存备注名 / 启用 / 自动开始 / 配置覆盖 |
| POST | `/api/bots/:id/task/start` | 启动该机器人的扩地(body 为配置覆盖) |
| POST | `/api/bots/:id/task/stop` | 停止该机器人的扩地 |
| POST | `/api/bots/:id/reconnect` | 重连该机器人 |
| POST | `/api/bots/:id/command` | 向该机器人发送游戏指令(要求已登录验证) |
| POST | `/api/bots/:id/chat` | 向该机器人发送公屏消息(要求已登录验证) |
| GET | `/api/bots/:id/chat` | 该机器人的公屏日志(`limit` / `since` / `kind` / `keyword`) |
| POST | `/api/bots/:id/chat/export` | 导出该机器人的公屏日志到 `data/` |
| GET | `/api/expansion-log` | 开拓日志(`limit` / `botId` / `state` / `since`)+ 统计 |
| POST | `/api/expansion-log/export` | 导出开拓日志 CSV 到 `data/` |
| POST | `/api/expansion-log/clear` | 清空内存中的开拓日志(磁盘保留) |
| GET | `/api/bigmap` | 大地图元信息(坐标系、瓦片范围、缓存进度、机器人坐标、领土标记) |
| GET | `/api/bigmap/markers` | 大地图领土标记(多边形 + 王城) |
| GET | `/api/bigmap/tile/:z/:x/:y` | 瓦片代理(缓存优先,避免跨域) |
| POST | `/api/bigmap/crawl` | 手动触发批量爬取瓦片 |
| POST | `/api/bigmap/cache/clear` | 清空瓦片缓存 |
| GET | `/api/config` | 全局配置 |
| POST | `/api/config` | 保存并立即应用全局配置 |
| GET | `/api/map?full=1&bot=<id>` | 领土地图数据(`full=1` 附带全部邦国区块,`bot` 指定视角机器人) |
| GET | `/api/logs` | 运行日志 |
| POST | `/api/logs/export` | 导出日志到 `data/` |
| POST | `/api/task/start` | 启动扩地(可带配置覆盖) |
| POST | `/api/task/stop` | 停止扩地 |
| POST | `/api/logs/export` | 导出运行日志到 `data/` |
| POST | `/api/map/refresh` | 手动刷新地图数据 |
| POST | `/api/api/fetch-all` | 手动拉取全服领土(重接口) |
| POST | `/api/command` | 发送游戏指令 |
| POST | `/api/bot/reconnect` | 重连服务器 |
| WS | `/ws` | 实时状态推送(1s)与日志流 |
| POST | `/api/task/start`、`/api/task/stop`、`/api/command`、`/api/bot/reconnect` | 默认机器人的等价别名(兼容单机器人用法) |
| WS | `/ws` | 实时推送:`status`(1s)、`log`、`chat`、`expansion` |
---
## 配置项
配置保存在 `data/autostore.json`,可通过控制台表单修改。完整字段见
配置保存在 `data/autostore.json`(全局默认),可通过控制台表单修改;勾选「仅对当前机器人生效」时
写入 `data/bots.json` 作为该机器人的配置覆盖。完整字段见
[`src/config/expansionConfig.js`](src/config/expansionConfig.js),分组如下:
| 分组 | 内容 |
@@ -215,8 +374,9 @@ npm start
| 寻路 | 疾跑 / 游泳 / 跳跃 / 挖方块 / 放方块、高度偏好、跑酷、搭塔、下落高度、液体代价 |
| 生存 | 自动进食、自动攻击、耐久保护、战斗筛选、水域策略、自动用船 |
| 数据 | 地图刷新间隔、SimpCore API |
| 大地图 | 开关、世界名、预爬取层级、最大层级、覆盖半径、启动预爬取、下载并发数 |
| Grim | 合规层全部阈值(见上) |
| 服务器 | 开垦模式指令、登录 / 注册模板、指令节流、重连 |
| 服务器 | 开垦模式指令、登录 / 注册模板、登录确认超时、指令节流、重连 |
---
@@ -230,36 +390,52 @@ npm run dev # 文件变更自动重启
### 测试覆盖
154 个测试用例,覆盖:
242 个测试用例,覆盖:
- 区块坐标编解码(含负数与边界)
- 领土模型:飞地识别、会话占领、API 数据合并
- 规划器:四种策略、S 形顺序、**接壤只算己方领土**(历史 bug 回归)、他国区块切断连通
- markers.json 解析:奇偶填充孔洞、王城换算、颜色解析
- 领土模型:飞地识别、会话占领、按邦国名取飞地与己方区块、API 数据合并、多边形保留
- 规划器:四种策略、S 形顺序、**接壤只算己方领土**(历史 bug 回归)、他国区块切断连通、
按 `config.stateName` 解析己方邦国
- markers.json 解析:奇偶填充孔洞、王城换算、颜色解析、多边形扁平化
- 字幕 / 聊天解析:进度、带坐标成功、通用成功、模糊匹配
- Grim 合规层:旋转限速、动作互斥、攻击冷却表、距离 / 视线校验、物理自检
- 战斗筛选:中立生物豁免、条件敌对、猪灵金甲判定、耐久保护
- 寻路服务:Grim 约束是否真正施加到 Movements、高度偏好目标
- 控制器状态机:完整扩地流程、超时重试、走两步、回城、耐久保护停止、完成
- Web 控制台:地图负载、REST 接口、令牌鉴权
- 控制器状态机:完整扩地流程、超时重试、走两步、回城、耐久保护停止、完成、
多机器人邦国归属隔离、开拓日志写入与去重
- 多机器人:凭据解析(JSON / 逗号分隔 / 兜底)、机器人池组装、配置覆盖与持久化、
密码不落配置文件、未登录时拒绝发送、全局配置下发保留各自覆盖
- 机器人登录门禁:就绪判定、登录成功 / 失败 / 已登录提示、注册与登录自动补发、
确认兜底超时、公屏消息类型映射
- 开拓日志:字段记录、筛选、统计、JSONL 落盘与重启恢复、损坏行容错、环形缓冲、CSV 导出
- 公屏日志:按机器人隔离、类型 / 关键字过滤、分文件落盘、文件名安全化、文本导出
- 大地图:坐标系换算(含对源站实测标定的王城用例)、瓦片范围、缓存命中、批量爬取、缓存清理
- Web 控制台:地图负载、单 / 多机器人接口、聊天与开拓日志接口、瓦片代理、
令牌与登录鉴权、WebSocket 握手鉴权
### 目录结构
```
src/
├── index.js 入口:装配模块、启动 Web 控制台、驱动 tick
├── index.js 入口:装配模块、启动 Web 控制台、驱动多机器人 tick
├── config/
│ ├── expansionConfig.js 配置默认值 / 校验 / 归一化
│ └── configStore.js 配置 JSON 持久化
│ ├── configStore.js 全局配置 JSON 持久化
│ ├── botsConfig.js 从 .env 解析多机器人凭据(密码只在这里读)
│ └── botStore.js 机器人设置持久化(不含密码)
├── core/
│ ├── chunkPos.js 区块坐标编解码(整数键,零临时对象)
│ ├── territoryModel.js 领土模型:区块集合、飞地、会话占领、API 合并
│ ├── territoryDataService.js Dynmap markers.json 拉取与解析
│ ├── territoryModel.js 领土模型:区块集合、飞地、会话占领、API 合并、多边形
│ ├── territoryDataService.js markers.json 拉取与解析
│ ├── simpApiClient.js SimpCore 开放 API 客户端
│ ├── expansionPlanner.js 四种扩地策略
│ └── subtitleChatMonitor.js 字幕 / 聊天解析与模糊匹配
│ ├── subtitleChatMonitor.js 字幕 / 聊天解析与模糊匹配
│ ├── expansionLog.js ★ 开拓日志(玩家 / 邦国 / 区块 / 时间 / 来源)
│ ├── chatLog.js ★ 游戏公屏完整日志(按机器人分文件)
│ └── bigMapService.js ★ 大地图瓦片爬取与缓存
├── bot/
│ ├── botManager.js 连接、登录、重连、事件归一化
│ ├── botPool.js ★ 机器人池:同时挂多个玩家
│ ├── botManager.js 连接、登录验证、公屏留档、指令 / 聊天发送
│ ├── grimCompat.js ★ Grim 反作弊合规层
│ ├── pathingService.js 寻路服务(替代 Baritone)
│ ├── autoCombat.js 战斗筛选与耐久检查
@@ -267,12 +443,19 @@ src/
│ ├── autoBoat.js 自动用船(默认关闭)
│ └── durabilityGuard.js 耐久保护
├── controller/
│ └── expansionController.js 扩地任务状态机
│ └── expansionController.js 扩地任务状态机(按机器人实例化)
├── web/
│ ├── server.js Express + WebSocket
│ ├── server.js Express + WebSocket + 登录鉴权
│ └── public/ 控制台前端(原生 HTML/CSS/JS,无构建步骤)
└── util/
└── logger.js 环形日志缓冲
data/ (已 gitignore,运行时生成)
├── autostore.json 全局配置
├── bots.json 机器人设置(启用 / 备注 / 自动开始 / 配置覆盖,不含密码)
├── expansion-log.jsonl 开拓日志
├── chat/<机器人>.jsonl 游戏公屏日志
└── mapcache/<world>/<z>/ 大地图瓦片缓存
```
---