From 048fe647f044d5439dc43c34fbd31b9ba7f81279 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Thu, 31 Oct 2019 22:19:26 +0800
Subject: [PATCH] Reduce multiplatform functions

---
 build.gradle                                  |   4 +-
 .../main/java/net/mamoe/mirai/MiraiMain.java  |   3 -
 .../main/java/net/mamoe/mirai/MiraiServer.kt  | 186 ---------------
 .../commonMain/kotlin/net.mamoe.mirai/Bot.kt  |   5 +-
 .../kotlin/net.mamoe.mirai/event/Event.kt     |   9 +-
 .../kotlin/net.mamoe.mirai/message/Message.kt |  28 ++-
 .../message/internal/MessageDataInternal.kt   |   2 +-
 .../protocol/tim/TIMBotNetworkHandler.kt      |  40 ++--
 .../network/protocol/tim/TIMProtocol.kt       |   5 +-
 .../tim/handler/ActionPacketHandler.kt        |   3 +-
 .../tim/packet/UnknownServerPacket.kt         |   2 +-
 .../tim/packet/event/ServerEventPackets.kt    |   8 +-
 .../network/protocol/tim/packet/login/SKey.kt |   4 +-
 .../net.mamoe.mirai/utils/MiraiLogger.kt      | 221 ++++++++++--------
 .../net.mamoe.mirai/utils/PlatformLogger.kt   |   2 +
 .../net.mamoe.mirai/utils/PlatformUtils.kt    |   5 -
 .../net.mamoe.mirai/utils/io/DebugUtil.kt     |  18 +-
 .../utils/io/HexComparator.kt                 | 197 +++++++++-------
 .../net.mamoe.mirai/utils/io/InputUtils.kt    |   6 +-
 .../mamoe/mirai/utils/CaptchaResolverJvm.kt   |  10 +-
 .../net/mamoe/mirai/utils/MiraiLoggerJvm.kt   |  54 +++--
 .../net/mamoe/mirai/utils/PlatformUtilsJvm.kt |   6 +-
 .../src/jvmTest/kotlin}/HexComparator.kt      |   0
 mirai-core/src/jvmTest/kotlin/Run.kt          |   0
 24 files changed, 345 insertions(+), 473 deletions(-)
 delete mode 100644 mirai-console/src/main/java/net/mamoe/mirai/MiraiServer.kt
 create mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformLogger.kt
 rename mirai-core/src/{jvmMain/kotlin/net/mamoe/mirai => commonMain/kotlin/net.mamoe.mirai}/utils/io/HexComparator.kt (63%)
 rename {mirai-debug/src/main/java => mirai-core/src/jvmTest/kotlin}/HexComparator.kt (100%)
 create mode 100644 mirai-core/src/jvmTest/kotlin/Run.kt

diff --git a/build.gradle b/build.gradle
index 73f2eff73..cf3c40f74 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,7 +9,7 @@ buildscript {
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.4.2'
+        classpath 'com.android.tools.build:gradle:3.5.1'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
         classpath "org.jetbrains.kotlinx:atomicfu-gradle-plugin:$atomicfu_version"
     }
@@ -25,4 +25,4 @@ allprojects {
         google()
         maven { url "https://mirrors.huaweicloud.com/repository/maven/" }
     }
-}
\ No newline at end of file
+}
diff --git a/mirai-console/src/main/java/net/mamoe/mirai/MiraiMain.java b/mirai-console/src/main/java/net/mamoe/mirai/MiraiMain.java
index ccb8eb44a..a2bc5301a 100644
--- a/mirai-console/src/main/java/net/mamoe/mirai/MiraiMain.java
+++ b/mirai-console/src/main/java/net/mamoe/mirai/MiraiMain.java
@@ -5,9 +5,6 @@ package net.mamoe.mirai;
  * @author NaturalHG
  */
 public final class MiraiMain {
-    private static MiraiServer server;
-
     public static void main(String[] args) {
-        Runtime.getRuntime().addShutdownHook(new Thread(() -> server.shutdown()));
     }
 }
\ No newline at end of file
diff --git a/mirai-console/src/main/java/net/mamoe/mirai/MiraiServer.kt b/mirai-console/src/main/java/net/mamoe/mirai/MiraiServer.kt
deleted file mode 100644
index 9e7d1165e..000000000
--- a/mirai-console/src/main/java/net/mamoe/mirai/MiraiServer.kt
+++ /dev/null
@@ -1,186 +0,0 @@
-@file:Suppress("EXPERIMENTAL_API_USAGE")
-
-package net.mamoe.mirai
-
-import kotlinx.coroutines.runBlocking
-import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
-import net.mamoe.mirai.utils.LoggerTextFormat
-import net.mamoe.mirai.utils.MiraiLogger
-import net.mamoe.mirai.utils.config.MiraiConfig
-import net.mamoe.mirai.utils.setting.MiraiSettings
-import java.io.File
-import java.io.IOException
-import java.util.concurrent.ExecutionException
-
-
-/**
- * Mirai 服务器.
- * 管理一些基础的事务
- *
- * @author NaturalHG
- */
-object MiraiServer {
-    var isUnix: Boolean = false
-        private set
-
-    var parentFolder: File = File(System.getProperty("user.dir"))
-
-    var logger: MiraiLogger
-        internal set
-
-    internal lateinit var settings: MiraiSettings
-
-    internal lateinit var qqs: MiraiConfig
-
-    private var enabled: Boolean = false
-
-
-    init {
-        this.isUnix = !System.getProperties().getProperty("os.name").toUpperCase().contains("WINDOWS")
-
-        this.logger = MiraiLogger
-
-        logger.logInfo("About to run Mirai (" + Mirai.VERSION + ") under " + if (isUnix) "unix" else "windows")
-        logger.logInfo("Loading data under " + LoggerTextFormat.GREEN + this.parentFolder)
-
-        val setting = File(this.parentFolder, "/Mirai.ini")
-        logger.logInfo("Selecting setting from " + LoggerTextFormat.GREEN + setting)
-
-        /*
-        if (!setting.exists()) {
-            this.initSetting(setting);
-        } else {
-            this.settings = new MiraiSettings(setting);
-        }
-
-        File qqs = new File(this.parentFolder + "/QQ.yml");
-        getLogger().logInfo("Reading QQ accounts from  " + LoggerTextFormat.GREEN + qqs);
-        if (!qqs.exists()) {
-            this.initQQConfig(qqs);
-        } else {
-            this.qqs = new MiraiConfig(qqs);
-        }
-        if (this.qqs.isEmpty()) {
-            this.initQQConfig(qqs);
-        }*/
-
-        /*
-        MiraiSettingMapSection qqs = this.setting.getMapSection("qq");
-        qqs.forEach((a,p) -> {
-            this.getLogger().logInfo("Finding available ports between " + "1-65536");
-            try {
-                int port = MiraiNetwork.getAvailablePort();
-                this.getLogger().logInfo("Listening on port " + port);
-
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-        });
-        */
-
-
-        this.reload()
-    }
-
-    fun shutdown() {
-        if (this.enabled) {
-            logger.logInfo("About to shutdown Mirai")
-            logger.logInfo("Data have been saved")
-        }
-
-    }
-
-    private fun initSetting(setting: File) {
-        logger.logInfo("Thanks for using Mirai")
-        logger.logInfo("initializing Settings")
-        try {
-            if (setting.createNewFile()) {
-                logger.logInfo("Mirai Config Created")
-            }
-        } catch (e: IOException) {
-            e.printStackTrace()
-        }
-
-        this.settings = MiraiSettings(setting)
-        val network = this.settings.getMapSection("network")
-        network["enable_proxy"] = "not supporting yet"
-
-        val proxy = this.settings.getListSection("proxy")
-        proxy.add("1.2.3.4:95")
-        proxy.add("1.2.3.4:100")
-
-        val worker = this.settings.getMapSection("worker")
-        worker["core_task_pool_worker_amount"] = 5
-
-        val plugin = this.settings.getMapSection("plugin")
-        plugin["logDebug"] = false
-
-        this.settings.save()
-        logger.logInfo("initialized; changing can be made in setting file: $setting")
-    }
-
-    private fun initQQConfig(qqConfig: File) {
-        this.qqs = MiraiConfig(qqConfig)
-        MiraiLogger.logInfo("QQ account initialized; changing can be made in Config file: $qqConfig")
-        logger.logInfo("QQ 账户管理初始化完毕")
-    }
-
-    private fun reload() {
-        this.enabled = true
-        MiraiLogger.logInfo(LoggerTextFormat.GREEN.toString() + "Server enabled; Welcome to Mirai")
-        MiraiLogger.logInfo("Mirai Version=" + Mirai.VERSION)
-
-        MiraiLogger.logInfo("Initializing [Bot]s")
-
-        try {
-            availableBot
-        } catch (e: ExecutionException) {
-            e.printStackTrace()
-        } catch (e: InterruptedException) {
-            e.printStackTrace()
-        }
-
-        /*
-        this.qqs.keySet().stream().map(key -> this.qqs.getSection(key)).forEach(section -> {
-            getLogger().logInfo("Initializing [Bot] " + section.getString("account"));
-            try {
-                Bot bot = new Bot(section);
-                var state = bot.network.login$mirai_core().of();
-                //bot.network.login$mirai_core().whenComplete((state, e) -> {
-                if (state == LoginState.REQUIRE_UPLOAD) {
-                    Bot.instances.add(bot);
-                    getLogger().logGreen("   Login Succeed");
-                } else {
-                    getLogger().logError("   Login Failed with logError " + state);
-                    bot.close();
-                }
-                //  }).of();
-
-            } catch (Throwable e) {
-                e.printStackTrace();
-                getLogger().logError("Could not load QQ bots config!");
-                System.exit(1);
-            }
-        });*/
-    }
-
-
-    //todo only for test now
-    private var qqList = "1683921395----bb22222\n"
-
-    private val availableBot: Bot
-        @Throws(ExecutionException::class, InterruptedException::class)
-        get() {
-            for (it in qqList.split("\n").dropLastWhile { it.isEmpty() }.toTypedArray()) {
-                val strings = it.split("----").dropLastWhile { it.isEmpty() }.toTypedArray()
-                val bot = Bot(BotAccount(strings[0].toUInt(), strings[1]), MiraiLogger)
-
-                if (runBlocking { bot.login() } === LoginResult.SUCCESS) {
-                    bot.logger.logGreen("Login succeed")
-                    return bot
-                }
-            }
-
-            throw RuntimeException()
-        }
-}
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
index a028f44a2..ff4b88dea 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
@@ -14,7 +14,6 @@ import net.mamoe.mirai.utils.BotNetworkConfiguration
 import net.mamoe.mirai.utils.DefaultLogger
 import net.mamoe.mirai.utils.MiraiLogger
 import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail
-import net.mamoe.mirai.utils.log
 import kotlin.jvm.JvmOverloads
 
 data class BotAccount(
@@ -75,11 +74,11 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) {
         configuration: BotNetworkConfiguration,
         cause: Throwable? = null
     ): LoginResult {
-        logger.logPurple("Reinitializing BotNetworkHandler")
+        logger.warning("Reinitializing BotNetworkHandler")
         try {
             network.close(cause)
         } catch (e: Exception) {
-            e.log()
+            logger.error(e)
         }
         network = TIMBotNetworkHandler(this)
         return network.login(configuration)
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt
index 1dbf61861..77f32bd1c 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Event.kt
@@ -10,7 +10,6 @@ import net.mamoe.mirai.event.internal.broadcastInternal
 import net.mamoe.mirai.network.BotNetworkHandler
 import net.mamoe.mirai.utils.DefaultLogger
 import net.mamoe.mirai.utils.MiraiLogger
-import net.mamoe.mirai.utils.log
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
 import kotlin.jvm.JvmOverloads
@@ -44,7 +43,7 @@ abstract class Event {
 
     init {
         if (EventDebuggingFlag) {
-            EventLogger.logDebug(this::class.simpleName + " created")
+            EventLogger.debug(this::class.simpleName + " created")
         }
     }
 }
@@ -66,20 +65,20 @@ interface Cancellable {
 
 /**
  * 广播一个事件的唯一途径.
- * 若 [context] 不包含 [CoroutineExceptionHandler], 将会使用默认的异常捕获, 即 [log]
+ * 若 [context] 不包含 [CoroutineExceptionHandler], 将会使用默认的异常捕获, 即 [error]
  * 也就是说, 这个方法不会抛出异常, 只会把异常交由 [context] 捕获
  */
 @Suppress("UNCHECKED_CAST")
 @JvmOverloads
 suspend fun <E : Event> E.broadcast(context: CoroutineContext = EmptyCoroutineContext): E {
     if (EventDebuggingFlag) {
-        EventLogger.logDebug(this::class.simpleName + " pre broadcast")
+        EventLogger.debug(this::class.simpleName + " pre broadcast")
     }
     try {
         return withContext(EventScope.coroutineContext + context) { this@broadcast.broadcastInternal() }
     } finally {
         if (EventDebuggingFlag) {
-            EventLogger.logDebug(this::class.simpleName + " after broadcast")
+            EventLogger.debug(this::class.simpleName + " after broadcast")
         }
     }
 }
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/Message.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/Message.kt
index 42868257e..528c51f67 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/Message.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/Message.kt
@@ -13,18 +13,34 @@ import net.mamoe.mirai.utils.ExternalImage
  * 采用这样的消息模式是因为 QQ 的消息多元化, 一条消息中可包含 [纯文本][PlainText], [图片][Image] 等.
  *
  * **在 Kotlin 使用 [Message]**
- *  这与使用 [String] 的使用非常类似.
+ * 这与使用 [String] 的使用非常类似.
  *
- *  比较 [Message] 与 [String] (使用 infix [Message.eq]):
+ * 比较 [Message] 与 [String] (使用 infix [Message.eq]):
  *  `if(event eq "你好") qq.sendMessage(event)`
  *
- *  连接 [Message] 与 [Message], [String], (使用 operator [Message.plus]):
- *  ```
+ * 连接 [Message] 与 [Message], [String], (使用 operator [Message.plus]):
+ *  ```kotlin
  *      event = PlainText("Hello ")
  *      qq.sendMessage(event + "world")
  *  ```
  *
- *  但注意: 不能 `String + Message`. 只能 `Message + String`
+ * `Message1 + Message2 + Message3`, 类似 [String] 的连接:
+ *
+ *   +----------+   plus  +----------+   plus  +----------+
+ *   | Message1 | <------ | Message2 | <------ | Message3 |
+ *   +----------+         +----------+         +----------+
+ *        |                    |                    |
+ *        +--------------------+--------------------+
+ *                             |
+ *                             | 构成
+ *                             |
+ *                             V
+ *                      +--------------+
+ *                      | MessageChain |
+ *                      +--------------+
+ *
+ *
+ * 但注意: 不能 `String + Message`. 只能 `Message + String`
  *
  * @see PlainText 纯文本
  * @see Image 图片
@@ -49,7 +65,7 @@ interface Message {
      * 除 [MessageChain] 外, 每个 [Message] 类型都拥有一个`伴生对象`(companion object) 来持有一个 Key
      * 在 [MessageChain.get] 时将会使用到这个 Key 进行判断类型.
      *
-     * @param M 指代持有它的消息类型
+     * @param M 指代持有这个 Key 的消息类型
      */
     interface Key<M : Message>
 
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt
index 922d225be..fe6d4908c 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt
@@ -33,7 +33,7 @@ internal fun IoBuffer.parseLongText0x19(): PlainText {
 
 internal fun IoBuffer.parseMessageImage0x06(): Image {
     discardExact(1)
-    //MiraiLogger.logDebug(this.toUHexString())
+    //MiraiLogger.debug(this.toUHexString())
     val filenameLength = readShort()
 
     discardExact(filenameLength.toInt())
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt
index ed619ed53..80c649cb8 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt
@@ -26,7 +26,6 @@ import net.mamoe.mirai.qqAccount
 import net.mamoe.mirai.utils.BotNetworkConfiguration
 import net.mamoe.mirai.utils.OnlineStatus
 import net.mamoe.mirai.utils.io.*
-import net.mamoe.mirai.utils.log
 import net.mamoe.mirai.utils.solveCaptcha
 import kotlin.coroutines.CoroutineContext
 
@@ -39,7 +38,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
 
     override val coroutineContext: CoroutineContext =
         Dispatchers.Default + CoroutineExceptionHandler { _, e ->
-            bot.logger.log(e)
+            bot.logger.error(e)
         } + SupervisorJob()
 
 
@@ -61,7 +60,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
 
     override suspend fun login(configuration: BotNetworkConfiguration): LoginResult = withContext(this.coroutineContext) {
         TIMProtocol.SERVER_IP.forEach { ip ->
-            bot.logger.logPurple("Connecting server $ip")
+            bot.logger.warning("Connecting server $ip")
             socket = BotSocketAdapter(ip, configuration)
 
             loginResult = CompletableDeferred()
@@ -69,7 +68,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
             socket.resendTouch().takeIf { it != LoginResult.TIMEOUT }?.let { return@withContext it }
 
             println()
-            bot.logger.logPurple("Timeout. Retrying next server")
+            bot.logger.warning("Timeout. Retrying next server")
 
             socket.close()
         }
@@ -86,7 +85,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
 
         add(EventPacketHandler(session).asNode(EventPacketHandler))
         add(ActionPacketHandler(session).asNode(ActionPacketHandler))
-        bot.logger.logPurple("Successfully logged in")
+        bot.logger.warning("Successfully logged in")
     }
 
     private lateinit var sessionKey: ByteArray
@@ -134,15 +133,14 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
                     close()
                     return
                 } catch (e: ReadPacketInternalException) {
-                    bot.logger.logError("Socket channel read failed: ${e.message}")
+                    bot.logger.error("Socket channel read failed: ${e.message}")
                     continue
                 } catch (e: Throwable) {
-                    bot.logger.logError("Caught unexpected exceptions")
-                    bot.logger.logError(e)
+                    bot.logger.error("Caught unexpected exceptions", e)
                     continue
                 } finally {
                     if (!buffer.canRead() || buffer.readRemaining == 0) {//size==0
-                        //bot.logger.logDebug("processReceive: Buffer cannot be read")
+                        //bot.logger.debug("processReceive: Buffer cannot be read")
                         buffer.release(IoBuffer.Pool)
                         continue
                     }// sometimes exceptions are thrown without this `if` clause
@@ -156,7 +154,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
                         try {
                             distributePacket(it.parseServerPacket(buffer.readRemaining))
                         } catch (e: Exception) {
-                            bot.logger.logError(e)
+                            bot.logger.error(e)
                         }
                     }
                 }
@@ -197,17 +195,17 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
             try {
                 packet.decode()
             } catch (e: Exception) {
-                e.log()
-                bot.logger.logDebug("Packet=$packet")
-                bot.logger.logDebug("Packet size=" + packet.input.readBytes().size)
-                bot.logger.logDebug("Packet data=" + packet.input.readBytes().toUHexString())
+                bot.logger.error("When distributePacket", e)
+                bot.logger.debug("Packet=$packet")
+                bot.logger.debug("Packet size=" + packet.input.readBytes().size)
+                bot.logger.debug("Packet data=" + packet.input.readBytes().toUHexString())
                 packet.close()
                 throw e
             }
 
             packet.use {
                 packet::class.simpleName?.takeIf { !it.endsWith("Encrypted") && !it.endsWith("Raw") }?.let {
-                    bot.logger.logCyan("Packet received: $packet")
+                    bot.logger.verbose("Packet received: $packet")
                 }
 
                 // Remove first to release the lock
@@ -284,7 +282,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
                     val shouldBeSent = buffer.readRemaining
                     check(channel.send(buffer) == shouldBeSent) { "Buffer is not entirely sent. Required sent length=$shouldBeSent, but after channel.send, buffer remains ${buffer.readBytes().toUHexString()}" }//JVM: withContext(IO)
                 } catch (e: SendPacketInternalException) {
-                    bot.logger.logError("Caught SendPacketInternalException: ${e.cause?.message}")
+                    bot.logger.error("Caught SendPacketInternalException: ${e.cause?.message}")
                     bot.reinitializeNetworkHandler(configuration, e)
                     return@withContext
                 } finally {
@@ -292,7 +290,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
                 }
             }
 
-            bot.logger.logGreen("Packet sent:     $packet")
+            bot.logger.info("Packet sent:     $packet")
 
             PacketSentEvent(bot, packet).broadcast()
 
@@ -340,7 +338,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
                     if (packet.serverIP != null) {//redirection
                         socket.close()
                         socket = BotSocketAdapter(packet.serverIP!!, socket.configuration)
-                        bot.logger.logPurple("Redirecting to ${packet.serverIP}")
+                        bot.logger.warning("Redirecting to ${packet.serverIP}")
                         loginResult.complete(socket.resendTouch())
                     } else {//password submission
                         this.loginIP = packet.loginIP
@@ -406,7 +404,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
                 is CaptchaTransmissionResponsePacket -> {
                     //packet is ServerCaptchaWrongPacket
                     if (this.captchaSectionId == 0) {
-                        bot.logger.logPurple("验证码错误, 请重新输入")
+                        bot.logger.warning("验证码错误, 请重新输入")
                         this.captchaSectionId = 1
                         this.captchaCache = null
                     }
@@ -478,7 +476,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
 
                 is SessionKeyResponsePacket -> {
                     sessionKey = packet.sessionKey
-                    bot.logger.logPurple("sessionKey = ${sessionKey.toUHexString()}")
+                    bot.logger.warning("sessionKey = ${sessionKey.toUHexString()}")
 
                     heartbeatJob = launch {
                         while (socket.isOpen) {
@@ -492,7 +490,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
                                             sessionKey
                                         ).sendAndExpect<HeartbeatPacket.Response>().join()
                                     } == null) {
-                                    bot.logger.logPurple("Heartbeat timed out")
+                                    bot.logger.warning("Heartbeat timed out")
                                     bot.reinitializeNetworkHandler(configuration, HeartbeatTimeoutException())
                                     return@launch
                                 }
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMProtocol.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMProtocol.kt
index fe06e2dca..812f355a5 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMProtocol.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMProtocol.kt
@@ -2,9 +2,6 @@
 
 package net.mamoe.mirai.network.protocol.tim
 
-import net.mamoe.mirai.utils.solveIpAddress
-
-
 object TIMProtocol {
     val SERVER_IP: List<String> = {
         //add("183.60.56.29")
@@ -17,7 +14,7 @@ object TIMProtocol {
                 "sz8.tencent.com",
                 "sz9.tencent.com",
                 "sz2.tencent.com"
-        ).forEach { list.add(solveIpAddress(it)) }
+        ).forEach { list.add(it) }
 
         list.toList()
     }()//不使用lazy, 在初始化时就加载.
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/ActionPacketHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/ActionPacketHandler.kt
index f00f5c8b5..795129cf9 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/ActionPacketHandler.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/ActionPacketHandler.kt
@@ -14,7 +14,6 @@ import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
 import net.mamoe.mirai.network.protocol.tim.packet.event.ServerEventPacket
 import net.mamoe.mirai.network.protocol.tim.packet.login.RequestSKeyPacket
 import net.mamoe.mirai.network.qqAccount
-import net.mamoe.mirai.utils.log
 
 /**
  * 动作: 获取好友列表, 点赞, 踢人等.
@@ -50,7 +49,7 @@ class ActionPacketHandler(session: BotSession) : PacketHandler(session) {
                             try {
                                 requestSKey()
                             } catch (e: Throwable) {
-                                e.log()
+                                bot.logger.error(e)
                             }
                         }
                     }
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UnknownServerPacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UnknownServerPacket.kt
index 4981b07a4..53f203996 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UnknownServerPacket.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UnknownServerPacket.kt
@@ -16,7 +16,7 @@ class UnknownServerPacket(
 ) : ServerPacket(input) {
     override fun decode() {
         val raw = this.input.readBytes()
-        MiraiLogger.logDebug("UnknownServerPacket data: " + raw.toUHexString())
+        MiraiLogger.debug("UnknownServerPacket data: " + raw.toUHexString())
     }
 
     class Encrypted(
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/ServerEventPackets.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/ServerEventPackets.kt
index 7a1ab091a..9b7eb3091 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/ServerEventPackets.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/ServerEventPackets.kt
@@ -50,7 +50,7 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
             )
             discardExact(2)
             val type = readUShort()
-            //DebugLogger.logPurple("unknown2Byte+byte = ${unknown2Byte.toUHexString()} ${type.toUHexString()}")
+            //DebugLogger.warning("unknown2Byte+byte = ${unknown2Byte.toUHexString()} ${type.toUHexString()}")
             return when (type.toUInt()) {
                 0x00C4u -> {
                     discardExact(13)
@@ -90,7 +90,7 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
                 //"02 10", "00 12" -> ServerUnknownEventPacket(input, eventIdentity)
 
                 else -> {//0x00 79u, 可能是正在输入的包
-                    MiraiLogger.logDebug("UnknownEvent type = ${type.toByteArray().toUHexString()}")
+                    MiraiLogger.debug("UnknownEvent type = ${type.toByteArray().toUHexString()}")
                     UnknownServerEventPacket(input, eventIdentity)
                 }
             }.applyId(id).applySequence(sequenceId)
@@ -126,7 +126,7 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
 class IgnoredServerEventPacket(val eventId: ByteArray, private val showData: Boolean = false, input: ByteReadPacket, eventIdentity: EventPacketIdentity) : ServerEventPacket(input, eventIdentity) {
     override fun decode() {
         if (showData) {
-            MiraiLogger.logDebug("IgnoredServerEventPacket data: " + this.input.readBytes().toUHexString())
+            MiraiLogger.debug("IgnoredServerEventPacket data: " + this.input.readBytes().toUHexString())
         } else {
             this.input.discard()
         }
@@ -138,6 +138,6 @@ class IgnoredServerEventPacket(val eventId: ByteArray, private val showData: Boo
  */
 class UnknownServerEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) : ServerEventPacket(input, eventIdentity) {
     override fun decode() {
-        MiraiLogger.logDebug("UnknownServerEventPacket data: " + this.input.readBytes().toUHexString())
+        MiraiLogger.debug("UnknownServerEventPacket data: " + this.input.readBytes().toUHexString())
     }
 }
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/SKey.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/SKey.kt
index c07dfea1b..0cbab7b45 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/SKey.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/SKey.kt
@@ -40,8 +40,8 @@ class RequestSKeyPacket(
             discardExact(4)
             //debugDiscardExact(2)
             sKey = this.readString(10)//16??
-            DebugLogger.logPurple("SKey=$sKey")
-            DebugLogger.logPurple("SKey 包后面${this.readRemainingBytes().toUHexString()}")
+            DebugLogger.warning("SKey=$sKey")
+            DebugLogger.warning("SKey 包后面${this.readRemainingBytes().toUHexString()}")
         }
     }
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt
index f6b0dfdb6..b0db9990b 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt
@@ -3,6 +3,23 @@ package net.mamoe.mirai.utils
 import net.mamoe.mirai.Bot
 import kotlin.jvm.JvmOverloads
 
+
+/**
+ * 用于创建默认的日志记录器. 在一些需要使用日志的 Mirai 的组件, 如 [Bot], 都会通过这个函数构造日志记录器
+ * 可直接修改这个变量的值来重定向日志输出.
+ */
+var DefaultLogger: (identity: String?) -> MiraiLogger = { PlatformLogger() }
+
+/**
+ * 当前平台的默认的日志记录器.
+ * 在 _JVM 控制台_ 端的实现为 [println]
+ * 在 _Android_ 端的实现为 [android.util.Log]
+ *
+ * 不应该直接构造这个类的实例. 请使用 [DefaultLogger]
+ */
+expect open class PlatformLogger @JvmOverloads internal constructor(identity: String? = null) : MiraiLoggerPlatformBase
+
+
 /**
  * 日志记录器. 所有的输出均依赖于它.
  * 不同的对象可能拥有只属于自己的 logger. 通过 [identity] 来区分.
@@ -28,38 +45,67 @@ interface MiraiLogger {
      * val bot = Bot( ... )
      * bot.follower = MyOwnLogger()
      *
-     * bot.logInfo("Hi")
+     * bot.info("Hi")
      * ```
      * 在这个例子中的 `MyOwnLogger` 将可以记录到 "Hi".
      */
     var follower: MiraiLogger?
 
-    fun logInfo(any: Any?) = log(any)
+    /**
+     * 记录一个 `verbose` 级别的日志.
+     */
+    fun verbose(any: Any?)
 
-    fun log(e: Throwable)
+    fun verbose(e: Throwable?) = verbose(null, e)
+    fun verbose(message: String?, e: Throwable?)
 
-    fun log(any: Any?)
+    /**
+     * 记录一个 `debug` 级别的日志.
+     */
+    fun debug(any: Any?)
 
-    fun logError(any: Any?)
+    fun debug(e: Throwable?) = debug(null, e)
+    fun debug(message: String?, e: Throwable?)
 
-    fun logError(e: Throwable) = log(e)
 
-    fun logDebug(any: Any?)
+    /**
+     * 记录一个 `info` 级别的日志.
+     */
+    fun info(any: Any?)
 
-    fun logCyan(any: Any?)
+    fun info(e: Throwable?) = info(null, e)
+    fun info(message: String?, e: Throwable?)
 
-    fun logPurple(any: Any?)
 
-    fun logGreen(any: Any?)
+    /**
+     * 记录一个 `warning` 级别的日志.
+     */
+    fun warning(any: Any?)
+
+    fun warning(e: Throwable?) = warning(null, e)
+    fun warning(message: String?, e: Throwable?)
+
+
+    /**
+     * 记录一个 `error` 级别的日志.
+     */
+    fun error(e: Any?)
+
+    fun error(e: Throwable?) = error(null, e)
+    fun error(message: String?, e: Throwable?)
 
-    fun logBlue(any: Any?)
 
     /**
      * 添加一个 [follower], 返回 [follower]
      * 它只会把 `this` 的属性 [MiraiLogger.follower] 修改为这个函数的参数 [follower], 然后返回这个参数.
      * 若 [MiraiLogger.follower] 已经有值, 则会替换掉这个值.
      *
+     *   +------+      +----------+      +----------+      +----------+
+     *   | base | <--  | follower | <--  | follower | <--  | follower |
+     *   +------+      +----------+      +----------+      +----------+
+     *
      * @see follower
+     * @return [follower]
      */
     operator fun plus(follower: MiraiLogger): MiraiLogger
 
@@ -72,6 +118,29 @@ interface MiraiLogger {
     operator fun plusAssign(follower: MiraiLogger)
 }
 
+/**
+ * 不做任何事情的 logger, keep silent.
+ */
+@Suppress("unused")
+object Silent : PlatformLogger() {
+    override val identity: String? = null
+
+    override fun error0(any: Any?) {
+    }
+
+    override fun debug0(any: Any?) {
+    }
+
+    override fun warning0(any: Any?) {
+    }
+
+    override fun verbose0(any: Any?) {
+    }
+
+    override fun info0(any: Any?) {
+    }
+}
+
 /**
  * 平台基类.
  * 实现了 [follower] 的调用传递.
@@ -80,58 +149,66 @@ interface MiraiLogger {
 abstract class MiraiLoggerPlatformBase : MiraiLogger {
     final override var follower: MiraiLogger? = null
 
-    final override fun logInfo(any: Any?) = log(any)
-
-    final override fun log(e: Throwable) {
-        log0(e)
-        follower?.log(e)
+    final override fun verbose(any: Any?) {
+        follower?.verbose(any)
+        verbose0(any)
     }
 
-    final override fun log(any: Any?) {
-        log0(any)
-        follower?.log(any)
+    final override fun verbose(message: String?, e: Throwable?) {
+        follower?.verbose(message, e)
+        verbose0(message, e)
     }
 
-    final override fun logError(any: Any?) {
-        logError0(any)
-        follower?.logError(any)
+    final override fun debug(any: Any?) {
+        follower?.debug(any)
+        debug0(any)
     }
 
-    override fun logError(e: Throwable) = log(e)
-
-    final override fun logDebug(any: Any?) {
-        logDebug0(any)
-        follower?.logDebug(any)
+    final override fun debug(message: String?, e: Throwable?) {
+        follower?.debug(message, e)
+        debug0(message, e)
     }
 
-    final override fun logCyan(any: Any?) {
-        logCyan0(any)
-        follower?.logCyan(any)
+    final override fun info(any: Any?) {
+        follower?.info(any)
+        info0(any)
     }
 
-    final override fun logPurple(any: Any?) {
-        logPurple0(any)
-        follower?.logPurple(any)
+    final override fun info(message: String?, e: Throwable?) {
+        follower?.info(message, e)
+        info0(message, e)
     }
 
-    final override fun logGreen(any: Any?) {
-        logGreen0(any)
-        follower?.logGreen(any)
+    final override fun warning(any: Any?) {
+        follower?.warning(any)
+        warning0(any)
     }
 
-    final override fun logBlue(any: Any?) {
-        logBlue0(any)
-        follower?.logBlue(any)
+    final override fun warning(message: String?, e: Throwable?) {
+        follower?.warning(message, e)
+        warning0(message, e)
     }
 
-    protected abstract fun log0(e: Throwable)
-    protected abstract fun log0(any: Any?)
-    protected abstract fun logError0(any: Any?)
-    protected abstract fun logDebug0(any: Any?)
-    protected abstract fun logCyan0(any: Any?)
-    protected abstract fun logPurple0(any: Any?)
-    protected abstract fun logGreen0(any: Any?)
-    protected abstract fun logBlue0(any: Any?)
+    final override fun error(e: Any?) {
+        follower?.error(e)
+        error0(e)
+    }
+
+    final override fun error(message: String?, e: Throwable?) {
+        follower?.error(message, e)
+        error0(message, e)
+    }
+
+    protected abstract fun verbose0(any: Any?)
+    protected abstract fun verbose0(message: String?, e: Throwable?)
+    protected abstract fun debug0(any: Any?)
+    protected abstract fun debug0(message: String?, e: Throwable?)
+    protected abstract fun info0(any: Any?)
+    protected abstract fun info0(message: String?, e: Throwable?)
+    protected abstract fun warning0(any: Any?)
+    protected abstract fun warning0(message: String?, e: Throwable?)
+    protected abstract fun error0(any: Any?)
+    protected abstract fun error0(message: String?, e: Throwable?)
 
     override fun plus(follower: MiraiLogger): MiraiLogger {
         this.follower = follower
@@ -142,53 +219,3 @@ abstract class MiraiLoggerPlatformBase : MiraiLogger {
         if (this.follower == null) this.follower = follower
         else this.follower!! += follower
 }
-
-/**
- * 用于创建默认的日志记录器. 在一些需要使用日志的 Mirai 的组件, 如 [Bot], 都会通过这个函数构造日志记录器
- */
-var DefaultLogger: (identity: String?) -> MiraiLogger = { PlatformLogger() }
-
-/**
- * 当前平台的默认的日志记录器.
- * 在 _JVM 控制台_ 端的实现为 [println]
- *
- * 不应该直接构造这个类的实例. 需使用 [DefaultLogger]
- */
-expect open class PlatformLogger @JvmOverloads internal constructor(identity: String? = null) : MiraiLoggerPlatformBase
-
-/**
- * 不作任何事情的 logger
- */
-@Suppress("unused")
-object NoLogger : PlatformLogger() {
-    override val identity: String? = null
-
-    override fun log0(e: Throwable) {
-    }
-
-    override fun log0(any: Any?) {
-    }
-
-    override fun logError0(any: Any?) {
-    }
-
-    override fun logDebug0(any: Any?) {
-    }
-
-    override fun logCyan0(any: Any?) {
-    }
-
-    override fun logPurple0(any: Any?) {
-    }
-
-    override fun logGreen0(any: Any?) {
-    }
-
-    override fun logBlue0(any: Any?) {
-    }
-}
-
-/**
- * 在顶层日志记录这个异常
- */
-fun Throwable.log() = MiraiLogger.log(this)
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformLogger.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformLogger.kt
new file mode 100644
index 000000000..371c10fdf
--- /dev/null
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformLogger.kt
@@ -0,0 +1,2 @@
+package net.mamoe.mirai.utils 
+
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt
index 5af98c39a..0f4510790 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt
@@ -30,11 +30,6 @@ expect fun crc32(key: ByteArray): Int
  */
 expect fun md5(byteArray: ByteArray): ByteArray
 
-/**
- * Hostname 解析 IP 地址
- */
-expect fun solveIpAddress(hostname: String): String // TODO: 2019/10/28 是否有必要?
-
 /**
  * Localhost 解析
  */
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
index 8d6001535..4a7af8d2f 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
@@ -7,29 +7,29 @@ import net.mamoe.mirai.utils.MiraiLogger
 
 internal object DebugLogger : MiraiLogger by DefaultLogger("Packet Debug")
 
-internal fun debugPrintln(any: Any?) = DebugLogger.logPurple(any)
+internal fun debugPrintln(any: Any?) = DebugLogger.warning(any)
 
 internal fun ByteArray.debugPrint(name: String): ByteArray {
-    DebugLogger.logPurple(name + "=" + this.toUHexString())
+    DebugLogger.warning(name + "=" + this.toUHexString())
     return this
 }
 
 @Deprecated("Low efficiency, only for debug purpose", ReplaceWith("this"))
 internal fun IoBuffer.debugPrint(name: String): IoBuffer {
     val readBytes = this.readBytes()
-    DebugLogger.logPurple(name + "=" + readBytes.toUHexString())
+    DebugLogger.warning(name + "=" + readBytes.toUHexString())
     return readBytes.toIoBuffer()
 }
 
 @Deprecated("Low efficiency, only for debug purpose", ReplaceWith("discardExact(n)"))
 internal fun Input.debugDiscardExact(n: Number, name: String = "") {
-    DebugLogger.logPurple("Discarded($n) $name=" + this.readBytes(n.toInt()).toUHexString())
+    DebugLogger.warning("Discarded($n) $name=" + this.readBytes(n.toInt()).toUHexString())
 }
 
 @Deprecated("Low efficiency, only for debug purpose", ReplaceWith("this"))
 internal fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket {
     val bytes = this.readBytes()
-    DebugLogger.logPurple("ByteReadPacket $name=" + bytes.toUHexString())
+    DebugLogger.warning("ByteReadPacket $name=" + bytes.toUHexString())
     return bytes.toReadPacket()
 }
 
@@ -74,10 +74,4 @@ internal fun ByteArray.printColorizedHex(name: String = "", ignoreUntilFirstCons
         println(toUHexString().printColorize(ignoreUntilFirstConst))
     }
     println()
-}
-
-/**
- * TODO 这两个方法不应该 MPP
- */
-expect fun printCompareHex(hex1s: String, hex2s: String): String
-expect fun String.printColorize(ignoreUntilFirstConst: Boolean = false): String
+}
\ No newline at end of file
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/HexComparator.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/HexComparator.kt
similarity index 63%
rename from mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/HexComparator.kt
rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/HexComparator.kt
index 50a6e8270..5c8efaaf1 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/HexComparator.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/HexComparator.kt
@@ -1,11 +1,79 @@
-@file:Suppress("ObjectPropertyName", "MayBeConstant", "NonAsciiCharacters", "SpellCheckingInspection")
+@file:Suppress("ObjectPropertyName", "MayBeConstant", "NonAsciiCharacters", "SpellCheckingInspection", "unused")
 
 package net.mamoe.mirai.utils.io
 
+import kotlinx.io.core.toByteArray
 import net.mamoe.mirai.network.protocol.tim.TIMProtocol
-import java.lang.reflect.Field
-import java.util.*
 import kotlin.math.max
+import kotlin.reflect.KProperty
+
+/**
+ * 匹配已知 hex 常量并格式化后打印到控制台.
+ *
+ * 低效率, 仅调试使用.
+ */
+internal fun String.printColorize(ignoreUntilFirstConst: Boolean): String = with(HexComparator) { colorize(ignoreUntilFirstConst) }
+
+/**
+ * 比较两个 hex 并格式化后打印到控制台.
+ *
+ * 低效率, 仅调试使用.
+ */
+internal fun printCompareHex(hex1s: String, hex2s: String): String = with(HexComparator) { compare(hex1s, hex2s) }
+
+data class NamedHexElement(
+    val name: String,
+    val value: String
+)
+
+/**
+ * 初始化用于匹配的 Hex 列表
+ */
+private fun LinkedHashSet<NamedHexElement>.initConstFileds() {
+    listOf(
+        TestConsts,
+        TIMProtocol,
+        PacketIds
+    ).forEach { obj ->
+        obj::class.members.filterIsInstance<KProperty<*>>().forEach { property ->
+            add(NamedHexElement(property.name, property.getter.call().toString()))
+        }
+    }
+}
+
+private object TestConsts {
+    val NIU_BI = "牛逼".toByteArray().toUHexString()
+    val _1994701021 = 1994701021.toUHexString(" ")
+    val _1040400290 = 1040400290.toUHexString(" ")
+    val _580266363 = 580266363.toUHexString(" ")
+
+    val _1040400290_ = "3E 03 3F A2"
+    val _1994701021_ = "76 E4 B8 DD"
+    val _jiahua_ = "B1 89 BE 09"
+    val _Him188moe_ = "Him188moe".toByteArray().toUHexString()
+    val 发图片2 = "发图片2".toByteArray().toUHexString()
+    val 发图片群 = "发图片群".toByteArray().toUHexString()
+    val 发图片 = "发图片".toByteArray().toUHexString()
+    val 群 = "群".toByteArray().toUHexString()
+    val 你好 = "你好".toByteArray().toUHexString()
+
+    val MESSAGE_TAIL_10404 =
+        "0E  00  07  01  00  04  00  00  00  09 19  00  18  01  00  15  AA  02  12  9A  01  0F  80  01  01  C8  01  00  F0  01  00  F8  01  00  90  02  00"
+            .replace("  ", " ")
+
+    val FONT_10404 = "E5 BE AE E8 BD AF E9 9B 85 E9 BB 91"
+
+    val varint580266363 = "FB D2 D8 94 02"
+    val varint1040400290 = "A2 FF 8C F0 03"
+    var varint1994701021 = "DD F1 92 B7 07"
+}
+
+@Suppress("SpellCheckingInspection")
+private object PacketIds {
+    val heartbeat = "00 58"
+    val friendmsgsend = "00 CD"
+    val friendmsgevent = "00 CE"
+}
 
 /**
  * Hex 比较器, 并着色已知常量
@@ -16,7 +84,7 @@ import kotlin.math.max
  * @author NaturalHG
  * @author Him188moe
  */
-internal object HexComparator {
+private object HexComparator {
 
     private val RED = "\u001b[31m"
     private val GREEN = "\u001b[33m"
@@ -24,49 +92,8 @@ internal object HexComparator {
     private val BLUE = "\u001b[34m"
 
     @Suppress("unused")
-    class ConstMatcher constructor(hex: String) {
-        private val matches = LinkedList<Match>()
-
-        object TestConsts {
-            val NIU_BI = "牛逼".toByteArray().toUHexString()
-            val _1994701021 = 1994701021.toUHexString(" ")
-            val _1040400290 = 1040400290.toUHexString(" ")
-            val _580266363 = 580266363.toUHexString(" ")
-
-            val _1040400290_ = "3E 03 3F A2"
-            val _1994701021_ = "76 E4 B8 DD"
-            val _jiahua_ = "B1 89 BE 09"
-            val _Him188moe_ = "Him188moe".toByteArray().toUHexString()
-            val 发图片2 = "发图片2".toByteArray().toUHexString()
-            val 发图片群 = "发图片群".toByteArray().toUHexString()
-            val 发图片 = "发图片".toByteArray().toUHexString()
-            val 群 = "群".toByteArray().toUHexString()
-            val 你好 = "你好".toByteArray().toUHexString()
-
-            val MESSAGE_TAIL_10404 = "0E  00  07  01  00  04  00  00  00  09 19  00  18  01  00  15  AA  02  12  9A  01  0F  80  01  01  C8  01  00  F0  01  00  F8  01  00  90  02  00"
-                    .replace("  ", " ")
-
-            val FONT_10404 = "E5 BE AE E8 BD AF E9 9B 85 E9 BB 91"
-
-            val varint580266363 = "FB D2 D8 94 02"
-            val varint1040400290 = "A2 FF 8C F0 03"
-            var varint1994701021 = "DD F1 92 B7 07"
-        }
-
-        @Suppress("SpellCheckingInspection")
-        object PacketIds {
-            val heartbeat = "00 58"
-            val friendmsgsend = "00 CD"
-            val friendmsgevent = "00 CE"
-        }
-
-        init {
-            CONST_FIELDS.forEach { field ->
-                for (match in match(hex, field)) {
-                    matches.add(Match(match, field.name))
-                }
-            }
-        }
+    private class ConstMatcher constructor(hex: String) {
+        private val matches = linkedSetOf<Match>()
 
         fun getMatchedConstName(hexNumber: Int): String? {
             for (match in this.matches) {
@@ -79,41 +106,36 @@ internal object HexComparator {
 
         private class Match internal constructor(val range: IntRange, val constName: String)
 
-        companion object {
-            private val CONST_FIELDS: List<Field> = listOf(
-                    TestConsts::class.java,
-                    TIMProtocol::class.java,
-                    PacketIds::class.java
-            ).map { it.declaredFields }.flatMap { fields ->
-                fields.map { field ->
-                    field.trySetAccessible()
-                    field
+        init {
+            TIMProtocol::class.members.filterIsInstance<KProperty<*>>().forEach {
+                for (match in match(hex, it.getter.call().toString())) {
+                    matches.add(Match(match, it.getter.call().toString()))
                 }
             }
         }
 
-        private fun match(hex: String, field: Field): List<IntRange> {
+        companion object {
+            val CONST_FIELDS: Set<NamedHexElement> = linkedSetOf<NamedHexElement>().apply { initConstFileds() }
+        }
+
+        private fun match(hex: String, field: String): Set<IntRange> {
             val constValue: String
             try {
-                constValue = (field.get(null) as String).trim { it <= ' ' }
+                constValue = field.trim { it <= ' ' }
                 if (constValue.length / 3 <= 3) {//Minimum numbers of const hex bytes
-                    return LinkedList()
+                    return linkedSetOf()
                 }
-            } catch (e: IllegalAccessException) {
-                throw RuntimeException(e)
             } catch (ignored: ClassCastException) {
-                return LinkedList()
+                return linkedSetOf()
             }
 
-            return object : LinkedList<IntRange>() {
-                init {
-                    var index = -1
-                    index = hex.indexOf(constValue, index + 1)
-                    while (index != -1) {
-                        add(IntRange(index / 3, (index + constValue.length) / 3))
+            return mutableSetOf<IntRange>().apply {
+                var index = -1
+                index = hex.indexOf(constValue, index + 1)
+                while (index != -1) {
+                    add(IntRange(index / 3, (index + constValue.length) / 3))
 
-                        index = hex.indexOf(constValue, index + 1)
-                    }
+                    index = hex.indexOf(constValue, index + 1)
                 }
             }
         }
@@ -222,12 +244,12 @@ internal object HexComparator {
         }
 
         return builder.append(" ").append(dif).append(" 个不同").append("\n")
-                .append(numberLine).append("\n")
-                .append(hex1ConstName).append("\n")
-                .append(hex1b).append("\n")
-                .append(hex2b).append("\n")
-                .append(hex2ConstName).append("\n")
-                .toString()
+            .append(numberLine).append("\n")
+            .append(hex1ConstName).append("\n")
+            .append(hex1b).append("\n")
+            .append(hex2b).append("\n")
+            .append(hex2ConstName).append("\n")
+            .toString()
 
 
     }
@@ -267,15 +289,15 @@ internal object HexComparator {
         }
 
         return builder.append("\n")
-                .append(numberLine).append("\n")
-                .append(if (firstConst == null) hex1ConstName else {
-                    with(hex1ConstName) {
-                        val index = indexOf(firstConst)
-                        if (index == -1) toString() else " " + substring(index, length)
-                    }
-                }).append("\n")
-                .append(hex1b).append("\n")
-                .toString()
+            .append(numberLine).append("\n")
+            .append(if (firstConst == null) hex1ConstName else {
+                with(hex1ConstName) {
+                    val index = indexOf(firstConst)
+                    if (index == -1) toString() else " " + substring(index, length)
+                }
+            }).append("\n")
+            .append(hex1b).append("\n")
+            .toString()
     }
 
 
@@ -288,7 +310,4 @@ internal object HexComparator {
         } else number.toString()
     }
 
-}
-
-actual fun String.printColorize(ignoreUntilFirstConst: Boolean): String = with(HexComparator) { colorize(ignoreUntilFirstConst) }
-actual fun printCompareHex(hex1s: String, hex2s: String): String = with(HexComparator) { compare(hex1s, hex2s) }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt
index e1e6974a2..e64564d18 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt
@@ -47,7 +47,7 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket {
                 135 -> {//包数据错误. 目前怀疑是 tlv0006
                     this.readRemainingBytes().cutTail(1).decryptBy(TIMProtocol.shareKey).read {
                         discardExact(51)
-                        MiraiLogger.logError("Internal logError: " + readLVString())//抱歉,请重新输入密码。
+                        MiraiLogger.error("Internal error: " + readLVString())//抱歉,请重新输入密码。
                     }
 
                     LoginResult.INTERNAL_ERROR
@@ -63,8 +63,8 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket {
                 else -> LoginResult.UNKNOWN
                 /*
                 //unknown
-                63 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown logError)")
-                351 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown logError)")
+                63 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown error)")
+                351 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown error)")
 
                 else -> throw IllegalArgumentException(bytes.size.toString())*/
             }, this).applySequence(sequenceId)
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/CaptchaResolverJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/CaptchaResolverJvm.kt
index 6f022f59b..26ea9c398 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/CaptchaResolverJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/CaptchaResolverJvm.kt
@@ -22,17 +22,17 @@ import kotlin.math.min
 internal actual suspend fun solveCaptcha(captchaBuffer: IoBuffer): String? = captchaLock.withLock {
     val captcha = captchaBuffer.readBytes()
     withContext(Dispatchers.IO) {
-        MiraiLogger.logCyan(ImageIO.read(captcha.inputStream()).createCharImg())
+        MiraiLogger.verbose(ImageIO.read(captcha.inputStream()).createCharImg())
     }
-    MiraiLogger.logCyan("需要验证码登录, 验证码为 4 字母")
+    MiraiLogger.verbose("需要验证码登录, 验证码为 4 字母")
     try {
         File(System.getProperty("user.dir") + "/temp/Captcha.png")
                 .let { withContext(Dispatchers.IO) { it.createNewFile(); it.writeBytes(captcha) } }
-        MiraiLogger.logCyan("若看不清字符图片, 请查看 Mirai 目录下 /temp/Captcha.png")
+        MiraiLogger.verbose("若看不清字符图片, 请查看 Mirai 目录下 /temp/Captcha.png")
     } catch (e: Exception) {
-        MiraiLogger.logCyan("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片")
+        MiraiLogger.verbose("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片")
     }
-    MiraiLogger.logCyan("若要更换验证码, 请直接回车")
+    MiraiLogger.verbose("若要更换验证码, 请直接回车")
     readLine()?.takeUnless { it.isEmpty() || it.length != 4 }
 }
 
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/MiraiLoggerJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/MiraiLoggerJvm.kt
index 11883e913..50ff00953 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/MiraiLoggerJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/MiraiLoggerJvm.kt
@@ -1,5 +1,7 @@
 package net.mamoe.mirai.utils
 
+import java.io.ByteArrayOutputStream
+import java.io.PrintStream
 import java.text.SimpleDateFormat
 import java.util.*
 
@@ -11,21 +13,41 @@ actual typealias PlatformLogger = Console
 open class Console @JvmOverloads internal constructor(
     override val identity: String? = null
 ) : MiraiLoggerPlatformBase() {
-    override fun logGreen0(any: Any?) = println(any.toString(), LoggerTextFormat.GREEN)
-    override fun logPurple0(any: Any?) = println(any.toString(), LoggerTextFormat.LIGHT_PURPLE)
-    override fun logBlue0(any: Any?) = println(any.toString(), LoggerTextFormat.BLUE)
-    override fun logCyan0(any: Any?) = println(any.toString(), LoggerTextFormat.LIGHT_CYAN)
-    override fun logError0(any: Any?) = println(any.toString(), LoggerTextFormat.RED)
-    override fun log0(e: Throwable) = e.printStackTrace()
-    override fun log0(any: Any?) = println(any.toString(), LoggerTextFormat.WHITE)
-    override fun logDebug0(any: Any?) {
-        if (DEBUGGING) {
-            println(any.toString(), LoggerTextFormat.YELLOW)
-        }
+    override fun verbose0(any: Any?) = println(any.toString(), LoggerTextFormat.LIGHT_GRAY)
+    override fun verbose0(message: String?, e: Throwable?) {
+        verbose(message.toString())
+        e?.printStackTrace()
+    }
+
+    override fun info0(any: Any?) = println(any.toString(), LoggerTextFormat.GREEN)
+    override fun info0(message: String?, e: Throwable?) {
+        info(message.toString())
+        e?.printStackTrace()
+    }
+
+    override fun warning0(any: Any?) = println(any.toString(), LoggerTextFormat.YELLOW)
+    override fun warning0(message: String?, e: Throwable?) {
+        warning(message.toString())
+        e?.printStackTrace()
+    }
+
+    override fun error0(any: Any?) = println(any.toString(), LoggerTextFormat.RED)
+    override fun error0(message: String?, e: Throwable?) {
+        error(message.toString())
+        e?.printStackTrace()
+    }
+
+    override fun debug0(any: Any?) {
+        println(any.toString(), LoggerTextFormat.CYAN)
+    }
+
+    override fun debug0(message: String?, e: Throwable?) {
+        debug(message.toString())
+        e?.printStackTrace()
     }
 
     private fun println(value: String?, color: LoggerTextFormat) {
-        val time = SimpleDateFormat("HH:mm:ss").format(Date())
+        val time = SimpleDateFormat("HH:mm:ss", Locale.SIMPLIFIED_CHINESE).format(Date())
 
         if (identity == null) {
             println("$color$time : $value")
@@ -35,8 +57,6 @@ open class Console @JvmOverloads internal constructor(
     }
 }
 
-private val DEBUGGING: Boolean by lazy {
-    //todo 添加环境变量检测
-    //avoid inspections
-    true
-}
\ No newline at end of file
+@Suppress("unused")
+val Throwable.stacktraceString: String
+    get() = ByteArrayOutputStream().also { printStackTrace(PrintStream(it)) }.toString()
\ No newline at end of file
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
index c7a2032ac..8bae7bf2d 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
@@ -19,10 +19,6 @@ import java.util.zip.CRC32
 
 actual val deviceName: String = InetAddress.getLocalHost().hostName
 
-/*
- * TODO: we may use libraries that provide these functions
- */
-
 actual fun crc32(key: ByteArray): Int = CRC32().let { it.update(key); it.value.toInt() }
 
 actual fun md5(byteArray: ByteArray): ByteArray = MessageDigest.getInstance("MD5").digest(byteArray)
@@ -55,7 +51,7 @@ fun DataInput.md5(): ByteArray {
     return digest.digest()
 }
 
-actual fun solveIpAddress(hostname: String): String = InetAddress.getByName(hostname).hostAddress
+//actual fun solveIpAddress(hostname: String): String = InetAddress.getByName(hostname).hostAddress
 
 actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress
 
diff --git a/mirai-debug/src/main/java/HexComparator.kt b/mirai-core/src/jvmTest/kotlin/HexComparator.kt
similarity index 100%
rename from mirai-debug/src/main/java/HexComparator.kt
rename to mirai-core/src/jvmTest/kotlin/HexComparator.kt
diff --git a/mirai-core/src/jvmTest/kotlin/Run.kt b/mirai-core/src/jvmTest/kotlin/Run.kt
new file mode 100644
index 000000000..e69de29bb