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
|
||||
Version 3, 29 June 2007
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
Copyright (c) 2020 gnembon
|
||||
|
||||
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 terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
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.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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
|
||||
SOFTWARE.
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
[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)
|
||||
|
||||
|
||||
+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
|
||||
designed to provide the ability to write custom programs to run within Minecraft
|
||||
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
|
||||
Minecraft related features and could function on its own, and CarpetExpression
|
||||
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)
|
||||
</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
|
||||
|
||||
`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
|
||||
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`
|
||||
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
|
||||
|
||||
<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
|
||||
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,
|
||||
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>
|
||||
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))
|
||||
</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)`
|
||||
|
||||
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 has two uses - as a function definition operator and key-value initializer for maps.
|
||||
|
||||
To organize code better than a flat sequence of operations, one can define functions. Definition is correct
|
||||
if has the following form
|
||||
|
||||
@@ -1207,7 +1271,17 @@ a(lst) -> lst+=1; list = l(1,2,3); a(list); a(list); list // => [1,2,3]
|
||||
</pre>
|
||||
|
||||
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)`
|
||||
|
||||
@@ -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
|
||||
|
||||
### `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
|
||||
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
|
||||
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>
|
||||
l(1,2,'foo') => [1, 2, foo]
|
||||
l() => [] (empty list)
|
||||
l(range(10)) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
l(1, 2) = l(3, 4) => Error: l is not a variable
|
||||
l(foo, bar) = l(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]
|
||||
l(1,2,'foo') <=> [1, 2, 'foo']
|
||||
l() <=> [] (empty list)
|
||||
[range(10)] => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
[1, 2] = [3, 4] => Error: l is not a variable
|
||||
[foo, bar] = [3, 4]; foo==3 && bar==4 => 1
|
||||
[foo, bar, baz] = [2, 4, 6]; [min(foo, bar), baz] = [3, 5]; [foo, bar, baz] => [3, 4, 5]
|
||||
</pre>
|
||||
|
||||
In the last example `l(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
|
||||
In the last example `[min(foo, bar), baz]` creates a valid L-value, as `min(foo, bar)` finds the lower of the
|
||||
variables (in this case `foo`) creating a valid assignable L-list of `[foo, baz]`, and these values
|
||||
will be assigned new values
|
||||
|
||||
### `join(delim, list), join(delim, values ...)`
|
||||
@@ -1516,29 +1595,37 @@ range(5,10) => [5, 6, 7, 8, 9]
|
||||
range(20, 10, -2) => [20, 18, 16, 14, 12]
|
||||
</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
|
||||
|
||||
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:
|
||||
|
||||
### `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
|
||||
treated as keys with no value, same goes with the iterator - creates a map that behaves like a set. If the
|
||||
arguments is a list of lists, they have to have two elements each, and then first is a key, and second, a value
|
||||
|
||||
In map creation context (directly inside `{}` or `m{}` call), `->` operator acts like a pair constructor for simpler
|
||||
syntax providing key value pairs, so the invocation to `{foo -> bar, baz -> quux}` is equivalent to
|
||||
`{l(foo, bar), l(baz, quux)}`, which is equivalent to somewhat older, but more traditional functional form of
|
||||
`m(l(foo, bar),l(baz, quuz))`.
|
||||
|
||||
Internally, `{?}`(list syntax) and `m(?)`(function syntax) are equivalent. `{}` is simply translated to
|
||||
`m()` in the scarpet preprocessing stage. This means that internally the code has always expression syntax despite `{}`
|
||||
not using different kinds of brackets and not being proper operators. This means that `m(}` and `{)` are also valid
|
||||
although not recommended as they will make your code far less readable.
|
||||
|
||||
When converting map value to string, `':'` is used as a key-value separator due to tentative compatibility with NBT
|
||||
notation, meaning in simpler cases maps can be converted to NBT parsable string by calling `str()`. This however
|
||||
does not guarantee a parsable output. To properly convert to NBT value, use `encode_nbt()`.
|
||||
|
||||
<pre>
|
||||
m(1,2,'foo') => {1: null, 2: null, foo: null}
|
||||
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}
|
||||
m(l(1, 2), l(3, 4)) => {1: 2, 3: 4}
|
||||
reduce(range(10), put(_a, _, _*_); _a, m())
|
||||
{1, 2, 'foo'} => {1: null, 2: null, foo: null}
|
||||
m() <=> {} (empty map)
|
||||
{range(10)} => {0: null, 1: null, 2: null, 3: null, 4: null, 5: null, 6: null, 7: null, 8: null, 9: null}
|
||||
m(l(1, 2), l(3, 4)) <=> {1 -> 2, 3 -> 4} => {1: 2, 3: 4}
|
||||
reduce(range(10), put(_a, _, _*_); _a, {})
|
||||
=> {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
|
||||
</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
|
||||
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.
|
||||
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
|
||||
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, block('iron_trapdoor[half=top]')) // also correct, block() provides extra parsing
|
||||
set(x,y,z,'hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') // extra block data
|
||||
set(x,y,z,'hopper', l('facing', 'north'), nbt('{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') ) // same
|
||||
</pre>
|
||||
|
||||
### `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
|
||||
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`.
|
||||
|
||||
### `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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
### `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`.
|
||||
|
||||
@@ -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
|
||||
</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
|
||||
point is specified, it iterates over 27 blocks. If `d` 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.
|
||||
Returns an iterator, just like `range` function that iterates over a rectangular area of blocks. If only center
|
||||
point is specified, it iterates over 27 blocks. If `range` arguments are specified, expands selection by the respective
|
||||
number of blocks in each direction. If `positive_range` arguments are specified,
|
||||
it uses `range` for negative offset, and `positive_range` for positive.
|
||||
|
||||
### `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
|
||||
(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
|
||||
to manipulate how much 'food' some action cost.
|
||||
|
||||
### `modify(e, 'nbt_merge', partial_tag)`
|
||||
|
||||
Merges a partial tag into the entity data and reloads the entity from its updated tag. Cannot be applied to players
|
||||
|
||||
### `modify(e, 'nbt', tag)`
|
||||
|
||||
Reloads the entity from a supplied tag. Better get a valid entity tag, what can go wrong. Wonder what would happen if
|
||||
transplant rabbit's brain into a villager. Cannot be applied to players.
|
||||
|
||||
## Entity Events
|
||||
|
||||
There is a number of events that happen to entities that you can attach your own code to in the form of event handlers.
|
||||
@@ -2940,6 +3049,51 @@ item_category('ender_pearl') => misc
|
||||
item_category('stone') => building_blocks
|
||||
</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)`
|
||||
|
||||
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_places_block(player, item_tuple, hand, block) // player have just placed the block.
|
||||
__on_player_interacts_with_entity(player, entity, hand)
|
||||
__on_player_chooses_recipe(player, recipe, full_stack)
|
||||
__on_player_switches_slot(player, from, to)
|
||||
__on_player_attacks_entity(player, entity)
|
||||
__on_player_takes_damage(player, amount, source, source_entity)
|
||||
__on_player_deals_damage(player, amount, entity)
|
||||
@@ -3185,7 +3341,7 @@ Optional shared shape attributes:
|
||||
in the form of `0xRRGGBBAA`, with the default of `-1`, so white opaque, or `0xFFFFFFFF`.
|
||||
* `player` - name or player entity to send the shape to. If specified, the shapes will appear only for the specified
|
||||
player, otherwise it will be send to all players in the dimension.
|
||||
* `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
|
||||
* `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
|
||||
@@ -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
|
||||
as a string value.
|
||||
|
||||
### parse_nbt(tag)
|
||||
|
||||
Converts NBT tag to a scarpet value, which you can navigate through much better.
|
||||
|
||||
Converts:
|
||||
- Compound tags into maps with string keys
|
||||
- List tags into list values
|
||||
- Numbers (Ints, Floats, Doubles, Longs) into a number
|
||||
- Rest is converted to strings.
|
||||
|
||||
### encode_nbt(expr, force?)
|
||||
|
||||
Encodes value of the expression as an NBT tag. By default (or when `force` is false), it will only allow
|
||||
to encode values that are guaranteed to return the same value when applied the resulting tag to `parse_nbt()`.
|
||||
Supported types that can reliably convert back and forth to and from NBT values are:
|
||||
- Maps with string keywords
|
||||
- Lists of items of the same type (scarpet will take care of unifying value types if possible)
|
||||
- Numbers (encoded as Ints -> Longs -> Doubles, as needed)
|
||||
- Strings
|
||||
|
||||
Other value types will only be converted to tags (including NBT tags) if `force` is true. They would require
|
||||
extra treatment when loading them back from NBT, but using `force` true will always produce output / never
|
||||
produce an exception.
|
||||
|
||||
### `print(expr)`
|
||||
|
||||
Displays the result of the expression to the chat. Overrides default `scarpet` behaviour of sending everyting to stderr.
|
||||
@@ -3318,7 +3498,7 @@ With the specified `resource` in the scripts folder, of a specific `type`, write
|
||||
content, or deletes the resource.
|
||||
|
||||
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
|
||||
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`.
|
||||
* `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.
|
||||
* `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
|
||||
* `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
|
||||
@@ -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
|
||||
as a string value.
|
||||
|
||||
### parse_nbt(tag)
|
||||
|
||||
Converts NBT tag to a scarpet value, which you can navigate through much better.
|
||||
|
||||
Converts:
|
||||
- Compound tags into maps with string keys
|
||||
- List tags into list values
|
||||
- Numbers (Ints, Floats, Doubles, Longs) into a number
|
||||
- Rest is converted to strings.
|
||||
|
||||
### encode_nbt(expr, force?)
|
||||
|
||||
Encodes value of the expression as an NBT tag. By default (or when `force` is false), it will only allow
|
||||
to encode values that are guaranteed to return the same value when applied the resulting tag to `parse_nbt()`.
|
||||
Supported types that can reliably convert back and forth to and from NBT values are:
|
||||
- Maps with string keywords
|
||||
- Lists of items of the same type (scarpet will take care of unifying value types if possible)
|
||||
- Numbers (encoded as Ints -> Longs -> Doubles, as needed)
|
||||
- Strings
|
||||
|
||||
Other value types will only be converted to tags (including NBT tags) if `force` is true. They would require
|
||||
extra treatment when loading them back from NBT, but using `force` true will always produce output / never
|
||||
produce an exception.
|
||||
|
||||
### `print(expr)`
|
||||
|
||||
Displays the result of the expression to the chat. Overrides default `scarpet` behaviour of sending everyting to stderr.
|
||||
@@ -200,7 +224,7 @@ With the specified `resource` in the scripts folder, of a specific `type`, write
|
||||
content, or deletes the resource.
|
||||
|
||||
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
|
||||
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
|
||||
* `dropped`: items dropped
|
||||
* `killed`: mobs killed
|
||||
* `killed_by`: blocks mined
|
||||
* `killed_by`: mobs killed by
|
||||
* `custom`: various random stats
|
||||
|
||||
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`.
|
||||
|
||||
### `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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
### `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`.
|
||||
|
||||
@@ -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
|
||||
</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
|
||||
point is specified, it iterates over 27 blocks. If `d` 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.
|
||||
Returns an iterator, just like `range` function that iterates over a rectangular area of blocks. If only center
|
||||
point is specified, it iterates over 27 blocks. If `range` arguments are specified, expands selection by the respective
|
||||
number of blocks in each direction. If `positive_range` arguments are specified,
|
||||
it uses `range` for negative offset, and `positive_range` for positive.
|
||||
|
||||
### `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
|
||||
(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
|
||||
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.
|
||||
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
|
||||
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, block('iron_trapdoor[half=top]')) // also correct, block() provides extra parsing
|
||||
set(x,y,z,'hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') // extra block data
|
||||
set(x,y,z,'hopper', l('facing', 'north'), nbt('{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') ) // same
|
||||
</pre>
|
||||
|
||||
### `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
|
||||
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
|
||||
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>
|
||||
run('/kill ' + e~'command_name');
|
||||
</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.
|
||||
|
||||
### `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)`
|
||||
|
||||
@@ -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
|
||||
to manipulate how much 'food' some action cost.
|
||||
|
||||
### `modify(e, 'nbt_merge', partial_tag)`
|
||||
|
||||
Merges a partial tag into the entity data and reloads the entity from its updated tag. Cannot be applied to players
|
||||
|
||||
### `modify(e, 'nbt', tag)`
|
||||
|
||||
Reloads the entity from a supplied tag. Better get a valid entity tag, what can go wrong. Wonder what would happen if
|
||||
transplant rabbit's brain into a villager. Cannot be applied to players.
|
||||
|
||||
## Entity Events
|
||||
|
||||
There is a number of events that happen to entities that you can attach your own code to in the form of event handlers.
|
||||
|
||||
@@ -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_places_block(player, item_tuple, hand, block) // player have just placed the block.
|
||||
__on_player_interacts_with_entity(player, entity, hand)
|
||||
__on_player_chooses_recipe(player, recipe, full_stack)
|
||||
__on_player_switches_slot(player, from, to)
|
||||
__on_player_attacks_entity(player, entity)
|
||||
__on_player_takes_damage(player, amount, source, source_entity)
|
||||
__on_player_deals_damage(player, amount, entity)
|
||||
|
||||
@@ -38,6 +38,51 @@ item_category('ender_pearl') => misc
|
||||
item_category('stone') => building_blocks
|
||||
</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)`
|
||||
|
||||
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
|
||||
|
||||
### `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
|
||||
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
|
||||
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>
|
||||
l(1,2,'foo') => [1, 2, foo]
|
||||
l() => [] (empty list)
|
||||
l(range(10)) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
l(1, 2) = l(3, 4) => Error: l is not a variable
|
||||
l(foo, bar) = l(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]
|
||||
l(1,2,'foo') <=> [1, 2, 'foo']
|
||||
l() <=> [] (empty list)
|
||||
[range(10)] => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
[1, 2] = [3, 4] => Error: l is not a variable
|
||||
[foo, bar] = [3, 4]; foo==3 && bar==4 => 1
|
||||
[foo, bar, baz] = [2, 4, 6]; [min(foo, bar), baz] = [3, 5]; [foo, bar, baz] => [3, 4, 5]
|
||||
</pre>
|
||||
|
||||
In the last example `l(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
|
||||
In the last example `[min(foo, bar), baz]` creates a valid L-value, as `min(foo, bar)` finds the lower of the
|
||||
variables (in this case `foo`) creating a valid assignable L-list of `[foo, baz]`, and these values
|
||||
will be assigned new values
|
||||
|
||||
### `join(delim, list), join(delim, values ...)`
|
||||
@@ -210,29 +215,37 @@ range(5,10) => [5, 6, 7, 8, 9]
|
||||
range(20, 10, -2) => [20, 18, 16, 14, 12]
|
||||
</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
|
||||
|
||||
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:
|
||||
|
||||
### `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
|
||||
treated as keys with no value, same goes with the iterator - creates a map that behaves like a set. If the
|
||||
arguments is a list of lists, they have to have two elements each, and then first is a key, and second, a value
|
||||
|
||||
In map creation context (directly inside `{}` or `m{}` call), `->` operator acts like a pair constructor for simpler
|
||||
syntax providing key value pairs, so the invocation to `{foo -> bar, baz -> quux}` is equivalent to
|
||||
`{l(foo, bar), l(baz, quux)}`, which is equivalent to somewhat older, but more traditional functional form of
|
||||
`m(l(foo, bar),l(baz, quuz))`.
|
||||
|
||||
Internally, `{?}`(list syntax) and `m(?)`(function syntax) are equivalent. `{}` is simply translated to
|
||||
`m()` in the scarpet preprocessing stage. This means that internally the code has always expression syntax despite `{}`
|
||||
not using different kinds of brackets and not being proper operators. This means that `m(}` and `{)` are also valid
|
||||
although not recommended as they will make your code far less readable.
|
||||
|
||||
When converting map value to string, `':'` is used as a key-value separator due to tentative compatibility with NBT
|
||||
notation, meaning in simpler cases maps can be converted to NBT parsable string by calling `str()`. This however
|
||||
does not guarantee a parsable output. To properly convert to NBT value, use `encode_nbt()`.
|
||||
|
||||
<pre>
|
||||
m(1,2,'foo') => {1: null, 2: null, foo: null}
|
||||
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}
|
||||
m(l(1, 2), l(3, 4)) => {1: 2, 3: 4}
|
||||
reduce(range(10), put(_a, _, _*_); _a, m())
|
||||
{1, 2, 'foo'} => {1: null, 2: null, foo: null}
|
||||
m() <=> {} (empty map)
|
||||
{range(10)} => {0: null, 1: null, 2: null, 3: null, 4: null, 5: null, 6: null, 7: null, 8: null, 9: null}
|
||||
m(l(1, 2), l(3, 4)) <=> {1 -> 2, 3 -> 4} => {1: 2, 3: 4}
|
||||
reduce(range(10), put(_a, _, _*_); _a, {})
|
||||
=> {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
|
||||
</pre>
|
||||
|
||||
|
||||
@@ -47,6 +47,8 @@ a() -> global_list+=1; global_list = l(1,2,3); a(); a(); global_list // => [1,2
|
||||
|
||||
### `Operator ->`
|
||||
|
||||
`->` operator has two uses - as a function definition operator and key-value initializer for maps.
|
||||
|
||||
To organize code better than a flat sequence of operations, one can define functions. Definition is correct
|
||||
if has the following form
|
||||
|
||||
@@ -71,7 +73,17 @@ a(lst) -> lst+=1; list = l(1,2,3); a(list); a(list); list // => [1,2,3]
|
||||
</pre>
|
||||
|
||||
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)`
|
||||
|
||||
|
||||
@@ -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
|
||||
designed to provide the ability to write custom programs to run within Minecraft
|
||||
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
|
||||
Minecraft related features and could function on its own, and CarpetExpression
|
||||
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)
|
||||
</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
|
||||
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,
|
||||
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>
|
||||
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))
|
||||
</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)`
|
||||
|
||||
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
|
||||
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`
|
||||
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
|
||||
|
||||
<pre>
|
||||
|
||||
@@ -12,6 +12,7 @@ blocks_daylight
|
||||
blocks_movement
|
||||
brightness
|
||||
chunk_tickets
|
||||
crafting_remaining_item
|
||||
create_marker
|
||||
current_dimension
|
||||
day_time
|
||||
@@ -67,6 +68,7 @@ power
|
||||
property
|
||||
query
|
||||
random_tick
|
||||
recipe_data
|
||||
rect
|
||||
reload_chunk
|
||||
remove_all_markers
|
||||
|
||||
@@ -6,6 +6,7 @@ __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
|
||||
@@ -21,6 +22,7 @@ __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
|
||||
|
||||
@@ -62,7 +62,9 @@
|
||||
|
||||
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
|
||||
|
||||
@@ -87,6 +89,7 @@
|
||||
__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
|
||||
@@ -102,6 +105,7 @@
|
||||
__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
|
||||
|
||||
@@ -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
|
||||
minecraft_version=1.15.2
|
||||
yarn_mappings=1.15.2+build.17
|
||||
loader_version=0.8.7+build.201
|
||||
loader_version=0.8.8+build.202
|
||||
|
||||
# Mod Properties
|
||||
mod_version = 1.3.29+v200611
|
||||
mod_version = 1.4.0+v200623
|
||||
maven_group = carpet
|
||||
archives_base_name = fabric-carpet
|
||||
|
||||
|
||||
@@ -16,11 +16,18 @@ public interface CarpetExtension
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -55,4 +62,6 @@ public interface CarpetExtension
|
||||
default void registerLoggers() {}
|
||||
|
||||
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);
|
||||
});
|
||||
scriptServer = new CarpetScriptServer(server);
|
||||
scriptServer.loadAllWorldScripts();
|
||||
MobAI.resetTrackers();
|
||||
LoggerRegistry.initLoggers();
|
||||
//TickSpeed.reset();
|
||||
}
|
||||
|
||||
public static void onServerLoadedWorlds(MinecraftServer minecraftServer)
|
||||
{
|
||||
extensions.forEach(e -> e.onServerLoadedWorlds(minecraftServer));
|
||||
scriptServer.loadAllWorldScripts();
|
||||
}
|
||||
|
||||
public static void tick(MinecraftServer server)
|
||||
{
|
||||
TickSpeed.tick(server);
|
||||
|
||||
@@ -34,7 +34,7 @@ import static carpet.settings.RuleCategory.CLIENT;
|
||||
@SuppressWarnings("CanBeFinal")
|
||||
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 boolean skipGenerationChecks = 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);
|
||||
|
||||
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"))
|
||||
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())
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// Dedicated server only
|
||||
@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)
|
||||
{
|
||||
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"))
|
||||
private void serverClosed(CallbackInfo ci)
|
||||
{
|
||||
|
||||
@@ -39,7 +39,7 @@ public abstract class MinecraftServer_scarpetMixin extends ReentrantThreadExecut
|
||||
{
|
||||
timeReference = field_4557 = Util.getMeasuringTimeMs();
|
||||
tick(isAhead);
|
||||
runTask();
|
||||
while(runTask()) {Thread.yield();}
|
||||
}
|
||||
|
||||
@Inject(method = "tick", at = @At(
|
||||
|
||||
@@ -4,6 +4,7 @@ import carpet.helpers.TickSpeed;
|
||||
import carpet.utils.CarpetProfiler;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.ServerTask;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.profiler.DisableableProfiler;
|
||||
import net.minecraft.util.thread.ReentrantThreadExecutor;
|
||||
@@ -50,6 +51,8 @@ public abstract class MinecraftServer_tickspeedMixin extends ReentrantThreadExec
|
||||
|
||||
@Shadow private volatile boolean loading;
|
||||
|
||||
@Shadow public abstract Iterable<ServerWorld> getWorlds();
|
||||
|
||||
CarpetProfiler.ProfilerToken currentSection;
|
||||
|
||||
private float carpetMsptAccum = 0.0f;
|
||||
@@ -120,16 +123,16 @@ public abstract class MinecraftServer_tickspeedMixin extends ReentrantThreadExec
|
||||
|
||||
this.profiler.startTick();
|
||||
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");
|
||||
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_19248 = Math.max(Util.getMeasuringTimeMs() + /*50L*/ msThisTick, this.timeReference);
|
||||
if (TickSpeed.time_warp_start_time != 0)
|
||||
{
|
||||
runTasks();
|
||||
runTasks(() -> !hasRunningTasks() );
|
||||
}
|
||||
else { this.method_16208(); }
|
||||
// run all tasks (this will not do a lot when warping), but that's fine since we already run them
|
||||
this.method_16208();
|
||||
this.profiler.pop();
|
||||
this.profiler.endTick();
|
||||
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(
|
||||
value = "INVOKE",
|
||||
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 goalSelector;
|
||||
@Shadow private boolean persistent;
|
||||
public final Map<String, Goal> temporaryTasks = new HashMap<>();
|
||||
|
||||
@Override
|
||||
@@ -30,5 +31,9 @@ public abstract class MobEntityMixin implements MobEntityInterface
|
||||
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;
|
||||
|
||||
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.ServerPlayerEntity;
|
||||
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_JUMPS;
|
||||
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_SPRINTING;
|
||||
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_SWITCHES_SLOT;
|
||||
import static carpet.script.CarpetEventServer.Event.PLAYER_USES_ITEM;
|
||||
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
|
||||
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;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.script.utils.ShapesRenderer;
|
||||
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;
|
||||
|
||||
public class CarpetClient
|
||||
@@ -51,4 +56,26 @@ public class CarpetClient
|
||||
{
|
||||
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)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 io.netty.buffer.Unpooled;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.PacketByteBuf;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class ServerNetworkHandler
|
||||
{
|
||||
private static Map<ServerPlayerEntity, String> remoteCarpetPlayers = new HashMap<>();
|
||||
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)
|
||||
{
|
||||
if (data != null)
|
||||
@@ -28,8 +42,13 @@ public class ServerNetworkHandler
|
||||
int id = data.readVarInt();
|
||||
if (id == CarpetClient.HELLO)
|
||||
onHello(player, data);
|
||||
if (id == CarpetClient.DATA)
|
||||
onClientData(player, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void onPlayerJoin(ServerPlayerEntity playerEntity)
|
||||
{
|
||||
if (!playerEntity.networkHandler.connection.isLocal())
|
||||
@@ -60,6 +79,67 @@ public class ServerNetworkHandler
|
||||
CarpetServer.settingsManager.getRules().forEach(data::withRule);
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -488,6 +488,37 @@ public class CarpetEventServer
|
||||
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)
|
||||
{
|
||||
@Override
|
||||
@@ -647,6 +678,8 @@ public class CarpetEventServer
|
||||
public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand) { }
|
||||
public void onDimensionChange(ServerPlayerEntity player, Vec3d from, Vec3d to, DimensionType fromDim, DimensionType dimTo) {}
|
||||
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) { }
|
||||
|
||||
@@ -2,8 +2,10 @@ package carpet.script;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.fakes.ChunkTicketManagerInterface;
|
||||
import carpet.fakes.IngredientInterface;
|
||||
import carpet.fakes.MinecraftServerInterface;
|
||||
import carpet.fakes.BiomeArrayInterface;
|
||||
import carpet.fakes.RecipeManagerInterface;
|
||||
import carpet.fakes.ServerChunkManagerInterface;
|
||||
import carpet.fakes.ServerLightingProviderInterface;
|
||||
import carpet.fakes.StatTypeInterface;
|
||||
@@ -81,6 +83,13 @@ import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.particle.ParticleEffect;
|
||||
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.ScoreboardCriterion;
|
||||
import net.minecraft.scoreboard.ScoreboardObjective;
|
||||
@@ -97,6 +106,7 @@ import net.minecraft.stat.Stat;
|
||||
import net.minecraft.stat.StatType;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.state.property.Property;
|
||||
import net.minecraft.structure.StructureFeatures;
|
||||
import net.minecraft.structure.StructurePiece;
|
||||
import net.minecraft.structure.StructureStart;
|
||||
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.EulerAngle;
|
||||
import net.minecraft.util.math.BlockBox;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.Vec3i;
|
||||
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.StructureFeature;
|
||||
import net.minecraft.world.poi.PointOfInterest;
|
||||
@@ -130,8 +142,10 @@ import net.minecraft.world.dimension.DimensionType;
|
||||
import net.minecraft.world.gen.ChunkRandom;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
@@ -342,6 +356,18 @@ public class CarpetExpression
|
||||
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()
|
||||
{
|
||||
this.expr.addLazyFunction("block", -1, (c, t, lv) ->
|
||||
@@ -737,43 +763,76 @@ public class CarpetExpression
|
||||
this.expr.addLazyFunction("without_updates", 1, (c, t, lv) ->
|
||||
{
|
||||
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;
|
||||
Value ret = lv.get(0).evalValue(c, t);
|
||||
return (cc, tt) -> ret;
|
||||
}
|
||||
finally
|
||||
{
|
||||
CarpetSettings.impendingFillSkipUpdates = previous;
|
||||
}
|
||||
try
|
||||
{
|
||||
CarpetSettings.impendingFillSkipUpdates = true;
|
||||
result[0] = lv.get(0).evalValue(c, t);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CarpetSettings.impendingFillSkipUpdates = previous;
|
||||
}
|
||||
});
|
||||
return (cc, tt) -> result[0];
|
||||
});
|
||||
|
||||
this.expr.addLazyFunction("set", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
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 sourceLocator = BlockArgument.findIn(cc, lv, targetLocator.offset, true);
|
||||
BlockState sourceBlockState = sourceLocator.block.getBlockState();
|
||||
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();
|
||||
for (int i = sourceLocator.offset; i < lv.size(); i += 2)
|
||||
List<Value> args = new ArrayList<>();
|
||||
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);
|
||||
if (property == null)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
CompoundTag data = sourceLocator.block.getData();
|
||||
if (data == null) data = sourceLocator.block.getData();
|
||||
CompoundTag finalData = data;
|
||||
|
||||
if (sourceBlockState == targetBlockState && data == null)
|
||||
return (c_, t_) -> Value.FALSE;
|
||||
@@ -783,12 +842,12 @@ public class CarpetExpression
|
||||
{
|
||||
Clearable.clear(world.getBlockEntity(targetPos));
|
||||
world.setBlockState(targetPos, finalSourceBlockState, 2);
|
||||
if (data != null)
|
||||
if (finalData != null)
|
||||
{
|
||||
BlockEntity be = world.getBlockEntity(targetPos);
|
||||
if (be != null)
|
||||
{
|
||||
CompoundTag destTag = data.copy();
|
||||
CompoundTag destTag = finalData.copy();
|
||||
destTag.putInt("x", targetPos.getX());
|
||||
destTag.putInt("y", targetPos.getY());
|
||||
destTag.putInt("z", targetPos.getZ());
|
||||
@@ -897,7 +956,7 @@ public class CarpetExpression
|
||||
Tag outtag = tool.getTag();
|
||||
if (outtag == null)
|
||||
return LazyValue.TRUE;
|
||||
Value ret = new NBTSerializableValue(outtag);
|
||||
Value ret = new NBTSerializableValue(() -> outtag);
|
||||
return (_c, _t) -> ret;
|
||||
|
||||
});
|
||||
@@ -1096,6 +1155,9 @@ public class CarpetExpression
|
||||
BlockArgument locator = BlockArgument.findIn(cc, lv, 0);
|
||||
|
||||
ServerWorld world = cc.s.getWorld();
|
||||
|
||||
BooYah(world.getChunkManager().getChunkGenerator());
|
||||
|
||||
BlockPos pos = locator.block.getPos();
|
||||
StructureFeature<?> structure = null;
|
||||
boolean needSize = false;
|
||||
@@ -1385,13 +1447,14 @@ public class CarpetExpression
|
||||
});
|
||||
|
||||
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;
|
||||
Scoreboard scoreboard = cc.s.getMinecraftServer().getScoreboard();
|
||||
String objectiveName = lv.get(0).evalValue(c).getString();
|
||||
ScoreboardObjective objective = scoreboard.getObjective(objectiveName);
|
||||
if (objective == null)
|
||||
return LazyValue.FALSE;
|
||||
if (lv.size() == 0)
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
scoreboard.removeObjective(objective);
|
||||
return LazyValue.TRUE;
|
||||
@@ -1472,6 +1535,120 @@ public class CarpetExpression
|
||||
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) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext) c;
|
||||
@@ -1825,11 +2002,9 @@ public class CarpetExpression
|
||||
Value nbt = lv.get(position.offset).evalValue(c);
|
||||
NBTSerializableValue v = (nbt instanceof NBTSerializableValue) ? (NBTSerializableValue) nbt
|
||||
: NBTSerializableValue.parseString(nbt.getString());
|
||||
if (v != null)
|
||||
{
|
||||
hasTag = true;
|
||||
tag = v.getCompoundTag();
|
||||
}
|
||||
hasTag = true;
|
||||
tag = v.getCompoundTag();
|
||||
|
||||
}
|
||||
tag.putString("id", entityId.toString());
|
||||
Vec3d vec3d = position.vec;
|
||||
@@ -2097,21 +2272,28 @@ public class CarpetExpression
|
||||
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();
|
||||
int yi = (int) NumericValue.asNumber(lv.get(1).evalValue(c)).getLong();
|
||||
int zi = (int) NumericValue.asNumber(lv.get(2).evalValue(c)).getLong();
|
||||
int xj = (int) NumericValue.asNumber(lv.get(3).evalValue(c)).getLong();
|
||||
int yj = (int) NumericValue.asNumber(lv.get(4).evalValue(c)).getLong();
|
||||
int zj = (int) NumericValue.asNumber(lv.get(5).evalValue(c)).getLong();
|
||||
int minx = min(xi, xj);
|
||||
int miny = min(yi, yj);
|
||||
int minz = min(zi, zj);
|
||||
int maxx = max(xi, xj);
|
||||
int maxy = max(yi, yj);
|
||||
int maxz = max(zi, zj);
|
||||
LazyValue expr = lv.get(6);
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
|
||||
BlockArgument pos1Locator = BlockArgument.findIn(cc, lv, 0);
|
||||
BlockArgument pos2Locator = BlockArgument.findIn(cc, lv, pos1Locator.offset);
|
||||
BlockPos pos1 = pos1Locator.block.getPos();
|
||||
BlockPos pos2 = pos2Locator.block.getPos();
|
||||
|
||||
int x1 = pos1.getX();
|
||||
int y1 = pos1.getY();
|
||||
int z1 = pos1.getZ();
|
||||
int x2 = pos2.getX();
|
||||
int y2 = pos2.getY();
|
||||
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
|
||||
LazyValue _x = c.getVariable("_x");
|
||||
@@ -2181,57 +2363,63 @@ public class CarpetExpression
|
||||
|
||||
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");
|
||||
}
|
||||
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
|
||||
Vector3Argument diffLocator = Vector3Argument.findIn(cc, lv, cposLocator.offset);
|
||||
if (diffLocator.fromBlock)
|
||||
{
|
||||
sminx = 1;
|
||||
sminy = 1;
|
||||
sminz = 1;
|
||||
smaxx = 1;
|
||||
smaxy = 1;
|
||||
smaxz = 1;
|
||||
sminx = MathHelper.floor(abs(diffLocator.vec.x - cx));
|
||||
sminy = MathHelper.floor(abs(diffLocator.vec.y - cx));
|
||||
sminz = MathHelper.floor(abs(diffLocator.vec.z - cx));
|
||||
}
|
||||
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;
|
||||
smaxy = sminy;
|
||||
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()
|
||||
{
|
||||
final int minx = cx-sminx;
|
||||
@@ -2240,6 +2428,7 @@ public class CarpetExpression
|
||||
final int maxx = cx+smaxx;
|
||||
final int maxy = cy+smaxy;
|
||||
final int maxz = cz+smaxz;
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
@@ -2299,10 +2488,9 @@ public class CarpetExpression
|
||||
this.expr.addLazyFunction("diamond", -1, (c, t, lv)->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
if (lv.size() != 3 && lv.size() != 4 && lv.size() != 5)
|
||||
{
|
||||
throw new InternalExpressionException("'diamond' region should be specified with 3 to 5 coordinates");
|
||||
}
|
||||
|
||||
BlockArgument cposLocator=BlockArgument.findIn((CarpetContext)c,lv,0);
|
||||
BlockPos cpos = cposLocator.block.getPos();
|
||||
|
||||
int cx;
|
||||
int cy;
|
||||
@@ -2311,10 +2499,11 @@ public class CarpetExpression
|
||||
int height;
|
||||
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)
|
||||
cx = cpos.getX();
|
||||
cy = cpos.getY();
|
||||
cz = cpos.getZ();
|
||||
|
||||
if (lv.size()==cposLocator.offset)
|
||||
{
|
||||
Value retval = ListValue.of(
|
||||
BlockValue.fromCoords(cc, cx, cy-1, cz),
|
||||
@@ -2327,20 +2516,22 @@ public class CarpetExpression
|
||||
);
|
||||
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;
|
||||
}
|
||||
else // size == 5
|
||||
else if(lv.size()==2+cposLocator.offset)
|
||||
{
|
||||
width = (int) ((NumericValue) lv.get(3).evalValue(c)).getLong();
|
||||
height = (int) ((NumericValue) lv.get(4).evalValue(c)).getLong();
|
||||
width = (int) ((NumericValue) lv.get(cposLocator.offset).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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -2480,7 +2671,7 @@ public class CarpetExpression
|
||||
public static String recognizeResource(Value value)
|
||||
{
|
||||
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("/"));
|
||||
if (file.isEmpty())
|
||||
{
|
||||
@@ -2756,12 +2947,7 @@ public class CarpetExpression
|
||||
});
|
||||
|
||||
this.expr.addLazyFunction("nbt", 1, (c, t, lv) -> {
|
||||
Value v = 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;
|
||||
Value ret = NBTSerializableValue.fromValue(lv.get(0).evalValue(c));
|
||||
return (cc, tt) -> ret;
|
||||
});
|
||||
|
||||
@@ -2772,11 +2958,50 @@ public class CarpetExpression
|
||||
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
|
||||
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;
|
||||
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)
|
||||
{
|
||||
s.sendFeedback(((FormattedTextValue) res).getText(), false);
|
||||
@@ -2792,7 +3017,8 @@ public class CarpetExpression
|
||||
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) -> {
|
||||
@@ -2862,9 +3088,14 @@ public class CarpetExpression
|
||||
BlockPos target = ((CarpetContext)c).origin;
|
||||
Vec3d posf = new Vec3d((double)target.getX()+0.5D,(double)target.getY(),(double)target.getZ()+0.5D);
|
||||
ServerCommandSource s = ((CarpetContext)c).s;
|
||||
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;
|
||||
try
|
||||
{
|
||||
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) -> {
|
||||
@@ -2945,7 +3176,7 @@ public class CarpetExpression
|
||||
|
||||
this.expr.addLazyFunction("seed", -1, (c, t, lv) -> {
|
||||
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;
|
||||
});
|
||||
|
||||
@@ -3013,10 +3244,12 @@ public class CarpetExpression
|
||||
default:
|
||||
throw new InternalExpressionException("Incorrect dimension string: "+dimString);
|
||||
}
|
||||
}
|
||||
((CarpetContext) c).s = innerSource;
|
||||
Value retval = lv.get(1).evalValue(c);
|
||||
((CarpetContext) c).s = outerSource;
|
||||
};
|
||||
if (innerSource.getWorld() == outerSource.getWorld()) return lv.get(1);
|
||||
Context newCtx = c.recreate();
|
||||
((CarpetContext) newCtx).s = innerSource;
|
||||
newCtx.variables = c.variables;
|
||||
Value retval = lv.get(1).evalValue(newCtx);
|
||||
return (cc, tt) -> retval;
|
||||
});
|
||||
|
||||
|
||||
@@ -18,8 +18,9 @@ public class Context
|
||||
public static final int SIGNATURE = 7;
|
||||
public static final int LOCALIZATION = 8;
|
||||
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;
|
||||
|
||||
|
||||
@@ -43,7 +43,9 @@ import org.apache.commons.lang3.text.WordUtils;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.IllegalFormatException;
|
||||
import java.util.Iterator;
|
||||
@@ -490,6 +492,11 @@ public class Expression
|
||||
//assigns const procedure to the lhs, returning its previous value
|
||||
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);
|
||||
if (!(v1 instanceof FunctionSignatureValue))
|
||||
throw new InternalExpressionException("'->' operator requires a function signature on the LHS");
|
||||
@@ -1333,11 +1340,18 @@ public class Expression
|
||||
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)
|
||||
return new MapValue(((LazyListValue) lv.get(0)).unroll());
|
||||
return new MapValue(lv);
|
||||
ret = new MapValue(((LazyListValue) lv.get(0)).unroll());
|
||||
else
|
||||
ret = new MapValue(lv);
|
||||
return (cc, tt) -> ret;
|
||||
});
|
||||
|
||||
addUnaryFunction("keys", v -> {
|
||||
@@ -1449,20 +1463,6 @@ public class Expression
|
||||
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) ->
|
||||
{
|
||||
if(lv.size()<2)
|
||||
@@ -1790,6 +1790,66 @@ public class Expression
|
||||
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) ->
|
||||
{
|
||||
LazyValue lazy = lv.get(0);
|
||||
@@ -2000,7 +2060,7 @@ public class Expression
|
||||
|
||||
Tokenizer tokenizer = new Tokenizer(c, this, expression, allowComments, allowNewlineSubstitutions);
|
||||
// stripping lousy but acceptable semicolons
|
||||
List<Tokenizer.Token> cleanedTokens = tokenizer.fixSemicolons();
|
||||
List<Tokenizer.Token> cleanedTokens = tokenizer.postProcess();
|
||||
|
||||
Tokenizer.Token lastFunction = null;
|
||||
Tokenizer.Token previousToken = null;
|
||||
|
||||
@@ -43,7 +43,7 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
this.newLinesMarkers = allowNewLineMakers;
|
||||
}
|
||||
|
||||
public List<Token> fixSemicolons()
|
||||
public List<Token> postProcess()
|
||||
{
|
||||
Iterable<Token> iterable = () -> this;
|
||||
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)
|
||||
{
|
||||
Token current = originalTokens.remove(originalTokens.size()-1);
|
||||
if (current.type == Token.TokenType.MARKER && current.surface.startsWith("//"))
|
||||
continue;
|
||||
// skipping comments
|
||||
if (!isSemicolon(current)
|
||||
|| (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.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);
|
||||
}
|
||||
if (current.type != Token.TokenType.MARKER)
|
||||
if (!(current.type == Token.TokenType.MARKER && current.surface.equals("$")))
|
||||
last = current;
|
||||
}
|
||||
Collections.reverse(cleanedTokens);
|
||||
@@ -242,7 +263,8 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
}
|
||||
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 == '(')
|
||||
{
|
||||
@@ -252,17 +274,21 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
{
|
||||
token.type = Token.TokenType.CLOSE_PAREN;
|
||||
}
|
||||
else
|
||||
else if (ch == ',')
|
||||
{
|
||||
token.type = Token.TokenType.COMMA;
|
||||
}
|
||||
else
|
||||
{
|
||||
token.type = Token.TokenType.MARKER;
|
||||
}
|
||||
token.append(ch);
|
||||
pos++;
|
||||
linepos++;
|
||||
|
||||
if (expression != null && context != null && previousToken != null &&
|
||||
previousToken.type == Token.TokenType.OPERATOR &&
|
||||
(ch == ')' || ch == ',') &&
|
||||
(ch == ')' || ch == ',' || ch == ']' || ch == '}') &&
|
||||
!previousToken.surface.equalsIgnoreCase(";")
|
||||
)
|
||||
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
|
||||
|| 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.type = Token.TokenType.UNARY_OPERATOR;
|
||||
@@ -376,6 +404,22 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
public int pos;
|
||||
public int linepos;
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -39,16 +39,13 @@ public class ShapesRenderer
|
||||
{
|
||||
this.client = minecraftClient;
|
||||
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)
|
||||
{
|
||||
IWorld iWorld = this.client.world;
|
||||
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();
|
||||
RenderSystem.disableTexture();
|
||||
RenderSystem.enableDepthTest();
|
||||
@@ -59,8 +56,7 @@ public class ShapesRenderer
|
||||
//RenderSystem.shadeModel(7425);
|
||||
RenderSystem.shadeModel(GL11.GL_FLAT);
|
||||
RenderSystem.enableAlphaTest();
|
||||
RenderSystem.defaultAlphaFunc();
|
||||
//RenderSystem.alphaFunc(GL11.GL_GREATER, 0.005F);
|
||||
RenderSystem.alphaFunc(GL11.GL_GREATER, 0.003f);
|
||||
RenderSystem.disableCull();
|
||||
RenderSystem.disableLighting();
|
||||
RenderSystem.depthMask(false);
|
||||
@@ -131,7 +127,7 @@ public class ShapesRenderer
|
||||
long key = rshape.key();
|
||||
synchronized (shapes)
|
||||
{
|
||||
RenderedShape<?> existing = shapes.get(dim).get(key);
|
||||
RenderedShape<?> existing = shapes.computeIfAbsent(dim, d -> new Long2ObjectOpenHashMap<>()).get(key);
|
||||
if (existing != null)
|
||||
{ // promoting previous shape
|
||||
existing.expiryTick = rshape.expiryTick;
|
||||
|
||||
@@ -12,7 +12,10 @@ import net.minecraft.block.Blocks;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.state.property.Property;
|
||||
import net.minecraft.util.GlobalPos;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
@@ -24,8 +27,10 @@ import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.WorldChunk;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -180,6 +185,33 @@ public class BlockValue extends Value
|
||||
|
||||
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 {
|
||||
UP("up",0.5, 0.0, 0.5, Direction.UP),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package carpet.script.value;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.fakes.EntityInterface;
|
||||
import carpet.fakes.ItemEntityInterface;
|
||||
import carpet.fakes.LivingEntityInterface;
|
||||
@@ -12,10 +11,10 @@ import carpet.script.CarpetContext;
|
||||
import carpet.script.EntityEventsGroup;
|
||||
import carpet.script.argument.Vector3Argument;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.utils.Messenger;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
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.EntityPositionS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
|
||||
@@ -61,6 +60,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
@@ -87,11 +87,11 @@ public class EntityValue extends Value
|
||||
EntitySelector entitySelector = selectorCache.get(selector);
|
||||
if (entitySelector != null)
|
||||
{
|
||||
return entitySelector.getEntities(source);
|
||||
return entitySelector.getEntities(source.withMaxLevel(4));
|
||||
}
|
||||
entitySelector = new EntitySelectorReader(new StringReader(selector), true).read();
|
||||
selectorCache.put(selector, entitySelector);
|
||||
return entitySelector.getEntities(source);
|
||||
return entitySelector.getEntities(source.withMaxLevel(4));
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
@@ -289,6 +289,10 @@ public class EntityValue extends Value
|
||||
put("sneaking", (e, a) -> e.isSneaking()?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("persistence", (e, a) -> {
|
||||
if (e instanceof MobEntity) return new NumericValue(((MobEntity) e).isPersistent());
|
||||
return Value.NULL;
|
||||
});
|
||||
put("hunger", (e, a) -> {
|
||||
if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).getHungerManager().getFoodLevel());
|
||||
return Value.NULL;
|
||||
@@ -520,6 +524,10 @@ public class EntityValue extends 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)
|
||||
@@ -715,9 +723,23 @@ public class EntityValue extends Value
|
||||
e.setCustomName(null);
|
||||
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()));
|
||||
});
|
||||
|
||||
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("mount", (e, v) -> {
|
||||
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());
|
||||
});
|
||||
|
||||
// gamemode [check]
|
||||
// spectate [check]
|
||||
// "fire" [check]
|
||||
// "extinguish" [set fire ticks to 0]
|
||||
// "silent" [check]
|
||||
// "gravity" [check]
|
||||
// "invulnerable" [check]
|
||||
put("nbt", (e, v) -> {
|
||||
if (!(e instanceof PlayerEntity))
|
||||
{
|
||||
UUID uUID = e.getUuid();
|
||||
Value tagValue = NBTSerializableValue.fromValue(v);
|
||||
if (tagValue instanceof NBTSerializableValue)
|
||||
{
|
||||
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" []
|
||||
// "item" []
|
||||
// "count", []
|
||||
// "age", [check]
|
||||
// "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)
|
||||
@@ -1001,4 +1038,14 @@ public class EntityValue extends Value
|
||||
throw new InternalExpressionException("Unknown entity event: " + eventName);
|
||||
((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;
|
||||
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.text.BaseText;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
public class FormattedTextValue extends StringValue
|
||||
{
|
||||
@@ -37,4 +40,11 @@ public class FormattedTextValue extends StringValue
|
||||
{
|
||||
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;
|
||||
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
public abstract class FrameworkValue extends Value
|
||||
{
|
||||
@Override
|
||||
@@ -25,4 +27,10 @@ public abstract class FrameworkValue extends Value
|
||||
{
|
||||
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.ReturnStatement;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -94,6 +96,8 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
return name.hashCode()+(int)variant;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
@@ -231,4 +235,11 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
{
|
||||
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;
|
||||
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
@@ -152,4 +154,11 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
|
||||
{
|
||||
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.exception.InternalExpressionException;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
@@ -98,6 +108,13 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
{
|
||||
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()
|
||||
{
|
||||
@@ -447,4 +464,65 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
{
|
||||
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;
|
||||
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -226,4 +228,16 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
|
||||
{
|
||||
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.Box;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -48,6 +49,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
private String nbtString = null;
|
||||
private Tag nbtTag = null;
|
||||
private Supplier<Tag> nbtSupplier = null;
|
||||
private boolean owned = false;
|
||||
|
||||
private NBTSerializableValue() {}
|
||||
|
||||
@@ -64,11 +66,18 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
throw new InternalExpressionException("Incorrect NBT data: "+nbtString);
|
||||
}
|
||||
};
|
||||
owned = true;
|
||||
}
|
||||
|
||||
public NBTSerializableValue(Tag tag)
|
||||
{
|
||||
nbtTag = tag;
|
||||
owned = true;
|
||||
}
|
||||
|
||||
public NBTSerializableValue(Supplier<Tag> tagSupplier)
|
||||
{
|
||||
nbtSupplier = tagSupplier;
|
||||
}
|
||||
|
||||
public static Value fromStack(ItemStack stack)
|
||||
@@ -100,7 +109,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
return null;
|
||||
throw new InternalExpressionException("Incorrect NBT tag: nbtString");
|
||||
}
|
||||
NBTSerializableValue value = new NBTSerializableValue(tag);
|
||||
value.nbtString = nbtString;
|
||||
@@ -115,6 +124,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
NBTSerializableValue copy = new NBTSerializableValue(nbtTag);
|
||||
copy.nbtSupplier = this.nbtSupplier;
|
||||
copy.nbtString = this.nbtString;
|
||||
copy.owned = this.owned;
|
||||
return copy;
|
||||
}
|
||||
|
||||
@@ -122,9 +132,8 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
public Value deepcopy()
|
||||
{
|
||||
NBTSerializableValue copy = (NBTSerializableValue) clone();
|
||||
// same fields except when tag is set - need to copy it.
|
||||
if (copy.nbtTag != null)
|
||||
copy.nbtTag = copy.getTag().copy();
|
||||
copy.owned = false;
|
||||
ensureOwnership();
|
||||
return copy;
|
||||
}
|
||||
|
||||
@@ -290,7 +299,50 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
private static Value decodeTag(Tag t)
|
||||
{
|
||||
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)
|
||||
return new NumericValue(((AbstractNumberTag) t).getDouble());
|
||||
// more can be done here
|
||||
@@ -304,13 +356,6 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
return nbtTag;
|
||||
}
|
||||
|
||||
private void replaceTag(Tag newTag)
|
||||
{
|
||||
nbtTag = newTag;
|
||||
nbtString = null;
|
||||
nbtSupplier = null; // just to be sure
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o)
|
||||
{
|
||||
@@ -346,6 +391,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
{
|
||||
try
|
||||
{
|
||||
ensureOwnership();
|
||||
return (CompoundTag) getTag();
|
||||
}
|
||||
catch (ClassCastException e)
|
||||
@@ -364,12 +410,12 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
public boolean put(Value where, Value value, Value conditions)
|
||||
{
|
||||
/// WIP
|
||||
|
||||
ensureOwnership();
|
||||
NbtPathArgumentType.NbtPath path = cachePath(where.getString());
|
||||
Tag tagToInsert = value instanceof NBTSerializableValue ?
|
||||
((NBTSerializableValue) value).getTag() :
|
||||
new NBTSerializableValue(value.getString()).getTag();
|
||||
Tag modifiedTag;
|
||||
boolean modifiedTag;
|
||||
if (conditions instanceof NumericValue)
|
||||
{
|
||||
modifiedTag = modify_insert((int)((NumericValue) conditions).getLong(), path, tagToInsert);
|
||||
@@ -390,22 +436,17 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (modifiedTag != null)
|
||||
{
|
||||
replaceTag(modifiedTag);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return modifiedTag;
|
||||
}
|
||||
|
||||
|
||||
|
||||
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;
|
||||
try
|
||||
@@ -414,7 +455,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean modified = false;
|
||||
@@ -428,27 +469,27 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
{
|
||||
AbstractListTag<?> targetList = (AbstractListTag) target;
|
||||
if (!targetList.addTag(index < 0 ? targetList.size() + index + 1 : index, newElement.copy()))
|
||||
return null;
|
||||
return false;
|
||||
modified = true;
|
||||
}
|
||||
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))
|
||||
{
|
||||
return getTag();
|
||||
return false;
|
||||
}
|
||||
Tag originalTag = getTag().copy();
|
||||
Tag ownTag = getTag();
|
||||
try
|
||||
{
|
||||
for (Tag target : nbtPath.getOrInit(originalTag, CompoundTag::new))
|
||||
for (Tag target : nbtPath.getOrInit(ownTag, CompoundTag::new))
|
||||
{
|
||||
if (!(target instanceof CompoundTag))
|
||||
{
|
||||
@@ -457,23 +498,26 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
((CompoundTag) target).copyFrom((CompoundTag) replacement);
|
||||
}
|
||||
}
|
||||
catch (CommandSyntaxException ignored) { }
|
||||
return originalTag;
|
||||
catch (CommandSyntaxException ignored)
|
||||
{
|
||||
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();
|
||||
if (pathText.endsWith("]")) // workaround for array replacement or item in the array replacement
|
||||
{
|
||||
if (nbtPath.remove(originalTag)==0)
|
||||
return null;
|
||||
if (nbtPath.remove(tag)==0)
|
||||
return false;
|
||||
Pattern pattern = Pattern.compile("\\[[^\\[]*]$");
|
||||
Matcher matcher = pattern.matcher(pathText);
|
||||
if (!matcher.find()) // malformed path
|
||||
{
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
String arrAccess = matcher.group();
|
||||
int pos;
|
||||
@@ -487,21 +531,21 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
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
|
||||
{
|
||||
nbtPath.put(originalTag, () -> replacement);
|
||||
nbtPath.put(tag, () -> replacement);
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
return originalTag;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -527,13 +571,23 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
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
|
||||
public boolean delete(Value where)
|
||||
{
|
||||
NbtPathArgumentType.NbtPath path = cachePath(where.getString());
|
||||
Tag tag = getTag().copy();
|
||||
int removed = path.remove(tag);
|
||||
if (removed > 0) replaceTag(tag);
|
||||
ensureOwnership();
|
||||
int removed = path.remove(getTag());
|
||||
return removed > 0;
|
||||
}
|
||||
|
||||
@@ -584,4 +638,23 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
{
|
||||
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;
|
||||
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
public class NullValue extends NumericValue // TODO check nonsingleton code
|
||||
{
|
||||
@Override
|
||||
@@ -50,4 +53,11 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
|
||||
{
|
||||
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;
|
||||
|
||||
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 java.math.BigDecimal;
|
||||
@@ -204,4 +208,20 @@ public class NumericValue extends Value
|
||||
{
|
||||
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;
|
||||
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
public class StringValue extends Value
|
||||
{
|
||||
private String str;
|
||||
@@ -30,4 +33,10 @@ public class StringValue extends Value
|
||||
{
|
||||
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.exception.ExpressionException;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -144,4 +145,10 @@ public class ThreadValue extends Value
|
||||
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;
|
||||
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Tag toTag(boolean force);
|
||||
}
|
||||
@@ -470,7 +470,7 @@ public class SettingsManager
|
||||
writeSettingsToConf(conf.getLeft()); // this may feels weird, but if conf
|
||||
// is locked, it will never reach this point.
|
||||
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;
|
||||
}
|
||||
// removes overrides of the default values in the file
|
||||
@@ -482,6 +482,7 @@ public class SettingsManager
|
||||
conf.getLeft().remove(rule.name);
|
||||
writeSettingsToConf(conf.getLeft());
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,8 @@
|
||||
"ui.tags": "Tags",
|
||||
"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.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"
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"rule.antiCheatDisabled.name": "移动检测通杀",
|
||||
"rule.antiCheatDisabled.name": "禁用反作弊移动监测",
|
||||
"rule.antiCheatDisabled.desc": "防止玩家因为“本服务器未启用飞行”,“移动过快”等原因被踢出",
|
||||
|
||||
"rule.carpets.name": "放置地毯执行功能",
|
||||
"rule.carpets.name": "多功能地毯",
|
||||
"rule.carpets.desc": "通过放置地毯方块快捷执行部分功能",
|
||||
|
||||
"rule.combineXPOrbs.name": "经验球合并",
|
||||
@@ -135,7 +135,7 @@
|
||||
"rule.pushLimit.desc": "自定义活塞推动上限",
|
||||
"rule.pushLimit.extra.0": "你必须在1-1024中选取一个值",
|
||||
|
||||
"rule.quasiConnectivity.name": "qC激活开关",
|
||||
"rule.quasiConnectivity.name": "半连接激活开关",
|
||||
"rule.quasiConnectivity.desc": "活塞,发射器和投掷器在他们上方方块激活时是否响应",
|
||||
"rule.quasiConnectivity.extra.0": "设置为true为开启 设置为false为禁用",
|
||||
|
||||
@@ -228,7 +228,8 @@
|
||||
"ui.tags": "分类",
|
||||
"ui.change_permanently": "永久更改",
|
||||
"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": "当前的值"
|
||||
|
||||
}
|
||||
@@ -331,7 +331,7 @@ select(num) ->
|
||||
);
|
||||
global_selected_point = if (global_selected_point == selected_point, null, selected_point);
|
||||
global_needs_updating = true;
|
||||
// no need to _update since path is still valid
|
||||
|
||||
);
|
||||
|
||||
// player can also punch the mannequin to select/deselect it
|
||||
@@ -501,7 +501,7 @@ play() ->
|
||||
task( _() -> (
|
||||
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
|
||||
sleep(1500); // in case we interrupt previous playbacks
|
||||
sleep(1500);
|
||||
);
|
||||
showing_path = global_showing_path;
|
||||
hide();
|
||||
|
||||
@@ -244,3 +244,23 @@ __on_player_disconnects(player, reason) ->
|
||||
print('__on_player_disconnects(player)');
|
||||
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;
|
||||
total_size = _euclidean(from, to);
|
||||
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');
|
||||
for (structure_pieces, l(piece, direction, from, to) = _;
|
||||
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/ServerLightingProvider$Stage
|
||||
accessible class net/minecraft/recipe/Ingredient$Entry
|
||||
@@ -169,6 +169,9 @@
|
||||
"ServerWorld_scarpetEventMixin",
|
||||
"HungerManagerMixin_scarpetEventsMixin",
|
||||
|
||||
"RecipeManager_scarpetMixin",
|
||||
"Ingredient_scarpetMixin",
|
||||
|
||||
"PistonBlock_movableTEMixin",
|
||||
"PistonBlockEntity_movableTEMixin",
|
||||
"World_movableTEMixin",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "carpet",
|
||||
"version": "1.3.29",
|
||||
"version": "1.4.0",
|
||||
|
||||
"name": "Carpet Mod in Fabric",
|
||||
"description": "Carpet made out of fabric",
|
||||
@@ -13,7 +13,7 @@
|
||||
"sources": "https://github.com/gnembon/fabric-carpet"
|
||||
},
|
||||
|
||||
"license": "GNU LGPL V3",
|
||||
"license": "MIT",
|
||||
"icon": "assets/carpet/icon.png",
|
||||
|
||||
"environment": "*",
|
||||
@@ -24,7 +24,8 @@
|
||||
"mixins": [
|
||||
"carpet.mixins.json"
|
||||
],
|
||||
"accesswidener": "carpet.accesswidener",
|
||||
"accessWidener" : "carpet.accesswidener",
|
||||
|
||||
"depends": {
|
||||
"minecraft": "1.15.x",
|
||||
"fabricloader": ">=0.7.1"
|
||||
|
||||
Reference in new issue
Block a user