mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-25 15:40:28 +08:00
Public api stabilization:
Separate PluginManager and its implementations; Add Setting extensions; Documentation updates
This commit is contained in:
parent
b2177c16cf
commit
74925ff6e8
@ -1,6 +1,5 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version Versions.kotlinCompiler
|
||||
@ -75,6 +74,8 @@ dependencies {
|
||||
|
||||
api("com.vdurmont:semver4j:3.1.0")
|
||||
|
||||
//api(kotlinx("collections-immutable", Versions.collectionsImmutable))
|
||||
|
||||
testApi("net.mamoe:mirai-core-qqandroid:${Versions.core}")
|
||||
testApi(kotlin("stdlib-jdk8"))
|
||||
testApi(kotlin("test"))
|
||||
|
@ -23,10 +23,10 @@ import net.mamoe.mirai.console.command.internal.InternalCommandManager
|
||||
import net.mamoe.mirai.console.command.primaryName
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.PluginManager
|
||||
import net.mamoe.mirai.console.plugin.PluginManagerImpl
|
||||
import net.mamoe.mirai.console.plugin.center.CuiPluginCenter
|
||||
import net.mamoe.mirai.console.plugin.center.PluginCenter
|
||||
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||
import net.mamoe.mirai.console.plugin.jvm.PluginManagerImpl
|
||||
import net.mamoe.mirai.console.setting.SettingStorage
|
||||
import net.mamoe.mirai.console.utils.ConsoleBuiltInSettingStorage
|
||||
import net.mamoe.mirai.console.utils.ConsoleExperimentalAPI
|
||||
@ -64,7 +64,9 @@ public interface MiraiConsole : CoroutineScope {
|
||||
public val mainLogger: MiraiLogger
|
||||
|
||||
/**
|
||||
* 内建加载器列表, 一般需要包含 [JarPluginLoader]
|
||||
* 内建加载器列表, 一般需要包含 [JarPluginLoader].
|
||||
*
|
||||
* @return 不可变 [List] ([java.util.Collections.unmodifiableList])
|
||||
*/
|
||||
public val builtInPluginLoaders: List<PluginLoader<*, *>>
|
||||
|
||||
|
@ -55,7 +55,9 @@ public interface MiraiConsoleImplementation : CoroutineScope {
|
||||
public val mainLogger: MiraiLogger
|
||||
|
||||
/**
|
||||
* 内建加载器列表, 一般需要包含 [JarPluginLoader]
|
||||
* 内建加载器列表, 一般需要包含 [JarPluginLoader].
|
||||
*
|
||||
* @return 不可变的 [List]
|
||||
*/
|
||||
public val builtInPluginLoaders: List<PluginLoader<*, *>>
|
||||
|
||||
|
@ -36,7 +36,7 @@ public interface PluginLoader<P : Plugin, D : PluginDescription> {
|
||||
*/
|
||||
@get:JvmName("getPluginDescription")
|
||||
@get:Throws(PluginLoadException::class)
|
||||
public val P.description: D // Java signature: `public P getDescription(P)`
|
||||
public val P.description: D // Java signature: `public D getDescription(P)`
|
||||
|
||||
/**
|
||||
* 加载一个插件 (实例), 但不 [启用][enable] 它. 返回加载成功的主类实例
|
||||
@ -50,6 +50,7 @@ public interface PluginLoader<P : Plugin, D : PluginDescription> {
|
||||
public fun disable(plugin: P)
|
||||
}
|
||||
|
||||
@JvmSynthetic
|
||||
public inline fun <D : PluginDescription, P : Plugin> PluginLoader<in P, out D>.getDescription(plugin: P): D =
|
||||
plugin.description
|
||||
|
||||
|
@ -11,30 +11,22 @@
|
||||
|
||||
package net.mamoe.mirai.console.plugin
|
||||
|
||||
import kotlinx.atomicfu.locks.withLock
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.setting.internal.cast
|
||||
import net.mamoe.mirai.utils.info
|
||||
import net.mamoe.mirai.console.plugin.jvm.PluginManagerImpl
|
||||
import java.io.File
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
// TODO: 2020/7/11 top-level or in PluginManager.companion?
|
||||
public val Plugin.description: PluginDescription
|
||||
get() = PluginManagerImpl.resolvedPlugins.firstOrNull { it == this }
|
||||
?.loader?.cast<PluginLoader<Plugin, PluginDescription>>()
|
||||
?.getDescription(this)
|
||||
?: error("Plugin is unloaded")
|
||||
|
||||
@JvmSynthetic
|
||||
public inline fun PluginLoader<*, *>.register(): Boolean = PluginManager.registerPluginLoader(this)
|
||||
|
||||
@JvmSynthetic
|
||||
public inline fun PluginLoader<*, *>.unregister(): Boolean = PluginManager.unregisterPluginLoader(this)
|
||||
|
||||
// TODO: 2020/7/11 document
|
||||
/**
|
||||
* 插件管理器.
|
||||
*/
|
||||
public interface PluginManager {
|
||||
/**
|
||||
* `$rootDir/plugins`
|
||||
*/
|
||||
public val pluginsDir: File
|
||||
|
||||
/**
|
||||
* `$rootDir/data`
|
||||
*/
|
||||
public val pluginsDataFolder: File
|
||||
|
||||
/**
|
||||
@ -43,7 +35,9 @@ public interface PluginManager {
|
||||
public val plugins: List<Plugin>
|
||||
|
||||
/**
|
||||
* 内建的插件加载器列表. 由 [MiraiConsole] 初始化
|
||||
* 内建的插件加载器列表. 由 [MiraiConsole] 初始化.
|
||||
*
|
||||
* @return 不可变的 list.
|
||||
*/
|
||||
public val builtInLoaders: List<PluginLoader<*, *>>
|
||||
|
||||
@ -56,9 +50,20 @@ public interface PluginManager {
|
||||
|
||||
public fun unregisterPluginLoader(loader: PluginLoader<*, *>): Boolean
|
||||
|
||||
/**
|
||||
* 获取插件的 [描述][PluginDescription], 通过 [PluginLoader.getDescription]
|
||||
*/
|
||||
public val Plugin.description: PluginDescription
|
||||
|
||||
public companion object INSTANCE : PluginManager by PluginManagerImpl
|
||||
}
|
||||
|
||||
@JvmSynthetic
|
||||
public inline fun PluginLoader<*, *>.register(): Boolean = PluginManager.registerPluginLoader(this)
|
||||
|
||||
@JvmSynthetic
|
||||
public inline fun PluginLoader<*, *>.unregister(): Boolean = PluginManager.unregisterPluginLoader(this)
|
||||
|
||||
public class PluginMissingDependencyException : PluginResolutionException {
|
||||
public constructor() : super()
|
||||
public constructor(message: String?) : super(message)
|
||||
@ -71,175 +76,4 @@ public open class PluginResolutionException : Exception {
|
||||
public constructor(message: String?) : super(message)
|
||||
public constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||
public constructor(cause: Throwable?) : super(cause)
|
||||
}
|
||||
|
||||
|
||||
// internal
|
||||
|
||||
|
||||
internal object PluginManagerImpl : PluginManager {
|
||||
override val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() }
|
||||
override val pluginsDataFolder = File(MiraiConsole.rootDir, "data").apply { mkdir() }
|
||||
|
||||
@Suppress("ObjectPropertyName")
|
||||
private val _pluginLoaders: MutableList<PluginLoader<*, *>> = mutableListOf()
|
||||
private val loadersLock: ReentrantLock = ReentrantLock()
|
||||
private val logger = MiraiConsole.newLogger("PluginManager")
|
||||
|
||||
@JvmField
|
||||
internal val resolvedPlugins: MutableList<Plugin> = mutableListOf()
|
||||
override val plugins: List<Plugin>
|
||||
get() = resolvedPlugins.toList()
|
||||
override val builtInLoaders: List<PluginLoader<*, *>>
|
||||
get() = MiraiConsole.builtInPluginLoaders
|
||||
override val pluginLoaders: List<PluginLoader<*, *>>
|
||||
get() = _pluginLoaders.toList()
|
||||
|
||||
override fun registerPluginLoader(loader: PluginLoader<*, *>): Boolean = loadersLock.withLock {
|
||||
if (_pluginLoaders.any { it::class == loader }) {
|
||||
return false
|
||||
}
|
||||
_pluginLoaders.add(loader)
|
||||
}
|
||||
|
||||
override fun unregisterPluginLoader(loader: PluginLoader<*, *>) = loadersLock.withLock {
|
||||
_pluginLoaders.remove(loader)
|
||||
}
|
||||
|
||||
|
||||
// region LOADING
|
||||
|
||||
private fun <P : Plugin, D : PluginDescription> PluginLoader<P, D>.loadPluginNoEnable(description: D): P {
|
||||
return kotlin.runCatching {
|
||||
this.load(description).also { resolvedPlugins.add(it) }
|
||||
}.fold(
|
||||
onSuccess = {
|
||||
logger.info { "Successfully loaded plugin ${description.name}" }
|
||||
it
|
||||
},
|
||||
onFailure = {
|
||||
logger.info { "Cannot load plugin ${description.name}" }
|
||||
throw it
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun <P : Plugin, D : PluginDescription> PluginLoader<P, D>.enablePlugin(plugin: Plugin) {
|
||||
kotlin.runCatching {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
this.enable(plugin as P)
|
||||
}.fold(
|
||||
onSuccess = {
|
||||
logger.info { "Successfully enabled plugin ${plugin.description.name}" }
|
||||
},
|
||||
onFailure = {
|
||||
logger.info { "Cannot enable plugin ${plugin.description.name}" }
|
||||
throw it
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* STEPS:
|
||||
* 1. 遍历插件列表, 使用 [builtInLoaders] 加载 [PluginKind.LOADER] 类型的插件
|
||||
* 2. [启动][PluginLoader.enable] 所有 [PluginKind.LOADER] 的插件
|
||||
* 3. 使用内建和所有插件提供的 [PluginLoader] 加载全部除 [PluginKind.LOADER] 外的插件列表.
|
||||
* 4. 解决依赖并排序
|
||||
* 5. 依次 [PluginLoader.load]
|
||||
* 但不 [PluginLoader.enable]
|
||||
*
|
||||
* @return [builtInLoaders] 可以加载的插件. 已经完成了 [PluginLoader.load], 但没有 [PluginLoader.enable]
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Throws(PluginMissingDependencyException::class)
|
||||
internal fun loadEnablePlugins() {
|
||||
(loadAndEnableLoaderProviders() + _pluginLoaders.listAllPlugins().flatMap { it.second })
|
||||
.sortByDependencies().loadAndEnableAllInOrder()
|
||||
}
|
||||
|
||||
private fun List<PluginDescriptionWithLoader>.loadAndEnableAllInOrder() {
|
||||
return this.map { (loader, desc) ->
|
||||
loader to loader.loadPluginNoEnable(desc)
|
||||
}.forEach { (loader, plugin) ->
|
||||
loader.enablePlugin(plugin)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return [builtInLoaders] 可以加载的插件. 已经完成了 [PluginLoader.load], 但没有 [PluginLoader.enable]
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Throws(PluginMissingDependencyException::class)
|
||||
private fun loadAndEnableLoaderProviders(): List<PluginDescriptionWithLoader> {
|
||||
val allDescriptions =
|
||||
this.builtInLoaders.listAllPlugins()
|
||||
.asSequence()
|
||||
.onEach { (loader, descriptions) ->
|
||||
loader as PluginLoader<Plugin, PluginDescription>
|
||||
|
||||
descriptions.filter { it.kind == PluginKind.LOADER }.sortByDependencies().loadAndEnableAllInOrder()
|
||||
}
|
||||
.flatMap { it.second.asSequence() }
|
||||
|
||||
return allDescriptions.toList()
|
||||
}
|
||||
|
||||
private fun List<PluginLoader<*, *>>.listAllPlugins(): List<Pair<PluginLoader<*, *>, List<PluginDescriptionWithLoader>>> {
|
||||
return associateWith { loader -> loader.listPlugins().map { desc -> desc.wrapWith(loader) } }.toList()
|
||||
}
|
||||
|
||||
@Throws(PluginMissingDependencyException::class)
|
||||
private fun <D : PluginDescription> List<D>.sortByDependencies(): List<D> {
|
||||
val resolved = ArrayList<D>(this.size)
|
||||
|
||||
fun D.canBeLoad(): Boolean = this.dependencies.all { it.isOptional || it in resolved }
|
||||
|
||||
fun List<D>.consumeLoadable(): List<D> {
|
||||
val (canBeLoad, cannotBeLoad) = this.partition { it.canBeLoad() }
|
||||
resolved.addAll(canBeLoad)
|
||||
return cannotBeLoad
|
||||
}
|
||||
|
||||
fun List<PluginDependency>.filterIsMissing(): List<PluginDependency> =
|
||||
this.filterNot { it.isOptional || it in resolved }
|
||||
|
||||
tailrec fun List<D>.doSort() {
|
||||
if (this.isEmpty()) return
|
||||
|
||||
val beforeSize = this.size
|
||||
this.consumeLoadable().also { resultPlugins ->
|
||||
check(resultPlugins.size < beforeSize) {
|
||||
throw PluginMissingDependencyException(resultPlugins.joinToString("\n") { badPlugin ->
|
||||
"Cannot load plugin ${badPlugin.name}, missing dependencies: ${
|
||||
badPlugin.dependencies.filterIsMissing()
|
||||
.joinToString()
|
||||
}"
|
||||
})
|
||||
}
|
||||
}.doSort()
|
||||
}
|
||||
|
||||
this.doSort()
|
||||
return resolved
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
internal data class PluginDescriptionWithLoader(
|
||||
@JvmField val loader: PluginLoader<*, PluginDescription>, // easier type
|
||||
@JvmField val delegate: PluginDescription
|
||||
) : PluginDescription by delegate
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun <D : PluginDescription> PluginDescription.unwrap(): D =
|
||||
if (this is PluginDescriptionWithLoader) this.delegate as D else this as D
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun PluginDescription.wrapWith(loader: PluginLoader<*, *>): PluginDescriptionWithLoader =
|
||||
PluginDescriptionWithLoader(
|
||||
loader as PluginLoader<*, PluginDescription>, this
|
||||
)
|
||||
|
||||
internal operator fun List<PluginDescription>.contains(dependency: PluginDependency): Boolean =
|
||||
any { it.name == dependency.name }
|
||||
}
|
@ -20,6 +20,21 @@ import net.mamoe.yamlkt.YamlDynamicSerializer
|
||||
import java.io.File
|
||||
|
||||
|
||||
/**
|
||||
* 插件描述
|
||||
*/
|
||||
public interface PluginDescription {
|
||||
public val kind: PluginKind
|
||||
|
||||
public val name: String
|
||||
public val author: String
|
||||
public val version: Semver
|
||||
public val info: String
|
||||
|
||||
/** 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件 */
|
||||
public val dependencies: List<@Serializable(with = PluginDependency.SmartSerializer::class) PluginDependency>
|
||||
}
|
||||
|
||||
/** 插件类型 */
|
||||
@Serializable(with = PluginKind.AsStringSerializer::class)
|
||||
public enum class PluginKind {
|
||||
@ -39,21 +54,6 @@ public enum class PluginKind {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件描述
|
||||
*/
|
||||
public interface PluginDescription {
|
||||
public val kind: PluginKind
|
||||
|
||||
public val name: String
|
||||
public val author: String
|
||||
public val version: Semver
|
||||
public val info: String
|
||||
|
||||
/** 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件 */
|
||||
public val dependencies: List<@Serializable(with = PluginDependency.SmartSerializer::class) PluginDependency>
|
||||
}
|
||||
|
||||
/** 插件的一个依赖的信息 */
|
||||
@Serializable
|
||||
public data class PluginDependency(
|
||||
|
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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:Suppress("NOTHING_TO_INLINE", "unused")
|
||||
|
||||
package net.mamoe.mirai.console.plugin.jvm
|
||||
|
||||
import kotlinx.atomicfu.locks.withLock
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.plugin.*
|
||||
import net.mamoe.mirai.console.setting.internal.cast
|
||||
import net.mamoe.mirai.utils.info
|
||||
import java.io.File
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
internal object PluginManagerImpl : PluginManager {
|
||||
override val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() }
|
||||
override val pluginsDataFolder = File(MiraiConsole.rootDir, "data").apply { mkdir() }
|
||||
|
||||
@Suppress("ObjectPropertyName")
|
||||
private val _pluginLoaders: MutableList<PluginLoader<*, *>> = mutableListOf()
|
||||
private val loadersLock: ReentrantLock = ReentrantLock()
|
||||
private val logger = MiraiConsole.newLogger("PluginManager")
|
||||
|
||||
@JvmField
|
||||
internal val resolvedPlugins: MutableList<Plugin> = mutableListOf()
|
||||
override val plugins: List<Plugin>
|
||||
get() = resolvedPlugins.toList()
|
||||
override val builtInLoaders: List<PluginLoader<*, *>>
|
||||
get() = MiraiConsole.builtInPluginLoaders
|
||||
override val pluginLoaders: List<PluginLoader<*, *>>
|
||||
get() = _pluginLoaders.toList()
|
||||
|
||||
override val Plugin.description: PluginDescription
|
||||
get() = resolvedPlugins.firstOrNull { it == this }
|
||||
?.loader?.cast<PluginLoader<Plugin, PluginDescription>>()
|
||||
?.getDescription(this)
|
||||
?: error("Plugin is unloaded")
|
||||
|
||||
override fun registerPluginLoader(loader: PluginLoader<*, *>): Boolean = loadersLock.withLock {
|
||||
if (_pluginLoaders.any { it::class == loader }) {
|
||||
return false
|
||||
}
|
||||
_pluginLoaders.add(loader)
|
||||
}
|
||||
|
||||
override fun unregisterPluginLoader(loader: PluginLoader<*, *>) = loadersLock.withLock {
|
||||
_pluginLoaders.remove(loader)
|
||||
}
|
||||
|
||||
|
||||
// region LOADING
|
||||
|
||||
private fun <P : Plugin, D : PluginDescription> PluginLoader<P, D>.loadPluginNoEnable(description: D): P {
|
||||
return kotlin.runCatching {
|
||||
this.load(description).also { resolvedPlugins.add(it) }
|
||||
}.fold(
|
||||
onSuccess = {
|
||||
logger.info { "Successfully loaded plugin ${description.name}" }
|
||||
it
|
||||
},
|
||||
onFailure = {
|
||||
logger.info { "Cannot load plugin ${description.name}" }
|
||||
throw it
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun <P : Plugin, D : PluginDescription> PluginLoader<P, D>.enablePlugin(plugin: Plugin) {
|
||||
kotlin.runCatching {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
this.enable(plugin as P)
|
||||
}.fold(
|
||||
onSuccess = {
|
||||
logger.info { "Successfully enabled plugin ${plugin.description.name}" }
|
||||
},
|
||||
onFailure = {
|
||||
logger.info { "Cannot enable plugin ${plugin.description.name}" }
|
||||
throw it
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* STEPS:
|
||||
* 1. 遍历插件列表, 使用 [builtInLoaders] 加载 [PluginKind.LOADER] 类型的插件
|
||||
* 2. [启动][PluginLoader.enable] 所有 [PluginKind.LOADER] 的插件
|
||||
* 3. 使用内建和所有插件提供的 [PluginLoader] 加载全部除 [PluginKind.LOADER] 外的插件列表.
|
||||
* 4. 解决依赖并排序
|
||||
* 5. 依次 [PluginLoader.load]
|
||||
* 但不 [PluginLoader.enable]
|
||||
*
|
||||
* @return [builtInLoaders] 可以加载的插件. 已经完成了 [PluginLoader.load], 但没有 [PluginLoader.enable]
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Throws(PluginMissingDependencyException::class)
|
||||
internal fun loadEnablePlugins() {
|
||||
(loadAndEnableLoaderProviders() + _pluginLoaders.listAllPlugins().flatMap { it.second })
|
||||
.sortByDependencies().loadAndEnableAllInOrder()
|
||||
}
|
||||
|
||||
private fun List<PluginDescriptionWithLoader>.loadAndEnableAllInOrder() {
|
||||
return this.map { (loader, desc) ->
|
||||
loader to loader.loadPluginNoEnable(desc)
|
||||
}.forEach { (loader, plugin) ->
|
||||
loader.enablePlugin(plugin)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return [builtInLoaders] 可以加载的插件. 已经完成了 [PluginLoader.load], 但没有 [PluginLoader.enable]
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Throws(PluginMissingDependencyException::class)
|
||||
private fun loadAndEnableLoaderProviders(): List<PluginDescriptionWithLoader> {
|
||||
val allDescriptions =
|
||||
this.builtInLoaders.listAllPlugins()
|
||||
.asSequence()
|
||||
.onEach { (loader, descriptions) ->
|
||||
loader as PluginLoader<Plugin, PluginDescription>
|
||||
|
||||
descriptions.filter { it.kind == PluginKind.LOADER }.sortByDependencies().loadAndEnableAllInOrder()
|
||||
}
|
||||
.flatMap { it.second.asSequence() }
|
||||
|
||||
return allDescriptions.toList()
|
||||
}
|
||||
|
||||
private fun List<PluginLoader<*, *>>.listAllPlugins(): List<Pair<PluginLoader<*, *>, List<PluginDescriptionWithLoader>>> {
|
||||
return associateWith { loader -> loader.listPlugins().map { desc -> desc.wrapWith(loader) } }.toList()
|
||||
}
|
||||
|
||||
@Throws(PluginMissingDependencyException::class)
|
||||
private fun <D : PluginDescription> List<D>.sortByDependencies(): List<D> {
|
||||
val resolved = ArrayList<D>(this.size)
|
||||
|
||||
fun D.canBeLoad(): Boolean = this.dependencies.all { it.isOptional || it in resolved }
|
||||
|
||||
fun List<D>.consumeLoadable(): List<D> {
|
||||
val (canBeLoad, cannotBeLoad) = this.partition { it.canBeLoad() }
|
||||
resolved.addAll(canBeLoad)
|
||||
return cannotBeLoad
|
||||
}
|
||||
|
||||
fun List<PluginDependency>.filterIsMissing(): List<PluginDependency> =
|
||||
this.filterNot { it.isOptional || it in resolved }
|
||||
|
||||
tailrec fun List<D>.doSort() {
|
||||
if (this.isEmpty()) return
|
||||
|
||||
val beforeSize = this.size
|
||||
this.consumeLoadable().also { resultPlugins ->
|
||||
check(resultPlugins.size < beforeSize) {
|
||||
throw PluginMissingDependencyException(resultPlugins.joinToString("\n") { badPlugin ->
|
||||
"Cannot load plugin ${badPlugin.name}, missing dependencies: ${
|
||||
badPlugin.dependencies.filterIsMissing()
|
||||
.joinToString()
|
||||
}"
|
||||
})
|
||||
}
|
||||
}.doSort()
|
||||
}
|
||||
|
||||
this.doSort()
|
||||
return resolved
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
internal data class PluginDescriptionWithLoader(
|
||||
@JvmField val loader: PluginLoader<*, PluginDescription>, // easier type
|
||||
@JvmField val delegate: PluginDescription
|
||||
) : PluginDescription by delegate
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun <D : PluginDescription> PluginDescription.unwrap(): D =
|
||||
if (this is PluginDescriptionWithLoader) this.delegate as D else this as D
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun PluginDescription.wrapWith(loader: PluginLoader<*, *>): PluginDescriptionWithLoader =
|
||||
PluginDescriptionWithLoader(
|
||||
loader as PluginLoader<*, PluginDescription>, this
|
||||
)
|
||||
|
||||
internal operator fun List<PluginDescription>.contains(dependency: PluginDependency): Boolean =
|
||||
any { it.name == dependency.name }
|
@ -88,7 +88,7 @@ public abstract class AbstractSetting : Setting, SettingImpl() {
|
||||
*
|
||||
* @see JvmPlugin.loadSetting 通过 [JvmPlugin] 获取指定 [Setting] 实例.
|
||||
*/
|
||||
public interface Setting {
|
||||
public interface Setting : ExperimentalSettingExtensions {
|
||||
/**
|
||||
* 使用 `by` 时自动调用此方法, 添加对 [Value] 的值修改的跟踪.
|
||||
*/
|
||||
@ -113,6 +113,21 @@ public interface Setting {
|
||||
public fun setStorage(storage: SettingStorage)
|
||||
}
|
||||
|
||||
@ConsoleExperimentalAPI("")
|
||||
public interface ExperimentalSettingExtensions {
|
||||
public fun <E, V, K> MutableMap<E, V>.shadowMap(
|
||||
eToK: (E) -> K,
|
||||
kToE: (K) -> E
|
||||
): MutableMap<K, V> {
|
||||
return this.shadowMap(
|
||||
kTransform = eToK,
|
||||
kTransformBack = kToE,
|
||||
vTransform = { it },
|
||||
vTransformBack = { it }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//// region Setting_value_primitives CODEGEN ////
|
||||
|
||||
public fun Setting.value(default: Byte): SerializerAwareValue<Byte> = valueImpl(default)
|
||||
|
@ -41,7 +41,7 @@ internal fun Bot.addManager(id: Long): Boolean {
|
||||
}
|
||||
|
||||
|
||||
internal object ManagersConfig : Setting by (ConsoleBuiltInSettingStorage.load(ConsoleBuiltInSettingHolder)) {
|
||||
internal object ManagersConfig : Setting by ConsoleBuiltInSettingStorage.load() {
|
||||
private val managers: MutableMap<Long, MutableSet<Long>> by value()
|
||||
|
||||
internal operator fun get(bot: Bot): MutableSet<Long> = managers.getOrPut(bot.id, ::mutableSetOf)
|
||||
@ -54,10 +54,12 @@ internal fun CoroutineScope.childScope(context: CoroutineContext = EmptyCoroutin
|
||||
|
||||
internal object ConsoleBuiltInSettingHolder : AutoSaveSettingHolder,
|
||||
CoroutineScope by MiraiConsole.childScope() {
|
||||
override val autoSaveIntervalMillis: LongRange
|
||||
get() = 30.minutesToMillis..60.minutesToMillis
|
||||
override val autoSaveIntervalMillis: LongRange = 30.minutesToMillis..60.minutesToMillis
|
||||
override val name: String get() = "ConsoleBuiltIns"
|
||||
}
|
||||
|
||||
internal object ConsoleBuiltInSettingStorage :
|
||||
SettingStorage by MiraiConsoleImplementationBridge.settingStorageForJarPluginLoader
|
||||
SettingStorage by MiraiConsoleImplementationBridge.settingStorageForJarPluginLoader {
|
||||
|
||||
inline fun <reified T : Setting> load(): T = load(ConsoleBuiltInSettingHolder)
|
||||
}
|
@ -42,21 +42,21 @@ public interface ResourceContainer {
|
||||
*/
|
||||
@JvmDefault
|
||||
public fun getResource(name: String, charset: Charset): String =
|
||||
this.getResourceAsStream(name).use { it.readBytes() }.encodeToString()
|
||||
this.getResourceAsStream(name).use { it.readBytes() }.encodeToString(charset)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* 使用 [Class.getResourceAsStream] 读取资源文件
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmName("byClass")
|
||||
@JvmName("create")
|
||||
public fun KClass<*>.asResourceContainer(): ResourceContainer = this.java.asResourceContainer()
|
||||
|
||||
/**
|
||||
* 使用 [Class.getResourceAsStream] 读取资源文件
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmName("byClass")
|
||||
@JvmName("create")
|
||||
public fun Class<*>.asResourceContainer(): ResourceContainer = ClassAsResourceContainer(this)
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import net.mamoe.mirai.console.setting.SettingStorage
|
||||
import net.mamoe.mirai.console.utils.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* mirai-console-pure 后端实现
|
||||
@ -47,7 +48,10 @@ import java.io.File
|
||||
class MiraiConsoleImplementationPure
|
||||
@JvmOverloads constructor(
|
||||
override val rootDir: File = File("."),
|
||||
override val builtInPluginLoaders: List<PluginLoader<*, *>> = listOf(DeferredPluginLoader { JarPluginLoader }),
|
||||
override val builtInPluginLoaders: List<PluginLoader<*, *>> = Collections.unmodifiableList(
|
||||
listOf(
|
||||
DeferredPluginLoader { JarPluginLoader })
|
||||
),
|
||||
override val frontEnd: MiraiConsoleFrontEnd = MiraiConsoleFrontEndPure,
|
||||
override val mainLogger: MiraiLogger = frontEnd.loggerFor("main"),
|
||||
override val consoleCommandSender: ConsoleCommandSender = ConsoleCommandSenderImpl,
|
||||
|
Loading…
Reference in New Issue
Block a user