Selectors

Selectors

The basics

General Syntax

To create a selector, you need to provide the target and the arguments to Selector:
import { Selector } from 'sandstone'

Selector('@a', { /** Arguments */ })
The first part is the targets: it can be @s, @r, @p, @a or @e. In second part are the arguments. That’s where you specify any property that would fit into a vanilla selector:
Selector('@e', { type: 'minecraft:cow', limit: 1, sort: 'random' })

Usage

To use selectors, you need to assign them to a variable, and pass them to a command:
// Kill all cows
const cows = Selector('@e', { type: 'minecraft:cow' })
kill(cows)

// Give 32 diamonds to winners
const winners = Selector('@e', { tag: 'winner' })
give(winners, 'minecraft:diamond', 32)

// Or directly use the selector
give(Selector('@e', { tag: 'winner' }), 'minecraft:diamond', 32)

Special Arguments

While most arguments work exactly like the vanilla ones, some of them have a few particularities.

Scores

General syntax

For the scores argument, you can specify a whole number or a range. Ranges are made using an array: [0, 7] becomes 0..7. If you need an open-ended range (like 8.. or ..19), you can use null or Infinity:
  • [8, null] and [8, +Infinity] both become 8..
  • [null, 19] and [-Infinity, 19] both become ..19
Here is an exemple of a selector that checks if a player has more than 10 kills, less than 50 coins and exactly 0 deaths:
Selector('@s', {
  scores: {
    'kills': [10, +Infinity],
    'coins': [-Infinity, 50],
    'deaths': 0,
  }
})

Matching a Sandstone objective

If you want to use an existing objective, you can use the following ES6 syntax:
const killsObjective = createObjective('kills', 'playerKillCount')

Selector('@s', {
  scores: {
    [killsObjective.name]: [10, +Infinity],
  }
})

Advancements

General syntax

Advancements arguments in Sandstone selectors work precisely like they would in-game, but their complicated syntax deserves a reminder.
To ensure the player has the minecraft:story:/form_obsidian advancement, you would use the following syntax:
Selector('@s', {
  advancements: {
    'story/form_obsidian': true,
  }
})
To select player who wore at least once a full iron armor, you would do:
Selector('@s', {
  advancements: {
    'story/obtain_armor': true,
  }
})

Matching a criteria

To select players who wore at least once a iron helmet, you would do:
Selector('@s', {
  advancements: {
    'story/obtain_armor': {
      'iron_helmet': true,
    },
  }
})
As you can see, matching a single (or several) advancements criteria is done using a nested object. In the end, the syntax could be defined as:
Selector('@s', {
  advancements: {
    // Matches a whole advancement
    '<advancement name>': boolean,
    // Matches some advancement criterion
    '<advancement name>': {
      '<criteria 1>': boolean,
      '<criteria 2>': boolean,
    }
  }
})

Matching a Sandstone Advancement

In order to use a Sandstone advancement in a selector, you can use the following ES6 syntax:
const myAdvancement = Advancement('myAdvancement', ...)

Selector('@s', {
  advancements: {
    [myAdvancement.name]: true,
  }
})

Tags

Entity tags can be either specified as a single string, or an array of strings. If the tag starts by !, the Selector will match entities that don’t have this tag.
// Will compile to `@a[tag=single_tag]`
Selector('@a', { tag: 'single_tag' })

// Will compile to `@a[tag=tag1,tag=tag2]`
Selector('@a', { tag: ['tag1', 'tag2'] })

// Will compile to `@a[tag=!tag1,tag=!tag2]`
Selector('@a', { tag: ['!tag1', '!tag2'] })

Predicates

General Syntax

Just like tags, you can match one or several predicates at once. This is done using a string or an array of strings. You can also invert the selector by adding a !.
// Will compile to `@a[predicate=mypredicate]`
Selector('@a', { predicate: 'mypredicate' })

// Will compile to `@a[predicate=first_predicate,predicate=!second_predicate]`
Selector('@a', { predicate: ['first_predicate', '!second_predicate'] })

Matching a Sandstone Predicate

In order to use a Sandstone predicate in a selector, you just need to use the variable like you would use a string:
const myPredicate = Predicate('mypredicate', ...)

// To match a single predicate
Selector('@s', { predicate: myPredicate })

// To match several predicates
Selector('@s', { predicate: [myPredicate, 'predicate2'] })

Team

The team argument has the same syntax than the vanilla one, but has a built-in feature to check for the lack or the presence of a team. You can use true to check for entities having a team, and false for entities that are not inside a team.
// Check for entities inside the "red" team
Selector('@e', { team: 'red' })

// Check for entities NOT inside the "red" team
Selector('@e', { team: '!red' })

// Check for entities that aren't inside a team
Selector('@e', { team: false })

// Check for entities that are inside a team
Selector('@e', { team: true })

NBTs

The nbt argument works normally, except when inverted. Here, you should use NBT.not(...):
Selector('@e', { nbt: NBT.not({ Invisible: 1 
}) })