mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-29 01:01:19 +08:00
Merge branch 'dev'
This commit is contained in:
commit
713308331d
@ -1,5 +1,8 @@
|
|||||||
# Version 1.x
|
# Version 1.x
|
||||||
|
|
||||||
|
## `1.2.2` 2020/8/22
|
||||||
|
- 修复依赖冲突问题 (#523)
|
||||||
|
|
||||||
## `1.2.1` 2020/8/19
|
## `1.2.1` 2020/8/19
|
||||||
- 修复在 Java 调用 `group.uploadImage` 时编译出错的问题 (#511)
|
- 修复在 Java 调用 `group.uploadImage` 时编译出错的问题 (#511)
|
||||||
- 为 `group.uploadVoice` 添加 Java 方法 (需要 [kotlin-jvm-blocking-bridge](https://github.com/mamoe/kotlin-jvm-blocking-bridge)) (#512)
|
- 为 `group.uploadVoice` 添加 Java 方法 (需要 [kotlin-jvm-blocking-bridge](https://github.com/mamoe/kotlin-jvm-blocking-bridge)) (#512)
|
||||||
@ -8,7 +11,7 @@
|
|||||||
## `1.2.0` 2020/8/19
|
## `1.2.0` 2020/8/19
|
||||||
|
|
||||||
### 新特性
|
### 新特性
|
||||||
- 初步语音支持: `Group.uploadVoice`, 支持 silk 或 arm 格式.
|
- 初步语音支持: `Group.uploadVoice`, 支持 silk 或 amr 格式.
|
||||||
**注意**: 现阶段语音实现仅为临时方案, 在将来 (`2.0.0`) 一定会变动. 使用时请评估可能带来的不兼容性.
|
**注意**: 现阶段语音实现仅为临时方案, 在将来 (`2.0.0`) 一定会变动. 使用时请评估可能带来的不兼容性.
|
||||||
|
|
||||||
- 新增将日志转换为 log4j, JDK Logger, SLF4J 等框架的方法: `LoggerAdapters` (#498 by [@Karlatemp](https://github.com/Karlatemp))
|
- 新增将日志转换为 log4j, JDK Logger, SLF4J 等框架的方法: `LoggerAdapters` (#498 by [@Karlatemp](https://github.com/Karlatemp))
|
||||||
|
@ -8,6 +8,12 @@ mirai 欢迎一切形式的代码贡献。你可以通过以下几种途径向 m
|
|||||||
|
|
||||||
**阅读文档**: [docs/mirai.md](docs/mirai.md)
|
**阅读文档**: [docs/mirai.md](docs/mirai.md)
|
||||||
|
|
||||||
|
### 构建
|
||||||
|
- 要构建项目, 请运行 `gradlew assemble`
|
||||||
|
- 要运行测试, 请运行 `gradlew test`
|
||||||
|
- 要构建项目并运行测试, 请运行 `gradlew build`
|
||||||
|
- 若要添加一个 suspend 函数, 请务必考虑 Java 兼容性, 使用 [kotlin-jvm-blocking-bridge](https://github.com/mamoe/kotlin-jvm-blocking-bridge/blob/master/README-chs.md)
|
||||||
|
|
||||||
### 能做什么?
|
### 能做什么?
|
||||||
|
|
||||||
**请基于 `master` 分支进行文档修改; 基于 `dev` 分支进行其他修改 (否则你的修改可能被关闭或不会立即合并)**
|
**请基于 `master` 分支进行文档修改; 基于 `dev` 分支进行其他修改 (否则你的修改可能被关闭或不会立即合并)**
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
object Versions {
|
object Versions {
|
||||||
object Mirai {
|
object Mirai {
|
||||||
const val version = "1.2.1"
|
const val version = "1.2.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
object Kotlin {
|
object Kotlin {
|
||||||
|
@ -60,14 +60,12 @@ kotlin {
|
|||||||
|
|
||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
api(kotlinx("serialization-core", Versions.Kotlin.serialization))
|
api1(kotlinx("serialization-core", Versions.Kotlin.serialization))
|
||||||
api(kotlinx("coroutines-core", Versions.Kotlin.coroutines))
|
api(kotlinx("coroutines-core", Versions.Kotlin.coroutines))
|
||||||
implementation(kotlinx("serialization-protobuf", Versions.Kotlin.serialization))
|
implementation1(kotlinx("serialization-protobuf", Versions.Kotlin.serialization))
|
||||||
api("org.jetbrains.kotlinx:atomicfu:${Versions.Kotlin.atomicFU}")
|
api1("org.jetbrains.kotlinx:atomicfu:${Versions.Kotlin.atomicFU}")
|
||||||
api(kotlinx("io", Versions.Kotlin.io)) {
|
api1(kotlinx("io", Versions.Kotlin.io))
|
||||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
implementation1(kotlinx("coroutines-io", Versions.Kotlin.coroutinesIo))
|
||||||
}
|
|
||||||
implementation(kotlinx("coroutines-io", Versions.Kotlin.coroutinesIo))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,11 +95,8 @@ kotlin {
|
|||||||
|
|
||||||
val jvmMain by getting {
|
val jvmMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE
|
|
||||||
implementation("org.bouncycastle:bcprov-jdk15on:1.64")
|
implementation("org.bouncycastle:bcprov-jdk15on:1.64")
|
||||||
api(kotlinx("io-jvm", Versions.Kotlin.io)) {
|
api1(kotlinx("io-jvm", Versions.Kotlin.io))
|
||||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
|
||||||
}
|
|
||||||
// api(kotlinx("coroutines-debug", Versions.Kotlin.coroutines))
|
// api(kotlinx("coroutines-debug", Versions.Kotlin.coroutines))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,14 +107,29 @@ kotlin {
|
|||||||
implementation(kotlin("test", Versions.Kotlin.compiler))
|
implementation(kotlin("test", Versions.Kotlin.compiler))
|
||||||
implementation(kotlin("test-junit", Versions.Kotlin.compiler))
|
implementation(kotlin("test-junit", Versions.Kotlin.compiler))
|
||||||
implementation("org.pcap4j:pcap4j-distribution:1.8.2")
|
implementation("org.pcap4j:pcap4j-distribution:1.8.2")
|
||||||
|
|
||||||
runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE
|
|
||||||
runtimeOnly(files("build/classes/kotlin/jvm/test")) // classpath is not properly set by IDE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.implementation1(dependencyNotation: String) =
|
||||||
|
implementation(dependencyNotation) {
|
||||||
|
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core-common")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core-jvm")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core-metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.api1(dependencyNotation: String) =
|
||||||
|
api(dependencyNotation) {
|
||||||
|
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core-common")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core-jvm")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core-metadata")
|
||||||
|
}
|
||||||
|
|
||||||
apply(from = rootProject.file("gradle/publish.gradle"))
|
apply(from = rootProject.file("gradle/publish.gradle"))
|
||||||
|
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ internal class MemberImpl constructor(
|
|||||||
check(this.id != bot.id) {
|
check(this.id != bot.id) {
|
||||||
"A bot can't mute itself."
|
"A bot can't mute itself."
|
||||||
}
|
}
|
||||||
checkBotPermissionHigherThanThis()
|
checkBotPermissionHigherThanThis("mute")
|
||||||
bot.network.run {
|
bot.network.run {
|
||||||
TroopManagement.Mute(
|
TroopManagement.Mute(
|
||||||
client = bot.client,
|
client = bot.client,
|
||||||
@ -194,10 +194,10 @@ internal class MemberImpl constructor(
|
|||||||
net.mamoe.mirai.event.events.MemberMuteEvent(this@MemberImpl, durationSeconds, null).broadcast()
|
net.mamoe.mirai.event.events.MemberMuteEvent(this@MemberImpl, durationSeconds, null).broadcast()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkBotPermissionHigherThanThis() {
|
private fun checkBotPermissionHigherThanThis(operationName: String) {
|
||||||
check(group.botPermission > this.permission) {
|
check(group.botPermission > this.permission) {
|
||||||
throw PermissionDeniedException(
|
throw PermissionDeniedException(
|
||||||
"`kick` operation requires bot to have a higher permission than the target member, " +
|
"`$operationName` operation requires bot to have a higher permission than the target member, " +
|
||||||
"but bot's is ${group.botPermission}, target's is ${this.permission}"
|
"but bot's is ${group.botPermission}, target's is ${this.permission}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -205,7 +205,7 @@ internal class MemberImpl constructor(
|
|||||||
|
|
||||||
@JvmSynthetic
|
@JvmSynthetic
|
||||||
override suspend fun unmute() {
|
override suspend fun unmute() {
|
||||||
checkBotPermissionHigherThanThis()
|
checkBotPermissionHigherThanThis("unmute")
|
||||||
bot.network.run {
|
bot.network.run {
|
||||||
TroopManagement.Mute(
|
TroopManagement.Mute(
|
||||||
client = bot.client,
|
client = bot.client,
|
||||||
@ -222,7 +222,7 @@ internal class MemberImpl constructor(
|
|||||||
|
|
||||||
@JvmSynthetic
|
@JvmSynthetic
|
||||||
override suspend fun kick(message: String) {
|
override suspend fun kick(message: String) {
|
||||||
checkBotPermissionHigherThanThis()
|
checkBotPermissionHigherThanThis("kick")
|
||||||
check(group.members.getOrNull(this.id) != null) {
|
check(group.members.getOrNull(this.id) != null) {
|
||||||
"Member ${this.id} had already been kicked from group ${group.id}"
|
"Member ${this.id} had already been kicked from group ${group.id}"
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ internal class MsgSvc : ProtoBuf {
|
|||||||
@ProtoNumber(1) @JvmField val result: Int = 0,
|
@ProtoNumber(1) @JvmField val result: Int = 0,
|
||||||
@ProtoNumber(2) @JvmField val errmsg: String = "",
|
@ProtoNumber(2) @JvmField val errmsg: String = "",
|
||||||
@ProtoNumber(3) @JvmField val syncCookie: ByteArray? = EMPTY_BYTE_ARRAY,
|
@ProtoNumber(3) @JvmField val syncCookie: ByteArray? = EMPTY_BYTE_ARRAY,
|
||||||
@ProtoNumber(4) @JvmField val syncFlag: SyncFlag,
|
@ProtoNumber(4) @JvmField val syncFlag: SyncFlag = SyncFlag.CONTINUE,
|
||||||
@ProtoNumber(5) @JvmField val uinPairMsgs: List<MsgComm.UinPairMsg>? = null,
|
@ProtoNumber(5) @JvmField val uinPairMsgs: List<MsgComm.UinPairMsg>? = null,
|
||||||
@ProtoNumber(6) @JvmField val bindUin: Long = 0L,
|
@ProtoNumber(6) @JvmField val bindUin: Long = 0L,
|
||||||
@ProtoNumber(7) @JvmField val msgRspType: Int = 0,
|
@ProtoNumber(7) @JvmField val msgRspType: Int = 0,
|
||||||
|
@ -14,6 +14,7 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
|
|||||||
import kotlinx.atomicfu.loop
|
import kotlinx.atomicfu.loop
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.io.core.ByteReadPacket
|
import kotlinx.io.core.ByteReadPacket
|
||||||
import kotlinx.io.core.discardExact
|
import kotlinx.io.core.discardExact
|
||||||
@ -43,6 +44,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
|
|||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.NewContact
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.NewContact
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
|
||||||
|
import net.mamoe.mirai.qqandroid.utils._miraiContentToString
|
||||||
import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
|
import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
|
||||||
import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf
|
import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf
|
||||||
import net.mamoe.mirai.qqandroid.utils.read
|
import net.mamoe.mirai.qqandroid.utils.read
|
||||||
@ -56,6 +58,12 @@ import net.mamoe.mirai.utils.warning
|
|||||||
* 获取好友消息和消息记录
|
* 获取好友消息和消息记录
|
||||||
*/
|
*/
|
||||||
internal object MessageSvcPbGetMsg : OutgoingPacketFactory<MessageSvcPbGetMsg.Response>("MessageSvc.PbGetMsg") {
|
internal object MessageSvcPbGetMsg : OutgoingPacketFactory<MessageSvcPbGetMsg.Response>("MessageSvc.PbGetMsg") {
|
||||||
|
|
||||||
|
|
||||||
|
private val msgUidQueue = ArrayDeque<Long>()
|
||||||
|
private val msgUidSet = hashSetOf<Long>()
|
||||||
|
private val msgQueueMutex = Mutex()
|
||||||
|
|
||||||
@Suppress("SpellCheckingInspection")
|
@Suppress("SpellCheckingInspection")
|
||||||
operator fun invoke(
|
operator fun invoke(
|
||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
@ -114,8 +122,9 @@ internal object MessageSvcPbGetMsg : OutgoingPacketFactory<MessageSvcPbGetMsg.Re
|
|||||||
|
|
||||||
private fun MsgComm.Msg.getNewMemberInfo(): MemberInfo {
|
private fun MsgComm.Msg.getNewMemberInfo(): MemberInfo {
|
||||||
return object : MemberInfo {
|
return object : MemberInfo {
|
||||||
override val nameCard: String get() = msgHead.authNick.takeIf { it.isNotEmpty() }
|
override val nameCard: String
|
||||||
?: msgHead.fromNick
|
get() = msgHead.authNick.takeIf { it.isNotEmpty() }
|
||||||
|
?: msgHead.fromNick
|
||||||
override val permission: MemberPermission get() = MemberPermission.MEMBER
|
override val permission: MemberPermission get() = MemberPermission.MEMBER
|
||||||
override val specialTitle: String get() = ""
|
override val specialTitle: String get() = ""
|
||||||
override val muteTimestamp: Int get() = 0
|
override val muteTimestamp: Int get() = 0
|
||||||
@ -135,9 +144,23 @@ internal object MessageSvcPbGetMsg : OutgoingPacketFactory<MessageSvcPbGetMsg.Re
|
|||||||
.warning { "MessageSvcPushNotify: result != 0, result = ${resp.result}, errorMsg=${resp.errmsg}" }
|
.warning { "MessageSvcPushNotify: result != 0, result = ${resp.result}, errorMsg=${resp.errmsg}" }
|
||||||
return EmptyResponse
|
return EmptyResponse
|
||||||
}
|
}
|
||||||
|
when (resp.msgRspType) {
|
||||||
|
0 -> {
|
||||||
|
bot.client.c2cMessageSync.syncCookie = resp.syncCookie
|
||||||
|
bot.client.c2cMessageSync.pubAccountCookie = resp.pubAccountCookie
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
bot.client.c2cMessageSync.syncCookie = resp.syncCookie
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
bot.client.c2cMessageSync.pubAccountCookie = resp.pubAccountCookie
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bot.logger.debug(resp.msgRspType._miraiContentToString())
|
||||||
|
// bot.logger.debug(resp.syncCookie._miraiContentToString())
|
||||||
|
|
||||||
bot.client.c2cMessageSync.syncCookie = resp.syncCookie
|
|
||||||
bot.client.c2cMessageSync.pubAccountCookie = resp.pubAccountCookie
|
|
||||||
bot.client.c2cMessageSync.msgCtrlBuf = resp.msgCtrlBuf
|
bot.client.c2cMessageSync.msgCtrlBuf = resp.msgCtrlBuf
|
||||||
|
|
||||||
if (resp.uinPairMsgs == null) {
|
if (resp.uinPairMsgs == null) {
|
||||||
@ -151,10 +174,21 @@ internal object MessageSvcPbGetMsg : OutgoingPacketFactory<MessageSvcPbGetMsg.Re
|
|||||||
.filter { msg: MsgComm.Msg -> msg.msgHead.msgTime > it.lastReadTime.toLong() and 4294967295L }
|
.filter { msg: MsgComm.Msg -> msg.msgHead.msgTime > it.lastReadTime.toLong() and 4294967295L }
|
||||||
}.also {
|
}.also {
|
||||||
MessageSvcPbDeleteMsg.delete(bot, it) // 删除消息
|
MessageSvcPbDeleteMsg.delete(bot, it) // 删除消息
|
||||||
// todo 实现一个锁来防止重复收到消息
|
|
||||||
}
|
}
|
||||||
.mapNotNull<MsgComm.Msg, Packet> { msg ->
|
.mapNotNull<MsgComm.Msg, Packet> { msg ->
|
||||||
|
|
||||||
|
msgQueueMutex.lock()
|
||||||
|
val msgUid = msg.msgHead.msgUid
|
||||||
|
if (msgUidSet.size > 50) {
|
||||||
|
msgUidSet.remove(msgUidQueue.removeFirst())
|
||||||
|
}
|
||||||
|
if (!msgUidSet.add(msgUid)) {
|
||||||
|
msgQueueMutex.unlock()
|
||||||
|
return@mapNotNull null
|
||||||
|
}
|
||||||
|
msgQueueMutex.unlock()
|
||||||
|
msgUidQueue.addLast(msgUid)
|
||||||
|
|
||||||
suspend fun createGroupForBot(groupUin: Long): Group? {
|
suspend fun createGroupForBot(groupUin: Long): Group? {
|
||||||
val group = bot.getGroupByUinOrNull(groupUin)
|
val group = bot.getGroupByUinOrNull(groupUin)
|
||||||
if (group != null) {
|
if (group != null) {
|
||||||
@ -294,18 +328,15 @@ internal object MessageSvcPbGetMsg : OutgoingPacketFactory<MessageSvcPbGetMsg.Re
|
|||||||
return@mapNotNull null
|
return@mapNotNull null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (friend.lastMessageSequence.compareAndSet(
|
friend.lastMessageSequence.loop {
|
||||||
friend.lastMessageSequence.value,
|
if (friend.lastMessageSequence.compareAndSet(it, msg.msgHead.msgSeq)) {
|
||||||
msg.msgHead.msgSeq
|
return@mapNotNull FriendMessageEvent(
|
||||||
)
|
friend,
|
||||||
) {
|
msg.toMessageChain(bot, groupIdOrZero = 0, onlineSource = true),
|
||||||
return@mapNotNull FriendMessageEvent(
|
msg.msgHead.msgTime
|
||||||
friend,
|
)
|
||||||
msg.toMessageChain(bot, groupIdOrZero = 0, onlineSource = true),
|
} else return@mapNotNull null
|
||||||
msg.msgHead.msgTime
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return@mapNotNull null
|
|
||||||
}
|
}
|
||||||
208 -> {
|
208 -> {
|
||||||
// friend ptt
|
// friend ptt
|
||||||
@ -387,7 +418,7 @@ internal object MessageSvcPbGetMsg : OutgoingPacketFactory<MessageSvcPbGetMsg.Re
|
|||||||
MessageSvcPbGetMsg(
|
MessageSvcPbGetMsg(
|
||||||
client,
|
client,
|
||||||
MsgSvc.SyncFlag.CONTINUE,
|
MsgSvc.SyncFlag.CONTINUE,
|
||||||
packet.syncCookie
|
bot.client.c2cMessageSync.syncCookie
|
||||||
).sendAndExpect<Packet>()
|
).sendAndExpect<Packet>()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -398,7 +429,7 @@ internal object MessageSvcPbGetMsg : OutgoingPacketFactory<MessageSvcPbGetMsg.Re
|
|||||||
MessageSvcPbGetMsg(
|
MessageSvcPbGetMsg(
|
||||||
client,
|
client,
|
||||||
MsgSvc.SyncFlag.CONTINUE,
|
MsgSvc.SyncFlag.CONTINUE,
|
||||||
packet.syncCookie
|
bot.client.c2cMessageSync.syncCookie
|
||||||
).sendAndExpect<Packet>()
|
).sendAndExpect<Packet>()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -63,21 +63,17 @@ kotlin {
|
|||||||
api(kotlin("serialization"))
|
api(kotlin("serialization"))
|
||||||
api(kotlin("reflect"))
|
api(kotlin("reflect"))
|
||||||
|
|
||||||
api(kotlinx("serialization-core", Versions.Kotlin.serialization))
|
api1(kotlinx("serialization-core", Versions.Kotlin.serialization))
|
||||||
implementation(kotlinx("serialization-protobuf", Versions.Kotlin.serialization))
|
implementation1(kotlinx("serialization-protobuf", Versions.Kotlin.serialization))
|
||||||
api(kotlinx("io", Versions.Kotlin.io)) {
|
api1(kotlinx("io", Versions.Kotlin.io))
|
||||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
api1(kotlinx("coroutines-io", Versions.Kotlin.coroutinesIo))
|
||||||
}
|
|
||||||
api(kotlinx("coroutines-io", Versions.Kotlin.coroutinesIo)) {
|
|
||||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
|
||||||
}
|
|
||||||
api(kotlinx("coroutines-core", Versions.Kotlin.coroutines))
|
api(kotlinx("coroutines-core", Versions.Kotlin.coroutines))
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlinx:atomicfu:${Versions.Kotlin.atomicFU}")
|
implementation1("org.jetbrains.kotlinx:atomicfu:${Versions.Kotlin.atomicFU}")
|
||||||
|
|
||||||
api(ktor("client-cio"))
|
api1(ktor("client-cio"))
|
||||||
api(ktor("client-core"))
|
api1(ktor("client-core"))
|
||||||
api(ktor("network"))
|
api1(ktor("network"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,14 +89,10 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
api(kotlin("reflect"))
|
api(kotlin("reflect"))
|
||||||
|
|
||||||
api(kotlinx("io-jvm", Versions.Kotlin.io)) {
|
api1(kotlinx("io-jvm", Versions.Kotlin.io))
|
||||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
api1(kotlinx("coroutines-io-jvm", Versions.Kotlin.coroutinesIo))
|
||||||
}
|
|
||||||
api(kotlinx("coroutines-io-jvm", Versions.Kotlin.coroutinesIo)) {
|
|
||||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
|
||||||
}
|
|
||||||
|
|
||||||
api(ktor("client-android", Versions.Kotlin.ktor))
|
api1(ktor("client-android", Versions.Kotlin.ktor))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,21 +108,13 @@ kotlin {
|
|||||||
|
|
||||||
val jvmMain by getting {
|
val jvmMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
//api(kotlin("stdlib-jdk8"))
|
|
||||||
//api(kotlin("stdlib-jdk7"))
|
|
||||||
api(kotlin("reflect"))
|
api(kotlin("reflect"))
|
||||||
compileOnly("org.apache.logging.log4j:log4j-api:" + Versions.Logging.log4j)
|
compileOnly("org.apache.logging.log4j:log4j-api:" + Versions.Logging.log4j)
|
||||||
compileOnly("org.slf4j:slf4j-api:" + Versions.Logging.slf4j)
|
compileOnly("org.slf4j:slf4j-api:" + Versions.Logging.slf4j)
|
||||||
|
|
||||||
api(ktor("client-core-jvm", Versions.Kotlin.ktor))
|
api1(ktor("client-core-jvm", Versions.Kotlin.ktor))
|
||||||
api(kotlinx("io-jvm", Versions.Kotlin.io)) {
|
api1(kotlinx("io-jvm", Versions.Kotlin.io))
|
||||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
api1(kotlinx("coroutines-io-jvm", Versions.Kotlin.coroutinesIo))
|
||||||
}
|
|
||||||
api(kotlinx("coroutines-io-jvm", Versions.Kotlin.coroutinesIo)) {
|
|
||||||
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
|
||||||
}
|
|
||||||
|
|
||||||
runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,6 +130,24 @@ kotlin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.implementation1(dependencyNotation: String) =
|
||||||
|
implementation(dependencyNotation) {
|
||||||
|
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core-common")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core-jvm")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core-metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.api1(dependencyNotation: String) =
|
||||||
|
api(dependencyNotation) {
|
||||||
|
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core-common")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core-jvm")
|
||||||
|
exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-core-metadata")
|
||||||
|
}
|
||||||
|
|
||||||
apply(from = rootProject.file("gradle/publish.gradle"))
|
apply(from = rootProject.file("gradle/publish.gradle"))
|
||||||
|
|
||||||
tasks.withType<com.jfrog.bintray.gradle.tasks.BintrayUploadTask> {
|
tasks.withType<com.jfrog.bintray.gradle.tasks.BintrayUploadTask> {
|
||||||
|
@ -26,7 +26,7 @@ import kotlin.reflect.KClass
|
|||||||
internal fun <L : Listener<E>, E : Event> KClass<out E>.subscribeInternal(listener: L): L {
|
internal fun <L : Listener<E>, E : Event> KClass<out E>.subscribeInternal(listener: L): L {
|
||||||
with(GlobalEventListeners[listener.priority]) {
|
with(GlobalEventListeners[listener.priority]) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val node = ListenerNode(listener as Listener<Event>, this@subscribeInternal)
|
val node = ListenerRegistry(listener as Listener<Event>, this@subscribeInternal)
|
||||||
addLast(node)
|
addLast(node)
|
||||||
listener.invokeOnCompletion {
|
listener.invokeOnCompletion {
|
||||||
this.remove(node)
|
this.remove(node)
|
||||||
@ -107,13 +107,13 @@ internal class Handler<in E : Event> internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ListenerNode(
|
internal class ListenerRegistry(
|
||||||
val listener: Listener<Event>,
|
val listener: Listener<Event>,
|
||||||
val owner: KClass<out Event>
|
val type: KClass<out Event>
|
||||||
)
|
)
|
||||||
|
|
||||||
internal expect object GlobalEventListeners {
|
internal expect object GlobalEventListeners {
|
||||||
operator fun get(priority: Listener.EventPriority): LockFreeLinkedList<ListenerNode>
|
operator fun get(priority: Listener.EventPriority): LockFreeLinkedList<ListenerRegistry>
|
||||||
}
|
}
|
||||||
|
|
||||||
internal expect class MiraiAtomicBoolean(initial: Boolean) {
|
internal expect class MiraiAtomicBoolean(initial: Boolean) {
|
||||||
@ -135,50 +135,50 @@ internal suspend inline fun AbstractEvent.broadcastInternal() {
|
|||||||
internal suspend inline fun <E : AbstractEvent> callAndRemoveIfRequired(
|
internal suspend inline fun <E : AbstractEvent> callAndRemoveIfRequired(
|
||||||
event: E
|
event: E
|
||||||
) {
|
) {
|
||||||
for (p in Listener.EventPriority.valuesExceptMonitor) {
|
for (p in Listener.EventPriority.prioritiesExcludedMonitor) {
|
||||||
GlobalEventListeners[p].forEachNode { eventNode ->
|
GlobalEventListeners[p].forEachNode { registeredRegistryNode ->
|
||||||
if (event.isIntercepted) {
|
if (event.isIntercepted) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val node = eventNode.nodeValue
|
val listenerRegistry = registeredRegistryNode.nodeValue
|
||||||
if (!node.owner.isInstance(event)) return@forEachNode
|
if (!listenerRegistry.type.isInstance(event)) return@forEachNode
|
||||||
val listener = node.listener
|
val listener = listenerRegistry.listener
|
||||||
when (listener.concurrencyKind) {
|
when (listener.concurrencyKind) {
|
||||||
Listener.ConcurrencyKind.LOCKED -> {
|
Listener.ConcurrencyKind.LOCKED -> {
|
||||||
(listener as Handler).lock!!.withLock {
|
(listener as Handler).lock!!.withLock {
|
||||||
if (listener.onEvent(event) == ListeningStatus.STOPPED) {
|
if (listener.onEvent(event) == ListeningStatus.STOPPED) {
|
||||||
removeNode(eventNode)
|
removeNode(registeredRegistryNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Listener.ConcurrencyKind.CONCURRENT -> {
|
Listener.ConcurrencyKind.CONCURRENT -> {
|
||||||
if (listener.onEvent(event) == ListeningStatus.STOPPED) {
|
if (listener.onEvent(event) == ListeningStatus.STOPPED) {
|
||||||
removeNode(eventNode)
|
removeNode(registeredRegistryNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
GlobalEventListeners[EventPriority.MONITOR].forEachNode { eventNode ->
|
GlobalEventListeners[EventPriority.MONITOR].forEachNode { registeredRegistryNode ->
|
||||||
if (event.isIntercepted) {
|
if (event.isIntercepted) {
|
||||||
return@coroutineScope
|
return@coroutineScope
|
||||||
}
|
}
|
||||||
val node = eventNode.nodeValue
|
val listenerRegistry = registeredRegistryNode.nodeValue
|
||||||
if (!node.owner.isInstance(event)) return@forEachNode
|
if (!listenerRegistry.type.isInstance(event)) return@forEachNode
|
||||||
val listener = node.listener
|
val listener = listenerRegistry.listener
|
||||||
launch {
|
launch {
|
||||||
when (listener.concurrencyKind) {
|
when (listener.concurrencyKind) {
|
||||||
Listener.ConcurrencyKind.LOCKED -> {
|
Listener.ConcurrencyKind.LOCKED -> {
|
||||||
(listener as Handler).lock!!.withLock {
|
(listener as Handler).lock!!.withLock {
|
||||||
if (listener.onEvent(event) == ListeningStatus.STOPPED) {
|
if (listener.onEvent(event) == ListeningStatus.STOPPED) {
|
||||||
removeNode(eventNode)
|
removeNode(registeredRegistryNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Listener.ConcurrencyKind.CONCURRENT -> {
|
Listener.ConcurrencyKind.CONCURRENT -> {
|
||||||
if (listener.onEvent(event) == ListeningStatus.STOPPED) {
|
if (listener.onEvent(event) == ListeningStatus.STOPPED) {
|
||||||
removeNode(eventNode)
|
removeNode(registeredRegistryNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,9 @@ public interface Listener<in E : Event> : CompletableJob {
|
|||||||
|
|
||||||
internal companion object {
|
internal companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
internal val valuesExceptMonitor: Array<EventPriority> = arrayOf(HIGHEST, HIGH, NORMAL, LOW, LOWEST)
|
internal val prioritiesExcludedMonitor: Array<EventPriority> = run {
|
||||||
|
values().filter { it != MONITOR }.toTypedArray()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -844,7 +844,7 @@ internal open class LockFreeLinkedListNode<E>(
|
|||||||
* [Tail], is not being tested.
|
* [Tail], is not being tested.
|
||||||
*/
|
*/
|
||||||
inline fun allMatching(condition: (LockFreeLinkedListNode<E>) -> Boolean): Boolean =
|
inline fun allMatching(condition: (LockFreeLinkedListNode<E>) -> Boolean): Boolean =
|
||||||
this.childIterateReturnsLastSatisfying({ it.nextNode }, condition) !is Tail
|
this.childIterateReturnsLastSatisfying({ it.nextNode }, condition) is Tail
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop on and returns the former element of the element that is [equals] to the [element]
|
* Stop on and returns the former element of the element that is [equals] to the [element]
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||||
|
*
|
||||||
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
|
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions license that can be found via the following link.
|
||||||
|
*
|
||||||
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.event.internal
|
||||||
|
|
||||||
|
import net.mamoe.mirai.event.Listener
|
||||||
|
import net.mamoe.mirai.utils.LockFreeLinkedList
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
internal actual object GlobalEventListeners {
|
||||||
|
private val ALL_LEVEL_REGISTRIES: Map<Listener.EventPriority, LockFreeLinkedList<ListenerRegistry>>
|
||||||
|
|
||||||
|
init {
|
||||||
|
val map = EnumMap<Listener.EventPriority, LockFreeLinkedList<ListenerRegistry>>(Listener.EventPriority::class.java)
|
||||||
|
Listener.EventPriority.values().forEach {
|
||||||
|
map[it] = LockFreeLinkedList()
|
||||||
|
}
|
||||||
|
this.ALL_LEVEL_REGISTRIES = map
|
||||||
|
}
|
||||||
|
|
||||||
|
actual operator fun get(priority: Listener.EventPriority): LockFreeLinkedList<ListenerRegistry> = ALL_LEVEL_REGISTRIES[priority]!!
|
||||||
|
|
||||||
|
}
|
@ -9,16 +9,10 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.event.internal
|
package net.mamoe.mirai.event.internal
|
||||||
|
|
||||||
import net.mamoe.mirai.event.Event
|
|
||||||
import net.mamoe.mirai.event.Listener
|
import net.mamoe.mirai.event.Listener
|
||||||
import net.mamoe.mirai.utils.LockFreeLinkedList
|
import net.mamoe.mirai.utils.LockFreeLinkedList
|
||||||
import net.mamoe.mirai.utils.LockFreeLinkedListNode
|
|
||||||
import net.mamoe.mirai.utils.isRemoved
|
|
||||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
|
|
||||||
internal actual class MiraiAtomicBoolean actual constructor(initial: Boolean) {
|
internal actual class MiraiAtomicBoolean actual constructor(initial: Boolean) {
|
||||||
private val delegate: AtomicBoolean = AtomicBoolean(initial)
|
private val delegate: AtomicBoolean = AtomicBoolean(initial)
|
||||||
@ -33,42 +27,3 @@ internal actual class MiraiAtomicBoolean actual constructor(initial: Boolean) {
|
|||||||
delegate.set(value)
|
delegate.set(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal actual object GlobalEventListeners {
|
|
||||||
private val map: Map<Listener.EventPriority, LockFreeLinkedList<ListenerNode>>
|
|
||||||
|
|
||||||
init {
|
|
||||||
val map = EnumMap<Listener.EventPriority, LockFreeLinkedList<ListenerNode>>(Listener.EventPriority::class.java)
|
|
||||||
Listener.EventPriority.values().forEach {
|
|
||||||
map[it] = LockFreeLinkedList()
|
|
||||||
}
|
|
||||||
this.map = map
|
|
||||||
}
|
|
||||||
|
|
||||||
actual operator fun get(priority: Listener.EventPriority): LockFreeLinkedList<ListenerNode> = map[priority]!!
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
internal actual class EventListeners<E : Event> actual constructor(clazz: KClass<E>) :
|
|
||||||
LockFreeLinkedList<Listener<E>>() {
|
|
||||||
@Suppress("UNCHECKED_CAST", "UNSUPPORTED", "NO_REFLECTION_IN_CLASS_PATH")
|
|
||||||
actual val supertypes: Set<KClass<out Event>> by lazy {
|
|
||||||
val supertypes = mutableSetOf<KClass<out Event>>()
|
|
||||||
|
|
||||||
fun addSupertypes(klass: KClass<out Event>) {
|
|
||||||
klass.supertypes.forEach {
|
|
||||||
val classifier = it.classifier as? KClass<out Event>
|
|
||||||
if (classifier != null) {
|
|
||||||
supertypes.add(classifier)
|
|
||||||
addSupertypes(classifier)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addSupertypes(clazz)
|
|
||||||
|
|
||||||
supertypes
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
@ -33,6 +33,14 @@ internal class LockFreeLinkedListTest {
|
|||||||
list.size shouldBeEqualTo 4
|
list.size shouldBeEqualTo 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isEmpty() {
|
||||||
|
val list = LockFreeLinkedList<Int>()
|
||||||
|
assertTrue { list.isEmpty() }
|
||||||
|
list.addLast(1)
|
||||||
|
assertFalse { list.isEmpty() }
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addAndGetConcurrent() = runBlocking {
|
fun addAndGetConcurrent() = runBlocking {
|
||||||
//withContext(Dispatchers.Default){
|
//withContext(Dispatchers.Default){
|
||||||
|
Loading…
Reference in New Issue
Block a user