Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions app/models/config/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -2499,31 +2499,48 @@ class EmulatorFields(ConfigGroup):

class GameFields(ConfigGroup):
Arguments: CliArgumentString = Field(
default_factory=lambda: "", description="游戏启动参数"
default_factory=lambda: "",
description="游戏启动参数,仅 DirectExe 模式使用;URL 模式的参数写进链接",
)

CloseMode: Literal["Script", "OnFailure", "Always"] = Field(
default_factory=lambda: "OnFailure",
description="AttachOnly 模式下结束后谁关游戏:Script 交给脚本(MAS 不关);OnFailure 本次运行没正常完成(失败、超时、停止、异常)时由 MAS 关;Always 结束后一律由 MAS 关。MAS 只关本次运行开始之后才出现的进程,重试之间不关。DirectExe 与 URL 模式忽略它",
)

Hotkeys: JsonDictString = Field(
default_factory=lambda: "{}",
description='托管级键位(PI v2.8 hotkey 选项):``{option 名: {字段名: 组合键字符串}}``, 组合键写成「修饰键+…+主键」(如 ``"Ctrl+E"``)。只存与 interface 默认不同的 字段;运行时叠加到每个任务实际生效的 hotkey 选项上,盖过账号快照 / 预设里的值。 仅 Win32 控制器生效(Adb 等其他控制器不叠加)。interface 里已没有的 option / 字段静默忽略,映射不了的值告警后回落到快照 / 默认值(runner/run_plan.py)。',
)

LaunchMode: Literal["DirectExe", "AttachOnly"] = Field(
default_factory=lambda: "DirectExe", description="游戏生命周期模式"
LaunchMode: Literal["DirectExe", "URL", "AttachOnly"] = Field(
default_factory=lambda: "DirectExe",
description="游戏生命周期模式:DirectExe 由 MAS 按 exe 启动、结束后关闭;URL 由 MAS 打开链接拉起游戏、结束后关闭;AttachOnly 由脚本或用户自己启动,MAS 只接管窗口,结束后是否由 MAS 关闭见 CloseMode",
)

LaunchPath: FilePath = Field(
default_factory=lambda: None,
description="DirectExe 模式下 MAS 启动的游戏 exe",
description="游戏 exe:DirectExe 模式下 MAS 启动它;URL 模式下必填,用来认出游戏(是否已在运行、等窗口、关闭、Unity 分辨率);AttachOnly 模式下可选,用来认出要关闭的游戏",
)

LauncherPath: FilePath = Field(
default_factory=lambda: None,
description="AttachOnly 模式下可选的启动器 exe,MAS 兜底关闭游戏时一并关闭本次运行期间打开的启动器",
)

PackageName: str = Field(
default_factory=lambda: "",
description="安卓游戏包名,Adb controller 用:启动模拟器时顺带把游戏拉起来。 留空表示从项目的 pipeline 里自动识别(见 embedded/game_package.py); 自动识别是启发式的,填了这里就以这里为准。识别不出且没填则不启动游戏。",
)

URL: str = Field(
default_factory=lambda: "",
description="URL 模式下 MAS 打开的链接,如 steam://rungameid/<appid>",
)

UnityResolution: Literal["Off", "1920x1080", "1280x720"] = Field(
default_factory=lambda: "Off",
description="DirectExe 模式下,启动游戏前按 exe 路径反查 Unity 注册表,临时把分辨率改成所选 尺寸的窗口模式,游戏关闭后恢复原值(tools/embedded/game_resolution.py)。 只对 Unity 引擎的游戏有效;游戏已在运行时不改。Off 表示不碰。",
description="DirectExe 与 URL 模式下,启动游戏前按 exe 路径反查 Unity 注册表,临时把分辨率改成所选 尺寸的窗口模式,游戏关闭后恢复原值(tools/embedded/game_resolution.py)。 只对 Unity 引擎的游戏有效;游戏已在运行时不改。Off 表示不碰。",
)

WaitTime: Annotated[int, Field(ge=0, le=9999), none_as(0)] = Field(
Expand Down
28 changes: 23 additions & 5 deletions app/task/MaaFW/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -497,18 +497,36 @@ MFW 不使用原生编辑器会话或 base 三态选择运行配置;已有配
异常用异常原文,同样过 `human_reason` 过滤)与失败类别的固定说法;`errorMessage`(节点链、框架失败
事件、原生栈)不进推送,仍照旧写进任务日志、history 状态与调度台完成面板。记录是旁路,
**不参与重试判断**。
- Win32 下 `Game.LaunchMode` 只有两态:`DirectExe`(默认,MAS 启动、结束后一律关闭)与
`AttachOnly`(其他方式启停,MAS 只接管窗口)。关不关只看 `opened_game`,没有开关;
DirectExe 下发现游戏已在运行时也只接管、不关。`Game.UnityResolution`(Off / 1920x1080 /
1280x720)走
- Win32 下 `Game.LaunchMode` 三态:`DirectExe`(默认,MAS 按 `Game.LaunchPath` 启动、结束后关闭)、
`URL`(MAS 用 ShellExecute 打开 `Game.URL` 拉起游戏,如 `steam://rungameid/<appid>`,结束后关闭)与
`AttachOnly`(脚本或用户自己启动,MAS 只接管窗口)。
- DirectExe / URL 关不关只看 `opened_game`;发现游戏已在运行(控制器窗口在,或 exe 在跑)时只接管、
不关。重试前 `_restart_client_before_retry` 照常关、下一轮重新拉起。
- **URL 模式**:`LaunchPath` 必填但不用来启动,只用来认游戏(是否已在运行、等窗口、前置、关闭、Unity
分辨率覆盖);缺 exe 或缺链接在运行前检查就报。打开链接前记下时刻,按 exe +
`min_create_time` 找进程,时限 `max(WaitTime, 60)`(Steam 冷启动慢);链接一打开就置
`opened_game`。`Game.Arguments` 不用,参数写进链接(`steam://run/<appid>//<参数>/`)。关闭时
`kill()` 跟踪的那个之后,再把 exe 等于游戏 exe、创建时间不早于打开链接时刻(容差 2s)的进程都
结束(先 terminate,3s 后 kill):游戏不是 MAS 的子进程,有的还会自我重启换 PID。
- **AttachOnly 兜底关闭**(`Game.CloseMode`,默认 `OnFailure`;DirectExe / URL 忽略它):`Script`
交给脚本、MAS 不关;`OnFailure` 本次运行没正常完成(`run_complete` 为假:失败、超时、停止、维护
信号,以及 `on_crash`)时关;`Always` 结束后一律关。**硬规则:只关本次运行期间新出现的进程**——
账号运行开始(脚本前任务与第一次尝试的运行前设置之前)记下 `run_started_at`,收尾时候选三路(控制器窗口匹配
到的窗口所属进程、exe 等于 `Game.LaunchPath` 的、exe 等于可选 `Game.LauncherPath` 的),只结束
创建时间 ≥ `run_started_at − 2s` 的,运行前就在的各写一行「运行开始前就已打开,不关闭」;创建时间
读不到的按「运行前就在」处理。控制器没写窗口规则时窗口这一路不参与。**只在最终收尾做**
(`_close_game_after_run`:`_finalize_user` 与 `on_crash`),重试之间不关,免得和运行前设置的
重跑判定(`_pretasks_need_rerun`)打架;每次运行只做一次。运行前设置超时 / 停止时的清理不变,
照旧不碰游戏和启动器。
- `Game.UnityResolution`(Off / 1920x1080 / 1280x720,DirectExe 与 URL 下生效)走
`game_resolution.py`:按 `<exe>_Data/app.info` 反查 `HKCU\Software\<公司>\<产品>`,
只改 Unity 播放器的 `Screenmanager *` 值,不碰游戏自有的那层(星铁的
`GraphicsSettings_PCResolution`、终末地的 `video_resolution_*`),效果要实机验证。
这个通用选项**不做逐游戏适配**(2026-10-04 定,理由见 `game_resolution.py` 文件头),
游戏自有层由该游戏的特调或专项自己调。
- **项目的运行前设置(interface `pretask`)在每次尝试里跑**,不像账号级「脚本前任务」只跑一次:
MaaEnd 的「🚀启动游戏(PC)」就是它,此时 `Game.LaunchMode` 是 AttachOnly、`opened_game` 为假,
MAS 既不拉起也不关游戏。第一次尝试照常跑;它自己失败按普通失败重试;成功过之后的重试只在客户端
MAS 不拉起游戏、重试之间也不关(结束后按 `Game.CloseMode` 兜底,见上一条)。第一次尝试照常跑;它自己失败按普通失败重试;成功过之后的重试只在客户端
已经不在时重跑(`_pretasks_need_rerun`:MAS 刚关掉自己起的游戏 / 模拟器——关的时候报错也算,
`_close_*` 不论成败都清 `opened_*`、下一轮会重新拉起,见 `_reset_client_for_retry`;或 Win32 下
控制器窗口找不到——队列末尾的「关闭游戏」任务会把它关掉)。游戏还开着不重跑,免得项目的启动脚本
Expand Down
Loading
Loading