Skip to main content

ItemPredicate

Overview​

ItemPredicate is a builder for creating item predicates that test for items in inventories, containers, and slots. It provides a fluent API for matching items by type, components, and properties.

Item predicates are used in:

  • clear command - remove items from player inventories
  • execute if items - test for items in entity/block inventories
  • Flow conditions (_.items) - conditional logic based on inventory contents
  • Item model definitions - conditional models based on item properties
import { ItemPredicate, NBT } from 'sandstone'

// Simple item match
const diamondSword = ItemPredicate('minecraft:diamond_sword')

// Item with component tests
const enchantedSword = ItemPredicate('minecraft:diamond_sword')
.has('minecraft:enchantments')
.without('minecraft:damage')

// Any item with count constraint
const smallStack = ItemPredicate('*').count({ max: 16 })

// Tag with negation
const undamagedSword = ItemPredicate('#minecraft:swords').without('minecraft:damage')

Creating Predicates​

ItemPredicate accepts an item type as its first argument:

// Specific item
ItemPredicate('minecraft:diamond')

// Item tag (prefixed with #)
ItemPredicate('#minecraft:logs')

// Tag object
const swordsTag = Tag('item', 'mypack:special_swords', ['minecraft:diamond_sword'])
ItemPredicate(swordsTag)

// Wildcard (any item)
ItemPredicate('*')

Component Tests​

Existence Tests​

Check whether a component exists on an item:

// Has custom name
ItemPredicate('minecraft:diamond_sword').has('minecraft:custom_name')

// Does not have damage
ItemPredicate('minecraft:iron_pickaxe').without('minecraft:damage')

// Has enchantments but no custom data
ItemPredicate('*')
.has('minecraft:enchantments')
.without('minecraft:custom_data')

Exact Matching​

Test for exact component values using the = operator:

// Item with zero damage
ItemPredicate('minecraft:diamond_sword').exact('minecraft:damage', NBT.int(0))

// Item with specific unbreakable value
ItemPredicate('*').exact('minecraft:unbreakable', {})

// Negated exact match (item with non-zero damage)
ItemPredicate('minecraft:diamond_sword').notExact('minecraft:damage', NBT.int(0))

Sub-Predicate Matching​

Match against component predicates using the ~ operator. This allows partial matching and range tests:

// Sword with Sharpness enchantment at level 3+
ItemPredicate('minecraft:diamond_sword')
.match('minecraft:enchantments', [{
enchantments: 'minecraft:sharpness',
levels: { min: NBT.int(3) }
}])

// Item with low durability
ItemPredicate('*').match('minecraft:damage', {
durability: { max: NBT.int(10) }
})

// Item with specific custom data field
ItemPredicate('*').match('minecraft:custom_data', {
player_id: NBT.int(123)
})

// Negated match (sword without Curse of Vanishing)
ItemPredicate('minecraft:diamond_sword')
.notMatch('minecraft:enchantments', [{
enchantments: 'minecraft:curse_of_vanishing'
}])

Count Tests​

Test stack count ranges:

// Exactly 64 items
ItemPredicate('*').count(64)

// Between 1 and 16 items
ItemPredicate('minecraft:diamond').count({ min: 1, max: 16 })

// At least 32 items
ItemPredicate('*').count({ min: 32 })

// At most 16 items
ItemPredicate('*').count({ max: 16 })

OR Logic​

Create OR groups where any condition can match. Use the or() method with builder callbacks:

// Items without damage OR with damage=0 (pristine items)
ItemPredicate('*').or(
i => i.without('minecraft:damage'),
i => i.exact('minecraft:damage', NBT.int(0))
)

// Diamond or gold swords
ItemPredicate('*').or(
i => i.exact('minecraft:item', 'minecraft:diamond_sword'),
i => i.exact('minecraft:item', 'minecraft:golden_sword')
)

// Multiple conditions in OR
ItemPredicate('#minecraft:tools').or(
i => i.has('minecraft:enchantments'),
i => i.without('minecraft:damage'),
i => i.match('minecraft:custom_data', { special: true })
)

The resulting predicate uses | to join OR conditions: *[!minecraft:damage|minecraft:damage=0]

Using with Commands​

Clear Command​

Remove items from player inventories:

import { clear, ItemPredicate, NBT } from 'sandstone'

// Clear specific item
clear('@p', 'minecraft:dirt')

// Clear with predicate - remove enchanted swords
clear('@p', ItemPredicate('minecraft:diamond_sword').has('minecraft:enchantments'))

// Clear damaged tools
clear('@a', ItemPredicate('#minecraft:tools').has('minecraft:damage'))

// Clear with quantity limit - remove up to 64 cobblestone
clear('@p', ItemPredicate('minecraft:cobblestone'), 64)

// Clear any item with low durability
clear('@a', ItemPredicate('*').match('minecraft:damage', {
durability: { max: NBT.int(10) }
}))

Execute If Items​

Test for items in inventories:

import { execute, ItemPredicate, _, Objective, abs } from 'sandstone'

// Check if player has diamonds in inventory
_.if(execute.if.items.entity('@p', 'inventory.*', 'minecraft:diamond'), () => {
// Commands here run if player has diamonds
})

// Check for enchanted sword in main hand
_.if(
execute.if.items.entity('@s', 'weapon.mainhand',
ItemPredicate('minecraft:diamond_sword').has('minecraft:enchantments')
),
() => {
// Player is holding an enchanted sword
}
)

// Count items in inventory
const score = Objective.create('item_count', 'dummy')('@s')
execute.store.result.score(score).if.items.entity('@p', 'inventory.*',
ItemPredicate('minecraft:diamond')
)

// Check chest for specific items
_.if(
execute.if.items.block(abs(0, 64, 0), 'container.*',
ItemPredicate('minecraft:diamond').count({ min: 10 })
),
() => {
// Chest has at least 10 diamonds
}
)

Flow Conditions​

Use ItemPredicate with Flow's _.items condition to create cleaner, more readable conditional logic:

import { _, ItemPredicate, say, give, abs, NBT } from 'sandstone'

// Check if player has diamonds in inventory
_.if(_.items.entity('@p', 'inventory.*', 'minecraft:diamond'), () => {
say('Player has diamonds!')
})

// Check for enchanted item in main hand
_.if(
_.items.entity('@s', 'weapon.mainhand',
ItemPredicate('minecraft:diamond_sword').has('minecraft:enchantments')
),
() => {
say('You have an enchanted sword!')
}
)

// Check chest contents
_.if(
_.items.block(abs(0, 64, 0), 'container.*',
ItemPredicate('minecraft:diamond').count({ min: 10 })
),
() => {
say('Chest has at least 10 diamonds')
}
)

// Complex condition with else
_.if(
_.items.entity('@s', 'inventory.*',
ItemPredicate('#minecraft:tools').match('minecraft:damage', {
durability: { max: NBT.int(10) }
})
),
() => {
say('You have a nearly broken tool!')
give('@s', 'minecraft:diamond', 1)
}
).else(() => {
say('All tools are in good condition')
})

Negating Predicates:

Use .notMatch and .notExact to create negative conditions directly on the predicate:

import { _, ItemPredicate, say, NBT } from 'sandstone'

// Check for items WITHOUT a specific enchantment
_.if(
_.items.entity('@s', 'inventory.*',
ItemPredicate('minecraft:diamond_sword')
.notMatch('minecraft:enchantments', [{
enchantments: 'minecraft:curse_of_vanishing'
}])
),
() => {
say('You have a sword without Curse of Vanishing!')
}
)

// Check for items with non-zero damage (damaged items)
_.if(
_.items.entity('@s', 'inventory.*',
ItemPredicate('#minecraft:tools').notExact('minecraft:damage', NBT.int(0))
),
() => {
say('You have a damaged tool!')
}
)

// Combine positive and negative conditions
_.if(
_.items.entity('@s', 'weapon.mainhand',
ItemPredicate('minecraft:diamond_sword')
.has('minecraft:enchantments')
.notMatch('minecraft:enchantments', [{
enchantments: 'minecraft:curse_of_binding'
}])
),
() => {
say('Enchanted sword without curses!')
}
)

Slot Selectors:

Entity slot patterns for _.items.entity():

  • inventory.* - All inventory slots
  • hotbar.* - Hotbar slots (0-8)
  • inventory.0 - Specific inventory slot
  • weapon.mainhand / weapon.offhand - Hands
  • armor.head / armor.chest / armor.legs / armor.feet - Armor slots
  • enderchest.* - Ender chest contents
  • horse.chest - Horse armor/chest slot
  • horse.* - All horse/donkey/llama inventory slots

Container slot patterns for _.items.block():

  • container.* - All container slots
  • container.0 - Specific container slot

Item Model Definitions​

Override vanilla item models to display different models based on item properties. This is useful for showing custom models when items have specific enchantments, custom data, or other components:

import { ItemPredicate, ItemModelDefinition } from 'sandstone'

// Override diamond sword - show custom model when enchanted and undamaged
ItemModelDefinition('minecraft:diamond_sword',
ItemPredicate('minecraft:diamond_sword')
.has('minecraft:enchantments')
.without('minecraft:damage')
.model()
.onTrue('mypack:item/enchanted_pristine_sword')
.onFalse('minecraft:item/diamond_sword')
)

// Override diamond - show special model for quest items
ItemModelDefinition('minecraft:diamond',
ItemPredicate('minecraft:diamond')
.match('minecraft:custom_data', { quest_item: true })
.model()
.onTrue('mypack:item/quest_diamond')
.onFalse('minecraft:item/diamond')
)

Complex Examples​

Multi-Condition Predicates​

Combine multiple tests with AND logic (each method call):

import { ItemPredicate, NBT } from 'sandstone'

// Enchanted, undamaged diamond sword with custom name
const specialSword = ItemPredicate('minecraft:diamond_sword')
.has('minecraft:enchantments')
.has('minecraft:custom_name')
.without('minecraft:damage')

// Tool with specific enchantment and high durability
const goodTool = ItemPredicate('#minecraft:tools')
.match('minecraft:enchantments', [{
enchantments: 'minecraft:unbreaking',
levels: { min: NBT.int(2) }
}])
.match('minecraft:damage', {
durability: { min: NBT.int(50) }
})

Combining AND and OR​

// Swords that are either enchanted OR undamaged
const usefulSword = ItemPredicate('#minecraft:swords').or(
i => i.has('minecraft:enchantments'),
i => i.without('minecraft:damage')
)

// Items with custom data matching one of several patterns
const markedItem = ItemPredicate('*')
.or(
i => i.match('minecraft:custom_data', { type: 'quest' }),
i => i.match('minecraft:custom_data', { type: 'reward' }),
i => i.match('minecraft:custom_data', { type: 'special' })
)

Practical Use Cases​

import { clear, _, ItemPredicate, say, NBT } from 'sandstone'

// Clean up damaged tools from inventory
clear('@a', ItemPredicate('#minecraft:tools').match('minecraft:damage', {
durability: { max: NBT.int(5) }
}))

// Check if player has required quest item
_.if(
_.items.entity('@s', 'inventory.*',
ItemPredicate('minecraft:diamond').match('minecraft:custom_data', {
quest_id: 'main_quest_1'
})
),
() => {
say('Quest item found!')
}
)

// Remove all non-stackable items
clear('@a', ItemPredicate('*')
.exact('minecraft:max_stack_size', NBT.int(1))
)

Component Types​

ItemPredicate uses generated types from Minecraft data. Common components include:

Existence/Exact (has, without, exact):

  • minecraft:custom_name
  • minecraft:lore
  • minecraft:enchantments
  • minecraft:damage
  • minecraft:unbreakable
  • minecraft:custom_data
  • minecraft:dyed_color
  • minecraft:trim

Sub-predicates (match):

  • minecraft:enchantments - enchantment list with level ranges
  • minecraft:damage - durability ranges
  • minecraft:custom_data - NBT pattern matching
  • minecraft:stored_enchantments - for enchanted books
  • minecraft:potion_contents - potion types and effects

TypeScript autocomplete will show all available components based on the Minecraft version configured in sandstone.config.ts.

Technical Details​

String Representation​

ItemPredicate compiles to Minecraft's item predicate syntax:

import { ItemPredicate, NBT } from 'sandstone'

ItemPredicate('minecraft:diamond')
// → minecraft:diamond

ItemPredicate('minecraft:diamond_sword').has('minecraft:enchantments')
// → minecraft:diamond_sword[minecraft:enchantments]

ItemPredicate('*').without('minecraft:damage')
// → *[!minecraft:damage]

ItemPredicate('*').or(
i => i.without('minecraft:damage'),
i => i.exact('minecraft:damage', NBT.int(0))
)
// → *[!minecraft:damage|minecraft:damage=0]

ItemPredicate('#minecraft:swords')
.has('minecraft:enchantments')
.count({ min: 1 })
// → #minecraft:swords[minecraft:enchantments,count~{min:1}]

Method Chaining​

All predicate methods return this, allowing fluent chaining:

ItemPredicate('minecraft:diamond_sword')
.has('minecraft:enchantments')
.without('minecraft:damage')
.has('minecraft:custom_name')
.count({ max: 1 })

Type Safety​

ItemPredicate leverages TypeScript's type system:

  • Component names are type-checked against Minecraft's registry
  • Component values are validated against their types
  • Predicate values have stricter types than exact values
  • Tag types are properly distinguished from item types
import { ItemPredicate, NBT } from 'sandstone'

// ✓ Valid
ItemPredicate('minecraft:diamond_sword').exact('minecraft:damage', NBT.int(0))

// ✗ Type error - 'invalid_component' doesn't exist
ItemPredicate('minecraft:diamond_sword').exact('invalid_component', NBT.int(0))