# Fundamental components of `scarpet` programming language. Scarpet (a.k.a. Carpet Script, or Script for Carpet) is a programming language designed to provide the ability to write custom programs to run within Minecraft and interact with the world. This specification is divided into two sections: this one is agnostic to any Minecraft related features and could function on its own, and CarpetExpression for Minecraft specific routines and world manipulation functions. # Synopsis
script run print('Hello World!')
or an OVERLY complex example:
/script run
block_check(x1, y1, z1, x2, y2, z2, block_to_check) ->
(
l(minx, maxx) = sort(l(x1, x2));
l(miny, maxy) = sort(l(y1, y2));
l(minz, maxz) = sort(l(z1, z2));
'Need to compute the size of the area of course';
'Cause this language doesn\'t support comments in the command mode';
xsize = maxx - minx + 1;
ysize = maxy - miny + 1;
zsize = maxz - minz + 1;
total_count = 0;
loop(xsize,
xx = minx + _ ;
loop(ysize,
yy = miny + _ ;
loop(zsize,
zz = minz + _ ;
if ( block(xx,yy,zz) == block_to_check,
total_count += ceil(rand(1))
)
)
)
);
total_count
);
check_area_around_closest_player_for_block(block_to_check) ->
(
closest_player = player();
l(posx, posy, posz) = query(closest_player, 'pos');
total_count = block_check( posx-8,1,posz-8, posx+8,17,posz+8, block_to_check);
print('There is '+total_count+' of '+block_to_check+' around you')
)
/script invoke check_area_around_closest_player_for_block 'diamond_ore'
or simply
/script run print('There is '+for(rect(x,9,z,8,8,8), _ == 'diamond_ore')+' diamond ore around you')
It definitely pays to check what higher level `scarpet` functions have to offer.
# Programs
You can think of an program like a mathematical expression, like `"2.4*sin(45)/(2-4)"` or `"sin(y)>0 & max(z, 3)>3"`.
Writing a program, is like writing a `2+3`, just a bit longer.
## Basic language components
Programs consist of constants, like `2`, `3.14`, `pi`, or `'foo'`, operators like `+`, `/`, `->`, variables which you
can define, like `foo` or special ones that will be defined for you, like `_x`, or `_` , which I specific to a each
built in function, and functions with name, and arguments in the form of `f(a,b,c)`, where `f` is the function name,
and `a, b, c` are the arguments which can be any other expression. And that's all the parts of the language, so all
in all - sounds quite simple.
## Code flow
Like any other proper programming language, `scarpet` needs brackets, basically to identify where stuff begins and
where it ends. In the languages that uses much more complicated constructs, like Java, they tend to use all sort of
them, round ones to indicate function calls, curly to indicate section of code, square to access lists, pointy for
generic types etc... I mean - there is no etc, cause they have exhausted all the bracket options...
`Scarpet` is different, since it runs everything based on functions (although its not per se a functional
language like lisp) only needs the round brackets for everything, and it is up to the programmer to organize
its code so its readable, as adding more brackets does not have any effect on the performance of the programs
as they are compiled before they are executed. Look at the following example usage of `if()` function:
if(x<y+6,set(x,8+y,z,'air');plop(x,top('surface',x,z),z,'birch'),sin(query(player(),'yaw'))>0.5,plop(0,0,0,'boulder'),particle('fire',x,y,z))
Would you prefer to read
if( x<y+6,
set(x,8+y,z,'air');
plop(x,top('surface',x,z),z,'birch'),
sin(query(player(),'yaw'))>0.5,
plop(0,0,0,'boulder'),
particle('fire',x,y,z)
)
Or rather:
if
( x<y+6,
(
set(x,8+y,z,'air');
plop(x,top('surface',x,z),z,'birch')
),
// else if
sin(query(player(),'yaw'))>0.5,
(
plop(0,0,0,'boulder')
),
// else
particle('fire',x,y,z)
)
Whichever style you prefer it doesn't matter. It typically depends on the situation and the complexity of the
subcomponents. No matter how many whitespaces and extra brackets you add - the code will evaluate to exactly the
same expression, and will run exactly the same, so make sure your programs are nice and clean so others don't
have problems with them
## Functions and scoping
Users can define functions in the form `fun(args....) -> expression` and they are compiled and saved for further
execution in this, but also subsequent calls of /script command, added to events, etc. Functions can also be
assigned to variables,
passed as arguments, called with `call('fun', args...)` function, but in most cases you would want to
call them directly by
name, in the form of `fun(args...)`. This means that once defined functions are saved with the world for
further use. For variables, there are two types of them, global - which are shared anywhere in the code,
and those are all which name starts with 'global_', and local variables which is everything else and those
are only visible inside each function. This also means that all the parameters in functions are
passed 'by value', not 'by reference'.
## Outer variables
Functions can still 'borrow' variables from the outer scope, by adding them to the function signature wrapped
around built-in function `outer`. It adds the specified value to the function call stack so they behave exactly
like capturing lambdas in Java, but unlike java captured variables don't need to be final. Scarpet will just
attach their new values at the time of the function definition, even if they change later. Most value will be
copied, but mutable values, like maps or lists, allow to keep the 'state' with the function, allowing them to
have memory and act like objects so to speak. . Check `outer(var)` for details.
## Code delivery, line indicators
Note that this should only apply to pasting your code to execute with commandblock. Scarpet recommends placing
your code in apps (files with `.sc` extension that can be placed inside "/scripts" folder in the world files and
loaded as a scarpet app with command `/script load [app_name]`. Scarpet apps loaded from disk should only
contain code, no need to start with "/script run" prefix
The following is the code that could be provided in a `foo.sc` app file located in world `/scripts` folder
run_program() -> (
loop( 10,
// looping 10 times
// comments are allowed in scripts located in world files
// since we can tell where that line ends
foo = floor(rand(10));
check_not_zero(foo);
print(_+' - foo: '+foo);
print(' reciprocal: '+ _/foo )
)
);
check_not_zero(foo) -> (
if (foo==0, foo = 1)
)
Which we then call in-game with:
/script load foo /script in foo invoke run_programHowever the following code can also be input as a command, or in a command block. Since the maximum command that can be input to the chat is limited in length, you will be probably inserting your programs by pasting them to command blocks or reading from world files, however pasting to command blocks will remove some whitespaces and squish your newlines making the code not readable. If you are pasting a program that is perfect and will never cause an error, I salute you, but for the most part it is quite likely that your program might break, either at compile time, when its initially analyzed, or at execute time, when you suddenly attempt to divide something by zero. In these cases you would want to get a meaningful error message, but for that you would need to indicate for the compiler where did you put these new lines, since command block would squish them. For that, place at the beginning of the line to let the compiler know where are you. This makes so that `$` is the only character that is illegal in programs, since it will be replaced with new lines. As far as I know, `$` is not used anywhere inside Minecraft identifiers, so this shouldn't hinder the abilities of your programs. Consider the following program executed as command block command:
/script run
run_program() -> (
loop( 10,
foo = floor(rand(_));
check_not_zero(foo);
print(_+' - foo: '+foo);
print(' reciprocal: '+ _/foo )
)
);
check_not_zero(foo) -> (
if (foo==0, foo = 1)
)
Lets say that the intention was to check if the bar is zero and prevent division by zero in print, but because
the `foo` is passed as a variable, it never changes the original foo value. Because of the inevitable division
by zero, we get the following message:
Your math is wrong, Incorrect number format for NaN at pos 98
run_program() -> ( loop( 10, foo = floor(rand(_)); check_not_zero(foo); print(_+' - foo: '+foo);
HERE>> print(' reciprocal: '+ _/foo ) ));check_not_zero(foo) -> ( if (foo==0, foo = 1))
As we can see, we got our problem where the result of the mathematical operation was not a number
(infinity, so not a number), however by pasting our program into the command made it squish the newlines so
while it is clear where the error happened and we still can track the error down, the position of the error (98)
is not very helpful and wouldn't be useful if the program gets significantly longer. To combat this issue we can
precede every line of the script with dollar signs `$`:
/script run
$run_program() -> (
$ loop( 10,
$ foo = floor(rand(_));
$ check_not_zero(foo);
$ print(_+' - foo: '+foo);
$ print(' reciprocal: '+ _/foo )
$ )
$);
$check_not_zero(foo) -> (
$ if (foo==0, foo = 1)
$)
Then we get the following error message
Your math is wrong, Incorrect number format for NaN at line 7, pos 2
print(_+' - foo: '+foo);
HERE>> print(' reciprocal: '+ _/foo )
)
As we can note not only we get much more concise snippet, but also information about the line number and position,
so means its way easier to locate the potential problems problem
Obviously that's not the way we intended this program to work. To get it `foo` modified via a function call,
we would either return it as a result and assign it to the new variable:
foo = check_not_zero(foo); ... check_not_zero(foo) -> if(foo == 0, 1, foo).. or convert it to a global variable, which in this case passing as an argument is not required
global_foo = floor(rand(10)); check_foo_not_zero(); ... check_foo_not_zero() -> if(global_foo == 0, global_foo = 1)## Scarpet preprocessor There are several preprocessing operations applied to the source of your program to clean it up and prepare for execution. Some of them will affect your code as it is reported via stack traces and function definition, and some are applied only on the surface. - stripping `//` comments (in file mode) - replacing `$` with newlines (in command mode, modifies submitted code) - removing extra semicolons that don't follow `;` use as a binary operator, allowing for lenient use of semicolons - translating `{` into `m(`, `[` into `l(`, and `]` and `}` into `)` No further optimizations are currently applied to your code. ## Mentions LR1 parser, tokenizer, and several built-in functions are built based on the EvalEx project. EvalEx is a handy expression evaluator for Java, that allows to evaluate simple mathematical and boolean expressions. EvalEx is distributed under MIT licence. For more information, see: [EvalEx GitHub repository](https://github.com/uklimaschewski/EvalEx) # Variables and Constants `scarpet` provides a number of constants that can be used literally in scripts * `null`: nothing, zilch, not even false * `true`: pure true, or just 1 (one) * `false`: false truth, or true falsth, 0 (zero) actually * `pi`: for the fans of perimeters, its a perimeter of an apple pi of diameter 1\. About 3.14 * `euler`: clever guy. Derivative of its exponent is goto 1\. About 2.72 Apart from that, there is a bunch of system variables, that start with `_` that are set by `scarpet` built-ins, like `_`, typically each consecutive value in loops, `_i` indicating iteration, or `_a` like an accumulator for `reduce` function. Certain calls to Minecraft specific calls would also set `_x`, `_y`, `_z`, indicating block positions. All variables starting with `_` are read-only, and cannot be declared and modified in client code. ## Literals `scarpet` accepts numeric and string liters constants. Numbers look like `1, 2.5, -3e-7, 0xff,` and are internally represented as Java's `double` but `scarpet` will try to trim trailing zeros as much as possible so if you need to use them as intergers, you can. Strings use single quoting, for multiple reasons, but primarily to allow for easier use of strings inside doubly quoted command arguments (when passing a script as a parameter of `/script fill` for example), or when typing in jsons inside scarpet (to feed back into a `/data merge` command for example). Strings also use backslashes `\` for quoting special characters, in both plain strings and regular expressions
'foo'
print('This doesn\'t work')
nbt ~ '\\.foo' // matching '.' as a '.', not 'any character match'
# Operators
There is a number of operators you can use inside the expressions. Those could be considered generic type operators
that apply to most data types. They also follow standard operator precedence, i.e. `2+2*2` is understood
as `2+(2*2)`, not `(2+2)*2`, otherwise they are applied from left to right, i.e. `2+4-3` is interpreted
as `(2+4)-3`, which in case of numbers doesn't matter, but since `scarpet` allows for mixing all value types
the associativity would matter, and may lead to unintended effects:
Important operator is function definition `->` operator. It will be covered
in [User Defined Functions and Program Control Flow](docs/scarpet/language/FunctionsAndControlFlow.md)
'123'+4-2 => ('123'+4)-2 => '1234'-2 => '134'
'123'+(4-2) => '123'+2 => '1232'
3*'foo' => 'foofoofoo'
1357-5 => 1352
1357-'5' => 137
3*'foo'-'o' => 'fff'
l(1,3,5)+7 => l(8,10,12)
As you can see, values can behave differently when mixed with other types in the same expression.
In case values are of the same types, the result tends to be obvious, but `Scarpet` tries to make sense of whatever
it has to deal with
## Operator Precedence
Here is the complete list of operators in `scarpet` including control flow operators. Note, that commas and brackets
are not technically operators, but part of the language, even if they look like them:
* Match, Get `~ :`
* Unary `+ - !`
* Exponent `^`
* Multiplication `* / %`
* Addition `+ -`
* Comparison `== != > >= <= <`
* Logical And`&&`
* Logical Or `||`
* Assignment `= += <>`
* Definition `->`
* Next statement`;`
* Comma `,`
* Bracket `( )`
### `Get, Accessor Operator :`
Operator version of the `get(...)` function to access elements of lists, maps, and potentially other containers
(i.e. NBTs). It is important to distinguish from `~` operator, which is a matching operator, which is expected to
perform some extra computations to retrieve the result, while `:` should be straightforward and immediate, and
the source object should behave like a container and support full container API,
meaning `get(...)`, `put(...)`, `delete(...)`, and `has(...)` functions
For certain operators and functions (get, put, delete, has, =, +=) objects can use `:` annotated fields as l-values,
meaning construct like `foo:0 = 5`, would act like `put(foo, 0, 5)`, rather than `get(foo, 0) = 5`,
which would result in an error.
TODO: add more information about l-value behaviour.
### `Matching Operator ~`
This operator should be understood as 'matches', 'contains', 'is_in', or 'find me some stuff about something else.
For strings it matches the right operand as a regular expression to the left, returning the first match.
This can be used to extract information from unparsed nbt's in a more convoluted way (use `get(...)` for
more appropriate way of doing it). For lists it checks if an element is in the list, and returns the
index of that element, or `null` if no such element was found, especially that the use of `first(...)`
function will not return the index. Currently it doesn't have any special behaviour for numbers - it checks for
existence of characters in string representation of the left operand with respect of the regular expression on
the right hand side.
In Minecraft API portion `entity ~ feature` is a shortcode for `query(entity,feature)` for queries that do not take
any extra arguments.
l(1,2,3) ~ 2 => 1
l(1,2,3) ~ 4 => null
'foobar' ~ '.b' => 'ob'
player('*') ~ 'gnembon' // null unless player gnembon is logged in (better to use player('gnembon') instead
p ~ 'sneaking' // if p is an entity returns whether p is sneaking
Or a longer example of an ineffective way to searching for a squid
entities = entities_area('all',x,y,z,100,10,100);
sid = entities ~ 'Squid';
if(sid != null, run('execute as '+query(get(entities,sid),'id')+' run say I am here '+query(get(entities,sid),'pos') ) )
Or an example to find if a player has specific enchantment on a held axe (either hand) and get its level
(not using proper NBTs query support via `get(...)`):
global_get_enchantment(p, ench) -> (
$ for(l('main','offhand'),
$ holds = query(p, 'holds', _);
$ if( holds,
$ l(what, count, nbt) = holds;
$ if( what ~ '_axe' && nbt ~ ench,
$ lvl = max(lvl, number(nbt ~ '(?<=lvl:)\\d') )
$ )
$ )
$ );
$ lvl
$);
/script run global_get_enchantment(players(), 'sharpness')
### `Basic Arithmetic Operators + - * /`
Allows to add the results of two expressions. If the operands resolve to numbers, the result is arithmetic operation.
In case of strings, adding or subtracting from a string results in string concatenation and removal of substrings
from that string. Multiplication of strings and numbers results in repeating the string N times and division results
in taking the first k'th part of the string, so that `str*n/n ~ str`
In case first operand is a list, either it
results in a new list with all elements modified one by one with the other operand, or if the operand is a list
with the same number of items - element-wise addition/subtraction. This prioritize treating lists as value containers
to lists treated as vectors.
Addition with maps (`{}` or `m()`) results in a new map with keys from both maps added, if both operands are maps,
adding elements of the right argument to the keys, of left map, or just adding the right value as a new key
in the output map.
Examples:
2+3 => 5
'foo'+3+2 => 'foo32'
'foo'+(3+2) => 'foo5'
3+2+'bar' => '5bar'
'foo'*3 => 'foofoofoo'
'foofoofoo' / 3 => 'foo'
'foofoofoo'-'o' => 'fff'
l(1,2,3)+1 => l(2,3,4)
b = l(100,63,100); b+l(10,0,10) => l(110,63,110)
{'a' -> 1} + {'b' -> 2} => {'a' -> 1, 'b' -> 2}
### `Just Operators % ^`
The modulo and exponent (power) operators work only if both operands are numbers
pi^pi%euler => 1.124.... -9 % 4 => -1 9 % -4 => 0 ¯\_(ツ)_/¯ Java -3 ^ 2 => 9 -3 ^ pi => // Error### `Comparison Operators == != < > <= >=` Allows to compare the results of two expressions. For numbers it is considers arithmetic order of numbers, for strings - lexicographical, nulls are always 'less' than everything else, and lists check their elements - if the sizes are different, the size matters, otherwise, pairwise comparisons for each elements are performed. The same order rules than with all these operators are used with the default sortographical order as used by `sort` function. All of these are true:
null == null null != false 0 == false 1 == true null < 0 null < -1000 1000 < 'a' 'bar' < 'foo' 3 == 3.0### `Logical Operators && ||` These operator compute respective boolean operation on the operands. What it important is that if calculating of the second operand is not necessary, it won't be evaluated, which means one can use them as conditional statements. In case of success returns first positive operand (`||`) or last one (`&&`).
true || false => 1
null || false => 0
null != false || run('kill gnembon') => 1 // gnembon survives
null != false && run('kill gnembon') => 0 // when cheats not allowed
null != false && run('kill gnembon') => 1 // gnembon dies, cheats allowed
### `Assignment Operators = <> +=`
A set of assignment operators. All require bounded variable on the LHS, `<>` requires bounded arguments on the
right hand side as well (bounded, meaning being variables). Additionally they can also handle list constructors
with all bounded variables, and work then as list assignment operators. When `+=` is used on a list, it extends
that list of that element, and returns the list (old == new). `scarpet` doesn't support currently removal of items.
Removal of items can be obtaine via `filter` command, and reassigning it fo the same variable. Both operations would
require rewriting of the array anyways.
a = 5 => a == 5 l(a,b,c) = l(3,4,5) => a==3, b==4, c==5 l(minx,maxx) = sort(xi,xj); // minx assumes min(xi, xj) and maxx, max(xi, xj) l(a,b,c,d,e,f) = l(range(6)); l(a,b,c) <> l(d,e,f); l(a,b,c,d,e,f) => [3,4,5,0,1,2] a = l(1,2,3); a += 4 => [1,2,3,4] a = l(1,2,3,4); a = filter(a,_!=2) => [1,3,4]### `Unary Operators - +` Require a number, flips the sign. One way to assert it's a number is by crashing the script. gg.
-4 => -4 +4 => 4 +'4' // Error message### `Negation Operator !` flips boolean condition of the expression. Equivalent of `bool(expr)==false`
!true => 0 !false => 1 !null => 1 !5 => 0 !l() => 1 !l(null) => 0# Arithmetic operations ## Basic Arithmetic Functions There is bunch of them - they require a number and spit out a number, doing what you would expect them to do. ### `fact(n)` Factorial of a number, a.k.a `n!`, just not in `scarpet`. Gets big... quick... ### `sqrt(n)` Square root. For other fancy roots, use `^`, math and yo noggin. Imagine square roots on a tree... ### `abs(n)` Absolut value. ### `round(n)` Closest integer value. Did you know the earth is also round? ### `floor(n)` Highest integer that is still no larger then `n`. Insert a floor pun here. ### `ceil(n)` First lucky integer that is not smalller than `n`. As you would expect, ceiling is typically right above the floor. ### `ln(n)` Natural logarithm of `n`. Naturally. ### `ln1p(n)` Natural logarithm of `n+1`. Very optimistic. ### `log10(n)` Decimal logarithm of `n`. Its ceiling is the length of its floor. ### `log(n)` Binary logarithm of `n`. Finally, a proper one, not like the previous 11. ### `log1p(n)` Binary logarithm of `n+1`. Also always positive. ### `mandelbrot(a, b, limit)` Computes the value of the mandelbrot set, for set `a` and `b` spot. Spot the beetle. Why not. ### `min(arg, ...), min(list), max(arg, ...), max(list)` Compute minimum or maximum of supplied arguments assuming default sorthoraphical order. In case you are missing `argmax`, just use `a ~ max(a)`, little less efficient, but still fun. Interesting bit - `min` and `max` don't remove variable associations from arguments, which means can be used as LHS of assignments (obvious case), or argument spec in function definitions (far less obvious).
a = 1; b = 2; min(a,b) = 3; l(a,b) => [3, 2] a = 1; b = 2; fun(x, min(a,b)) -> l(a,b); fun(3,5) => [5, 0]Absolutely no idea, how the latter might be useful in practice. But since it compiles, can ship it. ### `relu(n)` Linear rectifier of `n`. 0 below 0, n above. Why not. `max(0,n)` with less moral repercussions. ## Trigonometric / Geometric Functions ### `sin(x)` ### `cos(x)` ### `tan(x)` ### `asin(x)` ### `acos(x)` ### `atan(x)` ### `atan2(x,y)` ### `sinh(x)` ### `cosh(x)` ### `tanh(x)` ### `sec(x)` ### `csc(x)` ### `sech(x)` ### `csch(x)` ### `cot(x)` ### `acot(x)` ### `coth(x)` ### `asinh(x)` ### `acosh(x)` ### `atanh(x)` ### `rad(deg)` ### `deg(rad)` Use as you wish # System functions ## Type conversion functions ### `copy(expr)` Returns the deep copy of the expression. Can be used to copy mutable objects, like maps and lists ### `type(expr)` Returns the string value indicating type of the expression. Possible outcomes are `null`, `number`, `string`, `list`, `map`, `iterator`, `function`, `task`, as well as minecraft related concepts like `block`, `entity`, `nbt`, `text`. ### `bool(expr)` Returns a boolean context of the expression. Note that there are no true/false values in scarpet. `true` is alias of 1, and `false` is 0\. Bool is also interpreting string values as boolean, which is different from other places where boolean context can be used. This can be used in places where API functions return string values to represent binary values
bool(pi) => 1
bool(false) => 0
bool('') => 0
bool(l()) => 0
bool(l('')) => 1
bool('foo') => 1
bool('false') => 0
bool('nulL') => 0
if('false',1,0) => 1
### `number(expr)`
Returns a numeric context of the expression. Can be used to read numbers from strings
number(null) => null
number(false) => 0
number('') => null
number('3.14') => 3.14
number(l()) => 0
number(l('')) => 1
number('foo') => null
number('3bar') => null
number('2')+number('2') => 4
### `str(expr)`,`str(expr, params? ... )`, `str(expr, param_list)`
If called with one argument, returns string representation of such value.
Otherwise, returns a formatted string representing the expression. Arguments for formatting can either be provided as
each consecutive parameter, or as a list which then would be the only extra parameter. To format one list argument
, you can use `str(list)`, or `str('foo %s', l(list))`.
Accepts formatting style accepted by `String.format`.
Supported types (with `"%>"` syntax):
* `d`, `o`, `x`: integers, octal, hex
* `a`, `e`, `f`, `g`: floats
* `b`: booleans
* `s`: strings
* `%%`: '%' character
str(null) => null
str(false) => 0
str('') => null
str('3.14') => 3.14
str(l()) => 0
str(l('')) => 1
str('foo') => null
str('3bar') => null
str(2)+str(2) => 22
str('pi: %.2f',pi) => 'pi: 3.14'
str('player at: %d %d %d',pos(player())) => 'player at: 567, -2423, 124'
* * *
## Threading and Parallel Execution
Scarpet allows to run threads of execution in parallel to the main script execution thread. In Minecraft, the main
thread scripts are executed on the main server thread. Since Minecraft is inherently NOT thread safe, it is not that
beneficial to parallel execution in order to access world resources faster. Both `GetBlockState` and `setBlockState`
are not thread safe and require the execution to park on the server thread, where these requests can be executed in
the off-tick time in between ticks that didn't take 50ms. There are however benefits of running things in parallel,
like fine time control not relying on the tick clock, or running things independent on each other. You can still run
your actions on tick-by-tick basis, either taking control of the execution using `game_tick()` API function
(nasty solution), or scheduling tick using `schedule()` function (much nicer solution), but threading often gives
the neatest solution to solve problems in parallel
(see [scarpet camera](/src/main/resources/assets/carpet/scripts/camera.sc)).
Due to limitations with the game, there are some limits to the threading as well. You cannot for
instance `join_task()` at all from the main script and server thread, because any use of Minecraft specific
function that require any world access, will require to park and join on the main thread to get world access,
meaning that calling join on that task would inevitably lead to a typical deadlock. You can still join tasks
from other threads, just because the only possibility of a deadlock in this case would come explicitly from your
bad code, not the internal world access behaviour. Some things tough like player or entity manipulation, can be
effectively parallelized.
### `task(function, ?args...., ?executor)`
Creates and runs a parallel task, returning the handle to the task object. Task will return the return value of the
function when its completed, or will return `null` immediately if task is still in progress, so grabbing a value of
a task object is non-blocking. Function can be either function value, or function lambda, or a name of an existing
defined function. In case function needs arguments to be called with, they should be supplied after the function
name, or value. Optional `executor` identifier is to place the task in a specific queue identified by this value.
The default thread value is the `null` thread. There is no limits on number of parallel tasks for any executor,
so using different queues is solely for synchronization purposes. Also, since `task` function knows how many extra
arguments it requires, the use of tailing optional parameter for the custom executor thread pool is not ambiguous.
task( _() -> print('Hello Other World') ) => Runs print command on a separate thread
foo(a, b) -> print(a+b); task('foo',2,2) => Uses existing function definition to start a task
task('foo',3,5,'temp'); => runs function foo with a different thread executor, identified as 'temp'
a = 3; task( _(outer(a), b) -> foo(a,b), 5, 'temp')
=> Another example of running the same thing passing arguments using closure over anonymous function as well as passing a parameter.
### `task_count(executor?)`
If no argument provided, returns total number of tasks being executed in parallel at this moment using scarpet
threading system. If the executor is provided, returns number of active tasks for that provider. Use `task_count(null)`
to get the task count of the default executor only.
### `task_value(task)`
Returns the task return value, or `null` if task hasn't finished yet. Its a non-blocking operation. Unlike `join_task`,
can be called on any task at any point
### `task_join(task)`
Waits for the task completion and returns its computed value. If the task has already finished returns it immediately.
Unless taking the task value directly, i.e. via `task_value`, this operation is blocking. Since Minecraft has a
limitation that all world access operations have to be performed on the main game thread in the off-tick time,
joining any tasks that use Minecraft API from the main thread would mean automatic lock, so joining from the main
thread is not allowed. Join tasks from other threads, if you really need to, or communicate asynchronously with
the task via globals or function data / arguments to monitor its progress, communicate, get partial results,
or signal termination.
### `task_completed(task)`
Returns true if task has completed, or false otherwise.
### `synchronize(lock, expression)`
Evaluates `expression` synchronized with respect to the lock `lock`. Returns the value of the expression.
### `task_dock(expr)`
In a not-task (running regular code on the main game thread) it is a pass-through command. In tasks - it docks
the current thread on the main server thread and executes expression as one server offline server task.
This is especially helpful in case a task has several docking operations to perform, such as setting a block, and
it would be much more efficient to do them all at once rather then packing each block access in each own call.
Be mindful, that docking the task means that the tick execution will be delayed until the expression is evaluated.
This will synchronize your task with other tasks using `task_dock`, but if you should be using `synchronize` to
synchronize tasks without locking the main thread.
* * *
## Auxiliary functions
### `lower(expr), upper(expr), title(expr)`
Returns lowercase, uppercase or titlecase representation of a string representation of the passed expression
lower('aBc') => 'abc'
upper('aBc') => 'ABC'
title('aBc') => 'Abc'
### `replace(string, regex, repl?); replace_first(string, regex, repl?)`
Replaces all, or first occurence of a regular expression in the string with `repl` expression,
or nothing, if not specified
replace('abbccddebfg','b+','z') // => azccddezfg
replace_first('abbccddebfg','b+','z') // => azccddebfg
### `length(expr)`
Returns length of the expression, the length of the string, the length of the integer part of the number,
or length of the list
length(pi) => 1
length(pi*pi) => 1
length(pi^pi) => 2
length(l()) => 0
length(l(1,2,3)) => 3
length('') => 0
length('foo') => 3
### `rand(expr), rand(expr, seed)`
returns a random number from `0.0` (inclusive) to `expr` (exclusive). In boolean context (in conditions,
boolean functions, or `bool`), returns false if the randomly selected value is less than 1\. This means
that `rand(2)` returns true half of the time and `rand(5)` returns true for 80% (4/5) of the time. If seed is not
provided, uses a random seed. If seed is provided, each consecutive call to rand() will act like 'next' call to the
same random object. Scarpet keeps track of up to 1024 custom random number generators, so if you exceed this number
(per app), then your random sequence will revert to the beginning.
map(range(10), floor(rand(10))) => [5, 8, 0, 6, 9, 3, 9, 9, 1, 8]
map(range(10), bool(rand(2))) => [1, 1, 1, 0, 0, 1, 1, 0, 0, 0]
map(range(10), str('%.1f',rand(_))) => [0.0, 0.4, 0.6, 1.9, 2.8, 3.8, 5.3, 2.2, 1.6, 5.6]
### `perlin(x), perlin(x, y), perlin(x, y, z), perlin(x, y, z, seed)`
returns a noise value from `0.0` to `1.0` (roughly) for 1, 2 or 3 dimensional coordinate. The default seed it samples
from is `0`, but seed can be specified as a 4th argument as well. In case you need 1D or 2D noise values with custom
seed, use `null` for `z`, or `y` and `z` arguments respectively.
Perlin noise is based on a square grid and generates rougher maps comparing to Simplex, which is creamier.
Querying for lower-dimensional result, rather than affixing unused dimensions to constants has a speed benefit,
Thou shall not sample from noise changing seed frequently. Scarpet will keep track of the last 256 perlin seeds
used for sampling providing similar speed comparing to the default seed of `0`. In case the app engine uses more
than 256 seeds at the same time, switching between them can get much more expensive.
### `simplex(x, y), simplex(x, y, z), simplex(x, y, z, seed)`
returns a noise value from `0.0` to `1.0` (roughly) for 2 or 3 dimensional coordinate. The default seed it samples
from is `0`, but seed can be specified as a 4th argument as well. In case you need 2D noise values with custom seed,
use `null` for `z` argument.
Simplex noise is based on a triangular grid and generates smoother maps comparing to Perlin. To sample 1D simplex
noise, affix other coordinate to a constant.
Thou shall not sample from noise changing seed frequently. Scarpet will keep track of the last 256 simplex seeds
used for sampling providing similar speed comparing to the default seed of `0`. In case the app engine uses more
than 256 seeds at the same time, switching between them can get much more expensive.
### `print(expr)`, `print(player, expr)`
prints the value of the expression to chat. Passes the result of the argument to the output unchanged,
so `print`-statements can be weaved in code to debug programming issues. By default it uses the same communication
channels that most vanilla commands are using.
In case player is directly specified, it only sends the message to that player, like `tell` command.
print('foo') => results in foo, prints: foo
a = 1; print(a = 5) => results in 5, prints: 5
a = 1; print(a) = 5 => results in 5, prints: 1
print('pi = '+pi) => prints: pi = 3.141592653589793
print(str('pi = %.2f',pi)) => prints: pi = 3.14
### `sleep(expr)`
Halts the execution of the program (and the game itself) for `expr` milliseconds. All in all, its better to
use `game_tick(expr)` to let the game do its job while the program waits
sleep(50)### `time()` Returns the number of milliseconds since 'some point', like Java's `System.nanoTime()`, which varies from system to system and from Java to Java. This measure should NOT be used to determine the current (date)time, but to measure durations of things. it returns a float with time in milliseconds (ms) for convenience and microsecond (μs) resolution for sanity.
start_time = time();
flip_my_world_upside_down();
print(str('this took %d milliseconds',time()-start_time))
### `unix_time()`
Returns standard POSIX time as a number of milliseconds since the start of the epoch
(00:00 am and 0 seconds, 1 Jan 1970).
Unlike the previous function, this can be used to get exact time, but it varies from time zone to time zone.
### `convert_date(milliseconds)`
### `convert_date(year, month, date, hours?, mins?, secs?)`
### `convert_date(l(year, month, date, hours?, mins?, secs?))`
If called with a single argument, converts standard POSIX time to a list in the format:
`l(year, month, date, hours, mins, secs, day_of_week, day_of_year, week_of_year)`
eg: `convert_date(1592401346960) -> [2020, 6, 17, 10, 42, 26, 3, 169, 25]`
Where the `6` stands for June, but `17` stands for 17th, `10` stands for 10am,
`42` stands for 42 minutes past the hour, and `26` stands for 26 seconds past the minute,
and `3` stands for Wednesday, `169` is the day of year, and `25` is a week of year.
Run `convert_date(unix_time())` to get current time as list.
When called with a list, or with 3 or 6 arguments, returns standard POSIX time as a number of milliseconds since the
start of the epoch (1 Jan 1970),
using the time inputted into the function as opposed to the system time.
Example editing:
date = convert_date(unix_time());
months = l('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
days = l('Mon','Tue','Wed','Thu','Fri','Sat','Sun');
print(
str('Its %s, %d %s %d, %02d:%02d:%02d',
days:(date:6-1), date:2, months:(date:1-1), date:0, date:3, date:4, date:5
)
)
This will give you a date:
It is currently `hrs`:`mins` and `secs` seconds on the `date`th of `month`, `year`
### `profile_expr(expression)`
Returns number of times given expression can be run in 50ms time. Useful to profile and optimize your code.
Note that, even if its only a number, it WILL run these commands, so if they are destructive, you need to be careful.
* * *
## Access to variables and stored functions (use with caution)
### `var(expr)`
Returns the variable under the name of the string value of the expression. Allows to manipulate variables in more
programmatic manner, which allows to use local variable set with a hash map type key-value access,
can also be used with global variables
a = 1; var('a') = 'foo'; a => a == 'foo'
### `undef(expr)`
Removes all bindings of a variable with a name of `expr`. Removes also all function definitions with that name.
It can affect global variable pool, and local variable set for a particular function.
inc(i) -> i+1; foo = 5; inc(foo) => 6
inc(i) -> i+1; foo = 5; undef('foo'); inc(foo) => 1
inc(i) -> i+1; foo = 5; undef('inc'); undef('foo'); inc(foo) => Error: Function inc is not defined yet at pos 53
undef('pi') => bad idea - removes hidden variable holding the pi value
undef('true') => even worse idea, unbinds global true value, all references to true would now refer to the default 0
### `vars(prefix)`
It returns all names of variables from local scope (if prefix does not start with 'global') or global variables
(otherwise). Here is a larger example that uses combination of `vars` and `var` functions to be
used for object counting
/script run
$ count_blocks(ent) -> (
$ l(cx, cy, cz) = query(ent, 'pos');
$ scan(cx, cy, cz, 16, 16, 16, var('count_'+_) += 1);
$ for ( sort_key( vars('count_'), -var(_)),
$ print(str( '%s: %d', slice(_,6), var(_) ))
$ )
$ )
/script run count_blocks(player())
* * *
## System key-value storage
Scarpet runs apps in isolation. The can share code via use of shared libraries, but each library that is imported to
each app is specific to that app. Apps can store and fetch state from disk, but its restricted to specific locations
meaning apps cannot interact via disk either. To facilitate communication for interappperability, scarpet hosts its
own key-value storage that is shared between all apps currently running on the host, providing methods for getting an
associated value with optional setting it if not present, and an operation of modifying a content of a system
global value.
### `system_variable_get(key, default_value ?)`
Returns the variable from the system shared key-value storage keyed with a `key` value, optionally if value is
not present, and default expression is provided, sets a new value to be associated with that key
### `system_variable_set(key, new_value)`
Returns the variable from the system shared key-value storage keyed with a `key` value, and sets a new
mapping for the key# Loops, and higher order functions
Efficient use of these functions can greatly simplify your programs and speed them up, as these functions will
internalize most of the operations that need to be applied on multiple values at the same time. Most of them take
a `list` argument which can be any iterable structure in scarpet, including generators, like `rect`, or `range`,
and maps, where the iterator returns all the map keys
## Loops
### `break(), break(expr), continue(), continue(expr)`
These allow to control execution of a loop either skipping current iteration code, using `continue`, or finishing the
current loop, using `break`. `break` and `continue` can only be used inside `for`, `c_for`, `while`, `loop`, `map`,
`filter`, `reduce` as well as Minecraft API block loops, `scan` and `volume`
functions, while `break` can be used in `first` as well. Outside of the internal expressions of these functions,
calling `break` or `continue` will cause an error. In case of the nested loops, and more complex setups, use
custom `try` and `throw` setup.
Please check corresponding loop function description what `continue` and `break` do in their contexts, but in
general case, passed values to `break` and `continue` will be used in place of the return value of the internal
iteration expression.
### `c_for(init, condition, increment, body)`
`c_for` Mimics c-style tri-arg (plus body) for loops. Return value of `c_for` is number of iterations performed in the
loop. Unlike other loops, the `body` is not provided with pre-initialized `_` style variables - all initialization
and increments has to be handled by the programmers themselves.
`break` and `continue` statements are handled within `body` expression only, and not in `condition` or `increment`.
c_for(x=0, x<10, x+=1,
c_for(y=0, y<10, y+=1,
print(str('%d * %d = %d', x, y, x*y))
)
)
### `for(list,expr(_,_i))`
Evaluates expression over list of items from the `list`. Supplies `_`(value) and `_i`(iteration number) to the `expr`.
Returns the number of times `expr` was successful. Uses `continue` and `break` argument in place of the returned
value from the `expr`(if supplied), to determine if the iteration was successful.
check_prime(n) -> !first( range(2, sqrt(n)+1), !(n % _) ); for(range(1000000,1100000),check_prime(_)) => 7216From which we can learn that there is 7216 primes between 1M and 1.1M ### `while(cond, limit)` Evaluates expression `expr` repeatedly until condition `cond` becomes false, but not more than `limit` times. Returns the result of the last `expr` evaluation, or `null` if nothing was successful. Both `expr` and `cond` will recveived a bound variable `_` indicating current iteration, so its a number.
while(a<100,10,a=_*_) => 81 // loop exhausted via limit while(a<100,20,a=_*_) => 100 // loop stopped at condition, but a has already been assigned while(_*_<100,20,a=_*_) => 81 // loop stopped at condition, before a was assigned a value### `loop(num,expr(_),exit(_)?)` Evaluates expression `expr`, `num` number of times. code>expr receives `_` system variable indicating the iteration.
loop(5, game_tick()) => repeat tick 5 times list = l(); loop(5, x = _; loop(5, list += l(x, _) ) ); list // double loop, produces: [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [1, 0], [1, 1], ... , [4, 2], [4, 3], [4, 4]]In this small example we will search for first 10 primes, apparently including 0:
check_prime(n) -> !first( range(2, sqrt(n)+1), !(n % _) ); primes = l(); loop(10000, if(check_prime(_), primes += _ ; if (length(primes) >= 10, break()))); primes // outputs: [0, 1, 2, 3, 5, 7, 11, 13, 17, 19]## Higher Order Functions ### `map(list,expr(_,_i))` Converts a `list` of values, to another list where each value is result of an expression `v = expr(_, _i)` where `_` is passed as each element of the list, and `_i` is the index of such element. If `break` is called the map returns whatever collected thus far. If `continue` and `break` are used with supplied argument, it is used in place of the resulting map element, otherwise current element is skipped.
map(range(10), _*_) => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
map(players('*'), _+' is stoopid') [gnembon is stoopid, herobrine is stoopid]
### `filter(list,expr(_,_i))`
filters `list` elements returning only these that return positive result of the `expr`. With `break` and `continue`
statements, the supplied value can be used as a boolean check instead.
filter(range(100), !(_%5), _*_>1000) => [0, 5, 10, 15, 20, 25, 30]
map(filter(entity_list('*'),_=='Witch'), query(_,'pos') ) => [[1082.5, 57, 1243.5]]
### `first(list,expr(_,_i))`
Finds and returns the first item in the list that satisfies `expr`. It sets `_` for current element value,
and `_i` for index of that element. `break` can be called inside the iteration code, using its argument value
instead of the current item. `continue` has no sense and cannot be called inside `first` call.
first(range(1000,10000), n=_; !first( range(2, sqrt(n)+1), !(n % _) ) ) => 1009 // first prime after 1000Notice in the example above, that we needed to rename the outer `_` to be albe to use in in the inner `first` call ### `all(list,expr(_,_i))` Returns `true` if all elements on the list satisfy the condition. Its roughly equivalent to `all(list,expr) <=> for(list,expr)==length(list)`. `expr` also receives bound `_` and `_i` variables. `break` and `continue` have no sense and cannot be used inside of `expr` body.
all([1,2,3], check_prime(_)) => 1
all(neighbours(x,y,z), _=='stone') => 1 // if all neighbours of [x, y, z] are stone
map(filter(rect(0,4,0,1000,0,1000), l(x,y,z)=pos(_); all(rect(x,y,z,1,0,1),_=='bedrock') ), pos(_) )
=> [[-298, 4, -703], [-287, 4, -156], [-269, 4, 104], [242, 4, 250], [-159, 4, 335], [-208, 4, 416], [-510, 4, 546], [376, 4, 806]]
// find all 3x3 bedrock structures in the top bedrock layer
map( filter( rect(0,4,0,1000,1,1000,1000,0,1000), l(x,y,z)=pos(_);
all(rect(x,y,z,1,0,1),_=='bedrock') && for(rect(x,y-1,z,1,1,1,1,0,1),_=='bedrock')<8),
pos(_) ) => [[343, 3, -642], [153, 3, -285], [674, 3, 167], [-710, 3, 398]]
// ditto, but requiring at most 7 bedrock block in the 18 blocks below them
### `reduce(list,expr(_a,_,_i), initial)`
Applies `expr` for each element of the list and saves the result in `_a` accumulator. Consecutive calls to `expr`
can access that value to apply more values. You also need to specify the initial value to apply for the
accumulator. `break` can be used to terminate reduction prematurely. If a value is provided to `break` or `continue`,
it will be used from now on as a new value for the accumulator.
reduce([1,2,3,4],_a+_,0) => 10 reduce([1,2,3,4],_a*_,1) => 24# User-defined functions and program control flow ## Writing programs with more than 1 line ### `Operator ;` To effectively write programs that have more than one line, a programmer needs way to specify a sequence of commands that execute one after another. In `scarpet` this can be achieved with `;`. Its an operator, and by separating statements with semicolons. And since whitespaces and `$` (commandline visible newline separator) sign are all treats as whitespaces, how you layout your code doesn't matter, as long as it is readable to everyone involved.
expr; expr; expr; exprNotice that the last expression is not followed by a semicolon. Since instruction separation is functional in `scarpet`, and not barely an instruction delimiter, terminating the code with a dangling operator wouldn't be valid. Having said that, since many programming languages don't care about the number of op terminators programmers use, carpet preprocessor will remove all unnecessary semicolons from scripts when compiled. In general `expr; expr; expr; expr` is equivalent to `(((expr ; expr) ; expr) ; expr)`. Result of the evaluated expression is the same as the result of the second expression, but first expression is also evaluated for side-effects
expr1 ; expr2 => expr2 // with expr1 as a side-effect## Global variables All defined functions are compiled, stored persistently, and available globally - accessible to all other scripts. Functions can only be undefined via call to `undef('fun')`, which would erase global entry for function `fun`. Since all variables have local scope inside each function, one way to share large objects is via global variables Any variable that is used with a name that starts with `'global_'` will be stored and accessible globally, not, inside current scope. It will also persist across scripts, so if a procedure needs to use its own construct, it needs to define it, or initialize it explicitly, or undefine it via `undef`
a() -> global_list+=1; global_list = l(1,2,3); a(); a(); global_list // => [1,2,3,1,1]### `Operator ->` `->` operator has two uses - as a function definition operator and key-value initializer for maps. To organize code better than a flat sequence of operations, one can define functions. Definition is correct if has the following form
fun(args, ...) -> exprWhere `fun(args, ...)` is a function signature indicating function name, number of arguments, and their names, and expr is an expression (can be complex) that is evaluated when `fun` is called. Names in the signature don't need to be used anywhere else, other occurrences of these names will be masked in this function scope. Function call creates new scope for variables inside `expr`, so all non-global variables are not visible from the caller scope. All parameters are passed by value to the new scope, including lists and other containers, however their copy will be shallow. The function returns its name as a string, which means it can be used to call it later with the `call` function Using `_` as the function name creates anonymous function, so each time `_` function is defined, it will be given a unique name, which you can pass somewhere else to get this function `call`ed.
a(lst) -> lst+=1; list = l(1,2,3); a(list); a(list); list // => [1,2,3]In case the inner function wants to operate and modify larger objects, lists from the outer scope, but not global, it needs to use `outer` function in function signature. in map construction context (directly in `m()` or `{}`), the `->` operator has a different function by converting its arguments to a tuple which is used by map constructor as a key-value pair:
{ 'foo' -> 'bar' } => {l('foo', 'bar')}
This means that it is not possible to define literally a set of inline function, however a set of functions can still
be created by adding elements to an empty set, and building it this way.
### `outer(arg)`
`outer` function can only be used in the function signature, and it will cause an error everywhere else. It
saves the value of that variable from the outer scope and allows its use in the inner scope. This is a similar
behaviour to using outer variables in lambda function definitions from Java, except here you have to specify
which variables you want to use, and borrow
This mechanism can be used to use static mutable objects without the need of using `global_...` variables
list = l(1,2,3); a(outer(list)) -> list+=1; a(); a(); list // => [1,2,3,1,1]The return value of a function is the value of the last expression. This as the same effect as using outer or global lists, but is more expensive
a(lst) -> lst+=1; list = l(1,2,3); list=a(list); list=a(list); list // => [1,2,3,1,1]Ability to combine more statements into one expression, with functions, passing parameters, and global and outer scoping allow to organize even larger scripts ### `import(module_name, symbols ...)` Imports symbols from other apps and libraries into the current one: global variables or functions, allowing to use them in the current app. This include other symbols imported by these modules. Scarpet supports cicular dependencies, but if symbols are used directly in the module body rather than functions, it may not be able to retrieve them. Returns full list of available symbols that could be imported from this module, which can be used to debug import issues, and list contents of libraries. ### `call(function, args.....)` calls a user defined function with specified arguments. It is equivalent to calling `function(args...)` directly except you can use it with function value, or name instead. This means you can pass functions to other user defined functions as arguments and call them with `call` internally. And since function definitions return the defined function, they can be defined in place as anonymous functions. Little technical note: the use of `_` in expression passed to built in functions is much more efficient due to not creating new call stacks for each invoked function, but anonymous functions is the only mechanism available for programmers with their own lambda arguments
my_map(list, function) -> map(list, call(function, _)); my_map(l(1,2,3), _(x) -> x*x); // => [1,4,9] profile_expr(my_map(l(1,2,3), _(x) -> x*x)); // => ~32000 sq(x) -> x*x; profile_expr(my_map(l(1,2,3), 'sq')); // => ~36000 sq = (_(x) -> x*x); profile_expr(my_map(l(1,2,3), sq)); // => ~36000 profile_expr(map(l(1,2,3), _*_)); // => ~80000## Control flow ### `return(expr?)` Sometimes its convenient to break the organized control flow, or it is not practical to pass the final result value of a function to the last statement, in this case a return statement can be used If no argument is provided - returns null value.
def() -> ( expr1; expr2; return(expr3); // function terminates returning expr3 expr4; // skipped expr5 // skipped )In general its cheaper to leave the last expression as a return value, rather than calling returns everywhere, but it would often lead to a messy code. ### `exit(expr?)` It terminates entire program passing `expr` as the result of the program execution, or null if omitted. ### `try(expr, catch_expr(_)?) ... throw(value?)` `try` function evaluates expression, and continues further unless `throw` function is called anywhere inside `expr`. In that case the `catch_expr` is evaluates with `_` set to the argument `throw` was called with. This mechanic accepts skipping thrown value - it throws null instead, and catch expression - then try returns null as well This mechanism allows to terminate large portion of a convoluted call stack and continue program execution. There is only one level of exceptions currently in carpet, so if the inner function also defines the `try` catchment area, it will received the exception first, but it can technically rethrow the value its getting for the outer scope. Unhandled throw acts like an exit statement. ### `if(cond, expr, cond?, expr?, ..., default?)` If statement is a function that takes a number of conditions that are evaluated one after another and if any of them turns out true, its `expr` gets returned, otherwise, if all conditions fail, the return value is `default` expression, or `null` if default is skipped `if` function is equivalent to `if (cond) expr; else if (cond) expr; else default;` from Java, just in a functional form # Lists, Maps and API support for Containers Scarpet supports basic container types: lists and maps (aka hashmaps, dicts etc..) ## Container manipulation Here is a list of operations that work on all types of containers: lists, maps, as well as other Minecraft specific modifyable containers, like NBTs ### `get(container, address, ...), get(lvalue), ':' operator` Returns the value at `address` element from the `value`. For lists it indicates an index, use negative numbers to reach elements from the end of the list. `get` call will always be able to find the index. In case there is few items, it will loop over for maps, retrieves the value under the key specified in the `address` or null otherwise [Minecraft specific usecase]: In case `value` is of `nbt` type, uses address as the nbt path to query, returning null, if path is not found, one value if there was one match, or list of values if result is a list. Returned elements can be of numerical type, string texts, or another compound nbt tags In case to simplify the access with nested objects, you can add chain of addresses to the arguments of `get` rather than calling it multiple times. `get(get(foo,a),b)` is equivalent to `get(foo, a, b)`, or `foo:a:b`.
get(l(range(10)), 5) => 5
get(l(range(10)), -1) => 9
get(l(range(10)), 10) => 0
l(range(10)):93 => 3
get(player() ~ 'nbt', 'Health') => 20 // inefficient way to get player health, use player() ~ 'health' instead
get(m( l('foo',2), l('bar',3), l('baz',4) ), 'bar') => 3
### `has(container, address, ...), has(lvalue)`
Similar to `get`, but returns boolean value indicating if the given index / key / path is in the container.
Can be used to determine if `get(...)==null` means the element doesn't exist, or the stored value for this
address is `null`, and is cheaper to run than `get`.
Like get, it can accept multiple addresses for chains in nested containers. In this case `has(foo:a:b)` is
equivalent to `has(get(foo,a), b)` or `has(foo, a, b)`
### `delete(container, address, ...), delete(lvalue)`
Removes specific entry from the container. For the lists - removes the element and shrinks it. For maps, it
removes the key from the map, and for nbt - removes content from a given path. For lists and maps returns previous
entry at the address, for nbt's - number of removed objects, with 0 indicating that the original value was unaffected.
Like with the `get` and `has`, `delete` can accept chained addresses, as well as l-value container access, removing
the value from the leaf of the path provided, so `delete(foo, a, b)` is the
same as `delete(get(foo,a),b)` or `delete(foo:a:b)`
Returns true, if container was changed, false, if it was left unchanged, and null if operation was invalid.
### `put(container, address, value), put(container, address, value, mode), put(lvalue, value)`
**Lists**
Modifies the container by replacing the value under the address with the supplied `value`. For lists, a valid
index is required, but can be negative as well to indicate positions from the end of the list. If `null` is
supplied as the address, it always means - add to the end of the list.
There are three modes that lists can have items added to them:
* `replace`(default): Replaces item under given index(address). Doesn't change the size of the array
unless `null` address is used, which is an exception and then it appends to the end
* `insert`: Inserts given element at a specified index, shifting the rest of the array to make space for the item.
Note that index of -1 points to the last element of the list, thus inserting at that position and moving the previous
last element to the new last element position. To insert at the end, use `+=` operator, or `null` address in put
* `extend`: treats the supplied value as an iterable set of values to insert at a given index, extending the list
by this amount of items. Again use `null` address/index to point to the end of the list
Due to the extra mode parameter, there is no chaining for `put`, but you can still use l-value container access to
indicate container and address, so `put(foo, key, value)` is the same as `put(foo:key, value)` or `foo:key=value`
Returns true, if container got modified, false otherwise, and null if operation was invalid.
**Maps**
For maps there are no modes available (yet, seems there is no reason to). It replaces the value under the supplied
key (address), or sets it if not currently present.
**NBT Tags**
The address for nbt values is a valid nbt path that you would use with `/data` command, and tag is any tag that
would be applicable for a given insert operation. Note that to distinguish between proper types (like integer types,
you need to use command notation, i.e. regular ints is `123`, while byte size int would be `123b` and an explicit
string would be `"5"`, so it helps that scarpet uses single quotes in his strings. Unlike for lists and maps, it
returns the number of affected nodes, or 0 if none were affected.
There are three modes that NBT tags can have items added to them:
* `replace`(default): Replaces item under given path(address). Removes them first if possible, and then adds given
element to the supplied position. The target path can indicate compound tag keys, lists, or individual elements
of the lists.
* `
a = l(1, 2, 3); put(a, 1, 4); a => [1, 4, 3]
a = l(1, 2, 3); put(a, null, 4); a => [1, 2, 3, 4]
a = l(1, 2, 3); put(a, 1, 4, 'insert'); a => [1, 4, 2, 3]
a = l(1, 2, 3); put(a, null, l(4, 5, 6), 'extend'); a => [1, 2, 3, 4, 5, 6]
a = l(1, 2, 3); put(a, 1, l(4, 5, 6), 'extend'); a => [1, 4, 5, 6, 2, 3]
a = l(l(0,0,0),l(0,0,0),l(0,0,0)); put(a.1, 1, 1); a => [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
a = m(1,2,3,4); put(a, 5, null); a => {1: null, 2: null, 3: null, 4: null, 5: null}
tag = nbt('{}'); put(tag, 'BlockData.Properties', '[1,2,3,4]'); tag => {BlockData:{Properties:[1,2,3,4]}}
tag = nbt('{a:[{lvl:3},{lvl:5},{lvl:2}]}'); put(tag, 'a[].lvl', 1); tag => {a:[{lvl:1},{lvl:1},{lvl:1}]}
tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl', 1, 2); tag
=> {a:[{lvl:[1,2,1,3]},{lvl:[3,2,1,1]},{lvl:[4,5,1,6]}]}
tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl[1]', 1); tag
=> {a:[{lvl:[1,1,3]},{lvl:[3,1,1]},{lvl:[4,1,6]}]}
## List operations
### `[value, ...?]`,`[iterator]`,`l(value, ...?)`, `l(iterator)`
Creates a list of values of the expressions passed as parameters. It can be used as an L-value and if all
elements are variables, you coujld use it to return multiple results from one function call, if that
function returns a list of results with the same size as the `[]` call uses. In case there is only one
argument and it is an iterator (vanilla expression specification has `range`, but Minecraft API implements
a bunch of them, like `diamond`), it will convert it to a proper list. Iterators can only be used in high order
functions, and are treated as empty lists, unless unrolled with `[]`.
Internally, `[elem, ...]`(list syntax) and `l(elem, ...)`(function syntax) are equivalent. `[]` is simply translated to
`l()` in the scarpet preprocessing stage. This means that internally the code has always expression syntax despite `[]`
not using different kinds of brackets and not being proper operators. This means that `l(]` and `[)` are also valid
although not recommended as they will make your code far less readable.
l(1,2,'foo') <=> [1, 2, 'foo'] l() <=> [] (empty list) [range(10)] => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [1, 2] = [3, 4] => Error: l is not a variable [foo, bar] = [3, 4]; foo==3 && bar==4 => 1 [foo, bar, baz] = [2, 4, 6]; [min(foo, bar), baz] = [3, 5]; [foo, bar, baz] => [3, 4, 5]In the last example `[min(foo, bar), baz]` creates a valid L-value, as `min(foo, bar)` finds the lower of the variables (in this case `foo`) creating a valid assignable L-list of `[foo, baz]`, and these values will be assigned new values ### `join(delim, list), join(delim, values ...)` Returns a string that contains joined elements of the list, iterator, or all values, concatenated with `delim` delimiter
join('-',range(10)) => 0-1-2-3-4-5-6-7-8-9
join('-','foo') => foo
join('-', 'foo', 'bar') => foo-bar
### `split(delim, expr)`
Splits a string undr `expr` by `delim` which can be a regular expression
split('',foo) => [f, o, o]
split('.','foo.bar') => []
split('\\.','foo.bar') => [foo, bar]
### `slice(expr, from, to?)`
extracts a substring, or sublist (based on the type of the result of the expression under expr with
starting index of `from`, and ending at `to` if provided, or the end, if omitted
slice(l(0,1,2,3,4,5), 1, 3) => [1, 2, 3]
slice('foobar', 0, 1) => 'f'
slice('foobar', 3) => 'bar'
slice(range(10), 3, 5) => [3, 4, 5]
slice(range(10), 5) => [5, 6, 7, 8, 9]
### `sort(list), sort(values ...)`
Sorts in the default sortographical order either all arguments, or a list if its the only argument.
It returns a new sorted list, not affecting the list passed to the argument
sort(3,2,1) => [1, 2, 3]
sort('a',3,11,1) => [1, 3, 11, 'a']
list = l(4,3,2,1); sort(list) => [1, 2, 3, 4]
### `sort_key(list, key_expr)`
Sorts a copy of the list in the order or keys as defined by the `key_expr` for each element
sort_key([1,3,2],_) => [1, 2, 3] sort_key([1,3,2],-_) => [3, 2, 1] sort_key(l(range(10)),rand(1)) => [1, 0, 9, 6, 8, 2, 4, 5, 7, 3] sort_key(l(range(20)),str(_)) => [0, 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 3, 4, 5, 6, 7, 8, 9]### `range(to), range(from, to), range(from, to, step)` Creates a range of numbers from `from`, no greater/larger than `to`. The `step` parameter dictates not only the increment size, but also direction (can be negative). The returned value is not a proper list, just the iterator but if for whatever reason you need a proper list with all items evaluated, use `l(range(to))`. Primarily to be used in higher order functions
range(10) => [...] l(range(10)) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] map(range(10),_*_) => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] reduce(range(10),_a+_, 0) => 45 range(5,10) => [5, 6, 7, 8, 9] range(20, 10, -2) => [20, 18, 16, 14, 12]## Map operations Scarpet supports map structures, aka hashmaps, dicts etc. Map structure can also be used, with `null` values as sets. Apart from container access functions, (`. , get, put, has, delete`), the following functions: ### `{values, ...}`,`{iterator}`,`{key -> value, ...}`,`m(values, ...)`, `m(iterator)`, `m(l(key, value), ...))` creates and initializes a map with supplied keys, and values. If the arguments contains a flat list, these are all treated as keys with no value, same goes with the iterator - creates a map that behaves like a set. If the arguments is a list of lists, they have to have two elements each, and then first is a key, and second, a value In map creation context (directly inside `{}` or `m{}` call), `->` operator acts like a pair constructor for simpler syntax providing key value pairs, so the invocation to `{foo -> bar, baz -> quux}` is equivalent to `{l(foo, bar), l(baz, quux)}`, which is equivalent to somewhat older, but more traditional functional form of `m(l(foo, bar),l(baz, quuz))`. Internally, `{?}`(list syntax) and `m(?)`(function syntax) are equivalent. `{}` is simply translated to `m()` in the scarpet preprocessing stage. This means that internally the code has always expression syntax despite `{}` not using different kinds of brackets and not being proper operators. This means that `m(}` and `{)` are also valid although not recommended as they will make your code far less readable. When converting map value to string, `':'` is used as a key-value separator due to tentative compatibility with NBT notation, meaning in simpler cases maps can be converted to NBT parsable string by calling `str()`. This however does not guarantee a parsable output. To properly convert to NBT value, use `encode_nbt()`.
{1, 2, 'foo'} => {1: null, 2: null, foo: null}
m() <=> {} (empty map)
{range(10)} => {0: null, 1: null, 2: null, 3: null, 4: null, 5: null, 6: null, 7: null, 8: null, 9: null}
m(l(1, 2), l(3, 4)) <=> {1 -> 2, 3 -> 4} => {1: 2, 3: 4}
reduce(range(10), put(_a, _, _*_); _a, {})
=> {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
### `keys(map), values(map), pairs(map)`
Returns full lists of keys, values and key-value pairs (2-element lists) for all the entries in the map
# Minecraft specific API and `scarpet` language add-ons and commands
Here is the gist of the Minecraft related functions. Otherwise the CarpetScript could live without Minecraft.
## Dimension warning
One note, which is important is that most of the calls for entities and blocks would refer to the current
dimension of the caller, meaning, that if we for example list all the players using `player('all')` function,
if a player is in the other dimension, calls to entities and blocks around that player would point incorrectly.
Moreover, running commandblocks in the spawn chunks would mean that commands will always refer to the overworld
blocks and entities. In case you would want to run commands across all dimensions, just run three of them,
using `/execute in overworld/the_nether/the_end run script run ...` and query players using `player('*')`,
which only returns players in current dimension, or use `in_dimension(expr)` function.# Blocks / World API
## Specifying blocks
### `block(x, y, z)`, `block(l(x,y,z))`, `block(state)`
Returns either a block from specified location, or block with a specific state (as used by `/setblock` command),
so allowing for block properties, block entity data etc. Blocks otherwise can be referenced everywhere by its simple
string name, but its only used in its default state.
block('air') => air
block('iron_trapdoor[half=top]') => iron_trapdoor
block(0,0,0) == block('bedrock') => 1
block('hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') => hopper
Retrieving a block with `block` function has also a side-effect of evaluating its current state and data.
So if you use it later it will reflect block state and data of the block that was when block was called, rather than
when it was used. Block values passed in various places like `scan` functions, etc, are not fully evaluated unless
its properties are needed. This means that if the block at the location changes before its queried in the program this
might result in getting the later state, which might not be desired. Consider the following example:
set(10,10,10,'stone'); scan(10,10,10,0,0,0, b = _); set(10,10,10,'air'); print(b); // 'air', block was remembered 'lazily', and evaluated by `print`, when it was already set to air set(10,10,10,'stone'); scan(10,10,10,0,0,0, b = block(_)); set(10,10,10,'air'); print(b); // 'stone', block was evaluated 'eagerly' but call to `block`## World Manipulation All the functions below can be used with block value, queried with coord triple, or 3-long list. All `pos` in the functions referenced below refer to either method of passing block position. ### `set(pos, block, property?, value?, ..., block_data?)`, `set(pos, block, l(property?, value?, ...), block_data?)` First part of the `set` function is either a coord triple, list of three numbers, or other block with coordinates. Second part, `block` is either block value as a result of `block()` function string value indicating the block name, and optional `property - value` pairs for extra block properties. Optional `block_data` include the block data to be set for the target block. If `block` is specified only by name, then if a destination block is the same the `set` operation is skipped, otherwise is executed, for other potential extra properties. The returned value is either the block state that has been set, or `false` if block setting was skipped
set(0,5,0,'bedrock') => bedrock
set(l(0,5,0), 'bedrock') => bedrock
set(block(0,5,0), 'bedrock') => bedrock
scan(0,5,0,0,0,0,set(_,'bedrock')) => 1
set(pos(players()), 'bedrock') => bedrock
set(0,0,0,'bedrock') => 0 // or 1 in overworlds generated in 1.8 and before
scan(0,100,0,20,20,20,set(_,'glass'))
// filling the area with glass
scan(0,100,0,20,20,20,set(_,block('glass')))
// little bit faster due to internal caching of block state selectors
b = block('glass'); scan(0,100,0,20,20,20,set(_,b))
// yet another option, skips all parsing
set(x,y,z,'iron_trapdoor') // sets bottom iron trapdoor
set(x,y,z,'iron_trapdoor[half=top]') // Incorrect. sets bottom iron trapdoor - no parsing of properties
set(x,y,z,'iron_trapdoor','half','top') // correct - top trapdoor
set(x,y,z, block('iron_trapdoor[half=top]')) // also correct, block() provides extra parsing
set(x,y,z,'hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') // extra block data
set(x,y,z,'hopper', l('facing', 'north'), nbt('{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') ) // same
### `without_updates(expr)`
Evaluates subexpression without causing updates when blocks change in the world.
For synchronization sake, as well as from the fact that suppressed update can only happen within a tick,
the call to the `expr` is docked on the main server task.
Consider following scenario: We would like to generate a bunch of terrain in a flat world following a perlin noise
generator. The following code causes a cascading effect as blocks placed on chunk borders will cause other chunks to get
loaded to full, thus generated:
__config() -> m(l('scope', 'global'));
__on_chunk_generated(x, z) -> (
scan(x,0,z,0,0,0,15,15,15,
if (perlin(_x/16, _y/8, _z/16) > _y/16,
set(_, 'black_stained_glass');
)
)
)
The following addition resolves this issue, by not allowing block updates past chunk borders:
__config() -> m(l('scope', 'global'));
__on_chunk_generated(x, z) -> (
without_updates(
scan(x,0,z,0,0,0,15,15,15,
if (perlin(_x/16, _y/8, _z/16) > _y/16,
set(_, 'black_stained_glass');
)
)
)
)
### `place_item(item, pos, facing?, sneak?)`
Uses a given item in the world like it was used by a player. Item names are default minecraft item name,
less the minecraft prefix. Default facing is 'up', but there are other options: 'down', 'north', 'east', 'south',
'west', but also there are other secondary directions important for placement of blocks like stairs, doors, etc.
Try experiment with options like 'north-up' which is placed facing north with cursor pointing to the upper part of the
block, or 'up-north', which means a block placed facing up (player looking down) and placed smidge away of the block
center towards north. Optional sneak is a boolean indicating if a player would be sneaking while placing the
block - this option only affects placement of chests and scaffolding at the moment.
Works with items that have the right-click effect on the block placed, like `bone_meal` on grass or axes on logs,
but doesn't open chests / containers, so have no effect on interactive blocks, like TNT, comparators, etc.
Returns true if placement/use was
successful, false otherwise.
place_item('stone',x,y,z) // places a stone block on x,y,z block
place_item('piston,x,y,z,'down') // places a piston facing down
place_item('carrot',x,y,z) // attempts to plant a carrot plant. Returns true if could place carrots at that position.
place_item('bone_meal',x,y,z) // attempts to bonemeal the ground.
place_item('wooden_axe',x,y,z) // attempts to strip the log.
### `set_poi(pos, type, occupancy?)`
Sets a Point of Interest (POI) of a specified type with optional custom occupancy. By default new POIs are not occupied.
If type is `null`, POI at position is removed. In any case, previous POI is also removed. Available POI types are:
* `'unemployed', 'armorer', 'butcher', 'cartographer', 'cleric', 'farmer', 'fisherman', 'fletcher', 'leatherworker', 'librarian', 'mason', 'nitwit', 'shepherd', 'toolsmith', 'weaponsmith', 'home', 'meeting', 'beehive', 'bee_nest', 'nether_portal'`
Interestingly, `unemployed`, and `nitwit` are not used in the game, meaning, they could be used as permanent spatial
markers for scarpet apps. `meeting` is the only one with increased max occupancy of 32.
### `set_biome(pos, biome_name, update=true)`
Changes the biome at that block position. if update is specified and false, then chunk will not be refreshed
on the clients. Biome changes can only be send to clients with the entire data from the chunk.
### `update(pos)`
Causes a block update at position.
### `block_tick(pos)`
Causes a block to tick at position.
### `random_tick(pos)`
Causes a random tick at position.
### `destroy(pos), destroy(pos, -1), destroy(pos,
mine(x,y,z) ->
(
p = player();
slot = p~'selected_slot';
item_tuple = inventory_get(p, slot);
if (!item_tuple, destroy(x,y,z,'air'); return()); // empty hand, just break with 'air'
l(item, count, tag) = item_tuple;
tag_back = destroy(x,y,z, item, tag);
if (tag_back == false, // failed to break the item
return(tag_back)
);
if (tag_back == true, // block broke, tool has no tag
return(tag_back)
);
if (tag_back == null, //item broke
delete(tag:'Damage');
inventory_set(p, slot, count-1, item, tag);
return(tag_back)
);
if (type(tag_back) == 'nbt', // item didn't break, here is the effective nbt
inventory_set(p, slot, count, item, tag_back);
return(tag_back)
);
print('How did we get here?');
)
### `harvest(player, pos)`
Causes a block to be harvested by a specified player entity. Honors player item enchantments, as well as damages the
tool if applicable. If the entity is not a valid player, no block gets destroyed. If a player is not allowed to break
that block, a block doesn't get destroyed either.
## Block and World querying
### `pos(block), pos(entity)`
Returns a triple of coordinates of a specified block or entity. Technically entities are queried with `query` function
and the same can be achieved with `query(entity,'pos')`, but for simplicity `pos` allows to pass all positional objects.
pos(block(0,5,0)) => l(0,5,0)
pos(players()) => l(12.3, 45.6, 32.05)
pos(block('stone')) => Error: Cannot fetch position of an unrealized block
### `pos_offset(pos, direction, amount?)`
Returns a coords triple that is offset in a specified `direction` by `amount` of blocks. The default offset amount is
1 block. To offset into opposite facing, use negative numbers for the `amount`.
pos_offset(block(0,5,0), 'up', 2) => l(0,7,0) pos_offset(l(0,5,0), 'up', -2 ) => l(0,3,0)### `block_properties(pos)` Returns a list of available block properties for a particular block. If a block has no properties, returns an empty list. ### `property(pos, name)` Returns property of block at `pos`, or specified by `block` argument. If a block doesn't have that property, `null` value is returned. Returned values are always strings. It is expected from the user to know what to expect and convert values to numbers using `number()` function or booleans using `bool()` function.
set(x,y,z,'iron_trapdoor','half','top'); property(x,y,z,'half') => top
set(x,y,z,'air'); property(x,y,z,'half') => null
property(block('iron_trapdoor[half=top]'),'half') => top
property(block('iron_trapdoor[half=top]'),'powered') => false
bool(property(block('iron_trapdoor[half=top]'),'powered')) => 0
### `block_data(pos)`
Return NBT string associated with specific location, or null if the block does not carry block data. Can be currently
used to match specific information from it, or use it to copy to another block
block_data(x,y,z) => '{TransferCooldown:0,x:450,y:68, ... }'
### `poi(pos), poi(pos, radius), poi(pos, radius, status)`
Queries a POI (Point of Interest) at a given position, returning `null` if none is found, or tuple of poi type and its
occupancy load. With optional `radius` and `status`, returns a list of POIs around `pos` within a given `radius`.
If `status` is specified (either 'available', or 'occupied') returns only POIs with that status. The return format is
again, poi type, occupancy load, and extra tripple of coordinates.
Querying for POIs using the radius is intended use of POI mechanics and ability of accessing individual POIs
via `poi(pos)` in only provided for completness.
poi(x,y,z) => null // nothing set at position poi(x,y,z) => ['meeting',3] // its a bell-type meeting point occupied by 3 villagers poi(x,y,z,5) => [] // nothing around poi(x,y,z,5) => [['nether_portal',0,[7,8,9]],['nether_portal',0,[7,9,9]]] // two portal blocks in the range### `biome(pos)` Returns the biome at that block position. ### `solid(pos)` Boolean function, true if the block is solid. ### `air(pos)` Boolean function, true if a block is air... or cave air... or void air... or any other air they come up with. ### `liquid(pos)` Boolean function, true if the block is liquid, or waterlogged (with any liquid). ### `flammable(pos)` Boolean function, true if the block is flammable. ### `transparent(pos)` Boolean function, true if the block is transparent. ### `opacity(pos)` Numeric function, returning the opacity level of a block. ### `blocks_daylight(pos)` Boolean function, true if the block blocks daylight. ### `emitted_light(pos)` Numeric function, returning the light level emitted from the block. ### `light(pos)` Numeric function, returning the total light level at position. ### `block_light(pos)` Numeric function, returning the block light at position (from torches and other light sources). ### `sky_light(pos)` Numeric function, returning the sky light at position (from sky access). ### `see_sky(pos)` Boolean function, returning true if the block can see sky. ### `hardness(pos)` Numeric function, indicating hardness of a block. ### `blast_resistance(pos)` Numeric function, indicating blast_resistance of a block. ### `in_slime_chunk(pos)` Boolean indicating if the given block position is in a slime chunk. ### `top(type, pos)` Returns the Y value of the topmost block at given x, z coords (y value of a block is not important), according to the heightmap specified by `type`. Valid options are: * `light`: topmost light blocking block (1.13 only) * `motion`: topmost motion blocking block * `terrain`: topmost motion blocking block except leaves * `ocean_floor`: topmost non-water block * `surface`: topmost surface block
top('motion', x, y, z) => 63
top('ocean_floor', x, y, z) => 41
### `suffocates(pos)`
Boolean function, true if the block causes suffocation.
### `power(pos)`
Numeric function, returning redstone power level at position.
### `ticks_randomly(pos)`
Boolean function, true if the block ticks randomly.
### `blocks_movement(pos)`
Boolean function, true if the block at position blocks movement.
### `block_sound(pos)`
Returns the name of sound type made by the block at position. One of:
`'wood'`, `'gravel'`, `'grass'`, `'stone'`, `'metal'`, `'glass'`, `'wool'`, `'sand'`, `'snow'`,
`'ladder'`, `'anvil'`, `'slime'`, `'sea_grass'`, `'coral'`
### `material(pos)`
Returns the name of material of the block at position. very useful to target a group of blocks. One of:
`'air'`, `'void'`, `'portal'`, `'carpet'`, `'plant'`, `'water_plant'`, `'vine'`, `'sea_grass'`, `'water'`,
`'bubble_column'`, `'lava'`, `'snow_layer'`, `'fire'`, `'redstone_bits'`, `'cobweb'`, `'redstone_lamp'`, `'clay'`,
`'dirt'`, `'grass'`, `'packed_ice'`, `'sand'`, `'sponge'`, `'wood'`, `'wool'`, `'tnt'`, `'leaves'`, `'glass'`,
`'ice'`, `'cactus'`, `'stone'`, `'iron'`, `'snow'`, `'anvil'`, `'barrier'`, `'piston'`, `'coral'`, `'gourd'`,
`'dragon_egg'`, `'cake'`
### `map_colour(pos)`
Returns the map colour of a block at position. One of:
`'air'`, `'grass'`, `'sand'`, `'wool'`, `'tnt'`, `'ice'`, `'iron'`, `'foliage'`, `'snow'`, `'clay'`, `'dirt'`,
`'stone'`, `'water'`, `'wood'`, `'quartz'`, `'adobe'`, `'magenta'`, `'light_blue'`, `'yellow'`, `'lime'`, `'pink'`,
`'gray'`, `'light_gray'`, `'cyan'`, `'purple'`, `'blue'`, `'brown'`, `'green'`, `'red'`, `'black'`, `'gold
'`, `'diamond'`, `'lapis'`, `'emerald'`, `'obsidian'`, `'netherrack'`, `'white_terracotta'`, `'orange_terracotta'`,
`'magenta_terracotta'`, `'light_blue_terracotta'`, `'yellow_terracotta'`, `'lime_terracotta'`, `'pink_terracotta'`,
`'gray_terracotta'`, `'light_gray_terracotta'`, `'cyan_terracotta'`, `'purple_terracotta'`, `'blue_terracotta'`,
`'brown_terracotta'`, `'green_terracotta'`, `'red_terracotta'`, `'black_terracotta'`
### `loaded(pos)`
Boolean function, true if the block is accessible for the game mechanics. Normally `scarpet` doesn't check if operates
on loaded area - the game will automatically load missing blocks. We see this as an advantage. Vanilla `fill/clone`
commands only check the specified corners for loadness.
To check if a block is truly loaded, I mean in memory, use `generation_status(x) != null`, as chunks can still be loaded
outside of the playable area, just are not used by any of the game mechanic processes.
loaded(pos(players())) => 1 loaded(100000,100,1000000) => 0### `(Deprecated) loaded_ep(pos)` Boolean function, true if the block is loaded and entity processing, as per 1.13.2 Deprecated as of scarpet 1.6, use `loaded_status(x) > 0`, or just `loaded(x)` with the same effect ### `loaded_status(pos)` Returns loaded status as per new 1.14 chunk ticket system, 0 for inaccessible, 1 for border chunk, 2 for redstone ticking, 3 for entity ticking ### `is_chunk_generated(pos)`, `is_chunk_generated(pos, force)` Returns `true` if the region file for the chunk exists, `false` otherwise. If optional force is `true` it will also check if the chunk has a non-empty entry in its region file Can be used to assess if the chunk has been touched by the game or not. `generation_status(pos, false)` only works on currently loaded chunks, and `generation_status(pos, true)` will create an empty loaded chunk, even if it is not needed, so `is_chunk_generated` can be used as a efficient proxy to determine if the chunk physically exists. Running `is_chunk_generated` is has no effects on the world, but since it is an external file operation, it is considerably more expensive (unless area is loaded) than other generation and loaded checks. ### `generation_status(pos), generation_status(pos, true)` Returns generation status as per the ticket system. Can return any value from several available but chunks can only be stable in a few states: `full`, `features`, `liquid_carvers`, and `structure_starts`. Returns `null` if the chunk is not in memory unless called with optional `true`. ### `inhabited_time(pos)` Returns inhabited time for a chunk. ### `spawn_potential(pos)` Returns spawn potential at a location (1.16+ only) ### `reload_chunk(pos)` Sends full chunk data to clients. Useful when lots stuff happened and you want to refresh it on the clients. ### `reset_chunk(pos)`, `reset_chunk(from_pos, to_pos)`, `reset_chunk(l(pos, ...))` Removes and resets the chunk, all chunks in the specified area or all chunks in a list at once, removing all previous blocks and entities, and replacing it with a new generation. For all currently loaded chunks, they will be brought to their current generation status, and updated to the player. All chunks that are not in the loaded area, will only be generated to the `'structure_starts'` status, allowing to generate them fully as players are visiting them. Chunks in the area that has not been touched yet by the game will not be generated / regenerated. It returns a `map` with a report indicating how many chunks were affected, and how long each step took: * `requested_chunks`: total number of chunks in the requested area or list * `affected_chunks`: number of chunks that will be removed / regenerated * `loaded_chunks`: number of currently loaded chunks in the requested area / list * `relight_count`: number of relit chunks * `relight_time`: time took to relit chunks * `layer_count_
for(neighbours(x,y,z),air(_)) => 4 // number of air blocks around a block### `rect(centre, range?, positive_range?)` Returns an iterator, just like `range` function that iterates over a rectangular area of blocks. If only center point is specified, it iterates over 27 blocks. If `range` arguments are specified, expands selection by the respective number of blocks in each direction. If `positive_range` arguments are specified, it uses `range` for negative offset, and `positive_range` for positive. `centre` can be defined either as three coordinates, a list of three coords, or a block value. `range` and `positive_range` can have the same representations, just if its a block, it computes the distance to the center as range instead of taking the values as is.` ### `diamond(centre_pos, radius?, height?)` Iterates over a diamond like area of blocks. With no radius and height, its 7 blocks centered around the middle (block + neighbours). With a radius specified, it expands shape on x and z coords, and with a custom height, on y. Any of these can be zero as well. radius of 0 makes a stick, height of 0 makes a diamond shape pad. # Entity API ## Entity Selection Entities have to be fetched before using them. Entities can also change their state between calls to the script if game ticks occur either in between separate calls to the programs, or if the program calls `game_tick` on its own. In this case - entities would need to be re-fetched, or the code should account for entities dying. ### `player(), player(type), player(name)` With no arguments, it returns the calling player or the player closest to the caller. Note that the main context will receive `p` variable pointing to this player. With `type` or `name` specified, it will try first to match a type, returning a list of players matching a type, and if this fails, will assume its player name query retuning player with that name, or `null` if no player was found. With `'all'`, list of all players in the game, in all dimensions, so end user needs to be cautious, that you might be referring to wrong blocks and entities around the player in question. With `type = '*'` it returns all players in caller dimension, `'survival'` returns all survival and adventure players, `'creative'` returns all creative players, `'spectating'` returns all spectating players, and `'!spectating'`, all not-spectating players. If all fails, with `name`, the player in question, if he/she is logged in. ### `entity_id(uuid), entity_id(id)` Fetching entities either by their ID obtained via `entity ~ 'id'`, which is unique for a dimension and current world run, or by UUID, obtained via `entity ~ 'uuid'`. It returns null if no such entity is found. Safer way to 'store' entities between calls, as missing entities will be returning `null`. Both calls using UUID or numerical ID are `O(1)`, but obviously using UUIDs takes more memory and compute. ### `entity_list(type)` Returns global lists of entities in the current dimension of a specified type. Currently the following selectors are available: * `*`: all entites * `living` * `items` * `players` * `!players` ### `entity_area(type, cx, cy, cz, dx, dy, dz)` Returns entities of a specified type in an area centered on `cx, cy, cz` and at most `dx, dy, dz` blocks away from the center point. Uses the same selectors as `entities_list`. ### `entity_selector(selector)` Returns entities satisifying given vanilla entity selector. Most complex among all the methods of selecting entities, but the most capable. Selectors are cached so it should be as fast as other methods of selecting entities. ### `spawn(name, pos, nbt?)` Spawns and places an entity in world, like `/summon` vanilla command. Requires a position to spawn, and optional extra nbt data to merge with the entity. What makes it different from calling `run('summon ...')`, is the fact that you get the entity back as a return value, which is swell. ## Entity Manipulation Unlike with blocks, that use a plethora of vastly different querying functions, entities are queried with the `query` function and altered via the `modify` function. Type of information needed or values to be modified are different for each call. Using `~` (in) operator is an alias for `query`. Especially useful if a statement has no arguments, which in this case can be radically simplified:
query(p, 'name') <=> p ~ 'name' // much shorter and cleaner
query(p, 'holds', 'offhand') <=> p ~ l('holds', 'offhand') // not really but can be done
### `query(e, 'removed')`
Boolean. True if the entity is removed.
### `query(e, 'id')`
Returns numerical id of the entity. Most efficient way to keep track of entites in a script.
Ids are only unique within current game session (ids are not preserved between restarts),
and dimension (each dimension has its own ids which can overlap).
### `query(e, 'uuid')`
Returns the UUID (unique id) of the entity. Can be used to access entities with the other vanilla commands and
remains unique regardless of the dimension, and is preserved between game restarts. Apparently players cannot be
accessed via UUID, but should be accessed with their name instead.
map(entities_area('*',x,y,z,30,30,30),run('kill '+query(_,'id'))) // doesn't kill the player
### `query(e, 'pos')`
Triple of the entity's position
### `query(e, 'location')`
Quin-tuple of the entity's position (x, y, and z coords), and rotation (yaw, pitch)
### `query(e, 'x'), query(e, 'y'), query(e, 'z')`
Respective component of entity's coordinates
### `query(e, 'pitch'), query(e, 'yaw')`
Pitch and Yaw or where entity is looking.
### `query(e, 'look')`
Returns a 3d vector where the entity is looking.
### `query(e, 'motion')`
Triple of entity's motion vector, `l(motion_x, motion_y, motion_z)`
### `query(e, 'motion_x'), query(e, 'motion_y'), query(e, 'motion_z')`
Respective component of the entity's motion vector
### `query(e, 'name'), query(e, 'display_name'), query(e, 'custom_name'), query(e, 'type')`
String of entity name
query(e,'name') => Leatherworker query(e,'custom_name') => null query(e,'type') => villager## `query(e, 'command_name')` Returns a valid string to be used in commands to address an entity. Its UUID for all entities except player, where its their name. ## `query(e, 'persistence')` Returns if a mob has a persistence tag or not. Returns `null` for non-mob entities.
run('/kill ' + e~'command_name');
### `query(e, 'is_riding')`
Boolean, true if the entity is riding another entity.
### `query(e, 'is_ridden')`
Boolean, true if another entity is riding it.
### `query(e, 'passengers')`
List of entities riding the entity.
### `query(e, 'mount')`
Entity that `e` rides.
### `query(e, 'tags')`
List of entity's tags.
### `query(e, 'has_tag',tag)`
Boolean, true if the entity is marked with `tag`.
### `query(e, 'is_burning')`
Boolean, true if the entity is burning.
### `query(e, 'fire')`
Number of remaining ticks of being on fire.
### `query(e, 'silent')`
Boolean, true if the entity is silent.
### `query(e, 'gravity')`
Boolean, true if the entity is affected by gravity, like most entities are.
### `query(e, 'immune_to_fire')`
Boolean, true if the entity is immune to fire.
### `query(e, 'dimension')`
Name of the dimension the entity is in.
### `query(e, 'height')`
Height of the entity in blocks.
### `query(e, 'width')`
Width of the entity in blocks.
### `query(e, 'eye_height')`
Eye height of the entity in blocks.
### `query(e, 'age')`
Age of the entity in ticks, i.e. how long it existed.
### `query(e, 'breeding_age')`
Breeding age of passive entity, in ticks. If negative, time to adulthood, if positive, breeding cooldown
### `query(e, 'despawn_timer')`
For living entities, the number of ticks they fall outside of immediate player presence.
### `query(e, 'portal_cooldown')`
Number of ticks remaining until an entity can use a portal again.
### `query(e, 'portal_timer')`
Number of ticks an entity sits in a portal.
### `query(e, 'item')`
The item triple (name, count, nbt) if its an item entity, `null` otherwise
### `query(e, 'count')`
Number of items in a stack from item entity, `null` otherwise
### `query(e, 'pickup_delay')`
Retrieves pickup delay timeout for an item entity, `null` otherwise
### `query(e, 'is_baby')`
Boolean, true if its a baby.
### `query(e, 'target')`
Returns mob's attack target or null if none or not applicable.
### `query(e, 'home')`
Returns creature's home position or null if none or not applicable.
### `query(e, 'pose')`
Returns a pose of an entity, one of the following options
* `'standing'`
* `'fall_flying'`
* `'sleeping'`
* `'swimming'`
* `'spin_attack'`
* `'crouching'`
* `'dying'`
### `query(e, 'sneaking')`
Boolean, true if the entity is sneaking.
### `query(e, 'sprinting')`
Boolean, true if the entity is sprinting.
### `query(e, 'swimming')`
Boolean, true if the entity is swimming.
### `query(e, 'jumping')`
Boolean, true if the entity is jumping.
### `query(e, 'gamemode')`
String with gamemode, or `null` if not a player.
### `query(e, 'gamemode_id')`
Good'ol gamemode id, or null if not a player.
### `query(e, 'player_type')`
Returns `null` if the argument is not a player, otherwise:
* `singleplayer`: for singleplayer game
* `multiplayer`: for players on a dedicated server
* `lan_host`: for singleplayer owner that opened the game to LAN
* `lan_player`: for all other players that connected to a LAN host
* `fake`: any carpet-spanwed fake player
* `shadow`: any carpet-shadowed real player
* `realms`: ?
### `query(e, 'category')`
Returns a lowercase string containing the category of the entity (hostile, passive, water, ambient, misc)
### `query(e, 'team')`
Team name for entity, or `null` if no team is assigned.
### `query(e, 'ping')`
Player's ping in milliseconds, or `null` if its not a player.
### `query(e, 'permission_level')`
Player's permission level, or `null` if not applicable for this entity.
### `query(e, 'effect', name?)`
Without extra arguments, it returns list of effect active on a living entity. Each entry is a triple of short
effect name, amplifier, and remaining duration in ticks. With an argument, if the living entity has not that potion active,
returns `null`, otherwise return a tuple of amplifier and remaining duration.
query(p,'effect') => [[haste, 0, 177], [speed, 0, 177]] query(p,'effect','haste') => [0, 177] query(p,'effect','resistance') => null### `query(e, 'health')` Number indicating remaining entity health, or `null` if not applicable. ### `query(e, 'hunger')` ### `query(e, 'saturation')` ### `query(e, 'exhaustion')` Retrieves player hunger related information. For non-players, returns `null`. ### `query(e, 'holds', slot?)` Returns triple of short name, stack count, and NBT of item held in `slot`, or `null` if nothing or not applicable. Available options for `slot` are: * `mainhand` * `offhand` * `head` * `chest` * `legs` * `feet` If `slot` is not specified, it defaults to the main hand. ### `query(e, 'selected_slot')` Number indicating the selected slot of entity's inventory. Currently only applicable to players. ### `query(e, 'facing', order?)` Returns where the entity is facing. optional order (number from 0 to 5, and negative), indicating primary directions where entity is looking at. From most prominent (order 0) to opposite (order 5, or -1) ### `query(e, 'trace', reach?, options?...)` Returns the result of ray tracing from entity perspective, indicating what it is looking at. Default reach is 4.5 blocks (5 for creative players), and by default it traces for blocks and entities, identical to player attack tracing action. This can be customized with `options`, use 'blocks' to trace for blocks, 'liquids' to include liquid blocks as possible results, and 'entities' to trace entities. Any combination of the above is possible. When tracing entities and blocks, blocks will take over the priority even if transparent or non-colliding (aka fighting chickens in tall grass). Regardless of the options selected, the result could be `null` if nothing is in reach, entity, if look targets an entity, and block value if block is in reach. ### `query(e, 'nbt', path?)` Returns full NBT of the entity. If path is specified, it fetches only the portion of the NBT that corresponds to the path. For specification of `path` attribute, consult vanilla `/data get entity` command. Note that calls to `nbt` are considerably more expensive comparing to other calls in Minecraft API, and should only be used when there is no other option. Returned value is of type `nbt`, which can be further manipulated with nbt type objects via `get, put, has, delete`, so try to use API calls first for that. ## Entity Modification Like with entity querying, entity modifications happen through one function. Most position and movements modifications don't work currently on players as their position is controlled by clients. Currently there is no ability to modify NBT directly, but you could always use `run('data modify entity ...')` ### `modify(e, 'remove')` Removes (not kills) entity from the game. ### `modify(e, 'kill')` Kills the entity. ### `modify(e, 'pos', x, y, z), modify(e, 'pos', l(x,y,z) )` Moves the entity to a specified coords. ### `modify(e, 'location', x, y, z, yaw, pitch), modify(e, 'location', l(x, y, z, yaw, pitch) )` Changes full location vector all at once. ### `modify(e, 'x', x), modify(e, 'y', y), modify(e, 'z', z)` Moves the entity in one direction. ### `modify(e, 'pitch', pitch), modify(e, 'yaw', yaw)` Changes entity's pitch or yaw. ### `modify(e, 'move', x, y, z), modify(e, 'move', l(x,y,z) )` Moves the entity by a vector from its current location. ### `modify(e, 'motion', x, y, z), modify(e, 'motion', l(x,y,z) )` Sets the motion vector (where and how much entity is moving). ### `modify(e, 'motion_z', x), modify(e, 'motion_y', y), modify(e, 'motion_z', z)` Sets the corresponding component of the motion vector. ### `modify(e, 'accelerate', x, y, z), modify(e, 'accelerate', l(x, y, z) )` Adds a vector to the motion vector. Most realistic way to apply a force to an entity. ### `modify(e, 'custom_name')`, `modify(e, 'custom_name', name)`, `modify(e, 'custom_name', name, visible)` Sets the custom name of the entity. Without arguments - clears current custom name. Optional visible affects if the custom name is always visible, even through blocks. ### `modify(e, 'persistence', bool?)` Sets the entity persistence tag to `true` (default) or `false`. Only affects mobs. Persistent mobs don't despawn and don't count towards the mobcap. ### `modify(e, 'age', number)` Modifies entity's internal age counter. Fiddling with this will affect directly AI behaviours of complex entities, so use it with caution. ### `modify(e, 'pickup_delay', number)` Sets the pickup delay for the item entity. ### `modify(e, 'breeding_age', number)` Sets the breeding age for the animal. ### `modify(e, 'despawn_timer', number)` Sets a custom despawn timer value. ### `modify(e, 'portal_cooldown', number)` Sets a custom number of ticks remaining until an entity can use a portal again. ### `modify(e, 'portal_timer', number)` Sets a custom number of ticks an entity sits in a portal. ### `modify(e, 'dismount')` Dismounts riding entity. ### `modify(e, 'mount', other)` Mounts the entity to the `other`. ### `modify(e, 'drop_passengers')` Shakes off all passengers. ### `modify(e, 'mount_passengers', passenger, ? ...), modify(e, 'mount_passengers', l(passengers) )` Mounts on all listed entities on `e`. ### `modify(e, 'tag', tag, ? ...), modify(e, 'tag', l(tags) )` Adds tag(s) to the entity. ### `modify(e, 'clear_tag', tag, ? ...), modify(e, 'clear_tag', l(tags) )` Removes tag(s) from the entity. ### `modify(e, 'talk')` Make noises. ### `modify(e, 'ai', boolean)` If called with `false` value, it will disable AI in the mob. `true` will enable it again. ### `modify(e, 'no_clip', boolean)` Sets if the entity obeys any collisions, including collisions with the terrain and basic physics. Not affecting players, since they are controlled client side. ### `modify(e, 'effect', name?, duration?, amplifier?, show_particles?, show_icon?)` Applies status effect to the living entity. Takes several optional parameters, which default to `0`, `true` and `true`. If no duration is specified, or if it's null or 0, the effect is removed. If name is not specified, it clears all effects. ### `modify(e, 'home', null), modify(e, 'home', block, distance?), modify(e, 'home', x, y, z, distance?)` Sets AI to stay around the home position, within `distance` blocks from it. `distance` defaults to 16 blocks. `null` removes it. _May_ not work fully with mobs that have this AI built in, like Villagers. ### `modify(e, 'gamemode', gamemode?), modify(e, 'gamemode', gamemode_id?)` Modifies gamemode of player to whatever string (case-insensitive) or number you put in. * 0: survival * 1: creative * 2: adventure * 3: spectator ### `modify(e, 'jumping', boolean)` Will make the entity constantly jump if set to true, and will stop the entity from jumping if set to false. Note that jumping parameter can be fully controlled by the entity AI, so don't expect that this will have a permanent effect. Use `'jump'` to make an entity jump once for sure. Requires a living entity as an argument. ### `modify(e, 'jump'))` Will make the entity jump once. ### `modify(e, 'silent', boolean)` Silences or unsilences the entity. ### `modify(e, 'gravity', boolean)` Toggles gravity for the entity. ### `modify(e, 'fire', ticks)` Will set entity on fire for `ticks` ticks. Set to 0 to extinguish. ### `modify(e, 'hunger', value)` ### `modify(e, 'saturation', value)` ### `modify(e, 'exhaustion', value)` Modifies directly player raw hunger components. Has no effect on non-players ### `modify(e, 'add_exhaustion', value)` adds exhaustion value to the current player exhaustion level - that's the method you probably want to use to manipulate how much 'food' some action cost. ### `modify(e, 'nbt_merge', partial_tag)` Merges a partial tag into the entity data and reloads the entity from its updated tag. Cannot be applied to players ### `modify(e, 'nbt', tag)` Reloads the entity from a supplied tag. Better get a valid entity tag, what can go wrong. Wonder what would happen if transplant rabbit's brain into a villager. Cannot be applied to players. ## Entity Events There is a number of events that happen to entities that you can attach your own code to in the form of event handlers. The event handler is any function that runs in your package that accepts certain expected parameters, which you can expand with your own arguments. When it comes to the moment when the given command needs to be executed, it does so providing that number of arguments it accepts is equal number of event arguments, and extra arguments passed when defining the callback with `entity_event` The following events can be handled by entities. * `'on_tick'`: executes every tick right before the entity is ticked in the game. Required arguments: `entity` * `'on_death'`: executes once when a living entity dies. Required arguments: `entity, reason` * `'on_removed'`: execute once when an entity is removed. Required arguments: `entity` * `'on_damaged'`: executed every time a living entity is about to receive damage. Required arguments: `entity, amount, source, attacking_entity` It doesn't mean that all entity types will have a chance to execute a given event, but entities will not error when you attach inapplicable event to it. ### `entity_event(e, event, call_name, args...)` Attaches specific function from the current package to be called upon the `event`, with extra `args` carried to the original required arguments for the event handler.
protect_villager(entity, amount, source, source_entity, healing_player) ->
(
if(source_entity && source_entity~'type' != 'player',
modify(entity, 'health', amount + entity~'health' );
particle('end_rod', pos(entity)+l(0,3,0));
print(str('%s healed thanks to %s', entity, healing_player))
)
);
__on_player_interacts_with_entity(player, entity, hand) ->
(
if (entity~'type' == 'villager',
entity_event(entity, 'on_damage', 'protect_villager', player~'name')
)
)
In this case this will protect a villager from entity damage (zombies, etc.) except players by granting all the
health back to the villager after being harmed.
# Inventory and Items API
## Manipulating inventories of blocks and entities
Most functions in this category require inventory as the first argument. Inventory could be specified by an entity,
or a block, or position (three coordinates) of a potential block with inventory. Player enderchest inventory require
two arguments, keyword `'enderchest'`, followed by the player entity argument, or a single argument as a string of a
form: `'enderchest_steve'`. If your player name starts with enderchest, it can be always accessed by passing a player
entity value. If all else fails, it will try to identify first three arguments as coordinates of a block position of
a block inventory. Player inventories can also be called by their name.
If the entity or a block doesn't have
an inventory, they typically do nothing and return null.
Most items returned are in the form of a triple of item name, count, and nbt or the extra data associated with an item.
Manipulating of the nbt data can be costly, but retrieving them from the tuple to match other aspects is cheap
### `stack_limit(item)`
Returns number indicating what is the stack limit for the item. Its typically 1 (non-stackable), 16 (like buckets),
or 64 - rest. It is recommended to consult this, as other inventory API functions ignore normal stack limits, and
it is up to the programmer to keep it at bay. As of 1.13, game checks for negative numbers and setting an item to
negative is the same as empty.
stack_limit('wooden_axe') => 1
stack_limit('ender_pearl') => 16
stack_limit('stone') => 64
### `item_category(item)`
Returns the string representing the category of a given item, like `building_blocks`, `combat`, or `tools`.
item_category('wooden_axe') => tools
item_category('ender_pearl') => misc
item_category('stone') => building_blocks
### `recipe_data(item, type?)`, `recipe_data(recipe, type?)`
returns all recipes matching either an `item`, or represent actual `recipe` name. In vanilla datapack, for all items
that have one recipe available, the recipe name is the same as the item name but if an item has multiple recipes, its
direct name can be different.
Recipe type can take one of the following options:
* `'crafting'` - default, crafting table recipe
* `'smelting'` - furnace recipe
* `'blasting'` - blast furnace recipe
* `'smoking'` - smoker recipe
* `'campfire_cooking'` - campfire recipe
* `'stonecutting'` - stonecutter recipe
* `'smithing'` - smithing table (1.16+)
The return value is a list of available recipes (even if there is only one recipe available). Each recipe contains of
an item triple of the crafting result, list of ingredients, each containing a list of possible variants of the
ingredients in this slot, as item triples, or `null` if its a shaped recipe and a given slot in the patterns is left
empty, and recipe specification as another list. Possible recipe specs is:
* `['shaped', width, height]` - shaped crafting. `width` and `height` can be 1, 2 or 3.
* `['shapeless']` - shapeless crafting
* `['smelting', duration, xp]` - smelting/cooking recipes
* `['cutting']` - stonecutter recipe
* `['special']` - special crafting recipe, typically not present in the crafting menu
* `['custom']` - other recipe types
Note that ingredients are specified as tripes, with count and nbt information. Currently all recipes require always one
of the ingredients, and for some recipes, even if the nbt data for the ingredient is specified (e.g. `dispenser`), it
can accept items of any tags.
Also note that some recipes leave some products in the crafting window, and these can be determined using
`crafting_remaining_item()` function
Examples:
recipe_data('iron_ingot_from_nuggets')
recipe_data('iron_ingot')
recipe_data('glass', 'smelting')
### `crafting_remaining_item(item)`
returns `null` if the item has no remaining item in the crafting window when used as a crafting ingredient, or an
item name that serves as a replacement after crafting is done. Currently it can only be buckets and glass bottles.
### `inventory_size(inventory)`
Returns the size of the inventory for the entity or block in question. Returns null if the block or entity don't
have an inventory
inventory_size(player()) => 41
inventory_size('enderchest', player()) => 27 // enderchest
inventory_size(x,y,z) => 27 // chest
inventory_size(block(pos)) => 5 // hopper
### `inventory_has_items(inventory)`
Returns true, if the inventory is not empty, false if it is empty, and null, if its not an inventory.
inventory_has_items(player()) => true
inventory_has_items(x,y,z) => false // empty chest
inventory_has_items(block(pos)) => null // stone
### `inventory_get(inventory, slot)`
Returns the item in the corresponding inventory slot, or null if slot empty or inventory is invalid. You can use
negative numbers to indicate slots counted from 'the back'.
inventory_get(player(), 0) => null // nothing in first hotbar slot
inventory_get(x,y,z, 5) => ['stone', 1, {}]
inventory_get(player(), -1) => ['diamond_pickaxe', 1, {Damage:4}] // slightly damaged diamond pick in the offhand
### `inventory_set(inventory, slot, count, item?, nbt?)`
Modifies or sets a stack in inventory. specify count 0 to empty the slot. If item is not specified, keeps existing
item, just modifies the count. If item is provided - replaces current item. If nbt is provided - adds a tag to the
stack at slot. Returns previous stack in that slot.
inventory_set(player(), 0, 0) => ['stone', 64, {}] // player had a stack of stone in first hotbar slot
inventory_set(player(), 0, 6) => ['diamond', 64, {}] // changed stack of diamonds in player slot to 6
inventory_set(player(), 0, 1, 'diamond_axe','{Damage:5}') => null //added slightly damaged pick to first player slot
### `inventory_find(inventory, item, start_slot?, ), inventory_find(inventory, null, start_slot?)`
Finds the first slot with a corresponding item in the inventory, or if queried with null: the first empty slot.
Returns slot number if found, or null otherwise. Optional start_slot argument allows to skip all preceeding slots
allowing for efficient (so not slot-by-slot) inventory search for items.
inventory_find(player(), 'stone') => 0 // player has stone in first hotbar slot
inventory_find(player(), null) => null // player's inventory has no empty spot
while( (slot = inventory_find(p, 'diamond', slot)) != null, 41, drop_item(p, slot) )
// spits all diamonds from player inventory wherever they are
inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items
### `inventory_remove(inventory, item, amount?)`
Removes amount (defaults to 1) of item from inventory. If the inventory doesn't have the defined amount, nothing
happens, otherwise the given amount of items is removed wherever they are in the inventory. Returns boolean
whether the removal operation was successful. Easiest way to remove a specific item from player inventory
without specifying the slot.
inventory_remove(player(), 'diamond') => 1 // removed diamond from player inventory inventory_remove(player(), 'diamond', 100) => 0 // player doesn't have 100 diamonds, nothing happened### `drop_item(inventory, slot, amount?, )` Drops the items from indicated inventory slot, like player that Q's an item or villager, that exchanges food. You can Q items from block inventories as well. default amount is 0 - which is all from the slot. NOTE: hoppers are quick enough to pick all the queued items from their inventory anyways. Returns size of the actual dropped items.
inventory_drop(player(), 0, 1) => 1 // Q's one item on the ground inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items# Scarpet events system Provides the ability to execute specific function whenever an event occurs. The functions to be registered need to conform with the arguments to the event specification. When loading module functions, each function that starts with `__on_...` and has the required arguments, will be bound automatically. In case of player specific modules, all player action events will be directed to the appropriate player space, and all tick events will be executed in the global context, so its not a good idea to mix these two, so use either of these, or use commands to call tick events directly, or handle player specific data inside a module. In near future using global events (on_tick, on_chunk_generated) will be not allowed in player scoped apps. ### Event list Here is a list of events that can be handled by scarpet. This list includes prefixes required by modules to autoload them, but you can add any function to any event if it accepts required number of parameters:
// world callbacks
__on_tick() // can access blocks and entities in the overworld
__on_tick_nether() // can access blocks and entities in the nether
__on_tick_ender() // can access blocks and entities in the end
__on_chunk_generated(x,z) // called after a chunk is promoted to the full chunk,
// providing lowest x and z coords in the chunk
// event will not work with optifine installed in the game
__on_lightning(block, mode) // mode is `true` if lightning caused horse trap to spawn
// player specific callbacks
__on_player_uses_item(player, item_tuple, hand) // right click action
__on_player_releases_item(player, item_tuple, hand) // client action (e.g. bow)
__on_player_finishes_using_item(player, item_tuple, hand)) // server action (e.g. food), called item is from before it is used.
__on_player_clicks_block(player, block, face) // left click attack on a block
__on_player_breaks_block(player, block) // called after block is broken (the caller receives previous blockstate)
__on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec) // player right clicks block with anything
__on_player_interacts_with_block(player, hand, block, face, hitvec) //right click on a block resulted in activation of said block
__on_player_places_block(player, item_tuple, hand, block) // player have just placed the block.
__on_player_interacts_with_entity(player, entity, hand)
__on_player_chooses_recipe(player, recipe, full_stack)
__on_player_switches_slot(player, from, to)
__on_player_attacks_entity(player, entity)
__on_player_takes_damage(player, amount, source, source_entity)
__on_player_deals_damage(player, amount, entity)
__on_player_dies(player)
__on_player_respawns(player)
__on_player_changes_dimension(player, from_pos, from_dimension, to_pos, to_dimension)
__on_player_rides(player, forward, strafe, jumping, sneaking)
__on_player_jumps(player)
__on_player_deploys_elytra(player)
__on_player_wakes_up(player)
__on_player_starts_sneaking(player)
__on_player_stops_sneaking(player)
__on_player_starts_sprinting(player)
__on_player_stops_sprinting(player)
__on_player_drops_item(player)
__on_player_drops_stack(player)
__on_player_connects(player)
__on_player_disconnects(player, reason)
__on_statistic(player, category, event, value) // player statistic changes
Couple special cases.
* `__on_player_changes_dimension` returns `null` as `to_pos` when player goes back to the overworld from the end
, since the respawn location of the player is not control by the teleport, or a player can see the end credits. After
the player is eligible to respawn in the overworld, `__on_player_respawns` will be triggered.
### `/script event` command
used to display current events and bounded functions. use `add_to` ro register new event, or `remove_from` to
unbind a specific function from an event. Function to be bounded to an event needs to have the same number of
parameters as the action is attempting to bind to (see list above). All calls in modules loaded via `/script load`
that have functions listed above will be automatically bounded and unbounded when script is unloaded.
# Scoreboard
### `scoreboard()`, `scoreboard(objective)`, `scoreboard(objective, key)`, `scoreboard(objective, key, value)`
Displays or modifies individual scoreboard values. With no arguments, returns the list of current objectives.
With specified `objective`, lists all keys (players) associated with current objective. With specified `objective`,
`key`, returns current value of the objective for a given player (key). With additional `value` sets a new scoreboard
value, returning previous value associated with the `key`.
### `scoreboard_add(objective, criterion?)`
Adds a new objective to scoreboard. If `criterion` is not specified, assumes `'dummy'`. Returns `false` if objective
already existed, `true` otherwise.
scoreboard_add('counter')
scoreboard_add('lvl','level')
### `scoreboard_remove(objective)` `scoreboard_remove(objective, key)`
Removes an entire objective, or an entry in the scoreboard associated with the key.
Returns `true` if objective has existed and has been removed, or previous
value of the scoreboard if players score is removed. Returns `null` if objective didn't exist, or a key was missing
for the objective.
### `scoreboard_display(place, objective)`
sets display location for a specified `objective`. If `objective` is `null`, then display is cleared.
# Auxiliary aspects
Collection of other methods that control smaller, yet still important aspects of the game
## Sounds
### `sound(name, pos, volume?, pitch?, mixer?)`
Plays a specific sound `name`, at block or position `pos`, with optional `volume` and modified `pitch`, and under
optional `mixer`. Default values for `volume`, `pitch` and `mixer` are `1.0`, `1.0`, and `master`.
Valid mixer options are `master`, `music`, `record`, `weather`, `block`, `hostile`,`neutral`, `player`, `ambient`
and `voice`. `pos` can be either a block, triple of coords, or a list of thee numbers. Uses the same options as a
corresponding `playsound` command.
## Particles
### `particle(name, pos, count?. spread?, speed?, player?)`
Renders a cloud of particles `name` centered around `pos` position, by default `count` 10 of them, default `speed`
of 0, and to all players nearby, but these options can be changed via optional arguments. Follow vanilla `/particle`
command on details on those options. Valid particle names are
for example `'angry_villager', 'item diamond', 'block stone', 'dust 0.8 0.1 0.1 4'`.
### `particle_line(name, pos, pos2, density?, player?)`
Renders a line of particles from point `pos` to `pos2` with supplied density (defaults to 1), which indicates how far
apart you would want particles to appear, so `0.1` means one every 10cm. If a player (or player name) is supplied, only
that player will receive particles.
### `particle_box(name, pos, pos2, density?, player?)`
### `particle_rect` (deprecated)
Renders a cuboid of particles between points `pos` and `pos2` with supplied density. If a player (or player name) is
supplied, only that player will receive particles.
## Markers
### `draw_shape(shape, duration, key?, value?, ... )`,
### `draw_shape(shape, duration, l(key?, value?, ... ))`,
### `draw_shape(shape, duration, attribute_map)`
### `draw_shape(shape_list)`
Draws a shape in the world that will expire in `duration` ticks. Other attributes of the shape should be provided as
consecutive key - value argument pairs, either as next arguments, or packed in a list, or supplied as a proper key-value
`map`. Arguments may include shared shape attributes, which are all optional, as well as shape-specific attributes, that
could be either optional or required. Shapes will draw properly on all carpet clients. Other connected players that don't
have carpet installed will still be able to see the required shapes in the form of dust particles. Replacement shapes
are not required to follow all attributes precisely, but will allow vanilla clients to receive some experience of your
apps. One of the attributes that will definitely not be honored is the duration - particles will be send once
per shape and last whatever they typically last in the game.
Shapes can be send one by one, using either of the first three invocations, or batched as a list of shape descriptors.
Batching has this benefit that they will be send possibly as one packet, limiting network overhead of
sending many small packets to draw several shapes at once.
Shapes will fail to draw and raise a runtime error if not all its required parameters
are specified and all available shapes have some parameters that are required, so make sure to have them in place:
On the client, shapes can recognize that they are being redrawn again with the same parameters, disregarding the
duration parameter. This updates the expiry on the drawn shape to the new value, instead of adding new shape in its
place. This can be used for toggling the shapes on and off that has been send previously with very large durations,
or simply refresh the shapes periodically in more dynamic applications.
Optional shared shape attributes:
* `color` - integer value indicating the main color of the shape in the form of red, green, blue and alpha components
in the form of `0xRRGGBBAA`, with the default of `-1`, so white opaque, or `0xFFFFFFFF`.
* `player` - name or player entity to send the shape to. If specified, the shapes will appear only for the specified
player, otherwise it will be send to all players in the dimension.
* `line` - line thickness, defaults to 2.0pt
* `fill` - color for the faces, defaults to no fill. Use `color` attribute format
* `follow` - entity, or player name. Shape will follow an entity instead of being static.
Follow attribute requires all positional arguments to be relative to the entity and disallow
of using entity or block as position markers. You must specify positions as a triple.
* `snap` - if `follow` is present, indicated on which axis the snapping to entity coordinates occurs, and which axis
will be treated statically, i.e. the coordinate passed in a coord triple is the actual value in the world. Default
value is `'xyz'`, meaning the shape will be drawn relatively to the entity in all three directions. Using `xz` for
instance makes so that the shape follows the entity, but stays at the same, absolute Y coordinate.
Available shapes:
* `'line'` - draws a straight line between two points.
* Required attributes:
* `from` - triple coordinates, entity, or block value indicating one end of the line
* `to` - other end of the line, same format as `from`
* `'label'` - draws a text in the world. Default `line` attribute controls main font color.
`fill` controls the color of the background.
* Required attributes:
* `pos` - position
* `text` - string or formatted text to display
* Optional attributes
* `value` - string or formatted text to display instead of the main `text`. `value` unlike `text`
is not used to determine uniqueness of the drawn text so can be used to
display smoothly dynamic elements where value of an element is constantly
changing and updates to it are being sent from the server.
* `size` - float. Default font size is 10.
* `facing` - text direction, where its facing. Possible options are: `player` (default, text
always rotates to face the player), `north`, `south`, `east`, `west`, `up`, `down`
* `doublesided` - if `true` it will make the text visible from the back as well. Default is `false` (1.16+)
* `align` - text alignment with regards to `pos`. Default is `center` (displayed text is
centered with respect to `pos`), `left` (`pos` indicates beginning of text), and `right` (`pos`
indicates the end of text).
* `tilt` - additional rotation of the text on the canvas
* `indent`, `height`, `raise` - offsets for text rendering on X (`indent`), Y (`height`), and Z axis (`raise`)
with regards to the plane of the text. One unit of these corresponds to 1 line spacing, which
can be used to display multiple lines of text bound to the same `pos`
* `'box'` - draws a box with corners in specified points
* Required attributes:
* `from` - triple coordinates, entity, or block value indicating one corner of the box
* `to` - other corner, same format as `from`
* `'sphere'`:
* Required attributes:
* `center` - center of the sphere
* `radius` - radius of the sphere
* Optional attributes:
* `level` - level of details, or grid size. The more the denser your sphere. Default level of 0, means that the
level of detail will be selected automatically based on radius.
* `'cylinder'`:
* Required attributes:
* `center` - center of the base
* `radius` - radius of the base circle
* Optional attributes:
* `axis` - cylinder direction, one of `'x'`, `'y'`, `'z'` defaults to `'y'`
* `height` - height of the cyllinder, defaults to `0`, so flat disk. Can be negative.
* `level` - level of details, see `'sphere'`.
### `create_marker(text, pos, rotation?, block?, interactive?)`
Spawns a (permanent) marker entity with text or block at position. Returns that entity for further manipulations.
Unloading the app that spawned them will cause all the markers from the loaded portion of the world to be removed.
Also, if the game loads that marker in the future and the app is not loaded, it will be removed as well.
If `interactive` (`true` by default) is `false`, the armorstand will be a marker and would not be interactive in any
gamemode. But blocks can be placed inside markers and will not catch any interaction events.
Y Position of a marker text or block will be adjusted to make blocks or text appear at the specified position.
This makes so that actual armorstand position may be offset on Y axis. You would need to adjust your entity
locations if you plan to move the armorstand around after the fact. If both text and block are specified - one of them
will be aligned (armorstand type markers text shows up at their feet, while for regular armorstands - above the head,
while block on the head always render in the same position regardless if its a marker or not).
### `remove_all_markers()`
Removes all scarpet markers from the loaded portion of the world created by this app, in case you didn't want to do
the proper cleanup.
## System function
### `nbt(expr)`
Treats the argument as a nbt serializable string and returns its nbt value. In case nbt is not in a correct nbt
compound tag format, it will return `null` value.
Consult section about container operations in `Expression` to learn about possible operations on nbt values.
### `escape_nbt(expr)`
Excapes all the special characters in the string or nbt tag and returns a string that can be stored in nbt directly
as a string value.
### parse_nbt(tag)
Converts NBT tag to a scarpet value, which you can navigate through much better.
Converts:
- Compound tags into maps with string keys
- List tags into list values
- Numbers (Ints, Floats, Doubles, Longs) into a number
- Rest is converted to strings.
### encode_nbt(expr, force?)
Encodes value of the expression as an NBT tag. By default (or when `force` is false), it will only allow
to encode values that are guaranteed to return the same value when applied the resulting tag to `parse_nbt()`.
Supported types that can reliably convert back and forth to and from NBT values are:
- Maps with string keywords
- Lists of items of the same type (scarpet will take care of unifying value types if possible)
- Numbers (encoded as Ints -> Longs -> Doubles, as needed)
- Strings
Other value types will only be converted to tags (including NBT tags) if `force` is true. They would require
extra treatment when loading them back from NBT, but using `force` true will always produce output / never
produce an exception.
### `print(expr)`
Displays the result of the expression to the chat. Overrides default `scarpet` behaviour of sending everyting to stderr.
### `format(components, ...)`, `format(l(components, ...))`
Creates a line of formatted text. Each component is either a string indicating formatting and text it corresponds to
or a decorator affecting the component preceding it.
Regular formatting components is a string that have the structure of:
`'
print(format('rbu Error: ', 'r Stuff happened!'))
print(format('w Click ','tb [HERE]', '^di Awesome!', '!/kill', 'w \ button to win $1000'))
// the reason why I backslash the second space is that otherwise command parser may contract consecutive spaces
// not a problem in apps
### `logger(expr)`
Prints the message to system logs, and not to chat.
### read_file(resource, type)
### delete_file(resource, type)
### write_file(resource, type, data, ...)
With the specified `resource` in the scripts folder, of a specific `type`, writes/appends `data` to it, reads its
content, or deletes the resource.
Resource is identified by a path to the file.
A path can contain letters, numbers, characters `-`, `+`, or `_`, and a folder separator: `'/'`. Any other characters are stripped
from the name. Empty descriptors are invalid. Do not add file extensions to the descriptor - extensions are inferred
based on the `type` of the file.
Resources can be located in the app specific space, or a shared space for all the apps. Accessing of app-specific
resources is guaranteed to be isolated from other apps. Shared resources are... well, shared across all apes, meaning
they can eat of each others file, however all access to files is synchronized, and files are never left open, so
this should not lead to any access problems.
If the app's name is `'foo'`, the script location would
be `world/scripts/foo.sc`, app
specific data directory is under `world/scripts/foo.data/...`, and shared data space is under
`world/scripts/shared/...`.
The default no-name app, via `/script run` command can only save/load/read files from the shared space.
Functions return `null` if an error is encounter or no file is present (for read and delete operations). Returns `true`
for success writes and deletes, and requested data, based on the file type, for read operations.
NBT files can be written once as they an store one tag at a time. Consecutive writes will overwrite previous data.
Write operations to text files always result in appending to the existing file, so consecutive writes will increase
the size of the file and add data to it. Since files are closed after each write, sending multiple lines of data to
write is beneficial for writing speed. To send multiple packs of data, either provide them flat or as a list in the
third argument.
* `write_file('temp', 'text', 'foo', 'bar', 'baz')` or
* write_file('temp', 'text', l('foo', 'bar', 'baz'))
To log a single line of string
Supported values for resource `type` is:
* `nbt` - NBT tag
* `text` - text resource with automatic newlines added
* `raw` - text resource without implied newlines
* `shared_nbt`, `shared_text`, `shared_raw` - shared versions of the above
NBT files have extension `.nbt`, store one NBT tag, and return a NBT type value. Text files have `.txt` extension,
stores multiple lines of text and returns lists of all lines from the file. With `write_file`, multiple lines can be
sent to the file at once. The only difference between `raw` and `text` types are automatic newlines added after each
record to the file.
write_file('foo', 'shared_text, l('one', 'two'));
write_file('foo', 'shared_text', 'three\n', 'four\n');
write_file('foo', 'shared_raw', 'five\n', 'six\n');
read_file('foo', 'shared_text') => ['one', 'two', 'three', '', 'four', '', 'five', 'six']
### `run(expr)`
Runs a vanilla command from the string result of the `expr` and returns its success count
run('fill 1 1 1 10 10 10 air') -> 123 // 123 block were filled, this operation was successful 123 times out of a possible 1000 block volume
run('give @s stone 4') -> 1 // this operation was successful once
### `save()`
Performs autosave, saves all chunks, player data, etc. Useful for programs where autosave is disabled due to
performance reasons and saves the world only on demand.
### `load_app_data()`
NOTE: usages with arguments, so `load_app_data(file)` and `load_app_data(file, shared?)` are deprecated.
Use `read_file` instead.
Loads the app data associated with the app from the world /scripts folder. Without argument returns the memory
managed and buffered / throttled NBT tag. With a file name, reads explicitly a file with that name from the
scripts folder that belongs exclusively to the app. if `shared` is true, the file location is not exclusive
to the app anymore, but located in a shared app space.
File descriptor can contain letters, numbers and folder separator: `'/'`. Any other characters are stripped
from the name before saving/loading. Empty descriptors are invalid. Do not add file extensions to the descriptor
Function returns nbt value with the file content, or `null` if the file is missing or there were problems
with retrieving the data.
The default no-name app, via `/script run` command can only save/load file from the shared data location.
If the app's name is `'foo'`, the script location would
be `world/scripts/foo.sc`, system-managed default app data storage is in `world/scripts/foo.data.nbt`, app
specific data directory is under `world/scripts/foo.data/bar/../baz.nbt`, and shared data space is under
`world/scripts/shared/bar/../baz.nbt`.
You can use app data to save non-vanilla information separately from the world and other scripts.
### `store_app_data(tag)`
Note: `store_app_data(tag, file)` and `store_app_data(tag, file, shared?)` usages deprecated. Use `write_file` instead.
Stores the app data associated with the app from the world `/scripts` folder. With the `file` parameter saves
immediately and with every call to a specific file defined by the `file`, either in app space, or in the scripts
shared space if `shared` is true. Without `file` parameter, it may take up to 10
seconds for the output file
to sync preventing flickering in case this tag changes frequently. It will be synced when server closes.
Returns `true` if the file was saved successfully, `false` otherwise.
Uses the same file structure for exclusive app data, and shared data folder as `load_app_data`.
### `tick_time()`
Returns server tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time.
### `world_time()`
Returns dimension-specific tick counter.
### `day_time(new_time?)`
Returns current daytime clock value. If `new_time` is specified, sets a new clock
to that value. Daytime clocks are shared between all dimensions.
### `last_tick_times()`
Returns a 100-long array of recent tick times, in milliseconds. First item on the list is the most recent tick
If called outside of the main tick (either throgh scheduled tasks, or async execution), then the first item on the
list may refer to the previous tick performance. In this case the last entry (tick 100) would refer to the most current
tick. For all intent and purpose, `last_tick_times():0` should be used as last tick execution time, but
individual tick times may vary greatly, and these need to be taken with the little grain of
averaging.
### `game_tick(mstime?)`
Causes game to run for one tick. By default runs it and returns control to the program, but can optionally
accept expected tick length, in milliseconds. You can't use it to permanently change the game speed, but setting
longer commands with custom tick speeds can be interrupted via `/script stop` command
loop(1000,game_tick()) // runs the game as fast as it can for 1000 ticks loop(1000,game_tick(100)) // runs the game twice as slow for 1000 ticks### `seed()` Returns current world seed. ### `current_dimension()` Returns current dimension that the script runs in. ### `in_dimension(smth, expr)` Evaluates the expression `expr` with different dimension execution context. `smth` can be an entity, world-localized block, so not `block('stone')`, or a string representing a dimension like: `'nether'`, `'the_nether'`, `'end'` or `'overworld'`, etc. ### `view_distance()` Returns the view distance of the server. ### `mob_counts()`, `mob_counts(category)` 1.16+ Returns either a map of mob categories with its respective counts and capacities (a.k.a. mobcaps) or just a tuple of count and limit for a specific category. If a category was not spawning for whatever reason it may not be returned from `mob_counts()`, but could be retrieved for `mob_counts(category)`. Returned counts is what spawning algorithm has taken in to account last time mobs spawned. ### `schedule(delay, function, args...)` Schedules a user defined function to run with a specified `delay` ticks of delay. Scheduled functions run at the end of the tick, and they will run in order they were scheduled. ### `statistic(player, category, entry)` Queries in-game statistics for certain values. Categories include: * `mined`: blocks mined * `crafted`: items crafted * `used`: items used * `broken`: items broken * `picked_up`: items picked up * `dropped`: items dropped * `killed`: mobs killed * `killed_by`: mobs killed by * `custom`: various random stats For the options of `entry`, consult your statistics page, or give it a guess. The call will return `null` if the statistics options are incorrect, or player doesn't have them in their history. If the player encountered the statistic, or game created for him empty one, it will return a number. Scarpet will not affect the entries of the statistics, even if it is just creating empty ones. With `null` response it could either mean your input is wrong, or statistic effectively has a value of `0`. # `/script run` command Primary way to input commands. The command executes in the context, position, and dimension of the executing player, commandblock, etc... The command receives 4 variables, `x`, `y`, `z` and `p` indicating position and the executing entity of the command. You will receive tab completion suggestions as you type your code suggesting functions and global variables. It is advisable to use `/execute in ... at ... as ... run script run ...` or similar, to simulate running commands in a different scope. # `/script load / unload
foo = 1; //This is a comment bar = 2; // This never worked, so I commented it out // baz = foo()### `/script load/unload
/script run example_function(const, phrase, price) -> print(const+' '+phrase+' '+price) /script invoke example_function pi costs 5### `/script invokepoint
/script outline 0 100 0 -40 60 -40 40 140 40 "x*x+y*y+z*z < 32*32" white_stained_glass replace air /script outline 0 100 0 -40 60 -40 40 140 40 "x*x+y*y+z*z <= 32*32" white_stained_glass replace air /script outline 0 100 0 -40 60 -40 40 140 40 "x*x+y*y+z*z < 32.5*32.5" white_stained_glass replace air /script fill 0 100 0 -40 60 -40 40 140 40 "floor(sqrt(x*x+y*y+z*z)) == 32" white_stained_glass replace air /script fill 0 100 0 -40 60 -40 40 140 40 "round(sqrt(x*x+y*y+z*z)) == 32" white_stained_glass replace air /script fill 0 100 0 -40 60 -40 40 140 40 "ceil(sqrt(x*x+y*y+z*z)) == 32" white_stained_glass replace air /draw sphere 0 100 0 32 white_stained_glass replace air // fluffy ball round(sqrt(x*x+y*y+z*z)-rand(abs(y)))==32The last method is the one that world edit is using (part of carpet mod). It turns out that the outline method with `32.5` radius, fill method with `round` function and draw command are equivalent # `script stop/script resume` command `/script stop` allows to stop execution of any script currently running that calls the `game_tick()` function which allows the game loop to regain control of the game and process other commands. This will also make sure that all current and future programs will stop their execution. Execution of all programs will be prevented until `/script resume` command is called. Lets look at the following example. This is a program computes Fibonacci number in a recursive manner:
fib(n) -> if(n<3, 1, fib(n-1)+fib(n-2) ); fib(8)That's really bad way of doing it, because the higher number we need to compute the compute requirements will rise exponentially with `n`. It takes a little over 50 milliseconds to do fib(24), so above one tick, but about a minute to do fib(40). Calling fib(40) will not only freeze the game, but also you woudn't be able to interrupt its execution. We can modify the script as follows
fib(n) -> ( game_tick(50); if(n<3, 1, fib(n-1)+fib(n-2) ) ); fib(40)But this would never finish as such call would finish after `~ 2^40` ticks. To make our computations responsive, yet able to respond to user interactions, other commands, as well as interrupt execution, we could do the following:
fib(n) -> ( if(n==23, game_tick(50) ); if(n<3, 1, fib(n-1)+fib(n-2) ) ); fib(40)This would slow down the computation of fib(40) from a minute to two, but allows the game to keep continue running and be responsive to commands, using about half of each tick to advance the computation. Obviously depending on the problem, and available hardware, certain things can take more or less time to execute, so portioning of work with calling `gametick` should be balanced in each case separately