Creating a Minecraft function

As you saw earlier, you can create a Minecraft function using MCFunction:
// Create a Minecraft function inside the default namespace,
// named "main.mcfunction"
MCFunction('main', () => {...})
By specifying only the function's name, it will be created inside the default namespace. However, you can specify it yourself:
// Create a Minecraft function inside the `mydatapack` namespace,
// named "main.mcfunction"
MCFunction('mydatapack:main', () => {...})
Here, your function will be created inside the mydatapack namespace.

Calling a Minecraft function

Sandstone promotes reusable block of commands. To make this possible, you have the ability to call other functions.
Your first possibility is to call another Minecraft function, just like you would in a normal Minecraft Datapack. To achieve this, you need to assign your MCFunction to a variable:
const main = MCFunction('main', () => {
  say("This is the main function.")
  give('@a', 'minecraft:diamond')

MCFunction('callMainThreeTimes', () => {
This will result in the following functions:
# default:main
say This is the main function
give @a minecraft:diamond

# default:callMainThreeTimes
function default:main
function default:main
function default:main
This approach has several advantages:
  • Commands are not duplicated. This results in a lighter datapack.
  • The function can be recursive.
  • It has a meaningful name in Minecraft (here, default:main).

Minecraft function options

You can specify different options, other than lazy, for your Minecraft functions.
Whether the function should run when the datapack is loaded or reloaded. Uses minecraft:load.
Whether the function should run each tick. Uses minecraft:tick.
number | Time
Calls the function at a given frequency.
The function tags to apply to this function.
Whether the function should only be created if called from another function.
However, it has drawbacks:
  1. It cannot take parameters. If you want to have a generic set of commands, that changes depending on some parameters, this is not possible.
  1. It will create a .mcfunction file for each functions, even if they are never called. It makes it hard to share your functions with other peoples. If your library contains 100 helper functions, all datapacks using your library will include those 100 functions - even if they only use one. This is mainly a problem for libraries - not for data packs.
The first drawback can be solved using inline functions, and the second one using lazy functions.

Inline functions

Inline functions are normal, Javascript functions. They group up related commands in a reusable, readable piece of code. Inline functions do not create additional MCFunctions. They are inlined when they are called.
Let's take a simple example using inline functions:
function giveDiamonds(count: number) {
  give('@a', 'minecraft:diamond', count)
  say('I gave', count, 'diamonds to everyone!')

MCFunction('main', () => {
This results in:
## Function default:main
give @a minecraft:diamond 64
say I gave 64 diamonds to everyone!
give @a minecraft:diamond 32
say I gave 32 diamonds to everyone!
As you can see, the commands from the giveDiamonds function are directly written inside main. Inline functions are a very efficient way to group up related commands, which helps writing a clean and logical data pack.

Lazy Minecraft Functions

This feature is mostly for library creators!
A lazy function will be created only if another function calls it. It prevents Sandstone creating unnecessary functions.
const useless = MCFunction('useless', () => {
  say('This function is not used anywhere')
}, { lazy: true })

MCFunction('main', () => {
  say('Main function')
Results in:
# default:main
say Main function
As you can see, the useless function has not been created. On the other hand, this:
const useless = MCFunction('useless', () => {
  say('This function is not used anywhere')
}, { lazy: true })

MCFunction('main', () => {
  say('Calling "useless"...')
Results in:
# default:useless
say This function is not used anywhere

# default:main
say Calling "useless"...
function default:useless
As you can see, the useless function has been created. This feature is mostly useful for a library exporting a lot of functions.

Waiting between commands

The basics

Sandstone allows you to wait a fixed amount of time between some commands, without having to manually declare a new function each time. This has several purpose: dialogs, event scheduling, animations etc... Under the hood, this uses the /schedule command: however, all the complexity is abstracted away.

Basic syntax

To wait a specific time between commands, there are two things to do:
  1. Change your function to an asynchronous function, by adding the async keyword
  1. Add the await sleep(delay) line between your commands.
delay can be a number of ticks, or a time string like '1t', '1s', '1d'...
Here is a minimal syntax:
MCFunction('minimal', async () => {
  say('This command runs now')
  await sleep('5s')
  say('This command runs 5s later')
There are two things to notice. First, our function is now asynchronous. They async keyword has been added before the parameters list:
MCFunction('minimal', async () => {...})
Second, we await the sleep function.


You could simulate a dialog like this:
MCFunction('council', async () => {
  tellraw('@a', '[Aragorn] - You have my sword.')
  await sleep(10) // sleep 10 ticks, half a second.

  tellraw('@a', '[Legolas] - And my bow.')
  await sleep('1s') // sleep 1 second

  tellraw('@a', '[Gimly] - AND MY AXE!')
This example would compile to the following resources:
## Function default:council
tellraw @a "[Aragorn] - You have my sword."
schedule function default:council/__sleep 10 replace
schedule function default:council/__sleep_2 1s replace

## Function default:council/__sleep
tellraw @a "[Legolas] - And my bow."

## Function default:council/__sleep_2
tellraw @a "[Gimly] - AND MY AXE!"