fix: 修复登录按钮死锁,移植旧版 player_input 补包解决「机器人走不动」

控制台登录按钮点不动(致命)
- bindEvents() 只在 startApp() 里执行,而 startApp() 只在登录成功后才跑,
  未登录时登录按钮根本没绑上事件,形成死锁。现在在 main() 里无条件绑定。

机器人走不动(致命,根因定位并修复)
- mineflayer 不发 player_input 包:1.21.2+ 服务端的移动预测依赖它,原版客户端每 tick 都发,
  而 mineflayer 只在骑乘/跳下/潜行时发,地面移动的 forward/jump/sprint 完全不发。
  带反作弊的服务器判定「没有任何输入却发生位移」,每 tick 把玩家拉回原位置。
  实测:修复前入站强制位置同步约 17 次/秒、按方向键 2 秒位移 0.00 格;
  修复后入站降到 0 次、位移 0.53~1.40 格。该修复来自旧版机器人(感谢原作者)。
- 同时补发 tick_end(原版每 tick 末尾发送,反作弊用它判定 tick 边界,mineflayer 完全不发)
- 资源包未自动接受会卡在加载界面进不了世界,现自动接受
- configure() 每次配置变更都重复注册寻路事件监听,导致同一事件被处理多次

长距离分段寻路
- mineflayer-pathfinder 只能规划已加载区块范围(实测约 8 区块 ≈ 130 格),
  扩地目标常在几十个区块外,A* 瞬间 noPath。现在自动拆段:走到已加载范围边缘 → 新区块加载 → 再规划。
  段长用 bot.blockAt 对未加载区块返回 null 的特性二分探测,不依赖世界内部结构。

新增
- pathing.thinkTimeoutMs / tickTimeoutMs / searchRadius(负值表示不限制剪枝)
- 位置包收发计数、player_input/tick_end 计数、游戏模式、维度、脚下方块、
  六向「脚部/头部/地面」与可通行判定等诊断字段
- 手动移动测试按钮(绕开寻路器直接按方向键并回报真实位移)

已知限制
- 机器人当前所在位置 (6759,72,11926) 无法寻路:A* 恒定只展开 47 个节点就 noPath。
  已逐项排除挖方块/放方块/跑酷/搭塔/开门/最大下落(试到 64 格)/高度偏好/水域代价/
  搜索半径(试到不限制)/时间预算/依赖版本(与能正常移动的旧机器人完全一致)。
  旧机器人日志证明寻路可用(2 次 goal_reached、2 秒走 8.7 格),但它当时在 y=-59,
  本机器人被 /spawn 放到了 y=72。下一步需把机器人挪到开阔地再试。

测试 290 -> 308,全部通过。
这个提交包含在:
lyizai
2026-09-27 23:52:29 +08:00
父节点 abecdd2648
当前提交 fcd5fb2bd9
共修改 11 个文件,包含 783 行新增和 49 行删除
+47 -2
查看文件
@@ -128,7 +128,30 @@ function defaultConfig() {
/** 进入液体格的额外代价(越大越倾向绕开水域) */
liquidCost: 40,
/** 是否允许打开门/栅栏门 */
canOpenDoors: false
canOpenDoors: false,
/**
* 单段寻路的最长方块数(默认 512)。
*
* mineflayer-pathfinder 只能规划**已加载区块**范围内(实测本服约 8 区块 ≈ 130 格),
* 而扩地目标常在几十个区块之外,A* 会瞬间返回 noPath。
* 因此长距离会被拆成若干段:走到已加载范围边缘附近再重新规划下一段。
* 这个值只是单段的上限,实际段长由「已加载范围」动态决定。
*/
maxSegmentBlocks: 96,
/** 分段寻路时距已加载范围边缘留出的安全边距(方块),避免踩在未加载边界上 */
segmentMarginBlocks: 24,
/**
* A* 单次搜索的时间预算(毫秒)。
* 太小会在复杂地形里搜不完就放弃,表现为「目标不可达」。
*/
thinkTimeoutMs: 10000,
/** A* 每 tick 的时间片(毫秒),越大单帧越卡但搜得更快 */
tickTimeoutMs: 40,
/**
* A* 的代价搜索半径:节点代价超过「起点到目标的启发值 + 该值」就被剪枝。
* 负数表示不限制(复杂地形里最稳,但搜索更慢)。
*/
searchRadius: 256
},
// ==================== 战斗辅助(新增) ====================
@@ -179,7 +202,23 @@ function defaultConfig() {
* 被反作弊判定异常后每 tick 拉回一次,表现为机器人原地抖动、完全走不动。
* 开启后发包前会用脚下方块复核,只有确实不一致时才改写。
*/
reportOnGroundFromWorld: true
reportOnGroundFromWorld: true,
/**
* 是否每 tick 补发 `player_input` 包(默认开启,**线上服务器必需**)。
*
* 1.21.2+ 服务端的移动预测依赖它,原版客户端每 tick 都发,而 mineflayer 只在
* 骑乘 / 跳下 / 潜行时发 —— 地面移动的 forward / jump / sprint 完全不发。
* 带反作弊的服务器会判定「无输入却位移」并每 tick 把玩家拉回,
* 表现就是机器人原地抖动、怎么按方向键都不动。
*/
sendPlayerInput: true,
/**
* 是否每 tick 补发 `tick_end` 包(默认开启)。
*
* 原版 1.21.2+ 客户端在每 tick 末尾发送,反作弊用它判定 tick 边界;
* mineflayer 完全不发,会导致 PacketOrder / Timer 类判定异常。
*/
sendTickEnd: true
},
// ==================== 大地图(新增) ====================
@@ -335,6 +374,12 @@ function validateConfig(config) {
config.pathing.maxDropDown = clampInt(config.pathing.maxDropDown, 0, 64, 3);
config.pathing.liquidCost = clampInt(config.pathing.liquidCost, 1, 1000, 40);
config.pathing.maxSegmentBlocks = clampInt(config.pathing.maxSegmentBlocks, 32, 4096, 512);
config.pathing.segmentMarginBlocks = clampInt(config.pathing.segmentMarginBlocks, 0, 256, 24);
config.pathing.thinkTimeoutMs = clampInt(config.pathing.thinkTimeoutMs, 100, 300000, 10000);
config.pathing.tickTimeoutMs = clampInt(config.pathing.tickTimeoutMs, 5, 5000, 40);
// searchRadius 允许为负(不限制),因此单独钳制下限
config.pathing.searchRadius = clampInt(config.pathing.searchRadius, -1, 100000, 256);
config.combat.scanRange = clampNumber(config.combat.scanRange, 1, 32, 4.5);