feat(gateway): handle opcode 7 reconnect

This commit is contained in:
Cilly Leang 2026-01-27 21:11:34 +11:00
parent 22faef0fb0
commit 56fe5082ec
Signed by: cilly
GPG key ID: 6500251E087653C9
4 changed files with 23 additions and 9 deletions

View file

@ -12,6 +12,7 @@ sealed interface GatewayCloseReason {
data class InvalidSession(val resumable: Boolean) : ShouldReconnect(resume = resumable), ClientInitiated data class InvalidSession(val resumable: Boolean) : ShouldReconnect(resume = resumable), ClientInitiated
// TODO: handle non-resumable cases properly // TODO: handle non-resumable cases properly
data class ServerClosed(val closeCode: CloseReason) : ShouldReconnect(resume = true) data class ServerClosed(val closeCode: CloseReason) : ShouldReconnect(resume = true)
data object ServerReconnect : ShouldReconnect(resume = true), ClientInitiated
data object ClientPaused : KeepDisconnected(), ClientInitiated data object ClientPaused : KeepDisconnected(), ClientInitiated

View file

@ -101,15 +101,17 @@ class GatewaySession private constructor(
private suspend fun handlePayload(payload: Payload.Incoming<*>) { private suspend fun handlePayload(payload: Payload.Incoming<*>) {
logger.d { payload.toString() } logger.d { payload.toString() }
when (val event = payload.d) { when (val event = payload.d) {
is Event.Heartbeat -> handleHeartbeat()
is Event.Reconnect -> close(GatewayCloseReason.ServerReconnect)
is Event.InvalidSession -> close(GatewayCloseReason.InvalidSession(event.resumable))
is Event.Hello -> handleHello(event) is Event.Hello -> handleHello(event)
is Event.HeartbeatAck -> { missedHeartbeats -= 1 }
is Event.Ready -> handlers.ready.handle(event) { is Event.Ready -> handlers.ready.handle(event) {
resumeProps = it resumeProps = it
onSuccess() onSuccess()
} }
is Event.Resumed -> onSuccess() is Event.Resumed -> onSuccess()
is Event.Heartbeat -> handleHeartbeat()
is Event.InvalidSession -> close(GatewayCloseReason.InvalidSession(event.resumable))
is Event.HeartbeatAck -> { missedHeartbeats -= 1 }
} }
} }
@ -160,8 +162,11 @@ class GatewaySession private constructor(
"invalid session (resumable: $reason)" "invalid session (resumable: $reason)"
is GatewayCloseReason.ClientPaused -> is GatewayCloseReason.ClientPaused ->
"client requested pause" "client requested pause"
is GatewayCloseReason.ServerReconnect ->
"server requested reconnect"
null -> null ->
"no reason" "no reason"
} }
closeReason = reason closeReason = reason

View file

@ -72,6 +72,10 @@ sealed interface Event {
val seq: Int, val seq: Int,
) : Outgoing ) : Outgoing
// 7
@Serializable
data object Reconnect : Incoming
// 9 // 9
@JvmInline @JvmInline
@Serializable @Serializable
@ -82,9 +86,8 @@ sealed interface Event {
data class Hello(val heartbeatInterval: Int) : Incoming data class Hello(val heartbeatInterval: Int) : Incoming
// 11 // 11
@JvmInline
@Serializable @Serializable
value class HeartbeatAck(private val nothing: Nothing?) : Incoming data object HeartbeatAck : Incoming
// 40 // 40
@Serializable @Serializable

View file

@ -1,6 +1,5 @@
package moe.lava.neon.core.api.gateway package moe.lava.neon.core.api.gateway
import kotlinx.serialization.KSerializer
import kotlinx.serialization.json.JsonNull import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.json.decodeFromJsonElement
import moe.lava.neon.core.api.ApiConstants import moe.lava.neon.core.api.ApiConstants
@ -25,9 +24,10 @@ fun Payload.Unknown.asIncoming() : Payload.WithSequence {
else -> this else -> this
} }
1 -> decode<Event.Heartbeat>() 1 -> decode<Event.Heartbeat>()
7 -> decodeObject(Event.Reconnect)
9 -> decode<Event.InvalidSession>() 9 -> decode<Event.InvalidSession>()
10 -> decode<Event.Hello>() 10 -> decode<Event.Hello>()
11 -> decode<Event.HeartbeatAck>() 11 -> decodeObject(Event.HeartbeatAck)
else -> this else -> this
} }
} }
@ -40,5 +40,10 @@ private inline fun <reified T : Event.Incoming> Payload.Unknown.decode(): Payloa
t = t, t = t,
) )
private inline fun <reified T : Event.Incoming> KSerializer<T>.wrap(): KSerializer<Payload.Incoming<T>> = private inline fun <reified T : Event.Incoming> Payload.Unknown.decodeObject(obj: T): Payload.Incoming<T> =
Payload.Incoming.serializer(this) Payload.Incoming(
op = op,
d = obj,
s = s,
t = t,
)