Move packet stuffs to mirai-core

This commit is contained in:
Him188 2019-12-24 00:18:18 +08:00
parent 19d5c910fd
commit c8eac6aff4
38 changed files with 377 additions and 224 deletions

View File

@ -11,6 +11,9 @@ import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.ImageId0x03
import net.mamoe.mirai.message.data.ImageId0x06
import net.mamoe.mirai.network.packet.KnownPacketId
import net.mamoe.mirai.network.packet.OutgoingPacket
import net.mamoe.mirai.network.packet.SessionKey
import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.timpc.internal.RawGroupInfo
import net.mamoe.mirai.timpc.network.GroupImpl
@ -18,10 +21,10 @@ import net.mamoe.mirai.timpc.network.MemberImpl
import net.mamoe.mirai.timpc.network.QQImpl
import net.mamoe.mirai.timpc.network.TIMBotNetworkHandler
import net.mamoe.mirai.timpc.network.handler.TemporaryPacketHandler
import net.mamoe.mirai.timpc.network.packet.KnownPacketId
import net.mamoe.mirai.timpc.network.packet.OutgoingPacket
import net.mamoe.mirai.timpc.network.packet.SessionKey
import net.mamoe.mirai.timpc.network.packet.action.*
import net.mamoe.mirai.timpc.network.packet.event.EventPacketFactory
import net.mamoe.mirai.timpc.network.packet.event.FriendOnlineStatusChangedPacket
import net.mamoe.mirai.timpc.network.packet.login.*
import net.mamoe.mirai.timpc.utils.assertUnreachable
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail
@ -44,9 +47,41 @@ internal abstract class TIMPCBotBase constructor(
logger: MiraiLogger?,
context: CoroutineContext
) : BotImpl<TIMBotNetworkHandler>(account, logger ?: DefaultLogger("Bot(" + account.id + ")"), context) {
@UseExperimental(ExperimentalUnsignedTypes::class)
companion object {
init {
KnownPacketId.values() /* load id classes */
KnownPacketId[0x0825u] = TouchPacket
KnownPacketId[0x0828u] = RequestSessionPacket
KnownPacketId[0x0836u] = SubmitPasswordPacket
KnownPacketId[0x00BAu] = CaptchaPacket
KnownPacketId[0x00CEu] = EventPacketFactory
KnownPacketId[0x0017u] = EventPacketFactory
KnownPacketId[0x0081u] = FriendOnlineStatusChangedPacket
KnownPacketId[0x00ECu] = ChangeOnlineStatusPacket
KnownPacketId[0x0058u] = HeartbeatPacket
KnownPacketId[0x001Du] = RequestSKeyPacket
KnownPacketId[0x005Cu] = RequestAccountInfoPacket
KnownPacketId[0x0002u] = GroupPacket
KnownPacketId[0x00CDu] = SendFriendMessagePacket
KnownPacketId[0x00A7u] = CanAddFriendPacket
KnownPacketId[0x00A8u] = AddFriendPacket
KnownPacketId[0x00AEu] = RequestFriendAdditionKeyPacket
KnownPacketId[0x0388u] = GroupImagePacket
KnownPacketId[0x0352u] = FriendImagePacket
KnownPacketId[0x0031u] = RequestProfileAvatarPacket
KnownPacketId[0x003Cu] = RequestProfileDetailsPacket
KnownPacketId[0x0126u] = QueryNicknamePacket
KnownPacketId[0x01BCu] = QueryPreviousNamePacket
KnownPacketId[0x003Eu] = QueryFriendRemarkPacket
// 031F 查询 "新朋友" 记录
// @Suppress("DEPRECATION")
// inline SUBMIT_IMAGE_FILE_NAME(0x01BDu, SubmitImageFilenamePacket),
}
}
@ -69,7 +104,7 @@ internal abstract class TIMPCBotBase constructor(
reinitializeNetworkHandler(configuration, cause)
logger.info("Reconnected successfully")
return@launch
} catch (e: LoginFailedException){
} catch (e: LoginFailedException) {
delay(configuration.reconnectPeriodMillis)
}
}

View File

@ -6,19 +6,19 @@ import kotlinx.coroutines.*
import kotlinx.io.core.*
import kotlinx.io.pool.useInstance
import net.mamoe.mirai.Bot
import net.mamoe.mirai.data.LoginResult
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.event.BroadcastControllable
import net.mamoe.mirai.event.Cancellable
import net.mamoe.mirai.event.Subscribable
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.BotLoginSucceedEvent
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.LoginResult
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.timpc.TIMPCBot
import net.mamoe.mirai.timpc.network.handler.DataPacketSocketAdapter
import net.mamoe.mirai.timpc.network.handler.TemporaryPacketHandler
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.login.*
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.io.*

View File

@ -0,0 +1,97 @@
package net.mamoe.mirai.timpc.network.packet
import kotlinx.io.core.BytePacketBuilder
import kotlinx.serialization.SerializationStrategy
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.utils.MiraiInternalAPI
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.jvm.JvmOverloads
/**
* 构造一个待发送给服务器的数据包.
*/
@UseExperimental(ExperimentalContracts::class, MiraiInternalAPI::class, ExperimentalUnsignedTypes::class)
@JvmOverloads
inline fun PacketFactory<*, *>.buildOutgoingPacket(
name: String? = null,
id: PacketId = this.id,
sequenceId: UShort = PacketFactory.atomicNextSequenceId(),
headerSizeHint: Int = 0,
block: BytePacketBuilder.() -> Unit
): OutgoingPacket {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return buildOutgoingPacket0(name, id, sequenceId, headerSizeHint, TIMProtocol.head, TIMProtocol.ver, TIMProtocol.tail, block)
}
/**
* 构造一个待发送给服务器的会话数据包.
*/
@UseExperimental(ExperimentalContracts::class, MiraiInternalAPI::class, ExperimentalUnsignedTypes::class)
@JvmOverloads
inline fun PacketFactory<*, *>.buildSessionPacket(
bot: Long,
sessionKey: SessionKey,
name: String? = null,
id: PacketId = this.id,
sequenceId: UShort = PacketFactory.atomicNextSequenceId(),
headerSizeHint: Int = 0,
version: ByteArray = TIMProtocol.version0x02, // in packet body
block: BytePacketBuilder.() -> Unit
): OutgoingPacket {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return buildSessionPacket0(
bot = bot,
sessionKey = sessionKey,
name = name,
id = id,
sequenceId = sequenceId,
headerSizeHint = headerSizeHint,
version = version,
head = TIMProtocol.head,
ver = TIMProtocol.ver,
tail = TIMProtocol.tail,
block = block
)
}
/**
* 构造一个待发送给服务器的会话数据包.
*/
@UseExperimental(ExperimentalContracts::class, MiraiInternalAPI::class, ExperimentalUnsignedTypes::class)
@JvmOverloads
fun <T> PacketFactory<*, *>.buildSessionProtoPacket(
bot: Long,
sessionKey: SessionKey,
name: String? = null,
id: PacketId = this.id,
sequenceId: UShort = PacketFactory.atomicNextSequenceId(),
headerSizeHint: Int = 0,
version: ByteArray = TIMProtocol.version0x04,
head: Any,
serializer: SerializationStrategy<T>,
protoObj: T
): OutgoingPacket = buildSessionProtoPacket0(
bot = bot,
sessionKey = sessionKey,
name = name,
id = id,
sequenceId = sequenceId,
headerSizeHint = headerSizeHint,
version = version,
head = head,
serializer = serializer,
protoObj = protoObj,
packetHead = TIMProtocol.head,
ver = TIMProtocol.ver,
tail = TIMProtocol.tail
)

View File

@ -1,100 +0,0 @@
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
package net.mamoe.mirai.timpc.network.packet
import net.mamoe.mirai.timpc.network.packet.action.*
import net.mamoe.mirai.timpc.network.packet.event.EventPacketFactory
import net.mamoe.mirai.timpc.network.packet.event.FriendOnlineStatusChangedPacket
import net.mamoe.mirai.timpc.network.packet.login.*
import net.mamoe.mirai.utils.io.toUHexString
/**
* 通过 [value] 匹配一个 [IgnoredPacketId] [KnownPacketId], 无匹配则返回一个 [UnknownPacketId].
*/
internal fun matchPacketId(value: UShort): PacketId =
IgnoredPacketIds.firstOrNull { it.value == value } ?: KnownPacketId.values().firstOrNull { it.value == value } ?: UnknownPacketId(value)
/**
* ID.
*/
internal interface PacketId {
val value: UShort
val factory: PacketFactory<*, *>
}
/**
* 用于代表 `null`. 调用任何属性时都将会得到一个 [error]
*/
@Suppress("unused")
internal object NullPacketId : PacketId {
override val factory: PacketFactory<*, *> get() = error("uninitialized")
override val value: UShort get() = error("uninitialized")
override fun toString(): String = "NullPacketId"
}
/**
* 未知的 [PacketId]
*/
internal inline class UnknownPacketId(override inline val value: UShort) : PacketId {
override val factory: PacketFactory<*, *> get() = UnknownPacketFactory
override fun toString(): String = "UnknownPacketId(${value.toUHexString()})"
}
internal object IgnoredPacketIds : List<IgnoredPacketId> by {
listOf<UShort>(
).map { IgnoredPacketId(it.toUShort()) }
}()
internal inline class IgnoredPacketId constructor(override val value: UShort) : PacketId {
override val factory: PacketFactory<*, *> get() = IgnoredPacketFactory
override fun toString(): String = "IgnoredPacketId(${value.toUHexString()})"
}
/**
* 已知的 [matchPacketId]. 所有在 Mirai 中实现过的包都会使用这些 Id
*/
@Suppress("unused")
internal enum class KnownPacketId(override val value: UShort, override val factory: PacketFactory<*, *>) :
PacketId {
TOUCH(0x0825u, TouchPacket),
SESSION_KEY(0x0828u, RequestSessionPacket),
LOGIN(0x0836u, SubmitPasswordPacket),
CAPTCHA(0x00BAu, CaptchaPacket),
SERVER_EVENT_1(0x00CEu, EventPacketFactory),
SERVER_EVENT_2(0x0017u, EventPacketFactory),
FRIEND_ONLINE_STATUS_CHANGE(0x0081u, FriendOnlineStatusChangedPacket),
CHANGE_ONLINE_STATUS(0x00ECu, ChangeOnlineStatusPacket),
HEARTBEAT(0x0058u, HeartbeatPacket),
S_KEY(0x001Du, RequestSKeyPacket),
ACCOUNT_INFO(0x005Cu, RequestAccountInfoPacket),
GROUP_PACKET(0x0002u, GroupPacket),
SEND_FRIEND_MESSAGE(0x00CDu, SendFriendMessagePacket),
CAN_ADD_FRIEND(0x00A7u, CanAddFriendPacket),
ADD_FRIEND(0x00A8u, AddFriendPacket),
REQUEST_FRIEND_ADDITION_KEY(0x00AEu, RequestFriendAdditionKeyPacket),
GROUP_IMAGE_ID(0x0388u, GroupImagePacket),
FRIEND_IMAGE_ID(0x0352u, FriendImagePacket),
REQUEST_PROFILE_AVATAR(0x0031u, RequestProfileAvatarPacket),
REQUEST_PROFILE_DETAILS(0x003Cu, RequestProfileDetailsPacket),
QUERY_NICKNAME(0x0126u, QueryNicknamePacket),
QUERY_PREVIOUS_NAME(0x01BCu, QueryPreviousNamePacket),
QUERY_FRIEND_REMARK(0x003Eu, QueryFriendRemarkPacket)
// 031F 查询 "新朋友" 记录
// @Suppress("DEPRECATION")
// inline SUBMIT_IMAGE_FILE_NAME(0x01BDu, SubmitImageFilenamePacket),
;
init {
factory._id = this
}
override fun toString(): String = (factory::class.simpleName ?: this.name) + "(${value.toUHexString()})"
}

View File

@ -8,8 +8,9 @@ import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.EventPacket
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.data.PreviousNameList
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildSessionPacket
import net.mamoe.mirai.utils.io.*

View File

@ -4,14 +4,15 @@ package net.mamoe.mirai.timpc.network.packet.action
import kotlinx.io.charsets.Charsets
import kotlinx.io.core.*
import net.mamoe.mirai.data.EventPacket
import net.mamoe.mirai.data.ImageLink
import net.mamoe.mirai.message.data.ImageId
import net.mamoe.mirai.message.data.ImageId0x06
import net.mamoe.mirai.message.data.requireLength
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.EventPacket
import net.mamoe.mirai.data.ImageLink
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildSessionPacket
import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.io.*

View File

@ -5,8 +5,8 @@ package net.mamoe.mirai.timpc.network.packet.action
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.timpc.network.packet.PacketId
import net.mamoe.mirai.timpc.network.packet.SessionPacketFactory
import net.mamoe.mirai.network.packet.PacketId
import net.mamoe.mirai.network.packet.SessionPacketFactory
// 0001

View File

@ -7,8 +7,9 @@ import kotlinx.io.core.writeFully
import kotlinx.io.core.writeUByte
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildOutgoingPacket
import net.mamoe.mirai.utils.io.writeQQ
/**

View File

@ -11,7 +11,8 @@ import net.mamoe.mirai.message.data.requireLength
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.EventPacket
import net.mamoe.mirai.data.ImageLink
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildSessionProtoPacket
import net.mamoe.mirai.timpc.utils.assertUnreachable
import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.io.toUHexString

View File

@ -6,17 +6,17 @@ import kotlinx.io.core.*
import net.mamoe.mirai.contact.GroupInternalId
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.contact.groupInternalId
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.internal.toPacket
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.internal.RawGroupInfo
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildSessionPacket
import net.mamoe.mirai.timpc.utils.unsupportedFlag
import net.mamoe.mirai.timpc.utils.unsupportedType
import net.mamoe.mirai.utils.io.*
import kotlin.collections.mutableMapOf
import kotlin.collections.set

View File

@ -5,10 +5,11 @@ package net.mamoe.mirai.timpc.network.packet.action
import io.ktor.util.date.GMTDate
import kotlinx.io.core.*
import net.mamoe.mirai.data.Gender
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.data.Profile
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildSessionPacket
import net.mamoe.mirai.utils.io.*
inline class AvatarLink(val value: String) : Packet

View File

@ -6,7 +6,8 @@ import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.FriendNameRemark
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildSessionPacket
import net.mamoe.mirai.utils.io.readUShortLVString
import net.mamoe.mirai.utils.io.writeQQ
import net.mamoe.mirai.utils.io.writeZero

View File

@ -5,8 +5,9 @@ package net.mamoe.mirai.timpc.network.packet.action
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildSessionPacket
import net.mamoe.mirai.utils.io.writeZero
class FriendList : Packet

View File

@ -7,8 +7,9 @@ import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.internal.toPacket
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildSessionPacket
import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.md5

View File

@ -6,7 +6,7 @@ import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import net.mamoe.mirai.Bot
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.timpc.network.packet.PacketVersion
import net.mamoe.mirai.network.packet.PacketVersion
import net.mamoe.mirai.utils.io.readBoolean

View File

@ -6,9 +6,10 @@ import kotlinx.io.core.*
import net.mamoe.mirai.Bot
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.TIMBotNetworkHandler
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.timpc.network.packet.buildSessionPacket
import net.mamoe.mirai.utils.io.readIoBuffer
/**

View File

@ -6,7 +6,7 @@ import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import net.mamoe.mirai.Bot
import net.mamoe.mirai.event.events.ReceiveFriendAddRequestEvent
import net.mamoe.mirai.timpc.network.packet.PacketVersion
import net.mamoe.mirai.network.packet.PacketVersion
import net.mamoe.mirai.utils.io.readQQ
import net.mamoe.mirai.utils.io.readUShortLVString

View File

@ -4,10 +4,9 @@ package net.mamoe.mirai.timpc.network.packet.event
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import kotlinx.io.core.readUInt
import net.mamoe.mirai.Bot
import net.mamoe.mirai.data.EventPacket
import net.mamoe.mirai.timpc.network.packet.PacketVersion
import net.mamoe.mirai.network.packet.PacketVersion
import net.mamoe.mirai.utils.io.readQQ

View File

@ -5,12 +5,10 @@ package net.mamoe.mirai.timpc.network.packet.event
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import kotlinx.io.core.readUByte
import kotlinx.io.core.readUInt
import net.mamoe.mirai.event.events.FriendStatusChanged
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.timpc.network.packet.KnownPacketId
import net.mamoe.mirai.timpc.network.packet.PacketId
import net.mamoe.mirai.timpc.network.packet.SessionPacketFactory
import net.mamoe.mirai.network.packet.PacketId
import net.mamoe.mirai.network.packet.SessionPacketFactory
import net.mamoe.mirai.utils.OnlineStatus
import net.mamoe.mirai.utils.io.readQQ

View File

@ -5,7 +5,7 @@ package net.mamoe.mirai.timpc.network.packet.event
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.Bot
import net.mamoe.mirai.data.EventPacket
import net.mamoe.mirai.timpc.network.packet.PacketVersion
import net.mamoe.mirai.network.packet.PacketVersion
import net.mamoe.mirai.utils.io.debugPrint

View File

@ -5,11 +5,11 @@ package net.mamoe.mirai.timpc.network.packet.event
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import net.mamoe.mirai.Bot
import net.mamoe.mirai.data.EventPacket
import net.mamoe.mirai.timpc.network.packet.PacketVersion
import net.mamoe.mirai.utils.io.readQQ
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.data.EventPacket
import net.mamoe.mirai.network.packet.PacketVersion
import net.mamoe.mirai.utils.io.readQQ
data class MemberPermissionChangePacket(

View File

@ -7,7 +7,7 @@ import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.message.GroupMessage
import net.mamoe.mirai.message.internal.readMessageChain
import net.mamoe.mirai.message.FriendMessage
import net.mamoe.mirai.timpc.network.packet.PacketVersion
import net.mamoe.mirai.network.packet.PacketVersion
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.io.*

View File

@ -5,11 +5,13 @@ package net.mamoe.mirai.timpc.network.packet.login
import kotlinx.io.core.*
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildOutgoingPacket
import net.mamoe.mirai.utils.io.*
internal object CaptchaKey : DecrypterByteArray, DecrypterType<CaptchaKey> {
internal object CaptchaKey : DecrypterByteArray,
DecrypterType<CaptchaKey> {
override val value: ByteArray = TIMProtocol.key00BA
}

View File

@ -7,8 +7,9 @@ import kotlinx.io.core.writeFully
import kotlinx.io.core.writeUByte
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildOutgoingPacket
import net.mamoe.mirai.utils.OnlineStatus
import net.mamoe.mirai.utils.io.writeHex
import net.mamoe.mirai.utils.io.writeQQ
@ -16,7 +17,9 @@ import net.mamoe.mirai.utils.io.writeQQ
/**
* 改变在线状态: "我在线上", "隐身"
*/
internal object ChangeOnlineStatusPacket : PacketFactory<ChangeOnlineStatusPacket.ChangeOnlineStatusResponse, NoDecrypter>(NoDecrypter) {
internal object ChangeOnlineStatusPacket : PacketFactory<ChangeOnlineStatusPacket.ChangeOnlineStatusResponse, NoDecrypter>(
NoDecrypter
) {
operator fun invoke(
bot: Long,
sessionKey: SessionKey,

View File

@ -7,8 +7,9 @@ import kotlinx.io.core.writeFully
import net.mamoe.mirai.event.Subscribable
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildOutgoingPacket
import net.mamoe.mirai.utils.io.writeHex
import net.mamoe.mirai.utils.io.writeQQ

View File

@ -7,12 +7,14 @@ import net.mamoe.mirai.data.Gender
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.LoginResult
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildOutgoingPacket
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.io.*
internal object ShareKey : DecrypterByteArray, DecrypterType<ShareKey> {
internal object ShareKey : DecrypterByteArray,
DecrypterType<ShareKey> {
override val value: ByteArray = TIMProtocol.shareKey
}

View File

@ -7,8 +7,9 @@ import kotlinx.io.core.discardExact
import kotlinx.io.core.writeFully
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildOutgoingPacket
import net.mamoe.mirai.utils.io.*
internal inline class SKey(

View File

@ -5,8 +5,11 @@ package net.mamoe.mirai.timpc.network.packet.login
import kotlinx.io.core.*
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.network.packet.PacketFactory
import net.mamoe.mirai.network.packet.PacketId
import net.mamoe.mirai.network.packet.SessionKey
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildOutgoingPacket
import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.localIpAddress

View File

@ -6,13 +6,15 @@ import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import kotlinx.io.core.readBytes
import kotlinx.io.core.writeFully
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.buildOutgoingPacket
import net.mamoe.mirai.utils.io.*
internal object TouchKey : DecrypterByteArray, DecrypterType<TouchKey> {
internal object TouchKey : DecrypterByteArray,
DecrypterType<TouchKey> {
override val value: ByteArray = TIMProtocol.touchKey
}

View File

@ -16,13 +16,12 @@ import kotlinx.serialization.internal.ArrayListSerializer
import kotlinx.serialization.json.Json
import net.mamoe.mirai.Bot
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.timpc.TIMPC
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.event.FriendOnlineStatusChangedPacket
import net.mamoe.mirai.timpc.network.packet.event.IgnoredEventPacket
import net.mamoe.mirai.timpc.network.packet.login.CaptchaKey
import net.mamoe.mirai.timpc.network.packet.login.ShareKey
import net.mamoe.mirai.timpc.network.packet.login.TouchKey
import net.mamoe.mirai.timpc.network.packet.login.*
import net.mamoe.mirai.utils.DecryptionFailedException
import net.mamoe.mirai.utils.decryptBy
import net.mamoe.mirai.utils.io.*
@ -181,7 +180,8 @@ internal object PacketDebugger {
* 7. 运行完 `mov eax,dword ptr ss:[ebp+10]`
* 8. 查看内存, `eax` `eax+10` 16 字节就是 `sessionKey`
*/
val sessionKey: SessionKey = SessionKey("95 F3 24 8E 7B B6 62 AA 98 C0 EE 45 CE CE 2B 69".hexToBytes())
val sessionKey: SessionKey =
SessionKey("95 F3 24 8E 7B B6 62 AA 98 C0 EE 45 CE CE 2B 69".hexToBytes())
// TODO: 2019/12/7 无法访问 internal 是 kotlin bug, KT-34849
/**
@ -194,9 +194,9 @@ internal object PacketDebugger {
val recorder: Recorder? = Recorder()
val IgnoredPacketIdList: List<PacketId> = listOf(
KnownPacketId.FRIEND_ONLINE_STATUS_CHANGE,
KnownPacketId.CHANGE_ONLINE_STATUS,
KnownPacketId.HEARTBEAT
KnownPacketId.get<FriendOnlineStatusChangedPacket>(),
KnownPacketId.get<ChangeOnlineStatusPacket>(),
KnownPacketId.get<HeartbeatPacket>()
)
suspend fun dataReceived(data: ByteArray) {
@ -204,10 +204,10 @@ internal object PacketDebugger {
//println("raw = " + data.toUHexString())
data.read {
discardExact(3)
val id = matchPacketId(readUShort())
val id = net.mamoe.mirai.network.packet.matchPacketId(readUShort())
val sequenceId = readUShort()
val packetQQ = readQQ()
if (id == KnownPacketId.HEARTBEAT || (qq != null && packetQQ != qq))
if (id == KnownPacketId.get<HeartbeatPacket>() || (qq != null && packetQQ != qq))
return@read
if (IgnoredPacketIdList.contains(id)) {
@ -301,7 +301,7 @@ internal object PacketDebugger {
// 3E 03 3F A2 02 00 00 00 01 2E 01 00 00 69 35
discardExact(3)//head
val id = matchPacketId(readUShort())
val id = net.mamoe.mirai.network.packet.matchPacketId(readUShort())
val sequence = readUShort().toUHexString()
if (IgnoredPacketIdList.contains(id)) {
return

View File

@ -3,8 +3,8 @@ package packetdebugger
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import kotlinx.io.core.readUShort
import net.mamoe.mirai.timpc.network.packet.PacketId
import net.mamoe.mirai.timpc.network.packet.matchPacketId
import net.mamoe.mirai.network.packet.PacketId
import net.mamoe.mirai.network.packet.matchPacketId
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract

View File

@ -19,14 +19,18 @@ import kotlin.coroutines.CoroutineContext
*/
@MiraiInternalAPI
abstract class BotImpl<N : BotNetworkHandler> constructor(
override val account: BotAccount,
override val logger: MiraiLogger = DefaultLogger("Bot(" + account.id + ")"),
account: BotAccount,
logger: MiraiLogger?,
context: CoroutineContext
) : Bot(), CoroutineScope {
private val supervisorJob = SupervisorJob(context[Job])
override val coroutineContext: CoroutineContext =
context + supervisorJob + CoroutineExceptionHandler { _, e -> e.logStacktrace("An exception was thrown under a coroutine of Bot") }
@Suppress("CanBePrimaryConstructorProperty") // for logger
override val account: BotAccount = account
override val logger: MiraiLogger = logger ?: DefaultLogger("Bot(" + account.id + ")")
init {
@Suppress("LeakingThis")
instances.addLast(this)

View File

@ -1,6 +1,6 @@
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused")
package net.mamoe.mirai.timpc.network.packet
package net.mamoe.mirai.network.packet
/**
* 包的最后一次修改时间, 和分析时使用的 TIM 版本
@ -8,11 +8,11 @@ package net.mamoe.mirai.timpc.network.packet
@MustBeDocumented
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.SOURCE)
internal annotation class PacketVersion(val date: String, val timVersion: String)
annotation class PacketVersion(val date: String, val timVersion: String)
/**
* 带有这个注解的 [Packet] 将不会被记录在 log .
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class NoLog
annotation class NoLog

View File

@ -1,26 +1,23 @@
package net.mamoe.mirai.timpc.network.packet
package net.mamoe.mirai.network.packet
import kotlinx.io.core.BytePacketBuilder
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.IoBuffer
import kotlinx.io.core.writeFully
import net.mamoe.mirai.utils.decryptBy
import net.mamoe.mirai.utils.encryptBy
import net.mamoe.mirai.utils.io.encryptAndWrite
/**
* 会话密匙
*/
internal inline class SessionKey(override val value: ByteArray) : DecrypterByteArray {
inline class SessionKey(override val value: ByteArray) : DecrypterByteArray {
companion object Type : DecrypterType<SessionKey>
}
/**
* [ByteArray] 解密器
*/
@PublishedApi
internal interface DecrypterByteArray : Decrypter {
interface DecrypterByteArray : Decrypter {
val value: ByteArray
override fun decrypt(input: ByteReadPacket): ByteReadPacket = input.decryptBy(value)
}
@ -28,7 +25,7 @@ internal interface DecrypterByteArray : Decrypter {
/**
* [IoBuffer] 解密器
*/
internal interface DecrypterIoBuffer : Decrypter {
interface DecrypterIoBuffer : Decrypter {
val value: IoBuffer
override fun decrypt(input: ByteReadPacket): ByteReadPacket = input.decryptBy(value)
}
@ -36,27 +33,28 @@ internal interface DecrypterIoBuffer : Decrypter {
/**
* 连接在一起的解密器
*/
internal inline class LinkedDecrypter(inline val block: (ByteReadPacket) -> ByteReadPacket) : Decrypter {
inline class LinkedDecrypter(inline val block: (ByteReadPacket) -> ByteReadPacket) : Decrypter {
override fun decrypt(input: ByteReadPacket): ByteReadPacket = block(input)
}
internal object NoDecrypter : Decrypter, DecrypterType<NoDecrypter> {
object NoDecrypter : Decrypter,
DecrypterType<NoDecrypter> {
override fun decrypt(input: ByteReadPacket): ByteReadPacket = input
}
/**
* 解密器
*/
internal interface Decrypter {
*/
interface Decrypter {
fun decrypt(input: ByteReadPacket): ByteReadPacket
/**
* 连接后将会先用 this 解密, 再用 [another] 解密
*/
operator fun plus(another: Decrypter): Decrypter = LinkedDecrypter { another.decrypt(this.decrypt(it)) }
operator fun plus(another: Decrypter): Decrypter =
LinkedDecrypter { another.decrypt(this.decrypt(it)) }
}
interface DecrypterType<D : Decrypter>
internal interface DecrypterType<D : Decrypter>
@PublishedApi
internal inline fun BytePacketBuilder.encryptAndWrite(key: DecrypterByteArray, encoder: BytePacketBuilder.() -> Unit) =
inline fun BytePacketBuilder.encryptAndWrite(key: DecrypterByteArray, encoder: BytePacketBuilder.() -> Unit) =
this.encryptAndWrite(key.value, encoder)

View File

@ -1,13 +1,13 @@
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS", "unused", "MemberVisibilityCanBePrivate")
package net.mamoe.mirai.timpc.network.packet
package net.mamoe.mirai.network.packet
import kotlinx.io.core.*
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.protobuf.ProtoBuf
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.io.hexToBytes
import net.mamoe.mirai.utils.io.writeQQ
import kotlin.contracts.ExperimentalContracts
@ -18,11 +18,11 @@ import kotlin.jvm.JvmOverloads
/**
* 待发送给服务器的数据包. 它代表着一个 [ByteReadPacket],
*/
internal class OutgoingPacket(
class OutgoingPacket(
name: String?,
val packetId: PacketId,
val sequenceId: UShort,
internal val delegate: ByteReadPacket
val delegate: ByteReadPacket
) : Packet {
val name: String by lazy {
name ?: packetId.toString()
@ -35,7 +35,9 @@ internal class OutgoingPacket(
*
* @param TPacket invariant
*/
internal abstract class SessionPacketFactory<TPacket : Packet> : PacketFactory<TPacket, SessionKey>(SessionKey) {
abstract class SessionPacketFactory<TPacket : Packet> : PacketFactory<TPacket, SessionKey>(
SessionKey
) {
/**
* [BotNetworkHandler] 下处理这个包. 广播事件等.
*/
@ -45,13 +47,16 @@ internal abstract class SessionPacketFactory<TPacket : Packet> : PacketFactory<T
/**
* 构造一个待发送给服务器的数据包.
*/
@UseExperimental(ExperimentalContracts::class)
@UseExperimental(ExperimentalContracts::class, MiraiInternalAPI::class)
@JvmOverloads
internal inline fun PacketFactory<*, *>.buildOutgoingPacket(
inline fun PacketFactory<*, *>.buildOutgoingPacket0(
name: String? = null,
id: PacketId = this.id,
sequenceId: UShort = PacketFactory.atomicNextSequenceId(),
headerSizeHint: Int = 0,
head: ByteArray,
ver: ByteArray,
tail: ByteArray,
block: BytePacketBuilder.() -> Unit
): OutgoingPacket {
contract {
@ -60,12 +65,12 @@ internal inline fun PacketFactory<*, *>.buildOutgoingPacket(
BytePacketBuilder(headerSizeHint).use {
with(it) {
writeFully(TIMProtocol.head)
writeFully(TIMProtocol.ver)
writeFully(head)
writeFully(ver)
writeUShort(id.value)
writeUShort(sequenceId)
block(this)
writeFully(TIMProtocol.tail)
writeFully(tail)
}
return OutgoingPacket(name, id, sequenceId, it.build())
}
@ -75,22 +80,33 @@ internal inline fun PacketFactory<*, *>.buildOutgoingPacket(
/**
* 构造一个待发送给服务器的会话数据包.
*/
@UseExperimental(ExperimentalContracts::class)
@UseExperimental(ExperimentalContracts::class, MiraiInternalAPI::class)
@JvmOverloads
internal inline fun PacketFactory<*, *>.buildSessionPacket(
inline fun PacketFactory<*, *>.buildSessionPacket0(
bot: Long,
sessionKey: SessionKey,
name: String? = null,
id: PacketId = this.id,
sequenceId: UShort = PacketFactory.atomicNextSequenceId(),
headerSizeHint: Int = 0,
version: ByteArray = TIMProtocol.version0x02,
version: ByteArray, // in packet body
head: ByteArray,
ver: ByteArray, // in packet head
tail: ByteArray,
block: BytePacketBuilder.() -> Unit
): OutgoingPacket {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return buildOutgoingPacket(name, id, sequenceId, headerSizeHint) {
return buildOutgoingPacket0(
name = name,
id = id,
sequenceId = sequenceId,
headerSizeHint = headerSizeHint,
head = head,
ver = ver,
tail = tail
) {
writeQQ(bot)
writeFully(version)
encryptAndWrite(sessionKey) {
@ -102,22 +118,25 @@ internal inline fun PacketFactory<*, *>.buildSessionPacket(
/**
* 构造一个待发送给服务器的会话数据包.
*/
@UseExperimental(ExperimentalContracts::class)
@UseExperimental(ExperimentalContracts::class, MiraiInternalAPI::class)
@JvmOverloads
internal fun <T> PacketFactory<*, *>.buildSessionProtoPacket(
fun <T> PacketFactory<*, *>.buildSessionProtoPacket0(
bot: Long,
sessionKey: SessionKey,
name: String? = null,
id: PacketId = this.id,
sequenceId: UShort = PacketFactory.atomicNextSequenceId(),
headerSizeHint: Int = 0,
version: ByteArray = TIMProtocol.version0x04,
version: ByteArray,
head: Any,
serializer: SerializationStrategy<T>,
protoObj: T
protoObj: T,
packetHead: ByteArray,
ver: ByteArray, // in packet head
tail: ByteArray
): OutgoingPacket {
require(head is ByteArray || head is UByteArray || head is String) { "Illegal head type" }
return buildOutgoingPacket(name, id, sequenceId, headerSizeHint) {
return buildOutgoingPacket0(name, id, sequenceId, headerSizeHint, head = packetHead, ver = ver, tail = tail) {
writeQQ(bot)
writeFully(version)
encryptAndWrite(sessionKey) {
@ -136,17 +155,20 @@ internal fun <T> PacketFactory<*, *>.buildSessionProtoPacket(
writeFully(head)
writeFully(proto)
}
is String -> buildSessionProtoPacket(
bot,
sessionKey,
name,
id,
sequenceId,
headerSizeHint,
version,
head.hexToBytes(),
serializer,
protoObj
is String -> buildSessionProtoPacket0(
bot = bot,
sessionKey = sessionKey,
name = name,
id = id,
sequenceId = sequenceId,
headerSizeHint = headerSizeHint,
version = version,
head = head.hexToBytes(),
serializer = serializer,
protoObj = protoObj,
packetHead = packetHead,
ver = ver,
tail = tail
)
}
}

View File

@ -1,4 +1,4 @@
package net.mamoe.mirai.timpc.network.packet
package net.mamoe.mirai.network.packet
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.readBytes
@ -8,18 +8,18 @@ import net.mamoe.mirai.utils.io.toUHexString
/**
* 被忽略的数据包.
*/
internal inline class IgnoredPacket(internal val id: PacketId) : Packet
inline class IgnoredPacket(internal val id: PacketId) : Packet
/**
* 未知的包.
*/
internal class UnknownPacket(val id: PacketId, val body: ByteReadPacket) : Packet {
class UnknownPacket(val id: PacketId, val body: ByteReadPacket) : Packet {
override fun toString(): String = "UnknownPacket(${id.value.toUHexString()})\nbody=${body.readBytes().toUHexString()}"
}
/**
* 仅用于替换类型应为 [Unit] 的情况
*/
internal object NoPacket : Packet {
object NoPacket : Packet {
override fun toString(): String = "NoPacket"
}

View File

@ -1,6 +1,6 @@
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
package net.mamoe.mirai.timpc.network.packet
package net.mamoe.mirai.network.packet
import kotlinx.atomicfu.atomic
import kotlinx.io.core.ByteReadPacket
@ -11,6 +11,7 @@ import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.protobuf.ProtoBuf
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.io.ByteArrayPool
import net.mamoe.mirai.utils.io.debugPrint
import net.mamoe.mirai.utils.io.read
@ -24,7 +25,7 @@ import net.mamoe.mirai.utils.readProtoMap
* @param TPacket 服务器回复包解析结果
* @param TDecrypter 服务器回复包解密器
*/
internal abstract class PacketFactory<out TPacket : Packet, TDecrypter : Decrypter>(val decrypterType: DecrypterType<TDecrypter>) {
abstract class PacketFactory<out TPacket : Packet, TDecrypter : Decrypter>(val decrypterType: DecrypterType<TDecrypter>) {
@Suppress("PropertyName")
internal var _id: PacketId = NullPacketId
@ -64,8 +65,8 @@ internal abstract class PacketFactory<out TPacket : Packet, TDecrypter : Decrypt
companion object {
private val sequenceIdInternal = atomic(1)
@PublishedApi
internal fun atomicNextSequenceId(): UShort = sequenceIdInternal.getAndIncrement().toUShort()
@MiraiInternalAPI
fun atomicNextSequenceId(): UShort = sequenceIdInternal.getAndIncrement().toUShort()
}
}

View File

@ -0,0 +1,76 @@
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
package net.mamoe.mirai.network.packet
import net.mamoe.mirai.utils.io.toUHexString
/**
* 通过 [value] 匹配一个 [IgnoredPacketId] [KnownPacketId], 无匹配则返回一个 [UnknownPacketId].
*/
fun matchPacketId(value: UShort): PacketId =
IgnoredPacketIds.firstOrNull { it.value == value }
?: KnownPacketId.entries.firstOrNull { it.value.value == value }?.value
?: UnknownPacketId(value)
/**
* ID.
*/
interface PacketId {
val value: UShort
val factory: PacketFactory<*, *>
}
/**
* 用于代表 `null`. 调用任何属性时都将会得到一个 [error]
*/
@Suppress("unused")
object NullPacketId : PacketId {
override val factory: PacketFactory<*, *> get() = error("uninitialized")
override val value: UShort get() = error("uninitialized")
override fun toString(): String = "NullPacketId"
}
/**
* 未知的 [PacketId]
*/
inline class UnknownPacketId(override inline val value: UShort) : PacketId {
override val factory: PacketFactory<*, *> get() = UnknownPacketFactory
override fun toString(): String = "UnknownPacketId(${value.toUHexString()})"
}
object IgnoredPacketIds : List<IgnoredPacketId> by {
listOf<UShort>(
).map { IgnoredPacketId(it.toUShort()) }
}()
inline class IgnoredPacketId constructor(override val value: UShort) : PacketId {
override val factory: PacketFactory<*, *> get() = IgnoredPacketFactory
override fun toString(): String = "IgnoredPacketId(${value.toUHexString()})"
}
class KnownPacketId(override val value: UShort, override val factory: PacketFactory<*, *>) : PacketId {
companion object : MutableMap<UShort, KnownPacketId> by mutableMapOf() {
operator fun set(key: UShort, factory: PacketFactory<*, *>) {
this[key] = KnownPacketId(key, factory)
}
inline fun <reified PF : PacketFactory<*, *>> getOrNull(): KnownPacketId? {
val clazz = PF::class
this.forEach {
if (clazz.isInstance(it.value)) {
return it.value
}
}
return null
}
inline fun <reified PF : PacketFactory<*, *>> get(): KnownPacketId = getOrNull<PF>() ?: throw NoSuchElementException()
}
override fun toString(): String = (factory::class.simpleName ?: factory::class.simpleName) + "(${value.toUHexString()})"
init {
factory._id = this
}
}