Godot 4 · GDScript
Global leaderboards
Creating Leaderboards
From the dashboard, open the Leaderboards section and create a table by providing a name. Configure order, number of scores, and extra data fields.
Leaderboard Settings
Once created, configure:
- Order: descending or ascending.
- Scores to show: number of rows returned.
- Min/Max score: optional bounds; submissions outside them are rejected.
- Custom fields: extra data per score (level, country, etc.), up to 20 depending on your plan.
- Temporal scores: daily/weekly/monthly buckets. Off = historic-only table; a submit that does not beat the best score stores nothing.
Save each table key: you will use it in set_leaderboard.
Multiple Tables & Indexes
Declare all your tables together. The order defines the index you will always use (1, 2, 3...).
Gamdato.set_leaderboard(["key_1", "key_2", "key_3"])
# key_1 -> table 1, key_2 -> table 2, key_3 -> table 3Submitting Scores
Submit a player score to a table by its index, with optional custom data:
# Recommended: retries when offline and survives app closes.
Gamdato.submit_with_retry(1, score, {"level": level_reached})# submit_completed(success: bool, table_index: int, server_score: int)
# submit_effect(table_index: int, effect: String, periods_enabled: bool)
# effect = "updated" the historic (all-time) score was written
# "periods_only" only the daily/weekly/monthly buckets improved
# "not_improved" nothing was stored (doesn't beat your best)
Gamdato.submit_effect.connect(func(table, effect, periods_enabled):
match effect:
"updated": print("new historic best!")
"periods_only": print("improved a daily/weekly/monthly board")
"not_improved": print("kept your previous best"))submit_with_retry only replaces a queued score when the new one is higher. Use submit() for a simple send without retry. The submit_effect signal reports the exact effect: "updated" (historic score written), "periods_only" (only the daily/weekly/monthly buckets improved) or "not_improved" (nothing stored). On tables with temporal scores disabled, a score that cannot beat your cached best is not even sent (the call returns false), check get_table_config(table).
Reading Scores
These functions return the last loaded data instantly, with no network wait:
- get_table_data(table): The top of the table with extra data.
- get_best_score(table) / get_player_pos(table): Your best score and your rank.
for row in Gamdato.get_table_data(1):
print("#%d %s %d" % [row.rank, row.player_name, row.score])
print("Your best: %d (rank %d)" % [Gamdato.get_best_score(1), Gamdato.get_player_pos(1)])# get_table_data(table) -> Array, each row:
{
"rank": int,
"player_key": String,
"player_name": String,
"score": int,
"custom_data": Dictionary
}
# custom_data holds the extra fields you defined for the table in the panel, e.g.:
{
"level": 2,
"country": "Mexico"
}Views: Periods & Friends
Request filtered views (daily, weekly, monthly, or friends-only) and listen to view_loaded:
Gamdato.view_loaded.connect(_on_view)
Gamdato.load_leaderboard_view(1, "weekly") # weekly top
Gamdato.load_leaderboard_view(1, "all", {"friends_only": true}) # friends only
func _on_view(table, period, friends_only, scores, my_score) -> void:
for row in scores:
print("#%d %s %d" % [row.rank, row.player_name, row.score])# scores: Array, each row:
{
"rank": int,
"player_key": String,
"player_name": String,
"score": int,
"custom_data": Dictionary,
"date": String
}
# player_score: Dictionary (your own row), or null if you have no score:
{
"rank": int,
"score": int,
"custom_data": Dictionary,
"date": String # period bucket the score belongs to
}
# custom_data keys are the extra fields you defined for the table in the panel.
# The response also carries the table's dashboard config:
# "leaderboard": { "name": String, "sort_order": "asc" | "desc", "periods_enabled": bool }
# Period views (daily/weekly/monthly) additionally include "period" and "period_key".Search & View Players
Search by name or look up another player's profile:
Gamdato.search_players(1, "text") # -> players_search_completed
Gamdato.load_player_info(player_key) # -> player_info_loaded# players_search_completed -> results: Array, each:
{
"rank": int,
"player_key": String,
"player_name": String,
"score": int
}
# player_info_loaded -> player: Dictionary
{
"player_key": String,
"player_name": String,
"is_invitable": bool,
"created_at": String
}
# ...plus scores: Array, one per leaderboard the player appears on:
{
"leaderboard": {
"table_key": String,
"name": String,
"sort_order": String # "asc" | "desc"
},
"rank": int,
"score": int,
"custom_data": Dictionary,
"date": String,
"updated_at": String
}