added encode_nbt, added 'nbt' and 'nbt_merge' to modify entity API.

This commit is contained in:
gnembon committed 2020-06-20 02:19:20 -03:00
1 parent 4fe18a402c
commit ccd92c5957
18 files changed
+321 -15

No files matched your search

+24
View File
@@ -130,6 +130,30 @@ Consult section about container operations in `Expression` to learn about possib
Excapes all the special characters in the string or nbt tag and returns a string that can be stored in nbt directly
as a string value.
### parse_nbt(tag)
Converts NBT tag to a scarpet value, which you can navigate through much better.
Converts:
- Compound tags into maps with string keys
- List tags into list values
- Numbers (Ints, Floats, Doubles, Longs) into a number
- Rest is converted to strings.
### encode_nbt(expr, force?)
Encodes value of the expression as an NBT tag. By default (or when `force` is false), it will only allow
to encode values that are guaranteed to return the same value when applied the resulting tag to `parse_nbt()`.
Supported types that can reliably convert back and forth to and from NBT values are:
- Maps with string keywords
- Lists of items of the same type (scarpet will take care of unifying value types if possible)
- Numbers (encoded as Ints -> Longs -> Doubles, as needed)
- Strings
Other value types will only be converted to tags (including NBT tags) if `force` is true. They would require
extra treatment when loading them back from NBT, but using `force` true will always produce output / never
produce an exception.
### `print(expr)`
Displays the result of the expression to the chat. Overrides default `scarpet` behaviour of sending everyting to stderr.
+9
View File
@@ -532,6 +532,15 @@ Modifies directly player raw hunger components. Has no effect on non-players
adds exhaustion value to the current player exhaustion level - that's the method you probably want to use
to manipulate how much 'food' some action cost.
### `modify(e, 'nbt_merge', partial_tag)`
Merges a partial tag into the entity data and reloads the entity from its updated tag. Cannot be applied to players
### `modify(e, 'nbt', tag)`
Reloads the entity from a supplied tag. Better get a valid entity tag, what can go wrong. Wonder what would happen if
transplant rabbit's brain into a villager. Cannot be applied to players.
## Entity Events
There is a number of events that happen to entities that you can attach your own code to in the form of event handlers.
@@ -2949,6 +2949,24 @@ public class CarpetExpression
return (cc, tt) -> parsed;
});
this.expr.addLazyFunction("encode_nbt", -1, (c, t, lv) -> {
int argSize = lv.size();
if (argSize==0 || argSize > 2) throw new InternalExpressionException("'encode_nbt' requires 1 or 2 parameters");
Value v = lv.get(0).evalValue(c);
boolean force = (argSize > 1) && lv.get(1).evalValue(c).getBoolean();
Tag tag;
try
{
tag = v.toTag(force);
}
catch (NBTSerializableValue.IncompatibleTypeException ignored)
{
throw new InternalExpressionException("cannot reliably encode to a tag the value of '"+ignored.val.getPrettyString()+"'");
}
Value tagValue = new NBTSerializableValue(tag);
return (cc, tt) -> tagValue;
});
//"overridden" native call that prints to stderr
this.expr.addLazyFunction("print", -1, (c, t, lv) ->
{
@@ -12,7 +12,10 @@ import net.minecraft.block.Blocks;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.state.property.Property;
import net.minecraft.util.GlobalPos;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
@@ -24,8 +27,10 @@ import net.minecraft.world.World;
import net.minecraft.world.chunk.WorldChunk;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
@@ -180,6 +185,33 @@ public class BlockValue extends Value
public ServerWorld getWorld() { return world;}
@Override
public Tag toTag(boolean force)
{
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
// follows falling block convertion
CompoundTag tag = new CompoundTag();
CompoundTag state = new CompoundTag();
BlockState s = getBlockState();
state.put("Name", StringTag.of(Registry.BLOCK.getId(s.getBlock()).toString()));
Collection<Property<?>> properties = s.getProperties();
if (!properties.isEmpty())
{
CompoundTag props = new CompoundTag();
for (Property<?> p: properties)
{
props.put(p.getName(), StringTag.of(s.get(p).toString().toLowerCase(Locale.ROOT)));
}
state.put("Properties", props);
}
tag.put("BlockState", state);
CompoundTag dataTag = getData();
if (dataTag != null)
{
tag.put("TileEntityData", dataTag);
}
return tag;
}
public enum SpecificDirection {
UP("up",0.5, 0.0, 0.5, Direction.UP),
@@ -1,6 +1,5 @@
package carpet.script.value;
import carpet.CarpetSettings;
import carpet.fakes.EntityInterface;
import carpet.fakes.ItemEntityInterface;
import carpet.fakes.LivingEntityInterface;
@@ -12,10 +11,10 @@ import carpet.script.CarpetContext;
import carpet.script.EntityEventsGroup;
import carpet.script.argument.Vector3Argument;
import carpet.script.exception.InternalExpressionException;
import carpet.utils.Messenger;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.entity.player.HungerManager;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket;
import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket;
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
@@ -61,6 +60,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Predicate;
@@ -976,22 +976,37 @@ public class EntityValue extends Value
if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setSaturationLevelClient((float)NumericValue.asNumber(v).getLong());
});
// gamemode [check]
// spectate [check]
// "fire" [check]
// "extinguish" [set fire ticks to 0]
// "silent" [check]
// "gravity" [check]
// "invulnerable" [check]
put("nbt", (e, v) -> {
if (!(e instanceof PlayerEntity))
{
UUID uUID = e.getUuid();
Value tagValue = NBTSerializableValue.fromValue(v);
if (tagValue instanceof NBTSerializableValue)
{
e.fromTag(((NBTSerializableValue) tagValue).getCompoundTag());
e.setUuid(uUID);
}
}
});
put("nbt_merge", (e, v) -> {
if (!(e instanceof PlayerEntity))
{
UUID uUID = e.getUuid();
Value tagValue = NBTSerializableValue.fromValue(v);
if (tagValue instanceof NBTSerializableValue)
{
CompoundTag nbttagcompound = e.toTag((new CompoundTag()));
nbttagcompound.copyFrom(((NBTSerializableValue) tagValue).getCompoundTag());
e.fromTag(nbttagcompound);
e.setUuid(uUID);
}
}
});
// "dimension" []
// "item" []
// "count", []
// "age", [check]
// "effect_"name []
// "hold" [inventory_set?]
// "hold_offhand" [inventory_set?]
// "jump" [check]
// "nbt" <-big one, for now use run('data merge entity ...
}};
public void setEvent(CarpetContext cc, String eventName, FunctionValue fun, List<Value> args)
@@ -1001,4 +1016,14 @@ public class EntityValue extends Value
throw new InternalExpressionException("Unknown entity event: " + eventName);
((EntityInterface)entity).getEventContainer().addEvent(event, cc, fun, args);
}
@Override
public Tag toTag(boolean force)
{
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
CompoundTag tag = new CompoundTag();
tag.put("Data", getEntity().toTag( new CompoundTag()));
tag.put("Name", StringTag.of(Registry.ENTITY_TYPE.getId(entity.getType()).toString()));
return tag;
}
}
@@ -1,6 +1,9 @@
package carpet.script.value;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.text.BaseText;
import net.minecraft.text.Text;
public class FormattedTextValue extends StringValue
{
@@ -37,4 +40,11 @@ public class FormattedTextValue extends StringValue
{
return text;
}
@Override
public Tag toTag(boolean force)
{
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
return StringTag.of(Text.Serializer.toJson(text));
}
}
@@ -1,5 +1,7 @@
package carpet.script.value;
import net.minecraft.nbt.Tag;
public abstract class FrameworkValue extends Value
{
@Override
@@ -25,4 +27,10 @@ public abstract class FrameworkValue extends Value
{
throw new UnsupportedOperationException("Scarpet language component cannot be used as map key");
}
@Override
public Tag toTag(boolean force)
{
throw new UnsupportedOperationException("Scarpet language component cannot be serialized to the tag");
}
}
@@ -13,6 +13,8 @@ import carpet.script.exception.ExpressionException;
import carpet.script.exception.InternalExpressionException;
import carpet.script.exception.ReturnStatement;
import carpet.script.exception.ThrowStatement;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import java.util.ArrayList;
import java.util.Collections;
@@ -94,6 +96,8 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
return name.hashCode()+(int)variant;
}
@Override
public boolean equals(Object o)
{
@@ -231,4 +235,11 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
{
return args;
}
@Override
public Tag toTag(boolean force)
{
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
return StringTag.of(getString());
}
}
@@ -1,6 +1,7 @@
package carpet.script.value;
import carpet.script.exception.InternalExpressionException;
import net.minecraft.nbt.Tag;
public class GlobalValue extends Value
{
@@ -28,4 +29,10 @@ public class GlobalValue extends Value
{
throw new UnsupportedOperationException("Global value cannot be used as a map key");
}
@Override
public Tag toTag(boolean force)
{
throw new UnsupportedOperationException("Global value cannot be serialized to the tag");
}
}
@@ -1,6 +1,8 @@
package carpet.script.value;
import carpet.script.exception.InternalExpressionException;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import java.util.ArrayList;
import java.util.Iterator;
@@ -152,4 +154,11 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
{
return ("i"+getString()).hashCode();
}
@Override
public Tag toTag(boolean force)
{
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
return StringTag.of(getString());
}
}
@@ -3,13 +3,23 @@ package carpet.script.value;
import carpet.script.LazyValue;
import carpet.script.exception.InternalExpressionException;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.AbstractNumberTag;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.DoubleTag;
import net.minecraft.nbt.IntTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.LongTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.util.registry.Registry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
@@ -454,4 +464,65 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
{
return items.hashCode();
}
private enum TagTypeCompat
{
INT,
LONG,
DBL,
LIST,
MAP,
STRING;
private static TagTypeCompat getType(Tag tag)
{
if (tag instanceof IntTag) return INT;
if (tag instanceof LongTag) return LONG;
if (tag instanceof DoubleTag) return DBL;
if (tag instanceof ListTag) return LIST;
if (tag instanceof CompoundTag) return MAP;
return STRING;
}
}
@Override
public Tag toTag(boolean force)
{
int argSize = items.size();
if (argSize == 0) return new ListTag();
ListTag tag = new ListTag();
if (argSize ==1)
{
tag.add(items.get(0).toTag(force));
return tag;
}
// figuring out the types
List<Tag> tags= new ArrayList<>();
items.forEach(v -> tags.add(v.toTag(force)));
Set<TagTypeCompat> cases = EnumSet.noneOf(TagTypeCompat.class);
tags.forEach(t -> cases.add(TagTypeCompat.getType(t)));
if (cases.size()==1) // well, one type of items
{
tag.addAll(tags);
return tag;
}
if (cases.contains(TagTypeCompat.LIST)
|| cases.contains(TagTypeCompat.MAP)
|| cases.contains(TagTypeCompat.STRING)) // incompatible types
{
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
tags.forEach(t -> tag.add(StringTag.of(t.asString())));
return tag;
}
// only numbers / mixed types
if (cases.contains(TagTypeCompat.DBL))
{
tags.forEach(t -> tag.add(DoubleTag.of(((AbstractNumberTag)t).getDouble())));
}
else
{
tags.forEach(t -> tag.add(LongTag.of(((AbstractNumberTag)t).getLong())));
}
return tag;
}
}
@@ -1,6 +1,8 @@
package carpet.script.value;
import carpet.script.exception.InternalExpressionException;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import java.util.ArrayList;
import java.util.HashMap;
@@ -226,4 +228,16 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
{
return map.hashCode();
}
@Override
public Tag toTag(boolean force)
{
CompoundTag tag = new CompoundTag() ;
map.forEach( (k, v) ->
{
if (!force && !(k instanceof StringValue)) throw new NBTSerializableValue.IncompatibleTypeException(k);
tag.put(k.getString(), v.toTag(force));
});
return tag;
}
}
@@ -638,4 +638,23 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
{
return "nbt";
}
@Override
public Tag toTag(boolean force)
{
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
ensureOwnership();
return getTag();
}
public static class IncompatibleTypeException extends RuntimeException
{
private IncompatibleTypeException() {}
public Value val;
public IncompatibleTypeException(Value val)
{
this.val = val;
}
};
}
@@ -1,5 +1,8 @@
package carpet.script.value;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
public class NullValue extends NumericValue // TODO check nonsingleton code
{
@Override
@@ -50,4 +53,11 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
{
return 0;
}
@Override
public Tag toTag(boolean force)
{
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
return StringTag.of("null");
}
}
@@ -1,6 +1,10 @@
package carpet.script.value;
import carpet.script.exception.InternalExpressionException;
import net.minecraft.nbt.DoubleTag;
import net.minecraft.nbt.IntTag;
import net.minecraft.nbt.LongTag;
import net.minecraft.nbt.Tag;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
@@ -204,4 +208,20 @@ public class NumericValue extends Value
{
return (int)getLong();
}
@Override
public Tag toTag(boolean force)
{
long longValue = getLong();
if (value == (double)longValue)
{
if (abs(value) < Integer.MAX_VALUE-2)
return IntTag.of((int)longValue);
return LongTag.of(getLong());
}
else
{
return DoubleTag.of(value);
}
}
}
@@ -1,5 +1,8 @@
package carpet.script.value;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
public class StringValue extends Value
{
private String str;
@@ -30,4 +33,10 @@ public class StringValue extends Value
{
return "string";
}
@Override
public Tag toTag(boolean force)
{
return StringTag.of(str);
}
}
@@ -6,6 +6,7 @@ import carpet.script.LazyValue;
import carpet.script.Tokenizer;
import carpet.script.exception.ExpressionException;
import carpet.script.exception.InternalExpressionException;
import net.minecraft.nbt.Tag;
import java.util.HashMap;
import java.util.List;
@@ -144,4 +145,10 @@ public class ThreadValue extends Value
locks.clear();
}
@Override
public Tag toTag(boolean force)
{
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
return getValue().toTag(true);
}
}
@@ -1,6 +1,7 @@
package carpet.script.value;
import carpet.script.exception.InternalExpressionException;
import net.minecraft.nbt.Tag;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -203,4 +204,6 @@ public abstract class Value implements Comparable<Value>, Cloneable
throw new InternalExpressionException("Cannot make a copy of value: "+this);
}
}
public abstract Tag toTag(boolean force);
}