diff --git a/README.md b/README.md index 00ecdfa..d3aac3f 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Maven gg.flyte twilight - 1.1.13 + 1.1.14 ``` @@ -33,14 +33,14 @@ maven { url "https://repo.flyte.gg/releases" } -implementation "gg.flyte:twilight:1.1.13" +implementation "gg.flyte:twilight:1.1.14" ``` Gradle (Kotlin DSL) ```kotlin maven("https://repo.flyte.gg/releases") -implementation("gg.flyte:twilight:1.1.13") +implementation("gg.flyte:twilight:1.1.14") ``` Certain features of Twilight require configuration, which can be done via the Twilight class. To setup a Twilight class instance, you can use the `twilight` function as shown below: @@ -266,6 +266,32 @@ repeat(5, 10, TimeUnit.SECONDS, true) { > Twilight `repeat` conflicting with Kotlin's `repeat`? As an alternative, you can use `repeatingTask`. +You can also chain runnables together using `onComplete` to nicely nest sync/async executions. Here's an example: +```kotlin +async { + println("I am an async BukkitRunnable called Atom") +}.onComplete() { + println("I am an async BukkitRunnable called Brandon running immediately after Atom finishes executing") +}.onCompleteSync(10) { + println("I am a sync BukkitRunnable called Charlie running 10 ticks after Brandon finishes executing") +}.onComplete(5, TimeUnit.SECONDS) { + println("I am a sync BukkitRunnable called Dawson running 5 seconds after Charlie finishes executing") +}.onCompleteAsync { + println("I am an async BukkitRunnable called Enid running immediately after Dawson finishes executing") +} +``` +As you can see, you can specify whether sync/async (if unspecified, it will not change) and you can pass in an optional delay. +
This also works with a delay from the get-go: +```kotlin +delay(20, TimeUnit.SECONDS) { + println("I am a sync BukkitRunnable delayed by 20 seconds") +}.onCompleteAsync(10, TimeUnit.SECONDS) { + println("I am an async BukkitRunnable delayed by a further 10 seconds") +} +``` + +> Currently, onComplete is incompatible with repeating tasks. + ### GUI Builder Creating GUI's can be an incredibly long and tedious process, however, in Twilight we offer a clean and efficient way to build GUIs. diff --git a/build.gradle.kts b/build.gradle.kts index 99c34fc..fee4bf5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } group = "gg.flyte" -version = "1.1.13" +version = "1.1.14" repositories { mavenLocal() diff --git a/src/main/kotlin/gg/flyte/twilight/scheduler/Scheduler.kt b/src/main/kotlin/gg/flyte/twilight/scheduler/Scheduler.kt index f393390..f2ef835 100644 --- a/src/main/kotlin/gg/flyte/twilight/scheduler/Scheduler.kt +++ b/src/main/kotlin/gg/flyte/twilight/scheduler/Scheduler.kt @@ -12,20 +12,20 @@ import org.bukkit.scheduler.BukkitTask * Schedules a synchronous task to be executed by the Bukkit scheduler. * * @param runnable The function representing the task to be executed. - * @return The BukkitTask representing the scheduled task. + * @return The TwilightRunnable representing the scheduled task. */ -fun sync(runnable: BukkitRunnable.() -> Unit): BukkitTask { - return createBukkitRunnable(runnable).runTask(Twilight.plugin) +fun sync(runnable: TwilightRunnable.() -> Unit): TwilightRunnable { + return TwilightRunnable(runnable, false).apply { schedule() } } /** * Schedules an asynchronous task to be executed by the Bukkit scheduler. * * @param runnable The function representing the task to be executed. - * @return The BukkitTask representing the scheduled task. + * @return The TwilightRunnable representing the scheduled task. */ -fun async(runnable: BukkitRunnable.() -> Unit): BukkitTask { - return createBukkitRunnable(runnable).runTaskAsynchronously(Twilight.plugin) +fun async(runnable: TwilightRunnable.() -> Unit): TwilightRunnable { + return TwilightRunnable(runnable, true).apply { schedule() } } /** @@ -35,19 +35,15 @@ fun async(runnable: BukkitRunnable.() -> Unit): BukkitTask { * @param unit The TimeUnit representing the time unit of the delay (default: MILLISECONDS). * @param async Whether the task should be executed asynchronously (default: false). * @param runnable The function representing the task to be executed. - * @return The BukkitTask representing the scheduled task. + * @return The TwilightRunnable representing the scheduled task. */ fun delay( value: Int, unit: TimeUnit = TimeUnit.TICKS, async: Boolean = false, - runnable: BukkitRunnable.() -> Unit -): BukkitTask { - return if (async) { - createBukkitRunnable(runnable).runTaskLaterAsynchronously(Twilight.plugin, unit.toTicks(value.toLong())) - } else { - createBukkitRunnable(runnable).runTaskLater(Twilight.plugin, unit.toTicks(value.toLong())) - } + runnable: TwilightRunnable.() -> Unit +): TwilightRunnable { + return TwilightRunnable(runnable, async, unit.toTicks(value.toLong())).apply { schedule() } } /** @@ -56,10 +52,10 @@ fun delay( * * @param ticks The number of ticks for the delay (default: 1). * @param runnable The function representing the task to be executed. - * @return The BukkitTask representing the scheduled task. + * @return The TwilightRunnable representing the scheduled task. * @see delay */ -fun delay(ticks: Int = 1, runnable: BukkitRunnable.() -> Unit): BukkitTask { +fun delay(ticks: Int = 1, runnable: BukkitRunnable.() -> Unit): TwilightRunnable { return delay(ticks, TimeUnit.TICKS, false, runnable) } @@ -71,10 +67,10 @@ fun delay(ticks: Int = 1, runnable: BukkitRunnable.() -> Unit): BukkitTask { * @param ticks The number of ticks for the delay (default: 1). * @param async Whether the task should be executed asynchronously. * @param runnable The function representing the task to be executed. - * @return The BukkitTask representing the scheduled task. + * @return The TwilightRunnable representing the scheduled task. * @see delay */ -fun delay(ticks: Int = 1, async: Boolean, runnable: BukkitRunnable.() -> Unit): BukkitTask { +fun delay(ticks: Int = 1, async: Boolean, runnable: BukkitRunnable.() -> Unit): TwilightRunnable { return delay(ticks, TimeUnit.TICKS, async, runnable) } diff --git a/src/main/kotlin/gg/flyte/twilight/scheduler/TwilightRunnable.kt b/src/main/kotlin/gg/flyte/twilight/scheduler/TwilightRunnable.kt new file mode 100644 index 0000000..bde9c3a --- /dev/null +++ b/src/main/kotlin/gg/flyte/twilight/scheduler/TwilightRunnable.kt @@ -0,0 +1,117 @@ +package gg.flyte.twilight.scheduler + +import gg.flyte.twilight.Twilight +import gg.flyte.twilight.time.TimeUnit +import org.bukkit.scheduler.BukkitRunnable +import org.bukkit.scheduler.BukkitTask + +/** + * A flexible runnable wrapper class for Bukkit/Spigot that supports chaining of tasks with + * optionally set delays and sync/async execution + * + * @param task The task context to be executed by this runnable + * @param async Whether this task should be run async + * @param delay The delay before this task should be executed in ticks + */ +class TwilightRunnable( + private val task: TwilightRunnable.() -> Unit, + val async: Boolean, + private val delay: Long = 0 +) : BukkitRunnable() { + + private var nextRunnable: TwilightRunnable? = null + + // Executes main task and schedules the next runnable if one exists + override fun run() { + try { + task() + } finally { nextRunnable?.schedule() } + } + + /** + * Define a runnable to executed after the completion of this runnable, + * This runnable will run the same async state of the previous runnable + * @param delay delay before this task should be executed + * @param unit The unit of time the delay is in + * @param action The task to be executed + * @return The new TwilightRunnable + */ + fun onComplete( + delay: Int = 0, + unit: TimeUnit = TimeUnit.TICKS, + action: TwilightRunnable.() -> Unit + ): TwilightRunnable { + return chainRunnable(action, async, unit.toTicks(delay.toLong())) + } + + /** + * Define a runnable to executed synchronously after the completion of this runnable + * @param delay delay before this task should be executed + * @param unit The unit of time the delay is in + * @param action The task to be executed + * @return The new TwilightRunnable + */ + fun onCompleteSync( + delay: Int = 0, + unit: TimeUnit = TimeUnit.TICKS, + action: TwilightRunnable.() -> Unit + ): TwilightRunnable { + return chainRunnable(action, false, unit.toTicks(delay.toLong())) + } + + /** + * Define a runnable to executed asynchronously after the completion of this runnable + * @param delay delay before this task should be executed + * @param unit The unit of time the delay is in + * @param action The task to be executed + * @return The new TwilightRunnable + */ + fun onCompleteAsync( + delay: Int = 0, + unit: TimeUnit = TimeUnit.TICKS, + action: TwilightRunnable.() -> Unit + ): TwilightRunnable { + return chainRunnable(action, true, unit.toTicks(delay.toLong())) + } + + /** + * Chains a new task to be executed after the current one completes + * @param action The task to be executed + * @param async Whether the new task is async + * @param delay The delay before the new task should execute in ticks + * @return The new TwilightRunnable + */ + private fun chainRunnable(action: TwilightRunnable.() -> Unit, async: Boolean, delay: Long = 0): TwilightRunnable { + val newRunnable = TwilightRunnable(action, async, delay) + if (nextRunnable == null) { + nextRunnable = newRunnable + } else { + var last = nextRunnable + while (last?.nextRunnable != null) { + last = last.nextRunnable + } + last?.nextRunnable = newRunnable + } + return newRunnable + } + + /** + * Schedules this runnable to be executed + * TODO Repeating runnables cause issues with onComplete as it attempts to schedule identical tasks + * TODO Will fix at some point but for now just dont onComplete on repeat, it's pretty pointless to do so anyway + * TODO For now, I have not implemented this class for repeat in Scheduler.kt to prevent people using it + * + * @param delay Additional delay to be added in ticks + * @return The BukkitTask + */ + fun schedule(delay: Int = 0): BukkitTask { + val totalDelay = this.delay + delay + return if (async) { + if (totalDelay > 0) this.runTaskLaterAsynchronously(Twilight.plugin, totalDelay) + else this.runTaskAsynchronously(Twilight.plugin) + } else { + if (totalDelay > 0) this.runTaskLater(Twilight.plugin, totalDelay) + else this.runTask(Twilight.plugin) + } + } +} \ No newline at end of file