mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-27 00:30:17 +08:00
SettingStorage infrastructure
This commit is contained in:
parent
3d53f7f7bc
commit
f1b0bf7e68
@ -15,6 +15,8 @@ import kotlinx.io.charsets.Charset
|
|||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||||
import net.mamoe.mirai.console.plugin.builtin.JarPluginLoader
|
import net.mamoe.mirai.console.plugin.builtin.JarPluginLoader
|
||||||
|
import net.mamoe.mirai.console.plugin.builtin.JvmPlugin
|
||||||
|
import net.mamoe.mirai.console.setting.SettingStorage
|
||||||
import net.mamoe.mirai.utils.DefaultLogger
|
import net.mamoe.mirai.utils.DefaultLogger
|
||||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
@ -41,7 +43,11 @@ object MiraiConsole : CoroutineScope, IMiraiConsole {
|
|||||||
override val mainLogger: MiraiLogger get() = instance.mainLogger
|
override val mainLogger: MiraiLogger get() = instance.mainLogger
|
||||||
override val coroutineContext: CoroutineContext get() = instance.coroutineContext
|
override val coroutineContext: CoroutineContext get() = instance.coroutineContext
|
||||||
|
|
||||||
override val builtInPluginLoaders: List<PluginLoader<*, *>> = instance.builtInPluginLoaders
|
override val builtInPluginLoaders: List<PluginLoader<*, *>> get() = instance.builtInPluginLoaders
|
||||||
|
|
||||||
|
@Suppress("CANNOT_WEAKEN_ACCESS_PRIVILEGE")
|
||||||
|
internal override val jvmSettingStorage: SettingStorage
|
||||||
|
get() = instance.jvmSettingStorage
|
||||||
|
|
||||||
init {
|
init {
|
||||||
DefaultLogger = { identity -> this.newLogger(identity) }
|
DefaultLogger = { identity -> this.newLogger(identity) }
|
||||||
@ -79,6 +85,11 @@ internal interface IMiraiConsole : CoroutineScope {
|
|||||||
* 内建加载器列表, 一般需要包含 [JarPluginLoader]
|
* 内建加载器列表, 一般需要包含 [JarPluginLoader]
|
||||||
*/
|
*/
|
||||||
val builtInPluginLoaders: List<PluginLoader<*, *>>
|
val builtInPluginLoaders: List<PluginLoader<*, *>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内建的供 [JvmPlugin] 使用的 [SettingStorage]
|
||||||
|
*/
|
||||||
|
val jvmSettingStorage: SettingStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
package net.mamoe.mirai.console.plugin
|
package net.mamoe.mirai.console.plugin
|
||||||
|
|
||||||
import net.mamoe.mirai.console.plugin.builtin.JvmPlugin
|
import net.mamoe.mirai.console.plugin.builtin.JvmPlugin
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表示一个 mirai-console 插件.
|
* 表示一个 mirai-console 插件.
|
||||||
@ -22,4 +23,9 @@ interface Plugin {
|
|||||||
* 所属插件加载器实例
|
* 所属插件加载器实例
|
||||||
*/
|
*/
|
||||||
val loader: PluginLoader<*, *>
|
val loader: PluginLoader<*, *>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件数据目录
|
||||||
|
*/
|
||||||
|
val dataFolder: File
|
||||||
}
|
}
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.console.plugin
|
package net.mamoe.mirai.console.plugin
|
||||||
|
|
||||||
|
import net.mamoe.mirai.console.plugin.builtin.JarPluginLoader
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,7 +60,7 @@ open class PluginLoadException : RuntimeException {
|
|||||||
*/
|
*/
|
||||||
interface FilePluginLoader<P : Plugin, D : PluginDescription> : PluginLoader<P, D> {
|
interface FilePluginLoader<P : Plugin, D : PluginDescription> : PluginLoader<P, D> {
|
||||||
/**
|
/**
|
||||||
* 所支持的插件文件后缀, 不含 '.'. 如 [JarPluginLoader] 为 "jar"
|
* 所支持的插件文件后缀, 含 '.'. 如 [JarPluginLoader] 为 ".jar"
|
||||||
*/
|
*/
|
||||||
val fileSuffix: String
|
val fileSuffix: String
|
||||||
}
|
}
|
||||||
@ -70,6 +71,9 @@ abstract class AbstractFilePluginLoader<P : Plugin, D : PluginDescription>(
|
|||||||
private fun pluginsFilesSequence(): Sequence<File> =
|
private fun pluginsFilesSequence(): Sequence<File> =
|
||||||
PluginManager.pluginsDir.walk().filter { it.isFile && it.name.endsWith(fileSuffix, ignoreCase = true) }
|
PluginManager.pluginsDir.walk().filter { it.isFile && it.name.endsWith(fileSuffix, ignoreCase = true) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取扫描到的后缀与 [fileSuffix] 相同的文件中的 [PluginDescription]
|
||||||
|
*/
|
||||||
protected abstract fun Sequence<File>.mapToDescription(): List<D>
|
protected abstract fun Sequence<File>.mapToDescription(): List<D>
|
||||||
|
|
||||||
final override fun listPlugins(): List<D> = pluginsFilesSequence().mapToDescription()
|
final override fun listPlugins(): List<D> = pluginsFilesSequence().mapToDescription()
|
||||||
|
@ -24,6 +24,7 @@ inline fun PluginLoader<*, *>.unregister() = PluginManager.unregisterPluginLoade
|
|||||||
|
|
||||||
object PluginManager {
|
object PluginManager {
|
||||||
val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() }
|
val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() }
|
||||||
|
val pluginsDataFolder = File(MiraiConsole.rootDir, "data").apply { mkdir() }
|
||||||
|
|
||||||
private val _pluginLoaders: MutableList<PluginLoader<*, *>> = mutableListOf()
|
private val _pluginLoaders: MutableList<PluginLoader<*, *>> = mutableListOf()
|
||||||
private val loadersLock: ReentrantLock = ReentrantLock()
|
private val loadersLock: ReentrantLock = ReentrantLock()
|
||||||
|
@ -14,6 +14,7 @@ import net.mamoe.mirai.console.MiraiConsole
|
|||||||
import net.mamoe.mirai.console.plugin.AbstractFilePluginLoader
|
import net.mamoe.mirai.console.plugin.AbstractFilePluginLoader
|
||||||
import net.mamoe.mirai.console.plugin.PluginLoadException
|
import net.mamoe.mirai.console.plugin.PluginLoadException
|
||||||
import net.mamoe.mirai.console.plugin.PluginsLoader
|
import net.mamoe.mirai.console.plugin.PluginsLoader
|
||||||
|
import net.mamoe.mirai.console.setting.SettingStorage
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
import net.mamoe.yamlkt.Yaml
|
import net.mamoe.yamlkt.Yaml
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -24,7 +25,7 @@ import kotlin.reflect.full.createInstance
|
|||||||
/**
|
/**
|
||||||
* 内建的 Jar (JVM) 插件加载器
|
* 内建的 Jar (JVM) 插件加载器
|
||||||
*/
|
*/
|
||||||
object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>("jar"), CoroutineScope {
|
object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>(".jar"), CoroutineScope {
|
||||||
private val logger: MiraiLogger by lazy {
|
private val logger: MiraiLogger by lazy {
|
||||||
MiraiConsole.newLogger(JarPluginLoader::class.simpleName!!)
|
MiraiConsole.newLogger(JarPluginLoader::class.simpleName!!)
|
||||||
}
|
}
|
||||||
@ -46,6 +47,8 @@ object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescriptio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val settingStorage: SettingStorage = MiraiConsole.jvmSettingStorage
|
||||||
|
|
||||||
override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description
|
override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description
|
||||||
|
|
||||||
override fun Sequence<File>.mapToDescription(): List<JvmPluginDescription> {
|
override fun Sequence<File>.mapToDescription(): List<JvmPluginDescription> {
|
||||||
|
@ -12,18 +12,20 @@
|
|||||||
package net.mamoe.mirai.console.plugin.builtin
|
package net.mamoe.mirai.console.plugin.builtin
|
||||||
|
|
||||||
import kotlinx.atomicfu.locks.withLock
|
import kotlinx.atomicfu.locks.withLock
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.SupervisorJob
|
|
||||||
import net.mamoe.mirai.console.MiraiConsole
|
import net.mamoe.mirai.console.MiraiConsole
|
||||||
import net.mamoe.mirai.console.plugin.Plugin
|
import net.mamoe.mirai.console.plugin.Plugin
|
||||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||||
|
import net.mamoe.mirai.console.plugin.PluginManager
|
||||||
|
import net.mamoe.mirai.console.setting.*
|
||||||
import net.mamoe.mirai.console.utils.JavaPluginScheduler
|
import net.mamoe.mirai.console.utils.JavaPluginScheduler
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,7 +42,7 @@ interface JvmPlugin : Plugin, CoroutineScope {
|
|||||||
val description: JvmPluginDescription
|
val description: JvmPluginDescription
|
||||||
|
|
||||||
/** 所属插件加载器实例 */
|
/** 所属插件加载器实例 */
|
||||||
override val loader: PluginLoader<*, *> get() = JarPluginLoader
|
override val loader: JarPluginLoader get() = JarPluginLoader
|
||||||
|
|
||||||
@JvmDefault
|
@JvmDefault
|
||||||
fun onLoad() {
|
fun onLoad() {
|
||||||
@ -72,36 +74,46 @@ abstract class JavaPlugin @JvmOverloads constructor(
|
|||||||
abstract class KotlinPlugin @JvmOverloads constructor(
|
abstract class KotlinPlugin @JvmOverloads constructor(
|
||||||
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
|
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||||
) : JvmPlugin, JvmPluginImpl(parentCoroutineContext) {
|
) : JvmPlugin, JvmPluginImpl(parentCoroutineContext) {
|
||||||
// that's it
|
|
||||||
|
abstract inner class PluginSetting : Setting() {
|
||||||
|
private val track =
|
||||||
|
@Suppress("LeakingThis")
|
||||||
|
loader.settingStorage.trackOn(this)
|
||||||
|
|
||||||
|
init {
|
||||||
|
this@KotlinPlugin.job.invokeOnCompletion {
|
||||||
|
track.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onElementChanged(value: Value<*>) {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val <T> T.job: Job where T : CoroutineScope, T : Plugin get() = this.coroutineContext[Job]!!
|
||||||
|
|
||||||
internal sealed class JvmPluginImpl(
|
internal sealed class JvmPluginImpl(
|
||||||
parentCoroutineContext: CoroutineContext
|
parentCoroutineContext: CoroutineContext
|
||||||
) : JvmPlugin, CoroutineScope {
|
) : JvmPlugin, CoroutineScope {
|
||||||
|
// region JvmPlugin
|
||||||
/**
|
/**
|
||||||
* Initialized immediately after construction of [JvmPluginImpl] instance
|
* Initialized immediately after construction of [JvmPluginImpl] instance
|
||||||
*/
|
*/
|
||||||
@Suppress("PropertyName")
|
@Suppress("PropertyName")
|
||||||
internal lateinit var _description: JvmPluginDescription
|
internal lateinit var _description: JvmPluginDescription
|
||||||
|
|
||||||
// for future use
|
|
||||||
@Suppress("PropertyName")
|
|
||||||
@JvmField
|
|
||||||
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) }
|
||||||
|
|
||||||
@JvmField
|
|
||||||
internal val coroutineContextInitializer = {
|
|
||||||
CoroutineExceptionHandler { _, throwable -> logger.error(throwable) }
|
|
||||||
.plus(parentCoroutineContext)
|
|
||||||
.plus(SupervisorJob(parentCoroutineContext[Job])) + _intrinsicCoroutineContext
|
|
||||||
}
|
|
||||||
|
|
||||||
private var firstRun = true
|
private var firstRun = true
|
||||||
|
|
||||||
|
override val dataFolder: File by lazy {
|
||||||
|
File(PluginManager.pluginsDataFolder, description.name).apply { mkdir() }
|
||||||
|
}
|
||||||
|
|
||||||
internal fun internalOnDisable() {
|
internal fun internalOnDisable() {
|
||||||
firstRun = false
|
firstRun = false
|
||||||
this.onDisable()
|
this.onDisable()
|
||||||
@ -116,6 +128,22 @@ internal sealed class JvmPluginImpl(
|
|||||||
this.onEnable()
|
this.onEnable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region CoroutineScope
|
||||||
|
|
||||||
|
// for future use
|
||||||
|
@Suppress("PropertyName")
|
||||||
|
@JvmField
|
||||||
|
internal var _intrinsicCoroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
internal val coroutineContextInitializer = {
|
||||||
|
CoroutineExceptionHandler { _, throwable -> logger.error(throwable) }
|
||||||
|
.plus(parentCoroutineContext)
|
||||||
|
.plus(SupervisorJob(parentCoroutineContext[Job])) + _intrinsicCoroutineContext
|
||||||
|
}
|
||||||
|
|
||||||
private fun refreshCoroutineContext(): CoroutineContext {
|
private fun refreshCoroutineContext(): CoroutineContext {
|
||||||
return coroutineContextInitializer().also { _coroutineContext = it }
|
return coroutineContextInitializer().also { _coroutineContext = it }
|
||||||
}
|
}
|
||||||
@ -126,4 +154,5 @@ internal sealed class JvmPluginImpl(
|
|||||||
get() = _coroutineContext
|
get() = _coroutineContext
|
||||||
?: contextUpdateLock.withLock { _coroutineContext ?: refreshCoroutineContext() }
|
?: contextUpdateLock.withLock { _coroutineContext ?: refreshCoroutineContext() }
|
||||||
|
|
||||||
|
// endregion
|
||||||
}
|
}
|
@ -13,6 +13,7 @@ package net.mamoe.mirai.console.setting
|
|||||||
|
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import net.mamoe.mirai.console.setting.internal.SettingImpl
|
import net.mamoe.mirai.console.setting.internal.SettingImpl
|
||||||
|
import net.mamoe.mirai.console.setting.internal.serialNameOrPropertyName
|
||||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||||
import kotlin.properties.ReadWriteProperty
|
import kotlin.properties.ReadWriteProperty
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
@ -33,6 +34,12 @@ typealias Comment = net.mamoe.yamlkt.Comment
|
|||||||
*/
|
*/
|
||||||
@Suppress("EXPOSED_SUPER_CLASS")
|
@Suppress("EXPOSED_SUPER_CLASS")
|
||||||
abstract class Setting : SettingImpl() {
|
abstract class Setting : SettingImpl() {
|
||||||
|
|
||||||
|
data class PropertyInfo(
|
||||||
|
val serialName: String,
|
||||||
|
val annotations: List<Annotation>
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 这个配置的名称, 仅对于顶层配置有效.
|
* 这个配置的名称, 仅对于顶层配置有效.
|
||||||
*/
|
*/
|
||||||
@ -43,6 +50,16 @@ abstract class Setting : SettingImpl() {
|
|||||||
?: error("Names should be assigned to anonymous classes manually by overriding serialName")
|
?: error("Names should be assigned to anonymous classes manually by overriding serialName")
|
||||||
|
|
||||||
|
|
||||||
|
// for Java only
|
||||||
|
fun <T : Any> addProperty(
|
||||||
|
propertyInfo: PropertyInfo,
|
||||||
|
value: Value<*>
|
||||||
|
): Value<*> {
|
||||||
|
if (built) error("The Setting is already serialized so it's structure is immutable.")
|
||||||
|
valueList.add(value to propertyInfo)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提供属性委托, 并添加这个对象的自动保存跟踪.
|
* 提供属性委托, 并添加这个对象的自动保存跟踪.
|
||||||
*/
|
*/
|
||||||
@ -52,10 +69,12 @@ abstract class Setting : SettingImpl() {
|
|||||||
property: KProperty<*>
|
property: KProperty<*>
|
||||||
): ReadWriteProperty<Setting, T> {
|
): ReadWriteProperty<Setting, T> {
|
||||||
if (built) error("The Setting is already serialized so it's structure is immutable.")
|
if (built) error("The Setting is already serialized so it's structure is immutable.")
|
||||||
valueList.add(this to property)
|
valueList.add(this to PropertyInfo(property.serialNameOrPropertyName, property.annotations))
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract override fun onElementChanged(value: Value<*>)
|
||||||
|
|
||||||
override fun toString(): String = yamlForToString.stringify(this.serializer, this)
|
override fun toString(): String = yamlForToString.stringify(this.serializer, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.setting
|
||||||
|
|
||||||
|
import kotlinx.atomicfu.locks.withLock
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.internal.getChecked
|
||||||
|
import net.mamoe.mirai.console.setting.internal.SettingSerializerMark
|
||||||
|
import java.io.Closeable
|
||||||
|
import java.io.File
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Setting] 存储方式
|
||||||
|
*/
|
||||||
|
interface SettingStorage {
|
||||||
|
interface TrackedSetting : Closeable {
|
||||||
|
fun save()
|
||||||
|
fun update()
|
||||||
|
|
||||||
|
override fun close()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun trackOn(setting: Setting): TrackedSetting
|
||||||
|
|
||||||
|
fun saveAll()
|
||||||
|
fun updateAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
class SingleFileSettingStorage(
|
||||||
|
val file: File
|
||||||
|
) : SettingStorage {
|
||||||
|
private val descriptor: MutableList<Setting> = ArrayList()
|
||||||
|
private val updaterSerializer: KSerializer<SettingSerializerMark> = object : KSerializer<SettingSerializerMark> {
|
||||||
|
override val descriptor: SerialDescriptor = SerialDescriptor("SingleFileSettingStorage") {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): SettingSerializerMark {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: SettingSerializerMark) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(file.isFile) { "file $file is not a file" }
|
||||||
|
require(file.canRead()) { "file $file is not readable" }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun trackOn(setting: Setting): SettingStorage.TrackedSetting {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveAll() {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateAll() {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
@ -13,13 +13,19 @@ package net.mamoe.mirai.console.command
|
|||||||
|
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.console.plugin.builtin.KotlinPlugin
|
import net.mamoe.mirai.console.plugin.builtin.KotlinPlugin
|
||||||
|
import net.mamoe.mirai.console.setting.value
|
||||||
import net.mamoe.mirai.message.data.*
|
import net.mamoe.mirai.message.data.*
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
|
||||||
val plugin: KotlinPlugin = object : KotlinPlugin() {
|
val plugin = MyPlugin()
|
||||||
|
|
||||||
|
class MyPlugin : KotlinPlugin() {
|
||||||
|
|
||||||
|
inner class MySetting : PluginSetting() {
|
||||||
|
val int by value(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user