What are functions?

Like any language, functions in Minr Script Code are reusable pieces of code which perform a specific task. They allow you to encapsulate a set of instructions and give it a name, making your code more organized, readable, and maintainable. Functions can take input arguments, perform operations, and return a value if needed.

In this guide, we’re going to make a function which takes in a player and a set of block coordinates and determines how far away the player is from the coordinates.

Why are functions useful?

How do you create a function?

To create a new function in MSC, you use the following syntax:

/function define <namespace> <returnType> <functionName>(<type_1> <argument_1>, ...)

We’re now ready to create our command!

/function define function_demo Double getDistance(Player player, Int[] coordinates)

This creates a function named getDistance in the function_demo namespace, which takes the two arguments we discussed. Great! But now we need our function to actually do something, and for that we’ll need to write the script.

Writing a function script

Writing the script for a function body is very similar to writing an interact script, except the variables that you have access to are slightly different by default. Recall that in an interact script, you can access

These variables are part of the local namespace: they are pre-populated, accessible variables which you can access without needing an @using line or a namespace:: and which are unique to the script being run. This is also where variables created using @define are located.

In a function, the local namespace is instead populated with the arguments you defined earlier. Here, that means we have access to

Now, let’s write our script:

# The difference between the player's X/Y/Z ordinate and the provided one@define Double deltaX = player.getX() - coordinates[0]@define Double deltaY = player.getY() - coordinates[1]@define Double deltaZ = player.getZ() - coordinates[2]# Calculate the distance based on these components@define Double distance = (deltaX^2 + deltaY^2 + deltaZ^2)^0.5@return distance

Here, the distance is calculated as x2+y2+z2\sqrt{x^2 + y^2 + z^2}, by Pythagoras’ Theorem. On the last line, we use the @return keyword. You might recognise this from normal scripts as ending the execution of a program. It still does this in functions, but it has a more important purpose: it returns the value of the distance as the function’s output. We’ll see what this means when we use the function.

Importing functions

We can import this function using the command

/script import function function_demo getDistance https://paste.minr.org/ababababab

or alternatively add it line-by-line:

/script create function function_demo getDistance @define Double deltaX = player.getX() - coordinates[0]...

This is similar to the process of importing any other script. For short, you can use

/s i f namespace functionName https://paste.minr.org/dedededede

Calling functions

Okay, we have a function which calculates the distance between a player and a point. So what? How do we even use it?

The process of using a function is known as calling it. In MSC, this uses the @var keyword.

@define Double dist@var dist = function_demo::getDistance(player, Int[123, 45, 678])@player You are {{dist}} metres away from the point (123, 45, 678)!

The function_demo:: denotes our namespace. Alternatively, if we’d used @using function_demo at the start of this script, we could have omitted it.

Here, since our function returns a value, we probably want to use that value. That’s why we set our variable to the output of function_demo::getDistance. If we weren’t using a value (eg. if our function was Void), we’d use @var function_demo::getDistance(...) without any dist = or such.

Note that the player in the function does not have to be the same as the player in the local namespace of the calling script. For example,

@define Double dist@var dist = function_demo::getDistance(Player("AK1089"), Int[123, 45, 678])@player AK1089 is {{dist}} metres away from the point (123, 45, 678)!

does the same thing no matter who runs it.

More complex examples

Here’s a example from real code I’ve written for a map:

# FUNCTION | getBookPages(Item book) -> String[]@define String bookString = book.string()# extract the pages from the book string@define Int startIndex = bookString.indexOf("pages=[") + 7@define String pagesString = bookString.substring(startIndex, bookString.length() - 3)# split the pages string by ", " to get individual pages@define String[] rawPages = pagesString.split(", ")# process each raw page to remove leading "# file <filename>" and trailing "\\n"@define String[] pages = String[]@for Int i in list::range(0, rawPages.length())    @define String rawPage = rawPages[i]    @define Int newlineIndex = rawPage.indexOf("\\n")    @if newlineIndex != -1        @var pages[i] = rawPage.substring(newlineIndex + 2, rawPage.length() - 2)    @else        @var pages[i] = rawPage    @fi@done@return pages

This function allowed me to get the pages of a written book the player was holding based on the Item object corresponding to it. This saved a lot of time, as in other scripts I could simply use

@define String[] bookPages = akrobots::getBookPages(player.getItemInMainHand())# do stuff with the pages ...

rather than rewriting the code to extract the book pages every time. Also, say later I want to change the format of the book, and use a §§ instead of a #. If I hadn’t used a function, I would have had to change every single occurence across hundreds of scripts, but now I can just change one number in line 17 of this script.

list::range

You’ve probably encountered at least one function before, possibly without realising it! The range function in the list namespace is a function, which we could recreate (if we had a forever keyword) as follows:

# /function define list Int[] range(Int start, Int end)@define Int[] numbers = Int[]@define Int x = start# This doesn't really exist, but pretend it does! @forever    # If we reach the end of the specified range, we can stop and feed back our generated list    @if x >= end        @return numbers    @fi    # Otherwise, add our current number to the list and increase it by 1 for the next round    @var numbers.append(x)    @var x = x + 1@done

This creates a list of numbers, starting from the first argument (the start) and finishing when we hit the second argument (the end).

Shortcuts

Let’s take our previous script, which tells the player how far from a point they are.

@define Double dist@var dist = function_demo::getDistance(player, Int[123, 45, 678])@player You are {{dist}} metres away from the point (123, 45, 678)!

We can shorten this to:

@player You are {{function_demo::getDistance(player, Int[123, 45, 678])}} metres away from the point (123, 45, 678)!

{{•}} is the evaluation operator: it automatically evaluates everything you put inside it. Here, it’s evaluating the expression function_demo::getDistance(player, Int[123, 45, 678]), which is equal to the distance from the player to (123,45,678)(123, 45, 678).

Test yourself

How would you define a function in the function_demo namespace which:

How would you call the following functions in the function_demo namespace from an interact script?

More advanced notes and quirks

Documentation

The full MSC documentation is available at ReadTheDocs. If there are any errors here, please let me know on Discord!