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:
jiahua.liu 2020-05-25 00:48:33 +08:00
commit 4190058b6d
55 changed files with 1596 additions and 1638 deletions

View File

@ -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 }}

View File

@ -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 }}

View File

@ -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 图形化界面前端.

View File

@ -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
""" """
) )
} }

View File

@ -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"

View File

@ -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>()
""" """
) )
} }

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
} }
} }

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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
}
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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
}
}*/

View File

@ -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() {}
} }

View File

@ -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 = ""
}

View File

@ -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)
}
} }

View File

@ -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 }

View File

@ -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()

View File

@ -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()
}
}

View File

@ -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() }
}

View File

@ -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)"
}
}

View 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 "
}

View File

@ -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]!!
}
}

View File

@ -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>

View File

@ -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
*/

View File

@ -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

View File

@ -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>()

View File

@ -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

View File

@ -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

View File

@ -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))
}
}

View File

@ -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)
}
} }
} }
} }

View File

@ -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

View File

@ -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)
} }

View File

@ -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
}
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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() {
} }

View File

@ -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)
}

View 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)
}