mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-25 23:50:15 +08:00
Rearrange JvmPlugin implementations
This commit is contained in:
parent
d37d6881fc
commit
11bdb438eb
@ -0,0 +1,83 @@
|
|||||||
|
package net.mamoe.mirai.console.plugins.builtin
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import net.mamoe.mirai.console.MiraiConsole
|
||||||
|
import net.mamoe.mirai.console.plugins.AbstractFilePluginLoader
|
||||||
|
import net.mamoe.mirai.console.plugins.PluginLoadException
|
||||||
|
import net.mamoe.mirai.console.plugins.PluginsLoader
|
||||||
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
import kotlin.reflect.full.createInstance
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内建的 Jar (JVM) 插件加载器
|
||||||
|
*/
|
||||||
|
object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>("jar"),
|
||||||
|
CoroutineScope {
|
||||||
|
private val logger: MiraiLogger by lazy {
|
||||||
|
MiraiConsole.newLogger(JarPluginLoader::class.simpleName!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val coroutineContext: CoroutineContext by lazy {
|
||||||
|
MiraiConsole.coroutineContext + SupervisorJob(
|
||||||
|
MiraiConsole.coroutineContext[Job]
|
||||||
|
) + CoroutineExceptionHandler { _, throwable ->
|
||||||
|
logger.error("Unhandled Jar plugin exception: ${throwable.message}", throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val classLoader: PluginsLoader =
|
||||||
|
PluginsLoader(this.javaClass.classLoader)
|
||||||
|
|
||||||
|
override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description
|
||||||
|
|
||||||
|
override fun Sequence<File>.mapToDescription(): List<JvmPluginDescription> {
|
||||||
|
TODO(
|
||||||
|
"""
|
||||||
|
CHECK IS JAR FILE AND CAN BE READ
|
||||||
|
READ JAR FILE, EXTRACT PLUGIN DESCRIPTION
|
||||||
|
SET JvmPluginDescription._file
|
||||||
|
RETURN PLUGIN
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(PluginLoadException::class)
|
||||||
|
override fun load(description: JvmPluginDescription): JvmPlugin = description.runCatching {
|
||||||
|
val main = classLoader.loadPluginMainClassByJarFile(name, mainClassName, file).kotlin.run {
|
||||||
|
objectInstance
|
||||||
|
?: kotlin.runCatching { createInstance() }.getOrNull()
|
||||||
|
?: (java.constructors + java.declaredConstructors)
|
||||||
|
.firstOrNull { it.parameterCount == 0 }
|
||||||
|
?.apply { kotlin.runCatching { isAccessible = true } }
|
||||||
|
?.newInstance()
|
||||||
|
} ?: error("No Kotlin object or public no-arg constructor found")
|
||||||
|
|
||||||
|
check(main is JvmPlugin) { "The main class of Jar plugin must extend JvmPlugin, recommending JavaPlugin or KotlinPlugin" }
|
||||||
|
|
||||||
|
if (main is JvmPluginImpl) {
|
||||||
|
main._description = description
|
||||||
|
}
|
||||||
|
|
||||||
|
TODO(
|
||||||
|
"""
|
||||||
|
FIND PLUGIN MAIN, THEN LOAD
|
||||||
|
SET JvmPluginImpl._description
|
||||||
|
SET JvmPluginImpl._intrinsicCoroutineContext
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
// no need to check dependencies
|
||||||
|
}.getOrElse {
|
||||||
|
throw PluginLoadException(
|
||||||
|
"Exception while loading ${description.name}",
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun enable(plugin: JvmPlugin) = plugin.onEnable()
|
||||||
|
override fun disable(plugin: JvmPlugin) = plugin.onDisable()
|
||||||
|
}
|
@ -9,24 +9,31 @@
|
|||||||
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS")
|
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS")
|
||||||
|
|
||||||
|
|
||||||
package net.mamoe.mirai.console.plugins
|
package net.mamoe.mirai.console.plugins.builtin
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlinx.serialization.Transient
|
|
||||||
import net.mamoe.mirai.console.MiraiConsole
|
import net.mamoe.mirai.console.MiraiConsole
|
||||||
import net.mamoe.mirai.console.scheduler.PluginScheduler
|
import net.mamoe.mirai.console.plugins.Plugin
|
||||||
|
import net.mamoe.mirai.console.utils.JavaPluginScheduler
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
import java.io.File
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 或 Kotlin Jar 插件
|
||||||
|
*
|
||||||
|
* @see JavaPlugin Java 插件
|
||||||
|
* @see KotlinPlugin Kotlin 插件
|
||||||
|
*/
|
||||||
interface JvmPlugin : Plugin, CoroutineScope {
|
interface JvmPlugin : Plugin, CoroutineScope {
|
||||||
|
/** 日志 */
|
||||||
val logger: MiraiLogger
|
val logger: MiraiLogger
|
||||||
|
|
||||||
|
/** 插件描述 */
|
||||||
val description: JvmPluginDescription
|
val description: JvmPluginDescription
|
||||||
|
|
||||||
@JvmDefault
|
@JvmDefault
|
||||||
@ -42,7 +49,9 @@ interface JvmPlugin : Plugin, CoroutineScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 插件的父类
|
||||||
|
*/
|
||||||
abstract class JavaPlugin @JvmOverloads constructor(
|
abstract class JavaPlugin @JvmOverloads constructor(
|
||||||
coroutineContext: CoroutineContext = EmptyCoroutineContext
|
coroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||||
) : JvmPlugin, JvmPluginImpl(coroutineContext) {
|
) : JvmPlugin, JvmPluginImpl(coroutineContext) {
|
||||||
@ -50,7 +59,8 @@ abstract class JavaPlugin @JvmOverloads constructor(
|
|||||||
/**
|
/**
|
||||||
* Java API Scheduler
|
* Java API Scheduler
|
||||||
*/
|
*/
|
||||||
val scheduler: PluginScheduler? = PluginScheduler(this.coroutineContext)
|
val scheduler: JavaPluginScheduler =
|
||||||
|
JavaPluginScheduler(this.coroutineContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class KotlinPlugin @JvmOverloads constructor(
|
abstract class KotlinPlugin @JvmOverloads constructor(
|
||||||
@ -59,41 +69,6 @@ abstract class KotlinPlugin @JvmOverloads constructor(
|
|||||||
// that's it
|
// that's it
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class JvmPluginDescription internal constructor( // serializer 可以用这个构造器
|
|
||||||
override val kind: PluginKind,
|
|
||||||
override val name: String,
|
|
||||||
override val author: String,
|
|
||||||
override val version: String,
|
|
||||||
override val info: String,
|
|
||||||
override val loadBefore: List<String>,
|
|
||||||
override val dependencies: List<PluginDependency>
|
|
||||||
) : PluginDescription, FilePluginDescription {
|
|
||||||
/**
|
|
||||||
* 在手动实现时使用这个构造器.
|
|
||||||
*/
|
|
||||||
@Suppress("unused")
|
|
||||||
constructor(
|
|
||||||
kind: PluginKind,
|
|
||||||
name: String,
|
|
||||||
author: String,
|
|
||||||
version: String,
|
|
||||||
info: String,
|
|
||||||
loadBefore: List<String>,
|
|
||||||
depends: List<PluginDependency>,
|
|
||||||
file: File
|
|
||||||
) : this(kind, name, author, version, info, loadBefore, depends) {
|
|
||||||
this._file = file
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("PropertyName")
|
|
||||||
@Transient
|
|
||||||
internal var _file: File? = null
|
|
||||||
|
|
||||||
override val file: File
|
|
||||||
get() = _file ?: error("Internal error: JvmPluginDescription(name=$name)._file == null")
|
|
||||||
}
|
|
||||||
|
|
||||||
internal abstract class JvmPluginImpl(
|
internal abstract class JvmPluginImpl(
|
||||||
parentCoroutineContext: CoroutineContext
|
parentCoroutineContext: CoroutineContext
|
||||||
) : JvmPlugin, CoroutineScope {
|
) : JvmPlugin, CoroutineScope {
|
||||||
@ -102,6 +77,11 @@ internal abstract class JvmPluginImpl(
|
|||||||
*/
|
*/
|
||||||
@Suppress("PropertyName")
|
@Suppress("PropertyName")
|
||||||
internal lateinit var _description: JvmPluginDescription
|
internal lateinit var _description: JvmPluginDescription
|
||||||
|
|
||||||
|
// for future use
|
||||||
|
@Suppress("PropertyName")
|
||||||
|
internal var _intrinsicCoroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||||
|
|
||||||
override val description: JvmPluginDescription get() = _description
|
override val description: JvmPluginDescription get() = _description
|
||||||
|
|
||||||
final override val logger: MiraiLogger by lazy { MiraiConsole.newLogger(this._description.name) }
|
final override val logger: MiraiLogger by lazy { MiraiConsole.newLogger(this._description.name) }
|
||||||
@ -109,37 +89,10 @@ internal abstract class JvmPluginImpl(
|
|||||||
final override val coroutineContext: CoroutineContext by lazy {
|
final override val coroutineContext: CoroutineContext by lazy {
|
||||||
CoroutineExceptionHandler { _, throwable -> logger.error(throwable) }
|
CoroutineExceptionHandler { _, throwable -> logger.error(throwable) }
|
||||||
.plus(parentCoroutineContext)
|
.plus(parentCoroutineContext)
|
||||||
.plus(SupervisorJob(parentCoroutineContext[Job]))
|
.plus(SupervisorJob(parentCoroutineContext[Job])) + _intrinsicCoroutineContext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 内建的 Jar (JVM) 插件加载器
|
|
||||||
*/
|
|
||||||
object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>("jar") {
|
|
||||||
override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description
|
|
||||||
|
|
||||||
override fun Sequence<File>.mapToDescription(): List<JvmPluginDescription> {
|
|
||||||
TODO(
|
|
||||||
"""
|
|
||||||
CHECK IS JAR FILE AND CAN BE READ
|
|
||||||
READ JAR FILE, EXTRACT PLUGIN DESCRIPTION
|
|
||||||
SET JvmPluginDescription._file
|
|
||||||
RETURN PLUGIN
|
|
||||||
""".trimIndent()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(PluginLoadException::class)
|
|
||||||
override fun load(description: JvmPluginDescription): JvmPlugin {
|
|
||||||
TODO("FIND PLUGIN MAIN, THEN LOAD")
|
|
||||||
// no need to check dependencies
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun enable(plugin: JvmPlugin) = plugin.onEnable()
|
|
||||||
override fun disable(plugin: JvmPlugin) = plugin.onDisable()
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
object PluginManagerOld {
|
object PluginManagerOld {
|
||||||
/**
|
/**
|
@ -0,0 +1,48 @@
|
|||||||
|
package net.mamoe.mirai.console.plugins.builtin
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.Transient
|
||||||
|
import net.mamoe.mirai.console.plugins.FilePluginDescription
|
||||||
|
import net.mamoe.mirai.console.plugins.PluginDependency
|
||||||
|
import net.mamoe.mirai.console.plugins.PluginDescription
|
||||||
|
import net.mamoe.mirai.console.plugins.PluginKind
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class JvmPluginDescription internal constructor(
|
||||||
|
override val kind: PluginKind,
|
||||||
|
override val name: String,
|
||||||
|
@SerialName("main")
|
||||||
|
val mainClassName: String,
|
||||||
|
override val author: String,
|
||||||
|
override val version: String,
|
||||||
|
override val info: String,
|
||||||
|
override val dependencies: List<PluginDependency>
|
||||||
|
) : PluginDescription, FilePluginDescription {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在手动实现时使用这个构造器.
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
constructor(
|
||||||
|
kind: PluginKind, name: String, mainClassName: String, author: String,
|
||||||
|
version: String, info: String, depends: List<PluginDependency>,
|
||||||
|
file: File
|
||||||
|
) : this(kind, name, mainClassName, author, version, info, depends) {
|
||||||
|
this._file = file
|
||||||
|
}
|
||||||
|
|
||||||
|
override val file: File
|
||||||
|
get() = _file ?: error("Internal error: JvmPluginDescription(name=$name)._file == null")
|
||||||
|
|
||||||
|
|
||||||
|
@Suppress("PropertyName")
|
||||||
|
@Transient
|
||||||
|
@JvmField
|
||||||
|
internal var _file: File? = null
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "JvmPluginDescription(kind=$kind, name='$name', mainClassName='$mainClassName', author='$author', version='$version', info='$info', dependencies=$dependencies, _file=$_file)"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* 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.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 dependencies: List<PluginDependency>
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 插件的一个依赖的信息 */
|
||||||
|
@Serializable
|
||||||
|
data class PluginDependency(
|
||||||
|
/** 依赖插件名 */
|
||||||
|
val name: String,
|
||||||
|
/**
|
||||||
|
* 依赖版本号
|
||||||
|
* @see versionKind 版本号类型
|
||||||
|
*/
|
||||||
|
val version: String,
|
||||||
|
/** 版本号类型 */
|
||||||
|
val versionKind: VersionKind,
|
||||||
|
/**
|
||||||
|
* 若为 `false`, 插件在找不到此依赖时也能正常加载.
|
||||||
|
*/
|
||||||
|
val isOptional: Boolean
|
||||||
|
) {
|
||||||
|
enum class VersionKind {
|
||||||
|
/** 要求依赖精确的版本 */
|
||||||
|
EXACT,
|
||||||
|
|
||||||
|
/** 要求依赖最低版本 */
|
||||||
|
AT_LEAST,
|
||||||
|
|
||||||
|
/** 要求依赖最高版本 */
|
||||||
|
AT_MOST
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "$name ${versionKind.toEnglishString()}v$version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于文件的插件的描述
|
||||||
|
*/
|
||||||
|
interface FilePluginDescription : PluginDescription {
|
||||||
|
val file: File
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun PluginDependency.VersionKind.toEnglishString(): String = when (this) {
|
||||||
|
PluginDependency.VersionKind.EXACT -> ""
|
||||||
|
PluginDependency.VersionKind.AT_LEAST -> "at least "
|
||||||
|
PluginDependency.VersionKind.AT_MOST -> "at most "
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user