Skip to content

物品匹配器

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

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

所有支持匹配器的模块都使用同一套语法,本页是唯一的权威说明。各模块页只列出自己的落位路径和特有边界。

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

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

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

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

matcher 内部禁止写任何物品源条件。 type: item_sourceitem_sourcessourcesources 这四个 type 值会被解析器拒绝并打 warning,该 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_sourcesingredients[]

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_sources",也就是不限来源

组件 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_exprexprexpression表达式或 PAPI 条件。
compare_targettarget与目标装备比较 PDC 数值。
all_ofalland子条件全部成立。
any_ofanyor子条件任一成立。
none_ofnonenot子条件全部不成立。
at_least至少 required_count 条成立。
exactly恰好 required_count 条成立。

类型名不区分大小写。

type 必填,且不能是物品源

  • 省略 type 不再默认成物品源判定,而是加载时被拒绝并打 warning,该 matcher 恒不匹配。
  • type: item_source / item_sources / source / sources 这四个值同样被拒绝并打 warning。物品源条件写到同级的 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 缺省时:未写 valueexists,写了 value==

component 会被规范化——转小写、空格转下划线、缺命名空间时补 minecraft:。因此 custom_nameCustom_Nameminecraft: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]数组下标,从 0floats[0]
[*]通配全部元素modifiers[*].amount
"..."引号包裹含特殊字符的键"minecraft:sharpness"

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

资源 ID 键会自动补全命名空间,两个方向都通:path: sharpness 能命中实际键 minecraft:sharpnesspath: 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_groupsstats_anysource_patterns 也是独立的顶层字段,同样item_sourcesmatcher 取 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_sourcesmatcher 仍只负责判定。
Gem gems/*.yml 顶层无。构造底材是独立字段 base_item_source;旧的顶层 item_sources 仅作为构造底材回退读取。
Cooking recipes/fermentation_barrelinputs[]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_damageexists 成立,但拿 max_damage 做数值比较不会命中。想按"服主实际写进去的值"筛选,用值比较;想按"这个物品有没有这类属性"筛选,用 exists

WARNING

这条语义的运行期实际形态未在真实 Paper 服务器上验证过,详见未验证项

单位组件只支持 exists / absent

unbreakablegliderintangible_projectile 这三个组件没有值,只是一个标记。给它们写值比较会在加载时被拒绝并打 warning,该条件恒不命中。

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

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

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

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

regex 编译失败判否

regex 的模式串由服主提供。编译失败时打 warning 并判否,不影响其他条件。

类型写错会恒不命中

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

DANGER

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

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

matcher 必须是一个映射(键值结构)。写成标量会在加载时打 warning,该条件恒不命中。

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

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

组合与嵌套

all_ofany_ofnone_ofat_leastexactly 的子条件写在 matchers 列表里。子列表里同样不能出现物品源条件

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

at_leastexactly 需要额外的 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_countinteger1at_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_countrequired_count: 0 时为真

未验证项

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

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

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

NOTE

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

各模块的落位

模块支持 matcher 的配置路径
下表中每个 matcher 位置都可以在同级item_sources(Strengthen 顶层用 source_patterns),两者取 AND。
模块支持 matcher 的配置路径
Strengthenenhancement_recipes/*.ymltarget.filtermaterials[]recipes/*.yml 顶层 matcher(物品源用 source_patterns);stars.*.materials[]
Gemconfig.ymlsocket_openers.*items/*.yml 顶层;gems/*.yml 顶层(构造底材另写 base_item_source
Itemrepair.materials[]
Forgeblueprint_requirements[]materials[]
Stationrecipes/*.ymlmaterials[]recipes_dismantle/*.yml 顶层
Cookingingredients[]inputs[]inputcontainerstations.chopping_board.toolstations.wok.spatulastations.juicer.containerstations.oven.fuels[]stations.steamer.fuels[]stations.steamer.moisture_rules[].inputnutrition.food_sources[]
Levelsources/*.ymlrules[]
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_sourcesmoisture_rules[].item_sources(产出的空容器)、stations.wok.invalid_result_item_sourcesfailure.item_sources
Forgeoutputs[].item_sources
Stationoutputs[].item_sources
Levelupgrade.rewards.items[].item_sources
Geminlay_costextract_cost

Level 的奖励物品是真实发放的,走 ItemSourceService.createItemgiveOrDropPlayerLevelService.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_sourcesstations.wok.heat_levels[]lit_ / unlit_ 系列、stations.steamer.heat_item_sources

GUI 渲染侧(gui/*.ymlslots.*.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. 看启动日志:加载期被拒绝的条件都会打 warning,包括缺 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 匹配器读取的数据来源。