From 1e4035c7a47f4bb214a99048bd973cf7f3465df7 Mon Sep 17 00:00:00 2001 From: "jiahua.liu" Date: Sun, 17 May 2020 13:30:11 +0800 Subject: [PATCH 01/28] change command structure --- .../main/kotlin/net/mamoe/mirai/console/command/Command.kt | 4 ++++ .../net/mamoe/mirai/console/command/CommandPermission.kt | 6 +----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt index ff438859d..733646b84 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt @@ -97,6 +97,10 @@ abstract class RawCommand(name:String, alias: Array = arrayOf()):Command } + @Command.Permission(CommandPermission.Default::class) + abstract fun onCommand(list: List) + + } /** * 解析完成的指令实际参数列表. 参数顺序与 [Command.descriptor] 的 [CommandDescriptor.params] 相同. diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt index e1eff53c2..86c292c3a 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt @@ -28,7 +28,6 @@ interface CommandPermission { */ fun CommandSender.hasPermission(): Boolean - /** * 满足两个权限其中一个即可使用指令 */ // no extension for Java @@ -39,7 +38,6 @@ interface CommandPermission { */ // no extension for Java infix fun and(another: CommandPermission): CommandPermission = AndCommandPermission(this, another) - /** * 任何人都可以使用这个指令 */ @@ -55,7 +53,7 @@ interface CommandPermission { } /** - * 来自任何 [Bot] 的任何一个管理员或群主都可以使用这个指令 + * 来自任何 [Bot] 的任何一个群管理员或群主都可以使用这个指令 */ object Operator : CommandPermission { override fun CommandSender.hasPermission(): Boolean { @@ -117,10 +115,8 @@ inline fun AnonymousCommandPermission(crossinline block: CommandSender.() -> Boo inline fun CommandSender.hasPermission(permission: CommandPermission): Boolean = permission.run { this@hasPermission.hasPermission() } - inline fun CommandPermission.hasPermission(sender: CommandSender): Boolean = this.run { sender.hasPermission() } - internal class OrCommandPermission( private val first: CommandPermission, private val second: CommandPermission From 35b10ce5c2c8dc94ff9f20dfbba8810d2d74b526 Mon Sep 17 00:00:00 2001 From: "jiahua.liu" Date: Tue, 19 May 2020 14:51:40 +0800 Subject: [PATCH 02/28] to merge --- .../net/mamoe/mirai/console/command/CommandPermission.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt index e8f52b4b4..dc69cfd96 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt @@ -95,7 +95,7 @@ interface CommandPermission { */ object Console : CommandPermission { override fun CommandSender.hasPermission(): Boolean = false - } + object Default : CommandPermission by (Manager or Console) } @@ -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 From 52f5008379841dafe66680878c51ac853aa2ca06 Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 19 May 2020 16:08:25 +0800 Subject: [PATCH 03/28] Fix compilation --- .../mirai/console/codegen/ValueImplCodegen.kt | 7 +- backend/mirai-console/build.gradle.kts | 2 +- .../mamoe/mirai/console/setting/ValueImpl.kt | 40 +++--- .../mamoe/mirai/console/setting/_ValueImpl.kt | 127 ++++++++++-------- 4 files changed, 99 insertions(+), 77 deletions(-) diff --git a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt index 0401e5093..a054f8857 100644 --- a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt +++ b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt @@ -108,7 +108,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 +119,7 @@ fun genAllValueImpl(): String = buildString { } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = ${collectionName}Serializer(${number}.serializer()) @@ -126,7 +127,7 @@ fun genAllValueImpl(): String = buildString { override fun deserialize(decoder: Decoder): Mutable${collectionName}<${number}> { return delegate.deserialize(decoder).toMutable${collectionName}().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } diff --git a/backend/mirai-console/build.gradle.kts b/backend/mirai-console/build.gradle.kts index 60572b5fd..bf46b81c7 100644 --- a/backend/mirai-console/build.gradle.kts +++ b/backend/mirai-console/build.gradle.kts @@ -32,7 +32,7 @@ dependencies { compileAndRuntime("net.mamoe:mirai-core:${Versions.Mirai.core}") compileAndRuntime(kotlin("stdlib")) - api("net.mamoe.yamlkt:yamlkt:0.2.0") + api("net.mamoe.yamlkt:yamlkt:0.3.0") api("org.jsoup:jsoup:1.12.1") diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/ValueImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/ValueImpl.kt index 0331ad96e..10660113f 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/ValueImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/ValueImpl.kt @@ -53,7 +53,10 @@ internal fun Setting.valueImpl( internalValue.shadowMap(transform = { it.value }, transformBack = { valueMapper(it) }) var shadowed: MutableList = updateShadow() - return object : MutableListValue(), MutableList by dynamicMutableList({ shadowed }) { + + + val delegt = dynamicMutableList { shadowed } + return object : MutableListValue(), MutableList by delegt { override var value: MutableList> get() = internalValue set(new) { @@ -86,7 +89,8 @@ internal fun Setting.valueImpl( ): MutableSettingListValue { var internalValue: MutableList = default - return object : MutableSettingListValue(), MutableList by dynamicMutableList({ internalValue }) { + val delegt = dynamicMutableList { internalValue } + return object : MutableSettingListValue(), MutableList by delegt { override var value: MutableList get() = internalValue set(new) { @@ -118,7 +122,8 @@ internal fun Setting.valueImpl( ): SettingListValue { var internalValue: List = default - return object : SettingListValue(), List by dynamicList({ internalValue }) { + val delegt = dynamicList { internalValue } + return object : SettingListValue(), List by delegt { override var value: List get() = internalValue set(new) { @@ -150,7 +155,8 @@ internal fun Setting.valueImpl( ): SettingSetValue { var internalValue: Set = default - return object : SettingSetValue(), Set by dynamicSet({ internalValue }) { + val delegt = dynamicSet { internalValue } + return object : SettingSetValue(), Set by delegt { override var value: Set get() = internalValue set(new) { @@ -182,7 +188,8 @@ internal fun Setting.valueImpl( ): MutableSettingSetValue { var internalValue: MutableSet = default - return object : MutableSettingSetValue(), MutableSet by dynamicMutableSet({ internalValue }) { + val delegt = dynamicMutableSet { internalValue } + return object : MutableSettingSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue set(new) { @@ -247,7 +254,9 @@ internal fun Setting.valueImpl( internalValue.shadowMap(transform = { it.value }, transformBack = { valueMapper(it) }) var shadowed: MutableSet = updateShadow() - return object : MutableSetValue(), MutableSet by dynamicMutableSet({ shadowed }) { + + val delegt = dynamicMutableSet { shadowed } + return object : MutableSetValue(), MutableSet by delegt { override var value: MutableSet> get() = internalValue set(new) { @@ -293,20 +302,15 @@ internal fun Setting.valueImpl(default: T, clazz: KClass): Valu } return object : DynamicReferenceValue() { override var value: T = default - override val serializer: KSerializer - get() = object : KSerializer { - override val descriptor: SerialDescriptor - get() = YamlDynamicSerializer.descriptor + override val serializer: KSerializer = object : KSerializer { + 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) + } } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_ValueImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_ValueImpl.kt index 17ce4184b..8382b8532 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_ValueImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_ValueImpl.kt @@ -7,7 +7,6 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ - package net.mamoe.mirai.console.setting import kotlinx.serialization.Decoder @@ -690,7 +689,8 @@ internal fun Setting.valueImpl( ): MutableIntListValue { var internalValue: MutableList = default - return object : MutableIntListValue(), MutableList by dynamicMutableList({ internalValue }) { + val delegt = dynamicMutableList { internalValue } + return object : MutableIntListValue(), MutableList by delegt { override var value: MutableList get() = internalValue set(new) { @@ -700,7 +700,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Int.serializer()) @@ -708,7 +708,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableList { return delegate.deserialize(decoder).toMutableList().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -725,7 +725,8 @@ internal fun Setting.valueImpl( ): MutableShortListValue { var internalValue: MutableList = default - return object : MutableShortListValue(), MutableList by dynamicMutableList({ internalValue }) { + val delegt = dynamicMutableList { internalValue } + return object : MutableShortListValue(), MutableList by delegt { override var value: MutableList get() = internalValue set(new) { @@ -735,7 +736,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Short.serializer()) @@ -743,7 +744,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableList { return delegate.deserialize(decoder).toMutableList().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -760,7 +761,8 @@ internal fun Setting.valueImpl( ): MutableByteListValue { var internalValue: MutableList = default - return object : MutableByteListValue(), MutableList by dynamicMutableList({ internalValue }) { + val delegt = dynamicMutableList { internalValue } + return object : MutableByteListValue(), MutableList by delegt { override var value: MutableList get() = internalValue set(new) { @@ -770,7 +772,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Byte.serializer()) @@ -778,7 +780,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableList { return delegate.deserialize(decoder).toMutableList().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -795,7 +797,8 @@ internal fun Setting.valueImpl( ): MutableLongListValue { var internalValue: MutableList = default - return object : MutableLongListValue(), MutableList by dynamicMutableList({ internalValue }) { + val delegt = dynamicMutableList { internalValue } + return object : MutableLongListValue(), MutableList by delegt { override var value: MutableList get() = internalValue set(new) { @@ -805,7 +808,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Long.serializer()) @@ -813,7 +816,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableList { return delegate.deserialize(decoder).toMutableList().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -830,7 +833,8 @@ internal fun Setting.valueImpl( ): MutableFloatListValue { var internalValue: MutableList = default - return object : MutableFloatListValue(), MutableList by dynamicMutableList({ internalValue }) { + val delegt = dynamicMutableList { internalValue } + return object : MutableFloatListValue(), MutableList by delegt { override var value: MutableList get() = internalValue set(new) { @@ -840,7 +844,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Float.serializer()) @@ -848,7 +852,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableList { return delegate.deserialize(decoder).toMutableList().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -865,7 +869,8 @@ internal fun Setting.valueImpl( ): MutableDoubleListValue { var internalValue: MutableList = default - return object : MutableDoubleListValue(), MutableList by dynamicMutableList({ internalValue }) { + val delegt = dynamicMutableList { internalValue } + return object : MutableDoubleListValue(), MutableList by delegt { override var value: MutableList get() = internalValue set(new) { @@ -875,7 +880,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Double.serializer()) @@ -883,7 +888,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableList { return delegate.deserialize(decoder).toMutableList().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -900,7 +905,8 @@ internal fun Setting.valueImpl( ): MutableBooleanListValue { var internalValue: MutableList = default - return object : MutableBooleanListValue(), MutableList by dynamicMutableList({ internalValue }) { + val delegt = dynamicMutableList { internalValue } + return object : MutableBooleanListValue(), MutableList by delegt { override var value: MutableList get() = internalValue set(new) { @@ -910,7 +916,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Boolean.serializer()) @@ -918,7 +924,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableList { return delegate.deserialize(decoder).toMutableList().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -935,7 +941,8 @@ internal fun Setting.valueImpl( ): MutableCharListValue { var internalValue: MutableList = default - return object : MutableCharListValue(), MutableList by dynamicMutableList({ internalValue }) { + val delegt = dynamicMutableList { internalValue } + return object : MutableCharListValue(), MutableList by delegt { override var value: MutableList get() = internalValue set(new) { @@ -945,7 +952,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Char.serializer()) @@ -953,7 +960,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableList { return delegate.deserialize(decoder).toMutableList().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -970,7 +977,8 @@ internal fun Setting.valueImpl( ): MutableStringListValue { var internalValue: MutableList = default - return object : MutableStringListValue(), MutableList by dynamicMutableList({ internalValue }) { + val delegt = dynamicMutableList { internalValue } + return object : MutableStringListValue(), MutableList by delegt { override var value: MutableList get() = internalValue set(new) { @@ -980,7 +988,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(String.serializer()) @@ -988,7 +996,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableList { return delegate.deserialize(decoder).toMutableList().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -1005,7 +1013,8 @@ internal fun Setting.valueImpl( ): MutableIntSetValue { var internalValue: MutableSet = default - return object : MutableIntSetValue(), MutableSet by dynamicMutableSet({ internalValue }) { + val delegt = dynamicMutableSet { internalValue } + return object : MutableIntSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue set(new) { @@ -1015,7 +1024,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Int.serializer()) @@ -1023,7 +1032,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableSet { return delegate.deserialize(decoder).toMutableSet().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -1040,7 +1049,8 @@ internal fun Setting.valueImpl( ): MutableShortSetValue { var internalValue: MutableSet = default - return object : MutableShortSetValue(), MutableSet by dynamicMutableSet({ internalValue }) { + val delegt = dynamicMutableSet { internalValue } + return object : MutableShortSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue set(new) { @@ -1050,7 +1060,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Short.serializer()) @@ -1058,7 +1068,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableSet { return delegate.deserialize(decoder).toMutableSet().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -1075,7 +1085,8 @@ internal fun Setting.valueImpl( ): MutableByteSetValue { var internalValue: MutableSet = default - return object : MutableByteSetValue(), MutableSet by dynamicMutableSet({ internalValue }) { + val delegt = dynamicMutableSet { internalValue } + return object : MutableByteSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue set(new) { @@ -1085,7 +1096,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Byte.serializer()) @@ -1093,7 +1104,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableSet { return delegate.deserialize(decoder).toMutableSet().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -1110,7 +1121,8 @@ internal fun Setting.valueImpl( ): MutableLongSetValue { var internalValue: MutableSet = default - return object : MutableLongSetValue(), MutableSet by dynamicMutableSet({ internalValue }) { + val delegt = dynamicMutableSet { internalValue } + return object : MutableLongSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue set(new) { @@ -1120,7 +1132,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Long.serializer()) @@ -1128,7 +1140,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableSet { return delegate.deserialize(decoder).toMutableSet().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -1145,7 +1157,8 @@ internal fun Setting.valueImpl( ): MutableFloatSetValue { var internalValue: MutableSet = default - return object : MutableFloatSetValue(), MutableSet by dynamicMutableSet({ internalValue }) { + val delegt = dynamicMutableSet { internalValue } + return object : MutableFloatSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue set(new) { @@ -1155,7 +1168,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Float.serializer()) @@ -1163,7 +1176,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableSet { return delegate.deserialize(decoder).toMutableSet().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -1180,7 +1193,8 @@ internal fun Setting.valueImpl( ): MutableDoubleSetValue { var internalValue: MutableSet = default - return object : MutableDoubleSetValue(), MutableSet by dynamicMutableSet({ internalValue }) { + val delegt = dynamicMutableSet { internalValue } + return object : MutableDoubleSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue set(new) { @@ -1190,7 +1204,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Double.serializer()) @@ -1198,7 +1212,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableSet { return delegate.deserialize(decoder).toMutableSet().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -1215,7 +1229,8 @@ internal fun Setting.valueImpl( ): MutableBooleanSetValue { var internalValue: MutableSet = default - return object : MutableBooleanSetValue(), MutableSet by dynamicMutableSet({ internalValue }) { + val delegt = dynamicMutableSet { internalValue } + return object : MutableBooleanSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue set(new) { @@ -1225,7 +1240,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Boolean.serializer()) @@ -1233,7 +1248,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableSet { return delegate.deserialize(decoder).toMutableSet().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -1250,7 +1265,8 @@ internal fun Setting.valueImpl( ): MutableCharSetValue { var internalValue: MutableSet = default - return object : MutableCharSetValue(), MutableSet by dynamicMutableSet({ internalValue }) { + val delegt = dynamicMutableSet { internalValue } + return object : MutableCharSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue set(new) { @@ -1260,7 +1276,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Char.serializer()) @@ -1268,7 +1284,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableSet { return delegate.deserialize(decoder).toMutableSet().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } @@ -1285,7 +1301,8 @@ internal fun Setting.valueImpl( ): MutableStringSetValue { var internalValue: MutableSet = default - return object : MutableStringSetValue(), MutableSet by dynamicMutableSet({ internalValue }) { + val delegt = dynamicMutableSet { internalValue } + return object : MutableStringSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue set(new) { @@ -1295,7 +1312,7 @@ internal fun Setting.valueImpl( } } - private inline val `this` get() = this + private val outerThis get() = this override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(String.serializer()) @@ -1303,7 +1320,7 @@ internal fun Setting.valueImpl( override fun deserialize(decoder: Decoder): MutableSet { return delegate.deserialize(decoder).toMutableSet().observable { - onElementChanged(`this`) + onElementChanged(outerThis) } } From 8ddda7d68753dd29b7f94711e0797d71b7ba7b2d Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 19 May 2020 16:09:40 +0800 Subject: [PATCH 04/28] Fix command --- .../kotlin/net/mamoe/mirai/console/command/CommandPermission.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt index dc69cfd96..2023953fa 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandPermission.kt @@ -95,7 +95,7 @@ interface CommandPermission { */ object Console : CommandPermission { override fun CommandSender.hasPermission(): Boolean = false - + } object Default : CommandPermission by (Manager or Console) } From 7a02e10ac553db658158e8e752320550c901fc49 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 20 May 2020 10:08:52 +0800 Subject: [PATCH 05/28] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index de9b23dd3..db4d71a2c 100644 --- a/README.md +++ b/README.md @@ -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 图形化界面前端. From 73d68b3123b06047120f63376d69f3efa6737aff Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 22 May 2020 15:11:09 +0800 Subject: [PATCH 06/28] Fix typo --- .github/workflows/cui.yml | 2 +- .github/workflows/shadow.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cui.yml b/.github/workflows/cui.yml index 0bbcd36eb..eee6c4ecb 100644 --- a/.github/workflows/cui.yml +++ b/.github/workflows/cui.yml @@ -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 }} diff --git a/.github/workflows/shadow.yml b/.github/workflows/shadow.yml index 8dbaf88c1..2277d04cd 100644 --- a/.github/workflows/shadow.yml +++ b/.github/workflows/shadow.yml @@ -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 }} From fe19474c272f73f18b7f446258859d7f82940c8f Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 22 May 2020 19:33:46 +0800 Subject: [PATCH 07/28] Update dependencies --- backend/mirai-console/build.gradle.kts | 2 +- .../main/kotlin/{versions.kt => Versions.kt} | 21 +++---------------- .../src/main/kotlin/dependencyExtensions.kt | 14 +++++++++++++ 3 files changed, 18 insertions(+), 19 deletions(-) rename buildSrc/src/main/kotlin/{versions.kt => Versions.kt} (55%) create mode 100644 buildSrc/src/main/kotlin/dependencyExtensions.kt diff --git a/backend/mirai-console/build.gradle.kts b/backend/mirai-console/build.gradle.kts index bf46b81c7..1eb497fc3 100644 --- a/backend/mirai-console/build.gradle.kts +++ b/backend/mirai-console/build.gradle.kts @@ -32,7 +32,7 @@ dependencies { compileAndRuntime("net.mamoe:mirai-core:${Versions.Mirai.core}") compileAndRuntime(kotlin("stdlib")) - api("net.mamoe.yamlkt:yamlkt:0.3.0") + api("net.mamoe.yamlkt:yamlkt:0.3.1") api("org.jsoup:jsoup:1.12.1") diff --git a/buildSrc/src/main/kotlin/versions.kt b/buildSrc/src/main/kotlin/Versions.kt similarity index 55% rename from buildSrc/src/main/kotlin/versions.kt rename to buildSrc/src/main/kotlin/Versions.kt index 2e1176452..6647de79e 100644 --- a/buildSrc/src/main/kotlin/versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -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) -} +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/dependencyExtensions.kt b/buildSrc/src/main/kotlin/dependencyExtensions.kt new file mode 100644 index 000000000..03bee26e5 --- /dev/null +++ b/buildSrc/src/main/kotlin/dependencyExtensions.kt @@ -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) +} From ef7ea024a06164ba807c1c4d8cee9275bf59ab2a Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 22 May 2020 19:40:37 +0800 Subject: [PATCH 08/28] Fix serializing --- .../mamoe/mirai/console/setting/Internal.kt | 52 ++++++++++++++----- .../mamoe/mirai/console/setting/Setting.kt | 2 +- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Internal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Internal.kt index 46a7b73ef..0be4020e5 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Internal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Internal.kt @@ -10,12 +10,14 @@ package net.mamoe.mirai.console.setting import kotlinx.serialization.* +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.builtins.serializer 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, KProperty<*>>> = mutableListOf() @@ -31,15 +33,15 @@ internal abstract class AbstractSetting { internal val kotlinSerializer: KSerializer by lazy { object : KSerializer { 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) } } } @@ -101,20 +103,42 @@ 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, - value.value - ) + private val emptyList = emptyList() + 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) + } } } } +internal fun CompositeEncoder.encodeElementSmart( + descriptor: SerialDescriptor, + index: Int, + value: Value +) { + 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, value.value) + } +} + internal object SettingSerializerMark internal val KProperty<*>.serialNameOrPropertyName: String get() = this.findAnnotation()?.value ?: this.name diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt index ed4373690..f18fd1fcb 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt @@ -22,7 +22,7 @@ typealias SerialName = kotlinx.serialization.SerialName * 配置的基类. 所有配置必须拥有一个无参构造器, 以用于在 [MutableList] 与 [MutableMap] 中动态识别类型 */ @Suppress("EXPOSED_SUPER_CLASS") -abstract class Setting : AbstractSetting() { +abstract class Setting : SettingImpl() { open val serialName: String get() = this::class.findAnnotation()?.value ?: this::class.qualifiedName From f82d01e606142c9a10c4b67bba8ea62d8229d2cc Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 22 May 2020 19:56:02 +0800 Subject: [PATCH 09/28] Use kotlinx.serialization, remove jsoup dependency --- backend/mirai-console/build.gradle.kts | 2 - .../mirai/console/center/CuiPluginCenter.kt | 99 +++++++------------ .../mirai/console/center/PluginCenter.kt | 21 +++- .../net/mamoe/mirai/console/setting/Value.kt | 16 --- 4 files changed, 53 insertions(+), 85 deletions(-) delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Value.kt diff --git a/backend/mirai-console/build.gradle.kts b/backend/mirai-console/build.gradle.kts index 1eb497fc3..1377bd738 100644 --- a/backend/mirai-console/build.gradle.kts +++ b/backend/mirai-console/build.gradle.kts @@ -34,8 +34,6 @@ dependencies { api("net.mamoe.yamlkt:yamlkt:0.3.1") - api("org.jsoup:jsoup:1.12.1") - api("org.jetbrains:annotations:19.0.0") testApi("net.mamoe:mirai-core-qqandroid:${Versions.Mirai.core}") diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/CuiPluginCenter.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/CuiPluginCenter.kt index 60b041c17..1c9c6ee23 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/CuiPluginCenter.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/CuiPluginCenter.kt @@ -1,89 +1,73 @@ +@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? = null /** - * 一页10个吧,pageMinNum=1 + * 一页 10 个 pageMinNum=1 */ override suspend fun fetchPlugin(page: Int): Map { check(page > 0) val startIndex = (page - 1) * 10 val endIndex = startIndex + 9 val map = mutableMapOf() - (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("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("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 + ) + + 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.downloadPlugin(name: String, progressListener: T.(Float) -> Unit): File { @@ -112,15 +96,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() = "崔云" } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/PluginCenter.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/PluginCenter.kt index 4456b7f34..26c4f63a7 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/PluginCenter.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/PluginCenter.kt @@ -1,13 +1,20 @@ 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 +22,13 @@ interface PluginCenter { val commands: List ) + @Serializable data class PluginInfo( val name: String, val version: String, + @SerialName("core") val coreVersion: String, + @SerialName("console") val consoleVersion: String, val tags: List, val author: String, @@ -32,7 +42,7 @@ interface PluginCenter { /** * 获取一些中心的插件基本信息, - * 能获取到多少由实际的PluginCenter决定 + * 能获取到多少由实际的 [PluginCenter] 决定 * 返回 插件名->Insight */ suspend fun fetchPlugin(page: Int): Map @@ -41,17 +51,18 @@ interface PluginCenter { * 尝试获取到某个插件 by 全名, case sensitive * null 则没有 */ - suspend fun findPlugin(name:String):PluginInfo? + suspend fun findPlugin(name: String): PluginInfo? - suspend fun T.downloadPlugin(name:String, progressListener:T.(Float) -> Unit): File + suspend fun T.downloadPlugin(name: String, progressListener: T.(Float) -> Unit): File - suspend fun downloadPlugin(name:String, progressListener:PluginCenter.(Float) -> Unit): File = downloadPlugin(name,progressListener) + suspend fun downloadPlugin(name: String, progressListener: PluginCenter.(Float) -> Unit): File = + downloadPlugin(name, progressListener) /** * 刷新 */ suspend fun refresh() - val name:String + val name: String } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Value.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Value.kt deleted file mode 100644 index 989ece08b..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Value.kt +++ /dev/null @@ -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 - */ From c87e8b07df9635333f70ac36a00a47e319bff8c3 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 22 May 2020 20:04:23 +0800 Subject: [PATCH 10/28] Add docs --- backend/mirai-console/build.gradle.kts | 1 + .../net/mamoe/mirai/console/setting/Setting.kt | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/backend/mirai-console/build.gradle.kts b/backend/mirai-console/build.gradle.kts index 1377bd738..57f14a79f 100644 --- a/backend/mirai-console/build.gradle.kts +++ b/backend/mirai-console/build.gradle.kts @@ -21,6 +21,7 @@ kotlin { languageSettings.useExperimentalAnnotation("kotlin.OptIn") languageSettings.progressiveMode = true languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI") + languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiExperimentalAPI") languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes") languageSettings.useExperimentalAnnotation("kotlin.experimental.ExperimentalTypeInference") languageSettings.useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts") diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt index f18fd1fcb..6282aa90b 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt @@ -12,23 +12,39 @@ package net.mamoe.mirai.console.setting import kotlinx.serialization.KSerializer +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 : SettingImpl() { + /** + * 这个配置的名称, 仅对于顶层配置有效. + */ + @MiraiExperimentalAPI open val serialName: String get() = this::class.findAnnotation()?.value ?: this::class.qualifiedName ?: error("Names should be assigned to anonymous classes manually by overriding serialName") + /** + * 提供属性委托, 并添加这个对象的自动保存跟踪. + */ @JvmSynthetic operator fun Value.provideDelegate( thisRef: Setting, From 6619c0b05acff1d9b8c83b7707d2988dec29c1e2 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 22 May 2020 20:07:47 +0800 Subject: [PATCH 11/28] Rearrange implementations --- .../mamoe/mirai/console/codegen/ValueImplCodegen.kt | 2 +- .../net/mamoe/mirai/console/codegen/ValuesCodegen.kt | 2 +- .../kotlin/net/mamoe/mirai/console/setting/Setting.kt | 1 + .../kotlin/net/mamoe/mirai/console/setting/_Setting.kt | 2 ++ .../mirai/console/setting/{ => internal}/_ValueImpl.kt | 8 ++++++-- .../setting/{utils.kt => internal/collectionUtil.kt} | 2 +- .../setting/{Internal.kt => internal/internal.kt} | 10 ++++++++-- .../{ValueImpl.kt => internal/internalValueImpl.kt} | 3 ++- 8 files changed, 22 insertions(+), 8 deletions(-) rename backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/{ => internal}/_ValueImpl.kt (99%) rename backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/{utils.kt => internal/collectionUtil.kt} (99%) rename backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/{Internal.kt => internal/internal.kt} (94%) rename backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/{ValueImpl.kt => internal/internalValueImpl.kt} (99%) diff --git a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt index a054f8857..d7f912fb5 100644 --- a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt +++ b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt @@ -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) diff --git a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt index 3b3d095c9..35cdf17b0 100644 --- a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt +++ b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt @@ -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 */ diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt index 6282aa90b..5aeb0f2e3 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt @@ -12,6 +12,7 @@ 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 diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt index 2f4b80a48..32966a2a3 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt @@ -10,6 +10,8 @@ package net.mamoe.mirai.console.setting +import net.mamoe.mirai.console.setting.internal.valueImpl + /** * !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.SettingValueUseSiteCodegen.kt * !!! DO NOT MODIFY THIS FILE MANUALLY diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_ValueImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt similarity index 99% rename from backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_ValueImpl.kt rename to backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt index 8382b8532..ded01b9ef 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_ValueImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt @@ -7,13 +7,14 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ -package net.mamoe.mirai.console.setting +package net.mamoe.mirai.console.setting.internal import kotlinx.serialization.Decoder import kotlinx.serialization.Encoder import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialDescriptor import kotlinx.serialization.builtins.* +import net.mamoe.mirai.console.setting.* /** @@ -1353,7 +1354,10 @@ internal fun Setting.valueImpl(default: T): Value { } override fun serialize(encoder: Encoder, value: T) { - internalValue.updaterSerializer.serialize(encoder, SettingSerializerMark) + internalValue.updaterSerializer.serialize( + encoder, + SettingSerializerMark + ) } } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/utils.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/collectionUtil.kt similarity index 99% rename from backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/utils.kt rename to backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/collectionUtil.kt index 0f069db89..c686d2fdb 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/utils.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/collectionUtil.kt @@ -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 diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Internal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internal.kt similarity index 94% rename from backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Internal.kt rename to backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internal.kt index 0be4020e5..84a9c1b72 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Internal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internal.kt @@ -7,11 +7,14 @@ * 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.yamlkt.Yaml import net.mamoe.yamlkt.YamlConfiguration import kotlin.reflect.KProperty @@ -41,7 +44,10 @@ internal abstract class SettingImpl { } override fun serialize(encoder: Encoder, value: Setting) { - this@SettingImpl.updaterSerializer.serialize(encoder, SettingSerializerMark) + this@SettingImpl.updaterSerializer.serialize( + encoder, + SettingSerializerMark + ) } } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/ValueImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internalValueImpl.kt similarity index 99% rename from backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/ValueImpl.kt rename to backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internalValueImpl.kt index 10660113f..23d9e4efb 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/ValueImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internalValueImpl.kt @@ -7,11 +7,12 @@ * 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.SetSerializer +import net.mamoe.mirai.console.setting.* import net.mamoe.yamlkt.YamlDynamicSerializer import kotlin.reflect.KClass import kotlin.reflect.full.createInstance From f10d35db8f125c759e26ddfe7fd6c64fabd2177b Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 22 May 2020 20:24:17 +0800 Subject: [PATCH 12/28] Support dynamic referenced values --- .../codegen/SettingValueUseSiteCodegen.kt | 34 ++++++++++++++++--- .../mamoe/mirai/console/setting/_Setting.kt | 23 +++++++++++++ .../setting/internal/internalValueImpl.kt | 3 ++ 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/SettingValueUseSiteCodegen.kt b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/SettingValueUseSiteCodegen.kt index 00aa5a915..87af74d6a 100644 --- a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/SettingValueUseSiteCodegen.kt +++ b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/SettingValueUseSiteCodegen.kt @@ -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 Setting.value(default: MutableSet): MutableSettingSetValue = valueImpl(default) + + /** + * 创建一个只引用对象而不跟踪其属性的值. + * + * @param T 类型. 必须拥有 [kotlinx.serialization.Serializable] 注解 (因此编译器会自动生成序列化器) + */ + @DangerousReferenceOnlyValue + @JvmName("valueDynamic") + @LowPriorityInOverloadResolution + inline fun Setting.value(default: T): Value = valueImpl(default) + + @RequiresOptIn( + ""${'"'} + 这种只保存引用的 Value 可能会导致意料之外的结果, 在使用时须保持谨慎. + 对值的改变不会触发自动保存, 也不会同步到 UI 中. 在 UI 中只能编辑序列化之后的值. + ""${'"'}, level = RequiresOptIn.Level.WARNING + ) + @Retention(AnnotationRetention.BINARY) + @Target(AnnotationTarget.FUNCTION) + annotation class DangerousReferenceOnlyValue """ ) } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt index 32966a2a3..1fd951369 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt @@ -7,10 +7,13 @@ * 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 @@ -138,3 +141,23 @@ inline fun Setting.value(default: Set): SettingSetValue @JvmName("valueMutable") inline fun Setting.value(default: MutableSet): MutableSettingSetValue = valueImpl(default) +/** + * 创建一个只引用对象而不跟踪其属性的值. + * + * @param T 类型. 必须拥有 [kotlinx.serialization.Serializable] 注解 (因此编译器会自动生成序列化器) + */ +@DangerousReferenceOnlyValue +@JvmName("valueDynamic") +@LowPriorityInOverloadResolution +inline fun Setting.value(default: T): Value = valueImpl(default) + +@RequiresOptIn( + """ + 这种只保存引用的 Value 可能会导致意料之外的结果, 在使用时须保持谨慎. + 对值的改变不会触发自动保存, 也不会同步到 UI 中. 在 UI 中只能编辑序列化之后的值. +""", level = RequiresOptIn.Level.WARNING +) +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.FUNCTION) +annotation class DangerousReferenceOnlyValue + diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internalValueImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internalValueImpl.kt index 23d9e4efb..0942f9fdd 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internalValueImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internalValueImpl.kt @@ -6,6 +6,7 @@ * * https://github.com/mamoe/mirai/blob/master/LICENSE */ +@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") package net.mamoe.mirai.console.setting.internal @@ -14,6 +15,7 @@ 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 @@ -290,6 +292,7 @@ internal fun Setting.valueImpl( * For primitives and serializable only */ @PublishedApi +@LowPriorityInOverloadResolution internal inline fun Setting.valueImpl(default: T): Value = valueImpl(default, T::class) From f363a7d4b07463e4852d48190fb32508b64240b7 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 22 May 2020 20:39:33 +0800 Subject: [PATCH 13/28] Support generic List and Set implementations --- .../mirai/console/codegen/ValueImplCodegen.kt | 54 +++++++++++-- .../mirai/console/codegen/ValuesCodegen.kt | 17 ++-- .../net/mamoe/mirai/console/setting/_Value.kt | 20 ++--- .../console/setting/internal/_ValueImpl.kt | 77 +++++++++---------- 4 files changed, 104 insertions(+), 64 deletions(-) diff --git a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt index d7f912fb5..d518c32cd 100644 --- a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt +++ b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt @@ -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())", @@ -178,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() { @@ -206,3 +213,38 @@ 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 + return object : ${miraiValueName}Value(), $kotlinTypeName by dynamic$collectionName({ internalValue }) { + 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" + diff --git a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt index 35cdf17b0..4da6e25c1 100644 --- a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt +++ b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt @@ -157,13 +157,14 @@ sealed class Value : ReadWriteProperty { 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 : ReadWriteProperty { 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 : ReadWriteProperty { appendln( """ - sealed class ${collectionName}Value : Value<${collectionName}>(), Iterable{ - override fun iterator() = this.value.iterator() - } + sealed class ${collectionName}Value : Value<${collectionName}>(), ${collectionName} """ ) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Value.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Value.kt index 1aa6600b5..6dc6f62be 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Value.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Value.kt @@ -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 : ArrayValue() abstract class IntArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { override fun iterator(): Iterator = this.value.iterator() } + abstract class ShortArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { override fun iterator(): Iterator = this.value.iterator() } + abstract class ByteArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { override fun iterator(): Iterator = this.value.iterator() } + abstract class LongArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { override fun iterator(): Iterator = this.value.iterator() } + abstract class FloatArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { override fun iterator(): Iterator = this.value.iterator() } + abstract class DoubleArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { override fun iterator(): Iterator = this.value.iterator() } + abstract class BooleanArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { override fun iterator(): Iterator = this.value.iterator() } + abstract class CharArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { override fun iterator(): Iterator = this.value.iterator() } + sealed class TypedPrimitiveArrayValue : ArrayValue>(), Iterable { override fun iterator() = this.value.iterator() } @@ -98,10 +106,7 @@ abstract class TypedBooleanArrayValue internal constructor() : TypedPrimitiveArr abstract class TypedCharArrayValue internal constructor() : TypedPrimitiveArrayValue() abstract class TypedStringArrayValue internal constructor() : TypedPrimitiveArrayValue() -sealed class ListValue : Value>(), Iterable { - override fun iterator() = this.value.iterator() -} - +sealed class ListValue : Value>(), List abstract class IntListValue internal constructor() : ListValue() abstract class ShortListValue internal constructor() : ListValue() abstract class ByteListValue internal constructor() : ListValue() @@ -114,10 +119,7 @@ abstract class StringListValue internal constructor() : ListValue() abstract class SettingListValue internal constructor() : Value>(), List -sealed class SetValue : Value>(), Iterable { - override fun iterator() = this.value.iterator() -} - +sealed class SetValue : Value>(), Set abstract class IntSetValue internal constructor() : SetValue() abstract class ShortSetValue internal constructor() : SetValue() abstract class ByteSetValue internal constructor() : SetValue() diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt index ded01b9ef..983ee212e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt @@ -414,8 +414,8 @@ internal fun Setting.valueImpl(default: Array): TypedStringArrayValue { } internal fun Setting.valueImpl(default: List): IntListValue { - return object : IntListValue() { - private var internalValue: List = default + var internalValue: List = default + return object : IntListValue(), List by dynamicList({ internalValue }) { override var value: List get() = internalValue set(new) { @@ -429,8 +429,8 @@ internal fun Setting.valueImpl(default: List): IntListValue { } internal fun Setting.valueImpl(default: List): ShortListValue { - return object : ShortListValue() { - private var internalValue: List = default + var internalValue: List = default + return object : ShortListValue(), List by dynamicList({ internalValue }) { override var value: List get() = internalValue set(new) { @@ -444,8 +444,8 @@ internal fun Setting.valueImpl(default: List): ShortListValue { } internal fun Setting.valueImpl(default: List): ByteListValue { - return object : ByteListValue() { - private var internalValue: List = default + var internalValue: List = default + return object : ByteListValue(), List by dynamicList({ internalValue }) { override var value: List get() = internalValue set(new) { @@ -459,8 +459,8 @@ internal fun Setting.valueImpl(default: List): ByteListValue { } internal fun Setting.valueImpl(default: List): LongListValue { - return object : LongListValue() { - private var internalValue: List = default + var internalValue: List = default + return object : LongListValue(), List by dynamicList({ internalValue }) { override var value: List get() = internalValue set(new) { @@ -474,8 +474,8 @@ internal fun Setting.valueImpl(default: List): LongListValue { } internal fun Setting.valueImpl(default: List): FloatListValue { - return object : FloatListValue() { - private var internalValue: List = default + var internalValue: List = default + return object : FloatListValue(), List by dynamicList({ internalValue }) { override var value: List get() = internalValue set(new) { @@ -489,8 +489,8 @@ internal fun Setting.valueImpl(default: List): FloatListValue { } internal fun Setting.valueImpl(default: List): DoubleListValue { - return object : DoubleListValue() { - private var internalValue: List = default + var internalValue: List = default + return object : DoubleListValue(), List by dynamicList({ internalValue }) { override var value: List get() = internalValue set(new) { @@ -504,8 +504,8 @@ internal fun Setting.valueImpl(default: List): DoubleListValue { } internal fun Setting.valueImpl(default: List): BooleanListValue { - return object : BooleanListValue() { - private var internalValue: List = default + var internalValue: List = default + return object : BooleanListValue(), List by dynamicList({ internalValue }) { override var value: List get() = internalValue set(new) { @@ -519,8 +519,8 @@ internal fun Setting.valueImpl(default: List): BooleanListValue { } internal fun Setting.valueImpl(default: List): CharListValue { - return object : CharListValue() { - private var internalValue: List = default + var internalValue: List = default + return object : CharListValue(), List by dynamicList({ internalValue }) { override var value: List get() = internalValue set(new) { @@ -534,8 +534,8 @@ internal fun Setting.valueImpl(default: List): CharListValue { } internal fun Setting.valueImpl(default: List): StringListValue { - return object : StringListValue() { - private var internalValue: List = default + var internalValue: List = default + return object : StringListValue(), List by dynamicList({ internalValue }) { override var value: List get() = internalValue set(new) { @@ -549,8 +549,8 @@ internal fun Setting.valueImpl(default: List): StringListValue { } internal fun Setting.valueImpl(default: Set): IntSetValue { - return object : IntSetValue() { - private var internalValue: Set = default + var internalValue: Set = default + return object : IntSetValue(), Set by dynamicSet({ internalValue }) { override var value: Set get() = internalValue set(new) { @@ -564,8 +564,8 @@ internal fun Setting.valueImpl(default: Set): IntSetValue { } internal fun Setting.valueImpl(default: Set): ShortSetValue { - return object : ShortSetValue() { - private var internalValue: Set = default + var internalValue: Set = default + return object : ShortSetValue(), Set by dynamicSet({ internalValue }) { override var value: Set get() = internalValue set(new) { @@ -579,8 +579,8 @@ internal fun Setting.valueImpl(default: Set): ShortSetValue { } internal fun Setting.valueImpl(default: Set): ByteSetValue { - return object : ByteSetValue() { - private var internalValue: Set = default + var internalValue: Set = default + return object : ByteSetValue(), Set by dynamicSet({ internalValue }) { override var value: Set get() = internalValue set(new) { @@ -594,8 +594,8 @@ internal fun Setting.valueImpl(default: Set): ByteSetValue { } internal fun Setting.valueImpl(default: Set): LongSetValue { - return object : LongSetValue() { - private var internalValue: Set = default + var internalValue: Set = default + return object : LongSetValue(), Set by dynamicSet({ internalValue }) { override var value: Set get() = internalValue set(new) { @@ -609,8 +609,8 @@ internal fun Setting.valueImpl(default: Set): LongSetValue { } internal fun Setting.valueImpl(default: Set): FloatSetValue { - return object : FloatSetValue() { - private var internalValue: Set = default + var internalValue: Set = default + return object : FloatSetValue(), Set by dynamicSet({ internalValue }) { override var value: Set get() = internalValue set(new) { @@ -624,8 +624,8 @@ internal fun Setting.valueImpl(default: Set): FloatSetValue { } internal fun Setting.valueImpl(default: Set): DoubleSetValue { - return object : DoubleSetValue() { - private var internalValue: Set = default + var internalValue: Set = default + return object : DoubleSetValue(), Set by dynamicSet({ internalValue }) { override var value: Set get() = internalValue set(new) { @@ -639,8 +639,8 @@ internal fun Setting.valueImpl(default: Set): DoubleSetValue { } internal fun Setting.valueImpl(default: Set): BooleanSetValue { - return object : BooleanSetValue() { - private var internalValue: Set = default + var internalValue: Set = default + return object : BooleanSetValue(), Set by dynamicSet({ internalValue }) { override var value: Set get() = internalValue set(new) { @@ -654,8 +654,8 @@ internal fun Setting.valueImpl(default: Set): BooleanSetValue { } internal fun Setting.valueImpl(default: Set): CharSetValue { - return object : CharSetValue() { - private var internalValue: Set = default + var internalValue: Set = default + return object : CharSetValue(), Set by dynamicSet({ internalValue }) { override var value: Set get() = internalValue set(new) { @@ -669,8 +669,8 @@ internal fun Setting.valueImpl(default: Set): CharSetValue { } internal fun Setting.valueImpl(default: Set): StringSetValue { - return object : StringSetValue() { - private var internalValue: Set = default + var internalValue: Set = default + return object : StringSetValue(), Set by dynamicSet({ internalValue }) { override var value: Set get() = internalValue set(new) { @@ -1354,10 +1354,7 @@ internal fun Setting.valueImpl(default: T): Value { } override fun serialize(encoder: Encoder, value: T) { - internalValue.updaterSerializer.serialize( - encoder, - SettingSerializerMark - ) + internalValue.updaterSerializer.serialize(encoder, SettingSerializerMark) } } } From 912b2e34645b6375d610b588e89b517524767406 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 22 May 2020 20:43:37 +0800 Subject: [PATCH 14/28] Fix value updating --- .../kotlin/net/mamoe/mirai/console/setting/Setting.kt | 2 +- .../mamoe/mirai/console/setting/internal/internal.kt | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt index 5aeb0f2e3..87c75855e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt @@ -56,7 +56,7 @@ abstract class Setting : SettingImpl() { return this } - override fun toString(): String = yaml.stringify(this.serializer, this) + override fun toString(): String = yamlForToString.stringify(this.serializer, this) } @Suppress("UNCHECKED_CAST") diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internal.kt index 84a9c1b72..92d22dd46 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internal.kt @@ -15,6 +15,7 @@ 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 @@ -58,7 +59,7 @@ internal abstract class SettingImpl { companion object { @JvmStatic - internal val yaml = + internal val yamlForToString = Yaml( configuration = YamlConfiguration( nonStrictNullability = true, @@ -75,6 +76,7 @@ internal class SettingUpdaterSerializer( private val instance: Setting ) : KSerializer { 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) @@ -97,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 + value.value = this.decodeSerializableElement( descriptor, index, value.serializer @@ -125,6 +126,7 @@ internal class SettingUpdaterSerializer( } +// until https://github.com/Him188/yamlkt/issues/2 fixed internal fun CompositeEncoder.encodeElementSmart( descriptor: SerialDescriptor, index: Int, From 4bbf6549a7849e4854a170ba16df663ae83296b6 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 22 May 2020 20:46:42 +0800 Subject: [PATCH 15/28] Give DynamicReferenceValue a public constructor for custom extending --- .../kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt | 2 +- .../src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt index 4da6e25c1..018f7242e 100644 --- a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt +++ b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt @@ -264,7 +264,7 @@ sealed class Value : ReadWriteProperty { * 只引用这个对象, 而不跟踪其成员. * 仅适用于基础类型, 用于 mutable list/map 等情况; 或标注了 [Serializable] 的类. */ - abstract class DynamicReferenceValue internal constructor() : Value() + abstract class DynamicReferenceValue : Value() """ ) } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt index 87c75855e..0c42bc83d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt @@ -59,6 +59,9 @@ abstract class Setting : SettingImpl() { override fun toString(): String = yamlForToString.stringify(this.serializer, this) } +/** + * 用于更新或保存这个 [Value] 的序列化器. + */ @Suppress("UNCHECKED_CAST") val T.serializer: KSerializer get() = kotlinSerializer as KSerializer From 4de8a6b01cd46b70ca0948b7bc1d930fd0de6724 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 22 May 2020 20:47:40 +0800 Subject: [PATCH 16/28] Fix build --- .../src/main/kotlin/net/mamoe/mirai/console/command/Command.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt index f9b568763..87e5f775e 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt @@ -15,7 +15,6 @@ import net.mamoe.mirai.console.command.description.CommandParam 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.console.plugins.MyArg import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.SingleMessage import kotlin.reflect.KAnnotatedElement @@ -104,7 +103,7 @@ abstract class CompositeCommand @JvmOverloads constructor( this::class.declaredFunctions.filter { it.hasAnnotation() }.map { function -> SubCommandDescriptor( arrayOf(function.name), - arrayOf(CommandParam("p", MyArg::class)), + arrayOf(), "", CommandPermission.Default, onCommand = block { sender: CommandSender, args: Array -> From 9c18c04466bc724f528e1c3fa7447e4755b453f2 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 23 May 2020 17:44:34 +0800 Subject: [PATCH 17/28] Implement PluginLoaders and PluginManager --- .../mirai/console/codegen/ValueImplCodegen.kt | 3 +- backend/mirai-console/build.gradle.kts | 81 +++---- .../net/mamoe/mirai/console/MiraiConsole.kt | 34 ++- .../{utils => }/MiraiConsoleFrontEnd.kt | 2 +- .../description/CommandArgParserBuiltins.kt | 6 +- .../mamoe/mirai/console/command/internal.kt | 95 ++++++++ .../plugins/{JarPlugin.kt => JvmPlugin.kt} | 117 ++++++++-- .../net/mamoe/mirai/console/plugins/Plugin.kt | 95 +++++--- .../mirai/console/plugins/PluginLoader.kt | 83 +++++-- .../mirai/console/plugins/PluginManager.kt | 176 ++++++++++++++- .../console/setting/internal/_ValueImpl.kt | 205 ++++++++++-------- .../mamoe/mirai/console/utils/BotHelper.kt | 7 - .../net/mamoe/mirai/console/utils/Utils.kt | 161 -------------- .../net/mamoe/mirai/console/utils/Value.kt | 50 ----- .../mirai/console/utils/retryCatching.kt | 38 ++++ .../src/test/kotlin/StringFuzzyTest.kt | 4 +- 16 files changed, 708 insertions(+), 449 deletions(-) rename backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/{utils => }/MiraiConsoleFrontEnd.kt (97%) rename backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/{JarPlugin.kt => JvmPlugin.kt} (79%) delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/Utils.kt delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/Value.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/retryCatching.kt diff --git a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt index d518c32cd..d09d147c8 100644 --- a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt +++ b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt @@ -224,7 +224,8 @@ fun genCollectionValueImpl( """ internal fun Setting.valueImpl(default: ${kotlinTypeName}): ${miraiValueName}Value { var internalValue: $kotlinTypeName = default - return object : ${miraiValueName}Value(), $kotlinTypeName by dynamic$collectionName({ internalValue }) { + val delegt = dynamic$collectionName { internalValue } + return object : ${miraiValueName}Value(), $kotlinTypeName by delegt { override var value: $kotlinTypeName get() = internalValue set(new) { diff --git a/backend/mirai-console/build.gradle.kts b/backend/mirai-console/build.gradle.kts index 57f14a79f..4fc131443 100644 --- a/backend/mirai-console/build.gradle.kts +++ b/backend/mirai-console/build.gradle.kts @@ -1,4 +1,3 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import upload.Bintray import java.util.* @@ -12,19 +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("net.mamoe.mirai.utils.MiraiExperimentalAPI") - 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" + } } } } @@ -34,36 +60,15 @@ dependencies { compileAndRuntime(kotlin("stdlib")) api("net.mamoe.yamlkt:yamlkt:0.3.1") - api("org.jetbrains:annotations:19.0.0") 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 { @@ -128,4 +133,6 @@ if (Bintray.isBintrayAvailable(project)) { } } } -} else println("bintray isn't available. NO PUBLICATIONS WILL BE SET") \ No newline at end of file +} else println("bintray isn't available. NO PUBLICATIONS WILL BE SET") + +// endregion \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt index a69a4eb6a..46cfcef00 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt @@ -13,33 +13,40 @@ 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.JarPluginLoader +import net.mamoe.mirai.console.plugins.PluginLoader 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 { +internal interface IMiraiConsole : CoroutineScope { val build: String val version: String /** - * Console运行路径 + * Console 运行路径 */ - val path: String + val rootDir: File /** - * Console前端接口 + * Console 前端接口 */ val frontEnd: MiraiConsoleFrontEnd /** - * 与前端交互所使用的Logger + * 与前端交互所使用的 Logger */ val mainLogger: MiraiLogger + + /** + * 内建加载器列表, 一般需要包含 [JarPluginLoader] + */ + val builtInPluginLoaders: List> } object MiraiConsole : CoroutineScope, IMiraiConsole { @@ -52,19 +59,19 @@ 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> = 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 +79,9 @@ object MiraiConsole : CoroutineScope, IMiraiConsole { fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity) } +/** + * Included in kotlin stdlib 1.4 + */ internal val Throwable.stacktraceString: String get() = ByteArrayOutputStream().apply { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/MiraiConsoleFrontEnd.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleFrontEnd.kt similarity index 97% rename from backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/MiraiConsoleFrontEnd.kt rename to backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleFrontEnd.kt index f34db8ff3..f94a8eaea 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/MiraiConsoleFrontEnd.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsoleFrontEnd.kt @@ -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 diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgParserBuiltins.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgParserBuiltins.kt index 01f2cdf92..8399965b2 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgParserBuiltins.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgParserBuiltins.kt @@ -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 diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/internal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/internal.kt index 1594fd98c..e88f5a486 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/internal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/internal.kt @@ -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 Array.intersects(other: Array): 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 Collection.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 Collection.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 + } } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JarPlugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JvmPlugin.kt similarity index 79% rename from backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JarPlugin.kt rename to backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JvmPlugin.kt index 83c2cd4e7..6cc2c9552 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JarPlugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JvmPlugin.kt @@ -6,50 +6,133 @@ * * https://github.com/mamoe/mirai/blob/master/LICENSE */ -@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS") package net.mamoe.mirai.console.plugins +import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient +import net.mamoe.mirai.console.MiraiConsole +import net.mamoe.mirai.utils.MiraiLogger +import java.io.File +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext -sealed class JarPlugin : Plugin(), CoroutineScope { - internal lateinit var _description: JarPluginDescription +interface JvmPlugin : Plugin, CoroutineScope { + val logger: MiraiLogger + val description: JvmPluginDescription - final override val description: PluginDescription get() = _description - final override val loader: JarPluginLoader get() = JarPluginLoader + @JvmDefault + fun onLoad() { + } + + @JvmDefault + fun onEnable() { + } + + @JvmDefault + fun onDisable() { + } +} + + +abstract class JavaPlugin @JvmOverloads constructor( + coroutineContext: CoroutineContext = EmptyCoroutineContext +) : JvmPlugin, JvmPluginImpl(coroutineContext) { + // TODO: 2020/5/23 scheduler, event listener(?) +} + +abstract class KotlinPlugin @JvmOverloads constructor( + coroutineContext: CoroutineContext = EmptyCoroutineContext +) : JvmPlugin, JvmPluginImpl(coroutineContext) { + // that's it } @Serializable -internal class JarPluginDescription( +data class JvmPluginDescription internal constructor( // serializer 可以用这个构造器 + override val kind: PluginKind, override val name: String, override val author: String, override val version: String, override val info: String, - override val depends: List -) : PluginDescription + override val loadBefore: List, + override val dependencies: List +) : PluginDescription, FilePluginDescription { + /** + * 在手动实现时使用这个构造器. + */ + @Suppress("unused") + constructor( + kind: PluginKind, + name: String, + author: String, + version: String, + info: String, + loadBefore: List, + depends: List, + file: File + ) : this(kind, name, author, version, info, loadBefore, depends) { + this._file = file + } -abstract class JavaPlugin : JarPlugin() + @Suppress("PropertyName") + @Transient + internal var _file: File? = null -abstract class KotlinPlugin : JarPlugin() + override val file: File + get() = _file ?: error("Internal error: JvmPluginDescription(name=$name)._file == null") +} +internal abstract class JvmPluginImpl( + parentCoroutineContext: CoroutineContext +) : JvmPlugin, CoroutineScope { + /** + * Initialized immediately after construction of [JvmPluginImpl] instance + */ + @Suppress("PropertyName") + internal lateinit var _description: JvmPluginDescription + override val description: JvmPluginDescription get() = _description + + final override val logger: MiraiLogger by lazy { MiraiConsole.newLogger(this._description.name) } + + final override val coroutineContext: CoroutineContext by lazy { + SupervisorJob(parentCoroutineContext[Job]) + CoroutineExceptionHandler { _, throwable -> + logger.error(throwable) + } + } +} /** * 内建的 Jar (JVM) 插件加载器 */ -object JarPluginLoader : PluginLoader { - override val list: List - get() = TODO("Not yet implemented") +object JarPluginLoader : AbstractFilePluginLoader("jar") { + override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description - override fun load(plugin: JarPlugin) { - TODO("Not yet implemented") + override fun Sequence.mapToDescription(): List { + TODO( + """ + CHECK IS JAR FILE AND CAN BE READ + READ JAR FILE, EXTRACT PLUGIN DESCRIPTION + SET JvmPluginDescription._file + RETURN PLUGIN + """.trimIndent() + ) } - override fun enable(plugin: JarPlugin) { - TODO("Not yet implemented") + @Throws(PluginLoadException::class) + override fun load(description: JvmPluginDescription): JvmPlugin { + TODO("FIND PLUGIN MAIN, THEN LOAD") + // no need to check dependencies } + + override fun enable(plugin: JvmPlugin) = plugin.onEnable() + override fun disable(plugin: JvmPlugin) = plugin.onDisable() } /* diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/Plugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/Plugin.kt index f967bff79..f0df37871 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/Plugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/Plugin.kt @@ -9,45 +9,82 @@ 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 kotlinx.serialization.Serializable +import java.io.File + +/** 插件类型 */ +enum class PluginKind { + /** 表示此插件提供一个 [PluginLoader], 应在加载其他 [NORMAL] 类型插件前加载 */ + LOADER, + + /** 表示此插件为一个通常的插件, 按照正常的依赖关系加载. */ + NORMAL +} /** - * 插件信息 + * 插件描述 */ interface PluginDescription { + val kind: PluginKind + val name: String val author: String val version: String val info: String - val depends: List + + /** 指定此插件需要在这些插件之前加载 */ + val loadBefore: List + + /** 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件 */ + val dependencies: List +} + +/** 插件的一个依赖的信息 */ +@Serializable +data class PluginDependency( + /** 依赖插件名 */ + val name: String, + /** + * 依赖版本号 + * @see versionKind 版本号类型 + */ + val version: String, + /** 版本号类型 */ + val versionKind: VersionKind +) { + enum class VersionKind { + /** 要求依赖精确的版本 */ + EXACT, + + /** 要求依赖最低版本 */ + AT_LEAST, + + /** 要求依赖最高版本 */ + AT_MOST + } + + override fun toString(): String { + return "$name ${versionKind.toEnglishString()}v$version" + } +} + + +internal fun PluginDependency.VersionKind.toEnglishString(): String = when (this) { + PluginDependency.VersionKind.EXACT -> "" + PluginDependency.VersionKind.AT_LEAST -> "at least " + PluginDependency.VersionKind.AT_MOST -> "at most " } /** - * 插件基类. - * - * 内建的插件类型: - * - [JarPlugin] + * 基于文件的插件的描述 */ -abstract class Plugin : CoroutineScope { - abstract val description: PluginDescription - abstract val loader: PluginLoader<*> +interface FilePluginDescription : PluginDescription { + val file: File +} - @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() {} -} \ No newline at end of file +/** + * 表示一个 mirai-console 插件. + * + * @see JvmPlugin + */ +interface Plugin \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginLoader.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginLoader.kt index 144b9b21d..acd31a025 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginLoader.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginLoader.kt @@ -1,29 +1,68 @@ +@file:Suppress("unused") + package net.mamoe.mirai.console.plugins +import net.mamoe.mirai.console.MiraiConsole +import java.io.File + /** - * 插件加载器 + * 插件加载器. * + * 插件加载器只实现寻找插件列表, 加载插件, 启用插件, 关闭插件这四个功能. + * + * 有关插件的依赖和已加载的插件列表由 [PluginManager] 维护. + */ +interface PluginLoader

{ + /** + * 扫描并返回可以被加载的插件的 [描述][PluginDescription] 列表. 此函数只会被调用一次 + */ + fun listPlugins(): List + + /** + * 获取此插件的描述 + */ + @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

{ - val list: List

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

: PluginLoader { + /** + * 所支持的插件文件后缀, 不含 '.'. 如 [JarPluginLoader] 为 "jar" + */ + val fileSuffix: String } + +abstract class AbstractFilePluginLoader

( + override val fileSuffix: String +) : FilePluginLoader { + private fun pluginsFilesSequence(): Sequence = + PluginManager.pluginsDir.walk().filter { it.isFile && it.name.endsWith(fileSuffix, ignoreCase = true) } + + protected abstract fun Sequence.mapToDescription(): List + + final override fun listPlugins(): List = pluginsFilesSequence().mapToDescription() +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt index 5b01f905c..d2386f4d8 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt @@ -1,24 +1,180 @@ +@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.* +import java.util.concurrent.locks.ReentrantLock +import kotlin.collections.ArrayList + +val Plugin.description: PluginDescription get() = TODO() +val

P.loader: PluginLoader get() = TODO() + +inline fun PluginLoader<*, *>.register() = PluginManager.registerPluginLoader(this) +inline fun PluginLoader<*, *>.unregister() = PluginManager.unregisterPluginLoader(this) object PluginManager { - private val _loaders: MutableSet> = mutableSetOf() + val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() } - val loaders: Set> get() = _loaders + class LoaderNode

( + val loader: PluginLoader, + val loadedPlugins: MutableList

= mutableListOf() + ) - fun registerPluginLoader(loader: PluginLoader<*>) { - _loaders.add(loader) + private val _pluginLoaders: MutableSet> = mutableSetOf() + private val loadersLock: ReentrantLock = ReentrantLock() + + private val resolvedPlugins: LinkedList = LinkedList() + + @JvmStatic + val plugins: List + get() = _pluginLoaders.flatMap { it.loadedPlugins } + + /** + * 内建的插件加载器列表. 由 [MiraiConsole] 初始化 + */ + @JvmStatic + val builtInLoaders: List> + get() = MiraiConsole.builtInPluginLoaders + + /** + * 由插件创建的 [PluginLoader] + */ + val pluginLoaders: List> get() = _pluginLoaders.map { it.loader } + + @JvmStatic + fun registerPluginLoader(loader: PluginLoader<*, *>): Boolean = loadersLock.withLock { + _pluginLoaders.add(LoaderNode(loader)) } - fun unregisterPluginLoader(loader: PluginLoader<*>) { - _loaders.remove(loader) + @JvmStatic + fun unregisterPluginLoader(loader: PluginLoader<*, *>) = loadersLock.withLock { + _pluginLoaders.removeAll { it.loader == loader } } - fun loadPlugins() { - loaders.forEach(PluginLoader<*>::loadAll) + + // region LOADING + + private fun

PluginLoader.loadPluginNoEnable(description: D): P { + return this.load(description).also { resolvedPlugins.add(it) } } - fun enablePlugins() { - loaders.forEach(PluginLoader<*>::enableAll) + private fun

PluginLoader.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 { + val allDescriptions = + this.builtInLoaders.listAllPlugins() + .asSequence() + .onEach { (loader, descriptions) -> + loader as PluginLoader + + for (it in descriptions.filter { it.kind == PluginKind.LOADER }.sortByDependencies()) { + loader.loadPluginAndEnable(it) + } + } + .flatMap { it.second.asSequence() } + + return allDescriptions.toList() + } + + private fun List>.listAllPlugins(): List, List>> { + return associateWith { loader -> loader.listPlugins().map { desc -> desc.wrapWith(loader) } }.toList() + } + + @Throws(PluginMissingDependencyException::class) + private fun List.sortByDependencies(): List { + val resolved = ArrayList(this.size) + + fun D.canBeLoad(): Boolean = this.dependencies.all { it in resolved } + + fun List.consumeLoadable(): List { + val (canBeLoad, cannotBeLoad) = this.partition { it.canBeLoad() } + resolved.addAll(canBeLoad) + return cannotBeLoad + } + + fun List.filterIsMissing(): List = this.filterNot { it in resolved } + + tailrec fun List.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 +} + +internal data class PluginDescriptionWithLoader( + @JvmField val loader: PluginLoader<*, PluginDescription>, // easier type + @JvmField val delegate: PluginDescription +) : PluginDescription by delegate + +@Suppress("UNCHECKED_CAST") +internal fun 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.contains(dependency: PluginDependency): Boolean = + any { it.name == dependency.name } + +class PluginMissingDependencyException : Exception { + 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) } \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt index 983ee212e..d43cfc582 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt @@ -9,10 +9,7 @@ package net.mamoe.mirai.console.setting.internal -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.* @@ -415,7 +412,8 @@ internal fun Setting.valueImpl(default: Array): TypedStringArrayValue { internal fun Setting.valueImpl(default: List): IntListValue { var internalValue: List = default - return object : IntListValue(), List by dynamicList({ internalValue }) { + val delegt = dynamicList { internalValue } + return object : IntListValue(), List by delegt { override var value: List get() = internalValue set(new) { @@ -430,7 +428,8 @@ internal fun Setting.valueImpl(default: List): IntListValue { internal fun Setting.valueImpl(default: List): ShortListValue { var internalValue: List = default - return object : ShortListValue(), List by dynamicList({ internalValue }) { + val delegt = dynamicList { internalValue } + return object : ShortListValue(), List by delegt { override var value: List get() = internalValue set(new) { @@ -445,7 +444,8 @@ internal fun Setting.valueImpl(default: List): ShortListValue { internal fun Setting.valueImpl(default: List): ByteListValue { var internalValue: List = default - return object : ByteListValue(), List by dynamicList({ internalValue }) { + val delegt = dynamicList { internalValue } + return object : ByteListValue(), List by delegt { override var value: List get() = internalValue set(new) { @@ -460,7 +460,8 @@ internal fun Setting.valueImpl(default: List): ByteListValue { internal fun Setting.valueImpl(default: List): LongListValue { var internalValue: List = default - return object : LongListValue(), List by dynamicList({ internalValue }) { + val delegt = dynamicList { internalValue } + return object : LongListValue(), List by delegt { override var value: List get() = internalValue set(new) { @@ -475,7 +476,8 @@ internal fun Setting.valueImpl(default: List): LongListValue { internal fun Setting.valueImpl(default: List): FloatListValue { var internalValue: List = default - return object : FloatListValue(), List by dynamicList({ internalValue }) { + val delegt = dynamicList { internalValue } + return object : FloatListValue(), List by delegt { override var value: List get() = internalValue set(new) { @@ -490,7 +492,8 @@ internal fun Setting.valueImpl(default: List): FloatListValue { internal fun Setting.valueImpl(default: List): DoubleListValue { var internalValue: List = default - return object : DoubleListValue(), List by dynamicList({ internalValue }) { + val delegt = dynamicList { internalValue } + return object : DoubleListValue(), List by delegt { override var value: List get() = internalValue set(new) { @@ -505,7 +508,8 @@ internal fun Setting.valueImpl(default: List): DoubleListValue { internal fun Setting.valueImpl(default: List): BooleanListValue { var internalValue: List = default - return object : BooleanListValue(), List by dynamicList({ internalValue }) { + val delegt = dynamicList { internalValue } + return object : BooleanListValue(), List by delegt { override var value: List get() = internalValue set(new) { @@ -520,7 +524,8 @@ internal fun Setting.valueImpl(default: List): BooleanListValue { internal fun Setting.valueImpl(default: List): CharListValue { var internalValue: List = default - return object : CharListValue(), List by dynamicList({ internalValue }) { + val delegt = dynamicList { internalValue } + return object : CharListValue(), List by delegt { override var value: List get() = internalValue set(new) { @@ -535,7 +540,8 @@ internal fun Setting.valueImpl(default: List): CharListValue { internal fun Setting.valueImpl(default: List): StringListValue { var internalValue: List = default - return object : StringListValue(), List by dynamicList({ internalValue }) { + val delegt = dynamicList { internalValue } + return object : StringListValue(), List by delegt { override var value: List get() = internalValue set(new) { @@ -550,7 +556,8 @@ internal fun Setting.valueImpl(default: List): StringListValue { internal fun Setting.valueImpl(default: Set): IntSetValue { var internalValue: Set = default - return object : IntSetValue(), Set by dynamicSet({ internalValue }) { + val delegt = dynamicSet { internalValue } + return object : IntSetValue(), Set by delegt { override var value: Set get() = internalValue set(new) { @@ -565,7 +572,8 @@ internal fun Setting.valueImpl(default: Set): IntSetValue { internal fun Setting.valueImpl(default: Set): ShortSetValue { var internalValue: Set = default - return object : ShortSetValue(), Set by dynamicSet({ internalValue }) { + val delegt = dynamicSet { internalValue } + return object : ShortSetValue(), Set by delegt { override var value: Set get() = internalValue set(new) { @@ -580,7 +588,8 @@ internal fun Setting.valueImpl(default: Set): ShortSetValue { internal fun Setting.valueImpl(default: Set): ByteSetValue { var internalValue: Set = default - return object : ByteSetValue(), Set by dynamicSet({ internalValue }) { + val delegt = dynamicSet { internalValue } + return object : ByteSetValue(), Set by delegt { override var value: Set get() = internalValue set(new) { @@ -595,7 +604,8 @@ internal fun Setting.valueImpl(default: Set): ByteSetValue { internal fun Setting.valueImpl(default: Set): LongSetValue { var internalValue: Set = default - return object : LongSetValue(), Set by dynamicSet({ internalValue }) { + val delegt = dynamicSet { internalValue } + return object : LongSetValue(), Set by delegt { override var value: Set get() = internalValue set(new) { @@ -610,7 +620,8 @@ internal fun Setting.valueImpl(default: Set): LongSetValue { internal fun Setting.valueImpl(default: Set): FloatSetValue { var internalValue: Set = default - return object : FloatSetValue(), Set by dynamicSet({ internalValue }) { + val delegt = dynamicSet { internalValue } + return object : FloatSetValue(), Set by delegt { override var value: Set get() = internalValue set(new) { @@ -625,7 +636,8 @@ internal fun Setting.valueImpl(default: Set): FloatSetValue { internal fun Setting.valueImpl(default: Set): DoubleSetValue { var internalValue: Set = default - return object : DoubleSetValue(), Set by dynamicSet({ internalValue }) { + val delegt = dynamicSet { internalValue } + return object : DoubleSetValue(), Set by delegt { override var value: Set get() = internalValue set(new) { @@ -640,7 +652,8 @@ internal fun Setting.valueImpl(default: Set): DoubleSetValue { internal fun Setting.valueImpl(default: Set): BooleanSetValue { var internalValue: Set = default - return object : BooleanSetValue(), Set by dynamicSet({ internalValue }) { + val delegt = dynamicSet { internalValue } + return object : BooleanSetValue(), Set by delegt { override var value: Set get() = internalValue set(new) { @@ -655,7 +668,8 @@ internal fun Setting.valueImpl(default: Set): BooleanSetValue { internal fun Setting.valueImpl(default: Set): CharSetValue { var internalValue: Set = default - return object : CharSetValue(), Set by dynamicSet({ internalValue }) { + val delegt = dynamicSet { internalValue } + return object : CharSetValue(), Set by delegt { override var value: Set get() = internalValue set(new) { @@ -670,7 +684,8 @@ internal fun Setting.valueImpl(default: Set): CharSetValue { internal fun Setting.valueImpl(default: Set): StringSetValue { var internalValue: Set = default - return object : StringSetValue(), Set by dynamicSet({ internalValue }) { + val delegt = dynamicSet { internalValue } + return object : StringSetValue(), Set by delegt { override var value: Set get() = internalValue set(new) { @@ -690,7 +705,7 @@ internal fun Setting.valueImpl( ): MutableIntListValue { var internalValue: MutableList = default - val delegt = dynamicMutableList { internalValue } + val delegt = dynamicMutableList{ internalValue } return object : MutableIntListValue(), MutableList by delegt { override var value: MutableList get() = internalValue @@ -700,15 +715,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Int.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableList { - return delegate.deserialize(decoder).toMutableList().observable { + return delegate.deserialize(decoder).toMutableList().observable { onElementChanged(outerThis) } } @@ -726,7 +741,7 @@ internal fun Setting.valueImpl( ): MutableShortListValue { var internalValue: MutableList = default - val delegt = dynamicMutableList { internalValue } + val delegt = dynamicMutableList{ internalValue } return object : MutableShortListValue(), MutableList by delegt { override var value: MutableList get() = internalValue @@ -736,15 +751,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Short.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableList { - return delegate.deserialize(decoder).toMutableList().observable { + return delegate.deserialize(decoder).toMutableList().observable { onElementChanged(outerThis) } } @@ -762,7 +777,7 @@ internal fun Setting.valueImpl( ): MutableByteListValue { var internalValue: MutableList = default - val delegt = dynamicMutableList { internalValue } + val delegt = dynamicMutableList{ internalValue } return object : MutableByteListValue(), MutableList by delegt { override var value: MutableList get() = internalValue @@ -772,15 +787,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Byte.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableList { - return delegate.deserialize(decoder).toMutableList().observable { + return delegate.deserialize(decoder).toMutableList().observable { onElementChanged(outerThis) } } @@ -798,7 +813,7 @@ internal fun Setting.valueImpl( ): MutableLongListValue { var internalValue: MutableList = default - val delegt = dynamicMutableList { internalValue } + val delegt = dynamicMutableList{ internalValue } return object : MutableLongListValue(), MutableList by delegt { override var value: MutableList get() = internalValue @@ -808,15 +823,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Long.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableList { - return delegate.deserialize(decoder).toMutableList().observable { + return delegate.deserialize(decoder).toMutableList().observable { onElementChanged(outerThis) } } @@ -834,7 +849,7 @@ internal fun Setting.valueImpl( ): MutableFloatListValue { var internalValue: MutableList = default - val delegt = dynamicMutableList { internalValue } + val delegt = dynamicMutableList{ internalValue } return object : MutableFloatListValue(), MutableList by delegt { override var value: MutableList get() = internalValue @@ -844,15 +859,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Float.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableList { - return delegate.deserialize(decoder).toMutableList().observable { + return delegate.deserialize(decoder).toMutableList().observable { onElementChanged(outerThis) } } @@ -870,7 +885,7 @@ internal fun Setting.valueImpl( ): MutableDoubleListValue { var internalValue: MutableList = default - val delegt = dynamicMutableList { internalValue } + val delegt = dynamicMutableList{ internalValue } return object : MutableDoubleListValue(), MutableList by delegt { override var value: MutableList get() = internalValue @@ -880,15 +895,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Double.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableList { - return delegate.deserialize(decoder).toMutableList().observable { + return delegate.deserialize(decoder).toMutableList().observable { onElementChanged(outerThis) } } @@ -906,7 +921,7 @@ internal fun Setting.valueImpl( ): MutableBooleanListValue { var internalValue: MutableList = default - val delegt = dynamicMutableList { internalValue } + val delegt = dynamicMutableList{ internalValue } return object : MutableBooleanListValue(), MutableList by delegt { override var value: MutableList get() = internalValue @@ -916,15 +931,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Boolean.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableList { - return delegate.deserialize(decoder).toMutableList().observable { + return delegate.deserialize(decoder).toMutableList().observable { onElementChanged(outerThis) } } @@ -942,7 +957,7 @@ internal fun Setting.valueImpl( ): MutableCharListValue { var internalValue: MutableList = default - val delegt = dynamicMutableList { internalValue } + val delegt = dynamicMutableList{ internalValue } return object : MutableCharListValue(), MutableList by delegt { override var value: MutableList get() = internalValue @@ -952,15 +967,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(Char.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableList { - return delegate.deserialize(decoder).toMutableList().observable { + return delegate.deserialize(decoder).toMutableList().observable { onElementChanged(outerThis) } } @@ -978,7 +993,7 @@ internal fun Setting.valueImpl( ): MutableStringListValue { var internalValue: MutableList = default - val delegt = dynamicMutableList { internalValue } + val delegt = dynamicMutableList{ internalValue } return object : MutableStringListValue(), MutableList by delegt { override var value: MutableList get() = internalValue @@ -988,15 +1003,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = ListSerializer(String.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableList { - return delegate.deserialize(decoder).toMutableList().observable { + return delegate.deserialize(decoder).toMutableList().observable { onElementChanged(outerThis) } } @@ -1014,7 +1029,7 @@ internal fun Setting.valueImpl( ): MutableIntSetValue { var internalValue: MutableSet = default - val delegt = dynamicMutableSet { internalValue } + val delegt = dynamicMutableSet{ internalValue } return object : MutableIntSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue @@ -1024,15 +1039,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Int.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableSet { - return delegate.deserialize(decoder).toMutableSet().observable { + return delegate.deserialize(decoder).toMutableSet().observable { onElementChanged(outerThis) } } @@ -1050,7 +1065,7 @@ internal fun Setting.valueImpl( ): MutableShortSetValue { var internalValue: MutableSet = default - val delegt = dynamicMutableSet { internalValue } + val delegt = dynamicMutableSet{ internalValue } return object : MutableShortSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue @@ -1060,15 +1075,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Short.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableSet { - return delegate.deserialize(decoder).toMutableSet().observable { + return delegate.deserialize(decoder).toMutableSet().observable { onElementChanged(outerThis) } } @@ -1086,7 +1101,7 @@ internal fun Setting.valueImpl( ): MutableByteSetValue { var internalValue: MutableSet = default - val delegt = dynamicMutableSet { internalValue } + val delegt = dynamicMutableSet{ internalValue } return object : MutableByteSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue @@ -1096,15 +1111,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Byte.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableSet { - return delegate.deserialize(decoder).toMutableSet().observable { + return delegate.deserialize(decoder).toMutableSet().observable { onElementChanged(outerThis) } } @@ -1122,7 +1137,7 @@ internal fun Setting.valueImpl( ): MutableLongSetValue { var internalValue: MutableSet = default - val delegt = dynamicMutableSet { internalValue } + val delegt = dynamicMutableSet{ internalValue } return object : MutableLongSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue @@ -1132,15 +1147,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Long.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableSet { - return delegate.deserialize(decoder).toMutableSet().observable { + return delegate.deserialize(decoder).toMutableSet().observable { onElementChanged(outerThis) } } @@ -1158,7 +1173,7 @@ internal fun Setting.valueImpl( ): MutableFloatSetValue { var internalValue: MutableSet = default - val delegt = dynamicMutableSet { internalValue } + val delegt = dynamicMutableSet{ internalValue } return object : MutableFloatSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue @@ -1168,15 +1183,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Float.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableSet { - return delegate.deserialize(decoder).toMutableSet().observable { + return delegate.deserialize(decoder).toMutableSet().observable { onElementChanged(outerThis) } } @@ -1194,7 +1209,7 @@ internal fun Setting.valueImpl( ): MutableDoubleSetValue { var internalValue: MutableSet = default - val delegt = dynamicMutableSet { internalValue } + val delegt = dynamicMutableSet{ internalValue } return object : MutableDoubleSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue @@ -1204,15 +1219,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Double.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableSet { - return delegate.deserialize(decoder).toMutableSet().observable { + return delegate.deserialize(decoder).toMutableSet().observable { onElementChanged(outerThis) } } @@ -1230,7 +1245,7 @@ internal fun Setting.valueImpl( ): MutableBooleanSetValue { var internalValue: MutableSet = default - val delegt = dynamicMutableSet { internalValue } + val delegt = dynamicMutableSet{ internalValue } return object : MutableBooleanSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue @@ -1240,15 +1255,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Boolean.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableSet { - return delegate.deserialize(decoder).toMutableSet().observable { + return delegate.deserialize(decoder).toMutableSet().observable { onElementChanged(outerThis) } } @@ -1266,7 +1281,7 @@ internal fun Setting.valueImpl( ): MutableCharSetValue { var internalValue: MutableSet = default - val delegt = dynamicMutableSet { internalValue } + val delegt = dynamicMutableSet{ internalValue } return object : MutableCharSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue @@ -1276,15 +1291,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(Char.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableSet { - return delegate.deserialize(decoder).toMutableSet().observable { + return delegate.deserialize(decoder).toMutableSet().observable { onElementChanged(outerThis) } } @@ -1302,7 +1317,7 @@ internal fun Setting.valueImpl( ): MutableStringSetValue { var internalValue: MutableSet = default - val delegt = dynamicMutableSet { internalValue } + val delegt = dynamicMutableSet{ internalValue } return object : MutableStringSetValue(), MutableSet by delegt { override var value: MutableSet get() = internalValue @@ -1312,15 +1327,15 @@ internal fun Setting.valueImpl( onElementChanged(this) } } - + private val outerThis get() = this - + override val serializer: KSerializer> = object : KSerializer> { private val delegate = SetSerializer(String.serializer()) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MutableSet { - return delegate.deserialize(decoder).toMutableSet().observable { + return delegate.deserialize(decoder).toMutableSet().observable { onElementChanged(outerThis) } } @@ -1344,7 +1359,7 @@ internal fun Setting.valueImpl(default: T): Value { onElementChanged(this) } } - override val serializer = object : KSerializer { + override val serializer = object : KSerializer{ override val descriptor: SerialDescriptor get() = internalValue.updaterSerializer.descriptor diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/BotHelper.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/BotHelper.kt index 2746a1672..5bef2c575 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/BotHelper.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/BotHelper.kt @@ -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 diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/Utils.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/Utils.kt deleted file mode 100644 index 5562465be..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/Utils.kt +++ /dev/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 retryCatching(n: Int, block: () -> R): Result { - 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 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 Collection.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 Collection.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 - } -} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/Value.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/Value.kt deleted file mode 100644 index 85a3ba822..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/Value.kt +++ /dev/null @@ -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 { - 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( - var value: T -) : Value() { - override fun get() = this.value - - override fun set(t: T) { - this.value = t - } -} - -@MiraiExperimentalAPI -open class NullableSimpleValue( - value: T? = null -) : SimpleValue( - value -) { - fun isNull() = value == null -} - diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/retryCatching.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/retryCatching.kt new file mode 100644 index 000000000..a8c425921 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/retryCatching.kt @@ -0,0 +1,38 @@ +@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 retryCatching(n: @Range(from = 1, to = Long.MAX_VALUE) Int, block: () -> R): Result { + 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) + } +} \ No newline at end of file diff --git a/backend/mirai-console/src/test/kotlin/StringFuzzyTest.kt b/backend/mirai-console/src/test/kotlin/StringFuzzyTest.kt index 58177caeb..d9259738c 100644 --- a/backend/mirai-console/src/test/kotlin/StringFuzzyTest.kt +++ b/backend/mirai-console/src/test/kotlin/StringFuzzyTest.kt @@ -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 { From 156c9e9cffae4f7aa74ab20c47e5359904cdf5e0 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 23 May 2020 17:47:31 +0800 Subject: [PATCH 18/28] Cleanup old classes --- .../mamoe/mirai/console/plugins/PluginBase.kt | 149 ------------------ .../mirai/console/plugins/PluginManagerOld.kt | 15 -- 2 files changed, 164 deletions(-) delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManagerOld.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt deleted file mode 100644 index d83a35559..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt +++ /dev/null @@ -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) { - - } - - /** - * 插件的日志 - */ - 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 = "" -} diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManagerOld.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManagerOld.kt deleted file mode 100644 index ade4a15c6..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManagerOld.kt +++ /dev/null @@ -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() - From a861b73cc7fee77552aad8a2b11e41c0a30ef6dc Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 23 May 2020 17:53:01 +0800 Subject: [PATCH 19/28] Implement Java specific scheduler; better coroutine exception handling --- .../mirai/console/event/EventListener.java | 51 ------------------- .../net/mamoe/mirai/console/event/Events.java | 30 ----------- .../mamoe/mirai/console/event/EventsImpl.kt | 32 ------------ .../mamoe/mirai/console/plugins/JvmPlugin.kt | 13 +++-- 4 files changed, 9 insertions(+), 117 deletions(-) delete mode 100644 backend/mirai-console/src/main/java/net/mamoe/mirai/console/event/EventListener.java delete mode 100644 backend/mirai-console/src/main/java/net/mamoe/mirai/console/event/Events.java delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/event/EventsImpl.kt diff --git a/backend/mirai-console/src/main/java/net/mamoe/mirai/console/event/EventListener.java b/backend/mirai-console/src/main/java/net/mamoe/mirai/console/event/EventListener.java deleted file mode 100644 index fbc6be9cb..000000000 --- a/backend/mirai-console/src/main/java/net/mamoe/mirai/console/event/EventListener.java +++ /dev/null @@ -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 事件类型 - * @return 事件监听器. 可调用 {@link Listener#complete()} 或 {@link Listener#completeExceptionally(Throwable)} 让监听正常停止或异常停止. - */ - @NotNull - public Listener subscribe(@NotNull Class eventClass, @NotNull Function onEvent) { - return EventsImplKt.subscribeEventForJaptOnly(eventClass, base, onEvent); - } - - - /** - * 监听一个事件, 直到手动停止. - * 机器人离线后不会停止监听. - * - * @param eventClass 事件类 - * @param onEvent 事件处理. 返回 {@link ListeningStatus#LISTENING} 时继续监听. - * @param 事件类型 - * @return 事件监听器. 可调用 {@link Listener#complete()} 或 {@link Listener#completeExceptionally(Throwable)} 让监听正常停止或异常停止. - */ - @NotNull - public Listener subscribeAlways(@NotNull Class eventClass, @NotNull Consumer onEvent) { - return EventsImplKt.subscribeEventForJaptOnly(eventClass, base, onEvent); - } - -} diff --git a/backend/mirai-console/src/main/java/net/mamoe/mirai/console/event/Events.java b/backend/mirai-console/src/main/java/net/mamoe/mirai/console/event/Events.java deleted file mode 100644 index 6dcdd0e2a..000000000 --- a/backend/mirai-console/src/main/java/net/mamoe/mirai/console/event/Events.java +++ /dev/null @@ -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 事件类型 - * @return {@code event} 本身 - */ - @NotNull - public static E broadcast(@NotNull E event) { - return EventsImplKt.broadcast(event); - } -} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/event/EventsImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/event/EventsImpl.kt deleted file mode 100644 index 738088acb..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/event/EventsImpl.kt +++ /dev/null @@ -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 broadcast(e: E): E = runBlocking { e.broadcast() } - -internal fun Class.subscribeEventForJaptOnly( - scope: CoroutineScope, - onEvent: Function -): Listener = _subscribeEventForJaptOnly(scope, onEvent) - -internal fun Class.subscribeEventForJaptOnly(scope: CoroutineScope, onEvent: Consumer): Listener = - _subscribeEventForJaptOnly(scope, onEvent) \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JvmPlugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JvmPlugin.kt index 6cc2c9552..0ed7eae34 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JvmPlugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JvmPlugin.kt @@ -18,6 +18,7 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import net.mamoe.mirai.console.MiraiConsole +import net.mamoe.mirai.console.scheduler.PluginScheduler import net.mamoe.mirai.utils.MiraiLogger import java.io.File import kotlin.coroutines.CoroutineContext @@ -45,7 +46,11 @@ interface JvmPlugin : Plugin, CoroutineScope { abstract class JavaPlugin @JvmOverloads constructor( coroutineContext: CoroutineContext = EmptyCoroutineContext ) : JvmPlugin, JvmPluginImpl(coroutineContext) { - // TODO: 2020/5/23 scheduler, event listener(?) + + /** + * Java API Scheduler + */ + val scheduler: PluginScheduler? = PluginScheduler(this.coroutineContext) } abstract class KotlinPlugin @JvmOverloads constructor( @@ -102,9 +107,9 @@ internal abstract class JvmPluginImpl( final override val logger: MiraiLogger by lazy { MiraiConsole.newLogger(this._description.name) } final override val coroutineContext: CoroutineContext by lazy { - SupervisorJob(parentCoroutineContext[Job]) + CoroutineExceptionHandler { _, throwable -> - logger.error(throwable) - } + CoroutineExceptionHandler { _, throwable -> logger.error(throwable) } + .plus(parentCoroutineContext) + .plus(SupervisorJob(parentCoroutineContext[Job])) } } From d37d6881fc15bfe8d5b460d4cb34b5617ab22afc Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 23 May 2020 18:45:56 +0800 Subject: [PATCH 20/28] Improve JavaPluginScheduler --- .../mirai/console/scheduler/SchedulerTask.kt | 149 ------------------ .../console/utils/JavaPluginScheduler.kt | 86 ++++++++++ 2 files changed, 86 insertions(+), 149 deletions(-) delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/scheduler/SchedulerTask.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/JavaPluginScheduler.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/scheduler/SchedulerTask.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/scheduler/SchedulerTask.kt deleted file mode 100644 index c6a1e996f..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/scheduler/SchedulerTask.kt +++ /dev/null @@ -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 async(supplier: Supplier): Future { - 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( - private val deferred: Deferred -) : Future { - - 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() - - private val mutex = Mutex() - - fun getPluginScheduler(pluginBase: PluginBase): PluginScheduler { - runBlocking { - mutex.withLock { - if (!schedulerTaskManagerInstance.containsKey(pluginBase)) { - schedulerTaskManagerInstance[pluginBase] = PluginScheduler(pluginBase.coroutineContext) - } - } - } - return schedulerTaskManagerInstance[pluginBase]!! - } -} - diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/JavaPluginScheduler.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/JavaPluginScheduler.kt new file mode 100644 index 000000000..826759aef --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/JavaPluginScheduler.kt @@ -0,0 +1,86 @@ +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 { + return this.future { + while (isActive) { + withContext(Dispatchers.IO) { runnable.run() } + delay(intervalMs) + } + null + } + } + + /** + * 新增一个 Delayed Task (延迟任务) + * + * 在延迟 [delayMillis] 后执行 [runnable] + */ + fun delayed(delayMillis: Long, runnable: Runnable): CompletableFuture { + return future { + delay(delayMillis) + withContext(Dispatchers.IO) { + runnable.run() + } + null + } + } + + /** + * 新增一个 Delayed Task (延迟任务) + * + * 在延迟 [delayMillis] 后执行 [runnable] + */ + fun delayed(delayMillis: Long, runnable: Callable): CompletableFuture { + return future { + delay(delayMillis) + withContext(Dispatchers.IO) { runnable.call() } + null + } + } + + /** + * 异步执行一个任务, 最终返回 [Future], 与 Java 使用方法无异, 但效率更高且可以在插件关闭时停止 + */ + fun async(supplier: Callable): Future { + return future { + withContext(Dispatchers.IO) { supplier.call() } + } + } + + /** + * 异步执行一个任务, 没有返回 + */ + fun async(runnable: Runnable): Future { + return future { + withContext(Dispatchers.IO) { runnable.run() } + null + } + } +} \ No newline at end of file From 11bdb438eb3028e9bef555743de071584cc3b792 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 23 May 2020 18:46:33 +0800 Subject: [PATCH 21/28] Rearrange JvmPlugin implementations --- .../plugins/builtin/JarPluginLoader.kt | 83 +++++++++++++++++ .../plugins/{ => builtin}/JvmPlugin.kt | 93 +++++-------------- .../plugins/builtin/JvmPluginDescription.kt | 48 ++++++++++ .../mirai/console/plugins/description.kt | 84 +++++++++++++++++ 4 files changed, 238 insertions(+), 70 deletions(-) create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt rename backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/{ => builtin}/JvmPlugin.kt (87%) create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPluginDescription.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/description.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt new file mode 100644 index 000000000..bac4a2fe2 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt @@ -0,0 +1,83 @@ +package net.mamoe.mirai.console.plugins.builtin + +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.AbstractFilePluginLoader +import net.mamoe.mirai.console.plugins.PluginLoadException +import net.mamoe.mirai.console.plugins.PluginsLoader +import net.mamoe.mirai.utils.MiraiLogger +import java.io.File +import kotlin.coroutines.CoroutineContext +import kotlin.reflect.full.createInstance + +/** + * 内建的 Jar (JVM) 插件加载器 + */ +object JarPluginLoader : AbstractFilePluginLoader("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 classLoader: PluginsLoader = + PluginsLoader(this.javaClass.classLoader) + + override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description + + override fun Sequence.mapToDescription(): List { + TODO( + """ + CHECK IS JAR FILE AND CAN BE READ + READ JAR FILE, EXTRACT PLUGIN DESCRIPTION + SET JvmPluginDescription._file + RETURN PLUGIN + """.trimIndent() + ) + } + + @Throws(PluginLoadException::class) + override fun load(description: JvmPluginDescription): JvmPlugin = description.runCatching { + 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 + } + + TODO( + """ + FIND PLUGIN MAIN, THEN LOAD + SET JvmPluginImpl._description + SET JvmPluginImpl._intrinsicCoroutineContext + """.trimIndent() + ) + // no need to check dependencies + }.getOrElse { + throw PluginLoadException( + "Exception while loading ${description.name}", + it + ) + } + + override fun enable(plugin: JvmPlugin) = plugin.onEnable() + override fun disable(plugin: JvmPlugin) = plugin.onDisable() +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JvmPlugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt similarity index 87% rename from backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JvmPlugin.kt rename to backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt index 0ed7eae34..7adbd6940 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/JvmPlugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt @@ -9,24 +9,31 @@ @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS") -package net.mamoe.mirai.console.plugins +package net.mamoe.mirai.console.plugins.builtin import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob -import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient import net.mamoe.mirai.console.MiraiConsole -import net.mamoe.mirai.console.scheduler.PluginScheduler +import net.mamoe.mirai.console.plugins.Plugin +import net.mamoe.mirai.console.utils.JavaPluginScheduler import net.mamoe.mirai.utils.MiraiLogger -import java.io.File 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 @JvmDefault @@ -42,7 +49,9 @@ interface JvmPlugin : Plugin, CoroutineScope { } } - +/** + * Java 插件的父类 + */ abstract class JavaPlugin @JvmOverloads constructor( coroutineContext: CoroutineContext = EmptyCoroutineContext ) : JvmPlugin, JvmPluginImpl(coroutineContext) { @@ -50,7 +59,8 @@ abstract class JavaPlugin @JvmOverloads constructor( /** * Java API Scheduler */ - val scheduler: PluginScheduler? = PluginScheduler(this.coroutineContext) + val scheduler: JavaPluginScheduler = + JavaPluginScheduler(this.coroutineContext) } abstract class KotlinPlugin @JvmOverloads constructor( @@ -59,41 +69,6 @@ abstract class KotlinPlugin @JvmOverloads constructor( // that's it } -@Serializable -data class JvmPluginDescription internal constructor( // serializer 可以用这个构造器 - override val kind: PluginKind, - override val name: String, - override val author: String, - override val version: String, - override val info: String, - override val loadBefore: List, - override val dependencies: List -) : PluginDescription, FilePluginDescription { - /** - * 在手动实现时使用这个构造器. - */ - @Suppress("unused") - constructor( - kind: PluginKind, - name: String, - author: String, - version: String, - info: String, - loadBefore: List, - depends: List, - file: File - ) : this(kind, name, author, version, info, loadBefore, depends) { - this._file = file - } - - @Suppress("PropertyName") - @Transient - internal var _file: File? = null - - override val file: File - get() = _file ?: error("Internal error: JvmPluginDescription(name=$name)._file == null") -} - internal abstract class JvmPluginImpl( parentCoroutineContext: CoroutineContext ) : JvmPlugin, CoroutineScope { @@ -102,6 +77,11 @@ internal abstract class JvmPluginImpl( */ @Suppress("PropertyName") internal lateinit var _description: JvmPluginDescription + + // for future use + @Suppress("PropertyName") + internal var _intrinsicCoroutineContext: CoroutineContext = EmptyCoroutineContext + override val description: JvmPluginDescription get() = _description final override val logger: MiraiLogger by lazy { MiraiConsole.newLogger(this._description.name) } @@ -109,37 +89,10 @@ internal abstract class JvmPluginImpl( final override val coroutineContext: CoroutineContext by lazy { CoroutineExceptionHandler { _, throwable -> logger.error(throwable) } .plus(parentCoroutineContext) - .plus(SupervisorJob(parentCoroutineContext[Job])) + .plus(SupervisorJob(parentCoroutineContext[Job])) + _intrinsicCoroutineContext } } -/** - * 内建的 Jar (JVM) 插件加载器 - */ -object JarPluginLoader : AbstractFilePluginLoader("jar") { - override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description - - override fun Sequence.mapToDescription(): List { - TODO( - """ - CHECK IS JAR FILE AND CAN BE READ - READ JAR FILE, EXTRACT PLUGIN DESCRIPTION - SET JvmPluginDescription._file - RETURN PLUGIN - """.trimIndent() - ) - } - - @Throws(PluginLoadException::class) - override fun load(description: JvmPluginDescription): JvmPlugin { - TODO("FIND PLUGIN MAIN, THEN LOAD") - // no need to check dependencies - } - - override fun enable(plugin: JvmPlugin) = plugin.onEnable() - override fun disable(plugin: JvmPlugin) = plugin.onDisable() -} - /* object PluginManagerOld { /** diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPluginDescription.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPluginDescription.kt new file mode 100644 index 000000000..03cf5c579 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPluginDescription.kt @@ -0,0 +1,48 @@ +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, + override val name: String, + @SerialName("main") + val mainClassName: String, + override val author: String, + override val version: String, + override val info: String, + override val dependencies: List +) : PluginDescription, FilePluginDescription { + + /** + * 在手动实现时使用这个构造器. + */ + @Suppress("unused") + constructor( + kind: PluginKind, name: String, mainClassName: String, author: String, + version: String, info: String, depends: List, + 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)" + } +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/description.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/description.kt new file mode 100644 index 000000000..15622c178 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/description.kt @@ -0,0 +1,84 @@ +/* + * 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.Serializable +import java.io.File + + +/** 插件类型 */ +enum class PluginKind { + /** 表示此插件提供一个 [PluginLoader], 应在加载其他 [NORMAL] 类型插件前加载 */ + LOADER, + + /** 表示此插件为一个通常的插件, 按照正常的依赖关系加载. */ + NORMAL +} + +/** + * 插件描述 + */ +interface PluginDescription { + val kind: PluginKind + + val name: String + val author: String + val version: String + val info: String + + /** 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件 */ + val dependencies: List +} + +/** 插件的一个依赖的信息 */ +@Serializable +data class PluginDependency( + /** 依赖插件名 */ + val name: String, + /** + * 依赖版本号 + * @see versionKind 版本号类型 + */ + val version: String, + /** 版本号类型 */ + val versionKind: VersionKind, + /** + * 若为 `false`, 插件在找不到此依赖时也能正常加载. + */ + val isOptional: Boolean +) { + enum class VersionKind { + /** 要求依赖精确的版本 */ + EXACT, + + /** 要求依赖最低版本 */ + AT_LEAST, + + /** 要求依赖最高版本 */ + AT_MOST + } + + override fun toString(): String { + return "$name ${versionKind.toEnglishString()}v$version" + } +} + +/** + * 基于文件的插件的描述 + */ +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 " +} From c3120cf1ac80b6a97a455aeba76296c2a040d983 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 23 May 2020 18:53:54 +0800 Subject: [PATCH 22/28] Misc --- backend/mirai-console/build.gradle.kts | 1 + .../net/mamoe/mirai/console/MiraiConsole.kt | 64 ++++++++------- .../net/mamoe/mirai/console/plugins/Plugin.kt | 81 ++----------------- .../mirai/console/plugins/PluginLoader.kt | 1 - .../mirai/console/plugins/PluginManager.kt | 68 ++++++++-------- .../console/plugins/builtin/JvmPlugin.kt | 12 ++- 6 files changed, 86 insertions(+), 141 deletions(-) diff --git a/backend/mirai-console/build.gradle.kts b/backend/mirai-console/build.gradle.kts index 4fc131443..b2e0e655f 100644 --- a/backend/mirai-console/build.gradle.kts +++ b/backend/mirai-console/build.gradle.kts @@ -61,6 +61,7 @@ dependencies { 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-jdk8")) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt index 46cfcef00..82b299f82 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt @@ -13,8 +13,8 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.io.charsets.Charset import net.mamoe.mirai.Bot -import net.mamoe.mirai.console.plugins.JarPluginLoader 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 @@ -23,6 +23,38 @@ import java.io.File import java.io.PrintStream import kotlin.coroutines.CoroutineContext +/** + * mirai 控制台实例. + */ +object MiraiConsole : CoroutineScope, IMiraiConsole { + private lateinit var instance: IMiraiConsole + + /** 由前端调用 */ + internal fun init(instance: IMiraiConsole) { + this.instance = instance + } + + override val build: String get() = instance.build + override val version: String get() = instance.version + 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> = instance.builtInPluginLoaders + + init { + DefaultLogger = { identity -> this.newLogger(identity) } + this.coroutineContext[Job]!!.invokeOnCompletion { + Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) } + } + } + + @MiraiExperimentalAPI + fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity) +} + + // 前端使用 internal interface IMiraiConsole : CoroutineScope { val build: String @@ -49,36 +81,6 @@ internal interface IMiraiConsole : CoroutineScope { val builtInPluginLoaders: List> } -object MiraiConsole : CoroutineScope, IMiraiConsole { - private lateinit var instance: IMiraiConsole - - /** 由前端调用 */ - internal fun init(instance: IMiraiConsole) { - this.instance = instance - } - - override val build: String get() = instance.build - override val version: String get() = instance.version - 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> = instance.builtInPluginLoaders - - init { - DefaultLogger = { identity -> - this.newLogger(identity) - } - this.coroutineContext[Job]!!.invokeOnCompletion { - Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) } - } - } - - @MiraiExperimentalAPI - fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity) -} - /** * Included in kotlin stdlib 1.4 */ diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/Plugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/Plugin.kt index f0df37871..d9ecf24e4 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/Plugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/Plugin.kt @@ -9,82 +9,17 @@ package net.mamoe.mirai.console.plugins -import kotlinx.serialization.Serializable -import java.io.File - -/** 插件类型 */ -enum class PluginKind { - /** 表示此插件提供一个 [PluginLoader], 应在加载其他 [NORMAL] 类型插件前加载 */ - LOADER, - - /** 表示此插件为一个通常的插件, 按照正常的依赖关系加载. */ - NORMAL -} - -/** - * 插件描述 - */ -interface PluginDescription { - val kind: PluginKind - - val name: String - val author: String - val version: String - val info: String - - /** 指定此插件需要在这些插件之前加载 */ - val loadBefore: List - - /** 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件 */ - val dependencies: List -} - -/** 插件的一个依赖的信息 */ -@Serializable -data class PluginDependency( - /** 依赖插件名 */ - val name: String, - /** - * 依赖版本号 - * @see versionKind 版本号类型 - */ - val version: String, - /** 版本号类型 */ - val versionKind: VersionKind -) { - enum class VersionKind { - /** 要求依赖精确的版本 */ - EXACT, - - /** 要求依赖最低版本 */ - AT_LEAST, - - /** 要求依赖最高版本 */ - AT_MOST - } - - override fun toString(): String { - return "$name ${versionKind.toEnglishString()}v$version" - } -} - - -internal fun PluginDependency.VersionKind.toEnglishString(): String = when (this) { - PluginDependency.VersionKind.EXACT -> "" - PluginDependency.VersionKind.AT_LEAST -> "at least " - PluginDependency.VersionKind.AT_MOST -> "at most " -} - -/** - * 基于文件的插件的描述 - */ -interface FilePluginDescription : PluginDescription { - val file: File -} +import net.mamoe.mirai.console.plugins.builtin.JvmPlugin /** * 表示一个 mirai-console 插件. * * @see JvmPlugin + * @see PluginDescription 插件描述 */ -interface Plugin \ No newline at end of file +interface Plugin { + /** + * 所属插件加载器实例 + */ + val loader: PluginLoader<*, *> +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginLoader.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginLoader.kt index acd31a025..1194b9f03 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginLoader.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginLoader.kt @@ -2,7 +2,6 @@ package net.mamoe.mirai.console.plugins -import net.mamoe.mirai.console.MiraiConsole import java.io.File /** diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt index d2386f4d8..e91396594 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt @@ -5,12 +5,10 @@ package net.mamoe.mirai.console.plugins import kotlinx.atomicfu.locks.withLock import net.mamoe.mirai.console.MiraiConsole import java.io.File -import java.util.* import java.util.concurrent.locks.ReentrantLock -import kotlin.collections.ArrayList -val Plugin.description: PluginDescription get() = TODO() -val

P.loader: PluginLoader get() = TODO() +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) @@ -18,19 +16,18 @@ inline fun PluginLoader<*, *>.unregister() = PluginManager.unregisterPluginLoade object PluginManager { val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() } - class LoaderNode

( - val loader: PluginLoader, - val loadedPlugins: MutableList

= mutableListOf() - ) - - private val _pluginLoaders: MutableSet> = mutableSetOf() + private val _pluginLoaders: MutableList> = mutableListOf() private val loadersLock: ReentrantLock = ReentrantLock() - private val resolvedPlugins: LinkedList = LinkedList() + @JvmField + internal val resolvedPlugins: MutableList = mutableListOf() + /** + * 已加载的插件列表 + */ @JvmStatic val plugins: List - get() = _pluginLoaders.flatMap { it.loadedPlugins } + get() = resolvedPlugins.toList() /** * 内建的插件加载器列表. 由 [MiraiConsole] 初始化 @@ -42,16 +39,21 @@ object PluginManager { /** * 由插件创建的 [PluginLoader] */ - val pluginLoaders: List> get() = _pluginLoaders.map { it.loader } + @JvmStatic + val pluginLoaders: List> + get() = _pluginLoaders.toList() @JvmStatic fun registerPluginLoader(loader: PluginLoader<*, *>): Boolean = loadersLock.withLock { - _pluginLoaders.add(LoaderNode(loader)) + if (_pluginLoaders.any { it::class == loader }) { + return false + } + _pluginLoaders.add(loader) } @JvmStatic fun unregisterPluginLoader(loader: PluginLoader<*, *>) = loadersLock.withLock { - _pluginLoaders.removeAll { it.loader == loader } + _pluginLoaders.remove(loader) } @@ -80,7 +82,7 @@ object PluginManager { @Suppress("UNCHECKED_CAST") @Throws(PluginMissingDependencyException::class) internal fun loadEnablePlugins() { - val all = loadAndEnableLoaderProviders() + pluginLoaders.listAllPlugins().flatMap { it.second } + val all = loadAndEnableLoaderProviders() + _pluginLoaders.listAllPlugins().flatMap { it.second } for ((loader, desc) in all.sortByDependencies()) { loader.loadPluginAndEnable(desc) @@ -116,7 +118,7 @@ object PluginManager { private fun List.sortByDependencies(): List { val resolved = ArrayList(this.size) - fun D.canBeLoad(): Boolean = this.dependencies.all { it in resolved } + fun D.canBeLoad(): Boolean = this.dependencies.all { it.isOptional || it in resolved } fun List.consumeLoadable(): List { val (canBeLoad, cannotBeLoad) = this.partition { it.canBeLoad() } @@ -124,7 +126,8 @@ object PluginManager { return cannotBeLoad } - fun List.filterIsMissing(): List = this.filterNot { it in resolved } + fun List.filterIsMissing(): List = + this.filterNot { it.isOptional || it in resolved } tailrec fun List.doSort() { if (this.isEmpty()) return @@ -147,6 +150,21 @@ object PluginManager { // 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 @@ -164,17 +182,3 @@ internal fun PluginDescription.wrapWith(loader: PluginLoader<*, *>): PluginDescr internal operator fun List.contains(dependency: PluginDependency): Boolean = any { it.name == dependency.name } - -class PluginMissingDependencyException : Exception { - 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) -} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt index 7adbd6940..9dda35f2b 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt @@ -17,6 +17,7 @@ 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 kotlin.coroutines.CoroutineContext @@ -36,6 +37,9 @@ interface JvmPlugin : Plugin, CoroutineScope { /** 插件描述 */ val description: JvmPluginDescription + /** 所属插件加载器实例 */ + override val loader: PluginLoader<*, *> get() = JarPluginLoader + @JvmDefault fun onLoad() { } @@ -53,8 +57,8 @@ interface JvmPlugin : Plugin, CoroutineScope { * Java 插件的父类 */ abstract class JavaPlugin @JvmOverloads constructor( - coroutineContext: CoroutineContext = EmptyCoroutineContext -) : JvmPlugin, JvmPluginImpl(coroutineContext) { + parentCoroutineContext: CoroutineContext = EmptyCoroutineContext +) : JvmPlugin, JvmPluginImpl(parentCoroutineContext) { /** * Java API Scheduler @@ -64,8 +68,8 @@ abstract class JavaPlugin @JvmOverloads constructor( } abstract class KotlinPlugin @JvmOverloads constructor( - coroutineContext: CoroutineContext = EmptyCoroutineContext -) : JvmPlugin, JvmPluginImpl(coroutineContext) { + parentCoroutineContext: CoroutineContext = EmptyCoroutineContext +) : JvmPlugin, JvmPluginImpl(parentCoroutineContext) { // that's it } From 633e333609791bd4450f7b9a1dfd6302b3d9f946 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 23 May 2020 19:11:47 +0800 Subject: [PATCH 23/28] Support plugin reloading --- .../plugins/builtin/JarPluginLoader.kt | 46 ++- .../console/plugins/builtin/JvmPlugin.kt | 388 ++---------------- 2 files changed, 49 insertions(+), 385 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt index bac4a2fe2..084e0bce1 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt @@ -1,9 +1,6 @@ package net.mamoe.mirai.console.plugins.builtin -import kotlinx.coroutines.CoroutineExceptionHandler -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.* import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.plugins.AbstractFilePluginLoader import net.mamoe.mirai.console.plugins.PluginLoadException @@ -16,8 +13,7 @@ import kotlin.reflect.full.createInstance /** * 内建的 Jar (JVM) 插件加载器 */ -object JarPluginLoader : AbstractFilePluginLoader("jar"), - CoroutineScope { +object JarPluginLoader : AbstractFilePluginLoader("jar"), CoroutineScope { private val logger: MiraiLogger by lazy { MiraiConsole.newLogger(JarPluginLoader::class.simpleName!!) } @@ -29,9 +25,15 @@ object JarPluginLoader : AbstractFilePluginLoader logger.error(throwable) } .plus(parentCoroutineContext) .plus(SupervisorJob(parentCoroutineContext[Job])) + _intrinsicCoroutineContext } -} -/* -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") + private var firstRun = true + + internal fun internalOnDisable() { + firstRun = false + this.onDisable() } - /** - * 获取所有插件摘要 - */ - fun getAllPluginDescriptions(): Collection { - return pluginDescriptions.values + internal fun internalOnLoad() { + this.onLoad() } - /** - * 关闭所有插件 - */ - @JvmOverloads - fun disablePlugins(throwable: CancellationException? = null) { - pluginsSequence.forEach { plugin -> - plugin.unregisterAllCommands() - plugin.disable(throwable) - } - nameToPluginBaseMap.clear() - pluginDescriptions.clear() - pluginsLoader.clear() - pluginsSequence.clear() + internal fun internalOnEnable() { + if (!firstRun) refreshCoroutineContext() + this.onEnable() } - /** - * 重载所有插件 - */ - fun reloadPlugins() { - pluginsSequence.forEach { - it.disable() - } - loadPlugins(false) + private fun refreshCoroutineContext(): CoroutineContext { + return coroutineContextInitializer().also { _coroutineContext = it } } - /** - * 尝试加载全部插件 - */ - fun loadPlugins(clear: Boolean = true) = loadPluginsImpl(clear) + private val contextUpdateLock: ReentrantLock = ReentrantLock() + private var _coroutineContext: CoroutineContext? = null + final override val coroutineContext: CoroutineContext + get() = _coroutineContext + ?: contextUpdateLock.withLock { _coroutineContext ?: refreshCoroutineContext() } - - ////////////////// - //// internal //// - ////////////////// - - internal val pluginsPath = (MiraiConsole.path + "/plugins/").replace("//", "/").also { - File(it).mkdirs() - } - - private val logger = MiraiConsole.newLogger("Plugin Manager") - - /** - * 加载成功的插件, 名字->插件 - */ - internal val nameToPluginBaseMap: MutableMap = mutableMapOf() - - /** - * 加载成功的插件, 名字->插件摘要 - */ - private val pluginDescriptions: MutableMap = mutableMapOf() - - /** - * 加载插件的PluginsLoader - */ - private val pluginsLoader: PluginsLoader = PluginsLoader(this.javaClass.classLoader) - - /** - * 插件优先级队列 - * 任何操作应该按这个Sequence顺序进行 - * 他的优先级取决于依赖, - * 在这个队列中, 被依赖的插件会在依赖的插件之前 - */ - private val pluginsSequence: LockFreeLinkedList = LockFreeLinkedList() - - - /** - * 广播Command方法 - */ - internal fun onCommand(command: Command, sender: CommandSender, args: List) { - 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, - val pluginsFound: MutableMap - ) - - internal fun findPlugins(): FindPluginsResult { - val pluginsLocation: MutableMap = mutableMapOf() - val pluginsFound: MutableMap = 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, - existDepends: MutableList - ) { - - 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 - } -}*/ \ No newline at end of file +} \ No newline at end of file From 8cc09c77fb1e4d763d9375117818a678fc130fd6 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 23 May 2020 19:51:22 +0800 Subject: [PATCH 24/28] Make JvmPluginImpl sealed --- .../kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt index 9c2eefc60..4fa0e4d10 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt @@ -75,7 +75,7 @@ abstract class KotlinPlugin @JvmOverloads constructor( // that's it } -internal abstract class JvmPluginImpl( +internal sealed class JvmPluginImpl( parentCoroutineContext: CoroutineContext ) : JvmPlugin, CoroutineScope { /** From f6af60191bc129a5ddb430c9995f58fb8f3a0aa6 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 23 May 2020 20:28:25 +0800 Subject: [PATCH 25/28] Implement plugin recognizing --- .../mirai/console/plugins/PluginManager.kt | 1 + .../plugins/builtin/JarPluginLoader.kt | 29 +++++++++++-------- .../plugins/builtin/JvmPluginDescription.kt | 5 ++-- .../mirai/console/plugins/description.kt | 19 ++++++++++-- 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt index e91396594..fcba50859 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt @@ -60,6 +60,7 @@ object PluginManager { // region LOADING private fun

PluginLoader.loadPluginNoEnable(description: D): P { + // TODO: 2020/5/23 HANDLE INITIALIZATION EXCEPTION return this.load(description).also { resolvedPlugins.add(it) } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt index 084e0bce1..0d132becd 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt @@ -6,7 +6,10 @@ 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 @@ -38,14 +41,19 @@ object JarPluginLoader : AbstractFilePluginLoader.mapToDescription(): List { - TODO( - """ - CHECK IS JAR FILE AND CAN BE READ - READ JAR FILE, EXTRACT PLUGIN DESCRIPTION - SET JvmPluginDescription._file - RETURN PLUGIN - """.trimIndent() - ) + 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 } + } } @Throws(PluginLoadException::class) @@ -68,10 +76,7 @@ object JarPluginLoader : AbstractFilePluginLoader ) : PluginDescription, FilePluginDescription { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/description.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/description.kt index 15622c178..284025f84 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/description.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/description.kt @@ -9,17 +9,32 @@ package net.mamoe.mirai.console.plugins -import kotlinx.serialization.Serializable +import kotlinx.serialization.* +import kotlinx.serialization.builtins.serializer import java.io.File /** 插件类型 */ +@Serializable(with = PluginKind.Serializer::class) enum class PluginKind { /** 表示此插件提供一个 [PluginLoader], 应在加载其他 [NORMAL] 类型插件前加载 */ LOADER, /** 表示此插件为一个通常的插件, 按照正常的依赖关系加载. */ - NORMAL + NORMAL; + + companion object Serializer : KSerializer { + override val descriptor: SerialDescriptor get() = String.serializer().descriptor + + override fun deserialize(decoder: Decoder): PluginKind { + val name = String.serializer().deserialize(decoder) + return values().firstOrNull { it.name.equals(name, ignoreCase = true) } ?: NORMAL + } + + override fun serialize(encoder: Encoder, value: PluginKind) { + return String.serializer().serialize(encoder, value.toString()) + } + } } /** From 8ea0c538a091dfd3d95eb0aea2c8594e6f42ac3d Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 24 May 2020 14:53:17 +0800 Subject: [PATCH 26/28] Add copyright --- .../scheduler/SchedulerTaskManager.java | 36 ------------------- .../mamoe/mirai/console/utils/BotManager.java | 19 +++++++--- .../net/mamoe/mirai/console/utils/Utils.java | 35 ------------------ .../mirai/console/center/CuiPluginCenter.kt | 9 +++++ .../mirai/console/center/PluginCenter.kt | 9 +++++ .../mirai/console/command/CommandManager.kt | 13 +++++-- .../command/description/CommandArgParser.kt | 9 +++++ .../CompositeCommand.CommandParam.kt | 9 +++++ .../console/event/CommandExecutionEvent.kt | 9 +++++ .../mamoe/mirai/console/event/ConsoleEvent.kt | 9 +++++ .../mirai/console/plugins/PluginLoader.kt | 9 +++++ .../mirai/console/plugins/PluginManager.kt | 9 +++++ .../plugins/builtin/JarPluginLoader.kt | 9 +++++ .../console/plugins/builtin/JvmPlugin.kt | 2 +- .../plugins/builtin/JvmPluginDescription.kt | 9 +++++ .../mamoe/mirai/console/utils/ConsoleInput.kt | 17 ++++----- .../console/utils/JavaPluginScheduler.kt | 9 +++++ .../mirai/console/utils/retryCatching.kt | 9 +++++ 18 files changed, 141 insertions(+), 89 deletions(-) delete mode 100644 backend/mirai-console/src/main/java/net/mamoe/mirai/console/scheduler/SchedulerTaskManager.java delete mode 100644 backend/mirai-console/src/main/java/net/mamoe/mirai/console/utils/Utils.java diff --git a/backend/mirai-console/src/main/java/net/mamoe/mirai/console/scheduler/SchedulerTaskManager.java b/backend/mirai-console/src/main/java/net/mamoe/mirai/console/scheduler/SchedulerTaskManager.java deleted file mode 100644 index 5c49b524a..000000000 --- a/backend/mirai-console/src/main/java/net/mamoe/mirai/console/scheduler/SchedulerTaskManager.java +++ /dev/null @@ -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 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; - } -} - diff --git a/backend/mirai-console/src/main/java/net/mamoe/mirai/console/utils/BotManager.java b/backend/mirai-console/src/main/java/net/mamoe/mirai/console/utils/BotManager.java index 080ca51bb..c30422724 100644 --- a/backend/mirai-console/src/main/java/net/mamoe/mirai/console/utils/BotManager.java +++ b/backend/mirai-console/src/main/java/net/mamoe/mirai/console/utils/BotManager.java @@ -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 getManagers(Bot bot){ + public static List 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); } } diff --git a/backend/mirai-console/src/main/java/net/mamoe/mirai/console/utils/Utils.java b/backend/mirai-console/src/main/java/net/mamoe/mirai/console/utils/Utils.java deleted file mode 100644 index 6f2c654db..000000000 --- a/backend/mirai-console/src/main/java/net/mamoe/mirai/console/utils/Utils.java +++ /dev/null @@ -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 tryNTimes(@Range(from = 1, to = Integer.MAX_VALUE) int n, - Callable 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; - } -} diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/CuiPluginCenter.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/CuiPluginCenter.kt index 1c9c6ee23..2d911108d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/CuiPluginCenter.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/CuiPluginCenter.kt @@ -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:OptIn(MiraiExperimentalAPI::class) package net.mamoe.mirai.console.center diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/PluginCenter.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/PluginCenter.kt index 26c4f63a7..deffd06dd 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/PluginCenter.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/PluginCenter.kt @@ -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.center import kotlinx.serialization.SerialName diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt index 63facd067..c9ff772ac 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt @@ -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(plugin: PluginBase) : CommandOwner() +abstract class PluginCommandOwner(plugin: Plugin) : CommandOwner() // 由前端实现 internal abstract class ConsoleCommandOwner : CommandOwner() diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgParser.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgParser.kt index 740f6d2e1..94acd0519 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgParser.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgParser.kt @@ -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 diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CompositeCommand.CommandParam.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CompositeCommand.CommandParam.kt index b20dad891..2ac138bc5 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CompositeCommand.CommandParam.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CompositeCommand.CommandParam.kt @@ -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 diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/event/CommandExecutionEvent.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/event/CommandExecutionEvent.kt index 02dadb887..6a048337f 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/event/CommandExecutionEvent.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/event/CommandExecutionEvent.kt @@ -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 diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/event/ConsoleEvent.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/event/ConsoleEvent.kt index 9a0504837..50b2aeff6 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/event/ConsoleEvent.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/event/ConsoleEvent.kt @@ -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 diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginLoader.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginLoader.kt index 1194b9f03..43af25148 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginLoader.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginLoader.kt @@ -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.plugins diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt index fcba50859..09724d3ee 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginManager.kt @@ -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.plugins diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt index 0d132becd..a8077caec 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt @@ -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.plugins.builtin import kotlinx.coroutines.* diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt index 4fa0e4d10..970f0b8ae 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPlugin.kt @@ -6,8 +6,8 @@ * * https://github.com/mamoe/mirai/blob/master/LICENSE */ -@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS") +@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS") package net.mamoe.mirai.console.plugins.builtin diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPluginDescription.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPluginDescription.kt index b7e9e74ff..be5e73810 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPluginDescription.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPluginDescription.kt @@ -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.plugins.builtin import kotlinx.serialization.SerialName diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/ConsoleInput.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/ConsoleInput.kt index 8295c7012..75f66a8b0 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/ConsoleInput.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/ConsoleInput.kt @@ -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{ - return pluginBase.async { + ): Deferred { + return scope.async { requestInput(hint) } } - suspend fun MiraiConsole.requestInput(hint:String):String = requestInput(hint) - - suspend fun PluginBase.requestInputAsync(hint:String):Deferred = requestInputAsync(hint) + suspend fun MiraiConsole.requestInput(hint: String): String = requestInput(hint) } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/JavaPluginScheduler.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/JavaPluginScheduler.kt index 826759aef..ab334872c 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/JavaPluginScheduler.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/JavaPluginScheduler.kt @@ -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 kotlinx.coroutines.* diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/retryCatching.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/retryCatching.kt index a8c425921..2cdb0b423 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/retryCatching.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/retryCatching.kt @@ -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("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE") package net.mamoe.mirai.console.utils From 1cf35b8554c989f93327b629c0459b4a48a4be61 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 24 May 2020 14:56:06 +0800 Subject: [PATCH 27/28] Fix build --- .../plugins/builtin/JarPluginLoader.kt | 40 ++++++++++--------- .../mirai/console/command/TestCommands.kt | 4 +- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt index a8077caec..476a863af 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JarPluginLoader.kt @@ -65,28 +65,30 @@ object JarPluginLoader : AbstractFilePluginLoader { + 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" } + 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 { - throw PluginLoadException("Exception while loading ${description.name}", it) - } + if (main is JvmPluginImpl) { + main._description = description + main.internalOnLoad() + } else main.onLoad() + main + }.getOrElse { + throw PluginLoadException("Exception while loading ${description.name}", it) + } override fun enable(plugin: JvmPlugin) { ensureActive() diff --git a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommands.kt b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommands.kt index 5ff9aca1e..11350c771 100644 --- a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommands.kt +++ b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommands.kt @@ -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() { } From ff931ed26e0500e13edf7d3ea7302ae7f53377f2 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 24 May 2020 18:56:08 +0800 Subject: [PATCH 28/28] Add smart serializers for PluginDescription --- .../plugins/builtin/JvmPluginDescription.kt | 4 +- .../mirai/console/plugins/description.kt | 68 +++++++++++++------ .../console/setting/internal/internal.kt | 11 +++ 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPluginDescription.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPluginDescription.kt index be5e73810..562ad58f2 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPluginDescription.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/builtin/JvmPluginDescription.kt @@ -20,7 +20,7 @@ import java.io.File @Serializable class JvmPluginDescription internal constructor( - override val kind: PluginKind, + override val kind: PluginKind = PluginKind.NORMAL, override val name: String, @SerialName("main") val mainClassName: String, @@ -28,7 +28,7 @@ class JvmPluginDescription internal constructor( override val version: String, override val info: String = "", @SerialName("depends") - override val dependencies: List + override val dependencies: List<@Serializable(with = PluginDependency.SmartSerializer::class) PluginDependency> = listOf() ) : PluginDescription, FilePluginDescription { /** diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/description.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/description.kt index 284025f84..4d23c90e4 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/description.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/description.kt @@ -11,11 +11,14 @@ 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.Serializer::class) +@Serializable(with = PluginKind.AsStringSerializer::class) enum class PluginKind { /** 表示此插件提供一个 [PluginLoader], 应在加载其他 [NORMAL] 类型插件前加载 */ LOADER, @@ -23,18 +26,14 @@ enum class PluginKind { /** 表示此插件为一个通常的插件, 按照正常的依赖关系加载. */ NORMAL; - companion object Serializer : KSerializer { - override val descriptor: SerialDescriptor get() = String.serializer().descriptor - - override fun deserialize(decoder: Decoder): PluginKind { - val name = String.serializer().deserialize(decoder) - return values().firstOrNull { it.name.equals(name, ignoreCase = true) } ?: NORMAL + object AsStringSerializer : KSerializer by String.serializer().map( + serializer = { it.name }, + deserializer = { str -> + values().firstOrNull { + it.name.equals(str, ignoreCase = true) + } ?: NORMAL } - - override fun serialize(encoder: Encoder, value: PluginKind) { - return String.serializer().serialize(encoder, value.toString()) - } - } + ) } /** @@ -49,7 +48,7 @@ interface PluginDescription { val info: String /** 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件 */ - val dependencies: List + val dependencies: List<@Serializable(with = PluginDependency.SmartSerializer::class) PluginDependency> } /** 插件的一个依赖的信息 */ @@ -58,31 +57,58 @@ data class PluginDependency( /** 依赖插件名 */ val name: String, /** - * 依赖版本号 + * 依赖版本号. 为 null 时则为不限制版本. * @see versionKind 版本号类型 */ - val version: String, + val version: String? = null, /** 版本号类型 */ - val versionKind: VersionKind, + val versionKind: VersionKind = VersionKind.AT_LEAST, /** * 若为 `false`, 插件在找不到此依赖时也能正常加载. */ - val isOptional: Boolean + val isOptional: Boolean = false ) { - enum class VersionKind { + /** 版本号类型 */ + @Serializable(with = VersionKind.AsStringSerializer::class) + enum class VersionKind( + private vararg val serialNames: String + ) { /** 要求依赖精确的版本 */ - EXACT, + EXACT("exact"), /** 要求依赖最低版本 */ - AT_LEAST, + AT_LEAST("at_least", "AtLeast", "least", "lowest", "+"), /** 要求依赖最高版本 */ - AT_MOST + AT_MOST("at_most", "AtMost", "most", "highest", "-"); + + object AsStringSerializer : KSerializer 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 by YamlDynamicSerializer.map( + serializer = { it }, + deserializer = { any -> + when (any) { + is Map<*, *> -> Yaml.nonStrict.parse(serializer(), Yaml.nonStrict.stringify(any)) + else -> PluginDependency(any.toString()) + } + } + ) } /** diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internal.kt index 92d22dd46..ee3e4418b 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/internal.kt @@ -164,3 +164,14 @@ internal inline fun KSerializer.bind( this@bind.serialize(encoder, getter()) } } + +internal inline fun KSerializer.map( + crossinline serializer: (R) -> E, + crossinline deserializer: (E) -> R +): KSerializer { + return object : KSerializer { + 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)) + } +}