Function Data Packs for Dummies #9 Part 3 | NBT based targeting (+ /execute if data)

  • 10
  • 4
  • 4
avatar Bertiecrafter
Level 55 : Grandmaster Cyborg
156
This series of tutorials will teach you how to create your very own datapacks, assuming you know absolutely nothing about commands. Datapacks can contain a number of things, but only functions and tags will be covered in these posts. In every part I assume you've read the previous parts.

Recap

In the last tutorials we learned how to spawn entities, blocks and items with custom NBT and how to edit the NBT afterwards. A value in a compound can be selected by key, using a path separated by dots. For example, for the NBT "{A:{B:C}}" the value "C" can be retrieved using the path "A.B". An item in a list can be selected by index, using square brackets. For example, for the NBT "{List:[​A, {B: [​C, D]}]}" the value "C" can be retrieved using the path "List[​1].B[​0]". This is the last part of the NBT madness, where you'll learn how to test for NBT values, so you can run functions and other commands only for the right blocks, entities or items.

Paths

In order to test for NBT, it's essential to know the full NBT Path syntax. In the last tutorial we covered a couple elements already and in this section I'll be explaining the rest of it. Again, this wiki page has been used as source for the path syntax. Paths with filters can be used in /data as well, automatically aborting the command if the filter doesn't match.

Filtering the root
The root can be filtered by starting the path with {<NBT>}.
NBT: {createdByMe:1b,shouldExplode:0b,someKey:"someValue"}
PATH: {createdByMe:1b}
RESULT: {createdByMe:1b,shouldExplode:0b,someKey:"someValue"}

PATH: {shouldExplode:1b}
RESULT: *Command failed*

Filtering compounds
Suffix a key with {<NBT>} to apply a filter to the value of the specified key.
NBT: {key:"value",deepCompound:{myTypeValue:1b,anotherCompound:{key:"value"}}}

PATH: deepCompound{myTypeValue:1b}.anotherCompound.key
RESULT: "value"

PATH: deepCompound{myTypeValue:2b}.anotherCompound.key
RESULT: *Command failed*

Filtering items from lists.
Instead of an index, use {<NBT>} to filter items. If more than 1 item matches, the command gets executed for each of the items if possible, since not all commands support this. To be safe, make sure your filter can never select more than 1 item, like filtering by inventory slot number for example. (A slot can't contain 2 different items.)
NBT: {Inventory:[{Slot:1b,id:"minecraft:stone",Count:1b},{Slot:0b,id:"minecraft:diamond",Count:1b}]}

PATH: Inventory[0]
RESULT: {Slot:1b,id:"minecraft:stone",Count:1b} <-- Does not give item in first slot!

PATH: Inventory[{Slot:0b}].id
RESULT: "minecraft:diamond"

PATH: Inventory[{Slot:3b}].id
RESULT: *Command failed*

Checking NBT the all-round generic way

The "/execute (if|unless) data" instruction can be used to test if a certain path exists. By using the filter syntax above, you can also check for a single value at a time. Although this generic way is easier to read and probably a tiny bit more efficient, more powerful checks can be done if you use techniques specific to what you're targeting. Before continuing on to the more advanced ways, let's look at the syntax for this generic way.
execute (if|unless) data (block <xyz>|entity <target>|storage <namespace>) <path> ...

# Note that only 1 entity can be selected. Just like in the /data command, use "execute as <targets>"
# to select multiple targets and "data entity @s" afterwards.

# Make chickens lay diamonds.
execute as @e[type=chicken] if data entity @s {EggLayTime:1} run function namespace:folder/summon_diamond_item_and_reset_egglaytime

Checking block NBT

Use "/execute (if|unless) block" to target blocks based on filters. The advantages over the generic way are being able to test block type and block states.
execute (if|unless) block <xyz> <block> ...
# Note: The block argument follows the same syntax as any other block argument (in /setblock for example):
# <block> = <namespace>:<id>[<state key>=<state value>, ...]{<NBT>}

# A mob packaging device, turning any mob on the hopper into a spawn egg
/execute as @e[type=#namespace:folder/supported_mobs] at @s if block ~ ~ ~ hopper{CustomName:'{"text":"Mob Packager"}',TransferCooldown:1} run function namespace:folder/spawn_spawnegg_item_and_copy_nbt_data_and_kill_mob_and_reset_transfercooldown

Checking entity NBT

Use the "nbt" target selector argument to filter targeted entities. The advantage over the generic way is being able to select the entity (to set @s) and apply other target selector arguments at the same time.
@e[nbt={<NBT>}]
# Remember that you can test for entities NOT matching the NBT with "nbt=!{<NBT>}"

# Make all non-charged creepers charged.
execute as @e[type=creeper,nbt={powered:0b}] run data modify entity @s powered set value 1b
If you want to add custom tags to entities so you can target them later, I recommend using the tag system (/tag command, @e[​tag=<tag>] and the "Tags:["tag1"," tag2"]" NBT value).

Checking item NBT

An item is not a separate kind of data container, so you can use the techniques used above to test for item data. In the next examples we're just going to use simple "/data get" commands to visualize the NBT structure, you should be able to build filters from there. No matter how the item is represented, it will have a predefined NBT structure somewhere that could look like this:
{id:"minecraft:diamond_sword",Count:1b,Slot:13b,tag:{Damage:25,display:{Name:'{"text":"My Epic Sword"}'}}}

# Setting the count tag ignores natural stacking limits, although the max value is 127.
# This means that you can make a stack of 16 swords for example.

# The Slot tag is only available in player inventories and containers

# The tag tag contains item specific information.
# In /give <targets> <namespace>:<item>{<NBT>}, the NBT gets placed in the tag tag.
All item specific NBT data can be found here.

On the ground an item is a minecraft:item entity with it's data stored in the Item tag. The Slot tag doesn't exist.
/data get entity @e[type=item,limit=1,sort=nearest] Item.id
/data get entity @e[type=item,limit=1,sort=nearest] Item.tag.Damage
In the player inventory, items can be found in the "SelectedItem", "Inventory" and "EnderItems" tags.
/data get entity @s SelectedItem.id
/data get entity @s SelectedItem.tag.Damage
/data get entity @s Inventory[{Slot:0b}].id
/data get entity @s Inventory[{Slot:0b}].tag.Damage
/data get entity @s EnderItems[{Slot:0b}].id
/data get entity @s EnderItems[{Slot:0b}].tag.Damage
In containers, items can most commonly be found in the "Items" tag, although remember that you can find all (block) entity data here if you want to be sure.
/data get block ~ ~-1 ~ Items[{Slot:0b}].id
/data get block ~ ~-1 ~ Items[{Slot:0b}].tag.Damage
If you want to tag a custom item so you can easily target it later, use any custom NBT string in the tag tag. It's one of the very few places where you can store any NBT you want.
/give @s blaze_rod{display:{Name:'{"text":"MY EPIC WAND"}'},is_custom_wand:1b}
execute as @a[nbt={SelectedItem:{tag:{is_custom_wand:1b}}}] at @s run function namespace:folder/do_something
Do not use the tag system (/tag command, etc.) here, since tags are attached to entities and get lost as soon as the item entity on the ground gets picked up.

Challenge Yourself

After every tutorial, I'll include a section where you are challenged to apply what you've learned. I recommend you playing with what you've learned, it helps you getting familiar with new concepts and be able to find solutions to problems. You don't have to do exactly what's written below, you can always challenge yourself in a different way.

Now you've truly mastered the ins and outs of NBT, let's create a gamble game!
Requirements:
  • If 3 diamonds are thrown on the ground, spawn a gamble chest and invisible armor stand.
  • The chest contains 3 visually identical "tickets" (maybe a paper item?), each with custom NBT to distinguish the actual outcomes.
  • To randomly generate the order of the tickets, spawn a bunch of armor stands (6 possible combinations) tagged with a unique tag and a tag they all have. To randomly pick an option:/tag add @e[​tag=<common_tag>,limit=1,sort=random] <picked_tag>To generate a unique chest for each of the unique tags:
    execute if entity @e[​tag=<unique_tag>,tag=<picked_tag>] run function <function that generates unique chest>Clean up:/kill @e[​tag=<common_tag>]
  • Whenever the player inventory contains a ticket, give them 0, 3 or 6 diamonds based on the value in the custom NBT and clear the tickets from the inventory.
  • If the player/hopper takes an item from the chest (test for its NBT) or the chest gets broken, remove the armor stand, destroy the chest and kill any chest items nearby.

What's next?

In the next tutorial we're going to have a look at a bunch of JSON files that can be used in data packs, from advancements to predicates and loot tables.

Subscribe if you want to get notified of new posts.


Tags

2
08/17/2020 3:00 pm
Level 30 : Artisan Procrastinator
ElectroSheep_
I love that you made this! I feel like not enough extensive guides for datapacks are out there rn. Being a relatively new way to modify the game, I think there's not enough exposure for them! They're such a cool and flexible feature, we need more datapack creators!!!
1
08/10/2020 8:52 pm
Level 15 : Journeyman Network
sadxdd
-_-
1
08/11/2020 6:01 am
Level 55 : Grandmaster Cyborg
Bertiecrafter
.-.
3
08/10/2020 2:10 pmhistory
Level 55 : Grandmaster Cyborg
Bertiecrafter
Thank you pettyGamingHD, Luracasmus, Nitrox Nova, sadxdd, PixieMax, StarLander5772, ElectroSheep_, CorporalKeith and end-user for the diamonds!
Planet Minecraft Logo

Website

© 2010 - 2020
www.planetminecraft.com

Welcome