mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-20 22:37:05 +08:00
NetInternalKit
This commit is contained in:
parent
d10d20302e
commit
561d333cc7
58
mirai-core-utils/src/commonMain/kotlin/SizedCache.kt
Normal file
58
mirai-core-utils/src/commonMain/kotlin/SizedCache.kt
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
mirai-core/src/jvmTest/kotlin/netinternalkit/Ansi.kt
Normal file
21
mirai-core/src/jvmTest/kotlin/netinternalkit/Ansi.kt
Normal 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, "")
|
60
mirai-core/src/jvmTest/kotlin/netinternalkit/LogCapture.kt
Normal file
60
mirai-core/src/jvmTest/kotlin/netinternalkit/LogCapture.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user