utopia-rise / godot-kotlin-jvm

Godot Kotlin JVM Module
MIT License
585 stars 38 forks source link

Add a coroutine context for Godot and its signals. #501

Open CedNaru opened 11 months ago

CedNaru commented 11 months ago

One useful feature GDScript has is the ability to suspend execution of the current function and wait for a signal to be emitted before resuming. Kotlin has an awesome coroutine, and it should be possible to recreate a similar behaviour. Godot uses signals intensively. Not being able to wait for them create many cases where a speudocode that could look like this:

fun foo1() {
  doSomething()
  signal1.await()
  doSomething2()
  signal2.await()
  doSomething3()
}

ends up looking like this:

fun foo1() {
  doSomething()
  signal1.connect(this, ::foo2)
}

fun foo2() {
  doSomething2()
  signal2.connect(this, ::foo3)
}

fun foo3() {
  doSomething3()
}

I suggest to create a coroutine context for Godot kotlin that would extend the signal class by adding an await method. I don't recommend supporting the registration of suspend functions directly as they do not work the same way as regular functions and would require an important rework of the library's internals + the entry generator. Instead the usage would look like this:

fun foo1() = godotCoroutine {
  doSomething()
  signal1.await()
  doSomething2()
}

When using signal1.await() it would call the C++ code to create a new Object called GodotContinuation that will connect to the signal. When the signal is emitted, GodotContinuation would then callback the JVM to resume the coroutine execution. The more complex implementations details are to be investigated, as I am not familiar with the lower level layer of Kotlin coroutines right now.