Skip to content

Commit 2073f38

Browse files
committed
Task docs
1 parent 6c9973e commit 2073f38

File tree

4 files changed

+174
-9
lines changed

4 files changed

+174
-9
lines changed

common/src/main/kotlin/com/lambda/Lambda.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ object Lambda {
2727
taskContext {
2828
HelloWorldTask()
2929
.withDelay(500L)
30-
.withTimeout(5000L)
31-
.withRepeats(2)
30+
.withTimeout(200L)
31+
.withRepeats(10)
32+
.withMaxAttempts(5)
3233
.onSuccess {
3334
LOG.info("Hello, World! Task completed")
3435
}.onRepeat { repeats ->

common/src/main/kotlin/com/lambda/event/listener/SafeListener.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,39 @@ class SafeListener(
6464
return listener
6565
}
6666

67+
/**
68+
* Registers a new [SafeListener] for a generic [Event] type [T] within the context of a [Task].
69+
* The [function] is executed on the same thread where the [Event] was dispatched.
70+
* The [function] will only be executed when the context satisfies certain safety conditions.
71+
* These conditions are met when none of the following [SafeContext] properties are null:
72+
* - [SafeContext.world]
73+
* - [SafeContext.player]
74+
* - [SafeContext.interaction]
75+
* - [SafeContext.connection]
76+
*
77+
* This listener is special for tasks, as its behavior is
78+
* to only listen while the [Task.onAction] function is active / while the task is running.
79+
*
80+
* Usage:
81+
* ```kotlin
82+
* myTask.listener<MyEvent> { event ->
83+
* player.sendMessage("Event received: $event")
84+
* }
85+
*
86+
* myTask.listener<MyEvent>(priority = 1) { event ->
87+
* player.sendMessage("Event received before the previous listener: $event")
88+
* }
89+
* ```
90+
*
91+
* @param T The type of the event to listen for.
92+
* This should be a subclass of Event.
93+
* @param priority The priority of the listener.
94+
* Listeners with higher priority will be executed first.
95+
* Default value is 0.
96+
* @param function The function to be executed when the event is posted.
97+
* This function should take a SafeContext and an event of type T as parameters.
98+
* @return The newly created and registered [SafeListener].
99+
*/
67100
inline fun <reified T : Event> Task<*>.listener(priority: Int = 0, noinline function: SafeContext.(T) -> Unit): SafeListener {
68101
val listener = SafeListener(priority, this) { event ->
69102
function(event as T)

common/src/main/kotlin/com/lambda/task/Task.kt

Lines changed: 109 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,33 @@ import kotlinx.coroutines.TimeoutCancellationException
1010
import kotlinx.coroutines.delay
1111
import kotlinx.coroutines.withTimeout
1212

13+
/**
14+
* A [Task] represents a time-critical activity that executes a suspending action function.
15+
* It is designed to automate in-game activities without the need for strict event-based programming,
16+
* thanks to the use of suspending functions which allow for linear coding.
17+
*
18+
* A [Task] can have event listeners, but they are only active while the action function is running.
19+
*
20+
* It supports a builder pattern, allowing you to chain configuration methods like [withDelay],
21+
* [withTimeout], [withMaxAttempts], [withRepeats], [onSuccess], [onRetry], [onTimeout], [onFailure], and [onRepeat]
22+
* to construct a [Task] instance.
23+
* This makes it easy to build complex flows of nested tasks.
24+
*
25+
* CAUTION: When implementing the `onAction` function,
26+
* ensure that the function adheres to thread safety measures.
27+
* This includes avoiding write operations on non-synchronized in-game data
28+
* unless explicitly running on the game thread using `runSafeOnGameThread { ... }`.
29+
*
30+
* @property delay The delay before the task starts, in milliseconds.
31+
* @property timeout The maximum time that the task is allowed to run, in milliseconds.
32+
* @property maxAttempts The maximum number of attempts to execute the task before it is considered failed.
33+
* @property repeats The number of times the task should be repeated.
34+
* @property onSuccess The action to be performed when the task completes successfully.
35+
* @property onRetry The action to be performed when the task is retried after a failure or timeout.
36+
* @property onTimeout The action to be performed when the task times out.
37+
* @property onRepeat The action to be performed each time the task is repeated.
38+
* @property onException The action to be performed when the task encounters an exception.
39+
*/
1340
abstract class Task<Result>(
1441
private var delay: Long = 0L,
1542
private var timeout: Long = Long.MAX_VALUE,
@@ -21,18 +48,43 @@ abstract class Task<Result>(
2148
private var onRepeat: (suspend Task<Result>.(Int) -> Unit) = {},
2249
private var onException: (suspend Task<Result>.(Throwable) -> Unit) = {},
2350
) {
51+
private val creationTimestamp = System.currentTimeMillis()
52+
private val age: Long get() = System.currentTimeMillis() - creationTimestamp
53+
open val name: String get() = this::class.simpleName ?: "Task"
54+
2455
val syncListeners = Subscriber()
2556
private val concurrentListeners = Subscriber()
2657

2758
/**
28-
* Make sure to only do read operations on the game thread!
29-
* Use `runSafeOnGameThread { ... }` to execute safe write operations on the game thread.
30-
* @return The [Result] of the action
59+
* Executes the main action of the task.
60+
*
61+
* This function should only perform read operations on the game thread due to potential concurrency issues.
62+
* If write operations are necessary, they should be wrapped in the `runSafeOnGameThread { ... }` function to ensure thread safety.
63+
*
64+
* @return The result of the action, of type [Result].
3165
*/
3266
abstract suspend fun SafeContext.onAction(): Result
3367

68+
/**
69+
* This function is called when the task is cancelled.
70+
* It should be overridden for tasks that need to perform cleanup operations,
71+
* such as cancelling a block breaking progress, releasing resources,
72+
* or stopping any ongoing operations that were started by the task.
73+
*/
3474
open fun SafeContext.onCancel() {}
3575

76+
/**
77+
* Executes the task with the configured parameters.
78+
*
79+
* This function will attempt to execute the task for a specified number of attempts and repeats.
80+
* It handles delays before task execution, task timeouts, and task cancellations.
81+
* It also manages the lifecycle of event listeners associated with the task.
82+
*
83+
* @return The result of the task execution, represented as a [TaskResult].
84+
* This could be a successful result ([TaskResult.Success]), a cancellation ([TaskResult.Cancelled]),
85+
* a failure ([TaskResult.Failure]) due to an exception, or a timeout
86+
* ([TaskResult.Timeout]).
87+
*/
3688
suspend fun execute(): TaskResult<Result> {
3789
var attempt = 1
3890
var iteration = 0
@@ -107,50 +159,100 @@ abstract class Task<Result>(
107159
}
108160
}
109161

110-
private val creationTimestamp = System.currentTimeMillis()
111-
val age: Long get() = System.currentTimeMillis() - creationTimestamp
112-
val name: String get() = this::class.simpleName ?: "Task"
113-
162+
/**
163+
* Sets the delay before the task starts.
164+
*
165+
* @param delay The delay in milliseconds.
166+
* @return This task instance with the updated delay.
167+
*/
114168
fun withDelay(delay: Long): Task<Result> {
115169
this.delay = delay
116170
return this
117171
}
118172

173+
/**
174+
* Sets the timeout for a single attempt of the task
175+
*
176+
* @param timeout The timeout in milliseconds
177+
* @return This task instance with the updated timeout.
178+
*/
119179
fun withTimeout(timeout: Long): Task<Result> {
120180
this.timeout = timeout
121181
return this
122182
}
123183

184+
/**
185+
* Sets the maximum number of attempts to execute the task before it is considered failed.
186+
*
187+
* @param maxAttempts The maximum number of attempts.
188+
* @return This task instance with the updated maximum attempts.
189+
*/
124190
fun withMaxAttempts(maxAttempts: Int): Task<Result> {
125191
this.maxAttempts = maxAttempts
126192
return this
127193
}
128194

195+
/**
196+
* Sets the number of times the task should be repeated.
197+
*
198+
* @param repeats The number of repeats.
199+
* @return This task instance with the updated number of repeats.
200+
*/
129201
fun withRepeats(repeats: Int): Task<Result> {
130202
this.repeats = repeats
131203
return this
132204
}
133205

206+
/**
207+
* Sets the action to be performed when the task completes successfully.
208+
*
209+
* @param action The action to be performed.
210+
* @return The task instance with the updated success action.
211+
*/
134212
fun onSuccess(action: suspend (Result) -> Unit): Task<Result> {
135213
this.onSuccess = action
136214
return this
137215
}
138216

217+
/**
218+
* Sets the action to be performed when the task is retried after a failure or timeout.
219+
*
220+
* @param action The action to be performed.
221+
* @return The task instance with the updated retry action.
222+
*/
139223
fun onRetry(action: suspend Task<Result>.() -> Unit): Task<Result> {
140224
this.onRetry = action
141225
return this
142226
}
143227

228+
/**
229+
* Sets the action to be performed when the task times out.
230+
*
231+
* @param action The action to be performed.
232+
* @return The task instance with the updated timeout action.
233+
*/
144234
fun onTimeout(action: suspend Task<Result>.() -> Unit): Task<Result> {
145235
this.onTimeout = action
146236
return this
147237
}
148238

239+
/**
240+
* Sets the action to be performed when the task encounters an exception.
241+
*
242+
* @param action The action to be performed.
243+
* @return The task instance with the updated exception action.
244+
*/
149245
fun onFailure(action: suspend Task<Result>.(Throwable) -> Unit): Task<Result> {
150246
this.onException = action
151247
return this
152248
}
153249

250+
/**
251+
* Sets the action to be performed each time the task is repeated.
252+
*
253+
* @param action The action to be performed.
254+
* @return The task instance with the updated repeat action.
255+
*/
154256
fun onRepeat(action: suspend Task<Result>.(Int) -> Unit): Task<Result> {
155257
this.onRepeat = action
156258
return this
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,37 @@
11
package com.lambda.task
22

3+
/**
4+
* Represents the result of a task.
5+
*
6+
* @param Result The type of the result value for successful tasks. Covariant type parameter.
7+
*/
38
sealed class TaskResult<out Result> {
9+
10+
/**
11+
* Represents a successful task result.
12+
*
13+
* @param T The type of the result value.
14+
* @property value The result value.
15+
*/
416
data class Success<out T>(val value: T) : TaskResult<T>()
17+
18+
/**
19+
* Represents a failed task result.
20+
*
21+
* @property throwable The exception that caused the task to fail.
22+
*/
523
data class Failure(val throwable: Throwable) : TaskResult<Nothing>()
24+
25+
/**
26+
* Represents a task result that timed out.
27+
*
28+
* @property timeout The timeout duration in milliseconds.
29+
* @property triesUsed The number of attempts made before the task timed out.
30+
*/
631
data class Timeout(val timeout: Long, val triesUsed: Int) : TaskResult<Nothing>()
32+
33+
/**
34+
* Represents a cancelled task result.
35+
*/
736
data object Cancelled : TaskResult<Nothing>()
837
}

0 commit comments

Comments
 (0)