简体中文
物品匹配器
物品匹配器 (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_match | pdc | 按 PDC 键值匹配 |
lore_match | lore | 按 Lore 文本匹配 |
component | component_match | 按 Minecraft 物品组件及其内部取值匹配 |
variable_expr | expr, expression | 表达式或 PAPI 条件 |
compare_target | target | 与目标装备比较 PDC 数值 |
all_of | all, and | 子条件全部成立 |
any_of | any, or | 子条件任一成立 |
none_of | none, 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| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
component | string | 无 (必填) | 组件 ID. 可省略 minecraft: 命名空间 |
path | string | "" | 组件内部取值路径. 不写时表示取组件整体值 |
operator | string | 见下 | 比较方式 |
value | any | 无 | 期望值. 除 exists / absent 外必填 |
operator 不写时: 没写 value 取 exists, 写了 value 取 ==
component 会被规范化 -- 转小写, 空格转下划线, 没写命名空间时补 minecraft:. 因此 custom_name, Custom_Name, minecraft:custom_name 三种写法等价
operator 全集
| operator | 别名 | 需要 value | 说明 |
|---|---|---|---|
exists | present | 否 | 组件存在 |
absent | missing | 否 | 组件不存在 |
== | equals, = | 是 | 相等. 数值按数值比, 布尔按布尔比, 其余按文本比 |
!= | not_equals | 是 | 不相等 |
> | greater_than | 是 | 数值大于 |
>= | greater_or_equal | 是 | 数值大于等于 |
< | less_than | 是 | 数值小于 |
<= | less_or_equal | 是 | 数值小于等于 |
contains | 无 | 是 | 文本包含 |
starts_with | 无 | 是 | 文本前缀 |
ends_with | 无 | 是 | 文本后缀 |
regex | pattern | 是 | 正则查找 (和 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: 5matcher 与 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: 1slot_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: absentat_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| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
matchers | list | [] | 子条件列表 |
required_count | integer | 1 | 仅 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: 3item_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 的配置路径 |
|---|---|
| Strengthen | enhancement_recipes/*.yml 的 target.filter 与 materials[]; recipes/*.yml 顶层 matcher (物品源用 source_patterns); stars.*.materials[] |
| Gem | config.yml 的 socket_openers.*; items/*.yml 顶层; gems/*.yml 顶层 (构造底材另写 base_item_source) |
| Item | repair.materials[] |
| Forge | blueprint_requirements[]; materials[] |
| Station | recipes/*.yml 的 materials[]; recipes_dismantle/*.yml 顶层 |
| Cooking | ingredients[], 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[] |
| Level | sources/*.yml 的 rules[] |
| Storage | behavior.deposit_filter.matcher |
| Skills | upgrade.levels.<n>.materials[] |
有两处模块特有的边界:
- Station: 材料只要声明了
matcher, 这一项就只能从背包供料, 仓库检索会跳过它 (仓库只按物品源统计库存, 看不到真实物品的组件). 需要走仓库的材料只写item_sources. 详见 Station 配方定义 - Forge: 材料按配方内声明顺序首命中, 先声明的宽条件会遮蔽后声明的窄条件. 纯 matcher 材料建议显式写
material_id. 详见 Forge 图纸与材料
不支持 matcher 的两类位置
matcher 的唯一能力是 boolean test(MatchContext) -- 它只回答"这一枚够不够格", 不能构造物品, 也读不到方块. 因此下面两类配置位置一律只认 item_sources, 写 matcher 不会生效:
一, 产出侧 (要构造物品). 这些位置需要凭配置造出一个 ItemStack 交给玩家, 而 matcher 只能判定既有物品:
| 模块 | 不支持的路径 |
|---|---|
| Cooking | result.*.outputs[].item_sources, moisture_rules[].item_sources (产出的空容器), stations.wok.invalid_result_item_sources, failure.item_sources |
| Forge | outputs[].item_sources |
| Station | outputs[].item_sources |
| Level | upgrade.rewards.items[].item_sources |
| Gem | inlay_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 配错时不会中断加载, 只是那一条恒不命中, 所以你要主动验证
- 正反两面都测: 既测"该通过的能过", 也测"不该通过的会被拒"
- 看启动日志: 加载期被拒绝的条件都会输出警告, 包括缺
type, 未知type, 在 matcher 内写物品源条件, 未知 operator, 单位组件写值比较,matcher不是映射结构 - 逐条拆开: 组合条件不命中时, 把子条件一条条单独试, 定位到具体哪条不成立
- 先用 exists 探形状: 不确定组件在物品上是什么形态时, 先写
operator: exists确认组件在不在, 再逐层加path - 开 Debug: CoreLib 的
debug.global_all或/corelib debug all on会输出更多匹配过程信息