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):
class_name Steam
signal leaderboard_find_result(handle: int, found: int)
func findLeaderboard(lb_name: String) -> void:
...
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:
Steam.findLeaderboard("highscore")
var result = await Steam.leaderboard_find_result
Do you see a problem here?
What about here:
Steam.findLeaderboard("highscore")
await get_tree().create_timer(5.0).timeout
var 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
func fetch() -> void:
Steam.leaderboard_find_result.connect(_on_leaderboard_find_result, CONNECT_ONE_SHOT)
Steam.findLeaderboard("highscore")
func _on_leaderboard_find_result(handle: int, found: int) -> void:
# process result
pass
or using call_deferred.
func fetch() -> void:
Steam.findLeaderboard.call_deferred("highscore")
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.