mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-25 15:40:28 +08:00
Public API stabilization: misc improvements
This commit is contained in:
parent
66e4852c4d
commit
c821f9e9e9
@ -54,6 +54,7 @@ kotlin {
|
||||
useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts")
|
||||
useExperimentalAnnotation("kotlinx.serialization.ExperimentalSerializationApi")
|
||||
useExperimentalAnnotation("net.mamoe.mirai.console.data.ExperimentalPluginConfig")
|
||||
useExperimentalAnnotation("net.mamoe.mirai.console.util.ConsoleInternalAPI")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,9 +21,9 @@ import net.mamoe.mirai.console.MiraiConsoleFrontEndDescription
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation
|
||||
import net.mamoe.mirai.console.command.BuiltInCommands
|
||||
import net.mamoe.mirai.console.command.Command.Companion.primaryName
|
||||
import net.mamoe.mirai.console.command.CommandManagerImpl
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||
import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.internal.command.CommandManagerImpl
|
||||
import net.mamoe.mirai.console.internal.plugin.CuiPluginCenter
|
||||
import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl
|
||||
import net.mamoe.mirai.console.internal.util.ConsoleBuiltInPluginDataStorage
|
||||
|
@ -0,0 +1,23 @@
|
||||
package net.mamoe.mirai.console.internal.command
|
||||
|
||||
import net.mamoe.mirai.console.command.CommandPermission
|
||||
import net.mamoe.mirai.console.command.CommandSender
|
||||
import net.mamoe.mirai.console.command.hasPermission
|
||||
|
||||
internal class OrCommandPermissionImpl(
|
||||
private val first: CommandPermission,
|
||||
private val second: CommandPermission
|
||||
) : CommandPermission {
|
||||
override fun CommandSender.hasPermission(): Boolean {
|
||||
return this.hasPermission(first) || this.hasPermission(second)
|
||||
}
|
||||
}
|
||||
|
||||
internal class AndCommandPermissionImpl(
|
||||
private val first: CommandPermission,
|
||||
private val second: CommandPermission
|
||||
) : CommandPermission {
|
||||
override fun CommandSender.hasPermission(): Boolean {
|
||||
return this.hasPermission(first) && this.hasPermission(second)
|
||||
}
|
||||
}
|
@ -1,10 +1,7 @@
|
||||
package net.mamoe.mirai.console.internal.data
|
||||
|
||||
import kotlinx.atomicfu.atomic
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.console.data.*
|
||||
import net.mamoe.mirai.console.internal.plugin.updateWhen
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
@ -27,6 +24,15 @@ internal open class AutoSavePluginData(
|
||||
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
|
||||
@ -34,25 +40,30 @@ internal open class AutoSavePluginData(
|
||||
internal var lastAutoSaveJob: Job? = null
|
||||
|
||||
@JvmField
|
||||
@Volatile
|
||||
internal var currentFirstStartTime = atomic(0L)
|
||||
internal val currentFirstStartTime = atomic(0L)
|
||||
|
||||
protected open fun shouldPerformAutoSaveWheneverChanged(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
init {
|
||||
owner.coroutineContext[Job]?.invokeOnCompletion { doSave() }
|
||||
}
|
||||
|
||||
private val updaterBlock: suspend CoroutineScope.() -> Unit = {
|
||||
currentFirstStartTime.updateWhen({ it == 0L }, { currentTimeMillis })
|
||||
if (::storage.isInitialized) {
|
||||
currentFirstStartTime.updateWhen({ it == 0L }, { currentTimeMillis })
|
||||
|
||||
delay(owner.autoSaveIntervalMillis.first.coerceAtLeast(1000)) // for safety
|
||||
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()
|
||||
if (lastAutoSaveJob == this.coroutineContext[Job]) {
|
||||
doSave()
|
||||
} else {
|
||||
if (currentFirstStartTime.updateWhen(
|
||||
{ currentTimeMillis - it >= owner.autoSaveIntervalMillis.last },
|
||||
{ 0 })
|
||||
) doSave()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ internal open class MultiFilePluginDataStorageImpl(
|
||||
}.also { it.setStorage(this) }
|
||||
|
||||
protected open fun getPluginDataFile(holder: PluginDataHolder, clazz: KClass<*>): File = with(clazz) {
|
||||
val name = findASerialName()
|
||||
val name = findValueName()
|
||||
|
||||
val dir = directoryPath.resolve(holder.name)
|
||||
if (dir.isFile) {
|
||||
|
@ -29,7 +29,7 @@ import net.mamoe.yamlkt.YamlNullableDynamicSerializer
|
||||
* - Auto-saving
|
||||
*/
|
||||
internal abstract class PluginDataImpl {
|
||||
internal fun findNodeInstance(name: String): ValueNode<*>? = valueNodes.firstOrNull { it.serialName == name }
|
||||
internal fun findNodeInstance(name: String): ValueNode<*>? = valueNodes.firstOrNull { it.valueName == name }
|
||||
|
||||
internal abstract val valueNodes: MutableList<ValueNode<*>>
|
||||
|
||||
@ -43,8 +43,8 @@ internal abstract class PluginDataImpl {
|
||||
if (decodeSequentially()) {
|
||||
var index = 0
|
||||
repeat(decodeCollectionSize(descriptor)) {
|
||||
val serialName = decodeSerializableElement(descriptor, index++, String.serializer())
|
||||
val node = findNodeInstance(serialName)
|
||||
val valueName = decodeSerializableElement(descriptor, index++, String.serializer())
|
||||
val node = findNodeInstance(valueName)
|
||||
if (node == null) {
|
||||
decodeSerializableElement(descriptor, index++, YamlNullableDynamicSerializer)
|
||||
} else {
|
||||
@ -53,21 +53,21 @@ internal abstract class PluginDataImpl {
|
||||
}
|
||||
} else {
|
||||
outerLoop@ while (true) {
|
||||
var serialName: String? = null
|
||||
var valueName: String? = null
|
||||
innerLoop@ while (true) {
|
||||
val index = decodeElementIndex(descriptor)
|
||||
if (index == CompositeDecoder.DECODE_DONE) {
|
||||
check(serialName == null) { "name must be null at this moment." }
|
||||
check(valueName == null) { "name must be null at this moment." }
|
||||
break@outerLoop
|
||||
}
|
||||
|
||||
if (!index.isOdd()) { // key
|
||||
check(serialName == null) { "name must be null at this moment" }
|
||||
serialName = decodeSerializableElement(descriptor, index, String.serializer())
|
||||
check(valueName == null) { "name must be null at this moment" }
|
||||
valueName = decodeSerializableElement(descriptor, index, String.serializer())
|
||||
} else {
|
||||
check(serialName != null) { "name must not be null at this moment" }
|
||||
check(valueName != null) { "name must not be null at this moment" }
|
||||
|
||||
val node = findNodeInstance(serialName)
|
||||
val node = findNodeInstance(valueName)
|
||||
if (node == null) {
|
||||
decodeSerializableElement(descriptor, index, YamlNullableDynamicSerializer)
|
||||
} else {
|
||||
@ -92,8 +92,8 @@ internal abstract class PluginDataImpl {
|
||||
var index = 0
|
||||
|
||||
// val vSerializer = dataUpdaterSerializerTypeArguments[1] as KSerializer<Any?>
|
||||
valueNodes.forEach { (serialName, _, valueSerializer) ->
|
||||
encodeSerializableElement(descriptor, index++, String.serializer(), serialName)
|
||||
valueNodes.forEach { (valueName, _, valueSerializer) ->
|
||||
encodeSerializableElement(descriptor, index++, String.serializer(), valueName)
|
||||
encodeSerializableElement(descriptor, index++, valueSerializer, Unit)
|
||||
}
|
||||
endStructure(descriptor)
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
package net.mamoe.mirai.console.internal.data
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import net.mamoe.mirai.console.data.PluginData
|
||||
import net.mamoe.mirai.console.data.ValueName
|
||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KParameter
|
||||
@ -20,7 +20,7 @@ import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal inline fun <reified T : Any> KType.asKClass(): KClass<out T> {
|
||||
internal inline fun <reified T : Any> KType.toKClass(): KClass<out T> {
|
||||
val clazz = requireNotNull(classifier as? KClass<T>) { "Unsupported classifier: $classifier" }
|
||||
|
||||
val fromClass = arguments[0].type?.classifier as? KClass<*> ?: Any::class
|
||||
@ -34,24 +34,22 @@ internal inline fun <reified T : Any> KType.asKClass(): KClass<out T> {
|
||||
}
|
||||
|
||||
internal inline fun <reified T : PluginData> newPluginDataInstanceUsingReflection(type: KType): T {
|
||||
val classifier = type.asKClass<T>()
|
||||
val classifier = type.toKClass<T>()
|
||||
|
||||
return with(classifier) {
|
||||
objectInstance
|
||||
?: createInstanceOrNull()
|
||||
?: throw IllegalArgumentException(
|
||||
"Cannot create PluginData instance. " +
|
||||
"PluginDataHolder supports PluginDatas implemented as an object " +
|
||||
"PluginDataHolder supports PluginData implemented as an object " +
|
||||
"or the ones with a constructor which either has no parameters or all parameters of which are optional, by default newPluginDataInstance implementation."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun isReferenceArray(rootClass: KClass<Any>): Boolean = rootClass.java.isArray
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun KType.kclass() = when (val t = classifier) {
|
||||
internal fun KType.classifierAsKClass() = when (val t = classifier) {
|
||||
is KClass<*> -> t
|
||||
else -> error("Only KClass supported as classifier, got $t")
|
||||
} as KClass<Any>
|
||||
@ -65,14 +63,12 @@ internal fun <T : Any> KClass<T>.createInstanceOrNull(): T? {
|
||||
}
|
||||
|
||||
@JvmSynthetic
|
||||
internal fun KClass<*>.findASerialName(): String =
|
||||
findAnnotation<SerialName>()?.value
|
||||
internal fun KClass<*>.findValueName(): String =
|
||||
findAnnotation<ValueName>()?.value
|
||||
?: qualifiedName
|
||||
?: throw IllegalArgumentException("Cannot find a serial name for $this")
|
||||
|
||||
|
||||
internal val KProperty<*>.serialNameOrPropertyName: String get() = this.findAnnotation<SerialName>()?.value ?: this.name
|
||||
|
||||
internal fun Int.isOdd() = this and 0b1 != 0
|
||||
|
||||
internal val KProperty<*>.serialName: String get() = this.findAnnotation<SerialName>()?.value ?: this.name
|
||||
internal val KProperty<*>.valueName: String get() = this.findAnnotation<ValueName>()?.value ?: this.name
|
||||
|
@ -39,7 +39,7 @@ import kotlin.reflect.KType
|
||||
)
|
||||
internal fun serializerMirai(type: KType): KSerializer<Any?> {
|
||||
fun serializerByKTypeImpl(type: KType): KSerializer<Any> {
|
||||
val rootClass = type.kclass()
|
||||
val rootClass = type.classifierAsKClass()
|
||||
|
||||
val typeArguments = type.arguments
|
||||
.map { requireNotNull(it.type) { "Star projections in type arguments are not allowed, but had $type" } }
|
||||
@ -60,7 +60,7 @@ internal fun serializerMirai(type: KType): KSerializer<Any?> {
|
||||
Triple::class -> TripleSerializer(serializers[0], serializers[1], serializers[2])
|
||||
/* mamoe modify */ Any::class -> if (type.isMarkedNullable) YamlNullableDynamicSerializer else YamlDynamicSerializer
|
||||
else -> {
|
||||
if (isReferenceArray(rootClass)) {
|
||||
if (rootClass.java.isArray) {
|
||||
return ArraySerializer(
|
||||
typeArguments[0].classifier as KClass<Any>,
|
||||
serializers[0]
|
||||
|
@ -87,7 +87,19 @@ public enum class PluginKind {
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件的一个依赖的信息
|
||||
* 插件的一个依赖的信息.
|
||||
*
|
||||
* 在 YAML 格式下, 典型的插件依赖示例:
|
||||
* ```yaml
|
||||
* dependencies:
|
||||
* - name: "依赖的插件名" # 依赖的插件名
|
||||
* version: "" # 依赖的版本号, 支持 Apache Ivy 格式. 为 null 或不指定时不限制版本
|
||||
* isOptional: true # `true` 表示插件在找不到此依赖时也能正常加载
|
||||
* - "SamplePlugin" # 名称为 SamplePlugin 的插件, 不限制版本, isOptional=false
|
||||
* - "TestPlugin:1.0.0+" # 名称为 ExamplePlugin 的插件, 版本至少为 1.0.0, isOptional=false
|
||||
* - "ExamplePlugin:1.5.0+?" # 名称为 ExamplePlugin 的插件, 版本至少为 1.5.0, 末尾 `?` 表示 isOptional=true
|
||||
* - "Another test plugin:[1.0.0, 2.0.0)" # 名称为 Another test plugin 的插件, 版本要求大于等于 1.0.0, 小于 2.0.0, isOptional=false
|
||||
* ```
|
||||
*
|
||||
* @see PluginDescription.dependencies
|
||||
*/
|
||||
@ -100,7 +112,7 @@ public data class PluginDependency(
|
||||
*
|
||||
* 版本遵循 [语义化版本 2.0 规范](https://semver.org/lang/zh-CN/),
|
||||
*
|
||||
* 允许 [Apache Ivy 格式版本号](http://ant.apache.org/ivy/history/latest-milestone/ivyfile/dependency.html)
|
||||
* 允许 [Apache Ivy 风格版本号表示](http://ant.apache.org/ivy/history/latest-milestone/settings/version-matchers.html)
|
||||
*/
|
||||
public val version: @Serializable(SemverAsStringSerializerIvy::class) Semver? = null,
|
||||
/**
|
||||
@ -124,7 +136,24 @@ public data class PluginDependency(
|
||||
serializer(),
|
||||
Yaml.nonStrict.encodeToString<Map<*, *>>(any)
|
||||
)
|
||||
else -> PluginDependency(any.toString())
|
||||
else -> {
|
||||
var value = any.toString()
|
||||
val isOptional = value.endsWith('?')
|
||||
if (isOptional) {
|
||||
value = value.removeSuffix("?")
|
||||
}
|
||||
|
||||
val components = value.split(':')
|
||||
when (components.size) {
|
||||
1 -> PluginDependency(value, isOptional = isOptional)
|
||||
2 -> PluginDependency(
|
||||
components[0],
|
||||
Semver(components[1], Semver.SemverType.IVY),
|
||||
isOptional = isOptional
|
||||
)
|
||||
else -> error("Illegal plugin dependency statement: $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -25,6 +25,7 @@ import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregisterAllComm
|
||||
import net.mamoe.mirai.console.command.description.CommandArgumentParser
|
||||
import net.mamoe.mirai.console.command.description.buildCommandArgumentContext
|
||||
import net.mamoe.mirai.console.initTestEnvironment
|
||||
import net.mamoe.mirai.console.internal.command.CommandManagerImpl
|
||||
import net.mamoe.mirai.console.internal.command.flattenCommandComponents
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.message.data.PlainText
|
||||
|
Loading…
Reference in New Issue
Block a user