概要
モデル、翻訳、Block State、ルートテーブル、レシピの各JSONファイルをJavaのコードから自動生成させます。
入門編のようにリソースパックやデータパックと同じ方法で作っても問題ありませんが、自動生成させれば、別ファイルの参照などを自動で埋め、JSON記述時の誤字を避けることができます。
前提
- 入門編すべて
動作確認
2021年3月22日
- Minecraft 1.16.5
- Forge 36.1.0
解説
TitaniumMod
TitaniumMod/src/main/java/com/tntmodders/titaniummod/TitaniumMod.java
package com.tntmodders.titaniummod;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.material.MaterialColor;
import net.minecraft.data.DataGenerator;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraftforge.common.ToolType;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.RegistryObject;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.GatherDataEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import java.util.Collection;
@Mod(TitaniumMod.MOD_ID)
public class TitaniumMod {
public static final String MOD_ID = "titaniummod";
public TitaniumMod() {
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
Blocks.register(modEventBus);
Items.register(modEventBus);
modEventBus.addListener(this::registerProviders);
}
private void registerProviders(GatherDataEvent event) {
DataGenerator gen = event.getGenerator();
if (event.includeClient()) {
gen.addProvider(new TitaniumModBlockStateProvider(gen, MOD_ID, event.getExistingFileHelper()));
gen.addProvider(new TitaniumModItemModelProvider(gen, MOD_ID, event.getExistingFileHelper()));
gen.addProvider(new TitaniumModEnUsLanguageProvider(gen, MOD_ID));
gen.addProvider(new TitaniumModJaJpLanguageProvider(gen, MOD_ID));
}
if (event.includeServer()) {
gen.addProvider(new TitaniumModRecipeProvider(gen));
gen.addProvider(new TitaniumModLootTableProvider(gen));
}
}
public static class Blocks {
private static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MOD_ID);
public static final RegistryObject<Block> TITANIUM_BLOCK = BLOCKS.register("titanium_block", () -> new Block(AbstractBlock.Properties
.of(Material.METAL, MaterialColor.METAL)
.requiresCorrectToolForDrops()
.strength(5.0F, 6.0F)
.sound(SoundType.METAL)
.harvestTool(ToolType.PICKAXE)
.harvestLevel(1)
));
public static void register(IEventBus eventBus) {
BLOCKS.register(eventBus);
}
public static Collection<RegistryObject<Block>> getEntries() {
return BLOCKS.getEntries();
}
}
public static class Items {
private static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MOD_ID);
public static final RegistryObject<Item> TITANIUM_BLOCK = ITEMS.register("titanium_block", () -> new BlockItem(Blocks.TITANIUM_BLOCK.get(), new Item.Properties()
.tab(ItemGroup.TAB_BUILDING_BLOCKS)));
public static final RegistryObject<Item> TITANIUM_INGOT = ITEMS.register("titanium_ingot", () -> new Item(new Item.Properties()
.tab(ItemGroup.TAB_MATERIALS)));
public static void register(IEventBus eventBus) {
ITEMS.register(eventBus);
}
}
}
GatherDataEvent
を引数とするregisterProviders
をmodEventBus.addListener
で登録することで、「runData」を実行した時にregisterProviders
が呼ばれるようになります。
GatherDataEvent
から得られるDataGenerator
にaddProvider
でIDataProvider
を渡すことで、JSONファイルが生成されるようになります。
TitaniumMod.Blocks.getEntries
はTitaniumModLootTableProvider
で利用しています。
TitaniumModBlockStateProvider
TitaniumMod/src/main/java/com/tntmodders/titaniummod/TitaniumModBlockStateProvider.java
package com.tntmodders.titaniummod;
import net.minecraft.block.Block;
import net.minecraft.data.DataGenerator;
import net.minecraftforge.client.model.generators.BlockStateProvider;
import net.minecraftforge.client.model.generators.ModelFile;
import net.minecraftforge.common.data.ExistingFileHelper;
public class TitaniumModBlockStateProvider extends BlockStateProvider {
public TitaniumModBlockStateProvider(DataGenerator gen, String modId, ExistingFileHelper exFileHelper) {
super(gen, modId, exFileHelper);
}
@Override
protected void registerStatesAndModels() {
simpleBlockWithItem(TitaniumMod.Blocks.TITANIUM_BLOCK.get());
}
private void simpleBlockWithItem(Block block) {
ModelFile model = cubeAll(block);
simpleBlock(block, model);
simpleBlockItem(block, model);
}
}
BlockStateProvider
を継承しているため、registerStatesAndModels
内で登録したBlock Stateとモデルが生成されます。BlockStateProvider
にはsimpleBlock
の他にも、原木や階段などに対応したメソッドが用意されています。
TitaniumModItemModelProvider
TitaniumMod/src/main/java/com/tntmodders/titaniummod/TitaniumModItemModelProvider.java
package com.tntmodders.titaniummod;
import net.minecraft.data.DataGenerator;
import net.minecraft.item.Item;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.generators.ItemModelProvider;
import net.minecraftforge.common.data.ExistingFileHelper;
import java.util.Objects;
public class TitaniumModItemModelProvider extends ItemModelProvider {
public TitaniumModItemModelProvider(DataGenerator gen, String modId, ExistingFileHelper existingFileHelper) {
super(gen, modId, existingFileHelper);
}
@Override
protected void registerModels() {
simpleItem(TitaniumMod.Items.TITANIUM_INGOT.get());
}
private void simpleItem(Item item) {
ResourceLocation name = Objects.requireNonNull(item.getRegistryName());
singleTexture(name.getPath(), mcLoc(folder + "/generated"), "layer0", new ResourceLocation(name.getNamespace(), folder + "/" + name.getPath()));
}
}
ItemModelProvider
を継承しているため、registerModels
内で登録したモデルが生成されます。
TitaniumModEnUsLanguageProvider
TitaniumMod/src/main/java/com/tntmodders/titaniummod/TitaniumModEnUsLanguageProvider.java
package com.tntmodders.titaniummod;
import net.minecraft.data.DataGenerator;
import net.minecraftforge.common.data.LanguageProvider;
public class TitaniumModEnUsLanguageProvider extends LanguageProvider {
public TitaniumModEnUsLanguageProvider(DataGenerator gen, String modId) {
super(gen, modId, "en_us");
}
@Override
protected void addTranslations() {
add(TitaniumMod.Blocks.TITANIUM_BLOCK.get(), "Block of Titanium");
add(TitaniumMod.Items.TITANIUM_INGOT.get(), "Titanium Ingot");
}
}
LanguageProvider
を継承しているため、addTranslations
内で登録した翻訳が生成されます。LanguageProvider
には、エンティティや任意の翻訳鍵に対応したadd
のオーバーロードが用意されています。
TitaniumModJaJpLanguageProvider
TitaniumMod/src/main/java/com/tntmodders/titaniummod/TitaniumModJaJpLanguageProvider.java
package com.tntmodders.titaniummod;
import net.minecraft.data.DataGenerator;
import net.minecraftforge.common.data.LanguageProvider;
public class TitaniumModJaJpLanguageProvider extends LanguageProvider {
public TitaniumModJaJpLanguageProvider(DataGenerator gen, String modId) {
super(gen, modId, "ja_jp");
}
@Override
protected void addTranslations() {
add(TitaniumMod.Blocks.TITANIUM_BLOCK.get(), "チタンブロック");
add(TitaniumMod.Items.TITANIUM_INGOT.get(), "チタンインゴット");
}
}
出力されたJSONファイル内で日本語の文字はエスケープされますが、ゲーム内での表示に問題はありません。
TitaniumModRecipeProvider
TitaniumMod/src/main/java/com/tntmodders/titaniummod/TitaniumModRecipeProvider.java
package com.tntmodders.titaniummod;
import net.minecraft.data.*;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.function.Consumer;
@ParametersAreNonnullByDefault
public class TitaniumModRecipeProvider extends RecipeProvider {
public TitaniumModRecipeProvider(DataGenerator generatorIn) {
super(generatorIn);
}
@Override
protected void buildShapelessRecipes(Consumer<IFinishedRecipe> consumer) {
ShapedRecipeBuilder.shaped(TitaniumMod.Blocks.TITANIUM_BLOCK.get())
.define('#', TitaniumMod.Items.TITANIUM_INGOT.get())
.pattern("###")
.pattern("###")
.pattern("###")
.unlockedBy("has_titanium_ingot", has(TitaniumMod.Items.TITANIUM_INGOT.get()))
.save(consumer);
ShapelessRecipeBuilder.shapeless(TitaniumMod.Items.TITANIUM_INGOT.get(), 9)
.requires(TitaniumMod.Blocks.TITANIUM_BLOCK.get())
.group("titanium_ingot")
.unlockedBy("has_titanium_block", has(TitaniumMod.Blocks.TITANIUM_BLOCK.get()))
.save(consumer, "titaniummod:titanium_ingot_from_titanium_block");
}
}
RecipeProvider
を継承しているため、buildShapelessRecipes
内でconsumer
に登録したレシピとそれに対応する進捗が生成されます。RecipeProvider
にはバニラのレシピの登録処理があるので、参考にしてください。
TitaniumModLootTableProvider
TitaniumMod/src/main/java/com/tntmodders/titaniummod/TitaniumModLootTableProvider.java
package com.tntmodders.titaniummod;
import com.google.common.collect.ImmutableList;
import com.mojang.datafixers.util.Pair;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.Block;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.LootTableProvider;
import net.minecraft.data.loot.BlockLootTables;
import net.minecraft.loot.LootParameterSet;
import net.minecraft.loot.LootParameterSets;
import net.minecraft.loot.LootTable;
import net.minecraft.loot.ValidationTracker;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.RegistryObject;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
public class TitaniumModLootTableProvider extends LootTableProvider {
public TitaniumModLootTableProvider(DataGenerator gen) {
super(gen);
}
@Override
protected List<Pair<Supplier<Consumer<BiConsumer<ResourceLocation, LootTable.Builder>>>, LootParameterSet>> getTables() {
return ImmutableList.of(Pair.of(TitaniumModBlockLootTables::new, LootParameterSets.BLOCK));
}
@Override
protected void validate(Map<ResourceLocation, LootTable> map, ValidationTracker validationtracker) {
}
private static class TitaniumModBlockLootTables extends BlockLootTables {
@Override
protected Iterable<Block> getKnownBlocks() {
return TitaniumMod.Blocks.getEntries().stream().map(RegistryObject::get).collect(Collectors.toList());
}
@Override
protected void addTables() {
dropSelf(TitaniumMod.Blocks.TITANIUM_BLOCK.get());
}
}
}
LootTableProvider
を継承しているため、getTables
で返した種類のルートテーブルが生成されます。今回は、ブロックのドロップについてTitaniumModBlockLootTables
で生成します。
validate
は、LootTableProvider
ではバニラのルートテーブルがすべて生成されたか確認しているので、何もしないようオーバーライドします。
TitaniumModBlockLootTables
はBlockLootTables
を継承しているため、getKnownBlocks
で返したブロックに対応する情報をaddTables
で登録することで、ルートテーブルが生成されます。BlockLootTables
には、dropSelf
の他にも様々なメソッドが用意されており、バニラの登録処理もあります。
build.gradle
「MODの情報の登録」で変更した以下の二点を確認し、Gradle Tool Windowから「genIntellijRuns」を再度実行してください。
minecraft { runs {
内の4つのexamplemod
をtitaniummod
に置き換えてください。jar {
とmanifest {
との間に行を追加し、compileJava { options.encoding = 'UTF-8' }
を挿入してください。
生成
実行構成選択から「runData」を選択し、実行してください。成功したらTitaniumMod/generated/resources/
にJSONファイルが生成されます。runClientなどの実行やビルドの際は自動的にTitaniumMod/main/resources/
のファイルと合わせて読み込まれるので、移動する必要はありません。
TitaniumMod/main/resources/
にある入門編で書いたJSONファイルは不要なので、同じファイルが正しく生成されていることを確認して削除してください。テクスチャを削除しないよう気を付けてください。
ファイルが生成されない場合は、「Run」のTool WindowやTitaniumMod/run/logs/latest.log
に失敗した原因が出力されるので、解決してください。
関連クラス
net.minecraft.data.BlockModelProvider
:バニラのブロックのモデルとBlock Stateの登録処理があります。Forgeの処理と異なるため同じ方法では登録できませんが、参考にしてください。net.minecraft.data.ItemModelProvider
:バニラのアイテムモデルの登録処理があります。Forgeの処理と異なるため同じ方法では登録できませんが、参考にしてください。
“JSONファイルの自動生成 (1.16.5)” への2件のフィードバック
TitaniumModJaJpLanguageProviderをUTF-8で保存しているのですが、runDataするとそのファイルをShiftJISで読み込むみたいで文字化けした状態でエスケープされるようでした。なので、TitaniumModJaJpLanguageProviderをShiftJISで保存してからrunDataしたところ、ただしくエスケープされマイクラ上でもただしく表示されます。
TNT Moddersさんはどのようにこれを回避しているのでしょうか?たとえばですが、UTF-8のままでも正しく処理されるように何かしらの設定を施しているのでしょうか。
ソースコードはUTF-8で保存し、
build.gradle
にてUTF-8でビルドするよう設定しています。「MODの情報の登録」の
build.gradle
の項目で、「jar {
とmanifest {
との間に行を追加し、compileJava { options.encoding = 'UTF-8' }
を挿入しました」とあるのがその部分です。この記事の内容だけでは分かりづらかったので、「MODの情報の登録」とともに更新しました。