-
Notifications
You must be signed in to change notification settings - Fork 20
Coroutines and Skedule
KotlinBukkitAPI has Skedule library built in, it's brings Bukkit "thread system" to the Kotlin Coroutines.
KotlinBukkitAPI provide some extensions to work with Skedule in the project style.
WithPlugin<*>.BukkitDispatchers
& Plugin.BukkitDispatchers
will provide to you the SYNC (Bukkit Main Thread) and ASYNC executions for Coroutines.
pluginCoroutineScope.launch(BukkitDispatchers.SYNC) {
val allBlocks = blocksAroundThePlayer(100)
val result = withContext(Dispatchers.IO) {
someHaveCalculations(allBlocks)
}
result.forEach { it.applyToBlock() }
}
Normally you will be able to easy access BukkitDispatchers
in your code base if you are using WithPlugin
or any interface that implements it like LifeclycleListener
.
KotlinBukkitAPI has a lot to offer when using Coroutines, we recommend that if you not use yet, starts to use it because help us in many tasks. The project has coroutines built in the Command DSL, provides an Event Flow and much more.
If you notice, in the previous snippet, we use pluginCoroutineScope
, this scopes is provided by the KotlinBukkitAPI.
What he does is that when your plugin get disabled, all of yours scopes will be canceled prevents leaks in your Plugin user server.
This is a part of the Architecture system of KotlinBukkitAPI and is only provided if you are using KotlinPlugin. It can be used inside of your KotlinPlugin
and in any LifecycleListener
(that will be most likely any of your Managers instances)
LifecycleListener<*>.pluginCoroutineScope
& KotlinPlugin.pluginCoroutineScope
We also provide a playerCoroutineScope
, the key difference is that is bound to a Player as well, and when the player get disconnect your Job will be canceled too.
LifecycleListener<*>.playerCoroutineScope
& KotlinPlugin.playerCoroutineScope
When using Coroutines in general the recommendations is to build your APIs safe as possible, using Structured concurrency is really important to let your project Job leak free.
Main-safety: this is a technique that allows to you safe call any suspend functions without care where it will run in the current Dispatcher or in a specific one like IO. This improves your API readability and removes a lot of withContext
just for "safety" in your project.
// before Main-safety
suspend updateBlocks(world: World, blocks: List<Pair<BlockPos, Material>>) {
for((pos, type) in blocks) {
pos.asBlock(world).type = type
}
}
pluginCoroutineScope.launch(Dispatchers.IO) {
val blocks = retrieveBlocksFromDatabase()
withContext(BukkitDispatchers.SYNC) {
updateBlocks(world, blocks)
}
}
// after, with Main-Safety
suspend updateBlocks(world: World, blocks: List<Pair<BlockPos, Material>>) = withContext(BukkitDispachers.SYNC) {
for((pos, type) in blocks) {
pos.asBlock(world).type = type
}
}
pluginCoroutineScope.launch(Dispatchers.IO) {
val blocks = retrieveBlocksFromDatabase()
updateBlocks(world, blocks)
}
This will improve safety because you can use this updateBlocks
in other parts of your code base but sometimes you can forget the withContext
.
takeMaxPerTick
Plugin.takeMaxPerTick(time: Millisecond)
& WithPlugin<*>.takeMaxPerTick(time: Millisecond)
Is possible to suspend your Coroutines until the next server tick if it uses the max milliseconds that you want to use in a tick.
val maxBlock = locMax.block
val minBlock = locMin.block
for(block in minBlock..maxBlock) {
block.type = Material.DIRT
takeMaxPerTick(5.millisecond)
}
Result