From 1abe93f0dc74ce227da8f26414d9d581e4544bd2 Mon Sep 17 00:00:00 2001 From: KingOfTheClouds <47048831+KingOfTheClouds@users.noreply.github.com> Date: Sat, 28 Nov 2020 20:21:21 -0500 Subject: [PATCH 1/9] Fix scarpet modify(e, 'effect', ...) (#587) --- docs/scarpet/api/Entities.md | 6 +++--- src/main/java/carpet/script/value/EntityValue.java | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/scarpet/api/Entities.md b/docs/scarpet/api/Entities.md index 794ecc6b..a60d426c 100644 --- a/docs/scarpet/api/Entities.md +++ b/docs/scarpet/api/Entities.md @@ -641,10 +641,10 @@ If called with `false` value, it will disable AI in the mob. `true` will enable Sets if the entity obeys any collisions, including collisions with the terrain and basic physics. Not affecting players, since they are controlled client side. -### `modify(e, 'effect', name?, duration?, amplifier?, show_particles?, show_icon?)` +### `modify(e, 'effect', name?, duration?, amplifier?, show_particles?, show_icon?, ambient?)` -Applies status effect to the living entity. Takes several optional parameters, which default to `0`, `true` -and `true`. If no duration is specified, or if it's null or 0, the effect is removed. If name is not specified, +Applies status effect to the living entity. Takes several optional parameters, which default to `0`, `true`, +`true` and `false`. If no duration is specified, or if it's null or 0, the effect is removed. If name is not specified, it clears all effects. ### `modify(e, 'home', null), modify(e, 'home', block, distance?), modify(e, 'home', x, y, z, distance?)` diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index e010c027..22078193 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -1256,7 +1256,10 @@ public class EntityValue extends Value boolean showIcon = true; if (lv.size() > 4) showIcon = lv.get(4).getBoolean(); - le.addStatusEffect(new StatusEffectInstance(effect, duration, amplifier, showParticles, showIcon)); + boolean ambient = false; + if (lv.size() > 5) + showIcon = lv.get(5).getBoolean(); + le.addStatusEffect(new StatusEffectInstance(effect, duration, amplifier, ambient, showParticles, showIcon)); return; } } @@ -1269,7 +1272,7 @@ public class EntityValue extends Value le.removeStatusEffect(effect); return; } - throw new InternalExpressionException("'effect' needs either no arguments (clear) or effect name, duration, and optional amplifier, show particles and show icon"); + throw new InternalExpressionException("'effect' needs either no arguments (clear) or effect name, duration, and optional amplifier, show particles, show icon and ambient"); }); put("gamemode", (e,v)->{ From 10f027d7fe56572e50b79828f3bbcd84459151a8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 29 Nov 2020 01:21:36 +0000 Subject: [PATCH 2/9] Merge docs for 'Fix scarpet modify(e, 'effect', ...) (#587)' --- docs/scarpet/Full.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md index de176790..ddc697d7 100644 --- a/docs/scarpet/Full.md +++ b/docs/scarpet/Full.md @@ -3429,10 +3429,10 @@ If called with `false` value, it will disable AI in the mob. `true` will enable Sets if the entity obeys any collisions, including collisions with the terrain and basic physics. Not affecting players, since they are controlled client side. -### `modify(e, 'effect', name?, duration?, amplifier?, show_particles?, show_icon?)` +### `modify(e, 'effect', name?, duration?, amplifier?, show_particles?, show_icon?, ambient?)` -Applies status effect to the living entity. Takes several optional parameters, which default to `0`, `true` -and `true`. If no duration is specified, or if it's null or 0, the effect is removed. If name is not specified, +Applies status effect to the living entity. Takes several optional parameters, which default to `0`, `true`, +`true` and `false`. If no duration is specified, or if it's null or 0, the effect is removed. If name is not specified, it clears all effects. ### `modify(e, 'home', null), modify(e, 'home', block, distance?), modify(e, 'home', x, y, z, distance?)` From ce0cca4005d75e23c7f68a7a51ac218c139d15fa Mon Sep 17 00:00:00 2001 From: altrisi Date: Sun, 29 Nov 2020 02:40:16 +0100 Subject: [PATCH 3/9] [Scarpet] Add display_title function (#555) --- docs/scarpet/api/Auxiliary.md | 14 +++- docs/scarpet/resources/editors/idea/2.txt | 1 + .../scarpet/resources/editors/npp/scarpet.xml | 2 + .../java/carpet/script/api/Auxiliary.java | 71 +++++++++++++++++++ 4 files changed, 87 insertions(+), 1 deletion(-) diff --git a/docs/scarpet/api/Auxiliary.md b/docs/scarpet/api/Auxiliary.md index 3f2ddcd9..8f52ecee 100644 --- a/docs/scarpet/api/Auxiliary.md +++ b/docs/scarpet/api/Auxiliary.md @@ -245,6 +245,18 @@ Example usages: // not a problem in apps +### `display_title(players, type, title?, fadeInTicks?, stayTicks?, fadeOutTicks),` + +Sends the player (or players if `players` is a list) a title of a specific type, with optionally some times. + * `players` is either an online player or a list of players. When sending a single player, it will throw if the player is invalid or offline. + * `type` is either `'title'`, `'subtitle'`, `actionbar` or `clear`. + Note: `subtitle` will only be displayed if there is a title being displayed (can be an empty one) + * `title` is what title to send to the player. It is required except for `clear` type. Can be a text formatted using `format()` + * `...Ticks` are the number of ticks the title will stay in that state. + If not specified, it will use current defaults (those defaults may have changed from a previous `/title times` execution). + Executing with those will set the times to the specified ones. + Note that `actionbar` type doesn't support changing times (vanilla bug, see [MC-106167](https://bugs.mojang.com/browse/MC-106167)). + ### `logger(msg), logger(type, msg)` Prints the message to system logs, and not to chat. By default prints an info, unless you specify otherwise in the `type` parameter. @@ -469,7 +481,7 @@ Available options in the scarpet app space: * `world_seed` - a numeric seed of the world * `world_path` - full path to the world saves folder * `world_folder` - name of the direct folder in the saves that holds world files - * `world_carpet_rules` - returns all Carpet rules in a map form (`rule`->`value`). Includes rules from extensions with their namespace (`namespace:rule`->`value`). You can later listen to rule changes with the `on_carpet_rule_change(rule, newValue)` event. + * `world_carpet_rules` - returns all Carpet rules in a map form (`rule`->`value`). Note that the values are always returned as strings, so you can't do boolean comparisons directly. Includes rules from extensions with their namespace (`namespace:rule`->`value`). You can later listen to rule changes with the `on_carpet_rule_changes(rule, newValue)` event. Relevant gameplay related properties * `game_difficulty` - current difficulty of the game: `'peacefu'`, `'easy'`, `'normal'`, or `'hard'` diff --git a/docs/scarpet/resources/editors/idea/2.txt b/docs/scarpet/resources/editors/idea/2.txt index ae16592b..05473aff 100644 --- a/docs/scarpet/resources/editors/idea/2.txt +++ b/docs/scarpet/resources/editors/idea/2.txt @@ -21,6 +21,7 @@ custom_dimension day_time destroy diamond +display_title draw_shape drop_item emitted_light diff --git a/docs/scarpet/resources/editors/npp/scarpet.xml b/docs/scarpet/resources/editors/npp/scarpet.xml index 92da9036..f4cd1ddd 100644 --- a/docs/scarpet/resources/editors/npp/scarpet.xml +++ b/docs/scarpet/resources/editors/npp/scarpet.xml @@ -58,6 +58,8 @@ structure_eligibility structures structure_references set_structure reset_chunk reload_chunk custom_dimension add_chunk_ticket + display_title + scoreboard scoreboard_add scoreboard_remove scoreboard_display team_add team_list team_remove team_leave team_property diff --git a/src/main/java/carpet/script/api/Auxiliary.java b/src/main/java/carpet/script/api/Auxiliary.java index edf922c3..ab652360 100644 --- a/src/main/java/carpet/script/api/Auxiliary.java +++ b/src/main/java/carpet/script/api/Auxiliary.java @@ -43,10 +43,13 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; import net.minecraft.network.packet.s2c.play.PlaySoundIdS2CPacket; +import net.minecraft.network.packet.s2c.play.TitleS2CPacket; +import net.minecraft.network.packet.s2c.play.TitleS2CPacket.Action; import net.minecraft.particle.ParticleEffect; import net.minecraft.server.MinecraftServer; import net.minecraft.server.command.CommandOutput; import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundCategory; @@ -455,6 +458,74 @@ public class Auxiliary { Value finalRes = res; return (_c, _t) -> finalRes; // pass through for variables }); + expression.addLazyFunction("display_title", -1, (c, t, lv) -> { + if (lv.size() < 2) throw new InternalExpressionException("'display_title' needs at least a target, type and message, and optionally times"); + Value pVal = lv.get(0).evalValue(c); + TitleS2CPacket.Action action; + List targets; + Text title = null; + int in, out, stay; + MinecraftServer server = ((CarpetContext)c).s.getMinecraftServer(); + if (pVal instanceof ListValue) + { + targets = ((ListValue) pVal).getItems().stream() + .map(v -> EntityValue.getPlayerByValue(server, v)) + .filter(v -> v != null).collect(Collectors.toList()); + } + else + { + targets = Collections.singletonList(EntityValue.getPlayerByValue(server, pVal)); + if (targets.get(0) == null) + throw new InternalExpressionException("'display_title' requires an online player or a list of players as first argument"); + } + pVal = lv.get(1).evalValue(c); + switch (pVal.getString()) + { + case "title": + action = Action.TITLE; + break; + case "subtitle": + action = Action.SUBTITLE; + break; + case "actionbar": + action = Action.ACTIONBAR; + break; + case "clear": + action = Action.CLEAR; + break; + default: + throw new InternalExpressionException("'display_title' requires 'title', 'subtitle', 'actionbar' or 'clear' as second argument"); + } + if (action != Action.CLEAR && lv.size() < 3) + throw new InternalExpressionException("Third argument of 'display_title' must be present except for 'clear' type"); + if (lv.size() > 2) + { + pVal = lv.get(2).evalValue(c); + if (pVal instanceof FormattedTextValue) + title = ((FormattedTextValue) pVal).getText(); + else + title = Text.of(pVal.getString()); + } + TitleS2CPacket timesPacket = null; + if (lv.size() > 3) + { + try { + in = Integer.valueOf(lv.get(3).evalValue(c).getString()); + stay = Integer.valueOf(lv.get(4).evalValue(c).getString()); + out = Integer.valueOf(lv.get(5).evalValue(c).getString()); + timesPacket = new TitleS2CPacket(Action.TIMES, null, in, stay, out); + } catch (NumberFormatException e) { + throw new InternalExpressionException("If specified, 'display_title' requires integers as fourth, fifth and sixth arguments"); + } + } + TitleS2CPacket packet = new TitleS2CPacket(action, title); + for (ServerPlayerEntity player:targets) + { + if (timesPacket != null) player.networkHandler.sendPacket(timesPacket); + player.networkHandler.sendPacket(packet); + } + return (cc, tt) -> Value.NULL; + }); expression.addLazyFunction("format", -1, (c, t, lv) -> { if (lv.size() == 0 ) throw new InternalExpressionException("'format' requires at least one component"); From 0a22c2c375e79a5477fdacef0152ac71a9f83692 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 29 Nov 2020 01:40:33 +0000 Subject: [PATCH 4/9] Merge docs for '[Scarpet] Add display_title function (#555)' --- docs/scarpet/Full.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md index ddc697d7..27d933c0 100644 --- a/docs/scarpet/Full.md +++ b/docs/scarpet/Full.md @@ -4514,6 +4514,18 @@ Example usages: // not a problem in apps +### `display_title(players, type, title?, fadeInTicks?, stayTicks?, fadeOutTicks),` + +Sends the player (or players if `players` is a list) a title of a specific type, with optionally some times. + * `players` is either an online player or a list of players. When sending a single player, it will throw if the player is invalid or offline. + * `type` is either `'title'`, `'subtitle'`, `actionbar` or `clear`. + Note: `subtitle` will only be displayed if there is a title being displayed (can be an empty one) + * `title` is what title to send to the player. It is required except for `clear` type. Can be a text formatted using `format()` + * `...Ticks` are the number of ticks the title will stay in that state. + If not specified, it will use current defaults (those defaults may have changed from a previous `/title times` execution). + Executing with those will set the times to the specified ones. + Note that `actionbar` type doesn't support changing times (vanilla bug, see [MC-106167](https://bugs.mojang.com/browse/MC-106167)). + ### `logger(msg), logger(type, msg)` Prints the message to system logs, and not to chat. By default prints an info, unless you specify otherwise in the `type` parameter. @@ -4738,7 +4750,7 @@ Available options in the scarpet app space: * `world_seed` - a numeric seed of the world * `world_path` - full path to the world saves folder * `world_folder` - name of the direct folder in the saves that holds world files - * `world_carpet_rules` - returns all Carpet rules in a map form (`rule`->`value`). Includes rules from extensions with their namespace (`namespace:rule`->`value`). You can later listen to rule changes with the `on_carpet_rule_change(rule, newValue)` event. + * `world_carpet_rules` - returns all Carpet rules in a map form (`rule`->`value`). Note that the values are always returned as strings, so you can't do boolean comparisons directly. Includes rules from extensions with their namespace (`namespace:rule`->`value`). You can later listen to rule changes with the `on_carpet_rule_changes(rule, newValue)` event. Relevant gameplay related properties * `game_difficulty` - current difficulty of the game: `'peacefu'`, `'easy'`, `'normal'`, or `'hard'` From bab7037b803553d2743210e0f609e8cbb36f1e56 Mon Sep 17 00:00:00 2001 From: gnembon Date: Sat, 28 Nov 2020 22:21:38 -0400 Subject: [PATCH 5/9] cleanup --- .../java/carpet/script/api/Auxiliary.java | 58 +++++++++---------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/src/main/java/carpet/script/api/Auxiliary.java b/src/main/java/carpet/script/api/Auxiliary.java index ab652360..8a0dcbde 100644 --- a/src/main/java/carpet/script/api/Auxiliary.java +++ b/src/main/java/carpet/script/api/Auxiliary.java @@ -76,7 +76,9 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import java.util.stream.Stream; import static java.lang.Math.max; import static java.lang.Math.min; @@ -461,25 +463,16 @@ public class Auxiliary { expression.addLazyFunction("display_title", -1, (c, t, lv) -> { if (lv.size() < 2) throw new InternalExpressionException("'display_title' needs at least a target, type and message, and optionally times"); Value pVal = lv.get(0).evalValue(c); - TitleS2CPacket.Action action; - List targets; - Text title = null; - int in, out, stay; + if (!(pVal instanceof ListValue)) pVal = ListValue.of(pVal); MinecraftServer server = ((CarpetContext)c).s.getMinecraftServer(); - if (pVal instanceof ListValue) + Stream targets = ((ListValue) pVal).getItems().stream().map(v -> { - targets = ((ListValue) pVal).getItems().stream() - .map(v -> EntityValue.getPlayerByValue(server, v)) - .filter(v -> v != null).collect(Collectors.toList()); - } - else - { - targets = Collections.singletonList(EntityValue.getPlayerByValue(server, pVal)); - if (targets.get(0) == null) - throw new InternalExpressionException("'display_title' requires an online player or a list of players as first argument"); - } - pVal = lv.get(1).evalValue(c); - switch (pVal.getString()) + ServerPlayerEntity player = EntityValue.getPlayerByValue(server, v); + if (player == null) throw new InternalExpressionException("'display_title' requires a valid online player or a list of players as first argument. "+v.getString()+" is not a player."); + return player; + }); + TitleS2CPacket.Action action; + switch (lv.get(1).evalValue(c).getString().toLowerCase(Locale.ROOT)) { case "title": action = Action.TITLE; @@ -498,6 +491,7 @@ public class Auxiliary { } if (action != Action.CLEAR && lv.size() < 3) throw new InternalExpressionException("Third argument of 'display_title' must be present except for 'clear' type"); + Text title = null; if (lv.size() > 2) { pVal = lv.get(2).evalValue(c); @@ -506,25 +500,25 @@ public class Auxiliary { else title = Text.of(pVal.getString()); } - TitleS2CPacket timesPacket = null; + TitleS2CPacket timesPacket; if (lv.size() > 3) { - try { - in = Integer.valueOf(lv.get(3).evalValue(c).getString()); - stay = Integer.valueOf(lv.get(4).evalValue(c).getString()); - out = Integer.valueOf(lv.get(5).evalValue(c).getString()); - timesPacket = new TitleS2CPacket(Action.TIMES, null, in, stay, out); - } catch (NumberFormatException e) { - throw new InternalExpressionException("If specified, 'display_title' requires integers as fourth, fifth and sixth arguments"); - } + int in = NumericValue.asNumber(lv.get(3).evalValue(c),"fade in for display_title" ).getInt(); + int stay = NumericValue.asNumber(lv.get(4).evalValue(c),"stay for display_title" ).getInt(); + int out = NumericValue.asNumber(lv.get(5).evalValue(c),"fade out for display_title" ).getInt(); + timesPacket = new TitleS2CPacket(Action.TIMES, null, in, stay, out); } + else timesPacket = null; + TitleS2CPacket packet = new TitleS2CPacket(action, title); - for (ServerPlayerEntity player:targets) - { - if (timesPacket != null) player.networkHandler.sendPacket(timesPacket); - player.networkHandler.sendPacket(packet); - } - return (cc, tt) -> Value.NULL; + AtomicInteger total = new AtomicInteger(0); + targets.forEach(p -> { + if (timesPacket != null) p.networkHandler.sendPacket(timesPacket); + p.networkHandler.sendPacket(packet); + total.getAndIncrement(); + }); + Value ret = NumericValue.of(total.get()); + return (cc, tt) -> ret; }); expression.addLazyFunction("format", -1, (c, t, lv) -> { From 076ad62fad19b1782022572979aba9f642e2ed4e Mon Sep 17 00:00:00 2001 From: gnembon Date: Sat, 28 Nov 2020 22:23:57 -0400 Subject: [PATCH 6/9] potential NPE issue --- src/main/java/carpet/script/api/Auxiliary.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/carpet/script/api/Auxiliary.java b/src/main/java/carpet/script/api/Auxiliary.java index 8a0dcbde..d1e97972 100644 --- a/src/main/java/carpet/script/api/Auxiliary.java +++ b/src/main/java/carpet/script/api/Auxiliary.java @@ -439,6 +439,7 @@ public class Auxiliary { if (lv.size() == 2) { ServerPlayerEntity player = EntityValue.getPlayerByValue(s.getMinecraftServer(), res); + if (player == null) return LazyValue.NULL; s = player.getCommandSource(); res = lv.get(1).evalValue(c); } From e15afd9763ec2cb8f8d624fd5ff9c29e1b8a162d Mon Sep 17 00:00:00 2001 From: gnembon Date: Sun, 29 Nov 2020 00:33:01 -0400 Subject: [PATCH 7/9] added support for block predicate argument support and `tag_matches` function. Fixes #563 --- docs/scarpet/api/Auxiliary.md | 6 ++ docs/scarpet/resources/editors/idea/2.txt | 8 ++- .../scarpet/resources/editors/npp/scarpet.xml | 3 +- gradle.properties | 2 +- .../carpet/fakes/BlockPredicateInterface.java | 17 ++++++ .../mixins/StatePredicate_scarpetMixin.java | 58 +++++++++++++++++++ .../mixins/TagPredicate_scarpetMixin.java | 53 +++++++++++++++++ .../java/carpet/script/api/Auxiliary.java | 13 ++++- src/main/java/carpet/script/api/Entities.java | 2 +- .../java/carpet/script/api/WorldAccess.java | 4 +- .../script/command/CommandArgument.java | 4 ++ .../script/value/NBTSerializableValue.java | 7 ++- .../carpet/script/value/NumericValue.java | 2 +- .../carpet/script/value/ValueConversions.java | 15 +++++ src/main/resources/carpet.accesswidener | 4 +- src/main/resources/carpet.mixins.json | 2 + 16 files changed, 187 insertions(+), 13 deletions(-) create mode 100644 src/main/java/carpet/fakes/BlockPredicateInterface.java create mode 100644 src/main/java/carpet/mixins/StatePredicate_scarpetMixin.java create mode 100644 src/main/java/carpet/mixins/TagPredicate_scarpetMixin.java diff --git a/docs/scarpet/api/Auxiliary.md b/docs/scarpet/api/Auxiliary.md index 8f52ecee..e5e9209b 100644 --- a/docs/scarpet/api/Auxiliary.md +++ b/docs/scarpet/api/Auxiliary.md @@ -163,6 +163,12 @@ Consult section about container operations in `Expression` to learn about possib Excapes all the special characters in the string or nbt tag and returns a string that can be stored in nbt directly as a string value. +### `tag_matches(daddy_tag, baby_tag, match_lists?)` + +Utility returning `true` if `baby_tag` is fully contained in `daddy_tag`. Anything matches `null` baby tag, and +Nothing is contained in a `null` daddy tag. If `match_lists` is specified and `false`, content of nested lists is ignored. +Default behaviour is to match them. + ### `parse_nbt(tag)` Converts NBT tag to a scarpet value, which you can navigate through much better. diff --git a/docs/scarpet/resources/editors/idea/2.txt b/docs/scarpet/resources/editors/idea/2.txt index 05473aff..642a8bc5 100644 --- a/docs/scarpet/resources/editors/idea/2.txt +++ b/docs/scarpet/resources/editors/idea/2.txt @@ -19,12 +19,14 @@ create_marker current_dimension custom_dimension day_time +delete_file destroy diamond display_title draw_shape drop_item emitted_light +encode_nbt entity_area entity_event entity_id @@ -62,6 +64,7 @@ modify nbt neighbours opacity +parse_nbt particle particle_line particle_rect @@ -87,11 +90,13 @@ scoreboard scoreboard_add scoreboard_remove scoreboard_display +tag_matches team_add team_list team_remove team_leave team_property +read_file see_sky set set_biome @@ -119,4 +124,5 @@ update view_distance volume without_updates -world_time \ No newline at end of file +world_time +write_file \ No newline at end of file diff --git a/docs/scarpet/resources/editors/npp/scarpet.xml b/docs/scarpet/resources/editors/npp/scarpet.xml index f4cd1ddd..65a7800d 100644 --- a/docs/scarpet/resources/editors/npp/scarpet.xml +++ b/docs/scarpet/resources/editors/npp/scarpet.xml @@ -80,7 +80,8 @@ plop harvest destroy schedule nbt draw_shape create_marker remove_all_markers - load_app_data store_app_data escape_nbt statistic + load_app_data store_app_data read_file write_file delete_file + escape_nbt parse_nbt encode_nbt tag_matches statistic euler pi null true false x y z p diff --git a/gradle.properties b/gradle.properties index 8894d5cb..8caaf1a1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ org.gradle.jvmargs=-Xmx1G # or better: https://modmuss50.me/fabric.html minecraft_version=1.16.4 yarn_mappings=1.16.4+build.7 - loader_version=0.10.7 + loader_version=0.10.8 # Mod Properties mod_version = 1.4.19+v201125 diff --git a/src/main/java/carpet/fakes/BlockPredicateInterface.java b/src/main/java/carpet/fakes/BlockPredicateInterface.java new file mode 100644 index 00000000..cd1e4349 --- /dev/null +++ b/src/main/java/carpet/fakes/BlockPredicateInterface.java @@ -0,0 +1,17 @@ +package carpet.fakes; + +import carpet.script.value.Value; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.tag.Tag; + +import java.util.Map; + +public interface BlockPredicateInterface +{ + BlockState getCMBlockState(); + Tag getCMBlockTag(); + Map getCMProperties(); + CompoundTag getCMDataTag(); +} diff --git a/src/main/java/carpet/mixins/StatePredicate_scarpetMixin.java b/src/main/java/carpet/mixins/StatePredicate_scarpetMixin.java new file mode 100644 index 00000000..054e6e67 --- /dev/null +++ b/src/main/java/carpet/mixins/StatePredicate_scarpetMixin.java @@ -0,0 +1,58 @@ +package carpet.mixins; + +import carpet.fakes.BlockPredicateInterface; +import carpet.script.value.StringValue; +import carpet.script.value.Value; +import carpet.script.value.ValueConversions; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.command.argument.BlockPredicateArgumentType; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.state.property.Property; +import net.minecraft.tag.Tag; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +@Mixin(BlockPredicateArgumentType.StatePredicate.class) +public class StatePredicate_scarpetMixin implements BlockPredicateInterface +{ + + @Shadow @Final private BlockState state; + + @Shadow @Final /*@Nullable*/ private CompoundTag nbt; + + @Shadow @Final private Set> properties; + + @Override + public BlockState getCMBlockState() + { + return state; + } + + @Override + public Tag getCMBlockTag() + { + return null; + } + + @Override + public Map getCMProperties() + { + return properties.stream().collect(Collectors.toMap( + p -> StringValue.of(p.getName()), + p -> ValueConversions.fromProperty(state, p), + (a, b) -> b + )); + } + + @Override + public CompoundTag getCMDataTag() + { + return nbt; + } +} diff --git a/src/main/java/carpet/mixins/TagPredicate_scarpetMixin.java b/src/main/java/carpet/mixins/TagPredicate_scarpetMixin.java new file mode 100644 index 00000000..0a45f4d4 --- /dev/null +++ b/src/main/java/carpet/mixins/TagPredicate_scarpetMixin.java @@ -0,0 +1,53 @@ +package carpet.mixins; + +import carpet.fakes.BlockPredicateInterface; +import carpet.script.value.StringValue; +import carpet.script.value.Value; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.command.argument.BlockPredicateArgumentType; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.tag.Tag; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Map; +import java.util.stream.Collectors; + +@Mixin(BlockPredicateArgumentType.TagPredicate.class) +public class TagPredicate_scarpetMixin implements BlockPredicateInterface +{ + @Shadow @Final private Tag tag; + + @Shadow @Final private Map properties; + + @Shadow @Final /*@Nullable*/ private CompoundTag nbt; + + @Override + public BlockState getCMBlockState() + { + return null; + } + + @Override + public Tag getCMBlockTag() + { + return tag; + } + + @Override + public Map getCMProperties() + { + return properties.entrySet().stream().collect(Collectors.toMap( + e -> new StringValue(e.getKey()), + e -> new StringValue(e.getValue()) + )); + } + + @Override + public CompoundTag getCMDataTag() + { + return nbt; + } +} diff --git a/src/main/java/carpet/script/api/Auxiliary.java b/src/main/java/carpet/script/api/Auxiliary.java index d1e97972..a835fc5c 100644 --- a/src/main/java/carpet/script/api/Auxiliary.java +++ b/src/main/java/carpet/script/api/Auxiliary.java @@ -40,6 +40,7 @@ import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.decoration.ArmorStandEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtHelper; import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; import net.minecraft.network.packet.s2c.play.PlaySoundIdS2CPacket; @@ -405,13 +406,23 @@ public class Auxiliary { Value parsed = ((NBTSerializableValue) v).toValue(); return (cc, tt) -> parsed; } - NBTSerializableValue ret = NBTSerializableValue.parseString(v.getString()); + NBTSerializableValue ret = NBTSerializableValue.parseString(v.getString(), false); if (ret == null) return LazyValue.NULL; Value parsed = ret.toValue(); return (cc, tt) -> parsed; }); + expression.addFunction("tag_matches", (lv) -> { + int numParam = lv.size(); + if (numParam != 2 && numParam != 3) throw new InternalExpressionException("'tag_matches' requires 2 or 3 arguments"); + if (lv.get(1).isNull()) return Value.TRUE; + if (lv.get(0).isNull()) return Value.FALSE; + Tag source = ((NBTSerializableValue)(NBTSerializableValue.fromValue(lv.get(0)))).getTag(); + Tag match = ((NBTSerializableValue)(NBTSerializableValue.fromValue(lv.get(1)))).getTag(); + return new NumericValue(NbtHelper.matches(match, source, numParam == 2 || lv.get(2).getBoolean())); + }); + expression.addLazyFunction("encode_nbt", -1, (c, t, lv) -> { int argSize = lv.size(); if (argSize==0 || argSize > 2) throw new InternalExpressionException("'encode_nbt' requires 1 or 2 parameters"); diff --git a/src/main/java/carpet/script/api/Entities.java b/src/main/java/carpet/script/api/Entities.java index 58894bd5..7fac7ef1 100644 --- a/src/main/java/carpet/script/api/Entities.java +++ b/src/main/java/carpet/script/api/Entities.java @@ -137,7 +137,7 @@ public class Entities { { Value nbt = lv.get(position.offset).evalValue(c); NBTSerializableValue v = (nbt instanceof NBTSerializableValue) ? (NBTSerializableValue) nbt - : NBTSerializableValue.parseString(nbt.getString()); + : NBTSerializableValue.parseString(nbt.getString(), true); hasTag = true; tag = v.getCompoundTag(); diff --git a/src/main/java/carpet/script/api/WorldAccess.java b/src/main/java/carpet/script/api/WorldAccess.java index 5beb2a25..fa7a6361 100644 --- a/src/main/java/carpet/script/api/WorldAccess.java +++ b/src/main/java/carpet/script/api/WorldAccess.java @@ -808,9 +808,7 @@ public class WorldAccess { tag = ((NBTSerializableValue) tagValue).getCompoundTag(); else { - NBTSerializableValue readTag = NBTSerializableValue.parseString(tagValue.getString()); - if (readTag == null) - throw new InternalExpressionException("Incorrect tag: "+tagValue.getString()); + NBTSerializableValue readTag = NBTSerializableValue.parseString(tagValue.getString(), true); tag = readTag.getCompoundTag(); } } diff --git a/src/main/java/carpet/script/command/CommandArgument.java b/src/main/java/carpet/script/command/CommandArgument.java index c0e5ebef..1ddede7f 100644 --- a/src/main/java/carpet/script/command/CommandArgument.java +++ b/src/main/java/carpet/script/command/CommandArgument.java @@ -36,6 +36,7 @@ import net.minecraft.advancement.Advancement; import net.minecraft.command.CommandSource; import net.minecraft.command.argument.AngleArgumentType; import net.minecraft.command.argument.BlockPosArgumentType; +import net.minecraft.command.argument.BlockPredicateArgumentType; import net.minecraft.command.argument.BlockStateArgument; import net.minecraft.command.argument.BlockStateArgumentType; import net.minecraft.command.argument.ColorArgumentType; @@ -126,6 +127,9 @@ public abstract class CommandArgument }, false ), + new VanillaUnconfigurableArgument( "blockpredicate", BlockPredicateArgumentType::blockPredicate, + (c, p) -> ValueConversions.ofBlockPredicate(c.getSource().getMinecraftServer().getTagManager(), BlockPredicateArgumentType.getBlockPredicate(c, p)), false + ), // block_predicate todo - not sure about the returned format. Needs to match block tags used in the API (future) new VanillaUnconfigurableArgument("teamcolor", ColorArgumentType::color, (c, p) -> { diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index 81560643..938acd20 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -104,7 +104,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa return id.toString(); } - public static NBTSerializableValue parseString(String nbtString) + public static NBTSerializableValue parseString(String nbtString, boolean fail) { Tag tag; try @@ -113,7 +113,8 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa } catch (CommandSyntaxException e) { - throw new InternalExpressionException("Incorrect NBT tag: nbtString"); + if (fail) throw new InternalExpressionException("Incorrect NBT tag: "+ nbtString); + return null; } NBTSerializableValue value = new NBTSerializableValue(tag); value.nbtString = null; @@ -331,7 +332,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa return v; if (v instanceof NullValue) return Value.NULL; - return NBTSerializableValue.parseString(v.getString()); + return NBTSerializableValue.parseString(v.getString(), true); } diff --git a/src/main/java/carpet/script/value/NumericValue.java b/src/main/java/carpet/script/value/NumericValue.java index b4e47501..caec9776 100644 --- a/src/main/java/carpet/script/value/NumericValue.java +++ b/src/main/java/carpet/script/value/NumericValue.java @@ -227,7 +227,7 @@ public class NumericValue extends Value } public NumericValue(boolean boolval) { - this(boolval?1.0D:0.0D); + this(boolval?1L:0L); } @Override diff --git a/src/main/java/carpet/script/value/ValueConversions.java b/src/main/java/carpet/script/value/ValueConversions.java index 1108b1e2..b3344f40 100644 --- a/src/main/java/carpet/script/value/ValueConversions.java +++ b/src/main/java/carpet/script/value/ValueConversions.java @@ -1,9 +1,11 @@ package carpet.script.value; +import carpet.fakes.BlockPredicateInterface; import carpet.script.exception.InternalExpressionException; import carpet.utils.BlockInfo; import net.minecraft.block.BlockState; import net.minecraft.block.MaterialColor; +import net.minecraft.block.pattern.CachedBlockPosition; import net.minecraft.entity.Entity; import net.minecraft.entity.ai.brain.LookTarget; import net.minecraft.entity.ai.brain.WalkTarget; @@ -21,6 +23,7 @@ import net.minecraft.server.world.ServerWorld; import net.minecraft.state.property.Property; import net.minecraft.structure.StructurePiece; import net.minecraft.structure.StructureStart; +import net.minecraft.tag.TagManager; import net.minecraft.util.Identifier; import net.minecraft.util.StringIdentifiable; @@ -40,6 +43,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.function.Predicate; import java.util.stream.Collectors; public class ValueConversions @@ -379,4 +383,15 @@ public class ValueConversions if (ret == null) return ListValue.of(Value.NULL, NumericValue.of(itemSlot)); return ret; } + + public static Value ofBlockPredicate(TagManager tagManager, Predicate blockPredicate) + { + BlockPredicateInterface predicateData = (BlockPredicateInterface) blockPredicate; + return ListValue.of( + predicateData.getCMBlockState()==null?Value.NULL:of(Registry.BLOCK.getId(predicateData.getCMBlockState().getBlock())), + predicateData.getCMBlockTag()==null?Value.NULL:of(tagManager.getBlocks().getTagId(predicateData.getCMBlockTag())), + MapValue.wrap(predicateData.getCMProperties()), + predicateData.getCMDataTag() == null?Value.NULL:new NBTSerializableValue(predicateData.getCMDataTag()) + ); + } } diff --git a/src/main/resources/carpet.accesswidener b/src/main/resources/carpet.accesswidener index e9dad565..ede3ac69 100644 --- a/src/main/resources/carpet.accesswidener +++ b/src/main/resources/carpet.accesswidener @@ -2,4 +2,6 @@ accessWidener v1 named accessible class net/minecraft/server/world/ThreadedAnvilChunkStorage$TicketManager accessible class net/minecraft/server/world/ServerLightingProvider$Stage -accessible class net/minecraft/recipe/Ingredient$Entry \ No newline at end of file +accessible class net/minecraft/recipe/Ingredient$Entry +accessible class net/minecraft/command/argument/BlockPredicateArgumentType$StatePredicate +accessible class net/minecraft/command/argument/BlockPredicateArgumentType$TagPredicate \ No newline at end of file diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index 9806f587..8a90de0a 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -174,6 +174,8 @@ "RecipeManager_scarpetMixin", "Ingredient_scarpetMixin", "BlockStateArgument_scarpetMixin", + "StatePredicate_scarpetMixin", + "TagPredicate_scarpetMixin", "HorseBaseEntity_scarpetMixin", "PillagerEntity_scarpetMixin", From 08adaaeffd3e7f18026fa083f1a8f1036090a7a5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 29 Nov 2020 04:33:30 +0000 Subject: [PATCH 8/9] Merge docs for 'added support for block predicate argument support and function. Fixes #563' --- docs/scarpet/Full.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md index 27d933c0..59ad59c2 100644 --- a/docs/scarpet/Full.md +++ b/docs/scarpet/Full.md @@ -4432,6 +4432,12 @@ Consult section about container operations in `Expression` to learn about possib Excapes all the special characters in the string or nbt tag and returns a string that can be stored in nbt directly as a string value. +### `tag_matches(daddy_tag, baby_tag, match_lists?)` + +Utility returning `true` if `baby_tag` is fully contained in `daddy_tag`. Anything matches `null` baby tag, and +Nothing is contained in a `null` daddy tag. If `match_lists` is specified and `false`, content of nested lists is ignored. +Default behaviour is to match them. + ### `parse_nbt(tag)` Converts NBT tag to a scarpet value, which you can navigate through much better. From 735925b48fa076ca097d88ba978190a16f766a4e Mon Sep 17 00:00:00 2001 From: gnembon Date: Sun, 29 Nov 2020 01:21:11 -0400 Subject: [PATCH 9/9] we don't need these actually --- src/main/java/carpet/mixins/StatePredicate_scarpetMixin.java | 3 +-- src/main/java/carpet/mixins/TagPredicate_scarpetMixin.java | 3 +-- src/main/resources/carpet.accesswidener | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/carpet/mixins/StatePredicate_scarpetMixin.java b/src/main/java/carpet/mixins/StatePredicate_scarpetMixin.java index 054e6e67..5ef599e9 100644 --- a/src/main/java/carpet/mixins/StatePredicate_scarpetMixin.java +++ b/src/main/java/carpet/mixins/StatePredicate_scarpetMixin.java @@ -6,7 +6,6 @@ import carpet.script.value.Value; import carpet.script.value.ValueConversions; import net.minecraft.block.Block; import net.minecraft.block.BlockState; -import net.minecraft.command.argument.BlockPredicateArgumentType; import net.minecraft.nbt.CompoundTag; import net.minecraft.state.property.Property; import net.minecraft.tag.Tag; @@ -18,7 +17,7 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -@Mixin(BlockPredicateArgumentType.StatePredicate.class) +@Mixin(targets = "net.minecraft.command.argument.BlockPredicateArgumentType$StatePredicate") public class StatePredicate_scarpetMixin implements BlockPredicateInterface { diff --git a/src/main/java/carpet/mixins/TagPredicate_scarpetMixin.java b/src/main/java/carpet/mixins/TagPredicate_scarpetMixin.java index 0a45f4d4..f64062e0 100644 --- a/src/main/java/carpet/mixins/TagPredicate_scarpetMixin.java +++ b/src/main/java/carpet/mixins/TagPredicate_scarpetMixin.java @@ -5,7 +5,6 @@ import carpet.script.value.StringValue; import carpet.script.value.Value; import net.minecraft.block.Block; import net.minecraft.block.BlockState; -import net.minecraft.command.argument.BlockPredicateArgumentType; import net.minecraft.nbt.CompoundTag; import net.minecraft.tag.Tag; import org.spongepowered.asm.mixin.Final; @@ -15,7 +14,7 @@ import org.spongepowered.asm.mixin.Shadow; import java.util.Map; import java.util.stream.Collectors; -@Mixin(BlockPredicateArgumentType.TagPredicate.class) +@Mixin(targets = "net.minecraft.command.argument.BlockPredicateArgumentType$TagPredicate") public class TagPredicate_scarpetMixin implements BlockPredicateInterface { @Shadow @Final private Tag tag; diff --git a/src/main/resources/carpet.accesswidener b/src/main/resources/carpet.accesswidener index ede3ac69..e9dad565 100644 --- a/src/main/resources/carpet.accesswidener +++ b/src/main/resources/carpet.accesswidener @@ -2,6 +2,4 @@ accessWidener v1 named accessible class net/minecraft/server/world/ThreadedAnvilChunkStorage$TicketManager accessible class net/minecraft/server/world/ServerLightingProvider$Stage -accessible class net/minecraft/recipe/Ingredient$Entry -accessible class net/minecraft/command/argument/BlockPredicateArgumentType$StatePredicate -accessible class net/minecraft/command/argument/BlockPredicateArgumentType$TagPredicate \ No newline at end of file +accessible class net/minecraft/recipe/Ingredient$Entry \ No newline at end of file