Merge branch 'dev'

This commit is contained in:
Him188 2020-08-22 14:05:15 +08:00
commit 713308331d
14 changed files with 181 additions and 134 deletions

View File

@ -1,5 +1,8 @@
# Version 1.x
## `1.2.2` 2020/8/22
- 修复依赖冲突问题 (#523)
## `1.2.1` 2020/8/19
- 修复在 Java 调用 `group.uploadImage` 时编译出错的问题 (#511)
- 为 `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
### 新特性
- 初步语音支持: `Group.uploadVoice`, 支持 silk 或 arm 格式.
- 初步语音支持: `Group.uploadVoice`, 支持 silk 或 amr 格式.
**注意**: 现阶段语音实现仅为临时方案, 在将来 (`2.0.0`) 一定会变动. 使用时请评估可能带来的不兼容性.
- 新增将日志转换为 log4j, JDK Logger, SLF4J 等框架的方法: `LoggerAdapters` (#498 by [@Karlatemp](https://github.com/Karlatemp))

View File

@ -8,6 +8,12 @@ mirai 欢迎一切形式的代码贡献。你可以通过以下几种途径向 m
**阅读文档** [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` 分支进行其他修改 (否则你的修改可能被关闭或不会立即合并)**

View File

@ -9,7 +9,7 @@
object Versions {
object Mirai {
const val version = "1.2.1"
const val version = "1.2.2"
}
object Kotlin {

View File

@ -60,14 +60,12 @@ kotlin {
val commonMain by getting {
dependencies {
api(kotlinx("serialization-core", Versions.Kotlin.serialization))
api1(kotlinx("serialization-core", Versions.Kotlin.serialization))
api(kotlinx("coroutines-core", Versions.Kotlin.coroutines))
implementation(kotlinx("serialization-protobuf", Versions.Kotlin.serialization))
api("org.jetbrains.kotlinx:atomicfu:${Versions.Kotlin.atomicFU}")
api(kotlinx("io", Versions.Kotlin.io)) {
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
}
implementation(kotlinx("coroutines-io", Versions.Kotlin.coroutinesIo))
implementation1(kotlinx("serialization-protobuf", Versions.Kotlin.serialization))
api1("org.jetbrains.kotlinx:atomicfu:${Versions.Kotlin.atomicFU}")
api1(kotlinx("io", Versions.Kotlin.io))
implementation1(kotlinx("coroutines-io", Versions.Kotlin.coroutinesIo))
}
}
@ -97,11 +95,8 @@ kotlin {
val jvmMain by getting {
dependencies {
runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE
implementation("org.bouncycastle:bcprov-jdk15on:1.64")
api(kotlinx("io-jvm", Versions.Kotlin.io)) {
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
}
api1(kotlinx("io-jvm", Versions.Kotlin.io))
// api(kotlinx("coroutines-debug", Versions.Kotlin.coroutines))
}
}
@ -112,14 +107,29 @@ kotlin {
implementation(kotlin("test", Versions.Kotlin.compiler))
implementation(kotlin("test-junit", Versions.Kotlin.compiler))
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"))

View File

@ -180,7 +180,7 @@ internal class MemberImpl constructor(
check(this.id != bot.id) {
"A bot can't mute itself."
}
checkBotPermissionHigherThanThis()
checkBotPermissionHigherThanThis("mute")
bot.network.run {
TroopManagement.Mute(
client = bot.client,
@ -194,10 +194,10 @@ internal class MemberImpl constructor(
net.mamoe.mirai.event.events.MemberMuteEvent(this@MemberImpl, durationSeconds, null).broadcast()
}
private fun checkBotPermissionHigherThanThis() {
private fun checkBotPermissionHigherThanThis(operationName: String) {
check(group.botPermission > this.permission) {
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}"
)
}
@ -205,7 +205,7 @@ internal class MemberImpl constructor(
@JvmSynthetic
override suspend fun unmute() {
checkBotPermissionHigherThanThis()
checkBotPermissionHigherThanThis("unmute")
bot.network.run {
TroopManagement.Mute(
client = bot.client,
@ -222,7 +222,7 @@ internal class MemberImpl constructor(
@JvmSynthetic
override suspend fun kick(message: String) {
checkBotPermissionHigherThanThis()
checkBotPermissionHigherThanThis("kick")
check(group.members.getOrNull(this.id) != null) {
"Member ${this.id} had already been kicked from group ${group.id}"
}

View File

@ -19,7 +19,7 @@ internal class MsgSvc : ProtoBuf {
@ProtoNumber(1) @JvmField val result: Int = 0,
@ProtoNumber(2) @JvmField val errmsg: String = "",
@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(6) @JvmField val bindUin: Long = 0L,
@ProtoNumber(7) @JvmField val msgRspType: Int = 0,

View File

@ -14,6 +14,7 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import kotlinx.atomicfu.loop
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.io.core.ByteReadPacket
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.NewContact
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.writeProtoBuf
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") {
private val msgUidQueue = ArrayDeque<Long>()
private val msgUidSet = hashSetOf<Long>()
private val msgQueueMutex = Mutex()
@Suppress("SpellCheckingInspection")
operator fun invoke(
client: QQAndroidClient,
@ -114,8 +122,9 @@ internal object MessageSvcPbGetMsg : OutgoingPacketFactory<MessageSvcPbGetMsg.Re
private fun MsgComm.Msg.getNewMemberInfo(): MemberInfo {
return object : MemberInfo {
override val nameCard: String get() = msgHead.authNick.takeIf { it.isNotEmpty() }
?: msgHead.fromNick
override val nameCard: String
get() = msgHead.authNick.takeIf { it.isNotEmpty() }
?: msgHead.fromNick
override val permission: MemberPermission get() = MemberPermission.MEMBER
override val specialTitle: String get() = ""
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}" }
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
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 }
}.also {
MessageSvcPbDeleteMsg.delete(bot, it) // 删除消息
// todo 实现一个锁来防止重复收到消息
}
.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? {
val group = bot.getGroupByUinOrNull(groupUin)
if (group != null) {
@ -294,18 +328,15 @@ internal object MessageSvcPbGetMsg : OutgoingPacketFactory<MessageSvcPbGetMsg.Re
return@mapNotNull null
}
if (friend.lastMessageSequence.compareAndSet(
friend.lastMessageSequence.value,
msg.msgHead.msgSeq
)
) {
return@mapNotNull FriendMessageEvent(
friend,
msg.toMessageChain(bot, groupIdOrZero = 0, onlineSource = true),
msg.msgHead.msgTime
)
friend.lastMessageSequence.loop {
if (friend.lastMessageSequence.compareAndSet(it, msg.msgHead.msgSeq)) {
return@mapNotNull FriendMessageEvent(
friend,
msg.toMessageChain(bot, groupIdOrZero = 0, onlineSource = true),
msg.msgHead.msgTime
)
} else return@mapNotNull null
}
return@mapNotNull null
}
208 -> {
// friend ptt
@ -387,7 +418,7 @@ internal object MessageSvcPbGetMsg : OutgoingPacketFactory<MessageSvcPbGetMsg.Re
MessageSvcPbGetMsg(
client,
MsgSvc.SyncFlag.CONTINUE,
packet.syncCookie
bot.client.c2cMessageSync.syncCookie
).sendAndExpect<Packet>()
}
return
@ -398,7 +429,7 @@ internal object MessageSvcPbGetMsg : OutgoingPacketFactory<MessageSvcPbGetMsg.Re
MessageSvcPbGetMsg(
client,
MsgSvc.SyncFlag.CONTINUE,
packet.syncCookie
bot.client.c2cMessageSync.syncCookie
).sendAndExpect<Packet>()
}
return

View File

@ -63,21 +63,17 @@ kotlin {
api(kotlin("serialization"))
api(kotlin("reflect"))
api(kotlinx("serialization-core", Versions.Kotlin.serialization))
implementation(kotlinx("serialization-protobuf", Versions.Kotlin.serialization))
api(kotlinx("io", Versions.Kotlin.io)) {
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
}
api(kotlinx("coroutines-io", Versions.Kotlin.coroutinesIo)) {
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
}
api1(kotlinx("serialization-core", Versions.Kotlin.serialization))
implementation1(kotlinx("serialization-protobuf", Versions.Kotlin.serialization))
api1(kotlinx("io", Versions.Kotlin.io))
api1(kotlinx("coroutines-io", Versions.Kotlin.coroutinesIo))
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"))
api(ktor("client-core"))
api(ktor("network"))
api1(ktor("client-cio"))
api1(ktor("client-core"))
api1(ktor("network"))
}
}
@ -93,14 +89,10 @@ kotlin {
dependencies {
api(kotlin("reflect"))
api(kotlinx("io-jvm", Versions.Kotlin.io)) {
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
}
api(kotlinx("coroutines-io-jvm", Versions.Kotlin.coroutinesIo)) {
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
}
api1(kotlinx("io-jvm", Versions.Kotlin.io))
api1(kotlinx("coroutines-io-jvm", Versions.Kotlin.coroutinesIo))
api(ktor("client-android", Versions.Kotlin.ktor))
api1(ktor("client-android", Versions.Kotlin.ktor))
}
}
@ -116,21 +108,13 @@ kotlin {
val jvmMain by getting {
dependencies {
//api(kotlin("stdlib-jdk8"))
//api(kotlin("stdlib-jdk7"))
api(kotlin("reflect"))
compileOnly("org.apache.logging.log4j:log4j-api:" + Versions.Logging.log4j)
compileOnly("org.slf4j:slf4j-api:" + Versions.Logging.slf4j)
api(ktor("client-core-jvm", Versions.Kotlin.ktor))
api(kotlinx("io-jvm", Versions.Kotlin.io)) {
exclude("org.jetbrains.kotlin", "kotlin-stdlib")
}
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
api1(ktor("client-core-jvm", Versions.Kotlin.ktor))
api1(kotlinx("io-jvm", Versions.Kotlin.io))
api1(kotlinx("coroutines-io-jvm", Versions.Kotlin.coroutinesIo))
}
}
@ -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"))
tasks.withType<com.jfrog.bintray.gradle.tasks.BintrayUploadTask> {

View File

@ -26,7 +26,7 @@ import kotlin.reflect.KClass
internal fun <L : Listener<E>, E : Event> KClass<out E>.subscribeInternal(listener: L): L {
with(GlobalEventListeners[listener.priority]) {
@Suppress("UNCHECKED_CAST")
val node = ListenerNode(listener as Listener<Event>, this@subscribeInternal)
val node = ListenerRegistry(listener as Listener<Event>, this@subscribeInternal)
addLast(node)
listener.invokeOnCompletion {
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 owner: KClass<out Event>
val type: KClass<out Event>
)
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) {
@ -135,50 +135,50 @@ internal suspend inline fun AbstractEvent.broadcastInternal() {
internal suspend inline fun <E : AbstractEvent> callAndRemoveIfRequired(
event: E
) {
for (p in Listener.EventPriority.valuesExceptMonitor) {
GlobalEventListeners[p].forEachNode { eventNode ->
for (p in Listener.EventPriority.prioritiesExcludedMonitor) {
GlobalEventListeners[p].forEachNode { registeredRegistryNode ->
if (event.isIntercepted) {
return
}
val node = eventNode.nodeValue
if (!node.owner.isInstance(event)) return@forEachNode
val listener = node.listener
val listenerRegistry = registeredRegistryNode.nodeValue
if (!listenerRegistry.type.isInstance(event)) return@forEachNode
val listener = listenerRegistry.listener
when (listener.concurrencyKind) {
Listener.ConcurrencyKind.LOCKED -> {
(listener as Handler).lock!!.withLock {
if (listener.onEvent(event) == ListeningStatus.STOPPED) {
removeNode(eventNode)
removeNode(registeredRegistryNode)
}
}
}
Listener.ConcurrencyKind.CONCURRENT -> {
if (listener.onEvent(event) == ListeningStatus.STOPPED) {
removeNode(eventNode)
removeNode(registeredRegistryNode)
}
}
}
}
}
coroutineScope {
GlobalEventListeners[EventPriority.MONITOR].forEachNode { eventNode ->
GlobalEventListeners[EventPriority.MONITOR].forEachNode { registeredRegistryNode ->
if (event.isIntercepted) {
return@coroutineScope
}
val node = eventNode.nodeValue
if (!node.owner.isInstance(event)) return@forEachNode
val listener = node.listener
val listenerRegistry = registeredRegistryNode.nodeValue
if (!listenerRegistry.type.isInstance(event)) return@forEachNode
val listener = listenerRegistry.listener
launch {
when (listener.concurrencyKind) {
Listener.ConcurrencyKind.LOCKED -> {
(listener as Handler).lock!!.withLock {
if (listener.onEvent(event) == ListeningStatus.STOPPED) {
removeNode(eventNode)
removeNode(registeredRegistryNode)
}
}
}
Listener.ConcurrencyKind.CONCURRENT -> {
if (listener.onEvent(event) == ListeningStatus.STOPPED) {
removeNode(eventNode)
removeNode(registeredRegistryNode)
}
}
}

View File

@ -110,7 +110,9 @@ public interface Listener<in E : Event> : CompletableJob {
internal companion object {
@JvmStatic
internal val valuesExceptMonitor: Array<EventPriority> = arrayOf(HIGHEST, HIGH, NORMAL, LOW, LOWEST)
internal val prioritiesExcludedMonitor: Array<EventPriority> = run {
values().filter { it != MONITOR }.toTypedArray()
}
}
}

View File

@ -844,7 +844,7 @@ internal open class LockFreeLinkedListNode<E>(
* [Tail], is not being tested.
*/
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]

View File

@ -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]!!
}

View File

@ -9,16 +9,10 @@
package net.mamoe.mirai.event.internal
import net.mamoe.mirai.event.Event
import net.mamoe.mirai.event.Listener
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.concurrent.atomic.AtomicBoolean
import kotlin.reflect.KClass
internal actual class MiraiAtomicBoolean actual constructor(initial: Boolean) {
private val delegate: AtomicBoolean = AtomicBoolean(initial)
@ -33,42 +27,3 @@ internal actual class MiraiAtomicBoolean actual constructor(initial: Boolean) {
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
}
}
*/

View File

@ -33,6 +33,14 @@ internal class LockFreeLinkedListTest {
list.size shouldBeEqualTo 4
}
@Test
fun isEmpty() {
val list = LockFreeLinkedList<Int>()
assertTrue { list.isEmpty() }
list.addLast(1)
assertFalse { list.isEmpty() }
}
@Test
fun addAndGetConcurrent() = runBlocking {
//withContext(Dispatchers.Default){