Sift blocks through a sieve to get drops. The sieve is data-driven with what can be sieved and what it drops are controlled by tags and loot tables for easy use in modpacks or other mods.
Features
- A craftable sieve block
- 6 craftable mesh tiers
- String
- Copper
- Iron
- Gold
- Diamond
- Netherite
- Each mesh has its own loot table, so better meshes can give better drops
- Fully configurable through tags and loot tables
- Several fun easter eggs
How to use
- Place the sieve
- Right-click it with a mesh to install it. Right-click with another mesh to swap
- Right-click it with a sieveable block
- Keep right-clicking to sift
- Repeat
Upcoming
- Better Data-driven usages
- Less restrictive namespace pathways

For modpack and mod developers
Le Sieve uses two tags and one loot table path. Everything lives in the le-sieve namespace.
1. What can be sieved: le-sieve:sieveable (block tag)
Add any block to this tag and its item can be loaded into the sieve.
data/le-sieve/tags/block/sieveable.json
{
"values": [
"minecraft:dirt",
"minecraft:gravel"
]
}
Only blocks that have an item will work
2. Which items work as meshes: le-sieve:sieve_meshes (item tag)
Built-in meshes are in this tag by default.
data/le-sieve/tags/item/sieve_meshes.json
{
"values": [
"le-sieve:string_mesh",
"le-sieve:iron_mesh"
]
}
The tag decides what can be inserted. The mesh shown in the sieve comes from a custom item class, so a mesh from another mod needs to be built on SieveMeshItem to render correctly. Other items in the tag will be stored but won't render.
3. What it drops: sieve loot tables
For each sieveable block, add a loot table at:
data/le-sieve/loot_table/sieve/<block_id>.json
<block_id> is the path of the block's item id with no namespace. Dirt is dirt.json, so minecraft:dirt uses data/le-sieve/loot_table/sieve/dirt.json.
Rules:
- The table type must be
minecraft:block. Any other type is ignored and the block is consumed with no drops. - The mesh used is passed as the tool. Use a
minecraft:match_toolcondition on each pool to give each mesh its own drops. - A missing or empty table is logged and the material is consumed without dropping anything.
- The table is always looked up in the
le-sievenamespace using only the item's path. Two blocks from different mods with the same path would share a table.
Example: dirt with the string mesh drops a stick, leather, or nothing (weights 30 / 30 / 5).
{
"type": "minecraft:block",
"pools": [
{
"condition": {
"type": "minecraft:match_tool",
"predicate": {
"items": "le-sieve:string_mesh"
}
},
"entries": [
{
"type": "minecraft:item",
"modifier": {
"type": "minecraft:set_count",
"count": {
"type": "minecraft:uniform",
"max": 1,
"min": 1
}
},
"name": "minecraft:stick",
"weight": 30
},
{
"type": "minecraft:item",
"modifier": {
"type": "minecraft:set_count",
"count": {
"type": "minecraft:uniform",
"max": 1,
"min": 1
}
},
"name": "minecraft:leather",
"weight": 30
},
{
"type": "minecraft:empty",
"weight": 5
}
],
"rolls": 1
}
]
}
Add one pool per mesh. Pools whose condition doesn't match the installed mesh are skipped. To make a pool apply to several meshes, match on the tag instead.
Generating loot tables with datagen
If you use Fabric datagen, you don't need to write the JSON by hand. The mod's SieveLootTableProvider includes public static helpers that you can reuse from your own provider
| Helper | What it does |
|---|---|
sieveDrop(item, weight) |
A drop of exactly 1 item at the given weight |
sieveDrop(item, weight, min, max) |
A drop with a random count between min and max |
sieveEmpty() |
A "nothing drops" entry at weight 5 |
sieveEmpty(weight) |
A "nothing drops" entry at a custom weight |
Each helper returns a LootPoolEntryContainer.Builder<?> that you pass to a pool's .add(...).
Example: a gravel table where the iron mesh drops flint (sometimes 2) or nothing, and the gold mesh drops flint or gold nuggets.
biConsumer.accept(ResourceKey.create(Registries.LOOT_TABLE, LeSieveMod.Id("sieve/gravel")), GravelBuilder(itemLookUp));
private LootTable.@NotNull Builder GravelBuilder(HolderLookup.RegistryLookup<Item> itemLookUp) {
LootTable.Builder builder = LootTable.lootTable().setParamSet(LootContextParamSets.BLOCK).withPool(LootPool.lootPool().setRolls(ContextIntProviders.exactly(1))
.add(sieveDrop(Items.FLINT, 40, 1, 2))
.add(sieveEmpty(10))
.when(MatchTool.toolMatches(ItemPredicate.Builder.item().of(itemLookUp, LeSieveItems.IRON_MESH))));
builder.withPool(LootPool.lootPool().setRolls(ContextIntProviders.exactly(1))
.add(sieveDrop(Items.FLINT, 30))
.add(sieveDrop(Items.GOLD_NUGGET, 10))
.add(sieveEmpty())
.when(MatchTool.toolMatches(ItemPredicate.Builder.item().of(itemLookUp, LeSieveItems.GOLD_MESH))));
return builder;
}
Things to remember:
- The loot table key must be
le-sieve:sieve/<item_path>. The sieve looks it up in that namespace, and the file is generated underdata/le-sieve/loot_table/sieve/. - The table's parameter set must be
LootContextParamSets.BLOCK. - Match the mesh with
MatchTooland the mesh item. The pool applies only when that mesh is installed.