条件加载数据

条件加载数据

开发者有时希望使用另一个 Mod 的信息创建数据驱动对象,却不想将该 Mod 显式设为依赖;也可能希望在其他 Mod 的条目存在时替换某些对象。条件子系统可以实现这些需求。

条件加载顺序示意:先检查候选 A,任一条件为假则检查候选 B;B 条件全部为真便使用 B 数据;继续无匹配则没有数据。支持 Mod 加载、物品存在、标签为空及布尔组合条件。

原创说明图,依据 Forge 1.21.x 官方文档 绘制。

实现

目前配方和进度支持条件加载。条件配方或进度会加载条件与数据项的配对列表。如果某个数据项的条件为真,则返回该数据项;否则丢弃。

带注释和省略号的示例用于说明结构;实际 JSON 文件需移除注释并补全内容。

{
  // 配方可有自定义序列化器,因此需要指定类型;进度不需要
  "type": "forge:conditional",
  "recipes": [ // 进度使用 'advancements'
    {
      // 要检查的条件;列表中的条件以 AND 组合
      "conditions": [
        { /* 条件 1 */ },
        { /* 条件 2 */ }
      ],
      "recipe": { // 进度使用 'advancement';所有条件成功时使用的配方
      }
    },
    { /* 前一项失败时检查的下一条件 */ }
  ]
}

条件数据还通过 ConditionalRecipe$Builder 和 ConditionalAdvancement$Builder 提供数据生成包装器。

条件

将 type 设置为 IConditionSerializer#getID 指定的条件名称。

True 和 False

布尔条件不含数据,返回对应的预期值,分别为 forge:true 和 forge:false。

// 某个条件:始终返回 true(forge:false 返回 false)
{ "type": "forge:true" }

Not、And 和 Or

布尔运算条件包含被操作的条件,分别为 forge:not、forge:and 和 forge:or。

// 对保存的条件结果取反
{ "type": "forge:not", "value": { /* 一个条件 */ } }
// 对保存的条件执行 AND;forge:or 则执行 OR
{
  "type": "forge:and",
  "values": [
    { /* 第一个条件 */ },
    { /* 第二个 AND 条件;forge:or 时为 OR 条件 */ }
  ]
}

Mod 已加载

当前应用中加载了指定 ID 的 Mod 时,ModLoadedCondition 返回 true,对应 forge:mod_loaded。

{
  "type": "forge:mod_loaded",
  "modid": "examplemod" // examplemod 加载时返回 true
}

物品存在

给定物品已在当前应用注册时,ItemExistsCondition 返回 true,对应 forge:item_exists。

{
  "type": "forge:item_exists",
  "item": "examplemod:example_item" // 已注册时返回 true
}

标签为空

给定物品标签没有条目时,TagEmptyCondition 返回 true,对应 forge:tag_empty。

{
  "type": "forge:tag_empty",
  "tag": "examplemod:example_tag" // 为空物品标签时返回 true
}

创建自定义条件

实现 ICondition 及其对应的 IConditionSerializer 即可创建自定义条件。

ICondition

条件只需实现两个方法:

方法 说明
getID 条件注册表名称,必须与 IConditionSerializer#getID 一致,仅用于数据生成。
test 条件满足时返回 true。

每个 #test 都可以访问表示游戏状态的 IContext;目前只能从注册表获取标签。

IConditionSerializer

序列化器需实现三个方法:

方法 说明
getID 条件注册表名称,必须与 ICondition#getID 一致。
read 从 JSON 读取条件数据。
write 将给定条件数据写入 JSON。

与 Minecraft 的其他序列化器实现类似,条件序列化器不负责读写自身类型。

随后声明静态实例保存初始化后的序列化器,并在 RecipeSerializer 的 RegisterEvent 或 FMLCommonSetupEvent 期间使用 CraftingHelper#register 注册:

// 在某个序列化器类中
public static final ExampleConditionSerializer INSTANCE = new ExampleConditionSerializer();

// 在某个处理器类中
public void registerSerializers(RegisterEvent event) {
  event.register(ForgeRegistries.Keys.RECIPE_SERIALIZERS,
    helper -> CraftingHelper.register(INSTANCE)
  );
}

重要:如果在 FMLCommonSetupEvent 注册条件序列化器,必须通过 FMLCommonSetupEvent#enqueueWork 加入同步工作队列,因为 CraftingHelper#register 不是线程安全的。

原文:Conditionally-Loaded Data。