Skip to content

YAML 脚本语法(V1 唯一正式方案)

2026-09-19 编辑/诊断补齐:所有 _function*.yaml 使用同一画布编辑并按所属文件的 expected_version 保存。保存函数库时拒绝原生/包内同名函数;删除或改名仍被引用的函数返回 yaml.functions.referenced(列出调用资源),其他文件无法解析且无法确认引用时返回 yaml.functions.references_unknown。这不是跨文件自动重构;修复引用后再提交。删除最后一个函数可保存 functions: {}

gamer-yaml 3.1.2 的运行事件可携带 tracerun_idframe_idparent_frame_idsource{package_id,plugin_id,path,version,function?}。定义来源来自执行冻结资源;递归调用有独立帧 ID,path 为该调用中的步骤路径。Core 仅转发可选数据,YAML 插件解释语义。不含完整历史源码,当前文件版本已变时 UI 显示身份/差异而不错误高亮。此字段属于运行事件,不是 YAML 新关键字。

从界面建脚本、找图点击、参数到函数复用和定时运行,见 YAML 自动化教程(2026-09-14 核对)。

Gamer 自动化脚本只支持 YAML V1(Gamer V1 简化计划 Phase 1;无 version 字段——出现 version: 直接报 yaml.version.removed 拒绝诊断,旧 v3/v2 脚本 无兼容分支、无 fallback、无迁移工具)。

核心原则:YAML 只描述流程,所有实际操作都是函数调用。解释器只认识 函数调用 / if / repeat / return 四类步骤;tapfindsleep 等 都不是语法关键字,而是函数。

  • 权威实现:plugins/gamer-yaml/interpreter/(唯一解释器,WASM guest 与宿主测试 同源)+ plugins/gamer-yaml/host/syntax.rs(解析/校验/降线)+ native_funcs.rs(原生函数注册表);前端可视化编辑器(plugins/gamer-yaml/ui/src/script-editor/) 与 Runtime 共用同一 V1 surface DSL;
  • 旧 v3 语法文档(docs/yaml-v3/)已删除,历史实现见 git 历史。

1. 目录与函数来源

脚本、函数库、模板按 Package(数据一级作用域)存放:

data/packages/<package-id>/
├── package.toml                      # manifest(id/name/version/author/targets/plugins 依赖)
├── shared/                           # 跨插件保留区(gamer-yaml 不写)
└── plugins/
    ├── gamer-yaml/
    │   ├── automations/              # 自动化脚本 + 函数库(按文件名前缀识别,见 §4)
    │   │   ├── daily.yaml            # 自动化(普通 .yaml)
    │   │   └── _function.yaml        # Package 默认函数库(functions: 包装)
    │   └── templates/                # 模板图片(8-bit 灰度 PNG)
    └── <其他插件>/                    # dormant 数据原样保留,Core 不解释

文件识别规则(简化计划:识别只用于资源发现,不加 kind 字段;目录与 文件名不产生函数命名空间):

text
automations/ 内文件名以 _function 开头且以 .yaml 结尾 → 函数库(functions: 包装)
automations/ 内其他 .yaml                            → 自动化脚本

functions/ 专属目录已删除(保存钩子报 yaml.functions.dir.removed, 无兼容层、无自动迁移)。

函数只有两种来源

  1. 插件函数gamer-yaml 原生注册表(native_funcs.rs,Schema 唯一声明点), 随插件安装/启用变化;受插件权限约束;其中 tap / swipe / key / input_text / launch / stop_app / sleep / log / find 是 Core 能力的基础包装, wait_find / tap_template / wait_disappear 是插件便利函数(复用 find/sleep 组合,不复制视觉算法),eq..le 是纯数据函数;
  2. 当前 Package 函数automations/_function*.yaml(用户可编辑,默认只有 _function.yaml 一个文件;手动拆分的 _function_battle.yaml 等同样参与 加载),解释器本地执行。

运行前组合为唯一函数名注册表:同名冲突(原生 vs Package、跨文件重复)一律 拒绝,文件顺序不决定胜者;不跨 Package 查找;运行开始时冻结全部函数定义。 统一命名空间:调用名 = 函数名,移动/重命名函数库文件不改变调用名。 函数目标寻址 = <package-id>#<函数名>(函数测试运行、参数 schema 查询)。 通过 POST /api/runs 运行时,content_package 只填写配置包 ID(例如 com.mihoyo.hkrpg),不能包含 #函数名 或 Android 应用名;前端显式传递此字段,服务端缺省按入口第一个 /# 之前的配置包 ID 解析。 首版原生函数清单:

text
原子:tap / swipe / key / input_text / launch / stop_app / sleep / log / find
便利:wait_find / tap_template / wait_disappear
比较:eq / ne / gt / ge / lt / le

GET /api/runners/gamer-yaml/functions 返回原生函数目录(Schema 唯一前端来源)。 前端「函数」页面按函数展示,默认在 _function.yaml 新建;额外拆分的 _function*.yaml 同样可在画布中编辑,并按实际定义文件保存。

2. 脚本格式

yaml
name: 每日签到            # 可选

params:                   # 可选:运行参数 Schema(类型/必填/默认值/说明)
  retry:
    type: integer
    default: 3
    desc: 重试次数
  secret:
    type: string
    required: true

vars:                     # 可选:字面量表(不做引用解析)
  timeout: 15s

run:                      # 必有(可为空列表):执行入口
  - launch: com.example.game
  - wait_find:
      template: home.png
      timeout: $timeout
    as: home
  - if: $home
    then:
      - claim_daily: {}
    else:
      - log: 未进入主页
  - return: true

参数类型:any / boolean / integer / number / string / list / object / duration / point / template / key(别名 bool/int/float/text 解析期归一)。 默认值按类型校验(yaml.param.default.invalid)。

3. 步骤与表达式

一个步骤 = 恰好一个动作键(函数名或 if/repeat/return)+ 可选 asthen/else/do 是 if/repeat 的结构键。

yaml
run:
  - tap: [0.5, 0.8]              # 位置值简写(标量/数组 → 第一个参数)
  - find: login_button.png       # 同上
    as: button                   # as = 返回值赋给变量
  - tap: $button.center          # $name.field 引用(仅点号字段,无索引)
  - swipe:
      from: [0.5, 0.8]
      to: [0.5, 0.2]
      duration: 500ms
  - key: HOME
  - input_text: 你好
  - sleep: 1s                    # duration:带单位串或毫秒数;0 合法
  - log: 未进入主页               # 非字符串值自动转 JSON 文本
  - log:
      message: 带级别
      level: warn
  - launch: com.example.game     # 缺省包名 = 设备配置的应用(冷启动)
  - stop_app: {}
  - repeat: $retry               # 固定次数(非负整数或整数引用)
    do:
      - tap: [0.5, 0.5]
  - return: $button              # 返回值(脚本顶层返回即运行结果)
  • 所有函数支持可选字符串参数 name(也可用变量引用),写在函数参数映射中, 如 tap: {name: 点击登录, position: [0.5, 0.8]}。可视化卡片直接展示该值, 不再拼接函数名或参数。未填写时,原生函数默认使用中文名(如「点击」「等待」), 配置包函数默认使用 description,未写说明则使用函数名;显式声明的 name 参数默认值优先。 name 不改变调用目标,位置值简写仍对应原来的第一个参数。
  • 无参或全部参数可省略的函数允许 {} 或 null(如 launch: {} / launch:); sleep 的 duration 必填,不能用 sleep: 省略;
  • if 条件:只有 false/null 为假,其余值均为真(包括 0、空字符串、空数组、空对象; 比较用 eq/gt 等函数);
  • 函数调用独立局部作用域:参数显式传入,as 接收返回值;
  • find/wait_find 未命中返回 null(不是错误)。find单次模板匹配 (无 timeout/interval 参数);等待轮询用 wait_find(timeout 缺省 3s, interval 缺省 250ms,轮询直到命中或超时);tap_template 的 timeout 缺省 3s (显式传 0ms 只尝试一次)、interval 缺省 100ms,找到后点击命中中心并等待 300ms; wait_disappear 的 timeout 缺省 3s、interval 缺省 250ms,轮询直到模板消失 (消失返回 true,超时返回 false)。轮询间隔最低 50ms。

表达式只有两种:字面量、$name.field 引用。字符串以 $ 开头是引用; 字面量 $$$ 转义。无算术、无插值、无 eval、无动态索引。 步骤表达式中的数组和对象会递归解析引用,如 tap: {position: [$x, $y]}vars 与参数默认值仍是字面量,不做引用解析。

诊断码命名空间 yaml.*(解析/结构)与 param.args.*(绑定);旧 v3 源在 解析层直接报 yaml.version.removed,其余旧形态报 yaml.top.unknown—— 不接受旧语法

4. 函数库文件(当前 Package 函数)

存储位置 = automations/ 内文件名以 _function 开头的 .yaml 文件(默认库 _function.yaml;手动拆分可加 _function_battle.yaml 等,第一版只识别小写 _function 前缀 + .yaml 后缀,不接受 .yml)。文件内容继续使用 functions: 包装,不新增 kind 字段

yaml
functions:                        # 顶层必须有 functions: 包装
  claim_daily:
    description: 领取每日奖励      # 可选说明
    params:
      timeout:
        type: duration
        default: 3s
    vars:                         # 可选:函数内字面量
      tag: local
    returns:                      # 可选:仅文档/提示,不做运行时校验
      type: boolean
    run:                          # 必有
      - tap_template:
          template: daily_button.png
          timeout: $timeout
      - return: true

脚本直接调用(两种来源语法一致):

yaml
run:
  - claim_daily:
      timeout: 10s
    as: success
  • 函数名允许中文汉字(CJK 基本区与扩展 A)、小写英文字母、数字、下划线,不能以数字开头,例如 每日任务跳转领取_daily2;空格、点号、斜杠等分隔符不可用,保留字 if/repeat/return 不可用;参数名、变量名仍使用 [a-z_][a-z0-9_]*
  • 统一命名空间:文件名与目录只是存储组织,不进入调用名(_function_battle.yaml 里的 attack 调用仍写 attack);同一文件内函数名唯一,跨文件/与原生函数 同名直接报冲突(yaml.fn.conflict),文件顺序不决定胜者;
  • 一个函数库文件可定义多个函数;函数库文件不进入自动化运行列表与定时任务 选择器(函数测试运行 = POST /api/runs,entrypoint <pkg>#<函数名>);
  • 脚本文件不再内嵌局部函数库;可复用函数一律存 _function*.yaml

5. 错误处理与预算

无 try/catch/throw/on_error。业务未命中(find 未找到)返回 null;参数错误、 资源不存在、设备断开、权限不足属于执行错误,终止当前 Run 并给结构化错误。

保留宿主安全机制(计划 §1.6):取消(stop 标志 + epoch 兜底)、步预算 STEP_BUDGET_EXCEEDED(上限 100,000 逻辑步)、调用深度 CALL_DEPTH_EXCEEDED(上限 32,Package 函数本地解释递归)。

6. 模板引用与重命名

脚本/函数中 find / wait_find / tap_template / wait_disappeartemplate 实参用模板短名;模板重命名经 AST 同步改写(yaml.resource.*, 文本字面量不误改)。V1 起模板引用改写收敛为上述四个函数 + 任意调用步骤的 template 键。

Gamer · Android 游戏自动化工具