Shynixn / MCCoroutine

MCCoroutine is a library, which adds extensive support for Kotlin Coroutines for Minecraft Server environments.
Other
203 stars 17 forks source link

Help with coroutines #34

Closed DokanBoy closed 3 years ago

DokanBoy commented 3 years ago

Hi, this is my first time working with coroutines. I have a service that accesses the database. The service is called when the player logs in.

I marked the method in the service as suspend, to use it in the Listener I just need to make a launch { service.callMethod(arg) }? Do I need to mark the listener method as suspend in this case and register accordingly?

DokanBoy commented 3 years ago

Or can I do all the suspend logic without wrapping it in a coroutine, but register the listener as suspending?

DokanBoy commented 3 years ago

My code

interface UserService : Service {
    suspend fun createAction(player: Player, action: JoinLeaveAction)
}
class PlayerListener(private val userService: UserService) : Listener {
    @EventHandler
    suspend fun onJoin(event: PlayerJoinEvent) {
        userService.createAction(event.player, JoinLeaveAction.JOIN)
    }
}    
class TimeTrackerPlugin : JavaPlugin() {
    override fun onEnable() {
        val databaseManager = SqlDatabaseManager(DatabaseConfig("jdbc:mysql://localhost:3306/db", "root", "qwerty"))
        launch {
            databaseManager.connect()
            SchemaUtils.create(Users, Actions)
        }

        val userService = SimpleUserService()
        registerService(UserService::class, userService)

        server.pluginManager.registerSuspendingEvents(PlayerListener(userService), this)
    }
}
Shynixn commented 3 years ago

Hi, MCCoroutine automatically wraps all of your listener functions marked with suspend (onJoin) with a coroutine. You can directly call your UserService. Basically it calls internall launch{} before calling your listener function.

In your UserService keep in mind that the calling function is in the context of coroutine running on the minecraft server thread which ticks everything on your server. Therefore you should wrap your long running database operations in an async context.


withContext(Dispatchers.IO) // Or plugin.asyncDispatcher
{
 // database code.
}
Shynixn commented 3 years ago

In the future, I plan to make it possible to be able to simply prepend suspend also to onEnable() and onDisable() but I still need to write the documentation for that.

DokanBoy commented 3 years ago

withContext(Dispatchers.async) {} is somehow different from launchAsync {} (from the Guide)?

If so, when and what should I use?

Shynixn commented 3 years ago

`launchAsync should only be used in special cases where you want to bridge non suspendable code with suspendable code. Once you are in a suspendable function use Kotlin Coroutines as explained on the wiki pages of Kotlin Coroutines. (withContext)

https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html