diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f6f729..6f34c0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ 本项目遵循 [约定式提交](https://www.conventionalcommits.org/zh-hans/) 与 [语义化版本](https://semver.org/lang/zh-CN/)。 +## [1.10.1] - 2026-10-01 + +修掉 1.9.3 自动恢复里的一个会让恢复永久丢失的缺陷。 + +### 修复 + +- **`resumeConfig` 在尝试恢复之前就被清掉了**:`tick()` 里先取走待恢复的配置再调 + `start()`,而 `start()` 在「已进入世界但还没完成登录」时会直接返回 false + (`isReady()` 要求 `loggedIn`)。本服掉线频繁、重连后常常短暂处于这个状态, + 于是**一次失败的尝试就把这次恢复永久丢掉了**,任务再也不会自己接上。 + 现在:① 等 `bm.isReady()` 才尝试;② 只有 `start()` 真的返回 true 才清掉登记, + 失败时保留并在下一 tick 继续试(日志打「自动恢复未成功(…),稍后重试」)。 + +### 测试 + +新增 1 个用例(477 → 478):未就绪时不启动也不丢登记,就绪后恢复并清登记。 + ## [1.10.0] - 2026-10-01 补上出站包一直漏报的 `hasHorizontalCollision`,针对「机器人一直在走、却只走出 diff --git a/package-lock.json b/package-lock.json index 27257db..bc0d34b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autostore-bot", - "version": "1.10.0", + "version": "1.10.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autostore-bot", - "version": "1.10.0", + "version": "1.10.1", "license": "MIT", "dependencies": { "chalk": "^4.1.2", diff --git a/package.json b/package.json index d360b98..7ff1482 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autostore-bot", - "version": "1.10.0", + "version": "1.10.1", "description": "AutoStore Bot —— Minecraft 自动扩地机器人(mineflayer 重写版,多机器人挂机,Web 控制台,开拓日志,大地图,Grim 反作弊适配)", "main": "src/index.js", "scripts": { diff --git a/src/controller/expansionController.js b/src/controller/expansionController.js index 8af7b0a..dd94d08 100644 --- a/src/controller/expansionController.js +++ b/src/controller/expansionController.js @@ -237,8 +237,6 @@ class ExpansionController { return false; } this.config = requestedConfig; - // 显式启动的任务优先于「掉线待恢复」的那一次 - this.resumeConfig = null; // 现实时间限制:不在允许时段时登记「等待自动开始」,进入时段后由 tick() 自动启动 if (!this.isWithinClaimTime()) { this.pendingStart = requestedConfig; @@ -360,12 +358,23 @@ class ExpansionController { } return; } - // 重新进入世界:把被掉线打断的任务接上 + // 重新进入世界:把被掉线打断的任务接上。 + // 必须等 isReady()(含登录完成)才试,否则 start() 会直接返回 false; + // 而且只有在真的启动成功后才清掉 resumeConfig —— 否则一次失败的尝试 + // 就会把这次恢复永久丢掉(实测本服掉线频繁,很容易踩到)。 if (this.resumeConfig && !this.isRunning()) { + if (!bm.isReady()) return; // 还没就绪,下一 tick 再试 const resume = this.resumeConfig; - this.resumeConfig = null; this._log('已重新进入世界,自动恢复扩地任务'); - this.start(resume).catch((err) => this._log('自动恢复任务失败:' + err.message, 'error')); + Promise.resolve(this.start(resume)) + .then((ok) => { + if (ok) { + this.resumeConfig = null; + } else { + this._log('自动恢复未成功(' + this.status + '),稍后重试', 'warn'); + } + }) + .catch((err) => this._log('自动恢复任务失败:' + err.message, 'error')); return; } this.playerChunk = this._playerChunk(); diff --git a/test/expansionController.test.js b/test/expansionController.test.js index 1e23955..ba7a66d 100644 --- a/test/expansionController.test.js +++ b/test/expansionController.test.js @@ -89,10 +89,12 @@ class FakeBotManager { this._model = model; /** 设为 false 模拟「世界已退出」(掉线 / 被踢 / 被切到登录服) */ this.spawned = true; + /** 设为 false 模拟「已进入世界但还没完成登录」 */ + this.ready = true; } isReady() { - return true; + return this.ready; } isSpawned() { @@ -168,6 +170,28 @@ test('世界退出后任务暂停,重连进入世界时自动恢复', async () assert.strictEqual(botManager.pathing.pathCalls, 2, '恢复后应重新下发寻路'); }); +test('恢复尝试失败时不会丢掉这次恢复(等就绪后继续试)', async () => { + // 回归:本服掉线频繁,重连后常常「已进入世界但还没完成登录」, + // 此时 start() 会直接返回 false。若在尝试前就清掉 resumeConfig, + // 这次恢复就永久丢了 —— 任务再也不会自己接上。 + const { controller, botManager, config } = setup(); + await controller.start(config); + botManager.spawned = false; + controller.tick(); // 世界退出 → 登记待恢复 + botManager.spawned = true; + botManager.ready = false; // 已进入世界但未就绪 + controller.tick(); + assert.strictEqual(controller.isRunning(), false, '未就绪时不应启动'); + assert.ok(controller.resumeConfig, '未就绪时不能丢掉待恢复的任务'); + + botManager.ready = true; + controller.tick(); + // start() 是异步的,等它落地再断言 + await new Promise((resolve) => setTimeout(resolve, 0)); + assert.strictEqual(controller.isRunning(), true, '就绪后应恢复'); + assert.strictEqual(controller.resumeConfig, null, '恢复成功后清掉登记'); +}); + test('自动恢复可以关闭', async () => { const { controller, botManager, config } = setup({ config: { autoResumeAfterReconnect: false } }); await controller.start(config);