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

概要

モデル、翻訳、Block State、ルートテーブル、レシピの各JSONファイルをJavaのコードから自動生成させます。

入門編のようにリソースパックやデータパックと同じ方法で作っても問題ありませんが、自動生成させれば、別ファイルの参照などを自動で埋め、JSON記述時の誤字を避けることができます。

GitHubでこの記事の時点の状態を確認できます。

前提

  • 入門編すべて

動作確認

2021年1月28日

  • Minecraft 1.16.4
  • Forge 35.1.4

解説

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.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 BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MOD_ID);
        public static final RegistryObject TITANIUM_BLOCK = BLOCKS.register("titanium_block", () -> new Block(AbstractBlock.Properties
                .create(Material.IRON)
                .setRequiresTool()
                .hardnessAndResistance(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> getEntries() {
            return BLOCKS.getEntries();
        }
    }

    public static class Items {
        private static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MOD_ID);
        public static final RegistryObject TITANIUM_BLOCK = ITEMS.register("titanium_block", () -> new BlockItem(Blocks.TITANIUM_BLOCK.get(), new Item.Properties()
                .group(ItemGroup.BUILDING_BLOCKS)));
        public static final RegistryObject TITANIUM_INGOT = ITEMS.register("titanium_ingot", () -> new Item(new Item.Properties()
                .group(ItemGroup.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 registerRecipes(Consumer consumer) {
        ShapedRecipeBuilder.shapedRecipe(TitaniumMod.Blocks.TITANIUM_BLOCK.get())
                .key('#', TitaniumMod.Items.TITANIUM_INGOT.get())
                .patternLine("###")
                .patternLine("###")
                .patternLine("###")
                .addCriterion("has_titanium_ingot", hasItem(TitaniumMod.Items.TITANIUM_INGOT.get()))
                .build(consumer);
        ShapelessRecipeBuilder.shapelessRecipe(TitaniumMod.Items.TITANIUM_INGOT.get(), 9)
                .addIngredient(TitaniumMod.Blocks.TITANIUM_BLOCK.get())
                .setGroup("titanium_ingot")
                .addCriterion("has_titanium_block", hasItem(TitaniumMod.Blocks.TITANIUM_BLOCK.get()))
                .build(consumer, "titaniummod:titanium_ingot_from_titanium_block");
    }
}

RecipeProviderを継承しているため、registerRecipes内で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 getKnownBlocks() {
            return TitaniumMod.Blocks.getEntries().stream().map(RegistryObject::get).collect(Collectors.toList());
        }

        @Override
        protected void addTables() {
            registerDropSelfLootTable(TitaniumMod.Blocks.TITANIUM_BLOCK.get());
        }
    }
}

LootTableProviderを継承しているため、getTablesで返した種類のルートテーブルが生成されます。今回は、ブロックのドロップについてTitaniumModBlockLootTablesで生成します。

validateは、LootTableProviderではバニラのルートテーブルがすべて生成されたか確認しているので、何もしないようオーバーライドします。

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

build.gradle

MODの情報の登録」でminecraft { runs { data {内のargs '--mod', 'examplemod', '--all', ...examplemodtitaniummodに置き換えてあることを確認し、Gradle Tool Windowから「genIntellijRuns」を再度実行してください。

生成

実行構成選択から「runData」を選択し、実行してください。成功したらTitaniumMod/generated/resources/にJSONファイルが生成されます。runClientなどの実行やビルドの際は自動的にTitaniumMod/main/resources/のファイルと合わせて読み込まれるので、移動する必要はありません。

TitaniumMod/main/resources/にある入門編で書いたJSONファイルは不要なので、同じファイルが正しく生成されていることを確認して削除してください。テクスチャを削除しないよう気を付けてください。

ファイルが生成されない場合は、「Run」のTool WindowやTitaniumMod/run/logs/latest.logに失敗した原因が出力されるので、解決してください。

リンク

コメントはこちらです。(スパム対策の為コメントは手動承認になっています。未承認のコメントは表示されないので連投はお控え下さい。)

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