Skip to content

feat(mfw): PC 游戏支持通过链接启动,由脚本控制时由 MAS 兜底关闭 - #1982

Merged
qiyinxi merged 3 commits into
AUTO-MAS-Project:devfrom
qiyinxi:feat/mfw-game-close-link-20261113
Nov 13, 2026
Merged

qiyinxi merged 3 commits into
AUTO-MAS-Project:devfrom
qiyinxi:feat/mfw-game-close-link-20261113

Conversation

@qiyinxi

@qiyinxi qiyinxi commented Nov 13, 2026

Copy link
Copy Markdown
Member

摘要

  • MFW 的 PC 游戏启动方式新增「通过链接启动(Steam 等)」:打开 steam://rungameid/... 这类链接,再按「游戏 exe + 启动时间」找到游戏进程。等待上限是 max(等待时间, 60) 秒。游戏已经在运行就直接接管,不再打开链接。
  • 「由脚本自身控制游戏」新增「关闭游戏」选项,可选:交给脚本 / 未正常结束时由 MAS 关闭(默认)/ 总是由 MAS 关闭。另外可以选填「启动器 exe」。MAS 只在最终收尾时关一次,重试之间不关。关的范围是控制器窗口所属进程、游戏 exe、启动器 exe 三类,且只关本次运行开始之后才创建的进程。运行前用户自己开着的进程不碰。
  • 链接启动的游戏不是 MAS 的子进程,还可能自我重启换 PID,所以收尾时按游戏 exe 再扫一遍。等待超时之后才起来的游戏也会被关。关不掉的(例如权限不够)会写进运行日志。
  • 前端:游戏相关的表单项从 ControlConfigSection.vue 拆到 MaaFWDesktopGameFields.vue,三种语言都已补齐。OpenAPI 已重新生成。

测试与验证

  • 自动化测试:.venv\Scripts\python.exe -m pytest tests 结果是 2099 passed, 1 skipped。corepack yarn typecheck 和 corepack yarn lint 退出码都是 0。
  • 测试提交:没有随 PR 提交测试。本地回归 tests/task/test_maafw_game_close_link.py 共 13 条,覆盖链接启动、接管已运行的游戏、超时后晚到、各关闭方式乘以成功/失败、重试之间不关、关不掉写日志。改坏验证:去掉收尾时的兜底扫描后,test_url_mode_game_arriving_after_timeout_is_closed_at_teardown 变红。
  • 环境与提交:1a0efc900,基于 ad3de270e。Windows 11,Python 3.12.13(uv 锁定环境)。
  • 未覆盖范围:
    • 控制器只靠窗口正则匹配时,如果运行期间新开了一个标题匹配的无关窗口,它的进程也会被关。
    • Steam 冷启动时,Steam 本身可能被算进 MAS 打开的进程树。这是 open_protocol 原有的行为。
    • 已有的「由脚本控制」配置升级后默认是「未正常结束时由 MAS 关闭」。
    • 维护信号也按失败算。
    • 启动器在运行前已常驻的(例如鹰角的 Games.exe)不会被关。
    • Steam 游戏有多个启动选项时,需要用户先设好默认选项,否则会弹出选择框。
    • Ubuntu 没有跑。
  • 作者本机真实流程:
    • MaaEnd 和终末地,由脚本控制游戏,通过 MaaEnd 的「启动游戏(PC)」拉起:
      • 游戏已开着时,收尾不关。
      • 中途停止时,停止后 1.6 秒关掉本次拉起的游戏。
      • 选「交给脚本」时,游戏保留。
    • 链接启动 steam://rungameid/431960(Wallpaper Engine):拉起后 2 秒按 exe 认出进程。控制器窗口没就绪导致失败,收尾时关掉了它和子进程,没有残留。

- Game.LaunchMode 新增 URL:MAS 打开 Game.URL(如 steam://rungameid/<appid>)拉起游戏,
  Game.LaunchPath 只用来识别;按 exe + 启动时刻找进程,时限 max(WaitTime, 60),
  关闭时兜底结束启动后出现的同 exe 进程;不使用 Game.Arguments
- 新增 Game.CloseMode(Script / OnFailure / Always,默认 OnFailure)与可选 Game.LauncherPath:
  AttachOnly 下最终收尾按控制器窗口、游戏 exe、启动器 exe 收集候选,只关本次运行开始后
  出现的进程,重试之间不关
- 编辑页 Win32 区块拆到 MaaFWDesktopGameFields,启动方式三项,补齐三种语言词条
- 重新生成受影响的 OpenAPI 模型,更新 app/task/MaaFW/AGENTS.md
@github-actions github-actions Bot added 专项: MaaFW 类型: 新增 按更新日志碎片自动设置 labels Nov 13, 2026

Copy link
Copy Markdown
Contributor

总体结论:修改后合并

宝宝,把脚本模式的关闭动作留到最终收尾,没有在重试之间乱关游戏,这点处理对啦。不过“运行前已经开着的进程不碰”还差一道边界,先补好再合喵。

[逻辑 · P2] 关闭筛选会误收运行前两秒内启动的进程

位置:runner_task.py:4609–4613。

按源码静态推演:用户先打开游戏,1 秒后启动账号任务。选择“由脚本自身控制游戏”,再遇到默认 OnFailure 的失败收尾,或选择 Always,最终收尾会从窗口 PID、游戏 exe、启动器 exe 中找匹配进程。这里用 create_time >= run_started_at - 2 把那份早已运行的进程也放进关闭列表,随后 _end_processes() 对它执行 terminate。

AttachOnly 没有启动前的“已运行则接管”保护,也没保存并排除已有进程;结束前复核创建时间只能防 PID 被复用,拦不住这个场景。原先 AttachOnly 不执行这段关闭,所以这是新增的误关风险。查找刚启动进程时允许时间容差,不等于结束进程时也能把权限往前放宽两秒呀。

请在这条关闭路径严格排除运行前创建的进程;若确实需要保留时间容差,就在运行开始时记录已有进程身份,并在关闭时排除。把“提前 1 秒打开的进程保留、本次启动的进程可按配置关闭”做成随 PR 提交的回归测试,别只留在本机。

—— 替已打开的游戏守住关闭边界的 dot酱 🐾

@qiyinxi
qiyinxi merged commit 6ebe0eb into AUTO-MAS-Project:dev Nov 13, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

专项: MaaFW 类型: 新增 按更新日志碎片自动设置

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants