Merge branch 'master' into 1.17

# Conflicts:
#	gradle.properties
#	src/main/java/carpet/script/value/ValueConversions.java
This commit is contained in:
gnembon committed 2020-11-29 01:23:22 -04:00
commit 973dd29739
17 files changed
+293 -21

No files matched your search

+22 -4
View File
@@ -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?)`
@@ -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.
@@ -4514,6 +4520,18 @@ Example usages:
// not a problem in apps
</pre>
### `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 +4756,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'`
+19 -1
View File
@@ -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.
@@ -245,6 +251,18 @@ Example usages:
// not a problem in apps
</pre>
### `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 +487,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'`
+3 -3
View File
@@ -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?)`
+8 -1
View File
@@ -19,11 +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
@@ -61,6 +64,7 @@ modify
nbt
neighbours
opacity
parse_nbt
particle
particle_line
particle_rect
@@ -86,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
@@ -118,4 +124,5 @@ update
view_distance
volume
without_updates
world_time
world_time
write_file
@@ -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
@@ -78,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
</Keywords>
<Keywords name="Keywords3">euler pi null true false</Keywords>
<Keywords name="Keywords4">x y z p</Keywords>
@@ -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<Block> getCMBlockTag();
Map<Value, Value> getCMProperties();
CompoundTag getCMDataTag();
}
@@ -0,0 +1,57 @@
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.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(targets = "net.minecraft.command.argument.BlockPredicateArgumentType$StatePredicate")
public class StatePredicate_scarpetMixin implements BlockPredicateInterface
{
@Shadow @Final private BlockState state;
@Shadow @Final /*@Nullable*/ private CompoundTag nbt;
@Shadow @Final private Set<Property<?>> properties;
@Override
public BlockState getCMBlockState()
{
return state;
}
@Override
public Tag<Block> getCMBlockTag()
{
return null;
}
@Override
public Map<Value, Value> 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;
}
}
@@ -0,0 +1,52 @@
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.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(targets = "net.minecraft.command.argument.BlockPredicateArgumentType$TagPredicate")
public class TagPredicate_scarpetMixin implements BlockPredicateInterface
{
@Shadow @Final private Tag<Block> tag;
@Shadow @Final private Map<String, String> properties;
@Shadow @Final /*@Nullable*/ private CompoundTag nbt;
@Override
public BlockState getCMBlockState()
{
return null;
}
@Override
public Tag<Block> getCMBlockTag()
{
return tag;
}
@Override
public Map<Value, Value> getCMProperties()
{
return properties.entrySet().stream().collect(Collectors.toMap(
e -> new StringValue(e.getKey()),
e -> new StringValue(e.getValue())
));
}
@Override
public CompoundTag getCMDataTag()
{
return nbt;
}
}
+78 -1
View File
@@ -40,13 +40,17 @@ 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;
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;
@@ -73,7 +77,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;
@@ -400,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");
@@ -434,6 +450,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);
}
@@ -455,6 +472,66 @@ 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);
if (!(pVal instanceof ListValue)) pVal = ListValue.of(pVal);
MinecraftServer server = ((CarpetContext)c).s.getMinecraftServer();
Stream<ServerPlayerEntity> targets = ((ListValue) pVal).getItems().stream().map(v ->
{
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;
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");
Text title = null;
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;
if (lv.size() > 3)
{
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);
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) -> {
if (lv.size() == 0 ) throw new InternalExpressionException("'format' requires at least one component");
@@ -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();
@@ -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();
}
}
@@ -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) -> {
@@ -1271,7 +1271,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;
}
}
@@ -1284,7 +1287,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)->{
@@ -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);
}
@@ -227,7 +227,7 @@ public class NumericValue extends Value
}
public NumericValue(boolean boolval)
{
this(boolval?1.0D:0.0D);
this(boolval?1L:0L);
}
@Override
@@ -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.MapColor;
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<CachedBlockPosition> 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())
);
}
}
+2
View File
@@ -176,6 +176,8 @@
"RecipeManager_scarpetMixin",
"Ingredient_scarpetMixin",
"BlockStateArgument_scarpetMixin",
"StatePredicate_scarpetMixin",
"TagPredicate_scarpetMixin",
"HorseBaseEntity_scarpetMixin",
"PillagerEntity_scarpetMixin",