Merge branch 'master' into light
This commit is contained in:
commit
ac9fcd9ece
122 files changed
+7753
-1373
No files matched your search
+1
-1
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id 'fabric-loom' version '0.2.6-SNAPSHOT'
|
||||
id 'fabric-loom' version '0.4-SNAPSHOT'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
|
||||
+630
-197
File diff suppressed because it is too large.
Load diff
+252
-19
@@ -4,36 +4,112 @@ Collection of other methods that control smaller, yet still important aspects of
|
||||
|
||||
## Sounds
|
||||
|
||||
### `sound(name, pos, volume?, pitch?)`
|
||||
### `sound(name, pos, volume?, pitch?, mixer?)`
|
||||
|
||||
Plays a specific sound `name`, at block or position `pos`, with optional `volume` and modified `pitch`. `pos` can be
|
||||
either a block, triple of coords, or a list of thee numbers. Uses the same options as a corresponding `playsound` command.
|
||||
Plays a specific sound `name`, at block or position `pos`, with optional `volume` and modified `pitch`, and under
|
||||
optional `mixer`. Default values for `volume`, `pitch` and `mixer` are `1.0`, `1.0`, and `master`.
|
||||
Valid mixer options are `master`, `music`, `record`, `weather`, `block`, `hostile`,`neutral`, `player`, `ambient`
|
||||
and `voice`. `pos` can be either a block, triple of coords, or a list of thee numbers. Uses the same options as a
|
||||
corresponding `playsound` command.
|
||||
|
||||
## Particles
|
||||
|
||||
### `particle(name, pos, count?. spread?, speed?, playername?)`
|
||||
### `particle(name, pos, count?. spread?, speed?, player?)`
|
||||
|
||||
Renders a cloud of particles `name` centered around `pos` position, by default `count` 10 of them, default `speed`
|
||||
of 0, and to all players nearby, but these options can be changed via optional arguments. Follow vanilla `/particle`
|
||||
command on details on those options. Valid particle names are
|
||||
for example `'angry_villager', 'item diamond', 'block stone', 'dust 0.8 0.1 0.1 4'`.
|
||||
|
||||
### `particle_line(name, pos, pos2, density?)`
|
||||
### `particle_line(name, pos, pos2, density?, player?)`
|
||||
|
||||
Renders a line of particles from point `pos` to `pos2` with supplied density (defaults to 1), which indicates how far
|
||||
apart you would want particles to appear, so `0.1` means one every 10cm.
|
||||
apart you would want particles to appear, so `0.1` means one every 10cm. If a player (or player name) is supplied, only
|
||||
that player will receive particles.
|
||||
|
||||
### `particle_rect(name, pos, pos2, density?)`
|
||||
|
||||
Renders a cuboid of particles between point `pos` to `pos2` with supplied density.
|
||||
### `particle_box(name, pos, pos2, density?, player?)`
|
||||
### `particle_rect` (deprecated)
|
||||
|
||||
Renders a cuboid of particles between points `pos` and `pos2` with supplied density. If a player (or player name) is
|
||||
supplied, only that player will receive particles.
|
||||
|
||||
## Markers
|
||||
|
||||
### `draw_shape(shape, duration, key?, value?, ... )`,
|
||||
### `draw_shape(shape, duration, l(key?, value?, ... ))`,
|
||||
### `draw_shape(shape, duration, attribute_map)`
|
||||
### `draw_shape(shape_list)`
|
||||
|
||||
Draws a shape in the world that will expire in `duration` ticks. Other attributes of the shape should be provided as
|
||||
consecutive key - value argument pairs, either as next arguments, or packed in a list, or supplied as a proper key-value
|
||||
`map`. Arguments may include shared shape attributes, which are all optional, as well as shape-specific attributes, that
|
||||
could be either optional or required. Shapes will draw properly on all carpet clients. Other connected players that don't
|
||||
have carpet installed will still be able to see the required shapes in the form of dust particles. Replacement shapes
|
||||
are not required to follow all attributes precisely, but will allow vanilla clients to receive some experience of your
|
||||
apps. One of the attributes that will definitely not be honored is the duration - particles will be send once
|
||||
per shape and last whatever they typically last in the game.
|
||||
|
||||
Shapes can be send one by one, using either of the first three invocations, or batched as a list of shape descriptors.
|
||||
Batching has this benefit that they will be send possibly as one packet, limiting network overhead of
|
||||
sending many small packets to draw several shapes at once.
|
||||
|
||||
Shapes will fail to draw and raise a runtime error if not all its required parameters
|
||||
are specified and all available shapes have some parameters that are required, so make sure to have them in place:
|
||||
|
||||
On the client, shapes can recognize that they are being redrawn again with the same parameters, disregarding the
|
||||
duration parameter. This updates the expiry on the drawn shape to the new value, instead of adding new shape in its
|
||||
place. This can be used for toggling the shapes on and off that has been send previously with very large durations,
|
||||
or simply refresh the shapes periodically in more dynamic applications.
|
||||
|
||||
Optional shared shape attributes:
|
||||
* `color` - integer value indicating the main color of the shape in the form of red, green, blue and alpha components
|
||||
in the form of `0xRRGGBBAA`, with the default of `-1`, so white opaque, or `0xFFFFFFFF`.
|
||||
* `player` - name or player entity to send the shape to. If specified, the shapes will appear only for the specified
|
||||
player, otherwise it will be send to all players in the dimension.
|
||||
* `width` - line thickness, defaults to 2.0pt
|
||||
* `fill` - color for the faces, defaults to no fill. Use `color` attribute format
|
||||
* `follow` - entity, or player name. Shape will follow an entity instead of being static.
|
||||
Follow attribute requires all positional arguments to be relative to the entity and disallow
|
||||
of using entity or block as position markers. You must specify positions as a triple.
|
||||
* `snap` - if `follow` is present, indicated on which axis the snapping to entity coordinates occurs, and which axis
|
||||
will be treated statically, i.e. the coordinate passed in a coord triple is the actual value in the world. Default
|
||||
value is `'xyz'`, meaning the shape will be drawn relatively to the entity in all three directions. Using `xz` for
|
||||
instance makes so that the shape follows the entity, but stays at the same, absolute Y coordinate.
|
||||
|
||||
Available shapes:
|
||||
* `'line'` - draws a straight line between two points
|
||||
* Required attributes:
|
||||
* `from` - triple coordinates, entity, or block value indicating one end of the line
|
||||
* `to` - other end of the line, same format as `from`
|
||||
|
||||
* `'box'` - draws a box with corners in specified points
|
||||
* Required attributes:
|
||||
* `from` - triple coordinates, entity, or block value indicating one corner of the box
|
||||
* `to` - other corner, same format as `from`
|
||||
|
||||
* `'sphere'`:
|
||||
* Required attributes:
|
||||
* `center` - center of the sphere
|
||||
* `radius` - radius of the sphere
|
||||
* Optional attributes:
|
||||
* `level` - level of details, or grid size. The more the denser your sphere. Default level of 0, means that the
|
||||
level of detail will be selected automatically based on radius.
|
||||
|
||||
* `'cylinder'`:
|
||||
* Required attributes:
|
||||
* `center` - center of the base
|
||||
* `radius` - radius of the base circle
|
||||
* Optional attributes:
|
||||
* `axis` - cylinder direction, one of `'x'`, `'y'`, `'z'` defaults to `'y'`
|
||||
* `height` - height of the cyllinder, defaults to `0`, so flat disk. Can be negative.
|
||||
* `level` - level of details, see `'sphere'`.
|
||||
|
||||
### `create_marker(text, pos, rotation?, block?)`
|
||||
|
||||
Spawns a (permanent) marker entity with text or block at position. Returns that entity for further manipulations.
|
||||
Unloading the app that spawned them will cause all the markers from the loaded portion of the world to be removed.
|
||||
Also - if the game loads that marker in the future and the app is not loaded, it will be removed as well.
|
||||
Also, if the game loads that marker in the future and the app is not loaded, it will be removed as well.
|
||||
|
||||
### `remove_all_markers()`
|
||||
|
||||
@@ -58,16 +134,127 @@ as a string value.
|
||||
|
||||
Displays the result of the expression to the chat. Overrides default `scarpet` behaviour of sending everyting to stderr.
|
||||
|
||||
### `format(components, ...)`, `format(l(components, ...))`
|
||||
|
||||
Creates a line of formatted text. Each component is either a string indicating formatting and text it corresponds to
|
||||
or a decorator affecting the component preceding it.
|
||||
|
||||
Regular formatting components is a string that have the structure of:
|
||||
`'<format> <text>'`, like `'gi Hi'`, which in this case indicates a grey, italicised word `'Hi'`. The space to separate the format and the text is mandatory. The format can be empty, but the space still
|
||||
needs to be there otherwise the first word of the text will be used as format, which nobody wants.
|
||||
|
||||
Format is a list of formatting symbols indicating the format. They can be mixed and matched although color will only be
|
||||
applied once. Available symbols include:
|
||||
* `i` - _italic_
|
||||
* `b` - **bold**
|
||||
* `s` - ~~strikethrough~~
|
||||
* `u` - <u>underline</u>
|
||||
* `o` - obfuscated
|
||||
|
||||
And colors:
|
||||
* `w` - White (default)
|
||||
* `y` - Yellow
|
||||
* `m` - Magenta (light purple)
|
||||
* `r` - Red
|
||||
* `c` - Cyan (aqua)
|
||||
* `l` - Lime
|
||||
* `t` - lighT blue
|
||||
* `f` - dark grayF (weird Flex, but ok)
|
||||
* `g` - Gray
|
||||
* `d` - golD
|
||||
* `p` - PurPle
|
||||
* `n` - browN (dark red)
|
||||
* `q` - turQuoise (dark aqua)
|
||||
* `e` - grEEn
|
||||
* `v` - naVy blue
|
||||
* `k` - blaK
|
||||
* `#FFAACC` - arbitrary RGB color (1.16+), hex notation. Use uppercase for A-F symbols
|
||||
|
||||
Decorators (listed as extra argument after the component they would affect):
|
||||
* `'^<format> <text>'` - hover over tooltip text, appearing when hovering with your mouse over the text below.
|
||||
* `'?<suggestion>` - command suggestion - a message that will be pasted to chat when text below it is clicked.
|
||||
* `'!<message>'` - a chat message that will be executed when the text below it is clicked.
|
||||
|
||||
Both suggestions and messages can contain a command, which will be executed as a player that clicks it.
|
||||
|
||||
So far the only usecase for formatted texts is with a `print` command. Otherwise it functions like a normal
|
||||
string value representing what is actually displayed on screen.
|
||||
|
||||
Example usages:
|
||||
<pre>
|
||||
print(format('rbu Error: ', 'r Stuff happened!'))
|
||||
print(format('w Click ','tb [HERE]', '^di Awesome!', '!/kill', 'w \ button to win $1000'))
|
||||
// the reason why I backslash the second space is that otherwise command parser may contract consecutive spaces
|
||||
// not a problem in apps
|
||||
</pre>
|
||||
|
||||
### `logger(expr)`
|
||||
|
||||
Prints the message to system logs, and not to chat.
|
||||
|
||||
### read_file(resource, type)
|
||||
### delete_file(resource, type)
|
||||
### write_file(resource, type, data, ...)
|
||||
|
||||
With the specified `resource` in the scripts folder, of a specific `type`, writes/appends `data` to it, reads its
|
||||
content, or deletes the resource.
|
||||
|
||||
Resource is identified by a path to the file.
|
||||
A path can contain letters, numbers and folder separator: `'/'`. Any other characters are stripped
|
||||
from the name. Empty descriptors are invalid. Do not add file extensions to the descriptor - extensions are inferred
|
||||
based on the `type` of the file.
|
||||
|
||||
Resources can be located in the app specific space, or a shared space for all the apps. Accessing of app-specific
|
||||
resources is guaranteed to be isolated from other apps. Shared resources are... well, shared across all apes, meaning
|
||||
they can eat of each others file, however all access to files is synchronized, and files are never left open, so
|
||||
this should not lead to any access problems.
|
||||
|
||||
If the app's name is `'foo'`, the script location would
|
||||
be `world/scripts/foo.sc`, app
|
||||
specific data directory is under `world/scripts/foo.data/...`, and shared data space is under
|
||||
`world/scripts/shared/...`.
|
||||
|
||||
The default no-name app, via `/script run` command can only save/load/read files from the shared space.
|
||||
|
||||
Functions return `null` if an error is encounter or no file is present (for read and delete operations). Returns `true`
|
||||
for success writes and deletes, and requested data, based on the file type, for read operations.
|
||||
|
||||
NBT files can be written once as they an store one tag at a time. Consecutive writes will overwrite previous data.
|
||||
|
||||
Write operations to text files always result in appending to the existing file, so consecutive writes will increase
|
||||
the size of the file and add data to it. Since files are closed after each write, sending multiple lines of data to
|
||||
write is beneficial for writing speed. To send multiple packs of data, either provide them flat or as a list in the
|
||||
third argument.
|
||||
* `write_file('temp', 'text', 'foo', 'bar', 'baz')` or
|
||||
* write_file('temp', 'text', l('foo', 'bar', 'baz'))
|
||||
|
||||
To log a single line of string
|
||||
|
||||
Supported values for resource `type` is:
|
||||
* `nbt` - NBT tag
|
||||
* `text` - text resource with automatic newlines added
|
||||
* `raw` - text resource without implied newlines
|
||||
* `shared_nbt`, `shared_text`, `shared_raw` - shared versions of the above
|
||||
|
||||
NBT files have extension `.nbt`, store one NBT tag, and return a NBT type value. Text files have `.txt` extension,
|
||||
stores multiple lines of text and returns lists of all lines from the file. With `write_file`, multiple lines can be
|
||||
sent to the file at once. The only difference between `raw` and `text` types are automatic newlines added after each
|
||||
record to the file.
|
||||
|
||||
<pre>
|
||||
write_file('foo', 'shared_text, l('one', 'two'));
|
||||
write_file('foo', 'shared_text', 'three\n', 'four\n');
|
||||
write_file('foo', 'shared_raw', 'five\n', 'six\n');
|
||||
|
||||
read_file('foo', 'shared_text') => ['one', 'two', 'three', '', 'four', '', 'five', 'six']
|
||||
</pre>
|
||||
|
||||
### `run(expr)`
|
||||
|
||||
Runs a vanilla command from the string result of the `expr` and returns its success count
|
||||
|
||||
<pre>
|
||||
run('fill 1 1 1 10 10 10 air') -> 123 // 123 block were filled, this operation was successful 123 times out of a possible 1000 blocks volume
|
||||
run('fill 1 1 1 10 10 10 air') -> 123 // 123 block were filled, this operation was successful 123 times out of a possible 1000 block volume
|
||||
run('give @s stone 4') -> 1 // this operation was successful once
|
||||
</pre>
|
||||
|
||||
@@ -76,23 +263,66 @@ run('give @s stone 4') -> 1 // this operation was successful once
|
||||
Performs autosave, saves all chunks, player data, etc. Useful for programs where autosave is disabled due to
|
||||
performance reasons and saves the world only on demand.
|
||||
|
||||
### `load_app_data(), load_app_data(file)`
|
||||
### `load_app_data()`
|
||||
|
||||
NOTE: usages with arguments, so `load_app_data(file)` and `load_app_data(file, shared?)` are deprecated.
|
||||
Use `read_file` instead.
|
||||
|
||||
Loads the app data associated with the app from the world /scripts folder. Without argument returns the memory
|
||||
managed and buffered / throttled NBT tag. With a file name - reads explicitly a file with that name from the
|
||||
scripts folder.
|
||||
managed and buffered / throttled NBT tag. With a file name, reads explicitly a file with that name from the
|
||||
scripts folder that belongs exclusively to the app. if `shared` is true, the file location is not exclusive
|
||||
to the app anymore, but located in a shared app space.
|
||||
|
||||
File descriptor can contain letters, numbers and folder separator: `'/'`. Any other characters are stripped
|
||||
from the name before saving/loading. Empty descriptors are invalid. Do not add file extensions to the descriptor
|
||||
|
||||
Function returns nbt value with the file content, or `null` if the file is missing or there were problems
|
||||
with retrieving the data.
|
||||
|
||||
The default no-name app, via `/script run` command can only save/load file from the shared data location.
|
||||
|
||||
If the app's name is `'foo'`, the script location would
|
||||
be `world/scripts/foo.sc`, system-managed default app data storage is in `world/scripts/foo.data.nbt`, app
|
||||
specific data directory is under `world/scripts/foo.data/bar/../baz.nbt`, and shared data space is under
|
||||
`world/scripts/shared/bar/../baz.nbt`.
|
||||
|
||||
You can use app data to save non-vanilla information separately from the world and other scripts.
|
||||
|
||||
### `store_app_data(tag), store_app_data(tag, file)`
|
||||
### `store_app_data(tag)`
|
||||
|
||||
Stores the app data associated with the app from the world /scripts folder. With the `file` parameter saves
|
||||
immediately and with every call, without `file` parameter, it may take up to 10 seconds for the output file
|
||||
Note: `store_app_data(tag, file)` and `store_app_data(tag, file, shared?)` usages deprecated. Use `write_file` instead.
|
||||
|
||||
Stores the app data associated with the app from the world `/scripts` folder. With the `file` parameter saves
|
||||
immediately and with every call to a specific file defined by the `file`, either in app space, or in the scripts
|
||||
shared space if `shared` is true. Without `file` parameter, it may take up to 10
|
||||
seconds for the output file
|
||||
to sync preventing flickering in case this tag changes frequently. It will be synced when server closes.
|
||||
|
||||
Returns `true` if the file was saved successfully, `false` otherwise.
|
||||
|
||||
Uses the same file structure for exclusive app data, and shared data folder as `load_app_data`.
|
||||
|
||||
### `tick_time()`
|
||||
|
||||
Returns game tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time
|
||||
Returns server tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time.
|
||||
|
||||
### `world_time()`
|
||||
|
||||
Returns dimension-specific tick counter.
|
||||
|
||||
### `day_time(new_time?)`
|
||||
|
||||
Returns current daytime clock value. If `new_time` is specified, sets a new clock
|
||||
to that value. Daytime clocks are shared between all dimensions.
|
||||
|
||||
### `last_tick_times()`
|
||||
|
||||
Returns a 100-long array of recent tick times, in milliseconds. First item on the list is the most recent tick
|
||||
If called outside of the main tick (either throgh scheduled tasks, or async execution), then the first item on the
|
||||
list may refer to the previous tick performance. In this case the last entry (tick 100) would refer to the most current
|
||||
tick. For all intent and purpose, `last_tick_times():0` should be used as last tick execution time, but
|
||||
individual tick times may vary greatly, and these need to be taken with the little grain of
|
||||
averaging.
|
||||
|
||||
### `game_tick(mstime?)`
|
||||
|
||||
@@ -118,6 +348,9 @@ Returns current dimension that the script runs in.
|
||||
Evaluates the expression `expr` with different dimension execution context. `smth` can be an entity,
|
||||
world-localized block, so not `block('stone')`, or a string representing a dimension like:
|
||||
`'nether'`, `'the_nether'`, `'end'` or `'overworld'`, etc.
|
||||
|
||||
### `view_distance()`
|
||||
Returns the view distance of the server.
|
||||
|
||||
### `schedule(delay, function, args...)`
|
||||
|
||||
@@ -140,7 +373,7 @@ Queries in-game statistics for certain values. Categories include:
|
||||
|
||||
For the options of `entry`, consult your statistics page, or give it a guess.
|
||||
|
||||
The call will return `null` if the statistics options are incorrect, or player didn't get these in their history.
|
||||
The call will return `null` if the statistics options are incorrect, or player doesn't have them in their history.
|
||||
If the player encountered the statistic, or game created for him empty one, it will return a number.
|
||||
Scarpet will not affect the entries of the statistics, even if it is just creating empty ones. With `null` response
|
||||
it could either mean your input is wrong, or statistic has effectively a value of `0`.
|
||||
it could either mean your input is wrong, or statistic effectively has a value of `0`.
|
||||
@@ -2,11 +2,15 @@
|
||||
|
||||
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(cx, cy, cz, dx, dy, dz, px?, py?, pz?, expr)`, `scan(center, range, lower_range?, expr)`
|
||||
|
||||
Evaluates expression over area of blocks defined by its center (`cx, cy, cz`), expanded in all directions
|
||||
by `dx, dy, dz` blocks, or optionally in negative with `d` coords, and `p` coords in positive values. `expr`
|
||||
receives `_x, _y, _z` as coords of current analyzed block and `_`, which represents the block itself.
|
||||
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
|
||||
positive values.
|
||||
`center` can be defined either as three coordinates, a list of three coords, or a block value.
|
||||
`range` and `lower_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.
|
||||
`expr` receives `_x, _y, _z` as coords of current analyzed block and `_`, which represents the block itself.
|
||||
|
||||
Returns number of successful evaluations of `expr` (with `true` boolean result) unless called in void context,
|
||||
which would cause the expression not be evaluated for their boolean value.
|
||||
@@ -16,7 +20,7 @@ return value. `break` return value has no effect.
|
||||
|
||||
### `volume(x1, y1, z1, x2, y2, z2, expr)`
|
||||
|
||||
Evaluates expression for each block in the area, the same as the `scan`function, but using two opposite corners of
|
||||
Evaluates expression for each block in the area, the same as the `scan` function, but using two opposite corners of
|
||||
the rectangular cuboid. Any corners can be specified, its like you would do with `/fill` command.
|
||||
|
||||
For return value and handling `break` and `continue` statements, see `scan` function above.
|
||||
@@ -32,7 +36,7 @@ for(neighbours(x,y,z),air(_)) => 4 // number of air blocks around a block
|
||||
### `rect(cx, cy, cz, dx?, dy?, dz?, px?, py?, pz?)`
|
||||
|
||||
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 of respective
|
||||
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.
|
||||
|
||||
### `diamond(cx, cy, cz, radius?, height?)`
|
||||
|
||||
@@ -70,8 +70,8 @@ set(x,y,z,'hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:
|
||||
|
||||
Evaluates subexpression without causing updates when blocks change in the world
|
||||
|
||||
Consider following scenario: We would like to generate a bunch of terrain in a flat world following a perling noise
|
||||
generator. The following code causes cascading effect as blocks placed on chunk borders will cause other chunks to get
|
||||
Consider following scenario: We would like to generate a bunch of terrain in a flat world following a perlin noise
|
||||
generator. The following code causes a cascading effect as blocks placed on chunk borders will cause other chunks to get
|
||||
loaded to full, thus generated:
|
||||
|
||||
<pre>
|
||||
@@ -85,7 +85,7 @@ __on_chunk_generated(x, z) -> (
|
||||
)
|
||||
</pre>
|
||||
|
||||
The following addition resolves this issue, by not allowing block updates pass chunk borders:
|
||||
The following addition resolves this issue, by not allowing block updates past chunk borders:
|
||||
|
||||
<pre>
|
||||
__config() -> m(l('scope', 'global'));
|
||||
@@ -102,19 +102,26 @@ __on_chunk_generated(x, z) -> (
|
||||
|
||||
### `place_item(item, pos, facing?, sneak?)`
|
||||
|
||||
Places a given item in the world like it was placed by a player. Item names are default minecraft item name,
|
||||
Uses a given item in the world like it was used by a player. Item names are default minecraft item name,
|
||||
less the minecraft prefix. Default facing is 'up', but there are other options: 'down', 'north', 'east', 'south',
|
||||
'west', but also there are other secondary directions important for placement of blocks like stairs, doors, etc.
|
||||
Try experiment with options like 'north-up' which is placed facing north with cursor pointing to the upper part of the
|
||||
block, or 'up-north', which means a block placed facing up (player looking down) and placed smidge away of the block
|
||||
center towards north. Optional sneak is a boolean indicating if a player would be sneaking while placing the
|
||||
block - this option only affects placement of chests and scaffolding at the moment. Returns true if placement was
|
||||
block - this option only affects placement of chests and scaffolding at the moment.
|
||||
|
||||
Works with items that have the right-click effect on the block placed, like `bone_meal` on grass or axes on logs,
|
||||
but doesn't open chests / containers, so have no effect on interactive blocks, like TNT, comparators, etc.
|
||||
|
||||
Returns true if placement/use was
|
||||
successful, false otherwise.
|
||||
|
||||
<pre>
|
||||
place_item('stone',x,y,z) // places a stone block on x,y,z block
|
||||
place_item('piston,x,y,z,'down') // places a piston facing down
|
||||
place_item('carrot',x,y,z) // attempts to plant a carrot plant. Returns true if could place carrots at that position.
|
||||
place_item('bone_meal',x,y,z) // attempts to bonemeal the ground.
|
||||
place_item('wooden_axe',x,y,z) // attempts to strip the log.
|
||||
</pre>
|
||||
|
||||
### `set_poi(pos, type, occupancy?)`
|
||||
@@ -127,9 +134,10 @@ If type is `null`, POI at position is removed. In any case, previous POI is also
|
||||
Interestingly, `unemployed`, and `nitwit` are not used in the game, meaning, they could be used as permanent spatial
|
||||
markers for scarpet apps. `meeting` is the only one with increased max occupancy of 32.
|
||||
|
||||
### `set_biome(pos, biome_name)`
|
||||
### `set_biome(pos, biome_name, update=true)`
|
||||
|
||||
changes biome at that block position.
|
||||
Changes the biome at that block position. if update is specified and false, then chunk will not be refreshed
|
||||
on the clients. Biome changes can only be send to clients with the entire data from the chunk.
|
||||
|
||||
### `update(pos)`
|
||||
|
||||
@@ -145,7 +153,7 @@ Causes a random tick at position.
|
||||
|
||||
### `destroy(pos), destroy(pos, -1), destroy(pos, <N>), destroy(pos, tool, nbt?)`
|
||||
|
||||
Destroys the block like it was mined by a player. Add -1 for silk touch, and positive number for fortune level.
|
||||
Destroys the block like it was mined by a player. Add -1 for silk touch, and a positive number for fortune level.
|
||||
If tool is specified, and optionally its nbt, it will use that tool and will attempt to mine the block with this tool.
|
||||
If called without item context, this function, unlike harvest, will affect all kinds of blocks. If called with item
|
||||
in context, it will fail to break blocks that cannot be broken by a survival player.
|
||||
@@ -161,28 +169,28 @@ for mining. Obviously, in this case the use of `harvest` would be much more appl
|
||||
<pre>
|
||||
mine(x,y,z) ->
|
||||
(
|
||||
p = player();
|
||||
slot = p~'selected_slot';
|
||||
item_tuple = inventory_get(p, slot);
|
||||
if (!item_tuple, destroy(x,y,z,'air'); return()); // empty hand, just break with 'air'
|
||||
l(item, count, tag) = item_tuple;
|
||||
tag_back = destroy(x,y,z, item, tag);
|
||||
if (tag_back == false, // failed to break the item
|
||||
return(tag_back)
|
||||
);
|
||||
if (tag_back == true, // block broke, tool has no tag
|
||||
return(tag_back)
|
||||
);
|
||||
if (tag_back == null, //item broke
|
||||
delete(tag:'Damage');
|
||||
inventory_set(p, slot, count-1, item, tag);
|
||||
return(tag_back)
|
||||
);
|
||||
if (type(tag_back) == 'nbt', // item didn't break, here is the effective nbt
|
||||
inventory_set(p, slot, count, item, tag_back);
|
||||
return(tag_back)
|
||||
);
|
||||
print('How did we get here?');
|
||||
p = player();
|
||||
slot = p~'selected_slot';
|
||||
item_tuple = inventory_get(p, slot);
|
||||
if (!item_tuple, destroy(x,y,z,'air'); return()); // empty hand, just break with 'air'
|
||||
l(item, count, tag) = item_tuple;
|
||||
tag_back = destroy(x,y,z, item, tag);
|
||||
if (tag_back == false, // failed to break the item
|
||||
return(tag_back)
|
||||
);
|
||||
if (tag_back == true, // block broke, tool has no tag
|
||||
return(tag_back)
|
||||
);
|
||||
if (tag_back == null, //item broke
|
||||
delete(tag:'Damage');
|
||||
inventory_set(p, slot, count-1, item, tag);
|
||||
return(tag_back)
|
||||
);
|
||||
if (type(tag_back) == 'nbt', // item didn't break, here is the effective nbt
|
||||
inventory_set(p, slot, count, item, tag_back);
|
||||
return(tag_back)
|
||||
);
|
||||
print('How did we get here?');
|
||||
)
|
||||
</pre>
|
||||
|
||||
@@ -200,9 +208,9 @@ Returns a triple of coordinates of a specified block or entity. Technically enti
|
||||
and the same can be achieved with `query(entity,'pos')`, but for simplicity `pos` allows to pass all positional objects.
|
||||
|
||||
<pre>
|
||||
pos(block(0,5,0)) => l(0,5,0)
|
||||
pos(block(0,5,0)) => l(0,5,0)
|
||||
pos(players()) => l(12.3, 45.6, 32.05)
|
||||
pos(block('stone')) => Error: Cannot fetch position of an unrealized block
|
||||
pos(block('stone')) => Error: Cannot fetch position of an unrealized block
|
||||
</pre>
|
||||
|
||||
### `pos_offset(pos, direction, amount?)`
|
||||
@@ -260,51 +268,51 @@ poi(x,y,z,5) => [['nether_portal',0,[7,8,9]],['nether_portal',0,[7,9,9]]] // two
|
||||
|
||||
### `biome(pos)`
|
||||
|
||||
returns biome at that block position.
|
||||
Returns the biome at that block position.
|
||||
|
||||
### `solid(pos)`
|
||||
|
||||
Boolean function, true if the block is solid
|
||||
Boolean function, true if the block is solid.
|
||||
|
||||
### `air(pos)`
|
||||
|
||||
Boolean function, true if a block is air.... or cave air... or void air.... or any other air they come up with.
|
||||
Boolean function, true if a block is air... or cave air... or void air... or any other air they come up with.
|
||||
|
||||
### `liquid(pos)`
|
||||
|
||||
Boolean function, true if the block is liquid, or liquidlogged
|
||||
Boolean function, true if the block is liquid, or waterlogged (with any liquid).
|
||||
|
||||
### `flammable(pos)`
|
||||
|
||||
Boolean function, true if the block is flammable
|
||||
Boolean function, true if the block is flammable.
|
||||
|
||||
### `transparent(pos)`
|
||||
|
||||
Boolean function, true if the block is transparent
|
||||
Boolean function, true if the block is transparent.
|
||||
|
||||
### `opacity(pos)`
|
||||
|
||||
Numeric, returning opacity level of a block
|
||||
Numeric function, returning the opacity level of a block.
|
||||
|
||||
### `blocks_daylight(pos)`
|
||||
|
||||
Boolean function, true if the block blocks daylight
|
||||
Boolean function, true if the block blocks daylight.
|
||||
|
||||
### `emitted_light(pos)`
|
||||
|
||||
Numeric, returning light level emitted from block
|
||||
Numeric function, returning the light level emitted from the block.
|
||||
|
||||
### `light(pos)`
|
||||
|
||||
Integer function, returning total light level at position
|
||||
Numeric function, returning the total light level at position.
|
||||
|
||||
### `block_light(pos)`
|
||||
|
||||
Integer function, returning block light at position. From torches and other light sources.
|
||||
Numeric function, returning the block light at position (from torches and other light sources).
|
||||
|
||||
### `sky_light(pos)`
|
||||
|
||||
Numeric function, returning sky light at position. From the sky access.
|
||||
Numeric function, returning the sky light at position (from sky access).
|
||||
|
||||
### `see_sky(pos)`
|
||||
|
||||
@@ -352,7 +360,7 @@ Boolean function, true if the block ticks randomly.
|
||||
|
||||
### `blocks_movement(pos)`
|
||||
|
||||
Boolean function, true if block at position blocks movement.
|
||||
Boolean function, true if the block at position blocks movement.
|
||||
|
||||
### `block_sound(pos)`
|
||||
|
||||
@@ -478,11 +486,11 @@ Returns the map colour of a block at position. One of:
|
||||
### `loaded(pos)`
|
||||
|
||||
Boolean function, true if the block is accessible for the game mechanics. Normally `scarpet` doesn't check if operates
|
||||
on loaded area - the game will automatically load missing blocks. We see this as advantage. Vanilla `fill/clone`
|
||||
on loaded area - the game will automatically load missing blocks. We see this as an advantage. Vanilla `fill/clone`
|
||||
commands only check the specified corners for loadness.
|
||||
|
||||
To check if block is truly loaded, I mean in memory, use `generation_status(x) != null`, as chunks can still be loaded
|
||||
outside of the playable area, just are not used any of the game mechanics processes.
|
||||
To check if a block is truly loaded, I mean in memory, use `generation_status(x) != null`, as chunks can still be loaded
|
||||
outside of the playable area, just are not used by any of the game mechanic processes.
|
||||
|
||||
<pre>
|
||||
loaded(pos(players())) => 1
|
||||
@@ -497,7 +505,7 @@ Deprecated as of scarpet 1.6, use `loaded_status(x) > 0`, or just `loaded(x)` wi
|
||||
|
||||
### `loaded_status(pos)`
|
||||
|
||||
Returns loaded status as per new 1.14 chunk ticket system, 0 for inaccessible, 1 for border chunk, 2 for ticking,
|
||||
Returns loaded status as per new 1.14 chunk ticket system, 0 for inaccessible, 1 for border chunk, 2 for redstone ticking,
|
||||
3 for entity ticking
|
||||
|
||||
### `is_chunk_generated(pos)`, `is_chunk_generated(pos, force)`
|
||||
@@ -519,13 +527,41 @@ Returns generation status as per the ticket system. Can return any value from se
|
||||
can only be stable in a few states: `full`, `features`, `liquid_carvers`, and `structure_starts`. Returns `null`
|
||||
if the chunk is not in memory unless called with optional `true`.
|
||||
|
||||
### `inhabited_time(pos)`
|
||||
|
||||
Returns inhabited time for a chunk.
|
||||
|
||||
### `spawn_potential(pos)`
|
||||
|
||||
Returns spawn potential at a location (1.16+ only)
|
||||
|
||||
### `structure_eligibility(pos, ?structure, ?size_needed)`
|
||||
|
||||
Checks wordgen eligibility for a structure in a given chunk. If no structure is given, or `null`, then it will check
|
||||
for all structures. If bounding box of the structures is also requested, it will compute size of potential
|
||||
structures. This function, unlike other in the `structure*` category is not using world data nor accesses chunks
|
||||
making it preferred for scoping ungenerated terrain, but it takes some compute resources to calculate the structure.
|
||||
|
||||
Unlike `'structure'` this will return a tentative structure location. Random factors in world generation may prevent
|
||||
the actual structure from forming.
|
||||
|
||||
If structure is specified, it will return `null` if a chunk is not eligible, `true` if the structure should appear, or
|
||||
a map with two values: `'box'` for a pair of coordinates indicating bounding box of the structure, and `'pieces'` for
|
||||
list of elements of the structure (as a tuple), with its name, direction, and box coordinates of the piece.
|
||||
|
||||
If structure is not specified, it will return a set of structure names that are eligible, or a map with structures
|
||||
as keys, and same type of map values as with a single structure call. An empty set or an empty map would indicate that nothing
|
||||
should be generated there.
|
||||
|
||||
|
||||
### `structures(pos), structures(pos, structure_name)`
|
||||
|
||||
Returns structure information for a given block position. Note that structure information is the same for all the
|
||||
blocks from the same chunk. `structures` function can be called with a block, or a block and a structure name. In
|
||||
the first case it returns a map of structures at a given position, keyed by structure name, with values indicating
|
||||
the bounding box of the structure - a pair of two 3-value coords (see examples). When called with an extra structure
|
||||
name, returns list of components for that structure, with their name, direction and two sets of coordinates
|
||||
name, returns a map with two values, `'box'` for bounding box of the structure, and `'pieces'` for a list of
|
||||
components for that structure, with their name, direction and two sets of coordinates
|
||||
indicating the bounding box of the structure piece.
|
||||
|
||||
### `structure_references(pos), structure_references(pos, structure_name)`
|
||||
@@ -540,7 +576,7 @@ then to get its bounding boxes.
|
||||
|
||||
Creates or removes structure information of a structure associated with a chunk of `pos`. Unlike `plop`, blocks are
|
||||
not placed in the world, only structure information is set. For the game this is a fully functional structure even
|
||||
if blocks are not set. To remove structure a given point is in, use `structure_references` to find where current
|
||||
if blocks are not set. To remove the structure a given point is in, use `structure_references` to find where current
|
||||
structure starts.
|
||||
|
||||
### `plop(pos, what)`
|
||||
@@ -586,6 +622,13 @@ Structure list:
|
||||
* `village_savanna`: Savanna, acacia village.
|
||||
* `village_taiga`: Taiga, spruce village.
|
||||
* `village_snowy`: Resolute, Canada.
|
||||
* `nether_fossil`: Pile of bones (1.16)
|
||||
* `ruined_portal`: Ruined portal, random variant.
|
||||
* `bastion_remnant`: Piglin bastion, random variant for the chunk (1.16)
|
||||
* `bastion_remnant_housing`: Housing units version of a piglin bastion (1.16)
|
||||
* `bastion_remnant_stable`: Hoglin stables version of q piglin bastion (1.16)
|
||||
* `bastion_remnant_treasure`: Treasure room version of a piglin bastion (1.16)
|
||||
* `bastion_remnant_bridge` : Bridge version of a piglin bastion (1.16)
|
||||
|
||||
Feature list:
|
||||
|
||||
@@ -642,6 +685,18 @@ Feature list:
|
||||
* `coral`: random coral structure. Require water to spawn.
|
||||
* `sea_pickle`
|
||||
* `boulder`: A rocky, mossy formation from a giant taiga biome. Doesn't update client properly, needs relogging.
|
||||
* `crimson_fungus` (1.16)
|
||||
* `warped_fungus` (1.16)
|
||||
* `nether_sprouts` (1.16)
|
||||
* `crimson_roots` (1.16)
|
||||
* `warped_roots` (1.16)
|
||||
* `weeping_vines` (1.16)
|
||||
* `twisting_vines` (1.16)
|
||||
* `basalt_pillar` (1.16)
|
||||
|
||||
### `reload_chunk(pos)`
|
||||
|
||||
Sends full chunk data to clients. Useful when lots stuff happened and you want to refresh it on the clients.
|
||||
|
||||
### `reset_chunk(pos)`, `reset_chunk(from_pos, to_pos)`, `reset_chunk(l(pos, ...))`
|
||||
Removes and resets the chunk, all chunks in the specified area or all chunks in a list at once, removing all previous
|
||||
@@ -657,3 +712,10 @@ It returns a `map` with a report indicating how many chunks were affected, and h
|
||||
* `layer_count_<status>`: number of chunks for which a `<status>` generation step has been performed
|
||||
* `layer_time_<status>`: cumulative time for all chunks spent on generating `<status>` step
|
||||
|
||||
### add_chunk_ticket(pos, type, radius)
|
||||
|
||||
Adds a chunk ticket at a position, which makes the game to keep the designated area centered around
|
||||
`pos` with radius of `radius` loaded for a predefined amount of ticks, defined by `type`. Allowed types
|
||||
are `portal`: 300 ticks, `teleport`: 5 ticks, and `unknown`: 1 tick. Radius can be from 1 to 32 ticks.
|
||||
|
||||
This function is tentative - will likely change when chunk ticket API is properly fleshed out.
|
||||
+201
-110
@@ -3,23 +3,23 @@
|
||||
## Entity Selection
|
||||
|
||||
Entities have to be fetched before using them. Entities can also change their state between calls to the script if
|
||||
game happens either in between separate calls to the programs, or if the program calls `game_tick` on its own.
|
||||
In this case - entities would need to be re-fetched, or the code should account for entities getting dead.
|
||||
game ticks occur either in between separate calls to the programs, or if the program calls `game_tick` on its own.
|
||||
In this case - entities would need to be re-fetched, or the code should account for entities dying.
|
||||
|
||||
### `player(), player(type), player(name)`
|
||||
|
||||
With no arguments, it returns the calling player or the player closest to the caller. Note that the main context
|
||||
will receive `p` variable pointing to this player. With `type` or `name` specified it will try first to match a type,
|
||||
will receive `p` variable pointing to this player. With `type` or `name` specified, it will try first to match a type,
|
||||
returning a list of players matching a type, and if this fails, will assume its player name query retuning player with
|
||||
that name, or `null` if no player was found. With `'all'`, list of all players in the game, in all dimensions, so end
|
||||
user needs to be cautious, that you might be referring to wrong blocks and entities around the player in question.
|
||||
With `type = '*'` it returns all players in caller dimension, `'survival'` returns all survival and adventure players,
|
||||
`'creative'` returns all creative players, `'spectating'` returns all spectating players, and `'!spectating'`,
|
||||
all not-spectating players. If all fails, with `name`, the player in question, if is logged in.
|
||||
all not-spectating players. If all fails, with `name`, the player in question, if he/she is logged in.
|
||||
|
||||
### `entity_id(uuid), entity_id(id)`
|
||||
|
||||
Fetching entities wither by their ID obtained via `entity ~ 'id'`, which is unique for a dimension and current world
|
||||
Fetching entities either by their ID obtained via `entity ~ 'id'`, which is unique for a dimension and current world
|
||||
run, or by UUID, obtained via `entity ~ 'uuid'`. It returns null if no such entity is found. Safer way to 'store'
|
||||
entities between calls, as missing entities will be returning `null`. Both calls using UUID or numerical ID are `O(1)`,
|
||||
but obviously using UUIDs takes more memory and compute.
|
||||
@@ -38,12 +38,12 @@ selectors are available:
|
||||
### `entity_area(type, cx, cy, cz, dx, dy, dz)`
|
||||
|
||||
Returns entities of a specified type in an area centered on `cx, cy, cz` and at most `dx, dy, dz` blocks away from
|
||||
the center point. Uses same selectors as `entities_list`
|
||||
the center point. Uses the same selectors as `entities_list`.
|
||||
|
||||
### `entity_selector(selector)`
|
||||
|
||||
Returns entities satisfying given vanilla entity selector. Most complex among all the methods of selecting entities,
|
||||
but the most capable. Selectors are cached so should be as fast as other methods of selecting entities.
|
||||
Returns entities satisifying given vanilla entity selector. Most complex among all the methods of selecting entities,
|
||||
but the most capable. Selectors are cached so it should be as fast as other methods of selecting entities.
|
||||
|
||||
### `spawn(name, pos, nbt?)`
|
||||
|
||||
@@ -53,168 +53,186 @@ you get the entity back as a return value, which is swell.
|
||||
|
||||
## Entity Manipulation
|
||||
|
||||
Unlike with blocks, that use plethora of vastly different querying functions, entities are queried with `query`
|
||||
function and altered via `modify` function. Type of information needed or values to be modified are different for
|
||||
Unlike with blocks, that use a plethora of vastly different querying functions, entities are queried with the `query`
|
||||
function and altered via the `modify` function. Type of information needed or values to be modified are different for
|
||||
each call.
|
||||
|
||||
Using `~` (in) operator is an alias for `query`. Especially useful if a statement has no arguments,
|
||||
which in this case can be radically simplified
|
||||
which in this case can be radically simplified:
|
||||
|
||||
<pre>
|
||||
query(p, 'name') <=> p ~ 'name' // much shorter and cleaner
|
||||
query(p, 'holds', 'offhand') <=> p ~ l('holds', 'offhand') // not really but can be done
|
||||
</pre>
|
||||
|
||||
### `query(e,'removed')`
|
||||
### `query(e, 'removed')`
|
||||
|
||||
Boolean. True if the entity is removed
|
||||
Boolean. True if the entity is removed.
|
||||
|
||||
### `query(e,'id')`
|
||||
### `query(e, 'id')`
|
||||
|
||||
Returns numerical id of the entity. Most efficient way to keep track of entites in a script.
|
||||
Ids are only unique within current game session (ids are not preserved between restarts),
|
||||
and dimension (each dimension has its own ids which can overlap.
|
||||
and dimension (each dimension has its own ids which can overlap).
|
||||
|
||||
### `query(e,'uuid')`
|
||||
### `query(e, 'uuid')`
|
||||
|
||||
Returns UUID (unique id) of the entity. Can be used to access entities with the other vanilla commands and
|
||||
Returns the UUID (unique id) of the entity. Can be used to access entities with the other vanilla commands and
|
||||
remains unique regardless of the dimension, and is preserved between game restarts. Apparently players cannot be
|
||||
accessed via UUID, but name instead.
|
||||
accessed via UUID, but should be accessed with their name instead.
|
||||
|
||||
<pre>
|
||||
map(entities_area('*',x,y,z,30,30,30),run('kill '+query(_,'id'))) // doesn't kill the player
|
||||
</pre>
|
||||
|
||||
### `query(e,'pos')`
|
||||
### `query(e, 'pos')`
|
||||
|
||||
Triple of entity position
|
||||
Triple of the entity's position
|
||||
|
||||
### `query(e,'location')`
|
||||
### `query(e, 'location')`
|
||||
|
||||
Quin-tuple of entity position (x, y, and z coords), and rotation (yaw, pitch)
|
||||
Quin-tuple of the entity's position (x, y, and z coords), and rotation (yaw, pitch)
|
||||
|
||||
### `query(e,'x'), query(e,'y'), query(e,'z')`
|
||||
### `query(e, 'x'), query(e, 'y'), query(e, 'z')`
|
||||
|
||||
Respective entity coordinate
|
||||
Respective component of entity's coordinates
|
||||
|
||||
### `query(e,'pitch'), query(e,'yaw')`
|
||||
### `query(e, 'pitch'), query(e, 'yaw')`
|
||||
|
||||
Pitch and Yaw or where entity is looking.
|
||||
|
||||
### `query(e,'look')`
|
||||
### `query(e, 'look')`
|
||||
|
||||
Returns a 3d vector where the entity is looking.
|
||||
|
||||
### `query(e,'motion')`
|
||||
### `query(e, 'motion')`
|
||||
|
||||
Triple of entity motion vector, `l(motion_x, motion_y, motion_z)`
|
||||
Triple of entity's motion vector, `l(motion_x, motion_y, motion_z)`
|
||||
|
||||
### `query(e,'motion_x'), query(e,'motion_y'), query(e,'motion_z')`
|
||||
### `query(e, 'motion_x'), query(e, 'motion_y'), query(e, 'motion_z')`
|
||||
|
||||
Respective component of the motion vector
|
||||
Respective component of the entity's motion vector
|
||||
|
||||
### `query(e,'name'), query(e,'custom_name'), query(e,'type')`
|
||||
### `query(e, 'name'), query(e, 'display_name'), query(e, 'custom_name'), query(e, 'type')`
|
||||
|
||||
String of entity name
|
||||
|
||||
<pre>query(e,'name') => Leatherworker
|
||||
<pre>
|
||||
query(e,'name') => Leatherworker
|
||||
query(e,'custom_name') => null
|
||||
query(e,'type') => villager
|
||||
</pre>
|
||||
|
||||
### `query(e,'is_riding')`
|
||||
## `query(e, 'command_name')`
|
||||
|
||||
Boolean. True if riding another entity.
|
||||
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,'is_ridden')`
|
||||
<pre>
|
||||
run('/kill ' + e~'command_name');
|
||||
</pre>
|
||||
|
||||
Boolean. True if another entity is riding it.
|
||||
### `query(e, 'is_riding')`
|
||||
|
||||
### `query(e,'passengers')`
|
||||
Boolean, true if the entity is riding another entity.
|
||||
|
||||
### `query(e, 'is_ridden')`
|
||||
|
||||
Boolean, true if another entity is riding it.
|
||||
|
||||
### `query(e, 'passengers')`
|
||||
|
||||
List of entities riding the entity.
|
||||
|
||||
### `query(e,'mount')`
|
||||
### `query(e, 'mount')`
|
||||
|
||||
Entity that `e` rides.
|
||||
|
||||
### `query(e,'tags')`
|
||||
### `query(e, 'tags')`
|
||||
|
||||
List of entity tags.
|
||||
List of entity's tags.
|
||||
|
||||
### `query(e,'has_tags',tag)`
|
||||
### `query(e, 'has_tag',tag)`
|
||||
|
||||
Boolean, True if the entity is marked with `tag`.
|
||||
Boolean, true if the entity is marked with `tag`.
|
||||
|
||||
### `query(e,'is_burning')`
|
||||
### `query(e, 'is_burning')`
|
||||
|
||||
Boolean, True if the entity is burning.
|
||||
Boolean, true if the entity is burning.
|
||||
|
||||
### `query(e,'fire')`
|
||||
### `query(e, 'fire')`
|
||||
|
||||
Number of remaining ticks of being on fire.
|
||||
|
||||
### `query(e,'silent')`
|
||||
### `query(e, 'silent')`
|
||||
|
||||
Boolean, True if the entity is silent.
|
||||
Boolean, true if the entity is silent.
|
||||
|
||||
### `query(e,'gravity')`
|
||||
### `query(e, 'gravity')`
|
||||
|
||||
Boolean, True if the entity is affected by gravity, like most entities do.
|
||||
Boolean, true if the entity is affected by gravity, like most entities are.
|
||||
|
||||
### `query(e,'immune_to_fire')`
|
||||
### `query(e, 'immune_to_fire')`
|
||||
|
||||
Boolean, True if the entity is immune to fire.
|
||||
Boolean, true if the entity is immune to fire.
|
||||
|
||||
### `query(e,'dimension')`
|
||||
### `query(e, 'dimension')`
|
||||
|
||||
Name of the dimension entity is in.
|
||||
Name of the dimension the entity is in.
|
||||
|
||||
### `query(e,'height')`
|
||||
### `query(e, 'height')`
|
||||
|
||||
Height of the entity.
|
||||
Height of the entity in blocks.
|
||||
|
||||
### `query(e,'width')`
|
||||
### `query(e, 'width')`
|
||||
|
||||
Width of the entity.
|
||||
Width of the entity in blocks.
|
||||
|
||||
### `query(e,'eye_height')`
|
||||
### `query(e, 'eye_height')`
|
||||
|
||||
Eye height of the entity.
|
||||
Eye height of the entity in blocks.
|
||||
|
||||
### `query(e,'age')`
|
||||
### `query(e, 'age')`
|
||||
|
||||
Age, in ticks, of the entity, i.e. how long it existed.
|
||||
Age of the entity in ticks, i.e. how long it existed.
|
||||
|
||||
### `query(e,'breeding_age')`
|
||||
### `query(e, 'breeding_age')`
|
||||
|
||||
Breeding age of passive entity, in ticks. If negative it it time to adulthood, if positive, breeding cooldown
|
||||
Breeding age of passive entity, in ticks. If negative, time to adulthood, if positive, breeding cooldown
|
||||
|
||||
### `query(e,'despawn_timer')`
|
||||
### `query(e, 'despawn_timer')`
|
||||
|
||||
For living entities - the number of ticks they fall outside of immediate player presence.
|
||||
For living entities, the number of ticks they fall outside of immediate player presence.
|
||||
|
||||
### `query(e,'item')`
|
||||
### `query(e, 'portal_cooldown')`
|
||||
|
||||
Number of ticks remaining until an entity can use a portal again.
|
||||
|
||||
### `query(e, 'portal_timer')`
|
||||
|
||||
Number of ticks an entity sits in a portal.
|
||||
|
||||
### `query(e, 'item')`
|
||||
|
||||
The item triple (name, count, nbt) if its an item entity, `null` otherwise
|
||||
|
||||
### `query(e,'count')`
|
||||
### `query(e, 'count')`
|
||||
|
||||
Number of items in a stack from item entity, `null` otherwise
|
||||
|
||||
### `query(e,'pickup_delay')`
|
||||
### `query(e, 'pickup_delay')`
|
||||
|
||||
Retrieves pickup delay timeout for an item entity, `null` otherwise
|
||||
|
||||
### `query(e,'is_baby')`
|
||||
### `query(e, 'is_baby')`
|
||||
|
||||
Boolean, true if its a baby.
|
||||
|
||||
### `query(e,'target')`
|
||||
### `query(e, 'target')`
|
||||
|
||||
Returns mob's attack target or null if none or not applicable.
|
||||
|
||||
### `query(e,'home')`
|
||||
### `query(e, 'home')`
|
||||
|
||||
Returns creature's home position or null if none or not applicable.
|
||||
|
||||
@@ -229,31 +247,31 @@ Returns a pose of an entity, one of the following options
|
||||
* `'crouching'`
|
||||
* `'dying'`
|
||||
|
||||
### `query(e,'sneaking')`
|
||||
### `query(e, 'sneaking')`
|
||||
|
||||
Boolean, true if entity is sneaking.
|
||||
Boolean, true if the entity is sneaking.
|
||||
|
||||
### `query(e,'sprinting')`
|
||||
### `query(e, 'sprinting')`
|
||||
|
||||
Boolean, true if entity is sprinting.
|
||||
Boolean, true if the entity is sprinting.
|
||||
|
||||
### `query(e,'swimming')`
|
||||
### `query(e, 'swimming')`
|
||||
|
||||
Boolean, true if entity is swimming.
|
||||
Boolean, true if the entity is swimming.
|
||||
|
||||
### `query(e,'jumping')`
|
||||
### `query(e, 'jumping')`
|
||||
|
||||
Boolean, true if entity is jumping.
|
||||
Boolean, true if the entity is jumping.
|
||||
|
||||
### `query(e,'gamemode')`
|
||||
### `query(e, 'gamemode')`
|
||||
|
||||
String with gamemode, or `null` if not a player.
|
||||
|
||||
### `query(e,'gamemode_id')`
|
||||
### `query(e, 'gamemode_id')`
|
||||
|
||||
Good'ol gamemode id, or null if not a player.
|
||||
|
||||
### `query(e,'player_type')`
|
||||
### `query(e, 'player_type')`
|
||||
|
||||
Returns `null` if the argument is not a player, otherwise:
|
||||
|
||||
@@ -265,18 +283,25 @@ Returns `null` if the argument is not a player, otherwise:
|
||||
* `shadow`: any carpet-shadowed real player
|
||||
* `realms`: ?
|
||||
|
||||
### `query(e,'ping')`
|
||||
### `query(e, 'category')`
|
||||
Returns a lowercase string containing the category of the entity (hostile, passive, water, ambient, misc)
|
||||
|
||||
### `query(e, 'team')`
|
||||
|
||||
Team name for entity, or `null` if no team is assigned.
|
||||
|
||||
### `query(e, 'ping')`
|
||||
|
||||
Player's ping in milliseconds, or `null` if its not a player.
|
||||
|
||||
### `query(e,'permission_level')`
|
||||
### `query(e, 'permission_level')`
|
||||
|
||||
Player's permission level, or `null` if not applicable for this entity.
|
||||
|
||||
### `query(e,'effect',name?)`
|
||||
### `query(e, 'effect', name?)`
|
||||
|
||||
Without extra arguments, it returns list of effect active on a living entity. Each entry is a triple of short
|
||||
effect name, amplifier, and remaining duration. With an argument, if the living entity has not that potion active,
|
||||
effect name, amplifier, and remaining duration in ticks. With an argument, if the living entity has not that potion active,
|
||||
returns `null`, otherwise return a tuple of amplifier and remaining duration.
|
||||
|
||||
<pre>
|
||||
@@ -285,13 +310,20 @@ query(p,'effect','haste') => [0, 177]
|
||||
query(p,'effect','resistance') => null
|
||||
</pre>
|
||||
|
||||
### `query(e,'health')`
|
||||
### `query(e, 'health')`
|
||||
|
||||
Number indicating remaining entity health, or `null` if not applicable.
|
||||
|
||||
### `query(e,'holds',slot?)`
|
||||
### `query(e, 'hunger')`
|
||||
### `query(e, 'saturation')`
|
||||
### `query(e, 'exhaustion')`
|
||||
|
||||
Returns triple of short name, stack count, and NBT of item held in `slot`. Available options for `slot` are:
|
||||
Retrieves player hunger related information. For non-players, returns `null`.
|
||||
|
||||
|
||||
### `query(e, 'holds', slot?)`
|
||||
|
||||
Returns triple of short name, stack count, and NBT of item held in `slot`, or `null` if nothing or not applicable. Available options for `slot` are:
|
||||
|
||||
* `mainhand`
|
||||
* `offhand`
|
||||
@@ -302,16 +334,16 @@ Returns triple of short name, stack count, and NBT of item held in `slot`. Avail
|
||||
|
||||
If `slot` is not specified, it defaults to the main hand.
|
||||
|
||||
### `query(e,'selected_slot')`
|
||||
### `query(e, 'selected_slot')`
|
||||
|
||||
Number indicating the selected slot of entity inventory. Currently only applicable to players.
|
||||
Number indicating the selected slot of entity's inventory. Currently only applicable to players.
|
||||
|
||||
### `query(e,'facing', order?)`
|
||||
### `query(e, 'facing', order?)`
|
||||
|
||||
Returns where the entity is facing. optional order (number from 0 to 5, and negative), indicating primary directions
|
||||
where entity is looking at. From most prominent (order 0) to opposite (order 5, or -1)
|
||||
|
||||
### `query(e,'trace', reach?, options?...)`
|
||||
### `query(e, 'trace', reach?, options?...)`
|
||||
|
||||
Returns the result of ray tracing from entity perspective, indicating what it is looking at. Default reach is 4.5
|
||||
blocks (5 for creative players), and by default it traces for blocks and entities, identical to player attack tracing
|
||||
@@ -323,9 +355,9 @@ entities and blocks, blocks will take over the priority even if transparent or n
|
||||
Regardless of the options selected, the result could be `null` if nothing is in reach, entity, if look targets an
|
||||
entity, and block value if block is in reach.
|
||||
|
||||
### `query(e,'nbt',path?)`
|
||||
### `query(e, 'nbt', path?)`
|
||||
|
||||
Returns full NBT of the entity. If path is specified, it fetches only that portion of the NBT, that corresponds to the
|
||||
Returns full NBT of the entity. If path is specified, it fetches only the portion of the NBT that corresponds to the
|
||||
path. For specification of `path` attribute, consult vanilla `/data get entity` command.
|
||||
|
||||
Note that calls to `nbt` are considerably more expensive comparing to other calls in Minecraft API, and should only
|
||||
@@ -337,13 +369,13 @@ type objects via `get, put, has, delete`, so try to use API calls first for that
|
||||
Like with entity querying, entity modifications happen through one function. Most position and movements modifications
|
||||
don't work currently on players as their position is controlled by clients.
|
||||
|
||||
Currently there is no ability to modify NBT directly, but you could always use `run('data modify entity`
|
||||
Currently there is no ability to modify NBT directly, but you could always use `run('data modify entity ...')`
|
||||
|
||||
### `modify(e,'remove')`
|
||||
### `modify(e, 'remove')`
|
||||
|
||||
Removes (not kills) entity from the game.
|
||||
|
||||
### `modify(e,'kill')`
|
||||
### `modify(e, 'kill')`
|
||||
|
||||
Kills the entity.
|
||||
|
||||
@@ -357,7 +389,7 @@ Changes full location vector all at once.
|
||||
|
||||
### `modify(e, 'x', x), modify(e, 'y', y), modify(e, 'z', z)`
|
||||
|
||||
Moves the entity in.... one direction.
|
||||
Moves the entity in one direction.
|
||||
|
||||
### `modify(e, 'pitch', pitch), modify(e, 'yaw', yaw)`
|
||||
|
||||
@@ -379,21 +411,35 @@ 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)`
|
||||
|
||||
Sets the custom name of the entity.
|
||||
|
||||
### `modify(e, 'age', number)`
|
||||
|
||||
Modifies entity's internal age counter. Fiddling with this will affect directly behaviours of complex
|
||||
Modifies entity's internal age counter. Fiddling with this will affect directly AI behaviours of complex
|
||||
entities, so use it with caution.
|
||||
|
||||
### `modify(e, 'pickup_delay', number)`
|
||||
|
||||
Sets the pickup delay for the item entity.
|
||||
|
||||
### `modify(e, 'breeding_age', number)`
|
||||
|
||||
Sets the breeding age for the animal.
|
||||
|
||||
### `modify(e, 'despawn_timer', number)`
|
||||
|
||||
Sets a custom despawn timer value.
|
||||
|
||||
### `modify(e, 'portal_cooldown', number)`
|
||||
|
||||
Sets a custom number of ticks remaining until an entity can use a portal again.
|
||||
|
||||
### `modify(e, 'portal_timer', number)`
|
||||
|
||||
Sets a custom number of ticks an entity sits in a portal.
|
||||
|
||||
### `modify(e, 'dismount')`
|
||||
|
||||
Dismounts riding entity.
|
||||
@@ -412,11 +458,11 @@ Mounts on all listed entities on `e`.
|
||||
|
||||
### `modify(e, 'tag', tag, ? ...), modify(e, 'tag', l(tags) )`
|
||||
|
||||
Adds tag / tags to the entity.
|
||||
Adds tag(s) to the entity.
|
||||
|
||||
### `modify(e, 'clear_tag', tag, ? ...), modify(e, 'clear_tag', l(tags) )`
|
||||
|
||||
Removes tag from entity.
|
||||
Removes tag(s) from the entity.
|
||||
|
||||
### `modify(e, 'talk')`
|
||||
|
||||
@@ -429,18 +475,63 @@ If called with `false` value, it will disable AI in the mob. `true` will enable
|
||||
### `modify(e, 'no_clip', boolean)`
|
||||
|
||||
Sets if the entity obeys any collisions, including collisions with the terrain and basic physics. Not affecting
|
||||
players, since they are controlled client side
|
||||
players, since they are controlled client side.
|
||||
|
||||
### `modify(e, 'effect', name, duration?, amplifier?, show_particles?, show_icon?)`
|
||||
### `modify(e, 'effect', name?, duration?, amplifier?, show_particles?, show_icon?)`
|
||||
|
||||
Applies status effect to the living entity. Takes several optional parameters, which default to `0`, `true`
|
||||
and `true`. If no duration is specified, or it is null or 0, the effect is removed.
|
||||
and `true`. If no duration is specified, or if it's null or 0, the effect is removed. If name is not specified,
|
||||
it clears all effects.
|
||||
|
||||
### `modify(e, 'home', null), modify(e, 'home', block, distance?), modify(e, 'home', x, y, z, distance?)`
|
||||
|
||||
Sets AI to stay around the home position, within `distance` blocks from it. `distance` defaults to 16 blocks.
|
||||
`null` removes it. _May_ not work fully with mobs that have this AI built in, like Villagers.
|
||||
|
||||
### `modify(e, 'gamemode', gamemode?), modify(e, 'gamemode', gamemode_id?)`
|
||||
|
||||
Modifies gamemode of player to whatever string (case-insensitive) or number you put in.
|
||||
|
||||
* 0: survival
|
||||
* 1: creative
|
||||
* 2: adventure
|
||||
* 3: spectator
|
||||
|
||||
### `modify(e, 'jumping', boolean)`
|
||||
|
||||
Will make the entity constantly jump if set to true, and will stop the entity from jumping if set to false.
|
||||
Note that jumping parameter can be fully controlled by the entity AI, so don't expect that this will have
|
||||
a permanent effect. Use `'jump'` to make an entity jump once for sure.
|
||||
|
||||
Requires a living entity as an argument.
|
||||
|
||||
### `modify(e, 'jump'))`
|
||||
|
||||
Will make the entity jump once.
|
||||
|
||||
### `modify(e, 'silent', boolean)`
|
||||
|
||||
Silences or unsilences the entity.
|
||||
|
||||
### `modify(e, 'gravity', boolean)`
|
||||
|
||||
Toggles gravity for the entity.
|
||||
|
||||
### `modify(e, 'fire', ticks)`
|
||||
|
||||
Will set entity on fire for `ticks` ticks. Set to 0 to extinguish.
|
||||
|
||||
### `modify(e, 'hunger', value)`
|
||||
### `modify(e, 'saturation', value)`
|
||||
### `modify(e, 'exhaustion', value)`
|
||||
|
||||
Modifies directly player raw hunger components. Has no effect on non-players
|
||||
|
||||
### `modify(e, 'add_exhaustion', value)`
|
||||
|
||||
adds exhaustion value to the current player exhaustion level - that's the method you probably want to use
|
||||
to manipulate how much 'food' some action cost.
|
||||
|
||||
## 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.
|
||||
@@ -451,19 +542,19 @@ defining the callback with `entity_event`
|
||||
|
||||
The following events can be handled by entities.
|
||||
|
||||
* `'on_tick'`: executes every tick right before entity is ticked in the game. Required arguments: `entity`
|
||||
* `'on_tick'`: executes every tick right before the entity is ticked in the game. Required arguments: `entity`
|
||||
* `'on_death'`: executes once when a living entity dies. Required arguments: `entity, reason`
|
||||
* `'on_removed'`: execute once when an entity is removed. Required arguments: `entity`
|
||||
* `'on_damaged'`: executed every time a living entity is about to receive damage.
|
||||
Required arguments: `entity, amount, source, attacking_entity`
|
||||
|
||||
It doesn't mean that all entity types will have a chance to execute a given event, but entities will not error
|
||||
when you will attach inapplicable event to it.
|
||||
when you attach inapplicable event to it.
|
||||
|
||||
### `entity_event(e, event, call_name, args...)`
|
||||
|
||||
Attaches specific function from the current package to be called upon the `event`, with extra `args` curried to the
|
||||
original required arguments for the event handler
|
||||
Attaches specific function from the current package to be called upon the `event`, with extra `args` carried to the
|
||||
original required arguments for the event handler.
|
||||
|
||||
<pre>
|
||||
protect_villager(entity, amount, source, source_entity, healing_player) ->
|
||||
|
||||
@@ -19,7 +19,7 @@ __on_tick() // can access blocks and entities in the overworld
|
||||
__on_tick_nether() // can access blocks and entities in the nether
|
||||
__on_tick_ender() // can access blocks and entities in the end
|
||||
__on_chunk_generated(x,z) // called after a chunk is promoted to the full chunk,
|
||||
// prodiving lowest x and z coords in the chunk
|
||||
// providing lowest x and z coords in the chunk
|
||||
// event will not work with optifine installed in the game
|
||||
__on_lightning(block, mode) // mode is `true` if lightning caused horse trap to spawn
|
||||
// player specific callbacks
|
||||
@@ -33,6 +33,11 @@ __on_player_interacts_with_block(player, hand, block, face, hitvec) //right cli
|
||||
__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_attacks_entity(player, entity)
|
||||
__on_player_takes_damage(player, amount, source, source_entity)
|
||||
__on_player_deals_damage(player, amount, entity)
|
||||
__on_player_dies(player)
|
||||
__on_player_respawns(player)
|
||||
__on_player_changes_dimension(player, from_pos, from_dimension, to_pos, to_dimension)
|
||||
__on_player_rides(player, forward, strafe, jumping, sneaking)
|
||||
__on_player_jumps(player)
|
||||
__on_player_deploys_elytra(player)
|
||||
@@ -43,9 +48,16 @@ __on_player_starts_sprinting(player)
|
||||
__on_player_stops_sprinting(player)
|
||||
__on_player_drops_item(player)
|
||||
__on_player_drops_stack(player)
|
||||
__on_player_connects(player)
|
||||
__on_player_disconnects(player, reason)
|
||||
__on_statistic(player, category, event, value) // player statistic changes
|
||||
</pre>
|
||||
|
||||
Couple special cases.
|
||||
* `__on_player_changes_dimension` returns `null` as `to_pos` when player goes back to the overworld from the end
|
||||
, since the respawn location of the player is not control by the teleport, or a player can see the end credits. After
|
||||
the player is eligible to respawn in the overworld, `__on_player_respawns` will be triggered.
|
||||
|
||||
### `/script event` command
|
||||
|
||||
used to display current events and bounded functions. use `add_to` ro register new event, or `remove_from` to
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
|
||||
Most functions in this category require inventory as the first argument. Inventory could be specified by an entity,
|
||||
or a block, or position (three coordinates) of a potential block with inventory. Player enderchest inventory require
|
||||
two arguments, keyword `'enderchest'`, followed by the player entity argument. If the entity or a block doesn't have
|
||||
two arguments, keyword `'enderchest'`, followed by the player entity argument, or a single argument as a string of a
|
||||
form: `'enderchest_steve'`. If your player name starts with enderchest, it can be always accessed by passing a player
|
||||
entity value. If all else fails, it will try to identify first three arguments as coordinates of a block position of
|
||||
a block inventory. Player inventories can also be called by their name.
|
||||
|
||||
If the entity or a block doesn't have
|
||||
an inventory, they typically do nothing and return null.
|
||||
|
||||
Most items returned are in the form of a triple of item name, count, and nbt or the extra data associated with an item.
|
||||
|
||||
@@ -17,9 +17,12 @@ scoreboard_add('counter')
|
||||
scoreboard_add('lvl','level')
|
||||
</pre>
|
||||
|
||||
### `scoreboard_remove(objective)`
|
||||
### `scoreboard_remove(objective)` `scoreboard_remove(objective, key)`
|
||||
|
||||
Removes an objective. Returns `true` if objective has existed and has been removed.
|
||||
Removes an entire objective, or an entry in the scoreboard associated with the key.
|
||||
Returns `true` if objective has existed and has been removed, or previous
|
||||
value of the scoreboard if players score is removed. Returns `null` if objective didn't exist, or a key was missing
|
||||
for the objective.
|
||||
|
||||
### `scoreboard_display(place, objective)`
|
||||
|
||||
|
||||
@@ -276,4 +276,4 @@ global_foo = floor(rand(10));
|
||||
check_foo_not_zero();
|
||||
...
|
||||
check_foo_not_zero() -> if(global_foo == 0, global_foo = 1)
|
||||
</pre>
|
||||
</pre>
|
||||
@@ -46,15 +46,22 @@ number('3bar') => null
|
||||
number('2')+number('2') => 4
|
||||
</pre>
|
||||
|
||||
### `str(expr, params? ... ), str(expr, param_list)`
|
||||
### `str(expr)`,`str(expr, params? ... )`, `str(expr, param_list)`
|
||||
|
||||
Returns a formatted string representing expression. Accepts formatting style accepted by `String.format`.
|
||||
Supported types (with `"%?"` syntax):
|
||||
If called with one argument, returns string representation of such value.
|
||||
|
||||
Otherwise, returns a formatted string representing the expression. Arguments for formatting can either be provided as
|
||||
each consecutive parameter, or as a list which then would be the only extra parameter. To format one list argument
|
||||
, you can use `str(list)`, or `str('foo %s', l(list))`.
|
||||
|
||||
Accepts formatting style accepted by `String.format`.
|
||||
Supported types (with `"%<?>"` syntax):
|
||||
|
||||
* `d`, `o`, `x`: integers, octal, hex
|
||||
* `a`, `e`, `f`, `g`: floats
|
||||
* `b`: booleans
|
||||
* `s`: strings
|
||||
* `%%`: '%' character
|
||||
|
||||
<pre>
|
||||
str(null) => null
|
||||
@@ -140,6 +147,18 @@ Returns true if task has completed, or false otherwise.
|
||||
|
||||
Evaluates `expression` synchronized with respect to the lock `lock`. Returns the value of the expression.
|
||||
|
||||
### `task_dock(expr)`
|
||||
|
||||
In a not-task (running regular code on the main game thread) it is a pass-through command. In tasks - it docks
|
||||
the current thread on the main server thread and executes expression as one server offline server task.
|
||||
This is especially helpful in case a task has several docking operations to perform, such as setting a block, and
|
||||
it would be much more efficient to do them all at once rather then packing each block access in each own call.
|
||||
|
||||
Be mindful, that docking the task means that the tick execution will be delayed until the expression is evaluated.
|
||||
This will synchronize your task with other tasks using `task_dock`, but if you should be using `synchronize` to
|
||||
synchronize tasks without locking the main thread.
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
## Auxiliary functions
|
||||
@@ -244,8 +263,11 @@ sleep(50)
|
||||
|
||||
### `time()`
|
||||
|
||||
Returns the number of milliseconds since 'some point', like Java's `System.nanoTime()`.
|
||||
It returns a float, which has 1 microsecond precision (0.001 ms)
|
||||
Returns the number of milliseconds since 'some point', like Java's `System.nanoTime()`, which varies from system to
|
||||
system and from Java to Java. This measure should NOT be used to determine the current (date)time, but to measure
|
||||
durations of things.
|
||||
it returns a float with time in milliseconds (ms) for convenience and microsecond (μs) resolution for sanity.
|
||||
|
||||
|
||||
<pre>
|
||||
start_time = time();
|
||||
|
||||
@@ -85,6 +85,7 @@ tanh
|
||||
task
|
||||
task_completed
|
||||
task_count
|
||||
task_dock
|
||||
task_join
|
||||
task_value
|
||||
throw
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
air
|
||||
add_chunk_ticket
|
||||
biome
|
||||
blast_resistance
|
||||
block
|
||||
@@ -13,8 +14,10 @@ brightness
|
||||
chunk_tickets
|
||||
create_marker
|
||||
current_dimension
|
||||
day_time
|
||||
destroy
|
||||
diamond
|
||||
draw_shape
|
||||
drop_item
|
||||
emitted_light
|
||||
entity_area
|
||||
@@ -24,18 +27,21 @@ entity_list
|
||||
entity_selector
|
||||
escape_nbt
|
||||
flammable
|
||||
format
|
||||
game_tick
|
||||
generation_status
|
||||
hardness
|
||||
harvest
|
||||
in_dimension
|
||||
in_slime_chunk
|
||||
inhabited_time
|
||||
inventory_find
|
||||
inventory_get
|
||||
inventory_remove
|
||||
inventory_set
|
||||
inventory_size
|
||||
item_category
|
||||
last_tick_times
|
||||
light
|
||||
liquid
|
||||
load_app_data
|
||||
@@ -62,6 +68,7 @@ property
|
||||
query
|
||||
random_tick
|
||||
rect
|
||||
reload_chunk
|
||||
remove_all_markers
|
||||
reset_chunk
|
||||
run
|
||||
@@ -82,10 +89,12 @@ sky_light
|
||||
solid
|
||||
sound
|
||||
spawn
|
||||
spawn_potential
|
||||
stack_limit
|
||||
statistic
|
||||
store_app_data
|
||||
structures
|
||||
structure_eligibility
|
||||
structure_references
|
||||
suffocates
|
||||
tick_time
|
||||
@@ -93,5 +102,7 @@ ticks_randomly
|
||||
top
|
||||
transparent
|
||||
update
|
||||
view_distance
|
||||
volume
|
||||
without_updates
|
||||
without_updates
|
||||
world_time
|
||||
@@ -5,6 +5,7 @@ __on_close
|
||||
__on_player_attacks_entity
|
||||
__on_player_breaks_block
|
||||
__on_player_clicks_block
|
||||
__on_player_changes_dimension
|
||||
__on_player_deploys_elytra
|
||||
__on_player_drops_item
|
||||
__on_player_drops_stack
|
||||
@@ -25,4 +26,10 @@ __on_player_wakes_up
|
||||
__on_statistic
|
||||
__on_tick
|
||||
__on_tick_ender
|
||||
__on_tick_nether
|
||||
__on_tick_nether
|
||||
__on_player_takes_damage
|
||||
__on_player_deals_damage
|
||||
__on_player_dies
|
||||
__on_player_respawns
|
||||
__on_player_disconnects
|
||||
__on_player_connects
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
print return exit throw try
|
||||
|
||||
task task_count task_value task_join task_completed synchronize
|
||||
task task_count task_value task_join task_completed synchronize task_dock
|
||||
|
||||
system_variable_get system_variable_set
|
||||
|
||||
@@ -54,8 +54,9 @@
|
||||
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 chunk_tickets
|
||||
structures structure_references set_structure reset_chunk
|
||||
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
|
||||
|
||||
@@ -65,11 +66,13 @@
|
||||
|
||||
scan volume neighbours rect diamond
|
||||
|
||||
logger sound particle particle_line particle_rect
|
||||
format logger sound particle particle_line particle_rect
|
||||
|
||||
run save tick_time game_tick seed current_dimension in_dimension
|
||||
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 create_marker remove_all_markers load_app_data store_app_data escape_nbt statistic
|
||||
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>
|
||||
@@ -83,6 +86,7 @@
|
||||
__on_player_attacks_entity
|
||||
__on_player_breaks_block
|
||||
__on_player_clicks_block
|
||||
__on_player_changes_dimension
|
||||
__on_player_deploys_elytra
|
||||
__on_player_drops_item
|
||||
__on_player_drops_stack
|
||||
@@ -104,6 +108,12 @@
|
||||
__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>
|
||||
|
||||
+3
-3
@@ -5,11 +5,11 @@ org.gradle.jvmargs=-Xmx1G
|
||||
# check these on https://fabricmc.net/use
|
||||
# or better: https://modmuss50.me/fabric.html
|
||||
minecraft_version=1.15.2
|
||||
yarn_mappings=1.15.2+build.14
|
||||
loader_version=0.7.8+build.188
|
||||
yarn_mappings=1.15.2+build.17
|
||||
loader_version=0.8.7+build.201
|
||||
|
||||
# Mod Properties
|
||||
mod_version = 1.3.17+v200401
|
||||
mod_version = 1.3.29+v200611
|
||||
maven_group = carpet
|
||||
archives_base_name = fabric-carpet
|
||||
|
||||
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
cat docs/scarpet/language/Overview.md docs/scarpet/language/VariablesAndConstants.md docs/scarpet/language/Operators.md docs/scarpet/language/Math.md docs/scarpet/language/SystemFunctions.md docs/scarpet/language/LoopsAndHigherOrderFunctions.md docs/scarpet/language/FunctionsAndControlFlow.md docs/scarpet/language/Containers.md docs/scarpet/api/Overview.md docs/scarpet/api/BlocksAndWorldAccess.md docs/scarpet/api/BlockIterations.md docs/scarpet/api/Entities.md docs/scarpet/api/Inventories.md docs/scarpet/api/Events.md docs/scarpet/api/Scoreboard.md docs/scarpet/api/Auxiliary.md docs/scarpet/api/ScriptCommand.md > docs/scarpet/Full.md
|
||||
|
||||
@@ -6,6 +6,8 @@ import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface CarpetExtension
|
||||
{
|
||||
/**
|
||||
@@ -51,4 +53,6 @@ public interface CarpetExtension
|
||||
default String version() {return null;}
|
||||
|
||||
default void registerLoggers() {}
|
||||
|
||||
default Map<String, String> canHasTranslations(String lang) { return null;}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import carpet.commands.*;
|
||||
import carpet.network.ServerNetworkHandler;
|
||||
import carpet.helpers.TickSpeed;
|
||||
import carpet.logging.LoggerRegistry;
|
||||
import carpet.script.CarpetScriptServer;
|
||||
@@ -56,10 +57,11 @@ public class CarpetServer // static for now - easier to handle all around the co
|
||||
if (sm != null) sm.attachServer(server);
|
||||
e.onServerLoaded(server);
|
||||
});
|
||||
scriptServer = new CarpetScriptServer();
|
||||
scriptServer = new CarpetScriptServer(server);
|
||||
scriptServer.loadAllWorldScripts();
|
||||
MobAI.resetTrackers();
|
||||
LoggerRegistry.initLoggers();
|
||||
//TickSpeed.reset();
|
||||
}
|
||||
|
||||
public static void tick(MinecraftServer server)
|
||||
@@ -102,29 +104,41 @@ public class CarpetServer // static for now - easier to handle all around the co
|
||||
|
||||
public static void onPlayerLoggedIn(ServerPlayerEntity player)
|
||||
{
|
||||
ServerNetworkHandler.onPlayerJoin(player);
|
||||
LoggerRegistry.playerConnected(player);
|
||||
extensions.forEach(e -> e.onPlayerLoggedIn(player));
|
||||
|
||||
}
|
||||
|
||||
public static void onPlayerLoggedOut(ServerPlayerEntity player)
|
||||
{
|
||||
ServerNetworkHandler.onPlayerLoggedOut(player);
|
||||
LoggerRegistry.playerDisconnected(player);
|
||||
extensions.forEach(e -> e.onPlayerLoggedOut(player));
|
||||
}
|
||||
|
||||
public static void onServerClosed(MinecraftServer server)
|
||||
{
|
||||
ServerNetworkHandler.close();
|
||||
currentCommandDispatcher = null;
|
||||
if (scriptServer != null) scriptServer.onClose();
|
||||
settingsManager.detachServer();
|
||||
|
||||
LoggerRegistry.stopLoggers();
|
||||
extensions.forEach(e -> e.onServerClosed(server));
|
||||
minecraft_server = null;
|
||||
disconnect();
|
||||
}
|
||||
|
||||
public static void registerExtensionLoggers()
|
||||
{
|
||||
extensions.forEach(CarpetExtension::registerLoggers);
|
||||
}
|
||||
|
||||
public static void disconnect()
|
||||
{
|
||||
// this for whatever reason gets called multiple times even when joining;
|
||||
TickSpeed.reset();
|
||||
settingsManager.detachServer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,13 @@ import carpet.settings.ParsedRule;
|
||||
import carpet.settings.Rule;
|
||||
import carpet.settings.SettingsManager;
|
||||
import carpet.settings.Validator;
|
||||
import carpet.utils.Translations;
|
||||
import carpet.utils.Messenger;
|
||||
import carpet.utils.SpawnChunks;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.server.world.ChunkTicketType;
|
||||
import net.minecraft.server.world.ServerChunkManager;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Box;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
@@ -35,7 +34,7 @@ import static carpet.settings.RuleCategory.CLIENT;
|
||||
@SuppressWarnings("CanBeFinal")
|
||||
public class CarpetSettings
|
||||
{
|
||||
public static final String carpetVersion = "1.3.17+v200401";
|
||||
public static final String carpetVersion = "1.3.29+v200611";
|
||||
public static final Logger LOG = LogManager.getLogger();
|
||||
public static boolean skipGenerationChecks = false;
|
||||
public static boolean impendingFillSkipUpdates = false;
|
||||
@@ -43,6 +42,31 @@ public class CarpetSettings
|
||||
public static Vec3d fixedPosition = null;
|
||||
public static int runPermissionLevel = 2;
|
||||
|
||||
private static class LanguageValidator extends Validator<String> {
|
||||
@Override public String validate(ServerCommandSource source, ParsedRule<String> currentRule, String newValue, String string) {
|
||||
if (currentRule.get().equals(newValue) || source == null)
|
||||
{
|
||||
return newValue;
|
||||
}
|
||||
if (!Translations.isValidLanguage(newValue))
|
||||
{
|
||||
Messenger.m(source, "r "+newValue+" is not a valid language");
|
||||
return null;
|
||||
}
|
||||
CarpetSettings.language = newValue;
|
||||
Translations.updateLanguage(source);
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
@Rule(
|
||||
desc = "sets the language for carpet",
|
||||
category = FEATURE,
|
||||
options = {"none", "zh_cn", "zh_tw"},
|
||||
strict = false,
|
||||
validate = LanguageValidator.class
|
||||
)
|
||||
public static String language = "none";
|
||||
|
||||
@Rule(
|
||||
desc = "Nether portals correctly place entities going through",
|
||||
extra = "Entities shouldn't suffocate in obsidian",
|
||||
@@ -71,6 +95,7 @@ public class CarpetSettings
|
||||
)
|
||||
public static int portalSurvivalDelay = 80;
|
||||
|
||||
|
||||
private static class OneHourMaxDelayLimit extends Validator<Integer> {
|
||||
@Override public Integer validate(ServerCommandSource source, ParsedRule<Integer> currentRule, Integer newValue, String string) {
|
||||
return (newValue > 0 && newValue <= 72000) ? newValue : null;
|
||||
@@ -236,13 +261,6 @@ public class CarpetSettings
|
||||
@Rule( desc = "Pistons can push block entities, like hoppers, chests etc.", category = {EXPERIMENTAL, FEATURE} )
|
||||
public static boolean movableBlockEntities = false;
|
||||
|
||||
/*@Rule(
|
||||
desc = "Orange stained glass behaves like slime, but doesn't stick to it",
|
||||
extra = "Only available for 1.14. in 1.15 use honey instead",
|
||||
category = {EXPERIMENTAL, FEATURE}
|
||||
)
|
||||
public static boolean honeySlime = false;*/
|
||||
|
||||
@Rule( desc = "Saplings turn into dead shrubs in hot climates and no water access", category = FEATURE )
|
||||
public static boolean desertShrubs = false;
|
||||
|
||||
@@ -308,7 +326,7 @@ public class CarpetSettings
|
||||
)
|
||||
public static String commandPerimeterInfo = "true";
|
||||
|
||||
@Rule(desc = "Enables /draw commands", extra = "... allows for drawing simple shapes", category = COMMAND)
|
||||
@Rule(desc = "Enables /draw commands", extra = {"... allows for drawing simple shapes or","other shapes which are sorta difficult to do normally"}, category = COMMAND)
|
||||
public static String commandDraw = "true";
|
||||
|
||||
@Rule(
|
||||
@@ -356,9 +374,6 @@ public class CarpetSettings
|
||||
@Rule(desc = "Pistons, Glass and Sponge can be broken faster with their appropriate tools", category = SURVIVAL)
|
||||
public static boolean missingTools = false;
|
||||
|
||||
//@Rule(desc = "Alternative, persistent caching strategy for nether portals", category = {SURVIVAL, CREATIVE})
|
||||
//public static boolean portalCaching = false; // ineffective in 1.15
|
||||
|
||||
@Rule(desc = "fill/clone/setblock and structure blocks cause block updates", category = CREATIVE)
|
||||
public static boolean fillUpdates = true;
|
||||
|
||||
@@ -472,7 +487,7 @@ public class CarpetSettings
|
||||
{
|
||||
@Override public Integer validate(ServerCommandSource source, ParsedRule<Integer> currentRule, Integer newValue, String string)
|
||||
{
|
||||
if (currentRule.get().equals(newValue))
|
||||
if (currentRule.get().equals(newValue) || source == null)
|
||||
{
|
||||
return newValue;
|
||||
}
|
||||
@@ -486,8 +501,8 @@ public class CarpetSettings
|
||||
if (server.isDedicated())
|
||||
{
|
||||
int vd = (newValue >= 2)?newValue:((DedicatedServer) server).getProperties().viewDistance;
|
||||
if (vd != CarpetServer.minecraft_server.getPlayerManager().getViewDistance())
|
||||
CarpetServer.minecraft_server.getPlayerManager().setViewDistance(vd);
|
||||
if (vd != server.getPlayerManager().getViewDistance())
|
||||
server.getPlayerManager().setViewDistance(vd);
|
||||
return newValue;
|
||||
}
|
||||
else
|
||||
@@ -509,13 +524,9 @@ public class CarpetSettings
|
||||
)
|
||||
public static int viewDistance = 0;
|
||||
|
||||
private static class DisableSpawnChunksValidator extends Validator<Boolean> {
|
||||
@Override public Boolean validate(ServerCommandSource source, ParsedRule<Boolean> currentRule, Boolean newValue, String string) {
|
||||
if (currentRule.get().booleanValue() == newValue.booleanValue())
|
||||
{
|
||||
//must been some startup thing
|
||||
return newValue;
|
||||
}
|
||||
public static class ChangeSpawnChunksValidator extends Validator<Integer> {
|
||||
public static void changeSpawnSize(int size)
|
||||
{
|
||||
ServerWorld overworld = CarpetServer.minecraft_server.getWorld(DimensionType.OVERWORLD);
|
||||
if (overworld != null) {
|
||||
ChunkPos centerChunk = new ChunkPos(new BlockPos(
|
||||
@@ -523,40 +534,39 @@ public class CarpetSettings
|
||||
overworld.getLevelProperties().getSpawnY(),
|
||||
overworld.getLevelProperties().getSpawnZ()
|
||||
));
|
||||
ServerChunkManager chunkManager = (ServerChunkManager) overworld.getChunkManager();
|
||||
|
||||
chunkManager.removeTicket(ChunkTicketType.START, centerChunk, 11, Unit.INSTANCE);
|
||||
if (!newValue)
|
||||
{
|
||||
chunkManager.addTicket(ChunkTicketType.START, centerChunk, 11, Unit.INSTANCE);
|
||||
}
|
||||
SpawnChunks.changeSpawnChunks(overworld.getChunkManager(), centerChunk, size);
|
||||
}
|
||||
}
|
||||
@Override public Integer validate(ServerCommandSource source, ParsedRule<Integer> currentRule, Integer newValue, String string) {
|
||||
if (source == null) return newValue;
|
||||
if (newValue < 0 || newValue > 32)
|
||||
{
|
||||
Messenger.m(source, "r spawn chunk size has to be between 0 and 32");
|
||||
return null;
|
||||
}
|
||||
if (currentRule.get().intValue() == newValue.intValue())
|
||||
{
|
||||
//must been some startup thing
|
||||
return newValue;
|
||||
}
|
||||
if (CarpetServer.minecraft_server == null) return newValue;
|
||||
ServerWorld currentOverworld = CarpetServer.minecraft_server.getWorld(DimensionType.OVERWORLD);
|
||||
if (currentOverworld != null)
|
||||
{
|
||||
changeSpawnSize(newValue);
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
@Rule(
|
||||
desc = "Allows spawn chunks to unload",
|
||||
desc = "Changes size of spawn chunks",
|
||||
extra = {"Defines new radius", "setting it to 0 - disables spawn chunks"},
|
||||
category = CREATIVE,
|
||||
validate = DisableSpawnChunksValidator.class
|
||||
)
|
||||
public static boolean disableSpawnChunks = false;
|
||||
|
||||
/*private static class KelpLimit extends Validator<Integer>
|
||||
{
|
||||
@Override public Integer validate(ServerCommandSource source, ParsedRule<Integer> currentRule, Integer newValue, String string) {
|
||||
return (newValue>=0 && newValue <=25)?newValue:null;
|
||||
}
|
||||
@Override
|
||||
public String description() { return "You must choose a value from 0 to 25. 25 and all natural kelp can grow 25 blocks, choose 0 to make all generated kelp not to grow";}
|
||||
}
|
||||
@Rule(
|
||||
desc = "limits growth limit of newly naturally generated kelp to this amount of blocks",
|
||||
options = {"0", "2", "25"},
|
||||
category = FEATURE,
|
||||
strict = false,
|
||||
validate = KelpLimit.class
|
||||
options = {"0", "11"},
|
||||
validate = ChangeSpawnChunksValidator.class
|
||||
)
|
||||
public static int kelpGenerationGrowthLimit = 25;*/
|
||||
public static int spawnChunksSize = 11;
|
||||
|
||||
@Rule(desc = "Coral structures will grow with bonemeal from coral plants", category = FEATURE)
|
||||
public static boolean renewableCoral = false;
|
||||
@@ -573,13 +583,6 @@ public class CarpetSettings
|
||||
|
||||
@Rule(desc = "Spawning requires much less CPU and Memory", category = OPTIMIZATION)
|
||||
public static boolean lagFreeSpawning = false;
|
||||
|
||||
//@Rule(
|
||||
// desc = "Prevents horses and other mobs to wander into the distance after dismounting",
|
||||
// extra = "Fixes issues with various Joergens wandering off and disappearing client-side",
|
||||
// category = BUGFIX
|
||||
//)
|
||||
//public static boolean horseWanderingFix = false;
|
||||
|
||||
@Rule(
|
||||
desc = "Allows structure mobs to spawn in flat worlds",
|
||||
@@ -599,4 +602,54 @@ public class CarpetSettings
|
||||
category = CREATIVE
|
||||
)
|
||||
public static boolean extremeBehaviours = false;
|
||||
|
||||
@Rule(
|
||||
desc = "Removes fog from client in the nether and the end",
|
||||
extra = "Improves visibility, but looks weird",
|
||||
category = CLIENT
|
||||
)
|
||||
public static boolean fogOff = false;
|
||||
|
||||
@Rule(
|
||||
desc = "Creative No Clip",
|
||||
extra = {
|
||||
"On servers it needs to be set on both ",
|
||||
"client and server to function properly.",
|
||||
"Has no effect when set on the server only",
|
||||
"Can allow to phase through walls",
|
||||
"if only set on the carpet client side",
|
||||
"but requires some trapdoor magic to",
|
||||
"allow the player to enter blocks"
|
||||
},
|
||||
category = {CREATIVE, CLIENT}
|
||||
)
|
||||
public static boolean creativeNoClip = false;
|
||||
|
||||
|
||||
@Rule(
|
||||
desc = "Creative flying speed multiplier",
|
||||
extra = {
|
||||
"Purely client side setting, meaning that",
|
||||
"having it set on the decicated server has no effect",
|
||||
"but this also means it will work on vanilla servers as well"
|
||||
},
|
||||
category = {CREATIVE, CLIENT},
|
||||
validate = Validator.NONNEGATIVE_NUMBER.class
|
||||
)
|
||||
public static double creativeFlySpeed = 1.0;
|
||||
|
||||
@Rule(
|
||||
desc = "Creative air drag",
|
||||
extra = {
|
||||
"Increased drag will slow down your flight",
|
||||
"So need to adjust speed accordingly",
|
||||
"With 1.0 drag, using speed of 11 seems to matching vanilla speeds.",
|
||||
"Purely client side setting, meaning that",
|
||||
"having it set on the decicated server has no effect",
|
||||
"but this also means it will work on vanilla servers as well"
|
||||
},
|
||||
category = {CREATIVE, CLIENT},
|
||||
validate = Validator.PROBABILITY.class
|
||||
)
|
||||
public static double creativeFlyDrag = 0.09;
|
||||
}
|
||||
@@ -7,6 +7,11 @@ import com.google.common.collect.Lists;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.pattern.CachedBlockPosition;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
@@ -18,12 +23,15 @@ import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.lang.Math;
|
||||
|
||||
import static net.minecraft.server.command.CommandManager.argument;
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
import static net.minecraft.server.command.CommandSource.suggestMatching;
|
||||
|
||||
public class DrawCommand
|
||||
{
|
||||
@@ -32,45 +40,142 @@ public class DrawCommand
|
||||
LiteralArgumentBuilder<ServerCommandSource> command = literal("draw").
|
||||
requires((player) -> SettingsManager.canUseCommand(player, CarpetSettings.commandDraw)).
|
||||
then(literal("sphere").
|
||||
then(argument("center",BlockPosArgumentType.blockPos()).
|
||||
then(argument("radius",IntegerArgumentType.integer(1)).
|
||||
then(argument("block",BlockStateArgumentType.blockState()).
|
||||
executes((c)-> drawCircle(
|
||||
c.getSource(),
|
||||
BlockPosArgumentType.getBlockPos(c, "center"),
|
||||
IntegerArgumentType.getInteger(c, "radius"),
|
||||
BlockStateArgumentType.getBlockState (c,"block"),
|
||||
null
|
||||
)
|
||||
).
|
||||
then(literal("replace").
|
||||
then(argument("filter",BlockPredicateArgumentType.blockPredicate())
|
||||
.executes((c)-> drawCircle(
|
||||
c.getSource(),
|
||||
BlockPosArgumentType.getBlockPos(c, "center"),
|
||||
IntegerArgumentType.getInteger(c, "radius"),
|
||||
BlockStateArgumentType.getBlockState(c,"block"),
|
||||
BlockPredicateArgumentType.getBlockPredicate(c,
|
||||
"filter")
|
||||
))))))));
|
||||
then(argument("center", BlockPosArgumentType.blockPos()).
|
||||
then(argument("radius", IntegerArgumentType.integer(1)).
|
||||
then(drawShape(c -> DrawCommand.drawSphere(c, false)))))).
|
||||
then(literal("ball").
|
||||
then(argument("center", BlockPosArgumentType.blockPos()).
|
||||
then(argument("radius", IntegerArgumentType.integer(1)).
|
||||
then(drawShape(c -> DrawCommand.drawSphere(c, true)))))).
|
||||
then(literal("diamond").
|
||||
then(argument("center", BlockPosArgumentType.blockPos()).
|
||||
then(argument("radius", IntegerArgumentType.integer(1)).
|
||||
then(drawShape(c -> DrawCommand.drawDiamond(c, true)))))).
|
||||
then(literal("pyramid").
|
||||
then(argument("center", BlockPosArgumentType.blockPos()).
|
||||
then(argument("radius", IntegerArgumentType.integer(1)).
|
||||
then(argument("height",IntegerArgumentType.integer(1)).
|
||||
then(argument("pointing",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"up","down"},b)).
|
||||
then(argument("orientation",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"y","x","z"},b)).
|
||||
then(drawShape(c -> DrawCommand.drawPyramid(c, "square", true))))))))).
|
||||
then(literal("cone").
|
||||
then(argument("center", BlockPosArgumentType.blockPos()).
|
||||
then(argument("radius", IntegerArgumentType.integer(1)).
|
||||
then(argument("height",IntegerArgumentType.integer(1)).
|
||||
then(argument("pointing",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"up","down"},b)).
|
||||
then(argument("orientation",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"y","x","z"},b))
|
||||
.then(drawShape(c -> DrawCommand.drawPyramid(c, "circle", true))))))))).
|
||||
then(literal("cylinder").
|
||||
then(argument("center", BlockPosArgumentType.blockPos()).
|
||||
then(argument("radius", IntegerArgumentType.integer(1)).
|
||||
then(argument("height",IntegerArgumentType.integer(1)).
|
||||
then(argument("orientation",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"y","x","z"},b))
|
||||
.then(drawShape(c -> DrawCommand.drawPrism(c, "circle")))))))).
|
||||
then(literal("cuboid").
|
||||
then(argument("center", BlockPosArgumentType.blockPos()).
|
||||
then(argument("radius", IntegerArgumentType.integer(1)).
|
||||
then(argument("height",IntegerArgumentType.integer(1)).
|
||||
then(argument("orientation",StringArgumentType.word()).suggests( (c, b) -> suggestMatching(new String[]{"y","x","z"},b))
|
||||
.then(drawShape(c -> DrawCommand.drawPrism(c, "square"))))))));
|
||||
dispatcher.register(command);
|
||||
}
|
||||
|
||||
|
||||
private static int drawCircle(ServerCommandSource source, BlockPos pos, int radius, BlockStateArgument block,
|
||||
Predicate<CachedBlockPosition> replacement)
|
||||
@FunctionalInterface
|
||||
private interface OptionalBlockSelector
|
||||
{
|
||||
return drawCircle(source, pos, (double)radius, (double)radius, (double)radius, block, replacement, false);
|
||||
Integer apply(final CommandContext<ServerCommandSource> ctx) throws CommandSyntaxException;
|
||||
}
|
||||
private static int drawCircle(ServerCommandSource source, BlockPos pos, double radiusX, double radiusY, double radiusZ,
|
||||
BlockStateArgument block, Predicate<CachedBlockPosition> replacement, boolean solid)
|
||||
{
|
||||
int affected = 0;
|
||||
ServerWorld world = source.getWorld();
|
||||
|
||||
radiusX += 0.5;
|
||||
radiusY += 0.5;
|
||||
radiusZ += 0.5;
|
||||
@FunctionalInterface
|
||||
private interface ArgumentExtractor<T>
|
||||
{
|
||||
T apply(final CommandContext<ServerCommandSource> ctx, final String argName) throws CommandSyntaxException;
|
||||
}
|
||||
|
||||
private static RequiredArgumentBuilder<ServerCommandSource, BlockStateArgument>
|
||||
drawShape(OptionalBlockSelector drawer)
|
||||
{
|
||||
return argument("block", BlockStateArgumentType.blockState()).
|
||||
executes(drawer::apply)
|
||||
.then(literal("replace")
|
||||
.then(argument("filter", BlockPredicateArgumentType.blockPredicate())
|
||||
.executes(drawer::apply)));
|
||||
}
|
||||
|
||||
private static class ErrorHandled extends RuntimeException {}
|
||||
|
||||
private static <T> T getArg(CommandContext<ServerCommandSource> ctx, ArgumentExtractor<T> extract, String hwat) throws CommandSyntaxException
|
||||
{
|
||||
return getArg(ctx, extract, hwat, false);
|
||||
}
|
||||
|
||||
private static <T> T getArg(CommandContext<ServerCommandSource> ctx, ArgumentExtractor<T> extract, String hwat, boolean optional) throws CommandSyntaxException
|
||||
{
|
||||
T arg = null;
|
||||
try
|
||||
{
|
||||
arg = extract.apply(ctx, hwat);
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
if (optional) return null;
|
||||
Messenger.m(ctx.getSource(), "rb Missing "+hwat);
|
||||
throw new ErrorHandled();
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
private static double lengthSq(double x, double y, double z)
|
||||
{
|
||||
return (x * x) + (y * y) + (z * z);
|
||||
}
|
||||
|
||||
private static int setBlock(
|
||||
ServerWorld world, BlockPos.Mutable mbpos, int x, int y, int z,
|
||||
BlockStateArgument block, Predicate<CachedBlockPosition> replacement,
|
||||
List<BlockPos> list
|
||||
)
|
||||
{
|
||||
mbpos.set(x, y, z);
|
||||
int success=0;
|
||||
if (replacement == null || replacement.test(new CachedBlockPosition(world, mbpos, true)))
|
||||
{
|
||||
BlockEntity tileentity = world.getBlockEntity(mbpos);
|
||||
if (tileentity instanceof Inventory)
|
||||
{
|
||||
((Inventory) tileentity).clear();
|
||||
}
|
||||
if (block.setBlockState(world, mbpos, 2))
|
||||
{
|
||||
list.add(mbpos.toImmutable());
|
||||
++success;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private static int drawSphere(CommandContext<ServerCommandSource> ctx, boolean solid) throws CommandSyntaxException
|
||||
{
|
||||
BlockPos pos;
|
||||
int radius;
|
||||
BlockStateArgument block;
|
||||
Predicate<CachedBlockPosition> replacement;
|
||||
try
|
||||
{
|
||||
pos = getArg(ctx, BlockPosArgumentType::getBlockPos, "center");
|
||||
radius = getArg(ctx, IntegerArgumentType::getInteger, "radius");
|
||||
block = getArg(ctx, BlockStateArgumentType::getBlockState, "block");
|
||||
replacement = getArg(ctx, BlockPredicateArgumentType::getBlockPredicate, "filter", true);
|
||||
}
|
||||
catch (ErrorHandled ignored) { return 0; }
|
||||
|
||||
int affected = 0;
|
||||
ServerWorld world = ctx.getSource().getWorld();
|
||||
|
||||
double radiusX = radius+0.5;
|
||||
double radiusY = radius+0.5;
|
||||
double radiusZ = radius+0.5;
|
||||
|
||||
final double invRadiusX = 1 / radiusX;
|
||||
final double invRadiusY = 1 / radiusY;
|
||||
@@ -85,22 +190,28 @@ public class DrawCommand
|
||||
|
||||
double nextXn = 0;
|
||||
|
||||
forX: for (int x = 0; x <= ceilRadiusX; ++x) {
|
||||
forX: for (int x = 0; x <= ceilRadiusX; ++x)
|
||||
{
|
||||
final double xn = nextXn;
|
||||
nextXn = (x + 1) * invRadiusX;
|
||||
double nextYn = 0;
|
||||
forY: for (int y = 0; y <= ceilRadiusY; ++y) {
|
||||
forY: for (int y = 0; y <= ceilRadiusY; ++y)
|
||||
{
|
||||
final double yn = nextYn;
|
||||
nextYn = (y + 1) * invRadiusY;
|
||||
double nextZn = 0;
|
||||
forZ: for (int z = 0; z <= ceilRadiusZ; ++z) {
|
||||
forZ: for (int z = 0; z <= ceilRadiusZ; ++z)
|
||||
{
|
||||
final double zn = nextZn;
|
||||
nextZn = (z + 1) * invRadiusZ;
|
||||
|
||||
double distanceSq = lengthSq(xn, yn, zn);
|
||||
if (distanceSq > 1) {
|
||||
if (z == 0) {
|
||||
if (y == 0) {
|
||||
if (distanceSq > 1)
|
||||
{
|
||||
if (z == 0)
|
||||
{
|
||||
if (y == 0)
|
||||
{
|
||||
break forX;
|
||||
}
|
||||
break forY;
|
||||
@@ -108,35 +219,22 @@ public class DrawCommand
|
||||
break forZ;
|
||||
}
|
||||
|
||||
if (!solid) {
|
||||
if (lengthSq(nextXn, yn, zn) <= 1 && lengthSq(xn, nextYn, zn) <= 1 && lengthSq(xn, yn, nextZn) <= 1) {
|
||||
continue;
|
||||
}
|
||||
if (!solid && lengthSq(nextXn, yn, zn) <= 1 && lengthSq(xn, nextYn, zn) <= 1 && lengthSq(xn, yn, nextZn) <= 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
|
||||
for (int xmod = -1; xmod < 2; xmod+= 2)
|
||||
for (int xmod = -1; xmod < 2; xmod += 2)
|
||||
{
|
||||
for (int ymod = -1; ymod < 2; ymod += 2)
|
||||
{
|
||||
for (int zmod = -1; zmod < 2; zmod += 2)
|
||||
{
|
||||
mbpos.set(pos.getX()+xmod*x, pos.getY()+ymod*y, pos.getZ()+zmod*z);
|
||||
if (replacement == null || replacement.test(
|
||||
new CachedBlockPosition( world, mbpos, true)))
|
||||
{
|
||||
BlockEntity tileentity = world.getBlockEntity(mbpos);
|
||||
if (tileentity instanceof Inventory)
|
||||
{
|
||||
((Inventory)tileentity).clear();
|
||||
}
|
||||
|
||||
if (block.setBlockState(world, mbpos,2))
|
||||
{
|
||||
list.add(mbpos.toImmutable());
|
||||
++affected;
|
||||
}
|
||||
}
|
||||
affected+= setBlock(world, mbpos,
|
||||
pos.getX() + xmod * x, pos.getY() + ymod * y, pos.getZ() + zmod * z,
|
||||
block, replacement, list
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,19 +244,212 @@ public class DrawCommand
|
||||
}
|
||||
if (CarpetSettings.fillUpdates)
|
||||
{
|
||||
list.forEach(blockpos1 -> world.updateNeighbors(blockpos1, world.getBlockState(blockpos1).getBlock()));
|
||||
}
|
||||
Messenger.m(ctx.getSource(), "gi Filled " + affected + " blocks");
|
||||
return affected;
|
||||
}
|
||||
|
||||
for (BlockPos blockpos1 : list)
|
||||
private static int drawDiamond(CommandContext<ServerCommandSource> ctx, boolean solid) throws CommandSyntaxException
|
||||
{
|
||||
BlockPos pos;
|
||||
int radius;
|
||||
BlockStateArgument block;
|
||||
Predicate<CachedBlockPosition> replacement;
|
||||
try
|
||||
{
|
||||
pos = getArg(ctx, BlockPosArgumentType::getBlockPos, "center");
|
||||
radius = getArg(ctx, IntegerArgumentType::getInteger, "radius");
|
||||
block = getArg(ctx, BlockStateArgumentType::getBlockState, "block");
|
||||
replacement = getArg(ctx, BlockPredicateArgumentType::getBlockPredicate, "filter", true);
|
||||
}
|
||||
catch (ErrorHandled ignored) { return 0; }
|
||||
|
||||
ServerCommandSource source = ctx.getSource();
|
||||
|
||||
int affected=0;
|
||||
|
||||
BlockPos.Mutable mbpos = new BlockPos.Mutable(pos);
|
||||
List<BlockPos> list = Lists.newArrayList();
|
||||
|
||||
ServerWorld world = source.getWorld();
|
||||
|
||||
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
|
||||
|
||||
for (int r = 0; r < radius; ++r)
|
||||
{
|
||||
int y=r-radius+1;
|
||||
for (int x = -r; x <= r; ++x)
|
||||
{
|
||||
Block blokc = world.getBlockState(blockpos1).getBlock();
|
||||
world.updateNeighbors(blockpos1, blokc); // or always version????
|
||||
int z=r-Math.abs(x);
|
||||
|
||||
affected+= setBlock(world, mbpos, pos.getX()+x, pos.getY()-y, pos.getZ()+z, block, replacement, list);
|
||||
affected+= setBlock(world, mbpos, pos.getX()+x, pos.getY()-y, pos.getZ()-z, block, replacement, list);
|
||||
affected+= setBlock(world, mbpos, pos.getX()+x, pos.getY()+y, pos.getZ()+z, block, replacement, list);
|
||||
affected+= setBlock(world, mbpos, pos.getX()+x, pos.getY()+y, pos.getZ()-z, block, replacement, list);
|
||||
}
|
||||
}
|
||||
Messenger.m(source, "gi Filled "+affected+" blocks");
|
||||
|
||||
return 1;
|
||||
}
|
||||
private static double lengthSq(double x, double y, double z)
|
||||
CarpetSettings.impendingFillSkipUpdates = false;
|
||||
|
||||
if (CarpetSettings.fillUpdates)
|
||||
{
|
||||
return (x * x) + (y * y) + (z * z);
|
||||
list.forEach(p -> world.updateNeighbors(p, world.getBlockState(p).getBlock()));
|
||||
}
|
||||
|
||||
Messenger.m(source, "gi Filled " + affected + " blocks");
|
||||
|
||||
return affected;
|
||||
}
|
||||
|
||||
private static int fillFlat(
|
||||
ServerWorld world, BlockPos pos, int offset, double dr, boolean rectangle, String orientation,
|
||||
BlockStateArgument block, Predicate<CachedBlockPosition> replacement,
|
||||
List<BlockPos> list, BlockPos.Mutable mbpos
|
||||
)
|
||||
{
|
||||
int successes=0;
|
||||
int r = MathHelper.floor(dr);
|
||||
double drsq = dr*dr;
|
||||
if (orientation.equalsIgnoreCase("x"))
|
||||
{
|
||||
for(int a=-r; a<=r; ++a) for(int b=-r; b<=r; ++b) if(rectangle || a*a + b*b <= drsq)
|
||||
{
|
||||
successes += setBlock(
|
||||
world, mbpos,pos.getX()+offset, pos.getY()+a, pos.getZ()+b,
|
||||
block, replacement, list
|
||||
);
|
||||
}
|
||||
return successes;
|
||||
}
|
||||
if (orientation.equalsIgnoreCase("y"))
|
||||
{
|
||||
for(int a=-r; a<=r; ++a) for(int b=-r; b<=r; ++b) if(rectangle || a*a + b*b <= drsq)
|
||||
{
|
||||
successes += setBlock(
|
||||
world, mbpos,pos.getX()+a, pos.getY()+offset, pos.getZ()+b,
|
||||
block, replacement, list
|
||||
);
|
||||
}
|
||||
return successes;
|
||||
}
|
||||
if (orientation.equalsIgnoreCase("z"))
|
||||
{
|
||||
for(int a=-r; a<=r; ++a) for(int b=-r; b<=r; ++b) if(rectangle || a*a + b*b <= drsq)
|
||||
{
|
||||
successes += setBlock(
|
||||
world, mbpos,pos.getX()+b, pos.getY()+a, pos.getZ()+offset,
|
||||
block, replacement, list
|
||||
);
|
||||
}
|
||||
return successes;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int drawPyramid(CommandContext<ServerCommandSource> ctx, String base, boolean solid) throws CommandSyntaxException
|
||||
{
|
||||
BlockPos pos;
|
||||
double radius;
|
||||
int height;
|
||||
boolean pointup;
|
||||
String orientation;
|
||||
BlockStateArgument block;
|
||||
Predicate<CachedBlockPosition> replacement;
|
||||
try
|
||||
{
|
||||
pos = getArg(ctx, BlockPosArgumentType::getBlockPos, "center");
|
||||
radius = getArg(ctx, IntegerArgumentType::getInteger, "radius")+0.5D;
|
||||
height = getArg(ctx, IntegerArgumentType::getInteger, "height");
|
||||
pointup = getArg(ctx, StringArgumentType::getString, "pointing").equalsIgnoreCase("up");
|
||||
orientation = getArg(ctx, StringArgumentType::getString,"orientation");
|
||||
block = getArg(ctx, BlockStateArgumentType::getBlockState, "block");
|
||||
replacement = getArg(ctx, BlockPredicateArgumentType::getBlockPredicate, "filter", true);
|
||||
}
|
||||
catch (ErrorHandled ignored) { return 0; }
|
||||
|
||||
ServerCommandSource source = ctx.getSource();
|
||||
|
||||
int affected = 0;
|
||||
BlockPos.Mutable mbpos = new BlockPos.Mutable(pos);
|
||||
|
||||
List<BlockPos> list = Lists.newArrayList();
|
||||
|
||||
ServerWorld world = source.getWorld();
|
||||
|
||||
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
|
||||
|
||||
boolean isSquare = base.equalsIgnoreCase("square");
|
||||
|
||||
for(int i =0; i<height;++i)
|
||||
{
|
||||
double r = pointup ? radius - radius * i / height - 1 : radius * i / height;
|
||||
affected+= fillFlat(world, pos, i, r, isSquare, orientation, block, replacement, list, mbpos);
|
||||
}
|
||||
|
||||
CarpetSettings.impendingFillSkipUpdates = false;
|
||||
|
||||
if (CarpetSettings.fillUpdates) {
|
||||
|
||||
for (BlockPos blockpos1 : list) {
|
||||
Block blokc = world.getBlockState(blockpos1).getBlock();
|
||||
world.updateNeighbors(blockpos1, blokc);
|
||||
}
|
||||
}
|
||||
|
||||
Messenger.m(source, "gi Filled " + affected + " blocks");
|
||||
|
||||
return affected;
|
||||
}
|
||||
|
||||
private static int drawPrism(CommandContext<ServerCommandSource> ctx, String base){
|
||||
BlockPos pos;
|
||||
double radius;
|
||||
int height;
|
||||
String orientation;
|
||||
BlockStateArgument block;
|
||||
Predicate<CachedBlockPosition> replacement;
|
||||
try
|
||||
{
|
||||
pos = getArg(ctx, BlockPosArgumentType::getBlockPos, "center");
|
||||
radius = getArg(ctx, IntegerArgumentType::getInteger, "radius")+0.5D;
|
||||
height = getArg(ctx, IntegerArgumentType::getInteger, "height");
|
||||
orientation = getArg(ctx, StringArgumentType::getString,"orientation");
|
||||
block = getArg(ctx, BlockStateArgumentType::getBlockState, "block");
|
||||
replacement = getArg(ctx, BlockPredicateArgumentType::getBlockPredicate, "filter", true);
|
||||
}
|
||||
catch (ErrorHandled | CommandSyntaxException ignored) { return 0; }
|
||||
|
||||
ServerCommandSource source = ctx.getSource();
|
||||
|
||||
int affected = 0;
|
||||
BlockPos.Mutable mbpos = new BlockPos.Mutable(pos);
|
||||
|
||||
List<BlockPos> list = Lists.newArrayList();
|
||||
|
||||
ServerWorld world = source.getWorld();
|
||||
|
||||
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
|
||||
|
||||
boolean isSquare = base.equalsIgnoreCase("square");
|
||||
|
||||
for(int i =0; i<height;++i)
|
||||
{
|
||||
affected+= fillFlat(world, pos, i, radius, isSquare, orientation, block, replacement, list, mbpos);
|
||||
}
|
||||
|
||||
CarpetSettings.impendingFillSkipUpdates = false;
|
||||
|
||||
if (CarpetSettings.fillUpdates) {
|
||||
|
||||
for (BlockPos blockpos1 : list) {
|
||||
Block blokc = world.getBlockState(blockpos1).getBlock();
|
||||
world.updateNeighbors(blockpos1, blokc);
|
||||
}
|
||||
}
|
||||
|
||||
Messenger.m(source, "gi Filled " + affected + " blocks");
|
||||
|
||||
return affected;
|
||||
}
|
||||
}
|
||||
@@ -296,6 +296,14 @@ public class PlayerCommand
|
||||
Messenger.m(context.getSource(), "r Cannot shadow fake players");
|
||||
return 0;
|
||||
}
|
||||
ServerPlayerEntity sendingPlayer = null;
|
||||
try
|
||||
{
|
||||
sendingPlayer = context.getSource().getPlayer();
|
||||
}
|
||||
catch (CommandSyntaxException ignored) { }
|
||||
|
||||
if (sendingPlayer!=player && cantManipulate(context)) return 0;
|
||||
EntityPlayerMPFake.createShadow(player.server, player);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import net.minecraft.command.arguments.DimensionArgumentType;
|
||||
import net.minecraft.entity.EntityCategory;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.DyeColor;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
@@ -98,7 +99,8 @@ public class SpawnCommand
|
||||
executes( (c) -> generalMobcaps(c.getSource()) ).
|
||||
then(argument("type", string()).
|
||||
suggests( (c, b)->suggestMatching(Arrays.stream(EntityCategory.values()).map(EntityCategory::getName), b)).
|
||||
executes( (c) -> listEntitiesOfType(c.getSource(), getString(c, "type")))));
|
||||
executes( (c) -> listEntitiesOfType(c.getSource(), getString(c, "type"), false)).
|
||||
then(literal("all").executes( (c) -> listEntitiesOfType(c.getSource(), getString(c, "type"), true)))));
|
||||
|
||||
dispatcher.register(literalargumentbuilder);
|
||||
}
|
||||
@@ -194,7 +196,7 @@ public class SpawnCommand
|
||||
TickSpeed.tickrate_advance(null, 0, null, null);
|
||||
// tick warp given player
|
||||
ServerCommandSource csource = null;
|
||||
PlayerEntity player = null;
|
||||
ServerPlayerEntity player = null;
|
||||
try
|
||||
{
|
||||
player = source.getPlayer();
|
||||
@@ -262,10 +264,10 @@ public class SpawnCommand
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int listEntitiesOfType(ServerCommandSource source, String mobtype) throws CommandSyntaxException
|
||||
private static int listEntitiesOfType(ServerCommandSource source, String mobtype, boolean all) throws CommandSyntaxException
|
||||
{
|
||||
EntityCategory cat = getCategory(mobtype);
|
||||
Messenger.send(source, SpawnReporter.printEntitiesByType(cat, source.getWorld()));
|
||||
Messenger.send(source, SpawnReporter.printEntitiesByType(cat, source.getWorld(), all));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.BaseText;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
|
||||
@@ -43,7 +44,8 @@ public class TickCommand
|
||||
c.getSource(),
|
||||
getInteger(c,"ticks"),
|
||||
getString(c, "tail command")))))).
|
||||
then(literal("freeze").executes( (c)-> toggleFreeze(c.getSource()))).
|
||||
then(literal("freeze").executes( (c)-> toggleFreeze(c.getSource(), false)).
|
||||
then(literal("deep").executes( (c)-> toggleFreeze(c.getSource(), true)))).
|
||||
then(literal("step").
|
||||
executes((c) -> step(1)).
|
||||
then(argument("ticks", integer(1,72000)).
|
||||
@@ -66,7 +68,7 @@ public class TickCommand
|
||||
|
||||
private static int setTps(ServerCommandSource source, float tps)
|
||||
{
|
||||
TickSpeed.tickrate(tps);
|
||||
TickSpeed.tickrate(tps, true);
|
||||
queryTps(source);
|
||||
return (int)tps;
|
||||
}
|
||||
@@ -79,7 +81,7 @@ public class TickCommand
|
||||
|
||||
private static int setWarp(ServerCommandSource source, int advance, String tail_command)
|
||||
{
|
||||
PlayerEntity player = null;
|
||||
ServerPlayerEntity player = null;
|
||||
try
|
||||
{
|
||||
player = source.getPlayer();
|
||||
@@ -95,15 +97,18 @@ public class TickCommand
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int toggleFreeze(ServerCommandSource source)
|
||||
private static int toggleFreeze(ServerCommandSource source, boolean isDeep)
|
||||
{
|
||||
TickSpeed.is_paused = !TickSpeed.is_paused;
|
||||
if (TickSpeed.is_paused)
|
||||
{
|
||||
Messenger.m(source, "gi Game is paused");
|
||||
TickSpeed.deepFreeze = isDeep;
|
||||
Messenger.m(source, "gi Game is "+(isDeep?"deeply ":"")+"frozen");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
TickSpeed.deepFreeze = false;
|
||||
Messenger.m(source, "gi Game runs normally");
|
||||
}
|
||||
return 1;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package carpet.fakes;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import net.minecraft.server.world.ChunkTicket;
|
||||
import net.minecraft.util.SortedArraySet;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
|
||||
public interface ChunkTicketManagerInterface
|
||||
{
|
||||
void changeSpawnChunks(ChunkPos pos, int distance);
|
||||
Long2ObjectOpenHashMap<SortedArraySet<ChunkTicket<?>>> getTicketsByPosition();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package carpet.fakes;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.PacketByteBuf;
|
||||
|
||||
public interface CustomPayloadC2SPacketInterface
|
||||
{
|
||||
Identifier getPacketChannel();
|
||||
PacketByteBuf getPacketData();
|
||||
}
|
||||
@@ -11,4 +11,8 @@ public interface EntityInterface
|
||||
boolean isPermanentVehicle();
|
||||
|
||||
void setPermanentVehicle(boolean permanent);
|
||||
|
||||
int getPortalTimer();
|
||||
|
||||
void setPortalTimer(int amount);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package carpet.fakes;
|
||||
|
||||
public interface HungerManagerInterface {
|
||||
float getExhaustionCM();
|
||||
void setExhaustionCM(float exhaust);
|
||||
}
|
||||
@@ -2,6 +2,6 @@ package carpet.fakes;
|
||||
|
||||
public interface ItemEntityInterface
|
||||
{
|
||||
int getAge();
|
||||
int getPickupDelay();
|
||||
int getAgeCM();
|
||||
int getPickupDelayCM();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package carpet.fakes;
|
||||
|
||||
public interface LivingEntityInterface
|
||||
{
|
||||
void doJumpCM(); // added CM suffix to remove potential collisions with other mods
|
||||
boolean isJumpingCM();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package carpet.fakes;
|
||||
|
||||
import net.minecraft.server.world.ChunkTicketManager;
|
||||
|
||||
public interface ServerChunkManagerInterface
|
||||
{
|
||||
ChunkTicketManager getCMTicketManager();
|
||||
}
|
||||
@@ -263,6 +263,15 @@ public class BlockRotator
|
||||
}
|
||||
world.setBlockState(blockpos, iblockstate.with(HorizontalFacingBlock.FACING, face), 3);
|
||||
}
|
||||
else if(block == Blocks.HOPPER )
|
||||
{
|
||||
Direction face = iblockstate.get(HopperBlock.FACING);
|
||||
if (face != Direction.DOWN)
|
||||
{
|
||||
face = rotateClockwise(face, Direction.Axis.Y);
|
||||
world.setBlockState(blockpos, iblockstate.with(HopperBlock.FACING, face), 3);
|
||||
}
|
||||
}
|
||||
// Send block update to the block that just have been rotated.
|
||||
world.updateNeighbor(blockpos, block, source.getBlockPos());
|
||||
|
||||
@@ -270,8 +279,6 @@ public class BlockRotator
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static boolean flip_block(BlockState state, World world, PlayerEntity player, Hand hand, BlockHitResult hit)
|
||||
{
|
||||
Block block = state.getBlock();
|
||||
|
||||
@@ -390,6 +390,7 @@ public class EntityPlayerActionPack
|
||||
{
|
||||
player.interactionManager.processBlockBreakingAction(pos, PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, side, player.server.getWorldHeight());
|
||||
ap.currentBlock = null;
|
||||
ap.blockHitDelay = 5;
|
||||
blockBroken = true;
|
||||
}
|
||||
player.world.setBlockBreakingInfo(-1, pos, (int) (ap.curBlockDamageMP * 10));
|
||||
|
||||
@@ -6,13 +6,19 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Sets;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.structure.StructureManager;
|
||||
import net.minecraft.structure.StructureStart;
|
||||
import net.minecraft.util.math.BlockBox;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.Biomes;
|
||||
import net.minecraft.world.biome.DefaultBiomeFeatures;
|
||||
import net.minecraft.world.biome.source.BiomeAccess;
|
||||
import net.minecraft.world.biome.source.BiomeSource;
|
||||
import net.minecraft.world.biome.source.VanillaLayeredBiomeSource;
|
||||
import net.minecraft.world.biome.source.VanillaLayeredBiomeSourceConfig;
|
||||
import net.minecraft.world.gen.ChunkRandom;
|
||||
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.gen.chunk.OverworldChunkGenerator;
|
||||
import net.minecraft.world.gen.chunk.OverworldChunkGeneratorConfig;
|
||||
@@ -141,26 +147,47 @@ public class FeatureGenerator
|
||||
return null;
|
||||
}
|
||||
|
||||
public static StructureStart shouldStructureStartAt(ServerWorld world, BlockPos pos, StructureFeature<?> structure, boolean computeBox)
|
||||
{
|
||||
ChunkGenerator<?> generator = world.getChunkManager().getChunkGenerator();
|
||||
if (!generator.getBiomeSource().hasStructureFeature(structure))
|
||||
return null;
|
||||
BiomeAccess biomeAccess = world.getBiomeAccess().withSource(generator.getBiomeSource());
|
||||
ChunkRandom chunkRandom = new ChunkRandom();
|
||||
ChunkPos chunkPos = new ChunkPos(pos);
|
||||
Biome biome = biomeAccess.getBiome(new BlockPos(chunkPos.getStartX() + 9, 0, chunkPos.getStartZ() + 9));
|
||||
if (structure.shouldStartAt(biomeAccess, generator, chunkRandom, chunkPos.x, chunkPos.z, biome))
|
||||
{
|
||||
if (!computeBox) return StructureStart.DEFAULT;
|
||||
StructureManager manager = world.getSaveHandler().getStructureManager();
|
||||
StructureStart structureStart3 = structure.getStructureStartFactory().create(structure, chunkPos.x, chunkPos.z, BlockBox.empty(), 0, generator.getSeed());
|
||||
structureStart3.initialize(generator, manager, chunkPos.x, chunkPos.z, biome);
|
||||
if (!structureStart3.hasChildren()) return null;
|
||||
return structureStart3;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static final Map<String, List<String>> structureToFeature = new HashMap<>();
|
||||
public static final Map<String, String> featureToStructure = new HashMap<>();
|
||||
static
|
||||
{
|
||||
structureToFeature.put("Monument", Collections.singletonList("monument"));
|
||||
structureToFeature.put("EndCity", Collections.singletonList("end_city"));
|
||||
structureToFeature.put("Ocean_Ruin", Arrays.asList("ocean_ruin", "ocean_ruin_warm", "ocean_ruin_small", "ocean_ruin_warm_small", "ocean_ruin_tall", "ocean_ruin_warm_tall"));
|
||||
structureToFeature.put("Village", Arrays.asList("village", "village_desert", "village_savanna", "village_taiga", "village_snowy"));
|
||||
structureToFeature.put("Mansion", Collections.singletonList("mansion"));
|
||||
structureToFeature.put("Buried_Treasure", Collections.singletonList("treasure"));
|
||||
structureToFeature.put("Swamp_Hut", Collections.singletonList("witch_hut"));
|
||||
structureToFeature.put("Stronghold", Collections.singletonList("stronghold"));
|
||||
structureToFeature.put("Desert_Pyramid", Collections.singletonList("desert_temple"));
|
||||
structureToFeature.put("Jungle_Pyramid", Collections.singletonList("jungle_temple"));
|
||||
structureToFeature.put("Shipwreck", Arrays.asList("shipwreck", "shipwreck2"));
|
||||
structureToFeature.put("Pillager_Outpost", Collections.singletonList("pillager_outpost"));
|
||||
structureToFeature.put("Mineshaft", Arrays.asList("mineshaft", "mineshaft_mesa"));
|
||||
structureToFeature.put("Igloo", Collections.singletonList("igloo"));
|
||||
structureToFeature.put("Fortress", Collections.singletonList("fortress"));
|
||||
structureToFeature.put(Feature.OCEAN_MONUMENT.getName(), Collections.singletonList("monument"));
|
||||
structureToFeature.put(Feature.END_CITY.getName(), Collections.singletonList("end_city"));
|
||||
structureToFeature.put(Feature.OCEAN_RUIN.getName(), Arrays.asList("ocean_ruin", "ocean_ruin_warm", "ocean_ruin_small", "ocean_ruin_warm_small", "ocean_ruin_tall", "ocean_ruin_warm_tall"));
|
||||
structureToFeature.put(Feature.VILLAGE.getName(), Arrays.asList("village", "village_desert", "village_savanna", "village_taiga", "village_snowy"));
|
||||
structureToFeature.put(Feature.WOODLAND_MANSION.getName(), Collections.singletonList("mansion"));
|
||||
structureToFeature.put(Feature.BURIED_TREASURE.getName(), Collections.singletonList("treasure"));
|
||||
structureToFeature.put(Feature.SWAMP_HUT.getName(), Collections.singletonList("witch_hut"));
|
||||
structureToFeature.put(Feature.STRONGHOLD.getName(), Collections.singletonList("stronghold"));
|
||||
structureToFeature.put(Feature.DESERT_PYRAMID.getName(), Collections.singletonList("desert_temple"));
|
||||
structureToFeature.put(Feature.JUNGLE_TEMPLE.getName(), Collections.singletonList("jungle_temple"));
|
||||
structureToFeature.put(Feature.SHIPWRECK.getName(), Arrays.asList("shipwreck", "shipwreck2"));
|
||||
structureToFeature.put(Feature.PILLAGER_OUTPOST.getName(), Collections.singletonList("pillager_outpost"));
|
||||
structureToFeature.put(Feature.MINESHAFT.getName(), Arrays.asList("mineshaft", "mineshaft_mesa"));
|
||||
structureToFeature.put(Feature.IGLOO.getName(), Collections.singletonList("igloo"));
|
||||
structureToFeature.put(Feature.NETHER_BRIDGE.getName(), Collections.singletonList("fortress"));
|
||||
|
||||
structureToFeature.forEach((key, value) -> value.forEach(el -> featureToStructure.put(el, key)));
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ public class HopperCounter
|
||||
color, ticks*1.0/(20*60), (realTime?" - real time":""), total, total*1.0*(20*60*60)/ticks),
|
||||
"nb [X]", "^g reset", "!/counter "+color+" reset"
|
||||
));
|
||||
items.addAll(counter.object2LongEntrySet().stream().map(e ->
|
||||
items.addAll(counter.object2LongEntrySet().stream().sorted((e, f) -> Long.compare(f.getLongValue(), e.getLongValue())).map(e ->
|
||||
{
|
||||
BaseText itemName = new TranslatableText(e.getKey().getTranslationKey());
|
||||
long count = e.getLongValue();
|
||||
|
||||
@@ -7,8 +7,10 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import carpet.logging.logHelpers.ExplosionLogHelper;
|
||||
import carpet.mixins.ExplosionAccessor;
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.mixins.ExplosionMixin;
|
||||
import carpet.utils.Messenger;
|
||||
import com.google.common.collect.Lists;
|
||||
import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
|
||||
@@ -51,6 +53,7 @@ public class OptimizedExplosion
|
||||
private static List<Entity> entitylist;
|
||||
private static Vec3d vec3dmem;
|
||||
private static long tickmem;
|
||||
private static Vec3d vec3dZero = new Vec3d(0, 0, 0);
|
||||
// For disabling the explosion particles and sound
|
||||
public static int explosionSound = 0;
|
||||
|
||||
@@ -66,7 +69,7 @@ public class OptimizedExplosion
|
||||
private static ArrayList<Float> chances = new ArrayList<>();
|
||||
private static BlockPos blastChanceLocation;
|
||||
|
||||
public static void doExplosionA(Explosion e) {
|
||||
public static void doExplosionA(Explosion e, ExplosionLogHelper eLogger) {
|
||||
ExplosionAccessor eAccess = (ExplosionAccessor) e;
|
||||
|
||||
blastCalc(e);
|
||||
@@ -120,6 +123,9 @@ public class OptimizedExplosion
|
||||
entity.getX() == eAccess.getEntity().getX() &&
|
||||
entity.getY() == eAccess.getEntity().getY() &&
|
||||
entity.getZ() == eAccess.getEntity().getZ()) {
|
||||
if (eLogger != null) {
|
||||
eLogger.onEntityImpacted(entity, new Vec3d(0,-0.9923437498509884d, 0));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -158,6 +164,10 @@ public class OptimizedExplosion
|
||||
d11 = ProtectionEnchantment.transformExplosionKnockback((LivingEntity) entity, d10);
|
||||
}
|
||||
|
||||
if (eLogger != null) {
|
||||
eLogger.onEntityImpacted(entity, new Vec3d(d5 * d11, d7 * d11, d9 * d11));
|
||||
}
|
||||
|
||||
entity.setVelocity(entity.getVelocity().add(d5 * d11, d7 * d11, d9 * d11));
|
||||
|
||||
if (entity instanceof PlayerEntity) {
|
||||
|
||||
@@ -5,11 +5,12 @@ import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.network.ServerNetworkHandler;
|
||||
import carpet.utils.Messenger;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.BaseText;
|
||||
|
||||
public class TickSpeed
|
||||
@@ -20,11 +21,12 @@ public class TickSpeed
|
||||
public static long time_bias = 0;
|
||||
public static long time_warp_start_time = 0;
|
||||
public static long time_warp_scheduled_ticks = 0;
|
||||
public static PlayerEntity time_advancerer = null;
|
||||
public static ServerPlayerEntity time_advancerer = null;
|
||||
public static String tick_warp_callback = null;
|
||||
public static ServerCommandSource tick_warp_sender = null;
|
||||
public static int player_active_timeout = 0;
|
||||
public static boolean process_entities = true;
|
||||
public static boolean deepFreeze = false;
|
||||
public static boolean is_paused = false;
|
||||
public static boolean is_superHot = false;
|
||||
|
||||
@@ -43,18 +45,37 @@ public class TickSpeed
|
||||
}
|
||||
}
|
||||
|
||||
public static void reset()
|
||||
{
|
||||
tickrate = 20.0f;
|
||||
mspt = 50.0f;
|
||||
time_bias = 0;
|
||||
time_warp_start_time = 0;
|
||||
time_warp_scheduled_ticks = 0;
|
||||
time_advancerer = null;
|
||||
tick_warp_callback = null;
|
||||
tick_warp_sender = null;
|
||||
player_active_timeout = 0;
|
||||
process_entities = true;
|
||||
deepFreeze = false;
|
||||
is_paused = false;
|
||||
is_superHot = false;
|
||||
notifyTickrateListeners("carpet");
|
||||
}
|
||||
|
||||
public static void add_ticks_to_run_in_pause(int ticks)
|
||||
{
|
||||
player_active_timeout = PLAYER_GRACE+ticks;
|
||||
}
|
||||
|
||||
public static BaseText tickrate_advance(PlayerEntity player, int advance, String callback, ServerCommandSource source)
|
||||
public static BaseText tickrate_advance(ServerPlayerEntity player, int advance, String callback, ServerCommandSource source)
|
||||
{
|
||||
if (0 == advance)
|
||||
{
|
||||
tick_warp_callback = null;
|
||||
tick_warp_sender = null;
|
||||
if (source != tick_warp_sender) tick_warp_sender = null;
|
||||
finish_time_warp();
|
||||
tick_warp_sender = null;
|
||||
return Messenger.c("gi Warp interrupted");
|
||||
}
|
||||
if (time_bias > 0)
|
||||
@@ -157,8 +178,9 @@ public class TickSpeed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void tickrate(float rate)
|
||||
//unused - mod compat reasons
|
||||
public static void tickrate(float rate) {tickrate(rate, true);}
|
||||
public static void tickrate(float rate, boolean update)
|
||||
{
|
||||
tickrate = rate;
|
||||
long mspt = (long)(1000.0 / tickrate);
|
||||
@@ -170,7 +192,7 @@ public class TickSpeed
|
||||
|
||||
TickSpeed.mspt = (float)mspt;
|
||||
|
||||
notifyTickrateListeners("carpet");
|
||||
if (update) notifyTickrateListeners("carpet");
|
||||
}
|
||||
|
||||
private static void tickrateChanged(String modId, float rate)
|
||||
@@ -202,6 +224,7 @@ public class TickSpeed
|
||||
}
|
||||
}
|
||||
}
|
||||
ServerNetworkHandler.updateTickSpeedToConnectedPlayers();
|
||||
}
|
||||
|
||||
public static BiConsumer<String, Float> addTickrateListener(String modId, BiConsumer<String, Float> tickrateListener)
|
||||
|
||||
@@ -30,6 +30,7 @@ public class LoggerRegistry
|
||||
public static boolean __packets;
|
||||
public static boolean __weather;
|
||||
public static boolean __pathfinding;
|
||||
public static boolean __explosions;
|
||||
|
||||
public static void initLoggers()
|
||||
{
|
||||
@@ -52,6 +53,7 @@ public class LoggerRegistry
|
||||
registerLogger("packets", HUDLogger.stardardHUDLogger("packets", null, null));
|
||||
registerLogger("counter",HUDLogger.stardardHUDLogger("counter","white", Arrays.stream(DyeColor.values()).map(Object::toString).toArray(String[]::new)));
|
||||
registerLogger("mobcaps", HUDLogger.stardardHUDLogger("mobcaps", "dynamic",new String[]{"dynamic", "overworld", "nether","end"}));
|
||||
registerLogger("explosions", HUDLogger.stardardLogger("explosions", "brief",new String[]{"brief", "full"}));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
package carpet.logging.logHelpers;
|
||||
|
||||
import carpet.logging.LoggerRegistry;
|
||||
import carpet.utils.Messenger;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.text.BaseText;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.world.explosion.Explosion;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static carpet.utils.Messenger.c;
|
||||
|
||||
public class ExplosionLogHelper
|
||||
{
|
||||
private final boolean createFire;
|
||||
private final Explosion.DestructionType blockDestructionType;
|
||||
public final Vec3d pos;
|
||||
public final Entity entity;
|
||||
private final float power;
|
||||
private boolean affectBlocks = false;
|
||||
private Object2IntMap<EntityChangedStatusWithCount> impactedEntities = new Object2IntOpenHashMap<>();
|
||||
|
||||
private static long lastGametime = 0;
|
||||
private static int explosionCountInCurretGT = 0;
|
||||
|
||||
public ExplosionLogHelper(Entity entity, double x, double y, double z, float power, boolean createFire, Explosion.DestructionType blockDestructionType) {
|
||||
this.entity = entity;
|
||||
this.power = power;
|
||||
this.pos = new Vec3d(x,y,z);
|
||||
this.createFire = createFire;
|
||||
this.blockDestructionType = blockDestructionType;
|
||||
}
|
||||
|
||||
public void setAffectBlocks(boolean b)
|
||||
{
|
||||
affectBlocks = b;
|
||||
}
|
||||
|
||||
public void onExplosionDone(long gametime)
|
||||
{
|
||||
List<BaseText> messages = new ArrayList<>();
|
||||
if (!(lastGametime == gametime)){
|
||||
explosionCountInCurretGT = 0;
|
||||
lastGametime = gametime;
|
||||
messages.add(c("wb tick : ", "d " + gametime));
|
||||
}
|
||||
explosionCountInCurretGT++;
|
||||
LoggerRegistry.getLogger("explosions").log( (option) -> {
|
||||
if ("brief".equals(option))
|
||||
{
|
||||
messages.add( c("d #" + explosionCountInCurretGT,"gb ->",
|
||||
Messenger.dblt("l", pos.x, pos.y, pos.z), (affectBlocks)?"m (affects blocks)":"m (doesn't affect blocks)" ));
|
||||
}
|
||||
if ("full".equals(option))
|
||||
{
|
||||
messages.add( c("d #" + explosionCountInCurretGT,"gb ->", Messenger.dblt("l", pos.x, pos.y, pos.z) ));
|
||||
messages.add(c("w affects blocks: ", "m " + this.affectBlocks));
|
||||
messages.add(c("w creates fire: ", "m " + this.createFire));
|
||||
messages.add(c("w power: ", "c " + this.power));
|
||||
messages.add(c( "w destruction: ", "c " + this.blockDestructionType.name()));
|
||||
if (impactedEntities.isEmpty())
|
||||
{
|
||||
messages.add(c("w affected entities: ", "m None"));
|
||||
}
|
||||
else
|
||||
{
|
||||
messages.add(c("w affected entities:"));
|
||||
impactedEntities.forEach((k, v) ->
|
||||
{
|
||||
messages.add(c((k.pos.equals(pos))?"r - TNT":"w - ",
|
||||
Messenger.dblt((k.pos.equals(pos))?"r":"y", k.pos.x, k.pos.y, k.pos.z), "w dV",
|
||||
Messenger.dblt("d", k.accel.x, k.accel.y, k.accel.z),
|
||||
"w "+Registry.ENTITY_TYPE.getId(k.type).getPath(), (v>1)?"l ("+v+")":""
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
return messages.toArray(new BaseText[0]);
|
||||
});
|
||||
}
|
||||
|
||||
public void onEntityImpacted(Entity entity, Vec3d accel)
|
||||
{
|
||||
EntityChangedStatusWithCount ent = new EntityChangedStatusWithCount(entity, accel);
|
||||
impactedEntities.put(ent, impactedEntities.getOrDefault(ent, 0)+1);
|
||||
}
|
||||
|
||||
|
||||
public static class EntityChangedStatusWithCount
|
||||
{
|
||||
public final Vec3d pos;
|
||||
public final EntityType type;
|
||||
public final Vec3d accel;
|
||||
|
||||
public EntityChangedStatusWithCount(Entity e, Vec3d accel)
|
||||
{
|
||||
this.pos = e.getPos();
|
||||
this.type = e.getType();
|
||||
this.accel = accel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj instanceof EntityChangedStatusWithCount)
|
||||
{
|
||||
EntityChangedStatusWithCount other = (EntityChangedStatusWithCount) obj;
|
||||
return other.pos.equals(pos) && other.accel.equals(accel) && other.type.equals(type);
|
||||
}
|
||||
return super.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return pos.hashCode()+ type.hashCode()+accel.hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ public class TNTLogHelper
|
||||
{
|
||||
public boolean initialized;
|
||||
private double primedX, primedY, primedZ;
|
||||
private static long lastGametime = 0;
|
||||
private static int tntCount = 0;
|
||||
private Vec3d primedAngle;
|
||||
/**
|
||||
* Runs when the TNT is primed. Expects the position and motion angle of the TNT.
|
||||
@@ -24,8 +26,13 @@ public class TNTLogHelper
|
||||
/**
|
||||
* Runs when the TNT explodes. Expects the position of the TNT.
|
||||
*/
|
||||
public void onExploded(double x, double y, double z)
|
||||
public void onExploded(double x, double y, double z, long gametime)
|
||||
{
|
||||
if (!(lastGametime == gametime)){
|
||||
tntCount = 0;
|
||||
lastGametime = gametime;
|
||||
}
|
||||
tntCount++;
|
||||
LoggerRegistry.getLogger("tnt").log( (option) -> {
|
||||
switch (option)
|
||||
{
|
||||
@@ -36,6 +43,9 @@ public class TNTLogHelper
|
||||
"r E ",Messenger.dblt("r",x, y, z))};
|
||||
case "full":
|
||||
return new BaseText[]{Messenger.c(
|
||||
"r #" + String.valueOf(tntCount),
|
||||
"m @" + String.valueOf(gametime),
|
||||
"g : ",
|
||||
"l P ",Messenger.dblf("l",primedX,primedY,primedZ),
|
||||
"w ",Messenger.dblf("l", primedAngle.x, primedAngle.y, primedAngle.z),
|
||||
"r E ",Messenger.dblf("r",x, y, z))};
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.EntityContext;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.World;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(BlockItem.class)
|
||||
public class BlockItem_creativeNoClipMixin
|
||||
{
|
||||
@Redirect(method = "canPlace", at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/World;canPlace(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/entity/EntityContext;)Z"
|
||||
))
|
||||
private boolean canSpectatingPlace(World world, BlockState state, BlockPos pos, EntityContext context,
|
||||
ItemPlacementContext contextOuter, BlockState stateOuter)
|
||||
{
|
||||
PlayerEntity player = contextOuter.getPlayer();
|
||||
if (CarpetSettings.creativeNoClip && player != null && player.isCreative() && player.abilities.flying)
|
||||
{
|
||||
// copy from canPlace
|
||||
VoxelShape voxelShape = state.getCollisionShape(world, pos, context);
|
||||
return voxelShape.isEmpty() || world.intersectsEntities(player, voxelShape.offset(pos.getX(), pos.getY(), pos.getZ()));
|
||||
|
||||
}
|
||||
return world.canPlace(state, pos, context);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
@Mixin(Chunk.class)
|
||||
public abstract class ChunkMixin
|
||||
{
|
||||
// failed to mixin into interface. need to mixin in two places that uses it
|
||||
@Inject(method = "markBlockForPostProcessing(Lnet/minecraft/util/math/BlockPos;)V",
|
||||
at =@At("HEAD"), cancellable = true)
|
||||
private void squashWarnings(BlockPos blockPos_1, CallbackInfo ci)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.ChunkRegion;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ChunkRegion.class)
|
||||
public class ChunkRegion_scarpetPlopMixin
|
||||
{
|
||||
@Inject(method = "markBlockForPostProcessing", at = @At("HEAD"))
|
||||
private void markOrNot(BlockPos blockPos, CallbackInfo ci)
|
||||
{
|
||||
if (CarpetSettings.skipGenerationChecks) ci.cancel();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,23 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.fakes.ChunkTicketManagerInterface;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import net.minecraft.server.world.ChunkTicket;
|
||||
import net.minecraft.server.world.ChunkTicketManager;
|
||||
import net.minecraft.util.SortedArraySet;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(ChunkTicketManager.class)
|
||||
public interface ChunkTicketManager_scarpetMixin
|
||||
public abstract class ChunkTicketManager_scarpetMixin implements ChunkTicketManagerInterface
|
||||
{
|
||||
@Accessor
|
||||
Long2ObjectOpenHashMap<SortedArraySet<ChunkTicket<?>>> getTicketsByPosition();
|
||||
@Shadow @Final private Long2ObjectOpenHashMap<SortedArraySet<ChunkTicket<?>>> ticketsByPosition;
|
||||
|
||||
@Override
|
||||
public Long2ObjectOpenHashMap<SortedArraySet<ChunkTicket<?>>> getTicketsByPosition()
|
||||
{
|
||||
return ticketsByPosition;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.fakes.ChunkTicketManagerInterface;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import net.minecraft.server.world.ChunkTicket;
|
||||
import net.minecraft.server.world.ChunkTicketManager;
|
||||
import net.minecraft.server.world.ChunkTicketType;
|
||||
import net.minecraft.util.SortedArraySet;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
@Mixin(ChunkTicketManager.class)
|
||||
public abstract class ChunkTicketManager_spawnChunksMixin implements ChunkTicketManagerInterface
|
||||
{
|
||||
|
||||
@Shadow protected abstract SortedArraySet<ChunkTicket<?>> getTicketSet(long position);
|
||||
|
||||
@Shadow @Final private Long2ObjectOpenHashMap<SortedArraySet<ChunkTicket<?>>> ticketsByPosition;
|
||||
|
||||
@Shadow public abstract <T> void removeTicket(ChunkTicketType<T> type, ChunkPos pos, int radius, T argument);
|
||||
|
||||
@Shadow protected abstract void removeTicket(long pos, ChunkTicket<?> ticket);
|
||||
|
||||
@Shadow public abstract <T> void addTicket(ChunkTicketType<T> type, ChunkPos pos, int radius, T argument);
|
||||
|
||||
@Override
|
||||
public void changeSpawnChunks(ChunkPos chunkPos, int distance)
|
||||
{
|
||||
long pos = chunkPos.toLong();
|
||||
SortedArraySet<ChunkTicket<?>> set = ticketsByPosition.get(pos);
|
||||
ChunkTicket existingTicket = null;
|
||||
if (set != null)
|
||||
{
|
||||
Iterator<ChunkTicket<?>> iter = set.iterator();
|
||||
while(iter.hasNext())
|
||||
{
|
||||
ChunkTicket ticket = iter.next();
|
||||
if (ticket.getType() == ChunkTicketType.START)
|
||||
{
|
||||
existingTicket = ticket;
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
set.add(existingTicket);
|
||||
}
|
||||
// the reason we are removing the ticket this way is that there are sideeffects of removal
|
||||
if (existingTicket != null)
|
||||
{
|
||||
removeTicket(pos, existingTicket);
|
||||
}
|
||||
// set optionally new spawn ticket
|
||||
if (distance > 0)
|
||||
addTicket(ChunkTicketType.START, chunkPos, distance, Unit.INSTANCE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.helpers.TickSpeed;
|
||||
import net.minecraft.server.world.ChunkTicketManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ChunkTicketManager.class)
|
||||
public class ChunkTicketManager_tickMixin
|
||||
{
|
||||
@Shadow private long age;
|
||||
|
||||
@Inject(method = "purge", at = @At("HEAD"), cancellable = true)
|
||||
private void pauseTicketSystem(CallbackInfo ci)
|
||||
{
|
||||
// pausing expiry of tickets
|
||||
// that will prevent also chunks from unloading, so require a deep frozen state
|
||||
if (!TickSpeed.process_entities && TickSpeed.deepFreeze) age--;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.network.CarpetClient;
|
||||
import carpet.network.ClientNetworkHandler;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket;
|
||||
import net.minecraft.text.Text;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ClientPlayNetworkHandler.class)
|
||||
public abstract class ClientPlayNetworkHandlerMixin
|
||||
{
|
||||
@Shadow private MinecraftClient client;
|
||||
|
||||
@Inject(method = "onCustomPayload", at = @At("HEAD"), cancellable = true)
|
||||
private void onOnCustomPayload(CustomPayloadS2CPacket packet, CallbackInfo ci)
|
||||
{
|
||||
if (CarpetClient.CARPET_CHANNEL.equals(packet.getChannel()))
|
||||
{
|
||||
ClientNetworkHandler.handleData(packet.getData(), client.player);
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "onGameJoin", at = @At("RETURN"))
|
||||
private void onGameJoined(GameJoinS2CPacket packet, CallbackInfo info)
|
||||
{
|
||||
CarpetClient.gameJoined(client.player);
|
||||
}
|
||||
|
||||
@Inject(method = "onDisconnected", at = @At("HEAD"))
|
||||
private void onCMDisconnected(Text reason, CallbackInfo ci)
|
||||
{
|
||||
CarpetClient.disconnect();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.network.CarpetClient;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -13,7 +14,10 @@ public class ClientPlayerEntity_clientCommandMixin
|
||||
@Inject(method = "sendChatMessage", at = @At("HEAD"))
|
||||
private void inspectMessage(String string, CallbackInfo ci)
|
||||
{
|
||||
if (CarpetServer.minecraft_server == null)
|
||||
CarpetServer.settingsManager.inspectClientsideCommand(string);
|
||||
if (CarpetServer.minecraft_server == null && !CarpetClient.isCarpet())
|
||||
{
|
||||
ClientPlayerEntity playerSource = (ClientPlayerEntity)(Object) this;
|
||||
CarpetServer.settingsManager.inspectClientsideCommand(playerSource.getCommandSource(), string);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.fakes.CustomPayloadC2SPacketInterface;
|
||||
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.PacketByteBuf;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(CustomPayloadC2SPacket.class)
|
||||
public class CustomPayloadC2SPacketMixin implements CustomPayloadC2SPacketInterface
|
||||
{
|
||||
@Shadow
|
||||
private Identifier channel;
|
||||
@Shadow
|
||||
private PacketByteBuf data;
|
||||
|
||||
@Override
|
||||
public Identifier getPacketChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketByteBuf getPacketData() {
|
||||
return new PacketByteBuf(this.data.copy());
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import carpet.script.EntityEventsGroup;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.entity.damage.DamageTracker;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@@ -12,8 +13,11 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import static carpet.script.CarpetEventServer.Event.PLAYER_DEALS_DAMAGE;
|
||||
import static carpet.script.CarpetEventServer.Event.PLAYER_TAKES_DAMAGE;
|
||||
|
||||
@Mixin(DamageTracker.class)
|
||||
public class DamageTracker_scarpetEventMixin
|
||||
public abstract class DamageTracker_scarpetEventMixin
|
||||
{
|
||||
@Shadow @Final private LivingEntity entity;
|
||||
|
||||
@@ -21,5 +25,13 @@ public class DamageTracker_scarpetEventMixin
|
||||
private void onDamageTaken(DamageSource damageSource_1, float float_1, float float_2, CallbackInfo ci)
|
||||
{
|
||||
((EntityInterface)entity).getEventContainer().onEvent(EntityEventsGroup.EntityEventType.ON_DAMAGE, entity, float_2, damageSource_1);
|
||||
if (entity instanceof ServerPlayerEntity && PLAYER_TAKES_DAMAGE.isNeeded())
|
||||
{
|
||||
PLAYER_TAKES_DAMAGE.onDamage(entity, float_2, damageSource_1);
|
||||
}
|
||||
if (damageSource_1.getAttacker() instanceof ServerPlayerEntity && PLAYER_DEALS_DAMAGE.isNeeded())
|
||||
{
|
||||
PLAYER_DEALS_DAMAGE.onDamage(entity, float_2, damageSource_1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.network.CarpetClient;
|
||||
import net.minecraft.client.render.debug.DebugRenderer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(DebugRenderer.class)
|
||||
public class DebugRenderer_scarpetRenderMixin
|
||||
{
|
||||
@Inject(method = "reset", at = @At("HEAD"))
|
||||
private void resetScarpetRenderes(CallbackInfo ci)
|
||||
{
|
||||
if (CarpetClient.shapes != null)
|
||||
CarpetClient.shapes.reset();
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ public abstract class Entity_scarpetEventsMixin implements EntityInterface
|
||||
{
|
||||
@Shadow public boolean removed;
|
||||
|
||||
@Shadow protected int netherPortalTime;
|
||||
private boolean permanentVehicle;
|
||||
|
||||
private final EntityEventsGroup events = new EntityEventsGroup();
|
||||
@@ -36,6 +37,18 @@ public abstract class Entity_scarpetEventsMixin implements EntityInterface
|
||||
permanentVehicle = permanent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPortalTimer()
|
||||
{
|
||||
return netherPortalTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPortalTimer(int amount)
|
||||
{
|
||||
netherPortalTime = amount;
|
||||
}
|
||||
|
||||
@Inject(method = "tick", at = @At("HEAD"))
|
||||
private void onTickCall(CallbackInfo ci)
|
||||
{
|
||||
|
||||
@@ -2,9 +2,13 @@ package carpet.mixins;
|
||||
|
||||
import carpet.helpers.OptimizedExplosion;
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.logging.LoggerRegistry;
|
||||
import carpet.logging.logHelpers.ExplosionLogHelper;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.explosion.Explosion;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
@@ -24,13 +28,17 @@ public abstract class ExplosionMixin
|
||||
@Final
|
||||
private List<BlockPos> affectedBlocks;
|
||||
|
||||
@Shadow @Final private World world;
|
||||
|
||||
private ExplosionLogHelper eLogger;
|
||||
|
||||
@Inject(method = "collectBlocksAndDamageEntities", at = @At("HEAD"),
|
||||
cancellable = true)
|
||||
private void onExplosionA(CallbackInfo ci)
|
||||
{
|
||||
if (CarpetSettings.optimizedTNT)
|
||||
{
|
||||
OptimizedExplosion.doExplosionA((Explosion) (Object) this);
|
||||
OptimizedExplosion.doExplosionA((Explosion) (Object) this, eLogger);
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
@@ -39,6 +47,11 @@ public abstract class ExplosionMixin
|
||||
cancellable = true)
|
||||
private void onExplosionB(boolean spawnParticles, CallbackInfo ci)
|
||||
{
|
||||
if (eLogger != null)
|
||||
{
|
||||
eLogger.setAffectBlocks( ! affectedBlocks.isEmpty());
|
||||
eLogger.onExplosionDone(this.world.getTime());
|
||||
}
|
||||
if (CarpetSettings.explosionNoBlockDamage)
|
||||
{
|
||||
affectedBlocks.clear();
|
||||
@@ -58,4 +71,24 @@ public abstract class ExplosionMixin
|
||||
if (CarpetSettings.explosionNoBlockDamage) return Blocks.BEDROCK.getDefaultState();
|
||||
return world.getBlockState(pos);
|
||||
}
|
||||
|
||||
@Inject(method = "<init>(Lnet/minecraft/world/World;Lnet/minecraft/entity/Entity;DDDFZLnet/minecraft/world/explosion/Explosion$DestructionType;)V",
|
||||
at = @At(value = "RETURN"))
|
||||
private void onExplosionCreated(World world, Entity entity, double x, double y, double z, float power, boolean createFire, Explosion.DestructionType blockDestructionType, CallbackInfo ci)
|
||||
{
|
||||
if (LoggerRegistry.__explosions && ! world.isClient)
|
||||
{
|
||||
eLogger = new ExplosionLogHelper(entity, x, y, z, power, createFire, blockDestructionType);
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(method = "collectBlocksAndDamageEntities",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;setVelocity(Lnet/minecraft/util/math/Vec3d;)V"))
|
||||
private void setVelocityAndUpdateLogging(Entity entity, Vec3d velocity)
|
||||
{
|
||||
if (eLogger != null) {
|
||||
eLogger.onEntityImpacted(entity, velocity.subtract(entity.getVelocity()));
|
||||
}
|
||||
entity.setVelocity(velocity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.fakes.HungerManagerInterface;
|
||||
import net.minecraft.entity.player.HungerManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(HungerManager.class)
|
||||
public class HungerManagerMixin_scarpetEventsMixin implements HungerManagerInterface {
|
||||
@Shadow private float exhaustion;
|
||||
public float getExhaustionCM(){
|
||||
return exhaustion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExhaustionCM(float exhaust)
|
||||
{
|
||||
exhaustion = exhaust;
|
||||
}
|
||||
}
|
||||
@@ -30,12 +30,12 @@ public abstract class ItemEntityMixin extends Entity implements ItemEntityInterf
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAge() {
|
||||
public int getAgeCM() {
|
||||
return this.age;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPickupDelay() {
|
||||
public int getPickupDelayCM() {
|
||||
return this.pickupDelay;
|
||||
}
|
||||
|
||||
@@ -91,8 +91,8 @@ public abstract class ItemEntityMixin extends Entity implements ItemEntityInterf
|
||||
selfStack.increment(amount);
|
||||
self.setStack(selfStack);
|
||||
|
||||
this.pickupDelay = Math.max(((ItemEntityInterface)other).getPickupDelay(), this.pickupDelay);
|
||||
this.age = Math.min(((ItemEntityInterface)other).getAge(), this.age);
|
||||
this.pickupDelay = Math.max(((ItemEntityInterface)other).getPickupDelayCM(), this.pickupDelay);
|
||||
this.age = Math.min(((ItemEntityInterface)other).getAgeCM(), this.age);
|
||||
|
||||
otherStack.decrement(amount);
|
||||
if (otherStack.isEmpty())
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.world.World;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Constant;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(LivingEntity.class)
|
||||
public abstract class LivingEntity_creativeFlyMixin extends Entity
|
||||
{
|
||||
@Shadow public float flyingSpeed;
|
||||
|
||||
public LivingEntity_creativeFlyMixin(EntityType<?> type, World world)
|
||||
{
|
||||
super(type, world);
|
||||
}
|
||||
|
||||
@ModifyConstant(method = "travel", constant = @Constant(floatValue = 0.91F), expect = 2)
|
||||
private float drag(float original)
|
||||
{
|
||||
if (CarpetSettings.creativeFlyDrag != 0.09 && (Object)this instanceof PlayerEntity)
|
||||
{
|
||||
PlayerEntity self = (PlayerEntity)(Object)(this);
|
||||
if (self.abilities.flying && ! onGround )
|
||||
return (float)(1.0-CarpetSettings.creativeFlyDrag);
|
||||
}
|
||||
return original;
|
||||
}
|
||||
|
||||
|
||||
@Inject(method = "getMovementSpeed(F)F", at = @At("HEAD"), cancellable = true)
|
||||
private void flyingAltSpeed(float slipperiness, CallbackInfoReturnable<Float> cir)
|
||||
{
|
||||
if (CarpetSettings.creativeFlySpeed != 1.0D && (Object)this instanceof PlayerEntity)
|
||||
{
|
||||
PlayerEntity self = (PlayerEntity)(Object)(this);
|
||||
if (self.abilities.flying && !onGround)
|
||||
cir.setReturnValue(flyingSpeed* (float)CarpetSettings.creativeFlySpeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,33 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.fakes.EntityInterface;
|
||||
import carpet.fakes.LivingEntityInterface;
|
||||
import carpet.script.EntityEventsGroup;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.world.World;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Constant;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(LivingEntity.class)
|
||||
public abstract class LivingEntity_scarpetEventsMixin extends Entity
|
||||
public abstract class LivingEntity_scarpetEventsMixin extends Entity implements LivingEntityInterface
|
||||
{
|
||||
|
||||
@Shadow protected abstract void jump();
|
||||
|
||||
@Shadow protected boolean jumping;
|
||||
|
||||
public LivingEntity_scarpetEventsMixin(EntityType<?> type, World world)
|
||||
{
|
||||
super(type, world);
|
||||
@@ -26,4 +38,16 @@ public abstract class LivingEntity_scarpetEventsMixin extends Entity
|
||||
{
|
||||
((EntityInterface)this).getEventContainer().onEvent(EntityEventsGroup.EntityEventType.ON_DEATH, this, damageSource_1.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doJumpCM()
|
||||
{
|
||||
jump();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJumpingCM()
|
||||
{
|
||||
return jumping;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.network.CarpetClient;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@@ -18,4 +20,10 @@ public class MinecraftClientMixin
|
||||
//CM start game hook
|
||||
CarpetServer.onGameStarted();
|
||||
}
|
||||
|
||||
@Inject(method = "disconnect(Lnet/minecraft/client/gui/screen/Screen;)V", at = @At("HEAD"))
|
||||
private void onCloseGame(Screen screen, CallbackInfo ci)
|
||||
{
|
||||
CarpetClient.disconnect();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.CarpetSettings;
|
||||
import com.google.gson.JsonElement;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.WorldGenerationProgressListener;
|
||||
import net.minecraft.world.level.LevelGeneratorType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -41,4 +43,11 @@ public abstract class MinecraftServer_coreMixin
|
||||
{
|
||||
CarpetServer.onServerClosed((MinecraftServer) (Object) this);
|
||||
}
|
||||
|
||||
@Inject(method = "prepareStartRegion", at = @At("RETURN"))
|
||||
private void afterSpawnCreated(WorldGenerationProgressListener worldGenerationProgressListener, CallbackInfo ci)
|
||||
{
|
||||
if (CarpetSettings.spawnChunksSize != 11)
|
||||
CarpetSettings.ChangeSpawnChunksValidator.changeSpawnSize(CarpetSettings.spawnChunksSize);
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ public abstract class MinecraftServer_tickspeedMixin extends ReentrantThreadExec
|
||||
|
||||
this.profiler.startTick();
|
||||
this.profiler.push("tick");
|
||||
this.tick(TickSpeed.time_warp_start_time != 0 ? ()->true : this::shouldKeepTicking);
|
||||
this.tick(TickSpeed.time_warp_start_time != 0 ? ()->!hasRunningTasks() : this::shouldKeepTicking);
|
||||
this.profiler.swap("nextTickWait");
|
||||
this.field_19249 = true;
|
||||
this.field_19248 = Math.max(Util.getMeasuringTimeMs() + /*50L*/ msThisTick, this.timeReference);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import net.minecraft.block.entity.PistonBlockEntity;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(PistonBlockEntity.class)
|
||||
public class PistonBlockEntity_creativeNoClipMixin
|
||||
{
|
||||
@Inject(method = "method_23672", at = @At("HEAD"), cancellable = true)
|
||||
private static void dontPushSpectators(Direction direction, Entity entity, double d, Direction direction2, CallbackInfo ci)
|
||||
{
|
||||
if (CarpetSettings.creativeNoClip && entity instanceof PlayerEntity && (((PlayerEntity) entity).isCreative()) && ((PlayerEntity) entity).abilities.flying) ci.cancel();
|
||||
}
|
||||
|
||||
@Redirect(method = "pushEntities", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;setVelocity(DDD)V"))
|
||||
private void ignoreAccel(Entity entity, double x, double y, double z)
|
||||
{
|
||||
if (CarpetSettings.creativeNoClip && entity instanceof PlayerEntity && (((PlayerEntity) entity).isCreative()) && ((PlayerEntity) entity).abilities.flying) return;
|
||||
entity.setVelocity(x,y,z);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import carpet.fakes.PistonBlockEntityInterface;
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.piston.PistonBehavior;
|
||||
import net.minecraft.block.piston.PistonHandler;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
@@ -62,6 +63,17 @@ public abstract class PistonBlock_movableTEMixin extends FacingBlock
|
||||
return !(CarpetSettings.movableBlockEntities && isPushableBlockEntity(block));
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(method = "isMovable", at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/block/BlockState;getPistonBehavior()Lnet/minecraft/block/piston/PistonBehavior;"
|
||||
))
|
||||
private static PistonBehavior moveGrindstones(BlockState blockState)
|
||||
{
|
||||
if (CarpetSettings.movableBlockEntities && blockState.getBlock() == Blocks.GRINDSTONE) return PistonBehavior.NORMAL;
|
||||
return blockState.getPistonBehavior();
|
||||
}
|
||||
|
||||
@Inject(method = "move", at = @At(value = "INVOKE", shift = At.Shift.BEFORE,
|
||||
target = "Ljava/util/List;size()I", ordinal = 4),locals = LocalCapture.CAPTURE_FAILHARD)
|
||||
private void onMove(World world_1, BlockPos blockPos_1, Direction direction_1, boolean boolean_1,
|
||||
|
||||
@@ -23,7 +23,7 @@ public abstract class PlayerEntity_antiCheatDisabled
|
||||
@Inject(method = "method_23668", at = @At("HEAD"), cancellable = true)
|
||||
private void allowDeploys(CallbackInfoReturnable<Boolean> cir)
|
||||
{
|
||||
if (CarpetSettings.antiCheatDisabled && CarpetServer.minecraft_server.isDedicated())
|
||||
if (CarpetSettings.antiCheatDisabled && CarpetServer.minecraft_server != null && CarpetServer.minecraft_server.isDedicated())
|
||||
{
|
||||
ItemStack itemStack_1 = getEquippedStack(EquipmentSlot.CHEST);
|
||||
if (itemStack_1.getItem() == Items.ELYTRA && ElytraItem.isUsable(itemStack_1)) {
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.MovementType;
|
||||
import net.minecraft.entity.player.PlayerAbilities;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(PlayerEntity.class)
|
||||
public abstract class PlayerEntity_creativeNoClipMixin extends LivingEntity
|
||||
{
|
||||
@Shadow public abstract boolean isCreative();
|
||||
|
||||
@Shadow @Final public PlayerAbilities abilities;
|
||||
|
||||
protected PlayerEntity_creativeNoClipMixin(EntityType<? extends LivingEntity> type, World world)
|
||||
{
|
||||
super(type, world);
|
||||
}
|
||||
|
||||
@Redirect(method = "tick", at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/entity/player/PlayerEntity;isSpectator()Z")
|
||||
)
|
||||
private boolean canClipTroughWorld(PlayerEntity playerEntity)
|
||||
{
|
||||
return playerEntity.isSpectator() || (CarpetSettings.creativeNoClip && playerEntity.isCreative() && playerEntity.abilities.flying);
|
||||
|
||||
}
|
||||
|
||||
@Redirect(method = "tickMovement", at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/entity/player/PlayerEntity;isSpectator()Z")
|
||||
)
|
||||
private boolean collidesWithEntities(PlayerEntity playerEntity)
|
||||
{
|
||||
return playerEntity.isSpectator() || (CarpetSettings.creativeNoClip && playerEntity.isCreative() && playerEntity.abilities.flying);
|
||||
}
|
||||
|
||||
@Redirect(method = "updateSize", at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/entity/player/PlayerEntity;isSpectator()Z")
|
||||
)
|
||||
private boolean spectatorsDontPose(PlayerEntity playerEntity)
|
||||
{
|
||||
return playerEntity.isSpectator() || (CarpetSettings.creativeNoClip && playerEntity.isCreative() && playerEntity.abilities.flying);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import static carpet.script.CarpetEventServer.Event.PLAYER_CONNECTS;
|
||||
|
||||
@Mixin(PlayerManager.class)
|
||||
public class PlayerManager_coreMixin
|
||||
{
|
||||
@@ -16,5 +18,6 @@ public class PlayerManager_coreMixin
|
||||
private void onPlayerConnected(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci)
|
||||
{
|
||||
CarpetServer.onPlayerLoggedIn(player);
|
||||
PLAYER_CONNECTS.onPlayerEvent(player);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import net.minecraft.server.PlayerManager;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import static carpet.script.CarpetEventServer.Event.PLAYER_RESPAWNS;
|
||||
|
||||
@Mixin(PlayerManager.class)
|
||||
public class PlayerManager_scarpetEventsMixin
|
||||
{
|
||||
@Inject(method = "respawnPlayer", at = @At("HEAD"))
|
||||
private void onRespawn(ServerPlayerEntity player, DimensionType dimension, boolean alive, CallbackInfoReturnable<ServerPlayerEntity> cir)
|
||||
{
|
||||
PLAYER_RESPAWNS.onPlayerEvent(player);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.fakes.ServerChunkManagerInterface;
|
||||
import carpet.utils.SpawnReporter;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import net.minecraft.entity.EntityCategory;
|
||||
import net.minecraft.server.world.ChunkTicketManager;
|
||||
import net.minecraft.server.world.ServerChunkManager;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -24,12 +26,20 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
@Mixin(ServerChunkManager.class)
|
||||
public abstract class ServerChunkManagerMixin
|
||||
public abstract class ServerChunkManagerMixin implements ServerChunkManagerInterface
|
||||
{
|
||||
@Shadow @Final private ServerWorld world;
|
||||
|
||||
@Shadow @Final private static int CHUNKS_ELIGIBLE_FOR_SPAWNING;
|
||||
|
||||
@Shadow @Final private ChunkTicketManager ticketManager;
|
||||
|
||||
@Override // shared between scarpet and spawnChunks setting
|
||||
public ChunkTicketManager getCMTicketManager()
|
||||
{
|
||||
return ticketManager;
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "tickChunks",
|
||||
locals = LocalCapture.CAPTURE_FAILHARD,
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import net.minecraft.server.world.ChunkTicketManager;
|
||||
import net.minecraft.server.world.ServerChunkManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(ServerChunkManager.class)
|
||||
public interface ServerChunkManager_scarpetMixin
|
||||
{
|
||||
@Accessor
|
||||
ChunkTicketManager getTicketManager();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.fakes.CustomPayloadC2SPacketInterface;
|
||||
import carpet.network.CarpetClient;
|
||||
import carpet.network.ServerNetworkHandler;
|
||||
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ServerPlayNetworkHandler.class)
|
||||
public class ServerPlayNetworkHandlerMixin
|
||||
{
|
||||
@Shadow public ServerPlayerEntity player;
|
||||
|
||||
@Inject(method = "onCustomPayload", at = @At("HEAD"), cancellable = true)
|
||||
private void onCustomCarpetPayload(CustomPayloadC2SPacket packet, CallbackInfo ci)
|
||||
{
|
||||
Identifier channel = ((CustomPayloadC2SPacketInterface) packet).getPacketChannel();
|
||||
if (CarpetClient.CARPET_CHANNEL.equals(channel))
|
||||
{
|
||||
ServerNetworkHandler.handleData(((CustomPayloadC2SPacketInterface) packet).getPacketData(), player);
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import static carpet.script.CarpetEventServer.Event.PLAYER_DISCONNECTS;
|
||||
|
||||
@Mixin(ServerPlayNetworkHandler.class)
|
||||
public class ServerPlayNetworkHandler_coreMixin {
|
||||
@Shadow
|
||||
@@ -21,5 +23,6 @@ public class ServerPlayNetworkHandler_coreMixin {
|
||||
@Inject(method = "onDisconnected", at = @At("HEAD"))
|
||||
private void onPlayerDisconnect(Text reason, CallbackInfo ci) {
|
||||
CarpetServer.onPlayerLoggedOut(this.player);
|
||||
if (PLAYER_DISCONNECTS.isNeeded()) PLAYER_DISCONNECTS.onPlayerMessage(player, reason.asString());
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,10 @@ public class ServerPlayNetworkHandler_tickMixin
|
||||
}
|
||||
}
|
||||
|
||||
// to skip reposition adjustment check
|
||||
private static long lastMovedTick = 0L;
|
||||
private static double lastMoved = 0.0D;
|
||||
|
||||
@Inject(method = "onPlayerMove", at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/network/ServerPlayerEntity;isSleeping()Z",
|
||||
@@ -38,10 +42,23 @@ public class ServerPlayNetworkHandler_tickMixin
|
||||
))
|
||||
private void checkMove(PlayerMoveC2SPacket p, CallbackInfo ci)
|
||||
{
|
||||
if (Math.abs(p.getX(player.getX()) - lastTickX) > 0.0001D
|
||||
|| Math.abs(p.getY(player.getY()) - lastTickY) > 0.0001D
|
||||
|| Math.abs(p.getY(player.getZ()) - lastTickZ) > 0.0001D)
|
||||
double movedBy = player.getPos().squaredDistanceTo(lastTickX, lastTickY, lastTickZ);
|
||||
if (movedBy == 0.0D) return;
|
||||
// corrective tick
|
||||
if (movedBy < 0.0009 && lastMoved > 0.0009 && Math.abs(player.getServer().getTicks()-lastMovedTick-20)<2)
|
||||
{
|
||||
//CarpetSettings.LOG.error("Corrective movement packet");
|
||||
return;
|
||||
}
|
||||
if (movedBy > 0.0D)
|
||||
{
|
||||
//CarpetSettings.LOG.error(String.format(
|
||||
// "moved by %.6f at %d",
|
||||
// player.getPos().squaredDistanceTo(lastTickX, lastTickY, lastTickZ),
|
||||
// player.getServer().getTicks()-lastMovedTick
|
||||
//));
|
||||
lastMoved = movedBy;
|
||||
lastMovedTick = player.getServer().getTicks();
|
||||
TickSpeed.reset_player_active_timeout();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,20 +3,26 @@ package carpet.mixins;
|
||||
import carpet.fakes.EntityInterface;
|
||||
import carpet.script.EntityEventsGroup;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.stat.Stat;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import static carpet.script.CarpetEventServer.Event.PLAYER_CHANGES_DIMENSION;
|
||||
import static carpet.script.CarpetEventServer.Event.PLAYER_DIES;
|
||||
import static carpet.script.CarpetEventServer.Event.PLAYER_FINISHED_USING_ITEM;
|
||||
import static carpet.script.CarpetEventServer.Event.STATISTICS;
|
||||
|
||||
@@ -30,6 +36,8 @@ public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity
|
||||
|
||||
@Shadow protected abstract void consumeItem();
|
||||
|
||||
@Shadow public boolean notInAnyWorld;
|
||||
|
||||
@Redirect(method = "consumeItem", at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/entity/player/PlayerEntity;consumeItem()V"
|
||||
@@ -61,6 +69,10 @@ public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity
|
||||
private void onDeathEvent(DamageSource source, CallbackInfo ci)
|
||||
{
|
||||
((EntityInterface)this).getEventContainer().onEvent(EntityEventsGroup.EntityEventType.ON_DEATH, this, source.name);
|
||||
if (PLAYER_DIES.isNeeded())
|
||||
{
|
||||
PLAYER_DIES.onPlayerEvent((ServerPlayerEntity) (Object)this);
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(method = "method_14218", at = @At(
|
||||
@@ -72,4 +84,30 @@ public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity
|
||||
if (!((EntityInterface)serverPlayerEntity.getVehicle()).isPermanentVehicle()) // won't since that method makes sure its not null
|
||||
serverPlayerEntity.setSneaking(sneaking);
|
||||
}
|
||||
|
||||
private Vec3d previousLocation;
|
||||
private DimensionType previousDimension;
|
||||
|
||||
@Inject(method = "changeDimension", at = @At("HEAD"))
|
||||
private void logPreviousCoordinates(DimensionType newDimension, CallbackInfoReturnable<Entity> cir)
|
||||
{
|
||||
previousLocation = getPos();
|
||||
previousDimension = dimension;
|
||||
}
|
||||
|
||||
@Inject(method = "changeDimension", at = @At("RETURN"))
|
||||
private void atChangeDimension(DimensionType newDimension, CallbackInfoReturnable<Entity> cir)
|
||||
{
|
||||
if (PLAYER_CHANGES_DIMENSION.isNeeded())
|
||||
{
|
||||
ServerPlayerEntity player = (ServerPlayerEntity) (Object)this;
|
||||
Vec3d to = null;
|
||||
if (!notInAnyWorld || previousDimension != DimensionType.THE_END || newDimension != DimensionType.OVERWORLD)
|
||||
{
|
||||
to = getPos();
|
||||
}
|
||||
PLAYER_CHANGES_DIMENSION.onDimensionChange(player, previousLocation, to, previousDimension, newDimension);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.Constant;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||
|
||||
@Mixin(ServerWorld.class)
|
||||
public class ServerWorld_spawnChunksMixin
|
||||
{
|
||||
@ModifyConstant(method = "setSpawnPos", constant = @Constant(intValue = 11), expect = 2)
|
||||
private int pushLimit(int original)
|
||||
{
|
||||
return CarpetSettings.spawnChunksSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import net.minecraft.structure.StructurePiece;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(StructurePiece.class)
|
||||
public class StructurePiece_scarpetPlopMixin
|
||||
{
|
||||
@Redirect(method = "addBlock", at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/chunk/Chunk;markBlockForPostProcessing(Lnet/minecraft/util/math/BlockPos;)V"
|
||||
))
|
||||
private void markOrNot(Chunk chunk, BlockPos pos)
|
||||
{
|
||||
if (!CarpetSettings.skipGenerationChecks) chunk.markBlockForPostProcessing(pos);
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ public abstract class TntEntityMixin extends Entity implements TntEntityInterfac
|
||||
private void onExplode(CallbackInfo ci)
|
||||
{
|
||||
if (LoggerRegistry.__tnt && logHelper != null)
|
||||
logHelper.onExploded(getX(), getY(), getZ());
|
||||
logHelper.onExploded(getX(), getY(), getZ(), this.world.getTime());
|
||||
|
||||
if (mergedTNT > 1)
|
||||
for (int i = 0; i < mergedTNT - 1; i++)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(WorldRenderer.class)
|
||||
public class WorldRenderer_creativeNoClipMixin
|
||||
{
|
||||
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isSpectator()Z"))
|
||||
private boolean canSeeWorld(ClientPlayerEntity clientPlayerEntity)
|
||||
{
|
||||
return clientPlayerEntity.isSpectator() || (CarpetSettings.creativeNoClip && clientPlayerEntity.isCreative());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
import net.minecraft.world.dimension.Dimension;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(value = WorldRenderer.class, priority = 69420)
|
||||
public class WorldRenderer_fogOffMixin
|
||||
{
|
||||
@Redirect(method = "render", require = 0, expect = 0, at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/dimension/Dimension;isFogThick(II)Z"
|
||||
))
|
||||
private boolean isReallyThick(Dimension dimension, int x, int z)
|
||||
{
|
||||
if (CarpetSettings.fogOff) return false;
|
||||
return dimension.isFogThick(x, z);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.network.CarpetClient;
|
||||
import carpet.script.utils.ShapesRenderer;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.BufferBuilderStorage;
|
||||
import net.minecraft.client.render.Camera;
|
||||
import net.minecraft.client.render.GameRenderer;
|
||||
import net.minecraft.client.render.LightmapTextureManager;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
import net.minecraft.client.util.math.Matrix4f;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(WorldRenderer.class)
|
||||
public class WorldRenderer_scarpetRenderMixin
|
||||
{
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void addRenderers(MinecraftClient client, BufferBuilderStorage bufferBuilders, CallbackInfo ci)
|
||||
{
|
||||
CarpetClient.shapes = new ShapesRenderer(client);
|
||||
}
|
||||
|
||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/debug/DebugRenderer;render(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider$Immediate;DDD)V"))
|
||||
private void renderScarpetThings(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo ci)
|
||||
{
|
||||
if (CarpetClient.shapes != null) CarpetClient.shapes.render(camera, tickDelta);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package carpet.network;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.script.utils.ShapesRenderer;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class CarpetClient
|
||||
{
|
||||
public static final Object sync = new Object();
|
||||
public static final int HI = 69;
|
||||
public static final int HELLO = 420;
|
||||
public static final int DATA = 1;
|
||||
public static ShapesRenderer shapes = null;
|
||||
|
||||
private static ClientPlayerEntity clientPlayer = null;
|
||||
private static boolean isServerCarpet = false;
|
||||
public static String serverCarpetVersion;
|
||||
public static final Identifier CARPET_CHANNEL = new Identifier("carpet:hello");
|
||||
|
||||
public static void gameJoined(ClientPlayerEntity player)
|
||||
{
|
||||
synchronized (sync)
|
||||
{
|
||||
clientPlayer = player;
|
||||
// client didn't say hi back yet
|
||||
if (isServerCarpet)
|
||||
ClientNetworkHandler.respondHello();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void disconnect()
|
||||
{
|
||||
isServerCarpet = false;
|
||||
clientPlayer = null;
|
||||
CarpetServer.disconnect();
|
||||
}
|
||||
|
||||
public static void setCarpet()
|
||||
{
|
||||
isServerCarpet = true;
|
||||
}
|
||||
|
||||
public static ClientPlayerEntity getPlayer()
|
||||
{
|
||||
return clientPlayer;
|
||||
}
|
||||
|
||||
public static boolean isCarpet()
|
||||
{
|
||||
return isServerCarpet;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package carpet.network;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.helpers.TickSpeed;
|
||||
import carpet.settings.ParsedRule;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.nbt.AbstractNumberTag;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
|
||||
import net.minecraft.util.PacketByteBuf;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class ClientNetworkHandler
|
||||
{
|
||||
private static Map<String, BiConsumer<ClientPlayerEntity, Tag>> dataHandlers = new HashMap<String, BiConsumer<ClientPlayerEntity, Tag>>(){{
|
||||
put("Rules", (p, t) -> {
|
||||
CompoundTag ruleset = (CompoundTag)t;
|
||||
for (String ruleName: ruleset.getKeys())
|
||||
{
|
||||
ParsedRule<?> rule = CarpetServer.settingsManager.getRule(ruleName);
|
||||
if (rule == null)
|
||||
CarpetSettings.LOG.error("Received unknown rule: "+ruleName);
|
||||
else
|
||||
{
|
||||
CompoundTag ruleNBT = (CompoundTag) ruleset.get(ruleName);
|
||||
String value = ruleNBT.getString("Value");
|
||||
rule.set(null, value);
|
||||
}
|
||||
}
|
||||
});
|
||||
put("TickRate", (p, t) -> TickSpeed.tickrate(((AbstractNumberTag)t).getFloat(), false));
|
||||
put("scShape", (p, t) -> { // deprecated
|
||||
if (CarpetClient.shapes != null)
|
||||
CarpetClient.shapes.addShape((CompoundTag)t);
|
||||
});
|
||||
put("scShapes", (p, t) -> {
|
||||
if (CarpetClient.shapes != null)
|
||||
CarpetClient.shapes.addShapes((ListTag) t);
|
||||
});
|
||||
}};
|
||||
|
||||
|
||||
public static void handleData(PacketByteBuf data, ClientPlayerEntity player)
|
||||
{
|
||||
if (data != null)
|
||||
{
|
||||
int id = data.readVarInt();
|
||||
if (id == CarpetClient.HI)
|
||||
onHi(data);
|
||||
if (id == CarpetClient.DATA)
|
||||
onSyncData(data, player);
|
||||
}
|
||||
}
|
||||
|
||||
private static void onHi(PacketByteBuf data)
|
||||
{
|
||||
synchronized (CarpetClient.sync)
|
||||
{
|
||||
CarpetClient.setCarpet();
|
||||
CarpetClient.serverCarpetVersion = data.readString(64);
|
||||
if (CarpetSettings.carpetVersion.equals(CarpetClient.serverCarpetVersion))
|
||||
{
|
||||
CarpetSettings.LOG.info("Joined carpet server with matching carpet version");
|
||||
}
|
||||
else
|
||||
{
|
||||
CarpetSettings.LOG.warn("Joined carpet server with another carpet version: "+CarpetClient.serverCarpetVersion);
|
||||
}
|
||||
if (CarpetClient.getPlayer() != null)
|
||||
respondHello();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void respondHello()
|
||||
{
|
||||
CarpetClient.getPlayer().networkHandler.sendPacket(new CustomPayloadC2SPacket(
|
||||
CarpetClient.CARPET_CHANNEL,
|
||||
(new PacketByteBuf(Unpooled.buffer())).writeVarInt(CarpetClient.HELLO).writeString(CarpetSettings.carpetVersion)
|
||||
));
|
||||
}
|
||||
|
||||
private static void onSyncData(PacketByteBuf data, ClientPlayerEntity player)
|
||||
{
|
||||
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.error("Unknown carpet data: "+key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package carpet.network;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.helpers.TickSpeed;
|
||||
import carpet.settings.ParsedRule;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.PacketByteBuf;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class ServerNetworkHandler
|
||||
{
|
||||
private static Map<ServerPlayerEntity, String> remoteCarpetPlayers = new HashMap<>();
|
||||
private static Set<ServerPlayerEntity> validCarpetPlayers = new HashSet<>();
|
||||
|
||||
public static void handleData(PacketByteBuf data, ServerPlayerEntity player)
|
||||
{
|
||||
if (data != null)
|
||||
{
|
||||
int id = data.readVarInt();
|
||||
if (id == CarpetClient.HELLO)
|
||||
onHello(player, data);
|
||||
}
|
||||
}
|
||||
public static void onPlayerJoin(ServerPlayerEntity playerEntity)
|
||||
{
|
||||
if (!playerEntity.networkHandler.connection.isLocal())
|
||||
{
|
||||
playerEntity.networkHandler.sendPacket(new CustomPayloadS2CPacket(
|
||||
CarpetClient.CARPET_CHANNEL,
|
||||
(new PacketByteBuf(Unpooled.buffer())).writeVarInt(CarpetClient.HI).writeString(CarpetSettings.carpetVersion)
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
validCarpetPlayers.add(playerEntity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void onHello(ServerPlayerEntity playerEntity, PacketByteBuf packetData)
|
||||
{
|
||||
|
||||
validCarpetPlayers.add(playerEntity);
|
||||
String clientVersion = packetData.readString(64);
|
||||
remoteCarpetPlayers.put(playerEntity, clientVersion);
|
||||
if (clientVersion.equals(CarpetSettings.carpetVersion))
|
||||
CarpetSettings.LOG.info("Player "+playerEntity.getName().getString()+" joined with a matching carpet client");
|
||||
else
|
||||
CarpetSettings.LOG.warn("Player "+playerEntity.getName().getString()+" joined with another carpet version: "+clientVersion);
|
||||
DataBuilder data = DataBuilder.create().withTickRate();
|
||||
CarpetServer.settingsManager.getRules().forEach(data::withRule);
|
||||
playerEntity.networkHandler.sendPacket(new CustomPayloadS2CPacket(CarpetClient.CARPET_CHANNEL, data.build() ));
|
||||
}
|
||||
|
||||
public static void updateRuleWithConnectedClients(ParsedRule<?> rule)
|
||||
{
|
||||
if (CarpetSettings.superSecretSetting) return;
|
||||
for (ServerPlayerEntity player : remoteCarpetPlayers.keySet())
|
||||
{
|
||||
player.networkHandler.sendPacket(new CustomPayloadS2CPacket(
|
||||
CarpetClient.CARPET_CHANNEL,
|
||||
DataBuilder.create().withRule(rule).build()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateTickSpeedToConnectedPlayers()
|
||||
{
|
||||
if (CarpetSettings.superSecretSetting) return;
|
||||
for (ServerPlayerEntity player : remoteCarpetPlayers.keySet())
|
||||
{
|
||||
player.networkHandler.sendPacket(new CustomPayloadS2CPacket(
|
||||
CarpetClient.CARPET_CHANNEL,
|
||||
DataBuilder.create().withTickRate().build()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public static void broadcastCustomCommand(String command, Tag data)
|
||||
{
|
||||
if (CarpetSettings.superSecretSetting) return;
|
||||
for (ServerPlayerEntity player : validCarpetPlayers)
|
||||
{
|
||||
player.networkHandler.sendPacket(new CustomPayloadS2CPacket(
|
||||
CarpetClient.CARPET_CHANNEL,
|
||||
DataBuilder.create().withCustomNbt(command, data).build()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendCustomCommand(ServerPlayerEntity player, String command, Tag data)
|
||||
{
|
||||
if (isValidCarpetPlayer(player))
|
||||
{
|
||||
player.networkHandler.sendPacket(new CustomPayloadS2CPacket(
|
||||
CarpetClient.CARPET_CHANNEL,
|
||||
DataBuilder.create().withCustomNbt(command, data).build()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void onPlayerLoggedOut(ServerPlayerEntity player)
|
||||
{
|
||||
validCarpetPlayers.remove(player);
|
||||
if (!player.networkHandler.connection.isLocal())
|
||||
remoteCarpetPlayers.remove(player);
|
||||
}
|
||||
|
||||
public static void close()
|
||||
{
|
||||
remoteCarpetPlayers.clear();
|
||||
validCarpetPlayers.clear();
|
||||
}
|
||||
|
||||
public static boolean isValidCarpetPlayer(ServerPlayerEntity player)
|
||||
{
|
||||
if (CarpetSettings.superSecretSetting) return false;
|
||||
return validCarpetPlayers.contains(player);
|
||||
|
||||
}
|
||||
|
||||
private static class DataBuilder
|
||||
{
|
||||
private CompoundTag tag;
|
||||
private static DataBuilder create()
|
||||
{
|
||||
return new DataBuilder();
|
||||
}
|
||||
private DataBuilder()
|
||||
{
|
||||
tag = new CompoundTag();
|
||||
}
|
||||
private DataBuilder withTickRate()
|
||||
{
|
||||
tag.putFloat("TickRate", TickSpeed.tickrate);
|
||||
return this;
|
||||
}
|
||||
private DataBuilder withRule(ParsedRule<?> rule)
|
||||
{
|
||||
CompoundTag rules = (CompoundTag) tag.get("Rules");
|
||||
if (rules == null)
|
||||
{
|
||||
rules = new CompoundTag();
|
||||
tag.put("Rules", rules);
|
||||
}
|
||||
CompoundTag ruleNBT = new CompoundTag();
|
||||
ruleNBT.putString("Value", rule.getAsString());
|
||||
rules.put(rule.name, ruleNBT);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DataBuilder withCustomNbt(String key, Tag value)
|
||||
{
|
||||
tag.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
private PacketByteBuf build()
|
||||
{
|
||||
PacketByteBuf packetBuf = new PacketByteBuf(Unpooled.buffer());
|
||||
packetBuf.writeVarInt(CarpetClient.DATA);
|
||||
packetBuf.writeCompoundTag(tag);
|
||||
return packetBuf;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import carpet.utils.Messenger;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
@@ -50,6 +52,7 @@ import java.util.stream.Collectors;
|
||||
public class CarpetEventServer
|
||||
{
|
||||
public final List<ScheduledCall> scheduledCalls = new LinkedList<>();
|
||||
public final MinecraftServer server;
|
||||
|
||||
public static class Callback
|
||||
{
|
||||
@@ -125,7 +128,13 @@ public class CarpetEventServer
|
||||
List<LazyValue> argv = argumentSupplier.get(); // empty for onTickDone
|
||||
ServerCommandSource source = cmdSourceSupplier.get();
|
||||
assert argv.size() == reqArgs;
|
||||
callList.removeIf(call -> !CarpetServer.scriptServer.runas(source, call.host, call.function, argv)); // this actually does the calls
|
||||
List<Callback> fails = new ArrayList<>();
|
||||
for (Callback call: callList)
|
||||
{
|
||||
if (!CarpetServer.scriptServer.runas(source, call.host, call.function, argv))
|
||||
fails.add(call);
|
||||
}
|
||||
for (Callback call : fails) callList.remove(call);
|
||||
}
|
||||
}
|
||||
public boolean addEventCall(ServerCommandSource source, String hostName, String funName, Function<ScriptHost, Boolean> verifier)
|
||||
@@ -479,6 +488,90 @@ public class CarpetEventServer
|
||||
handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource);
|
||||
}
|
||||
},
|
||||
PLAYER_TAKES_DAMAGE("player_takes_damage", 4, false)
|
||||
{
|
||||
@Override
|
||||
public void onDamage(Entity target, float amount, DamageSource source)
|
||||
{
|
||||
handler.call( () ->
|
||||
{
|
||||
return Arrays.asList(
|
||||
((c, t) -> new EntityValue(target)),
|
||||
((c, t) -> new NumericValue(amount)),
|
||||
((c, t) ->new StringValue(source.getName())),
|
||||
((c, t) -> source.getAttacker()==null?Value.NULL:new EntityValue(source.getAttacker()))
|
||||
);
|
||||
}, target::getCommandSource);
|
||||
}
|
||||
},
|
||||
PLAYER_DEALS_DAMAGE("player_deals_damage", 3, false)
|
||||
{
|
||||
@Override
|
||||
public void onDamage(Entity target, float amount, DamageSource source)
|
||||
{
|
||||
handler.call( () ->
|
||||
{
|
||||
return Arrays.asList(
|
||||
((c, t) -> new EntityValue(source.getAttacker())),
|
||||
((c, t) -> new NumericValue(amount)),
|
||||
((c, t) -> new EntityValue(target))
|
||||
);
|
||||
}, () -> source.getAttacker().getCommandSource());
|
||||
}
|
||||
},
|
||||
PLAYER_DIES("player_dies", 1, false)
|
||||
{
|
||||
@Override
|
||||
public void onPlayerEvent(ServerPlayerEntity player)
|
||||
{
|
||||
handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource);
|
||||
}
|
||||
},
|
||||
PLAYER_RESPAWNS("player_respawns", 1, false)
|
||||
{
|
||||
@Override
|
||||
public void onPlayerEvent(ServerPlayerEntity player)
|
||||
{
|
||||
handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource);
|
||||
}
|
||||
},
|
||||
PLAYER_CHANGES_DIMENSION("player_changes_dimension", 5, false)
|
||||
{
|
||||
@Override
|
||||
public void onDimensionChange(ServerPlayerEntity player, Vec3d from, Vec3d to, DimensionType fromDim, DimensionType dimTo)
|
||||
{
|
||||
// eligibility already checked in mixin
|
||||
Value fromValue = ListValue.fromTriple(from.x, from.y, from.z);
|
||||
Value toValue = (to == null)?Value.NULL:ListValue.fromTriple(to.x, to.y, to.z);
|
||||
Value fromDimStr = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.DIMENSION_TYPE.getId(fromDim)));
|
||||
Value toDimStr = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.DIMENSION_TYPE.getId(dimTo)));
|
||||
|
||||
handler.call( () -> Arrays.asList(
|
||||
((c, t) -> new EntityValue(player)),
|
||||
((c, t) -> fromValue),
|
||||
((c, t) -> fromDimStr),
|
||||
((c, t) -> toValue),
|
||||
((c, t) -> toDimStr)
|
||||
), player::getCommandSource);
|
||||
}
|
||||
},
|
||||
PLAYER_CONNECTS("player_connects", 1, false) {
|
||||
@Override
|
||||
public void onPlayerEvent(ServerPlayerEntity player)
|
||||
{
|
||||
handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource);
|
||||
}
|
||||
},
|
||||
PLAYER_DISCONNECTS("player_disconnects", 2, false) {
|
||||
@Override
|
||||
public void onPlayerMessage(ServerPlayerEntity player, String message)
|
||||
{
|
||||
handler.call( () -> Arrays.asList(
|
||||
((c, t) -> new EntityValue(player)),
|
||||
((c, t) -> new StringValue(message))
|
||||
), player::getCommandSource);
|
||||
}
|
||||
},
|
||||
STATISTICS("statistic", 4, false)
|
||||
{
|
||||
private <T> Identifier getStatId(Stat<T> stat)
|
||||
@@ -543,6 +636,7 @@ public class CarpetEventServer
|
||||
public void onTick() { }
|
||||
public void onChunkGenerated(ServerWorld world, Chunk chunk) { }
|
||||
public void onPlayerEvent(ServerPlayerEntity player) { }
|
||||
public void onPlayerMessage(ServerPlayerEntity player, String message) { }
|
||||
public void onPlayerStatistic(ServerPlayerEntity player, Stat<?> stat, int amount) { }
|
||||
public void onMountControls(ServerPlayerEntity player, float strafeSpeed, float forwardSpeed, boolean jumping, boolean sneaking) { }
|
||||
public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) { }
|
||||
@@ -551,13 +645,18 @@ public class CarpetEventServer
|
||||
public void onBlockBroken(ServerPlayerEntity player, BlockPos pos, BlockState previousBS) { }
|
||||
public void onBlockPlaced(ServerPlayerEntity player, BlockPos pos, Hand enumhand, ItemStack itemstack) { }
|
||||
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 onWorldEvent(ServerWorld world, BlockPos pos) { }
|
||||
public void onWorldEventFlag(ServerWorld world, BlockPos pos, int flag) { }
|
||||
}
|
||||
|
||||
|
||||
public CarpetEventServer()
|
||||
public CarpetEventServer(MinecraftServer server)
|
||||
{
|
||||
this.server = server;
|
||||
for (Event e: Event.values())
|
||||
e.handler.callList.clear();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -96,7 +96,7 @@ public class CarpetScriptHost extends ScriptHost
|
||||
public void addUserDefinedFunction(Context ctx, Module module, String funName, FunctionValue function)
|
||||
{
|
||||
super.addUserDefinedFunction(ctx, module, funName, function);
|
||||
// mcarpet
|
||||
if (ctx.host.main != module) return; // not dealing with automatic imports / exports /configs / apps from imports
|
||||
if (funName.startsWith("__")) // potential fishy activity
|
||||
{
|
||||
if (funName.startsWith("__on_")) // here we can make a determination if we want to only accept events from main module.
|
||||
@@ -323,7 +323,7 @@ public class CarpetScriptHost extends ScriptHost
|
||||
{
|
||||
try
|
||||
{
|
||||
callUDF(BlockPos.ORIGIN, CarpetServer.minecraft_server.getCommandSource(), closing, Collections.emptyList());
|
||||
callUDF(BlockPos.ORIGIN, scriptServer.server.getCommandSource(), closing, Collections.emptyList());
|
||||
}
|
||||
catch (InvalidCallbackException ignored)
|
||||
{
|
||||
@@ -334,8 +334,8 @@ public class CarpetScriptHost extends ScriptHost
|
||||
FunctionValue userClosing = value.getFunction("__on_close");
|
||||
if (userClosing != null)
|
||||
{
|
||||
ServerPlayerEntity player = CarpetServer.minecraft_server.getPlayerManager().getPlayer(key);
|
||||
ServerCommandSource source = (player != null)?player.getCommandSource():CarpetServer.minecraft_server.getCommandSource();
|
||||
ServerPlayerEntity player = scriptServer.server.getPlayerManager().getPlayer(key);
|
||||
ServerCommandSource source = (player != null)?player.getCommandSource():scriptServer.server.getCommandSource();
|
||||
try
|
||||
{
|
||||
((CarpetScriptHost) value).callUDF(BlockPos.ORIGIN, source, userClosing, Collections.emptyList());
|
||||
@@ -347,7 +347,7 @@ public class CarpetScriptHost extends ScriptHost
|
||||
});
|
||||
|
||||
String markerName = CarpetExpression.MARKER_STRING+"_"+((getName()==null)?"":getName());
|
||||
for (ServerWorld world : CarpetServer.minecraft_server.getWorlds())
|
||||
for (ServerWorld world : scriptServer.server.getWorlds())
|
||||
{
|
||||
for (Entity e : world.getEntities(EntityType.ARMOR_STAND, (as) -> as.getScoreboardTags().contains(markerName)))
|
||||
{
|
||||
@@ -361,32 +361,31 @@ public class CarpetScriptHost extends ScriptHost
|
||||
|
||||
private void dumpState()
|
||||
{
|
||||
main.saveData(null, globalState);
|
||||
Module.saveData(main, null, globalState, false);
|
||||
}
|
||||
|
||||
private Tag loadState()
|
||||
{
|
||||
return main.getData(null);
|
||||
return Module.getData(main, null, false);
|
||||
}
|
||||
|
||||
public Tag getGlobalState(String file)
|
||||
public Tag readFileTag(String file, boolean isShared)
|
||||
{
|
||||
if (getName() == null ) return null;
|
||||
if (getName() == null && !isShared) return null;
|
||||
if (file != null)
|
||||
return main.getData(file);
|
||||
return Module.getData(main, file, isShared);
|
||||
if (parent == null)
|
||||
return globalState;
|
||||
return ((CarpetScriptHost)parent).globalState;
|
||||
}
|
||||
|
||||
public void setGlobalState(Tag tag, String file)
|
||||
public boolean writeTagFile(Tag tag, String file, boolean isShared)
|
||||
{
|
||||
if (getName() == null ) return;
|
||||
if (getName() == null && !isShared) return false; // if belongs to an app, cannot be default host.
|
||||
|
||||
if (file!= null)
|
||||
{
|
||||
main.saveData(file, tag);
|
||||
return;
|
||||
return Module.saveData(main, file, tag, isShared);
|
||||
}
|
||||
|
||||
CarpetScriptHost responsibleHost = (parent != null)?(CarpetScriptHost) parent:this;
|
||||
@@ -396,8 +395,28 @@ public class CarpetScriptHost extends ScriptHost
|
||||
responsibleHost.dumpState();
|
||||
responsibleHost.saveTimeout = 200;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean removeResourceFile(String resource, boolean isShared, String type)
|
||||
{
|
||||
if (getName() == null && !isShared) return false; //
|
||||
return Module.dropExistingFile(main, resource, type.equals("nbt")?"nbt":"txt", isShared);
|
||||
}
|
||||
|
||||
public boolean appendLogFile(String resource, boolean isShared, String type, List<String> data)
|
||||
{
|
||||
if (getName() == null && !isShared) return false; // if belongs to an app, cannot be default host.
|
||||
return Module.appendToTextFile(main, resource, type, isShared, data);
|
||||
}
|
||||
|
||||
public List<String> readTextResource(String resource, boolean isShared)
|
||||
{
|
||||
if (getName() == null && !isShared) return null; //
|
||||
return Module.listFile(main, resource, "txt", isShared);
|
||||
}
|
||||
|
||||
|
||||
public void tick()
|
||||
{
|
||||
if (this.saveTimeout > 0)
|
||||
@@ -491,4 +510,9 @@ public class CarpetScriptHost extends ScriptHost
|
||||
{
|
||||
handleErrorWithStack(message, new CarpetExpressionException(exc.getMessage(), exc.stack));
|
||||
}
|
||||
|
||||
public CarpetScriptServer getScriptServer()
|
||||
{
|
||||
return scriptServer;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import carpet.utils.Messenger;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
@@ -34,6 +35,7 @@ import static net.minecraft.server.command.CommandManager.literal;
|
||||
public class CarpetScriptServer
|
||||
{
|
||||
//make static for now, but will change that later:
|
||||
public final MinecraftServer server;
|
||||
public final CarpetScriptHost globalHost;
|
||||
public final Map<String, CarpetScriptHost> modules;
|
||||
public long tickStart;
|
||||
@@ -47,16 +49,18 @@ public class CarpetScriptServer
|
||||
add(new BundledModule("event_test", false));
|
||||
add(new BundledModule("stats_test", false));
|
||||
add(new BundledModule("math", true));
|
||||
add(new BundledModule("chunk_display", false));
|
||||
}};
|
||||
|
||||
public CarpetScriptServer()
|
||||
public CarpetScriptServer(MinecraftServer server)
|
||||
{
|
||||
this.server = server;
|
||||
ScriptHost.systemGlobals.clear();
|
||||
events = new CarpetEventServer();
|
||||
events = new CarpetEventServer(server);
|
||||
modules = new HashMap<>();
|
||||
tickStart = 0L;
|
||||
stopAll = false;
|
||||
holyMoly = CarpetServer.minecraft_server.getCommandManager().getDispatcher().getRoot().getChildren().stream().map(CommandNode::getName).collect(Collectors.toSet());
|
||||
holyMoly = server.getCommandManager().getDispatcher().getRoot().getChildren().stream().map(CommandNode::getName).collect(Collectors.toSet());
|
||||
globalHost = CarpetScriptHost.create(this, null, false, null);
|
||||
}
|
||||
|
||||
@@ -64,10 +68,10 @@ public class CarpetScriptServer
|
||||
{
|
||||
if (CarpetSettings.scriptsAutoload)
|
||||
{
|
||||
Messenger.m(CarpetServer.minecraft_server.getCommandSource(), "Auto-loading world scarpet apps");
|
||||
Messenger.m(server.getCommandSource(), "Auto-loading world scarpet apps");
|
||||
for (String moduleName: listAvailableModules(false))
|
||||
{
|
||||
addScriptHost(CarpetServer.minecraft_server.getCommandSource(), moduleName, true, true);
|
||||
addScriptHost(server.getCommandSource(), moduleName, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,8 +79,8 @@ public class CarpetScriptServer
|
||||
|
||||
public Module getModule(String name, boolean allowLibraries)
|
||||
{
|
||||
File folder = CarpetServer.minecraft_server.getLevelStorage().resolveFile(
|
||||
CarpetServer.minecraft_server.getLevelName(), "scripts");
|
||||
File folder = server.getLevelStorage().resolveFile(
|
||||
server.getLevelName(), "scripts");
|
||||
File[] listOfFiles = folder.listFiles();
|
||||
if (listOfFiles != null)
|
||||
for (File script : listOfFiles)
|
||||
@@ -110,8 +114,8 @@ public class CarpetScriptServer
|
||||
if (!mi.isLibrary()) moduleNames.add(mi.getName());
|
||||
}
|
||||
}
|
||||
File folder = CarpetServer.minecraft_server.getLevelStorage().resolveFile(
|
||||
CarpetServer.minecraft_server.getLevelName(), "scripts");
|
||||
File folder = server.getLevelStorage().resolveFile(
|
||||
server.getLevelName(), "scripts");
|
||||
File[] listOfFiles = folder.listFiles();
|
||||
if (listOfFiles == null)
|
||||
return moduleNames;
|
||||
@@ -223,7 +227,7 @@ public class CarpetScriptServer
|
||||
})));
|
||||
}
|
||||
Messenger.m(source, "gi "+hostName+" app loaded with /"+hostName+" command");
|
||||
CarpetServer.minecraft_server.getCommandManager().getDispatcher().register(command);
|
||||
server.getCommandManager().getDispatcher().register(command);
|
||||
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import carpet.script.Fluff.QuadFunction;
|
||||
import carpet.script.Fluff.QuinnFunction;
|
||||
import carpet.script.Fluff.SexFunction;
|
||||
import carpet.script.Fluff.TriFunction;
|
||||
import carpet.script.argument.FunctionArgument;
|
||||
import carpet.script.bundled.Module;
|
||||
import carpet.script.exception.BreakStatement;
|
||||
import carpet.script.exception.ContinueStatement;
|
||||
@@ -417,6 +418,12 @@ public class Expression
|
||||
return result;
|
||||
}
|
||||
|
||||
public void alias(String copy, String original)
|
||||
{
|
||||
functions.put(copy, functions.get(original));
|
||||
}
|
||||
|
||||
|
||||
private void UserDefinedFunctionsAndControlFlow() // public just to get the javadoc right
|
||||
{
|
||||
// artificial construct to handle user defined functions and function definitions
|
||||
@@ -439,19 +446,9 @@ public class Expression
|
||||
//lv.remove(lv.size()-1); // aint gonna cut it // maybe it will because of the eager eval changes
|
||||
if (t != Context.SIGNATURE) // just call the function
|
||||
{
|
||||
Value functionValue = lv.get(0).evalValue(c);
|
||||
if (!(functionValue instanceof FunctionValue))
|
||||
{
|
||||
String name = functionValue.getString();
|
||||
functionValue = c.host.getAssertFunction(module, name);
|
||||
}
|
||||
List<LazyValue> lvargs = new ArrayList<>(lv.size()-1);
|
||||
for (int i=1; i< lv.size(); i++)
|
||||
{
|
||||
lvargs.add(lv.get(i));
|
||||
}
|
||||
FunctionValue fun = (FunctionValue)functionValue;
|
||||
Value retval = fun.callInContext(expr, c, t, fun.getExpression(), fun.getToken(), lvargs).evalValue(c);
|
||||
FunctionArgument functionArgument = FunctionArgument.findIn(c, module, lv, 0, false, true);
|
||||
FunctionValue fun = functionArgument.function;
|
||||
Value retval = fun.callInContext(expr, c, t, fun.getExpression(), fun.getToken(), functionArgument.args).evalValue(c);
|
||||
return (cc, tt) -> retval; ///!!!! dono might need to store expr and token in statics? (e? t?)
|
||||
}
|
||||
// gimme signature
|
||||
@@ -657,9 +654,18 @@ public class Expression
|
||||
throw new InternalExpressionException("Failed to resolve left hand side of the += operation");
|
||||
}
|
||||
Value key = ((LContainerValue) v1).getAddress();
|
||||
Value res = cvi.get(key).add(v2);
|
||||
cvi.put(key, res);
|
||||
return (cc, tt) -> res;
|
||||
Value value = cvi.get(key);
|
||||
if (value instanceof ListValue || value instanceof MapValue)
|
||||
{
|
||||
((AbstractListValue) value).append(v2);
|
||||
return (cc, tt) -> value;
|
||||
}
|
||||
else
|
||||
{
|
||||
Value res = value.add(v2);
|
||||
cvi.put(key, res);
|
||||
return (cc, tt) -> res;
|
||||
}
|
||||
}
|
||||
v1.assertAssignable();
|
||||
String varname = v1.getVariable();
|
||||
@@ -1114,7 +1120,7 @@ public class Expression
|
||||
{
|
||||
Value rval= lv.get(0).evalValue(c);
|
||||
if (!(rval instanceof AbstractListValue))
|
||||
throw new InternalExpressionException("Second argument of 'for' function should be a list or iterator");
|
||||
throw new InternalExpressionException("First argument of 'for' function should be a list or iterator");
|
||||
Iterator<Value> iterator = ((AbstractListValue) rval).iterator();
|
||||
LazyValue expr = lv.get(1);
|
||||
//scoping
|
||||
@@ -1558,7 +1564,7 @@ public class Expression
|
||||
if (lv.size() == 1)
|
||||
return new StringValue(format);
|
||||
int argIndex = 1;
|
||||
if (lv.get(1) instanceof ListValue)
|
||||
if (lv.get(1) instanceof ListValue && lv.size() == 2)
|
||||
{
|
||||
lv = ((ListValue) lv.get(1)).getItems();
|
||||
argIndex = 0;
|
||||
@@ -1861,26 +1867,10 @@ public class Expression
|
||||
addLazyFunctionWithDelegation("task", -1, (c, t, expr, tok, lv) -> {
|
||||
if (lv.size() == 0)
|
||||
throw new InternalExpressionException("'task' requires at least function to call as a parameter");
|
||||
Value funcDesc = lv.get(0).evalValue(c);
|
||||
if (!(funcDesc instanceof FunctionValue))
|
||||
{
|
||||
String name = funcDesc.getString();
|
||||
funcDesc = c.host.getAssertFunction(module, name);
|
||||
}
|
||||
FunctionValue fun = (FunctionValue)funcDesc;
|
||||
int extraargs = lv.size() - fun.getArguments().size();
|
||||
if (extraargs != 1 && extraargs != 2)
|
||||
{
|
||||
throw new InternalExpressionException("Function takes "+fun.getArguments().size()+" arguments.");
|
||||
}
|
||||
List<LazyValue> lvargs = new ArrayList<>();
|
||||
for (int i=0; i< fun.getArguments().size(); i++)
|
||||
{
|
||||
lvargs.add(lv.get(i+1));
|
||||
}
|
||||
FunctionArgument functionArgument = FunctionArgument.findIn(c, module, lv, 0, true, false);
|
||||
Value queue = Value.NULL;
|
||||
if (extraargs == 2) queue = lv.get(lv.size()-1).evalValue(c);
|
||||
ThreadValue thread = new ThreadValue(queue, fun, expr, tok, c, lvargs);
|
||||
if (lv.size() > functionArgument.offset) queue = lv.get(functionArgument.offset).evalValue(c);
|
||||
ThreadValue thread = new ThreadValue(queue, functionArgument.function, expr, tok, c, functionArgument.args);
|
||||
Thread.yield();
|
||||
return (cc, tt) -> thread;
|
||||
});
|
||||
@@ -1906,6 +1896,12 @@ public class Expression
|
||||
return ((ThreadValue) v).join();
|
||||
});
|
||||
|
||||
addLazyFunction("task_dock", 1, (c, t, lv) -> {
|
||||
// pass through placeholder
|
||||
// implmenetation should dock the task on the main thread.
|
||||
return lv.get(0);
|
||||
});
|
||||
|
||||
addUnaryFunction("task_completed", (v) -> {
|
||||
if (!(v instanceof ThreadValue))
|
||||
throw new InternalExpressionException("'task_completed' could only be used with a task value");
|
||||
|
||||
@@ -173,9 +173,9 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
}
|
||||
else if (nextChar == 't')
|
||||
{
|
||||
throw new ExpressionException(context, this.expression, token,
|
||||
"Tab character is not supported");
|
||||
//token.append('\t');
|
||||
//throw new ExpressionException(context, this.expression, token,
|
||||
// "Tab character is not supported");
|
||||
token.append('\t');
|
||||
}
|
||||
else if (nextChar == 'r')
|
||||
{
|
||||
@@ -201,6 +201,11 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
{
|
||||
token.append(input.charAt(pos++));
|
||||
linepos++;
|
||||
if (ch=='\n')
|
||||
{
|
||||
lineno++;
|
||||
linepos = 0;
|
||||
}
|
||||
if (pos == input.length() && expression != null && context != null)
|
||||
throw new ExpressionException(context, this.expression, token, "Program truncated");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package carpet.script.argument;
|
||||
|
||||
public abstract class Argument
|
||||
{
|
||||
public int offset;
|
||||
protected Argument(int offset)
|
||||
{
|
||||
this.offset = offset;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package carpet.script.argument;
|
||||
|
||||
import carpet.script.CarpetContext;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.value.BlockValue;
|
||||
import carpet.script.value.ListValue;
|
||||
import carpet.script.value.NullValue;
|
||||
import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BlockArgument extends Argument
|
||||
{
|
||||
public final BlockValue block;
|
||||
private BlockArgument(BlockValue b, int o)
|
||||
{
|
||||
super(o);
|
||||
block = b;
|
||||
}
|
||||
public static BlockArgument findIn(CarpetContext c, List<LazyValue> params, int offset)
|
||||
{
|
||||
return findIn(c, params,offset, false, false);
|
||||
}
|
||||
|
||||
public static BlockArgument findIn(CarpetContext c, List<LazyValue> params, int offset, boolean acceptString)
|
||||
{
|
||||
return findIn(c, params,offset, acceptString, false);
|
||||
}
|
||||
|
||||
public static BlockArgument findIn(CarpetContext c, List<LazyValue> params, int offset, boolean acceptString, boolean optional)
|
||||
{
|
||||
try
|
||||
{
|
||||
Value v1 = params.get(0 + offset).evalValue(c);
|
||||
//add conditional from string name
|
||||
if (optional && v1 instanceof NullValue)
|
||||
{
|
||||
return new BlockArgument(null, 1+offset);
|
||||
}
|
||||
if (acceptString && v1 instanceof StringValue)
|
||||
{
|
||||
return new BlockArgument(BlockValue.fromString(v1.getString()), 1+offset);
|
||||
}
|
||||
if (v1 instanceof BlockValue)
|
||||
{
|
||||
return new BlockArgument(((BlockValue) v1), 1+offset);
|
||||
}
|
||||
if (v1 instanceof ListValue)
|
||||
{
|
||||
List<Value> args = ((ListValue) v1).getItems();
|
||||
int xpos = (int) NumericValue.asNumber(args.get(0)).getLong();
|
||||
int ypos = (int) NumericValue.asNumber(args.get(1)).getLong();
|
||||
int zpos = (int) NumericValue.asNumber(args.get(2)).getLong();
|
||||
return new BlockArgument(
|
||||
new BlockValue(
|
||||
null,
|
||||
c.s.getWorld(),
|
||||
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
|
||||
),
|
||||
1+offset);
|
||||
}
|
||||
int xpos = (int) NumericValue.asNumber(v1).getLong();
|
||||
int ypos = (int) NumericValue.asNumber( params.get(1 + offset).evalValue(c)).getLong();
|
||||
int zpos = (int) NumericValue.asNumber( params.get(2 + offset).evalValue(c)).getLong();
|
||||
return new BlockArgument(
|
||||
new BlockValue(
|
||||
null,
|
||||
c.s.getWorld(),
|
||||
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
|
||||
),
|
||||
3+offset
|
||||
);
|
||||
}
|
||||
catch (IndexOutOfBoundsException e)
|
||||
{
|
||||
throw handleError(optional, acceptString);
|
||||
}
|
||||
}
|
||||
|
||||
public static BlockArgument findInValues(CarpetContext c, List<Value> params, int offset, boolean acceptString, boolean optional)
|
||||
{
|
||||
try
|
||||
{
|
||||
Value v1 = params.get(0 + offset);
|
||||
//add conditional from string name
|
||||
if (optional && v1 instanceof NullValue)
|
||||
{
|
||||
return new BlockArgument(null, 1+offset);
|
||||
}
|
||||
if (acceptString && v1 instanceof StringValue)
|
||||
{
|
||||
return new BlockArgument(BlockValue.fromString(v1.getString()), 1+offset);
|
||||
}
|
||||
if (v1 instanceof BlockValue)
|
||||
{
|
||||
return new BlockArgument(((BlockValue) v1), 1+offset);
|
||||
}
|
||||
if (v1 instanceof ListValue)
|
||||
{
|
||||
List<Value> args = ((ListValue) v1).getItems();
|
||||
int xpos = (int) NumericValue.asNumber(args.get(0)).getLong();
|
||||
int ypos = (int) NumericValue.asNumber(args.get(1)).getLong();
|
||||
int zpos = (int) NumericValue.asNumber(args.get(2)).getLong();
|
||||
return new BlockArgument(
|
||||
new BlockValue(
|
||||
null,
|
||||
c.s.getWorld(),
|
||||
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
|
||||
),
|
||||
1+offset);
|
||||
}
|
||||
int xpos = (int) NumericValue.asNumber(v1).getLong();
|
||||
int ypos = (int) NumericValue.asNumber( params.get(1 + offset)).getLong();
|
||||
int zpos = (int) NumericValue.asNumber( params.get(2 + offset)).getLong();
|
||||
return new BlockArgument(
|
||||
new BlockValue(
|
||||
null,
|
||||
c.s.getWorld(),
|
||||
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
|
||||
),
|
||||
3+offset
|
||||
);
|
||||
}
|
||||
catch (IndexOutOfBoundsException e)
|
||||
{
|
||||
throw handleError(optional, acceptString);
|
||||
}
|
||||
}
|
||||
|
||||
private static InternalExpressionException handleError(boolean optional, boolean acceptString)
|
||||
{
|
||||
String message = "Block-type argument should be defined either by three coordinates (a triple or by three arguments), or a block value";
|
||||
if (acceptString)
|
||||
message+=", or a string with block description";
|
||||
if (optional)
|
||||
message+=", or null";
|
||||
return new InternalExpressionException(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package carpet.script.argument;
|
||||
|
||||
import carpet.script.CarpetContext;
|
||||
import carpet.script.Context;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.bundled.Module;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.value.FunctionValue;
|
||||
import carpet.script.value.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FunctionArgument extends Argument
|
||||
{
|
||||
public FunctionValue function;
|
||||
public List<LazyValue> args;
|
||||
|
||||
private FunctionArgument(FunctionValue function, int offset, List<LazyValue> args)
|
||||
{
|
||||
super(offset);
|
||||
this.function = function;
|
||||
this.args = args;
|
||||
}
|
||||
public static FunctionArgument findIn(Context c, Module module, List<LazyValue> params, int offset, boolean prematureEvaluation, boolean strict)
|
||||
{
|
||||
Value functionValue = params.get(offset).evalValue(c);
|
||||
if (!(functionValue instanceof FunctionValue))
|
||||
{
|
||||
String name = functionValue.getString();
|
||||
functionValue = c.host.getAssertFunction(module, name);
|
||||
}
|
||||
FunctionValue fun = (FunctionValue)functionValue;
|
||||
int argsize = fun.getArguments().size();
|
||||
int extraargs = params.size() - argsize - offset - 1;
|
||||
if (extraargs < 0 || (extraargs > 0 && strict))
|
||||
throw new InternalExpressionException("Function "+fun.getPrettyString()+" requires "+fun.getArguments().size()+" arguments");
|
||||
List<LazyValue> lvargs = new ArrayList<>();
|
||||
if (prematureEvaluation)
|
||||
{
|
||||
for (int i = 0; i < argsize; i++)
|
||||
{
|
||||
Value arg = params.get(offset + 1 + i).evalValue(c);
|
||||
lvargs.add((cc, tt) -> arg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < argsize; i++) lvargs.add(params.get(offset + 1 + i));
|
||||
}
|
||||
return new FunctionArgument(fun, offset+1+argsize, lvargs);
|
||||
}
|
||||
|
||||
public List<Value> resolveArgs(Context context, Integer type)
|
||||
{
|
||||
List<Value> res = new ArrayList<>(args.size());
|
||||
for (LazyValue arg : args) res.add(arg.evalValue(context, type));
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package carpet.script.argument;
|
||||
|
||||
import carpet.script.CarpetContext;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.value.BlockValue;
|
||||
import carpet.script.value.ListValue;
|
||||
import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.Value;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Vector3Argument extends Argument
|
||||
{
|
||||
public Vec3d vec;
|
||||
public final double yaw;
|
||||
public final double pitch;
|
||||
public boolean fromBlock;
|
||||
private Vector3Argument(Vec3d v, int o)
|
||||
{
|
||||
super(o);
|
||||
this.vec = v;
|
||||
this.yaw = 0.0D;
|
||||
this.pitch = 0.0D;
|
||||
}
|
||||
private Vector3Argument(Vec3d v, int o, double y, double p)
|
||||
{
|
||||
super(o);
|
||||
this.vec = v;
|
||||
this.yaw = y;
|
||||
this.pitch = p;
|
||||
}
|
||||
|
||||
private Vector3Argument fromBlock()
|
||||
{
|
||||
fromBlock = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public static Vector3Argument findIn(CarpetContext c, List<LazyValue> params, int offset)
|
||||
{
|
||||
return findIn(c,params, offset, false);
|
||||
}
|
||||
|
||||
public static Vector3Argument findIn(CarpetContext c, List<LazyValue> params, int offset, boolean optionalDirection)
|
||||
{
|
||||
try
|
||||
{
|
||||
Value v1 = params.get(0 + offset).evalValue(c);
|
||||
if (v1 instanceof BlockValue)
|
||||
{
|
||||
return (new Vector3Argument(new Vec3d(((BlockValue) v1).getPos()).add(0.5,0.5,0.5), 1+offset)).fromBlock();
|
||||
}
|
||||
if (v1 instanceof ListValue)
|
||||
{
|
||||
List<Value> args = ((ListValue) v1).getItems();
|
||||
Vec3d pos = new Vec3d(
|
||||
NumericValue.asNumber(args.get(0)).getDouble(),
|
||||
NumericValue.asNumber(args.get(1)).getDouble(),
|
||||
NumericValue.asNumber(args.get(2)).getDouble());
|
||||
double yaw = 0.0D;
|
||||
double pitch = 0.0D;
|
||||
if (args.size()>3 && optionalDirection)
|
||||
{
|
||||
yaw = NumericValue.asNumber(args.get(3)).getDouble();
|
||||
pitch = NumericValue.asNumber(args.get(4)).getDouble();
|
||||
}
|
||||
return new Vector3Argument(pos,offset+1, yaw, pitch);
|
||||
}
|
||||
Vec3d pos = new Vec3d(
|
||||
NumericValue.asNumber(v1).getDouble(),
|
||||
NumericValue.asNumber(params.get(1 + offset).evalValue(c)).getDouble(),
|
||||
NumericValue.asNumber(params.get(2 + offset).evalValue(c)).getDouble());
|
||||
double yaw = 0.0D;
|
||||
double pitch = 0.0D;
|
||||
int eatenLength = 3;
|
||||
if (params.size()>3+offset && optionalDirection)
|
||||
{
|
||||
yaw = NumericValue.asNumber(params.get(3 + offset).evalValue(c)).getDouble();
|
||||
pitch = NumericValue.asNumber(params.get(4 + offset).evalValue(c)).getDouble();
|
||||
eatenLength = 5;
|
||||
}
|
||||
|
||||
return new Vector3Argument(pos,offset+eatenLength, yaw, pitch);
|
||||
}
|
||||
catch (IndexOutOfBoundsException e)
|
||||
{
|
||||
throw new InternalExpressionException("Position argument should be defined either by three coordinates (a triple or by three arguments), or a positioned block value");
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector3Argument findIn(List<Value> params, int offset, boolean optionalDirection)
|
||||
{
|
||||
try
|
||||
{
|
||||
Value v1 = params.get(0 + offset);
|
||||
if (v1 instanceof BlockValue)
|
||||
{
|
||||
return (new Vector3Argument(new Vec3d(((BlockValue) v1).getPos()).add(0.5,0.5,0.5), 1+offset)).fromBlock();
|
||||
}
|
||||
if (v1 instanceof ListValue)
|
||||
{
|
||||
List<Value> args = ((ListValue) v1).getItems();
|
||||
Vec3d pos = new Vec3d(
|
||||
NumericValue.asNumber(args.get(0)).getDouble(),
|
||||
NumericValue.asNumber(args.get(1)).getDouble(),
|
||||
NumericValue.asNumber(args.get(2)).getDouble());
|
||||
double yaw = 0.0D;
|
||||
double pitch = 0.0D;
|
||||
if (args.size()>3 && optionalDirection)
|
||||
{
|
||||
yaw = NumericValue.asNumber(args.get(3)).getDouble();
|
||||
pitch = NumericValue.asNumber(args.get(4)).getDouble();
|
||||
}
|
||||
return new Vector3Argument(pos,offset+1, yaw, pitch);
|
||||
}
|
||||
Vec3d pos = new Vec3d(
|
||||
NumericValue.asNumber(v1).getDouble(),
|
||||
NumericValue.asNumber(params.get(1 + offset)).getDouble(),
|
||||
NumericValue.asNumber(params.get(2 + offset)).getDouble());
|
||||
double yaw = 0.0D;
|
||||
double pitch = 0.0D;
|
||||
int eatenLength = 3;
|
||||
if (params.size()>3+offset && optionalDirection)
|
||||
{
|
||||
yaw = NumericValue.asNumber(params.get(3 + offset)).getDouble();
|
||||
pitch = NumericValue.asNumber(params.get(4 + offset)).getDouble();
|
||||
eatenLength = 5;
|
||||
}
|
||||
|
||||
return new Vector3Argument(pos,offset+eatenLength, yaw, pitch);
|
||||
}
|
||||
catch (IndexOutOfBoundsException e)
|
||||
{
|
||||
throw new InternalExpressionException("Position argument should be defined either by three coordinates (a triple or by three arguments), or a positioned block value");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,24 @@
|
||||
package carpet.script.bundled;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import net.minecraft.nbt.PositionTracker;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.nbt.TagReaders;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class FileModule extends Module
|
||||
@@ -74,7 +81,7 @@ public class FileModule extends Module
|
||||
}
|
||||
|
||||
//copied private method from net.minecraft.nbt.NbtIo.write() and client method safe_write
|
||||
public static void write(Tag tag_1, File file)
|
||||
public static boolean write(Tag tag_1, File file)
|
||||
{
|
||||
File file_2 = new File(file.getAbsolutePath() + "_tmp");
|
||||
if (file_2.exists()) file_2.delete();
|
||||
@@ -90,9 +97,50 @@ public class FileModule extends Module
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (file.exists()) file.delete();
|
||||
if (!file.exists()) file_2.renameTo(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean appendText(File filePath, boolean addNewLines, List<String> data)
|
||||
{
|
||||
try
|
||||
{
|
||||
OutputStream out = Files.newOutputStream(filePath.toPath(), StandardOpenOption.APPEND, StandardOpenOption.CREATE);
|
||||
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)))
|
||||
{
|
||||
for (String line: data)
|
||||
{
|
||||
writer.append(line);
|
||||
if (addNewLines) writer.newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static List<String> listFileContent(File filePath)
|
||||
{
|
||||
try (BufferedReader reader = Files.newBufferedReader(filePath.toPath(), StandardCharsets.UTF_8)) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (;;) {
|
||||
String line = reader.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
result.add(line.replaceAll("[\n\r]+",""));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,42 +4,76 @@ import carpet.CarpetServer;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import static carpet.script.bundled.FileModule.read;
|
||||
import static carpet.script.bundled.FileModule.write;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Module
|
||||
{
|
||||
public abstract String getName();
|
||||
public abstract String getCode();
|
||||
public abstract boolean isLibrary();
|
||||
public final static Object writeIOSync = new Object();
|
||||
|
||||
public Tag getData(String file)
|
||||
private static String getDescriptor(Module module, String file, boolean isShared)
|
||||
{
|
||||
File dataFile = CarpetServer.minecraft_server.getLevelStorage().resolveFile(
|
||||
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt");
|
||||
if (!Files.exists(dataFile.toPath()) || !(dataFile.isFile())) return null;
|
||||
return read(dataFile);
|
||||
if (isShared)
|
||||
{
|
||||
return "shared/"+file;
|
||||
}
|
||||
else if (module != null && module.getName() != null) // appdata
|
||||
{
|
||||
return module.getName()+".data"+(file==null?"":"/"+file);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException("Invalid file descriptor: "+file);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveData(String file, Tag globalState)
|
||||
public static Tag getData(Module module, String file, boolean isShared)
|
||||
{
|
||||
File dataFile =CarpetServer.minecraft_server.getLevelStorage().resolveFile(
|
||||
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt");
|
||||
if (!Files.exists(dataFile.toPath().getParent()))
|
||||
{
|
||||
try
|
||||
{
|
||||
Files.createDirectory(dataFile.toPath().getParent());
|
||||
}
|
||||
catch (IOException ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
write(globalState, dataFile);
|
||||
File dataFile = resolveResource(module, file, "nbt", isShared);
|
||||
if (dataFile == null) return null;
|
||||
if (!Files.exists(dataFile.toPath()) || !(dataFile.isFile())) return null;
|
||||
synchronized (writeIOSync) { return FileModule.read(dataFile); }
|
||||
}
|
||||
|
||||
public static boolean saveData(Module module, String file, Tag globalState, boolean isShared)
|
||||
{
|
||||
File dataFile = resolveResource(module, file, "nbt", isShared);
|
||||
if (dataFile == null) return false;
|
||||
if (!Files.exists(dataFile.toPath().getParent()) && !dataFile.getParentFile().mkdirs()) return false;
|
||||
synchronized (writeIOSync) { return FileModule.write(globalState, dataFile); }
|
||||
}
|
||||
|
||||
public static boolean appendToTextFile(Module module, String resourceName, String type, boolean isShared, List<String> message)
|
||||
{
|
||||
File dataFile = resolveResource(module, resourceName, "txt", isShared);
|
||||
if (dataFile == null) return false;
|
||||
if (!Files.exists(dataFile.toPath().getParent()) && !dataFile.getParentFile().mkdirs()) return false;
|
||||
synchronized (writeIOSync) { return FileModule.appendText(dataFile, type.equals("text"), message); }
|
||||
}
|
||||
|
||||
public static boolean dropExistingFile(Module module, String resourceName, String ext, boolean isShared)
|
||||
{
|
||||
File dataFile = resolveResource(module, resourceName, ext, isShared);
|
||||
if (dataFile == null) return false;
|
||||
synchronized (writeIOSync) { return dataFile.delete(); }
|
||||
}
|
||||
|
||||
public static List<String> listFile(Module module, String resourceName, String ext, boolean isShared)
|
||||
{
|
||||
File dataFile = resolveResource(module, resourceName, ext, isShared);
|
||||
if (dataFile == null) return null;
|
||||
if (!dataFile.exists()) return null;
|
||||
synchronized (writeIOSync) { return FileModule.listFileContent(dataFile); }
|
||||
}
|
||||
|
||||
private static File resolveResource(Module module, String resourceName, String ext, boolean isShared)
|
||||
{
|
||||
if (!isShared && (module == null || module.getName() == null)) return null;
|
||||
return CarpetServer.minecraft_server.getLevelStorage().resolveFile(
|
||||
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getDescriptor(module, resourceName, isShared)+"."+ext);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,790 @@
|
||||
package carpet.script.utils;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.BufferBuilder;
|
||||
import net.minecraft.client.render.Camera;
|
||||
import net.minecraft.client.render.Tessellator;
|
||||
import net.minecraft.client.render.VertexFormats;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class ShapesRenderer
|
||||
{
|
||||
private final Map<DimensionType, Long2ObjectOpenHashMap<RenderedShape<? extends ShapeDispatcher.ExpiringShape>>> shapes;
|
||||
private MinecraftClient client;
|
||||
|
||||
private Map<String, BiFunction<MinecraftClient, ShapeDispatcher.ExpiringShape, RenderedShape<? extends ShapeDispatcher.ExpiringShape >>> renderedShapes
|
||||
= new HashMap<String, BiFunction<MinecraftClient, ShapeDispatcher.ExpiringShape, RenderedShape<? extends ShapeDispatcher.ExpiringShape>>>()
|
||||
{{
|
||||
put("line", RenderedLine::new);
|
||||
put("box", RenderedBox::new);
|
||||
put("sphere", RenderedSphere::new);
|
||||
put("cylinder", RenderedCylinder::new);
|
||||
}};
|
||||
|
||||
public ShapesRenderer(MinecraftClient minecraftClient)
|
||||
{
|
||||
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;
|
||||
long currentTime = client.world.getTime();
|
||||
RenderSystem.disableTexture();
|
||||
RenderSystem.enableDepthTest();
|
||||
RenderSystem.depthFunc(515);
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.defaultBlendFunc();
|
||||
//RenderSystem.blendFuncSeparate(GlStateManager.SrcFactor.SRC_ALPHA, GlStateManager.DstFactor.ONE, GlStateManager.SrcFactor.ONE, GlStateManager.DstFactor.ZERO);
|
||||
//RenderSystem.shadeModel(7425);
|
||||
RenderSystem.shadeModel(GL11.GL_FLAT);
|
||||
RenderSystem.enableAlphaTest();
|
||||
RenderSystem.defaultAlphaFunc();
|
||||
//RenderSystem.alphaFunc(GL11.GL_GREATER, 0.005F);
|
||||
RenderSystem.disableCull();
|
||||
RenderSystem.disableLighting();
|
||||
RenderSystem.depthMask(false);
|
||||
//RenderSystem.polygonOffset(-3f, -3f);
|
||||
//RenderSystem.enablePolygonOffset();
|
||||
//Entity entity = this.client.gameRenderer.getCamera().getFocusedEntity();
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder bufferBuilder = tessellator.getBuffer();
|
||||
|
||||
// render
|
||||
double cameraX = camera.getPos().x;
|
||||
double cameraY = camera.getPos().y;
|
||||
double cameraZ = camera.getPos().z;
|
||||
|
||||
synchronized (shapes)
|
||||
{
|
||||
shapes.get(dimensionType).long2ObjectEntrySet().removeIf(
|
||||
entry -> entry.getValue().isExpired(currentTime)
|
||||
);
|
||||
shapes.get(dimensionType).values().forEach(
|
||||
s ->
|
||||
{
|
||||
if ( s.shouldRender(dimensionType))
|
||||
s.renderFaces(tessellator, bufferBuilder, cameraX, cameraY, cameraZ, partialTick);
|
||||
}
|
||||
);
|
||||
//lines
|
||||
shapes.get(dimensionType).values().forEach(
|
||||
|
||||
s -> {
|
||||
if ( s.shouldRender(dimensionType))
|
||||
s.renderLines(tessellator, bufferBuilder, cameraX, cameraY, cameraZ, partialTick);
|
||||
}
|
||||
);
|
||||
}
|
||||
RenderSystem.enableCull();
|
||||
RenderSystem.depthMask(true);
|
||||
RenderSystem.lineWidth(1.0F);
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.defaultBlendFunc();
|
||||
RenderSystem.enableTexture();
|
||||
RenderSystem.shadeModel(7424);
|
||||
}
|
||||
|
||||
public void addShapes(ListTag tag)
|
||||
{
|
||||
for (int i=0, count = tag.size(); i < count; i++)
|
||||
{
|
||||
addShape(tag.getCompound(i));
|
||||
}
|
||||
}
|
||||
|
||||
public void addShape(CompoundTag tag)
|
||||
{
|
||||
ShapeDispatcher.ExpiringShape shape = ShapeDispatcher.fromTag(tag);
|
||||
if (shape == null) return;
|
||||
BiFunction<MinecraftClient, ShapeDispatcher.ExpiringShape, RenderedShape<? extends ShapeDispatcher.ExpiringShape >> shapeFactory;
|
||||
shapeFactory = renderedShapes.get(tag.getString("shape"));
|
||||
if (shapeFactory == null)
|
||||
{
|
||||
CarpetSettings.LOG.info("Unrecognized shape: "+tag.getString("shape"));
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderedShape<?> rshape = shapeFactory.apply(client, shape);
|
||||
DimensionType dim = shape.shapeDimension;
|
||||
long key = rshape.key();
|
||||
synchronized (shapes)
|
||||
{
|
||||
RenderedShape<?> existing = shapes.get(dim).get(key);
|
||||
if (existing != null)
|
||||
{ // promoting previous shape
|
||||
existing.expiryTick = rshape.expiryTick;
|
||||
}
|
||||
else
|
||||
{
|
||||
shapes.get(dim).put(key, rshape);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public void reset()
|
||||
{
|
||||
synchronized (shapes)
|
||||
{
|
||||
shapes.values().forEach(Long2ObjectOpenHashMap::clear);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public abstract static class RenderedShape<T extends ShapeDispatcher.ExpiringShape>
|
||||
{
|
||||
protected T shape;
|
||||
protected MinecraftClient client;
|
||||
long expiryTick;
|
||||
double renderEpsilon = 0;
|
||||
public abstract void renderLines(Tessellator tessellator, BufferBuilder builder, double cx, double cy, double cz, float partialTick );
|
||||
public void renderFaces(Tessellator tessellator, BufferBuilder builder, double cx, double cy, double cz, float partialTick ) {}
|
||||
protected RenderedShape(MinecraftClient client, T shape)
|
||||
{
|
||||
this.shape = shape;
|
||||
expiryTick = client.world.getTime()+shape.getExpiry();
|
||||
renderEpsilon = (3+((double)shape.key())/Long.MAX_VALUE)/1000;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public boolean isExpired(long currentTick)
|
||||
{
|
||||
return expiryTick < currentTick;
|
||||
}
|
||||
public long key()
|
||||
{
|
||||
return shape.key();
|
||||
};
|
||||
public boolean shouldRender(DimensionType dim)
|
||||
{
|
||||
if (shape.followEntity <=0 ) return true;
|
||||
if (client.world == null) return false;
|
||||
if (client.world.getEntityById(shape.followEntity) == null) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RenderedBox extends RenderedShape<ShapeDispatcher.Box>
|
||||
{
|
||||
|
||||
private RenderedBox(MinecraftClient client, ShapeDispatcher.ExpiringShape shape)
|
||||
{
|
||||
super(client, (ShapeDispatcher.Box)shape);
|
||||
|
||||
}
|
||||
@Override
|
||||
public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick)
|
||||
{
|
||||
if (shape.a == 0.0) return;
|
||||
Vec3d v1 = shape.relativiseRender(client.world, shape.from, partialTick);
|
||||
Vec3d v2 = shape.relativiseRender(client.world, shape.to, partialTick);
|
||||
RenderSystem.lineWidth(shape.lineWidth);
|
||||
drawBoxWireGLLines(tessellator, bufferBuilder,
|
||||
(float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon),
|
||||
(float)(v2.x-cx+renderEpsilon), (float)(v2.y-cy+renderEpsilon), (float)(v2.z-cz+renderEpsilon),
|
||||
v1.x!=v2.x, v1.y!=v2.y, v1.z!=v2.z,
|
||||
shape.r, shape.g, shape.b, shape.a, shape.r, shape.g, shape.b
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public void renderFaces(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick)
|
||||
{
|
||||
if (shape.fa == 0.0) return;
|
||||
Vec3d v1 = shape.relativiseRender(client.world, shape.from, partialTick);
|
||||
Vec3d v2 = shape.relativiseRender(client.world, shape.to, partialTick);
|
||||
RenderSystem.lineWidth(1.0F);
|
||||
drawBoxFaces(tessellator, bufferBuilder,
|
||||
(float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon),
|
||||
(float)(v2.x-cx+renderEpsilon), (float)(v2.y-cy+renderEpsilon), (float)(v2.z-cz+renderEpsilon),
|
||||
v1.x!=v2.x, v1.y!=v2.y, v1.z!=v2.z,
|
||||
shape.fr, shape.fg, shape.fb, shape.fa
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class RenderedLine extends RenderedShape<ShapeDispatcher.Line>
|
||||
{
|
||||
public RenderedLine(MinecraftClient client, ShapeDispatcher.ExpiringShape shape)
|
||||
{
|
||||
super(client, (ShapeDispatcher.Line)shape);
|
||||
}
|
||||
@Override
|
||||
public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick)
|
||||
{
|
||||
Vec3d v1 = shape.relativiseRender(client.world, shape.from, partialTick);
|
||||
Vec3d v2 = shape.relativiseRender(client.world, shape.to, partialTick);
|
||||
RenderSystem.lineWidth(shape.lineWidth);
|
||||
drawLine(tessellator, bufferBuilder,
|
||||
(float)(v1.x-cx-renderEpsilon), (float)(v1.y-cy-renderEpsilon), (float)(v1.z-cz-renderEpsilon),
|
||||
(float)(v2.x-cx+renderEpsilon), (float)(v2.y-cy+renderEpsilon), (float)(v2.z-cz+renderEpsilon),
|
||||
shape.r, shape.g, shape.b, shape.a
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static class RenderedSphere extends RenderedShape<ShapeDispatcher.Sphere>
|
||||
{
|
||||
public RenderedSphere(MinecraftClient client, ShapeDispatcher.ExpiringShape shape)
|
||||
{
|
||||
super(client, (ShapeDispatcher.Sphere)shape);
|
||||
}
|
||||
@Override
|
||||
public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick)
|
||||
{
|
||||
if (shape.a == 0.0) return;
|
||||
Vec3d vc = shape.relativiseRender(client.world, shape.center, partialTick);
|
||||
RenderSystem.lineWidth(shape.lineWidth);
|
||||
drawSphereWireframe(tessellator, bufferBuilder,
|
||||
(float)(vc.x-cx), (float)(vc.y-cy), (float)(vc.z-cz),
|
||||
(float)(shape.radius+renderEpsilon), shape.subdivisions,
|
||||
shape.r, shape.g, shape.b, shape.a);
|
||||
}
|
||||
@Override
|
||||
public void renderFaces(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick)
|
||||
{
|
||||
if (shape.fa == 0.0) return;
|
||||
Vec3d vc = shape.relativiseRender(client.world, shape.center, partialTick);
|
||||
RenderSystem.lineWidth(1.0f);
|
||||
drawSphereFaces(tessellator, bufferBuilder,
|
||||
(float)(vc.x-cx), (float)(vc.y-cy), (float)(vc.z-cz),
|
||||
(float)(shape.radius+renderEpsilon), shape.subdivisions,
|
||||
shape.fr, shape.fg, shape.fb, shape.fa);
|
||||
}
|
||||
}
|
||||
|
||||
public static class RenderedCylinder extends RenderedShape<ShapeDispatcher.Cylinder>
|
||||
{
|
||||
public RenderedCylinder(MinecraftClient client, ShapeDispatcher.ExpiringShape shape)
|
||||
{
|
||||
super(client, (ShapeDispatcher.Cylinder)shape);
|
||||
}
|
||||
@Override
|
||||
public void renderLines(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick)
|
||||
{
|
||||
if (shape.a == 0.0) return;
|
||||
Vec3d vc = shape.relativiseRender(client.world, shape.center, partialTick);
|
||||
RenderSystem.lineWidth(shape.lineWidth);
|
||||
double dir = MathHelper.sign(shape.height);
|
||||
drawCylinderWireframe(tessellator, bufferBuilder,
|
||||
(float) (vc.x - cx - dir * renderEpsilon), (float) (vc.y - cy - dir * renderEpsilon), (float) (vc.z - cz - dir * renderEpsilon),
|
||||
(float) (shape.radius + renderEpsilon), (float) (shape.height + 2*dir*renderEpsilon), shape.axis,
|
||||
shape.subdivisions, shape.radius == 0,
|
||||
shape.r, shape.g, shape.b, shape.a);
|
||||
|
||||
}
|
||||
@Override
|
||||
public void renderFaces(Tessellator tessellator, BufferBuilder bufferBuilder, double cx, double cy, double cz, float partialTick)
|
||||
{
|
||||
if (shape.fa == 0.0) return;
|
||||
Vec3d vc = shape.relativiseRender(client.world, shape.center, partialTick);
|
||||
RenderSystem.lineWidth(1.0f);
|
||||
double dir = MathHelper.sign(shape.height);
|
||||
drawCylinderFaces(tessellator, bufferBuilder,
|
||||
(float) (vc.x - cx - dir * renderEpsilon), (float) (vc.y - cy - dir * renderEpsilon), (float) (vc.z - cz - dir * renderEpsilon),
|
||||
(float) (shape.radius + renderEpsilon), (float) (shape.height + 2*dir*renderEpsilon), shape.axis,
|
||||
shape.subdivisions, shape.radius == 0,
|
||||
shape.fr, shape.fg, shape.fb, shape.fa);
|
||||
}
|
||||
}
|
||||
|
||||
// some raw shit
|
||||
|
||||
public static void drawLine(Tessellator tessellator, BufferBuilder builder, float x1, float y1, float z1, float x2, float y2, float z2, float red1, float grn1, float blu1, float alpha) {
|
||||
builder.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR); // 3
|
||||
builder.vertex(x1, y1, z1).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next();
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
public static void drawBoxWireGLLines(
|
||||
Tessellator tessellator, BufferBuilder builder,
|
||||
float x1, float y1, float z1,
|
||||
float x2, float y2, float z2,
|
||||
boolean xthick, boolean ythick, boolean zthick,
|
||||
float red1, float grn1, float blu1, float alpha, float red2, float grn2, float blu2)
|
||||
{
|
||||
builder.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR); // 3
|
||||
if (xthick)
|
||||
{
|
||||
builder.vertex(x1, y1, z1).color(red1, grn2, blu2, alpha).next();
|
||||
builder.vertex(x2, y1, z1).color(red1, grn2, blu2, alpha).next();
|
||||
|
||||
builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next();
|
||||
|
||||
builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next();
|
||||
|
||||
builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next();
|
||||
}
|
||||
if (ythick)
|
||||
{
|
||||
builder.vertex(x1, y1, z1).color(red2, grn1, blu2, alpha).next();
|
||||
builder.vertex(x1, y2, z1).color(red2, grn1, blu2, alpha).next();
|
||||
|
||||
builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next();
|
||||
|
||||
builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next();
|
||||
|
||||
builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next();
|
||||
}
|
||||
if (zthick)
|
||||
{
|
||||
builder.vertex(x1, y1, z1).color(red2, grn2, blu1, alpha).next();
|
||||
builder.vertex(x1, y1, z2).color(red2, grn2, blu1, alpha).next();
|
||||
|
||||
builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next();
|
||||
|
||||
builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next();
|
||||
|
||||
builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
public static void drawBoxFaces(
|
||||
Tessellator tessellator, BufferBuilder builder,
|
||||
float x1, float y1, float z1,
|
||||
float x2, float y2, float z2,
|
||||
boolean xthick, boolean ythick, boolean zthick,
|
||||
float red1, float grn1, float blu1, float alpha)
|
||||
{
|
||||
builder.begin(GL11.GL_QUADS, VertexFormats.POSITION_COLOR);
|
||||
|
||||
if (xthick && ythick)
|
||||
{
|
||||
builder.vertex(x1, y1, z1).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next();
|
||||
if (zthick)
|
||||
{
|
||||
builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (zthick && ythick)
|
||||
{
|
||||
builder.vertex(x1, y1, z1).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next();
|
||||
|
||||
if (xthick)
|
||||
{
|
||||
builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next();
|
||||
}
|
||||
}
|
||||
|
||||
// now at least drawing one
|
||||
if (zthick && xthick)
|
||||
{
|
||||
builder.vertex(x1, y1, z1).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y1, z1).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y1, z2).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x1, y1, z2).color(red1, grn1, blu1, alpha).next();
|
||||
|
||||
|
||||
if (ythick)
|
||||
{
|
||||
builder.vertex(x1, y2, z1).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y2, z1).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x2, y2, z2).color(red1, grn1, blu1, alpha).next();
|
||||
builder.vertex(x1, y2, z2).color(red1, grn1, blu1, alpha).next();
|
||||
}
|
||||
}
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
public static void drawCylinderWireframe(Tessellator tessellator, BufferBuilder builder,
|
||||
float cx, float cy, float cz,
|
||||
float r, float h, Direction.Axis axis, int subd, boolean isFlat,
|
||||
float red, float grn, float blu, float alpha)
|
||||
{
|
||||
float step = (float)Math.PI / (subd/2);
|
||||
int num_steps180 = (int)(Math.PI / step)+1;
|
||||
int num_steps360 = (int)(2*Math.PI / step);
|
||||
int hsteps = 1;
|
||||
float hstep = 1.0f;
|
||||
if (!isFlat)
|
||||
{
|
||||
hsteps = (int) Math.ceil(MathHelper.abs(h) / (step * r)) + 1;
|
||||
hstep = h / (hsteps - 1);
|
||||
}// draw base
|
||||
|
||||
if (axis == Direction.Axis.Y)
|
||||
{
|
||||
for (int dh = 0; dh < hsteps; dh++)
|
||||
{
|
||||
float hh = dh*hstep;
|
||||
builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR);
|
||||
for (int i = 0; i <= num_steps360; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float x = r * MathHelper.cos(theta);
|
||||
float y = hh;
|
||||
float z = r * MathHelper.sin(theta);
|
||||
builder.vertex(x + cx, y + cy, z + cz).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
if (!isFlat)
|
||||
{
|
||||
for (int i = 0; i <= num_steps180; i++)
|
||||
{
|
||||
builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR);
|
||||
float theta = step * i;
|
||||
float x = r * MathHelper.cos(theta);
|
||||
|
||||
float z = r * MathHelper.sin(theta);
|
||||
|
||||
builder.vertex(cx - x, cy + 0, cz + z).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(cx + x, cy + 0, cz - z).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(cx + x, cy + h, cz - z).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(cx - x, cy + h, cz + z).color(red, grn, blu, alpha).next();
|
||||
tessellator.draw();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR);
|
||||
for (int i = 0; i <= num_steps180; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float x = r * MathHelper.cos(theta);
|
||||
float z = r * MathHelper.sin(theta);
|
||||
builder.vertex(cx - x, cy, cz + z).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(cx + x, cy, cz - z).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
}
|
||||
else if (axis == Direction.Axis.X)
|
||||
{
|
||||
for (int dh = 0; dh < hsteps; dh++)
|
||||
{
|
||||
float hh = dh * hstep;
|
||||
builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR);
|
||||
for (int i = 0; i <= num_steps360; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float z = r * MathHelper.cos(theta);
|
||||
float x = hh;
|
||||
float y = r * MathHelper.sin(theta);
|
||||
builder.vertex(x + cx, y + cy, z + cz).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
if (!isFlat)
|
||||
{
|
||||
for (int i = 0; i <= num_steps180; i++)
|
||||
{
|
||||
builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR);
|
||||
float theta = step * i;
|
||||
float y = r * MathHelper.cos(theta);
|
||||
|
||||
float z = r * MathHelper.sin(theta);
|
||||
|
||||
builder.vertex(cx + 0, cy - y, cz + z).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(cx + 0, cy + y, cz - z).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(cx + h, cy + y, cz - z).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(cx + h, cy - y, cz + z).color(red, grn, blu, alpha).next();
|
||||
tessellator.draw();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR);
|
||||
for (int i = 0; i <= num_steps180; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float y = r * MathHelper.cos(theta);
|
||||
float z = r * MathHelper.sin(theta);
|
||||
builder.vertex(cx, cy - y, cz + z).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(cx, cy + y, cz - z).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
}
|
||||
}
|
||||
else if (axis == Direction.Axis.Z)
|
||||
{
|
||||
for (int dh = 0; dh < hsteps; dh++)
|
||||
{
|
||||
float hh = dh * hstep;
|
||||
builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR);
|
||||
for (int i = 0; i <= num_steps360; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float y = r * MathHelper.cos(theta);
|
||||
float z = hh;
|
||||
float x = r * MathHelper.sin(theta);
|
||||
builder.vertex(x + cx, y + cy, z + cz).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
}
|
||||
if (!isFlat)
|
||||
{
|
||||
for (int i = 0; i <= num_steps180; i++)
|
||||
{
|
||||
builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR);
|
||||
float theta = step * i;
|
||||
float x = r * MathHelper.cos(theta);
|
||||
|
||||
float y = r * MathHelper.sin(theta);
|
||||
|
||||
builder.vertex(cx + x, cy - y, cz + 0).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(cx - x, cy + y, cz + 0).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(cx - x, cy + y, cz + h).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(cx + x, cy - y, cz + h).color(red, grn, blu, alpha).next();
|
||||
tessellator.draw();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR);
|
||||
for (int i = 0; i <= num_steps180; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float x = r * MathHelper.cos(theta);
|
||||
float y = r * MathHelper.sin(theta);
|
||||
builder.vertex(cx + x, cy - y, cz).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(cx - x, cy + y, cz).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void drawCylinderFaces(Tessellator tessellator, BufferBuilder builder,
|
||||
float cx, float cy, float cz,
|
||||
float r, float h, Direction.Axis axis, int subd, boolean isFlat,
|
||||
float red, float grn, float blu, float alpha)
|
||||
{
|
||||
float step = (float)Math.PI / (subd/2);
|
||||
int num_steps180 = (int)(Math.PI / step)+1;
|
||||
int num_steps360 = (int)(2*Math.PI / step)+1;
|
||||
|
||||
if (axis == Direction.Axis.Y)
|
||||
{
|
||||
|
||||
builder.begin(GL11.GL_TRIANGLE_FAN, VertexFormats.POSITION_COLOR);
|
||||
builder.vertex(cx, cy, cz).color(red, grn, blu, alpha).next();
|
||||
for (int i = 0; i <= num_steps360; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float x = r * MathHelper.cos(theta);
|
||||
float z = r * MathHelper.sin(theta);
|
||||
builder.vertex(x + cx, cy, z + cz).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
if (!isFlat)
|
||||
{
|
||||
builder.begin(GL11.GL_TRIANGLE_FAN, VertexFormats.POSITION_COLOR);
|
||||
builder.vertex(cx, cy+h, cz).color(red, grn, blu, alpha).next();
|
||||
for (int i = 0; i <= num_steps360; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float x = r * MathHelper.cos(theta);
|
||||
float z = r * MathHelper.sin(theta);
|
||||
builder.vertex(x + cx, cy+h, z + cz).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
|
||||
builder.begin(GL11.GL_QUAD_STRIP, VertexFormats.POSITION_COLOR);
|
||||
for (int i = 0; i <= num_steps360; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float x = r * MathHelper.cos(theta);
|
||||
float z = r * MathHelper.sin(theta);
|
||||
builder.vertex(cx + x, cy + 0, cz + z).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(cx + x, cy + h, cz + z).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
}
|
||||
else if (axis == Direction.Axis.X)
|
||||
{
|
||||
builder.begin(GL11.GL_TRIANGLE_FAN, VertexFormats.POSITION_COLOR);
|
||||
builder.vertex(cx, cy, cz).color(red, grn, blu, alpha).next();
|
||||
for (int i = 0; i <= num_steps360; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float y = r * MathHelper.cos(theta);
|
||||
float z = r * MathHelper.sin(theta);
|
||||
builder.vertex(cx, cy + y, z + cz).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
if (!isFlat)
|
||||
{
|
||||
builder.begin(GL11.GL_TRIANGLE_FAN, VertexFormats.POSITION_COLOR);
|
||||
builder.vertex(cx+h, cy, cz).color(red, grn, blu, alpha).next();
|
||||
for (int i = 0; i <= num_steps360; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float y = r * MathHelper.cos(theta);
|
||||
float z = r * MathHelper.sin(theta);
|
||||
builder.vertex(cx+h, cy + y, cz + z).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
|
||||
builder.begin(GL11.GL_QUAD_STRIP, VertexFormats.POSITION_COLOR);
|
||||
for (int i = 0; i <= num_steps360; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float y = r * MathHelper.cos(theta);
|
||||
float z = r * MathHelper.sin(theta);
|
||||
builder.vertex(cx + 0, cy + y, cz + z).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(cx + h, cy + y, cz + z).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
}
|
||||
}
|
||||
else if (axis == Direction.Axis.Z)
|
||||
{
|
||||
builder.begin(GL11.GL_TRIANGLE_FAN, VertexFormats.POSITION_COLOR);
|
||||
builder.vertex(cx, cy, cz).color(red, grn, blu, alpha).next();
|
||||
for (int i = 0; i <= num_steps360; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float x = r * MathHelper.cos(theta);
|
||||
float y = r * MathHelper.sin(theta);
|
||||
builder.vertex(x + cx, cy+y, cz).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
if (!isFlat)
|
||||
{
|
||||
builder.begin(GL11.GL_TRIANGLE_FAN, VertexFormats.POSITION_COLOR);
|
||||
builder.vertex(cx, cy, cz+h).color(red, grn, blu, alpha).next();
|
||||
for (int i = 0; i <= num_steps360; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float x = r * MathHelper.cos(theta);
|
||||
float y = r * MathHelper.sin(theta);
|
||||
builder.vertex(x + cx, cy+y, cz+h).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
|
||||
builder.begin(GL11.GL_QUAD_STRIP, VertexFormats.POSITION_COLOR);
|
||||
for (int i = 0; i <= num_steps360; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float x = r * MathHelper.cos(theta);
|
||||
float y = r * MathHelper.sin(theta);
|
||||
builder.vertex(cx + x, cy + y, cz + 0).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(cx + x, cy + y, cz + h).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void drawSphereWireframe(Tessellator tessellator, BufferBuilder builder,
|
||||
float cx, float cy, float cz,
|
||||
float r, int subd,
|
||||
float red, float grn, float blu, float alpha)
|
||||
{
|
||||
float step = (float)Math.PI / (subd/2);
|
||||
int num_steps180 = (int)(Math.PI / step)+1;
|
||||
int num_steps360 = (int)(2*Math.PI / step);
|
||||
for (int i = 0; i <= num_steps360; i++)
|
||||
{
|
||||
builder.begin(GL11.GL_LINE_STRIP, VertexFormats.POSITION_COLOR);
|
||||
float theta = step * i ;
|
||||
for (int j = 0; j <= num_steps180; j++)
|
||||
{
|
||||
float phi = step * j ;
|
||||
float x = r * MathHelper.sin(phi) * MathHelper.cos(theta);
|
||||
float z = r * MathHelper.sin(phi) * MathHelper.sin(theta);
|
||||
float y = r * MathHelper.cos(phi);
|
||||
builder.vertex(x+cx, y+cy, z+cz).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
}
|
||||
for (int j = 0; j <= num_steps180; j++)
|
||||
{
|
||||
builder.begin(GL11.GL_LINE_LOOP, VertexFormats.POSITION_COLOR);
|
||||
float phi = step * j ;
|
||||
|
||||
for (int i = 0; i <= num_steps360; i++)
|
||||
{
|
||||
float theta = step * i;
|
||||
float x = r * MathHelper.sin(phi) * MathHelper.cos(theta);
|
||||
float z = r * MathHelper.sin(phi) * MathHelper.sin(theta);
|
||||
float y = r * MathHelper.cos(phi);
|
||||
builder.vertex(x+cx, y+cy, z+cz).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void drawSphereFaces(Tessellator tessellator, BufferBuilder builder,
|
||||
float cx, float cy, float cz,
|
||||
float r, int subd,
|
||||
float red, float grn, float blu, float alpha)
|
||||
{
|
||||
|
||||
float step = (float)Math.PI / (subd/2);
|
||||
int num_steps180 = (int)(Math.PI / step)+1;
|
||||
int num_steps360 = (int)(2*Math.PI / step);
|
||||
for (int i = 0; i <= num_steps360; i++)
|
||||
{
|
||||
float theta = i * step;
|
||||
float thetaprime = theta+step;
|
||||
builder.begin(GL11.GL_QUAD_STRIP, VertexFormats.POSITION_COLOR);
|
||||
for (int j = 0; j <= num_steps180; j++)
|
||||
{
|
||||
float phi = j * step;
|
||||
float x = r * MathHelper.sin(phi) * MathHelper.cos(theta);
|
||||
float z = r * MathHelper.sin(phi) * MathHelper.sin(theta);
|
||||
float y = r * MathHelper.cos(phi);
|
||||
float xp = r * MathHelper.sin(phi) * MathHelper.cos(thetaprime);
|
||||
float zp = r * MathHelper.sin(phi) * MathHelper.sin(thetaprime);
|
||||
builder.vertex(x + cx, y + cy, z + cz).color(red, grn, blu, alpha).next();
|
||||
builder.vertex(xp + cx, y + cy, zp + cz).color(red, grn, blu, alpha).next();
|
||||
}
|
||||
tessellator.draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,172 +72,11 @@ public class BlockValue extends Value
|
||||
throw new InternalExpressionException("Cannot parse block: "+str);
|
||||
}
|
||||
|
||||
public static VectorLocator locateVec(CarpetContext c, List<LazyValue> params, int offset)
|
||||
{
|
||||
return locateVec(c,params, offset, false);
|
||||
}
|
||||
public static VectorLocator locateVec(CarpetContext c, List<LazyValue> params, int offset, boolean optionalDirection)
|
||||
{
|
||||
try
|
||||
{
|
||||
Value v1 = params.get(0 + offset).evalValue(c);
|
||||
if (v1 instanceof BlockValue)
|
||||
{
|
||||
return (new VectorLocator(new Vec3d(((BlockValue) v1).getPos()).add(0.5,0.5,0.5), 1+offset)).fromBlock();
|
||||
}
|
||||
if (v1 instanceof ListValue)
|
||||
{
|
||||
List<Value> args = ((ListValue) v1).getItems();
|
||||
Vec3d pos = new Vec3d(
|
||||
NumericValue.asNumber(args.get(0)).getDouble(),
|
||||
NumericValue.asNumber(args.get(1)).getDouble(),
|
||||
NumericValue.asNumber(args.get(2)).getDouble());
|
||||
double yaw = 0.0D;
|
||||
double pitch = 0.0D;
|
||||
if (args.size()>3 && optionalDirection)
|
||||
{
|
||||
yaw = NumericValue.asNumber(args.get(3)).getDouble();
|
||||
pitch = NumericValue.asNumber(args.get(4)).getDouble();
|
||||
}
|
||||
return new VectorLocator(pos,offset+1, yaw, pitch);
|
||||
}
|
||||
Vec3d pos = new Vec3d(
|
||||
NumericValue.asNumber(v1).getDouble(),
|
||||
NumericValue.asNumber(params.get(1 + offset).evalValue(c)).getDouble(),
|
||||
NumericValue.asNumber(params.get(2 + offset).evalValue(c)).getDouble());
|
||||
double yaw = 0.0D;
|
||||
double pitch = 0.0D;
|
||||
int eatenLength = 3;
|
||||
if (params.size()>3+offset && optionalDirection)
|
||||
{
|
||||
yaw = NumericValue.asNumber(params.get(3 + offset).evalValue(c)).getDouble();
|
||||
pitch = NumericValue.asNumber(params.get(4 + offset).evalValue(c)).getDouble();
|
||||
eatenLength = 5;
|
||||
}
|
||||
|
||||
return new VectorLocator(pos,offset+eatenLength, yaw, pitch);
|
||||
}
|
||||
catch (IndexOutOfBoundsException e)
|
||||
{
|
||||
throw new InternalExpressionException("Position should be defined either by three coordinates, or a block value");
|
||||
}
|
||||
}
|
||||
|
||||
public static BlockPos locateBlockPos(CarpetContext c, int xpos, int ypos, int zpos)
|
||||
{
|
||||
return new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos);
|
||||
}
|
||||
|
||||
public static LocatorResult fromParams(CarpetContext c, List<LazyValue> params, int offset)
|
||||
{
|
||||
return fromParams(c, params,offset, false, false);
|
||||
}
|
||||
|
||||
public static LocatorResult fromParams(CarpetContext c, List<LazyValue> params, int offset, boolean acceptString)
|
||||
{
|
||||
return fromParams(c, params,offset, acceptString, false);
|
||||
}
|
||||
|
||||
public static LocatorResult fromParams(CarpetContext c, List<LazyValue> params, int offset, boolean acceptString, boolean optional)
|
||||
{
|
||||
try
|
||||
{
|
||||
Value v1 = params.get(0 + offset).evalValue(c);
|
||||
//add conditional from string name
|
||||
if (optional && v1 instanceof NullValue)
|
||||
{
|
||||
return new LocatorResult(null, 1+offset);
|
||||
}
|
||||
if (acceptString && v1 instanceof StringValue)
|
||||
{
|
||||
return new LocatorResult(fromString(v1.getString()), 1+offset);
|
||||
}
|
||||
if (v1 instanceof BlockValue)
|
||||
{
|
||||
return new LocatorResult(((BlockValue) v1), 1+offset);
|
||||
}
|
||||
if (v1 instanceof ListValue)
|
||||
{
|
||||
List<Value> args = ((ListValue) v1).getItems();
|
||||
int xpos = (int) NumericValue.asNumber(args.get(0)).getLong();
|
||||
int ypos = (int) NumericValue.asNumber(args.get(1)).getLong();
|
||||
int zpos = (int) NumericValue.asNumber(args.get(2)).getLong();
|
||||
return new LocatorResult(
|
||||
new BlockValue(
|
||||
null,
|
||||
c.s.getWorld(),
|
||||
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
|
||||
),
|
||||
1+offset);
|
||||
}
|
||||
int xpos = (int) NumericValue.asNumber(v1).getLong();
|
||||
int ypos = (int) NumericValue.asNumber( params.get(1 + offset).evalValue(c)).getLong();
|
||||
int zpos = (int) NumericValue.asNumber( params.get(2 + offset).evalValue(c)).getLong();
|
||||
return new LocatorResult(
|
||||
new BlockValue(
|
||||
null,
|
||||
c.s.getWorld(),
|
||||
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
|
||||
),
|
||||
3+offset
|
||||
);
|
||||
}
|
||||
catch (IndexOutOfBoundsException e)
|
||||
{
|
||||
throw new InternalExpressionException("Block should be defined either by three coordinates, a block value, or a proper string");
|
||||
}
|
||||
}
|
||||
|
||||
public static LocatorResult fromParamValues(CarpetContext c, List<Value> params, int offset, boolean acceptString, boolean optional)
|
||||
{
|
||||
try
|
||||
{
|
||||
Value v1 = params.get(0 + offset);
|
||||
//add conditional from string name
|
||||
if (optional && v1 instanceof NullValue)
|
||||
{
|
||||
return new LocatorResult(null, 1+offset);
|
||||
}
|
||||
if (acceptString && v1 instanceof StringValue)
|
||||
{
|
||||
return new LocatorResult(fromString(v1.getString()), 1+offset);
|
||||
}
|
||||
if (v1 instanceof BlockValue)
|
||||
{
|
||||
return new LocatorResult(((BlockValue) v1), 1+offset);
|
||||
}
|
||||
if (v1 instanceof ListValue)
|
||||
{
|
||||
List<Value> args = ((ListValue) v1).getItems();
|
||||
int xpos = (int) NumericValue.asNumber(args.get(0)).getLong();
|
||||
int ypos = (int) NumericValue.asNumber(args.get(1)).getLong();
|
||||
int zpos = (int) NumericValue.asNumber(args.get(2)).getLong();
|
||||
return new LocatorResult(
|
||||
new BlockValue(
|
||||
null,
|
||||
c.s.getWorld(),
|
||||
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
|
||||
),
|
||||
1+offset);
|
||||
}
|
||||
int xpos = (int) NumericValue.asNumber(v1).getLong();
|
||||
int ypos = (int) NumericValue.asNumber( params.get(1 + offset)).getLong();
|
||||
int zpos = (int) NumericValue.asNumber( params.get(2 + offset)).getLong();
|
||||
return new LocatorResult(
|
||||
new BlockValue(
|
||||
null,
|
||||
c.s.getWorld(),
|
||||
new BlockPos(c.origin.getX() + xpos, c.origin.getY() + ypos, c.origin.getZ() + zpos)
|
||||
),
|
||||
3+offset
|
||||
);
|
||||
}
|
||||
catch (IndexOutOfBoundsException e)
|
||||
{
|
||||
throw new InternalExpressionException("Block should be defined either by three coordinates, a block value, or a proper string");
|
||||
}
|
||||
}
|
||||
|
||||
public BlockState getBlockState()
|
||||
{
|
||||
if (blockState != null)
|
||||
@@ -342,46 +181,6 @@ public class BlockValue extends Value
|
||||
public ServerWorld getWorld() { return world;}
|
||||
|
||||
|
||||
public static class LocatorResult
|
||||
{
|
||||
public final BlockValue block;
|
||||
public final int offset;
|
||||
LocatorResult(BlockValue b, int o)
|
||||
{
|
||||
block = b;
|
||||
offset = o;
|
||||
}
|
||||
}
|
||||
|
||||
public static class VectorLocator
|
||||
{
|
||||
public Vec3d vec;
|
||||
public final int offset;
|
||||
public final double yaw;
|
||||
public final double pitch;
|
||||
public boolean fromBlock;
|
||||
VectorLocator(Vec3d v, int o)
|
||||
{
|
||||
vec = v;
|
||||
offset = o;
|
||||
yaw = 0.0D;
|
||||
pitch = 0.0D;
|
||||
}
|
||||
VectorLocator(Vec3d v, int o, double y, double p)
|
||||
{
|
||||
vec = v;
|
||||
offset = o;
|
||||
yaw = y;
|
||||
pitch = p;
|
||||
}
|
||||
|
||||
public VectorLocator fromBlock()
|
||||
{
|
||||
fromBlock = true;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public enum SpecificDirection {
|
||||
UP("up",0.5, 0.0, 0.5, Direction.UP),
|
||||
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
package carpet.script.value;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.fakes.EntityInterface;
|
||||
import carpet.fakes.ItemEntityInterface;
|
||||
import carpet.fakes.LivingEntityInterface;
|
||||
import carpet.fakes.MobEntityInterface;
|
||||
import carpet.fakes.HungerManagerInterface;
|
||||
import carpet.helpers.Tracer;
|
||||
import carpet.patches.EntityPlayerMPFake;
|
||||
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.network.packet.s2c.play.EntityPassengersSetS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
|
||||
@@ -45,6 +51,7 @@ import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.world.GameMode;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -60,6 +67,7 @@ import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static carpet.script.value.NBTSerializableValue.nameFromRegistryId;
|
||||
import static carpet.utils.MobAI.genericJump;
|
||||
|
||||
// TODO: decide whether copy(entity) should duplicate entity in the world.
|
||||
public class EntityValue extends Value
|
||||
@@ -118,7 +126,7 @@ public class EntityValue extends Value
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
return entity.getDisplayName().getString();
|
||||
return entity.getName().getString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -220,7 +228,9 @@ public class EntityValue extends Value
|
||||
put("motion_x", (e, a) -> new NumericValue(e.getVelocity().x));
|
||||
put("motion_y", (e, a) -> new NumericValue(e.getVelocity().y));
|
||||
put("motion_z", (e, a) -> new NumericValue(e.getVelocity().z));
|
||||
put("name", (e, a) -> new StringValue(e.getDisplayName().getString()));
|
||||
put("name", (e, a) -> new StringValue(e.getName().getString()));
|
||||
put("display_name", (e, a) -> new StringValue(e.getDisplayName().getString()));
|
||||
put("command_name", (e, a) -> new StringValue(e.getEntityName()));
|
||||
put("custom_name", (e, a) -> e.hasCustomName()?new StringValue(e.getCustomName().getString()):Value.NULL);
|
||||
put("type", (e, a) -> new StringValue(nameFromRegistryId(Registry.ENTITY_TYPE.getId(e.getType()))));
|
||||
put("is_riding", (e, a) -> new NumericValue(e.hasVehicle()));
|
||||
@@ -252,7 +262,9 @@ public class EntityValue extends Value
|
||||
put("despawn_timer", (e, a) -> e instanceof LivingEntity?new NumericValue(((LivingEntity) e).getDespawnCounter()):Value.NULL);
|
||||
put("item", (e, a) -> (e instanceof ItemEntity)?ListValue.fromItemStack(((ItemEntity) e).getStack()):Value.NULL);
|
||||
put("count", (e, a) -> (e instanceof ItemEntity)?new NumericValue(((ItemEntity) e).getStack().getCount()):Value.NULL);
|
||||
put("pickup_delay", (e, a) -> (e instanceof ItemEntity)?new NumericValue(((ItemEntityInterface) e).getPickupDelay()):Value.NULL);
|
||||
put("pickup_delay", (e, a) -> (e instanceof ItemEntity)?new NumericValue(((ItemEntityInterface) e).getPickupDelayCM()):Value.NULL);
|
||||
put("portal_cooldown", (e , a) ->new NumericValue(((EntityInterface)e).getPortalTimer()));
|
||||
put("portal_timer", (e , a) ->new NumericValue(e.netherPortalCooldown));
|
||||
// ItemEntity -> despawn timer via ssGetAge
|
||||
put("is_baby", (e, a) -> (e instanceof LivingEntity)?new NumericValue(((LivingEntity) e).isBaby()):Value.NULL);
|
||||
put("target", (e, a) -> {
|
||||
@@ -277,13 +289,27 @@ 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("jumping", (e, a) -> {
|
||||
put("hunger", (e, a) -> {
|
||||
if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).getHungerManager().getFoodLevel());
|
||||
return Value.NULL;
|
||||
});
|
||||
put("saturation", (e, a) -> {
|
||||
if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).getHungerManager().getSaturationLevel());
|
||||
return Value.NULL;
|
||||
});
|
||||
|
||||
put("exhaustion",(e, a)->{
|
||||
if(e instanceof PlayerEntity) return new NumericValue(((HungerManagerInterface)((PlayerEntity) e).getHungerManager()).getExhaustionCM());
|
||||
return Value.NULL;
|
||||
});
|
||||
|
||||
put("jumping", (e, a) -> {
|
||||
if (e instanceof LivingEntity)
|
||||
{
|
||||
return ((LivingEntity) e).getJumping()?Value.TRUE:Value.FALSE;
|
||||
return ((LivingEntityInterface) e).isJumpingCM()?Value.TRUE:Value.FALSE;
|
||||
}
|
||||
return Value.NULL;
|
||||
});*/ //needs mixing
|
||||
});
|
||||
put("gamemode", (e, a) -> {
|
||||
if (e instanceof ServerPlayerEntity)
|
||||
{
|
||||
@@ -331,6 +357,8 @@ public class EntityValue extends Value
|
||||
return Value.NULL;
|
||||
});
|
||||
|
||||
put("team", (e, a) -> e.getScoreboardTeam()==null?Value.NULL:new StringValue(e.getScoreboardTeam().getName()));
|
||||
|
||||
put("ping", (e, a) -> {
|
||||
if (e instanceof ServerPlayerEntity)
|
||||
{
|
||||
@@ -370,6 +398,7 @@ public class EntityValue extends Value
|
||||
StatusEffectInstance pe = ((LivingEntity) e).getStatusEffect(potion);
|
||||
return ListValue.of( new NumericValue(pe.getAmplifier()), new NumericValue(pe.getDuration()) );
|
||||
});
|
||||
|
||||
put("health", (e, a) ->
|
||||
{
|
||||
if (e instanceof LivingEntity)
|
||||
@@ -475,6 +504,8 @@ public class EntityValue extends Value
|
||||
return new NBTSerializableValue(nbttagcompound);
|
||||
return new NBTSerializableValue(nbttagcompound).get(a);
|
||||
});
|
||||
|
||||
put("category",(e,a)->{return new StringValue(e.getType().getCategory().toString().toLowerCase(Locale.ROOT));});
|
||||
}};
|
||||
|
||||
public void set(String what, Value toWhat)
|
||||
@@ -523,12 +554,40 @@ public class EntityValue extends Value
|
||||
//((ServerWorld)e.getEntityWorld()).method_14178().sendToNearbyPlayers(e, new EntityVelocityUpdateS2CPacket(e));
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static final Map<String, BiConsumer<Entity, Value>> featureModifiers = new HashMap<String, BiConsumer<Entity, Value>>() {{
|
||||
put("remove", (entity, value) -> entity.remove());
|
||||
put("age", (e, v) -> e.age = Math.abs((int)NumericValue.asNumber(v).getLong()) );
|
||||
put("health", (e, v) -> { if (e instanceof LivingEntity) ((LivingEntity) e).setHealth((float) NumericValue.asNumber(v).getDouble()); });
|
||||
put("health", (e, v) -> {
|
||||
float health = (float) NumericValue.asNumber(v).getDouble();
|
||||
if (health <= 0f && e instanceof ServerPlayerEntity)
|
||||
{
|
||||
ServerPlayerEntity player = (ServerPlayerEntity) e;
|
||||
if (player.container != null)
|
||||
{
|
||||
// if player dies with open container, then that causes NPE on the client side
|
||||
// its a client side bug that may never surface unless vanilla gets into scripting at some point
|
||||
// bug: #228
|
||||
player.closeContainer();
|
||||
}
|
||||
((LivingEntity) e).setHealth(health);
|
||||
}
|
||||
if (e instanceof LivingEntity) ((LivingEntity) e).setHealth(health);
|
||||
});
|
||||
// todo add handling of the source for extra effects
|
||||
/*put("damage", (e, v) -> {
|
||||
float dmgPoints;
|
||||
DamageSource source;
|
||||
if (v instanceof ListValue && ((ListValue) v).getItems().size() > 1)
|
||||
{
|
||||
List<Value> vals = ((ListValue) v).getItems();
|
||||
dmgPoints = (float) NumericValue.asNumber(v).getDouble();
|
||||
source = DamageSource ... yeah...
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
});*/
|
||||
put("kill", (e, v) -> e.kill());
|
||||
put("location", (e, v) ->
|
||||
{
|
||||
@@ -751,26 +810,12 @@ public class EntityValue extends Value
|
||||
else if (v instanceof ListValue)
|
||||
{
|
||||
List<Value> lv = ((ListValue) v).getItems();
|
||||
if (lv.get(0) instanceof BlockValue)
|
||||
Vector3Argument locator = Vector3Argument.findIn(lv, 0, false);
|
||||
pos = new BlockPos(locator.vec.x, locator.vec.y, locator.vec.z);
|
||||
if (lv.size() > locator.offset)
|
||||
{
|
||||
pos = ((BlockValue) lv.get(0)).getPos();
|
||||
if (lv.size()>1)
|
||||
{
|
||||
distance = (int) NumericValue.asNumber(lv.get(1)).getLong();
|
||||
}
|
||||
distance = (int) NumericValue.asNumber(lv.get(locator.offset)).getLong();
|
||||
}
|
||||
else if (lv.size()>=3)
|
||||
{
|
||||
pos = new BlockPos(NumericValue.asNumber(lv.get(0)).getLong(),
|
||||
NumericValue.asNumber(lv.get(1)).getLong(),
|
||||
NumericValue.asNumber(lv.get(2)).getLong());
|
||||
if (lv.size()>3)
|
||||
{
|
||||
distance = (int) NumericValue.asNumber(lv.get(4)).getLong();
|
||||
}
|
||||
}
|
||||
else throw new InternalExpressionException("'home' requires at least one position argument, and optional distance");
|
||||
|
||||
}
|
||||
else throw new InternalExpressionException("'home' requires at least one position argument, and optional distance");
|
||||
|
||||
@@ -800,6 +845,20 @@ public class EntityValue extends Value
|
||||
}
|
||||
});
|
||||
|
||||
put("portal_cooldown", (e , v) ->
|
||||
{
|
||||
if (v==null)
|
||||
throw new InternalExpressionException("'portal_cooldown' requires a value to set");
|
||||
e.netherPortalCooldown = NumericValue.asNumber(v).getInt();
|
||||
});
|
||||
|
||||
put("portal_timer", (e , v) ->
|
||||
{
|
||||
if (v==null)
|
||||
throw new InternalExpressionException("'portal_timer' requires a value to set");
|
||||
((EntityInterface) e).setPortalTimer(NumericValue.asNumber(v).getInt());
|
||||
});
|
||||
|
||||
put("ai", (e, v) ->
|
||||
{
|
||||
if (e instanceof MobEntity)
|
||||
@@ -820,7 +879,10 @@ public class EntityValue extends Value
|
||||
if (!(e instanceof LivingEntity)) return;
|
||||
LivingEntity le = (LivingEntity)e;
|
||||
if (v == null)
|
||||
{
|
||||
le.clearStatusEffects();
|
||||
return;
|
||||
}
|
||||
else if (v instanceof ListValue)
|
||||
{
|
||||
List<Value> lv = ((ListValue) v).getItems();
|
||||
@@ -840,7 +902,7 @@ public class EntityValue extends Value
|
||||
{
|
||||
le.removeStatusEffect(effect);
|
||||
return;
|
||||
}
|
||||
}
|
||||
int amplifier = 0;
|
||||
if (lv.size() > 2)
|
||||
amplifier = (int)NumericValue.asNumber(lv.get(2)).getLong();
|
||||
@@ -854,24 +916,81 @@ public class EntityValue extends Value
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
String effectName = v.getString();
|
||||
StatusEffect effect = Registry.STATUS_EFFECT.get(new Identifier(effectName));
|
||||
if (effect == null)
|
||||
throw new InternalExpressionException("Wrong effect name: "+effectName);
|
||||
le.removeStatusEffect(effect);
|
||||
return;
|
||||
}
|
||||
throw new InternalExpressionException("'effect' needs either no arguments (clear) or effect name, duration, and optional amplifier, show particles and show icon");
|
||||
});
|
||||
|
||||
// gamemode
|
||||
// spectate
|
||||
// "fire"
|
||||
// "extinguish"
|
||||
// "silent"
|
||||
// "gravity"
|
||||
// "invulnerable"
|
||||
// "dimension"
|
||||
// "item"
|
||||
// "count",
|
||||
// "age",
|
||||
// "effect_"name
|
||||
// "hold"
|
||||
// "hold_offhand"
|
||||
// "jump"
|
||||
put("gamemode", (e,v)->{
|
||||
if(!(e instanceof ServerPlayerEntity)) return;
|
||||
GameMode toSet = v instanceof NumericValue ?
|
||||
GameMode.byId(((NumericValue) v).getInt(), null) :
|
||||
GameMode.byName(v.getString().toLowerCase(Locale.ROOT), null);
|
||||
if (toSet != null) ((ServerPlayerEntity) e).setGameMode(toSet);
|
||||
});
|
||||
|
||||
put("jumping",(e,v)->{
|
||||
if(!(e instanceof LivingEntity)) return;
|
||||
((LivingEntity) e).setJumping(v.getBoolean());
|
||||
});
|
||||
|
||||
put("jump",(e,v)->{
|
||||
if (e instanceof LivingEntity)
|
||||
{
|
||||
((LivingEntityInterface)e).doJumpCM();
|
||||
}
|
||||
else
|
||||
{
|
||||
genericJump(e);
|
||||
}
|
||||
});
|
||||
|
||||
put("silent",(e,v)-> e.setSilent(v.getBoolean()));
|
||||
|
||||
put("gravity",(e,v)-> e.setNoGravity(!v.getBoolean()));
|
||||
|
||||
put("invulnerable",(e,v)-> e.setInvulnerable(v.getBoolean()));
|
||||
|
||||
put("fire",(e,v)-> e.setFireTicks((int)NumericValue.asNumber(v).getLong()));
|
||||
|
||||
put("hunger", (e, v)-> {
|
||||
if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setFoodLevel((int) NumericValue.asNumber(v).getLong());
|
||||
});
|
||||
|
||||
put("exhaustion", (e, v)-> {
|
||||
if(e instanceof PlayerEntity) ((HungerManagerInterface) ((PlayerEntity) e).getHungerManager()).setExhaustionCM(NumericValue.asNumber(v).getFloat());
|
||||
});
|
||||
|
||||
put("add_exhaustion", (e, v)-> {
|
||||
if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().addExhaustion((int) NumericValue.asNumber(v).getLong());
|
||||
});
|
||||
|
||||
put("saturation", (e, v)-> {
|
||||
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]
|
||||
// "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 ...
|
||||
}};
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package carpet.script.value;
|
||||
|
||||
import net.minecraft.text.BaseText;
|
||||
|
||||
public class FormattedTextValue extends StringValue
|
||||
{
|
||||
BaseText text;
|
||||
public FormattedTextValue(BaseText text)
|
||||
{
|
||||
super(null);
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
return text.getString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean() {
|
||||
return text.getSiblings().size() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value clone()
|
||||
{
|
||||
return new FormattedTextValue(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeString()
|
||||
{
|
||||
return "text";
|
||||
}
|
||||
|
||||
public BaseText getText()
|
||||
{
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import carpet.script.Expression;
|
||||
import carpet.script.Fluff;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.Tokenizer;
|
||||
import carpet.script.bundled.Module;
|
||||
import carpet.script.exception.BreakStatement;
|
||||
import carpet.script.exception.ContinueStatement;
|
||||
import carpet.script.exception.ExitStatement;
|
||||
@@ -58,6 +59,8 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
return name;
|
||||
}
|
||||
|
||||
public Module getModule() {return expression.module;}
|
||||
|
||||
@Override
|
||||
public String getPrettyString()
|
||||
{
|
||||
|
||||
@@ -48,7 +48,7 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
return String.format(Locale.ROOT, "[%d, %d, ..., %d]",start, start+stepp, limit);
|
||||
return String.format(Locale.ROOT, "[%d, %d, ..., %d)",start, start+stepp, limit);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Loaded 100 of 122 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user