Chapter 18 — Predicates: Reusable Conditions
What You’ll Build
Back in Chapter 17 you wrote conditions on loot pools — little “only if…” tests like
random_chance or match_tool that decided whether an entry dropped. Those tests were trapped
inside one loot table. This chapter sets them free. You’ll learn that the very same condition
vocabulary can be saved on its own, in a small JSON file called a predicate, and then reused
anywhere the game accepts a yes/no check: in commands, in target selectors, and back inside loot
tables and advancements.
By the end you’ll have added a predicate named mypack:raining_dark_forest to the pack you started
in Chapter 9, plus a function that hands out effects to every player standing in a rainy dark forest,
and nowhere else. You’ll also finally use execute if predicate and the predicate= selector
argument, both of which were named-but-deferred all the way back in Chapters 3 and 4.
This chapter assumes you’re comfortable with /execute and its if/run pieces and chaining
(Chapter 4), with target selectors like @a and @e and their square-bracket arguments
(Chapter 3), and with the idea of loot-table conditions from Chapter 17, since predicates use the exact
same condition types. It also leans on JSON objects, arrays, and booleans (Chapter 8) and the
data/<namespace>/... folder layout (Chapters 8–9).
Modern Minecraft Older tutorials wrote the same
entity[...]-style checks over and over, copied between commands. Modern data packs write the check once as a predicate and reference it by name. If the rule changes, you edit one file. Predicates are also the only way to express conditions too complex for the selector syntax: “is this entity holding an enchanted diamond sword in a dark forest while it rains” is a predicate, not a string of selector arguments.
What a predicate is
A predicate is a JSON structure the game invokes to check a condition within the world. It returns a pass or fail result to whatever invoked it, which then acts differently based on the result. That’s the whole idea: a predicate is a named yes/no question the game can ask about the world. Put another way, predicates are a flexible way for data packs to encode “if this, then that” logic without needing custom code.
A predicate file is a standalone data pack file containing one or more predicates. Like every other data pack file you’ve made, it lives at a fixed address. Predicate files go in:
data/<namespace>/predicate/
So a predicate you call mypack:is_raining is the file
data/mypack/predicate/is_raining.json. The name follows the same namespace:path rule as your
functions and recipes.
Inside the file is a JSON object with one required field, condition, and then extra fields that
depend on which condition you chose. The root has a condition string (the resource location of
the condition type to check) plus the other parts of the predicate specific to that type. The
condition value is an identifier just like minecraft:say
or minecraft:diamond; for built-in conditions you can write it with or without the minecraft:
prefix, and the examples in this chapter spell it out in full so there’s never any doubt.
Here is the smallest useful predicate — “is it raining right now?”:
data/mypack/predicate/is_raining.json
{
"condition": "minecraft:weather_check",
"raining": true
}
The condition field picks the kind of check (weather_check), and raining is the extra field
that check needs. When the game asks this predicate its question, it answers true (pass) only when
it’s raining, and false (fail) otherwise. That’s exactly what weather_check does: it checks the
current game weather, with a boolean raining that passes only if it is raining or thundering, and a
second boolean thundering that passes only if it is thundering.
Under the Hood (skippable): loot context Some conditions need a fact about the world to do their job: a position, a tool, an entity. This bundle of facts is called the loot context, a set of parameters available to loot tables, predicates, item modifiers, and number providers. Whoever invokes a predicate supplies a context. What happens when a needed fact is missing is clear-cut: a condition like
location_checkrequires an origin provided by loot context, and always fails if it isn’t provided. That’s why some predicates work in one place and silently fail in another. Keep it in mind for “What Can Go Wrong” at the end of the chapter. Conditions marked “invokable from any context” (likeweather_check,time_check,random_chance) work everywhere.
Single-condition predicates
Let’s build a small library of one-condition predicates before combining them. Each is a complete file; each uses a standard built-in condition type.
Time of day — time_check
time_check compares the current day time against given values and is invokable from any context.
It takes a value (the time to compare against) and an optional period. This
predicate passes during the first half of the day:
data/mypack/predicate/is_daytime.json
{
"condition": "minecraft:time_check",
"value": {
"min": 0,
"max": 12000
},
"period": 24000
}
A Minecraft day is 24,000 ticks long (you met that number in Chapter 7). The period field first
reduces the time modulo the given number before it’s checked. Setting it to 24000 causes the
checked time to be equal to the current daytime, so the comparison resets each day instead of
climbing forever. The value here is a min/max range, the long form; there’s also a shorthand
where value is a single integer.
A coin flip — random_chance
random_chance generates a random number between 0.0 and 1.0 and checks if it is less than a
specified value. Its one field is chance, a success rate as a number from 0.0 to 1.0. This one
passes a quarter of the time:
data/mypack/predicate/one_in_four.json
{
"condition": "minecraft:random_chance",
"chance": 0.25
}
You met random_chance as a loot condition in Chapter 17. It is the same condition type, and
that’s the whole point of predicates. Anything you learned as a loot condition is also a predicate.
Checking an entity — entity_properties
The condition you’ll reach for most often is entity_properties. It checks properties of an entity and is
invokable from any context. It has two fields: entity (which entity to look at) and predicate
(the actual test, which uses the same structure as advancements). The simplest entity to name is
"this", the entity the predicate is being asked about.
This predicate passes when the entity is holding a diamond sword in its main hand:
data/mypack/predicate/holding_diamond_sword.json
{
"condition": "minecraft:entity_properties",
"entity": "this",
"predicate": {
"equipment": {
"mainhand": {
"items": ["minecraft:diamond_sword"]
}
}
}
}
Reading it inside-out: equipment is a field for testing the items that this entity holds in its
equipment slots, with one key per slot (the valid keys are mainhand, offhand, head, chest,
legs, feet, and body). Each slot holds an item test, and item conditions include an items
field, a list of item IDs that tests if the type of item matches any of the listed values. So this
whole structure reads: “the item in this entity’s main hand is a diamond sword.”
Checking the block underfoot — block_state_property
block_state_property checks the mined block and its block states, and it requires a block state
provided by loot context, always failing if that isn’t provided. It has a block field (a block
ID; the test fails if the block doesn’t match) and an optional properties map of block state names
to values. This one passes when the block is an
oak log laid on its side along the X axis (you met block states with the Debug Stick in Chapter 10):
data/mypack/predicate/sideways_oak_log.json
{
"condition": "minecraft:block_state_property",
"block": "minecraft:oak_log",
"properties": {
"axis": "x"
}
}
Because this condition needs a block-state context, it works inside a block’s loot table or a
mine context, not from a bare /execute if predicate standing in open air, which supplies no
block. We’ll come back to that in “What Can Go Wrong.”
Checking the held tool — match_tool
match_tool checks the tool used to mine the block and, like block_state_property, requires a
tool provided by loot context, always failing if it isn’t provided. Its single field is
predicate, an item test using the same structure as advancements (the same item test you saw
inside equipment above):
data/mypack/predicate/mined_with_netherite_pickaxe.json
{
"condition": "minecraft:match_tool",
"predicate": {
"items": ["minecraft:netherite_pickaxe"]
}
}
This is the predicate form of the loot condition you used in Chapter 17 to check what tool broke a block. Saved as a file, you can now reuse it across every loot table that wants “only when mined with a netherite pickaxe.”
Checking an active enchantment — enchantment_active_check
The last single condition is a specialist. enchantment_active_check checks if the enchantment has
been active, needs the enchantment-active-status context, and is therefore only usable from the
enchanted_location loot context. Its one field is a boolean active, whether to check for an
active (true) or inactive (false) enchantment:
data/mypack/predicate/enchant_is_active.json
{
"condition": "minecraft:enchantment_active_check",
"active": true
}
You won’t call this one from a command. It only makes sense deep inside an enchantment’s own effects, which is a Chapter 35 topic. It’s listed here so you recognize it as a predicate condition and know it belongs to enchantments specifically.
Reference box: the other condition types There are more condition types than this chapter drills. You don’t need them yet, but here’s the full menu so nothing surprises you later:
location_check(covered below),damage_source_properties,entity_scores,killed_by_player,survives_explosion,table_bonus,random_chance_with_enchanted_bonus,value_check, andreference(covered at the end of this chapter). Each one is aconditionvalue just like the ones above. Many belong to loot and advancement contexts you’ll meet in Chapters 17, 19, and 36.
Invoking a predicate
A predicate that no one asks is useless. Predicate files can be invoked in several different manners from other data pack files.
From a command, with /execute if predicate. The /execute if predicate subcommand can invoke
a predicate file or an in-line predicate definition to decide whether to continue with a subcommand
chain. The predicate is invoked once at the current contextual position of execution. It slots into
the if/run chain you learned in Chapter 4. For example:
execute if predicate mypack:is_daytime run say Good morning!
(As always in this book, that line lives inside a .mcfunction file, with no leading slash.) The
say runs only on the days the predicate passes.
From a target selector, with predicate=. This is the argument that was previewed but deferred
back in Chapter 3. The selector argument predicate= checks predicate files as a filter for entity
selection. The predicate file is invoked once per entity that needs filtering, each time at the
entity’s location. So this selects every player holding a diamond sword, using the predicate file
from earlier:
execute as @a[predicate=mypack:holding_diamond_sword] run say You're armed!
Each player is tested in turn; only those who pass are selected. Like every selector argument, you
can combine predicate= with the others you know — @a[predicate=mypack:holding_diamond_sword, distance=..10].
From inside loot tables and advancements. Predicates are also used in other locations within
other data pack files such as advancements and loot tables. In a loot table, the conditions list
you wrote in Chapter 17 is a list of predicates: a pool’s conditions are a list of predicates
that must all pass for this pool to be used. So a condition you tested inline
in Chapter 17 can instead be saved as a predicate file and pulled in by name, which is the next
topic.
Combining conditions: all_of, any_of, inverted
Single checks are handy, but real rules combine them: “raining and in a dark forest,” “diamond sword or netherite sword,” “not daytime.” There are three combiner condition types for exactly this.
all_of — AND. Evaluates a list of predicates and passes if all of them pass. Its field is
terms, the list of predicates to evaluate.
any_of — OR. Evaluates a list of predicates and passes if any one of them passes. It also uses
a terms list.
inverted — NOT. Inverts another predicate condition. Its field is term (a single predicate,
not a list): the condition to be negated.
Each term inside a combiner is itself a full predicate object, the same {"condition": ...} shape,
nested as deeply as you like. Here’s “not daytime,”
combining inverted with the is_daytime test from earlier, written inline:
data/mypack/predicate/is_nighttime.json
{
"condition": "minecraft:inverted",
"term": {
"condition": "minecraft:time_check",
"value": {
"min": 0,
"max": 12000
},
"period": 24000
}
}
Under the Hood (skippable): the list shortcut A predicate file’s root can be either a compound or a list containing multiple predicates. In the latter case all predicates must evaluate to true. So a JSON array at the top of a predicate file is a built-in
all_of. One catch withany_of:any_ofonly applies toterms; nested lists areall_of. When in doubt, write the combiner out explicitly. It’s clearer to a reader and never surprises you.
Building raining_dark_forest
Now the chapter’s goal: a predicate that passes only when it’s raining and the location is a
dark forest. That’s an all_of of two conditions. The first you already have: weather_check with
raining: true. The second is location_check.
location_check checks the current location against location criteria, requires the origin
context, and has a predicate field whose body uses the same structure as advancements. In that
advancement location structure, the biome is tested with a biomes field (the biome at this
location), written as a list of biome IDs. The dark forest (the dense, dark-oak biome where woodland
mansions generate) has the identifier minecraft:dark_forest, one entry in the full biome ID table
Chapter 42 puts to work. Putting it together:
data/mypack/predicate/raining_dark_forest.json
{
"condition": "minecraft:all_of",
"terms": [
{
"condition": "minecraft:weather_check",
"raining": true
},
{
"condition": "minecraft:location_check",
"predicate": {
"biomes": ["minecraft:dark_forest"]
}
}
]
}
Reading it top to bottom: the outer all_of passes only when both terms pass. The first term is
the weather check. The second is a location check whose inner predicate says “the biome here is in
this list,” and the list holds just the dark forest. Because location_check needs the origin
position, you must invoke this predicate from somewhere that has a position, which is what
/execute ... at ... gives you.
Now a function to use it. We want: for every player, go to where they are, and if the predicate
passes there, give them an effect. You built as @a ... at @s chains like this in Chapter 4:
data/mypack/function/dark_forest_effects.mcfunction
execute as @a at @s if predicate mypack:raining_dark_forest run effect give @s minecraft:regeneration 5 0
Walk through it with your Chapter 4 eyes: as @a forks once per player; at @s moves the check to
that player’s position (giving location_check its origin); if predicate mypack:raining_dark_forest tests there; and only if it passes does run effect give @s … grant
five seconds of Regeneration. Players standing in a rainy dark forest get the effect; everyone else
gets nothing.
To make it run continuously, append it to the tick function tag you first created in Chapter 11 and have been adding to since. (You already have entries here from earlier chapters, so add this one to the end of the list, don’t replace them.)
data/minecraft/tags/function/tick.json
{
"values": [
"mypack:kill_on_gold",
"mypack:timer_tick",
"mypack:dark_forest_effects"
]
}
Save, run /reload, fly to a dark forest, and /weather rain. Stand under the dark-oak canopy and
watch the Regeneration hearts appear; step into the next biome and they stop renewing.
Figure (to be captured). player standing in a rainy dark forest with the Regeneration effect icon and swirling particles visible; rain falling through the dark-oak canopy
Reusing predicates by name: reference
The last condition type closes the loop on reuse. reference invokes a predicate file and returns
its result. Its field is name, the resource location of the predicate to invoke. This lets one
predicate stand on the shoulders of another instead of copy-pasting its guts.
Say you want a stricter rule: raining in a dark forest and the player is holding a diamond
sword. You already have mypack:raining_dark_forest and mypack:holding_diamond_sword as files, so
just reference both:
data/mypack/predicate/armed_in_storm.json
{
"condition": "minecraft:all_of",
"terms": [
{
"condition": "minecraft:reference",
"name": "mypack:raining_dark_forest"
},
{
"condition": "minecraft:reference",
"name": "mypack:holding_diamond_sword"
}
]
}
If you later change what counts as a “dark forest storm,” you edit raining_dark_forest.json once
and every predicate that references it updates automatically. One warning: a cyclic reference causes
a parsing failure, so don’t make predicate A reference B while B references A.
Practice
-
Stormy nights only. Write
data/mypack/predicate/stormy_night.jsonas anall_ofof two references:mypack:is_nighttimeand a newis_thunderingpredicate (aweather_checkwith"thundering": true). Add a tick line that gives Night Vision to players when it passes. -
Either sword. Write
data/mypack/predicate/holding_a_sword.jsonusingany_ofwith twoentity_propertiesterms, one checking forminecraft:diamond_sword, one forminecraft:netherite_swordinmainhand. Test it with@a[predicate=mypack:holding_a_sword]. -
Safe zone. Write a predicate that passes when a player is not in a dark forest: wrap a
referencetomypack:raining_dark_forest’s location half in aninverted. (Hint: you’ll first want ain_dark_forestpredicate holding just thelocation_check, then invert a reference to it.)
What Can Go Wrong
What Went Wrong? “My predicate never passes anywhere.” You probably invoked a context-hungry condition from a context that doesn’t supply its fact. The rule is explicit:
location_check,block_state_property, andmatch_toolalways fail if not provided their origin/block/tool. A bareexecute if predicatestanding in midair has a position but no block and no tool, so ablock_state_propertypredicate fails there every time. Fixes: givelocation_checka position withat @s(as we did), and only useblock_state_property/match_toolfrom a loot table or a context that actually breaks a block.
What Went Wrong? “Invalid predicate” / it won’t load. Two classic typos. First, the field that names the check is
condition, nottype;typeis what loot entries and number providers use, but a predicate’s check is keyed bycondition. Second, the combiners take their field names exactly:all_ofandany_ofuse atermslist, whileinverteduses a singleterm(nos). Mixing them up (atermsobject or aninvertedwith a list) fails to load.
What Went Wrong? “I nested predicates in a plain list and it acts like AND, not OR.” That’s the list shortcut biting you. A JSON array of predicates means all must pass (all predicates must evaluate to true), and
any_ofonly applies toterms; nested lists areall_of. If you wanted OR, write an explicitany_ofwith atermsarray, not a bare list.
What You Know Now
A predicate is a named yes/no condition stored as JSON in data/<namespace>/predicate/, keyed
by a condition field. You can write single checks (weather_check, time_check, random_chance,
entity_properties, block_state_property, match_tool, enchantment_active_check) and combine
them with all_of, any_of, and inverted. You can invoke a predicate three ways: from a command
with execute if predicate, from a selector with predicate=, and from inside loot tables and
advancements. You can chain predicates together with reference so a rule is written once and reused
everywhere. And mypack now has a raining_dark_forest predicate that fires real effects only when
the weather and the biome both agree. Next chapter puts predicates to work as the brains of
advancements, the game’s built-in event detectors.