From d3270bb36f11c16eebb13ab4b8da293b9a1390f1 Mon Sep 17 00:00:00 2001 From: Him188 Date: Mon, 28 Oct 2019 21:24:03 +0800 Subject: [PATCH] Adjust running coroutineContext --- .../kotlin/net.mamoe.mirai/contact/Contact.kt | 10 +++----- .../network/BotNetworkHandler.kt | 7 +++--- .../net.mamoe.mirai/network/BotSession.kt | 8 +++--- .../protocol/tim/TIMBotNetworkHandler.kt | 25 ++++++++----------- .../tim/handler/TemporaryPacketHandler.kt | 23 +++++++++++------ .../protocol/tim/packet/login/Captcha.kt | 12 ++++----- .../kotlin/net.mamoe.mirai/utils/Tested.kt | 3 ++- 7 files changed, 47 insertions(+), 41 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt index 77220cc42..0b06547e0 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt @@ -14,8 +14,7 @@ import net.mamoe.mirai.withSession class ContactList : MutableMap by mutableMapOf() /** - * 联系人. 虽然叫做联系人, 但它直营 - * 现支持的联系人只有 [QQ号][QQ] 和 [群][Group]. + * 联系人. 虽然叫做联系人, 但他的子类有 [QQ] 和 [群][Group]. * * @param bot 这个联系人所属 [Bot] * @param id 可以是 QQ 号码或者群号码 [GroupId]. @@ -70,8 +69,7 @@ inline class GroupInternalId(val value: UInt) class Group internal constructor(bot: Bot, val groupId: GroupId) : Contact(bot, groupId.value) { val internalId = GroupId(id).toInternalId() val members: ContactList - //todo members - get() = throw UnsupportedOperationException("Not yet supported") + get() = TODO("Implementing group members is less important") override suspend fun sendMessage(message: MessageChain) { bot.network[EventPacketHandler].sendGroupMessage(this, message) @@ -87,7 +85,7 @@ class Group internal constructor(bot: Bot, val groupId: GroupId) : Contact(bot, inline fun Group.withSession(block: BotSession.() -> R): R = bot.withSession(block) /** - * QQ 账号. + * QQ 对象. * 注意: 一个 [QQ] 实例并不是独立的, 它属于一个 [Bot]. * * A QQ instance helps you to receive event from or sendPacket event to. @@ -95,7 +93,7 @@ inline fun Group.withSession(block: BotSession.() -> R): R = bot.withSession * * @author Him188moe */ -class QQ internal constructor(bot: Bot, number: UInt) : Contact(bot, number) { +class QQ internal constructor(bot: Bot, id: UInt) : Contact(bot, id) { override suspend fun sendMessage(message: MessageChain) { bot.network[EventPacketHandler].sendFriendMessage(this, message) } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt index bda5b2b86..b7d51a58c 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt @@ -2,6 +2,7 @@ package net.mamoe.mirai.network import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.cancelChildren import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.BotSocketAdapter import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.LoginHandler @@ -15,7 +16,6 @@ import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult import net.mamoe.mirai.network.protocol.tim.packet.login.RequestSKeyPacket import net.mamoe.mirai.utils.BotNetworkConfiguration import net.mamoe.mirai.utils.io.PlatformDatagramChannel -import kotlin.coroutines.ContinuationInterceptor /** * Mirai 的网络处理器, 它承担所有数据包([Packet])的处理任务. @@ -86,7 +86,8 @@ interface BotNetworkHandler : CoroutineScope { * 关闭网络接口, 停止所有有关协程和任务 */ suspend fun close(cause: Throwable? = null) { - //todo check?? - coroutineContext[ContinuationInterceptor]!!.cancelChildren(CancellationException("handler closed", cause)) + val job = coroutineContext[Job] + checkNotNull(job) { "Job should not be null because there will always be a SupervisorJob. There may be a internal mistake" } + job.cancelChildren(CancellationException("handler closed", cause)) } } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt index 350f6ba9c..776683ee0 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt @@ -33,7 +33,7 @@ internal inline fun TIMBotNetworkHandler.BotSession( */ class BotSession( val bot: Bot, - val sessionKey: ByteArray,//TODO 协议抽象? 可能并不是所有协议均需要 sessionKey + val sessionKey: ByteArray, val socket: DataPacketSocketAdapter, val NetworkScope: CoroutineScope ) { @@ -61,7 +61,7 @@ class BotSession( /** * 发送一个数据包, 并期待接受一个特定的 [ServerPacket][P]. - * 发送成功后, 该方法会等待收到 [ServerPacket][P] 直到超时. + * 这个方法会立即返回. * * 实现方法: * ```kotlin @@ -75,13 +75,13 @@ class BotSession( * * @param checkSequence 是否期待 [ServerPacket.sequenceId] 与 [OutgoingPacket.sequenceId] 相同的包. * @param P 期待的包 - * @param handler 处理期待的包 + * @param handler 处理期待的包. 将会在调用 [sendAndExpect] 的函数所在 [coroutineContext] 下执行. * * @see Bot.withSession 转换接收器 (receiver, 即 `this` 的指向) 为 [BotSession] */ suspend inline fun OutgoingPacket.sendAndExpect(checkSequence: Boolean = true, noinline handler: suspend (P) -> R): CompletableDeferred { val deferred: CompletableDeferred = CompletableDeferred(coroutineContext[Job]) - bot.network.addHandler(TemporaryPacketHandler(P::class, deferred, this@BotSession, checkSequence).also { + bot.network.addHandler(TemporaryPacketHandler(P::class, deferred, this@BotSession, checkSequence, coroutineContext + deferred).also { it.toSend(this) it.onExpect(handler) }) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt index 1549cc2a3..8245f5f04 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt @@ -36,7 +36,8 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : BotNetworkHandler, PacketHandlerList() { override val coroutineContext: CoroutineContext = - Dispatchers.Default + CoroutineExceptionHandler { _, e -> bot.logger.log(e) } + Dispatchers.Default + CoroutineExceptionHandler { _, e -> bot.logger.log(e) } + SupervisorJob() + override lateinit var socket: BotSocketAdapter private set @@ -156,7 +157,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : } } - internal suspend fun resendTouch(): LoginResult { + internal suspend fun resendTouch(): LoginResult = coroutineScope { if (::loginHandler.isInitialized) loginHandler.close() loginHandler = LoginHandler(configuration) @@ -169,9 +170,9 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : loginResult.complete(LoginResult.TIMEOUT) } } - sendPacket(TouchPacket(bot.qqAccount, this.serverIp)) + sendPacket(TouchPacket(bot.qqAccount, serverIp)) - return loginResult.await() + return@coroutineScope loginResult.await() } private suspend inline fun expectPacket(): CompletableDeferred

{ @@ -199,20 +200,19 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : } packet.use { - val name = packet::class.simpleName - if (name != null && !name.endsWith("Encrypted") && !name.endsWith("Raw")) { + packet::class.simpleName?.takeIf { !it.endsWith("Encrypted") && !it.endsWith("Raw") }?.let { bot.logger.logCyan("Packet received: $packet") } - //Remove first to release the lock + // Remove first to release the lock handlersLock.withLock { temporaryPacketHandlers.filter { it.filter(session, packet) } }.forEach { - it.doReceive(packet) + it.doReceiveWithoutExceptions(packet) } if (packet is ServerEventPacket) { - //ensure the response packet is sent + // Ensure the response packet is sent sendPacket(packet.ResponsePacket(bot.qqAccount, sessionKey)) } @@ -220,7 +220,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : return@coroutineScope } - //they should be called in sequence otherwise because packet is lock-free + // They should be called in sequence otherwise because packet is lock-free loginHandler.onPacketReceived(packet) this@TIMBotNetworkHandler.forEach { it.instance.onPacketReceived(packet) @@ -293,9 +293,6 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : private lateinit var loginIP: String private var privateKey: ByteArray = getRandomByteArray(16) - /** - * 0828_decr_key - */ private lateinit var sessionResponseDecryptionKey: IoBuffer private var captchaSectionId: Int = 1 @@ -406,7 +403,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : bot.qqAccount, token0825, code, - packet.verificationToken + packet.captchaToken ) ) } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/TemporaryPacketHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/TemporaryPacketHandler.kt index b023ee2ee..086c585a0 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/TemporaryPacketHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/TemporaryPacketHandler.kt @@ -1,9 +1,13 @@ +@file:Suppress("EXPERIMENTAL_API_USAGE") + package net.mamoe.mirai.network.protocol.tim.handler import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.withContext import net.mamoe.mirai.network.BotSession import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket +import kotlin.coroutines.CoroutineContext import kotlin.reflect.KClass /** @@ -23,7 +27,11 @@ class TemporaryPacketHandler

( private val expectationClass: KClass

, private val deferred: CompletableDeferred, private val fromSession: BotSession, - private val checkSequence: Boolean + private val checkSequence: Boolean, + /** + * 调用者的 [CoroutineContext] + */ + private val callerContext: CoroutineContext ) { private lateinit var toSend: OutgoingPacket @@ -40,21 +48,22 @@ class TemporaryPacketHandler

( this.handler = handler } - suspend fun send(session: BotSession) { + internal suspend fun send(session: BotSession) { require(::handler.isInitialized) { "handler is not initialized" } this.session = session session.socket.sendPacket(toSend) } - @ExperimentalUnsignedTypes - fun filter(session: BotSession, packet: ServerPacket): Boolean = + internal fun filter(session: BotSession, packet: ServerPacket): Boolean = expectationClass.isInstance(packet) && session === this.fromSession && if (checkSequence) packet.sequenceId == toSend.sequenceId else true - suspend fun doReceive(packet: ServerPacket) { + internal suspend fun doReceiveWithoutExceptions(packet: ServerPacket) { @Suppress("UNCHECKED_CAST") val ret = try { - handler(packet as P) - } catch (e: Exception) { + withContext(callerContext) { + handler(packet as P) + } + } catch (e: Throwable) { deferred.completeExceptionally(e) return } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Captcha.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Captcha.kt index 8df5c3469..82dcca8ba 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Captcha.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Captcha.kt @@ -15,7 +15,7 @@ import net.mamoe.mirai.utils.io.* class RequestCaptchaTransmissionPacket( private val bot: UInt, private val token0825: ByteArray, - private val verificationSequence: Int, + private val captchaSequence: Int, private val token00BA: ByteArray ) : OutgoingPacket() { @Tested @@ -31,7 +31,7 @@ class RequestCaptchaTransmissionPacket( writeHex("01 03 00 19") writeHex(TIMProtocol.publicKey) writeHex("13 00 05 00 00 00 00") - writeUByte(verificationSequence.toUByte()) + writeUByte(captchaSequence.toUByte()) writeHex("00 28") writeFully(token00BA) writeHex("00 10") @@ -48,7 +48,7 @@ class SubmitCaptchaPacket( private val bot: UInt, private val token0825: ByteArray, private val captcha: String, - private val verificationToken: IoBuffer + private val captchaToken: IoBuffer ) : OutgoingPacket() { init { require(captcha.length == 4) { "captcha.length must == 4" } @@ -71,7 +71,7 @@ class SubmitCaptchaPacket( writeHex("14 00 05 00 00 00 00 00 04") writeStringUtf8(captcha.toUpperCase()) writeHex("00 38") - writeFully(verificationToken) + writeFully(captchaToken) writeShort(16) writeHex(TIMProtocol.key00BAFix)//16 @@ -113,14 +113,14 @@ class OutgoingCaptchaRefreshPacket( open class CaptchaTransmissionResponsePacket(input: ByteReadPacket) : ServerCaptchaPacket(input) { lateinit var captchaSectionN: IoBuffer - lateinit var verificationToken: IoBuffer//56bytes + lateinit var captchaToken: IoBuffer//56bytes var transmissionCompleted: Boolean = false//验证码是否已经传输完成 lateinit var token00BA: ByteArray//40 bytes override fun decode() = with(input) { input.discardExact(10)//13 00 05 01 00 00 01 23 00 38 - verificationToken = readIoBuffer(56) + captchaToken = readIoBuffer(56) val length = readShort() captchaSectionN = readIoBuffer(length) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Tested.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Tested.kt index d9bd874b7..1f898030c 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Tested.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Tested.kt @@ -5,4 +5,5 @@ package net.mamoe.mirai.utils * * @author Him188moe */ -internal annotation class Tested \ No newline at end of file +@Suppress("unused") +internal annotation class Tested(val date: String = "") \ No newline at end of file