NetInternalKit

This commit is contained in:
Karlatemp 2021-12-05 18:28:44 +08:00
parent d10d20302e
commit 561d333cc7
No known key found for this signature in database
GPG Key ID: 21FBDDF664FF06F8
5 changed files with 258 additions and 28 deletions

View File

@ -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<T>(size: Int) : Iterable<T> {
public val lock: ReentrantLock = ReentrantLock()
public val data: Array<Any?> = 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<T> {
if (filled) {
return object : Iterator<T> {
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<T> {
private var idx0: Int = 0
override fun hasNext(): Boolean = idx0 < idx
override fun next(): T = data[idx0].also { idx0++ } as T
}
}
}

View File

@ -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<String>(20)
val list = mutableListOf<String>()
repeat(10) {
cache.emit("-$it")
list.add("-$it")
}
assertEquals(list, cache.toList())
}
@Test
fun validIfNotUsed() {
val cache = SizedCache<String>(20)
assertEquals(emptyList(), cache.toList())
}
@Test
fun validIfFilled() {
val cache = SizedCache<String>(20)
val list = mutableListOf<String>()
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<Int>()
val cache = SizedCache<Int>(size)
repeat((100..5000).random().coerceAtLeast(size)) {
cache.emit(it)
list.add(it)
}
assertEquals(
list.subList(list.size - size, list.size).toMutableList(),
cache.toMutableList(),
)
}
}
}

View File

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

View File

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

View File

@ -10,7 +10,7 @@
@file:JvmName("NetReplayHelper") @file:JvmName("NetReplayHelper")
@file:Suppress("TestFunctionName") @file:Suppress("TestFunctionName")
package net.mamoe.mirai.internal.bootstrap package net.mamoe.mirai.internal.netinternalkit
import io.netty.channel.* import io.netty.channel.*
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
@ -38,28 +38,42 @@ import kotlin.reflect.full.declaredMembers
import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField import kotlin.reflect.jvm.javaField
private val droppedCommands: Collection<String> by lazy {
listOf( internal object NetReplayHelperSettings {
"Heartbeat.Alive", var commands_hide_hideAll: Collection<String> by lateinitMutableProperty {
"wtlogin.exchange_emp", listOf(
"StatSvc.register", "Heartbeat.Alive",
"StatSvc.GetDevLoginInfo", "wtlogin.exchange_emp",
"MessageSvc.PbGetMsg", "StatSvc.register",
"friendlist.getFriendGroupList", "StatSvc.GetDevLoginInfo",
"friendlist.GetTroopListReqV2", "MessageSvc.PbGetMsg",
"friendlist.GetTroopMemberListReq", "friendlist.getFriendGroupList",
"ConfigPushSvc.PushReq", "friendlist.GetTroopListReqV2",
*PacketLoggingStrategyImpl.getDefaultBlacklist().toTypedArray(), "friendlist.GetTroopMemberListReq",
) )
}
var commands_hide_hideInConsole: Collection<String> 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<*> { private fun NetReplayHelperClass(): Class<*> {
return MethodHandles.lookup().lookupClass() return MethodHandles.lookup().lookupClass()
} }
private val logger by lazy {
MiraiLogger.Factory.create(NetReplayHelperClass())
}
private fun attachNetReplayHelper(channel: Channel) { private fun attachNetReplayHelper(channel: Channel) {
channel.pipeline() channel.pipeline()
@ -71,10 +85,27 @@ private fun attachNetReplayHelper(channel: Channel) {
private fun newRawPacketDumper(): ChannelHandler = object : ChannelInboundHandlerAdapter() { private fun newRawPacketDumper(): ChannelHandler = object : ChannelInboundHandlerAdapter() {
override fun channelRead(ctx: ChannelHandlerContext?, msg: Any?) { override fun channelRead(ctx: ChannelHandlerContext?, msg: Any?) {
if (msg is RawIncomingPacket) { if (msg is RawIncomingPacket) {
if (msg.commandName in droppedCommands) { if (msg.commandName in NetReplayHelperSettings.commands_hide_hideAll) {
logger.debug { "sid=${msg.sequenceId}, cmd=${msg.commandName}, body=<DROPPED>" } NetReplayHelperSettings.logger_console.debug {
"sid=${msg.sequenceId}, cmd=${msg.commandName}, body=<DROPPED>"
}
NetReplayHelperSettings.logger_file.debug {
"sid=${msg.sequenceId}, cmd=${msg.commandName}, body=<DROPPED>"
}
super.channelRead(ctx, msg)
return
}
if (msg.commandName in NetReplayHelperSettings.commands_hide_hideInConsole) {
NetReplayHelperSettings.logger_console.debug {
"sid=${msg.sequenceId}, cmd=${msg.commandName}, body=<DROPPED>"
}
} else { } 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) super.channelRead(ctx, msg)
@ -156,7 +187,10 @@ private fun attachNetReplayWView(channel: Channel) {
fun Component.onClick(handle: () -> Unit) { fun Component.onClick(handle: () -> Unit) {
addMouseListener(object : MouseAdapter() { addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent?) { 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.setLocationRelativeTo(null)
frame.isVisible = true frame.isVisible = true
channel.pipeline().addFirst(object : ChannelInboundHandlerAdapter() { channel.closeFuture().addListener {
override fun channelInactive(ctx: ChannelHandlerContext?) { SwingUtilities.invokeLater {
SwingUtilities.invokeLater { frame.dispose()
frame.dispose()
}
super.channelInactive(ctx)
} }
}) }
} }