added encode_nbt, added 'nbt' and 'nbt_merge' to modify entity API.
This commit is contained in:
1 parent
4fe18a402c
commit
ccd92c5957
18 files changed
+321
-15
No files matched your search
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user