Skip to content

物品匹配器 ​

物品匹配器 (matcher) 在物品来源的基础上再加条件, 判断玩家放进来的物品算不算这条配方要的材料

物品匹配器 (matcher) 是 CoreLib 提供的通用输入判定块, 它只回答一个问题: 玩家放进来的这枚物品, 算不算这条配方要的材料

先分清两件事: 物品来源系统 解决的是"这是哪个插件的哪个物品", 匹配器则在此之上追加条件: 附魔够不够高, 名字里有没有某个字, 是不是没有被摔过, 耐久还剩多少. 两者可以单独用, 也可以组合用

所有支持匹配器的模块都用同一套语法, 你在这里看到的就是唯一权威说明; 各模块页只列出自己的配置路径和特有边界

判定位由两个同级字段合成

判定位 (判断"这一枚够不够格"的位置) 由两个同级并行的字段共同决定, 两者取 AND:

  • item_sources: 只表达"允许哪些物品源". 省略 = 不限来源
  • matcher: 只表达组件, PDC, Lore, 变量等非物品源条件. 省略 = 不加额外条件

两者都省略时, 该条目永不命中

matcher 内部禁止写任何物品源条件. type: item_source, item_sources, source, sources 这四个 type 值会被解析器拒绝并输出警告, 该 matcher 恒不匹配. 物品源条件请一律写到同级的 item_sources

"只按物品源判定"就只写 item_sources, 不写 matcher; "只按组件判定"就只写 matcher, 不写 item_sources

只有输入匹配点支持 matcher ​

配置文件里长得像"物品源"的字段其实有三类, 含义完全不同. 只有第三类支持 matcher

类别作用典型字段支持 matcher
T1 构造底材用哪个原版物品构造出这个自定义物品EmakiItem 的 item.source, Gem 的 base_item_source否
T2 GUI 渲染界面上显示什么装饰物, 占位物GUI 模板里的物品定义否
T3 输入匹配判定玩家放进来的物品算不算材料/输入materials[].item_sources, ingredients[] 等是

DANGER

不要给 T1 字段写 matcher. item.source 是"拿什么物品当模具", 不是"匹配什么物品". 在那里写 matcher 会让物品无法生成

T1 和 T3 经常出现在同一个文件里, 形状还很像. 以 EmakiItem 为例: item.source 是 T1, 不可改; repair.materials[].matcher 是 T3, 可以加. 判断依据只有一条 -- 这个字段是在造物品, 还是在认物品

物品源用连字符, 组件 ID 用冒号 ​

这是配错率最高的一处形状差异, 两种写法不能互换

场景分隔符正确示例
物品源简写连字符 -minecraft-diamond_sword
组件 ID冒号 : (或省略命名空间)minecraft:enchantments / enchantments
组件内部的资源 ID冒号 : (或省略命名空间)minecraft:sharpness / sharpness

DANGER

物品源写成 minecraft:diamond_sword 会被解析器判为无效并且不报错地丢掉, 不是报错 -- 原版标识符不允许出现冒号. 若整个 item_sources 列表中没有任何有效项, 该条目会在加载期被判为永不命中 (对应配置预检错误 item source field must contain at least one valid source), 不是"不限来源": 只有完全没写 item_sources 且同级存在 matcher 时, 才是"只看 matcher, 不限来源"; item_sources 与 matcher 都缺时同样永不命中

组件 ID 反过来: 写 minecraft-enchantments 会被当成一个不存在的命名空间, 永远取不到值

yaml
# 正确
item_sources:
  - minecraft-diamond_sword         # 物品源: 连字符
matcher:
  type: component
  component: minecraft:enchantments   # 组件 ID: 冒号
  path: minecraft:sharpness           # 资源 ID: 冒号
  operator: '>='
  value: 3

匹配器类型 ​

type别名说明
pdc_matchpdc按 PDC 键值匹配
lore_matchlore按 Lore 文本匹配
componentcomponent_match按 Minecraft 物品组件及其内部取值匹配
variable_exprexpr, expression表达式或 PAPI 条件
compare_targettarget与目标装备比较 PDC 数值
all_ofall, and子条件全部成立
any_ofany, or子条件任一成立
none_ofnone, not子条件全部不成立
at_least无至少 required_count 条成立
exactly无恰好 required_count 条成立

类型名不区分大小写

type 必填, 且不能是物品源

  • 省略 type 不再默认成物品源判定, 而是加载时被拒绝并输出警告, 该 matcher 恒不匹配
  • type: item_source / item_sources / source / sources 这四个值同样被拒绝并输出警告. 物品源条件写到同级的 item_sources 字段
  • 未知 type 也被拒绝, 恒不匹配 (不再是过去的"恒为真, 放行所有物品")

这条限制对组合类型的 matchers 子列表同样生效: all_of / any_of / none_of / at_least / exactly 里也不能塞物品源条件

type: component ​

按 Minecraft 物品组件匹配

yaml
matcher:
  type: component
  component: enchantments
  path: sharpness
  operator: '>='
  value: 5
字段类型默认值说明
componentstring无 (必填)组件 ID. 可省略 minecraft: 命名空间
pathstring""组件内部取值路径. 不写时表示取组件整体值
operatorstring见下比较方式
valueany无期望值. 除 exists / absent 外必填

operator 不写时: 没写 value 取 exists, 写了 value 取 ==

component 会被规范化 -- 转小写, 空格转下划线, 没写命名空间时补 minecraft:. 因此 custom_name, Custom_Name, minecraft:custom_name 三种写法等价

operator 全集 ​

operator别名需要 value说明
existspresent否组件存在
absentmissing否组件不存在
==equals, =是相等. 数值按数值比, 布尔按布尔比, 其余按文本比
!=not_equals是不相等
>greater_than是数值大于
>=greater_or_equal是数值大于等于
<less_than是数值小于
<=less_or_equal是数值小于等于
contains无是文本包含
starts_with无是文本前缀
ends_with无是文本后缀
regexpattern是正则查找 (和 find 一样, 不要求整串匹配)
has_key无是映射结构含指定键. 比较时两侧都会去掉 minecraft: 前缀
has_value无是列表或映射的值里含指定值
size无是列表长度 / 映射条目数 / 字符串长度等于指定数值

数值比较对两侧都做数值解析, 解析不出数字就判否. >, >=, <, <= 只接受数值, 不做文本字典序比较

path 语法 ​

path 用来深入组件内部取值

写法含义示例
.分隔层级levels.sharpness
[n]数组下标, 从 0 起floats[0]
[*]通配全部元素modifiers[*].amount
"..."引号包裹含特殊字符的键"minecraft:sharpness"

[*] 展开列表的全部元素或映射的全部值, 任一元素满足即算命中

资源 ID 键会自动补全命名空间, 两个方向都通: path: sharpness 能命中实际键 minecraft:sharpness, path: minecraft:sharpness 也能命中实际键 sharpness

yaml
# 任一属性修饰符的数值大于 5
matcher:
  type: component
  component: attribute_modifiers
  path: modifiers[*].amount
  operator: '>'
  value: 5

matcher 与 item_sources 的关系 ​

判定位上这两个字段同级并行, 结果取 AND:

写了什么判定结果
只写 item_sources只看物品源
只写 matcher只看组件 / PDC / Lore / 变量等条件, 不限来源
两者都写物品源命中且 matcher 条件成立才命中
两者都不写永不命中

想表达"某种物品 且 满足某组件条件"时

两个字段各写一半, 不要往 matcher 里塞物品源:

yaml
item_sources:
  - minecraft-diamond_sword
matcher:
  type: component
  component: enchantments
  path: sharpness
  operator: '>='
  value: 1

slot_groups, stats_any, source_patterns 也是独立的顶层字段, 同样与 item_sources, matcher 取 AND (全部成立才命中)

除了判定, item_sources 在部分位置还兼着与判定无关的差事; 下表列出这些额外职责:

输出节点使用单值 item_source

Cooking, Forge, Station 的产出节点已经使用单值 item_source 作为规范字段: 它只接受一个来源值, 不能写列表, 也不能与 item_sources 同时出现. 旧的单项 item_sources 仍由对应加载器兼容读取并告警; 多项来源或带 matcher 的产出节点会被拒绝. 产出是构造物品, 不属于 matcher 的判定位

模块 / 位置item_sources 的额外职责
Forge materials[]仅在未显式指定身份时参与派生材料身份; material_id 用于选择, 计数索引和 lookup, count_key 用于数量聚合/消费, audit_id 用于成品 PDC 审计与刷新. 三者可独立指定, 详见 Forge 图纸与材料
Forge blueprint_requirements[]id 用于同一图纸多种形态的聚合与 API/反向索引; item_sources 与 matcher 仍只负责判定
Gem gems/*.yml 顶层无. 构造底材是独立字段 base_item_source; 旧的顶层 item_sources 仅作为构造底材回退读取
Cooking recipes/fermentation_barrel 的 inputs[]slot_id 是持久化槽位身份, count_key 是数量聚合与消费身份; 两者必须稳定且在同一配方内唯一
Station materials[]material_id 用于材料选择身份, requirement_id/count_key 用于分配和消费记录. 仓库只按物品源计数; 声明 matcher 的材料走背包栈分配, 详见 Station 配方定义
Skills materials[], Item repair.materials[]物品源路径本身就是扣除实现
Cooking moisture_rules[] 根部的 item_sources产出空容器的构造字段, 不是判定位. 判定写在子节点 input.{item_sources,matcher} 上
Storage deposit_filter.matcher这里是过滤器不是判定位: entries 命中或 matcher 命中都算命中
Strengthen recipes/*.yml 顶层顶层物品源条件用既有的 source_patterns (正则), 不是 item_sources. 阶段材料使用 material_id 作为规则/选择身份, count_key 作为聚合身份

几个关键点 ​

这几条是配置时最容易踩的坑, 建议你写第一个 matcher 之前先读完

缺失不等于 0 ​

组件不存在时, 任何值比较一律判否. 匹配器不会把缺失的组件当成 0, "" 或空列表

yaml
# 这条不会命中"没有附魔的剑", 它只命中"有附魔组件且锋利等级为 0"
matcher:
  type: component
  component: enchantments
  path: sharpness
  operator: '=='
  value: 0

想表达"没有这个组件", 必须显式用 absent:

yaml
matcher:
  type: component
  component: enchantments
  operator: absent

同理, != 也不会因为组件缺失而成立. 要表达"要么没有, 要么不等于某值", 用 any_of 把两种情况都列出来

exists 与值比较的取值范围不同 ​

exists / absent 判断组件在不在, 值比较读取组件的数据, 两者的取值范围不一样:

  • exists 会认原版默认组件 (例如任何工具都自带 max_damage)
  • 值比较只看物品上显式设置的组件数据

例如, 一个从未被修改过的原版钻石剑, max_damage 的 exists 成立, 但拿 max_damage 做数值比较不会命中. 想按"服主实际写进去的值"筛选, 用值比较; 想按"这个物品有没有这类属性"筛选, 用 exists

WARNING

这条规则在实际运行时的表现没有在真实 Paper 服务器上验证过, 详见未验证项

单位组件只支持 exists / absent ​

unbreakable, glider, intangible_projectile 这三个组件没有值, 只是一个标记. 给它们写值比较会在加载时被拒绝并输出警告, 该条件恒不命中

yaml
# 正确
matcher:
  type: component
  component: unbreakable
  operator: exists

# 错误: 加载时被拒绝, 恒不命中
matcher:
  type: component
  component: unbreakable
  operator: '=='
  value: true

路径求值失败判否, 不抛异常 ​

path 写错不会导致报错刷屏, 也不会中断配方加载 -- 条件只是永远不成立. 配错了没有直接反馈, 要靠日志和实测排查

regex 编译失败判否 ​

regex 的模式串由服主提供. 编译失败时输出警告并判否, 不影响其他条件

类型写错会恒不命中 ​

type 写了一个不存在的值, 或者干脆没写 type 时, 加载期输出警告, 该 matcher 恒不匹配. 这条与旧版行为相反 -- 过去是"退化成空 all_of, 恒为真, 放行所有物品", 现在是收紧成永不通过

DANGER

拼错 type 的表现是"配方怎么都做不出来", 而不是"什么都能做出来". 加了 matcher 之后请正反两面各实测一次, 并检查启动日志里有没有 matcher 相关警告

配置不是映射结构时恒不命中 ​

matcher 必须是一个映射 (键值结构). 写成标量会在加载时输出警告, 该条件恒不命中

yaml
# 错误: 标量
matcher: 'foo'

# 正确: 映射
matcher:
  type: component
  component: unbreakable
  operator: exists

组合与嵌套 ​

all_of, any_of, none_of, at_least, exactly 的子条件写在 matchers 列表里. 子列表里同样不能出现物品源条件

yaml
matcher:
  type: all_of
  matchers:
    - type: component
      component: enchantments
      path: sharpness
      operator: '>='
      value: 3
    - type: component
      component: damage
      operator: absent

at_least 和 exactly 需要额外的 required_count:

yaml
matcher:
  type: at_least
  required_count: 2
  matchers:
    - type: component
      component: enchantments
      path: sharpness
      operator: '>='
      value: 5
    - type: component
      component: custom_name
      operator: contains
      value: 炎
    - type: component
      component: rarity
      operator: '=='
      value: epic
字段类型默认值说明
matcherslist[]子条件列表
required_countinteger1仅 at_least / exactly 生效

组合类型可以任意嵌套, 嵌套项的写法与顶层完全一致:

yaml
# 钻石剑或下界合金剑, 且没有被摔过, 且锋利 >= 3
# "哪几种剑"交给同级的 item_sources, matcher 只管组件条件
item_sources:
  - minecraft-diamond_sword
  - minecraft-netherite_sword
matcher:
  type: all_of
  matchers:
    - type: none_of
      matchers:
        - type: component
          component: damage
          operator: '>'
          value: 0
    - type: component
      component: enchantments
      path: sharpness
      operator: '>='
      value: 3

item_sources 列表本身就是"任一命中", 所以过去用 any_of 包多个 item_source 的写法直接列进 item_sources 即可

空列表的行为 ​

matchers 为空时各类型的结果不一致, 配置时不要留空列表:

类型空列表结果
all_of真 (恒命中)
any_of假 (恒不命中)
none_of真 (恒命中)
at_least / exactly取决于 required_count, required_count: 0 时为真

未验证项 ​

以下 6 项属于真实 Paper 服务器上的运行期形态, 本轮开发未在实际服务器上验证, 只经过本地 JVM 的解析与求值验证. 涉及这些细节的配置请先在测试服实测再上生产

未验证项影响
组件字符串是否包含原版默认组件影响 exists 与值比较的取值范围
附魔组件的真实键形态levels 子键是否存在, 命名空间是否带前缀, 影响 path 怎么写
attribute_modifiers 的真实结构modifiers 数组的字段名
custom_name 的真实序列化形态纯文本还是 JSON 文本组件, 影响文本类 operator
嵌套物品栈的字段名bundle_contents, charged_projectiles 内部结构
NBT 类型化数组的实际出现形式[I;...] 这类写法在组件里怎么呈现

所有 path 示例 (levels.sharpness, modifiers[*].amount 等) 的语法是确定的, 但具体键名取决于上述形态, 可能需要按实测调整

NOTE

已确定的部分: SNBT 数值后缀剥离 (3.0f 按 3.0 比, 5b 按 5 比), 类型化数组解析, path 求值 (含 [*] 通配与资源 ID 双向补全), 五种组合类型与任意层级嵌套的配置解析. 这些来自本地 JVM 测试; 其中没有 Paper 或 Folia 实机通过结论. Paper/Folia 的实际组件序列化, 线程时序和旧 YAML/旧存档兼容性仍需在目标服务端单独验证

各模块用在哪里 ​

下表中每个 matcher 位置都可以在同级写 item_sources (Strengthen 顶层用 source_patterns), 两者取 AND

模块支持 matcher 的配置路径
Strengthenenhancement_recipes/*.yml 的 target.filter 与 materials[]; recipes/*.yml 顶层 matcher (物品源用 source_patterns); stars.*.materials[]
Gemconfig.yml 的 socket_openers.*; items/*.yml 顶层; gems/*.yml 顶层 (构造底材另写 base_item_source)
Itemrepair.materials[]
Forgeblueprint_requirements[]; materials[]
Stationrecipes/*.yml 的 materials[]; recipes_dismantle/*.yml 顶层
Cookingingredients[], inputs[], input, container, stations.chopping_board.tool, stations.wok.spatula, stations.juicer.container, stations.oven.fuels[], stations.steamer.fuels[], stations.steamer.moisture_rules[].input, nutrition.food_sources[]
Levelsources/*.yml 的 rules[]
Storagebehavior.deposit_filter.matcher
Skillsupgrade.levels.<n>.materials[]

有两处模块特有的边界:

  • Station: 材料只要声明了 matcher, 这一项就只能从背包供料, 仓库检索会跳过它 (仓库只按物品源统计库存, 看不到真实物品的组件). 需要走仓库的材料只写 item_sources. 详见 Station 配方定义
  • Forge: 材料按配方内声明顺序首命中, 先声明的宽条件会遮蔽后声明的窄条件. 纯 matcher 材料建议显式写 material_id. 详见 Forge 图纸与材料

不支持 matcher 的两类位置 ​

matcher 的唯一能力是 boolean test(MatchContext) -- 它只回答"这一枚够不够格", 不能构造物品, 也读不到方块. 因此下面两类配置位置一律只认 item_sources, 写 matcher 不会生效:

一, 产出侧 (要构造物品). 这些位置需要凭配置造出一个 ItemStack 交给玩家, 而 matcher 只能判定既有物品:

模块不支持的路径
Cookingresult.*.outputs[].item_sources, moisture_rules[].item_sources (产出的空容器), stations.wok.invalid_result_item_sources, failure.item_sources
Forgeoutputs[].item_sources
Stationoutputs[].item_sources
Levelupgrade.rewards.items[].item_sources
Geminlay_cost, extract_cost

Level 的奖励物品是真实发放的, 走 ItemSourceService.createItem 再 giveOrDrop (PlayerLevelService.java:513-524), 这条路径没有任何 matcher 参与的余地

Gem gems/*.yml 的构造底材现在是独立字段 base_item_source (标量), 与判定位彻底分开: base_item_source 负责造, 同级的 item_sources + matcher 负责认. 旧的顶层 item_sources 仍被解析器作为构造底材回退读取, 但新配置请写 base_item_source

二, 方块侧 (要判方块). MatchContext 只承载 ItemStack / ItemSourceRef / Player, 没有 Block 字段, 所以工位方块识别类配置无法用 matcher: Cooking 的 block_item_sources, stations.wok.heat_levels[], lit_ / unlit_ 系列, stations.steamer.heat_item_sources

GUI 渲染侧 (gui/*.yml 的 slots.*.source) 同理不支持 -- 它要按配置取出图标物品, 不是判定

配置示例 ​

只要没摔过的钻石剑 ​

yaml
item_sources:
  - minecraft-diamond_sword
matcher:
  type: component
  component: damage
  operator: absent

排除已经附了魔的装备 ​

yaml
matcher:
  type: none_of
  matchers:
    - type: component
      component: enchantments
      operator: exists

名字里带指定字样 ​

yaml
matcher:
  type: component
  component: custom_name
  operator: contains
  value: 传说

附魔种类数达标 ​

yaml
matcher:
  type: component
  component: enchantments
  path: levels
  operator: size
  value: 3

三个条件里满足任意两个 ​

yaml
matcher:
  type: at_least
  required_count: 2
  matchers:
    - type: component
      component: enchantments
      path: sharpness
      operator: '>='
      value: 5
    - type: component
      component: unbreakable
      operator: exists
    - type: component
      component: rarity
      operator: '=='
      value: epic

排查建议 ​

matcher 配错时不会中断加载, 只是那一条恒不命中, 所以你要主动验证

  1. 正反两面都测: 既测"该通过的能过", 也测"不该通过的会被拒"
  2. 看启动日志: 加载期被拒绝的条件都会输出警告, 包括缺 type, 未知 type, 在 matcher 内写物品源条件, 未知 operator, 单位组件写值比较, matcher 不是映射结构
  3. 逐条拆开: 组合条件不命中时, 把子条件一条条单独试, 定位到具体哪条不成立
  4. 先用 exists 探形状: 不确定组件在物品上是什么形态时, 先写 operator: exists 确认组件在不在, 再逐层加 path
  5. 开 Debug: CoreLib 的 debug.global_all 或 /corelib debug all on 会输出更多匹配过程信息

相关页面 ​

  • 物品来源系统: 所有 item_sources 字段的取值来源
  • 条件系统: 判断"操作能不能继续", 与 matcher 的"物品算不算材料"是两套独立机制
  • PDC 服务: pdc_match 匹配器读取的数据来源