mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-11 02:50:15 +08:00
Merge remote-tracking branch 'origin/reborn' into reborn
# Conflicts: # backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt # backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt
This commit is contained in:
commit
4190058b6d
2
.github/workflows/cui.yml
vendored
2
.github/workflows/cui.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
|||||||
run: ./gradlew build # if test's failed, don't publish
|
run: ./gradlew build # if test's failed, don't publish
|
||||||
- name: Gradle :mirai-console:cuiCloudUpload
|
- name: Gradle :mirai-console:cuiCloudUpload
|
||||||
run: ./gradlew :mirai-console:cuiCloudUpload -Dcui_cloud_key=${{ secrets.CUI_CLOUD_KEY }} -Pcui_cloud_key=${{ secrets.CUI_CLOUD_KEY }} -Dcui_cloud_url=${{ secrets.CUI_CLOUD_URL }} -Pcui_cloud_url=${{ secrets.CUI_CLOUD_URL }}
|
run: ./gradlew :mirai-console:cuiCloudUpload -Dcui_cloud_key=${{ secrets.CUI_CLOUD_KEY }} -Pcui_cloud_key=${{ secrets.CUI_CLOUD_KEY }} -Dcui_cloud_url=${{ secrets.CUI_CLOUD_URL }} -Pcui_cloud_url=${{ secrets.CUI_CLOUD_URL }}
|
||||||
- name: Gradle :mirai-console-qqandroid:cuiCloudUpload
|
- name: Gradle :mirai-console-graphical:cuiCloudUpload
|
||||||
run: ./gradlew :mirai-console-graphical:cuiCloudUpload -Dcui_cloud_key=${{ secrets.CUI_CLOUD_KEY }} -Pcui_cloud_key=${{ secrets.CUI_CLOUD_KEY }} -Dcui_cloud_url=${{ secrets.CUI_CLOUD_URL }} -Pcui_cloud_url=${{ secrets.CUI_CLOUD_URL }}
|
run: ./gradlew :mirai-console-graphical:cuiCloudUpload -Dcui_cloud_key=${{ secrets.CUI_CLOUD_KEY }} -Pcui_cloud_key=${{ secrets.CUI_CLOUD_KEY }} -Dcui_cloud_url=${{ secrets.CUI_CLOUD_URL }} -Pcui_cloud_url=${{ secrets.CUI_CLOUD_URL }}
|
||||||
|
|
||||||
|
|
||||||
|
2
.github/workflows/shadow.yml
vendored
2
.github/workflows/shadow.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
|||||||
run: ./gradlew build # if test's failed, don't publish
|
run: ./gradlew build # if test's failed, don't publish
|
||||||
- name: Gradle :mirai-console:githubUpload
|
- name: Gradle :mirai-console:githubUpload
|
||||||
run: ./gradlew :mirai-console:githubUpload -Dgithub_token=${{ secrets.MAMOE_TOKEN }} -Pgithub_token=${{ secrets.MAMOE_TOKEN }}
|
run: ./gradlew :mirai-console:githubUpload -Dgithub_token=${{ secrets.MAMOE_TOKEN }} -Pgithub_token=${{ secrets.MAMOE_TOKEN }}
|
||||||
- name: Gradle :mirai-console-qqandroid:githubUpload
|
- name: Gradle :mirai-console-graphical:githubUpload
|
||||||
run: ./gradlew :mirai-console-graphical:githubUpload -Dgithub_token=${{ secrets.MAMOE_TOKEN }} -Pgithub_token=${{ secrets.MAMOE_TOKEN }}
|
run: ./gradlew :mirai-console-graphical:githubUpload -Dgithub_token=${{ secrets.MAMOE_TOKEN }} -Pgithub_token=${{ secrets.MAMOE_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,7 +21,8 @@ Mirai 是一个在全平台下运行,提供 QQ Android 和 TIM PC 协议支持
|
|||||||
|
|
||||||
console 由后端和前端一起工作. 使用时必须选择一个前端.
|
console 由后端和前端一起工作. 使用时必须选择一个前端.
|
||||||
|
|
||||||
**注意:`mirai-console` 后端和 pure 前端正在进行完全的重构,master 分支将不再维护。可以在 [reborn](https://github.com/mamoe/mirai-console/tree/reborn) 查看进度**
|
**注意:`mirai-console` 后端和 pure 前端正在进行完全的重构,master 分支将不再维护。**
|
||||||
|
**`mirai-console` 将在短时间内不可用。`
|
||||||
|
|
||||||
- `mirai-console`: console 的后端, 包含插件管理, 指令系统, 配置系统. 还包含一个轻量命令行的前端 (因此可以独立启动 `mirai-console`).
|
- `mirai-console`: console 的后端, 包含插件管理, 指令系统, 配置系统. 还包含一个轻量命令行的前端 (因此可以独立启动 `mirai-console`).
|
||||||
- `mirai-console-graphical`: console 的 JavaFX 图形化界面前端.
|
- `mirai-console-graphical`: console 的 JavaFX 图形化界面前端.
|
||||||
|
@ -21,11 +21,13 @@ fun main() {
|
|||||||
}.writeText(buildString {
|
}.writeText(buildString {
|
||||||
appendln(COPYRIGHT)
|
appendln(COPYRIGHT)
|
||||||
appendln()
|
appendln()
|
||||||
|
appendln(FILE_SUPPRESS)
|
||||||
|
appendln()
|
||||||
appendln(PACKAGE)
|
appendln(PACKAGE)
|
||||||
appendln()
|
appendln()
|
||||||
// appendln(IMPORTS)
|
appendln(IMPORTS)
|
||||||
// appendln()
|
appendln()
|
||||||
// appendln()
|
appendln()
|
||||||
appendln(DO_NOT_MODIFY)
|
appendln(DO_NOT_MODIFY)
|
||||||
appendln()
|
appendln()
|
||||||
appendln()
|
appendln()
|
||||||
@ -44,8 +46,12 @@ private val PACKAGE = """
|
|||||||
package net.mamoe.mirai.console.setting
|
package net.mamoe.mirai.console.setting
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
|
private val FILE_SUPPRESS = """
|
||||||
|
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "unused")
|
||||||
|
""".trimIndent()
|
||||||
private val IMPORTS = """
|
private val IMPORTS = """
|
||||||
import kotlinx.serialization.builtins.*
|
import net.mamoe.mirai.console.setting.internal.valueImpl
|
||||||
|
import kotlin.internal.LowPriorityInOverloadResolution
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
fun genAllValueUseSite(): String = buildString {
|
fun genAllValueUseSite(): String = buildString {
|
||||||
@ -111,6 +117,26 @@ fun genAllValueUseSite(): String = buildString {
|
|||||||
|
|
||||||
@JvmName("valueMutable")
|
@JvmName("valueMutable")
|
||||||
inline fun <reified T : Setting> Setting.value(default: MutableSet<T>): MutableSettingSetValue<T> = valueImpl(default)
|
inline fun <reified T : Setting> Setting.value(default: MutableSet<T>): MutableSettingSetValue<T> = valueImpl(default)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个只引用对象而不跟踪其属性的值.
|
||||||
|
*
|
||||||
|
* @param T 类型. 必须拥有 [kotlinx.serialization.Serializable] 注解 (因此编译器会自动生成序列化器)
|
||||||
|
*/
|
||||||
|
@DangerousReferenceOnlyValue
|
||||||
|
@JvmName("valueDynamic")
|
||||||
|
@LowPriorityInOverloadResolution
|
||||||
|
inline fun <reified T : Any> Setting.value(default: T): Value<T> = valueImpl(default)
|
||||||
|
|
||||||
|
@RequiresOptIn(
|
||||||
|
""${'"'}
|
||||||
|
这种只保存引用的 Value 可能会导致意料之外的结果, 在使用时须保持谨慎.
|
||||||
|
对值的改变不会触发自动保存, 也不会同步到 UI 中. 在 UI 中只能编辑序列化之后的值.
|
||||||
|
""${'"'}, level = RequiresOptIn.Level.WARNING
|
||||||
|
)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
|
annotation class DangerousReferenceOnlyValue
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import java.io.File
|
|||||||
fun main() {
|
fun main() {
|
||||||
println(File("").absolutePath) // default project base dir
|
println(File("").absolutePath) // default project base dir
|
||||||
|
|
||||||
File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_ValueImpl.kt").apply {
|
File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt").apply {
|
||||||
createNewFile()
|
createNewFile()
|
||||||
}.writeText(buildString {
|
}.writeText(buildString {
|
||||||
appendln(COPYRIGHT)
|
appendln(COPYRIGHT)
|
||||||
@ -41,12 +41,13 @@ private val DO_NOT_MODIFY = """
|
|||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
private val PACKAGE = """
|
private val PACKAGE = """
|
||||||
package net.mamoe.mirai.console.setting
|
package net.mamoe.mirai.console.setting.internal
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
private val IMPORTS = """
|
private val IMPORTS = """
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.*
|
||||||
import kotlinx.serialization.builtins.*
|
import kotlinx.serialization.builtins.*
|
||||||
|
import net.mamoe.mirai.console.setting.*
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
fun genAllValueImpl(): String = buildString {
|
fun genAllValueImpl(): String = buildString {
|
||||||
@ -56,20 +57,20 @@ fun genAllValueImpl(): String = buildString {
|
|||||||
|
|
||||||
// PRIMITIVE
|
// PRIMITIVE
|
||||||
for (number in NUMBERS + OTHER_PRIMITIVES) {
|
for (number in NUMBERS + OTHER_PRIMITIVES) {
|
||||||
appendln(genValueImpl(number, number, "$number.serializer()", false))
|
appendln(genPrimitiveValueImpl(number, number, "$number.serializer()", false))
|
||||||
appendln()
|
appendln()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PRIMITIVE ARRAYS
|
// PRIMITIVE ARRAYS
|
||||||
for (number in NUMBERS + OTHER_PRIMITIVES.filterNot { it == "String" }) {
|
for (number in NUMBERS + OTHER_PRIMITIVES.filterNot { it == "String" }) {
|
||||||
appendln(genValueImpl("${number}Array", "${number}Array", "${number}ArraySerializer()", true))
|
appendln(genPrimitiveValueImpl("${number}Array", "${number}Array", "${number}ArraySerializer()", true))
|
||||||
appendln()
|
appendln()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TYPED ARRAYS
|
// TYPED ARRAYS
|
||||||
for (number in NUMBERS + OTHER_PRIMITIVES) {
|
for (number in NUMBERS + OTHER_PRIMITIVES) {
|
||||||
appendln(
|
appendln(
|
||||||
genValueImpl(
|
genPrimitiveValueImpl(
|
||||||
"Array<${number}>",
|
"Array<${number}>",
|
||||||
"Typed${number}Array",
|
"Typed${number}Array",
|
||||||
"ArraySerializer(${number}.serializer())",
|
"ArraySerializer(${number}.serializer())",
|
||||||
@ -83,7 +84,8 @@ fun genAllValueImpl(): String = buildString {
|
|||||||
for (collectionName in listOf("List", "Set")) {
|
for (collectionName in listOf("List", "Set")) {
|
||||||
for (number in NUMBERS + OTHER_PRIMITIVES) {
|
for (number in NUMBERS + OTHER_PRIMITIVES) {
|
||||||
appendln(
|
appendln(
|
||||||
genValueImpl(
|
genCollectionValueImpl(
|
||||||
|
collectionName,
|
||||||
"${collectionName}<${number}>",
|
"${collectionName}<${number}>",
|
||||||
"${number}${collectionName}",
|
"${number}${collectionName}",
|
||||||
"${collectionName}Serializer(${number}.serializer())",
|
"${collectionName}Serializer(${number}.serializer())",
|
||||||
@ -108,7 +110,8 @@ fun genAllValueImpl(): String = buildString {
|
|||||||
): Mutable${number}${collectionName}Value {
|
): Mutable${number}${collectionName}Value {
|
||||||
var internalValue: Mutable${collectionName}<${number}> = default
|
var internalValue: Mutable${collectionName}<${number}> = default
|
||||||
|
|
||||||
return object : Mutable${number}${collectionName}Value(), Mutable${collectionName}<${number}> by dynamicMutable${collectionName}({ internalValue }) {
|
val delegt = dynamicMutable${collectionName}{ internalValue }
|
||||||
|
return object : Mutable${number}${collectionName}Value(), Mutable${collectionName}<${number}> by delegt {
|
||||||
override var value: Mutable${collectionName}<${number}>
|
override var value: Mutable${collectionName}<${number}>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -118,7 +121,7 @@ fun genAllValueImpl(): String = buildString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<Mutable${collectionName}<${number}>> = object : KSerializer<Mutable${collectionName}<${number}>> {
|
override val serializer: KSerializer<Mutable${collectionName}<${number}>> = object : KSerializer<Mutable${collectionName}<${number}>> {
|
||||||
private val delegate = ${collectionName}Serializer(${number}.serializer())
|
private val delegate = ${collectionName}Serializer(${number}.serializer())
|
||||||
@ -126,7 +129,7 @@ fun genAllValueImpl(): String = buildString {
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): Mutable${collectionName}<${number}> {
|
override fun deserialize(decoder: Decoder): Mutable${collectionName}<${number}> {
|
||||||
return delegate.deserialize(decoder).toMutable${collectionName}().observable {
|
return delegate.deserialize(decoder).toMutable${collectionName}().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +180,12 @@ fun genAllValueImpl(): String = buildString {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun genValueImpl(kotlinTypeName: String, miraiValueName: String, serializer: String, isArray: Boolean): String =
|
fun genPrimitiveValueImpl(
|
||||||
|
kotlinTypeName: String,
|
||||||
|
miraiValueName: String,
|
||||||
|
serializer: String,
|
||||||
|
isArray: Boolean
|
||||||
|
): String =
|
||||||
"""
|
"""
|
||||||
internal fun Setting.valueImpl(default: ${kotlinTypeName}): ${miraiValueName}Value {
|
internal fun Setting.valueImpl(default: ${kotlinTypeName}): ${miraiValueName}Value {
|
||||||
return object : ${miraiValueName}Value() {
|
return object : ${miraiValueName}Value() {
|
||||||
@ -205,3 +213,39 @@ fun genValueImpl(kotlinTypeName: String, miraiValueName: String, serializer: Str
|
|||||||
}
|
}
|
||||||
""".trimIndent() + "\n"
|
""".trimIndent() + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
fun genCollectionValueImpl(
|
||||||
|
collectionName: String,
|
||||||
|
kotlinTypeName: String,
|
||||||
|
miraiValueName: String,
|
||||||
|
serializer: String,
|
||||||
|
isArray: Boolean
|
||||||
|
): String =
|
||||||
|
"""
|
||||||
|
internal fun Setting.valueImpl(default: ${kotlinTypeName}): ${miraiValueName}Value {
|
||||||
|
var internalValue: $kotlinTypeName = default
|
||||||
|
val delegt = dynamic$collectionName { internalValue }
|
||||||
|
return object : ${miraiValueName}Value(), $kotlinTypeName by delegt {
|
||||||
|
override var value: $kotlinTypeName
|
||||||
|
get() = internalValue
|
||||||
|
set(new) {
|
||||||
|
${
|
||||||
|
if (isArray) """
|
||||||
|
if (!new.contentEquals(internalValue)) {
|
||||||
|
internalValue = new
|
||||||
|
onElementChanged(this)
|
||||||
|
}
|
||||||
|
""".trim()
|
||||||
|
else """
|
||||||
|
if (new != internalValue) {
|
||||||
|
internalValue = new
|
||||||
|
onElementChanged(this)
|
||||||
|
}
|
||||||
|
""".trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override val serializer = $serializer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent() + "\n"
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ fun genPublicApi() = buildString {
|
|||||||
appendln(
|
appendln(
|
||||||
"""
|
"""
|
||||||
/**
|
/**
|
||||||
* !!! These primitive types are auto-generated by backend/codegen/src/main/kotlin/net.mamoe.mirai.console.codegen.ValuesCodegen.kt
|
* !!! This file is auto-generated by backend/codegen/src/main/kotlin/net.mamoe.mirai.console.codegen.ValuesCodegen.kt
|
||||||
* !!! for better performance
|
* !!! for better performance
|
||||||
* !!! DO NOT MODIFY THIS FILE MANUALLY
|
* !!! DO NOT MODIFY THIS FILE MANUALLY
|
||||||
*/
|
*/
|
||||||
@ -157,13 +157,14 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
|
|||||||
appendln()
|
appendln()
|
||||||
|
|
||||||
for (number in (NUMBERS + OTHER_PRIMITIVES).filterNot { it == "String" }) {
|
for (number in (NUMBERS + OTHER_PRIMITIVES).filterNot { it == "String" }) {
|
||||||
val template = """
|
appendln(
|
||||||
|
"""
|
||||||
abstract class ${number}ArrayValue internal constructor() : PrimitiveArrayValue<${number}Array>(), Iterable<${number}> {
|
abstract class ${number}ArrayValue internal constructor() : PrimitiveArrayValue<${number}Array>(), Iterable<${number}> {
|
||||||
override fun iterator(): Iterator<${number}> = this.value.iterator()
|
override fun iterator(): Iterator<${number}> = this.value.iterator()
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
appendln(template)
|
appendln()
|
||||||
}
|
}
|
||||||
|
|
||||||
appendln()
|
appendln()
|
||||||
@ -180,11 +181,11 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
|
|||||||
appendln()
|
appendln()
|
||||||
|
|
||||||
for (number in (NUMBERS + OTHER_PRIMITIVES)) {
|
for (number in (NUMBERS + OTHER_PRIMITIVES)) {
|
||||||
val template = """
|
appendln(
|
||||||
|
"""
|
||||||
abstract class Typed${number}ArrayValue internal constructor() : TypedPrimitiveArrayValue<${number}>()
|
abstract class Typed${number}ArrayValue internal constructor() : TypedPrimitiveArrayValue<${number}>()
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
appendln(template)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
appendln()
|
appendln()
|
||||||
@ -194,9 +195,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
|
|||||||
|
|
||||||
appendln(
|
appendln(
|
||||||
"""
|
"""
|
||||||
sealed class ${collectionName}Value<E> : Value<${collectionName}<E>>(), Iterable<E>{
|
sealed class ${collectionName}Value<E> : Value<${collectionName}<E>>(), ${collectionName}<E>
|
||||||
override fun iterator() = this.value.iterator()
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -265,7 +264,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
|
|||||||
* 只引用这个对象, 而不跟踪其成员.
|
* 只引用这个对象, 而不跟踪其成员.
|
||||||
* 仅适用于基础类型, 用于 mutable list/map 等情况; 或标注了 [Serializable] 的类.
|
* 仅适用于基础类型, 用于 mutable list/map 等情况; 或标注了 [Serializable] 的类.
|
||||||
*/
|
*/
|
||||||
abstract class DynamicReferenceValue<T : Any> internal constructor() : Value<T>()
|
abstract class DynamicReferenceValue<T : Any> : Value<T>()
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -1,4 +1,3 @@
|
|||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|
||||||
import upload.Bintray
|
import upload.Bintray
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -12,18 +11,46 @@ plugins {
|
|||||||
|
|
||||||
apply(plugin = "com.github.johnrengelman.shadow")
|
apply(plugin = "com.github.johnrengelman.shadow")
|
||||||
|
|
||||||
kotlin {
|
version = Versions.Mirai.console
|
||||||
sourceSets {
|
description = "Console backend for mirai"
|
||||||
all {
|
|
||||||
languageSettings.enableLanguageFeature("InlineClasses")
|
|
||||||
|
|
||||||
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
|
java {
|
||||||
languageSettings.useExperimentalAnnotation("kotlin.OptIn")
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
languageSettings.progressiveMode = true
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI")
|
}
|
||||||
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
|
|
||||||
languageSettings.useExperimentalAnnotation("kotlin.experimental.ExperimentalTypeInference")
|
tasks.withType(JavaCompile::class.java) {
|
||||||
languageSettings.useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts")
|
options.encoding = "UTF8"
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets.all {
|
||||||
|
target.compilations.all {
|
||||||
|
kotlinOptions {
|
||||||
|
freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=enable"
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
languageSettings.apply {
|
||||||
|
enableLanguageFeature("InlineClasses")
|
||||||
|
progressiveMode = true
|
||||||
|
|
||||||
|
useExperimentalAnnotation("kotlin.Experimental")
|
||||||
|
useExperimentalAnnotation("kotlin.OptIn")
|
||||||
|
|
||||||
|
useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI")
|
||||||
|
useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiExperimentalAPI")
|
||||||
|
useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
|
||||||
|
useExperimentalAnnotation("kotlin.experimental.ExperimentalTypeInference")
|
||||||
|
useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
getByName("test") {
|
||||||
|
languageSettings.apply {
|
||||||
|
languageVersion = "1.4"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,39 +59,17 @@ dependencies {
|
|||||||
compileAndRuntime("net.mamoe:mirai-core:${Versions.Mirai.core}")
|
compileAndRuntime("net.mamoe:mirai-core:${Versions.Mirai.core}")
|
||||||
compileAndRuntime(kotlin("stdlib"))
|
compileAndRuntime(kotlin("stdlib"))
|
||||||
|
|
||||||
api("net.mamoe.yamlkt:yamlkt:0.2.0")
|
api("net.mamoe.yamlkt:yamlkt:0.3.1")
|
||||||
|
|
||||||
api("org.jsoup:jsoup:1.12.1")
|
|
||||||
|
|
||||||
api("org.jetbrains:annotations:19.0.0")
|
api("org.jetbrains:annotations:19.0.0")
|
||||||
|
api(kotlinx("coroutines-jdk8", Versions.Kotlin.coroutines))
|
||||||
|
|
||||||
testApi("net.mamoe:mirai-core-qqandroid:${Versions.Mirai.core}")
|
testApi("net.mamoe:mirai-core-qqandroid:${Versions.Mirai.core}")
|
||||||
testApi(kotlin("stdlib"))
|
testApi(kotlin("stdlib-jdk8"))
|
||||||
testApi(kotlin("test"))
|
testApi(kotlin("test"))
|
||||||
testApi(kotlin("test-junit5"))
|
testApi(kotlin("test-junit5"))
|
||||||
}
|
}
|
||||||
|
|
||||||
version = Versions.Mirai.console
|
// region PUBLISHING
|
||||||
|
|
||||||
description = "Console backend for mirai"
|
|
||||||
|
|
||||||
val compileKotlin: KotlinCompile by tasks
|
|
||||||
compileKotlin.kotlinOptions {
|
|
||||||
jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
val compileTestKotlin: KotlinCompile by tasks
|
|
||||||
compileTestKotlin.kotlinOptions {
|
|
||||||
jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
java {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
tasks.withType(JavaCompile::class.java) {
|
|
||||||
options.encoding = "UTF8"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
tasks.register("ensureBintrayAvailable") {
|
tasks.register("ensureBintrayAvailable") {
|
||||||
doLast {
|
doLast {
|
||||||
@ -130,3 +135,5 @@ if (Bintray.isBintrayAvailable(project)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else println("bintray isn't available. NO PUBLICATIONS WILL BE SET")
|
} else println("bintray isn't available. NO PUBLICATIONS WILL BE SET")
|
||||||
|
|
||||||
|
// endregion
|
@ -1,51 +0,0 @@
|
|||||||
package net.mamoe.mirai.console.event;
|
|
||||||
|
|
||||||
import net.mamoe.mirai.console.plugins.PluginBase;
|
|
||||||
import net.mamoe.mirai.event.Event;
|
|
||||||
import net.mamoe.mirai.event.Listener;
|
|
||||||
import net.mamoe.mirai.event.ListeningStatus;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class EventListener {
|
|
||||||
|
|
||||||
PluginBase base;
|
|
||||||
|
|
||||||
public EventListener(
|
|
||||||
PluginBase base
|
|
||||||
){
|
|
||||||
this.base = base;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 监听一个事件, 当 {@code onEvent} 返回 {@link ListeningStatus#STOPPED} 时停止监听.
|
|
||||||
* 机器人离线后不会停止监听.
|
|
||||||
*
|
|
||||||
* @param eventClass 事件类
|
|
||||||
* @param onEvent 事件处理. 返回 {@link ListeningStatus#LISTENING} 时继续监听.
|
|
||||||
* @param <E> 事件类型
|
|
||||||
* @return 事件监听器. 可调用 {@link Listener#complete()} 或 {@link Listener#completeExceptionally(Throwable)} 让监听正常停止或异常停止.
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
public <E extends Event> Listener<E> subscribe(@NotNull Class<E> eventClass, @NotNull Function<E, ListeningStatus> onEvent) {
|
|
||||||
return EventsImplKt.subscribeEventForJaptOnly(eventClass, base, onEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 监听一个事件, 直到手动停止.
|
|
||||||
* 机器人离线后不会停止监听.
|
|
||||||
*
|
|
||||||
* @param eventClass 事件类
|
|
||||||
* @param onEvent 事件处理. 返回 {@link ListeningStatus#LISTENING} 时继续监听.
|
|
||||||
* @param <E> 事件类型
|
|
||||||
* @return 事件监听器. 可调用 {@link Listener#complete()} 或 {@link Listener#completeExceptionally(Throwable)} 让监听正常停止或异常停止.
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
public <E extends Event> Listener<E> subscribeAlways(@NotNull Class<E> eventClass, @NotNull Consumer<E> onEvent) {
|
|
||||||
return EventsImplKt.subscribeEventForJaptOnly(eventClass, base, onEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.console.event;
|
|
||||||
|
|
||||||
import net.mamoe.mirai.event.Event;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 事件处理
|
|
||||||
*/
|
|
||||||
public final class Events {
|
|
||||||
/**
|
|
||||||
* 阻塞地广播一个事件.
|
|
||||||
*
|
|
||||||
* @param event 事件
|
|
||||||
* @param <E> 事件类型
|
|
||||||
* @return {@code event} 本身
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
public static <E extends Event> E broadcast(@NotNull E event) {
|
|
||||||
return EventsImplKt.broadcast(event);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package net.mamoe.mirai.console.scheduler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Java开发者的SchedulerTask
|
|
||||||
* 使用kt实现, java的API
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PluginScheduler.RepeatTaskReceipt repeatTaskReceipt = this.getScheduler().repeat(() -> {
|
|
||||||
* getLogger().info("I repeat");
|
|
||||||
* },100);
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* this.getScheduler().delay(() -> {
|
|
||||||
* repeatTaskReceipt.setCancelled(true);
|
|
||||||
* },10000);
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Future<String> future = this.getScheduler().async(() -> {
|
|
||||||
* //do some task
|
|
||||||
* return "success";
|
|
||||||
* });
|
|
||||||
*
|
|
||||||
* try {
|
|
||||||
* getLogger().info(future.get());
|
|
||||||
* } catch (InterruptedException | ExecutionException e) {
|
|
||||||
* e.printStackTrace();
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class SchedulerTaskManager {
|
|
||||||
public static SchedulerTaskManagerInstance getInstance(){
|
|
||||||
return SchedulerTaskManagerInstance.INSTANCE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
package net.mamoe.mirai.console.utils;
|
package net.mamoe.mirai.console.utils;
|
||||||
|
|
||||||
import net.mamoe.mirai.Bot;
|
import net.mamoe.mirai.Bot;
|
||||||
@ -5,8 +14,8 @@ import net.mamoe.mirai.Bot;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取Bot Manager
|
* 获取 Bot Manager
|
||||||
* Java友好API
|
* Java 友好 API
|
||||||
*/
|
*/
|
||||||
public class BotManager {
|
public class BotManager {
|
||||||
|
|
||||||
@ -15,15 +24,15 @@ public class BotManager {
|
|||||||
return getManagers(bot);
|
return getManagers(bot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Long> getManagers(Bot bot){
|
public static List<Long> getManagers(Bot bot) {
|
||||||
return BotHelperKt.getBotManagers(bot);
|
return BotHelperKt.getBotManagers(bot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isManager(Bot bot, long target){
|
public static boolean isManager(Bot bot, long target) {
|
||||||
return getManagers(bot).contains(target);
|
return getManagers(bot).contains(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isManager(long botAccount, long target){
|
public static boolean isManager(long botAccount, long target) {
|
||||||
return getManagers(botAccount).contains(target);
|
return getManagers(botAccount).contains(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
package net.mamoe.mirai.console.utils;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Range;
|
|
||||||
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
|
|
||||||
public final class Utils {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行N次 callable
|
|
||||||
* 成功一次就会结束
|
|
||||||
* 否则就会throw
|
|
||||||
*/
|
|
||||||
public static <T> T tryNTimes(@Range(from = 1, to = Integer.MAX_VALUE) int n,
|
|
||||||
Callable<T> callable) throws Exception {
|
|
||||||
Exception last = null;
|
|
||||||
|
|
||||||
while (n-- > 0) {
|
|
||||||
try {
|
|
||||||
return callable.call();
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (last == null) {
|
|
||||||
last = e;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
last.addSuppressed(e);
|
|
||||||
} catch (Throwable ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw last;
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,35 +13,19 @@ import kotlinx.coroutines.CoroutineScope
|
|||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.io.charsets.Charset
|
import kotlinx.io.charsets.Charset
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.console.utils.MiraiConsoleFrontEnd
|
import net.mamoe.mirai.console.plugins.PluginLoader
|
||||||
|
import net.mamoe.mirai.console.plugins.builtin.JarPluginLoader
|
||||||
import net.mamoe.mirai.utils.DefaultLogger
|
import net.mamoe.mirai.utils.DefaultLogger
|
||||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.File
|
||||||
import java.io.PrintStream
|
import java.io.PrintStream
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
// 前端使用
|
/**
|
||||||
interface IMiraiConsole : CoroutineScope {
|
* mirai 控制台实例.
|
||||||
val build: String
|
|
||||||
val version: String
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Console运行路径
|
|
||||||
*/
|
*/
|
||||||
val path: String
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Console前端接口
|
|
||||||
*/
|
|
||||||
val frontEnd: MiraiConsoleFrontEnd
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 与前端交互所使用的Logger
|
|
||||||
*/
|
|
||||||
val mainLogger: MiraiLogger
|
|
||||||
}
|
|
||||||
|
|
||||||
object MiraiConsole : CoroutineScope, IMiraiConsole {
|
object MiraiConsole : CoroutineScope, IMiraiConsole {
|
||||||
private lateinit var instance: IMiraiConsole
|
private lateinit var instance: IMiraiConsole
|
||||||
|
|
||||||
@ -52,19 +36,17 @@ object MiraiConsole : CoroutineScope, IMiraiConsole {
|
|||||||
|
|
||||||
override val build: String get() = instance.build
|
override val build: String get() = instance.build
|
||||||
override val version: String get() = instance.version
|
override val version: String get() = instance.version
|
||||||
override val path: String get() = instance.path
|
override val rootDir: File get() = instance.rootDir
|
||||||
override val frontEnd: MiraiConsoleFrontEnd get() = instance.frontEnd
|
override val frontEnd: MiraiConsoleFrontEnd get() = instance.frontEnd
|
||||||
override val mainLogger: MiraiLogger get() = instance.mainLogger
|
override val mainLogger: MiraiLogger get() = instance.mainLogger
|
||||||
override val coroutineContext: CoroutineContext get() = instance.coroutineContext
|
override val coroutineContext: CoroutineContext get() = instance.coroutineContext
|
||||||
|
|
||||||
|
override val builtInPluginLoaders: List<PluginLoader<*, *>> = instance.builtInPluginLoaders
|
||||||
|
|
||||||
init {
|
init {
|
||||||
DefaultLogger = {
|
DefaultLogger = { identity -> this.newLogger(identity) }
|
||||||
this.newLogger(it)
|
|
||||||
}
|
|
||||||
this.coroutineContext[Job]!!.invokeOnCompletion {
|
this.coroutineContext[Job]!!.invokeOnCompletion {
|
||||||
Bot.botInstances.forEach {
|
Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) }
|
||||||
it.close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,6 +54,36 @@ object MiraiConsole : CoroutineScope, IMiraiConsole {
|
|||||||
fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity)
|
fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 前端使用
|
||||||
|
internal interface IMiraiConsole : CoroutineScope {
|
||||||
|
val build: String
|
||||||
|
val version: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Console 运行路径
|
||||||
|
*/
|
||||||
|
val rootDir: File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Console 前端接口
|
||||||
|
*/
|
||||||
|
val frontEnd: MiraiConsoleFrontEnd
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 与前端交互所使用的 Logger
|
||||||
|
*/
|
||||||
|
val mainLogger: MiraiLogger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内建加载器列表, 一般需要包含 [JarPluginLoader]
|
||||||
|
*/
|
||||||
|
val builtInPluginLoaders: List<PluginLoader<*, *>>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Included in kotlin stdlib 1.4
|
||||||
|
*/
|
||||||
internal val Throwable.stacktraceString: String
|
internal val Throwable.stacktraceString: String
|
||||||
get() =
|
get() =
|
||||||
ByteArrayOutputStream().apply {
|
ByteArrayOutputStream().apply {
|
||||||
|
@ -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.utils
|
package net.mamoe.mirai.console
|
||||||
|
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.console.center.CuiPluginCenter
|
import net.mamoe.mirai.console.center.CuiPluginCenter
|
@ -1,89 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* 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:OptIn(MiraiExperimentalAPI::class)
|
||||||
|
|
||||||
package net.mamoe.mirai.console.center
|
package net.mamoe.mirai.console.center
|
||||||
|
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.engine.cio.CIO
|
import io.ktor.client.engine.cio.CIO
|
||||||
import io.ktor.client.request.get
|
import io.ktor.client.request.get
|
||||||
import io.ktor.util.KtorExperimentalAPI
|
import io.ktor.util.KtorExperimentalAPI
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.UnstableDefault
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.JsonConfiguration
|
||||||
import net.mamoe.mirai.console.utils.retryCatching
|
import net.mamoe.mirai.console.utils.retryCatching
|
||||||
|
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
@OptIn(UnstableDefault::class)
|
||||||
|
internal val json = Json(JsonConfiguration(isLenient = true, ignoreUnknownKeys = true))
|
||||||
|
|
||||||
|
@OptIn(KtorExperimentalAPI::class)
|
||||||
|
internal val Http = HttpClient(CIO)
|
||||||
|
|
||||||
internal object CuiPluginCenter : PluginCenter {
|
internal object CuiPluginCenter : PluginCenter {
|
||||||
|
|
||||||
var plugins: JsonArray? = null
|
var plugins: List<PluginCenter.PluginInsight>? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 一页10个吧,pageMinNum=1
|
* 一页 10 个 pageMinNum=1
|
||||||
*/
|
*/
|
||||||
override suspend fun fetchPlugin(page: Int): Map<String, PluginCenter.PluginInsight> {
|
override suspend fun fetchPlugin(page: Int): Map<String, PluginCenter.PluginInsight> {
|
||||||
check(page > 0)
|
check(page > 0)
|
||||||
val startIndex = (page - 1) * 10
|
val startIndex = (page - 1) * 10
|
||||||
val endIndex = startIndex + 9
|
val endIndex = startIndex + 9
|
||||||
val map = mutableMapOf<String, PluginCenter.PluginInsight>()
|
val map = mutableMapOf<String, PluginCenter.PluginInsight>()
|
||||||
(startIndex until endIndex).forEach {
|
(startIndex until endIndex).forEach { index ->
|
||||||
if (plugins == null) {
|
val plugins = plugins ?: kotlin.run {
|
||||||
refresh()
|
refresh()
|
||||||
}
|
plugins
|
||||||
if (it >= plugins!!.size) {
|
} ?: return mapOf()
|
||||||
|
|
||||||
|
if (index >= plugins.size) {
|
||||||
return@forEach
|
return@forEach
|
||||||
}
|
}
|
||||||
val info = plugins!![it]
|
|
||||||
with(info.jsonObject) {
|
map[name] = plugins[index]
|
||||||
map[this["name"]!!.toString()] = PluginCenter.PluginInsight(
|
|
||||||
this["name"]?.primitive?.content ?: "",
|
|
||||||
this["version"]?.primitive?.content ?: "",
|
|
||||||
this["core"]?.primitive?.content ?: "",
|
|
||||||
this["console"]?.primitive?.content ?: "",
|
|
||||||
this["author"]?.primitive?.content ?: "",
|
|
||||||
this["description"]?.primitive?.content ?: "",
|
|
||||||
this["tags"]?.jsonArray?.map { it.primitive.content } ?: arrayListOf(),
|
|
||||||
this["commands"]?.jsonArray?.map { it.primitive.content } ?: arrayListOf()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return map
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(KtorExperimentalAPI::class)
|
|
||||||
private val Http = HttpClient(CIO)
|
|
||||||
|
|
||||||
override suspend fun findPlugin(name: String): PluginCenter.PluginInfo? {
|
override suspend fun findPlugin(name: String): PluginCenter.PluginInfo? {
|
||||||
val result = retryCatching(3) {
|
val result = retryCatching(3) {
|
||||||
Http.get<String>("https://miraiapi.jasonczc.cn/getPluginDetailedInfo?name=$name")
|
Http.get<String>("https://miraiapi.jasonczc.cn/getPluginDetailedInfo?name=$name")
|
||||||
}.recover {
|
}.getOrElse { return null }
|
||||||
return null
|
if (result == "err:not found") return null
|
||||||
}.getOrNull() ?: return null
|
|
||||||
|
|
||||||
if (result == "err:not found") {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.asJson().run {
|
|
||||||
PluginCenter.PluginInfo(
|
|
||||||
this["name"]?.primitive?.content ?: "",
|
|
||||||
this["version"]?.primitive?.content ?: "",
|
|
||||||
this["core"]?.primitive?.content ?: "",
|
|
||||||
this["console"]?.primitive?.content ?: "",
|
|
||||||
this["tags"]?.jsonArray?.map { it.primitive.content } ?: arrayListOf(),
|
|
||||||
this["author"]?.primitive?.content ?: "",
|
|
||||||
this["contact"]?.primitive?.content ?: "",
|
|
||||||
this["description"]?.primitive?.content ?: "",
|
|
||||||
this["usage"]?.primitive?.content ?: "",
|
|
||||||
this["vsc"]?.primitive?.content ?: "",
|
|
||||||
this["commands"]?.jsonArray?.map { it.primitive.content } ?: arrayListOf(),
|
|
||||||
this["changeLog"]?.jsonArray?.map { it.primitive.content } ?: arrayListOf()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return json.parse(PluginCenter.PluginInfo.serializer(), result)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun refresh() {
|
override suspend fun refresh() {
|
||||||
val results = Http.get<String>("https://miraiapi.jasonczc.cn/getPluginList").asJson()
|
|
||||||
|
|
||||||
if (!(results.containsKey("success") && results["success"]?.boolean == true)) {
|
@Serializable
|
||||||
error("Failed to fetch plugin list from Cui Cloud")
|
data class Result(
|
||||||
}
|
val success: Boolean,
|
||||||
plugins = results["result"]?.jsonArray//先不解析
|
val result: List<PluginCenter.PluginInsight>
|
||||||
|
)
|
||||||
|
|
||||||
|
val result = json.parse(Result.serializer(), Http.get("https://miraiapi.jasonczc.cn/getPluginList"))
|
||||||
|
|
||||||
|
check(result.success) { "Failed to fetch plugin list from Cui Cloud" }
|
||||||
|
plugins = result.result
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun <T : Any> T.downloadPlugin(name: String, progressListener: T.(Float) -> Unit): File {
|
override suspend fun <T : Any> T.downloadPlugin(name: String, progressListener: T.(Float) -> Unit): File {
|
||||||
@ -112,15 +105,6 @@ internal object CuiPluginCenter : PluginCenter {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
override val name: String
|
override val name: String get() = "崔云"
|
||||||
get() = "崔云"
|
|
||||||
|
|
||||||
|
|
||||||
private val json = Json(JsonConfiguration.Stable)
|
|
||||||
|
|
||||||
private fun String.asJson(): JsonObject {
|
|
||||||
return json.parseJson(this).jsonObject
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
package net.mamoe.mirai.console.center
|
package net.mamoe.mirai.console.center
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
@MiraiExperimentalAPI
|
||||||
interface PluginCenter {
|
interface PluginCenter {
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class PluginInsight(
|
data class PluginInsight(
|
||||||
val name: String,
|
val name: String,
|
||||||
val version: String,
|
val version: String,
|
||||||
|
@SerialName("core")
|
||||||
val coreVersion: String,
|
val coreVersion: String,
|
||||||
|
@SerialName("console")
|
||||||
val consoleVersion: String,
|
val consoleVersion: String,
|
||||||
val author: String,
|
val author: String,
|
||||||
val description: String,
|
val description: String,
|
||||||
@ -15,10 +31,13 @@ interface PluginCenter {
|
|||||||
val commands: List<String>
|
val commands: List<String>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class PluginInfo(
|
data class PluginInfo(
|
||||||
val name: String,
|
val name: String,
|
||||||
val version: String,
|
val version: String,
|
||||||
|
@SerialName("core")
|
||||||
val coreVersion: String,
|
val coreVersion: String,
|
||||||
|
@SerialName("console")
|
||||||
val consoleVersion: String,
|
val consoleVersion: String,
|
||||||
val tags: List<String>,
|
val tags: List<String>,
|
||||||
val author: String,
|
val author: String,
|
||||||
@ -32,7 +51,7 @@ interface PluginCenter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取一些中心的插件基本信息,
|
* 获取一些中心的插件基本信息,
|
||||||
* 能获取到多少由实际的PluginCenter决定
|
* 能获取到多少由实际的 [PluginCenter] 决定
|
||||||
* 返回 插件名->Insight
|
* 返回 插件名->Insight
|
||||||
*/
|
*/
|
||||||
suspend fun fetchPlugin(page: Int): Map<String, PluginInsight>
|
suspend fun fetchPlugin(page: Int): Map<String, PluginInsight>
|
||||||
@ -41,17 +60,18 @@ interface PluginCenter {
|
|||||||
* 尝试获取到某个插件 by 全名, case sensitive
|
* 尝试获取到某个插件 by 全名, case sensitive
|
||||||
* null 则没有
|
* null 则没有
|
||||||
*/
|
*/
|
||||||
suspend fun findPlugin(name:String):PluginInfo?
|
suspend fun findPlugin(name: String): PluginInfo?
|
||||||
|
|
||||||
|
|
||||||
suspend fun <T:Any> T.downloadPlugin(name:String, progressListener:T.(Float) -> Unit): File
|
suspend fun <T : Any> T.downloadPlugin(name: String, progressListener: T.(Float) -> Unit): File
|
||||||
|
|
||||||
suspend fun downloadPlugin(name:String, progressListener:PluginCenter.(Float) -> Unit): File = downloadPlugin<PluginCenter>(name,progressListener)
|
suspend fun downloadPlugin(name: String, progressListener: PluginCenter.(Float) -> Unit): File =
|
||||||
|
downloadPlugin<PluginCenter>(name, progressListener)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 刷新
|
* 刷新
|
||||||
*/
|
*/
|
||||||
suspend fun refresh()
|
suspend fun refresh()
|
||||||
|
|
||||||
val name:String
|
val name: String
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,9 @@ package net.mamoe.mirai.console.command
|
|||||||
|
|
||||||
import net.mamoe.mirai.console.command.description.*
|
import net.mamoe.mirai.console.command.description.*
|
||||||
import net.mamoe.mirai.console.command.description.CommandParam
|
import net.mamoe.mirai.console.command.description.CommandParam
|
||||||
import net.mamoe.mirai.console.command.hasAnnotation
|
import net.mamoe.mirai.console.command.description.CommandParserContext
|
||||||
|
import net.mamoe.mirai.console.command.description.EmptyCommandParserContext
|
||||||
|
import net.mamoe.mirai.console.command.description.plus
|
||||||
import net.mamoe.mirai.message.data.PlainText
|
import net.mamoe.mirai.message.data.PlainText
|
||||||
import net.mamoe.mirai.message.data.SingleMessage
|
import net.mamoe.mirai.message.data.SingleMessage
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* 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("NOTHING_TO_INLINE", "unused")
|
@file:Suppress("NOTHING_TO_INLINE", "unused")
|
||||||
@file:JvmName("CommandManager")
|
@file:JvmName("CommandManager")
|
||||||
|
|
||||||
package net.mamoe.mirai.console.command
|
package net.mamoe.mirai.console.command
|
||||||
|
|
||||||
import kotlinx.atomicfu.locks.withLock
|
import kotlinx.atomicfu.locks.withLock
|
||||||
import net.mamoe.mirai.console.plugins.PluginBase
|
import net.mamoe.mirai.console.plugins.Plugin
|
||||||
import net.mamoe.mirai.message.data.Message
|
import net.mamoe.mirai.message.data.Message
|
||||||
import net.mamoe.mirai.message.data.MessageChain
|
import net.mamoe.mirai.message.data.MessageChain
|
||||||
import net.mamoe.mirai.message.data.SingleMessage
|
import net.mamoe.mirai.message.data.SingleMessage
|
||||||
@ -13,7 +22,7 @@ sealed class CommandOwner
|
|||||||
|
|
||||||
object TestCommandOwner : CommandOwner()
|
object TestCommandOwner : CommandOwner()
|
||||||
|
|
||||||
abstract class PluginCommandOwner(val plugin: PluginBase) : CommandOwner()
|
abstract class PluginCommandOwner(val plugin: Plugin) : CommandOwner()
|
||||||
|
|
||||||
// 由前端实现
|
// 由前端实现
|
||||||
internal abstract class ConsoleCommandOwner : CommandOwner()
|
internal abstract class ConsoleCommandOwner : CommandOwner()
|
||||||
|
@ -117,7 +117,6 @@ inline fun CommandSender.hasPermission(permission: CommandPermission): Boolean =
|
|||||||
|
|
||||||
inline fun CommandPermission.testPermission(sender: CommandSender): Boolean = this.run { sender.hasPermission() }
|
inline fun CommandPermission.testPermission(sender: CommandSender): Boolean = this.run { sender.hasPermission() }
|
||||||
|
|
||||||
|
|
||||||
internal class OrCommandPermission(
|
internal class OrCommandPermission(
|
||||||
private val first: CommandPermission,
|
private val first: CommandPermission,
|
||||||
private val second: CommandPermission
|
private val second: CommandPermission
|
||||||
@ -127,6 +126,7 @@ internal class OrCommandPermission(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal class AndCommandPermission(
|
internal class AndCommandPermission(
|
||||||
private val first: CommandPermission,
|
private val first: CommandPermission,
|
||||||
private val second: CommandPermission
|
private val second: CommandPermission
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* 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("NOTHING_TO_INLINE")
|
@file:Suppress("NOTHING_TO_INLINE")
|
||||||
|
|
||||||
package net.mamoe.mirai.console.command.description
|
package net.mamoe.mirai.console.command.description
|
||||||
|
@ -10,11 +10,7 @@
|
|||||||
package net.mamoe.mirai.console.command.description
|
package net.mamoe.mirai.console.command.description
|
||||||
|
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.console.command.BotAwareCommandSender
|
import net.mamoe.mirai.console.command.*
|
||||||
import net.mamoe.mirai.console.command.CommandSender
|
|
||||||
import net.mamoe.mirai.console.command.MemberCommandSender
|
|
||||||
import net.mamoe.mirai.console.command.UserCommandSender
|
|
||||||
import net.mamoe.mirai.console.utils.fuzzySearchMember
|
|
||||||
import net.mamoe.mirai.contact.Friend
|
import net.mamoe.mirai.contact.Friend
|
||||||
import net.mamoe.mirai.contact.Group
|
import net.mamoe.mirai.contact.Group
|
||||||
import net.mamoe.mirai.contact.Member
|
import net.mamoe.mirai.contact.Member
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* 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")
|
@file:Suppress("unused")
|
||||||
|
|
||||||
package net.mamoe.mirai.console.command.description
|
package net.mamoe.mirai.console.command.description
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.console.command
|
package net.mamoe.mirai.console.command
|
||||||
|
|
||||||
|
import net.mamoe.mirai.contact.Group
|
||||||
|
import net.mamoe.mirai.contact.Member
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
|
||||||
|
|
||||||
@ -62,3 +64,96 @@ internal infix fun <T> Array<out T>.intersects(other: Array<out T>): Boolean {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
internal fun String.fuzzyCompare(target: String): Double {
|
||||||
|
var step = 0
|
||||||
|
if (this == target) {
|
||||||
|
return 1.0
|
||||||
|
}
|
||||||
|
if (target.length > this.length) {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
for (i in this.indices) {
|
||||||
|
if (target.length == i) {
|
||||||
|
step--
|
||||||
|
} else {
|
||||||
|
if (this[i] != target[i]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
step++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step == this.length - 1) {
|
||||||
|
return 1.0
|
||||||
|
}
|
||||||
|
return step.toDouble() / this.length
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模糊搜索一个List中index最接近target的东西
|
||||||
|
*/
|
||||||
|
internal inline fun <T : Any> Collection<T>.fuzzySearch(
|
||||||
|
target: String,
|
||||||
|
index: (T) -> String
|
||||||
|
): T? {
|
||||||
|
if (this.isEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
var potential: T? = null
|
||||||
|
var rate = 0.0
|
||||||
|
this.forEach {
|
||||||
|
val thisIndex = index(it)
|
||||||
|
if (thisIndex == target) {
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
with(thisIndex.fuzzyCompare(target)) {
|
||||||
|
if (this > rate) {
|
||||||
|
rate = this
|
||||||
|
potential = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return potential
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模糊搜索一个List中index最接近target的东西
|
||||||
|
* 并且确保target是唯一的
|
||||||
|
* 如搜索index为XXXXYY list中同时存在XXXXYYY XXXXYYYY 将返回null
|
||||||
|
*/
|
||||||
|
internal inline fun <T : Any> Collection<T>.fuzzySearchOnly(
|
||||||
|
target: String,
|
||||||
|
index: (T) -> String
|
||||||
|
): T? {
|
||||||
|
if (this.isEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
var potential: T? = null
|
||||||
|
var rate = 0.0
|
||||||
|
var collide = 0
|
||||||
|
this.forEach {
|
||||||
|
with(index(it).fuzzyCompare(target)) {
|
||||||
|
if (this > rate) {
|
||||||
|
rate = this
|
||||||
|
potential = it
|
||||||
|
}
|
||||||
|
if (this == 1.0) {
|
||||||
|
collide++
|
||||||
|
}
|
||||||
|
if (collide > 1) {
|
||||||
|
return null//collide
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return potential
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal fun Group.fuzzySearchMember(nameCardTarget: String): Member? {
|
||||||
|
return this.members.fuzzySearchOnly(nameCardTarget) {
|
||||||
|
it.nameCard
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
package net.mamoe.mirai.console.event
|
package net.mamoe.mirai.console.event
|
||||||
|
|
||||||
import net.mamoe.mirai.console.command.Command
|
import net.mamoe.mirai.console.command.Command
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
package net.mamoe.mirai.console.event
|
package net.mamoe.mirai.console.event
|
||||||
|
|
||||||
import net.mamoe.mirai.event.Event
|
import net.mamoe.mirai.event.Event
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
|
||||||
|
|
||||||
package net.mamoe.mirai.console.event
|
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import net.mamoe.mirai.event.Event
|
|
||||||
import net.mamoe.mirai.event.Listener
|
|
||||||
import net.mamoe.mirai.event.ListeningStatus
|
|
||||||
import net.mamoe.mirai.event.broadcast
|
|
||||||
import net.mamoe.mirai.event.internal._subscribeEventForJaptOnly
|
|
||||||
import java.util.function.Consumer
|
|
||||||
import java.util.function.Function
|
|
||||||
|
|
||||||
internal fun <E : Event> broadcast(e: E): E = runBlocking { e.broadcast() }
|
|
||||||
|
|
||||||
internal fun <E : Event> Class<E>.subscribeEventForJaptOnly(
|
|
||||||
scope: CoroutineScope,
|
|
||||||
onEvent: Function<E, ListeningStatus>
|
|
||||||
): Listener<E> = _subscribeEventForJaptOnly(scope, onEvent)
|
|
||||||
|
|
||||||
internal fun <E : Event> Class<E>.subscribeEventForJaptOnly(scope: CoroutineScope, onEvent: Consumer<E>): Listener<E> =
|
|
||||||
_subscribeEventForJaptOnly(scope, onEvent)
|
|
@ -1,426 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
|
||||||
|
|
||||||
|
|
||||||
package net.mamoe.mirai.console.plugins
|
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
|
|
||||||
sealed class JarPlugin : Plugin(), CoroutineScope {
|
|
||||||
internal lateinit var _description: JarPluginDescription
|
|
||||||
|
|
||||||
final override val description: PluginDescription get() = _description
|
|
||||||
final override val loader: JarPluginLoader get() = JarPluginLoader
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
internal class JarPluginDescription(
|
|
||||||
override val name: String,
|
|
||||||
override val author: String,
|
|
||||||
override val version: String,
|
|
||||||
override val info: String,
|
|
||||||
override val depends: List<String>
|
|
||||||
) : PluginDescription
|
|
||||||
|
|
||||||
abstract class JavaPlugin : JarPlugin()
|
|
||||||
|
|
||||||
abstract class KotlinPlugin : JarPlugin()
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 内建的 Jar (JVM) 插件加载器
|
|
||||||
*/
|
|
||||||
object JarPluginLoader : PluginLoader<JarPlugin> {
|
|
||||||
override val list: List<JarPlugin>
|
|
||||||
get() = TODO("Not yet implemented")
|
|
||||||
|
|
||||||
override fun load(plugin: JarPlugin) {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun enable(plugin: JarPlugin) {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
object PluginManagerOld {
|
|
||||||
/**
|
|
||||||
* 通过插件获取介绍
|
|
||||||
* @see description
|
|
||||||
*/
|
|
||||||
fun getPluginDescription(base: PluginBase): PluginDescription {
|
|
||||||
nameToPluginBaseMap.forEach { (s, pluginBase) ->
|
|
||||||
if (pluginBase == base) {
|
|
||||||
return pluginDescriptions[s]!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
error("can not find plugin description")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有插件摘要
|
|
||||||
*/
|
|
||||||
fun getAllPluginDescriptions(): Collection<PluginDescription> {
|
|
||||||
return pluginDescriptions.values
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭所有插件
|
|
||||||
*/
|
|
||||||
@JvmOverloads
|
|
||||||
fun disablePlugins(throwable: CancellationException? = null) {
|
|
||||||
pluginsSequence.forEach { plugin ->
|
|
||||||
plugin.unregisterAllCommands()
|
|
||||||
plugin.disable(throwable)
|
|
||||||
}
|
|
||||||
nameToPluginBaseMap.clear()
|
|
||||||
pluginDescriptions.clear()
|
|
||||||
pluginsLoader.clear()
|
|
||||||
pluginsSequence.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 重载所有插件
|
|
||||||
*/
|
|
||||||
fun reloadPlugins() {
|
|
||||||
pluginsSequence.forEach {
|
|
||||||
it.disable()
|
|
||||||
}
|
|
||||||
loadPlugins(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 尝试加载全部插件
|
|
||||||
*/
|
|
||||||
fun loadPlugins(clear: Boolean = true) = loadPluginsImpl(clear)
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////
|
|
||||||
//// internal ////
|
|
||||||
//////////////////
|
|
||||||
|
|
||||||
internal val pluginsPath = (MiraiConsole.path + "/plugins/").replace("//", "/").also {
|
|
||||||
File(it).mkdirs()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val logger = MiraiConsole.newLogger("Plugin Manager")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载成功的插件, 名字->插件
|
|
||||||
*/
|
|
||||||
internal val nameToPluginBaseMap: MutableMap<String, PluginBase> = mutableMapOf()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载成功的插件, 名字->插件摘要
|
|
||||||
*/
|
|
||||||
private val pluginDescriptions: MutableMap<String, PluginDescription> = mutableMapOf()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载插件的PluginsLoader
|
|
||||||
*/
|
|
||||||
private val pluginsLoader: PluginsLoader = PluginsLoader(this.javaClass.classLoader)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 插件优先级队列
|
|
||||||
* 任何操作应该按这个Sequence顺序进行
|
|
||||||
* 他的优先级取决于依赖,
|
|
||||||
* 在这个队列中, 被依赖的插件会在依赖的插件之前
|
|
||||||
*/
|
|
||||||
private val pluginsSequence: LockFreeLinkedList<PluginBase> = LockFreeLinkedList()
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 广播Command方法
|
|
||||||
*/
|
|
||||||
internal fun onCommand(command: Command, sender: CommandSender, args: List<String>) {
|
|
||||||
pluginsSequence.forEach {
|
|
||||||
try {
|
|
||||||
it.onCommand(command, sender, args)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
logger.info(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Volatile
|
|
||||||
internal var lastPluginName: String = ""
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断文件名/插件名是否已加载
|
|
||||||
*/
|
|
||||||
private fun isPluginLoaded(file: File, name: String): Boolean {
|
|
||||||
pluginDescriptions.forEach {
|
|
||||||
if (it.key == name || it.value.file == file) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 寻找所有安装的插件(在文件夹), 并将它读取, 记录位置
|
|
||||||
* 这个不等同于加载的插件, 可以理解为还没有加载的插件
|
|
||||||
*/
|
|
||||||
internal data class FindPluginsResult(
|
|
||||||
val pluginsLocation: MutableMap<String, File>,
|
|
||||||
val pluginsFound: MutableMap<String, PluginDescription>
|
|
||||||
)
|
|
||||||
|
|
||||||
internal fun findPlugins(): FindPluginsResult {
|
|
||||||
val pluginsLocation: MutableMap<String, File> = mutableMapOf()
|
|
||||||
val pluginsFound: MutableMap<String, PluginDescription> = mutableMapOf()
|
|
||||||
|
|
||||||
File(pluginsPath).listFiles()?.forEach { file ->
|
|
||||||
if (file != null && file.extension == "jar") {
|
|
||||||
val jar = JarFile(file)
|
|
||||||
val pluginYml =
|
|
||||||
jar.entries().asSequence().filter { it.name.toLowerCase().contains("plugin.yml") }.firstOrNull()
|
|
||||||
|
|
||||||
if (pluginYml == null) {
|
|
||||||
logger.info("plugin.yml not found in jar " + jar.name + ", it will not be consider as a Plugin")
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
val description = PluginDescription.readFromContent(
|
|
||||||
URL("jar:file:" + file.absoluteFile + "!/" + pluginYml.name).openConnection().let {
|
|
||||||
val res = it.inputStream.use { input ->
|
|
||||||
input.readBytes().encodeToString()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭jarFile,解决热更新插件问题
|
|
||||||
(it as JarURLConnection).jarFile.close()
|
|
||||||
res
|
|
||||||
}, file
|
|
||||||
)
|
|
||||||
if (!isPluginLoaded(file, description.name)) {
|
|
||||||
pluginsFound[description.name] = description
|
|
||||||
pluginsLocation[description.name] = file
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logger.info(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FindPluginsResult(pluginsLocation, pluginsFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun loadPluginsImpl(clear: Boolean = true) {
|
|
||||||
logger.info("""开始加载${pluginsPath}下的插件""")
|
|
||||||
val findPluginsResult = findPlugins()
|
|
||||||
val pluginsFound = findPluginsResult.pluginsFound
|
|
||||||
val pluginsLocation = findPluginsResult.pluginsLocation
|
|
||||||
|
|
||||||
//不仅要解决A->B->C->A, 还要解决A->B->A->A
|
|
||||||
fun checkNoCircularDepends(
|
|
||||||
target: PluginDescription,
|
|
||||||
needDepends: List<String>,
|
|
||||||
existDepends: MutableList<String>
|
|
||||||
) {
|
|
||||||
|
|
||||||
if (!target.noCircularDepend) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
existDepends.add(target.name)
|
|
||||||
|
|
||||||
if (needDepends.any { existDepends.contains(it) }) {
|
|
||||||
target.noCircularDepend = false
|
|
||||||
}
|
|
||||||
|
|
||||||
existDepends.addAll(needDepends)
|
|
||||||
|
|
||||||
needDepends.forEach {
|
|
||||||
if (pluginsFound.containsKey(it)) {
|
|
||||||
checkNoCircularDepends(pluginsFound[it]!!, pluginsFound[it]!!.depends, existDepends)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pluginsFound.values.forEach {
|
|
||||||
checkNoCircularDepends(it, it.depends, mutableListOf())
|
|
||||||
}
|
|
||||||
|
|
||||||
//load plugin individually
|
|
||||||
fun loadPlugin(description: PluginDescription): Boolean {
|
|
||||||
if (!description.noCircularDepend) {
|
|
||||||
logger.error("Failed to load plugin " + description.name + " because it has circular dependency")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (description.loaded || nameToPluginBaseMap.containsKey(description.name)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
description.depends.forEach { dependent ->
|
|
||||||
if (!pluginsFound.containsKey(dependent)) {
|
|
||||||
logger.error("Failed to load plugin " + description.name + " because it need " + dependent + " as dependency")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
val depend = pluginsFound[dependent]!!
|
|
||||||
|
|
||||||
if (!loadPlugin(depend)) {//先加载depend
|
|
||||||
logger.error("Failed to load plugin " + description.name + " because " + dependent + " as dependency failed to load")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("loading plugin " + description.name)
|
|
||||||
|
|
||||||
val jarFile = pluginsLocation[description.name]!!
|
|
||||||
val pluginClass = try {
|
|
||||||
pluginsLoader.loadPluginMainClassByJarFile(description.name, description.basePath, jarFile)
|
|
||||||
} catch (e: ClassNotFoundException) {
|
|
||||||
pluginsLoader.loadPluginMainClassByJarFile(description.name, "${description.basePath}Kt", jarFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
val subClass = pluginClass.asSubclass(PluginBase::class.java)
|
|
||||||
|
|
||||||
lastPluginName = description.name
|
|
||||||
val plugin: PluginBase =
|
|
||||||
subClass.kotlin.objectInstance ?: subClass.getDeclaredConstructor().apply {
|
|
||||||
kotlin.runCatching {
|
|
||||||
this.isAccessible = true
|
|
||||||
}
|
|
||||||
}.newInstance()
|
|
||||||
plugin.dataFolder // initialize right now
|
|
||||||
|
|
||||||
description.loaded = true
|
|
||||||
logger.info("successfully loaded plugin " + description.name + " version " + description.version + " by " + description.author)
|
|
||||||
logger.info(description.info)
|
|
||||||
|
|
||||||
nameToPluginBaseMap[description.name] = plugin
|
|
||||||
pluginDescriptions[description.name] = description
|
|
||||||
plugin.pluginName = description.name
|
|
||||||
pluginsSequence.addLast(plugin)//按照实际加载顺序加入队列
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (clear) {
|
|
||||||
//清掉优先级队列, 来重新填充
|
|
||||||
pluginsSequence.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
pluginsFound.values.forEach {
|
|
||||||
try {
|
|
||||||
// 尝试加载插件
|
|
||||||
loadPlugin(it)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
pluginsLoader.remove(it.name)
|
|
||||||
when (e) {
|
|
||||||
is ClassCastException -> logger.error(
|
|
||||||
"failed to load plugin " + it.name + " , Main class does not extends PluginBase",
|
|
||||||
e
|
|
||||||
)
|
|
||||||
is ClassNotFoundException -> logger.error(
|
|
||||||
"failed to load plugin " + it.name + " , Main class not found under " + it.basePath,
|
|
||||||
e
|
|
||||||
)
|
|
||||||
is NoClassDefFoundError -> logger.error(
|
|
||||||
"failed to load plugin " + it.name + " , dependent class not found.",
|
|
||||||
e
|
|
||||||
)
|
|
||||||
else -> logger.error("failed to load plugin " + it.name, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pluginsSequence.forEach {
|
|
||||||
try {
|
|
||||||
it.load()
|
|
||||||
} catch (ignored: Throwable) {
|
|
||||||
logger.info(ignored)
|
|
||||||
logger.info(it.pluginName + " failed to load, disabling it")
|
|
||||||
logger.info(it.pluginName + " 推荐立即删除/替换并重启")
|
|
||||||
if (ignored is CancellationException) {
|
|
||||||
disablePlugin(it, ignored)
|
|
||||||
} else {
|
|
||||||
disablePlugin(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pluginsSequence.forEach {
|
|
||||||
try {
|
|
||||||
it.enable()
|
|
||||||
} catch (ignored: Throwable) {
|
|
||||||
logger.info(ignored)
|
|
||||||
logger.info(it.pluginName + " failed to enable, disabling it")
|
|
||||||
logger.info(it.pluginName + " 推荐立即删除/替换并重启")
|
|
||||||
if (ignored is CancellationException) {
|
|
||||||
disablePlugin(it, ignored)
|
|
||||||
} else {
|
|
||||||
disablePlugin(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("""加载了${nameToPluginBaseMap.size}个插件""")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun disablePlugin(
|
|
||||||
plugin: PluginBase,
|
|
||||||
exception: CancellationException? = null
|
|
||||||
) {
|
|
||||||
plugin.unregisterAllCommands()
|
|
||||||
plugin.disable(exception)
|
|
||||||
nameToPluginBaseMap.remove(plugin.pluginName)
|
|
||||||
pluginDescriptions.remove(plugin.pluginName)
|
|
||||||
pluginsLoader.remove(plugin.pluginName)
|
|
||||||
pluginsSequence.remove(plugin)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据插件名字找Jar的文件
|
|
||||||
* null => 没找到
|
|
||||||
* 这里的url的jarFile没关,热更新插件可能出事
|
|
||||||
*/
|
|
||||||
internal fun getJarFileByName(pluginName: String): File? {
|
|
||||||
File(pluginsPath).listFiles()?.forEach { file ->
|
|
||||||
if (file != null && file.extension == "jar") {
|
|
||||||
val jar = JarFile(file)
|
|
||||||
val pluginYml =
|
|
||||||
jar.entries().asSequence().filter { it.name.toLowerCase().contains("plugin.yml") }.firstOrNull()
|
|
||||||
if (pluginYml != null) {
|
|
||||||
val description =
|
|
||||||
PluginDescription.readFromContent(
|
|
||||||
URL("jar:file:" + file.absoluteFile + "!/" + pluginYml.name).openConnection().inputStream.use {
|
|
||||||
it.readBytes().encodeToString()
|
|
||||||
}, file
|
|
||||||
)
|
|
||||||
if (description.name.toLowerCase() == pluginName.toLowerCase()) {
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据插件名字找Jar中的文件
|
|
||||||
* null => 没找到
|
|
||||||
* 这里的url的jarFile没关,热更新插件可能出事
|
|
||||||
*/
|
|
||||||
internal fun getFileInJarByName(pluginName: String, toFind: String): InputStream? {
|
|
||||||
val jarFile = getJarFileByName(pluginName) ?: return null
|
|
||||||
val jar = JarFile(jarFile)
|
|
||||||
val toFindFile =
|
|
||||||
jar.entries().asSequence().filter { it.name == toFind }.firstOrNull() ?: return null
|
|
||||||
return URL("jar:file:" + jarFile.absoluteFile + "!/" + toFindFile.name).openConnection().inputStream
|
|
||||||
}
|
|
||||||
}*/
|
|
@ -9,45 +9,17 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.console.plugins
|
package net.mamoe.mirai.console.plugins
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import net.mamoe.mirai.console.plugins.builtin.JvmPlugin
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.SupervisorJob
|
|
||||||
import net.mamoe.mirai.console.MiraiConsole
|
|
||||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 插件信息
|
* 表示一个 mirai-console 插件.
|
||||||
*/
|
|
||||||
interface PluginDescription {
|
|
||||||
val name: String
|
|
||||||
val author: String
|
|
||||||
val version: String
|
|
||||||
val info: String
|
|
||||||
val depends: List<String>
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 插件基类.
|
|
||||||
*
|
*
|
||||||
* 内建的插件类型:
|
* @see JvmPlugin
|
||||||
* - [JarPlugin]
|
* @see PluginDescription 插件描述
|
||||||
*/
|
*/
|
||||||
abstract class Plugin : CoroutineScope {
|
interface Plugin {
|
||||||
abstract val description: PluginDescription
|
/**
|
||||||
abstract val loader: PluginLoader<*>
|
* 所属插件加载器实例
|
||||||
|
*/
|
||||||
@OptIn(MiraiExperimentalAPI::class)
|
val loader: PluginLoader<*, *>
|
||||||
val logger: MiraiLogger by lazy { MiraiConsole.newLogger(description.name) }
|
|
||||||
|
|
||||||
override val coroutineContext: CoroutineContext
|
|
||||||
get() = SupervisorJob(MiraiConsole.coroutineContext[Job]) + CoroutineExceptionHandler { _, throwable ->
|
|
||||||
logger.error(throwable)
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun onLoaded() {}
|
|
||||||
open fun onDisabled() {}
|
|
||||||
open fun onEnabled() {}
|
|
||||||
}
|
}
|
@ -1,149 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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("MemberVisibilityCanBePrivate", "unused")
|
|
||||||
|
|
||||||
package net.mamoe.mirai.console.plugins
|
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import net.mamoe.mirai.console.command.Command
|
|
||||||
import net.mamoe.mirai.console.command.CommandSender
|
|
||||||
import net.mamoe.mirai.console.event.EventListener
|
|
||||||
import net.mamoe.mirai.console.scheduler.PluginScheduler
|
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStream
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 所有插件的基类
|
|
||||||
*/
|
|
||||||
abstract class PluginBase
|
|
||||||
@JvmOverloads constructor(coroutineContext: CoroutineContext = EmptyCoroutineContext) : CoroutineScope {
|
|
||||||
final override val coroutineContext: CoroutineContext = coroutineContext + SupervisorJob()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 插件被分配的数据目录。数据目录会与插件名称同名。
|
|
||||||
*/
|
|
||||||
val dataFolder: File by lazy {
|
|
||||||
TODO()
|
|
||||||
/*
|
|
||||||
File(PluginManager.pluginsPath + "/" + PluginManager.lastPluginName)
|
|
||||||
.also { it.mkdir() }*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 当一个插件被加载时调用
|
|
||||||
*/
|
|
||||||
open fun onLoad() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 当插件被启用时调用.
|
|
||||||
* 此时所有其他插件都已经被调用了 [onLoad]
|
|
||||||
*/
|
|
||||||
open fun onEnable() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 当插件关闭前被调用
|
|
||||||
*/
|
|
||||||
open fun onDisable() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 当任意指令被使用时调用.
|
|
||||||
*
|
|
||||||
* 指令调用将优先触发 [Command.onCommand], 若该函数返回 `false`, 则不会调用 [PluginBase.onCommand]
|
|
||||||
*/
|
|
||||||
open fun onCommand(command: Command, sender: CommandSender, args: List<String>) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 插件的日志
|
|
||||||
*/
|
|
||||||
val logger: MiraiLogger by lazy {
|
|
||||||
TODO()
|
|
||||||
/*
|
|
||||||
SimpleLogger("Plugin $pluginName") { priority, message, e ->
|
|
||||||
val identityString = "[${pluginName}]"
|
|
||||||
MiraiConsole.logger(priority, identityString, 0, message)
|
|
||||||
if (e != null) {
|
|
||||||
MiraiConsole.logger(priority, identityString, 0, e)
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载 resources 中的文件
|
|
||||||
*/
|
|
||||||
fun getResources(fileName: String): InputStream? {
|
|
||||||
return try {
|
|
||||||
this.javaClass.classLoader.getResourceAsStream(fileName)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
TODO()
|
|
||||||
|
|
||||||
/*
|
|
||||||
PluginManager.getFileInJarByName(
|
|
||||||
this.pluginName,
|
|
||||||
fileName
|
|
||||||
)*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Java API Scheduler
|
|
||||||
*/
|
|
||||||
val scheduler: PluginScheduler? = PluginScheduler(this.coroutineContext)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Java API EventListener
|
|
||||||
*/
|
|
||||||
val eventListener: EventListener = EventListener(@Suppress("LeakingThis") this)
|
|
||||||
|
|
||||||
|
|
||||||
// internal
|
|
||||||
|
|
||||||
private var loaded = false
|
|
||||||
private var enabled = false
|
|
||||||
|
|
||||||
internal fun load() {
|
|
||||||
if (!loaded) {
|
|
||||||
onLoad()
|
|
||||||
loaded = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun enable() {
|
|
||||||
if (!enabled) {
|
|
||||||
onEnable()
|
|
||||||
enabled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun disable(throwable: CancellationException? = null) {
|
|
||||||
if (enabled) {
|
|
||||||
this.coroutineContext[Job]!!.cancelChildren(throwable)
|
|
||||||
try {
|
|
||||||
onDisable()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logger.error(e)
|
|
||||||
}
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal var pluginName: String = ""
|
|
||||||
}
|
|
@ -1,29 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* 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.plugins
|
package net.mamoe.mirai.console.plugins
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 插件加载器
|
* 插件加载器.
|
||||||
*
|
*
|
||||||
|
* 插件加载器只实现寻找插件列表, 加载插件, 启用插件, 关闭插件这四个功能.
|
||||||
|
*
|
||||||
|
* 有关插件的依赖和已加载的插件列表由 [PluginManager] 维护.
|
||||||
|
*/
|
||||||
|
interface PluginLoader<P : Plugin, D : PluginDescription> {
|
||||||
|
/**
|
||||||
|
* 扫描并返回可以被加载的插件的 [描述][PluginDescription] 列表. 此函数只会被调用一次
|
||||||
|
*/
|
||||||
|
fun listPlugins(): List<D>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取此插件的描述
|
||||||
|
*/
|
||||||
|
@Throws(PluginLoadException::class)
|
||||||
|
fun getPluginDescription(plugin: P): D
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载一个插件 (实例), 但不 [启用][enable] 它. 返回加载成功的实例
|
||||||
|
*
|
||||||
|
* @throws PluginLoadException 在加载插件遇到意料之中的错误时抛出 (如找不到主类等).
|
||||||
|
*/
|
||||||
|
@Throws(PluginLoadException::class)
|
||||||
|
fun load(description: D): P
|
||||||
|
|
||||||
|
fun enable(plugin: P)
|
||||||
|
fun disable(plugin: P)
|
||||||
|
}
|
||||||
|
|
||||||
|
open class PluginLoadException : RuntimeException {
|
||||||
|
constructor() : super()
|
||||||
|
constructor(message: String?) : super(message)
|
||||||
|
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||||
|
constructor(cause: Throwable?) : super(cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* '/plugins' 目录中的插件的加载器. 每个加载器需绑定一个后缀.
|
||||||
|
*
|
||||||
|
* @see AbstractFilePluginLoader
|
||||||
* @see JarPluginLoader 内建的 Jar (JVM) 插件加载器.
|
* @see JarPluginLoader 内建的 Jar (JVM) 插件加载器.
|
||||||
*/
|
*/
|
||||||
interface PluginLoader<P : Plugin> {
|
interface FilePluginLoader<P : Plugin, D : PluginDescription> : PluginLoader<P, D> {
|
||||||
val list: List<P>
|
/**
|
||||||
|
* 所支持的插件文件后缀, 不含 '.'. 如 [JarPluginLoader] 为 "jar"
|
||||||
fun loadAll() = list.forEach(::load)
|
*/
|
||||||
fun enableAll() = list.forEach(::enable)
|
val fileSuffix: String
|
||||||
fun unloadAll() = list.forEach(::unload)
|
}
|
||||||
fun reloadAll() = list.forEach(::reload)
|
|
||||||
|
abstract class AbstractFilePluginLoader<P : Plugin, D : PluginDescription>(
|
||||||
val isUnloadSupported: Boolean
|
override val fileSuffix: String
|
||||||
get() = false
|
) : FilePluginLoader<P, D> {
|
||||||
|
private fun pluginsFilesSequence(): Sequence<File> =
|
||||||
fun load(plugin: P)
|
PluginManager.pluginsDir.walk().filter { it.isFile && it.name.endsWith(fileSuffix, ignoreCase = true) }
|
||||||
fun enable(plugin: P)
|
|
||||||
fun unload(plugin: P) {
|
protected abstract fun Sequence<File>.mapToDescription(): List<D>
|
||||||
error("NotImplemented")
|
|
||||||
}
|
final override fun listPlugins(): List<D> = pluginsFilesSequence().mapToDescription()
|
||||||
|
|
||||||
fun reload(plugin: P) {
|
|
||||||
unload(plugin)
|
|
||||||
load(plugin)
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,24 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* 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("NOTHING_TO_INLINE")
|
||||||
|
|
||||||
package net.mamoe.mirai.console.plugins
|
package net.mamoe.mirai.console.plugins
|
||||||
|
|
||||||
|
import kotlinx.atomicfu.locks.withLock
|
||||||
|
import net.mamoe.mirai.console.MiraiConsole
|
||||||
|
import java.io.File
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
|
||||||
|
val Plugin.description: PluginDescription
|
||||||
|
get() = PluginManager.resolvedPlugins.firstOrNull { it == this }?.description ?: error("Plugin is unloaded")
|
||||||
|
|
||||||
|
inline fun PluginLoader<*, *>.register() = PluginManager.registerPluginLoader(this)
|
||||||
|
inline fun PluginLoader<*, *>.unregister() = PluginManager.unregisterPluginLoader(this)
|
||||||
|
|
||||||
object PluginManager {
|
object PluginManager {
|
||||||
private val _loaders: MutableSet<PluginLoader<*>> = mutableSetOf()
|
val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() }
|
||||||
|
|
||||||
val loaders: Set<PluginLoader<*>> get() = _loaders
|
private val _pluginLoaders: MutableList<PluginLoader<*, *>> = mutableListOf()
|
||||||
|
private val loadersLock: ReentrantLock = ReentrantLock()
|
||||||
|
|
||||||
fun registerPluginLoader(loader: PluginLoader<*>) {
|
@JvmField
|
||||||
_loaders.add(loader)
|
internal val resolvedPlugins: MutableList<Plugin> = mutableListOf()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已加载的插件列表
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
val plugins: List<Plugin>
|
||||||
|
get() = resolvedPlugins.toList()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内建的插件加载器列表. 由 [MiraiConsole] 初始化
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
val builtInLoaders: List<PluginLoader<*, *>>
|
||||||
|
get() = MiraiConsole.builtInPluginLoaders
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 由插件创建的 [PluginLoader]
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
val pluginLoaders: List<PluginLoader<*, *>>
|
||||||
|
get() = _pluginLoaders.toList()
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun registerPluginLoader(loader: PluginLoader<*, *>): Boolean = loadersLock.withLock {
|
||||||
|
if (_pluginLoaders.any { it::class == loader }) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_pluginLoaders.add(loader)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unregisterPluginLoader(loader: PluginLoader<*>) {
|
@JvmStatic
|
||||||
_loaders.remove(loader)
|
fun unregisterPluginLoader(loader: PluginLoader<*, *>) = loadersLock.withLock {
|
||||||
|
_pluginLoaders.remove(loader)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadPlugins() {
|
|
||||||
loaders.forEach(PluginLoader<*>::loadAll)
|
// region LOADING
|
||||||
|
|
||||||
|
private fun <P : Plugin, D : PluginDescription> PluginLoader<P, D>.loadPluginNoEnable(description: D): P {
|
||||||
|
// TODO: 2020/5/23 HANDLE INITIALIZATION EXCEPTION
|
||||||
|
return this.load(description).also { resolvedPlugins.add(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun enablePlugins() {
|
private fun <P : Plugin, D : PluginDescription> PluginLoader<P, D>.loadPluginAndEnable(description: D) {
|
||||||
loaders.forEach(PluginLoader<*>::enableAll)
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return this.enable(loadPluginNoEnable(description.unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STEPS:
|
||||||
|
* 1. 遍历插件列表, 使用 [builtInLoaders] 加载 [PluginKind.LOADER] 类型的插件
|
||||||
|
* 2. [启动][PluginLoader.enable] 所有 [PluginKind.LOADER] 的插件
|
||||||
|
* 3. 使用内建和所有插件提供的 [PluginLoader] 加载全部除 [PluginKind.LOADER] 外的插件列表.
|
||||||
|
* 4. 解决依赖并排序
|
||||||
|
* 5. 依次 [PluginLoader.load]
|
||||||
|
* 但不 [PluginLoader.enable]
|
||||||
|
*
|
||||||
|
* @return [builtInLoaders] 可以加载的插件. 已经完成了 [PluginLoader.load], 但没有 [PluginLoader.enable]
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@Throws(PluginMissingDependencyException::class)
|
||||||
|
internal fun loadEnablePlugins() {
|
||||||
|
val all = loadAndEnableLoaderProviders() + _pluginLoaders.listAllPlugins().flatMap { it.second }
|
||||||
|
|
||||||
|
for ((loader, desc) in all.sortByDependencies()) {
|
||||||
|
loader.loadPluginAndEnable(desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return [builtInLoaders] 可以加载的插件. 已经完成了 [PluginLoader.load], 但没有 [PluginLoader.enable]
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@Throws(PluginMissingDependencyException::class)
|
||||||
|
private fun loadAndEnableLoaderProviders(): List<PluginDescriptionWithLoader> {
|
||||||
|
val allDescriptions =
|
||||||
|
this.builtInLoaders.listAllPlugins()
|
||||||
|
.asSequence()
|
||||||
|
.onEach { (loader, descriptions) ->
|
||||||
|
loader as PluginLoader<Plugin, PluginDescription>
|
||||||
|
|
||||||
|
for (it in descriptions.filter { it.kind == PluginKind.LOADER }.sortByDependencies()) {
|
||||||
|
loader.loadPluginAndEnable(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.flatMap { it.second.asSequence() }
|
||||||
|
|
||||||
|
return allDescriptions.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<PluginLoader<*, *>>.listAllPlugins(): List<Pair<PluginLoader<*, *>, List<PluginDescriptionWithLoader>>> {
|
||||||
|
return associateWith { loader -> loader.listPlugins().map { desc -> desc.wrapWith(loader) } }.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(PluginMissingDependencyException::class)
|
||||||
|
private fun <D : PluginDescription> List<D>.sortByDependencies(): List<D> {
|
||||||
|
val resolved = ArrayList<D>(this.size)
|
||||||
|
|
||||||
|
fun D.canBeLoad(): Boolean = this.dependencies.all { it.isOptional || it in resolved }
|
||||||
|
|
||||||
|
fun List<D>.consumeLoadable(): List<D> {
|
||||||
|
val (canBeLoad, cannotBeLoad) = this.partition { it.canBeLoad() }
|
||||||
|
resolved.addAll(canBeLoad)
|
||||||
|
return cannotBeLoad
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<PluginDependency>.filterIsMissing(): List<PluginDependency> =
|
||||||
|
this.filterNot { it.isOptional || it in resolved }
|
||||||
|
|
||||||
|
tailrec fun List<D>.doSort() {
|
||||||
|
if (this.isEmpty()) return
|
||||||
|
|
||||||
|
val beforeSize = this.size
|
||||||
|
this.consumeLoadable().also { resultPlugins ->
|
||||||
|
check(resultPlugins.size < beforeSize) {
|
||||||
|
throw PluginMissingDependencyException(resultPlugins.joinToString("\n") { badPlugin ->
|
||||||
|
"Cannot load plugin ${badPlugin.name}, missing dependencies: ${badPlugin.dependencies.filterIsMissing()
|
||||||
|
.joinToString()}"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}.doSort()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.doSort()
|
||||||
|
return resolved
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PluginMissingDependencyException : PluginResolutionException {
|
||||||
|
constructor() : super()
|
||||||
|
constructor(message: String?) : super(message)
|
||||||
|
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||||
|
constructor(cause: Throwable?) : super(cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
open class PluginResolutionException : Exception {
|
||||||
|
constructor() : super()
|
||||||
|
constructor(message: String?) : super(message)
|
||||||
|
constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||||
|
constructor(cause: Throwable?) : super(cause)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal data class PluginDescriptionWithLoader(
|
||||||
|
@JvmField val loader: PluginLoader<*, PluginDescription>, // easier type
|
||||||
|
@JvmField val delegate: PluginDescription
|
||||||
|
) : PluginDescription by delegate
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
internal fun <D : PluginDescription> PluginDescription.unwrap(): D =
|
||||||
|
if (this is PluginDescriptionWithLoader) this.delegate as D else this as D
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
internal fun PluginDescription.wrapWith(loader: PluginLoader<*, *>): PluginDescriptionWithLoader =
|
||||||
|
PluginDescriptionWithLoader(
|
||||||
|
loader as PluginLoader<*, PluginDescription>, this
|
||||||
|
)
|
||||||
|
|
||||||
|
internal operator fun List<PluginDescription>.contains(dependency: PluginDependency): Boolean =
|
||||||
|
any { it.name == dependency.name }
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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", "unused", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
|
||||||
|
|
||||||
package net.mamoe.mirai.console.plugins
|
|
||||||
|
|
||||||
val PluginBase.description: PluginDescription get() = TODO()
|
|
||||||
|
|
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.plugins.builtin
|
||||||
|
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import net.mamoe.mirai.console.MiraiConsole
|
||||||
|
import net.mamoe.mirai.console.plugins.AbstractFilePluginLoader
|
||||||
|
import net.mamoe.mirai.console.plugins.PluginLoadException
|
||||||
|
import net.mamoe.mirai.console.plugins.PluginsLoader
|
||||||
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
|
import net.mamoe.mirai.utils.error
|
||||||
|
import net.mamoe.yamlkt.Yaml
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URL
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
import kotlin.reflect.full.createInstance
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内建的 Jar (JVM) 插件加载器
|
||||||
|
*/
|
||||||
|
object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>("jar"), CoroutineScope {
|
||||||
|
private val logger: MiraiLogger by lazy {
|
||||||
|
MiraiConsole.newLogger(JarPluginLoader::class.simpleName!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val coroutineContext: CoroutineContext by lazy {
|
||||||
|
MiraiConsole.coroutineContext + SupervisorJob(
|
||||||
|
MiraiConsole.coroutineContext[Job]
|
||||||
|
) + CoroutineExceptionHandler { _, throwable ->
|
||||||
|
logger.error("Unhandled Jar plugin exception: ${throwable.message}", throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private val supervisor: Job = coroutineContext[Job]!!
|
||||||
|
|
||||||
|
private val classLoader: PluginsLoader = PluginsLoader(this.javaClass.classLoader)
|
||||||
|
|
||||||
|
init {
|
||||||
|
supervisor.invokeOnCompletion {
|
||||||
|
classLoader.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description
|
||||||
|
|
||||||
|
override fun Sequence<File>.mapToDescription(): List<JvmPluginDescription> {
|
||||||
|
return this.associateWith { URL("jar:${it.absolutePath}!/plugin.yml") }.mapNotNull { (file, url) ->
|
||||||
|
kotlin.runCatching {
|
||||||
|
url.readText()
|
||||||
|
}.fold(
|
||||||
|
onSuccess = { yaml ->
|
||||||
|
Yaml.nonStrict.parse(JvmPluginDescription.serializer(), yaml)
|
||||||
|
},
|
||||||
|
onFailure = {
|
||||||
|
logger.error("Cannot load plugin file ${file.name}", it)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
)?.also { it._file = file }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("RemoveExplicitTypeArguments") // until Kotlin 1.4 NI
|
||||||
|
@Throws(PluginLoadException::class)
|
||||||
|
override fun load(description: JvmPluginDescription): JvmPlugin =
|
||||||
|
description.runCatching<JvmPluginDescription, JvmPlugin> {
|
||||||
|
ensureActive()
|
||||||
|
val main = classLoader.loadPluginMainClassByJarFile(name, mainClassName, file).kotlin.run {
|
||||||
|
objectInstance
|
||||||
|
?: kotlin.runCatching { createInstance() }.getOrNull()
|
||||||
|
?: (java.constructors + java.declaredConstructors)
|
||||||
|
.firstOrNull { it.parameterCount == 0 }
|
||||||
|
?.apply { kotlin.runCatching { isAccessible = true } }
|
||||||
|
?.newInstance()
|
||||||
|
} ?: error("No Kotlin object or public no-arg constructor found")
|
||||||
|
|
||||||
|
check(main is JvmPlugin) { "The main class of Jar plugin must extend JvmPlugin, recommending JavaPlugin or KotlinPlugin" }
|
||||||
|
|
||||||
|
if (main is JvmPluginImpl) {
|
||||||
|
main._description = description
|
||||||
|
main.internalOnLoad()
|
||||||
|
} else main.onLoad()
|
||||||
|
main
|
||||||
|
}.getOrElse<JvmPlugin, JvmPlugin> {
|
||||||
|
throw PluginLoadException("Exception while loading ${description.name}", it)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun enable(plugin: JvmPlugin) {
|
||||||
|
ensureActive()
|
||||||
|
if (plugin is JvmPluginImpl) {
|
||||||
|
plugin.internalOnEnable()
|
||||||
|
} else plugin.onEnable()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun disable(plugin: JvmPlugin) {
|
||||||
|
if (plugin is JvmPluginImpl) {
|
||||||
|
plugin.internalOnDisable()
|
||||||
|
} else plugin.onDisable()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* 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("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS")
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.plugins.builtin
|
||||||
|
|
||||||
|
import kotlinx.atomicfu.locks.withLock
|
||||||
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import net.mamoe.mirai.console.MiraiConsole
|
||||||
|
import net.mamoe.mirai.console.plugins.Plugin
|
||||||
|
import net.mamoe.mirai.console.plugins.PluginLoader
|
||||||
|
import net.mamoe.mirai.console.utils.JavaPluginScheduler
|
||||||
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 或 Kotlin Jar 插件
|
||||||
|
*
|
||||||
|
* @see JavaPlugin Java 插件
|
||||||
|
* @see KotlinPlugin Kotlin 插件
|
||||||
|
*/
|
||||||
|
interface JvmPlugin : Plugin, CoroutineScope {
|
||||||
|
/** 日志 */
|
||||||
|
val logger: MiraiLogger
|
||||||
|
|
||||||
|
/** 插件描述 */
|
||||||
|
val description: JvmPluginDescription
|
||||||
|
|
||||||
|
/** 所属插件加载器实例 */
|
||||||
|
override val loader: PluginLoader<*, *> get() = JarPluginLoader
|
||||||
|
|
||||||
|
@JvmDefault
|
||||||
|
fun onLoad() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmDefault
|
||||||
|
fun onEnable() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmDefault
|
||||||
|
fun onDisable() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java 插件的父类
|
||||||
|
*/
|
||||||
|
abstract class JavaPlugin @JvmOverloads constructor(
|
||||||
|
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||||
|
) : JvmPlugin, JvmPluginImpl(parentCoroutineContext) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java API Scheduler
|
||||||
|
*/
|
||||||
|
val scheduler: JavaPluginScheduler =
|
||||||
|
JavaPluginScheduler(this.coroutineContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class KotlinPlugin @JvmOverloads constructor(
|
||||||
|
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||||
|
) : JvmPlugin, JvmPluginImpl(parentCoroutineContext) {
|
||||||
|
// that's it
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class JvmPluginImpl(
|
||||||
|
parentCoroutineContext: CoroutineContext
|
||||||
|
) : JvmPlugin, CoroutineScope {
|
||||||
|
/**
|
||||||
|
* Initialized immediately after construction of [JvmPluginImpl] instance
|
||||||
|
*/
|
||||||
|
@Suppress("PropertyName")
|
||||||
|
internal lateinit var _description: JvmPluginDescription
|
||||||
|
|
||||||
|
// for future use
|
||||||
|
@Suppress("PropertyName")
|
||||||
|
@JvmField
|
||||||
|
internal var _intrinsicCoroutineContext: CoroutineContext = EmptyCoroutineContext
|
||||||
|
|
||||||
|
override val description: JvmPluginDescription get() = _description
|
||||||
|
|
||||||
|
final override val logger: MiraiLogger by lazy { MiraiConsole.newLogger(this._description.name) }
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
internal val coroutineContextInitializer = {
|
||||||
|
CoroutineExceptionHandler { _, throwable -> logger.error(throwable) }
|
||||||
|
.plus(parentCoroutineContext)
|
||||||
|
.plus(SupervisorJob(parentCoroutineContext[Job])) + _intrinsicCoroutineContext
|
||||||
|
}
|
||||||
|
|
||||||
|
private var firstRun = true
|
||||||
|
|
||||||
|
internal fun internalOnDisable() {
|
||||||
|
firstRun = false
|
||||||
|
this.onDisable()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun internalOnLoad() {
|
||||||
|
this.onLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun internalOnEnable() {
|
||||||
|
if (!firstRun) refreshCoroutineContext()
|
||||||
|
this.onEnable()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshCoroutineContext(): CoroutineContext {
|
||||||
|
return coroutineContextInitializer().also { _coroutineContext = it }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val contextUpdateLock: ReentrantLock = ReentrantLock()
|
||||||
|
private var _coroutineContext: CoroutineContext? = null
|
||||||
|
final override val coroutineContext: CoroutineContext
|
||||||
|
get() = _coroutineContext
|
||||||
|
?: contextUpdateLock.withLock { _coroutineContext ?: refreshCoroutineContext() }
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.plugins.builtin
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.Transient
|
||||||
|
import net.mamoe.mirai.console.plugins.FilePluginDescription
|
||||||
|
import net.mamoe.mirai.console.plugins.PluginDependency
|
||||||
|
import net.mamoe.mirai.console.plugins.PluginDescription
|
||||||
|
import net.mamoe.mirai.console.plugins.PluginKind
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class JvmPluginDescription internal constructor(
|
||||||
|
override val kind: PluginKind = PluginKind.NORMAL,
|
||||||
|
override val name: String,
|
||||||
|
@SerialName("main")
|
||||||
|
val mainClassName: String,
|
||||||
|
override val author: String = "",
|
||||||
|
override val version: String,
|
||||||
|
override val info: String = "",
|
||||||
|
@SerialName("depends")
|
||||||
|
override val dependencies: List<@Serializable(with = PluginDependency.SmartSerializer::class) PluginDependency> = listOf()
|
||||||
|
) : PluginDescription, FilePluginDescription {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在手动实现时使用这个构造器.
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
constructor(
|
||||||
|
kind: PluginKind, name: String, mainClassName: String, author: String,
|
||||||
|
version: String, info: String, depends: List<PluginDependency>,
|
||||||
|
file: File
|
||||||
|
) : this(kind, name, mainClassName, author, version, info, depends) {
|
||||||
|
this._file = file
|
||||||
|
}
|
||||||
|
|
||||||
|
override val file: File
|
||||||
|
get() = _file ?: error("Internal error: JvmPluginDescription(name=$name)._file == null")
|
||||||
|
|
||||||
|
|
||||||
|
@Suppress("PropertyName")
|
||||||
|
@Transient
|
||||||
|
@JvmField
|
||||||
|
internal var _file: File? = null
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "JvmPluginDescription(kind=$kind, name='$name', mainClassName='$mainClassName', author='$author', version='$version', info='$info', dependencies=$dependencies, _file=$_file)"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.plugins
|
||||||
|
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
import net.mamoe.mirai.console.setting.internal.map
|
||||||
|
import net.mamoe.yamlkt.Yaml
|
||||||
|
import net.mamoe.yamlkt.YamlDynamicSerializer
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
|
/** 插件类型 */
|
||||||
|
@Serializable(with = PluginKind.AsStringSerializer::class)
|
||||||
|
enum class PluginKind {
|
||||||
|
/** 表示此插件提供一个 [PluginLoader], 应在加载其他 [NORMAL] 类型插件前加载 */
|
||||||
|
LOADER,
|
||||||
|
|
||||||
|
/** 表示此插件为一个通常的插件, 按照正常的依赖关系加载. */
|
||||||
|
NORMAL;
|
||||||
|
|
||||||
|
object AsStringSerializer : KSerializer<PluginKind> by String.serializer().map(
|
||||||
|
serializer = { it.name },
|
||||||
|
deserializer = { str ->
|
||||||
|
values().firstOrNull {
|
||||||
|
it.name.equals(str, ignoreCase = true)
|
||||||
|
} ?: NORMAL
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插件描述
|
||||||
|
*/
|
||||||
|
interface PluginDescription {
|
||||||
|
val kind: PluginKind
|
||||||
|
|
||||||
|
val name: String
|
||||||
|
val author: String
|
||||||
|
val version: String
|
||||||
|
val info: String
|
||||||
|
|
||||||
|
/** 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件 */
|
||||||
|
val dependencies: List<@Serializable(with = PluginDependency.SmartSerializer::class) PluginDependency>
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 插件的一个依赖的信息 */
|
||||||
|
@Serializable
|
||||||
|
data class PluginDependency(
|
||||||
|
/** 依赖插件名 */
|
||||||
|
val name: String,
|
||||||
|
/**
|
||||||
|
* 依赖版本号. 为 null 时则为不限制版本.
|
||||||
|
* @see versionKind 版本号类型
|
||||||
|
*/
|
||||||
|
val version: String? = null,
|
||||||
|
/** 版本号类型 */
|
||||||
|
val versionKind: VersionKind = VersionKind.AT_LEAST,
|
||||||
|
/**
|
||||||
|
* 若为 `false`, 插件在找不到此依赖时也能正常加载.
|
||||||
|
*/
|
||||||
|
val isOptional: Boolean = false
|
||||||
|
) {
|
||||||
|
/** 版本号类型 */
|
||||||
|
@Serializable(with = VersionKind.AsStringSerializer::class)
|
||||||
|
enum class VersionKind(
|
||||||
|
private vararg val serialNames: String
|
||||||
|
) {
|
||||||
|
/** 要求依赖精确的版本 */
|
||||||
|
EXACT("exact"),
|
||||||
|
|
||||||
|
/** 要求依赖最低版本 */
|
||||||
|
AT_LEAST("at_least", "AtLeast", "least", "lowest", "+"),
|
||||||
|
|
||||||
|
/** 要求依赖最高版本 */
|
||||||
|
AT_MOST("at_most", "AtMost", "most", "highest", "-");
|
||||||
|
|
||||||
|
object AsStringSerializer : KSerializer<VersionKind> by String.serializer().map(
|
||||||
|
serializer = { it.serialNames.first() },
|
||||||
|
deserializer = { str ->
|
||||||
|
values().firstOrNull {
|
||||||
|
it.serialNames.any { name -> name.equals(str, ignoreCase = true) }
|
||||||
|
} ?: AT_LEAST
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "$name ${versionKind.toEnglishString()}v$version"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可支持解析 [String] 作为 [PluginDependency.version] 或单个 [PluginDependency]
|
||||||
|
*/
|
||||||
|
object SmartSerializer : KSerializer<PluginDependency> by YamlDynamicSerializer.map(
|
||||||
|
serializer = { it },
|
||||||
|
deserializer = { any ->
|
||||||
|
when (any) {
|
||||||
|
is Map<*, *> -> Yaml.nonStrict.parse(serializer(), Yaml.nonStrict.stringify(any))
|
||||||
|
else -> PluginDependency(any.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于文件的插件的描述
|
||||||
|
*/
|
||||||
|
interface FilePluginDescription : PluginDescription {
|
||||||
|
val file: File
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun PluginDependency.VersionKind.toEnglishString(): String = when (this) {
|
||||||
|
PluginDependency.VersionKind.EXACT -> ""
|
||||||
|
PluginDependency.VersionKind.AT_LEAST -> "at least "
|
||||||
|
PluginDependency.VersionKind.AT_MOST -> "at most "
|
||||||
|
}
|
@ -1,149 +0,0 @@
|
|||||||
package net.mamoe.mirai.console.scheduler
|
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
import kotlinx.coroutines.sync.withLock
|
|
||||||
import net.mamoe.mirai.console.plugins.PluginBase
|
|
||||||
import java.util.concurrent.Future
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import java.util.concurrent.TimeoutException
|
|
||||||
import java.util.function.Supplier
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 作为Java插件开发者, 你应该使用PluginScheduler
|
|
||||||
* 他们使用kotlin更高效的协程实现,并在API上对java有很高的亲和度
|
|
||||||
* 且可以保证在PluginBase关闭的时候结束所有任务
|
|
||||||
*
|
|
||||||
* 你应该使用SchedulerTaskManager获取PluginScheduler, 或直接通过PluginBase获取
|
|
||||||
*/
|
|
||||||
|
|
||||||
class PluginScheduler(_coroutineContext: CoroutineContext) : CoroutineScope {
|
|
||||||
override val coroutineContext: CoroutineContext = _coroutineContext + SupervisorJob(_coroutineContext[Job])
|
|
||||||
|
|
||||||
|
|
||||||
class RepeatTaskReceipt(@Volatile var cancelled: Boolean = false)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增一个 Repeat Task (定时任务)
|
|
||||||
*
|
|
||||||
* 这个 Runnable 会被每 [intervalMs] 调用一次(不包含 [runnable] 执行时间)
|
|
||||||
*
|
|
||||||
* 使用返回的 [RepeatTaskReceipt], 可以取消这个定时任务
|
|
||||||
*/
|
|
||||||
fun repeat(runnable: Runnable, intervalMs: Long): RepeatTaskReceipt {
|
|
||||||
val receipt = RepeatTaskReceipt()
|
|
||||||
|
|
||||||
this.launch {
|
|
||||||
while (isActive && (!receipt.cancelled)) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
runnable.run()
|
|
||||||
}
|
|
||||||
delay(intervalMs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return receipt
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增一个 Delay Task (延迟任务)
|
|
||||||
*
|
|
||||||
* 在延迟 [delayMs] 后执行 [runnable]
|
|
||||||
*
|
|
||||||
* 作为 Java 使用者, 你要注意可见性, 原子性
|
|
||||||
*/
|
|
||||||
fun delay(runnable: Runnable, delayMs: Long) {
|
|
||||||
this.launch {
|
|
||||||
delay(delayMs)
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
runnable.run()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 异步执行一个任务, 最终返回 [Future], 与 Java 使用方法无异, 但效率更高且可以在插件关闭时停止
|
|
||||||
*/
|
|
||||||
fun <T> async(supplier: Supplier<T>): Future<T> {
|
|
||||||
return AsyncResult(
|
|
||||||
this.async {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
supplier.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 异步执行一个任务, 没有返回
|
|
||||||
*/
|
|
||||||
fun async(runnable: Runnable) {
|
|
||||||
this.launch {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
runnable.run()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 这个类作为 Java 与 Kotlin 的桥接
|
|
||||||
* 用 Java 的 interface 进行了 Kotlin 的实现
|
|
||||||
* 使得 Java 开发者可以使用 Kotlin 的协程 [CoroutineScope.async]
|
|
||||||
* 具体使用方法与 Java 的 [Future] 没有区别
|
|
||||||
*/
|
|
||||||
class AsyncResult<T>(
|
|
||||||
private val deferred: Deferred<T>
|
|
||||||
) : Future<T> {
|
|
||||||
|
|
||||||
override fun isDone(): Boolean {
|
|
||||||
return deferred.isCompleted
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(): T {
|
|
||||||
return runBlocking {
|
|
||||||
deferred.await()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
override fun get(p0: Long, p1: TimeUnit): T {
|
|
||||||
return runBlocking {
|
|
||||||
withTimeoutOrNull(p1.toMillis(p0)) {
|
|
||||||
deferred.await()
|
|
||||||
} ?: throw TimeoutException()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun cancel(p0: Boolean): Boolean {
|
|
||||||
deferred.cancel()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isCancelled(): Boolean {
|
|
||||||
return deferred.isCancelled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal object SchedulerTaskManagerInstance {
|
|
||||||
private val schedulerTaskManagerInstance = mutableMapOf<PluginBase, PluginScheduler>()
|
|
||||||
|
|
||||||
private val mutex = Mutex()
|
|
||||||
|
|
||||||
fun getPluginScheduler(pluginBase: PluginBase): PluginScheduler {
|
|
||||||
runBlocking {
|
|
||||||
mutex.withLock {
|
|
||||||
if (!schedulerTaskManagerInstance.containsKey(pluginBase)) {
|
|
||||||
schedulerTaskManagerInstance[pluginBase] = PluginScheduler(pluginBase.coroutineContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return schedulerTaskManagerInstance[pluginBase]!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -12,23 +12,40 @@
|
|||||||
package net.mamoe.mirai.console.setting
|
package net.mamoe.mirai.console.setting
|
||||||
|
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
|
import net.mamoe.mirai.console.setting.internal.SettingImpl
|
||||||
|
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||||
import kotlin.properties.ReadWriteProperty
|
import kotlin.properties.ReadWriteProperty
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
import kotlin.reflect.full.findAnnotation
|
import kotlin.reflect.full.findAnnotation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在配置文件和图像界面中保存的名称.
|
||||||
|
*/
|
||||||
typealias SerialName = kotlinx.serialization.SerialName
|
typealias SerialName = kotlinx.serialization.SerialName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在配置文件和图像界面中显示的说明.
|
||||||
|
*/
|
||||||
|
typealias Comment = net.mamoe.yamlkt.Comment
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配置的基类. 所有配置必须拥有一个无参构造器, 以用于在 [MutableList] 与 [MutableMap] 中动态识别类型
|
* 配置的基类. 所有配置必须拥有一个无参构造器, 以用于在 [MutableList] 与 [MutableMap] 中动态识别类型
|
||||||
*/
|
*/
|
||||||
@Suppress("EXPOSED_SUPER_CLASS")
|
@Suppress("EXPOSED_SUPER_CLASS")
|
||||||
abstract class Setting : AbstractSetting() {
|
abstract class Setting : SettingImpl() {
|
||||||
|
/**
|
||||||
|
* 这个配置的名称, 仅对于顶层配置有效.
|
||||||
|
*/
|
||||||
|
@MiraiExperimentalAPI
|
||||||
open val serialName: String
|
open val serialName: String
|
||||||
get() = this::class.findAnnotation<SerialName>()?.value
|
get() = this::class.findAnnotation<SerialName>()?.value
|
||||||
?: this::class.qualifiedName
|
?: this::class.qualifiedName
|
||||||
?: error("Names should be assigned to anonymous classes manually by overriding serialName")
|
?: error("Names should be assigned to anonymous classes manually by overriding serialName")
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供属性委托, 并添加这个对象的自动保存跟踪.
|
||||||
|
*/
|
||||||
@JvmSynthetic
|
@JvmSynthetic
|
||||||
operator fun <T : Any> Value<T>.provideDelegate(
|
operator fun <T : Any> Value<T>.provideDelegate(
|
||||||
thisRef: Setting,
|
thisRef: Setting,
|
||||||
@ -39,9 +56,12 @@ abstract class Setting : AbstractSetting() {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String = yaml.stringify(this.serializer, this)
|
override fun toString(): String = yamlForToString.stringify(this.serializer, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于更新或保存这个 [Value] 的序列化器.
|
||||||
|
*/
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val <T : Setting> T.serializer: KSerializer<T>
|
val <T : Setting> T.serializer: KSerializer<T>
|
||||||
get() = kotlinSerializer as KSerializer<T>
|
get() = kotlinSerializer as KSerializer<T>
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.console.setting
|
|
||||||
|
|
||||||
object ValueSerializerMark
|
|
||||||
|
|
||||||
/*
|
|
||||||
* More generic ones
|
|
||||||
*/
|
|
@ -7,9 +7,14 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "unused")
|
||||||
|
|
||||||
package net.mamoe.mirai.console.setting
|
package net.mamoe.mirai.console.setting
|
||||||
|
|
||||||
|
import net.mamoe.mirai.console.setting.internal.valueImpl
|
||||||
|
import kotlin.internal.LowPriorityInOverloadResolution
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.SettingValueUseSiteCodegen.kt
|
* !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.SettingValueUseSiteCodegen.kt
|
||||||
* !!! DO NOT MODIFY THIS FILE MANUALLY
|
* !!! DO NOT MODIFY THIS FILE MANUALLY
|
||||||
@ -136,3 +141,23 @@ inline fun <reified T : Setting> Setting.value(default: Set<T>): SettingSetValue
|
|||||||
@JvmName("valueMutable")
|
@JvmName("valueMutable")
|
||||||
inline fun <reified T : Setting> Setting.value(default: MutableSet<T>): MutableSettingSetValue<T> = valueImpl(default)
|
inline fun <reified T : Setting> Setting.value(default: MutableSet<T>): MutableSettingSetValue<T> = valueImpl(default)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个只引用对象而不跟踪其属性的值.
|
||||||
|
*
|
||||||
|
* @param T 类型. 必须拥有 [kotlinx.serialization.Serializable] 注解 (因此编译器会自动生成序列化器)
|
||||||
|
*/
|
||||||
|
@DangerousReferenceOnlyValue
|
||||||
|
@JvmName("valueDynamic")
|
||||||
|
@LowPriorityInOverloadResolution
|
||||||
|
inline fun <reified T : Any> Setting.value(default: T): Value<T> = valueImpl(default)
|
||||||
|
|
||||||
|
@RequiresOptIn(
|
||||||
|
"""
|
||||||
|
这种只保存引用的 Value 可能会导致意料之外的结果, 在使用时须保持谨慎.
|
||||||
|
对值的改变不会触发自动保存, 也不会同步到 UI 中. 在 UI 中只能编辑序列化之后的值.
|
||||||
|
""", level = RequiresOptIn.Level.WARNING
|
||||||
|
)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
|
annotation class DangerousReferenceOnlyValue
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import kotlin.properties.ReadWriteProperty
|
|||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* !!! These primitive types are auto-generated by backend/codegen/src/main/kotlin/net.mamoe.mirai.console.codegen.ValuesCodegen.kt
|
* !!! This file is auto-generated by backend/codegen/src/main/kotlin/net.mamoe.mirai.console.codegen.ValuesCodegen.kt
|
||||||
* !!! for better performance
|
* !!! for better performance
|
||||||
* !!! DO NOT MODIFY THIS FILE MANUALLY
|
* !!! DO NOT MODIFY THIS FILE MANUALLY
|
||||||
*/
|
*/
|
||||||
@ -62,28 +62,36 @@ sealed class PrimitiveArrayValue<T : Any> : ArrayValue<T>()
|
|||||||
abstract class IntArrayValue internal constructor() : PrimitiveArrayValue<IntArray>(), Iterable<Int> {
|
abstract class IntArrayValue internal constructor() : PrimitiveArrayValue<IntArray>(), Iterable<Int> {
|
||||||
override fun iterator(): Iterator<Int> = this.value.iterator()
|
override fun iterator(): Iterator<Int> = this.value.iterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ShortArrayValue internal constructor() : PrimitiveArrayValue<ShortArray>(), Iterable<Short> {
|
abstract class ShortArrayValue internal constructor() : PrimitiveArrayValue<ShortArray>(), Iterable<Short> {
|
||||||
override fun iterator(): Iterator<Short> = this.value.iterator()
|
override fun iterator(): Iterator<Short> = this.value.iterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ByteArrayValue internal constructor() : PrimitiveArrayValue<ByteArray>(), Iterable<Byte> {
|
abstract class ByteArrayValue internal constructor() : PrimitiveArrayValue<ByteArray>(), Iterable<Byte> {
|
||||||
override fun iterator(): Iterator<Byte> = this.value.iterator()
|
override fun iterator(): Iterator<Byte> = this.value.iterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class LongArrayValue internal constructor() : PrimitiveArrayValue<LongArray>(), Iterable<Long> {
|
abstract class LongArrayValue internal constructor() : PrimitiveArrayValue<LongArray>(), Iterable<Long> {
|
||||||
override fun iterator(): Iterator<Long> = this.value.iterator()
|
override fun iterator(): Iterator<Long> = this.value.iterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class FloatArrayValue internal constructor() : PrimitiveArrayValue<FloatArray>(), Iterable<Float> {
|
abstract class FloatArrayValue internal constructor() : PrimitiveArrayValue<FloatArray>(), Iterable<Float> {
|
||||||
override fun iterator(): Iterator<Float> = this.value.iterator()
|
override fun iterator(): Iterator<Float> = this.value.iterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class DoubleArrayValue internal constructor() : PrimitiveArrayValue<DoubleArray>(), Iterable<Double> {
|
abstract class DoubleArrayValue internal constructor() : PrimitiveArrayValue<DoubleArray>(), Iterable<Double> {
|
||||||
override fun iterator(): Iterator<Double> = this.value.iterator()
|
override fun iterator(): Iterator<Double> = this.value.iterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class BooleanArrayValue internal constructor() : PrimitiveArrayValue<BooleanArray>(), Iterable<Boolean> {
|
abstract class BooleanArrayValue internal constructor() : PrimitiveArrayValue<BooleanArray>(), Iterable<Boolean> {
|
||||||
override fun iterator(): Iterator<Boolean> = this.value.iterator()
|
override fun iterator(): Iterator<Boolean> = this.value.iterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class CharArrayValue internal constructor() : PrimitiveArrayValue<CharArray>(), Iterable<Char> {
|
abstract class CharArrayValue internal constructor() : PrimitiveArrayValue<CharArray>(), Iterable<Char> {
|
||||||
override fun iterator(): Iterator<Char> = this.value.iterator()
|
override fun iterator(): Iterator<Char> = this.value.iterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sealed class TypedPrimitiveArrayValue<E> : ArrayValue<Array<E>>(), Iterable<E> {
|
sealed class TypedPrimitiveArrayValue<E> : ArrayValue<Array<E>>(), Iterable<E> {
|
||||||
override fun iterator() = this.value.iterator()
|
override fun iterator() = this.value.iterator()
|
||||||
}
|
}
|
||||||
@ -98,10 +106,7 @@ abstract class TypedBooleanArrayValue internal constructor() : TypedPrimitiveArr
|
|||||||
abstract class TypedCharArrayValue internal constructor() : TypedPrimitiveArrayValue<Char>()
|
abstract class TypedCharArrayValue internal constructor() : TypedPrimitiveArrayValue<Char>()
|
||||||
abstract class TypedStringArrayValue internal constructor() : TypedPrimitiveArrayValue<String>()
|
abstract class TypedStringArrayValue internal constructor() : TypedPrimitiveArrayValue<String>()
|
||||||
|
|
||||||
sealed class ListValue<E> : Value<List<E>>(), Iterable<E> {
|
sealed class ListValue<E> : Value<List<E>>(), List<E>
|
||||||
override fun iterator() = this.value.iterator()
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class IntListValue internal constructor() : ListValue<Int>()
|
abstract class IntListValue internal constructor() : ListValue<Int>()
|
||||||
abstract class ShortListValue internal constructor() : ListValue<Short>()
|
abstract class ShortListValue internal constructor() : ListValue<Short>()
|
||||||
abstract class ByteListValue internal constructor() : ListValue<Byte>()
|
abstract class ByteListValue internal constructor() : ListValue<Byte>()
|
||||||
@ -114,10 +119,7 @@ abstract class StringListValue internal constructor() : ListValue<String>()
|
|||||||
|
|
||||||
abstract class SettingListValue<T : Setting> internal constructor() : Value<List<T>>(), List<T>
|
abstract class SettingListValue<T : Setting> internal constructor() : Value<List<T>>(), List<T>
|
||||||
|
|
||||||
sealed class SetValue<E> : Value<Set<E>>(), Iterable<E> {
|
sealed class SetValue<E> : Value<Set<E>>(), Set<E>
|
||||||
override fun iterator() = this.value.iterator()
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class IntSetValue internal constructor() : SetValue<Int>()
|
abstract class IntSetValue internal constructor() : SetValue<Int>()
|
||||||
abstract class ShortSetValue internal constructor() : SetValue<Short>()
|
abstract class ShortSetValue internal constructor() : SetValue<Short>()
|
||||||
abstract class ByteSetValue internal constructor() : SetValue<Byte>()
|
abstract class ByteSetValue internal constructor() : SetValue<Byte>()
|
||||||
|
@ -7,14 +7,11 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.setting.internal
|
||||||
|
|
||||||
package net.mamoe.mirai.console.setting
|
import kotlinx.serialization.*
|
||||||
|
|
||||||
import kotlinx.serialization.Decoder
|
|
||||||
import kotlinx.serialization.Encoder
|
|
||||||
import kotlinx.serialization.KSerializer
|
|
||||||
import kotlinx.serialization.SerialDescriptor
|
|
||||||
import kotlinx.serialization.builtins.*
|
import kotlinx.serialization.builtins.*
|
||||||
|
import net.mamoe.mirai.console.setting.*
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -414,8 +411,9 @@ internal fun Setting.valueImpl(default: Array<String>): TypedStringArrayValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: List<Int>): IntListValue {
|
internal fun Setting.valueImpl(default: List<Int>): IntListValue {
|
||||||
return object : IntListValue() {
|
var internalValue: List<Int> = default
|
||||||
private var internalValue: List<Int> = default
|
val delegt = dynamicList { internalValue }
|
||||||
|
return object : IntListValue(), List<Int> by delegt {
|
||||||
override var value: List<Int>
|
override var value: List<Int>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -429,8 +427,9 @@ internal fun Setting.valueImpl(default: List<Int>): IntListValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: List<Short>): ShortListValue {
|
internal fun Setting.valueImpl(default: List<Short>): ShortListValue {
|
||||||
return object : ShortListValue() {
|
var internalValue: List<Short> = default
|
||||||
private var internalValue: List<Short> = default
|
val delegt = dynamicList { internalValue }
|
||||||
|
return object : ShortListValue(), List<Short> by delegt {
|
||||||
override var value: List<Short>
|
override var value: List<Short>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -444,8 +443,9 @@ internal fun Setting.valueImpl(default: List<Short>): ShortListValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: List<Byte>): ByteListValue {
|
internal fun Setting.valueImpl(default: List<Byte>): ByteListValue {
|
||||||
return object : ByteListValue() {
|
var internalValue: List<Byte> = default
|
||||||
private var internalValue: List<Byte> = default
|
val delegt = dynamicList { internalValue }
|
||||||
|
return object : ByteListValue(), List<Byte> by delegt {
|
||||||
override var value: List<Byte>
|
override var value: List<Byte>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -459,8 +459,9 @@ internal fun Setting.valueImpl(default: List<Byte>): ByteListValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: List<Long>): LongListValue {
|
internal fun Setting.valueImpl(default: List<Long>): LongListValue {
|
||||||
return object : LongListValue() {
|
var internalValue: List<Long> = default
|
||||||
private var internalValue: List<Long> = default
|
val delegt = dynamicList { internalValue }
|
||||||
|
return object : LongListValue(), List<Long> by delegt {
|
||||||
override var value: List<Long>
|
override var value: List<Long>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -474,8 +475,9 @@ internal fun Setting.valueImpl(default: List<Long>): LongListValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: List<Float>): FloatListValue {
|
internal fun Setting.valueImpl(default: List<Float>): FloatListValue {
|
||||||
return object : FloatListValue() {
|
var internalValue: List<Float> = default
|
||||||
private var internalValue: List<Float> = default
|
val delegt = dynamicList { internalValue }
|
||||||
|
return object : FloatListValue(), List<Float> by delegt {
|
||||||
override var value: List<Float>
|
override var value: List<Float>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -489,8 +491,9 @@ internal fun Setting.valueImpl(default: List<Float>): FloatListValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: List<Double>): DoubleListValue {
|
internal fun Setting.valueImpl(default: List<Double>): DoubleListValue {
|
||||||
return object : DoubleListValue() {
|
var internalValue: List<Double> = default
|
||||||
private var internalValue: List<Double> = default
|
val delegt = dynamicList { internalValue }
|
||||||
|
return object : DoubleListValue(), List<Double> by delegt {
|
||||||
override var value: List<Double>
|
override var value: List<Double>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -504,8 +507,9 @@ internal fun Setting.valueImpl(default: List<Double>): DoubleListValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: List<Boolean>): BooleanListValue {
|
internal fun Setting.valueImpl(default: List<Boolean>): BooleanListValue {
|
||||||
return object : BooleanListValue() {
|
var internalValue: List<Boolean> = default
|
||||||
private var internalValue: List<Boolean> = default
|
val delegt = dynamicList { internalValue }
|
||||||
|
return object : BooleanListValue(), List<Boolean> by delegt {
|
||||||
override var value: List<Boolean>
|
override var value: List<Boolean>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -519,8 +523,9 @@ internal fun Setting.valueImpl(default: List<Boolean>): BooleanListValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: List<Char>): CharListValue {
|
internal fun Setting.valueImpl(default: List<Char>): CharListValue {
|
||||||
return object : CharListValue() {
|
var internalValue: List<Char> = default
|
||||||
private var internalValue: List<Char> = default
|
val delegt = dynamicList { internalValue }
|
||||||
|
return object : CharListValue(), List<Char> by delegt {
|
||||||
override var value: List<Char>
|
override var value: List<Char>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -534,8 +539,9 @@ internal fun Setting.valueImpl(default: List<Char>): CharListValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: List<String>): StringListValue {
|
internal fun Setting.valueImpl(default: List<String>): StringListValue {
|
||||||
return object : StringListValue() {
|
var internalValue: List<String> = default
|
||||||
private var internalValue: List<String> = default
|
val delegt = dynamicList { internalValue }
|
||||||
|
return object : StringListValue(), List<String> by delegt {
|
||||||
override var value: List<String>
|
override var value: List<String>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -549,8 +555,9 @@ internal fun Setting.valueImpl(default: List<String>): StringListValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: Set<Int>): IntSetValue {
|
internal fun Setting.valueImpl(default: Set<Int>): IntSetValue {
|
||||||
return object : IntSetValue() {
|
var internalValue: Set<Int> = default
|
||||||
private var internalValue: Set<Int> = default
|
val delegt = dynamicSet { internalValue }
|
||||||
|
return object : IntSetValue(), Set<Int> by delegt {
|
||||||
override var value: Set<Int>
|
override var value: Set<Int>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -564,8 +571,9 @@ internal fun Setting.valueImpl(default: Set<Int>): IntSetValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: Set<Short>): ShortSetValue {
|
internal fun Setting.valueImpl(default: Set<Short>): ShortSetValue {
|
||||||
return object : ShortSetValue() {
|
var internalValue: Set<Short> = default
|
||||||
private var internalValue: Set<Short> = default
|
val delegt = dynamicSet { internalValue }
|
||||||
|
return object : ShortSetValue(), Set<Short> by delegt {
|
||||||
override var value: Set<Short>
|
override var value: Set<Short>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -579,8 +587,9 @@ internal fun Setting.valueImpl(default: Set<Short>): ShortSetValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: Set<Byte>): ByteSetValue {
|
internal fun Setting.valueImpl(default: Set<Byte>): ByteSetValue {
|
||||||
return object : ByteSetValue() {
|
var internalValue: Set<Byte> = default
|
||||||
private var internalValue: Set<Byte> = default
|
val delegt = dynamicSet { internalValue }
|
||||||
|
return object : ByteSetValue(), Set<Byte> by delegt {
|
||||||
override var value: Set<Byte>
|
override var value: Set<Byte>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -594,8 +603,9 @@ internal fun Setting.valueImpl(default: Set<Byte>): ByteSetValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: Set<Long>): LongSetValue {
|
internal fun Setting.valueImpl(default: Set<Long>): LongSetValue {
|
||||||
return object : LongSetValue() {
|
var internalValue: Set<Long> = default
|
||||||
private var internalValue: Set<Long> = default
|
val delegt = dynamicSet { internalValue }
|
||||||
|
return object : LongSetValue(), Set<Long> by delegt {
|
||||||
override var value: Set<Long>
|
override var value: Set<Long>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -609,8 +619,9 @@ internal fun Setting.valueImpl(default: Set<Long>): LongSetValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: Set<Float>): FloatSetValue {
|
internal fun Setting.valueImpl(default: Set<Float>): FloatSetValue {
|
||||||
return object : FloatSetValue() {
|
var internalValue: Set<Float> = default
|
||||||
private var internalValue: Set<Float> = default
|
val delegt = dynamicSet { internalValue }
|
||||||
|
return object : FloatSetValue(), Set<Float> by delegt {
|
||||||
override var value: Set<Float>
|
override var value: Set<Float>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -624,8 +635,9 @@ internal fun Setting.valueImpl(default: Set<Float>): FloatSetValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: Set<Double>): DoubleSetValue {
|
internal fun Setting.valueImpl(default: Set<Double>): DoubleSetValue {
|
||||||
return object : DoubleSetValue() {
|
var internalValue: Set<Double> = default
|
||||||
private var internalValue: Set<Double> = default
|
val delegt = dynamicSet { internalValue }
|
||||||
|
return object : DoubleSetValue(), Set<Double> by delegt {
|
||||||
override var value: Set<Double>
|
override var value: Set<Double>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -639,8 +651,9 @@ internal fun Setting.valueImpl(default: Set<Double>): DoubleSetValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: Set<Boolean>): BooleanSetValue {
|
internal fun Setting.valueImpl(default: Set<Boolean>): BooleanSetValue {
|
||||||
return object : BooleanSetValue() {
|
var internalValue: Set<Boolean> = default
|
||||||
private var internalValue: Set<Boolean> = default
|
val delegt = dynamicSet { internalValue }
|
||||||
|
return object : BooleanSetValue(), Set<Boolean> by delegt {
|
||||||
override var value: Set<Boolean>
|
override var value: Set<Boolean>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -654,8 +667,9 @@ internal fun Setting.valueImpl(default: Set<Boolean>): BooleanSetValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: Set<Char>): CharSetValue {
|
internal fun Setting.valueImpl(default: Set<Char>): CharSetValue {
|
||||||
return object : CharSetValue() {
|
var internalValue: Set<Char> = default
|
||||||
private var internalValue: Set<Char> = default
|
val delegt = dynamicSet { internalValue }
|
||||||
|
return object : CharSetValue(), Set<Char> by delegt {
|
||||||
override var value: Set<Char>
|
override var value: Set<Char>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -669,8 +683,9 @@ internal fun Setting.valueImpl(default: Set<Char>): CharSetValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Setting.valueImpl(default: Set<String>): StringSetValue {
|
internal fun Setting.valueImpl(default: Set<String>): StringSetValue {
|
||||||
return object : StringSetValue() {
|
var internalValue: Set<String> = default
|
||||||
private var internalValue: Set<String> = default
|
val delegt = dynamicSet { internalValue }
|
||||||
|
return object : StringSetValue(), Set<String> by delegt {
|
||||||
override var value: Set<String>
|
override var value: Set<String>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -690,7 +705,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableIntListValue {
|
): MutableIntListValue {
|
||||||
var internalValue: MutableList<Int> = default
|
var internalValue: MutableList<Int> = default
|
||||||
|
|
||||||
return object : MutableIntListValue(), MutableList<Int> by dynamicMutableList({ internalValue }) {
|
val delegt = dynamicMutableList{ internalValue }
|
||||||
|
return object : MutableIntListValue(), MutableList<Int> by delegt {
|
||||||
override var value: MutableList<Int>
|
override var value: MutableList<Int>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -700,7 +716,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableList<Int>> = object : KSerializer<MutableList<Int>> {
|
override val serializer: KSerializer<MutableList<Int>> = object : KSerializer<MutableList<Int>> {
|
||||||
private val delegate = ListSerializer(Int.serializer())
|
private val delegate = ListSerializer(Int.serializer())
|
||||||
@ -708,7 +724,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableList<Int> {
|
override fun deserialize(decoder: Decoder): MutableList<Int> {
|
||||||
return delegate.deserialize(decoder).toMutableList().observable {
|
return delegate.deserialize(decoder).toMutableList().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -725,7 +741,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableShortListValue {
|
): MutableShortListValue {
|
||||||
var internalValue: MutableList<Short> = default
|
var internalValue: MutableList<Short> = default
|
||||||
|
|
||||||
return object : MutableShortListValue(), MutableList<Short> by dynamicMutableList({ internalValue }) {
|
val delegt = dynamicMutableList{ internalValue }
|
||||||
|
return object : MutableShortListValue(), MutableList<Short> by delegt {
|
||||||
override var value: MutableList<Short>
|
override var value: MutableList<Short>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -735,7 +752,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableList<Short>> = object : KSerializer<MutableList<Short>> {
|
override val serializer: KSerializer<MutableList<Short>> = object : KSerializer<MutableList<Short>> {
|
||||||
private val delegate = ListSerializer(Short.serializer())
|
private val delegate = ListSerializer(Short.serializer())
|
||||||
@ -743,7 +760,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableList<Short> {
|
override fun deserialize(decoder: Decoder): MutableList<Short> {
|
||||||
return delegate.deserialize(decoder).toMutableList().observable {
|
return delegate.deserialize(decoder).toMutableList().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -760,7 +777,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableByteListValue {
|
): MutableByteListValue {
|
||||||
var internalValue: MutableList<Byte> = default
|
var internalValue: MutableList<Byte> = default
|
||||||
|
|
||||||
return object : MutableByteListValue(), MutableList<Byte> by dynamicMutableList({ internalValue }) {
|
val delegt = dynamicMutableList{ internalValue }
|
||||||
|
return object : MutableByteListValue(), MutableList<Byte> by delegt {
|
||||||
override var value: MutableList<Byte>
|
override var value: MutableList<Byte>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -770,7 +788,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableList<Byte>> = object : KSerializer<MutableList<Byte>> {
|
override val serializer: KSerializer<MutableList<Byte>> = object : KSerializer<MutableList<Byte>> {
|
||||||
private val delegate = ListSerializer(Byte.serializer())
|
private val delegate = ListSerializer(Byte.serializer())
|
||||||
@ -778,7 +796,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableList<Byte> {
|
override fun deserialize(decoder: Decoder): MutableList<Byte> {
|
||||||
return delegate.deserialize(decoder).toMutableList().observable {
|
return delegate.deserialize(decoder).toMutableList().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -795,7 +813,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableLongListValue {
|
): MutableLongListValue {
|
||||||
var internalValue: MutableList<Long> = default
|
var internalValue: MutableList<Long> = default
|
||||||
|
|
||||||
return object : MutableLongListValue(), MutableList<Long> by dynamicMutableList({ internalValue }) {
|
val delegt = dynamicMutableList{ internalValue }
|
||||||
|
return object : MutableLongListValue(), MutableList<Long> by delegt {
|
||||||
override var value: MutableList<Long>
|
override var value: MutableList<Long>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -805,7 +824,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableList<Long>> = object : KSerializer<MutableList<Long>> {
|
override val serializer: KSerializer<MutableList<Long>> = object : KSerializer<MutableList<Long>> {
|
||||||
private val delegate = ListSerializer(Long.serializer())
|
private val delegate = ListSerializer(Long.serializer())
|
||||||
@ -813,7 +832,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableList<Long> {
|
override fun deserialize(decoder: Decoder): MutableList<Long> {
|
||||||
return delegate.deserialize(decoder).toMutableList().observable {
|
return delegate.deserialize(decoder).toMutableList().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -830,7 +849,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableFloatListValue {
|
): MutableFloatListValue {
|
||||||
var internalValue: MutableList<Float> = default
|
var internalValue: MutableList<Float> = default
|
||||||
|
|
||||||
return object : MutableFloatListValue(), MutableList<Float> by dynamicMutableList({ internalValue }) {
|
val delegt = dynamicMutableList{ internalValue }
|
||||||
|
return object : MutableFloatListValue(), MutableList<Float> by delegt {
|
||||||
override var value: MutableList<Float>
|
override var value: MutableList<Float>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -840,7 +860,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableList<Float>> = object : KSerializer<MutableList<Float>> {
|
override val serializer: KSerializer<MutableList<Float>> = object : KSerializer<MutableList<Float>> {
|
||||||
private val delegate = ListSerializer(Float.serializer())
|
private val delegate = ListSerializer(Float.serializer())
|
||||||
@ -848,7 +868,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableList<Float> {
|
override fun deserialize(decoder: Decoder): MutableList<Float> {
|
||||||
return delegate.deserialize(decoder).toMutableList().observable {
|
return delegate.deserialize(decoder).toMutableList().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -865,7 +885,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableDoubleListValue {
|
): MutableDoubleListValue {
|
||||||
var internalValue: MutableList<Double> = default
|
var internalValue: MutableList<Double> = default
|
||||||
|
|
||||||
return object : MutableDoubleListValue(), MutableList<Double> by dynamicMutableList({ internalValue }) {
|
val delegt = dynamicMutableList{ internalValue }
|
||||||
|
return object : MutableDoubleListValue(), MutableList<Double> by delegt {
|
||||||
override var value: MutableList<Double>
|
override var value: MutableList<Double>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -875,7 +896,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableList<Double>> = object : KSerializer<MutableList<Double>> {
|
override val serializer: KSerializer<MutableList<Double>> = object : KSerializer<MutableList<Double>> {
|
||||||
private val delegate = ListSerializer(Double.serializer())
|
private val delegate = ListSerializer(Double.serializer())
|
||||||
@ -883,7 +904,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableList<Double> {
|
override fun deserialize(decoder: Decoder): MutableList<Double> {
|
||||||
return delegate.deserialize(decoder).toMutableList().observable {
|
return delegate.deserialize(decoder).toMutableList().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -900,7 +921,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableBooleanListValue {
|
): MutableBooleanListValue {
|
||||||
var internalValue: MutableList<Boolean> = default
|
var internalValue: MutableList<Boolean> = default
|
||||||
|
|
||||||
return object : MutableBooleanListValue(), MutableList<Boolean> by dynamicMutableList({ internalValue }) {
|
val delegt = dynamicMutableList{ internalValue }
|
||||||
|
return object : MutableBooleanListValue(), MutableList<Boolean> by delegt {
|
||||||
override var value: MutableList<Boolean>
|
override var value: MutableList<Boolean>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -910,7 +932,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableList<Boolean>> = object : KSerializer<MutableList<Boolean>> {
|
override val serializer: KSerializer<MutableList<Boolean>> = object : KSerializer<MutableList<Boolean>> {
|
||||||
private val delegate = ListSerializer(Boolean.serializer())
|
private val delegate = ListSerializer(Boolean.serializer())
|
||||||
@ -918,7 +940,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableList<Boolean> {
|
override fun deserialize(decoder: Decoder): MutableList<Boolean> {
|
||||||
return delegate.deserialize(decoder).toMutableList().observable {
|
return delegate.deserialize(decoder).toMutableList().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -935,7 +957,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableCharListValue {
|
): MutableCharListValue {
|
||||||
var internalValue: MutableList<Char> = default
|
var internalValue: MutableList<Char> = default
|
||||||
|
|
||||||
return object : MutableCharListValue(), MutableList<Char> by dynamicMutableList({ internalValue }) {
|
val delegt = dynamicMutableList{ internalValue }
|
||||||
|
return object : MutableCharListValue(), MutableList<Char> by delegt {
|
||||||
override var value: MutableList<Char>
|
override var value: MutableList<Char>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -945,7 +968,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableList<Char>> = object : KSerializer<MutableList<Char>> {
|
override val serializer: KSerializer<MutableList<Char>> = object : KSerializer<MutableList<Char>> {
|
||||||
private val delegate = ListSerializer(Char.serializer())
|
private val delegate = ListSerializer(Char.serializer())
|
||||||
@ -953,7 +976,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableList<Char> {
|
override fun deserialize(decoder: Decoder): MutableList<Char> {
|
||||||
return delegate.deserialize(decoder).toMutableList().observable {
|
return delegate.deserialize(decoder).toMutableList().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -970,7 +993,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableStringListValue {
|
): MutableStringListValue {
|
||||||
var internalValue: MutableList<String> = default
|
var internalValue: MutableList<String> = default
|
||||||
|
|
||||||
return object : MutableStringListValue(), MutableList<String> by dynamicMutableList({ internalValue }) {
|
val delegt = dynamicMutableList{ internalValue }
|
||||||
|
return object : MutableStringListValue(), MutableList<String> by delegt {
|
||||||
override var value: MutableList<String>
|
override var value: MutableList<String>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -980,7 +1004,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableList<String>> = object : KSerializer<MutableList<String>> {
|
override val serializer: KSerializer<MutableList<String>> = object : KSerializer<MutableList<String>> {
|
||||||
private val delegate = ListSerializer(String.serializer())
|
private val delegate = ListSerializer(String.serializer())
|
||||||
@ -988,7 +1012,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableList<String> {
|
override fun deserialize(decoder: Decoder): MutableList<String> {
|
||||||
return delegate.deserialize(decoder).toMutableList().observable {
|
return delegate.deserialize(decoder).toMutableList().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1005,7 +1029,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableIntSetValue {
|
): MutableIntSetValue {
|
||||||
var internalValue: MutableSet<Int> = default
|
var internalValue: MutableSet<Int> = default
|
||||||
|
|
||||||
return object : MutableIntSetValue(), MutableSet<Int> by dynamicMutableSet({ internalValue }) {
|
val delegt = dynamicMutableSet{ internalValue }
|
||||||
|
return object : MutableIntSetValue(), MutableSet<Int> by delegt {
|
||||||
override var value: MutableSet<Int>
|
override var value: MutableSet<Int>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -1015,7 +1040,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableSet<Int>> = object : KSerializer<MutableSet<Int>> {
|
override val serializer: KSerializer<MutableSet<Int>> = object : KSerializer<MutableSet<Int>> {
|
||||||
private val delegate = SetSerializer(Int.serializer())
|
private val delegate = SetSerializer(Int.serializer())
|
||||||
@ -1023,7 +1048,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableSet<Int> {
|
override fun deserialize(decoder: Decoder): MutableSet<Int> {
|
||||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1040,7 +1065,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableShortSetValue {
|
): MutableShortSetValue {
|
||||||
var internalValue: MutableSet<Short> = default
|
var internalValue: MutableSet<Short> = default
|
||||||
|
|
||||||
return object : MutableShortSetValue(), MutableSet<Short> by dynamicMutableSet({ internalValue }) {
|
val delegt = dynamicMutableSet{ internalValue }
|
||||||
|
return object : MutableShortSetValue(), MutableSet<Short> by delegt {
|
||||||
override var value: MutableSet<Short>
|
override var value: MutableSet<Short>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -1050,7 +1076,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableSet<Short>> = object : KSerializer<MutableSet<Short>> {
|
override val serializer: KSerializer<MutableSet<Short>> = object : KSerializer<MutableSet<Short>> {
|
||||||
private val delegate = SetSerializer(Short.serializer())
|
private val delegate = SetSerializer(Short.serializer())
|
||||||
@ -1058,7 +1084,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableSet<Short> {
|
override fun deserialize(decoder: Decoder): MutableSet<Short> {
|
||||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1075,7 +1101,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableByteSetValue {
|
): MutableByteSetValue {
|
||||||
var internalValue: MutableSet<Byte> = default
|
var internalValue: MutableSet<Byte> = default
|
||||||
|
|
||||||
return object : MutableByteSetValue(), MutableSet<Byte> by dynamicMutableSet({ internalValue }) {
|
val delegt = dynamicMutableSet{ internalValue }
|
||||||
|
return object : MutableByteSetValue(), MutableSet<Byte> by delegt {
|
||||||
override var value: MutableSet<Byte>
|
override var value: MutableSet<Byte>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -1085,7 +1112,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableSet<Byte>> = object : KSerializer<MutableSet<Byte>> {
|
override val serializer: KSerializer<MutableSet<Byte>> = object : KSerializer<MutableSet<Byte>> {
|
||||||
private val delegate = SetSerializer(Byte.serializer())
|
private val delegate = SetSerializer(Byte.serializer())
|
||||||
@ -1093,7 +1120,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableSet<Byte> {
|
override fun deserialize(decoder: Decoder): MutableSet<Byte> {
|
||||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1110,7 +1137,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableLongSetValue {
|
): MutableLongSetValue {
|
||||||
var internalValue: MutableSet<Long> = default
|
var internalValue: MutableSet<Long> = default
|
||||||
|
|
||||||
return object : MutableLongSetValue(), MutableSet<Long> by dynamicMutableSet({ internalValue }) {
|
val delegt = dynamicMutableSet{ internalValue }
|
||||||
|
return object : MutableLongSetValue(), MutableSet<Long> by delegt {
|
||||||
override var value: MutableSet<Long>
|
override var value: MutableSet<Long>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -1120,7 +1148,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableSet<Long>> = object : KSerializer<MutableSet<Long>> {
|
override val serializer: KSerializer<MutableSet<Long>> = object : KSerializer<MutableSet<Long>> {
|
||||||
private val delegate = SetSerializer(Long.serializer())
|
private val delegate = SetSerializer(Long.serializer())
|
||||||
@ -1128,7 +1156,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableSet<Long> {
|
override fun deserialize(decoder: Decoder): MutableSet<Long> {
|
||||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1145,7 +1173,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableFloatSetValue {
|
): MutableFloatSetValue {
|
||||||
var internalValue: MutableSet<Float> = default
|
var internalValue: MutableSet<Float> = default
|
||||||
|
|
||||||
return object : MutableFloatSetValue(), MutableSet<Float> by dynamicMutableSet({ internalValue }) {
|
val delegt = dynamicMutableSet{ internalValue }
|
||||||
|
return object : MutableFloatSetValue(), MutableSet<Float> by delegt {
|
||||||
override var value: MutableSet<Float>
|
override var value: MutableSet<Float>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -1155,7 +1184,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableSet<Float>> = object : KSerializer<MutableSet<Float>> {
|
override val serializer: KSerializer<MutableSet<Float>> = object : KSerializer<MutableSet<Float>> {
|
||||||
private val delegate = SetSerializer(Float.serializer())
|
private val delegate = SetSerializer(Float.serializer())
|
||||||
@ -1163,7 +1192,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableSet<Float> {
|
override fun deserialize(decoder: Decoder): MutableSet<Float> {
|
||||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1180,7 +1209,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableDoubleSetValue {
|
): MutableDoubleSetValue {
|
||||||
var internalValue: MutableSet<Double> = default
|
var internalValue: MutableSet<Double> = default
|
||||||
|
|
||||||
return object : MutableDoubleSetValue(), MutableSet<Double> by dynamicMutableSet({ internalValue }) {
|
val delegt = dynamicMutableSet{ internalValue }
|
||||||
|
return object : MutableDoubleSetValue(), MutableSet<Double> by delegt {
|
||||||
override var value: MutableSet<Double>
|
override var value: MutableSet<Double>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -1190,7 +1220,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableSet<Double>> = object : KSerializer<MutableSet<Double>> {
|
override val serializer: KSerializer<MutableSet<Double>> = object : KSerializer<MutableSet<Double>> {
|
||||||
private val delegate = SetSerializer(Double.serializer())
|
private val delegate = SetSerializer(Double.serializer())
|
||||||
@ -1198,7 +1228,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableSet<Double> {
|
override fun deserialize(decoder: Decoder): MutableSet<Double> {
|
||||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1215,7 +1245,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableBooleanSetValue {
|
): MutableBooleanSetValue {
|
||||||
var internalValue: MutableSet<Boolean> = default
|
var internalValue: MutableSet<Boolean> = default
|
||||||
|
|
||||||
return object : MutableBooleanSetValue(), MutableSet<Boolean> by dynamicMutableSet({ internalValue }) {
|
val delegt = dynamicMutableSet{ internalValue }
|
||||||
|
return object : MutableBooleanSetValue(), MutableSet<Boolean> by delegt {
|
||||||
override var value: MutableSet<Boolean>
|
override var value: MutableSet<Boolean>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -1225,7 +1256,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableSet<Boolean>> = object : KSerializer<MutableSet<Boolean>> {
|
override val serializer: KSerializer<MutableSet<Boolean>> = object : KSerializer<MutableSet<Boolean>> {
|
||||||
private val delegate = SetSerializer(Boolean.serializer())
|
private val delegate = SetSerializer(Boolean.serializer())
|
||||||
@ -1233,7 +1264,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableSet<Boolean> {
|
override fun deserialize(decoder: Decoder): MutableSet<Boolean> {
|
||||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1250,7 +1281,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableCharSetValue {
|
): MutableCharSetValue {
|
||||||
var internalValue: MutableSet<Char> = default
|
var internalValue: MutableSet<Char> = default
|
||||||
|
|
||||||
return object : MutableCharSetValue(), MutableSet<Char> by dynamicMutableSet({ internalValue }) {
|
val delegt = dynamicMutableSet{ internalValue }
|
||||||
|
return object : MutableCharSetValue(), MutableSet<Char> by delegt {
|
||||||
override var value: MutableSet<Char>
|
override var value: MutableSet<Char>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -1260,7 +1292,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableSet<Char>> = object : KSerializer<MutableSet<Char>> {
|
override val serializer: KSerializer<MutableSet<Char>> = object : KSerializer<MutableSet<Char>> {
|
||||||
private val delegate = SetSerializer(Char.serializer())
|
private val delegate = SetSerializer(Char.serializer())
|
||||||
@ -1268,7 +1300,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableSet<Char> {
|
override fun deserialize(decoder: Decoder): MutableSet<Char> {
|
||||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1285,7 +1317,8 @@ internal fun Setting.valueImpl(
|
|||||||
): MutableStringSetValue {
|
): MutableStringSetValue {
|
||||||
var internalValue: MutableSet<String> = default
|
var internalValue: MutableSet<String> = default
|
||||||
|
|
||||||
return object : MutableStringSetValue(), MutableSet<String> by dynamicMutableSet({ internalValue }) {
|
val delegt = dynamicMutableSet{ internalValue }
|
||||||
|
return object : MutableStringSetValue(), MutableSet<String> by delegt {
|
||||||
override var value: MutableSet<String>
|
override var value: MutableSet<String>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -1295,7 +1328,7 @@ internal fun Setting.valueImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline val `this` get() = this
|
private val outerThis get() = this
|
||||||
|
|
||||||
override val serializer: KSerializer<MutableSet<String>> = object : KSerializer<MutableSet<String>> {
|
override val serializer: KSerializer<MutableSet<String>> = object : KSerializer<MutableSet<String>> {
|
||||||
private val delegate = SetSerializer(String.serializer())
|
private val delegate = SetSerializer(String.serializer())
|
||||||
@ -1303,7 +1336,7 @@ internal fun Setting.valueImpl(
|
|||||||
|
|
||||||
override fun deserialize(decoder: Decoder): MutableSet<String> {
|
override fun deserialize(decoder: Decoder): MutableSet<String> {
|
||||||
return delegate.deserialize(decoder).toMutableSet().observable {
|
return delegate.deserialize(decoder).toMutableSet().observable {
|
||||||
onElementChanged(`this`)
|
onElementChanged(outerThis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1326,7 +1359,7 @@ internal fun <T : Setting> Setting.valueImpl(default: T): Value<T> {
|
|||||||
onElementChanged(this)
|
onElementChanged(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override val serializer = object : KSerializer<T> {
|
override val serializer = object : KSerializer<T>{
|
||||||
override val descriptor: SerialDescriptor
|
override val descriptor: SerialDescriptor
|
||||||
get() = internalValue.updaterSerializer.descriptor
|
get() = internalValue.updaterSerializer.descriptor
|
||||||
|
|
@ -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.setting
|
package net.mamoe.mirai.console.setting.internal
|
||||||
|
|
||||||
import kotlinx.serialization.ImplicitReflectionSerializer
|
import kotlinx.serialization.ImplicitReflectionSerializer
|
||||||
import kotlinx.serialization.serializer
|
import kotlinx.serialization.serializer
|
@ -7,15 +7,21 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package net.mamoe.mirai.console.setting
|
package net.mamoe.mirai.console.setting.internal
|
||||||
|
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.builtins.ListSerializer
|
||||||
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
import net.mamoe.mirai.console.setting.SerialName
|
||||||
|
import net.mamoe.mirai.console.setting.Setting
|
||||||
|
import net.mamoe.mirai.console.setting.Value
|
||||||
|
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||||
import net.mamoe.yamlkt.Yaml
|
import net.mamoe.yamlkt.Yaml
|
||||||
import net.mamoe.yamlkt.YamlConfiguration
|
import net.mamoe.yamlkt.YamlConfiguration
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
import kotlin.reflect.full.findAnnotation
|
import kotlin.reflect.full.findAnnotation
|
||||||
|
|
||||||
internal abstract class AbstractSetting {
|
internal abstract class SettingImpl {
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
internal var valueList: MutableList<Pair<Value<*>, KProperty<*>>> = mutableListOf()
|
internal var valueList: MutableList<Pair<Value<*>, KProperty<*>>> = mutableListOf()
|
||||||
@ -31,15 +37,18 @@ internal abstract class AbstractSetting {
|
|||||||
internal val kotlinSerializer: KSerializer<Setting> by lazy {
|
internal val kotlinSerializer: KSerializer<Setting> by lazy {
|
||||||
object : KSerializer<Setting> {
|
object : KSerializer<Setting> {
|
||||||
override val descriptor: SerialDescriptor
|
override val descriptor: SerialDescriptor
|
||||||
get() = this@AbstractSetting.updaterSerializer.descriptor
|
get() = this@SettingImpl.updaterSerializer.descriptor
|
||||||
|
|
||||||
override fun deserialize(decoder: Decoder): Setting {
|
override fun deserialize(decoder: Decoder): Setting {
|
||||||
this@AbstractSetting.updaterSerializer.deserialize(decoder)
|
this@SettingImpl.updaterSerializer.deserialize(decoder)
|
||||||
return this@AbstractSetting as Setting
|
return this@SettingImpl as Setting
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, value: Setting) {
|
override fun serialize(encoder: Encoder, value: Setting) {
|
||||||
this@AbstractSetting.updaterSerializer.serialize(encoder, SettingSerializerMark)
|
this@SettingImpl.updaterSerializer.serialize(
|
||||||
|
encoder,
|
||||||
|
SettingSerializerMark
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,7 +59,7 @@ internal abstract class AbstractSetting {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
internal val yaml =
|
internal val yamlForToString =
|
||||||
Yaml(
|
Yaml(
|
||||||
configuration = YamlConfiguration(
|
configuration = YamlConfiguration(
|
||||||
nonStrictNullability = true,
|
nonStrictNullability = true,
|
||||||
@ -67,6 +76,7 @@ internal class SettingUpdaterSerializer(
|
|||||||
private val instance: Setting
|
private val instance: Setting
|
||||||
) : KSerializer<SettingSerializerMark> {
|
) : KSerializer<SettingSerializerMark> {
|
||||||
override val descriptor: SerialDescriptor by lazy {
|
override val descriptor: SerialDescriptor by lazy {
|
||||||
|
@OptIn(MiraiExperimentalAPI::class)
|
||||||
SerialDescriptor(instance.serialName) {
|
SerialDescriptor(instance.serialName) {
|
||||||
for ((value, property) in instance.valueList) {
|
for ((value, property) in instance.valueList) {
|
||||||
element(property.serialNameOrPropertyName, value.serializer.descriptor, annotations, true)
|
element(property.serialNameOrPropertyName, value.serializer.descriptor, annotations, true)
|
||||||
@ -89,9 +99,8 @@ internal class SettingUpdaterSerializer(
|
|||||||
while (true) {
|
while (true) {
|
||||||
val index = this.decodeElementIndex(descriptor)
|
val index = this.decodeElementIndex(descriptor)
|
||||||
if (index == CompositeDecoder.READ_DONE) return@decodeStructure SettingSerializerMark
|
if (index == CompositeDecoder.READ_DONE) return@decodeStructure SettingSerializerMark
|
||||||
val value = instance.valueList[index].first
|
val value = instance.valueList[index].first as Value<Any>
|
||||||
|
value.value = this.decodeSerializableElement(
|
||||||
this.decodeSerializableElement(
|
|
||||||
descriptor,
|
descriptor,
|
||||||
index,
|
index,
|
||||||
value.serializer
|
value.serializer
|
||||||
@ -101,20 +110,43 @@ internal class SettingUpdaterSerializer(
|
|||||||
SettingSerializerMark
|
SettingSerializerMark
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, value: SettingSerializerMark) = encoder.encodeStructure(descriptor) {
|
private val emptyList = emptyList<String>()
|
||||||
|
private val emptyListSerializer = ListSerializer(String.serializer())
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: SettingSerializerMark) {
|
||||||
|
if (instance.valueList.isEmpty()) {
|
||||||
|
emptyListSerializer.serialize(encoder, emptyList)
|
||||||
|
} else encoder.encodeStructure(descriptor) {
|
||||||
instance.valueList.forEachIndexed { index, (value, _) ->
|
instance.valueList.forEachIndexed { index, (value, _) ->
|
||||||
@Suppress("UNCHECKED_CAST") // erased, no problem.
|
@Suppress("UNCHECKED_CAST") // erased, no problem.
|
||||||
this.encodeSerializableElement(
|
this.encodeElementSmart(descriptor, index, value)
|
||||||
descriptor,
|
}
|
||||||
index,
|
|
||||||
value.serializer as KSerializer<Any>,
|
|
||||||
value.value
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// until https://github.com/Him188/yamlkt/issues/2 fixed
|
||||||
|
internal fun <T : Any> CompositeEncoder.encodeElementSmart(
|
||||||
|
descriptor: SerialDescriptor,
|
||||||
|
index: Int,
|
||||||
|
value: Value<T>
|
||||||
|
) {
|
||||||
|
when (value.value::class) {
|
||||||
|
String::class -> this.encodeStringElement(descriptor, index, value.value as String)
|
||||||
|
Int::class -> this.encodeIntElement(descriptor, index, value.value as Int)
|
||||||
|
Byte::class -> this.encodeByteElement(descriptor, index, value.value as Byte)
|
||||||
|
Char::class -> this.encodeCharElement(descriptor, index, value.value as Char)
|
||||||
|
Long::class -> this.encodeLongElement(descriptor, index, value.value as Long)
|
||||||
|
Float::class -> this.encodeFloatElement(descriptor, index, value.value as Float)
|
||||||
|
Double::class -> this.encodeDoubleElement(descriptor, index, value.value as Double)
|
||||||
|
Boolean::class -> this.encodeBooleanElement(descriptor, index, value.value as Boolean)
|
||||||
|
else ->
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
this.encodeSerializableElement(descriptor, index, value.serializer as KSerializer<Any>, value.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal object SettingSerializerMark
|
internal object SettingSerializerMark
|
||||||
|
|
||||||
internal val KProperty<*>.serialNameOrPropertyName: String get() = this.findAnnotation<SerialName>()?.value ?: this.name
|
internal val KProperty<*>.serialNameOrPropertyName: String get() = this.findAnnotation<SerialName>()?.value ?: this.name
|
||||||
@ -132,3 +164,14 @@ internal inline fun <E> KSerializer<E>.bind(
|
|||||||
this@bind.serialize(encoder, getter())
|
this@bind.serialize(encoder, getter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal inline fun <E, R> KSerializer<E>.map(
|
||||||
|
crossinline serializer: (R) -> E,
|
||||||
|
crossinline deserializer: (E) -> R
|
||||||
|
): KSerializer<R> {
|
||||||
|
return object : KSerializer<R> {
|
||||||
|
override val descriptor: SerialDescriptor get() = this@map.descriptor
|
||||||
|
override fun deserialize(decoder: Decoder): R = this@map.deserialize(decoder).let(deserializer)
|
||||||
|
override fun serialize(encoder: Encoder, value: R) = this@map.serialize(encoder, value.let(serializer))
|
||||||
|
}
|
||||||
|
}
|
@ -6,13 +6,16 @@
|
|||||||
*
|
*
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||||
|
|
||||||
package net.mamoe.mirai.console.setting
|
package net.mamoe.mirai.console.setting.internal
|
||||||
|
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.*
|
||||||
import kotlinx.serialization.builtins.ListSerializer
|
import kotlinx.serialization.builtins.ListSerializer
|
||||||
import kotlinx.serialization.builtins.SetSerializer
|
import kotlinx.serialization.builtins.SetSerializer
|
||||||
|
import net.mamoe.mirai.console.setting.*
|
||||||
import net.mamoe.yamlkt.YamlDynamicSerializer
|
import net.mamoe.yamlkt.YamlDynamicSerializer
|
||||||
|
import kotlin.internal.LowPriorityInOverloadResolution
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.full.createInstance
|
import kotlin.reflect.full.createInstance
|
||||||
|
|
||||||
@ -53,7 +56,10 @@ internal fun <T : Any> Setting.valueImpl(
|
|||||||
internalValue.shadowMap(transform = { it.value }, transformBack = { valueMapper(it) })
|
internalValue.shadowMap(transform = { it.value }, transformBack = { valueMapper(it) })
|
||||||
|
|
||||||
var shadowed: MutableList<T> = updateShadow()
|
var shadowed: MutableList<T> = updateShadow()
|
||||||
return object : MutableListValue<T>(), MutableList<T> by dynamicMutableList({ shadowed }) {
|
|
||||||
|
|
||||||
|
val delegt = dynamicMutableList { shadowed }
|
||||||
|
return object : MutableListValue<T>(), MutableList<T> by delegt {
|
||||||
override var value: MutableList<Value<T>>
|
override var value: MutableList<Value<T>>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -86,7 +92,8 @@ internal fun <T : Setting> Setting.valueImpl(
|
|||||||
): MutableSettingListValue<T> {
|
): MutableSettingListValue<T> {
|
||||||
var internalValue: MutableList<T> = default
|
var internalValue: MutableList<T> = default
|
||||||
|
|
||||||
return object : MutableSettingListValue<T>(), MutableList<T> by dynamicMutableList({ internalValue }) {
|
val delegt = dynamicMutableList { internalValue }
|
||||||
|
return object : MutableSettingListValue<T>(), MutableList<T> by delegt {
|
||||||
override var value: MutableList<T>
|
override var value: MutableList<T>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -118,7 +125,8 @@ internal fun <T : Setting> Setting.valueImpl(
|
|||||||
): SettingListValue<T> {
|
): SettingListValue<T> {
|
||||||
var internalValue: List<T> = default
|
var internalValue: List<T> = default
|
||||||
|
|
||||||
return object : SettingListValue<T>(), List<T> by dynamicList({ internalValue }) {
|
val delegt = dynamicList { internalValue }
|
||||||
|
return object : SettingListValue<T>(), List<T> by delegt {
|
||||||
override var value: List<T>
|
override var value: List<T>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -150,7 +158,8 @@ internal fun <T : Setting> Setting.valueImpl(
|
|||||||
): SettingSetValue<T> {
|
): SettingSetValue<T> {
|
||||||
var internalValue: Set<T> = default
|
var internalValue: Set<T> = default
|
||||||
|
|
||||||
return object : SettingSetValue<T>(), Set<T> by dynamicSet({ internalValue }) {
|
val delegt = dynamicSet { internalValue }
|
||||||
|
return object : SettingSetValue<T>(), Set<T> by delegt {
|
||||||
override var value: Set<T>
|
override var value: Set<T>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -182,7 +191,8 @@ internal fun <T : Setting> Setting.valueImpl(
|
|||||||
): MutableSettingSetValue<T> {
|
): MutableSettingSetValue<T> {
|
||||||
var internalValue: MutableSet<T> = default
|
var internalValue: MutableSet<T> = default
|
||||||
|
|
||||||
return object : MutableSettingSetValue<T>(), MutableSet<T> by dynamicMutableSet({ internalValue }) {
|
val delegt = dynamicMutableSet { internalValue }
|
||||||
|
return object : MutableSettingSetValue<T>(), MutableSet<T> by delegt {
|
||||||
override var value: MutableSet<T>
|
override var value: MutableSet<T>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -247,7 +257,9 @@ internal fun <T : Any> Setting.valueImpl(
|
|||||||
internalValue.shadowMap(transform = { it.value }, transformBack = { valueMapper(it) })
|
internalValue.shadowMap(transform = { it.value }, transformBack = { valueMapper(it) })
|
||||||
|
|
||||||
var shadowed: MutableSet<T> = updateShadow()
|
var shadowed: MutableSet<T> = updateShadow()
|
||||||
return object : MutableSetValue<T>(), MutableSet<T> by dynamicMutableSet({ shadowed }) {
|
|
||||||
|
val delegt = dynamicMutableSet { shadowed }
|
||||||
|
return object : MutableSetValue<T>(), MutableSet<T> by delegt {
|
||||||
override var value: MutableSet<Value<T>>
|
override var value: MutableSet<Value<T>>
|
||||||
get() = internalValue
|
get() = internalValue
|
||||||
set(new) {
|
set(new) {
|
||||||
@ -280,6 +292,7 @@ internal fun <T : Any> Setting.valueImpl(
|
|||||||
* For primitives and serializable only
|
* For primitives and serializable only
|
||||||
*/
|
*/
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
|
@LowPriorityInOverloadResolution
|
||||||
internal inline fun <reified T : Any> Setting.valueImpl(default: T): Value<T> =
|
internal inline fun <reified T : Any> Setting.valueImpl(default: T): Value<T> =
|
||||||
valueImpl(default, T::class)
|
valueImpl(default, T::class)
|
||||||
|
|
||||||
@ -293,19 +306,14 @@ internal fun <T : Any> Setting.valueImpl(default: T, clazz: KClass<out T>): Valu
|
|||||||
}
|
}
|
||||||
return object : DynamicReferenceValue<T>() {
|
return object : DynamicReferenceValue<T>() {
|
||||||
override var value: T = default
|
override var value: T = default
|
||||||
override val serializer: KSerializer<T>
|
override val serializer: KSerializer<T> = object : KSerializer<T> {
|
||||||
get() = object : KSerializer<T> {
|
|
||||||
override val descriptor: SerialDescriptor
|
override val descriptor: SerialDescriptor
|
||||||
get() = YamlDynamicSerializer.descriptor
|
get() = YamlDynamicSerializer.descriptor
|
||||||
|
|
||||||
override fun deserialize(decoder: Decoder): T {
|
override fun deserialize(decoder: Decoder): T =
|
||||||
return YamlDynamicSerializer.deserialize(decoder).smartCastPrimitive(clazz)
|
YamlDynamicSerializer.deserialize(decoder).smartCastPrimitive(clazz)
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ImplicitReflectionSerializer::class)
|
override fun serialize(encoder: Encoder, value: T) = YamlDynamicSerializer.serialize(encoder, value)
|
||||||
override fun serialize(encoder: Encoder, value: T) {
|
|
||||||
YamlDynamicSerializer.serialize(encoder, value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -22,13 +22,6 @@ import java.io.File
|
|||||||
val User.isManager: Boolean
|
val User.isManager: Boolean
|
||||||
get() = this.bot.managers.contains(this.id)
|
get() = this.bot.managers.contains(this.id)
|
||||||
|
|
||||||
@JvmName("addManager")
|
|
||||||
@JvmSynthetic
|
|
||||||
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
|
|
||||||
fun Bot.addManagerDeprecated(long: Long) {
|
|
||||||
addManager(long)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Bot.addManager(long: Long): Boolean {
|
internal fun Bot.addManager(long: Long): Boolean {
|
||||||
TODO()
|
TODO()
|
||||||
return true
|
return true
|
||||||
|
@ -11,7 +11,6 @@ package net.mamoe.mirai.console.utils
|
|||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import net.mamoe.mirai.console.MiraiConsole
|
import net.mamoe.mirai.console.MiraiConsole
|
||||||
import net.mamoe.mirai.console.plugins.PluginBase
|
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@ -25,30 +24,28 @@ object ConsoleInput {
|
|||||||
* 如弹出框,或一行字
|
* 如弹出框,或一行字
|
||||||
*/
|
*/
|
||||||
suspend fun requestInput(
|
suspend fun requestInput(
|
||||||
hint:String
|
hint: String
|
||||||
):String{
|
): String {
|
||||||
return withContext(inputDispatcher) {
|
return withContext(inputDispatcher) {
|
||||||
MiraiConsole.frontEnd.requestInput(hint)
|
MiraiConsole.frontEnd.requestInput(hint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requestInputBlocking(hint:String):String = runBlocking { requestInput(hint) }
|
fun requestInputBlocking(hint: String): String = runBlocking { requestInput(hint) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* asnyc获取
|
* asnyc获取
|
||||||
*/
|
*/
|
||||||
fun requestInputAsync(
|
fun requestInputAsync(
|
||||||
pluginBase: PluginBase,
|
scope: CoroutineScope,
|
||||||
hint: String
|
hint: String
|
||||||
):Deferred<String>{
|
): Deferred<String> {
|
||||||
return pluginBase.async {
|
return scope.async {
|
||||||
requestInput(hint)
|
requestInput(hint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun MiraiConsole.requestInput(hint:String):String = requestInput(hint)
|
suspend fun MiraiConsole.requestInput(hint: String): String = requestInput(hint)
|
||||||
|
|
||||||
suspend fun PluginBase.requestInputAsync(hint:String):Deferred<String> = requestInputAsync(hint)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.utils
|
||||||
|
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.future.future
|
||||||
|
import net.mamoe.mirai.console.plugins.builtin.JavaPlugin
|
||||||
|
import java.util.concurrent.Callable
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.Future
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拥有生命周期管理的 Java 线程池.
|
||||||
|
*
|
||||||
|
* 在插件被 [卸载][JavaPlugin.onDisable] 时将会自动停止.
|
||||||
|
*
|
||||||
|
* @see JavaPlugin.scheduler 获取实例
|
||||||
|
*/
|
||||||
|
class JavaPluginScheduler internal constructor(parentCoroutineContext: CoroutineContext) : CoroutineScope {
|
||||||
|
override val coroutineContext: CoroutineContext =
|
||||||
|
parentCoroutineContext + SupervisorJob(parentCoroutineContext[Job])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增一个 Repeating Task (定时任务)
|
||||||
|
*
|
||||||
|
* 这个 Runnable 会被每 [intervalMs] 调用一次(不包含 [runnable] 执行时间)
|
||||||
|
*
|
||||||
|
* @see Future.cancel 取消这个任务
|
||||||
|
*/
|
||||||
|
fun repeating(intervalMs: Long, runnable: Runnable): Future<Void?> {
|
||||||
|
return this.future {
|
||||||
|
while (isActive) {
|
||||||
|
withContext(Dispatchers.IO) { runnable.run() }
|
||||||
|
delay(intervalMs)
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增一个 Delayed Task (延迟任务)
|
||||||
|
*
|
||||||
|
* 在延迟 [delayMillis] 后执行 [runnable]
|
||||||
|
*/
|
||||||
|
fun delayed(delayMillis: Long, runnable: Runnable): CompletableFuture<Void?> {
|
||||||
|
return future {
|
||||||
|
delay(delayMillis)
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
runnable.run()
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增一个 Delayed Task (延迟任务)
|
||||||
|
*
|
||||||
|
* 在延迟 [delayMillis] 后执行 [runnable]
|
||||||
|
*/
|
||||||
|
fun <R> delayed(delayMillis: Long, runnable: Callable<R>): CompletableFuture<Void?> {
|
||||||
|
return future {
|
||||||
|
delay(delayMillis)
|
||||||
|
withContext(Dispatchers.IO) { runnable.call() }
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步执行一个任务, 最终返回 [Future], 与 Java 使用方法无异, 但效率更高且可以在插件关闭时停止
|
||||||
|
*/
|
||||||
|
fun <R> async(supplier: Callable<R>): Future<R> {
|
||||||
|
return future {
|
||||||
|
withContext(Dispatchers.IO) { supplier.call() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步执行一个任务, 没有返回
|
||||||
|
*/
|
||||||
|
fun async(runnable: Runnable): Future<Void?> {
|
||||||
|
return future {
|
||||||
|
withContext(Dispatchers.IO) { runnable.run() }
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,161 +0,0 @@
|
|||||||
package net.mamoe.mirai.console.utils
|
|
||||||
import net.mamoe.mirai.contact.Group
|
|
||||||
import net.mamoe.mirai.contact.Member
|
|
||||||
import kotlin.contracts.ExperimentalContracts
|
|
||||||
import kotlin.contracts.InvocationKind
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行N次 builder
|
|
||||||
* 成功一次就会结束
|
|
||||||
* 否则就会throw
|
|
||||||
*/
|
|
||||||
@OptIn(ExperimentalContracts::class)
|
|
||||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE")
|
|
||||||
@kotlin.internal.InlineOnly
|
|
||||||
inline fun <R> retryCatching(n: Int, block: () -> R): Result<R> {
|
|
||||||
contract {
|
|
||||||
callsInPlace(block, InvocationKind.AT_LEAST_ONCE)
|
|
||||||
}
|
|
||||||
require(n >= 0) { "param n for retryCatching must not be negative" }
|
|
||||||
var exception: Throwable? = null
|
|
||||||
repeat(n){
|
|
||||||
try {
|
|
||||||
return Result.success(block())
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
exception?.addSuppressedMirai(e)
|
|
||||||
exception = e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Result.failure(exception!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalContracts::class)
|
|
||||||
inline fun <T> tryNTimes(n: Int = 2, block: () -> T):T {
|
|
||||||
contract {
|
|
||||||
callsInPlace(block, InvocationKind.AT_LEAST_ONCE)
|
|
||||||
}
|
|
||||||
require(n >= 0) { "param n for tryNTimes must not be negative" }
|
|
||||||
var last:Exception? = null
|
|
||||||
repeat(n){
|
|
||||||
try {
|
|
||||||
return block.invoke()
|
|
||||||
}catch (e:Exception){
|
|
||||||
last = e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//给我编译
|
|
||||||
|
|
||||||
throw last!!
|
|
||||||
}
|
|
||||||
|
|
||||||
@PublishedApi
|
|
||||||
internal fun Throwable.addSuppressedMirai(e: Throwable) {
|
|
||||||
if (e === this) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
kotlin.runCatching {
|
|
||||||
this.addSuppressed(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 两个字符串的近似值
|
|
||||||
* 要求前面完全一致
|
|
||||||
* 如
|
|
||||||
* XXXXXYYYYY.fuzzyCompare(XXXXXYYY) = 0.8
|
|
||||||
* XXXXXYYYYY.fuzzyCompare(XXXXXYYYZZ) = 0.8
|
|
||||||
*/
|
|
||||||
|
|
||||||
internal fun String.fuzzyCompare(target: String): Double {
|
|
||||||
var step = 0
|
|
||||||
if (this == target) {
|
|
||||||
return 1.0
|
|
||||||
}
|
|
||||||
if (target.length > this.length) {
|
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
for (i in this.indices) {
|
|
||||||
if (target.length == i) {
|
|
||||||
step--
|
|
||||||
}else {
|
|
||||||
if (this[i] != target[i]) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
step++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(step == this.length-1){
|
|
||||||
return 1.0
|
|
||||||
}
|
|
||||||
return step.toDouble()/this.length
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模糊搜索一个List中index最接近target的东西
|
|
||||||
*/
|
|
||||||
internal inline fun <T : Any> Collection<T>.fuzzySearch(
|
|
||||||
target: String,
|
|
||||||
index: (T) -> String
|
|
||||||
): T? {
|
|
||||||
if (this.isEmpty()) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
var potential: T? = null
|
|
||||||
var rate = 0.0
|
|
||||||
this.forEach {
|
|
||||||
val thisIndex = index(it)
|
|
||||||
if(thisIndex == target){
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
with(thisIndex.fuzzyCompare(target)) {
|
|
||||||
if (this > rate) {
|
|
||||||
rate = this
|
|
||||||
potential = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return potential
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模糊搜索一个List中index最接近target的东西
|
|
||||||
* 并且确保target是唯一的
|
|
||||||
* 如搜索index为XXXXYY list中同时存在XXXXYYY XXXXYYYY 将返回null
|
|
||||||
*/
|
|
||||||
internal inline fun <T : Any> Collection<T>.fuzzySearchOnly(
|
|
||||||
target: String,
|
|
||||||
index: (T) -> String
|
|
||||||
): T? {
|
|
||||||
if (this.isEmpty()) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
var potential: T? = null
|
|
||||||
var rate = 0.0
|
|
||||||
var collide = 0
|
|
||||||
this.forEach {
|
|
||||||
with(index(it).fuzzyCompare(target)) {
|
|
||||||
if (this > rate) {
|
|
||||||
rate = this
|
|
||||||
potential = it
|
|
||||||
}
|
|
||||||
if(this == 1.0){
|
|
||||||
collide++
|
|
||||||
}
|
|
||||||
if(collide > 1){
|
|
||||||
return null//collide
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return potential
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal fun Group.fuzzySearchMember(nameCardTarget: String): Member? {
|
|
||||||
return this.members.fuzzySearchOnly(nameCardTarget) {
|
|
||||||
it.nameCard
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
package net.mamoe.mirai.console.utils
|
|
||||||
|
|
||||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Value
|
|
||||||
* the input type of this Value is T while the output is V
|
|
||||||
*/
|
|
||||||
@MiraiExperimentalAPI
|
|
||||||
abstract class Value<T, V> {
|
|
||||||
operator fun invoke(): V = get()
|
|
||||||
|
|
||||||
abstract fun get(): V
|
|
||||||
|
|
||||||
abstract fun set(t: T)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This value can be used as a Config Value
|
|
||||||
*/
|
|
||||||
@MiraiExperimentalAPI
|
|
||||||
interface ConfigValue
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple value
|
|
||||||
* the input type is same as output value
|
|
||||||
*/
|
|
||||||
|
|
||||||
@MiraiExperimentalAPI
|
|
||||||
open class SimpleValue<T>(
|
|
||||||
var value: T
|
|
||||||
) : Value<T, T>() {
|
|
||||||
override fun get() = this.value
|
|
||||||
|
|
||||||
override fun set(t: T) {
|
|
||||||
this.value = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@MiraiExperimentalAPI
|
|
||||||
open class NullableSimpleValue<T>(
|
|
||||||
value: T? = null
|
|
||||||
) : SimpleValue<T?>(
|
|
||||||
value
|
|
||||||
) {
|
|
||||||
fun isNull() = value == null
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE")
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.utils
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Range
|
||||||
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行 [n] 次 [block], 在第一次成功时返回执行结果, 在捕获到异常时返回异常.
|
||||||
|
*/
|
||||||
|
@kotlin.internal.InlineOnly
|
||||||
|
inline fun <R> retryCatching(n: @Range(from = 1, to = Long.MAX_VALUE) Int, block: () -> R): Result<R> {
|
||||||
|
contract {
|
||||||
|
callsInPlace(block, InvocationKind.AT_LEAST_ONCE)
|
||||||
|
}
|
||||||
|
require(n >= 0) { "param n for retryCatching must not be negative" }
|
||||||
|
var exception: Throwable? = null
|
||||||
|
repeat(n) {
|
||||||
|
try {
|
||||||
|
return Result.success(block())
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
exception?.addSuppressedMirai(e)
|
||||||
|
exception = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.failure(exception!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal fun Throwable.addSuppressedMirai(e: Throwable) {
|
||||||
|
if (e === this) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
kotlin.runCatching {
|
||||||
|
this.addSuppressed(e)
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import net.mamoe.mirai.console.utils.fuzzySearch
|
import net.mamoe.mirai.console.command.fuzzySearch
|
||||||
import net.mamoe.mirai.console.utils.fuzzySearchOnly
|
import net.mamoe.mirai.console.command.fuzzySearchOnly
|
||||||
|
|
||||||
class Him188(val name:String){
|
class Him188(val name:String){
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
|
@ -12,13 +12,13 @@
|
|||||||
package net.mamoe.mirai.console.command
|
package net.mamoe.mirai.console.command
|
||||||
|
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.console.plugins.PluginBase
|
import net.mamoe.mirai.console.plugins.builtin.KotlinPlugin
|
||||||
import net.mamoe.mirai.message.data.*
|
import net.mamoe.mirai.message.data.*
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
|
||||||
val plugin: PluginBase = object : PluginBase() {
|
val plugin: KotlinPlugin = object : KotlinPlugin() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
import org.gradle.api.artifacts.dsl.DependencyHandler
|
|
||||||
import org.gradle.kotlin.dsl.DependencyHandlerScope
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2020 Mamoe Technologies and contributors.
|
* Copyright 2020 Mamoe Technologies and contributors.
|
||||||
*
|
*
|
||||||
@ -12,7 +9,7 @@ import org.gradle.kotlin.dsl.DependencyHandlerScope
|
|||||||
|
|
||||||
object Versions {
|
object Versions {
|
||||||
object Mirai {
|
object Mirai {
|
||||||
const val core = "1.0-RC2-1"
|
const val core = "1.0.0"
|
||||||
const val console = "0.5.1"
|
const val console = "0.5.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"
|
||||||
@ -21,20 +18,8 @@ object Versions {
|
|||||||
|
|
||||||
object Kotlin {
|
object Kotlin {
|
||||||
const val stdlib = "1.3.72"
|
const val stdlib = "1.3.72"
|
||||||
const val coroutines = "1.3.5"
|
const val coroutines = "1.3.7"
|
||||||
const val serialization = "0.20.0"
|
const val serialization = "0.20.0"
|
||||||
const val ktor = "1.3.2"
|
const val ktor = "1.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
fun DependencyHandlerScope.kotlinx(id: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$id:$version"
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
fun DependencyHandlerScope.ktor(id: String, version: String = Versions.Kotlin.ktor) = "io.ktor:ktor-$id:$version"
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
fun DependencyHandler.compileAndRuntime(any: Any) {
|
|
||||||
add("compileOnly", any)
|
|
||||||
add("runtimeOnly", any)
|
|
||||||
}
|
|
14
buildSrc/src/main/kotlin/dependencyExtensions.kt
Normal file
14
buildSrc/src/main/kotlin/dependencyExtensions.kt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||||
|
import org.gradle.kotlin.dsl.DependencyHandlerScope
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun DependencyHandlerScope.kotlinx(id: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$id:$version"
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun DependencyHandlerScope.ktor(id: String, version: String = Versions.Kotlin.ktor) = "io.ktor:ktor-$id:$version"
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun DependencyHandler.compileAndRuntime(any: Any) {
|
||||||
|
add("compileOnly", any)
|
||||||
|
add("runtimeOnly", any)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user