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
// TODO: handle non-resumable cases properly
data class ServerClosed(val closeCode: CloseReason) : ShouldReconnect(resume = true)
data object ServerReconnect : ShouldReconnect(resume = true), ClientInitiated
data object ClientPaused : KeepDisconnected(), ClientInitiated

View file

@ -101,15 +101,17 @@ class GatewaySession private constructor(
private suspend fun handlePayload(payload: Payload.Incoming<*>) {
logger.d { payload.toString() }
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.HeartbeatAck -> { missedHeartbeats -= 1 }
is Event.Ready -> handlers.ready.handle(event) {
resumeProps = it
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)"
is GatewayCloseReason.ClientPaused ->
"client requested pause"
is GatewayCloseReason.ServerReconnect ->
"server requested reconnect"
null ->
"no reason"
}
closeReason = reason

View file

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

View file

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