package com.asteria.task; import java.util.Objects; import com.google.common.base.Preconditions; /** * An assignment that has been scheduled to be completed sometime in the future. * These tasks run in intervals of {@code 600}ms and are executed on the main * game thread. They support dynamic delay changes, which means that the delay * for the task can be changed during the execution of the task. To conclude, * tasks have the ability to have any Object attached to them which can later be * utilized for things like stopping the task. * <p> * <p> * The data structures that hold tasks are not thread safe, which means tasks * should not be used across multiple threads. Tasks should only ever be used to * execute game logic. * * @author lare96 <http://github.com/lare96> */ public abstract class Task { /** * The default attachment key for all tasks. */ public static final Object DEFAULT_KEY = new Object(); /** * The delay for this task. */ private int delay; /** * The pause delay for this task. */ private int pauseDelay; /** * The counter used for determining when this task executes. */ private int counter; /** * Determines if this task executes when submitted. */ private final boolean instant; /** * The attachment key that will be used by this task. */ private Object key; /** * Determines if this task is running. */ private boolean running; /** * Creates a new {@link Task}. * * @param delay * the delay for this task. * @param instant * if this task executes when submitted. */ public Task(int delay, boolean instant) { Preconditions.checkArgument(delay >= 0); this.delay = delay; this.instant = instant; this.running = true; attach(DEFAULT_KEY); } /** * The code that will be executed by this task. This should rarely; if ever, * be invoked unless by a {@linkplain TaskQueue task queue}. Illegal * invocation of this method will lead to unpredictable issues depending on * the contents of the task. */ public abstract void execute(); /** * The method executed when this task is submitted to the task manager. */ public void onSubmit() { } /** * The method executed every {@code 600}ms when this task is sequenced. */ public void onSequence() { } /** * The method executed when this task is cancelled using {@code cancel()}. */ public void onCancel() { } /** * The method executed when {@code execute()} throws an error. * * @param t * the error thrown by execution of the task. */ public void onThrowable(Throwable t) { } /** * Determines if this task needs to be executed. * * @return {@code true} if this task needs to be executed, {@code false} * otherwise. */ public final boolean needsExecute() { if (pauseDelay > 0) { pauseDelay--; if (pauseDelay != 0) return false; } if (++counter >= delay && running) { counter = 0; return true; } return false; } /** * Cancels this task and executes the {@code onCancel()} method only if this * task is running. */ public final void cancel() { if (running) { running = false; onCancel(); } } /** * Pauses this task, or in other words temporarily cancels it for * {@code duration}. * * @param duration * the duration to pause this task for. */ public final void pause(int duration) { if (pauseDelay > 0) throw new IllegalStateException("This task is already paused!"); this.pauseDelay = duration; } /** * Attaches {@code key} to this task that can later be retrieved with * {@code getKey()}. This is a very useful feature because similar or * related tasks can be bound with the same key, and can then be retrieved * or cancelled later on. All player related tasks should be bound with the * player's instance so all tasks are automatically stopped on logout. * <p> * <p> * Keys with a value of {@code null} are <b>not</b> permitted, the default * value for all keys is {@code DEFAULT_KEY}. * * @param key * the key to bind to this task, cannot be {@code null}. * @return an instance of this task. */ public final Task attach(Object key) { this.key = Objects.requireNonNull(key); return this; } /** * Sets the new delay for this task in a dynamic fashion. * * @param delay * the new delay to set for this task. */ public final void newDelay(int delay) { Preconditions.checkArgument(delay >= 0); this.delay = delay; } /** * Determines if this task executes when submitted. * * @return {@code true} if this task executes when submitted, {@code false} * otherwise. */ public final boolean isInstant() { return instant; } /** * Gets the attachment key that will be used by this task. * * @return the attachment key. */ public final Object getKey() { return key; } /** * Determines if this task is running. * * @return {@code true} if this task is running, {@code false} otherwise. */ public final boolean isRunning() { return running; } }