package net.glowstone.scheduler;
import net.glowstone.GlowServer;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.scheduler.BukkitWorker;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Represents a task which is executed periodically.
* @author Graham Edgecombe
*/
public class GlowTask extends FutureTask<Void> implements BukkitTask, BukkitWorker {
/**
* The next task ID pending.
*/
private static final AtomicInteger nextTaskId = new AtomicInteger(0);
/**
* The ID of this task.
*/
private final int taskId;
/**
* The Plugin that owns this task
*/
private final Plugin owner;
/**
* The number of ticks before the call to the Runnable.
*/
private final long delay;
/**
* The number of ticks between each call to the Runnable.
*/
private final long period;
/**
* The current number of ticks since last initialization.
*/
private long counter;
/**
* A flag indicating whether this task is to be run asynchronously
*/
private final boolean sync;
/**
* The thread this task has been last executed on, if this task is async.
*/
private Thread executionThread;
/**
* Return the last state returned by {@link #shouldExecute()}
*/
private volatile TaskExecutionState lastExecutionState = TaskExecutionState.WAIT;
/**
* A description of the runnable assigned to this task.
*/
private final String description;
/**
* Creates a new task with the specified number of ticks between
* consecutive calls to execute().
*/
public GlowTask(Plugin owner, Runnable task, boolean sync, long delay, long period) {
super(task, null);
this.taskId = nextTaskId.getAndIncrement();
this.description = task.toString();
this.owner = owner;
this.delay = delay;
this.period = period;
this.counter = 0;
this.sync = sync;
}
@Override
public String toString() {
return "GlowTask{" +
"id=" + taskId +
", plugin=" + owner +
", sync=" + sync +
": " + description +
'}';
}
/**
* Gets the ID of this task.
*/
@Override
public int getTaskId() {
return taskId;
}
@Override
public boolean isSync() {
return sync;
}
@Override
public Plugin getOwner() {
return owner;
}
@Override
public Thread getThread() {
return executionThread;
}
/**
* Stops this task.
*/
@Override
public void cancel() {
this.cancel(false);
}
/**
* Called every 'pulse' which is around 50ms in Minecraft. This method
* updates the counters and returns whether execute() should be called
* @return Execution state for this task
*/
TaskExecutionState shouldExecute() {
final TaskExecutionState execState = shouldExecuteUpdate();
lastExecutionState = execState;
return execState;
}
private TaskExecutionState shouldExecuteUpdate() {
if (isDone()) // Stop running if cancelled, exception, or not repeating
return TaskExecutionState.STOP;
++counter;
if (counter >= delay) {
if (period == -1 || (counter - delay) % period == 0) {
return TaskExecutionState.RUN;
}
}
return TaskExecutionState.WAIT;
}
/**
* Return the last execution state returned by {@link #shouldExecute()}
* @return the last state (most likely the state the task is currently in)
*/
public TaskExecutionState getLastExecutionState() {
return lastExecutionState;
}
@Override
public void run() {
executionThread = Thread.currentThread();
if (period == -1) {
super.run();
} else {
runAndReset();
}
}
@Override
protected void done() {
super.done();
if (isCancelled()) {
return;
}
try {
get();
} catch (ExecutionException ex) {
Logger log = owner == null ? GlowServer.logger : owner.getLogger();
log.log(Level.SEVERE, "Error while executing " + this, ex.getCause());
} catch (InterruptedException e) {
// Task is already done, see the fact that we're in done() method
}
}
}