Merge branch 'master' into 1.17

This commit is contained in:
gnembon committed 2021-01-07 00:41:15 -04:00
commit ed4afa15ff
49 files changed
+593 -211

No files matched your search

+29 -18
View File
@@ -7,7 +7,7 @@ sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8
archivesBaseName = project.archives_base_name archivesBaseName = project.archives_base_name
version = project.minecraft_version+'-'+project.mod_version version = project.minecraft_version+'-'+project.mod_version+'+v'+new Date().format('yyMMdd')
group = project.maven_group group = project.maven_group
minecraft { minecraft {
@@ -15,7 +15,7 @@ minecraft {
} }
dependencies { dependencies {
//to change the versions see the gradle.properties file // To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}" minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modCompile "net.fabricmc:fabric-loader:${project.loader_version}" modCompile "net.fabricmc:fabric-loader:${project.loader_version}"
@@ -32,7 +32,7 @@ processResources {
from(sourceSets.main.resources.srcDirs) { from(sourceSets.main.resources.srcDirs) {
include "fabric.mod.json" include "fabric.mod.json"
expand "version": project.version expand "version": project.mod_version
} }
from(sourceSets.main.resources.srcDirs) { from(sourceSets.main.resources.srcDirs) {
@@ -40,23 +40,34 @@ processResources {
} }
} }
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly tasks.withType(JavaCompile).configureEach {
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html // ensure that the encoding is set to UTF-8, no matter what the system default is
tasks.withType(JavaCompile) { // this fixes some edge cases with special characters not displaying correctly
options.encoding = "UTF-8" // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
// If Javadoc is generated, this must be specified in that task too.
it.options.encoding = "UTF-8"
// The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too
// JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used.
// We'll use that if it's available, but otherwise we'll use the older option.
def targetVersion = 8
if (JavaVersion.current().isJava9Compatible()) {
it.options.release = targetVersion
}
} }
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task java {
// if it is present. // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// If you remove this task, sources will not be generated. // if it is present.
task sourcesJar(type: Jar, dependsOn: classes) { // If you remove this line, sources will not be generated.
classifier = "sources" withSourcesJar()
from sourceSets.main.allSource
} }
jar { jar {
from "LICENSE" from("LICENSE") {
rename { "${it}_${project.archivesBaseName}"}
}
} }
// configure the maven publication // configure the maven publication
@@ -73,10 +84,10 @@ publishing {
} }
} }
// select the repositories you want to publish to // Select the repositories you want to publish to
// To publish to maven local, no extra repositories are necessary. Just use the task `publishToMavenLocal`.
repositories { repositories {
// uncomment to publish to the local maven // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
// mavenLocal()
maven { maven {
url "$projectDir/publish" url "$projectDir/publish"
} }
+29 -13
View File
@@ -1665,14 +1665,21 @@ split('\\.','foo.bar') => [foo, bar]
### `slice(expr, from, to?)` ### `slice(expr, from, to?)`
extracts a substring, or sublist (based on the type of the result of the expression under expr with extracts a substring, or sublist (based on the type of the result of the expression under expr with
starting index of `from`, and ending at `to` if provided, or the end, if omitted starting index of `from`, and ending at `to` if provided, or the end, if omitted. Can use negative indices to
indicate counting form the back of the list, so `-1 <=> length(_)`.
Special case is made for iterators (`range`, `rect` etc), which does require non-negative indices (negative `from` is treated as
`0`, and negative `to` as `inf`), but allows retrieving parts of the sequence and ignore
other parts. In that case consecutive calls to `slice` will refer to index `0` the current iteration position since iterators
cannot go back nor track where they are in the sequence (see examples).
<pre> <pre>
slice(l(0,1,2,3,4,5), 1, 3) => [1, 2, 3] slice([0,1,2,3,4,5], 1, 3) => [1, 2, 3]
slice('foobar', 0, 1) => 'f' slice('foobar', 0, 1) => 'f'
slice('foobar', 3) => 'bar' slice('foobar', 3) => 'bar'
slice(range(10), 3, 5) => [3, 4, 5] slice(range(10), 3, 5) => [3, 4, 5]
slice(range(10), 5) => [5, 6, 7, 8, 9] slice(range(10), 5) => [5, 6, 7, 8, 9]
r = range(100); [slice(r, 5, 7), slice(r, 1, 3)] => [[5, 6], [8, 9]]
</pre> </pre>
### `sort(list), sort(values ...)` ### `sort(list), sort(values ...)`
@@ -2893,7 +2900,10 @@ In this case - entities would need to be re-fetched, or the code should account
### `player(), player(type), player(name)` ### `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 With no arguments, it returns the calling player or the player closest to the caller.
For player-scoped apps (which is a default) its always the owning player or `null` if it not present even if some code
still runs in their name.
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 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 that name, or `null` if no player was found. With `'all'`, list of all players in the game, in all dimensions, so end
@@ -4109,8 +4119,10 @@ about players death and applying external effects (like mob anger etc).
### `__on_player_respawns(player)` ### `__on_player_respawns(player)`
Triggered when a player respawns. This includes spawning after death, or landing in the overworld after leaving the end. Triggered when a player respawns. This includes spawning after death, or landing in the overworld after leaving the end.
When the event is handled, a player is still in its previous location and dimension - will be repositioned right after. When the event is handled, a player is still in its previous location and dimension - will be repositioned right after. In
case player died, its previous inventory as already been scattered, and its current inventory will not be copied to the respawned
entity, so any manipulation to player data is
best to be scheduled at the end of the tick, but you can still use its current reference to query its status as of the respawn event.
### `__on_player_changes_dimension(player, from_pos, from_dimension, to_pos, to_dimension)` ### `__on_player_changes_dimension(player, from_pos, from_dimension, to_pos, to_dimension)`
Called when a player moves from one dimension to another. Event is handled still when the player is in its previous Called when a player moves from one dimension to another. Event is handled still when the player is in its previous
@@ -4299,8 +4311,9 @@ With specified `objective` and
### `scoreboard_add(objective, criterion?)` ### `scoreboard_add(objective, criterion?)`
Adds a new objective to scoreboard. If `criterion` is not specified, assumes `'dummy'`. Returns `false` if objective Adds a new objective to scoreboard. If `criterion` is not specified, assumes `'dummy'`.
already existed, `true` otherwise. If the objective already exists, changes the criterion of that objective and returns `false`. If the criterion was not specified but the objective already exists, returns the current criterion.
If the objective was added, returns `true`. If nothing is affected, returns `null`
<pre> <pre>
scoreboard_add('counter') scoreboard_add('counter')
@@ -4451,7 +4464,6 @@ for(bossbar(),bossbar(_,'remove')) -> remove all bossbars
# Auxiliary aspects # Auxiliary aspects
Collection of other methods that control smaller, yet still important aspects of the game Collection of other methods that control smaller, yet still important aspects of the game
@@ -4759,12 +4771,15 @@ for folder listing.
Supported values for resource `type` are: Supported values for resource `type` are:
* `nbt` - NBT tag * `nbt` - NBT tag
* `json` - JSON file
* `text` - text resource with automatic newlines added * `text` - text resource with automatic newlines added
* `raw` - text resource without implied newlines * `raw` - text resource without implied newlines
* `folder` - for `list_files` only - indicting folder listing instead of files * `folder` - for `list_files` only - indicating folder listing instead of files
* `shared_nbt`, `shared_text`, `shared_raw`, `shared_folder` - shared versions of the above * `shared_nbt`, `shared_text`, `shared_raw`, `shared_folder`, `shared_json` - 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, NBT files have extension `.nbt`, store one NBT tag, and return a NBT type value. JSON files have `.json` extension, store
Scarpet numbers, strings, lists, maps and `null` values. Anything else will be saved as a string (including NBT).
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 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 sent to the file at once. The only difference between `raw` and `text` types are automatic newlines added after each
record to the file. Since files are closed after each write, sending multiple lines of data to record to the file. Since files are closed after each write, sending multiple lines of data to
@@ -4942,7 +4957,7 @@ it could either mean your input is wrong, or statistic effectively has a value o
### `system_info()`, `system_info(property)` ### `system_info()`, `system_info(property)`
Fetches the value of a system property or returns all inforation as a map when called without any arguments. It can be used to Fetches the value of a system property or returns all inforation as a map when called without any arguments. It can be used to
fetch various information, mostly not changing, or only available via low level fetch various information, mostly not changing, or only available via low level
system calls. In all cirumstances, these are only provided as read-only. system calls. In all circumstances, these are only provided as read-only.
Available options in the scarpet app space: Available options in the scarpet app space:
* `app_name` - current app name or `null` if its a default app * `app_name` - current app name or `null` if its a default app
@@ -4960,7 +4975,7 @@ Available options in the scarpet app space:
Relevant gameplay related properties Relevant gameplay related properties
* `game_difficulty` - current difficulty of the game: `'peacefu'`, `'easy'`, `'normal'`, or `'hard'` * `game_difficulty` - current difficulty of the game: `'peaceful'`, `'easy'`, `'normal'`, or `'hard'`
* `game_hardcore` - boolean whether the game is in hardcore mode * `game_hardcore` - boolean whether the game is in hardcore mode
* `game_storage_format` - format of the world save files, either `'McRegion'` or `'Anvil'` * `game_storage_format` - format of the world save files, either `'McRegion'` or `'Anvil'`
* `game_default_gamemode` - default gamemode for new players * `game_default_gamemode` - default gamemode for new players
@@ -4968,6 +4983,7 @@ Available options in the scarpet app space:
* `game_view_distance` - the view distance * `game_view_distance` - the view distance
* `game_mod_name` - the name of the base mod. Expect `'fabric'` * `game_mod_name` - the name of the base mod. Expect `'fabric'`
* `game_version` - base version of the game * `game_version` - base version of the game
* `game_data_version` - data version of the game. Returns an integer, so it can be compared.
Server related properties Server related properties
* `server_motd` - the motd of the server visible when joining * `server_motd` - the motd of the server visible when joining
+9 -5
View File
@@ -305,12 +305,15 @@ for folder listing.
Supported values for resource `type` are: Supported values for resource `type` are:
* `nbt` - NBT tag * `nbt` - NBT tag
* `json` - JSON file
* `text` - text resource with automatic newlines added * `text` - text resource with automatic newlines added
* `raw` - text resource without implied newlines * `raw` - text resource without implied newlines
* `folder` - for `list_files` only - indicting folder listing instead of files * `folder` - for `list_files` only - indicating folder listing instead of files
* `shared_nbt`, `shared_text`, `shared_raw`, `shared_folder` - shared versions of the above * `shared_nbt`, `shared_text`, `shared_raw`, `shared_folder`, `shared_json` - 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, NBT files have extension `.nbt`, store one NBT tag, and return a NBT type value. JSON files have `.json` extension, store
Scarpet numbers, strings, lists, maps and `null` values. Anything else will be saved as a string (including NBT).
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 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 sent to the file at once. The only difference between `raw` and `text` types are automatic newlines added after each
record to the file. Since files are closed after each write, sending multiple lines of data to record to the file. Since files are closed after each write, sending multiple lines of data to
@@ -488,7 +491,7 @@ it could either mean your input is wrong, or statistic effectively has a value o
### `system_info()`, `system_info(property)` ### `system_info()`, `system_info(property)`
Fetches the value of a system property or returns all inforation as a map when called without any arguments. It can be used to Fetches the value of a system property or returns all inforation as a map when called without any arguments. It can be used to
fetch various information, mostly not changing, or only available via low level fetch various information, mostly not changing, or only available via low level
system calls. In all cirumstances, these are only provided as read-only. system calls. In all circumstances, these are only provided as read-only.
Available options in the scarpet app space: Available options in the scarpet app space:
* `app_name` - current app name or `null` if its a default app * `app_name` - current app name or `null` if its a default app
@@ -506,7 +509,7 @@ Available options in the scarpet app space:
Relevant gameplay related properties Relevant gameplay related properties
* `game_difficulty` - current difficulty of the game: `'peacefu'`, `'easy'`, `'normal'`, or `'hard'` * `game_difficulty` - current difficulty of the game: `'peaceful'`, `'easy'`, `'normal'`, or `'hard'`
* `game_hardcore` - boolean whether the game is in hardcore mode * `game_hardcore` - boolean whether the game is in hardcore mode
* `game_storage_format` - format of the world save files, either `'McRegion'` or `'Anvil'` * `game_storage_format` - format of the world save files, either `'McRegion'` or `'Anvil'`
* `game_default_gamemode` - default gamemode for new players * `game_default_gamemode` - default gamemode for new players
@@ -514,6 +517,7 @@ Available options in the scarpet app space:
* `game_view_distance` - the view distance * `game_view_distance` - the view distance
* `game_mod_name` - the name of the base mod. Expect `'fabric'` * `game_mod_name` - the name of the base mod. Expect `'fabric'`
* `game_version` - base version of the game * `game_version` - base version of the game
* `game_data_version` - data version of the game. Returns an integer, so it can be compared.
Server related properties Server related properties
* `server_motd` - the motd of the server visible when joining * `server_motd` - the motd of the server visible when joining
+12 -1
View File
@@ -8,7 +8,10 @@ In this case - entities would need to be re-fetched, or the code should account
### `player(), player(type), player(name)` ### `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 With no arguments, it returns the calling player or the player closest to the caller.
For player-scoped apps (which is a default) its always the owning player or `null` if it not present even if some code
still runs in their name.
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 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 that name, or `null` if no player was found. With `'all'`, list of all players in the game, in all dimensions, so end
@@ -243,6 +246,10 @@ Boolean, true if the entity is silent.
Boolean, true if the entity is affected by gravity, like most entities are. Boolean, true if the entity is affected by gravity, like most entities are.
### `query(e, 'invulnerable')`
Boolean, true if the entity is invulnerable.
### `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.
@@ -723,6 +730,10 @@ Silences or unsilences the entity.
Toggles gravity for the entity. Toggles gravity for the entity.
### `modify(e, 'invulnerable', boolean)`
Toggles invulnerability for the entity.
### `modify(e, 'fire', ticks)` ### `modify(e, 'fire', ticks)`
Will set entity on fire for `ticks` ticks. Set to 0 to extinguish. Will set entity on fire for `ticks` ticks. Set to 0 to extinguish.
+4 -2
View File
@@ -160,8 +160,10 @@ about players death and applying external effects (like mob anger etc).
### `__on_player_respawns(player)` ### `__on_player_respawns(player)`
Triggered when a player respawns. This includes spawning after death, or landing in the overworld after leaving the end. Triggered when a player respawns. This includes spawning after death, or landing in the overworld after leaving the end.
When the event is handled, a player is still in its previous location and dimension - will be repositioned right after. When the event is handled, a player is still in its previous location and dimension - will be repositioned right after. In
case player died, its previous inventory as already been scattered, and its current inventory will not be copied to the respawned
entity, so any manipulation to player data is
best to be scheduled at the end of the tick, but you can still use its current reference to query its status as of the respawn event.
### `__on_player_changes_dimension(player, from_pos, from_dimension, to_pos, to_dimension)` ### `__on_player_changes_dimension(player, from_pos, from_dimension, to_pos, to_dimension)`
Called when a player moves from one dimension to another. Event is handled still when the player is in its previous Called when a player moves from one dimension to another. Event is handled still when the player is in its previous
+3 -3
View File
@@ -10,8 +10,9 @@ With specified `objective` and
### `scoreboard_add(objective, criterion?)` ### `scoreboard_add(objective, criterion?)`
Adds a new objective to scoreboard. If `criterion` is not specified, assumes `'dummy'`. Returns `false` if objective Adds a new objective to scoreboard. If `criterion` is not specified, assumes `'dummy'`.
already existed, `true` otherwise. If the objective already exists, changes the criterion of that objective and returns `false`. If the criterion was not specified but the objective already exists, returns the current criterion.
If the objective was added, returns `true`. If nothing is affected, returns `null`
<pre> <pre>
scoreboard_add('counter') scoreboard_add('counter')
@@ -162,4 +163,3 @@ for(bossbar(),bossbar(_,'remove')) -> remove all bossbars
+9 -2
View File
@@ -170,14 +170,21 @@ split('\\.','foo.bar') => [foo, bar]
### `slice(expr, from, to?)` ### `slice(expr, from, to?)`
extracts a substring, or sublist (based on the type of the result of the expression under expr with extracts a substring, or sublist (based on the type of the result of the expression under expr with
starting index of `from`, and ending at `to` if provided, or the end, if omitted starting index of `from`, and ending at `to` if provided, or the end, if omitted. Can use negative indices to
indicate counting form the back of the list, so `-1 <=> length(_)`.
Special case is made for iterators (`range`, `rect` etc), which does require non-negative indices (negative `from` is treated as
`0`, and negative `to` as `inf`), but allows retrieving parts of the sequence and ignore
other parts. In that case consecutive calls to `slice` will refer to index `0` the current iteration position since iterators
cannot go back nor track where they are in the sequence (see examples).
<pre> <pre>
slice(l(0,1,2,3,4,5), 1, 3) => [1, 2, 3] slice([0,1,2,3,4,5], 1, 3) => [1, 2, 3]
slice('foobar', 0, 1) => 'f' slice('foobar', 0, 1) => 'f'
slice('foobar', 3) => 'bar' slice('foobar', 3) => 'bar'
slice(range(10), 3, 5) => [3, 4, 5] slice(range(10), 3, 5) => [3, 4, 5]
slice(range(10), 5) => [5, 6, 7, 8, 9] slice(range(10), 5) => [5, 6, 7, 8, 9]
r = range(100); [slice(r, 5, 7), slice(r, 1, 3)] => [[5, 6], [8, 9]]
</pre> </pre>
### `sort(list), sort(values ...)` ### `sort(list), sort(values ...)`
+1 -1
View File
@@ -9,7 +9,7 @@ org.gradle.jvmargs=-Xmx1G
loader_version=0.10.8 loader_version=0.10.8
# Mod Properties # Mod Properties
mod_version = 1.4.21+v201216 mod_version = 1.4.21
maven_group = carpet maven_group = carpet
archives_base_name = fabric-carpet archives_base_name = fabric-carpet
Binary file not shown.
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
Vendored
+15 -19
View File
@@ -125,10 +125,11 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi fi
# For Cygwin, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if $cygwin ; then if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"` JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath # We build the pattern for arguments to be converted via cygpath
@@ -154,19 +155,19 @@ if $cygwin ; then
else else
eval `echo args$i`="\"$arg\"" eval `echo args$i`="\"$arg\""
fi fi
i=$((i+1)) i=`expr $i + 1`
done done
case $i in case $i in
(0) set -- ;; 0) set -- ;;
(1) set -- "$args0" ;; 1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;; 2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;; 3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;; 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
@@ -175,14 +176,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " " echo " "
} }
APP_ARGS=$(save "$@") APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules # Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"
Vendored
+7 -18
View File
@@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init if "%ERRORLEVEL%" == "0" goto execute
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -51,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init if exist "%JAVA_EXE%" goto execute
echo. echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -61,28 +64,14 @@ echo location of your Java installation.
goto fail goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
+1 -1
View File
@@ -109,7 +109,7 @@ public class CarpetServer implements ClientModInitializer,DedicatedServerModInit
public static void tick(MinecraftServer server) public static void tick(MinecraftServer server)
{ {
TickSpeed.tick(server); TickSpeed.tick();
HUDController.update_hud(server); HUDController.update_hud(server);
scriptServer.tick(); scriptServer.tick();
@@ -2,6 +2,7 @@ package carpet.commands;
import carpet.CarpetSettings; import carpet.CarpetSettings;
import carpet.helpers.TickSpeed; import carpet.helpers.TickSpeed;
import carpet.network.ServerNetworkHandler;
import carpet.settings.SettingsManager; import carpet.settings.SettingsManager;
import carpet.utils.CarpetProfiler; import carpet.utils.CarpetProfiler;
import carpet.utils.Messenger; import carpet.utils.Messenger;
@@ -95,16 +96,13 @@ public class TickCommand
private static int toggleFreeze(ServerCommandSource source, boolean isDeep) private static int toggleFreeze(ServerCommandSource source, boolean isDeep)
{ {
TickSpeed.is_paused = !TickSpeed.is_paused; TickSpeed.setFrozenState(!TickSpeed.isPaused(), isDeep);
if (TickSpeed.is_paused) if (TickSpeed.isPaused())
{ {
TickSpeed.deepFreeze = isDeep;
Messenger.m(source, "gi Game is "+(isDeep?"deeply ":"")+"frozen"); Messenger.m(source, "gi Game is "+(isDeep?"deeply ":"")+"frozen");
} }
else else
{ {
TickSpeed.deepFreeze = false;
Messenger.m(source, "gi Game runs normally"); Messenger.m(source, "gi Game runs normally");
} }
return 1; return 1;
@@ -119,9 +117,10 @@ public class TickCommand
private static int toggleSuperHot(ServerCommandSource source) private static int toggleSuperHot(ServerCommandSource source)
{ {
TickSpeed.is_superHot = !TickSpeed.is_superHot; TickSpeed.is_superHot = !TickSpeed.is_superHot;
ServerNetworkHandler.updateSuperHotStateToConnectedPlayers();
if (TickSpeed.is_superHot) if (TickSpeed.is_superHot)
{ {
Messenger.m(source,"gi Superhot enabled"); Messenger.m(source, "gi Superhot enabled");
} }
else else
{ {
@@ -5,4 +5,7 @@ import carpet.helpers.EntityPlayerActionPack;
public interface ServerPlayerEntityInterface public interface ServerPlayerEntityInterface
{ {
EntityPlayerActionPack getActionPack(); EntityPlayerActionPack getActionPack();
void invalidateEntityObjectReference();
boolean isInvalidEntityObject();
} }
+37 -4
View File
@@ -7,7 +7,6 @@ import java.util.function.BiConsumer;
import carpet.CarpetServer; import carpet.CarpetServer;
import carpet.network.ServerNetworkHandler; import carpet.network.ServerNetworkHandler;
import carpet.utils.Messenger; import carpet.utils.Messenger;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
@@ -26,10 +25,43 @@ public class TickSpeed
public static ServerCommandSource tick_warp_sender = null; public static ServerCommandSource tick_warp_sender = null;
public static int player_active_timeout = 0; public static int player_active_timeout = 0;
public static boolean process_entities = true; public static boolean process_entities = true;
public static boolean deepFreeze = false; private static boolean deepFreeze = false;
public static boolean is_paused = false; private static boolean is_paused = false;
public static boolean is_superHot = false; public static boolean is_superHot = false;
/**
* @return Whether or not the game is in a frozen state.
* You should normally use {@link #process_entities} instead,
* since that one accounts for tick steps and superhot
*/
public static boolean isPaused() {
return is_paused;
}
/**
* Whether or not the game is deeply frozen.
* This can be used for things that you may not normally want
* to freeze, but may need to in some situations.
* This should be checked with {@link #process_entities} to make sure the
* current tick is actually frozen, not only the game
* @return Whether or not the game is deeply frozen.
*/
public static boolean deeplyFrozen() {
return deepFreeze;
}
/**
* Used to update the frozen state of the game.
* Handles connected clients as well.
* @param isPaused Whether or not the game is paused
* @param isDeepFreeze Whether or not the game is deeply frozen
*/
public static void setFrozenState(boolean isPaused, boolean isDeepFreeze) {
is_paused = isPaused;
deepFreeze = isPaused ? isDeepFreeze : false;
ServerNetworkHandler.updateFrozenStateToConnectedPlayers();
}
/** /**
* Functional interface that listens for tickrate changes. This is * Functional interface that listens for tickrate changes. This is
* implemented to allow tickrate compatibility with other mods etc. * implemented to allow tickrate compatibility with other mods etc.
@@ -66,6 +98,7 @@ public class TickSpeed
public static void add_ticks_to_run_in_pause(int ticks) public static void add_ticks_to_run_in_pause(int ticks)
{ {
player_active_timeout = PLAYER_GRACE+ticks; player_active_timeout = PLAYER_GRACE+ticks;
ServerNetworkHandler.updateTickPlayerActiveTimeoutToConnectedPlayers();
} }
public static BaseText tickrate_advance(ServerPlayerEntity player, int advance, String callback, ServerCommandSource source) public static BaseText tickrate_advance(ServerPlayerEntity player, int advance, String callback, ServerCommandSource source)
@@ -155,7 +188,7 @@ public class TickSpeed
} }
} }
public static void tick(MinecraftServer server) public static void tick()
{ {
process_entities = true; process_entities = true;
if (player_active_timeout > 0) if (player_active_timeout > 0)
@@ -18,6 +18,6 @@ public class ChunkTicketManager_tickMixin
{ {
// pausing expiry of tickets // pausing expiry of tickets
// that will prevent also chunks from unloading, so require a deep frozen state // that will prevent also chunks from unloading, so require a deep frozen state
if (!TickSpeed.process_entities && TickSpeed.deepFreeze) age--; if (!TickSpeed.process_entities && TickSpeed.deeplyFrozen()) age--;
} }
} }
@@ -1,5 +1,6 @@
package carpet.mixins; package carpet.mixins;
import carpet.helpers.TickSpeed;
import carpet.utils.CarpetProfiler; import carpet.utils.CarpetProfiler;
import net.minecraft.server.function.CommandFunctionManager; import net.minecraft.server.function.CommandFunctionManager;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -12,9 +13,11 @@ public class CommandFunctionManager_tickMixin
{ {
CarpetProfiler.ProfilerToken currentSection; CarpetProfiler.ProfilerToken currentSection;
@Inject(method = "tick", at = @At("HEAD")) @Inject(method = "tick", at = @At("HEAD"), cancellable = true)
private void beforeDatapacks(CallbackInfo ci) private void beforeDatapacks(CallbackInfo ci)
{ {
if (!TickSpeed.process_entities) ci.cancel();
else
currentSection = CarpetProfiler.start_section(null, "Datapacks", CarpetProfiler.TYPE.GENERAL); currentSection = CarpetProfiler.start_section(null, "Datapacks", CarpetProfiler.TYPE.GENERAL);
} }
@@ -1,9 +1,13 @@
package carpet.mixins; package carpet.mixins;
import carpet.CarpetServer;
import carpet.helpers.TickSpeed;
import carpet.network.CarpetClient; import carpet.network.CarpetClient;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.world.ClientWorld;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@@ -11,9 +15,21 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MinecraftClient.class) @Mixin(MinecraftClient.class)
public class MinecraftClientMixin public class MinecraftClientMixin
{ {
@Shadow public ClientWorld world;
@Inject(method = "disconnect(Lnet/minecraft/client/gui/screen/Screen;)V", at = @At("HEAD")) @Inject(method = "disconnect(Lnet/minecraft/client/gui/screen/Screen;)V", at = @At("HEAD"))
private void onCloseGame(Screen screen, CallbackInfo ci) private void onCloseGame(Screen screen, CallbackInfo ci)
{ {
CarpetClient.disconnect(); CarpetClient.disconnect();
} }
@Inject(at = @At("HEAD"), method = "tick")
private void onClientTick(CallbackInfo info) {
if (this.world != null) {
if (CarpetServer.minecraft_server == null)
TickSpeed.tick();
if (!TickSpeed.process_entities)
CarpetClient.shapes.renewShapes();
}
}
} }
@@ -1,6 +1,7 @@
package carpet.mixins; package carpet.mixins;
import carpet.fakes.MinecraftServerInterface; import carpet.fakes.MinecraftServerInterface;
import carpet.helpers.TickSpeed;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.ServerTask; import net.minecraft.server.ServerTask;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ServerWorld;
@@ -69,6 +70,8 @@ public abstract class MinecraftServer_scarpetMixin extends ReentrantThreadExecut
)) ))
public void tickTasks(BooleanSupplier booleanSupplier_1, CallbackInfo ci) public void tickTasks(BooleanSupplier booleanSupplier_1, CallbackInfo ci)
{ {
if (!TickSpeed.process_entities)
return;
TICK.onTick(); TICK.onTick();
NETHER_TICK.onTick(); NETHER_TICK.onTick();
ENDER_TICK.onTick(); ENDER_TICK.onTick();
@@ -1,5 +1,6 @@
package carpet.mixins; package carpet.mixins;
import carpet.fakes.ServerPlayerEntityInterface;
import net.minecraft.server.PlayerManager; import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -17,4 +18,11 @@ public class PlayerManager_scarpetEventsMixin
{ {
PLAYER_RESPAWNS.onPlayerEvent(player); PLAYER_RESPAWNS.onPlayerEvent(player);
} }
@Inject(method = "respawnPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerEntity;onSpawn()V"))
private void invalidatePreviousInstance(ServerPlayerEntity player, boolean alive, CallbackInfoReturnable<ServerPlayerEntity> cir)
{
((ServerPlayerEntityInterface)player).invalidateEntityObjectReference();
}
} }
@@ -0,0 +1,12 @@
package carpet.mixins;
import net.minecraft.scoreboard.ScoreboardCriterion;
import net.minecraft.scoreboard.ScoreboardObjective;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(ScoreboardObjective.class)
public interface ScoreboardObjective_scarpetMixin {
@Accessor("criterion")
void setCriterion(ScoreboardCriterion criterion);
}
@@ -0,0 +1,16 @@
package carpet.mixins;
import net.minecraft.scoreboard.Scoreboard;
import net.minecraft.scoreboard.ScoreboardCriterion;
import net.minecraft.scoreboard.ScoreboardObjective;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.List;
import java.util.Map;
@Mixin(Scoreboard.class)
public interface Scoreboard_scarpetMixin {
@Accessor("objectivesByCriterion")
Map<ScoreboardCriterion, List<ScoreboardObjective>> getObjectivesByCriterion();
}
@@ -1,6 +1,7 @@
package carpet.mixins; package carpet.mixins;
import carpet.fakes.EntityInterface; import carpet.fakes.EntityInterface;
import carpet.fakes.ServerPlayerEntityInterface;
import carpet.script.EntityEventsGroup; import carpet.script.EntityEventsGroup;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
@@ -28,8 +29,11 @@ import static carpet.script.CarpetEventServer.Event.PLAYER_FINISHED_USING_ITEM;
import static carpet.script.CarpetEventServer.Event.STATISTICS; import static carpet.script.CarpetEventServer.Event.STATISTICS;
@Mixin(ServerPlayerEntity.class) @Mixin(ServerPlayerEntity.class)
public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity implements ServerPlayerEntityInterface
{ {
// to denote if the player reference is valid
private boolean isInvalidReference = false;
public ServerPlayerEntity_scarpetEventMixin(World world, BlockPos blockPos, float f, GameProfile gameProfile) public ServerPlayerEntity_scarpetEventMixin(World world, BlockPos blockPos, float f, GameProfile gameProfile)
{ {
@@ -109,6 +113,17 @@ public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity
} }
PLAYER_CHANGES_DIMENSION.onDimensionChange(player, previousLocation, to, previousDimension, destination.getRegistryKey()); PLAYER_CHANGES_DIMENSION.onDimensionChange(player, previousLocation, to, previousDimension, destination.getRegistryKey());
} }
};
@Override
public void invalidateEntityObjectReference()
{
isInvalidReference = true;
} }
@Override
public boolean isInvalidEntityObject()
{
return isInvalidReference;
}
} }
@@ -9,6 +9,7 @@ import carpet.settings.SettingsManager;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.nbt.AbstractNumberTag; import net.minecraft.nbt.AbstractNumberTag;
import net.minecraft.nbt.ByteTag;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag; import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
@@ -65,6 +66,14 @@ public class ClientNetworkHandler
} }
}); });
dataHandlers.put("TickRate", (p, t) -> TickSpeed.tickrate(((AbstractNumberTag)t).getFloat(), false)); dataHandlers.put("TickRate", (p, t) -> TickSpeed.tickrate(((AbstractNumberTag)t).getFloat(), false));
dataHandlers.put("TickingState", (p, t) -> {
CompoundTag tickingState = (CompoundTag)t;
TickSpeed.setFrozenState(tickingState.getBoolean("is_paused"), tickingState.getBoolean("deepFreeze"));
});
dataHandlers.put("SuperHotState", (p, t) -> {
TickSpeed.is_superHot = ((ByteTag) t).equals(ByteTag.ONE);
});
dataHandlers.put("TickPlayerActiveTimeout", (p, t) -> TickSpeed.player_active_timeout = ((AbstractNumberTag)t).getInt());
dataHandlers.put("scShape", (p, t) -> { // deprecated dataHandlers.put("scShape", (p, t) -> { // deprecated
if (CarpetClient.shapes != null) if (CarpetClient.shapes != null)
CarpetClient.shapes.addShape((CompoundTag)t); CarpetClient.shapes.addShape((CompoundTag)t);
@@ -77,7 +77,7 @@ public class ServerNetworkHandler
CarpetSettings.LOG.info("Player "+playerEntity.getName().getString()+" joined with a matching carpet client"); CarpetSettings.LOG.info("Player "+playerEntity.getName().getString()+" joined with a matching carpet client");
else else
CarpetSettings.LOG.warn("Player "+playerEntity.getName().getString()+" joined with another carpet version: "+clientVersion); CarpetSettings.LOG.warn("Player "+playerEntity.getName().getString()+" joined with another carpet version: "+clientVersion);
DataBuilder data = DataBuilder.create().withTickRate(); DataBuilder data = DataBuilder.create().withTickRate().withFrozenState().withTickPlayerActiveTimeout(); // .withSuperHotState()
CarpetServer.settingsManager.getRules().forEach(data::withRule); CarpetServer.settingsManager.getRules().forEach(data::withRule);
CarpetServer.extensions.forEach(e -> { CarpetServer.extensions.forEach(e -> {
SettingsManager eManager = e.customSettingsManager(); SettingsManager eManager = e.customSettingsManager();
@@ -173,6 +173,42 @@ public class ServerNetworkHandler
} }
} }
public static void updateFrozenStateToConnectedPlayers()
{
if (CarpetSettings.superSecretSetting) return;
for (ServerPlayerEntity player : remoteCarpetPlayers.keySet())
{
player.networkHandler.sendPacket(new CustomPayloadS2CPacket(
CarpetClient.CARPET_CHANNEL,
DataBuilder.create().withFrozenState().build()
));
}
}
public static void updateSuperHotStateToConnectedPlayers()
{
if(CarpetSettings.superSecretSetting) return;
for (ServerPlayerEntity player : remoteCarpetPlayers.keySet())
{
player.networkHandler.sendPacket(new CustomPayloadS2CPacket(
CarpetClient.CARPET_CHANNEL,
DataBuilder.create().withSuperHotState().build()
));
}
}
public static void updateTickPlayerActiveTimeoutToConnectedPlayers()
{
if (CarpetSettings.superSecretSetting) return;
for (ServerPlayerEntity player : remoteCarpetPlayers.keySet())
{
player.networkHandler.sendPacket(new CustomPayloadS2CPacket(
CarpetClient.CARPET_CHANNEL,
DataBuilder.create().withTickPlayerActiveTimeout().build()
));
}
}
public static void broadcastCustomCommand(String command, Tag data) public static void broadcastCustomCommand(String command, Tag data)
{ {
if (CarpetSettings.superSecretSetting) return; if (CarpetSettings.superSecretSetting) return;
@@ -240,6 +276,24 @@ public class ServerNetworkHandler
tag.putFloat("TickRate", TickSpeed.tickrate); tag.putFloat("TickRate", TickSpeed.tickrate);
return this; return this;
} }
private DataBuilder withFrozenState()
{
CompoundTag tickingState = new CompoundTag();
tickingState.putBoolean("is_paused", TickSpeed.isPaused());
tickingState.putBoolean("deepFreeze", TickSpeed.deeplyFrozen());
tag.put("TickingState", tickingState);
return this;
}
private DataBuilder withSuperHotState()
{
tag.putBoolean("SuperHotState", TickSpeed.is_superHot);
return this;
}
private DataBuilder withTickPlayerActiveTimeout()
{
tag.putInt("TickPlayerActiveTimeout", TickSpeed.player_active_timeout);
return this;
}
private DataBuilder withRule(ParsedRule<?> rule) private DataBuilder withRule(ParsedRule<?> rule)
{ {
CompoundTag rules = (CompoundTag) tag.get("Rules"); CompoundTag rules = (CompoundTag) tag.get("Rules");
@@ -2,6 +2,7 @@ package carpet.script;
import carpet.CarpetServer; import carpet.CarpetServer;
import carpet.CarpetSettings; import carpet.CarpetSettings;
import carpet.helpers.TickSpeed;
import carpet.script.exception.InternalExpressionException; import carpet.script.exception.InternalExpressionException;
import carpet.script.value.BlockValue; import carpet.script.value.BlockValue;
import carpet.script.value.EntityValue; import carpet.script.value.EntityValue;
@@ -973,6 +974,8 @@ public class CarpetEventServer
public void tick() public void tick()
{ {
if (!TickSpeed.process_entities)
return;
Iterator<ScheduledCall> eventIterator = scheduledCalls.iterator(); Iterator<ScheduledCall> eventIterator = scheduledCalls.iterator();
List<ScheduledCall> currentCalls = new ArrayList<>(); List<ScheduledCall> currentCalls = new ArrayList<>();
while(eventIterator.hasNext()) while(eventIterator.hasNext())
@@ -18,6 +18,8 @@ import carpet.script.value.NumericValue;
import carpet.script.value.StringValue; import carpet.script.value.StringValue;
import carpet.script.value.Value; import carpet.script.value.Value;
import carpet.utils.Messenger; import carpet.utils.Messenger;
import com.google.gson.JsonElement;
import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
@@ -705,7 +707,7 @@ public class CarpetScriptHost extends ScriptHost
public boolean removeResourceFile(String resource, boolean isShared, String type) public boolean removeResourceFile(String resource, boolean isShared, String type)
{ {
if (getName() == null && !isShared) return false; // if (getName() == null && !isShared) return false; //
return Module.dropExistingFile(main, resource, type.equals("nbt")?"nbt":"txt", isShared); return Module.dropExistingFile(main, resource, type, isShared);
} }
public boolean appendLogFile(String resource, boolean isShared, String type, List<String> data) public boolean appendLogFile(String resource, boolean isShared, String type, List<String> data)
@@ -714,16 +716,22 @@ public class CarpetScriptHost extends ScriptHost
return Module.appendToTextFile(main, resource, type, isShared, data); return Module.appendToTextFile(main, resource, type, isShared, data);
} }
public List<String> readTextResource(String resource, boolean isShared) public List<String> readTextResource(String resource, String type, boolean isShared)
{ {
if (getName() == null && !isShared) return null; // if (getName() == null && !isShared) return null;
return Module.listFile(main, resource, "txt", isShared); return Module.listFile(main, resource, type, isShared);
}
public JsonElement readJsonFile(String resource, String type, boolean isShared)
{
if (getName() == null && !isShared) return null;
return Module.readJsonFile(main, resource, type, isShared);
} }
public Stream<String> listFolder(String resource, String ext, boolean isShared) public Stream<String> listFolder(String resource, String type, boolean isShared)
{ {
if (getName() == null && !isShared) return null; // if (getName() == null && !isShared) return null; //
return Module.listFolder(main, resource, ext, isShared); return Module.listFolder(main, resource, type, isShared);
} }
+1 -1
View File
@@ -91,7 +91,7 @@ public abstract class ScriptHost
protected ScriptHost parent; protected ScriptHost parent;
protected boolean perUser; protected boolean perUser;
protected String user; public String user;
public String getName() {return main ==null?null: main.getName();} public String getName() {return main ==null?null: main.getName();}
+38 -11
View File
@@ -6,6 +6,7 @@ import carpet.fakes.MinecraftServerInterface;
import carpet.fakes.StatTypeInterface; import carpet.fakes.StatTypeInterface;
import carpet.fakes.ThreadedAnvilChunkStorageInterface; import carpet.fakes.ThreadedAnvilChunkStorageInterface;
import carpet.helpers.FeatureGenerator; import carpet.helpers.FeatureGenerator;
import carpet.script.bundled.Module;
import carpet.script.CarpetContext; import carpet.script.CarpetContext;
import carpet.script.CarpetEventServer; import carpet.script.CarpetEventServer;
import carpet.script.CarpetScriptHost; import carpet.script.CarpetScriptHost;
@@ -19,6 +20,7 @@ import carpet.script.argument.Vector3Argument;
import carpet.script.exception.ExitStatement; import carpet.script.exception.ExitStatement;
import carpet.script.exception.InternalExpressionException; import carpet.script.exception.InternalExpressionException;
import carpet.script.utils.FixedCommandSource; import carpet.script.utils.FixedCommandSource;
import carpet.script.utils.ScarpetJsonDeserializer;
import carpet.script.utils.ShapeDispatcher; import carpet.script.utils.ShapeDispatcher;
import carpet.script.utils.WorldTools; import carpet.script.utils.WorldTools;
import carpet.script.value.EntityValue; import carpet.script.value.EntityValue;
@@ -33,7 +35,6 @@ import carpet.script.value.StringValue;
import carpet.script.value.Value; import carpet.script.value.Value;
import carpet.script.value.ValueConversions; import carpet.script.value.ValueConversions;
import carpet.utils.Messenger; import carpet.utils.Messenger;
import com.google.common.collect.ImmutableMap;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType; import net.minecraft.entity.EntityType;
@@ -69,6 +70,11 @@ import net.minecraft.world.World;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple; import org.apache.commons.lang3.tuple.Triple;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@@ -86,12 +92,7 @@ import static java.lang.Math.min;
public class Auxiliary { public class Auxiliary {
public static final String MARKER_STRING = "__scarpet_marker"; public static final String MARKER_STRING = "__scarpet_marker";
private static final Map<String, SoundCategory> mixerMap = Arrays.stream(SoundCategory.values()).collect(Collectors.toMap(SoundCategory::getName, k -> k)); private static final Map<String, SoundCategory> mixerMap = Arrays.stream(SoundCategory.values()).collect(Collectors.toMap(SoundCategory::getName, k -> k));
private static final Map<String, String> supportedTypes = ImmutableMap.of( public static final Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().registerTypeAdapter(Value.class, new ScarpetJsonDeserializer()).create();
"raw", ".txt",
"text", ".txt",
"nbt", ".nbt",
"folder", "folder"
);
public static String recognizeResource(Value value, boolean isFloder) public static String recognizeResource(Value value, boolean isFloder)
{ {
@@ -112,7 +113,7 @@ public class Auxiliary {
String origtype = lv.get(1).evalValue(c).getString().toLowerCase(Locale.ROOT); String origtype = lv.get(1).evalValue(c).getString().toLowerCase(Locale.ROOT);
boolean shared = origtype.startsWith("shared_"); boolean shared = origtype.startsWith("shared_");
String type = shared ? origtype.substring(7) : origtype; //len(shared_) String type = shared ? origtype.substring(7) : origtype; //len(shared_)
if (!supportedTypes.containsKey(type)) if (!Module.supportedTypes.containsKey(type))
throw new InternalExpressionException("Unsupported file type: "+origtype); throw new InternalExpressionException("Unsupported file type: "+origtype);
if (type.equals("folder") && !isFloder) if (type.equals("folder") && !isFloder)
throw new InternalExpressionException("Folder types are no supported for this IO function"); throw new InternalExpressionException("Folder types are no supported for this IO function");
@@ -526,7 +527,7 @@ public class Auxiliary {
Text title = null; Text title = null;
if (lv.size() > 2) if (lv.size() > 2)
{ {
pVal = lv.get(2).evalValue(c); pVal = lv.get(2).evalValue(c);
if (pVal instanceof FormattedTextValue) if (pVal instanceof FormattedTextValue)
title = ((FormattedTextValue) pVal).getText(); title = ((FormattedTextValue) pVal).getText();
else else
@@ -803,7 +804,7 @@ public class Auxiliary {
expression.addLazyFunction("list_files", 2, (c, t, lv) -> expression.addLazyFunction("list_files", 2, (c, t, lv) ->
{ {
Triple<String, String, Boolean> fdesc = getFileDescriptor(lv, c, true); Triple<String, String, Boolean> fdesc = getFileDescriptor(lv, c, true);
Stream<String> files = ((CarpetScriptHost) c.host).listFolder(fdesc.getLeft(), supportedTypes.get(fdesc.getMiddle()), fdesc.getRight()); Stream<String> files = ((CarpetScriptHost) c.host).listFolder(fdesc.getLeft(), fdesc.getMiddle(), fdesc.getRight());
if (files == null) return LazyValue.NULL; if (files == null) return LazyValue.NULL;
Value ret = ListValue.wrap(files.map(StringValue::of).collect(Collectors.toList())); Value ret = ListValue.wrap(files.map(StringValue::of).collect(Collectors.toList()));
return (cc, tt) -> ret; return (cc, tt) -> ret;
@@ -820,9 +821,29 @@ public class Auxiliary {
if (state == null) return LazyValue.NULL; if (state == null) return LazyValue.NULL;
retVal = new NBTSerializableValue(state); retVal = new NBTSerializableValue(state);
} }
else if (fdesc.getMiddle().equals("json"))
{
JsonElement json;
try
{
json = ((CarpetScriptHost) c.host).readJsonFile(fdesc.getLeft(), fdesc.getMiddle(), fdesc.getRight());
}
catch (JsonParseException e)
{
Throwable exception = e;
if(e.getCause() != null)
exception = e.getCause();
throw new InternalExpressionException("Failed to read JSON file: "+exception.getMessage());
}
Value parsedJson = gson.fromJson(json, Value.class);
if (parsedJson == null)
retVal = Value.NULL;
else
retVal = parsedJson;
}
else else
{ {
List<String> content = ((CarpetScriptHost) c.host).readTextResource(fdesc.getLeft(), fdesc.getRight()); List<String> content = ((CarpetScriptHost) c.host).readTextResource(fdesc.getLeft(), fdesc.getMiddle(), fdesc.getRight());
if (content == null) return LazyValue.NULL; if (content == null) return LazyValue.NULL;
retVal = ListValue.wrap(content.stream().map(StringValue::new).collect(Collectors.toList())); retVal = ListValue.wrap(content.stream().map(StringValue::new).collect(Collectors.toList()));
} }
@@ -851,6 +872,12 @@ public class Auxiliary {
Tag tag = tagValue.getTag(); Tag tag = tagValue.getTag();
success = ((CarpetScriptHost) c.host).writeTagFile(tag, fdesc.getLeft(), fdesc.getRight()); success = ((CarpetScriptHost) c.host).writeTagFile(tag, fdesc.getLeft(), fdesc.getRight());
} }
else if (fdesc.getMiddle().equals("json"))
{
List<String> data = Collections.singletonList(gson.toJson(lv.get(2).evalValue(c).toJson()));
((CarpetScriptHost) c.host).removeResourceFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle());
success = ((CarpetScriptHost) c.host).appendLogFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle(), data);
}
else else
{ {
List<String> data = new ArrayList<>(); List<String> data = new ArrayList<>();
+11 -4
View File
@@ -45,10 +45,17 @@ public class Entities {
{ {
expression.addLazyFunction("player", -1, (c, t, lv) -> expression.addLazyFunction("player", -1, (c, t, lv) ->
{ {
if (lv.size() ==0) if (lv.size() == 0)
{ {
CarpetContext cc = (CarpetContext)c;
Entity callingEntity = ((CarpetContext)c).s.getEntity(); if (cc.host.user != null)
{
ServerPlayerEntity player = cc.s.getMinecraftServer().getPlayerManager().getPlayer(cc.host.user);
if (player == null) return LazyValue.NULL;
Value ret = new EntityValue(player);
return (_c, _t) -> ret;
}
Entity callingEntity = cc.s.getEntity();
if (callingEntity instanceof PlayerEntity) if (callingEntity instanceof PlayerEntity)
{ {
Value retval = new EntityValue(callingEntity); Value retval = new EntityValue(callingEntity);
@@ -108,7 +115,7 @@ public class Entities {
retval = new EntityValue(player); retval = new EntityValue(player);
} }
Value finalVar = retval; Value finalVar = retval;
return (cc, tt) -> finalVar; return (_c, _t) -> finalVar;
}); });
expression.addLazyFunction("spawn", -1, (c, t, lv) -> expression.addLazyFunction("spawn", -1, (c, t, lv) ->
@@ -1,5 +1,7 @@
package carpet.script.api; package carpet.script.api;
import carpet.mixins.ScoreboardObjective_scarpetMixin;
import carpet.mixins.Scoreboard_scarpetMixin;
import carpet.script.CarpetContext; import carpet.script.CarpetContext;
import carpet.script.Expression; import carpet.script.Expression;
import carpet.script.LazyValue; import carpet.script.LazyValue;
@@ -11,6 +13,7 @@ import carpet.script.value.NumericValue;
import carpet.script.value.StringValue; import carpet.script.value.StringValue;
import carpet.script.value.FormattedTextValue; import carpet.script.value.FormattedTextValue;
import carpet.script.value.Value; import carpet.script.value.Value;
import com.google.common.collect.Lists;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.boss.BossBar; import net.minecraft.entity.boss.BossBar;
import net.minecraft.entity.boss.BossBarManager; import net.minecraft.entity.boss.BossBarManager;
@@ -133,8 +136,15 @@ public class Scoreboards {
} }
ScoreboardObjective objective = scoreboard.getObjective(objectiveName); ScoreboardObjective objective = scoreboard.getObjective(objectiveName);
if (objective != null) if (objective != null) {
if(lv.size() == 1) return (_c, _t) -> StringValue.of(objective.getCriterion().getName());
if(objective.getCriterion().equals(criterion) || lv.size() == 1) return LazyValue.NULL;
((Scoreboard_scarpetMixin)scoreboard).getObjectivesByCriterion().get(objective.getCriterion()).remove(objective);
((ScoreboardObjective_scarpetMixin) objective).setCriterion(criterion);
(((Scoreboard_scarpetMixin)scoreboard).getObjectivesByCriterion().computeIfAbsent(criterion, (criterion1) -> Lists.newArrayList())).add(objective);
scoreboard.updateObjective(objective);
return LazyValue.FALSE; return LazyValue.FALSE;
}
scoreboard.addObjective(objectiveName, criterion, new LiteralText(objectiveName), criterion.getDefaultRenderType()); scoreboard.addObjective(objectiveName, criterion, new LiteralText(objectiveName), criterion.getDefaultRenderType());
return LazyValue.TRUE; return LazyValue.TRUE;
@@ -25,6 +25,10 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
public class FileModule extends Module public class FileModule extends Module
{ {
private String name; private String name;
@@ -137,11 +141,11 @@ public class FileModule extends Module
return true; return true;
} }
public static boolean appendText(File filePath, boolean addNewLines, List<String> data) public static boolean appendText(Path filePath, boolean addNewLines, List<String> data)
{ {
try try
{ {
OutputStream out = Files.newOutputStream(filePath.toPath(), StandardOpenOption.APPEND, StandardOpenOption.CREATE); OutputStream out = Files.newOutputStream(filePath, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8))) try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)))
{ {
for (String line: data) for (String line: data)
@@ -159,9 +163,9 @@ public class FileModule extends Module
} }
public static List<String> listFileContent(File filePath) public static List<String> listFileContent(Path filePath)
{ {
try (BufferedReader reader = Files.newBufferedReader(filePath.toPath(), StandardCharsets.UTF_8)) { try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) {
List<String> result = new ArrayList<>(); List<String> result = new ArrayList<>();
for (;;) { for (;;) {
String line = reader.readLine(); String line = reader.readLine();
@@ -176,6 +180,18 @@ public class FileModule extends Module
return null; return null;
} }
} }
public static JsonElement readJsonContent(Path filePath)
{
try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8))
{
return new JsonParser().parse(new JsonReader(reader));
}
catch (IOException e)
{
return null;
}
}
public static Stream<Path> listFiles(Path dir, String ext) public static Stream<Path> listFiles(Path dir, String ext)
{ {
+30 -11
View File
@@ -5,9 +5,13 @@ import net.minecraft.nbt.Tag;
import net.minecraft.util.WorldSavePath; import net.minecraft.util.WorldSavePath;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonElement;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Stream; import java.util.stream.Stream;
public abstract class Module public abstract class Module
@@ -16,6 +20,13 @@ public abstract class Module
public abstract String getCode(); public abstract String getCode();
public abstract boolean isLibrary(); public abstract boolean isLibrary();
public final static Object writeIOSync = new Object(); public final static Object writeIOSync = new Object();
public final static Map<String, String> supportedTypes = ImmutableMap.of(
"raw", ".txt",
"text", ".txt",
"nbt", ".nbt",
"json", ".json",
"folder", "folder"
);
private static String getDescriptor(Module module, String file, boolean isShared) private static String getDescriptor(Module module, String file, boolean isShared)
{ {
@@ -32,7 +43,7 @@ public abstract class Module
public static Tag getData(Module module, String file, boolean isShared) public static Tag getData(Module module, String file, boolean isShared)
{ {
Path dataFile = resolveResource(module, file, "nbt", isShared); Path dataFile = resolveResource(module, file, ".nbt", isShared);
if (dataFile == null) return null; if (dataFile == null) return null;
if (!Files.exists(dataFile) || !(dataFile.toFile().isFile())) return null; if (!Files.exists(dataFile) || !(dataFile.toFile().isFile())) return null;
synchronized (writeIOSync) { return FileModule.read(dataFile.toFile()); } synchronized (writeIOSync) { return FileModule.read(dataFile.toFile()); }
@@ -40,7 +51,7 @@ public abstract class Module
public static boolean saveData(Module module, String file, Tag globalState, boolean isShared) public static boolean saveData(Module module, String file, Tag globalState, boolean isShared)
{ {
Path dataFile = resolveResource(module, file, "nbt", isShared); Path dataFile = resolveResource(module, file, ".nbt", isShared);
if (dataFile == null) return false; if (dataFile == null) return false;
if (!Files.exists(dataFile.getParent()) && !dataFile.toFile().getParentFile().mkdirs()) return false; if (!Files.exists(dataFile.getParent()) && !dataFile.toFile().getParentFile().mkdirs()) return false;
synchronized (writeIOSync) { return FileModule.write(globalState, dataFile.toFile()); } synchronized (writeIOSync) { return FileModule.write(globalState, dataFile.toFile()); }
@@ -48,30 +59,38 @@ public abstract class Module
public static boolean appendToTextFile(Module module, String resourceName, String type, boolean isShared, List<String> message) public static boolean appendToTextFile(Module module, String resourceName, String type, boolean isShared, List<String> message)
{ {
Path dataFile = resolveResource(module, resourceName, "txt", isShared); Path dataFile = resolveResource(module, resourceName, supportedTypes.get(type), isShared);
if (dataFile == null) return false; if (dataFile == null) return false;
if (!Files.exists(dataFile.getParent()) && !dataFile.toFile().getParentFile().mkdirs()) return false; if (!Files.exists(dataFile.getParent()) && !dataFile.toFile().getParentFile().mkdirs()) return false;
synchronized (writeIOSync) { return FileModule.appendText(dataFile.toFile(), type.equals("text"), message); } synchronized (writeIOSync) { return FileModule.appendText(dataFile, type.equals("text"), message); }
} }
public static boolean dropExistingFile(Module module, String resourceName, String ext, boolean isShared) public static boolean dropExistingFile(Module module, String resourceName, String type, boolean isShared)
{ {
Path dataFile = resolveResource(module, resourceName, ext, isShared); Path dataFile = resolveResource(module, resourceName, supportedTypes.get(type), isShared);
if (dataFile == null) return false; if (dataFile == null) return false;
synchronized (writeIOSync) { return dataFile.toFile().delete(); } synchronized (writeIOSync) { return dataFile.toFile().delete(); }
} }
public static List<String> listFile(Module module, String resourceName, String ext, boolean isShared) public static List<String> listFile(Module module, String resourceName, String type, boolean isShared)
{ {
Path dataFile = resolveResource(module, resourceName, ext, isShared); Path dataFile = resolveResource(module, resourceName, supportedTypes.get(type), isShared);
if (dataFile == null) return null; if (dataFile == null) return null;
if (!dataFile.toFile().exists()) return null; if (!dataFile.toFile().exists()) return null;
synchronized (writeIOSync) { return FileModule.listFileContent(dataFile.toFile()); } synchronized (writeIOSync) { return FileModule.listFileContent(dataFile); }
}
public static JsonElement readJsonFile(Module module, String resourceName, String type, boolean isShared) {
Path dataFile = resolveResource(module, resourceName, supportedTypes.get(type), isShared);
if (dataFile == null) return null;
if (!dataFile.toFile().exists()) return null;
synchronized (writeIOSync) { return FileModule.readJsonContent(dataFile); }
} }
public static Stream<String> listFolder(Module module, String resourceName, String ext, boolean isShared) public static Stream<String> listFolder(Module module, String resourceName, String type, boolean isShared)
{ {
Path dir = resolveResource(module, resourceName, null, isShared); Path dir = resolveResource(module, resourceName, null, isShared);
String ext = supportedTypes.get(type);
if (dir == null) return null; if (dir == null) return null;
if (!Files.exists(dir)) return null; if (!Files.exists(dir)) return null;
Stream<Path> result; Stream<Path> result;
@@ -87,7 +106,7 @@ public abstract class Module
private static Path resolveResource(Module module, String resourceName, String ext, boolean isShared) private static Path resolveResource(Module module, String resourceName, String ext, boolean isShared)
{ {
if (!isShared && (module == null || module.getName() == null)) return null; if (!isShared && (module == null || module.getName() == null)) return null;
return CarpetServer.minecraft_server.getSavePath(WorldSavePath.ROOT).resolve("scripts/"+getDescriptor(module, resourceName, isShared)+(ext==null?"":"."+ext)); return CarpetServer.minecraft_server.getSavePath(WorldSavePath.ROOT).resolve("scripts/"+getDescriptor(module, resourceName, isShared)+(ext==null?"":ext));
} }
@Override @Override
@@ -315,13 +315,13 @@ public abstract class CommandArgument
public static CommandArgument buildFromConfig(String suffix, Map<String, Value> config, CarpetScriptHost host) public static CommandArgument buildFromConfig(String suffix, Map<String, Value> config, CarpetScriptHost host)
{ {
if (!config.containsKey("type")) if (!config.containsKey("type"))
throw new InternalExpressionException("Custom types should at least specify the type"); throw new InternalExpressionException("Custom type "+suffix+" should at least specify the type");
String baseType = config.get("type").getString(); String baseType = config.get("type").getString();
if (!builtIns.containsKey(baseType)) if (!builtIns.containsKey(baseType))
throw new InternalExpressionException("Unknown base type: "+baseType); throw new InternalExpressionException("Unknown base type "+baseType+" for custom type "+suffix);
CommandArgument variant = builtIns.get(baseType).factory().get(); CommandArgument variant = builtIns.get(baseType).factory().get();
variant.configure(config, host);
variant.suffix = suffix; variant.suffix = suffix;
variant.configure(config, host);
return variant; return variant;
} }
@@ -334,9 +334,9 @@ public abstract class CommandArgument
} }
if (config.containsKey("suggest")) if (config.containsKey("suggest"))
{ {
if (config.containsKey("suggester")) throw new InternalExpressionException("Attempted to provide 'suggest' list while 'suggester' is present"); if (config.containsKey("suggester")) throw new InternalExpressionException("Attempted to provide 'suggest' list while 'suggester' is present"+" for custom type "+suffix);
Value suggestionValue = config.get("suggest"); Value suggestionValue = config.get("suggest");
if (!(suggestionValue instanceof ListValue)) throw new InternalExpressionException("Argument suggestions needs to be a list"); if (!(suggestionValue instanceof ListValue)) throw new InternalExpressionException("Argument suggestions needs to be a list"+" for custom type "+suffix);
examples = ((ListValue) suggestionValue).getItems().stream() examples = ((ListValue) suggestionValue).getItems().stream()
.map(Value::getString) .map(Value::getString)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
@@ -378,7 +378,7 @@ public abstract class CommandArgument
args.add(MapValue.wrap(params)); args.add(MapValue.wrap(params));
args.addAll(customSuggester.args); args.addAll(customSuggester.args);
Value response = host.handleCommand(context.getSource(), customSuggester.function, args); Value response = host.handleCommand(context.getSource(), customSuggester.function, args);
if (!(response instanceof ListValue)) throw error("Custom suggester should return a list of options"); if (!(response instanceof ListValue)) throw error("Custom suggester should return a list of options"+" for custom type "+suffix);
return ((ListValue) response).getItems().stream().map(Value::getString).collect(Collectors.toList()); return ((ListValue) response).getItems().stream().map(Value::getString).collect(Collectors.toList());
} }
if (needsMatching) return examples; if (needsMatching) return examples;
@@ -426,7 +426,7 @@ public abstract class CommandArgument
if (!caseSensitive) choseValue = choseValue.toLowerCase(Locale.ROOT); if (!caseSensitive) choseValue = choseValue.toLowerCase(Locale.ROOT);
if (!validOptions.isEmpty() && !validOptions.contains(choseValue)) if (!validOptions.isEmpty() && !validOptions.contains(choseValue))
{ {
throw new SimpleCommandExceptionType(new LiteralText("Incorrect value for "+param+": "+choseValue)).create(); throw new SimpleCommandExceptionType(new LiteralText("Incorrect value for "+param+": "+choseValue+" for custom type "+suffix)).create();
} }
return StringValue.of(choseValue); return StringValue.of(choseValue);
} }
@@ -438,7 +438,7 @@ public abstract class CommandArgument
if (config.containsKey("options")) if (config.containsKey("options"))
{ {
Value optionsValue = config.get("options"); Value optionsValue = config.get("options");
if (!(optionsValue instanceof ListValue)) throw new InternalExpressionException("Custom string type requires options passed as a list"); if (!(optionsValue instanceof ListValue)) throw new InternalExpressionException("Custom string type requires options passed as a list"+" for custom type "+suffix);
validOptions = ((ListValue) optionsValue).getItems().stream() validOptions = ((ListValue) optionsValue).getItems().stream()
.map(v -> caseSensitive?v.getString():(v.getString().toLowerCase(Locale.ROOT))) .map(v -> caseSensitive?v.getString():(v.getString().toLowerCase(Locale.ROOT)))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
@@ -577,7 +577,7 @@ public abstract class CommandArgument
if (!single) return ListValue.wrap(founds.stream().map(EntityValue::new).collect(Collectors.toList())); if (!single) return ListValue.wrap(founds.stream().map(EntityValue::new).collect(Collectors.toList()));
if (founds.size() == 0) return Value.NULL; if (founds.size() == 0) return Value.NULL;
if (founds.size() == 1) return new EntityValue(founds.iterator().next()); if (founds.size() == 1) return new EntityValue(founds.iterator().next());
throw new SimpleCommandExceptionType(new LiteralText("Multiple entities returned while only one was requested")).create(); throw new SimpleCommandExceptionType(new LiteralText("Multiple entities returned while only one was requested"+" for custom type "+suffix)).create();
} }
@Override @Override
@@ -618,7 +618,7 @@ public abstract class CommandArgument
int size = profiles.size(); int size = profiles.size();
if (size == 0) return Value.NULL; if (size == 0) return Value.NULL;
if (size == 1) return StringValue.of(profiles.iterator().next().getName()); if (size == 1) return StringValue.of(profiles.iterator().next().getName());
throw new SimpleCommandExceptionType(new LiteralText("Multiple game profiles returned while only one was requested")).create(); throw new SimpleCommandExceptionType(new LiteralText("Multiple game profiles returned while only one was requested"+" for custom type "+suffix)).create();
} }
@Override @Override
@@ -659,7 +659,7 @@ public abstract class CommandArgument
int size = holders.size(); int size = holders.size();
if (size == 0) return Value.NULL; if (size == 0) return Value.NULL;
if (size == 1) return StringValue.of(holders.iterator().next()); if (size == 1) return StringValue.of(holders.iterator().next());
throw new SimpleCommandExceptionType(new LiteralText("Multiple score holders returned while only one was requested")).create(); throw new SimpleCommandExceptionType(new LiteralText("Multiple score holders returned while only one was requested"+" for custom type "+suffix)).create();
} }
@Override @Override
@@ -734,7 +734,7 @@ public abstract class CommandArgument
Identifier choseValue = IdentifierArgumentType.getIdentifier(context, param); Identifier choseValue = IdentifierArgumentType.getIdentifier(context, param);
if (!validOptions.isEmpty() && !validOptions.contains(choseValue)) if (!validOptions.isEmpty() && !validOptions.contains(choseValue))
{ {
throw new SimpleCommandExceptionType(new LiteralText("Incorrect value for "+param+": "+choseValue)).create(); throw new SimpleCommandExceptionType(new LiteralText("Incorrect value for "+param+": "+choseValue+" for custom type "+suffix)).create();
} }
return ValueConversions.of(choseValue); return ValueConversions.of(choseValue);
} }
@@ -752,7 +752,7 @@ public abstract class CommandArgument
if (config.containsKey("options")) if (config.containsKey("options"))
{ {
Value optionsValue = config.get("options"); Value optionsValue = config.get("options");
if (!(optionsValue instanceof ListValue)) throw new InternalExpressionException("Custom sting type requires options passed as a list"); if (!(optionsValue instanceof ListValue)) throw new InternalExpressionException("Custom sting type requires options passed as a list"+" for custom type "+suffix);
validOptions = ((ListValue) optionsValue).getItems().stream().map(v -> new Identifier(v.getString())).collect(Collectors.toSet()); validOptions = ((ListValue) optionsValue).getItems().stream().map(v -> new Identifier(v.getString())).collect(Collectors.toSet());
} }
} }
@@ -799,7 +799,7 @@ public abstract class CommandArgument
{ {
max = NumericValue.asNumber(config.get("max"), "max").getDouble(); max = NumericValue.asNumber(config.get("max"), "max").getDouble();
} }
if (max != null && min == null) throw new InternalExpressionException("Double types cannot be only upper-bounded"); if (max != null && min == null) throw new InternalExpressionException("Double types cannot be only upper-bounded"+" for custom type "+suffix);
} }
@Override @Override
@@ -850,7 +850,7 @@ public abstract class CommandArgument
{ {
max = NumericValue.asNumber(config.get("max"), "max").getLong(); max = NumericValue.asNumber(config.get("max"), "max").getLong();
} }
if (max != null && min == null) throw new InternalExpressionException("Double types cannot be only upper-bounded"); if (max != null && min == null) throw new InternalExpressionException("Double types cannot be only upper-bounded"+" for custom type "+suffix);
} }
@Override @Override
@@ -923,7 +923,7 @@ public abstract class CommandArgument
int slot = ItemSlotArgumentType.getItemSlot(context, param); int slot = ItemSlotArgumentType.getItemSlot(context, param);
if (restrict != null && !RESTRICTED_CONTAINERS.get(restrict).getLeft().contains(slot)) if (restrict != null && !RESTRICTED_CONTAINERS.get(restrict).getLeft().contains(slot))
{ {
throw new SimpleCommandExceptionType(new LiteralText("Incorrect slot restricted to "+restrict)).create(); throw new SimpleCommandExceptionType(new LiteralText("Incorrect slot restricted to "+restrict+" for custom type "+suffix)).create();
} }
return ValueConversions.ofVanillaSlotResult(slot); return ValueConversions.ofVanillaSlotResult(slot);
} }
@@ -937,7 +937,7 @@ public abstract class CommandArgument
restrict = config.get("restrict").getString().toLowerCase(Locale.ROOT); restrict = config.get("restrict").getString().toLowerCase(Locale.ROOT);
needsMatching = true; needsMatching = true;
if (!RESTRICTED_CONTAINERS.containsKey(restrict)) if (!RESTRICTED_CONTAINERS.containsKey(restrict))
throw new InternalExpressionException("Incorrect slot restriction: "+restrict); throw new InternalExpressionException("Incorrect slot restriction "+restrict+" for custom type "+suffix);
} }
} }
@@ -87,7 +87,7 @@ public class DataStructures {
throw new InternalExpressionException("'slice' takes 2 or 3 arguments"); throw new InternalExpressionException("'slice' takes 2 or 3 arguments");
Value hwat = lv.get(0); Value hwat = lv.get(0);
long from = NumericValue.asNumber(lv.get(1)).getLong(); long from = NumericValue.asNumber(lv.get(1)).getLong();
long to = -1; Long to = null;
if (lv.size()== 3) if (lv.size()== 3)
to = NumericValue.asNumber(lv.get(2)).getLong(); to = NumericValue.asNumber(lv.get(2)).getLong();
return hwat.slice(from, to); return hwat.slice(from, to);
@@ -0,0 +1,78 @@
package carpet.script.utils;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import carpet.script.value.ListValue;
import carpet.script.value.MapValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
public class ScarpetJsonDeserializer implements JsonDeserializer<Value>{
@Override
public Value deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
return parseElement(json);
}
private Value parseElement(JsonElement element) throws JsonParseException {
if (element.isJsonObject())
{
return parseMap(element.getAsJsonObject());
}
else if (element.isJsonArray())
{
return parseList(element.getAsJsonArray());
}
else if (element.isJsonPrimitive())
{
return parsePrimitive(element.getAsJsonPrimitive());
}
return Value.NULL;
}
private Value parseMap(JsonObject jsonMap) throws JsonParseException {
Map<Value, Value> map = new HashMap<Value, Value>();
jsonMap.entrySet().forEach(entry -> {
map.put(new StringValue(entry.getKey()), parseElement(entry.getValue()));
});
return MapValue.wrap(map);
}
private Value parseList(JsonArray jsonList) throws JsonParseException {
List<Value> list = new ArrayList<Value>();
jsonList.forEach(elem -> {
list.add(parseElement(elem));
});
return new ListValue(list);
}
private Value parsePrimitive(JsonPrimitive primitive) throws JsonParseException {
if (primitive.isString())
{
return new StringValue(primitive.getAsString());
}
else if (primitive.isBoolean())
{
return primitive.getAsBoolean() ? Value.TRUE : Value.FALSE;
}
else if (primitive.isNumber())
{
return NumericValue.of(primitive.getAsNumber());
}
return Value.NULL;
}
}
@@ -168,6 +168,15 @@ public class ShapesRenderer
} }
} }
public void renewShapes()
{
synchronized (shapes)
{
shapes.values().forEach(el -> el.values().forEach(shape -> {
shape.expiryTick++;
}));
}
}
public abstract static class RenderedShape<T extends ShapeDispatcher.ExpiringShape> public abstract static class RenderedShape<T extends ShapeDispatcher.ExpiringShape>
{ {
@@ -13,6 +13,7 @@ import carpet.settings.ParsedRule;
import carpet.settings.SettingsManager; import carpet.settings.SettingsManager;
import com.sun.management.OperatingSystemMXBean; import com.sun.management.OperatingSystemMXBean;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.SharedConstants;
import net.minecraft.util.WorldSavePath; import net.minecraft.util.WorldSavePath;
import net.minecraft.world.GameRules; import net.minecraft.world.GameRules;
@@ -56,6 +57,7 @@ public class SystemInfo {
put("game_view_distance", c -> new NumericValue(c.s.getMinecraftServer().getPlayerManager().getViewDistance())); put("game_view_distance", c -> new NumericValue(c.s.getMinecraftServer().getPlayerManager().getViewDistance()));
put("game_mod_name", c -> StringValue.of(c.s.getMinecraftServer().getServerModName())); put("game_mod_name", c -> StringValue.of(c.s.getMinecraftServer().getServerModName()));
put("game_version", c -> StringValue.of(c.s.getMinecraftServer().getVersion())); put("game_version", c -> StringValue.of(c.s.getMinecraftServer().getVersion()));
put("game_data_version", c->NumericValue.of(SharedConstants.getGameVersion().getWorldVersion()));
put("server_ip", c -> StringValue.of(c.s.getMinecraftServer().getServerIp())); put("server_ip", c -> StringValue.of(c.s.getMinecraftServer().getServerIp()));
put("server_whitelisted", c -> new NumericValue(c.s.getMinecraftServer().isEnforceWhitelist())); put("server_whitelisted", c -> new NumericValue(c.s.getMinecraftServer().isEnforceWhitelist()));
@@ -4,7 +4,7 @@ import carpet.script.exception.InternalExpressionException;
import java.util.Iterator; import java.util.Iterator;
public abstract class AbstractListValue extends Value public abstract class AbstractListValue extends Value implements Iterable<Value>
{ {
public abstract Iterator<Value> iterator(); public abstract Iterator<Value> iterator();
public void fatality() { } public void fatality() { }
@@ -7,6 +7,7 @@ import carpet.fakes.LivingEntityInterface;
import carpet.fakes.MemoryInterface; import carpet.fakes.MemoryInterface;
import carpet.fakes.MobEntityInterface; import carpet.fakes.MobEntityInterface;
import carpet.fakes.HungerManagerInterface; import carpet.fakes.HungerManagerInterface;
import carpet.fakes.ServerPlayerEntityInterface;
import carpet.fakes.ServerPlayerInteractionManagerInterface; import carpet.fakes.ServerPlayerInteractionManagerInterface;
import carpet.helpers.Tracer; import carpet.helpers.Tracer;
import carpet.network.ServerNetworkHandler; import carpet.network.ServerNetworkHandler;
@@ -91,7 +92,7 @@ import static carpet.utils.MobAI.genericJump;
// TODO: decide whether copy(entity) should duplicate entity in the world. // TODO: decide whether copy(entity) should duplicate entity in the world.
public class EntityValue extends Value public class EntityValue extends Value
{ {
private final Entity entity; private Entity entity;
public EntityValue(Entity e) public EntityValue(Entity e)
{ {
@@ -120,6 +121,11 @@ public class EntityValue extends Value
public Entity getEntity() public Entity getEntity()
{ {
if (entity instanceof ServerPlayerEntity && ((ServerPlayerEntityInterface)entity).isInvalidEntityObject())
{
ServerPlayerEntity newPlayer = entity.getServer().getPlayerManager().getPlayer(entity.getUuid());
if (newPlayer != null) entity = newPlayer;
}
return entity; return entity;
} }
@@ -171,7 +177,7 @@ public class EntityValue extends Value
@Override @Override
public String getString() public String getString()
{ {
return entity.getName().getString(); return getEntity().getName().getString();
} }
@Override @Override
@@ -185,7 +191,7 @@ public class EntityValue extends Value
{ {
if (v instanceof EntityValue) if (v instanceof EntityValue)
{ {
return entity.getEntityId()==((EntityValue) v).entity.getEntityId(); return getEntity().getEntityId()==((EntityValue) v).getEntity().getEntityId();
} }
return super.equals((Value)v); return super.equals((Value)v);
} }
@@ -221,7 +227,7 @@ public class EntityValue extends Value
@Override @Override
public int hashCode() public int hashCode()
{ {
return entity.hashCode(); return getEntity().hashCode();
} }
public static EntityClassDescriptor getEntityDescriptor(String who, MinecraftServer server) public static EntityClassDescriptor getEntityDescriptor(String who, MinecraftServer server)
@@ -389,7 +395,7 @@ public class EntityValue extends Value
throw new InternalExpressionException("Unknown entity feature: "+what); throw new InternalExpressionException("Unknown entity feature: "+what);
try try
{ {
return featureAccessors.get(what).apply(entity, arg); return featureAccessors.get(what).apply(getEntity(), arg);
} }
catch (NullPointerException npe) catch (NullPointerException npe)
{ {
@@ -850,7 +856,7 @@ public class EntityValue extends Value
throw new InternalExpressionException("Unknown entity action: " + what); throw new InternalExpressionException("Unknown entity action: " + what);
try try
{ {
featureModifiers.get(what).accept(entity, toWhat); featureModifiers.get(what).accept(getEntity(), toWhat);
} }
catch (NullPointerException npe) catch (NullPointerException npe)
{ {
@@ -1483,7 +1489,7 @@ public class EntityValue extends Value
EntityEventsGroup.Event event = EntityEventsGroup.Event.byName.get(eventName); EntityEventsGroup.Event event = EntityEventsGroup.Event.byName.get(eventName);
if (event == null) if (event == null)
throw new InternalExpressionException("Unknown entity event: " + eventName); throw new InternalExpressionException("Unknown entity event: " + eventName);
((EntityInterface)entity).getEventContainer().addEvent(event, cc.host, fun, args); ((EntityInterface)getEntity()).getEventContainer().addEvent(event, cc.host, fun, args);
} }
@Override @Override
@@ -1492,7 +1498,7 @@ public class EntityValue extends Value
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this); if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
CompoundTag tag = new CompoundTag(); CompoundTag tag = new CompoundTag();
tag.put("Data", getEntity().toTag( new CompoundTag())); tag.put("Data", getEntity().toTag( new CompoundTag()));
tag.put("Name", StringTag.of(Registry.ENTITY_TYPE.getId(entity.getType()).toString())); tag.put("Name", StringTag.of(Registry.ENTITY_TYPE.getId(getEntity().getType()).toString()));
return tag; return tag;
} }
} }
@@ -133,7 +133,7 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
} }
@Override @Override
public Value slice(long from, long to) public Value slice(long from, Long to)
{ {
throw new InternalExpressionException("Cannot slice a function"); throw new InternalExpressionException("Cannot slice a function");
} }
@@ -129,9 +129,9 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
} }
@Override @Override
public Value slice(long from, long to) public Value slice(long from, Long to)
{ {
if (to < 0) to = Integer.MAX_VALUE; if (to == null || to < 0) to = (long) Integer.MAX_VALUE;
if (from < 0) from = 0; if (from < 0) from = 0;
if (from > to) if (from > to)
return ListValue.of(); return ListValue.of();
@@ -289,27 +289,22 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
items.add(v); items.add(v);
} }
public void addAtIndex(int index, List<Value> subList) /**
* Finds a proper list index >=0 and < len that correspont to the rolling index value of idx
*
* @param idx
* @param len
* @return
*/
public static int normalizeIndex(long idx, int len)
{ {
int numitems = items.size(); if (idx >=0 && idx < len) return (int)idx;
long range = abs(index)/numitems; long range = abs(idx)/len;
index += (range+2)*numitems; idx += (range+2)*len;
index = index % numitems; idx = idx % len;
for (Value v: subList) return (int)idx;
{
if (index < numitems)
{
items.set(index, v);
}
else
{
items.add(v);
}
index++;
}
} }
public static class ListConstructorValue extends ListValue public static class ListConstructorValue extends ListValue
{ {
public ListConstructorValue(Collection<? extends Value> list) public ListConstructorValue(Collection<? extends Value> list)
@@ -339,15 +334,17 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
} }
@Override @Override
public Value slice(long from, long to) public Value slice(long fromDesc, Long toDesc)
{ {
List<Value> items = getItems(); List<Value> items = getItems();
int size = items.size(); int size = items.size();
if (to < 0 || to > size) to = size; int from = normalizeIndex(fromDesc, size);
if (from < 0 || from > size) from = size; if (toDesc == null)
return new ListValue((Collection<? extends Value>) getItems().subList(from, size));
int to = normalizeIndex(toDesc, size);
if (from > to) if (from > to)
return ListValue.of(); return ListValue.of();
return new ListValue((Collection<? extends Value>) getItems().subList((int)from, (int) to)); return new ListValue((Collection<? extends Value>) getItems().subList(from, to));
} }
@Override @Override
@@ -395,10 +392,8 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
return false; return false;
int index = (int)((NumericValue) ind).getLong(); int index = (int)((NumericValue) ind).getLong();
if (index < 0) if (index < 0)
{ {// only for values < 0
long range = abs(index) / numitems; index = normalizeIndex(index, numitems);
index += (range + 2) * numitems;
index = index % numitems;
} }
if (replace) if (replace)
{ {
@@ -424,11 +419,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
public Value get(Value value) public Value get(Value value)
{ {
long index = NumericValue.asNumber(value, "'address' to a list index").getLong(); long index = NumericValue.asNumber(value, "'address' to a list index").getLong();
int numitems = items.size(); return items.get(normalizeIndex(index, items.size()));
long range = abs(index)/numitems;
index += (range+2)*numitems;
index = index % numitems;
return items.get((int)index);
} }
@Override @Override
@@ -443,11 +434,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
{ {
if (!(where instanceof NumericValue) || items.isEmpty()) return false; if (!(where instanceof NumericValue) || items.isEmpty()) return false;
long index = ((NumericValue) where).getLong(); long index = ((NumericValue) where).getLong();
int numitems = items.size(); items.remove(normalizeIndex(index, items.size()));
long range = abs(index)/numitems;
index += (range+2)*numitems;
index = index % numitems;
items.remove((int)index);
return true; return true;
} }
@@ -198,7 +198,7 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
} }
@Override @Override
public Value slice(long from, long to) public Value slice(long from, Long to)
{ {
throw new InternalExpressionException("Cannot slice a map value"); throw new InternalExpressionException("Cannot slice a map value");
} }
@@ -5,6 +5,8 @@ import net.minecraft.nbt.Tag;
public class StringValue extends Value public class StringValue extends Value
{ {
public static Value EMPTY = StringValue.of("");
private String str; private String str;
@Override @Override
+6 -7
View File
@@ -178,16 +178,15 @@ public abstract class Value implements Comparable<Value>, Cloneable
return getString().length(); return getString().length();
} }
public Value slice(long from, long to) public Value slice(long fromDesc, Long toDesc)
{ {
String value = this.getString(); String value = this.getString();
int size = value.length(); int size = value.length();
if (to > size) to = -1; int from = ListValue.normalizeIndex(fromDesc, size);
if (from < 0) from = 0; if (toDesc == null) return new StringValue(value.substring(from));
if (from > size) from = size; int to = ListValue.normalizeIndex(toDesc, size);
if (to>=0) if (from > to) return StringValue.EMPTY;
return new StringValue(value.substring((int)from, (int)to)); return new StringValue(value.substring(from, to));
return new StringValue(value.substring((int)from));
} }
public double readDoubleNumber() public double readDoubleNumber()
{ {
+2
View File
@@ -166,6 +166,8 @@
"BlockItem_scarpetEventMixin", "BlockItem_scarpetEventMixin",
"PlayerManager_scarpetEventsMixin", "PlayerManager_scarpetEventsMixin",
"PlayerInventory_scarpetEventMixin", "PlayerInventory_scarpetEventMixin",
"ScoreboardObjective_scarpetMixin",
"Scoreboard_scarpetMixin",
"TradeOutputSlot_scarpetEventMixin", "TradeOutputSlot_scarpetEventMixin",
"ChunkTicketManager_scarpetMixin", "ChunkTicketManager_scarpetMixin",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"schemaVersion": 1, "schemaVersion": 1,
"id": "carpet", "id": "carpet",
"version": "1.4.21", "version": "${version}",
"name": "Carpet Mod in Fabric", "name": "Carpet Mod in Fabric",
"description": "Carpet made out of fabric", "description": "Carpet made out of fabric",