fix: 定位「寻路恒定 47 节点」的根因(专属配置被冻结)并改为按进度判超时
找到「寻路怎么调都只探索 47 个节点」的真凶 - 启动任务时若带了配置,会被整份存为该机器人的专属配置覆盖。它是一份**完整快照**, 等于把当时那一刻的全局配置冻结在这个机器人身上,之后改全局配置再也影响不到它。 实测这台机器人的覆盖里 allowBreak/allowParkour 都是 false,所以我前面所有 「放开挖方块 / 跑酷 / 不限搜索半径」的排查全部无效,一直在跟同一份旧配置较劲。 - 清除覆盖后寻路器的邻居生成数从 **2 变成 15**,机器人开始真正移动。 - 现在保存覆盖时会明确记一条日志(含策略/邦国/数量),说明「此后全局改动不影响它」。 - 新增「清除专属配置」按钮与 POST /api/bots/:id/settings/clear-config, 并在配置面板提示该机器人是否有专属覆盖 —— 这是「改了全局却没生效」的唯一退路。 寻路超时改为按进度判定 - 原来只看墙钟时间:目标常在几百格外,而寻路速度受地形与反作弊回弹影响可能只有 正常步行的十分之一,于是「走得慢但在稳步接近」会被误判成卡死,然后 /u return 回城把进度全部清零。现在只要与目标的曼哈顿距离有实质缩短就重置这一段计时。 - 首次观察只记录距离基准、不续期,保证「原地不动」仍会照常超时。 新增寻路诊断(排查这类问题的关键手段) - GET /api/bots/:id/status 的 pathingDiag 暴露:起点可走邻居数与具体坐标、 寻路器能否通过 world.getBlock 读到方块、四向可见距离。 - 邻居数极少(如 2)说明不是目标太远,而是移动生成器从起点就找不到分支, 此时调任何搜索参数都没用。 测试 355 -> 360,全部通过。
这个提交包含在:
@@ -415,6 +415,42 @@ test('未启动任务时 tick 不做任何事', () => {
|
||||
assert.strictEqual(controller.state, State.IDLE);
|
||||
});
|
||||
|
||||
test('寻路中只要在稳步接近目标,就不算超时(慢速推进不会被回城打断)', async () => {
|
||||
const { controller, config, bot } = setup({ config: { waitTimeoutSeconds: 5, retries: 3 } });
|
||||
await controller.start(config);
|
||||
const target = controller.target;
|
||||
assert.ok(target, '应选出目标区块');
|
||||
|
||||
// 先跑一次 tick 建立距离基准(plan() 会把基准清空,首次观察只记录不续期)
|
||||
// 把机器人放在目标西侧 10 个区块处,后面每步靠近 2 格
|
||||
const place = (offsetX) => {
|
||||
bot.entity.position.x = (target.x + offsetX) * 16 + 8;
|
||||
bot.entity.position.z = target.z * 16 + 8;
|
||||
};
|
||||
place(-10);
|
||||
controller.tick();
|
||||
|
||||
// 模拟「走得很慢但在推进」:每次 tick 靠近 2 个区块,但耗时都超过超时阈值
|
||||
for (let step = 1; step <= 4; step++) {
|
||||
place(-10 + step * 2);
|
||||
controller.stageStartedMillis = Date.now() - 10000; // 每次都「已超时」
|
||||
controller.tick();
|
||||
assert.strictEqual(controller.retryCount, 0,
|
||||
`第 ${step} 次推进不应被判定为超时(retryCount=${controller.retryCount})`);
|
||||
assert.notStrictEqual(controller.state, State.RETURNING, '不应触发回城');
|
||||
}
|
||||
assert.strictEqual(controller.consecutiveTimeoutMillis, 0, '有推进时连续超时应清零');
|
||||
});
|
||||
|
||||
test('寻路中原地不动时仍然会超时并重试(回归保护)', async () => {
|
||||
const { controller, config } = setup({ config: { waitTimeoutSeconds: 5, retries: 3 } });
|
||||
await controller.start(config);
|
||||
controller.tick(); // 首次 tick 只记录距离基准
|
||||
controller.stageStartedMillis = Date.now() - 10000;
|
||||
controller.tick();
|
||||
assert.strictEqual(controller.retryCount, 1, '原地不动必须照常判超时');
|
||||
});
|
||||
|
||||
test('规划队列为空时直接进入 COMPLETE', async () => {
|
||||
// 己方领土被完全包围且无空位:构造一个只有 1 个区块、四周全被他国占领的局面
|
||||
const { controller, config } = setup({
|
||||
|
||||
在新工单中引用
屏蔽一个用户