Leaderboard Updates – The Original Social Feature from GameSparks

What’s New with GameSparks Leaderboards?

Leaderboards are often seen as the backbone of a game. The display of competition drives people to continue playing, craving to reach the elusive ‘Rank 1’ position and tower above the rest. This is why Leaderboard data is, and always will be, the most accessed and most updated of our features.

When creating a Leaderboard, developers look out for three top quality attributes; performance, scalability and flexibility. Myself and other developers here at GameSparks have recently finished some work on the latter and so this article has been written to give you an update of what has been going on behind the scenes of the GameSparks platform.

Supplemental Information for a Leaderboard Entry

Up until recently, every attribute within GameSparks Leaderboards contributed towards the ranking of a player. This was changed by adding the calculation type ‘Supplemental’.

A supplemental attribute stores some extra information about a leaderboard entry without having any influence upon the final position of a player’s rank. A leaderboard will only be updated if a different score is achieved, if a player achieves the same score with different supplemental information, the Leaderboard will not refresh.

As an example, let’s assume that you have a racing game and you want to log the track conditions as well as the time, but you don’t want these to influence the Leaderboard position of the user. With the addition of Supplemental attributes, you can now store added information such as the weather.

If your player submits the score:

{time: 100, weather:”sunny”}

Then:

{time: 100, weather:”wet”}

The final entry in the leaderboard will be {time: 100, weather:”sunny”}. This is because the player’s time has not altered and so the game’s Leaderboard has not refreshed.

 

No More ‘One Entry Per Player’

Since opening our doors in 2013, GameSparks has always only ever allowed a user to have one entry in a Leaderboard, and in most cases, this is all you would ever need. However, there may be an occasion you would like to allow your players to be present twice in the same Leaderboard based on different attributes.

For instance, in your racing game, you may want to allow players to log a different track time with different vehicles. As an example, your player may have managed to reach position 45 by driving a sports car, but was only able to achieve position 87 whilst driving a monster truck.

As a team, we have worked to make this possible by allowing you to define custom ID components. Previously, a Leaderboard used to have a standard system-defined ID, which was structured by defining the type of Leaderboard.

The new anatomy of a Leaderboard entry ID is presented in the next table:

TypeFieldsExampleWith two custom IDs
Player leaderboard– userId“_id”: “56952934e4b0a5ca4424d577”“_id”: {“userId”: “56952934e4b0a5ca4424d577″,”carType”: “Ferrari”,”pilot”: “Vettel”}
Team leaderboard– teamId“_id”: “5695295919f98f4aa26f2968”“_id”: {“teamId”: “5695295919f98f4aa26f2968″,”carType”: “Ferrari”,”pilot “: “Vettel”}
Challengeleaderboard– userId/teamId- challengeInstanceId“_id”: {“userId”: “56952934e4b0a5ca4424d577″,”challengeInstanceId”: “5695294ae4b0a5ca4424f946”}“_id”: {“userId”: “56952934e4b0a5ca4424d577″,”challengeInstanceId”: “5695294ae4b0a5ca4424f946″”carType”: “Ferrari”,”pilot “: “Vettel”}
Partitioned leaderboard– playerId/teamId- key: a string representation of the partition values“_id”: {“userId”: “5695293ae4b0a5ca4424daf9″,”key”: “country.UK.level.1”}“_id”: {“userId”: “5695293ae4b0a5ca4424daf9″,”key”: “country.UK.level.1″ ,”carType”: “Ferrari”,”pilot “: “Vettel”}

 

This updated structure has provided developers with even further flexibility. For this new functionality to work correctly, some instructions must be followed:

  • You should not use userId, teamId, key and challengeInstanceId as custom IDs
  • Only event attributes with the default calculation ‘Grouped’ can be custom IDs
  • You should pay special attention when changing your custom ID’s, this is because a mongo ID cannot be changed after creation.
  • If you do change a custom ID and you have any data in your leaderboards, it will be deleted once you publish your game. This is because inconsistencies could be created. However, you can now rebuild Leaderboards from the running totals.
  • Again, because the mongo ID cannot be changed, if you use a custom ID in your Leaderboard, all the Leaderboard fields need to come from the same running total.

 

Request and Cloud Code Changes

For the sake of the following examples, let’s assume that we have the subsequent configuration: A Leaderboard called ‘RACE’ that has a ‘time’ attribute ordered ascending and the ‘carType’ and ‘pilot’ custom IDs:

alex blog

 

Let’s assume that the Leaderboard order is:

 

alex blog 2

 

RequestChangeExample
AroundMeLeaderboardRequestAdded the ability to get the ‘around me’ list of any of the user’s entries in the Leaderboard.You can now specify a custom ID filter that will be used to identify your player’s entry for which we perform the ‘around me’ search.The filter can be full or partial. If more than one entries match it, the Leaderboard entries around the highest score for the given constraints will be returned.Please note that the ‘customIdFilter’ parameter does not act as a filter on the entire result set, it only serves to identify your player’s entry in the Leaderboard.{“@class”: “.AroundMeLeaderboardRequest”,”customIdFilter”: {“carType”:”Mercedes”},”entryCount”: 1,”leaderboardShortCode”: “RACE”}

 

{

“@class”: “.AroundMeLeaderboardResponse”,

“data”: [

{

“userId”: “55537c82e4b05b415dc12dc1”,

“time”: 85,

“carType”: “Mercedes”,

“pilot”: “Hamilton”,

“when”: “2016-02-01T11:58Z”,

“rank”: 2,

“city”: “York”,

“country”: “GB”,

“userName”: “PLAYER 2”,

“externalIds”: {}

},

{

“userId”: “55537c7ae4b05b415dc12d69”,

“time”: 89,

“carType”: “Ferrari”,

“pilot”: “Vettel”,

“when”: “2016-02-01T11:41Z”,

“rank”: 3,

“city”: “York”,

“country”: “GB”,

“userName”: “PLAYER 1”,

“externalIds”: {}

},

{

“userId”: “55537c7ae4b05b415dc12d69”,

“time”: 90,

“carType”: “Mercedes”,

“pilot”: “Rosberg”,

“when”: “2016-02-01T11:42Z”,

“rank”: 4,

“city”: “York”,

“country”: “GB”,

“userName”: “PLAYER 1”,

“externalIds”: {}

}

],

“leaderboardShortCode”: “RACE”,

“scriptData”: null,

“social”: false

}

LeaderboardsEntriesRequestAdded possibility to find all of the players’ entries in a Leaderboard.{“@class”: “.LeaderboardsEntriesRequest”,”leaderboards”: [“RACE”],”player”: “55537c82e4b05b415dc12dc1”

}

 

{

“@class”: “.LeaderboardsEntriesResponse”,

“scriptData”: null,

“RACE”: [

{

“userId”: “55537c82e4b05b415dc12dc1”,

“time”: 80,

“carType”: “Ferrari”,

“pilot”: “Vettel”,

“when”: “2016-02-01T11:58Z”,

“city”: “York”,

“country”: “GB”,

“userName”: “PLAYER 2”,

“externalIds”: {},

“rank”: 1

},

{

“userId”: “55537c82e4b05b415dc12dc1”,

“time”: 85,

“carType”: “Mercedes”,

“pilot”: “Hamilton”,

“when”: “2016-02-01T11:58Z”,

“city”: “York”,

“country”: “GB”,

“userName”: “PLAYER 2”,

“externalIds”: {},

“rank”: 2

},

{

“userId”: “55537c82e4b05b415dc12dc1”,

“time”: 90,

“carType”: “Mercedes”,

“pilot”: “Rosberg”,

“when”: “2016-02-01T12:01Z”,

“city”: “York”,

“country”: “GB”,

“userName”: “PLAYER 2”,

“externalIds”: {},

“rank”: 5

}

]

}

GetLeaderboardEntriesRequestThe call for GetLeaderboardEntriesRequest will now return the highest player entry in each of the Leaderboards passed in.{“@class”: “.GetLeaderboardEntriesRequest”,”leaderboards”: [“RACE”],”player”: “55537c82e4b05b415dc12dc1”

}

 

{

“@class”: “.GetLeaderboardEntriesResponse”,

“scriptData”: null,

“RACE”: {

“userId”: “55537c82e4b05b415dc12dc1”,

“time”: 80,

“carType”: “Ferrari”,

“pilot”: “Vettel”,

“when”: “2016-02-01T11:58Z”,

“city”: “York”,

“country”: “GB”,

“userName”: “PLAYER 2”,

“externalIds”: {},

“rank”: 1

}

}

 

We added the following cloud code methods:

SparkLeaderboard.getEntryCountForIdentifier(identifier)Returns the total number of entries in this leaderboard for the specified identifier. The latter can be the userId of a player or the ID of a team.
SparkLeaderboard. getEntriesFromPlayerForCustomId(playerId, count, customIdFilter)Returns a cursor over the leaderboard entries starting from the highest score of the supplied playerId and customIdFilter. If the customId filter is not an object with valid ID fields, it will return an empty cursor
SparkLeaderboard.getEntriesForItentifier(identifier, customIdFilter)Returns the array of leaderboard entries that correspond to the supplied identifier and customIdFilterIf the customIdFilter is null, the method returns all the entries in the leaderboard for the suplied identifier
SparkLeaderboard.getIdFields()Returns the list of custom ID fields that are defined on the leaderboard
SparkLeaderboard. deleteEntriesForCustomId(identifier, deleteRunningTotals, customIdFilterDeletes the entries from the leaderboard that match the specified customIdFilterThis method only works for realtime leaderboards.

The cloud code method deleteEntry(identifier, deleteRunningTotals) was deprecated. Instead, either the method deleteAllEntries or deleteEntriesForCustomId should be used

For more details about the cloud code methods, head over to our documentation pages.

 

When Do I Use a Partition and When Do I Use a Custom ID?

When you use a partition you are essentially creating a number of individual Leaderboards corresponding to the number of partition values you define. So, you should use partitions if you need several instances of the same Leaderboard configuration based on some discriminator factors: the PARTITION attributes.

Let’s say that you have a football championship with two leagues: juniors and seniors. A user can only have one team in each of these leagues. This can be translated into a Leaderboard that is partitioned by seniority, as you will never want to compare the performance of a junior team in relation to a senior team.

On the other hand, if you want a [[goal getter]] Leaderboard, you could have a ‘playerName’ custom ID. This allows your user to be present multiple times in a leaderboard.

Going further, if you don’t want your junior goal getters to get mixed up with your seniors, you can partition your second Leaderboard by seniority.

 

Rebuilding Leaderboards from Running Totals

In addition to the other upgrades to Leaderboards, we have also added the possibility to rebuild them from the data already existing in your running totals. This should be used in rare cases where you would like to change the composition of your Leaderboard, or when you want to create a new Leaderboard populated with already-existent data.

This functionality is accessible in cloud code by using the method rebuildLeaderboard(awardAchievements). You can find more documentation on this topic here.

The internal process for rebuilding the Leaderboard is:

  1. Drop Leaderboard
  2. Compute new ranks
  3. Start reinserting the data beginning with the users with the highest ranks in batches of 1000 entries. Award achievements if needed.

This operation needs to be used with care because whilst the Leaderboard is rebuilt, it will be in an inconsistent state. This means that if a player submits a score before all of the other entries are inserted back, it’s possible that that player might receive a high score message and even awards.

This is an admin operation and should not be used very frequently. You need to define a strategy of updating your Leaderboards. One option is to create a screen in ‘Manage’ that allows you to rebuild any Leaderboard. Another option would be to define a ‘Game Published’ cloud code that keeps track of whether it needs to rebuild the leaderboard when the game is published.

 

 

Who uses GameSparks?