-
Notifications
You must be signed in to change notification settings - Fork 15
Leaderboards
The Steam API supports persistent leaderboards with automatically ordered entries. These leaderboards can be used to display global and friend leaderboards in your game and on the community web page for your game. Each game can have up to 10,000 leaderboards, and each leaderboard can be retrieved immediately after a player's score has been inserted into it, but note that for each leaderboard, a player can have only one entry, although there is no limit on the number of players per leaderboard.
Each leaderboard entry contains a name, a score and a rank for the leaderboard, and this data will be replaced when a new leaderboard entry is created for the user, and the following functions can be used to add and retrieve this data form the leaderboards for your game:
- steam_create_leaderboard
- steam_upload_score
- steam_upload_score_ext
- steam_upload_score_buffer
- steam_upload_score_buffer_ext
- steam_download_scores
- steam_download_scores_around_user
- steam_download_friends_scores
- steam_get_leaderboard_entry_count
- steam_get_leaderboard_display_type
The following data types are used by the leaderboard functions:
The following constants are used by the leaderboard functions:
With this function you can create a new leaderboard for your game. The first argument is a string which defines the name of your leaderboard, and this name should be used in any further function calls relating to the leaderboard being created. You can then define the sort order (see LeaderboardSortOrder constants) as well as the way in which the information is displayed (see LeaderboardDisplayType constants).
Note
If you have previously created a leaderboard with the same name (either through code or through your Steam page for the game), then this function will not create a new one.
This function operates asynchronously, which means that it does not immediately return the requested result. Instead, upon completion of the task, it will trigger the Steam Async Event.
Syntax:
steam_create_leaderboard(lb_name, sort_order, display_type)
Argument | Type | Description |
---|---|---|
lb_name | String | The name of the leaderboard that you are creating |
sort_order | LeaderboardSortOrder | The method for sorting the leaderboard entries |
display_type | LeaderboardDisplayType | The way to display the leaderboard to the user |
Returns:
Triggers:
Key | Type | Description |
---|---|---|
id | Real | The asynchronous request ID |
event_type | String | The string value "create_leaderboard"
|
status | Real | The status code, 0 if the leaderboard was created and 1 if it already existed |
lb_name | String | The name of the leaderboard |
Example:
steam_create_leaderboard("Game Times", lb_sort_ascending, lb_disp_time_sec);
The above code will create a leaderboard called "Game Times", and set it to display the results in ascending order and with a display in seconds.
This function will send a score to the given leaderboard. The score to be uploaded is a real number, and the leaderboard name is a string that was defined when you created the leaderboard using the function steam_create_leaderboard.
Note
If the function call fails for any reason it will return -1 and the Async event will not be triggered.
This function operates asynchronously, which means that it does not immediately return the requested result. Instead, upon completion of the task, it will trigger the Steam Async Event.
Syntax:
steam_upload_score(lb_name, score)
Argument | Type | Description |
---|---|---|
lb_name | String | The name of the leaderboard that you are uploading the scores to |
score | Real | The score to upload |
Returns:
Triggers:
Key | Type | Description |
---|---|---|
post_id | Real | The asynchronous request ID |
event_type | String | The string value "leaderboard_upload"
|
lb_name | String | The name of the leaderboard |
num_entries | Real | The number of returned entries |
success | Boolean | Whether or not the request was successful |
updated | Boolean | Whether or not the leaderboard was updated (i.e.: the new score was better) |
score | Real | The score that was posted to the leaderboard |
Example:
In this example, we first upload a score and then parse the async_load
map returned if successful. The code below shows a typical example for uploading:
if (hp <= 0)
{
upload_ID = steam_upload_score("Game Scores", score);
if (!upload_ID)
{
alarm[0] = game_get_speed(gamespeed_fps);
}
}
Note that we have set an alarm if the call fails. This would be used to try the upload again at a later time and you can add extra code there to retry the upload or to save the score to a text file should it continue to fail, etc. We now add the following into the Steam Async Event for the instance controlling the scores:
var _type = ds_map_find_value(async_load, "event_type");
if (_type == "leaderboard_upload")
{
var _lb_ID = ds_map_find_value(async_load, "post_id");
if _lb_ID == upload_ID
{
var _lb_name = ds_map_find_value(async_load, "lb_name");
var _lb_done = ds_map_find_value(async_load, "success");
var _lb_score = ds_map_find_value(async_load, "score");
var _lb_updated = ds_map_find_value(async_load, "updated");
show_debug_message("leaderboard post ID:" + string(_lb_ID) + " to lb:" + string(_lb_name) + " with score:" + string(_lb_score) + " updated=" + string(_lb_updated));
if (_lb_done)
{
show_debug_message("- Succeeded");
}
else
{
show_debug_message("- Failed");
}
}
}
In the example we are simply outputting the return values to the compiler window as debug messages, but you can use this event to deal with the information in any way you choose.
This function will send a score to the given leaderboard. It is similar to the function steam_upload_score but has an extra argument that will allow you to force the update of the score, as by default Steam only updates the score if it is better than the previous one.
Note
If the function call fails for any reason it will return -1 and the Async event will not be triggered.
This function operates asynchronously, which means that it does not immediately return the requested result. Instead, upon completion of the task, it will trigger the Steam Async Event.
Syntax:
steam_upload_score_ext(lb_name, score, force_update)
Argument | Type | Description |
---|---|---|
lb_name | String | The name of the leaderboard that you are uploading the scores to |
score | Real | The score to upload |
force_update | Boolean | Whether or not the value should be replaced |
Returns:
Triggers:
Key | Type | Description |
---|---|---|
post_id | Real | The asynchronous request ID |
event_type | String | The string value "leaderboard_upload"
|
lb_name | String | The name of the leaderboard |
num_entries | Real | The number of returned entries |
success | Boolean | Whether or not the request was successful |
updated | Boolean | Whether or not the leaderboard was updated (i.e.: the new score was better or forceUpdate was set to true ) |
score | Real | The score that was posted to the leaderboard |
Example:
In this example, we first upload a score and then parse the async_load
map returned if successful. The code below shows a typical example for uploading:
if (hp <= 0)
{
upload_ID = steam_upload_score_ext("Game Scores", score, true);
if (!upload_ID)
{
alarm[0] = game_get_speed(gamespeed_fps);
}
}
Note that we have set an alarm if the call fails. This would be used to try the upload again at a later time and you can add extra code there to retry the upload or to save the score to a text file should it continue to fail, etc. We now add the following into the Steam Async Event for the instance controlling the scores:
var _type = ds_map_find_value(async_load, "event_type");
if (_type == "leaderboard_upload")
{
var _lb_ID = ds_map_find_value(async_load, "post_id");
if _lb_ID == upload_ID
{
var _lb_name = ds_map_find_value(async_load, "lb_name");
var _lb_done = ds_map_find_value(async_load, "success");
var _lb_score = ds_map_find_value(async_load, "score");
var _lb_updated = ds_map_find_value(async_load, "updated");
show_debug_message("leaderboard post id:" + string(_lb_ID) + " to lb:" + string(_lb_name) + " with score:" + string(_lb_score) + " updated=" + string(_lb_updated));
if (_lb_done)
{
show_debug_message("- Succeeded");
}
else
{
show_debug_message("- Failed");
}
}
}
In the example we are simply outputting the return values to the compiler window as debug messages, but you can use this event to deal with the information in any way you choose.
This function will send a score to the given leaderboard along with a data package created from a buffer. The buffer should be no more than 256 bytes in size - anything beyond that will be chopped off - and can contain any data you require. The score to be uploaded should be a real number, and the leaderboard name is a string that was defined when you created the leaderboard using the function steam_create_leaderboard.
Note
If the function call fails for any reason it will return -1 and the Async event will not be triggered.
This function operates asynchronously, which means that it does not immediately return the requested result. Instead, upon completion of the task, it will trigger the Steam Async Event.
Syntax:
steam_upload_score_buffer(lb_name, score, buffer)
Argument | Type | Description |
---|---|---|
lb_name | String | The name of the leaderboard that you are uploading the scores to |
score | Real | The score to upload |
buffer | Buffer | The ID of the buffer to attach |
Returns:
Triggers:
Key | Type | Description |
---|---|---|
post_id | Real | The asynchronous request ID |
event_type | String | The string value "leaderboard_upload"
|
lb_name | String | The name of the leaderboard |
num_entries | Real | The number of returned entries |
success | Boolean | Whether or not the request was successful |
updated | Boolean | Whether or not the leaderboard was updated (i.e.: the new score was better). Note that if you score was not updated neither will the data buffer. |
score | Real | The score that was posted to the leaderboard |
Example:
In this example, we first upload a score and then parse the async_load
map returned if successful. The code below shows a typical example for uploading, with a buffer being created to hold a string telling us which level the score was uploaded from:
if (hp <= 0)
{
var _buff = buffer_create(256, buffer_fixed, 1);
buffer_write(_buff, buffer_string, "Uploaded on level " + string(global.Level));
upload_ID = steam_upload_score("Game Scores", score, _buff);
if (!upload_ID)
{
alarm[0] = game_get_speed(gamespeed_fps);
}
buffer_delete(_buff);
}
Note that we have set an alarm if the call fails. This would be used to try the upload again at a later time and you can add extra code there to retry the upload or to save the score to a text file should it continue to fail, etc. Also note that we immediately delete the buffer, since it is no longer required for the function. We now add the following into the Steam Async Event for the instance controlling the scores:
var _type = ds_map_find_value(async_load, "event_type");
if (_type == "leaderboard_upload")
{
var _lb_ID = ds_map_find_value(async_load, "post_id");
if _lb_ID == upload_ID
{
var _lb_name = ds_map_find_value(async_load, "lb_name");
var _lb_done = ds_map_find_value(async_load, "success");
var _lb_score = ds_map_find_value(async_load, "score");
var _lb_updated = ds_map_find_value(async_load, "updated");
show_debug_message("leaderboard post ID:" + string(_lb_ID) + " to lb:" + string(_lb_name) + " with score:" + string(_lb_score) + " updated=" + string(_lb_updated));
if (_lb_done)
{
show_debug_message("- Succeeded");
}
else
{
show_debug_message("- Failed");
}
}
}
In the example we are simply outputting the return values to the compiler window as debug messages, but you can use this event to deal with the information in any way you choose.
This function will send a score to the given leaderboard along with a data package created from a buffer. The buffer should be no more than 256 bytes in size - anything beyond that will be chopped off - and can contain any data you require. This function is similar to steam_upload_score_buffer but has an extra argument that will allow you to force the update of the score, as by default Steam only updates the score if it is better than the previous one.
Note
If the function call fails for any reason it will return -1 and the Async event will not be triggered.
This function operates asynchronously, which means that it does not immediately return the requested result. Instead, upon completion of the task, it will trigger the Steam Async Event.
Syntax:
steam_upload_score_buffer_ext(lb_name, score, buffer, force_update)
Argument | Type | Description |
---|---|---|
lb_name | String | The name of the leaderboard that you are uploading the scores to |
score | Real | The score to upload |
buffer | Buffer | The ID of the buffer to attach |
force_update | Boolean | Whether or not the value should be replaced |
Returns:
Triggers:
Key | Type | Description |
---|---|---|
post_id | Real | The asynchronous request ID |
event_type | String | The string value "leaderboard_upload"
|
lb_name | String | The name of the leaderboard |
num_entries | Real | The number of returned entries |
success | Boolean | Whether or not the request was successful |
updated | Boolean | Whether or not the leaderboard was updated (i.e.: the new score was better or forceUpdate was set to true ). Note that if you score was not updated neither will be the data buffer. |
score | Real | The score that was posted to the leaderboard |
Example:
In this example, we first upload a score and then parse the async_load
map returned if successful. The code below shows a typical example for uploading, with a buffer being created to hold a string telling us which level the score was uploaded from:
if (hp <= 0)
{
var _buff = buffer_create(256, buffer_fixed, 1 );
buffer_write(_buff, buffer_string, "Uploaded on level " + string(global.Level));
upload_ID = steam_upload_score_buffer_ext("Game Scores", score, _buff, true);
if (!upload_ID)
{
alarm[0] = game_get_speed(gamespeed_fps);
}
buffer_delete(_buff);
}
Note that we have set an alarm if the call fails. This would be used to try the upload again at a later time and you can add extra code there to retry the upload or to save the score to a text file should it continue to fail, etc. Also note that we immediately delete the buffer, since it is no longer required for the function. We now add the following into the Steam Async Event for the instance controlling the scores:
var _type = ds_map_find_value(async_load, "event_type");
if (_type == "leaderboard_upload")
{
var _lb_ID = ds_map_find_value(async_load, "post_id");
if _lb_ID == upload_ID
{
var _lb_name = ds_map_find_value(async_load, "lb_name");
var _lb_done = ds_map_find_value(async_load, "success");
var _lb_score = ds_map_find_value(async_load, "score");
var _lb_updated = ds_map_find_value(async_load, "updated");
show_debug_message("leaderboard post ID:" + string(_lb_ID) + " to lb:" + string(_lb_name) + " with score:" + string(_lb_score) + " updated=" + string(_lb_updated));
if (_lb_done)
{
show_debug_message("- Succeeded");
}
else
{
show_debug_message("- Failed");
}
}
}
In the example we are simply outputting the return values to the compiler window as debug messages, but you can use this event to deal with the information in any way you choose.
This function is used to retrieve a sequential range of leaderboard entries by leaderboard ranking. The start_idx
and end_idx
parameters control the requested range of ranks, for example, you can display the top 10 on a leaderboard for your game by setting the start value to 1 and the end value to 10. The leaderboard name is a string that was defined when you created the leaderboard using the function steam_create_leaderboard.
Note
If the function call fails for any reason it will return -1 and the async event will not be triggered.
This function operates asynchronously, which means that it does not immediately return the requested result. Instead, upon completion of the task, it will trigger the Steam Async Event.
Syntax:
steam_download_scores(lb_name, start_idx, end_idx)
Argument | Type | Description |
---|---|---|
lb_name | String | The name of the leaderboard that you are downloading the scores from |
start_idx | Real | The start position within the leaderboard |
end_idx | Real | The end position within the leaderboard |
Returns:
Triggers:
Key | Type | Description |
---|---|---|
id | Real | The asynchronous request ID |
event_type | String | The string value "leaderboard_download"
|
status | Boolean | The status code if download fails |
lb_name | String | The name of the leaderboard |
num_entries | Real | The number of entries returned |
entries | String | A JSON-formatted string with all the downloaded entries (see LeaderboardEntry for details) |
Example:
In this extended example we will request the top ten ranking entries for the given leaderboard and parse its results in the Steam Async Event. To start with we need to request the scores with the following code:
/// Create Event
leaderboard = [];
scores_request_id = steam_download_scores("Game Scores", 1, 10);
This code initialises an array variable in leaderboard
and then sends off a request to the Steam Server to download the scores from the leaderboard named "Game Scores". The function returns the async ID of the request, which is stored in an instance variable scores_request_id
. This will then be handled in the Steam Async Event in the following way:
/// Async Steam Event
var _async_id = ds_map_find_value(async_load, "id");
if (_async_id != scores_request_id)
{
// This isn't a response to our request, ignore it.
exit;
}
if (async_load[? "status"] == 0)
{
// This is a response to the request we made, but something seems to have gone wrong.
// Handle this, then exit the event.
exit;
}
// Get the entries
var _entries_json = ds_map_find_value(async_load, "entries");
var _entries = json_parse(_entries_json);
leaderboard = _entries.entries;
// Get any custom data if it's present and assign to a struct variable named "message"
var _data_base64, _buffer, _message;
array_foreach(leaderboard, function(_element, _index)
{
_element.message = "";
if (struct_exists(_element, "data"))
{
_data_base64 = _element.data;
_buffer = buffer_base64_decode(_data_base64);
_element.message = buffer_read(_buffer, buffer_string);
buffer_delete(_buffer);
}
});
First we check the "id"
key of the async_load DSMap. If this value is the same as the value returned by the original call to the function (stored in the scores_request_id
variable) we continue to process the data. After checking the async ID, we can be sure that the async event is of the right "event_type"
, so a check on that isn't strictly necessary here.
The request status is checked next by checking async_load's "status"
key. If anything went wrong, it can be handled and we exit the event.
The first thing we do after the above checks is get the value of the async_load map's "entries"
key which will contain a JSON-formatted string containing the leaderboard data.
This JSON object is then parsed using json_parse and the returned struct stored in a local variable _entries
.
The actual leaderboard data is in an array under the struct's entries
variable, which can be directly assigned to the instance's leaderboard
variable.
In an optional next step we check every item using array_foreach for the presence of a "data"
key. If this key exists, the data is decoded into a Buffer and the contents read as a String.
The message is then assigned as an additional variable "message"
to the current score struct to avoid reading from the buffer again when drawing this info in a Draw event.
/// Draw GUI Event
array_foreach(leaderboard, function(_element, _index)
{
var _msg = (_element.message == "") ? "No Message" : _element.message;
draw_text(5, 5 + _index * 20, $"{_element.rank}. {_element.score} ({_element.user}) - (_msg)");
});
The code above draws the leaderboard in the Draw GUI event. If the message turns out to be an empty string for whatever reason, the text "No Message" is shown instead.
This function is used to retrieve leaderboard entries relative to the current user's entry. The range_start
parameter is the number of entries to retrieve before the current user's entry, and the range_end
parameter is the number of entries after the current user's entry, and the current user's entry is always included in the results. For example, if the current user is number 5 on a given leaderboard, then setting the start range to -2 and the end range to 2 will return 5 entries: 3 through 7. If there are not enough entries in the leaderboard before or after the user's entry, Steam will adjust the range start and end points trying to maintained the range size. For example, if the user is #1 on the leaderboard, start is set to -2, and end is set to 2, Steam will return the first 5 entries in the leaderboard.
Note
If the function call fails for any reason it will return -1 and the async event will not be triggered.
This function operates asynchronously, which means that it does not immediately return the requested result. Instead, upon completion of the task, it will trigger the Steam Async Event.
Syntax:
steam_download_scores_around_user(lb_name, range_start, range_end)
Argument | Type | Description |
---|---|---|
lb_name | String | The name of the leaderboard that you are downloading the scores from |
range_start | Real | The start position within the leaderboard |
range_end | Real | The end position within the leaderboard |
Returns:
Triggers:
Key | Type | Description |
---|---|---|
id | Real | The asynchronous request ID |
event_type | String | The string value "leaderboard_download"
|
status | Boolean | The status code if download fails |
lb_name | String | The name of the leaderboard |
num_entries | Real | The number of entries returned |
entries | String | A JSON-formatted string with all the downloaded entries (see LeaderboardEntry for details) |
Example:
request_id = steam_download_scores_around_user("Game Scores", -4, 5);
This will send off a request to the Steam Server for a range of 10 scores from the leaderboard "Game Scores"
, centered on the player and will store the async ID of the request in the variable request_id
. This will then be handled in the Steam Async Event, as shown in the example for steam_download_scores.
With this function you can retrieve only the scores on the leaderboard that belong to those people that are marked as "friends" in the Steam client. So, if your leaderboard has 200 entries, and 50 of them are your friends, this function will retrieve only those 50 results. The leaderboard name is a string that was defined when you created the leaderboard using the function steam_create_leaderboard.
Note
If the function call fails for any reason it will return -1 and the async event will not be triggered.
This function operates asynchronously, which means that it does not immediately return the requested result. Instead, upon completion of the task, it will trigger the Steam Async Event.
Syntax:
steam_download_friends_scores(lb_name)
Argument | Type | Description |
---|---|---|
lb_name | String | The name of the leaderboard that you are downloading the scores from |
Returns:
Triggers:
Key | Type | Description |
---|---|---|
id | Real | The asynchronous request ID |
event_type | String | The string value "leaderboard_download"
|
status | Int64 | The status code if download fails |
lb_name | String | The name of the leaderboard |
num_entries | Real | The number of returned entries |
entries | String | A JSON formatted string with all the downloaded entries (see LeaderboardEntry for details) |
Example:
request_id = steam_download_friends_scores("Game Scores");
This will send off a request to the Steam Server for the users friends scores from the given leaderboard and will store the async ID of the request in the variable request_id
. This will then be handled in the Steam Async Event, as shown in the example for steam_download_scores.
This function returns the total number of entries in a leaderboard.
The function returns -1 in case something went wrong.
Syntax:
steam_get_leaderboard_entry_count(lb_name)
Argument | Type | Description |
---|---|---|
lb_name | String | The name of the leaderboard. |
Returns:
This function returns the display type of a leaderboard handle.
The function returns -1 in case something went wrong.
Syntax:
steam_get_leaderboard_display_type(lb_name)
Argument | Type | Description |
---|---|---|
lb_name | String | The name of the leaderboard. |
Returns:
These constants specify the display type of a leaderboard.
These constants are referenced by the following functions:
Member | Description |
---|---|
lb_disp_none |
Show the leaderboard "as is". |
lb_disp_numeric |
Show the leaderboard as a numeric display. |
lb_disp_time_sec |
Show the leaderboard values as times, with the base value being seconds. |
lb_disp_time_ms |
Show the leaderboard values as times, with the base value being milliseconds |
These constants specify the sort order of a leaderboard.
These constants are referenced by the following functions:
Member | Description |
---|---|
lb_sort_none |
No sorting. The information will be displayed "as is". |
lb_sort_ascending |
Sort the leaderboard in ascending order. |
lb_sort_descending |
Sort the leaderboard in descending order. |
A leaderboard entry is represented by a JSON formatted string that can be returned by the async callback event of a couple of functions.
This string can be decoded into a DSMap (see json_decode, needs to be destroyed afterwards) or into a Struct (see json_parse, recommended) and will provide the following members.
Member | Type | Description |
---|---|---|
rank | Real | The rank of the entry on the specified leaderboard |
data | String | The base64 encoded string with the data provided when uploading scores using the steam_upload_score_buffer or steam_upload_score_buffer_ext functions ✴️ OPTIONAL |
score | Real | The score attributed to this entry |
name | String | The display name of the player for this entry |
userID | Int64 | The unique user ID of the player for this entry > [!NOTE] > > If steam_upload_score_buffer or steam_upload_score_buffer_ext were used to upload the score, the decoded entry will now have a "data" key so you can retrieve the data of the uploaded buffer (see the steam_download_scores extended code example for further details). This data will be base64-encoded and so you will need to use the function buffer_base64_decode on the data before reading from the buffer. |
GameMaker 2025