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 } } }