Functions

Functions represent reusable pieces of logic callable in a datapack.

Create a function with the function builder:

function("my_function") {
	say("Hello world!")
}
Kotlin

Then in game, call the function with /function my_datapack:my_function.

To call functions from other datapacks, see Bindings.

The function builder returns a FunctionArgument object that you can reuse to call the function from other functions:

val myFunction = function("my_function") {
	say("Hello world!")
}

function("my_second_function") {
	function(myFunction)
}
Kotlin

You can also package this pattern into reusable Function extensions:

fun Function.myFunction() = function("my_function") {
	say("Hello world!")
}

load {
	function(myFunction())
}
Kotlin

That helper may be called from several places without worry. Kore is optimized for recreating the same named function, so using this pattern stays effectively instant while keeping your code easy to organize.

If you only want to reuse a small block of commands without generating a separate /function, prefer a regular extension instead:

fun Function.saySomething() {
	say("yay")
	say("also, yay")
}

load {
	saySomething()
}
Kotlin

Tags

You can set the tag of the current function you're working in with the setTag function:

function("my_function") {
	setTag(tagFile = "load", tagNamespace = "minecraft")
}
Kotlin

This will add the function to the minecraft:load tag.

But you have simpler builders for the most common tags:

load {
	say("Hello world!")
}

tick {
	execute {
		ifCondition(myPredicate)

		run {
			say("Hello world!")
		}
	}
}
Kotlin
  • load tag: minecraft:load
  • tick tag: minecraft:tick

This will create functions with randomly generated names, but you can also specify the name of the function:

load("my_load_function") {
	say("Hello world!")
}
Kotlin

Commands

Many common commands have convenience builders like say, teleport, etc. See the Commands page for a comprehensive guide with examples.

For example:

function("commands") {
	say("Hello!") // say command
	teleport(player("Steve"), vec3(100.0, 64.0, 100.0)) // teleport command
}
Kotlin

You can also build raw command strings and execute them:

addLine("say Hello from raw command!")
Kotlin

Note: This is not recommended, but can be useful for commands not yet supported by the DSL, or if you use Macros.

Available Commands

All commands from the version cited in the README are available. For detailed documentation on each command, see Commands.

Custom Commands

You can pretty easily add new commands by creating your own builders. For example, imagine you created a mod that adds a new command /my_command that takes a player name and a message as arguments.

You can create a builder for this command like this:

import io.github.ayfri.kore.functions.Function

fun Function.myCommand(player: String, message: String) = addLine(command("my_command", literal(player), literal(message)))
Kotlin

Then you can use it like any other command:

function("my_function") {
	myCommand("Steve", "Hello!")
}
Kotlin

Argument types (selectors, positions, resource locations, ranges...) go into command() as they are, and so do the enums of Kore's commands like Gamemode, which are EnumArguments written as their lowercase name:

fun Function.myMode(target: EntityArgument, mode: Gamemode) = addLine(command("my_mode", target, mode))
Kotlin

Your own enum becomes an argument the same way, overriding asString() when the game names an entry differently:

enum class Speed : EnumArgument {
	SLOW,
	FAST,
	VERY_FAST;

	override fun asString() = name.lowercase().replace("_", "")
}
Kotlin

See the code of the repository for more examples.
Link to time command.
Link to weather command.

Complex Commands

Some commands are more complex and require more than just a few arguments. For example, the execute or data commands.

In that case, you can use complex builders that includes all the arguments of the command. But the syntax may vary depending on the command, so pairing this page with the broader Commands reference and selector-heavy examples from Selectors is often helpful.

An example of the execute command:

execute {
	asTarget(allEntities {
		limit = 3
		sort = Sort.RANDOM
	})

	ifCondition {
		score(self(), "test") lessThan 10
		predicate(myPredicate)
	}

	run { // be sure to import the run function, do not use the one from kotlin.
		teleport(entity)
	}
}
Kotlin

You can use predicates in the ifCondition block to check complex conditions. See the Predicates documentation for more details.

You may also have commands where you can create "contexts".

An example of the data command:

data(self()) {
	modify("Health", 20)
	modify("Inventory[0]", Items.DIAMOND_SWORD)
}
Kotlin

Macros

See Macros.

Generated Functions

The same way the unnamed load and tick builders generate their own functions, the execute builder also generates a function if you call multiple commands inside the run block.

execute {
	run {
		say("Hello world!")
		say("Hello world2!")
	}
}
Kotlin

This will generate a function that will be called by the execute command.

Note: The generated functions will be generated inside a folder named generated_scopes in the functions folder. You can change the folder to whatever you want in Configuration.

Note: The generated name follows the pattern generated_<hash>, where <hash> is a hash of the function body (load_<hash>, tick_<hash> and schedule_<hash> for the other builders). The same body always gets the same name, on every run and platform, and two builders producing the same body reuse a single function.

Your own builders get the same naming with hashedGeneratedFunction(prefix), which names the function <prefix>_<hash of its body>. generatedFunctionName(prefix, lines) returns that name alone, for a function you register yourself:

val onClick = hashedGeneratedFunction("on_click") {
	say("clicked")
}
// data/<namespace>/function/generated_scopes/on_click_<hash>.mcfunction
Kotlin

Two generated functions can't share a path with different bodies: generatedFunction("init") called twice with two different blocks throws an IllegalStateException instead of silently keeping only the first one. The same goes for any two files of the pack at generation time, two function("init") or two resources with one file name writing different contents throw instead of dropping one of them.

If you want to turn that into an explicit project pattern, the Cookbook shows how to wrap reusable logic in Function extensions with or without dedicated generated functions.

Debugging

You have multiple ways to debug your functions. First, a debug function is available, it is pretty much the same as tellraw but always displaying the message to everyone.

function("my_function") {
	debug("Hello world!", Color.RED)
}
Kotlin

You also have a debug block for printing a log message to the console for each command you call inside the block.

function("my_function") {
	debug {
		say("hello !")
	}
}
Kotlin

This will add a command call to tellraw command, writing the exact command generated, clicking on the text will also call the command. Example of what is generated:

say hello !
tellraw @a {"type":"text","click_event":{"action":"suggest_command","command":"/say hello !"},"hover_event":{"action":"show_text","value":{"type":"text","color":"gray","italic":true,"text":"Click to copy command"}},"text":"/say hello !"}
Mcfunction

Calling startDebug() yourself, without the matching endDebug() the debug block adds, keeps debug mode on until the pack is written: every command gets its log message, and the written file also starts and ends with a message naming the function. toString() and lines never contain those two markers, only the written file does.

function("my_function") {
	startDebug()
	say("hello !")
}
Kotlin
tellraw @a [{"type":"text","color":"gray","italic":true,"text":"Running function "},{"type":"text","bold":true,"click_event":{"action":"run_command","command":"function my_datapack:my_function"},"color":"white","hover_event":{"action":"show_text","value":{"type":"text","color":"gray","italic":true,"text":"Click to execute function"}},"italic":true,"text":"my_datapack:my_function"}]
say hello !
tellraw @a {"type":"text","click_event":{"action":"suggest_command","command":"/say hello !"},"hover_event":{"action":"show_text","value":{"type":"text","color":"gray","italic":true,"text":"Click to copy command"}},"text":"/say hello !"}
tellraw @a [{"type":"text","color":"gray","italic":true,"text":"Finished running function "},{"type":"text","bold":true,"click_event":{"action":"run_command","command":"function my_datapack:my_function"},"color":"white","hover_event":{"action":"show_text","value":{"type":"text","color":"gray","italic":true,"text":"Click to execute function"}},"italic":true,"text":"my_datapack:my_function"}]
Mcfunction

You can call the command by clicking on the debug texts added.

Also running toString() in a function will return the generated function as a string, so you can manipulate it as you want.

See Also

  • Commands - Complete command reference
  • Macros - Dynamic command arguments
  • Tags - Function tags for load and tick events
  • Cookbook - Practical patterns for reusable function helpers

External Resources