Add better control over expression timeouts. (#451)

Add better control over expression timeouts.
* //timeout command can be used to change player's current timeout.
* Config now also has a max timeout, can be bypassed with permission
* Timeout of < 0 will let expressions run indefinitely.
* Said expressions won't run on a separate thread, slightly reducing the
  overhead from context switching. For large //gen commands, for example,
  this can actually increase speed.
This commit is contained in:
wizjany
2019-03-06 19:58:32 -05:00
committed by GitHub
parent f84f3c6f85
commit de08c8b8c7
21 changed files with 301 additions and 76 deletions

View File

@ -21,6 +21,7 @@ package com.sk89q.worldedit.function;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.regions.Region;
@ -32,6 +33,7 @@ public class EditContext {
private Extent destination;
@Nullable private Region region;
@Nullable private Pattern fill;
@Nullable private LocalSession session;
public Extent getDestination() {
return destination;
@ -60,4 +62,12 @@ public class EditContext {
this.fill = fill;
}
@Nullable
public LocalSession getSession() {
return session;
}
public void setSession(@Nullable LocalSession session) {
this.session = session;
}
}

View File

@ -23,6 +23,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static com.sk89q.worldedit.util.GuavaUtil.firstNonNull;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.NullExtent;
@ -147,7 +149,9 @@ public class Deform implements Contextual<Operation> {
unit = Vector3.ONE;
}
return new DeformOperation(context.getDestination(), region, zero, unit, expression);
LocalSession session = context.getSession();
return new DeformOperation(context.getDestination(), region, zero, unit, expression,
session == null ? WorldEdit.getInstance().getConfiguration().calculationTimeout : session.getTimeout());
}
private static final class DeformOperation implements Operation {
@ -156,20 +160,22 @@ public class Deform implements Contextual<Operation> {
private final Vector3 zero;
private final Vector3 unit;
private final String expression;
private final int timeout;
private DeformOperation(Extent destination, Region region, Vector3 zero, Vector3 unit, String expression) {
private DeformOperation(Extent destination, Region region, Vector3 zero, Vector3 unit, String expression, int timeout) {
this.destination = destination;
this.region = region;
this.zero = zero;
this.unit = unit;
this.expression = expression;
this.timeout = timeout;
}
@Override
public Operation resume(RunContext run) throws WorldEditException {
try {
// TODO: Move deformation code
((EditSession) destination).deformRegion(region, zero, unit, expression);
((EditSession) destination).deformRegion(region, zero, unit, expression, timeout);
return null;
} catch (ExpressionException e) {
throw new RuntimeException("Failed to execute expression", e); // TODO: Better exception to throw here?

View File

@ -21,6 +21,7 @@ package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.internal.expression.Expression;
import com.sk89q.worldedit.internal.expression.ExpressionException;
import com.sk89q.worldedit.internal.expression.runtime.EvaluationException;
@ -28,6 +29,7 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment;
import javax.annotation.Nullable;
import java.util.function.IntSupplier;
/**
* A mask that evaluates an expression.
@ -38,6 +40,7 @@ import javax.annotation.Nullable;
public class ExpressionMask extends AbstractMask {
private final Expression expression;
private final IntSupplier timeout;
/**
* Create a new instance.
@ -46,8 +49,7 @@ public class ExpressionMask extends AbstractMask {
* @throws ExpressionException thrown if there is an error with the expression
*/
public ExpressionMask(String expression) throws ExpressionException {
checkNotNull(expression);
this.expression = Expression.compile(expression, "x", "y", "z");
this(Expression.compile(checkNotNull(expression), "x", "y", "z"));
}
/**
@ -56,8 +58,13 @@ public class ExpressionMask extends AbstractMask {
* @param expression the expression
*/
public ExpressionMask(Expression expression) {
this(expression, null);
}
public ExpressionMask(Expression expression, @Nullable IntSupplier timeout) {
checkNotNull(expression);
this.expression = expression;
this.timeout = timeout;
}
@Override
@ -66,7 +73,12 @@ public class ExpressionMask extends AbstractMask {
if (expression.getEnvironment() instanceof WorldEditExpressionEnvironment) {
((WorldEditExpressionEnvironment) expression.getEnvironment()).setCurrentBlock(vector.toVector3());
}
return expression.evaluate(vector.getX(), vector.getY(), vector.getZ()) > 0;
if (timeout == null) {
return expression.evaluate(vector.getX(), vector.getY(), vector.getZ()) > 0;
} else {
return expression.evaluate(new double[]{vector.getX(), vector.getY(), vector.getZ()},
timeout.getAsInt()) > 0;
}
} catch (EvaluationException e) {
return false;
}
@ -75,7 +87,7 @@ public class ExpressionMask extends AbstractMask {
@Nullable
@Override
public Mask2D toMask2D() {
return new ExpressionMask2D(expression);
return new ExpressionMask2D(expression, timeout);
}
}

View File

@ -21,14 +21,19 @@ package com.sk89q.worldedit.function.mask;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.internal.expression.Expression;
import com.sk89q.worldedit.internal.expression.ExpressionException;
import com.sk89q.worldedit.internal.expression.runtime.EvaluationException;
import com.sk89q.worldedit.math.BlockVector2;
import javax.annotation.Nullable;
import java.util.function.IntSupplier;
public class ExpressionMask2D extends AbstractMask2D {
private final Expression expression;
private final IntSupplier timeout;
/**
* Create a new instance.
@ -37,8 +42,7 @@ public class ExpressionMask2D extends AbstractMask2D {
* @throws ExpressionException thrown if there is an error with the expression
*/
public ExpressionMask2D(String expression) throws ExpressionException {
checkNotNull(expression);
this.expression = Expression.compile(expression, "x", "z");
this(Expression.compile(checkNotNull(expression), "x", "z"));
}
/**
@ -47,14 +51,23 @@ public class ExpressionMask2D extends AbstractMask2D {
* @param expression the expression
*/
public ExpressionMask2D(Expression expression) {
this(expression, null);
}
public ExpressionMask2D(Expression expression, @Nullable IntSupplier timeout) {
checkNotNull(expression);
this.expression = expression;
this.timeout = timeout;
}
@Override
public boolean test(BlockVector2 vector) {
try {
return expression.evaluate(vector.getX(), 0, vector.getZ()) > 0;
if (timeout != null) {
return expression.evaluate(vector.getX(), 0, vector.getZ()) > 0;
} else {
return expression.evaluate(new double[]{vector.getX(), 0, vector.getZ()}, timeout.getAsInt()) > 0;
}
} catch (EvaluationException e) {
return false;
}