diff --git a/voice/api/voice.api b/voice/api/voice.api index 186b2bbf5d6..92c1018d8c5 100644 --- a/voice/api/voice.api +++ b/voice/api/voice.api @@ -526,21 +526,24 @@ public final class dev/kord/voice/gateway/DefaultVoiceGatewayBuilder { public final fun getReconnectRetry ()Ldev/kord/gateway/retry/Retry; public final fun getSelfId ()Ldev/kord/common/entity/Snowflake; public final fun getSessionId ()Ljava/lang/String; + public final fun isDeaf ()Z public final fun setClient (Lio/ktor/client/HttpClient;)V + public final fun setDeaf (Z)V public final fun setEventFlow (Lkotlinx/coroutines/flow/MutableSharedFlow;)V public final fun setReconnectRetry (Ldev/kord/gateway/retry/Retry;)V } public final class dev/kord/voice/gateway/DefaultVoiceGatewayData { - public fun (Ldev/kord/common/entity/Snowflake;Ldev/kord/common/entity/Snowflake;Ljava/lang/String;Lio/ktor/client/HttpClient;Ldev/kord/gateway/retry/Retry;Lkotlinx/coroutines/flow/MutableSharedFlow;)V + public fun (Ldev/kord/common/entity/Snowflake;Ldev/kord/common/entity/Snowflake;Ljava/lang/String;Lio/ktor/client/HttpClient;Ldev/kord/gateway/retry/Retry;ZLkotlinx/coroutines/flow/MutableSharedFlow;)V public final fun component1 ()Ldev/kord/common/entity/Snowflake; public final fun component2 ()Ldev/kord/common/entity/Snowflake; public final fun component3 ()Ljava/lang/String; public final fun component4 ()Lio/ktor/client/HttpClient; public final fun component5 ()Ldev/kord/gateway/retry/Retry; - public final fun component6 ()Lkotlinx/coroutines/flow/MutableSharedFlow; - public final fun copy (Ldev/kord/common/entity/Snowflake;Ldev/kord/common/entity/Snowflake;Ljava/lang/String;Lio/ktor/client/HttpClient;Ldev/kord/gateway/retry/Retry;Lkotlinx/coroutines/flow/MutableSharedFlow;)Ldev/kord/voice/gateway/DefaultVoiceGatewayData; - public static synthetic fun copy$default (Ldev/kord/voice/gateway/DefaultVoiceGatewayData;Ldev/kord/common/entity/Snowflake;Ldev/kord/common/entity/Snowflake;Ljava/lang/String;Lio/ktor/client/HttpClient;Ldev/kord/gateway/retry/Retry;Lkotlinx/coroutines/flow/MutableSharedFlow;ILjava/lang/Object;)Ldev/kord/voice/gateway/DefaultVoiceGatewayData; + public final fun component6 ()Z + public final fun component7 ()Lkotlinx/coroutines/flow/MutableSharedFlow; + public final fun copy (Ldev/kord/common/entity/Snowflake;Ldev/kord/common/entity/Snowflake;Ljava/lang/String;Lio/ktor/client/HttpClient;Ldev/kord/gateway/retry/Retry;ZLkotlinx/coroutines/flow/MutableSharedFlow;)Ldev/kord/voice/gateway/DefaultVoiceGatewayData; + public static synthetic fun copy$default (Ldev/kord/voice/gateway/DefaultVoiceGatewayData;Ldev/kord/common/entity/Snowflake;Ldev/kord/common/entity/Snowflake;Ljava/lang/String;Lio/ktor/client/HttpClient;Ldev/kord/gateway/retry/Retry;ZLkotlinx/coroutines/flow/MutableSharedFlow;ILjava/lang/Object;)Ldev/kord/voice/gateway/DefaultVoiceGatewayData; public fun equals (Ljava/lang/Object;)Z public final fun getClient ()Lio/ktor/client/HttpClient; public final fun getEventFlow ()Lkotlinx/coroutines/flow/MutableSharedFlow; @@ -549,6 +552,7 @@ public final class dev/kord/voice/gateway/DefaultVoiceGatewayData { public final fun getSelfId ()Ldev/kord/common/entity/Snowflake; public final fun getSessionId ()Ljava/lang/String; public fun hashCode ()I + public final fun isDeaf ()Z public fun toString ()Ljava/lang/String; } @@ -668,12 +672,46 @@ public final class dev/kord/voice/gateway/Identify$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class dev/kord/voice/gateway/MediaSinkWants : dev/kord/voice/gateway/Command { + public static final field Serializer Ldev/kord/voice/gateway/MediaSinkWants$Serializer; + public fun (ZLjava/util/Map;)V + public synthetic fun (ZLjava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Z + public final fun component2 ()Ljava/util/Map; + public final fun copy (ZLjava/util/Map;)Ldev/kord/voice/gateway/MediaSinkWants; + public static synthetic fun copy$default (Ldev/kord/voice/gateway/MediaSinkWants;ZLjava/util/Map;ILjava/lang/Object;)Ldev/kord/voice/gateway/MediaSinkWants; + public fun equals (Ljava/lang/Object;)Z + public final fun getSsrcs ()Ljava/util/Map; + public final fun getWants ()Z + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/kord/voice/gateway/MediaSinkWants$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Ldev/kord/voice/gateway/MediaSinkWants$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ldev/kord/voice/gateway/MediaSinkWants; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ldev/kord/voice/gateway/MediaSinkWants;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class dev/kord/voice/gateway/MediaSinkWants$Serializer : kotlinx/serialization/SerializationStrategy { + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ldev/kord/voice/gateway/MediaSinkWants;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class dev/kord/voice/gateway/OpCode : java/lang/Enum { public static final field ClientDisconnect Ldev/kord/voice/gateway/OpCode; public static final field Heartbeat Ldev/kord/voice/gateway/OpCode; public static final field HeartbeatAck Ldev/kord/voice/gateway/OpCode; public static final field Hello Ldev/kord/voice/gateway/OpCode; public static final field Identify Ldev/kord/voice/gateway/OpCode; + public static final field MediaSinkWants Ldev/kord/voice/gateway/OpCode; public static final field Ready Ldev/kord/voice/gateway/OpCode; public static final field Resume Ldev/kord/voice/gateway/OpCode; public static final field Resumed Ldev/kord/voice/gateway/OpCode; diff --git a/voice/src/main/kotlin/gateway/Command.kt b/voice/src/main/kotlin/gateway/Command.kt index 163678a7c58..4d801d693a3 100644 --- a/voice/src/main/kotlin/gateway/Command.kt +++ b/voice/src/main/kotlin/gateway/Command.kt @@ -4,12 +4,15 @@ import dev.kord.common.annotation.KordVoice import dev.kord.common.entity.Snowflake import dev.kord.voice.EncryptionMode import dev.kord.voice.SpeakingFlags +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.buildClassSerialDescriptor import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put import kotlinx.serialization.SerializationStrategy as KSerializationStrategy public sealed class Command { @@ -31,6 +34,10 @@ public sealed class Command { composite.encodeSerializableElement(descriptor, 0, OpCode.Serializer, OpCode.Heartbeat) composite.encodeLongElement(descriptor, 1, value.nonce) } + is MediaSinkWants -> { + composite.encodeSerializableElement(descriptor, 0, OpCode.Serializer, OpCode.MediaSinkWants) + composite.encodeSerializableElement(descriptor, 1, MediaSinkWants.Serializer, value) + } is SendSpeaking -> { composite.encodeSerializableElement(descriptor, 0, OpCode.Serializer, OpCode.Speaking) composite.encodeSerializableElement(descriptor, 1, SendSpeaking.serializer(), value) @@ -72,6 +79,34 @@ public data class SendSpeaking( val ssrc: UInt ) : Command() +@KordVoice +@Serializable +public data class MediaSinkWants( + /** + * Control whether the client wants to receive audio packets from **ANY** SSRC. + */ + val wants: Boolean, + /** + * Control whether the client wants to receive audio packets from a specific SSRC. + */ + val ssrcs: Map = emptyMap(), +) : Command() { + public companion object Serializer : KSerializationStrategy { + @OptIn(ExperimentalSerializationApi::class) + override val descriptor: SerialDescriptor = + SerialDescriptor("MediaSinkWants", JsonObject.serializer().descriptor) + + override fun serialize(encoder: Encoder, value: MediaSinkWants) { + val obj = buildJsonObject { + put("any", if (value.wants) 100 else 0) + value.ssrcs.forEach { (ssrc, wants) -> put(ssrc.toString(), if (wants) 100 else 0) } + } + + encoder.encodeSerializableValue(JsonObject.serializer(), obj) + } + } +} + @KordVoice @Serializable public data class SelectProtocol( diff --git a/voice/src/main/kotlin/gateway/DefaultVoiceGateway.kt b/voice/src/main/kotlin/gateway/DefaultVoiceGateway.kt index 9469a04f78c..e527af1fed9 100644 --- a/voice/src/main/kotlin/gateway/DefaultVoiceGateway.kt +++ b/voice/src/main/kotlin/gateway/DefaultVoiceGateway.kt @@ -37,6 +37,7 @@ public data class DefaultVoiceGatewayData( val sessionId: String, val client: HttpClient, val reconnectRetry: Retry, + val isDeaf: Boolean, val eventFlow: MutableSharedFlow ) @@ -196,7 +197,8 @@ public class DefaultVoiceGateway( val copy = command.copy(data = command.data.copy(address = "ip")) "Voice Gateway >>> ${Json.encodeToString(Command.SerializationStrategy, copy)}" } - is Heartbeat, is Resume, is SendSpeaking -> "Voice Gateway >>> $json" + + is SendSpeaking, is Resume, is MediaSinkWants, is Heartbeat -> "Voice Gateway >>> $json" } } socket.send(Frame.Text(json)) diff --git a/voice/src/main/kotlin/gateway/DefaultVoiceGatewayBuilder.kt b/voice/src/main/kotlin/gateway/DefaultVoiceGatewayBuilder.kt index fa9426296d5..140ce46b866 100644 --- a/voice/src/main/kotlin/gateway/DefaultVoiceGatewayBuilder.kt +++ b/voice/src/main/kotlin/gateway/DefaultVoiceGatewayBuilder.kt @@ -21,6 +21,7 @@ public class DefaultVoiceGatewayBuilder( public var client: HttpClient? = null public var reconnectRetry: Retry? = null public var eventFlow: MutableSharedFlow = MutableSharedFlow(extraBufferCapacity = Int.MAX_VALUE) + public var isDeaf: Boolean = false public fun build(): DefaultVoiceGateway { val client = client ?: HttpClient(CIO) { @@ -37,6 +38,7 @@ public class DefaultVoiceGatewayBuilder( sessionId, client, retry, + isDeaf, eventFlow ) diff --git a/voice/src/main/kotlin/gateway/OpCode.kt b/voice/src/main/kotlin/gateway/OpCode.kt index 4a9f0bf7224..ef2b668287c 100644 --- a/voice/src/main/kotlin/gateway/OpCode.kt +++ b/voice/src/main/kotlin/gateway/OpCode.kt @@ -19,7 +19,8 @@ public enum class OpCode(public val code: Int) { Resume(7), Hello(8), Resumed(9), - ClientDisconnect(13); + ClientDisconnect(13), + MediaSinkWants(15); internal object Serializer : KSerializer { override val descriptor: SerialDescriptor diff --git a/voice/src/main/kotlin/gateway/handler/HandshakeHandler.kt b/voice/src/main/kotlin/gateway/handler/HandshakeHandler.kt index a9241fa7720..f3098b8ea1b 100644 --- a/voice/src/main/kotlin/gateway/handler/HandshakeHandler.kt +++ b/voice/src/main/kotlin/gateway/handler/HandshakeHandler.kt @@ -23,6 +23,7 @@ internal class HandshakeHandler( on { data.reconnectRetry.reset() send(identify) + send(MediaSinkWants(!data.isDeaf)) } } }