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()
/**
* 联系人. 虽然叫做联系人, 但它直营
* 现支持的联系人只有 [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<QQ>
//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 <R> 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 <R> 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)
}

View File

@ -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<Socket : DataPacketSocketAdapter> : 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))
}
}

View File

@ -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 <reified P : ServerPacket, R> OutgoingPacket.sendAndExpect(checkSequence: Boolean = true, noinline handler: suspend (P) -> R): CompletableDeferred<R> {
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.onExpect(handler)
})

View File

@ -36,7 +36,8 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
BotNetworkHandler<TIMBotNetworkHandler.BotSocketAdapter>, 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 <reified P : ServerPacket> expectPacket(): CompletableDeferred<P> {
@ -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
)
)
}

View File

@ -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<P : ServerPacket, R>(
private val expectationClass: KClass<P>,
private val deferred: CompletableDeferred<R>,
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<P : ServerPacket, R>(
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
}

View File

@ -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)

View File

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