Minecraft 1.21 Fabric 模組開發(1)-前置準備
Minecraft 1.21 Fabric 模組開發(2)-新增物品
Minecraft 1.21 Fabric 模組開發(3)-新增方塊
寫在前頭
1.不是JAVA教學所以不解釋程式碼為何這樣寫,想知道為什麼這麼寫可以在 Fabric docs 或 Fabric wiki 裡面找到。
2.我的模組名稱為mays-mod
,程式碼中看到 mays-mod 的地方請自行換成你的 mod 名稱。
3.圖片看不清的話可以點擊放大看。
為什麼需要 Data Generation
模組東西一旦很多的時候一個一個 json 檔去寫會很麻煩,為了提升效率,建議改成用程式碼去自動產生 json。
src/main/java/com/<mod_id>
底下新增 datagen
資料夾並新增五個 java 檔:
- ModBlockTagProvider
- ModItemTagProvider
- ModLootTablesProvider
- ModModelsProvider
- ModRecipesProvider
好奇Providers怎麼寫請參考:datagen_setup#adding_providers
語系檔也可以生成,不過我這邊還是沿用之前手動建的json,有需要可以看 Language Generation
ModBlockTagProvider
/data/tags/block/special_ores.json
{
"replace": false,
"values": [
"mays-mod:creeper_ore",
"#minecraft:coal_ores",
"#minecraft:iron_ores",
"#minecraft:gold_ores",
"#minecraft:lapis_ores",
"#minecraft:diamond_ores",
"#minecraft:redstone_ores",
"#minecraft:emerald_ores",
"#minecraft:copper_ores"
]
}
轉換為 /java/com/datagen/ModBlockTagProvider.java
package com.may.firstmod.datagen;
import com.may.firstmod.block.ModBlocks;
import com.may.firstmod.util.ModTags;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
import net.minecraft.registry.RegistryWrapper;
import net.minecraft.registry.tag.BlockTags;
import java.util.concurrent.CompletableFuture;
public class ModBlockTagProvider extends FabricTagProvider.BlockTagProvider {
public ModBlockTagProvider(FabricDataOutput output, CompletableFuture<RegistryWrapper.WrapperLookup> registriesFuture) {
super(output, registriesFuture);
}
@Override
protected void configure(RegistryWrapper.WrapperLookup arg) {
getOrCreateTagBuilder(ModTags.Blocks.SPECIAL_ORES)
.add(ModBlocks.CREEPER_ORE)
.forceAddTag(BlockTags.COAL_ORES)
.forceAddTag(BlockTags.IRON_ORES)
.forceAddTag(BlockTags.GOLD_ORES)
.forceAddTag(BlockTags.LAPIS_ORES)
.forceAddTag(BlockTags.DIAMOND_ORES)
.forceAddTag(BlockTags.REDSTONE_ORES)
.forceAddTag(BlockTags.EMERALD_ORES)
.forceAddTag(BlockTags.COPPER_ORES);
getOrCreateTagBuilder(BlockTags.PICKAXE_MINEABLE)
.add(ModBlocks.CREEPER_ORE)
.add(ModBlocks.CREEPER_BLOCK);
getOrCreateTagBuilder(BlockTags.NEEDS_IRON_TOOL)
.add(ModBlocks.CREEPER_ORE)
.add(ModBlocks.CREEPER_BLOCK);
}
}
ModItemTagProvider
目前我們沒用到 item tag,所以內容留空,之後有需要再修改。
/java/com/datagen/ModItemTagProvider.java
package com.may.firstmod.datagen;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
import net.minecraft.registry.RegistryWrapper;
import java.util.concurrent.CompletableFuture;
public class ModItemTagProvider extends FabricTagProvider.ItemTagProvider {
public ModItemTagProvider(FabricDataOutput output, CompletableFuture<RegistryWrapper.WrapperLookup> completableFuture) {
super(output, completableFuture);
}
@Override
protected void configure(RegistryWrapper.WrapperLookup wrapperLookup) {
}
}
ModLootTablesProvider
/data/loot_table/blocks
轉換為 /java/com/datagen/ModLootTablesProvider.java
package com.may.firstmod.datagen;
import com.may.firstmod.block.ModBlocks;
import com.may.firstmod.item.ModItems;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockLootTableProvider;
import net.minecraft.block.Block;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.Enchantments;
import net.minecraft.item.Item;
import net.minecraft.loot.LootTable;
import net.minecraft.loot.entry.ItemEntry;
import net.minecraft.loot.entry.LootPoolEntry;
import net.minecraft.loot.function.ApplyBonusLootFunction;
import net.minecraft.loot.function.SetCountLootFunction;
import net.minecraft.loot.provider.number.UniformLootNumberProvider;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.RegistryWrapper;
import java.util.concurrent.CompletableFuture;
public class ModLootTablesProvider extends FabricBlockLootTableProvider {
public ModLootTablesProvider(FabricDataOutput dataOutput, CompletableFuture<RegistryWrapper.WrapperLookup> registryLookup) {
super(dataOutput, registryLookup);
}
@Override
public void generate() {
addDrop(ModBlocks.CREEPER_ORE, copperOreDrops(ModBlocks.CREEPER_ORE, ModItems.RAW_CREEPER, 1.0f, 1.0f));
addDrop(ModBlocks.CREEPER_BLOCK, copperOreDrops(ModBlocks.CREEPER_BLOCK, ModItems.CREEPER_INGOT, 1.0f, 1.0f));
}
public LootTable.Builder copperOreDrops(Block drop, Item item, float minExp, float maxExp) {
RegistryWrapper.Impl<Enchantment> impl = this.registryLookup.getWrapperOrThrow(RegistryKeys.ENCHANTMENT);
return this.dropsWithSilkTouch(
drop,
(LootPoolEntry.Builder<?>) this.applyExplosionDecay(
drop,
ItemEntry.builder(item)
.apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(minExp, maxExp)))
.apply(ApplyBonusLootFunction.oreDrops(impl.getOrThrow(Enchantments.FORTUNE)))
)
);
}
}
ModModelsProvider
/resources/assets/models
轉換為 /java/com/datagen/ModModelsProvider.java
package com.may.firstmod.datagen;
import com.may.firstmod.block.ModBlocks;
import com.may.firstmod.item.ModItems;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider;
import net.minecraft.data.client.BlockStateModelGenerator;
import net.minecraft.data.client.ItemModelGenerator;
import net.minecraft.data.client.Models;
public class ModModelsProvider extends FabricModelProvider {
public ModModelsProvider(FabricDataOutput output) {
super(output);
}
@Override
public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) {
blockStateModelGenerator.registerSimpleCubeAll(ModBlocks.CREEPER_ORE);
blockStateModelGenerator.registerSimpleCubeAll(ModBlocks.CREEPER_BLOCK);
}
@Override
public void generateItemModels(ItemModelGenerator itemModelGenerator) {
itemModelGenerator.register(ModItems.RAW_CREEPER, Models.GENERATED);
itemModelGenerator.register(ModItems.CREEPER_INGOT, Models.GENERATED);
}
}
ModRecipesProvider
/resources/data/recipe
轉換為 /java/com/datagen/ModRecipesProvider.java
package com.may.firstmod.datagen;
import com.may.firstmod.block.ModBlocks;
import com.may.firstmod.item.ModItems;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.item.ItemConvertible;
import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.registry.RegistryWrapper;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class ModRecipesProvider extends FabricRecipeProvider {
public static final List<ItemConvertible> SMELTABLE_TO_INGOT = List.of(ModItems.RAW_CREEPER);
public ModRecipesProvider(FabricDataOutput output, CompletableFuture<RegistryWrapper.WrapperLookup> registriesFuture) {
super(output, registriesFuture);
}
@Override
public void generate(RecipeExporter exporter) {
// offerReversibleCompactingRecipes 可逆
// offerCompactingRecipe 不可逆
offerReversibleCompactingRecipes(exporter, RecipeCategory.MISC, ModItems.CREEPER_INGOT, RecipeCategory.BUILDING_BLOCKS, ModBlocks.CREEPER_BLOCK);
// offerSmelting 熔爐
offerSmelting(exporter, SMELTABLE_TO_INGOT, RecipeCategory.MISC, ModItems.CREEPER_INGOT, 0.7F, 200, "creeper_ingot");
// offerBlasting 高爐
offerBlasting(exporter, SMELTABLE_TO_INGOT, RecipeCategory.MISC, ModItems.CREEPER_INGOT, 0.7F, 100, "creeper_ingot");
}
}
初始化 Providers
在 /java/com/XxxModDataGenerator.java 中初始化上面這些 provider
每當執行我們先前建立的 gradle 任務(runDatagen)時,都會呼叫onInitializeDataGenerator函數。
// MaysModDataGenerator.java
package com.may.firstmod;
import com.may.firstmod.datagen.*;
import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
public class MaysModDataGenerator implements DataGeneratorEntrypoint {
@Override
public void onInitializeDataGenerator(FabricDataGenerator generator) {
FabricDataGenerator.Pack pack = generator.createPack();
pack.addProvider(ModModelsProvider::new);
pack.addProvider(ModItemTagProvider::new);
pack.addProvider(ModBlockTagProvider::new);
pack.addProvider(ModLootTablesProvider::new);
pack.addProvider(ModRecipesProvider::new);
}
}
接著刪除以下資料夾(記得備份或先建新的branch):
- resources/data
- resources/assets/models
- resources/assets/blockstates
啟用Data generation
如果一開始使用Fabric模组生成器有勾選 Data generation 的話可跳過這節。
在 build.gradle 中加上:
fabricApi {
configureDataGeneration()
}
並確認一下 entrypoints 有沒有 fabric-datagen,沒有的話一樣手動補上:
"entrypoints": {
"main": [
"com.may.firstmod.MaysMod"
],
"client": [
"com.may.firstmod.MaysModClient"
],
"fabric-datagen": [
"com.may.firstmod.MaysModDataGenerator"
]
},
生成json
以上都做完之後,從IDE右側開啟gradle視窗,找到 Tasks.fabric.runDatagen 雙擊執行。
完成之後,src/main 底下會多出一個資料夾 generated
,裡面的內容就是我們之前手動新建的那些 json檔。
現在可以重新開啟遊戲看看之前新增的方塊、物品、配方有沒有都正常,沒有的話就是有程式碼寫錯了,再認真檢查一下。