The LeaderboardManager object is an API for getting and setting leaderboard scores.
A LeaderboardManager object can be created by calling TurbulenzServices.createLeaderboardManager. None of the LeaderboardManager functions can be used until the TurbulenzServices.createLeaderboardManager leaderboardsReceivedFn callback function has been called.
Required scripts
The LeaderboardManager object requires:
/*{{ javascript("jslib/utilities.js") }}*/
/*{{ javascript("jslib/services/leaderboardmanager.js") }}*/
How leaderboards work
A game can create multiple leaderboards to track and compare a player’s score with other players of the game. The different leaderboards need to be specified in a configuration file which is deployed to the Hub during development. Each specified leaderboard can contain one score for each player that plays the game. When a score is submitted using the API, only the player’s best score is recorded. Scores for each leaderboard must be numerical. There is no upper limit to value that a leaderboard entry records other than the maximum representable numerical value.
In general there are two leaderboard types:
Ranking system
We use Standard competition ranking (http://en.wikipedia.org/wiki/Ranking) for leaderboards. Leaderboards are ordered by highest or lowest score.
The ordering for scores is first by score and then time that the score was achieved (first player to achieve the score is highest).
Security
Security is important to make sure that players cannot cheat by altering the requests and responses that your game makes to our API.
All POSTs to the Leaderboards API are expected to be protected. This means that any POST requests made must be encrypted and signed. When using the LeaderboardManager this is handled automatically.
However, the GET requests to the Leaderboards API are not encrypted or signed. This means that the responses that you get from the getOverview and get requests cannot be trusted and should only be displayed and not used internally. You should not use the results of these requests to find a players score, instead you should track the players score using the UserDataManager.
Limitations
To avoid overloading the service, cumulative leaderboards that increment frequently should not be updated for each increment. For example, if a leaderboard tracks the number of steps taken by the player in game, then you should avoid submitting a score for each step. This could also seriously effect the performance of your game. It is preferable to update these leaderboards on a less frequent interval (e.g. somewhere between every 1-10 minutes) possibly at the same time as saving your game.
Defining your game’s leaderboards
Leaderboards are defined with a leaderboards.yaml file.
Here is an example file:
- key: steps
title: Total steps taken
aggregate: true
sortBy: 1
icon256: img/Most Steps256.png
icon48: img/Most Steps48.png
icon32: img/Most Steps32.png
default-scores:
- user: bob
score: 1000
- user: bill
score: 2000
- user: ben
score: 3000
- email: barry@example.com
score: 4000
- email: brian@example.net
score: 5000
- email: boris@example.org
score: 6000
- email: bruce@example.edu
score: 7000
- key: best-level-time
title: Best level time (seconds)
sortBy: -1
icon256: img/Best level time.png
icon32: img/Best level time32.png
- key: best-dm-kills
title: Highest kills in deathmatch round
sortBy: 1
icon256: img/Best dm kills256.png
The definition file is a dictionary of leaderboard definitions. Each key in the dictionary is a leaderboard key which is used as a reference to the leaderboard for the LeaderboardManager API calls. Keys in this system are restricted to alphanumeric characters separated by either hyphens or dots.
Each leaderboard definition contains:
A list of default scores for selected users. This can be used for testing your leaderboards on Local/Hub and to populate some target scores on the Gamesite. Make sure that these scores can be beaten before deploying the game to the Gamesite. These scores are reset to their defaults every time:
- Leaderboards are reset.
- The game is uploaded to the Hub.
- The game is deployed to the Gamesite.
However, the score can be changed by the user setting a better score after a reset.
Each element of the default scores list is an object with the following properties:
This leaderboards.yaml file should be in the game directory and be added to the deploy files for the game. The order of the keys in the leaderboards.yaml file is the order that the leaderboards will appear on the Gamesite.
Warning
default-scores should only be used for changing scores for accounts owned by you. Please make sure when using user that you own the username used on both the Hub AND the Gamesite.
Manually editing/removing leaderboards
You can find the leaderboards in devserver/localdata/leaderboards/{game-slug}/{leaderboard-key}.yaml. Each file contains a list of the scores in the following example format:
- score: 10.0
time: 1346671479
user: dave
- score: 5.0
time: 1346671526
user: bob
The list is sorted by first best score. The list will be resorted after a leaderboardManager.set request.
To edit the leaderboards stop the local server and then edit this file. To remove all leaderboards for a game stop the local server and remove the devserver/localdata/leaderboards/{game-slug} directory. If the leaderboard file is removed the default user scores will be reinserted after the first leaderboardManager.set request.
Paging and scrolling
Turbulenz provides a simple API for paging or scrolling through leaderboard results. Any leaderboardManager.get request using our API is taken from the live data. This means that the data retrieved can become stale very quickly. For example, any of the users listed in a get request could, at any time, set a new score. This when combined with paging can result in the following:
Because of these issues it is not recommended to:
The pageUp, pageDown, scrollUp and scrollDown functions use a sliding window of results to reduce the number of HTTP requests made while paging/scrolling. To access the full set of results for looking up game profiles use the leaderboardResult.getSlidingWindow function. When a HTTP request is made the leaderboardResult.onSlidingWindowUpdate callback is called. Please read the example for paging leaderboards.
Aggregates, number of users and averages
If the aggregate flag is enabled in the leaderboards.yaml file then aggregates can be collected. To find the aggregates for your leaderboards use the leaderboardManager.getAggregates function. You can also calculate the average score by dividing the aggregate score by the number of users.
Adding meta data to leaderboards
Meta data can also be displayed for each user on the leaderboard by using the GameProfileManager.
Setting
Setting a score on a leaderboard:
var leaderboardsSetCallback = function leaderboardsSetCallbackFn(key,
score,
newBest,
bestScore)
{
var sortBy = leaderboardManager.meta[key].sortBy;
if (newBest)
{
document.write('New best score ' + score);
}
else if (score === bestScore)
{
document.write('Matched your best score ' + bestScore);
}
else
{
document.write('Score not good enough. Need to beat ' + bestScore);
}
}
leaderboardManager.set(key, score, callbackFn, errorCallbackFn);
Setting scores doesn’t make redundant requests:
// Set the score to 10. The leaderboard manager goes and makes a HTTP request to set a new best score.
leaderboardManager.set('apples', 10, callbackFn, errorCallbackFn);
// Set the score to 5. The leaderboard manager knows that this score is not the players best.
// Therefore, it will just call the callbackFn with newBest = false and bestScore = 10.
leaderboardManager.set('apples', 5, callbackFn, errorCallbackFn);
// Set the score to 20. The leaderboard manager knows that the score is 10 and so makes a HTTP request to set a new best score.
leaderboardManager.set('apples', 20, callbackFn, errorCallbackFn);
// Set the score to 15. Yet again, the leaderboard manager knows this is a worse score so avoids making the request.
leaderboardManager.set('apples', 15, callbackFn, errorCallbackFn);
There is no need to check if the players score has improved just set the score and let the leaderboard manager figure it out.
Getting
Displaying a leaderboard:
var displayLeaderboard = function displayLeaderboardFn(key, leaderboardResult)
{
if (leaderboardResult)
{
var view = leaderboardResult.getView();
var leaderboardString = leaderboardManager.meta[key].title + '<br/>';
var ranking = view.ranking;
var numScores = ranking.length;
for (var i = 0; i < numScores; i += 1)
{
var row = ranking[i];
leaderboardString += row.rank + ',';
leaderboardString += row.user.username + ',';
leaderboardString += row.score + '<br/>';
}
document.write(leaderboardString);
}
};
var spec = {
type: 'near',
size: 5,
friendsOnly: true
};
leaderboardManager.get('best-dm-kills', spec, displayLeaderboard);
Paging/scrolling
Paging or scrolling through leaderboards:
var currentLeaderboardResult;
function displayLeaderboardUI(key, leaderboardResult)
{
currentLeaderboardResult = leaderboardResult;
var view = leaderboardResult.getView();
if (!view.top)
{
displayUpButton();
}
displayLeaderboard(key, leaderboardResult);
if (!view.bottom)
{
displayDownButton();
}
}
function onPageUpClicked()
{
currentLeaderboardResult.pageUp(displayLeaderboardUI);
}
function onPageDownClicked()
{
currentLeaderboardResult.pageDown(displayLeaderboardUI);
}
function onScrollUpClicked()
{
currentLeaderboardResult.scrollUp(displayLeaderboardUI);
}
function onScrollDownClicked()
{
currentLeaderboardResult.scrollDown(displayLeaderboardUI);
}
var spec = {
type: 'near',
size: 20,
friendsOnly: true
};
leaderboardManager.get('best-dm-kills', spec, displayLeaderboard);
Summary
Get an overview of all leaderboards scores.
Syntax
var spec = {
friendsOnly: true
};
function callbackFn(overview) {}
leaderboardManager.getOverview(spec, callbackFn, errorCallbackFn);
errorCallbackFn (Optional)
Note
Currently the friendsOnly option is only supported on the Gamesite. It is ignored on Local and Hub.
The callback callbackFn is called with an overview array with the following format:
[
{
key: "best-level-time",
score: 190.3242,
rank: 12
},
{
key: "steps",
score: 739.0,
rank:8
},
{
key: "best-dm-kills",
score: 47.0,
rank: 1
}
]
The leaderboards order is the same order they are specified in the leaderboards.yaml file. If the player has not yet set a score for a leaderboard then the leaderboard will not be included in these results.
Summary
Get the aggregates for the leaderboards.
Syntax
var spec = {};
function callbackFn(aggregates) {}
leaderboardManager.getAggregates(spec, callbackFn, errorCallbackFn);
errorCallbackFn (Optional)
The callback callbackFn is called with an aggregates array with the following format:
[
{
key: "best-level-time",
aggregateScore: 97423847.3432,
numUsers: 511883,
}
]
The leaderboards order is the same order they are specified in the leaderboards.yaml file. Only leaderboards with the aggregate flag enabled will be listed in the results.
Summary
Get a group of scores for a leaderboard.
Syntax
var spec = {
type: 'top',
size: 5,
friendsOnly: true
};
function callbackFn(key, leaderboardResult) {
var view = leaderboardResult.getView();
}
leaderboardManager.get(key, spec, callbackFn, errorCallbackFn);
errorCallbackFn (Optional)
The callback callbackFn is called with the following properties:
To extract the view results of a LeaderboardResult object call the leaderboardResult.getView function.
Warning
You can request a size up to 32. If your request size is more than 32 then the request is ignored. This is to limit the load on our servers.
Note
Currently the friendsOnly option is only supported on the Gamesite. It is ignored on Local and Hub.
Summary
Set the players score on a leaderboard.
Note
This is an encrypted API call
Syntax
function callbackFn(key, score, bestScore) {}
leaderboardManager.set(key, score, callbackFn, errorCallbackFn);
errorCallbackFn (Optional)
The callbackFn is called with the following arguments:
Players scores can only get better. If you try to set a worse score then the players leaderboard score will not change. The LeaderboardManager will not make a request if it knows the score from a previous request is better.
Note
There is no need to check if a players score is better than their previous score. The LeaderboardManager tracks previous scores and will not make redundant HTTP requests.
Summary
The ServiceRequester object for the leaderboards service.
Syntax
var serviceRequester = leaderboardManager.service;
Summary
The meta information for the leaderboards.
Syntax
var meta = leaderboardManager.meta;
meta is a JavaScript object of the following format:
{
steps:
{
title: "Total steps taken",
sortBy: 1
},
best-level-time:
{
title: "Best time to clear level",
sortBy: -1
},
best-dm-kills:
{
title: "Highest kills in deathmatch round",
sortBy: 1
}
}
This contains some of the information from the leaderboards.yaml definition file.
The meta object is a dictionary of leaderboard keys each with an object property with:
Note
This property is read only.
A LeaderboardResult object is given as an argument to the callback for a LeaderboardManager.get request. It contains all of the information required to render a leaderboard UI.
Internally, the LeaderboardResult object contains the results of a single LeaderboardManager.get request. It manages a sliding window over a large set of results to seamlessly reduce the number of HTTP requests that the manager has to make.
Summary
Move the view up one page.
Syntax
function callbackFn(key, leaderboardResult) {}
var ok = leaderboardResult.pageUp(callbackFn, errorCallbackFn);
// equivalent to
var ok = leaderboardResult.moveUp(leaderboardResult.spec.size, callbackFn, errorCallbackFn);
For more information see leaderboardResult.moveUp.
Summary
Move the view down one page.
Syntax
function callbackFn(key, leaderboardResult) {}
var ok = leaderboardResult.pageDown(callbackFn, errorCallbackFn);
// equivalent to
var ok = leaderboardResult.moveDown(leaderboardResult.spec.size, callbackFn, errorCallbackFn);
For more information see leaderboardResult.moveDown.
Summary
Scroll the view up one score.
Syntax
function callbackFn() {}
var ok = leaderboardResult.scrollUp(callbackFn, errorCallbackFn);
// equivalent to
var ok = leaderboardResult.moveUp(1, callbackFn, errorCallbackFn);
For more information see leaderboardResult.moveUp.
Summary
Scroll the view down one score.
Syntax
function callbackFn() {}
var ok = leaderboardResult.scrollDown(callbackFn, errorCallbackFn);
// equivalent to
var ok = leaderboardResult.moveDown(1, callbackFn, errorCallbackFn);
For more information see leaderboardResult.moveDown.
Summary
Scroll the view up by offset scores.
Syntax
function callbackFn() {}
var ok = leaderboardResult.moveUp(offset, callbackFn, errorCallbackFn);
A JavaScript function. Called asynchronously once the leaderboard results have been retrieved and the view has been updated. Called with arguments:
errorCallbackFn (Optional)
Returns a boolean ok value which is true if the operation will be carried out. This is false if another view operation is already in progress.
To get the results after moving up down call the leaderboardResult.getView function.
Please read the leaderboard paging guide before using this function. For an example of how to use these functions see here.
Note
View operations cannot be called synchronously, as they might have to do an asynchronous HTTP request:
function moveDownCompleted()
{
// This call will work
leaderboardResult.moveUp(1);
}
leaderboardResult.moveDown(1, moveDownCompleted);
// This call will be ignored
leaderboardResult.moveUp(1);
Summary
Scroll the view down by offset scores.
Syntax
function callbackFn(key, leaderboardResult) {}
var ok = leaderboardResult.moveDown(offset, callbackFn, errorCallbackFn);
A JavaScript function. Called asynchronously once the leaderboard results have been retrieved and the view has been updated. Called with arguments:
errorCallbackFn (Optional)
Returns a boolean ok value which is true if the operation will be carried out. This is false if another view operation is already in progress.
To get the results after moving down down call the leaderboardResult.getView function.
Please read the leaderboard paging guide before using this function. For an example of how to use these functions see here.
Note
View operations cannot be called synchronously, as they might have to do an asynchronous HTTP request:
function moveUpCompleted()
{
// This call will work
leaderboardResult.moveDown(1);
}
leaderboardResult.moveUp(1, moveUpCompleted);
// This call will be ignored
leaderboardResult.moveDown(1);
Summary
Returns an ordered array of ranks, users and scores (best to worst) in the current results view.
Syntax
var view = LeaderboardResult.getView();
// example usage:
var ranking = view.ranking;
var bestInResult = ranking[0];
var worstInResult = ranking[ranking.length - 1];
var bestScore = bestInResult.score;
var bestScoreUsername = bestInResult.user.username;
var bestScoreRank = bestInResult.rank;
var bestScoreTime = bestInResult.time;
Returns a view object with the following properties:
An ordered (by rank and time) JavaScript array. Each element is an object, representing a user’s score, with the following properties:
A JavaScript object with the username, display name and avatar of the user. For example:
{
username: "dave",
displayname: "dave"
avatar: "https://..."
}
The view can be adjusted using the pageUp , pageDown , scrollUp , scrollDown , moveUp and moveDown functions above.
The result of this function is automatically cached so it can be called inside of a rendering loop.
Please read the leaderboard paging guide before using this function. For an example of how to use these functions see here.
Summary
Returns an ordered array of ranks, users and scores (best to worst). This is a superset of the results of getView. It is typically a set of 64 leaderboard scores (although can be smaller) centered around the view returned by getView.
Syntax
var slidingWindow = LeaderboardResult.getSlidingWindow();
Returns a slidingWindow object with the following properties:
An ordered (by rank and time) JavaScript array. Each element is an object, representing a user’s score, with the following properties:
A JavaScript object with the username, display name and avatar of the user. For example:
{
username: "dave",
displayname: "dave"
avatar: "https://..."
}
This can be combined with the leaderboardResult.onSlidingWindowUpdate() function to efficiently create avatar images or collect game profiles for users in the leaderboard.
Summary
The meta key used to generate the leaderboard result.
Syntax
var key = leaderboardResult.key;
Summary
Called for each HTTP request the LeaderboardResult object makes. This is useful for making game profile requests efficiently.
Syntax
leaderboardResult.onSlidingWindowUpdate = function onSlidingWindowUpdate() {}
This can be combined with the leaderboardResult.getSlidingWindow() function to efficiently create avatar images or collect game profiles for users in the leaderboard.
Summary
The spec object used by the LeaderboardManager.get to create this LeaderboardResult object.
Syntax
var spec = leaderboardResult.originalSpec;
var specSize = spec.size;
var specType = spec.type;
var friendsOnly = spec.friendsOnly;
This originalSpec object will have the default values populated if they were missing in the original LeaderboardManager.get request. The defaults are:
If no error callback is given then the TurbulenzServices.createLeaderboardManager errorCallbackFn is used.
Summary
A JavaScript function. Returns an error message and its HTTP status.
Syntax
function errorCallbackFn(errorMsg, httpStatus, calledByFn, calledByParams) {}