Kotlin在Minecraft插口插件中协同工作



在文档中,它说协程比线程更轻,所以我想使用kotlin协程而不是BukkitRunnable。

//Defined as class field
private val scope = coroutineScope(Dispatchers.Default)
//In class method
scope.launch {/* wait some seconds and then change blockdata */}

从Dispatchers调用setBlockData。默认线程抛出错误,因为spigot API不是线程安全的,并且不能从main之外的线程调用API内容。

java.lang.IllegalStateException: Asynchronous block remove!

我认为更改块数据相当于Minecraft中的android UI更改,这意味着协同程序需要运行/注入到主线程中。因此,在Dispatchers中运行我的协同程序是有意义的。主要的但是,我找不到使用Dispatchers的方法。Main并将其设置到主线程,而不会获得非法StateException

我希望我的逻辑在这里是正确的

如果你想要一个简单的方法,能够将挂起的代码与主线程桥接(有可能从主线程获取一些信息并在协同程序中使用(,你可以使用这个方法:

suspend fun <T> suspendSync(plugin: Plugin, task: () -> T): T = withTimeout(10000L) {
// Context: The current coroutine context
suspendCancellableCoroutine { cont ->
// Context: The current coroutine context
Bukkit.getScheduler().runTask(plugin) {
// Context: Bukkit MAIN thread
// runCatching is used to forward any exception that may occur here back to
// our coroutine, keeping the exception transparency of Kotlin coroutines
runCatching(task).fold({ cont.resume(it) }, cont::resumeWithException)
}
}
}

我已经评论了代码的每个部分执行的上下文,以便您可以可视化上下文切换。suspendCancellableCoroutine是一种获取所有协同程序在后台使用的continuation对象的方法,因此我们可以在主线程执行任务后手动恢复它。

使用外部块withTimeout,这样,如果主线程没有在10秒内完成我们的任务,我们的协程就会放弃,而不是永远挂起。

使用也非常简单:

val plugin = // comes from somewhere
// example coroutine scope
CoroutineScope(Dispatchers.Default).launch {
// doing stuff async
// oh no, I need some data from the main thread!
val block = suspendSync(plugin) { 
// this code runs on the MAIN thread
Bukkit.getWorld("blah").getBlockAt(0, 0, 0) 
}
// back to async here, do stuff with block (just don't MODIFY it async, use more suspendSync if needed)
}

如果你有任何问题,或者认为我可以改进这个答案,不要害怕让我知道。

最新更新