From 561d333cc7702fc801d9ebdfe2a7ba6daa24356a Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Sun, 5 Dec 2021 18:28:44 +0800 Subject: [PATCH] NetInternalKit --- .../src/commonMain/kotlin/SizedCache.kt | 58 +++++++++++++ .../net/mamoe/mirai/utils/SizedCacheTest.kt | 60 +++++++++++++ .../src/jvmTest/kotlin/netinternalkit/Ansi.kt | 21 +++++ .../kotlin/netinternalkit/LogCapture.kt | 60 +++++++++++++ .../NetReplayHelper.kt | 87 +++++++++++++------ 5 files changed, 258 insertions(+), 28 deletions(-) create mode 100644 mirai-core-utils/src/commonMain/kotlin/SizedCache.kt create mode 100644 mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/SizedCacheTest.kt create mode 100644 mirai-core/src/jvmTest/kotlin/netinternalkit/Ansi.kt create mode 100644 mirai-core/src/jvmTest/kotlin/netinternalkit/LogCapture.kt rename mirai-core/src/jvmTest/kotlin/{bootstrap => netinternalkit}/NetReplayHelper.kt (75%) diff --git a/mirai-core-utils/src/commonMain/kotlin/SizedCache.kt b/mirai-core-utils/src/commonMain/kotlin/SizedCache.kt new file mode 100644 index 000000000..c093934b4 --- /dev/null +++ b/mirai-core-utils/src/commonMain/kotlin/SizedCache.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2019-2021 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import kotlinx.atomicfu.locks.withLock +import java.util.concurrent.locks.ReentrantLock + +@Suppress("unused", "UNCHECKED_CAST") +public class SizedCache(size: Int) : Iterable { + public val lock: ReentrantLock = ReentrantLock() + public val data: Array = arrayOfNulls(size) + + public var filled: Boolean = false + public var idx: Int = 0 + + public fun emit(v: T) { + lock.withLock { + data[idx] = v + idx++ + if (idx == data.size) { + filled = true + idx = 0 + } + } + } + + override fun iterator(): Iterator { + if (filled) { + return object : Iterator { + private var idx0: Int = idx + private var floopend = false + override fun hasNext(): Boolean = !floopend || idx0 != idx + override fun next(): T { + val rsp = data[idx0] as T + idx0++ + if (idx0 == data.size) { + idx0 = 0 + floopend = true + } + return rsp + } + } + } + + return object : Iterator { + private var idx0: Int = 0 + override fun hasNext(): Boolean = idx0 < idx + override fun next(): T = data[idx0].also { idx0++ } as T + } + } +} \ No newline at end of file diff --git a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/SizedCacheTest.kt b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/SizedCacheTest.kt new file mode 100644 index 000000000..eb3b368d6 --- /dev/null +++ b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/SizedCacheTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2019-2021 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +internal class SizedCacheTest { + @Test + fun validIfCacheNotFilled() { + val cache = SizedCache(20) + val list = mutableListOf() + repeat(10) { + cache.emit("-$it") + list.add("-$it") + } + assertEquals(list, cache.toList()) + } + + @Test + fun validIfNotUsed() { + val cache = SizedCache(20) + assertEquals(emptyList(), cache.toList()) + } + + @Test + fun validIfFilled() { + val cache = SizedCache(20) + val list = mutableListOf() + repeat(20) { + cache.emit("-$it") + list.add("-$it") + } + assertEquals(list, cache.toList()) + } + + @Test + fun chaosTest() { + repeat(1000) { + val size = (5..200).random() + val list = mutableListOf() + val cache = SizedCache(size) + repeat((100..5000).random().coerceAtLeast(size)) { + cache.emit(it) + list.add(it) + } + assertEquals( + list.subList(list.size - size, list.size).toMutableList(), + cache.toMutableList(), + ) + } + } +} \ No newline at end of file diff --git a/mirai-core/src/jvmTest/kotlin/netinternalkit/Ansi.kt b/mirai-core/src/jvmTest/kotlin/netinternalkit/Ansi.kt new file mode 100644 index 000000000..47daa5cdb --- /dev/null +++ b/mirai-core/src/jvmTest/kotlin/netinternalkit/Ansi.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2019-2021 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.netinternalkit + +// Copied from mirai-console/backend/mirai-console/src/util/AnsiMessageBuilder.kt + + +@Suppress("RegExpRedundantEscape") +private val DROP_CSI_PATTERN = """\u001b\[([\u0030-\u003F])*?([\u0020-\u002F])*?[\u0040-\u007E]""".toRegex() +private val DROP_ANSI_PATTERN = """\u001b[\u0040–\u005F]""".toRegex() + +internal fun String.dropAnsi(): String = this + .replace(DROP_CSI_PATTERN, "") // 先进行 CSI 剔除后进行 ANSI 剔除 + .replace(DROP_ANSI_PATTERN, "") diff --git a/mirai-core/src/jvmTest/kotlin/netinternalkit/LogCapture.kt b/mirai-core/src/jvmTest/kotlin/netinternalkit/LogCapture.kt new file mode 100644 index 000000000..dcea33a54 --- /dev/null +++ b/mirai-core/src/jvmTest/kotlin/netinternalkit/LogCapture.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2019-2021 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") + +package net.mamoe.mirai.internal.netinternalkit + +import kotlinx.atomicfu.locks.withLock +import net.mamoe.mirai.utils.DefaultFactoryOverrides +import net.mamoe.mirai.utils.PlatformLogger +import net.mamoe.mirai.utils.SizedCache +import net.mamoe.mirai.utils.currentTimeMillis +import java.io.File + +internal object LogCapture { + lateinit var logCache: SizedCache + private val output: (String) -> Unit = { log -> + println(log) + logCache.emit(log.dropAnsi()) + } + var outputDir = File("test/capture") + + fun setupCapture(maxLine: Int = 200) { + logCache = SizedCache(maxLine) + @Suppress("INVISIBLE_MEMBER") + DefaultFactoryOverrides.override { requester, identity -> + PlatformLogger( + identity ?: requester.kotlin.simpleName ?: requester.simpleName, + output + ) + } + + NetReplayHelperSettings.logger_console = PlatformLogger( + identity = "NetReplayHelper", + output = ::println + ) + NetReplayHelperSettings.logger_file = PlatformLogger( + identity = "NetReplayHelper", + output = { log -> logCache.emit(log.dropAnsi()) } + ) + } + + fun saveCapture(type: String = "capture") { + val output = outputDir.resolve("$type-${currentTimeMillis()}.txt") + logCache.lock.withLock { + output.also { it.parentFile.mkdirs() }.bufferedWriter().use { writer -> + logCache.forEach { line -> + writer.write(line) + writer.write(10) + } + } + } + } +} diff --git a/mirai-core/src/jvmTest/kotlin/bootstrap/NetReplayHelper.kt b/mirai-core/src/jvmTest/kotlin/netinternalkit/NetReplayHelper.kt similarity index 75% rename from mirai-core/src/jvmTest/kotlin/bootstrap/NetReplayHelper.kt rename to mirai-core/src/jvmTest/kotlin/netinternalkit/NetReplayHelper.kt index d20814be0..455bb859b 100644 --- a/mirai-core/src/jvmTest/kotlin/bootstrap/NetReplayHelper.kt +++ b/mirai-core/src/jvmTest/kotlin/netinternalkit/NetReplayHelper.kt @@ -10,7 +10,7 @@ @file:JvmName("NetReplayHelper") @file:Suppress("TestFunctionName") -package net.mamoe.mirai.internal.bootstrap +package net.mamoe.mirai.internal.netinternalkit import io.netty.channel.* import net.mamoe.mirai.Bot @@ -38,28 +38,42 @@ import kotlin.reflect.full.declaredMembers import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.javaField -private val droppedCommands: Collection by lazy { - listOf( - "Heartbeat.Alive", - "wtlogin.exchange_emp", - "StatSvc.register", - "StatSvc.GetDevLoginInfo", - "MessageSvc.PbGetMsg", - "friendlist.getFriendGroupList", - "friendlist.GetTroopListReqV2", - "friendlist.GetTroopMemberListReq", - "ConfigPushSvc.PushReq", - *PacketLoggingStrategyImpl.getDefaultBlacklist().toTypedArray(), - ) + +internal object NetReplayHelperSettings { + var commands_hide_hideAll: Collection by lateinitMutableProperty { + listOf( + "Heartbeat.Alive", + "wtlogin.exchange_emp", + "StatSvc.register", + "StatSvc.GetDevLoginInfo", + "MessageSvc.PbGetMsg", + "friendlist.getFriendGroupList", + "friendlist.GetTroopListReqV2", + "friendlist.GetTroopMemberListReq", + ) + } + + var commands_hide_hideInConsole: Collection by lateinitMutableProperty { + listOf( + "ConfigPushSvc.PushReq", + *PacketLoggingStrategyImpl.getDefaultBlacklist().toTypedArray(), + ) + } + + var logger_console: MiraiLogger by lateinitMutableProperty { + MiraiLogger.Factory.create(NetReplayHelperClass()) + } + + var logger_file: MiraiLogger = SilentLogger.withSwitch(false) + + @JvmField + val NetReplyHelper: Class<*> = NetReplayHelperClass() } private fun NetReplayHelperClass(): Class<*> { return MethodHandles.lookup().lookupClass() } -private val logger by lazy { - MiraiLogger.Factory.create(NetReplayHelperClass()) -} private fun attachNetReplayHelper(channel: Channel) { channel.pipeline() @@ -71,10 +85,27 @@ private fun attachNetReplayHelper(channel: Channel) { private fun newRawPacketDumper(): ChannelHandler = object : ChannelInboundHandlerAdapter() { override fun channelRead(ctx: ChannelHandlerContext?, msg: Any?) { if (msg is RawIncomingPacket) { - if (msg.commandName in droppedCommands) { - logger.debug { "sid=${msg.sequenceId}, cmd=${msg.commandName}, body=" } + if (msg.commandName in NetReplayHelperSettings.commands_hide_hideAll) { + NetReplayHelperSettings.logger_console.debug { + "sid=${msg.sequenceId}, cmd=${msg.commandName}, body=" + } + NetReplayHelperSettings.logger_file.debug { + "sid=${msg.sequenceId}, cmd=${msg.commandName}, body=" + } + super.channelRead(ctx, msg) + return + } + if (msg.commandName in NetReplayHelperSettings.commands_hide_hideInConsole) { + NetReplayHelperSettings.logger_console.debug { + "sid=${msg.sequenceId}, cmd=${msg.commandName}, body=" + } } else { - logger.debug { "sid=${msg.sequenceId}, cmd=${msg.commandName}, body=${msg.body.toUHexString()}" } + NetReplayHelperSettings.logger_console.debug { + "sid=${msg.sequenceId}, cmd=${msg.commandName}, body=${msg.body.toUHexString()}" + } + } + NetReplayHelperSettings.logger_file.debug { + "sid=${msg.sequenceId}, cmd=${msg.commandName}, body=${msg.body.toUHexString()}" } } super.channelRead(ctx, msg) @@ -156,7 +187,10 @@ private fun attachNetReplayWView(channel: Channel) { fun Component.onClick(handle: () -> Unit) { addMouseListener(object : MouseAdapter() { override fun mouseClicked(e: MouseEvent?) { - runCatching(handle).onFailure { logger.error(it) } + runCatching(handle).onFailure { err -> + NetReplayHelperSettings.logger_console.error(err) + NetReplayHelperSettings.logger_file.error(err) + } } }) } @@ -192,14 +226,11 @@ private fun attachNetReplayWView(channel: Channel) { frame.setLocationRelativeTo(null) frame.isVisible = true - channel.pipeline().addFirst(object : ChannelInboundHandlerAdapter() { - override fun channelInactive(ctx: ChannelHandlerContext?) { - SwingUtilities.invokeLater { - frame.dispose() - } - super.channelInactive(ctx) + channel.closeFuture().addListener { + SwingUtilities.invokeLater { + frame.dispose() } - }) + } }