用 1.9.0 新增的物理 tick 录制逐 tick 抓「输入 → 输出」,把三种完全不同的现象区分开了: - 登录服大厅:物理每 tick 正常走 0.116 格,服务端每 2 tick 推回一次 → 真回弹,但那是未完成验证的惩罚,不是 bug; - 真实世界建筑内:物理加速到 0.21 格/tick(≈4.3 格/秒,正常步行速度), 入站位置包 0~1 个(服务端没插手),collidedHorizontally=true、blockedTicks 42~59/60 → 纯粹是撞墙(地形),不是协议问题; - 真实世界卡在低处(y=72,王城平台在 y≈146):inputTicks=0, 两种执行器都是 timeout → 寻路器 60 秒规划超时,从未产生可执行路径。 frozenTicks 在所有场景都是 0,说明「输入没进物理」从未出现; searchRadius 从 -1 改到 128 对结果没有影响。物理引擎、协议补包、Grim 合规层均正常。 当前扩地的真正拦路虎(按优先级): 1. 耐久保护(主手剩余耐久 15% ≤ 15%)一启动就回城停机,需人工修装备; 2. 机器人掉到低处后规划不出路径,需先带回高处; 3. /u return 会造成摔落伤害(实测 health 3.79 → 1.24,更早一次直接摔死)。
447 行
24 KiB
Markdown
447 行
24 KiB
Markdown
# TODO:留给后续开发者的待办与已知问题
|
||
|
||
> 本文档记录**尚未解决的大工程**、已经排查过的线索、以及推荐的验证方法。
|
||
> 小事已在代码里做完并提交,这里只写需要集中攻关的部分。
|
||
>
|
||
> 最后更新:2026-09-29
|
||
|
||
---
|
||
|
||
## 0. 先读这里:项目现状与部署
|
||
|
||
| 项 | 值 |
|
||
| --- | --- |
|
||
| 技术栈 | Node.js ≥ 22 + mineflayer 4.39.0 + mineflayer-pathfinder 2.4.5 |
|
||
| 测试 | `npm test`(436 个用例全绿) |
|
||
| 语法与前端 id 自检 | `npm run check` |
|
||
| 本地启动 | `npm start`(控制台 http://127.0.0.1:3080/) |
|
||
| 服务器 | `root@103.236.99.177:33851`,部署目录 `/opt/autostore-bot` |
|
||
| 控制台 | http://103.236.99.177:78/ ,密码在服务器 `.env` 的 `WEB_PASSWORD` |
|
||
| 进程管理 | pm2 应用 `autostore-bot`(端口 78);旧版 Fabric 机器人为 `minecraft-bot`(端口 91,已停) |
|
||
| 私有 Node | `/opt/autostore-bot/runtime/current/bin/node`(v24.16.0) |
|
||
|
||
**旧版 Fabric 模组源码**(重要参考,实机可用的实现):
|
||
`C:\Users\luoyi\AppData\Local\Temp\autostore-deploy\oldbot.js`(1356 行)。
|
||
它在这台服务器上是**真的能走路、能寻路、能扩地**的,所以遇到 mineflayer 行为诡异时,
|
||
第一件事应该是拿它的实现逐项对照,而不是自己猜。
|
||
|
||
> ⚠️ 上面的临时目录会被系统清理。若已丢失,它只作为历史参考 ——
|
||
> 不影响当前项目运行,但排查移动问题时很有价值,建议接手后先备份一份到仓库外。
|
||
|
||
> 📌 **本轮(1.7.0)已把本文档里的待办全部落地为代码** —— 详见 CHANGELOG 的 [1.7.0]。
|
||
> 下面每节都标注了「已实现 / 待实测」,未实测的部分需要部署到服务器后按验证方法确认。
|
||
|
||
---
|
||
|
||
## 0.1 🚀 部署方法(完整步骤)
|
||
|
||
### 方式一:一键部署(推荐)
|
||
|
||
```powershell
|
||
# 推荐:把 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
|
||
```
|
||
|
||
`scripts/deploy.js` 会依次做:打包 → 上传 → 远程执行 `update.sh` →
|
||
轮询确认控制台返回 200。任一步失败都会明确报错。
|
||
|
||
可选环境变量:`DEPLOY_APP`(默认 `/opt/autostore-bot`)、`DEPLOY_PORT`(默认 `78`)。
|
||
`SSH_*` 与 `DEPLOY_*` **只在本机生效**,服务器上的 `.env` 不需要填。
|
||
|
||
### 方式二:手动部署
|
||
|
||
```bash
|
||
# 1. 打包(务必排除 data / .env / node_modules,否则会覆盖服务器上的真实数据)
|
||
tar -czf update.tar.gz \
|
||
--exclude=node_modules --exclude=.git --exclude=data --exclude='*.log' --exclude=.env \
|
||
-C . .
|
||
|
||
# 2. 上传
|
||
scp -P 33851 update.tar.gz root@103.236.99.177:/opt/autostore-bot/update.tar.gz
|
||
|
||
# 3. 远程应用(update.sh 会保留 .env / data / node_modules / runtime)
|
||
ssh -p 33851 root@103.236.99.177 'bash /opt/autostore-bot/update.sh'
|
||
```
|
||
|
||
### 辅助脚本
|
||
|
||
| 脚本 | 用途 |
|
||
| --- | --- |
|
||
| `scripts/deploy.js` | 一键部署(推荐) |
|
||
| `scripts/ssh-helper.js` | 单条远程命令 / 上传:`node scripts/ssh-helper.js exec "<命令>"`、`... put <本地> <远程>` |
|
||
|
||
Windows 自带 OpenSSH 但**没有 sshpass / plink**,无法非交互传密码,
|
||
所以这两个脚本用 `ssh2` 直接实现协议层(`ssh2` 是 devDependencies,
|
||
未安装时脚本会提示 `npm install --save-dev ssh2` 而不是裸崩)。
|
||
|
||
**凭据一律走环境变量,脚本里不写死任何密码。**
|
||
|
||
### update.sh 做了什么
|
||
|
||
`/opt/autostore-bot/update.sh` 的行为:
|
||
1. 解压 `update.tar.gz` 到 `/opt/autostore-bot`(**保留** `.env`、`data/`、`node_modules/`、`runtime/`)
|
||
2. 用私有 Node 重新安装/校验依赖
|
||
3. `pm2 restart autostore-bot --update-env`
|
||
4. 打印进程状态与端口监听情况
|
||
|
||
### 常用运维命令
|
||
|
||
```bash
|
||
# 进程状态
|
||
pm2 status
|
||
pm2 list
|
||
|
||
# 重启 / 停止
|
||
pm2 restart autostore-bot --update-env
|
||
pm2 stop autostore-bot
|
||
|
||
# 日志(实时)
|
||
tail -f /opt/autostore-bot/data/pm2-out.log # 运行日志
|
||
tail -f /opt/autostore-bot/data/pm2-error.log # 告警与错误
|
||
pm2 logs autostore-bot
|
||
|
||
# 端口
|
||
ss -lntp | grep ':78 '
|
||
|
||
# 回滚:把上一份备份目录改回来(update.sh 会先备份旧版本目录)
|
||
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`、可选 `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 |
|
||
| `/opt/autostore-bot/data/chat/<玩家名>.jsonl` | 公屏完整日志 |
|
||
| `/opt/autostore-bot/data/expansion-log.jsonl` | 开拓日志 |
|
||
| `/opt/autostore-bot/data/mapcache/` | 大地图瓦片缓存 |
|
||
| `/opt/autostore-bot/ecosystem.config.js` | pm2 配置 |
|
||
| `/opt/autostore-bot/runtime/current` | 私有 Node(服务器自带 Node 太旧时用) |
|
||
|
||
### 新增环境变量时
|
||
|
||
1. 在 `.env` 里加真实值(**只在服务器上加,不要提交**);
|
||
2. 在 `.env.example` 里加**占位符**(如 `<your-xxx-key>`),绝不含真实密钥;
|
||
3. 确认 `.gitignore` 已包含 `.env`(本项目已含,并有 `!.env.example` 例外)。
|
||
|
||
---
|
||
|
||
## 1. 🔴 「机器人走不动」:**已实测定位到客户端物理层**(不是服务端回弹,见本节开头的实测补充)
|
||
|
||
### 🔴 2026-10-01 实测:三种「走不动」要分开看(**先读这段**)
|
||
|
||
用新增的**物理 tick 录制**(`POST/GET /api/bots/:id/physics`,见 CHANGELOG [1.9.0])
|
||
逐 tick 抓了「输入 → 输出」,把三种完全不同的现象区分开了:
|
||
|
||
| 场景 | 物理有没有动 | 服务端在干什么 | 结论 |
|
||
| --- | --- | --- | --- |
|
||
| **登录服大厅** | ✅ 每 tick 走 0.116 格(正常速度) | 每 2 tick 推回一次(`被服务器传送另有` 240~260 次/30 秒) | **真回弹**,但这是未完成验证的惩罚,不是 bug |
|
||
| **真实世界、建筑内** | ✅ 加速到 0.21 格/tick(≈4.3 格/秒,正常步行) | 入站位置包 **0~1 个**,没插手 | **撞墙**:`collidedHorizontally=true`,`blockedTicks` 42~59/60 |
|
||
| **真实世界、卡在低处** | ❌ **一次都没动** | — | **寻路器 60 秒规划超时**,`inputTicks=0`,从未产生可执行路径 |
|
||
|
||
**关键结论:物理引擎、协议补包、Grim 合规层全都正常**(能加速到正常步行速度),
|
||
`frozenTicks` 在所有场景都是 0 —— 「输入没进物理」这种情况从未出现。
|
||
`searchRadius` 从 -1 改成 128 对结果**没有影响**,所以也不是搜索空间无界导致的。
|
||
|
||
**当前扩地的真正拦路虎(按优先级):**
|
||
|
||
1. **耐久保护**:`lastReturn.reason = "装备耐久过低(主手 剩余耐久 15% ≤ 15%)"`
|
||
→ 一启动扩地就 `/u return` 回城并停机,`inputTicks=0`(压根没尝试移动)。
|
||
**需要人工修装备**。
|
||
2. **机器人掉到了低处**(y=72,王城平台在 y≈146):从该位置规划不出路径,
|
||
`pathfinder` 与 `manual` 两种执行器都是 `timeout`。需要先把它带回高处。
|
||
3. **⚠️ `/u return` 会造成摔落伤害**:实测 `health 3.79 → 1.24`,
|
||
更早一次直接 `fell from a high place` 摔死。回城前应确认落点高度。
|
||
|
||
### ⚠️ 线上实测补充(2026-09-29,**它推翻了下面原有的框架**)
|
||
|
||
用新增的**出站包抓取**(`POST/GET /api/bots/:id/capture`,见 CHANGELOG [1.8.0])做了对照,
|
||
结论是:**「走不动」不是服务端回弹造成的,是客户端物理引擎没有把按键积分成位移。**
|
||
|
||
手动按住一个方向键 3 秒的逐包实测(任务未启动、机器人空闲):
|
||
|
||
| 项 | 实测 |
|
||
| --- | --- |
|
||
| 入站位置包(服务端回弹) | **0 个** —— 服务端全程没插手 |
|
||
| 出站位置包 | 5 个,全是 1 秒一次的空闲包(**不是**每 tick 一个) |
|
||
| 客户端自己的坐标 | **完全没变**(`6752.3000, 146, 11922.3000` → 一字不差) |
|
||
| 朝向 | 完全没变(`-0.20876`) |
|
||
| `moveDiag.velocity` | 恒为 `(0, -0.078, 0)`,`speed = 0.078` |
|
||
| `player_input` 位标志 | `backward: true` ✅ —— 控制键确实按下去了 |
|
||
|
||
四方向各按 3 秒的位移:`forward 0.02 / back 0.00 / left 1.22 / right 1.40`(正常应约 12 格)。
|
||
|
||
**推论:**
|
||
|
||
1. `player_input` 如实上报了输入、`bot.getControlState()` 也是 true,
|
||
但 `prismarine-physics` 的 `applyHeading` 没有产生任何水平速度 ——
|
||
要么物理引擎拿到的 `control` 快照里 `forward/back` 是 0,
|
||
要么 `moveEntity` 把位移全吃掉了。**下一步要加 `physicsDiag` 直接看**:
|
||
`bot.physicsEnabled`、每 tick 的 `physicsTick` 计数、simulate 前后 `onGround`、
|
||
`world.getBlock(脚下)` 是否命中、以及 PlayerState 里读到的 `control` 快照。
|
||
2. **「服务端回弹」只在寻路任务运行时出现**(实测任务中入站 6/秒、出站 `position_look` 25/秒),
|
||
手动移动时入站为 **0** —— 回弹是「任务驱动移动」的伴生现象,**不是走不动的原因**。
|
||
这也解释了为什么下面两套针对回弹的修正(方案 A/B)开关对位移毫无影响
|
||
(实测:修正开 0.4~6.4 格 / 修正关 0.4~2.4 格,同量级)。
|
||
3. 原先记的「手动按键移动完全正常(forward 2 秒 4~8 格)」**已不成立**,
|
||
该基准需要重建 —— 基于它的推理(「规划问题 vs 协议问题」二分)也就全部偏了。
|
||
|
||
**顺带确认(1.21.4 协议):**
|
||
|
||
- `flags.hasHorizontalCollision` 恒为 `null`(= 0):mineflayer 在 `physics.js` 里
|
||
把它写成 `undefined`,而原版客户端会在水平碰撞时置位;实测 62/62 个位置包都是 0。
|
||
- `teleport_confirm.teleportId` 是大负数(如 `-1884468723`):该服务端用的是随机大整数,
|
||
不是原版的自增小整数,属于它自己的实现,不是 bug,但排查时别按原版预期去读。
|
||
- 包类型分布对照(5 秒):空闲 `player_input/tick_end/pong ≈ 100` 且 `position=5`;
|
||
手动按键与空闲同构(**没有** `position_look`);寻路中才有 `position_look=126`。
|
||
|
||
### 现象回顾(历史记录;其中「手动移动正常」已被上面的实测推翻)
|
||
|
||
- **手动按键移动完全正常**:通过控制台「移动测试」按 forward 2 秒,实测位移 4~8 格,位置保持。
|
||
- **寻路器驱动的移动被回弹**:一旦 `bot.pathfinder.setGoal()` 开始执行,
|
||
服务端以 **5~9 次/秒** 的频率强制同步位置(`forcedMove`),机器人几乎原地不动。
|
||
- 偶尔能挪动一点,但速度只有 **0.3 格/秒**(正常步行 4.3 格/秒)。
|
||
|
||
### 已确认正常的部分(不要再怀疑这些)
|
||
|
||
| 项 | 状态 | 证据 |
|
||
| --- | --- | --- |
|
||
| `player_input` 补包 | ✅ 每 physicsTick 都发(~20/s),bitflags 正确 | `packetStats.playerInput` 持续增长 |
|
||
| `tick_end` 补包 | ✅ 每帧发送 | `grim.protocolPackets.tickEndSent` |
|
||
| `onGround` 标志(出站包) | ✅ 已按世界数据复核 | `packetStats.onGroundFixed` |
|
||
| 世界数据可读性 | ✅ `world.getBlock` 正常,四向可见 115~125 格 | `pathingDiag.visibleDistance` |
|
||
| 寻路器本身 | ✅ 在开阔地能找到路径 | 实测「已探索 3633 节点 → success,代价 113.8」 |
|
||
| 服务端回弹是否持续 | ❌ **只在机器人尝试移动时出现** | 12 秒采样:空闲 +0,寻路中 +105 |
|
||
| 关闭疾跑 | 试过,**无改善** | `allowSprint=false` 后仍是 4.8 次/秒回弹 |
|
||
| 关闭合规层(`grim.enabled=false`) | 试过,**无改善** | A/B 实测 6.0 vs 5.5 次/秒,位移都是 0 |
|
||
| 世界数据是否错误 | ❌ **不是原因** | 实测脚下 `stone`,`blockBelow` 正确 |
|
||
|
||
### 🎯 关键线索(未变)
|
||
|
||
用 `/api/status` 的 `moveDiag` 采集到的实测数据:
|
||
|
||
- 寻路时 `forward`/`sprint` 都按下、`isMoving=true`,但**水平速度恒为 0**;
|
||
- `velocity.y = -0.078` 恰是自由落体第一 tick 的重力值 —— 说明速度每 tick 被清零;
|
||
- `bot.entity.onGround` 被服务端位置包反复置为 false(mineflayer 的 `physics.js`
|
||
在处理 `position` 包时无条件置 false),物理引擎因此一直走「空中」分支。
|
||
|
||
### ✅ 本轮已实现的代码(`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. 部署后先在控制台确认「Grim 合规」卡片里的 `地面修正` 一行出现非零计数
|
||
(`物理对齐 N · 保住 M · 最近同步差 X 格`);
|
||
2. 用「移动测试」确认手动移动仍然正常;
|
||
3. 开始扩地,观察 `moveDiag.speed` 是否 > 0.1、`moveDiag.onGround` 是否稳定为 true、
|
||
「被服务器传送另有」的计数是否下降、以及是否真的能走完一段路;
|
||
4. 若仍有回弹 → 在配置「寻路 → 寻路执行器」切到 **手动执行(备选方案)**,
|
||
再观察一次(它只借用 A* 规划,执行层完全是我们自己的按键时序);
|
||
5. 若两者都不行 → 回到下面「未完成的排查方向」,用 `packetStats.outboundTypes` +
|
||
旧模组抓包做逐项 diff(假设 5)。
|
||
|
||
### 未完成的排查方向(按优先级)
|
||
|
||
#### 假设 3:跳跃 / 跑酷的物理预测不匹配
|
||
|
||
Grim 的 Prediction 检查对**跳跃**最严格(起跳时机、跳跃高度、疾跑跳加成)。
|
||
mineflayer 的 `physics.js` 在这些细节上与原版有已知偏差。
|
||
|
||
**验证方法**:把路径限制成纯平地,禁用一切跳跃:
|
||
|
||
```json
|
||
{"allowSprint": false, "pathing": {"allowParkour": false, "allow1by1towers": false, "maxDropDown": 1}}
|
||
```
|
||
|
||
若回弹消失 → 需要在 `GrimCompat` 里拦下 `jump` 控制状态,改用更保守的移动策略。
|
||
|
||
#### 假设 5:直接抓包对照(最彻底)
|
||
|
||
用 `scripts/check-protocol.js` 的思路扩展一个抓包脚本,
|
||
分别在「旧模组移动」和「mineflayer 寻路移动」时记录 5 秒的**完整出站包序列**,diff 出差异。
|
||
`packetStats.outboundTypes` 已经给出包类型分布作为起点。
|
||
|
||
> 提示:`scripts/check-protocol.js` 里读取 `node_modules` 的路径用了
|
||
> `path.join(__dirname, 'node_modules', ...)`,在 `scripts/` 下是拿不到的 ——
|
||
> 顺手修一下(应为 `path.join(__dirname, '..', 'node_modules', ...)`)。
|
||
|
||
---
|
||
|
||
## 2. ✅ 原点自救(已实现判定与统计,实测成功率待观察)
|
||
|
||
服务器把玩家丢到世界原点附近有两种完全不同的原因,**以前在日志里长得一样**:
|
||
|
||
| 原因 | 判断依据 | 处理方式 | 现状 |
|
||
| --- | --- | --- | --- |
|
||
| 死亡后被卡住 | **服务器没有要求验证码** | 左右 / 前后移动即可触发服务器重新传送 | ✅ 两轮自救 + 成败判定 |
|
||
| 被切到登录服 | **服务器要求输入验证码** | **必须重新输入验证码**,只能人工完成 | ✅ 已识别并提示,支持 Webhook 提醒 |
|
||
|
||
> ⚠️ **不要用「脚下是不是基岩」来区分这两种情况 —— 实测两种情况下脚下都是基岩。**
|
||
> 唯一可靠的信号是**服务器有没有发「请直接输入验证码进行验证」这类提示**。
|
||
|
||
**本轮改进**:分两轮(左右 → 前后 + 跳),每轮结束后等 800ms 让传送落地再判定「位置是否恢复」;
|
||
统计 `attempts / successes / lastResult`(`/api/status` 与控制台「连接」卡片可见);
|
||
两轮都失败时升级为明确提示,并支持 Webhook 提醒。
|
||
**待观察**:线上实际成功率(看 `originRescue.successes / attempts`),
|
||
若长期为 0,说明该服务器对移动输入不再响应,应把重心放在验证码流程上。
|
||
|
||
---
|
||
|
||
## 3. ✅ 自动回城(已加「原地是否更差」评估与前后坐标)
|
||
|
||
回城是**破坏性**操作:它把机器人拉回安全点,会**清掉玩家在游戏客户端里手动摆好的位置**。
|
||
|
||
现状(1.7.0 之后):
|
||
|
||
- `returnOnTimeout` **默认关闭**;即便打开,`returnGraceSeconds`(默认 300 秒)内不执行;
|
||
- **回城前先评估**:`pathing.diagnostics().neighborCount` 为 0(确实无路可走)才回城,
|
||
否则记为「原地还能走」并跳过目标 —— 回城只会白白丢掉位置;
|
||
- **回城后会记录前后坐标与直线距离**,日志写「回城结果:机器人从 (A) 挪到了 (B)(直线 X 格)」,
|
||
控制台「任务」卡片显示 `最近回城`;位置几乎没变时明确提示回城没有帮助。
|
||
|
||
---
|
||
|
||
## 4. ✅ 其它已知问题(本轮都已落地)
|
||
|
||
### 4.1 大地图 ✅
|
||
|
||
- 层级 1~3 现在有**后台预热**:机器人移动超过 `bigMap.prewarmMinMoveBlocks`(默认 512 格)
|
||
或距上次超过 `prewarmIntervalMs`(默认 10 分钟)时,用一半并发把周边瓦片补下来;
|
||
- 领土多边形按**视野裁剪**绘制,全世界视角不再全量绘制。
|
||
|
||
### 4.2 控制台 ✅
|
||
|
||
- 日志改为**按帧批量写入**(一次布局、一次滚动),并用 `content-visibility: auto`
|
||
让浏览器跳过视口外行的渲染(等价于浏览器自带的虚拟列表,不改变内容与滚动位置);
|
||
- 配置面板新增**搜索框**:按名称 / 字段名 / 说明过滤,隐藏不删除(保存时仍能读到隐藏字段的值)。
|
||
|
||
### 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
|
||
|
||
# 公屏完整日志(含服务器消息,JSONL)
|
||
tail -f /opt/autostore-bot/data/chat/<玩家名>.jsonl
|
||
|
||
# 寻路状态(判断「探索了多少节点」是定位卡死的关键指标)
|
||
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
|
||
|
||
# 抓出站包(诊断「走不动」的主力工具,见问题 1 · 假设 5):
|
||
# 先在「能走」时抓 5 秒,再在「走不动」时抓 5 秒,然后逐包 diff
|
||
BOT=<机器人id>
|
||
curl -s -X POST -H "X-Auth-Token: $PW" -H 'Content-Type: application/json' \
|
||
-d '{"seconds":5}' http://103.236.99.177:78/api/bots/$BOT/capture/start
|
||
# 取包类型分布(先看「发了哪几种、各多少次」)
|
||
curl -s -H "X-Auth-Token: $PW" http://103.236.99.177:78/api/bots/$BOT/capture | jq '.data.byType'
|
||
# 只取移动相关的原始包内容(看 onGround / hasHorizontalCollision / player_input 位标志 / teleportId)
|
||
curl -s -H "X-Auth-Token: $PW" \
|
||
'http://103.236.99.177:78/api/bots/$BOT/capture?types=position_look,look,position,flying,player_input,teleport_confirm&limit=60' \
|
||
| jq '.data.packets'
|
||
# 手动停止(给 seconds 时可省略)
|
||
curl -s -X POST -H "X-Auth-Token: $PW" http://103.236.99.177:78/api/bots/$BOT/capture/stop
|
||
|
||
# 协议字段定义自检(核对 mineflayer 写出的字段名与 1.21.4 是否一致)
|
||
node scripts/check-protocol.js
|
||
|
||
# 录物理 tick(诊断「走不动」的另一半:抓包看「发了什么」,这里看「物理读到了什么、动没动」)
|
||
# 一边录一边按方向键,然后看三个计数:
|
||
# inputTicks>0 且 inputMovedTicks=0 且 frozenTicks>0 → 输入根本没进物理
|
||
# inputTicks>0 且 inputMovedTicks=0 且 blockedTicks>0 → 被方块挡住(地形)
|
||
# inputMovedTicks>0 但服务端把位置拉回 → 才是服务端回弹
|
||
curl -s -X POST -H "X-Auth-Token: $PW" -H 'Content-Type: application/json' \
|
||
-d '{"seconds":10}' http://103.236.99.177:78/api/bots/$BOT/physics/start
|
||
curl -s -X POST -H "X-Auth-Token: $PW" -H 'Content-Type: application/json' \
|
||
-d '{"direction":"forward","seconds":3}' http://103.236.99.177:78/api/bots/$BOT/move
|
||
curl -s -H "X-Auth-Token: $PW" http://103.236.99.177:78/api/bots/$BOT/physics \
|
||
| jq '.data | {inputTicks, inputMovedTicks, blockedTicks, frozenTicks, movedTicks, maxHorizDelta, last}'
|
||
# 逐 tick 明细(看速度、onGround、碰撞、脚下方块)
|
||
curl -s -H "X-Auth-Token: $PW" 'http://103.236.99.177:78/api/bots/$BOT/physics?limit=40' \
|
||
| jq '.data.ticks[] | {t, dx, dz, vx, vz, onGround, collidedHorizontally, blockBelow, control}'
|
||
```
|
||
|
||
### 判断「机器人是不是卡在登录服」
|
||
|
||
```bash
|
||
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"` **不能**用来判断 —— 实测两种情况脚下都是基岩
|
||
|
||
---
|
||
|
||
## 6. 代码约定(改动前请遵守)
|
||
|
||
- 提交信息用约定式提交(`feat:` / `fix:` / `docs:` / `refactor:` / `test:` / `chore:`),主题行说清做了什么。
|
||
- **所有密钥只走 `.env`**(已在 `.gitignore` 里),代码里绝不硬编码;
|
||
新增配置项要同时更新 `.env.example` 的占位符。
|
||
- 注释与文档用中文,解释「为什么」而不是「做了什么」。
|
||
- 每个 `fix` 都应补一个能复现原问题的测试;本项目测试在 `test/`,用 `node:test`。
|
||
- 改前端记得跑 `npm run check`,它会校验 `app.js` 引用的元素 id 在 `index.html` 里都存在。
|
||
- ⚠️ 写块注释时注意:**中文解释里不要出现 `*/` 这两个字符**(例如 cron 步长写法),
|
||
它会提前结束注释并导致语法错误(本项目已踩过一次)。 |