Skip to content

停止机(Hard Stop)功能半实现,需前后端协同完善以支持船舱/战利品/目标船掉落即时停止 #431

@syokounya

Description

@syokounya

Validations

  • 我已经阅读了 用户文档 并尝试自己解决问题,同时在社群中进行了讨论
  • 我无法找到任何 open issue 提出了相同的建议

问题描述

当前停止机(Hard Stop)功能处于半实现状态。前端已有 StopCondition 框架和日志解析触发的 taskStop() 调用,但后端的单轮战斗流程内部并没有被 stop 信号真正穿透,导致停止行为只能达到**「轮次级」(即等当前这一整轮 run_normal_fight(times=1) 跑完才能停下),而非「秒级」**(类似 Ctrl+C 在下一个安全检查点立即退出)。

具体表现:

  • 当船舱接近 500、胖次接近 50,或已经捞到目标船时,如果当前轮次刚好进入 run_combat 状态机,即使点击停止或条件已触发,也必须等整场战斗(包括地图导航、出征准备、多节点战斗、结算动画)全部结束后才能停;
  • 对于只想「捞到某艘船就停」的场景,目前 StopCondition 完全缺失 target_ship_dropped 字段,没有任何自动停止手段;
  • 后端 DailyAutomationConfig 中存在 stop_max_ship / stop_max_loot 两个声明了但未被任何代码读取的死字段,属于未落地的配置残留。

另外,现有 taskStop() 的语义偏向「强制终止当前任务线程」,停止后任务虽然会标记为 STOPPED,但:

  • 前端 stopRunning() 会把任务恢复为未执行状态并塞回队列;
  • 后端 executor 只是在 for 循环开头检查 should_stop(),状态机内部(如长时间 OCR 等待、点击重试、地图导航)完全不检查 ctx.stop_event
  • 这种设计使得「立即停止」与「优雅恢复」之间存在张力:要么等很久才停,要么强行中断后前后端状态可能不一致。

解决方案

建议将停止机升级为前后端协同的安全检查点机制,核心目标:

  1. 在战斗流程内部增加可安全退出的检查点(如出征准备页、战斗节点间、结算页),实现秒级响应;
  2. 停止后不影响后端进程和后续任务队列,无需用户手动重启后端;
  3. 补齐目标船掉落停止条件,并统一前后端配置入口。

1. 扩展 StopCondition 语义

在前后端统一的停止条件定义中增加:

  • ship_count_ge: int — 舰船获取数达到即停;
  • loot_count_ge: int — 战利品达到即停(已半实现,需后端兜底);
  • target_ship_dropped: list[str] — 掉落指定舰船即停(新增);
  • (可选)stop_after_map_count: int — 完成 N 张地图后停止。

2. 后端在战斗流程中嵌入安全检查点

NormalFightRunner(及 EventFightRunner 等)在以下节点主动检查停止条件:

  • 出征前_enter_fight 读取面板数量后):若已触及上限,直接返回一个 STOP_CONDITION_MET 标志的 CombatResult,该轮不再进入战斗;
  • 战斗节点间 / 结算后:若检测到目标船掉落或数量达标,立即标记本轮结果为停止条件触发;
  • 状态机空闲期:在 combat.engine 的页面跳转、等待动画、OCR 轮询等非原子操作间隙,检查 ctx.stop_event.is_set(),安全退出当前轮次。

3. 统一停止后的行为语义

  • 当停止条件触发或被用户手动停止时,当前任务应被标记为已完成但提前终止stopped / condition_met),而不是失败;
  • TaskScheduler / task.py 的各 executor 应识别该状态,不再重试、不再塞回队列,而是正常进入 handleTaskFinished 的后续流程;
  • 后端进程保持存活,队列中其他任务(如远征、日常)仍可继续执行。

4. 清理并暴露配置入口

  • 移除或激活 DailyAutomationConfig 中的 stop_max_ship / stop_max_loot 死字段(建议映射为全局默认 StopCondition,或直接删除避免误导);
  • 在前端 Plan / Preset 编辑器中增加「停止条件」配置项,将条件随 TaskRequest 下发到后端;
  • 后端 CombatPlan.from_yaml() / from_dict() 支持读取 stop_condition 字段,runner 直接消费,不再完全依赖前端日志解析作为触发源。

通过以上改造,停止机可以从「轮次结束后才能停」升级为「在下一个安全检查点立刻停」,同时保证停止是可恢复、可继续的,不会导致后端进程异常或必须手动重启。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels
    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions