Merge branch 'master' into light
# Conflicts: # src/main/resources/carpet.accesswidener # src/main/resources/fabric.mod.json
This commit is contained in:
commit
7067e59a21
66 files changed
+1898
-495
No files matched your search
@@ -1,165 +1,21 @@
|
|||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
MIT License
|
||||||
Version 3, 29 June 2007
|
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
Copyright (c) 2020 gnembon
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
This version of the GNU Lesser General Public License incorporates
|
The above copyright notice and this permission notice shall be included in all
|
||||||
the terms and conditions of version 3 of the GNU General Public
|
copies or substantial portions of the Software.
|
||||||
License, supplemented by the additional permissions listed below.
|
|
||||||
|
|
||||||
0. Additional Definitions.
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
General Public License.
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
"The Library" refers to a covered work governed by this License,
|
SOFTWARE.
|
||||||
other than an Application or a Combined Work as defined below.
|
|
||||||
|
|
||||||
An "Application" is any work that makes use of an interface provided
|
|
||||||
by the Library, but which is not otherwise based on the Library.
|
|
||||||
Defining a subclass of a class defined by the Library is deemed a mode
|
|
||||||
of using an interface provided by the Library.
|
|
||||||
|
|
||||||
A "Combined Work" is a work produced by combining or linking an
|
|
||||||
Application with the Library. The particular version of the Library
|
|
||||||
with which the Combined Work was made is also called the "Linked
|
|
||||||
Version".
|
|
||||||
|
|
||||||
The "Minimal Corresponding Source" for a Combined Work means the
|
|
||||||
Corresponding Source for the Combined Work, excluding any source code
|
|
||||||
for portions of the Combined Work that, considered in isolation, are
|
|
||||||
based on the Application, and not on the Linked Version.
|
|
||||||
|
|
||||||
The "Corresponding Application Code" for a Combined Work means the
|
|
||||||
object code and/or source code for the Application, including any data
|
|
||||||
and utility programs needed for reproducing the Combined Work from the
|
|
||||||
Application, but excluding the System Libraries of the Combined Work.
|
|
||||||
|
|
||||||
1. Exception to Section 3 of the GNU GPL.
|
|
||||||
|
|
||||||
You may convey a covered work under sections 3 and 4 of this License
|
|
||||||
without being bound by section 3 of the GNU GPL.
|
|
||||||
|
|
||||||
2. Conveying Modified Versions.
|
|
||||||
|
|
||||||
If you modify a copy of the Library, and, in your modifications, a
|
|
||||||
facility refers to a function or data to be supplied by an Application
|
|
||||||
that uses the facility (other than as an argument passed when the
|
|
||||||
facility is invoked), then you may convey a copy of the modified
|
|
||||||
version:
|
|
||||||
|
|
||||||
a) under this License, provided that you make a good faith effort to
|
|
||||||
ensure that, in the event an Application does not supply the
|
|
||||||
function or data, the facility still operates, and performs
|
|
||||||
whatever part of its purpose remains meaningful, or
|
|
||||||
|
|
||||||
b) under the GNU GPL, with none of the additional permissions of
|
|
||||||
this License applicable to that copy.
|
|
||||||
|
|
||||||
3. Object Code Incorporating Material from Library Header Files.
|
|
||||||
|
|
||||||
The object code form of an Application may incorporate material from
|
|
||||||
a header file that is part of the Library. You may convey such object
|
|
||||||
code under terms of your choice, provided that, if the incorporated
|
|
||||||
material is not limited to numerical parameters, data structure
|
|
||||||
layouts and accessors, or small macros, inline functions and templates
|
|
||||||
(ten or fewer lines in length), you do both of the following:
|
|
||||||
|
|
||||||
a) Give prominent notice with each copy of the object code that the
|
|
||||||
Library is used in it and that the Library and its use are
|
|
||||||
covered by this License.
|
|
||||||
|
|
||||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
|
||||||
document.
|
|
||||||
|
|
||||||
4. Combined Works.
|
|
||||||
|
|
||||||
You may convey a Combined Work under terms of your choice that,
|
|
||||||
taken together, effectively do not restrict modification of the
|
|
||||||
portions of the Library contained in the Combined Work and reverse
|
|
||||||
engineering for debugging such modifications, if you also do each of
|
|
||||||
the following:
|
|
||||||
|
|
||||||
a) Give prominent notice with each copy of the Combined Work that
|
|
||||||
the Library is used in it and that the Library and its use are
|
|
||||||
covered by this License.
|
|
||||||
|
|
||||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
|
||||||
document.
|
|
||||||
|
|
||||||
c) For a Combined Work that displays copyright notices during
|
|
||||||
execution, include the copyright notice for the Library among
|
|
||||||
these notices, as well as a reference directing the user to the
|
|
||||||
copies of the GNU GPL and this license document.
|
|
||||||
|
|
||||||
d) Do one of the following:
|
|
||||||
|
|
||||||
0) Convey the Minimal Corresponding Source under the terms of this
|
|
||||||
License, and the Corresponding Application Code in a form
|
|
||||||
suitable for, and under terms that permit, the user to
|
|
||||||
recombine or relink the Application with a modified version of
|
|
||||||
the Linked Version to produce a modified Combined Work, in the
|
|
||||||
manner specified by section 6 of the GNU GPL for conveying
|
|
||||||
Corresponding Source.
|
|
||||||
|
|
||||||
1) Use a suitable shared library mechanism for linking with the
|
|
||||||
Library. A suitable mechanism is one that (a) uses at run time
|
|
||||||
a copy of the Library already present on the user's computer
|
|
||||||
system, and (b) will operate properly with a modified version
|
|
||||||
of the Library that is interface-compatible with the Linked
|
|
||||||
Version.
|
|
||||||
|
|
||||||
e) Provide Installation Information, but only if you would otherwise
|
|
||||||
be required to provide such information under section 6 of the
|
|
||||||
GNU GPL, and only to the extent that such information is
|
|
||||||
necessary to install and execute a modified version of the
|
|
||||||
Combined Work produced by recombining or relinking the
|
|
||||||
Application with a modified version of the Linked Version. (If
|
|
||||||
you use option 4d0, the Installation Information must accompany
|
|
||||||
the Minimal Corresponding Source and Corresponding Application
|
|
||||||
Code. If you use option 4d1, you must provide the Installation
|
|
||||||
Information in the manner specified by section 6 of the GNU GPL
|
|
||||||
for conveying Corresponding Source.)
|
|
||||||
|
|
||||||
5. Combined Libraries.
|
|
||||||
|
|
||||||
You may place library facilities that are a work based on the
|
|
||||||
Library side by side in a single library together with other library
|
|
||||||
facilities that are not Applications and are not covered by this
|
|
||||||
License, and convey such a combined library under terms of your
|
|
||||||
choice, if you do both of the following:
|
|
||||||
|
|
||||||
a) Accompany the combined library with a copy of the same work based
|
|
||||||
on the Library, uncombined with any other library facilities,
|
|
||||||
conveyed under the terms of this License.
|
|
||||||
|
|
||||||
b) Give prominent notice with the combined library that part of it
|
|
||||||
is a work based on the Library, and explaining where to find the
|
|
||||||
accompanying uncombined form of the same work.
|
|
||||||
|
|
||||||
6. Revised Versions of the GNU Lesser General Public License.
|
|
||||||
|
|
||||||
The Free Software Foundation may publish revised and/or new versions
|
|
||||||
of the GNU Lesser General Public License from time to time. Such new
|
|
||||||
versions will be similar in spirit to the present version, but may
|
|
||||||
differ in detail to address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the
|
|
||||||
Library as you received it specifies that a certain numbered version
|
|
||||||
of the GNU Lesser General Public License "or any later version"
|
|
||||||
applies to it, you have the option of following the terms and
|
|
||||||
conditions either of that published version or of any later version
|
|
||||||
published by the Free Software Foundation. If the Library as you
|
|
||||||
received it does not specify a version number of the GNU Lesser
|
|
||||||
General Public License, you may choose any version of the GNU Lesser
|
|
||||||
General Public License ever published by the Free Software Foundation.
|
|
||||||
|
|
||||||
If the Library as you received it specifies that a proxy can decide
|
|
||||||
whether future versions of the GNU Lesser General Public License shall
|
|
||||||
apply, that proxy's public statement of acceptance of any version is
|
|
||||||
permanent authorization for you to choose that version for the
|
|
||||||
Library.
|
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
[Events](/docs/scarpet/api/Events.md)
|
[Events](/docs/scarpet/api/Events.md)
|
||||||
|
|
||||||
[Scoreboard manipulation](docs/scarpet/api/Scoreboard.md)
|
[Scoreboard manipulation](/docs/scarpet/api/Scoreboard.md)
|
||||||
|
|
||||||
[Auxiliary Functions](/docs/scarpet/api/Auxiliary.md)
|
[Auxiliary Functions](/docs/scarpet/api/Auxiliary.md)
|
||||||
|
|
||||||
|
|||||||
+228
-48
@@ -1,15 +1,9 @@
|
|||||||
# Fundamental components of `scarpet` programming language (towards version 1.7).
|
# Fundamental components of `scarpet` programming language.
|
||||||
|
|
||||||
Scarpet (a.k.a. Carpet Script, or Script for Carpet) is a programming language
|
Scarpet (a.k.a. Carpet Script, or Script for Carpet) is a programming language
|
||||||
designed to provide the ability to write custom programs to run within Minecraft
|
designed to provide the ability to write custom programs to run within Minecraft
|
||||||
and interact with the world.
|
and interact with the world.
|
||||||
|
|
||||||
The project was initially built based on the EvalEx project, however it now diverged
|
|
||||||
so far away from the original implementation, it would be hard to tell it without
|
|
||||||
this mention. EvalEx is a handy expression evaluator for Java, that allows to evaluate
|
|
||||||
simple mathematical and boolean expressions. For more information,
|
|
||||||
see: [EvalEx GitHub repository](https://github.com/uklimaschewski/EvalEx)
|
|
||||||
|
|
||||||
This specification is divided into two sections: this one is agnostic to any
|
This specification is divided into two sections: this one is agnostic to any
|
||||||
Minecraft related features and could function on its own, and CarpetExpression
|
Minecraft related features and could function on its own, and CarpetExpression
|
||||||
for Minecraft specific routines and world manipulation functions.
|
for Minecraft specific routines and world manipulation functions.
|
||||||
@@ -277,6 +271,25 @@ check_foo_not_zero();
|
|||||||
...
|
...
|
||||||
check_foo_not_zero() -> if(global_foo == 0, global_foo = 1)
|
check_foo_not_zero() -> if(global_foo == 0, global_foo = 1)
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## Scarpet preprocessor
|
||||||
|
|
||||||
|
There are several preprocessing operations applied to the source of your program to clean it up and prepare for
|
||||||
|
execution. Some of them will affect your code as it is reported via stack traces and function definition, and some
|
||||||
|
are applied only on the surface.
|
||||||
|
- stripping `//` comments (in file mode)
|
||||||
|
- replacing `$` with newlines (in command mode, modifies submitted code)
|
||||||
|
- removing extra semicolons that don't follow `;` use as a binary operator, allowing for lenient use of semicolons
|
||||||
|
- translating `{` into `m(`, `[` into `l(`, and `]` and `}` into `)`
|
||||||
|
|
||||||
|
No further optimizations are currently applied to your code.
|
||||||
|
|
||||||
|
## Mentions
|
||||||
|
|
||||||
|
LR1 parser, tokenizer, and several built-in functions are built based on the EvalEx project.
|
||||||
|
EvalEx is a handy expression evaluator for Java, that allows to evaluate
|
||||||
|
simple mathematical and boolean expressions. EvalEx is distributed under MIT licence.
|
||||||
|
For more information, see: [EvalEx GitHub repository](https://github.com/uklimaschewski/EvalEx)
|
||||||
# Variables and Constants
|
# Variables and Constants
|
||||||
|
|
||||||
`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
|
||||||
@@ -296,9 +309,9 @@ block positions. All variables starting with `_` are read-only, and cannot be de
|
|||||||
|
|
||||||
`scarpet` accepts numeric and string liters constants. Numbers look like `1, 2.5, -3e-7, 0xff,` and are internally
|
`scarpet` accepts numeric and string liters constants. Numbers look like `1, 2.5, -3e-7, 0xff,` and are internally
|
||||||
represented as Java's `double` but `scarpet` will try to trim trailing zeros as much as possible so if you need to
|
represented as Java's `double` but `scarpet` will try to trim trailing zeros as much as possible so if you need to
|
||||||
use them as intergers, you can. Strings using single quoting, for multiple reasons, but primarily to allow for
|
use them as intergers, you can. Strings use single quoting, for multiple reasons, but primarily to allow for
|
||||||
easier use of strings inside doubly quoted command arguments (when passing a script as a parameter of `/script fill`
|
easier use of strings inside doubly quoted command arguments (when passing a script as a parameter of `/script fill`
|
||||||
for example, or when typing in jsons inside scarpet to feed back into a `/data merge` command for example.
|
for example), or when typing in jsons inside scarpet (to feed back into a `/data merge` command for example).
|
||||||
Strings also use backslashes `\` for quoting special characters, in both plain strings and regular expressions
|
Strings also use backslashes `\` for quoting special characters, in both plain strings and regular expressions
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
@@ -882,10 +895,13 @@ Thou shall not sample from noise changing seed frequently. Scarpet will keep tra
|
|||||||
used for sampling providing similar speed comparing to the default seed of `0`. In case the app engine uses more
|
used for sampling providing similar speed comparing to the default seed of `0`. In case the app engine uses more
|
||||||
than 256 seeds at the same time, switching between them can get much more expensive.
|
than 256 seeds at the same time, switching between them can get much more expensive.
|
||||||
|
|
||||||
### `print(expr)`
|
### `print(expr)`, `print(player, expr)`
|
||||||
|
|
||||||
prints the value of the expression to chat. Passes the result of the argument to the output unchanged,
|
prints the value of the expression to chat. Passes the result of the argument to the output unchanged,
|
||||||
so `print`-statements can be weaved in code to debug programming issues
|
so `print`-statements can be weaved in code to debug programming issues. By default it uses the same communication
|
||||||
|
channels that most vanilla commands are using.
|
||||||
|
|
||||||
|
In case player is directly specified, it only sends the message to that player, like `tell` command.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
print('foo') => results in foo, prints: foo
|
print('foo') => results in foo, prints: foo
|
||||||
@@ -918,6 +934,52 @@ flip_my_world_upside_down();
|
|||||||
print(str('this took %d milliseconds',time()-start_time))
|
print(str('this took %d milliseconds',time()-start_time))
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
### `unix_time()`
|
||||||
|
|
||||||
|
Returns standard POSIX time as a number of milliseconds since the start of the epoch
|
||||||
|
(00:00 am and 0 seconds, 1 Jan 1970).
|
||||||
|
Unlike the previous function, this can be used to get exact time, but it varies from time zone to time zone.
|
||||||
|
|
||||||
|
### `convert_date(milliseconds)`
|
||||||
|
### `convert_date(year, month, date, hours?, mins?, secs?)`
|
||||||
|
### `convert_date(l(year, month, date, hours?, mins?, secs?))`
|
||||||
|
|
||||||
|
If called with a single argument, converts standard POSIX time to a list in the format:
|
||||||
|
|
||||||
|
`l(year, month, date, hours, mins, secs, day_of_week, day_of_year, week_of_year)`
|
||||||
|
|
||||||
|
eg: `convert_date(1592401346960) -> [2020, 6, 17, 10, 42, 26, 3, 169, 25]`
|
||||||
|
|
||||||
|
Where the `6` stands for June, but `17` stands for 17th, `10` stands for 10am,
|
||||||
|
`42` stands for 42 minutes past the hour, and `26` stands for 26 seconds past the minute,
|
||||||
|
and `3` stands for Wednesday, `169` is the day of year, and `25` is a week of year.
|
||||||
|
|
||||||
|
Run `convert_date(unix_time())` to get current time as list.
|
||||||
|
|
||||||
|
|
||||||
|
When called with a list, or with 3 or 6 arguments, returns standard POSIX time as a number of milliseconds since the
|
||||||
|
start of the epoch (1 Jan 1970),
|
||||||
|
using the time inputted into the function as opposed to the system time.
|
||||||
|
|
||||||
|
Example editing:
|
||||||
|
<pre>
|
||||||
|
date = convert_date(unix_time());
|
||||||
|
|
||||||
|
months = l('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
|
||||||
|
|
||||||
|
days = l('Mon','Tue','Wed','Thu','Fri','Sat','Sun');
|
||||||
|
|
||||||
|
print(
|
||||||
|
str('Its %s, %d %s %d, %02d:%02d:%02d',
|
||||||
|
days:(date:6-1), date:2, months:(date:1-1), date:0, date:3, date:4, date:5
|
||||||
|
)
|
||||||
|
)
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
This will give you a date:
|
||||||
|
|
||||||
|
It is currently `hrs`:`mins` and `secs` seconds on the `date`th of `month`, `year`
|
||||||
|
|
||||||
### `profile_expr(expression)`
|
### `profile_expr(expression)`
|
||||||
|
|
||||||
Returns number of times given expression can be run in 50ms time. Useful to profile and optimize your code.
|
Returns number of times given expression can be run in 50ms time. Useful to profile and optimize your code.
|
||||||
@@ -1183,6 +1245,8 @@ a() -> global_list+=1; global_list = l(1,2,3); a(); a(); global_list // => [1,2
|
|||||||
|
|
||||||
### `Operator ->`
|
### `Operator ->`
|
||||||
|
|
||||||
|
`->` operator has two uses - as a function definition operator and key-value initializer for maps.
|
||||||
|
|
||||||
To organize code better than a flat sequence of operations, one can define functions. Definition is correct
|
To organize code better than a flat sequence of operations, one can define functions. Definition is correct
|
||||||
if has the following form
|
if has the following form
|
||||||
|
|
||||||
@@ -1207,7 +1271,17 @@ a(lst) -> lst+=1; list = l(1,2,3); a(list); a(list); list // => [1,2,3]
|
|||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
In case the inner function wants to operate and modify larger objects, lists from the outer scope, but not global,
|
In case the inner function wants to operate and modify larger objects, lists from the outer scope, but not global,
|
||||||
it needs to use `outer` function in function signature
|
it needs to use `outer` function in function signature.
|
||||||
|
|
||||||
|
in map construction context (directly in `m()` or `{}`), the `->` operator has a different function by converting its
|
||||||
|
arguments to a tuple which is used by map constructor as a key-value pair:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
{ 'foo' -> 'bar' } => {l('foo', 'bar')}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
This means that it is not possible to define literally a set of inline function, however a set of functions can still
|
||||||
|
be created by adding elements to an empty set, and building it this way.
|
||||||
|
|
||||||
### `outer(arg)`
|
### `outer(arg)`
|
||||||
|
|
||||||
@@ -1423,26 +1497,31 @@ tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl[
|
|||||||
|
|
||||||
## List operations
|
## List operations
|
||||||
|
|
||||||
### `l(values ...), l(iterator)`
|
### `[value, ...?]`,`[iterator]`,`l(value, ...?)`, `l(iterator)`
|
||||||
|
|
||||||
Creates a list of values of the expressions passed as parameters. It can be used as an L-value and if all
|
Creates a list of values of the expressions passed as parameters. It can be used as an L-value and if all
|
||||||
elements are variables, you coujld use it to return multiple results from one function call, if that
|
elements are variables, you coujld use it to return multiple results from one function call, if that
|
||||||
function returns a list of results with the same size as the `l` call uses. In case there is only one
|
function returns a list of results with the same size as the `[]` call uses. In case there is only one
|
||||||
argument and it is an iterator (vanilla expression specification has `range`, but Minecraft API implements
|
argument and it is an iterator (vanilla expression specification has `range`, but Minecraft API implements
|
||||||
a bunch of them, like `diamond`), it will convert it to a proper list. Iterators can only be used in high order
|
a bunch of them, like `diamond`), it will convert it to a proper list. Iterators can only be used in high order
|
||||||
functions, and are treated as empty lists, unless unrolled with `l`
|
functions, and are treated as empty lists, unless unrolled with `[]`.
|
||||||
|
|
||||||
|
Internally, `[elem, ...]`(list syntax) and `l(elem, ...)`(function syntax) are equivalent. `[]` is simply translated to
|
||||||
|
`l()` in the scarpet preprocessing stage. This means that internally the code has always expression syntax despite `[]`
|
||||||
|
not using different kinds of brackets and not being proper operators. This means that `l(]` and `[)` are also valid
|
||||||
|
although not recommended as they will make your code far less readable.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
l(1,2,'foo') => [1, 2, foo]
|
l(1,2,'foo') <=> [1, 2, 'foo']
|
||||||
l() => [] (empty list)
|
l() <=> [] (empty list)
|
||||||
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]
|
||||||
l(1, 2) = l(3, 4) => Error: l is not a variable
|
[1, 2] = [3, 4] => Error: l is not a variable
|
||||||
l(foo, bar) = l(3,4); foo==3 && bar==4 => 1
|
[foo, bar] = [3, 4]; foo==3 && bar==4 => 1
|
||||||
l(foo, bar, baz) = l(2, 4, 6); l(min(foo, bar), baz) = l(3, 5); l(foo, bar, baz) => [3, 4, 5]
|
[foo, bar, baz] = [2, 4, 6]; [min(foo, bar), baz] = [3, 5]; [foo, bar, baz] => [3, 4, 5]
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
In the last example `l(min(foo, bar), baz)` creates a valid L-value, as min(foo, bar) finds the lower of the
|
In the last example `[min(foo, bar), baz]` creates a valid L-value, as `min(foo, bar)` finds the lower of the
|
||||||
variables (in this case `foo`) creating a valid assignable L-list of [foo, baz], and these values
|
variables (in this case `foo`) creating a valid assignable L-list of `[foo, baz]`, and these values
|
||||||
will be assigned new values
|
will be assigned new values
|
||||||
|
|
||||||
### `join(delim, list), join(delim, values ...)`
|
### `join(delim, list), join(delim, values ...)`
|
||||||
@@ -1516,29 +1595,37 @@ range(5,10) => [5, 6, 7, 8, 9]
|
|||||||
range(20, 10, -2) => [20, 18, 16, 14, 12]
|
range(20, 10, -2) => [20, 18, 16, 14, 12]
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `element(list, index)(deprecated)`
|
|
||||||
|
|
||||||
Legacy support for older method that worked only on lists. Please use `get(...)` for equivalent support, or `.`
|
|
||||||
operator. Also previous unique behaviours with `put` on lists has been removed to support all type of
|
|
||||||
container types.
|
|
||||||
|
|
||||||
## Map operations
|
## Map operations
|
||||||
|
|
||||||
Scarpet supports map structures, aka hashmaps, dicts etc. Map structure can also be used, with `null` values as sets.
|
Scarpet supports map structures, aka hashmaps, dicts etc. Map structure can also be used, with `null` values as sets.
|
||||||
Apart from container access functions, (`. , get, put, has, delete`), the following functions:
|
Apart from container access functions, (`. , get, put, has, delete`), the following functions:
|
||||||
|
|
||||||
### `m(values ...), m(iterator), m(key_value_pairs)`
|
### `{values, ...}`,`{iterator}`,`{key -> value, ...}`,`m(values, ...)`, `m(iterator)`, `m(l(key, value), ...))`
|
||||||
|
|
||||||
creates and initializes a map with supplied keys, and values. If the arguments contains a flat list, these are all
|
creates and initializes a map with supplied keys, and values. If the arguments contains a flat list, these are all
|
||||||
treated as keys with no value, same goes with the iterator - creates a map that behaves like a set. If the
|
treated as keys with no value, same goes with the iterator - creates a map that behaves like a set. If the
|
||||||
arguments is a list of lists, they have to have two elements each, and then first is a key, and second, a value
|
arguments is a list of lists, they have to have two elements each, and then first is a key, and second, a value
|
||||||
|
|
||||||
|
In map creation context (directly inside `{}` or `m{}` call), `->` operator acts like a pair constructor for simpler
|
||||||
|
syntax providing key value pairs, so the invocation to `{foo -> bar, baz -> quux}` is equivalent to
|
||||||
|
`{l(foo, bar), l(baz, quux)}`, which is equivalent to somewhat older, but more traditional functional form of
|
||||||
|
`m(l(foo, bar),l(baz, quuz))`.
|
||||||
|
|
||||||
|
Internally, `{?}`(list syntax) and `m(?)`(function syntax) are equivalent. `{}` is simply translated to
|
||||||
|
`m()` in the scarpet preprocessing stage. This means that internally the code has always expression syntax despite `{}`
|
||||||
|
not using different kinds of brackets and not being proper operators. This means that `m(}` and `{)` are also valid
|
||||||
|
although not recommended as they will make your code far less readable.
|
||||||
|
|
||||||
|
When converting map value to string, `':'` is used as a key-value separator due to tentative compatibility with NBT
|
||||||
|
notation, meaning in simpler cases maps can be converted to NBT parsable string by calling `str()`. This however
|
||||||
|
does not guarantee a parsable output. To properly convert to NBT value, use `encode_nbt()`.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
m(1,2,'foo') => {1: null, 2: null, foo: null}
|
{1, 2, 'foo'} => {1: null, 2: null, foo: null}
|
||||||
m() => {} (empty map)
|
m() <=> {} (empty map)
|
||||||
m(range(10)) => {0: null, 1: null, 2: null, 3: null, 4: null, 5: null, 6: null, 7: null, 8: null, 9: null}
|
{range(10)} => {0: null, 1: null, 2: null, 3: null, 4: null, 5: null, 6: null, 7: null, 8: null, 9: null}
|
||||||
m(l(1, 2), l(3, 4)) => {1: 2, 3: 4}
|
m(l(1, 2), l(3, 4)) <=> {1 -> 2, 3 -> 4} => {1: 2, 3: 4}
|
||||||
reduce(range(10), put(_a, _, _*_); _a, m())
|
reduce(range(10), put(_a, _, _*_); _a, {})
|
||||||
=> {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
|
=> {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
@@ -1595,11 +1682,14 @@ print(b); // 'stone', block was evaluated 'eagerly' but call to `block`
|
|||||||
All the functions below can be used with block value, queried with coord triple, or 3-long list. All `pos` in the
|
All the functions below can be used with block value, queried with coord triple, or 3-long list. All `pos` in the
|
||||||
functions referenced below refer to either method of passing block position.
|
functions referenced below refer to either method of passing block position.
|
||||||
|
|
||||||
### `set(pos, block, property?, value?, ...)`
|
### `set(pos, block, property?, value?, ..., block_data?)`, `set(pos, block, l(property?, value?, ...), block_data?)`
|
||||||
|
|
||||||
First part of the `set` function is either a coord triple, list of three numbers, or other block with coordinates.
|
First part of the `set` function is either a coord triple, list of three numbers, or other block with coordinates.
|
||||||
Second part, `block` is either block value as a result of `block()` function string value indicating the block name,
|
Second part, `block` is either block value as a result of `block()` function string value indicating the block name,
|
||||||
and optional `property - value` pairs for extra block properties. If `block` is specified only by name, then if a
|
and optional `property - value` pairs for extra block properties. Optional `block_data` include the block data to
|
||||||
|
be set for the target block.
|
||||||
|
|
||||||
|
If `block` is specified only by name, then if a
|
||||||
destination block is the same the `set` operation is skipped, otherwise is executed, for other potential extra
|
destination block is the same the `set` operation is skipped, otherwise is executed, for other potential extra
|
||||||
properties.
|
properties.
|
||||||
|
|
||||||
@@ -1623,11 +1713,15 @@ set(x,y,z,'iron_trapdoor[half=top]') // Incorrect. sets bottom iron trapdoor -
|
|||||||
set(x,y,z,'iron_trapdoor','half','top') // correct - top trapdoor
|
set(x,y,z,'iron_trapdoor','half','top') // correct - top trapdoor
|
||||||
set(x,y,z, block('iron_trapdoor[half=top]')) // also correct, block() provides extra parsing
|
set(x,y,z, block('iron_trapdoor[half=top]')) // also correct, block() provides extra parsing
|
||||||
set(x,y,z,'hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') // extra block data
|
set(x,y,z,'hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') // extra block data
|
||||||
|
set(x,y,z,'hopper', l('facing', 'north'), nbt('{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') ) // same
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `without_updates(expr)`
|
### `without_updates(expr)`
|
||||||
|
|
||||||
Evaluates subexpression without causing updates when blocks change in the world
|
Evaluates subexpression without causing updates when blocks change in the world.
|
||||||
|
|
||||||
|
For synchronization sake, as well as from the fact that suppressed update can only happen within a tick,
|
||||||
|
the call to the `expr` is docked on the main server task.
|
||||||
|
|
||||||
Consider following scenario: We would like to generate a bunch of terrain in a flat world following a perlin noise
|
Consider following scenario: We would like to generate a bunch of terrain in a flat world following a perlin noise
|
||||||
generator. The following code causes a cascading effect as blocks placed on chunk borders will cause other chunks to get
|
generator. The following code causes a cascading effect as blocks placed on chunk borders will cause other chunks to get
|
||||||
@@ -2281,7 +2375,7 @@ This function is tentative - will likely change when chunk ticket API is properl
|
|||||||
|
|
||||||
These functions help scan larger areas of blocks without using generic loop functions, like nested `loop`.
|
These functions help scan larger areas of blocks without using generic loop functions, like nested `loop`.
|
||||||
|
|
||||||
### `scan(cx, cy, cz, dx, dy, dz, px?, py?, pz?, expr)`, `scan(center, range, lower_range?, expr)`
|
### `scan(center, range, lower_range?, expr)`
|
||||||
|
|
||||||
Evaluates expression over area of blocks defined by its center `center = (cx, cy, cz)`, expanded in all directions
|
Evaluates expression over area of blocks defined by its center `center = (cx, cy, cz)`, expanded in all directions
|
||||||
by `range = (dx, dy, dz)` blocks, or optionally in negative with `range` coords, and `upper_range` coords in
|
by `range = (dx, dy, dz)` blocks, or optionally in negative with `range` coords, and `upper_range` coords in
|
||||||
@@ -2297,14 +2391,15 @@ which would cause the expression not be evaluated for their boolean value.
|
|||||||
`scan` also handles `continue` and `break` statements, using `continue`'s return value to use in place of expression
|
`scan` also handles `continue` and `break` statements, using `continue`'s return value to use in place of expression
|
||||||
return value. `break` return value has no effect.
|
return value. `break` return value has no effect.
|
||||||
|
|
||||||
### `volume(x1, y1, z1, x2, y2, z2, expr)`
|
### `volume(from_pos, to_pos, expr)`
|
||||||
|
|
||||||
Evaluates expression for each block in the area, the same as the `scan` function, but using two opposite corners of
|
Evaluates expression for each block in the area, the same as the `scan` function, but using two opposite corners of
|
||||||
the rectangular cuboid. Any corners can be specified, its like you would do with `/fill` command.
|
the rectangular cuboid. Any corners can be specified, its like you would do with `/fill` command.
|
||||||
|
You can use a position or three coordinates to specify, it doesn't matter.
|
||||||
|
|
||||||
For return value and handling `break` and `continue` statements, see `scan` function above.
|
For return value and handling `break` and `continue` statements, see `scan` function above.
|
||||||
|
|
||||||
### `neighbours(x, y, z), neighbours(block), neighbours(l(x,y,z))`
|
### `neighbours(pos)`
|
||||||
|
|
||||||
Returns the list of 6 neighbouring blocks to the argument. Commonly used with other loop functions like `for`.
|
Returns the list of 6 neighbouring blocks to the argument. Commonly used with other loop functions like `for`.
|
||||||
|
|
||||||
@@ -2312,13 +2407,18 @@ Returns the list of 6 neighbouring blocks to the argument. Commonly used with ot
|
|||||||
for(neighbours(x,y,z),air(_)) => 4 // number of air blocks around a block
|
for(neighbours(x,y,z),air(_)) => 4 // number of air blocks around a block
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `rect(cx, cy, cz, dx?, dy?, dz?, px?, py?, pz?)`
|
### `rect(centre, range?, positive_range?)`
|
||||||
|
|
||||||
returns an iterator, just like `range` function that iterates over rectangular cubarea of blocks. If only center
|
Returns an iterator, just like `range` function that iterates over a rectangular area of blocks. If only center
|
||||||
point is specified, it iterates over 27 blocks. If `d` arguments are specified, expands selection by the respective
|
point is specified, it iterates over 27 blocks. If `range` arguments are specified, expands selection by the respective
|
||||||
number of blocks in each direction. If `p` arguments are specified, it uses `d` for negative offset, and `p` for positive.
|
number of blocks in each direction. If `positive_range` arguments are specified,
|
||||||
|
it uses `range` for negative offset, and `positive_range` for positive.
|
||||||
|
|
||||||
### `diamond(cx, cy, cz, radius?, height?)`
|
`centre` can be defined either as three coordinates, a list of three coords, or a block value.
|
||||||
|
`range` and `positive_range` can have the same representations, just if its a block, it computes the distance to the center
|
||||||
|
as range instead of taking the values as is.`
|
||||||
|
|
||||||
|
### `diamond(centre_pos, radius?, height?)`
|
||||||
|
|
||||||
Iterates over a diamond like area of blocks. With no radius and height, its 7 blocks centered around the middle
|
Iterates over a diamond like area of blocks. With no radius and height, its 7 blocks centered around the middle
|
||||||
(block + neighbours). With a radius specified, it expands shape on x and z coords, and with a custom height, on y.
|
(block + neighbours). With a radius specified, it expands shape on x and z coords, and with a custom height, on y.
|
||||||
@@ -2857,6 +2957,15 @@ Modifies directly player raw hunger components. Has no effect on non-players
|
|||||||
adds exhaustion value to the current player exhaustion level - that's the method you probably want to use
|
adds exhaustion value to the current player exhaustion level - that's the method you probably want to use
|
||||||
to manipulate how much 'food' some action cost.
|
to manipulate how much 'food' some action cost.
|
||||||
|
|
||||||
|
### `modify(e, 'nbt_merge', partial_tag)`
|
||||||
|
|
||||||
|
Merges a partial tag into the entity data and reloads the entity from its updated tag. Cannot be applied to players
|
||||||
|
|
||||||
|
### `modify(e, 'nbt', tag)`
|
||||||
|
|
||||||
|
Reloads the entity from a supplied tag. Better get a valid entity tag, what can go wrong. Wonder what would happen if
|
||||||
|
transplant rabbit's brain into a villager. Cannot be applied to players.
|
||||||
|
|
||||||
## Entity Events
|
## Entity Events
|
||||||
|
|
||||||
There is a number of events that happen to entities that you can attach your own code to in the form of event handlers.
|
There is a number of events that happen to entities that you can attach your own code to in the form of event handlers.
|
||||||
@@ -2940,6 +3049,51 @@ item_category('ender_pearl') => misc
|
|||||||
item_category('stone') => building_blocks
|
item_category('stone') => building_blocks
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
### `recipe_data(item, type?)`, `recipe_data(recipe, type?)`
|
||||||
|
|
||||||
|
returns all recipes matching either an `item`, or represent actual `recipe` name. In vanilla datapack, for all items
|
||||||
|
that have one recipe available, the recipe name is the same as the item name but if an item has multiple recipes, its
|
||||||
|
direct name can be different.
|
||||||
|
|
||||||
|
Recipe type can take one of the following options:
|
||||||
|
* `'crafting'` - default, crafting table recipe
|
||||||
|
* `'smelting'` - furnace recipe
|
||||||
|
* `'blasting'` - blast furnace recipe
|
||||||
|
* `'smoking'` - smoker recipe
|
||||||
|
* `'campfire_cooking'` - campfire recipe
|
||||||
|
* `'stonecutting'` - stonecutter recipe
|
||||||
|
* `'smithing'` - smithing table (1.16+)
|
||||||
|
|
||||||
|
The return value is a list of available recipes (even if there is only one recipe available). Each recipe contains of
|
||||||
|
an item triple of the crafting result, list of ingredients, each containing a list of possible variants of the
|
||||||
|
ingredients in this slot, as item triples, or `null` if its a shaped recipe and a given slot in the patterns is left
|
||||||
|
empty, and recipe specification as another list. Possible recipe specs is:
|
||||||
|
* `['shaped', width, height]` - shaped crafting. `width` and `height` can be 1, 2 or 3.
|
||||||
|
* `['shapeless']` - shapeless crafting
|
||||||
|
* `['smelting', duration, xp]` - smelting/cooking recipes
|
||||||
|
* `['cutting']` - stonecutter recipe
|
||||||
|
* `['special']` - special crafting recipe, typically not present in the crafting menu
|
||||||
|
* `['custom']` - other recipe types
|
||||||
|
|
||||||
|
Note that ingredients are specified as tripes, with count and nbt information. Currently all recipes require always one
|
||||||
|
of the ingredients, and for some recipes, even if the nbt data for the ingredient is specified (e.g. `dispenser`), it
|
||||||
|
can accept items of any tags.
|
||||||
|
|
||||||
|
Also note that some recipes leave some products in the crafting window, and these can be determined using
|
||||||
|
`crafting_remaining_item()` function
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
<pre>
|
||||||
|
recipe_data('iron_ingot_from_nuggets')
|
||||||
|
recipe_data('iron_ingot')
|
||||||
|
recipe_data('glass', 'smelting')
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
### `crafting_remaining_item(item)`
|
||||||
|
|
||||||
|
returns `null` if the item has no remaining item in the crafting window when used as a crafting ingredient, or an
|
||||||
|
item name that serves as a replacement after crafting is done. Currently it can only be buckets and glass bottles.
|
||||||
|
|
||||||
### `inventory_size(inventory)`
|
### `inventory_size(inventory)`
|
||||||
|
|
||||||
Returns the size of the inventory for the entity or block in question. Returns null if the block or entity don't
|
Returns the size of the inventory for the entity or block in question. Returns null if the block or entity don't
|
||||||
@@ -3055,6 +3209,8 @@ __on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec) /
|
|||||||
__on_player_interacts_with_block(player, hand, block, face, hitvec) //right click on a block resulted in activation of said block
|
__on_player_interacts_with_block(player, hand, block, face, hitvec) //right click on a block resulted in activation of said block
|
||||||
__on_player_places_block(player, item_tuple, hand, block) // player have just placed the block.
|
__on_player_places_block(player, item_tuple, hand, block) // player have just placed the block.
|
||||||
__on_player_interacts_with_entity(player, entity, hand)
|
__on_player_interacts_with_entity(player, entity, hand)
|
||||||
|
__on_player_chooses_recipe(player, recipe, full_stack)
|
||||||
|
__on_player_switches_slot(player, from, to)
|
||||||
__on_player_attacks_entity(player, entity)
|
__on_player_attacks_entity(player, entity)
|
||||||
__on_player_takes_damage(player, amount, source, source_entity)
|
__on_player_takes_damage(player, amount, source, source_entity)
|
||||||
__on_player_deals_damage(player, amount, entity)
|
__on_player_deals_damage(player, amount, entity)
|
||||||
@@ -3185,7 +3341,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.
|
||||||
* `width` - line thickness, defaults to 2.0pt
|
* `line` - line thickness, defaults to 2.0pt
|
||||||
* `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
|
||||||
@@ -3248,6 +3404,30 @@ Consult section about container operations in `Expression` to learn about possib
|
|||||||
Excapes all the special characters in the string or nbt tag and returns a string that can be stored in nbt directly
|
Excapes all the special characters in the string or nbt tag and returns a string that can be stored in nbt directly
|
||||||
as a string value.
|
as a string value.
|
||||||
|
|
||||||
|
### parse_nbt(tag)
|
||||||
|
|
||||||
|
Converts NBT tag to a scarpet value, which you can navigate through much better.
|
||||||
|
|
||||||
|
Converts:
|
||||||
|
- Compound tags into maps with string keys
|
||||||
|
- List tags into list values
|
||||||
|
- Numbers (Ints, Floats, Doubles, Longs) into a number
|
||||||
|
- Rest is converted to strings.
|
||||||
|
|
||||||
|
### encode_nbt(expr, force?)
|
||||||
|
|
||||||
|
Encodes value of the expression as an NBT tag. By default (or when `force` is false), it will only allow
|
||||||
|
to encode values that are guaranteed to return the same value when applied the resulting tag to `parse_nbt()`.
|
||||||
|
Supported types that can reliably convert back and forth to and from NBT values are:
|
||||||
|
- Maps with string keywords
|
||||||
|
- Lists of items of the same type (scarpet will take care of unifying value types if possible)
|
||||||
|
- Numbers (encoded as Ints -> Longs -> Doubles, as needed)
|
||||||
|
- Strings
|
||||||
|
|
||||||
|
Other value types will only be converted to tags (including NBT tags) if `force` is true. They would require
|
||||||
|
extra treatment when loading them back from NBT, but using `force` true will always produce output / never
|
||||||
|
produce an exception.
|
||||||
|
|
||||||
### `print(expr)`
|
### `print(expr)`
|
||||||
|
|
||||||
Displays the result of the expression to the chat. Overrides default `scarpet` behaviour of sending everyting to stderr.
|
Displays the result of the expression to the chat. Overrides default `scarpet` behaviour of sending everyting to stderr.
|
||||||
@@ -3318,7 +3498,7 @@ With the specified `resource` in the scripts folder, of a specific `type`, write
|
|||||||
content, or deletes the resource.
|
content, or deletes the resource.
|
||||||
|
|
||||||
Resource is identified by a path to the file.
|
Resource is identified by a path to the file.
|
||||||
A path can contain letters, numbers and 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. Do not add file extensions to the descriptor - extensions are inferred
|
from the name. Empty descriptors are invalid. Do not add file extensions to the descriptor - extensions are inferred
|
||||||
based on the `type` of the file.
|
based on the `type` of the file.
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,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.
|
||||||
* `width` - line thickness, defaults to 2.0pt
|
* `line` - line thickness, defaults to 2.0pt
|
||||||
* `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
|
||||||
@@ -130,6 +130,30 @@ Consult section about container operations in `Expression` to learn about possib
|
|||||||
Excapes all the special characters in the string or nbt tag and returns a string that can be stored in nbt directly
|
Excapes all the special characters in the string or nbt tag and returns a string that can be stored in nbt directly
|
||||||
as a string value.
|
as a string value.
|
||||||
|
|
||||||
|
### parse_nbt(tag)
|
||||||
|
|
||||||
|
Converts NBT tag to a scarpet value, which you can navigate through much better.
|
||||||
|
|
||||||
|
Converts:
|
||||||
|
- Compound tags into maps with string keys
|
||||||
|
- List tags into list values
|
||||||
|
- Numbers (Ints, Floats, Doubles, Longs) into a number
|
||||||
|
- Rest is converted to strings.
|
||||||
|
|
||||||
|
### encode_nbt(expr, force?)
|
||||||
|
|
||||||
|
Encodes value of the expression as an NBT tag. By default (or when `force` is false), it will only allow
|
||||||
|
to encode values that are guaranteed to return the same value when applied the resulting tag to `parse_nbt()`.
|
||||||
|
Supported types that can reliably convert back and forth to and from NBT values are:
|
||||||
|
- Maps with string keywords
|
||||||
|
- Lists of items of the same type (scarpet will take care of unifying value types if possible)
|
||||||
|
- Numbers (encoded as Ints -> Longs -> Doubles, as needed)
|
||||||
|
- Strings
|
||||||
|
|
||||||
|
Other value types will only be converted to tags (including NBT tags) if `force` is true. They would require
|
||||||
|
extra treatment when loading them back from NBT, but using `force` true will always produce output / never
|
||||||
|
produce an exception.
|
||||||
|
|
||||||
### `print(expr)`
|
### `print(expr)`
|
||||||
|
|
||||||
Displays the result of the expression to the chat. Overrides default `scarpet` behaviour of sending everyting to stderr.
|
Displays the result of the expression to the chat. Overrides default `scarpet` behaviour of sending everyting to stderr.
|
||||||
@@ -200,7 +224,7 @@ With the specified `resource` in the scripts folder, of a specific `type`, write
|
|||||||
content, or deletes the resource.
|
content, or deletes the resource.
|
||||||
|
|
||||||
Resource is identified by a path to the file.
|
Resource is identified by a path to the file.
|
||||||
A path can contain letters, numbers and 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. Do not add file extensions to the descriptor - extensions are inferred
|
from the name. Empty descriptors are invalid. Do not add file extensions to the descriptor - extensions are inferred
|
||||||
based on the `type` of the file.
|
based on the `type` of the file.
|
||||||
|
|
||||||
@@ -368,7 +392,7 @@ Queries in-game statistics for certain values. Categories include:
|
|||||||
* `picked_up`: items picked up
|
* `picked_up`: items picked up
|
||||||
* `dropped`: items dropped
|
* `dropped`: items dropped
|
||||||
* `killed`: mobs killed
|
* `killed`: mobs killed
|
||||||
* `killed_by`: blocks mined
|
* `killed_by`: mobs killed by
|
||||||
* `custom`: various random stats
|
* `custom`: various random stats
|
||||||
|
|
||||||
For the options of `entry`, consult your statistics page, or give it a guess.
|
For the options of `entry`, consult your statistics page, or give it a guess.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
These functions help scan larger areas of blocks without using generic loop functions, like nested `loop`.
|
These functions help scan larger areas of blocks without using generic loop functions, like nested `loop`.
|
||||||
|
|
||||||
### `scan(cx, cy, cz, dx, dy, dz, px?, py?, pz?, expr)`, `scan(center, range, lower_range?, expr)`
|
### `scan(center, range, lower_range?, expr)`
|
||||||
|
|
||||||
Evaluates expression over area of blocks defined by its center `center = (cx, cy, cz)`, expanded in all directions
|
Evaluates expression over area of blocks defined by its center `center = (cx, cy, cz)`, expanded in all directions
|
||||||
by `range = (dx, dy, dz)` blocks, or optionally in negative with `range` coords, and `upper_range` coords in
|
by `range = (dx, dy, dz)` blocks, or optionally in negative with `range` coords, and `upper_range` coords in
|
||||||
@@ -18,14 +18,15 @@ which would cause the expression not be evaluated for their boolean value.
|
|||||||
`scan` also handles `continue` and `break` statements, using `continue`'s return value to use in place of expression
|
`scan` also handles `continue` and `break` statements, using `continue`'s return value to use in place of expression
|
||||||
return value. `break` return value has no effect.
|
return value. `break` return value has no effect.
|
||||||
|
|
||||||
### `volume(x1, y1, z1, x2, y2, z2, expr)`
|
### `volume(from_pos, to_pos, expr)`
|
||||||
|
|
||||||
Evaluates expression for each block in the area, the same as the `scan` function, but using two opposite corners of
|
Evaluates expression for each block in the area, the same as the `scan` function, but using two opposite corners of
|
||||||
the rectangular cuboid. Any corners can be specified, its like you would do with `/fill` command.
|
the rectangular cuboid. Any corners can be specified, its like you would do with `/fill` command.
|
||||||
|
You can use a position or three coordinates to specify, it doesn't matter.
|
||||||
|
|
||||||
For return value and handling `break` and `continue` statements, see `scan` function above.
|
For return value and handling `break` and `continue` statements, see `scan` function above.
|
||||||
|
|
||||||
### `neighbours(x, y, z), neighbours(block), neighbours(l(x,y,z))`
|
### `neighbours(pos)`
|
||||||
|
|
||||||
Returns the list of 6 neighbouring blocks to the argument. Commonly used with other loop functions like `for`.
|
Returns the list of 6 neighbouring blocks to the argument. Commonly used with other loop functions like `for`.
|
||||||
|
|
||||||
@@ -33,13 +34,18 @@ Returns the list of 6 neighbouring blocks to the argument. Commonly used with ot
|
|||||||
for(neighbours(x,y,z),air(_)) => 4 // number of air blocks around a block
|
for(neighbours(x,y,z),air(_)) => 4 // number of air blocks around a block
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `rect(cx, cy, cz, dx?, dy?, dz?, px?, py?, pz?)`
|
### `rect(centre, range?, positive_range?)`
|
||||||
|
|
||||||
returns an iterator, just like `range` function that iterates over rectangular cubarea of blocks. If only center
|
Returns an iterator, just like `range` function that iterates over a rectangular area of blocks. If only center
|
||||||
point is specified, it iterates over 27 blocks. If `d` arguments are specified, expands selection by the respective
|
point is specified, it iterates over 27 blocks. If `range` arguments are specified, expands selection by the respective
|
||||||
number of blocks in each direction. If `p` arguments are specified, it uses `d` for negative offset, and `p` for positive.
|
number of blocks in each direction. If `positive_range` arguments are specified,
|
||||||
|
it uses `range` for negative offset, and `positive_range` for positive.
|
||||||
|
|
||||||
### `diamond(cx, cy, cz, radius?, height?)`
|
`centre` can be defined either as three coordinates, a list of three coords, or a block value.
|
||||||
|
`range` and `positive_range` can have the same representations, just if its a block, it computes the distance to the center
|
||||||
|
as range instead of taking the values as is.`
|
||||||
|
|
||||||
|
### `diamond(centre_pos, radius?, height?)`
|
||||||
|
|
||||||
Iterates over a diamond like area of blocks. With no radius and height, its 7 blocks centered around the middle
|
Iterates over a diamond like area of blocks. With no radius and height, its 7 blocks centered around the middle
|
||||||
(block + neighbours). With a radius specified, it expands shape on x and z coords, and with a custom height, on y.
|
(block + neighbours). With a radius specified, it expands shape on x and z coords, and with a custom height, on y.
|
||||||
|
|||||||
@@ -36,11 +36,14 @@ print(b); // 'stone', block was evaluated 'eagerly' but call to `block`
|
|||||||
All the functions below can be used with block value, queried with coord triple, or 3-long list. All `pos` in the
|
All the functions below can be used with block value, queried with coord triple, or 3-long list. All `pos` in the
|
||||||
functions referenced below refer to either method of passing block position.
|
functions referenced below refer to either method of passing block position.
|
||||||
|
|
||||||
### `set(pos, block, property?, value?, ...)`
|
### `set(pos, block, property?, value?, ..., block_data?)`, `set(pos, block, l(property?, value?, ...), block_data?)`
|
||||||
|
|
||||||
First part of the `set` function is either a coord triple, list of three numbers, or other block with coordinates.
|
First part of the `set` function is either a coord triple, list of three numbers, or other block with coordinates.
|
||||||
Second part, `block` is either block value as a result of `block()` function string value indicating the block name,
|
Second part, `block` is either block value as a result of `block()` function string value indicating the block name,
|
||||||
and optional `property - value` pairs for extra block properties. If `block` is specified only by name, then if a
|
and optional `property - value` pairs for extra block properties. Optional `block_data` include the block data to
|
||||||
|
be set for the target block.
|
||||||
|
|
||||||
|
If `block` is specified only by name, then if a
|
||||||
destination block is the same the `set` operation is skipped, otherwise is executed, for other potential extra
|
destination block is the same the `set` operation is skipped, otherwise is executed, for other potential extra
|
||||||
properties.
|
properties.
|
||||||
|
|
||||||
@@ -64,11 +67,15 @@ set(x,y,z,'iron_trapdoor[half=top]') // Incorrect. sets bottom iron trapdoor -
|
|||||||
set(x,y,z,'iron_trapdoor','half','top') // correct - top trapdoor
|
set(x,y,z,'iron_trapdoor','half','top') // correct - top trapdoor
|
||||||
set(x,y,z, block('iron_trapdoor[half=top]')) // also correct, block() provides extra parsing
|
set(x,y,z, block('iron_trapdoor[half=top]')) // also correct, block() provides extra parsing
|
||||||
set(x,y,z,'hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') // extra block data
|
set(x,y,z,'hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') // extra block data
|
||||||
|
set(x,y,z,'hopper', l('facing', 'north'), nbt('{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') ) // same
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `without_updates(expr)`
|
### `without_updates(expr)`
|
||||||
|
|
||||||
Evaluates subexpression without causing updates when blocks change in the world
|
Evaluates subexpression without causing updates when blocks change in the world.
|
||||||
|
|
||||||
|
For synchronization sake, as well as from the fact that suppressed update can only happen within a tick,
|
||||||
|
the call to the `expr` is docked on the main server task.
|
||||||
|
|
||||||
Consider following scenario: We would like to generate a bunch of terrain in a flat world following a perlin noise
|
Consider following scenario: We would like to generate a bunch of terrain in a flat world following a perlin noise
|
||||||
generator. The following code causes a cascading effect as blocks placed on chunk borders will cause other chunks to get
|
generator. The following code causes a cascading effect as blocks placed on chunk borders will cause other chunks to get
|
||||||
|
|||||||
@@ -128,6 +128,10 @@ query(e,'type') => villager
|
|||||||
Returns a valid string to be used in commands to address an entity. Its UUID for all entities except
|
Returns a valid string to be used in commands to address an entity. Its UUID for all entities except
|
||||||
player, where its their name.
|
player, where its their name.
|
||||||
|
|
||||||
|
## `query(e, 'persistence')`
|
||||||
|
|
||||||
|
Returns if a mob has a persistence tag or not. Returns `null` for non-mob entities.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
run('/kill ' + e~'command_name');
|
run('/kill ' + e~'command_name');
|
||||||
</pre>
|
</pre>
|
||||||
@@ -411,9 +415,15 @@ Sets the corresponding component of the motion vector.
|
|||||||
|
|
||||||
Adds a vector to the motion vector. Most realistic way to apply a force to an entity.
|
Adds a vector to the motion vector. Most realistic way to apply a force to an entity.
|
||||||
|
|
||||||
### `modify(e, 'custom_name'), modify(e, 'custom_name', name)`
|
### `modify(e, 'custom_name')`, `modify(e, 'custom_name', name)`, `modify(e, 'custom_name', name, visible)`
|
||||||
|
|
||||||
Sets the custom name of the entity.
|
Sets the custom name of the entity. Without arguments - clears current custom name. Optional visible affects
|
||||||
|
if the custom name is always visible, even through blocks.
|
||||||
|
|
||||||
|
### `modify(e, 'persistence', bool?)`
|
||||||
|
|
||||||
|
Sets the entity persistence tag to `true` (default) or `false`. Only affects mobs. Persistent mobs
|
||||||
|
don't despawn and don't count towards the mobcap.
|
||||||
|
|
||||||
### `modify(e, 'age', number)`
|
### `modify(e, 'age', number)`
|
||||||
|
|
||||||
@@ -532,6 +542,15 @@ Modifies directly player raw hunger components. Has no effect on non-players
|
|||||||
adds exhaustion value to the current player exhaustion level - that's the method you probably want to use
|
adds exhaustion value to the current player exhaustion level - that's the method you probably want to use
|
||||||
to manipulate how much 'food' some action cost.
|
to manipulate how much 'food' some action cost.
|
||||||
|
|
||||||
|
### `modify(e, 'nbt_merge', partial_tag)`
|
||||||
|
|
||||||
|
Merges a partial tag into the entity data and reloads the entity from its updated tag. Cannot be applied to players
|
||||||
|
|
||||||
|
### `modify(e, 'nbt', tag)`
|
||||||
|
|
||||||
|
Reloads the entity from a supplied tag. Better get a valid entity tag, what can go wrong. Wonder what would happen if
|
||||||
|
transplant rabbit's brain into a villager. Cannot be applied to players.
|
||||||
|
|
||||||
## Entity Events
|
## Entity Events
|
||||||
|
|
||||||
There is a number of events that happen to entities that you can attach your own code to in the form of event handlers.
|
There is a number of events that happen to entities that you can attach your own code to in the form of event handlers.
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ __on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec) /
|
|||||||
__on_player_interacts_with_block(player, hand, block, face, hitvec) //right click on a block resulted in activation of said block
|
__on_player_interacts_with_block(player, hand, block, face, hitvec) //right click on a block resulted in activation of said block
|
||||||
__on_player_places_block(player, item_tuple, hand, block) // player have just placed the block.
|
__on_player_places_block(player, item_tuple, hand, block) // player have just placed the block.
|
||||||
__on_player_interacts_with_entity(player, entity, hand)
|
__on_player_interacts_with_entity(player, entity, hand)
|
||||||
|
__on_player_chooses_recipe(player, recipe, full_stack)
|
||||||
|
__on_player_switches_slot(player, from, to)
|
||||||
__on_player_attacks_entity(player, entity)
|
__on_player_attacks_entity(player, entity)
|
||||||
__on_player_takes_damage(player, amount, source, source_entity)
|
__on_player_takes_damage(player, amount, source, source_entity)
|
||||||
__on_player_deals_damage(player, amount, entity)
|
__on_player_deals_damage(player, amount, entity)
|
||||||
|
|||||||
@@ -38,6 +38,51 @@ item_category('ender_pearl') => misc
|
|||||||
item_category('stone') => building_blocks
|
item_category('stone') => building_blocks
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
### `recipe_data(item, type?)`, `recipe_data(recipe, type?)`
|
||||||
|
|
||||||
|
returns all recipes matching either an `item`, or represent actual `recipe` name. In vanilla datapack, for all items
|
||||||
|
that have one recipe available, the recipe name is the same as the item name but if an item has multiple recipes, its
|
||||||
|
direct name can be different.
|
||||||
|
|
||||||
|
Recipe type can take one of the following options:
|
||||||
|
* `'crafting'` - default, crafting table recipe
|
||||||
|
* `'smelting'` - furnace recipe
|
||||||
|
* `'blasting'` - blast furnace recipe
|
||||||
|
* `'smoking'` - smoker recipe
|
||||||
|
* `'campfire_cooking'` - campfire recipe
|
||||||
|
* `'stonecutting'` - stonecutter recipe
|
||||||
|
* `'smithing'` - smithing table (1.16+)
|
||||||
|
|
||||||
|
The return value is a list of available recipes (even if there is only one recipe available). Each recipe contains of
|
||||||
|
an item triple of the crafting result, list of ingredients, each containing a list of possible variants of the
|
||||||
|
ingredients in this slot, as item triples, or `null` if its a shaped recipe and a given slot in the patterns is left
|
||||||
|
empty, and recipe specification as another list. Possible recipe specs is:
|
||||||
|
* `['shaped', width, height]` - shaped crafting. `width` and `height` can be 1, 2 or 3.
|
||||||
|
* `['shapeless']` - shapeless crafting
|
||||||
|
* `['smelting', duration, xp]` - smelting/cooking recipes
|
||||||
|
* `['cutting']` - stonecutter recipe
|
||||||
|
* `['special']` - special crafting recipe, typically not present in the crafting menu
|
||||||
|
* `['custom']` - other recipe types
|
||||||
|
|
||||||
|
Note that ingredients are specified as tripes, with count and nbt information. Currently all recipes require always one
|
||||||
|
of the ingredients, and for some recipes, even if the nbt data for the ingredient is specified (e.g. `dispenser`), it
|
||||||
|
can accept items of any tags.
|
||||||
|
|
||||||
|
Also note that some recipes leave some products in the crafting window, and these can be determined using
|
||||||
|
`crafting_remaining_item()` function
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
<pre>
|
||||||
|
recipe_data('iron_ingot_from_nuggets')
|
||||||
|
recipe_data('iron_ingot')
|
||||||
|
recipe_data('glass', 'smelting')
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
### `crafting_remaining_item(item)`
|
||||||
|
|
||||||
|
returns `null` if the item has no remaining item in the crafting window when used as a crafting ingredient, or an
|
||||||
|
item name that serves as a replacement after crafting is done. Currently it can only be buckets and glass bottles.
|
||||||
|
|
||||||
### `inventory_size(inventory)`
|
### `inventory_size(inventory)`
|
||||||
|
|
||||||
Returns the size of the inventory for the entity or block in question. Returns null if the block or entity don't
|
Returns the size of the inventory for the entity or block in question. Returns null if the block or entity don't
|
||||||
|
|||||||
@@ -117,26 +117,31 @@ tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl[
|
|||||||
|
|
||||||
## List operations
|
## List operations
|
||||||
|
|
||||||
### `l(values ...), l(iterator)`
|
### `[value, ...?]`,`[iterator]`,`l(value, ...?)`, `l(iterator)`
|
||||||
|
|
||||||
Creates a list of values of the expressions passed as parameters. It can be used as an L-value and if all
|
Creates a list of values of the expressions passed as parameters. It can be used as an L-value and if all
|
||||||
elements are variables, you coujld use it to return multiple results from one function call, if that
|
elements are variables, you coujld use it to return multiple results from one function call, if that
|
||||||
function returns a list of results with the same size as the `l` call uses. In case there is only one
|
function returns a list of results with the same size as the `[]` call uses. In case there is only one
|
||||||
argument and it is an iterator (vanilla expression specification has `range`, but Minecraft API implements
|
argument and it is an iterator (vanilla expression specification has `range`, but Minecraft API implements
|
||||||
a bunch of them, like `diamond`), it will convert it to a proper list. Iterators can only be used in high order
|
a bunch of them, like `diamond`), it will convert it to a proper list. Iterators can only be used in high order
|
||||||
functions, and are treated as empty lists, unless unrolled with `l`
|
functions, and are treated as empty lists, unless unrolled with `[]`.
|
||||||
|
|
||||||
|
Internally, `[elem, ...]`(list syntax) and `l(elem, ...)`(function syntax) are equivalent. `[]` is simply translated to
|
||||||
|
`l()` in the scarpet preprocessing stage. This means that internally the code has always expression syntax despite `[]`
|
||||||
|
not using different kinds of brackets and not being proper operators. This means that `l(]` and `[)` are also valid
|
||||||
|
although not recommended as they will make your code far less readable.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
l(1,2,'foo') => [1, 2, foo]
|
l(1,2,'foo') <=> [1, 2, 'foo']
|
||||||
l() => [] (empty list)
|
l() <=> [] (empty list)
|
||||||
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]
|
||||||
l(1, 2) = l(3, 4) => Error: l is not a variable
|
[1, 2] = [3, 4] => Error: l is not a variable
|
||||||
l(foo, bar) = l(3,4); foo==3 && bar==4 => 1
|
[foo, bar] = [3, 4]; foo==3 && bar==4 => 1
|
||||||
l(foo, bar, baz) = l(2, 4, 6); l(min(foo, bar), baz) = l(3, 5); l(foo, bar, baz) => [3, 4, 5]
|
[foo, bar, baz] = [2, 4, 6]; [min(foo, bar), baz] = [3, 5]; [foo, bar, baz] => [3, 4, 5]
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
In the last example `l(min(foo, bar), baz)` creates a valid L-value, as min(foo, bar) finds the lower of the
|
In the last example `[min(foo, bar), baz]` creates a valid L-value, as `min(foo, bar)` finds the lower of the
|
||||||
variables (in this case `foo`) creating a valid assignable L-list of [foo, baz], and these values
|
variables (in this case `foo`) creating a valid assignable L-list of `[foo, baz]`, and these values
|
||||||
will be assigned new values
|
will be assigned new values
|
||||||
|
|
||||||
### `join(delim, list), join(delim, values ...)`
|
### `join(delim, list), join(delim, values ...)`
|
||||||
@@ -210,29 +215,37 @@ range(5,10) => [5, 6, 7, 8, 9]
|
|||||||
range(20, 10, -2) => [20, 18, 16, 14, 12]
|
range(20, 10, -2) => [20, 18, 16, 14, 12]
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### `element(list, index)(deprecated)`
|
|
||||||
|
|
||||||
Legacy support for older method that worked only on lists. Please use `get(...)` for equivalent support, or `.`
|
|
||||||
operator. Also previous unique behaviours with `put` on lists has been removed to support all type of
|
|
||||||
container types.
|
|
||||||
|
|
||||||
## Map operations
|
## Map operations
|
||||||
|
|
||||||
Scarpet supports map structures, aka hashmaps, dicts etc. Map structure can also be used, with `null` values as sets.
|
Scarpet supports map structures, aka hashmaps, dicts etc. Map structure can also be used, with `null` values as sets.
|
||||||
Apart from container access functions, (`. , get, put, has, delete`), the following functions:
|
Apart from container access functions, (`. , get, put, has, delete`), the following functions:
|
||||||
|
|
||||||
### `m(values ...), m(iterator), m(key_value_pairs)`
|
### `{values, ...}`,`{iterator}`,`{key -> value, ...}`,`m(values, ...)`, `m(iterator)`, `m(l(key, value), ...))`
|
||||||
|
|
||||||
creates and initializes a map with supplied keys, and values. If the arguments contains a flat list, these are all
|
creates and initializes a map with supplied keys, and values. If the arguments contains a flat list, these are all
|
||||||
treated as keys with no value, same goes with the iterator - creates a map that behaves like a set. If the
|
treated as keys with no value, same goes with the iterator - creates a map that behaves like a set. If the
|
||||||
arguments is a list of lists, they have to have two elements each, and then first is a key, and second, a value
|
arguments is a list of lists, they have to have two elements each, and then first is a key, and second, a value
|
||||||
|
|
||||||
|
In map creation context (directly inside `{}` or `m{}` call), `->` operator acts like a pair constructor for simpler
|
||||||
|
syntax providing key value pairs, so the invocation to `{foo -> bar, baz -> quux}` is equivalent to
|
||||||
|
`{l(foo, bar), l(baz, quux)}`, which is equivalent to somewhat older, but more traditional functional form of
|
||||||
|
`m(l(foo, bar),l(baz, quuz))`.
|
||||||
|
|
||||||
|
Internally, `{?}`(list syntax) and `m(?)`(function syntax) are equivalent. `{}` is simply translated to
|
||||||
|
`m()` in the scarpet preprocessing stage. This means that internally the code has always expression syntax despite `{}`
|
||||||
|
not using different kinds of brackets and not being proper operators. This means that `m(}` and `{)` are also valid
|
||||||
|
although not recommended as they will make your code far less readable.
|
||||||
|
|
||||||
|
When converting map value to string, `':'` is used as a key-value separator due to tentative compatibility with NBT
|
||||||
|
notation, meaning in simpler cases maps can be converted to NBT parsable string by calling `str()`. This however
|
||||||
|
does not guarantee a parsable output. To properly convert to NBT value, use `encode_nbt()`.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
m(1,2,'foo') => {1: null, 2: null, foo: null}
|
{1, 2, 'foo'} => {1: null, 2: null, foo: null}
|
||||||
m() => {} (empty map)
|
m() <=> {} (empty map)
|
||||||
m(range(10)) => {0: null, 1: null, 2: null, 3: null, 4: null, 5: null, 6: null, 7: null, 8: null, 9: null}
|
{range(10)} => {0: null, 1: null, 2: null, 3: null, 4: null, 5: null, 6: null, 7: null, 8: null, 9: null}
|
||||||
m(l(1, 2), l(3, 4)) => {1: 2, 3: 4}
|
m(l(1, 2), l(3, 4)) <=> {1 -> 2, 3 -> 4} => {1: 2, 3: 4}
|
||||||
reduce(range(10), put(_a, _, _*_); _a, m())
|
reduce(range(10), put(_a, _, _*_); _a, {})
|
||||||
=> {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
|
=> {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ a() -> global_list+=1; global_list = l(1,2,3); a(); a(); global_list // => [1,2
|
|||||||
|
|
||||||
### `Operator ->`
|
### `Operator ->`
|
||||||
|
|
||||||
|
`->` operator has two uses - as a function definition operator and key-value initializer for maps.
|
||||||
|
|
||||||
To organize code better than a flat sequence of operations, one can define functions. Definition is correct
|
To organize code better than a flat sequence of operations, one can define functions. Definition is correct
|
||||||
if has the following form
|
if has the following form
|
||||||
|
|
||||||
@@ -71,7 +73,17 @@ a(lst) -> lst+=1; list = l(1,2,3); a(list); a(list); list // => [1,2,3]
|
|||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
In case the inner function wants to operate and modify larger objects, lists from the outer scope, but not global,
|
In case the inner function wants to operate and modify larger objects, lists from the outer scope, but not global,
|
||||||
it needs to use `outer` function in function signature
|
it needs to use `outer` function in function signature.
|
||||||
|
|
||||||
|
in map construction context (directly in `m()` or `{}`), the `->` operator has a different function by converting its
|
||||||
|
arguments to a tuple which is used by map constructor as a key-value pair:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
{ 'foo' -> 'bar' } => {l('foo', 'bar')}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
This means that it is not possible to define literally a set of inline function, however a set of functions can still
|
||||||
|
be created by adding elements to an empty set, and building it this way.
|
||||||
|
|
||||||
### `outer(arg)`
|
### `outer(arg)`
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,9 @@
|
|||||||
# Fundamental components of `scarpet` programming language (towards version 1.7).
|
# Fundamental components of `scarpet` programming language.
|
||||||
|
|
||||||
Scarpet (a.k.a. Carpet Script, or Script for Carpet) is a programming language
|
Scarpet (a.k.a. Carpet Script, or Script for Carpet) is a programming language
|
||||||
designed to provide the ability to write custom programs to run within Minecraft
|
designed to provide the ability to write custom programs to run within Minecraft
|
||||||
and interact with the world.
|
and interact with the world.
|
||||||
|
|
||||||
The project was initially built based on the EvalEx project, however it now diverged
|
|
||||||
so far away from the original implementation, it would be hard to tell it without
|
|
||||||
this mention. EvalEx is a handy expression evaluator for Java, that allows to evaluate
|
|
||||||
simple mathematical and boolean expressions. For more information,
|
|
||||||
see: [EvalEx GitHub repository](https://github.com/uklimaschewski/EvalEx)
|
|
||||||
|
|
||||||
This specification is divided into two sections: this one is agnostic to any
|
This specification is divided into two sections: this one is agnostic to any
|
||||||
Minecraft related features and could function on its own, and CarpetExpression
|
Minecraft related features and could function on its own, and CarpetExpression
|
||||||
for Minecraft specific routines and world manipulation functions.
|
for Minecraft specific routines and world manipulation functions.
|
||||||
@@ -277,3 +271,22 @@ check_foo_not_zero();
|
|||||||
...
|
...
|
||||||
check_foo_not_zero() -> if(global_foo == 0, global_foo = 1)
|
check_foo_not_zero() -> if(global_foo == 0, global_foo = 1)
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## Scarpet preprocessor
|
||||||
|
|
||||||
|
There are several preprocessing operations applied to the source of your program to clean it up and prepare for
|
||||||
|
execution. Some of them will affect your code as it is reported via stack traces and function definition, and some
|
||||||
|
are applied only on the surface.
|
||||||
|
- stripping `//` comments (in file mode)
|
||||||
|
- replacing `$` with newlines (in command mode, modifies submitted code)
|
||||||
|
- removing extra semicolons that don't follow `;` use as a binary operator, allowing for lenient use of semicolons
|
||||||
|
- translating `{` into `m(`, `[` into `l(`, and `]` and `}` into `)`
|
||||||
|
|
||||||
|
No further optimizations are currently applied to your code.
|
||||||
|
|
||||||
|
## Mentions
|
||||||
|
|
||||||
|
LR1 parser, tokenizer, and several built-in functions are built based on the EvalEx project.
|
||||||
|
EvalEx is a handy expression evaluator for Java, that allows to evaluate
|
||||||
|
simple mathematical and boolean expressions. EvalEx is distributed under MIT licence.
|
||||||
|
For more information, see: [EvalEx GitHub repository](https://github.com/uklimaschewski/EvalEx)
|
||||||
@@ -239,10 +239,13 @@ Thou shall not sample from noise changing seed frequently. Scarpet will keep tra
|
|||||||
used for sampling providing similar speed comparing to the default seed of `0`. In case the app engine uses more
|
used for sampling providing similar speed comparing to the default seed of `0`. In case the app engine uses more
|
||||||
than 256 seeds at the same time, switching between them can get much more expensive.
|
than 256 seeds at the same time, switching between them can get much more expensive.
|
||||||
|
|
||||||
### `print(expr)`
|
### `print(expr)`, `print(player, expr)`
|
||||||
|
|
||||||
prints the value of the expression to chat. Passes the result of the argument to the output unchanged,
|
prints the value of the expression to chat. Passes the result of the argument to the output unchanged,
|
||||||
so `print`-statements can be weaved in code to debug programming issues
|
so `print`-statements can be weaved in code to debug programming issues. By default it uses the same communication
|
||||||
|
channels that most vanilla commands are using.
|
||||||
|
|
||||||
|
In case player is directly specified, it only sends the message to that player, like `tell` command.
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
print('foo') => results in foo, prints: foo
|
print('foo') => results in foo, prints: foo
|
||||||
@@ -275,6 +278,52 @@ flip_my_world_upside_down();
|
|||||||
print(str('this took %d milliseconds',time()-start_time))
|
print(str('this took %d milliseconds',time()-start_time))
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
### `unix_time()`
|
||||||
|
|
||||||
|
Returns standard POSIX time as a number of milliseconds since the start of the epoch
|
||||||
|
(00:00 am and 0 seconds, 1 Jan 1970).
|
||||||
|
Unlike the previous function, this can be used to get exact time, but it varies from time zone to time zone.
|
||||||
|
|
||||||
|
### `convert_date(milliseconds)`
|
||||||
|
### `convert_date(year, month, date, hours?, mins?, secs?)`
|
||||||
|
### `convert_date(l(year, month, date, hours?, mins?, secs?))`
|
||||||
|
|
||||||
|
If called with a single argument, converts standard POSIX time to a list in the format:
|
||||||
|
|
||||||
|
`l(year, month, date, hours, mins, secs, day_of_week, day_of_year, week_of_year)`
|
||||||
|
|
||||||
|
eg: `convert_date(1592401346960) -> [2020, 6, 17, 10, 42, 26, 3, 169, 25]`
|
||||||
|
|
||||||
|
Where the `6` stands for June, but `17` stands for 17th, `10` stands for 10am,
|
||||||
|
`42` stands for 42 minutes past the hour, and `26` stands for 26 seconds past the minute,
|
||||||
|
and `3` stands for Wednesday, `169` is the day of year, and `25` is a week of year.
|
||||||
|
|
||||||
|
Run `convert_date(unix_time())` to get current time as list.
|
||||||
|
|
||||||
|
|
||||||
|
When called with a list, or with 3 or 6 arguments, returns standard POSIX time as a number of milliseconds since the
|
||||||
|
start of the epoch (1 Jan 1970),
|
||||||
|
using the time inputted into the function as opposed to the system time.
|
||||||
|
|
||||||
|
Example editing:
|
||||||
|
<pre>
|
||||||
|
date = convert_date(unix_time());
|
||||||
|
|
||||||
|
months = l('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
|
||||||
|
|
||||||
|
days = l('Mon','Tue','Wed','Thu','Fri','Sat','Sun');
|
||||||
|
|
||||||
|
print(
|
||||||
|
str('Its %s, %d %s %d, %02d:%02d:%02d',
|
||||||
|
days:(date:6-1), date:2, months:(date:1-1), date:0, date:3, date:4, date:5
|
||||||
|
)
|
||||||
|
)
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
This will give you a date:
|
||||||
|
|
||||||
|
It is currently `hrs`:`mins` and `secs` seconds on the `date`th of `month`, `year`
|
||||||
|
|
||||||
### `profile_expr(expression)`
|
### `profile_expr(expression)`
|
||||||
|
|
||||||
Returns number of times given expression can be run in 50ms time. Useful to profile and optimize your code.
|
Returns number of times given expression can be run in 50ms time. Useful to profile and optimize your code.
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ block positions. All variables starting with `_` are read-only, and cannot be de
|
|||||||
|
|
||||||
`scarpet` accepts numeric and string liters constants. Numbers look like `1, 2.5, -3e-7, 0xff,` and are internally
|
`scarpet` accepts numeric and string liters constants. Numbers look like `1, 2.5, -3e-7, 0xff,` and are internally
|
||||||
represented as Java's `double` but `scarpet` will try to trim trailing zeros as much as possible so if you need to
|
represented as Java's `double` but `scarpet` will try to trim trailing zeros as much as possible so if you need to
|
||||||
use them as intergers, you can. Strings using single quoting, for multiple reasons, but primarily to allow for
|
use them as intergers, you can. Strings use single quoting, for multiple reasons, but primarily to allow for
|
||||||
easier use of strings inside doubly quoted command arguments (when passing a script as a parameter of `/script fill`
|
easier use of strings inside doubly quoted command arguments (when passing a script as a parameter of `/script fill`
|
||||||
for example, or when typing in jsons inside scarpet to feed back into a `/data merge` command for example.
|
for example), or when typing in jsons inside scarpet (to feed back into a `/data merge` command for example).
|
||||||
Strings also use backslashes `\` for quoting special characters, in both plain strings and regular expressions
|
Strings also use backslashes `\` for quoting special characters, in both plain strings and regular expressions
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ blocks_daylight
|
|||||||
blocks_movement
|
blocks_movement
|
||||||
brightness
|
brightness
|
||||||
chunk_tickets
|
chunk_tickets
|
||||||
|
crafting_remaining_item
|
||||||
create_marker
|
create_marker
|
||||||
current_dimension
|
current_dimension
|
||||||
day_time
|
day_time
|
||||||
@@ -67,6 +68,7 @@ power
|
|||||||
property
|
property
|
||||||
query
|
query
|
||||||
random_tick
|
random_tick
|
||||||
|
recipe_data
|
||||||
rect
|
rect
|
||||||
reload_chunk
|
reload_chunk
|
||||||
remove_all_markers
|
remove_all_markers
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ __on_player_attacks_entity
|
|||||||
__on_player_breaks_block
|
__on_player_breaks_block
|
||||||
__on_player_clicks_block
|
__on_player_clicks_block
|
||||||
__on_player_changes_dimension
|
__on_player_changes_dimension
|
||||||
|
__on_player_chooses_recipe
|
||||||
__on_player_deploys_elytra
|
__on_player_deploys_elytra
|
||||||
__on_player_drops_item
|
__on_player_drops_item
|
||||||
__on_player_drops_stack
|
__on_player_drops_stack
|
||||||
@@ -21,6 +22,7 @@ __on_player_starts_sneaking
|
|||||||
__on_player_starts_sprinting
|
__on_player_starts_sprinting
|
||||||
__on_player_stops_sneaking
|
__on_player_stops_sneaking
|
||||||
__on_player_stops_sprinting
|
__on_player_stops_sprinting
|
||||||
|
__on_player_switches_slot
|
||||||
__on_player_uses_item
|
__on_player_uses_item
|
||||||
__on_player_wakes_up
|
__on_player_wakes_up
|
||||||
__on_statistic
|
__on_statistic
|
||||||
|
|||||||
@@ -62,7 +62,9 @@
|
|||||||
|
|
||||||
player spawn entity_id entity_list entity_area entity_selector query modify entity_event
|
player spawn entity_id entity_list entity_area entity_selector query modify entity_event
|
||||||
|
|
||||||
stack_limit item_category inventory_size inventory_has_items inventory_get inventory_set inventory_find inventory_remove drop_item
|
stack_limit item_category recipe_data crafting_remaining_item
|
||||||
|
|
||||||
|
inventory_size inventory_has_items inventory_get inventory_set inventory_find inventory_remove drop_item
|
||||||
|
|
||||||
scan volume neighbours rect diamond
|
scan volume neighbours rect diamond
|
||||||
|
|
||||||
@@ -87,6 +89,7 @@
|
|||||||
__on_player_breaks_block
|
__on_player_breaks_block
|
||||||
__on_player_clicks_block
|
__on_player_clicks_block
|
||||||
__on_player_changes_dimension
|
__on_player_changes_dimension
|
||||||
|
__on_player_chooses_recipe
|
||||||
__on_player_deploys_elytra
|
__on_player_deploys_elytra
|
||||||
__on_player_drops_item
|
__on_player_drops_item
|
||||||
__on_player_drops_stack
|
__on_player_drops_stack
|
||||||
@@ -102,6 +105,7 @@
|
|||||||
__on_player_starts_sprinting
|
__on_player_starts_sprinting
|
||||||
__on_player_stops_sneaking
|
__on_player_stops_sneaking
|
||||||
__on_player_stops_sprinting
|
__on_player_stops_sprinting
|
||||||
|
__on_player_switches_slot
|
||||||
__on_player_uses_item
|
__on_player_uses_item
|
||||||
__on_player_wakes_up
|
__on_player_wakes_up
|
||||||
__on_statistic
|
__on_statistic
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
<NotepadPlus>
|
||||||
|
<UserLang name="scarpet" ext="sc scl" udlVersion="2.1">
|
||||||
|
<Settings>
|
||||||
|
<Global caseIgnored="yes" allowFoldOfComments="no" foldCompact="no" forcePureLC="0" decimalSeparator="0" />
|
||||||
|
<Prefix Keywords1="no" Keywords2="no" Keywords3="no" Keywords4="no" Keywords5="no" Keywords6="yes" Keywords7="no" Keywords8="no" />
|
||||||
|
</Settings>
|
||||||
|
<KeywordLists>
|
||||||
|
<Keywords name="Comments">00// 01 02 03 04</Keywords>
|
||||||
|
<Keywords name="Numbers, prefix1">0x</Keywords>
|
||||||
|
<Keywords name="Numbers, prefix2"></Keywords>
|
||||||
|
<Keywords name="Numbers, extras1"></Keywords>
|
||||||
|
<Keywords name="Numbers, extras2"></Keywords>
|
||||||
|
<Keywords name="Numbers, suffix1"></Keywords>
|
||||||
|
<Keywords name="Numbers, suffix2"></Keywords>
|
||||||
|
<Keywords name="Numbers, range"></Keywords>
|
||||||
|
<Keywords name="Operators1">; + - * / % ^ && || > >= < <= == != = += -> <> ! ~ , :</Keywords>
|
||||||
|
<Keywords name="Operators2"></Keywords>
|
||||||
|
<Keywords name="Folders in code1, open">(</Keywords>
|
||||||
|
<Keywords name="Folders in code1, middle"></Keywords>
|
||||||
|
<Keywords name="Folders in code1, close">)</Keywords>
|
||||||
|
<Keywords name="Folders in code2, open"></Keywords>
|
||||||
|
<Keywords name="Folders in code2, middle"></Keywords>
|
||||||
|
<Keywords name="Folders in code2, close"></Keywords>
|
||||||
|
<Keywords name="Folders in comment, open"></Keywords>
|
||||||
|
<Keywords name="Folders in comment, middle"></Keywords>
|
||||||
|
<Keywords name="Folders in comment, close"></Keywords>
|
||||||
|
<Keywords name="Keywords1">
|
||||||
|
not fact rand
|
||||||
|
sin cos tan asin acos atan atan2 sinh cosh tanh sec csc sech csch cot acot coth asinh acosh atanh
|
||||||
|
rad deg
|
||||||
|
log log10 log1p
|
||||||
|
sqrt
|
||||||
|
max min abs round floor ceil
|
||||||
|
mandelbrot relu
|
||||||
|
perlin simplex
|
||||||
|
|
||||||
|
print return exit throw try
|
||||||
|
|
||||||
|
task task_count task_value task_join task_completed synchronize task_dock
|
||||||
|
|
||||||
|
system_variable_get system_variable_set
|
||||||
|
|
||||||
|
l join split slice sort sort_key range m keys values pairs copy
|
||||||
|
|
||||||
|
get put has delete
|
||||||
|
|
||||||
|
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
|
||||||
|
</Keywords>
|
||||||
|
<Keywords name="Keywords2">
|
||||||
|
block pos pos_offset solid air liquid flammable
|
||||||
|
transparent opacity blocks_daylight emitted_light light block_light sky_light see_sky brightness
|
||||||
|
hardness blast_resistance top loaded suffocates power ticks_randomly update block_tick random_tick in_slime_chunk
|
||||||
|
set without_updates
|
||||||
|
blocks_movement block_sound material map_colour property block_properties block_data poi set_poi
|
||||||
|
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
|
||||||
|
add_chunk_ticket
|
||||||
|
|
||||||
|
scoreboard scoreboard_add scoreboard_remove scoreboard_display
|
||||||
|
|
||||||
|
player spawn entity_id entity_list entity_area entity_selector query modify entity_event
|
||||||
|
|
||||||
|
stack_limit item_category recipe_data crafting_remaining_item
|
||||||
|
|
||||||
|
inventory_size inventory_has_items inventory_get inventory_set inventory_find inventory_remove drop_item
|
||||||
|
|
||||||
|
scan volume neighbours rect diamond
|
||||||
|
|
||||||
|
format logger sound particle particle_line particle_rect
|
||||||
|
|
||||||
|
run save tick_time world_time day_time last_tick_times game_tick seed current_dimension in_dimension view_distance
|
||||||
|
|
||||||
|
plop harvest destroy schedule nbt draw_shape create_marker remove_all_markers
|
||||||
|
|
||||||
|
load_app_data store_app_data escape_nbt statistic
|
||||||
|
</Keywords>
|
||||||
|
<Keywords name="Keywords3">euler pi null true false</Keywords>
|
||||||
|
<Keywords name="Keywords4">x y z p</Keywords>
|
||||||
|
<Keywords name="Keywords5">_ _i _a _x _y _z</Keywords>
|
||||||
|
<Keywords name="Keywords6">global_</Keywords>
|
||||||
|
<Keywords name="Keywords7">
|
||||||
|
__command
|
||||||
|
__config
|
||||||
|
__on_chunk_generated
|
||||||
|
__on_close
|
||||||
|
__on_player_attacks_entity
|
||||||
|
__on_player_breaks_block
|
||||||
|
__on_player_clicks_block
|
||||||
|
__on_player_changes_dimension
|
||||||
|
__on_player_chooses_recipe
|
||||||
|
__on_player_deploys_elytra
|
||||||
|
__on_player_drops_item
|
||||||
|
__on_player_drops_stack
|
||||||
|
__on_player_finishes_using_item
|
||||||
|
__on_player_interacts_with_block
|
||||||
|
__on_player_interacts_with_entity
|
||||||
|
__on_player_jumps
|
||||||
|
__on_player_places_block
|
||||||
|
__on_player_releases_item
|
||||||
|
__on_player_rides
|
||||||
|
__on_player_right_clicks_block
|
||||||
|
__on_player_starts_sneaking
|
||||||
|
__on_player_starts_sprinting
|
||||||
|
__on_player_stops_sneaking
|
||||||
|
__on_player_stops_sprinting
|
||||||
|
__on_player_switches_slot
|
||||||
|
__on_player_uses_item
|
||||||
|
__on_player_wakes_up
|
||||||
|
__on_statistic
|
||||||
|
__on_tick
|
||||||
|
__on_tick_ender
|
||||||
|
__on_tick_nether
|
||||||
|
__on_player_takes_damage
|
||||||
|
__on_player_deals_damage
|
||||||
|
__on_player_dies
|
||||||
|
__on_player_respawns
|
||||||
|
__on_player_disconnects
|
||||||
|
__on_player_connects
|
||||||
|
</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>
|
||||||
|
</KeywordLists>
|
||||||
|
<Styles>
|
||||||
|
<WordsStyle name="DEFAULT" fgColor="000040" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="COMMENTS" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="LINE COMMENTS" fgColor="808080" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="NUMBERS" fgColor="008040" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="KEYWORDS1" fgColor="0000FF" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="KEYWORDS2" fgColor="800000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="KEYWORDS3" fgColor="0080C0" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="KEYWORDS4" fgColor="400080" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="KEYWORDS5" fgColor="FF0080" bgColor="FFFFFF" fontName="" fontStyle="1" nesting="0" />
|
||||||
|
<WordsStyle name="KEYWORDS6" fgColor="008000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="KEYWORDS7" fgColor="FF8040" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="KEYWORDS8" fgColor="DEDEDE" bgColor="FFFFFF" fontName="" fontStyle="1" nesting="0" />
|
||||||
|
<WordsStyle name="OPERATORS" fgColor="804040" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="FOLDER IN CODE1" fgColor="515151" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="FOLDER IN CODE2" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="FOLDER IN COMMENT" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="DELIMITERS1" fgColor="0080C0" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="DELIMITERS2" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="DELIMITERS3" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="DELIMITERS4" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="DELIMITERS5" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="DELIMITERS6" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="DELIMITERS7" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
<WordsStyle name="DELIMITERS8" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||||
|
</Styles>
|
||||||
|
</UserLang>
|
||||||
|
</NotepadPlus>
|
||||||
+2
-2
@@ -6,10 +6,10 @@ org.gradle.jvmargs=-Xmx1G
|
|||||||
# or better: https://modmuss50.me/fabric.html
|
# or better: https://modmuss50.me/fabric.html
|
||||||
minecraft_version=1.15.2
|
minecraft_version=1.15.2
|
||||||
yarn_mappings=1.15.2+build.17
|
yarn_mappings=1.15.2+build.17
|
||||||
loader_version=0.8.7+build.201
|
loader_version=0.8.8+build.202
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version = 1.3.29+v200611
|
mod_version = 1.4.0+v200623
|
||||||
maven_group = carpet
|
maven_group = carpet
|
||||||
archives_base_name = fabric-carpet
|
archives_base_name = fabric-carpet
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,18 @@ public interface CarpetExtension
|
|||||||
default void onGameStarted() {}
|
default void onGameStarted() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs once per loaded world once the World files / gamerules etc are fully loaded
|
* Runs once per loaded world once the server / gamerules etc are fully loaded
|
||||||
|
* but before worlds are loaded
|
||||||
* Can be loaded multiple times in SinglePlayer
|
* Can be loaded multiple times in SinglePlayer
|
||||||
*/
|
*/
|
||||||
default void onServerLoaded(MinecraftServer server) {}
|
default void onServerLoaded(MinecraftServer server) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs once per loaded world once the World files are fully loaded
|
||||||
|
* Can be loaded multiple times in SinglePlayer
|
||||||
|
*/
|
||||||
|
default void onServerLoadedWorlds(MinecraftServer server) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs once per game tick, as a first thing in the tick
|
* Runs once per game tick, as a first thing in the tick
|
||||||
*/
|
*/
|
||||||
@@ -55,4 +62,6 @@ public interface CarpetExtension
|
|||||||
default void registerLoggers() {}
|
default void registerLoggers() {}
|
||||||
|
|
||||||
default Map<String, String> canHasTranslations(String lang) { return null;}
|
default Map<String, String> canHasTranslations(String lang) { return null;}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -58,12 +58,17 @@ public class CarpetServer // static for now - easier to handle all around the co
|
|||||||
e.onServerLoaded(server);
|
e.onServerLoaded(server);
|
||||||
});
|
});
|
||||||
scriptServer = new CarpetScriptServer(server);
|
scriptServer = new CarpetScriptServer(server);
|
||||||
scriptServer.loadAllWorldScripts();
|
|
||||||
MobAI.resetTrackers();
|
MobAI.resetTrackers();
|
||||||
LoggerRegistry.initLoggers();
|
LoggerRegistry.initLoggers();
|
||||||
//TickSpeed.reset();
|
//TickSpeed.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void onServerLoadedWorlds(MinecraftServer minecraftServer)
|
||||||
|
{
|
||||||
|
extensions.forEach(e -> e.onServerLoadedWorlds(minecraftServer));
|
||||||
|
scriptServer.loadAllWorldScripts();
|
||||||
|
}
|
||||||
|
|
||||||
public static void tick(MinecraftServer server)
|
public static void tick(MinecraftServer server)
|
||||||
{
|
{
|
||||||
TickSpeed.tick(server);
|
TickSpeed.tick(server);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import static carpet.settings.RuleCategory.CLIENT;
|
|||||||
@SuppressWarnings("CanBeFinal")
|
@SuppressWarnings("CanBeFinal")
|
||||||
public class CarpetSettings
|
public class CarpetSettings
|
||||||
{
|
{
|
||||||
public static final String carpetVersion = "1.3.29+v200611";
|
public static final String carpetVersion = "1.4.0+v200623";
|
||||||
public static final Logger LOG = LogManager.getLogger();
|
public static final Logger LOG = LogManager.getLogger();
|
||||||
public static boolean skipGenerationChecks = false;
|
public static boolean skipGenerationChecks = false;
|
||||||
public static boolean impendingFillSkipUpdates = false;
|
public static boolean impendingFillSkipUpdates = false;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package carpet.fakes;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface IngredientInterface
|
||||||
|
{
|
||||||
|
List<Collection<ItemStack>> getRecipeStacks();
|
||||||
|
}
|
||||||
@@ -10,4 +10,6 @@ public interface MobEntityInterface
|
|||||||
GoalSelector getAI(boolean target);
|
GoalSelector getAI(boolean target);
|
||||||
|
|
||||||
Map<String, Goal> getTemporaryTasks();
|
Map<String, Goal> getTemporaryTasks();
|
||||||
|
|
||||||
|
void setPersistence(boolean what);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package carpet.fakes;
|
||||||
|
|
||||||
|
import net.minecraft.recipe.Recipe;
|
||||||
|
import net.minecraft.recipe.RecipeType;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface RecipeManagerInterface
|
||||||
|
{
|
||||||
|
List<Recipe<?>> getAllMatching(RecipeType type, Identifier output);
|
||||||
|
}
|
||||||
@@ -14,6 +14,11 @@ public class ClientPlayerEntity_clientCommandMixin
|
|||||||
@Inject(method = "sendChatMessage", at = @At("HEAD"))
|
@Inject(method = "sendChatMessage", at = @At("HEAD"))
|
||||||
private void inspectMessage(String string, CallbackInfo ci)
|
private void inspectMessage(String string, CallbackInfo ci)
|
||||||
{
|
{
|
||||||
|
if (string.startsWith("/call"))
|
||||||
|
{
|
||||||
|
String command = string.substring(6);
|
||||||
|
CarpetClient.sendClientCommand(command);
|
||||||
|
}
|
||||||
if (CarpetServer.minecraft_server == null && !CarpetClient.isCarpet())
|
if (CarpetServer.minecraft_server == null && !CarpetClient.isCarpet())
|
||||||
{
|
{
|
||||||
ClientPlayerEntity playerSource = (ClientPlayerEntity)(Object) this;
|
ClientPlayerEntity playerSource = (ClientPlayerEntity)(Object) this;
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package carpet.mixins;
|
||||||
|
|
||||||
|
import carpet.fakes.IngredientInterface;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.recipe.Ingredient;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Mixin(Ingredient.class)
|
||||||
|
public class Ingredient_scarpetMixin implements IngredientInterface
|
||||||
|
{
|
||||||
|
@Shadow @Final private Ingredient.Entry[] entries;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Collection<ItemStack>> getRecipeStacks()
|
||||||
|
{
|
||||||
|
List<Collection<ItemStack>> res = Arrays.stream(entries).map(Ingredient.Entry::getStacks).collect(Collectors.toList());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,13 +31,18 @@ public abstract class MinecraftServer_coreMixin
|
|||||||
CarpetServer.tick((MinecraftServer) (Object) this);
|
CarpetServer.tick((MinecraftServer) (Object) this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dedicated server only
|
|
||||||
@Inject(method = "loadWorld", at = @At("HEAD"))
|
@Inject(method = "loadWorld", at = @At("HEAD"))
|
||||||
private void serverLoaded(String string_1, String string_2, long long_1, LevelGeneratorType levelGeneratorType_1, JsonElement jsonElement_1, CallbackInfo ci)
|
private void serverLoaded(String string_1, String string_2, long long_1, LevelGeneratorType levelGeneratorType_1, JsonElement jsonElement_1, CallbackInfo ci)
|
||||||
{
|
{
|
||||||
CarpetServer.onServerLoaded((MinecraftServer) (Object) this);
|
CarpetServer.onServerLoaded((MinecraftServer) (Object) this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject(method = "loadWorld", at = @At("RETURN"))
|
||||||
|
private void serverLoadedWorlds(String string_1, String string_2, long long_1, LevelGeneratorType levelGeneratorType_1, JsonElement jsonElement_1, CallbackInfo ci)
|
||||||
|
{
|
||||||
|
CarpetServer.onServerLoadedWorlds((MinecraftServer) (Object) this);
|
||||||
|
}
|
||||||
|
|
||||||
@Inject(method = "shutdown", at = @At("HEAD"))
|
@Inject(method = "shutdown", at = @At("HEAD"))
|
||||||
private void serverClosed(CallbackInfo ci)
|
private void serverClosed(CallbackInfo ci)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public abstract class MinecraftServer_scarpetMixin extends ReentrantThreadExecut
|
|||||||
{
|
{
|
||||||
timeReference = field_4557 = Util.getMeasuringTimeMs();
|
timeReference = field_4557 = Util.getMeasuringTimeMs();
|
||||||
tick(isAhead);
|
tick(isAhead);
|
||||||
runTask();
|
while(runTask()) {Thread.yield();}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "tick", at = @At(
|
@Inject(method = "tick", at = @At(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import carpet.helpers.TickSpeed;
|
|||||||
import carpet.utils.CarpetProfiler;
|
import carpet.utils.CarpetProfiler;
|
||||||
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.util.Util;
|
import net.minecraft.util.Util;
|
||||||
import net.minecraft.util.profiler.DisableableProfiler;
|
import net.minecraft.util.profiler.DisableableProfiler;
|
||||||
import net.minecraft.util.thread.ReentrantThreadExecutor;
|
import net.minecraft.util.thread.ReentrantThreadExecutor;
|
||||||
@@ -50,6 +51,8 @@ public abstract class MinecraftServer_tickspeedMixin extends ReentrantThreadExec
|
|||||||
|
|
||||||
@Shadow private volatile boolean loading;
|
@Shadow private volatile boolean loading;
|
||||||
|
|
||||||
|
@Shadow public abstract Iterable<ServerWorld> getWorlds();
|
||||||
|
|
||||||
CarpetProfiler.ProfilerToken currentSection;
|
CarpetProfiler.ProfilerToken currentSection;
|
||||||
|
|
||||||
private float carpetMsptAccum = 0.0f;
|
private float carpetMsptAccum = 0.0f;
|
||||||
@@ -120,16 +123,16 @@ public abstract class MinecraftServer_tickspeedMixin extends ReentrantThreadExec
|
|||||||
|
|
||||||
this.profiler.startTick();
|
this.profiler.startTick();
|
||||||
this.profiler.push("tick");
|
this.profiler.push("tick");
|
||||||
this.tick(TickSpeed.time_warp_start_time != 0 ? ()->!hasRunningTasks() : this::shouldKeepTicking);
|
this.tick(TickSpeed.time_warp_start_time != 0 ? ()->true : this::shouldKeepTicking);
|
||||||
this.profiler.swap("nextTickWait");
|
this.profiler.swap("nextTickWait");
|
||||||
|
if (TickSpeed.time_warp_start_time != 0) // clearing all hanging tasks no matter what when warping
|
||||||
|
{
|
||||||
|
while(this.runEveryTask()) {Thread.yield();}
|
||||||
|
}
|
||||||
this.field_19249 = true;
|
this.field_19249 = true;
|
||||||
this.field_19248 = Math.max(Util.getMeasuringTimeMs() + /*50L*/ msThisTick, this.timeReference);
|
this.field_19248 = Math.max(Util.getMeasuringTimeMs() + /*50L*/ msThisTick, this.timeReference);
|
||||||
if (TickSpeed.time_warp_start_time != 0)
|
// run all tasks (this will not do a lot when warping), but that's fine since we already run them
|
||||||
{
|
this.method_16208();
|
||||||
runTasks();
|
|
||||||
runTasks(() -> !hasRunningTasks() );
|
|
||||||
}
|
|
||||||
else { this.method_16208(); }
|
|
||||||
this.profiler.pop();
|
this.profiler.pop();
|
||||||
this.profiler.endTick();
|
this.profiler.endTick();
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
@@ -137,6 +140,23 @@ public abstract class MinecraftServer_tickspeedMixin extends ReentrantThreadExec
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean runEveryTask() {
|
||||||
|
if (super.runTask()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (true) { // unconditionally this time
|
||||||
|
for(ServerWorld serverlevel : getWorlds()) {
|
||||||
|
if (serverlevel.getChunkManager().executeQueuedTasks()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Inject(method = "tick", at = @At(
|
@Inject(method = "tick", at = @At(
|
||||||
value = "INVOKE",
|
value = "INVOKE",
|
||||||
target = "Lnet/minecraft/server/PlayerManager;saveAllPlayerData()V",
|
target = "Lnet/minecraft/server/PlayerManager;saveAllPlayerData()V",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ public abstract class MobEntityMixin implements MobEntityInterface
|
|||||||
{
|
{
|
||||||
@Shadow @Final protected GoalSelector targetSelector;
|
@Shadow @Final protected GoalSelector targetSelector;
|
||||||
@Shadow @Final protected GoalSelector goalSelector;
|
@Shadow @Final protected GoalSelector goalSelector;
|
||||||
|
@Shadow private boolean persistent;
|
||||||
public final Map<String, Goal> temporaryTasks = new HashMap<>();
|
public final Map<String, Goal> temporaryTasks = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -30,5 +31,9 @@ public abstract class MobEntityMixin implements MobEntityInterface
|
|||||||
return temporaryTasks;
|
return temporaryTasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPersistence(boolean what)
|
||||||
|
{
|
||||||
|
persistent = what;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package carpet.mixins;
|
||||||
|
|
||||||
|
import carpet.fakes.RecipeManagerInterface;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import net.minecraft.recipe.Recipe;
|
||||||
|
import net.minecraft.recipe.RecipeManager;
|
||||||
|
import net.minecraft.recipe.RecipeType;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.registry.Registry;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Mixin(RecipeManager.class)
|
||||||
|
public class RecipeManager_scarpetMixin implements RecipeManagerInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
@Shadow private Map<RecipeType<?>, Map<Identifier, Recipe<?>>> recipes;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Recipe<?>> getAllMatching(RecipeType type, Identifier output)
|
||||||
|
{
|
||||||
|
Map<Identifier, Recipe<?>> typeRecipes = recipes.get(type);
|
||||||
|
if (typeRecipes.containsKey(output)) return Collections.singletonList(typeRecipes.get(output));
|
||||||
|
return Lists.newArrayList(typeRecipes.values().stream().filter(
|
||||||
|
r -> Registry.ITEM.getId(r.getOutput().getItem()).equals(output)).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
package carpet.mixins;
|
package carpet.mixins;
|
||||||
|
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.network.packet.c2s.play.ButtonClickC2SPacket;
|
||||||
|
import net.minecraft.network.packet.c2s.play.CraftRequestC2SPacket;
|
||||||
|
import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket;
|
||||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket;
|
import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket;
|
||||||
@@ -29,10 +32,12 @@ import static carpet.script.CarpetEventServer.Event.PLAYER_RELEASED_ITEM;
|
|||||||
import static carpet.script.CarpetEventServer.Event.PLAYER_RIDES;
|
import static carpet.script.CarpetEventServer.Event.PLAYER_RIDES;
|
||||||
import static carpet.script.CarpetEventServer.Event.PLAYER_JUMPS;
|
import static carpet.script.CarpetEventServer.Event.PLAYER_JUMPS;
|
||||||
import static carpet.script.CarpetEventServer.Event.PLAYER_RIGHT_CLICKS_BLOCK;
|
import static carpet.script.CarpetEventServer.Event.PLAYER_RIGHT_CLICKS_BLOCK;
|
||||||
|
import static carpet.script.CarpetEventServer.Event.PLAYER_CHOOSES_RECIPE;
|
||||||
import static carpet.script.CarpetEventServer.Event.PLAYER_STARTS_SNEAKING;
|
import static carpet.script.CarpetEventServer.Event.PLAYER_STARTS_SNEAKING;
|
||||||
import static carpet.script.CarpetEventServer.Event.PLAYER_STARTS_SPRINTING;
|
import static carpet.script.CarpetEventServer.Event.PLAYER_STARTS_SPRINTING;
|
||||||
import static carpet.script.CarpetEventServer.Event.PLAYER_STOPS_SNEAKING;
|
import static carpet.script.CarpetEventServer.Event.PLAYER_STOPS_SNEAKING;
|
||||||
import static carpet.script.CarpetEventServer.Event.PLAYER_STOPS_SPRINTING;
|
import static carpet.script.CarpetEventServer.Event.PLAYER_STOPS_SPRINTING;
|
||||||
|
import static carpet.script.CarpetEventServer.Event.PLAYER_SWITCHES_SLOT;
|
||||||
import static carpet.script.CarpetEventServer.Event.PLAYER_USES_ITEM;
|
import static carpet.script.CarpetEventServer.Event.PLAYER_USES_ITEM;
|
||||||
import static carpet.script.CarpetEventServer.Event.PLAYER_WAKES_UP;
|
import static carpet.script.CarpetEventServer.Event.PLAYER_WAKES_UP;
|
||||||
|
|
||||||
@@ -219,4 +224,28 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin
|
|||||||
//todo add hit and hand in the future
|
//todo add hit and hand in the future
|
||||||
PLAYER_ATTACKS_ENTITY.onEntityAction(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), null);
|
PLAYER_ATTACKS_ENTITY.onEntityAction(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject(method = "onButtonClick", at = @At("HEAD"))
|
||||||
|
private void onItemBeingPickedFromInventory(ButtonClickC2SPacket packet, CallbackInfo ci)
|
||||||
|
{
|
||||||
|
// crafts not int the crafting window
|
||||||
|
//CarpetSettings.LOG.error("Player clicks button "+packet.getButtonId());
|
||||||
|
}
|
||||||
|
@Inject(method = "onCraftRequest", at = @At("HEAD"))
|
||||||
|
private void onRecipeSelectedInRecipeManager(CraftRequestC2SPacket packet, CallbackInfo ci)
|
||||||
|
{
|
||||||
|
if (PLAYER_CHOOSES_RECIPE.isNeeded())
|
||||||
|
{
|
||||||
|
PLAYER_CHOOSES_RECIPE.onRecipeSelected(player, packet.getRecipe(), packet.shouldCraftAll());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "onUpdateSelectedSlot", at = @At("HEAD"))
|
||||||
|
private void onUpdatedSelectedSLot(UpdateSelectedSlotC2SPacket packet, CallbackInfo ci)
|
||||||
|
{
|
||||||
|
if (PLAYER_SWITCHES_SLOT.isNeeded())
|
||||||
|
{
|
||||||
|
PLAYER_SWITCHES_SLOT.onSlotSwitch(player, player.inventory.selectedSlot, packet.getSelectedSlot());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
package carpet.network;
|
package carpet.network;
|
||||||
|
|
||||||
import carpet.CarpetServer;
|
import carpet.CarpetServer;
|
||||||
|
import carpet.CarpetSettings;
|
||||||
import carpet.script.utils.ShapesRenderer;
|
import carpet.script.utils.ShapesRenderer;
|
||||||
import net.minecraft.client.network.ClientPlayerEntity;
|
import net.minecraft.client.network.ClientPlayerEntity;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
public class CarpetClient
|
public class CarpetClient
|
||||||
@@ -51,4 +56,26 @@ public class CarpetClient
|
|||||||
{
|
{
|
||||||
return isServerCarpet;
|
return isServerCarpet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean sendClientCommand(String command)
|
||||||
|
{
|
||||||
|
if (!isServerCarpet && CarpetServer.minecraft_server == null) return false;
|
||||||
|
ClientNetworkHandler.clientCommand(command);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onClientCommand(Tag t)
|
||||||
|
{
|
||||||
|
CarpetSettings.LOG.info("Server Response:");
|
||||||
|
CompoundTag tag = (CompoundTag)t;
|
||||||
|
CarpetSettings.LOG.info(" - id: "+tag.getString("id"));
|
||||||
|
CarpetSettings.LOG.info(" - code: "+tag.getInt("code"));
|
||||||
|
if (tag.contains("error")) CarpetSettings.LOG.warn(" - error: "+tag.getString("error"));
|
||||||
|
if (tag.contains("output"))
|
||||||
|
{
|
||||||
|
ListTag outputTag = (ListTag) tag.get("output");
|
||||||
|
for (int i = 0; i < outputTag.size(); i++)
|
||||||
|
CarpetSettings.LOG.info(" - response: " + Text.Serializer.fromJson(outputTag.getString(i)).getString());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -44,6 +44,9 @@ public class ClientNetworkHandler
|
|||||||
if (CarpetClient.shapes != null)
|
if (CarpetClient.shapes != null)
|
||||||
CarpetClient.shapes.addShapes((ListTag) t);
|
CarpetClient.shapes.addShapes((ListTag) t);
|
||||||
});
|
});
|
||||||
|
put("clientCommand", (p, t) -> {
|
||||||
|
CarpetClient.onClientCommand(t);
|
||||||
|
});
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
|
||||||
@@ -99,4 +102,17 @@ public class ClientNetworkHandler
|
|||||||
CarpetSettings.LOG.error("Unknown carpet data: "+key);
|
CarpetSettings.LOG.error("Unknown carpet data: "+key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void clientCommand(String command)
|
||||||
|
{
|
||||||
|
CompoundTag tag = new CompoundTag();
|
||||||
|
tag.putString("id", command);
|
||||||
|
tag.putString("command", command);
|
||||||
|
CompoundTag outer = new CompoundTag();
|
||||||
|
outer.put("clientCommand", tag);
|
||||||
|
CarpetClient.getPlayer().networkHandler.sendPacket(new CustomPayloadC2SPacket(
|
||||||
|
CarpetClient.CARPET_CHANNEL,
|
||||||
|
(new PacketByteBuf(Unpooled.buffer())).writeVarInt(CarpetClient.DATA).writeCompoundTag(outer)
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,21 +6,35 @@ import carpet.helpers.TickSpeed;
|
|||||||
import carpet.settings.ParsedRule;
|
import carpet.settings.ParsedRule;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.nbt.StringTag;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
|
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
|
||||||
|
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.text.Text;
|
||||||
import net.minecraft.util.PacketByteBuf;
|
import net.minecraft.util.PacketByteBuf;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
public class ServerNetworkHandler
|
public class ServerNetworkHandler
|
||||||
{
|
{
|
||||||
private static Map<ServerPlayerEntity, String> remoteCarpetPlayers = new HashMap<>();
|
private static Map<ServerPlayerEntity, String> remoteCarpetPlayers = new HashMap<>();
|
||||||
private static Set<ServerPlayerEntity> validCarpetPlayers = new HashSet<>();
|
private static Set<ServerPlayerEntity> validCarpetPlayers = new HashSet<>();
|
||||||
|
|
||||||
|
private static Map<String, BiConsumer<ServerPlayerEntity, Tag>> dataHandlers = new HashMap<String, BiConsumer<ServerPlayerEntity, Tag>>(){{
|
||||||
|
put("clientCommand", (p, t) -> {
|
||||||
|
handleClientCommand(p, (CompoundTag)t);
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
|
||||||
public static void handleData(PacketByteBuf data, ServerPlayerEntity player)
|
public static void handleData(PacketByteBuf data, ServerPlayerEntity player)
|
||||||
{
|
{
|
||||||
if (data != null)
|
if (data != null)
|
||||||
@@ -28,8 +42,13 @@ public class ServerNetworkHandler
|
|||||||
int id = data.readVarInt();
|
int id = data.readVarInt();
|
||||||
if (id == CarpetClient.HELLO)
|
if (id == CarpetClient.HELLO)
|
||||||
onHello(player, data);
|
onHello(player, data);
|
||||||
|
if (id == CarpetClient.DATA)
|
||||||
|
onClientData(player, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static void onPlayerJoin(ServerPlayerEntity playerEntity)
|
public static void onPlayerJoin(ServerPlayerEntity playerEntity)
|
||||||
{
|
{
|
||||||
if (!playerEntity.networkHandler.connection.isLocal())
|
if (!playerEntity.networkHandler.connection.isLocal())
|
||||||
@@ -60,6 +79,67 @@ public class ServerNetworkHandler
|
|||||||
CarpetServer.settingsManager.getRules().forEach(data::withRule);
|
CarpetServer.settingsManager.getRules().forEach(data::withRule);
|
||||||
playerEntity.networkHandler.sendPacket(new CustomPayloadS2CPacket(CarpetClient.CARPET_CHANNEL, data.build() ));
|
playerEntity.networkHandler.sendPacket(new CustomPayloadS2CPacket(CarpetClient.CARPET_CHANNEL, data.build() ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void handleClientCommand(ServerPlayerEntity player, CompoundTag commandData)
|
||||||
|
{
|
||||||
|
String command = commandData.getString("command");
|
||||||
|
String id = commandData.getString("id");
|
||||||
|
List<Text> output = new ArrayList<>();
|
||||||
|
String[] error = {null};
|
||||||
|
int resultCode = -1;
|
||||||
|
if (player.getServer() == null)
|
||||||
|
{
|
||||||
|
error[0] = "No Server";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resultCode = player.getServer().getCommandManager().execute(
|
||||||
|
new ServerCommandSource(player, player.getPos(), player.getRotationClient(),
|
||||||
|
player.world instanceof ServerWorld ? (ServerWorld) player.world : null,
|
||||||
|
player.server.getPermissionLevel(player.getGameProfile()), player.getName().getString(), player.getDisplayName(),
|
||||||
|
player.world.getServer(), player)
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void sendError(Text message)
|
||||||
|
{
|
||||||
|
error[0] = message.getString();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void sendFeedback(Text message, boolean broadcastToOps)
|
||||||
|
{
|
||||||
|
output.add(message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
command
|
||||||
|
);
|
||||||
|
};
|
||||||
|
CompoundTag result = new CompoundTag();
|
||||||
|
result.putString("id", id);
|
||||||
|
result.putInt("code", resultCode);
|
||||||
|
if (error[0] != null) result.putString("error", error[0]);
|
||||||
|
ListTag outputResult = new ListTag();
|
||||||
|
for (Text line: output) outputResult.add(StringTag.of(Text.Serializer.toJson(line)));
|
||||||
|
if (!output.isEmpty()) result.put("output", outputResult);
|
||||||
|
player.networkHandler.sendPacket(new CustomPayloadS2CPacket(
|
||||||
|
CarpetClient.CARPET_CHANNEL,
|
||||||
|
DataBuilder.create().withCustomNbt("clientCommand", result).build()
|
||||||
|
));
|
||||||
|
// run command plug to command output,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void onClientData(ServerPlayerEntity player, PacketByteBuf data)
|
||||||
|
{
|
||||||
|
CompoundTag compound = data.readCompoundTag();
|
||||||
|
if (compound == null) return;
|
||||||
|
for (String key: compound.getKeys())
|
||||||
|
{
|
||||||
|
if (dataHandlers.containsKey(key))
|
||||||
|
dataHandlers.get(key).accept(player, compound.get(key));
|
||||||
|
else
|
||||||
|
CarpetSettings.LOG.warn("Unknown carpet client data: "+key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void updateRuleWithConnectedClients(ParsedRule<?> rule)
|
public static void updateRuleWithConnectedClients(ParsedRule<?> rule)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -488,6 +488,37 @@ public class CarpetEventServer
|
|||||||
handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource);
|
handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
PLAYER_CHOOSES_RECIPE("player_chooses_recipe", 3, false)
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onRecipeSelected(ServerPlayerEntity player, Identifier recipe, boolean fullStack)
|
||||||
|
{
|
||||||
|
handler.call( () ->
|
||||||
|
{
|
||||||
|
return Arrays.asList(
|
||||||
|
((c, t) -> new EntityValue(player)),
|
||||||
|
((c, t) -> new StringValue(NBTSerializableValue.nameFromRegistryId(recipe))),
|
||||||
|
((c, t) -> new NumericValue(fullStack))
|
||||||
|
);
|
||||||
|
}, player::getCommandSource);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PLAYER_SWITCHES_SLOT("player_switches_slot", 3, false)
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onSlotSwitch(ServerPlayerEntity player, int from, int to)
|
||||||
|
{
|
||||||
|
if (from == to) return; // initial slot update
|
||||||
|
handler.call( () ->
|
||||||
|
{
|
||||||
|
return Arrays.asList(
|
||||||
|
((c, t) -> new EntityValue(player)),
|
||||||
|
((c, t) -> new NumericValue(from)),
|
||||||
|
((c, t) -> new NumericValue(to))
|
||||||
|
);
|
||||||
|
}, player::getCommandSource);
|
||||||
|
}
|
||||||
|
},
|
||||||
PLAYER_TAKES_DAMAGE("player_takes_damage", 4, false)
|
PLAYER_TAKES_DAMAGE("player_takes_damage", 4, false)
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
@@ -647,6 +678,8 @@ public class CarpetEventServer
|
|||||||
public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand) { }
|
public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand) { }
|
||||||
public void onDimensionChange(ServerPlayerEntity player, Vec3d from, Vec3d to, DimensionType fromDim, DimensionType dimTo) {}
|
public void onDimensionChange(ServerPlayerEntity player, Vec3d from, Vec3d to, DimensionType fromDim, DimensionType dimTo) {}
|
||||||
public void onDamage(Entity target, float amount, DamageSource source) { }
|
public void onDamage(Entity target, float amount, DamageSource source) { }
|
||||||
|
public void onRecipeSelected(ServerPlayerEntity player, Identifier recipe, boolean fullStack) {}
|
||||||
|
public void onSlotSwitch(ServerPlayerEntity player, int from, int to) {}
|
||||||
|
|
||||||
|
|
||||||
public void onWorldEvent(ServerWorld world, BlockPos pos) { }
|
public void onWorldEvent(ServerWorld world, BlockPos pos) { }
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package carpet.script;
|
|||||||
|
|
||||||
import carpet.CarpetServer;
|
import carpet.CarpetServer;
|
||||||
import carpet.fakes.ChunkTicketManagerInterface;
|
import carpet.fakes.ChunkTicketManagerInterface;
|
||||||
|
import carpet.fakes.IngredientInterface;
|
||||||
import carpet.fakes.MinecraftServerInterface;
|
import carpet.fakes.MinecraftServerInterface;
|
||||||
import carpet.fakes.BiomeArrayInterface;
|
import carpet.fakes.BiomeArrayInterface;
|
||||||
|
import carpet.fakes.RecipeManagerInterface;
|
||||||
import carpet.fakes.ServerChunkManagerInterface;
|
import carpet.fakes.ServerChunkManagerInterface;
|
||||||
import carpet.fakes.ServerLightingProviderInterface;
|
import carpet.fakes.ServerLightingProviderInterface;
|
||||||
import carpet.fakes.StatTypeInterface;
|
import carpet.fakes.StatTypeInterface;
|
||||||
@@ -81,6 +83,13 @@ import net.minecraft.nbt.StringTag;
|
|||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.particle.ParticleEffect;
|
import net.minecraft.particle.ParticleEffect;
|
||||||
import net.minecraft.predicate.entity.EntityPredicates;
|
import net.minecraft.predicate.entity.EntityPredicates;
|
||||||
|
import net.minecraft.recipe.AbstractCookingRecipe;
|
||||||
|
import net.minecraft.recipe.CuttingRecipe;
|
||||||
|
import net.minecraft.recipe.Recipe;
|
||||||
|
import net.minecraft.recipe.RecipeType;
|
||||||
|
import net.minecraft.recipe.ShapedRecipe;
|
||||||
|
import net.minecraft.recipe.ShapelessRecipe;
|
||||||
|
import net.minecraft.recipe.SpecialCraftingRecipe;
|
||||||
import net.minecraft.scoreboard.Scoreboard;
|
import net.minecraft.scoreboard.Scoreboard;
|
||||||
import net.minecraft.scoreboard.ScoreboardCriterion;
|
import net.minecraft.scoreboard.ScoreboardCriterion;
|
||||||
import net.minecraft.scoreboard.ScoreboardObjective;
|
import net.minecraft.scoreboard.ScoreboardObjective;
|
||||||
@@ -97,6 +106,7 @@ import net.minecraft.stat.Stat;
|
|||||||
import net.minecraft.stat.StatType;
|
import net.minecraft.stat.StatType;
|
||||||
import net.minecraft.state.StateManager;
|
import net.minecraft.state.StateManager;
|
||||||
import net.minecraft.state.property.Property;
|
import net.minecraft.state.property.Property;
|
||||||
|
import net.minecraft.structure.StructureFeatures;
|
||||||
import net.minecraft.structure.StructurePiece;
|
import net.minecraft.structure.StructurePiece;
|
||||||
import net.minecraft.structure.StructureStart;
|
import net.minecraft.structure.StructureStart;
|
||||||
import net.minecraft.text.LiteralText;
|
import net.minecraft.text.LiteralText;
|
||||||
@@ -111,9 +121,11 @@ import net.minecraft.util.math.ChunkPos;
|
|||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.util.math.EulerAngle;
|
import net.minecraft.util.math.EulerAngle;
|
||||||
import net.minecraft.util.math.BlockBox;
|
import net.minecraft.util.math.BlockBox;
|
||||||
|
import net.minecraft.util.math.MathHelper;
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.util.math.Vec3i;
|
import net.minecraft.util.math.Vec3i;
|
||||||
import net.minecraft.util.registry.Registry;
|
import net.minecraft.util.registry.Registry;
|
||||||
|
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||||
import net.minecraft.world.gen.feature.Feature;
|
import net.minecraft.world.gen.feature.Feature;
|
||||||
import net.minecraft.world.gen.feature.StructureFeature;
|
import net.minecraft.world.gen.feature.StructureFeature;
|
||||||
import net.minecraft.world.poi.PointOfInterest;
|
import net.minecraft.world.poi.PointOfInterest;
|
||||||
@@ -130,8 +142,10 @@ import net.minecraft.world.dimension.DimensionType;
|
|||||||
import net.minecraft.world.gen.ChunkRandom;
|
import net.minecraft.world.gen.ChunkRandom;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -342,6 +356,18 @@ public class CarpetExpression
|
|||||||
return MapValue.wrap(ret);
|
return MapValue.wrap(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static long lastSeed = -1;
|
||||||
|
private void BooYah(ChunkGenerator generator)
|
||||||
|
{
|
||||||
|
synchronized (generator)
|
||||||
|
{
|
||||||
|
if (generator.getSeed() != lastSeed)
|
||||||
|
{
|
||||||
|
StructureFeatures.STRONGHOLD.shouldStartAt(null, generator, null, 0, 0, null);
|
||||||
|
lastSeed = generator.getSeed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
private void API_BlockManipulation()
|
private void API_BlockManipulation()
|
||||||
{
|
{
|
||||||
this.expr.addLazyFunction("block", -1, (c, t, lv) ->
|
this.expr.addLazyFunction("block", -1, (c, t, lv) ->
|
||||||
@@ -737,43 +763,76 @@ public class CarpetExpression
|
|||||||
this.expr.addLazyFunction("without_updates", 1, (c, t, lv) ->
|
this.expr.addLazyFunction("without_updates", 1, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
boolean previous = CarpetSettings.impendingFillSkipUpdates;
|
boolean previous = CarpetSettings.impendingFillSkipUpdates;
|
||||||
try
|
if (previous) return lv.get(0);
|
||||||
|
Value [] result = new Value[]{Value.NULL};
|
||||||
|
((CarpetContext)c).s.getMinecraftServer().submitAndJoin( () ->
|
||||||
{
|
{
|
||||||
CarpetSettings.impendingFillSkipUpdates = true;
|
try
|
||||||
Value ret = lv.get(0).evalValue(c, t);
|
{
|
||||||
return (cc, tt) -> ret;
|
CarpetSettings.impendingFillSkipUpdates = true;
|
||||||
}
|
result[0] = lv.get(0).evalValue(c, t);
|
||||||
finally
|
}
|
||||||
{
|
finally
|
||||||
CarpetSettings.impendingFillSkipUpdates = previous;
|
{
|
||||||
}
|
CarpetSettings.impendingFillSkipUpdates = previous;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return (cc, tt) -> result[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
this.expr.addLazyFunction("set", -1, (c, t, lv) ->
|
this.expr.addLazyFunction("set", -1, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
CarpetContext cc = (CarpetContext)c;
|
CarpetContext cc = (CarpetContext)c;
|
||||||
ServerWorld world = cc.s.getWorld();
|
ServerWorld world = cc.s.getWorld();
|
||||||
if (lv.size() < 2 || lv.size() % 2 == 1)
|
|
||||||
throw new InternalExpressionException("'set' should have at least 2 params and odd attributes");
|
|
||||||
BlockArgument targetLocator = BlockArgument.findIn(cc, lv, 0);
|
BlockArgument targetLocator = BlockArgument.findIn(cc, lv, 0);
|
||||||
BlockArgument sourceLocator = BlockArgument.findIn(cc, lv, targetLocator.offset, true);
|
BlockArgument sourceLocator = BlockArgument.findIn(cc, lv, targetLocator.offset, true);
|
||||||
BlockState sourceBlockState = sourceLocator.block.getBlockState();
|
BlockState sourceBlockState = sourceLocator.block.getBlockState();
|
||||||
BlockState targetBlockState = world.getBlockState(targetLocator.block.getPos());
|
BlockState targetBlockState = world.getBlockState(targetLocator.block.getPos());
|
||||||
if (sourceLocator.offset < lv.size())
|
CompoundTag data = null;
|
||||||
|
if (lv.size() > sourceLocator.offset)
|
||||||
{
|
{
|
||||||
StateManager<Block, BlockState> states = sourceBlockState.getBlock().getStateManager();
|
List<Value> args = new ArrayList<>();
|
||||||
for (int i = sourceLocator.offset; i < lv.size(); i += 2)
|
for (int i = sourceLocator.offset, m = lv.size(); i < m; i++)
|
||||||
{
|
{
|
||||||
String paramString = lv.get(i).evalValue(c).getString();
|
args.add(lv.get(i).evalValue(c));
|
||||||
|
}
|
||||||
|
if (args.get(0) instanceof ListValue)
|
||||||
|
{
|
||||||
|
if (args.size() == 2)
|
||||||
|
{
|
||||||
|
Value dataValue = NBTSerializableValue.fromValue( args.get(1));
|
||||||
|
if (dataValue instanceof NBTSerializableValue)
|
||||||
|
{
|
||||||
|
data = ((NBTSerializableValue) dataValue).getCompoundTag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args = ((ListValue) args.get(0)).getItems();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((args.size() & 1) == 1)
|
||||||
|
{
|
||||||
|
Value dataValue = NBTSerializableValue.fromValue( args.get(args.size()-1));
|
||||||
|
if (dataValue instanceof NBTSerializableValue)
|
||||||
|
{
|
||||||
|
data = ((NBTSerializableValue) dataValue).getCompoundTag();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StateManager<Block, BlockState> states = sourceBlockState.getBlock().getStateManager();
|
||||||
|
for (int i = 0; i < args.size()-1; i += 2)
|
||||||
|
{
|
||||||
|
String paramString = args.get(i).getString();
|
||||||
Property<?> property = states.getProperty(paramString);
|
Property<?> property = states.getProperty(paramString);
|
||||||
if (property == null)
|
if (property == null)
|
||||||
throw new InternalExpressionException("Property " + paramString + " doesn't apply to " + sourceLocator.block.getString());
|
throw new InternalExpressionException("Property " + paramString + " doesn't apply to " + sourceLocator.block.getString());
|
||||||
String paramValue = lv.get(i + 1).evalValue(c).getString();
|
String paramValue = args.get(i + 1).getString();
|
||||||
sourceBlockState = setProperty(property, paramString, paramValue, sourceBlockState);
|
sourceBlockState = setProperty(property, paramString, paramValue, sourceBlockState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CompoundTag data = sourceLocator.block.getData();
|
if (data == null) data = sourceLocator.block.getData();
|
||||||
|
CompoundTag finalData = data;
|
||||||
|
|
||||||
if (sourceBlockState == targetBlockState && data == null)
|
if (sourceBlockState == targetBlockState && data == null)
|
||||||
return (c_, t_) -> Value.FALSE;
|
return (c_, t_) -> Value.FALSE;
|
||||||
@@ -783,12 +842,12 @@ public class CarpetExpression
|
|||||||
{
|
{
|
||||||
Clearable.clear(world.getBlockEntity(targetPos));
|
Clearable.clear(world.getBlockEntity(targetPos));
|
||||||
world.setBlockState(targetPos, finalSourceBlockState, 2);
|
world.setBlockState(targetPos, finalSourceBlockState, 2);
|
||||||
if (data != null)
|
if (finalData != null)
|
||||||
{
|
{
|
||||||
BlockEntity be = world.getBlockEntity(targetPos);
|
BlockEntity be = world.getBlockEntity(targetPos);
|
||||||
if (be != null)
|
if (be != null)
|
||||||
{
|
{
|
||||||
CompoundTag destTag = data.copy();
|
CompoundTag destTag = finalData.copy();
|
||||||
destTag.putInt("x", targetPos.getX());
|
destTag.putInt("x", targetPos.getX());
|
||||||
destTag.putInt("y", targetPos.getY());
|
destTag.putInt("y", targetPos.getY());
|
||||||
destTag.putInt("z", targetPos.getZ());
|
destTag.putInt("z", targetPos.getZ());
|
||||||
@@ -897,7 +956,7 @@ public class CarpetExpression
|
|||||||
Tag outtag = tool.getTag();
|
Tag outtag = tool.getTag();
|
||||||
if (outtag == null)
|
if (outtag == null)
|
||||||
return LazyValue.TRUE;
|
return LazyValue.TRUE;
|
||||||
Value ret = new NBTSerializableValue(outtag);
|
Value ret = new NBTSerializableValue(() -> outtag);
|
||||||
return (_c, _t) -> ret;
|
return (_c, _t) -> ret;
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -1096,6 +1155,9 @@ public class CarpetExpression
|
|||||||
BlockArgument locator = BlockArgument.findIn(cc, lv, 0);
|
BlockArgument locator = BlockArgument.findIn(cc, lv, 0);
|
||||||
|
|
||||||
ServerWorld world = cc.s.getWorld();
|
ServerWorld world = cc.s.getWorld();
|
||||||
|
|
||||||
|
BooYah(world.getChunkManager().getChunkGenerator());
|
||||||
|
|
||||||
BlockPos pos = locator.block.getPos();
|
BlockPos pos = locator.block.getPos();
|
||||||
StructureFeature<?> structure = null;
|
StructureFeature<?> structure = null;
|
||||||
boolean needSize = false;
|
boolean needSize = false;
|
||||||
@@ -1385,13 +1447,14 @@ public class CarpetExpression
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.expr.addLazyFunction("scoreboard_remove", -1, (c, t, lv)-> {
|
this.expr.addLazyFunction("scoreboard_remove", -1, (c, t, lv)-> {
|
||||||
|
if (lv.size()==0) throw new InternalExpressionException("'scoreboard_remove' requires at least one parameter");
|
||||||
CarpetContext cc = (CarpetContext)c;
|
CarpetContext cc = (CarpetContext)c;
|
||||||
Scoreboard scoreboard = cc.s.getMinecraftServer().getScoreboard();
|
Scoreboard scoreboard = cc.s.getMinecraftServer().getScoreboard();
|
||||||
String objectiveName = lv.get(0).evalValue(c).getString();
|
String objectiveName = lv.get(0).evalValue(c).getString();
|
||||||
ScoreboardObjective objective = scoreboard.getObjective(objectiveName);
|
ScoreboardObjective objective = scoreboard.getObjective(objectiveName);
|
||||||
if (objective == null)
|
if (objective == null)
|
||||||
return LazyValue.FALSE;
|
return LazyValue.FALSE;
|
||||||
if (lv.size() == 0)
|
if (lv.size() == 1)
|
||||||
{
|
{
|
||||||
scoreboard.removeObjective(objective);
|
scoreboard.removeObjective(objective);
|
||||||
return LazyValue.TRUE;
|
return LazyValue.TRUE;
|
||||||
@@ -1472,6 +1535,120 @@ public class CarpetExpression
|
|||||||
return (_c, _t) -> res;
|
return (_c, _t) -> res;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.expr.addLazyFunction("recipe_data", -1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
CarpetContext cc = (CarpetContext)c;
|
||||||
|
if (lv.size() < 1) throw new InternalExpressionException("'recipe_data' requires at least one argument");
|
||||||
|
String recipeName = lv.get(0).evalValue(c).getString();
|
||||||
|
RecipeType type = RecipeType.CRAFTING;
|
||||||
|
if (lv.size() > 1)
|
||||||
|
{
|
||||||
|
String recipeType = lv.get(1).evalValue(c).getString();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
type = Registry.RECIPE_TYPE.get(new Identifier(recipeType));
|
||||||
|
}
|
||||||
|
catch (InvalidIdentifierException ignored)
|
||||||
|
{
|
||||||
|
throw new InternalExpressionException("Unknown crafting category: " + recipeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<Recipe<?>> recipes;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
recipes = ((RecipeManagerInterface) cc.s.getMinecraftServer().getRecipeManager()).getAllMatching(type, new Identifier(recipeName));
|
||||||
|
}
|
||||||
|
catch (InvalidIdentifierException ignored)
|
||||||
|
{
|
||||||
|
return LazyValue.NULL;
|
||||||
|
}
|
||||||
|
if (recipes.isEmpty())
|
||||||
|
return LazyValue.NULL;
|
||||||
|
List<Value> recipesOutput = new ArrayList<>();
|
||||||
|
for (Recipe<?> recipe: recipes)
|
||||||
|
{
|
||||||
|
ItemStack result = recipe.getOutput();
|
||||||
|
List<Value> ingredientValue = new ArrayList<>();
|
||||||
|
recipe.getPreviewInputs().forEach(
|
||||||
|
ingredient ->
|
||||||
|
{
|
||||||
|
// I am flattening ingredient lists per slot.
|
||||||
|
// consider recipe_data('wooden_sword','crafting') and ('iron_nugget', 'blasting') and notice difference
|
||||||
|
// in depths of lists.
|
||||||
|
List<Collection<ItemStack>> stacks = ((IngredientInterface) (Object) ingredient).getRecipeStacks();
|
||||||
|
if (stacks.isEmpty())
|
||||||
|
{
|
||||||
|
ingredientValue.add(Value.NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<Value> alternatives = new ArrayList<>();
|
||||||
|
stacks.forEach(col -> col.stream().map(ListValue::fromItemStack).forEach(alternatives::add));
|
||||||
|
ingredientValue.add(ListValue.wrap(alternatives));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Value recipeSpec;
|
||||||
|
if (recipe instanceof ShapedRecipe)
|
||||||
|
{
|
||||||
|
recipeSpec = ListValue.of(
|
||||||
|
new StringValue("shaped"),
|
||||||
|
new NumericValue(((ShapedRecipe) recipe).getWidth()),
|
||||||
|
new NumericValue(((ShapedRecipe) recipe).getHeight())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (recipe instanceof ShapelessRecipe)
|
||||||
|
{
|
||||||
|
recipeSpec = ListValue.of(new StringValue("shapeless"));
|
||||||
|
}
|
||||||
|
else if (recipe instanceof AbstractCookingRecipe)
|
||||||
|
{
|
||||||
|
recipeSpec = ListValue.of(
|
||||||
|
new StringValue("smelting"),
|
||||||
|
new NumericValue(((AbstractCookingRecipe) recipe).getCookTime()),
|
||||||
|
new NumericValue(((AbstractCookingRecipe) recipe).getExperience())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (recipe instanceof CuttingRecipe)
|
||||||
|
{
|
||||||
|
recipeSpec = ListValue.of(new StringValue("cutting"));
|
||||||
|
}
|
||||||
|
else if (recipe instanceof SpecialCraftingRecipe)
|
||||||
|
{
|
||||||
|
recipeSpec = ListValue.of(new StringValue("special"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
recipeSpec = ListValue.of(new StringValue("custom"));
|
||||||
|
}
|
||||||
|
|
||||||
|
recipesOutput.add(ListValue.of(ListValue.fromItemStack(result), ListValue.wrap(ingredientValue), recipeSpec));
|
||||||
|
}
|
||||||
|
Value ret = ListValue.wrap(recipesOutput);
|
||||||
|
return (_c, _t) -> ret;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.expr.addLazyFunction("crafting_remaining_item", 1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
String itemStr = lv.get(0).evalValue(c).getString();
|
||||||
|
Item item;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
Value ret = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.ITEM.getId(item.getRecipeRemainder())));
|
||||||
|
return (_c, _t ) -> ret;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
this.expr.addLazyFunction("inventory_size", -1, (c, t, lv) ->
|
this.expr.addLazyFunction("inventory_size", -1, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
CarpetContext cc = (CarpetContext) c;
|
CarpetContext cc = (CarpetContext) c;
|
||||||
@@ -1825,11 +2002,9 @@ public class CarpetExpression
|
|||||||
Value nbt = lv.get(position.offset).evalValue(c);
|
Value nbt = lv.get(position.offset).evalValue(c);
|
||||||
NBTSerializableValue v = (nbt instanceof NBTSerializableValue) ? (NBTSerializableValue) nbt
|
NBTSerializableValue v = (nbt instanceof NBTSerializableValue) ? (NBTSerializableValue) nbt
|
||||||
: NBTSerializableValue.parseString(nbt.getString());
|
: NBTSerializableValue.parseString(nbt.getString());
|
||||||
if (v != null)
|
hasTag = true;
|
||||||
{
|
tag = v.getCompoundTag();
|
||||||
hasTag = true;
|
|
||||||
tag = v.getCompoundTag();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
tag.putString("id", entityId.toString());
|
tag.putString("id", entityId.toString());
|
||||||
Vec3d vec3d = position.vec;
|
Vec3d vec3d = position.vec;
|
||||||
@@ -2097,21 +2272,28 @@ public class CarpetExpression
|
|||||||
return (c_, t_) -> new NumericValue(finalSCount);
|
return (c_, t_) -> new NumericValue(finalSCount);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.expr.addLazyFunction("volume", 7, (c, t, lv) ->
|
this.expr.addLazyFunction("volume", -1, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
int xi = (int) NumericValue.asNumber(lv.get(0).evalValue(c)).getLong();
|
CarpetContext cc = (CarpetContext)c;
|
||||||
int yi = (int) NumericValue.asNumber(lv.get(1).evalValue(c)).getLong();
|
|
||||||
int zi = (int) NumericValue.asNumber(lv.get(2).evalValue(c)).getLong();
|
BlockArgument pos1Locator = BlockArgument.findIn(cc, lv, 0);
|
||||||
int xj = (int) NumericValue.asNumber(lv.get(3).evalValue(c)).getLong();
|
BlockArgument pos2Locator = BlockArgument.findIn(cc, lv, pos1Locator.offset);
|
||||||
int yj = (int) NumericValue.asNumber(lv.get(4).evalValue(c)).getLong();
|
BlockPos pos1 = pos1Locator.block.getPos();
|
||||||
int zj = (int) NumericValue.asNumber(lv.get(5).evalValue(c)).getLong();
|
BlockPos pos2 = pos2Locator.block.getPos();
|
||||||
int minx = min(xi, xj);
|
|
||||||
int miny = min(yi, yj);
|
int x1 = pos1.getX();
|
||||||
int minz = min(zi, zj);
|
int y1 = pos1.getY();
|
||||||
int maxx = max(xi, xj);
|
int z1 = pos1.getZ();
|
||||||
int maxy = max(yi, yj);
|
int x2 = pos2.getX();
|
||||||
int maxz = max(zi, zj);
|
int y2 = pos2.getY();
|
||||||
LazyValue expr = lv.get(6);
|
int z2 = pos2.getZ();
|
||||||
|
int minx = min(x1, x2);
|
||||||
|
int miny = min(y1, y2);
|
||||||
|
int minz = min(z1, z2);
|
||||||
|
int maxx = max(x1, x2);
|
||||||
|
int maxy = max(y1, y2);
|
||||||
|
int maxz = max(z1, z2);
|
||||||
|
LazyValue expr = lv.get(pos2Locator.offset);
|
||||||
|
|
||||||
//saving outer scope
|
//saving outer scope
|
||||||
LazyValue _x = c.getVariable("_x");
|
LazyValue _x = c.getVariable("_x");
|
||||||
@@ -2181,57 +2363,63 @@ public class CarpetExpression
|
|||||||
|
|
||||||
this.expr.addLazyFunction("rect", -1, (c, t, lv)->
|
this.expr.addLazyFunction("rect", -1, (c, t, lv)->
|
||||||
{
|
{
|
||||||
if (lv.size() != 3 && lv.size() != 6 && lv.size() != 9)
|
CarpetContext cc = (CarpetContext) c;
|
||||||
|
int cx, cy, cz;
|
||||||
|
int sminx, sminy, sminz;
|
||||||
|
int smaxx, smaxy, smaxz;
|
||||||
|
BlockArgument cposLocator = BlockArgument.findIn(cc, lv, 0);
|
||||||
|
BlockPos cpos = cposLocator.block.getPos();
|
||||||
|
cx = cpos.getX();
|
||||||
|
cy = cpos.getY();
|
||||||
|
cz = cpos.getZ();
|
||||||
|
if (lv.size() > cposLocator.offset)
|
||||||
{
|
{
|
||||||
throw new InternalExpressionException("Rectangular region should be specified with 3, 6, or 9 coordinates");
|
Vector3Argument diffLocator = Vector3Argument.findIn(cc, lv, cposLocator.offset);
|
||||||
}
|
if (diffLocator.fromBlock)
|
||||||
int cx;
|
|
||||||
int cy;
|
|
||||||
int cz;
|
|
||||||
int sminx;
|
|
||||||
int sminy;
|
|
||||||
int sminz;
|
|
||||||
int smaxx;
|
|
||||||
int smaxy;
|
|
||||||
int smaxz;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
cx = (int)((NumericValue) lv.get(0).evalValue(c)).getLong();
|
|
||||||
cy = (int)((NumericValue) lv.get(1).evalValue(c)).getLong();
|
|
||||||
cz = (int)((NumericValue) lv.get(2).evalValue(c)).getLong();
|
|
||||||
if (lv.size()==3) // only done this way because of stupid Java lambda final reqs
|
|
||||||
{
|
{
|
||||||
sminx = 1;
|
sminx = MathHelper.floor(abs(diffLocator.vec.x - cx));
|
||||||
sminy = 1;
|
sminy = MathHelper.floor(abs(diffLocator.vec.y - cx));
|
||||||
sminz = 1;
|
sminz = MathHelper.floor(abs(diffLocator.vec.z - cx));
|
||||||
smaxx = 1;
|
|
||||||
smaxy = 1;
|
|
||||||
smaxz = 1;
|
|
||||||
}
|
}
|
||||||
else if (lv.size()==6)
|
else
|
||||||
|
{
|
||||||
|
sminx = MathHelper.floor(abs(diffLocator.vec.x));
|
||||||
|
sminy = MathHelper.floor(abs(diffLocator.vec.y));
|
||||||
|
sminz = MathHelper.floor(abs(diffLocator.vec.z));
|
||||||
|
}
|
||||||
|
if (lv.size() > diffLocator.offset)
|
||||||
|
{
|
||||||
|
Vector3Argument posDiff = Vector3Argument.findIn(cc, lv, diffLocator.offset);
|
||||||
|
if (posDiff.fromBlock)
|
||||||
|
{
|
||||||
|
smaxx = MathHelper.floor(abs(posDiff.vec.x - cx));
|
||||||
|
smaxy = MathHelper.floor(abs(posDiff.vec.y - cx));
|
||||||
|
smaxz = MathHelper.floor(abs(posDiff.vec.z - cx));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
smaxx = MathHelper.floor(abs(posDiff.vec.x));
|
||||||
|
smaxy = MathHelper.floor(abs(posDiff.vec.y));
|
||||||
|
smaxz = MathHelper.floor(abs(posDiff.vec.z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
sminx = (int) ((NumericValue) lv.get(3).evalValue(c)).getLong();
|
|
||||||
sminy = (int) ((NumericValue) lv.get(4).evalValue(c)).getLong();
|
|
||||||
sminz = (int) ((NumericValue) lv.get(5).evalValue(c)).getLong();
|
|
||||||
smaxx = sminx;
|
smaxx = sminx;
|
||||||
smaxy = sminy;
|
smaxy = sminy;
|
||||||
smaxz = sminz;
|
smaxz = sminz;
|
||||||
}
|
}
|
||||||
else // size == 9
|
|
||||||
{
|
|
||||||
sminx = (int) ((NumericValue) lv.get(3).evalValue(c)).getLong();
|
|
||||||
sminy = (int) ((NumericValue) lv.get(4).evalValue(c)).getLong();
|
|
||||||
sminz = (int) ((NumericValue) lv.get(5).evalValue(c)).getLong();
|
|
||||||
smaxx = (int)((NumericValue) lv.get(6).evalValue(c)).getLong();
|
|
||||||
smaxy = (int)((NumericValue) lv.get(7).evalValue(c)).getLong();
|
|
||||||
smaxz = (int)((NumericValue) lv.get(8).evalValue(c)).getLong();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (ClassCastException exc)
|
else
|
||||||
{
|
{
|
||||||
throw new InternalExpressionException("Attempted to pass a non-number to rect");
|
sminx = 1;
|
||||||
|
sminy = 1;
|
||||||
|
sminz = 1;
|
||||||
|
smaxx = 1;
|
||||||
|
smaxy = 1;
|
||||||
|
smaxz = 1;
|
||||||
}
|
}
|
||||||
CarpetContext cc = (CarpetContext)c;
|
|
||||||
return (c_, t_) -> new LazyListValue()
|
return (c_, t_) -> new LazyListValue()
|
||||||
{
|
{
|
||||||
final int minx = cx-sminx;
|
final int minx = cx-sminx;
|
||||||
@@ -2240,6 +2428,7 @@ public class CarpetExpression
|
|||||||
final int maxx = cx+smaxx;
|
final int maxx = cx+smaxx;
|
||||||
final int maxy = cy+smaxy;
|
final int maxy = cy+smaxy;
|
||||||
final int maxz = cz+smaxz;
|
final int maxz = cz+smaxz;
|
||||||
|
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
int z;
|
int z;
|
||||||
@@ -2299,10 +2488,9 @@ public class CarpetExpression
|
|||||||
this.expr.addLazyFunction("diamond", -1, (c, t, lv)->
|
this.expr.addLazyFunction("diamond", -1, (c, t, lv)->
|
||||||
{
|
{
|
||||||
CarpetContext cc = (CarpetContext)c;
|
CarpetContext cc = (CarpetContext)c;
|
||||||
if (lv.size() != 3 && lv.size() != 4 && lv.size() != 5)
|
|
||||||
{
|
BlockArgument cposLocator=BlockArgument.findIn((CarpetContext)c,lv,0);
|
||||||
throw new InternalExpressionException("'diamond' region should be specified with 3 to 5 coordinates");
|
BlockPos cpos = cposLocator.block.getPos();
|
||||||
}
|
|
||||||
|
|
||||||
int cx;
|
int cx;
|
||||||
int cy;
|
int cy;
|
||||||
@@ -2311,10 +2499,11 @@ public class CarpetExpression
|
|||||||
int height;
|
int height;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
cx = (int)((NumericValue) lv.get(0).evalValue(c)).getLong();
|
cx = cpos.getX();
|
||||||
cy = (int)((NumericValue) lv.get(1).evalValue(c)).getLong();
|
cy = cpos.getY();
|
||||||
cz = (int)((NumericValue) lv.get(2).evalValue(c)).getLong();
|
cz = cpos.getZ();
|
||||||
if (lv.size()==3)
|
|
||||||
|
if (lv.size()==cposLocator.offset)
|
||||||
{
|
{
|
||||||
Value retval = ListValue.of(
|
Value retval = ListValue.of(
|
||||||
BlockValue.fromCoords(cc, cx, cy-1, cz),
|
BlockValue.fromCoords(cc, cx, cy-1, cz),
|
||||||
@@ -2327,20 +2516,22 @@ public class CarpetExpression
|
|||||||
);
|
);
|
||||||
return (_c, _t ) -> retval;
|
return (_c, _t ) -> retval;
|
||||||
}
|
}
|
||||||
else if (lv.size()==4)
|
else if (lv.size()==1+cposLocator.offset)
|
||||||
{
|
{
|
||||||
width = (int) ((NumericValue) lv.get(3).evalValue(c)).getLong();
|
width = (int) ((NumericValue) lv.get(cposLocator.offset).evalValue(c)).getLong();
|
||||||
height = 0;
|
height = 0;
|
||||||
}
|
}
|
||||||
else // size == 5
|
else if(lv.size()==2+cposLocator.offset)
|
||||||
{
|
{
|
||||||
width = (int) ((NumericValue) lv.get(3).evalValue(c)).getLong();
|
width = (int) ((NumericValue) lv.get(cposLocator.offset).evalValue(c)).getLong();
|
||||||
height = (int) ((NumericValue) lv.get(4).evalValue(c)).getLong();
|
height = (int) ((NumericValue) lv.get(cposLocator.offset+1).evalValue(c)).getLong();
|
||||||
|
} else{
|
||||||
|
throw new InternalExpressionException("Incorrect number of arguments for 'diamond'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ClassCastException exc)
|
catch (ClassCastException exc)
|
||||||
{
|
{
|
||||||
throw new InternalExpressionException("Attempted to pass a non-number to diamond");
|
throw new InternalExpressionException("Attempted to pass a non-number to 'diamond'");
|
||||||
}
|
}
|
||||||
if (height == 0)
|
if (height == 0)
|
||||||
{
|
{
|
||||||
@@ -2480,7 +2671,7 @@ public class CarpetExpression
|
|||||||
public static String recognizeResource(Value value)
|
public static String recognizeResource(Value value)
|
||||||
{
|
{
|
||||||
String origfile = value.getString();
|
String origfile = value.getString();
|
||||||
String file = origfile.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9/]", "");
|
String file = origfile.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9\\-+_/]", "");
|
||||||
file = Arrays.stream(file.split("/+")).filter(s -> !s.isEmpty()).collect(Collectors.joining("/"));
|
file = Arrays.stream(file.split("/+")).filter(s -> !s.isEmpty()).collect(Collectors.joining("/"));
|
||||||
if (file.isEmpty())
|
if (file.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -2756,12 +2947,7 @@ public class CarpetExpression
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.expr.addLazyFunction("nbt", 1, (c, t, lv) -> {
|
this.expr.addLazyFunction("nbt", 1, (c, t, lv) -> {
|
||||||
Value v = lv.get(0).evalValue(c);
|
Value ret = NBTSerializableValue.fromValue(lv.get(0).evalValue(c));
|
||||||
if (v instanceof NBTSerializableValue)
|
|
||||||
return (cc, tt) -> v;
|
|
||||||
Value ret = NBTSerializableValue.parseString(v.getString());
|
|
||||||
if (ret == null)
|
|
||||||
return LazyValue.NULL;
|
|
||||||
return (cc, tt) -> ret;
|
return (cc, tt) -> ret;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2772,11 +2958,50 @@ public class CarpetExpression
|
|||||||
return (cc, tt) -> ret;
|
return (cc, tt) -> ret;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.expr.addLazyFunction("parse_nbt", 1, (c, t, lv) -> {
|
||||||
|
Value v = lv.get(0).evalValue(c);
|
||||||
|
if (v instanceof NBTSerializableValue)
|
||||||
|
{
|
||||||
|
Value parsed = ((NBTSerializableValue) v).toValue();
|
||||||
|
return (cc, tt) -> parsed;
|
||||||
|
}
|
||||||
|
NBTSerializableValue ret = NBTSerializableValue.parseString(v.getString());
|
||||||
|
if (ret == null)
|
||||||
|
return LazyValue.NULL;
|
||||||
|
Value parsed = ret.toValue();
|
||||||
|
return (cc, tt) -> parsed;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.expr.addLazyFunction("encode_nbt", -1, (c, t, lv) -> {
|
||||||
|
int argSize = lv.size();
|
||||||
|
if (argSize==0 || argSize > 2) throw new InternalExpressionException("'encode_nbt' requires 1 or 2 parameters");
|
||||||
|
Value v = lv.get(0).evalValue(c);
|
||||||
|
boolean force = (argSize > 1) && lv.get(1).evalValue(c).getBoolean();
|
||||||
|
Tag tag;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tag = v.toTag(force);
|
||||||
|
}
|
||||||
|
catch (NBTSerializableValue.IncompatibleTypeException ignored)
|
||||||
|
{
|
||||||
|
throw new InternalExpressionException("cannot reliably encode to a tag the value of '"+ignored.val.getPrettyString()+"'");
|
||||||
|
}
|
||||||
|
Value tagValue = new NBTSerializableValue(tag);
|
||||||
|
return (cc, tt) -> tagValue;
|
||||||
|
});
|
||||||
|
|
||||||
//"overridden" native call that prints to stderr
|
//"overridden" native call that prints to stderr
|
||||||
this.expr.addLazyFunction("print", 1, (c, t, lv) ->
|
this.expr.addLazyFunction("print", -1, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
|
if (lv.size() == 0 || lv.size() > 2) throw new InternalExpressionException("'print' takes one or two arguments");
|
||||||
ServerCommandSource s = ((CarpetContext)c).s;
|
ServerCommandSource s = ((CarpetContext)c).s;
|
||||||
Value res = lv.get(0).evalValue(c);
|
Value res = lv.get(0).evalValue(c);
|
||||||
|
if (lv.size() == 2)
|
||||||
|
{
|
||||||
|
ServerPlayerEntity player = EntityValue.getPlayerByValue(s.getMinecraftServer(), res);
|
||||||
|
s = player.getCommandSource();
|
||||||
|
res = lv.get(1).evalValue(c);
|
||||||
|
}
|
||||||
if (res instanceof FormattedTextValue)
|
if (res instanceof FormattedTextValue)
|
||||||
{
|
{
|
||||||
s.sendFeedback(((FormattedTextValue) res).getText(), false);
|
s.sendFeedback(((FormattedTextValue) res).getText(), false);
|
||||||
@@ -2792,7 +3017,8 @@ public class CarpetExpression
|
|||||||
Messenger.m(s, "w " + res.getString());
|
Messenger.m(s, "w " + res.getString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (_c, _t) -> res; // pass through for variables
|
Value finalRes = res;
|
||||||
|
return (_c, _t) -> finalRes; // pass through for variables
|
||||||
});
|
});
|
||||||
|
|
||||||
this.expr.addLazyFunction("format", -1, (c, t, lv) -> {
|
this.expr.addLazyFunction("format", -1, (c, t, lv) -> {
|
||||||
@@ -2862,9 +3088,14 @@ public class CarpetExpression
|
|||||||
BlockPos target = ((CarpetContext)c).origin;
|
BlockPos target = ((CarpetContext)c).origin;
|
||||||
Vec3d posf = new Vec3d((double)target.getX()+0.5D,(double)target.getY(),(double)target.getZ()+0.5D);
|
Vec3d posf = new Vec3d((double)target.getX()+0.5D,(double)target.getY(),(double)target.getZ()+0.5D);
|
||||||
ServerCommandSource s = ((CarpetContext)c).s;
|
ServerCommandSource s = ((CarpetContext)c).s;
|
||||||
Value retval = new NumericValue(s.getMinecraftServer().getCommandManager().execute(
|
try
|
||||||
s.withPosition(posf).withSilent().withLevel(CarpetSettings.runPermissionLevel), lv.get(0).evalValue(c).getString()));
|
{
|
||||||
return (c_, t_) -> retval;
|
Value retval = new NumericValue(s.getMinecraftServer().getCommandManager().execute(
|
||||||
|
s.withPosition(posf).withSilent().withLevel(CarpetSettings.runPermissionLevel), lv.get(0).evalValue(c).getString()));
|
||||||
|
return (c_, t_) -> retval;
|
||||||
|
}
|
||||||
|
catch (Exception ignored) {}
|
||||||
|
return LazyValue.NULL;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.expr.addLazyFunction("save", 0, (c, t, lv) -> {
|
this.expr.addLazyFunction("save", 0, (c, t, lv) -> {
|
||||||
@@ -2945,7 +3176,7 @@ public class CarpetExpression
|
|||||||
|
|
||||||
this.expr.addLazyFunction("seed", -1, (c, t, lv) -> {
|
this.expr.addLazyFunction("seed", -1, (c, t, lv) -> {
|
||||||
ServerCommandSource s = ((CarpetContext)c).s;
|
ServerCommandSource s = ((CarpetContext)c).s;
|
||||||
Value ret = new NumericValue(s.getWorld().getSeed());
|
Value ret = new StringValue(Long.toString(s.getWorld().getSeed()));
|
||||||
return (cc, tt) -> ret;
|
return (cc, tt) -> ret;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -3013,10 +3244,12 @@ public class CarpetExpression
|
|||||||
default:
|
default:
|
||||||
throw new InternalExpressionException("Incorrect dimension string: "+dimString);
|
throw new InternalExpressionException("Incorrect dimension string: "+dimString);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
((CarpetContext) c).s = innerSource;
|
if (innerSource.getWorld() == outerSource.getWorld()) return lv.get(1);
|
||||||
Value retval = lv.get(1).evalValue(c);
|
Context newCtx = c.recreate();
|
||||||
((CarpetContext) c).s = outerSource;
|
((CarpetContext) newCtx).s = innerSource;
|
||||||
|
newCtx.variables = c.variables;
|
||||||
|
Value retval = lv.get(1).evalValue(newCtx);
|
||||||
return (cc, tt) -> retval;
|
return (cc, tt) -> retval;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ public class Context
|
|||||||
public static final int SIGNATURE = 7;
|
public static final int SIGNATURE = 7;
|
||||||
public static final int LOCALIZATION = 8;
|
public static final int LOCALIZATION = 8;
|
||||||
public static final int LVALUE = 9;
|
public static final int LVALUE = 9;
|
||||||
|
public static final int MAPDEF = 10;
|
||||||
|
|
||||||
public final Map<String, LazyValue> variables = new HashMap<>();
|
public Map<String, LazyValue> variables = new HashMap<>();
|
||||||
|
|
||||||
public final ScriptHost host;
|
public final ScriptHost host;
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ import org.apache.commons.lang3.text.WordUtils;
|
|||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.IllegalFormatException;
|
import java.util.IllegalFormatException;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@@ -490,6 +492,11 @@ public class Expression
|
|||||||
//assigns const procedure to the lhs, returning its previous value
|
//assigns const procedure to the lhs, returning its previous value
|
||||||
addLazyBinaryOperatorWithDelegation("->", precedence.get("def->"), false, (c, type, e, t, lv1, lv2) ->
|
addLazyBinaryOperatorWithDelegation("->", precedence.get("def->"), false, (c, type, e, t, lv1, lv2) ->
|
||||||
{
|
{
|
||||||
|
if (type == Context.MAPDEF)
|
||||||
|
{
|
||||||
|
Value result = ListValue.of(lv1.evalValue(c), lv2.evalValue(c));
|
||||||
|
return (cc, tt) -> result;
|
||||||
|
}
|
||||||
Value v1 = lv1.evalValue(c, Context.SIGNATURE);
|
Value v1 = lv1.evalValue(c, Context.SIGNATURE);
|
||||||
if (!(v1 instanceof FunctionSignatureValue))
|
if (!(v1 instanceof FunctionSignatureValue))
|
||||||
throw new InternalExpressionException("'->' operator requires a function signature on the LHS");
|
throw new InternalExpressionException("'->' operator requires a function signature on the LHS");
|
||||||
@@ -1333,11 +1340,18 @@ public class Expression
|
|||||||
return LazyListValue.range(from, to, step);
|
return LazyListValue.range(from, to, step);
|
||||||
});
|
});
|
||||||
|
|
||||||
addFunction("m", lv ->
|
addLazyFunction("m", -1, (c, t, llv) ->
|
||||||
{
|
{
|
||||||
|
List<Value> lv = new ArrayList<>();
|
||||||
|
for (LazyValue lazyParam : llv) {
|
||||||
|
lv.add(lazyParam.evalValue(c, Context.MAPDEF)); // none type default by design
|
||||||
|
}
|
||||||
|
Value ret;
|
||||||
if (lv.size() == 1 && lv.get(0) instanceof LazyListValue)
|
if (lv.size() == 1 && lv.get(0) instanceof LazyListValue)
|
||||||
return new MapValue(((LazyListValue) lv.get(0)).unroll());
|
ret = new MapValue(((LazyListValue) lv.get(0)).unroll());
|
||||||
return new MapValue(lv);
|
else
|
||||||
|
ret = new MapValue(lv);
|
||||||
|
return (cc, tt) -> ret;
|
||||||
});
|
});
|
||||||
|
|
||||||
addUnaryFunction("keys", v -> {
|
addUnaryFunction("keys", v -> {
|
||||||
@@ -1449,20 +1463,6 @@ public class Expression
|
|||||||
return (cc, tt) -> ret;
|
return (cc, tt) -> ret;
|
||||||
});
|
});
|
||||||
|
|
||||||
//Deprecated, use "get" instead, or . operator
|
|
||||||
addBinaryFunction("element", (v1, v2) ->
|
|
||||||
{
|
|
||||||
if (!(v1 instanceof ListValue))
|
|
||||||
throw new InternalExpressionException("First argument of 'get' should be a list");
|
|
||||||
List<Value> items = ((ListValue)v1).getItems();
|
|
||||||
long index = NumericValue.asNumber(v2).getLong();
|
|
||||||
int numitems = items.size();
|
|
||||||
long range = abs(index)/numitems;
|
|
||||||
index += (range+2)*numitems;
|
|
||||||
index = index % numitems;
|
|
||||||
return items.get((int)index);
|
|
||||||
});
|
|
||||||
|
|
||||||
addLazyFunction("put", -1, (c, t, lv) ->
|
addLazyFunction("put", -1, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
if(lv.size()<2)
|
if(lv.size()<2)
|
||||||
@@ -1790,6 +1790,66 @@ public class Expression
|
|||||||
return (cc, tt) -> time;
|
return (cc, tt) -> time;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addLazyFunction("unix_time", 0, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
Value time = new NumericValue(System.currentTimeMillis());
|
||||||
|
return (cc, tt) -> time;
|
||||||
|
});
|
||||||
|
|
||||||
|
addFunction("convert_date", lv ->
|
||||||
|
{
|
||||||
|
int argsize = lv.size();
|
||||||
|
if (lv.size() == 0) throw new InternalExpressionException("'convert_date' requires at least one parameter");
|
||||||
|
Value value = lv.get(0);
|
||||||
|
if (argsize == 1 && !(value instanceof ListValue))
|
||||||
|
{
|
||||||
|
Calendar cal = new GregorianCalendar(Locale.ROOT);
|
||||||
|
cal.setTimeInMillis(NumericValue.asNumber(value, "timestamp").getLong());
|
||||||
|
int weekday = cal.get(Calendar.DAY_OF_WEEK)-1;
|
||||||
|
if (weekday == 0) weekday = 7;
|
||||||
|
Value retVal = ListValue.ofNums(
|
||||||
|
cal.get(Calendar.YEAR),
|
||||||
|
cal.get(Calendar.MONTH)+1,
|
||||||
|
cal.get(Calendar.DAY_OF_MONTH),
|
||||||
|
cal.get(Calendar.HOUR_OF_DAY),
|
||||||
|
cal.get(Calendar.MINUTE),
|
||||||
|
cal.get(Calendar.SECOND),
|
||||||
|
weekday,
|
||||||
|
cal.get(Calendar.DAY_OF_YEAR),
|
||||||
|
cal.get(Calendar.WEEK_OF_YEAR)
|
||||||
|
);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
else if(value instanceof ListValue)
|
||||||
|
{
|
||||||
|
lv = ((ListValue) value).getItems();
|
||||||
|
argsize = lv.size();
|
||||||
|
}
|
||||||
|
Calendar cal = new GregorianCalendar(0, 0, 0, 0, 0, 0);
|
||||||
|
|
||||||
|
if (argsize == 3)
|
||||||
|
{
|
||||||
|
cal.set(
|
||||||
|
NumericValue.asNumber(lv.get(0)).getInt(),
|
||||||
|
NumericValue.asNumber(lv.get(1)).getInt()-1,
|
||||||
|
NumericValue.asNumber(lv.get(2)).getInt()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (argsize == 6)
|
||||||
|
{
|
||||||
|
cal.set(
|
||||||
|
NumericValue.asNumber(lv.get(0)).getInt(),
|
||||||
|
NumericValue.asNumber(lv.get(1)).getInt()-1,
|
||||||
|
NumericValue.asNumber(lv.get(2)).getInt(),
|
||||||
|
NumericValue.asNumber(lv.get(3)).getInt(),
|
||||||
|
NumericValue.asNumber(lv.get(4)).getInt(),
|
||||||
|
NumericValue.asNumber(lv.get(5)).getInt()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else throw new InternalExpressionException("Date conversion requires 3 arguments for Dates or 6 arguments, for time");
|
||||||
|
return new NumericValue(cal.getTimeInMillis());
|
||||||
|
});
|
||||||
|
|
||||||
addLazyFunction("profile_expr", 1, (c, t, lv) ->
|
addLazyFunction("profile_expr", 1, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
LazyValue lazy = lv.get(0);
|
LazyValue lazy = lv.get(0);
|
||||||
@@ -2000,7 +2060,7 @@ public class Expression
|
|||||||
|
|
||||||
Tokenizer tokenizer = new Tokenizer(c, this, expression, allowComments, allowNewlineSubstitutions);
|
Tokenizer tokenizer = new Tokenizer(c, this, expression, allowComments, allowNewlineSubstitutions);
|
||||||
// stripping lousy but acceptable semicolons
|
// stripping lousy but acceptable semicolons
|
||||||
List<Tokenizer.Token> cleanedTokens = tokenizer.fixSemicolons();
|
List<Tokenizer.Token> cleanedTokens = tokenizer.postProcess();
|
||||||
|
|
||||||
Tokenizer.Token lastFunction = null;
|
Tokenizer.Token lastFunction = null;
|
||||||
Tokenizer.Token previousToken = null;
|
Tokenizer.Token previousToken = null;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
|||||||
this.newLinesMarkers = allowNewLineMakers;
|
this.newLinesMarkers = allowNewLineMakers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Token> fixSemicolons()
|
public List<Token> postProcess()
|
||||||
{
|
{
|
||||||
Iterable<Token> iterable = () -> this;
|
Iterable<Token> iterable = () -> this;
|
||||||
List<Token> originalTokens = StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList());
|
List<Token> originalTokens = StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList());
|
||||||
@@ -52,6 +52,9 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
|||||||
while (originalTokens.size() > 0)
|
while (originalTokens.size() > 0)
|
||||||
{
|
{
|
||||||
Token current = originalTokens.remove(originalTokens.size()-1);
|
Token current = originalTokens.remove(originalTokens.size()-1);
|
||||||
|
if (current.type == Token.TokenType.MARKER && current.surface.startsWith("//"))
|
||||||
|
continue;
|
||||||
|
// skipping comments
|
||||||
if (!isSemicolon(current)
|
if (!isSemicolon(current)
|
||||||
|| (last != null && last.type != Token.TokenType.CLOSE_PAREN && last.type != Token.TokenType.COMMA && !isSemicolon(last)))
|
|| (last != null && last.type != Token.TokenType.CLOSE_PAREN && last.type != Token.TokenType.COMMA && !isSemicolon(last)))
|
||||||
{
|
{
|
||||||
@@ -60,9 +63,27 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
|||||||
current.surface = ";";
|
current.surface = ";";
|
||||||
current.type = Token.TokenType.OPERATOR;
|
current.type = Token.TokenType.OPERATOR;
|
||||||
}
|
}
|
||||||
|
if (current.type == Token.TokenType.MARKER)
|
||||||
|
{
|
||||||
|
// dealing with tokens in reversed order
|
||||||
|
if ("{".equals(current.surface))
|
||||||
|
{
|
||||||
|
cleanedTokens.add(current.morphedInto(Token.TokenType.OPEN_PAREN, "("));
|
||||||
|
current.morph(Token.TokenType.FUNCTION, "m");
|
||||||
|
}
|
||||||
|
else if ("[".equals(current.surface))
|
||||||
|
{
|
||||||
|
cleanedTokens.add(current.morphedInto(Token.TokenType.OPEN_PAREN, "("));
|
||||||
|
current.morph(Token.TokenType.FUNCTION, "l");
|
||||||
|
}
|
||||||
|
else if ("}".equals(current.surface) || "]".equals(current.surface))
|
||||||
|
{
|
||||||
|
current.morph(Token.TokenType.CLOSE_PAREN, ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
cleanedTokens.add(current);
|
cleanedTokens.add(current);
|
||||||
}
|
}
|
||||||
if (current.type != Token.TokenType.MARKER)
|
if (!(current.type == Token.TokenType.MARKER && current.surface.equals("$")))
|
||||||
last = current;
|
last = current;
|
||||||
}
|
}
|
||||||
Collections.reverse(cleanedTokens);
|
Collections.reverse(cleanedTokens);
|
||||||
@@ -242,7 +263,8 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
|||||||
}
|
}
|
||||||
token.type = ch == '(' ? Token.TokenType.FUNCTION : Token.TokenType.VARIABLE;
|
token.type = ch == '(' ? Token.TokenType.FUNCTION : Token.TokenType.VARIABLE;
|
||||||
}
|
}
|
||||||
else if (ch == '(' || ch == ')' || ch == ',')
|
else if (ch == '(' || ch == ')' || ch == ',' ||
|
||||||
|
ch == '{' || ch == '}' || ch == '[' || ch == ']')
|
||||||
{
|
{
|
||||||
if (ch == '(')
|
if (ch == '(')
|
||||||
{
|
{
|
||||||
@@ -252,17 +274,21 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
|||||||
{
|
{
|
||||||
token.type = Token.TokenType.CLOSE_PAREN;
|
token.type = Token.TokenType.CLOSE_PAREN;
|
||||||
}
|
}
|
||||||
else
|
else if (ch == ',')
|
||||||
{
|
{
|
||||||
token.type = Token.TokenType.COMMA;
|
token.type = Token.TokenType.COMMA;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
token.type = Token.TokenType.MARKER;
|
||||||
|
}
|
||||||
token.append(ch);
|
token.append(ch);
|
||||||
pos++;
|
pos++;
|
||||||
linepos++;
|
linepos++;
|
||||||
|
|
||||||
if (expression != null && context != null && previousToken != null &&
|
if (expression != null && context != null && previousToken != null &&
|
||||||
previousToken.type == Token.TokenType.OPERATOR &&
|
previousToken.type == Token.TokenType.OPERATOR &&
|
||||||
(ch == ')' || ch == ',') &&
|
(ch == ')' || ch == ',' || ch == ']' || ch == '}') &&
|
||||||
!previousToken.surface.equalsIgnoreCase(";")
|
!previousToken.surface.equalsIgnoreCase(";")
|
||||||
)
|
)
|
||||||
throw new ExpressionException(context, this.expression, previousToken,
|
throw new ExpressionException(context, this.expression, previousToken,
|
||||||
@@ -326,7 +352,9 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (previousToken == null || previousToken.type == Token.TokenType.OPERATOR
|
if (previousToken == null || previousToken.type == Token.TokenType.OPERATOR
|
||||||
|| previousToken.type == Token.TokenType.OPEN_PAREN || previousToken.type == Token.TokenType.COMMA)
|
|| previousToken.type == Token.TokenType.OPEN_PAREN || previousToken.type == Token.TokenType.COMMA
|
||||||
|
|| (previousToken.type == Token.TokenType.MARKER && ( previousToken.surface.equals("{") || previousToken.surface.equals("[") ) )
|
||||||
|
)
|
||||||
{
|
{
|
||||||
token.surface += "u";
|
token.surface += "u";
|
||||||
token.type = Token.TokenType.UNARY_OPERATOR;
|
token.type = Token.TokenType.UNARY_OPERATOR;
|
||||||
@@ -376,6 +404,22 @@ 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 Token morphedInto(TokenType newType, String newSurface)
|
||||||
|
{
|
||||||
|
Token created = new Token();
|
||||||
|
created.surface = newSurface;
|
||||||
|
created.type = newType;
|
||||||
|
created.pos = pos;
|
||||||
|
created.linepos = linepos;
|
||||||
|
created.lineno= lineno;
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void morph(TokenType type, String s)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
this.surface = s;
|
||||||
|
}
|
||||||
|
|
||||||
public void append(char c)
|
public void append(char c)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,16 +39,13 @@ public class ShapesRenderer
|
|||||||
{
|
{
|
||||||
this.client = minecraftClient;
|
this.client = minecraftClient;
|
||||||
shapes = new HashMap<>();
|
shapes = new HashMap<>();
|
||||||
shapes.put(DimensionType.OVERWORLD, new Long2ObjectOpenHashMap<>());
|
|
||||||
shapes.put(DimensionType.THE_NETHER, new Long2ObjectOpenHashMap<>());
|
|
||||||
shapes.put(DimensionType.THE_END, new Long2ObjectOpenHashMap<>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(Camera camera, float partialTick)
|
public void render(Camera camera, float partialTick)
|
||||||
{
|
{
|
||||||
IWorld iWorld = this.client.world;
|
IWorld iWorld = this.client.world;
|
||||||
DimensionType dimensionType = iWorld.getDimension().getType();
|
DimensionType dimensionType = iWorld.getDimension().getType();
|
||||||
if (shapes.get(dimensionType).isEmpty()) return;
|
if (shapes.get(dimensionType) == null || shapes.get(dimensionType).isEmpty()) return;
|
||||||
long currentTime = client.world.getTime();
|
long currentTime = client.world.getTime();
|
||||||
RenderSystem.disableTexture();
|
RenderSystem.disableTexture();
|
||||||
RenderSystem.enableDepthTest();
|
RenderSystem.enableDepthTest();
|
||||||
@@ -59,8 +56,7 @@ public class ShapesRenderer
|
|||||||
//RenderSystem.shadeModel(7425);
|
//RenderSystem.shadeModel(7425);
|
||||||
RenderSystem.shadeModel(GL11.GL_FLAT);
|
RenderSystem.shadeModel(GL11.GL_FLAT);
|
||||||
RenderSystem.enableAlphaTest();
|
RenderSystem.enableAlphaTest();
|
||||||
RenderSystem.defaultAlphaFunc();
|
RenderSystem.alphaFunc(GL11.GL_GREATER, 0.003f);
|
||||||
//RenderSystem.alphaFunc(GL11.GL_GREATER, 0.005F);
|
|
||||||
RenderSystem.disableCull();
|
RenderSystem.disableCull();
|
||||||
RenderSystem.disableLighting();
|
RenderSystem.disableLighting();
|
||||||
RenderSystem.depthMask(false);
|
RenderSystem.depthMask(false);
|
||||||
@@ -131,7 +127,7 @@ public class ShapesRenderer
|
|||||||
long key = rshape.key();
|
long key = rshape.key();
|
||||||
synchronized (shapes)
|
synchronized (shapes)
|
||||||
{
|
{
|
||||||
RenderedShape<?> existing = shapes.get(dim).get(key);
|
RenderedShape<?> existing = shapes.computeIfAbsent(dim, d -> new Long2ObjectOpenHashMap<>()).get(key);
|
||||||
if (existing != null)
|
if (existing != null)
|
||||||
{ // promoting previous shape
|
{ // promoting previous shape
|
||||||
existing.expiryTick = rshape.expiryTick;
|
existing.expiryTick = rshape.expiryTick;
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ import net.minecraft.block.Blocks;
|
|||||||
import net.minecraft.item.ItemPlacementContext;
|
import net.minecraft.item.ItemPlacementContext;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.StringTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.state.property.Property;
|
||||||
import net.minecraft.util.GlobalPos;
|
import net.minecraft.util.GlobalPos;
|
||||||
import net.minecraft.util.Hand;
|
import net.minecraft.util.Hand;
|
||||||
import net.minecraft.util.hit.BlockHitResult;
|
import net.minecraft.util.hit.BlockHitResult;
|
||||||
@@ -24,8 +27,10 @@ import net.minecraft.world.World;
|
|||||||
import net.minecraft.world.chunk.WorldChunk;
|
import net.minecraft.world.chunk.WorldChunk;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -180,6 +185,33 @@ public class BlockValue extends Value
|
|||||||
|
|
||||||
public ServerWorld getWorld() { return world;}
|
public ServerWorld getWorld() { return world;}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tag toTag(boolean force)
|
||||||
|
{
|
||||||
|
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||||
|
// follows falling block convertion
|
||||||
|
CompoundTag tag = new CompoundTag();
|
||||||
|
CompoundTag state = new CompoundTag();
|
||||||
|
BlockState s = getBlockState();
|
||||||
|
state.put("Name", StringTag.of(Registry.BLOCK.getId(s.getBlock()).toString()));
|
||||||
|
Collection<Property<?>> properties = s.getProperties();
|
||||||
|
if (!properties.isEmpty())
|
||||||
|
{
|
||||||
|
CompoundTag props = new CompoundTag();
|
||||||
|
for (Property<?> p: properties)
|
||||||
|
{
|
||||||
|
props.put(p.getName(), StringTag.of(s.get(p).toString().toLowerCase(Locale.ROOT)));
|
||||||
|
}
|
||||||
|
state.put("Properties", props);
|
||||||
|
}
|
||||||
|
tag.put("BlockState", state);
|
||||||
|
CompoundTag dataTag = getData();
|
||||||
|
if (dataTag != null)
|
||||||
|
{
|
||||||
|
tag.put("TileEntityData", dataTag);
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
public enum SpecificDirection {
|
public enum SpecificDirection {
|
||||||
UP("up",0.5, 0.0, 0.5, Direction.UP),
|
UP("up",0.5, 0.0, 0.5, Direction.UP),
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package carpet.script.value;
|
package carpet.script.value;
|
||||||
|
|
||||||
import carpet.CarpetSettings;
|
|
||||||
import carpet.fakes.EntityInterface;
|
import carpet.fakes.EntityInterface;
|
||||||
import carpet.fakes.ItemEntityInterface;
|
import carpet.fakes.ItemEntityInterface;
|
||||||
import carpet.fakes.LivingEntityInterface;
|
import carpet.fakes.LivingEntityInterface;
|
||||||
@@ -12,10 +11,10 @@ import carpet.script.CarpetContext;
|
|||||||
import carpet.script.EntityEventsGroup;
|
import carpet.script.EntityEventsGroup;
|
||||||
import carpet.script.argument.Vector3Argument;
|
import carpet.script.argument.Vector3Argument;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
import carpet.utils.Messenger;
|
|
||||||
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.entity.player.HungerManager;
|
import net.minecraft.nbt.StringTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket;
|
import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket;
|
||||||
import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket;
|
import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket;
|
||||||
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
|
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
|
||||||
@@ -61,6 +60,7 @@ 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.UUID;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
@@ -87,11 +87,11 @@ public class EntityValue extends Value
|
|||||||
EntitySelector entitySelector = selectorCache.get(selector);
|
EntitySelector entitySelector = selectorCache.get(selector);
|
||||||
if (entitySelector != null)
|
if (entitySelector != null)
|
||||||
{
|
{
|
||||||
return entitySelector.getEntities(source);
|
return entitySelector.getEntities(source.withMaxLevel(4));
|
||||||
}
|
}
|
||||||
entitySelector = new EntitySelectorReader(new StringReader(selector), true).read();
|
entitySelector = new EntitySelectorReader(new StringReader(selector), true).read();
|
||||||
selectorCache.put(selector, entitySelector);
|
selectorCache.put(selector, entitySelector);
|
||||||
return entitySelector.getEntities(source);
|
return entitySelector.getEntities(source.withMaxLevel(4));
|
||||||
}
|
}
|
||||||
catch (CommandSyntaxException e)
|
catch (CommandSyntaxException e)
|
||||||
{
|
{
|
||||||
@@ -289,6 +289,10 @@ public class EntityValue extends Value
|
|||||||
put("sneaking", (e, a) -> e.isSneaking()?Value.TRUE:Value.FALSE);
|
put("sneaking", (e, a) -> e.isSneaking()?Value.TRUE:Value.FALSE);
|
||||||
put("sprinting", (e, a) -> e.isSprinting()?Value.TRUE:Value.FALSE);
|
put("sprinting", (e, a) -> e.isSprinting()?Value.TRUE:Value.FALSE);
|
||||||
put("swimming", (e, a) -> e.isSwimming()?Value.TRUE:Value.FALSE);
|
put("swimming", (e, a) -> e.isSwimming()?Value.TRUE:Value.FALSE);
|
||||||
|
put("persistence", (e, a) -> {
|
||||||
|
if (e instanceof MobEntity) return new NumericValue(((MobEntity) e).isPersistent());
|
||||||
|
return Value.NULL;
|
||||||
|
});
|
||||||
put("hunger", (e, a) -> {
|
put("hunger", (e, a) -> {
|
||||||
if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).getHungerManager().getFoodLevel());
|
if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).getHungerManager().getFoodLevel());
|
||||||
return Value.NULL;
|
return Value.NULL;
|
||||||
@@ -520,6 +524,10 @@ public class EntityValue extends Value
|
|||||||
{
|
{
|
||||||
throw new InternalExpressionException("'modify' for '"+what+"' expects a value");
|
throw new InternalExpressionException("'modify' for '"+what+"' expects a value");
|
||||||
}
|
}
|
||||||
|
catch (IndexOutOfBoundsException ind)
|
||||||
|
{
|
||||||
|
throw new InternalExpressionException("Wrong number of arguments for `modify` option: "+what);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void updatePosition(Entity e, double x, double y, double z, float yaw, float pitch)
|
private static void updatePosition(Entity e, double x, double y, double z, float yaw, float pitch)
|
||||||
@@ -715,9 +723,23 @@ public class EntityValue extends Value
|
|||||||
e.setCustomName(null);
|
e.setCustomName(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
e.setCustomNameVisible(true);
|
boolean showName = false;
|
||||||
|
if (v instanceof ListValue)
|
||||||
|
{
|
||||||
|
showName = ((ListValue) v).getItems().get(1).getBoolean();
|
||||||
|
v = ((ListValue) v).getItems().get(0);
|
||||||
|
}
|
||||||
|
e.setCustomNameVisible(showName);
|
||||||
e.setCustomName(new LiteralText(v.getString()));
|
e.setCustomName(new LiteralText(v.getString()));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
put("persistence", (e, v) ->
|
||||||
|
{
|
||||||
|
if (!(e instanceof MobEntity)) return;
|
||||||
|
if (v == null) v = Value.TRUE;
|
||||||
|
((MobEntityInterface)e).setPersistence(v.getBoolean());
|
||||||
|
});
|
||||||
|
|
||||||
put("dismount", (e, v) -> e.stopRiding() );
|
put("dismount", (e, v) -> e.stopRiding() );
|
||||||
put("mount", (e, v) -> {
|
put("mount", (e, v) -> {
|
||||||
if (v instanceof EntityValue)
|
if (v instanceof EntityValue)
|
||||||
@@ -976,22 +998,37 @@ public class EntityValue extends Value
|
|||||||
if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setSaturationLevelClient((float)NumericValue.asNumber(v).getLong());
|
if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setSaturationLevelClient((float)NumericValue.asNumber(v).getLong());
|
||||||
});
|
});
|
||||||
|
|
||||||
// gamemode [check]
|
put("nbt", (e, v) -> {
|
||||||
// spectate [check]
|
if (!(e instanceof PlayerEntity))
|
||||||
// "fire" [check]
|
{
|
||||||
// "extinguish" [set fire ticks to 0]
|
UUID uUID = e.getUuid();
|
||||||
// "silent" [check]
|
Value tagValue = NBTSerializableValue.fromValue(v);
|
||||||
// "gravity" [check]
|
if (tagValue instanceof NBTSerializableValue)
|
||||||
// "invulnerable" [check]
|
{
|
||||||
|
e.fromTag(((NBTSerializableValue) tagValue).getCompoundTag());
|
||||||
|
e.setUuid(uUID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
put("nbt_merge", (e, v) -> {
|
||||||
|
if (!(e instanceof PlayerEntity))
|
||||||
|
{
|
||||||
|
UUID uUID = e.getUuid();
|
||||||
|
Value tagValue = NBTSerializableValue.fromValue(v);
|
||||||
|
if (tagValue instanceof NBTSerializableValue)
|
||||||
|
{
|
||||||
|
CompoundTag nbttagcompound = e.toTag((new CompoundTag()));
|
||||||
|
nbttagcompound.copyFrom(((NBTSerializableValue) tagValue).getCompoundTag());
|
||||||
|
e.fromTag(nbttagcompound);
|
||||||
|
e.setUuid(uUID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// "dimension" []
|
// "dimension" []
|
||||||
// "item" []
|
// "item" []
|
||||||
// "count", []
|
// "count", []
|
||||||
// "age", [check]
|
|
||||||
// "effect_"name []
|
// "effect_"name []
|
||||||
// "hold" [inventory_set?]
|
|
||||||
// "hold_offhand" [inventory_set?]
|
|
||||||
// "jump" [check]
|
|
||||||
// "nbt" <-big one, for now use run('data merge entity ...
|
|
||||||
}};
|
}};
|
||||||
|
|
||||||
public void setEvent(CarpetContext cc, String eventName, FunctionValue fun, List<Value> args)
|
public void setEvent(CarpetContext cc, String eventName, FunctionValue fun, List<Value> args)
|
||||||
@@ -1001,4 +1038,14 @@ public class EntityValue extends Value
|
|||||||
throw new InternalExpressionException("Unknown entity event: " + eventName);
|
throw new InternalExpressionException("Unknown entity event: " + eventName);
|
||||||
((EntityInterface)entity).getEventContainer().addEvent(event, cc, fun, args);
|
((EntityInterface)entity).getEventContainer().addEvent(event, cc, fun, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tag toTag(boolean force)
|
||||||
|
{
|
||||||
|
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||||
|
CompoundTag tag = new CompoundTag();
|
||||||
|
tag.put("Data", getEntity().toTag( new CompoundTag()));
|
||||||
|
tag.put("Name", StringTag.of(Registry.ENTITY_TYPE.getId(entity.getType()).toString()));
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
package carpet.script.value;
|
package carpet.script.value;
|
||||||
|
|
||||||
|
import net.minecraft.nbt.StringTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.text.BaseText;
|
import net.minecraft.text.BaseText;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
|
||||||
public class FormattedTextValue extends StringValue
|
public class FormattedTextValue extends StringValue
|
||||||
{
|
{
|
||||||
@@ -37,4 +40,11 @@ public class FormattedTextValue extends StringValue
|
|||||||
{
|
{
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tag toTag(boolean force)
|
||||||
|
{
|
||||||
|
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||||
|
return StringTag.of(Text.Serializer.toJson(text));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package carpet.script.value;
|
package carpet.script.value;
|
||||||
|
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
|
||||||
public abstract class FrameworkValue extends Value
|
public abstract class FrameworkValue extends Value
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
@@ -25,4 +27,10 @@ public abstract class FrameworkValue extends Value
|
|||||||
{
|
{
|
||||||
throw new UnsupportedOperationException("Scarpet language component cannot be used as map key");
|
throw new UnsupportedOperationException("Scarpet language component cannot be used as map key");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tag toTag(boolean force)
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException("Scarpet language component cannot be serialized to the tag");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,8 @@ import carpet.script.exception.ExpressionException;
|
|||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
import carpet.script.exception.ReturnStatement;
|
import carpet.script.exception.ReturnStatement;
|
||||||
import carpet.script.exception.ThrowStatement;
|
import carpet.script.exception.ThrowStatement;
|
||||||
|
import net.minecraft.nbt.StringTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -94,6 +96,8 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
|||||||
return name.hashCode()+(int)variant;
|
return name.hashCode()+(int)variant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o)
|
public boolean equals(Object o)
|
||||||
{
|
{
|
||||||
@@ -231,4 +235,11 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
|||||||
{
|
{
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tag toTag(boolean force)
|
||||||
|
{
|
||||||
|
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||||
|
return StringTag.of(getString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package carpet.script.value;
|
package carpet.script.value;
|
||||||
|
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
|
||||||
public class GlobalValue extends Value
|
public class GlobalValue extends Value
|
||||||
{
|
{
|
||||||
@@ -28,4 +29,10 @@ public class GlobalValue extends Value
|
|||||||
{
|
{
|
||||||
throw new UnsupportedOperationException("Global value cannot be used as a map key");
|
throw new UnsupportedOperationException("Global value cannot be used as a map key");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tag toTag(boolean force)
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException("Global value cannot be serialized to the tag");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package carpet.script.value;
|
package carpet.script.value;
|
||||||
|
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import net.minecraft.nbt.StringTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@@ -152,4 +154,11 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
|
|||||||
{
|
{
|
||||||
return ("i"+getString()).hashCode();
|
return ("i"+getString()).hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tag toTag(boolean force)
|
||||||
|
{
|
||||||
|
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||||
|
return StringTag.of(getString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,13 +3,23 @@ package carpet.script.value;
|
|||||||
import carpet.script.LazyValue;
|
import carpet.script.LazyValue;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.nbt.AbstractNumberTag;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.DoubleTag;
|
||||||
|
import net.minecraft.nbt.IntTag;
|
||||||
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.nbt.LongTag;
|
||||||
|
import net.minecraft.nbt.StringTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.util.registry.Registry;
|
import net.minecraft.util.registry.Registry;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
@@ -98,6 +108,13 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
|||||||
{
|
{
|
||||||
return ListValue.wrap(Arrays.asList(list));
|
return ListValue.wrap(Arrays.asList(list));
|
||||||
}
|
}
|
||||||
|
public static ListValue ofNums(Number ... list)
|
||||||
|
{
|
||||||
|
List<Value> valList = new ArrayList<>();
|
||||||
|
for (Number i : list)
|
||||||
|
valList.add(new NumericValue(i.doubleValue()));
|
||||||
|
return ListValue.wrap(valList);
|
||||||
|
}
|
||||||
|
|
||||||
public static LazyValue lazyEmpty()
|
public static LazyValue lazyEmpty()
|
||||||
{
|
{
|
||||||
@@ -447,4 +464,65 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
|||||||
{
|
{
|
||||||
return items.hashCode();
|
return items.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum TagTypeCompat
|
||||||
|
{
|
||||||
|
INT,
|
||||||
|
LONG,
|
||||||
|
DBL,
|
||||||
|
LIST,
|
||||||
|
MAP,
|
||||||
|
STRING;
|
||||||
|
private static TagTypeCompat getType(Tag tag)
|
||||||
|
{
|
||||||
|
if (tag instanceof IntTag) return INT;
|
||||||
|
if (tag instanceof LongTag) return LONG;
|
||||||
|
if (tag instanceof DoubleTag) return DBL;
|
||||||
|
if (tag instanceof ListTag) return LIST;
|
||||||
|
if (tag instanceof CompoundTag) return MAP;
|
||||||
|
return STRING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tag toTag(boolean force)
|
||||||
|
{
|
||||||
|
int argSize = items.size();
|
||||||
|
if (argSize == 0) return new ListTag();
|
||||||
|
ListTag tag = new ListTag();
|
||||||
|
if (argSize ==1)
|
||||||
|
{
|
||||||
|
tag.add(items.get(0).toTag(force));
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
// figuring out the types
|
||||||
|
List<Tag> tags= new ArrayList<>();
|
||||||
|
items.forEach(v -> tags.add(v.toTag(force)));
|
||||||
|
Set<TagTypeCompat> cases = EnumSet.noneOf(TagTypeCompat.class);
|
||||||
|
tags.forEach(t -> cases.add(TagTypeCompat.getType(t)));
|
||||||
|
if (cases.size()==1) // well, one type of items
|
||||||
|
{
|
||||||
|
tag.addAll(tags);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
if (cases.contains(TagTypeCompat.LIST)
|
||||||
|
|| cases.contains(TagTypeCompat.MAP)
|
||||||
|
|| cases.contains(TagTypeCompat.STRING)) // incompatible types
|
||||||
|
{
|
||||||
|
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||||
|
tags.forEach(t -> tag.add(StringTag.of(t.asString())));
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
// only numbers / mixed types
|
||||||
|
if (cases.contains(TagTypeCompat.DBL))
|
||||||
|
{
|
||||||
|
tags.forEach(t -> tag.add(DoubleTag.of(((AbstractNumberTag)t).getDouble())));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tags.forEach(t -> tag.add(LongTag.of(((AbstractNumberTag)t).getLong())));
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package carpet.script.value;
|
package carpet.script.value;
|
||||||
|
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -226,4 +228,16 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
|
|||||||
{
|
{
|
||||||
return map.hashCode();
|
return map.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tag toTag(boolean force)
|
||||||
|
{
|
||||||
|
CompoundTag tag = new CompoundTag() ;
|
||||||
|
map.forEach( (k, v) ->
|
||||||
|
{
|
||||||
|
if (!force && !(k instanceof StringValue)) throw new NBTSerializableValue.IncompatibleTypeException(k);
|
||||||
|
tag.put(k.getString(), v.toTag(force));
|
||||||
|
});
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -33,6 +33,7 @@ import net.minecraft.util.Identifier;
|
|||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Box;
|
import net.minecraft.util.math.Box;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -48,6 +49,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
private String nbtString = null;
|
private String nbtString = null;
|
||||||
private Tag nbtTag = null;
|
private Tag nbtTag = null;
|
||||||
private Supplier<Tag> nbtSupplier = null;
|
private Supplier<Tag> nbtSupplier = null;
|
||||||
|
private boolean owned = false;
|
||||||
|
|
||||||
private NBTSerializableValue() {}
|
private NBTSerializableValue() {}
|
||||||
|
|
||||||
@@ -64,11 +66,18 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
throw new InternalExpressionException("Incorrect NBT data: "+nbtString);
|
throw new InternalExpressionException("Incorrect NBT data: "+nbtString);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
owned = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NBTSerializableValue(Tag tag)
|
public NBTSerializableValue(Tag tag)
|
||||||
{
|
{
|
||||||
nbtTag = tag;
|
nbtTag = tag;
|
||||||
|
owned = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NBTSerializableValue(Supplier<Tag> tagSupplier)
|
||||||
|
{
|
||||||
|
nbtSupplier = tagSupplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Value fromStack(ItemStack stack)
|
public static Value fromStack(ItemStack stack)
|
||||||
@@ -100,7 +109,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
}
|
}
|
||||||
catch (CommandSyntaxException e)
|
catch (CommandSyntaxException e)
|
||||||
{
|
{
|
||||||
return null;
|
throw new InternalExpressionException("Incorrect NBT tag: nbtString");
|
||||||
}
|
}
|
||||||
NBTSerializableValue value = new NBTSerializableValue(tag);
|
NBTSerializableValue value = new NBTSerializableValue(tag);
|
||||||
value.nbtString = nbtString;
|
value.nbtString = nbtString;
|
||||||
@@ -115,6 +124,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
NBTSerializableValue copy = new NBTSerializableValue(nbtTag);
|
NBTSerializableValue copy = new NBTSerializableValue(nbtTag);
|
||||||
copy.nbtSupplier = this.nbtSupplier;
|
copy.nbtSupplier = this.nbtSupplier;
|
||||||
copy.nbtString = this.nbtString;
|
copy.nbtString = this.nbtString;
|
||||||
|
copy.owned = this.owned;
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,9 +132,8 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
public Value deepcopy()
|
public Value deepcopy()
|
||||||
{
|
{
|
||||||
NBTSerializableValue copy = (NBTSerializableValue) clone();
|
NBTSerializableValue copy = (NBTSerializableValue) clone();
|
||||||
// same fields except when tag is set - need to copy it.
|
copy.owned = false;
|
||||||
if (copy.nbtTag != null)
|
ensureOwnership();
|
||||||
copy.nbtTag = copy.getTag().copy();
|
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +299,50 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
private static Value decodeTag(Tag t)
|
private static Value decodeTag(Tag t)
|
||||||
{
|
{
|
||||||
if (t instanceof CompoundTag)
|
if (t instanceof CompoundTag)
|
||||||
return new NBTSerializableValue(t);
|
return new NBTSerializableValue(() -> t);
|
||||||
|
if (t instanceof AbstractNumberTag)
|
||||||
|
return new NumericValue(((AbstractNumberTag) t).getDouble());
|
||||||
|
// more can be done here
|
||||||
|
return new StringValue(t.asString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Value toValue()
|
||||||
|
{
|
||||||
|
return decodeTagDeep(this.getTag());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Value fromValue(Value v)
|
||||||
|
{
|
||||||
|
if (v instanceof NBTSerializableValue)
|
||||||
|
return v;
|
||||||
|
if (v instanceof NullValue)
|
||||||
|
return Value.NULL;
|
||||||
|
return NBTSerializableValue.parseString(v.getString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Value decodeTagDeep(Tag t)
|
||||||
|
{
|
||||||
|
if (t instanceof CompoundTag)
|
||||||
|
{
|
||||||
|
Map<Value, Value> pairs = new HashMap<>();
|
||||||
|
CompoundTag ctag = (CompoundTag)t;
|
||||||
|
for (String key: ctag.getKeys())
|
||||||
|
{
|
||||||
|
pairs.put(new StringValue(key), decodeTagDeep(ctag.get(key)));
|
||||||
|
}
|
||||||
|
return MapValue.wrap(pairs);
|
||||||
|
}
|
||||||
|
if (t instanceof AbstractListTag)
|
||||||
|
{
|
||||||
|
List<Value> elems = new ArrayList<>();
|
||||||
|
AbstractListTag<? extends Tag> ltag = (AbstractListTag<? extends Tag>)t;
|
||||||
|
for (Tag elem: ltag)
|
||||||
|
{
|
||||||
|
elems.add(decodeTagDeep(elem));
|
||||||
|
}
|
||||||
|
return ListValue.wrap(elems);
|
||||||
|
}
|
||||||
if (t instanceof AbstractNumberTag)
|
if (t instanceof AbstractNumberTag)
|
||||||
return new NumericValue(((AbstractNumberTag) t).getDouble());
|
return new NumericValue(((AbstractNumberTag) t).getDouble());
|
||||||
// more can be done here
|
// more can be done here
|
||||||
@@ -304,13 +356,6 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
return nbtTag;
|
return nbtTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void replaceTag(Tag newTag)
|
|
||||||
{
|
|
||||||
nbtTag = newTag;
|
|
||||||
nbtString = null;
|
|
||||||
nbtSupplier = null; // just to be sure
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object o)
|
public boolean equals(final Object o)
|
||||||
{
|
{
|
||||||
@@ -346,6 +391,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
ensureOwnership();
|
||||||
return (CompoundTag) getTag();
|
return (CompoundTag) getTag();
|
||||||
}
|
}
|
||||||
catch (ClassCastException e)
|
catch (ClassCastException e)
|
||||||
@@ -364,12 +410,12 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
public boolean put(Value where, Value value, Value conditions)
|
public boolean put(Value where, Value value, Value conditions)
|
||||||
{
|
{
|
||||||
/// WIP
|
/// WIP
|
||||||
|
ensureOwnership();
|
||||||
NbtPathArgumentType.NbtPath path = cachePath(where.getString());
|
NbtPathArgumentType.NbtPath path = cachePath(where.getString());
|
||||||
Tag tagToInsert = value instanceof NBTSerializableValue ?
|
Tag tagToInsert = value instanceof NBTSerializableValue ?
|
||||||
((NBTSerializableValue) value).getTag() :
|
((NBTSerializableValue) value).getTag() :
|
||||||
new NBTSerializableValue(value.getString()).getTag();
|
new NBTSerializableValue(value.getString()).getTag();
|
||||||
Tag modifiedTag;
|
boolean modifiedTag;
|
||||||
if (conditions instanceof NumericValue)
|
if (conditions instanceof NumericValue)
|
||||||
{
|
{
|
||||||
modifiedTag = modify_insert((int)((NumericValue) conditions).getLong(), path, tagToInsert);
|
modifiedTag = modify_insert((int)((NumericValue) conditions).getLong(), path, tagToInsert);
|
||||||
@@ -390,22 +436,17 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (modifiedTag != null)
|
return modifiedTag;
|
||||||
{
|
|
||||||
replaceTag(modifiedTag);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private Tag modify_insert(int index, NbtPathArgumentType.NbtPath nbtPath, Tag newElement)
|
private boolean modify_insert(int index, NbtPathArgumentType.NbtPath nbtPath, Tag newElement)
|
||||||
{
|
{
|
||||||
return modify_insert(index, nbtPath, newElement, this.getTag().copy());
|
return modify_insert(index, nbtPath, newElement, this.getTag());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tag modify_insert(int index, NbtPathArgumentType.NbtPath nbtPath, Tag newElement, Tag currentTag)
|
private boolean modify_insert(int index, NbtPathArgumentType.NbtPath nbtPath, Tag newElement, Tag currentTag)
|
||||||
{
|
{
|
||||||
Collection<Tag> targets;
|
Collection<Tag> targets;
|
||||||
try
|
try
|
||||||
@@ -414,7 +455,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
}
|
}
|
||||||
catch (CommandSyntaxException e)
|
catch (CommandSyntaxException e)
|
||||||
{
|
{
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean modified = false;
|
boolean modified = false;
|
||||||
@@ -428,27 +469,27 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
{
|
{
|
||||||
AbstractListTag<?> targetList = (AbstractListTag) target;
|
AbstractListTag<?> targetList = (AbstractListTag) target;
|
||||||
if (!targetList.addTag(index < 0 ? targetList.size() + index + 1 : index, newElement.copy()))
|
if (!targetList.addTag(index < 0 ? targetList.size() + index + 1 : index, newElement.copy()))
|
||||||
return null;
|
return false;
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
catch (IndexOutOfBoundsException ignored)
|
catch (IndexOutOfBoundsException ignored)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return modified?currentTag:null;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Tag modify_merge(NbtPathArgumentType.NbtPath nbtPath, Tag replacement) //nbtPathArgumentType$NbtPath_1, list_1)
|
private boolean modify_merge(NbtPathArgumentType.NbtPath nbtPath, Tag replacement) //nbtPathArgumentType$NbtPath_1, list_1)
|
||||||
{
|
{
|
||||||
if (!(replacement instanceof CompoundTag))
|
if (!(replacement instanceof CompoundTag))
|
||||||
{
|
{
|
||||||
return getTag();
|
return false;
|
||||||
}
|
}
|
||||||
Tag originalTag = getTag().copy();
|
Tag ownTag = getTag();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
for (Tag target : nbtPath.getOrInit(originalTag, CompoundTag::new))
|
for (Tag target : nbtPath.getOrInit(ownTag, CompoundTag::new))
|
||||||
{
|
{
|
||||||
if (!(target instanceof CompoundTag))
|
if (!(target instanceof CompoundTag))
|
||||||
{
|
{
|
||||||
@@ -457,23 +498,26 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
((CompoundTag) target).copyFrom((CompoundTag) replacement);
|
((CompoundTag) target).copyFrom((CompoundTag) replacement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (CommandSyntaxException ignored) { }
|
catch (CommandSyntaxException ignored)
|
||||||
return originalTag;
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tag modify_replace(NbtPathArgumentType.NbtPath nbtPath, Tag replacement) //nbtPathArgumentType$NbtPath_1, list_1)
|
private boolean modify_replace(NbtPathArgumentType.NbtPath nbtPath, Tag replacement) //nbtPathArgumentType$NbtPath_1, list_1)
|
||||||
{
|
{
|
||||||
Tag originalTag = getTag().copy();
|
Tag tag = getTag();
|
||||||
String pathText = nbtPath.toString();
|
String pathText = nbtPath.toString();
|
||||||
if (pathText.endsWith("]")) // workaround for array replacement or item in the array replacement
|
if (pathText.endsWith("]")) // workaround for array replacement or item in the array replacement
|
||||||
{
|
{
|
||||||
if (nbtPath.remove(originalTag)==0)
|
if (nbtPath.remove(tag)==0)
|
||||||
return null;
|
return false;
|
||||||
Pattern pattern = Pattern.compile("\\[[^\\[]*]$");
|
Pattern pattern = Pattern.compile("\\[[^\\[]*]$");
|
||||||
Matcher matcher = pattern.matcher(pathText);
|
Matcher matcher = pattern.matcher(pathText);
|
||||||
if (!matcher.find()) // malformed path
|
if (!matcher.find()) // malformed path
|
||||||
{
|
{
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
String arrAccess = matcher.group();
|
String arrAccess = matcher.group();
|
||||||
int pos;
|
int pos;
|
||||||
@@ -487,21 +531,21 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
}
|
}
|
||||||
catch (NumberFormatException e)
|
catch (NumberFormatException e)
|
||||||
{
|
{
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NbtPathArgumentType.NbtPath newPath = cachePath(pathText.substring(0, pathText.length()-arrAccess.length()));
|
NbtPathArgumentType.NbtPath newPath = cachePath(pathText.substring(0, pathText.length()-arrAccess.length()));
|
||||||
return modify_insert(pos,newPath,replacement, originalTag);
|
return modify_insert(pos,newPath,replacement, tag);
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
nbtPath.put(originalTag, () -> replacement);
|
nbtPath.put(tag, () -> replacement);
|
||||||
}
|
}
|
||||||
catch (CommandSyntaxException e)
|
catch (CommandSyntaxException e)
|
||||||
{
|
{
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
return originalTag;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -527,13 +571,23 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
return cachePath(where.getString()).count(getTag()) > 0;
|
return cachePath(where.getString()).count(getTag()) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ensureOwnership()
|
||||||
|
{
|
||||||
|
if (!owned)
|
||||||
|
{
|
||||||
|
nbtTag = getTag().copy();
|
||||||
|
nbtString = null;
|
||||||
|
nbtSupplier = null; // just to be sure
|
||||||
|
owned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean delete(Value where)
|
public boolean delete(Value where)
|
||||||
{
|
{
|
||||||
NbtPathArgumentType.NbtPath path = cachePath(where.getString());
|
NbtPathArgumentType.NbtPath path = cachePath(where.getString());
|
||||||
Tag tag = getTag().copy();
|
ensureOwnership();
|
||||||
int removed = path.remove(tag);
|
int removed = path.remove(getTag());
|
||||||
if (removed > 0) replaceTag(tag);
|
|
||||||
return removed > 0;
|
return removed > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,4 +638,23 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
{
|
{
|
||||||
return "nbt";
|
return "nbt";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tag toTag(boolean force)
|
||||||
|
{
|
||||||
|
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||||
|
ensureOwnership();
|
||||||
|
return getTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class IncompatibleTypeException extends RuntimeException
|
||||||
|
{
|
||||||
|
private IncompatibleTypeException() {}
|
||||||
|
public Value val;
|
||||||
|
public IncompatibleTypeException(Value val)
|
||||||
|
{
|
||||||
|
this.val = val;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package carpet.script.value;
|
package carpet.script.value;
|
||||||
|
|
||||||
|
import net.minecraft.nbt.StringTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
|
||||||
public class NullValue extends NumericValue // TODO check nonsingleton code
|
public class NullValue extends NumericValue // TODO check nonsingleton code
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
@@ -50,4 +53,11 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tag toTag(boolean force)
|
||||||
|
{
|
||||||
|
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||||
|
return StringTag.of("null");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
package carpet.script.value;
|
package carpet.script.value;
|
||||||
|
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import net.minecraft.nbt.DoubleTag;
|
||||||
|
import net.minecraft.nbt.IntTag;
|
||||||
|
import net.minecraft.nbt.LongTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@@ -204,4 +208,20 @@ public class NumericValue extends Value
|
|||||||
{
|
{
|
||||||
return (int)getLong();
|
return (int)getLong();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tag toTag(boolean force)
|
||||||
|
{
|
||||||
|
long longValue = getLong();
|
||||||
|
if (value == (double)longValue)
|
||||||
|
{
|
||||||
|
if (abs(value) < Integer.MAX_VALUE-2)
|
||||||
|
return IntTag.of((int)longValue);
|
||||||
|
return LongTag.of(getLong());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return DoubleTag.of(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package carpet.script.value;
|
package carpet.script.value;
|
||||||
|
|
||||||
|
import net.minecraft.nbt.StringTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
|
||||||
public class StringValue extends Value
|
public class StringValue extends Value
|
||||||
{
|
{
|
||||||
private String str;
|
private String str;
|
||||||
@@ -30,4 +33,10 @@ public class StringValue extends Value
|
|||||||
{
|
{
|
||||||
return "string";
|
return "string";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tag toTag(boolean force)
|
||||||
|
{
|
||||||
|
return StringTag.of(str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ import carpet.script.LazyValue;
|
|||||||
import carpet.script.Tokenizer;
|
import carpet.script.Tokenizer;
|
||||||
import carpet.script.exception.ExpressionException;
|
import carpet.script.exception.ExpressionException;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -144,4 +145,10 @@ public class ThreadValue extends Value
|
|||||||
locks.clear();
|
locks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tag toTag(boolean force)
|
||||||
|
{
|
||||||
|
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||||
|
return getValue().toTag(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package carpet.script.value;
|
package carpet.script.value;
|
||||||
|
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -203,4 +204,6 @@ public abstract class Value implements Comparable<Value>, Cloneable
|
|||||||
throw new InternalExpressionException("Cannot make a copy of value: "+this);
|
throw new InternalExpressionException("Cannot make a copy of value: "+this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract Tag toTag(boolean force);
|
||||||
}
|
}
|
||||||
@@ -470,7 +470,7 @@ public class SettingsManager
|
|||||||
writeSettingsToConf(conf.getLeft()); // this may feels weird, but if conf
|
writeSettingsToConf(conf.getLeft()); // this may feels weird, but if conf
|
||||||
// is locked, it will never reach this point.
|
// is locked, it will never reach this point.
|
||||||
rule.set(source,stringValue);
|
rule.set(source,stringValue);
|
||||||
Messenger.m(source ,"gi "+String.format(tr("ui.rule_%(rule)s_will_now_default_to_%(value)s","rule %s will now default to %s"), rule.translatedName(), stringValue));
|
Messenger.m(source ,"gi "+String.format(tr("ui.rule_%(rule)s_will_now_default_to_%(value)s","Rule %s will now default to %s"), rule.translatedName(), stringValue));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// removes overrides of the default values in the file
|
// removes overrides of the default values in the file
|
||||||
@@ -482,6 +482,7 @@ public class SettingsManager
|
|||||||
conf.getLeft().remove(rule.name);
|
conf.getLeft().remove(rule.name);
|
||||||
writeSettingsToConf(conf.getLeft());
|
writeSettingsToConf(conf.getLeft());
|
||||||
rules.get(rule.name).resetToDefault(source);
|
rules.get(rule.name).resetToDefault(source);
|
||||||
|
Messenger.m(source ,"gi "+String.format(tr("ui.rule_%(rule)s_not_set_restart","Rule %s will now not be set on restart"), rule.translatedName()));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,8 @@
|
|||||||
"ui.tags": "Tags",
|
"ui.tags": "Tags",
|
||||||
"ui.change_permanently": "Change permanently",
|
"ui.change_permanently": "Change permanently",
|
||||||
"ui.click_to_keep_the_settings_in_%(conf)s_to_save_across_restarts": "click to keep the settings in %s to save across restarts",
|
"ui.click_to_keep_the_settings_in_%(conf)s_to_save_across_restarts": "click to keep the settings in %s to save across restarts",
|
||||||
"ui.rule_%(rule)s_will_now_default_to_%(value)s": "rule %s will now default to %s",
|
"ui.rule_%(rule)s_will_now_default_to_%(value)s": "Rule %s will now default to %s",
|
||||||
|
"ui.rule_%(rule)s_not_set_restart": "Rule %s will now not be set on restart",
|
||||||
"ui.current_value": "current value"
|
"ui.current_value": "current value"
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"rule.antiCheatDisabled.name": "移动检测通杀",
|
"rule.antiCheatDisabled.name": "禁用反作弊移动监测",
|
||||||
"rule.antiCheatDisabled.desc": "防止玩家因为“本服务器未启用飞行”,“移动过快”等原因被踢出",
|
"rule.antiCheatDisabled.desc": "防止玩家因为“本服务器未启用飞行”,“移动过快”等原因被踢出",
|
||||||
|
|
||||||
"rule.carpets.name": "放置地毯执行功能",
|
"rule.carpets.name": "多功能地毯",
|
||||||
"rule.carpets.desc": "通过放置地毯方块快捷执行部分功能",
|
"rule.carpets.desc": "通过放置地毯方块快捷执行部分功能",
|
||||||
|
|
||||||
"rule.combineXPOrbs.name": "经验球合并",
|
"rule.combineXPOrbs.name": "经验球合并",
|
||||||
@@ -135,7 +135,7 @@
|
|||||||
"rule.pushLimit.desc": "自定义活塞推动上限",
|
"rule.pushLimit.desc": "自定义活塞推动上限",
|
||||||
"rule.pushLimit.extra.0": "你必须在1-1024中选取一个值",
|
"rule.pushLimit.extra.0": "你必须在1-1024中选取一个值",
|
||||||
|
|
||||||
"rule.quasiConnectivity.name": "qC激活开关",
|
"rule.quasiConnectivity.name": "半连接激活开关",
|
||||||
"rule.quasiConnectivity.desc": "活塞,发射器和投掷器在他们上方方块激活时是否响应",
|
"rule.quasiConnectivity.desc": "活塞,发射器和投掷器在他们上方方块激活时是否响应",
|
||||||
"rule.quasiConnectivity.extra.0": "设置为true为开启 设置为false为禁用",
|
"rule.quasiConnectivity.extra.0": "设置为true为开启 设置为false为禁用",
|
||||||
|
|
||||||
@@ -228,7 +228,8 @@
|
|||||||
"ui.tags": "分类",
|
"ui.tags": "分类",
|
||||||
"ui.change_permanently": "永久更改",
|
"ui.change_permanently": "永久更改",
|
||||||
"ui.click_to_keep_the_settings_in_%(conf)s_to_save_across_restarts": "点击此按钮以使此选项保存于%s,重启后依旧生效",
|
"ui.click_to_keep_the_settings_in_%(conf)s_to_save_across_restarts": "点击此按钮以使此选项保存于%s,重启后依旧生效",
|
||||||
"ui.rule_%(rule)s_will_now_default_to_%(value)s": "规则%s将会被默认设置为%s",
|
"ui.rule_%(rule)s_will_now_default_to_%(value)s": "规则“%s”将会被默认设置为%s",
|
||||||
|
"ui.rule_%(rule)s_not_set_restart": "规则“%s”将在重启后恢复默认值",
|
||||||
"ui.current_value": "当前的值"
|
"ui.current_value": "当前的值"
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -331,7 +331,7 @@ select(num) ->
|
|||||||
);
|
);
|
||||||
global_selected_point = if (global_selected_point == selected_point, null, selected_point);
|
global_selected_point = if (global_selected_point == selected_point, null, selected_point);
|
||||||
global_needs_updating = true;
|
global_needs_updating = true;
|
||||||
// no need to _update since path is still valid
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// player can also punch the mannequin to select/deselect it
|
// player can also punch the mannequin to select/deselect it
|
||||||
@@ -501,7 +501,7 @@ play() ->
|
|||||||
task( _() -> (
|
task( _() -> (
|
||||||
if (global_playing_path, // we don't want to join_task not to lock it just in case. No need to panic here
|
if (global_playing_path, // we don't want to join_task not to lock it just in case. No need to panic here
|
||||||
global_playing_path = false; // cancel current path rendering
|
global_playing_path = false; // cancel current path rendering
|
||||||
sleep(1500); // in case we interrupt previous playbacks
|
sleep(1500);
|
||||||
);
|
);
|
||||||
showing_path = global_showing_path;
|
showing_path = global_showing_path;
|
||||||
hide();
|
hide();
|
||||||
|
|||||||
@@ -244,3 +244,23 @@ __on_player_disconnects(player, reason) ->
|
|||||||
print('__on_player_disconnects(player)');
|
print('__on_player_disconnects(player)');
|
||||||
print('Player '+player+' disconnects because: '+reason)
|
print('Player '+player+' disconnects because: '+reason)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
__on_player_chooses_recipe(player, recipe, full_stack) ->
|
||||||
|
(
|
||||||
|
print('');
|
||||||
|
print('__on_player_chooses_recipe(player, recipe, full_stack)');
|
||||||
|
print('player chooses recipe:');
|
||||||
|
print(' - player: '+player);
|
||||||
|
print(' - recipe: '+recipe);
|
||||||
|
print(' - full stack: '+ full_stack);
|
||||||
|
);
|
||||||
|
|
||||||
|
__on_player_switches_slot(player, from, to) ->
|
||||||
|
(
|
||||||
|
print('');
|
||||||
|
print('__on_player_switches_slot(player, from, to)');
|
||||||
|
print('player switches inventory slot:');
|
||||||
|
print(' - player: '+player);
|
||||||
|
print(' - from: '+ from);
|
||||||
|
print(' - to: '+ to);
|
||||||
|
);
|
||||||
@@ -94,7 +94,7 @@ __structure_renderer(player_name) ->
|
|||||||
l(from, to) = structure_data;
|
l(from, to) = structure_data;
|
||||||
total_size = _euclidean(from, to);
|
total_size = _euclidean(from, to);
|
||||||
density = max(10, total_size/10);
|
density = max(10, total_size/10);
|
||||||
draw_shape('box', 15, 'from', from, 'to', to+1, 'color', 0x00FFFFFF, 'line', 3, 'fill', 0x00FFFF22);
|
draw_shape('box', 15, 'from', from, 'to', to+1, 'color', 0x00FFFFFF, 'line', 3, 'fill', 0x00FFFF12);
|
||||||
structure_pieces = slice(sort_key(structure_pieces, _euclidean_sq((_:2+_:3)/2,ppos)), 0, config:'max_pieces');
|
structure_pieces = slice(sort_key(structure_pieces, _euclidean_sq((_:2+_:3)/2,ppos)), 0, config:'max_pieces');
|
||||||
for (structure_pieces, l(piece, direction, from, to) = _;
|
for (structure_pieces, l(piece, direction, from, to) = _;
|
||||||
r = 255 - floor(128 * _i/config:'max_pieces');
|
r = 255 - floor(128 * _i/config:'max_pieces');
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ accessWidener v1 named
|
|||||||
|
|
||||||
accessible class net/minecraft/server/world/ThreadedAnvilChunkStorage$TicketManager
|
accessible class net/minecraft/server/world/ThreadedAnvilChunkStorage$TicketManager
|
||||||
accessible class net/minecraft/server/world/ServerLightingProvider$Stage
|
accessible class net/minecraft/server/world/ServerLightingProvider$Stage
|
||||||
|
accessible class net/minecraft/recipe/Ingredient$Entry
|
||||||
@@ -169,6 +169,9 @@
|
|||||||
"ServerWorld_scarpetEventMixin",
|
"ServerWorld_scarpetEventMixin",
|
||||||
"HungerManagerMixin_scarpetEventsMixin",
|
"HungerManagerMixin_scarpetEventsMixin",
|
||||||
|
|
||||||
|
"RecipeManager_scarpetMixin",
|
||||||
|
"Ingredient_scarpetMixin",
|
||||||
|
|
||||||
"PistonBlock_movableTEMixin",
|
"PistonBlock_movableTEMixin",
|
||||||
"PistonBlockEntity_movableTEMixin",
|
"PistonBlockEntity_movableTEMixin",
|
||||||
"World_movableTEMixin",
|
"World_movableTEMixin",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"schemaVersion": 1,
|
"schemaVersion": 1,
|
||||||
"id": "carpet",
|
"id": "carpet",
|
||||||
"version": "1.3.29",
|
"version": "1.4.0",
|
||||||
|
|
||||||
"name": "Carpet Mod in Fabric",
|
"name": "Carpet Mod in Fabric",
|
||||||
"description": "Carpet made out of fabric",
|
"description": "Carpet made out of fabric",
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
"sources": "https://github.com/gnembon/fabric-carpet"
|
"sources": "https://github.com/gnembon/fabric-carpet"
|
||||||
},
|
},
|
||||||
|
|
||||||
"license": "GNU LGPL V3",
|
"license": "MIT",
|
||||||
"icon": "assets/carpet/icon.png",
|
"icon": "assets/carpet/icon.png",
|
||||||
|
|
||||||
"environment": "*",
|
"environment": "*",
|
||||||
@@ -24,7 +24,8 @@
|
|||||||
"mixins": [
|
"mixins": [
|
||||||
"carpet.mixins.json"
|
"carpet.mixins.json"
|
||||||
],
|
],
|
||||||
"accesswidener": "carpet.accesswidener",
|
"accessWidener" : "carpet.accesswidener",
|
||||||
|
|
||||||
"depends": {
|
"depends": {
|
||||||
"minecraft": "1.15.x",
|
"minecraft": "1.15.x",
|
||||||
"fabricloader": ">=0.7.1"
|
"fabricloader": ">=0.7.1"
|
||||||
|
|||||||
Reference in new issue
Block a user