mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-27 17:00:14 +08:00
Introduce PluginData and PluginConfig
This commit is contained in:
parent
9cc05f682b
commit
ed2ef37304
@ -116,7 +116,7 @@ dependencies:
|
|||||||
|
|
||||||
|
|
||||||
[`Plugin`]: src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt
|
[`Plugin`]: src/main/kotlin/net/mamoe/mirai/console/plugin/Plugin.kt
|
||||||
[`PluginDescription`]: src/main/kotlin/net/mamoe/mirai/console/plugin/description.kt
|
[`PluginDescription`]: src/main/kotlin/net/mamoe/mirai/console/plugin/description/PluginDescription.kt
|
||||||
[`PluginLoader`]: src/main/kotlin/net/mamoe/mirai/console/plugin/PluginLoader.kt
|
[`PluginLoader`]: src/main/kotlin/net/mamoe/mirai/console/plugin/PluginLoader.kt
|
||||||
[`PluginManager`]: src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt
|
[`PluginManager`]: src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt
|
||||||
[`JarPluginLoader`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JarPluginLoader.kt
|
[`JarPluginLoader`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JarPluginLoader.kt
|
||||||
|
@ -60,8 +60,7 @@ kotlin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileAndRuntime("net.mamoe:mirai-core:${Versions.core}")
|
implementation("net.mamoe:mirai-core:${Versions.core}")
|
||||||
compileAndRuntime(kotlin("stdlib-jdk8", Versions.kotlinStdlib))
|
|
||||||
|
|
||||||
implementation(kotlinx("serialization-core", Versions.serialization))
|
implementation(kotlinx("serialization-core", Versions.serialization))
|
||||||
implementation(kotlin("reflect"))
|
implementation(kotlin("reflect"))
|
||||||
@ -92,6 +91,8 @@ ext.apply {
|
|||||||
dependencyFilter.exclude {
|
dependencyFilter.exclude {
|
||||||
when ("${it.moduleGroup}:${it.moduleName}") {
|
when ("${it.moduleGroup}:${it.moduleName}") {
|
||||||
"net.mamoe:mirai-core" -> true
|
"net.mamoe:mirai-core" -> true
|
||||||
|
"org.jetbrains.kotlin:kotlin-stdlib" -> true
|
||||||
|
"org.jetbrains.kotlin:kotlin-stdlib-jdk8" -> true
|
||||||
"net.mamoe:mirai-core-qqandroid" -> true
|
"net.mamoe:mirai-core-qqandroid" -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ public abstract class AbstractPluginData : PluginData, PluginDataImpl() {
|
|||||||
/**
|
/**
|
||||||
* 供手动实现时值跟踪使用 (如 Java 用户). 一般 Kotlin 用户需使用 [provideDelegate]
|
* 供手动实现时值跟踪使用 (如 Java 用户). 一般 Kotlin 用户需使用 [provideDelegate]
|
||||||
*/
|
*/
|
||||||
|
@ConsoleExperimentalAPI
|
||||||
public override fun <T : SerializerAwareValue<*>> T.track(valueName: String): T =
|
public override fun <T : SerializerAwareValue<*>> T.track(valueName: String): T =
|
||||||
apply { valueNodes.add(ValueNode(valueName, this, this.serializer)) }
|
apply { valueNodes.add(ValueNode(valueName, this, this.serializer)) }
|
||||||
|
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.data
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 链接自动保存的 [PluginConfig].
|
||||||
|
*
|
||||||
|
* 当任一相关 [Value] 的值被修改时, 将在一段时间无其他修改时保存
|
||||||
|
*
|
||||||
|
* 若 [AutoSavePluginDataHolder.coroutineContext] 含有 [Job], 则 [AutoSavePluginData] 会通过 [Job.invokeOnCompletion] 在 Job 完结时触发自动保存.
|
||||||
|
*
|
||||||
|
* @see PluginConfig
|
||||||
|
* @see AutoSavePluginData
|
||||||
|
*/
|
||||||
|
@ExperimentalPluginConfig
|
||||||
|
public open class AutoSavePluginConfig : AutoSavePluginData(), PluginConfig
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||||
|
*
|
||||||
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
|
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
||||||
|
*
|
||||||
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("unused", "PropertyName", "PrivatePropertyName")
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.data
|
||||||
|
|
||||||
|
import kotlinx.atomicfu.atomic
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import net.mamoe.mirai.console.internal.plugin.updateWhen
|
||||||
|
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||||
|
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||||
|
import net.mamoe.mirai.utils.currentTimeMillis
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 链接自动保存的 [PluginData].
|
||||||
|
*
|
||||||
|
* 当任一相关 [Value] 的值被修改时, 将在一段时间无其他修改时保存
|
||||||
|
*
|
||||||
|
* 若 [AutoSavePluginDataHolder.coroutineContext] 含有 [Job], 则 [AutoSavePluginData] 会通过 [Job.invokeOnCompletion] 在 Job 完结时触发自动保存.
|
||||||
|
*
|
||||||
|
* @see PluginData
|
||||||
|
*/
|
||||||
|
public open class AutoSavePluginData private constructor(
|
||||||
|
@Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any?
|
||||||
|
) : AbstractPluginData() {
|
||||||
|
private lateinit var owner_: AutoSavePluginDataHolder
|
||||||
|
private val autoSaveIntervalMillis_: LongRange get() = owner_.autoSaveIntervalMillis
|
||||||
|
private lateinit var storage_: PluginDataStorage
|
||||||
|
|
||||||
|
public constructor() : this(null)
|
||||||
|
|
||||||
|
|
||||||
|
override fun onStored(owner: PluginDataHolder, storage: PluginDataStorage) {
|
||||||
|
check(owner is AutoSavePluginDataHolder) { "owner must be AutoSavePluginDataHolder for AutoSavePluginData" }
|
||||||
|
check(!this::storage_.isInitialized) { "storage is already initialized" }
|
||||||
|
this.storage_ = storage
|
||||||
|
this.owner_ = owner
|
||||||
|
|
||||||
|
owner_.coroutineContext[Job]?.invokeOnCompletion { doSave() }
|
||||||
|
|
||||||
|
if (shouldPerformAutoSaveWheneverChanged()) {
|
||||||
|
owner_.launch {
|
||||||
|
while (isActive) {
|
||||||
|
delay(autoSaveIntervalMillis_.last) // 定时自动保存一次, 用于 kts 序列化的对象
|
||||||
|
doSave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Volatile
|
||||||
|
internal var lastAutoSaveJob_: Job? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
internal val currentFirstStartTime_ = atomic(0L)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return `true` 时, 一段时间后, 即使无属性改变, 也会进行保存.
|
||||||
|
*/
|
||||||
|
@ConsoleExperimentalAPI
|
||||||
|
protected open fun shouldPerformAutoSaveWheneverChanged(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private val updaterBlock: suspend CoroutineScope.() -> Unit = {
|
||||||
|
if (::storage_.isInitialized) {
|
||||||
|
currentFirstStartTime_.updateWhen({ it == 0L }, { currentTimeMillis })
|
||||||
|
|
||||||
|
delay(autoSaveIntervalMillis_.first.coerceAtLeast(1000)) // for safety
|
||||||
|
|
||||||
|
if (lastAutoSaveJob_ == this.coroutineContext[Job]) {
|
||||||
|
doSave()
|
||||||
|
} else {
|
||||||
|
if (currentFirstStartTime_.updateWhen(
|
||||||
|
{ currentTimeMillis - it >= autoSaveIntervalMillis_.last },
|
||||||
|
{ 0 })
|
||||||
|
) doSave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("RedundantVisibilityModifier")
|
||||||
|
@ConsoleInternalAPI
|
||||||
|
public final override fun onValueChanged(value: Value<*>) {
|
||||||
|
if (::owner_.isInitialized) {
|
||||||
|
lastAutoSaveJob_ = owner_.launch(block = updaterBlock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doSave() = storage_.store(owner_, this)
|
||||||
|
}
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.console.data
|
package net.mamoe.mirai.console.data
|
||||||
|
|
||||||
|
import net.mamoe.mirai.console.data.java.JAutoSavePluginConfig
|
||||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||||
import kotlin.annotation.AnnotationTarget.*
|
import kotlin.annotation.AnnotationTarget.*
|
||||||
|
|
||||||
@ -27,14 +28,14 @@ import kotlin.annotation.AnnotationTarget.*
|
|||||||
* 在 [PluginData] 的示例基础上, 修改对象定义
|
* 在 [PluginData] 的示例基础上, 修改对象定义
|
||||||
* ```
|
* ```
|
||||||
* // 原
|
* // 原
|
||||||
* object MyPluginData : PluginData by PluginMain.loadPluginData()
|
* object MyPluginData : AutoSavePluginData()
|
||||||
* // 修改为
|
* // 修改为
|
||||||
* object MyPluginConfig : PluginConfig by PluginMain.loadPluginConfig()
|
* object MyPluginConfig : AutoSavePluginConfig()
|
||||||
* ```
|
* ```
|
||||||
* 即可将一个 [PluginData] 变更为 [PluginConfig].
|
* 即可将一个 [PluginData] 变更为 [PluginConfig].
|
||||||
*
|
*
|
||||||
* ### Java
|
* ### Java
|
||||||
* 见 [JPluginConfig]
|
* 见 [JAutoSavePluginConfig]
|
||||||
*
|
*
|
||||||
* @see PluginData
|
* @see PluginData
|
||||||
*/
|
*/
|
||||||
|
@ -18,15 +18,17 @@
|
|||||||
package net.mamoe.mirai.console.data
|
package net.mamoe.mirai.console.data
|
||||||
|
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
|
import net.mamoe.mirai.console.data.java.JAutoSavePluginData
|
||||||
import net.mamoe.mirai.console.internal.data.*
|
import net.mamoe.mirai.console.internal.data.*
|
||||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||||
import net.mamoe.mirai.console.plugin.jvm.loadPluginData
|
import net.mamoe.mirai.console.plugin.jvm.reloadPluginData
|
||||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||||
import kotlin.internal.LowPriorityInOverloadResolution
|
import kotlin.internal.LowPriorityInOverloadResolution
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
|
import kotlin.reflect.full.findAnnotation
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 一个插件内部的, 对用户隐藏的数据对象. 可包含对多个 [Value] 的值变更的跟踪.
|
* 一个插件内部的, 对用户隐藏的数据对象. 可包含对多个 [Value] 的值变更的跟踪.
|
||||||
@ -47,7 +49,7 @@ import kotlin.reflect.KType
|
|||||||
* ```
|
* ```
|
||||||
* object PluginMain : KotlinPlugin()
|
* object PluginMain : KotlinPlugin()
|
||||||
*
|
*
|
||||||
* object MyPluginData : PluginData by PluginMain.loadPluginData() {
|
* object MyPluginData : AutoSavePluginData() {
|
||||||
* val list: MutableList<String> by value(mutableListOf("a", "b")) // mutableListOf("a", "b") 是初始值, 可以省略
|
* val list: MutableList<String> by value(mutableListOf("a", "b")) // mutableListOf("a", "b") 是初始值, 可以省略
|
||||||
* val custom: Map<Long, CustomData> by value() // 使用 kotlinx-serialization 序列化的类型. (目前还不支持)
|
* val custom: Map<Long, CustomData> by value() // 使用 kotlinx-serialization 序列化的类型. (目前还不支持)
|
||||||
* var long: Long by value(0) // 允许 var
|
* var long: Long by value(0) // 允许 var
|
||||||
@ -69,11 +71,9 @@ import kotlin.reflect.KType
|
|||||||
*
|
*
|
||||||
* ### 使用 Java
|
* ### 使用 Java
|
||||||
*
|
*
|
||||||
* 参考 [JPluginData]
|
* 参考 [JAutoSavePluginData]
|
||||||
*
|
*
|
||||||
* **注意**: 由于实现特殊, 请不要在初始化 Value 时就使用 `.get()`. 这可能会导致自动保存追踪失效. 必须在使用时才调用 `.get()` 获取真实数据对象.
|
* @see JvmPlugin.reloadPluginData 通过 [JvmPlugin] 获取指定 [PluginData] 实例.
|
||||||
*
|
|
||||||
* @see JvmPlugin.loadPluginData 通过 [JvmPlugin] 获取指定 [PluginData] 实例.
|
|
||||||
* @see PluginDataStorage [PluginData] 存储仓库
|
* @see PluginDataStorage [PluginData] 存储仓库
|
||||||
*/
|
*/
|
||||||
public interface PluginData {
|
public interface PluginData {
|
||||||
@ -88,6 +88,18 @@ public interface PluginData {
|
|||||||
@ConsoleExperimentalAPI
|
@ConsoleExperimentalAPI
|
||||||
public val valueNodes: MutableList<ValueNode<*>>
|
public val valueNodes: MutableList<ValueNode<*>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这个 [PluginData] 保存时使用的名称. 默认通过 [ValueName] 获取, 否则使用 [类全名][KClass.qualifiedName] (即 [Class.getCanonicalName])
|
||||||
|
*/
|
||||||
|
@ConsoleExperimentalAPI
|
||||||
|
public val saveName: String
|
||||||
|
get() {
|
||||||
|
val clazz = this::class
|
||||||
|
return clazz.findAnnotation<ValueName>()?.value
|
||||||
|
?: clazz.qualifiedName
|
||||||
|
?: throw IllegalArgumentException("Cannot find a serial name for ${this::class}")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 由 [provideDelegate] 创建, 来自一个通过 `by value` 初始化的属性节点.
|
* 由 [provideDelegate] 创建, 来自一个通过 `by value` 初始化的属性节点.
|
||||||
*/
|
*/
|
||||||
@ -123,6 +135,7 @@ public interface PluginData {
|
|||||||
/**
|
/**
|
||||||
* 供手动实现时值跟踪使用 (如 Java 用户). 一般 Kotlin 用户需使用 [provideDelegate]
|
* 供手动实现时值跟踪使用 (如 Java 用户). 一般 Kotlin 用户需使用 [provideDelegate]
|
||||||
*/
|
*/
|
||||||
|
@ConsoleExperimentalAPI
|
||||||
public fun <T : SerializerAwareValue<*>> T.track(
|
public fun <T : SerializerAwareValue<*>> T.track(
|
||||||
/**
|
/**
|
||||||
* 值名称.
|
* 值名称.
|
||||||
@ -153,7 +166,7 @@ public interface PluginData {
|
|||||||
* 当这个 [PluginData] 被放入一个 [PluginDataStorage] 时调用
|
* 当这个 [PluginData] 被放入一个 [PluginDataStorage] 时调用
|
||||||
*/
|
*/
|
||||||
@ConsoleInternalAPI
|
@ConsoleInternalAPI
|
||||||
public fun setStorage(storage: PluginDataStorage)
|
public fun onStored(owner: PluginDataHolder, storage: PluginDataStorage)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -161,12 +174,14 @@ public interface PluginData {
|
|||||||
*
|
*
|
||||||
* 如, 对于
|
* 如, 对于
|
||||||
* ```
|
* ```
|
||||||
* object MyData : PluginData {
|
* object MyData : AutoSavePluginData(PluginMain) {
|
||||||
* val list: List<String> by value()
|
* val list: List<String> by value()
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* val value: Value<List<String>> = MyData.findBackingFieldValue(MyData::list)
|
* val value: Value<List<String>> = MyData.findBackingFieldValue(MyData::list)
|
||||||
* ```
|
* ```
|
||||||
|
*
|
||||||
|
* @see PluginData
|
||||||
*/
|
*/
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
public fun <T> PluginData.findBackingFieldValue(property: KProperty<T>): Value<out T>? =
|
public fun <T> PluginData.findBackingFieldValue(property: KProperty<T>): Value<out T>? =
|
||||||
@ -177,7 +192,7 @@ public fun <T> PluginData.findBackingFieldValue(property: KProperty<T>): Value<o
|
|||||||
*
|
*
|
||||||
* 如, 对于
|
* 如, 对于
|
||||||
* ```
|
* ```
|
||||||
* object MyData : PluginData {
|
* object MyData : AutoSavePluginData(PluginMain) {
|
||||||
* @ValueName("theList")
|
* @ValueName("theList")
|
||||||
* val list: List<String> by value()
|
* val list: List<String> by value()
|
||||||
* val int: Int by value()
|
* val int: Int by value()
|
||||||
@ -186,6 +201,8 @@ public fun <T> PluginData.findBackingFieldValue(property: KProperty<T>): Value<o
|
|||||||
* val value: Value<List<String>> = MyData.findBackingFieldValue("theList") // 需使用 @ValueName 标注的名称
|
* val value: Value<List<String>> = MyData.findBackingFieldValue("theList") // 需使用 @ValueName 标注的名称
|
||||||
* val intValue: Value<Int> = MyData.findBackingFieldValue("int")
|
* val intValue: Value<Int> = MyData.findBackingFieldValue("int")
|
||||||
* ```
|
* ```
|
||||||
|
*
|
||||||
|
* @see PluginData
|
||||||
*/
|
*/
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
public fun <T> PluginData.findBackingFieldValue(propertyValueName: String): Value<out T>? {
|
public fun <T> PluginData.findBackingFieldValue(propertyValueName: String): Value<out T>? {
|
||||||
@ -198,12 +215,14 @@ public fun <T> PluginData.findBackingFieldValue(propertyValueName: String): Valu
|
|||||||
*
|
*
|
||||||
* 如, 对于
|
* 如, 对于
|
||||||
* ```
|
* ```
|
||||||
* object MyData : PluginData {
|
* object MyData : AutoSavePluginData(PluginMain) {
|
||||||
* val list: List<String> by value()
|
* val list: List<String> by value()
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* val value: PluginData.ValueNode<List<String>> = MyData.findBackingFieldValueNode(MyData::list)
|
* val value: PluginData.ValueNode<List<String>> = MyData.findBackingFieldValueNode(MyData::list)
|
||||||
* ```
|
* ```
|
||||||
|
*
|
||||||
|
* @see PluginData
|
||||||
*/
|
*/
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
public fun <T> PluginData.findBackingFieldValueNode(property: KProperty<T>): PluginData.ValueNode<out T>? {
|
public fun <T> PluginData.findBackingFieldValueNode(property: KProperty<T>): PluginData.ValueNode<out T>? {
|
||||||
|
@ -13,8 +13,7 @@ package net.mamoe.mirai.console.data
|
|||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import net.mamoe.mirai.console.data.PluginDataStorage.Companion.load
|
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||||
import net.mamoe.mirai.console.internal.data.AutoSavePluginData
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 可以持有相关 [PluginData] 实例的对象, 作为 [PluginData] 实例的拥有者.
|
* 可以持有相关 [PluginData] 实例的对象, 作为 [PluginData] 实例的拥有者.
|
||||||
@ -24,6 +23,7 @@ import net.mamoe.mirai.console.internal.data.AutoSavePluginData
|
|||||||
*
|
*
|
||||||
* @see AutoSavePluginDataHolder 支持自动保存
|
* @see AutoSavePluginDataHolder 支持自动保存
|
||||||
*/
|
*/
|
||||||
|
@ConsoleExperimentalAPI
|
||||||
public interface PluginDataHolder {
|
public interface PluginDataHolder {
|
||||||
/**
|
/**
|
||||||
* 保存时使用的分类名
|
* 保存时使用的分类名
|
||||||
@ -36,6 +36,7 @@ public interface PluginDataHolder {
|
|||||||
*
|
*
|
||||||
* @see net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
* @see net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||||
*/
|
*/
|
||||||
|
@ConsoleExperimentalAPI
|
||||||
public interface AutoSavePluginDataHolder : PluginDataHolder, CoroutineScope {
|
public interface AutoSavePluginDataHolder : PluginDataHolder, CoroutineScope {
|
||||||
/**
|
/**
|
||||||
* [AutoSavePluginData] 每次自动保存时间间隔
|
* [AutoSavePluginData] 每次自动保存时间间隔
|
||||||
|
@ -15,16 +15,15 @@ import net.mamoe.mirai.console.internal.data.MemoryPluginDataStorageImpl
|
|||||||
import net.mamoe.mirai.console.internal.data.MultiFilePluginDataStorageImpl
|
import net.mamoe.mirai.console.internal.data.MultiFilePluginDataStorageImpl
|
||||||
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||||
|
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [数据对象][PluginData] 存储仓库.
|
* [数据对象][PluginData] 存储仓库.
|
||||||
*
|
*
|
||||||
* ## 职责
|
* ## 职责
|
||||||
* [PluginDataStorage] 类似于一个数据库, 它只承担将序列化之后的数据保存到数据库中, 和从数据库取出这个对象的任务.
|
* [PluginDataStorage] 类似于一个数据库, 它只承担将序列化之后的数据保存到数据库中, 和从数据库取出这个对象的任务.
|
||||||
* [PluginDataStorage] 不考虑一个 []
|
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 此为较低层的 API, 一般插件开发者不会接触.
|
* 此为较低层的 API, 一般插件开发者不会接触.
|
||||||
@ -34,25 +33,50 @@ import kotlin.reflect.KClass
|
|||||||
* @see PluginDataHolder
|
* @see PluginDataHolder
|
||||||
* @see JarPluginLoader.dataStorage
|
* @see JarPluginLoader.dataStorage
|
||||||
*/
|
*/
|
||||||
|
@ConsoleExperimentalAPI
|
||||||
public interface PluginDataStorage {
|
public interface PluginDataStorage {
|
||||||
/**
|
/**
|
||||||
* 读取一个实例. 在 [T] 实例创建后 [设置 [PluginDataStorage]][PluginData.setStorage]
|
* 读取一个实例. 在 [T] 实例创建后 [设置 [PluginDataStorage]][PluginData.onStored]
|
||||||
*/
|
*/
|
||||||
public fun <T : PluginData> load(holder: PluginDataHolder, dataClass: Class<T>): T
|
public fun load(holder: PluginDataHolder, instance: PluginData)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存一个实例.
|
* 保存一个实例.
|
||||||
*
|
*
|
||||||
* **实现细节**: 调用 [PluginData.updaterSerializer], 将
|
* **实现细节**: 调用 [PluginData.updaterSerializer], 将
|
||||||
*/
|
*/
|
||||||
public fun store(holder: PluginDataHolder, pluginData: PluginData)
|
public fun store(holder: PluginDataHolder, instance: PluginData)
|
||||||
|
|
||||||
|
/*
|
||||||
|
public companion object {
|
||||||
|
/**
|
||||||
|
* 通过反射
|
||||||
|
* 读取一个实例.
|
||||||
|
*
|
||||||
|
* 在 [T] 实例创建后 [设置 [PluginDataStorage]][PluginData.setStorage]
|
||||||
|
*/
|
||||||
|
@ConsoleExperimentalAPI
|
||||||
|
@JvmStatic
|
||||||
|
public fun <T : PluginData> PluginDataStorage.load(holder: PluginDataHolder, dataClass: KClass<T>): T {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val instance = with(dataClass){
|
||||||
|
objectInstance
|
||||||
|
?: createInstanceOrNull()
|
||||||
|
?: throw IllegalArgumentException(
|
||||||
|
"Cannot create PluginData instance. Make sure dataClass is PluginData::class.java or a Kotlin's object, " +
|
||||||
|
"or has a constructor which either has no parameters or all parameters of which are optional"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
load(holder, instance)
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
public companion object {
|
|
||||||
/**
|
/**
|
||||||
* 读取一个实例. 在 [T] 实例创建后 [设置 [PluginDataStorage]][PluginData.setStorage]
|
* 读取一个实例. 在 [T] 实例创建后 [设置 [PluginDataStorage]][PluginData.setStorage]
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
public fun <T : PluginData> PluginDataStorage.load(holder: PluginDataHolder, dataClass: KClass<T>): T =
|
public fun <T : PluginData> PluginDataStorage.load(holder: PluginDataHolder, dataClass: Class<T>): T =
|
||||||
this.load(holder, dataClass.java)
|
this.load(holder, dataClass.java)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,42 +85,25 @@ public interface PluginDataStorage {
|
|||||||
@JvmSynthetic
|
@JvmSynthetic
|
||||||
public inline fun <reified T : PluginData> PluginDataStorage.load(holder: PluginDataHolder): T =
|
public inline fun <reified T : PluginData> PluginDataStorage.load(holder: PluginDataHolder): T =
|
||||||
this.load(holder, T::class)
|
this.load(holder, T::class)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在内存存储所有 [PluginData] 实例的 [PluginDataStorage]. 在内存数据丢失后相关 [PluginData] 实例也会丢失.
|
* 在内存存储所有 [PluginData] 实例的 [PluginDataStorage]. 在内存数据丢失后相关 [PluginData] 实例也会丢失.
|
||||||
* @see PluginDataStorage
|
* @see PluginDataStorage
|
||||||
*/
|
*/
|
||||||
public interface MemoryPluginDataStorage : PluginDataStorage, Map<Class<out PluginData>, PluginData> {
|
@ConsoleExperimentalAPI
|
||||||
/**
|
public interface MemoryPluginDataStorage : PluginDataStorage {
|
||||||
* 当任一 [PluginData] 实例拥有的 [Value] 的值被改变后调用的回调函数.
|
|
||||||
*/
|
|
||||||
public fun interface OnChangedCallback {
|
|
||||||
public fun onChanged(storage: MemoryPluginDataStorage, value: Value<*>)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 无任何操作的 [OnChangedCallback]
|
|
||||||
* @see OnChangedCallback
|
|
||||||
*/
|
|
||||||
public object NoOp : OnChangedCallback {
|
|
||||||
public override fun onChanged(storage: MemoryPluginDataStorage, value: Value<*>) {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
/**
|
/**
|
||||||
* 创建一个 [MemoryPluginDataStorage] 实例.
|
* 创建一个 [MemoryPluginDataStorage] 实例.
|
||||||
*
|
|
||||||
* @param onChanged 当任一 [PluginData] 实例拥有的 [Value] 的值被改变后调用的回调函数.
|
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@JvmName("create")
|
@JvmName("create")
|
||||||
// @JvmOverloads
|
// @JvmOverloads
|
||||||
public operator fun invoke(onChanged: OnChangedCallback = OnChangedCallback.NoOp): MemoryPluginDataStorage =
|
public operator fun invoke(): MemoryPluginDataStorage = MemoryPluginDataStorageImpl()
|
||||||
MemoryPluginDataStorageImpl(onChanged)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,14 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.mamoe.mirai.console.data
|
@file:Suppress("unused", "EXPOSED_SUPER_CLASS")
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.data.java
|
||||||
|
|
||||||
|
import net.mamoe.mirai.console.data.AutoSavePluginConfig
|
||||||
|
import net.mamoe.mirai.console.data.ExperimentalPluginConfig
|
||||||
|
import net.mamoe.mirai.console.data.PluginConfig
|
||||||
|
import net.mamoe.mirai.console.data.PluginData
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 一个插件的配置数据, 用于和用户交互.
|
* 一个插件的配置数据, 用于和用户交互.
|
||||||
@ -19,16 +26,17 @@ package net.mamoe.mirai.console.data
|
|||||||
*
|
*
|
||||||
* ### 实现
|
* ### 实现
|
||||||
*
|
*
|
||||||
* 在 [JPluginData] 的示例基础上, 修改类定义
|
* 在 [JAutoSavePluginData] 的示例基础上, 修改类定义
|
||||||
* ```java
|
* ```java
|
||||||
* // 原
|
* // 原
|
||||||
* public class AccountPluginData extends JPluginData {
|
* public class AccountPluginData extends JAutoSavePluginData {
|
||||||
* // 修改为
|
* // 修改为
|
||||||
* public class AccountPluginConfig extends JPluginConfig {
|
* public class AccountPluginConfig extends JAutoSavePluginConfig {
|
||||||
* ```
|
* ```
|
||||||
* 即可将一个 [PluginData] 变更为 [PluginConfig].
|
* 即可将一个 [PluginData] 变更为 [PluginConfig].
|
||||||
*
|
*
|
||||||
* @see JPluginData
|
* @see JAutoSavePluginData
|
||||||
* @see PluginConfig
|
* @see PluginConfig
|
||||||
*/
|
*/
|
||||||
public abstract class JPluginConfig(delegate: PluginData) : JPluginData(delegate), PluginConfig
|
@ExperimentalPluginConfig
|
||||||
|
public abstract class JAutoSavePluginConfig : AutoSavePluginConfig(), PluginConfig
|
@ -7,10 +7,11 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@file:Suppress("unused")
|
@file:Suppress("unused", "EXPOSED_SUPER_CLASS")
|
||||||
|
|
||||||
package net.mamoe.mirai.console.data
|
package net.mamoe.mirai.console.data.java
|
||||||
|
|
||||||
|
import net.mamoe.mirai.console.data.*
|
||||||
import net.mamoe.mirai.console.internal.data.cast
|
import net.mamoe.mirai.console.internal.data.cast
|
||||||
import net.mamoe.mirai.console.internal.data.setValueBySerializer
|
import net.mamoe.mirai.console.internal.data.setValueBySerializer
|
||||||
import net.mamoe.mirai.console.internal.data.valueImpl
|
import net.mamoe.mirai.console.internal.data.valueImpl
|
||||||
@ -27,18 +28,19 @@ import kotlin.reflect.full.createType
|
|||||||
* ```
|
* ```
|
||||||
* // PluginMain.java
|
* // PluginMain.java
|
||||||
* public final class PluginMain extends JavaPlugin {
|
* public final class PluginMain extends JavaPlugin {
|
||||||
* public static PluginMain INSTANCE = null;
|
* public static PluginMain INSTANCE;
|
||||||
* public PluginMain() {
|
* public PluginMain() {
|
||||||
* INSTANCE = this;
|
* INSTANCE = this;
|
||||||
|
* this.reloadPluginData(MyPluginData.INSTANCE); // 读取文件等
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* // MyPluginData.java
|
* // MyPluginData.java
|
||||||
* public class AccountPluginData extends JPluginData {
|
* public class MyPluginData extends JAutoSavePluginData {
|
||||||
* public static AccountPluginData INSTANCE;
|
* public static final MyPluginData INSTANCE = new MyPluginData();
|
||||||
*
|
*
|
||||||
* public AccountPluginData() {
|
* private MyPluginData() {
|
||||||
* super(PluginMain.INSTANCE.loadPluginData(AccountPluginData.class));
|
* super(PluginMain.INSTANCE);
|
||||||
* INSTANCE = this;
|
* INSTANCE = this;
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
@ -64,57 +66,57 @@ import kotlin.reflect.full.createType
|
|||||||
* theList.set();
|
* theList.set();
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
* **注意**: 由于实现特殊, 请不要在初始化 Value 时就使用 `.get()`. 这可能会导致自动保存追踪失效. 必须在使用时才调用 `.get()` 获取真实数据对象.
|
||||||
|
*
|
||||||
* @see PluginData
|
* @see PluginData
|
||||||
*/
|
*/
|
||||||
public open class JPluginData(
|
public abstract class JAutoSavePluginData : AutoSavePluginData(), PluginConfig {
|
||||||
private val delegate: PluginData
|
|
||||||
) : PluginData by delegate {
|
|
||||||
//// region JPluginData_value_primitives CODEGEN ////
|
//// region JPluginData_value_primitives CODEGEN ////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个 [Byte] 类型的 [Value], 并设置初始值为 [default]
|
* 创建一个 [Byte] 类型的 [Value], 并设置初始值为 [default]
|
||||||
*/
|
*/
|
||||||
public fun value(default: Byte): SerializerAwareValue<Byte> = delegate.valueImpl(default)
|
public fun value(default: Byte): SerializerAwareValue<Byte> = valueImpl(default)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个 [Short] 类型的 [Value], 并设置初始值为 [default]
|
* 创建一个 [Short] 类型的 [Value], 并设置初始值为 [default]
|
||||||
*/
|
*/
|
||||||
public fun value(default: Short): SerializerAwareValue<Short> = delegate.valueImpl(default)
|
public fun value(default: Short): SerializerAwareValue<Short> = valueImpl(default)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个 [Int] 类型的 [Value], 并设置初始值为 [default]
|
* 创建一个 [Int] 类型的 [Value], 并设置初始值为 [default]
|
||||||
*/
|
*/
|
||||||
public fun value(default: Int): SerializerAwareValue<Int> = delegate.valueImpl(default)
|
public fun value(default: Int): SerializerAwareValue<Int> = valueImpl(default)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个 [Long] 类型的 [Value], 并设置初始值为 [default]
|
* 创建一个 [Long] 类型的 [Value], 并设置初始值为 [default]
|
||||||
*/
|
*/
|
||||||
public fun value(default: Long): SerializerAwareValue<Long> = delegate.valueImpl(default)
|
public fun value(default: Long): SerializerAwareValue<Long> = valueImpl(default)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个 [Float] 类型的 [Value], 并设置初始值为 [default]
|
* 创建一个 [Float] 类型的 [Value], 并设置初始值为 [default]
|
||||||
*/
|
*/
|
||||||
public fun value(default: Float): SerializerAwareValue<Float> = delegate.valueImpl(default)
|
public fun value(default: Float): SerializerAwareValue<Float> = valueImpl(default)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个 [Double] 类型的 [Value], 并设置初始值为 [default]
|
* 创建一个 [Double] 类型的 [Value], 并设置初始值为 [default]
|
||||||
*/
|
*/
|
||||||
public fun value(default: Double): SerializerAwareValue<Double> = delegate.valueImpl(default)
|
public fun value(default: Double): SerializerAwareValue<Double> = valueImpl(default)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个 [Char] 类型的 [Value], 并设置初始值为 [default]
|
* 创建一个 [Char] 类型的 [Value], 并设置初始值为 [default]
|
||||||
*/
|
*/
|
||||||
public fun value(default: Char): SerializerAwareValue<Char> = delegate.valueImpl(default)
|
public fun value(default: Char): SerializerAwareValue<Char> = valueImpl(default)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个 [Boolean] 类型的 [Value], 并设置初始值为 [default]
|
* 创建一个 [Boolean] 类型的 [Value], 并设置初始值为 [default]
|
||||||
*/
|
*/
|
||||||
public fun value(default: Boolean): SerializerAwareValue<Boolean> = delegate.valueImpl(default)
|
public fun value(default: Boolean): SerializerAwareValue<Boolean> = valueImpl(default)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个 [String] 类型的 [Value], 并设置初始值为 [default]
|
* 创建一个 [String] 类型的 [Value], 并设置初始值为 [default]
|
||||||
*/
|
*/
|
||||||
public fun value(default: String): SerializerAwareValue<String> = delegate.valueImpl(default)
|
public fun value(default: String): SerializerAwareValue<String> = valueImpl(default)
|
||||||
|
|
||||||
//// endregion JPluginData_value_primitives CODEGEN ////
|
//// endregion JPluginData_value_primitives CODEGEN ////
|
||||||
|
|
||||||
@ -134,7 +136,7 @@ public open class JPluginData(
|
|||||||
*/
|
*/
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
public fun <T : Any> typedValue(type: KType, default: T? = null): SerializerAwareValue<T> {
|
public fun <T : Any> typedValue(type: KType, default: T? = null): SerializerAwareValue<T> {
|
||||||
val value = delegate.valueImpl<T>(type, type.classifier!!.cast())
|
val value = valueImpl<T>(type, type.classifier!!.cast())
|
||||||
if (default != null) value.setValueBySerializer(default)
|
if (default != null) value.setValueBySerializer(default)
|
||||||
return value
|
return value
|
||||||
}
|
}
|
@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.console.internal.data
|
|
||||||
|
|
||||||
import kotlinx.atomicfu.atomic
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import net.mamoe.mirai.console.data.*
|
|
||||||
import net.mamoe.mirai.console.internal.plugin.updateWhen
|
|
||||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
|
||||||
import net.mamoe.mirai.utils.currentTimeMillis
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 链接自动保存的 [PluginData].
|
|
||||||
* 当任一相关 [Value] 的值被修改时, 将在一段时间无其他修改时保存
|
|
||||||
*
|
|
||||||
* 若 [AutoSavePluginDataHolder.coroutineContext] 含有 [Job], 则 [AutoSavePluginData] 会通过 [Job.invokeOnCompletion] 在 Job 完结时触发自动保存.
|
|
||||||
*/
|
|
||||||
internal open class AutoSavePluginData(
|
|
||||||
private val owner: AutoSavePluginDataHolder,
|
|
||||||
internal val originPluginDataClass: KClass<out PluginData>
|
|
||||||
) : AbstractPluginData(), PluginConfig {
|
|
||||||
|
|
||||||
private lateinit var storage: PluginDataStorage
|
|
||||||
|
|
||||||
override fun setStorage(storage: PluginDataStorage) {
|
|
||||||
check(!this::storage.isInitialized) { "storage is already initialized" }
|
|
||||||
this.storage = storage
|
|
||||||
|
|
||||||
if (shouldPerformAutoSaveWheneverChanged()) {
|
|
||||||
owner.launch {
|
|
||||||
while (isActive) {
|
|
||||||
delay(owner.autoSaveIntervalMillis.last) // 定时自动保存一次, 用于 kts 序列化的对象
|
|
||||||
doSave()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmField
|
|
||||||
@Volatile
|
|
||||||
internal var lastAutoSaveJob: Job? = null
|
|
||||||
|
|
||||||
@JvmField
|
|
||||||
internal val currentFirstStartTime = atomic(0L)
|
|
||||||
|
|
||||||
protected open fun shouldPerformAutoSaveWheneverChanged(): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
owner.coroutineContext[Job]?.invokeOnCompletion { doSave() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private val updaterBlock: suspend CoroutineScope.() -> Unit = {
|
|
||||||
if (::storage.isInitialized) {
|
|
||||||
currentFirstStartTime.updateWhen({ it == 0L }, { currentTimeMillis })
|
|
||||||
|
|
||||||
delay(owner.autoSaveIntervalMillis.first.coerceAtLeast(1000)) // for safety
|
|
||||||
|
|
||||||
if (lastAutoSaveJob == this.coroutineContext[Job]) {
|
|
||||||
doSave()
|
|
||||||
} else {
|
|
||||||
if (currentFirstStartTime.updateWhen(
|
|
||||||
{ currentTimeMillis - it >= owner.autoSaveIntervalMillis.last },
|
|
||||||
{ 0 })
|
|
||||||
) doSave()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("RedundantVisibilityModifier")
|
|
||||||
@ConsoleInternalAPI
|
|
||||||
public final override fun onValueChanged(value: Value<*>) {
|
|
||||||
lastAutoSaveJob = owner.launch(block = updaterBlock)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun doSave() = storage.store(owner, this)
|
|
||||||
}
|
|
@ -9,45 +9,20 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.console.internal.data
|
package net.mamoe.mirai.console.internal.data
|
||||||
|
|
||||||
import net.mamoe.mirai.console.data.*
|
import net.mamoe.mirai.console.data.MemoryPluginDataStorage
|
||||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
import net.mamoe.mirai.console.data.PluginData
|
||||||
|
import net.mamoe.mirai.console.data.PluginDataHolder
|
||||||
|
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||||
|
|
||||||
internal class MemoryPluginDataStorageImpl(
|
internal class MemoryPluginDataStorageImpl : PluginDataStorage, MemoryPluginDataStorage,
|
||||||
private val onChanged: MemoryPluginDataStorage.OnChangedCallback
|
|
||||||
) : PluginDataStorage, MemoryPluginDataStorage,
|
|
||||||
MutableMap<Class<out PluginData>, PluginData> by mutableMapOf() {
|
MutableMap<Class<out PluginData>, PluginData> by mutableMapOf() {
|
||||||
|
|
||||||
internal inner class MemoryPluginDataImpl : AbstractPluginData() {
|
|
||||||
@ConsoleInternalAPI
|
|
||||||
override fun onValueChanged(value: Value<*>) {
|
|
||||||
onChanged.onChanged(this@MemoryPluginDataStorageImpl, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setStorage(storage: PluginDataStorage) {
|
|
||||||
check(storage is MemoryPluginDataStorageImpl) { "storage is not MemoryPluginDataStorageImpl" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : PluginData> load(holder: PluginDataHolder, dataClass: Class<T>): T = (synchronized(this) {
|
override fun load(holder: PluginDataHolder, instance: PluginData) {
|
||||||
this.getOrPut(dataClass) {
|
instance.onStored(holder, this)
|
||||||
dataClass.kotlin.run {
|
|
||||||
objectInstance ?: createInstanceOrNull() ?: kotlin.run {
|
|
||||||
if (dataClass != PluginData::class.java) {
|
|
||||||
throw IllegalArgumentException(
|
|
||||||
"Cannot create PluginData instance. Make sure dataClass is PluginData::class.java or a Kotlin's object, " +
|
|
||||||
"or has a constructor which either has no parameters or all parameters of which are optional"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
MemoryPluginDataImpl()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} as T).also { it.setStorage(this) }
|
|
||||||
|
|
||||||
override fun store(holder: PluginDataHolder, pluginData: PluginData) {
|
override fun store(holder: PluginDataHolder, instance: PluginData) {
|
||||||
synchronized(this) {
|
// no-op
|
||||||
this[pluginData::class.java] = pluginData
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,75 +9,59 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.console.internal.data
|
package net.mamoe.mirai.console.internal.data
|
||||||
|
|
||||||
import net.mamoe.mirai.console.data.*
|
import net.mamoe.mirai.console.data.MultiFilePluginDataStorage
|
||||||
|
import net.mamoe.mirai.console.data.PluginData
|
||||||
|
import net.mamoe.mirai.console.data.PluginDataHolder
|
||||||
|
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||||
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
|
import net.mamoe.mirai.utils.SilentLogger
|
||||||
|
import net.mamoe.mirai.utils.debug
|
||||||
import net.mamoe.yamlkt.Yaml
|
import net.mamoe.yamlkt.Yaml
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
@Suppress("RedundantVisibilityModifier") // might be public in the future
|
@Suppress("RedundantVisibilityModifier") // might be public in the future
|
||||||
internal open class MultiFilePluginDataStorageImpl(
|
internal open class MultiFilePluginDataStorageImpl(
|
||||||
public final override val directoryPath: Path
|
public final override val directoryPath: Path,
|
||||||
|
private val logger: MiraiLogger = SilentLogger,
|
||||||
) : PluginDataStorage, MultiFilePluginDataStorage {
|
) : PluginDataStorage, MultiFilePluginDataStorage {
|
||||||
init {
|
init {
|
||||||
directoryPath.mkdir()
|
directoryPath.mkdir()
|
||||||
}
|
}
|
||||||
|
|
||||||
public override fun <T : PluginData> load(holder: PluginDataHolder, dataClass: Class<T>): T =
|
public override fun load(holder: PluginDataHolder, instance: PluginData) {
|
||||||
with(dataClass.kotlin) {
|
instance.onStored(holder, this)
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
val instance = objectInstance ?: this.createInstanceOrNull() ?: kotlin.run {
|
|
||||||
require(dataClass == PluginData::class.java) {
|
|
||||||
"Cannot create PluginData instance. Make sure dataClass is PluginData::class.java or a Kotlin's object, " +
|
|
||||||
"or has a constructor which either has no parameters or all parameters of which are optional"
|
|
||||||
}
|
|
||||||
if (holder is AutoSavePluginDataHolder) {
|
|
||||||
AutoSavePluginData(holder, this) as T?
|
|
||||||
} else null
|
|
||||||
} ?: throw IllegalArgumentException(
|
|
||||||
"Cannot create PluginData instance. Make sure 'holder' is a AutoSavePluginDataHolder, " +
|
|
||||||
"or 'data' is an object or has a constructor which either has no parameters or all parameters of which are optional"
|
|
||||||
)
|
|
||||||
|
|
||||||
val file = getPluginDataFile(holder, this)
|
val text = getPluginDataFile(holder, instance).readText()
|
||||||
file.createNewFile()
|
|
||||||
check(file.exists() && file.isFile && file.canRead()) { "${file.absolutePath} cannot be read" }
|
|
||||||
val text = file.readText()
|
|
||||||
if (text.isNotBlank()) {
|
if (text.isNotBlank()) {
|
||||||
Yaml.default.decodeFromString(instance.updaterSerializer, file.readText())
|
Yaml.default.decodeFromString(instance.updaterSerializer, text)
|
||||||
|
}
|
||||||
|
logger.debug { "Successfully loaded PluginData: ${instance.saveName} (containing ${instance.valueNodes.size} properties)" }
|
||||||
}
|
}
|
||||||
instance
|
|
||||||
}.also { it.setStorage(this) }
|
|
||||||
|
|
||||||
protected open fun getPluginDataFile(holder: PluginDataHolder, clazz: KClass<*>): File = with(clazz) {
|
protected open fun getPluginDataFile(holder: PluginDataHolder, instance: PluginData): File {
|
||||||
val name = findValueName()
|
val name = instance.saveName
|
||||||
|
|
||||||
val dir = directoryPath.resolve(holder.name)
|
val dir = directoryPath.resolve(holder.name)
|
||||||
if (dir.isFile) {
|
if (dir.isFile) {
|
||||||
error("Target directory $dir for holder $holder is occupied by a file therefore data $qualifiedNameOrTip can't be saved.")
|
error("Target directory $dir for holder $holder is occupied by a file therefore data ${instance::class.qualifiedNameOrTip} can't be saved.")
|
||||||
}
|
}
|
||||||
dir.mkdir()
|
dir.mkdir()
|
||||||
|
|
||||||
val file = directoryPath.resolve(name)
|
val file = dir.resolve(name)
|
||||||
if (file.isDirectory) {
|
if (file.isDirectory) {
|
||||||
error("Target file $file is occupied by a directory therefore data $qualifiedNameOrTip can't be saved.")
|
error("Target File $file is occupied by a directory therefore data ${instance::class.qualifiedNameOrTip} can't be saved.")
|
||||||
}
|
}
|
||||||
return file.toFile()
|
logger.debug { "File allocated for ${instance.saveName}: $file" }
|
||||||
|
return file.toFile().also { it.createNewFile() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ConsoleExperimentalAPI
|
@ConsoleExperimentalAPI
|
||||||
public override fun store(holder: PluginDataHolder, pluginData: PluginData) {
|
public override fun store(holder: PluginDataHolder, instance: PluginData) {
|
||||||
val file =
|
getPluginDataFile(holder, instance).writeText(Yaml.default.encodeToString(instance.updaterSerializer, Unit))
|
||||||
getPluginDataFile(
|
logger.debug { "Successfully saved PluginData: ${instance.saveName} (containing ${instance.valueNodes.size} properties)" }
|
||||||
holder,
|
|
||||||
if (pluginData is AutoSavePluginData) pluginData.originPluginDataClass else pluginData::class
|
|
||||||
)
|
|
||||||
|
|
||||||
if (file.exists() && file.isFile && file.canRead()) {
|
|
||||||
file.writeText(Yaml.default.encodeToString(pluginData.updaterSerializer, Unit))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,9 +17,9 @@ import net.mamoe.mirai.console.MiraiConsole
|
|||||||
import net.mamoe.mirai.console.internal.data.cast
|
import net.mamoe.mirai.console.internal.data.cast
|
||||||
import net.mamoe.mirai.console.internal.data.mkdir
|
import net.mamoe.mirai.console.internal.data.mkdir
|
||||||
import net.mamoe.mirai.console.plugin.*
|
import net.mamoe.mirai.console.plugin.*
|
||||||
import net.mamoe.mirai.console.plugin.dsecription.PluginDependency
|
import net.mamoe.mirai.console.plugin.description.PluginDependency
|
||||||
import net.mamoe.mirai.console.plugin.dsecription.PluginDescription
|
import net.mamoe.mirai.console.plugin.description.PluginDescription
|
||||||
import net.mamoe.mirai.console.plugin.dsecription.PluginKind
|
import net.mamoe.mirai.console.plugin.description.PluginKind
|
||||||
import net.mamoe.mirai.utils.info
|
import net.mamoe.mirai.utils.info
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
@ -17,7 +17,6 @@ import kotlinx.coroutines.SupervisorJob
|
|||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.console.MiraiConsole
|
import net.mamoe.mirai.console.MiraiConsole
|
||||||
import net.mamoe.mirai.console.data.*
|
import net.mamoe.mirai.console.data.*
|
||||||
import net.mamoe.mirai.console.data.PluginDataStorage.Companion.load
|
|
||||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||||
import net.mamoe.mirai.console.util.BotManager
|
import net.mamoe.mirai.console.util.BotManager
|
||||||
import net.mamoe.mirai.contact.User
|
import net.mamoe.mirai.contact.User
|
||||||
@ -43,7 +42,7 @@ internal object BotManagerImpl : BotManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object ManagersConfig : PluginData by ConsoleBuiltInPluginDataStorage.load() {
|
internal object ManagersConfig : AutoSavePluginData() {
|
||||||
private val managers: MutableMap<Long, MutableSet<Long>> by value()
|
private val managers: MutableMap<Long, MutableSet<Long>> by value()
|
||||||
|
|
||||||
internal operator fun get(bot: Bot): MutableSet<Long> = managers.getOrPut(bot.id, ::mutableSetOf)
|
internal operator fun get(bot: Bot): MutableSet<Long> = managers.getOrPut(bot.id, ::mutableSetOf)
|
||||||
@ -61,7 +60,4 @@ internal object ConsoleBuiltInPluginDataHolder : AutoSavePluginDataHolder,
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal object ConsoleBuiltInPluginDataStorage :
|
internal object ConsoleBuiltInPluginDataStorage :
|
||||||
PluginDataStorage by MiraiConsoleImplementationBridge.dataStorageForBuiltIns {
|
PluginDataStorage by MiraiConsoleImplementationBridge.dataStorageForBuiltIns
|
||||||
|
|
||||||
inline fun <reified T : PluginData> load(): T = load(ConsoleBuiltInPluginDataHolder)
|
|
||||||
}
|
|
@ -14,7 +14,7 @@ package net.mamoe.mirai.console.plugin
|
|||||||
import net.mamoe.mirai.console.command.CommandOwner
|
import net.mamoe.mirai.console.command.CommandOwner
|
||||||
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.disable
|
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.disable
|
||||||
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.enable
|
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.enable
|
||||||
import net.mamoe.mirai.console.plugin.dsecription.PluginDescription
|
import net.mamoe.mirai.console.plugin.description.PluginDescription
|
||||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -14,7 +14,7 @@ package net.mamoe.mirai.console.plugin
|
|||||||
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.disable
|
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.disable
|
||||||
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.enable
|
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.enable
|
||||||
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.register
|
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.register
|
||||||
import net.mamoe.mirai.console.plugin.dsecription.PluginDescription
|
import net.mamoe.mirai.console.plugin.description.PluginDescription
|
||||||
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ package net.mamoe.mirai.console.plugin
|
|||||||
|
|
||||||
import net.mamoe.mirai.console.MiraiConsole
|
import net.mamoe.mirai.console.MiraiConsole
|
||||||
import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl
|
import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl
|
||||||
import net.mamoe.mirai.console.plugin.dsecription.PluginDescription
|
import net.mamoe.mirai.console.plugin.description.PluginDescription
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.mamoe.mirai.console.plugin.dsecription
|
package net.mamoe.mirai.console.plugin.description
|
||||||
|
|
||||||
import com.vdurmont.semver4j.Semver
|
import com.vdurmont.semver4j.Semver
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
@ -7,7 +7,7 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.mamoe.mirai.console.plugin.dsecription
|
package net.mamoe.mirai.console.plugin.description
|
||||||
|
|
||||||
import com.vdurmont.semver4j.Semver
|
import com.vdurmont.semver4j.Semver
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
@ -7,7 +7,7 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.mamoe.mirai.console.plugin.dsecription
|
package net.mamoe.mirai.console.plugin.description
|
||||||
|
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
@ -21,13 +21,13 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
|||||||
@ConsoleExperimentalAPI("classname might change")
|
@ConsoleExperimentalAPI("classname might change")
|
||||||
public interface JarPluginLoader : CoroutineScope, FilePluginLoader<JvmPlugin, JvmPluginDescription> {
|
public interface JarPluginLoader : CoroutineScope, FilePluginLoader<JvmPlugin, JvmPluginDescription> {
|
||||||
/**
|
/**
|
||||||
* [JvmPlugin.loadPluginData] 默认使用的实例
|
* [JvmPlugin.reloadPluginData] 默认使用的实例
|
||||||
*/
|
*/
|
||||||
@ConsoleExperimentalAPI
|
@ConsoleExperimentalAPI
|
||||||
public val dataStorage: PluginDataStorage
|
public val dataStorage: PluginDataStorage
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [JvmPlugin.loadPluginData] 默认使用的实例
|
* [JvmPlugin.reloadPluginData] 默认使用的实例
|
||||||
*/
|
*/
|
||||||
@ConsoleExperimentalAPI
|
@ConsoleExperimentalAPI
|
||||||
public val configStorage: PluginDataStorage
|
public val configStorage: PluginDataStorage
|
||||||
|
@ -7,7 +7,13 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS", "NOTHING_TO_INLINE")
|
@file:Suppress(
|
||||||
|
"INVISIBLE_MEMBER",
|
||||||
|
"INVISIBLE_REFERENCE",
|
||||||
|
"EXPOSED_SUPER_CLASS",
|
||||||
|
"NOTHING_TO_INLINE",
|
||||||
|
"INAPPLICABLE_JVM_NAME"
|
||||||
|
)
|
||||||
|
|
||||||
package net.mamoe.mirai.console.plugin.jvm
|
package net.mamoe.mirai.console.plugin.jvm
|
||||||
|
|
||||||
@ -19,7 +25,6 @@ import net.mamoe.mirai.console.plugin.Plugin
|
|||||||
import net.mamoe.mirai.console.plugin.PluginFileExtensions
|
import net.mamoe.mirai.console.plugin.PluginFileExtensions
|
||||||
import net.mamoe.mirai.console.plugin.ResourceContainer
|
import net.mamoe.mirai.console.plugin.ResourceContainer
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,16 +56,22 @@ public interface JvmPlugin : Plugin, CoroutineScope,
|
|||||||
get() = JarPluginLoader
|
get() = JarPluginLoader
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 读取一个 [PluginData] 实例
|
* 重载 [PluginData]
|
||||||
|
*
|
||||||
|
* @see reloadPluginData
|
||||||
*/
|
*/
|
||||||
@JvmDefault
|
@JvmDefault
|
||||||
public fun <T : PluginData> loadPluginData(clazz: Class<T>): T = loader.dataStorage.load(this, clazz)
|
@JvmName("reloadPluginData")
|
||||||
|
public fun <T : PluginData> T.reload(): Unit = loader.dataStorage.load(this@JvmPlugin, this)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 读取一个 [PluginConfig] 实例
|
* 重载 [PluginConfig]
|
||||||
|
*
|
||||||
|
* @see reloadPluginConfig
|
||||||
*/
|
*/
|
||||||
@JvmDefault
|
@JvmDefault
|
||||||
public fun <T : PluginConfig> loadPluginConfig(clazz: Class<T>): T = loader.configStorage.load(this, clazz)
|
@JvmName("reloadPluginConfig")
|
||||||
|
public fun <T : PluginConfig> T.reload(): Unit = loader.configStorage.load(this@JvmPlugin, this)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在插件被加载时调用. 只会被调用一次.
|
* 在插件被加载时调用. 只会被调用一次.
|
||||||
@ -85,25 +96,17 @@ public interface JvmPlugin : Plugin, CoroutineScope,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 读取一个 [PluginData] 实例
|
* 重载一个 [PluginData]
|
||||||
|
*
|
||||||
|
* @see JvmPlugin.reload
|
||||||
*/
|
*/
|
||||||
@JvmSynthetic
|
@JvmSynthetic
|
||||||
public inline fun <T : PluginData> JvmPlugin.loadPluginData(clazz: KClass<T>): T = this.loadPluginData(clazz.java)
|
public inline fun JvmPlugin.reloadPluginData(instance: PluginData): Unit = this.run { instance.reload() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 读取一个 [PluginData] 实例
|
* 重载一个 [PluginConfig]
|
||||||
|
*
|
||||||
|
* @see JvmPlugin.reload
|
||||||
*/
|
*/
|
||||||
@JvmSynthetic
|
@JvmSynthetic
|
||||||
public inline fun <reified T : PluginData> JvmPlugin.loadPluginData(): T = this.loadPluginData(T::class)
|
public inline fun JvmPlugin.reloadPluginConfig(instance: PluginConfig): Unit = this.run { instance.reload() }
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取一个 [PluginConfig] 实例
|
|
||||||
*/
|
|
||||||
@JvmSynthetic
|
|
||||||
public inline fun <T : PluginConfig> JvmPlugin.loadPluginConfig(clazz: KClass<T>): T = this.loadPluginConfig(clazz.java)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取一个 [PluginConfig] 实例
|
|
||||||
*/
|
|
||||||
@JvmSynthetic
|
|
||||||
public inline fun <reified T : PluginConfig> JvmPlugin.loadPluginConfig(): T = this.loadPluginConfig(T::class)
|
|
@ -14,9 +14,9 @@ import kotlinx.serialization.SerialName
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.Transient
|
import kotlinx.serialization.Transient
|
||||||
import net.mamoe.mirai.console.internal.data.SemverAsStringSerializerLoose
|
import net.mamoe.mirai.console.internal.data.SemverAsStringSerializerLoose
|
||||||
import net.mamoe.mirai.console.plugin.dsecription.PluginDependency
|
import net.mamoe.mirai.console.plugin.description.PluginDependency
|
||||||
import net.mamoe.mirai.console.plugin.dsecription.PluginDescription
|
import net.mamoe.mirai.console.plugin.description.PluginDescription
|
||||||
import net.mamoe.mirai.console.plugin.dsecription.PluginKind
|
import net.mamoe.mirai.console.plugin.description.PluginKind
|
||||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -24,7 +24,6 @@ public abstract class KotlinPlugin @JvmOverloads constructor(
|
|||||||
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
|
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||||
) : JvmPlugin, AbstractJvmPlugin(parentCoroutineContext)
|
) : JvmPlugin, AbstractJvmPlugin(parentCoroutineContext)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在内存动态加载的插件. 此为预览版本 API.
|
* 在内存动态加载的插件. 此为预览版本 API.
|
||||||
*/
|
*/
|
||||||
|
@ -49,8 +49,7 @@ fun initTestEnvironment() {
|
|||||||
override suspend fun sendMessage(message: Message) = println(message)
|
override suspend fun sendMessage(message: Message) = println(message)
|
||||||
}
|
}
|
||||||
override val dataStorageForJarPluginLoader: PluginDataStorage get() = MemoryPluginDataStorage()
|
override val dataStorageForJarPluginLoader: PluginDataStorage get() = MemoryPluginDataStorage()
|
||||||
override val configStorageForJarPluginLoader: PluginDataStorage
|
override val configStorageForJarPluginLoader: PluginDataStorage get() = TODO("Not yet implemented")
|
||||||
get() = TODO("Not yet implemented")
|
|
||||||
override val dataStorageForBuiltIns: PluginDataStorage get() = MemoryPluginDataStorage()
|
override val dataStorageForBuiltIns: PluginDataStorage get() = MemoryPluginDataStorage()
|
||||||
override val consoleInput: ConsoleInput = object : ConsoleInput {
|
override val consoleInput: ConsoleInput = object : ConsoleInput {
|
||||||
override suspend fun requestInput(hint: String): String {
|
override suspend fun requestInput(hint: String): String {
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
package net.mamoe.mirai.console.data
|
package net.mamoe.mirai.console.data
|
||||||
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import net.mamoe.mirai.console.data.AutoSavePluginDataHolder.Companion.createPluginData
|
||||||
|
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
||||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -18,18 +20,12 @@ import kotlin.test.assertSame
|
|||||||
@OptIn(ConsoleInternalAPI::class)
|
@OptIn(ConsoleInternalAPI::class)
|
||||||
internal class PluginDataTest {
|
internal class PluginDataTest {
|
||||||
|
|
||||||
class MyPluginData : AbstractPluginData() {
|
object MyPlugin : KotlinPlugin()
|
||||||
|
|
||||||
|
class MyPluginData : PluginData by MyPlugin.createPluginData() {
|
||||||
var int by value(1)
|
var int by value(1)
|
||||||
val map by value<MutableMap<String, String>>()
|
val map: MutableMap<String, String> by value()
|
||||||
val map2 by value<MutableMap<String, MutableMap<String, String>>>()
|
val map2: MutableMap<String, MutableMap<String, String>> by value()
|
||||||
|
|
||||||
@ConsoleInternalAPI
|
|
||||||
override fun onValueChanged(value: Value<*>) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setStorage(storage: PluginDataStorage) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val jsonPrettyPrint = Json { prettyPrint = true }
|
private val jsonPrettyPrint = Json { prettyPrint = true }
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
object Versions {
|
object Versions {
|
||||||
const val core = "1.2.2"
|
const val core = "1.2.2"
|
||||||
const val console = "1.0-M2-1"
|
const val console = "1.0-M3-dev-1"
|
||||||
const val consoleGraphical = "0.0.7"
|
const val consoleGraphical = "0.0.7"
|
||||||
const val consoleTerminal = "0.1.0"
|
const val consoleTerminal = "0.1.0"
|
||||||
const val consolePure = console
|
const val consolePure = console
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
package net.mamoe.mirai.console.pure
|
package net.mamoe.mirai.console.pure
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
import net.mamoe.mirai.console.MiraiConsole
|
import net.mamoe.mirai.console.MiraiConsole
|
||||||
import net.mamoe.mirai.console.command.BuiltInCommands
|
import net.mamoe.mirai.console.command.BuiltInCommands
|
||||||
import net.mamoe.mirai.console.command.Command.Companion.primaryName
|
import net.mamoe.mirai.console.command.Command.Companion.primaryName
|
||||||
@ -20,23 +22,23 @@ import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
|||||||
import net.mamoe.mirai.console.util.requestInput
|
import net.mamoe.mirai.console.util.requestInput
|
||||||
import net.mamoe.mirai.utils.DefaultLogger
|
import net.mamoe.mirai.utils.DefaultLogger
|
||||||
import org.fusesource.jansi.Ansi
|
import org.fusesource.jansi.Ansi
|
||||||
|
import org.jline.reader.UserInterruptException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.Executors
|
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
@OptIn(ConsoleInternalAPI::class)
|
@OptIn(ConsoleInternalAPI::class)
|
||||||
internal fun startupConsoleThread() {
|
internal fun startupConsoleThread() {
|
||||||
val service = Executors.newSingleThreadExecutor { code ->
|
val mutex = Mutex()
|
||||||
thread(start = false, isDaemon = false, name = "Console Input", block = code::run)
|
|
||||||
}
|
|
||||||
val dispatch = service.asCoroutineDispatcher()
|
|
||||||
ConsoleUtils.miraiLineReader = { hint ->
|
ConsoleUtils.miraiLineReader = { hint ->
|
||||||
withContext(dispatch) {
|
mutex.withLock {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
println("Requesting input")
|
||||||
ConsoleUtils.lineReader.readLine(
|
ConsoleUtils.lineReader.readLine(
|
||||||
if (hint.isNotEmpty()) {
|
if (hint.isNotEmpty()) {
|
||||||
ConsoleUtils.lineReader.printAbove(
|
ConsoleUtils.lineReader.printAbove(
|
||||||
Ansi.ansi()
|
Ansi.ansi()
|
||||||
.fgCyan().a(MiraiConsoleFrontEndPure.sdf.format(Date())).a(" ")
|
.fgCyan().a(sdf.format(Date())).a(" ")
|
||||||
.fgMagenta().a(hint)
|
.fgMagenta().a(hint)
|
||||||
.reset()
|
.reset()
|
||||||
.toString()
|
.toString()
|
||||||
@ -46,10 +48,14 @@ internal fun startupConsoleThread() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MiraiConsole.launch(dispatch) {
|
|
||||||
val consoleLogger = DefaultLogger("console")
|
val consoleLogger = DefaultLogger("console")
|
||||||
while (isActive) {
|
|
||||||
|
val inputThread = thread(start = true, isDaemon = false, name = "Console Input") {
|
||||||
|
try {
|
||||||
|
runBlocking {
|
||||||
|
while (true) {
|
||||||
try {
|
try {
|
||||||
val next = MiraiConsole.requestInput("").let {
|
val next = MiraiConsole.requestInput("").let {
|
||||||
when {
|
when {
|
||||||
@ -58,6 +64,7 @@ internal fun startupConsoleThread() {
|
|||||||
else -> CommandManager.commandPrefix + it
|
else -> CommandManager.commandPrefix + it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
exitProcess(123456)
|
||||||
if (next.isBlank()) {
|
if (next.isBlank()) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -77,24 +84,35 @@ internal fun startupConsoleThread() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
return@launch
|
return@runBlocking
|
||||||
} catch (e: CancellationException) {
|
} catch (e: CancellationException) {
|
||||||
return@launch
|
return@runBlocking
|
||||||
|
} catch (e: UserInterruptException) {
|
||||||
|
MiraiConsole.cancel()
|
||||||
|
return@runBlocking
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
consoleLogger.error("Unhandled exception", e)
|
consoleLogger.error("Unhandled exception", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.let { consoleJob ->
|
}
|
||||||
|
} catch (e: InterruptedException) {
|
||||||
|
return@thread
|
||||||
|
} catch (e: CancellationException) {
|
||||||
|
return@thread
|
||||||
|
} catch (e: UserInterruptException) {
|
||||||
|
MiraiConsole.cancel()
|
||||||
|
return@thread
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
consoleLogger.error("Unhandled exception", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MiraiConsole.job.invokeOnCompletion {
|
MiraiConsole.job.invokeOnCompletion {
|
||||||
runCatching {
|
runCatching {
|
||||||
consoleJob.cancel()
|
inputThread.interrupt()
|
||||||
}.exceptionOrNull()?.printStackTrace()
|
|
||||||
runCatching {
|
|
||||||
service.shutdownNow()
|
|
||||||
}.exceptionOrNull()?.printStackTrace()
|
}.exceptionOrNull()?.printStackTrace()
|
||||||
runCatching {
|
runCatching {
|
||||||
ConsoleUtils.terminal.close()
|
ConsoleUtils.terminal.close()
|
||||||
}.exceptionOrNull()?.printStackTrace()
|
}.exceptionOrNull()?.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,149 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:Suppress(
|
|
||||||
"INVISIBLE_MEMBER",
|
|
||||||
"INVISIBLE_REFERENCE",
|
|
||||||
"CANNOT_OVERRIDE_INVISIBLE_MEMBER",
|
|
||||||
"INVISIBLE_SETTER",
|
|
||||||
"INVISIBLE_GETTER",
|
|
||||||
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER",
|
|
||||||
"INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER_WARNING",
|
|
||||||
"EXPOSED_SUPER_CLASS"
|
|
||||||
)
|
|
||||||
|
|
||||||
package net.mamoe.mirai.console.pure
|
|
||||||
|
|
||||||
//import net.mamoe.mirai.console.command.CommandManager
|
|
||||||
//import net.mamoe.mirai.console.utils.MiraiConsoleFrontEnd
|
|
||||||
import com.vdurmont.semver4j.Semver
|
|
||||||
import net.mamoe.mirai.console.MiraiConsoleFrontEndDescription
|
|
||||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
|
||||||
import net.mamoe.mirai.utils.PlatformLogger
|
|
||||||
import org.fusesource.jansi.Ansi
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
|
|
||||||
private val ANSI_RESET = Ansi().reset().toString()
|
|
||||||
|
|
||||||
internal val LoggerCreator: (identity: String?) -> MiraiLogger = {
|
|
||||||
PlatformLogger(identity = it, output = { line ->
|
|
||||||
ConsoleUtils.lineReader.printAbove(line + ANSI_RESET)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mirai-console-pure 前端实现
|
|
||||||
*
|
|
||||||
* @see MiraiConsoleImplementationPure 后端实现
|
|
||||||
* @see MiraiConsolePureLoader CLI 入口点
|
|
||||||
*/
|
|
||||||
@ConsoleInternalAPI
|
|
||||||
@Suppress("unused")
|
|
||||||
object MiraiConsoleFrontEndPure : MiraiConsoleFrontEndDescription {
|
|
||||||
internal val sdf by ThreadLocal.withInitial {
|
|
||||||
// SimpleDateFormat not thread safe.
|
|
||||||
SimpleDateFormat("HH:mm:ss")
|
|
||||||
}
|
|
||||||
|
|
||||||
private operator fun <T> ThreadLocal<T>.getValue(thiz: Any, property: Any): T {
|
|
||||||
return this.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
override val name: String get() = "Pure"
|
|
||||||
override val version: Semver get() = net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.version
|
|
||||||
override val vendor: String get() = "Mamoe Technologies"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
class MiraiConsoleFrontEndPure : MiraiConsoleFrontEnd {
|
|
||||||
private var requesting = false
|
|
||||||
private var requestStr = ""
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
companion object {
|
|
||||||
// ANSI color codes
|
|
||||||
const val COLOR_RED = "\u001b[38;5;196m"
|
|
||||||
const val COLOR_CYAN = "\u001b[38;5;87m"
|
|
||||||
const val COLOR_GREEN = "\u001b[38;5;82m"
|
|
||||||
|
|
||||||
// use a dark yellow(more like orange) instead of light one to save Solarized-light users
|
|
||||||
const val COLOR_YELLOW = "\u001b[38;5;220m"
|
|
||||||
const val COLOR_GREY = "\u001b[38;5;244m"
|
|
||||||
const val COLOR_BLUE = "\u001b[38;5;27m"
|
|
||||||
const val COLOR_NAVY = "\u001b[38;5;24m" // navy uniform blue
|
|
||||||
const val COLOR_PINK = "\u001b[38;5;207m"
|
|
||||||
const val COLOR_RESET = "\u001b[39;49m"
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
thread(name = "Mirai Console Input Thread") {
|
|
||||||
while (true) {
|
|
||||||
val input = readLine() ?: return@thread
|
|
||||||
if (requesting) {
|
|
||||||
requestStr = input
|
|
||||||
requesting = false
|
|
||||||
} else {
|
|
||||||
CommandManager.runCommand(ConsoleCommandSender, input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val sdf by lazy {
|
|
||||||
SimpleDateFormat("HH:mm:ss")
|
|
||||||
}
|
|
||||||
|
|
||||||
override val logger: MiraiLogger = DefaultLogger("Console") // CLI logger from mirai-core
|
|
||||||
|
|
||||||
fun pushLog(identity: Long, message: String) {
|
|
||||||
println("\u001b[0m " + sdf.format(Date()) + " $message")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun prePushBot(identity: Long) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pushBot(bot: Bot) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pushVersion(consoleVersion: String, consoleBuild: String, coreVersion: String) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun requestInput(hint: String): String {
|
|
||||||
if (hint.isNotEmpty()) {
|
|
||||||
println("\u001b[0m " + sdf.format(Date()) + COLOR_PINK + " $hint")
|
|
||||||
}
|
|
||||||
requesting = true
|
|
||||||
while (true) {
|
|
||||||
delay(50)
|
|
||||||
if (!requesting) {
|
|
||||||
return requestStr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pushBotAdminStatus(identity: Long, admins: List<Long>) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createLoginSolver(): LoginSolver {
|
|
||||||
return DefaultLoginSolver(
|
|
||||||
input = suspend {
|
|
||||||
requestInput("")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
@ -38,12 +38,11 @@ import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
|||||||
import net.mamoe.mirai.console.pure.ConsoleInputImpl.requestInput
|
import net.mamoe.mirai.console.pure.ConsoleInputImpl.requestInput
|
||||||
import net.mamoe.mirai.console.util.ConsoleInput
|
import net.mamoe.mirai.console.util.ConsoleInput
|
||||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||||
import net.mamoe.mirai.utils.BotConfiguration
|
import net.mamoe.mirai.utils.*
|
||||||
import net.mamoe.mirai.utils.DefaultLoginSolver
|
import org.fusesource.jansi.Ansi
|
||||||
import net.mamoe.mirai.utils.LoginSolver
|
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -92,3 +91,21 @@ private object ConsoleFrontEndDescImpl : MiraiConsoleFrontEndDescription {
|
|||||||
override val vendor: String get() = "Mamoe Technologies"
|
override val vendor: String get() = "Mamoe Technologies"
|
||||||
override val version: Semver = net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.version
|
override val version: Semver = net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val ANSI_RESET = Ansi().reset().toString()
|
||||||
|
|
||||||
|
internal val LoggerCreator: (identity: String?) -> MiraiLogger = {
|
||||||
|
PlatformLogger(identity = it, output = { line ->
|
||||||
|
ConsoleUtils.lineReader.printAbove(line + ANSI_RESET)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val sdf by ThreadLocal.withInitial {
|
||||||
|
// SimpleDateFormat not thread safe.
|
||||||
|
SimpleDateFormat("HH:mm:ss")
|
||||||
|
}
|
||||||
|
|
||||||
|
private operator fun <T> ThreadLocal<T>.getValue(thisRef: Any?, property: Any): T {
|
||||||
|
return this.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -36,12 +36,13 @@ object MiraiConsolePureLoader {
|
|||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
startup()
|
startup()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
internal fun startup() {
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
MiraiConsoleImplementationPure().start()
|
internal fun startup(instance: MiraiConsoleImplementationPure = MiraiConsoleImplementationPure()) {
|
||||||
|
instance.start()
|
||||||
overrideSTD()
|
overrideSTD()
|
||||||
startupConsoleThread()
|
startupConsoleThread()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun overrideSTD() {
|
internal fun overrideSTD() {
|
||||||
|
Loading…
Reference in New Issue
Block a user