fix: 修正原点(00)两种原因的判断依据 —— 两种情况下脚下都是基岩

用户指出:死亡被卡到 00 时脚下**也是基岩**,所以原来用
lockBelow === 'bedrock' 区分「登录服」和「死亡卡住」是错的,
会把死亡卡住误判成登录服,于是不做自救、只让用户去输验证码。

改成以「服务器有没有要求输入验证码」为唯一可靠信号:
- 要求验证码 -> 判定为登录服,提示人工发验证码(不做无意义的左右移动)
- 没要求验证码 -> 判定为死亡卡住,自动左右移动触发服务器重新传送

自救本身在两种情况下都无害(在登录服里按左右键不会有后果),
所以即使判断错了也不会把事情弄得更糟。

另外:验证码提示往往比 spawn 晚到,所以 _handleAuthPrompt 里也补了一次判断 ——
若提示到达时机器人正好在原点附近,会明确点出「被反复传送刷屏」正是登录服造成的,
把这个因果关系讲清楚,避免用户只看到传送刷屏而不知原因。

同步修正 TODO.md 与 CHANGELOG 里那条错误结论,并标注「不要用基岩做判断依据」。

测试 363 -> 368,全部通过。
这个提交包含在:
lyizai
2026-09-28 20:32:49 +08:00
父节点 9ca80d2f6e
当前提交 0cd65cf9be
共修改 4 个文件,包含 129 行新增和 21 行删除
+88
查看文件
@@ -611,6 +611,94 @@ test('识别「请使用微信扫描手中的二维码进行身份验证」', ()
assert.strictEqual(manager._needAuthWarned, true);
});
// ==================== 原点(00)自救 ====================
/** 把机器人放到世界原点附近(脚下是基岩 —— 实测两种情况都是基岩) */
function placeAtOrigin(manager, bot, y) {
bot.entity.position.x = -1.5;
bot.entity.position.y = y === undefined ? -2 : y;
bot.entity.position.z = -4.5;
bot.blockAt = () => ({ name: 'bedrock', boundingBox: 'block' });
manager.bot = bot;
}
test('原点附近且服务器没要求验证码 → 按「死亡卡住」处理并尝试左右移动自救', () => {
const { manager, logger } = setup();
const bot = new FakeBot();
placeAtOrigin(manager, bot);
manager._needAuthWarned = false;
manager._handleZeroPosition();
assert.strictEqual(manager.needsVerificationHint, false, '不应误判成登录服');
assert.strictEqual(manager._escaping, true, '应开始左右移动自救');
const lines = logger.list().map((e) => e.message).join('\n');
assert.ok(lines.includes('疑似死亡后卡住'), '应说明是死亡卡住:' + lines);
assert.strictEqual(manager.needsVerificationHint, false, '不应把标记置成登录服');
assert.ok(lines.includes('左右移动'), '应说明自救手段');
});
test('原点附近且服务器要求验证码 → 判定为登录服,提示人工发验证码(不做自救)', () => {
const { manager, logger } = setup();
const bot = new FakeBot();
placeAtOrigin(manager, bot);
manager._needAuthWarned = true;
manager._handleZeroPosition();
assert.strictEqual(manager.needsVerificationHint, true);
assert.notStrictEqual(manager._escaping, true, '登录服里左右移动没有意义');
const lines = logger.list().map((e) => e.message).join('\n');
assert.ok(lines.includes('被切到了登录服'), '应明确指出是登录服:' + lines);
assert.ok(lines.includes('验证码'), '应引导用户发验证码');
});
test('脚下是基岩不能作为区分依据(两种情况都是基岩)', () => {
const { manager } = setup();
const bot = new FakeBot();
placeAtOrigin(manager, bot);
// 同一个基岩地形,仅凭「服务器要不要验证码」得出不同结论
manager._needAuthWarned = false;
manager._handleZeroPosition();
const asDeath = manager.needsVerificationHint;
const second = setup();
const bot2 = new FakeBot();
placeAtOrigin(second.manager, bot2);
second.manager._needAuthWarned = true;
second.manager._handleZeroPosition();
assert.strictEqual(asDeath, false);
assert.strictEqual(second.manager.needsVerificationHint, true);
});
test('验证码提示到达时若人在原点附近,点明「被反复传送」的因果关系', () => {
const { manager, logger } = setup();
const bot = new FakeBot();
placeAtOrigin(manager, bot);
manager._needAuthWarned = false;
manager._handleAuthPrompt('请直接输入验证码进行验证');
assert.strictEqual(manager.needsVerificationHint, true);
const lines = logger.list().map((e) => e.message).join('\n');
assert.ok(lines.includes('已被切到登录服'), '应把传送刷屏与登录服关联起来:' + lines);
});
test('不在原点附近时不触发任何原点逻辑', () => {
const { manager } = setup();
const bot = new FakeBot();
bot.entity.position.x = 6760;
bot.entity.position.y = 148;
bot.entity.position.z = 11934;
manager.bot = bot;
manager._handleZeroPosition();
assert.notStrictEqual(manager._escaping, true);
assert.strictEqual(manager.needsVerificationHint, false);
});
test('snapshot 暴露 alreadyConnected 供控制台提示用户', () => {
const { manager } = setup();
assert.strictEqual(manager.snapshot().alreadyConnected, false);