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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions