From 2853cdcba24b20be7b792136bd52ced27ad6a9da Mon Sep 17 00:00:00 2001 From: Michael Rittmeister Date: Sat, 19 Aug 2023 14:06:13 +0200 Subject: [PATCH] Add Java API to receive events (Fix #23) --- .../kotlin/lavalink-jvm-module.gradle.kts | 11 ++++- .../main/kotlin/lavalink-module.gradle.kts | 15 ++++-- .../schlaubi/lavakord/audio/EventSource.kt | 4 +- example/src/commonMain/kotlin/Lavakord.kt | 13 +++-- .../java/dev/schlaubi/lavakord/Javakord.java | 3 ++ java/build.gradle.kts | 13 +++++ .../lavakord/interop/JavaEventSource.kt | 48 +++++++++++++++++++ .../dev/schlaubi/lavakord/interop/JavaLink.kt | 6 ++- .../schlaubi/lavakord/interop/JavaPlayer.kt | 6 ++- jda-java/build.gradle.kts | 11 +++++ settings.gradle.kts | 1 + 11 files changed, 118 insertions(+), 13 deletions(-) create mode 100644 java/src/main/kotlin/dev/schlaubi/lavakord/interop/JavaEventSource.kt diff --git a/buildSrc/src/main/kotlin/lavalink-jvm-module.gradle.kts b/buildSrc/src/main/kotlin/lavalink-jvm-module.gradle.kts index 2246066d..028c0790 100644 --- a/buildSrc/src/main/kotlin/lavalink-jvm-module.gradle.kts +++ b/buildSrc/src/main/kotlin/lavalink-jvm-module.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { kotlin("jvm") id("org.jetbrains.dokka") @@ -13,7 +15,6 @@ repositories { kotlin { explicitApi() - jvmToolchain(8) sourceSets { all { @@ -27,6 +28,14 @@ kotlin { } } } + + compilerOptions { + jvmTarget = JvmTarget.JVM_1_8 + } +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 } tasks { diff --git a/buildSrc/src/main/kotlin/lavalink-module.gradle.kts b/buildSrc/src/main/kotlin/lavalink-module.gradle.kts index 6ff9ba61..cbc4dcd0 100644 --- a/buildSrc/src/main/kotlin/lavalink-module.gradle.kts +++ b/buildSrc/src/main/kotlin/lavalink-module.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { kotlin("multiplatform") id("org.jetbrains.dokka") @@ -14,11 +16,10 @@ repositories { kotlin { explicitApi() - jvmToolchain(8) jvm { - tasks { - withType { - useJUnitPlatform() + compilations.all { + compilerOptions.configure { + jvmTarget = JvmTarget.JVM_1_8 } } } @@ -43,3 +44,9 @@ kotlin { } } } + +tasks { + withType { + useJUnitPlatform() + } +} diff --git a/core/src/commonMain/kotlin/dev/schlaubi/lavakord/audio/EventSource.kt b/core/src/commonMain/kotlin/dev/schlaubi/lavakord/audio/EventSource.kt index b229ff63..e3cd9cef 100644 --- a/core/src/commonMain/kotlin/dev/schlaubi/lavakord/audio/EventSource.kt +++ b/core/src/commonMain/kotlin/dev/schlaubi/lavakord/audio/EventSource.kt @@ -15,7 +15,7 @@ import kotlinx.coroutines.launch * * @see EventSource.on */ -public interface EventSource { +public interface EventSource { public val events: Flow public val coroutineScope: CoroutineScope } @@ -31,7 +31,7 @@ public interface EventSource { * The returned [Job] is a reference to the created coroutine, call [Job.cancel] to cancel the processing of any further * events. */ -public inline fun EventSource.on( +public inline fun EventSource.on( scope: CoroutineScope = coroutineScope, noinline consumer: suspend E.() -> Unit ): Job = diff --git a/example/src/commonMain/kotlin/Lavakord.kt b/example/src/commonMain/kotlin/Lavakord.kt index 18887806..52e7fa98 100644 --- a/example/src/commonMain/kotlin/Lavakord.kt +++ b/example/src/commonMain/kotlin/Lavakord.kt @@ -13,8 +13,9 @@ import dev.kord.core.on import dev.kord.gateway.Intent import dev.kord.gateway.PrivilegedIntent import dev.kord.rest.builder.interaction.string -import dev.schlaubi.lavakord.audio.Event +import dev.schlaubi.lavakord.plugins.lavasrc.lavaSrcInfo import dev.schlaubi.lavakord.audio.Link +import dev.schlaubi.lavakord.audio.TrackEvent import dev.schlaubi.lavakord.audio.on import dev.schlaubi.lavakord.kord.getLink import dev.schlaubi.lavakord.kord.lavakord @@ -49,7 +50,7 @@ suspend fun main() { } } - lavalink.addNode("ws://localhost:2333", "youshallnotpass") + lavalink.addNode("wss://joachim.lava-hosts.schlaubi.net", "jcdrNb7Y8D8TTo6D64x&msiA3CzD") kord.on { val ack = interaction.deferPublicResponse() @@ -60,8 +61,12 @@ suspend fun main() { val followUpCreator = FollowupPermittingInteractionResponseBehavior( interaction.applicationId, interaction.token, interaction.kord, interaction.supplier ) - player.on { - followUpCreator.createEphemeralFollowup { content = "Event: $this" } + player.on { + try { + followUpCreator.createEphemeralFollowup { content = "Event: ${this@on.track.lavaSrcInfo}" } + } catch (e: Exception) { + e.printStackTrace() + } } listenedGuilds.add(interaction.guildId) } diff --git a/example/src/jvmMain/java/dev/schlaubi/lavakord/Javakord.java b/example/src/jvmMain/java/dev/schlaubi/lavakord/Javakord.java index 20fc3cfe..0edbf899 100644 --- a/example/src/jvmMain/java/dev/schlaubi/lavakord/Javakord.java +++ b/example/src/jvmMain/java/dev/schlaubi/lavakord/Javakord.java @@ -1,4 +1,7 @@ +package dev.schlaubi.lavakord; + import dev.arbjerg.lavalink.protocol.v4.LoadResult; +import dev.schlaubi.lavakord.audio.TrackEndEvent; import dev.schlaubi.lavakord.interop.JavaInterop; import dev.schlaubi.lavakord.interop.JavaLavakord; import dev.schlaubi.lavakord.interop.TrackUtil; diff --git a/java/build.gradle.kts b/java/build.gradle.kts index 54331846..4931d3bb 100644 --- a/java/build.gradle.kts +++ b/java/build.gradle.kts @@ -1,5 +1,6 @@ import com.vanniktech.maven.publish.JavadocJar import com.vanniktech.maven.publish.KotlinJvm +import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { `lavalink-jvm-module` @@ -12,6 +13,18 @@ dependencies { exclude(module = "opus-java") } implementation(libs.kotlinx.coroutines.jdk8) + implementation(libs.kotlinx.coroutines.jdk9) +} + + +kotlin { + compilerOptions { + jvmTarget = JvmTarget.JVM_11 + } +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 } mavenPublishing { diff --git a/java/src/main/kotlin/dev/schlaubi/lavakord/interop/JavaEventSource.kt b/java/src/main/kotlin/dev/schlaubi/lavakord/interop/JavaEventSource.kt new file mode 100644 index 00000000..8cb6c6f0 --- /dev/null +++ b/java/src/main/kotlin/dev/schlaubi/lavakord/interop/JavaEventSource.kt @@ -0,0 +1,48 @@ +package dev.schlaubi.lavakord.interop + +import dev.schlaubi.lavakord.audio.EventSource +import kotlinx.coroutines.jdk9.asPublisher +import mu.KotlinLogging +import java.util.concurrent.Flow +import java.util.concurrent.Flow.Subscriber +import java.util.function.Consumer + +private val LOG = KotlinLogging.logger { } + +/** + * Java equivalent of [EventSource]. + * + * @param T the base event type + * + * @property suspendingEventSource the underlying delegate [EventSource] + * @property events All events received using a [Flow.Publisher] + */ +public interface JavaEventSource { + public val suspendingEventSource: EventSource + + public val events: Flow.Publisher + get() = suspendingEventSource.events.asPublisher() + + /** + * Creates an event handler which executes [Consumer] for every event of [eventType]. + */ + public fun on(eventType: Class, handler: Consumer) { + events.subscribe(object : Subscriber { + override fun onSubscribe(subscription: Flow.Subscription) = Unit + + override fun onError(throwable: Throwable) { + // This in theory should never happen + LOG.error(throwable) { "An error occurred whilst subscribing to events" } + } + + override fun onComplete() = Unit + + override fun onNext(item: T) { + if (eventType.isInstance(item)) { + handler.accept(eventType.cast(item)) + } + } + }) + } +} + diff --git a/java/src/main/kotlin/dev/schlaubi/lavakord/interop/JavaLink.kt b/java/src/main/kotlin/dev/schlaubi/lavakord/interop/JavaLink.kt index 906e310c..b444115e 100644 --- a/java/src/main/kotlin/dev/schlaubi/lavakord/interop/JavaLink.kt +++ b/java/src/main/kotlin/dev/schlaubi/lavakord/interop/JavaLink.kt @@ -2,6 +2,8 @@ package dev.schlaubi.lavakord.interop import dev.schlaubi.lavakord.InsufficientPermissionException import dev.schlaubi.lavakord.LavaKord +import dev.schlaubi.lavakord.audio.Event +import dev.schlaubi.lavakord.audio.EventSource import dev.schlaubi.lavakord.audio.Link import dev.schlaubi.lavakord.audio.Link.State import dev.schlaubi.lavakord.audio.Node @@ -20,9 +22,11 @@ import kotlin.coroutines.CoroutineContext * @property guildId the id of the Guild this [Link] is connected to * @property lastChannelId the id of the last channel this Link is connected to */ -public class JavaLink(internal val suspendingLink: Link) : CoroutineScope { +public class JavaLink(internal val suspendingLink: Link) : CoroutineScope, JavaEventSource { override val coroutineContext: CoroutineContext get() = suspendingLink.lavakord.coroutineContext + override val suspendingEventSource: EventSource + get() = suspendingLink.node public val node: Node get() = suspendingLink.node public val player: JavaPlayer by lazy { JavaPlayer(suspendingLink.player) } diff --git a/java/src/main/kotlin/dev/schlaubi/lavakord/interop/JavaPlayer.kt b/java/src/main/kotlin/dev/schlaubi/lavakord/interop/JavaPlayer.kt index c33336da..c8138eda 100644 --- a/java/src/main/kotlin/dev/schlaubi/lavakord/interop/JavaPlayer.kt +++ b/java/src/main/kotlin/dev/schlaubi/lavakord/interop/JavaPlayer.kt @@ -1,6 +1,8 @@ package dev.schlaubi.lavakord.interop import dev.arbjerg.lavalink.protocol.v4.Track +import dev.schlaubi.lavakord.audio.Event +import dev.schlaubi.lavakord.audio.EventSource import dev.schlaubi.lavakord.audio.player.Player import kotlinx.coroutines.CoroutineScope import java.time.Duration @@ -16,9 +18,11 @@ import kotlin.coroutines.CoroutineContext * @property volume the current volume of this player * @property position the position of the current song the player is at (-1 if [playingTrack] is null) */ -public class JavaPlayer(internal val suspendingPlayer: Player) : CoroutineScope { +public class JavaPlayer(internal val suspendingPlayer: Player) : CoroutineScope, JavaEventSource { override val coroutineContext: CoroutineContext get() = suspendingPlayer.coroutineScope.coroutineContext + override val suspendingEventSource: EventSource + get() = suspendingPlayer public val playingTrack: Track? get() = suspendingPlayer.playingTrack public val paused: Boolean diff --git a/jda-java/build.gradle.kts b/jda-java/build.gradle.kts index 4c78b8fb..68c82588 100644 --- a/jda-java/build.gradle.kts +++ b/jda-java/build.gradle.kts @@ -1,5 +1,6 @@ import com.vanniktech.maven.publish.JavadocJar import com.vanniktech.maven.publish.KotlinJvm +import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { `lavalink-jvm-module` @@ -11,6 +12,16 @@ dependencies { api(projects.java) } +kotlin { + compilerOptions { + jvmTarget = JvmTarget.JVM_11 + } +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 +} + mavenPublishing { configure(KotlinJvm(JavadocJar.Dokka("dokkaHtml"))) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 8b3a52c9..f69cfbf4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -68,6 +68,7 @@ fun VersionCatalogBuilder.kotlinx() { val coroutines = version("coroutines", "1.7.1") library("kotlinx-coroutines-core", "org.jetbrains.kotlinx", "kotlinx-coroutines-core").versionRef(coroutines) library("kotlinx-coroutines-jdk8", "org.jetbrains.kotlinx", "kotlinx-coroutines-jdk8").versionRef(coroutines) + library("kotlinx-coroutines-jdk9", "org.jetbrains.kotlinx", "kotlinx-coroutines-jdk9").versionRef(coroutines) library("kotlinx-coroutines-test", "org.jetbrains.kotlinx", "kotlinx-coroutines-test").versionRef(coroutines) library("kotlinx-serialization-json", "org.jetbrains.kotlinx", "kotlinx-serialization-json").version("1.5.0") library("kotlinx-datetime", "org.jetbrains.kotlinx", "kotlinx-datetime").version("0.4.0")