Skip to main content
Version: 1.20.x

配方生成

可以通过子类化RecipeProvider并实现#buildRecipes来为模组生成配方。一旦Consumer接受FinishedRecipe视图,就会提供一个用于生成数据的配方。FinishedRecipe既可以手动创建和提供,也可以为方便起见,使用RecipeBuilder创建。

实现后,该提供者必须被添加到DataGenerator。

// 在模组事件总线上
@SubscribeEvent
public void gatherData(GatherDataEvent event) {
event.getGenerator().addProvider(
// 告诉生成器仅在生成服务端资源时运行
event.includeServer(),
MyRecipeProvider::new
);
}

RecipeBuilder​

RecipeBuilder是一个方便的实现,用于创建要生成的FinishedRecipe。它提供了解锁、分组、保存和获取配方结果的基本定义。这分别通过#unlockedBy、#group、#save和#getResult来完成。

!!! 重要 原版配方生成器中不支持配方中的ItemStack输出。对于现有的原版配方序列化器,必须以不同的方式构建FinishedRecipe才能生成此数据。

!!! 警告 正在生成的物品结果必须指定有效的RecipeCategory;否则,将引发NullPointerException。

除[SpecialRecipeBuilder]外的所有配方构建器都需要指定一个进度标准。如果玩家以前使用过配方,则所有配方都会生成解锁配方的标准。然而,必须指定一个额外的标准,允许玩家在没有任何先验知识的情况下获得配方。如果指定的任何标准为真,则玩家将获得配方书的配方。

!!! 提示 配方标准通常使用InventoryChangeTrigger在用户物品栏中存在某些物品时解锁配方。

ShapedRecipeBuilder​

ShapedRecipeBuilder用于生成有序配方。该生成器可以通过#shaped进行初始化。保存前可以指定配方组、输入符号模式、配料的符号定义和配方解锁条件。

// 在RecipeProvider#buildRecipes(writer)中
ShapedRecipeBuilder builder = ShapedRecipeBuilder.shaped(RecipeCategory.MISC, result)
.pattern("a a") // 创建配方图案
.define('a', item) // 定义符号代表什么
.unlockedBy("criteria", criteria) // 该配方如何解锁
.save(writer); // 将数据加入生成器

附加验证检查​

有序配方在构建前进行了一些额外的验证检查:

  • 图案必须被定义且接受多于一个物品。
  • 所有图案行的宽度必须相同。
  • 一个符号不能被定义多次。
  • 空格字符(' ')被保留用于表示格中无物品,因此无法被定义。
  • 图案必须使用用户定义的全部符号。

ShapelessRecipeBuilder​

ShapelessRecipeBuilder用于生成无序配方。该生成器可以通过#shapeless进行初始化。保存前可以指定配方组、输入原料和配方解锁条件。

// 在RecipeProvider#buildRecipes(writer)中
ShapelessRecipeBuilder builder = ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, result)
.requires(item) // 将物品加入配方
.unlockedBy("criteria", criteria) // 该配方如何解锁
.save(writer); // 将数据加入生成器

SimpleCookingRecipeBuilder​

SimpleCookingRecipeBuilder用于生成熔炼、高炉熔炼、烟熏和篝火烹饪配方。此外,使用SimpleCookingSerializer的自定义烹饪配方也可以是使用该生成器生成的数据。生成器可以分别通过#smelting、#blasting、#smoking、#campfireCooking或#cooking进行初始化。保存前可以指定配方组和配方解锁条件。

// 在RecipeProvider#buildRecipes(writer)中
SimpleCookingRecipeBuilder builder = SimpleCookingRecipeBuilder.smelting(input, RecipeCategory.MISC, result, experience, cookingTime)
.unlockedBy("criteria", criteria) // 该配方如何解锁
.save(writer); // 将数据加入生成器

SingleItemRecipeBuilder​

SingleItemRecipeBuilder用于生成切石配方。此外,使用类似SingleItemRecipe$Serializer的序列化器的自定义但物品配方也可以是使用该生成器生成的数据。生成器可以分别通过#stonecutting或通过构造函数进行初始化。保存前可以指定配方组和配方解锁条件。

// 在RecipeProvider#buildRecipes(writer)中
SingleItemRecipeBuilder builder = SingleItemRecipeBuilder.stonecutting(input, RecipeCategory.MISC, result)
.unlockedBy("criteria", criteria) // 该配方如何解锁
.save(writer); // 将数据加入生成器

非RecipeBuilder生成器​

由于缺少前面提到的所有配方所使用的功能,一些配方生成器没有实现RecipeBuilder。

SmithingTransformRecipeBuilder​

SmithingTransformRecipeBuilder用于生成转换物品的锻造配方。此外,使用序列化器(如SmithingTransformRecipe$Serializer)的自定义配方也可以是使用此生成器生成的数据。生成器可以分别通过#smithing或通过构造函数进行初始化。保存前可以指定配方解锁条件。

// 在RecipeProvider#buildRecipes(writer)中
SmithingTransformRecipeBuilder builder = SmithingTransformRecipeBuilder.smithing(template, base, addition, RecipeCategory.MISC, result)
.unlocks("criteria", criteria) // 该配方如何解锁
.save(writer, name); // 将数据加入生成器

SmithingTrimRecipeBuilder​

SmithingTrimRecipeBuilder用于生成盔甲装饰的锻造配方。此外,使用类似SmithingTrimRecipe$Serializer的序列化器的自定义升级配方也可以是使用该生成器生成的数据。生成器可以分别通过#smithingTrim或通过构造函数进行初始化。保存前可以指定配方解锁条件。

// 在RecipeProvider#buildRecipes(writer)中
SmithingTrimRecipe builder = SmithingTrimRecipe.smithingTrim(template, base, addition, RecipeCategory.MISC)
.unlocks("criteria", criteria) // 该配方如何解锁
.save(writer, name); // 将数据加入生成器

SpecialRecipeBuilder​

SpecialRecipeBuilder is used to generate empty JSONs for dynamic recipes that cannot easily be constrained to the recipe JSON format (dying armor, firework, etc.). The builder can be initialized via #special.

// 在RecipeProvider#buildRecipes(writer)中
SpecialRecipeBuilder.special(dynamicRecipeSerializer)
.save(writer, name); // 将数据加入生成器

条件性配方​

条件性配方也可以是通过ConditionalRecipe$Builder生成的数据。生成器可以使用#builder获得。

每个配方的条件可以通过首先调用#addCondition,然后在指定所有条件后调用#addRecipe来指定。这个过程可以重复多次,只要程序员愿意。

在指定了所有配方后,可以在最后使用#generateAdvancement为每个配方添加进度。或者,可以使用#setAdvancement设置条件性进度。

// 在RecipeProvider#buildRecipes(writer)中
ConditionalRecipe.builder()
// 为该配方添加条件
.addCondition(...)
// 添加当条件为true时返回的配方
.addRecipe(...)

// 为下一个配方添加接下来的条件
.addCondition(...)
// 添加当条件为true时返回的下一个配方
.addRecipe(...)

// 创建条件性进度,其使用上面配方中的条件和所解锁的进度
.generateAdvancement()
.build(writer, name);

IConditionBuilder​

为了简化向条件配方添加条件而不必手动构造每个条件实例,扩展的RecipeProvider可以实现IConditionBuilder。该接口添加了可以轻松构造条件实例的方法。

// 在ConditionalRecipe$Builder#addCondition中
(
// 如果'examplemod:example_item'
// 或(OR)'examplemod:example_item2'存在
// 并且(AND)
// 非FALSE(NOT FALSE)

// Methods are defined by IConditionBuilder
and(
or(
itemExists("examplemod", "example_item"),
itemExists("examplemod", "example_item2")
),
not(
FALSE()
)
)
)

自定义配方序列化器​

自定义配方序列化器可以是通过创建可以构造FinishedRecipe的生成器生成的数据。完成的配方将配方数据及其所解锁的进度(如果存在)编码为JSON。此外,还指定了配方的名称和序列化器,以了解在加载时向何处写入以及可以解码对象的内容。构造完FinishedRecipe后,只需将其传递给RecipeProvider#buildRecipes提供的Consumer。

!!! 提示 FinishedRecipe足够灵活,任何对象转换都可以是数据生成的,而不仅仅是物品。