Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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_check requires 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” (like weather_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, and reference (covered at the end of this chapter). Each one is a condition value 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 with any_of: any_of only applies to terms; nested lists are all_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

  1. Stormy nights only. Write data/mypack/predicate/stormy_night.json as an all_of of two references: mypack:is_nighttime and a new is_thundering predicate (a weather_check with "thundering": true). Add a tick line that gives Night Vision to players when it passes.

  2. Either sword. Write data/mypack/predicate/holding_a_sword.json using any_of with two entity_properties terms, one checking for minecraft:diamond_sword, one for minecraft:netherite_sword in mainhand. Test it with @a[predicate=mypack:holding_a_sword].

  3. Safe zone. Write a predicate that passes when a player is not in a dark forest: wrap a reference to mypack:raining_dark_forest’s location half in an inverted. (Hint: you’ll first want a in_dark_forest predicate holding just the location_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, and match_tool always fail if not provided their origin/block/tool. A bare execute if predicate standing in midair has a position but no block and no tool, so a block_state_property predicate fails there every time. Fixes: give location_check a position with at @s (as we did), and only use block_state_property/match_tool from 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, not type; type is what loot entries and number providers use, but a predicate’s check is keyed by condition. Second, the combiners take their field names exactly: all_of and any_of use a terms list, while inverted uses a single term (no s). Mixing them up (a terms object or an inverted with 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_of only applies to terms; nested lists are all_of. If you wanted OR, write an explicit any_of with a terms array, 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.