前后端都写完了,接下来编写我们的数据,首先介绍我希望合成表应当是什么形式的结构,我们希望这个合成表是一种特殊的合成表,只能在之前编写的WorkBlock中形成。其次我们希望使用泥土+高草丛合成一个粘液球:
{
"type": "modid:work_recipe",
"input": [
{
"item": "minecraft:dirt"
},
{
"item": "minecraft:tall_grass"
}
],
"resultItem": "minecraft:slime_ball",
"resultCount": 1
}
这里type制定了配方类型,其指向序列化器WorkBlockRecipeSerializer的ID。
input是一个数组,其中包含一个item,这个input数组指向一个Ingredient数组,你也可以在序列化时做一些其它的方式来简化它的结构。
result*的两个选字段分别表示了合成结果及其结果的数量,即1个粘液球,这两个字段最终会一起成为一个ItemStack实例
这里采用了两种class来表示输入和输出,你也可以只采用一种,比如只采用ItemStack,此处我写出两种仅是因为MC自己的合成台代码里采用了这两种混合的方式,因为Ingredient有一个test方法来对方块进行对比,但我认为自己去编写这个对比方式或许会更好(尽管我并没有在这么去做)。
下面是对应的实体类和序列器:
/**
* 合成表的抽象
*/
public class WorkBlockRecipe implements Recipe<WorkBlockInputInventory> {
// 合成表本身的输入,也可以不是数组,主要是在json序列化时不出问题即可
private final Ingredient[] inputStackList;
// 合成表本身的输入所对应的输出
private final ItemStack outputStack;
private final Identifier id;
public WorkBlockRecipe(Ingredient[] inputStackList, ItemStack outputStack, Identifier id) {
this.inputStackList = inputStackList;
this.outputStack = outputStack;
// 网络读写需要,基本是一个合成表一个id
this.id = id;
}
// 合成表也需要一个Type,是fabric的规定,重写好toString即可
public static class Type implements RecipeType<WorkBlockRecipe> {
private Type() {}
public static final Type INSTANCE = new Type();
public static final String NAME = "work_recipe_type";
public static final Identifier ID = new Identifier(ModInfo.MOD_ID, NAME);
@Override
public String toString() {
return NAME;
}
}
public Ingredient[] getInputStackList() {
return inputStackList;
}
public ItemStack getOutputStack() {
return outputStack;
}
// fabric在创建世界时会将所有合成表存放到内存里,然后调用这个方法进行匹配
@Override
public boolean matches(WorkBlockInputInventory inventory, World world) {
if(inventory.size() < 2) return false;
for (int i = 0; i < 9; i++) {
if(!inputStackList[i].test(inventory.getStack(i))){
return false;
}
}
return true;
}
// 进行合成
@Override
public ItemStack craft(WorkBlockInputInventory inventory, DynamicRegistryManager registryManager) {
return this.getOutputStack().copy();
}
// 可能是校验合成表长宽是否合适吧,你可以在里面做一点判断
@Override
public boolean fits(int width, int height) {
return true;
}
@Override
public ItemStack getOutput(DynamicRegistryManager registryManager) {
return outputStack;
}
@Override
public Identifier getId() {
return this.id;
}
// 序列化工具,用于网络传输
@Override
public RecipeSerializer<?> getSerializer() {
return WorkBlockRecipeSerializer.INSTANCE;
}
@Override
public RecipeType<?> getType() {
return Type.INSTANCE;
}
}
// 序列化工具,用于将服务端验证后的合成表的数据传输给客户端以及将json合成表转换成合成表对象实例
public class WorkBlockRecipeSerializer implements RecipeSerializer<WorkBlockRecipe> {
public static WorkBlockRecipeSerializer INSTANCE = new WorkBlockRecipeSerializer();
public static String NAME = "work_recipe";
public static Identifier ID = new Identifier(ModInfo.MOD_ID, NAME);
// 将json文件转化为WorkBlockRecipe实例,可以根据你自己的设计需要进行
@Override
public WorkBlockRecipe read(Identifier identifier, JsonObject jsonObject) {
JsonArray input = jsonObject.get("input").getAsJsonArray();
Ingredient[] ingredient = new Ingredient[9];
for (int i = 0; i < 9; i++) {
ingredient[i] = Ingredient.ofItems(Registries.ITEM.get(new Identifier("minecraft:air")));
}
for (int i = 0; i < input.size(); i++) {
JsonElement jsonElement = input.get(i);
Ingredient ingredientTmp = Ingredient.fromJson(jsonElement);
ingredient[i] = ingredientTmp;
}
int resultCount = jsonObject.get("resultCount").getAsInt();
String resultItem = jsonObject.get("resultItem").getAsString();
if (resultItem == null) {
throw new JsonSyntaxException("工作配方格式错误,resultItem异常");
}
Item item = Registries.ITEM.getOrEmpty(new Identifier(resultItem))
.orElseThrow(() -> new JsonSyntaxException("无法找到对应resultItem产物"));
ItemStack outputStack = new ItemStack(item, resultCount);
return new WorkBlockRecipe(ingredient, outputStack, identifier);
}
// 由fabric调用,将得到的合成表序列化成二进制
@Override
public void write(PacketByteBuf buf, WorkBlockRecipe recipe) {
for (int i = 0; i < 9; i++) {
recipe.getInputStackList()[i].write(buf);
}
buf.writeItemStack(recipe.getOutputStack());
}
// 由fabric调用,将收到的二进制转为合成表
@Override
public WorkBlockRecipe read(Identifier id, PacketByteBuf buf) {
Ingredient[] ingredient = new Ingredient[9];
for (int i = 0; i < 9; i++) {
ingredient[i] = Ingredient.fromPacket(buf);
}
ItemStack outputStack = buf.readItemStack();
return new WorkBlockRecipe(ingredient, outputStack, id);
}
}
至于合成表的目录结构建议阅读官方文档,我这里仅给出参考 src └─main ├─java │ └─x │ └─x │ └─amazing │ ├─block // 这里面放方块 │ │ └─entity // 这里面放方块实体 │ ├─client │ ├─constant // modinfo在这里面 │ ├─inventory // 输入输出接口在这里面 │ │ └─api // easyinventory在这里面 │ ├─item // 物品在这里 │ ├─recipe // 配方实体类 │ │ └─serializer // 配方序列化器 │ ├─screen // GUI │ │ ├─handler │ │ └─slot │ └─util └─resources ├─assets │ └─amazing │ ├─blockstates │ ├─lang │ ├─models │ │ ├─block │ │ └─item │ └─textures │ ├─block │ └─item └─data └─amazing ├─loot_tables │ └─blocks └─recipes // 合成表json文件在这里