Skip to content

Commit b820b11

Browse files
authored
Fix signal.await() when signal belongs to a Node (#710)
* Fix connect on nodes
1 parent a62d1dc commit b820b11

File tree

10 files changed

+644
-115
lines changed

10 files changed

+644
-115
lines changed

harness/tests/src/main/kotlin/godot/tests/coroutine/CoroutineTest.kt

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import godot.core.Vector2
1212
import godot.core.signal0
1313
import godot.core.signal1
1414
import godot.core.signal4
15-
import godot.coroutines.GodotCoroutine
15+
import godot.coroutines.godotCoroutine
1616
import godot.coroutines.await
17-
import godot.coroutines.awaitDeferred
1817
import godot.coroutines.awaitLoadAs
18+
import godot.coroutines.awaitMainThread
1919
import godot.global.GD
2020
import kotlinx.coroutines.CoroutineStart
2121

@@ -35,27 +35,27 @@ class CoroutineTest : Object() {
3535
var step: Int = 0
3636

3737
@RegisterFunction
38-
fun startCoroutineWithoutParameter() = GodotCoroutine {
38+
fun startCoroutineWithoutParameter() = godotCoroutine {
3939
step = 1
4040
signalWithoutParameter.await()
4141
step = 2
4242
}
4343

4444
@RegisterFunction
45-
fun startCoroutineWithOneParameter() = GodotCoroutine {
45+
fun startCoroutineWithOneParameter() = godotCoroutine {
4646
step = 3
4747
step = signalWithOneParameter.await()
4848
}
4949

5050
@RegisterFunction
51-
fun startCoroutineWithManyParameters() = GodotCoroutine {
51+
fun startCoroutineWithManyParameters() = godotCoroutine {
5252
step = 5
5353
val (int, _, _, _) = signalWithManyParameters.await()
5454
step = int
5555
}
5656

5757
@RegisterFunction
58-
fun startCoroutineUndispatched() = GodotCoroutine(start = CoroutineStart.UNDISPATCHED) {
58+
fun startCoroutineUndispatched() = godotCoroutine(start = CoroutineStart.UNDISPATCHED) {
5959
step = 7
6060
signalWithoutParameter.await()
6161
step = 8
@@ -68,19 +68,19 @@ class CoroutineTest : Object() {
6868
fun runOnMainThreadFromBackgroundThread() {
6969
val thread = Thread.currentThread().name
7070

71-
GodotCoroutine {
71+
godotCoroutine {
7272
val bgThread = Thread.currentThread().name
7373

7474
var fgThread = ""
75-
awaitDeferred {
75+
awaitMainThread {
7676
fgThread = Thread.currentThread().name
7777
}
7878

7979
val bg2Thread = Thread.currentThread().name
8080

8181
GD.print("Thread names: ${listOf(thread, bgThread, fgThread, bg2Thread).joinToString()}")
8282

83-
awaitDeferred {
83+
awaitMainThread {
8484
runOnMainThreadFromBackgroundThreadFinished.emit(
8585
thread != bgThread
8686
&& thread != bg2Thread
@@ -96,14 +96,14 @@ class CoroutineTest : Object() {
9696

9797
@RegisterFunction
9898
fun asyncLoadResource() {
99-
GodotCoroutine {
99+
godotCoroutine {
100100
val resource = ResourceLoader.awaitLoadAs<PackedScene>("res://Spatial.tscn") { progress ->
101101
GD.print("Resource load progress: $progress")
102102
}
103103

104104
GD.print("Resource: $resource")
105105

106-
awaitDeferred {
106+
awaitMainThread {
107107
asyncLoadResourceFinished.emit(resource != null)
108108
}
109109
}

kt/api-generator/src/main/kotlin/godot/codegen/services/impl/AwaitGenerationService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import godot.tools.common.constants.kotlinxCoroutinePackage
2222

2323
private val cancellableContinuationClass = ClassName(kotlinxCoroutinePackage, "CancellableContinuation")
2424
private val suspendCancellableCoroutine = MemberName(kotlinxCoroutinePackage, "suspendCancellableCoroutine")
25-
private val connect = MemberName(godotCorePackage, "connect")
25+
private val connect = MemberName(godotCorePackage, "connectThreadSafe")
2626
private val resume = MemberName(kotlinCoroutinePackage, "resume")
2727

2828
object AwaitGenerationService : IAwaitGenerationService {

kt/api-generator/src/main/kotlin/godot/codegen/services/impl/SignalGenerationService.kt

Lines changed: 67 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ class SignalGenerationService : ISignalGenerationService {
9898

9999
signalFileSpec.addFunction(generateFakeSignalConstructor(argCount, genericClassNameInfo))
100100
signalFileSpec.addFunction(generateSignalDelegate(argCount, genericClassNameInfo))
101-
signalFileSpec.addFunction(generateSignalExtension(genericClassNameInfo))
101+
signalFileSpec.addFunction(generateSignalExtension(genericClassNameInfo, false))
102+
signalFileSpec.addFunction(generateSignalExtension(genericClassNameInfo, true))
102103
}
103104

104105
return signalFileSpec
@@ -179,6 +180,19 @@ class SignalGenerationService : ISignalGenerationService {
179180
.addCode(generateConnectionCodeBlock())
180181
.build(),
181182

183+
FunSpec.builder(CONNECT_THREAD_SAFE_METHOD_NAME)
184+
.addTypeVariable(godotObjectBoundTypeVariable)
185+
.addParameters(
186+
listOf(
187+
ParameterSpec.builder(TARGET_PARAMETER_NAME, godotObjectBoundTypeVariable).build(),
188+
ParameterSpec.builder(METHOD_PARAMETER_NAME, lambdaTypeName).build(),
189+
flagsParameter
190+
)
191+
)
192+
.returns(ANY.copy(nullable = true))
193+
.addCode(generateConnectionCodeBlock(false, true))
194+
.build(),
195+
182196
FunSpec.builder(DISCONNECT_METHOD_NAME)
183197
.addTypeVariable(godotObjectBoundTypeVariable)
184198
.addParameters(
@@ -196,8 +210,16 @@ class SignalGenerationService : ISignalGenerationService {
196210
.build()
197211
}
198212

199-
private fun generateConnectionCodeBlock(isDisconnect: Boolean = false): CodeBlock {
200-
val methodName = if (isDisconnect) DISCONNECT_METHOD_NAME else CONNECT_METHOD_NAME
213+
private fun generateConnectionCodeBlock(isDisconnect: Boolean = false, isThreadSafe: Boolean = false): CodeBlock {
214+
val methodName = if (isDisconnect) {
215+
DISCONNECT_METHOD_NAME
216+
} else {
217+
if (isThreadSafe) {
218+
CONNECT_THREAD_SAFE_METHOD_NAME
219+
} else {
220+
CONNECT_METHOD_NAME
221+
}
222+
}
201223
val flagsParameters = if (isDisconnect) "" else "$FLAGS_PARAMETER_NAME"
202224

203225
return CodeBlock.of(
@@ -301,37 +323,37 @@ class SignalGenerationService : ISignalGenerationService {
301323
val genericReadOnlyPropertyClassName = readOnlyPropertyClassName.parameterizedBy(GODOT_OBJECT, genericClassNameInfo.genericClassName)
302324

303325
return genericClassNameInfo
304-
.toFunSpecBuilder(SIGNAL_METHOD_NAME + argCount)
305-
.addModifiers(KModifier.INLINE)
306-
.receiver(GODOT_OBJECT)
307-
.addParameters(
308-
(0..<argCount)
309-
.map {
310-
ParameterSpec.builder("p$it", STRING).build()
311-
}
312-
)
313-
.addCode(
314-
if (argCount == 0) {
315-
CodeBlock.of("return·%T.$DELEGATE_PROPERTY_NAME", genericClassNameInfo.className)
316-
} else {
317-
CodeBlock.of("return·%T.$DELEGATE_PROPERTY_NAME·as·%T", genericClassNameInfo.className, genericReadOnlyPropertyClassName)
318-
}
319-
)
320-
.apply {
321-
if (argCount != 0) {
322-
addAnnotation(
323-
AnnotationSpec
324-
.builder(Suppress::class)
325-
.addMember("\"UNCHECKED_CAST\"")
326-
.build()
327-
)
326+
.toFunSpecBuilder(SIGNAL_METHOD_NAME + argCount)
327+
.addModifiers(KModifier.INLINE)
328+
.receiver(GODOT_OBJECT)
329+
.addParameters(
330+
(0..<argCount)
331+
.map {
332+
ParameterSpec.builder("p$it", STRING).build()
328333
}
334+
)
335+
.addCode(
336+
if (argCount == 0) {
337+
CodeBlock.of("return·%T.$DELEGATE_PROPERTY_NAME", genericClassNameInfo.className)
338+
} else {
339+
CodeBlock.of("return·%T.$DELEGATE_PROPERTY_NAME·as·%T", genericClassNameInfo.className, genericReadOnlyPropertyClassName)
340+
}
341+
)
342+
.apply {
343+
if (argCount != 0) {
344+
addAnnotation(
345+
AnnotationSpec
346+
.builder(Suppress::class)
347+
.addMember("\"UNCHECKED_CAST\"")
348+
.build()
349+
)
329350
}
330-
.build()
351+
}
352+
.build()
331353
}
332354

333355

334-
private fun generateSignalExtension(genericClassNameInfo: GenericClassNameInfo): FunSpec {
356+
private fun generateSignalExtension(genericClassNameInfo: GenericClassNameInfo, isThreadSafe: Boolean): FunSpec {
335357
val flagsParameter = ParameterSpec.builder(FLAGS_PARAMETER_NAME, INT)
336358
.defaultValue("0")
337359
.build()
@@ -346,23 +368,34 @@ class SignalGenerationService : ISignalGenerationService {
346368
.addModifiers(KModifier.NOINLINE)
347369
.build()
348370

371+
val methodName = if (isThreadSafe) {
372+
CONNECT_THREAD_SAFE_METHOD_NAME
373+
} else {
374+
CONNECT_METHOD_NAME
375+
}
376+
349377
return genericClassNameInfo
350-
.toReifiedExtensionFunSpecBuilder(CONNECT_METHOD_NAME)
378+
.toReifiedExtensionFunSpecBuilder(methodName)
351379
.addParameters(
352380
listOf(
353381
flagsParameter,
354382
signalConnectExtensionGenericParameters
355383
)
356384
)
357385
.addCode(
358-
"return·$CONNECT_METHOD_NAME($METHOD_PARAMETER_NAME.%M(),·$FLAGS_PARAMETER_NAME)",
386+
"return·$methodName($METHOD_PARAMETER_NAME.%M(),·$FLAGS_PARAMETER_NAME)",
359387
AS_CALLABLE_UTIL_FUNCTION
360388
)
361-
.returns(GODOT_ERROR)
389+
.returns(
390+
if (isThreadSafe) {
391+
ANY.copy(nullable = true)
392+
} else {
393+
GODOT_ERROR
394+
}
395+
)
362396
.build()
363397
}
364398

365-
366399
companion object {
367400
private const val SIGNAL_CLASS_NAME = "Signal"
368401
private const val INSTANCE_PARAMETER = "instance"
@@ -375,6 +408,7 @@ class SignalGenerationService : ISignalGenerationService {
375408

376409
private const val EMIT_METHOD_NAME = "emit"
377410
private const val CONNECT_METHOD_NAME = "connect"
411+
private const val CONNECT_THREAD_SAFE_METHOD_NAME = "connectThreadSafe"
378412
private const val DISCONNECT_METHOD_NAME = "disconnect"
379413
private const val SIGNAL_METHOD_NAME = "signal"
380414
private const val JAVA_CREATE_METHOD_NAME = "javaCreate"

0 commit comments

Comments
 (0)