Skip to content

Data Generators

Data Generators are tools in Minecraft mod development used to automatically generate game resource files. Registrum has built-in rich data generation support, allowing automatic generation of models, recipes, language files, loot tables, and more.

Data Generation Overview

When using Registrum to register items, blocks, and entities, most data generation is handled automatically. This chapter will introduce how to customize and extend data generation.

Model Generation

Item Models

Use the model() method in ItemBuilder to customize item models:

java
public static final ItemEntry<Item> CUSTOM_ITEM = REGISTRUM
    .item("custom_item", Item::new)
    .model((ctx, provider) -> {
        // Use generated model
        provider.generated(ctx::getEntry);
    })
    .register();

// Handheld item model (like tools)
public static final ItemEntry<Item> CUSTOM_TOOL = REGISTRUM
    .item("custom_tool", Item::new)
    .model((ctx, provider) -> provider.handheld(ctx))
    .register();

// Custom parent model
public static final ItemEntry<Item> CUSTOM_MODEL = REGISTRUM
    .item("custom_model", Item::new)
    .model((ctx, provider) -> {
        provider.withExistingParent(ctx.getName(), 
            ResourceLocation.fromNamespaceAndPath("modid", "item/custom_parent"));
    })
    .register();

Block Models and Blockstates

Use the blockstate() method in BlockBuilder:

java
// Simple block (same texture on all sides)
public static final BlockEntry<Block> SIMPLE_BLOCK = REGISTRUM
    .block("simple_block", Block::new)
    .blockstate((ctx, provider) -> provider.simpleBlock(ctx.getEntry()))
    .simpleItem()
    .register();

// Pillar block (different top/bottom vs sides)
public static final BlockEntry<RotatedPillarBlock> PILLAR_BLOCK = REGISTRUM
    .block("pillar_block", RotatedPillarBlock::new)
    .blockstate((ctx, provider) -> provider.logBlock(ctx.getEntry()))
    .simpleItem()
    .register();

// Stairs block
public static final BlockEntry<StairBlock> STAIRS = REGISTRUM
    .block("stairs", p -> new StairBlock(Blocks.STONE.defaultBlockState(), p))
    .blockstate((ctx, provider) -> provider.stairsBlock(ctx.getEntry(), 
        ResourceLocation.fromNamespaceAndPath("modid", "block/base_texture")))
    .simpleItem()
    .register();

// Slab block
public static final BlockEntry<SlabBlock> SLAB = REGISTRUM
    .block("slab", SlabBlock::new)
    .blockstate((ctx, provider) -> {
        ResourceLocation texture = ResourceLocation.fromNamespaceAndPath("modid", "block/base_texture");
        provider.slabBlock(ctx.getEntry(), texture, texture);
    })
    .simpleItem()
    .register();

// Directional block
public static final BlockEntry<DirectionalBlock> DIRECTIONAL = REGISTRUM
    .block("directional", DirectionalBlock::new)
    .blockstate((ctx, provider) -> {
        provider.directionalBlock(ctx.getEntry(), 
            provider.models().cubeAll(ctx.getName(), 
                ResourceLocation.fromNamespaceAndPath("modid", "block/" + ctx.getName())));
    })
    .simpleItem()
    .register();

Recipe Generation

Basic Recipes

Use the recipe() method in ItemBuilder or BlockBuilder:

java
// Shaped crafting recipe
public static final ItemEntry<Item> CRAFTED_ITEM = REGISTRUM
    .item("crafted_item", Item::new)
    .recipe((ctx, provider) -> {
        ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ctx.get())
            .pattern("ABA")
            .pattern("BCB")
            .pattern("ABA")
            .define('A', Items.IRON_INGOT)
            .define('B', Items.GOLD_INGOT)
            .define('C', Items.DIAMOND)
            .unlockedBy("has_diamond", RegistrumRecipeProvider.has(Items.DIAMOND))
            .save(provider);
    })
    .register();

// Shapeless crafting recipe
public static final ItemEntry<Item> SHAPELESS_ITEM = REGISTRUM
    .item("shapeless_item", Item::new)
    .recipe((ctx, provider) -> {
        ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, ctx.get())
            .requires(Items.IRON_INGOT)
            .requires(Items.GOLD_INGOT)
            .unlockedBy("has_iron", RegistrumRecipeProvider.has(Items.IRON_INGOT))
            .save(provider);
    })
    .register();

// Smelting recipe
public static final ItemEntry<Item> SMELTED_ITEM = REGISTRUM
    .item("smelted_item", Item::new)
    .recipe((ctx, provider) -> {
        SimpleCookingRecipeBuilder.smelting(
            Ingredient.of(Items.RAW_IRON),
            RecipeCategory.MISC,
            ctx.get(),
            0.7F,  // Experience
            200    // Cooking time (ticks)
        )
        .unlockedBy("has_raw_iron", RegistrumRecipeProvider.has(Items.RAW_IRON))
        .save(provider);
    })
    .register();

// Smithing table recipe
public static final ItemEntry<Item> SMITHED_ITEM = REGISTRUM
    .item("smithed_item", Item::new)
    .recipe((ctx, provider) -> {
        SmithingTransformRecipeBuilder.smithing(
            Ingredient.of(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE),
            Ingredient.of(Items.DIAMOND_PICKAXE),
            Ingredient.of(Items.NETHERITE_INGOT),
            RecipeCategory.TOOLS,
            ctx.get()
        )
        .unlocks("has_netherite", RegistrumRecipeProvider.has(Items.NETHERITE_INGOT))
        .save(provider, ResourceLocation.fromNamespaceAndPath("modid", "smithing/custom_tool"));
    })
    .register();

Multiple Recipes

An item can have multiple recipes:

java
public static final ItemEntry<Item> MULTI_RECIPE_ITEM = REGISTRUM
    .item("multi_recipe_item", Item::new)
    .recipe((ctx, provider) -> {
        // Recipe 1: Crafting
        ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ctx.get())
            .pattern("II")
            .pattern("II")
            .define('I', Items.IRON_INGOT)
            .unlockedBy("has_iron", RegistrumRecipeProvider.has(Items.IRON_INGOT))
            .save(provider);

        // Recipe 2: Smelting
        SimpleCookingRecipeBuilder.smelting(
            Ingredient.of(Items.IRON_BLOCK),
            RecipeCategory.MISC,
            ctx.get(),
            1.0F,
            200
        )
        .unlockedBy("has_iron_block", RegistrumRecipeProvider.has(Items.IRON_BLOCK))
        .save(provider, ResourceLocation.fromNamespaceAndPath("modid", "smelting/custom_from_block"));
    })
    .register();

Language File Generation

Automatic Generation

By default, Registrum will automatically generate English names from registration IDs (converting underscores to spaces, capitalizing first letters).

Custom Names

Use the lang() method to customize names:

java
// Item
public static final ItemEntry<Item> CUSTOM_ITEM = REGISTRUM
    .item("custom_item", Item::new)
    .lang("My Custom Item")
    .register();

// Block
public static final BlockEntry<Block> CUSTOM_BLOCK = REGISTRUM
    .block("custom_block", Block::new)
    .lang("My Custom Block")
    .simpleItem()
    .register();

// Entity
public static final EntityEntry<CustomEntity> CUSTOM_ENTITY = REGISTRUM
    .entity("custom_entity", CustomEntity::new, MobCategory.MISC)
    .lang("My Custom Entity")
    .register();

Tag Generation

Item Tags

java
public static final ItemEntry<Item> TAGGED_ITEM = REGISTRUM
    .item("tagged_item", Item::new)
    .tag(ItemTags.AXES)
    .tag(Tags.Items.TOOLS)
    .tag(ItemTags.create(ResourceLocation.fromNamespaceAndPath("c", "tools/custom")))
    .register();

Block Tags

java
public static final BlockEntry<Block> TAGGED_BLOCK = REGISTRUM
    .block("tagged_block", Block::new)
    .tag(BlockTags.MINEABLE_WITH_PICKAXE)
    .tag(BlockTags.NEEDS_IRON_TOOL)
    .tag(Tags.Blocks.ORES)
    .simpleItem()
    .register();

Entity Tags

java
public static final EntityEntry<CustomEntity> TAGGED_ENTITY = REGISTRUM
    .entity("tagged_entity", CustomEntity::new, MobCategory.MISC)
    .tag(EntityTypeTags.ARROWS)
    .register();

Loot Table Generation

Block Loot Tables

java
// Default drops itself
public static final BlockEntry<Block> SIMPLE_LOOT = REGISTRUM
    .block("simple_loot", Block::new)
    .simpleItem()
    .register();

// Custom loot table
public static final BlockEntry<Block> CUSTOM_LOOT = REGISTRUM
    .block("custom_loot", Block::new)
    .loot((tables, block) -> {
        tables.dropSelf(block);  // Drops itself
    })
    .simpleItem()
    .register();

// Using silk touch
public static final BlockEntry<Block> SILK_TOUCH_LOOT = REGISTRUM
    .block("silk_touch_block", Block::new)
    .loot((tables, block) -> {
        tables.add(block, tables.createSilkTouchDispatchTable(
            block,
            LootItem.lootTableItem(Items.DIAMOND)
                .apply(SetItemCountFunction.setCount(UniformGenerator.between(1, 3)))
        ));
    })
    .simpleItem()
    .register();

Entity Loot Tables

java
public static final EntityEntry<CustomMob> MOB_WITH_LOOT = REGISTRUM
    .<CustomMob>entity("custom_mob", CustomMob::new, MobCategory.CREATURE)
    .attributes(CustomMob::createAttributes)
    .loot((tables, entityType) -> {
        tables.add(entityType, LootTable.lootTable()
            .withPool(LootPool.lootPool()
                .setRolls(ConstantValue.exactly(1))
                .add(LootItem.lootTableItem(Items.LEATHER)
                    .apply(SetItemCountFunction.setCount(UniformGenerator.between(0, 2)))
                    .apply(LootingEnchantFunction.lootingMultiplier(
                        UniformGenerator.between(0, 1)))
                )
            )
        );
    })
    .register();

Item Fuel and Composting

Burn Time

java
public static final ItemEntry<Item> FUEL_ITEM = REGISTRUM
    .item("fuel_item", Item::new)
    .burnTime(1600)  // Burn time (ticks), coal is 1600
    .register();

Compost Chance

java
public static final ItemEntry<Item> COMPOST_ITEM = REGISTRUM
    .item("compost_item", Item::new)
    .compostable(0.65F)  // Compost chance, 0.0-1.0
    .register();

Running Data Generation

Run data generation in your development environment:

  1. Use Gradle task: Tasks -> build -> runData
  2. Or run in command line: ./gradlew runData

Generated files will be placed in the src/generated/resources directory.

Best Practices

  1. Maintain Consistency

    • Use a uniform naming style
    • Follow Minecraft's vanilla resource naming conventions
  2. Use Tags

    • Prefer using common tags (c: namespace)
    • Create your own tags for custom content
  3. Recipe Design

    • Ensure recipes have appropriate unlock conditions
    • Use meaningful recipe IDs
  4. Model Optimization

    • Reuse parent models when possible
    • Avoid overly complex custom models
  5. Localization

    • Even with auto-generated names, check if they are appropriate
    • Manually provide translations for non-English languages

Released under the CC-BY-NC-SA 4.0 License.