From 99af49661b953674c08efcd6e80665549e062492 Mon Sep 17 00:00:00 2001 From: gnembon Date: Mon, 30 Sep 2019 10:39:25 -0300 Subject: [PATCH 01/13] added string ops and biome getter --- .../java/carpet/script/CarpetExpression.java | 12 ++++++++++++ src/main/java/carpet/script/Expression.java | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index 0ab51a92..c80dee86 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -400,6 +400,8 @@ public class CarpetExpression * place_item('piston,x,y,z,'down') // places a piston facing down * place_item('carrot',x,y,z) // attempts to plant a carrot plant. Returns true if could place carrots at that position. * + *

biome(pos)

+ *

returns biome at that block position.

*

set_biome(pos, biome_name)

*

changes biome at that block position.

*

update(pos)

@@ -997,6 +999,16 @@ public class CarpetExpression return (_c, _t) -> res; }); + this.expr.addLazyFunction("biome", -1, (c, t, lv) -> { + CarpetContext cc = (CarpetContext)c; + BlockValue.LocatorResult locator = BlockValue.fromParams(cc, lv, 0); + ServerWorld world = cc.s.getWorld(); + BlockPos pos = locator.block.getPos(); + Biome biome = world.getBiome(pos); + Value res = new StringValue(biome.getTranslationKey().replaceFirst("^biome\\.minecraft\\.", "")); + return (_c, _t) -> res; + }); + this.expr.addLazyFunction("set_biome", -1, (c, t, lv) -> { CarpetContext cc = (CarpetContext)c; BlockValue.LocatorResult locator = BlockValue.fromParams(cc, lv, 0); diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index f3256963..a812c876 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -20,6 +20,7 @@ import carpet.script.value.ListValue; import carpet.script.value.NumericValue; import carpet.script.value.StringValue; import carpet.script.value.Value; +import org.apache.commons.lang3.text.WordUtils; import java.math.BigInteger; import java.util.ArrayList; @@ -2288,6 +2289,14 @@ public class Expression implements Cloneable *
*

Auxiliary functions

* + *

lower(expr), uppper(expr), title(expr)

+ *

Returns lowercase, uppercase or titlecase representation of a string representation of the passed expression

+ *
+     * lower('aBc') => 'abc'
+     * upper('aBc') => 'ABC'
+     * title('aBc') => 'Abc'
+     * 
+ * *

length(expr)

*

Returns length of the expression, the length of the string, * the length of the integer part of the number, or length of the list

@@ -2483,6 +2492,13 @@ public class Expression implements Cloneable } }); + addUnaryFunction("lower", v -> new StringValue(v.getString().toLowerCase(Locale.ROOT))); + + addUnaryFunction("upper", v -> new StringValue(v.getString().toUpperCase(Locale.ROOT))); + + addUnaryFunction("title", v -> new StringValue(WordUtils.capitalizeFully(v.getString()))); + + addUnaryFunction("type", v -> new StringValue(v.getTypeString())); addUnaryFunction("length", v -> new NumericValue(v.length())); From aac313bbb3ea5b89af146488d9f6c154cb958392 Mon Sep 17 00:00:00 2001 From: gnembon Date: Tue, 1 Oct 2019 02:37:30 -0300 Subject: [PATCH 02/13] added . operator, changed priorities of ~ and ., cleaned entity_event handler and docs, replaced default call function to space, added stacktrace to unknown exceptions, renamed event to 'on_damaged' --- .../java/carpet/script/CarpetEventServer.java | 6 +-- .../java/carpet/script/CarpetExpression.java | 37 +++++++++++++++++++ src/main/java/carpet/script/Expression.java | 24 ++++++++---- .../java/carpet/script/value/EntityValue.java | 2 +- 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java index fb352c64..72a52108 100644 --- a/src/main/java/carpet/script/CarpetEventServer.java +++ b/src/main/java/carpet/script/CarpetEventServer.java @@ -235,7 +235,7 @@ public class CarpetEventServer private ScheduledCall makeEventCall(CarpetContext cc, String function, List extraArgs, int argCount) { UserDefinedFunction udf = cc.host.globalFunctions.get(function); - if (udf == null || (udf.getArguments().size()-extraArgs.size()) != argCount) + if (udf == null || (udf.getArguments().size()-(extraArgs == null ? 0 : extraArgs.size())) != argCount) { // call won't match arguments return null; @@ -389,11 +389,9 @@ public class CarpetEventServer } public ScheduledCall makeDamageCall(CarpetContext cc, String function, List extraArgs) { - return makeEventCall(cc, function, extraArgs, 3); + return makeEventCall(cc, function, extraArgs, 4); } - - public void onEntityDeath(ScheduledCall call, Entity e, String reason) { call.execute( Arrays.asList( (c, t)-> new EntityValue(e), (c, t)-> new StringValue(reason))); diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index c80dee86..d6ff1f4e 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -1586,6 +1586,43 @@ public class CarpetExpression *

Sets AI to stay around the home position, within distance blocks from it. distance * defaults to 16 blocks. null removes it. May not work fully with mobs that have this AI built in, like * Villagers.

+ *

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. The event handler is any function that runs in your package that accepts certain expected parameters, which + * you can expand with your own arguments. When it comes to the moment when the given command needs to be executed, it + * does so providing that number of arguments it accepts is equal number of event arguments, and extra arguments passed when + * defining the callback with entity_event

+ *

The following events can be handled by entities.

+ *
    + *
  • 'on_tick': executes every tick right before entity is ticked in the game. Required arguments: entity
  • + *
  • 'on_death': executes once when a living entity dies. Required arguments: entity, reason
  • + *
  • 'on_removed': execute once when an entity is removed. Required arguments: entity
  • + *
  • 'on_damaged': executed every time a living entity is about to receive damage. Required arguments: entity, amount, source, attacking_entity
  • + *
+ *

It doesn't mean that all entity types will have a chance to + * execute a given event, but entities will not error when you will attach inapplicable event to it.

+ *

entity_event(e, event, call_name, args...)

+ *

Attaches specific function from the current package to be called upon the event, with extra args + * curried to the original required arguments for the event handler

+ *
+     * protect_villager(entity, amount, source, source_entity, healing_player) ->
+     * (
+     * 	 if(source_entity && source_entity~'type' != 'player',
+     * 	   modify(entity, 'health', amount + entity~'health' );
+     * 	   particle('end_rod', pos(entity)+l(0,3,0));
+     * 	   print(str('%s healed thanks to %s', entity, healing_player))
+     * 	 )
+     * );
+     *
+     * __on_player_interacts_with_entity(player, entity, hand) ->
+     * (
+     *   if (entity~'type' == 'villager',
+     * 	   entity_event(entity, 'on_damage', 'protect_villager', player~'name')
+     *   )
+     * )
+     * 
+ *

In this case this will protect a villager from entity damage (zombies, etc.) except players by granting all the health + * back to the villager after being harmed.

* */ diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index a812c876..155e7c7f 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -309,6 +309,7 @@ import static java.lang.Math.min; public class Expression implements Cloneable { private static final Map precedence = new HashMap() {{ + put("attribute~.", 80); put("unary+-!", 60); put("exponent^", 40); put("multiplication*/%", 30); @@ -560,7 +561,8 @@ public class Expression implements Cloneable return new ExpressionException(e, token, "Your math is wrong, "+exc.getMessage()); if (exc instanceof ExpressionException) return exc; - return new ExpressionException(e, token, "Error while evaluating expression: "+exc.getMessage()); + exc.printStackTrace(); + return new ExpressionException(e, token, "Error while evaluating expression: "+exc); } private void addUnaryOperator(String surface, boolean leftAssoc, Function fun) @@ -912,7 +914,7 @@ public class Expression implements Cloneable public void UserDefinedFunctionsAndControlFlow() // public just to get the javadoc right { // artificial construct to handle user defined functions and function definitions - addLazyFunction(".",-1, (c, t, lv) -> { // adjust based on c + addLazyFunction(" ",-1, (c, t, lv) -> { // adjust based on c String name = lv.get(lv.size()-1).evalValue(c).getString(); //lv.remove(lv.size()-1); // aint gonna cut it // maybe it will because of the eager eval changes if (t != Context.SIGNATURE) // just call the function @@ -1062,11 +1064,12 @@ public class Expression implements Cloneable * note, that commas and brackets are not operators, but behave like them: *

*
    + *
  • Match, Get~ .
  • *
  • Unary + - !
  • *
  • Exponent ^
  • *
  • Multiplication * / %
  • *
  • Addition + -
  • - *
  • Comparison == != > >= <= < ~
  • + *
  • Comparison == != > >= <= <
  • *
  • Logical And&&
  • *
  • Logical Or ||
  • *
  • Assignment = += <>
  • @@ -1140,8 +1143,14 @@ public class Expression implements Cloneable * null != false && run('kill gnembon') => 1 // gnembon dies, cheats allowed * * + *

    Get, Accessor Operator .

    + *

    Operator version of the get(...) function to access elements of lists, nbts, and potentially other + * containers. It is important to distinguish from ~ operator, which is a matching operator, meaning it + * is expected to perform some extra computations to retrieve the result, while . should be + * straightforward and immediate.

    + * *

    Matching Operator ~

    - *

    This operator should be understood as 'matches', or 'in'. For strings it matches the right operand as a regular + *

    This operator should be understood as 'matches', 'contains' or 'is_in'. For strings it matches the right operand as a regular * expression to the left one, returning the first match. This can be used to extract information from unparsed nbt's * in a more efficient way. For lists it checks if an element is in the list, and returns the index of that element, * or null if no such element was found, especially that the use of first function will not @@ -1161,7 +1170,7 @@ public class Expression implements Cloneable *

          * entities = entities_area('all',x,y,z,100,10,100);
          * sid = entities ~ 'Squid';
    -     * if(sid != null, run('execute as '+query(element(entities,sid),'id')+' run say I am here '+query(element(entities,sid),'pos') ) )
    +     * if(sid != null, run('execute as '+query(get(entities,sid),'id')+' run say I am here '+query(get(entities,sid),'pos') ) )
          * 
    *

    Or an example to find if a player has specific enchantment on a held axe (either hand) and get its level * (despite obvious lack of support for json NBT's):

    @@ -1218,6 +1227,7 @@ public class Expression implements Cloneable */ public void Operators() { + addBinaryOperator(".", precedence.get("attribute~."),true, Value::getElementAt); addBinaryOperator("+", precedence.get("addition+-"), true, Value::add); addBinaryOperator("-", precedence.get("addition+-"), true, Value::subtract); addBinaryOperator("*", precedence.get("multiplication*/%"), true, Value::multiply); @@ -1243,7 +1253,7 @@ public class Expression implements Cloneable return v2.getBoolean() ? ((cc, tt) -> v2) : LazyValue.FALSE; }); - addBinaryOperator("~", precedence.get("compare>=><=<"), true, Value::in); + addBinaryOperator("~", precedence.get("attribute~."), true, Value::in); addBinaryOperator(">", precedence.get("compare>=><=<"), false, (v1, v2) -> v1.compareTo(v2) > 0 ? Value.TRUE : Value.FALSE); @@ -2890,7 +2900,7 @@ public class Expression implements Cloneable } else // potentially unknown function or just unknown function { - f = functions.get("."); + f = functions.get(" "); p = new ArrayList<>(); } // pop parameters off the stack until we hit the start of diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index e357fa10..52f563e6 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -728,6 +728,6 @@ public class EntityValue extends Value put("on_death", (c, e, f, l) -> ((EntityInterface)e).setDeathCallback(c, f, l)); put("on_removed", (c, e, f, l) -> ((EntityInterface)e).setRemovedCallback(c, f, l)); put("on_tick", (c, e, f, l) -> ((EntityInterface)e).setTickCallback(c, f, l)); - put("on_damage", (c, e, f, l) -> ((EntityInterface)e).setDamageCallback(c, f, l)); + put("on_damaged", (c, e, f, l) -> ((EntityInterface)e).setDamageCallback(c, f, l)); }}; } From b6fe264a76471b545bc288ce75f74b2b523398ca Mon Sep 17 00:00:00 2001 From: gnembon Date: Tue, 1 Oct 2019 10:47:25 -0300 Subject: [PATCH 03/13] pickup delay docs --- src/main/java/carpet/script/CarpetExpression.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index d6ff1f4e..621a62b2 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -1474,9 +1474,11 @@ public class CarpetExpression *

    query(e,'age')

    *

    Age, in ticks, of the entity, i.e. how long it existed.

    *

    query(e,'item')

    - *

    Name of the item if its an item entity, null otherwise

    + *

    The item triple (name, count, nbt) if its an item entity, null otherwise

    *

    query(e,'count')

    *

    Number of items in a stack from item entity, null otherwise

    + *

    query(e,'pickup_delay')

    + *

    Retrieves pickup delay timeout for an item entity, null otherwise

    *

    query(e,'is_baby')

    *

    Boolean, true if its a baby.

    *

    query(e,'target')

    @@ -1567,7 +1569,8 @@ public class CarpetExpression *

    modify(e, 'accelerate', x, y, z), modify(e, 'accelerate', l(x, y, z) )

    *

    Adds a vector to the motion vector. Most realistic way to apply a force to an entity.

    *

    modify(e, 'custom_name'), modify(e, 'custom_name', name )

    - *

    Sets a custom name for an entity. Removes a custom name if the argument is null

    + *

    modify(e,'pickup_delay')

    + *

    Sets a custom pickup delay if the entity argument is an item entity

    *

    modify(e, 'dismount')

    *

    Dismounts riding entity.

    *

    modify(e, 'mount', other)

    @@ -1586,6 +1589,7 @@ public class CarpetExpression *

    Sets AI to stay around the home position, within distance blocks from it. distance * defaults to 16 blocks. null removes it. May not work fully with mobs that have this AI built in, like * Villagers.

    + * *

    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. The event handler is any function that runs in your package that accepts certain expected parameters, which From 8b7b285503e595cce1f29565a4045836edf162a0 Mon Sep 17 00:00:00 2001 From: gnembon Date: Tue, 1 Oct 2019 14:50:16 -0300 Subject: [PATCH 04/13] hash code and stump of map --- .../java/carpet/script/CarpetExpression.java | 18 ++++++++++ src/main/java/carpet/script/Expression.java | 2 ++ .../java/carpet/script/value/BlockValue.java | 9 +++++ .../java/carpet/script/value/EntityValue.java | 7 ++++ .../script/value/FunctionSignatureValue.java | 6 ++++ .../java/carpet/script/value/GlobalValue.java | 6 ++++ .../carpet/script/value/LazyListValue.java | 12 +++++++ .../java/carpet/script/value/ListValue.java | 13 ++++--- .../java/carpet/script/value/MapValue.java | 34 +++++++++++++++++++ .../java/carpet/script/value/NullValue.java | 6 ++++ .../carpet/script/value/NumericValue.java | 9 +++++ src/main/java/carpet/script/value/Value.java | 8 +++++ 12 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 src/main/java/carpet/script/value/MapValue.java diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index 621a62b2..d2b75ded 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -2155,6 +2155,12 @@ public class CarpetExpression y = miny; z = minz; } + + @Override + public String getString() + { + return String.format("rect[(%d,%d,%d),..,(%d,%d,%d)]",minx, miny, minz, maxx, maxy, maxz); + } }; }); @@ -2246,6 +2252,12 @@ public class CarpetExpression curradius = 0; curpos = 0; } + + @Override + public String getString() + { + return String.format("diamond[(%d,%d,%d),%d,0]",cx, cy, cz, width); + } }; } else @@ -2302,6 +2314,12 @@ public class CarpetExpression curpos = 0; curheight = -height; } + + @Override + public String getString() + { + return String.format("diamond[(%d,%d,%d),%d,%d]",cx, cy, cz, width, height); + } }; } }); diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index 155e7c7f..6b4f22b6 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -2402,6 +2402,8 @@ public class Expression implements Cloneable */ public void SystemFunctions() { + addUnaryFunction("hash_code", v -> new NumericValue(v.hashCode())); + addLazyFunction("bool", 1, (c, t, lv) -> { Value v = lv.get(0).evalValue(c, Context.BOOLEAN); if (v instanceof StringValue) diff --git a/src/main/java/carpet/script/value/BlockValue.java b/src/main/java/carpet/script/value/BlockValue.java index 9818e8ee..c84071bc 100644 --- a/src/main/java/carpet/script/value/BlockValue.java +++ b/src/main/java/carpet/script/value/BlockValue.java @@ -13,6 +13,7 @@ import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.GlobalPos; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; @@ -254,6 +255,14 @@ public class BlockValue extends Value return new BlockValue(blockState, world, pos); } + @Override + public int hashCode() + { + if (world != null && pos != null ) + return GlobalPos.create(world.getDimension().getType(), pos).hashCode(); + return ("b"+getString()).hashCode(); + } + public BlockPos getPos() { return pos; diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 52f563e6..9d4d1e71 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -138,6 +138,13 @@ public class EntityValue extends Value return "entity"; } + + @Override + public int hashCode() + { + return entity.hashCode(); + } + public static Pair, Predicate> getPredicate(String who) { Pair, Predicate> res = entityPredicates.get(who); diff --git a/src/main/java/carpet/script/value/FunctionSignatureValue.java b/src/main/java/carpet/script/value/FunctionSignatureValue.java index 73976f6b..48596a49 100644 --- a/src/main/java/carpet/script/value/FunctionSignatureValue.java +++ b/src/main/java/carpet/script/value/FunctionSignatureValue.java @@ -43,5 +43,11 @@ public class FunctionSignatureValue extends Value } public List getGlobals() {return globals;} + @Override + public int hashCode() + { + throw new UnsupportedOperationException("function signature value cannot be used as map key"); + } + } diff --git a/src/main/java/carpet/script/value/GlobalValue.java b/src/main/java/carpet/script/value/GlobalValue.java index 47dcf994..451582d9 100644 --- a/src/main/java/carpet/script/value/GlobalValue.java +++ b/src/main/java/carpet/script/value/GlobalValue.java @@ -19,4 +19,10 @@ public class GlobalValue extends Value { return false; } + + @Override + public int hashCode() + { + throw new UnsupportedOperationException("global value cannot be used as map key"); + } } diff --git a/src/main/java/carpet/script/value/LazyListValue.java b/src/main/java/carpet/script/value/LazyListValue.java index 016d6556..e34a83e7 100644 --- a/src/main/java/carpet/script/value/LazyListValue.java +++ b/src/main/java/carpet/script/value/LazyListValue.java @@ -44,6 +44,12 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato { return stepp > 0?(current < limit):(current > limit); } + + @Override + public String getString() + { + return String.format("[%d, %d, ..., %d]",start, start+stepp, limit); + } }; } @@ -145,4 +151,10 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato ((LazyListValue)copy).reset(); return copy; } + + @Override + public int hashCode() + { + return ("i"+getString()).hashCode(); + } } diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java index 6f0b656c..d24cd39c 100644 --- a/src/main/java/carpet/script/value/ListValue.java +++ b/src/main/java/carpet/script/value/ListValue.java @@ -4,11 +4,7 @@ import carpet.script.exception.InternalExpressionException; import net.minecraft.item.ItemStack; import net.minecraft.util.registry.Registry; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; import static java.lang.Math.abs; @@ -322,9 +318,16 @@ public class ListValue extends AbstractListValue index = index % numitems; return items.get((int)index); } + @Override public String getTypeString() { return "list"; } + + @Override + public int hashCode() + { + return items.hashCode(); + } } diff --git a/src/main/java/carpet/script/value/MapValue.java b/src/main/java/carpet/script/value/MapValue.java new file mode 100644 index 00000000..57610d09 --- /dev/null +++ b/src/main/java/carpet/script/value/MapValue.java @@ -0,0 +1,34 @@ +package carpet.script.value; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +public class MapValue extends AbstractListValue +{ + private Map map; + public MapValue() + { + map = new HashMap<>(); + } + + @Override + public Iterator iterator() + { + return map.keySet().iterator(); + } + + @Override + public String getString() + { + return Arrays.toString(map.entrySet().toArray()); + } + + @Override + public boolean getBoolean() + { + return !map.isEmpty(); + } + +} diff --git a/src/main/java/carpet/script/value/NullValue.java b/src/main/java/carpet/script/value/NullValue.java index a749a40b..91994ccd 100644 --- a/src/main/java/carpet/script/value/NullValue.java +++ b/src/main/java/carpet/script/value/NullValue.java @@ -44,4 +44,10 @@ public class NullValue extends NumericValue // TODO check nonsingleton code { return "null"; } + + @Override + public int hashCode() + { + return 0; + } } diff --git a/src/main/java/carpet/script/value/NumericValue.java b/src/main/java/carpet/script/value/NumericValue.java index c47c6ae0..48e90372 100644 --- a/src/main/java/carpet/script/value/NumericValue.java +++ b/src/main/java/carpet/script/value/NumericValue.java @@ -179,4 +179,13 @@ public class NumericValue extends Value return "number"; } + @Override + public int hashCode() + { + if (Math.abs(Math.floor(value + 0.5D)-value) < epsilon) // is sufficiently close to the integer value + return Long.hashCode(getLong()); + return Double.hashCode(value); + } + + } diff --git a/src/main/java/carpet/script/value/Value.java b/src/main/java/carpet/script/value/Value.java index ce061449..c0bffde9 100644 --- a/src/main/java/carpet/script/value/Value.java +++ b/src/main/java/carpet/script/value/Value.java @@ -182,4 +182,12 @@ public abstract class Value implements Comparable, Cloneable { throw new InternalExpressionException("How did you get here? Cannot get type of an intenal type."); } + + @Override + public int hashCode() + { + String stringVal = getString(); + if (stringVal.isEmpty()) return 0; + return ("s"+stringVal).hashCode(); + } } From 718a61912861c71273e01a045607da8c3a707835 Mon Sep 17 00:00:00 2001 From: gnembon Date: Wed, 2 Oct 2019 03:09:22 -0300 Subject: [PATCH 05/13] nbt support WIP and proper container API --- src/main/java/carpet/script/Expression.java | 83 ++++++-- .../script/value/AbstractListValue.java | 6 + .../script/value/ContainerValueInterface.java | 13 ++ .../carpet/script/value/LazyListValue.java | 5 - .../java/carpet/script/value/ListValue.java | 48 ++++- .../java/carpet/script/value/MapValue.java | 181 +++++++++++++++++- .../script/value/NBTSerializableValue.java | 101 ++++++++-- src/main/java/carpet/script/value/Value.java | 5 - 8 files changed, 393 insertions(+), 49 deletions(-) create mode 100644 src/main/java/carpet/script/value/ContainerValueInterface.java diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index 6b4f22b6..fe4fafab 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -13,10 +13,12 @@ import carpet.script.Fluff.TriFunction; import carpet.script.exception.ExpressionException; import carpet.script.exception.InternalExpressionException; import carpet.script.value.AbstractListValue; +import carpet.script.value.ContainerValueInterface; import carpet.script.value.FunctionSignatureValue; import carpet.script.value.GlobalValue; import carpet.script.value.LazyListValue; import carpet.script.value.ListValue; +import carpet.script.value.MapValue; import carpet.script.value.NumericValue; import carpet.script.value.StringValue; import carpet.script.value.Value; @@ -1227,7 +1229,12 @@ public class Expression implements Cloneable */ public void Operators() { - addBinaryOperator(".", precedence.get("attribute~."),true, Value::getElementAt); + addBinaryOperator(".", precedence.get("attribute~."),true, (container, key) -> + { + if (container instanceof ContainerValueInterface) + return ((ContainerValueInterface) container).get(key); + throw new InternalExpressionException("Cannot access elements of a non-container"); + }); addBinaryOperator("+", precedence.get("addition+-"), true, Value::add); addBinaryOperator("-", precedence.get("addition+-"), true, Value::subtract); addBinaryOperator("*", precedence.get("multiplication*/%"), true, Value::multiply); @@ -1322,7 +1329,7 @@ public class Expression implements Cloneable v1.assertAssignable(); String varname = v1.getVariable(); LazyValue boundedLHS; - if (v1 instanceof ListValue) + if (v1 instanceof ListValue || v1 instanceof MapValue) { ((ListValue) v1).append(v2); boundedLHS = (cc, tt)-> v1; @@ -1872,7 +1879,46 @@ public class Expression implements Cloneable return LazyListValue.range(from, to, step); }); - addBinaryFunction("get", Value::getElementAt); + addFunction("m", lv -> + { + if (lv.size() == 1 && lv.get(0) instanceof LazyListValue) + return new MapValue(((LazyListValue) lv.get(0)).unroll()); + return new MapValue(lv); + }); + + addUnaryFunction("keys", v -> { + if (v instanceof MapValue) + return new ListValue(((MapValue) v).getMap().keySet()); + throw new InternalExpressionException("keys applies only to maps"); + }); + + addUnaryFunction("values", v -> { + if (v instanceof MapValue) + return new ListValue(((MapValue) v).getMap().values()); + throw new InternalExpressionException("keys applies only to maps"); + }); + + addUnaryFunction("pairs", v -> { + if (v instanceof MapValue) + return new ListValue(((MapValue) v).getMap().entrySet().stream().map( + (p) -> ListValue.of(p.getKey(), p.getValue()) + ).collect(Collectors.toList())); + throw new InternalExpressionException("pairs applies only to maps"); + }); + + addBinaryFunction("get", (container, key) -> + { + if (container instanceof ContainerValueInterface) + return ((ContainerValueInterface) container).get(key); + throw new InternalExpressionException("Cannot access elements of a non-container"); + }); + + addBinaryFunction("has", (container, key) -> + { + if (container instanceof ContainerValueInterface) + return new NumericValue(((ContainerValueInterface) container).has(key)); + throw new InternalExpressionException("Cannot access elements of a non-container"); + }); //Deprecated, use "get" instead addBinaryFunction("element", (v1, v2) -> @@ -1892,27 +1938,36 @@ public class Expression implements Cloneable { if(lv.size()<3) { - throw new InternalExpressionException("put takes at least three arguments, a list, index, and values to insert at that index"); + throw new InternalExpressionException("put takes at least three arguments, a container, address, and values to insert at that index"); } - Value list = lv.get(0); - if (!(list instanceof ListValue)) + Value container = lv.get(0); + if (!(container instanceof ContainerValueInterface)) { - throw new InternalExpressionException("First argument of element should be a list"); + throw new InternalExpressionException("First argument of put should be a container: list, map or nbt"); } - int size = lv.size(); - //List items = ((ListValue)lv.get(0)).getItems(); - Value index = lv.get(1); - if (index == Value.NULL) + Value where = lv.get(1); + Value what = lv.get(2); + Value ret; + if (lv.size()>3) { - ((ListValue) list).extend(lv.subList(2,size)); + return ((ContainerValueInterface) container).put(where, what, lv.get(3)); } else { - ((ListValue) list).addAtIndex((int) NumericValue.asNumber(index).getLong(), lv.subList(2,size)); + return ((ContainerValueInterface) container).put(where, what); } - return new NumericValue(size-2); }); + addBinaryFunction("delete", (container, address) -> + { + if (container instanceof ContainerValueInterface) + { + return ((ContainerValueInterface) container).remove(address); + } + throw new InternalExpressionException("First argument of remove should be a container: list, map or nbt"); + }); + + //condition and expression will get a bound 'i' //returns last successful expression or false // while(cond, limit, expr) => ?? diff --git a/src/main/java/carpet/script/value/AbstractListValue.java b/src/main/java/carpet/script/value/AbstractListValue.java index f7e33a02..f72ffd59 100644 --- a/src/main/java/carpet/script/value/AbstractListValue.java +++ b/src/main/java/carpet/script/value/AbstractListValue.java @@ -1,9 +1,15 @@ package carpet.script.value; +import carpet.script.exception.InternalExpressionException; + import java.util.Iterator; public abstract class AbstractListValue extends Value { public abstract Iterator iterator(); public void fatality() { } + public void append(Value v) + { + throw new InternalExpressionException("Cannot append a value to abstract list"); + } } diff --git a/src/main/java/carpet/script/value/ContainerValueInterface.java b/src/main/java/carpet/script/value/ContainerValueInterface.java new file mode 100644 index 00000000..09e54b28 --- /dev/null +++ b/src/main/java/carpet/script/value/ContainerValueInterface.java @@ -0,0 +1,13 @@ +package carpet.script.value; + +public interface ContainerValueInterface +{ + public Value put(Value where, Value value); + default Value put(Value where, Value value, Value conditions) + { + return put(where, value); + } + public Value get(Value where); + public boolean has(Value where); + public Value remove(Value where); +} diff --git a/src/main/java/carpet/script/value/LazyListValue.java b/src/main/java/carpet/script/value/LazyListValue.java index e34a83e7..5035c018 100644 --- a/src/main/java/carpet/script/value/LazyListValue.java +++ b/src/main/java/carpet/script/value/LazyListValue.java @@ -126,11 +126,6 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato return false; } - @Override - public Value getElementAt(Value v2) - { - throw new InternalExpressionException("get element can only be obtained for regular lists"); - } @Override public String getTypeString() { diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java index d24cd39c..a875e519 100644 --- a/src/main/java/carpet/script/value/ListValue.java +++ b/src/main/java/carpet/script/value/ListValue.java @@ -9,7 +9,7 @@ import java.util.stream.Collectors; import static java.lang.Math.abs; -public class ListValue extends AbstractListValue +public class ListValue extends AbstractListValue implements ContainerValueInterface { protected List items; @Override @@ -98,6 +98,7 @@ public class ListValue extends AbstractListValue } return output; } + @Override public void append(Value v) { items.add(v); @@ -271,6 +272,8 @@ public class ListValue extends AbstractListValue super(list); } } + + @Override public int length() { return items.size(); @@ -309,7 +312,30 @@ public class ListValue extends AbstractListValue } @Override - public Value getElementAt(Value value) + public Value put(Value ind, Value value) + { + if (ind == Value.NULL) + { + items.add(value); + return items.get(items.size()-1); + } + else + { + int numitems = items.size(); + int index = (int) NumericValue.asNumber(ind).getLong(); + if (index < 0) + { + long range = abs(index) / numitems; + index += (range + 2) * numitems; + index = index % numitems; + } + while (index >= items.size()) items.add(Value.NULL); + return items.set(index, value); + } + } + + @Override + public Value get(Value value) { long index = NumericValue.asNumber(value).getLong(); int numitems = items.size(); @@ -319,6 +345,24 @@ public class ListValue extends AbstractListValue return items.get((int)index); } + @Override + public boolean has(Value where) + { + long index = NumericValue.asNumber(where).getLong(); + return index >= 0 && index < items.size(); + } + + @Override + public Value remove(Value where) + { + long index = NumericValue.asNumber(where).getLong(); + int numitems = items.size(); + long range = abs(index)/numitems; + index += (range+2)*numitems; + index = index % numitems; + return items.remove((int)index); + } + @Override public String getTypeString() { diff --git a/src/main/java/carpet/script/value/MapValue.java b/src/main/java/carpet/script/value/MapValue.java index 57610d09..adc6ffb7 100644 --- a/src/main/java/carpet/script/value/MapValue.java +++ b/src/main/java/carpet/script/value/MapValue.java @@ -1,18 +1,32 @@ package carpet.script.value; -import java.util.Arrays; +import carpet.script.exception.InternalExpressionException; + +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.stream.Collectors; -public class MapValue extends AbstractListValue +public class MapValue extends AbstractListValue implements ContainerValueInterface { private Map map; - public MapValue() + + private MapValue() { map = new HashMap<>(); } + public MapValue(List kvPairs) + { + this(); + for (Value v : kvPairs) + { + put(v); + } + } + @Override public Iterator iterator() { @@ -22,7 +36,20 @@ public class MapValue extends AbstractListValue @Override public String getString() { - return Arrays.toString(map.entrySet().toArray()); + return "{"+map.entrySet().stream().map((p) -> p.getKey().getString()+": "+p.getValue().getString()).collect(Collectors.joining(", "))+"}"; + } + + @Override + public String getPrettyString() + { + if (map.size()<6) + return "{"+map.entrySet().stream().map((p) -> p.getKey().getPrettyString()+": "+p.getValue().getPrettyString()).collect(Collectors.joining(", "))+"}"; + List keys = new ArrayList<>(map.keySet()); + int max = keys.size(); + return "{"+keys.get(0).getPrettyString()+": "+map.get(keys.get(0)).getPrettyString()+", "+ + keys.get(1).getPrettyString()+": "+map.get(keys.get(1)).getPrettyString()+", ..., "+ + keys.get(max-2).getPrettyString()+": "+map.get(keys.get(max-2)).getPrettyString()+", "+ + keys.get(max-1).getPrettyString()+": "+map.get(keys.get(max-1)).getPrettyString()+"}"; } @Override @@ -31,4 +58,150 @@ public class MapValue extends AbstractListValue return !map.isEmpty(); } + @Override + public Value clone() + { + return new MapValue(map); + } + public MapValue(Map other) + { + map = new HashMap<>(other); + } + + @Override + public Value add(Value o) + { + throw new InternalExpressionException("cannot add to a map value"); + } + + @Override + public Value subtract(Value v) + { + throw new InternalExpressionException("cannot subtract to a map value"); + } + + @Override + public Value multiply(Value v) + { + throw new InternalExpressionException("cannot multiply to a map value"); + } + + @Override + public Value divide(Value v) + { + throw new InternalExpressionException("cannot divide to a map value"); + } + + public void put(Value v) + { + if (!(v instanceof ListValue)) + { + map.put(v, Value.NULL); + return; + } + ListValue pair = (ListValue)v; + if (pair.getItems().size() != 2) + { + throw new InternalExpressionException("map constructor requires elements that have two elements"); + } + map.put(pair.getItems().get(0), pair.getItems().get(1)); + } + + @Override + public void append(Value v) + { + put(v); + } + + @Override + public int compareTo(Value o) + { + throw new InternalExpressionException("cannot compare to a map value"); + } + + @Override + public boolean equals(final Value o) + { + if (o instanceof MapValue) + { + MapValue ol = (MapValue)o; + int this_size = this.map.keySet().size(); + int o_size = ol.map.keySet().size(); + if (this_size != o_size) return false; + if (this_size == 0) return true; + return this.map.keySet().equals(ol.map.keySet()); + } + return false; + } + + public Map getMap() + { + return map; + } + + public void extend(List subList) + { + for (Value v: subList) put(v); + } + + @Override + public int length() + { + return map.size(); + } + + @Override + public Value in(Value value1) + { + if (map.containsKey(value1)) return value1; + return Value.NULL; + } + + @Override + public Value slice(long from, long to) + { + throw new InternalExpressionException("cannot slice a map value"); + } + + @Override + public double readNumber() + { + return (double)map.size(); + } + + @Override + public Value get(Value v2) + { + return map.getOrDefault(v2, Value.NULL); + } + + @Override + public boolean has(Value where) + { + return map.containsKey(where); + } + + @Override + public Value remove(Value where) + { + return map.remove(where); + } + + @Override + public Value put(Value key, Value value) + { + return map.put(key, value); + } + + @Override + public String getTypeString() + { + return "map"; + } + + @Override + public int hashCode() + { + return map.hashCode(); + } } diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index efb616ee..65eddd74 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -3,6 +3,7 @@ package carpet.script.value; import carpet.script.CarpetContext; import carpet.script.LazyValue; import carpet.script.exception.InternalExpressionException; +import carpet.settings.CarpetSettings; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.block.entity.HopperBlockEntity; @@ -20,17 +21,18 @@ import net.minecraft.nbt.StringNbtReader; import net.minecraft.nbt.Tag; import net.minecraft.util.math.BlockPos; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Supplier; import java.util.stream.Collectors; -public class NBTSerializableValue extends Value +public class NBTSerializableValue extends Value implements ContainerValueInterface { private String nbtString = null; private Tag nbtTag = null; - private Supplier nbtSupplier = null; + private Supplier nbtSupplier = null; public NBTSerializableValue(ItemStack stack) { @@ -43,7 +45,7 @@ public class NBTSerializableValue extends Value { try { - return (new StringNbtReader(new StringReader(nbtString))).parseCompoundTag(); + return (new StringNbtReader(new StringReader(nbtString))).parseTag(); } catch (CommandSyntaxException e) { @@ -207,6 +209,82 @@ public class NBTSerializableValue extends Value } } + @Override + public Value put(Value where, Value value) + { + /// WIP + + NbtPathArgumentType.NbtPath path = cachePath(where.getString()); + Tag tagToInsert = value instanceof NBTSerializableValue ? + ((NBTSerializableValue) value).getTag() : + new NBTSerializableValue(value.getString()).getTag(); + CarpetSettings.LOG.error("Inserting tag of class: "+tagToInsert.getClass().getSimpleName()); + + Tag tag = getTag().copy(); + try + { + Collection collection_1 = path.get(tag); //, CompoundTag::new); + for(Tag t: collection_1) + { + ((CompoundTag)t).copyFrom((CompoundTag) tagToInsert); + } + if (collection_1.size() > 0) + { + nbtSupplier = () -> tag; + nbtTag = tag; + } + return this; + } + catch (CommandSyntaxException e) + { + return Value.NULL; + } + } + + @Override + public Value put(Value where, Value value, Value conditions) + { + throw new InternalExpressionException("not implemented"); + } + + @Override + public Value get(Value value) + { + NbtPathArgumentType.NbtPath path = cachePath(value.getString()); + try + { + List tags = path.get(getTag()); + if (tags.size()==0) + return Value.NULL; + if (tags.size()==1) + return NBTSerializableValue.decodeTag(tags.get(0)); + return ListValue.wrap(tags.stream().map(NBTSerializableValue::decodeTag).collect(Collectors.toList())); + } + catch (CommandSyntaxException ignored) { } + return Value.NULL; + } + + @Override + public boolean has(Value where) + { + return cachePath(where.getString()).count(getTag()) > 0; + } + + @Override + public Value remove(Value where) + { + NbtPathArgumentType.NbtPath path = cachePath(where.getString()); + Tag tag = getTag().copy(); + int removed = path.remove(tag); + if (removed > 0) + { + nbtSupplier = null; + nbtTag = tag; + return Value.TRUE; + } + return Value.FALSE; + } + public static class InventoryLocator { public Object owner; @@ -242,22 +320,7 @@ public class NBTSerializableValue extends Value return res; } - @Override - public Value getElementAt(Value value) - { - NbtPathArgumentType.NbtPath path = cachePath(value.getString()); - try - { - List tags = path.get(getTag()); - if (tags.size()==0) - return Value.NULL; - if (tags.size()==1) - return NBTSerializableValue.decodeTag(tags.get(0)); - return ListValue.wrap(tags.stream().map(NBTSerializableValue::decodeTag).collect(Collectors.toList())); - } - catch (CommandSyntaxException ignored) { } - return Value.NULL; - } + @Override public String getTypeString() diff --git a/src/main/java/carpet/script/value/Value.java b/src/main/java/carpet/script/value/Value.java index c0bffde9..eda60bb7 100644 --- a/src/main/java/carpet/script/value/Value.java +++ b/src/main/java/carpet/script/value/Value.java @@ -173,11 +173,6 @@ public abstract class Value implements Comparable, Cloneable return (long)readNumber(); } - public Value getElementAt(Value v2) - { - throw new InternalExpressionException("get element can only be obtained for lists and nbt values"); - } - public String getTypeString() { throw new InternalExpressionException("How did you get here? Cannot get type of an intenal type."); From 801121220a5da503cf40eea2a65ed473a198e593 Mon Sep 17 00:00:00 2001 From: gnembon Date: Wed, 2 Oct 2019 14:27:44 -0300 Subject: [PATCH 06/13] experiments with nbt --- src/main/java/carpet/script/value/NBTSerializableValue.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index 65eddd74..bfa83caf 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -218,14 +218,15 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa Tag tagToInsert = value instanceof NBTSerializableValue ? ((NBTSerializableValue) value).getTag() : new NBTSerializableValue(value.getString()).getTag(); - CarpetSettings.LOG.error("Inserting tag of class: "+tagToInsert.getClass().getSimpleName()); + CarpetSettings.LOG.error("Inserting tag "+tagToInsert.asString()+" of class: "+tagToInsert.getClass().getSimpleName()); Tag tag = getTag().copy(); try { - Collection collection_1 = path.get(tag); //, CompoundTag::new); + Collection collection_1 = path.putIfAbsent(tag, CompoundTag::new); for(Tag t: collection_1) { + CarpetSettings.LOG.error("Found target: "+t.asString()); ((CompoundTag)t).copyFrom((CompoundTag) tagToInsert); } if (collection_1.size() > 0) From 974aa51e2a98f7feb7f3903ea9231891dc34a5a6 Mon Sep 17 00:00:00 2001 From: gnembon Date: Sat, 5 Oct 2019 02:03:16 -0300 Subject: [PATCH 07/13] nbt done --- src/main/java/carpet/script/Expression.java | 1 - .../java/carpet/script/value/EntityValue.java | 16 +- .../script/value/NBTSerializableValue.java | 154 +++++++++++++----- 3 files changed, 120 insertions(+), 51 deletions(-) diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index fe4fafab..042ebc76 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -1947,7 +1947,6 @@ public class Expression implements Cloneable } Value where = lv.get(1); Value what = lv.get(2); - Value ret; if (lv.size()>3) { return ((ContainerValueInterface) container).put(where, what, lv.get(3)); diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 9d4d1e71..9dfea2be 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -393,21 +393,11 @@ public class EntityValue extends Value put("nbt",(e, a) -> { CompoundTag nbttagcompound = e.toTag((new CompoundTag())); if (a==null) - return new NBTSerializableValue(nbttagcompound);//StringValue(nbttagcompound.toString()); - NbtPathArgumentType.NbtPath path = NBTSerializableValue.cachePath(a.getString()); - try - { - List tags = path.get(nbttagcompound); - if (tags.size()==0) - return Value.NULL; - if (tags.size()==1) - return NBTSerializableValue.decodeTag(tags.get(0)); - return ListValue.wrap(tags.stream().map(NBTSerializableValue::decodeTag).collect(Collectors.toList())); - } - catch (CommandSyntaxException ignored) { } - return Value.NULL; + return new NBTSerializableValue(nbttagcompound); + return new NBTSerializableValue(nbttagcompound).get(a); }); }}; + private static Req assertEntityArgType(Class klass, Value arg) { if (!(arg instanceof EntityValue)) diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index bfa83caf..79b250d9 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -3,7 +3,6 @@ package carpet.script.value; import carpet.script.CarpetContext; import carpet.script.LazyValue; import carpet.script.exception.InternalExpressionException; -import carpet.settings.CarpetSettings; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.block.entity.HopperBlockEntity; @@ -15,8 +14,10 @@ import net.minecraft.entity.passive.VillagerEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.Inventory; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.AbstractListTag; import net.minecraft.nbt.AbstractNumberTag; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; import net.minecraft.nbt.StringNbtReader; import net.minecraft.nbt.Tag; import net.minecraft.util.math.BlockPos; @@ -158,7 +159,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa return slot; } - public static Value decodeTag(Tag t) + private static Value decodeTag(Tag t) { if (t instanceof CompoundTag) return new NBTSerializableValue(t); @@ -168,13 +169,20 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa return new StringValue(t.asString()); } - public Tag getTag() + private Tag getTag() { if (nbtTag == null) nbtTag = nbtSupplier.get(); return nbtTag; } + private void replaceTag(Tag newTag) + { + nbtTag = newTag; + nbtString = null; + nbtSupplier = null; // just to be sure + } + @Override public boolean equals(final Value o) { @@ -212,40 +220,115 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa @Override public Value put(Value where, Value value) { - /// WIP - - NbtPathArgumentType.NbtPath path = cachePath(where.getString()); - Tag tagToInsert = value instanceof NBTSerializableValue ? - ((NBTSerializableValue) value).getTag() : - new NBTSerializableValue(value.getString()).getTag(); - CarpetSettings.LOG.error("Inserting tag "+tagToInsert.asString()+" of class: "+tagToInsert.getClass().getSimpleName()); - - Tag tag = getTag().copy(); - try - { - Collection collection_1 = path.putIfAbsent(tag, CompoundTag::new); - for(Tag t: collection_1) - { - CarpetSettings.LOG.error("Found target: "+t.asString()); - ((CompoundTag)t).copyFrom((CompoundTag) tagToInsert); - } - if (collection_1.size() > 0) - { - nbtSupplier = () -> tag; - nbtTag = tag; - } - return this; - } - catch (CommandSyntaxException e) - { - return Value.NULL; - } + return put(where, value, new StringValue("replace")); } @Override public Value put(Value where, Value value, Value conditions) { - throw new InternalExpressionException("not implemented"); + /// WIP + + NbtPathArgumentType.NbtPath path = cachePath(where.getString()); + Tag tagToInsert = value instanceof NBTSerializableValue ? + ((NBTSerializableValue) value).getTag() : + new NBTSerializableValue(value.getString()).getTag(); + Tag modifiedTag; + if (conditions instanceof NumericValue) + { + modifiedTag = modify_insert((int)((NumericValue) conditions).getLong(), path, tagToInsert); + } + else + { + String ops = conditions.getString(); + if (ops.equalsIgnoreCase("merge")) + { + modifiedTag = modify_merge(path, tagToInsert); + } + else if (ops.equalsIgnoreCase("replace")) + { + modifiedTag = modify_replace(path, tagToInsert); + } + else + { + return this; + } + } + if (modifiedTag != null) + { + replaceTag(modifiedTag); + } + return this; + } + + + private Tag modify_insert(int index, NbtPathArgumentType.NbtPath nbtPath, Tag newElement) + { + Collection targets; + Tag currentTag = getTag().copy(); + try + { + targets = nbtPath.putIfAbsent(currentTag, ListTag::new); + } + catch (CommandSyntaxException e) + { + return null; + } + + boolean modified = false; + for (Tag target : targets) + { + if (!(target instanceof AbstractListTag)) + { + continue; + } + try + { + AbstractListTag targetList = (AbstractListTag) target; + targetList.addTag(index < 0 ? targetList.size() + index + 1 : index, newElement.copy()); + modified = true; + } + catch (IndexOutOfBoundsException ignored) + { + } + } + return modified?currentTag:null; + } + + + private Tag modify_merge(NbtPathArgumentType.NbtPath nbtPath, Tag replacement) //nbtPathArgumentType$NbtPath_1, list_1) + { + if (!(replacement instanceof CompoundTag)) + { + return getTag(); + } + Tag originalTag = getTag().copy(); + try + { + for (Tag target : nbtPath.putIfAbsent(originalTag, CompoundTag::new)) + { + if (!(target instanceof CompoundTag)) + { + continue; + } + ((CompoundTag) target).copyFrom((CompoundTag) replacement); + } + } + catch (CommandSyntaxException ignored) { } + return originalTag; + } + + private Tag modify_replace(NbtPathArgumentType.NbtPath nbtPath, Tag replacement) //nbtPathArgumentType$NbtPath_1, list_1) + { + Tag originalTag = getTag().copy(); + try + { + nbtPath.put(originalTag, () -> replacement); + } + catch (CommandSyntaxException e) + { + return null; + } + return originalTag; } @Override @@ -279,8 +362,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa int removed = path.remove(tag); if (removed > 0) { - nbtSupplier = null; - nbtTag = tag; + replaceTag(tag); return Value.TRUE; } return Value.FALSE; @@ -302,7 +384,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa } private static Map pathCache = new HashMap<>(); - public static NbtPathArgumentType.NbtPath cachePath(String arg) + private static NbtPathArgumentType.NbtPath cachePath(String arg) { NbtPathArgumentType.NbtPath res = pathCache.get(arg); if (res != null) @@ -321,8 +403,6 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa return res; } - - @Override public String getTypeString() { From dab9c5debef998ca4f036c647a585eecb7bb4dd3 Mon Sep 17 00:00:00 2001 From: gnembon Date: Sat, 5 Oct 2019 02:23:53 -0300 Subject: [PATCH 08/13] cleanup and added requirement for floats to start with 0.2 --- src/main/java/carpet/script/Expression.java | 4 ++-- src/main/java/carpet/script/Tokenizer.java | 3 ++- src/main/java/carpet/script/value/ListValue.java | 9 +-------- src/main/java/carpet/script/value/MapValue.java | 7 +------ 4 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index 042ebc76..e1d3ce48 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -1900,7 +1900,7 @@ public class Expression implements Cloneable addUnaryFunction("pairs", v -> { if (v instanceof MapValue) - return new ListValue(((MapValue) v).getMap().entrySet().stream().map( + return ListValue.wrap(((MapValue) v).getMap().entrySet().stream().map( (p) -> ListValue.of(p.getKey(), p.getValue()) ).collect(Collectors.toList())); throw new InternalExpressionException("pairs applies only to maps"); @@ -1920,7 +1920,7 @@ public class Expression implements Cloneable throw new InternalExpressionException("Cannot access elements of a non-container"); }); - //Deprecated, use "get" instead + //Deprecated, use "get" instead, or . operator addBinaryFunction("element", (v1, v2) -> { if (!(v1 instanceof ListValue)) diff --git a/src/main/java/carpet/script/Tokenizer.java b/src/main/java/carpet/script/Tokenizer.java index b84be668..0a1f7524 100644 --- a/src/main/java/carpet/script/Tokenizer.java +++ b/src/main/java/carpet/script/Tokenizer.java @@ -94,7 +94,8 @@ public class Tokenizer implements Iterator boolean isHex = false; - if (Character.isDigit(ch) || (ch == decimalSeparator && Character.isDigit(peekNextChar()))) + if (Character.isDigit(ch)) // || (ch == decimalSeparator && Character.isDigit(peekNextChar()))) + // decided to no support this notation to favour element access via . operator { if (ch == '0' && (peekNextChar() == 'x' || peekNextChar() == 'X')) isHex = true; diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java index a875e519..fce10b91 100644 --- a/src/main/java/carpet/script/value/ListValue.java +++ b/src/main/java/carpet/script/value/ListValue.java @@ -216,14 +216,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf { if (o instanceof ListValue) { - ListValue ol = (ListValue)o; - int this_size = this.getItems().size(); - int o_size = ol.getItems().size(); - if (this_size != o_size) return false; - if (this_size == 0) return true; - for (int i = 0; i < this_size; i++) - if (!this.items.get(i).equals(ol.items.get(i))) return false; - return true; + return getItems().equals(((ListValue) o).getItems()); } return false; } diff --git a/src/main/java/carpet/script/value/MapValue.java b/src/main/java/carpet/script/value/MapValue.java index adc6ffb7..4c782770 100644 --- a/src/main/java/carpet/script/value/MapValue.java +++ b/src/main/java/carpet/script/value/MapValue.java @@ -124,12 +124,7 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa { if (o instanceof MapValue) { - MapValue ol = (MapValue)o; - int this_size = this.map.keySet().size(); - int o_size = ol.map.keySet().size(); - if (this_size != o_size) return false; - if (this_size == 0) return true; - return this.map.keySet().equals(ol.map.keySet()); + return map.equals(((MapValue) o).map); } return false; } From 2167c929d57f93eac809d49a0cc991fa21dff200 Mon Sep 17 00:00:00 2001 From: gnembon Date: Sun, 6 Oct 2019 22:06:38 -0300 Subject: [PATCH 09/13] two more events, and nbt cleanups --- .../java/carpet/commands/ScriptCommand.java | 12 +- .../mixins/MinecraftServer_scarpetMixin.java | 8 +- ...PlayNetworkHandler_scarpetEventsMixin.java | 80 +++- .../ServerPlayerEntity_scarpetEventMixin.java | 46 ++ ...InteractionManager_scarpetEventsMixin.java | 5 +- .../java/carpet/script/CarpetEventServer.java | 426 +++++++++++------- .../java/carpet/script/CarpetExpression.java | 32 +- .../carpet/script/CarpetScriptServer.java | 2 +- .../script/value/NBTSerializableValue.java | 57 ++- src/main/resources/carpet.mixins.json | 1 + 10 files changed, 462 insertions(+), 207 deletions(-) create mode 100644 src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java diff --git a/src/main/java/carpet/commands/ScriptCommand.java b/src/main/java/carpet/commands/ScriptCommand.java index 347876f2..fb4b690f 100644 --- a/src/main/java/carpet/commands/ScriptCommand.java +++ b/src/main/java/carpet/commands/ScriptCommand.java @@ -233,7 +233,7 @@ public class ScriptCommand executes( (cc) -> listEvents(cc.getSource())). then(literal("add_to"). then(argument("event", StringArgumentType.word()). - suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.events.eventHandlers.keySet() ,bb)). + suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.byName.keySet() ,bb)). then(argument("call", StringArgumentType.word()). suggests( (cc, bb) -> suggestMatching(suggestFunctionCalls(cc), bb)). executes( (cc) -> CarpetServer.scriptServer.events.addEvent( @@ -253,9 +253,9 @@ public class ScriptCommand )?1:0)))))). then(literal("remove_from"). then(argument("event", StringArgumentType.word()). - suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.events.eventHandlers.keySet() ,bb)). + suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.byName.keySet() ,bb)). then(argument("call", StringArgumentType.greedyString()). - suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.events.eventHandlers.get(StringArgumentType.getString(cc, "event")).callList.stream().map(CarpetEventServer.Callback::toString), bb)). + suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.byName.get(StringArgumentType.getString(cc, "event")).handler.callList.stream().map(CarpetEventServer.Callback::toString), bb)). executes( (cc) -> CarpetServer.scriptServer.events.removeEvent( StringArgumentType.getString(cc, "event"), StringArgumentType.getString(cc, "call") @@ -293,14 +293,14 @@ public class ScriptCommand private static int listEvents(ServerCommandSource source) { Messenger.m(source, "w Lists ALL event handlers:"); - for (String event: CarpetServer.scriptServer.events.eventHandlers.keySet()) + for (CarpetEventServer.Event event: CarpetEventServer.Event.values()) { boolean shownEvent = false; - for (CarpetEventServer.Callback c: CarpetServer.scriptServer.events.eventHandlers.get(event).callList) + for (CarpetEventServer.Callback c: event.handler.callList) { if (!shownEvent) { - Messenger.m(source, "w Handlers for "+event+": "); + Messenger.m(source, "w Handlers for "+event.name+": "); shownEvent = true; } Messenger.m(source, "w - "+c.udf+(c.host==null?"":" (from "+c.host+")")); diff --git a/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java b/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java index 6e008402..a656b640 100644 --- a/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java +++ b/src/main/java/carpet/mixins/MinecraftServer_scarpetMixin.java @@ -14,6 +14,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.function.BooleanSupplier; +import static carpet.script.CarpetEventServer.Event.ENDER_TICK; +import static carpet.script.CarpetEventServer.Event.NETHER_TICK; +import static carpet.script.CarpetEventServer.Event.TICK; + @Mixin(MinecraftServer.class) public abstract class MinecraftServer_scarpetMixin extends NonBlockingThreadExecutor implements MinecraftServerInterface { @@ -42,6 +46,8 @@ public abstract class MinecraftServer_scarpetMixin extends NonBlockingThreadExec )) public void tickTasks(BooleanSupplier booleanSupplier_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onTick(); + TICK.onTick(); + NETHER_TICK.onTick(); + ENDER_TICK.onTick(); } } diff --git a/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java b/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java index aacf95ac..82420afe 100644 --- a/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java +++ b/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java @@ -1,6 +1,6 @@ package carpet.mixins; -import carpet.CarpetServer; +import net.minecraft.item.ItemStack; import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.packet.ClientCommandC2SPacket; @@ -16,8 +16,25 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import static carpet.script.CarpetEventServer.Event.PLAYER_ATTACKS_ENTITY; +import static carpet.script.CarpetEventServer.Event.PLAYER_CLICKS_BLOCK; +import static carpet.script.CarpetEventServer.Event.PLAYER_DEPLOYS_ELYTRA; +import static carpet.script.CarpetEventServer.Event.PLAYER_INERACTSW_WITH_ENTITY; +import static carpet.script.CarpetEventServer.Event.PLAYER_RELEASED_ITEM; +import static carpet.script.CarpetEventServer.Event.PLAYER_RIDES; +import static carpet.script.CarpetEventServer.Event.PLAYER_JUMPS; +import static carpet.script.CarpetEventServer.Event.PLAYER_RIGHT_CLICKS_BLOCK; +import static carpet.script.CarpetEventServer.Event.PLAYER_STARTS_SNEAKING; +import static carpet.script.CarpetEventServer.Event.PLAYER_STARTS_SPRINTING; +import static carpet.script.CarpetEventServer.Event.PLAYER_STOPS_SNEAKING; +import static carpet.script.CarpetEventServer.Event.PLAYER_STOPS_SPRINTING; +import static carpet.script.CarpetEventServer.Event.PLAYER_USES_ITEM; +import static carpet.script.CarpetEventServer.Event.PLAYER_WAKES_UP; + + @Mixin(ServerPlayNetworkHandler.class) public class ServerPlayNetworkHandler_scarpetEventsMixin { @@ -26,9 +43,9 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin @Inject(method = "onPlayerInput", at = @At(value = "RETURN")) private void checkMoves(PlayerInputC2SPacket p, CallbackInfo ci) { - if (p.getSideways() != 0.0F || p.getForward() != 0.0F || p.isJumping() || p.isSneaking()) + if (PLAYER_RIDES.isNeeded() && (p.getSideways() != 0.0F || p.getForward() != 0.0F || p.isJumping() || p.isSneaking())) { - CarpetServer.scriptServer.events.onMountControls(player, p.getSideways(), p.getForward(), p.isJumping(), p.isSneaking()); + PLAYER_RIDES.onMountControls(player, p.getSideways(), p.getForward(), p.isJumping(), p.isSneaking()); } } @@ -38,17 +55,37 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onJump(PlayerMoveC2SPacket playerMoveC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onPlayerJumped(player); + PLAYER_JUMPS.onPlayerEvent(player); } @Inject(method = "onPlayerAction", at = @At( value = "INVOKE", - target = "Lnet/minecraft/server/network/ServerPlayerInteractionManager;method_14263(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/server/network/packet/PlayerActionC2SPacket$Action;Lnet/minecraft/util/math/Direction;I)V" + target = "Lnet/minecraft/server/network/ServerPlayerInteractionManager;method_14263(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/server/network/packet/PlayerActionC2SPacket$Action;Lnet/minecraft/util/math/Direction;I)V", + shift = At.Shift.AFTER )) private void onClicked(PlayerActionC2SPacket packet, CallbackInfo ci) { if (packet.getAction() == PlayerActionC2SPacket.Action.START_DESTROY_BLOCK) - CarpetServer.scriptServer.events.onBlockClicked(player, packet.getPos(), packet.getDirection()); + PLAYER_CLICKS_BLOCK.onBlockAction(player, packet.getPos(), packet.getDirection()); + } + + @Redirect(method = "onPlayerAction", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;stopUsingItem()V" + )) + private void onStopUsing(ServerPlayerEntity serverPlayerEntity) + { + if (PLAYER_RELEASED_ITEM.isNeeded()) + { + Hand hand = serverPlayerEntity.getActiveHand(); + ItemStack stack = serverPlayerEntity.getActiveItem().copy(); + serverPlayerEntity.stopUsingItem(); + PLAYER_RELEASED_ITEM.onItemAction(player, hand, stack); + } + else + { + serverPlayerEntity.stopUsingItem(); + } } @Inject(method = "onPlayerInteractBlock", at = @At( @@ -57,9 +94,12 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onBlockInteracted(PlayerInteractBlockC2SPacket playerInteractBlockC2SPacket_1, CallbackInfo ci) { - Hand hand = playerInteractBlockC2SPacket_1.getHand(); - BlockHitResult hitRes = playerInteractBlockC2SPacket_1.getHitY(); - CarpetServer.scriptServer.events.onRightClickBlock(player, hand, hitRes ); + if (PLAYER_RIGHT_CLICKS_BLOCK.isNeeded()) + { + Hand hand = playerInteractBlockC2SPacket_1.getHand(); + BlockHitResult hitRes = playerInteractBlockC2SPacket_1.getHitY(); + PLAYER_RIGHT_CLICKS_BLOCK.onBlockHit(player, hand, hitRes); + } } @Inject(method = "onPlayerInteractItem", at = @At( @@ -68,7 +108,11 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onItemClicked(PlayerInteractItemC2SPacket playerInteractItemC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onRightClick(player, playerInteractItemC2SPacket_1.getHand()); + if (PLAYER_USES_ITEM.isNeeded()) + { + Hand hand = playerInteractItemC2SPacket_1.getHand(); + PLAYER_USES_ITEM.onItemAction(player, hand, player.getStackInHand(hand).copy()); + } } @Inject(method = "onClientCommand", at = @At( @@ -78,7 +122,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onStartSneaking(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onStartSneaking(player); + PLAYER_STARTS_SNEAKING.onPlayerEvent(player); } @Inject(method = "onClientCommand", at = @At( @@ -88,7 +132,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onStopSneaking(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onStopSneaking(player); + PLAYER_STOPS_SNEAKING.onPlayerEvent(player); } @Inject(method = "onClientCommand", at = @At( @@ -98,7 +142,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onStartSprinting(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onStartSprinting(player); + PLAYER_STARTS_SPRINTING.onPlayerEvent(player); } @Inject(method = "onClientCommand", at = @At( @@ -108,7 +152,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onStopSprinting(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onStopSprinting(player); + PLAYER_STOPS_SPRINTING.onPlayerEvent(player); } @Inject(method = "onClientCommand", at = @At( @@ -119,7 +163,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin private void onWakeUp(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) { //weird one - doesn't seem to work, maybe MP - CarpetServer.scriptServer.events.onOutOfBed(player); + PLAYER_WAKES_UP.onPlayerEvent(player); } @Inject(method = "onClientCommand", at = @At( @@ -129,7 +173,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onElytraEngage(ClientCommandC2SPacket clientCommandC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onElytraDeploy(player); + PLAYER_DEPLOYS_ELYTRA.onPlayerEvent(player); } @Inject(method = "onPlayerInteractEntity", at = @At( @@ -138,7 +182,7 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin )) private void onEntityInteract(PlayerInteractEntityC2SPacket playerInteractEntityC2SPacket_1, CallbackInfo ci) { - CarpetServer.scriptServer.events.onEntityInteracted(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), playerInteractEntityC2SPacket_1.getHand()); + PLAYER_INERACTSW_WITH_ENTITY.onEntityAction(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), playerInteractEntityC2SPacket_1.getHand()); } @Inject(method = "onPlayerInteractEntity", at = @At( @@ -148,6 +192,6 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin private void onEntityAttack(PlayerInteractEntityC2SPacket playerInteractEntityC2SPacket_1, CallbackInfo ci) { //todo add hit and hand in the future - CarpetServer.scriptServer.events.onEntityAttacked(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld())); + PLAYER_ATTACKS_ENTITY.onEntityAction(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), null); } } diff --git a/src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java b/src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java new file mode 100644 index 00000000..d291efb5 --- /dev/null +++ b/src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java @@ -0,0 +1,46 @@ +package carpet.mixins; + +import com.mojang.authlib.GameProfile; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.Hand; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import static carpet.script.CarpetEventServer.Event.PLAYER_FINISHED_USING_ITEM; + +@Mixin(ServerPlayerEntity.class) +public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity +{ + public ServerPlayerEntity_scarpetEventMixin(World world_1, GameProfile gameProfile_1) + { + super(world_1, gameProfile_1); + } + + @Shadow protected abstract void method_6040(); + + @Redirect(method = "method_6040", at = @At( + value = "INVOKE", + target = "Lnet/minecraft/entity/player/PlayerEntity;method_6040()V" + )) + private void finishedUsingItem(PlayerEntity playerEntity) + { + if (PLAYER_FINISHED_USING_ITEM.isNeeded()) + { + Hand hand = getActiveHand(); + ItemStack stack = getActiveItem().copy(); + // do vanilla + super.method_6040(); + PLAYER_FINISHED_USING_ITEM.onItemAction((ServerPlayerEntity) (Object)this, hand, stack); + } + else + { + // do vanilla + super.method_6040(); + } + } +} diff --git a/src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java b/src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java index add9efd1..79e83cec 100644 --- a/src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java +++ b/src/main/java/carpet/mixins/ServerPlayerInteractionManager_scarpetEventsMixin.java @@ -1,6 +1,5 @@ package carpet.mixins; -import carpet.CarpetServer; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; @@ -14,6 +13,8 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import static carpet.script.CarpetEventServer.Event.PLAYER_BREAK_BLOCK; + @Mixin(ServerPlayerInteractionManager.class) public class ServerPlayerInteractionManager_scarpetEventsMixin { @@ -26,6 +27,6 @@ public class ServerPlayerInteractionManager_scarpetEventsMixin )) private void onBlockBroken(BlockPos blockPos_1, CallbackInfoReturnable cir, BlockState blockState_1, BlockEntity be, Block b, boolean boolean_1) { - CarpetServer.scriptServer.events.onBlockBroken(player, blockPos_1, blockState_1); + PLAYER_BREAK_BLOCK.onBlockBroken(player, blockPos_1, blockState_1); } } diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java index 72a52108..c68e9960 100644 --- a/src/main/java/carpet/script/CarpetEventServer.java +++ b/src/main/java/carpet/script/CarpetEventServer.java @@ -7,13 +7,10 @@ import carpet.script.value.ListValue; import carpet.script.value.NumericValue; import carpet.script.value.StringValue; import carpet.script.value.Value; -import carpet.settings.CarpetSettings; import net.minecraft.block.BlockState; import net.minecraft.entity.damage.DamageSource; -import net.minecraft.nbt.CompoundTag; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.entity.Entity; -import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.util.Hand; @@ -21,9 +18,6 @@ import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; -import net.minecraft.util.registry.Registry; -import net.minecraft.world.World; -import net.minecraft.world.dimension.DimensionType; import java.util.ArrayList; import java.util.Arrays; @@ -36,10 +30,11 @@ import java.util.Map; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; public class CarpetEventServer { + public List scheduledCalls = new LinkedList<>(); + public static class Callback { public String host; @@ -146,9 +141,262 @@ public class CarpetEventServer } } - public Map eventHandlers = new HashMap<>(); + public enum Event + { + TICK("tick", new CallbackList(0)) + { + @Override + public void onTick() + { + handler.call(Collections::emptyList, CarpetServer.minecraft_server::getCommandSource); + } + }, + NETHER_TICK("tick_nether",new CallbackList(0)) + { + @Override + public void onTick() + { + handler.call(Collections::emptyList, CarpetServer.minecraft_server::getCommandSource); + } + }, + ENDER_TICK("tick_ender",new CallbackList(0)) + { + @Override + public void onTick() + { + handler.call(Collections::emptyList, CarpetServer.minecraft_server::getCommandSource); + } + }, + PLAYER_JUMPS("player_jumps", new CallbackList(1)) + { + @Override + public void onPlayerEvent(ServerPlayerEntity player) + { + handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + }, + PLAYER_DEPLOYS_ELYTRA("player_deploys_elytra",new CallbackList(1)) + { + @Override + public void onPlayerEvent(ServerPlayerEntity player) + { + handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + }, + PLAYER_WAKES_UP("player_wakes_up",new CallbackList(1)) + { + @Override + public void onPlayerEvent(ServerPlayerEntity player) + { + handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + }, + PLAYER_RIDES("player_rides",new CallbackList(5)) + { + @Override + public void onMountControls(ServerPlayerEntity player, float strafeSpeed, float forwardSpeed, boolean jumping, boolean sneaking) + { + handler.call( () -> Arrays.asList( + ((c, t) -> new EntityValue(player)), + ((c, t) -> new NumericValue(forwardSpeed)), + ((c, t) -> new NumericValue(strafeSpeed)), + ((c, t) -> new NumericValue(jumping)), + ((c, t) -> new NumericValue(sneaking)) + ), player::getCommandSource); + } + }, + PLAYER_USES_ITEM("player_uses_item",new CallbackList(3)) + { + public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) + { + handler.call( () -> + { + //ItemStack itemstack = player.getStackInHand(enumhand); + return Arrays.asList( + ((c, t) -> new EntityValue(player)), + ((c, t) -> ListValue.fromItemStack(itemstack)), + ((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")) + ); + }, player::getCommandSource); + } + }, + PLAYER_CLICKS_BLOCK("player_clicks_block",new CallbackList(3)) + { + public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) + { + handler.call( () -> + { + return Arrays.asList( + ((c, t) -> new EntityValue(player)), + ((c, t) -> ListValue.fromItemStack(itemstack)), + ((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")) + ); + }, player::getCommandSource); + } + }, + PLAYER_RIGHT_CLICKS_BLOCK("player_right_clicks_block",new CallbackList(6)) + { + @Override + public void onBlockHit(ServerPlayerEntity player, Hand enumhand, BlockHitResult hitRes)//ItemStack itemstack, Hand enumhand, BlockPos blockpos, Direction enumfacing, Vec3d vec3d) + { + handler.call( () -> + { + ItemStack itemstack = player.getStackInHand(enumhand); + BlockPos blockpos = hitRes.getBlockPos(); + Direction enumfacing = hitRes.getSide(); + Vec3d vec3d = hitRes.getPos().subtract(blockpos.getX(), blockpos.getY(), blockpos.getZ()); + return Arrays.asList( + ((c, t) -> new EntityValue(player)), + ((c, t) -> ListValue.fromItemStack(itemstack)), + ((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")), + ((c, t) -> new BlockValue(null, player.getServerWorld(), blockpos)), + ((c, t) -> new StringValue(enumfacing.getName())), + ((c, t) -> ListValue.of( + new NumericValue(vec3d.x), + new NumericValue(vec3d.y), + new NumericValue(vec3d.z) + )) + ); + }, player::getCommandSource); + } + }, + PLAYER_BREAK_BLOCK("player_breaks_block",new CallbackList(2)) + { + @Override + public void onBlockBroken(ServerPlayerEntity player, BlockPos pos, BlockState previousBS) + { + handler.call( () -> Arrays.asList( + ((c, t) -> new EntityValue(player)), + ((c, t) -> new BlockValue(previousBS, player.getServerWorld(), pos)) + ), player::getCommandSource); + } + }, + PLAYER_INERACTSW_WITH_ENTITY("player_interacts_with_entity",new CallbackList(3)) + { + @Override + public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand) + { + handler.call( () -> Arrays.asList( + ((c, t) -> new EntityValue(player)), + ((c, t) -> new EntityValue(entity)), + ((c, t) -> new StringValue(enumhand==Hand.MAIN_HAND?"mainhand":"offhand")) + ), player::getCommandSource); + } + }, + PLAYER_ATTACKS_ENTITY("player_attacks_entity",new CallbackList(2)) + { + @Override + public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand) + { + handler.call( () -> Arrays.asList( + ((c, t) -> new EntityValue(player)), + ((c, t) -> new EntityValue(entity)) + ), player::getCommandSource); + } + }, + PLAYER_STARTS_SNEAKING("player_starts_sneaking",new CallbackList(1)) + { + @Override + public void onPlayerEvent(ServerPlayerEntity player) + { + handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + }, + PLAYER_STOPS_SNEAKING("player_stops_sneaking",new CallbackList(1)) + { + @Override + public void onPlayerEvent(ServerPlayerEntity player) + { + handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + }, + PLAYER_STARTS_SPRINTING("player_starts_sprinting",new CallbackList(1)) + { + @Override + public void onPlayerEvent(ServerPlayerEntity player) + { + handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + }, + PLAYER_STOPS_SPRINTING("player_stops_sprinting",new CallbackList(1)) + { + @Override + public void onPlayerEvent(ServerPlayerEntity player) + { + handler.call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); + } + }, - public List scheduledCalls = new LinkedList<>(); + PLAYER_RELEASED_ITEM("player_released_item",new CallbackList(3)) + { + @Override + public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) + { + // this.getStackInHand(this.getActiveHand()), this.activeItemStack) + handler.call( () -> + { + return Arrays.asList( + ((c, t) -> new EntityValue(player)), + ((c, t) -> ListValue.fromItemStack(itemstack)), + ((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")) + ); + }, player::getCommandSource); + } + }, + PLAYER_FINISHED_USING_ITEM("player_finished_using_item",new CallbackList(3)) + { + public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) + { + // this.getStackInHand(this.getActiveHand()), this.activeItemStack) + handler.call( () -> + { + return Arrays.asList( + ((c, t) -> new EntityValue(player)), + ((c, t) -> ListValue.fromItemStack(itemstack)), + ((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")) + ); + }, player::getCommandSource); + } + }; + // on projectile thrown (arrow from bows, crossbows, tridents, snoballs, e-pearls + + + + + public String name; + public static Map byName = new HashMap(){{ + for (Event e: Event.values()) + { + put(e.name, e); + } + }}; + public CallbackList handler; + Event(String name, CallbackList eventHandler) + { + this.name = name; + this.handler = eventHandler; + } + public boolean isNeeded() + { + return handler.callList.size() > 0; + } + //stubs for calls just to ease calls in vanilla code so they don't need to deal with scarpet value types + public void onTick() { } + public void onPlayerEvent(ServerPlayerEntity player) { } + public void onMountControls(ServerPlayerEntity player, float strafeSpeed, float forwardSpeed, boolean jumping, boolean sneaking) { } + public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) { } + public void onBlockAction(ServerPlayerEntity player, BlockPos blockpos, Direction facing) { } + public void onBlockHit(ServerPlayerEntity player, Hand enumhand, BlockHitResult hitRes) { } + public void onBlockBroken(ServerPlayerEntity player, BlockPos pos, BlockState previousBS) { } + public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand) { } + } + + + public CarpetEventServer() + { + for (Event e: Event.values()) + e.handler.callList.clear(); + } public void tick() { @@ -175,50 +423,28 @@ public class CarpetEventServer scheduledCalls.add(new ScheduledCall(context, function, args, due)); } - - public CarpetEventServer() - { - eventHandlers.put("tick",new CallbackList(0)); - eventHandlers.put("tick_nether",new CallbackList(0)); - eventHandlers.put("tick_ender",new CallbackList(0)); - eventHandlers.put("player_jumps",new CallbackList(1)); - eventHandlers.put("player_deploys_elytra",new CallbackList(1)); - eventHandlers.put("player_wakes_up",new CallbackList(1)); - eventHandlers.put("player_rides",new CallbackList(5)); - eventHandlers.put("player_uses_item",new CallbackList(3)); - eventHandlers.put("player_clicks_block",new CallbackList(3)); - eventHandlers.put("player_right_clicks_block",new CallbackList(6)); - eventHandlers.put("player_breaks_block",new CallbackList(2)); - eventHandlers.put("player_interacts_with_entity",new CallbackList(3)); - eventHandlers.put("player_attacks_entity",new CallbackList(2)); - eventHandlers.put("player_starts_sneaking",new CallbackList(1)); - eventHandlers.put("player_stops_sneaking",new CallbackList(1)); - eventHandlers.put("player_starts_sprinting",new CallbackList(1)); - eventHandlers.put("player_stops_sprinting",new CallbackList(1)); - } - public boolean addEvent(String event, String host, String funName) { - if (!eventHandlers.containsKey(event)) + if (!Event.byName.containsKey(event)) { return false; } - return eventHandlers.get(event).addEventCall(host, funName); + return Event.byName.get(event).handler.addEventCall(host, funName); } public boolean removeEvent(String event, String funName) { - if (!eventHandlers.containsKey(event)) + if (!Event.byName.containsKey(event)) return false; Callback callback= decodeCallback(funName); - eventHandlers.get(event).removeEventCall(callback.host, callback.udf); + Event.byName.get(event).handler.removeEventCall(callback.host, callback.udf); return true; } public void removeAllHostEvents(String hostName) { - eventHandlers.forEach((e, a) -> a.removeAllCalls(hostName)); + Event.byName.values().forEach((e) -> e.handler.removeAllCalls(hostName)); } private Callback decodeCallback(String funName) @@ -250,131 +476,6 @@ public class CarpetEventServer return new ScheduledCall(cc, function, lazyArgs, 0); } - public void onTick() - { - eventHandlers.get("tick").call(Collections::emptyList, CarpetServer.minecraft_server::getCommandSource); - eventHandlers.get("tick_nether").call(Collections::emptyList, () -> - CarpetServer.minecraft_server.getCommandSource().withWorld(CarpetServer.minecraft_server.getWorld(DimensionType.THE_NETHER))); - eventHandlers.get("tick_ender").call(Collections::emptyList, () -> - CarpetServer.minecraft_server.getCommandSource().withWorld(CarpetServer.minecraft_server.getWorld(DimensionType.THE_END))); - } - - public void onPlayerJumped(PlayerEntity player) - { - eventHandlers.get("player_jumps").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); - } - - public void onElytraDeploy(ServerPlayerEntity player) - { - eventHandlers.get("player_deploys_elytra").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); - } - - public void onOutOfBed(ServerPlayerEntity player) - { - eventHandlers.get("player_wakes_up").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); - } - - public void onMountControls(PlayerEntity player, float strafeSpeed, float forwardSpeed, boolean jumping, boolean sneaking) - { - eventHandlers.get("player_rides").call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new NumericValue(forwardSpeed)), - ((c, t) -> new NumericValue(strafeSpeed)), - ((c, t) -> new NumericValue(jumping)), - ((c, t) -> new NumericValue(sneaking)) - ), player::getCommandSource); - } - - public void onRightClick(ServerPlayerEntity player, Hand enumhand) - { - eventHandlers.get("player_uses_item").call( () -> - { - ItemStack itemstack = player.getStackInHand(enumhand); - return Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> ListValue.fromItemStack(itemstack)), - ((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")) - ); - }, player::getCommandSource); - } - - public void onBlockClicked(ServerPlayerEntity player, BlockPos blockpos, Direction facing) - { - eventHandlers.get("player_clicks_block").call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new BlockValue(null, player.getServerWorld(), blockpos)), - ((c, t) -> new StringValue(facing.getName())) - ), player::getCommandSource); - } - - public void onRightClickBlock(ServerPlayerEntity player, Hand enumhand, BlockHitResult hitRes)//ItemStack itemstack, Hand enumhand, BlockPos blockpos, Direction enumfacing, Vec3d vec3d) - { - eventHandlers.get("player_right_clicks_block").call( () -> - { - ItemStack itemstack = player.getStackInHand(enumhand); - BlockPos blockpos = hitRes.getBlockPos(); - Direction enumfacing = hitRes.getSide(); - Vec3d vec3d = hitRes.getPos().subtract(blockpos.getX(), blockpos.getY(), blockpos.getZ()); - return Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> ListValue.fromItemStack(itemstack)), - ((c, t) -> new StringValue(enumhand == Hand.MAIN_HAND ? "mainhand" : "offhand")), - ((c, t) -> new BlockValue(null, player.getServerWorld(), blockpos)), - ((c, t) -> new StringValue(enumfacing.getName())), - ((c, t) -> ListValue.of( - new NumericValue(vec3d.x), - new NumericValue(vec3d.y), - new NumericValue(vec3d.z) - )) - ); - }, player::getCommandSource); - } - - public void onBlockBroken(ServerPlayerEntity player, BlockPos pos, BlockState previousBS) - { - eventHandlers.get("player_breaks_block").call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new BlockValue(previousBS, player.getServerWorld(), pos)) - ), player::getCommandSource); - } - - public void onEntityInteracted(ServerPlayerEntity player, Entity entity, Hand enumhand) - { - eventHandlers.get("player_interacts_with_entity").call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new EntityValue(entity)), - ((c, t) -> new StringValue(enumhand==Hand.MAIN_HAND?"mainhand":"offhand")) - ), player::getCommandSource); - } - - public void onEntityAttacked(ServerPlayerEntity player, Entity entity) - { - eventHandlers.get("player_attacks_entity").call( () -> Arrays.asList( - ((c, t) -> new EntityValue(player)), - ((c, t) -> new EntityValue(entity)) - ), player::getCommandSource); - } - - public void onStartSneaking(ServerPlayerEntity player) - { - eventHandlers.get("player_starts_sneaking").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); - } - - public void onStopSneaking(ServerPlayerEntity player) - { - eventHandlers.get("player_stops_sneaking").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); - } - - public void onStartSprinting(ServerPlayerEntity player) - { - eventHandlers.get("player_starts_sprinting").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); - } - - public void onStopSprinting(ServerPlayerEntity player) - { - eventHandlers.get("player_stops_sprinting").call( () -> Arrays.asList(((c, t) -> new EntityValue(player))), player::getCommandSource); - } - public ScheduledCall makeDeathCall(CarpetContext cc, String function, List extraArgs) { return makeEventCall(cc, function, extraArgs, 2); @@ -413,7 +514,4 @@ public class CarpetEventServer (c, t) -> source.getAttacker()==null?Value.NULL:new EntityValue(source.getAttacker()) )); } - - - } diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index d2b75ded..dacd3d24 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -347,7 +347,7 @@ public class CarpetExpression *

    Blocks / World API

    *
    *

    Specifying blocks

    - *

    block(x, y, z), block(state)

    + *

    block(x, y, z), block(l(x,y,z)), block(state)

    *

    Returns either a block from specified location, or block with a specific state * (as used by /setblock command), so allowing for block properties, block entity data etc. * Blocks otherwise can be referenced everywhere by its simple string name, but its only used in its default state

    @@ -1189,6 +1189,8 @@ public class CarpetExpression Value nbtValue = lv.get(inventoryLocator.offset+3).evalValue(c); if (nbtValue instanceof NBTSerializableValue) nbt = ((NBTSerializableValue)nbtValue).getCompoundTag(); + else if (nbtValue instanceof NullValue) + nbt = null; else nbt = new NBTSerializableValue(nbtValue.getString()).getCompoundTag(); } @@ -1251,7 +1253,7 @@ public class CarpetExpression if (inventoryLocator == null) return (_c, _t) -> Value.NULL; if (lv.size() <= inventoryLocator.offset) - throw new InternalExpressionException("inventory_set requires at least an item to be removed"); + throw new InternalExpressionException("inventory_remove requires at least an item to be removed"); ItemStackArgument searchItem = NBTSerializableValue.parseItem(lv.get(inventoryLocator.offset).evalValue(c).getString()); int amount = 1; if (lv.size() > inventoryLocator.offset+1) @@ -1540,10 +1542,9 @@ public class CarpetExpression * that corresponds to the path. For specification of path attribute, consult * vanilla /data get entity command.

    *

    Note that calls to nbt are considerably more expensive comparing to other - * calls in Minecraft API, and should only be used when there is no other option. Also returned - * NBT is just a string to any retrieval of information post can currently only be done with matching - * operator ~. With time we are hoping not to support the 'nbt' call better, - * but rather to fill the API, so that 'nbt' calls are not needed

    + * calls in Minecraft API, and should only be used when there is no other option.A returned value is of type nbt, + * which can be further manipulated with nbt type objects via get, put, has, delete, so try to use API calls + * first

    *

    Entity Modification

    *

    Like with entity querying, entity modifications happen through one function. Most position and movements * modifications don't work currently on players as their position is controlled by clients.

    @@ -1724,8 +1725,12 @@ public class CarpetExpression boolean hasTag = false; if (lv.size() > position.offset) { - hasTag = true; - tag = new NBTSerializableValue(lv.get(position.offset).evalValue(c).getString()).getCompoundTag(); + NBTSerializableValue v = NBTSerializableValue.parseString(lv.get(position.offset).evalValue(c).getString()); + if (v != null) + { + hasTag = true; + tag = v.getCompoundTag(); + } } tag.putString("id", entityId.toString()); Vec3d vec3d = position.vec; @@ -2355,8 +2360,7 @@ public class CarpetExpression *

    System function

    *

    nbt(expr)

    *

    Treats the argument as a nbt serializable string and returns its nbt value. - * In case nbt is not in a correct nbt compound tag format, the execution of a script will stop with an error, - * whenever that tag is used.

    + * In case nbt is not in a correct nbt compound tag format, it will return null value.

    *

    print(expr)

    *

    Displays the result of the expression to the chat. Overrides default scarpet behaviour of * sending everyting to stderr.

    @@ -2376,11 +2380,11 @@ public class CarpetExpression * loop(1000,tick(100)) // runs the game twice as slow for 1000 ticks * *

    current_dimension()

    - *

    Returns current dimension that scripts run in.

    + *

    Returns current dimension that the script runs in.

    *

    in_dimension(smth, expr)

    *

    Evaluates the expression expr with different dimension execution context. smth can * be an entity, world-localized block, so not block('stone'), or a string representing a dimension like: - * 'nether', 'end' or 'overworld'.

    + * 'nether', 'the_nether', 'end' or 'overworld', etc.

    *

    schedule(delay, function, args...)

    *

    Schedules a user defined function to run with a specified delay ticks of delay. * Scheduled functions run at the end of the tick, and they will run in order they were scheduled.

    @@ -2677,7 +2681,9 @@ public class CarpetExpression Value v = lv.get(0).evalValue(c); if (v instanceof NBTSerializableValue) return (cc, tt) -> v; - Value ret = new NBTSerializableValue(v.getString()); + Value ret = NBTSerializableValue.parseString(v.getString()); + if (ret == null) + return LazyValue.NULL; return (cc, tt) -> ret; }); diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java index f9e5c602..d4a29872 100644 --- a/src/main/java/carpet/script/CarpetScriptServer.java +++ b/src/main/java/carpet/script/CarpetScriptServer.java @@ -200,7 +200,7 @@ public class CarpetScriptServer if (!fun.startsWith("__on_")) continue; String event = fun.replaceFirst("__on_",""); - if (!events.eventHandlers.containsKey(event)) + if (!CarpetEventServer.Event.byName.containsKey(event)) continue; events.addEvent(event, hostName, fun); } diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index 79b250d9..5618c869 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -27,6 +27,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; public class NBTSerializableValue extends Value implements ContainerValueInterface @@ -55,6 +57,22 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa }; } + public static NBTSerializableValue parseString(String nbtString) + { + Tag tag; + try + { + tag = (new StringNbtReader(new StringReader(nbtString))).parseTag(); + } + catch (CommandSyntaxException e) + { + return null; + } + NBTSerializableValue value = new NBTSerializableValue(tag); + value.nbtString = nbtString; + return value; + } + public NBTSerializableValue(Tag tag) { nbtTag = tag; @@ -261,10 +279,15 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa } + private Tag modify_insert(int index, NbtPathArgumentType.NbtPath nbtPath, Tag newElement) + { + return modify_insert(index, nbtPath, newElement, this.getTag().copy()); + } + + private Tag modify_insert(int index, NbtPathArgumentType.NbtPath nbtPath, Tag newElement, Tag currentTag) { Collection targets; - Tag currentTag = getTag().copy(); try { targets = nbtPath.putIfAbsent(currentTag, ListTag::new); @@ -284,7 +307,8 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa try { AbstractListTag targetList = (AbstractListTag) target; - targetList.addTag(index < 0 ? targetList.size() + index + 1 : index, newElement.copy()); + if (!targetList.addTag(index < 0 ? targetList.size() + index + 1 : index, newElement.copy())) + return null; modified = true; } catch (IndexOutOfBoundsException ignored) @@ -320,6 +344,35 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa private Tag modify_replace(NbtPathArgumentType.NbtPath nbtPath, Tag replacement) //nbtPathArgumentType$NbtPath_1, list_1) { Tag originalTag = getTag().copy(); + String pathText = nbtPath.toString(); + if (pathText.endsWith("]")) // workaround for array replacement or item in the array replacement + { + if (nbtPath.remove(originalTag)==0) + return null; + Pattern pattern = Pattern.compile("\\[[^\\[]*]$"); + Matcher matcher = pattern.matcher(pathText); + if (!matcher.find()) // malformed path + { + return null; + } + String arrAccess = matcher.group(); + int pos; + if (arrAccess.length()==2) // we just removed entire array + pos = 0; + else + { + try + { + pos = Integer.parseInt(arrAccess.substring(1, arrAccess.length() - 1)); + } + catch (NumberFormatException e) + { + return null; + } + } + NbtPathArgumentType.NbtPath newPath = cachePath(pathText.substring(0, pathText.length()-arrAccess.length())); + return modify_insert(pos,newPath,replacement, originalTag); + } try { nbtPath.put(originalTag, () -> replacement); diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index bbda0dea..2781ed19 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -123,6 +123,7 @@ "Entity_scarpetEventsMixin", "LivingEntity_scarpetEventsMixin", "DamageTracker_scarpetEventMixin", + "ServerPlayerEntity_scarpetEventMixin", "PistonBlock_movableTEMixin", From 768c44cc1481c14a8bb45fabca2be252784d672c Mon Sep 17 00:00:00 2001 From: gnembon Date: Mon, 7 Oct 2019 09:30:25 -0300 Subject: [PATCH 10/13] equality issues and docs --- .../java/carpet/script/CarpetEventServer.java | 4 +- .../java/carpet/script/CarpetExpression.java | 28 +- src/main/java/carpet/script/Expression.java | 820 +++++++++--------- .../script/value/ContainerValueInterface.java | 2 +- .../java/carpet/script/value/EntityValue.java | 4 +- .../carpet/script/value/LazyListValue.java | 2 +- .../java/carpet/script/value/ListValue.java | 4 +- .../java/carpet/script/value/MapValue.java | 4 +- .../script/value/NBTSerializableValue.java | 14 +- .../java/carpet/script/value/NullValue.java | 2 +- .../carpet/script/value/NumericValue.java | 4 +- src/main/java/carpet/script/value/Value.java | 10 + 12 files changed, 469 insertions(+), 429 deletions(-) diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java index c68e9960..b1f0016c 100644 --- a/src/main/java/carpet/script/CarpetEventServer.java +++ b/src/main/java/carpet/script/CarpetEventServer.java @@ -327,7 +327,7 @@ public class CarpetEventServer } }, - PLAYER_RELEASED_ITEM("player_released_item",new CallbackList(3)) + PLAYER_RELEASED_ITEM("player_releases_item",new CallbackList(3)) { @Override public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) @@ -343,7 +343,7 @@ public class CarpetEventServer }, player::getCommandSource); } }, - PLAYER_FINISHED_USING_ITEM("player_finished_using_item",new CallbackList(3)) + PLAYER_FINISHED_USING_ITEM("player_finishes_using_item",new CallbackList(3)) { public void onItemAction(ServerPlayerEntity player, Hand enumhand, ItemStack itemstack) { diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index dacd3d24..db6a5a70 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -1542,9 +1542,9 @@ public class CarpetExpression * that corresponds to the path. For specification of path attribute, consult * vanilla /data get entity command.

    *

    Note that calls to nbt are considerably more expensive comparing to other - * calls in Minecraft API, and should only be used when there is no other option.A returned value is of type nbt, + * calls in Minecraft API, and should only be used when there is no other option. Returned value is of type nbt, * which can be further manipulated with nbt type objects via get, put, has, delete, so try to use API calls - * first

    + * first for that.

    *

    Entity Modification

    *

    Like with entity querying, entity modifications happen through one function. Most position and movements * modifications don't work currently on players as their position is controlled by clients.

    @@ -3080,22 +3080,25 @@ public class CarpetExpression *

    *

    Event list

    *

    Here is a list of events that can be handled by scarpet. This list includes prefixes required by modules to - * autoload them, but you can add any function to any event if it had required parameters:

    + * autoload them, but you can add any function to any event if it accepts required number of parameters:

    *
          * __on_tick()         // can access blocks and entities in the overworld
          * __on_tick_nether()  // can access blocks and entities in the nether
          * __on_tick_ender()   // can access blocks and entities in the end
    +     *
          * // player specific callbacks
    +     * __on_player_uses_item(player, item_tuple, hand)  // right click action
    +     * __on_player_releases_item(player, item_tuple, hand)  // client action (e.g. bow)
    +     * __on_player_finishes_using_item(player, item_tuple, hand))  // server action (e.g. food), called item is from before it is used.
    +     * __on_player_clicks_block(player, block, face)  // left click attack on a block
    +     * __on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec)
    +     * __on_player_breaks_block(player, block) // called after block is broken (the caller receives previous blockstate)
    +     * __on_player_interacts_with_entity(player, entity, hand)
    +     * __on_player_attacks_entity(player, entity)
    +     * __on_player_rides(player, forward, strafe, jumping, sneaking)
          * __on_player_jumps(player)
          * __on_player_deploys_elytra(player)
          * __on_player_wakes_up(player)
    -     * __on_player_rides(player, forward, strafe, jumping, sneaking)
    -     * __on_player_uses_item(player, item_tuple, hand)
    -     * __on_player_clicks_block(player, block, face)
    -     * __on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec)
    -     * __on_player_breaks_block(player, block)
    -     * __on_player_interacts_with_entity(player, entity, hand)
    -     * __on_player_attacks_entity(player, entity)
          * __on_player_starts_sneaking(player)
          * __on_player_stops_sneaking(player)
          * __on_player_starts_sprinting(player)
    @@ -3103,7 +3106,10 @@ public class CarpetExpression
          * 
    *

    /script event command

    *

    used to display current events and bounded functions. use add_to ro register new event, or remove_from - * to unbind a specific function from an event.

    + * to unbind a specific function from an event. Function to be bounded to an event needs to have the same + * number of parameters as the action is attempting to bind to (see list above). All calls in modules + * loaded via /script load that have functions listed above will be automatically bounded and unbounded when + * script is unloaded.

    *
    */ public void gameEventsSystem() diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index e1d3ce48..44fb59a3 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -366,29 +366,6 @@ public class Expression implements Cloneable private Map functions = new HashMap<>(); Set getFunctionNames() {return functions.keySet();} - //static final Map globalFunctions = new HashMap<>(); - - //static final Map globalVariables = new HashMap<>(); - - /*static - { - setGlobals(); - } - static void setGlobals() - { - globalVariables.put("euler", (c, t) -> euler); - globalVariables.put("pi", (c, t) -> PI); - globalVariables.put("null", (c, t) -> Value.NULL); - globalVariables.put("true", (c, t) -> Value.TRUE); - globalVariables.put("false", (c, t) -> Value.FALSE); - - //special variables for second order functions so we don't need to check them all the time - globalVariables.put("_", (c, t) -> Value.ZERO); - globalVariables.put("_i", (c, t) -> Value.ZERO); - globalVariables.put("_a", (c, t) -> Value.ZERO); - } - */ - @Override protected Expression clone() throws CloneNotSupportedException { @@ -563,6 +540,7 @@ public class Expression implements Cloneable return new ExpressionException(e, token, "Your math is wrong, "+exc.getMessage()); if (exc instanceof ExpressionException) return exc; + // unexpected really - should be caught earlier and converted to InternalExpressionException exc.printStackTrace(); return new ExpressionException(e, token, "Error while evaluating expression: "+exc); } @@ -1062,24 +1040,76 @@ public class Expression implements Cloneable * has to deal with

    *

    Operator Precedence

    *

    - * Here is the complete list of operators in scarpet including control flow operators. - * note, that commas and brackets are not operators, but behave like them: - *

    - *
      - *
    • Match, Get~ .
    • - *
    • Unary + - !
    • - *
    • Exponent ^
    • - *
    • Multiplication * / %
    • - *
    • Addition + -
    • - *
    • Comparison == != > >= <= <
    • - *
    • Logical And&&
    • - *
    • Logical Or ||
    • - *
    • Assignment = += <>
    • - *
    • Definition ->
    • - *
    • Next statement;
    • - *
    • Comma ,
    • - *
    • Bracket ( )
    • - *
    + * Here is the complete list of operators in scarpet including control flow operators. + * Note, that commas and brackets are not technically operators, but part of the language, + * even if they look like them: + *

    + *
      + *
    • Match, Get~ .
    • + *
    • Unary + - !
    • + *
    • Exponent ^
    • + *
    • Multiplication * / %
    • + *
    • Addition + -
    • + *
    • Comparison == != > >= <= <
    • + *
    • Logical And&&
    • + *
    • Logical Or ||
    • + *
    • Assignment = += <>
    • + *
    • Definition ->
    • + *
    • Next statement;
    • + *
    • Comma ,
    • + *
    • Bracket ( )
    • + *
    + * + *

    Get, Accessor Operator .

    + *

    Operator version of the get(...) function to access elements of lists, maps, and potentially other + * containers (i.e. NBTs). It is important to distinguish from ~ operator, which is a matching operator, + * which is expected to perform some extra computations to retrieve the result, while . should be + * straightforward and immediate, and the source object should behave like a container and support full container + * API, meaning get(...), put(...), delete(...), + * and has(...) functions

    + * + *

    Matching Operator ~

    + *

    This operator should be understood as 'matches', 'contains', 'is_in', + * or 'find me some stuff about something else. For strings it matches the right operand as a regular + * expression to the left, returning the first match. This can be used to extract information from unparsed nbt's + * in a more convoluted way (use get(...) for more appropriate way of doing it). + * For lists it checks if an element is in the list, and returns the index of that element, + * or null if no such element was found, especially that the use of first(...) function will not + * return the index. Currently it doesn't have any special behaviour for numbers - it checks for existence of characters + * in string representation of the left operand with respect of the regular expression on the right hand side. + * string

    + *

    in Minecraft API portion entity ~ feature is a shortcode for query(entity,feature) + * for queries that do not take any extra arguments.

    + *
    +     * l(1,2,3) ~ 2  => 1
    +     * l(1,2,3) ~ 4  => null
    +     * 'foobar' ~ '.b'  => 'ob'
    +     * player('*') ~ 'gnembon'  // null unless player gnembon is logged in (better to use player('gnembon') instead
    +     * p ~ 'sneaking' // if p is an entity returns whether p is sneaking
    +     * 
    + *

    Or a longer example of an ineffective way to searching for a squid

    + *
    +     * entities = entities_area('all',x,y,z,100,10,100);
    +     * sid = entities ~ 'Squid';
    +     * if(sid != null, run('execute as '+query(get(entities,sid),'id')+' run say I am here '+query(get(entities,sid),'pos') ) )
    +     * 
    + *

    Or an example to find if a player has specific enchantment on a held axe (either hand) and get its level + * (not using proper NBTs query support via get(...)):

    + *
    +     * global_get_enchantment(p, ench) -> (
    +     * $   for(l('main','offhand'),
    +     * $      holds = query(p, 'holds', _);
    +     * $      if( holds,
    +     * $         l(what, count, nbt) = holds;
    +     * $         if( what ~ '_axe' && nbt ~ ench,
    +     * $            lvl = max(lvl, number(nbt ~ '(?<=lvl:)\\d') )
    +     * $         )
    +     * $      )
    +     * $   );
    +     * $   lvl
    +     * $);
    +     * /script run global_get_enchantment(players(), 'sharpness')
    +     * 
    * *

    Basic Arithmetic Operators + - * /

    *

    Allows to add the results of two expressions. If the operands resolve to numbers, the result is @@ -1145,52 +1175,7 @@ public class Expression implements Cloneable * null != false && run('kill gnembon') => 1 // gnembon dies, cheats allowed * * - *

    Get, Accessor Operator .

    - *

    Operator version of the get(...) function to access elements of lists, nbts, and potentially other - * containers. It is important to distinguish from ~ operator, which is a matching operator, meaning it - * is expected to perform some extra computations to retrieve the result, while . should be - * straightforward and immediate.

    - * - *

    Matching Operator ~

    - *

    This operator should be understood as 'matches', 'contains' or 'is_in'. For strings it matches the right operand as a regular - * expression to the left one, returning the first match. This can be used to extract information from unparsed nbt's - * in a more efficient way. For lists it checks if an element is in the list, and returns the index of that element, - * or null if no such element was found, especially that the use of first function will not - * return the index. Currently it doesn't have any special behaviour for numbers - it checks for existence of characters - * in string representation of the left operand with respect of the regular expression on the right hand side. - * string

    - *

    in Minecraft API portion entity ~ feature is a shortcode for query(entity,feature) - * for queries that do not take any extra arguments.

    - *
    -     * l(1,2,3) ~ 2  => 1
    -     * l(1,2,3) ~ 4  => null
    -     * 'foobar' ~ '.b'  => 'ob'
    -     * player('*') ~ 'gnembon'  // null unless player gnembon is logged in (better to use player('gnembon') instead
    -     * p ~ 'sneaking' // if p is an entity returns whether p is sneaking
    -     * 
    - *

    Or a longer example of an ineffective way to searching for a squid

    - *
    -     * entities = entities_area('all',x,y,z,100,10,100);
    -     * sid = entities ~ 'Squid';
    -     * if(sid != null, run('execute as '+query(get(entities,sid),'id')+' run say I am here '+query(get(entities,sid),'pos') ) )
    -     * 
    - *

    Or an example to find if a player has specific enchantment on a held axe (either hand) and get its level - * (despite obvious lack of support for json NBT's):

    - *
    -     * global_get_enchantment(p, ench) -> (
    -     * $   for(l('main','offhand'),
    -     * $      holds = query(p, 'holds', _);
    -     * $      if( holds,
    -     * $         l(what, count, nbt) = holds;
    -     * $         if( what ~ '_axe' && nbt ~ ench,
    -     * $            lvl = max(lvl, number(nbt ~ '(?<=lvl:)\\d') )
    -     * $         )
    -     * $      )
    -     * $   );
    -     * $   lvl
    -     * $);
    -     * /script run global_get_enchantment(players(), 'sharpness')
    -     * 
    + *

    Assignment Operators = <> +=

    *

    A set of assignment operators. All require bounded variable on the LHS, <> requires * bounded arguments on the right hand side as well (bounded, meaning being variables). Additionally they can also @@ -1569,118 +1554,22 @@ public class Expression implements Cloneable } /** - *

    Lists, loops, and higher order functions

    + *

    Loops, and higher order functions

    *
    - *

    Efficient use of these functions cangreatly simplify your programs and speed them up, as these functions - * will internalize most of the operations that need to be applied on multiple values at the same time

    - *

    Basic list operations

    - *

    l(values ...), l(iterator)

    - *

    Creates a list of values of the expressions passed as parameters. It can be used as an L-value and if all - * elements are variables, you coujld use it to return multiple results from one function call, if that function returns - * a list of results with the same size as the l call uses. In case there is only one argument and it is - * an iterator (vanilla expression specification has range, but Minecraft API implements - * a bunch of them, like diamond), it will convert it to a proper list. Iterators can only be used in - * high order functions, and are treated as empty lists, unless unrolled with l

    + *

    Efficient use of these functions can greatly simplify your programs and speed them up, as these functions + * will internalize most of the operations that need to be applied on multiple values at the same time

    + *

    Loops

    + * + *

    for(list,expr(_,_i),exit(_,_i)?)

    + *

    Evaluates expression over list of items from the list, and can optionally stop early if + * exit expression is specified and evaluates true for a given iteration. Supplies + * _(value) and _i(iteration number) to both expr and exit. + * Returns the number of times expr was successful

    *
    -     * l(1,2,'foo') => [1, 2, foo]
    -     * l() => [] (empty list)
    -     * l(range(10)) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    -     * l(1, 2) = l(3, 4) => Error: l is not a variable
    -     * l(foo, bar) = l(3,4); foo==3 && bar==4 => 1
    -     * l(foo, bar, baz) = l(2, 4, 6); l(min(foo, bar), baz) = l(3, 5); l(foo, bar, baz)  => [3, 4, 5]
    -     * 
    - *

    In the last example l(min(foo, bar), baz) creates a valid L-value, as min(foo, bar) finds the - * lower of the variables (in this case foo) creating a valid assignable L-list of [foo, baz], and these - * values will be assigned new values

    - * - *

    join(delim, list), join(delim, values ...)

    - *

    Returns a string that contains joined elements of the list, iterator, or all values, concatenated with delim delimiter

    - *
    -     *     join('-',range(10))  => 0-1-2-3-4-5-6-7-8-9
    -     *     join('-','foo')  => foo
    -     *     join('-', 'foo', 'bar')  => foo-bar
    -     * 
    - * - *

    split(delim, expr)

    - *

    Splits a string undr expr by delim which can be a regular expression

    - *
    -     *     split('',foo)  => [f, o, o]
    -     *     split('.','foo.bar')  => []
    -     *     split('\\.','foo.bar')  => [foo, bar]
    -     * 
    - * - *

    slice(expr, from, to?)

    - *

    extracts a substring, or sublist (based on the type of the result of the expression under expr with starting index - * of from, and ending at to if provided, or the end, if omitted

    - *
    -     *     slice(l(0,1,2,3,4,5), 1, 3)  => [1, 2, 3] 
    -     *     slice('foobar', 0, 1)  => 'f'
    -     *     slice('foobar', 3)  => 'bar'
    -     *     slice(range(10), 3, 5)  => [3, 4, 5]
    -     *     slice(range(10), 5)  => [5, 6, 7, 8, 9]
    -     * 
    - * - * - * - *

    sort(list), sort(values ...)

    - *

    Sorts in the default sortographical order either all arguments, or a list if its the only argument. It returns a new - * sorted list, not affecting the list passed to the argument

    - *
    -     * sort(3,2,1)  => [1, 2, 3]
    -     * sort('a',3,11,1)  => [1, 3, 11, 'a']
    -     * list = l(4,3,2,1); sort(list)  => [1, 2, 3, 4]
    -     * 
    - * - *

    sort_key(list, key_expr)

    - *

    Sorts a copy of the list in the order or keys as defined by the key_expr for each element

    - *
    -     *     sort_key([1,3,2],_)  => [1, 2, 3]
    -     *     sort_key([1,3,2],-_)  => [3, 2, 1]
    -     *     sort_key(l(range(10)),rand(1))  => [1, 0, 9, 6, 8, 2, 4, 5, 7, 3]
    -     *     sort_key(l(range(20)),str(_))  => [0, 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 3, 4, 5, 6, 7, 8, 9]
    -     * 
    - * - *

    range(to), range(from, to), range(from, to, step)

    - *

    Creates a range of numbers from from, no greater/larger than to. - * The step parameter dictates not only the increment size, but also direction (can be negative). - * The returned value is not a proper list, just the iterator - * but if for whatever reason you need a proper list with all items evaluated, use l(range(to)). - * Primarily to be used in higher order functions

    - *
    -     *     range(10)  => [...]
    -     *     l(range(10))  => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    -     *     map(range(10),_*_)  => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    -     *     reduce(range(10),_a+_, 0)  => 45
    -     *     range(5,10)  => [5, 6, 7, 8, 9]
    -     *     range(20, 10, -2)  => [20, 18, 16, 14, 12]
    -     * 
    - * - *

    get(value, address), element(value, address)(deprecated)

    - *

    Returns the value at address element from the value. - * For lists it indicates an index, use negative numbers to reach elements from the end of the list. get - * call will always be able to find the index. In case there is few items, it will loop over

    - *

    [Minecraft specific usecase]: In case value is of nbt type, uses addess as the nbt path to query, - * returning null, if path is not found, one value if there was one match, or list of values if result is a list. - * Returned elements can be of numerical type, string texts, or another compound nbt tags

    - *
    -     *     get(l(range(10)), 5)  => 5
    -     *     get(l(range(10)), -1)  => 9
    -     *     get(l(range(10)), 10)  => 0
    -     *     get(l(range(10)), 93)  => 3
    -     *     get(player() ~ 'nbt', 'Health') => 20 // inefficient way to get player health
    -     * 
    - * - *

    put(list, index, values ...), put(list, null, values ...)

    - *

    Modifies the list by replacing values starting from index with values. - * use negative numbers to reach elements from the end of the list. put - * call will always be able to find the index. In case there is few items, it will loop over. In case end - * of the list is reached before values run out, list is extended to accomodate for more values. in case you - * want to append at the end of the list, use null as index. returns number of elements inserted.

    - *
    -     *     a = l(1, 2, 3); put(a, 1, 4); a  => [1, 4, 3]
    -     *     a = l(1, 2, 3); put(a, null, 4, 5, 6); a  => [1, 2, 3, 4, 5, 6]
    -     *     a = l(l(0,0,0),l(0,0,0),l(0,0,0)); put(element(a, 1), 1, 1); a => [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
    +     *     check_prime(n) -> !first( range(2, sqrt(n)+1), !(n % _) );
    +     *        for(range(1000000,1100000),check_prime(_))  => 7216
          * 
    + *

    From which we can learn that there is 7216 primes between 1M and 1.1M

    *

    while(cond, limit, expr)

    *

    Evaluates expression expr repeatedly until condition cond becomes false, * but not more than limit times. Returns the result of the last expr evaluation, @@ -1710,6 +1599,7 @@ public class Expression implements Cloneable * * // outputs: [0, 1, 2, 3, 5, 7, 11, 13, 17, 19] * + *

    Higher Order Functions

    *

    map(list,expr(_,_i), exit(_,_i)?)

    *

    Converts a list of values, to another list where each value is result of an expression * v = expr(_, _i) where _ is passed as each element of the list, and _i is @@ -1754,17 +1644,6 @@ public class Expression implements Cloneable * // ditto, but requiring at most 7 bedrock block in the 18 blocks below them * * - * - *

    for(list,expr(_,_i),exit(_,_i)?)

    - *

    Evaluates expression over list of items from the list, and can optionally stop early if - * exit expression is specified and evaluates true for a given iteration. Supplies - * _(value) and _i(iteration number) to both expr and exit. - * Returns the number of times expr was successful

    - *
    -     *     check_prime(n) -> !first( range(2, sqrt(n)+1), !(n % _) );
    -     *        for(range(1000000,1100000),check_prime(_))  => 7216
    -     * 
    - *

    From which we can learn that there is 7216 primes between 1M and 1.1M

    *

    reduce(list,expr(_a,_,_i), initial)

    *

    Applies expr for each element of the list and saves the result in _a accumulator. * Consecutive calls to expr can access that value to apply more values. You also need to specify @@ -1775,196 +1654,9 @@ public class Expression implements Cloneable * *

    */ - public void ListsLoopsAndHigherOrderFunctions() + public void LoopsAndHigherOrderFunctions() { - addFunction("l", lv -> - { - if (lv.size() == 1 && lv.get(0) instanceof LazyListValue) - return ListValue.wrap(((LazyListValue) lv.get(0)).unroll()); - return new ListValue.ListConstructorValue(lv); - }); - addFunction("join", (lv) -> - { - if (lv.size() < 2) - throw new InternalExpressionException("join takes at least 2 arguments"); - String delimiter = lv.get(0).getString(); - List toJoin; - if (lv.size()==2 && lv.get(1) instanceof LazyListValue) - { - toJoin = ((LazyListValue) lv.get(1)).unroll(); - - } - else if (lv.size() == 2 && lv.get(1) instanceof ListValue) - { - toJoin = new ArrayList<>(((ListValue)lv.get(1)).getItems()); - } - else - { - toJoin = lv.subList(1,lv.size()); - } - return new StringValue(toJoin.stream().map(Value::getString).collect(Collectors.joining(delimiter))); - }); - - addBinaryFunction("split", (d, v) -> { - String delimiter = d.getString(); - String hwat = v.getString(); - return ListValue.wrap(Arrays.stream(hwat.split(delimiter)).map(StringValue::new).collect(Collectors.toList())); - }); - - addFunction("slice", (lv) -> { - - if (lv.size() != 2 && lv.size() != 3) - throw new InternalExpressionException("slice takes 2 or 3 arguments"); - Value hwat = lv.get(0); - long from = NumericValue.asNumber(lv.get(1)).getLong(); - long to = -1; - if (lv.size()== 3) - to = NumericValue.asNumber(lv.get(2)).getLong(); - return hwat.slice(from, to); - }); - - addFunction("sort", (lv) -> - { - List toSort = lv; - if (lv.size()==1 && lv.get(0) instanceof ListValue) - { - toSort = new ArrayList<>(((ListValue)lv.get(0)).getItems()); - } - Collections.sort(toSort); - return ListValue.wrap(toSort); - }); - - addLazyFunction("sort_key", 2, (c, t, lv) -> //get working with iterators - { - Value v = lv.get(0).evalValue(c); - if (!(v instanceof ListValue)) - throw new InternalExpressionException("First argument for sort_key should be a List"); - LazyValue sortKey = lv.get(1); - //scoping - LazyValue __ = c.getVariable("_"); - - List toSort = new ArrayList<>(((ListValue) v).getItems()); - - Collections.sort(toSort,(v1, v2) -> { - c.setVariable("_",(cc, tt) -> v1); - Value ev1 = sortKey.evalValue(c); - c.setVariable("_",(cc, tt) -> v2); - Value ev2 = sortKey.evalValue(c); - return ev1.compareTo(ev2); - }); - //revering scope - c.setVariable("_", __); - return (cc, tt) -> ListValue.wrap(toSort); - }); - - addFunction("range", (lv) -> - { - long from = 0; - long to = 0; - long step = 1; - int argsize = lv.size(); - if (argsize == 0 || argsize > 3) - throw new InternalExpressionException("range accepts from 1 to 3 arguments, not "+argsize); - to = NumericValue.asNumber(lv.get(0)).getLong(); - if (lv.size() > 1) - { - from = to; - to = NumericValue.asNumber(lv.get(1)).getLong(); - if (lv.size() > 2) - { - step = NumericValue.asNumber(lv.get(2)).getLong(); - } - } - return LazyListValue.range(from, to, step); - }); - - addFunction("m", lv -> - { - if (lv.size() == 1 && lv.get(0) instanceof LazyListValue) - return new MapValue(((LazyListValue) lv.get(0)).unroll()); - return new MapValue(lv); - }); - - addUnaryFunction("keys", v -> { - if (v instanceof MapValue) - return new ListValue(((MapValue) v).getMap().keySet()); - throw new InternalExpressionException("keys applies only to maps"); - }); - - addUnaryFunction("values", v -> { - if (v instanceof MapValue) - return new ListValue(((MapValue) v).getMap().values()); - throw new InternalExpressionException("keys applies only to maps"); - }); - - addUnaryFunction("pairs", v -> { - if (v instanceof MapValue) - return ListValue.wrap(((MapValue) v).getMap().entrySet().stream().map( - (p) -> ListValue.of(p.getKey(), p.getValue()) - ).collect(Collectors.toList())); - throw new InternalExpressionException("pairs applies only to maps"); - }); - - addBinaryFunction("get", (container, key) -> - { - if (container instanceof ContainerValueInterface) - return ((ContainerValueInterface) container).get(key); - throw new InternalExpressionException("Cannot access elements of a non-container"); - }); - - addBinaryFunction("has", (container, key) -> - { - if (container instanceof ContainerValueInterface) - return new NumericValue(((ContainerValueInterface) container).has(key)); - throw new InternalExpressionException("Cannot access elements of a non-container"); - }); - - //Deprecated, use "get" instead, or . operator - addBinaryFunction("element", (v1, v2) -> - { - if (!(v1 instanceof ListValue)) - throw new InternalExpressionException("First argument of get should be a list"); - List items = ((ListValue)v1).getItems(); - long index = NumericValue.asNumber(v2).getLong(); - int numitems = items.size(); - long range = abs(index)/numitems; - index += (range+2)*numitems; - index = index % numitems; - return items.get((int)index); - }); - - addFunction("put", (lv) -> - { - if(lv.size()<3) - { - throw new InternalExpressionException("put takes at least three arguments, a container, address, and values to insert at that index"); - } - Value container = lv.get(0); - if (!(container instanceof ContainerValueInterface)) - { - throw new InternalExpressionException("First argument of put should be a container: list, map or nbt"); - } - Value where = lv.get(1); - Value what = lv.get(2); - if (lv.size()>3) - { - return ((ContainerValueInterface) container).put(where, what, lv.get(3)); - } - else - { - return ((ContainerValueInterface) container).put(where, what); - } - }); - - addBinaryFunction("delete", (container, address) -> - { - if (container instanceof ContainerValueInterface) - { - return ((ContainerValueInterface) container).remove(address); - } - throw new InternalExpressionException("First argument of remove should be a container: list, map or nbt"); - }); //condition and expression will get a bound 'i' @@ -2282,6 +1974,341 @@ public class Expression implements Cloneable }); } + /** + *

    Lists, Maps and API support for Containers

    + *
    + *

    Scarpet supports basic container types: lists and maps (aka hashmaps, dicts etc..)

    + *

    Container manipulation

    + *

    Here is a list of operations that work on all types of containers: lists, maps, as well as other Minecraft + * specific modifyable containers, like NBTs

    + * + *

    get(container, address), '.' operator

    + *

    Returns the value at address element from the value. + * For lists it indicates an index, use negative numbers to reach elements from the end of the list. get + * call will always be able to find the index. In case there is few items, it will loop over

    + *

    for maps, retrieves the value under the key specified in the address or null otherwise

    + *

    [Minecraft specific usecase]: In case value is of nbt type, uses address as the + * nbt path to query, + * returning null, if path is not found, one value if there was one match, or list of values if result is a list. + * Returned elements can be of numerical type, string texts, or another compound nbt tags

    + *
    +     *     get(l(range(10)), 5)  => 5
    +     *     get(l(range(10)), -1)  => 9
    +     *     get(l(range(10)), 10)  => 0
    +     *     l(range(10)).93  => 3
    +     *
    +     *     get(player() ~ 'nbt', 'Health') => 20 // inefficient way to get player health, use player() ~ 'health' instead
    +     *
    +     *     get(m( l('foo',2), l('bar',3), l('baz',4) ), 'bar')  => 3
    +     * 
    + * + *

    has(container, address)

    + *

    Similar to get, but returns boolean value indicating if the given index / key / path is in the + * container. Can be used to determine if get(...)==null means the element doesn't exist, or the stored value + * for this address is null.

    + * + *

    delete(container, address)

    + *

    Removes specific entry from the container. For the lists - removes the element and shrinks it. For maps, it + * removes the key from the map, and for nbt - removes content from a given path. For lists and maps returns previous + * entry at the address, for nbt's - number of removed objects.

    + * + * + *

    put(list, index, values ...), put(list, null, values ...)

    + *

    Modifies the list by replacing values starting from index with values. + * use negative numbers to reach elements from the end of the list. put + * call will always be able to find the index. In case there is few items, it will loop over. In case end + * of the list is reached before values run out, list is extended to accomodate for more values. in case you + * want to append at the end of the list, use null as index. returns number of elements inserted.

    + *
    +     *     a = l(1, 2, 3); put(a, 1, 4); a  => [1, 4, 3]
    +     *     a = l(1, 2, 3); put(a, null, 4, 5, 6); a  => [1, 2, 3, 4, 5, 6]
    +     *     a = l(l(0,0,0),l(0,0,0),l(0,0,0)); put(element(a, 1), 1, 1); a => [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
    +     * 
    + * + *

    List operations

    + *

    l(values ...), l(iterator)

    + *

    Creates a list of values of the expressions passed as parameters. It can be used as an L-value and if all + * elements are variables, you coujld use it to return multiple results from one function call, if that function returns + * a list of results with the same size as the l call uses. In case there is only one argument and it is + * an iterator (vanilla expression specification has range, but Minecraft API implements + * a bunch of them, like diamond), it will convert it to a proper list. Iterators can only be used in + * high order functions, and are treated as empty lists, unless unrolled with l

    + *
    +     * l(1,2,'foo') => [1, 2, foo]
    +     * l() => [] (empty list)
    +     * l(range(10)) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    +     * l(1, 2) = l(3, 4) => Error: l is not a variable
    +     * l(foo, bar) = l(3,4); foo==3 && bar==4 => 1
    +     * l(foo, bar, baz) = l(2, 4, 6); l(min(foo, bar), baz) = l(3, 5); l(foo, bar, baz)  => [3, 4, 5]
    +     * 
    + *

    In the last example l(min(foo, bar), baz) creates a valid L-value, as min(foo, bar) finds the + * lower of the variables (in this case foo) creating a valid assignable L-list of [foo, baz], and these + * values will be assigned new values

    + * + *

    join(delim, list), join(delim, values ...)

    + *

    Returns a string that contains joined elements of the list, iterator, or all values, concatenated with delim delimiter

    + *
    +     *     join('-',range(10))  => 0-1-2-3-4-5-6-7-8-9
    +     *     join('-','foo')  => foo
    +     *     join('-', 'foo', 'bar')  => foo-bar
    +     * 
    + * + *

    split(delim, expr)

    + *

    Splits a string undr expr by delim which can be a regular expression

    + *
    +     *     split('',foo)  => [f, o, o]
    +     *     split('.','foo.bar')  => []
    +     *     split('\\.','foo.bar')  => [foo, bar]
    +     * 
    + * + *

    slice(expr, from, to?)

    + *

    extracts a substring, or sublist (based on the type of the result of the expression under expr with starting index + * of from, and ending at to if provided, or the end, if omitted

    + *
    +     *     slice(l(0,1,2,3,4,5), 1, 3)  => [1, 2, 3]
    +     *     slice('foobar', 0, 1)  => 'f'
    +     *     slice('foobar', 3)  => 'bar'
    +     *     slice(range(10), 3, 5)  => [3, 4, 5]
    +     *     slice(range(10), 5)  => [5, 6, 7, 8, 9]
    +     * 
    + * + *

    sort(list), sort(values ...)

    + *

    Sorts in the default sortographical order either all arguments, or a list if its the only argument. It returns a new + * sorted list, not affecting the list passed to the argument

    + *
    +     * sort(3,2,1)  => [1, 2, 3]
    +     * sort('a',3,11,1)  => [1, 3, 11, 'a']
    +     * list = l(4,3,2,1); sort(list)  => [1, 2, 3, 4]
    +     * 
    + * + *

    sort_key(list, key_expr)

    + *

    Sorts a copy of the list in the order or keys as defined by the key_expr for each element

    + *
    +     *     sort_key([1,3,2],_)  => [1, 2, 3]
    +     *     sort_key([1,3,2],-_)  => [3, 2, 1]
    +     *     sort_key(l(range(10)),rand(1))  => [1, 0, 9, 6, 8, 2, 4, 5, 7, 3]
    +     *     sort_key(l(range(20)),str(_))  => [0, 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 3, 4, 5, 6, 7, 8, 9]
    +     * 
    + * + *

    range(to), range(from, to), range(from, to, step)

    + *

    Creates a range of numbers from from, no greater/larger than to. + * The step parameter dictates not only the increment size, but also direction (can be negative). + * The returned value is not a proper list, just the iterator + * but if for whatever reason you need a proper list with all items evaluated, use l(range(to)). + * Primarily to be used in higher order functions

    + *
    +     *     range(10)  => [...]
    +     *     l(range(10))  => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    +     *     map(range(10),_*_)  => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    +     *     reduce(range(10),_a+_, 0)  => 45
    +     *     range(5,10)  => [5, 6, 7, 8, 9]
    +     *     range(20, 10, -2)  => [20, 18, 16, 14, 12]
    +     * 
    + * + *

    element(list, index)(deprecated)

    + *

    Legacy support for older method that worked only on lists. Please use get(...) for equivalent + * support, or . operator. Also previous unique behaviours with put on lists has been + * removed to support all type of container types.

    + * + *

    Map operations

    + *

    Scarpet supports map structures, aka hashmaps, dicts etc. Map structure can also be used, with null + * values as sets. Apart from container access functions, (. , get, put, has, delete), + * the following functions:

    + *
    + */ + + void BasicDataStructures() + { + addFunction("l", lv -> + { + if (lv.size() == 1 && lv.get(0) instanceof LazyListValue) + return ListValue.wrap(((LazyListValue) lv.get(0)).unroll()); + return new ListValue.ListConstructorValue(lv); + }); + + addFunction("join", (lv) -> + { + if (lv.size() < 2) + throw new InternalExpressionException("join takes at least 2 arguments"); + String delimiter = lv.get(0).getString(); + List toJoin; + if (lv.size()==2 && lv.get(1) instanceof LazyListValue) + { + toJoin = ((LazyListValue) lv.get(1)).unroll(); + + } + else if (lv.size() == 2 && lv.get(1) instanceof ListValue) + { + toJoin = new ArrayList<>(((ListValue)lv.get(1)).getItems()); + } + else + { + toJoin = lv.subList(1,lv.size()); + } + return new StringValue(toJoin.stream().map(Value::getString).collect(Collectors.joining(delimiter))); + }); + + addBinaryFunction("split", (d, v) -> { + String delimiter = d.getString(); + String hwat = v.getString(); + return ListValue.wrap(Arrays.stream(hwat.split(delimiter)).map(StringValue::new).collect(Collectors.toList())); + }); + + addFunction("slice", (lv) -> { + + if (lv.size() != 2 && lv.size() != 3) + throw new InternalExpressionException("slice takes 2 or 3 arguments"); + Value hwat = lv.get(0); + long from = NumericValue.asNumber(lv.get(1)).getLong(); + long to = -1; + if (lv.size()== 3) + to = NumericValue.asNumber(lv.get(2)).getLong(); + return hwat.slice(from, to); + }); + + addFunction("sort", (lv) -> + { + List toSort = lv; + if (lv.size()==1 && lv.get(0) instanceof ListValue) + { + toSort = new ArrayList<>(((ListValue)lv.get(0)).getItems()); + } + Collections.sort(toSort); + return ListValue.wrap(toSort); + }); + + addLazyFunction("sort_key", 2, (c, t, lv) -> //get working with iterators + { + Value v = lv.get(0).evalValue(c); + if (!(v instanceof ListValue)) + throw new InternalExpressionException("First argument for sort_key should be a List"); + LazyValue sortKey = lv.get(1); + //scoping + LazyValue __ = c.getVariable("_"); + + List toSort = new ArrayList<>(((ListValue) v).getItems()); + + Collections.sort(toSort,(v1, v2) -> { + c.setVariable("_",(cc, tt) -> v1); + Value ev1 = sortKey.evalValue(c); + c.setVariable("_",(cc, tt) -> v2); + Value ev2 = sortKey.evalValue(c); + return ev1.compareTo(ev2); + }); + //revering scope + c.setVariable("_", __); + return (cc, tt) -> ListValue.wrap(toSort); + }); + + addFunction("range", (lv) -> + { + long from = 0; + long to = 0; + long step = 1; + int argsize = lv.size(); + if (argsize == 0 || argsize > 3) + throw new InternalExpressionException("range accepts from 1 to 3 arguments, not "+argsize); + to = NumericValue.asNumber(lv.get(0)).getLong(); + if (lv.size() > 1) + { + from = to; + to = NumericValue.asNumber(lv.get(1)).getLong(); + if (lv.size() > 2) + { + step = NumericValue.asNumber(lv.get(2)).getLong(); + } + } + return LazyListValue.range(from, to, step); + }); + + addFunction("m", lv -> + { + if (lv.size() == 1 && lv.get(0) instanceof LazyListValue) + return new MapValue(((LazyListValue) lv.get(0)).unroll()); + return new MapValue(lv); + }); + + addUnaryFunction("keys", v -> { + if (v instanceof MapValue) + return new ListValue(((MapValue) v).getMap().keySet()); + throw new InternalExpressionException("keys applies only to maps"); + }); + + addUnaryFunction("values", v -> { + if (v instanceof MapValue) + return new ListValue(((MapValue) v).getMap().values()); + throw new InternalExpressionException("keys applies only to maps"); + }); + + addUnaryFunction("pairs", v -> { + if (v instanceof MapValue) + return ListValue.wrap(((MapValue) v).getMap().entrySet().stream().map( + (p) -> ListValue.of(p.getKey(), p.getValue()) + ).collect(Collectors.toList())); + throw new InternalExpressionException("pairs applies only to maps"); + }); + + addBinaryFunction("get", (container, key) -> + { + if (container instanceof ContainerValueInterface) + return ((ContainerValueInterface) container).get(key); + throw new InternalExpressionException("Cannot access elements of a non-container"); + }); + + addBinaryFunction("has", (container, key) -> + { + if (container instanceof ContainerValueInterface) + return new NumericValue(((ContainerValueInterface) container).has(key)); + throw new InternalExpressionException("Cannot access elements of a non-container"); + }); + + //Deprecated, use "get" instead, or . operator + addBinaryFunction("element", (v1, v2) -> + { + if (!(v1 instanceof ListValue)) + throw new InternalExpressionException("First argument of get should be a list"); + List items = ((ListValue)v1).getItems(); + long index = NumericValue.asNumber(v2).getLong(); + int numitems = items.size(); + long range = abs(index)/numitems; + index += (range+2)*numitems; + index = index % numitems; + return items.get((int)index); + }); + + addFunction("put", (lv) -> + { + if(lv.size()<3) + { + throw new InternalExpressionException("put takes at least three arguments, a container, address, and values to insert at that index"); + } + Value container = lv.get(0); + if (!(container instanceof ContainerValueInterface)) + { + throw new InternalExpressionException("First argument of put should be a container: list, map or nbt"); + } + Value where = lv.get(1); + Value what = lv.get(2); + if (lv.size()>3) + { + return ((ContainerValueInterface) container).put(where, what, lv.get(3)); + } + else + { + return ((ContainerValueInterface) container).put(where, what); + } + }); + + addBinaryFunction("delete", (container, address) -> + { + if (container instanceof ContainerValueInterface) + { + return ((ContainerValueInterface) container).delete(address); + } + throw new InternalExpressionException("First argument of delete should be a container: list, map or nbt"); + }); + } + /** * * @@ -2695,7 +2722,8 @@ public class Expression implements Cloneable Operators(); ArithmeticOperations(); SystemFunctions(); - ListsLoopsAndHigherOrderFunctions(); + LoopsAndHigherOrderFunctions(); + BasicDataStructures(); } private List shuntingYard() diff --git a/src/main/java/carpet/script/value/ContainerValueInterface.java b/src/main/java/carpet/script/value/ContainerValueInterface.java index 09e54b28..3b62d327 100644 --- a/src/main/java/carpet/script/value/ContainerValueInterface.java +++ b/src/main/java/carpet/script/value/ContainerValueInterface.java @@ -9,5 +9,5 @@ public interface ContainerValueInterface } public Value get(Value where); public boolean has(Value where); - public Value remove(Value where); + public Value delete(Value where); } diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 9dfea2be..32ad09b3 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -101,13 +101,13 @@ public class EntityValue extends Value } @Override - public boolean equals(Value v) + public boolean equals(Object v) { if (v instanceof EntityValue) { return entity.getEntityId()==((EntityValue) v).entity.getEntityId(); } - return super.equals(v); + return super.equals((Value)v); } @Override diff --git a/src/main/java/carpet/script/value/LazyListValue.java b/src/main/java/carpet/script/value/LazyListValue.java index 5035c018..4a22d0de 100644 --- a/src/main/java/carpet/script/value/LazyListValue.java +++ b/src/main/java/carpet/script/value/LazyListValue.java @@ -121,7 +121,7 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato } @Override - public boolean equals(final Value o) + public boolean equals(final Object o) { return false; } diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java index fce10b91..5c94c308 100644 --- a/src/main/java/carpet/script/value/ListValue.java +++ b/src/main/java/carpet/script/value/ListValue.java @@ -212,7 +212,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf } @Override - public boolean equals(final Value o) + public boolean equals(final Object o) { if (o instanceof ListValue) { @@ -346,7 +346,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf } @Override - public Value remove(Value where) + public Value delete(Value where) { long index = NumericValue.asNumber(where).getLong(); int numitems = items.size(); diff --git a/src/main/java/carpet/script/value/MapValue.java b/src/main/java/carpet/script/value/MapValue.java index 4c782770..a6137e21 100644 --- a/src/main/java/carpet/script/value/MapValue.java +++ b/src/main/java/carpet/script/value/MapValue.java @@ -120,7 +120,7 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa } @Override - public boolean equals(final Value o) + public boolean equals(final Object o) { if (o instanceof MapValue) { @@ -177,7 +177,7 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa } @Override - public Value remove(Value where) + public Value delete(Value where) { return map.remove(where); } diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index 5618c869..f326be97 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -202,11 +202,11 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa } @Override - public boolean equals(final Value o) + public boolean equals(final Object o) { if (o instanceof NBTSerializableValue) return getTag().equals(((NBTSerializableValue) o).getTag()); - return super.equals(o); + return super.equals((Value)o); } @Override @@ -408,17 +408,13 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa } @Override - public Value remove(Value where) + public Value delete(Value where) { NbtPathArgumentType.NbtPath path = cachePath(where.getString()); Tag tag = getTag().copy(); int removed = path.remove(tag); - if (removed > 0) - { - replaceTag(tag); - return Value.TRUE; - } - return Value.FALSE; + if (removed > 0) replaceTag(tag); + return new NumericValue(removed); } public static class InventoryLocator diff --git a/src/main/java/carpet/script/value/NullValue.java b/src/main/java/carpet/script/value/NullValue.java index 91994ccd..eeef2f0d 100644 --- a/src/main/java/carpet/script/value/NullValue.java +++ b/src/main/java/carpet/script/value/NullValue.java @@ -28,7 +28,7 @@ public class NullValue extends NumericValue // TODO check nonsingleton code public NullValue() {super(0.0D);} @Override - public boolean equals(final Value o) + public boolean equals(final Object o) { return o instanceof NullValue; } diff --git a/src/main/java/carpet/script/value/NumericValue.java b/src/main/java/carpet/script/value/NumericValue.java index 48e90372..77ca3ea7 100644 --- a/src/main/java/carpet/script/value/NumericValue.java +++ b/src/main/java/carpet/script/value/NumericValue.java @@ -125,7 +125,7 @@ public class NumericValue extends Value return getString().compareTo(o.getString()); } @Override - public boolean equals(Value o) + public boolean equals(Object o) { if (o instanceof NullValue) { @@ -133,7 +133,7 @@ public class NumericValue extends Value } if (o instanceof NumericValue) { - return !this.subtract(o).getBoolean(); + return !this.subtract((Value) o).getBoolean(); } return super.equals(o); } diff --git a/src/main/java/carpet/script/value/Value.java b/src/main/java/carpet/script/value/Value.java index eda60bb7..c585748d 100644 --- a/src/main/java/carpet/script/value/Value.java +++ b/src/main/java/carpet/script/value/Value.java @@ -115,6 +115,16 @@ public abstract class Value implements Comparable, Cloneable } return getString().compareTo(o.getString()); } + + @Override // for hashmap key access + public boolean equals(final Object o) + { + if (o instanceof Value) + return this.compareTo((Value) o)==0; + return false; + } + + // for general use public boolean equals(final Value o) { return this.compareTo(o)==0; From c442a78e49c27db9b239148dfb295407703b9d66 Mon Sep 17 00:00:00 2001 From: gnembon Date: Mon, 7 Oct 2019 16:50:36 -0300 Subject: [PATCH 11/13] docs --- src/main/java/carpet/script/Expression.java | 30 +++++++++---------- .../java/carpet/script/value/ListValue.java | 25 +++++++++++++++- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index 44fb59a3..06bec169 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -1557,7 +1557,9 @@ public class Expression implements Cloneable *

    Loops, and higher order functions

    *
    *

    Efficient use of these functions can greatly simplify your programs and speed them up, as these functions - * will internalize most of the operations that need to be applied on multiple values at the same time

    + * will internalize most of the operations that need to be applied on multiple values at the same time. Most + * of them take a list argument which can be any iterable structure in scarpet, including + * generators, like rect, or range, and maps, where the iterator returns all the map keys

    *

    Loops

    * *

    for(list,expr(_,_i),exit(_,_i)?)

    @@ -1656,12 +1658,9 @@ public class Expression implements Cloneable */ public void LoopsAndHigherOrderFunctions() { - - - - //condition and expression will get a bound 'i' - //returns last successful expression or false - // while(cond, limit, expr) => ?? + // condition and expression will get a bound '_i' + // returns last successful expression or false + // while(cond, limit, expr) => ?? addLazyFunction("while", 3, (c, t, lv) -> { long limit = NumericValue.asNumber(lv.get(1).evalValue(c)).getLong(); @@ -2005,20 +2004,21 @@ public class Expression implements Cloneable *

    has(container, address)

    *

    Similar to get, but returns boolean value indicating if the given index / key / path is in the * container. Can be used to determine if get(...)==null means the element doesn't exist, or the stored value - * for this address is null.

    + * for this address is null, and is cheaper to run than get

    * *

    delete(container, address)

    *

    Removes specific entry from the container. For the lists - removes the element and shrinks it. For maps, it * removes the key from the map, and for nbt - removes content from a given path. For lists and maps returns previous - * entry at the address, for nbt's - number of removed objects.

    + * entry at the address, for nbt's - number of removed objects, with 0 indicating that the original value was unaffected.

    * + *

    put(container, address, value), put(container, address, value, condition)

    + *

    Modifies the container by replacing the value under the address with the supplied value. + * For lists, a valid index is required, but can be negative as well. If null is supplied as the address, it + * appends that value to the end of the table. * - *

    put(list, index, values ...), put(list, null, values ...)

    - *

    Modifies the list by replacing values starting from index with values. - * use negative numbers to reach elements from the end of the list. put - * call will always be able to find the index. In case there is few items, it will loop over. In case end - * of the list is reached before values run out, list is extended to accomodate for more values. in case you - * want to append at the end of the list, use null as index. returns number of elements inserted.

    + * ............ + * + * returns number of elements inserted.

    *
          *     a = l(1, 2, 3); put(a, 1, 4); a  => [1, 4, 3]
          *     a = l(1, 2, 3); put(a, null, 4, 5, 6); a  => [1, 2, 3, 4, 5, 6]
    diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java
    index 5c94c308..dd3e75a4 100644
    --- a/src/main/java/carpet/script/value/ListValue.java
    +++ b/src/main/java/carpet/script/value/ListValue.java
    @@ -304,8 +304,23 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
             return (double)items.size();
         }
     
    +    @Override
    +    public Value put(Value where, Value value, Value conditionValue)
    +    {
    +        String condition = conditionValue.getString();
    +        if (condition.equalsIgnoreCase("insert"))
    +            return put(where, value, false);
    +        if (condition.equalsIgnoreCase("replace"))
    +            return put(where, value, true);
    +        throw new  InternalExpressionException("list put modifier could be either insert, or replace");
    +    }
    +
         @Override
         public Value put(Value ind, Value value)
    +    {
    +        return put(ind, value, true);
    +    }
    +    private Value  put(Value ind, Value value, boolean replace)
         {
             if (ind == Value.NULL)
             {
    @@ -323,7 +338,15 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
                     index = index % numitems;
                 }
                 while (index >= items.size()) items.add(Value.NULL);
    -            return items.set(index, value);
    +            if (replace)
    +            {
    +                return items.set(index, value);
    +            }
    +            else
    +            {
    +                items.add(index, value);
    +                return value;
    +            }
             }
         }
     
    
    From daea1c7aa88a8837fa011d017a74c8400559105f Mon Sep 17 00:00:00 2001
    From: gnembon 
    Date: Tue, 8 Oct 2019 01:59:12 -0300
    Subject: [PATCH 12/13] extended list put support
    
    ---
     .../java/carpet/script/value/ListValue.java   | 35 ++++++++++++++-----
     1 file changed, 26 insertions(+), 9 deletions(-)
    
    diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java
    index dd3e75a4..3e1b43fd 100644
    --- a/src/main/java/carpet/script/value/ListValue.java
    +++ b/src/main/java/carpet/script/value/ListValue.java
    @@ -6,6 +6,7 @@ import net.minecraft.util.registry.Registry;
     
     import java.util.*;
     import java.util.stream.Collectors;
    +import java.util.stream.StreamSupport;
     
     import static java.lang.Math.abs;
     
    @@ -309,22 +310,31 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
         {
             String condition = conditionValue.getString();
             if (condition.equalsIgnoreCase("insert"))
    -            return put(where, value, false);
    +            return put(where, value, false, false);
    +        if (condition.equalsIgnoreCase("extend"))
    +            return put(where, value, false, true);
             if (condition.equalsIgnoreCase("replace"))
    -            return put(where, value, true);
    +            return put(where, value, true, false);
             throw new  InternalExpressionException("list put modifier could be either insert, or replace");
         }
     
         @Override
         public Value put(Value ind, Value value)
         {
    -        return put(ind, value, true);
    +        return put(ind, value, true, false);
         }
    -    private Value  put(Value ind, Value value, boolean replace)
    +    private Value  put(Value ind, Value value, boolean replace, boolean extend)
         {
             if (ind == Value.NULL)
             {
    -            items.add(value);
    +            if (extend && value instanceof AbstractListValue)
    +            {
    +                ((AbstractListValue) value).iterator().forEachRemaining((v)-> items.add(v));
    +            }
    +            else
    +            {
    +                items.add(value);
    +            }
                 return items.get(items.size()-1);
             }
             else
    @@ -337,16 +347,23 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
                     index += (range + 2) * numitems;
                     index = index % numitems;
                 }
    -            while (index >= items.size()) items.add(Value.NULL);
                 if (replace)
                 {
    +                while (index >= items.size()) items.add(Value.NULL);
                     return items.set(index, value);
                 }
    -            else
    +            while (index > items.size()) items.add(Value.NULL);
    +
    +            if (extend && value instanceof AbstractListValue)
                 {
    -                items.add(index, value);
    -                return value;
    +                Iterable iterable = ((AbstractListValue) value)::iterator;
    +                List appendix = StreamSupport.stream( iterable.spliterator(), false).collect(Collectors.toList());
    +                items.addAll(index, appendix );
    +                return items.get(index+appendix.size()-1);
                 }
    +            items.add(index, value);
    +            return value;
    +
             }
         }
     
    
    From ef757aec1d9c752420602793b919996d4442a5e2 Mon Sep 17 00:00:00 2001
    From: gnembon 
    Date: Tue, 8 Oct 2019 01:52:53 -0300
    Subject: [PATCH 13/13] docs contd
    
    ---
     .../java/carpet/script/CarpetExpression.java  |   6 +-
     src/main/java/carpet/script/Expression.java   | 130 +++++++++++++++---
     .../java/carpet/script/value/MapValue.java    |  10 +-
     .../script/value/NBTSerializableValue.java    |   4 +
     src/main/resources/carpet.mixins.json         |   1 +
     5 files changed, 128 insertions(+), 23 deletions(-)
    
    diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java
    index db6a5a70..84e27f33 100644
    --- a/src/main/java/carpet/script/CarpetExpression.java
    +++ b/src/main/java/carpet/script/CarpetExpression.java
    @@ -99,7 +99,7 @@ import static java.lang.Math.sqrt;
     /**
      * 

    Minecraft specific API and scarpet language add-ons and commands

    *

    Here is the gist of the Minecraft related functions. Otherwise the CarpetScript could live without Minecraft.

    - *

    Dimension issues

    + *

    Dimension warning

    *

    One note, which is important is that most of the calls for entities and blocks * would refer to the current dimension of the caller, meaning, that if we for example * list all the players using player('all') function, if a player is in the @@ -108,7 +108,8 @@ import static java.lang.Math.sqrt; * refer to the overworld blocks and entities. * In case you would want to run commands across all dimensions, just run three of them, using * /execute in overworld/the_nether/the_end run script run ... and query - * players using player('*'), which only returns players in current dimension.

    + * players using player('*'), which only returns players in current dimension, or use + * in_dimension(expr) function.

    */ public class CarpetExpression { @@ -2361,6 +2362,7 @@ public class CarpetExpression *

    nbt(expr)

    *

    Treats the argument as a nbt serializable string and returns its nbt value. * In case nbt is not in a correct nbt compound tag format, it will return null value.

    + *

    Consult section about container operations in Expression to learn about possible operations on nbt values.

    *

    print(expr)

    *

    Displays the result of the expression to the chat. Overrides default scarpet behaviour of * sending everyting to stderr.

    diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index 06bec169..7ffaabe8 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -50,7 +50,7 @@ import static java.lang.Math.min; /** - *

    Fundamental components of scarpet programming language.

    + *

    Fundamental components of scarpet programming language (towards upcoming version 1.5).

    * *

    Scarpet (a.k.a. Carpet Script, or Script for Carpet) is a programming language designed to provide * the ability to write custom programs to run within Minecraft and @@ -74,7 +74,7 @@ import static java.lang.Math.min; *

      * script run print('Hello World!')
      * 
    - *

    or an overly complex example:

    + *

    or an OVERLY complex example:

    *
      * /script run
      *     block_check(x1, y1, z1, x2, y2, z2, block_to_check) ->
    @@ -83,7 +83,7 @@ import static java.lang.Math.min;
      *         l(miny, maxy) = sort(l(y1, y2));
      *         l(minz, maxz) = sort(l(z1, z2));
      *         'Need to compute the size of the area of course';
    - *         'Cause this language doesn\'t support comments';
    + *         'Cause this language doesn\'t support comments in the command mode';
      *         xsize = maxx - minx + 1;
      *         ysize = maxy - miny + 1;
      *         zsize = maxz - minz + 1;
    @@ -202,11 +202,36 @@ import static java.lang.Math.min;
      * Variables from outer scopes have a similar behaviour to, for example, nonlocal variables from python. 

    * * - *

    Line indicators

    + *

    Code delivery, line indicators

    *

    Note that this should only apply to pasting your code to execute with commandblock. Scarpet recommends placing your * code in packages (files with .sc extension that can be placed inside "/scripts" folder in the world files * and loaded as a module with command /script load [package_name]. Scarpet modules loaded from disk should only * contain code, no need to start with "/script run" prefix

    + *

    The following is the code that could be provided in a foo.sc file located in world /scripts folder

    + * + *
    + * run_program() -> (
    + *   loop( 10,
    + *     // looping 10 times
    + *     // comments are allowed in scripts located in world files
    + *     // since we can tell where that line ends
    + *     foo = floor(rand(10));
    + *     check_not_zero(foo);
    + *     print(_+' - foo: '+foo);
    + *     print('  reciprocal: '+  _/foo )
    + *   )
    + * );
    + * check_not_zero(foo) -> (
    + *   if (foo==0, foo = 1)
    + * )
    + * 
    + *

    Which we then call in-game with:

    + *
    + *     /script load foo
    + *     /script in foo invoke run_program
    + * 
    + *

    However the following code can also be input as a command, or in a command block.

    + * *

    Since the maximum command that can be input to the chat is limited in length, you will be probably inserting your * programs by pasting them to command blocks or reading from world files, however pasting to command blocks will remove some whitespaces and squish * your newlines making the code not readable. If you are pasting a program that is perfect and will never cause an error, @@ -833,7 +858,8 @@ public class Expression implements Cloneable * Names in the signature don't need to be used anywhere else, other occurrences of these names * will be masked in this function scope. * Function call creates new scope for variables inside expr, so all non-global variables are not - * visible from the caller scope. All parameters are passed by value to the new scope, including lists

    + * visible from the caller scope. All parameters are passed by value to the new scope, including lists and other + * containers, however their copy will be shallow.

    *
          * a(lst) -> lst+=1; list = l(1,2,3); a(list); a(list); list  // => [1,2,3]
          * 
    @@ -1045,7 +1071,7 @@ public class Expression implements Cloneable * even if they look like them: *

    *
      - *
    • Match, Get~ .
    • + *
    • Match, Get ~ .
    • *
    • Unary + - !
    • *
    • Exponent ^
    • *
    • Multiplication * / %
    • @@ -1067,6 +1093,8 @@ public class Expression implements Cloneable * straightforward and immediate, and the source object should behave like a container and support full container * API, meaning get(...), put(...), delete(...), * and has(...) functions

      + *

      WARNING: Unlike for some languages, where item access operator can be an L-value, to assign new values to + * container elements, use put function instead, i.e. a.1='foo' won't work.

      * *

      Matching Operator ~

      *

      This operator should be understood as 'matches', 'contains', 'is_in', @@ -1400,14 +1428,14 @@ public class Expression implements Cloneable *

      log10(n)

      *

      Decimal logarithm of n. Its ceiling is the length of its floor.

      *

      log(n)

      - *

      Binary logarithm of n. Finally, a proper one, not like the other 100.

      + *

      Binary logarithm of n. Finally, a proper one, not like the previous 11.

      *

      log1p(n)

      *

      Binary logarithm of n+1. Also always positive.

      *

      mandelbrot(a, b, limit)

      *

      Computes the value of the mandelbrot set, for set a and b spot. * Spot the beetle. Why not.

      *

      min(arg, ...), min(list), max(arg, ...), max(list)

      - *

      Compute minimum or maximum of supplied arguments assuming default sortraphical order. In case you are + *

      Compute minimum or maximum of supplied arguments assuming default sorthoraphical order. In case you are * missing argmax, just use a ~ max(a), little less efficient, but still fun. *

      *

      @@ -2011,18 +2039,61 @@ public class Expression implements Cloneable * removes the key from the map, and for nbt - removes content from a given path. For lists and maps returns previous * entry at the address, for nbt's - number of removed objects, with 0 indicating that the original value was unaffected.

      * - *

      put(container, address, value), put(container, address, value, condition)

      + *

      put(container, address, value), put(container, address, value, mode)

      + *

      Lists

      *

      Modifies the container by replacing the value under the address with the supplied value. - * For lists, a valid index is required, but can be negative as well. If null is supplied as the address, it - * appends that value to the end of the table. + * For lists, a valid index is required, but can be negative as well to indicate positions from the end of the list. + * If null is supplied as the address, it always means - add to the end of the list.

      + *

      There are three modes that lists can have items added to them: + *

        + *
      • replace(default): Replaces item under given index(address). Doesn't change the size of the array + * unless null address is used, which is an exception and then it appends to the end
      • + *
      • insert: Inserts given element at a specified index, shifting the rest of the array to make space + * for the item. Note that index of -1 points to the last element of the list, thus inserting at that position and + * moving the previous last element to the new last element position. To insert at the end, use += + * operator, or null address in put
      • + *
      • extend: treats the supplied value as an iterable set of values to insert at a given index, + * extending the list by this amount of items. Again use null address/index to point to the end of + * the list
      • + *
      + *

      Maps

      + *

      For maps there are no modes available (yet, seems there is no reason to). It replaces the value under the supplied + * key (address), or sets it if not currently present.

      + *

      NBT Tags

      + *

      + * The address for nbt values is a valid nbt path that you would use with /data command, and tag is any + * tag that would be applicable for a given insert operation. Note that to distinguish between proper types (like integer + * types, you need to use command notation, i.e. regular ints is 123, while byte size int would be 123b + * and an explicit string would be "5", so it helps that scarpet uses single quotes in his strings. Unlike + * for lists and maps, it returns the number of affected nodes, or 0 if none were affected. + *

      + *

      There are three modes that NBT tags can have items added to them: + *

        + *
      • replace(default): Replaces item under given path(address). Removes them first if possible, and then + * adds given element to the supplied position. The target path can indicate compound tag keys, lists, or individual + * elements of the lists.
      • + *
      • <N>: Index for list insertions. Inserts given element at a specified index, inside a list + * specified with the path address. Fails if list is not specified. It behaves like insert mode for lists, + * i.e. it is not removing any of the existing elements. Use replace to remove and replace existing element. + *
      • + *
      • merge: assumes that both path and replacement target are of compound type (dictionaries, maps, + * {} types), and merges keys from value with the compound tag under the path
      • + *
      * - * ............ - * - * returns number of elements inserted.

      *
            *     a = l(1, 2, 3); put(a, 1, 4); a  => [1, 4, 3]
      -     *     a = l(1, 2, 3); put(a, null, 4, 5, 6); a  => [1, 2, 3, 4, 5, 6]
      -     *     a = l(l(0,0,0),l(0,0,0),l(0,0,0)); put(element(a, 1), 1, 1); a => [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
      +     *     a = l(1, 2, 3); put(a, null, 4); a  => [1, 2, 3, 4]
      +     *     a = l(1, 2, 3); put(a, 1, 4, 'insert'); a  => [1, 4, 2, 3]
      +     *     a = l(1, 2, 3); put(a, null, l(4, 5, 6), 'extend'); a  => [1, 2, 3, 4, 5, 6]
      +     *     a = l(1, 2, 3); put(a, 1, l(4, 5, 6), 'extend'); a  => [1, 4, 5, 6, 2, 3]
      +     *     a = l(l(0,0,0),l(0,0,0),l(0,0,0)); put(a.1, 1, 1); a  => [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
      +     *     a = m(1,2,3,4); put(a, 5, null); a  => {1: null, 2: null, 3: null, 4: null, 5: null}
      +     *     tag = nbt('{}'); put(tag, 'BlockData.Properties', '[1,2,3,4]'); tag  => {BlockData:{Properties:[1,2,3,4]}}
      +     *     tag = nbt('{a:[{lvl:3},{lvl:5},{lvl:2}]}'); put(tag, 'a[].lvl', 1); tag  => {a:[{lvl:1},{lvl:1},{lvl:1}]}
      +     *     tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl', 1, 2); tag
      +     *          => {a:[{lvl:[1,2,1,3]},{lvl:[3,2,1,1]},{lvl:[4,5,1,6]}]}
      +     *     tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl[1]', 1); tag
      +     *          => {a:[{lvl:[1,1,3]},{lvl:[3,1,1]},{lvl:[4,1,6]}]}
            * 
      * *

      List operations

      @@ -2114,10 +2185,26 @@ public class Expression implements Cloneable *

      Scarpet supports map structures, aka hashmaps, dicts etc. Map structure can also be used, with null * values as sets. Apart from container access functions, (. , get, put, has, delete), * the following functions:

      + *

      m(values ...), m(iterator), m(key_value_pairs)

      + *

      creates and initializes a map with supplied keys, and values. If the arguments contains a flat list, + * these are all treated as keys with no value, same goes with the iterator - creates a map that behaves like a set. + * If the arguments is a list of lists, they have to have two elements each, and then first is a key, and second, + * a value

      + *
      +     * m(1,2,'foo') => {1: null, 2: null, foo: null}
      +     * m() => {} (empty map)
      +     * m(range(10)) => {0: null, 1: null, 2: null, 3: null, 4: null, 5: null, 6: null, 7: null, 8: null, 9: null}
      +     * m(l(1, 2), l(3, 4)) => {1: 2, 3: 4}
      +     * reduce(range(10), put(_a, _, _*_); _a, m())
      +     *      => {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
      +     * 
      + *

      keys(map), values(map), pairs(map)

      + *

      Returns full lists of keys, values and key-value pairs (2-element lists) for all the entries in the map

      + * *
    */ - void BasicDataStructures() + public void BasicDataStructures() { addFunction("l", lv -> { @@ -2380,7 +2467,7 @@ public class Expression implements Cloneable *
    *

    Auxiliary functions

    * - *

    lower(expr), uppper(expr), title(expr)

    + *

    lower(expr), upper(expr), title(expr)

    *

    Returns lowercase, uppercase or titlecase representation of a string representation of the passed expression

    *
          * lower('aBc') => 'abc'
    @@ -2439,8 +2526,13 @@ public class Expression implements Cloneable
          *     print(str('this took %d milliseconds',time()-start_time))
          * 
    * + *

    profile_expr(expression)

    + *

    Returns number of times given expression can be run in 50ms time. Useful to profile and optimize your + * code. Note that, even if its only a number, it WILL run these commands, so if they are destructive, + * you need to be careful.

    + * *
    - *

    Access to variables and stored functions

    + *

    Access to variables and stored functions (use with caution)

    * *

    var(expr)

    *

    Returns the variable under the name of the string value of the expression. Allows to diff --git a/src/main/java/carpet/script/value/MapValue.java b/src/main/java/carpet/script/value/MapValue.java index a6137e21..bf217311 100644 --- a/src/main/java/carpet/script/value/MapValue.java +++ b/src/main/java/carpet/script/value/MapValue.java @@ -179,13 +179,19 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa @Override public Value delete(Value where) { - return map.remove(where); + Value ret = map.remove(where); + if (ret == null) + return Value.NULL; + return ret; } @Override public Value put(Value key, Value value) { - return map.put(key, value); + Value ret = map.put(key, value); + if (ret == null) + return Value.NULL; + return ret; } @Override diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index f326be97..ebfee982 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -73,6 +73,10 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa return value; } + public Value clone() + { + return new NBTSerializableValue(getTag().copy()); + } public NBTSerializableValue(Tag tag) { nbtTag = tag; diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index 2781ed19..afc47410 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -1,5 +1,6 @@ { "required": true, + "refmap": "carpet-refmap.json", "package": "carpet.mixins", "compatibilityLevel": "JAVA_8", "mixins": [