Commit 6ef78118 authored by Him188's avatar Him188

Fix bugs

parent 05dfeb76
...@@ -3,29 +3,29 @@ package net.mamoe.mirai.qqandroid.network ...@@ -3,29 +3,29 @@ package net.mamoe.mirai.qqandroid.network
import kotlinx.atomicfu.AtomicRef import kotlinx.atomicfu.AtomicRef
import kotlinx.atomicfu.atomic import kotlinx.atomicfu.atomic
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.Input import kotlinx.io.core.Input
import kotlinx.io.core.buildPacket import kotlinx.io.core.buildPacket
import kotlinx.io.core.use import kotlinx.io.core.use
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.data.MultiPacket import net.mamoe.mirai.data.MultiPacket
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.event.BroadcastControllable import net.mamoe.mirai.event.*
import net.mamoe.mirai.event.Cancellable
import net.mamoe.mirai.event.Subscribable
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.QQImpl import net.mamoe.mirai.qqandroid.QQImpl
import net.mamoe.mirai.qqandroid.event.ForceOfflineEvent
import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent
import net.mamoe.mirai.qqandroid.network.protocol.packet.* import net.mamoe.mirai.qqandroid.network.protocol.packet.*
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.LockFreeLinkedList
import net.mamoe.mirai.utils.cryptor.contentToString import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.getValue
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.unsafeWeakRef
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
...@@ -41,14 +41,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -41,14 +41,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
private lateinit var channel: PlatformSocket private lateinit var channel: PlatformSocket
override suspend fun login() { override suspend fun login() {
if (::channel.isInitialized) { if (::channel.isInitialized) {
channel.close() channel.close()
} }
channel = PlatformSocket() channel = PlatformSocket()
channel.connect("113.96.13.208", 8080) channel.connect("113.96.13.208", 8080)
launch(CoroutineName("Incoming Packet Receiver")) { processReceive() } this.launch(CoroutineName("Incoming Packet Receiver")) { processReceive() }
// bot.logger.info("Trying login") // bot.logger.info("Trying login")
var response: LoginPacket.LoginPacketResponse = LoginPacket.SubCommand9(bot.client).sendAndExpect() var response: LoginPacket.LoginPacketResponse = LoginPacket.SubCommand9(bot.client).sendAndExpect()
...@@ -104,27 +103,18 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -104,27 +103,18 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
} }
override suspend fun init() { override suspend fun init() {
//start updating friend/group list bot.logger.info("开始加载好友信息")
bot.logger.info("Start updating friend/group list")
/*
val data = FriendList.GetFriendGroupList(
bot.client,
0,
20,
0,
0
).sendAndExpect<FriendList.GetFriendGroupList.Response>()
println(data.contentToString())
*/
this@QQAndroidBotNetworkHandler.subscribeAlways<ForceOfflineEvent> {
if (this@QQAndroidBotNetworkHandler.bot == this.bot) {
close()
}
}
/* /*
* 开始加载Contact表 * 开始加载Contact表
* */ * */
var currentFriendCount = 0 var currentFriendCount = 0
var totalFriendCount: Short = 0 var totalFriendCount: Short
while (true) { while (true) {
val data = FriendList.GetFriendGroupList( val data = FriendList.GetFriendGroupList(
bot.client, bot.client,
...@@ -134,14 +124,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -134,14 +124,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
0 0
).sendAndExpect<FriendList.GetFriendGroupList.Response>() ).sendAndExpect<FriendList.GetFriendGroupList.Response>()
totalFriendCount = data.totalFriendCount totalFriendCount = data.totalFriendCount
bot.qqs.delegate.addAll( data.friendList.forEach {
data.friendList.map { // atomic add
QQImpl(this@QQAndroidBotNetworkHandler.bot, EmptyCoroutineContext, it.friendUin!!).also { bot.qqs.delegate.addLast(QQImpl(bot, EmptyCoroutineContext, it.friendUin).also {
currentFriendCount++ currentFriendCount++
})
} }
} bot.logger.verbose("正在加载好友信息 ${currentFriendCount}/${totalFriendCount}")
)
bot.logger.info("正在加载好友信息 ${currentFriendCount}/${totalFriendCount}")
if (currentFriendCount >= totalFriendCount) { if (currentFriendCount >= totalFriendCount) {
break break
} }
...@@ -155,22 +144,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -155,22 +144,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
).sendAndExpect<FriendList.GetTroopList.Response>(100000) ).sendAndExpect<FriendList.GetTroopList.Response>(100000)
println(data.contentToString()) println(data.contentToString())
*/ */
} }
/**
* 单线程处理包的接收, 分割和连接.
*/
@Suppress("PrivatePropertyName")
private val PacketReceiveDispatcher = newCoroutineDispatcher(1)
/**
* 单线程处理包的解析 (协程挂起效率够)
*/
@Suppress("PrivatePropertyName")
private val PacketProcessDispatcher = newCoroutineDispatcher(1)
/** /**
* 缓存超时处理的 [Job]. 超时后将清空缓存, 以免阻碍后续包的处理 * 缓存超时处理的 [Job]. 超时后将清空缓存, 以免阻碍后续包的处理
*/ */
...@@ -185,12 +160,12 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -185,12 +160,12 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
private var expectingRemainingLength: Long = 0 private var expectingRemainingLength: Long = 0
/** /**
* 在 [PacketProcessDispatcher] 调度器中解析包内容. * 解析包内容.
* *
* @param input 一个完整的包的内容, 去掉开头的 int 包长度 * @param input 一个完整的包的内容, 去掉开头的 int 包长度
*/ */
fun parsePacketAsync(input: Input): Job { fun parsePacketAsync(input: Input): Job {
return this.launch(PacketProcessDispatcher) { return this.launch {
input.use { parsePacket(it) } input.use { parsePacket(it) }
} }
} }
...@@ -337,27 +312,31 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -337,27 +312,31 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
val rawInput = try { val rawInput = try {
channel.read() channel.read()
} catch (e: ClosedChannelException) { } catch (e: ClosedChannelException) {
dispose() close()
bot.tryReinitializeNetworkHandler(e) bot.tryReinitializeNetworkHandler(e)
return return
} catch (e: ReadPacketInternalException) { } catch (e: ReadPacketInternalException) {
bot.logger.error("Socket channel read failed: ${e.message}") bot.logger.error("Socket channel read failed: ${e.message}")
dispose() close()
bot.tryReinitializeNetworkHandler(e) bot.tryReinitializeNetworkHandler(e)
return return
} catch (e: CancellationException) { } catch (e: CancellationException) {
return return
} catch (e: Throwable) { } catch (e: Throwable) {
bot.logger.error("Caught unexpected exceptions", e) bot.logger.error("Caught unexpected exceptions", e)
dispose() close()
bot.tryReinitializeNetworkHandler(e) bot.tryReinitializeNetworkHandler(e)
return return
} }
launch(context = PacketReceiveDispatcher + CoroutineName("Incoming Packet handler"), start = CoroutineStart.ATOMIC) { launch(CoroutineName("Incoming Packet handler"), start = CoroutineStart.ATOMIC) {
packetReceiveLock.withLock {
processPacket(rawInput) processPacket(rawInput)
} }
} }
} }
}
private val packetReceiveLock: Mutex = Mutex()
/** /**
* 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms) * 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms)
...@@ -381,7 +360,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -381,7 +360,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
} }
private suspend inline fun <E : Packet> OutgoingPacket.doSendAndReceive(timeoutMillis: Long = 3000, handler: PacketListener): E { private suspend inline fun <E : Packet> OutgoingPacket.doSendAndReceive(timeoutMillis: Long = 3000, handler: PacketListener): E {
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
channel.send(delegate) channel.send(delegate)
}
bot.logger.info("Send: ${this.commandName}") bot.logger.info("Send: ${this.commandName}")
return withTimeoutOrNull(timeoutMillis) { return withTimeoutOrNull(timeoutMillis) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
...@@ -403,11 +384,11 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -403,11 +384,11 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
fun filter(commandName: String, sequenceId: Int) = this.commandName == commandName && this.sequenceId == sequenceId fun filter(commandName: String, sequenceId: Int) = this.commandName == commandName && this.sequenceId == sequenceId
} }
override fun dispose(cause: Throwable?) { override fun close(cause: Throwable?) {
if (::channel.isInitialized) { if (::channel.isInitialized) {
channel.close() channel.close()
} }
super.dispose(cause) super.close(cause)
} }
override suspend fun awaitDisconnection() = supervisor.join() override suspend fun awaitDisconnection() = supervisor.join()
......
...@@ -67,7 +67,7 @@ internal class FriendListSubSrvRspCode( ...@@ -67,7 +67,7 @@ internal class FriendListSubSrvRspCode(
@Serializable @Serializable
internal class FriendInfo( internal class FriendInfo(
@SerialId(0) val friendUin: Long? = 0, @SerialId(0) val friendUin: Long,
@SerialId(1) val groupId: Byte, @SerialId(1) val groupId: Byte,
@SerialId(2) val faceId: Short, @SerialId(2) val faceId: Short,
@SerialId(3) val remark: String = "", @SerialId(3) val remark: String = "",
......
...@@ -16,7 +16,6 @@ import net.mamoe.mirai.utils.io.writeQQ ...@@ -16,7 +16,6 @@ import net.mamoe.mirai.utils.io.writeQQ
* 待发送给服务器的数据包. 它代表着一个 [ByteReadPacket]. * 待发送给服务器的数据包. 它代表着一个 [ByteReadPacket].
* 只有最终的包才会被包装为 [OutgoingPacket]. * 只有最终的包才会被包装为 [OutgoingPacket].
*/ */
@UseExperimental(ExperimentalUnsignedTypes::class)
internal class OutgoingPacket constructor( internal class OutgoingPacket constructor(
name: String?, name: String?,
val commandName: String, val commandName: String,
......
...@@ -12,7 +12,6 @@ import net.mamoe.mirai.qqandroid.utils.GuidSource ...@@ -12,7 +12,6 @@ import net.mamoe.mirai.qqandroid.utils.GuidSource
import net.mamoe.mirai.qqandroid.utils.MacOrAndroidIdChangeFlag import net.mamoe.mirai.qqandroid.utils.MacOrAndroidIdChangeFlag
import net.mamoe.mirai.qqandroid.utils.guidFlag import net.mamoe.mirai.qqandroid.utils.guidFlag
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.cryptor.contentToString
import net.mamoe.mirai.utils.cryptor.decryptBy import net.mamoe.mirai.utils.cryptor.decryptBy
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.io.discardExact import net.mamoe.mirai.utils.io.discardExact
...@@ -317,13 +316,13 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt ...@@ -317,13 +316,13 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
val subCommand = readUShort().toInt() val subCommand = readUShort().toInt()
println("subCommand=$subCommand") // println("subCommand=$subCommand")
val type = readUByte() val type = readUByte()
println("type=$type") // println("type=$type")
discardExact(2) discardExact(2)
val tlvMap: TlvMap = this.readTLVMap() val tlvMap: TlvMap = this.readTLVMap()
tlvMap.printTLVMap() // tlvMap.printTLVMap()
return when (type.toInt()) { return when (type.toInt()) {
0 -> onLoginSuccess(tlvMap, bot) 0 -> onLoginSuccess(tlvMap, bot)
1, 15 -> onErrorMessage(tlvMap) 1, 15 -> onErrorMessage(tlvMap)
...@@ -340,7 +339,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt ...@@ -340,7 +339,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
bot: QQAndroidBot bot: QQAndroidBot
): LoginPacketResponse.DeviceLockLogin { ): LoginPacketResponse.DeviceLockLogin {
bot.client.t104 = tlvMap.getOrFail(0x104) bot.client.t104 = tlvMap.getOrFail(0x104)
println("403: " + tlvMap[0x403]?.toUHexString()) // println("403: " + tlvMap[0x403]?.toUHexString())
return LoginPacketResponse.DeviceLockLogin(tlvMap[0x402]!!, tlvMap.getOrFail(0x403)) return LoginPacketResponse.DeviceLockLogin(tlvMap[0x402]!!, tlvMap.getOrFail(0x403))
} }
...@@ -392,10 +391,10 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt ...@@ -392,10 +391,10 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
@UseExperimental(MiraiDebugAPI::class) @UseExperimental(MiraiDebugAPI::class)
private fun onLoginSuccess(tlvMap: TlvMap, bot: QQAndroidBot): LoginPacketResponse.Success { private fun onLoginSuccess(tlvMap: TlvMap, bot: QQAndroidBot): LoginPacketResponse.Success {
val client = bot.client val client = bot.client
println("TLV KEYS: " + tlvMap.keys.joinToString { it.contentToString() }) //println("TLV KEYS: " + tlvMap.keys.joinToString { it.contentToString() })
tlvMap[0x150]?.let { client.analysisTlv150(it) } tlvMap[0x150]?.let { client.analysisTlv150(it) }
tlvMap[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") } // tlvMap[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") }
tlvMap[0x161]?.let { client.analysisTlv161(it) } tlvMap[0x161]?.let { client.analysisTlv161(it) }
tlvMap[0x119]?.let { t119Data -> tlvMap[0x119]?.let { t119Data ->
t119Data.decryptBy(client.tgtgtKey).toReadPacket().debugPrint("0x119data").apply { t119Data.decryptBy(client.tgtgtKey).toReadPacket().debugPrint("0x119data").apply {
......
...@@ -92,7 +92,7 @@ internal class TIMPCBotNetworkHandler internal constructor(coroutineContext: Cor ...@@ -92,7 +92,7 @@ internal class TIMPCBotNetworkHandler internal constructor(coroutineContext: Cor
} }
override fun dispose(cause: Throwable?) { override fun dispose(cause: Throwable?) {
super.dispose(cause) super.close(cause)
this.heartbeatJob?.cancel(CancellationException("handler closed")) this.heartbeatJob?.cancel(CancellationException("handler closed"))
this.heartbeatJob = null this.heartbeatJob = null
......
...@@ -22,7 +22,8 @@ data class BotAccount( ...@@ -22,7 +22,8 @@ data class BotAccount(
/** /**
* 标记直接访问 [BotAccount.id], 而不是访问 [Bot.uin]. 这可能会不兼容未来的 API 修改. * 标记直接访问 [BotAccount.id], 而不是访问 [Bot.uin]. 这可能会不兼容未来的 API 修改.
*/ */
@MiraiInternalAPI
@Retention(AnnotationRetention.SOURCE) @Retention(AnnotationRetention.SOURCE)
@Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR) @Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR)
@Experimental(level = Experimental.Level.WARNING) @Experimental(level = Experimental.Level.WARNING)
internal annotation class RawAccountIdUse annotation class RawAccountIdUse
\ No newline at end of file \ No newline at end of file
...@@ -92,20 +92,20 @@ abstract class BotImpl<N : BotNetworkHandler> constructor( ...@@ -92,20 +92,20 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
try { try {
if (::_network.isInitialized) { if (::_network.isInitialized) {
BotOfflineEvent(this).broadcast() BotOfflineEvent(this).broadcast()
_network.dispose(cause) _network.close(cause)
} }
} catch (e: Exception) { } catch (e: Exception) {
logger.error("Cannot close network handler", e) logger.error("Cannot close network handler", e)
} }
_network = createNetworkHandler(this.coroutineContext)
loginLoop@ while (true) { loginLoop@ while (true) {
_network = createNetworkHandler(this.coroutineContext)
try { try {
_network.login() _network.login()
break@loginLoop break@loginLoop
} catch (e: Exception) { } catch (e: Exception) {
e.logStacktrace() e.logStacktrace()
_network.dispose(e) _network.close(e)
} }
logger.warning("Login failed. Retrying in 3s...") logger.warning("Login failed. Retrying in 3s...")
delay(3000) delay(3000)
...@@ -116,7 +116,7 @@ abstract class BotImpl<N : BotNetworkHandler> constructor( ...@@ -116,7 +116,7 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
return _network.init() return _network.init()
} catch (e: Exception) { } catch (e: Exception) {
e.logStacktrace() e.logStacktrace()
_network.dispose(e) _network.close(e)
} }
logger.warning("Init failed. Retrying in 3s...") logger.warning("Init failed. Retrying in 3s...")
delay(3000) delay(3000)
...@@ -130,12 +130,12 @@ abstract class BotImpl<N : BotNetworkHandler> constructor( ...@@ -130,12 +130,12 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
override fun dispose(throwable: Throwable?) { override fun dispose(throwable: Throwable?) {
if (throwable == null) { if (throwable == null) {
network.dispose() network.close()
this.botJob.complete() this.botJob.complete()
groups.delegate.clear() groups.delegate.clear()
qqs.delegate.clear() qqs.delegate.clear()
} else { } else {
network.dispose(throwable) network.close(throwable)
this.botJob.completeExceptionally(throwable) this.botJob.completeExceptionally(throwable)
groups.delegate.clear() groups.delegate.clear()
qqs.delegate.clear() qqs.delegate.clear()
......
...@@ -24,7 +24,7 @@ import net.mamoe.mirai.utils.io.PlatformDatagramChannel ...@@ -24,7 +24,7 @@ import net.mamoe.mirai.utils.io.PlatformDatagramChannel
* - Key 刷新 * - Key 刷新
* - 所有数据包处理和发送 * - 所有数据包处理和发送
* *
* [BotNetworkHandler.dispose] 时将会 [取消][Job.cancel] 所有此作用域下的协程 * [BotNetworkHandler.close] 时将会 [取消][Job.cancel] 所有此作用域下的协程
*/ */
@Suppress("PropertyName") @Suppress("PropertyName")
abstract class BotNetworkHandler : CoroutineScope { abstract class BotNetworkHandler : CoroutineScope {
...@@ -64,12 +64,12 @@ abstract class BotNetworkHandler : CoroutineScope { ...@@ -64,12 +64,12 @@ abstract class BotNetworkHandler : CoroutineScope {
/** /**
* 关闭网络接口, 停止所有有关协程和任务 * 关闭网络接口, 停止所有有关协程和任务
*/ */
open fun dispose(cause: Throwable? = null) { open fun close(cause: Throwable? = null) {
if (supervisor.isActive) { if (supervisor.isActive) {
if (cause != null) { if (cause != null) {
supervisor.cancel(CancellationException("handler closed", cause)) supervisor.cancel(CancellationException("NetworkHandler closed", cause))
} else { } else {
supervisor.cancel() supervisor.cancel(CancellationException("NetworkHandler closed"))
} }
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment