This commit is contained in:
Him188 2020-05-23 18:53:54 +08:00
parent 11bdb438eb
commit c3120cf1ac
6 changed files with 86 additions and 141 deletions

View File

@ -61,6 +61,7 @@ dependencies {
api("net.mamoe.yamlkt:yamlkt:0.3.1")
api("org.jetbrains:annotations:19.0.0")
api(kotlinx("coroutines-jdk8", Versions.Kotlin.coroutines))
testApi("net.mamoe:mirai-core-qqandroid:${Versions.Mirai.core}")
testApi(kotlin("stdlib-jdk8"))

View File

@ -13,8 +13,8 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.io.charsets.Charset
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.plugins.JarPluginLoader
import net.mamoe.mirai.console.plugins.PluginLoader
import net.mamoe.mirai.console.plugins.builtin.JarPluginLoader
import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiLogger
@ -23,6 +23,38 @@ import java.io.File
import java.io.PrintStream
import kotlin.coroutines.CoroutineContext
/**
* mirai 控制台实例.
*/
object MiraiConsole : CoroutineScope, IMiraiConsole {
private lateinit var instance: IMiraiConsole
/** 由前端调用 */
internal fun init(instance: IMiraiConsole) {
this.instance = instance
}
override val build: String get() = instance.build
override val version: String get() = instance.version
override val rootDir: File get() = instance.rootDir
override val frontEnd: MiraiConsoleFrontEnd get() = instance.frontEnd
override val mainLogger: MiraiLogger get() = instance.mainLogger
override val coroutineContext: CoroutineContext get() = instance.coroutineContext
override val builtInPluginLoaders: List<PluginLoader<*, *>> = instance.builtInPluginLoaders
init {
DefaultLogger = { identity -> this.newLogger(identity) }
this.coroutineContext[Job]!!.invokeOnCompletion {
Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) }
}
}
@MiraiExperimentalAPI
fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity)
}
// 前端使用
internal interface IMiraiConsole : CoroutineScope {
val build: String
@ -49,36 +81,6 @@ internal interface IMiraiConsole : CoroutineScope {
val builtInPluginLoaders: List<PluginLoader<*, *>>
}
object MiraiConsole : CoroutineScope, IMiraiConsole {
private lateinit var instance: IMiraiConsole
/** 由前端调用 */
internal fun init(instance: IMiraiConsole) {
this.instance = instance
}
override val build: String get() = instance.build
override val version: String get() = instance.version
override val rootDir: File get() = instance.rootDir
override val frontEnd: MiraiConsoleFrontEnd get() = instance.frontEnd
override val mainLogger: MiraiLogger get() = instance.mainLogger
override val coroutineContext: CoroutineContext get() = instance.coroutineContext
override val builtInPluginLoaders: List<PluginLoader<*, *>> = instance.builtInPluginLoaders
init {
DefaultLogger = { identity ->
this.newLogger(identity)
}
this.coroutineContext[Job]!!.invokeOnCompletion {
Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) }
}
}
@MiraiExperimentalAPI
fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity)
}
/**
* Included in kotlin stdlib 1.4
*/

View File

@ -9,82 +9,17 @@
package net.mamoe.mirai.console.plugins
import kotlinx.serialization.Serializable
import java.io.File
/** 插件类型 */
enum class PluginKind {
/** 表示此插件提供一个 [PluginLoader], 应在加载其他 [NORMAL] 类型插件前加载 */
LOADER,
/** 表示此插件为一个通常的插件, 按照正常的依赖关系加载. */
NORMAL
}
/**
* 插件描述
*/
interface PluginDescription {
val kind: PluginKind
val name: String
val author: String
val version: String
val info: String
/** 指定此插件需要在这些插件之前加载 */
val loadBefore: List<String>
/** 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件 */
val dependencies: List<PluginDependency>
}
/** 插件的一个依赖的信息 */
@Serializable
data class PluginDependency(
/** 依赖插件名 */
val name: String,
/**
* 依赖版本号
* @see versionKind 版本号类型
*/
val version: String,
/** 版本号类型 */
val versionKind: VersionKind
) {
enum class VersionKind {
/** 要求依赖精确的版本 */
EXACT,
/** 要求依赖最低版本 */
AT_LEAST,
/** 要求依赖最高版本 */
AT_MOST
}
override fun toString(): String {
return "$name ${versionKind.toEnglishString()}v$version"
}
}
internal fun PluginDependency.VersionKind.toEnglishString(): String = when (this) {
PluginDependency.VersionKind.EXACT -> ""
PluginDependency.VersionKind.AT_LEAST -> "at least "
PluginDependency.VersionKind.AT_MOST -> "at most "
}
/**
* 基于文件的插件的描述
*/
interface FilePluginDescription : PluginDescription {
val file: File
}
import net.mamoe.mirai.console.plugins.builtin.JvmPlugin
/**
* 表示一个 mirai-console 插件.
*
* @see JvmPlugin
* @see PluginDescription 插件描述
*/
interface Plugin
interface Plugin {
/**
* 所属插件加载器实例
*/
val loader: PluginLoader<*, *>
}

View File

@ -2,7 +2,6 @@
package net.mamoe.mirai.console.plugins
import net.mamoe.mirai.console.MiraiConsole
import java.io.File
/**

View File

@ -5,12 +5,10 @@ package net.mamoe.mirai.console.plugins
import kotlinx.atomicfu.locks.withLock
import net.mamoe.mirai.console.MiraiConsole
import java.io.File
import java.util.*
import java.util.concurrent.locks.ReentrantLock
import kotlin.collections.ArrayList
val Plugin.description: PluginDescription get() = TODO()
val <P : Plugin> P.loader: PluginLoader<P, *> get() = TODO()
val Plugin.description: PluginDescription
get() = PluginManager.resolvedPlugins.firstOrNull { it == this }?.description ?: error("Plugin is unloaded")
inline fun PluginLoader<*, *>.register() = PluginManager.registerPluginLoader(this)
inline fun PluginLoader<*, *>.unregister() = PluginManager.unregisterPluginLoader(this)
@ -18,19 +16,18 @@ inline fun PluginLoader<*, *>.unregister() = PluginManager.unregisterPluginLoade
object PluginManager {
val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() }
class LoaderNode<P : Plugin, D : PluginDescription>(
val loader: PluginLoader<P, D>,
val loadedPlugins: MutableList<P> = mutableListOf()
)
private val _pluginLoaders: MutableSet<LoaderNode<*, *>> = mutableSetOf()
private val _pluginLoaders: MutableList<PluginLoader<*, *>> = mutableListOf()
private val loadersLock: ReentrantLock = ReentrantLock()
private val resolvedPlugins: LinkedList<Plugin> = LinkedList()
@JvmField
internal val resolvedPlugins: MutableList<Plugin> = mutableListOf()
/**
* 已加载的插件列表
*/
@JvmStatic
val plugins: List<Plugin>
get() = _pluginLoaders.flatMap { it.loadedPlugins }
get() = resolvedPlugins.toList()
/**
* 内建的插件加载器列表. [MiraiConsole] 初始化
@ -42,16 +39,21 @@ object PluginManager {
/**
* 由插件创建的 [PluginLoader]
*/
val pluginLoaders: List<PluginLoader<*, *>> get() = _pluginLoaders.map { it.loader }
@JvmStatic
val pluginLoaders: List<PluginLoader<*, *>>
get() = _pluginLoaders.toList()
@JvmStatic
fun registerPluginLoader(loader: PluginLoader<*, *>): Boolean = loadersLock.withLock {
_pluginLoaders.add(LoaderNode(loader))
if (_pluginLoaders.any { it::class == loader }) {
return false
}
_pluginLoaders.add(loader)
}
@JvmStatic
fun unregisterPluginLoader(loader: PluginLoader<*, *>) = loadersLock.withLock {
_pluginLoaders.removeAll { it.loader == loader }
_pluginLoaders.remove(loader)
}
@ -80,7 +82,7 @@ object PluginManager {
@Suppress("UNCHECKED_CAST")
@Throws(PluginMissingDependencyException::class)
internal fun loadEnablePlugins() {
val all = loadAndEnableLoaderProviders() + pluginLoaders.listAllPlugins().flatMap { it.second }
val all = loadAndEnableLoaderProviders() + _pluginLoaders.listAllPlugins().flatMap { it.second }
for ((loader, desc) in all.sortByDependencies()) {
loader.loadPluginAndEnable(desc)
@ -116,7 +118,7 @@ object PluginManager {
private fun <D : PluginDescription> List<D>.sortByDependencies(): List<D> {
val resolved = ArrayList<D>(this.size)
fun D.canBeLoad(): Boolean = this.dependencies.all { it in resolved }
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() }
@ -124,7 +126,8 @@ object PluginManager {
return cannotBeLoad
}
fun List<PluginDependency>.filterIsMissing(): List<PluginDependency> = this.filterNot { it in resolved }
fun List<PluginDependency>.filterIsMissing(): List<PluginDependency> =
this.filterNot { it.isOptional || it in resolved }
tailrec fun List<D>.doSort() {
if (this.isEmpty()) return
@ -147,6 +150,21 @@ object PluginManager {
// endregion
}
class PluginMissingDependencyException : PluginResolutionException {
constructor() : super()
constructor(message: String?) : super(message)
constructor(message: String?, cause: Throwable?) : super(message, cause)
constructor(cause: Throwable?) : super(cause)
}
open class PluginResolutionException : Exception {
constructor() : super()
constructor(message: String?) : super(message)
constructor(message: String?, cause: Throwable?) : super(message, cause)
constructor(cause: Throwable?) : super(cause)
}
internal data class PluginDescriptionWithLoader(
@JvmField val loader: PluginLoader<*, PluginDescription>, // easier type
@JvmField val delegate: PluginDescription
@ -164,17 +182,3 @@ internal fun PluginDescription.wrapWith(loader: PluginLoader<*, *>): PluginDescr
internal operator fun List<PluginDescription>.contains(dependency: PluginDependency): Boolean =
any { it.name == dependency.name }
class PluginMissingDependencyException : Exception {
constructor() : super()
constructor(message: String?) : super(message)
constructor(message: String?, cause: Throwable?) : super(message, cause)
constructor(cause: Throwable?) : super(cause)
}
open class PluginResolutionException : Exception {
constructor() : super()
constructor(message: String?) : super(message)
constructor(message: String?, cause: Throwable?) : super(message, cause)
constructor(cause: Throwable?) : super(cause)
}

View File

@ -17,6 +17,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.plugins.Plugin
import net.mamoe.mirai.console.plugins.PluginLoader
import net.mamoe.mirai.console.utils.JavaPluginScheduler
import net.mamoe.mirai.utils.MiraiLogger
import kotlin.coroutines.CoroutineContext
@ -36,6 +37,9 @@ interface JvmPlugin : Plugin, CoroutineScope {
/** 插件描述 */
val description: JvmPluginDescription
/** 所属插件加载器实例 */
override val loader: PluginLoader<*, *> get() = JarPluginLoader
@JvmDefault
fun onLoad() {
}
@ -53,8 +57,8 @@ interface JvmPlugin : Plugin, CoroutineScope {
* Java 插件的父类
*/
abstract class JavaPlugin @JvmOverloads constructor(
coroutineContext: CoroutineContext = EmptyCoroutineContext
) : JvmPlugin, JvmPluginImpl(coroutineContext) {
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
) : JvmPlugin, JvmPluginImpl(parentCoroutineContext) {
/**
* Java API Scheduler
@ -64,8 +68,8 @@ abstract class JavaPlugin @JvmOverloads constructor(
}
abstract class KotlinPlugin @JvmOverloads constructor(
coroutineContext: CoroutineContext = EmptyCoroutineContext
) : JvmPlugin, JvmPluginImpl(coroutineContext) {
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
) : JvmPlugin, JvmPluginImpl(parentCoroutineContext) {
// that's it
}