diff --git a/mirai-core-utils/src/commonMain/kotlin/CoroutineUtils.kt b/mirai-core-utils/src/commonMain/kotlin/CoroutineUtils.kt index 439ada516..415052fb9 100644 --- a/mirai-core-utils/src/commonMain/kotlin/CoroutineUtils.kt +++ b/mirai-core-utils/src/commonMain/kotlin/CoroutineUtils.kt @@ -63,6 +63,22 @@ public fun CoroutineScope.childScope( public fun CoroutineContext.childScope( coroutineContext: CoroutineContext = EmptyCoroutineContext, ): CoroutineScope { + return CoroutineScope(this.childScopeContext(coroutineContext)) +} + +/** + * Creates a child scope of the receiver context scope. + */ +public fun CoroutineContext.childScopeContext( + coroutineContext: CoroutineContext = EmptyCoroutineContext, +): CoroutineContext { val ctx = this + coroutineContext - return CoroutineScope(ctx + SupervisorJob(ctx.job)) + return ctx + SupervisorJob(ctx.job) +} + +public inline fun CoroutineContext.getOrElse( + key: CoroutineContext.Key, + default: () -> U +): U { + return this[key] ?: default() } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/AbstractBot.kt b/mirai-core/src/commonMain/kotlin/AbstractBot.kt index afd8c5f60..9bf0564a5 100644 --- a/mirai-core/src/commonMain/kotlin/AbstractBot.kt +++ b/mirai-core/src/commonMain/kotlin/AbstractBot.kt @@ -18,27 +18,24 @@ package net.mamoe.mirai.internal import kotlinx.coroutines.* -import kotlinx.coroutines.sync.Mutex import net.mamoe.mirai.Bot import net.mamoe.mirai.Mirai import net.mamoe.mirai.contact.* -import net.mamoe.mirai.event.ConcurrencyKind import net.mamoe.mirai.event.EventChannel -import net.mamoe.mirai.event.EventPriority.MONITOR import net.mamoe.mirai.event.GlobalEventChannel -import net.mamoe.mirai.event.Listener import net.mamoe.mirai.event.events.BotEvent -import net.mamoe.mirai.event.events.BotOfflineEvent import net.mamoe.mirai.internal.contact.info.FriendInfoImpl import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl import net.mamoe.mirai.internal.contact.uin +import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage import net.mamoe.mirai.internal.network.handler.NetworkHandler import net.mamoe.mirai.supervisorJob import net.mamoe.mirai.utils.* import kotlin.coroutines.CoroutineContext -import kotlin.time.ExperimentalTime -import kotlin.time.measureTime +/** + * Protocol-irrelevant implementations + */ internal abstract class AbstractBot constructor( final override val configuration: BotConfiguration, final override val id: Long, @@ -48,20 +45,17 @@ internal abstract class AbstractBot constructor( /////////////////////////////////////////////////////////////////////////// // FASTEST INIT - private val supervisor = SupervisorJob(configuration.parentCoroutineContext[Job]) - final override val logger: MiraiLogger by lazy { configuration.botLoggerSupplier(this) } - final override val coroutineContext: CoroutineContext = // for id - configuration.parentCoroutineContext - .plus(supervisor) - .plus( - configuration.parentCoroutineContext[CoroutineExceptionHandler] - ?: CoroutineExceptionHandler { _, e -> - logger.error("An exception was thrown under a coroutine of Bot", e) - } - ) - .plus(CoroutineName("Mirai Bot")) + final override val coroutineContext: CoroutineContext = configuration.parentCoroutineContext.childScopeContext( + configuration.parentCoroutineContext.getOrElse(CoroutineExceptionHandler) { + CoroutineExceptionHandler { _, e -> + logger.error("An exception was thrown under a coroutine of Bot", e) + } + } + CoroutineName("Mirai Bot") + ) + + abstract val components: ConcurrentComponentStorage init { @Suppress("LeakingThis") @@ -87,86 +81,6 @@ internal abstract class AbstractBot constructor( final override val asFriend: Friend by lazy { Mirai.newFriend(this, FriendInfoImpl(uin, nick, "")) } final override val asStranger: Stranger by lazy { Mirai.newStranger(bot, StrangerInfoImpl(bot.id, bot.nick)) } - /////////////////////////////////////////////////////////////////////////// - // sync (// TODO: 2021/4/14 extract sync logic - /////////////////////////////////////////////////////////////////////////// - - - val otherClientsLock = Mutex() // lock sync - - // TODO: 2021/4/14 extract offlineListener - - @OptIn(ExperimentalTime::class) - @Suppress("unused") - private val offlineListener: Listener = - this@AbstractBot.eventChannel.parentJob(supervisor).subscribeAlways( - priority = MONITOR, - concurrency = ConcurrencyKind.LOCKED - ) { event -> - val bot = bot.asQQAndroidBot() - if ( - !event.bot.isActive // bot closed - // || _isConnecting // bot 还在登入 // TODO: 2021/4/14 处理还在登入? - ) { - // Close network to avoid endless reconnection while network is ok - // https://github.com/mamoe/mirai/issues/894 - kotlin.runCatching { network.close(null) } - return@subscribeAlways - } - /* - if (network.areYouOk() && event !is BotOfflineEvent.Force && event !is BotOfflineEvent.MsfOffline) { - // network 运行正常 - return@subscribeAlways - }*/ - when (event) { - is BotOfflineEvent.Active -> { - val cause = event.cause - val msg = if (cause == null) "" else " with exception: $cause" - bot.logger.info("Bot is closed manually $msg", cause) - network.close(null) - } - is BotOfflineEvent.Force -> { - bot.logger.info { "Connection occupied by another android device: ${event.message}" } - bot.asQQAndroidBot().accountSecretsFile.delete() - bot.client = bot.initClient() - if (event.reconnect) { - bot.logger.info { "Reconnecting..." } - // delay(3000) - } else { - network.close(null) - } - } - is BotOfflineEvent.MsfOffline, - is BotOfflineEvent.Dropped, - is BotOfflineEvent.RequireReconnect, - is BotOfflineEvent.PacketFactoryErrorCode - -> { - // nothing to do - } - } - - if (event.reconnect) { - if (!network.isOk()) { - // normally closed - return@subscribeAlways - } - - val causeMessage = event.castOrNull()?.cause?.toString() ?: event.toString() - bot.logger.info { "Connection lost, retrying login ($causeMessage)" } - - bot.launch { - val success: Boolean - val time = measureTime { - success = TODO("relogin") - } - - if (success) { - logger.info { "Reconnected successfully in ${time.toHumanReadableString()}" } - } - } - } - } - /////////////////////////////////////////////////////////////////////////// // network /////////////////////////////////////////////////////////////////////////// @@ -191,7 +105,6 @@ internal abstract class AbstractBot constructor( kotlin.runCatching { network.close(throwable) } - offlineListener.cancel(CancellationException("Bot cancelled", throwable)) // help GC release instances groups.forEach { diff --git a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt index 0b94eb1e0..80f7fa06d 100644 --- a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt +++ b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt @@ -121,7 +121,7 @@ internal open class QQAndroidBot constructor( private val networkLogger: MiraiLogger by lazy { configuration.networkLoggerSupplier(this) } - internal val components: ConcurrentComponentStorage by lazy { + override val components: ConcurrentComponentStorage by lazy { ConcurrentComponentStorage().apply { val components = this // avoid mistakes set(SsoProcessorContext, SsoProcessorContextImpl(bot)) diff --git a/mirai-core/src/commonMain/kotlin/network/components/ContactUpdater.kt b/mirai-core/src/commonMain/kotlin/network/components/ContactUpdater.kt index 399456541..0c15ae559 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/ContactUpdater.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/ContactUpdater.kt @@ -53,6 +53,8 @@ import net.mamoe.mirai.utils.verbose * Uses [ContactCacheService]. */ internal interface ContactUpdater { + val otherClientsLock: Mutex + /** * Load all caches to the bot this [ContactUpdater] works for. * @@ -75,6 +77,7 @@ internal class ContactUpdaterImpl( val components: ComponentStorage, private val logger: MiraiLogger, ) : ContactUpdater { + override val otherClientsLock: Mutex = Mutex() private val cacheService get() = components[ContactCacheService] private val lock = Mutex() diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.RequestPushStatus.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.RequestPushStatus.kt index 2a94777b2..5e76e1ced 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.RequestPushStatus.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.RequestPushStatus.kt @@ -25,6 +25,7 @@ import net.mamoe.mirai.internal.contact.appId import net.mamoe.mirai.internal.contact.createOtherClient import net.mamoe.mirai.internal.message.contextualBugReportException import net.mamoe.mirai.internal.network.Packet +import net.mamoe.mirai.internal.network.components.ContactUpdater import net.mamoe.mirai.internal.network.handler.logger import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushStatus import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory @@ -36,7 +37,7 @@ internal object MessageSvcRequestPushStatus : IncomingPacketFactory( ) { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? { val packet = readUniPacket(RequestPushStatus.serializer()) - bot.otherClientsLock.withLock { + bot.components[ContactUpdater].otherClientsLock.withLock { val instanceInfo = packet.vecInstanceList?.firstOrNull() val appId = instanceInfo?.iAppId ?: 1 return when (packet.status.toInt()) { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt index e26d65054..2cc7222fd 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt @@ -29,6 +29,7 @@ import net.mamoe.mirai.internal.contact.createOtherClient import net.mamoe.mirai.internal.message.contextualBugReportException import net.mamoe.mirai.internal.network.* import net.mamoe.mirai.internal.network.components.ContactCacheService +import net.mamoe.mirai.internal.network.components.ContactUpdater import net.mamoe.mirai.internal.network.protocol.data.jce.* import net.mamoe.mirai.internal.network.protocol.data.proto.Oidb0x769 import net.mamoe.mirai.internal.network.protocol.data.proto.StatSvcGetOnline @@ -232,11 +233,6 @@ internal class StatSvc { } - private fun String.ipToLong(): Long { - return split('.').foldIndexed(0L) { index: Int, acc: Long, s: String -> - acc or (((s.toLongOrNull() ?: 0) shl (index * 16))) - } - } } internal object ReqMSFOffline : @@ -283,7 +279,7 @@ internal class StatSvc { IncomingPacketFactory("StatSvc.SvcReqMSFLoginNotify", "StatSvc.SvcReqMSFLoginNotify") { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? = - bot.otherClientsLock.withLock { + bot.components[ContactUpdater].otherClientsLock.withLock { val notify = readUniPacket(SvcReqMSFLoginNotifyData.serializer()) val appId = notify.iAppId.toInt()