threading
This commit is contained in:
1 parent
8752ac7011
commit
5d32213922
7 files changed
+230
-44
No files matched your search
@@ -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<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);
|
||||
}
|
||||
}
|
||||
|
||||
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) -> {
|
||||
|
||||
@@ -38,9 +38,9 @@ public class CarpetScriptServer
|
||||
//make static for now, but will change that later:
|
||||
public CarpetScriptHost globalHost;
|
||||
public Map<String, CarpetScriptHost> modules;
|
||||
long tickStart;
|
||||
public long tickStart;
|
||||
public boolean stopAll;
|
||||
Set<String> holyMoly;
|
||||
private Set<String> holyMoly;
|
||||
public CarpetEventServer events;
|
||||
|
||||
public static List<ModuleInterface> bundledModuleData = new ArrayList<ModuleInterface>(){{
|
||||
|
||||
@@ -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<String, LazyValue> variables = new HashMap<>();
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -246,7 +246,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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<Value> taskFuture;
|
||||
private CompletableFuture<Value> taskFuture;
|
||||
private long id;
|
||||
private final Object lock;
|
||||
private final boolean[] started;
|
||||
private Supplier<Value>task;
|
||||
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++;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ public abstract class Value implements Comparable<Value>, 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);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user