Iris is a utility data pack for Minecraft: Java Edition 1.20.3+ designed to determine what block or entity a player is looking at, with micrometric precision and taking into account individual block geometries.
Note
The recommended method for using Iris on any supported Minecraft version is to download Iris directly from GitHub releases. You can alternatively build Iris yourself as detailed in this section.
The repository contains most of the code for the data pack. The gen_files.py
script in the build
directory generates missing files for block and entity hitboxes. To generate the full data pack, the script should be executed in the root of the repository. The script requires hitbox information for every blockstate and entity type ID as two JSON files; these reports should be generated with IrisDataGen.
# Clone repository
git clone https://github.com/Aeldrion/Iris
cd Iris
# Build data pack
./build/gen_files.py -v [data pack format] -b blocks.json -e entities.json
Replace blocks.json
and entities.json
with the path to the two reports generated by IrisDataGen
Casts a ray from the current position, oriented with the current rotation, and returns data on the block or entity that is found. To tell where a player is facing, starting at the eye position is needed. Entities with the iris.ignore
tag are ignored.
execute as <player> at @s anchored eyes positioned ^ ^ ^ run function iris:get_target
The result
of this function is the distance (in micrometers) before an obstacle is hit, or 0 if no block or entity is found before the maximum recursion depth is reached.
The success
is 1 if a block or entity was hit, 0 otherwise.
Available information about the targeted position is saved to the iris:output
storage. The exact storage output of the function is further documented in the header of the function itself (data/iris/functions/get_target.mcfunction
).
If a block was hit, a marker with the iris.targeted_block
tag is summoned in the middle of the targeted tile. If an entity was hit, it gets the iris.targeted_entity
tag. When running the function again, iris.targeted_block
markers are killed and iris.targeted_entity
entities get the tag removed, and so there can only be one such entity in the world at any given moment.
Settings of the function can be modified in the iris:settings
storage:
Tag | Description | Default value |
---|---|---|
MaxRecursionDepth |
How many blocks to traverse before giving up | 16 |
TargetEntities |
Whether or not to look for collisions with entities | false |
Callback |
A function or function tag to run where the raycast ends | Unset |
Blacklist |
Which blocks to ignore during block traversal | "#iris:air" |
Whitelist |
The only blocks to look for during traversal | Unset |
If both Blacklist
and Whitelist
are set, all blocks outside of the whitelist or in the blacklist will be ignored. If both Blacklist
and Whitelist
are unset, collisions will be tested for all tiles traversed. It is recommended that blocks with no outline (e.g. air
) be blacklisted, or that a whitelist be set up to ignore them.
In the following example, it is assumed that the function is executed as each player. A message is displayed in the actionbar of players targeting a block. The block reach is 5 blocks in creative mode, 4.5 blocks in survival mode.
# Detect when the player is looking at a block that is within reach
execute store result score $max_distance mypack run attribute @s minecraft:player.block_interaction_range get 1000000
execute at @s anchored eyes positioned ^ ^ ^ store result score $distance mypack run function iris:get_target
title @s actionbar ""
execute if score $distance mypack <= $max_distance mypack at @e[type=minecraft:marker, tag=iris.targeted_block] run title @s actionbar "Looking at a block"
In the following example, levitation is given to cows that the player looks at within a 10 block radius. It is assumed that TargetEntities
has been set to true, and that MaxRecursionDepth
has been set to a high enough value to catch everything within ten blocks; 20
is enough.
# Give levitation to cows the player is looking at
execute as <player> at @s anchored eyes positioned ^ ^ ^ run function iris:get_target
execute at <player> run effect give @e[type=minecraft:cow, tag=iris.targeted_entity, distance=..10] minecraft:levitation 1 0
Targeting hidden blocks
In the following example, a sound is played at the location of diamond ores that the player looks at, including through other blocks.
# Play a sound at diamond ores the player looks at, including through other blocks
data modify storage iris:settings Whitelist set value "#mypack:diamond_ores"
execute at @s anchored eyes positioned ^ ^ ^ run function iris:get_target
execute if data storage iris:output {TargetType: "BLOCK"} at @e[type=minecraft:marker, tag=iris.targeted_block] run playsound minecraft:block.note_block.bell block
// data/mypack/tags/blocks/diamond_ores.json
{
"values": [
"minecraft:diamond_ore",
"minecraft:deepslate_diamond_ore"
]
}
In the following example, the direction of the targeted face (TargetedFace.Direction
in storage iris:output
) and the direction of the player (scores $dx
, $dy
, $dz
on objective iris
) is used to predict where a block would be placed if the player attempted to place one. If no block is targeted, the function fails early; otherwise, the iris.targeted_block
marker that is summoned by iris:get_target
is teleported, and a block is placed at its new position.
execute at @s anchored eyes positioned ^ ^ ^ run function iris:get_target
execute unless data storage iris:output {TargetType: "BLOCK"} run return fail
execute if data storage iris:output TargetedFace{Direction: "WEST"} as @e[type=minecraft:marker, tag=iris.targeted_block] at @s run teleport @s ~-1 ~ ~
execute if data storage iris:output TargetedFace{Direction: "EAST"} as @e[type=minecraft:marker, tag=iris.targeted_block] at @s run teleport @s ~1 ~ ~
execute if data storage iris:output TargetedFace{Direction: "DOWN"} as @e[type=minecraft:marker, tag=iris.targeted_block] at @s run teleport @s ~ ~-1 ~
execute if data storage iris:output TargetedFace{Direction: "UP"} as @e[type=minecraft:marker, tag=iris.targeted_block] at @s run teleport @s ~ ~1 ~
execute if data storage iris:output TargetedFace{Direction: "NORTH"} as @e[type=minecraft:marker, tag=iris.targeted_block] at @s run teleport @s ~ ~ ~-1
execute if data storage iris:output TargetedFace{Direction: "SOUTH"} as @e[type=minecraft:marker, tag=iris.targeted_block] at @s run teleport @s ~ ~ ~1
execute at @e[type=minecraft:marker, tag=iris.targeted_block] if block ~ ~ ~ minecraft:air run playsound minecraft:block.stone.place block @a ~ ~ ~
execute at @e[type=minecraft:marker, tag=iris.targeted_block] if block ~ ~ ~ minecraft:air run setblock ~ ~ ~ minecraft:cobblestone
Note: Here, we check that the placing position is not occupied (if block ~ ~ ~ minecraft:air
). A better approach would be to test for any replaceable block (including water, tall gras...) with a block tag check.
Teleports the executing entity to a position provided by six scores on the iris
objective: $[x]
, $[y]
, $[z]
for integer coordinates, ${x}
, ${y}
, ${z}
for fractional coordinates (with a scale of 1,000,000). After running iris:get_target
, the six scores are set to the exact position where the ray lands and so iris:get_target
and iris:set_coordinates
can easily be used in conjunction:
# Teleport the player where they are looking
execute as <player> at @s anchored eyes positioned ^ ^ ^ run function iris:get_target
execute as <player> run function iris:set_coordinates
The result
and success
of this function is 1 if executed by an entity, 0 otherwise.
To include Iris in your own data pack:
- Copy the
iris
folder and its contents in thedata
folder of your data pack - Make sure the
#minecraft:load
function tag includesiris:setup/load
If you are redistributing modified versions of Iris as a part of your own data packs, it is recommended to change the iris
namespace to avoid conflicts with other data packs using Iris. To do so, you may simply rename the iris
folder to e.g. iris_mypack
and every instance of iris
in the contents of data pack files to iris_mypack
(including storage names, objectives, entity tags...)
Since this will most likely be used mostly by other data pack nerds, here is a summary of how Iris operates.
execute store
can be used to get an entity's position, however any scale over 70 is unusable for X and Z coordinates due to possible overflowing. To get the current position with enough detail, string manipulation is done with macro functions to cut and read everything past the decimal point in any position coordinate. From then on, the starting position is saved as six scores: the integer part ($[x]
, $[y]
, $[z]
) and the fractional part (${x}
, ${y}
, ${z}
).
To get the rotation, a marker is summoned 1,000,000 blocks forward starting from world origin (0.0
, 0.0
, 0.0
) using the executing rotation. The marker's position describes the rotation as a steering vector that can be used in later calculations.
Instead of progressing by a fixed length at every iteration like most raycasting functions do, the data pack solves a series of simple linear equations to figure out which tile the ray traverses next (ray/plane intersections). Upon hitting a block other than air (or an entity, if TargetEntities
is true), it gets its shape as a list of axis-aligned bounding boxes (AABB) and checks which faces the ray hits. For every AABB, there are six candidates; back-face culling narrows it down to three.
Block hitbox information is pulled from IrisDataGen. Blocks with the same set of block state properties and corresponding hitboxes are grouped together in block tags. Iris also accounts for the random horizontal offset on the hitbox of a few blocks, namely flowers, bamboo, pointed dripstone, and mangrove propagules. Minecraft computes this offset with a seeded RNG using the tile's X and Z coordinates; this offset can be identically computed easily on the scoreboard.