mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-25 15:40:28 +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
|
||||
[`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
|
||||
[`PluginManager`]: src/main/kotlin/net/mamoe/mirai/console/plugin/PluginManager.kt
|
||||
[`JarPluginLoader`]: src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JarPluginLoader.kt
|
||||
|
@ -60,8 +60,7 @@ kotlin {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileAndRuntime("net.mamoe:mirai-core:${Versions.core}")
|
||||
compileAndRuntime(kotlin("stdlib-jdk8", Versions.kotlinStdlib))
|
||||
implementation("net.mamoe:mirai-core:${Versions.core}")
|
||||
|
||||
implementation(kotlinx("serialization-core", Versions.serialization))
|
||||
implementation(kotlin("reflect"))
|
||||
@ -92,6 +91,8 @@ ext.apply {
|
||||
dependencyFilter.exclude {
|
||||
when ("${it.moduleGroup}:${it.moduleName}") {
|
||||
"net.mamoe:mirai-core" -> true
|
||||
"org.jetbrains.kotlin:kotlin-stdlib" -> true
|
||||
"org.jetbrains.kotlin:kotlin-stdlib-jdk8" -> true
|
||||
"net.mamoe:mirai-core-qqandroid" -> true
|
||||
else -> false
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ public abstract class AbstractPluginData : PluginData, PluginDataImpl() {
|
||||
/**
|
||||
* 供手动实现时值跟踪使用 (如 Java 用户). 一般 Kotlin 用户需使用 [provideDelegate]
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public override fun <T : SerializerAwareValue<*>> T.track(valueName: String): T =
|
||||
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
|
||||
|
||||
import net.mamoe.mirai.console.data.java.JAutoSavePluginConfig
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import kotlin.annotation.AnnotationTarget.*
|
||||
|
||||
@ -27,14 +28,14 @@ import kotlin.annotation.AnnotationTarget.*
|
||||
* 在 [PluginData] 的示例基础上, 修改对象定义
|
||||
* ```
|
||||
* // 原
|
||||
* object MyPluginData : PluginData by PluginMain.loadPluginData()
|
||||
* object MyPluginData : AutoSavePluginData()
|
||||
* // 修改为
|
||||
* object MyPluginConfig : PluginConfig by PluginMain.loadPluginConfig()
|
||||
* object MyPluginConfig : AutoSavePluginConfig()
|
||||
* ```
|
||||
* 即可将一个 [PluginData] 变更为 [PluginConfig].
|
||||
*
|
||||
* ### Java
|
||||
* 见 [JPluginConfig]
|
||||
* 见 [JAutoSavePluginConfig]
|
||||
*
|
||||
* @see PluginData
|
||||
*/
|
||||
|
@ -18,15 +18,17 @@
|
||||
package net.mamoe.mirai.console.data
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import net.mamoe.mirai.console.data.java.JAutoSavePluginData
|
||||
import net.mamoe.mirai.console.internal.data.*
|
||||
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.ConsoleInternalAPI
|
||||
import kotlin.internal.LowPriorityInOverloadResolution
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
|
||||
/**
|
||||
* 一个插件内部的, 对用户隐藏的数据对象. 可包含对多个 [Value] 的值变更的跟踪.
|
||||
@ -47,7 +49,7 @@ import kotlin.reflect.KType
|
||||
* ```
|
||||
* 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 custom: Map<Long, CustomData> by value() // 使用 kotlinx-serialization 序列化的类型. (目前还不支持)
|
||||
* var long: Long by value(0) // 允许 var
|
||||
@ -69,11 +71,9 @@ import kotlin.reflect.KType
|
||||
*
|
||||
* ### 使用 Java
|
||||
*
|
||||
* 参考 [JPluginData]
|
||||
* 参考 [JAutoSavePluginData]
|
||||
*
|
||||
* **注意**: 由于实现特殊, 请不要在初始化 Value 时就使用 `.get()`. 这可能会导致自动保存追踪失效. 必须在使用时才调用 `.get()` 获取真实数据对象.
|
||||
*
|
||||
* @see JvmPlugin.loadPluginData 通过 [JvmPlugin] 获取指定 [PluginData] 实例.
|
||||
* @see JvmPlugin.reloadPluginData 通过 [JvmPlugin] 获取指定 [PluginData] 实例.
|
||||
* @see PluginDataStorage [PluginData] 存储仓库
|
||||
*/
|
||||
public interface PluginData {
|
||||
@ -88,6 +88,18 @@ public interface PluginData {
|
||||
@ConsoleExperimentalAPI
|
||||
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` 初始化的属性节点.
|
||||
*/
|
||||
@ -123,6 +135,7 @@ public interface PluginData {
|
||||
/**
|
||||
* 供手动实现时值跟踪使用 (如 Java 用户). 一般 Kotlin 用户需使用 [provideDelegate]
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public fun <T : SerializerAwareValue<*>> T.track(
|
||||
/**
|
||||
* 值名称.
|
||||
@ -153,7 +166,7 @@ public interface PluginData {
|
||||
* 当这个 [PluginData] 被放入一个 [PluginDataStorage] 时调用
|
||||
*/
|
||||
@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 value: Value<List<String>> = MyData.findBackingFieldValue(MyData::list)
|
||||
* ```
|
||||
*
|
||||
* @see PluginData
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
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")
|
||||
* val list: List<String> 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 intValue: Value<Int> = MyData.findBackingFieldValue("int")
|
||||
* ```
|
||||
*
|
||||
* @see PluginData
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
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 value: PluginData.ValueNode<List<String>> = MyData.findBackingFieldValueNode(MyData::list)
|
||||
* ```
|
||||
*
|
||||
* @see PluginData
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
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.Job
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage.Companion.load
|
||||
import net.mamoe.mirai.console.internal.data.AutoSavePluginData
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
|
||||
/**
|
||||
* 可以持有相关 [PluginData] 实例的对象, 作为 [PluginData] 实例的拥有者.
|
||||
@ -24,6 +23,7 @@ import net.mamoe.mirai.console.internal.data.AutoSavePluginData
|
||||
*
|
||||
* @see AutoSavePluginDataHolder 支持自动保存
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public interface PluginDataHolder {
|
||||
/**
|
||||
* 保存时使用的分类名
|
||||
@ -36,6 +36,7 @@ public interface PluginDataHolder {
|
||||
*
|
||||
* @see net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public interface AutoSavePluginDataHolder : PluginDataHolder, CoroutineScope {
|
||||
/**
|
||||
* [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.plugin.jvm.JarPluginLoader
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* [数据对象][PluginData] 存储仓库.
|
||||
*
|
||||
* ## 职责
|
||||
* [PluginDataStorage] 类似于一个数据库, 它只承担将序列化之后的数据保存到数据库中, 和从数据库取出这个对象的任务.
|
||||
* [PluginDataStorage] 不考虑一个 []
|
||||
*
|
||||
*
|
||||
* 此为较低层的 API, 一般插件开发者不会接触.
|
||||
@ -34,69 +33,77 @@ import kotlin.reflect.KClass
|
||||
* @see PluginDataHolder
|
||||
* @see JarPluginLoader.dataStorage
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
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], 将
|
||||
*/
|
||||
public fun store(holder: PluginDataHolder, pluginData: PluginData)
|
||||
public fun store(holder: PluginDataHolder, instance: PluginData)
|
||||
|
||||
public companion object {
|
||||
/**
|
||||
* 读取一个实例. 在 [T] 实例创建后 [设置 [PluginDataStorage]][PluginData.setStorage]
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun <T : PluginData> PluginDataStorage.load(holder: PluginDataHolder, dataClass: KClass<T>): T =
|
||||
this.load(holder, dataClass.java)
|
||||
/*
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取一个实例. 在 [T] 实例创建后 [设置 [PluginDataStorage]][PluginData.setStorage]
|
||||
*/
|
||||
@JvmStatic
|
||||
public fun <T : PluginData> PluginDataStorage.load(holder: PluginDataHolder, dataClass: Class<T>): T =
|
||||
this.load(holder, dataClass.java)
|
||||
|
||||
/**
|
||||
* 读取一个实例. 在 [T] 实例创建后 [设置 [PluginDataStorage]][PluginData.setStorage]
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun <reified T : PluginData> PluginDataStorage.load(holder: PluginDataHolder): T =
|
||||
this.load(holder, T::class)
|
||||
|
||||
/**
|
||||
* 读取一个实例. 在 [T] 实例创建后 [设置 [PluginDataStorage]][PluginData.setStorage]
|
||||
*/
|
||||
@JvmSynthetic
|
||||
public inline fun <reified T : PluginData> PluginDataStorage.load(holder: PluginDataHolder): T =
|
||||
this.load(holder, T::class)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* 在内存存储所有 [PluginData] 实例的 [PluginDataStorage]. 在内存数据丢失后相关 [PluginData] 实例也会丢失.
|
||||
* @see PluginDataStorage
|
||||
*/
|
||||
public interface MemoryPluginDataStorage : PluginDataStorage, Map<Class<out PluginData>, PluginData> {
|
||||
/**
|
||||
* 当任一 [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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ConsoleExperimentalAPI
|
||||
public interface MemoryPluginDataStorage : PluginDataStorage {
|
||||
public companion object {
|
||||
/**
|
||||
* 创建一个 [MemoryPluginDataStorage] 实例.
|
||||
*
|
||||
* @param onChanged 当任一 [PluginData] 实例拥有的 [Value] 的值被改变后调用的回调函数.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmName("create")
|
||||
// @JvmOverloads
|
||||
public operator fun invoke(onChanged: OnChangedCallback = OnChangedCallback.NoOp): MemoryPluginDataStorage =
|
||||
MemoryPluginDataStorageImpl(onChanged)
|
||||
public operator fun invoke(): MemoryPluginDataStorage = MemoryPluginDataStorageImpl()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,14 @@
|
||||
* 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
|
||||
* // 原
|
||||
* public class AccountPluginData extends JPluginData {
|
||||
* public class AccountPluginData extends JAutoSavePluginData {
|
||||
* // 修改为
|
||||
* public class AccountPluginConfig extends JPluginConfig {
|
||||
* public class AccountPluginConfig extends JAutoSavePluginConfig {
|
||||
* ```
|
||||
* 即可将一个 [PluginData] 变更为 [PluginConfig].
|
||||
*
|
||||
* @see JPluginData
|
||||
* @see JAutoSavePluginData
|
||||
* @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
|
||||
*/
|
||||
|
||||
@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.setValueBySerializer
|
||||
import net.mamoe.mirai.console.internal.data.valueImpl
|
||||
@ -27,18 +28,19 @@ import kotlin.reflect.full.createType
|
||||
* ```
|
||||
* // PluginMain.java
|
||||
* public final class PluginMain extends JavaPlugin {
|
||||
* public static PluginMain INSTANCE = null;
|
||||
* public static PluginMain INSTANCE;
|
||||
* public PluginMain() {
|
||||
* INSTANCE = this;
|
||||
* this.reloadPluginData(MyPluginData.INSTANCE); // 读取文件等
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // MyPluginData.java
|
||||
* public class AccountPluginData extends JPluginData {
|
||||
* public static AccountPluginData INSTANCE;
|
||||
* public class MyPluginData extends JAutoSavePluginData {
|
||||
* public static final MyPluginData INSTANCE = new MyPluginData();
|
||||
*
|
||||
* public AccountPluginData() {
|
||||
* super(PluginMain.INSTANCE.loadPluginData(AccountPluginData.class));
|
||||
* private MyPluginData() {
|
||||
* super(PluginMain.INSTANCE);
|
||||
* INSTANCE = this;
|
||||
* }
|
||||
*
|
||||
@ -64,57 +66,57 @@ import kotlin.reflect.full.createType
|
||||
* theList.set();
|
||||
* ```
|
||||
*
|
||||
* **注意**: 由于实现特殊, 请不要在初始化 Value 时就使用 `.get()`. 这可能会导致自动保存追踪失效. 必须在使用时才调用 `.get()` 获取真实数据对象.
|
||||
*
|
||||
* @see PluginData
|
||||
*/
|
||||
public open class JPluginData(
|
||||
private val delegate: PluginData
|
||||
) : PluginData by delegate {
|
||||
public abstract class JAutoSavePluginData : AutoSavePluginData(), PluginConfig {
|
||||
//// region JPluginData_value_primitives CODEGEN ////
|
||||
|
||||
/**
|
||||
* 创建一个 [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]
|
||||
*/
|
||||
public fun value(default: Short): SerializerAwareValue<Short> = delegate.valueImpl(default)
|
||||
public fun value(default: Short): SerializerAwareValue<Short> = valueImpl(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]
|
||||
*/
|
||||
public fun value(default: Long): SerializerAwareValue<Long> = delegate.valueImpl(default)
|
||||
public fun value(default: Long): SerializerAwareValue<Long> = valueImpl(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]
|
||||
*/
|
||||
public fun value(default: Double): SerializerAwareValue<Double> = delegate.valueImpl(default)
|
||||
public fun value(default: Double): SerializerAwareValue<Double> = valueImpl(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]
|
||||
*/
|
||||
public fun value(default: Boolean): SerializerAwareValue<Boolean> = delegate.valueImpl(default)
|
||||
public fun value(default: Boolean): SerializerAwareValue<Boolean> = valueImpl(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 ////
|
||||
|
||||
@ -134,7 +136,7 @@ public open class JPluginData(
|
||||
*/
|
||||
@JvmOverloads
|
||||
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)
|
||||
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
|
||||
|
||||
import net.mamoe.mirai.console.data.*
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.console.data.MemoryPluginDataStorage
|
||||
import net.mamoe.mirai.console.data.PluginData
|
||||
import net.mamoe.mirai.console.data.PluginDataHolder
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
|
||||
internal class MemoryPluginDataStorageImpl(
|
||||
private val onChanged: MemoryPluginDataStorage.OnChangedCallback
|
||||
) : PluginDataStorage, MemoryPluginDataStorage,
|
||||
internal class MemoryPluginDataStorageImpl : PluginDataStorage, MemoryPluginDataStorage,
|
||||
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")
|
||||
override fun load(holder: PluginDataHolder, instance: PluginData) {
|
||||
instance.onStored(holder, this)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : PluginData> load(holder: PluginDataHolder, dataClass: Class<T>): T = (synchronized(this) {
|
||||
this.getOrPut(dataClass) {
|
||||
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) {
|
||||
synchronized(this) {
|
||||
this[pluginData::class.java] = pluginData
|
||||
}
|
||||
override fun store(holder: PluginDataHolder, instance: PluginData) {
|
||||
// no-op
|
||||
}
|
||||
}
|
@ -9,75 +9,59 @@
|
||||
|
||||
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.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 java.io.File
|
||||
import java.nio.file.Path
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Suppress("RedundantVisibilityModifier") // might be public in the future
|
||||
internal open class MultiFilePluginDataStorageImpl(
|
||||
public final override val directoryPath: Path
|
||||
public final override val directoryPath: Path,
|
||||
private val logger: MiraiLogger = SilentLogger,
|
||||
) : PluginDataStorage, MultiFilePluginDataStorage {
|
||||
init {
|
||||
directoryPath.mkdir()
|
||||
}
|
||||
|
||||
public override fun <T : PluginData> load(holder: PluginDataHolder, dataClass: Class<T>): T =
|
||||
with(dataClass.kotlin) {
|
||||
@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"
|
||||
)
|
||||
public override fun load(holder: PluginDataHolder, instance: PluginData) {
|
||||
instance.onStored(holder, this)
|
||||
|
||||
val file = getPluginDataFile(holder, this)
|
||||
file.createNewFile()
|
||||
check(file.exists() && file.isFile && file.canRead()) { "${file.absolutePath} cannot be read" }
|
||||
val text = file.readText()
|
||||
if (text.isNotBlank()) {
|
||||
Yaml.default.decodeFromString(instance.updaterSerializer, file.readText())
|
||||
}
|
||||
instance
|
||||
}.also { it.setStorage(this) }
|
||||
val text = getPluginDataFile(holder, instance).readText()
|
||||
if (text.isNotBlank()) {
|
||||
Yaml.default.decodeFromString(instance.updaterSerializer, text)
|
||||
}
|
||||
logger.debug { "Successfully loaded PluginData: ${instance.saveName} (containing ${instance.valueNodes.size} properties)" }
|
||||
}
|
||||
|
||||
protected open fun getPluginDataFile(holder: PluginDataHolder, clazz: KClass<*>): File = with(clazz) {
|
||||
val name = findValueName()
|
||||
protected open fun getPluginDataFile(holder: PluginDataHolder, instance: PluginData): File {
|
||||
val name = instance.saveName
|
||||
|
||||
val dir = directoryPath.resolve(holder.name)
|
||||
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()
|
||||
|
||||
val file = directoryPath.resolve(name)
|
||||
val file = dir.resolve(name)
|
||||
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
|
||||
public override fun store(holder: PluginDataHolder, pluginData: PluginData) {
|
||||
val file =
|
||||
getPluginDataFile(
|
||||
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))
|
||||
}
|
||||
public override fun store(holder: PluginDataHolder, instance: PluginData) {
|
||||
getPluginDataFile(holder, instance).writeText(Yaml.default.encodeToString(instance.updaterSerializer, Unit))
|
||||
logger.debug { "Successfully saved PluginData: ${instance.saveName} (containing ${instance.valueNodes.size} properties)" }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.mkdir
|
||||
import net.mamoe.mirai.console.plugin.*
|
||||
import net.mamoe.mirai.console.plugin.dsecription.PluginDependency
|
||||
import net.mamoe.mirai.console.plugin.dsecription.PluginDescription
|
||||
import net.mamoe.mirai.console.plugin.dsecription.PluginKind
|
||||
import net.mamoe.mirai.console.plugin.description.PluginDependency
|
||||
import net.mamoe.mirai.console.plugin.description.PluginDescription
|
||||
import net.mamoe.mirai.console.plugin.description.PluginKind
|
||||
import net.mamoe.mirai.utils.info
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
@ -17,7 +17,6 @@ import kotlinx.coroutines.SupervisorJob
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
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.util.BotManager
|
||||
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()
|
||||
|
||||
internal operator fun get(bot: Bot): MutableSet<Long> = managers.getOrPut(bot.id, ::mutableSetOf)
|
||||
@ -61,7 +60,4 @@ internal object ConsoleBuiltInPluginDataHolder : AutoSavePluginDataHolder,
|
||||
}
|
||||
|
||||
internal object ConsoleBuiltInPluginDataStorage :
|
||||
PluginDataStorage by MiraiConsoleImplementationBridge.dataStorageForBuiltIns {
|
||||
|
||||
inline fun <reified T : PluginData> load(): T = load(ConsoleBuiltInPluginDataHolder)
|
||||
}
|
||||
PluginDataStorage by MiraiConsoleImplementationBridge.dataStorageForBuiltIns
|
@ -14,7 +14,7 @@ package net.mamoe.mirai.console.plugin
|
||||
import net.mamoe.mirai.console.command.CommandOwner
|
||||
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.disable
|
||||
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.util.ConsoleExperimentalAPI
|
||||
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.enable
|
||||
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 java.io.File
|
||||
|
||||
|
@ -13,7 +13,7 @@ package net.mamoe.mirai.console.plugin
|
||||
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
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.nio.file.Path
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
* 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 kotlinx.serialization.KSerializer
|
@ -7,7 +7,7 @@
|
||||
* 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 kotlinx.serialization.Serializable
|
@ -7,7 +7,7 @@
|
||||
* 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.Serializable
|
@ -21,13 +21,13 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
@ConsoleExperimentalAPI("classname might change")
|
||||
public interface JarPluginLoader : CoroutineScope, FilePluginLoader<JvmPlugin, JvmPluginDescription> {
|
||||
/**
|
||||
* [JvmPlugin.loadPluginData] 默认使用的实例
|
||||
* [JvmPlugin.reloadPluginData] 默认使用的实例
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public val dataStorage: PluginDataStorage
|
||||
|
||||
/**
|
||||
* [JvmPlugin.loadPluginData] 默认使用的实例
|
||||
* [JvmPlugin.reloadPluginData] 默认使用的实例
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public val configStorage: PluginDataStorage
|
||||
|
@ -7,7 +7,13 @@
|
||||
* 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
|
||||
|
||||
@ -19,7 +25,6 @@ import net.mamoe.mirai.console.plugin.Plugin
|
||||
import net.mamoe.mirai.console.plugin.PluginFileExtensions
|
||||
import net.mamoe.mirai.console.plugin.ResourceContainer
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
/**
|
||||
@ -51,16 +56,22 @@ public interface JvmPlugin : Plugin, CoroutineScope,
|
||||
get() = JarPluginLoader
|
||||
|
||||
/**
|
||||
* 读取一个 [PluginData] 实例
|
||||
* 重载 [PluginData]
|
||||
*
|
||||
* @see reloadPluginData
|
||||
*/
|
||||
@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
|
||||
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
|
||||
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
|
||||
public inline fun <reified T : PluginData> JvmPlugin.loadPluginData(): T = this.loadPluginData(T::class)
|
||||
|
||||
/**
|
||||
* 读取一个 [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)
|
||||
public inline fun JvmPlugin.reloadPluginConfig(instance: PluginConfig): Unit = this.run { instance.reload() }
|
@ -14,9 +14,9 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import net.mamoe.mirai.console.internal.data.SemverAsStringSerializerLoose
|
||||
import net.mamoe.mirai.console.plugin.dsecription.PluginDependency
|
||||
import net.mamoe.mirai.console.plugin.dsecription.PluginDescription
|
||||
import net.mamoe.mirai.console.plugin.dsecription.PluginKind
|
||||
import net.mamoe.mirai.console.plugin.description.PluginDependency
|
||||
import net.mamoe.mirai.console.plugin.description.PluginDescription
|
||||
import net.mamoe.mirai.console.plugin.description.PluginKind
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import java.io.File
|
||||
|
@ -24,7 +24,6 @@ public abstract class KotlinPlugin @JvmOverloads constructor(
|
||||
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||
) : JvmPlugin, AbstractJvmPlugin(parentCoroutineContext)
|
||||
|
||||
|
||||
/**
|
||||
* 在内存动态加载的插件. 此为预览版本 API.
|
||||
*/
|
||||
|
@ -49,8 +49,7 @@ fun initTestEnvironment() {
|
||||
override suspend fun sendMessage(message: Message) = println(message)
|
||||
}
|
||||
override val dataStorageForJarPluginLoader: PluginDataStorage get() = MemoryPluginDataStorage()
|
||||
override val configStorageForJarPluginLoader: PluginDataStorage
|
||||
get() = TODO("Not yet implemented")
|
||||
override val configStorageForJarPluginLoader: PluginDataStorage get() = TODO("Not yet implemented")
|
||||
override val dataStorageForBuiltIns: PluginDataStorage get() = MemoryPluginDataStorage()
|
||||
override val consoleInput: ConsoleInput = object : ConsoleInput {
|
||||
override suspend fun requestInput(hint: String): String {
|
||||
|
@ -10,6 +10,8 @@
|
||||
package net.mamoe.mirai.console.data
|
||||
|
||||
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 org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
@ -18,18 +20,12 @@ import kotlin.test.assertSame
|
||||
@OptIn(ConsoleInternalAPI::class)
|
||||
internal class PluginDataTest {
|
||||
|
||||
class MyPluginData : AbstractPluginData() {
|
||||
object MyPlugin : KotlinPlugin()
|
||||
|
||||
class MyPluginData : PluginData by MyPlugin.createPluginData() {
|
||||
var int by value(1)
|
||||
val map by value<MutableMap<String, String>>()
|
||||
val map2 by value<MutableMap<String, MutableMap<String, String>>>()
|
||||
|
||||
@ConsoleInternalAPI
|
||||
override fun onValueChanged(value: Value<*>) {
|
||||
|
||||
}
|
||||
|
||||
override fun setStorage(storage: PluginDataStorage) {
|
||||
}
|
||||
val map: MutableMap<String, String> by value()
|
||||
val map2: MutableMap<String, MutableMap<String, String>> by value()
|
||||
}
|
||||
|
||||
private val jsonPrettyPrint = Json { prettyPrint = true }
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
object Versions {
|
||||
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 consoleTerminal = "0.1.0"
|
||||
const val consolePure = console
|
||||
|
@ -10,6 +10,8 @@
|
||||
package net.mamoe.mirai.console.pure
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.command.BuiltInCommands
|
||||
import net.mamoe.mirai.console.command.Command.Companion.primaryName
|
||||
@ -20,81 +22,97 @@ import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.console.util.requestInput
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
import org.fusesource.jansi.Ansi
|
||||
import org.jline.reader.UserInterruptException
|
||||
import java.util.*
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
@OptIn(ConsoleInternalAPI::class)
|
||||
internal fun startupConsoleThread() {
|
||||
val service = Executors.newSingleThreadExecutor { code ->
|
||||
thread(start = false, isDaemon = false, name = "Console Input", block = code::run)
|
||||
}
|
||||
val dispatch = service.asCoroutineDispatcher()
|
||||
val mutex = Mutex()
|
||||
ConsoleUtils.miraiLineReader = { hint ->
|
||||
withContext(dispatch) {
|
||||
ConsoleUtils.lineReader.readLine(
|
||||
if (hint.isNotEmpty()) {
|
||||
ConsoleUtils.lineReader.printAbove(
|
||||
Ansi.ansi()
|
||||
.fgCyan().a(MiraiConsoleFrontEndPure.sdf.format(Date())).a(" ")
|
||||
.fgMagenta().a(hint)
|
||||
.reset()
|
||||
.toString()
|
||||
)
|
||||
"$hint > "
|
||||
} else "> "
|
||||
)
|
||||
mutex.withLock {
|
||||
withContext(Dispatchers.IO) {
|
||||
println("Requesting input")
|
||||
ConsoleUtils.lineReader.readLine(
|
||||
if (hint.isNotEmpty()) {
|
||||
ConsoleUtils.lineReader.printAbove(
|
||||
Ansi.ansi()
|
||||
.fgCyan().a(sdf.format(Date())).a(" ")
|
||||
.fgMagenta().a(hint)
|
||||
.reset()
|
||||
.toString()
|
||||
)
|
||||
"$hint > "
|
||||
} else "> "
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MiraiConsole.launch(dispatch) {
|
||||
val consoleLogger = DefaultLogger("console")
|
||||
while (isActive) {
|
||||
try {
|
||||
val next = MiraiConsole.requestInput("").let {
|
||||
when {
|
||||
it.startsWith(CommandManager.commandPrefix) -> it
|
||||
it == "?" -> CommandManager.commandPrefix + BuiltInCommands.Help.primaryName
|
||||
else -> CommandManager.commandPrefix + it
|
||||
val consoleLogger = DefaultLogger("console")
|
||||
|
||||
val inputThread = thread(start = true, isDaemon = false, name = "Console Input") {
|
||||
try {
|
||||
runBlocking {
|
||||
while (true) {
|
||||
try {
|
||||
val next = MiraiConsole.requestInput("").let {
|
||||
when {
|
||||
it.startsWith(CommandManager.commandPrefix) -> it
|
||||
it == "?" -> CommandManager.commandPrefix + BuiltInCommands.Help.primaryName
|
||||
else -> CommandManager.commandPrefix + it
|
||||
}
|
||||
}
|
||||
exitProcess(123456)
|
||||
if (next.isBlank()) {
|
||||
continue
|
||||
}
|
||||
// consoleLogger.debug("INPUT> $next")
|
||||
val result = ConsoleCommandSenderImpl.executeCommand(next)
|
||||
when (result.status) {
|
||||
CommandExecuteStatus.SUCCESSFUL -> {
|
||||
}
|
||||
CommandExecuteStatus.EXECUTION_EXCEPTION -> {
|
||||
result.exception?.printStackTrace()
|
||||
}
|
||||
CommandExecuteStatus.COMMAND_NOT_FOUND -> {
|
||||
consoleLogger.warning("未知指令: ${result.commandName}, 输入 ? 获取帮助")
|
||||
}
|
||||
CommandExecuteStatus.PERMISSION_DENIED -> {
|
||||
consoleLogger.warning("Permission denied.")
|
||||
}
|
||||
}
|
||||
} catch (e: InterruptedException) {
|
||||
return@runBlocking
|
||||
} catch (e: CancellationException) {
|
||||
return@runBlocking
|
||||
} catch (e: UserInterruptException) {
|
||||
MiraiConsole.cancel()
|
||||
return@runBlocking
|
||||
} catch (e: Throwable) {
|
||||
consoleLogger.error("Unhandled exception", e)
|
||||
}
|
||||
}
|
||||
if (next.isBlank()) {
|
||||
continue
|
||||
}
|
||||
// consoleLogger.debug("INPUT> $next")
|
||||
val result = ConsoleCommandSenderImpl.executeCommand(next)
|
||||
when (result.status) {
|
||||
CommandExecuteStatus.SUCCESSFUL -> {
|
||||
}
|
||||
CommandExecuteStatus.EXECUTION_EXCEPTION -> {
|
||||
result.exception?.printStackTrace()
|
||||
}
|
||||
CommandExecuteStatus.COMMAND_NOT_FOUND -> {
|
||||
consoleLogger.warning("未知指令: ${result.commandName}, 输入 ? 获取帮助")
|
||||
}
|
||||
CommandExecuteStatus.PERMISSION_DENIED -> {
|
||||
consoleLogger.warning("Permission denied.")
|
||||
}
|
||||
}
|
||||
} catch (e: InterruptedException) {
|
||||
return@launch
|
||||
} catch (e: CancellationException) {
|
||||
return@launch
|
||||
} catch (e: Throwable) {
|
||||
consoleLogger.error("Unhandled exception", e)
|
||||
}
|
||||
}
|
||||
}.let { consoleJob ->
|
||||
MiraiConsole.job.invokeOnCompletion {
|
||||
runCatching {
|
||||
consoleJob.cancel()
|
||||
}.exceptionOrNull()?.printStackTrace()
|
||||
runCatching {
|
||||
service.shutdownNow()
|
||||
}.exceptionOrNull()?.printStackTrace()
|
||||
runCatching {
|
||||
ConsoleUtils.terminal.close()
|
||||
}.exceptionOrNull()?.printStackTrace()
|
||||
} 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 {
|
||||
runCatching {
|
||||
inputThread.interrupt()
|
||||
}.exceptionOrNull()?.printStackTrace()
|
||||
runCatching {
|
||||
ConsoleUtils.terminal.close()
|
||||
}.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.util.ConsoleInput
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.utils.BotConfiguration
|
||||
import net.mamoe.mirai.utils.DefaultLoginSolver
|
||||
import net.mamoe.mirai.utils.LoginSolver
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.*
|
||||
import org.fusesource.jansi.Ansi
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@ -91,4 +90,22 @@ private object ConsoleFrontEndDescImpl : MiraiConsoleFrontEndDescription {
|
||||
override val name: String get() = "Pure"
|
||||
override val vendor: String get() = "Mamoe Technologies"
|
||||
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>) {
|
||||
startup()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun startup() {
|
||||
MiraiConsoleImplementationPure().start()
|
||||
overrideSTD()
|
||||
startupConsoleThread()
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
internal fun startup(instance: MiraiConsoleImplementationPure = MiraiConsoleImplementationPure()) {
|
||||
instance.start()
|
||||
overrideSTD()
|
||||
startupConsoleThread()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun overrideSTD() {
|
||||
|
Loading…
Reference in New Issue
Block a user