Chapter 27 — Advanced /execute Patterns
What You’ll Build
Back in Chapter 4 you learned the /execute command, the one that lets a command run with a
different who, where, and when. You met as, at, positioned, if entity, if block,
and run, and you chained them together. At the time, two of /execute’s powers were marked
“coming later”: execute store (saving a command’s answer somewhere) and execute in
(running a command in another dimension). Chapter 12 made the same promise about execute store
writing into storage, and about if data using storage as a yes/no test. This is the chapter
where all of those promises come due.
But this chapter is about more than filling in three missing subcommands. It’s about patterns: the handful of techniques that data pack authors combine over and over to do things that look, at first, impossible with commands: shooting an invisible ray out of a player’s eyes and reacting to the first block it hits, walking a function forward through the world one step at a time, parking an invisible “pin” at a position and coming back to it later, and running a little machine that remembers what state it’s in. Every one of these is built out of the pieces you already have, snapped together in clever ways.
By the end you’ll have extended your mypack pack with two real tools: a raycaster that
shoots a line of particles out of where you’re looking and stops at the first solid block, and a
nearest-block finder that searches outward from a point until it finds a block you named. Along
the way you’ll write your first recursive functions (functions that call themselves) and wire
them safely into your pack.
This chapter extends your mypack pack and uses the test world you’ve used since Chapter 1. It leans
hard on three earlier chapters, so keep them in mind: /execute (Chapter 4), command storage and
the /data command (Chapter 12), and function macros and /return (Chapter 25).
A quick map of where we’re going
Everything in this chapter is /execute plus things you already know. Here’s the shape of it:
- Nested execute chains: using more conditions in one chain, and splitting logic across several chains, for “only when all of these are true” rules.
execute store: the deep dive. Take the answer a command produces and save it into a score, a storage, an entity, or a block.execute in: run a command as if you were standing in a different dimension.if data: test whether some NBT data exists, and branch on it (the Chapter 12 promise).if items: ask “is it holding/wearing/carrying this item?” without touching raw NBT.- Raycasting: stepping a function forward along your line of sight with local
^ ^ ^coordinates, one block at a time, using a function that calls itself. - Marker entities: invisible, do-nothing entities that make perfect position pins.
- Loop patterns: recursive function calls as the data pack version of “repeat”.
- State machines: a pack that remembers “what mode am I in?” in storage and acts accordingly.
Let’s start with the one that unlocks the rest.
execute store: catching a command’s answer
Here is an idea that changes how you think about commands. Almost every command produces a
number when it runs: a hidden answer. Most of the time you never see it. execute store is the
subcommand that catches that number and saves it somewhere you choose.
result vs. success
Straight from the wiki: in Java Edition, every branch of an /execute outputs two values after it
finishes: a success value and a result value:
- The
successvalue is always 0 or 1. It answers “did the last subcommand succeed?” 1 means yes, 0 means no. - The
resultvalue is a fuller answer. It’s the actual number the last command produced, for example how many entities a condition matched, or how long a list is. It’s always an integer; if a command would produce a decimal, it’s rounded down.
The wiki is precise about where these come from: they “come from the last subcommand (may be a
condition subcommand or a run subcommand).” So the value you catch is the value of whatever sits
at the end of your chain. And: “If in a branch the last subcommand fails, both the two values
are 0 in the branch.”
A store subcommand says “when this chain finishes, take the result (or the success) and put it
here.” The wiki describes the timing exactly: a store subcommand “is first processed along with
other subcommands in the subcommand chain, recording the location to store in. After the last
subcommand … is executed, output values are stored in the recorded location.”
The five places you can store
The wiki lists exactly five storage modes:
store (result|success) block – Stores output value under one of a block's NBTs
store (result|success) bossbar – Stores output value as a bossbar data
store (result|success) entity – Stores output value under one of an entity's NBTs
store (result|success) score – Stores output value under a target's score on an objective
store (result|success) storage – Stores output value under one of a storage's NBTs
Four of the five (everything except score) write into NBT, so they share the same shape. Here is
the storage one, copied exactly from the wiki:
store (result|success) storage <target> <path> <type> <scale> -> execute
<target>: the storage’s resource location, likemypack:config. (Forblockit’s a position; forentityit’s a single entity.)<path>: the NBT path where the value should go (the dotted paths you learned in Chapter 12, likeSettings.last_count).<type>: the number type to save it as. The wiki says it “must be one ofbyte,short,int,long,float, anddouble.” These are the SNBT number types from Chapter 12.<scale>: a multiplier applied before storing, “may be negative.” If you don’t want to scale, use1.
One important detail from the wiki for the four NBT modes: “If the output value is a decimal, it is
rounded first and then multiplied by <scale>.” And a handy convenience for storage in particular:
“If the storage does not yet exist, it gets created.”
The score mode is the simplest, because a score is already a plain integer, with no type or scale:
store (result|success) score <targets> <objective> -> execute
The wiki’s one-line description: it “overrides the score held by <targets> on the given
<objective> with the final command’s output value.” Note overrides: it replaces the score,
it doesn’t add to it.
Under the Hood (skippable) Two limits worth knowing, both from the wiki. First,
store ... entity“cannot modify player NBT,” exactly like the/datacommand from Chapter 12: you can store into mobs and other entities but not into a player’s own NBT. Second, if a chain forks (Chapter 4’s word for “runs once per matched entity”) after a store, the store happens on each branch, and when several branches write to the same spot, “the output value of a later-executing branch directly overwrites the output value of the earlier-executed branch, rather than being accumulated.” So the value left behind is the one from the last branch. Storing per-branch into one shared spot does not add them up.
A worked example: count something into a score
The wiki gives this exact example, and it’s the perfect first taste. Read it right to left from the
if:
execute as @a at @s store result score @s nearbyRedSheep if entity @e[type=sheep,nbt={Color:14},distance=..20]
Take it apart with your Chapter 4 eyes:
as @a at @s: for every player, standing where they stand.if entity @e[type=sheep,nbt={Color:14},distance=..20]is the last subcommand, so itsresultis the value that gets caught. For anif entityat the end of a chain, the wiki says theresultvalue “is the number of matching entities.” So: how many red sheep are within 20 blocks.store result score @s nearbyRedSheep: catch that count and write it into each player’snearbyRedSheepscore.
After this runs, every player’s nearbyRedSheep score holds the number of nearby red sheep: a
fact you computed with a condition, not a counter. That’s the trick: if-style subcommands
measure as well as gate execution, and store result is how you keep the measurement.
Modern Minecraft Old tutorials did counting like this with long scoreboard loops: tag every sheep, add 1 per sheep, reset, repeat.
execute store result score ... if entity ...does the same job in one line. When you see an ancient guide manually counting entities with/scoreboard players add, this is the modern replacement.
Storing into your pack’s storage
Let’s put store to work in mypack. In Chapter 12 you built a mypack:config storage with a
Settings compound. We’ll add a function that measures how many armor stands are nearby and
records it into storage, so the rest of the pack can read it later.
mypack/data/mypack/function/count_markers.mcfunction
# Count armor stands within 16 blocks and remember the number in storage.
# The `if entity` at the end of the chain produces, as its result, the
# number of matching entities; `store result storage` catches that number.
execute store result storage mypack:config Settings.nearby_stands int 1 if entity @e[type=armor_stand,distance=..16]
Run mypack:count_markers, then read it back with the Chapter 12 command (typed in chat, so it
keeps its slash):
/data get storage mypack:config Settings.nearby_stands
You’ll see the count. Notice we asked for it as an int with scale 1: a plain integer, no
scaling. Because storage auto-creates, this works even on a fresh world where Settings didn’t
have a nearby_stands key yet.
if data: testing whether data exists
Chapter 12 promised that execute if data would let you use storage as a condition. Here it is.
The wiki: (if|unless) data “checks whether the targeted block, entity or storage has any data tag
for a given path.” It comes in three forms, copied exactly:
(if|unless) data block <pos> <path> -> [execute] – for data checking a block
(if|unless) data entity <target> <path> -> [execute] – for data checking an entity
(if|unless) data storage <source> <path> -> [execute] – for data checking a storage
The key word is exists. if data does not compare a value to anything; it only asks “is there
any data at this path?” If the path is there, if continues (and unless stops); if it’s
absent, if stops (and unless continues).
This is exactly what you need for “have I set this up yet?” checks. Add a guard to your pack so a function only runs once the config exists:
mypack/data/mypack/function/needs_config.mcfunction
# Only announce readiness if the config has actually been built.
# `if data storage` tests that the path EXISTS, nothing more.
execute if data storage mypack:config Settings run say Config is ready.
execute unless data storage mypack:config Settings run say No config yet — run mypack:config_init first.
if data can also reach into an entity’s NBT. This example kills zombies wearing no helmet
by testing whether the head slot of the mob’s equipment data holds anything:
execute as @e[type=zombie] unless data entity @s equipment.head run kill @s
unless data entity @s equipment.head reads as “unless this zombie has something on its head,”
i.e. only when the helmet slot is empty. That’s if data doing real in-game work, well beyond a
setup guard. (For item checks specifically there’s an even better tool, coming right up.)
if items: checking inventories
if data asks “does data exist at this path?” But the single most common version of that question
is really about items: is this player holding a sword? wearing boots? carrying a key anywhere in
their inventory? For that, /execute has a purpose-built pair. The wiki: (if|unless) items
“checks for a matching item in the provided inventory slots.” Two forms, copied exactly:
(if|unless) items block <sourcePos> <slots> <item_predicate> -> [execute] – items in a block's inventory (chest, furnace, ...)
(if|unless) items entity <source> <slots> <item_predicate> -> [execute] – items in an entity's inventory or equipment
<slots> names where to look, using the same slot names you met with /item in Chapter 20:
weapon.mainhand, armor.head, hotbar.0, and so on, and a * covers a whole slot group
(weapon.* is both hands, container.* is a whole chest). <item_predicate> is what to look
for: a plain item id, an item tag with #, or * for “any item at all,” optionally followed by
component tests in square brackets.
# Only players actually holding a diamond sword
execute as @a if items entity @s weapon.mainhand minecraft:diamond_sword run say En garde!
# Any sword counts, in either hand — item tags work here
execute as @a if items entity @s weapon.* #minecraft:swords run say Armed.
# Component tests ride in the brackets: an undamaged sword only
execute as @a if items entity @s weapon.mainhand minecraft:diamond_sword[damage=0] run say Factory fresh.
And the helmet-less zombie check from a moment ago, said the if items way, no NBT paths in
sight:
execute as @e[type=zombie] unless items entity @s armor.head * run kill @s
When the question is “is there an item like this in slot that,” reach for if items first
and save if data for data that isn’t an item.
execute in: reaching into another dimension
The second Chapter 4 promise. execute in <dimension> lets a command run as though it were
happening in a different dimension. The wiki’s description: it “sets the execution dimension and
execution position.” Syntax, copied exactly:
in <dimension> -> execute
The <dimension> is the dimension’s ID, like minecraft:the_nether, minecraft:the_end, or
minecraft:overworld. (Custom dimensions are a Part XI topic, Chapter 44, but execute in works
with them too.)
There’s one subtlety the wiki is careful about, and it matters: coordinate scaling between the
Overworld and the Nether. The wiki says in “respects dimension scaling for relative and local
coordinates: the execution position (only the X/Z part) is divided by 8 when changing from the
Overworld to the Nether, and is multiplied by 8 when vice versa.” This is the same 8:1 ratio you
know from Nether travel in normal play: one block in the Nether covers eight in the Overworld.
The wiki’s worked examples show this clearly. To teleport a player to the matching spot in the
Nether (same numbers), you pin the position first with positioned as @s:
execute in minecraft:the_nether positioned as @s run tp ~ ~ ~
The wiki: “If a player at position (16,64,16) in Overworld runs the following command, the player is teleported to (16,64,16) in the Nether.” The
positioned as @sgrabs the literal coordinates before the dimension switch, so no scaling is applied.
Without that positioned as @s, the scaling kicks in:
execute in minecraft:the_nether run tp ~ ~ ~
The wiki: “If a player at position (16,64,16) in Overworld runs the following command, the player is teleported to (2,64,2) in the Nether.” The X and Z were divided by 8.
Let’s add a small dimension probe to mypack. It uses execute in together with the if dimension
condition (also on the execute page: it “tests the dimension of the execution”) to report what’s
loaded where.
mypack/data/mypack/function/dimension_check.mcfunction
# Report which dimension the runner is in, using `if dimension`.
execute if dimension minecraft:overworld run say You are in the Overworld.
execute if dimension minecraft:the_nether run say You are in the Nether.
execute if dimension minecraft:the_end run say You are in the End.
Try It! Combine
inwith thestoreyou just learned.execute in minecraft:the_end store success storage mypack:config Settings.end_loaded byte 1 if loaded ~ ~ ~tries to test whether the chunk at your matching End position is loaded, and records a 1 or 0 into storage. (if loadedis another condition on the execute page; it “checks if chunks at a given position is fully loaded.”) This is how map-makers detect whether a far-off dimension is ready before acting in it.
Nested execute chains for multi-condition logic
You already chain subcommands. The “advanced” part is just doing it with intent. Two facts from the execute page make multi-condition logic work:
- You can use condition subcommands more than once in a chain. The page says subcommands other
than
run“can be arranged arbitrarily and used multiple times.” Soif ... if ... if ... runis perfectly legal, and everyifmust pass for the chain to reachrun. That’s “AND” for free. - A branch that fails a condition simply stops. The page: “When not at the end of the
subcommands chain, only if the condition tests pass does the branch continue; otherwise it
terminates.” So stacking
ifs narrows things down, step by step.
Here’s a multi-condition rule for mypack: give a player Glowing only when they’re a real player,
standing on a gold block, and it’s the Overworld. (You used Glowing back in Chapter 13; the gold
block test echoes Chapter 4.)
mypack/data/mypack/function/triple_check.mcfunction
# Every condition must pass before the effect is granted.
# `~ ~-1 ~` is the block just below each player's feet (Chapter 2 relative coords).
execute as @a at @s if dimension minecraft:overworld if block ~ ~-1 ~ minecraft:gold_block run effect give @s minecraft:glowing 5 0
What about “OR”? The execute page’s forking note tells you the clean way: when you need any of several conditions, you don’t cram them into one chain. You write several chains, often one per line in a function, and let each fire independently. That’s the data pack version of an “or”:
mypack/data/mypack/function/danger_floor.mcfunction
# OR logic: two separate chains. A player standing on EITHER block gets the message.
execute as @a at @s if block ~ ~-1 ~ minecraft:magma_block run say Hot floor!
execute as @a at @s if block ~ ~-1 ~ minecraft:lava run say Hot floor!
Under the Hood (skippable) There’s a third option for “or” that you met in Chapter 25:
execute if function. The execute page saysif function“checks if function(s) are non-void and the return value is non-zero.” So you can push complicated “is any of this true?” logic into a function that uses/returnto answer 1 or 0, then test it with one cleanif function. Reach for that when a single line of stacked conditions gets too long to read.
Marker entities: invisible position pins
Several of the patterns coming up need a way to remember a spot in the world: a pin you can drop
now and teleport back to, or run commands at, later. The tidy tool for that is the marker
entity.
The wiki describes the marker plainly: “Markers are entities intended for use in data packs and map-making. They can only be created with the summon command.” Their whole point is to have almost no behavior, copied from the wiki:
“Markers are intended to have minimal behavior. Markers do not move, do not take damage, and cannot be given status effects. Markers do not make sounds … Markers only exist on the server side, so they do not render.”
That list is exactly why they make good pins. A marker sits at a position and does nothing: it won’t drift, won’t get hurt, won’t be visible, and won’t shove blocks or players around (the wiki: markers “do not obstruct the placement of blocks, nor do they push players or other entities away from their own position”). The wiki even notes they “do not count toward the E-value (total amount of entities) listed on the debug screen,” so a few markers won’t clutter your entity count.
Because a marker is just an entity, everything you already know about entities applies: you can give
it a tag with /tag (Chapter 13) to find it again, select it with @e[type=marker,tag=...]
(Chapter 3), run commands at it (Chapter 4), and read or write its NBT with /data (Chapter 12).
Here’s a pair of functions for mypack: one drops a tagged marker where you’re standing, the other
teleports you back to it.
mypack/data/mypack/function/drop_pin.mcfunction
# Summon a marker at the runner's position and tag it so we can find it again.
# Markers can ONLY be made with summon (per the wiki).
execute at @s run summon minecraft:marker ~ ~ ~ {Tags:["mypack_pin"]}
say Pin dropped.
mypack/data/mypack/function/goto_pin.mcfunction
# Teleport the runner to the saved pin. `at` the marker makes its position
# the execution position; tp ~ ~ ~ lands the player exactly there.
execute as @s at @e[type=minecraft:marker,tag=mypack_pin,limit=1] run tp @s ~ ~ ~
What Went Wrong? Dropping pins over and over leaves a pile of markers stacked on old spots, and
goto_pinonly uses one of them. When you’re done with a pin, clear it:execute run kill @e[type=minecraft:marker,tag=mypack_pin]removes every marker carrying that tag. A good habit is to clear old pins right before dropping a new one, so there’s never more than one.
Under the Hood (skippable) One marker quirk the wiki calls out: “Using F3+I while aiming at a marker does not copy the entity data to the clipboard.” F3+I is the debug shortcut from Chapter 10 that copies an entity’s data. Because markers are server-side only, it won’t work on them. To inspect a marker, use
/data get entity @e[type=minecraft:marker,tag=mypack_pin,limit=1]instead.
Recursion: a function that calls itself
Now the big idea behind raycasting and loops. A .mcfunction runs its lines top to bottom and
stops. There’s no built-in “repeat 10 times.” So how do data packs loop? A function calls
itself. That’s recursion: a function whose job includes running itself again, usually after
moving a little or counting down, until some condition says “stop.”
You already have every piece. From Chapter 25’s /function command, a function runs another with
function <namespace>:<name>. There’s nothing stopping that “another” from being the same
function. And you have /return (Chapter 25) and if/unless conditions to decide when to stop.
The shape of every recursive function in this chapter is the same:
- Check a stop condition first. If we should stop, stop (often with
/return). - Do one step of work.
- Move the context forward (one block, or one counter tick).
- Call myself again to do the next step.
The “stop condition first” rule is the most important one. A recursion with no stop is an infinite
loop, and Minecraft will cut it off: there’s a hard limit on how many commands a chain of function
calls may run in a single tick (the maxCommandChainLength game rule). Hitting that limit means
your function silently stops partway, which looks like a bug. Always give recursion a way to
end.
What Went Wrong? The single most common recursion mistake is forgetting the stop condition, or putting it after the self-call instead of before. If your function “does nothing” or the game hitches when you run it, you’ve probably written a loop with no exit. Read your function top to bottom and ask: “what line makes this stop calling itself?” If you can’t point to one, that’s the bug.
Raycasting: stepping along your line of sight
A raycast is the technique of shooting an invisible line (a ray) out from a point in a direction, moving forward in small steps and checking each step for something (a block, an entity, a spot to mark). Minecraft has no “raycast” command. You build one out of recursion and the local coordinates from Chapter 2.
The engine is two facts you already know:
- Local coordinates step forward. The Coordinates page: a caret offset is “an offset within a
moving, entity-centric frame … with +Xlocal directed to its left, +Ylocal directed upward, and
+Zlocal directed in the direction the sender faces.” And: “
tp ^ ^ ^5teleports the player 5 blocks forward.” So^ ^ ^1means one block forward, in the direction you’re facing. execute positionedmoves the spot a command runs at without changing anything else (the execute page: it “sets the execution position, without changing execution rotation or dimension”).positioned ^ ^ ^1therefore nudges the execution point one block forward along the current facing, and keeps the rotation, so the next^ ^ ^1keeps going the same way.
Put them together and you get a step: “from here, move one block forward, do something, then call myself to take the next step.” Each call advances the ray one block. We stop when we either hit a solid block or run out of range.
First, the function that fires the ray. It records a step budget into storage (so we don’t loop forever) and aims along the runner’s eyes.
mypack/data/mypack/function/raycast_start.mcfunction
# Begin a raycast from the player's EYES, looking where they look.
# `anchored eyes` recenters local coordinates on the eyes, so ^ ^ ^ starts
# at eye level (per the execute page). We give the ray a budget of 30 steps
# stored in mypack:config, then hand off to the recursive stepper.
data modify storage mypack:config Ray.steps_left set value 30
execute as @s at @s anchored eyes positioned ^ ^ ^ run function mypack:raycast_step
Now the recursive stepper. This is the heart of the chapter: read the comments line by line.
mypack/data/mypack/function/raycast_step.mcfunction
# One step of the ray. Runs AT the current point along the line of sight.
# Pull the remaining budget OUT of storage into a fake-player score so we can
# test and change it. `store result score` catches data get's answer (Ch12:
# data get returns the value at the path).
execute store result score #ray mypack_zero run data get storage mypack:config Ray.steps_left
# STOP CONDITION 1: out of budget. If the score has reached 0, stop.
execute if score #ray mypack_zero matches 0 run return 0
# STOP CONDITION 2: we hit something solid. Mark the spot, then stop.
execute unless block ~ ~ ~ minecraft:air run particle minecraft:flame ~ ~ ~ 0 0 0 0 1 force
execute unless block ~ ~ ~ minecraft:air run return 0
# DO ONE STEP OF WORK: draw a particle at this point along the ray.
particle minecraft:end_rod ~ ~ ~ 0 0 0 0 1 force
# COUNT DOWN: subtract one and write the new budget BACK into storage.
scoreboard players remove #ray mypack_zero 1
execute store result storage mypack:config Ray.steps_left int 1 run scoreboard players get #ray mypack_zero
# MOVE FORWARD ONE BLOCK and CALL MYSELF for the next step.
execute positioned ^ ^ ^1 run function mypack:raycast_step
There’s a lot of Chapter 27 in those lines, so let’s name each move:
- We load the budget out of storage first with
store result score #ray mypack_zero run data get ....#rayis a fake player (the#prefix hides it from the sidebar, Chapter 11), andstore result scorecatchesdata get’s answer (the value at the path) into it. - The stop conditions come right after, exactly as the recursion rule demands. We stop if the
budget has hit 0 (
if score #ray mypack_zero matches 0), and we stop the moment a step lands on a non-air block (unless block ~ ~ ~ minecraft:air). On a hit we drop a brightflameparticle so you can see where the ray landed, thenreturn. - The work is one
end_rodparticle at the current point: that’s what draws the visible line. We useforceso it shows even on low particle settings (the particle page:force“always shown even if the ‘Particles’ option … is ‘Minimal’”). - The count down writes the new budget back: subtract one from
#ray, thenstore result storagefiles it intoRay.steps_left. Together with the load-out at the top, that’sstoredoing exactly what it’s for: moving a command’s numeric answer between a score and storage. - The move + self-call is the one line that makes it a ray:
positioned ^ ^ ^1 run function mypack:raycast_step. Forward one block, then do it all again from the new spot.
We need the scoreboard objective the stepper uses. Add it to the setup function that runs on load
(the mypack:score_setup you built in Chapter 11):
mypack/data/mypack/function/ray_setup.mcfunction
# Objective used as scratch space by the raycaster's countdown.
scoreboard objectives add mypack_zero dummy
Wire it into load by appending to your existing load tag (never rewrite the file: append, keeping the earlier entries):
mypack/data/minecraft/tags/function/load.json
{
"values": [
"mypack:load",
"mypack:score_setup",
"mypack:ray_setup"
]
}
To try it: in your test world, look in a direction with a wall or hill a little way off, and run
/function mypack:raycast_start. You’ll see a line of end_rod particles shoot from your eyes and
a flame puff appear on the first block the ray meets.
Figure (to be captured). a line of white end_rod particles streaming from the player’s eyes across a field, ending in a flame particle on the face of a distant dirt cliff
What Went Wrong? The ray goes straight through walls. Almost always this is the air test.
unless block ~ ~ ~ minecraft:aironly stops on a block named exactlyminecraft:air; if the very first step starts inside you or a block, the geometry can skip it. Make sureraycast_startusesanchored eyesso the ray begins at eye level, in open space, looking outward.The ray is too short or hangs. The whole ray runs inside one tick, so a giant budget can bump the
maxCommandChainLengthlimit and cut off. Thirty steps is a safe, generous default; raise it only if you need a longer reach and watch for the ray stopping early.
A nearest-block finder: expanding search
The raycast searches in one direction. The other classic pattern searches in all directions at once: start at a point and check farther and farther out until you find a block you’re hunting. This is the nearest-block finder, and it uses the same recursion shape with a different “step.”
The idea: keep a search radius in storage. Each round, test a ring of positions at the current radius for the target block; if found, mark it and stop; if not, grow the radius and recurse. A full spherical scan is a lot of positions, so for a beginner-friendly version we’ll search the ring of blocks straight out along the four compass directions at each radius: enough to feel the pattern without writing hundreds of lines.
mypack/data/mypack/function/find_start.mcfunction
# Start a search outward from the runner for diamond_block, up to radius 8.
data modify storage mypack:config Find.radius set value 1
data modify storage mypack:config Find.max set value 8
execute at @s run function mypack:find_step
mypack/data/mypack/function/find_step.mcfunction
# One ring of the expanding search, run AT the search origin.
# Pull radius and max out of storage into scores so we can compare them.
execute store result score #r mypack_zero run data get storage mypack:config Find.radius
execute store result score #max mypack_zero run data get storage mypack:config Find.max
# STOP CONDITION: searched past the maximum radius without a hit.
execute if score #r mypack_zero > #max mypack_zero run say Nothing found within range.
execute if score #r mypack_zero > #max mypack_zero run return 0
# CHECK THE RING: four compass points at the current radius. A hit marks the
# spot with a marker pin and stops. We use a macro so one line covers all four
# offsets — `with storage` feeds the radius in (Chapter 25 macros).
function mypack:find_ring with storage mypack:config Find
# GROW THE RADIUS by 1 and recurse for the next, wider ring.
scoreboard players add #r mypack_zero 1
execute store result storage mypack:config Find.radius int 1 run scoreboard players get #r mypack_zero
execute if data storage mypack:config Find.searching run function mypack:find_step
The ring check is a macro function (Chapter 25): the $(radius) placeholder gets filled in from
the storage we passed with with storage mypack:config Find. Each line tests one compass direction
at the current radius and, on a hit, drops a marker pin and clears the searching flag so the loop
ends.
mypack/data/mypack/function/find_ring.mcfunction
# Macro: test four blocks at distance $(radius) for diamond_block.
# Lines beginning with $ are macro lines; $(radius) is replaced at call time.
$execute if block ~ ~ ~$(radius) minecraft:diamond_block positioned ~ ~ ~$(radius) run function mypack:find_hit
$execute if block ~ ~ ~-$(radius) minecraft:diamond_block positioned ~ ~ ~-$(radius) run function mypack:find_hit
$execute if block ~$(radius) ~ ~ minecraft:diamond_block positioned ~$(radius) ~ ~ run function mypack:find_hit
$execute if block ~-$(radius) ~ ~ minecraft:diamond_block positioned ~-$(radius) ~ ~ run function mypack:find_hit
mypack/data/mypack/function/find_hit.mcfunction
# Runs AT a found diamond block. Pin it and stop the search.
summon minecraft:marker ~ ~ ~ {Tags:["mypack_found"]}
data remove storage mypack:config Find.searching
say Found a diamond block!
Two more pieces tie it together. The search needs a searching flag set when it begins, so
find_start should set it. Update that file to:
mypack/data/mypack/function/find_start.mcfunction
# Start a search outward from the runner for diamond_block, up to radius 8.
data modify storage mypack:config Find.radius set value 1
data modify storage mypack:config Find.max set value 8
data modify storage mypack:config Find.searching set value 1b
execute at @s run function mypack:find_step
Now the recursion has a clean stop: each find_step continues only if data storage mypack:config Find.searching, and find_hit removes that flag the instant a block is found. If nothing is found
by the time the radius passes max, the > #max check ends it instead.
To try it: in your test world, place a diamond_block a few blocks away from you (on the same Y
level for this simple version), stand near it, and run /function mypack:find_start. When the
expanding rings reach it, you’ll get “Found a diamond block!” and an invisible marker pinned on it,
which you can then teleport to with the same trick as goto_pin.
Figure (to be captured). chat showing “Found a diamond block!” with a diamond block sitting a few blocks from the player in a flat test world
Try It! This version only searches along the compass lines at each radius, on one Y level. Extend the macro
find_ringwith more$execute if blocklines to also check~ ~$(radius) ~and~ ~-$(radius) ~(up and down), and you’ll have a 3D plus-shaped search. Every new direction is one more macro line; the recursion that grows the radius doesn’t change at all.
State machines: a pack that remembers its mode
The last pattern ties storage and conditions together into something that behaves differently
depending on what’s happened before. A state machine is just that: a thing that’s always in
exactly one state (a named mode), does work according to that state, and transitions to
another state when something happens. You already have everything to build one: the “current
state” is a value in storage, and if data/score comparisons choose what to do.
Let’s give mypack a tiny three-state machine: a mini “game” that cycles idle → running →
finished → back to idle. We store the state as a string in mypack:config.
mypack/data/mypack/function/game_init.mcfunction
# Put the machine into its starting state.
data modify storage mypack:config Game.state set value "idle"
say Game ready (state: idle).
A single “advance” function reads the current state and moves to the next one. This is the transition table, written as one chain per state:
mypack/data/mypack/function/game_advance.mcfunction
# Read the state and transition to the next. `if data ... { ... }` matches a
# compound value: it only continues when state equals the given string.
execute if data storage mypack:config Game{state:"idle"} run data modify storage mypack:config Game.state set value "running"
execute if data storage mypack:config Game{state:"idle"} run say Game started! (idle -> running)
execute if data storage mypack:config Game{state:"running"} run data modify storage mypack:config Game.state set value "finished"
execute if data storage mypack:config Game{state:"running"} run say Game over! (running -> finished)
execute if data storage mypack:config Game{state:"finished"} run data modify storage mypack:config Game.state set value "idle"
execute if data storage mypack:config Game{state:"finished"} run say Reset. (finished -> idle)
The clever bit is the path Game{state:"idle"}. That’s an NBT path with a compound filter from
Chapter 12; it only matches if Game contains state:"idle". So if data storage mypack:config Game{state:"idle"} is true only when the current state is exactly “idle.” Each pair of lines is
one transition: “when in this state, do this, then switch.” Run mypack:game_init once, then run
mypack:game_advance repeatedly and watch it walk idle → running → finished → idle.
Under the Hood (skippable) Why a state machine instead of a pile of scoreboard flags? Because the state lives in one place and is always exactly one value, you can never get into a confused “both running and finished” situation. Chapter 12’s mnemonic was “scoreboards count; storage remembers”: a state machine is storage remembering which mode the pack is in. Real minigames (Chapter 33) are built on exactly this idea, usually with the per-tick logic for each state in its own function.
Practice
These extend the two tools you built. Do them inside mypack.
-
Raycast that paints. Change
raycast_stepso that on a hit (theunless block ~ ~ ~ minecraft:airbranch) it adds to the flame bysetblocking aminecraft:glowstoneone step back along the ray (positioned ^ ^ ^-1 run setblock ~ ~ ~ minecraft:glowstone). Now you have a “place a light where I’m looking” tool. (Hint: stop before you place, so you don’t overwrite the block you hit.) -
Measure the gap. Write
mypack:range_checkthat raycasts forward but, instead of drawing particles, counts how many steps it took to reach a block and stores that number inmypack:config Ray.distancewithexecute store result storage. You’ll reuse the countdown pattern: the distance is30 minus steps_leftat the moment of the hit. -
A four-state machine. Add a
pausedstate to the game machine, betweenrunningandfinished, so the cycle becomes idle → running → paused → finished → idle. You only add one more pair of lines togame_advance, following the exact compound-filter pattern. -
Cross-dimension pin. Combine markers and
execute in: writemypack:pin_netherthat drops amypack_pin-tagged marker at your matching position in the Nether usingexecute in minecraft:the_nether positioned as @s run summon minecraft:marker ~ ~ ~ {Tags:["mypack_pin"]}, then teleport to it with yourgoto_pintrick (widening its selector to find the marker in any dimension).
What Can Go Wrong
-
storecaught the wrong number. Remember the value comes from the last subcommand in the chain. If you writestore result ... run say hi, you’ll storesay’s output, not the count you meant. The measuringif/condition has to be the final link. Re-read the chain right-to-left and ask “what’s the last thing, and what number does it produce?” -
A recursion does nothing or stutters. That’s a missing or misplaced stop condition (or a too-big step budget hitting
maxCommandChainLength). Every recursive function must check its stop condition before it calls itself, and the call must move something forward (position or counter) so the stop condition eventually fires. -
execute inteleports to the wrong spot. If a player lands at scaled-down coordinates when you wanted the same numbers, you forgotpositioned as @s. The wiki’s rule: plainin ... run tp ~ ~ ~applies the 8:1 Nether scaling; pin the position withpositioned as @sfirst to keep the literal coordinates.
What You Know Now
You can catch the hidden answer of any command with execute store result/success and file it
into a score, storage, entity, or block, including the result-vs-success distinction, the
number types, and the scale. You can test for data with if data, check hands, armor slots, and
chests with if items, and reach into other dimensions with execute in (Nether scaling and all). You can
stack conditions for “AND” logic and split chains for “OR.” You met the marker entity (the
invisible, do-nothing position pin) and you wrote your first recursive functions, using them to
build a raycaster along your line of sight and an expanding nearest-block finder. And you
turned storage into a state machine that remembers what mode your pack is in. These are the core
moves behind nearly every advanced data pack; you’ll lean on all of them in the project chapters
ahead.
This closes Part VII. You now have functions that take input (macros, Chapter 25), control their own timing and randomness (Chapter 26), and reshape the who/where/when/answer of every command they run (this chapter). That’s the full programmable-function toolkit. Time to build real things with it.