Skip to content

NBT Predicate

Endor H edited this page Oct 8, 2023 · 22 revisions

TLDR: Just read the examples in each section


NBT predicates describe restrictions on NBT data. They are used by special recipes to define complex ingredients with NBT requirements.

There are different types of NBT predicates, for each different NBT type.

All predicates have a compound predicate as their root, which is simply a set of NBT Paths mapped to other predicates within braces. Multiple predicates within the compound must be separated by commas (,). A colon (:) is tolerated between paths and predicates, but not required.

{number: 0, text: ""} matches any compound that contains a number node equal to 0 and a text node equal to the empty string.

NBT predicates always allow extra nodes to exist, for example, the compound {number: 0, text: "", extra: 1} would also match the previous predicate.

If you're a mod developer, you may be interested in the following section

Numeric predicates

There are three types of numeric predicates.

Literal value

A literal number value. Only matches that exact number.

  • {n: 2} an n number equal to 2

Comparison predicate

A number preceded by a comparison operator, between the following:

  • = equal to
  • > greater than
  • < smaller than
  • >= greater than or equals
  • <= smaller than or equals
  • != not equals

It's recommended to omit the colon between paths and predicates when the predicate starts with an operator symbol.

  • {n >= 2} an n number greater than or equal to 2
  • {n != 2} an n number distinct from 2
  • {n > -0.5} an n number greater than -0.5

Range predicate

A range consists of two numbers between parentheses or brackets, separated by a tilde symbol ~. Brackets denote a closed range, while parentheses an open range (does not include the limits).

It's possible to mix brackets with parentheses to include one side and not the other. Spaces are tolerated between elements (except between a sign and its number), but not required.

  • {n: [0 ~ 10]} an n number between 0 and 10, including 0 and 10
  • {n: [0 ~ 1)} an n number between 0 and 1, including 0 but not 1
  • {n: [-1 ~ 1]} an n number between -1 and 1, including -1 and 1

Number type predicates

In addition to the above predicate types, all predicate types accept an additional single letter suffix among BSILFD (case-insensitive) to require the value to have a specific numeric type:

  • B Byte
  • S Short
  • I Int
  • L Long
  • F Float
  • D Double

The letter must be specified after another predicate:

  • {n >= 2i} an n Int number greater than or equal to 2
  • {n: 0b} an n Byte number equal to 0
  • {n: (0 ~ 1)f} an n Float number between 0 and 1 (not including 0 nor 1)

String predicates

There are two types of string predicates

Literal value

A literal string value between double quotes.

  • {str: "Steve"} matches a str node with the exact text Steve (case-sensitive)

Regular Expression

A tilde (~) preceding a regular expression between double quotes. Uses Java regex.

As with numbers, it's recommended to omit the colon when the predicate starts with an operator symbol.

  • {str ~ "Steve|Alex"} matches a str with either Steve or Alex
  • {str ~ "Alex.*"} matches a str with any text that starts with Alex
  • {str ~ ".*Steve.*"} matches a str with any text that contains Steve
  • {str ~ "Alex #\d+"} matches a str with any text of the form Alex #d where d is any number (one or more digits)

Keep in mind that, if you're writing these expressions within a JSON string literal, you'll need to escape quotes as \" and backslashes as \\. For example, the last example would be

{
  "predicate": "{str ~ \"Alex #\\d+\"}"
}

While Java regular expressions do not support recursive subpatterns like PCRE regex, they can be used to match many different types of text. You may learn about the syntax of these expressions at their official documentation, but whether you're new to them or not, I recommend using regex101.com to design, test or learn about these expressions (make sure you set the Regex dialect to "Java 8")

If your regex predicate is going to be executed very frequently (maybe a mod evaluates it every tick), you may want to learn about how to use possessive and reluctant quantifiers as well as atomic capture groups to improve matching efficiency, since Java regex doesn't apply automatic expression optimizations as some other regex engines do.

List predicates

List predicates compare a list to another one, using a given comparison mode.

The default comparison mode is equality, but a different mode can be specified with an operator preceding the list.

  • = Exact match: All elements must match in order (you may omit the =)
  • ~ Contain any: The list must contain at least one of the elements from the predicate list
  • > Contain all: The list must contain all of the elements from the predicate list
  • < Subset: The list must only contain elements from the predicate list
  • << Starts with: The list must start with the predicate list
  • >> Ends with: The list must end with the predicate list
  • >< Contains: The list must contain the predicate list at some position

In addition, the elements from the predicate list need not be exact values. They can be arbitrary NBT predicates.

  • {l: ["a", "b", "c"]} matches a string list l with exactly 3 elements, a, b and c in that order
  • {l: ["a", ~"b+", "c"]} matches a string list l with exactly 3 elements, the first and the last being a and c, and the second any amount of bs
  • {l ~ ["a", "b"]} matches a string list l that contains at least one a element or one b element
  • {l > ["a", "b"]} matches a string list l that contains one a element and one b element
  • {l > ["a", "a"]} matches a string list l that contains two a elements
  • {l < ["a", "a", "b", "c"]} matches a string list l if it contains only at most two a elements, one b element and one c element, and no other element
  • {l << ["a", "b"]} matches a string list l if its first element is a and its second element is b
  • {l[0]: "a", l[1]: "b"} equivalent to the one above using indexing paths and multiple predicates
  • {l >> ["y", "z"]} matches a string list l if its last elements are y and then z
  • {l[-2]: "y", l[-1]: "z"} equivalent to the one above using indexing paths and multiple predicates
  • {l >< ["b", "d"]} matches a string list l if it contains a b element followed by a d element
  • {l > [>= 2, >= 4]} matches a number list l if it contains one element that is greater than or equal to 2 and another element greater than or equal to 4. In particular, it would not match [4] alone, even though it matches both predicates, because each predicate must match a different element

Note that some comparison modes may be extremely inefficient if their subpredicates are expensive to compute. For example, the > (contains all) comparison mode may need to backtrack to check if predicates match in a different order.

Also, keep in mind that NBT lists only support a single type for all its elements. This restriction also applies to list predicates.

  • {l: [0l, [1~3], >=4]} the first predicate enforces numbers of Long type, so the list must either be a Long Array or a List of Long nodes. Matches a Long number list l with exactly three elements, the first being 0, the second between 1 and 3 (including 1 or 3), and the third greater than or equal to 4

Compound predicates

If you've read the above sections, you've already seen some compound predicates, as they must be the root of any NBT predicate. However, compound predicates support a few more complex uses.

Long paths

Compound predicates are not limited to simple name paths. You may use any NBT Path as key for a predicate.

  • {Fireworks.Flight >= 3} only matches if the NBT contains a compound named Fireworks that contains a number named Flight which is greater than or equal to 3

Nested predicates

You may specify a compound predicate as a subpredicate. This can be useful to shorten common paths.

  • {Fireworks: {Flight >= 3}} equivalent to the predicate above
  • {Very.Long.Path: {Shared: "Between", Multiple: "Paths"}} group common predicates in a compound predicate
  • {Very.Long.Path.Shared: "Between", Very.Long.Path.Multiple: "Paths"} equivalent to the predicate above

Repeated paths

You may repeat a path to specify multiple predicates for it, which must all match.

  • {number >= 0, number <= 10} a number between 0 and 10, including 0 and 10
  • {number: [0 ~ 10]} equivalent to the predicate above

Negated predicates

You may precede an NBT path with an exclamation symbol ! to negate its predicate. This can be combined with repeated paths to define complex predicates.

  • {number: [0 ~ 3], !number: (1 ~ 2)} matches a number within [0 ~ 1] or within [2 ~ 3]

Note that negated predicates do not require the path to exist. If a path doesn't exist then it can't fail the predicate, and thus it will pass the negated predicate. For example, the following two predicates are different:

  • {!number = 0} matches any NBT unless it contains a number named number that is equal to 0
  • {number != 0} matches any NBT that contains a number named number, only if it is not equal to 0

Both would match {number: 1}, but the first would match {not: "a number"}, and the second would not.


The following section is only relevant for developers.

NBTPredicate methods

NBT predicate objects can be created with the static NBTPredicate.parse(string) method.

NBT predicate objects support the following operations:

  • generateValid() attempts to generate a valid tag that would match this predicate. Note that this can be impossible, as you may easily define contradictory predicates such as {n = 0, n != 0}. This method cannot be used as a satisfiability test, it only does a best effort search for a valid pattern, without any backtracking.
  • test(nbt) tests this pattern on an NBT value
  • test(itemStack) tests this pattern on an item stack's NBT tag, if it has one
  • test(entity) tests this pattern on an entity's persistent data NBT tag
  • isUnique() whether this predicate only allows one matching value. Unique patterns are handled specially when comparing lists to avoid unnecessary backtracking.
  • getDisplay() pretty print
  • getDisplay(predicateStyle) pretty print with a given NBTPredicate.Style
  • write(byteBuffer) serialize to a packet buffer, to send over the network

You may also use the static method NBTPredicate.read(byteBuffer) to deserialize a predicate from a packet.

Clone this wiki locally