1. IIngredientFactory
Forge 在原版的 JSON 合成表的基础上提供的五种扩展功能之一。如名字所示,它是个将 JSON 转化为 Ingredient
实例的抽象工厂,本质上是 JSON 的反序列化器。
package my_mod;
public final class MyIngredientFactory implements IIngredientFactory {
@Nonnull
@Override
public Ingredient parse(JsonContext context, JsonObject json) {
return new MyIngredient();
}
}
1.1. “实现” Ingredient
Ingredient
并非是接口,所以说“继承”或“覆写”可能更准确些。总而言之,既然是自定义的 Ingredient
,它至少要覆写下面这几个方法:
public final class MyIngredient extends Ingredient {
// 来自 java.util.function.Predicate
// 在这里它用于检查给定的 ItemStack 是否与该 Ingredient 相匹配。
@Override
public boolean apply(@Nullable ItemStack input) { // 应该是 Mojang 抽风了,ItemStack 在这里不可能是 null
return false;
}
/*
* 返回包含已知所有能令 MyIngredient::apply 返回 true 的所有 ItemStack
* 的数组。用于原版小绿书中的合成提示。
*/
@Nonnull
@Override
public ItemStack[] getMatchingStacks() {
return new ItemStack[0];
}
/*
* 返回包含已知所有能令 MyIngredient::apply 返回 true 的所有 ItemStack
* 的打包版本的 IntList。打包需要通过 RecipeItemHelper 完成。用于原版小绿
* 书中的合成提示。这个方法的返回值应当等价于下列 statement 的结果:
* <pre>
* new IntArrayList(Arrays.stream(this.getMatchingStacks())
* .mapToInt(RecipeItemHelper::pack)
* .sorted()
* .toArray())
* </pre>
*
* 注:IntList 来自 FastUtil。FastUtil 的 IntList 有对 Java 集合框架
* 的兼容(即实现 List<Integer>)。
*
* 这里有一个小知识:虽然 RecipeItemHelper 这个类是客户端包里的(具体来说是
* net.minecraft.client.util 包中),但它没有 @SideOnly(Side.CLIENT),
* 因此可以放心使用。
*/
@Nonnull
@Override
public IntList getValidItemStacksPacked() {
return IntLists.empty();
}
/*
* 令此 Ingredient 实例“作废”。原版的 Ingredient 中,getMatchingStacks()
* 和 getValidItemStacksPacked() 两个方法实际上是惰性求值的,且会缓存结果。
* 此方法用于清理这些无效缓存。
*/
@Nonnull
@Override
protected void invalidate() {
}
/*
* 这个方法的存在和下列几个 Forge 的 Issue 和 Pull Request 有关:
*
* https://github.com/MinecraftForge/MinecraftForge/pull/4472
* https://github.com/MinecraftForge/MinecraftForge/issues/4516
* https://github.com/MinecraftForge/MinecraftForge/pull/4519
*
* 可通过下面的测试判断该方法的返回值:
* “该 Ingredient 的实现是否是纯粹地判断固定物品和固定 metadata”?
* 若通过该测试,则应返回 true,否则应返回 false。
* 举例:可以实现一个真正意义上的忽略 metadata 的 Ingredient,此时
* 对 metadata 的匹配就不是固定的(一对多),则 isSimple 应返回 false。
*/
@Override
public boolean isSimple() {
return true;
}
}
然后根据具体实现调整对应的 IIngredientFactory
实现即可。
1.2. 使用
要使用自定义的 Ingredient
,就需要先在 assets/[modid]/recipes/_factories.json
中声明:
{
"ingredients": {
"my_ingredient": "my_mod.MyIngredientFactory"
}
}
注意 "my_ingredient"
的值是一个类的 Canonical name。
正确声明后,即可在合成中如此声明:
{
"type": "minecraft:crafting_shapeless",
"group": "my_mod:example",
"result": { "item": "minecraft:diamond_block", "count": 64 },
"ingredients": [
{
"type": "my_mod:my_ingredient",
"...": "..."
}
]
}
IIngredientFactory
中,工厂方法的 JsonObject json
参数即代表这一小块 JSON:
{
"type": "my_mod:my_ingredient",
"...": "..."
}