mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 18:40:15 +08:00
Redesign extensions;
Introduce ComponentStorage; Add receiver ScopedComponentStorage to JvmPlugin; Remove dynamic registers for PluginLoader; Redesign plugin load flow;
This commit is contained in:
parent
ca4107b476
commit
a13ce6ff17
@ -18,7 +18,7 @@ import kotlinx.coroutines.Job
|
||||
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.extension.foldExtensions
|
||||
import net.mamoe.mirai.console.extension.GlobalComponentStorage
|
||||
import net.mamoe.mirai.console.extensions.BotConfigurationAlterer
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
@ -133,8 +133,10 @@ public interface MiraiConsole : CoroutineScope {
|
||||
configuration()
|
||||
}
|
||||
|
||||
config = BotConfigurationAlterer.foldExtensions(config) { acc, extension ->
|
||||
extension.alterConfiguration(id, acc)
|
||||
config = GlobalComponentStorage.run {
|
||||
BotConfigurationAlterer.foldExtensions(config) { acc, extension ->
|
||||
extension.alterConfiguration(id, acc)
|
||||
}
|
||||
}
|
||||
|
||||
return when (password) {
|
||||
|
@ -0,0 +1,218 @@
|
||||
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
|
||||
package net.mamoe.mirai.console.extension
|
||||
|
||||
import net.mamoe.mirai.console.extensions.LazyPermissionServiceProviderImpl
|
||||
import net.mamoe.mirai.console.extensions.PermissionServiceProvider
|
||||
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
|
||||
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector.ExtensionPoint.selectSingleton
|
||||
import net.mamoe.mirai.console.internal.data.kClassQualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.permission.ExperimentalPermission
|
||||
import net.mamoe.mirai.console.permission.PermissionService
|
||||
import net.mamoe.mirai.console.plugin.Plugin
|
||||
import net.mamoe.mirai.console.plugin.name
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.CopyOnWriteArraySet
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.internal.LowPriorityInOverloadResolution
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.companionObjectInstance
|
||||
|
||||
/**
|
||||
* 组件容器, 容纳 [Plugin] 注册的 [Extension].
|
||||
*/
|
||||
public interface ComponentStorage {
|
||||
public fun <T : Extension> contribute(
|
||||
extensionPoint: ExtensionPoint<T>,
|
||||
plugin: Plugin,
|
||||
extensionInstance: T,
|
||||
)
|
||||
|
||||
public fun <T : Extension> contribute(
|
||||
extensionPoint: ExtensionPoint<T>,
|
||||
plugin: Plugin,
|
||||
lazyInstance: () -> T,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("EXPOSED_SUPER_CLASS")
|
||||
public class ScopedComponentStorage(
|
||||
@JvmField
|
||||
internal val plugin: Plugin,
|
||||
) : AbstractConcurrentComponentStorage() {
|
||||
/**
|
||||
* 注册一个扩展
|
||||
*/
|
||||
public fun <E : Extension> contribute(
|
||||
extensionPoint: ExtensionPoint<E>,
|
||||
lazyInstance: () -> E,
|
||||
) {
|
||||
contribute(extensionPoint, plugin, lazyInstance)
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册一个扩展
|
||||
*/
|
||||
public inline fun <reified E : Extension> contribute(
|
||||
noinline lazyInstance: () -> E,
|
||||
) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(contribute(
|
||||
(E::class.companionObjectInstance as? ExtensionPoint<E>
|
||||
?: error("Companion object of ${E::class.qualifiedName} is not an ExtensionPoint")),
|
||||
lazyInstance
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册一个扩展
|
||||
*/
|
||||
@ExperimentalPermission
|
||||
public fun contributePermissionService(
|
||||
lazyInstance: () -> PermissionService<*>,
|
||||
) {
|
||||
contribute(PermissionServiceProvider, plugin, LazyPermissionServiceProviderImpl(lazyInstance))
|
||||
}
|
||||
}
|
||||
|
||||
internal object GlobalComponentStorage : AbstractConcurrentComponentStorage()
|
||||
|
||||
internal interface ExtensionRegistry<out E : Extension> {
|
||||
val plugin: Plugin
|
||||
val extension: E
|
||||
|
||||
operator fun component1(): Plugin {
|
||||
return this.plugin
|
||||
}
|
||||
|
||||
operator fun component2(): E {
|
||||
return this.extension
|
||||
}
|
||||
}
|
||||
|
||||
internal class LazyExtensionRegistry<out E : Extension>(
|
||||
override val plugin: Plugin,
|
||||
initializer: () -> E,
|
||||
) : ExtensionRegistry<E> {
|
||||
override val extension: E by lazy { initializer() }
|
||||
}
|
||||
|
||||
internal data class DataExtensionRegistry<out E : Extension>(
|
||||
override val plugin: Plugin,
|
||||
override val extension: E,
|
||||
) : ExtensionRegistry<E>
|
||||
|
||||
internal abstract class AbstractConcurrentComponentStorage : ComponentStorage {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun <T : Extension> ExtensionPoint<out T>.getExtensions(): Set<ExtensionRegistry<T>> {
|
||||
return instances.getOrPut(this, ::CopyOnWriteArraySet) as Set<ExtensionRegistry<T>>
|
||||
}
|
||||
|
||||
internal fun mergeWith(another: AbstractConcurrentComponentStorage) {
|
||||
for ((ep, list) in another.instances) {
|
||||
for (extensionRegistry in list) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
ep as ExtensionPoint<Extension>
|
||||
this.contribute(ep, extensionRegistry.plugin, lazyInstance = { extensionRegistry.extension })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <T : Extension> ExtensionPoint<out T>.withExtensions(block: T.() -> Unit) {
|
||||
return withExtensions { _ -> block() }
|
||||
}
|
||||
|
||||
@LowPriorityInOverloadResolution
|
||||
internal inline fun <T : Extension> ExtensionPoint<out T>.withExtensions(block: T.(plugin: Plugin) -> Unit) {
|
||||
contract {
|
||||
callsInPlace(block)
|
||||
}
|
||||
for ((plugin, extension) in this.getExtensions()) {
|
||||
kotlin.runCatching {
|
||||
block.invoke(extension, plugin)
|
||||
}.getOrElse { throwable ->
|
||||
throwExtensionException(extension, plugin, throwable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <reified E : SingletonExtension<*>> ExtensionPoint<out E>.findSingleton(builtin: E): E =
|
||||
findSingleton(E::class, builtin)
|
||||
|
||||
internal fun <E : SingletonExtension<*>> ExtensionPoint<out E>.findSingleton(type: KClass<E>, builtin: E): E {
|
||||
val candidates = this.getExtensions()
|
||||
return when (candidates.size) {
|
||||
0 -> builtin
|
||||
1 -> candidates.single().extension
|
||||
else -> SingletonExtensionSelector.instance.selectSingleton(type, candidates) ?: builtin
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <reified E : SingletonExtension<T>, T> ExtensionPoint<out E>.findSingletonInstance(builtin: T): T =
|
||||
findSingletonInstance(E::class, builtin)
|
||||
|
||||
internal fun <E : SingletonExtension<T>, T> ExtensionPoint<out E>.findSingletonInstance(
|
||||
type: KClass<E>,
|
||||
builtin: T,
|
||||
): T {
|
||||
val candidates = this.getExtensions()
|
||||
return when (candidates.size) {
|
||||
0 -> builtin
|
||||
1 -> candidates.single().extension.instance
|
||||
else -> SingletonExtensionSelector.instance.selectSingleton(type, candidates)?.instance ?: builtin
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <T : Extension, E> ExtensionPoint<out T>.foldExtensions(
|
||||
initial: E,
|
||||
block: (acc: E, extension: T) -> E,
|
||||
): E {
|
||||
contract {
|
||||
callsInPlace(block)
|
||||
}
|
||||
var e: E = initial
|
||||
for ((plugin, extension) in this.getExtensions()) {
|
||||
kotlin.runCatching {
|
||||
e = block.invoke(e, extension)
|
||||
}.getOrElse { throwable ->
|
||||
throwExtensionException(extension, plugin, throwable)
|
||||
}
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
internal fun <T : Extension> ExtensionPoint<out T>.throwExtensionException(
|
||||
extension: T,
|
||||
plugin: Plugin,
|
||||
throwable: Throwable,
|
||||
) {
|
||||
throw ExtensionException(
|
||||
"Exception while executing extension ${extension.kClassQualifiedNameOrTip} provided by plugin '${plugin.name}', registered for ${this.type.qualifiedName}",
|
||||
throwable
|
||||
)
|
||||
}
|
||||
|
||||
internal inline fun <T : Extension> ExtensionPoint<T>.useExtensions(block: (extension: T) -> Unit): Unit =
|
||||
withExtensions(block)
|
||||
|
||||
@LowPriorityInOverloadResolution
|
||||
internal inline fun <T : Extension> ExtensionPoint<T>.useExtensions(block: (extension: T, plugin: Plugin) -> Unit): Unit =
|
||||
withExtensions(block)
|
||||
|
||||
val instances: MutableMap<ExtensionPoint<*>, MutableSet<ExtensionRegistry<*>>> = ConcurrentHashMap()
|
||||
override fun <T : Extension> contribute(
|
||||
extensionPoint: ExtensionPoint<T>,
|
||||
plugin: Plugin,
|
||||
extensionInstance: T,
|
||||
) {
|
||||
instances.getOrPut(extensionPoint, ::CopyOnWriteArraySet).add(DataExtensionRegistry(plugin, extensionInstance))
|
||||
}
|
||||
|
||||
override fun <T : Extension> contribute(
|
||||
extensionPoint: ExtensionPoint<T>,
|
||||
plugin: Plugin,
|
||||
lazyInstance: () -> T,
|
||||
) {
|
||||
instances.getOrPut(extensionPoint, ::CopyOnWriteArraySet).add(LazyExtensionRegistry(plugin, lazyInstance))
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ package net.mamoe.mirai.console.extension
|
||||
import net.mamoe.mirai.console.extensions.PermissionServiceProvider
|
||||
import net.mamoe.mirai.console.extensions.PluginLoaderProvider
|
||||
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
|
||||
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector.ExtensionPoint.selectSingleton
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
|
||||
/**
|
||||
|
@ -11,16 +11,8 @@
|
||||
|
||||
package net.mamoe.mirai.console.extension
|
||||
|
||||
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
|
||||
import net.mamoe.mirai.console.internal.data.kClassQualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.plugin.Plugin
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.name
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import java.util.*
|
||||
import java.util.concurrent.CopyOnWriteArraySet
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.internal.LowPriorityInOverloadResolution
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
|
||||
@ -28,9 +20,6 @@ import kotlin.reflect.full.isSubclassOf
|
||||
public interface ExtensionPoint<T : Extension> {
|
||||
public val type: KClass<T>
|
||||
|
||||
public fun registerExtension(plugin: Plugin, extension: T)
|
||||
public fun getExtensions(): Set<ExtensionRegistry<T>>
|
||||
|
||||
public companion object {
|
||||
@JvmStatic
|
||||
@JvmSynthetic
|
||||
@ -44,15 +33,7 @@ public interface ExtensionPoint<T : Extension> {
|
||||
}
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public interface SingletonExtensionPoint<T : SingletonExtension<*>> : ExtensionPoint<T> {
|
||||
public companion object {
|
||||
@JvmStatic
|
||||
@ConsoleExperimentalAPI
|
||||
public fun <T : SingletonExtension<*>> SingletonExtensionPoint<T>.findSingleton(): T? {
|
||||
return SingletonExtensionSelector.selectSingleton(type, this.getExtensions())
|
||||
}
|
||||
}
|
||||
}
|
||||
public interface SingletonExtensionPoint<T : SingletonExtension<*>> : ExtensionPoint<T>
|
||||
|
||||
/**
|
||||
* 表示一个扩展点
|
||||
@ -60,29 +41,8 @@ public interface SingletonExtensionPoint<T : SingletonExtension<*>> : ExtensionP
|
||||
@ConsoleExperimentalAPI
|
||||
public open class AbstractExtensionPoint<T : Extension>(
|
||||
@ConsoleExperimentalAPI
|
||||
public override val type: KClass<T>
|
||||
) : ExtensionPoint<T> {
|
||||
init {
|
||||
@Suppress("LeakingThis")
|
||||
allExtensionPoints.add(this)
|
||||
}
|
||||
|
||||
private val instances: MutableSet<ExtensionRegistry<T>> = CopyOnWriteArraySet()
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public override fun registerExtension(plugin: Plugin, extension: T) {
|
||||
// require(plugin.isEnabled) { "Plugin $plugin must be enabled before registering an extension." }
|
||||
requireNotNull(extension::class.qualifiedName) { "Extension must not be an anonymous object" }
|
||||
instances.add(ExtensionRegistry(plugin, extension))
|
||||
}
|
||||
|
||||
public override fun getExtensions(): Set<ExtensionRegistry<T>> = Collections.unmodifiableSet(instances)
|
||||
|
||||
internal companion object {
|
||||
@ConsoleExperimentalAPI
|
||||
internal val allExtensionPoints: MutableList<AbstractExtensionPoint<*>> = mutableListOf()
|
||||
}
|
||||
}
|
||||
public override val type: KClass<T>,
|
||||
) : ExtensionPoint<T>
|
||||
|
||||
|
||||
/**
|
||||
@ -99,58 +59,4 @@ public open class ExtensionException : RuntimeException {
|
||||
public constructor(message: String?) : super(message)
|
||||
public constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||
public constructor(cause: Throwable?) : super(cause)
|
||||
}
|
||||
|
||||
internal inline fun <T : Extension> AbstractExtensionPoint<out T>.withExtensions(block: T.() -> Unit) {
|
||||
return withExtensions { _ -> block() }
|
||||
}
|
||||
|
||||
@LowPriorityInOverloadResolution
|
||||
internal inline fun <T : Extension> AbstractExtensionPoint<out T>.withExtensions(block: T.(plugin: Plugin) -> Unit) {
|
||||
contract {
|
||||
callsInPlace(block)
|
||||
}
|
||||
for ((plugin, extension) in this.getExtensions()) {
|
||||
kotlin.runCatching {
|
||||
block.invoke(extension, plugin)
|
||||
}.getOrElse { throwable ->
|
||||
throwExtensionException(extension, plugin, throwable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <T : Extension, E> AbstractExtensionPoint<out T>.foldExtensions(
|
||||
initial: E,
|
||||
block: (acc: E, extension: T) -> E
|
||||
): E {
|
||||
contract {
|
||||
callsInPlace(block)
|
||||
}
|
||||
var e: E = initial
|
||||
for ((plugin, extension) in this.getExtensions()) {
|
||||
kotlin.runCatching {
|
||||
e = block.invoke(e, extension)
|
||||
}.getOrElse { throwable ->
|
||||
throwExtensionException(extension, plugin, throwable)
|
||||
}
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
internal fun <T : Extension> AbstractExtensionPoint<out T>.throwExtensionException(
|
||||
extension: T,
|
||||
plugin: Plugin,
|
||||
throwable: Throwable
|
||||
) {
|
||||
throw ExtensionException(
|
||||
"Exception while executing extension ${extension.kClassQualifiedNameOrTip} provided by plugin '${plugin.name}', registered for ${this.type.qualifiedName}",
|
||||
throwable
|
||||
)
|
||||
}
|
||||
|
||||
internal inline fun <T : Extension> AbstractExtensionPoint<T>.useExtensions(block: (extension: T) -> Unit): Unit =
|
||||
withExtensions(block)
|
||||
|
||||
@LowPriorityInOverloadResolution
|
||||
internal inline fun <T : Extension> AbstractExtensionPoint<T>.useExtensions(block: (extension: T, plugin: Plugin) -> Unit): Unit =
|
||||
withExtensions(block)
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
/*
|
||||
* 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.extension
|
||||
|
||||
import net.mamoe.mirai.console.plugin.Plugin
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public data class ExtensionRegistry<T>(
|
||||
public val plugin: Plugin,
|
||||
public val extension: T
|
||||
)
|
@ -5,18 +5,23 @@ import net.mamoe.mirai.console.extension.SingletonExtension
|
||||
import net.mamoe.mirai.console.extension.SingletonExtensionPoint
|
||||
import net.mamoe.mirai.console.permission.ExperimentalPermission
|
||||
import net.mamoe.mirai.console.permission.PermissionService
|
||||
import net.mamoe.mirai.console.plugin.description.PluginLoadPriority
|
||||
|
||||
/**
|
||||
* [权限服务][PermissionService] 提供器.
|
||||
*
|
||||
* 当插件注册 [PermissionService] 后, 默认会使用插件的 [PermissionService].
|
||||
*
|
||||
* 此扩展可由 [PluginLoadPriority.BEFORE_EXTENSIONS] 和 [PluginLoadPriority.ON_EXTENSIONS] 插件提供
|
||||
*/
|
||||
@ExperimentalPermission
|
||||
public interface PermissionServiceProvider : SingletonExtension<PermissionService<*>> {
|
||||
public companion object ExtensionPoint :
|
||||
AbstractExtensionPoint<PermissionServiceProvider>(PermissionServiceProvider::class),
|
||||
SingletonExtensionPoint<PermissionServiceProvider>
|
||||
}
|
||||
|
||||
@ExperimentalPermission
|
||||
public class PermissionServiceProviderImpl(override val instance: PermissionService<*>) : PermissionServiceProvider
|
||||
|
||||
@ExperimentalPermission
|
||||
public class LazyPermissionServiceProviderImpl(initializer: () -> PermissionService<*>) : PermissionServiceProvider {
|
||||
override val instance: PermissionService<*> by lazy(initializer)
|
||||
}
|
@ -3,12 +3,9 @@ package net.mamoe.mirai.console.extensions
|
||||
import net.mamoe.mirai.console.extension.AbstractExtensionPoint
|
||||
import net.mamoe.mirai.console.extension.InstanceExtension
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.description.PluginLoadPriority
|
||||
|
||||
/**
|
||||
* 提供扩展 [PluginLoader]
|
||||
*
|
||||
* 此扩展可由 [PluginLoadPriority.BEFORE_EXTENSIONS] 插件提供
|
||||
*/
|
||||
public interface PluginLoaderProvider : InstanceExtension<PluginLoader<*, *>> {
|
||||
public companion object ExtensionPoint : AbstractExtensionPoint<PluginLoaderProvider>(PluginLoaderProvider::class)
|
||||
|
@ -12,8 +12,9 @@ package net.mamoe.mirai.console.extensions
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.extension.*
|
||||
import net.mamoe.mirai.console.internal.extensions.BuiltInSingletonExtensionSelector
|
||||
import net.mamoe.mirai.console.plugin.description.PluginLoadPriority
|
||||
import net.mamoe.mirai.console.plugin.Plugin
|
||||
import net.mamoe.mirai.console.plugin.name
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.utils.info
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@ -21,21 +22,33 @@ import kotlin.reflect.KClass
|
||||
* 用于同时拥有多个 [SingletonExtension] 时选择一个实例.
|
||||
*
|
||||
* 如有多个 [SingletonExtensionSelector] 注册, 将会停止服务器.
|
||||
*
|
||||
* 此扩展可由 [PluginLoadPriority.BEFORE_EXTENSIONS] 插件提供
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public interface SingletonExtensionSelector : FunctionExtension {
|
||||
public data class Registry<T : Extension>(
|
||||
val plugin: Plugin,
|
||||
val extension: T,
|
||||
)
|
||||
|
||||
/**
|
||||
* @return null 表示使用 builtin
|
||||
*/
|
||||
public fun <T : Extension> selectSingleton(
|
||||
extensionType: KClass<T>,
|
||||
candidates: Collection<ExtensionRegistry<T>>
|
||||
candidates: Collection<Registry<T>>,
|
||||
): T?
|
||||
|
||||
public companion object ExtensionPoint :
|
||||
AbstractExtensionPoint<SingletonExtensionSelector>(SingletonExtensionSelector::class) {
|
||||
internal val instance: SingletonExtensionSelector by lazy {
|
||||
val instances = SingletonExtensionSelector.getExtensions()
|
||||
when {
|
||||
|
||||
private var instanceField: SingletonExtensionSelector? = null
|
||||
|
||||
internal val instance: SingletonExtensionSelector get() = instanceField ?: error("")
|
||||
|
||||
internal fun init() {
|
||||
check(instanceField == null) { "Internal error: reinitialize SingletonExtensionSelector" }
|
||||
val instances = GlobalComponentStorage.run { SingletonExtensionSelector.getExtensions() }
|
||||
instanceField = when {
|
||||
instances.isEmpty() -> BuiltInSingletonExtensionSelector
|
||||
instances.size == 1 -> {
|
||||
instances.single().also { (plugin, ext) ->
|
||||
@ -50,8 +63,14 @@ public interface SingletonExtensionSelector : FunctionExtension {
|
||||
|
||||
internal fun <T : Extension> selectSingleton(
|
||||
extensionType: KClass<T>,
|
||||
candidates: Collection<ExtensionRegistry<T>>
|
||||
candidates: Collection<ExtensionRegistry<T>>,
|
||||
): T? =
|
||||
instance.selectSingleton(extensionType, candidates)
|
||||
instance.selectSingleton(extensionType, candidates.map { Registry(it.plugin, it.extension) })
|
||||
|
||||
|
||||
internal fun <T : Extension> SingletonExtensionSelector.selectSingleton(
|
||||
extensionType: KClass<T>,
|
||||
candidates: Collection<ExtensionRegistry<T>>,
|
||||
): T? = selectSingleton(extensionType, candidates.map { Registry(it.plugin, it.extension) })
|
||||
}
|
||||
}
|
@ -27,12 +27,14 @@ import net.mamoe.mirai.console.command.Command.Companion.primaryName
|
||||
import net.mamoe.mirai.console.command.CommandManager
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.extension.useExtensions
|
||||
import net.mamoe.mirai.console.extension.GlobalComponentStorage
|
||||
import net.mamoe.mirai.console.extensions.PermissionServiceProvider
|
||||
import net.mamoe.mirai.console.extensions.PostStartupExtension
|
||||
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
|
||||
import net.mamoe.mirai.console.internal.command.CommandManagerImpl
|
||||
import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig
|
||||
import net.mamoe.mirai.console.internal.data.builtins.ConsoleDataScope
|
||||
import net.mamoe.mirai.console.internal.data.castOrNull
|
||||
import net.mamoe.mirai.console.internal.extensions.BuiltInSingletonExtensionSelector
|
||||
import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl
|
||||
import net.mamoe.mirai.console.internal.util.autoHexToBytes
|
||||
@ -44,6 +46,7 @@ import net.mamoe.mirai.console.permission.RootPermission
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.PluginManager
|
||||
import net.mamoe.mirai.console.plugin.center.PluginCenter
|
||||
import net.mamoe.mirai.console.plugin.jvm.AbstractJvmPlugin
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.console.util.ConsoleInput
|
||||
import net.mamoe.mirai.utils.*
|
||||
@ -124,35 +127,48 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
|
||||
ConsoleDataScope.reloadAll()
|
||||
}
|
||||
|
||||
val pluginLoadSession: PluginManagerImpl.PluginLoadSession
|
||||
|
||||
phase `load BEFORE_EXTENSIONS plugins`@{
|
||||
phase `initialize all plugins`@{
|
||||
PluginManager // init
|
||||
|
||||
mainLogger.verbose { "Loading PluginLoader provider plugins..." }
|
||||
PluginManagerImpl.loadEnablePluginProviderPlugins()
|
||||
mainLogger.verbose { "${PluginManager.plugins.size} such plugin(s) loaded." }
|
||||
mainLogger.verbose { "Loading JVM plugins..." }
|
||||
PluginManagerImpl.loadAllPluginsUsingBuiltInLoaders()
|
||||
PluginManagerImpl.initExternalPluginLoaders().let { count ->
|
||||
mainLogger.verbose { "$count external PluginLoader(s) found. " }
|
||||
if (count != 0) {
|
||||
mainLogger.verbose { "Loading external plugins..." }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
phase `load all plugins`@{
|
||||
PluginManagerImpl.loadPlugins(PluginManagerImpl.scanPluginsUsingPluginLoadersIncludingThoseFromPluginLoaderProvider())
|
||||
|
||||
mainLogger.verbose { "${PluginManager.plugins.size} plugin(s) loaded." }
|
||||
}
|
||||
|
||||
phase `collect extensions`@{
|
||||
for (resolvedPlugin in PluginManagerImpl.resolvedPlugins) {
|
||||
resolvedPlugin.castOrNull<AbstractJvmPlugin>()?.let {
|
||||
GlobalComponentStorage.mergeWith(it.componentStorage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
phase `load SingletonExtensionSelector`@{
|
||||
SingletonExtensionSelector.init()
|
||||
val instance = SingletonExtensionSelector.instance
|
||||
if (instance is BuiltInSingletonExtensionSelector) {
|
||||
ConsoleDataScope.addAndReloadConfig(instance.config)
|
||||
}
|
||||
}
|
||||
|
||||
phase `load ON_EXTENSIONS plugins`@{
|
||||
mainLogger.verbose { "Scanning high-priority extension and normal plugins..." }
|
||||
pluginLoadSession = PluginManagerImpl.scanPluginsUsingPluginLoadersIncludingThoseFromPluginLoaderProvider()
|
||||
mainLogger.verbose { "${pluginLoadSession.allKindsOfPlugins.size} plugin(s) found." }
|
||||
|
||||
mainLogger.verbose { "Loading Extension provider plugins..." }
|
||||
PluginManagerImpl.loadEnableHighPriorityExtensionPlugins(pluginLoadSession)
|
||||
mainLogger.verbose { "${PluginManager.plugins.size} such plugin(s) loaded." }
|
||||
}
|
||||
|
||||
phase `load PermissionService`@{
|
||||
mainLogger.verbose { "Loading PermissionService..." }
|
||||
|
||||
PermissionService.instanceField = GlobalComponentStorage.run {
|
||||
PermissionServiceProvider.findSingletonInstance(BuiltInPermissionService)
|
||||
}
|
||||
|
||||
PermissionService.INSTANCE.let { ps ->
|
||||
if (ps is BuiltInPermissionService) {
|
||||
ConsoleDataScope.addAndReloadConfig(ps.config)
|
||||
@ -171,13 +187,13 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
|
||||
CommandManagerImpl.commandListener // start
|
||||
}
|
||||
|
||||
phase `load AFTER_EXTENSION plugins`@{
|
||||
mainLogger.verbose { "Loading normal plugins..." }
|
||||
val count = PluginManagerImpl.loadEnableNormalPlugins(pluginLoadSession)
|
||||
mainLogger.verbose { "$count normal plugin(s) loaded." }
|
||||
}
|
||||
phase `enable plugins`@{
|
||||
mainLogger.verbose { "Enabling plugins..." }
|
||||
|
||||
mainLogger.info { "${PluginManagerImpl.plugins.size} plugin(s) loaded." }
|
||||
PluginManagerImpl.enableAllLoadedPlugins()
|
||||
|
||||
mainLogger.info { "${PluginManagerImpl.plugins.size} plugin(s) enabled." }
|
||||
}
|
||||
|
||||
phase `auto-login bots`@{
|
||||
runBlocking {
|
||||
@ -198,7 +214,9 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
|
||||
}
|
||||
}
|
||||
|
||||
PostStartupExtension.useExtensions { it() }
|
||||
GlobalComponentStorage.run {
|
||||
PostStartupExtension.useExtensions { it() }
|
||||
}
|
||||
|
||||
mainLogger.info { "mirai-console started successfully." }
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.data.AutoSavePluginConfig
|
||||
import net.mamoe.mirai.console.data.value
|
||||
import net.mamoe.mirai.console.extension.Extension
|
||||
import net.mamoe.mirai.console.extension.ExtensionRegistry
|
||||
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
|
||||
import net.mamoe.mirai.console.internal.data.kClassQualifiedName
|
||||
import net.mamoe.mirai.console.plugin.name
|
||||
@ -25,7 +24,7 @@ internal object BuiltInSingletonExtensionSelector : SingletonExtensionSelector {
|
||||
|
||||
override fun <T : Extension> selectSingleton(
|
||||
extensionType: KClass<T>,
|
||||
candidates: Collection<ExtensionRegistry<T>>
|
||||
candidates: Collection<SingletonExtensionSelector.Registry<T>>,
|
||||
): T? = when {
|
||||
candidates.isEmpty() -> null
|
||||
candidates.size == 1 -> candidates.single().extension
|
||||
@ -42,14 +41,14 @@ internal object BuiltInSingletonExtensionSelector : SingletonExtensionSelector {
|
||||
|
||||
private fun <T : Extension> promptForSelectionAndSave(
|
||||
extensionType: KClass<T>,
|
||||
candidates: Collection<ExtensionRegistry<T>>
|
||||
candidates: Collection<SingletonExtensionSelector.Registry<T>>,
|
||||
) = promptForManualSelection(extensionType, candidates)
|
||||
.also { config.value[extensionType.qualifiedName!!] = it.extension.kClassQualifiedName!! }.extension
|
||||
|
||||
private fun <T : Any> promptForManualSelection(
|
||||
private fun <T : Extension> promptForManualSelection(
|
||||
extensionType: KClass<T>,
|
||||
candidates: Collection<ExtensionRegistry<T>>
|
||||
): ExtensionRegistry<T> {
|
||||
candidates: Collection<SingletonExtensionSelector.Registry<T>>,
|
||||
): SingletonExtensionSelector.Registry<T> {
|
||||
MiraiConsole.mainLogger.info { "There are multiple ${extensionType.simpleName}s, please select one:" }
|
||||
|
||||
val candidatesList = candidates.toList()
|
||||
|
@ -84,10 +84,10 @@ internal object JarPluginLoaderImpl :
|
||||
@Throws(PluginLoadException::class)
|
||||
override fun load(plugin: JvmPlugin) {
|
||||
ensureActive()
|
||||
|
||||
runCatching {
|
||||
if (plugin is JvmPluginInternal) {
|
||||
plugin.internalOnLoad()
|
||||
} else plugin.onLoad()
|
||||
check(plugin is JvmPluginInternal) { "A JvmPlugin must extend AbstractJvmPlugin" }
|
||||
plugin.internalOnLoad(plugin.componentStorage)
|
||||
}.getOrElse {
|
||||
throw PluginLoadException("Exception while loading ${plugin.description.name}", it)
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import kotlinx.atomicfu.locks.withLock
|
||||
import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.data.runCatchingLog
|
||||
import net.mamoe.mirai.console.extension.ScopedComponentStorage
|
||||
import net.mamoe.mirai.console.internal.data.mkdir
|
||||
import net.mamoe.mirai.console.permission.ExperimentalPermission
|
||||
import net.mamoe.mirai.console.permission.Permission
|
||||
@ -24,7 +25,9 @@ import net.mamoe.mirai.console.plugin.Plugin
|
||||
import net.mamoe.mirai.console.plugin.PluginManager
|
||||
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.safeLoader
|
||||
import net.mamoe.mirai.console.plugin.ResourceContainer.Companion.asResourceContainer
|
||||
import net.mamoe.mirai.console.plugin.jvm.AbstractJvmPlugin
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin.Companion.onLoad
|
||||
import net.mamoe.mirai.console.plugin.name
|
||||
import net.mamoe.mirai.console.util.NamedSupervisorJob
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
@ -41,9 +44,12 @@ internal val <T> T.job: Job where T : CoroutineScope, T : Plugin get() = this.co
|
||||
*/
|
||||
@PublishedApi
|
||||
internal abstract class JvmPluginInternal(
|
||||
parentCoroutineContext: CoroutineContext
|
||||
parentCoroutineContext: CoroutineContext,
|
||||
) : JvmPlugin, CoroutineScope {
|
||||
|
||||
@Suppress("LeakingThis")
|
||||
internal val componentStorage: ScopedComponentStorage = ScopedComponentStorage(this)
|
||||
|
||||
@OptIn(ExperimentalPermission::class)
|
||||
final override val parentPermission: Permission by lazy {
|
||||
PermissionService.INSTANCE.register(
|
||||
@ -106,8 +112,8 @@ internal abstract class JvmPluginInternal(
|
||||
}
|
||||
|
||||
@Throws(Throwable::class)
|
||||
internal fun internalOnLoad() { // propagate exceptions
|
||||
onLoad()
|
||||
internal fun internalOnLoad(componentStorage: ScopedComponentStorage) {
|
||||
onLoad(componentStorage)
|
||||
}
|
||||
|
||||
internal fun internalOnEnable(): Boolean {
|
||||
@ -135,6 +141,7 @@ internal abstract class JvmPluginInternal(
|
||||
// for future use
|
||||
@Suppress("PropertyName")
|
||||
internal val _intrinsicCoroutineContext: CoroutineContext by lazy {
|
||||
this as AbstractJvmPlugin
|
||||
CoroutineName("Plugin $dataHolderName")
|
||||
}
|
||||
|
||||
@ -149,7 +156,7 @@ internal abstract class JvmPluginInternal(
|
||||
.plus(parentCoroutineContext)
|
||||
.plus(
|
||||
NamedSupervisorJob(
|
||||
"Plugin $dataHolderName",
|
||||
"Plugin ${(this as AbstractJvmPlugin).dataHolderName}",
|
||||
parentCoroutineContext[Job] ?: JarPluginLoaderImpl.coroutineContext[Job]!!
|
||||
)
|
||||
)
|
||||
|
@ -11,24 +11,22 @@
|
||||
|
||||
package net.mamoe.mirai.console.internal.plugin
|
||||
|
||||
import kotlinx.atomicfu.locks.withLock
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.extension.useExtensions
|
||||
import net.mamoe.mirai.console.extension.GlobalComponentStorage
|
||||
import net.mamoe.mirai.console.extensions.PluginLoaderProvider
|
||||
import net.mamoe.mirai.console.internal.data.cast
|
||||
import net.mamoe.mirai.console.internal.data.mkdir
|
||||
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.PluginLoadPriority
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope
|
||||
import net.mamoe.mirai.utils.info
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsole.childScope("PluginManager") {
|
||||
|
||||
@ -43,11 +41,11 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
|
||||
private val _pluginLoaders: MutableList<PluginLoader<*, *>> by lazy {
|
||||
MiraiConsole.builtInPluginLoaders.toMutableList()
|
||||
}
|
||||
private val loadersLock: ReentrantLock = ReentrantLock()
|
||||
private val logger = MiraiConsole.createLogger("plugin")
|
||||
|
||||
@JvmField
|
||||
internal val resolvedPlugins: MutableList<Plugin> = mutableListOf()
|
||||
internal val resolvedPlugins: MutableList<Plugin> =
|
||||
CopyOnWriteArrayList() // write operations are mostly performed on init
|
||||
override val plugins: List<Plugin>
|
||||
get() = resolvedPlugins.toList()
|
||||
override val builtInLoaders: List<PluginLoader<*, *>>
|
||||
@ -64,17 +62,6 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
|
||||
?: error("Plugin is unloaded")
|
||||
|
||||
|
||||
override fun PluginLoader<*, *>.register(): Boolean = loadersLock.withLock {
|
||||
if (_pluginLoaders.any { it::class == this::class }) {
|
||||
return false
|
||||
}
|
||||
_pluginLoaders.add(this)
|
||||
}
|
||||
|
||||
override fun PluginLoader<*, *>.unregister() = loadersLock.withLock {
|
||||
_pluginLoaders.remove(this)
|
||||
}
|
||||
|
||||
init {
|
||||
MiraiConsole.coroutineContext[Job]!!.invokeOnCompletion {
|
||||
plugins.forEach { it.disable() }
|
||||
@ -114,59 +101,65 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
|
||||
}
|
||||
|
||||
internal class PluginLoadSession(
|
||||
val allKindsOfPlugins: List<Pair<PluginLoader<*, *>, List<PluginDescriptionWithLoader>>>
|
||||
val allKindsOfPlugins: List<PluginDescriptionWithLoader>,
|
||||
)
|
||||
|
||||
// Phase #2
|
||||
internal fun scanPluginsUsingPluginLoadersIncludingThoseFromPluginLoaderProvider(): PluginLoadSession {
|
||||
return PluginLoadSession(loadersLock.withLock { _pluginLoaders.listAllPlugins() })
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Phase #0:
|
||||
// - initialize all plugins using builtin loaders
|
||||
// - sort by dependencies
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* 使用 [builtInLoaders] 寻找所有插件, 并初始化其主类.
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Throws(PluginMissingDependencyException::class)
|
||||
private fun findAndSortAllPluginsUsingBuiltInLoaders(): List<PluginDescriptionWithLoader> {
|
||||
val allDescriptions =
|
||||
builtInLoaders.listAndSortAllPlugins()
|
||||
.asSequence()
|
||||
.onEach { (_, descriptions) ->
|
||||
descriptions.let(PluginManagerImpl::checkPluginDescription)
|
||||
}
|
||||
|
||||
return allDescriptions.toList().sortByDependencies()
|
||||
}
|
||||
|
||||
// Phase #0
|
||||
internal fun loadEnablePluginProviderPlugins() {
|
||||
loadAndEnableLoaderProvidersUsingBuiltInLoaders()
|
||||
}
|
||||
|
||||
// Phase #3
|
||||
internal fun loadEnableHighPriorityExtensionPlugins(session: PluginLoadSession): Int {
|
||||
loadersLock.withLock {
|
||||
session.allKindsOfPlugins.flatMap { it.second }
|
||||
.filter { it.loadPriority == PluginLoadPriority.ON_EXTENSIONS }
|
||||
.sortByDependencies()
|
||||
.also { it.loadAndEnableAllInOrder() }
|
||||
.let { return it.size }
|
||||
internal fun loadAllPluginsUsingBuiltInLoaders() {
|
||||
for ((l, _, p) in findAndSortAllPluginsUsingBuiltInLoaders()) {
|
||||
l.load(p)
|
||||
}
|
||||
}
|
||||
|
||||
// Phase #4
|
||||
internal fun loadEnableNormalPlugins(session: PluginLoadSession): Int {
|
||||
loadersLock.withLock {
|
||||
session.allKindsOfPlugins.flatMap { it.second }
|
||||
.filter { it.loadPriority == PluginLoadPriority.AFTER_EXTENSIONS }
|
||||
.sortByDependencies()
|
||||
.also { it.loadAndEnableAllInOrder() }
|
||||
.let { return it.size }
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Phase #1:
|
||||
// - load PluginLoaderProvider
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Phase #1
|
||||
internal fun loadPluginLoaderProvidedByPlugins() {
|
||||
loadersLock.withLock {
|
||||
internal fun initExternalPluginLoaders(): Int {
|
||||
var count = 0
|
||||
GlobalComponentStorage.run {
|
||||
PluginLoaderProvider.useExtensions { ext, plugin ->
|
||||
logger.info { "Loaded PluginLoader ${ext.instance} from ${plugin.name}" }
|
||||
_pluginLoaders.add(ext.instance)
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// Phase #2
|
||||
internal fun scanPluginsUsingPluginLoadersIncludingThoseFromPluginLoaderProvider(): PluginLoadSession {
|
||||
return PluginLoadSession(_pluginLoaders.filterNot { builtInLoaders.contains(it) }.listAndSortAllPlugins())
|
||||
}
|
||||
|
||||
private fun List<PluginDescriptionWithLoader>.loadAndEnableAllInOrder() {
|
||||
this.forEach { (loader, _, plugin) ->
|
||||
loader.loadPluginNoEnable(plugin)
|
||||
}
|
||||
this.forEach { (loader, _, plugin) ->
|
||||
loader.enablePlugin(plugin)
|
||||
}
|
||||
internal fun loadPlugins(session: PluginLoadSession) {
|
||||
session.allKindsOfPlugins.forEach { it.loader.load(it.plugin) }
|
||||
}
|
||||
|
||||
internal fun enableAllLoadedPlugins() {
|
||||
resolvedPlugins.forEach { it.enable() }
|
||||
}
|
||||
|
||||
@kotlin.jvm.Throws(PluginLoadException::class)
|
||||
@ -178,31 +171,10 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return [builtInLoaders] 可以加载的插件. 已经完成了 [PluginLoader.load], 但没有 [PluginLoader.enable]
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Throws(PluginMissingDependencyException::class)
|
||||
private fun loadAndEnableLoaderProvidersUsingBuiltInLoaders(): List<PluginDescriptionWithLoader> {
|
||||
val allDescriptions =
|
||||
builtInLoaders.listAllPlugins()
|
||||
.asSequence()
|
||||
.onEach { (loader, descriptions) ->
|
||||
loader as PluginLoader<Plugin, PluginDescription>
|
||||
|
||||
descriptions.forEach(PluginManagerImpl::checkPluginDescription)
|
||||
descriptions.filter { it.loadPriority == PluginLoadPriority.BEFORE_EXTENSIONS }.sortByDependencies()
|
||||
.loadAndEnableAllInOrder()
|
||||
}
|
||||
.flatMap { it.second.asSequence() }
|
||||
|
||||
return allDescriptions.toList()
|
||||
}
|
||||
|
||||
private fun List<PluginLoader<*, *>>.listAllPlugins(): List<Pair<PluginLoader<*, *>, List<PluginDescriptionWithLoader>>> {
|
||||
return associateWith { loader ->
|
||||
private fun List<PluginLoader<*, *>>.listAndSortAllPlugins(): List<PluginDescriptionWithLoader> {
|
||||
return flatMap { loader ->
|
||||
loader.listPlugins().map { plugin -> plugin.description.wrapWith(loader, plugin) }
|
||||
}.toList()
|
||||
}.sortByDependencies()
|
||||
}
|
||||
|
||||
@Throws(PluginMissingDependencyException::class)
|
||||
@ -220,7 +192,7 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
|
||||
fun Collection<PluginDependency>.filterIsMissing(): List<PluginDependency> =
|
||||
this.filterNot { it.isOptional || it in resolved }
|
||||
|
||||
tailrec fun List<D>.doSort() {
|
||||
fun List<D>.doSort() {
|
||||
if (this.isEmpty()) return
|
||||
|
||||
val beforeSize = this.size
|
||||
@ -239,14 +211,12 @@ internal object PluginManagerImpl : PluginManager, CoroutineScope by MiraiConsol
|
||||
this.doSort()
|
||||
return resolved
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
internal data class PluginDescriptionWithLoader(
|
||||
@JvmField val loader: PluginLoader<Plugin, PluginDescription>, // easier type
|
||||
@JvmField val delegate: PluginDescription,
|
||||
@JvmField val plugin: Plugin
|
||||
@JvmField val plugin: Plugin,
|
||||
) : PluginDescription by delegate
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
package net.mamoe.mirai.console.permission
|
||||
|
||||
import net.mamoe.mirai.console.extension.SingletonExtensionPoint.Companion.findSingleton
|
||||
import net.mamoe.mirai.console.extensions.PermissionServiceProvider
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.isSuperclassOf
|
||||
@ -47,7 +46,7 @@ public interface PermissionService<P : Permission> {
|
||||
public fun register(
|
||||
id: PermissionId,
|
||||
description: String,
|
||||
parent: Permission = RootPermission
|
||||
parent: Permission = RootPermission,
|
||||
): P
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@ -56,11 +55,12 @@ public interface PermissionService<P : Permission> {
|
||||
public fun deny(permissibleIdentifier: PermissibleIdentifier, permission: P)
|
||||
|
||||
public companion object {
|
||||
internal var instanceField: PermissionService<*>? = null
|
||||
|
||||
@get:JvmName("getInstance")
|
||||
@JvmStatic
|
||||
public val INSTANCE: PermissionService<out Permission> by lazy {
|
||||
PermissionServiceProvider.findSingleton()?.instance ?: BuiltInPermissionService
|
||||
}
|
||||
public val INSTANCE: PermissionService<out Permission>
|
||||
get() = instanceField ?: error("PermissionService is not yet initialized therefore cannot be used.")
|
||||
|
||||
public fun <P : Permission> PermissionService<P>.getOrFail(id: PermissionId): P =
|
||||
get(id) ?: throw PermissionNotFoundException(id)
|
||||
|
@ -18,7 +18,6 @@ import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.enable
|
||||
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
|
||||
import net.mamoe.mirai.console.plugin.description.PluginLoadPriority
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
|
||||
/**
|
||||
@ -64,11 +63,6 @@ public inline val Plugin.name: String get() = this.description.name
|
||||
*/
|
||||
public inline val Plugin.version: Semver get() = this.description.version
|
||||
|
||||
/**
|
||||
* 获取 [PluginDescription.loadPriority]
|
||||
*/
|
||||
public inline val Plugin.loadPriority: PluginLoadPriority get() = this.description.loadPriority
|
||||
|
||||
/**
|
||||
* 获取 [PluginDescription.info]
|
||||
*/
|
||||
|
@ -13,7 +13,6 @@ package net.mamoe.mirai.console.plugin
|
||||
|
||||
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.disable
|
||||
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.enable
|
||||
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
|
||||
@ -31,14 +30,11 @@ import java.util.*
|
||||
*
|
||||
* ## 扩展加载器
|
||||
* 插件被允许扩展一个加载器.
|
||||
* Console 使用 [ServiceLoader] 加载 [PluginLoader] 的实例.
|
||||
* 插件也可通过 [PluginManager.register] 手动注册, 然而这是不推荐的.
|
||||
*
|
||||
* ### 实现扩展加载器
|
||||
* 直接实现接口 [PluginLoader] 或 [FilePluginLoader], 并添加 [ServiceLoader] 相关资源文件即可.
|
||||
*
|
||||
* @see JarPluginLoader Jar 插件加载器
|
||||
* @see PluginManager.register 注册一个扩展的插件加载器
|
||||
*/
|
||||
public interface PluginLoader<P : Plugin, D : PluginDescription> {
|
||||
/**
|
||||
|
@ -14,10 +14,8 @@ 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.*
|
||||
|
||||
/**
|
||||
* 插件管理器.
|
||||
@ -103,22 +101,6 @@ public interface PluginManager {
|
||||
*/
|
||||
public val pluginLoaders: List<PluginLoader<*, *>>
|
||||
|
||||
/**
|
||||
* 手动注册一个扩展的插件加载器. 在启动时会通过 [ServiceLoader] 加载, 但也可以手动注册.
|
||||
*
|
||||
* @see PluginLoader 插件加载器
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public fun PluginLoader<*, *>.register(): Boolean
|
||||
|
||||
/**
|
||||
* 取消注册一个扩展的插件加载器
|
||||
*
|
||||
* @see PluginLoader 插件加载器
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public fun PluginLoader<*, *>.unregister(): Boolean
|
||||
|
||||
/**
|
||||
* 获取插件的 [描述][PluginDescription], 通过 [PluginLoader.getDescription]
|
||||
*/
|
||||
@ -158,8 +140,6 @@ public interface PluginManager {
|
||||
public companion object INSTANCE : PluginManager by PluginManagerImpl {
|
||||
// due to Kotlin's bug
|
||||
public override val Plugin.description: PluginDescription get() = PluginManagerImpl.run { description }
|
||||
public override fun PluginLoader<*, *>.register(): Boolean = PluginManagerImpl.run { register() }
|
||||
public override fun PluginLoader<*, *>.unregister(): Boolean = PluginManagerImpl.run { unregister() }
|
||||
public override fun Plugin.disable(): Unit = PluginManagerImpl.run { disable() }
|
||||
public override fun Plugin.enable(): Unit = PluginManagerImpl.run { enable() }
|
||||
public override fun Plugin.load(): Unit = PluginManagerImpl.run { load() }
|
||||
|
@ -20,13 +20,6 @@ import net.mamoe.mirai.console.plugin.PluginLoadException
|
||||
* @see Plugin
|
||||
*/
|
||||
public interface PluginDescription {
|
||||
/**
|
||||
* 插件类型. 将会决定加载顺序
|
||||
*
|
||||
* @see PluginLoadPriority
|
||||
*/
|
||||
public val loadPriority: PluginLoadPriority
|
||||
|
||||
/**
|
||||
* 插件 ID, 必须全英文, 仅允许英文字母, '-', '_', '.'.
|
||||
*
|
||||
|
@ -1,59 +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
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.console.plugin.description
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import net.mamoe.mirai.console.extension.Extension
|
||||
import net.mamoe.mirai.console.extensions.BotConfigurationAlterer
|
||||
import net.mamoe.mirai.console.extensions.PermissionServiceProvider
|
||||
import net.mamoe.mirai.console.extensions.PluginLoaderProvider
|
||||
import net.mamoe.mirai.console.extensions.SingletonExtensionSelector
|
||||
import net.mamoe.mirai.console.internal.data.map
|
||||
import net.mamoe.mirai.console.plugin.description.PluginLoadPriority.*
|
||||
|
||||
/**
|
||||
* 插件类型.
|
||||
*
|
||||
* 插件类型将影响加载顺序: [BEFORE_EXTENSIONS] -> [ON_EXTENSIONS] -> [AFTER_EXTENSIONS].
|
||||
*
|
||||
* 依赖解决过程与插件类型有很大关联. 在一个较早的阶段, 只会解决在此阶段加载的插件. 意味着 [BEFORE_EXTENSIONS] 不允许依赖一个 [AFTER_EXTENSIONS] 类型的插件.
|
||||
*/
|
||||
public enum class PluginLoadPriority {
|
||||
/**
|
||||
* 表示此插件最早被加载. 在 Console 启动时的第一初始化阶段就会加载这些插件.
|
||||
*
|
||||
* 一般只有提供 [PluginLoaderProvider] 或 [SingletonExtensionSelector] 的插件才需要在此阶段加载.
|
||||
*/
|
||||
BEFORE_EXTENSIONS,
|
||||
|
||||
/**
|
||||
* 表示此插件提供一些高优先级的 [Extension], 应在加载其他 [AFTER_EXTENSIONS] 类型插件前加载
|
||||
*
|
||||
* 高优先级的 [Extension] 通常是覆盖 Console 内置的部分服务的扩展. 如 [PermissionServiceProvider].
|
||||
*
|
||||
* 一些普通的 [Extension], 如 [BotConfigurationAlterer], 也可以使用 [AFTER_EXTENSIONS] 类型插件注册.
|
||||
*/
|
||||
ON_EXTENSIONS,
|
||||
|
||||
/**
|
||||
* 表示此插件为一个通常的插件, 在扩展处理完毕后加载.
|
||||
*/
|
||||
AFTER_EXTENSIONS;
|
||||
|
||||
public object AsStringSerializer : KSerializer<PluginLoadPriority> by String.serializer().map(
|
||||
serializer = { it.name },
|
||||
deserializer = { str ->
|
||||
values().firstOrNull {
|
||||
it.name.equals(str, ignoreCase = true)
|
||||
} ?: AFTER_EXTENSIONS
|
||||
}
|
||||
)
|
||||
}
|
@ -7,10 +7,13 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS")
|
||||
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS", "NOTHING_TO_INLINE")
|
||||
|
||||
package net.mamoe.mirai.console.plugin.jvm
|
||||
|
||||
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.internal.plugin.JvmPluginInternal
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.utils.minutesToMillis
|
||||
@ -19,18 +22,52 @@ import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
/**
|
||||
* [JavaPlugin] 和 [KotlinPlugin] 的父类
|
||||
* [JavaPlugin] 和 [KotlinPlugin] 的父类. 所有 [JvmPlugin] 都应该拥有此类作为直接或间接父类.
|
||||
*
|
||||
* @see JavaPlugin
|
||||
* @see KotlinPlugin
|
||||
*/
|
||||
public abstract class AbstractJvmPlugin @JvmOverloads constructor(
|
||||
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||
) : JvmPlugin, JvmPluginInternal(parentCoroutineContext) {
|
||||
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext,
|
||||
) : JvmPlugin, JvmPluginInternal(parentCoroutineContext), AutoSavePluginDataHolder {
|
||||
@ConsoleExperimentalAPI
|
||||
public final override val dataHolderName: String
|
||||
get() = this.description.name
|
||||
|
||||
public final override val loader: JarPluginLoader get() = super<JvmPluginInternal>.loader
|
||||
|
||||
/**
|
||||
* 重载 [PluginData]
|
||||
*
|
||||
* @see reloadPluginData
|
||||
*/
|
||||
@JvmName("reloadPluginData")
|
||||
public fun <T : PluginData> T.reload(): Unit = loader.dataStorage.load(this@AbstractJvmPlugin, this)
|
||||
|
||||
/**
|
||||
* 重载 [PluginConfig]
|
||||
*
|
||||
* @see reloadPluginConfig
|
||||
*/
|
||||
@JvmName("reloadPluginConfig")
|
||||
public fun <T : PluginConfig> T.reload(): Unit = loader.configStorage.load(this@AbstractJvmPlugin, this)
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public override val autoSaveIntervalMillis: LongRange = 30.secondsToMillis..10.minutesToMillis
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载一个 [PluginData]
|
||||
*
|
||||
* @see AbstractJvmPlugin.reload
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun AbstractJvmPlugin.reloadPluginData(instance: PluginData): Unit = this.run { instance.reload() }
|
||||
|
||||
/**
|
||||
* 重载一个 [PluginConfig]
|
||||
*
|
||||
* @see AbstractJvmPlugin.reload
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun AbstractJvmPlugin.reloadPluginConfig(instance: PluginConfig): Unit = this.run { instance.reload() }
|
@ -18,23 +18,18 @@
|
||||
package net.mamoe.mirai.console.plugin.jvm
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
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.extension.ScopedComponentStorage
|
||||
import net.mamoe.mirai.console.permission.ExperimentalPermission
|
||||
import net.mamoe.mirai.console.permission.PermissionIdNamespace
|
||||
import net.mamoe.mirai.console.plugin.Plugin
|
||||
import net.mamoe.mirai.console.plugin.PluginFileExtensions
|
||||
import net.mamoe.mirai.console.plugin.ResourceContainer
|
||||
import net.mamoe.mirai.console.plugin.getDescription
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
|
||||
|
||||
/**
|
||||
* Java, Kotlin 或其他 JVM 平台插件
|
||||
*
|
||||
* 有关 [JvmPlugin] 相关实现方法,请参考
|
||||
*
|
||||
* @see AbstractJvmPlugin 默认实现
|
||||
*
|
||||
* @see JavaPlugin Java 插件
|
||||
@ -45,71 +40,39 @@ import net.mamoe.mirai.utils.MiraiLogger
|
||||
*/
|
||||
@OptIn(ExperimentalPermission::class)
|
||||
public interface JvmPlugin : Plugin, CoroutineScope,
|
||||
PluginFileExtensions, ResourceContainer, AutoSavePluginDataHolder, PermissionIdNamespace {
|
||||
PluginFileExtensions, ResourceContainer, PermissionIdNamespace {
|
||||
|
||||
/** 日志 */
|
||||
public val logger: MiraiLogger
|
||||
|
||||
/** 插件描述 */
|
||||
public val description: JvmPluginDescription get() = loader.getDescription(this)
|
||||
public val description: JvmPluginDescription
|
||||
|
||||
/** 所属插件加载器实例 */
|
||||
@JvmDefault
|
||||
public override val loader: JarPluginLoader
|
||||
get() = JarPluginLoader
|
||||
|
||||
/**
|
||||
* 重载 [PluginData]
|
||||
*
|
||||
* @see reloadPluginData
|
||||
*/
|
||||
@JvmDefault
|
||||
@JvmName("reloadPluginData")
|
||||
public fun <T : PluginData> T.reload(): Unit = loader.dataStorage.load(this@JvmPlugin, this)
|
||||
|
||||
/**
|
||||
* 重载 [PluginConfig]
|
||||
*
|
||||
* @see reloadPluginConfig
|
||||
*/
|
||||
@JvmDefault
|
||||
@JvmName("reloadPluginConfig")
|
||||
public fun <T : PluginConfig> T.reload(): Unit = loader.configStorage.load(this@JvmPlugin, this)
|
||||
// `final` in AbstractJvmPlugin
|
||||
public override val loader: JarPluginLoader get() = JarPluginLoader
|
||||
|
||||
/**
|
||||
* 在插件被加载时调用. 只会被调用一次.
|
||||
*
|
||||
* 在 [onLoad] 时可注册扩展 [ScopedComponentStorage.contribute]
|
||||
*
|
||||
* @receiver 组件容器
|
||||
*/
|
||||
@JvmDefault
|
||||
public fun onLoad() {
|
||||
}
|
||||
public fun @ParameterName("storage") ScopedComponentStorage.onLoad() {}
|
||||
|
||||
/**
|
||||
* 在插件被启用时调用, 可能会被调用多次
|
||||
*/
|
||||
@JvmDefault
|
||||
public fun onEnable() {
|
||||
}
|
||||
public fun onEnable() {}
|
||||
|
||||
/**
|
||||
* 在插件被关闭时调用, 可能会被调用多次
|
||||
*/
|
||||
@JvmDefault
|
||||
public fun onDisable() {
|
||||
public fun onDisable() {}
|
||||
|
||||
public companion object {
|
||||
@JvmSynthetic
|
||||
public inline fun JvmPlugin.onLoad(storage: ScopedComponentStorage): Unit = storage.onLoad()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载一个 [PluginData]
|
||||
*
|
||||
* @see JvmPlugin.reload
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun JvmPlugin.reloadPluginData(instance: PluginData): Unit = this.run { instance.reload() }
|
||||
|
||||
/**
|
||||
* 重载一个 [PluginConfig]
|
||||
*
|
||||
* @see JvmPlugin.reload
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun JvmPlugin.reloadPluginConfig(instance: PluginConfig): Unit = this.run { instance.reload() }
|
||||
}
|
@ -14,7 +14,6 @@ package net.mamoe.mirai.console.plugin.jvm
|
||||
import com.vdurmont.semver4j.Semver
|
||||
import net.mamoe.mirai.console.plugin.description.PluginDependency
|
||||
import net.mamoe.mirai.console.plugin.description.PluginDescription
|
||||
import net.mamoe.mirai.console.plugin.description.PluginLoadPriority
|
||||
import kotlin.internal.LowPriorityInOverloadResolution
|
||||
|
||||
/**
|
||||
@ -22,6 +21,8 @@ import kotlin.internal.LowPriorityInOverloadResolution
|
||||
*
|
||||
* 请不要自行实现 [JvmPluginDescription] 接口. 它不具有继承稳定性.
|
||||
*
|
||||
* 要查看相关约束, 参考 [PluginDescription]
|
||||
*
|
||||
* @see SimpleJvmPluginDescription
|
||||
* @see JvmPluginDescriptionBuilder
|
||||
*/
|
||||
@ -82,7 +83,6 @@ public class JvmPluginDescriptionBuilder(
|
||||
private var author: String = ""
|
||||
private var info: String = ""
|
||||
private var dependencies: MutableSet<PluginDependency> = mutableSetOf()
|
||||
private var loadPriority: PluginLoadPriority = PluginLoadPriority.AFTER_EXTENSIONS
|
||||
|
||||
@ILoveKuriyamaMiraiForever
|
||||
public fun name(value: String): JvmPluginDescriptionBuilder = apply { this.name = value.trim() }
|
||||
@ -103,21 +103,6 @@ public class JvmPluginDescriptionBuilder(
|
||||
@ILoveKuriyamaMiraiForever
|
||||
public fun info(value: String): JvmPluginDescriptionBuilder = apply { this.info = value.trimIndent() }
|
||||
|
||||
@ILoveKuriyamaMiraiForever
|
||||
public fun kind(value: PluginLoadPriority): JvmPluginDescriptionBuilder = apply { this.loadPriority = value }
|
||||
|
||||
@ILoveKuriyamaMiraiForever
|
||||
public fun normalPlugin(): JvmPluginDescriptionBuilder =
|
||||
apply { this.loadPriority = PluginLoadPriority.AFTER_EXTENSIONS }
|
||||
|
||||
@ILoveKuriyamaMiraiForever
|
||||
public fun loaderProviderPlugin(): JvmPluginDescriptionBuilder =
|
||||
apply { this.loadPriority = PluginLoadPriority.BEFORE_EXTENSIONS }
|
||||
|
||||
@ILoveKuriyamaMiraiForever
|
||||
public fun highPriorityExtensionsPlugin(): JvmPluginDescriptionBuilder =
|
||||
apply { this.loadPriority = PluginLoadPriority.ON_EXTENSIONS }
|
||||
|
||||
@ILoveKuriyamaMiraiForever
|
||||
public fun dependsOn(
|
||||
pluginId: String,
|
||||
@ -154,7 +139,7 @@ public class JvmPluginDescriptionBuilder(
|
||||
|
||||
@Suppress("DEPRECATION_ERROR")
|
||||
public fun build(): JvmPluginDescription =
|
||||
SimpleJvmPluginDescription(name, version, id, author, info, dependencies, loadPriority)
|
||||
SimpleJvmPluginDescription(name, version, id, author, info, dependencies)
|
||||
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
@DslMarker
|
||||
@ -194,7 +179,6 @@ public data class SimpleJvmPluginDescription
|
||||
public override val author: String = "",
|
||||
public override val info: String = "",
|
||||
public override val dependencies: Set<PluginDependency> = setOf(),
|
||||
public override val loadPriority: PluginLoadPriority = PluginLoadPriority.AFTER_EXTENSIONS,
|
||||
) : JvmPluginDescription {
|
||||
|
||||
@Deprecated(
|
||||
@ -216,8 +200,7 @@ public data class SimpleJvmPluginDescription
|
||||
author: String = "",
|
||||
info: String = "",
|
||||
dependencies: Set<PluginDependency> = setOf(),
|
||||
loadPriority: PluginLoadPriority = PluginLoadPriority.AFTER_EXTENSIONS,
|
||||
) : this(name, Semver(version, Semver.SemverType.LOOSE), id, author, info, dependencies, loadPriority)
|
||||
) : this(name, Semver(version, Semver.SemverType.LOOSE), id, author, info, dependencies)
|
||||
|
||||
init {
|
||||
require(!name.contains(':')) { "':' is forbidden in plugin name" }
|
||||
@ -242,8 +225,7 @@ public fun JvmPluginDescription(
|
||||
author: String = "",
|
||||
info: String = "",
|
||||
dependencies: Set<PluginDependency> = setOf(),
|
||||
loadPriority: PluginLoadPriority = PluginLoadPriority.AFTER_EXTENSIONS
|
||||
): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies, loadPriority)
|
||||
): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies)
|
||||
|
||||
@Deprecated(
|
||||
"JvmPluginDescription 没有构造器. 请使用 SimpleJvmPluginDescription.",
|
||||
@ -262,5 +244,4 @@ public fun JvmPluginDescription(
|
||||
author: String = "",
|
||||
info: String = "",
|
||||
dependencies: Set<PluginDependency> = setOf(),
|
||||
loadPriority: PluginLoadPriority = PluginLoadPriority.AFTER_EXTENSIONS
|
||||
): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies, loadPriority)
|
||||
): JvmPluginDescription = SimpleJvmPluginDescription(name, version, id, author, info, dependencies)
|
||||
|
Loading…
Reference in New Issue
Block a user