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