diff --git a/src/com/massivecraft/massivecore/Couple.java b/src/com/massivecraft/massivecore/Couple.java
index bd4750a0..4b70eb0d 100644
--- a/src/com/massivecraft/massivecore/Couple.java
+++ b/src/com/massivecraft/massivecore/Couple.java
@@ -91,7 +91,7 @@ public class Couple implements Entry, Cloneable, Serializable
this.getSecond(), that.getSecond()
);
}
-
+
// -------------------------------------------- //
// CLONE
// -------------------------------------------- //
diff --git a/src/com/massivecraft/massivecore/Task.java b/src/com/massivecraft/massivecore/Task.java
new file mode 100644
index 00000000..cc1f327e
--- /dev/null
+++ b/src/com/massivecraft/massivecore/Task.java
@@ -0,0 +1,113 @@
+package com.massivecraft.massivecore;
+
+import com.massivecraft.massivecore.collections.MassiveList;
+import com.massivecraft.massivecore.util.Txt;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * This class is for tasks that are only run infrequently such as once a day
+ * and whose frequency must stay consistent between server restarts.
+ * An example is Faction taxing which must happen exactly once a day.
+ */
+public abstract class Task extends Engine
+{
+ // -------------------------------------------- //
+ // FIELDS
+ // -------------------------------------------- //
+
+ private boolean mustBeTaskServer = false;
+ public boolean mustBeTaskServer() { return mustBeTaskServer; }
+ public void setMustBeTaskServer(boolean mustBeTaskServer) { this.mustBeTaskServer = mustBeTaskServer; }
+
+ private boolean logTimeSpent = false;
+ public boolean isLoggingTimeSpent() { return logTimeSpent; }
+ public void setLoggingTimeSpent(boolean logTimeSpent) { this.logTimeSpent = logTimeSpent; }
+
+ private List> conditions = new MassiveList<>();
+ public List> getConditions() { return conditions; }
+ public void setConditions(List> conditions) { this.conditions = conditions; }
+ public void addCondition(Supplier condition) { this.conditions.add(condition); }
+ public boolean areConditionsMet()
+ {
+ return conditions.stream().map(Supplier::get).allMatch(b -> b == true);
+ }
+
+ // -------------------------------------------- //
+ // CONSTRUCT
+ // -------------------------------------------- //
+
+ public Task()
+ {
+ this.setPeriod(60 * 20L); // Once a minute
+ }
+
+ // -------------------------------------------- //
+ // INVOKE
+ // -------------------------------------------- //
+
+ @Override
+ public void run()
+ {
+ // Should it be the task server?
+ if (this.mustBeTaskServer() && ! MassiveCore.isTaskServer()) return;
+
+ // So the delay millis is lower than one? (could for example be zero)
+ // This probably means the task should not be executed at all.
+ if (this.getPeriodMillis() < 1) return;
+
+ // Other conditions
+ if (!this.areConditionsMet()) return;
+
+ // INVOCATION
+ long nowMillis = System.currentTimeMillis();
+ long lastMillis = this.getPreviousMillis();
+
+ long currentInvocation = this.getInvocationFromMillis(nowMillis);
+ long lastInvocation = this.getInvocationFromMillis(lastMillis);
+
+ if (currentInvocation == lastInvocation) return;
+
+ // Log time spent and invoke
+ if (this.isLoggingTimeSpent())
+ {
+ String message = Txt.parse("Running %s.", this.getClass().getSimpleName());
+ this.getPlugin().log(message);
+ }
+
+ long startNano = System.nanoTime();
+ this.invoke(nowMillis);
+ this.setPreviousMillis(nowMillis);
+ long endNano = System.nanoTime();
+
+ double elapsedSeconds = (endNano - startNano) / 1000_000_000D;
+ if (this.isLoggingTimeSpent())
+ {
+ String msg = String.format("Took %.2f seconds.", elapsedSeconds);
+ msg = Txt.parse(msg);
+ this.getPlugin().log(msg);
+ }
+ }
+
+ public abstract void invoke(long now);
+
+ // -------------------------------------------- //
+ // TASK MILLIS AND INVOCATION
+ // -------------------------------------------- //
+ // The invocation is the amount of periods from UNIX time to now.
+ // It will increment by one when a period has passed.
+
+ public abstract long getPreviousMillis();
+ public abstract void setPreviousMillis(long millis);
+
+ public abstract long getPeriodMillis();
+ public abstract long getOffsetMillis();
+
+ // Here we accept millis from inside the period by rounding down.
+ private long getInvocationFromMillis(long millis)
+ {
+ return (millis - this.getOffsetMillis()) / this.getPeriodMillis();
+ }
+
+}