threading
This commit is contained in:
1 parent
8752ac7011
commit
5d32213922
7 files changed
+218
-32
No files matched your search
@@ -101,6 +101,7 @@ import java.util.Locale;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -969,12 +970,14 @@ public class CarpetExpression
|
|||||||
|
|
||||||
if (sourceBlockState == targetBlockState && data == null)
|
if (sourceBlockState == targetBlockState && data == null)
|
||||||
return (c_, t_) -> Value.FALSE;
|
return (c_, t_) -> Value.FALSE;
|
||||||
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
|
BlockState finalSourceBlockState = sourceBlockState;
|
||||||
BlockPos targetPos = targetLocator.block.getPos();
|
BlockPos targetPos = targetLocator.block.getPos();
|
||||||
|
cc.s.getMinecraftServer().executeSync( () ->
|
||||||
|
{
|
||||||
|
CarpetSettings.impendingFillSkipUpdates = !CarpetSettings.fillUpdates;
|
||||||
Clearable.clear(world.getBlockEntity(targetPos));
|
Clearable.clear(world.getBlockEntity(targetPos));
|
||||||
world.setBlockState(targetPos, sourceBlockState, 2);
|
world.setBlockState(targetPos, finalSourceBlockState, 2);
|
||||||
|
if (data != null)
|
||||||
if ( data != null)
|
|
||||||
{
|
{
|
||||||
BlockEntity be = world.getBlockEntity(targetPos);
|
BlockEntity be = world.getBlockEntity(targetPos);
|
||||||
if (be != null)
|
if (be != null)
|
||||||
@@ -987,9 +990,63 @@ public class CarpetExpression
|
|||||||
be.markDirty();
|
be.markDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CarpetSettings.impendingFillSkipUpdates = false;
|
CarpetSettings.impendingFillSkipUpdates = false;
|
||||||
Value retval = new BlockValue(sourceBlockState, world, targetLocator.block.getPos());
|
});
|
||||||
|
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<Block, BlockState> 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;
|
return (c_, t_) -> retval;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2982,6 +3039,7 @@ public class CarpetExpression
|
|||||||
|
|
||||||
this.expr.addLazyFunction("game_tick", -1, (c, t, lv) -> {
|
this.expr.addLazyFunction("game_tick", -1, (c, t, lv) -> {
|
||||||
ServerCommandSource s = ((CarpetContext)c).s;
|
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);
|
((MinecraftServerInterface)s.getMinecraftServer()).forceTick( () -> System.nanoTime()- CarpetServer.scriptServer.tickStart<50000000L);
|
||||||
if (lv.size()>0)
|
if (lv.size()>0)
|
||||||
{
|
{
|
||||||
@@ -3064,12 +3122,19 @@ public class CarpetExpression
|
|||||||
if (lv.size() <= locator.offset)
|
if (lv.size() <= locator.offset)
|
||||||
throw new InternalExpressionException("'plop' needs extra argument indicating what to plop");
|
throw new InternalExpressionException("'plop' needs extra argument indicating what to plop");
|
||||||
String what = lv.get(locator.offset).evalValue(c).getString();
|
String what = lv.get(locator.offset).evalValue(c).getString();
|
||||||
Boolean res = FeatureGenerator.spawn(what, ((CarpetContext)c).s.getWorld(), locator.block.getPos());
|
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)
|
if (res == null)
|
||||||
return (c_, t_) -> Value.NULL;
|
return;
|
||||||
if (what.equalsIgnoreCase("boulder")) // there might be more of those
|
if (what.equalsIgnoreCase("boulder")) // there might be more of those
|
||||||
this.forceChunkUpdate(locator.block.getPos(), ((CarpetContext)c).s.getWorld());
|
this.forceChunkUpdate(locator.block.getPos(), ((CarpetContext) c).s.getWorld());
|
||||||
return (c_, t_) -> new NumericValue(res);
|
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) -> {
|
this.expr.addLazyFunction("schedule", -1, (c, t, lv) -> {
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ public class CarpetScriptServer
|
|||||||
//make static for now, but will change that later:
|
//make static for now, but will change that later:
|
||||||
public CarpetScriptHost globalHost;
|
public CarpetScriptHost globalHost;
|
||||||
public Map<String, CarpetScriptHost> modules;
|
public Map<String, CarpetScriptHost> modules;
|
||||||
long tickStart;
|
public long tickStart;
|
||||||
public boolean stopAll;
|
public boolean stopAll;
|
||||||
Set<String> holyMoly;
|
private Set<String> holyMoly;
|
||||||
public CarpetEventServer events;
|
public CarpetEventServer events;
|
||||||
|
|
||||||
public static List<ModuleInterface> bundledModuleData = new ArrayList<ModuleInterface>(){{
|
public static List<ModuleInterface> bundledModuleData = new ArrayList<ModuleInterface>(){{
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ import java.util.Set;
|
|||||||
|
|
||||||
public class Context
|
public class Context
|
||||||
{
|
{
|
||||||
static final int NONE = 0;
|
public static final int NONE = 0;
|
||||||
static final int VOID = 1;
|
public static final int VOID = 1;
|
||||||
static final int BOOLEAN = 2;
|
public static final int BOOLEAN = 2;
|
||||||
static final int NUMBER = 3;
|
public static final int NUMBER = 3;
|
||||||
static final int STRING = 4;
|
public static final int STRING = 4;
|
||||||
static final int LIST = 5;
|
public static final int LIST = 5;
|
||||||
static final int ITERATOR = 6;
|
public static final int ITERATOR = 6;
|
||||||
static final int SIGNATURE = 7;
|
public static final int SIGNATURE = 7;
|
||||||
static final int LOCALIZATION = 8;
|
public static final int LOCALIZATION = 8;
|
||||||
static final int LVALUE = 9;
|
public static final int LVALUE = 9;
|
||||||
|
|
||||||
public Map<String, LazyValue> variables = new HashMap<>();
|
public Map<String, LazyValue> variables = new HashMap<>();
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import carpet.script.value.ListValue;
|
|||||||
import carpet.script.value.MapValue;
|
import carpet.script.value.MapValue;
|
||||||
import carpet.script.value.NumericValue;
|
import carpet.script.value.NumericValue;
|
||||||
import carpet.script.value.StringValue;
|
import carpet.script.value.StringValue;
|
||||||
|
import carpet.script.value.ThreadValue;
|
||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
import org.apache.commons.lang3.text.WordUtils;
|
import org.apache.commons.lang3.text.WordUtils;
|
||||||
@@ -2992,6 +2993,67 @@ public class Expression implements Cloneable
|
|||||||
return (cc, tt) -> retval;
|
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");
|
static Expression none = new Expression("null");
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterator<Value> iterator() { return new ArrayList<>(items).iterator(); }
|
public Iterator<Value> iterator() { return new ArrayList<>(items).iterator(); } // should be thread safe
|
||||||
|
|
||||||
public void extend(List<Value> subList)
|
public void extend(List<Value> subList)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,29 +1,49 @@
|
|||||||
package carpet.script.value;
|
package carpet.script.value;
|
||||||
|
|
||||||
import carpet.script.Context;
|
import carpet.script.Context;
|
||||||
import carpet.script.LazyValue;
|
import carpet.script.Expression;
|
||||||
|
import carpet.script.Tokenizer;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
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.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class ThreadValue extends Value
|
public class ThreadValue extends Value
|
||||||
{
|
{
|
||||||
CompletableFuture<Value> taskFuture;
|
private CompletableFuture<Value> taskFuture;
|
||||||
private long id;
|
private long id;
|
||||||
|
private final Object lock;
|
||||||
|
private final boolean[] started;
|
||||||
|
private Supplier<Value>task;
|
||||||
private static long sequence = 0L;
|
private static long sequence = 0L;
|
||||||
|
|
||||||
private static final ExecutorService executorService = Executors.newCachedThreadPool();
|
private static final Map<Value,ThreadPoolExecutor> executorServices = new HashMap<>();
|
||||||
|
|
||||||
public ThreadValue(LazyValue expression, Context ctx)
|
public ThreadValue(Value pool, FunctionValue function, Expression expr, Tokenizer.Token token, Context ctx)
|
||||||
{
|
{
|
||||||
id = sequence++;
|
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(
|
taskFuture = CompletableFuture.supplyAsync(
|
||||||
() -> expression.evalValue(ctx),
|
() -> { synchronized (lock) { return task.get(); } },
|
||||||
executorService
|
executorServices.computeIfAbsent(pool, (v) -> (ThreadPoolExecutor)Executors.newCachedThreadPool())
|
||||||
);
|
);
|
||||||
|
Thread.yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -40,6 +60,18 @@ public class ThreadValue extends Value
|
|||||||
|
|
||||||
public Value join()
|
public Value join()
|
||||||
{
|
{
|
||||||
|
if (!started[0])
|
||||||
|
{
|
||||||
|
synchronized (lock)
|
||||||
|
{
|
||||||
|
if (!started[0])
|
||||||
|
{
|
||||||
|
Value res = task.get();
|
||||||
|
taskFuture.complete(res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return taskFuture.get();
|
return taskFuture.get();
|
||||||
@@ -57,8 +89,15 @@ public class ThreadValue extends Value
|
|||||||
|
|
||||||
public void stop()
|
public void stop()
|
||||||
{
|
{
|
||||||
|
synchronized (lock)
|
||||||
|
{
|
||||||
|
if (!taskFuture.isDone())
|
||||||
|
{
|
||||||
|
started[0] = true;
|
||||||
taskFuture.complete(Value.NULL);
|
taskFuture.complete(Value.NULL);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o)
|
public boolean equals(Object o)
|
||||||
@@ -72,7 +111,27 @@ public class ThreadValue extends Value
|
|||||||
public int compareTo(Value o)
|
public int compareTo(Value o)
|
||||||
{
|
{
|
||||||
if (!(o instanceof ThreadValue))
|
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);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,7 @@ public abstract class Value implements Comparable<Value>, Cloneable
|
|||||||
@Override
|
@Override
|
||||||
public int compareTo(final Value o)
|
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);
|
return -o.compareTo(this);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user