diff --git a/binary-compatibility-validator/api/binary-compatibility-validator.api b/binary-compatibility-validator/api/binary-compatibility-validator.api index d98a3a9c6..749012d51 100644 --- a/binary-compatibility-validator/api/binary-compatibility-validator.api +++ b/binary-compatibility-validator/api/binary-compatibility-validator.api @@ -4702,6 +4702,7 @@ public final class net/mamoe/mirai/message/data/MusicKind : java/lang/Enum { public final class net/mamoe/mirai/message/data/MusicShare : net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent { public static final field Key Lnet/mamoe/mirai/message/data/MusicShare$Key; + public static final field SERIAL_NAME Ljava/lang/String; public synthetic fun (ILnet/mamoe/mirai/message/data/MusicKind;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V public fun (Lnet/mamoe/mirai/message/data/MusicKind;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public fun (Lnet/mamoe/mirai/message/data/MusicKind;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V @@ -5084,14 +5085,17 @@ public final class net/mamoe/mirai/message/data/RichMessageKind : java/lang/Enum public final class net/mamoe/mirai/message/data/RichMessageOrigin : net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageMetadata { public static final field Key Lnet/mamoe/mirai/message/data/RichMessageOrigin$Key; + public static final field SERIAL_NAME Ljava/lang/String; public synthetic fun (ILnet/mamoe/mirai/message/data/RichMessage;Ljava/lang/String;Lnet/mamoe/mirai/message/data/RichMessageKind;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V public fun (Lnet/mamoe/mirai/message/data/RichMessage;Ljava/lang/String;Lnet/mamoe/mirai/message/data/RichMessageKind;)V public fun contentToString ()Ljava/lang/String; + public fun equals (Ljava/lang/Object;)Z public synthetic fun getKey ()Lnet/mamoe/mirai/message/data/MessageKey; public fun getKey ()Lnet/mamoe/mirai/message/data/RichMessageOrigin$Key; public final fun getKind ()Lnet/mamoe/mirai/message/data/RichMessageKind; public final fun getOrigin ()Lnet/mamoe/mirai/message/data/RichMessage; public final fun getResourceId ()Ljava/lang/String; + public fun hashCode ()I public fun toString ()Ljava/lang/String; public static final fun write$Self (Lnet/mamoe/mirai/message/data/RichMessageOrigin;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V } diff --git a/mirai-console b/mirai-console index a5481accb..8683e8889 160000 --- a/mirai-console +++ b/mirai-console @@ -1 +1 @@ -Subproject commit a5481accb5f882d121ff9fc1d55e4e5f3e908e76 +Subproject commit 8683e888926fbe2c76abf6362830f7470fc7c029 diff --git a/mirai-core-api/src/commonMain/kotlin/internal/message/MessageSerializersImpl.kt b/mirai-core-api/src/commonMain/kotlin/internal/message/MessageSerializersImpl.kt index 224bb06d6..5b0d23178 100644 --- a/mirai-core-api/src/commonMain/kotlin/internal/message/MessageSerializersImpl.kt +++ b/mirai-core-api/src/commonMain/kotlin/internal/message/MessageSerializersImpl.kt @@ -22,9 +22,10 @@ import kotlinx.serialization.modules.polymorphic import net.mamoe.mirai.Mirai import net.mamoe.mirai.message.MessageSerializers import net.mamoe.mirai.message.data.* -import net.mamoe.mirai.message.data.MessageChainImpl import net.mamoe.mirai.utils.MiraiInternalApi import kotlin.reflect.KClass +import kotlin.reflect.full.allSuperclasses +import kotlin.reflect.full.isSubclassOf internal fun ClassSerialDescriptorBuilder.takeElementsFrom(descriptor: SerialDescriptor) { @@ -70,38 +71,45 @@ public open class MessageSourceSerializerImpl(serialName: String) : private val builtInSerializersModule by lazy { SerializersModule { - // non-Message classes - contextual(RawForwardMessage::class, RawForwardMessage.serializer()) - contextual(ForwardMessage.Node::class, ForwardMessage.Node.serializer()) - contextual(VipFace.Kind::class, VipFace.Kind.serializer()) + // NOTE: contextual serializers disabled because of https://github.com/mamoe/mirai/issues/951 +// // non-Message classes +// contextual(RawForwardMessage::class, RawForwardMessage.serializer()) +// contextual(ForwardMessage.Node::class, ForwardMessage.Node.serializer()) +// contextual(VipFace.Kind::class, VipFace.Kind.serializer()) +// +// +// // In case Proguard or something else obfuscated the Kotlin metadata, providing the serializers explicitly will help. +// contextual(At::class, At.serializer()) +// contextual(AtAll::class, AtAll.serializer()) +// contextual(CustomMessage::class, CustomMessage.serializer()) +// contextual(CustomMessageMetadata::class, CustomMessageMetadata.serializer()) +// contextual(Face::class, Face.serializer()) +// contextual(Image::class, Image.Serializer) +// contextual(PlainText::class, PlainText.serializer()) +// contextual(QuoteReply::class, QuoteReply.serializer()) +// +// contextual(ForwardMessage::class, ForwardMessage.serializer()) +// +// +// contextual(LightApp::class, LightApp.serializer()) +// contextual(SimpleServiceMessage::class, SimpleServiceMessage.serializer()) +// contextual(AbstractServiceMessage::class, AbstractServiceMessage.serializer()) +// +// contextual(PttMessage::class, PttMessage.serializer()) +// contextual(Voice::class, Voice.serializer()) +// contextual(PokeMessage::class, PokeMessage.serializer()) +// contextual(VipFace::class, VipFace.serializer()) +// contextual(FlashImage::class, FlashImage.serializer()) +// +// contextual(MusicShare::class, MusicShare.serializer()) +// +// contextual(MessageSource::class, MessageSource.serializer()) - // In case Proguard or something else obfuscated the Kotlin metadata, providing the serializers explicitly will help. - contextual(At::class, At.serializer()) - contextual(AtAll::class, AtAll.serializer()) - contextual(CustomMessage::class, CustomMessage.serializer()) - contextual(CustomMessageMetadata::class, CustomMessageMetadata.serializer()) - contextual(Face::class, Face.serializer()) - contextual(Image::class, Image.Serializer) - contextual(PlainText::class, PlainText.serializer()) - contextual(QuoteReply::class, QuoteReply.serializer()) +// contextual(SingleMessage::class, SingleMessage.Serializer) + contextual(MessageChain::class, MessageChain.Serializer) + contextual(MessageChainImpl::class, MessageChainImpl.serializer()) - contextual(ForwardMessage::class, ForwardMessage.serializer()) - - - contextual(LightApp::class, LightApp.serializer()) - contextual(SimpleServiceMessage::class, SimpleServiceMessage.serializer()) - contextual(AbstractServiceMessage::class, AbstractServiceMessage.serializer()) - - contextual(PttMessage::class, PttMessage.serializer()) - contextual(Voice::class, Voice.serializer()) - contextual(PokeMessage::class, PokeMessage.serializer()) - contextual(VipFace::class, VipFace.serializer()) - contextual(FlashImage::class, FlashImage.serializer()) - - contextual(MusicShare::class, MusicShare.serializer()) - - contextual(MessageSource::class, MessageSource.serializer()) fun PolymorphicModuleBuilder.messageMetadataSubclasses() { subclass(MessageSource::class, MessageSource.serializer()) @@ -132,11 +140,6 @@ private val builtInSerializersModule by lazy { subclass(MusicShare::class, MusicShare.serializer()) } - contextual(SingleMessage::class, SingleMessage.Serializer) - contextual(MessageChain::class, MessageChain.Serializer) - contextual(MessageChainImpl::class, MessageChainImpl.serializer()) - -// polymorphicDefault(MessageChain::class) { MessageChainImpl.serializer() } // polymorphic(SingleMessage::class) { // subclass(MessageSource::class, MessageSource.serializer()) @@ -153,6 +156,23 @@ private val builtInSerializersModule by lazy { messageMetadataSubclasses() } + polymorphic(MessageContent::class) { + messageContentSubclasses() + } + + polymorphic(MessageMetadata::class) { + messageMetadataSubclasses() + } + + polymorphic(RichMessage::class) { + subclass(SimpleServiceMessage::class, SimpleServiceMessage.serializer()) + subclass(LightApp::class, LightApp.serializer()) + } + + polymorphic(ServiceMessage::class) { + subclass(SimpleServiceMessage::class, SimpleServiceMessage.serializer()) + } + //contextual(SingleMessage::class, SingleMessage.Serializer) // polymorphic(SingleMessage::class, SingleMessage.Serializer) { // messageContentSubclasses() @@ -180,11 +200,16 @@ internal object MessageSerializersImpl : MessageSerializers { override val serializersModule: SerializersModule get() = serializersModuleField ?: builtInSerializersModule @Synchronized - override fun registerSerializer(baseClass: KClass, serializer: KSerializer) { + override fun registerSerializer(type: KClass, serializer: KSerializer) { serializersModuleField = serializersModule.overwriteWith(SerializersModule { - contextual(baseClass, serializer) - polymorphic(SingleMessage::class) { - subclass(baseClass, serializer) + // contextual(type, serializer) + for (superclass in type.allSuperclasses) { + if (superclass.isFinal) continue + if (!superclass.isSubclassOf(SingleMessage::class)) continue + @Suppress("UNCHECKED_CAST") + polymorphic(superclass as KClass) { + subclass(type, serializer) + } } }) } diff --git a/mirai-core-api/src/commonMain/kotlin/message/MessageSerializers.kt b/mirai-core-api/src/commonMain/kotlin/message/MessageSerializers.kt index 2e2b45520..dcaa795cf 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/MessageSerializers.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/MessageSerializers.kt @@ -12,7 +12,9 @@ package net.mamoe.mirai.message import kotlinx.serialization.ContextualSerializer import kotlinx.serialization.KSerializer import kotlinx.serialization.json.Json -import kotlinx.serialization.modules.* +import kotlinx.serialization.modules.PolymorphicModuleBuilder +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.subclass import net.mamoe.mirai.internal.message.MessageSerializersImpl import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.MessageChain @@ -50,21 +52,26 @@ public interface MessageSerializers { public val serializersModule: SerializersModule /** - * 注册一个 [SerializersModuleBuilder.contextual] 和 [SingleMessage] 多态域的 [PolymorphicModuleBuilder.subclass]. + * 注册 [serializer] 到 [type] 的所有为 [SingleMessage] 子类型的超类型的多态域 [PolymorphicModuleBuilder.subclass] * - * 相当于 + * 实现: * ``` - * contextual(baseClass, serializer) - * polymorphic(SingleMessage::class) { - * subclass(baseClass, serializer) + * for (superclass in type.allSuperclasses) { + * if (superclass.isFinal) continue + * if (superclass.isSubclassOf(SingleMessage::class)) continue + * polymorphic(superclass) { + * subclass(type, serializer) + * } * } * ``` * * * 若要自己实现消息类型, 务必在这里注册对应序列化器, 否则在 [MessageChain.serializeToJsonString] 时将会出错. + * + * @since 2.0, revised 2.3 */ @MiraiExperimentalApi - public fun registerSerializer(baseClass: KClass, serializer: KSerializer) + public fun registerSerializer(type: KClass, serializer: KSerializer) /** * 合并 [serializersModule] 到 [MessageSerializers.serializersModule] 并覆盖. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt b/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt index 9f9cfa63d..f7a6c28b6 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt @@ -245,7 +245,7 @@ public interface MessageChain : */ public object Serializer : KSerializer { @Suppress("DEPRECATION_ERROR") - private val delegate = ListSerializer(PolymorphicSerializer(SingleMessage::class)) + private val delegate = ListSerializer(SingleMessage.Serializer) override val descriptor: SerialDescriptor = delegate.descriptor override fun deserialize(decoder: Decoder): MessageChain = delegate.deserialize(decoder).toMessageChain() override fun serialize(encoder: Encoder, value: MessageChain): Unit = delegate.serialize(encoder, value) diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/MusicShare.kt b/mirai-core-api/src/commonMain/kotlin/message/data/MusicShare.kt index b00d6ec2b..fe63cc520 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/MusicShare.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/MusicShare.kt @@ -11,6 +11,7 @@ package net.mamoe.mirai.message.data +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi @@ -22,6 +23,7 @@ import net.mamoe.mirai.utils.safeCast * @since 2.1 */ @Serializable +@SerialName(MusicShare.SERIAL_NAME) public data class MusicShare( /** * 音乐应用类型 @@ -93,7 +95,13 @@ public data class MusicShare( */ public companion object Key : AbstractPolymorphicMessageKey<@MiraiExperimentalApi MessageContent, MusicShare> - (MessageContent, { it.safeCast() }) + (MessageContent, { it.safeCast() }) { + + /** + * @since 2.3 + */ + public const val SERIAL_NAME: String = "MusicShare" + } } /** diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/RichMessageOrigin.kt b/mirai-core-api/src/commonMain/kotlin/message/data/RichMessageOrigin.kt index 89fae1787..5ffea7d43 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/RichMessageOrigin.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/RichMessageOrigin.kt @@ -37,7 +37,7 @@ import net.mamoe.mirai.utils.safeCast * @since 2.3 */ @Serializable -@SerialName("RichMessageOrigin") +@SerialName(RichMessageOrigin.SERIAL_NAME) @MiraiExperimentalApi("RichMessageOrigin 不稳定") public class RichMessageOrigin( /** @@ -66,7 +66,30 @@ public class RichMessageOrigin( override fun contentToString(): String = "" - public companion object Key : AbstractMessageKey({ it.safeCast() }) + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as RichMessageOrigin + + if (origin != other.origin) return false + if (resourceId != other.resourceId) return false + if (kind != other.kind) return false + + return true + } + + override fun hashCode(): Int { + var result = origin.hashCode() + result = 31 * result + (resourceId?.hashCode() ?: 0) + result = 31 * result + kind.hashCode() + return result + } + + + public companion object Key : AbstractMessageKey({ it.safeCast() }) { + public const val SERIAL_NAME: String = "RichMessageOrigin" + } } /** diff --git a/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt b/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt index 090e4e700..f65491e0f 100644 --- a/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt +++ b/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt @@ -9,14 +9,16 @@ package net.mamoe.mirai.internal.message.data -import kotlinx.serialization.KSerializer +import kotlinx.serialization.* import kotlinx.serialization.json.Json -import kotlinx.serialization.serializer +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive import net.mamoe.mirai.Mirai import net.mamoe.mirai.internal.message.MarketFaceImpl import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.message.MessageSerializers import net.mamoe.mirai.message.data.* +import net.mamoe.mirai.utils.cast import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test import kotlin.test.assertEquals @@ -101,7 +103,8 @@ internal class MessageSerializationTest { LightApp("lightApp"), image.flash(), image.toForwardMessage(1L, "test"), - MusicShare(MusicKind.NeteaseCloudMusic, "123", "123", "123", "123", "123", "123") + MusicShare(MusicKind.NeteaseCloudMusic, "123", "123", "123", "123", "123", "123"), + RichMessageOrigin(SimpleServiceMessage(1, "content"), "resource id", RichMessageKind.LONG) ) companion object { @@ -114,7 +117,19 @@ internal class MessageSerializationTest { @Test fun `test polymorphic serialization`() { + @Serializable + data class RichWrapper( + val richMessage: RichMessage + ) + val string = format.encodeToString(RichWrapper.serializer(), RichWrapper(SimpleServiceMessage(1, "content"))) + println(string) + var element = format.parseToJsonElement(string) + element as JsonObject + element = element["richMessage"] as JsonObject + assertEquals("SimpleServiceMessage", element["type"]?.cast()?.content) + assertEquals("content", element["content"]?.cast()?.content) + assertEquals(1, element["serviceId"]?.cast()?.content?.toInt()) } @Test