From 04cd9ce93ece89fc019787dc086d25299708fa14 Mon Sep 17 00:00:00 2001
From: gnembon
Date: Fri, 13 Dec 2019 01:25:08 -0400
Subject: [PATCH 01/14] should that fix mapping issues?
---
.../mixins/BlazeEntityShootFireballGoal_extremeMixin.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/carpet/mixins/BlazeEntityShootFireballGoal_extremeMixin.java b/src/main/java/carpet/mixins/BlazeEntityShootFireballGoal_extremeMixin.java
index 8ec6217d..e0cebe60 100644
--- a/src/main/java/carpet/mixins/BlazeEntityShootFireballGoal_extremeMixin.java
+++ b/src/main/java/carpet/mixins/BlazeEntityShootFireballGoal_extremeMixin.java
@@ -11,7 +11,7 @@ import java.util.Random;
@Mixin(targets = "net.minecraft.entity.mob.BlazeEntity$ShootFireballGoal")
public class BlazeEntityShootFireballGoal_extremeMixin
{
- @Redirect(method = "tick", expect = 2, at = @At(
+ @Redirect(method = "tick()V", expect = 2, at = @At(
value = "INVOKE",
target = "Ljava/util/Random;nextGaussian()D"
))
From 5d32213922734d8897544b9d4ef8e05e9536fcbd Mon Sep 17 00:00:00 2001
From: gnembon
Date: Mon, 16 Dec 2019 08:56:22 -0400
Subject: [PATCH 02/14] threading
---
.../java/carpet/script/CarpetExpression.java | 107 ++++++++++++++----
.../carpet/script/CarpetScriptServer.java | 4 +-
src/main/java/carpet/script/Context.java | 20 ++--
src/main/java/carpet/script/Expression.java | 62 ++++++++++
.../java/carpet/script/value/ListValue.java | 2 +-
.../java/carpet/script/value/ThreadValue.java | 77 +++++++++++--
src/main/java/carpet/script/value/Value.java | 2 +-
7 files changed, 230 insertions(+), 44 deletions(-)
diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java
index 7c972297..e5a27ac0 100644
--- a/src/main/java/carpet/script/CarpetExpression.java
+++ b/src/main/java/carpet/script/CarpetExpression.java
@@ -101,6 +101,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -969,27 +970,83 @@ public class CarpetExpression
if (sourceBlockState == targetBlockState && data == null)
return (c_, t_) -> Value.FALSE;
- CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
+ BlockState finalSourceBlockState = sourceBlockState;
BlockPos targetPos = targetLocator.block.getPos();
- Clearable.clear(world.getBlockEntity(targetPos));
- world.setBlockState(targetPos, sourceBlockState, 2);
-
- if ( data != null)
+ cc.s.getMinecraftServer().executeSync( () ->
{
- BlockEntity be = world.getBlockEntity(targetPos);
- if (be != null)
+ CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
+ Clearable.clear(world.getBlockEntity(targetPos));
+ world.setBlockState(targetPos, finalSourceBlockState, 2);
+ if (data != null)
{
- CompoundTag destTag = data.method_10553();
- destTag.putInt("x", targetPos.getX());
- destTag.putInt("y", targetPos.getY());
- destTag.putInt("z", targetPos.getZ());
- be.fromTag(destTag);
- be.markDirty();
+ BlockEntity be = world.getBlockEntity(targetPos);
+ if (be != null)
+ {
+ CompoundTag destTag = data.method_10553();
+ destTag.putInt("x", targetPos.getX());
+ destTag.putInt("y", targetPos.getY());
+ destTag.putInt("z", targetPos.getZ());
+ be.fromTag(destTag);
+ be.markDirty();
+ }
+ }
+ CarpetSettings.impendingFillSkipUpdates = false;
+ });
+ Value retval = new BlockValue(finalSourceBlockState, world, targetLocator.block.getPos());
+ return (c_, t_) -> retval;
+ });
+
+ this.expr.addLazyFunction("set_async", -1, (c, t, lv) ->
+ {
+ CarpetContext cc = (CarpetContext)c;
+ ServerWorld world = cc.s.getWorld();
+ if (lv.size() < 2 || lv.size() % 2 == 1)
+ throw new InternalExpressionException("'set' should have at least 2 params and odd attributes");
+ BlockValue.LocatorResult targetLocator = BlockValue.fromParams(cc, lv, 0);
+ BlockValue.LocatorResult sourceLocator = BlockValue.fromParams(cc, lv, targetLocator.offset, true);
+ BlockState sourceBlockState = sourceLocator.block.getBlockState();
+ BlockState targetBlockState = world.getBlockState(targetLocator.block.getPos());
+ if (sourceLocator.offset < lv.size())
+ {
+ StateFactory states = sourceBlockState.getBlock().getStateFactory();
+ for (int i = sourceLocator.offset; i < lv.size(); i += 2)
+ {
+ String paramString = lv.get(i).evalValue(c).getString();
+ Property> property = states.getProperty(paramString);
+ if (property == null)
+ throw new InternalExpressionException("Property " + paramString + " doesn't apply to " + sourceLocator.block.getString());
+ String paramValue = lv.get(i + 1).evalValue(c).getString();
+ sourceBlockState = setProperty(property, paramString, paramValue, sourceBlockState);
}
}
- CarpetSettings.impendingFillSkipUpdates = false;
- Value retval = new BlockValue(sourceBlockState, world, targetLocator.block.getPos());
+ CompoundTag data = sourceLocator.block.getData();
+
+ if (sourceBlockState == targetBlockState && data == null)
+ return (c_, t_) -> Value.FALSE;
+ BlockState finalSourceBlockState = sourceBlockState;
+ BlockPos targetPos = targetLocator.block.getPos();
+ cc.s.getMinecraftServer().execute( () ->
+ {
+ CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
+ Clearable.clear(world.getBlockEntity(targetPos));
+ world.setBlockState(targetPos, finalSourceBlockState, 2);
+ if (data != null)
+ {
+ BlockEntity be = world.getBlockEntity(targetPos);
+ if (be != null)
+ {
+ CompoundTag destTag = data.method_10553();
+ destTag.putInt("x", targetPos.getX());
+ destTag.putInt("y", targetPos.getY());
+ destTag.putInt("z", targetPos.getZ());
+ be.fromTag(destTag);
+ be.markDirty();
+ }
+ }
+ CarpetSettings.impendingFillSkipUpdates = false;
+ });
+ Value retval = new BlockValue(finalSourceBlockState, world, targetLocator.block.getPos());
return (c_, t_) -> retval;
});
@@ -2982,6 +3039,7 @@ public class CarpetExpression
this.expr.addLazyFunction("game_tick", -1, (c, t, lv) -> {
ServerCommandSource s = ((CarpetContext)c).s;
+ if (!s.getMinecraftServer().isOnThread()) throw new InternalExpressionException("Unable to run ticks from threads");
((MinecraftServerInterface)s.getMinecraftServer()).forceTick( () -> System.nanoTime()- CarpetServer.scriptServer.tickStart<50000000L);
if (lv.size()>0)
{
@@ -3064,12 +3122,19 @@ public class CarpetExpression
if (lv.size() <= locator.offset)
throw new InternalExpressionException("'plop' needs extra argument indicating what to plop");
String what = lv.get(locator.offset).evalValue(c).getString();
- Boolean res = FeatureGenerator.spawn(what, ((CarpetContext)c).s.getWorld(), locator.block.getPos());
- if (res == null)
- return (c_, t_) -> Value.NULL;
- if (what.equalsIgnoreCase("boulder")) // there might be more of those
- this.forceChunkUpdate(locator.block.getPos(), ((CarpetContext)c).s.getWorld());
- return (c_, t_) -> new NumericValue(res);
+ Value [] result = new Value[]{Value.NULL};
+ ((CarpetContext)c).s.getMinecraftServer().executeSync( () ->
+ {
+
+ Boolean res = FeatureGenerator.spawn(what, ((CarpetContext) c).s.getWorld(), locator.block.getPos());
+ if (res == null)
+ return;
+ if (what.equalsIgnoreCase("boulder")) // there might be more of those
+ this.forceChunkUpdate(locator.block.getPos(), ((CarpetContext) c).s.getWorld());
+ result[0] = new NumericValue(res);
+ });
+ Value ret = result[0]; // preventing from lazy evaluating of the result in case a future completes later
+ return (_c, _t) -> ret;
});
this.expr.addLazyFunction("schedule", -1, (c, t, lv) -> {
diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java
index 47cb273f..1df304c6 100644
--- a/src/main/java/carpet/script/CarpetScriptServer.java
+++ b/src/main/java/carpet/script/CarpetScriptServer.java
@@ -38,9 +38,9 @@ public class CarpetScriptServer
//make static for now, but will change that later:
public CarpetScriptHost globalHost;
public Map modules;
- long tickStart;
+ public long tickStart;
public boolean stopAll;
- Set holyMoly;
+ private Set holyMoly;
public CarpetEventServer events;
public static List bundledModuleData = new ArrayList(){{
diff --git a/src/main/java/carpet/script/Context.java b/src/main/java/carpet/script/Context.java
index fb9d5ff2..092a5697 100644
--- a/src/main/java/carpet/script/Context.java
+++ b/src/main/java/carpet/script/Context.java
@@ -6,16 +6,16 @@ import java.util.Set;
public class Context
{
- static final int NONE = 0;
- static final int VOID = 1;
- static final int BOOLEAN = 2;
- static final int NUMBER = 3;
- static final int STRING = 4;
- static final int LIST = 5;
- static final int ITERATOR = 6;
- static final int SIGNATURE = 7;
- static final int LOCALIZATION = 8;
- static final int LVALUE = 9;
+ public static final int NONE = 0;
+ public static final int VOID = 1;
+ public static final int BOOLEAN = 2;
+ public static final int NUMBER = 3;
+ public static final int STRING = 4;
+ public static final int LIST = 5;
+ public static final int ITERATOR = 6;
+ public static final int SIGNATURE = 7;
+ public static final int LOCALIZATION = 8;
+ public static final int LVALUE = 9;
public Map variables = new HashMap<>();
diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java
index 7b80332f..9af5fa2f 100644
--- a/src/main/java/carpet/script/Expression.java
+++ b/src/main/java/carpet/script/Expression.java
@@ -23,6 +23,7 @@ import carpet.script.value.ListValue;
import carpet.script.value.MapValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
+import carpet.script.value.ThreadValue;
import carpet.script.value.Value;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.apache.commons.lang3.text.WordUtils;
@@ -2992,6 +2993,67 @@ public class Expression implements Cloneable
return (cc, tt) -> retval;
});
+ addLazyFunctionWithDelegation("task", -1, (c, t, expr, tok, lv) -> {
+ Value queue = Value.NULL;
+ Value funcDesc;
+ if (lv.size()==1)
+ {
+ funcDesc = lv.get(0).evalValue(c);
+ }
+ else if (lv.size() == 2)
+ {
+ queue = lv.get(0).evalValue(c);
+ funcDesc = lv.get(1).evalValue(c);
+ }
+ else
+ {
+ throw new InternalExpressionException("'task' takes one or two parameters");
+ }
+ FunctionValue fun;
+ if (!(funcDesc instanceof FunctionValue))
+ {
+ String name = funcDesc.getString();
+ funcDesc = c.host.globalFunctions.get(name);
+ if (funcDesc == null)
+ throw new InternalExpressionException("Function "+name+" is not defined yet");
+ }
+ fun = (FunctionValue)funcDesc;
+ if (fun.getArguments().size() > 0)
+ throw new InternalExpressionException("tasks can only accept functions that don't take any arguments");
+ ThreadValue thread = new ThreadValue(queue, fun, expr, tok, c);
+ Thread.yield();
+ return (cc, tt) -> thread;
+ });
+
+ addFunction("task_count", (lv) ->
+ {
+ if (lv.size() > 0)
+ {
+ return new NumericValue(ThreadValue.taskCount(lv.get(0)));
+ }
+ return new NumericValue(ThreadValue.taskCount());
+ });
+
+ addUnaryFunction("join_task", (v) -> {
+ if (!(v instanceof ThreadValue))
+ throw new InternalExpressionException("'join_task' could only be used with a task value");
+ return ((ThreadValue) v).join();
+ });
+
+ addUnaryFunction("stop_task", (v) -> {
+ if (!(v instanceof ThreadValue))
+ throw new InternalExpressionException("'join_task' could only be used with a task value");
+ ((ThreadValue) v).stop();
+ return Value.NULL;
+ });
+
+ addUnaryFunction("is_task_completed", (v) -> {
+ if (!(v instanceof ThreadValue))
+ throw new InternalExpressionException("'join_task' could only be used with a task value");
+ return new NumericValue(((ThreadValue) v).isFinished());
+ });
+
+
}
static Expression none = new Expression("null");
diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java
index acb657a7..80ef40f7 100644
--- a/src/main/java/carpet/script/value/ListValue.java
+++ b/src/main/java/carpet/script/value/ListValue.java
@@ -246,7 +246,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
return items;
}
- public Iterator iterator() { return new ArrayList<>(items).iterator(); }
+ public Iterator iterator() { return new ArrayList<>(items).iterator(); } // should be thread safe
public void extend(List subList)
{
diff --git a/src/main/java/carpet/script/value/ThreadValue.java b/src/main/java/carpet/script/value/ThreadValue.java
index 3fd07772..382cd6b2 100644
--- a/src/main/java/carpet/script/value/ThreadValue.java
+++ b/src/main/java/carpet/script/value/ThreadValue.java
@@ -1,29 +1,49 @@
package carpet.script.value;
import carpet.script.Context;
-import carpet.script.LazyValue;
+import carpet.script.Expression;
+import carpet.script.Tokenizer;
import carpet.script.exception.InternalExpressionException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.function.Supplier;
public class ThreadValue extends Value
{
- CompletableFuture taskFuture;
+ private CompletableFuture taskFuture;
private long id;
+ private final Object lock;
+ private final boolean[] started;
+ private Suppliertask;
private static long sequence = 0L;
- private static final ExecutorService executorService = Executors.newCachedThreadPool();
+ private static final Map executorServices = new HashMap<>();
- public ThreadValue(LazyValue expression, Context ctx)
+ public ThreadValue(Value pool, FunctionValue function, Expression expr, Tokenizer.Token token, Context ctx)
{
id = sequence++;
+ lock = new Object();
+ started = new boolean[]{false};
+ task = () ->
+ {
+ if (!started[0])
+ {
+ started[0] = true;
+ return function.lazyEval(ctx, Context.NONE, expr, token, Collections.emptyList()).evalValue(ctx);
+ }
+ return null;
+ };
taskFuture = CompletableFuture.supplyAsync(
- () -> expression.evalValue(ctx),
- executorService
+ () -> { synchronized (lock) { return task.get(); } },
+ executorServices.computeIfAbsent(pool, (v) -> (ThreadPoolExecutor)Executors.newCachedThreadPool())
);
+ Thread.yield();
}
@Override
@@ -40,6 +60,18 @@ public class ThreadValue extends Value
public Value join()
{
+ if (!started[0])
+ {
+ synchronized (lock)
+ {
+ if (!started[0])
+ {
+ Value res = task.get();
+ taskFuture.complete(res);
+ return res;
+ }
+ }
+ }
try
{
return taskFuture.get();
@@ -57,7 +89,14 @@ public class ThreadValue extends Value
public void stop()
{
- taskFuture.complete(Value.NULL);
+ synchronized (lock)
+ {
+ if (!taskFuture.isDone())
+ {
+ started[0] = true;
+ taskFuture.complete(Value.NULL);
+ }
+ }
}
@Override
@@ -72,7 +111,27 @@ public class ThreadValue extends Value
public int compareTo(Value o)
{
if (!(o instanceof ThreadValue))
- throw new InternalExpressionException("Cannot compare ");
+ throw new InternalExpressionException("Cannot compare tasks to other types");
return (int) (this.id - ((ThreadValue) o).id);
}
+
+ @Override
+ public int hashCode()
+ {
+ return Long.hashCode(id);
+ }
+
+ public static int taskCount()
+ {
+ return executorServices.values().stream().map(ThreadPoolExecutor::getActiveCount).reduce(0, Integer::sum);
+ }
+
+ public static int taskCount(Value pool)
+ {
+ if (executorServices.containsKey(pool))
+ {
+ return executorServices.get(pool).getActiveCount();
+ }
+ return 0;
+ }
}
diff --git a/src/main/java/carpet/script/value/Value.java b/src/main/java/carpet/script/value/Value.java
index 8faf4c15..3755ca8a 100644
--- a/src/main/java/carpet/script/value/Value.java
+++ b/src/main/java/carpet/script/value/Value.java
@@ -110,7 +110,7 @@ public abstract class Value implements Comparable, Cloneable
@Override
public int compareTo(final Value o)
{
- if (o instanceof NumericValue || o instanceof ListValue)
+ if (o instanceof NumericValue || o instanceof ListValue || o instanceof ThreadValue)
{
return -o.compareTo(this);
}
From 9ba573afe6b92a2aeacb9ad818a5bad640f0b6ed Mon Sep 17 00:00:00 2001
From: gnembon
Date: Tue, 17 Dec 2019 10:28:14 -0400
Subject: [PATCH 03/14] threading testing
---
...nager_scarpetThreadingProtectionMixin.java | 39 +++++++++++++
.../java/carpet/script/CarpetExpression.java | 5 ++
.../java/carpet/script/value/ThreadValue.java | 55 ++++++++++++++-----
src/main/resources/carpet.mixins.json | 1 +
4 files changed, 86 insertions(+), 14 deletions(-)
create mode 100644 src/main/java/carpet/mixins/ServerChunkManager_scarpetThreadingProtectionMixin.java
diff --git a/src/main/java/carpet/mixins/ServerChunkManager_scarpetThreadingProtectionMixin.java b/src/main/java/carpet/mixins/ServerChunkManager_scarpetThreadingProtectionMixin.java
new file mode 100644
index 00000000..897b53b7
--- /dev/null
+++ b/src/main/java/carpet/mixins/ServerChunkManager_scarpetThreadingProtectionMixin.java
@@ -0,0 +1,39 @@
+package carpet.mixins;
+
+import carpet.script.exception.InternalExpressionException;
+import carpet.script.value.ThreadValue;
+import net.minecraft.server.world.ServerChunkManager;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraft.world.chunk.ChunkStatus;
+import org.spongepowered.asm.mixin.Mixin;
+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.CallbackInfoReturnable;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
+
+@Mixin(ServerChunkManager.class)
+public class ServerChunkManager_scarpetThreadingProtectionMixin
+{
+ @Redirect(method = "getChunk(IILnet/minecraft/world/chunk/ChunkStatus;Z)Lnet/minecraft/world/chunk/Chunk;", at = @At(
+ value = "INVOKE",
+ target = "Ljava/util/concurrent/CompletableFuture;join()Ljava/lang/Object;",
+ ordinal = 0
+ ))
+ private Object checkJoiningThreads(CompletableFuture completableFuture)
+ {
+ synchronized (ThreadValue.waitingTreads)
+ {
+ if (ThreadValue.waitingTreads.containsKey(Thread.currentThread()))
+ throw new InternalExpressionException("Cannot join/wait on world accessing thread");
+ ThreadValue.waitingTreads.put(Thread.currentThread(), 1);
+ }
+ Chunk res = completableFuture.join();
+ ThreadValue.waitingTreads.remove(Thread.currentThread());
+ return res;
+
+ }
+}
diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java
index e5a27ac0..8596f64b 100644
--- a/src/main/java/carpet/script/CarpetExpression.java
+++ b/src/main/java/carpet/script/CarpetExpression.java
@@ -944,14 +944,18 @@ public class CarpetExpression
this.expr.addLazyFunction("set", -1, (c, t, lv) ->
{
+ CarpetSettings.LOG.error("starting");
CarpetContext cc = (CarpetContext)c;
ServerWorld world = cc.s.getWorld();
if (lv.size() < 2 || lv.size() % 2 == 1)
throw new InternalExpressionException("'set' should have at least 2 params and odd attributes");
BlockValue.LocatorResult targetLocator = BlockValue.fromParams(cc, lv, 0);
BlockValue.LocatorResult sourceLocator = BlockValue.fromParams(cc, lv, targetLocator.offset, true);
+ CarpetSettings.LOG.error("dafuq");
BlockState sourceBlockState = sourceLocator.block.getBlockState();
+ CarpetSettings.LOG.error("finish dafuq");
BlockState targetBlockState = world.getBlockState(targetLocator.block.getPos());
+ CarpetSettings.LOG.error("finish dafuq 2");
if (sourceLocator.offset < lv.size())
{
StateFactory states = sourceBlockState.getBlock().getStateFactory();
@@ -965,6 +969,7 @@ public class CarpetExpression
sourceBlockState = setProperty(property, paramString, paramValue, sourceBlockState);
}
}
+ CarpetSettings.LOG.error("finish properties got");
CompoundTag data = sourceLocator.block.getData();
diff --git a/src/main/java/carpet/script/value/ThreadValue.java b/src/main/java/carpet/script/value/ThreadValue.java
index 382cd6b2..6c5409bd 100644
--- a/src/main/java/carpet/script/value/ThreadValue.java
+++ b/src/main/java/carpet/script/value/ThreadValue.java
@@ -1,5 +1,6 @@
package carpet.script.value;
+import carpet.CarpetSettings;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.Tokenizer;
@@ -9,6 +10,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
@@ -17,9 +19,13 @@ import java.util.function.Supplier;
public class ThreadValue extends Value
{
private CompletableFuture taskFuture;
+ //public static ThreadLocal joining = ThreadLocal.withInitial(() -> false);
+ public final static Map waitingTreads = new ConcurrentHashMap<>();
+ public static Map joinRequestedThreads = new ConcurrentHashMap<>();
private long id;
private final Object lock;
- private final boolean[] started;
+ //private final boolean[] started;
+ private Thread executingThread;
private Suppliertask;
private static long sequence = 0L;
@@ -29,18 +35,20 @@ public class ThreadValue extends Value
{
id = sequence++;
lock = new Object();
- started = new boolean[]{false};
+ executingThread = null;
+ //started = new boolean[]{false};
task = () ->
{
- if (!started[0])
+ if (executingThread == null)
{
- started[0] = true;
+ CarpetSettings.LOG.error("Setting the thread");
+ executingThread = Thread.currentThread();
return function.lazyEval(ctx, Context.NONE, expr, token, Collections.emptyList()).evalValue(ctx);
}
return null;
};
taskFuture = CompletableFuture.supplyAsync(
- () -> { synchronized (lock) { return task.get(); } },
+ () -> { synchronized (lock) { CarpetSettings.LOG.error("getting in future"); Value ret = task.get(); CarpetSettings.LOG.error("gotten in future"); return ret;} },
executorServices.computeIfAbsent(pool, (v) -> (ThreadPoolExecutor)Executors.newCachedThreadPool())
);
Thread.yield();
@@ -60,26 +68,45 @@ public class ThreadValue extends Value
public Value join()
{
- if (!started[0])
+ CarpetSettings.LOG.error("in join "+id);
+ if (executingThread == null)
{
- synchronized (lock)
+ CarpetSettings.LOG.error("not started "+id);
+ Value res;
+ waitingTreads.put(Thread.currentThread(), 1);
+ try
{
- if (!started[0])
- {
- Value res = task.get();
- taskFuture.complete(res);
- return res;
- }
+ CarpetSettings.LOG.error(" getting result "+id);
+ res = task.get();
+ CarpetSettings.LOG.error(" result got "+id);
}
+ finally
+ {
+ waitingTreads.remove(Thread.currentThread());
+ }
+ taskFuture.complete(res);
+ return res;
}
try
{
+ synchronized (waitingTreads)
+ {
+ if (waitingTreads.containsKey(executingThread))
+ throw new InternalExpressionException("Cannot wait/join on world accessing thread");
+ waitingTreads.put(executingThread, 1);
+ }
+ CarpetSettings.LOG.error("getting future "+id);
return taskFuture.get();
}
catch (InterruptedException | ExecutionException e)
{
return Value.NULL;
}
+ finally
+ {
+ waitingTreads.remove(executingThread);
+ CarpetSettings.LOG.error(" future gotten "+id);
+ }
}
public boolean isFinished()
@@ -93,7 +120,7 @@ public class ThreadValue extends Value
{
if (!taskFuture.isDone())
{
- started[0] = true;
+ executingThread = Thread.currentThread();
taskFuture.complete(Value.NULL);
}
}
diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json
index 5ce2fe8d..90b4e437 100644
--- a/src/main/resources/carpet.mixins.json
+++ b/src/main/resources/carpet.mixins.json
@@ -143,6 +143,7 @@
"FallingBlockEntity_scarpetEventsMixin",
"ChunkTicketManager_scarpetMixin",
"ServerChunkManager_scarpetMixin",
+ "ServerChunkManager_scarpetThreadingProtectionMixin",
"PistonBlock_movableTEMixin",
From 423701d4d7c591baad17191be22f7f49470a0342 Mon Sep 17 00:00:00 2001
From: gnembon
Date: Tue, 17 Dec 2019 11:58:35 -0400
Subject: [PATCH 04/14] Revert "threading testing"
This reverts commit 9ba573af
---
...nager_scarpetThreadingProtectionMixin.java | 39 -------------------
1 file changed, 39 deletions(-)
delete mode 100644 src/main/java/carpet/mixins/ServerChunkManager_scarpetThreadingProtectionMixin.java
diff --git a/src/main/java/carpet/mixins/ServerChunkManager_scarpetThreadingProtectionMixin.java b/src/main/java/carpet/mixins/ServerChunkManager_scarpetThreadingProtectionMixin.java
deleted file mode 100644
index 897b53b7..00000000
--- a/src/main/java/carpet/mixins/ServerChunkManager_scarpetThreadingProtectionMixin.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package carpet.mixins;
-
-import carpet.script.exception.InternalExpressionException;
-import carpet.script.value.ThreadValue;
-import net.minecraft.server.world.ServerChunkManager;
-import net.minecraft.world.chunk.Chunk;
-import net.minecraft.world.chunk.ChunkStatus;
-import org.spongepowered.asm.mixin.Mixin;
-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.CallbackInfoReturnable;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-import java.util.function.Supplier;
-
-@Mixin(ServerChunkManager.class)
-public class ServerChunkManager_scarpetThreadingProtectionMixin
-{
- @Redirect(method = "getChunk(IILnet/minecraft/world/chunk/ChunkStatus;Z)Lnet/minecraft/world/chunk/Chunk;", at = @At(
- value = "INVOKE",
- target = "Ljava/util/concurrent/CompletableFuture;join()Ljava/lang/Object;",
- ordinal = 0
- ))
- private Object checkJoiningThreads(CompletableFuture completableFuture)
- {
- synchronized (ThreadValue.waitingTreads)
- {
- if (ThreadValue.waitingTreads.containsKey(Thread.currentThread()))
- throw new InternalExpressionException("Cannot join/wait on world accessing thread");
- ThreadValue.waitingTreads.put(Thread.currentThread(), 1);
- }
- Chunk res = completableFuture.join();
- ThreadValue.waitingTreads.remove(Thread.currentThread());
- return res;
-
- }
-}
From 3295ca2af98db53691ed9c016bdb1203ead775e0 Mon Sep 17 00:00:00 2001
From: gnembon
Date: Tue, 17 Dec 2019 11:59:23 -0400
Subject: [PATCH 05/14] Revert "threading testing"
This reverts commit 9ba573af
---
.../java/carpet/script/CarpetExpression.java | 5 --
.../java/carpet/script/value/ThreadValue.java | 55 +++++--------------
src/main/resources/carpet.mixins.json | 1 -
3 files changed, 14 insertions(+), 47 deletions(-)
diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java
index 8596f64b..e5a27ac0 100644
--- a/src/main/java/carpet/script/CarpetExpression.java
+++ b/src/main/java/carpet/script/CarpetExpression.java
@@ -944,18 +944,14 @@ public class CarpetExpression
this.expr.addLazyFunction("set", -1, (c, t, lv) ->
{
- CarpetSettings.LOG.error("starting");
CarpetContext cc = (CarpetContext)c;
ServerWorld world = cc.s.getWorld();
if (lv.size() < 2 || lv.size() % 2 == 1)
throw new InternalExpressionException("'set' should have at least 2 params and odd attributes");
BlockValue.LocatorResult targetLocator = BlockValue.fromParams(cc, lv, 0);
BlockValue.LocatorResult sourceLocator = BlockValue.fromParams(cc, lv, targetLocator.offset, true);
- CarpetSettings.LOG.error("dafuq");
BlockState sourceBlockState = sourceLocator.block.getBlockState();
- CarpetSettings.LOG.error("finish dafuq");
BlockState targetBlockState = world.getBlockState(targetLocator.block.getPos());
- CarpetSettings.LOG.error("finish dafuq 2");
if (sourceLocator.offset < lv.size())
{
StateFactory states = sourceBlockState.getBlock().getStateFactory();
@@ -969,7 +965,6 @@ public class CarpetExpression
sourceBlockState = setProperty(property, paramString, paramValue, sourceBlockState);
}
}
- CarpetSettings.LOG.error("finish properties got");
CompoundTag data = sourceLocator.block.getData();
diff --git a/src/main/java/carpet/script/value/ThreadValue.java b/src/main/java/carpet/script/value/ThreadValue.java
index 6c5409bd..382cd6b2 100644
--- a/src/main/java/carpet/script/value/ThreadValue.java
+++ b/src/main/java/carpet/script/value/ThreadValue.java
@@ -1,6 +1,5 @@
package carpet.script.value;
-import carpet.CarpetSettings;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.Tokenizer;
@@ -10,7 +9,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
@@ -19,13 +17,9 @@ import java.util.function.Supplier;
public class ThreadValue extends Value
{
private CompletableFuture taskFuture;
- //public static ThreadLocal joining = ThreadLocal.withInitial(() -> false);
- public final static Map waitingTreads = new ConcurrentHashMap<>();
- public static Map joinRequestedThreads = new ConcurrentHashMap<>();
private long id;
private final Object lock;
- //private final boolean[] started;
- private Thread executingThread;
+ private final boolean[] started;
private Suppliertask;
private static long sequence = 0L;
@@ -35,20 +29,18 @@ public class ThreadValue extends Value
{
id = sequence++;
lock = new Object();
- executingThread = null;
- //started = new boolean[]{false};
+ started = new boolean[]{false};
task = () ->
{
- if (executingThread == null)
+ if (!started[0])
{
- CarpetSettings.LOG.error("Setting the thread");
- executingThread = Thread.currentThread();
+ started[0] = true;
return function.lazyEval(ctx, Context.NONE, expr, token, Collections.emptyList()).evalValue(ctx);
}
return null;
};
taskFuture = CompletableFuture.supplyAsync(
- () -> { synchronized (lock) { CarpetSettings.LOG.error("getting in future"); Value ret = task.get(); CarpetSettings.LOG.error("gotten in future"); return ret;} },
+ () -> { synchronized (lock) { return task.get(); } },
executorServices.computeIfAbsent(pool, (v) -> (ThreadPoolExecutor)Executors.newCachedThreadPool())
);
Thread.yield();
@@ -68,45 +60,26 @@ public class ThreadValue extends Value
public Value join()
{
- CarpetSettings.LOG.error("in join "+id);
- if (executingThread == null)
+ if (!started[0])
{
- CarpetSettings.LOG.error("not started "+id);
- Value res;
- waitingTreads.put(Thread.currentThread(), 1);
- try
+ synchronized (lock)
{
- CarpetSettings.LOG.error(" getting result "+id);
- res = task.get();
- CarpetSettings.LOG.error(" result got "+id);
+ if (!started[0])
+ {
+ Value res = task.get();
+ taskFuture.complete(res);
+ return res;
+ }
}
- finally
- {
- waitingTreads.remove(Thread.currentThread());
- }
- taskFuture.complete(res);
- return res;
}
try
{
- synchronized (waitingTreads)
- {
- if (waitingTreads.containsKey(executingThread))
- throw new InternalExpressionException("Cannot wait/join on world accessing thread");
- waitingTreads.put(executingThread, 1);
- }
- CarpetSettings.LOG.error("getting future "+id);
return taskFuture.get();
}
catch (InterruptedException | ExecutionException e)
{
return Value.NULL;
}
- finally
- {
- waitingTreads.remove(executingThread);
- CarpetSettings.LOG.error(" future gotten "+id);
- }
}
public boolean isFinished()
@@ -120,7 +93,7 @@ public class ThreadValue extends Value
{
if (!taskFuture.isDone())
{
- executingThread = Thread.currentThread();
+ started[0] = true;
taskFuture.complete(Value.NULL);
}
}
diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json
index 90b4e437..5ce2fe8d 100644
--- a/src/main/resources/carpet.mixins.json
+++ b/src/main/resources/carpet.mixins.json
@@ -143,7 +143,6 @@
"FallingBlockEntity_scarpetEventsMixin",
"ChunkTicketManager_scarpetMixin",
"ServerChunkManager_scarpetMixin",
- "ServerChunkManager_scarpetThreadingProtectionMixin",
"PistonBlock_movableTEMixin",
From 2ff7988251716c7e748e7754fed6b2f706fd558f Mon Sep 17 00:00:00 2001
From: gnembon
Date: Tue, 17 Dec 2019 14:41:47 -0400
Subject: [PATCH 06/14] resolved joins from main thread as illegal
---
.../java/carpet/script/CarpetExpression.java | 15 +++++++--
src/main/java/carpet/script/Expression.java | 12 +++++--
.../java/carpet/script/value/ThreadValue.java | 33 +++----------------
3 files changed, 26 insertions(+), 34 deletions(-)
diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java
index e5a27ac0..ea198f30 100644
--- a/src/main/java/carpet/script/CarpetExpression.java
+++ b/src/main/java/carpet/script/CarpetExpression.java
@@ -10,6 +10,7 @@ import carpet.script.exception.CarpetExpressionException;
import carpet.script.exception.ExitStatement;
import carpet.script.exception.ExpressionException;
import carpet.script.exception.InternalExpressionException;
+import carpet.CarpetSettings;
import carpet.script.value.BlockValue;
import carpet.script.value.EntityValue;
import carpet.script.value.FunctionValue;
@@ -19,8 +20,8 @@ import carpet.script.value.NBTSerializableValue;
import carpet.script.value.NullValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
+import carpet.script.value.ThreadValue;
import carpet.script.value.Value;
-import carpet.CarpetSettings;
import carpet.utils.BlockInfo;
import carpet.utils.Messenger;
import com.google.common.collect.Sets;
@@ -101,7 +102,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -3006,6 +3006,17 @@ public class CarpetExpression
return (_c, _t) -> res; // pass through for variables
});
+ //"overidden" native call to cancel if on main thread
+ this.expr.addLazyFunction("join_task", 1, (c, t, lv) -> {
+ if (((CarpetContext)c).s.getMinecraftServer().isOnThread())
+ throw new InternalExpressionException("'join_task' cannot be called from main thread to avoid deadlocks");
+ Value v = lv.get(0).evalValue(c);
+ if (!(v instanceof ThreadValue))
+ throw new InternalExpressionException("'join_task' could only be used with a task value");
+ Value ret = ((ThreadValue) v).join();
+ return (_c, _t) -> ret;
+ });
+
this.expr.addLazyFunction("logger", 1, (c, t, lv) ->
{
Value res = lv.get(0).evalValue(c);
diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java
index 9af5fa2f..facb3504 100644
--- a/src/main/java/carpet/script/Expression.java
+++ b/src/main/java/carpet/script/Expression.java
@@ -11,7 +11,13 @@ import carpet.script.Fluff.QuadFunction;
import carpet.script.Fluff.QuinnFunction;
import carpet.script.Fluff.SexFunction;
import carpet.script.Fluff.TriFunction;
-import carpet.script.exception.*;
+import carpet.script.exception.BreakStatement;
+import carpet.script.exception.ContinueStatement;
+import carpet.script.exception.ExitStatement;
+import carpet.script.exception.ExpressionException;
+import carpet.script.exception.InternalExpressionException;
+import carpet.script.exception.ReturnStatement;
+import carpet.script.exception.ThrowStatement;
import carpet.script.value.AbstractListValue;
import carpet.script.value.ContainerValueInterface;
import carpet.script.value.FunctionSignatureValue;
@@ -3040,12 +3046,12 @@ public class Expression implements Cloneable
return ((ThreadValue) v).join();
});
- addUnaryFunction("stop_task", (v) -> {
+ /*addUnaryFunction("stop_task", (v) -> {
if (!(v instanceof ThreadValue))
throw new InternalExpressionException("'join_task' could only be used with a task value");
((ThreadValue) v).stop();
return Value.NULL;
- });
+ });*/
addUnaryFunction("is_task_completed", (v) -> {
if (!(v instanceof ThreadValue))
diff --git a/src/main/java/carpet/script/value/ThreadValue.java b/src/main/java/carpet/script/value/ThreadValue.java
index 382cd6b2..667e087a 100644
--- a/src/main/java/carpet/script/value/ThreadValue.java
+++ b/src/main/java/carpet/script/value/ThreadValue.java
@@ -12,36 +12,24 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
-import java.util.function.Supplier;
public class ThreadValue extends Value
{
private CompletableFuture taskFuture;
private long id;
private final Object lock;
- private final boolean[] started;
- private Suppliertask;
private static long sequence = 0L;
-
private static final Map executorServices = new HashMap<>();
public ThreadValue(Value pool, FunctionValue function, Expression expr, Tokenizer.Token token, Context ctx)
{
id = sequence++;
lock = new Object();
- started = new boolean[]{false};
- task = () ->
- {
- if (!started[0])
- {
- started[0] = true;
- return function.lazyEval(ctx, Context.NONE, expr, token, Collections.emptyList()).evalValue(ctx);
- }
- return null;
- };
taskFuture = CompletableFuture.supplyAsync(
- () -> { synchronized (lock) { return task.get(); } },
- executorServices.computeIfAbsent(pool, (v) -> (ThreadPoolExecutor)Executors.newCachedThreadPool())
+ () -> { synchronized (lock) {
+ return function.lazyEval(ctx, Context.NONE, expr, token, Collections.emptyList()).evalValue(ctx);
+ } },
+ executorServices.computeIfAbsent(pool, (v) -> (ThreadPoolExecutor)Executors.newCachedThreadPool())
);
Thread.yield();
}
@@ -60,18 +48,6 @@ public class ThreadValue extends Value
public Value join()
{
- if (!started[0])
- {
- synchronized (lock)
- {
- if (!started[0])
- {
- Value res = task.get();
- taskFuture.complete(res);
- return res;
- }
- }
- }
try
{
return taskFuture.get();
@@ -93,7 +69,6 @@ public class ThreadValue extends Value
{
if (!taskFuture.isDone())
{
- started[0] = true;
taskFuture.complete(Value.NULL);
}
}
From 9457ce741c55bb2eee737c459242d9fe3dc0fa23 Mon Sep 17 00:00:00 2001
From: gnembon
Date: Wed, 18 Dec 2019 10:41:03 -0400
Subject: [PATCH 07/14] fixed te fillUpdates and client call fix. better camera
---
.../mixins/WorldChunk_fillUpdatesMixin.java | 10 -----
.../java/carpet/script/value/EntityValue.java | 2 +-
.../resources/assets/carpet/scripts/camera.sc | 45 ++++++++++---------
3 files changed, 24 insertions(+), 33 deletions(-)
diff --git a/src/main/java/carpet/mixins/WorldChunk_fillUpdatesMixin.java b/src/main/java/carpet/mixins/WorldChunk_fillUpdatesMixin.java
index b780dd36..10eb373e 100644
--- a/src/main/java/carpet/mixins/WorldChunk_fillUpdatesMixin.java
+++ b/src/main/java/carpet/mixins/WorldChunk_fillUpdatesMixin.java
@@ -21,14 +21,4 @@ public class WorldChunk_fillUpdatesMixin
if (!CarpetSettings.impendingFillSkipUpdates)
blockState.onBlockAdded(world_1, blockPos_1, blockState_1, boolean_1);
}
-
- @Redirect(method = "setBlockState", at = @At(
- value = "INVOKE",
- target = "Lnet/minecraft/block/BlockState;onBlockRemoved(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Z)V"
- ))
- private void onRemoved(BlockState blockState, World world_1, BlockPos blockPos_1, BlockState blockState_1, boolean boolean_1)
- {
- if (!CarpetSettings.impendingFillSkipUpdates)
- blockState.onBlockRemoved(world_1, blockPos_1, blockState_1, boolean_1);
- }
}
diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java
index 9c29cb09..e1aacde9 100644
--- a/src/main/java/carpet/script/value/EntityValue.java
+++ b/src/main/java/carpet/script/value/EntityValue.java
@@ -223,7 +223,7 @@ public class EntityValue extends Value
put("dimension", (e, a) -> new StringValue(nameFromRegistryId(Registry.DIMENSION.getId(e.dimension))));
put("height", (e, a) -> new NumericValue(e.getDimensions(EntityPose.STANDING).height));
put("width", (e, a) -> new NumericValue(e.getDimensions(EntityPose.STANDING).width));
- put("eye_height", (e, a) -> new NumericValue(e.getEyeHeight(EntityPose.STANDING)));
+ put("eye_height", (e, a) -> new NumericValue(e.getStandingEyeHeight()));
put("age", (e, a) -> new NumericValue(e.age));
put("despawn_timer", (e, a) -> e instanceof LivingEntity?new NumericValue(((LivingEntity) e).getDespawnCounter()):Value.NULL);
put("item", (e, a) -> (e instanceof ItemEntity)?ListValue.fromItemStack(((ItemEntity) e).getStack()):Value.NULL);
diff --git a/src/main/resources/assets/carpet/scripts/camera.sc b/src/main/resources/assets/carpet/scripts/camera.sc
index 960ac306..f06c4948 100644
--- a/src/main/resources/assets/carpet/scripts/camera.sc
+++ b/src/main/resources/assets/carpet/scripts/camera.sc
@@ -153,28 +153,29 @@ play(fps) ->
p = player();
__assert_valid_for_motion();
__prepare_path_if_needed();
- if ((fps % 20 != 0) || fps < 20, exit('FPS needs to be multiples of 20') );
- tpt = round(fps / 20);
- mspt = 50 / tpt;
- start_time = time();
- point = 0;
- loop( length(global_points)-1,
- segment = _;
- start = global_points:segment:1;
- end = global_points:(segment+1):1;
- loop(end-start,
- v = __get_path_at(segment, start, _);
- modify(p, 'location', v);
- point += 1;
- if ((point % tpt == 0), game_tick());
- end_time = time();
- took = end_time - start_time;
- if (took < mspt, sleep(mspt-took));
- start_time = time()
- )
- );
- game_tick(1000);
- 'Done!'
+ task( _(outer(fps), outer(p)) -> (
+ //if ((fps % 20 != 0) || fps < 20, exit('FPS needs to be multiples of 20') );
+ tpt = fps / 20;
+ mspt = 50 / tpt;
+ start_time = time();
+ point = 0;
+ loop( length(global_points)-1,
+ segment = _;
+ start = global_points:segment:1;
+ end = global_points:(segment+1):1;
+ loop(end-start,
+ v = __get_path_at(segment, start, _);
+ modify(p, 'location', v);
+ point += 1;
+ end_time = time();
+ took = end_time - start_time;
+ if (took < mspt, sleep(mspt-took));
+ start_time = time()
+ )
+ );
+ 'Done!'
+ ));
+
);
_show_path_tick(particle_type, total) ->
(
From 0e9566a58d73b55bf5a5f93a008bd749734b6379 Mon Sep 17 00:00:00 2001
From: gnembon
Date: Thu, 19 Dec 2019 01:48:33 -0400
Subject: [PATCH 08/14] removed set_async as pointless
---
.../java/carpet/script/CarpetExpression.java | 54 -------------------
1 file changed, 54 deletions(-)
diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java
index ea198f30..a66a9911 100644
--- a/src/main/java/carpet/script/CarpetExpression.java
+++ b/src/main/java/carpet/script/CarpetExpression.java
@@ -996,60 +996,6 @@ public class CarpetExpression
return (c_, t_) -> retval;
});
- this.expr.addLazyFunction("set_async", -1, (c, t, lv) ->
- {
- CarpetContext cc = (CarpetContext)c;
- ServerWorld world = cc.s.getWorld();
- if (lv.size() < 2 || lv.size() % 2 == 1)
- throw new InternalExpressionException("'set' should have at least 2 params and odd attributes");
- BlockValue.LocatorResult targetLocator = BlockValue.fromParams(cc, lv, 0);
- BlockValue.LocatorResult sourceLocator = BlockValue.fromParams(cc, lv, targetLocator.offset, true);
- BlockState sourceBlockState = sourceLocator.block.getBlockState();
- BlockState targetBlockState = world.getBlockState(targetLocator.block.getPos());
- if (sourceLocator.offset < lv.size())
- {
- StateFactory states = sourceBlockState.getBlock().getStateFactory();
- for (int i = sourceLocator.offset; i < lv.size(); i += 2)
- {
- String paramString = lv.get(i).evalValue(c).getString();
- Property> property = states.getProperty(paramString);
- if (property == null)
- throw new InternalExpressionException("Property " + paramString + " doesn't apply to " + sourceLocator.block.getString());
- String paramValue = lv.get(i + 1).evalValue(c).getString();
- sourceBlockState = setProperty(property, paramString, paramValue, sourceBlockState);
- }
- }
-
- CompoundTag data = sourceLocator.block.getData();
-
- if (sourceBlockState == targetBlockState && data == null)
- return (c_, t_) -> Value.FALSE;
- BlockState finalSourceBlockState = sourceBlockState;
- BlockPos targetPos = targetLocator.block.getPos();
- cc.s.getMinecraftServer().execute( () ->
- {
- CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
- Clearable.clear(world.getBlockEntity(targetPos));
- world.setBlockState(targetPos, finalSourceBlockState, 2);
- if (data != null)
- {
- BlockEntity be = world.getBlockEntity(targetPos);
- if (be != null)
- {
- CompoundTag destTag = data.method_10553();
- destTag.putInt("x", targetPos.getX());
- destTag.putInt("y", targetPos.getY());
- destTag.putInt("z", targetPos.getZ());
- be.fromTag(destTag);
- be.markDirty();
- }
- }
- CarpetSettings.impendingFillSkipUpdates = false;
- });
- Value retval = new BlockValue(finalSourceBlockState, world, targetLocator.block.getPos());
- return (c_, t_) -> retval;
- });
-
this.expr.addLazyFunction("destroy", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
From 54308bb131bbda4426c6a645390d85212c353531 Mon Sep 17 00:00:00 2001
From: gnembon
Date: Thu, 19 Dec 2019 01:49:00 -0400
Subject: [PATCH 09/14] camera using threading
---
.../resources/assets/carpet/scripts/camera.sc | 75 ++++++++++++-------
1 file changed, 49 insertions(+), 26 deletions(-)
diff --git a/src/main/resources/assets/carpet/scripts/camera.sc b/src/main/resources/assets/carpet/scripts/camera.sc
index f06c4948..1aaeb742 100644
--- a/src/main/resources/assets/carpet/scripts/camera.sc
+++ b/src/main/resources/assets/carpet/scripts/camera.sc
@@ -140,42 +140,65 @@ __get_path_at(segment, start, index) ->
v
);
__invalidate_points_cache() -> global_path_precalculated = map(range(global_points:(-1):1), null);
+
+global_showing_path = false;
+global_playing_path = false;
+
+stop() ->
+(
+ if (!global_playing_path && !global_showing_path, return('You can only stop playing or showing the path'));
+ if (global_showing_path,
+ global_showing_path = false;
+ print('stopped showing path');
+ );
+ if (global_playing_path,
+ global_playing_path = false;
+ print('stopped playing path');
+ );
+ ''
+);
+
show() ->
(
- loop(100,
- _show_path_tick('dust 0.9 0.1 0.1 1', 100);
- game_tick(50)
- );
- 'Done!'
+ global_showing_path = true;
+ task( _() -> (
+ loop(100,
+ if(!global_showing_path, break());
+ _show_path_tick('dust 0.9 0.1 0.1 1', 100);
+ sleep(50);
+ );
+ print('Done!');
+ ));
+ '';
);
+
play(fps) ->
(
p = player();
__assert_valid_for_motion();
__prepare_path_if_needed();
+ global_playing_path = true;
task( _(outer(fps), outer(p)) -> (
- //if ((fps % 20 != 0) || fps < 20, exit('FPS needs to be multiples of 20') );
- tpt = fps / 20;
- mspt = 50 / tpt;
- start_time = time();
- point = 0;
- loop( length(global_points)-1,
- segment = _;
- start = global_points:segment:1;
- end = global_points:(segment+1):1;
- loop(end-start,
- v = __get_path_at(segment, start, _);
- modify(p, 'location', v);
- point += 1;
- end_time = time();
- took = end_time - start_time;
- if (took < mspt, sleep(mspt-took));
- start_time = time()
- )
- );
- 'Done!'
+ mspt = 1000 / fps;
+ start_time = time();
+ point = 0;
+ loop( length(global_points)-1, segment = _;
+ if (!global_playing_path, break());
+ start = global_points:segment:1;
+ end = global_points:(segment+1):1;
+ loop(end-start,
+ v = __get_path_at(segment, start, _);
+ modify(p, 'location', v);
+ point += 1;
+ end_time = time();
+ took = end_time - start_time;
+ if (took < mspt, sleep(mspt-took));
+ start_time = time()
+ )
+ );
+ print('Done!');
));
-
+ '';
);
_show_path_tick(particle_type, total) ->
(
From 8c95aab2c62fc645847d9c8cc0da8a1ead2c67b2 Mon Sep 17 00:00:00 2001
From: gnembon
Date: Thu, 19 Dec 2019 18:11:13 -0400
Subject: [PATCH 10/14] scarpet thread safe pt1
---
.../java/carpet/script/CarpetExpression.java | 4 +-
.../java/carpet/script/value/BlockValue.java | 12 ++++-
.../java/carpet/script/value/EntityValue.java | 16 ++++++
.../script/value/NBTSerializableValue.java | 51 +++++++++++++++++--
4 files changed, 77 insertions(+), 6 deletions(-)
diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java
index a66a9911..c5cbe225 100644
--- a/src/main/java/carpet/script/CarpetExpression.java
+++ b/src/main/java/carpet/script/CarpetExpression.java
@@ -127,7 +127,7 @@ import static java.lang.Math.sqrt;
* players using player('*'), which only returns players in current dimension, or use
* in_dimension(expr) function.
*/
-public class CarpetExpression
+public class CarpetExpression
{
private ServerCommandSource source;
@@ -771,7 +771,7 @@ public class CarpetExpression
booleanStateTest(c, "liquid", lv, (s, p) -> !s.getFluidState().isEmpty()));
this.expr.addLazyFunction("flammable", -1, (c, t, lv) ->
- booleanStateTest(c, "flammable", lv, (s, p) -> !s.getMaterial().isBurnable()));
+ booleanStateTest(c, "flammable", lv, (s, p) -> s.getMaterial().isBurnable()));
this.expr.addLazyFunction("transparent", -1, (c, t, lv) ->
booleanStateTest(c, "transparent", lv, (s, p) -> !s.getMaterial().isSolid()));
diff --git a/src/main/java/carpet/script/value/BlockValue.java b/src/main/java/carpet/script/value/BlockValue.java
index 485aa5e8..9a460338 100644
--- a/src/main/java/carpet/script/value/BlockValue.java
+++ b/src/main/java/carpet/script/value/BlockValue.java
@@ -21,6 +21,7 @@ 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.chunk.WorldChunk;
import java.util.Arrays;
import java.util.HashMap;
@@ -192,6 +193,15 @@ public class BlockValue extends Value
throw new InternalExpressionException("Attempted to fetch block state without world or stored block state");
}
+ public static BlockEntity getBlockEntity(ServerWorld world, BlockPos pos)
+ {
+ if (world.getServer().isOnThread())
+ return world.getBlockEntity(pos);
+ else
+ return world.getWorldChunk(pos).getBlockEntity(pos, WorldChunk.CreationType.IMMEDIATE);
+ }
+
+
public CompoundTag getData()
{
if (data != null)
@@ -202,7 +212,7 @@ public class BlockValue extends Value
}
if (world != null && pos != null)
{
- BlockEntity be = world.getBlockEntity(pos);
+ BlockEntity be = getBlockEntity(world, pos);
CompoundTag tag = new CompoundTag();
if (be == null)
{
diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java
index e1aacde9..61760579 100644
--- a/src/main/java/carpet/script/value/EntityValue.java
+++ b/src/main/java/carpet/script/value/EntityValue.java
@@ -273,6 +273,22 @@ public class EntityValue extends Value
}
return Value.NULL;
});
+
+ put("permission_level", (e, a) -> {
+ if (e instanceof ServerPlayerEntity)
+ {
+ ServerPlayerEntity spe = (ServerPlayerEntity) e;
+ for (int i=4; i>=0; i--)
+ {
+ if (spe.allowsPermissionLevel(i))
+ return new NumericValue(i);
+
+ }
+ return new NumericValue(0);
+ }
+ return Value.NULL;
+ });
+
//spectating_entity
// isGlowing
put("effect", (e, a) ->
diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java
index 40b058ea..7cbeaec9 100644
--- a/src/main/java/carpet/script/value/NBTSerializableValue.java
+++ b/src/main/java/carpet/script/value/NBTSerializableValue.java
@@ -5,6 +5,12 @@ import carpet.script.LazyValue;
import carpet.script.exception.InternalExpressionException;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.block.ChestBlock;
+import net.minecraft.block.InventoryProvider;
+import net.minecraft.block.entity.BlockEntity;
+import net.minecraft.block.entity.ChestBlockEntity;
import net.minecraft.block.entity.HopperBlockEntity;
import net.minecraft.command.arguments.ItemStackArgument;
import net.minecraft.command.arguments.ItemStringReader;
@@ -21,8 +27,12 @@ import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.StringNbtReader;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
+import net.minecraft.predicate.entity.EntityPredicates;
+import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Box;
+import net.minecraft.world.World;
import java.util.Collection;
import java.util.HashMap;
@@ -116,6 +126,41 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
return copy;
}
+ // stolen from HopperBlockEntity, adjusted for threaded operation
+ public static Inventory getInventoryAt(ServerWorld world, BlockPos blockPos)
+ {
+ Inventory inventory = null;
+ BlockState blockState = world.getBlockState(blockPos);
+ Block block = blockState.getBlock();
+ if (block instanceof InventoryProvider) {
+ inventory = ((InventoryProvider)block).getInventory(blockState, world, blockPos);
+ } else if (block.hasBlockEntity()) {
+ BlockEntity blockEntity = BlockValue.getBlockEntity(world, blockPos);
+ if (blockEntity instanceof Inventory) {
+ inventory = (Inventory)blockEntity;
+ if (inventory instanceof ChestBlockEntity && block instanceof ChestBlock) {
+ inventory = ChestBlock.getInventory(blockState, world, blockPos, true);
+ }
+ }
+ }
+
+ if (inventory == null) {
+ List list = world.getEntities(
+ (Entity)null,
+ new Box(
+ blockPos.getX() - 0.5D, blockPos.getY() - 0.5D, blockPos.getZ() - 0.5D,
+ blockPos.getX() + 0.5D, blockPos.getY() + 0.5D, blockPos.getZ() + 0.5D),
+ EntityPredicates.VALID_INVENTORIES
+ );
+ if (!list.isEmpty()) {
+ inventory = (Inventory)list.get(world.random.nextInt(list.size()));
+ }
+ }
+
+ return inventory;
+ }
+
+
public static InventoryLocator locateInventory(CarpetContext c, List params, int offset)
{
try
@@ -139,7 +184,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
BlockPos pos = ((BlockValue) v1).getPos();
if (pos == null)
throw new InternalExpressionException("Block to access inventory needs to be positioned in the world");
- inv = HopperBlockEntity.getInventoryAt(c.s.getWorld(), pos);
+ inv = getInventoryAt(c.s.getWorld(), pos);
if (inv == null)
return null;
return new InventoryLocator(pos, pos, inv, offset + 1);
@@ -151,7 +196,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
NumericValue.asNumber(args.get(0)).getDouble(),
NumericValue.asNumber(args.get(1)).getDouble(),
NumericValue.asNumber(args.get(2)).getDouble());
- inv = HopperBlockEntity.getInventoryAt(c.s.getWorld(), pos);
+ inv = getInventoryAt(c.s.getWorld(), pos);
if (inv == null)
return null;
return new InventoryLocator(pos, pos, inv, offset + 1);
@@ -160,7 +205,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
NumericValue.asNumber(v1).getDouble(),
NumericValue.asNumber(params.get(1 + offset).evalValue(c)).getDouble(),
NumericValue.asNumber(params.get(2 + offset).evalValue(c)).getDouble());
- inv = HopperBlockEntity.getInventoryAt(c.s.getWorld(), pos);
+ inv = getInventoryAt(c.s.getWorld(), pos);
if (inv == null)
return null;
return new InventoryLocator(pos, pos, inv, offset + 3);
From 1dc2308d2ece40ea114645494b9630f951c30b8b Mon Sep 17 00:00:00 2001
From: gnembon
Date: Fri, 20 Dec 2019 19:02:33 -0400
Subject: [PATCH 11/14] camera threaded WIP
---
.../resources/assets/carpet/scripts/camera.sc | 58 ++++++++++++++++---
1 file changed, 50 insertions(+), 8 deletions(-)
diff --git a/src/main/resources/assets/carpet/scripts/camera.sc b/src/main/resources/assets/carpet/scripts/camera.sc
index 1aaeb742..4c61f8e9 100644
--- a/src/main/resources/assets/carpet/scripts/camera.sc
+++ b/src/main/resources/assets/carpet/scripts/camera.sc
@@ -69,9 +69,15 @@ __adjusted_rot(previous_rot, current_rot) ->
current_rot
);
+__print_path_size() ->
+(
+ __assert_valid_for_motion();
+ print("Path length: %d points, 0.2f secs at 60fps", length(global_points), global_points:(-1):1/60);
+);
repeat(times, last_section_duration) ->
(
+ __assert_valid_for_motion();
undef('global_path_precalculated');
positions = map(global_points, _:0);
modes = map(global_points, _:(-1));
@@ -82,10 +88,12 @@ repeat(times, last_section_duration) ->
__add_path_segment(positions:_, durations:_, modes:_)
)
);
- str('Add %d points %d times', length(positions), times)
+ __print_path_size();
+ '';
);
speed(percentage) ->
(
+ __assert_valid_for_motion();
undef('global_path_precalculated');
if (percentage < 25 || percentage > 400,
exit('path speed can only be speed, or slowed down 4 times. Recall command for larger changes')
@@ -94,10 +102,11 @@ speed(percentage) ->
previous_path_length = global_points:(-1):1;
for(global_points, _:1 = _:1*ratio );
undef('global_path_precalculated');
- str('path %s from %d to %d ticks',
+ str('path %s from %d to %d ticks (%.2f seconds with 60fps)',
if(ratio<1,'shortened','extended'),
previous_path_length,
- global_points:(-1):1
+ global_points:(-1):1,
+ global_points:(-1):1/60
)
);
select_interpolation(method) ->
@@ -158,15 +167,45 @@ stop() ->
''
);
+clear() ->
+(
+ global_points = l();
+ undef('global_path_precalculated');
+);
+
+global_needs_updating = false;
+global_selected_point = null;
+
show() ->
(
+ p = player();
+ __assert_valid_for_motion();
+ __print_path_size();
global_showing_path = true;
- task( _() -> (
- loop(100,
- if(!global_showing_path, break());
- _show_path_tick('dust 0.9 0.1 0.1 1', 100);
- sleep(50);
+ global_needs_updating= false;
+ def __create_markers()->
+ (
+ map(global_points,
+ m = create_marker(null, _:0, 'observer');
+ if (global_selected_point && _i == global_selected_point,
+ modify(m,'effect','glowing',72000);
+ );
+ m
);
+ );
+ task( _(outer(p)) -> (
+ markers = __create_markers();
+ loop(7200,
+ if(!global_showing_path, break());
+ if (global_needs_updating,
+ global_needs_updating = false;
+ for(markers, modify(_,'remove');
+ markers = __create_markers();
+ );
+ _show_path_tick('dust 0.9 0.1 0.1 1', 100);
+ sleep(100);
+ );
+ for(markers, modify(_,'remove');
print('Done!');
));
'';
@@ -179,10 +218,12 @@ play(fps) ->
__prepare_path_if_needed();
global_playing_path = true;
task( _(outer(fps), outer(p)) -> (
+ sleep(1000);
mspt = 1000 / fps;
start_time = time();
point = 0;
loop( length(global_points)-1, segment = _;
+ if (p~'sneaking', global_playing_path = false);
if (!global_playing_path, break());
start = global_points:segment:1;
end = global_points:(segment+1):1;
@@ -196,6 +237,7 @@ play(fps) ->
start_time = time()
)
);
+ sleep(1000);
print('Done!');
));
'';
From 62529a352ce1b36a4f478c2612b808ed322f8790 Mon Sep 17 00:00:00 2001
From: gnembon
Date: Sun, 22 Dec 2019 02:26:40 -0400
Subject: [PATCH 12/14] camera update
---
.../java/carpet/script/CarpetExpression.java | 15 +-
src/main/java/carpet/script/Expression.java | 16 +-
.../java/carpet/script/value/BlockValue.java | 11 +-
.../java/carpet/script/value/EntityValue.java | 33 ++
.../resources/assets/carpet/scripts/camera.sc | 357 ++++++++++++------
5 files changed, 312 insertions(+), 120 deletions(-)
diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java
index c5cbe225..95fe343c 100644
--- a/src/main/java/carpet/script/CarpetExpression.java
+++ b/src/main/java/carpet/script/CarpetExpression.java
@@ -106,7 +106,6 @@ import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;
-import static carpet.script.value.NBTSerializableValue.nameFromRegistryId;
import static java.lang.Math.abs;
import static java.lang.Math.max;
import static java.lang.Math.min;
@@ -2865,6 +2864,7 @@ public class CarpetExpression
CarpetContext cc = (CarpetContext)c;
BlockState targetBlock = null;
BlockValue.VectorLocator pointLocator;
+ boolean interactable = true;
String name;
try
{
@@ -2872,8 +2872,14 @@ public class CarpetExpression
name = nameValue instanceof NullValue ? "" : nameValue.getString();
pointLocator = BlockValue.locateVec(cc, lv, 1, true);
if (lv.size()>pointLocator.offset)
- targetBlock = BlockValue.fromParams(cc, lv, pointLocator.offset, true).block.getBlockState();
-
+ {
+ BlockValue.LocatorResult blockLocator = BlockValue.fromParams(cc, lv, pointLocator.offset, true, true);
+ if (blockLocator.block != null) targetBlock = blockLocator.block.getBlockState();
+ if (lv.size() > blockLocator.offset)
+ {
+ interactable = lv.get(blockLocator.offset).evalValue(c, Context.BOOLEAN).getBoolean();
+ }
+ }
}
catch (IndexOutOfBoundsException e)
{
@@ -2901,6 +2907,7 @@ public class CarpetExpression
armorstand.setNoGravity(true);
armorstand.setInvisible(true);
armorstand.setInvulnerable(true);
+ armorstand.getDataTracker().set(ArmorStandEntity.ARMOR_STAND_FLAGS, (byte)(interactable?8 : 16|8));
cc.s.getWorld().spawnEntity(armorstand);
EntityValue result = new EntityValue(armorstand);
return (_c, _t) -> result;
@@ -3023,7 +3030,7 @@ public class CarpetExpression
this.expr.addLazyFunction("current_dimension", 0, (c, t, lv) -> {
ServerCommandSource s = ((CarpetContext)c).s;
- Value retval = new StringValue(nameFromRegistryId(Registry.DIMENSION.getId(s.getWorld().dimension.getType())));
+ Value retval = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.DIMENSION.getId(s.getWorld().dimension.getType())));
return (cc, tt) -> retval;
});
diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java
index facb3504..4677800f 100644
--- a/src/main/java/carpet/script/Expression.java
+++ b/src/main/java/carpet/script/Expression.java
@@ -1198,16 +1198,14 @@ public class Expression implements Cloneable
{
Value v1 = lv1.evalValue(c, Context.BOOLEAN);
if (!v1.getBoolean()) return (cc, tt) -> v1;
- Value v2 = lv2.evalValue(c, Context.BOOLEAN);
- return v2.getBoolean() ? ((cc, tt) -> v2) : LazyValue.FALSE;
+ return lv2;
});
addLazyBinaryOperator("||", precedence.get("or||"), false, (c, t, lv1, lv2) ->
{
Value v1 = lv1.evalValue(c, Context.BOOLEAN);
if (v1.getBoolean()) return (cc, tt) -> v1;
- Value v2 = lv2.evalValue(c, Context.BOOLEAN);
- return v2.getBoolean() ? ((cc, tt) -> v2) : LazyValue.FALSE;
+ return lv2;
});
addBinaryOperator("~", precedence.get("attribute~:"), true, Value::in);
@@ -1928,7 +1926,7 @@ public class Expression implements Cloneable
LazyValue expr = lv.get(1);
//scoping
LazyValue _val = c.getVariable("_");
- LazyValue _iter = c.getVariable("_i");
+ LazyValue _ite = c.getVariable("_i");
int successCount = 0;
for (int i=0; iterator.hasNext(); i++)
{
@@ -1959,7 +1957,7 @@ public class Expression implements Cloneable
//revering scope
((AbstractListValue) rval).fatality();
c.setVariable("_", _val);
- c.setVariable("_i", _iter);
+ c.setVariable("_i", _ite);
long promiseWontChange = successCount;
return (cc, tt) -> new NumericValue(promiseWontChange);
});
@@ -1988,15 +1986,18 @@ public class Expression implements Cloneable
//scoping
LazyValue _val = c.getVariable("_");
LazyValue _acc = c.getVariable("_a");
+ LazyValue _ite = c.getVariable("_i");
- while (iterator.hasNext())
+ for (int i=0; iterator.hasNext(); i++)
{
Value next = iterator.next();
String var = next.boundVariable;
next.bindTo("_");
Value promiseWontChangeYou = acc;
+ int seriously = i;
c.setVariable("_a", (cc, tt) -> promiseWontChangeYou.bindTo("_a"));
c.setVariable("_", (cc, tt) -> next);
+ c.setVariable("_i", (cc, tt) -> new NumericValue(seriously).bindTo("_i"));
try
{
acc = expr.evalValue(c, t);
@@ -2016,6 +2017,7 @@ public class Expression implements Cloneable
((AbstractListValue) rval).fatality();
c.setVariable("_a", _acc);
c.setVariable("_", _val);
+ c.setVariable("_i", _ite);
Value hopeItsEnoughPromise = acc;
return (cc, tt) -> hopeItsEnoughPromise;
diff --git a/src/main/java/carpet/script/value/BlockValue.java b/src/main/java/carpet/script/value/BlockValue.java
index 9a460338..da9704d0 100644
--- a/src/main/java/carpet/script/value/BlockValue.java
+++ b/src/main/java/carpet/script/value/BlockValue.java
@@ -130,15 +130,24 @@ public class BlockValue extends Value
public static LocatorResult fromParams(CarpetContext c, List params, int offset)
{
- return fromParams(c, params,offset, false);
+ return fromParams(c, params,offset, false, false);
}
public static LocatorResult fromParams(CarpetContext c, List params, int offset, boolean acceptString)
+ {
+ return fromParams(c, params,offset, acceptString, false);
+ }
+
+ public static LocatorResult fromParams(CarpetContext c, List params, int offset, boolean acceptString, boolean optional)
{
try
{
Value v1 = params.get(0 + offset).evalValue(c);
//add conditional from string name
+ if (optional && v1 instanceof NullValue)
+ {
+ return new LocatorResult(null, 1+offset);
+ }
if (acceptString && v1 instanceof StringValue)
{
return new LocatorResult(fromString(v1.getString()), 1+offset);
diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java
index 61760579..f1e44d99 100644
--- a/src/main/java/carpet/script/value/EntityValue.java
+++ b/src/main/java/carpet/script/value/EntityValue.java
@@ -772,6 +772,39 @@ public class EntityValue extends Value
else
e.noClip = v.getBoolean();
});
+ put("effect", (e, v) ->
+ {
+ if (!(e instanceof LivingEntity)) return;
+ LivingEntity le = (LivingEntity)e;
+ if (v == null)
+ le.clearPotionEffects();
+ else if (v instanceof ListValue)
+ {
+ List lv = ((ListValue) v).getItems();
+ if (lv.size() >= 2 && lv.size() <= 5)
+ {
+ String effectName = lv.get(0).getString();
+ if (effectName.startsWith("minecraft:"))
+ effectName = "minecraft:"+effectName;
+ StatusEffect effect = Registry.STATUS_EFFECT.get(new Identifier(effectName));
+ if (effect == null)
+ throw new InternalExpressionException("Wrong effect name: "+effectName);
+ int duration = (int)NumericValue.asNumber(lv.get(1)).getLong();
+ int amplifier = 0;
+ if (lv.size() > 2)
+ amplifier = (int)NumericValue.asNumber(lv.get(2)).getLong();
+ boolean showParticles = true;
+ if (lv.size() > 3)
+ showParticles = lv.get(3).getBoolean();
+ boolean showIcon = true;
+ if (lv.size() > 4)
+ showIcon = lv.get(4).getBoolean();
+ le.addPotionEffect(new StatusEffectInstance(effect, duration, amplifier, showParticles, showIcon));
+ return;
+ }
+ }
+ throw new InternalExpressionException("'effect' needs either no arguments (clear) or effect name, duration, and optional amplifier, show particles and show icon");
+ });
// gamemode
// spectate
diff --git a/src/main/resources/assets/carpet/scripts/camera.sc b/src/main/resources/assets/carpet/scripts/camera.sc
index 4c61f8e9..100f9b98 100644
--- a/src/main/resources/assets/carpet/scripts/camera.sc
+++ b/src/main/resources/assets/carpet/scripts/camera.sc
@@ -35,31 +35,66 @@ __command() ->
''
);
+// camera position for the player sticks out of their eyes
+__camera_position(player) -> (player~'location' + l(0, player~'eye_height',0,0,0));
+
+__update() ->
+(
+ undef('global_path_precalculated');
+ global_needs_updating = true;
+);
+
start() ->
(
p = player();
- global_points = l(l(p ~ 'location', 0,'sharp'));
- undef('global_path_precalculated');
- str('Started path at %.1f %.1f %.1f', p~'x', p~'y', p~'z')
+ global_points = l(l(__camera_position(p), 0,'sharp'));
+ __update();
+ show();
+ str('Started path at %.1f %.1f %.1f', p~'x', p~'y', p~'z');
);
add(delay) ->
(
p = player();
- mode = 'sharp';
+ mode = 'smooth';
//mode is currently unused, run_path does always sharp, gauss interpolator is always smooth
// but this option could be used could be used at some point by more robust interpolators
- __add_path_segment(p ~ 'location', delay, mode);
- str('Added point %d: %.1f %.1f %.1f', length(global_points), p~'x', p~'y', p~'z')
+ __add_path_segment(__camera_position(p), round(60*delay), mode);
+ __print_path_size();
);
+
+prepend(delay) ->
+(
+ p = player();
+ mode = 'smooth';
+ __prepend_path_segment(__camera_position(p), round(60*delay), mode);
+ __print_path_size();
+);
+
__add_path_segment(vector, duration, mode) ->
(
- undef('global_path_precalculated');
if ( (l('sharp','smooth') ~ mode) == null, exit('use smooth or sharp point'));
if(!global_points, exit('Cannot add point to path that didn\'t started yet!'));
l(v, end_time, m) = global_points:(-1);
vector:(-2) = __adjusted_rot(v:(-2) , vector:(-2) );
- global_points += l(vector, end_time+duration, mode)
+ global_points += l(vector, end_time+duration, mode);
+ __update();
);
+
+__prepend_path_segment(vector, duration, mode) ->
+(
+ if ( (l('sharp','smooth') ~ mode) == null, exit('use smooth or sharp point'));
+ if(!global_points, exit('Cannot add point to path that didn\'t started yet!'));
+ l(v, start_time, m) = global_points:(-1);
+ vector:(-2) = __adjusted_rot(v:(-2), vector:(-2));
+ new_points = l(l(vector, 0, mode));
+ for (global_points,
+ _:1 += duration;
+ new_points += _;
+ );
+ global_points = new_points;
+ __update();
+);
+
// adjusts current rotation so we don\'t spin around like crazy
__adjusted_rot(previous_rot, current_rot) ->
(
@@ -71,79 +106,80 @@ __adjusted_rot(previous_rot, current_rot) ->
__print_path_size() ->
(
- __assert_valid_for_motion();
- print("Path length: %d points, 0.2f secs at 60fps", length(global_points), global_points:(-1):1/60);
+ if (!__assert_valid_for_motion(),
+ print(str('%d points, %.1f secs', length(global_points), global_points:(-1):1/60));
+ );
+ '';
);
repeat(times, last_section_duration) ->
(
- __assert_valid_for_motion();
- undef('global_path_precalculated');
+ if (err = __assert_valid_for_motion(), exit(err));
positions = map(global_points, _:0);
modes = map(global_points, _:(-1));
durations = map(global_points, global_points:(_i+1):1 - _:1 );
- durations:(-1) = last_section_duration;
+ durations:(-1) = round(60*last_section_duration);
loop(times,
loop( length(positions),
__add_path_segment(positions:_, durations:_, modes:_)
)
);
+ __update();
__print_path_size();
'';
);
-speed(percentage) ->
+stretch(percentage) ->
(
- __assert_valid_for_motion();
+ if (err = __assert_valid_for_motion(), exit(err));
undef('global_path_precalculated');
if (percentage < 25 || percentage > 400,
- exit('path speed can only be speed, or slowed down 4 times. Recall command for larger changes')
+ exit('path speed can only be speed, or slowed down 4 times. Re-call command for larger changes')
);
ratio = percentage/100;
previous_path_length = global_points:(-1):1;
for(global_points, _:1 = _:1*ratio );
- undef('global_path_precalculated');
- str('path %s from %d to %d ticks (%.2f seconds with 60fps)',
+ __update();
+ str('path %s from %.2f to %.2f seconds',
if(ratio<1,'shortened','extended'),
- previous_path_length,
- global_points:(-1):1,
+ previous_path_length/60,
global_points:(-1):1/60
)
);
+
select_interpolation(method) ->
(
- undef('global_path_precalculated');
__prepare_path_if_needed() -> __prepare_path_if_needed_generic();
// each supported method needs to specify its __find_position_for_point to trace the path
// accepting segment number, and position in the segment
// or optionally __prepare_path_if_needed, if path is inefficient to compute point by point
- if (
- method == 'linear',
- (
- __find_position_for_point(s, p) -> __find_position_for_linear(s, p)
- ),
+ global_interpolator = if (
+ method == 'linear', '__interpolator_linear',
+ method == 'cr', '__interpolator_cr',
+ method == 'gauss', _(s, p) -> __interpolator_gauss(s, p, 0),
method ~ '^gauss_',
- (
- type = method - 'gauss_';
- global_interpol_option = if(type=='auto',0,number(type));
- __find_position_for_point(s, p) -> __find_position_for_gauss(s, p)
- ),
-
- exit('Choose one of the following methods: linear, gauss:auto, gauss:')
+ (
+ type = method - 'gauss_';
+ variance = number(type);
+ _(s, p, outer(variance)) -> __interpolator_gauss(s, p, variance);
+ ),
+ exit('Choose one of the following methods: linear, gauss, gauss_, cr')
);
+ __update();
'Ok'
);
-select_interpolation('gauss_auto');
+select_interpolation('cr');
+
__assert_valid_for_motion() ->
(
- if(!global_points, exit('Path not defined yet'));
- if(length(global_points)<2, exit('Path not complete - add more points'));
+ if(!global_points, return('Path not defined yet'));
+ if(length(global_points)<2, return('Path not complete - add more points'));
null
);
__get_path_at(segment, start, index) ->
(
v = global_path_precalculated:(start+index);
if(v == null,
- v = __find_position_for_point(segment, index);
+ v = call(global_interpolator, segment, index);
global_path_precalculated:(start+index) = v
);
v
@@ -153,16 +189,11 @@ __invalidate_points_cache() -> global_path_precalculated = map(range(global_poin
global_showing_path = false;
global_playing_path = false;
-stop() ->
+hide() ->
(
- if (!global_playing_path && !global_showing_path, return('You can only stop playing or showing the path'));
if (global_showing_path,
global_showing_path = false;
- print('stopped showing path');
- );
- if (global_playing_path,
- global_playing_path = false;
- print('stopped playing path');
+ print('Stopped showing path');
);
''
);
@@ -170,97 +201,198 @@ stop() ->
clear() ->
(
global_points = l();
- undef('global_path_precalculated');
+ __update();
);
global_needs_updating = false;
global_selected_point = null;
+global_markers = null;
+
+__distsq(vec1, vec2) -> reduce(vec1 - vec2, _a + _*_, 0);
+
+__closest_point_to_center(center, points) ->
+(
+ dd = __distsq(points:0, center);
+ for(points,
+ d = __distsq(_, center);
+ if( d
+(
+ if (!global_points, return());
+ if (!global_showing_path, return());
+ p = player();
+ selected_point = __closest_point_to_center(
+ p~'pos'+l(0,p~'eye_height',0),
+ map(global_points, slice(_:0, 0, 3))
+ );
+ global_selected_point = if (global_selected_point == selected_point, null, selected_point);
+ global_needs_updating = true;
+ // no need to _update since path is still valid
+
+);
+
+__on_player_attacks_entity(p, e) ->
+(
+ if (e~'type' == 'armor_stand' && query(e, 'has_tag', '__scarpet_marker_camera') && global_markers,
+ for (global_markers,
+ if(_==e,
+ global_selected_point = if (global_selected_point == _i, null, _i);
+ global_needs_updating = true;
+ return();
+ )
+ );
+ );
+);
+
+global_show_thread = null;
show() ->
(
p = player();
- __assert_valid_for_motion();
__print_path_size();
+ if (global_showing_path, return ());
global_showing_path = true;
global_needs_updating= false;
- def __create_markers()->
+ __create_markers() ->
(
- map(global_points,
- m = create_marker(null, _:0, 'observer');
- if (global_selected_point && _i == global_selected_point,
- modify(m,'effect','glowing',72000);
+ map(global_points || l(),
+ is_selected = global_selected_point != null && _i == global_selected_point;
+ caption = if (_i == 0, 'start', str('%.1fs', global_points:_i:1/60); );
+ if (is_selected && _i > 0,
+ caption += str(' (%.1f current segment)', (global_points:_i:1 - global_points:(_i-1):1)/60);
+ );
+ m = create_marker(caption, _:0, 'observer');
+ if (is_selected,
+ modify(m,'effect','glowing',72000, 0, false, false);
);
m
);
);
+ __show_path_tick() ->
+ (
+ if (__assert_valid_for_motion(), return());
+ __prepare_path_if_needed();
+ loop(100,
+ segment = floor(rand(length(global_points)-1));
+ particle_type = if ((segment+1) == global_selected_point, 'dust 0.1 0.9 0.1 1','dust 0.6 0.6 0.6 1');
+ start = global_points:segment:1;
+ end = global_points:(segment+1):1;
+ index = floor(rand(end-start));
+ l(x, y, z) = slice(__get_path_at(segment, start, index), 0, 3);
+ particle(particle_type, x, y, z, 1, 0, 0)
+ );
+ null
+ );
+
task( _(outer(p)) -> (
- markers = __create_markers();
+ global_markers = __create_markers();
loop(7200,
if(!global_showing_path, break());
if (global_needs_updating,
global_needs_updating = false;
- for(markers, modify(_,'remove');
- markers = __create_markers();
+ for(global_markers, modify(_,'remove'));
+ global_markers = __create_markers();
);
- _show_path_tick('dust 0.9 0.1 0.1 1', 100);
+ __show_path_tick();
sleep(100);
);
- for(markers, modify(_,'remove');
+ for(global_markers, modify(_,'remove'));
+ global_markers = null;
+ global_showing_path = false;
+ ));
+ '';
+);
+
+move() ->
+(
+ if (global_selected_point == null, return());
+ if (!global_showing_path, return());
+ global_points:global_selected_point:0 = __camera_position(player());
+ __update();
+);
+
+duration(amount) ->
+(
+ if (!global_selected_point, return()); // skip nulls, and 0 (first one)
+ if (!global_showing_path, return());
+ duration = number(amount);
+ new_ticks = round(duration * 60);
+ if (new_ticks < 10, return());
+ previous_ticks = global_points:global_selected_point:1-global_points:(global_selected_point-1):1;
+ delta = new_ticks - previous_ticks;
+ // adjust duration of all points after that.
+ for (range(global_selected_point, length(global_points)),
+ global_points:_:1 += delta;
+ );
+ __update();
+);
+
+delete_point() ->
+(
+ if (global_selected_point == null, return()); // skip nulls, and 0 (first one)
+ if (!global_showing_path, return());
+ if (length(global_points) < 2, clear(); return());
+ if (global_selected_point == 0, global_points:1:1 = 0);
+ global_points = filter(global_points, _i != global_selected_point);
+ __update();
+);
+
+trim_path() ->
+(
+ if (global_selected_point == null, return()); // skip nulls, and 0 (first one)
+ if (!global_showing_path, return());
+ global_points = slice(global_points, 0, global_selected_point);
+ __update();
+);
+
+play() ->
+(
+ p = player();
+ if (err = __assert_valid_for_motion(), exit(err));
+ __prepare_path_if_needed();
+ task( _(outer(fps), outer(p)) -> (
+ sleep(1000);
+ global_playing_path = true;
+ mspt = 1000 / 60;
+ start_time = time();
+ point = 0;
+ player_offset = l(0,p~'eye_height',0,0,0);
+ try (
+ loop( length(global_points)-1, segment = _;
+ start = global_points:segment:1;
+ end = global_points:(segment+1):1;
+ loop(end-start,
+ if (p~'sneaking', global_playing_path = false);
+ if (!global_playing_path, throw());
+ v = __get_path_at(segment, start, _)-player_offset;
+ modify(p, 'location', v);
+ point += 1;
+ end_time = time();
+ took = end_time - start_time;
+ if (took < mspt, sleep(mspt-took));
+ start_time = time()
+ )
+ );
+ );
+ sleep(1000);
+ global_playing_path = false;
print('Done!');
));
'';
);
-play(fps) ->
-(
- p = player();
- __assert_valid_for_motion();
- __prepare_path_if_needed();
- global_playing_path = true;
- task( _(outer(fps), outer(p)) -> (
- sleep(1000);
- mspt = 1000 / fps;
- start_time = time();
- point = 0;
- loop( length(global_points)-1, segment = _;
- if (p~'sneaking', global_playing_path = false);
- if (!global_playing_path, break());
- start = global_points:segment:1;
- end = global_points:(segment+1):1;
- loop(end-start,
- v = __get_path_at(segment, start, _);
- modify(p, 'location', v);
- point += 1;
- end_time = time();
- took = end_time - start_time;
- if (took < mspt, sleep(mspt-took));
- start_time = time()
- )
- );
- sleep(1000);
- print('Done!');
- ));
- '';
-);
-_show_path_tick(particle_type, total) ->
-(
- __assert_valid_for_motion();
- __prepare_path_if_needed();
- loop(total,
- segment = floor(rand(length(global_points)-1));
- start = global_points:segment:1;
- end = global_points:(segment+1):1;
- index = floor(rand(end-start));
- l(x, y, z) = slice(__get_path_at(segment, start, index), 0, 3);
- particle(particle_type, x, y, z, 1, 0, 0)
- );
- null
-);
__prepare_path_if_needed_generic() ->
(
if(!global_path_precalculated, __invalidate_points_cache())
);
-__find_position_for_linear(segment, point) ->
+__interpolator_linear(segment, point) ->
(
l(va, start, mode_a) = global_points:segment;
l(vb, end, mode_b) = global_points:(segment+1);
@@ -271,16 +403,14 @@ __find_position_for_linear(segment, point) ->
//(1/sqrt(2*pi*d*d))*euler^(-((x-miu)^2)/(2*d*d))
// but we will be normalizing anyways, so who cares
__norm_prob(x, miu, d) -> euler^(-((x-miu)^2)/(2*d*d));
-__find_position_for_gauss(from_index, point) ->
+__interpolator_gauss(from_index, point, deviation) ->
(
- dev = global_interpol_option;
components = l();
path_point = global_points:from_index:1;
-
try(
for(range(from_index+1, length(global_points)),
l(v,ptime,mode) = global_points:_;
- dev = if (global_interpol_option > 0, global_interpol_option,
+ dev = if (deviation > 0, deviation,
devs = l();
if (_+1 < length(global_points), devs += global_points:(_+1):1-ptime);
if (_-1 >= 0, devs += ptime-global_points:(_-1):1);
@@ -295,7 +425,7 @@ __find_position_for_gauss(from_index, point) ->
try(
for(range(from_index, -1, -1),
l(v,ptime,mode) = global_points:_;
- dev = if (global_interpol_option > 0, global_interpol_option,
+ dev = if (deviation > 0, deviation,
devs = l();
if (_+1 < length(global_points), devs += global_points:(_+1):1-ptime);
if (_-1 >= 0, devs += ptime-global_points:(_-1):1);
@@ -309,4 +439,15 @@ __find_position_for_gauss(from_index, point) ->
);
total = rtotal+ltotal;
reduce(components, _a+_:0*(_:1/total), l(0,0,0,0,0))
-)
\ No newline at end of file
+);
+
+__interpolator_cr(from_index, point) ->
+(
+ total = global_points:(from_index+1):1 - global_points:from_index:1;
+ p__1 = global_points:(if(from_index == 0, 0, from_index-1)):0;
+ p_0 = global_points:from_index:0;
+ p_1 = global_points:(from_index+1):0;
+ p_2 = global_points:(if(from_index == (length(global_points)-2), -1, from_index+2)):0;
+ r = point/total; // ratio within segment
+ (r*((2-r)*r-1) * p__1 + (r*r*(3*r-5)+2) * p_0 + r*((4 - 3*r)*r + 1) * p_1 + (r-1)*r*r * p_2) / 2
+);
\ No newline at end of file
From a4e6e7cb100150fee4ffe876fdcb448c14989078 Mon Sep 17 00:00:00 2001
From: gnembon
Date: Wed, 25 Dec 2019 02:44:14 -0400
Subject: [PATCH 13/14] camera update and task clearing
---
.../java/carpet/mixins/ContainerMixin.java | 18 +-
.../ServerPlayerEntity_scarpetEventMixin.java | 10 +
.../java/carpet/script/CarpetEventServer.java | 35 +-
.../java/carpet/script/CarpetExpression.java | 2 +-
.../java/carpet/script/CarpetScriptHost.java | 37 +-
.../carpet/script/CarpetScriptServer.java | 51 +-
src/main/java/carpet/script/Expression.java | 37 +-
src/main/java/carpet/script/ScriptHost.java | 2 +-
.../carpet/script/bundled/FileModule.java | 23 +-
.../script/bundled/ModuleInterface.java | 36 +-
.../java/carpet/script/value/ThreadValue.java | 22 +-
.../resources/assets/carpet/scripts/camera.sc | 602 +++++++++++-------
.../assets/carpet/scripts/event_test.sc | 5 +
src/main/resources/carpet.mixins.json | 1 +
14 files changed, 545 insertions(+), 336 deletions(-)
diff --git a/src/main/java/carpet/mixins/ContainerMixin.java b/src/main/java/carpet/mixins/ContainerMixin.java
index f6cc0b29..2056d82f 100644
--- a/src/main/java/carpet/mixins/ContainerMixin.java
+++ b/src/main/java/carpet/mixins/ContainerMixin.java
@@ -26,26 +26,20 @@ public abstract class ContainerMixin
@Shadow public abstract void sendContentUpdates();
@Inject( method = "onSlotClick", at = @At(value = "HEAD"), cancellable = true)
- private void onThrowClick(
- int int_1,
- int int_2,
- SlotActionType slotActionType_1,
- PlayerEntity playerEntity_1,
- CallbackInfoReturnable cir
- )
+ private void onThrowClick( int slotId, int clickData, SlotActionType actionType, PlayerEntity playerEntity, CallbackInfoReturnable cir)
{
- if (slotActionType_1 == SlotActionType.THROW && CarpetSettings.ctrlQCraftingFix && playerEntity_1.inventory.getCursorStack().isEmpty() && int_1 >= 0)
+ if (actionType == SlotActionType.THROW && CarpetSettings.ctrlQCraftingFix && playerEntity.inventory.getCursorStack().isEmpty() && slotId >= 0)
{
ItemStack itemStack_1 = ItemStack.EMPTY;
- Slot slot_4 = slotList.get(int_1);
- if (slot_4 != null && slot_4.hasStack() && slot_4.canTakeItems(playerEntity_1))
+ Slot slot_4 = slotList.get(slotId);
+ if (slot_4 != null && slot_4.hasStack() && slot_4.canTakeItems(playerEntity))
{
- if(int_1 == 0 && int_2 == 1)
+ if(slotId == 0 && clickData == 1)
{
Item craftedItem = slot_4.getStack().getItem();
while(slot_4.hasStack() && slot_4.getStack().getItem() == craftedItem)
{
- this.onSlotClick(int_1, 0, SlotActionType.THROW, playerEntity_1);
+ this.onSlotClick(slotId, 0, SlotActionType.THROW, playerEntity);
}
this.sendContentUpdates();
cir.setReturnValue(itemStack_1);
diff --git a/src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java b/src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java
index d291efb5..9c10007c 100644
--- a/src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java
+++ b/src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java
@@ -4,14 +4,18 @@ 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.stat.Stat;
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.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import static carpet.script.CarpetEventServer.Event.PLAYER_FINISHED_USING_ITEM;
+import static carpet.script.CarpetEventServer.Event.STATISTICS;
@Mixin(ServerPlayerEntity.class)
public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity
@@ -43,4 +47,10 @@ public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity
super.method_6040();
}
}
+
+ @Inject(method = "increaseStat", at = @At("HEAD"))
+ private void grabStat(Stat> stat, int amount, CallbackInfo ci)
+ {
+ STATISTICS.onPlayerStatistic((ServerPlayerEntity) (Object)this, stat, amount);
+ }
}
diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java
index 1462b619..f132c638 100644
--- a/src/main/java/carpet/script/CarpetEventServer.java
+++ b/src/main/java/carpet/script/CarpetEventServer.java
@@ -5,6 +5,7 @@ import carpet.script.value.BlockValue;
import carpet.script.value.EntityValue;
import carpet.script.value.FunctionValue;
import carpet.script.value.ListValue;
+import carpet.script.value.NBTSerializableValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
@@ -13,11 +14,15 @@ import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.entity.Entity;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.item.ItemStack;
+import net.minecraft.stat.Stat;
+import net.minecraft.stat.Stats;
import net.minecraft.util.Hand;
+import net.minecraft.util.Identifier;
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.dimension.DimensionType;
import org.apache.commons.lang3.tuple.Pair;
@@ -25,10 +30,12 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -403,7 +410,32 @@ public class CarpetEventServer
{
handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource);
}
- };
+ },
+ STATISTICS("statistic", new CallbackList(4))
+ {
+ private Identifier getStatId(Stat stat)
+ {
+ return stat.getType().getRegistry().getId(stat.getValue());
+ }
+ private Set skippedStats = new HashSet(){{
+ add(Stats.TIME_SINCE_DEATH);
+ add(Stats.TIME_SINCE_REST);
+ add(Stats.PLAY_ONE_MINUTE);
+ }};
+ @Override
+ public void onPlayerStatistic(ServerPlayerEntity player, Stat> stat, int amount)
+ {
+ Identifier id = getStatId(stat);
+ if (skippedStats.contains(id)) return;
+ handler.call( () -> Arrays.asList(
+ ((c, t) -> new EntityValue(player)),
+ ((c, t) -> new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.STAT_TYPE.getId(stat.getType())))),
+ ((c, t) -> new StringValue(NBTSerializableValue.nameFromRegistryId(id))),
+ ((c, t) -> new NumericValue(amount))
+ ), player::getCommandSource);
+ }
+ }
+ ;
// on projectile thrown (arrow from bows, crossbows, tridents, snoballs, e-pearls
@@ -427,6 +459,7 @@ public class CarpetEventServer
//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 onPlayerStatistic(ServerPlayerEntity player, Stat> stat, int amount) { }
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) { }
diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java
index 95fe343c..e6c682a3 100644
--- a/src/main/java/carpet/script/CarpetExpression.java
+++ b/src/main/java/carpet/script/CarpetExpression.java
@@ -3157,7 +3157,7 @@ public class CarpetExpression
String file = null;
if (lv.size()>1)
{
- String origfile = lv.get(0).evalValue(c).getString();
+ String origfile = lv.get(1).evalValue(c).getString();
file = origfile.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9]", "");
if (file.isEmpty())
{
diff --git a/src/main/java/carpet/script/CarpetScriptHost.java b/src/main/java/carpet/script/CarpetScriptHost.java
index 78905ee4..c7e890f8 100644
--- a/src/main/java/carpet/script/CarpetScriptHost.java
+++ b/src/main/java/carpet/script/CarpetScriptHost.java
@@ -20,6 +20,7 @@ import net.minecraft.util.math.BlockPos;
import java.math.BigInteger;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -217,6 +218,35 @@ public class CarpetScriptHost extends ScriptHost
super.onClose();
if (this.saveTimeout > 0)
dumpState();
+
+ FunctionValue closing = globalFunctions.get("__on_close");
+ if (closing != null)
+ {
+ try
+ {
+ callUDF(BlockPos.ORIGIN, CarpetServer.minecraft_server.getCommandSource(), closing, Collections.emptyList());
+ }
+ catch (InvalidCallbackException ignored)
+ {
+ }
+ }
+ userHosts.forEach((key, value) ->
+ {
+ FunctionValue userClosing = value.globalFunctions.get("__on_close");
+ if (userClosing != null)
+ {
+ ServerPlayerEntity player = CarpetServer.minecraft_server.getPlayerManager().getPlayer(key);
+ ServerCommandSource source = (player != null)?player.getCommandSource():CarpetServer.minecraft_server.getCommandSource();
+ try
+ {
+ ((CarpetScriptHost) value).callUDF(BlockPos.ORIGIN, source, userClosing, Collections.emptyList());
+ }
+ catch (InvalidCallbackException ignored)
+ {
+ }
+ }
+ });
+
String markerName = ExpressionInspector.MARKER_STRING+"_"+((getName()==null)?"":getName());
for (ServerWorld world : CarpetServer.minecraft_server.getWorlds())
{
@@ -225,6 +255,7 @@ public class CarpetScriptHost extends ScriptHost
e.remove();
}
}
+
}
private void dumpState()
@@ -239,9 +270,9 @@ public class CarpetScriptHost extends ScriptHost
public Tag getGlobalState(String file)
{
- if (getName() == null || myCode.isInternal()) return null;
+ if (getName() == null ) return null;
if (file != null)
- myCode.getData(file);
+ return myCode.getData(file);
if (parent == null)
return globalState;
return ((CarpetScriptHost)parent).globalState;
@@ -249,7 +280,7 @@ public class CarpetScriptHost extends ScriptHost
public void setGlobalState(Tag tag, String file)
{
- if (getName() == null || myCode.isInternal()) return;
+ if (getName() == null ) return;
if (file!= null)
{
diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java
index 1df304c6..75968456 100644
--- a/src/main/java/carpet/script/CarpetScriptServer.java
+++ b/src/main/java/carpet/script/CarpetScriptServer.java
@@ -43,7 +43,7 @@ public class CarpetScriptServer
private Set holyMoly;
public CarpetEventServer events;
- public static List bundledModuleData = new ArrayList(){{
+ private static List bundledModuleData = new ArrayList(){{
add(new BundledModule("camera"));
add(new BundledModule("event_test"));
}};
@@ -72,7 +72,7 @@ public class CarpetScriptServer
}
- ModuleInterface getModule(String name)
+ private ModuleInterface getModule(String name)
{
File folder = CarpetServer.minecraft_server.getLevelStorage().resolveFile(
CarpetServer.minecraft_server.getLevelName(), "scripts");
@@ -218,25 +218,6 @@ public class CarpetScriptServer
}
return false;
}
- private void addEvents(ServerCommandSource source, String hostName)
- {
- if (1+2 == 3) throw new RuntimeException("This should run when code is evaluated");
- ScriptHost host = modules.get(hostName);
- if (host == null)
- {
- return;
- }
- for (String fun : host.globalFunctions.keySet())
- {
- if (!fun.startsWith("__on_"))
- continue;
- String event = fun.replaceFirst("__on_","");
- if (!CarpetEventServer.Event.byName.containsKey(event))
- continue;
- events.addEvent(event, hostName, fun);
- }
- }
-
private void addCommand(ServerCommandSource source, String hostName)
{
@@ -261,7 +242,9 @@ public class CarpetScriptServer
requires((player) -> modules.containsKey(hostName)).
executes( (c) ->
{
- Messenger.m(c.getSource(), "gi "+modules.get(hostName).retrieveForExecution(c.getSource()).call(c.getSource(),"__command", null, ""));
+ String response = modules.get(hostName).retrieveForExecution(c.getSource()).
+ call(c.getSource(),"__command", null, "");
+ if (!response.isEmpty()) Messenger.m(c.getSource(), "gi "+response);
return 1;
});
@@ -271,28 +254,16 @@ public class CarpetScriptServer
then(literal(function).
requires((player) -> modules.containsKey(hostName) && modules.get(hostName).getPublicFunctions().contains(function)).
executes( (c) -> {
- Messenger.m(
- c.getSource(),
- "gi "+modules.get(hostName).retrieveForExecution(c.getSource()).call(
- c.getSource(),
- function,
- null,
- ""
- )
- );
+ String response = modules.get(hostName).retrieveForExecution(c.getSource()).
+ call(c.getSource(), function,null,"");
+ if (!response.isEmpty()) Messenger.m(c.getSource(),"gi "+response);
return 1;
}).
then(argument("args...", StringArgumentType.greedyString()).
executes( (c) -> {
- Messenger.m(
- c.getSource(),
- "gi "+modules.get(hostName).retrieveForExecution(c.getSource()).call(
- c.getSource(),
- function,
- null,
- StringArgumentType.getString(c, "args...")
- )
- );
+ String response = modules.get(hostName).retrieveForExecution(c.getSource()).
+ call(c.getSource(), function,null, StringArgumentType.getString(c, "args..."));
+ if (!response.isEmpty()) Messenger.m(c.getSource(), "gi "+response);
return 1;
})));
}
diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java
index 4677800f..9ff9e44b 100644
--- a/src/main/java/carpet/script/Expression.java
+++ b/src/main/java/carpet/script/Expression.java
@@ -3002,22 +3002,9 @@ public class Expression implements Cloneable
});
addLazyFunctionWithDelegation("task", -1, (c, t, expr, tok, lv) -> {
- Value queue = Value.NULL;
- Value funcDesc;
- if (lv.size()==1)
- {
- funcDesc = lv.get(0).evalValue(c);
- }
- else if (lv.size() == 2)
- {
- queue = lv.get(0).evalValue(c);
- funcDesc = lv.get(1).evalValue(c);
- }
- else
- {
- throw new InternalExpressionException("'task' takes one or two parameters");
- }
- FunctionValue fun;
+ if (lv.size() == 0)
+ throw new InternalExpressionException("'task' requires at least function to call as a parameter");
+ Value funcDesc = lv.get(0).evalValue(c);
if (!(funcDesc instanceof FunctionValue))
{
String name = funcDesc.getString();
@@ -3025,10 +3012,20 @@ public class Expression implements Cloneable
if (funcDesc == null)
throw new InternalExpressionException("Function "+name+" is not defined yet");
}
- fun = (FunctionValue)funcDesc;
- if (fun.getArguments().size() > 0)
- throw new InternalExpressionException("tasks can only accept functions that don't take any arguments");
- ThreadValue thread = new ThreadValue(queue, fun, expr, tok, c);
+ FunctionValue fun = (FunctionValue)funcDesc;
+ int extraargs = lv.size() - fun.getArguments().size();
+ if (extraargs != 1 && extraargs != 2)
+ {
+ throw new InternalExpressionException("Function takes "+fun.getArguments().size()+" arguments.");
+ }
+ List lvargs = new ArrayList<>();
+ for (int i=0; i< fun.getArguments().size(); i++)
+ {
+ lvargs.add(lv.get(i+1));
+ }
+ Value queue = Value.NULL;
+ if (extraargs == 2) queue = lv.get(lv.size()-1).evalValue(c);
+ ThreadValue thread = new ThreadValue(queue, fun, expr, tok, c, lvargs);
Thread.yield();
return (cc, tt) -> thread;
});
diff --git a/src/main/java/carpet/script/ScriptHost.java b/src/main/java/carpet/script/ScriptHost.java
index fdb100f2..1a5c58d6 100644
--- a/src/main/java/carpet/script/ScriptHost.java
+++ b/src/main/java/carpet/script/ScriptHost.java
@@ -11,7 +11,7 @@ import java.util.stream.Collectors;
public class ScriptHost
{
- private final Map userHosts = new Object2ObjectOpenHashMap<>();
+ protected final Map userHosts = new Object2ObjectOpenHashMap<>();
public Map globalFunctions = new Object2ObjectOpenHashMap<>();
public Map globalVariables = new Object2ObjectOpenHashMap<>();
diff --git a/src/main/java/carpet/script/bundled/FileModule.java b/src/main/java/carpet/script/bundled/FileModule.java
index b9d45b22..facded41 100644
--- a/src/main/java/carpet/script/bundled/FileModule.java
+++ b/src/main/java/carpet/script/bundled/FileModule.java
@@ -56,7 +56,7 @@ public class FileModule implements ModuleInterface
}
//copied private method from net.minecraft.nbt.NbtIo.read()
- private static Tag read(File file)
+ public static Tag read(File file)
{
try (DataInputStream dataInput_1 = new DataInputStream(new FileInputStream(file)))
{
@@ -80,7 +80,7 @@ public class FileModule implements ModuleInterface
}
//copied private method from net.minecraft.nbt.NbtIo.write() and client method safe_write
- private static void write(Tag tag_1, File file)
+ public static void write(Tag tag_1, File file)
{
File file_2 = new File(file.getAbsolutePath() + "_tmp");
if (file_2.exists()) file_2.delete();
@@ -101,23 +101,4 @@ public class FileModule implements ModuleInterface
if (file.exists()) file.delete();
if (!file.exists()) file_2.renameTo(file);
}
-
- @Override
- public Tag getData(String file)
- {
- File dataFile =CarpetServer.minecraft_server.getLevelStorage().resolveFile(
- CarpetServer.minecraft_server.getLevelName(), "scripts/"+name+".data"+(file==null?"":"."+file)+".nbt");
- if (!(dataFile.isFile()))
- return null;
- return read(dataFile);
- }
-
- @Override
- public void saveData(String file, Tag globalState)
- {
- File dataFile =CarpetServer.minecraft_server.getLevelStorage().resolveFile(
- CarpetServer.minecraft_server.getLevelName(), "scripts/"+name+".data"+(file==null?"":"."+file)+".nbt");
- write(globalState, dataFile);
-
- }
}
diff --git a/src/main/java/carpet/script/bundled/ModuleInterface.java b/src/main/java/carpet/script/bundled/ModuleInterface.java
index 009df028..dc7aa2ad 100644
--- a/src/main/java/carpet/script/bundled/ModuleInterface.java
+++ b/src/main/java/carpet/script/bundled/ModuleInterface.java
@@ -1,12 +1,44 @@
package carpet.script.bundled;
+import carpet.CarpetServer;
import net.minecraft.nbt.Tag;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+import static carpet.script.bundled.FileModule.read;
+import static carpet.script.bundled.FileModule.write;
+
public interface ModuleInterface
{
String getName();
String getCode();
default boolean isInternal() {return true; }
- default Tag getData(String file) {return null; }
- default void saveData(String file, Tag globalState) {}
+
+ default Tag getData(String file)
+ {
+ File dataFile = CarpetServer.minecraft_server.getLevelStorage().resolveFile(
+ CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt");
+ if (!Files.exists(dataFile.toPath()) || !(dataFile.isFile())) return null;
+ return read(dataFile);
+ }
+
+ default void saveData(String file, Tag globalState)
+ {
+ File dataFile =CarpetServer.minecraft_server.getLevelStorage().resolveFile(
+ CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt");
+ if (!Files.exists(dataFile.toPath().getParent()))
+ {
+ try
+ {
+ Files.createDirectory(dataFile.toPath().getParent());
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ write(globalState, dataFile);
+
+ }
}
diff --git a/src/main/java/carpet/script/value/ThreadValue.java b/src/main/java/carpet/script/value/ThreadValue.java
index 667e087a..40500011 100644
--- a/src/main/java/carpet/script/value/ThreadValue.java
+++ b/src/main/java/carpet/script/value/ThreadValue.java
@@ -2,11 +2,12 @@ package carpet.script.value;
import carpet.script.Context;
import carpet.script.Expression;
+import carpet.script.LazyValue;
import carpet.script.Tokenizer;
import carpet.script.exception.InternalExpressionException;
-import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -17,18 +18,14 @@ public class ThreadValue extends Value
{
private CompletableFuture taskFuture;
private long id;
- private final Object lock;
private static long sequence = 0L;
private static final Map executorServices = new HashMap<>();
- public ThreadValue(Value pool, FunctionValue function, Expression expr, Tokenizer.Token token, Context ctx)
+ public ThreadValue(Value pool, FunctionValue function, Expression expr, Tokenizer.Token token, Context ctx, List args)
{
id = sequence++;
- lock = new Object();
taskFuture = CompletableFuture.supplyAsync(
- () -> { synchronized (lock) {
- return function.lazyEval(ctx, Context.NONE, expr, token, Collections.emptyList()).evalValue(ctx);
- } },
+ () -> function.lazyEval(ctx, Context.NONE, expr, token, args).evalValue(ctx),
executorServices.computeIfAbsent(pool, (v) -> (ThreadPoolExecutor)Executors.newCachedThreadPool())
);
Thread.yield();
@@ -63,17 +60,6 @@ public class ThreadValue extends Value
return taskFuture.isDone();
}
- public void stop()
- {
- synchronized (lock)
- {
- if (!taskFuture.isDone())
- {
- taskFuture.complete(Value.NULL);
- }
- }
- }
-
@Override
public boolean equals(Object o)
{
diff --git a/src/main/resources/assets/carpet/scripts/camera.sc b/src/main/resources/assets/carpet/scripts/camera.sc
index 100f9b98..ffd9130c 100644
--- a/src/main/resources/assets/carpet/scripts/camera.sc
+++ b/src/main/resources/assets/carpet/scripts/camera.sc
@@ -1,137 +1,210 @@
__command() ->
(
- print('camera_path module.');
+ print('camera: scarpet app.');
print('-------------------');
- print(' "/camera_path start" - set the starting point');
+ print(' "/camera start" - set the starting point, resets the path');
print('');
- print(' "/camera_path add " - add another point frames later');
+ print(' "/camera add " - add point to the end seconds later');
+ print(' "/camera prepend " - prepend point seconds before');
+ print(' "/camera clear" - removes entire path');
+ print(' "/camera select" - or punch a point, to select it');
+ print(' "/camera move" - moves selected camera to current player location');
+ print(' "/camera duration " - resizes current selected path');
+ print(' "/camera split_point>" - splits current selected path in half');
+ print(' "/camera delete_point" - removes current keypoint');
+ print(' "/camera trim_path" - removes all keypoints from selected');
print('');
- print(' "/camera_path select_interpolation < linear | gauss_? >"');
- print(' Select interpolation between points:');
+ print(' "/camera store "');
+ print(' "/camera load "');
+ print(' - stores and loads path from nbt files in /scripts world folder');
+ print('');
+ print(' "/camera interpolation < linear | gauss | cr | gauss_ >"');
+ print(' Selects interpolation between points:');
+ print(' - cr: Catmull-Rom interpolation (default).');
+ print(' smooth path that goes through all points.');
print(' - linear: straight paths between points.');
- print(' - gauss_auto: automatic smooth transitions.');
- print(' - gauss_: custom fixed variance ');
- print(' (in points) for special effects.');
+ print(' - gauss: automatic smooth transitions.');
+ print(' - gauss_: custom fixed variance ');
+ print(' (in seconds) for special effects.');
+ print(' use "_" for decimal separator, e.g. gauss_2_5');
+ print(' gauss makes the smoothest path,');
+ print(' but treating points as suggestions only');
print('');
- print(' "/camera_path repeat " - ');
+ print(' "/camera repeat " - ');
print(' Repeat existing points configuration n-times');
- print(' using points to link path ends');
+ print(' using seconds to link path ends');
print('');
- print(' "/camera_path speed - ');
+ print(' "/camera stretch " - ');
print(' Change number of frames between points');
- print(' from 25 -> 4 times faster (less points),');
- print(' to 400 -> 4 times slower (more points)');
+ print(' from 25 -> 4x faster, to 400 -> 4x slower,');
print('');
- print(' "/camera_path play : run a path with a player');
- print(' needs to be multiples of 20, (20 tps)');
+ print(' "/camera play : run a path with a player');
+ print(' use "sneak" to stop it prematurely');
+ print(' run /camera hide and F1 to clear the view');
print('');
- print(' "/camera_path show": shows current path for a moment');
+ print(' "/camera show": shows current path for a moment');
+ print(' color of particles used is different for different players');
+ print(' "/camera hide": hides path display');
print('');
- print(' "/script in camera_path invoke _show_path_tick ":');
- print(' place this in a repeating commandblock');
- print(' to display path continuously');
- print(' with set particle and number of particles per tick');
- print(' example: \'dust 0.9 0.1 0.1 1\' 100 ');
''
);
-// camera position for the player sticks out of their eyes
-__camera_position(player) -> (player~'location' + l(0, player~'eye_height',0,0,0));
+global_points = null;
+global_dimension = null;
+global_player = null;
-__update() ->
-(
- undef('global_path_precalculated');
- global_needs_updating = true;
-);
+global_player_eye_offset = map(range(5), 0);
-start() ->
-(
- p = player();
- global_points = l(l(__camera_position(p), 0,'sharp'));
- __update();
- show();
- str('Started path at %.1f %.1f %.1f', p~'x', p~'y', p~'z');
-);
-add(delay) ->
-(
- p = player();
- mode = 'smooth';
- //mode is currently unused, run_path does always sharp, gauss interpolator is always smooth
- // but this option could be used could be used at some point by more robust interpolators
- __add_path_segment(__camera_position(p), round(60*delay), mode);
- __print_path_size();
-);
+global_showing_path = false;
+global_playing_path = false;
-prepend(delay) ->
+global_needs_updating = false;
+global_selected_point = null;
+global_markers = null;
+
+global_particle_density = 100;
+
+global_color_a = null;
+global_color_b = null;
+
+global_path_precalculated = null;
+
+// starts the path with current player location
+start() -> __start_with( _() -> l(l(__camera_position(), 0,'sharp')) );
+
+// start path with customized initial points selection
+__start_with(points_supplier) ->
(
p = player();
- mode = 'smooth';
- __prepend_path_segment(__camera_position(p), round(60*delay), mode);
- __print_path_size();
-);
+ global_player = str(p);
+ global_player_eye_offset = l(0, p~'eye_height',0,0,0);
+ global_dimension = p~'dimension';
-__add_path_segment(vector, duration, mode) ->
-(
- if ( (l('sharp','smooth') ~ mode) == null, exit('use smooth or sharp point'));
- if(!global_points, exit('Cannot add point to path that didn\'t started yet!'));
- l(v, end_time, m) = global_points:(-1);
- vector:(-2) = __adjusted_rot(v:(-2) , vector:(-2) );
- global_points += l(vector, end_time+duration, mode);
- __update();
-);
+ code = abs(hash_code(str(p))-123456);
+ global_color_a = str('dust %.1f %.1f %.1f 1',(code%10)/10,(((code/10)%10)/10), (((code/100)%10)/10) ); // 'dust 0.1 0.9 0.1 1',
+ global_color_b = str('dust %.1f %.1f %.1f 1',(((code/1000)%10)/10),(((code/10000)%10)/10), (((code/100000)%10)/10) );// 'dust 0.6 0.6 0.6 1'
-__prepend_path_segment(vector, duration, mode) ->
-(
- if ( (l('sharp','smooth') ~ mode) == null, exit('use smooth or sharp point'));
- if(!global_points, exit('Cannot add point to path that didn\'t started yet!'));
- l(v, start_time, m) = global_points:(-1);
- vector:(-2) = __adjusted_rot(v:(-2), vector:(-2));
- new_points = l(l(vector, 0, mode));
- for (global_points,
- _:1 += duration;
- new_points += _;
- );
- global_points = new_points;
+ global_points = call(points_supplier);
__update();
+ show();
+ str('Started path at %.1f %.1f %.1f', p~'x', p~'y', p~'z');
);
-// adjusts current rotation so we don\'t spin around like crazy
-__adjusted_rot(previous_rot, current_rot) ->
+// gets current player controlling the path, or fails
+__get_player() ->
(
- while( abs(previous_rot-current_rot) > 180, 1000,
- current_rot += if(previous_rot < current_rot, -360, 360)
+ if (!global_player, exit('No player selected'));
+ p = player(global_player);
+ if (p == null, exit('Player logged out'));
+ p;
+);
+
+// clears current path
+clear() ->
+(
+ global_points = l();
+ global_dimension = null;
+ global_player = null;
+ global_showing_path = false;
+ global_playing_path = false;
+ __update();
+ 'Path cleared';
+);
+
+// camera position for the player sticks out of their eyes
+__camera_position() -> (__get_player()~'location' + global_player_eye_offset);
+
+// path changed, now notify all processes that they need to update;
+__update() ->
+(
+ global_path_precalculated = null;
+ global_playing_path = false;
+ global_needs_updating = true;
+);
+
+// ensures that the current execution context can modify the path
+__assert_can_modify_path() ->
+(
+ if (!global_points, exit('Path is not setup for current player'));
+ if (!global_showing_path, exit('Turn on path showing to edit key points'));
+ if(!global_player || !global_dimension || __get_player()~'dimension' != global_dimension,
+ exit('Player not in dimension'));
+);
+
+// make sure selected point is correct
+__assert_point_selected(validator) ->
+(
+ __assert_can_modify_path();
+ if (!call(validator, global_selected_point), exit('No appropriate point selected'));
+);
+
+//select a custom interpolation method
+interpolation(method) -> __interpolation(method, true);
+__interpolation(method, verbose) ->
+(
+ __prepare_path_if_needed() -> __prepare_path_if_needed_generic();
+ // each supported method needs to specify its __find_position_for_point to trace the path
+ // accepting segment number, and position in the segment
+ // or optionally __prepare_path_if_needed, if path is inefficient to compute point by point
+ global_interpolator = if (
+ method == 'linear', '__interpolator_linear',
+ method == 'cr', '__interpolator_cr',
+ method == 'gauss', _(s, p) -> __interpolator_gauB(s, p, 0),
+ method ~ '^gauss_',
+ (
+ type = method - 'gauss_';
+ type = replace(type,'_','.');
+ variance = round(60*number(type));
+ _(s, p, outer(variance)) -> __interpolator_gauB(s, p, variance);
+ ),
+ exit('Choose one of the following methods: linear, gauss, gauss_, cr')
);
- current_rot
+ __update();
+ if(verbose, 'Interpolation changed to '+method, '');
+);
+__interpolation('cr', false);
+
+// adds a point to the end of the path with delay in seconds
+add(delay) ->
+(
+ __assert_can_modify_path();
+ //mode is currently unused, run_path does always sharp, gauss interpolator is always smooth
+ // but this option could be used could be used at some point by more robust interpolators
+ __add_path_segment(__camera_position(), round(60*delay), 'smooth', true);
+ __update();
+ __get_path_size_string();
);
-__print_path_size() ->
+// prepends the path with a new starting point, with a segment of specified delay
+prepend(delay) ->
(
- if (!__assert_valid_for_motion(),
- print(str('%d points, %.1f secs', length(global_points), global_points:(-1):1/60));
- );
- '';
+ __assert_can_modify_path();
+ __add_path_segment(__camera_position(), round(60*delay), 'smooth', false);
+ __update();
+ __get_path_size_string();
);
-repeat(times, last_section_duration) ->
+// repeats existing points seveal times, using last section delay (seconds) to join points
+repeat(times, last_section_delay) ->
(
- if (err = __assert_valid_for_motion(), exit(err));
+ if (err = __is_not_valid_for_motion(), exit(err));
positions = map(global_points, _:0);
modes = map(global_points, _:(-1));
durations = map(global_points, global_points:(_i+1):1 - _:1 );
durations:(-1) = round(60*last_section_duration);
loop(times,
loop( length(positions),
- __add_path_segment(positions:_, durations:_, modes:_)
+ __add_path_segment(positions:_, durations:_, modes:_, true)
)
);
__update();
- __print_path_size();
- '';
+ __get_path_size_string();
);
+
+//stretches or shrinks current path to X percent of what it was before
stretch(percentage) ->
(
- if (err = __assert_valid_for_motion(), exit(err));
- undef('global_path_precalculated');
+ if (err = __is_not_valid_for_motion(), exit(err));
if (percentage < 25 || percentage > 400,
exit('path speed can only be speed, or slowed down 4 times. Re-call command for larger changes')
);
@@ -146,98 +219,106 @@ stretch(percentage) ->
)
);
-select_interpolation(method) ->
+// moves current selected point to player location
+move() ->
(
- __prepare_path_if_needed() -> __prepare_path_if_needed_generic();
- // each supported method needs to specify its __find_position_for_point to trace the path
- // accepting segment number, and position in the segment
- // or optionally __prepare_path_if_needed, if path is inefficient to compute point by point
- global_interpolator = if (
- method == 'linear', '__interpolator_linear',
- method == 'cr', '__interpolator_cr',
- method == 'gauss', _(s, p) -> __interpolator_gauss(s, p, 0),
- method ~ '^gauss_',
- (
- type = method - 'gauss_';
- variance = number(type);
- _(s, p, outer(variance)) -> __interpolator_gauss(s, p, variance);
- ),
- exit('Choose one of the following methods: linear, gauss, gauss_, cr')
- );
- __update();
- 'Ok'
+ __assert_point_selected(_(p) -> p != null);
+ global_points:global_selected_point:0 = __camera_position();
+ __update();
+ '';
);
-select_interpolation('cr');
-__assert_valid_for_motion() ->
+// chenges duration of the current selected segment to X seconds
+duration(amount) ->
(
- if(!global_points, return('Path not defined yet'));
- if(length(global_points)<2, return('Path not complete - add more points'));
- null
-);
-__get_path_at(segment, start, index) ->
-(
- v = global_path_precalculated:(start+index);
- if(v == null,
- v = call(global_interpolator, segment, index);
- global_path_precalculated:(start+index) = v
+ __assert_point_selected(_(p) -> p); // skips nulls and 0 - starting point
+ duration = number(amount);
+ new_ticks = round(duration * 60);
+ if (new_ticks < 10, return());
+ previous_ticks = global_points:global_selected_point:1-global_points:(global_selected_point-1):1;
+ delta = new_ticks - previous_ticks;
+ // adjust duration of all points after that.
+ for (range(global_selected_point, length(global_points)),
+ global_points:_:1 += delta;
);
- v
-);
-__invalidate_points_cache() -> global_path_precalculated = map(range(global_points:(-1):1), null);
-
-global_showing_path = false;
-global_playing_path = false;
-
-hide() ->
-(
- if (global_showing_path,
- global_showing_path = false;
- print('Stopped showing path');
- );
- ''
+ __update();
+ __get_path_size_string();
);
-clear() ->
+// deletes current keypoint without changing the path length
+delete_point() ->
(
- global_points = l();
- __update();
+ __assert_point_selected(_(p) -> p != null);
+ if (length(global_points) < 2, clear(); return());
+ if (global_selected_point == 0, global_points:1:1 = 0);
+ global_points = filter(global_points, _i != global_selected_point);
+ if (global_selected_point >= length(global_points), global_selected_point = null);
+ __update();
+ __get_path_size_string();
);
-global_needs_updating = false;
-global_selected_point = null;
-global_markers = null;
-
-__distsq(vec1, vec2) -> reduce(vec1 - vec2, _a + _*_, 0);
-
-__closest_point_to_center(center, points) ->
+// splits current selected segment in half by adding a keypoint in between
+split_point() ->
(
- dd = __distsq(points:0, center);
- for(points,
- d = __distsq(_, center);
- if( d p); // skips nulls and 0 - starting point
+ current_time = global_points:global_selected_point:1;
+ previous_time = global_points:(global_selected_point-1):1;
+ segment_duration = current_time-previous_time;
+ put(
+ global_points,
+ global_selected_point,
+ l(
+ __get_path_at(global_selected_point-1, previous_time, segment_duration/2),
+ previous_time+segment_duration/2,
+ global_points:global_selected_point:2
+ ),
+ 'insert'
);
- index;
+ __update();
+ __get_path_size_string();
);
-select() ->
+// removes all points in the path from the current point
+trim_path() ->
+(
+ __assert_point_selected(_(p) -> p != null);
+ global_points = slice(global_points, 0, global_selected_point);
+ global_selected_point = null;
+ __update();
+ __get_path_size_string();
+);
+
+// moves entire camera path keeping the angles to player position being in the starting point
+reposition() ->
+(
+ __assert_can_modify_path();
+ shift = pos(__get_player())-slice(global_points:0:0, 0, 3);
+ shift += 0; shift += 0;
+ shift = shift + global_player_eye_offset;
+ for(global_points, _:0 = _:0 + shift);
+ __update();
+ __get_path_size_string();
+);
+
+// selects either a point of certain number (starting from 1), or closest point
+select(num) ->
(
if (!global_points, return());
if (!global_showing_path, return());
- p = player();
- selected_point = __closest_point_to_center(
- p~'pos'+l(0,p~'eye_height',0),
- map(global_points, slice(_:0, 0, 3))
+ p = __get_player();
+ num = (num+1000*(length(global_points)+1)) % (length(global_points)+1);
+ selected_point = if (num, num-1,
+ __closest_point_to_center(
+ p~'pos'+l(0,p~'eye_height',0),
+ map(global_points, slice(_:0, 0, 3))
+ )
);
global_selected_point = if (global_selected_point == selected_point, null, selected_point);
global_needs_updating = true;
// no need to _update since path is still valid
-
);
+// player can also punch the mannequin to select/deselect it
__on_player_attacks_entity(p, e) ->
(
if (e~'type' == 'armor_stand' && query(e, 'has_tag', '__scarpet_marker_camera') && global_markers,
@@ -251,12 +332,80 @@ __on_player_attacks_entity(p, e) ->
);
);
-global_show_thread = null;
+// adds new segment to the path
+__add_path_segment(vector, duration, mode, append) ->
+(
+ if ( (l('sharp','smooth') ~ mode) == null, exit('use smooth or sharp point'));
+ l(v, segment_time, m) = global_points:(if(append, -1, 0));
+ vector:(-2) = __adjusted_rot(v:(-2), vector:(-2));
+ if (append,
+ global_points += l(vector, segment_time+duration, mode);
+ ,
+ new_points = l(l(vector, 0, mode));
+ for (global_points,
+ _:1 += duration;
+ new_points += _;
+ );
+ global_points = new_points;
+ );
+ null;
+);
+// adjusts current rotation so we don't spin around like crazy
+__adjusted_rot(previous_rot, current_rot) ->
+(
+ while( abs(previous_rot-current_rot) > 180, 1000,
+ current_rot += if(previous_rot < current_rot, -360, 360)
+ );
+ current_rot
+);
+
+// returns current path size blurb
+__get_path_size_string() ->
+(
+ if (!__is_not_valid_for_motion(),
+ str('%d points, %.1f secs', length(global_points), global_points:(-1):1/60);
+ ,
+ 'Path too small to run';
+ );
+);
+
+// checks if the current path is valid for motion
+__is_not_valid_for_motion() ->
+(
+ if(!global_points, return('Path not defined yet'));
+ if(length(global_points)<2, return('Path not complete - add more points'));
+ if(!global_dimension || __get_player()~'dimension' != global_dimension, return('Wrong dimension'));
+ false
+);
+
+// grabs position of a player for a given segment, which segment starts at start point, and offset by index points
+__get_path_at(segment, start, index) ->
+(
+ v = global_path_precalculated:(start+index);
+ if(v == null,
+ v = call(global_interpolator, segment, index);
+ global_path_precalculated:(start+index) = v
+ );
+ v
+);
+
+// squared euclidean distance between two points
+__distsq(vec1, vec2) -> reduce(vec1 - vec2, _a + _*_, 0);
+
+//finds index of the closest point from a list to the center point
+__closest_point_to_center(center, points) ->
+(
+ reduce(points,
+ d = __distsq(_, center); if( d
(
- p = player();
- __print_path_size();
+ __get_path_size_string();
if (global_showing_path, return ());
global_showing_path = true;
global_needs_updating= false;
@@ -264,9 +413,9 @@ show() ->
(
map(global_points || l(),
is_selected = global_selected_point != null && _i == global_selected_point;
- caption = if (_i == 0, 'start', str('%.1fs', global_points:_i:1/60); );
+ caption = if (_i == 0, '1: Start', str('%d: %.1fs', _i+1, global_points:_i:1/60); );
if (is_selected && _i > 0,
- caption += str(' (%.1f current segment)', (global_points:_i:1 - global_points:(_i-1):1)/60);
+ caption += str(' (%.1fs current segment)', (global_points:_i:1 - global_points:(_i-1):1)/60);
);
m = create_marker(caption, _:0, 'observer');
if (is_selected,
@@ -277,11 +426,11 @@ show() ->
);
__show_path_tick() ->
(
- if (__assert_valid_for_motion(), return());
+ if (__is_not_valid_for_motion(), return());
__prepare_path_if_needed();
- loop(100,
+ loop(global_particle_density,
segment = floor(rand(length(global_points)-1));
- particle_type = if ((segment+1) == global_selected_point, 'dust 0.1 0.9 0.1 1','dust 0.6 0.6 0.6 1');
+ particle_type = if ((segment+1) == global_selected_point, global_color_a, global_color_b); //'dust 0.1 0.9 0.1 1', 'dust 0.6 0.6 0.6 1');
start = global_points:segment:1;
end = global_points:(segment+1):1;
index = floor(rand(end-start));
@@ -291,10 +440,11 @@ show() ->
null
);
- task( _(outer(p)) -> (
+ task( _() -> (
global_markers = __create_markers();
loop(7200,
if(!global_showing_path, break());
+ __get_player();
if (global_needs_updating,
global_needs_updating = false;
for(global_markers, modify(_,'remove'));
@@ -310,60 +460,36 @@ show() ->
'';
);
-move() ->
+// hides path display
+hide() ->
(
- if (global_selected_point == null, return());
- if (!global_showing_path, return());
- global_points:global_selected_point:0 = __camera_position(player());
- __update();
-);
-
-duration(amount) ->
-(
- if (!global_selected_point, return()); // skip nulls, and 0 (first one)
- if (!global_showing_path, return());
- duration = number(amount);
- new_ticks = round(duration * 60);
- if (new_ticks < 10, return());
- previous_ticks = global_points:global_selected_point:1-global_points:(global_selected_point-1):1;
- delta = new_ticks - previous_ticks;
- // adjust duration of all points after that.
- for (range(global_selected_point, length(global_points)),
- global_points:_:1 += delta;
- );
- __update();
-);
-
-delete_point() ->
-(
- if (global_selected_point == null, return()); // skip nulls, and 0 (first one)
- if (!global_showing_path, return());
- if (length(global_points) < 2, clear(); return());
- if (global_selected_point == 0, global_points:1:1 = 0);
- global_points = filter(global_points, _i != global_selected_point);
- __update();
-);
-
-trim_path() ->
-(
- if (global_selected_point == null, return()); // skip nulls, and 0 (first one)
- if (!global_showing_path, return());
- global_points = slice(global_points, 0, global_selected_point);
- __update();
+ if (global_showing_path,
+ global_showing_path = false;
+ 'Stopped showing path';
+ ,
+ ''
+ );
);
+// runs the player on the path
play() ->
(
- p = player();
- if (err = __assert_valid_for_motion(), exit(err));
+ if (err = __is_not_valid_for_motion(), exit(err));
__prepare_path_if_needed();
- task( _(outer(fps), outer(p)) -> (
- sleep(1000);
+ if (!__get_player() || __get_player()~'dimension' != global_dimension, exit('No player in dimension'));
+ task( _() -> (
+ if (global_playing_path, // we don't want to join_task not to lock it just in case. No need to panic here
+ global_playing_path = false; // cancel current path rendering
+ sleep(1500); // in case we interrupt previous playbacks
+ );
+ showing_path = global_showing_path;
+ hide();
+ sleep(2000); // so particles can discipate
global_playing_path = true;
mspt = 1000 / 60;
start_time = time();
point = 0;
- player_offset = l(0,p~'eye_height',0,0,0);
+ p = __get_player();
try (
loop( length(global_points)-1, segment = _;
start = global_points:segment:1;
@@ -371,7 +497,7 @@ play() ->
loop(end-start,
if (p~'sneaking', global_playing_path = false);
if (!global_playing_path, throw());
- v = __get_path_at(segment, start, _)-player_offset;
+ v = __get_path_at(segment, start, _)-global_player_eye_offset;
modify(p, 'location', v);
point += 1;
end_time = time();
@@ -383,15 +509,18 @@ play() ->
);
sleep(1000);
global_playing_path = false;
- print('Done!');
+ if (showing_path, show());
));
'';
);
+// prepares empty path to fit new points
__prepare_path_if_needed_generic() ->
(
- if(!global_path_precalculated, __invalidate_points_cache())
+ if(!global_path_precalculated, global_path_precalculated = map(range(global_points:(-1):1), null))
);
+
+// linear interpolator
__interpolator_linear(segment, point) ->
(
l(va, start, mode_a) = global_points:segment;
@@ -400,10 +529,14 @@ __interpolator_linear(segment, point) ->
dt = point/section;
dt*vb+(1-dt)*va
);
+
+// normal distribution should look like that
//(1/sqrt(2*pi*d*d))*euler^(-((x-miu)^2)/(2*d*d))
// but we will be normalizing anyways, so who cares
__norm_prob(x, miu, d) -> euler^(-((x-miu)^2)/(2*d*d));
-__interpolator_gauss(from_index, point, deviation) ->
+
+//gauB interpolator
+__interpolator_gauB(from_index, point, deviation) ->
(
components = l();
path_point = global_points:from_index:1;
@@ -417,7 +550,7 @@ __interpolator_gauss(from_index, point, deviation) ->
0.6*reduce(devs, _a+_, 0)/length(devs)
);
impact = __norm_prob(path_point+point, ptime, dev);
- if(rtotal && impact < 0.000001*rtotal, throw());
+ //if(rtotal && impact < 0.000001*rtotal, throw()); // can work badly on segments with vastly diff lengths
components += l(v, impact);
rtotal += impact
)
@@ -432,7 +565,7 @@ __interpolator_gauss(from_index, point, deviation) ->
0.6*reduce(devs, _a+_, 0)/length(devs)
);
impact = __norm_prob(path_point+point, ptime, dev);
- if(ltotal && impact < 0.000001*ltotal, throw());
+ //if(ltotal && impact < 0.000001*ltotal, throw());
components += l(v, impact);
ltotal += impact
)
@@ -441,6 +574,7 @@ __interpolator_gauss(from_index, point, deviation) ->
reduce(components, _a+_:0*(_:1/total), l(0,0,0,0,0))
);
+// Catmull-Rom spline
__interpolator_cr(from_index, point) ->
(
total = global_points:(from_index+1):1 - global_points:from_index:1;
@@ -450,4 +584,38 @@ __interpolator_cr(from_index, point) ->
p_2 = global_points:(if(from_index == (length(global_points)-2), -1, from_index+2)):0;
r = point/total; // ratio within segment
(r*((2-r)*r-1) * p__1 + (r*r*(3*r-5)+2) * p_0 + r*((4 - 3*r)*r + 1) * p_1 + (r-1)*r*r * p_2) / 2
-);
\ No newline at end of file
+);
+
+// store current path in a world file
+store(file) ->
+(
+ if (!global_points, exit('No path to save'));
+ path_nbt = nbt('{}');
+ for (global_points,
+ point_nbt = nbt('{}');
+ point_nbt:'duration' = _:1;
+ point_nbt:'type' = _:2;
+ for(_:0, put(point_nbt:'pos',_,_i));
+ put(path_nbt:'points', point_nbt, _i);
+ );
+ store_app_data(path_nbt, file);
+);
+
+// loads path under the local file that
+load(file) ->
+(
+ path_nbt = load_app_data(file);
+ if (!path_nbt, exit('No path to load: '+file));
+ new_points = map(get(path_nbt, 'points[]'), l(_:'pos[]', _:'duration', _:'type'));
+ if (!new_points || first(new_points, length(_:0) != 5),
+ exit('Incorrect data for :'+file);
+ );
+ __start_with(_(outer(new_points)) -> new_points);
+);
+
+// when closing - shut-down visualization and playback threads
+__on_close() ->
+(
+ global_playing_path = false;
+ global_showing_path = false;
+);
diff --git a/src/main/resources/assets/carpet/scripts/event_test.sc b/src/main/resources/assets/carpet/scripts/event_test.sc
index a372c17b..4ac8d59e 100644
--- a/src/main/resources/assets/carpet/scripts/event_test.sc
+++ b/src/main/resources/assets/carpet/scripts/event_test.sc
@@ -158,4 +158,9 @@ __on_player_drops_stack(player) -> (
print('');
print('__on_player_drops_stack(player)');
print('Player '+player+' drops current stack.')
+);
+
+__on_statistic(player, category, event, value) ->
+(
+ print('__on_statistic(player, category, event, value) -> ['+join(', ',l(player, category, event, value))+']');
);
\ No newline at end of file
diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json
index 5ce2fe8d..0ff0fc32 100644
--- a/src/main/resources/carpet.mixins.json
+++ b/src/main/resources/carpet.mixins.json
@@ -141,6 +141,7 @@
"DamageTracker_scarpetEventMixin",
"ServerPlayerEntity_scarpetEventMixin",
"FallingBlockEntity_scarpetEventsMixin",
+
"ChunkTicketManager_scarpetMixin",
"ServerChunkManager_scarpetMixin",
From ce744802cfd1033132d546b9da8cb45602ccb71a Mon Sep 17 00:00:00 2001
From: gnembon
Date: Wed, 25 Dec 2019 03:10:32 -0400
Subject: [PATCH 14/14] cleanup
---
src/main/java/carpet/script/Expression.java | 11 ++---------
src/main/resources/assets/carpet/scripts/camera.sc | 8 ++++----
2 files changed, 6 insertions(+), 13 deletions(-)
diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java
index 9ff9e44b..0bc27604 100644
--- a/src/main/java/carpet/script/Expression.java
+++ b/src/main/java/carpet/script/Expression.java
@@ -3045,16 +3045,9 @@ public class Expression implements Cloneable
return ((ThreadValue) v).join();
});
- /*addUnaryFunction("stop_task", (v) -> {
+ addUnaryFunction("task_completed", (v) -> {
if (!(v instanceof ThreadValue))
- throw new InternalExpressionException("'join_task' could only be used with a task value");
- ((ThreadValue) v).stop();
- return Value.NULL;
- });*/
-
- addUnaryFunction("is_task_completed", (v) -> {
- if (!(v instanceof ThreadValue))
- throw new InternalExpressionException("'join_task' could only be used with a task value");
+ throw new InternalExpressionException("'task_completed' could only be used with a task value");
return new NumericValue(((ThreadValue) v).isFinished());
});
diff --git a/src/main/resources/assets/carpet/scripts/camera.sc b/src/main/resources/assets/carpet/scripts/camera.sc
index ffd9130c..225f6aec 100644
--- a/src/main/resources/assets/carpet/scripts/camera.sc
+++ b/src/main/resources/assets/carpet/scripts/camera.sc
@@ -8,7 +8,7 @@ __command() ->
print(' "/camera prepend " - prepend point seconds before');
print(' "/camera clear" - removes entire path');
print(' "/camera select" - or punch a point, to select it');
- print(' "/camera move" - moves selected camera to current player location');
+ print(' "/camera move" - moves selected point toplayer location');
print(' "/camera duration " - resizes current selected path');
print(' "/camera split_point>" - splits current selected path in half');
print(' "/camera delete_point" - removes current keypoint');
@@ -16,9 +16,9 @@ __command() ->
print('');
print(' "/camera store "');
print(' "/camera load "');
- print(' - stores and loads path from nbt files in /scripts world folder');
+ print(' - stores and loads paths from files in /scripts folder');
print('');
- print(' "/camera interpolation < linear | gauss | cr | gauss_ >"');
+ print(' "/camera interpolation < linear | gauss | cr | gauss_ >"');
print(' Selects interpolation between points:');
print(' - cr: Catmull-Rom interpolation (default).');
print(' smooth path that goes through all points.');
@@ -42,7 +42,7 @@ __command() ->
print(' use "sneak" to stop it prematurely');
print(' run /camera hide and F1 to clear the view');
print('');
- print(' "/camera show": shows current path for a moment');
+ print(' "/camera show": shows current path');
print(' color of particles used is different for different players');
print(' "/camera hide": hides path display');
print('');