HQService / HQFramework

A next-generation Bukkit development framework.
https://hqservice.kr
GNU General Public License v3.0
47 stars 1 forks source link

NMSWrapper 의 간헐적 예외들 #10

Closed vjh0107 closed 1 year ago

vjh0107 commented 1 year ago

아래는 플레이어 인벤토리의 아이템을 전부 Barrier 로 변경하는 예제코드입니다. 아래 예제코드가 포함되어있는 HQContainer 을 open 할 시, 오류가 발생합니다. 오류의 발생은 간헐적으로, ConcurrentModificationException 과 IllegalCallableAccessException 이렇게 2 종류의 예외가 발생합니다.

예제 코드

    override fun onOpen(vararg players: Player) {
        val player = players.first()
        val actualPlayerSlotStarts = this.inventory.size

        repeat(36) { index ->
            val actualPlayerSlot = actualPlayerSlotStarts + index
            player.virtual {
                inventory {
                    setItem(actualPlayerSlot, ItemStack(Material.BARRIER)) {

                    }
                }
            }
        }
    }

IllegalCallableAccessException 의 경우는, 아래의 코드와 같이 출력해보았을 경우 예외가 던져지기 전 false 로 출력되어집니다. ConcurrentModificationException 과 같은 이유로 오류가 발생하는것으로 간주하고 해결해보도록 하겠습니다.

internal inline fun <reified R> KCallable<*>.callAccess(vararg instance: Any): R {
    isAccessible.print("isAccessible: ")
    return if(!isAccessible) {
        isAccessible = true
        val result = call(*instance) as R
        isAccessible = false
        result
    } else call(*instance) as R
}

stacktrace 를 첨부합니다.

ConcurrentModificationException

java.util.ConcurrentModificationException: null
        at java.util.HashMap.computeIfAbsent(HashMap.java:1221) ~[?:?]
        at kr.hqservice.framework.nms.wrapper.reflect.NmsReflectionWrapperImpl.getNmsClass(NmsReflectionWrapperImpl.kt:72) ~[hqframework-bukkit-dist.jar:?]
        at kr.hqservice.framework.nms.virtual.item.VirtualItem.<init>(VirtualItem.kt:29) ~[hqframework-bukkit-dist.jar:?]
        at kr.hqservice.framework.nms.virtual.factory.VirtualContainerFactory.setItem(VirtualContainerFactory.kt:23) ~[hqframework-bukkit-dist.jar:?]
        at kr.hqservice.shop.core.gui.ShopContainer$onOpen$1$1$1.invoke(ShopContainer.kt:53) ~[hqshop.jar:?]
        at kr.hqservice.shop.core.gui.ShopContainer$onOpen$1$1$1.invoke(ShopContainer.kt:52) ~[hqshop.jar:?]
        at kr.hqservice.framework.nms.virtual.factory.impl.SingleVirtualFactory.inventory(SingleVirtualFactory.kt:33) ~[hqframework-bukkit-dist.jar:?]
        at kr.hqservice.shop.core.gui.ShopContainer$onOpen$1$1.invokeSuspend(ShopContainer.kt:52) ~[hqshop.jar:?]
        at kr.hqservice.shop.core.gui.ShopContainer$onOpen$1$1.invoke(ShopContainer.kt) ~[hqshop.jar:?]
        at kr.hqservice.shop.core.gui.ShopContainer$onOpen$1$1.invoke(ShopContainer.kt) ~[hqshop.jar:?]
        at kr.hqservice.framework.nms.extension.NmsPlayerExtensionKt$virtual$2.invokeSuspend(NmsPlayerExtension.kt:29) ~[hqframework-bukkit-dist.jar:?]
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[?:?]
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) ~[?:?]
        at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42) ~[?:?]
        at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95) ~[?:?]
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570) ~[?:?]
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750) ~[?:?]
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677) ~[?:?]
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664) ~[?:?]

IllegalCallableAccessException

kotlin.reflect.full.IllegalCallableAccessException: java.lang.IllegalAccessException: class kotlin.reflect.jvm.internal.calls.CallerImpl$FieldGetter cannot access a member of class net.minecraft.world.inventory.Container with modifiers "private"
        at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:224) ~[?:?]
        at kr.hqservice.framework.nms.wrapper.container.ContainerWrapperImpl.getStateId(ContainerWrapperImpl.kt:47) ~[hqframework-bukkit-dist.jar:?]
        at kr.hqservice.framework.nms.virtual.item.VirtualItem.createVirtualMessage(VirtualItem.kt:43) ~[hqframework-bukkit-dist.jar:?]
        at kr.hqservice.framework.nms.wrapper.reflect.NmsReflectionWrapperImpl.sendPacket(NmsReflectionWrapperImpl.kt:126) ~[hqframework-bukkit-dist.jar:?]
        at kr.hqservice.framework.nms.virtual.factory.impl.SingleVirtualFactory.inventory(SingleVirtualFactory.kt:34) ~[hqframework-bukkit-dist.jar:?]
        at kr.hqservice.shop.core.gui.ShopContainer$onOpen$1$1.invokeSuspend(ShopContainer.kt:52) ~[hqshop.jar:?]
        at kr.hqservice.shop.core.gui.ShopContainer$onOpen$1$1.invoke(ShopContainer.kt) ~[hqshop.jar:?]
        at kr.hqservice.shop.core.gui.ShopContainer$onOpen$1$1.invoke(ShopContainer.kt) ~[hqshop.jar:?]
        at kr.hqservice.framework.nms.extension.NmsPlayerExtensionKt$virtual$2.invokeSuspend(NmsPlayerExtension.kt:29) ~[hqframework-bukkit-dist.jar:?]
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[?:?]
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) ~[?:?]
        at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42) ~[?:?]
        at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95) ~[?:?]
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570) ~[?:?]
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750) ~[?:?]
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677) ~[?:?]
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664) ~[?:?]
Caused by: java.lang.IllegalAccessException: class kotlin.reflect.jvm.internal.calls.CallerImpl$FieldGetter cannot access a member of class net.minecraft.world.inventory.Container with modifiers "private"
        at jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:392) ~[?:?]
        at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:674) ~[?:?]
        at java.lang.reflect.Field.checkAccess(Field.java:1102) ~[?:?]
        at java.lang.reflect.Field.get(Field.java:423) ~[?:?]
        at kotlin.reflect.jvm.internal.calls.CallerImpl$FieldGetter.call(CallerImpl.kt:161) ~[?:?]
        at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:108) ~[?:?]
        ... 16 more
vjh0107 commented 1 year ago

문제는 VirtualFactory 가 내부적으로 suspend 함수들로 처리된다는 점을 인지하고 사용하지 않아 발생하였습니다. virtual 스코프 내에서는 suspend 람다이기 때문에, 정상적으로 동시성이 보장됩니다. 수정한 후 동작하는 코드입니다.

    override fun onOpen(vararg players: Player) {
        val player = players.first()
        val actualPlayerSlotStarts = this.inventory.size

        player.virtual {
            inventory {
                repeat(36) { index ->
                    val actualPlayerSlot = actualPlayerSlotStarts + index
                    setItem(actualPlayerSlot, ItemStack(Material.BARRIER)) {

                    }
                }
            }
        }
    }
vjh0107 commented 1 year ago

virtual factory 내를 suspend 임을 명시적으로 외부에 알릴 수 있는 방법이 있을까요?

cccgh5 commented 1 year ago

virtual factory 내를 suspend 임을 명시적으로 외부에 알릴 수 있는 방법이 있을까요?

sequence{}