question

Jimmie Tyrrell avatar image
Jimmie Tyrrell asked

Leaderboard with dates of each score

Hello, I'm migrating my leaderboard implementations from Apple's Game Center Leaderboards and Google's Play Games Services, hoping to consolidate them into one leaderboard under Playfab.

Currently with Game Center's, I can very easily get the player's highest score and the date that that score was set. So my leaderboard looks like this:

Rank   Name     Score          Date
1.     Jimmie   152 seconds    8/1/2020
2.     Luke     140 seconds    6/1/2020
3.     Leia     130 seconds    7/1/2020
4.     Ben      120 seconds    8/15/2020

If Luke were to get a higher score than 140 seconds today, his entry would be updated with his new high score and today's date.

While doing the transition, it wasn't immediately clear how I could store or obtain the date of the player's latest high score. I was expecting to find it on PlayerLeaderboardEntry, or for PlayerLeaderboardEntry to have some CustomData field to go along with the individual statistic, but couldn't find it.

Is there a way to do this in Playfab?

Leaderboards and Statistics
1 comment
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Jimmie Tyrrell avatar image Jimmie Tyrrell commented ·

My current thinking is to update the statistic with a version number that represents the date. Say, days since January 1, 2020"

const ONE_DAY_MS = 24 * 60 * 60 * 1000;

function getDaysSinceEpoch() {
  var epoch = new Date("1/1/2020");
  var today = new Date();
  var diff = today.getTime() - epoch.getTime();
  var days = diff / ONE_DAY_MS;


  return Math.floor(days);
}

// in a handler...

server.UpdatePlayerStatistics({
  PlayFabId: currentPlayerId,
  Statistics: [{
    StatisticName: "Leaderboard",
    Value: score,
    Version: getDaysSinceEpoch()
  }]
});

But it's hard to tell if this is a correct usage of the Version field. Any advice would be appreciated

0 Likes 0 ·
Sarah Zhang avatar image
Sarah Zhang answered

As you guessed, “statistics version” is not suitable for this use. This “statistics version” means the version number of one specified leaderboard, not means the version number of a single-player’s ranking. So you can’t store the date on it. It would cause that players’ ranking cannot be registered to the same leaderboard version.

Currently, the possible workaround to store the date of scores is using the “statistics value”. If using this way to store the date, you need to create a new leaderboard whose “aggregation method” is “Last”. For example, you can create a leaderboard “GameCenterLeaderboardDate” to save the date of “GameCenterLeaderboard”. Due to the “statistics value” can only be number, so you can store the date as the format of pure number timestamps. Then you can use the following request body to call the API GetLeaderboard to get scores in the “GameCenterLeaderboard” and the date in the “GameCenterLeaderboardDate”. Besides, you can add other showable items in the ProfileConstraints to show the player’s profile you need.

Request body (JSON) 
-----------------------------------------------
{
  "StatisticName": "GameCenterLeaderboard",

  "ProfileConstraints":{

    "ShowStatistics": true
  }
}

10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Jimmie Tyrrell avatar image
Jimmie Tyrrell answered

This approach overwrites the value of GameCenterLeaderboardDate whenever I submit to it. I was hoping that it would only store a date if the statistic in GameCenterLeaderboard changed (The date of the new high score).

I can of course do the comparison myself. For example, get the player's last known GameCenterLeaderboard statistic value. If the new score is higher, then set new statistic values for both GameCenterLeaderboard and GameCenterLeaderboardDate.

var currentStats = server.GetPlayerStatistics({
    PlayFabId: currentPlayerId,
    StatisticNames: ["GameCenterLeaderboard", "GameCenterLeaderboardDate"]
  });

  var lastScore = currentStats.Statistics.find(_ => _.StatisticName == "GameCenterLeaderboard");
  lastScore = lastScore || { Value: 0 };
  lastScore.Value = lastScore.Value || 0;

  if (submittedScore < lastScore.Value) return;

  var epoch = new Date("1/1/2020");
  var today = new Date();
  var diff = today.getTime() - epoch.getTime();
  var days = diff / (24 * 60 * 60 * 1000);
  days = Math.floor(days);

  return server.UpdatePlayerStatistics({
    PlayFabId: currentPlayerId,
    Statistics: [{
      StatisticName: "GameCenterLeaderboard",
      Value: submittedScore,
    },{
      StatisticName: "GameCenterLeaderboardDate",
      Value: days,
    }]
  });

Of course I'll clean this up as much as possible. But I have some problems with this approach:

1. Extra API call (GetPlayerStatistics)

2. Increased code complexity. Most leaderboard report APIs are "fire and forget"

3. Defeats the purpose of aggregating "GameCenterLeaderboard" by Highest. I've done that logic here myself.

4. Has two points of data that risk going out of sync (say, someone edits a value in the PlayFab UI and forgets to edit the other),

5. Surprising amount of work to store dates in statistics. Encoding the day since 1/1/2020. If I wanted the time too, I couldn't fit that in an Int32 - so yet another statistic (which exacerbates item 4)

Really all I want to know is the date that a statistic was changed. That's pretty standard for other leaderboard implementations I've seen.

Is there a way I could submit this as a feature request?

1 comment
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Jimmie Tyrrell avatar image Jimmie Tyrrell commented ·

One edit: Int32 is sufficient enough to store seconds since epoch until 2038. I did my math wrong. So the code is simpler and #5 doesn't apply. But this also highlights the pains of date math, which I assumed were handled by leaderboards

0 Likes 0 ·
Jimmie Tyrrell avatar image
Jimmie Tyrrell answered

One other thing - the inline docs in the Unity Playfab library say ShowStatistics is reserved for future development:

As do the online docs:

https://docs.microsoft.com/en-us/rest/api/playfab/server/player-data-management/getleaderboard?view=playfab-rest#playerprofileviewconstraints

Is this safe to use in production?


u7ky1.png (45.0 KiB)
1 comment
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Sarah Zhang avatar image Sarah Zhang commented ·

Yes, Statistics is one of the user's profile. This use is mentioned in the documentation -- Using the profile for advanced leaderboards. It can be used in production.

Currently, PlayFab doesn't support retrieve the date of the statistics' updates. Apologies for any inconvenience. And welcome to post your ideas to our feature request page.

0 Likes 0 ·

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.