From f1b0bf7e6818e994994d27e031cb134de0f7b381 Mon Sep 17 00:00:00 2001 From: Him188 Date: Mon, 25 May 2020 17:51:01 +0800 Subject: [PATCH] SettingStorage infrastructure --- .../net/mamoe/mirai/console/MiraiConsole.kt | 13 +++- .../net/mamoe/mirai/console/plugin/Plugin.kt | 6 ++ .../mirai/console/plugin/PluginLoader.kt | 6 +- .../mirai/console/plugin/PluginManager.kt | 1 + .../console/plugin/builtin/JarPluginLoader.kt | 5 +- .../mirai/console/plugin/builtin/JvmPlugin.kt | 65 ++++++++++++++----- .../mamoe/mirai/console/setting/Setting.kt | 21 +++++- .../mirai/console/setting/SettingStorage.kt | 65 +++++++++++++++++++ .../mirai/console/command/TestCommands.kt | 8 ++- 9 files changed, 167 insertions(+), 23 deletions(-) create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/SettingStorage.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt index 6abed19eb..3f538b345 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt @@ -15,6 +15,8 @@ import kotlinx.io.charsets.Charset import net.mamoe.mirai.Bot import net.mamoe.mirai.console.plugin.PluginLoader 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.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiLogger @@ -41,7 +43,11 @@ object MiraiConsole : CoroutineScope, IMiraiConsole { override val mainLogger: MiraiLogger get() = instance.mainLogger override val coroutineContext: CoroutineContext get() = instance.coroutineContext - override val builtInPluginLoaders: List> = instance.builtInPluginLoaders + override val builtInPluginLoaders: List> get() = instance.builtInPluginLoaders + + @Suppress("CANNOT_WEAKEN_ACCESS_PRIVILEGE") + internal override val jvmSettingStorage: SettingStorage + get() = instance.jvmSettingStorage init { DefaultLogger = { identity -> this.newLogger(identity) } @@ -79,6 +85,11 @@ internal interface IMiraiConsole : CoroutineScope { * 内建加载器列表, 一般需要包含 [JarPluginLoader] */ val builtInPluginLoaders: List> + + /** + * 内建的供 [JvmPlugin] 使用的 [SettingStorage] + */ + val jvmSettingStorage: SettingStorage } /** diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt index f590be7a4..0b462367a 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt @@ -10,6 +10,7 @@ package net.mamoe.mirai.console.plugin import net.mamoe.mirai.console.plugin.builtin.JvmPlugin +import java.io.File /** * 表示一个 mirai-console 插件. @@ -22,4 +23,9 @@ interface Plugin { * 所属插件加载器实例 */ val loader: PluginLoader<*, *> + + /** + * 插件数据目录 + */ + val dataFolder: File } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginLoader.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginLoader.kt index f5345e7f3..d775269ad 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginLoader.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginLoader.kt @@ -11,6 +11,7 @@ package net.mamoe.mirai.console.plugin +import net.mamoe.mirai.console.plugin.builtin.JarPluginLoader import java.io.File /** @@ -59,7 +60,7 @@ open class PluginLoadException : RuntimeException { */ interface FilePluginLoader

: PluginLoader { /** - * 所支持的插件文件后缀, 不含 '.'. 如 [JarPluginLoader] 为 "jar" + * 所支持的插件文件后缀, 含 '.'. 如 [JarPluginLoader] 为 ".jar" */ val fileSuffix: String } @@ -70,6 +71,9 @@ abstract class AbstractFilePluginLoader

( private fun pluginsFilesSequence(): Sequence = PluginManager.pluginsDir.walk().filter { it.isFile && it.name.endsWith(fileSuffix, ignoreCase = true) } + /** + * 读取扫描到的后缀与 [fileSuffix] 相同的文件中的 [PluginDescription] + */ protected abstract fun Sequence.mapToDescription(): List final override fun listPlugins(): List = pluginsFilesSequence().mapToDescription() diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt index 459951b2c..5b94228c5 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt @@ -24,6 +24,7 @@ inline fun PluginLoader<*, *>.unregister() = PluginManager.unregisterPluginLoade object PluginManager { val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() } + val pluginsDataFolder = File(MiraiConsole.rootDir, "data").apply { mkdir() } private val _pluginLoaders: MutableList> = mutableListOf() private val loadersLock: ReentrantLock = ReentrantLock() diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/builtin/JarPluginLoader.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/builtin/JarPluginLoader.kt index 2ad708b21..47855db46 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/builtin/JarPluginLoader.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/builtin/JarPluginLoader.kt @@ -14,6 +14,7 @@ import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.plugin.AbstractFilePluginLoader import net.mamoe.mirai.console.plugin.PluginLoadException import net.mamoe.mirai.console.plugin.PluginsLoader +import net.mamoe.mirai.console.setting.SettingStorage import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.yamlkt.Yaml import java.io.File @@ -24,7 +25,7 @@ import kotlin.reflect.full.createInstance /** * 内建的 Jar (JVM) 插件加载器 */ -object JarPluginLoader : AbstractFilePluginLoader("jar"), CoroutineScope { +object JarPluginLoader : AbstractFilePluginLoader(".jar"), CoroutineScope { private val logger: MiraiLogger by lazy { MiraiConsole.newLogger(JarPluginLoader::class.simpleName!!) } @@ -46,6 +47,8 @@ object JarPluginLoader : AbstractFilePluginLoader.mapToDescription(): List { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/builtin/JvmPlugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/builtin/JvmPlugin.kt index b0da3802c..74c28cbb8 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/builtin/JvmPlugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/builtin/JvmPlugin.kt @@ -12,18 +12,20 @@ package net.mamoe.mirai.console.plugin.builtin import kotlinx.atomicfu.locks.withLock -import kotlinx.coroutines.CoroutineExceptionHandler -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.* import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.plugin.Plugin 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.utils.MiraiLogger +import java.io.File +import java.util.* import java.util.concurrent.locks.ReentrantLock import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +import kotlin.reflect.KProperty /** @@ -40,7 +42,7 @@ interface JvmPlugin : Plugin, CoroutineScope { val description: JvmPluginDescription /** 所属插件加载器实例 */ - override val loader: PluginLoader<*, *> get() = JarPluginLoader + override val loader: JarPluginLoader get() = JarPluginLoader @JvmDefault fun onLoad() { @@ -72,36 +74,46 @@ abstract class JavaPlugin @JvmOverloads constructor( abstract class KotlinPlugin @JvmOverloads constructor( parentCoroutineContext: CoroutineContext = EmptyCoroutineContext ) : 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.job: Job where T : CoroutineScope, T : Plugin get() = this.coroutineContext[Job]!! + internal sealed class JvmPluginImpl( parentCoroutineContext: CoroutineContext ) : JvmPlugin, CoroutineScope { + // region JvmPlugin /** * Initialized immediately after construction of [JvmPluginImpl] instance */ @Suppress("PropertyName") internal lateinit var _description: JvmPluginDescription - // for future use - @Suppress("PropertyName") - @JvmField - internal var _intrinsicCoroutineContext: CoroutineContext = EmptyCoroutineContext - override val description: JvmPluginDescription get() = _description 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 + override val dataFolder: File by lazy { + File(PluginManager.pluginsDataFolder, description.name).apply { mkdir() } + } + internal fun internalOnDisable() { firstRun = false this.onDisable() @@ -116,6 +128,22 @@ internal sealed class JvmPluginImpl( 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 { return coroutineContextInitializer().also { _coroutineContext = it } } @@ -126,4 +154,5 @@ internal sealed class JvmPluginImpl( get() = _coroutineContext ?: contextUpdateLock.withLock { _coroutineContext ?: refreshCoroutineContext() } + // endregion } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt index 0c42bc83d..2e857e110 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt @@ -13,6 +13,7 @@ package net.mamoe.mirai.console.setting import kotlinx.serialization.KSerializer import net.mamoe.mirai.console.setting.internal.SettingImpl +import net.mamoe.mirai.console.setting.internal.serialNameOrPropertyName import net.mamoe.mirai.utils.MiraiExperimentalAPI import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -33,6 +34,12 @@ typealias Comment = net.mamoe.yamlkt.Comment */ @Suppress("EXPOSED_SUPER_CLASS") abstract class Setting : SettingImpl() { + + data class PropertyInfo( + val serialName: String, + val annotations: List + ) + /** * 这个配置的名称, 仅对于顶层配置有效. */ @@ -43,6 +50,16 @@ abstract class Setting : SettingImpl() { ?: error("Names should be assigned to anonymous classes manually by overriding serialName") + // for Java only + fun 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<*> ): ReadWriteProperty { 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 } + abstract override fun onElementChanged(value: Value<*>) + override fun toString(): String = yamlForToString.stringify(this.serializer, this) } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/SettingStorage.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/SettingStorage.kt new file mode 100644 index 000000000..9d14cfcc2 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/SettingStorage.kt @@ -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 = ArrayList() + private val updaterSerializer: KSerializer = object : KSerializer { + 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") + } +} \ No newline at end of file diff --git a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommands.kt b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommands.kt index 4b8e70f3a..fd949483e 100644 --- a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommands.kt +++ b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommands.kt @@ -13,13 +13,19 @@ package net.mamoe.mirai.console.command import net.mamoe.mirai.Bot import net.mamoe.mirai.console.plugin.builtin.KotlinPlugin +import net.mamoe.mirai.console.setting.value import net.mamoe.mirai.message.data.* import org.junit.jupiter.api.Test import kotlin.test.assertEquals -val plugin: KotlinPlugin = object : KotlinPlugin() { +val plugin = MyPlugin() +class MyPlugin : KotlinPlugin() { + + inner class MySetting : PluginSetting() { + val int by value(1) + } } /*