Skip to content

Commit

Permalink
Added timeout mechanic
Browse files Browse the repository at this point in the history
  • Loading branch information
roseckyj committed Oct 22, 2024
1 parent 8bf9dcb commit 2c3dc23
Show file tree
Hide file tree
Showing 12 changed files with 304 additions and 136 deletions.
158 changes: 107 additions & 51 deletions server/src/main/java/cz/xrosecky/terratinker/Evaluator.java
Original file line number Diff line number Diff line change
@@ -1,76 +1,132 @@
package cz.xrosecky.terratinker;

import cz.xrosecky.terratinker.evaluation.EvaluationCanceledException;
import cz.xrosecky.terratinker.evaluation.StaticInfo;
import cz.xrosecky.terratinker.geometry.CoordsTranslator;
import cz.xrosecky.terratinker.geometry.Vector2D;
import cz.xrosecky.terratinker.geometry.Vector2DInt;
import cz.xrosecky.terratinker.geometry.Vector3DInt;
import cz.xrosecky.terratinker.server.VoidGen;
import org.bukkit.Difficulty;
import org.bukkit.World;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.WorldCreator;
import org.bukkit.WorldType;
import org.json.JSONArray;
import org.json.JSONObject;

import java.util.Timer;

public class Evaluator {
public final String id;
private static final int PREVIEW_SIZE = 16 * 4;
private final JavaPlugin plugin;
private static final int TIMEOUT_TIME = 60 * 1000;
private final TerraTinker plugin;
private final String input;
private final boolean isPreview;
private boolean running = false;
private World world;

private EvaluatorStatus status = EvaluatorStatus.READY;

public Evaluator(JavaPlugin plugin, boolean isPreview) {
public Evaluator(TerraTinker plugin, String input, boolean isPreview) {
this.id = java.util.UUID.randomUUID().toString();
this.plugin = plugin;
this.input = input;
this.isPreview = isPreview;
}

public void evaluate(String input, World world) {
running = true;
long start = System.currentTimeMillis();
try {
JSONObject config = new JSONObject(input);

JSONArray mapCenter = config.getJSONArray("mapCenter");
float lat = mapCenter.getFloat(0);
float lon = mapCenter.getFloat(1);
JSONObject scale = config.getJSONObject("scale");
float horizontalScale = scale.getFloat("horizontal");
float verticalScale = scale.getFloat("vertical");
JSONObject mapSize = config.getJSONObject("mapSize");
int width = mapSize.getInt("width");
int height = mapSize.getInt("height");
float minAltitude = config.getFloat("minAltitude");

CoordsTranslator coordsTranslator = new CoordsTranslator(new Vector2D(lat, lon), new Vector2D(0, 0), 0, horizontalScale, verticalScale, world.getMinHeight());
coordsTranslator.setAltShift((int)(-minAltitude + 10 + 10 * verticalScale));
Vector2DInt size = isPreview ? new Vector2DInt(PREVIEW_SIZE, PREVIEW_SIZE) : new Vector2DInt(width, height);

StaticInfo staticInfo = new StaticInfo(plugin, world, coordsTranslator, size, minAltitude, isPreview ? new Vector3DInt(512 / 2, 0, 512 / 2) : new Vector3DInt(0, 0, 0));

JSONArray layers = config.getJSONArray("layers");

// We want to parse the layers in reverse order (top should be last)
for (int i = layers.length() - 1; i >= 0; i--) {
JSONObject layer = layers.getJSONObject(i);

if (layer.has("disabled") && layer.getBoolean("disabled")) {
continue;
}
public void evaluate() {
// Set timeout,
Timer timer = new Timer();

timer.schedule(new java.util.TimerTask() {
@Override
public void run() {
plugin.getLogger().warning("Session " + id + " timed out");
status = EvaluatorStatus.TIMEOUT;
}
}, TIMEOUT_TIME);

plugin.getServer().getScheduler().runTask(plugin, () -> {
this.status = EvaluatorStatus.RUNNING;
long start = System.currentTimeMillis();

// Create a new world
WorldCreator creator = new WorldCreator(id);
creator.type(WorldType.FLAT);
creator.generatorSettings("{\"layers\": [{\"block\": \"air\", \"height\": 1}], \"biome\":\"plains\"}");
creator.environment(World.Environment.NORMAL);
creator.generateStructures(false);
creator.generator(new VoidGen());
world = plugin.getServer().createWorld(creator);
if (world == null) {
throw new RuntimeException("Could not create world");
}
world.setDifficulty(Difficulty.PEACEFUL);
try {
JSONObject config = new JSONObject(input);

JSONArray mapCenter = config.getJSONArray("mapCenter");
float lat = mapCenter.getFloat(0);
float lon = mapCenter.getFloat(1);
JSONObject scale = config.getJSONObject("scale");
float horizontalScale = scale.getFloat("horizontal");
float verticalScale = scale.getFloat("vertical");
JSONObject mapSize = config.getJSONObject("mapSize");
int width = mapSize.getInt("width");
int height = mapSize.getInt("height");
float minAltitude = config.getFloat("minAltitude");

CoordsTranslator coordsTranslator = new CoordsTranslator(new Vector2D(lat, lon), new Vector2D(0, 0), 0, horizontalScale, verticalScale, world.getMinHeight());
coordsTranslator.setAltShift((int)(-minAltitude + 10 + 10 * verticalScale));
Vector2DInt size = isPreview ? new Vector2DInt(PREVIEW_SIZE, PREVIEW_SIZE) : new Vector2DInt(width, height);

StaticInfo staticInfo = new StaticInfo(plugin, world, this, coordsTranslator, size, minAltitude, isPreview ? new Vector3DInt(512 / 2, 0, 512 / 2) : new Vector3DInt(0, 0, 0));

JSONArray layers = config.getJSONArray("layers");

Program tree = Program.fromJson(layer);
// We want to parse the layers in reverse order (top should be last)
for (int i = layers.length() - 1; i >= 0; i--) {
JSONObject layer = layers.getJSONObject(i);

LayerEvaluator layerEvaluator = new LayerEvaluator(tree);
layerEvaluator.evaluate(staticInfo);
if (layer.has("disabled") && layer.getBoolean("disabled")) {
continue;
}

Program tree = Program.fromJson(layer);

LayerEvaluator layerEvaluator = new LayerEvaluator(tree);
layerEvaluator.evaluate(staticInfo);
}

this.status = EvaluatorStatus.FINISHED;
} catch (EvaluationCanceledException ignored) {
} catch (Exception e) {
this.status = EvaluatorStatus.ERROR;
}
} catch (Exception e) {
e.printStackTrace();
// plugin.getLogger().warning(e.getMessage());
}
world.save();
running = false;
long finish = System.currentTimeMillis();
long timeElapsed = finish - start;
plugin.getLogger().info("Finished in " + timeElapsed + " ms");

world.save();
plugin.getServer().unloadWorld(world, false);
timer.cancel();

long finish = System.currentTimeMillis();
long timeElapsed = finish - start;
plugin.getLogger().info("Finished in " + timeElapsed + " ms");
});
}

public EvaluatorStatus getStatus() {
return status;
}

public void cancel() {
this.status = EvaluatorStatus.CANCELED;
}

public boolean shouldStop() {
return status == EvaluatorStatus.CANCELED || status == EvaluatorStatus.ERROR || status == EvaluatorStatus.TIMEOUT;
}

public boolean isRunning() {
return running;
public World getWorld() {
return world;
}
}
}
10 changes: 10 additions & 0 deletions server/src/main/java/cz/xrosecky/terratinker/EvaluatorStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package cz.xrosecky.terratinker;

public enum EvaluatorStatus {
READY,
RUNNING,
CANCELED,
FINISHED,
ERROR,
TIMEOUT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package cz.xrosecky.terratinker.evaluation;

public class EvaluationCanceledException extends RuntimeException{
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cz.xrosecky.terratinker.evaluation;

import cz.xrosecky.terratinker.Evaluator;
import cz.xrosecky.terratinker.geometry.CoordsTranslator;
import cz.xrosecky.terratinker.geometry.Vector2DInt;
import cz.xrosecky.terratinker.geometry.Vector3DInt;
Expand All @@ -9,14 +10,16 @@
public class StaticInfo {
public final JavaPlugin plugin;
public final World world;
public final Evaluator evaluator;
public final CoordsTranslator coordsTranslator;
public final Vector2DInt size;
public final float minAltitude;
public final Vector3DInt origin;

public StaticInfo(JavaPlugin plugin, World world, CoordsTranslator coordsTranslator, Vector2DInt size, float minAltitude, Vector3DInt origin) {
public StaticInfo(JavaPlugin plugin, World world, Evaluator evaluator, CoordsTranslator coordsTranslator, Vector2DInt size, float minAltitude, Vector3DInt origin) {
this.plugin = plugin;
this.world = world;
this.evaluator = evaluator;
this.coordsTranslator = coordsTranslator;
this.size = size;
this.minAltitude = minAltitude;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package cz.xrosecky.terratinker.nodes;

import java.util.HashMap;
import java.util.function.BiConsumer;

import cz.xrosecky.terratinker.evaluation.InputMap;
import cz.xrosecky.terratinker.evaluation.*;
import org.json.JSONObject;

import cz.xrosecky.terratinker.Program;
import cz.xrosecky.terratinker.evaluation.EvaluationState;
import cz.xrosecky.terratinker.evaluation.NodeOutput;
import cz.xrosecky.terratinker.evaluation.NodeOutputResolver;
import cz.xrosecky.terratinker.evaluation.outputType.AbstractType;
import cz.xrosecky.terratinker.nodeInput.AbstractNodeInput;
import cz.xrosecky.terratinker.nodeInput.LinkNodeInput;
Expand All @@ -22,6 +18,10 @@ public AbstractActionNode(String id, JSONObject json) {

protected AbstractNode actionRoutine(Program program, EvaluationState tree,
BiConsumer<InputMap, NodeOutput> resolver) {
if (tree.info().evaluator.shouldStop()) {
throw new EvaluationCanceledException();
}

NodeOutput output = new NodeOutput();
AbstractNode fork = evaluatePrerequisites(program, tree);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
package cz.xrosecky.terratinker.nodes;

import java.util.HashMap;
import java.util.function.BiFunction;

import cz.xrosecky.terratinker.evaluation.InputMap;
import cz.xrosecky.terratinker.evaluation.*;
import org.json.JSONObject;

import cz.xrosecky.terratinker.Program;
import cz.xrosecky.terratinker.evaluation.EvaluationState;
import cz.xrosecky.terratinker.evaluation.NodeOutput;
import cz.xrosecky.terratinker.evaluation.NodeOutputResolver;
import cz.xrosecky.terratinker.evaluation.outputType.AbstractType;

public abstract class AbstractForkNode extends AbstractNode {
public AbstractForkNode(String id, JSONObject json) {
Expand Down Expand Up @@ -43,6 +38,10 @@ public void teardown() {

protected boolean forkRoutine(Program program, EvaluationState tree,
BiFunction<InputMap, NodeOutput, Boolean> resolver) {
if (tree.info().evaluator.shouldStop()) {
throw new EvaluationCanceledException();
}

InputMap inputs = getInputs(tree);
NodeOutput output = new NodeOutput();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@
import java.util.List;
import java.util.function.BiConsumer;

import cz.xrosecky.terratinker.evaluation.InputMap;
import cz.xrosecky.terratinker.evaluation.*;
import org.json.JSONObject;

import cz.xrosecky.terratinker.Program;
import cz.xrosecky.terratinker.evaluation.EvaluationState;
import cz.xrosecky.terratinker.evaluation.NodeOutput;
import cz.xrosecky.terratinker.evaluation.NodeOutputResolver;
import cz.xrosecky.terratinker.evaluation.outputType.AbstractType;
import cz.xrosecky.terratinker.nodeInput.AbstractNodeInput;
import cz.xrosecky.terratinker.nodeInput.LinkNodeInput;
Expand Down Expand Up @@ -95,6 +92,9 @@ protected AbstractNode evaluateInputs(Program program, EvaluationState tree) {

protected AbstractNode evaluationRoutine(Program program, EvaluationState tree,
BiConsumer<InputMap, NodeOutput> resolver) {
if (tree.info().evaluator.shouldStop()) {
throw new EvaluationCanceledException();
}
AbstractNode fork = evaluatePrerequisites(program, tree);
if (fork == null) {
fork = evaluateInputs(program, tree);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cz.xrosecky.terratinker.nodes.minecraft;

import cz.xrosecky.terratinker.Program;
import cz.xrosecky.terratinker.evaluation.EvaluationCanceledException;
import cz.xrosecky.terratinker.evaluation.EvaluationState;
import cz.xrosecky.terratinker.nodes.AbstractActionNode;
import cz.xrosecky.terratinker.nodes.AbstractNode;
Expand Down
Loading

0 comments on commit 2c3dc23

Please sign in to comment.