Merge branch 'master' into display-title-additions
This commit is contained in:
88 files changed
+3088
-1017
No files matched your search
+320
-99
@@ -296,8 +296,8 @@ For more information, see: [EvalEx GitHub repository](https://github.com/uklimas
|
|||||||
`scarpet` provides a number of constants that can be used literally in scripts
|
`scarpet` provides a number of constants that can be used literally in scripts
|
||||||
|
|
||||||
* `null`: nothing, zilch, not even false
|
* `null`: nothing, zilch, not even false
|
||||||
* `true`: pure true, or just 1 (one)
|
* `true`: pure true, can act as `1`
|
||||||
* `false`: false truth, or true falsth, 0 (zero) actually
|
* `false`: false truth, or true falsth, equals to `0`
|
||||||
* `pi`: for the fans of perimeters, its a perimeter of an apple pi of diameter 1\. About 3.14
|
* `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
|
* `euler`: clever guy. Derivative of its exponent is goto 1\. About 2.72
|
||||||
|
|
||||||
@@ -343,7 +343,7 @@ in [User Defined Functions and Program Control Flow](docs/scarpet/language/Funct
|
|||||||
1357-5 => 1352
|
1357-5 => 1352
|
||||||
1357-'5' => 137
|
1357-'5' => 137
|
||||||
3*'foo'-'o' => 'fff'
|
3*'foo'-'o' => 'fff'
|
||||||
l(1,3,5)+7 => l(8,10,12)
|
[1,3,5]+7 => [8,10,12]
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
As you can see, values can behave differently when mixed with other types in the same expression.
|
As you can see, values can behave differently when mixed with other types in the same expression.
|
||||||
@@ -403,8 +403,8 @@ In Minecraft API portion `entity ~ feature` is a shortcode for `query(entity,fea
|
|||||||
any extra arguments.
|
any extra arguments.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
l(1,2,3) ~ 2 => 1
|
[1,2,3] ~ 2 => 1
|
||||||
l(1,2,3) ~ 4 => null
|
[1,2,3] ~ 4 => null
|
||||||
|
|
||||||
'foobar' ~ 'baz' => null
|
'foobar' ~ 'baz' => null
|
||||||
'foobar' ~ '.b' => 'ob'
|
'foobar' ~ '.b' => 'ob'
|
||||||
@@ -430,10 +430,10 @@ Or an example to find if a player has specific enchantment on a held axe (either
|
|||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
global_get_enchantment(p, ench) -> (
|
global_get_enchantment(p, ench) -> (
|
||||||
$ for(l('main','offhand'),
|
$ for(['main','offhand'],
|
||||||
$ holds = query(p, 'holds', _);
|
$ holds = query(p, 'holds', _);
|
||||||
$ if( holds,
|
$ if( holds,
|
||||||
$ l(what, count, nbt) = holds;
|
$ [what, count, nbt] = holds;
|
||||||
$ if( what ~ '_axe' && nbt ~ ench,
|
$ if( what ~ '_axe' && nbt ~ ench,
|
||||||
$ lvl = max(lvl, number(nbt ~ '(?<=lvl:)\\d') )
|
$ lvl = max(lvl, number(nbt ~ '(?<=lvl:)\\d') )
|
||||||
$ )
|
$ )
|
||||||
@@ -470,8 +470,8 @@ Examples:
|
|||||||
'foo'*3 => 'foofoofoo'
|
'foo'*3 => 'foofoofoo'
|
||||||
'foofoofoo' / 3 => 'foo'
|
'foofoofoo' / 3 => 'foo'
|
||||||
'foofoofoo'-'o' => 'fff'
|
'foofoofoo'-'o' => 'fff'
|
||||||
l(1,2,3)+1 => l(2,3,4)
|
[1,2,3]+1 => [2,3,4]
|
||||||
b = l(100,63,100); b+l(10,0,10) => l(110,63,110)
|
b = [100,63,100]; b+[10,0,10] => [110,63,110]
|
||||||
{'a' -> 1} + {'b' -> 2} => {'a' -> 1, 'b' -> 2}
|
{'a' -> 1} + {'b' -> 2} => {'a' -> 1, 'b' -> 2}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
@@ -513,11 +513,12 @@ second operand is not necessary, it won't be evaluated, which means one can use
|
|||||||
case of success returns first positive operand (`||`) or last one (`&&`).
|
case of success returns first positive operand (`||`) or last one (`&&`).
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
true || false => 1
|
true || false => true
|
||||||
null || false => 0
|
null || false => false
|
||||||
null != false || run('kill gnembon') => 1 // gnembon survives
|
false || null => null
|
||||||
null != false && run('kill gnembon') => 0 // when cheats not allowed
|
null != false || run('kill gnembon') // gnembon survives
|
||||||
null != false && run('kill gnembon') => 1 // gnembon dies, cheats allowed
|
null != false && run('kill gnembon') // when cheats not allowed
|
||||||
|
null != false && run('kill gnembon') // gnembon dies, cheats allowed
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `Assignment Operators = <> +=`
|
### `Assignment Operators = <> +=`
|
||||||
@@ -531,11 +532,11 @@ require rewriting of the array anyways.
|
|||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
a = 5 => a == 5
|
a = 5 => a == 5
|
||||||
l(a,b,c) = l(3,4,5) => a==3, b==4, c==5
|
[a,b,c] = [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)
|
[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,b,c,d,e,f] = [range(6)]; [a,b,c] <> [d,e,f]; [a,b,c,d,e,f] => [3,4,5,0,1,2]
|
||||||
a = l(1,2,3); a += 4 => [1,2,3,4]
|
a = [1,2,3]; a += 4 => [1,2,3,4]
|
||||||
a = l(1,2,3,4); a = filter(a,_!=2) => [1,3,4]
|
a = [1,2,3,4]; a = filter(a,_!=2) => [1,3,4]
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `Unary Operators - +`
|
### `Unary Operators - +`
|
||||||
@@ -553,12 +554,12 @@ Require a number, flips the sign. One way to assert it's a number is by crashing
|
|||||||
flips boolean condition of the expression. Equivalent of `bool(expr)==false`
|
flips boolean condition of the expression. Equivalent of `bool(expr)==false`
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
!true => 0
|
!true => false
|
||||||
!false => 1
|
!false => true
|
||||||
!null => 1
|
!null => true
|
||||||
!5 => 0
|
!5 => false
|
||||||
!l() => 1
|
!l() => true
|
||||||
!l(null) => 0
|
!l(null) => false
|
||||||
</pre>
|
</pre>
|
||||||
# Arithmetic operations
|
# Arithmetic operations
|
||||||
|
|
||||||
@@ -696,30 +697,31 @@ as well as minecraft related concepts like `block`, `entity`, `nbt`, `text`.
|
|||||||
|
|
||||||
### `bool(expr)`
|
### `bool(expr)`
|
||||||
|
|
||||||
Returns a boolean context of the expression. Note that there are no true/false values in scarpet. `true` is
|
Returns a boolean context of the expression.
|
||||||
alias of 1, and `false` is 0\. Bool is also interpreting string values as boolean, which is different from other
|
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
|
places where boolean context can be used. This can be used in places where API functions return string values to
|
||||||
represent binary values
|
represent binary values.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
bool(pi) => 1
|
bool(pi) => true
|
||||||
bool(false) => 0
|
bool(false) => false
|
||||||
bool('') => 0
|
bool('') => false
|
||||||
bool(l()) => 0
|
bool(l()) => false
|
||||||
bool(l('')) => 1
|
bool(l('')) => true
|
||||||
bool('foo') => 1
|
bool('foo') => true
|
||||||
bool('false') => 0
|
bool('false') => false
|
||||||
bool('nulL') => 0
|
bool('nulL') => false
|
||||||
if('false',1,0) => 1
|
if('false',1,0) => true
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `number(expr)`
|
### `number(expr)`
|
||||||
|
|
||||||
Returns a numeric context of the expression. Can be used to read numbers from strings
|
Returns a numeric context of the expression. Can be used to read numbers from strings, or other types
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
number(null) => null
|
number(null) => 0
|
||||||
number(false) => 0
|
number(false) => 0
|
||||||
|
number(true) => 1
|
||||||
number('') => null
|
number('') => null
|
||||||
number('3.14') => 3.14
|
number('3.14') => 3.14
|
||||||
number(l()) => 0
|
number(l()) => 0
|
||||||
@@ -747,15 +749,15 @@ Supported types (with `"%<?>"` syntax):
|
|||||||
* `%%`: '%' character
|
* `%%`: '%' character
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
str(null) => null
|
str(null) => 'null'
|
||||||
str(false) => 0
|
str(false) => 'false'
|
||||||
str('') => null
|
str('') => ''
|
||||||
str('3.14') => 3.14
|
str('3.14') => '3.14'
|
||||||
str(l()) => 0
|
str([]) => '[]'
|
||||||
str(l('')) => 1
|
str(['']) => '[]'
|
||||||
str('foo') => null
|
str('foo') => 'foo'
|
||||||
str('3bar') => null
|
str('3bar') => '3bar'
|
||||||
str(2)+str(2) => 22
|
str(2)+str(2) => '22'
|
||||||
str('pi: %.2f',pi) => 'pi: 3.14'
|
str('pi: %.2f',pi) => 'pi: 3.14'
|
||||||
str('player at: %d %d %d',pos(player())) => 'player at: 567, -2423, 124'
|
str('player at: %d %d %d',pos(player())) => 'player at: 567, -2423, 124'
|
||||||
</pre>
|
</pre>
|
||||||
@@ -908,18 +910,24 @@ length('foo') => 3
|
|||||||
### `rand(expr), rand(expr, seed)`
|
### `rand(expr), rand(expr, seed)`
|
||||||
|
|
||||||
returns a random number from `0.0` (inclusive) to `expr` (exclusive). In boolean context (in conditions,
|
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
|
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
|
that `bool(rand(2))` returns true half of the time and `!rand(5)` returns true for 20% (1/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
|
provided, uses a random seed that's shared across all scarpet apps.
|
||||||
same random object. Scarpet keeps track of up to 1024 custom random number generators, so if you exceed this number
|
If seed is provided, each consecutive call to rand() will act like 'next' call to the
|
||||||
(per app), then your random sequence will revert to the beginning.
|
same random object. Scarpet keeps track of up to 65536 custom random number generators (custom seeds, per app),
|
||||||
|
so if you exceed this number, your random sequences will revert to the beginning and start over.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
map(range(10), floor(rand(10))) => [5, 8, 0, 6, 9, 3, 9, 9, 1, 8]
|
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), bool(rand(2))) => [false, false, true, false, false, false, true, false, true, false]
|
||||||
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]
|
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]
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## `reset_seed(seed)`
|
||||||
|
|
||||||
|
Resets the sequence of the randomizer used by `rand` for this seed to its initial state. Returns a boolean value
|
||||||
|
indicating if the given seed has been used or not.
|
||||||
|
|
||||||
### `perlin(x), perlin(x, y), perlin(x, y, z), perlin(x, y, z, seed)`
|
### `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
|
returns a noise value from `0.0` to `1.0` (roughly) for 1, 2 or 3 dimensional coordinate. The default seed it samples
|
||||||
@@ -1217,12 +1225,12 @@ to `all(list,expr) <=> for(list,expr)==length(list)`. `expr` also receives bound
|
|||||||
and `continue` have no sense and cannot be used inside of `expr` body.
|
and `continue` have no sense and cannot be used inside of `expr` body.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
all([1,2,3], check_prime(_)) => 1
|
all([1,2,3], check_prime(_)) => true
|
||||||
all(neighbours(x,y,z), _=='stone') => 1 // if all neighbours of [x, y, z] are stone
|
all(neighbours(x,y,z), _=='stone') => true // 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(_) )
|
map(filter(rect(0,4,0,1000,0,1000), [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]]
|
=> [[-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
|
// 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(_);
|
map( filter( rect(0,4,0,1000,1,1000,1000,0,1000), [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),
|
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]]
|
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
|
// ditto, but requiring at most 7 bedrock block in the 18 blocks below them
|
||||||
@@ -1475,15 +1483,73 @@ returns everywhere, but it would often lead to a messy code.
|
|||||||
|
|
||||||
It terminates entire program passing `expr` as the result of the program execution, or null if omitted.
|
It terminates entire program passing `expr` as the result of the program execution, or null if omitted.
|
||||||
|
|
||||||
### `try(expr, catch_expr(_)?) ... throw(value?)`
|
### `try(expr)` `try(expr, user_catch_expr)` `try(expr, type, catch_expr, type?, catch_expr?, ...)`
|
||||||
|
|
||||||
`try` function evaluates expression, and continues further unless `throw` function is called anywhere
|
`try` evaluates expression, allowing capturing exceptions that would be thrown inside `expr` statement. The exceptions can be
|
||||||
inside `expr`. In that case the `catch_expr` is evaluates with `_` set to the argument `throw` was called with.
|
thrown explicitly using `throw()` or internally by scarpet where code is correct but detects illegal state. The 2-argument form
|
||||||
This mechanic accepts skipping thrown value - it throws null instead, and catch expression - then try returns
|
catches only user-thrown exceptions and one argument call `try(expr)` is equivalent to `try(expr, null)`,
|
||||||
null as well This mechanism allows to terminate large portion of a convoluted call stack and continue program
|
or `try(expr, 'user_exception', null)`. If multiple `type-catch` pairs are defined, the execution terminates on the first
|
||||||
execution. There is only one level of exceptions currently in carpet, so if the inner function also defines
|
applicable type for the exception thrown. Therefore, even if the caught exception matches multiple filters, only
|
||||||
the `try` catchment area, it will received the exception first, but it can technically rethrow the value its
|
the first matching block will be executed.
|
||||||
getting for the outer scope. Unhandled throw acts like an exit statement.
|
|
||||||
|
Catch expressions are evaluated with
|
||||||
|
`_` set to the value associated with the exception and `_trace` set to contain details about point of error (token, and line and
|
||||||
|
column positions), call stack and local
|
||||||
|
variables at the time of failure. The `type` will catch any exception of that type and any subtype of this type.
|
||||||
|
|
||||||
|
|
||||||
|
You can use `try` mechanism to exit from large portion of a convoluted call stack and continue program execution, although catching
|
||||||
|
exceptions is typically much more expensive comparing to not throwing them.
|
||||||
|
|
||||||
|
The `try` function allows you to catch some scarpet exceptions for cases covering invalid data, like invalid
|
||||||
|
blocks, biomes, dimensions and other things, that may have been modified by datapacks, resourcepacks or other mods,
|
||||||
|
or when an error is outside of the programmers scope, such as problems when reading or decoding files.
|
||||||
|
|
||||||
|
This is the hierarchy of the exceptions that could be thrown/caught in the with the `try` function:
|
||||||
|
- `exception`: This is the base exception. Catching `'exception'` allows to catch everything that can be caught,
|
||||||
|
but like everywhere else, doing that sounds like a bad idea.
|
||||||
|
- `value_exception`: This is the parent for any exception that occurs due to an
|
||||||
|
incorrect argument value provided to a built-in function
|
||||||
|
- `unknown_item`, `unknown_block`, `unknown_biome`, `unknown_sound`, `unknown_particle`,
|
||||||
|
`unknown_poi_type`, `unknown_dimension`, `unknown_structure`, `unknown_criterion`: Specific
|
||||||
|
errors thrown when a specified internal name does not exist or is invalid.
|
||||||
|
- `io_exception`: This is the parent for any exception that occurs due to an error handling external data.
|
||||||
|
- `nbt_error`: Incorrect input/output NBT file.
|
||||||
|
- `json_error`: Incorrect input/output JSON data.
|
||||||
|
- `user_exception`: Exception thrown by default with `throw` function.
|
||||||
|
|
||||||
|
Synopsis:
|
||||||
|
<pre>
|
||||||
|
inner_call() ->
|
||||||
|
(
|
||||||
|
aaa = 'booyah';
|
||||||
|
try(
|
||||||
|
for (range(10), item_tags('stick'+_*'k'));
|
||||||
|
,
|
||||||
|
print(_trace) // not caught, only catching user_exceptions
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
outer_call() ->
|
||||||
|
(
|
||||||
|
try(
|
||||||
|
inner_call()
|
||||||
|
, 'exception', // catching everything
|
||||||
|
print(_trace)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
</pre>
|
||||||
|
Producing:
|
||||||
|
```
|
||||||
|
{stack: [[<app>, inner_call, 1, 14]], locals: {_a: 0, aaa: booyah, _: 1, _y: 0, _i: 1, _x: 0, _z: 0}, token: [item_tags, 5, 23]}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `throw(value?)`, `throw(type, value)`, `throw(subtype, type, value)`
|
||||||
|
|
||||||
|
Throws an exception that can be caught in a `try` block (see above). If ran without arguments, it will throw a `user_exception`
|
||||||
|
passing `null` as the value to the `catch_expr`. With two arguments you can mimic any other exception type thrown in scarpet.
|
||||||
|
With 3 arguments, you can specify a custom exception acting as a `subtype` of a provided `type`, allowing to customize `try`
|
||||||
|
statements with custom exceptions.
|
||||||
|
|
||||||
### `if(cond, expr, cond?, expr?, ..., default?)`
|
### `if(cond, expr, cond?, expr?, ..., default?)`
|
||||||
|
|
||||||
@@ -1518,12 +1584,12 @@ In case to simplify the access with nested objects, you can add chain of address
|
|||||||
than calling it multiple times. `get(get(foo,a),b)` is equivalent to `get(foo, a, b)`, or `foo:a:b`.
|
than calling it multiple times. `get(get(foo,a),b)` is equivalent to `get(foo, a, b)`, or `foo:a:b`.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
get(l(range(10)), 5) => 5
|
get([range(10)], 5) => 5
|
||||||
get(l(range(10)), -1) => 9
|
get([range(10)], -1) => 9
|
||||||
get(l(range(10)), 10) => 0
|
get([range(10)], 10) => 0
|
||||||
l(range(10)):93 => 3
|
[range(10)]:93 => 3
|
||||||
get(player() ~ 'nbt', 'Health') => 20 // inefficient way to get player health, use player() ~ 'health' instead
|
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
|
get({ 'foo' -> 2, 'bar' -> 3, 'baz' -> 4 }, 'bar') => 3
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `has(container, address, ...), has(lvalue)`
|
### `has(container, address, ...), has(lvalue)`
|
||||||
@@ -1595,13 +1661,13 @@ of the existing elements. Use `replace` to remove and replace existing element.
|
|||||||
and merges keys from `value` with the compound tag under the path
|
and merges keys from `value` with the compound tag under the path
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
a = l(1, 2, 3); put(a, 1, 4); a => [1, 4, 3]
|
a = [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 = [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 = [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 = [1, 2, 3]; put(a, null, [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 = [1, 2, 3]; put(a, 1, [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 = [[0,0,0],[0,0,0],[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}
|
a = {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('{}'); 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: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
|
tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl', 1, 2); tag
|
||||||
@@ -1682,10 +1748,10 @@ other parts. In that case consecutive calls to `slice` will refer to index `0` t
|
|||||||
cannot go back nor track where they are in the sequence (see examples).
|
cannot go back nor track where they are in the sequence (see examples).
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
slice([0,1,2,3,4,5], 1, 3) => [1, 2, 3]
|
slice([0,1,2,3,4,5], 1, 3) => [1, 2]
|
||||||
slice('foobar', 0, 1) => 'f'
|
slice('foobar', 0, 1) => 'f'
|
||||||
slice('foobar', 3) => 'bar'
|
slice('foobar', 3) => 'bar'
|
||||||
slice(range(10), 3, 5) => [3, 4, 5]
|
slice(range(10), 3, 5) => [3, 4]
|
||||||
slice(range(10), 5) => [5, 6, 7, 8, 9]
|
slice(range(10), 5) => [5, 6, 7, 8, 9]
|
||||||
r = range(100); [slice(r, 5, 7), slice(r, 1, 3)] => [[5, 6], [8, 9]]
|
r = range(100); [slice(r, 5, 7), slice(r, 1, 3)] => [[5, 6], [8, 9]]
|
||||||
</pre>
|
</pre>
|
||||||
@@ -1720,7 +1786,7 @@ Primarily to be used in higher order functions
|
|||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
range(10) => [...]
|
range(10) => [...]
|
||||||
l(range(10)) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
[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]
|
map(range(10),_*_) => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
|
||||||
reduce(range(10),_a+_, 0) => 45
|
reduce(range(10),_a+_, 0) => 45
|
||||||
range(5,10) => [5, 6, 7, 8, 9]
|
range(5,10) => [5, 6, 7, 8, 9]
|
||||||
@@ -2077,6 +2143,8 @@ when it was used. Block values passed in various places like `scan` functions, e
|
|||||||
its properties are needed. This means that if the block at the location changes before its queried in the program this
|
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:
|
might result in getting the later state, which might not be desired. Consider the following example:
|
||||||
|
|
||||||
|
Throws `unknown_block` if provided input is not valid.
|
||||||
|
|
||||||
<pre>set(10,10,10,'stone');
|
<pre>set(10,10,10,'stone');
|
||||||
scan(10,10,10,0,0,0, b = _);
|
scan(10,10,10,0,0,0, b = _);
|
||||||
set(10,10,10,'air');
|
set(10,10,10,'air');
|
||||||
@@ -2106,6 +2174,8 @@ properties that the original source block may have contained.
|
|||||||
|
|
||||||
The returned value is either the block state that has been set, or `false` if block setting was skipped, or failed
|
The returned value is either the block state that has been set, or `false` if block setting was skipped, or failed
|
||||||
|
|
||||||
|
Throws `unknown_block` if provided block to set is not valid
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
set(0,5,0,'bedrock') => bedrock
|
set(0,5,0,'bedrock') => bedrock
|
||||||
set(l(0,5,0), 'bedrock') => bedrock
|
set(l(0,5,0), 'bedrock') => bedrock
|
||||||
@@ -2184,6 +2254,8 @@ but doesn't open chests / containers, so have no effect on interactive blocks, l
|
|||||||
Returns true if placement/use was
|
Returns true if placement/use was
|
||||||
successful, false otherwise.
|
successful, false otherwise.
|
||||||
|
|
||||||
|
Throws `unknown_item` if `item` doesn't exist
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
place_item('stone',x,y,z) // places a stone block on x,y,z block
|
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('piston,x,y,z,'down') // places a piston facing down
|
||||||
@@ -2202,6 +2274,8 @@ If type is `null`, POI at position is removed. In any case, previous POI is also
|
|||||||
Interestingly, `unemployed`, and `nitwit` are not used in the game, meaning, they could be used as permanent spatial
|
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.
|
markers for scarpet apps. `meeting` is the only one with increased max occupancy of 32.
|
||||||
|
|
||||||
|
Throws `unknown_poi` if the provided point of interest doesn't exist
|
||||||
|
|
||||||
### `set_biome(pos, biome_name, update=true)`
|
### `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
|
Changes the biome at that block position. if update is specified and false, then chunk will not be refreshed
|
||||||
@@ -2212,6 +2286,8 @@ is only effective if you set it at y=0, and affects the entire column of. In the
|
|||||||
specific Y coordinate of the biome you want to change, and it affects roughly 4x4x4 area (give or take some random
|
specific Y coordinate of the biome you want to change, and it affects roughly 4x4x4 area (give or take some random
|
||||||
noise).
|
noise).
|
||||||
|
|
||||||
|
Throws `unknown_biome` if the `biome_name` doesn't exist.
|
||||||
|
|
||||||
### `update(pos)`
|
### `update(pos)`
|
||||||
|
|
||||||
Causes a block update at position.
|
Causes a block update at position.
|
||||||
@@ -2236,6 +2312,8 @@ In item context, `true` means that breaking item has no nbt to use, `null` indic
|
|||||||
considered broken in process, and `nbt` type value, for a resulting NBT tag on a hypothetical tool. Its up to the
|
considered broken in process, and `nbt` type value, for a resulting NBT tag on a hypothetical tool. Its up to the
|
||||||
programmer to use that nbt to apply it where it belong
|
programmer to use that nbt to apply it where it belong
|
||||||
|
|
||||||
|
Throws `unknown_item` if `tool` doesn't exist.
|
||||||
|
|
||||||
Here is a sample code that can be used to mine blocks using items in player inventory, without using player context
|
Here is a sample code that can be used to mine blocks using items in player inventory, without using player context
|
||||||
for mining. Obviously, in this case the use of `harvest` would be much more applicable:
|
for mining. Obviously, in this case the use of `harvest` would be much more applicable:
|
||||||
|
|
||||||
@@ -2327,6 +2405,8 @@ back in state definition in various applications where block properties are requ
|
|||||||
|
|
||||||
`block_state` can also accept block names as input, returning block's default state.
|
`block_state` can also accept block names as input, returning block's default state.
|
||||||
|
|
||||||
|
Throws `unknown_block` if the provided input is not valid.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
set(x,y,z,'iron_trapdoor','half','top'); block_state(x,y,z) => {waterlogged: false, half: top, open: false, ...}
|
set(x,y,z,'iron_trapdoor','half','top'); block_state(x,y,z) => {waterlogged: false, half: top, open: false, ...}
|
||||||
set(x,y,z,'iron_trapdoor','half','top'); block_state(x,y,z,'half') => top
|
set(x,y,z,'iron_trapdoor','half','top'); block_state(x,y,z,'half') => top
|
||||||
@@ -2347,6 +2427,8 @@ Without arguments, returns list of available tags, with block supplied (either b
|
|||||||
of tags the block belongs to, and if a tag is specified, returns `null` if tag is invalid, `false` if this block doesn't belong
|
of tags the block belongs to, and if a tag is specified, returns `null` if tag is invalid, `false` if this block doesn't belong
|
||||||
to this tag, and `true` if the block belongs to the tag.
|
to this tag, and `true` if the block belongs to the tag.
|
||||||
|
|
||||||
|
Throws `unknown_block` if `block` doesn't exist
|
||||||
|
|
||||||
### `block_data(pos)`
|
### `block_data(pos)`
|
||||||
|
|
||||||
Return NBT string associated with specific location, or null if the block does not carry block data. Can be currently
|
Return NBT string associated with specific location, or null if the block does not carry block data. Can be currently
|
||||||
@@ -2355,16 +2437,18 @@ used to match specific information from it, or use it to copy to another block
|
|||||||
<pre> block_data(x,y,z) => '{TransferCooldown:0,x:450,y:68, ... }'
|
<pre> block_data(x,y,z) => '{TransferCooldown:0,x:450,y:68, ... }'
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `poi(pos), poi(pos, radius?, type?, status?, column?)`
|
### `poi(pos), poi(pos, radius?, type?, status?, column_search?)`
|
||||||
|
|
||||||
Queries a POI (Point of Interest) at a given position, returning `null` if none is found, or tuple of poi type and its
|
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 `type`, `radius` and `status`, returns a list of POIs around `pos` within a
|
occupancy load. With optional `type`, `radius` and `status`, returns a list of POIs around `pos` within a
|
||||||
given `radius`. If the `type` is specified, returns only poi types of that types, or everything if omitted or `'any'`.
|
given `radius`. If the `type` is specified, returns only poi types of that types, or everything if omitted or `'any'`.
|
||||||
If `status` is specified (either `'any'`, `'available'`, or `'occupied'`) returns only POIs with that status.
|
If `status` is specified (either `'any'`, `'available'`, or `'occupied'`) returns only POIs with that status.
|
||||||
With `column` set to `true`, it will return all POIs in a cuboid with `radius` blocks away on x and z, in the entire
|
With `column_search` set to `true`, it will return all POIs in a cuboid with `radius` blocks away on x and z, in the entire
|
||||||
block column from 0 to 255. Default (`false`) returns POIs within a spherical area centered on `pos` and with `radius`
|
block column from 0 to 255. Default (`false`) returns POIs within a spherical area centered on `pos` and with `radius`
|
||||||
radius.
|
radius.
|
||||||
|
|
||||||
|
All results of `poi` calls are returned in sorted order with respect to the euclidean distance to the requested center of `pos`.
|
||||||
|
|
||||||
The return format of the results is a list of poi type, occupancy load, and extra triple of coordinates.
|
The return format of the results is a list of poi type, occupancy load, and extra triple of coordinates.
|
||||||
|
|
||||||
Querying for POIs using the radius is the intended use of POI mechanics, and the ability of accessing individual POIs
|
Querying for POIs using the radius is the intended use of POI mechanics, and the ability of accessing individual POIs
|
||||||
@@ -2381,9 +2465,9 @@ poi(x,y,z,5) => [['nether_portal',0,[7,8,9]],['nether_portal',0,[7,9,9]]] // two
|
|||||||
|
|
||||||
Without arguments, returns the list of biomes in the world.
|
Without arguments, returns the list of biomes in the world.
|
||||||
|
|
||||||
With block, or name, returns the name of the biome in that position, or null, if provided biome is not valid.
|
With block, or name, returns the name of the biome in that position, or throws `'unknown_biome'` if provided biome or block are not valid.
|
||||||
|
|
||||||
With an optional feature, it returns value for the specified attribute for that biome. Available and querable features include:
|
With an optional feature, it returns value for the specified attribute for that biome. Available and queryable features include:
|
||||||
* `'top_material'`: unlocalized block representing the top surface material
|
* `'top_material'`: unlocalized block representing the top surface material
|
||||||
* `'under_material'`: unlocalized block representing what sits below topsoil
|
* `'under_material'`: unlocalized block representing what sits below topsoil
|
||||||
* `'category'`: the parent biome this biome is derived from. Possible values include:
|
* `'category'`: the parent biome this biome is derived from. Possible values include:
|
||||||
@@ -2772,6 +2856,7 @@ If structure is not specified, it will return a set of structure names that are
|
|||||||
as keys, and same type of map values as with a single structure call. An empty set or an empty map would indicate that nothing
|
as keys, and same type of map values as with a single structure call. An empty set or an empty map would indicate that nothing
|
||||||
should be generated there.
|
should be generated there.
|
||||||
|
|
||||||
|
Throws `unknown_structure` if structure doesn't exist.
|
||||||
|
|
||||||
### `structures(pos), structures(pos, structure_name)`
|
### `structures(pos), structures(pos, structure_name)`
|
||||||
|
|
||||||
@@ -2805,6 +2890,8 @@ structure starts.
|
|||||||
Requires a `Structure Variant` or `Standard Structure` name (see above). If standard name is used, the variant of the
|
Requires a `Structure Variant` or `Standard Structure` name (see above). If standard name is used, the variant of the
|
||||||
structure may depend on the biome, otherwise the default structure for this type will be generated.
|
structure may depend on the biome, otherwise the default structure for this type will be generated.
|
||||||
|
|
||||||
|
Throws `unknown_structure` if structure doesn't exist.
|
||||||
|
|
||||||
### `plop(pos, what)`
|
### `plop(pos, what)`
|
||||||
|
|
||||||
Plops a structure or a feature at a given `pos`, so block, triple position coordinates or a list of coordinates.
|
Plops a structure or a feature at a given `pos`, so block, triple position coordinates or a list of coordinates.
|
||||||
@@ -2827,7 +2914,9 @@ All generated structures will retain their properties, like mob spawning, howeve
|
|||||||
itself has certain rules to spawn mobs, like plopping a nether fortress in the overworld will not spawn nether mobs,
|
itself has certain rules to spawn mobs, like plopping a nether fortress in the overworld will not spawn nether mobs,
|
||||||
because nether mobs can spawn only in the nether, but plopped in the nether - will behave like a valid nether fortress.
|
because nether mobs can spawn only in the nether, but plopped in the nether - will behave like a valid nether fortress.
|
||||||
|
|
||||||
### `custom_dimension(name, seed?)`
|
### (deprecated) `custom_dimension(name, seed?)`
|
||||||
|
|
||||||
|
Deprecated by `create_datapack()` which can be used to setup custom dimensions
|
||||||
|
|
||||||
Ensures the dimension with the given `'name'` is available and configured with the given seed. It merely sets the world
|
Ensures the dimension with the given `'name'` is available and configured with the given seed. It merely sets the world
|
||||||
generator settings to the overworld, and the optional custom seed (or using current world seed, if not provided).
|
generator settings to the overworld, and the optional custom seed (or using current world seed, if not provided).
|
||||||
@@ -3082,7 +3171,7 @@ Returns `true` if en entity is standing on firm ground and falling down due to t
|
|||||||
|
|
||||||
### `query(e, 'name'), query(e, 'display_name'), query(e, 'custom_name'), query(e, 'type')`
|
### `query(e, 'name'), query(e, 'display_name'), query(e, 'custom_name'), query(e, 'type')`
|
||||||
|
|
||||||
String of entity name
|
String of entity name or formatted text in the case of `display_name`
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
query(e,'name') => Leatherworker
|
query(e,'name') => Leatherworker
|
||||||
@@ -3627,6 +3716,10 @@ Requires a living entity as an argument.
|
|||||||
|
|
||||||
Will make the entity jump once.
|
Will make the entity jump once.
|
||||||
|
|
||||||
|
### `modify(e, 'swing')` `modify(e, 'swing', 'offhand')`
|
||||||
|
|
||||||
|
Makes the living entity swing their required limb.
|
||||||
|
|
||||||
### `modify(e, 'silent', boolean)`
|
### `modify(e, 'silent', boolean)`
|
||||||
|
|
||||||
Silences or unsilences the entity.
|
Silences or unsilences the entity.
|
||||||
@@ -3808,14 +3901,16 @@ an inventory, all API functions 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.
|
Most items returned are in the form of a triple of item name, count, and nbt or the extra data associated with an item.
|
||||||
|
|
||||||
### item_list(tag?)
|
### `item_list(tag?)`
|
||||||
|
|
||||||
With no arguments, returns a list of all items in the game. With an item tag provided, list items matching the tag, or `null` if tag is not valid.
|
With no arguments, returns a list of all items in the game. With an item tag provided, list items matching the tag, or `null` if tag is not valid.
|
||||||
|
|
||||||
### item_tags(item, tag?)
|
### `item_tags(item, tag?)`
|
||||||
|
|
||||||
Returns list of tags the item belongs to, or, if tag is provided, `true` if an item maches the tag, `false` if it doesn't and `null` if that's not a valid tag
|
Returns list of tags the item belongs to, or, if tag is provided, `true` if an item maches the tag, `false` if it doesn't and `null` if that's not a valid tag
|
||||||
|
|
||||||
|
Throws `unknown_item` if item doesn't exist.
|
||||||
|
|
||||||
### `stack_limit(item)`
|
### `stack_limit(item)`
|
||||||
|
|
||||||
Returns number indicating what is the stack limit for the item. Its typically 1 (non-stackable), 16 (like buckets),
|
Returns number indicating what is the stack limit for the item. Its typically 1 (non-stackable), 16 (like buckets),
|
||||||
@@ -3823,6 +3918,8 @@ or 64 - rest. It is recommended to consult this, as other inventory API function
|
|||||||
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
|
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.
|
negative is the same as empty.
|
||||||
|
|
||||||
|
Throws `unknown_item` if item doesn't exist.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
stack_limit('wooden_axe') => 1
|
stack_limit('wooden_axe') => 1
|
||||||
stack_limit('ender_pearl') => 16
|
stack_limit('ender_pearl') => 16
|
||||||
@@ -3833,6 +3930,8 @@ stack_limit('stone') => 64
|
|||||||
|
|
||||||
Returns the string representing the category of a given item, like `building_blocks`, `combat`, or `tools`.
|
Returns the string representing the category of a given item, like `building_blocks`, `combat`, or `tools`.
|
||||||
|
|
||||||
|
Throws `unknown_item` if item doesn't exist.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
item_category('wooden_axe') => tools
|
item_category('wooden_axe') => tools
|
||||||
item_category('ender_pearl') => misc
|
item_category('ender_pearl') => misc
|
||||||
@@ -3955,6 +4054,8 @@ while( (slot = inventory_find(p, 'diamond', slot)) != null, 41, drop_item(p, slo
|
|||||||
inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items
|
inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
Throws `unknown_item` if item doesn't exist.
|
||||||
|
|
||||||
### `inventory_remove(inventory, item, amount?)`
|
### `inventory_remove(inventory, item, amount?)`
|
||||||
|
|
||||||
Removes amount (defaults to 1) of item from inventory. If the inventory doesn't have the defined amount, nothing
|
Removes amount (defaults to 1) of item from inventory. If the inventory doesn't have the defined amount, nothing
|
||||||
@@ -4349,13 +4450,14 @@ Displays or modifies individual scoreboard values. With no arguments, returns th
|
|||||||
With specified `objective`, lists all keys (players) associated with current objective, or `null` if objective does not exist.
|
With specified `objective`, lists all keys (players) associated with current objective, or `null` if objective does not exist.
|
||||||
With specified `objective` and
|
With specified `objective` and
|
||||||
`key`, returns current value of the objective for a given player (key). With additional `value` sets a new scoreboard
|
`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`.
|
value, returning previous value associated with the `key`. If the `value` is null, resets the scoreboard value.
|
||||||
|
|
||||||
### `scoreboard_add(objective, criterion?)`
|
### `scoreboard_add(objective, criterion?)`
|
||||||
|
|
||||||
Adds a new objective to scoreboard. If `criterion` is not specified, assumes `'dummy'`.
|
Adds a new objective to scoreboard. If `criterion` is not specified, assumes `'dummy'`.
|
||||||
If the objective already exists, changes the criterion of that objective and returns `false`. If the criterion was not specified but the objective already exists, returns the current criterion.
|
Returns `true` if the objective was created, or `null` if an objective with the specified name already exists.
|
||||||
If the objective was added, returns `true`. If nothing is affected, returns `null`
|
|
||||||
|
Throws `unknown_criterion` if criterion doesn't exist.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
scoreboard_add('counter')
|
scoreboard_add('counter')
|
||||||
@@ -4374,6 +4476,15 @@ for the objective.
|
|||||||
Sets display location for a specified `objective`. If `objective` is `null`, then display is cleared. If objective is invalid,
|
Sets display location for a specified `objective`. If `objective` is `null`, then display is cleared. If objective is invalid,
|
||||||
returns `null`.
|
returns `null`.
|
||||||
|
|
||||||
|
### `scoreboard_property(objective, property)` `scoreboard_property(objective, property, value)`
|
||||||
|
|
||||||
|
Reads a property of an `objective` or sets it to a `value` if specified. Available properties are:
|
||||||
|
|
||||||
|
* `criterion`
|
||||||
|
* `display_name` (Formatted text supported)
|
||||||
|
* `display_slot`: When reading, returns a list of slots this objective is displayed in, when modifying, displays the objective in the specified slot
|
||||||
|
* `render_type`: Either `'integer'` or `'hearts'`, defaults to `'integer'` if invalid value specified
|
||||||
|
|
||||||
# Team
|
# Team
|
||||||
|
|
||||||
### `team_list()`, `team_list(team)`
|
### `team_list()`, `team_list(team)`
|
||||||
@@ -4512,29 +4623,40 @@ Collection of other methods that control smaller, yet still important aspects of
|
|||||||
|
|
||||||
## Sounds
|
## Sounds
|
||||||
|
|
||||||
### `sound(name, pos, volume?, pitch?, mixer?)`
|
### `sound()`, `sound(name, pos, volume?, pitch?, mixer?)`
|
||||||
|
|
||||||
Plays a specific sound `name`, at block or position `pos`, with optional `volume` and modified `pitch`, and under
|
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`.
|
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`
|
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
|
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.
|
corresponding `playsound` command.
|
||||||
|
|
||||||
|
Used with no arguments, return the list of available sound names.
|
||||||
|
|
||||||
|
Throws `unknown_sound` if sound doesn't exist.
|
||||||
|
|
||||||
## Particles
|
## Particles
|
||||||
|
|
||||||
### `particle(name, pos, count?. spread?, speed?, player?)`
|
### `particle()`, `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`
|
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`
|
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
|
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'`.
|
for example `'angry_villager', 'item diamond', 'block stone', 'dust 0.8 0.1 0.1 4'`.
|
||||||
|
|
||||||
|
Used with no arguments, return the list of available particle names. Note that some of the names do not correspond to a valid
|
||||||
|
particle that can be fed to `particle(...)` function due to a fact that some particles need more configuration
|
||||||
|
to be valid, like `dust`, `block` etc. Should be used as a reference only.
|
||||||
|
|
||||||
|
Throws `unknown_particle` if particle doesn't exist.
|
||||||
|
|
||||||
### `particle_line(name, pos, pos2, density?, player?)`
|
### `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
|
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
|
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.
|
that player will receive particles.
|
||||||
|
|
||||||
|
Throws `unknown_particle` if particle doesn't exist.
|
||||||
|
|
||||||
### `particle_box(name, pos, pos2, density?, player?)`
|
### `particle_box(name, pos, pos2, density?, player?)`
|
||||||
### `particle_rect` (deprecated)
|
### `particle_rect` (deprecated)
|
||||||
@@ -4542,6 +4664,8 @@ that player will receive particles.
|
|||||||
Renders a cuboid of particles between points `pos` and `pos2` with supplied density. If a player (or player name) is
|
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.
|
supplied, only that player will receive particles.
|
||||||
|
|
||||||
|
Throws `unknown_particle` if particle doesn't exist.
|
||||||
|
|
||||||
## Markers
|
## Markers
|
||||||
|
|
||||||
### `draw_shape(shape, duration, key?, value?, ... )`,
|
### `draw_shape(shape, duration, key?, value?, ... )`,
|
||||||
@@ -4575,7 +4699,7 @@ Optional shared shape attributes:
|
|||||||
in the form of `0xRRGGBBAA`, with the default of `-1`, so white opaque, or `0xFFFFFFFF`.
|
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` - 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.
|
player, otherwise it will be send to all players in the dimension.
|
||||||
* `line` - line thickness, defaults to 2.0pt
|
* `line` - (Deprecated) line thickness, defaults to 2.0pt. Not supported in 1.17's 3.2 core GL renderer.
|
||||||
* `fill` - color for the faces, defaults to no fill. Use `color` attribute format
|
* `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` - 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
|
Follow attribute requires all positional arguments to be relative to the entity and disallow
|
||||||
@@ -4609,7 +4733,7 @@ Available shapes:
|
|||||||
* `align` - text alignment with regards to `pos`. Default is `center` (displayed text is
|
* `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`
|
centered with respect to `pos`), `left` (`pos` indicates beginning of text), and `right` (`pos`
|
||||||
indicates the end of text).
|
indicates the end of text).
|
||||||
* `tilt` - additional rotation of the text on the canvas
|
* `tilt`, `lean`, `turn` - additional rotations of the text on the canvas along all three axis
|
||||||
* `indent`, `height`, `raise` - offsets for text rendering on X (`indent`), Y (`height`), and Z axis (`raise`)
|
* `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
|
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`
|
can be used to display multiple lines of text bound to the same `pos`
|
||||||
@@ -4793,7 +4917,8 @@ 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
|
A path can contain letters, numbers, characters `-`, `+`, or `_`, and a folder separator: `'/'`. Any other characters are stripped
|
||||||
from the name. Empty descriptors are invalid, except for `list_files` where it means the root folder.
|
from the name. Empty descriptors are invalid, except for `list_files` where it means the root folder.
|
||||||
Do not add file extensions to the descriptor - extensions are inferred
|
Do not add file extensions to the descriptor - extensions are inferred
|
||||||
based on the `type` of the file.
|
based on the `type` of the file. A path can have one `'.zip'` component indicating a zip folder allowing to read / write to and from
|
||||||
|
zip files, although you cannot nest zip files in other zip files.
|
||||||
|
|
||||||
Resources can be located in the app specific space, or a shared space for all the apps. Accessing of app-specific
|
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
|
resources is guaranteed to be isolated from other apps. Shared resources are... well, shared across all apes, meaning
|
||||||
@@ -4807,7 +4932,7 @@ specific data directory is under `world/scripts/foo.data/...`, and shared data s
|
|||||||
|
|
||||||
The default no-name app, via `/script run` command can only save/load/read files from the shared space.
|
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, list and delete operations). Returns `true`
|
Functions return `null` if no file is present (for read, list and delete operations). Returns `true`
|
||||||
for success writes and deletes, and requested data, based on the file type, for read operations. It returns list of files
|
for success writes and deletes, and requested data, based on the file type, for read operations. It returns list of files
|
||||||
for folder listing.
|
for folder listing.
|
||||||
|
|
||||||
@@ -4828,6 +4953,14 @@ record to the file. Since files are closed after each write, sending multiple li
|
|||||||
write is beneficial for writing speed. To send multiple packs of data, either provide them flat or as a list in the
|
write is beneficial for writing speed. To send multiple packs of data, either provide them flat or as a list in the
|
||||||
third argument.
|
third argument.
|
||||||
|
|
||||||
|
Throws:
|
||||||
|
- `nbt_read_error`: When failed to read NBT file.
|
||||||
|
- `json_read_error`: When failed to read JSON file. The exception data will contain details about the problem.
|
||||||
|
- `io_exception`: For all other errors when handling data on disk not related to encoding issues
|
||||||
|
|
||||||
|
All other errors resulting of improper use of input arguments should result in `null` returned from the function, rather than exception
|
||||||
|
thrown.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
write_file('foo', 'shared_text, ['one', 'two']);
|
write_file('foo', 'shared_text, ['one', 'two']);
|
||||||
write_file('foo', 'shared_text', 'three\n', 'four\n');
|
write_file('foo', 'shared_text', 'three\n', 'four\n');
|
||||||
@@ -4879,6 +5012,8 @@ specific data directory is under `world/scripts/foo.data/bar/../baz.nbt`, and sh
|
|||||||
|
|
||||||
You can use app data to save non-vanilla information separately from the world and other scripts.
|
You can use app data to save non-vanilla information separately from the world and other scripts.
|
||||||
|
|
||||||
|
Throws `nbt_read_error` if failed to read app data.
|
||||||
|
|
||||||
### `store_app_data(tag)`
|
### `store_app_data(tag)`
|
||||||
|
|
||||||
Note: `store_app_data(tag, file)` and `store_app_data(tag, file, shared?)` usages deprecated. Use `write_file` instead.
|
Note: `store_app_data(tag, file)` and `store_app_data(tag, file, shared?)` usages deprecated. Use `write_file` instead.
|
||||||
@@ -4893,6 +5028,83 @@ 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`.
|
Uses the same file structure for exclusive app data, and shared data folder as `load_app_data`.
|
||||||
|
|
||||||
|
### `create_datapack(name, data)`
|
||||||
|
|
||||||
|
Creates and loads custom datapack. The data has to be a map representing the file structure and the content of the
|
||||||
|
json files of the target pack.
|
||||||
|
|
||||||
|
Returns `null` if the pack with this name already exists or is loaded, meaning no change has been made.
|
||||||
|
Returns `false` if adding of the datapack wasn't successful.
|
||||||
|
Returns `true` if creation and loading of the datapack was successful. Loading of a datapack results in
|
||||||
|
reloading of all other datapacks (vanilla restrictions, identical to /datapack enable), however unlike with `/reload`
|
||||||
|
command, scarpet apps will not be reloaded by adding a datapack using `create_datapack`.
|
||||||
|
|
||||||
|
Currently, only json files are supported in the packs. `'pack.mcmeta'` file is added automatically.
|
||||||
|
|
||||||
|
Reloading of datapacks that define new dimensions is not implemented in vanilla. Vanilla game only loads
|
||||||
|
dimension information on server start. `create_datapack` is therefore a direct replacement of manually ploping of the specified
|
||||||
|
file structure in a datapack file and calling `/datapack enable` on the new datapack with all its quirks and sideeffects
|
||||||
|
(like no worldgen changes, reloading all other datapacks, etc.). To enable newly added custom dimensions, call much more
|
||||||
|
experimental `check_hidden_dimensions()` after adding a datapack if needed.
|
||||||
|
|
||||||
|
Synopsis:
|
||||||
|
<pre>
|
||||||
|
script run create_datapack('foo',
|
||||||
|
{
|
||||||
|
'foo' -> {
|
||||||
|
'bar.json' -> {
|
||||||
|
'c' -> true,
|
||||||
|
'd' -> false,
|
||||||
|
'e' -> {'foo' -> [1,2,3]},
|
||||||
|
'a' -> 'foobar',
|
||||||
|
'b' -> 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
script run create_datapack('funky_world', {
|
||||||
|
'data' -> {
|
||||||
|
'minecraft' -> {
|
||||||
|
'dimension' -> {
|
||||||
|
'custom_ow.json' -> {
|
||||||
|
'type' -> 'minecraft:the_end',
|
||||||
|
'generator' -> {
|
||||||
|
'biome_source' -> {
|
||||||
|
'seed' -> 0,
|
||||||
|
'large_biomes' -> false,
|
||||||
|
'type' -> 'minecraft:vanilla_layered'
|
||||||
|
},
|
||||||
|
'seed' -> 0,
|
||||||
|
'settings' -> 'minecraft:nether',
|
||||||
|
'type' -> 'minecraft:noise'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
check_hidden_dimensions(); => ['funky_world']
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
### `enable_hidden_dimensions()`
|
||||||
|
|
||||||
|
The function reads current datapack settings detecting new dimensions defined by these datapacks that have not yet been added
|
||||||
|
to the list of current dimensions and adds them so that they can be used and accessed right away. It doesn't matter how the
|
||||||
|
datapacks have been added to the game, either with 'create_datapack()' or manually by dropping a datapack file and calling
|
||||||
|
`/datapack enable` on it. Returns the list of valid dimension names / identifiers that has been added in the process.
|
||||||
|
|
||||||
|
Fine print: The function should be
|
||||||
|
considered experimental. There 'should not be' (famous last words) any side-effects if no worlds are added. Already connected
|
||||||
|
clients will not see suggestions for commands that use dimensions `/execute in <dim>` (vanilla client limitation)
|
||||||
|
but all commands should work just fine with
|
||||||
|
the new dimensions. Existing worlds that have gotten modified settings by the datapacks will not be reloaded or replaced.
|
||||||
|
The usability of the dimensions added this way has not been fully tested, but it seems it works just fine. Generator settings
|
||||||
|
for the new dimensions will not be added to `'level.dat'` but it will be added there automatically next time the game restarts by
|
||||||
|
vanilla. One could have said to use this method with caution, and the authors take no responsibility of any losses incurred due to
|
||||||
|
mis-handlilng of the temporary added dimensions, yet the feature itself (custom dimensions) is clearly experimental for Mojang
|
||||||
|
themselves, so that's about it.
|
||||||
|
|
||||||
### `tick_time()`
|
### `tick_time()`
|
||||||
|
|
||||||
Returns server tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time.
|
Returns server tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time.
|
||||||
@@ -4956,6 +5168,8 @@ Evaluates the expression `expr` with different dimension execution context. `smt
|
|||||||
world-localized block, so not `block('stone')`, or a string representing a dimension like:
|
world-localized block, so not `block('stone')`, or a string representing a dimension like:
|
||||||
`'nether'`, `'the_nether'`, `'end'` or `'overworld'`, etc.
|
`'nether'`, `'the_nether'`, `'end'` or `'overworld'`, etc.
|
||||||
|
|
||||||
|
Throws `unknown_dimension` if provided dimension can't be found.
|
||||||
|
|
||||||
### `view_distance()`
|
### `view_distance()`
|
||||||
Returns the view distance of the server.
|
Returns the view distance of the server.
|
||||||
|
|
||||||
@@ -5010,11 +5224,12 @@ Available options in the scarpet app space:
|
|||||||
Relevant world related properties
|
Relevant world related properties
|
||||||
* `world_name` - name of the world
|
* `world_name` - name of the world
|
||||||
* `world_seed` - a numeric seed of the world
|
* `world_seed` - a numeric seed of the world
|
||||||
|
* `world_dimensions` - a list of dimensions in the world
|
||||||
* `world_path` - full path to the world saves folder
|
* `world_path` - full path to the world saves folder
|
||||||
* `world_folder` - name of the direct folder in the saves that holds world files
|
* `world_folder` - name of the direct folder in the saves that holds world files
|
||||||
* `world_carpet_rules` - returns all Carpet rules in a map form (`rule`->`value`). Note that the values are always returned as strings, so you can't do boolean comparisons directly. Includes rules from extensions with their namespace (`namespace:rule`->`value`). You can later listen to rule changes with the `on_carpet_rule_changes(rule, newValue)` event.
|
* `world_carpet_rules` - returns all Carpet rules in a map form (`rule`->`value`). Note that the values are always returned as strings, so you can't do boolean comparisons directly. Includes rules from extensions with their namespace (`namespace:rule`->`value`). You can later listen to rule changes with the `on_carpet_rule_changes(rule, newValue)` event.
|
||||||
* `world_gamerules` - returns all gamerules in a map form (`rule`->`value`). Like carpet rules, values are returned as strings, so you can use appropriate value conversions using `bool()` or `number()` to convert them to other values. Gamerules are read-only to discourage app programmers to mess up with the settings intentionally applied by server admins. Isn't that just super annoying when a datapack messes up with your gamerule settings? It is still possible to change them though using `run('gamerule ...`.
|
* `world_gamerules` - returns all gamerules in a map form (`rule`->`value`). Like carpet rules, values are returned as strings, so you can use appropriate value conversions using `bool()` or `number()` to convert them to other values. Gamerules are read-only to discourage app programmers to mess up with the settings intentionally applied by server admins. Isn't that just super annoying when a datapack messes up with your gamerule settings? It is still possible to change them though using `run('gamerule ...`.
|
||||||
|
* `world_spawn_point` - world spawn point
|
||||||
|
|
||||||
Relevant gameplay related properties
|
Relevant gameplay related properties
|
||||||
* `game_difficulty` - current difficulty of the game: `'peaceful'`, `'easy'`, `'normal'`, or `'hard'`
|
* `game_difficulty` - current difficulty of the game: `'peaceful'`, `'easy'`, `'normal'`, or `'hard'`
|
||||||
@@ -5025,7 +5240,13 @@ Available options in the scarpet app space:
|
|||||||
* `game_view_distance` - the view distance
|
* `game_view_distance` - the view distance
|
||||||
* `game_mod_name` - the name of the base mod. Expect `'fabric'`
|
* `game_mod_name` - the name of the base mod. Expect `'fabric'`
|
||||||
* `game_version` - base version of the game
|
* `game_version` - base version of the game
|
||||||
|
* `game_target` - target release version
|
||||||
|
* `game_major_target` - major release target. For 1.12.2, that would be 12
|
||||||
|
* `game_minor_reease` - minor release target. For 1.12.2, that woudl be 2
|
||||||
|
* `game_protocol` - protocol version number
|
||||||
|
* `game_pack_version` - datapack version number
|
||||||
* `game_data_version` - data version of the game. Returns an integer, so it can be compared.
|
* `game_data_version` - data version of the game. Returns an integer, so it can be compared.
|
||||||
|
* `game_stable` - indicating if its a production release or a snapshot
|
||||||
|
|
||||||
Server related properties
|
Server related properties
|
||||||
* `server_motd` - the motd of the server visible when joining
|
* `server_motd` - the motd of the server visible when joining
|
||||||
|
|||||||
@@ -4,29 +4,40 @@ Collection of other methods that control smaller, yet still important aspects of
|
|||||||
|
|
||||||
## Sounds
|
## Sounds
|
||||||
|
|
||||||
### `sound(name, pos, volume?, pitch?, mixer?)`
|
### `sound()`, `sound(name, pos, volume?, pitch?, mixer?)`
|
||||||
|
|
||||||
Plays a specific sound `name`, at block or position `pos`, with optional `volume` and modified `pitch`, and under
|
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`.
|
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`
|
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
|
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.
|
corresponding `playsound` command.
|
||||||
|
|
||||||
|
Used with no arguments, return the list of available sound names.
|
||||||
|
|
||||||
|
Throws `unknown_sound` if sound doesn't exist.
|
||||||
|
|
||||||
## Particles
|
## Particles
|
||||||
|
|
||||||
### `particle(name, pos, count?. spread?, speed?, player?)`
|
### `particle()`, `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`
|
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`
|
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
|
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'`.
|
for example `'angry_villager', 'item diamond', 'block stone', 'dust 0.8 0.1 0.1 4'`.
|
||||||
|
|
||||||
|
Used with no arguments, return the list of available particle names. Note that some of the names do not correspond to a valid
|
||||||
|
particle that can be fed to `particle(...)` function due to a fact that some particles need more configuration
|
||||||
|
to be valid, like `dust`, `block` etc. Should be used as a reference only.
|
||||||
|
|
||||||
|
Throws `unknown_particle` if particle doesn't exist.
|
||||||
|
|
||||||
### `particle_line(name, pos, pos2, density?, player?)`
|
### `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
|
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
|
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.
|
that player will receive particles.
|
||||||
|
|
||||||
|
Throws `unknown_particle` if particle doesn't exist.
|
||||||
|
|
||||||
### `particle_box(name, pos, pos2, density?, player?)`
|
### `particle_box(name, pos, pos2, density?, player?)`
|
||||||
### `particle_rect` (deprecated)
|
### `particle_rect` (deprecated)
|
||||||
@@ -34,6 +45,8 @@ that player will receive particles.
|
|||||||
Renders a cuboid of particles between points `pos` and `pos2` with supplied density. If a player (or player name) is
|
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.
|
supplied, only that player will receive particles.
|
||||||
|
|
||||||
|
Throws `unknown_particle` if particle doesn't exist.
|
||||||
|
|
||||||
## Markers
|
## Markers
|
||||||
|
|
||||||
### `draw_shape(shape, duration, key?, value?, ... )`,
|
### `draw_shape(shape, duration, key?, value?, ... )`,
|
||||||
@@ -67,7 +80,7 @@ Optional shared shape attributes:
|
|||||||
in the form of `0xRRGGBBAA`, with the default of `-1`, so white opaque, or `0xFFFFFFFF`.
|
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` - 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.
|
player, otherwise it will be send to all players in the dimension.
|
||||||
* `line` - line thickness, defaults to 2.0pt
|
* `line` - (Deprecated) line thickness, defaults to 2.0pt. Not supported in 1.17's 3.2 core GL renderer.
|
||||||
* `fill` - color for the faces, defaults to no fill. Use `color` attribute format
|
* `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` - 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
|
Follow attribute requires all positional arguments to be relative to the entity and disallow
|
||||||
@@ -101,7 +114,7 @@ Available shapes:
|
|||||||
* `align` - text alignment with regards to `pos`. Default is `center` (displayed text is
|
* `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`
|
centered with respect to `pos`), `left` (`pos` indicates beginning of text), and `right` (`pos`
|
||||||
indicates the end of text).
|
indicates the end of text).
|
||||||
* `tilt` - additional rotation of the text on the canvas
|
* `tilt`, `lean`, `turn` - additional rotations of the text on the canvas along all three axis
|
||||||
* `indent`, `height`, `raise` - offsets for text rendering on X (`indent`), Y (`height`), and Z axis (`raise`)
|
* `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
|
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`
|
can be used to display multiple lines of text bound to the same `pos`
|
||||||
@@ -296,7 +309,8 @@ 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
|
A path can contain letters, numbers, characters `-`, `+`, or `_`, and a folder separator: `'/'`. Any other characters are stripped
|
||||||
from the name. Empty descriptors are invalid, except for `list_files` where it means the root folder.
|
from the name. Empty descriptors are invalid, except for `list_files` where it means the root folder.
|
||||||
Do not add file extensions to the descriptor - extensions are inferred
|
Do not add file extensions to the descriptor - extensions are inferred
|
||||||
based on the `type` of the file.
|
based on the `type` of the file. A path can have one `'.zip'` component indicating a zip folder allowing to read / write to and from
|
||||||
|
zip files, although you cannot nest zip files in other zip files.
|
||||||
|
|
||||||
Resources can be located in the app specific space, or a shared space for all the apps. Accessing of app-specific
|
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
|
resources is guaranteed to be isolated from other apps. Shared resources are... well, shared across all apes, meaning
|
||||||
@@ -310,7 +324,7 @@ specific data directory is under `world/scripts/foo.data/...`, and shared data s
|
|||||||
|
|
||||||
The default no-name app, via `/script run` command can only save/load/read files from the shared space.
|
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, list and delete operations). Returns `true`
|
Functions return `null` if no file is present (for read, list and delete operations). Returns `true`
|
||||||
for success writes and deletes, and requested data, based on the file type, for read operations. It returns list of files
|
for success writes and deletes, and requested data, based on the file type, for read operations. It returns list of files
|
||||||
for folder listing.
|
for folder listing.
|
||||||
|
|
||||||
@@ -331,6 +345,14 @@ record to the file. Since files are closed after each write, sending multiple li
|
|||||||
write is beneficial for writing speed. To send multiple packs of data, either provide them flat or as a list in the
|
write is beneficial for writing speed. To send multiple packs of data, either provide them flat or as a list in the
|
||||||
third argument.
|
third argument.
|
||||||
|
|
||||||
|
Throws:
|
||||||
|
- `nbt_read_error`: When failed to read NBT file.
|
||||||
|
- `json_read_error`: When failed to read JSON file. The exception data will contain details about the problem.
|
||||||
|
- `io_exception`: For all other errors when handling data on disk not related to encoding issues
|
||||||
|
|
||||||
|
All other errors resulting of improper use of input arguments should result in `null` returned from the function, rather than exception
|
||||||
|
thrown.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
write_file('foo', 'shared_text, ['one', 'two']);
|
write_file('foo', 'shared_text, ['one', 'two']);
|
||||||
write_file('foo', 'shared_text', 'three\n', 'four\n');
|
write_file('foo', 'shared_text', 'three\n', 'four\n');
|
||||||
@@ -382,6 +404,8 @@ specific data directory is under `world/scripts/foo.data/bar/../baz.nbt`, and sh
|
|||||||
|
|
||||||
You can use app data to save non-vanilla information separately from the world and other scripts.
|
You can use app data to save non-vanilla information separately from the world and other scripts.
|
||||||
|
|
||||||
|
Throws `nbt_read_error` if failed to read app data.
|
||||||
|
|
||||||
### `store_app_data(tag)`
|
### `store_app_data(tag)`
|
||||||
|
|
||||||
Note: `store_app_data(tag, file)` and `store_app_data(tag, file, shared?)` usages deprecated. Use `write_file` instead.
|
Note: `store_app_data(tag, file)` and `store_app_data(tag, file, shared?)` usages deprecated. Use `write_file` instead.
|
||||||
@@ -396,6 +420,83 @@ 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`.
|
Uses the same file structure for exclusive app data, and shared data folder as `load_app_data`.
|
||||||
|
|
||||||
|
### `create_datapack(name, data)`
|
||||||
|
|
||||||
|
Creates and loads custom datapack. The data has to be a map representing the file structure and the content of the
|
||||||
|
json files of the target pack.
|
||||||
|
|
||||||
|
Returns `null` if the pack with this name already exists or is loaded, meaning no change has been made.
|
||||||
|
Returns `false` if adding of the datapack wasn't successful.
|
||||||
|
Returns `true` if creation and loading of the datapack was successful. Loading of a datapack results in
|
||||||
|
reloading of all other datapacks (vanilla restrictions, identical to /datapack enable), however unlike with `/reload`
|
||||||
|
command, scarpet apps will not be reloaded by adding a datapack using `create_datapack`.
|
||||||
|
|
||||||
|
Currently, only json files are supported in the packs. `'pack.mcmeta'` file is added automatically.
|
||||||
|
|
||||||
|
Reloading of datapacks that define new dimensions is not implemented in vanilla. Vanilla game only loads
|
||||||
|
dimension information on server start. `create_datapack` is therefore a direct replacement of manually ploping of the specified
|
||||||
|
file structure in a datapack file and calling `/datapack enable` on the new datapack with all its quirks and sideeffects
|
||||||
|
(like no worldgen changes, reloading all other datapacks, etc.). To enable newly added custom dimensions, call much more
|
||||||
|
experimental `check_hidden_dimensions()` after adding a datapack if needed.
|
||||||
|
|
||||||
|
Synopsis:
|
||||||
|
<pre>
|
||||||
|
script run create_datapack('foo',
|
||||||
|
{
|
||||||
|
'foo' -> {
|
||||||
|
'bar.json' -> {
|
||||||
|
'c' -> true,
|
||||||
|
'd' -> false,
|
||||||
|
'e' -> {'foo' -> [1,2,3]},
|
||||||
|
'a' -> 'foobar',
|
||||||
|
'b' -> 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
script run create_datapack('funky_world', {
|
||||||
|
'data' -> {
|
||||||
|
'minecraft' -> {
|
||||||
|
'dimension' -> {
|
||||||
|
'custom_ow.json' -> {
|
||||||
|
'type' -> 'minecraft:the_end',
|
||||||
|
'generator' -> {
|
||||||
|
'biome_source' -> {
|
||||||
|
'seed' -> 0,
|
||||||
|
'large_biomes' -> false,
|
||||||
|
'type' -> 'minecraft:vanilla_layered'
|
||||||
|
},
|
||||||
|
'seed' -> 0,
|
||||||
|
'settings' -> 'minecraft:nether',
|
||||||
|
'type' -> 'minecraft:noise'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
check_hidden_dimensions(); => ['funky_world']
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
### `enable_hidden_dimensions()`
|
||||||
|
|
||||||
|
The function reads current datapack settings detecting new dimensions defined by these datapacks that have not yet been added
|
||||||
|
to the list of current dimensions and adds them so that they can be used and accessed right away. It doesn't matter how the
|
||||||
|
datapacks have been added to the game, either with 'create_datapack()' or manually by dropping a datapack file and calling
|
||||||
|
`/datapack enable` on it. Returns the list of valid dimension names / identifiers that has been added in the process.
|
||||||
|
|
||||||
|
Fine print: The function should be
|
||||||
|
considered experimental. There 'should not be' (famous last words) any side-effects if no worlds are added. Already connected
|
||||||
|
clients will not see suggestions for commands that use dimensions `/execute in <dim>` (vanilla client limitation)
|
||||||
|
but all commands should work just fine with
|
||||||
|
the new dimensions. Existing worlds that have gotten modified settings by the datapacks will not be reloaded or replaced.
|
||||||
|
The usability of the dimensions added this way has not been fully tested, but it seems it works just fine. Generator settings
|
||||||
|
for the new dimensions will not be added to `'level.dat'` but it will be added there automatically next time the game restarts by
|
||||||
|
vanilla. One could have said to use this method with caution, and the authors take no responsibility of any losses incurred due to
|
||||||
|
mis-handlilng of the temporary added dimensions, yet the feature itself (custom dimensions) is clearly experimental for Mojang
|
||||||
|
themselves, so that's about it.
|
||||||
|
|
||||||
### `tick_time()`
|
### `tick_time()`
|
||||||
|
|
||||||
Returns server tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time.
|
Returns server tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time.
|
||||||
@@ -459,6 +560,8 @@ Evaluates the expression `expr` with different dimension execution context. `smt
|
|||||||
world-localized block, so not `block('stone')`, or a string representing a dimension like:
|
world-localized block, so not `block('stone')`, or a string representing a dimension like:
|
||||||
`'nether'`, `'the_nether'`, `'end'` or `'overworld'`, etc.
|
`'nether'`, `'the_nether'`, `'end'` or `'overworld'`, etc.
|
||||||
|
|
||||||
|
Throws `unknown_dimension` if provided dimension can't be found.
|
||||||
|
|
||||||
### `view_distance()`
|
### `view_distance()`
|
||||||
Returns the view distance of the server.
|
Returns the view distance of the server.
|
||||||
|
|
||||||
@@ -513,11 +616,12 @@ Available options in the scarpet app space:
|
|||||||
Relevant world related properties
|
Relevant world related properties
|
||||||
* `world_name` - name of the world
|
* `world_name` - name of the world
|
||||||
* `world_seed` - a numeric seed of the world
|
* `world_seed` - a numeric seed of the world
|
||||||
|
* `world_dimensions` - a list of dimensions in the world
|
||||||
* `world_path` - full path to the world saves folder
|
* `world_path` - full path to the world saves folder
|
||||||
* `world_folder` - name of the direct folder in the saves that holds world files
|
* `world_folder` - name of the direct folder in the saves that holds world files
|
||||||
* `world_carpet_rules` - returns all Carpet rules in a map form (`rule`->`value`). Note that the values are always returned as strings, so you can't do boolean comparisons directly. Includes rules from extensions with their namespace (`namespace:rule`->`value`). You can later listen to rule changes with the `on_carpet_rule_changes(rule, newValue)` event.
|
* `world_carpet_rules` - returns all Carpet rules in a map form (`rule`->`value`). Note that the values are always returned as strings, so you can't do boolean comparisons directly. Includes rules from extensions with their namespace (`namespace:rule`->`value`). You can later listen to rule changes with the `on_carpet_rule_changes(rule, newValue)` event.
|
||||||
* `world_gamerules` - returns all gamerules in a map form (`rule`->`value`). Like carpet rules, values are returned as strings, so you can use appropriate value conversions using `bool()` or `number()` to convert them to other values. Gamerules are read-only to discourage app programmers to mess up with the settings intentionally applied by server admins. Isn't that just super annoying when a datapack messes up with your gamerule settings? It is still possible to change them though using `run('gamerule ...`.
|
* `world_gamerules` - returns all gamerules in a map form (`rule`->`value`). Like carpet rules, values are returned as strings, so you can use appropriate value conversions using `bool()` or `number()` to convert them to other values. Gamerules are read-only to discourage app programmers to mess up with the settings intentionally applied by server admins. Isn't that just super annoying when a datapack messes up with your gamerule settings? It is still possible to change them though using `run('gamerule ...`.
|
||||||
|
* `world_spawn_point` - world spawn point
|
||||||
|
|
||||||
Relevant gameplay related properties
|
Relevant gameplay related properties
|
||||||
* `game_difficulty` - current difficulty of the game: `'peaceful'`, `'easy'`, `'normal'`, or `'hard'`
|
* `game_difficulty` - current difficulty of the game: `'peaceful'`, `'easy'`, `'normal'`, or `'hard'`
|
||||||
@@ -528,7 +632,13 @@ Available options in the scarpet app space:
|
|||||||
* `game_view_distance` - the view distance
|
* `game_view_distance` - the view distance
|
||||||
* `game_mod_name` - the name of the base mod. Expect `'fabric'`
|
* `game_mod_name` - the name of the base mod. Expect `'fabric'`
|
||||||
* `game_version` - base version of the game
|
* `game_version` - base version of the game
|
||||||
|
* `game_target` - target release version
|
||||||
|
* `game_major_target` - major release target. For 1.12.2, that would be 12
|
||||||
|
* `game_minor_reease` - minor release target. For 1.12.2, that woudl be 2
|
||||||
|
* `game_protocol` - protocol version number
|
||||||
|
* `game_pack_version` - datapack version number
|
||||||
* `game_data_version` - data version of the game. Returns an integer, so it can be compared.
|
* `game_data_version` - data version of the game. Returns an integer, so it can be compared.
|
||||||
|
* `game_stable` - indicating if its a production release or a snapshot
|
||||||
|
|
||||||
Server related properties
|
Server related properties
|
||||||
* `server_motd` - the motd of the server visible when joining
|
* `server_motd` - the motd of the server visible when joining
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ when it was used. Block values passed in various places like `scan` functions, e
|
|||||||
its properties are needed. This means that if the block at the location changes before its queried in the program this
|
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:
|
might result in getting the later state, which might not be desired. Consider the following example:
|
||||||
|
|
||||||
|
Throws `unknown_block` if provided input is not valid.
|
||||||
|
|
||||||
<pre>set(10,10,10,'stone');
|
<pre>set(10,10,10,'stone');
|
||||||
scan(10,10,10,0,0,0, b = _);
|
scan(10,10,10,0,0,0, b = _);
|
||||||
set(10,10,10,'air');
|
set(10,10,10,'air');
|
||||||
@@ -50,6 +52,8 @@ properties that the original source block may have contained.
|
|||||||
|
|
||||||
The returned value is either the block state that has been set, or `false` if block setting was skipped, or failed
|
The returned value is either the block state that has been set, or `false` if block setting was skipped, or failed
|
||||||
|
|
||||||
|
Throws `unknown_block` if provided block to set is not valid
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
set(0,5,0,'bedrock') => bedrock
|
set(0,5,0,'bedrock') => bedrock
|
||||||
set(l(0,5,0), 'bedrock') => bedrock
|
set(l(0,5,0), 'bedrock') => bedrock
|
||||||
@@ -128,6 +132,8 @@ but doesn't open chests / containers, so have no effect on interactive blocks, l
|
|||||||
Returns true if placement/use was
|
Returns true if placement/use was
|
||||||
successful, false otherwise.
|
successful, false otherwise.
|
||||||
|
|
||||||
|
Throws `unknown_item` if `item` doesn't exist
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
place_item('stone',x,y,z) // places a stone block on x,y,z block
|
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('piston,x,y,z,'down') // places a piston facing down
|
||||||
@@ -146,6 +152,8 @@ If type is `null`, POI at position is removed. In any case, previous POI is also
|
|||||||
Interestingly, `unemployed`, and `nitwit` are not used in the game, meaning, they could be used as permanent spatial
|
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.
|
markers for scarpet apps. `meeting` is the only one with increased max occupancy of 32.
|
||||||
|
|
||||||
|
Throws `unknown_poi` if the provided point of interest doesn't exist
|
||||||
|
|
||||||
### `set_biome(pos, biome_name, update=true)`
|
### `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
|
Changes the biome at that block position. if update is specified and false, then chunk will not be refreshed
|
||||||
@@ -156,6 +164,8 @@ is only effective if you set it at y=0, and affects the entire column of. In the
|
|||||||
specific Y coordinate of the biome you want to change, and it affects roughly 4x4x4 area (give or take some random
|
specific Y coordinate of the biome you want to change, and it affects roughly 4x4x4 area (give or take some random
|
||||||
noise).
|
noise).
|
||||||
|
|
||||||
|
Throws `unknown_biome` if the `biome_name` doesn't exist.
|
||||||
|
|
||||||
### `update(pos)`
|
### `update(pos)`
|
||||||
|
|
||||||
Causes a block update at position.
|
Causes a block update at position.
|
||||||
@@ -180,6 +190,8 @@ In item context, `true` means that breaking item has no nbt to use, `null` indic
|
|||||||
considered broken in process, and `nbt` type value, for a resulting NBT tag on a hypothetical tool. Its up to the
|
considered broken in process, and `nbt` type value, for a resulting NBT tag on a hypothetical tool. Its up to the
|
||||||
programmer to use that nbt to apply it where it belong
|
programmer to use that nbt to apply it where it belong
|
||||||
|
|
||||||
|
Throws `unknown_item` if `tool` doesn't exist.
|
||||||
|
|
||||||
Here is a sample code that can be used to mine blocks using items in player inventory, without using player context
|
Here is a sample code that can be used to mine blocks using items in player inventory, without using player context
|
||||||
for mining. Obviously, in this case the use of `harvest` would be much more applicable:
|
for mining. Obviously, in this case the use of `harvest` would be much more applicable:
|
||||||
|
|
||||||
@@ -217,6 +229,13 @@ Causes a block to be harvested by a specified player entity. Honors player item
|
|||||||
tool if applicable. If the entity is not a valid player, no block gets destroyed. If a player is not allowed to break
|
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.
|
that block, a block doesn't get destroyed either.
|
||||||
|
|
||||||
|
### `create_explosion(pos, power?, mode?, fire?, source?, attacker?)`
|
||||||
|
|
||||||
|
Creates an explosion at a given position. Default values of optional parameters are: `'power'` - `4` (TNT power),
|
||||||
|
`'mode'` (block breaking effect `none`, `destroy` or `break`: `break`, `fire` (whether extra fire blocks should be created) - `false`,
|
||||||
|
`source` (exploding entity) - `null` and `attacker` (entity responsible for trigerring) - `null`. Explosions created with this
|
||||||
|
endpoint cannot be captured with `__on_explosion` event, however they will be captured by `__on_explosion_outcome`.
|
||||||
|
|
||||||
### `weather()`,`weather(type)`,`weather(type, ticks)`
|
### `weather()`,`weather(type)`,`weather(type, ticks)`
|
||||||
|
|
||||||
If called with no args, returns `'clear'`, `'rain` or `'thunder'` based on the current weather. If thundering, will
|
If called with no args, returns `'clear'`, `'rain` or `'thunder'` based on the current weather. If thundering, will
|
||||||
@@ -271,6 +290,8 @@ back in state definition in various applications where block properties are requ
|
|||||||
|
|
||||||
`block_state` can also accept block names as input, returning block's default state.
|
`block_state` can also accept block names as input, returning block's default state.
|
||||||
|
|
||||||
|
Throws `unknown_block` if the provided input is not valid.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
set(x,y,z,'iron_trapdoor','half','top'); block_state(x,y,z) => {waterlogged: false, half: top, open: false, ...}
|
set(x,y,z,'iron_trapdoor','half','top'); block_state(x,y,z) => {waterlogged: false, half: top, open: false, ...}
|
||||||
set(x,y,z,'iron_trapdoor','half','top'); block_state(x,y,z,'half') => top
|
set(x,y,z,'iron_trapdoor','half','top'); block_state(x,y,z,'half') => top
|
||||||
@@ -291,6 +312,8 @@ Without arguments, returns list of available tags, with block supplied (either b
|
|||||||
of tags the block belongs to, and if a tag is specified, returns `null` if tag is invalid, `false` if this block doesn't belong
|
of tags the block belongs to, and if a tag is specified, returns `null` if tag is invalid, `false` if this block doesn't belong
|
||||||
to this tag, and `true` if the block belongs to the tag.
|
to this tag, and `true` if the block belongs to the tag.
|
||||||
|
|
||||||
|
Throws `unknown_block` if `block` doesn't exist
|
||||||
|
|
||||||
### `block_data(pos)`
|
### `block_data(pos)`
|
||||||
|
|
||||||
Return NBT string associated with specific location, or null if the block does not carry block data. Can be currently
|
Return NBT string associated with specific location, or null if the block does not carry block data. Can be currently
|
||||||
@@ -299,16 +322,18 @@ used to match specific information from it, or use it to copy to another block
|
|||||||
<pre> block_data(x,y,z) => '{TransferCooldown:0,x:450,y:68, ... }'
|
<pre> block_data(x,y,z) => '{TransferCooldown:0,x:450,y:68, ... }'
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `poi(pos), poi(pos, radius?, type?, status?, column?)`
|
### `poi(pos), poi(pos, radius?, type?, status?, column_search?)`
|
||||||
|
|
||||||
Queries a POI (Point of Interest) at a given position, returning `null` if none is found, or tuple of poi type and its
|
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 `type`, `radius` and `status`, returns a list of POIs around `pos` within a
|
occupancy load. With optional `type`, `radius` and `status`, returns a list of POIs around `pos` within a
|
||||||
given `radius`. If the `type` is specified, returns only poi types of that types, or everything if omitted or `'any'`.
|
given `radius`. If the `type` is specified, returns only poi types of that types, or everything if omitted or `'any'`.
|
||||||
If `status` is specified (either `'any'`, `'available'`, or `'occupied'`) returns only POIs with that status.
|
If `status` is specified (either `'any'`, `'available'`, or `'occupied'`) returns only POIs with that status.
|
||||||
With `column` set to `true`, it will return all POIs in a cuboid with `radius` blocks away on x and z, in the entire
|
With `column_search` set to `true`, it will return all POIs in a cuboid with `radius` blocks away on x and z, in the entire
|
||||||
block column from 0 to 255. Default (`false`) returns POIs within a spherical area centered on `pos` and with `radius`
|
block column from 0 to 255. Default (`false`) returns POIs within a spherical area centered on `pos` and with `radius`
|
||||||
radius.
|
radius.
|
||||||
|
|
||||||
|
All results of `poi` calls are returned in sorted order with respect to the euclidean distance to the requested center of `pos`.
|
||||||
|
|
||||||
The return format of the results is a list of poi type, occupancy load, and extra triple of coordinates.
|
The return format of the results is a list of poi type, occupancy load, and extra triple of coordinates.
|
||||||
|
|
||||||
Querying for POIs using the radius is the intended use of POI mechanics, and the ability of accessing individual POIs
|
Querying for POIs using the radius is the intended use of POI mechanics, and the ability of accessing individual POIs
|
||||||
@@ -325,9 +350,9 @@ poi(x,y,z,5) => [['nether_portal',0,[7,8,9]],['nether_portal',0,[7,9,9]]] // two
|
|||||||
|
|
||||||
Without arguments, returns the list of biomes in the world.
|
Without arguments, returns the list of biomes in the world.
|
||||||
|
|
||||||
With block, or name, returns the name of the biome in that position, or null, if provided biome is not valid.
|
With block, or name, returns the name of the biome in that position, or throws `'unknown_biome'` if provided biome or block are not valid.
|
||||||
|
|
||||||
With an optional feature, it returns value for the specified attribute for that biome. Available and querable features include:
|
With an optional feature, it returns value for the specified attribute for that biome. Available and queryable features include:
|
||||||
* `'top_material'`: unlocalized block representing the top surface material
|
* `'top_material'`: unlocalized block representing the top surface material
|
||||||
* `'under_material'`: unlocalized block representing what sits below topsoil
|
* `'under_material'`: unlocalized block representing what sits below topsoil
|
||||||
* `'category'`: the parent biome this biome is derived from. Possible values include:
|
* `'category'`: the parent biome this biome is derived from. Possible values include:
|
||||||
@@ -716,6 +741,7 @@ If structure is not specified, it will return a set of structure names that are
|
|||||||
as keys, and same type of map values as with a single structure call. An empty set or an empty map would indicate that nothing
|
as keys, and same type of map values as with a single structure call. An empty set or an empty map would indicate that nothing
|
||||||
should be generated there.
|
should be generated there.
|
||||||
|
|
||||||
|
Throws `unknown_structure` if structure doesn't exist.
|
||||||
|
|
||||||
### `structures(pos), structures(pos, structure_name)`
|
### `structures(pos), structures(pos, structure_name)`
|
||||||
|
|
||||||
@@ -749,6 +775,8 @@ structure starts.
|
|||||||
Requires a `Structure Variant` or `Standard Structure` name (see above). If standard name is used, the variant of the
|
Requires a `Structure Variant` or `Standard Structure` name (see above). If standard name is used, the variant of the
|
||||||
structure may depend on the biome, otherwise the default structure for this type will be generated.
|
structure may depend on the biome, otherwise the default structure for this type will be generated.
|
||||||
|
|
||||||
|
Throws `unknown_structure` if structure doesn't exist.
|
||||||
|
|
||||||
### `plop(pos, what)`
|
### `plop(pos, what)`
|
||||||
|
|
||||||
Plops a structure or a feature at a given `pos`, so block, triple position coordinates or a list of coordinates.
|
Plops a structure or a feature at a given `pos`, so block, triple position coordinates or a list of coordinates.
|
||||||
@@ -771,7 +799,9 @@ All generated structures will retain their properties, like mob spawning, howeve
|
|||||||
itself has certain rules to spawn mobs, like plopping a nether fortress in the overworld will not spawn nether mobs,
|
itself has certain rules to spawn mobs, like plopping a nether fortress in the overworld will not spawn nether mobs,
|
||||||
because nether mobs can spawn only in the nether, but plopped in the nether - will behave like a valid nether fortress.
|
because nether mobs can spawn only in the nether, but plopped in the nether - will behave like a valid nether fortress.
|
||||||
|
|
||||||
### `custom_dimension(name, seed?)`
|
### (deprecated) `custom_dimension(name, seed?)`
|
||||||
|
|
||||||
|
Deprecated by `create_datapack()` which can be used to setup custom dimensions
|
||||||
|
|
||||||
Ensures the dimension with the given `'name'` is available and configured with the given seed. It merely sets the world
|
Ensures the dimension with the given `'name'` is available and configured with the given seed. It merely sets the world
|
||||||
generator settings to the overworld, and the optional custom seed (or using current world seed, if not provided).
|
generator settings to the overworld, and the optional custom seed (or using current world seed, if not provided).
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ Returns `true` if en entity is standing on firm ground and falling down due to t
|
|||||||
|
|
||||||
### `query(e, 'name'), query(e, 'display_name'), query(e, 'custom_name'), query(e, 'type')`
|
### `query(e, 'name'), query(e, 'display_name'), query(e, 'custom_name'), query(e, 'type')`
|
||||||
|
|
||||||
String of entity name
|
String of entity name or formatted text in the case of `display_name`
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
query(e,'name') => Leatherworker
|
query(e,'name') => Leatherworker
|
||||||
@@ -722,6 +722,10 @@ Requires a living entity as an argument.
|
|||||||
|
|
||||||
Will make the entity jump once.
|
Will make the entity jump once.
|
||||||
|
|
||||||
|
### `modify(e, 'swing')` `modify(e, 'swing', 'offhand')`
|
||||||
|
|
||||||
|
Makes the living entity swing their required limb.
|
||||||
|
|
||||||
### `modify(e, 'silent', boolean)`
|
### `modify(e, 'silent', boolean)`
|
||||||
|
|
||||||
Silences or unsilences the entity.
|
Silences or unsilences the entity.
|
||||||
|
|||||||
@@ -79,6 +79,19 @@ at the same time.
|
|||||||
Triggered right after a lightning strikes. Lightning entity as well as potential horseman trap would
|
Triggered right after a lightning strikes. Lightning entity as well as potential horseman trap would
|
||||||
already be spawned at that point. `mode` is `true` if the lightning did cause a trap to spawn.
|
already be spawned at that point. `mode` is `true` if the lightning did cause a trap to spawn.
|
||||||
|
|
||||||
|
### `__on_explosion(pos, power, source, causer, mode, fire)`
|
||||||
|
|
||||||
|
Event triggered right before explosion takes place and before has any effect on the world. `source` can be an entity causing
|
||||||
|
the explosion, and `causer` the entity triggering it,
|
||||||
|
`mode` indicates block effects: `'none'`, `'break'` (drop all blocks), or `'destroy'` - drop few blocks. Event
|
||||||
|
is not captured when `create_explosion()` is called.
|
||||||
|
|
||||||
|
### `__on_explosion_outcome(pos, power, source, causer, mode, fire, blocks, entities)`
|
||||||
|
Triggered during the explosion, before any changes to the blocks are done,
|
||||||
|
but the decision to blow up is already made and entities are already affected.
|
||||||
|
The parameter `blocks` contains the list of blocks that will blow up (empty if `explosionNoBlockDamage` is set to `true`).
|
||||||
|
The parameter `entities` contains the list of entities that have been affected by the explosion. Triggered even with `create_explosion()`.
|
||||||
|
|
||||||
### `__on_carpet_rule_changes(rule, new_value)`
|
### `__on_carpet_rule_changes(rule, new_value)`
|
||||||
Triggered when a carpet mod rule is changed. It includes extension rules, not using default `/carpet` command,
|
Triggered when a carpet mod rule is changed. It includes extension rules, not using default `/carpet` command,
|
||||||
which will then be namespaced as `namespace:rule`.
|
which will then be namespaced as `namespace:rule`.
|
||||||
@@ -107,7 +120,7 @@ Example events that may cause it to happen is releasing a bow. The event is trig
|
|||||||
the request, however the `item_tuple` is provided representing the item that the player started with. You can use that and
|
the request, however the `item_tuple` is provided representing the item that the player started with. You can use that and
|
||||||
compare with the currently held item for a delta.
|
compare with the currently held item for a delta.
|
||||||
|
|
||||||
### `__on_player_finishes_using_item(player, item_tuple, hand))`
|
### `__on_player_finishes_using_item(player, item_tuple, hand)`
|
||||||
Player using of an item is done. This is controlled server side and is responsible for such events as finishing
|
Player using of an item is done. This is controlled server side and is responsible for such events as finishing
|
||||||
eating. The event is triggered after confirming that the action is valid, and sending the feedback back
|
eating. The event is triggered after confirming that the action is valid, and sending the feedback back
|
||||||
to the client, but before triggering it and its effects in game.
|
to the client, but before triggering it and its effects in game.
|
||||||
|
|||||||
@@ -28,14 +28,16 @@ an inventory, all API functions 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.
|
Most items returned are in the form of a triple of item name, count, and nbt or the extra data associated with an item.
|
||||||
|
|
||||||
### item_list(tag?)
|
### `item_list(tag?)`
|
||||||
|
|
||||||
With no arguments, returns a list of all items in the game. With an item tag provided, list items matching the tag, or `null` if tag is not valid.
|
With no arguments, returns a list of all items in the game. With an item tag provided, list items matching the tag, or `null` if tag is not valid.
|
||||||
|
|
||||||
### item_tags(item, tag?)
|
### `item_tags(item, tag?)`
|
||||||
|
|
||||||
Returns list of tags the item belongs to, or, if tag is provided, `true` if an item maches the tag, `false` if it doesn't and `null` if that's not a valid tag
|
Returns list of tags the item belongs to, or, if tag is provided, `true` if an item maches the tag, `false` if it doesn't and `null` if that's not a valid tag
|
||||||
|
|
||||||
|
Throws `unknown_item` if item doesn't exist.
|
||||||
|
|
||||||
### `stack_limit(item)`
|
### `stack_limit(item)`
|
||||||
|
|
||||||
Returns number indicating what is the stack limit for the item. Its typically 1 (non-stackable), 16 (like buckets),
|
Returns number indicating what is the stack limit for the item. Its typically 1 (non-stackable), 16 (like buckets),
|
||||||
@@ -43,6 +45,8 @@ or 64 - rest. It is recommended to consult this, as other inventory API function
|
|||||||
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
|
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.
|
negative is the same as empty.
|
||||||
|
|
||||||
|
Throws `unknown_item` if item doesn't exist.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
stack_limit('wooden_axe') => 1
|
stack_limit('wooden_axe') => 1
|
||||||
stack_limit('ender_pearl') => 16
|
stack_limit('ender_pearl') => 16
|
||||||
@@ -53,6 +57,8 @@ stack_limit('stone') => 64
|
|||||||
|
|
||||||
Returns the string representing the category of a given item, like `building_blocks`, `combat`, or `tools`.
|
Returns the string representing the category of a given item, like `building_blocks`, `combat`, or `tools`.
|
||||||
|
|
||||||
|
Throws `unknown_item` if item doesn't exist.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
item_category('wooden_axe') => tools
|
item_category('wooden_axe') => tools
|
||||||
item_category('ender_pearl') => misc
|
item_category('ender_pearl') => misc
|
||||||
@@ -175,6 +181,8 @@ while( (slot = inventory_find(p, 'diamond', slot)) != null, 41, drop_item(p, slo
|
|||||||
inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items
|
inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
Throws `unknown_item` if item doesn't exist.
|
||||||
|
|
||||||
### `inventory_remove(inventory, item, amount?)`
|
### `inventory_remove(inventory, item, amount?)`
|
||||||
|
|
||||||
Removes amount (defaults to 1) of item from inventory. If the inventory doesn't have the defined amount, nothing
|
Removes amount (defaults to 1) of item from inventory. If the inventory doesn't have the defined amount, nothing
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ Displays or modifies individual scoreboard values. With no arguments, returns th
|
|||||||
With specified `objective`, lists all keys (players) associated with current objective, or `null` if objective does not exist.
|
With specified `objective`, lists all keys (players) associated with current objective, or `null` if objective does not exist.
|
||||||
With specified `objective` and
|
With specified `objective` and
|
||||||
`key`, returns current value of the objective for a given player (key). With additional `value` sets a new scoreboard
|
`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`.
|
value, returning previous value associated with the `key`. If the `value` is null, resets the scoreboard value.
|
||||||
|
|
||||||
### `scoreboard_add(objective, criterion?)`
|
### `scoreboard_add(objective, criterion?)`
|
||||||
|
|
||||||
Adds a new objective to scoreboard. If `criterion` is not specified, assumes `'dummy'`.
|
Adds a new objective to scoreboard. If `criterion` is not specified, assumes `'dummy'`.
|
||||||
If the objective already exists, changes the criterion of that objective and returns `false`. If the criterion was not specified but the objective already exists, returns the current criterion.
|
Returns `true` if the objective was created, or `null` if an objective with the specified name already exists.
|
||||||
If the objective was added, returns `true`. If nothing is affected, returns `null`
|
|
||||||
|
Throws `unknown_criterion` if criterion doesn't exist.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
scoreboard_add('counter')
|
scoreboard_add('counter')
|
||||||
@@ -31,6 +32,15 @@ for the objective.
|
|||||||
Sets display location for a specified `objective`. If `objective` is `null`, then display is cleared. If objective is invalid,
|
Sets display location for a specified `objective`. If `objective` is `null`, then display is cleared. If objective is invalid,
|
||||||
returns `null`.
|
returns `null`.
|
||||||
|
|
||||||
|
### `scoreboard_property(objective, property)` `scoreboard_property(objective, property, value)`
|
||||||
|
|
||||||
|
Reads a property of an `objective` or sets it to a `value` if specified. Available properties are:
|
||||||
|
|
||||||
|
* `criterion`
|
||||||
|
* `display_name` (Formatted text supported)
|
||||||
|
* `display_slot`: When reading, returns a list of slots this objective is displayed in, when modifying, displays the objective in the specified slot
|
||||||
|
* `render_type`: Either `'integer'` or `'hearts'`, defaults to `'integer'` if invalid value specified
|
||||||
|
|
||||||
# Team
|
# Team
|
||||||
|
|
||||||
### `team_list()`, `team_list(team)`
|
### `team_list()`, `team_list(team)`
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ In case to simplify the access with nested objects, you can add chain of address
|
|||||||
than calling it multiple times. `get(get(foo,a),b)` is equivalent to `get(foo, a, b)`, or `foo:a:b`.
|
than calling it multiple times. `get(get(foo,a),b)` is equivalent to `get(foo, a, b)`, or `foo:a:b`.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
get(l(range(10)), 5) => 5
|
get([range(10)], 5) => 5
|
||||||
get(l(range(10)), -1) => 9
|
get([range(10)], -1) => 9
|
||||||
get(l(range(10)), 10) => 0
|
get([range(10)], 10) => 0
|
||||||
l(range(10)):93 => 3
|
[range(10)]:93 => 3
|
||||||
get(player() ~ 'nbt', 'Health') => 20 // inefficient way to get player health, use player() ~ 'health' instead
|
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
|
get({ 'foo' -> 2, 'bar' -> 3, 'baz' -> 4 }, 'bar') => 3
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `has(container, address, ...), has(lvalue)`
|
### `has(container, address, ...), has(lvalue)`
|
||||||
@@ -100,13 +100,13 @@ of the existing elements. Use `replace` to remove and replace existing element.
|
|||||||
and merges keys from `value` with the compound tag under the path
|
and merges keys from `value` with the compound tag under the path
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
a = l(1, 2, 3); put(a, 1, 4); a => [1, 4, 3]
|
a = [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 = [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 = [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 = [1, 2, 3]; put(a, null, [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 = [1, 2, 3]; put(a, 1, [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 = [[0,0,0],[0,0,0],[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}
|
a = {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('{}'); 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: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
|
tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl', 1, 2); tag
|
||||||
@@ -187,10 +187,10 @@ other parts. In that case consecutive calls to `slice` will refer to index `0` t
|
|||||||
cannot go back nor track where they are in the sequence (see examples).
|
cannot go back nor track where they are in the sequence (see examples).
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
slice([0,1,2,3,4,5], 1, 3) => [1, 2, 3]
|
slice([0,1,2,3,4,5], 1, 3) => [1, 2]
|
||||||
slice('foobar', 0, 1) => 'f'
|
slice('foobar', 0, 1) => 'f'
|
||||||
slice('foobar', 3) => 'bar'
|
slice('foobar', 3) => 'bar'
|
||||||
slice(range(10), 3, 5) => [3, 4, 5]
|
slice(range(10), 3, 5) => [3, 4]
|
||||||
slice(range(10), 5) => [5, 6, 7, 8, 9]
|
slice(range(10), 5) => [5, 6, 7, 8, 9]
|
||||||
r = range(100); [slice(r, 5, 7), slice(r, 1, 3)] => [[5, 6], [8, 9]]
|
r = range(100); [slice(r, 5, 7), slice(r, 1, 3)] => [[5, 6], [8, 9]]
|
||||||
</pre>
|
</pre>
|
||||||
@@ -225,7 +225,7 @@ Primarily to be used in higher order functions
|
|||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
range(10) => [...]
|
range(10) => [...]
|
||||||
l(range(10)) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
[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]
|
map(range(10),_*_) => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
|
||||||
reduce(range(10),_a+_, 0) => 45
|
reduce(range(10),_a+_, 0) => 45
|
||||||
range(5,10) => [5, 6, 7, 8, 9]
|
range(5,10) => [5, 6, 7, 8, 9]
|
||||||
|
|||||||
@@ -234,15 +234,73 @@ returns everywhere, but it would often lead to a messy code.
|
|||||||
|
|
||||||
It terminates entire program passing `expr` as the result of the program execution, or null if omitted.
|
It terminates entire program passing `expr` as the result of the program execution, or null if omitted.
|
||||||
|
|
||||||
### `try(expr, catch_expr(_)?) ... throw(value?)`
|
### `try(expr)` `try(expr, user_catch_expr)` `try(expr, type, catch_expr, type?, catch_expr?, ...)`
|
||||||
|
|
||||||
`try` function evaluates expression, and continues further unless `throw` function is called anywhere
|
`try` evaluates expression, allowing capturing exceptions that would be thrown inside `expr` statement. The exceptions can be
|
||||||
inside `expr`. In that case the `catch_expr` is evaluates with `_` set to the argument `throw` was called with.
|
thrown explicitly using `throw()` or internally by scarpet where code is correct but detects illegal state. The 2-argument form
|
||||||
This mechanic accepts skipping thrown value - it throws null instead, and catch expression - then try returns
|
catches only user-thrown exceptions and one argument call `try(expr)` is equivalent to `try(expr, null)`,
|
||||||
null as well This mechanism allows to terminate large portion of a convoluted call stack and continue program
|
or `try(expr, 'user_exception', null)`. If multiple `type-catch` pairs are defined, the execution terminates on the first
|
||||||
execution. There is only one level of exceptions currently in carpet, so if the inner function also defines
|
applicable type for the exception thrown. Therefore, even if the caught exception matches multiple filters, only
|
||||||
the `try` catchment area, it will received the exception first, but it can technically rethrow the value its
|
the first matching block will be executed.
|
||||||
getting for the outer scope. Unhandled throw acts like an exit statement.
|
|
||||||
|
Catch expressions are evaluated with
|
||||||
|
`_` set to the value associated with the exception and `_trace` set to contain details about point of error (token, and line and
|
||||||
|
column positions), call stack and local
|
||||||
|
variables at the time of failure. The `type` will catch any exception of that type and any subtype of this type.
|
||||||
|
|
||||||
|
|
||||||
|
You can use `try` mechanism to exit from large portion of a convoluted call stack and continue program execution, although catching
|
||||||
|
exceptions is typically much more expensive comparing to not throwing them.
|
||||||
|
|
||||||
|
The `try` function allows you to catch some scarpet exceptions for cases covering invalid data, like invalid
|
||||||
|
blocks, biomes, dimensions and other things, that may have been modified by datapacks, resourcepacks or other mods,
|
||||||
|
or when an error is outside of the programmers scope, such as problems when reading or decoding files.
|
||||||
|
|
||||||
|
This is the hierarchy of the exceptions that could be thrown/caught in the with the `try` function:
|
||||||
|
- `exception`: This is the base exception. Catching `'exception'` allows to catch everything that can be caught,
|
||||||
|
but like everywhere else, doing that sounds like a bad idea.
|
||||||
|
- `value_exception`: This is the parent for any exception that occurs due to an
|
||||||
|
incorrect argument value provided to a built-in function
|
||||||
|
- `unknown_item`, `unknown_block`, `unknown_biome`, `unknown_sound`, `unknown_particle`,
|
||||||
|
`unknown_poi_type`, `unknown_dimension`, `unknown_structure`, `unknown_criterion`: Specific
|
||||||
|
errors thrown when a specified internal name does not exist or is invalid.
|
||||||
|
- `io_exception`: This is the parent for any exception that occurs due to an error handling external data.
|
||||||
|
- `nbt_error`: Incorrect input/output NBT file.
|
||||||
|
- `json_error`: Incorrect input/output JSON data.
|
||||||
|
- `user_exception`: Exception thrown by default with `throw` function.
|
||||||
|
|
||||||
|
Synopsis:
|
||||||
|
<pre>
|
||||||
|
inner_call() ->
|
||||||
|
(
|
||||||
|
aaa = 'booyah';
|
||||||
|
try(
|
||||||
|
for (range(10), item_tags('stick'+_*'k'));
|
||||||
|
,
|
||||||
|
print(_trace) // not caught, only catching user_exceptions
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
outer_call() ->
|
||||||
|
(
|
||||||
|
try(
|
||||||
|
inner_call()
|
||||||
|
, 'exception', // catching everything
|
||||||
|
print(_trace)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
</pre>
|
||||||
|
Producing:
|
||||||
|
```
|
||||||
|
{stack: [[<app>, inner_call, 1, 14]], locals: {_a: 0, aaa: booyah, _: 1, _y: 0, _i: 1, _x: 0, _z: 0}, token: [item_tags, 5, 23]}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `throw(value?)`, `throw(type, value)`, `throw(subtype, type, value)`
|
||||||
|
|
||||||
|
Throws an exception that can be caught in a `try` block (see above). If ran without arguments, it will throw a `user_exception`
|
||||||
|
passing `null` as the value to the `catch_expr`. With two arguments you can mimic any other exception type thrown in scarpet.
|
||||||
|
With 3 arguments, you can specify a custom exception acting as a `subtype` of a provided `type`, allowing to customize `try`
|
||||||
|
statements with custom exceptions.
|
||||||
|
|
||||||
### `if(cond, expr, cond?, expr?, ..., default?)`
|
### `if(cond, expr, cond?, expr?, ..., default?)`
|
||||||
|
|
||||||
|
|||||||
@@ -124,12 +124,12 @@ to `all(list,expr) <=> for(list,expr)==length(list)`. `expr` also receives bound
|
|||||||
and `continue` have no sense and cannot be used inside of `expr` body.
|
and `continue` have no sense and cannot be used inside of `expr` body.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
all([1,2,3], check_prime(_)) => 1
|
all([1,2,3], check_prime(_)) => true
|
||||||
all(neighbours(x,y,z), _=='stone') => 1 // if all neighbours of [x, y, z] are stone
|
all(neighbours(x,y,z), _=='stone') => true // 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(_) )
|
map(filter(rect(0,4,0,1000,0,1000), [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]]
|
=> [[-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
|
// 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(_);
|
map( filter( rect(0,4,0,1000,1,1000,1000,0,1000), [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),
|
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]]
|
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
|
// ditto, but requiring at most 7 bedrock block in the 18 blocks below them
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ in [User Defined Functions and Program Control Flow](docs/scarpet/language/Funct
|
|||||||
1357-5 => 1352
|
1357-5 => 1352
|
||||||
1357-'5' => 137
|
1357-'5' => 137
|
||||||
3*'foo'-'o' => 'fff'
|
3*'foo'-'o' => 'fff'
|
||||||
l(1,3,5)+7 => l(8,10,12)
|
[1,3,5]+7 => [8,10,12]
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
As you can see, values can behave differently when mixed with other types in the same expression.
|
As you can see, values can behave differently when mixed with other types in the same expression.
|
||||||
@@ -76,8 +76,8 @@ In Minecraft API portion `entity ~ feature` is a shortcode for `query(entity,fea
|
|||||||
any extra arguments.
|
any extra arguments.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
l(1,2,3) ~ 2 => 1
|
[1,2,3] ~ 2 => 1
|
||||||
l(1,2,3) ~ 4 => null
|
[1,2,3] ~ 4 => null
|
||||||
|
|
||||||
'foobar' ~ 'baz' => null
|
'foobar' ~ 'baz' => null
|
||||||
'foobar' ~ '.b' => 'ob'
|
'foobar' ~ '.b' => 'ob'
|
||||||
@@ -103,10 +103,10 @@ Or an example to find if a player has specific enchantment on a held axe (either
|
|||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
global_get_enchantment(p, ench) -> (
|
global_get_enchantment(p, ench) -> (
|
||||||
$ for(l('main','offhand'),
|
$ for(['main','offhand'],
|
||||||
$ holds = query(p, 'holds', _);
|
$ holds = query(p, 'holds', _);
|
||||||
$ if( holds,
|
$ if( holds,
|
||||||
$ l(what, count, nbt) = holds;
|
$ [what, count, nbt] = holds;
|
||||||
$ if( what ~ '_axe' && nbt ~ ench,
|
$ if( what ~ '_axe' && nbt ~ ench,
|
||||||
$ lvl = max(lvl, number(nbt ~ '(?<=lvl:)\\d') )
|
$ lvl = max(lvl, number(nbt ~ '(?<=lvl:)\\d') )
|
||||||
$ )
|
$ )
|
||||||
@@ -143,8 +143,8 @@ Examples:
|
|||||||
'foo'*3 => 'foofoofoo'
|
'foo'*3 => 'foofoofoo'
|
||||||
'foofoofoo' / 3 => 'foo'
|
'foofoofoo' / 3 => 'foo'
|
||||||
'foofoofoo'-'o' => 'fff'
|
'foofoofoo'-'o' => 'fff'
|
||||||
l(1,2,3)+1 => l(2,3,4)
|
[1,2,3]+1 => [2,3,4]
|
||||||
b = l(100,63,100); b+l(10,0,10) => l(110,63,110)
|
b = [100,63,100]; b+[10,0,10] => [110,63,110]
|
||||||
{'a' -> 1} + {'b' -> 2} => {'a' -> 1, 'b' -> 2}
|
{'a' -> 1} + {'b' -> 2} => {'a' -> 1, 'b' -> 2}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
@@ -186,11 +186,12 @@ second operand is not necessary, it won't be evaluated, which means one can use
|
|||||||
case of success returns first positive operand (`||`) or last one (`&&`).
|
case of success returns first positive operand (`||`) or last one (`&&`).
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
true || false => 1
|
true || false => true
|
||||||
null || false => 0
|
null || false => false
|
||||||
null != false || run('kill gnembon') => 1 // gnembon survives
|
false || null => null
|
||||||
null != false && run('kill gnembon') => 0 // when cheats not allowed
|
null != false || run('kill gnembon') // gnembon survives
|
||||||
null != false && run('kill gnembon') => 1 // gnembon dies, cheats allowed
|
null != false && run('kill gnembon') // when cheats not allowed
|
||||||
|
null != false && run('kill gnembon') // gnembon dies, cheats allowed
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `Assignment Operators = <> +=`
|
### `Assignment Operators = <> +=`
|
||||||
@@ -204,11 +205,11 @@ require rewriting of the array anyways.
|
|||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
a = 5 => a == 5
|
a = 5 => a == 5
|
||||||
l(a,b,c) = l(3,4,5) => a==3, b==4, c==5
|
[a,b,c] = [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)
|
[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,b,c,d,e,f] = [range(6)]; [a,b,c] <> [d,e,f]; [a,b,c,d,e,f] => [3,4,5,0,1,2]
|
||||||
a = l(1,2,3); a += 4 => [1,2,3,4]
|
a = [1,2,3]; a += 4 => [1,2,3,4]
|
||||||
a = l(1,2,3,4); a = filter(a,_!=2) => [1,3,4]
|
a = [1,2,3,4]; a = filter(a,_!=2) => [1,3,4]
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `Unary Operators - +`
|
### `Unary Operators - +`
|
||||||
@@ -226,10 +227,10 @@ Require a number, flips the sign. One way to assert it's a number is by crashing
|
|||||||
flips boolean condition of the expression. Equivalent of `bool(expr)==false`
|
flips boolean condition of the expression. Equivalent of `bool(expr)==false`
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
!true => 0
|
!true => false
|
||||||
!false => 1
|
!false => true
|
||||||
!null => 1
|
!null => true
|
||||||
!5 => 0
|
!5 => false
|
||||||
!l() => 1
|
!l() => true
|
||||||
!l(null) => 0
|
!l(null) => false
|
||||||
</pre>
|
</pre>
|
||||||
@@ -14,30 +14,31 @@ as well as minecraft related concepts like `block`, `entity`, `nbt`, `text`.
|
|||||||
|
|
||||||
### `bool(expr)`
|
### `bool(expr)`
|
||||||
|
|
||||||
Returns a boolean context of the expression. Note that there are no true/false values in scarpet. `true` is
|
Returns a boolean context of the expression.
|
||||||
alias of 1, and `false` is 0\. Bool is also interpreting string values as boolean, which is different from other
|
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
|
places where boolean context can be used. This can be used in places where API functions return string values to
|
||||||
represent binary values
|
represent binary values.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
bool(pi) => 1
|
bool(pi) => true
|
||||||
bool(false) => 0
|
bool(false) => false
|
||||||
bool('') => 0
|
bool('') => false
|
||||||
bool(l()) => 0
|
bool(l()) => false
|
||||||
bool(l('')) => 1
|
bool(l('')) => true
|
||||||
bool('foo') => 1
|
bool('foo') => true
|
||||||
bool('false') => 0
|
bool('false') => false
|
||||||
bool('nulL') => 0
|
bool('nulL') => false
|
||||||
if('false',1,0) => 1
|
if('false',1,0) => true
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `number(expr)`
|
### `number(expr)`
|
||||||
|
|
||||||
Returns a numeric context of the expression. Can be used to read numbers from strings
|
Returns a numeric context of the expression. Can be used to read numbers from strings, or other types
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
number(null) => null
|
number(null) => 0
|
||||||
number(false) => 0
|
number(false) => 0
|
||||||
|
number(true) => 1
|
||||||
number('') => null
|
number('') => null
|
||||||
number('3.14') => 3.14
|
number('3.14') => 3.14
|
||||||
number(l()) => 0
|
number(l()) => 0
|
||||||
@@ -65,15 +66,15 @@ Supported types (with `"%<?>"` syntax):
|
|||||||
* `%%`: '%' character
|
* `%%`: '%' character
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
str(null) => null
|
str(null) => 'null'
|
||||||
str(false) => 0
|
str(false) => 'false'
|
||||||
str('') => null
|
str('') => ''
|
||||||
str('3.14') => 3.14
|
str('3.14') => '3.14'
|
||||||
str(l()) => 0
|
str([]) => '[]'
|
||||||
str(l('')) => 1
|
str(['']) => '[]'
|
||||||
str('foo') => null
|
str('foo') => 'foo'
|
||||||
str('3bar') => null
|
str('3bar') => '3bar'
|
||||||
str(2)+str(2) => 22
|
str(2)+str(2) => '22'
|
||||||
str('pi: %.2f',pi) => 'pi: 3.14'
|
str('pi: %.2f',pi) => 'pi: 3.14'
|
||||||
str('player at: %d %d %d',pos(player())) => 'player at: 567, -2423, 124'
|
str('player at: %d %d %d',pos(player())) => 'player at: 567, -2423, 124'
|
||||||
</pre>
|
</pre>
|
||||||
@@ -226,18 +227,24 @@ length('foo') => 3
|
|||||||
### `rand(expr), rand(expr, seed)`
|
### `rand(expr), rand(expr, seed)`
|
||||||
|
|
||||||
returns a random number from `0.0` (inclusive) to `expr` (exclusive). In boolean context (in conditions,
|
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
|
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
|
that `bool(rand(2))` returns true half of the time and `!rand(5)` returns true for 20% (1/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
|
provided, uses a random seed that's shared across all scarpet apps.
|
||||||
same random object. Scarpet keeps track of up to 1024 custom random number generators, so if you exceed this number
|
If seed is provided, each consecutive call to rand() will act like 'next' call to the
|
||||||
(per app), then your random sequence will revert to the beginning.
|
same random object. Scarpet keeps track of up to 65536 custom random number generators (custom seeds, per app),
|
||||||
|
so if you exceed this number, your random sequences will revert to the beginning and start over.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
map(range(10), floor(rand(10))) => [5, 8, 0, 6, 9, 3, 9, 9, 1, 8]
|
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), bool(rand(2))) => [false, false, true, false, false, false, true, false, true, false]
|
||||||
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]
|
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]
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## `reset_seed(seed)`
|
||||||
|
|
||||||
|
Resets the sequence of the randomizer used by `rand` for this seed to its initial state. Returns a boolean value
|
||||||
|
indicating if the given seed has been used or not.
|
||||||
|
|
||||||
### `perlin(x), perlin(x, y), perlin(x, y, z), perlin(x, y, z, seed)`
|
### `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
|
returns a noise value from `0.0` to `1.0` (roughly) for 1, 2 or 3 dimensional coordinate. The default seed it samples
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
`scarpet` provides a number of constants that can be used literally in scripts
|
`scarpet` provides a number of constants that can be used literally in scripts
|
||||||
|
|
||||||
* `null`: nothing, zilch, not even false
|
* `null`: nothing, zilch, not even false
|
||||||
* `true`: pure true, or just 1 (one)
|
* `true`: pure true, can act as `1`
|
||||||
* `false`: false truth, or true falsth, 0 (zero) actually
|
* `false`: false truth, or true falsth, equals to `0`
|
||||||
* `pi`: for the fans of perimeters, its a perimeter of an apple pi of diameter 1\. About 3.14
|
* `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
|
* `euler`: clever guy. Derivative of its exponent is goto 1\. About 2.72
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ reduce
|
|||||||
relu
|
relu
|
||||||
replace
|
replace
|
||||||
replace_first
|
replace_first
|
||||||
|
reset_seed
|
||||||
return
|
return
|
||||||
round
|
round
|
||||||
sec
|
sec
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ brightness
|
|||||||
chunk_tickets
|
chunk_tickets
|
||||||
convert_date
|
convert_date
|
||||||
crafting_remaining_item
|
crafting_remaining_item
|
||||||
|
create_datapack
|
||||||
|
create_explosion
|
||||||
create_marker
|
create_marker
|
||||||
current_dimension
|
current_dimension
|
||||||
custom_dimension
|
|
||||||
day_time
|
day_time
|
||||||
delete_file
|
delete_file
|
||||||
destroy
|
destroy
|
||||||
@@ -28,6 +29,7 @@ display_title
|
|||||||
draw_shape
|
draw_shape
|
||||||
drop_item
|
drop_item
|
||||||
emitted_light
|
emitted_light
|
||||||
|
enable_hidden_dimensions
|
||||||
encode_nbt
|
encode_nbt
|
||||||
entity_area
|
entity_area
|
||||||
entity_event
|
entity_event
|
||||||
@@ -96,6 +98,7 @@ scoreboard
|
|||||||
scoreboard_add
|
scoreboard_add
|
||||||
scoreboard_remove
|
scoreboard_remove
|
||||||
scoreboard_display
|
scoreboard_display
|
||||||
|
scoreboard_property
|
||||||
tag_matches
|
tag_matches
|
||||||
team_add
|
team_add
|
||||||
team_leave
|
team_leave
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ _i
|
|||||||
_x
|
_x
|
||||||
_y
|
_y
|
||||||
_z
|
_z
|
||||||
|
_trace
|
||||||
euler
|
euler
|
||||||
false
|
false
|
||||||
null
|
null
|
||||||
|
|||||||
@@ -42,4 +42,6 @@ __on_player_deals_damage(player, amount, entity) ->
|
|||||||
__on_player_dies(player) ->
|
__on_player_dies(player) ->
|
||||||
__on_player_respawns(player) ->
|
__on_player_respawns(player) ->
|
||||||
__on_player_disconnects(player, reason) ->
|
__on_player_disconnects(player, reason) ->
|
||||||
__on_player_connects(player) ->
|
__on_player_connects(player) ->
|
||||||
|
__on_explosion(pos, power, source, causer, mode, fire) ->
|
||||||
|
__on_explosion_outcome(pos, power, source, causer, mode, fire, blocks, entities) ->
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
<Keywords name="Folders in comment, middle"></Keywords>
|
<Keywords name="Folders in comment, middle"></Keywords>
|
||||||
<Keywords name="Folders in comment, close"></Keywords>
|
<Keywords name="Folders in comment, close"></Keywords>
|
||||||
<Keywords name="Keywords1">
|
<Keywords name="Keywords1">
|
||||||
not fact rand
|
not fact rand reset_seed
|
||||||
sin cos tan asin acos atan atan2 sinh cosh tanh sec csc sech csch cot acot coth asinh acosh atanh
|
sin cos tan asin acos atan atan2 sinh cosh tanh sec csc sech csch cot acot coth asinh acosh atanh
|
||||||
rad deg
|
rad deg
|
||||||
log log10 log1p
|
log log10 log1p
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
|
|
||||||
var undef vars if loop map filter first all c_for for while reduce continue break
|
var undef vars if loop map filter first all c_for for while reduce continue break
|
||||||
|
|
||||||
import call outer type hash_code bool number str length rand sleep time profile_expr lower upper title replace replace_first
|
import call outer type hash_code bool number str length sleep time profile_expr lower upper title replace replace_first
|
||||||
</Keywords>
|
</Keywords>
|
||||||
<Keywords name="Keywords2">
|
<Keywords name="Keywords2">
|
||||||
block pos pos_offset solid air liquid flammable
|
block pos pos_offset solid air liquid flammable
|
||||||
@@ -55,14 +55,16 @@
|
|||||||
set without_updates
|
set without_updates
|
||||||
blocks_movement block_sound material map_colour block_state block_list block_tags block_data poi set_poi
|
blocks_movement block_sound material map_colour block_state block_list block_tags block_data poi set_poi
|
||||||
place_item set_biome biome loaded_status generation_status inhabited_time spawn_potential chunk_tickets
|
place_item set_biome biome loaded_status generation_status inhabited_time spawn_potential chunk_tickets
|
||||||
structure_eligibility structures structure_references set_structure reset_chunk reload_chunk custom_dimension
|
plop harvest destroy create_explosion
|
||||||
|
structure_eligibility structures structure_references set_structure
|
||||||
|
reset_chunk reload_chunk create_datapack enable_hidden_dimensions
|
||||||
add_chunk_ticket
|
add_chunk_ticket
|
||||||
|
|
||||||
convert_date unix_time
|
convert_date unix_time
|
||||||
|
|
||||||
display_title
|
display_title
|
||||||
|
|
||||||
scoreboard scoreboard_add scoreboard_remove scoreboard_display
|
scoreboard scoreboard_add scoreboard_remove scoreboard_display scoreboard_property
|
||||||
|
|
||||||
team_add team_list team_remove team_leave team_property
|
team_add team_list team_remove team_leave team_property
|
||||||
|
|
||||||
@@ -82,7 +84,7 @@
|
|||||||
|
|
||||||
run save tick_time world_time day_time last_tick_times game_tick current_dimension in_dimension view_distance
|
run save tick_time world_time day_time last_tick_times game_tick current_dimension in_dimension view_distance
|
||||||
|
|
||||||
plop harvest destroy schedule nbt draw_shape create_marker remove_all_markers
|
schedule nbt draw_shape create_marker remove_all_markers
|
||||||
|
|
||||||
load_app_data store_app_data read_file write_file delete_file list_files
|
load_app_data store_app_data read_file write_file delete_file list_files
|
||||||
escape_nbt parse_nbt encode_nbt tag_matches statistic
|
escape_nbt parse_nbt encode_nbt tag_matches statistic
|
||||||
@@ -91,7 +93,7 @@
|
|||||||
</Keywords>
|
</Keywords>
|
||||||
<Keywords name="Keywords3">euler pi null true false</Keywords>
|
<Keywords name="Keywords3">euler pi null true false</Keywords>
|
||||||
<Keywords name="Keywords4">x y z p</Keywords>
|
<Keywords name="Keywords4">x y z p</Keywords>
|
||||||
<Keywords name="Keywords5">_ _i _a _x _y _z</Keywords>
|
<Keywords name="Keywords5">_ _i _a _x _y _z _trace</Keywords>
|
||||||
<Keywords name="Keywords6">global_</Keywords>
|
<Keywords name="Keywords6">global_</Keywords>
|
||||||
<Keywords name="Keywords7">
|
<Keywords name="Keywords7">
|
||||||
__command
|
__command
|
||||||
@@ -139,6 +141,8 @@
|
|||||||
__on_player_respawns
|
__on_player_respawns
|
||||||
__on_player_disconnects
|
__on_player_disconnects
|
||||||
__on_player_connects
|
__on_player_connects
|
||||||
|
__on_explosion
|
||||||
|
__on_explosion_outcome
|
||||||
</Keywords>
|
</Keywords>
|
||||||
<Keywords name="Keywords8">$</Keywords>
|
<Keywords name="Keywords8">$</Keywords>
|
||||||
<Keywords name="Delimiters">00' 01\ 02' 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23</Keywords>
|
<Keywords name="Delimiters">00' 01\ 02' 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23</Keywords>
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ org.gradle.jvmargs=-Xmx1G
|
|||||||
fabric_version=0.29.4+1.16
|
fabric_version=0.29.4+1.16
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version = 1.4.27
|
mod_version = 1.4.31
|
||||||
maven_group = carpet
|
maven_group = carpet
|
||||||
archives_base_name = fabric-carpet
|
archives_base_name = fabric-carpet
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ import static carpet.settings.RuleCategory.CLIENT;
|
|||||||
@SuppressWarnings("CanBeFinal")
|
@SuppressWarnings("CanBeFinal")
|
||||||
public class CarpetSettings
|
public class CarpetSettings
|
||||||
{
|
{
|
||||||
public static final String carpetVersion = "1.4.27+v210217";
|
public static final String carpetVersion = "1.4.31+v210407";
|
||||||
public static final Logger LOG = LogManager.getLogger("carpet");
|
public static final Logger LOG = LogManager.getLogger("carpet");
|
||||||
public static boolean skipGenerationChecks = false;
|
public static boolean skipGenerationChecks = false;
|
||||||
public static boolean impendingFillSkipUpdates = false;
|
public static boolean impendingFillSkipUpdates = false;
|
||||||
@@ -413,20 +413,26 @@ public class CarpetSettings
|
|||||||
|
|
||||||
private static class ModulePermissionLevel extends Validator<String> {
|
private static class ModulePermissionLevel extends Validator<String> {
|
||||||
@Override public String validate(ServerCommandSource source, ParsedRule<String> currentRule, String newValue, String string) {
|
@Override public String validate(ServerCommandSource source, ParsedRule<String> currentRule, String newValue, String string) {
|
||||||
CarpetSettings.runPermissionLevel = SettingsManager.getCommandLevel(newValue);
|
int permissionLevel = SettingsManager.getCommandLevel(newValue);
|
||||||
|
if (source != null && !source.hasPermissionLevel(permissionLevel))
|
||||||
|
return null;
|
||||||
|
CarpetSettings.runPermissionLevel = permissionLevel;
|
||||||
|
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
|
||||||
return newValue;
|
return newValue;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public String description() { return "Also controls permission level of commands executed via `run()`";}
|
public String description() { return "When changing the rule, you must at least have the permission level you are trying to give it";}
|
||||||
}
|
}
|
||||||
@Rule(
|
@Rule(
|
||||||
desc = "Enables restrictions for arbitrary code execution with scarpet",
|
desc = "Enables restrictions for arbitrary code execution with scarpet",
|
||||||
extra = {
|
extra = {
|
||||||
"Users that don't have this permission level",
|
"Users that don't have this permission level",
|
||||||
"won't be able to load apps or /script run"
|
"won't be able to load apps or /script run.",
|
||||||
|
"It is also the permission level apps will",
|
||||||
|
"have when running commands with run()"
|
||||||
},
|
},
|
||||||
category = {COMMAND, SCARPET},
|
category = {SCARPET},
|
||||||
validate = ModulePermissionLevel.class
|
validate = {Validator._COMMAND_LEVEL_VALIDATOR.class, ModulePermissionLevel.class}
|
||||||
)
|
)
|
||||||
public static String commandScriptACE = "ops";
|
public static String commandScriptACE = "ops";
|
||||||
|
|
||||||
@@ -532,6 +538,14 @@ public class CarpetSettings
|
|||||||
)
|
)
|
||||||
public static int maxEntityCollisions = 0;
|
public static int maxEntityCollisions = 0;
|
||||||
|
|
||||||
|
@Rule(
|
||||||
|
desc = "Customizable server list ping (Multiplayer menu) playerlist sample limit",
|
||||||
|
options = {"0", "12", "20", "40"},
|
||||||
|
category = CREATIVE,
|
||||||
|
strict = false,
|
||||||
|
validate = Validator.NONNEGATIVE_NUMBER.class
|
||||||
|
)
|
||||||
|
public static int pingPlayerListLimit = 12;
|
||||||
/*
|
/*
|
||||||
|
|
||||||
@Rule(
|
@Rule(
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class PerimeterInfoCommand
|
|||||||
});
|
});
|
||||||
if (!(baseEntity instanceof MobEntity))
|
if (!(baseEntity instanceof MobEntity))
|
||||||
{
|
{
|
||||||
Messenger.m(source, "r Failed to spawn test entity");
|
Messenger.m(source, "r /perimeterinfo requires a mob entity to test agains.");
|
||||||
if (baseEntity != null) baseEntity.remove();
|
if (baseEntity != null) baseEntity.remove();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import net.minecraft.server.command.ServerCommandSource;
|
|||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.util.math.Vec2f;
|
import net.minecraft.util.math.Vec2f;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
@@ -271,7 +272,13 @@ public class PlayerCommand
|
|||||||
Messenger.m(context.getSource(), "rb Player name: "+playerName+" is too long");
|
Messenger.m(context.getSource(), "rb Player name: "+playerName+" is too long");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
MinecraftServer server = source.getMinecraftServer();
|
MinecraftServer server = source.getMinecraftServer();
|
||||||
|
if (!World.isInBuildLimit(new BlockPos(pos.x, pos.y, pos.z)))
|
||||||
|
{
|
||||||
|
Messenger.m(context.getSource(), "rb Player "+playerName+" cannot be placed outside of the world");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
PlayerEntity player = EntityPlayerMPFake.createFake(playerName, server, pos.x, pos.y, pos.z, facing.y, facing.x, dimType, mode);
|
PlayerEntity player = EntityPlayerMPFake.createFake(playerName, server, pos.x, pos.y, pos.z, facing.y, facing.x, dimType, mode);
|
||||||
if (player == null)
|
if (player == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import carpet.script.exception.CarpetExpressionException;
|
|||||||
import carpet.script.value.FunctionValue;
|
import carpet.script.value.FunctionValue;
|
||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
import carpet.settings.SettingsManager;
|
import carpet.settings.SettingsManager;
|
||||||
|
import carpet.utils.CarpetProfiler;
|
||||||
import carpet.utils.Messenger;
|
import carpet.utils.Messenger;
|
||||||
import com.mojang.brigadier.CommandDispatcher;
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
@@ -396,6 +397,7 @@ public class ScriptCommand
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet run", CarpetProfiler.TYPE.GENERAL);
|
||||||
host.setChatErrorSnooper(source);
|
host.setChatErrorSnooper(source);
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
Value result = call.get();
|
Value result = call.get();
|
||||||
@@ -412,7 +414,9 @@ public class ScriptCommand
|
|||||||
metric = "s";
|
metric = "s";
|
||||||
}
|
}
|
||||||
Messenger.m(source, "wi = ", "wb "+result.getString(), "gi ("+time+metric+")");
|
Messenger.m(source, "wi = ", "wb "+result.getString(), "gi ("+time+metric+")");
|
||||||
return (int)result.readInteger();
|
int intres = (int)result.readInteger();
|
||||||
|
CarpetProfiler.end_current_section(currentSection);
|
||||||
|
return intres;
|
||||||
}
|
}
|
||||||
catch (CarpetExpressionException e)
|
catch (CarpetExpressionException e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package carpet.fakes;
|
package carpet.fakes;
|
||||||
|
|
||||||
|
import net.minecraft.resource.ServerResourceManager;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.util.registry.RegistryKey;
|
import net.minecraft.util.registry.RegistryKey;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
@@ -13,4 +14,5 @@ public interface MinecraftServerInterface
|
|||||||
void forceTick(BooleanSupplier sup);
|
void forceTick(BooleanSupplier sup);
|
||||||
LevelStorage.Session getCMSession();
|
LevelStorage.Session getCMSession();
|
||||||
Map<RegistryKey<World>, ServerWorld> getCMWorlds();
|
Map<RegistryKey<World>, ServerWorld> getCMWorlds();
|
||||||
|
ServerResourceManager getResourceManager();
|
||||||
}
|
}
|
||||||
@@ -10,13 +10,10 @@ import java.util.List;
|
|||||||
import carpet.logging.logHelpers.ExplosionLogHelper;
|
import carpet.logging.logHelpers.ExplosionLogHelper;
|
||||||
import carpet.mixins.ExplosionAccessor;
|
import carpet.mixins.ExplosionAccessor;
|
||||||
import carpet.CarpetSettings;
|
import carpet.CarpetSettings;
|
||||||
import carpet.mixins.ExplosionMixin;
|
|
||||||
import carpet.utils.Messenger;
|
import carpet.utils.Messenger;
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.Blocks;
|
import net.minecraft.block.Blocks;
|
||||||
@@ -48,6 +45,8 @@ import net.minecraft.util.math.Vec3d;
|
|||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraft.world.chunk.Chunk;
|
import net.minecraft.world.chunk.Chunk;
|
||||||
|
|
||||||
|
import static carpet.script.CarpetEventServer.Event.EXPLOSION_OUTCOME;
|
||||||
|
|
||||||
public class OptimizedExplosion
|
public class OptimizedExplosion
|
||||||
{
|
{
|
||||||
private static List<Entity> entitylist;
|
private static List<Entity> entitylist;
|
||||||
@@ -68,9 +67,14 @@ public class OptimizedExplosion
|
|||||||
private static ArrayList<Float> chances = new ArrayList<>();
|
private static ArrayList<Float> chances = new ArrayList<>();
|
||||||
private static BlockPos blastChanceLocation;
|
private static BlockPos blastChanceLocation;
|
||||||
|
|
||||||
|
// Creating entity list for scarpet event
|
||||||
|
private static List<Entity> entityList = new ArrayList<>();
|
||||||
|
|
||||||
public static void doExplosionA(Explosion e, ExplosionLogHelper eLogger) {
|
public static void doExplosionA(Explosion e, ExplosionLogHelper eLogger) {
|
||||||
ExplosionAccessor eAccess = (ExplosionAccessor) e;
|
ExplosionAccessor eAccess = (ExplosionAccessor) e;
|
||||||
|
|
||||||
|
entityList.clear();
|
||||||
|
boolean eventNeeded = EXPLOSION_OUTCOME.isNeeded() && !eAccess.getWorld().isClient();
|
||||||
blastCalc(e);
|
blastCalc(e);
|
||||||
|
|
||||||
if (!CarpetSettings.explosionNoBlockDamage) {
|
if (!CarpetSettings.explosionNoBlockDamage) {
|
||||||
@@ -107,20 +111,22 @@ public class OptimizedExplosion
|
|||||||
|
|
||||||
explosionSound++;
|
explosionSound++;
|
||||||
|
|
||||||
|
Entity explodingEntity = eAccess.getEntity();
|
||||||
for (int k2 = 0; k2 < entitylist.size(); ++k2) {
|
for (int k2 = 0; k2 < entitylist.size(); ++k2) {
|
||||||
Entity entity = entitylist.get(k2);
|
Entity entity = entitylist.get(k2);
|
||||||
|
|
||||||
if (entity == eAccess.getEntity()) {
|
|
||||||
|
if (entity == explodingEntity) {
|
||||||
// entitylist.remove(k2);
|
// entitylist.remove(k2);
|
||||||
removeFast(entitylist, k2);
|
removeFast(entitylist, k2);
|
||||||
k2--;
|
k2--;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity instanceof TntEntity &&
|
if (entity instanceof TntEntity && explodingEntity != null &&
|
||||||
entity.getX() == eAccess.getEntity().getX() &&
|
entity.getX() == explodingEntity.getX() &&
|
||||||
entity.getY() == eAccess.getEntity().getY() &&
|
entity.getY() == explodingEntity.getY() &&
|
||||||
entity.getZ() == eAccess.getEntity().getZ()) {
|
entity.getZ() == explodingEntity.getZ()) {
|
||||||
if (eLogger != null) {
|
if (eLogger != null) {
|
||||||
eLogger.onEntityImpacted(entity, new Vec3d(0,-0.9923437498509884d, 0));
|
eLogger.onEntityImpacted(entity, new Vec3d(0,-0.9923437498509884d, 0));
|
||||||
}
|
}
|
||||||
@@ -154,6 +160,11 @@ public class OptimizedExplosion
|
|||||||
densityCache.put(pair, density);
|
densityCache.put(pair, density);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If it is needed, it saves the entity
|
||||||
|
if (eventNeeded) {
|
||||||
|
entityList.add(entity);
|
||||||
|
}
|
||||||
|
|
||||||
double d10 = (1.0D - d12) * density;
|
double d10 = (1.0D - d12) * density;
|
||||||
entity.damage(e.getDamageSource(),
|
entity.damage(e.getDamageSource(),
|
||||||
(float) ((int) ((d10 * d10 + d10) / 2.0D * 7.0D * (double) f3 + 1.0D)));
|
(float) ((int) ((d10 * d10 + d10) / 2.0D * 7.0D * (double) f3 + 1.0D)));
|
||||||
@@ -193,6 +204,11 @@ public class OptimizedExplosion
|
|||||||
double posY = eAccess.getY();
|
double posY = eAccess.getY();
|
||||||
double posZ = eAccess.getZ();
|
double posZ = eAccess.getZ();
|
||||||
|
|
||||||
|
// If it is needed, calls scarpet event
|
||||||
|
if (EXPLOSION_OUTCOME.isNeeded() && !world.isClient()) {
|
||||||
|
EXPLOSION_OUTCOME.onExplosion((ServerWorld) world, eAccess.getEntity(), e::getCausingEntity, eAccess.getX(), eAccess.getY(), eAccess.getZ(), eAccess.getPower(), eAccess.isCreateFire(), e.getAffectedBlocks(), entityList, eAccess.getDestructionType());
|
||||||
|
}
|
||||||
|
|
||||||
boolean damagesTerrain = eAccess.getDestructionType() != Explosion.DestructionType.NONE;
|
boolean damagesTerrain = eAccess.getDestructionType() != Explosion.DestructionType.NONE;
|
||||||
|
|
||||||
// explosionSound incremented till disabling the explosion particles and sound
|
// explosionSound incremented till disabling the explosion particles and sound
|
||||||
@@ -596,4 +612,4 @@ public class OptimizedExplosion
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,6 +74,7 @@ public class TickSpeed
|
|||||||
if (player_active_timeout < PLAYER_GRACE)
|
if (player_active_timeout < PLAYER_GRACE)
|
||||||
{
|
{
|
||||||
player_active_timeout = PLAYER_GRACE;
|
player_active_timeout = PLAYER_GRACE;
|
||||||
|
ServerNetworkHandler.updateTickPlayerActiveTimeoutToConnectedPlayers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package carpet.logging;
|
package carpet.logging;
|
||||||
|
|
||||||
|
import carpet.CarpetServer;
|
||||||
import carpet.helpers.HopperCounter;
|
import carpet.helpers.HopperCounter;
|
||||||
import carpet.helpers.TickSpeed;
|
import carpet.helpers.TickSpeed;
|
||||||
import carpet.logging.logHelpers.PacketCounter;
|
import carpet.logging.logHelpers.PacketCounter;
|
||||||
@@ -62,7 +63,7 @@ public class HUDController
|
|||||||
|
|
||||||
public static void update_hud(MinecraftServer server)
|
public static void update_hud(MinecraftServer server)
|
||||||
{
|
{
|
||||||
if(server.getTicks() % 20 != 0)
|
if(server.getTicks() % 20 != 0 || CarpetServer.minecraft_server == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
player_huds.clear();
|
player_huds.clear();
|
||||||
|
|||||||
@@ -44,9 +44,11 @@ public abstract class CommandManagerMixin
|
|||||||
|
|
||||||
@SuppressWarnings("UnresolvedMixinReference")
|
@SuppressWarnings("UnresolvedMixinReference")
|
||||||
@Redirect(method = "execute", at = @At(
|
@Redirect(method = "execute", at = @At(
|
||||||
value = "INVOKE",
|
value = "INVOKE",
|
||||||
target = "Lorg/apache/logging/log4j/Logger;isDebugEnabled()Z"
|
target = "Lorg/apache/logging/log4j/Logger;isDebugEnabled()Z"
|
||||||
))
|
),
|
||||||
|
require = 0
|
||||||
|
)
|
||||||
private boolean doesOutputCommandStackTrace(Logger logger)
|
private boolean doesOutputCommandStackTrace(Logger logger)
|
||||||
{
|
{
|
||||||
if (CarpetSettings.superSecretSetting)
|
if (CarpetSettings.superSecretSetting)
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package carpet.mixins;
|
||||||
|
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.damage.DamageSource;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraft.world.explosion.Explosion;
|
||||||
|
import net.minecraft.world.explosion.ExplosionBehavior;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static carpet.script.CarpetEventServer.Event.EXPLOSION_OUTCOME;
|
||||||
|
|
||||||
|
@Mixin(value = Explosion.class, priority = 990)
|
||||||
|
public abstract class Explosion_scarpetEventMixin
|
||||||
|
{
|
||||||
|
@Shadow @Final private World world;
|
||||||
|
@Shadow @Final private double x;
|
||||||
|
@Shadow @Final private double y;
|
||||||
|
@Shadow @Final private double z;
|
||||||
|
@Shadow @Final private float power;
|
||||||
|
@Shadow @Final private boolean createFire;
|
||||||
|
@Shadow @Final private List<BlockPos> affectedBlocks;
|
||||||
|
@Shadow @Final private Explosion.DestructionType destructionType;
|
||||||
|
@Shadow @Final private @Nullable Entity entity;
|
||||||
|
|
||||||
|
@Shadow /*@Nullable*/ public abstract /*@Nullable*/ LivingEntity getCausingEntity();
|
||||||
|
|
||||||
|
private List<Entity> affectedEntities;
|
||||||
|
|
||||||
|
@Inject(method = "<init>(Lnet/minecraft/world/World;Lnet/minecraft/entity/Entity;Lnet/minecraft/entity/damage/DamageSource;Lnet/minecraft/world/explosion/ExplosionBehavior;DDDFZLnet/minecraft/world/explosion/Explosion$DestructionType;)V",
|
||||||
|
at = @At(value = "RETURN"))
|
||||||
|
private void onExplosionCreated(World world, Entity entity, DamageSource damageSource, ExplosionBehavior explosionBehavior, double x, double y, double z, float power, boolean createFire, Explosion.DestructionType destructionType, CallbackInfo ci)
|
||||||
|
{
|
||||||
|
if (EXPLOSION_OUTCOME.isNeeded() && !world.isClient())
|
||||||
|
{
|
||||||
|
affectedEntities = new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "collectBlocksAndDamageEntities", locals= LocalCapture.CAPTURE_FAILHARD, at=@At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;damage(Lnet/minecraft/entity/damage/DamageSource;F)Z"))
|
||||||
|
private void onExplosion(CallbackInfo ci, Set<BlockPos> s1, float f1, int i1, int i2, int i3, int i4, int i5, int i6, List<Entity> l1, Vec3d v1, int i7, Entity entity)
|
||||||
|
{
|
||||||
|
if (affectedEntities != null)
|
||||||
|
{
|
||||||
|
affectedEntities.add(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "affectWorld", at = @At("HEAD"))
|
||||||
|
private void onExplosion(boolean spawnParticles, CallbackInfo ci)
|
||||||
|
{
|
||||||
|
if (EXPLOSION_OUTCOME.isNeeded() && !world.isClient())
|
||||||
|
{
|
||||||
|
EXPLOSION_OUTCOME.onExplosion((ServerWorld) world, entity, this::getCausingEntity, x, y, z, power, createFire, affectedBlocks, affectedEntities, destructionType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package carpet.mixins;
|
|||||||
|
|
||||||
import carpet.CarpetServer;
|
import carpet.CarpetServer;
|
||||||
import carpet.CarpetSettings;
|
import carpet.CarpetSettings;
|
||||||
|
import carpet.utils.CarpetProfiler;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.WorldGenerationProgressListener;
|
import net.minecraft.server.WorldGenerationProgressListener;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
@@ -26,7 +27,9 @@ public abstract class MinecraftServer_coreMixin
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
private void onTick(BooleanSupplier booleanSupplier_1, CallbackInfo ci) {
|
private void onTick(BooleanSupplier booleanSupplier_1, CallbackInfo ci) {
|
||||||
|
CarpetProfiler.ProfilerToken token = CarpetProfiler.start_section(null, "Carpet", CarpetProfiler.TYPE.GENERAL);
|
||||||
CarpetServer.tick((MinecraftServer) (Object) this);
|
CarpetServer.tick((MinecraftServer) (Object) this);
|
||||||
|
CarpetProfiler.end_current_section(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "loadWorld", at = @At("HEAD"))
|
@Inject(method = "loadWorld", at = @At("HEAD"))
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package carpet.mixins;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Constant;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||||
|
|
||||||
|
import carpet.CarpetSettings;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
||||||
|
@Mixin(MinecraftServer.class)
|
||||||
|
public abstract class MinecraftServer_pingPlayerSampleLimit
|
||||||
|
{
|
||||||
|
|
||||||
|
@ModifyConstant(method = "tick", constant = @Constant(intValue = 12), require = 0, allow = 1)
|
||||||
|
private int modifyPlayerSampleLimit(int value)
|
||||||
|
{
|
||||||
|
return CarpetSettings.pingPlayerListLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package carpet.mixins;
|
|||||||
|
|
||||||
import carpet.fakes.MinecraftServerInterface;
|
import carpet.fakes.MinecraftServerInterface;
|
||||||
import carpet.helpers.TickSpeed;
|
import carpet.helpers.TickSpeed;
|
||||||
|
import net.minecraft.resource.ServerResourceManager;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.ServerTask;
|
import net.minecraft.server.ServerTask;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
@@ -44,6 +45,8 @@ public abstract class MinecraftServer_scarpetMixin extends ReentrantThreadExecut
|
|||||||
|
|
||||||
@Shadow @Final private Map<RegistryKey<World>, ServerWorld> worlds;
|
@Shadow @Final private Map<RegistryKey<World>, ServerWorld> worlds;
|
||||||
|
|
||||||
|
@Shadow private ServerResourceManager serverResourceManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void forceTick(BooleanSupplier isAhead)
|
public void forceTick(BooleanSupplier isAhead)
|
||||||
{
|
{
|
||||||
@@ -58,6 +61,11 @@ public abstract class MinecraftServer_scarpetMixin extends ReentrantThreadExecut
|
|||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerResourceManager getResourceManager() {
|
||||||
|
return serverResourceManager;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<RegistryKey<World>, ServerWorld> getCMWorlds()
|
public Map<RegistryKey<World>, ServerWorld> getCMWorlds()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -208,7 +208,8 @@ public abstract class MinecraftServer_tickspeedMixin extends ReentrantThreadExec
|
|||||||
{
|
{
|
||||||
if (CarpetProfiler.tick_health_requested != 0L)
|
if (CarpetProfiler.tick_health_requested != 0L)
|
||||||
{
|
{
|
||||||
CarpetProfiler.end_tick_profiling((MinecraftServer) (Object)this, currentSection);
|
CarpetProfiler.end_current_section(currentSection);
|
||||||
|
CarpetProfiler.end_tick_profiling((MinecraftServer) (Object)this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,10 +25,4 @@ public class PlayerManager_coreMixin
|
|||||||
CarpetServer.onPlayerLoggedIn(player);
|
CarpetServer.onPlayerLoggedIn(player);
|
||||||
PLAYER_CONNECTS.onPlayerEvent(player);
|
PLAYER_CONNECTS.onPlayerEvent(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "onDataPacksReloaded", at = @At("HEAD"))
|
|
||||||
private void onResourcesRefreshedReloadCommand(CallbackInfo ci)
|
|
||||||
{
|
|
||||||
CarpetServer.onReload(server);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
package carpet.mixins;
|
package carpet.mixins;
|
||||||
|
|
||||||
|
import carpet.CarpetServer;
|
||||||
import carpet.fakes.ServerPlayerEntityInterface;
|
import carpet.fakes.ServerPlayerEntityInterface;
|
||||||
import net.minecraft.server.PlayerManager;
|
import net.minecraft.server.PlayerManager;
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
import static carpet.script.CarpetEventServer.Event.PLAYER_RESPAWNS;
|
import static carpet.script.CarpetEventServer.Event.PLAYER_RESPAWNS;
|
||||||
@@ -25,4 +27,9 @@ public class PlayerManager_scarpetEventsMixin
|
|||||||
((ServerPlayerEntityInterface)player).invalidateEntityObjectReference();
|
((ServerPlayerEntityInterface)player).invalidateEntityObjectReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject(method = "onDataPacksReloaded", at = @At("HEAD"))
|
||||||
|
private void reloadCommands(CallbackInfo ci)
|
||||||
|
{
|
||||||
|
CarpetServer.scriptServer.reAddCommands();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package carpet.mixins;
|
||||||
|
|
||||||
|
import carpet.CarpetServer;
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import net.minecraft.server.command.ReloadCommand;
|
||||||
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
@Mixin(ReloadCommand.class)
|
||||||
|
public class ReloadCommand_reloadAppsMixin {
|
||||||
|
//method_13530(Lcom/mojang/brigadier/context/CommandContext;)I
|
||||||
|
// internal of register.
|
||||||
|
@SuppressWarnings("UnresolvedMixinReference")
|
||||||
|
@Inject(method = "method_13530", at = @At("TAIL"))
|
||||||
|
private static void onReload(CommandContext<ServerCommandSource> context, CallbackInfoReturnable<Integer> cir)
|
||||||
|
{
|
||||||
|
// can't fetch here the reference to the server
|
||||||
|
CarpetServer.onReload(context.getSource().getMinecraftServer());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,20 +5,26 @@ import carpet.fakes.ServerWorldInterface;
|
|||||||
import carpet.script.CarpetEventServer;
|
import carpet.script.CarpetEventServer;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.LightningEntity;
|
import net.minecraft.entity.LightningEntity;
|
||||||
|
import net.minecraft.entity.damage.DamageSource;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.ChunkPos;
|
import net.minecraft.util.math.ChunkPos;
|
||||||
import net.minecraft.util.profiler.Profiler;
|
import net.minecraft.util.profiler.Profiler;
|
||||||
import net.minecraft.world.chunk.WorldChunk;
|
import net.minecraft.world.chunk.WorldChunk;
|
||||||
|
import net.minecraft.world.explosion.Explosion;
|
||||||
|
import net.minecraft.world.explosion.ExplosionBehavior;
|
||||||
import net.minecraft.world.level.ServerWorldProperties;
|
import net.minecraft.world.level.ServerWorldProperties;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||||
|
|
||||||
import static carpet.script.CarpetEventServer.Event.ENTITY_LOAD;
|
import static carpet.script.CarpetEventServer.Event.ENTITY_LOAD;
|
||||||
|
import static carpet.script.CarpetEventServer.Event.EXPLOSION;
|
||||||
import static carpet.script.CarpetEventServer.Event.LIGHTNING;
|
import static carpet.script.CarpetEventServer.Event.LIGHTNING;
|
||||||
|
|
||||||
@Mixin(ServerWorld.class)
|
@Mixin(ServerWorld.class)
|
||||||
@@ -57,6 +63,14 @@ public class ServerWorld_scarpetMixin implements ServerWorldInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject(method = "createExplosion", at = @At("HEAD"))
|
||||||
|
private void handleExplosion(/*@Nullable*/ Entity entity, /*@Nullable*/ DamageSource damageSource, /*@Nullable*/ ExplosionBehavior explosionBehavior, double d, double e, double f, float g, boolean bl, Explosion.DestructionType destructionType, CallbackInfoReturnable<Explosion> cir)
|
||||||
|
{
|
||||||
|
if (EXPLOSION.isNeeded())
|
||||||
|
EXPLOSION.onExplosion((ServerWorld) (Object)this, entity, null, d, e, f, g, bl, null, null, destructionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Final
|
||||||
@Shadow
|
@Shadow
|
||||||
private ServerWorldProperties worldProperties;
|
private ServerWorldProperties worldProperties;
|
||||||
public ServerWorldProperties getWorldPropertiesCM(){
|
public ServerWorldProperties getWorldPropertiesCM(){
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import org.spongepowered.asm.mixin.Mixin;
|
|||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
@Mixin(targets="net.minecraft.client.sound.SoundEngine$SourceSetImpl")
|
@Mixin(targets="net.minecraft.client.sound.SoundEngine$SourceSetImpl", priority = 69420)
|
||||||
public class SoundEngineInt_cleanLogsMixin
|
public class SoundEngineInt_cleanLogsMixin
|
||||||
{
|
{
|
||||||
// createSource // method_19666, remap = false,
|
// createSource // method_19666, remap = false,
|
||||||
@Redirect(method = "createSource()Lnet/minecraft/client/sound/Source;", at = @At(remap = false,
|
@Redirect(method = "createSource()Lnet/minecraft/client/sound/Source;", require = 0, at = @At(remap = false,
|
||||||
value="INVOKE",
|
value="INVOKE",
|
||||||
target = "Lorg/apache/logging/log4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V"
|
target = "Lorg/apache/logging/log4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V"
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import org.spongepowered.asm.mixin.Mixin;
|
|||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
@Mixin(SoundSystem.class)
|
@Mixin(value = SoundSystem.class, priority = 69420)
|
||||||
public class SoundSystem_cleanLogsMixin
|
public class SoundSystem_cleanLogsMixin
|
||||||
{
|
{
|
||||||
@Redirect(method = "play(Lnet/minecraft/client/sound/SoundInstance;)V", at = @At( remap = false,
|
@Redirect(method = "play(Lnet/minecraft/client/sound/SoundInstance;)V", require = 0, at = @At( remap = false,
|
||||||
value = "INVOKE",
|
value = "INVOKE",
|
||||||
target = "Lorg/apache/logging/log4j/Logger;warn(Ljava/lang/String;)V"
|
target = "Lorg/apache/logging/log4j/Logger;warn(Ljava/lang/String;)V"
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package carpet.mixins;
|
||||||
|
|
||||||
|
import carpet.CarpetSettings;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.block.ShapeContext;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.item.ItemPlacementContext;
|
||||||
|
import net.minecraft.item.WallStandingBlockItem;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.shape.VoxelShape;
|
||||||
|
import net.minecraft.world.WorldView;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
@Mixin(WallStandingBlockItem.class)
|
||||||
|
public class WallStandingBlockItem_creativeNoClipMixin
|
||||||
|
{
|
||||||
|
@Redirect(method = "getPlacementState", at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/world/WorldView;canPlace(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/ShapeContext;)Z"
|
||||||
|
))
|
||||||
|
private boolean canCreativePlayerPlace(
|
||||||
|
WorldView worldView, BlockState state, BlockPos pos, ShapeContext context,
|
||||||
|
ItemPlacementContext itemcontext
|
||||||
|
)
|
||||||
|
{
|
||||||
|
PlayerEntity player = itemcontext.getPlayer();
|
||||||
|
if (CarpetSettings.creativeNoClip && player != null && player.isCreative() && player.abilities.flying)
|
||||||
|
{
|
||||||
|
// copy from canPlace
|
||||||
|
VoxelShape voxelShape = state.getCollisionShape(worldView, pos, context);
|
||||||
|
return voxelShape.isEmpty() || worldView.intersectsEntities(player, voxelShape.offset(pos.getX(), pos.getY(), pos.getZ()));
|
||||||
|
|
||||||
|
}
|
||||||
|
return worldView.canPlace(state, pos, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,9 @@ import net.minecraft.entity.EntityType;
|
|||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.util.profiler.Profiler;
|
import net.minecraft.util.profiler.Profiler;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
@@ -24,6 +26,7 @@ import java.util.function.Consumer;
|
|||||||
@Mixin(World.class)
|
@Mixin(World.class)
|
||||||
public abstract class World_tickMixin implements WorldInterface
|
public abstract class World_tickMixin implements WorldInterface
|
||||||
{
|
{
|
||||||
|
@Shadow @Final public boolean isClient;
|
||||||
CarpetProfiler.ProfilerToken currentSection;
|
CarpetProfiler.ProfilerToken currentSection;
|
||||||
CarpetProfiler.ProfilerToken entitySection;
|
CarpetProfiler.ProfilerToken entitySection;
|
||||||
|
|
||||||
@@ -79,7 +82,7 @@ public abstract class World_tickMixin implements WorldInterface
|
|||||||
@Inject(method = "tickEntity", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "tickEntity", at = @At("HEAD"), cancellable = true)
|
||||||
private void startEntity(Consumer<Entity> consumer_1, Entity e, CallbackInfo ci)
|
private void startEntity(Consumer<Entity> consumer_1, Entity e, CallbackInfo ci)
|
||||||
{
|
{
|
||||||
if (!TickSpeed.process_entities && !(e instanceof PlayerEntity))
|
if (!(TickSpeed.process_entities || (e instanceof PlayerEntity) || (TickSpeed.is_superHot && isClient && e.getPrimaryPassenger() instanceof PlayerEntity)))
|
||||||
ci.cancel();
|
ci.cancel();
|
||||||
entitySection = CarpetProfiler.start_entity_section((World) (Object) this, e, CarpetProfiler.TYPE.ENTITY);
|
entitySection = CarpetProfiler.start_entity_section((World) (Object) this, e, CarpetProfiler.TYPE.ENTITY);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ public class ClientNetworkHandler
|
|||||||
TickSpeed.is_superHot = ((ByteTag) t).equals(ByteTag.ONE);
|
TickSpeed.is_superHot = ((ByteTag) t).equals(ByteTag.ONE);
|
||||||
});
|
});
|
||||||
dataHandlers.put("TickPlayerActiveTimeout", (p, t) -> TickSpeed.player_active_timeout = ((AbstractNumberTag)t).getInt());
|
dataHandlers.put("TickPlayerActiveTimeout", (p, t) -> TickSpeed.player_active_timeout = ((AbstractNumberTag)t).getInt());
|
||||||
dataHandlers.put("scShape", (p, t) -> { // deprecated
|
dataHandlers.put("scShape", (p, t) -> { // deprecated // and unused // should remove for 1.17
|
||||||
if (CarpetClient.shapes != null)
|
if (CarpetClient.shapes != null)
|
||||||
CarpetClient.shapes.addShape((CompoundTag)t);
|
CarpetClient.shapes.addShape((CompoundTag)t);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package carpet.patches;
|
|||||||
|
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
import net.minecraft.block.entity.SkullBlockEntity;
|
import net.minecraft.block.entity.SkullBlockEntity;
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket;
|
import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket;
|
||||||
import net.minecraft.network.packet.s2c.play.EntitySetHeadYawS2CPacket;
|
import net.minecraft.network.packet.s2c.play.EntitySetHeadYawS2CPacket;
|
||||||
@@ -102,6 +104,7 @@ public class EntityPlayerMPFake extends ServerPlayerEntity
|
|||||||
|
|
||||||
public void kill(Text reason)
|
public void kill(Text reason)
|
||||||
{
|
{
|
||||||
|
shakeOff();
|
||||||
this.server.send(new ServerTask(this.server.getTicks(), () -> {
|
this.server.send(new ServerTask(this.server.getTicks(), () -> {
|
||||||
this.networkHandler.onDisconnected(reason);
|
this.networkHandler.onDisconnected(reason);
|
||||||
}));
|
}));
|
||||||
@@ -120,9 +123,19 @@ public class EntityPlayerMPFake extends ServerPlayerEntity
|
|||||||
this.playerTick();
|
this.playerTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void shakeOff()
|
||||||
|
{
|
||||||
|
if (getVehicle() instanceof PlayerEntity) stopRiding();
|
||||||
|
for (Entity passenger : getPassengersDeep())
|
||||||
|
{
|
||||||
|
if (passenger instanceof PlayerEntity) passenger.stopRiding();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDeath(DamageSource cause)
|
public void onDeath(DamageSource cause)
|
||||||
{
|
{
|
||||||
|
shakeOff();
|
||||||
super.onDeath(cause);
|
super.onDeath(cause);
|
||||||
setHealth(20);
|
setHealth(20);
|
||||||
this.hungerManager = new HungerManager();
|
this.hungerManager = new HungerManager();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import carpet.CarpetSettings;
|
|||||||
import carpet.helpers.TickSpeed;
|
import carpet.helpers.TickSpeed;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
import carpet.script.value.BlockValue;
|
import carpet.script.value.BlockValue;
|
||||||
|
import carpet.script.value.BooleanValue;
|
||||||
import carpet.script.value.EntityValue;
|
import carpet.script.value.EntityValue;
|
||||||
import carpet.script.value.FunctionValue;
|
import carpet.script.value.FunctionValue;
|
||||||
import carpet.script.value.ListValue;
|
import carpet.script.value.ListValue;
|
||||||
@@ -14,12 +15,16 @@ import carpet.script.value.StringValue;
|
|||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
import carpet.script.value.ValueConversions;
|
import carpet.script.value.ValueConversions;
|
||||||
import carpet.settings.ParsedRule;
|
import carpet.settings.ParsedRule;
|
||||||
|
import carpet.utils.CarpetProfiler;
|
||||||
import carpet.utils.Messenger;
|
import carpet.utils.Messenger;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.TntEntity;
|
||||||
import net.minecraft.entity.damage.DamageSource;
|
import net.minecraft.entity.damage.DamageSource;
|
||||||
import net.minecraft.entity.passive.MerchantEntity;
|
import net.minecraft.entity.passive.MerchantEntity;
|
||||||
|
import net.minecraft.entity.projectile.ProjectileEntity;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.command.ServerCommandSource;
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
@@ -41,6 +46,7 @@ import net.minecraft.village.Merchant;
|
|||||||
import net.minecraft.village.TradeOffer;
|
import net.minecraft.village.TradeOffer;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraft.world.chunk.Chunk;
|
import net.minecraft.world.chunk.Chunk;
|
||||||
|
import net.minecraft.world.explosion.Explosion;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -51,6 +57,7 @@ import java.util.HashSet;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -204,6 +211,7 @@ public class CarpetEventServer
|
|||||||
{
|
{
|
||||||
if (callList.size() > 0)
|
if (callList.size() > 0)
|
||||||
{
|
{
|
||||||
|
CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet events", CarpetProfiler.TYPE.GENERAL);
|
||||||
List<Value> argv = argumentSupplier.get(); // empty for onTickDone
|
List<Value> argv = argumentSupplier.get(); // empty for onTickDone
|
||||||
ServerCommandSource source;
|
ServerCommandSource source;
|
||||||
try
|
try
|
||||||
@@ -225,6 +233,7 @@ public class CarpetEventServer
|
|||||||
if (!call.execute(source, argv)) fails.add(call);
|
if (!call.execute(source, argv)) fails.add(call);
|
||||||
}
|
}
|
||||||
for (Callback call : fails) callList.remove(call);
|
for (Callback call : fails) callList.remove(call);
|
||||||
|
CarpetProfiler.end_current_section(currentSection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -862,6 +871,59 @@ public class CarpetEventServer
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
//copy of Explosion.getCausingEntity() #TRACK#
|
||||||
|
private static LivingEntity getExplosionCausingEntity(Entity entity)
|
||||||
|
{
|
||||||
|
if (entity == null) return null;
|
||||||
|
else if (entity instanceof TntEntity) return ((TntEntity)entity).getCausingEntity();
|
||||||
|
else if (entity instanceof LivingEntity) return (LivingEntity)entity;
|
||||||
|
else if (entity instanceof ProjectileEntity) {
|
||||||
|
Entity owner = ((ProjectileEntity)entity).getOwner();
|
||||||
|
if (owner instanceof LivingEntity) return (LivingEntity)owner;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Event EXPLOSION_OUTCOME = new Event("explosion_outcome", 8, true)
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onExplosion(ServerWorld world, Entity e, Supplier<LivingEntity> attacker, double x, double y, double z, float power, boolean createFire, List<BlockPos> affectedBlocks, List<Entity> affectedEntities, Explosion.DestructionType type)
|
||||||
|
{
|
||||||
|
handler.call(
|
||||||
|
() -> Arrays.asList(
|
||||||
|
ListValue.fromTriple(x, y, z),
|
||||||
|
NumericValue.of(power),
|
||||||
|
EntityValue.of(e),
|
||||||
|
EntityValue.of(attacker!= null?attacker.get():Event.getExplosionCausingEntity(e)),
|
||||||
|
StringValue.of(type.name().toLowerCase(Locale.ROOT)),
|
||||||
|
BooleanValue.of(createFire),
|
||||||
|
new ListValue(affectedBlocks.stream().filter(b -> !world.isAir(b)). map( // da heck they send air blocks
|
||||||
|
b -> new BlockValue(world.getBlockState(b),world,b)
|
||||||
|
).collect(Collectors.toList())),
|
||||||
|
new ListValue(affectedEntities.stream().map(EntityValue::of).collect(Collectors.toList()))
|
||||||
|
), () -> CarpetServer.minecraft_server.getCommandSource().withWorld(world)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public static final Event EXPLOSION = new Event("explosion", 6, true)
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onExplosion(ServerWorld world, Entity e, Supplier<LivingEntity> attacker, double x, double y, double z, float power, boolean createFire, List<BlockPos> affectedBlocks, List<Entity> affectedEntities, Explosion.DestructionType type)
|
||||||
|
{
|
||||||
|
handler.call(
|
||||||
|
() -> Arrays.asList(
|
||||||
|
ListValue.fromTriple(x, y, z),
|
||||||
|
NumericValue.of(power),
|
||||||
|
EntityValue.of(e),
|
||||||
|
EntityValue.of(attacker!= null?attacker.get():Event.getExplosionCausingEntity(e)),
|
||||||
|
StringValue.of(type.name().toLowerCase(Locale.ROOT)),
|
||||||
|
BooleanValue.of(createFire)
|
||||||
|
), () -> CarpetServer.minecraft_server.getCommandSource().withWorld(world)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public static String getEntityLoadEventName(EntityType<? extends Entity> et)
|
public static String getEntityLoadEventName(EntityType<? extends Entity> et)
|
||||||
{
|
{
|
||||||
@@ -982,7 +1044,7 @@ public class CarpetEventServer
|
|||||||
public void onSlotSwitch(ServerPlayerEntity player, int from, int to) {}
|
public void onSlotSwitch(ServerPlayerEntity player, int from, int to) {}
|
||||||
public void onTrade(ServerPlayerEntity player, Merchant merchant, TradeOffer tradeOffer) {}
|
public void onTrade(ServerPlayerEntity player, Merchant merchant, TradeOffer tradeOffer) {}
|
||||||
|
|
||||||
|
public void onExplosion(ServerWorld world, Entity e, Supplier<LivingEntity> attacker, double x, double y, double z, float power, boolean createFire, List<BlockPos> affectedBlocks, List<Entity> affectedEntities, Explosion.DestructionType type) { }
|
||||||
public void onWorldEvent(ServerWorld world, BlockPos pos) { }
|
public void onWorldEvent(ServerWorld world, BlockPos pos) { }
|
||||||
public void onWorldEventFlag(ServerWorld world, BlockPos pos, int flag) { }
|
public void onWorldEventFlag(ServerWorld world, BlockPos pos, int flag) { }
|
||||||
public void onCarpetRuleChanges(ParsedRule<?> rule, ServerCommandSource source) { }
|
public void onCarpetRuleChanges(ParsedRule<?> rule, ServerCommandSource source) { }
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package carpet.script;
|
|||||||
import carpet.CarpetServer;
|
import carpet.CarpetServer;
|
||||||
import carpet.CarpetSettings;
|
import carpet.CarpetSettings;
|
||||||
import carpet.script.api.Auxiliary;
|
import carpet.script.api.Auxiliary;
|
||||||
|
import carpet.script.argument.FileArgument;
|
||||||
import carpet.script.argument.FunctionArgument;
|
import carpet.script.argument.FunctionArgument;
|
||||||
import carpet.script.bundled.Module;
|
import carpet.script.bundled.Module;
|
||||||
import carpet.script.command.CommandArgument;
|
import carpet.script.command.CommandArgument;
|
||||||
@@ -17,9 +18,11 @@ import carpet.script.value.MapValue;
|
|||||||
import carpet.script.value.NumericValue;
|
import carpet.script.value.NumericValue;
|
||||||
import carpet.script.value.StringValue;
|
import carpet.script.value.StringValue;
|
||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
|
import carpet.utils.CarpetProfiler;
|
||||||
import carpet.utils.Messenger;
|
import carpet.utils.Messenger;
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
@@ -32,6 +35,7 @@ import net.minecraft.server.command.ServerCommandSource;
|
|||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.text.LiteralText;
|
import net.minecraft.text.LiteralText;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
@@ -43,11 +47,13 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
|
import static net.minecraft.server.command.CommandManager.argument;
|
||||||
import static net.minecraft.server.command.CommandManager.literal;
|
import static net.minecraft.server.command.CommandManager.literal;
|
||||||
|
|
||||||
public class CarpetScriptHost extends ScriptHost
|
public class CarpetScriptHost extends ScriptHost
|
||||||
@@ -122,6 +128,7 @@ public class CarpetScriptHost extends ScriptHost
|
|||||||
|
|
||||||
private static int execute(CommandContext<ServerCommandSource> ctx, String hostName, FunctionArgument<Value> funcSpec, List<String> paramNames) throws CommandSyntaxException
|
private static int execute(CommandContext<ServerCommandSource> ctx, String hostName, FunctionArgument<Value> funcSpec, List<String> paramNames) throws CommandSyntaxException
|
||||||
{
|
{
|
||||||
|
CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet command", CarpetProfiler.TYPE.GENERAL);
|
||||||
CarpetScriptHost cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveOwnForExecution(ctx.getSource());
|
CarpetScriptHost cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveOwnForExecution(ctx.getSource());
|
||||||
List<String> argNames = funcSpec.function.getArguments();
|
List<String> argNames = funcSpec.function.getArguments();
|
||||||
if ((argNames.size()-funcSpec.args.size()) != paramNames.size())
|
if ((argNames.size()-funcSpec.args.size()) != paramNames.size())
|
||||||
@@ -135,7 +142,9 @@ public class CarpetScriptHost extends ScriptHost
|
|||||||
Value response = cHost.handleCommand(ctx.getSource(), funcSpec.function, args);
|
Value response = cHost.handleCommand(ctx.getSource(), funcSpec.function, args);
|
||||||
// will skip prints for new
|
// will skip prints for new
|
||||||
//if (!response.isNull()) Messenger.m(ctx.getSource(), "gi " + response.getString());
|
//if (!response.isNull()) Messenger.m(ctx.getSource(), "gi " + response.getString());
|
||||||
return (int) response.readInteger();
|
int intres = (int) response.readInteger();
|
||||||
|
CarpetProfiler.end_current_section(currentSection);
|
||||||
|
return intres;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiteralArgumentBuilder<ServerCommandSource> addPathToCommand(
|
public LiteralArgumentBuilder<ServerCommandSource> addPathToCommand(
|
||||||
@@ -205,12 +214,15 @@ public class CarpetScriptHost extends ScriptHost
|
|||||||
return s -> {
|
return s -> {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet command", CarpetProfiler.TYPE.GENERAL);
|
||||||
CarpetScriptHost cHost = null;
|
CarpetScriptHost cHost = null;
|
||||||
cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveOwnForExecution(s);
|
cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveOwnForExecution(s);
|
||||||
Value response = cHost.handleCommand(s, fun, Collections.singletonList(
|
Value response = cHost.handleCommand(s, fun, Collections.singletonList(
|
||||||
(s.getEntity() instanceof ServerPlayerEntity)?new EntityValue(s.getEntity()):Value.NULL)
|
(s.getEntity() instanceof ServerPlayerEntity)?new EntityValue(s.getEntity()):Value.NULL)
|
||||||
);
|
);
|
||||||
return response.getBoolean();
|
boolean res = response.getBoolean();
|
||||||
|
CarpetProfiler.end_current_section(currentSection);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
catch (CommandSyntaxException e)
|
catch (CommandSyntaxException e)
|
||||||
{
|
{
|
||||||
@@ -311,6 +323,107 @@ public class CarpetScriptHost extends ScriptHost
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean addAppCommands(Consumer<Text> notifier)
|
||||||
|
{
|
||||||
|
if (appConfig.get(StringValue.of("commands")) != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LiteralArgumentBuilder<ServerCommandSource> command = readCommands(commandValidator);
|
||||||
|
if (command != null)
|
||||||
|
{
|
||||||
|
scriptServer.server.getCommandManager().getDispatcher().register(command);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (CommandSyntaxException cse)
|
||||||
|
{
|
||||||
|
// failed
|
||||||
|
notifier.accept(Messenger.c("r Failed to build command system for "+getName()+" thus failed to load the app ", cse.getRawMessage()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return addLegacyCommand(notifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean addLegacyCommand(Consumer<Text> notifier)
|
||||||
|
{
|
||||||
|
if (main == null) return false;
|
||||||
|
if (getFunction("__command") == null) return false;
|
||||||
|
|
||||||
|
if (scriptServer.isInvalidCommandRoot(getName()))
|
||||||
|
{
|
||||||
|
notifier.accept(Messenger.c("gi Tried to mask vanilla command."));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Function<ServerCommandSource, Boolean> configValidator;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
configValidator = getCommandConfigPermissions();
|
||||||
|
}
|
||||||
|
catch (CommandSyntaxException e)
|
||||||
|
{
|
||||||
|
notifier.accept(Messenger.c("rb "+e.getMessage()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String hostName = getName();
|
||||||
|
LiteralArgumentBuilder<ServerCommandSource> command = literal(hostName).
|
||||||
|
requires((player) -> scriptServer.modules.containsKey(hostName) && commandValidator.apply(player) && configValidator.apply(player)).
|
||||||
|
executes( (c) ->
|
||||||
|
{
|
||||||
|
CarpetScriptHost targetHost = scriptServer.modules.get(hostName).retrieveOwnForExecution(c.getSource());
|
||||||
|
Value response = targetHost.handleCommandLegacy(c.getSource(),"__command", null, "");
|
||||||
|
if (!response.isNull()) Messenger.m(c.getSource(), "gi "+response.getString());
|
||||||
|
return (int)response.readInteger();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (String function : globaFunctionNames(main, s -> !s.startsWith("_")).sorted().collect(Collectors.toList()))
|
||||||
|
{
|
||||||
|
if (appConfig.getOrDefault(StringValue.of("legacy_command_type_support"), Value.FALSE).getBoolean())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FunctionValue functionValue = getFunction(function);
|
||||||
|
command = addPathToCommand(
|
||||||
|
command,
|
||||||
|
CommandToken.parseSpec(CommandToken.specFromSignature(functionValue), this),
|
||||||
|
FunctionArgument.fromCommandSpec(this, functionValue)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (CommandSyntaxException e)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
command = command.
|
||||||
|
then(literal(function).
|
||||||
|
requires((player) -> scriptServer.modules.containsKey(hostName) && scriptServer.modules.get(hostName).getFunction(function) != null).
|
||||||
|
executes((c) -> {
|
||||||
|
CarpetScriptHost targetHost = scriptServer.modules.get(hostName).retrieveOwnForExecution(c.getSource());
|
||||||
|
Value response = targetHost.handleCommandLegacy(c.getSource(),function, null, "");
|
||||||
|
if (!response.isNull()) Messenger.m(c.getSource(), "gi " + response.getString());
|
||||||
|
return (int) response.readInteger();
|
||||||
|
}).
|
||||||
|
then(argument("args...", StringArgumentType.greedyString()).
|
||||||
|
executes( (c) -> {
|
||||||
|
CarpetScriptHost targetHost = scriptServer.modules.get(hostName).retrieveOwnForExecution(c.getSource());
|
||||||
|
Value response = targetHost.handleCommandLegacy(c.getSource(),function, null, StringArgumentType.getString(c, "args..."));
|
||||||
|
if (!response.isNull()) Messenger.m(c.getSource(), "gi "+response.getString());
|
||||||
|
return (int)response.readInteger();
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scriptServer.server.getCommandManager().getDispatcher().register(command);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public LiteralArgumentBuilder<ServerCommandSource> readCommands(Function<ServerCommandSource, Boolean> useValidator) throws CommandSyntaxException
|
public LiteralArgumentBuilder<ServerCommandSource> readCommands(Function<ServerCommandSource, Boolean> useValidator) throws CommandSyntaxException
|
||||||
{
|
{
|
||||||
Value commands = appConfig.get(StringValue.of("commands"));
|
Value commands = appConfig.get(StringValue.of("commands"));
|
||||||
@@ -440,7 +553,10 @@ public class CarpetScriptHost extends ScriptHost
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return callLegacy(source, call, coords, arg);
|
CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet command", CarpetProfiler.TYPE.GENERAL);
|
||||||
|
Value res = callLegacy(source, call, coords, arg);
|
||||||
|
CarpetProfiler.end_current_section(currentSection);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
catch (CarpetExpressionException exc)
|
catch (CarpetExpressionException exc)
|
||||||
{
|
{
|
||||||
@@ -680,31 +796,31 @@ public class CarpetScriptHost extends ScriptHost
|
|||||||
|
|
||||||
private void dumpState()
|
private void dumpState()
|
||||||
{
|
{
|
||||||
Module.saveData(main, null, globalState, false);
|
Module.saveData(main, globalState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tag loadState()
|
private Tag loadState()
|
||||||
{
|
{
|
||||||
return Module.getData(main, null, false);
|
return Module.getData(main);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Tag readFileTag(String file, boolean isShared)
|
public Tag readFileTag(FileArgument fdesc)
|
||||||
{
|
{
|
||||||
if (getName() == null && !isShared) return null;
|
if (getName() == null && !fdesc.isShared) return null;
|
||||||
if (file != null)
|
if (fdesc.resource != null)
|
||||||
return Module.getData(main, file, isShared);
|
return fdesc.getNbtData(main);
|
||||||
if (parent == null)
|
if (parent == null)
|
||||||
return globalState;
|
return globalState;
|
||||||
return ((CarpetScriptHost)parent).globalState;
|
return ((CarpetScriptHost)parent).globalState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean writeTagFile(Tag tag, String file, boolean isShared)
|
public boolean writeTagFile(Tag tag, FileArgument fdesc)
|
||||||
{
|
{
|
||||||
if (getName() == null && !isShared) return false; // if belongs to an app, cannot be default host.
|
if (getName() == null && !fdesc.isShared) return false; // if belongs to an app, cannot be default host.
|
||||||
|
|
||||||
if (file!= null)
|
if (fdesc.resource != null)
|
||||||
{
|
{
|
||||||
return Module.saveData(main, file, tag, isShared);
|
return fdesc.saveNbtData(main, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
CarpetScriptHost responsibleHost = (parent != null)?(CarpetScriptHost) parent:this;
|
CarpetScriptHost responsibleHost = (parent != null)?(CarpetScriptHost) parent:this;
|
||||||
@@ -717,34 +833,34 @@ public class CarpetScriptHost extends ScriptHost
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean removeResourceFile(String resource, boolean isShared, String type)
|
public boolean removeResourceFile(FileArgument fdesc)
|
||||||
{
|
{
|
||||||
if (getName() == null && !isShared) return false; //
|
if (getName() == null && !fdesc.isShared) return false; //
|
||||||
return Module.dropExistingFile(main, resource, type, isShared);
|
return fdesc.dropExistingFile(main);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean appendLogFile(String resource, boolean isShared, String type, List<String> data)
|
public boolean appendLogFile(FileArgument fdesc, List<String> data)
|
||||||
{
|
{
|
||||||
if (getName() == null && !isShared) return false; // if belongs to an app, cannot be default host.
|
if (getName() == null && !fdesc.isShared) return false; // if belongs to an app, cannot be default host.
|
||||||
return Module.appendToTextFile(main, resource, type, isShared, data);
|
return fdesc.appendToTextFile(main, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> readTextResource(String resource, String type, boolean isShared)
|
public List<String> readTextResource(FileArgument fdesc)
|
||||||
{
|
{
|
||||||
if (getName() == null && !isShared) return null;
|
if (getName() == null && !fdesc.isShared) return null;
|
||||||
return Module.listFile(main, resource, type, isShared);
|
return fdesc.listFile(main);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonElement readJsonFile(String resource, String type, boolean isShared)
|
public JsonElement readJsonFile(FileArgument fdesc)
|
||||||
{
|
{
|
||||||
if (getName() == null && !isShared) return null;
|
if (getName() == null && !fdesc.isShared) return null;
|
||||||
return Module.readJsonFile(main, resource, type, isShared);
|
return fdesc.readJsonFile(main);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream<String> listFolder(String resource, String type, boolean isShared)
|
public Stream<String> listFolder(FileArgument fdesc)
|
||||||
{
|
{
|
||||||
if (getName() == null && !isShared) return null; //
|
if (getName() == null && !fdesc.isShared) return null; //
|
||||||
return Module.listFolder(main, resource, type, isShared);
|
return fdesc.listFolder(main);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
package carpet.script;
|
package carpet.script;
|
||||||
|
|
||||||
import carpet.CarpetSettings;
|
import carpet.CarpetSettings;
|
||||||
import carpet.script.argument.FunctionArgument;
|
|
||||||
import carpet.script.bundled.BundledModule;
|
import carpet.script.bundled.BundledModule;
|
||||||
import carpet.CarpetServer;
|
import carpet.CarpetServer;
|
||||||
import carpet.script.bundled.FileModule;
|
import carpet.script.bundled.FileModule;
|
||||||
import carpet.script.bundled.Module;
|
import carpet.script.bundled.Module;
|
||||||
import carpet.script.command.CommandToken;
|
|
||||||
import carpet.script.exception.InvalidCallbackException;
|
import carpet.script.exception.InvalidCallbackException;
|
||||||
import carpet.script.value.FunctionValue;
|
import carpet.script.value.FunctionValue;
|
||||||
import carpet.script.value.StringValue;
|
|
||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
|
import carpet.utils.CarpetProfiler;
|
||||||
import carpet.utils.Messenger;
|
import carpet.utils.Messenger;
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.mojang.brigadier.tree.CommandNode;
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
|
|
||||||
@@ -26,6 +22,7 @@ import net.minecraft.util.WorldSavePath;
|
|||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.FileVisitOption;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -93,6 +90,7 @@ public class CarpetScriptServer
|
|||||||
registerBuiltInScript(BundledModule.carpetNative("chunk_display", false));
|
registerBuiltInScript(BundledModule.carpetNative("chunk_display", false));
|
||||||
registerBuiltInScript(BundledModule.carpetNative("ai_tracker", false));
|
registerBuiltInScript(BundledModule.carpetNative("ai_tracker", false));
|
||||||
registerBuiltInScript(BundledModule.carpetNative("draw_beta", false));
|
registerBuiltInScript(BundledModule.carpetNative("draw_beta", false));
|
||||||
|
registerBuiltInScript(BundledModule.carpetNative("shapes", true));
|
||||||
registerBuiltInScript(BundledModule.carpetNative("distance_beta", false));
|
registerBuiltInScript(BundledModule.carpetNative("distance_beta", false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +134,8 @@ public class CarpetScriptServer
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Path folder = server.getSavePath(WorldSavePath.ROOT).resolve("scripts");
|
Path folder = server.getSavePath(WorldSavePath.ROOT).resolve("scripts");
|
||||||
Files.createDirectories(folder);
|
if (!Files.exists(folder))
|
||||||
|
Files.createDirectories(folder);
|
||||||
Optional<Path>
|
Optional<Path>
|
||||||
scriptPath = Files.list(folder)
|
scriptPath = Files.list(folder)
|
||||||
.filter(script ->
|
.filter(script ->
|
||||||
@@ -149,7 +148,8 @@ public class CarpetScriptServer
|
|||||||
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT)
|
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT)
|
||||||
{
|
{
|
||||||
Path globalFolder = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
Path globalFolder = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
||||||
Files.createDirectories(globalFolder);
|
if (!Files.exists(globalFolder))
|
||||||
|
Files.createDirectories(globalFolder);
|
||||||
scriptPath = Files.walk(globalFolder)
|
scriptPath = Files.walk(globalFolder)
|
||||||
.filter(script -> script.getFileName().toString().equalsIgnoreCase(name + ".sc") ||
|
.filter(script -> script.getFileName().toString().equalsIgnoreCase(name + ".sc") ||
|
||||||
(allowLibraries && script.getFileName().toString().equalsIgnoreCase(name + ".scl")))
|
(allowLibraries && script.getFileName().toString().equalsIgnoreCase(name + ".scl")))
|
||||||
@@ -194,7 +194,8 @@ public class CarpetScriptServer
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Path worldScripts = server.getSavePath(WorldSavePath.ROOT).resolve("scripts");
|
Path worldScripts = server.getSavePath(WorldSavePath.ROOT).resolve("scripts");
|
||||||
Files.createDirectories(worldScripts);
|
if (!Files.exists(worldScripts))
|
||||||
|
Files.createDirectories(worldScripts);
|
||||||
Files.list(worldScripts)
|
Files.list(worldScripts)
|
||||||
.filter(f -> f.toString().endsWith(".sc"))
|
.filter(f -> f.toString().endsWith(".sc"))
|
||||||
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT)));
|
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT)));
|
||||||
@@ -202,8 +203,9 @@ public class CarpetScriptServer
|
|||||||
if (includeBuiltIns && (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT))
|
if (includeBuiltIns && (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT))
|
||||||
{
|
{
|
||||||
Path globalScripts = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
Path globalScripts = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
||||||
Files.createDirectories(globalScripts);
|
if (!Files.exists(globalScripts))
|
||||||
Files.walk(globalScripts)
|
Files.createDirectories(globalScripts);
|
||||||
|
Files.walk(globalScripts, FileVisitOption.FOLLOW_LINKS)
|
||||||
.filter(f -> f.toString().endsWith(".sc"))
|
.filter(f -> f.toString().endsWith(".sc"))
|
||||||
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT)));
|
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT)));
|
||||||
}
|
}
|
||||||
@@ -223,6 +225,7 @@ public class CarpetScriptServer
|
|||||||
public boolean addScriptHost(ServerCommandSource source, String name, Function<ServerCommandSource, Boolean> commandValidator,
|
public boolean addScriptHost(ServerCommandSource source, String name, Function<ServerCommandSource, Boolean> commandValidator,
|
||||||
boolean perPlayer, boolean autoload, boolean isRuleApp)
|
boolean perPlayer, boolean autoload, boolean isRuleApp)
|
||||||
{
|
{
|
||||||
|
CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet load", CarpetProfiler.TYPE.GENERAL);
|
||||||
if (commandValidator == null) commandValidator = p -> true;
|
if (commandValidator == null) commandValidator = p -> true;
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
name = name.toLowerCase(Locale.ROOT);
|
name = name.toLowerCase(Locale.ROOT);
|
||||||
@@ -267,35 +270,23 @@ public class CarpetScriptServer
|
|||||||
}
|
}
|
||||||
//addEvents(source, name);
|
//addEvents(source, name);
|
||||||
String action = reload?"reloaded":"loaded";
|
String action = reload?"reloaded":"loaded";
|
||||||
if (newHost.appConfig.get(StringValue.of("commands")) != null)
|
Boolean isCommandAdded = newHost.addAppCommands(s -> {
|
||||||
{
|
if (!isRuleApp) Messenger.m(source, s);
|
||||||
try
|
});
|
||||||
{
|
if (isCommandAdded == null) // error should be dispatched
|
||||||
LiteralArgumentBuilder<ServerCommandSource> command = newHost.readCommands(commandValidator);
|
|
||||||
if (command != null)
|
|
||||||
{
|
|
||||||
if (!isRuleApp) Messenger.m(source, "gi "+name+" app "+action+" with /"+name+" command");
|
|
||||||
server.getCommandManager().getDispatcher().register(command);
|
|
||||||
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!isRuleApp) Messenger.m(source, "gi "+name+" app "+action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (CommandSyntaxException cse)
|
|
||||||
{
|
|
||||||
removeScriptHost(source, name, false, false);
|
|
||||||
Messenger.m(source, "r Failed to build command system for "+name+" thus failed to load the app ", cse.getRawMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (!addLegacyCommand(source, name, reload, !isRuleApp, commandValidator)) // this needs to be moved to config reader, only supporting legacy command here
|
|
||||||
{
|
{
|
||||||
removeScriptHost(source, name, false, false);
|
removeScriptHost(source, name, false, false);
|
||||||
Messenger.m(source, "r Failed to build command system for "+name+" thus failed to load the app");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
else if (isCommandAdded)
|
||||||
|
{
|
||||||
|
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
|
||||||
|
if (!isRuleApp) Messenger.m(source, "gi "+name+" app "+action+" with /"+name+" command");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!isRuleApp) Messenger.m(source, "gi "+name+" app "+action);
|
||||||
|
}
|
||||||
|
|
||||||
if (newHost.isPerUser())
|
if (newHost.isPerUser())
|
||||||
{
|
{
|
||||||
// that will provide player hosts right at the startup
|
// that will provide player hosts right at the startup
|
||||||
@@ -307,94 +298,15 @@ public class CarpetScriptServer
|
|||||||
FunctionValue onStart = newHost.getFunction("__on_start");
|
FunctionValue onStart = newHost.getFunction("__on_start");
|
||||||
if (onStart != null) newHost.callNow(onStart, Collections.emptyList());
|
if (onStart != null) newHost.callNow(onStart, Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
CarpetProfiler.end_current_section(currentSection);
|
||||||
long end = System.nanoTime();
|
long end = System.nanoTime();
|
||||||
CarpetSettings.LOG.info("App "+name+" loaded in "+(end-start)/1000000+" ms");
|
CarpetSettings.LOG.info("App "+name+" loaded in "+(end-start)/1000000+" ms");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean addLegacyCommand(ServerCommandSource source, String hostName, boolean isReload, boolean notifySource, Function<ServerCommandSource, Boolean> useValidator)
|
public boolean isInvalidCommandRoot(String appName)
|
||||||
{
|
{
|
||||||
CarpetScriptHost host = modules.get(hostName);
|
return holyMoly.contains(appName);
|
||||||
String loaded = isReload?"reloaded":"loaded";
|
|
||||||
if (host == null)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (host.getFunction("__command") == null)
|
|
||||||
{
|
|
||||||
if (notifySource) Messenger.m(source, "gi "+hostName+" app "+loaded+".");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (holyMoly.contains(hostName))
|
|
||||||
{
|
|
||||||
Messenger.m(source, "gi "+hostName+" app "+loaded+" with no command.");
|
|
||||||
Messenger.m(source, "gi Tried to mask vanilla command.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Function<ServerCommandSource, Boolean> configValidator;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
configValidator = host.getCommandConfigPermissions();
|
|
||||||
}
|
|
||||||
catch (CommandSyntaxException e)
|
|
||||||
{
|
|
||||||
Messenger.m(source, "rb "+e.getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
LiteralArgumentBuilder<ServerCommandSource> command = literal(hostName).
|
|
||||||
requires((player) -> modules.containsKey(hostName) && useValidator.apply(player) && configValidator.apply(player)).
|
|
||||||
executes( (c) ->
|
|
||||||
{
|
|
||||||
CarpetScriptHost targetHost = modules.get(hostName).retrieveOwnForExecution(c.getSource());
|
|
||||||
Value response = targetHost.handleCommandLegacy(c.getSource(),"__command", null, "");
|
|
||||||
if (!response.isNull()) Messenger.m(c.getSource(), "gi "+response.getString());
|
|
||||||
return (int)response.readInteger();
|
|
||||||
});
|
|
||||||
|
|
||||||
for (String function : host.globaFunctionNames(host.main, s -> !s.startsWith("_")).sorted().collect(Collectors.toList()))
|
|
||||||
{
|
|
||||||
if (host.appConfig.getOrDefault(StringValue.of("legacy_command_type_support"), Value.FALSE).getBoolean())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
FunctionValue functionValue = host.getFunction(function);
|
|
||||||
command = host.addPathToCommand(
|
|
||||||
command,
|
|
||||||
CommandToken.parseSpec(CommandToken.specFromSignature(functionValue), host),
|
|
||||||
FunctionArgument.fromCommandSpec(host, functionValue)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
catch (CommandSyntaxException e)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
command = command.
|
|
||||||
then(literal(function).
|
|
||||||
requires((player) -> modules.containsKey(hostName) && modules.get(hostName).getFunction(function) != null).
|
|
||||||
executes((c) -> {
|
|
||||||
CarpetScriptHost targetHost = modules.get(hostName).retrieveOwnForExecution(c.getSource());
|
|
||||||
Value response = targetHost.handleCommandLegacy(c.getSource(),function, null, "");
|
|
||||||
if (!response.isNull()) Messenger.m(c.getSource(), "gi " + response.getString());
|
|
||||||
return (int) response.readInteger();
|
|
||||||
}).
|
|
||||||
then(argument("args...", StringArgumentType.greedyString()).
|
|
||||||
executes( (c) -> {
|
|
||||||
CarpetScriptHost targetHost = modules.get(hostName).retrieveOwnForExecution(c.getSource());
|
|
||||||
Value response = targetHost.handleCommandLegacy(c.getSource(),function, null, StringArgumentType.getString(c, "args..."));
|
|
||||||
if (!response.isNull()) Messenger.m(c.getSource(), "gi "+response.getString());
|
|
||||||
return (int)response.readInteger();
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (notifySource) Messenger.m(source, "gi "+hostName+" app "+loaded+" with /"+hostName+" command");
|
|
||||||
server.getCommandManager().getDispatcher().register(command);
|
|
||||||
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -498,11 +410,16 @@ public class CarpetScriptServer
|
|||||||
|
|
||||||
public void tick()
|
public void tick()
|
||||||
{
|
{
|
||||||
|
CarpetProfiler.ProfilerToken token;
|
||||||
|
token = CarpetProfiler.start_section(null, "Scarpet schedule", CarpetProfiler.TYPE.GENERAL);
|
||||||
events.tick();
|
events.tick();
|
||||||
|
CarpetProfiler.end_current_section(token);
|
||||||
|
token = CarpetProfiler.start_section(null, "Scarpet app data", CarpetProfiler.TYPE.GENERAL);
|
||||||
for (CarpetScriptHost host : modules.values())
|
for (CarpetScriptHost host : modules.values())
|
||||||
{
|
{
|
||||||
host.tick();
|
host.tick();
|
||||||
}
|
}
|
||||||
|
CarpetProfiler.end_current_section(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onClose()
|
public void onClose()
|
||||||
@@ -553,4 +470,9 @@ public class CarpetScriptServer
|
|||||||
init();
|
init();
|
||||||
apps.forEach((s, data) -> addScriptHost(server.getCommandSource(), s,data.commandValidator, data.perUser,false, data.isRuleApp));
|
apps.forEach((s, data) -> addScriptHost(server.getCommandSource(), s,data.commandValidator, data.perUser,false, data.isRuleApp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void reAddCommands()
|
||||||
|
{
|
||||||
|
modules.values().forEach(host -> host.addAppCommands(s -> {}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,6 @@ import carpet.script.exception.ExpressionException;
|
|||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
import carpet.script.exception.ResolvedException;
|
import carpet.script.exception.ResolvedException;
|
||||||
import carpet.script.exception.ReturnStatement;
|
import carpet.script.exception.ReturnStatement;
|
||||||
import carpet.script.exception.ThrowStatement;
|
|
||||||
import carpet.script.language.Arithmetic;
|
import carpet.script.language.Arithmetic;
|
||||||
import carpet.script.language.ControlFlow;
|
import carpet.script.language.ControlFlow;
|
||||||
import carpet.script.language.DataStructures;
|
import carpet.script.language.DataStructures;
|
||||||
@@ -232,7 +231,7 @@ public class Expression
|
|||||||
if (exc instanceof ExitStatement)
|
if (exc instanceof ExitStatement)
|
||||||
return exc;
|
return exc;
|
||||||
if (exc instanceof InternalExpressionException)
|
if (exc instanceof InternalExpressionException)
|
||||||
return new ExpressionException(c, e, token, exc.getMessage(), ((InternalExpressionException) exc).stack);
|
return ((InternalExpressionException) exc).promote(c, e, token);
|
||||||
if (exc instanceof ArithmeticException)
|
if (exc instanceof ArithmeticException)
|
||||||
return new ExpressionException(c, e, token, "Your math is wrong, "+exc.getMessage());
|
return new ExpressionException(c, e, token, "Your math is wrong, "+exc.getMessage());
|
||||||
if (exc instanceof ResolvedException)
|
if (exc instanceof ResolvedException)
|
||||||
@@ -314,6 +313,11 @@ public class Expression
|
|||||||
addUnaryFunction(name, (v) -> new NumericValue(fun.apply(NumericValue.asNumber(v).getDouble())));
|
addUnaryFunction(name, (v) -> new NumericValue(fun.apply(NumericValue.asNumber(v).getDouble())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addMathematicalUnaryIntFunction(String name, Function<Double, Long> fun)
|
||||||
|
{
|
||||||
|
addUnaryFunction(name, (v) -> new NumericValue(fun.apply(NumericValue.asNumber(v).getDouble())));
|
||||||
|
}
|
||||||
|
|
||||||
public void addMathematicalBinaryFunction(String name, BiFunction<Double, Double, Double> fun)
|
public void addMathematicalBinaryFunction(String name, BiFunction<Double, Double, Double> fun)
|
||||||
{
|
{
|
||||||
addBinaryFunction(name, (w, v) ->
|
addBinaryFunction(name, (w, v) ->
|
||||||
@@ -624,9 +628,9 @@ public class Expression
|
|||||||
{
|
{
|
||||||
return exprProvider.get().evalValue(c, expectedType);
|
return exprProvider.get().evalValue(c, expectedType);
|
||||||
}
|
}
|
||||||
catch (ContinueStatement | BreakStatement | ReturnStatement | ThrowStatement exc)
|
catch (ContinueStatement | BreakStatement | ReturnStatement exc)
|
||||||
{
|
{
|
||||||
throw new ExpressionException(c, this, "Control flow functions, like continue, break, throw or return, should only be used in loops, try blocks, and functions respectively.");
|
throw new ExpressionException(c, this, "Control flow functions, like continue, break or return, should only be used in loops, and functions respectively.");
|
||||||
}
|
}
|
||||||
catch (ExitStatement exit)
|
catch (ExitStatement exit)
|
||||||
{
|
{
|
||||||
@@ -761,7 +765,7 @@ public class Expression
|
|||||||
{
|
{
|
||||||
if (token.surface.equals(";"))
|
if (token.surface.equals(";"))
|
||||||
{
|
{
|
||||||
throw new ExpressionException(c, this, token, "Unnecessary semicolon");
|
throw new ExpressionException(c, this, token, "Empty expression found for ';'");
|
||||||
}
|
}
|
||||||
throw new ExpressionException(c, this, token, "Missing parameter(s) for operator " + token);
|
throw new ExpressionException(c, this, token, "Missing parameter(s) for operator " + token);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,11 +37,15 @@ public abstract class ScriptHost
|
|||||||
|
|
||||||
public Random getRandom(long aLong)
|
public Random getRandom(long aLong)
|
||||||
{
|
{
|
||||||
if (randomizers.size() > 1024)
|
if (randomizers.size() > 65536)
|
||||||
randomizers.clear();
|
randomizers.clear();
|
||||||
return randomizers.computeIfAbsent(aLong, Random::new);
|
return randomizers.computeIfAbsent(aLong, Random::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean resetRandom(long aLong) {
|
||||||
|
return randomizers.remove(aLong) != null;
|
||||||
|
}
|
||||||
|
|
||||||
public static class ModuleData
|
public static class ModuleData
|
||||||
{
|
{
|
||||||
Module parent;
|
Module parent;
|
||||||
|
|||||||
@@ -406,6 +406,7 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
|||||||
public int pos;
|
public int pos;
|
||||||
public int linepos;
|
public int linepos;
|
||||||
public int lineno;
|
public int lineno;
|
||||||
|
public static final Token NONE = new Token();
|
||||||
public Token morphedInto(TokenType newType, String newSurface)
|
public Token morphedInto(TokenType newType, String newSurface)
|
||||||
{
|
{
|
||||||
Token created = new Token();
|
Token created = new Token();
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ package carpet.script.api;
|
|||||||
import carpet.CarpetServer;
|
import carpet.CarpetServer;
|
||||||
import carpet.CarpetSettings;
|
import carpet.CarpetSettings;
|
||||||
import carpet.fakes.MinecraftServerInterface;
|
import carpet.fakes.MinecraftServerInterface;
|
||||||
|
import carpet.fakes.ServerWorldInterface;
|
||||||
import carpet.fakes.StatTypeInterface;
|
import carpet.fakes.StatTypeInterface;
|
||||||
import carpet.fakes.ThreadedAnvilChunkStorageInterface;
|
import carpet.fakes.ThreadedAnvilChunkStorageInterface;
|
||||||
import carpet.helpers.FeatureGenerator;
|
import carpet.helpers.FeatureGenerator;
|
||||||
import carpet.mixins.PlayerListHeaderS2CPacketMixin;
|
import carpet.mixins.PlayerListHeaderS2CPacketMixin;
|
||||||
import carpet.script.bundled.Module;
|
import carpet.script.bundled.Module;
|
||||||
|
import carpet.script.argument.FileArgument;
|
||||||
import carpet.script.CarpetContext;
|
import carpet.script.CarpetContext;
|
||||||
import carpet.script.CarpetEventServer;
|
import carpet.script.CarpetEventServer;
|
||||||
import carpet.script.CarpetScriptHost;
|
import carpet.script.CarpetScriptHost;
|
||||||
@@ -20,7 +22,10 @@ import carpet.script.argument.FunctionArgument;
|
|||||||
import carpet.script.argument.Vector3Argument;
|
import carpet.script.argument.Vector3Argument;
|
||||||
import carpet.script.exception.ExitStatement;
|
import carpet.script.exception.ExitStatement;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import carpet.script.exception.ThrowStatement;
|
||||||
|
import carpet.script.exception.Throwables;
|
||||||
import carpet.script.utils.FixedCommandSource;
|
import carpet.script.utils.FixedCommandSource;
|
||||||
|
import carpet.script.utils.InputValidator;
|
||||||
import carpet.script.utils.ScarpetJsonDeserializer;
|
import carpet.script.utils.ScarpetJsonDeserializer;
|
||||||
import carpet.script.utils.ShapeDispatcher;
|
import carpet.script.utils.ShapeDispatcher;
|
||||||
import carpet.script.utils.WorldTools;
|
import carpet.script.utils.WorldTools;
|
||||||
@@ -36,6 +41,10 @@ import carpet.script.value.StringValue;
|
|||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
import carpet.script.value.ValueConversions;
|
import carpet.script.value.ValueConversions;
|
||||||
import carpet.utils.Messenger;
|
import carpet.utils.Messenger;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import net.minecraft.SharedConstants;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.command.DataCommandStorage;
|
import net.minecraft.command.DataCommandStorage;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
@@ -46,6 +55,7 @@ import net.minecraft.entity.player.PlayerEntity;
|
|||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.NbtHelper;
|
import net.minecraft.nbt.NbtHelper;
|
||||||
|
import net.minecraft.nbt.NbtOps;
|
||||||
import net.minecraft.nbt.StringTag;
|
import net.minecraft.nbt.StringTag;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.network.packet.s2c.play.PlaySoundIdS2CPacket;
|
import net.minecraft.network.packet.s2c.play.PlaySoundIdS2CPacket;
|
||||||
@@ -53,6 +63,11 @@ import net.minecraft.network.packet.s2c.play.PlayerListHeaderS2CPacket;
|
|||||||
import net.minecraft.network.packet.s2c.play.TitleS2CPacket;
|
import net.minecraft.network.packet.s2c.play.TitleS2CPacket;
|
||||||
import net.minecraft.network.packet.s2c.play.TitleS2CPacket.Action;
|
import net.minecraft.network.packet.s2c.play.TitleS2CPacket.Action;
|
||||||
import net.minecraft.particle.ParticleEffect;
|
import net.minecraft.particle.ParticleEffect;
|
||||||
|
import net.minecraft.resource.DataPackSettings;
|
||||||
|
import net.minecraft.resource.ReloadableResourceManagerImpl;
|
||||||
|
import net.minecraft.resource.ResourcePackManager;
|
||||||
|
import net.minecraft.resource.ResourcePackProfile;
|
||||||
|
import net.minecraft.resource.ServerResourceManager;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.command.ServerCommandSource;
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
@@ -64,21 +79,42 @@ import net.minecraft.text.LiteralText;
|
|||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.InvalidIdentifierException;
|
import net.minecraft.util.InvalidIdentifierException;
|
||||||
|
import net.minecraft.util.crash.CrashException;
|
||||||
|
import net.minecraft.util.Util;
|
||||||
|
import net.minecraft.util.WorldSavePath;
|
||||||
|
import net.minecraft.util.dynamic.RegistryOps;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.ChunkPos;
|
import net.minecraft.util.math.ChunkPos;
|
||||||
import net.minecraft.util.math.EulerAngle;
|
import net.minecraft.util.math.EulerAngle;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.util.registry.DynamicRegistryManager;
|
import net.minecraft.util.registry.DynamicRegistryManager;
|
||||||
import net.minecraft.util.registry.Registry;
|
import net.minecraft.util.registry.Registry;
|
||||||
|
import net.minecraft.util.registry.RegistryKey;
|
||||||
|
import net.minecraft.world.SaveProperties;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraft.world.biome.source.BiomeAccess;
|
||||||
|
import net.minecraft.world.border.WorldBorderListener;
|
||||||
|
import net.minecraft.world.dimension.DimensionOptions;
|
||||||
|
import net.minecraft.world.dimension.DimensionType;
|
||||||
|
import net.minecraft.world.gen.GeneratorOptions;
|
||||||
|
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||||
|
import net.minecraft.world.level.UnmodifiableLevelProperties;
|
||||||
|
import net.minecraft.world.level.storage.LevelStorage;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.apache.commons.lang3.tuple.Triple;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
|
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.file.FileSystem;
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -97,8 +133,9 @@ import static java.lang.Math.min;
|
|||||||
public class Auxiliary {
|
public class Auxiliary {
|
||||||
public static final String MARKER_STRING = "__scarpet_marker";
|
public static final String MARKER_STRING = "__scarpet_marker";
|
||||||
private static final Map<String, SoundCategory> mixerMap = Arrays.stream(SoundCategory.values()).collect(Collectors.toMap(SoundCategory::getName, k -> k));
|
private static final Map<String, SoundCategory> mixerMap = Arrays.stream(SoundCategory.values()).collect(Collectors.toMap(SoundCategory::getName, k -> k));
|
||||||
public static final Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().registerTypeAdapter(Value.class, new ScarpetJsonDeserializer()).create();
|
public static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().registerTypeAdapter(Value.class, new ScarpetJsonDeserializer()).create();
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static String recognizeResource(Value value, boolean isFloder)
|
public static String recognizeResource(Value value, boolean isFloder)
|
||||||
{
|
{
|
||||||
String origfile = value.getString();
|
String origfile = value.getString();
|
||||||
@@ -111,28 +148,20 @@ public class Auxiliary {
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Triple<String, String, Boolean> getFileDescriptor(List<LazyValue> lv, Context c, boolean isFloder)
|
|
||||||
{
|
|
||||||
if (lv.size() < 2) throw new InternalExpressionException("File functions require path and type as first two arguments");
|
|
||||||
String resource = recognizeResource(lv.get(0).evalValue(c), isFloder);
|
|
||||||
String origtype = lv.get(1).evalValue(c).getString().toLowerCase(Locale.ROOT);
|
|
||||||
boolean shared = origtype.startsWith("shared_");
|
|
||||||
String type = shared ? origtype.substring(7) : origtype; //len(shared_)
|
|
||||||
if (!Module.supportedTypes.containsKey(type))
|
|
||||||
throw new InternalExpressionException("Unsupported file type: "+origtype);
|
|
||||||
if (type.equals("folder") && !isFloder)
|
|
||||||
throw new InternalExpressionException("Folder types are no supported for this IO function");
|
|
||||||
return Triple.of(resource, type, shared);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void apply(Expression expression)
|
public static void apply(Expression expression)
|
||||||
{
|
{
|
||||||
expression.addLazyFunction("sound", -1, (c, t, lv) -> {
|
expression.addLazyFunction("sound", -1, (c, t, lv) -> {
|
||||||
CarpetContext cc = (CarpetContext)c;
|
CarpetContext cc = (CarpetContext)c;
|
||||||
Identifier soundName = new Identifier(lv.get(0).evalValue(c).getString());
|
if (lv.size() == 0)
|
||||||
|
{
|
||||||
|
Value ret = ListValue.wrap(Registry.SOUND_EVENT.getIds().stream().map(ValueConversions::of));
|
||||||
|
return (_c, _t) -> ret;
|
||||||
|
}
|
||||||
|
String rawString = lv.get(0).evalValue(c).getString();
|
||||||
|
Identifier soundName = new Identifier(rawString);
|
||||||
Vector3Argument locator = Vector3Argument.findIn(cc, lv, 1);
|
Vector3Argument locator = Vector3Argument.findIn(cc, lv, 1);
|
||||||
if (Registry.SOUND_EVENT.get(soundName) == null)
|
if (Registry.SOUND_EVENT.get(soundName) == null)
|
||||||
throw new InternalExpressionException("No such sound: "+soundName.getPath());
|
throw new ThrowStatement(rawString, Throwables.UNKNOWN_SOUND);
|
||||||
float volume = 1.0F;
|
float volume = 1.0F;
|
||||||
float pitch = 1.0F;
|
float pitch = 1.0F;
|
||||||
SoundCategory mixer = SoundCategory.MASTER;
|
SoundCategory mixer = SoundCategory.MASTER;
|
||||||
@@ -165,6 +194,12 @@ public class Auxiliary {
|
|||||||
expression.addLazyFunction("particle", -1, (c, t, lv) ->
|
expression.addLazyFunction("particle", -1, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
CarpetContext cc = (CarpetContext)c;
|
CarpetContext cc = (CarpetContext)c;
|
||||||
|
if (lv.size() == 0)
|
||||||
|
{
|
||||||
|
Value ret = ListValue.wrap(Registry.PARTICLE_TYPE.getIds().stream().map(ValueConversions::of));
|
||||||
|
return (_c, _t) -> ret;
|
||||||
|
}
|
||||||
|
|
||||||
MinecraftServer ms = cc.s.getMinecraftServer();
|
MinecraftServer ms = cc.s.getMinecraftServer();
|
||||||
ServerWorld world = cc.s.getWorld();
|
ServerWorld world = cc.s.getWorld();
|
||||||
Vector3Argument locator = Vector3Argument.findIn(cc, lv, 1);
|
Vector3Argument locator = Vector3Argument.findIn(cc, lv, 1);
|
||||||
@@ -847,39 +882,28 @@ public class Auxiliary {
|
|||||||
|
|
||||||
expression.addLazyFunction("list_files", 2, (c, t, lv) ->
|
expression.addLazyFunction("list_files", 2, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
Triple<String, String, Boolean> fdesc = getFileDescriptor(lv, c, true);
|
FileArgument fdesc = FileArgument.from(lv, c, true, FileArgument.Reason.READ);
|
||||||
Stream<String> files = ((CarpetScriptHost) c.host).listFolder(fdesc.getLeft(), fdesc.getMiddle(), fdesc.getRight());
|
Stream<String> files = ((CarpetScriptHost) c.host).listFolder(fdesc);
|
||||||
if (files == null) return LazyValue.NULL;
|
if (files == null) return LazyValue.NULL;
|
||||||
Value ret = ListValue.wrap(files.map(StringValue::of).collect(Collectors.toList()));
|
Value ret = ListValue.wrap(files.map(StringValue::of).collect(Collectors.toList()));
|
||||||
return (cc, tt) -> ret;
|
return (cc, tt) -> ret;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
expression.addLazyFunction("read_file", 2, (c, t, lv) ->
|
expression.addLazyFunction("read_file", 2, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
Triple<String, String, Boolean> fdesc = getFileDescriptor(lv, c, false);
|
FileArgument fdesc = FileArgument.from(lv, c, false, FileArgument.Reason.READ);
|
||||||
Value retVal;
|
Value retVal;
|
||||||
if (fdesc.getMiddle().equals("nbt"))
|
if (fdesc.type == FileArgument.Type.NBT)
|
||||||
{
|
{
|
||||||
Tag state = ((CarpetScriptHost) c.host).readFileTag(fdesc.getLeft(), fdesc.getRight());
|
Tag state = ((CarpetScriptHost) c.host).readFileTag(fdesc);
|
||||||
if (state == null) return LazyValue.NULL;
|
if (state == null) return LazyValue.NULL;
|
||||||
retVal = new NBTSerializableValue(state);
|
retVal = new NBTSerializableValue(state);
|
||||||
}
|
}
|
||||||
else if (fdesc.getMiddle().equals("json"))
|
else if (fdesc.type == FileArgument.Type.JSON)
|
||||||
{
|
{
|
||||||
JsonElement json;
|
JsonElement json;
|
||||||
try
|
json = ((CarpetScriptHost) c.host).readJsonFile(fdesc);
|
||||||
{
|
Value parsedJson = GSON.fromJson(json, Value.class);
|
||||||
json = ((CarpetScriptHost) c.host).readJsonFile(fdesc.getLeft(), fdesc.getMiddle(), fdesc.getRight());
|
|
||||||
}
|
|
||||||
catch (JsonParseException e)
|
|
||||||
{
|
|
||||||
Throwable exception = e;
|
|
||||||
if(e.getCause() != null)
|
|
||||||
exception = e.getCause();
|
|
||||||
throw new InternalExpressionException("Failed to read JSON file: "+exception.getMessage());
|
|
||||||
}
|
|
||||||
Value parsedJson = gson.fromJson(json, Value.class);
|
|
||||||
if (parsedJson == null)
|
if (parsedJson == null)
|
||||||
retVal = Value.NULL;
|
retVal = Value.NULL;
|
||||||
else
|
else
|
||||||
@@ -887,7 +911,7 @@ public class Auxiliary {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
List<String> content = ((CarpetScriptHost) c.host).readTextResource(fdesc.getLeft(), fdesc.getMiddle(), fdesc.getRight());
|
List<String> content = ((CarpetScriptHost) c.host).readTextResource(fdesc);
|
||||||
if (content == null) return LazyValue.NULL;
|
if (content == null) return LazyValue.NULL;
|
||||||
retVal = ListValue.wrap(content.stream().map(StringValue::new).collect(Collectors.toList()));
|
retVal = ListValue.wrap(content.stream().map(StringValue::new).collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
@@ -895,32 +919,29 @@ public class Auxiliary {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expression.addLazyFunction("delete_file", 2, (c, t, lv) -> {
|
expression.addLazyFunction("delete_file", 2, (c, t, lv) -> {
|
||||||
Triple<String, String, Boolean> fdesc = getFileDescriptor(lv, c, false);
|
FileArgument fdesc = FileArgument.from(lv, c, false, FileArgument.Reason.DELETE);
|
||||||
return ((CarpetScriptHost) c.host).removeResourceFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle())
|
return ((CarpetScriptHost) c.host).removeResourceFile(fdesc) ? LazyValue.TRUE : LazyValue.FALSE;
|
||||||
? LazyValue.TRUE
|
|
||||||
: LazyValue.FALSE;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expression.addLazyFunction("write_file", -1, (c, t, lv) -> {
|
expression.addLazyFunction("write_file", -1, (c, t, lv) -> {
|
||||||
if (lv.size() < 3) throw new InternalExpressionException("'write_file' requires three or more arguments");
|
if (lv.size() < 3) throw new InternalExpressionException("'write_file' requires three or more arguments");
|
||||||
Triple<String, String, Boolean> fdesc = getFileDescriptor(lv, c, false);
|
FileArgument fdesc = FileArgument.from(lv, c, false, FileArgument.Reason.CREATE);
|
||||||
|
|
||||||
|
|
||||||
boolean success;
|
boolean success;
|
||||||
if (fdesc.getMiddle().equals("nbt"))
|
if (fdesc.type == FileArgument.Type.NBT)
|
||||||
{
|
{
|
||||||
Value val = lv.get(2).evalValue(c);
|
Value val = lv.get(2).evalValue(c);
|
||||||
NBTSerializableValue tagValue = (val instanceof NBTSerializableValue)
|
NBTSerializableValue tagValue = (val instanceof NBTSerializableValue)
|
||||||
? (NBTSerializableValue) val
|
? (NBTSerializableValue) val
|
||||||
: new NBTSerializableValue(val.getString());
|
: new NBTSerializableValue(val.getString());
|
||||||
Tag tag = tagValue.getTag();
|
Tag tag = tagValue.getTag();
|
||||||
success = ((CarpetScriptHost) c.host).writeTagFile(tag, fdesc.getLeft(), fdesc.getRight());
|
success = ((CarpetScriptHost) c.host).writeTagFile(tag, fdesc);
|
||||||
}
|
}
|
||||||
else if (fdesc.getMiddle().equals("json"))
|
else if (fdesc.type == FileArgument.Type.JSON)
|
||||||
{
|
{
|
||||||
List<String> data = Collections.singletonList(gson.toJson(lv.get(2).evalValue(c).toJson()));
|
List<String> data = Collections.singletonList(GSON.toJson(lv.get(2).evalValue(c).toJson()));
|
||||||
((CarpetScriptHost) c.host).removeResourceFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle());
|
((CarpetScriptHost) c.host).removeResourceFile(fdesc);
|
||||||
success = ((CarpetScriptHost) c.host).appendLogFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle(), data);
|
success = ((CarpetScriptHost) c.host).appendLogFile(fdesc, data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -945,27 +966,22 @@ public class Auxiliary {
|
|||||||
data.add(lv.get(i).evalValue(c).getString());
|
data.add(lv.get(i).evalValue(c).getString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
success = ((CarpetScriptHost) c.host).appendLogFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle(), data);
|
success = ((CarpetScriptHost) c.host).appendLogFile(fdesc, data);
|
||||||
}
|
}
|
||||||
return success?LazyValue.TRUE:LazyValue.FALSE;
|
return success?LazyValue.TRUE:LazyValue.FALSE;
|
||||||
});
|
});
|
||||||
|
|
||||||
//write_file
|
|
||||||
|
|
||||||
expression.addLazyFunction("load_app_data", -1, (c, t, lv) ->
|
expression.addLazyFunction("load_app_data", -1, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
String file = null;
|
FileArgument fdesc = new FileArgument(null, FileArgument.Type.NBT, null, false, false, FileArgument.Reason.READ);
|
||||||
boolean shared = false;
|
|
||||||
if (lv.size()>0)
|
if (lv.size()>0)
|
||||||
{
|
{
|
||||||
c.host.issueDeprecation("load_app_data(...) with arguments");
|
c.host.issueDeprecation("load_app_data(...) with arguments");
|
||||||
file = recognizeResource(lv.get(0).evalValue(c), false);
|
String resource = recognizeResource(lv.get(0).evalValue(c), false);
|
||||||
if (lv.size() > 1)
|
boolean shared = lv.size() > 1 && lv.get(1).evalValue(c).getBoolean();
|
||||||
{
|
fdesc = new FileArgument(resource, FileArgument.Type.NBT, null, false, shared, FileArgument.Reason.READ);
|
||||||
shared = lv.get(1).evalValue(c).getBoolean();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Tag state = ((CarpetScriptHost)((CarpetContext)c).host).readFileTag(file, shared);
|
Tag state = ((CarpetScriptHost) c.host).readFileTag(fdesc);
|
||||||
if (state == null)
|
if (state == null)
|
||||||
return (cc, tt) -> Value.NULL;
|
return (cc, tt) -> Value.NULL;
|
||||||
Value retVal = new NBTSerializableValue(state);
|
Value retVal = new NBTSerializableValue(state);
|
||||||
@@ -977,22 +993,19 @@ public class Auxiliary {
|
|||||||
if (lv.size() == 0)
|
if (lv.size() == 0)
|
||||||
throw new InternalExpressionException("'store_app_data' needs NBT tag and an optional file");
|
throw new InternalExpressionException("'store_app_data' needs NBT tag and an optional file");
|
||||||
Value val = lv.get(0).evalValue(c);
|
Value val = lv.get(0).evalValue(c);
|
||||||
String file = null;
|
FileArgument fdesc = new FileArgument(null, FileArgument.Type.NBT, null, false, false, FileArgument.Reason.CREATE);
|
||||||
boolean shared = false;
|
|
||||||
if (lv.size()>1)
|
if (lv.size()>1)
|
||||||
{
|
{
|
||||||
c.host.issueDeprecation("store_app_data(...) with more than one argument");
|
c.host.issueDeprecation("store_app_data(...) with more than one argument");
|
||||||
file = recognizeResource(lv.get(1).evalValue(c), false);
|
String resource = recognizeResource(lv.get(1).evalValue(c), false);
|
||||||
if (lv.size() > 2)
|
boolean shared = lv.size() > 2 && lv.get(2).evalValue(c).getBoolean();
|
||||||
{
|
fdesc = new FileArgument(resource, FileArgument.Type.NBT, null, false, shared, FileArgument.Reason.CREATE);
|
||||||
shared = lv.get(2).evalValue(c).getBoolean();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
NBTSerializableValue tagValue = (val instanceof NBTSerializableValue)
|
NBTSerializableValue tagValue = (val instanceof NBTSerializableValue)
|
||||||
? (NBTSerializableValue) val
|
? (NBTSerializableValue) val
|
||||||
: new NBTSerializableValue(val.getString());
|
: new NBTSerializableValue(val.getString());
|
||||||
Tag tag = tagValue.getTag();
|
Tag tag = tagValue.getTag();
|
||||||
boolean success = ((CarpetScriptHost)((CarpetContext)c).host).writeTagFile(tag, file, shared);
|
boolean success = ((CarpetScriptHost) c.host).writeTagFile(tag, fdesc);
|
||||||
return success?LazyValue.TRUE:LazyValue.FALSE;
|
return success?LazyValue.TRUE:LazyValue.FALSE;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1089,8 +1102,179 @@ public class Auxiliary {
|
|||||||
if (old_nbt == null) return LazyValue.NULL;
|
if (old_nbt == null) return LazyValue.NULL;
|
||||||
return (_c, _t) -> new NBTSerializableValue(old_nbt);
|
return (_c, _t) -> new NBTSerializableValue(old_nbt);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// script run create_datapack('foo', {'foo' -> {'bar.json' -> {'c' -> true,'d' -> false,'e' -> {'foo' -> [1,2,3]},'a' -> 'foobar','b' -> 5}}})
|
||||||
|
expression.addLazyFunction("create_datapack", 2, (c, t, lv) -> {
|
||||||
|
CarpetContext cc = (CarpetContext)c;
|
||||||
|
String origName = lv.get(0).evalValue(c).getString();
|
||||||
|
String name = InputValidator.validateSimpleString(origName, true);
|
||||||
|
MinecraftServer server = cc.s.getMinecraftServer();
|
||||||
|
for (String dpName : server.getDataPackManager().getNames())
|
||||||
|
{
|
||||||
|
if (dpName.equalsIgnoreCase("file/"+name+".zip") ||
|
||||||
|
dpName.equalsIgnoreCase("file/"+name))
|
||||||
|
return LazyValue.NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
Value dpdata = lv.get(1).evalValue(c);
|
||||||
|
if (!(dpdata instanceof MapValue))
|
||||||
|
throw new InternalExpressionException("datapack data needs to be a valid map type");
|
||||||
|
ResourcePackManager packManager = server.getDataPackManager();
|
||||||
|
Path dbFloder = server.getSavePath(WorldSavePath.DATAPACKS);
|
||||||
|
Path packFloder = dbFloder.resolve(name+".zip");
|
||||||
|
if (Files.exists(packFloder) || Files.exists(dbFloder.resolve(name))) return LazyValue.NULL;
|
||||||
|
Boolean [] successful = new Boolean[]{true};
|
||||||
|
server.submitAndJoin( () ->
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
//Files.createDirectory(packFloder);
|
||||||
|
try (FileSystem zipfs = FileSystems.newFileSystem(URI.create("jar:" + packFloder.toUri().toString()), ImmutableMap.of("create", "true"))) {
|
||||||
|
Path zipRoot = zipfs.getPath("/");
|
||||||
|
zipValueToJson(zipRoot.resolve("pack.mcmeta"), MapValue.wrap(
|
||||||
|
ImmutableMap.of(StringValue.of("pack"), MapValue.wrap(ImmutableMap.of(
|
||||||
|
StringValue.of("pack_format"), new NumericValue(SharedConstants.getGameVersion().getPackVersion()),
|
||||||
|
StringValue.of("description"), StringValue.of(name),
|
||||||
|
StringValue.of("source"), StringValue.of("scarpet")
|
||||||
|
)))
|
||||||
|
));
|
||||||
|
walkTheDPMap((MapValue) dpdata, zipRoot);
|
||||||
|
}
|
||||||
|
packManager.scanPacks();
|
||||||
|
ResourcePackProfile resourcePackProfile = packManager.getProfile("file/" + name + ".zip");
|
||||||
|
if (resourcePackProfile == null || packManager.getEnabledProfiles().contains(resourcePackProfile)) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
List<ResourcePackProfile> list = Lists.newArrayList(packManager.getEnabledProfiles());
|
||||||
|
resourcePackProfile.getInitialPosition().insert(list, resourcePackProfile, p -> p, false);
|
||||||
|
|
||||||
|
|
||||||
|
server.reloadResources(list.stream().map(ResourcePackProfile::getName).collect(Collectors.toList())).
|
||||||
|
exceptionally(exc -> {
|
||||||
|
successful[0] = false;
|
||||||
|
return null;
|
||||||
|
}).join();
|
||||||
|
if (!successful[0]) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
} catch (IOException e)
|
||||||
|
{
|
||||||
|
successful[0] = false;
|
||||||
|
try {
|
||||||
|
FileUtils.forceDelete(packFloder.toFile());
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
throw new InternalExpressionException("Failed to install a datapack and failed to clean up after it");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return successful[0]?LazyValue.TRUE:LazyValue.FALSE;
|
||||||
|
});
|
||||||
|
|
||||||
|
expression.addLazyFunction("enable_hidden_dimensions", 0, (c, t, lv) -> {
|
||||||
|
CarpetContext cc = (CarpetContext)c;
|
||||||
|
// from minecraft.server.Main.main
|
||||||
|
MinecraftServer server = cc.s.getMinecraftServer();
|
||||||
|
LevelStorage.Session session = ((MinecraftServerInterface)server).getCMSession();
|
||||||
|
DataPackSettings dataPackSettings = session.getDataPackSettings();
|
||||||
|
ResourcePackManager resourcePackManager = server.getDataPackManager();
|
||||||
|
DataPackSettings dataPackSettings2 = MinecraftServer.loadDataPacks(resourcePackManager, dataPackSettings == null ? DataPackSettings.SAFE_MODE : dataPackSettings, false);
|
||||||
|
ServerResourceManager serverRM = ((MinecraftServerInterface)server).getResourceManager();
|
||||||
|
ReloadableResourceManagerImpl resourceManager = (ReloadableResourceManagerImpl) serverRM.getResourceManager();
|
||||||
|
|
||||||
|
//believe the other one will fillup based on the datapacks only.
|
||||||
|
resourceManager.close();
|
||||||
|
resourcePackManager.createResourcePacks().forEach(resourceManager::addPack);
|
||||||
|
|
||||||
|
//not sure its needed, but doesn't seem to have a negative effect and might be used in some custom shtuff
|
||||||
|
serverRM.loadRegistryTags();
|
||||||
|
|
||||||
|
RegistryOps<Tag> registryOps = RegistryOps.of(NbtOps.INSTANCE, serverRM.getResourceManager(), (DynamicRegistryManager.Impl) server.getRegistryManager());
|
||||||
|
SaveProperties saveProperties = session.readLevelProperties(registryOps, dataPackSettings2);
|
||||||
|
if (saveProperties == null) return LazyValue.NULL;
|
||||||
|
//session.backupLevelDataFile(server.getRegistryManager(), saveProperties); // no need
|
||||||
|
|
||||||
|
// MinecraftServer.createWorlds
|
||||||
|
// save properties should now contain dimension settings
|
||||||
|
GeneratorOptions generatorOptions = saveProperties.getGeneratorOptions();
|
||||||
|
boolean bl = generatorOptions.isDebugWorld();
|
||||||
|
long l = generatorOptions.getSeed();
|
||||||
|
long m = BiomeAccess.hashSeed(l);
|
||||||
|
Map<RegistryKey<World>, ServerWorld> existing_worlds = ((MinecraftServerInterface)server).getCMWorlds();
|
||||||
|
List<Value> addeds = new ArrayList<>();
|
||||||
|
for (Map.Entry<RegistryKey<DimensionOptions>, DimensionOptions> entry : generatorOptions.getDimensions().getEntries()) {
|
||||||
|
RegistryKey<DimensionOptions> registryKey = entry.getKey();
|
||||||
|
if (!existing_worlds.containsKey(registryKey))
|
||||||
|
{
|
||||||
|
addeds.add(ValueConversions.of(registryKey.getValue()));
|
||||||
|
RegistryKey<World> registryKey2 = RegistryKey.of(Registry.DIMENSION, registryKey.getValue());
|
||||||
|
DimensionType dimensionType3 = entry.getValue().getDimensionType();
|
||||||
|
ChunkGenerator chunkGenerator3 = entry.getValue().getChunkGenerator();
|
||||||
|
UnmodifiableLevelProperties unmodifiableLevelProperties = new UnmodifiableLevelProperties(saveProperties, ((ServerWorldInterface) server.getOverworld()).getWorldPropertiesCM());
|
||||||
|
ServerWorld serverWorld2 = new ServerWorld(server, Util.getMainWorkerExecutor(), session, unmodifiableLevelProperties, registryKey2, dimensionType3, WorldTools.NOOP_LISTENER, chunkGenerator3, bl, m, ImmutableList.of(), false);
|
||||||
|
server.getOverworld().getWorldBorder().addListener(new WorldBorderListener.WorldBorderSyncer(serverWorld2.getWorldBorder()));
|
||||||
|
existing_worlds.put(registryKey2, serverWorld2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value ret = ListValue.wrap(addeds);
|
||||||
|
return (_c, _t) -> ret;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void zipValueToJson(Path path, Value output) throws IOException
|
||||||
|
{
|
||||||
|
JsonElement element = output.toJson();
|
||||||
|
if (element == null)
|
||||||
|
throw new InternalExpressionException("Cannot interpret "+output.getPrettyString()+" as a json object");
|
||||||
|
String string = GSON.toJson(element);
|
||||||
|
Files.createDirectories(path.getParent());
|
||||||
|
BufferedWriter bufferedWriter = Files.newBufferedWriter(path);
|
||||||
|
Throwable incident = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bufferedWriter.write(string);
|
||||||
|
}
|
||||||
|
catch (Throwable shitHappened)
|
||||||
|
{
|
||||||
|
incident = shitHappened;
|
||||||
|
throw shitHappened;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (incident != null) {
|
||||||
|
try {
|
||||||
|
bufferedWriter.close();
|
||||||
|
} catch (Throwable otherShitHappened) {
|
||||||
|
incident.addSuppressed(otherShitHappened);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bufferedWriter.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void walkTheDPMap(MapValue node, Path path) throws IOException
|
||||||
|
{
|
||||||
|
Map<Value,Value> items = node.getMap();
|
||||||
|
for (Map.Entry<Value, Value> entry : items.entrySet())
|
||||||
|
{
|
||||||
|
Value val = entry.getValue();
|
||||||
|
String strkey = entry.getKey().getString();
|
||||||
|
Path child = path.resolve(strkey);
|
||||||
|
if (strkey.endsWith(".json"))
|
||||||
|
{
|
||||||
|
zipValueToJson(child, val);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!(val instanceof MapValue)) throw new InternalExpressionException("Value of "+strkey+" should be a map");
|
||||||
|
Files.createDirectory(child);
|
||||||
|
walkTheDPMap((MapValue) val, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static <T> Stat<T> getStat(StatType<T> type, Identifier id)
|
private static <T> Stat<T> getStat(StatType<T> type, Identifier id)
|
||||||
{
|
{
|
||||||
T key = type.getRegistry().get(id);
|
T key = type.getRegistry().get(id);
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import carpet.script.CarpetContext;
|
|||||||
import carpet.script.Expression;
|
import carpet.script.Expression;
|
||||||
import carpet.script.LazyValue;
|
import carpet.script.LazyValue;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import carpet.script.exception.ThrowStatement;
|
||||||
|
import carpet.script.exception.Throwables;
|
||||||
import carpet.script.value.ListValue;
|
import carpet.script.value.ListValue;
|
||||||
import carpet.script.value.NBTSerializableValue;
|
import carpet.script.value.NBTSerializableValue;
|
||||||
import carpet.script.value.NullValue;
|
import carpet.script.value.NullValue;
|
||||||
@@ -91,7 +93,7 @@ public class Inventories {
|
|||||||
Item item = NBTSerializableValue.parseItem(lv.get(0).evalValue(c).getString()).getItem();
|
Item item = NBTSerializableValue.parseItem(lv.get(0).evalValue(c).getString()).getItem();
|
||||||
if (lv.size() == 1)
|
if (lv.size() == 1)
|
||||||
{
|
{
|
||||||
Value ret = ListValue.wrap(tagManager.getItems().getTagsFor(item).stream().map(ValueConversions::of).collect(Collectors.toList()));
|
Value ret = ListValue.wrap(tagManager.getItems().getTags().entrySet().stream().filter(e -> e.getValue().contains(item)).map(e -> ValueConversions.of(e.getKey())).collect(Collectors.toList()));
|
||||||
return (_c, _t) -> ret;
|
return (_c, _t) -> ret;
|
||||||
}
|
}
|
||||||
String tag = lv.get(1).evalValue(c).getString();
|
String tag = lv.get(1).evalValue(c).getString();
|
||||||
@@ -119,14 +121,7 @@ public class Inventories {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<Recipe<?>> recipes;
|
List<Recipe<?>> recipes;
|
||||||
try
|
recipes = ((RecipeManagerInterface) cc.s.getMinecraftServer().getRecipeManager()).getAllMatching(type, new Identifier(recipeName));
|
||||||
{
|
|
||||||
recipes = ((RecipeManagerInterface) cc.s.getMinecraftServer().getRecipeManager()).getAllMatching(type, new Identifier(recipeName));
|
|
||||||
}
|
|
||||||
catch (InvalidIdentifierException ignored)
|
|
||||||
{
|
|
||||||
return LazyValue.NULL;
|
|
||||||
}
|
|
||||||
if (recipes.isEmpty())
|
if (recipes.isEmpty())
|
||||||
return LazyValue.NULL;
|
return LazyValue.NULL;
|
||||||
List<Value> recipesOutput = new ArrayList<>();
|
List<Value> recipesOutput = new ArrayList<>();
|
||||||
@@ -197,17 +192,9 @@ public class Inventories {
|
|||||||
{
|
{
|
||||||
String itemStr = lv.get(0).evalValue(c).getString();
|
String itemStr = lv.get(0).evalValue(c).getString();
|
||||||
Item item;
|
Item item;
|
||||||
try
|
Identifier id = new Identifier(itemStr);
|
||||||
{
|
item = Registry.ITEM.getOrEmpty(id).orElseThrow(() -> new ThrowStatement(itemStr, Throwables.UNKNOWN_ITEM));
|
||||||
Identifier id = new Identifier(itemStr);
|
|
||||||
item = Registry.ITEM.get(id);
|
|
||||||
if (item == Items.AIR && !id.getPath().equalsIgnoreCase("air"))
|
|
||||||
throw new InvalidIdentifierException("boo");
|
|
||||||
}
|
|
||||||
catch (InvalidIdentifierException ignored)
|
|
||||||
{
|
|
||||||
throw new InternalExpressionException("Incorrect item: "+itemStr);
|
|
||||||
}
|
|
||||||
if (!item.hasRecipeRemainder()) return LazyValue.NULL;
|
if (!item.hasRecipeRemainder()) return LazyValue.NULL;
|
||||||
Value ret = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.ITEM.getId(item.getRecipeRemainder())));
|
Value ret = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.ITEM.getId(item.getRecipeRemainder())));
|
||||||
return (_c, _t ) -> ret;
|
return (_c, _t ) -> ret;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import carpet.script.CarpetContext;
|
|||||||
import carpet.script.Expression;
|
import carpet.script.Expression;
|
||||||
import carpet.script.LazyValue;
|
import carpet.script.LazyValue;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import carpet.script.exception.ThrowStatement;
|
||||||
|
import carpet.script.exception.Throwables;
|
||||||
import carpet.script.value.EntityValue;
|
import carpet.script.value.EntityValue;
|
||||||
import carpet.script.value.ListValue;
|
import carpet.script.value.ListValue;
|
||||||
import carpet.script.value.NullValue;
|
import carpet.script.value.NullValue;
|
||||||
@@ -33,6 +35,8 @@ import net.minecraft.util.Formatting;
|
|||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.InvalidIdentifierException;
|
import net.minecraft.util.InvalidIdentifierException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class Scoreboards {
|
public class Scoreboards {
|
||||||
@@ -77,16 +81,27 @@ public class Scoreboards {
|
|||||||
Value ret = ListValue.wrap(scoreboard.getAllPlayerScores(objective).stream().map(s -> new StringValue(s.getPlayerName())).collect(Collectors.toList()));
|
Value ret = ListValue.wrap(scoreboard.getAllPlayerScores(objective).stream().map(s -> new StringValue(s.getPlayerName())).collect(Collectors.toList()));
|
||||||
return (_c, _t) -> ret;
|
return (_c, _t) -> ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
String key = getScoreboardKeyFromValue(lv.get(1).evalValue(c));
|
String key = getScoreboardKeyFromValue(lv.get(1).evalValue(c));
|
||||||
if (!scoreboard.playerHasObjective(key, objective) && lv.size()==2)
|
if(lv.size()==2) {
|
||||||
|
if(scoreboard.playerHasObjective(key, objective)) {
|
||||||
|
return (_c,_t) -> NumericValue.of(scoreboard.getPlayerScore(key,objective).getScore());
|
||||||
|
}
|
||||||
return LazyValue.NULL;
|
return LazyValue.NULL;
|
||||||
ScoreboardPlayerScore scoreboardPlayerScore = scoreboard.getPlayerScore(key, objective);
|
|
||||||
Value retval = new NumericValue(scoreboardPlayerScore.getScore());
|
|
||||||
if (lv.size() > 2)
|
|
||||||
{
|
|
||||||
scoreboardPlayerScore.setScore(NumericValue.asNumber(lv.get(2).evalValue(c)).getInt());
|
|
||||||
}
|
}
|
||||||
return (_c, _t) -> retval;
|
|
||||||
|
Value value = lv.get(2).evalValue(c);
|
||||||
|
if(value.isNull()) {
|
||||||
|
ScoreboardPlayerScore score = scoreboard.getPlayerScore(key, objective);
|
||||||
|
scoreboard.resetPlayerScore(key,objective);
|
||||||
|
return (_c,_t) -> NumericValue.of(score.getScore());
|
||||||
|
}
|
||||||
|
if (value instanceof NumericValue) {
|
||||||
|
ScoreboardPlayerScore score = scoreboard.getPlayerScore(key, objective);
|
||||||
|
score.setScore(NumericValue.asNumber(value).getInt());
|
||||||
|
return (_c,_t) -> NumericValue.of(score.getScore());
|
||||||
|
}
|
||||||
|
throw new InternalExpressionException("'scoreboard' requires a number or null as the third parameter");
|
||||||
});
|
});
|
||||||
|
|
||||||
expression.addLazyFunction("scoreboard_remove", -1, (c, t, lv)->
|
expression.addLazyFunction("scoreboard_remove", -1, (c, t, lv)->
|
||||||
@@ -131,12 +146,13 @@ public class Scoreboards {
|
|||||||
criterion = ScoreboardCriterion.createStatCriterion(critetionName).orElse(null);
|
criterion = ScoreboardCriterion.createStatCriterion(critetionName).orElse(null);
|
||||||
if (criterion==null)
|
if (criterion==null)
|
||||||
{
|
{
|
||||||
throw new InternalExpressionException("Unknown scoreboard criterion: "+critetionName);
|
throw new ThrowStatement(critetionName, Throwables.UNKNOWN_CRITERION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScoreboardObjective objective = scoreboard.getObjective(objectiveName);
|
ScoreboardObjective objective = scoreboard.getObjective(objectiveName);
|
||||||
if (objective != null) {
|
if (objective != null) {
|
||||||
|
c.host.issueDeprecation("reading or modifying an objective's criterion with scoreboard_add");
|
||||||
if(lv.size() == 1) return (_c, _t) -> StringValue.of(objective.getCriterion().getName());
|
if(lv.size() == 1) return (_c, _t) -> StringValue.of(objective.getCriterion().getName());
|
||||||
if(objective.getCriterion().equals(criterion) || lv.size() == 1) return LazyValue.NULL;
|
if(objective.getCriterion().equals(criterion) || lv.size() == 1) return LazyValue.NULL;
|
||||||
((Scoreboard_scarpetMixin)scoreboard).getObjectivesByCriterion().get(objective.getCriterion()).remove(objective);
|
((Scoreboard_scarpetMixin)scoreboard).getObjectivesByCriterion().get(objective.getCriterion()).remove(objective);
|
||||||
@@ -150,6 +166,72 @@ public class Scoreboards {
|
|||||||
return LazyValue.TRUE;
|
return LazyValue.TRUE;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expression.addLazyFunction("scoreboard_property", -1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
if(lv.size() < 2) throw new InternalExpressionException("'scoreboard_property' requires at least two parameters");
|
||||||
|
CarpetContext cc = (CarpetContext)c;
|
||||||
|
Scoreboard scoreboard = cc.s.getMinecraftServer().getScoreboard();
|
||||||
|
ScoreboardObjective objective = scoreboard.getObjective(lv.get(0).evalValue(c).getString());
|
||||||
|
if(objective == null) return LazyValue.NULL;
|
||||||
|
|
||||||
|
boolean modify = lv.size() > 2;
|
||||||
|
Value setValue = null;
|
||||||
|
if(modify) {
|
||||||
|
setValue = lv.get(2).evalValue(c);
|
||||||
|
}
|
||||||
|
String property = lv.get(1).evalValue(c).getString();
|
||||||
|
switch (property) {
|
||||||
|
case "criterion":
|
||||||
|
if(modify) {
|
||||||
|
ScoreboardCriterion criterion = ScoreboardCriterion.createStatCriterion(setValue.getString()).orElse(null);
|
||||||
|
if (criterion==null) throw new InternalExpressionException("Unknown scoreboard criterion: "+ setValue.getString());
|
||||||
|
if(objective.getCriterion().equals(criterion) || lv.size() == 1) return LazyValue.FALSE;
|
||||||
|
((Scoreboard_scarpetMixin)scoreboard).getObjectivesByCriterion().get(objective.getCriterion()).remove(objective);
|
||||||
|
((ScoreboardObjective_scarpetMixin) objective).setCriterion(criterion);
|
||||||
|
(((Scoreboard_scarpetMixin)scoreboard).getObjectivesByCriterion().computeIfAbsent(criterion, (criterion1) -> Lists.newArrayList())).add(objective);
|
||||||
|
scoreboard.updateObjective(objective);
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
}
|
||||||
|
return (_c, _t) -> StringValue.of(objective.getCriterion().getName());
|
||||||
|
case "display_name":
|
||||||
|
if(modify) {
|
||||||
|
Text text = (setValue instanceof FormattedTextValue)?((FormattedTextValue) setValue).getText():new LiteralText(setValue.getString());
|
||||||
|
objective.setDisplayName(text);
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
}
|
||||||
|
return (_c, _t) -> new FormattedTextValue(objective.getDisplayName());
|
||||||
|
case "display_slot":
|
||||||
|
if(modify) {
|
||||||
|
int slotId = Scoreboard.getDisplaySlotId(setValue.getString());
|
||||||
|
if(slotId == -1) throw new InternalExpressionException("Unknown scoreboard display slot: " + setValue.getString());
|
||||||
|
if(objective.equals(scoreboard.getObjectiveForSlot(slotId))) {
|
||||||
|
return LazyValue.FALSE;
|
||||||
|
}
|
||||||
|
scoreboard.setObjectiveSlot(slotId,objective);
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Value> slots = new ArrayList<>();
|
||||||
|
for(int i = 0; i < 19; i++) {
|
||||||
|
if (scoreboard.getObjectiveForSlot(i) == objective) {
|
||||||
|
String slotName = Scoreboard.getDisplaySlotName(i);
|
||||||
|
slots.add(StringValue.of(slotName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (_c, _t) -> ListValue.wrap(slots);
|
||||||
|
case "render_type":
|
||||||
|
if(modify) {
|
||||||
|
ScoreboardCriterion.RenderType renderType = ScoreboardCriterion.RenderType.getType(setValue.getString().toLowerCase());
|
||||||
|
if(objective.getRenderType().equals(renderType)) return LazyValue.FALSE;
|
||||||
|
objective.setRenderType(renderType);
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
}
|
||||||
|
return (_c, _t) -> StringValue.of(objective.getRenderType().getName());
|
||||||
|
default:
|
||||||
|
throw new InternalExpressionException("scoreboard property '" + property + "' is not a valid property");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
expression.addLazyFunction("scoreboard_display", 2, (c, t, lv) ->
|
expression.addLazyFunction("scoreboard_display", 2, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
CarpetContext cc = (CarpetContext)c;
|
CarpetContext cc = (CarpetContext)c;
|
||||||
@@ -408,7 +490,7 @@ public class Scoreboards {
|
|||||||
team.setSuffix(suffix);
|
team.setSuffix(suffix);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new InternalExpressionException("team property " + propertyVal.getString() + " is not a valid property");
|
throw new InternalExpressionException("team property '" + propertyVal.getString() + "' is not a valid property");
|
||||||
}
|
}
|
||||||
return LazyValue.TRUE;
|
return LazyValue.TRUE;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import carpet.script.LazyValue;
|
|||||||
import carpet.script.argument.BlockArgument;
|
import carpet.script.argument.BlockArgument;
|
||||||
import carpet.script.argument.Vector3Argument;
|
import carpet.script.argument.Vector3Argument;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import carpet.script.exception.ThrowStatement;
|
||||||
|
import carpet.script.exception.Throwables;
|
||||||
import carpet.script.utils.BiomeInfo;
|
import carpet.script.utils.BiomeInfo;
|
||||||
import carpet.script.utils.WorldTools;
|
import carpet.script.utils.WorldTools;
|
||||||
import carpet.script.value.BlockValue;
|
import carpet.script.value.BlockValue;
|
||||||
@@ -48,6 +50,7 @@ import net.minecraft.enchantment.Enchantments;
|
|||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
import net.minecraft.entity.FallingBlockEntity;
|
import net.minecraft.entity.FallingBlockEntity;
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
import net.minecraft.entity.ai.pathing.NavigationType;
|
import net.minecraft.entity.ai.pathing.NavigationType;
|
||||||
import net.minecraft.item.BlockItem;
|
import net.minecraft.item.BlockItem;
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
@@ -59,7 +62,7 @@ import net.minecraft.item.SwordItem;
|
|||||||
import net.minecraft.item.TridentItem;
|
import net.minecraft.item.TridentItem;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.network.packet.s2c.play.ExplosionS2CPacket;
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
import net.minecraft.server.world.ChunkTicket;
|
import net.minecraft.server.world.ChunkTicket;
|
||||||
import net.minecraft.server.world.ChunkTicketType;
|
import net.minecraft.server.world.ChunkTicketType;
|
||||||
@@ -78,6 +81,7 @@ import net.minecraft.util.math.BlockBox;
|
|||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.ChunkPos;
|
import net.minecraft.util.math.ChunkPos;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.util.registry.Registry;
|
import net.minecraft.util.registry.Registry;
|
||||||
import net.minecraft.world.Heightmap;
|
import net.minecraft.world.Heightmap;
|
||||||
import net.minecraft.world.LightType;
|
import net.minecraft.world.LightType;
|
||||||
@@ -87,6 +91,7 @@ import net.minecraft.world.biome.Biome;
|
|||||||
import net.minecraft.world.chunk.Chunk;
|
import net.minecraft.world.chunk.Chunk;
|
||||||
import net.minecraft.world.chunk.ChunkStatus;
|
import net.minecraft.world.chunk.ChunkStatus;
|
||||||
import net.minecraft.world.chunk.WorldChunk;
|
import net.minecraft.world.chunk.WorldChunk;
|
||||||
|
import net.minecraft.world.explosion.Explosion;
|
||||||
import net.minecraft.world.gen.ChunkRandom;
|
import net.minecraft.world.gen.ChunkRandom;
|
||||||
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||||
import net.minecraft.world.gen.feature.ConfiguredStructureFeature;
|
import net.minecraft.world.gen.feature.ConfiguredStructureFeature;
|
||||||
@@ -95,6 +100,7 @@ import net.minecraft.world.level.ServerWorldProperties;
|
|||||||
import net.minecraft.world.poi.PointOfInterest;
|
import net.minecraft.world.poi.PointOfInterest;
|
||||||
import net.minecraft.world.poi.PointOfInterestStorage;
|
import net.minecraft.world.poi.PointOfInterestStorage;
|
||||||
import net.minecraft.world.poi.PointOfInterestType;
|
import net.minecraft.world.poi.PointOfInterestType;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -106,7 +112,6 @@ import java.util.Locale;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@@ -121,8 +126,7 @@ public class WorldAccess {
|
|||||||
put("unknown", ChunkTicketType.UNKNOWN); // unknown
|
put("unknown", ChunkTicketType.UNKNOWN); // unknown
|
||||||
}};
|
}};
|
||||||
// dummy entity for dummy requirements in the loot tables (see snowball)
|
// dummy entity for dummy requirements in the loot tables (see snowball)
|
||||||
private static FallingBlockEntity DUMMY_ENTITY = new FallingBlockEntity(EntityType.FALLING_BLOCK, null);
|
private static FallingBlockEntity DUMMY_ENTITY = null;
|
||||||
|
|
||||||
private static LazyValue booleanStateTest(
|
private static LazyValue booleanStateTest(
|
||||||
Context c,
|
Context c,
|
||||||
String name,
|
String name,
|
||||||
@@ -336,8 +340,8 @@ public class WorldAccess {
|
|||||||
String poiType = lv.get(locator.offset+1).evalValue(c).getString().toLowerCase(Locale.ROOT);
|
String poiType = lv.get(locator.offset+1).evalValue(c).getString().toLowerCase(Locale.ROOT);
|
||||||
if (!"any".equals(poiType))
|
if (!"any".equals(poiType))
|
||||||
{
|
{
|
||||||
PointOfInterestType type = Registry.POINT_OF_INTEREST_TYPE.get(new Identifier(poiType));
|
PointOfInterestType type = Registry.POINT_OF_INTEREST_TYPE.getOrEmpty(new Identifier(poiType))
|
||||||
if (type == PointOfInterestType.UNEMPLOYED && !"unemployed".equals(poiType)) return LazyValue.NULL;
|
.orElseThrow(() -> new ThrowStatement(poiType, Throwables.UNKNOWN_POI));
|
||||||
condition = (tt) -> tt == type;
|
condition = (tt) -> tt == type;
|
||||||
}
|
}
|
||||||
if (locator.offset + 2 < lv.size())
|
if (locator.offset + 2 < lv.size())
|
||||||
@@ -391,9 +395,8 @@ public class WorldAccess {
|
|||||||
return LazyValue.FALSE;
|
return LazyValue.FALSE;
|
||||||
}
|
}
|
||||||
String poiTypeString = poi.getString().toLowerCase(Locale.ROOT);
|
String poiTypeString = poi.getString().toLowerCase(Locale.ROOT);
|
||||||
PointOfInterestType type = Registry.POINT_OF_INTEREST_TYPE.get(new Identifier(poiTypeString));
|
PointOfInterestType type = Registry.POINT_OF_INTEREST_TYPE.getOrEmpty(new Identifier(poiTypeString))
|
||||||
// solving lack of null with defaulted registries
|
.orElseThrow(() -> new ThrowStatement(poiTypeString, Throwables.UNKNOWN_POI));
|
||||||
if (type == PointOfInterestType.UNEMPLOYED && !"unemployed".equals(poiTypeString)) throw new InternalExpressionException("Unknown POI type: "+poiTypeString);
|
|
||||||
int occupancy = 0;
|
int occupancy = 0;
|
||||||
if (locator.offset + 1 < lv.size())
|
if (locator.offset + 1 < lv.size())
|
||||||
{
|
{
|
||||||
@@ -872,9 +875,8 @@ public class WorldAccess {
|
|||||||
{
|
{
|
||||||
playerBreak = true;
|
playerBreak = true;
|
||||||
String itemString = val.getString();
|
String itemString = val.getString();
|
||||||
item = Registry.ITEM.get(new Identifier(itemString));
|
item = Registry.ITEM.getOrEmpty(new Identifier(itemString))
|
||||||
if (item == Items.AIR && !itemString.equals("air"))
|
.orElseThrow(() -> new ThrowStatement(itemString, Throwables.UNKNOWN_ITEM));
|
||||||
throw new InternalExpressionException("Incorrect item: " + itemString);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CompoundTag tag = null;
|
CompoundTag tag = null;
|
||||||
@@ -932,6 +934,7 @@ public class WorldAccess {
|
|||||||
{
|
{
|
||||||
if (how > 0)
|
if (how > 0)
|
||||||
tool.addEnchantment(Enchantments.FORTUNE, (int) how);
|
tool.addEnchantment(Enchantments.FORTUNE, (int) how);
|
||||||
|
if (DUMMY_ENTITY == null) DUMMY_ENTITY = new FallingBlockEntity(EntityType.FALLING_BLOCK, null);
|
||||||
Block.dropStacks(state, world, where, be, DUMMY_ENTITY, tool);
|
Block.dropStacks(state, world, where, be, DUMMY_ENTITY, tool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -972,6 +975,88 @@ public class WorldAccess {
|
|||||||
return success ? LazyValue.TRUE : LazyValue.FALSE;
|
return success ? LazyValue.TRUE : LazyValue.FALSE;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expression.addLazyFunction("create_explosion", -1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
if (lv.isEmpty())
|
||||||
|
throw new InternalExpressionException("'create_explosion' requires at least a position to explode");
|
||||||
|
CarpetContext cc = (CarpetContext)c;
|
||||||
|
float powah = 4.0f;
|
||||||
|
Explosion.DestructionType mode = Explosion.DestructionType.BREAK;
|
||||||
|
boolean createFire = false;
|
||||||
|
Entity source = null;
|
||||||
|
LivingEntity attacker = null;
|
||||||
|
Vector3Argument location = Vector3Argument.findIn(cc, lv, 0, false, true);
|
||||||
|
Vec3d pos = location.vec;
|
||||||
|
if (lv.size() > location.offset)
|
||||||
|
{
|
||||||
|
powah = NumericValue.asNumber(lv.get(location.offset).evalValue(c), "explosion power").getFloat();
|
||||||
|
if (powah < 0) throw new InternalExpressionException("Explosion power cannot be negative");
|
||||||
|
if (lv.size() > location.offset+1)
|
||||||
|
{
|
||||||
|
String strval = lv.get(location.offset+1).evalValue(c).getString();
|
||||||
|
try {
|
||||||
|
mode = Explosion.DestructionType.valueOf(strval.toUpperCase(Locale.ROOT));
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException ile) { throw new InternalExpressionException("Illegal explosions block behaviour: "+strval); }
|
||||||
|
if (lv.size() > location.offset+2)
|
||||||
|
{
|
||||||
|
createFire = lv.get(location.offset+2).evalValue(c, Context.BOOLEAN).getBoolean();
|
||||||
|
if (lv.size() > location.offset+3)
|
||||||
|
{
|
||||||
|
Value enVal= lv.get(location.offset+3).evalValue(c);
|
||||||
|
if (enVal.isNull()) {} // is null already
|
||||||
|
else if (enVal instanceof EntityValue)
|
||||||
|
{
|
||||||
|
source = ((EntityValue) enVal).getEntity();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InternalExpressionException("Fourth parameter of the explosion has to be an entity, not "+enVal.getTypeString());
|
||||||
|
}
|
||||||
|
if (lv.size() > location.offset+4)
|
||||||
|
{
|
||||||
|
enVal = lv.get(location.offset+4).evalValue(c);
|
||||||
|
if (enVal.isNull()) {} // is null already
|
||||||
|
else if (enVal instanceof EntityValue)
|
||||||
|
{
|
||||||
|
Entity attackingEntity = ((EntityValue) enVal).getEntity();
|
||||||
|
if (attackingEntity instanceof LivingEntity)
|
||||||
|
{
|
||||||
|
attacker = (LivingEntity) attackingEntity;
|
||||||
|
}
|
||||||
|
else throw new InternalExpressionException("Attacking entity needs to be a living thing, "+
|
||||||
|
ValueConversions.of(Registry.ENTITY_TYPE.getId(attackingEntity.getType())).getString() +" ain't it.");
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InternalExpressionException("Fifth parameter of the explosion has to be a living entity, not "+enVal.getTypeString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LivingEntity theAttacker = attacker;
|
||||||
|
float thePowah = powah;
|
||||||
|
|
||||||
|
// copy of ServerWorld.createExplosion #TRACK#
|
||||||
|
Explosion explosion = new Explosion(cc.s.getWorld(), source, null, null, pos.x, pos.y, pos.z, powah, createFire, mode){
|
||||||
|
@Override
|
||||||
|
public @Nullable LivingEntity getCausingEntity() {
|
||||||
|
return theAttacker;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
explosion.collectBlocksAndDamageEntities();
|
||||||
|
explosion.affectWorld(false);
|
||||||
|
if (mode == Explosion.DestructionType.NONE) explosion.clearAffectedBlocks();
|
||||||
|
cc.s.getWorld().getPlayers().forEach(spe -> {
|
||||||
|
if (spe.squaredDistanceTo(pos) < 4096.0D)
|
||||||
|
spe.networkHandler.sendPacket(new ExplosionS2CPacket(pos.x, pos.y, pos.z, thePowah, explosion.getAffectedBlocks(), explosion.getAffectedPlayers().get(spe)));
|
||||||
|
});
|
||||||
|
return LazyValue.TRUE;
|
||||||
|
});
|
||||||
|
|
||||||
// TODO rename to use_item
|
// TODO rename to use_item
|
||||||
expression.addLazyFunction("place_item", -1, (c, t, lv) ->
|
expression.addLazyFunction("place_item", -1, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
@@ -1127,7 +1212,8 @@ public class WorldAccess {
|
|||||||
BlockArgument blockLocator = BlockArgument.findIn(cc, lv, 0, true);
|
BlockArgument blockLocator = BlockArgument.findIn(cc, lv, 0, true);
|
||||||
if (blockLocator.offset == lv.size())
|
if (blockLocator.offset == lv.size())
|
||||||
{
|
{
|
||||||
Value ret = ListValue.wrap(tagManager.getBlocks().getTagsFor(blockLocator.block.getBlockState().getBlock()).stream().map(ValueConversions::of).collect(Collectors.toList()));
|
Block target = blockLocator.block.getBlockState().getBlock();
|
||||||
|
Value ret = ListValue.wrap(tagManager.getBlocks().getTags().entrySet().stream().filter(e -> e.getValue().contains(target)).map(e -> ValueConversions.of(e.getKey())).collect(Collectors.toList()));
|
||||||
return (_c, _t) -> ret;
|
return (_c, _t) -> ret;
|
||||||
}
|
}
|
||||||
String tag = lv.get(blockLocator.offset).evalValue(c).getString();
|
String tag = lv.get(blockLocator.offset).evalValue(c).getString();
|
||||||
@@ -1151,7 +1237,7 @@ public class WorldAccess {
|
|||||||
if (locator.replacement != null)
|
if (locator.replacement != null)
|
||||||
{
|
{
|
||||||
biome = world.getRegistryManager().get(Registry.BIOME_KEY).get(new Identifier(locator.replacement));
|
biome = world.getRegistryManager().get(Registry.BIOME_KEY).get(new Identifier(locator.replacement));
|
||||||
if (biome == null) return LazyValue.NULL;
|
if (biome == null) throw new ThrowStatement(locator.replacement, Throwables.UNKNOWN_BIOME) ;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1180,9 +1266,8 @@ public class WorldAccess {
|
|||||||
throw new InternalExpressionException("'set_biome' needs a biome name as an argument");
|
throw new InternalExpressionException("'set_biome' needs a biome name as an argument");
|
||||||
String biomeName = lv.get(locator.offset+0).evalValue(c).getString();
|
String biomeName = lv.get(locator.offset+0).evalValue(c).getString();
|
||||||
// from locatebiome command code
|
// from locatebiome command code
|
||||||
Biome biome = cc.s.getMinecraftServer().getRegistryManager().get(Registry.BIOME_KEY).get(new Identifier(biomeName));
|
Biome biome = cc.s.getMinecraftServer().getRegistryManager().get(Registry.BIOME_KEY).getOrEmpty(new Identifier(biomeName))
|
||||||
if (biome == null)
|
.orElseThrow(() -> new ThrowStatement(biomeName, Throwables.UNKNOWN_BIOME));
|
||||||
throw new InternalExpressionException("Unknown biome: "+biomeName);
|
|
||||||
boolean doImmediateUpdate = true;
|
boolean doImmediateUpdate = true;
|
||||||
if (lv.size() > locator.offset+1)
|
if (lv.size() > locator.offset+1)
|
||||||
{
|
{
|
||||||
@@ -1251,8 +1336,8 @@ public class WorldAccess {
|
|||||||
if (!(requested instanceof NullValue))
|
if (!(requested instanceof NullValue))
|
||||||
{
|
{
|
||||||
String reqString = requested.getString();
|
String reqString = requested.getString();
|
||||||
structure = Registry.STRUCTURE_FEATURE.get(new Identifier(reqString));
|
structure = Registry.STRUCTURE_FEATURE.getOrEmpty(new Identifier(reqString))
|
||||||
if (structure == null) throw new InternalExpressionException("Unknown structure: " + reqString);
|
.orElseThrow(() -> new ThrowStatement(reqString, Throwables.UNKNOWN_STRUCTURE));
|
||||||
}
|
}
|
||||||
if (lv.size() > locator.offset+1)
|
if (lv.size() > locator.offset+1)
|
||||||
{
|
{
|
||||||
@@ -1332,7 +1417,7 @@ public class WorldAccess {
|
|||||||
throw new InternalExpressionException("'set_structure requires at least position and a structure name");
|
throw new InternalExpressionException("'set_structure requires at least position and a structure name");
|
||||||
String structureName = lv.get(locator.offset).evalValue(c).getString().toLowerCase(Locale.ROOT);
|
String structureName = lv.get(locator.offset).evalValue(c).getString().toLowerCase(Locale.ROOT);
|
||||||
ConfiguredStructureFeature<?, ?> configuredStructure = FeatureGenerator.resolveConfiguredStructure(structureName, world, pos);
|
ConfiguredStructureFeature<?, ?> configuredStructure = FeatureGenerator.resolveConfiguredStructure(structureName, world, pos);
|
||||||
if (configuredStructure == null) throw new InternalExpressionException("Unknown structure: "+structureName);
|
if (configuredStructure == null) throw new ThrowStatement(structureName, Throwables.UNKNOWN_STRUCTURE);
|
||||||
// good 'ol pointer
|
// good 'ol pointer
|
||||||
Value[] result = new Value[]{Value.NULL};
|
Value[] result = new Value[]{Value.NULL};
|
||||||
// technically a world modification. Even if we could let it slide, we will still park it
|
// technically a world modification. Even if we could let it slide, we will still park it
|
||||||
@@ -1382,6 +1467,7 @@ public class WorldAccess {
|
|||||||
{
|
{
|
||||||
if (lv.size() == 0) throw new InternalExpressionException("'custom_dimension' requires at least one argument");
|
if (lv.size() == 0) throw new InternalExpressionException("'custom_dimension' requires at least one argument");
|
||||||
CarpetContext cc = (CarpetContext)c;
|
CarpetContext cc = (CarpetContext)c;
|
||||||
|
cc.host.issueDeprecation("custom_dimension()");
|
||||||
String worldKey = lv.get(0).evalValue(c).getString();
|
String worldKey = lv.get(0).evalValue(c).getString();
|
||||||
|
|
||||||
Long seed = null;
|
Long seed = null;
|
||||||
|
|||||||
@@ -0,0 +1,502 @@
|
|||||||
|
package carpet.script.argument;
|
||||||
|
|
||||||
|
import carpet.CarpetServer;
|
||||||
|
import carpet.script.Context;
|
||||||
|
import carpet.script.LazyValue;
|
||||||
|
import carpet.script.bundled.Module;
|
||||||
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import carpet.script.exception.ThrowStatement;
|
||||||
|
import carpet.script.exception.Throwables;
|
||||||
|
import carpet.script.value.MapValue;
|
||||||
|
import carpet.script.value.StringValue;
|
||||||
|
import carpet.script.value.Value;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.NbtIo;
|
||||||
|
import net.minecraft.nbt.PositionTracker;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.nbt.TagReaders;
|
||||||
|
import net.minecraft.util.WorldSavePath;
|
||||||
|
import net.minecraft.util.crash.CrashException;
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.FileSystem;
|
||||||
|
import java.nio.file.FileSystemNotFoundException;
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class FileArgument
|
||||||
|
{
|
||||||
|
public String resource;
|
||||||
|
public Type type;
|
||||||
|
public String zipContainer;
|
||||||
|
public boolean isFolder;
|
||||||
|
public boolean isShared;
|
||||||
|
public Reason reason;
|
||||||
|
private FileSystem zfs;
|
||||||
|
private Path zipPath;
|
||||||
|
|
||||||
|
public final static Object writeIOSync = new Object();
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
if (zfs != null && zfs.isOpen()) {
|
||||||
|
try {
|
||||||
|
zfs.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new InternalExpressionException("Unable to close zip container: "+zipContainer);
|
||||||
|
}
|
||||||
|
zfs = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Type
|
||||||
|
{
|
||||||
|
RAW("raw", ".txt"),
|
||||||
|
TEXT("text", ".txt"),
|
||||||
|
NBT("nbt", ".nbt"),
|
||||||
|
JSON("json", ".json"),
|
||||||
|
FOLDER("folder", "");
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final String extension;
|
||||||
|
|
||||||
|
public static Map<String, Type> of = Arrays.stream(values()).collect(Collectors.toMap(t -> t.id, t -> t));
|
||||||
|
|
||||||
|
Type(String id, String extension)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
this.extension = extension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Reason
|
||||||
|
{
|
||||||
|
READ, CREATE, DELETE
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileArgument(String resource, Type type, String zipContainer, boolean isFolder, boolean isShared, Reason reason)
|
||||||
|
{
|
||||||
|
this.resource = resource;
|
||||||
|
this.type = type;
|
||||||
|
this.zipContainer = zipContainer;
|
||||||
|
this.isFolder = isFolder;
|
||||||
|
this.isShared = isShared;
|
||||||
|
this.reason = reason;
|
||||||
|
this.zfs = null;
|
||||||
|
this.zipPath = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "path: "+resource+" zip: "+zipContainer+" type: "+type.id+" folder: "+isFolder+" shared: "+isShared+" reason: "+reason.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileArgument from(List<LazyValue> lv, Context c, boolean isFolder, Reason reason)
|
||||||
|
{
|
||||||
|
if (lv.size() < 2) throw new InternalExpressionException("File functions require path and type as first two arguments");
|
||||||
|
Pair<String, String> resource = recognizeResource(lv.get(0).evalValue(c), isFolder);
|
||||||
|
String origtype = lv.get(1).evalValue(c).getString().toLowerCase(Locale.ROOT);
|
||||||
|
boolean shared = origtype.startsWith("shared_");
|
||||||
|
String typeString = shared ? origtype.substring(7) : origtype; //len(shared_)
|
||||||
|
Type type = Type.of.get(typeString);
|
||||||
|
if (type==null)
|
||||||
|
throw new InternalExpressionException("Unsupported file type: "+origtype);
|
||||||
|
if (type==Type.FOLDER && !isFolder)
|
||||||
|
throw new InternalExpressionException("Folder types are no supported for this IO function");
|
||||||
|
return new FileArgument(resource.getLeft(), type,resource.getRight(), isFolder, shared, reason);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Pair<String,String> recognizeResource(Value value, boolean isFolder)
|
||||||
|
{
|
||||||
|
String origfile = value.getString();
|
||||||
|
String[] pathElements = origfile.toLowerCase(Locale.ROOT).split("[/\\\\]+");
|
||||||
|
List<String> path = new ArrayList<>();
|
||||||
|
String zipPath = null;
|
||||||
|
for (String token : pathElements)
|
||||||
|
{
|
||||||
|
boolean isZip = token.endsWith(".zip");
|
||||||
|
if (zipPath != null && isZip) throw new InternalExpressionException(token+" indicates zip access in an already zipped location "+zipPath);
|
||||||
|
if (isZip) token = token.substring(0, token.length()-4);
|
||||||
|
token = token.replaceAll("[^A-Za-z0-9\\-+_]", "");
|
||||||
|
if (token.isEmpty()) continue;
|
||||||
|
if (isZip) token = token + ".zip";
|
||||||
|
path.add(token);
|
||||||
|
if (isZip)
|
||||||
|
{
|
||||||
|
zipPath = String.join("/", path);
|
||||||
|
path.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (path.isEmpty() && !isFolder)
|
||||||
|
throw new InternalExpressionException(
|
||||||
|
"Cannot use "+origfile+" as resource name: indicated path is empty"+((zipPath==null)?"":" in zip container "+zipPath)
|
||||||
|
);
|
||||||
|
return Pair.of(String.join("/", path), zipPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path resolve(String suffix)
|
||||||
|
{
|
||||||
|
if (CarpetServer.minecraft_server == null)
|
||||||
|
throw new InternalExpressionException("Accessing world files without server running");
|
||||||
|
return CarpetServer.minecraft_server.getSavePath(WorldSavePath.ROOT).resolve("scripts/"+suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path toPath(Module module)
|
||||||
|
{
|
||||||
|
if (!isShared && (module == null || module.getName() == null)) return null;
|
||||||
|
if (zipContainer == null)
|
||||||
|
return resolve(getDescriptor(module, resource)+(isFolder?"":type.extension));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (zfs == null)
|
||||||
|
{
|
||||||
|
Map<String, String> env = new HashMap<>();
|
||||||
|
if (reason == Reason.CREATE) env.put("create", "true");
|
||||||
|
zipPath = resolve(getDescriptor(module, zipContainer));
|
||||||
|
try {
|
||||||
|
zfs = FileSystems.newFileSystem(URI.create("jar:"+ zipPath.toUri().toString()), env);
|
||||||
|
}
|
||||||
|
catch (FileSystemNotFoundException fsnfe)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new ThrowStatement("Unable to open zip file: "+zipContainer, Throwables.IO_EXCEPTION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return zfs.getPath(resource+(isFolder?"/":type.extension));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path moduleRootPath(Module module)
|
||||||
|
{
|
||||||
|
if (!isShared && (module == null || module.getName() == null)) return null;
|
||||||
|
return resolve(isShared?"shared":module.getName()+".data");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayPath()
|
||||||
|
{
|
||||||
|
return (isShared?"shared/":"")+(zipContainer!=null?zipContainer+"/":"")+resource+type.extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDescriptor(Module module, String res)
|
||||||
|
{
|
||||||
|
if (isShared)
|
||||||
|
{
|
||||||
|
return res.isEmpty()?"shared":"shared/"+res;
|
||||||
|
}
|
||||||
|
if (module != null && module.getName() != null) // appdata
|
||||||
|
{
|
||||||
|
return module.getName()+".data"+(res==null || res.isEmpty()?"":"/"+res);
|
||||||
|
}
|
||||||
|
throw new InternalExpressionException("Invalid file descriptor: "+res);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<Path> listFiles(Module module)
|
||||||
|
{
|
||||||
|
Path dir = toPath(module);
|
||||||
|
if (dir == null || !Files.exists(dir)) return null;
|
||||||
|
String ext = type.extension;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Files.list(dir).filter(path -> (type==Type.FOLDER)?path.toString().endsWith("/"):path.toString().endsWith(ext));
|
||||||
|
}
|
||||||
|
catch (IOException ignored)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<String> listFolder(Module module)
|
||||||
|
{
|
||||||
|
Stream<String> strings;
|
||||||
|
try { synchronized (writeIOSync)
|
||||||
|
{
|
||||||
|
Stream<Path> result;
|
||||||
|
result = listFiles(module);
|
||||||
|
if (result == null) return null;
|
||||||
|
Path rootPath = moduleRootPath(module);
|
||||||
|
if (rootPath == null) return null;
|
||||||
|
String zipComponent = (zipContainer != null) ? rootPath.relativize(zipPath).toString() : null;
|
||||||
|
strings = result.map(p -> {
|
||||||
|
if (zipContainer == null)
|
||||||
|
return rootPath.relativize(p).toString().replaceAll("[\\\\/]+", "/");
|
||||||
|
return (zipComponent + '/'+ p.toString()).replaceAll("[\\\\/]+", "/");
|
||||||
|
});
|
||||||
|
} }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
if (type == Type.FOLDER)
|
||||||
|
return strings.map(s -> s.endsWith("/")?s.substring(0, s.length()-1):s);
|
||||||
|
return strings.map(FilenameUtils::removeExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createPaths(Path file)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ((zipContainer == null || file.getParent() != null) &&
|
||||||
|
!Files.exists(file.getParent()) &&
|
||||||
|
Files.createDirectories(file.getParent()) == null
|
||||||
|
) throw new IOException();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ThrowStatement("Unable to create paths for "+file.toString(), Throwables.IO_EXCEPTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean appendToTextFile(Module module, List<String> message)
|
||||||
|
{
|
||||||
|
try { synchronized (writeIOSync)
|
||||||
|
{
|
||||||
|
Path dataFile = toPath(module);//, resourceName, supportedTypes.get(type), isShared);
|
||||||
|
if (dataFile == null) return false;
|
||||||
|
createPaths(dataFile);
|
||||||
|
OutputStream out = Files.newOutputStream(dataFile, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
|
||||||
|
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)))
|
||||||
|
{
|
||||||
|
for (String line: message)
|
||||||
|
{
|
||||||
|
writer.append(line);
|
||||||
|
if (type == Type.TEXT) writer.newLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} }
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new ThrowStatement("Error when writing to the file: "+e, Throwables.IO_EXCEPTION);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tag getNbtData(Module module) // aka getData
|
||||||
|
{
|
||||||
|
try { synchronized (writeIOSync) {
|
||||||
|
Path dataFile = toPath(module);
|
||||||
|
if (dataFile == null || !Files.exists(dataFile)) return null;
|
||||||
|
return readTag(dataFile);
|
||||||
|
} }
|
||||||
|
finally {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//copied private method from net.minecraft.nbt.NbtIo.read()
|
||||||
|
// to read non-compound tags - these won't be compressed
|
||||||
|
public static Tag readTag(Path path)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return NbtIo.readCompressed(Files.newInputStream(path));
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
// Copy of NbtIo.read(File) because that's now client-side only
|
||||||
|
if (!Files.exists(path))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try (DataInputStream in = new DataInputStream(new BufferedInputStream(Files.newInputStream(path))))
|
||||||
|
{
|
||||||
|
return NbtIo.read(in);
|
||||||
|
}
|
||||||
|
catch (IOException ioException)
|
||||||
|
{
|
||||||
|
// not compressed compound tag neither uncompressed compound tag - trying any type of a tag
|
||||||
|
try (DataInputStream dataInput_1 = new DataInputStream(new BufferedInputStream(Files.newInputStream(path))))
|
||||||
|
{
|
||||||
|
byte byte_1 = dataInput_1.readByte();
|
||||||
|
if (byte_1 == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dataInput_1.readUTF();
|
||||||
|
return TagReaders.of(byte_1).read(dataInput_1, 0, PositionTracker.DEFAULT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException ignored)
|
||||||
|
{
|
||||||
|
throw new ThrowStatement("Not a valid NBT tag in "+path.toString(), Throwables.NBT_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (CrashException e)
|
||||||
|
{
|
||||||
|
throw new ThrowStatement("Error when reading NBT file "+path.toString(), Throwables.NBT_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean saveNbtData(Module module, Tag tag) // aka saveData
|
||||||
|
{
|
||||||
|
try { synchronized (writeIOSync) {
|
||||||
|
Path dataFile = toPath(module);
|
||||||
|
if (dataFile == null) return false;
|
||||||
|
createPaths(dataFile);
|
||||||
|
return writeTagDisk(tag, dataFile, zipContainer != null);
|
||||||
|
} }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//copied private method from net.minecraft.nbt.NbtIo.write() and client method safe_write
|
||||||
|
public static boolean writeTagDisk(Tag tag_1, Path path, boolean zipped)
|
||||||
|
{
|
||||||
|
Path original = path;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!zipped)
|
||||||
|
{
|
||||||
|
path = new File(path.toFile().getAbsolutePath() + "_tmp").toPath();
|
||||||
|
Files.deleteIfExists(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag_1 instanceof CompoundTag)
|
||||||
|
{
|
||||||
|
NbtIo.writeCompressed((CompoundTag) tag_1, Files.newOutputStream(path));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try (DataOutputStream dataOutputStream_1 = new DataOutputStream(Files.newOutputStream(path)))
|
||||||
|
{
|
||||||
|
dataOutputStream_1.writeByte(tag_1.getType());
|
||||||
|
if (tag_1.getType() != 0)
|
||||||
|
{
|
||||||
|
dataOutputStream_1.writeUTF("");
|
||||||
|
tag_1.write(dataOutputStream_1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!zipped)
|
||||||
|
{
|
||||||
|
Files.deleteIfExists(original);
|
||||||
|
Files.move(path, original);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new ThrowStatement("Unable to write tag to "+original.toString(), Throwables.IO_EXCEPTION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean dropExistingFile(Module module)
|
||||||
|
{
|
||||||
|
|
||||||
|
try { synchronized (writeIOSync)
|
||||||
|
{
|
||||||
|
Path dataFile = toPath(module);
|
||||||
|
if (dataFile == null) return false;
|
||||||
|
return Files.deleteIfExists(dataFile);
|
||||||
|
} }
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new ThrowStatement("Error while removing file: "+getDisplayPath(), Throwables.IO_EXCEPTION);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> listFile(Module module)
|
||||||
|
{
|
||||||
|
try { synchronized (writeIOSync) {
|
||||||
|
Path dataFile = toPath(module);
|
||||||
|
if (dataFile == null) return null;
|
||||||
|
if (!Files.exists(dataFile)) return null;
|
||||||
|
return listFileContent(dataFile);
|
||||||
|
} }
|
||||||
|
finally {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> listFileContent(Path filePath)
|
||||||
|
{
|
||||||
|
try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) {
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
for (;;) {
|
||||||
|
String line = reader.readLine();
|
||||||
|
if (line == null)
|
||||||
|
break;
|
||||||
|
result.add(line.replaceAll("[\n\r]+",""));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new ThrowStatement("Failed to read text file "+filePath.toString(), Throwables.IO_EXCEPTION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonElement readJsonFile(Module module) {
|
||||||
|
try { synchronized (writeIOSync) {
|
||||||
|
Path dataFile = toPath(module);
|
||||||
|
if (dataFile == null || !Files.exists(dataFile)) return null;
|
||||||
|
return readJsonContent(dataFile);
|
||||||
|
} }
|
||||||
|
finally {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonElement readJsonContent(Path filePath)
|
||||||
|
{
|
||||||
|
try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8))
|
||||||
|
{
|
||||||
|
return new JsonParser().parse(new JsonReader(reader));
|
||||||
|
}
|
||||||
|
catch (JsonParseException e)
|
||||||
|
{
|
||||||
|
Throwable exc = e;
|
||||||
|
if(e.getCause() != null)
|
||||||
|
exc = e.getCause();
|
||||||
|
throw new ThrowStatement(MapValue.wrap(ImmutableMap.of(
|
||||||
|
StringValue.of("error"), StringValue.of(exc.getMessage()),
|
||||||
|
StringValue.of("path"), StringValue.of(filePath.toString())
|
||||||
|
)), Throwables.JSON_ERROR);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new ThrowStatement("Failed to read json file content "+filePath.toString(), Throwables.IO_EXCEPTION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,33 +1,10 @@
|
|||||||
package carpet.script.bundled;
|
package carpet.script.bundled;
|
||||||
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.nbt.NbtIo;
|
|
||||||
import net.minecraft.nbt.PositionTracker;
|
|
||||||
import net.minecraft.nbt.Tag;
|
|
||||||
import net.minecraft.nbt.TagReaders;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
import com.google.gson.stream.JsonReader;
|
|
||||||
|
|
||||||
public class FileModule extends Module
|
public class FileModule extends Module
|
||||||
{
|
{
|
||||||
@@ -66,149 +43,4 @@ public class FileModule extends Module
|
|||||||
return library;
|
return library;
|
||||||
}
|
}
|
||||||
|
|
||||||
//copied private method from net.minecraft.nbt.NbtIo.read()
|
|
||||||
// to read non-compound tags - these won't be compressed
|
|
||||||
public static Tag read(File file)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return NbtIo.readCompressed(file);
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
// Copy of NbtIo.read(File) because that's now client-side only
|
|
||||||
if (!file.exists())
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try (DataInputStream in = new DataInputStream(new FileInputStream(file)))
|
|
||||||
{
|
|
||||||
return NbtIo.read(in);
|
|
||||||
}
|
|
||||||
catch (IOException ioException)
|
|
||||||
{
|
|
||||||
try (DataInputStream dataInput_1 = new DataInputStream(new FileInputStream(file)))
|
|
||||||
{
|
|
||||||
byte byte_1 = dataInput_1.readByte();
|
|
||||||
if (byte_1 == 0)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dataInput_1.readUTF();
|
|
||||||
return TagReaders.of(byte_1).read(dataInput_1, 0, PositionTracker.DEFAULT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//copied private method from net.minecraft.nbt.NbtIo.write() and client method safe_write
|
|
||||||
public static boolean write(Tag tag_1, File file)
|
|
||||||
{
|
|
||||||
File file_2 = new File(file.getAbsolutePath() + "_tmp");
|
|
||||||
if (file_2.exists()) file_2.delete();
|
|
||||||
|
|
||||||
if (tag_1 instanceof CompoundTag)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
NbtIo.writeCompressed((CompoundTag) tag_1, file_2);
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try (DataOutputStream dataOutputStream_1 = new DataOutputStream(new FileOutputStream(file_2)))
|
|
||||||
{
|
|
||||||
dataOutputStream_1.writeByte(tag_1.getType());
|
|
||||||
if (tag_1.getType() != 0)
|
|
||||||
{
|
|
||||||
dataOutputStream_1.writeUTF("");
|
|
||||||
tag_1.write(dataOutputStream_1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (file.exists()) file.delete();
|
|
||||||
if (!file.exists()) file_2.renameTo(file);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean appendText(Path filePath, boolean addNewLines, List<String> data)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
OutputStream out = Files.newOutputStream(filePath, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
|
|
||||||
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)))
|
|
||||||
{
|
|
||||||
for (String line: data)
|
|
||||||
{
|
|
||||||
writer.append(line);
|
|
||||||
if (addNewLines) writer.newLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static List<String> listFileContent(Path filePath)
|
|
||||||
{
|
|
||||||
try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) {
|
|
||||||
List<String> result = new ArrayList<>();
|
|
||||||
for (;;) {
|
|
||||||
String line = reader.readLine();
|
|
||||||
if (line == null)
|
|
||||||
break;
|
|
||||||
result.add(line.replaceAll("[\n\r]+",""));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JsonElement readJsonContent(Path filePath)
|
|
||||||
{
|
|
||||||
try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8))
|
|
||||||
{
|
|
||||||
return new JsonParser().parse(new JsonReader(reader));
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Stream<Path> listFiles(Path dir, String ext)
|
|
||||||
{
|
|
||||||
boolean folderListing = ext.equals("folder");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Files.list(dir).
|
|
||||||
filter(path -> folderListing?Files.isDirectory(path):path.toString().endsWith(ext));
|
|
||||||
}
|
|
||||||
catch (IOException ignored)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,112 +1,39 @@
|
|||||||
package carpet.script.bundled;
|
package carpet.script.bundled;
|
||||||
|
|
||||||
import carpet.CarpetServer;
|
import carpet.CarpetServer;
|
||||||
|
import carpet.script.argument.FileArgument;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.util.WorldSavePath;
|
import net.minecraft.util.WorldSavePath;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public abstract class Module
|
public abstract class Module
|
||||||
{
|
{
|
||||||
public abstract String getName();
|
public abstract String getName();
|
||||||
public abstract String getCode();
|
public abstract String getCode();
|
||||||
public abstract boolean isLibrary();
|
public abstract boolean isLibrary();
|
||||||
public final static Object writeIOSync = new Object();
|
|
||||||
public final static Map<String, String> supportedTypes = ImmutableMap.of(
|
|
||||||
"raw", ".txt",
|
|
||||||
"text", ".txt",
|
|
||||||
"nbt", ".nbt",
|
|
||||||
"json", ".json",
|
|
||||||
"folder", "folder"
|
|
||||||
);
|
|
||||||
|
|
||||||
private static String getDescriptor(Module module, String file, boolean isShared)
|
public static Tag getData(Module module)
|
||||||
{
|
{
|
||||||
if (isShared)
|
Path dataFile = resolveResource(module);
|
||||||
{
|
|
||||||
return file.isEmpty()?"shared":"shared/"+file;
|
|
||||||
}
|
|
||||||
if (module != null && module.getName() != null) // appdata
|
|
||||||
{
|
|
||||||
return module.getName()+".data"+(file==null || file.isEmpty()?"":"/"+file);
|
|
||||||
}
|
|
||||||
throw new RuntimeException("Invalid file descriptor: "+file);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Tag getData(Module module, String file, boolean isShared)
|
|
||||||
{
|
|
||||||
Path dataFile = resolveResource(module, file, ".nbt", isShared);
|
|
||||||
if (dataFile == null) return null;
|
if (dataFile == null) return null;
|
||||||
if (!Files.exists(dataFile) || !(dataFile.toFile().isFile())) return null;
|
if (!Files.exists(dataFile) || !(dataFile.toFile().isFile())) return null;
|
||||||
synchronized (writeIOSync) { return FileModule.read(dataFile.toFile()); }
|
synchronized (FileArgument.writeIOSync) { return FileArgument.readTag(dataFile); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean saveData(Module module, String file, Tag globalState, boolean isShared)
|
public static void saveData(Module module, Tag globalState)
|
||||||
{
|
{
|
||||||
Path dataFile = resolveResource(module, file, ".nbt", isShared);
|
Path dataFile = resolveResource(module);
|
||||||
if (dataFile == null) return false;
|
if (dataFile == null) return;
|
||||||
if (!Files.exists(dataFile.getParent()) && !dataFile.toFile().getParentFile().mkdirs()) return false;
|
if (!Files.exists(dataFile.getParent()) && !dataFile.toFile().getParentFile().mkdirs()) return;
|
||||||
synchronized (writeIOSync) { return FileModule.write(globalState, dataFile.toFile()); }
|
synchronized (FileArgument.writeIOSync) { FileArgument.writeTagDisk(globalState, dataFile, false); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean appendToTextFile(Module module, String resourceName, String type, boolean isShared, List<String> message)
|
private static Path resolveResource(Module module)
|
||||||
{
|
{
|
||||||
Path dataFile = resolveResource(module, resourceName, supportedTypes.get(type), isShared);
|
if (module == null || module.getName() == null) return null; // commandline app
|
||||||
if (dataFile == null) return false;
|
return CarpetServer.minecraft_server.getSavePath(WorldSavePath.ROOT).resolve("scripts/"+module.getName()+".data.nbt");
|
||||||
if (!Files.exists(dataFile.getParent()) && !dataFile.toFile().getParentFile().mkdirs()) return false;
|
|
||||||
synchronized (writeIOSync) { return FileModule.appendText(dataFile, type.equals("text"), message); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean dropExistingFile(Module module, String resourceName, String type, boolean isShared)
|
|
||||||
{
|
|
||||||
Path dataFile = resolveResource(module, resourceName, supportedTypes.get(type), isShared);
|
|
||||||
if (dataFile == null) return false;
|
|
||||||
synchronized (writeIOSync) { return dataFile.toFile().delete(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<String> listFile(Module module, String resourceName, String type, boolean isShared)
|
|
||||||
{
|
|
||||||
Path dataFile = resolveResource(module, resourceName, supportedTypes.get(type), isShared);
|
|
||||||
if (dataFile == null) return null;
|
|
||||||
if (!dataFile.toFile().exists()) return null;
|
|
||||||
synchronized (writeIOSync) { return FileModule.listFileContent(dataFile); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JsonElement readJsonFile(Module module, String resourceName, String type, boolean isShared) {
|
|
||||||
Path dataFile = resolveResource(module, resourceName, supportedTypes.get(type), isShared);
|
|
||||||
if (dataFile == null) return null;
|
|
||||||
if (!dataFile.toFile().exists()) return null;
|
|
||||||
synchronized (writeIOSync) { return FileModule.readJsonContent(dataFile); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Stream<String> listFolder(Module module, String resourceName, String type, boolean isShared)
|
|
||||||
{
|
|
||||||
Path dir = resolveResource(module, resourceName, null, isShared);
|
|
||||||
String ext = supportedTypes.get(type);
|
|
||||||
if (dir == null) return null;
|
|
||||||
if (!Files.exists(dir)) return null;
|
|
||||||
Stream<Path> result;
|
|
||||||
synchronized (writeIOSync) { result = FileModule.listFiles(dir, ext); }
|
|
||||||
if (result == null) return null;
|
|
||||||
Path rootPath = resolveResource(module, "", null, isShared);
|
|
||||||
Stream<String> strings = result.map(p -> rootPath.relativize(p).toString().replaceAll("[\\\\/]","/"));
|
|
||||||
if (!ext.equals("folder"))
|
|
||||||
return strings.map(FilenameUtils::removeExtension);
|
|
||||||
return strings;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Path resolveResource(Module module, String resourceName, String ext, boolean isShared)
|
|
||||||
{
|
|
||||||
if (!isShared && (module == null || module.getName() == null)) return null;
|
|
||||||
return CarpetServer.minecraft_server.getSavePath(WorldSavePath.ROOT).resolve("scripts/"+getDescriptor(module, resourceName, isShared)+(ext==null?"":ext));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package carpet.script.command;
|
package carpet.script.command;
|
||||||
|
|
||||||
|
import carpet.CarpetServer;
|
||||||
import carpet.fakes.BlockStateArgumentInterface;
|
import carpet.fakes.BlockStateArgumentInterface;
|
||||||
import carpet.script.CarpetScriptHost;
|
import carpet.script.CarpetScriptHost;
|
||||||
import carpet.script.argument.FunctionArgument;
|
import carpet.script.argument.FunctionArgument;
|
||||||
@@ -14,6 +15,7 @@ import carpet.script.value.NumericValue;
|
|||||||
import carpet.script.value.StringValue;
|
import carpet.script.value.StringValue;
|
||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
import carpet.script.value.ValueConversions;
|
import carpet.script.value.ValueConversions;
|
||||||
|
import carpet.utils.CarpetProfiler;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
import com.mojang.brigadier.arguments.ArgumentType;
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
@@ -276,7 +278,12 @@ public abstract class CommandArgument
|
|||||||
{
|
{
|
||||||
CommandArgument arg = getTypeForArgument(param, host);
|
CommandArgument arg = getTypeForArgument(param, host);
|
||||||
if (arg.suggestionProvider != null) return argument(param, arg.getArgumentType()).suggests(arg.suggestionProvider);
|
if (arg.suggestionProvider != null) return argument(param, arg.getArgumentType()).suggests(arg.suggestionProvider);
|
||||||
return arg.needsMatching? argument(param, arg.getArgumentType()).suggests((c, b) -> arg.suggest(c, b, host)) : argument(param, arg.getArgumentType());
|
if (!arg.needsMatching) return argument(param, arg.getArgumentType());
|
||||||
|
String hostName = host.getName();
|
||||||
|
return argument(param, arg.getArgumentType()).suggests((ctx, b) -> {
|
||||||
|
CarpetScriptHost cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveOwnForExecution(ctx.getSource());
|
||||||
|
return arg.suggest(ctx, b, cHost);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String suffix;
|
protected String suffix;
|
||||||
@@ -365,6 +372,7 @@ public abstract class CommandArgument
|
|||||||
{
|
{
|
||||||
if (customSuggester != null)
|
if (customSuggester != null)
|
||||||
{
|
{
|
||||||
|
CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet command", CarpetProfiler.TYPE.GENERAL);
|
||||||
Map<Value, Value> params = new HashMap<>();
|
Map<Value, Value> params = new HashMap<>();
|
||||||
for(ParsedCommandNode<ServerCommandSource> pnode : context.getNodes())
|
for(ParsedCommandNode<ServerCommandSource> pnode : context.getNodes())
|
||||||
{
|
{
|
||||||
@@ -379,7 +387,9 @@ public abstract class CommandArgument
|
|||||||
args.addAll(customSuggester.args);
|
args.addAll(customSuggester.args);
|
||||||
Value response = host.handleCommand(context.getSource(), customSuggester.function, args);
|
Value response = host.handleCommand(context.getSource(), customSuggester.function, args);
|
||||||
if (!(response instanceof ListValue)) throw error("Custom suggester should return a list of options"+" for custom type "+suffix);
|
if (!(response instanceof ListValue)) throw error("Custom suggester should return a list of options"+" for custom type "+suffix);
|
||||||
return ((ListValue) response).getItems().stream().map(Value::getString).collect(Collectors.toList());
|
Collection<String> res = ((ListValue) response).getItems().stream().map(Value::getString).collect(Collectors.toList());
|
||||||
|
CarpetProfiler.end_current_section(currentSection);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
if (needsMatching) return examples;
|
if (needsMatching) return examples;
|
||||||
//return Lists.newArrayList("");
|
//return Lists.newArrayList("");
|
||||||
|
|||||||
@@ -7,29 +7,43 @@ import carpet.script.Tokenizer;
|
|||||||
import carpet.script.value.FunctionValue;
|
import carpet.script.value.FunctionValue;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/* The expression evaluators exception class. */
|
/* The expression evaluators exception class. */
|
||||||
public class ExpressionException extends RuntimeException implements ResolvedException
|
public class ExpressionException extends RuntimeException implements ResolvedException
|
||||||
{
|
{
|
||||||
public final Context context;
|
public final Context context;
|
||||||
|
public final Tokenizer.Token token;
|
||||||
public final List<FunctionValue> stack = new ArrayList<>();
|
public final List<FunctionValue> stack = new ArrayList<>();
|
||||||
|
private final Supplier<String> lazyMessage;
|
||||||
|
private String cachedMessage = null;
|
||||||
|
|
||||||
public ExpressionException(Context c, Expression e, String message)
|
public ExpressionException(Context c, Expression e, String message)
|
||||||
{
|
{
|
||||||
super(makeMessage(c, e, null, message));
|
this(c, e, Tokenizer.Token.NONE, message);
|
||||||
context = c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpressionException(Context c, Expression e, Tokenizer.Token t, String message)
|
public ExpressionException(Context c, Expression e, Tokenizer.Token t, String message)
|
||||||
{
|
{
|
||||||
super(makeMessage(c, e, t, message));
|
this(c, e, t, message, Collections.emptyList());
|
||||||
context = c;
|
|
||||||
}
|
}
|
||||||
public ExpressionException(Context c, Expression e, Tokenizer.Token t, String message, List<FunctionValue> stack)
|
public ExpressionException(Context c, Expression e, Tokenizer.Token t, String message, List<FunctionValue> stack)
|
||||||
{
|
{
|
||||||
super(makeMessage(c, e, t, message));
|
super("Error");
|
||||||
this.stack.addAll(stack);
|
this.stack.addAll(stack);
|
||||||
|
lazyMessage = () -> makeMessage(c, e, t, message);
|
||||||
|
token = t;
|
||||||
|
context = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpressionException(Context c, Expression e, Tokenizer.Token t, Supplier<String> messageSupplier, List<FunctionValue> stack)
|
||||||
|
{
|
||||||
|
super("Error");
|
||||||
|
this.stack.addAll(stack);
|
||||||
|
lazyMessage = () -> makeMessage(c, e, t, messageSupplier.get());
|
||||||
|
token = t;
|
||||||
context = c;
|
context = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,4 +82,13 @@ public class ExpressionException extends RuntimeException implements ResolvedExc
|
|||||||
}
|
}
|
||||||
return String.join("\n", errorMaker.apply(e, t, message));
|
return String.join("\n", errorMaker.apply(e, t, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
if (cachedMessage == null)
|
||||||
|
{
|
||||||
|
cachedMessage = lazyMessage.get();
|
||||||
|
}
|
||||||
|
return cachedMessage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package carpet.script.exception;
|
package carpet.script.exception;
|
||||||
|
|
||||||
|
import carpet.script.Context;
|
||||||
|
import carpet.script.Expression;
|
||||||
|
import carpet.script.Tokenizer;
|
||||||
import carpet.script.value.FunctionValue;
|
import carpet.script.value.FunctionValue;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -13,4 +16,20 @@ public class InternalExpressionException extends RuntimeException
|
|||||||
{
|
{
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Promotes this simple exception into one with context and extra information.
|
||||||
|
*
|
||||||
|
* <p>Provides a cleaner way of handling similar exceptions, in this case
|
||||||
|
* {@link InternalExpressionException} and {@link ThrowStatement}
|
||||||
|
* @param c Context
|
||||||
|
* @param e Expression
|
||||||
|
* @param token Token
|
||||||
|
* @return The new {@link ExpressionException} (or {@link ProcessedThrowStatement}),
|
||||||
|
* depending on the implementation.
|
||||||
|
*/
|
||||||
|
public ExpressionException promote(Context c, Expression e, Tokenizer.Token token)
|
||||||
|
{
|
||||||
|
return new ExpressionException(c, e, token, getMessage(), stack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package carpet.script.exception;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import carpet.script.Context;
|
||||||
|
import carpet.script.Expression;
|
||||||
|
import carpet.script.Tokenizer.Token;
|
||||||
|
import carpet.script.value.FunctionValue;
|
||||||
|
import carpet.script.value.Value;
|
||||||
|
|
||||||
|
public class ProcessedThrowStatement extends ExpressionException {
|
||||||
|
public final Throwables thrownExceptionType;
|
||||||
|
public final Value data;
|
||||||
|
|
||||||
|
public ProcessedThrowStatement(Context c, Expression e, Token token, List<FunctionValue> stack, Throwables thrownExceptionType, Value data) {
|
||||||
|
super(c, e, token, () -> "Unhandled "+thrownExceptionType.getId()+" exception: "+data.getString(), stack);
|
||||||
|
this.thrownExceptionType = thrownExceptionType;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,54 @@
|
|||||||
package carpet.script.exception;
|
package carpet.script.exception;
|
||||||
|
|
||||||
|
import carpet.script.Context;
|
||||||
|
import carpet.script.Expression;
|
||||||
|
import carpet.script.Tokenizer.Token;
|
||||||
|
import carpet.script.value.StringValue;
|
||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
|
|
||||||
public class ThrowStatement extends ExitStatement
|
public class ThrowStatement extends InternalExpressionException
|
||||||
{
|
{
|
||||||
public ThrowStatement(Value value)
|
private final Throwables type;
|
||||||
|
private final Value data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a throw exception from a value, and assigns it a specified message.
|
||||||
|
* <p>To be used when throwing from Scarpet's {@code throw} function
|
||||||
|
* @param data The value to pass
|
||||||
|
* @param type Exception type
|
||||||
|
*/
|
||||||
|
public ThrowStatement(Value data, Throwables type)
|
||||||
{
|
{
|
||||||
super(value);
|
super(type.getId());
|
||||||
|
this.data = data;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThrowStatement(Value data, Throwables parent, String subtype)
|
||||||
|
{
|
||||||
|
super(subtype);
|
||||||
|
this.data = data;
|
||||||
|
this.type = new Throwables(subtype, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a throw exception.<br>
|
||||||
|
* Conveniently creates a value from the {@code value} String
|
||||||
|
* to be used easily in Java code
|
||||||
|
* @param message The message to display when not handled
|
||||||
|
* @param type An {@link Throwables} containing the inheritance data
|
||||||
|
* for this exception. When throwing from Java,
|
||||||
|
* those exceptions should be pre-registered.
|
||||||
|
*/
|
||||||
|
public ThrowStatement(String message, Throwables type)
|
||||||
|
{
|
||||||
|
super(type.getId());
|
||||||
|
this.data = StringValue.of(message);
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExpressionException promote(Context c, Expression e, Token token) {
|
||||||
|
return new ProcessedThrowStatement(c, e, token, stack, type, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package carpet.script.exception;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class contains default Scarpet catchable types, as well as their inheritance and
|
||||||
|
* methods to check whether filters are compatible with those.
|
||||||
|
*/
|
||||||
|
public class Throwables {
|
||||||
|
private final String id;
|
||||||
|
private final Throwables parent;
|
||||||
|
|
||||||
|
private static final Map<String, Throwables> byId = new HashMap<>();
|
||||||
|
|
||||||
|
public static final Throwables THROWN_EXCEPTION_TYPE = register("exception", null);
|
||||||
|
public static final Throwables VALUE_EXCEPTION = register("value_exception", THROWN_EXCEPTION_TYPE);
|
||||||
|
public static final Throwables UNKNOWN_ITEM = register("unknown_item", VALUE_EXCEPTION);
|
||||||
|
public static final Throwables UNKNOWN_BLOCK = register("unknown_block", VALUE_EXCEPTION);
|
||||||
|
public static final Throwables UNKNOWN_BIOME = register("unknown_biome", VALUE_EXCEPTION);
|
||||||
|
public static final Throwables UNKNOWN_SOUND = register("unknown_sound", VALUE_EXCEPTION);
|
||||||
|
public static final Throwables UNKNOWN_PARTICLE = register("unknown_particle", VALUE_EXCEPTION);
|
||||||
|
public static final Throwables UNKNOWN_POI = register("unknown_poi", VALUE_EXCEPTION);
|
||||||
|
public static final Throwables UNKNOWN_DIMENSION = register("unknown_dimension", VALUE_EXCEPTION);
|
||||||
|
public static final Throwables UNKNOWN_STRUCTURE = register("unknown_structure", VALUE_EXCEPTION);
|
||||||
|
public static final Throwables UNKNOWN_CRITERION = register("unknown_criterion", VALUE_EXCEPTION);
|
||||||
|
public static final Throwables IO_EXCEPTION = register("io_exception", THROWN_EXCEPTION_TYPE);
|
||||||
|
public static final Throwables NBT_ERROR = register("nbt_error", IO_EXCEPTION);
|
||||||
|
public static final Throwables JSON_ERROR = register("json_error", IO_EXCEPTION);
|
||||||
|
public static final Throwables USER_DEFINED = register("user_exception", THROWN_EXCEPTION_TYPE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an exception and registers it to be used as parent for
|
||||||
|
* user defined exceptions in Scarpet's throw function.
|
||||||
|
* <p>Scarpet exceptions should have a top-level parent being {@link Throwables#THROWN_EXCEPTION_TYPE}
|
||||||
|
* @param id The value for the exception as a {@link String}.
|
||||||
|
* @param parent The parent of the exception being created, or <code>null</code> if top-level
|
||||||
|
* @return The created exception
|
||||||
|
*/
|
||||||
|
public static Throwables register(String id, Throwables parent)
|
||||||
|
{
|
||||||
|
Throwables exc = new Throwables(id, parent);
|
||||||
|
byId.put(id, exc);
|
||||||
|
return exc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new exception.
|
||||||
|
* <p>Not suitable for creating exceptions that can't be caught.
|
||||||
|
* Use an {@link InternalExpressionException} for that
|
||||||
|
* @param id The exception's value as a {@link String}
|
||||||
|
*/
|
||||||
|
public Throwables(String id, Throwables parent)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Throwables getTypeForException(String type)
|
||||||
|
{
|
||||||
|
Throwables properType = byId.get(type);
|
||||||
|
if (properType == null) throw new InternalExpressionException("Unknown exception type: "+type);
|
||||||
|
return properType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given filter matches an instance of this exception, by checking equality
|
||||||
|
* with itself and possible parents.
|
||||||
|
* @param filter The type to check against
|
||||||
|
* @return Whether or not the given value matches this exception's hierarchy
|
||||||
|
*/
|
||||||
|
public boolean isRelevantFor(String filter)
|
||||||
|
{
|
||||||
|
return (id.equals(filter) || (parent != null && parent.isRelevantFor(filter)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUserException()
|
||||||
|
{
|
||||||
|
return this == USER_DEFINED || parent == USER_DEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the throwable type
|
||||||
|
* @return The type of this exception
|
||||||
|
*/
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,9 +60,9 @@ public class Arithmetic {
|
|||||||
expression.addMathematicalUnaryFunction("log1p", x -> Math.log1p(x)/Math.log(2));
|
expression.addMathematicalUnaryFunction("log1p", x -> Math.log1p(x)/Math.log(2));
|
||||||
expression.addMathematicalUnaryFunction("sqrt", Math::sqrt);
|
expression.addMathematicalUnaryFunction("sqrt", Math::sqrt);
|
||||||
expression.addMathematicalUnaryFunction("abs", Math::abs);
|
expression.addMathematicalUnaryFunction("abs", Math::abs);
|
||||||
expression.addMathematicalUnaryFunction("round", (d) -> (double)Math.round(d));
|
expression.addMathematicalUnaryIntFunction("round", Math::round);
|
||||||
expression.addMathematicalUnaryFunction("floor", Math::floor);
|
expression.addMathematicalUnaryIntFunction("floor", n -> (long)Math.floor(n));
|
||||||
expression.addMathematicalUnaryFunction("ceil", Math::ceil);
|
expression.addMathematicalUnaryIntFunction("ceil", n -> (long)Math.ceil(n));
|
||||||
|
|
||||||
expression.addLazyFunction("mandelbrot", 3, (c, t, lv) -> {
|
expression.addLazyFunction("mandelbrot", 3, (c, t, lv) -> {
|
||||||
double a0 = NumericValue.asNumber(lv.get(0).evalValue(c)).getDouble();
|
double a0 = NumericValue.asNumber(lv.get(0).evalValue(c)).getDouble();
|
||||||
|
|||||||
@@ -5,8 +5,17 @@ import carpet.script.Expression;
|
|||||||
import carpet.script.LazyValue;
|
import carpet.script.LazyValue;
|
||||||
import carpet.script.exception.ExitStatement;
|
import carpet.script.exception.ExitStatement;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import carpet.script.exception.ProcessedThrowStatement;
|
||||||
import carpet.script.exception.ThrowStatement;
|
import carpet.script.exception.ThrowStatement;
|
||||||
|
import carpet.script.exception.Throwables;
|
||||||
|
import carpet.script.value.ListValue;
|
||||||
|
import carpet.script.value.MapValue;
|
||||||
|
import carpet.script.value.NumericValue;
|
||||||
|
import carpet.script.value.StringValue;
|
||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class ControlFlow {
|
public class ControlFlow {
|
||||||
public static void apply(Expression expression) // public just to get the javadoc right
|
public static void apply(Expression expression) // public just to get the javadoc right
|
||||||
@@ -42,26 +51,98 @@ public class ControlFlow {
|
|||||||
|
|
||||||
expression.addFunction("exit", (lv) -> { throw new ExitStatement(lv.size()==0?Value.NULL:lv.get(0)); });
|
expression.addFunction("exit", (lv) -> { throw new ExitStatement(lv.size()==0?Value.NULL:lv.get(0)); });
|
||||||
|
|
||||||
expression.addFunction("throw", (lv)-> {throw new ThrowStatement(lv.size()==0?Value.NULL:lv.get(0)); });
|
expression.addLazyFunction("throw", -1, (c, t, lv)->
|
||||||
|
{
|
||||||
|
switch (lv.size())
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
throw new ThrowStatement(Value.NULL, Throwables.USER_DEFINED);
|
||||||
|
case 1:
|
||||||
|
throw new ThrowStatement(lv.get(0).evalValue(c), Throwables.USER_DEFINED );
|
||||||
|
case 2:
|
||||||
|
throw new ThrowStatement(lv.get(1).evalValue(c), Throwables.getTypeForException(lv.get(0).evalValue(c).getString()));
|
||||||
|
case 3:
|
||||||
|
throw new ThrowStatement(lv.get(2).evalValue(c), Throwables.getTypeForException(lv.get(1).evalValue(c).getString()), lv.get(0).evalValue(c).getString());
|
||||||
|
default:
|
||||||
|
throw new InternalExpressionException("throw() can't accept more than 3 parameters");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
expression.addLazyFunction("try", -1, (c, t, lv) ->
|
expression.addLazyFunction("try", -1, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
if (lv.size()==0)
|
if (lv.size()==0)
|
||||||
throw new InternalExpressionException("'try' needs at least an expression block");
|
throw new InternalExpressionException("'try' needs at least an expression block, and either a catch_epr, or a number of pairs of filters and catch_expr");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Value retval = lv.get(0).evalValue(c, t);
|
Value retval = lv.get(0).evalValue(c, t);
|
||||||
return (c_, t_) -> retval;
|
return (c_, t_) -> retval;
|
||||||
}
|
}
|
||||||
catch (ThrowStatement ret)
|
catch (ProcessedThrowStatement ret)
|
||||||
{
|
{
|
||||||
if (lv.size() == 1)
|
if (lv.size() == 1)
|
||||||
|
{
|
||||||
|
if (!ret.thrownExceptionType.isUserException())
|
||||||
|
throw ret;
|
||||||
return (c_, t_) -> Value.NULL;
|
return (c_, t_) -> Value.NULL;
|
||||||
|
}
|
||||||
|
if (lv.size() > 3 && lv.size() % 2 == 0)
|
||||||
|
{
|
||||||
|
throw new InternalExpressionException("Try-catch block needs the code to run, and either a catch expression for user thrown exceptions, or a number of pairs of filters and catch expressions");
|
||||||
|
}
|
||||||
|
|
||||||
|
Value val = null; // This is always assigned at some point, just the compiler doesn't know
|
||||||
|
|
||||||
LazyValue __ = c.getVariable("_");
|
LazyValue __ = c.getVariable("_");
|
||||||
c.setVariable("_", (__c, __t) -> ret.retval.reboundedTo("_"));
|
c.setVariable("_", (__c, __t) -> ret.data.reboundedTo("_"));
|
||||||
Value val = lv.get(1).evalValue(c, t);
|
LazyValue _trace = c.getVariable("_trace");
|
||||||
c.setVariable("_",__);
|
c.setVariable("_trace", (__c, __t) -> MapValue.wrap(ImmutableMap.of(
|
||||||
return (c_, t_) -> val;
|
StringValue.of("stack"), ListValue.wrap(ret.stack.stream().map(f -> ListValue.of(
|
||||||
|
StringValue.of(f.getModule().getName()),
|
||||||
|
StringValue.of(f.getString()),
|
||||||
|
NumericValue.of(f.getToken().lineno+1),
|
||||||
|
NumericValue.of(f.getToken().linepos+1)
|
||||||
|
)).collect(Collectors.toList())),
|
||||||
|
|
||||||
|
StringValue.of("locals"), MapValue.wrap(ret.context.variables.entrySet().stream().filter(e -> !e.getKey().equals("_trace")).collect(Collectors.toMap(
|
||||||
|
e -> StringValue.of(e.getKey()),
|
||||||
|
e -> e.getValue().evalValue(ret.context)
|
||||||
|
))),
|
||||||
|
StringValue.of("token"), ListValue.of(
|
||||||
|
StringValue.of(ret.token.surface),
|
||||||
|
NumericValue.of(ret.token.lineno+1),
|
||||||
|
NumericValue.of(ret.token.linepos+1)
|
||||||
|
)
|
||||||
|
)));
|
||||||
|
|
||||||
|
if (lv.size() == 2)
|
||||||
|
{
|
||||||
|
if (ret.thrownExceptionType.isUserException())
|
||||||
|
val = lv.get(1).evalValue(c, t);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int pointer = 1;
|
||||||
|
while (pointer < lv.size() -1)
|
||||||
|
{
|
||||||
|
if (ret.thrownExceptionType.isRelevantFor(lv.get(pointer).evalValue(c).getString()))
|
||||||
|
{
|
||||||
|
val = lv.get(pointer + 1).evalValue(c, t);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pointer += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.setVariable("_", __);
|
||||||
|
if (_trace != null)
|
||||||
|
c.setVariable("_trace", _trace);
|
||||||
|
else
|
||||||
|
c.delVariable("_trace");
|
||||||
|
if (val == null) // not handled
|
||||||
|
{
|
||||||
|
throw ret;
|
||||||
|
}
|
||||||
|
Value retval = val;
|
||||||
|
return (c_, t_) -> retval;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public class Loops {
|
|||||||
c.setVariable("_i", (cc, tt) -> new NumericValue(doYouReally).bindTo("_i"));
|
c.setVariable("_i", (cc, tt) -> new NumericValue(doYouReally).bindTo("_i"));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
result.add(expr.evalValue(c));
|
result.add(expr.evalValue(c, t));
|
||||||
}
|
}
|
||||||
catch (BreakStatement | ContinueStatement stmt)
|
catch (BreakStatement | ContinueStatement stmt)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public class Sys {
|
|||||||
return (cc, tt) -> Value.FALSE;
|
return (cc, tt) -> Value.FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value retval = new NumericValue(v.getBoolean());
|
Value retval = v.getBoolean()?Value.TRUE:Value.FALSE;
|
||||||
return (cc, tt) -> retval;
|
return (cc, tt) -> retval;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -56,7 +56,9 @@ public class Sys {
|
|||||||
{
|
{
|
||||||
if (v instanceof NumericValue)
|
if (v instanceof NumericValue)
|
||||||
{
|
{
|
||||||
return v;
|
NumericValue num = (NumericValue)v;
|
||||||
|
if (num.isInteger()) return new NumericValue(num.getLong());
|
||||||
|
return new NumericValue(num.getDouble());
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -208,6 +210,10 @@ public class Sys {
|
|||||||
Value retval = new NumericValue(NumericValue.asNumber(argument).getDouble()*randomizer.nextDouble());
|
Value retval = new NumericValue(NumericValue.asNumber(argument).getDouble()*randomizer.nextDouble());
|
||||||
return (cc, tt) -> retval;
|
return (cc, tt) -> retval;
|
||||||
});
|
});
|
||||||
|
expression.addLazyFunction("reset_seed", 1, (c, t, lv) -> {
|
||||||
|
boolean gotIt = c.host.resetRandom(NumericValue.asNumber(lv.get(0).evalValue(c)).getLong());
|
||||||
|
return gotIt?LazyValue.TRUE:LazyValue.FALSE;
|
||||||
|
});
|
||||||
|
|
||||||
expression.addLazyFunction("perlin", -1, (c, t, lv) ->
|
expression.addLazyFunction("perlin", -1, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package carpet.script.utils;
|
||||||
|
|
||||||
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class InputValidator {
|
||||||
|
public static String validateSimpleString(String input, boolean strict)
|
||||||
|
{
|
||||||
|
String simplified = input.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9+_]", "");
|
||||||
|
if (simplified.isEmpty() || (strict && !simplified.equals(input)))
|
||||||
|
throw new InternalExpressionException("simple name can only contain numbers, letter and _");
|
||||||
|
return simplified;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ import carpet.CarpetSettings;
|
|||||||
import carpet.network.ServerNetworkHandler;
|
import carpet.network.ServerNetworkHandler;
|
||||||
import carpet.script.CarpetContext;
|
import carpet.script.CarpetContext;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import carpet.script.exception.ThrowStatement;
|
||||||
|
import carpet.script.exception.Throwables;
|
||||||
import carpet.script.language.Sys;
|
import carpet.script.language.Sys;
|
||||||
import carpet.script.value.BlockValue;
|
import carpet.script.value.BlockValue;
|
||||||
import carpet.script.value.EntityValue;
|
import carpet.script.value.EntityValue;
|
||||||
@@ -156,7 +158,7 @@ public class ShapeDispatcher
|
|||||||
}
|
}
|
||||||
catch (CommandSyntaxException e)
|
catch (CommandSyntaxException e)
|
||||||
{
|
{
|
||||||
throw new InternalExpressionException("No such particle: "+name);
|
throw new ThrowStatement(name, Throwables.UNKNOWN_PARTICLE);
|
||||||
}
|
}
|
||||||
particleCache.put(name, particle);
|
particleCache.put(name, particle);
|
||||||
return particle;
|
return particle;
|
||||||
@@ -416,6 +418,8 @@ public class ShapeDispatcher
|
|||||||
put("facing", new StringValue("player")).
|
put("facing", new StringValue("player")).
|
||||||
put("raise", new NumericValue(0)).
|
put("raise", new NumericValue(0)).
|
||||||
put("tilt", new NumericValue(0)).
|
put("tilt", new NumericValue(0)).
|
||||||
|
put("lean", new NumericValue(0)).
|
||||||
|
put("turn", new NumericValue(0)).
|
||||||
put("indent", new NumericValue(0)).
|
put("indent", new NumericValue(0)).
|
||||||
put("height", new NumericValue(0)).
|
put("height", new NumericValue(0)).
|
||||||
put("align", new StringValue("center")).
|
put("align", new StringValue("center")).
|
||||||
@@ -437,6 +441,8 @@ public class ShapeDispatcher
|
|||||||
Direction facing;
|
Direction facing;
|
||||||
float raise;
|
float raise;
|
||||||
float tilt;
|
float tilt;
|
||||||
|
float lean;
|
||||||
|
float turn;
|
||||||
float size;
|
float size;
|
||||||
float indent;
|
float indent;
|
||||||
int align;
|
int align;
|
||||||
@@ -485,6 +491,8 @@ public class ShapeDispatcher
|
|||||||
|
|
||||||
raise = NumericValue.asNumber(options.getOrDefault("raise", optional.get("raise"))).getFloat();
|
raise = NumericValue.asNumber(options.getOrDefault("raise", optional.get("raise"))).getFloat();
|
||||||
tilt = NumericValue.asNumber(options.getOrDefault("tilt", optional.get("tilt"))).getFloat();
|
tilt = NumericValue.asNumber(options.getOrDefault("tilt", optional.get("tilt"))).getFloat();
|
||||||
|
lean = NumericValue.asNumber(options.getOrDefault("lean", optional.get("lean"))).getFloat();
|
||||||
|
turn = NumericValue.asNumber(options.getOrDefault("turn", optional.get("turn"))).getFloat();
|
||||||
indent = NumericValue.asNumber(options.getOrDefault("indent", optional.get("indent"))).getFloat();
|
indent = NumericValue.asNumber(options.getOrDefault("indent", optional.get("indent"))).getFloat();
|
||||||
height = NumericValue.asNumber(options.getOrDefault("height", optional.get("height"))).getFloat();
|
height = NumericValue.asNumber(options.getOrDefault("height", optional.get("height"))).getFloat();
|
||||||
|
|
||||||
@@ -517,6 +525,8 @@ public class ShapeDispatcher
|
|||||||
if (facing!= null) hash ^= facing.hashCode(); hash *= 1099511628211L;
|
if (facing!= null) hash ^= facing.hashCode(); hash *= 1099511628211L;
|
||||||
hash ^= Float.hashCode(raise); hash *= 1099511628211L;
|
hash ^= Float.hashCode(raise); hash *= 1099511628211L;
|
||||||
hash ^= Float.hashCode(tilt); hash *= 1099511628211L;
|
hash ^= Float.hashCode(tilt); hash *= 1099511628211L;
|
||||||
|
hash ^= Float.hashCode(lean); hash *= 1099511628211L;
|
||||||
|
hash ^= Float.hashCode(turn); hash *= 1099511628211L;
|
||||||
hash ^= Float.hashCode(indent); hash *= 1099511628211L;
|
hash ^= Float.hashCode(indent); hash *= 1099511628211L;
|
||||||
hash ^= Float.hashCode(height); hash *= 1099511628211L;
|
hash ^= Float.hashCode(height); hash *= 1099511628211L;
|
||||||
hash ^= Float.hashCode(size); hash *= 1099511628211L;
|
hash ^= Float.hashCode(size); hash *= 1099511628211L;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package carpet.script.utils;
|
package carpet.script.utils;
|
||||||
|
|
||||||
import carpet.CarpetSettings;
|
import carpet.CarpetSettings;
|
||||||
|
import carpet.utils.CarpetProfiler;
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
@@ -49,6 +50,7 @@ public class ShapesRenderer
|
|||||||
|
|
||||||
public void render(Camera camera, float partialTick)
|
public void render(Camera camera, float partialTick)
|
||||||
{
|
{
|
||||||
|
CarpetProfiler.ProfilerToken token = CarpetProfiler.start_section(null, "Scarpet client", CarpetProfiler.TYPE.GENERAL);
|
||||||
//Camera camera = this.client.gameRenderer.getCamera();
|
//Camera camera = this.client.gameRenderer.getCamera();
|
||||||
ClientWorld iWorld = this.client.world;
|
ClientWorld iWorld = this.client.world;
|
||||||
RegistryKey<World> dimensionType = iWorld.getRegistryKey();
|
RegistryKey<World> dimensionType = iWorld.getRegistryKey();
|
||||||
@@ -120,14 +122,17 @@ public class ShapesRenderer
|
|||||||
RenderSystem.defaultBlendFunc();
|
RenderSystem.defaultBlendFunc();
|
||||||
RenderSystem.enableTexture();
|
RenderSystem.enableTexture();
|
||||||
RenderSystem.shadeModel(7424);
|
RenderSystem.shadeModel(7424);
|
||||||
|
CarpetProfiler.end_current_section(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addShapes(ListTag tag)
|
public void addShapes(ListTag tag)
|
||||||
{
|
{
|
||||||
|
CarpetProfiler.ProfilerToken token = CarpetProfiler.start_section(null, "Scarpet client", CarpetProfiler.TYPE.GENERAL);
|
||||||
for (int i=0, count = tag.size(); i < count; i++)
|
for (int i=0, count = tag.size(); i < count; i++)
|
||||||
{
|
{
|
||||||
addShape(tag.getCompound(i));
|
addShape(tag.getCompound(i));
|
||||||
}
|
}
|
||||||
|
CarpetProfiler.end_current_section(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addShape(CompoundTag tag)
|
public void addShape(CompoundTag tag)
|
||||||
@@ -169,12 +174,14 @@ public class ShapesRenderer
|
|||||||
|
|
||||||
public void renewShapes()
|
public void renewShapes()
|
||||||
{
|
{
|
||||||
|
CarpetProfiler.ProfilerToken token = CarpetProfiler.start_section(null, "Scarpet client", CarpetProfiler.TYPE.GENERAL);
|
||||||
synchronized (shapes)
|
synchronized (shapes)
|
||||||
{
|
{
|
||||||
shapes.values().forEach(el -> el.values().forEach(shape -> {
|
shapes.values().forEach(el -> el.values().forEach(shape -> {
|
||||||
shape.expiryTick++;
|
shape.expiryTick++;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
CarpetProfiler.end_current_section(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract static class RenderedShape<T extends ShapeDispatcher.ExpiringShape>
|
public abstract static class RenderedShape<T extends ShapeDispatcher.ExpiringShape>
|
||||||
@@ -278,6 +285,14 @@ public class ShapesRenderer
|
|||||||
{
|
{
|
||||||
RenderSystem.rotatef(shape.tilt, 0.0f, 0.0f, 1.0f);
|
RenderSystem.rotatef(shape.tilt, 0.0f, 0.0f, 1.0f);
|
||||||
}
|
}
|
||||||
|
if (shape.lean!=0.0f)
|
||||||
|
{
|
||||||
|
RenderSystem.rotatef(shape.lean, 1.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
if (shape.turn!=0.0f)
|
||||||
|
{
|
||||||
|
RenderSystem.rotatef(shape.turn, 0.0f, 1.0f, 0.0f);
|
||||||
|
}
|
||||||
RenderSystem.translatef(-10*shape.indent, -10*shape.height-9, (float) (-10*renderEpsilon)-10*shape.raise);
|
RenderSystem.translatef(-10*shape.indent, -10*shape.height-9, (float) (-10*renderEpsilon)-10*shape.raise);
|
||||||
//if (visibleThroughWalls) RenderSystem.disableDepthTest();
|
//if (visibleThroughWalls) RenderSystem.disableDepthTest();
|
||||||
RenderSystem.scalef(-1.0F, 1.0F, 1.0F);
|
RenderSystem.scalef(-1.0F, 1.0F, 1.0F);
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import carpet.CarpetServer;
|
|||||||
import carpet.CarpetSettings;
|
import carpet.CarpetSettings;
|
||||||
import carpet.script.CarpetContext;
|
import carpet.script.CarpetContext;
|
||||||
import carpet.script.CarpetScriptHost;
|
import carpet.script.CarpetScriptHost;
|
||||||
|
import carpet.script.value.BooleanValue;
|
||||||
import carpet.script.value.ListValue;
|
import carpet.script.value.ListValue;
|
||||||
import carpet.script.value.MapValue;
|
import carpet.script.value.MapValue;
|
||||||
import carpet.script.value.NumericValue;
|
import carpet.script.value.NumericValue;
|
||||||
import carpet.script.value.StringValue;
|
import carpet.script.value.StringValue;
|
||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
|
import carpet.script.value.ValueConversions;
|
||||||
import carpet.settings.ParsedRule;
|
import carpet.settings.ParsedRule;
|
||||||
import carpet.settings.SettingsManager;
|
import carpet.settings.SettingsManager;
|
||||||
import com.sun.management.OperatingSystemMXBean;
|
import com.sun.management.OperatingSystemMXBean;
|
||||||
@@ -16,6 +18,7 @@ import net.fabricmc.loader.api.FabricLoader;
|
|||||||
import net.minecraft.SharedConstants;
|
import net.minecraft.SharedConstants;
|
||||||
import net.minecraft.util.WorldSavePath;
|
import net.minecraft.util.WorldSavePath;
|
||||||
import net.minecraft.world.GameRules;
|
import net.minecraft.world.GameRules;
|
||||||
|
import net.minecraft.world.WorldProperties;
|
||||||
|
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -49,6 +52,12 @@ public class SystemInfo {
|
|||||||
String tlf = serverPath.getName(nodeCount-2).toString();
|
String tlf = serverPath.getName(nodeCount-2).toString();
|
||||||
return StringValue.of(tlf);
|
return StringValue.of(tlf);
|
||||||
});
|
});
|
||||||
|
put("world_dimensions", c -> ListValue.wrap(c.s.getMinecraftServer().getWorldRegistryKeys().stream().map(k -> ValueConversions.of(k.getValue())).collect(Collectors.toList())));
|
||||||
|
put("world_spawn_point", c -> {
|
||||||
|
WorldProperties prop = c.s.getMinecraftServer().getOverworld().getLevelProperties();
|
||||||
|
return ListValue.of(NumericValue.of(prop.getSpawnX()), NumericValue.of(prop.getSpawnY()), NumericValue.of(prop.getSpawnZ()));
|
||||||
|
});
|
||||||
|
|
||||||
put("game_difficulty", c -> StringValue.of(c.s.getMinecraftServer().getSaveProperties().getDifficulty().getName()));
|
put("game_difficulty", c -> StringValue.of(c.s.getMinecraftServer().getSaveProperties().getDifficulty().getName()));
|
||||||
put("game_hardcore", c -> new NumericValue(c.s.getMinecraftServer().getSaveProperties().isHardcore()));
|
put("game_hardcore", c -> new NumericValue(c.s.getMinecraftServer().getSaveProperties().isHardcore()));
|
||||||
put("game_storage_format", c -> StringValue.of(c.s.getMinecraftServer().getSaveProperties().getFormatName(c.s.getMinecraftServer().getSaveProperties().getVersion())));
|
put("game_storage_format", c -> StringValue.of(c.s.getMinecraftServer().getSaveProperties().getFormatName(c.s.getMinecraftServer().getSaveProperties().getVersion())));
|
||||||
@@ -57,7 +66,19 @@ public class SystemInfo {
|
|||||||
put("game_view_distance", c -> new NumericValue(c.s.getMinecraftServer().getPlayerManager().getViewDistance()));
|
put("game_view_distance", c -> new NumericValue(c.s.getMinecraftServer().getPlayerManager().getViewDistance()));
|
||||||
put("game_mod_name", c -> StringValue.of(c.s.getMinecraftServer().getServerModName()));
|
put("game_mod_name", c -> StringValue.of(c.s.getMinecraftServer().getServerModName()));
|
||||||
put("game_version", c -> StringValue.of(c.s.getMinecraftServer().getVersion()));
|
put("game_version", c -> StringValue.of(c.s.getMinecraftServer().getVersion()));
|
||||||
|
put("game_target", c -> StringValue.of(SharedConstants.getGameVersion().getReleaseTarget()));
|
||||||
|
put("game_protocol", c -> NumericValue.of(SharedConstants.method_31372()));
|
||||||
|
put("game_major_target", c -> {
|
||||||
|
String [] vers = SharedConstants.getGameVersion().getReleaseTarget().split("\\.");
|
||||||
|
return NumericValue.of((vers.length > 1)?Integer.parseInt(vers[1]):0);
|
||||||
|
});
|
||||||
|
put("game_minor_target", c -> {
|
||||||
|
String [] vers = SharedConstants.getGameVersion().getReleaseTarget().split("\\.");
|
||||||
|
return NumericValue.of((vers.length > 2)?Integer.parseInt(vers[2]):0);
|
||||||
|
});
|
||||||
|
put("game_stable", c -> BooleanValue.of(SharedConstants.getGameVersion().isStable()));
|
||||||
put("game_data_version", c->NumericValue.of(SharedConstants.getGameVersion().getWorldVersion()));
|
put("game_data_version", c->NumericValue.of(SharedConstants.getGameVersion().getWorldVersion()));
|
||||||
|
put("game_pack_version", c->NumericValue.of(SharedConstants.getGameVersion().getPackVersion()));
|
||||||
|
|
||||||
put("server_ip", c -> StringValue.of(c.s.getMinecraftServer().getServerIp()));
|
put("server_ip", c -> StringValue.of(c.s.getMinecraftServer().getServerIp()));
|
||||||
put("server_whitelisted", c -> new NumericValue(c.s.getMinecraftServer().isEnforceWhitelist()));
|
put("server_whitelisted", c -> new NumericValue(c.s.getMinecraftServer().isEnforceWhitelist()));
|
||||||
|
|||||||
@@ -157,5 +157,5 @@ public class WorldTools
|
|||||||
@Override public void stop() { }
|
@Override public void stop() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final WorldGenerationProgressListener NOOP_LISTENER = new NoopWorldGenerationProgressListener();
|
public static final WorldGenerationProgressListener NOOP_LISTENER = new NoopWorldGenerationProgressListener();
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,9 @@ package carpet.script.value;
|
|||||||
|
|
||||||
import carpet.script.CarpetContext;
|
import carpet.script.CarpetContext;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import carpet.script.exception.ThrowStatement;
|
||||||
|
import carpet.script.exception.Throwables;
|
||||||
|
|
||||||
import com.mojang.brigadier.StringReader;
|
import com.mojang.brigadier.StringReader;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
@@ -73,7 +76,7 @@ public class BlockValue extends Value
|
|||||||
catch (CommandSyntaxException ignored)
|
catch (CommandSyntaxException ignored)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
throw new InternalExpressionException("Cannot parse block: "+str);
|
throw new ThrowStatement(str, Throwables.UNKNOWN_BLOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BlockPos locateBlockPos(CarpetContext c, int xpos, int ypos, int zpos)
|
public static BlockPos locateBlockPos(CarpetContext c, int xpos, int ypos, int zpos)
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package carpet.script.value;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import net.minecraft.nbt.ByteTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
|
||||||
|
public class BooleanValue extends NumericValue
|
||||||
|
{
|
||||||
|
public static final BooleanValue FALSE = new BooleanValue(false);
|
||||||
|
public static final BooleanValue TRUE = new BooleanValue(true);
|
||||||
|
|
||||||
|
boolean boolValue;
|
||||||
|
private BooleanValue(boolean boolval) {
|
||||||
|
super(boolval);
|
||||||
|
boolValue = boolval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BooleanValue of(boolean value)
|
||||||
|
{
|
||||||
|
return value?TRUE:FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getString() {
|
||||||
|
return boolValue?"true":"false";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPrettyString() {
|
||||||
|
return getString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTypeString() {
|
||||||
|
return "bool";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value clone() {
|
||||||
|
return new BooleanValue(boolValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Boolean.hashCode(boolValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tag toTag(boolean force) {
|
||||||
|
return ByteTag.of(boolValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonElement toJson() {
|
||||||
|
return new JsonPrimitive(boolValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInteger() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,6 +56,7 @@ import net.minecraft.server.command.ServerCommandSource;
|
|||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.text.LiteralText;
|
import net.minecraft.text.LiteralText;
|
||||||
|
import net.minecraft.util.Hand;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.hit.BlockHitResult;
|
import net.minecraft.util.hit.BlockHitResult;
|
||||||
import net.minecraft.util.hit.EntityHitResult;
|
import net.minecraft.util.hit.EntityHitResult;
|
||||||
@@ -98,6 +99,12 @@ public class EntityValue extends Value
|
|||||||
entity = e;
|
entity = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Value of(Entity e)
|
||||||
|
{
|
||||||
|
if (e == null) return Value.NULL;
|
||||||
|
return new EntityValue(e);
|
||||||
|
}
|
||||||
|
|
||||||
private static final Map<String, EntitySelector> selectorCache = new HashMap<>();
|
private static final Map<String, EntitySelector> selectorCache = new HashMap<>();
|
||||||
public static Collection<? extends Entity > getEntitiesFromSelector(ServerCommandSource source, String selector)
|
public static Collection<? extends Entity > getEntitiesFromSelector(ServerCommandSource source, String selector)
|
||||||
{
|
{
|
||||||
@@ -416,7 +423,7 @@ public class EntityValue extends Value
|
|||||||
put("motion_z", (e, a) -> new NumericValue(e.getVelocity().z));
|
put("motion_z", (e, a) -> new NumericValue(e.getVelocity().z));
|
||||||
put("on_ground", (e, a) -> new NumericValue(e.isOnGround()));
|
put("on_ground", (e, a) -> new NumericValue(e.isOnGround()));
|
||||||
put("name", (e, a) -> new StringValue(e.getName().getString()));
|
put("name", (e, a) -> new StringValue(e.getName().getString()));
|
||||||
put("display_name", (e, a) -> new StringValue(e.getDisplayName().getString()));
|
put("display_name", (e, a) -> new FormattedTextValue(e.getDisplayName()));
|
||||||
put("command_name", (e, a) -> new StringValue(e.getEntityName()));
|
put("command_name", (e, a) -> new StringValue(e.getEntityName()));
|
||||||
put("custom_name", (e, a) -> e.hasCustomName()?new StringValue(e.getCustomName().getString()):Value.NULL);
|
put("custom_name", (e, a) -> e.hasCustomName()?new StringValue(e.getCustomName().getString()):Value.NULL);
|
||||||
put("type", (e, a) -> new StringValue(nameFromRegistryId(Registry.ENTITY_TYPE.getId(e.getType()))));
|
put("type", (e, a) -> new StringValue(nameFromRegistryId(Registry.ENTITY_TYPE.getId(e.getType()))));
|
||||||
@@ -429,7 +436,7 @@ public class EntityValue extends Value
|
|||||||
put("tags", (e, a) -> ListValue.wrap(e.getScoreboardTags().stream().map(StringValue::new).collect(Collectors.toList())));
|
put("tags", (e, a) -> ListValue.wrap(e.getScoreboardTags().stream().map(StringValue::new).collect(Collectors.toList())));
|
||||||
|
|
||||||
put("scoreboard_tags", (e, a) -> ListValue.wrap(e.getScoreboardTags().stream().map(StringValue::new).collect(Collectors.toList())));
|
put("scoreboard_tags", (e, a) -> ListValue.wrap(e.getScoreboardTags().stream().map(StringValue::new).collect(Collectors.toList())));
|
||||||
put("entity_tags", (e, a) -> ListValue.wrap(e.getServer().getTagManager().getEntityTypes().getTagsFor(e.getType()).stream().map(ValueConversions::of).collect(Collectors.toList())));
|
put("entity_tags", (e, a) -> ListValue.wrap(e.getServer().getTagManager().getEntityTypes().getTags().entrySet().stream().filter(entry -> entry.getValue().contains(e.getType())).map(entry -> ValueConversions.of(entry.getKey())).collect(Collectors.toList())));
|
||||||
// deprecated
|
// deprecated
|
||||||
put("has_tag", (e, a) -> new NumericValue(e.getScoreboardTags().contains(a.getString())));
|
put("has_tag", (e, a) -> new NumericValue(e.getScoreboardTags().contains(a.getString())));
|
||||||
|
|
||||||
@@ -1307,7 +1314,7 @@ public class EntityValue extends Value
|
|||||||
else if (v instanceof ListValue)
|
else if (v instanceof ListValue)
|
||||||
{
|
{
|
||||||
List<Value> lv = ((ListValue) v).getItems();
|
List<Value> lv = ((ListValue) v).getItems();
|
||||||
if (lv.size() >= 1 && lv.size() <= 5)
|
if (lv.size() >= 1 && lv.size() <= 6)
|
||||||
{
|
{
|
||||||
String effectName = lv.get(0).getString();
|
String effectName = lv.get(0).getString();
|
||||||
StatusEffect effect = Registry.STATUS_EFFECT.get(new Identifier(effectName));
|
StatusEffect effect = Registry.STATUS_EFFECT.get(new Identifier(effectName));
|
||||||
@@ -1376,6 +1383,19 @@ public class EntityValue extends Value
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
put("swing", (e, v) -> {
|
||||||
|
if (e instanceof LivingEntity)
|
||||||
|
{
|
||||||
|
Hand hand = Hand.MAIN_HAND;
|
||||||
|
if (v != null)
|
||||||
|
{
|
||||||
|
String handString = v.getString().toLowerCase(Locale.ROOT);
|
||||||
|
if (handString.equals("offhand") || handString.equals("off_hand")) hand = Hand.OFF_HAND;
|
||||||
|
}
|
||||||
|
((LivingEntity)e).swingHand(hand, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
put("silent",(e,v)-> e.setSilent(v.getBoolean()));
|
put("silent",(e,v)-> e.setSilent(v.getBoolean()));
|
||||||
|
|
||||||
put("gravity",(e,v)-> e.setNoGravity(!v.getBoolean()));
|
put("gravity",(e,v)-> e.setNoGravity(!v.getBoolean()));
|
||||||
|
|||||||
@@ -215,7 +215,6 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
|||||||
|
|
||||||
}
|
}
|
||||||
Value retVal;
|
Value retVal;
|
||||||
boolean rethrow = false;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
retVal = body.evalValue(newFrame, type); // todo not sure if we need to propagete type / consider boolean context in defined functions - answer seems ye
|
retVal = body.evalValue(newFrame, type); // todo not sure if we need to propagete type / consider boolean context in defined functions - answer seems ye
|
||||||
@@ -228,15 +227,6 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
|||||||
{
|
{
|
||||||
retVal = returnStatement.retval;
|
retVal = returnStatement.retval;
|
||||||
}
|
}
|
||||||
catch (ThrowStatement throwStatement) // might not be really necessary
|
|
||||||
{
|
|
||||||
retVal = throwStatement.retval;
|
|
||||||
rethrow = true;
|
|
||||||
}
|
|
||||||
if (rethrow)
|
|
||||||
{
|
|
||||||
throw new ThrowStatement(retVal);
|
|
||||||
}
|
|
||||||
Value otherRetVal = retVal;
|
Value otherRetVal = retVal;
|
||||||
return (cc, tt) -> otherRetVal;
|
return (cc, tt) -> otherRetVal;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -341,7 +341,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
|||||||
int from = normalizeIndex(fromDesc, size);
|
int from = normalizeIndex(fromDesc, size);
|
||||||
if (toDesc == null)
|
if (toDesc == null)
|
||||||
return new ListValue((Collection<? extends Value>) getItems().subList(from, size));
|
return new ListValue((Collection<? extends Value>) getItems().subList(from, size));
|
||||||
int to = normalizeIndex(toDesc, size);
|
int to = normalizeIndex(toDesc, size+1);
|
||||||
if (from > to)
|
if (from > to)
|
||||||
return ListValue.of();
|
return ListValue.of();
|
||||||
return new ListValue((Collection<? extends Value>) getItems().subList(from, to));
|
return new ListValue((Collection<? extends Value>) getItems().subList(from, to));
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import net.minecraft.nbt.CompoundTag;
|
|||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -16,7 +17,7 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
public class MapValue extends AbstractListValue implements ContainerValueInterface
|
public class MapValue extends AbstractListValue implements ContainerValueInterface
|
||||||
{
|
{
|
||||||
private Map<Value, Value> map;
|
private final Map<Value, Value> map;
|
||||||
|
|
||||||
private MapValue()
|
private MapValue()
|
||||||
{
|
{
|
||||||
@@ -268,7 +269,9 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
|
|||||||
public JsonElement toJson()
|
public JsonElement toJson()
|
||||||
{
|
{
|
||||||
JsonObject jsonMap = new JsonObject();
|
JsonObject jsonMap = new JsonObject();
|
||||||
map.forEach((k, v) -> jsonMap.add(k.getString(), v.toJson()));
|
List<Value> keys = new ArrayList<>(map.keySet());
|
||||||
|
Collections.sort(keys);
|
||||||
|
keys.forEach( k -> jsonMap.add(k.getString(), map.get(k).toJson()));
|
||||||
return jsonMap;
|
return jsonMap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,8 @@ import carpet.fakes.InventoryBearerInterface;
|
|||||||
import carpet.script.CarpetContext;
|
import carpet.script.CarpetContext;
|
||||||
import carpet.script.LazyValue;
|
import carpet.script.LazyValue;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import carpet.script.exception.ThrowStatement;
|
||||||
|
import carpet.script.exception.Throwables;
|
||||||
import carpet.script.utils.EquipmentInventory;
|
import carpet.script.utils.EquipmentInventory;
|
||||||
import com.mojang.brigadier.StringReader;
|
import com.mojang.brigadier.StringReader;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
@@ -297,7 +299,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
}
|
}
|
||||||
catch (CommandSyntaxException e)
|
catch (CommandSyntaxException e)
|
||||||
{
|
{
|
||||||
throw new InternalExpressionException("Incorrect item: "+itemString);
|
throw new ThrowStatement(itemString, Throwables.UNKNOWN_ITEM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import net.minecraft.nbt.Tag;
|
|||||||
|
|
||||||
public class NullValue extends NumericValue // TODO check nonsingleton code
|
public class NullValue extends NumericValue // TODO check nonsingleton code
|
||||||
{
|
{
|
||||||
|
public static final NullValue NULL = new NullValue();
|
||||||
@Override
|
@Override
|
||||||
public String getString()
|
public String getString()
|
||||||
{
|
{
|
||||||
@@ -32,7 +33,7 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
|
|||||||
{
|
{
|
||||||
return new NullValue();
|
return new NullValue();
|
||||||
}
|
}
|
||||||
public NullValue() {super(0.0D);}
|
private NullValue() {super(false);}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object o)
|
public boolean equals(final Object o)
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
public abstract class Value implements Comparable<Value>, Cloneable
|
public abstract class Value implements Comparable<Value>, Cloneable
|
||||||
{
|
{
|
||||||
public static NumericValue FALSE = new NumericValue(0);
|
public static NumericValue FALSE = BooleanValue.FALSE;
|
||||||
public static NumericValue TRUE = new NumericValue(1);
|
public static NumericValue TRUE = BooleanValue.TRUE;
|
||||||
public static NumericValue ZERO = FALSE;
|
public static NumericValue ZERO = new NumericValue(0);
|
||||||
public static NumericValue ONE = TRUE;
|
public static NumericValue ONE = new NumericValue(1);
|
||||||
|
|
||||||
public static NullValue NULL = new NullValue();
|
public static NullValue NULL = NullValue.NULL;
|
||||||
|
|
||||||
public String boundVariable;
|
public String boundVariable;
|
||||||
|
|
||||||
@@ -141,12 +141,12 @@ public abstract class Value implements Comparable<Value>, Cloneable
|
|||||||
|
|
||||||
public void assertAssignable()
|
public void assertAssignable()
|
||||||
{
|
{
|
||||||
if (boundVariable == null || boundVariable.startsWith("_"))
|
if (boundVariable == null)// || boundVariable.startsWith("_"))
|
||||||
{
|
{
|
||||||
if (boundVariable != null)
|
/*if (boundVariable != null)
|
||||||
{
|
{
|
||||||
throw new InternalExpressionException(boundVariable+ " cannot be assigned a new value");
|
throw new InternalExpressionException(boundVariable+ " cannot be assigned a new value");
|
||||||
}
|
}*/
|
||||||
throw new InternalExpressionException(getString()+ " is not a variable");
|
throw new InternalExpressionException(getString()+ " is not a variable");
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -186,7 +186,7 @@ public abstract class Value implements Comparable<Value>, Cloneable
|
|||||||
int size = value.length();
|
int size = value.length();
|
||||||
int from = ListValue.normalizeIndex(fromDesc, size);
|
int from = ListValue.normalizeIndex(fromDesc, size);
|
||||||
if (toDesc == null) return new StringValue(value.substring(from));
|
if (toDesc == null) return new StringValue(value.substring(from));
|
||||||
int to = ListValue.normalizeIndex(toDesc, size);
|
int to = ListValue.normalizeIndex(toDesc, size+1);
|
||||||
if (from > to) return StringValue.EMPTY;
|
if (from > to) return StringValue.EMPTY;
|
||||||
return new StringValue(value.substring(from, to));
|
return new StringValue(value.substring(from, to));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package carpet.script.value;
|
|||||||
|
|
||||||
import carpet.fakes.BlockPredicateInterface;
|
import carpet.fakes.BlockPredicateInterface;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import carpet.script.exception.ThrowStatement;
|
||||||
|
import carpet.script.exception.Throwables;
|
||||||
import carpet.utils.BlockInfo;
|
import carpet.utils.BlockInfo;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.MaterialColor;
|
import net.minecraft.block.MaterialColor;
|
||||||
@@ -158,7 +160,7 @@ public class ValueConversions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dim == null)
|
if (dim == null)
|
||||||
throw new InternalExpressionException("Incorrect dimension string: "+dimString);
|
throw new ThrowStatement(dimString, Throwables.UNKNOWN_DIMENSION);
|
||||||
return server.getWorld(dim);
|
return server.getWorld(dim);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -318,10 +318,12 @@ public class SettingsManager
|
|||||||
}
|
}
|
||||||
server.send(new ServerTask(this.server.getTicks(), () ->
|
server.send(new ServerTask(this.server.getTicks(), () ->
|
||||||
{
|
{
|
||||||
for (ServerPlayerEntity entityplayermp : server.getPlayerManager().getPlayerList())
|
try {
|
||||||
{
|
for (ServerPlayerEntity entityplayermp : server.getPlayerManager().getPlayerList()) {
|
||||||
server.getCommandManager().sendCommandTree(entityplayermp);
|
server.getCommandManager().sendCommandTree(entityplayermp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
catch (NullPointerException ignored) {}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package carpet.utils;
|
package carpet.utils;
|
||||||
|
|
||||||
import carpet.CarpetSettings;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
||||||
import net.minecraft.block.entity.BlockEntityType;
|
import net.minecraft.block.entity.BlockEntityType;
|
||||||
@@ -9,6 +8,7 @@ import net.minecraft.entity.EntityType;
|
|||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.block.entity.BlockEntity;
|
import net.minecraft.block.entity.BlockEntity;
|
||||||
import net.minecraft.server.command.ServerCommandSource;
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.registry.Registry;
|
import net.minecraft.util.registry.Registry;
|
||||||
import net.minecraft.util.registry.RegistryKey;
|
import net.minecraft.util.registry.RegistryKey;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
@@ -29,9 +29,13 @@ public class CarpetProfiler
|
|||||||
private static ServerCommandSource currentRequester = null;
|
private static ServerCommandSource currentRequester = null;
|
||||||
public static int tick_health_requested = 0;
|
public static int tick_health_requested = 0;
|
||||||
private static int tick_health_elapsed = 0;
|
private static int tick_health_elapsed = 0;
|
||||||
private static TYPE test_type = TYPE.NONE; //1 for ticks, 2 for entities;
|
private static TYPE test_type = TYPE.NONE; //1 for ticks, 2 for entities
|
||||||
private static long current_tick_start = 0;
|
private static long current_tick_start = 0;
|
||||||
private static final String[] GENERAL_SECTIONS = {"Network", "Autosave", "Async Tasks", "Datapacks",};
|
private static final String[] GENERAL_SECTIONS = {"Network", "Autosave", "Async Tasks", "Datapacks", "Carpet"};
|
||||||
|
private static final String[] SCARPET_SECTIONS = {
|
||||||
|
"Scarpet run", "Scarpet events", "Scarpet schedule",
|
||||||
|
"Scarpet command", "Scarpet load", "Scarpet app data", "Scarpet client"
|
||||||
|
};
|
||||||
private static final String[] SECTIONS = {
|
private static final String[] SECTIONS = {
|
||||||
"Spawning and Random Ticks", "Ticket Manager","Unloading",
|
"Spawning and Random Ticks", "Ticket Manager","Unloading",
|
||||||
"Blocks", "Entities", "Block Entities",
|
"Blocks", "Entities", "Block Entities",
|
||||||
@@ -53,28 +57,15 @@ public class CarpetProfiler
|
|||||||
public final long start;
|
public final long start;
|
||||||
public final World world;
|
public final World world;
|
||||||
|
|
||||||
public ProfilerToken(TYPE type, Object section, World world, long start)
|
public ProfilerToken(TYPE type, Object section, World world)
|
||||||
{
|
{
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.section = section;
|
this.section = section;
|
||||||
this.start = start;
|
this.start = System.nanoTime();
|
||||||
this.world = world;
|
this.world = world;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getSectionString(World world, String section)
|
|
||||||
{
|
|
||||||
return String.format("%s.%s%s",
|
|
||||||
world.getRegistryKey().getValue().getPath(),
|
|
||||||
section, // ^ getDImension getId
|
|
||||||
world.isClient ? " (Client)" : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getDimName(RegistryKey<World> world)
|
|
||||||
{
|
|
||||||
return world.getValue().getPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void prepare_tick_report(ServerCommandSource source, int ticks)
|
public static void prepare_tick_report(ServerCommandSource source, int ticks)
|
||||||
{
|
{
|
||||||
//maybe add so it only spams the sending player, but honestly - all may want to see it
|
//maybe add so it only spams the sending player, but honestly - all may want to see it
|
||||||
@@ -83,17 +74,19 @@ public class CarpetProfiler
|
|||||||
ENTITY_TIMES.clear();
|
ENTITY_TIMES.clear();
|
||||||
test_type = TYPE.GENERAL;
|
test_type = TYPE.GENERAL;
|
||||||
SECTION_STATS.put("tick", 0L);
|
SECTION_STATS.put("tick", 0L);
|
||||||
SECTION_STATS.put("Network", 0L);
|
|
||||||
SECTION_STATS.put("Autosave", 0L);
|
|
||||||
for (String section : GENERAL_SECTIONS)
|
for (String section : GENERAL_SECTIONS)
|
||||||
{
|
{
|
||||||
SECTION_STATS.put(section, 0L);
|
SECTION_STATS.put(section, 0L);
|
||||||
}
|
}
|
||||||
|
for (String section : SCARPET_SECTIONS)
|
||||||
|
{
|
||||||
|
SECTION_STATS.put(section, 0L);
|
||||||
|
}
|
||||||
for (RegistryKey<World> level : source.getMinecraftServer().getWorldRegistryKeys())
|
for (RegistryKey<World> level : source.getMinecraftServer().getWorldRegistryKeys())
|
||||||
{
|
{
|
||||||
for (String section : SECTIONS)
|
for (String section : SECTIONS)
|
||||||
{
|
{
|
||||||
SECTION_STATS.put(getDimName(level) + "." + section, 0L);
|
SECTION_STATS.put(level.getValue() + "." + section, 0L);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,21 +114,21 @@ public class CarpetProfiler
|
|||||||
{
|
{
|
||||||
if (tick_health_requested == 0L || test_type != TYPE.GENERAL || current_tick_start == 0)
|
if (tick_health_requested == 0L || test_type != TYPE.GENERAL || current_tick_start == 0)
|
||||||
return null;
|
return null;
|
||||||
return new ProfilerToken(type, name, world, System.nanoTime());
|
return new ProfilerToken(type, name, world);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProfilerToken start_entity_section(World world, Entity e, TYPE type)
|
public static ProfilerToken start_entity_section(World world, Entity e, TYPE type)
|
||||||
{
|
{
|
||||||
if (tick_health_requested == 0L || test_type != TYPE.ENTITY || current_tick_start == 0)
|
if (tick_health_requested == 0L || test_type != TYPE.ENTITY || current_tick_start == 0)
|
||||||
return null;
|
return null;
|
||||||
return new ProfilerToken(type, e.getType(), world, System.nanoTime());
|
return new ProfilerToken(type, e.getType(), world);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ProfilerToken start_block_entity_section(World world, BlockEntity be, TYPE type)
|
public static ProfilerToken start_block_entity_section(World world, BlockEntity be, TYPE type)
|
||||||
{
|
{
|
||||||
if (tick_health_requested == 0L || test_type != TYPE.ENTITY || current_tick_start == 0)
|
if (tick_health_requested == 0L || test_type != TYPE.ENTITY || current_tick_start == 0)
|
||||||
return null;
|
return null;
|
||||||
return new ProfilerToken(type, be.getType(), world, System.nanoTime());
|
return new ProfilerToken(type, be.getType(), world);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void end_current_section(ProfilerToken tok)
|
public static void end_current_section(ProfilerToken tok)
|
||||||
@@ -145,14 +138,11 @@ public class CarpetProfiler
|
|||||||
long end_time = System.nanoTime();
|
long end_time = System.nanoTime();
|
||||||
if (tok.type == TYPE.GENERAL)
|
if (tok.type == TYPE.GENERAL)
|
||||||
{
|
{
|
||||||
String current_section = (tok.world == null) ? (String) tok.section :
|
World world = tok.world;
|
||||||
getSectionString(tok.world, (String)tok.section);
|
String current_section = (world == null) ?
|
||||||
if (SECTION_STATS.get(current_section) == null)
|
(String) tok.section :
|
||||||
{
|
String.format("%s.%s%s", world.getRegistryKey().getValue(), tok.section, world.isClient ? " (Client)" : "");
|
||||||
CarpetSettings.LOG.error("Current section: "+current_section);
|
SECTION_STATS.put(current_section, SECTION_STATS.getOrDefault(current_section, 0L) + end_time - tok.start);
|
||||||
}
|
|
||||||
else
|
|
||||||
SECTION_STATS.put(current_section, SECTION_STATS.get(current_section) + end_time - tok.start);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,12 +161,10 @@ public class CarpetProfiler
|
|||||||
current_tick_start = System.nanoTime();
|
current_tick_start = System.nanoTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void end_tick_profiling(MinecraftServer server, ProfilerToken postTasks)
|
public static void end_tick_profiling(MinecraftServer server)
|
||||||
{
|
{
|
||||||
if (current_tick_start == 0L)
|
if (current_tick_start == 0L)
|
||||||
return;
|
return;
|
||||||
if (postTasks != null)
|
|
||||||
end_current_section(postTasks);
|
|
||||||
SECTION_STATS.put("tick", SECTION_STATS.get("tick") + System.nanoTime() - current_tick_start);
|
SECTION_STATS.put("tick", SECTION_STATS.get("tick") + System.nanoTime() - current_tick_start);
|
||||||
tick_health_elapsed--;
|
tick_health_elapsed--;
|
||||||
if (tick_health_elapsed <= 0)
|
if (tick_health_elapsed <= 0)
|
||||||
@@ -215,7 +203,7 @@ public class CarpetProfiler
|
|||||||
long total_tick_time = SECTION_STATS.get("tick");
|
long total_tick_time = SECTION_STATS.get("tick");
|
||||||
double divider = 1.0D / tick_health_requested / 1000000;
|
double divider = 1.0D / tick_health_requested / 1000000;
|
||||||
Messenger.m(currentRequester, "w ");
|
Messenger.m(currentRequester, "w ");
|
||||||
Messenger.m(currentRequester, String.format("gi Avg. server tick time: %.3fms", divider * total_tick_time));
|
Messenger.m(currentRequester, "wb Average tick time: ", String.format("yb %.3fms", divider * total_tick_time));
|
||||||
long accumulated = 0L;
|
long accumulated = 0L;
|
||||||
|
|
||||||
for (String section : GENERAL_SECTIONS)
|
for (String section : GENERAL_SECTIONS)
|
||||||
@@ -224,17 +212,26 @@ public class CarpetProfiler
|
|||||||
if (amount > 0.01)
|
if (amount > 0.01)
|
||||||
{
|
{
|
||||||
accumulated += SECTION_STATS.get(section);
|
accumulated += SECTION_STATS.get(section);
|
||||||
Messenger.m(currentRequester, String.format("gi %s: %.3fms", section, amount));
|
Messenger.m(currentRequester, "w "+section+": ", String.format("y %.3fms", amount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String section : SCARPET_SECTIONS)
|
||||||
|
{
|
||||||
|
double amount = divider * SECTION_STATS.get(section);
|
||||||
|
if (amount > 0.01)
|
||||||
|
{
|
||||||
|
Messenger.m(currentRequester, "gi "+section+": ", String.format("di %.3fms", amount));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (RegistryKey<World> dim : server.getWorldRegistryKeys())
|
for (RegistryKey<World> dim : server.getWorldRegistryKeys())
|
||||||
{
|
{
|
||||||
String dimension = getDimName(dim);
|
Identifier dimensionId = dim.getValue();
|
||||||
boolean hasSomethin = false;
|
boolean hasSomethin = false;
|
||||||
for (String section : SECTIONS)
|
for (String section : SECTIONS)
|
||||||
{
|
{
|
||||||
double amount = divider * SECTION_STATS.get(dimension + "." + section);
|
|
||||||
|
double amount = divider * SECTION_STATS.getOrDefault(dimensionId + "." + section, 0L);
|
||||||
if (amount > 0.01)
|
if (amount > 0.01)
|
||||||
{
|
{
|
||||||
hasSomethin = true;
|
hasSomethin = true;
|
||||||
@@ -245,15 +242,16 @@ public class CarpetProfiler
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Messenger.m(currentRequester, "gi "+dimension + ":");
|
Messenger.m(currentRequester, "wb "+(dimensionId.getNamespace().equals("minecraft")?dimensionId.getPath():dimensionId.toString()) + ":");
|
||||||
for (String section : SECTIONS)
|
for (String section : SECTIONS)
|
||||||
{
|
{
|
||||||
double amount = divider * SECTION_STATS.get(dimension + "." + section);
|
double amount = divider * SECTION_STATS.getOrDefault(dimensionId + "." + section, 0L);
|
||||||
if (amount > 0.01)
|
if (amount > 0.01)
|
||||||
{
|
{
|
||||||
if (!(section.endsWith("(Client)")))
|
boolean cli = section.endsWith("(Client)");
|
||||||
accumulated += SECTION_STATS.get(dimension + "." + section);
|
if (!cli)
|
||||||
Messenger.m(currentRequester, String.format("gi - %s: %.3fms", section, amount));
|
accumulated += SECTION_STATS.get(dimensionId + "." + section);
|
||||||
|
Messenger.m(currentRequester, String.format("%s - %s: ", cli?"gi":"w", section), String.format("%s %.3fms", cli?"di":"y", amount));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -265,20 +263,22 @@ public class CarpetProfiler
|
|||||||
|
|
||||||
private static String sectionName(Pair<World,Object> section)
|
private static String sectionName(Pair<World,Object> section)
|
||||||
{
|
{
|
||||||
String name;
|
Identifier id;
|
||||||
if (section.getValue() instanceof EntityType)
|
if (section.getValue() instanceof EntityType)
|
||||||
{
|
{
|
||||||
name = Registry.ENTITY_TYPE.getId((EntityType<?>) section.getValue()).getPath();
|
id = Registry.ENTITY_TYPE.getId((EntityType<?>) section.getValue());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
name = Registry.BLOCK_ENTITY_TYPE.getId((BlockEntityType<?>) section.getValue()).getPath();
|
id = Registry.BLOCK_ENTITY_TYPE.getId((BlockEntityType<?>) section.getValue());
|
||||||
}
|
}
|
||||||
|
String name = "minecraft".equals(id.getNamespace())?id.getPath():id.toString();
|
||||||
if (section.getKey().isClient)
|
if (section.getKey().isClient)
|
||||||
{
|
{
|
||||||
name += " (client)";
|
name += " (client)";
|
||||||
}
|
}
|
||||||
String dim = section.getKey().getRegistryKey().getValue().getPath();
|
Identifier dimkey = section.getKey().getRegistryKey().getValue();
|
||||||
|
String dim = "minecraft".equals(dimkey.getNamespace())?dimkey.getPath():dimkey.toString();
|
||||||
return name+" in "+dim;
|
return name+" in "+dim;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,31 +289,35 @@ public class CarpetProfiler
|
|||||||
long total_tick_time = SECTION_STATS.get("tick");
|
long total_tick_time = SECTION_STATS.get("tick");
|
||||||
double divider = 1.0D / tick_health_requested / 1000000;
|
double divider = 1.0D / tick_health_requested / 1000000;
|
||||||
double divider_1 = 1.0D / (tick_health_requested - 1) / 1000000;
|
double divider_1 = 1.0D / (tick_health_requested - 1) / 1000000;
|
||||||
Messenger.m(currentRequester, "gi ");
|
Messenger.m(currentRequester, "w ");
|
||||||
Messenger.m(currentRequester, String.format("gi Average tick time: %.3fms", divider * total_tick_time));
|
Messenger.m(currentRequester, "wb Average tick time: ", String.format("yb %.3fms", divider * total_tick_time));
|
||||||
SECTION_STATS.remove("tick");
|
SECTION_STATS.remove("tick");
|
||||||
Messenger.m(currentRequester, "gi Top 10 counts:");
|
Messenger.m(currentRequester, "wb Top 10 counts:");
|
||||||
int total = 0;
|
int total = 0;
|
||||||
for (Object2LongMap.Entry<Pair<World, Object>> sectionEntry : ENTITY_COUNT.object2LongEntrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).collect(Collectors.toList()))
|
for (Object2LongMap.Entry<Pair<World, Object>> sectionEntry : ENTITY_COUNT.object2LongEntrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).collect(Collectors.toList()))
|
||||||
{
|
{
|
||||||
if (++total > 10) break;
|
if (++total > 10) break;
|
||||||
Pair<World,Object> section = sectionEntry.getKey();
|
Pair<World,Object> section = sectionEntry.getKey();
|
||||||
|
boolean cli = section.getKey().isClient;
|
||||||
Messenger.m(currentRequester, String.format(
|
Messenger.m(currentRequester, String.format(
|
||||||
"gi - %s: %.1f",
|
"%s - %s: ", cli?"gi":"w",
|
||||||
sectionName(section),
|
sectionName(section)),
|
||||||
1.0D * sectionEntry.getLongValue() / (tick_health_requested - (section.getKey().isClient? 1 : 0))
|
String.format("%s %.1f", cli?"di":"y",
|
||||||
|
1.0D * sectionEntry.getLongValue() / (tick_health_requested - (cli? 1 : 0))
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Messenger.m(currentRequester, "gi Top 10 CPU hogs:");
|
Messenger.m(currentRequester, "wb Top 10 CPU hogs:");
|
||||||
total = 0;
|
total = 0;
|
||||||
for (Object2LongMap.Entry<Pair<World,Object>> sectionEntry : ENTITY_TIMES.object2LongEntrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).collect(Collectors.toList()))
|
for (Object2LongMap.Entry<Pair<World,Object>> sectionEntry : ENTITY_TIMES.object2LongEntrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).collect(Collectors.toList()))
|
||||||
{
|
{
|
||||||
if (++total > 10) break;
|
if (++total > 10) break;
|
||||||
Pair<World,Object> section = sectionEntry.getKey();
|
Pair<World,Object> section = sectionEntry.getKey();
|
||||||
|
boolean cli = section.getKey().isClient;
|
||||||
Messenger.m(currentRequester, String.format(
|
Messenger.m(currentRequester, String.format(
|
||||||
"gi - %s: %.2fms",
|
"%s - %s: ", cli?"gi":"w",
|
||||||
sectionName(section),
|
sectionName(section)),
|
||||||
(section.getKey().isClient ? divider : divider_1) * sectionEntry.getLongValue()
|
String.format("%s %.2fms", cli?"di":"y",
|
||||||
|
(cli ? divider : divider_1) * sectionEntry.getLongValue()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__command() ->
|
_command() ->
|
||||||
(
|
(
|
||||||
print('camera: scarpet app.');
|
print('camera: scarpet app.');
|
||||||
print('-------------------');
|
print('-------------------');
|
||||||
@@ -11,7 +11,7 @@ __command() ->
|
|||||||
print(' "/camera place_player - Move player to the selected point');
|
print(' "/camera place_player - Move player to the selected point');
|
||||||
print(' "/camera move" - Move the selected point to players location');
|
print(' "/camera move" - Move the selected point to players location');
|
||||||
print(' "/camera duration <X>" - Set new selected path duration');
|
print(' "/camera duration <X>" - Set new selected path duration');
|
||||||
print(' "/camera split_point>" - Split selected path in half.');
|
print(' "/camera split_point" - Split selected path in half.');
|
||||||
print(' "/camera delete_point" - Remove current key point');
|
print(' "/camera delete_point" - Remove current key point');
|
||||||
print(' "/camera trim_path" - Remove all key points from selected up');
|
print(' "/camera trim_path" - Remove all key points from selected up');
|
||||||
print('');
|
print('');
|
||||||
@@ -57,6 +57,47 @@ __command() ->
|
|||||||
print('');
|
print('');
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
__config() ->{
|
||||||
|
'commands'->{
|
||||||
|
''->'_command',
|
||||||
|
'<command>'->'_call',
|
||||||
|
'add <seconds>'->'add',
|
||||||
|
'prepend <seconds>'->'prepend',
|
||||||
|
'duration <seconds>' -> 'duration',
|
||||||
|
'save_as <name>'->'save_as',
|
||||||
|
'load <name>'->'load',
|
||||||
|
'interpolation <interpolation>'->['__interpolation',true],
|
||||||
|
'interpolation gauss'->['__interpolation','gauss',true],
|
||||||
|
'interpolation gauss <float>'->_(float)->(__interpolation('gauss_'+str(float),true)),
|
||||||
|
'repeat <seconds> <last_delay>'->'repeat',
|
||||||
|
'stretch <factor>'->'stretch'
|
||||||
|
},
|
||||||
|
'arguments'->{
|
||||||
|
'seconds'->{'type'->'int','suggest'->[]},
|
||||||
|
'last_delay'->{'type'->'int','suggest'->[]},
|
||||||
|
'name'->{'type'->'string','suggest'->[]},
|
||||||
|
'interpolation'->{'type'->'term','options'->['linear','catmull_rom']},
|
||||||
|
'factor'->{'type'->'int','min'->25,'max'->400},
|
||||||
|
'command'->{'type'->'term','options'->[
|
||||||
|
'start',
|
||||||
|
'clear',
|
||||||
|
'select',
|
||||||
|
'place_player',
|
||||||
|
'move',
|
||||||
|
'split_point',
|
||||||
|
'delete_point',
|
||||||
|
'trim_path',
|
||||||
|
'transpose',
|
||||||
|
'play',
|
||||||
|
'show',
|
||||||
|
'hide',
|
||||||
|
'prefer_smooth_play',
|
||||||
|
'prefer_synced_play'
|
||||||
|
]}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_call(command)->call(command);
|
||||||
|
|
||||||
global_points = null;
|
global_points = null;
|
||||||
global_dimension = null;
|
global_dimension = null;
|
||||||
@@ -150,7 +191,6 @@ __assert_point_selected(validator) ->
|
|||||||
);
|
);
|
||||||
|
|
||||||
//select a custom interpolation method
|
//select a custom interpolation method
|
||||||
interpolation(method) -> __interpolation(method, true);
|
|
||||||
__interpolation(method, verbose) ->
|
__interpolation(method, verbose) ->
|
||||||
(
|
(
|
||||||
__prepare_path_if_needed() -> __prepare_path_if_needed_generic();
|
__prepare_path_if_needed() -> __prepare_path_if_needed_generic();
|
||||||
@@ -159,7 +199,7 @@ __interpolation(method, verbose) ->
|
|||||||
// or optionally __prepare_path_if_needed, if path is inefficient to compute point by point
|
// or optionally __prepare_path_if_needed, if path is inefficient to compute point by point
|
||||||
global_interpolator = if (
|
global_interpolator = if (
|
||||||
method == 'linear', '__interpolator_linear',
|
method == 'linear', '__interpolator_linear',
|
||||||
method == 'cr', '__interpolator_cr',
|
method == 'catmull_rom', '__interpolator_cr',
|
||||||
method == 'gauss', _(s, p) -> __interpolator_gauB(s, p, 0),
|
method == 'gauss', _(s, p) -> __interpolator_gauB(s, p, 0),
|
||||||
method ~ '^gauss_',
|
method ~ '^gauss_',
|
||||||
(
|
(
|
||||||
@@ -167,13 +207,12 @@ __interpolation(method, verbose) ->
|
|||||||
type = replace(type,'_','.');
|
type = replace(type,'_','.');
|
||||||
variance = round(60*number(type));
|
variance = round(60*number(type));
|
||||||
_(s, p, outer(variance)) -> __interpolator_gauB(s, p, variance);
|
_(s, p, outer(variance)) -> __interpolator_gauB(s, p, variance);
|
||||||
),
|
)
|
||||||
exit('Choose one of the following methods: linear, gauss, gauss_<deviation>, cr')
|
|
||||||
);
|
);
|
||||||
__update();
|
__update();
|
||||||
if(verbose, 'Interpolation changed to '+method, '');
|
if(verbose, 'Interpolation changed to '+method, '');
|
||||||
);
|
);
|
||||||
__interpolation('cr', false);
|
__interpolation('catmull_rom', false);
|
||||||
|
|
||||||
// adds a point to the end of the path with delay in seconds
|
// adds a point to the end of the path with delay in seconds
|
||||||
add(delay) ->
|
add(delay) ->
|
||||||
@@ -216,9 +255,6 @@ repeat(times, last_section_delay) ->
|
|||||||
stretch(percentage) ->
|
stretch(percentage) ->
|
||||||
(
|
(
|
||||||
if (err = __is_not_valid_for_motion(), exit(err));
|
if (err = __is_not_valid_for_motion(), exit(err));
|
||||||
if (percentage < 25 || percentage > 400,
|
|
||||||
exit('path speed can only be speed, or slowed down 4 times. Re-call command for larger changes')
|
|
||||||
);
|
|
||||||
ratio = percentage/100;
|
ratio = percentage/100;
|
||||||
previous_path_length = global_points:(-1):1;
|
previous_path_length = global_points:(-1):1;
|
||||||
for(global_points, _:1 = _:1*ratio );
|
for(global_points, _:1 = _:1*ratio );
|
||||||
@@ -660,4 +696,4 @@ __on_close() ->
|
|||||||
(
|
(
|
||||||
global_playing_path = false;
|
global_playing_path = false;
|
||||||
global_showing_path = false;
|
global_showing_path = false;
|
||||||
);
|
);
|
||||||
@@ -1,21 +1,23 @@
|
|||||||
import('math','_euclidean_sq','_vec_length');
|
import('shapes','draw_sphere', 'draw_diamond', 'draw_filled_diamond', 'draw_pyramid', 'draw_prism'); //importing all the shape funcs from the other app
|
||||||
|
|
||||||
__config() -> {
|
__config() -> {
|
||||||
'commands'->{
|
'commands'->{
|
||||||
'sphere <center> <radius> <block>'->['draw_sphere',null,true],
|
'sphere <center> <radius> <block>'->_(c,r,b)->draw('draw_sphere',[c,r, true],b,null),
|
||||||
'sphere <center> <radius> <block> replace <replacement>'->['draw_sphere',true],
|
'sphere <center> <radius> <block> replace <replacement>'->_(c,r,b,rp)->draw('draw_sphere',[c,r,true],b,rp),
|
||||||
'ball <center> <radius> <block>'->['draw_sphere',null,false],
|
'ball <center> <radius> <block>'->_(c,r,b)->draw('draw_sphere',[c,r,false],b,null),
|
||||||
'ball <center> <radius> <block> replace <replacement>'->['draw_sphere',false],
|
'ball <center> <radius> <block> replace <replacement>'->_(c,r,b,rp)->draw('draw_sphere',[c,r,false],b,rp),
|
||||||
'diamond <center> <radius> <block>'->['draw_diamond',null],
|
'diamond <center> <radius> <block> hollow' -> _(c,r,b)->draw('draw_diamond',[c,r],b,null),
|
||||||
'diamond <center> <radius> <block> replace <replacement>'->'draw_diamond',
|
'diamond <center> <radius> <block> hollow replace <replacement>'->_(c,r,b,rp)->draw('draw_diamond',[c, r], b, rp),
|
||||||
'pyramid <center> <radius> <height> <pointing> <orientation> <block> <hollow>'->['draw_pyramid',null, true],
|
'diamond <center> <radius> <block>' -> _(c,r,b)->draw('draw_filled_diamond',[c,r],b,null),
|
||||||
'pyramid <center> <radius> <height> <pointing> <orientation> <block> <hollow> replace <replacement>'->['draw_pyramid', true],
|
'diamond <center> <radius> <block> replace <replacement>'->_(c,r,b,rp)->draw('draw_filled_diamond',[c, r], b, rp),
|
||||||
'cone <center> <radius> <height> <pointing> <orientation> <block> <hollow>'->['draw_pyramid',null, false],
|
'pyramid <center> <radius> <height> <pointing> <orientation> <block> <hollow>'->_(c,r,h,p,o,b,ho)->draw('draw_pyramid',[c,r,h,p,o,ho, true],b,null),
|
||||||
'cone <center> <radius> <height> <pointing> <orientation> <block> <hollow> replace <replacement>'->['draw_pyramid', false],
|
'pyramid <center> <radius> <height> <pointing> <orientation> <block> <hollow> replace <replacement>'->_(c,r,h,p,o,b,ho,rp)->draw('draw_pyramid', [c,r,h,p,o,ho,true],b,rp),
|
||||||
'cuboid <center> <radius> <height> <orientation> <block> <hollow>'->['draw_prism', null, true],
|
'cone <center> <radius> <height> <pointing> <orientation> <block> <hollow>'->_(c,r,h,p,o,b,ho)->draw('draw_pyramid',[c,r,h,p,o,ho, false],b,null),
|
||||||
'cuboid <center> <radius> <height> <orientation> <block> <hollow> replace <replacement>'->['draw_prism', true],
|
'cone <center> <radius> <height> <pointing> <orientation> <block> <hollow> replace <replacement>'->_(c,r,h,p,o,b,ho,rp)->draw('draw_pyramid', [c,r,h,p,o,ho,false],b,rp),
|
||||||
'cylinder <center> <radius> <height> <orientation> <block> <hollow>'->['draw_prism', null, false],
|
'cuboid <center> <radius> <height> <orientation> <block> <hollow>'->_(c,r,h,o,b,ho)->draw('draw_prism',[c,r,h,p,o,ho, true],b,null),
|
||||||
'cylinder <center> <radius> <height> <orientation> <block> <hollow> replace <replacement>'->['draw_prism', false]
|
'cuboid <center> <radius> <height> <orientation> <block> <hollow> replace <replacement>'->_(c,r,h,o,b,ho,rp)->draw('draw_prism', [c,r,h,o,ho,true],b,rp),
|
||||||
|
'cylinder <center> <radius> <height> <orientation> <block> <hollow>'->_(c,r,h,o,b,ho)->draw('draw_prism',[c,r,h,p,o,ho, false],b,null),
|
||||||
|
'cylinder <center> <radius> <height> <orientation> <block> <hollow> replace <replacement>'->_(c,r,h,o,b,ho,rp)->draw('draw_prism', [c,r,h,o,ho,false],b,rp),
|
||||||
},
|
},
|
||||||
'arguments'->{
|
'arguments'->{
|
||||||
'center'->{'type'->'pos', 'loaded'->'true'},
|
'center'->{'type'->'pos', 'loaded'->'true'},
|
||||||
@@ -26,12 +28,9 @@ __config() -> {
|
|||||||
'pointing'->{'type'->'term','suggest'->['up','down']},
|
'pointing'->{'type'->'term','suggest'->['up','down']},
|
||||||
'hollow'->{'type'->'term','suggest'->['hollow','solid']},
|
'hollow'->{'type'->'term','suggest'->['hollow','solid']},
|
||||||
},
|
},
|
||||||
'scope'->'global'
|
'scope'->'global'//todo decide whether it rly needs to be global or not...
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//"Boilerplate" code
|
|
||||||
|
|
||||||
_block_matches(existing, block_predicate) ->
|
_block_matches(existing, block_predicate) ->
|
||||||
(
|
(
|
||||||
[name, block_tag, properties, nbt] = block_predicate;
|
[name, block_tag, properties, nbt] = block_predicate;
|
||||||
@@ -42,112 +41,16 @@ _block_matches(existing, block_predicate) ->
|
|||||||
(!tag || tag_matches(block_data(existing), tag))
|
(!tag || tag_matches(block_data(existing), tag))
|
||||||
);
|
);
|
||||||
|
|
||||||
set_block(x, y, z, block, replacement)-> (
|
draw(what, args, block, replacement)->(//custom setter cos it's easier
|
||||||
existing = block(x, y, z);
|
positions = call(what,args); //returning blocks to be set
|
||||||
if(block != existing && (replacement && _block_matches(existing, replacement) ),
|
|
||||||
without_updates( if (set(existing,block),
|
affected = 0
|
||||||
global_affected += [x, y, z];
|
|
||||||
));
|
for(positions,
|
||||||
|
existing = block(_);
|
||||||
|
if(block != existing && (!replacement || _block_matches(existing, replacement)),
|
||||||
|
affected += bool(set(existing,block))
|
||||||
|
)
|
||||||
);
|
);
|
||||||
);
|
|
||||||
|
|
||||||
global_affected = [];
|
|
||||||
|
|
||||||
affected(player) -> (
|
|
||||||
affected = length(global_affected);
|
|
||||||
print(player,format('gi Filled ' + affected + ' blocks'));
|
print(player,format('gi Filled ' + affected + ' blocks'));
|
||||||
|
|
||||||
if(system_info('world_carpet_rules'):'fillUpdates',
|
|
||||||
for(global_affected,update(_));//updating if fillUpdates is true
|
|
||||||
);
|
|
||||||
|
|
||||||
global_affected = [];
|
|
||||||
affected
|
|
||||||
);
|
|
||||||
|
|
||||||
length_sq(vec) -> reduce(vec, _a + _*_, 0);
|
|
||||||
|
|
||||||
fill_flat(pos, offset, dr, rectangle, orientation, block, hollow, replacement)->(
|
|
||||||
r = floor(dr);
|
|
||||||
drsq = dr^2;
|
|
||||||
if(orientation=='x',
|
|
||||||
scan(pos,0,-r,-r,
|
|
||||||
if((!hollow && (rectangle || _y^2 + _z^2 <= drsq))||//if not hollow, vry simple
|
|
||||||
(hollow && ((rectangle && (abs(_y) == r || abs(_z) ==r)) || //If hollow and it's a rectangle
|
|
||||||
(!rectangle && (_y^2 + _z^2 <= drsq && (abs(_y)+1)^ 2 + (abs(_z)+1)^2 >= drsq)))),//If hollow and not rectangle
|
|
||||||
set_block(_x+offset,_y,_z,block, replacement)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
orientation == 'y',
|
|
||||||
scan(pos,-r,0,-r,
|
|
||||||
if((!hollow && (rectangle || _x^2 + _z^2 <= drsq))||//if not hollow, vry simple
|
|
||||||
(hollow && ((rectangle && (abs(_x) == r || abs(_z) ==r)) || //If hollow and it's a rectangle
|
|
||||||
(!rectangle && (_x^2 + _z^2 <= drsq && (abs(_x)+1)^ 2 + (abs(_z)+1)^2 >= drsq)))),//If hollow and not rectangle
|
|
||||||
set_block(_x,_y+offset,_z,block, replacement)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
orientation == 'z',
|
|
||||||
scan(pos,-r,-r,0,
|
|
||||||
if((!hollow && (rectangle || _y^2 + _x^2 <= drsq))||//if not hollow, vry simple
|
|
||||||
(hollow && ((rectangle && (abs(_y) == r || abs(_x) ==r)) || //If hollow and it's a rectangle
|
|
||||||
(!rectangle && (_y^2 + _x^2 <= drsq && (abs(_y)+1)^ 2 + (abs(_x)+1)^2 >= drsq)))),//If hollow and not rectangle
|
|
||||||
set_block(_x,_y,_z+offset,block, replacement)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
print(player(),format('r Error while running command: orientation can only be "x", "y" or "z", '+orientation+' is invalid.'));
|
|
||||||
global_affected = [];
|
|
||||||
);
|
|
||||||
);
|
|
||||||
|
|
||||||
//drawing commands
|
|
||||||
|
|
||||||
draw_sphere(centre, radius, block, replacement, hollow)->(
|
|
||||||
scan(centre,[radius,radius,radius],
|
|
||||||
l = length_sq([_x,_y,_z]-centre);
|
|
||||||
if((l<=radius^2+radius) && (!hollow || l>=radius^2-radius),
|
|
||||||
set_block(_x, _y, _z, block, replacement)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
affected(player());
|
|
||||||
);
|
|
||||||
|
|
||||||
draw_diamond(pos, radius, block, replacement)->(
|
|
||||||
|
|
||||||
c_for(r=0, r<radius, r+=1,
|
|
||||||
y = r-radius+1;
|
|
||||||
c_for(x=-r,x<=r,x+=1,
|
|
||||||
z=r-abs(x);
|
|
||||||
|
|
||||||
set_block(pos:0+x,pos:1-y,pos:2+z, block, replacement);
|
|
||||||
set_block(pos:0+x,pos:1-y,pos:2-z, block, replacement);
|
|
||||||
set_block(pos:0+x,pos:1+y,pos:2+z, block, replacement);
|
|
||||||
set_block(pos:0+x,pos:1+y,pos:2-z, block, replacement);
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
affected(player())
|
|
||||||
);
|
|
||||||
|
|
||||||
draw_pyramid(pos, radius, height, pointing, orientation, block, fill_type, replacement, is_square)->(
|
|
||||||
|
|
||||||
hollow = fill_type=='hollow';
|
|
||||||
pointup = pointing=='up';
|
|
||||||
|
|
||||||
for(range(height),
|
|
||||||
r = if(pointup, radius - radius * _ / height -1, radius * _ / height);
|
|
||||||
fill_flat(pos, _, r, is_square, orientation, block, if((pointup&&_==0)||(!pointup && _==height-1),false,hollow),replacement)//Always close bottom off
|
|
||||||
);
|
|
||||||
affected(player())
|
|
||||||
);
|
|
||||||
|
|
||||||
draw_prism(pos, rad, height, orientation, block, fill_type, replacement, is_square)->(
|
|
||||||
|
|
||||||
hollow = fill_type =='hollow';
|
|
||||||
radius = rad+0.5;
|
|
||||||
|
|
||||||
for(range(height),
|
|
||||||
fill_flat(pos, _, radius, is_square, orientation, block, if(_==0 || _==height-1,false,hollow), replacement)//Always close ends off
|
|
||||||
);
|
|
||||||
affected(player())
|
|
||||||
);
|
);
|
||||||
@@ -328,3 +328,32 @@ __on_close() ->
|
|||||||
print('App is closing for '+player());
|
print('App is closing for '+player());
|
||||||
logger('App is closing for '+player());
|
logger('App is closing for '+player());
|
||||||
);
|
);
|
||||||
|
|
||||||
|
__on_explosion(pos, power, source, causer, mode, fire) ->
|
||||||
|
(
|
||||||
|
print('');
|
||||||
|
print('__on_explosion(pos, power, source, causer, mode, fire)');
|
||||||
|
print('explosion:');
|
||||||
|
print(' - pos: '+pos);
|
||||||
|
print(' - power: '+power);
|
||||||
|
print(' - source: '+source);
|
||||||
|
print(' - causer: '+causer);
|
||||||
|
print(' - mode: '+mode);
|
||||||
|
print(' - fire: '+fire);
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
__on_explosion_outcome(pos, power, source, causer, mode, fire, blocks, entities) ->
|
||||||
|
(
|
||||||
|
print('');
|
||||||
|
print('__on_explosion_outcome(pos, power, source, causer, mode, fire, blocks, entities)');
|
||||||
|
print('explosion outcome:');
|
||||||
|
print(' - pos: '+pos);
|
||||||
|
print(' - power: '+power);
|
||||||
|
print(' - source: '+source);
|
||||||
|
print(' - causer: '+causer);
|
||||||
|
print(' - mode: '+mode);
|
||||||
|
print(' - fire: '+fire);
|
||||||
|
print(' - number or affected blocks: '+length(blocks));
|
||||||
|
print(' - number of affected entities: '+length(entities));
|
||||||
|
);
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
//This is a library app, mostly for the draw command, which allows to import these shape-drawing functions into your own app.
|
||||||
|
//Arguments for each are to be given as a singleton list, for ease of use. This is with the exception of the fill_flat functions, with
|
||||||
|
//the assumption that they're unlikely to be used outside of internal calls, but it is still available to be called if necessary.
|
||||||
|
//Make sure you read the arguments and input them in the correct order, or it will mess up.
|
||||||
|
//these functions return a set of all the positions in the shape, given the parameters, without repeating a position.
|
||||||
|
//This way, you can efficiently iterate over the positions once having generated them. See draw_beta.sc for an example. todo change to draw once renamed
|
||||||
|
|
||||||
|
|
||||||
|
draw_sphere(args)->(
|
||||||
|
[centre, radius, hollow] = args;
|
||||||
|
positions = {};
|
||||||
|
[cx,cy,cz]=centre;
|
||||||
|
for(range(-90, 90, 45/radius),
|
||||||
|
cpitch = cos(_);
|
||||||
|
spitch = sin(_);
|
||||||
|
for(range(0, 180, 45/radius),
|
||||||
|
cyaw = cos(_)*cpitch*radius;
|
||||||
|
syaw = sin(_)*cpitch*radius;
|
||||||
|
if(hollow,
|
||||||
|
positions += [cx+cyaw,cy+spitch*radius,cz+syaw];
|
||||||
|
positions += [cx+cos(_+180)*cpitch*radius,cy+spitch*radius,cz+sin(_+180)*cpitch*radius],
|
||||||
|
for(range(-syaw,syaw+1),
|
||||||
|
positions += [cx+cyaw*cpitch,cy+spitch*radius,cz+_]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
positions
|
||||||
|
);
|
||||||
|
|
||||||
|
draw_diamond(args)->(
|
||||||
|
[pos, radius] = args;
|
||||||
|
positions = {};
|
||||||
|
c_for(r=0, r<radius, r+=1,
|
||||||
|
y = r-radius+1;
|
||||||
|
c_for(x=-r,x<=r,x+=1,
|
||||||
|
z=r-abs(x);
|
||||||
|
positions += [pos:0+x, pos:1 +y, pos:2+z];
|
||||||
|
positions += [pos:0+x, pos:1 +y, pos:2-z];
|
||||||
|
positions += [pos:0+x, pos:1 -y, pos:2+z];
|
||||||
|
positions += [pos:0+x, pos:1 -y, pos:2-z];
|
||||||
|
)
|
||||||
|
);
|
||||||
|
positions
|
||||||
|
);
|
||||||
|
|
||||||
|
draw_filled_diamond(args)->(
|
||||||
|
[pos, radius] = args;
|
||||||
|
positions = {};
|
||||||
|
for(diamond(pos,radius,radius),
|
||||||
|
positions += pos(_)
|
||||||
|
);
|
||||||
|
positions
|
||||||
|
);
|
||||||
|
|
||||||
|
draw_pyramid(args)->(
|
||||||
|
[pos, radius, height, pointing, orientation, fill_type, is_square] = args;//todo change to hollow boolean arg
|
||||||
|
positions = {};
|
||||||
|
hollow = fill_type=='hollow';
|
||||||
|
pointup = pointing=='up';
|
||||||
|
for(range(height),
|
||||||
|
r = if(pointup, radius * ( 1- _ / height) -1, radius * _ / height);
|
||||||
|
positions += fill_flat(pos, _, r, is_square, orientation, !((pointup&&_==0)||(!pointup && _==height-1)) && hollow)//Always close bottom off
|
||||||
|
);
|
||||||
|
positions
|
||||||
|
);
|
||||||
|
|
||||||
|
draw_prism(args)->(
|
||||||
|
[pos, rad, height, orientation, fill_type, is_square]=args;//todo change to hollow boolean arg
|
||||||
|
positions = {};
|
||||||
|
hollow = fill_type =='hollow';
|
||||||
|
radius = rad+0.5;
|
||||||
|
filled_circle_points = fill_flat(pos, 0, radius, is_square, orientation, false); //I dont actually need to generate all the points lol
|
||||||
|
offset_vector = if(orientation=='x', [1,0,0], orientation=='y', [0,1,0],[0,0,1]);
|
||||||
|
if(hollow,
|
||||||
|
hollow_circle_points = fill_flat(pos, 0, radius, is_square, orientation, true);
|
||||||
|
|
||||||
|
for(filled_circle_points,//Always close ends off
|
||||||
|
positions+=_;
|
||||||
|
positions+=_+offset_vector*(height-1)
|
||||||
|
);
|
||||||
|
|
||||||
|
for(range(1, height-1),//cos Im adding ends as filled anyways
|
||||||
|
offset=_;
|
||||||
|
for(hollow_circle_points,
|
||||||
|
positions+= _ + offset_vector * offset
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
for(range(height),
|
||||||
|
offset = _;
|
||||||
|
for(filled_circle_points,
|
||||||
|
positions += _ + offset_vector * offset
|
||||||
|
)
|
||||||
|
);
|
||||||
|
)
|
||||||
|
positions
|
||||||
|
);
|
||||||
|
|
||||||
|
fill_flat(pos, offset, dr, rectangle, orientation, hollow)->
|
||||||
|
if(rectangle,
|
||||||
|
fill_flat_rectangle(pos, offset, dr, orientation, hollow),
|
||||||
|
fill_flat_circle(pos, offset, dr, orientation, hollow)
|
||||||
|
);
|
||||||
|
|
||||||
|
fill_flat_circle(pos, offset, dr, orientation, hollow) ->( //Bresenham circle algorithm to be super efficient
|
||||||
|
r = floor(dr);
|
||||||
|
a=0;
|
||||||
|
b=r;
|
||||||
|
d=3-(2*r);
|
||||||
|
|
||||||
|
positions = {};
|
||||||
|
|
||||||
|
positions += fill_flat_circle_points(pos, orientation, offset, hollow, a, b);
|
||||||
|
|
||||||
|
while(a<=b, 10^10,
|
||||||
|
if(d<=0,
|
||||||
|
d += (4*a)+6,
|
||||||
|
d += (4*a)-(4*b)+10;
|
||||||
|
b=b-1
|
||||||
|
);
|
||||||
|
a+=1;
|
||||||
|
positions += fill_flat_circle_points(pos, orientation, offset, hollow, a, b)
|
||||||
|
);
|
||||||
|
positions
|
||||||
|
);
|
||||||
|
|
||||||
|
fill_flat_circle_points(pos, orientation, offset, hollow, a, b) ->(
|
||||||
|
[x, y, z] = pos;
|
||||||
|
positions = {};
|
||||||
|
if(orientation == 'x',
|
||||||
|
if(hollow,
|
||||||
|
positions += [x + offset, a+y, b+z];
|
||||||
|
positions += [x + offset, a+y,-b+z];
|
||||||
|
positions += [x + offset,-a+y,-b+z];
|
||||||
|
positions += [x + offset,-a+y, b+z];
|
||||||
|
positions += [x + offset, b+y, a+z];
|
||||||
|
positions += [x + offset, b+y,-a+z];
|
||||||
|
positions += [x + offset,-b+y,-a+z];
|
||||||
|
positions += [x + offset,-b+y, a+z],
|
||||||
|
c_for(c = -a, c<=a, c+=1,
|
||||||
|
positions += [x + offset,c+y, b + z];
|
||||||
|
positions += [x + offset,c+y,-b + z]
|
||||||
|
);
|
||||||
|
c_for(c = -b, c<=b, c+=1,
|
||||||
|
positions += [x + offset, a+y,c + z];
|
||||||
|
positions += [x + offset,-a+y,c + z]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
orientation=='y',
|
||||||
|
if(hollow,
|
||||||
|
positions += [ a+x, y + offset, b+z];
|
||||||
|
positions += [ a+x, y + offset,-b+z];
|
||||||
|
positions += [-a+x, y + offset,-b+z];
|
||||||
|
positions += [-a+x, y + offset, b+z];
|
||||||
|
positions += [ b+x, y + offset, a+z];
|
||||||
|
positions += [ b+x, y + offset,-a+z];
|
||||||
|
positions += [-b+x, y + offset,-a+z];
|
||||||
|
positions += [-b+x, y + offset, a+z],
|
||||||
|
c_for(c = -a, c<=a, c+=1,
|
||||||
|
positions += [c+x,y + offset, b + z];
|
||||||
|
positions += [c+x,y + offset,-b + z]
|
||||||
|
);
|
||||||
|
c_for(c = -b, c<=b, c+=1,
|
||||||
|
positions += [ a+x,y + offset,c + z];
|
||||||
|
positions += [-a+x,y + offset,c + z]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
orientation=='z',
|
||||||
|
if(hollow,
|
||||||
|
positions += [ a+x, b+y, z + offset];
|
||||||
|
positions += [ a+x,-b+y, z + offset];
|
||||||
|
positions += [-a+x,-b+y, z + offset];
|
||||||
|
positions += [-a+x, b+y, z + offset];
|
||||||
|
positions += [ b+x, a+y, z + offset];
|
||||||
|
positions += [ b+x,-a+y, z + offset];
|
||||||
|
positions += [-b+x,-a+y, z + offset];
|
||||||
|
positions += [-b+x, a+y, z + offset],
|
||||||
|
c_for(c = -a, c<=a, c+=1,
|
||||||
|
positions += [c+x, b + y,z + offset];
|
||||||
|
positions += [c+x,-b + y,z + offset]
|
||||||
|
);
|
||||||
|
c_for(c = -b, c<=b, c+=1,
|
||||||
|
positions += [ a+x,c + y,z + offset];
|
||||||
|
positions += [-a+x,c + y,z + offset]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
fill_flat_rectangle(pos, offset, dr, orientation, hollow) -> (
|
||||||
|
r = floor(dr);
|
||||||
|
drsq = dr^2;
|
||||||
|
positions = {};
|
||||||
|
if(orientation == 'x',
|
||||||
|
c_for(a = -r, a <=r, a +=1,
|
||||||
|
if(hollow,
|
||||||
|
positions += [r + offset, a, r];
|
||||||
|
positions += [r + offset, a,-r];
|
||||||
|
positions += [r + offset, r, a];
|
||||||
|
positions += [r + offset,-r, a],
|
||||||
|
c_for(b = -r, b <=r, b +=1,
|
||||||
|
positions += [r + offset, a, b]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
orientation == 'y',
|
||||||
|
c_for(a = -r, a <=r, a +=1,
|
||||||
|
if(hollow,
|
||||||
|
positions += [ r, r + offset,a];
|
||||||
|
positions += [-r, r + offset,a];
|
||||||
|
positions += [a, r + offset, r];
|
||||||
|
positions += [a, r + offset,-r],
|
||||||
|
c_for(b = -r, b <=r, b +=1,
|
||||||
|
positions += [a, r + offset, b]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
orientation == 'z',
|
||||||
|
c_for(a = -r, a <=r, a +=1,
|
||||||
|
if(hollow,
|
||||||
|
positions += [ r, a,r + offset];
|
||||||
|
positions += [-r, a,r + offset];
|
||||||
|
positions += [a, r, r + offset];
|
||||||
|
positions += [a,-r, r + offset],
|
||||||
|
c_for(b = -r, b <=r, b +=1,
|
||||||
|
positions += [b, a, r + offset]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
positions
|
||||||
|
);
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
"ServerPlayNetworkHandler_interactionUpdatesMixin",
|
"ServerPlayNetworkHandler_interactionUpdatesMixin",
|
||||||
|
|
||||||
"ServerMetadata_motdMixin",
|
"ServerMetadata_motdMixin",
|
||||||
|
"MinecraftServer_pingPlayerSampleLimit",
|
||||||
|
|
||||||
"MinecraftServer_coreMixin",
|
"MinecraftServer_coreMixin",
|
||||||
|
|
||||||
@@ -128,6 +129,7 @@
|
|||||||
"TntBlockMixin",
|
"TntBlockMixin",
|
||||||
"ExplosionMixin",
|
"ExplosionMixin",
|
||||||
"ExplosionAccessor",
|
"ExplosionAccessor",
|
||||||
|
"Explosion_scarpetEventMixin",
|
||||||
|
|
||||||
"TntEntityMixin",
|
"TntEntityMixin",
|
||||||
"ProjectileEntityMixin",
|
"ProjectileEntityMixin",
|
||||||
@@ -175,6 +177,7 @@
|
|||||||
"PointOfInterest_scarpetMixin",
|
"PointOfInterest_scarpetMixin",
|
||||||
"ServerWorld_scarpetMixin",
|
"ServerWorld_scarpetMixin",
|
||||||
"HungerManagerMixin_scarpetEventsMixin",
|
"HungerManagerMixin_scarpetEventsMixin",
|
||||||
|
"ReloadCommand_reloadAppsMixin",
|
||||||
|
|
||||||
"RecipeManager_scarpetMixin",
|
"RecipeManager_scarpetMixin",
|
||||||
"Ingredient_scarpetMixin",
|
"Ingredient_scarpetMixin",
|
||||||
@@ -192,6 +195,7 @@
|
|||||||
"PlayerEntity_creativeNoClipMixin",
|
"PlayerEntity_creativeNoClipMixin",
|
||||||
"PistonBlockEntity_playerHandlingMixin",
|
"PistonBlockEntity_playerHandlingMixin",
|
||||||
"BlockItem_creativeNoClipMixin",
|
"BlockItem_creativeNoClipMixin",
|
||||||
|
"WallStandingBlockItem_creativeNoClipMixin",
|
||||||
"LivingEntity_creativeFlyMixin"
|
"LivingEntity_creativeFlyMixin"
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in new issue
Block a user