Chapter 16 — Loot Tables: Pools, Entries, and Weights
What You’ll Build
Every time a chest in a dungeon hands you a surprise, a zombie drops rotten flesh, or your
fishing rod hooks an enchanted book, Minecraft is reading a loot table, a JSON file that
decides which items appear in that situation. In this chapter you’ll learn how a loot table is
put together, and then you’ll write one of your own: a treasure chest for your mypack pack that
almost always gives a common item but, once in a while, drops something rare.
By the end you’ll be able to read and write the three nested pieces every loot table is built from (pools, a group of possible drops; entries, the individual things that can drop; and weight, the dial that makes some entries rarer than others), and you’ll know how to make a count exact, random within a range, or random-but-clustered using a number provider.
This chapter leans on the registry tags you learned in Chapter 14 (those #namespace:path
groups of item types, like #minecraft:planks) because a loot table can drop a whole tag at
once. It extends the mypack pack you started in Chapter 9 and uses the test world from Chapter 1.
Loot tables can do even more than choosing items: they can attach conditions (“only if killed by a
player”) and run functions (“set the count to 3, add an enchantment”). Those two powers are big
enough to get their own chapter, so we’ll preview them at the end and teach them properly in
Chapter 17. This chapter is about getting the structure rock-solid first.
What a loot table is, and what it controls
A loot table is a technical JSON file used to dictate what items should generate in various situations, such as what items should be in naturally generated containers, what items should drop when breaking a block or killing a mob, what items can be fished, and more. One file, asked over and over, “what items come out this time?”
The “situation” a loot table runs in has a name: it’s called the loot context. You don’t have to memorize the list, but it’s worth seeing how many corners of the game loot tables quietly run. A loot table can be invoked for:
- Container contents: opening a barrel, chest, trapped chest, hopper, minecart with chest, boat with chest, minecart with hopper, dispenser, dropper, shulker box, dyed shulker box, or decorated pot.
- Mob drops: the loot from a living entity’s death.
- Block drops: the items dropped when mining a block (or when a block is exploded).
- Fishing: the items obtained via fishing.
- Gifts: a gift from a cat, villager, or sniffer, and the item laid by a chicken.
- Archaeology: using a brush on suspicious sand or suspicious gravel that has a loot table.
There are more (bartering with piglins, shearing certain mobs, vault loot, advancement rewards), but those six cover the situations a beginner meets first. The important takeaway: a chest’s loot and a zombie’s drops are data, and data packs can change or add them.
Modern Minecraft A few drops are not loot tables. Loot tables do not affect dropped experience, and don’t cover dropped non-item entities such as slimes from larger slimes or silverfish from infested blocks. Some unbreakable blocks (bedrock, end portals) have no loot table at all, and a few drops like the wither’s nether star are handled specially. If a drop seems to ignore your loot table, it may be one of these exceptions.
Where loot tables live
Like every other kind of file in a data pack, a loot table lives in a fixed folder. Loot tables
are defined using JSON files stored within a data pack in the path
data/<namespace>/loot_table.
So inside your mypack pack, your loot tables go here:
mypack/data/mypack/loot_table/
Just like the function, recipe, and tags/function folders you’ve already used, the folder
name is singular: loot_table, not loot_tables. (Older tutorials from before the great
folder-renaming will show you the plural. It won’t work today. See “What Can Go Wrong.”)
The shape of a loot table: pools
The top level of a loot table file is a JSON object. The field that does the real work is pools,
a list of all pools for this loot table. Pools are applied in order.
A pool is one group of possible drops. Think of a pool as a single bag you reach into. A loot table can have several bags (several pools), and when the loot table runs, it reaches into every bag. Here’s the smallest useful loot table: one pool, holding one possible drop.
mypack/data/mypack/loot_table/treasure_chest.json
{
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "minecraft:bread"
}
]
}
]
}
Notice pools is an array (square brackets) because there can be more than one pool, and inside
it each pool is its own object. Two fields appear in that pool, and they’re the heart of this whole
chapter: rolls and entries.
Rolls: how many times you reach in
The rolls field is a number provider that specifies the number of
rolls on the pool. A roll is one draw: in each roll of a pool, the pool draws one entry from
all its entries, and each roll of a pool is independent. So "rolls": 1 means reach into this bag
once and pull out one entry. "rolls": 3 would reach in three separate times, and because each
roll is independent, you could pull the same entry more than once.
For now we’re writing rolls as a plain number. Later in the chapter you’ll see that rolls can
also be a random count, which is where number providers come in.
Entries: what’s in the bag
The entries field is the list of things that could come out of a pool. One loot
entry is chosen per roll as a weighted random selection from all loot entries in the pool. So each
roll picks exactly one entry from this list. Get the list right and you control everything the pool
can produce.
The four entry types
Every entry has a type field, a resource location naming what kind of entry it is. There are
several types; this chapter uses the four most useful ones. Each is shown below as a complete entry
object.
item — drop one specific item. It drops a single item stack (the default is a
stack of 1 of the item). It needs a name field, the resource location of the item:
{
"type": "minecraft:item",
"name": "minecraft:diamond"
}
tag — drop items from a registry tag. This is where Chapter 14 pays off. The tag entry’s
name is “the resource location of the item tag to query, e.g. minecraft:arrows.” It has a
second field, expand, a true/false switch:
{
"type": "minecraft:tag",
"name": "minecraft:arrows",
"expand": false
}
Here’s what the switch does precisely. With "expand": false it is a singleton entry that
drops all items in the tag: pick this entry on a roll and you get one of each item in the tag.
With "expand": true it provides one singleton entry per item in the tag with the same weight,
so the tag fans out and the roll picks just one item from the tag at random. For a treasure chest you
almost always want true, so each roll gives a single random arrow type rather than every arrow at
once.
loot_table — drop the result of another loot table. This lets you reuse a table inside
another. Its field is value, the loot table to be used. Point it at one of your own tables,
for example a mypack:rare_drops table you’ve written in the same loot_table folder:
{
"type": "minecraft:loot_table",
"value": "mypack:rare_drops"
}
One rule: the value cannot be the ID of the current loot table file. Recursive calling is not allowed, so a table can’t include itself, or you’d get an infinite loop.
empty — drop nothing. It drops nothing, and takes no extra fields:
{
"type": "minecraft:empty"
}
An empty entry sounds pointless, but it’s the secret to making a pool sometimes give nothing,
which you’ll use in a moment.
Weight: making some entries rarer
If a pool has several entries, how does it choose between them on each roll? By weight. Every
item (and tag) entry can carry a weight field. It determines how
often this singleton entry is chosen out of all the singleton entries in the pool: entries with
higher weights are used more often. If you leave weight off, it defaults to 1.
The exact rule is a simple fraction:
The chance of an entry being chosen is [this entry’s weight ÷ total weight of all entries in the pool].
So weight is a share, not a percentage. Suppose a pool has three entries with weights 10, 1, and
- The total weight is 12, so the first entry is chosen 10⁄12 of the time (about 83%), and the other two are 1⁄12 each (about 8%). Want something rarer? Give it a smaller weight relative to the others. Here’s a pool that hands out bread most of the time and a diamond rarely:
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "minecraft:bread",
"weight": 20
},
{
"type": "minecraft:item",
"name": "minecraft:diamond",
"weight": 1
}
]
}
The total weight is 21, so each roll gives bread 20⁄21 of the time and a diamond 1⁄21 of the time, roughly a 1-in-21 treasure.
Try It! Add a third entry to that pool, an
emptyentry with"weight": 9. Now the totals are bread 20, empty 9, diamond 1 (total 30), so a roll gives nothing 9⁄30 (about 30%) of the time. Mixing inemptyis exactly how vanilla chests leave some slots blank.
Number providers: exact, range, or clustered
Back to rolls. So far it’s been a plain number like 1. But rolls is a
number provider, the game’s flexible way to supply a number that can
be fixed or random. There are three you’ll reach for, plus a couple of advanced ones we’ll
save for later.
constant — an exact value. This is what a plain number already is. The long form names a
type and gives the exact number in a value field:
{
"type": "minecraft:constant",
"value": 3
}
Writing "rolls": 3 is just the shorthand for this. Most of the time the shorthand is all you need.
uniform — a random number in a range. This is a random number following a uniform
distribution between two values (inclusive). It takes a min and a max, and every number in
between is equally likely:
{
"type": "minecraft:uniform",
"min": 1,
"max": 4
}
Use this for "rolls" when you want a chest to hold a varying number of drops (here, anywhere
from 1 to 4). There’s also a handy shorthand: a bare { "min": ..., "max": ... } object
(with no type) is automatically treated as a uniform distribution.
binomial — a clustered random number. This is a random number following a binomial
distribution. Instead of min/max, it takes n (the amount of trials) and p (the
probability of success on an individual trial):
{
"type": "minecraft:binomial",
"n": 10,
"p": 0.5
}
You can read this as “flip n coins, each landing heads with probability p, and count the
heads.” With n of 10 and p of 0.5 you’ll usually get a number near 5, only rarely 0 or 10. Use
uniform when every count is equally likely; use binomial when you want results to cluster
around a middle value.
Under the Hood Number providers show up in more places than
rolls, and there are more types than these three. There are alsoscore(read a scoreboard value),storage(read a value from command storage),enchantment_level, andsum. You won’t need them for a basic loot table; we’ll meetscoreand friends when they matter. (Skippable.)
Walkthrough: a weighted treasure chest
Let’s pull it all together into one real file for your pack. Open your mypack pack and create the
folder mypack/data/mypack/loot_table/ if it isn’t there yet. Inside it, make a file called
treasure_chest.json with this content:
mypack/data/mypack/loot_table/treasure_chest.json
{
"pools": [
{
"rolls": {
"type": "minecraft:uniform",
"min": 2,
"max": 4
},
"entries": [
{
"type": "minecraft:item",
"name": "minecraft:bread",
"weight": 20
},
{
"type": "minecraft:item",
"name": "minecraft:iron_ingot",
"weight": 6
},
{
"type": "minecraft:item",
"name": "minecraft:diamond",
"weight": 1
},
{
"type": "minecraft:empty",
"weight": 5
}
]
}
]
}
Walking through it: there’s one pool. Its rolls is a uniform number provider from 2 to 4, so
each time the chest is filled the game reaches into the bag 2, 3, or 4 times. Each reach picks one
entry by weight. The total weight is 20 + 6 + 1 + 5 = 32, so a single roll gives bread 20⁄32 of the
time, an iron ingot 6⁄32, a diamond 1⁄32, and nothing (empty) 5⁄32. A diamond is genuinely rare,
but over 2 to 4 rolls per chest you’ll see one now and then.
Save the file and run /reload in your test world to load it. Now you need to point a chest at the
table. The cleanest way to test a loot table without setting up structures is the /loot command,
which simply runs a loot table and hands you the result. Type this in your chat bar:
/loot give @s loot mypack:treasure_chest
That asks the game to roll your mypack:treasure_chest table and place the results in your own
inventory. Run it a few times. You should mostly get bread and iron, with the occasional diamond,
and sometimes fewer items than the maximum because of the empty entries and the random rolls.
Figure (to be captured). inventory after running /loot give @s loot mypack:treasure_chest several times, showing mostly bread and iron with one diamond
Under the Hood When a real chest is placed with a loot table attached, the table and a seed are stored on the container as data, and the actual items aren’t generated until there is an interaction with the container (e.g. opening or destroying). That’s why two chests with the same table and seed give identical loot, and why
/lootis the quick way to test, since it rolls the table immediately. (Skippable.)
Practice: extend the treasure chest
Now make the table your own. Each of these builds directly on the file above.
-
Add a tag entry. Give the rare slot more variety: replace the single diamond entry with a
tagentry that drops a random arrow fromminecraft:arrows, using"expand": trueso each roll yields just one arrow type:{ "type": "minecraft:tag", "name": "minecraft:arrows", "expand": true, "weight": 1 }(You can use any item tag you learned to write in Chapter 14, including one of your own.)
-
Add a second pool. Remember the loot table runs every pool. Add a second pool that always gives exactly one “guaranteed” reward, separate from the random bag above.
{ "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "minecraft:emerald" } ] }Add this as a second element of the top-level
poolsarray (after the first pool’s closing brace, separated by a comma). Now every chest is guaranteed one emerald plus the 2–4 random draws from the first pool. -
Try
binomialrolls. Change the second pool’srollsfrom1to abinomialprovider with"n": 3and"p": 0.5. Run/loot give @s loot mypack:treasure_chesta dozen times and watch how the emerald count clusters around 1–2 rather than spreading evenly.
After each change, save and /reload, then test with /loot.
What Can Go Wrong
You used the folder name loot_tables (plural). Modern Minecraft data-pack folders are all
singular, so the game looks for loot_table and ignores a loot_tables folder entirely. If
/loot give @s loot mypack:treasure_chest says the table is unknown, check the folder name first.
An item entry has no name. An item entry without its name field has nothing to drop and
the file won’t load correctly. Every item and tag entry needs a name; only empty (and the
number-provider snippets) skip it.
You expected weight to be a percentage. Weight is a share of the total, not a percent. An
entry with "weight": 10 isn’t a 10% chance unless the weights happen to total 100. Always compute
the chance as weight ÷ total weight of all entries in that pool.
Nothing dropped and you think it’s broken. Remember rolls can be random and you may have an
empty entry in the mix, so a single run legitimately producing few or zero items is normal. Run
/loot several times before deciding something’s wrong.
Preview: conditions and functions (Chapter 17)
The loot tables in this chapter are pure structure: pools, entries, weights, counts. But you may
have noticed two optional fields that keep appearing on pools and entries: conditions
and functions. They’re the next two superpowers, and Chapter 17 is devoted to them.
A conditions list is a set of tests that must all pass for a pool or entry to be used (for
example, only drop this if the mob was killed by a player). A functions list applies item
modifiers onto all item stacks dropped, for example set the dropped count to 3, or add a
random enchantment. Here’s a taste (don’t worry about the details yet, this is a Chapter 17
listing):
{
"type": "minecraft:item",
"name": "minecraft:diamond",
"weight": 1,
"functions": [
{
"function": "minecraft:set_count",
"count": 2
}
]
}
That entry would drop two diamonds instead of one. You’ll learn set_count, enchanting,
custom names, and the condition system in the next chapter. For now, you already own the part
everything else builds on: you can decide what can drop and how often.
What You Know Now
You can write a loot table from scratch. You know it’s a JSON file in
data/<namespace>/loot_table/, that it’s built from pools, that each pool draws one entry
per roll, and that weight sets how often each entry wins. You can drop a single item, a
whole tag of items, the result of another loot_table, or empty for nothing. And you can make
a count exact with constant, random with uniform, or clustered with binomial. You built a
weighted treasure chest in mypack and tested it with /loot. Next chapter, you’ll teach those
drops to react to who triggered them and to come out modified — conditions and functions.