MultiLoader 26.1+ · Part 6

Data Generation: Block Loot Tables (MultiLoader 26.1+)

INTERMEDIATE MULTILOADER 26.1-26.1.2 15 min read · Jun 12, 2026
MultiLoader 26.1+ · 17 parts
1 Getting Started with MultiLoader 26.1+ 2 Setting Up RegistrationUtils (MultiLoader 26.1+) 3 Creating Items (MultiLoader 26.1+) 4 Creating Blocks (MultiLoader 26.1+) 5 Data Generation: Block & Item Models (MultiLoader 26.1+) 6 Data Generation: Block Loot Tables (MultiLoader 26.1+) 7 Data Generation: Crafting Recipes (MultiLoader 26.1+) 8 Data Generation: Block & Item Tags (MultiLoader 26.1+) 9 Data Generation: Language Files (MultiLoader 26.1+) 10 Data Generation: Advancements (MultiLoader 26.1+) 11 Data Generation: Sound Definitions (MultiLoader 26.1+) 12 Data Generation: Particle Descriptions (MultiLoader 26.1+) 13 Data Generation: Enchantments (MultiLoader 26.1+) 14 Custom Food Items (MultiLoader 26.1+) 15 Custom Tools (MultiLoader 26.1+) 16 Custom Armour (MultiLoader 26.1+) 17 Block Entities (MultiLoader 26.1+)

After the previous tutorial your blocks render correctly in-game, but breaking them drops nothing. Minecraft requires a loot table for each block to know what items to drop. In this tutorial we generate those loot tables automatically using datagen.

NOTE
Complete Data Generation: Block & Item Models before continuing. We introduce a new server-side GatherDataEvent listener alongside the client-side one we already have.

Block Loot Provider

In your NeoForge data package, create a class called ExampleBlockLootTableProvider extending BlockLootSubProvider:

java
public class ExampleBlockLootTableProvider extends BlockLootSubProvider {
protected ExampleBlockLootTableProvider(HolderLookup.Provider registries) {
super(Set.of(), FeatureFlags.REGISTRY.allFlags(), registries);
}
@Override
protected void generate() {
dropSelf(BlockRegistry.NEW_DIRT.get());
}
@Override
protected Iterable<Block> getKnownBlocks() {
return BlockRegistry.BLOCKS.getEntries().stream()
.map(RegistryObject::get)
.collect(Collectors.toList());
}
}

The generate() method is where you define what each block drops. Right now we just call dropSelf so the block drops itself when broken with the correct tool.

getKnownBlocks() tells the generator which blocks this provider is responsible for. Using BlockRegistry.BLOCKS.getEntries() means every block you register will be validated automatically. If you forget to add a loot table for a block, datagen will throw an error rather than silently producing an incomplete jar.

Registering the Provider

Loot tables are server-side resources, so they are registered through GatherDataEvent rather than GatherDataEvent.Client. Add a second listener in your NeoForge mod class constructor:

java
// Inside your NeoForge mod class constructor:
eventBus.addListener(ExampleMod::gatherData);

Then add the static handler method. The loot provider is wrapped inside LootTableProvider, which handles the file writing and validation:

java
public static void gatherData(GatherDataEvent event) {
DataGenerator generator = event.getGenerator();
PackOutput output = generator.getPackOutput();
CompletableFuture<HolderLookup.Provider> registries = event.getLookupProvider();
generator.addProvider(true,
new LootTableProvider(output, Set.of(),
List.of(new LootTableProvider.SubProviderEntry(
ExampleBlockLootTableProvider::new,
LootContextParamSets.BLOCK
)), registries));
}

The CompletableFuture<HolderLookup.Provider> obtained from the event is what the loot provider needs to resolve datapack objects at generation time.

TIP
To add more block loot rules later, add extra calls inside generate(). Common helpers include:
  • dropSelf(block): drops the block itself
  • createOreDrop(block, item): drops an ore's yield, affected by Fortune
  • createSlabItemTable(block): correctly doubles the drop count for slabs

Running Datagen

Run the NeoForge Data configuration. After it finishes, check the common/src/generated/resources/data/examplemod/loot_table/blocks/ folder. You should see a new_dirt.json file that looks like this:

json
{
"type": "minecraft:block",
"pools": [
{
"bonus_rolls": 0.0,
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
],
"entries": [
{
"type": "minecraft:item",
"name": "examplemod:new_dirt"
}
],
"rolls": 1.0
}
]
}
TIP
Note the path uses loot_table (singular), not loot_tables. This is the correct path for 26.1.

Testing In-Game

Launch the client and place a New Dirt block in survival mode. Break it; it should now drop itself. Try breaking it with a Silk Touch pickaxe to confirm the survives_explosion condition does not affect normal drops.

You can find the source for this tutorial here:

View Source on GitHub
NEXT IN SERIES

Data Generation: Crafting Recipes (MultiLoader 26.1+)

Create a RecipeProvider to generate shaped crafting recipes, shapeless recipes, and smelting and blasting recipes via datagen, hooking everything into GatherDataEvent alongside your existing providers.

Continue →