agentsclimarketplace

Fabric datagen

Skill PSB1234/Fabric-Minecraft-Skill-1.20.1/fabric-datagen

Full reference for Fabric data generation in Minecraft 1.20.1 — auto-generating recipes, loot tables, tags, block models, item models, advancement JSON, and language files at build time. Use this skill whenever the user asks about runDatagen, FabricDataGenerator, writing a recipe provider, loot table provider, tag provider, model provider, advancement provider, or any "generate JSON assets programmatically" task. Triggers on: "datagen", "runDatagen", "FabricDataGenerator", "recipe provider", "loot table provider", "tag provider", "model provider", "BlockStateModelGenerator", "advancement provider", or any question about generating Minecraft JSON files via code. Part of the fabric-mc-modding skill family.From its SKILL.md

Install
npx -y skills add PSB1234/Fabric-Minecraft-Skill-1.20.1 --skill fabric-datagen

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

12.9 KB, ~2.8k tokens by cl100k_base, as published. Nobody here has run it

Fabric Data Generation — 1.20.1 Reference


Setup

fabric.mod.json — add datagen entrypoint

{
  "entrypoints": {
    "fabric-datagen": ["com.example.mymod.datagen.MyDataGenerator"]
  }
}

build.gradle — enable data generation

loom {
    runs {
        datagenClient {
            inherit client
            name "Data Generation"
            vmArg "-Dfabric-api.datagen"
            vmArg "-Dfabric-api.datagen.output-dir=${file("src/main/generated")}"
            vmArg "-Dfabric-api.datagen.modid=mymod"
            runDir "build/datagen"
        }
    }
}

// Add generated resources to main source set
sourceSets {
    main {
        resources { srcDirs += ['src/main/generated'] }
    }
}

Run with: ./gradlew runDatagenClient

Root DataGenerator entrypoint

public class MyDataGenerator implements DataGeneratorEntrypoint {
    @Override
    public void onInitializeDataGenerator(FabricDataGenerator gen) {
        FabricDataGenerator.Pack pack = gen.createPack();
        pack.addProvider(MyRecipeProvider::new);
        pack.addProvider(MyLootTableProvider::new);
        pack.addProvider(MyBlockTagProvider::new);
        pack.addProvider(MyItemTagProvider::new);
        pack.addProvider(MyModelProvider::new);
        pack.addProvider(MyAdvancementProvider::new);
        pack.addProvider(MyLangProvider::new);
    }
}

Recipe Provider

public class MyRecipeProvider extends FabricRecipeProvider {

    public MyRecipeProvider(FabricDataOutput output) {
        super(output);
    }

    @Override
    protected void generateRecipes(Consumer<RecipeJsonProvider> exporter) {

        // Shaped crafting
        ShapedRecipeJsonBuilder.create(RecipeCategory.MISC, ModItems.MY_ITEM, 4)
            .pattern("XX")
            .pattern("XX")
            .input('X', Items.DIAMOND)
            .criterion(FabricRecipeProvider.hasItem(Items.DIAMOND),
                FabricRecipeProvider.conditionsFromItem(Items.DIAMOND))
            .offerTo(exporter);

        // Shaped with custom recipe ID
        ShapedRecipeJsonBuilder.create(RecipeCategory.TOOLS, ModItems.MY_SWORD)
            .pattern(" X")
            .pattern(" X")
            .pattern(" S")
            .input('X', ModItems.MY_GEM)
            .input('S', Items.STICK)
            .criterion("has_gem", FabricRecipeProvider.conditionsFromItem(ModItems.MY_GEM))
            .offerTo(exporter, new Identifier("mymod", "my_sword_alt"));

        // Shapeless crafting
        ShapelessRecipeJsonBuilder.create(RecipeCategory.MISC, Items.COAL)
            .input(ModItems.MY_FUEL_BLOCK)
            .criterion("has_fuel", FabricRecipeProvider.conditionsFromItem(ModItems.MY_FUEL_BLOCK))
            .offerTo(exporter);

        // Smelting / Blasting / Smoking / Campfire
        offerSmelting(exporter,
            List.of(ModItems.MY_RAW_ORE),
            RecipeCategory.MISC,
            ModItems.MY_INGOT,
            0.7f,      // XP
            200,       // cook time (ticks)
            "my_ingot" // group
        );

        offerBlasting(exporter,
            List.of(ModItems.MY_RAW_ORE),
            RecipeCategory.MISC,
            ModItems.MY_INGOT,
            0.7f, 100, "my_ingot"
        );

        // Stonecutter
        SingleItemRecipeJsonBuilder.createStonecutting(
            Ingredient.ofItems(ModBlocks.MY_STONE), RecipeCategory.BUILDING_BLOCKS, ModBlocks.MY_STONE_SLAB, 2)
            .criterion("has_stone", FabricRecipeProvider.conditionsFromItem(ModBlocks.MY_STONE))
            .offerTo(exporter, new Identifier("mymod", "my_stone_slab_from_stonecutting"));
    }
}

Loot Table Provider

public class MyLootTableProvider extends SimpleFabricLootTableProvider {

    public MyLootTableProvider(FabricDataOutput output) {
        super(output, LootContextTypes.BLOCK);
    }

    @Override
    public void accept(BiConsumer<Identifier, LootTable.Builder> exporter) {

        // Drop itself when mined with correct tool
        exporter.accept(
            ModBlocks.MY_BLOCK.getLootTableId(),
            LootTable.builder()
                .pool(LootPool.builder()
                    .rolls(ConstantLootNumberProvider.create(1))
                    .with(ItemEntry.builder(ModBlocks.MY_BLOCK))
                    .apply(ExplosionDecayLootFunction.builder())
                )
        );

        // Drop a different item (e.g. ore → raw material)
        exporter.accept(
            ModBlocks.MY_ORE.getLootTableId(),
            LootTable.builder()
                .pool(LootPool.builder()
                    .rolls(ConstantLootNumberProvider.create(1))
                    .with(ItemEntry.builder(ModItems.MY_RAW_ORE)
                        .apply(ApplyBonusLootFunction.oreDrops(Enchantments.FORTUNE))
                    )
                    .apply(ExplosionDecayLootFunction.builder())
                )
        );

        // Drop 1–3 items
        exporter.accept(
            ModBlocks.MY_CROP.getLootTableId(),
            LootTable.builder()
                .pool(LootPool.builder()
                    .rolls(UniformLootNumberProvider.create(1, 3))
                    .with(ItemEntry.builder(ModItems.MY_SEED))
                )
        );
    }
}

Tag Providers

Block Tags

public class MyBlockTagProvider extends FabricTagProvider.BlockTagProvider {

    public MyBlockTagProvider(FabricDataOutput output, CompletableFuture<RegistryWrapper.WrapperLookup> lookup) {
        super(output, lookup);
    }

    @Override
    protected void configure(RegistryWrapper.WrapperLookup lookup) {
        // Add to an existing vanilla/Fabric tag
        getOrCreateTagBuilder(BlockTags.PICKAXE_MINEABLE)
            .add(ModBlocks.MY_BLOCK)
            .add(ModBlocks.MY_ORE);

        getOrCreateTagBuilder(BlockTags.NEEDS_IRON_TOOL)
            .add(ModBlocks.MY_BLOCK);

        // Create a custom tag
        getOrCreateTagBuilder(ModTags.Blocks.MY_SPECIAL_BLOCKS)
            .add(ModBlocks.MY_BLOCK)
            .add(ModBlocks.MY_ORE);
    }
}

Item Tags

public class MyItemTagProvider extends FabricTagProvider.ItemTagProvider {

    public MyItemTagProvider(FabricDataOutput output,
            CompletableFuture<RegistryWrapper.WrapperLookup> lookup,
            BlockTagProvider blockTagProvider) {
        super(output, lookup, blockTagProvider);
    }

    @Override
    protected void configure(RegistryWrapper.WrapperLookup lookup) {
        getOrCreateTagBuilder(ItemTags.TRIMMABLE_ARMOR)
            .add(ModItems.MY_CHESTPLATE);

        // Copy from a block tag (creates matching item tag for block items)
        copy(ModTags.Blocks.MY_SPECIAL_BLOCKS, ModTags.Items.MY_SPECIAL_BLOCKS);
    }
}

Defining Custom Tag Keys

public class ModTags {
    public static class Blocks {
        public static final TagKey<Block> MY_SPECIAL_BLOCKS =
            TagKey.of(RegistryKeys.BLOCK, new Identifier("mymod", "my_special_blocks"));
    }
    public static class Items {
        public static final TagKey<Item> MY_SPECIAL_BLOCKS =
            TagKey.of(RegistryKeys.ITEM, new Identifier("mymod", "my_special_blocks"));
    }
}

Model Provider

public class MyModelProvider extends FabricModelProvider {

    public MyModelProvider(FabricDataOutput output) {
        super(output);
    }

    @Override
    public void generateBlockStateModels(BlockStateModelGenerator gen) {

        // Simple cube-all block (all 6 sides same texture)
        gen.registerSimpleCubeAll(ModBlocks.MY_BLOCK);

        // Cube with separate top/side/bottom
        TextureMap texMap = new TextureMap()
            .put(TextureKey.TOP,    new Identifier("mymod", "block/my_block_top"))
            .put(TextureKey.SIDE,   new Identifier("mymod", "block/my_block_side"))
            .put(TextureKey.BOTTOM, new Identifier("mymod", "block/my_block_bottom"));
        Models.CUBE_BOTTOM_TOP.upload(
            ModelIds.getBlockModelId(ModBlocks.MY_BLOCK), texMap, gen.modelCollector);
        gen.blockStateCollector.accept(
            BlockStateModelGenerator.createSingletonBlockState(
                ModBlocks.MY_BLOCK,
                ModelIds.getBlockModelId(ModBlocks.MY_BLOCK)));

        // Pillar block (e.g. log)
        gen.registerLog(ModBlocks.MY_LOG)
            .log(ModBlocks.MY_LOG)
            .wood(ModBlocks.MY_WOOD);

        // Slab, stairs, wall from a base block
        gen.registerSlab(ModBlocks.MY_SLAB,
            ModelIds.getBlockModelId(ModBlocks.MY_STONE),
            ModelIds.getBlockModelId(ModBlocks.MY_STONE),
            ModelIds.getBlockModelId(ModBlocks.MY_STONE));
        gen.registerStairs(ModBlocks.MY_STAIRS, texMap);
        gen.registerWall(ModBlocks.MY_WALL, texMap);
    }

    @Override
    public void generateItemModels(ItemModelGenerator gen) {

        // Flat sprite item (sword, food, etc.)
        gen.register(ModItems.MY_ITEM, Models.GENERATED);

        // Handheld item (tools)
        gen.register(ModItems.MY_SWORD, Models.HANDHELD);

        // Item that uses its block model
        // (block items registered by registerSimpleCubeAll don't need this)
    }
}

Advancement Provider

public class MyAdvancementProvider implements FabricAdvancementProvider {

    public MyAdvancementProvider(FabricDataOutput output) { }

    @Override
    public void generateAdvancement(Consumer<AdvancementEntry> consumer) {

        AdvancementEntry root = Advancement.Builder.create()
            .display(ModItems.MY_ITEM,
                Text.translatable("advancements.mymod.root.title"),
                Text.translatable("advancements.mymod.root.description"),
                new Identifier("textures/gui/advancements/backgrounds/adventure.png"),
                AdvancementFrame.TASK, false, false, false)
            .criterion("has_item",
                InventoryChangedCriterion.Conditions.items(ModItems.MY_ITEM))
            .build(consumer, "mymod/root");

        Advancement.Builder.create()
            .parent(root)
            .display(ModItems.MY_SPECIAL_ITEM,
                Text.translatable("advancements.mymod.special.title"),
                Text.translatable("advancements.mymod.special.description"),
                null,  // no background for non-root
                AdvancementFrame.CHALLENGE, true, true, false)
            .criterion("has_special",
                InventoryChangedCriterion.Conditions.items(ModItems.MY_SPECIAL_ITEM))
            .rewards(AdvancementRewards.Builder.experience(100))
            .build(consumer, "mymod/special");
    }
}

Language File Provider

public class MyLangProvider extends FabricLanguageProvider {

    public MyLangProvider(FabricDataOutput output) {
        super(output, "en_us");
    }

    @Override
    public void generateTranslations(TranslationBuilder builder) {
        builder.add(ModBlocks.MY_BLOCK, "My Block");
        builder.add(ModItems.MY_ITEM,   "My Item");
        builder.add(ModItems.MY_SWORD,  "My Sword");
        builder.add("advancements.mymod.root.title",       "My Mod");
        builder.add("advancements.mymod.root.description", "Start using My Mod");
        builder.add("itemGroup.mymod.main",                "My Mod");
    }
}

Common Mistakes

  • Missing criterion in a recipe builder — every recipe must have at least one criterion (for recipe unlock). Use hasItem() for the key ingredient.
  • Forgetting to add provider to createPack() — adding pack.addProvider(...) is what registers it.
  • Tag provider constructor mismatchBlockTagProvider and ItemTagProvider take a CompletableFuture<RegistryWrapper.WrapperLookup> second arg in 1.20.1.
  • Running runDatagen without the entrypoint — add "fabric-datagen" entrypoint to fabric.mod.json.
  • Generated files not included — add src/main/generated to sourceSets.main.resources.srcDirs.

Further Reading

Related Skills

  • fabric-mc-modding — project setup, registering blocks/items
  • minecraft-json-assets — manual JSON format reference (when not using datagen)
  • fabric-blocks — block definitions that datagen generates assets for

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,861. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.