Adjust running coroutineContext

This commit is contained in:
Him188 2019-10-28 21:24:03 +08:00
parent ead17724b6
commit d3270bb36f
7 changed files with 47 additions and 41 deletions

View File

@ -14,8 +14,7 @@ import net.mamoe.mirai.withSession
class ContactList<C : Contact> : MutableMap<UInt, C> by mutableMapOf() class ContactList<C : Contact> : MutableMap<UInt, C> by mutableMapOf()
/** /**
* 联系人. 虽然叫做联系人, 但它直营 * 联系人. 虽然叫做联系人, 但他的子类有 [QQ] [][Group].
* 现支持的联系人只有 [QQ号][QQ] [][Group].
* *
* @param bot 这个联系人所属 [Bot] * @param bot 这个联系人所属 [Bot]
* @param id 可以是 QQ 号码或者群号码 [GroupId]. * @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) { class Group internal constructor(bot: Bot, val groupId: GroupId) : Contact(bot, groupId.value) {
val internalId = GroupId(id).toInternalId() val internalId = GroupId(id).toInternalId()
val members: ContactList<QQ> val members: ContactList<QQ>
//todo members get() = TODO("Implementing group members is less important")
get() = throw UnsupportedOperationException("Not yet supported")
override suspend fun sendMessage(message: MessageChain) { override suspend fun sendMessage(message: MessageChain) {
bot.network[EventPacketHandler].sendGroupMessage(this, message) bot.network[EventPacketHandler].sendGroupMessage(this, message)
@ -87,7 +85,7 @@ class Group internal constructor(bot: Bot, val groupId: GroupId) : Contact(bot,
inline fun <R> Group.withSession(block: BotSession.() -> R): R = bot.withSession(block) inline fun <R> Group.withSession(block: BotSession.() -> R): R = bot.withSession(block)
/** /**
* QQ 账号. * QQ 对象.
* 注意: 一个 [QQ] 实例并不是独立的, 它属于一个 [Bot]. * 注意: 一个 [QQ] 实例并不是独立的, 它属于一个 [Bot].
* *
* A QQ instance helps you to receive event from or sendPacket event to. * A QQ instance helps you to receive event from or sendPacket event to.
@ -95,7 +93,7 @@ inline fun <R> Group.withSession(block: BotSession.() -> R): R = bot.withSession
* *
* @author Him188moe * @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) { override suspend fun sendMessage(message: MessageChain) {
bot.network[EventPacketHandler].sendFriendMessage(this, message) bot.network[EventPacketHandler].sendFriendMessage(this, message)
} }

View File

@ -2,6 +2,7 @@ package net.mamoe.mirai.network
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.cancelChildren
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.BotSocketAdapter import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.BotSocketAdapter
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.LoginHandler 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.network.protocol.tim.packet.login.RequestSKeyPacket
import net.mamoe.mirai.utils.BotNetworkConfiguration import net.mamoe.mirai.utils.BotNetworkConfiguration
import net.mamoe.mirai.utils.io.PlatformDatagramChannel import net.mamoe.mirai.utils.io.PlatformDatagramChannel
import kotlin.coroutines.ContinuationInterceptor
/** /**
* Mirai 的网络处理器, 它承担所有数据包([Packet])的处理任务. * Mirai 的网络处理器, 它承担所有数据包([Packet])的处理任务.
@ -86,7 +86,8 @@ interface BotNetworkHandler<Socket : DataPacketSocketAdapter> : CoroutineScope {
* 关闭网络接口, 停止所有有关协程和任务 * 关闭网络接口, 停止所有有关协程和任务
*/ */
suspend fun close(cause: Throwable? = null) { suspend fun close(cause: Throwable? = null) {
//todo check?? val job = coroutineContext[Job]
coroutineContext[ContinuationInterceptor]!!.cancelChildren(CancellationException("handler closed", cause)) 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))
} }
} }

View File

@ -33,7 +33,7 @@ internal inline fun TIMBotNetworkHandler.BotSession(
*/ */
class BotSession( class BotSession(
val bot: Bot, val bot: Bot,
val sessionKey: ByteArray,//TODO 协议抽象? 可能并不是所有协议均需要 sessionKey val sessionKey: ByteArray,
val socket: DataPacketSocketAdapter, val socket: DataPacketSocketAdapter,
val NetworkScope: CoroutineScope val NetworkScope: CoroutineScope
) { ) {
@ -61,7 +61,7 @@ class BotSession(
/** /**
* 发送一个数据包, 并期待接受一个特定的 [ServerPacket][P]. * 发送一个数据包, 并期待接受一个特定的 [ServerPacket][P].
* 发送成功后, 该方法会等待收到 [ServerPacket][P] 直到超时. * 这个方法会立即返回.
* *
* 实现方法: * 实现方法:
* ```kotlin * ```kotlin
@ -75,13 +75,13 @@ class BotSession(
* *
* @param checkSequence 是否期待 [ServerPacket.sequenceId] [OutgoingPacket.sequenceId] 相同的包. * @param checkSequence 是否期待 [ServerPacket.sequenceId] [OutgoingPacket.sequenceId] 相同的包.
* @param P 期待的包 * @param P 期待的包
* @param handler 处理期待的包 * @param handler 处理期待的包. 将会在调用 [sendAndExpect] 的函数所在 [coroutineContext] 下执行.
* *
* @see Bot.withSession 转换接收器 (receiver, `this` 的指向) [BotSession] * @see Bot.withSession 转换接收器 (receiver, `this` 的指向) [BotSession]
*/ */
suspend inline fun <reified P : ServerPacket, R> OutgoingPacket.sendAndExpect(checkSequence: Boolean = true, noinline handler: suspend (P) -> R): CompletableDeferred<R> { suspend inline fun <reified P : ServerPacket, R> OutgoingPacket.sendAndExpect(checkSequence: Boolean = true, noinline handler: suspend (P) -> R): CompletableDeferred<R> {
val deferred: CompletableDeferred<R> = CompletableDeferred(coroutineContext[Job]) val deferred: CompletableDeferred<R> = 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.toSend(this)
it.onExpect(handler) it.onExpect(handler)
}) })

View File

@ -36,7 +36,8 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
BotNetworkHandler<TIMBotNetworkHandler.BotSocketAdapter>, PacketHandlerList() { BotNetworkHandler<TIMBotNetworkHandler.BotSocketAdapter>, PacketHandlerList() {
override val coroutineContext: CoroutineContext = 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 override lateinit var socket: BotSocketAdapter
private set 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() if (::loginHandler.isInitialized) loginHandler.close()
loginHandler = LoginHandler(configuration) loginHandler = LoginHandler(configuration)
@ -169,9 +170,9 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
loginResult.complete(LoginResult.TIMEOUT) 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 <reified P : ServerPacket> expectPacket(): CompletableDeferred<P> { private suspend inline fun <reified P : ServerPacket> expectPacket(): CompletableDeferred<P> {
@ -199,20 +200,19 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
} }
packet.use { packet.use {
val name = packet::class.simpleName packet::class.simpleName?.takeIf { !it.endsWith("Encrypted") && !it.endsWith("Raw") }?.let {
if (name != null && !name.endsWith("Encrypted") && !name.endsWith("Raw")) {
bot.logger.logCyan("Packet received: $packet") bot.logger.logCyan("Packet received: $packet")
} }
//Remove first to release the lock // Remove first to release the lock
handlersLock.withLock { handlersLock.withLock {
temporaryPacketHandlers.filter { it.filter(session, packet) } temporaryPacketHandlers.filter { it.filter(session, packet) }
}.forEach { }.forEach {
it.doReceive(packet) it.doReceiveWithoutExceptions(packet)
} }
if (packet is ServerEventPacket) { if (packet is ServerEventPacket) {
//ensure the response packet is sent // Ensure the response packet is sent
sendPacket(packet.ResponsePacket(bot.qqAccount, sessionKey)) sendPacket(packet.ResponsePacket(bot.qqAccount, sessionKey))
} }
@ -220,7 +220,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
return@coroutineScope 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) loginHandler.onPacketReceived(packet)
this@TIMBotNetworkHandler.forEach { this@TIMBotNetworkHandler.forEach {
it.instance.onPacketReceived(packet) it.instance.onPacketReceived(packet)
@ -293,9 +293,6 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
private lateinit var loginIP: String private lateinit var loginIP: String
private var privateKey: ByteArray = getRandomByteArray(16) private var privateKey: ByteArray = getRandomByteArray(16)
/**
* 0828_decr_key
*/
private lateinit var sessionResponseDecryptionKey: IoBuffer private lateinit var sessionResponseDecryptionKey: IoBuffer
private var captchaSectionId: Int = 1 private var captchaSectionId: Int = 1
@ -406,7 +403,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
bot.qqAccount, bot.qqAccount,
token0825, token0825,
code, code,
packet.verificationToken packet.captchaToken
) )
) )
} }

View File

@ -1,9 +1,13 @@
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.network.protocol.tim.handler package net.mamoe.mirai.network.protocol.tim.handler
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.withContext
import net.mamoe.mirai.network.BotSession import net.mamoe.mirai.network.BotSession
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import kotlin.coroutines.CoroutineContext
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
@ -23,7 +27,11 @@ class TemporaryPacketHandler<P : ServerPacket, R>(
private val expectationClass: KClass<P>, private val expectationClass: KClass<P>,
private val deferred: CompletableDeferred<R>, private val deferred: CompletableDeferred<R>,
private val fromSession: BotSession, private val fromSession: BotSession,
private val checkSequence: Boolean private val checkSequence: Boolean,
/**
* 调用者的 [CoroutineContext]
*/
private val callerContext: CoroutineContext
) { ) {
private lateinit var toSend: OutgoingPacket private lateinit var toSend: OutgoingPacket
@ -40,21 +48,22 @@ class TemporaryPacketHandler<P : ServerPacket, R>(
this.handler = handler this.handler = handler
} }
suspend fun send(session: BotSession) { internal suspend fun send(session: BotSession) {
require(::handler.isInitialized) { "handler is not initialized" } require(::handler.isInitialized) { "handler is not initialized" }
this.session = session this.session = session
session.socket.sendPacket(toSend) session.socket.sendPacket(toSend)
} }
@ExperimentalUnsignedTypes internal fun filter(session: BotSession, packet: ServerPacket): Boolean =
fun filter(session: BotSession, packet: ServerPacket): Boolean =
expectationClass.isInstance(packet) && session === this.fromSession && if (checkSequence) packet.sequenceId == toSend.sequenceId else true 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") @Suppress("UNCHECKED_CAST")
val ret = try { val ret = try {
handler(packet as P) withContext(callerContext) {
} catch (e: Exception) { handler(packet as P)
}
} catch (e: Throwable) {
deferred.completeExceptionally(e) deferred.completeExceptionally(e)
return return
} }

View File

@ -15,7 +15,7 @@ import net.mamoe.mirai.utils.io.*
class RequestCaptchaTransmissionPacket( class RequestCaptchaTransmissionPacket(
private val bot: UInt, private val bot: UInt,
private val token0825: ByteArray, private val token0825: ByteArray,
private val verificationSequence: Int, private val captchaSequence: Int,
private val token00BA: ByteArray private val token00BA: ByteArray
) : OutgoingPacket() { ) : OutgoingPacket() {
@Tested @Tested
@ -31,7 +31,7 @@ class RequestCaptchaTransmissionPacket(
writeHex("01 03 00 19") writeHex("01 03 00 19")
writeHex(TIMProtocol.publicKey) writeHex(TIMProtocol.publicKey)
writeHex("13 00 05 00 00 00 00") writeHex("13 00 05 00 00 00 00")
writeUByte(verificationSequence.toUByte()) writeUByte(captchaSequence.toUByte())
writeHex("00 28") writeHex("00 28")
writeFully(token00BA) writeFully(token00BA)
writeHex("00 10") writeHex("00 10")
@ -48,7 +48,7 @@ class SubmitCaptchaPacket(
private val bot: UInt, private val bot: UInt,
private val token0825: ByteArray, private val token0825: ByteArray,
private val captcha: String, private val captcha: String,
private val verificationToken: IoBuffer private val captchaToken: IoBuffer
) : OutgoingPacket() { ) : OutgoingPacket() {
init { init {
require(captcha.length == 4) { "captcha.length must == 4" } require(captcha.length == 4) { "captcha.length must == 4" }
@ -71,7 +71,7 @@ class SubmitCaptchaPacket(
writeHex("14 00 05 00 00 00 00 00 04") writeHex("14 00 05 00 00 00 00 00 04")
writeStringUtf8(captcha.toUpperCase()) writeStringUtf8(captcha.toUpperCase())
writeHex("00 38") writeHex("00 38")
writeFully(verificationToken) writeFully(captchaToken)
writeShort(16) writeShort(16)
writeHex(TIMProtocol.key00BAFix)//16 writeHex(TIMProtocol.key00BAFix)//16
@ -113,14 +113,14 @@ class OutgoingCaptchaRefreshPacket(
open class CaptchaTransmissionResponsePacket(input: ByteReadPacket) : ServerCaptchaPacket(input) { open class CaptchaTransmissionResponsePacket(input: ByteReadPacket) : ServerCaptchaPacket(input) {
lateinit var captchaSectionN: IoBuffer lateinit var captchaSectionN: IoBuffer
lateinit var verificationToken: IoBuffer//56bytes lateinit var captchaToken: IoBuffer//56bytes
var transmissionCompleted: Boolean = false//验证码是否已经传输完成 var transmissionCompleted: Boolean = false//验证码是否已经传输完成
lateinit var token00BA: ByteArray//40 bytes lateinit var token00BA: ByteArray//40 bytes
override fun decode() = with(input) { override fun decode() = with(input) {
input.discardExact(10)//13 00 05 01 00 00 01 23 00 38 input.discardExact(10)//13 00 05 01 00 00 01 23 00 38
verificationToken = readIoBuffer(56) captchaToken = readIoBuffer(56)
val length = readShort() val length = readShort()
captchaSectionN = readIoBuffer(length) captchaSectionN = readIoBuffer(length)

View File

@ -5,4 +5,5 @@ package net.mamoe.mirai.utils
* *
* @author Him188moe * @author Him188moe
*/ */
internal annotation class Tested @Suppress("unused")
internal annotation class Tested(val date: String = "")