Skip to content

Commit 3396537

Browse files
committed
Destroyable listener
1 parent 4b950ba commit 3396537

File tree

3 files changed

+105
-0
lines changed

3 files changed

+105
-0
lines changed

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.lambda.event.Muteable
77
import com.lambda.task.Task
88
import com.lambda.threading.runConcurrent
99
import com.lambda.threading.runSafe
10+
import com.lambda.util.selfReference
1011

1112

1213
/**
@@ -87,6 +88,54 @@ class SafeListener(
8788
return listener
8889
}
8990

91+
/**
92+
* This function registers a new [SafeListener] for a generic [Event] type [T].
93+
* The [function] is executed on the same thread where the [Event] was dispatched.
94+
* The [function] will only be executed when the context satisfies certain safety conditions.
95+
* These conditions are met when none of the following [SafeContext] properties are null:
96+
* - [SafeContext.world]
97+
* - [SafeContext.player]
98+
* - [SafeContext.interaction]
99+
* - [SafeContext.connection]
100+
*
101+
* This typically occurs when the user is in-game.
102+
*
103+
* After the [function] is executed once, the [SafeListener] will be automatically unsubscribed.
104+
*
105+
* Usage:
106+
* ```kotlin
107+
* listenerOnce<MyEvent> { event ->
108+
* player.sendMessage("Event received: $event")
109+
* }
110+
*
111+
* listenerOnce<MyEvent>(priority = 1) { event ->
112+
* player.sendMessage("Event received before the previous listener: $event")
113+
* }
114+
* ```
115+
*
116+
* @param T The type of the event to listen for. This should be a subclass of Event.
117+
* @param priority The priority of the listener. Listeners with higher priority will be executed first. The Default value is 0.
118+
* @param alwaysListen If true, the listener will be executed even if it is muted. The Default value is false.
119+
* @param function The function to be executed when the event is posted. This function should take a SafeContext and an event of type T as parameters.
120+
* @return The newly created and registered [SafeListener].
121+
*/
122+
inline fun <reified T : Event> Any.listenOnce(
123+
priority: Int = 0,
124+
alwaysListen: Boolean = false,
125+
noinline function: SafeContext.(T) -> Unit,
126+
): SafeListener {
127+
val destroyable by selfReference<SafeListener> {
128+
SafeListener(priority, this@listenOnce, alwaysListen) { event ->
129+
function(event as T)
130+
EventFlow.syncListeners.unsubscribe(self)
131+
}
132+
}
133+
134+
EventFlow.syncListeners.subscribe<T>(destroyable)
135+
136+
return destroyable
137+
}
138+
90139
/**
91140
* Registers a new [SafeListener] for a generic [Event] type [T] within the context of a [Task].
92141
* The [function] is executed on the same thread where the [Event] was dispatched.

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import com.lambda.event.Event
55
import com.lambda.event.EventFlow
66
import com.lambda.event.Muteable
77
import com.lambda.event.listener.SafeListener.Companion.concurrentListener
8+
import com.lambda.event.listener.SafeListener.Companion.listenOnce
89
import com.lambda.event.listener.SafeListener.Companion.listener
10+
import com.lambda.util.selfReference
911

1012
/**
1113
* An [UnsafeListener] is a specialized type of [Listener] that operates without a [SafeContext].
@@ -79,6 +81,50 @@ class UnsafeListener(
7981
return listener
8082
}
8183

84+
/**
85+
* Registers a new [UnsafeListener] for a generic [Event] type [T].
86+
* The [function] is executed only once when the [Event] is dispatched.
87+
* This function should only be used when the [function] performs read actions on the game data.
88+
* For only in-game related contexts, use the [SafeListener.listenOnce] function instead.
89+
* The listener will be automatically unsubscribed after the first execution.
90+
* This function is useful for one-time event handling.
91+
*
92+
* Usage:
93+
* ```kotlin
94+
* unsafeListenOnce<MyEvent> { event ->
95+
* println("Unsafe event received only once: $event")
96+
* }
97+
*
98+
* unsafeListenOnce<MyEvent>(priority = 1) { event ->
99+
* println("Unsafe event received only once before the previous listener: $event")
100+
* }
101+
* ```
102+
*
103+
* After the [function] is executed once, the [SafeListener] will be automatically unsubscribed.
104+
*
105+
* @param T The type of the event to listen for. This should be a subclass of Event.
106+
* @param priority The priority of the listener. Listeners with higher priority will be executed first. The Default value is 0.
107+
* @param alwaysListen If true, the listener will be executed even if it is muted. The Default value is false.
108+
* @param function The function to be executed when the event is posted. This function should take an event of type T as a parameter.
109+
* @return The newly created and registered [UnsafeListener].
110+
*/
111+
inline fun <reified T : Event> Any.unsafeListenOnce(
112+
priority: Int = 0,
113+
alwaysListen: Boolean = false,
114+
noinline function: (T) -> Unit,
115+
): UnsafeListener {
116+
val destroyable by selfReference<UnsafeListener> {
117+
UnsafeListener(priority, this@unsafeListenOnce, alwaysListen) { event ->
118+
function(event as T)
119+
EventFlow.syncListeners.unsubscribe(self)
120+
}
121+
}
122+
123+
EventFlow.syncListeners.subscribe<T>(destroyable)
124+
125+
return destroyable
126+
}
127+
82128
/**
83129
* Registers a new [UnsafeListener] for a generic [Event] type [T].
84130
* The [function] is executed on a new thread running asynchronously to the game thread.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.lambda.util
2+
3+
class SelfReference<T>(initializer: SelfReference<T>.() -> T) {
4+
val self: T by lazy { inner ?: throw IllegalStateException("Do not use `self` until initialized.") }
5+
6+
private val inner = initializer()
7+
operator fun getValue(thisRef: Any?, property: Any?) = self
8+
}
9+
10+
fun <T> selfReference(initializer: SelfReference<T>.() -> T): SelfReference<T> = SelfReference(initializer)

0 commit comments

Comments
 (0)