I’ve been doing Steam integration with GodotSteam and made a tricky mistake. It’s not an issue with GodotSteam in particular, but a pattern that is fairly common in GDScript. So, I thought I’d share.

Mistake

GodotSteam API looks sort of like this (pseudocode):

1class_name Steam
2
3signal leaderboard_find_result(handle: int, found: int)
4
5func findLeaderboard(lb_name: String) -> void:
6    ...

You call findLeaderboard, which is a long I/O bound operation, so you need to connect to leaderboard_find_result to get the result.

Because I didn’t really care to block anything, I wrote code like this:

1Steam.findLeaderboard("highscore")
2var result = await Steam.leaderboard_find_result

Do you see a problem here?

What about here:

1Steam.findLeaderboard("highscore")
2await get_tree().create_timer(5.0).timeout
3var result = await Steam.leaderboard_find_result

If leaderboard is found within the 5 second timeout, you may never get the handle you expect to get.

It may sound obvious, but you need to connect to a signal before making a function call. So awaiting with this API may not be the best option.

Solution

So your options are either avoiding awaits entirely

1func fetch() -> void:
2    Steam.leaderboard_find_result.connect(_on_leaderboard_find_result, CONNECT_ONE_SHOT)
3    Steam.findLeaderboard("highscore")
4
5func _on_leaderboard_find_result(handle: int, found: int) -> void:
6     # process result
7     pass

or using call_deferred.

1
2func fetch() -> void:
3    Steam.findLeaderboard.call_deferred("highscore")
4    var result = await Steam.leaderboard_find_result

In latter case Steam.findLeaderboard will be called at the end of the frame, i.e. after we have already started awaiting for Steam.leaderboard_find_result.