diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java index a0d2e6ab..890b8584 100644 --- a/src/main/java/carpet/script/CarpetEventServer.java +++ b/src/main/java/carpet/script/CarpetEventServer.java @@ -6,6 +6,7 @@ import carpet.helpers.TickSpeed; import carpet.script.exception.IntegrityException; import carpet.script.exception.InternalExpressionException; import carpet.script.exception.InvalidCallbackException; +import carpet.script.utils.GlocalFlag; import carpet.script.value.BlockValue; import carpet.script.value.BooleanValue; import carpet.script.value.EntityValue; @@ -73,8 +74,7 @@ public class CarpetEventServer public final CarpetScriptServer scriptServer; private static final List NOARGS = Collections.emptyList(); public final Map customEvents = new HashMap<>(); - - + public GlocalFlag handleEvents = new GlocalFlag(true); public enum CallbackResult { @@ -194,31 +194,33 @@ public class CarpetEventServer */ public void call(Supplier> argumentSupplier, Supplier cmdSourceSupplier) { - if (callList.size() > 0) + if (callList.size() > 0 && CarpetServer.scriptServer != null) { - CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet events", CarpetProfiler.TYPE.GENERAL); - List argv = argumentSupplier.get(); // empty for onTickDone - ServerCommandSource source; - try - { - source = cmdSourceSupplier.get(); - } - catch (NullPointerException noReference) // todo figure out what happens when closing. - { - return; - } - String nameCheck = perPlayerDistribution?source.getName():null; - assert argv.size() == reqArgs; - List fails = new ArrayList<>(); - for (Callback call: callList) - { - // supressing calls where target player hosts simply don't match - // handling global hosts with player targets is left to when the host is resolved (few calls deeper). - if (nameCheck != null && call.optionalTarget != null && !nameCheck.equals(call.optionalTarget)) continue; - if (call.execute(source, argv) == CallbackResult.FAIL) fails.add(call); - } - for (Callback call : fails) callList.remove(call); - CarpetProfiler.end_current_section(currentSection); + CarpetServer.scriptServer.events.handleEvents.runIfEnabled( () -> { + CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet events", CarpetProfiler.TYPE.GENERAL); + List argv = argumentSupplier.get(); // empty for onTickDone + ServerCommandSource source; + try + { + source = cmdSourceSupplier.get(); + } + catch (NullPointerException noReference) // todo figure out what happens when closing. + { + return; + } + String nameCheck = perPlayerDistribution ? source.getName() : null; + assert argv.size() == reqArgs; + List fails = new ArrayList<>(); + for (Callback call : callList) + { + // supressing calls where target player hosts simply don't match + // handling global hosts with player targets is left to when the host is resolved (few calls deeper). + if (nameCheck != null && call.optionalTarget != null && !nameCheck.equals(call.optionalTarget)) continue; + if (call.execute(source, argv) == CallbackResult.FAIL) fails.add(call); + } + for (Callback call : fails) callList.remove(call); + CarpetProfiler.end_current_section(currentSection); + }); } } diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index e23e2971..d8b9c5f9 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -72,7 +72,7 @@ public class CarpetExpression Value playerValue = new EntityValue(e).bindTo("p"); context.with("p", (cc, tt) -> playerValue); } - return this.expr.eval(context).getBoolean(); + return CarpetServer.scriptServer.events.handleEvents.getWhileDisabled(()-> this.expr.eval(context).getBoolean()); } catch (ExpressionException e) { @@ -109,7 +109,7 @@ public class CarpetExpression Value playerValue = new EntityValue(e).bindTo("p"); context.with("p", (cc, tt) -> playerValue); } - return this.expr.eval(context); + return CarpetServer.scriptServer.events.handleEvents.getWhileDisabled(()-> this.expr.eval(context)); } catch (ExpressionException e) { diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java index 5895a805..a25c3c20 100644 --- a/src/main/java/carpet/script/CarpetScriptHost.java +++ b/src/main/java/carpet/script/CarpetScriptHost.java @@ -710,7 +710,7 @@ public class CarpetScriptHost extends ScriptHost { try { - return call(source, function, args); + return scriptServer.events.handleEvents.getWhileDisabled(() -> call(source, function, args)); } catch (CarpetExpressionException exc) { @@ -815,11 +815,11 @@ public class CarpetScriptHost extends ScriptHost // TODO: this is just for now - invoke would be able to invoke other hosts scripts assertAppIntegrity(function.getModule()); Context context = new CarpetContext(this, source, BlockPos.ORIGIN); - return function.getExpression().evalValue( + return scriptServer.events.handleEvents.getWhileDisabled(() -> function.getExpression().evalValue( () -> function.lazyEval(context, Context.VOID, function.getExpression(), function.getToken(), argv), context, Context.VOID - ); + )); } catch (ExpressionException e) { @@ -891,6 +891,7 @@ public class CarpetScriptHost extends ScriptHost { ServerPlayerEntity player = (user==null)?null:scriptServer.server.getPlayerManager().getPlayer(user); ServerCommandSource source = (player != null)?player.getCommandSource():scriptServer.server.getCommandSource(); + return scriptServer.events.handleEvents.getWhileDisabled(()->{ try { return callUDF(BlockPos.ORIGIN, source, fun, arguments); @@ -899,6 +900,7 @@ public class CarpetScriptHost extends ScriptHost { return Value.NULL; } + }); } diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java index 2c5935c6..4624cc52 100644 --- a/src/main/java/carpet/script/CarpetScriptServer.java +++ b/src/main/java/carpet/script/CarpetScriptServer.java @@ -375,7 +375,7 @@ public class CarpetScriptServer { CarpetProfiler.ProfilerToken token; token = CarpetProfiler.start_section(null, "Scarpet schedule", CarpetProfiler.TYPE.GENERAL); - events.tick(); + events.handleEvents.getWhileDisabled( () -> {events.tick(); return null;}); CarpetProfiler.end_current_section(token); token = CarpetProfiler.start_section(null, "Scarpet app data", CarpetProfiler.TYPE.GENERAL); for (CarpetScriptHost host : modules.values()) diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index ac4b666f..1e09dc02 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -489,6 +489,39 @@ public class Expression return false; } + @Override + public LazyValue lazyEval(Context c, Context.Type i, Expression e, Tokenizer.Token t, List lazyParams) + { + ILazyFunction.checkInterrupts(); + if (num_params >= 0 && lazyParams.size() != num_params) + throw new InternalExpressionException("'"+name+"' requires "+num_params+" parameters, "+lazyParams.size()+" provided."); + + try + { + return fun.apply(c, i, lazyParams); + } + catch (RuntimeException exc) + { + throw handleCodeException(c, exc, e, t); + } + } + }); + } + + public void addLazyFunction(String name, TriFunction, LazyValue> fun) + { + functions.put(name, new AbstractLazyFunction(-1, name) + { + @Override + public boolean pure() { + return false; + } + + @Override + public boolean transitive() { + return false; + } + @Override public LazyValue lazyEval(Context c, Context.Type i, Expression e, Tokenizer.Token t, List lazyParams) { @@ -1216,6 +1249,7 @@ public class Expression if (arg.op instanceof LazyValue.ContextFreeLazyValue) continue; return optimized; } + // a few exceptions which we don't implement in the framework for simplicity for now if (!operation.pure()) { if (symbol.equals("->") && expectedType == Context.Type.MAPDEF) @@ -1226,6 +1260,14 @@ public class Expression return optimized; } } + if (operation.pure()) + { + // element access with constant elements will always resolve the same way. + if (symbol.equals(":") && expectedType == Context.Type.LVALUE) + { + expectedType = Context.Type.NONE; + } + } List args = new ArrayList<>(node.args.size()); for (ExpressionNode arg : node.args) { try { diff --git a/src/main/java/carpet/script/api/BlockIterators.java b/src/main/java/carpet/script/api/BlockIterators.java index 0c4ce028..27c15452 100644 --- a/src/main/java/carpet/script/api/BlockIterators.java +++ b/src/main/java/carpet/script/api/BlockIterators.java @@ -32,7 +32,7 @@ public class BlockIterators { public static void apply(Expression expression) { // lazy cause of lazy expression - expression.addLazyFunction("scan", -1, (c, t, llv) -> + expression.addLazyFunction("scan", (c, t, llv) -> { if (llv.size() < 3) throw new InternalExpressionException("'scan' needs many more arguments"); List lv = Fluff.AbstractFunction.unpackLazy(llv.subList(0, llv.size()-1), c, Context.NONE); @@ -140,7 +140,7 @@ public class BlockIterators { }); // must be lazy - expression.addLazyFunction("volume", -1, (c, t, llv) -> + expression.addLazyFunction("volume", (c, t, llv) -> { CarpetContext cc = (CarpetContext)c; if (llv.size() < 3) throw new InternalExpressionException("'volume' needs many more arguments"); diff --git a/src/main/java/carpet/script/api/WorldAccess.java b/src/main/java/carpet/script/api/WorldAccess.java index c02adb03..623e2713 100644 --- a/src/main/java/carpet/script/api/WorldAccess.java +++ b/src/main/java/carpet/script/api/WorldAccess.java @@ -219,46 +219,6 @@ public class WorldAccess { return bs; } - private static boolean tryBreakBlock_copy_from_ServerPlayerInteractionManager(ServerPlayerEntity player, BlockPos blockPos_1) - { - //this could be done little better, by hooking up event handling not in try_break_block but wherever its called - // so we can safely call it here - // but that would do for now. - BlockState blockState_1 = player.world.getBlockState(blockPos_1); - if (!player.getMainHandStack().getItem().canMine(blockState_1, player.world, blockPos_1, player)) { - return false; - } else { - BlockEntity blockEntity_1 = player.world.getBlockEntity(blockPos_1); - Block block_1 = blockState_1.getBlock(); - if ((block_1 instanceof CommandBlock || block_1 instanceof StructureBlock || block_1 instanceof JigsawBlock) && !player.isCreativeLevelTwoOp()) { - player.world.updateListeners(blockPos_1, blockState_1, blockState_1, 3); - return false; - } else if (player.isBlockBreakingRestricted(player.world, blockPos_1, player.interactionManager.getGameMode())) { - return false; - } else { - block_1.onBreak(player.world, blockPos_1, blockState_1, player); - boolean boolean_1 = player.world.removeBlock(blockPos_1, false); - if (boolean_1) { - block_1.onBroken(player.world, blockPos_1, blockState_1); - } - - if (player.isCreative()) { - return true; - } else { - ItemStack itemStack_1 = player.getMainHandStack(); - boolean boolean_2 = player.canHarvest(blockState_1); - itemStack_1.postMine(player.world, blockState_1, blockPos_1, player); - if (boolean_1 && boolean_2) { - ItemStack itemStack_2 = itemStack_1.isEmpty() ? ItemStack.EMPTY : itemStack_1.copy(); - block_1.afterBreak(player.world, player, blockPos_1, blockState_1, blockEntity_1, itemStack_2); - } - - return true; - } - } - } - } - private static void BooYah(ChunkGenerator generator) { synchronized (generator) @@ -928,7 +888,7 @@ public class WorldAccess { Block block = state.getBlock(); boolean success = false; if (!((block == Blocks.BEDROCK || block == Blocks.BARRIER) && player.interactionManager.isSurvivalLike())) - success = tryBreakBlock_copy_from_ServerPlayerInteractionManager(player, where); + success = player.interactionManager.tryBreakBlock(where); if (success) world.syncWorldEvent(null, 2001, where, Block.getRawIdFromState(state)); return BooleanValue.of(success); diff --git a/src/main/java/carpet/script/language/ControlFlow.java b/src/main/java/carpet/script/language/ControlFlow.java index bbb0610e..f7f32a44 100644 --- a/src/main/java/carpet/script/language/ControlFlow.java +++ b/src/main/java/carpet/script/language/ControlFlow.java @@ -38,7 +38,7 @@ public class ControlFlow { // obvious lazy due to conditional evaluation of arguments - expression.addLazyFunction("if", -1, (c, t, lv) -> + expression.addLazyFunction("if", (c, t, lv) -> { if ( lv.size() < 2 ) throw new InternalExpressionException("'if' statement needs to have at least one condition and one case"); @@ -79,7 +79,7 @@ public class ControlFlow { }); // needs to be lazy since execution of parameters but first one are conditional - expression.addLazyFunction("try", -1, (c, t, lv) -> + expression.addLazyFunction("try", (c, t, lv) -> { if (lv.size()==0) throw new InternalExpressionException("'try' needs at least an expression block, and either a catch_epr, or a number of pairs of filters and catch_expr"); diff --git a/src/main/java/carpet/script/language/DataStructures.java b/src/main/java/carpet/script/language/DataStructures.java index c472278d..13bb4516 100644 --- a/src/main/java/carpet/script/language/DataStructures.java +++ b/src/main/java/carpet/script/language/DataStructures.java @@ -104,7 +104,7 @@ public class DataStructures { }); // needs lazy cause sort function is reused - expression.addLazyFunction("sort_key", -1, (c, t, lv) -> //get working with iterators + expression.addLazyFunction("sort_key", (c, t, lv) -> //get working with iterators { if (lv.size() == 0) throw new InternalExpressionException("First argument for 'sort_key' should be a List"); @@ -207,7 +207,7 @@ public class DataStructures { }); // lazy cause conditional typing - questionable - expression.addLazyFunction("get", -1, (c, t, lv) -> + expression.addLazyFunction("get", (c, t, lv) -> { if (lv.size() == 0) throw new InternalExpressionException("'get' requires parameters"); @@ -235,7 +235,7 @@ public class DataStructures { }); // same as `get` - expression.addLazyFunction("has", -1, (c, t, lv) -> + expression.addLazyFunction("has", (c, t, lv) -> { if (lv.size() == 0) throw new InternalExpressionException("'has' requires parameters"); @@ -263,7 +263,7 @@ public class DataStructures { }); // same as `get` - expression.addLazyFunction("put", -1, (c, t, lv) -> + expression.addLazyFunction("put", (c, t, lv) -> { if(lv.size()<2) { @@ -302,7 +302,7 @@ public class DataStructures { }); // same as `get` - expression.addLazyFunction("delete", -1, (c, t, lv) -> + expression.addLazyFunction("delete", (c, t, lv) -> { if (lv.size() == 0) throw new InternalExpressionException("'delete' requires parameters"); diff --git a/src/main/java/carpet/script/language/Sys.java b/src/main/java/carpet/script/language/Sys.java index 8ecea532..c5fbb401 100644 --- a/src/main/java/carpet/script/language/Sys.java +++ b/src/main/java/carpet/script/language/Sys.java @@ -423,7 +423,7 @@ public class Sys { }); // lazy cause default expression may not be executed if not needed - expression.addLazyFunction("system_variable_get", -1, (c, t, lv) -> + expression.addLazyFunction("system_variable_get", (c, t, lv) -> { if (lv.size() == 0) throw new InternalExpressionException("'system_variable_get' expects at least a key to be fetched"); Value key = lv.get(0).evalValue(c); diff --git a/src/main/java/carpet/script/language/Threading.java b/src/main/java/carpet/script/language/Threading.java index bc2c0d09..52f3c1a5 100644 --- a/src/main/java/carpet/script/language/Threading.java +++ b/src/main/java/carpet/script/language/Threading.java @@ -67,7 +67,7 @@ public class Threading }); // lazy cause expr is evaluated in the same type - expression.addLazyFunction("synchronize", -1, (c, t, lv) -> + expression.addLazyFunction("synchronize", (c, t, lv) -> { if (lv.size() == 0) throw new InternalExpressionException("'synchronize' require at least an expression to synchronize"); Value lockValue = Value.NULL; @@ -85,7 +85,7 @@ public class Threading }); // lazy since exception expression is very conditional - expression.addLazyFunction("sleep", -1, (c, t, lv) -> + expression.addLazyFunction("sleep", (c, t, lv) -> { long time = lv.isEmpty()?0L:NumericValue.asNumber(lv.get(0).evalValue(c)).getLong(); boolean interrupted = false; diff --git a/src/main/java/carpet/script/utils/GlocalFlag.java b/src/main/java/carpet/script/utils/GlocalFlag.java new file mode 100644 index 00000000..af9ae8f4 --- /dev/null +++ b/src/main/java/carpet/script/utils/GlocalFlag.java @@ -0,0 +1,68 @@ +package carpet.script.utils; + +import java.util.function.Supplier; + +public class GlocalFlag extends ThreadLocal +{ + private final boolean initial; + + public GlocalFlag(boolean initial) + { + this.initial = initial; + } + + @Override + public Boolean initialValue() + { + return initial; + } + + /** + * Allows to thread-safely wrap a call while disabling a global flag and setting it back up right after. + * @param action - callback to invoke when the wrapping is all setup + * @param - returned value of that action, whatever that might be + * @return result of the action + */ + public T getWhileDisabled(Supplier action) + { + return whileValueReturn(!initial, action); + } + + private T whileValueReturn(boolean what, Supplier action) + { + T result; + boolean previous; + synchronized (this) + { + previous = get(); + set(what); + } + try + { + result = action.get(); + } + finally + { + set(previous); + } + return result; + } + + public boolean runIfEnabled(Runnable action) + { + synchronized (this) + { + if (get() != initial) return false; + set(!initial); + } + try + { + action.run(); + } + finally + { + set(initial); + } + return true; + } +} diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 6cb83757..a92d0dc7 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -1088,7 +1088,7 @@ public class EntityValue extends Value } List coords = ((ListValue) v).getItems(); double dx = NumericValue.asNumber(coords.get(0)).getDouble(); - double dy = NumericValue.asNumber(coords.get(0)).getDouble(); + double dy = NumericValue.asNumber(coords.get(1)).getDouble(); double dz = NumericValue.asNumber(coords.get(2)).getDouble(); e.setVelocity(dx, dy, dz); updateVelocity(e, MathHelper.absMax(MathHelper.absMax(dx, dy), dz)); diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index 89512ee5..b240173e 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -2,7 +2,6 @@ package carpet.script.value; import carpet.fakes.InventoryBearerInterface; import carpet.script.CarpetContext; -import carpet.script.LazyValue; import carpet.script.exception.InternalExpressionException; import carpet.script.exception.ThrowStatement; import carpet.script.exception.Throwables; @@ -21,16 +20,16 @@ import net.minecraft.command.argument.NbtPathArgumentType; import net.minecraft.entity.Entity; import net.minecraft.entity.InventoryOwner; import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.mob.MobEntity; -import net.minecraft.entity.passive.MerchantEntity; -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.AbstractNbtList; import net.minecraft.nbt.AbstractNbtNumber; import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtInt; import net.minecraft.nbt.NbtList; +import net.minecraft.nbt.NbtLong; +import net.minecraft.nbt.NbtNull; import net.minecraft.nbt.StringNbtReader; import net.minecraft.nbt.NbtString; import net.minecraft.nbt.NbtElement; @@ -326,31 +325,32 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa return slot; } + private static Value decodeSimpleTag(NbtElement t) + { + if (t instanceof AbstractNbtNumber) + { + if (t instanceof NbtLong || t instanceof NbtInt) // short and byte will never exceed float's precision, even int won't + { + return NumericValue.of(((AbstractNbtNumber) t).longValue()); + } + return NumericValue.of(((AbstractNbtNumber) t).numberValue()); + } + if (t instanceof NbtString) + return StringValue.of(t.asString()); + if (t instanceof NbtNull) + return Value.NULL; + + throw new InternalExpressionException("How did we get here: Unknown nbt element class: "+t.getNbtType().getCrashReportName()); + + } + private static Value decodeTag(NbtElement t) { - if (t instanceof NbtCompound) + if (t instanceof NbtCompound || t instanceof AbstractNbtList) return new NBTSerializableValue(() -> t); - if (t instanceof AbstractNbtNumber) - return new NumericValue(((AbstractNbtNumber) t).doubleValue()); - // more can be done here - return new StringValue(t.asString()); + return decodeSimpleTag(t); } - public Value toValue() - { - return decodeTagDeep(this.getTag()); - } - - public static Value fromValue(Value v) - { - if (v instanceof NBTSerializableValue) - return v; - if (v instanceof NullValue) - return Value.NULL; - return NBTSerializableValue.parseString(v.getString(), true); - } - - private static Value decodeTagDeep(NbtElement t) { if (t instanceof NbtCompound) @@ -373,10 +373,21 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa } return ListValue.wrap(elems); } - if (t instanceof AbstractNbtNumber) - return new NumericValue(((AbstractNbtNumber) t).doubleValue()); - // more can be done here - return new StringValue(t.asString()); + return decodeSimpleTag(t); + } + + public Value toValue() + { + return decodeTagDeep(this.getTag()); + } + + public static Value fromValue(Value v) + { + if (v instanceof NBTSerializableValue) + return v; + if (v instanceof NullValue) + return Value.NULL; + return NBTSerializableValue.parseString(v.getString(), true); } public NbtElement getTag()