mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 18:40:15 +08:00
Support loading PluginLoaders by ServiceLoader for each plugin;
Rearrange implementations
This commit is contained in:
parent
dee8e44110
commit
5a5d45778a
@ -19,7 +19,7 @@ import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.MiraiConsole.INSTANCE
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.internal.util.childScopeContext
|
||||
import net.mamoe.mirai.console.internal.data.builtin.childScopeContext
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.PluginManager
|
||||
import net.mamoe.mirai.console.plugin.center.PluginCenter
|
||||
|
@ -24,9 +24,9 @@ import net.mamoe.mirai.console.command.Command.Companion.primaryName
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.internal.command.CommandManagerImpl
|
||||
import net.mamoe.mirai.console.internal.data.builtin.ConsoleDataScope
|
||||
import net.mamoe.mirai.console.internal.plugin.CuiPluginCenter
|
||||
import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl
|
||||
import net.mamoe.mirai.console.internal.util.ConsoleDataScope
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.PluginManager
|
||||
import net.mamoe.mirai.console.plugin.center.PluginCenter
|
||||
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package net.mamoe.mirai.console.internal.data.builtin
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.data.AutoSavePluginConfig
|
||||
import net.mamoe.mirai.console.data.PluginDataExtensions.mapKeys
|
||||
import net.mamoe.mirai.console.data.PluginDataExtensions.withEmptyDefault
|
||||
import net.mamoe.mirai.console.data.getValue
|
||||
import net.mamoe.mirai.console.data.value
|
||||
import net.mamoe.mirai.console.util.BotManager
|
||||
import net.mamoe.mirai.contact.User
|
||||
|
||||
internal object BotManagerImpl : BotManager {
|
||||
override val User.isManager: Boolean get() = this.id in ManagersConfig[this.bot]
|
||||
|
||||
override fun Bot.removeManager(id: Long): Boolean {
|
||||
return ManagersConfig[this].remove(id)
|
||||
}
|
||||
|
||||
override val Bot.managers: List<Long>
|
||||
get() = ManagersConfig[this].toList()
|
||||
|
||||
override fun Bot.addManager(id: Long): Boolean {
|
||||
return ManagersConfig[this].add(id)
|
||||
}
|
||||
}
|
||||
|
||||
internal object ManagersConfig : AutoSavePluginConfig() {
|
||||
override val saveName: String
|
||||
get() = "Managers"
|
||||
|
||||
private val managers by value<MutableMap<Long, MutableSet<Long>>>().withEmptyDefault()
|
||||
.mapKeys(Bot::getInstance, Bot::id)
|
||||
|
||||
internal operator fun get(bot: Bot): MutableSet<Long> = managers[bot]!!
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2020 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/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.internal.data.builtin
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.data.AutoSavePluginDataHolder
|
||||
import net.mamoe.mirai.console.data.PluginConfig
|
||||
import net.mamoe.mirai.console.data.PluginData
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.utils.minutesToMillis
|
||||
|
||||
|
||||
internal object ConsoleDataScope : CoroutineScope by MiraiConsole.childScope("ConsoleDataScope") {
|
||||
private val data: Array<out PluginData> = arrayOf()
|
||||
private val configs: Array<out PluginConfig> = arrayOf(ManagersConfig)
|
||||
|
||||
fun reloadAll() {
|
||||
data.forEach { dt ->
|
||||
ConsoleBuiltInPluginDataStorage.load(ConsoleBuiltInPluginDataHolder, dt)
|
||||
}
|
||||
configs.forEach { config ->
|
||||
ConsoleBuiltInPluginConfigStorage.load(ConsoleBuiltInPluginConfigHolder, config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal object ConsoleBuiltInPluginDataHolder : AutoSavePluginDataHolder,
|
||||
CoroutineScope by ConsoleDataScope.childScope("ConsoleBuiltInPluginDataHolder") {
|
||||
override val autoSaveIntervalMillis: LongRange = 1.minutesToMillis..10.minutesToMillis
|
||||
override val name: String get() = "ConsoleBuiltIns"
|
||||
}
|
||||
|
||||
internal object ConsoleBuiltInPluginConfigHolder : AutoSavePluginDataHolder,
|
||||
CoroutineScope by ConsoleDataScope.childScope("ConsoleBuiltInPluginConfigHolder") {
|
||||
override val autoSaveIntervalMillis: LongRange = 1.minutesToMillis..10.minutesToMillis
|
||||
override val name: String get() = "ConsoleBuiltIns"
|
||||
}
|
||||
|
||||
internal object ConsoleBuiltInPluginDataStorage :
|
||||
PluginDataStorage by MiraiConsoleImplementationBridge.dataStorageForBuiltIns
|
||||
|
||||
internal object ConsoleBuiltInPluginConfigStorage :
|
||||
PluginDataStorage by MiraiConsoleImplementationBridge.configStorageForBuiltIns
|
@ -18,11 +18,11 @@ import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.internal.data.createInstanceOrNull
|
||||
import net.mamoe.mirai.console.internal.util.childScopeContext
|
||||
import net.mamoe.mirai.console.plugin.AbstractFilePluginLoader
|
||||
import net.mamoe.mirai.console.plugin.PluginLoadException
|
||||
import net.mamoe.mirai.console.plugin.jvm.*
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.console.util.childScopeContext
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.yamlkt.Yaml
|
||||
import java.io.File
|
||||
@ -48,7 +48,7 @@ internal object JarPluginLoaderImpl :
|
||||
logger.error("Unhandled Jar plugin exception: ${throwable.message}", throwable)
|
||||
})
|
||||
|
||||
private val classLoader: PluginsLoader = PluginsLoader(this.javaClass.classLoader)
|
||||
internal val classLoader: PluginsLoader = PluginsLoader(this.javaClass.classLoader)
|
||||
|
||||
init { // delayed
|
||||
coroutineContext[Job]!!.invokeOnCompletion {
|
||||
|
@ -15,18 +15,23 @@ import kotlinx.atomicfu.locks.withLock
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.internal.data.cast
|
||||
import net.mamoe.mirai.console.internal.data.mkdir
|
||||
import net.mamoe.mirai.console.internal.util.childScope
|
||||
import net.mamoe.mirai.console.plugin.*
|
||||
import net.mamoe.mirai.console.plugin.description.PluginDependency
|
||||
import net.mamoe.mirai.console.plugin.description.PluginDescription
|
||||
import net.mamoe.mirai.console.plugin.description.PluginKind
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
import net.mamoe.mirai.console.util.childScope
|
||||
import net.mamoe.mirai.utils.error
|
||||
import net.mamoe.mirai.utils.info
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.streams.asSequence
|
||||
|
||||
internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsole.childScope("PluginManager") {
|
||||
|
||||
@ -123,9 +128,38 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Throws(PluginMissingDependencyException::class)
|
||||
internal fun loadEnablePlugins() {
|
||||
(loadAndEnableLoaderProviders() + _pluginLoaders.listAllPlugins().flatMap { it.second })
|
||||
loadAndEnableLoaderProviders()
|
||||
loadPluginLoaderProvidedByPlugins()
|
||||
loadersLock.withLock {
|
||||
_pluginLoaders.listAllPlugins().flatMap { it.second }
|
||||
.sortByDependencies().loadAndEnableAllInOrder()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadPluginLoaderProvidedByPlugins() {
|
||||
loadersLock.withLock {
|
||||
JarPluginLoaderImpl.classLoader.pluginLoaders.asSequence()
|
||||
.flatMap { (name, pluginClassLoader) ->
|
||||
ServiceLoader.load(PluginLoader::class.java, pluginClassLoader)
|
||||
.stream().asSequence()
|
||||
.associateBy { name }
|
||||
.asSequence()
|
||||
}
|
||||
.forEach { (name, provider) ->
|
||||
val pluginLoader = kotlin.runCatching {
|
||||
provider.get()
|
||||
}.getOrElse {
|
||||
logger.error(
|
||||
{ "Could not load PluginLoader ${it::class.qualifiedNameOrTip} from plugin $name" },
|
||||
it
|
||||
)
|
||||
return@forEach
|
||||
}
|
||||
_pluginLoaders.add(pluginLoader)
|
||||
logger.info { "Successfully loaded PluginLoader ${pluginLoader::class.qualifiedNameOrTip} from plugin $name" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<PluginDescriptionWithLoader>.loadAndEnableAllInOrder() {
|
||||
return this.map { (loader, desc) ->
|
||||
|
@ -15,7 +15,7 @@ import java.net.URLClassLoader
|
||||
|
||||
internal class PluginsLoader(private val parentClassLoader: ClassLoader) {
|
||||
private val loggerName = "PluginsLoader"
|
||||
private val pluginLoaders = linkedMapOf<String, PluginClassLoader>()
|
||||
internal val pluginLoaders = linkedMapOf<String, PluginClassLoader>()
|
||||
private val classesCache = mutableMapOf<String, Class<*>>()
|
||||
private val logger = MiraiConsole.newLogger(loggerName)
|
||||
|
||||
|
@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package net.mamoe.mirai.console.internal.util
|
||||
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.data.*
|
||||
import net.mamoe.mirai.console.data.PluginDataExtensions.mapKeys
|
||||
import net.mamoe.mirai.console.data.PluginDataExtensions.withEmptyDefault
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.internal.plugin.NamedSupervisorJob
|
||||
import net.mamoe.mirai.console.util.BotManager
|
||||
import net.mamoe.mirai.contact.User
|
||||
import net.mamoe.mirai.utils.minutesToMillis
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
internal object BotManagerImpl : BotManager {
|
||||
/**
|
||||
* 判断此用户是否为 console 管理员
|
||||
*/
|
||||
override val User.isManager: Boolean get() = this.id in ManagersConfig[this.bot]
|
||||
|
||||
override fun Bot.removeManager(id: Long): Boolean {
|
||||
return ManagersConfig[this].remove(id)
|
||||
}
|
||||
|
||||
override val Bot.managers: List<Long>
|
||||
get() = ManagersConfig[this].toList()
|
||||
|
||||
override fun Bot.addManager(id: Long): Boolean {
|
||||
return ManagersConfig[this].add(id)
|
||||
}
|
||||
}
|
||||
|
||||
@ValueName("Managers")
|
||||
internal object ManagersConfig : AutoSavePluginConfig() {
|
||||
override val saveName: String
|
||||
get() = "Managers"
|
||||
private val managers by value<MutableMap<Long, MutableSet<Long>>>().withEmptyDefault()
|
||||
.mapKeys(Bot::getInstance, Bot::id)
|
||||
|
||||
internal operator fun get(bot: Bot): MutableSet<Long> = managers[bot]!!
|
||||
}
|
||||
|
||||
|
||||
internal fun CoroutineContext.overrideWithSupervisorJob(name: String? = null): CoroutineContext =
|
||||
this + NamedSupervisorJob(name ?: "<unnamed>", this[Job])
|
||||
|
||||
internal fun CoroutineScope.childScope(
|
||||
name: String? = null,
|
||||
context: CoroutineContext = EmptyCoroutineContext
|
||||
): CoroutineScope =
|
||||
CoroutineScope(this.childScopeContext(name, context))
|
||||
|
||||
internal fun CoroutineScope.childScopeContext(
|
||||
name: String? = null,
|
||||
context: CoroutineContext = EmptyCoroutineContext
|
||||
): CoroutineContext =
|
||||
this.coroutineContext.overrideWithSupervisorJob(name) + context.let {
|
||||
if (name != null) it + CoroutineName(name)
|
||||
else it
|
||||
}
|
||||
|
||||
internal object ConsoleDataScope : CoroutineScope by MiraiConsole.childScope("ConsoleDataScope") {
|
||||
private val data: Array<out PluginData> = arrayOf()
|
||||
private val configs: Array<out PluginConfig> = arrayOf(ManagersConfig)
|
||||
|
||||
fun reloadAll() {
|
||||
data.forEach { dt ->
|
||||
ConsoleBuiltInPluginDataStorage.load(ConsoleBuiltInPluginDataHolder, dt)
|
||||
}
|
||||
configs.forEach { config ->
|
||||
ConsoleBuiltInPluginConfigStorage.load(ConsoleBuiltInPluginConfigHolder, config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal object ConsoleBuiltInPluginDataHolder : AutoSavePluginDataHolder,
|
||||
CoroutineScope by ConsoleDataScope.childScope("ConsoleBuiltInPluginDataHolder") {
|
||||
override val autoSaveIntervalMillis: LongRange = 1.minutesToMillis..10.minutesToMillis
|
||||
override val name: String get() = "ConsoleBuiltIns"
|
||||
}
|
||||
|
||||
internal object ConsoleBuiltInPluginConfigHolder : AutoSavePluginDataHolder,
|
||||
CoroutineScope by ConsoleDataScope.childScope("ConsoleBuiltInPluginConfigHolder") {
|
||||
override val autoSaveIntervalMillis: LongRange = 1.minutesToMillis..10.minutesToMillis
|
||||
override val name: String get() = "ConsoleBuiltIns"
|
||||
}
|
||||
|
||||
internal object ConsoleBuiltInPluginDataStorage :
|
||||
PluginDataStorage by MiraiConsoleImplementationBridge.dataStorageForBuiltIns
|
||||
|
||||
internal object ConsoleBuiltInPluginConfigStorage :
|
||||
PluginDataStorage by MiraiConsoleImplementationBridge.configStorageForBuiltIns
|
@ -17,6 +17,7 @@ import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.register
|
||||
import net.mamoe.mirai.console.plugin.description.PluginDescription
|
||||
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 插件加载器.
|
||||
@ -25,11 +26,16 @@ import java.io.File
|
||||
*
|
||||
* 有关插件的依赖和已加载的插件列表由 [PluginManager] 维护.
|
||||
*
|
||||
* ### 内建加载器
|
||||
* ## 内建加载器
|
||||
* - [JarPluginLoader] Jar 插件加载器
|
||||
*
|
||||
* ### 扩展加载器
|
||||
* 插件被允许扩展一个加载器。 可通过 [PluginManager.register]
|
||||
* ## 扩展加载器
|
||||
* 插件被允许扩展一个加载器.
|
||||
* Console 使用 [ServiceLoader] 加载 [PluginLoader] 的实例.
|
||||
* 插件也可通过 [PluginManager.register] 手动注册, 然而这是不推荐的.
|
||||
*
|
||||
* ### 实现扩展加载器
|
||||
* 直接实现接口 [PluginLoader] 或 [FilePluginLoader], 并添加 [ServiceLoader] 相关资源文件即可.
|
||||
*
|
||||
* @see JarPluginLoader Jar 插件加载器
|
||||
* @see PluginManager.register 注册一个扩展的插件加载器
|
||||
|
@ -14,8 +14,10 @@ package net.mamoe.mirai.console.plugin
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl
|
||||
import net.mamoe.mirai.console.plugin.description.PluginDescription
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 插件管理器.
|
||||
@ -102,10 +104,11 @@ public interface PluginManager {
|
||||
public val pluginLoaders: List<PluginLoader<*, *>>
|
||||
|
||||
/**
|
||||
* 注册一个扩展的插件加载器
|
||||
* 手动注册一个扩展的插件加载器. 在启动时会通过 [ServiceLoader] 加载, 但也可以手动注册.
|
||||
*
|
||||
* @see PluginLoader 插件加载器
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public fun PluginLoader<*, *>.register(): Boolean
|
||||
|
||||
/**
|
||||
@ -113,6 +116,7 @@ public interface PluginManager {
|
||||
*
|
||||
* @see PluginLoader 插件加载器
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public fun PluginLoader<*, *>.unregister(): Boolean
|
||||
|
||||
/**
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
package net.mamoe.mirai.console.plugin.jvm
|
||||
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
@ -27,7 +26,6 @@ public abstract class KotlinPlugin @JvmOverloads constructor(
|
||||
/**
|
||||
* 在内存动态加载的插件. 此为预览版本 API.
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public abstract class KotlinMemoryPlugin @JvmOverloads constructor(
|
||||
description: JvmPluginDescription,
|
||||
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2020 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/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:JvmName("CoroutineScopeUtils")
|
||||
|
||||
package net.mamoe.mirai.console.util
|
||||
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import net.mamoe.mirai.console.internal.plugin.NamedSupervisorJob
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
internal fun CoroutineContext.overrideWithSupervisorJob(name: String? = null): CoroutineContext =
|
||||
this + NamedSupervisorJob(name ?: "<unnamed>", this[Job])
|
||||
|
||||
internal fun CoroutineScope.childScope(
|
||||
name: String? = null,
|
||||
context: CoroutineContext = EmptyCoroutineContext
|
||||
): CoroutineScope =
|
||||
CoroutineScope(this.childScopeContext(name, context))
|
||||
|
||||
internal fun CoroutineScope.childScopeContext(
|
||||
name: String? = null,
|
||||
context: CoroutineContext = EmptyCoroutineContext
|
||||
): CoroutineContext =
|
||||
this.coroutineContext.overrideWithSupervisorJob(name) + context.let {
|
||||
if (name != null) it + CoroutineName(name)
|
||||
else it
|
||||
}
|
Loading…
Reference in New Issue
Block a user