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
npx -y skills add PSB1234/Fabric-Minecraft-Skill-1.20.1 --skill fabric-datagenAssembled 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). UsehasItem()for the key ingredient. - Forgetting to add provider to
createPack()— addingpack.addProvider(...)is what registers it. - Tag provider constructor mismatch —
BlockTagProviderandItemTagProvidertake aCompletableFuture<RegistryWrapper.WrapperLookup>second arg in 1.20.1. - Running
runDatagenwithout the entrypoint — add"fabric-datagen"entrypoint tofabric.mod.json. - Generated files not included — add
src/main/generatedtosourceSets.main.resources.srcDirs.
Further Reading
- Fabric datagen guide: https://docs.fabricmc.net/develop/data-generation/setup
- FabricRecipeProvider source: https://github.com/FabricMC/fabric/tree/1.20.1/fabric-data-generation-api-v1
Related Skills
fabric-mc-modding— project setup, registering blocks/itemsminecraft-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.