Преглед на файлове

[console] Pre check plugin duplication & console requirement (#2703)

* feat: check in load plugin

* feat: console requirement

* feat: dependsOn mirai

* feat: dependsOn docs

* fix: docs

* use: net.mamoe.mirai-console

* revert

* [console] Wrap mirai-console as a plugin

* [console] Fix plugin dependencies checking skipped already loaded plugins

* [console/tests] Check plugin can depends on mirai-console

---------

Co-authored-by: Karlatemp <kar@kasukusakura.com>
cssxsh преди 2 години
родител
ревизия
7daf2d56bc

+ 10 - 0
mirai-console/backend/integration-test/testers/plugin-can-depends-on-mirai-console/resources/META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin

@@ -0,0 +1,10 @@
+#
+# Copyright 2019-2023 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
+#
+
+net.mamoe.console.itest.plugincandependsonmiraiconsole.PluginCanDependsOnMiraiConsole

+ 21 - 0
mirai-console/backend/integration-test/testers/plugin-can-depends-on-mirai-console/src/PluginCanDependsOnMiraiConsole.kt

@@ -0,0 +1,21 @@
+/*
+ * Copyright 2019-2023 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.console.itest.plugincandependsonmiraiconsole
+
+import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
+import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
+
+internal object PluginCanDependsOnMiraiConsole : KotlinPlugin(
+    JvmPluginDescription("net.mamoe.tester.plugin-can-depends-mirai-console", "1.0.0") {
+        dependsOn("net.mamoe.mirai-console")
+    }
+) {
+
+}

+ 8 - 2
mirai-console/backend/mirai-console/src/command/BuiltInCommands.kt

@@ -39,6 +39,7 @@ import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
 import net.mamoe.mirai.console.internal.permission.BuiltInPermissionService
 import net.mamoe.mirai.console.internal.permission.getPermittedPermissionsAndSource
 import net.mamoe.mirai.console.internal.plugin.JvmPluginInternal
+import net.mamoe.mirai.console.internal.plugin.MiraiConsoleAsPlugin
 import net.mamoe.mirai.console.internal.pluginManagerImpl
 import net.mamoe.mirai.console.internal.util.runIgnoreException
 import net.mamoe.mirai.console.permission.Permission
@@ -633,10 +634,15 @@ public object BuiltInCommands {
                 reset().append("\n\n")
 
                 append("Plugins: ")
-                if (MiraiConsole.pluginManagerImpl.resolvedPlugins.isEmpty()) {
+
+                val resolvedPlugins = MiraiConsole.pluginManagerImpl.resolvedPlugins.asSequence()
+                    .filter { it !is MiraiConsoleAsPlugin } // skip mirai-console in status
+                    .toList()
+
+                if (resolvedPlugins.isEmpty()) {
                     gray().append("<none>")
                 } else {
-                    MiraiConsole.pluginManagerImpl.resolvedPlugins.joinTo(this) { plugin ->
+                    resolvedPlugins.joinTo(this) { plugin ->
                         if (plugin.isEnabled) {
                             green().append(plugin.name).reset().append(" v").gold()
                         } else {

+ 3 - 3
mirai-console/backend/mirai-console/src/internal/plugin/BuiltInJvmPluginLoaderImpl.kt

@@ -287,7 +287,7 @@ internal class BuiltInJvmPluginLoaderImpl(
         return filePlugins.toSet().map { it.second }
     }
 
-    private val loadedPlugins = ConcurrentHashMap<JvmPlugin, Unit>()
+    private val loadedPlugins = ConcurrentHashMap<String, JvmPlugin>()
 
     private fun Path.moveNameFolder(plugin: JvmPlugin) {
         val nameFolder = this.resolve(plugin.description.name).toFile()
@@ -324,8 +324,8 @@ internal class BuiltInJvmPluginLoaderImpl(
     override fun load(plugin: JvmPlugin) {
         ensureActive()
 
-        if (loadedPlugins.put(plugin, Unit) != null) {
-            error("Plugin '${plugin.name}' is already loaded and cannot be reloaded.")
+        if (loadedPlugins.put(plugin.id, plugin) != null) {
+            error("Plugin '${plugin.id}' is already loaded and cannot be reloaded.")
         }
         logger.verbose { "Loading plugin ${plugin.description.smartToString()}" }
         runCatching {

+ 72 - 0
mirai-console/backend/mirai-console/src/internal/plugin/MiraiConsoleAsPlugin.kt

@@ -0,0 +1,72 @@
+/*
+ * Copyright 2019-2023 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.console.internal.plugin
+
+import net.mamoe.mirai.console.command.ConsoleCommandOwner
+import net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants
+import net.mamoe.mirai.console.permission.Permission
+import net.mamoe.mirai.console.permission.PermissionId
+import net.mamoe.mirai.console.plugin.Plugin
+import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.description
+import net.mamoe.mirai.console.plugin.description.PluginDependency
+import net.mamoe.mirai.console.plugin.description.PluginDescription
+import net.mamoe.mirai.console.plugin.loader.PluginLoader
+import net.mamoe.mirai.console.util.SemVersion
+
+internal object MiraiConsoleAsPlugin : Plugin {
+    // MiraiConsole always enabled
+    override val isEnabled: Boolean get() = true
+
+    override val loader: PluginLoader<*, *> get() = TheLoader
+
+    override val parentPermission: Permission
+        get() = ConsoleCommandOwner.parentPermission
+
+    override fun permissionId(name: String): PermissionId {
+        return ConsoleCommandOwner.permissionId(name)
+    }
+
+    internal object TheLoader : PluginLoader<Plugin, PluginDescription> {
+        override fun listPlugins(): List<Plugin> = listOf(MiraiConsoleAsPlugin)
+
+        override fun disable(plugin: Plugin) {
+            // noop
+        }
+
+        override fun enable(plugin: Plugin) {
+            // noop
+        }
+
+        override fun load(plugin: Plugin) {
+            // noop
+        }
+
+        override fun getPluginDescription(plugin: Plugin): PluginDescription {
+            if (plugin !== MiraiConsoleAsPlugin) {
+                error("loader not match with " + plugin.description.id)
+            }
+            return TheDescription
+        }
+    }
+
+    internal object TheDescription : PluginDescription {
+        override val id: String get() = "net.mamoe.mirai-console"
+        override val name: String get() = "Console"
+        override val author: String get() = "Mamoe Technologies"
+        override val version: SemVersion get() = MiraiConsoleBuildConstants.version
+        override val info: String get() = ""
+        override val dependencies: Set<PluginDependency> get() = setOf()
+
+
+        override fun toString(): String {
+            return "PluginDescription[ mirai-console ]"
+        }
+    }
+}

+ 7 - 0
mirai-console/backend/mirai-console/src/internal/plugin/PluginManagerImpl.kt

@@ -20,6 +20,7 @@ import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
 import net.mamoe.mirai.console.plugin.NotYetLoadedPlugin
 import net.mamoe.mirai.console.plugin.Plugin
 import net.mamoe.mirai.console.plugin.PluginManager
+import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.description
 import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.safeLoader
 import net.mamoe.mirai.console.plugin.description.PluginDependency
 import net.mamoe.mirai.console.plugin.description.PluginDescription
@@ -88,6 +89,8 @@ internal class PluginManagerImpl(
                 }
             }
         }
+
+        resolvedPlugins.add(MiraiConsoleAsPlugin)
     }
 
     // region LOADING
@@ -207,6 +210,8 @@ internal class PluginManagerImpl(
 
     @Throws(PluginResolutionException::class)
     private fun <D : PluginDescription> List<D>.sortByDependencies(): List<D> {
+        val alreadyLoadedPlugins = resolvedPlugins.asSequence().map { it.description }.toList() // snapshot
+
         val originPluginDescriptions = this@sortByDependencies
         val pending2BeResolved = originPluginDescriptions.toMutableList()
         val resolved = ArrayList<D>(pending2BeResolved.size)
@@ -252,6 +257,8 @@ internal class PluginManagerImpl(
             pending2BeResolved.forEach { pluginDesc ->
                 val missed = pluginDesc.dependencies.filter { dependency ->
                     val resolvedDep = originPluginDescriptions.findDependency(dependency)
+                        ?: alreadyLoadedPlugins.findDependency(dependency)
+
                     if (resolvedDep != null) {
                         resolvedDep.checkSatisfies(dependency, pluginDesc)
                         false

+ 2 - 0
mirai-console/backend/mirai-console/src/plugin/description/PluginDescription.kt

@@ -104,6 +104,8 @@ public interface PluginDescription {
     /**
      * 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件
      *
+     * 特别的, 可以使用 `net.mamoe.mirai-console` 作为插件 id 限制 mirai-console 版本 (自 2.16.0 后)
+     *
      * @see PluginDependency
      */
     public val dependencies: Set<PluginDependency>

+ 2 - 2
mirai-console/backend/mirai-console/test/testFramework/test/FrameworkInstanceTest.kt

@@ -18,11 +18,11 @@ class FrameworkInstanceTest : AbstractConsoleInstanceTest() {
 
     @Test
     fun testConsole1() {
-        assertEquals(0, PluginManager.plugins.size)
+        assertEquals(1, PluginManager.plugins.size)
     }
 
     @Test
     fun testConsole2() {
-        assertEquals(0, PluginManager.plugins.size)
+        assertEquals(1, PluginManager.plugins.size)
     }
 }