JSONファイルの自動生成 (1.16.5)

概要

モデル、翻訳、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を引数とするregisterProvidersmodEventBus.addListenerで登録することで、「runData」を実行した時にregisterProvidersが呼ばれるようになります。

GatherDataEventから得られるDataGeneratoraddProviderIDataProviderを渡すことで、JSONファイルが生成されるようになります。

TitaniumMod.Blocks.getEntriesTitaniumModLootTableProviderで利用しています。

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ではバニラのルートテーブルがすべて生成されたか確認しているので、何もしないようオーバーライドします。

TitaniumModBlockLootTablesBlockLootTablesを継承しているため、getKnownBlocksで返したブロックに対応する情報をaddTablesで登録することで、ルートテーブルが生成されます。BlockLootTablesには、dropSelfの他にも様々なメソッドが用意されており、バニラの登録処理もあります。

build.gradle

MODの情報の登録」で変更した以下の二点を確認し、Gradle Tool Windowから「genIntellijRuns」を再度実行してください。

  • minecraft { runs {内の4つのexamplemodtitaniummodに置き換えてください。
  • 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の処理と異なるため同じ方法では登録できませんが、参考にしてください。

リンク

2件のコメント

  1. 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の情報の登録」とともに更新しました。

Artan へ返信するコメントをキャンセル

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください