Quick Start
Overview
- Leaderboard lets developers build leaderboards along custom dimensions. The game submits scores; TapTap handles ranking and serves either UI or raw data for the game to display. Each leaderboard ships with both a public collection and a friends collection, and supports permanent boards as well as period-reset boards.
- For a more detailed feature overview, see Leaderboard Features.
Integration
- This guide uses C++ to demonstrate leaderboard integration; other languages can refer to the corresponding DLL calling methods.
- Before integrating leaderboard, read Quick Start to understand the basic integration process, and complete SDK initialization.
You must successfully call TapSDK_Init before calling any Leaderboard API. Calling leaderboard functions without initialization or after initialization failure will return errors or unexpected results.
1. Submit Scores
When a player produces a new score, call TapLeaderboard_AsyncSubmitScores() to submit it. Up to 5 records can be submitted per call. The result is returned via the TapEventID::LeaderboardSubmitScores callback:
#include <atomic>
#include <iostream>
#include <thread>
#include "taptap_api.h"
#include "taptap_leaderboard.h"
using namespace std;
atomic<int64_t> gRequestID;
// Submit scores callback
void submitScoresCallback(TapEventID eventID, void* data)
{
// Cast data to TapLeaderboardSubmitScoresResponse* to read the result
// Note: data and any memory it references are released after the callback returns. Copy if you need to keep it.
auto resp = static_cast<TapLeaderboardSubmitScoresResponse*>(data);
if (resp->error == nullptr) { // Request succeeded
for (uint32_t i = 0; i < resp->result_count; ++i) {
const auto& r = resp->results[i];
if (r.score_result == nullptr) {
continue;
}
// All string fields (char*) are UTF-8 encoded. Convert as needed.
cout << "submit callback success: requestID=" << resp->request_id
<< ", leaderboard_id=" << r.leaderboard_id
<< ", period_token=" << (r.period_token ? r.period_token : "null")
<< ", raw_score=" << r.score_result->raw_score
<< ", new_best=" << r.score_result->new_best << endl;
}
} else { // Request failed
cout << "submit callback failed, code=" << resp->error->code
<< ", message=" << resp->error->message << endl;
}
}
int main()
{
// SDK initialization and launch verification are omitted here — assume they have completed successfully
// 1. Register the submit-scores callback (only needs to be done once)
TapSDK_RegisterCallback(TapEventID::LeaderboardSubmitScores, submitScoresCallback);
// 2. Build the request — at most 5 items per call
TapLeaderboardScoreItem items[2];
// Zero-init the structs to avoid wild pointers from uninitialized memory
// For C++11+, you can use TapLeaderboardScoreItem items[2]{}; — no memset needed
memset(items, 0, sizeof(items));
// All string fields (char*) must be UTF-8 encoded
items[0].leaderboard_id = "your_leaderboard_id_1"; // Leaderboard ID configured in the developer center
items[0].score = 1999;
items[1].leaderboard_id = "your_leaderboard_id_2";
items[1].score = 256;
TapLeaderboardSubmitScoresRequest submitRequest;
memset(&submitRequest, 0, sizeof(submitRequest));
submitRequest.item_count = 2;
submitRequest.items = items;
// 3. Start the async submit-scores request
auto ret = TapLeaderboard_AsyncSubmitScores(
TapLeaderboard(), // Leaderboard singleton
++gRequestID, // Developer-generated request ID; echoed in the callback
&submitRequest);
if (ret != TapSDK_Result_OK) { // Failed to start the request — no callback will be invoked
cout << "TapLeaderboard_AsyncSubmitScores failed, ret=" << ret << endl;
return -1;
}
// Request started successfully — wait for the callback
// 4. Main game loop
bool running = true;
while (running) {
// Process SDK callback events
TapSDK_RunCallbacks();
// Your game logic
// ...
// Adjust sleep duration to match your target frame rate
this_thread::sleep_for(chrono::milliseconds(100));
}
// 5. Clean up
TapSDK_Shutdown();
return 0;
}
2. Request Friend-Info Authorization (only when using the friends collection)
Only required when the game needs the friends collection (collection = TapLeaderboardCollection_Friends). Before fetching or displaying the friends collection, it is recommended to first call TapUser_AsyncGetApprovedScopes to query the user's currently approved scopes, and only call TapUser_AsyncAuthorize("user_friends") when user_friends is missing from the approved scopes—this avoids re-prompting users who have already granted consent. Friends-collection APIs return an authorization error until consent is granted. The approved-scopes result is delivered via the TapEventID::UserGetApprovedScopes callback, and the authorization result is delivered via the TapEventID::AuthorizeFinished callback. See TapUser_AsyncGetApprovedScopes and TapUser_AsyncAuthorize for the related APIs.
#include <atomic>
#include <cstring>
#include <iostream>
#include <thread>
#include "taptap_api.h"
using namespace std;
// Request user_friends authorization
void requestUserFriendsAuthorize()
{
auto ret = TapUser_AsyncAuthorize("user_friends");
switch (ret) {
case TapUser_AsyncAuthorize_Result::OK:
cout << "authorize request sent, waiting for user confirmation" << endl;
break;
case TapUser_AsyncAuthorize_Result::InFlight:
cout << "authorize already in progress, please wait" << endl;
break;
case TapUser_AsyncAuthorize_Result::Failed:
cout << "TapUser_AsyncAuthorize failed, please retry" << endl;
break;
case TapUser_AsyncAuthorize_Result::Unknown:
cout << "authorize failed, please check SDK init state" << endl;
break;
}
}
// Get-approved-scopes callback
void getApprovedScopesCallback(TapEventID eventID, void* data)
{
// Cast data to TapUserGetApprovedScopesResponse* to read the approved scopes
auto resp = static_cast<TapUserGetApprovedScopesResponse*>(data);
if (resp->error != nullptr) {
cout << "get approved scopes failed, code=" << resp->error->code
<< ", message=" << resp->error->message << endl;
return;
}
// Check whether user_friends has been approved
bool hasUserFriends = false;
for (uint32_t i = 0; i < resp->scope_count; ++i) {
if (strcmp(resp->scopes[i], "user_friends") == 0) {
hasUserFriends = true;
break;
}
}
if (hasUserFriends) {
cout << "user_friends already approved, no need to request again" << endl;
// Already approved—friends-collection APIs (collection = TapLeaderboardCollection_Friends) can be called.
} else {
cout << "user_friends not approved, requesting authorize" << endl;
requestUserFriendsAuthorize();
}
}
// Authorize-finished callback
void authorizeFinishedCallback(TapEventID eventID, void* data)
{
// Cast data to AuthorizeFinishedResponse* to read the authorization result
auto resp = static_cast<AuthorizeFinishedResponse*>(data);
if (resp->is_cancel) {
cout << "authorize cancelled by user" << endl;
return;
}
if (resp->error[0] != '\0') {
cout << "authorize failed, error=" << resp->error << endl;
return;
}
// Note: every string field (char*) is UTF-8 encoded; convert as needed.
cout << "authorize success, scope=" << resp->scope << endl;
// From here on, friends-collection APIs (collection = TapLeaderboardCollection_Friends) can be called.
}
int main()
{
// SDK init and startup verification are omitted; assume they have completed successfully.
// 1. Register the callbacks once.
TapSDK_RegisterCallback(TapEventID::UserGetApprovedScopes, getApprovedScopesCallback);
TapSDK_RegisterCallback(TapEventID::AuthorizeFinished, authorizeFinishedCallback);
// 2. Query the currently approved scopes; only request authorization in the callback when user_friends is missing.
int64_t requestID = 1; // Generated by the developer to correlate request and callback
auto ret = TapUser_AsyncGetApprovedScopes(requestID);
if (ret != TapSDK_Result_OK) {
cout << "TapUser_AsyncGetApprovedScopes failed, code=" << ret << endl;
return -1;
}
// 3. Game main loop
bool running = true;
while (running) {
TapSDK_RunCallbacks();
// Your game logic
// ...
this_thread::sleep_for(chrono::milliseconds(100));
}
// 4. Clean up
TapSDK_Shutdown();
return 0;
}
3. Load Leaderboard Scores
Call TapLeaderboard_AsyncLoadScores() to fetch the score list of a leaderboard. Public/friends switching, period query, and pagination are supported. The result is returned via the TapEventID::LeaderboardLoadScores callback:
#include <atomic>
#include <iostream>
#include <thread>
#include "taptap_api.h"
#include "taptap_leaderboard.h"
using namespace std;
atomic<int64_t> gRequestID;
// Load-scores callback
void loadScoresCallback(TapEventID eventID, void* data)
{
// Cast data to TapLeaderboardLoadScoresResponse* to read the score list
// Note: data and any memory it references are released after the callback returns. Copy if you need to keep it.
auto resp = static_cast<TapLeaderboardLoadScoresResponse*>(data);
if (resp->error == nullptr) { // Request succeeded
if (resp->leaderboard != nullptr) {
cout << "load callback success: leaderboard=" << resp->leaderboard->name
<< ", score_count=" << resp->score_count
<< ", is_truncated=" << resp->is_truncated << endl;
}
for (uint32_t i = 0; i < resp->score_count; ++i) {
const auto& s = resp->scores[i];
// All string fields (char*) are UTF-8 encoded. Convert as needed.
cout << " rank=" << s.rank
<< ", user=" << (s.user.name ? s.user.name : "null")
<< ", score=" << s.score << endl;
}
// Pagination: when is_truncated is true, copy continuation_token and pass it back to fetch the next page
} else { // Request failed
cout << "load callback failed, code=" << resp->error->code
<< ", message=" << resp->error->message << endl;
}
}
int main()
{
// SDK initialization and launch verification are omitted here — assume they have completed successfully
// 1. Register the load-scores callback (only needs to be done once)
TapSDK_RegisterCallback(TapEventID::LeaderboardLoadScores, loadScoresCallback);
// 2. Build the load-scores request
TapLeaderboardLoadScoresRequest loadRequest;
// Zero-init the struct to avoid wild pointers from uninitialized memory
// For C++11+, you can use TapLeaderboardLoadScoresRequest loadRequest{}; — no memset needed
memset(&loadRequest, 0, sizeof(loadRequest));
// All string fields (char*) must be UTF-8 encoded
loadRequest.leaderboard_id = "your_leaderboard_id"; // Leaderboard ID configured in the developer center
loadRequest.collection = TapLeaderboardCollection_Public; // Public board; pass TapLeaderboardCollection_Friends for friends
loadRequest.continuation_token = nullptr; // NULL for the first page; pass the token from the previous callback for subsequent pages
loadRequest.period_token = nullptr; // Current period; pass a token for a historical period
// 3. Start the async load-scores request
auto ret = TapLeaderboard_AsyncLoadScores(
TapLeaderboard(), // Leaderboard singleton
++gRequestID, // Developer-generated request ID; echoed in the callback
&loadRequest);
if (ret != TapSDK_Result_OK) { // Failed to start the request — no callback will be invoked
cout << "TapLeaderboard_AsyncLoadScores failed, ret=" << ret << endl;
return -1;
}
// Request started successfully — wait for the callback
// 4. Main game loop
bool running = true;
while (running) {
TapSDK_RunCallbacks();
// Your game logic
// ...
this_thread::sleep_for(chrono::milliseconds(100));
}
// 5. Clean up
TapSDK_Shutdown();
return 0;
}
4. Load Current User's Rank
Call TapLeaderboard_AsyncLoadMyScores() to fetch only the current user's score and rank on a leaderboard. The result is returned via the TapEventID::LeaderboardLoadMyScores callback:
#include <atomic>
#include <iostream>
#include <thread>
#include "taptap_api.h"
#include "taptap_leaderboard.h"
using namespace std;
atomic<int64_t> gRequestID;
// Load-my-scores callback
void loadMyScoresCallback(TapEventID eventID, void* data)
{
// Cast data to TapLeaderboardLoadMyScoresResponse* to read the result
// Note: data and any memory it references are released after the callback returns. Copy if you need to keep it.
auto resp = static_cast<TapLeaderboardLoadMyScoresResponse*>(data);
if (resp->error == nullptr) { // Request succeeded
if (resp->score == nullptr) {
cout << "load my scores callback success: user not on leaderboard" << endl;
} else {
const auto& s = *resp->score;
// All string fields (char*) are UTF-8 encoded. Convert as needed.
cout << "load my scores callback success: rank=" << s.rank
<< ", score=" << s.score << endl;
}
} else { // Request failed
cout << "load my scores callback failed, code=" << resp->error->code
<< ", message=" << resp->error->message << endl;
}
}
int main()
{
// SDK initialization and launch verification are omitted here — assume they have completed successfully
// 1. Register the load-my-scores callback (only needs to be done once)
TapSDK_RegisterCallback(TapEventID::LeaderboardLoadMyScores, loadMyScoresCallback);
// 2. Build the load-my-scores request
TapLeaderboardLoadMyScoresRequest myRequest;
memset(&myRequest, 0, sizeof(myRequest));
// All string fields (char*) must be UTF-8 encoded
myRequest.leaderboard_id = "your_leaderboard_id";
myRequest.collection = TapLeaderboardCollection_Public; // Public / Friends
myRequest.period_token = nullptr; // Current period; pass a token for a historical period
// 3. Start the async request
auto ret = TapLeaderboard_AsyncLoadMyScores(
TapLeaderboard(), // Leaderboard singleton
++gRequestID, // Developer-generated request ID; echoed in the callback
&myRequest);
if (ret != TapSDK_Result_OK) {
cout << "TapLeaderboard_AsyncLoadMyScores failed, ret=" << ret << endl;
return -1;
}
// Request started successfully — wait for the callback
// 4. Main game loop
bool running = true;
while (running) {
TapSDK_RunCallbacks();
// Your game logic
// ...
this_thread::sleep_for(chrono::milliseconds(100));
}
// 5. Clean up
TapSDK_Shutdown();
return 0;
}
5. Load Nearby Scores
Call TapLeaderboard_AsyncLoadMyCenteredScores() to fetch nearby scores around the current user — useful for "ranks near me" displays. The result is returned via the TapEventID::LeaderboardLoadMyCenteredScores callback:
#include <atomic>
#include <iostream>
#include <thread>
#include "taptap_api.h"
#include "taptap_leaderboard.h"
using namespace std;
atomic<int64_t> gRequestID;
// Load nearby scores callback
void loadMyCenteredScoresCallback(TapEventID eventID, void* data)
{
// Cast data to TapLeaderboardLoadMyCenteredScoresResponse* to read the score list
// Note: data and any memory it references are released after the callback returns. Copy if you need to keep it.
auto resp = static_cast<TapLeaderboardLoadMyCenteredScoresResponse*>(data);
if (resp->error == nullptr) { // Request succeeded
cout << "centered callback success, score_count=" << resp->score_count << endl;
for (uint32_t i = 0; i < resp->score_count; ++i) {
const auto& s = resp->scores[i];
// All string fields (char*) are UTF-8 encoded. Convert as needed.
cout << " rank=" << s.rank
<< ", user=" << (s.user.name ? s.user.name : "null")
<< ", score=" << s.score << endl;
}
} else { // Request failed
cout << "centered callback failed, code=" << resp->error->code
<< ", message=" << resp->error->message << endl;
}
}
int main()
{
// SDK initialization and launch verification are omitted here — assume they have completed successfully
// 1. Register the load nearby scores callback (only needs to be done once)
TapSDK_RegisterCallback(TapEventID::LeaderboardLoadMyCenteredScores, loadMyCenteredScoresCallback);
// 2. Build the request
TapLeaderboardLoadMyCenteredScoresRequest centeredRequest;
memset(¢eredRequest, 0, sizeof(centeredRequest));
centeredRequest.leaderboard_id = "your_leaderboard_id";
centeredRequest.collection = TapLeaderboardCollection_Public;
centeredRequest.max_results = 21; // Total entries returned (including the current user); 0 means server default
// 3. Start the async request
auto ret = TapLeaderboard_AsyncLoadMyCenteredScores(
TapLeaderboard(),
++gRequestID,
¢eredRequest);
if (ret != TapSDK_Result_OK) {
cout << "TapLeaderboard_AsyncLoadMyCenteredScores failed, ret=" << ret << endl;
return -1;
}
// Request started successfully — wait for the callback
// 4. Main game loop
bool running = true;
while (running) {
TapSDK_RunCallbacks();
// Your game logic
// ...
this_thread::sleep_for(chrono::milliseconds(100));
}
// 5. Clean up
TapSDK_Shutdown();
return 0;
}
6. Open the Leaderboard Page
Call TapLeaderboard_ShowLeaderboards() to open the leaderboard page inside TapTap. This is a synchronous call — no callback is needed:
#include <iostream>
#include "taptap_api.h"
#include "taptap_leaderboard.h"
using namespace std;
int main()
{
// SDK initialization and launch verification are omitted here — assume they have completed successfully
TapLeaderboardShowRequest showRequest;
memset(&showRequest, 0, sizeof(showRequest));
// All string fields (char*) must be UTF-8 encoded
showRequest.leaderboard_id = "your_leaderboard_id";
showRequest.collection = TapLeaderboardCollection_Public; // Default to public collection on open
auto ret = TapLeaderboard_ShowLeaderboards(TapLeaderboard(), &showRequest);
if (ret != TapSDK_Result_OK) { // Failed to open
cout << "TapLeaderboard_ShowLeaderboards failed, ret=" << ret << endl;
return -1;
}
TapSDK_Shutdown();
return 0;
}
Error Handling
- All API calls return
TapSDK_Result; use it for user feedback or retry. See TapSDK_Result.- If the return value is not
TapSDK_Result_OK, the request failed to start and no callback will be invoked.
- If the return value is not
- When
resp->erroris non-null in a callback, the request failed; useTapSDK_ErrorCodefor feedback or retry. See TapSDK_ErrorCode.