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` 全绿。
这个提交包含在:
@@ -12,7 +12,7 @@
|
||||
| 项 | 值 |
|
||||
| --- | --- |
|
||||
| 技术栈 | Node.js ≥ 22 + mineflayer 4.39.0 + mineflayer-pathfinder 2.4.5 |
|
||||
| 测试 | `npm test`(368 个用例全绿) |
|
||||
| 测试 | `npm test`(436 个用例全绿) |
|
||||
| 语法与前端 id 自检 | `npm run check` |
|
||||
| 本地启动 | `npm start`(控制台 http://127.0.0.1:3080/) |
|
||||
| 服务器 | `root@103.236.99.177:33851`,部署目录 `/opt/autostore-bot` |
|
||||
@@ -28,6 +28,9 @@
|
||||
> ⚠️ 上面的临时目录会被系统清理。若已丢失,它只作为历史参考 ——
|
||||
> 不影响当前项目运行,但排查移动问题时很有价值,建议接手后先备份一份到仓库外。
|
||||
|
||||
> 📌 **本轮(1.7.0)已把本文档里的待办全部落地为代码** —— 详见 CHANGELOG 的 [1.7.0]。
|
||||
> 下面每节都标注了「已实现 / 待实测」,未实测的部分需要部署到服务器后按验证方法确认。
|
||||
|
||||
---
|
||||
|
||||
## 0.1 🚀 部署方法(完整步骤)
|
||||
@@ -35,13 +38,18 @@
|
||||
### 方式一:一键部署(推荐)
|
||||
|
||||
```powershell
|
||||
# Windows PowerShell
|
||||
$env:SSH_HOST='103.236.99.177'; $env:SSH_PORT='33851'
|
||||
$env:SSH_USER='root'; $env:SSH_PASS='<服务器密码>'
|
||||
# 推荐:把 SSH_HOST / SSH_PORT / SSH_USER / SSH_PASS 写进 .env(已被 .gitignore 忽略),
|
||||
# 然后直接跑 —— 命令行传密码会留在 shell 历史与进程列表里
|
||||
npm run deploy
|
||||
```
|
||||
|
||||
```bash
|
||||
# 也可以临时用环境变量覆盖(.env 里的值仍作为兜底)
|
||||
# Windows PowerShell
|
||||
$env:SSH_HOST='103.236.99.177'; $env:SSH_PORT='33851'
|
||||
$env:SSH_USER='root'; $env:SSH_PASS='<服务器密码>'
|
||||
npm run deploy
|
||||
|
||||
# Linux / macOS
|
||||
SSH_HOST=103.236.99.177 SSH_PORT=33851 SSH_USER=root SSH_PASS='<密码>' npm run deploy
|
||||
```
|
||||
@@ -50,6 +58,7 @@ SSH_HOST=103.236.99.177 SSH_PORT=33851 SSH_USER=root SSH_PASS='<密码>' npm run
|
||||
轮询确认控制台返回 200。任一步失败都会明确报错。
|
||||
|
||||
可选环境变量:`DEPLOY_APP`(默认 `/opt/autostore-bot`)、`DEPLOY_PORT`(默认 `78`)。
|
||||
`SSH_*` 与 `DEPLOY_*` **只在本机生效**,服务器上的 `.env` 不需要填。
|
||||
|
||||
### 方式二:手动部署
|
||||
|
||||
@@ -114,7 +123,7 @@ ls -1d /opt/autostore-bot/backup-* 2>/dev/null | tail -3
|
||||
|
||||
| 路径 | 说明 |
|
||||
| --- | --- |
|
||||
| `/opt/autostore-bot/.env` | **真实凭据**(`MC_USERNAME`、`MC_PASSWORD`、`WEB_PASSWORD`、`SIMP_API_KEY`、`SIMP_API_V2_KEY`),权限 0600,**绝不进仓库** |
|
||||
| `/opt/autostore-bot/.env` | **真实凭据**(`MC_USERNAME`、`MC_PASSWORD`、`WEB_PASSWORD`、`SIMP_API_KEY`、`SIMP_API_V2_KEY`、可选 `NOTIFY_WEBHOOK_URL`),权限 0600,**绝不进仓库** |
|
||||
| `/opt/autostore-bot/data/autostore.json` | 运行时配置(控制台保存的),会被 `update.sh` 保留 |
|
||||
| `/opt/autostore-bot/data/bots.json` | 机器人非敏感设置 |
|
||||
| `/opt/autostore-bot/data/bots.credentials.json` | 界面添加的账号密码,权限 0600 |
|
||||
@@ -132,15 +141,14 @@ ls -1d /opt/autostore-bot/backup-* 2>/dev/null | tail -3
|
||||
|
||||
---
|
||||
|
||||
## 1. 🔴 最大问题:寻路器驱动的移动会被服务端持续回弹
|
||||
## 1. 🟡 「机器人走不动」:本轮已实现两套修正 + 备选执行器,**待线上实测确认**
|
||||
|
||||
### 现象
|
||||
### 现象回顾
|
||||
|
||||
- **手动按键移动完全正常**:通过控制台「移动测试」按 forward 2 秒,实测位移 4~8 格,位置保持。
|
||||
- **寻路器驱动的移动被回弹**:一旦 `bot.pathfinder.setGoal()` 开始执行,
|
||||
服务端以 **5~9 次/秒** 的频率强制同步位置(`forcedMove`),机器人几乎原地不动。
|
||||
- 偶尔能挪动一点,但速度只有 **0.3 格/秒**(正常步行 4.3 格/秒),
|
||||
所以几百格外的目标在超时前根本走不到。
|
||||
- 偶尔能挪动一点,但速度只有 **0.3 格/秒**(正常步行 4.3 格/秒)。
|
||||
|
||||
### 已确认正常的部分(不要再怀疑这些)
|
||||
|
||||
@@ -148,68 +156,45 @@ ls -1d /opt/autostore-bot/backup-* 2>/dev/null | tail -3
|
||||
| --- | --- | --- |
|
||||
| `player_input` 补包 | ✅ 每 physicsTick 都发(~20/s),bitflags 正确 | `packetStats.playerInput` 持续增长 |
|
||||
| `tick_end` 补包 | ✅ 每帧发送 | `grim.protocolPackets.tickEndSent` |
|
||||
| `onGround` 标志 | ✅ 出站包已按世界数据复核 | 实测空闲时 `onGround=true`,世界脚下确实是 stone |
|
||||
| `onGround` 标志(出站包) | ✅ 已按世界数据复核 | `packetStats.onGroundFixed` |
|
||||
| 世界数据可读性 | ✅ `world.getBlock` 正常,四向可见 115~125 格 | `pathingDiag.visibleDistance` |
|
||||
| 寻路器本身 | ✅ 在开阔地能找到路径 | 实测「已探索 3633 节点 → success,代价 113.8」 |
|
||||
| 服务端回弹是否持续 | ❌ **只在机器人尝试移动时出现**;空闲时入站位置包为 0 | 12 秒采样:空闲 +0,寻路中 +105 |
|
||||
| 服务端回弹是否持续 | ❌ **只在机器人尝试移动时出现** | 12 秒采样:空闲 +0,寻路中 +105 |
|
||||
| 关闭疾跑 | 试过,**无改善** | `allowSprint=false` 后仍是 4.8 次/秒回弹 |
|
||||
| **关闭合规层(grim.enabled=false)** | 试过,**无改善** | A/B 实测:开启 6.0 次/秒 vs 关闭 5.5 次/秒,机器人位移都是 0 格 |
|
||||
| 关闭合规层(`grim.enabled=false`) | 试过,**无改善** | A/B 实测 6.0 vs 5.5 次/秒,位移都是 0 |
|
||||
| 世界数据是否错误 | ❌ **不是原因** | 实测脚下 `stone`,`blockBelow` 正确 |
|
||||
|
||||
### 🎯 最关键的一条线索:水平速度恒为 0
|
||||
### 🎯 关键线索(未变)
|
||||
|
||||
用 `/api/status` 的 `moveDiag` 字段采集到的实测数据:
|
||||
用 `/api/status` 的 `moveDiag` 采集到的实测数据:
|
||||
|
||||
**空闲时**
|
||||
```json
|
||||
{ "controls": { "forward": false, "sprint": false, ... }, "anyPressed": false,
|
||||
"velocity": { "x": 0, "y": -0.078, "z": 0 }, "speed": 0.078, "onGround": true,
|
||||
"pathfinder": { "isMoving": false, "goal": null } }
|
||||
```
|
||||
- 寻路时 `forward`/`sprint` 都按下、`isMoving=true`,但**水平速度恒为 0**;
|
||||
- `velocity.y = -0.078` 恰是自由落体第一 tick 的重力值 —— 说明速度每 tick 被清零;
|
||||
- `bot.entity.onGround` 被服务端位置包反复置为 false(mineflayer 的 `physics.js`
|
||||
在处理 `position` 包时无条件置 false),物理引擎因此一直走「空中」分支。
|
||||
|
||||
**寻路中**
|
||||
```json
|
||||
{ "controls": { "forward": true, "sprint": true, ... }, "anyPressed": true,
|
||||
"velocity": { "x": 0, "y": -0.078, "z": 0 }, "speed": 0.078, "onGround": false,
|
||||
"pathfinder": { "isMoving": true, "goal": {...} } }
|
||||
```
|
||||
### ✅ 本轮已实现的代码(`1.7.0`)
|
||||
|
||||
**解读**(这是下一步攻关的起点):
|
||||
| 手段 | 开关 | 位置 |
|
||||
| --- | --- | --- |
|
||||
| 方案 A:物理引擎 onGround 对齐 | `grim.syncGroundToPhysics`(默认开) | `botManager._installGroundStateFix` |
|
||||
| 方案 B:例行同步不采纳 onGround=false | `grim.trustSetbackOnGround` + `grim.setbackSyncEpsilon`(默认 0.5 格) | 同上(`prependListener` 抢在 mineflayer 之前快照) |
|
||||
| 出站包类型分布(假设 4 的对照依据) | 常开 | `packetStats.outboundTypes` |
|
||||
| 备选执行器:只借 A* 算路径、自己按键执行 | `pathing.executor: 'manual'` | `src/bot/manualMover.js` |
|
||||
|
||||
1. **寻路器确实在执行** —— `forward`/`sprint` 都按下去了,`isMoving=true`。
|
||||
所以不是「寻路器没跑」,问题在更底层。
|
||||
2. **水平速度恒为 0**,而 `velocity.y = -0.078` 恰好是**自由落体第一 tick** 的重力值。
|
||||
这说明速度**每 tick 都被清零**,机器人永远积累不起速度。
|
||||
3. **`onGround` 被服务端的 position 包反复置为 false**:
|
||||
mineflayer 的 `physics.js` 在处理服务端 `position` 包时会把 `entity.onGround` 置为 false,
|
||||
而本服下发位置包的频率(5~6 次/秒)远高于物理引擎把它改回来的速度。
|
||||
结果是:**机器人站在石头上,但物理引擎一直认为自己在空中**,
|
||||
于是水平加速走的是「空中控制」分支,而位置包又把速度清零,两者叠加就是「按着前进键却纹丝不动」。
|
||||
4. 已尝试的缓解:`_isStandingOnGround()` 里加了「服务端是否正把我们按在原地」的判断
|
||||
(`_serverPinsUs()`),出站包的 `onGround` 已经能正确上报。
|
||||
**但 `bot.entity.onGround`(物理引擎内部值)仍然是 false**,所以物理计算依旧按空中处理 ——
|
||||
这可能就是还差的那一步。
|
||||
**请按下面的顺序实测**:
|
||||
|
||||
### 排查建议(按优先级,已按实测结果更新)
|
||||
1. 部署后先在控制台确认「Grim 合规」卡片里的 `地面修正` 一行出现非零计数
|
||||
(`物理对齐 N · 保住 M · 最近同步差 X 格`);
|
||||
2. 用「移动测试」确认手动移动仍然正常;
|
||||
3. 开始扩地,观察 `moveDiag.speed` 是否 > 0.1、`moveDiag.onGround` 是否稳定为 true、
|
||||
「被服务器传送另有」的计数是否下降、以及是否真的能走完一段路;
|
||||
4. 若仍有回弹 → 在配置「寻路 → 寻路执行器」切到 **手动执行(备选方案)**,
|
||||
再观察一次(它只借用 A* 规划,执行层完全是我们自己的按键时序);
|
||||
5. 若两者都不行 → 回到下面「未完成的排查方向」,用 `packetStats.outboundTypes` +
|
||||
旧模组抓包做逐项 diff(假设 5)。
|
||||
|
||||
#### 假设 1:`GrimCompat` 对 `bot.look` 的拦截 ❌ **已排除**
|
||||
|
||||
A/B 实测 grim 开/关对回弹频率没有影响(6.0 vs 5.5 次/秒),机器人位移都是 0。不要再往这个方向查。
|
||||
|
||||
#### 假设 2:`bot.entity.onGround` 被位置包置 false(**最可疑,优先测**)
|
||||
|
||||
这是目前唯一能解释「按键有、速度恒 0」的机制。方向:
|
||||
|
||||
- **方案 A**:在 `physicsTick` 里,当出站包已判定「脚下有方块」时,
|
||||
把 `bot.entity.onGround` 也强制同步为 true,让物理引擎走地面分支。
|
||||
(注意:物理引擎每 tick 会自己重算这个值,所以需要在它之后、发送位置包之前介入,
|
||||
或者直接监听并覆盖。需要读 mineflayer `lib/plugins/physics.js` 确认执行顺序。)
|
||||
- **方案 B**:降低服务端下发位置包的频率对我们的影响 ——
|
||||
例如在收到 `position` 包时,如果位置与我们自己的预测几乎一致(只差浮点误差),
|
||||
**不采纳它的 onGround=false**,只同步坐标。
|
||||
这比方案 A 更贴近「我们是对的、服务端只是话多」的实际情况。
|
||||
- **验证**:改完后看 `moveDiag.speed` 是否变成 0.1 以上、`moveDiag.onGround` 是否稳定为 true,
|
||||
以及 `被服务器传送另有` 的计数是否下降。
|
||||
### 未完成的排查方向(按优先级)
|
||||
|
||||
#### 假设 3:跳跃 / 跑酷的物理预测不匹配
|
||||
|
||||
@@ -217,115 +202,111 @@ Grim 的 Prediction 检查对**跳跃**最严格(起跳时机、跳跃高度
|
||||
mineflayer 的 `physics.js` 在这些细节上与原版有已知偏差。
|
||||
|
||||
**验证方法**:把路径限制成纯平地,禁用一切跳跃:
|
||||
|
||||
```json
|
||||
{"allowSprint": false, "pathing": {"allowParkour": false, "allow1by1towers": false, "maxDropDown": 1}}
|
||||
```
|
||||
|
||||
若回弹消失 → 需要在 `GrimCompat` 里拦下 `jump` 控制状态,改用更保守的移动策略。
|
||||
|
||||
#### 假设 4:发包顺序 / 包类型与原版不一致
|
||||
|
||||
旧模组的调试指令 `!tp` 额外发的是 **`position_look`**(同时带位置与朝向)。
|
||||
需要实测确认:mineflayer 在朝向变化时到底发的是 `position` 还是 `position_look`,
|
||||
以及服务端是否因为「朝向变了却只发 position」而判定异常。
|
||||
|
||||
**验证方法**:在 `GrimCompat` 里统计出站包类型分布,
|
||||
与旧模组移动时的包序列逐项对照(旧模组源码见上文路径)。
|
||||
|
||||
#### 假设 5:直接抓包对照
|
||||
#### 假设 5:直接抓包对照(最彻底)
|
||||
|
||||
用 `scripts/check-protocol.js` 的思路扩展一个抓包脚本,
|
||||
分别在「旧模组移动」和「mineflayer 寻路移动」时记录 5 秒的完整出站包序列,
|
||||
diff 出差异。这是最彻底也最费时的办法,但能一次性定位问题。
|
||||
分别在「旧模组移动」和「mineflayer 寻路移动」时记录 5 秒的**完整出站包序列**,diff 出差异。
|
||||
`packetStats.outboundTypes` 已经给出包类型分布作为起点。
|
||||
|
||||
### 💡 备选方案:绕开 mineflayer-pathfinder
|
||||
|
||||
既然**手动控制状态是有效的**,可以考虑不依赖 pathfinder 的执行层,自己写一个简单的转向控制器:
|
||||
|
||||
1. 用 `bot.pathfinder` **只算路径**(`bot.pathfinder.getPathTo()` 拿到路径点数组,不执行);
|
||||
2. 自己实现执行:朝下一个路径点 `bot.look` → 按住 `forward` → 到位后切下一个点;
|
||||
3. 遇到 1 格高的坎就按 `jump`,遇到障碍就重新算路径。
|
||||
|
||||
这样能完全控制发包与朝向的时序,也便于在 `GrimCompat` 里做逐 tick 审计。
|
||||
工作量中等,但比继续调 pathfinder 的参数更可能出结果。
|
||||
> 提示:`scripts/check-protocol.js` 里读取 `node_modules` 的路径用了
|
||||
> `path.join(__dirname, 'node_modules', ...)`,在 `scripts/` 下是拿不到的 ——
|
||||
> 顺手修一下(应为 `path.join(__dirname, '..', 'node_modules', ...)`)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 🟡 「被传送到原点」的两种原因(已在代码里区分,但可继续完善)
|
||||
## 2. ✅ 原点自救(已实现判定与统计,实测成功率待观察)
|
||||
|
||||
服务器把玩家丢到世界原点附近有两种完全不同的原因,**以前在日志里长得一样**,
|
||||
经常被误判成「机器人坏了」:
|
||||
服务器把玩家丢到世界原点附近有两种完全不同的原因,**以前在日志里长得一样**:
|
||||
|
||||
| 原因 | 判断依据 | 处理方式 | 现状 |
|
||||
| --- | --- | --- | --- |
|
||||
| 死亡后被卡住 | **服务器没有要求验证码** | **左右移动即可触发服务器重新传送**,可自动自救 | ✅ `BotManager._escapeFromOrigin()` |
|
||||
| 被切到登录服 | **服务器要求输入验证码** | **必须重新输入验证码**,只能人工完成 | ✅ 已识别并提示,控制台有醒目横幅 |
|
||||
| 死亡后被卡住 | **服务器没有要求验证码** | 左右 / 前后移动即可触发服务器重新传送 | ✅ 两轮自救 + 成败判定 |
|
||||
| 被切到登录服 | **服务器要求输入验证码** | **必须重新输入验证码**,只能人工完成 | ✅ 已识别并提示,支持 Webhook 提醒 |
|
||||
|
||||
> ⚠️ **不要用「脚下是不是基岩」来区分这两种情况 —— 实测两种情况下脚下都是基岩。**
|
||||
> 唯一可靠的信号是**服务器有没有发「请直接输入验证码进行验证」这类提示**。
|
||||
> 代码里以 `_needAuthWarned` 为准;没要求验证码就按「死亡卡住」处理并尝试自救
|
||||
> (自救在两种情况下都是无害的,所以判断错了也不会把事情弄得更糟)。
|
||||
|
||||
**待完善**:自救的成功率没有统计。可以记录「自救后位置是否恢复」,
|
||||
失败时再升级为提示用户;也可以尝试更多方向组合(前/后/跳)。
|
||||
**本轮改进**:分两轮(左右 → 前后 + 跳),每轮结束后等 800ms 让传送落地再判定「位置是否恢复」;
|
||||
统计 `attempts / successes / lastResult`(`/api/status` 与控制台「连接」卡片可见);
|
||||
两轮都失败时升级为明确提示,并支持 Webhook 提醒。
|
||||
**待观察**:线上实际成功率(看 `originRescue.successes / attempts`),
|
||||
若长期为 0,说明该服务器对移动输入不再响应,应把重心放在验证码流程上。
|
||||
|
||||
---
|
||||
|
||||
## 3. 🟡 自动回城(`/u return`)已加护栏,但仍可改进
|
||||
## 3. ✅ 自动回城(已加「原地是否更差」评估与前后坐标)
|
||||
|
||||
回城是**破坏性**操作:它把机器人拉回安全点,会**清掉玩家在游戏客户端里手动摆好的位置**。
|
||||
项目里曾因此反复把机器人从玩家摆好的位置拉走,排查了很久。
|
||||
|
||||
现状(已修):
|
||||
- `returnOnTimeout` **默认关闭**;
|
||||
- 即便打开,`returnGraceSeconds`(默认 300 秒)内不执行回城;
|
||||
- 回城指令可配置(`returnCommand`,默认 `u return`);
|
||||
- 每次回城都明确记日志,说明「会丢失当前位置」。
|
||||
现状(1.7.0 之后):
|
||||
|
||||
**待改进**:
|
||||
- 回城前先判断「原地是否真的更差」——例如用 `pathingDiag.neighborCount` 判断
|
||||
当前位置是否还有可走方向;如果邻居数正常,回城毫无意义。
|
||||
- 记录回城前后的坐标,在控制台上展示「回城把机器人从 A 挪到了 B」,
|
||||
让用户能判断这次回城是否值得。
|
||||
- `returnOnTimeout` **默认关闭**;即便打开,`returnGraceSeconds`(默认 300 秒)内不执行;
|
||||
- **回城前先评估**:`pathing.diagnostics().neighborCount` 为 0(确实无路可走)才回城,
|
||||
否则记为「原地还能走」并跳过目标 —— 回城只会白白丢掉位置;
|
||||
- **回城后会记录前后坐标与直线距离**,日志写「回城结果:机器人从 (A) 挪到了 (B)(直线 X 格)」,
|
||||
控制台「任务」卡片显示 `最近回城`;位置几乎没变时明确提示回城没有帮助。
|
||||
|
||||
---
|
||||
|
||||
## 4. 🟢 其它已知但优先级较低的问题
|
||||
## 4. ✅ 其它已知问题(本轮都已落地)
|
||||
|
||||
### 4.1 大地图
|
||||
- 目前只有层级 0 会批量预爬取,层级 1~3 靠用户缩放时按需拉取,首次浏览较慢。
|
||||
- 可以考虑后台低优先级预热:机器人移动到新区域时,顺带爬取周边高层级瓦片。
|
||||
- 领土多边形在缩放较大时仍是全量绘制,超大地图(全世界视角 + 全部邦国)可能掉帧,
|
||||
可以按视野做裁剪。
|
||||
### 4.1 大地图 ✅
|
||||
|
||||
### 4.2 控制台
|
||||
- 移动端布局已适配,但**日志区在手机上滚动性能一般**(每行都是 DOM 节点)。
|
||||
数据量大时建议改虚拟列表,或限制保留行数。
|
||||
- 配置面板有 8 个标签页、字段很多,可以加搜索框。
|
||||
- 层级 1~3 现在有**后台预热**:机器人移动超过 `bigMap.prewarmMinMoveBlocks`(默认 512 格)
|
||||
或距上次超过 `prewarmIntervalMs`(默认 10 分钟)时,用一半并发把周边瓦片补下来;
|
||||
- 领土多边形按**视野裁剪**绘制,全世界视角不再全量绘制。
|
||||
|
||||
### 4.3 多机器人
|
||||
- 领土模型是全局共享的,多机器人同时扩地时对同一批候选区块的协调依赖
|
||||
`_isClaimedByOthers`,但没有真正的分布式锁;高并发时可能撞车。
|
||||
- 建议给「目标区块」加一个带租约的占用表。
|
||||
### 4.2 控制台 ✅
|
||||
|
||||
### 4.4 定时消息
|
||||
- 只支持固定间隔,没有「cron 式」或「每天某时刻」的调度。
|
||||
- 没有发送结果确认:`/u restore confirm` 发出去后是否成功,目前只靠人看日志。
|
||||
- 日志改为**按帧批量写入**(一次布局、一次滚动),并用 `content-visibility: auto`
|
||||
让浏览器跳过视口外行的渲染(等价于浏览器自带的虚拟列表,不改变内容与滚动位置);
|
||||
- 配置面板新增**搜索框**:按名称 / 字段名 / 说明过滤,隐藏不删除(保存时仍能读到隐藏字段的值)。
|
||||
|
||||
### 4.5 登录验证
|
||||
- 验证码只能人工获取与输入(服务器设计如此)。
|
||||
可以在「等待验证码」时通过通知渠道(如飞书机器人)提醒用户,避免错过 120 秒时限。
|
||||
### 4.3 多机器人 ✅
|
||||
|
||||
- 新增**目标区块租约表**(`src/core/targetLease.js`):谁先抢到谁去,其余机器人跳过;
|
||||
- 持有者每个 tick 续租;停止任务 / 完成任务时归还;掉线 / 卡死后租约到期自动失效;
|
||||
- 候选区块全被其他机器人预定时,等 15 秒再规划(而不是提前判「没有可用区块」)。
|
||||
- **待观察**:多机器人同时扩地的实际撞车率(日志里出现「已被其他机器人预定,跳过」的次数)。
|
||||
|
||||
### 4.4 定时消息 ✅
|
||||
|
||||
- 支持 **cron 表达式**(5 段,本地时间,含步长 / 区间 / 列表 / 星期范围);
|
||||
非法表达式不会被静默吞掉,列表里会显示原因并按间隔发送;
|
||||
- 支持**发送结果确认**:填「期望回复」(正则)后,命中回复记「已确认」,
|
||||
超时记「未确认」并写警告 —— 不再只靠人翻日志判断 `/u restore confirm` 是否生效。
|
||||
|
||||
### 4.5 登录验证 ✅
|
||||
|
||||
- 验证码仍只能人工输入(服务器设计如此),但等待验证码与原点自救失败时会通过
|
||||
**Webhook 通知**(默认按飞书 / Lark 自定义机器人的文本格式)提醒,避免错过 120 秒时限;
|
||||
- 配置位置:控制台「服务器 → 通知提醒(Webhook)」,或环境变量 `NOTIFY_WEBHOOK_URL`。
|
||||
|
||||
---
|
||||
|
||||
## 5. 常用排查命令
|
||||
|
||||
```bash
|
||||
# 机器人完整状态(含寻路诊断)
|
||||
# 机器人完整状态(含寻路诊断 / 地面修正计数 / 自查统计)
|
||||
curl -s -H "X-Auth-Token: $PW" http://103.236.99.177:78/api/status | jq '.data.bots[0]'
|
||||
|
||||
# 寻路诊断:邻居数、世界可读性、四向可见距离
|
||||
# 寻路诊断:邻居数、世界可读性、四向可见距离、当前执行器
|
||||
curl -s -H "X-Auth-Token: $PW" http://103.236.99.177:78/api/status | jq '.data.bots[0].pathingDiag'
|
||||
|
||||
# 地面状态修正与出站包类型分布
|
||||
curl -s -H "X-Auth-Token: $PW" http://103.236.99.177:78/api/status | jq '.data.bots[0].packetStats'
|
||||
|
||||
# 原点自救成功率 / 最近回城
|
||||
curl -s -H "X-Auth-Token: $PW" http://103.236.99.177:78/api/status | jq '.data.bots[0].originRescue'
|
||||
curl -s -H "X-Auth-Token: $PW" http://103.236.99.177:78/api/status | jq '.data.bots[0].task.lastReturn'
|
||||
|
||||
# 实时日志
|
||||
tail -f /opt/autostore-bot/data/pm2-out.log
|
||||
tail -f /opt/autostore-bot/data/pm2-error.log
|
||||
@@ -338,6 +319,9 @@ grep '寻路状态' /opt/autostore-bot/data/pm2-out.log | tail -20
|
||||
|
||||
# 被服务器回弹的频率(>100/30秒 说明移动被反作弊拒绝)
|
||||
grep '被服务器传送另有' /opt/autostore-bot/data/pm2-error.log | tail -5
|
||||
|
||||
# 多机器人协调:谁在占哪块(日志)
|
||||
grep '已被其他机器人预定' /opt/autostore-bot/data/pm2-out.log | tail -10
|
||||
```
|
||||
|
||||
### 判断「机器人是不是卡在登录服」
|
||||
@@ -347,8 +331,8 @@ curl -s -H "X-Auth-Token: $PW" http://103.236.99.177:78/api/status \
|
||||
| jq '.data.bots[0] | {position, chunk, needsVerification}'
|
||||
```
|
||||
- `needsVerification == true` → 服务器正在等待验证码,控制台会显示醒目提示(**这是可靠信号**)
|
||||
- 位置靠近原点且 `needsVerification == false` → 多半是死亡后卡住,机器人会自动左右移动自救
|
||||
- ⚠️ `world.blockBelow == "bedrock"` **不能**用来判断 —— 实测死亡卡住和登录服两种情况脚下都是基岩
|
||||
- 位置靠近原点且 `needsVerification == false` → 多半是死亡后卡住,机器人会自动自救(并记录成败)
|
||||
- ⚠️ `world.blockBelow == "bedrock"` **不能**用来判断 —— 实测两种情况脚下都是基岩
|
||||
|
||||
---
|
||||
|
||||
@@ -360,3 +344,5 @@ curl -s -H "X-Auth-Token: $PW" http://103.236.99.177:78/api/status \
|
||||
- 注释与文档用中文,解释「为什么」而不是「做了什么」。
|
||||
- 每个 `fix` 都应补一个能复现原问题的测试;本项目测试在 `test/`,用 `node:test`。
|
||||
- 改前端记得跑 `npm run check`,它会校验 `app.js` 引用的元素 id 在 `index.html` 里都存在。
|
||||
- ⚠️ 写块注释时注意:**中文解释里不要出现 `*/` 这两个字符**(例如 cron 步长写法),
|
||||
它会提前结束注释并导致语法错误(本项目已踩过一次)。
|
||||
在新工单中引用
屏蔽一个用户