feat: 1.7.0 落地 TODO 全部待办(地面修正 / 手动执行器 / 自救 / 回城 / 租约 / 通知)

按 TODO.md 的优先级把待办全部落地为代码,详见 CHANGELOG 的 [1.7.0]。

- 「走不动」攻关:地面状态修正(syncGroundToPhysics / trustSetbackOnGround)、
  出站包类型分布、备选执行器 manualMover(只借 A* 规划、自己按键执行)
- 原点自救分两轮并统计成败;回城前评估「原地是否更差」并记录前后坐标
- 新增目标区块租约表(targetLease)避免多机器人撞同一块
- 定时消息支持 cron 与「期望回复」确认;新增人工介入 Webhook 通知(notifier)
- 大地图后台预热与视野裁剪;控制台日志按帧批量写入、配置面板支持搜索

顺带修掉两个影响部署的细节:
- 部署脚本(deploy.js / ssh-helper.js)改为也能从 .env 读 SSH 凭据 ——
  在命令行传密码会留在 shell 历史与进程列表里,写进已被忽略的 .env 更安全;
  显式环境变量仍然优先。.env.example 补上占位符并说明这些只在本机用。
- package.json / package-lock.json 版本对齐到 1.7.0(此前停留在 1.1.0,
  与 CHANGELOG 的语义化版本声明脱节)。

测试:368 → 436 个用例,`npm test` 与 `npm run check` 全绿。
这个提交包含在:
lyizai
2026-09-28 22:46:39 +08:00
父节点 8d1b8d5122
当前提交 51664c55b6
共修改 31 个文件,包含 3535 行新增和 251 行删除
+86 -23
查看文件
@@ -36,11 +36,13 @@ npm start # 打开 http://127.0.0.1:3081
| 日志导出 | ✅ | 网页按钮导出到 `data/` |
| SimpCore 开放 API 第四数据源 | ✅ | 低频轮询 + 手动全服拉取 |
| Grim 反作弊适配 | ✅ 新增 | 见下方专章 |
| **多机器人同时挂机** | ✅ 新增 | 一个进程挂多个账号,各自独立连接 / 登录 / 扩地,配置可独立覆盖 |
| **多机器人同时挂机** | ✅ 新增 | 一个进程挂多个账号,各自独立连接 / 登录 / 扩地,配置可独立覆盖;**目标区块带租约**,不会撞在同一块上 |
| **开拓日志** | ✅ 新增 | 逐条记录开拓玩家、邦国、区块、方块坐标、时间、判定来源,落盘可导出 CSV |
| **游戏公屏完整日志 + 聊天框** | ✅ 新增 | 全部公屏 / 系统消息按机器人分文件留档,控制台可直接发公屏消息 |
| **大地图** | ✅ 新增 | 爬取服务器地图站点瓦片与领土标记,一张图上看全部机器人坐标 |
| **大地图** | ✅ 新增 | 爬取服务器地图站点瓦片与领土标记,一张图上看全部机器人坐标;高层级瓦片后台预热 |
| **登录验证门禁** | ✅ 新增 | 游戏内登录 + 控制台登录双重校验,通过后才允许操作机器人与发送消息 |
| **定时消息(间隔 / cron)** | ✅ 新增 | 固定间隔或 cron 表达式定时发送,可选「期望回复」确认发送是否真的生效 |
| **人工介入提醒(Webhook)** | ✅ 新增 | 等待验证码 / 原点自救失败时推送到飞书等自定义机器人,不必一直盯控制台 |
---
@@ -76,6 +78,11 @@ MC_PASSWORD=xxxxxx
- **共享一份领土模型**:`markers.json` 是全服数据(约 160KB / 11.5 万区块),
没必要每个机器人各拉一遍,因此数据服务是单例、共享给全部控制器。
- **目标区块带租约**(`src/core/targetLease.js`):领土模型是全局共享的,但「谁要去占哪一块」
原先只存在各自的队列里,彼此看不见 —— 多机器人很容易撞在同一批候选区块上。
现在抢到的机器人独占该区块(其余机器人跳过),持有者持续续租,
掉线 / 卡死后租约到期自动失效,不会永久锁死区块;
候选区块全被预定时会等 15 秒再规划,而不是提前判「没有可用区块」。
- **邦国归属按机器人解析**:`ExpansionController` 在 `start()` 时把识别到的邦国固化到
`controller.stateName` 并写回本次任务配置,规划器不再读全局 `selfStateName`
(否则会被最后启动的机器人覆盖,导致 A 机器人按 B 的邦国规划)。
@@ -158,12 +165,17 @@ squaremap 前端用 `L.CRS.Simple` + `tileSize: 512`,瓦片路径为
| 层级 | 每瓦片方块数 | 全世界瓦片数 | 策略 |
| --- | --- | --- | --- |
| 0 | 4096 | 10×10 = 100 | 启动时自动预爬取(以机器人位置为中心,只爬当前图幅内) |
| 1 | 2048 | 20×20 = 400 | 按需拉取 + 永久缓存 |
| 2 | 1024 | 40×40 = 1600 | 按需拉取 + 永久缓存 |
| 3 | 512 | 80×80 = 6400 | 按需拉取 + 永久缓存 |
| 1 | 2048 | 20×20 = 400 | 按需拉取 + **后台预热** + 永久缓存 |
| 2 | 1024 | 40×40 = 1600 | 按需拉取 + **后台预热** + 永久缓存 |
| 3 | 512 | 80×80 = 6400 | 按需拉取 + **后台预热** + 永久缓存 |
- **后台预热**:机器人移动到新区域(默认超过 512 格)时,用低并发(批量爬取并发的一半)
顺带把周边高层级瓦片拉下来,避免首次缩放时干等;距上次预热超过 10 分钟也会补一次。
已缓存的瓦片只会做一次 `statSync`,不会重复下载;正在批量爬取时预热自动让路
- 磁盘缓存:`data/mapcache/<world>/<z>/<x>_<y>.png`,命中即不再回源
- 瓦片由后端代理(`/api/bigmap/tile/:z/:x/:y`),避免浏览器直连地图站点的跨域问题
- 领土多边形按**视野裁剪**绘制(预编译路径时顺带记包围盒,视野外的直接跳过),
全世界视角 + 全部邦国也不会掉帧
- 领土以 `markers.json` 的**原始多边形**绘制(预编译成 `Path2D`,按 even-odd 填充保留国中孔洞),
叠加本次会话新占领区块、王城与**全部机器人坐标(含名字标签)**
- 操作:滚轮缩放、拖拽平移、图幅切换、「爬取周边」/「回到机器人」一键定位
@@ -235,6 +247,11 @@ V2 的接口级配额很小(末影龙只有 **1 次/分钟**),因此 V2
若该服务器提供固定验证码,可在配置的「账号绑定 / 二次验证模板」(`server.authCommand`)里填写
- **失败信息可操作**:发送失败会区分「未连接」「登录被服务器拒绝」「未通过登录验证」三种情况,
而不是笼统报一句「发送失败」
- **人工介入提醒(Webhook)**:验证码只有 120 秒时限,错过就得等下次重登。
在控制台「服务器 → 通知提醒」勾选「启用通知」并填 Webhook 地址(地址也可走环境变量
`NOTIFY_WEBHOOK_URL`)后,服务器要求验证码时会推送一条提醒到飞书 / Lark 自定义机器人;
原文里带上服务器提示与处理步骤。同一问题默认 60 秒内只推一次,避免重连循环里反复轰炸;
接口里只暴露 Webhook 域名,不会把带凭证的完整地址打进响应
**控制台侧**(`src/web/server.js`)
@@ -252,17 +269,25 @@ V2 的接口级配额很小(末影龙只有 **1 次/分钟**),因此 V2
### 0. 一键更新(已装好之后日常用这个)
```powershell
# Windows PowerShell —— 凭据走环境变量,脚本里不写死密码
$env:SSH_HOST='103.236.99.177'; $env:SSH_PORT='33851'
$env:SSH_USER='root'; $env:SSH_PASS='<服务器密码>'
# 推荐:把 SSH 凭据写进 .env(已被 .gitignore 忽略),然后直接跑
npm run deploy
```
```bash
# 也可以临时用环境变量覆盖(.env 里的值仍作为兜底)
# Windows PowerShell
$env:SSH_HOST='<服务器 IP>'; $env:SSH_PORT='<端口>'
$env:SSH_USER='root'; $env:SSH_PASS='<服务器密码>'
npm run deploy
# Linux / macOS
SSH_HOST=... SSH_PORT=... SSH_USER=... SSH_PASS='<密码>' npm run deploy
```
> 推荐写进 `.env` 而不是命令行:命令行传密码会留在 shell 历史与进程列表里。
> 需要哪些变量见 `.env.example` 的「部署」一节(`SSH_HOST` / `SSH_PORT` / `SSH_USER` / `SSH_PASS`,
> 可选 `DEPLOY_APP` / `DEPLOY_PORT`)。**这些只在本机用,服务器上的 `.env` 不需要填。**
`scripts/deploy.js` 会依次做:打包 → 上传 → 远程执行 `update.sh` →
轮询确认控制台返回 200,任一步失败都会明确报错。
需要单条远程命令时用 `node scripts/ssh-helper.js exec "<命令>"`。
@@ -442,6 +467,23 @@ Grim 的 Prediction 检查正是逐 tick 复算玩家位置并与上报位置比
| `liquidCost` | 200(avoid 模式) | mineflayer 液体物理与原版差异最大,优先绕开水域 |
| `allowFreeMotion` | 关闭 | 禁止自由运动 |
### 6. 地面状态修正与手动执行器(「走不动」的两级应对)
服务端频繁下发 `position` 包时,mineflayer 会把 `entity.onGround` 置为 false 并清零速度,
物理引擎于是长期走「空中」分支 —— 表现就是「按着前进键、水平速度恒为 0」。为此有两级措施:
| 措施 | 配置 | 说明 |
| --- | --- | --- |
| 出站包复核 | `grim.reportOnGroundFromWorld` | 发包前用脚下方块复核 `onGround` |
| **方案 A**:物理引擎对齐 | `grim.syncGroundToPhysics` | 每个物理 tick 在「物理算完、发包之前」把 `entity.onGround` 与世界数据对齐,让移动回到地面分支 |
| **方案 B**:不信例行同步 | `grim.trustSetbackOnGround` + `grim.setbackSyncEpsilon` | 服务端位置与本地几乎一致(例行同步)时只采纳坐标、保留我们自己的 `onGround` |
| **备选执行器** | `pathing.executor: 'manual'` | 只用 A* 算路径,执行改为自己按键(转向 → 前进 → 卡坎跳),时序与「移动测试」的手动按键完全一致 |
方案 A / B 与 `grim.enabled` 无关(实测关闭合规层后回弹依旧)。
控制台「Grim 合规」卡片会显示 `物理对齐 / 保住 / 最近同步差` 三个计数,
用它们可以判断修正是否真的生效;`/api/status` 的 `packetStats` 里还有出站包类型分布,
便于与旧模组抓包逐项对照。
---
## 与原模组的行为差异(重要)
@@ -555,7 +597,19 @@ npm start
### 定时重复发送消息
挂机时常需要周期性发同一条指令(例如每 300 秒 `/u restore confirm` 维持疆土民心)。
左栏「定时消息」卡片里点「+ 新增」,填内容与间隔即可;以 `/` 开头按游戏指令发送,否则按公屏消息发送。
左栏「定时消息」卡片里点「+ 新增」,填内容即可;以 `/` 开头按游戏指令发送,否则按公屏消息发送。
**两种调度方式**:
- **固定间隔**:每隔 N 秒发一次(下限 5 秒);
- **cron 表达式**:5 段「分 时 日 月 周」(本地时间),例如
`0 9 * * *` = 每天 09:00、`0/30 * * * *` = 每 30 分钟、`0 12 * * 1-5` = 工作日 12:00。
支持 `*`、步长、区间、列表;写错的表达式不会被静默吞掉,列表里会显示原因并按间隔发送。
**发送结果确认**(可选):填了「期望回复」(正则)后,发出消息会等待服务器的回复:
- 命中 → 任务状态显示 `✓ 已确认`,日志记一条「已确认生效」;
- 超时未回复 → 显示 `⚠ 未确认` 并写警告 —— 否则 `/u restore confirm` 到底成没成,只能靠人翻日志。
实现上刻意**不用**朴素的 `setInterval`:
@@ -624,9 +678,11 @@ npm start
| 寻路 | 疾跑 / 游泳 / 跳跃 / 挖方块 / 放方块、高度偏好、跑酷、搭塔、下落高度、液体代价 |
| 生存 | 自动进食、自动攻击、耐久保护、战斗筛选、水域策略、自动用船 |
| 数据 | 地图刷新间隔、SimpCore API |
| 大地图 | 开关、世界名、预爬取层级、最大层级、**图幅半径、是否跟随机器人**、启动预爬取、下载并发数 |
| Grim | 合规层全部阈值 + 协议补包开关(见上) |
| 服务器 | 开垦模式指令、登录 / 注册模板、登录确认超时、指令节流、重连 |
| 大地图 | 开关、世界名、预爬取层级、最大层级、**图幅半径、是否跟随机器人**、启动预爬取、下载并发数、**后台预热(开关 / 半径 / 层级 / 移动阈值 / 最小间隔)** |
| 协调 | 目标区块租约时长(多机器人避免撞车) |
| Grim | 合规层全部阈值 + 协议补包开关 + **地面状态修正(方案 A / B)**(见上) |
| 服务器 | 开垦模式指令、登录 / 注册 / 绑定模板、登录确认超时、指令节流、重连 |
| 通知 | Webhook 开关与地址(`NOTIFY_WEBHOOK_URL` 兜底)、验证码提醒、自救失败提醒 |
---
@@ -640,7 +696,7 @@ npm run dev # 文件变更自动重启
### 测试覆盖
242 个测试用例,覆盖:
436 个测试用例,覆盖:
- 区块坐标编解码(含负数与边界)
- 领土模型:飞地识别、会话占领、按邦国名取飞地与己方区块、API 数据合并、多边形保留
@@ -648,19 +704,23 @@ npm run dev # 文件变更自动重启
按 `config.stateName` 解析己方邦国
- markers.json 解析:奇偶填充孔洞、王城换算、颜色解析、多边形扁平化
- 字幕 / 聊天解析:进度、带坐标成功、通用成功、模糊匹配
- Grim 合规层:旋转限速、动作互斥、攻击冷却表、距离 / 视线校验、物理自检
- Grim 合规层:旋转限速、动作互斥、攻击冷却表、距离 / 视线校验、物理自检、
**地面状态修正(方案 A / B 的正反用例)**、出站包类型分布
- 战斗筛选:中立生物豁免、条件敌对、猪灵金甲判定、耐久保护
- 寻路服务:Grim 约束是否真正施加到 Movements、高度偏好目标
- 寻路服务:Grim 约束是否真正施加到 Movements、高度偏好目标、**手动执行器(沿路点行进、跳坎、卡住终止、取消、分段重试)**
- 控制器状态机:完整扩地流程、超时重试、走两步、回城、耐久保护停止、完成、
多机器人邦国归属隔离、开拓日志写入与去重
多机器人邦国归属隔离、开拓日志写入与去重、**回城前「原地是否更差」评估与前后坐标**、
**目标租约的挑选 / 续租 / 归还**
- 多机器人:凭据解析(JSON / 逗号分隔 / 兜底)、机器人池组装、配置覆盖与持久化、
密码不落配置文件、未登录时拒绝发送、全局配置下发保留各自覆盖
密码不落配置文件、未登录时拒绝发送、全局配置下发保留各自覆盖、**目标区块租约表**
- 机器人登录门禁:就绪判定、登录成功 / 失败 / 已登录提示、注册与登录自动补发、
确认兜底超时、公屏消息类型映射
确认兜底超时、公屏消息类型映射、**原点自救(判定成败 / 换方向重试 / 提前结束 / 通知提醒)**
- 开拓日志:字段记录、筛选、统计、JSONL 落盘与重启恢复、损坏行容错、环形缓冲、CSV 导出
- 公屏日志:按机器人隔离、类型 / 关键字过滤、分文件落盘、文件名安全化、文本导出
- 大地图:坐标系换算(含对源站实测标定的王城用例)、**以机器人为中心的图幅换算**、瓦片范围、缓存命中、批量爬取、缓存清理
- 定时消息:内容归一化与间隔钳制、到点发送与按周期重复、未就绪时顺延不补发、多任务独立计时、整体替换重置计时
- 大地图:坐标系换算(含对源站实测标定的王城用例)、**以机器人为中心的图幅换算**、瓦片范围、缓存命中、批量爬取、缓存清理、**后台预热的节流与层级**
- 定时消息:内容归一化与间隔钳制、到点发送与按周期重复、未就绪时顺延不补发、多任务独立计时、整体替换重置计时、
**cron 解析与下次时刻计算(步长 / 区间 / 星期 / 稀缺表达式 / 非法回退)**、**发送结果确认(命中 / 超时 / 未配置)**
- 通知提醒:启用判定、飞书文本格式、去重节流、环境变量兜底、失败不抛异常、不泄露完整地址
- Web 控制台:地图负载、单 / 多机器人接口、聊天与开拓日志接口、定时任务接口、
图幅与中心计算、瓦片代理、令牌与登录鉴权、WebSocket 握手鉴权
@@ -683,13 +743,16 @@ src/
│ ├── subtitleChatMonitor.js 字幕 / 聊天解析与模糊匹配
│ ├── expansionLog.js ★ 开拓日志(玩家 / 邦国 / 区块 / 时间 / 来源)
│ ├── chatLog.js ★ 游戏公屏完整日志(按机器人分文件)
│ └── bigMapService.js ★ 大地图瓦片爬取与缓存
│ ├── bigMapService.js ★ 大地图瓦片爬取与缓存 / 后台预热
│ ├── targetLease.js ★ 目标区块租约表(多机器人避免撞车)
│ └── notifier.js ★ 人工介入提醒(飞书等 Webhook)
├── bot/
│ ├── botPool.js ★ 机器人池:同时挂多个玩家
│ ├── botManager.js 连接、登录验证、公屏留档、指令 / 聊天发送
│ ├── scheduledMessenger.js ★ 定时重复发送消息 / 指令
│ ├── botManager.js 连接、登录验证、公屏留档、指令 / 聊天发送、地面状态修正
│ ├── scheduledMessenger.js ★ 定时发送:固定间隔 / cron + 发送结果确认
│ ├── grimCompat.js ★ Grim 反作弊合规层 + 协议补包(player_input / tick_end)
│ ├── pathingService.js 寻路服务(替代 Baritone)
│ ├── manualMover.js ★ 手动执行器(只借用 A* 规划,自己按键执行)
│ ├── autoCombat.js 战斗筛选与耐久检查
│ ├── autoEat.js 自动进食
│ ├── autoBoat.js 自动用船(默认关闭)