Update docs

This commit is contained in:
Him188 2020-08-23 16:36:10 +08:00
parent 6fe8f6f57c
commit 66e4852c4d
12 changed files with 248 additions and 106 deletions

View File

@ -4,11 +4,11 @@
## 包结构
- `net.mamoe.mirai.console.`
- `command`:指令模块:[Command]
- `data`:存储模块:[PluginData], [PluginConfig], [PluginDataStorage]
- `command`:指令模块:[`Command`]
- `data`:存储模块:[`PluginData`], [`PluginConfig`], [`PluginDataStorage`]
- `event`Console 实现的事件.
- `plugin`:插件模块:[Plugin], [PluginLoader], [JvmPlugin]
- `util`:工具类:[Annotations], [BotManager], [ConsoleInput], [JavaPluginScheduler]
- `plugin`:插件模块:[`Plugin`], [`PluginLoader`], [`JvmPlugin`]
- `util`:工具类:[`Annotations`], [`BotManager`], [`ConsoleInput`], [`JavaPluginScheduler`]
- `internal`:内部实现
@ -16,59 +16,134 @@
### `Plugin` 模块
Console 支持拥有强扩展性的插件加载器。内建 JVM 插件支持 ([JarPluginLoader])。
Console 支持拥有强扩展性的插件加载器。内建 JVM 插件支持 ([`JarPluginLoader`])。
#### [插件加载器 `PluginLoader`][PluginLoader]
Console 本身是一套高扩展性的「框架」,必然拥有通用的 [插件加载器][PluginLoader]。
#### [插件加载器 `PluginLoader`][`PluginLoader`] 和 [插件管理器][`PluginManager`]
Console 本身是一套高扩展性的「框架」,拥有通用的 [插件加载器][`PluginLoader`]。
Console 内置 [JarPluginLoader],支持加载使用 Kotlin、 Java或其他 JVM 平台编程语言并打包为 jar 的插件 (详见下文 `JvmPlugin`)。
Console 内置 [`JarPluginLoader`],支持加载使用 Kotlin、 Java或其他 JVM 平台编程语言并打包为 jar 的插件 (详见下文 `JvmPlugin`)。
扩展的 [插件加载器][PluginLoader] 可以由一个特别的 [JVM 插件][JvmPlugin] 提供。在启动时, Console 首先加载那些提供扩展 [插件加载器][PluginLoader] 的插件. 并允许它们 [注册扩展加载器]。
#### [`Plugin`][Plugin]
所有 Console 插件都必须实现 [`Plugin`][Plugin] 接口。
虽然 Console 是 JVM 平台程序, 但也拥有支持其他平台的插件管理系统。
[`Plugin`][Plugin] 可在相应 [插件加载器 `PluginLoader`][PluginLoader] 的帮助下,成为任何语言实现的插件与 Console 建立联系的桥梁。
扩展的 [插件加载器][`PluginLoader`] 可以由一个特别的 [JVM 插件][`JvmPlugin`] 提供。
##### 服务器启动过程中的插件加载流程
#### [JVM 插件][JvmPlugin]
在服务器启动过程中, Console 首先加载那些提供扩展 [插件加载器][`PluginLoader`] 的插件. 并允许它们 [注册扩展加载器]。
随后对插件按依赖顺序调用 `onLoad()`, 告知插件主类加载完毕, 相关依赖解决完毕.
当所有插件的 `onLoad()` 都被调用后, [`PluginManager`] 按依赖顺序依次调用 `onEnable()`
如果 A 依赖 B, B 依赖 C. 那么启动时的调用顺序为:
`C.onLoad()` -> `B.onLoad()` -> `A.onLoad()` -> `C.onEnable` -> `B.onEnable()` -> `A.onEnable()`
#### [`Plugin`]
所有 Console 插件都必须实现 [`Plugin`] 接口。
`Plugin` 很通用,它只拥有很少的成员:
```kotlin
interface Plugin : CommandOwner {
val isEnabled: Boolean
val loader: PluginLoader<*, *> // 能处理这个 Plugin 的 PluginLoader
}
```
[`Plugin`] 可在相应 [插件加载器 `PluginLoader`][`PluginLoader`] 的帮助下,成为任何语言实现的插件与 Console 建立联系的桥梁。
#### [JVM 插件][`JvmPlugin`]
##### [`JvmPlugin`]
```kotlin
interface JvmPlugin : Plugin, CoroutineScope, PluginFileExtensions, ResourceContainer, AutoSavePluginDataHolder {
val logger: MiraiLogger
val description: JvmPluginDescription
val loader: JarPluginLoader
fun <T : PluginData> loadPluginData(clazz: Class<T>): T
fun <T : PluginConfig> loadPluginConfig(clazz: Class<T>): T
fun onLoad()
fun onEnable()
fun onDisable()
}
```
##### `plugin.yml`
JVM 插件, 通常需要打包为 `jar` 后才能被加载. `jar` 中根目录需要包含一个 `plugin.yml`, 他将会被读取为 [`JvmPluginDescription`].
**注意**:
- 插件自身的版本要求遵循 [语义化版本 2.0.0](https://semver.org/lang/zh-CN/) 规范, 合格的版本例如: `1.0.0`, `1.0`, `1.0-M1`, `1.0-pre-1`
- 插件依赖的版本遵循 [语义化版本 2.0.0](https://semver.org/lang/zh-CN/) 规范, 同时支持 [Apache Ivy 风格表示方法](http://ant.apache.org/ivy/history/latest-milestone/settings/version-matchers.html).
`plugin.yml` 的内容如下:
```yaml
# 必须. 插件名称, 允许空格, 允许中文, 不允许 ':'
name: "MyTestPlugin"
# 必须. 插件主类, 即继承 KotlinPlugin 或 JavaPlugin 的类
main: org.example.MyPluginMain
# 必须. 插件版本. 遵循《语义化版本 2.0.0》规范
version: 0.1.0
# 可选. 插件种类.
# 'NORMAL': 表示普通插件
# 'LOADER': 表示提供扩展插件加载器的插件
kind: NORMAL
# 可选. 插件描述
info: "这是一个测试插件"
# 可选. 插件作者
author: "Mirai Example"
# 可选. 插件依赖列表. 两种指定方式均可.
dependencies:
- name: "the" # 依赖的插件名
version: null # 依赖的版本号, 支持 Apache Ivy 格式. 为 null 或不指定时不限制版本
isOptional: true # `true` 表示插件在找不到此依赖时也能正常加载
- "SamplePlugin" # 名称为 SamplePlugin 的插件, 不限制版本, isOptional=false
- "TestPlugin:1.0.0+" # 名称为 ExamplePlugin 的插件, 版本至少为 1.0.0, isOptional=false
- "ExamplePlugin:1.5.0+?" # 名称为 ExamplePlugin 的插件, 版本至少为 1.5.0, 末尾 `?` 表示 isOptional=true
- "Another test plugin:[1.0.0, 2.0.0)" # 名称为 Another test plugin 的插件, 版本要求大于等于 1.0.0, 小于 2.0.0, isOptional=false
```
#### 实现 Kotlin 插件
添加一个类
在完成上述 `plugin.yml` 之后, 创建 `main` 中指定的插件主类, 继承 [`KotlinPlugin`]
#### 实现 Java 插件
[Plugin]: src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt
[PluginDescription]: src/main/kotlin/net/mamoe/mirai/console/plugin/description.kt
[PluginLoader]: src/main/kotlin/net/mamoe/mirai/console/plugin/PluginLoader.kt
[JarPluginLoader]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JarPluginLoader.kt
[JvmPlugin]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt
[AbstractJvmPlugin]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/AbstractJvmPlugin.kt
[KotlinPlugin]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/KotlinPlugin.kt
[JavaPlugin]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JavaPlugin.kt
[`Plugin`]: src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt
[`PluginDescription`]: src/main/kotlin/net/mamoe/mirai/console/plugin/description.kt
[`PluginLoader`]: src/main/kotlin/net/mamoe/mirai/console/plugin/PluginLoader.kt
[`PluginManager`]: src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt
[`JarPluginLoader`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JarPluginLoader.kt
[`JvmPlugin`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt
[`JvmPluginDescription`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPluginDescription.kt
[`AbstractJvmPlugin`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/AbstractJvmPlugin.kt
[`KotlinPlugin`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/KotlinPlugin.kt
[`JavaPlugin`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JavaPlugin.kt
[PluginData]: src/main/kotlin/net/mamoe/mirai/console/data/PluginData.kt
[PluginConfig]: src/main/kotlin/net/mamoe/mirai/console/data/PluginConfig.kt
[PluginDataStorage]: src/main/kotlin/net/mamoe/mirai/console/data/PluginDataStorage.kt
[MiraiConsole]: src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt
[MiraiConsoleImplementation]: src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleImplementation.kt
[`PluginData`]: src/main/kotlin/net/mamoe/mirai/console/data/PluginData.kt
[`PluginConfig`]: src/main/kotlin/net/mamoe/mirai/console/data/PluginConfig.kt
[`PluginDataStorage`]: src/main/kotlin/net/mamoe/mirai/console/data/PluginDataStorage.kt
[`MiraiConsole`]: src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt
[`MiraiConsoleImplementation`]: src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleImplementation.kt
<!--[MiraiConsoleFrontEnd]: src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleFrontEnd.kt-->
[Command]: src/main/kotlin/net/mamoe/mirai/console/command/Command.kt
[CompositeCommand]: src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt
[SimpleCommand]: src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt
[RawCommand]: src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt
[CommandManager]: src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt
[`Command`]: src/main/kotlin/net/mamoe/mirai/console/command/Command.kt
[`CompositeCommand`]: src/main/kotlin/net/mamoe/mirai/console/command/CompositeCommand.kt
[`SimpleCommand`]: src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt
[`RawCommand`]: src/main/kotlin/net/mamoe/mirai/console/command/RawCommand.kt
[`CommandManager`]: src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt
[BotManager]: src/main/kotlin/net/mamoe/mirai/console/util/BotManager.kt
[Annotations]: src/main/kotlin/net/mamoe/mirai/console/util/Annotations.kt
[ConsoleInput]: src/main/kotlin/net/mamoe/mirai/console/util/ConsoleInput.kt
[JavaPluginScheduler]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JavaPluginScheduler.kt
[`BotManager`]: src/main/kotlin/net/mamoe/mirai/console/util/BotManager.kt
[`Annotations`]: src/main/kotlin/net/mamoe/mirai/console/util/Annotations.kt
[`ConsoleInput`]: src/main/kotlin/net/mamoe/mirai/console/util/ConsoleInput.kt
[`JavaPluginScheduler`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JavaPluginScheduler.kt
[注册扩展加载器]: src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt#L49-L51

View File

@ -34,6 +34,9 @@ public interface BuiltInCommand : Command, BuiltInCommandInternal
// for identification
internal interface BuiltInCommandInternal : Command
/**
* 内建指令列表
*/
@ConsoleExperimentalAPI
@Suppress("unused")
public object BuiltInCommands {

View File

@ -22,9 +22,9 @@ import net.mamoe.mirai.message.data.SingleMessage
*
* @see CommandManager.register 注册这个指令
*
* @see RawCommand
* @see CompositeCommand
* @see SimpleCommand
* @see RawCommand 无参数解析, 接收原生参数的指令
* @see CompositeCommand 复合指令
* @see SimpleCommand 简单的, 支持参数自动解析的指令
*/
public interface Command {
/**

View File

@ -9,11 +9,16 @@
package net.mamoe.mirai.console.command
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregisterAllCommands
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
/**
* 指令的所有者. 目前仅作为标识作用.
*
* @see CommandManager.unregisterAllCommands 取消注册所有属于一个 [CommandOwner] 的指令
* @see CommandManager.registeredCommands 获取已经注册了的属于一个 [CommandOwner] 的指令列表.
*
* @see JvmPlugin 是一个 [CommandOwner]
*/
public interface CommandOwner

View File

@ -12,13 +12,20 @@
package net.mamoe.mirai.console.command
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
import net.mamoe.mirai.console.internal.command.AndCommandPermissionImpl
import net.mamoe.mirai.console.internal.command.OrCommandPermissionImpl
import net.mamoe.mirai.console.util.BotManager.INSTANCE.isManager
import net.mamoe.mirai.contact.isAdministrator
import net.mamoe.mirai.contact.isOperator
import net.mamoe.mirai.contact.isOwner
/**
* 指令权限
* 指令权限.
*
* [CommandManager.executeCommand] 时将会检查权限.
*
* @see Command.permission 从指令获取权限
*/
public fun interface CommandPermission {
/**
@ -33,12 +40,12 @@ public fun interface CommandPermission {
/**
* 满足两个权限其中一个即可使用指令
*/ // no extension for Java
public infix fun or(another: CommandPermission): CommandPermission = OrCommandPermission(this, another)
public infix fun or(another: CommandPermission): CommandPermission = OrCommandPermissionImpl(this, another)
/**
* 同时拥有两个权限才能使用指令
*/ // no extension for Java
public infix fun and(another: CommandPermission): CommandPermission = AndCommandPermission(this, another)
public infix fun and(another: CommandPermission): CommandPermission = AndCommandPermissionImpl(this, another)
/**
@ -101,34 +108,36 @@ public fun interface CommandPermission {
/**
* 默认权限.
*
* [Manager] or [Console]
* @return [Manager] or [Console]
*/
public object Default : CommandPermission by (Manager or Console)
}
/**
* 判断 [this] 是否拥有权限 [permission]
*
* @see CommandSender.hasPermission
* @see CommandPermission.testPermission
* @see CommandPermission.hasPermission
*/
public inline fun CommandSender.hasPermission(permission: CommandPermission): Boolean =
permission.run { this@hasPermission.hasPermission() }
/**
* 判断 [sender] 是否拥有权限 [this]
*
* @see CommandSender.hasPermission
* @see CommandPermission.testPermission
* @see CommandPermission.hasPermission
*/
public inline fun CommandPermission.testPermission(sender: CommandSender): Boolean = this.run { sender.hasPermission() }
public inline fun Command.testPermission(sender: CommandSender): Boolean = sender.hasPermission(this.permission)
internal class OrCommandPermission(
private val first: CommandPermission,
private val second: CommandPermission
) : CommandPermission {
override fun CommandSender.hasPermission(): Boolean {
return this.hasPermission(first) || this.hasPermission(second)
}
}
internal class AndCommandPermission(
private val first: CommandPermission,
private val second: CommandPermission
) : CommandPermission {
override fun CommandSender.hasPermission(): Boolean {
return this.hasPermission(first) && this.hasPermission(second)
}
}
/**
* 判断 [sender] 是否拥有权限 [Command.permission]
*
* @see CommandSender.hasPermission
* @see CommandPermission.testPermission
* @see CommandPermission.hasPermission
*/
public inline fun Command.testPermission(sender: CommandSender): Boolean = sender.hasPermission(this.permission)

View File

@ -73,7 +73,7 @@ import kotlin.reflect.KClass
* @see buildCommandArgumentContext
*/
@ConsoleExperimentalAPI
public abstract class CompositeCommand @JvmOverloads constructor(
public abstract class CompositeCommand(
owner: CommandOwner,
vararg names: String,
description: String = "no description available",

View File

@ -28,7 +28,7 @@ import net.mamoe.mirai.message.data.SingleMessage
* @see SimpleCommand 简单指令
* @see CompositeCommand 复合指令
*/
public abstract class RawCommand @JvmOverloads constructor(
public abstract class RawCommand(
/**
* 指令拥有者.
* @see CommandOwner

View File

@ -45,7 +45,7 @@ import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotatio
* @see JSimpleCommand Java 实现
* @see [CommandManager.executeCommand]
*/
public abstract class SimpleCommand @JvmOverloads constructor(
public abstract class SimpleCommand(
owner: CommandOwner,
vararg names: String,
description: String = "no description available",

View File

@ -6,12 +6,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import net.mamoe.mirai.console.data.PluginDataStorage.Companion.load
import net.mamoe.mirai.console.internal.data.AutoSavePluginData
import net.mamoe.mirai.console.internal.data.cast
import net.mamoe.mirai.console.internal.data.newPluginDataInstanceUsingReflection
import net.mamoe.mirai.console.internal.data.typeOf0
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.full.createType
/**
* 可以持有相关 [PluginData] 实例的对象, 作为 [PluginData] 实例的拥有者.
@ -19,35 +13,13 @@ import kotlin.reflect.full.createType
* @see PluginDataStorage.load
* @see PluginDataStorage.store
*
* @see AutoSavePluginDataHolder 自动保存
* @see AutoSavePluginDataHolder 支持自动保存
*/
public interface PluginDataHolder {
/**
* 保存时使用的分类名
*/
public val name: String
/**
* 创建一个 [PluginData] 实例.
*
* @see Companion.newPluginDataInstance
* @see KClass.createType
*/
@JvmDefault
public fun <T : PluginData> newPluginDataInstance(type: KType): T =
newPluginDataInstanceUsingReflection<PluginData>(type) as T
public companion object {
/**
* 创建一个 [PluginData] 实例.
*
* @see PluginDataHolder.newPluginDataInstance
*/
@JvmSynthetic
public inline fun <reified T : PluginData> PluginDataHolder.newPluginDataInstance(): T {
return this.newPluginDataInstance(typeOf0<T>())
}
}
}
/**
@ -69,6 +41,38 @@ public interface AutoSavePluginDataHolder : PluginDataHolder, CoroutineScope {
* @see Long.rangeTo Kotlin 用户使用 [Long.rangeTo] 创建, `3000..50000`
*/
public val autoSaveIntervalMillis: LongRange
}
/*
public interface PluginDataHolder {
/**
* 创建一个 [PluginData] 实例.
*
* 注意, 此时的 [PluginData] 并没有绑定 [PluginDataStorage], 因此无法进行保存等操作.
*
* @see Companion.newPluginDataInstance
* @see KClass.createType
*/
@JvmDefault
public fun <T : PluginData> newPluginDataInstance(type: KType): T =
newPluginDataInstanceUsingReflection<PluginData>(type) as T
public companion object {
/**
* 创建一个 [PluginData] 实例.
*
* @see PluginDataHolder.newPluginDataInstance
*/
@JvmSynthetic
public inline fun <reified T : PluginData> PluginDataHolder.newPluginDataInstance(): T {
return this.newPluginDataInstance(typeOf0<T>())
}
}
*/
/*
public interface AutoSavePluginDataHolder : PluginDataHolder, CoroutineScope {
/**
* 仅支持确切的 [PluginData] 类型
@ -81,4 +85,4 @@ public interface AutoSavePluginDataHolder : PluginDataHolder, CoroutineScope {
}
return AutoSavePluginData(this, classifier) as T // T is always PluginData
}
}
*/

View File

@ -20,7 +20,12 @@ import java.nio.file.Path
import kotlin.reflect.KClass
/**
* [PluginData] 存储仓库.
* [数据对象][PluginData] 存储仓库.
*
* ## 职责
* [PluginDataStorage] 类似于一个数据库, 它只承担将序列化之后的数据保存到数据库中, 和从数据库取出这个对象的任务.
* [PluginDataStorage] 不考虑一个 []
*
*
* 此为较低层的 API, 一般插件开发者不会接触.
*
@ -36,7 +41,9 @@ public interface PluginDataStorage {
public fun <T : PluginData> load(holder: PluginDataHolder, dataClass: Class<T>): T
/**
* 保存一个实例
* 保存一个实例.
*
* **实现细节**: 调用 [PluginData.updaterSerializer],
*/
public fun store(holder: PluginDataHolder, pluginData: PluginData)

View File

@ -17,20 +17,59 @@ import net.mamoe.mirai.console.internal.data.SemverAsStringSerializerLoose
import net.mamoe.mirai.console.plugin.PluginDependency
import net.mamoe.mirai.console.plugin.PluginDescription
import net.mamoe.mirai.console.plugin.PluginKind
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import java.io.File
@MiraiExperimentalAPI
@Serializable
/**
* @see KotlinMemoryPlugin 不需要 "plugin.yml", 不需要相关资源的在内存中加载的插件.
*/
@ConsoleExperimentalAPI
public data class JvmMemoryPluginDescription(
public override val kind: PluginKind,
public override val name: String,
public override val author: String,
public override val version: @Serializable(with = SemverAsStringSerializerLoose::class) Semver,
public override val version: Semver,
public override val info: String,
public override val dependencies: List<@Serializable(with = PluginDependency.SmartSerializer::class) PluginDependency>
public override val dependencies: List<PluginDependency>
) : JvmPluginDescription
/**
* JVM 插件的描述. 通常作为 `plugin.yml`
*
*
* ```yaml
* # 必须. 插件名称, 允许空格, 允许中文, 不允许 ':'
* name: "MyTestPlugin"
*
* # 必须. 插件主类, 即继承 KotlinPlugin JavaPlugin 的类
* main: org.example.MyPluginMain
*
* # 必须. 插件版本. 遵循语义化版本 2.0.0规范
* version: 0.1.0
*
* # 可选. 插件种类.
* # 'NORMAL': 表示普通插件
* # 'LOADER': 表示提供扩展插件加载器的插件
* kind: NORMAL
*
* # 可选. 插件描述
* info: "这是一个测试插件"
*
* # 可选. 插件作者
* author: "Mirai Example"
*
* # 可选. 插件依赖列表. 两种指定方式均可.
* dependencies:
* - name: "the" # 依赖的插件名
* version: null # 依赖的版本号, 支持 Apache Ivy 格式. null 或不指定时不限制版本
* isOptional: true # `true` 表示插件在找不到此依赖时也能正常加载
* - "SamplePlugin" # 名称为 SamplePlugin 的插件, 不限制版本, isOptional=false
* - "TestPlugin:1.0.0+" # 名称为 ExamplePlugin 的插件, 版本至少为 1.0.0, isOptional=false
* - "ExamplePlugin:1.5.0+?" # 名称为 ExamplePlugin 的插件, 版本至少为 1.5.0, 末尾 `?` 表示 isOptional=true
* - "Another test plugin:[1.0.0, 2.0.0)" # 名称为 Another test plugin 的插件, 版本要求大于等于 1.0.0, 小于 2.0.0, isOptional=false
* ```
*/
public interface JvmPluginDescription : PluginDescription
@MiraiExperimentalAPI

View File

@ -26,7 +26,7 @@ public abstract class KotlinPlugin @JvmOverloads constructor(
/**
* 在内存动态加载的插件.
* 在内存动态加载的插件. 此为预览版本 API.
*/
@ConsoleExperimentalAPI
public abstract class KotlinMemoryPlugin @JvmOverloads constructor(