Commit e6cb229b authored by Him188's avatar Him188

Unified packet sending

parent a448dbba
...@@ -538,64 +538,73 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -538,64 +538,73 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
check(this@QQAndroidBotNetworkHandler.isActive) { "network is dead therefore can't send any packet" } check(this@QQAndroidBotNetworkHandler.isActive) { "network is dead therefore can't send any packet" }
logger.verbose("Send: ${this.commandName}") logger.verbose("Send: ${this.commandName}")
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) { withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
PacketLogger.debug { "Channel sending: $commandName" }
channel.send(delegate) channel.send(delegate)
PacketLogger.debug { "Channel send done: $commandName" }
} }
} }
class TimeoutException(override val message: String?) : Exception()
/** /**
* 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms) * 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms)
* *
* @param retry 当不为 0 时将使用 [ByteArrayPool] 缓存. 因此若非必要, 请不要允许 retry * @param retry 当不为 0 时将使用 [ByteArrayPool] 缓存. 因此若非必要, 请不要允许 retry
*/ */
suspend fun <E : Packet> OutgoingPacket.sendAndExpect(timeoutMillis: Long = 3000, retry: Int = 0): E { suspend fun <E : Packet> OutgoingPacket.sendAndExpect(timeoutMillis: Long = 3000, retry: Int = 0): E {
require(timeoutMillis > 0) { "timeoutMillis must > 0" } require(timeoutMillis > 100) { "timeoutMillis must > 100" }
require(retry >= 0) { "retry must >= 0" } require(retry >= 0) { "retry must >= 0" }
check(bot.isActive) { "bot is dead therefore can't send any packet" } check(bot.isActive) { "bot is dead therefore can't send any packet" }
check(this@QQAndroidBotNetworkHandler.isActive) { "network is dead therefore can't send any packet" } check(this@QQAndroidBotNetworkHandler.isActive) { "network is dead therefore can't send any packet" }
var lastException: Exception? = null suspend fun doSendAndReceive(handler: PacketListener, data: Any, length: Int): E {
if (retry == 0) { val result = async {
val handler = PacketListener(commandName = commandName, sequenceId = sequenceId) withTimeoutOrNull(3000) {
packetListeners.addLast(handler)
try {
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) { withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
channel.send(delegate) PacketLogger.debug { "Channel sending: $commandName" }
when (data) {
is ByteArray -> channel.send(data, 0, length)
is ByteReadPacket -> channel.send(data)
else -> error("Internal error: unexpected data type: ${data::class.simpleName}")
} }
logger.verbose("Send: ${this.commandName}") PacketLogger.debug { "Channel send done: $commandName" }
return withTimeoutOrNull(timeoutMillis) { }
} ?: return@async "timeout sending packet $commandName"
logger.verbose("Send done: $commandName")
withTimeoutOrNull(timeoutMillis) {
handler.await()
// 不要 `withTimeout`. timeout 的报错会不正常.
} ?: return@async "timeout receiving response of $commandName"
}
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
handler.await() as E when (val value = result.await()) {
// 不要 `withTimeout`. timeout 的异常会不知道去哪了. is String -> throw TimeoutException(value)
} ?: net.mamoe.mirai.qqandroid.utils.inline { else -> return value as E
error("timeout when receiving response of $commandName")
} }
}
if (retry == 0) {
val handler = PacketListener(commandName = commandName, sequenceId = sequenceId)
packetListeners.addLast(handler)
try {
return doSendAndReceive(handler, delegate, 0) // no need
} finally { } finally {
packetListeners.remove(handler) packetListeners.remove(handler)
} }
} else this.delegate.useBytes { data, length -> } else this.delegate.useBytes { data, length ->
repeat(retry + 1) { return tryNTimes(retry + 1) {
val handler = PacketListener(commandName = commandName, sequenceId = sequenceId) val handler = PacketListener(commandName = commandName, sequenceId = sequenceId)
packetListeners.addLast(handler) packetListeners.addLast(handler)
try { try {
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) { doSendAndReceive(handler, data, length)
channel.send(data, 0, length)
}
logger.verbose("Send: ${this.commandName}")
return withTimeoutOrNull(timeoutMillis) {
@Suppress("UNCHECKED_CAST")
handler.await() as E
// 不要 `withTimeout`. timeout 的异常会不知道去哪了.
} ?: net.mamoe.mirai.qqandroid.utils.inline {
error("timeout when receiving response of $commandName")
}
} catch (e: Exception) {
lastException = e
} finally { } finally {
packetListeners.remove(handler) packetListeners.remove(handler)
} }
} }
throw lastException!!
} }
} }
......
...@@ -214,6 +214,7 @@ internal object KnownPacketFactories { ...@@ -214,6 +214,7 @@ internal object KnownPacketFactories {
bot.network.pendingIncomingPackets?.addLast(it.also { bot.network.pendingIncomingPackets?.addLast(it.also {
it.consumer = consumer it.consumer = consumer
it.flag2 = flag2 it.flag2 = flag2
PacketLogger.info { "Cached ${it.commandName} #${it.sequenceId}" }
}) ?: handleIncomingPacket(it, bot, flag2, consumer) }) ?: handleIncomingPacket(it, bot, flag2, consumer)
} else { } else {
handleIncomingPacket(it, bot, flag2, consumer) handleIncomingPacket(it, bot, flag2, consumer)
...@@ -234,6 +235,7 @@ internal object KnownPacketFactories { ...@@ -234,6 +235,7 @@ internal object KnownPacketFactories {
return return
} }
PacketLogger.info { "Handle packet: ${it.commandName}" }
it.data.withUse { it.data.withUse {
when (flag2) { when (flag2) {
0, 1 -> 0, 1 ->
......
...@@ -86,7 +86,7 @@ internal class Handler<in E : Event> ...@@ -86,7 +86,7 @@ internal class Handler<in E : Event>
internal fun <E : Event> KClass<out E>.listeners(): EventListeners<E> = EventListenerManager.get(this) internal fun <E : Event> KClass<out E>.listeners(): EventListeners<E> = EventListenerManager.get(this)
internal class EventListeners<E : Event>(clazz: KClass<E>) : LockFreeLinkedList<Listener<E>>() { internal class EventListeners<E : Event>(clazz: KClass<E>) : LockFreeLinkedList<Listener<E>>() {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST", "UNSUPPORTED", "NO_REFLECTION_IN_CLASS_PATH")
val supertypes: Set<KClass<out Event>> by lazy { val supertypes: Set<KClass<out Event>> by lazy {
val supertypes = mutableSetOf<KClass<out Event>>() val supertypes = mutableSetOf<KClass<out Event>>()
......
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