Steamworks Documentation
Step by Step: Leaderboards

Introduction

The following is a quick step by step guide to integrating very basic Steam Leaderboards into your application in under 10 minutes. The Steamworks SDK comes with a great example application called Spacewar that shows off the full breadth of Steam features and should be your first stop to see all Steam features in action. This tutorial distills that information found in Spacewar and the leaderboards API found within ISteamUserStats down to just the necessary information required for Steam Leaderboards to keep things as straightforward as possible.

Step 1 - Defining your game's Leaderboards

Leaderboards are application specific and are setup on the Leaderboard Configuration page in the Steamworks Partner Site.

The following fields must be completed to define a leaderboard:
  • Name - Set this to be a name that makes sense as part of internal development.
  • Community Name - If the leaderboard is to be shown on the Community Hub, set the public facing name here. If there is no name entered, the leaderboard will not appear.
  • Sort Method - Set the order for the leaderboard sorting. For positional based leaderboards, use Ascending. For high-score, use Descending.
  • Display Type - Determines the types of data to be displayed with the leaderboard. Select from Numeric, Seconds or Milliseconds.
  • Writes - If this is set to Trusted, the leaderboard scores cannot be set by clients, and can only be set by via SetLeaderboardScore WebAPI. Defaults to false.
  • Reads - If this is set to Friends, the game can only read leaderboard scores for friends of the user, all scores can always be read by WebAPI. Defaults to false.

spacewar_leaderboards

Step 2 - Encapsulating Leaderboard work

The following code is game independent and can be added to your game as you see fit. The class is fully functional as is but can be easily extended to meet any further needs. All of this code was taken directly from the Spacewar example files Leaderboards.cpp/h.

Header File

We define a helper class that will wrap all of the Steam Leaderboard API calls as well as creating all of the Steam call result handlers.
class CSteamLeaderboards { private: SteamLeaderboard_t m_CurrentLeaderboard; // Handle to leaderboard public: int m_nLeaderboardEntries; // How many entries do we have? LeaderboardEntry_t m_leaderboardEntries[10]; // The entries CSteamLeaderboards(); ~CSteamLeaderboards(){}; void FindLeaderboard( const char *pchLeaderboardName ); bool UploadScore( int score ); bool DownloadScores(); void OnFindLeaderboard( LeaderboardFindResult_t *pResult, bool bIOFailure); CCallResult m_callResultFindLeaderboard; void OnUploadScore( LeaderboardScoreUploaded_t *pResult, bool bIOFailure); CCallResult m_callResultUploadScore; void OnDownloadScore( LeaderboardScoresDownloaded_t *pResult, bool bIOFailure); CCallResult m_callResultDownloadScore; };

Code File

Constructor

Parameters - None
Returns - N/A
What it does - This constructor just initializes the member variables.
CSteamLeaderboards::CSteamLeaderboards() : m_CurrentLeaderboard( NULL ), m_nLeaderboardEntries( 0 ) { }

FindLeaderboard()

Parameters - The string identifier of the leaderboard that you want to find (ie. "Feet Traveled")
Returns - Nothing
What it does - This method wraps a call to ISteamUserStats::FindLeaderboard which is an asynchronous call to steam requesting a handle to the given leaderboard. This call needs to be made before you can retrieve or set leaderboard entries. This method also sets up the call return method to use.
void CSteamLeaderboards::FindLeaderboard( const char *pchLeaderboardName ) { m_CurrentLeaderboard = NULL; SteamAPICall_t hSteamAPICall = SteamUserStats()->FindLeaderboard(pchLeaderboardName); m_callResultFindLeaderboard.Set(hSteamAPICall, this, &CSteamLeaderboards::OnFindLeaderboard); }

OnFindLeaderboard()

Parameters - N/A
Returns - Nothing
What it does - This method is a callback that is called anytime we attempt to find a leaderboard on Steam. If the requested leaderboard was found then we set that leaderboard handle as our current leaderboard.
void CSteamLeaderboards::OnFindLeaderboard( LeaderboardFindResult_t *pCallback, bool bIOFailure ) { // see if we encountered an error during the call if ( !pCallback->m_bLeaderboardFound || bIOFailure ) { OutputDebugString( "Leaderboard could not be found\n" ); return; } m_CurrentLeaderboard = pCallback->m_hSteamLeaderboard; }

UploadScore()

Parameters - an int32 representing the value to store in the current leaderboard.
Returns - false if a leaderboard has not been selected yet, otherwise it returns true.
What it does - This method wraps a call to ISteamUserStats::UploadLeaderboardScore which is an asynchronous call to steam that uploads the score of the current user to the currently selected leaderboard. This method also sets up the call return method to use. This call needs to be made after you have selected a leaderboard using FindLeaderboard().
bool CSteamLeaderboards::UploadScore( int score ) { if (!m_CurrentLeaderboard) return false; SteamAPICall_t hSteamAPICall = SteamUserStats()->UploadLeaderboardScore( m_CurrentLeaderboard, k_ELeaderboardUploadScoreMethodKeepBest, score, NULL, 0 ); m_callResultUploadScore.Set(hSteamAPICall, this, &CSteamLeaderboards::OnUploadScore); return true; }

OnUploadScore()

Parameters - N/A
Returns - Nothing
What it does - This method is a callback that is called anytime we attempt to upload a score to a leaderboard on Steam.
void CSteamLeaderboards::OnUploadScore(LeaderboardScoreUploaded_t *pCallback, bool bIOFailure) { if ( !pCallback->m_bSuccess || bIOFailure ) { OutputDebugString( "Score could not be uploaded to Steam\n" ); } }

DownloadScores()

Parameters - N/A
Returns - false if a leaderboard has not been selected yet, otherwise it returns true.
What it does - This method wraps a call to ISteamUserStats::DownloadLeaderboardEntries that is an asynchronous call to steam downloading a set of entries from the currently selected leaderboard. In this case we are downloading ten entries; four before the current user, the current user and five after the current user. This call can be altered to return any number of entries from any place in the leaderboard. This method also sets up the call return method to use. This call needs to be made after you have selected a leaderboard using FindLeaderboard().
bool CSteamLeaderboards::DownloadScores() { if (!m_CurrentLeaderboard) return false; // load the specified leaderboard data around the current user SteamAPICall_t hSteamAPICall = SteamUserStats()->DownloadLeaderboardEntries( m_CurrentLeaderboard, k_ELeaderboardDataRequestGlobalAroundUser, -4, 5); m_callResultDownloadScore.Set(hSteamAPICall, this, &CSteamLeaderboards::OnDownloadScore); return true; }

OnDownloadScore()

Parameters - N/A
Returns - Nothing
What it does - This method is a callback that is called anytime we attempt to download entries from a leaderboard on Steam. If the data was successfully downloaded then we copy the data into our array of entries. The number of entries downloaded is stored in m_nLeaderboardEntries.
void CSteamLeaderboards::OnDownloadScore(LeaderboardScoresDownloaded_t *pCallback, bool bIOFailure) { if (!bIOFailure) { m_nLeaderboardEntries = min(pCallback->m_cEntryCount, 10); for (int index = 0; index < m_nLeaderboardEntries; index++) { SteamUserStats()->GetDownloadedLeaderboardEntry( pCallback->m_hSteamLeaderboardEntries,index,&m_leaderboardEntries[index],NULL,0); } } }

Step 3 - Integrating into your game

The following is a complete listing of code snippets that you would need to integrate into your game in the appropriate locations.

Defines and Globals

The following is the list of includes that are needed to build with Leaderboards and a global pointer to our helper object.
... #include "steam_api.h" #include "SteamLeaderboards.h" // Global access to Leaderboards Object CSteamLeaderboards* g_SteamLeaderboards = NULL; ...

Initialization

The call to SteamAPI_Init initializes all of Steam and must be called before anything else. If that call succeeds then we create the helper object.
... // Initialize Steam bool bRet = SteamAPI_Init(); // Create the SteamLeaderboards object if Steam was successfully initialized if (bRet) { g_SteamLeaderboards = new CSteamLeaderboards(); } ...

Processing Callbacks

To ensure that we process all Steam callbacks we need to regularly pump for new messages. This is achieved by adding this call to the game loop.
... SteamAPI_RunCallbacks(); ...

Shutdown

The call to SteamAPI_Shutdown is probably something you already have in your code. It shuts down steam and must be called before you application exits. Finally we delete the helper object we created.
... // Shutdown Steam SteamAPI_Shutdown(); // Delete the SteamLeaderboards object if (g_SteamLeaderboards) delete g_SteamLeaderboards; ...

Step 4 - Testing and Troubleshooting

This sample code outputs debug information to the debug console that can help you understand what calls are succeeding or failing. The following are some typical failure messages and fixes:

This application has failed to start because steam_api.dll was not found. Re-installing the application may fix this problem.
Make sure steam_api.dll is in the same directory as the executable.

[S_API FAIL] SteamAPI_Init() failed; unable to locate a running instance of Steam, or a local steamclient.dll
You most likely don't have a Steam client running. Start Steam and log in.

[S_API FAIL] SteamAPI_Init() failed; no appID found.
You most likely don't have the steam_appid.txt file in place. Place it in your source folder and ensure that it has your appID number in it.