26. Contents support part 1: Leaderboards (rankings)

The leaderboard service includes the concepts of competing groups and competing periods.

  • Competing groups

    • Social: friends lists, guilds, etc.

    • All players

  • Competing periods of time

    • Daily

    • Weekly

    • All-time

These two dimensions can be combined for several types of ranking within a single game.

Examples: combining competing groups and competing periods
  • Weekly high score rankings between friends

  • All-time high score rankings among all players

  • Daily score rankings between guilds

  • Daily activity score rankings between guilds

26.1. iFun Leaderboard

iFun Engine uses a program called iFun Leaderboard as an agent to handle leaderboards. The game server sends requests for updated scores or ranking searches to iFun Leaderboard, and iFun Leaderboard performs the actual work.

Note

iFun Engine’s use of a separate leaderboard agent doesn’t affect game servers and is to ensure that new transactions can be added or changed.

With the use of an agent, it is easy to use ranking services through the agent’s REST API on the game server and other servers as well.

26.1.1. How to install

Tip

If MANIFEST.json Configuration’s use_leaderboard is set to false, the game server runs in test mode and handles all leaderboard requests as dummies. This setting is used during the development phase, and there is no need to install iFun Leaderboard in these instances.

Note

The leaderboard uses Redis to handle caching and MySQL Server to save rankings. Redis version 2.8.4 or higher and MySQL Server version 5.5 or higher are required.

26.1.1.1. For Ubuntu

$ sudo apt-get update
$ sudo apt-get install funapi-leaderboard1 redis-server mysql-server

26.1.1.2. For CentOS

$ sudo yum install funapi-leaderboard1 redis mysql-server
$ sudo systemctl enable funapi-leaderboard

26.1.2. How to execute

26.1.2.1. For Ubuntu 14.04 or CentOS 6

Open the file /etc/default/funapi-leaderboard and modify enabled=1.

Run the following command:

$ sudo service funapi-leaderboard start

26.1.2.2. For Ubuntu 16.04 or CentOS 7

Run the following command:

$ sudo systemctl enable funapi-leaderboard
$ sudo systemctl start funapi-leaderboard

26.1.3. Checking operation

26.1.3.1. For Ubuntu 14.04 or CentOS 6

$ sudo service funapi-leaderboard status

26.1.3.2. For Ubuntu 16.04 or CentOS 7

$ sudo systemctl status funapi-leaderboard

26.1.3.3. Log file

Logs are output to /var/log/funapi/funapi-leaderboard/.

26.1.4. Setting up iFun Leaderboard (MANIFEST.json)

Note

This is to configure iFun Leaderboard itself. Settings for game servers using iFun Leaderboard are in MANIFEST.json Configuration.

iFun Leaderboard was also written with iFun Engine. Therefore, you can naturally change settings in MANIFEST.json. iFun Leaderboard’s MANIFEST.json is in /usr/share/funapi-leaderboard/default/manifests/MANIFEST.json.

Configure as follows.

Network settings

  • server_tcp_port: Sets the TCP port number for the iFun Engine game server to communicate with iFun Leaderboard. (type=uint16, default=12820)

MySQL settings to save rankings

  • mysql_server_url: Enter the IP address and port of the DB server where ranking data is saved. (type=string, default=”tcp://127.0.0.1:3306”)

  • mysql_id: Enter the user ID of the DB where ranking data will be saved. (type=string, default=”funapileaderboard1”)

  • mysql_pw: Enter the user password for the DB where ranking data will be saved. (type=string, default=”qlffj1!!”)

  • mysql_db_name: Enter the name of the DB where ranking data will be saved. (type=string, default=”funapi_leaderboard1”)

  • mysql_db_connection_count: Connection pool size used for iFun Leaderboard to communicate with the MySQL server. (type=uint16, default=1)

  • mysql_db_character_set: 랭킹 정보가 저장되는 DB 에 적용할 character set 을 입력합니다. (type=string, default=”utf8”)

  • mysql_local_account_column_length: 랭킹 정보가 저장되는 DB 테이블의 local_account 컬럼의 길이를 입력합니다. (type=uint64, default=50)

  • leaderboard_use_db_stored_procedure: Mysql stored procedure 사용 여부를 입력합니다. (type=bool, default=true)

  • leaderboard_use_db_auto_schema_generation: 리더보드 서버를 실행할 때 DB 에 스키마를 자동으로 생성할 지 여부를 입력합니다. (type=bool, default=true)

  • export_db_schema_to_file: 지정된 경로에 DB 스키마 생성 스크립트를 저장하고 종료합니다. 단, leaderboard_use_db_auto_schema_generation 은 false 로 입력해야 합니다. (type=string, default=””)

Redis settings to save rankings

iFun Leaderboard uses Redis to cache rankings. iFun Engine’s Redis session is used as is for Redis settings. See MANIFEST.json Configuration to enter Redis data.

Important

Since existing Redis settings are reused, Redis is included under dependencies, unlike other settings.

Leaderboard settings

  • reset_schedules: You can change the ranking reset schedule. This is a JSON object array type and is written in the format below.

    "reset_schedules": [
      {
        "leaderboard_id": String,
        "period": String
        "interval": Integer,
        "starts": String,
        "ends": String
      },
      ...
    ]
    
    • leaderboard_id: Enter the name to identify rankings.

    • period: Enter the period to reset rankings. "day" and "week" are currently available.

    • interval: Enter the reset interval. This follows the unit defined in “Period”. For example, if the period is days and the interval is 1, rankings are reset daily. If the period is weeks and the interval is 2, rankings are reset every 2 weeks.

    • starts: Enter the date and time to start periodic ranking resets in the format “2015-04-30 13:00:00”.

    • ends: Enter the date and time to end periodic ranking resets in the format “2020-12-31 23:59:59”.

Tip

When the agent is updated, the existing MANIFEST.json is overwritten. To prevent this, you can use an override file as described in Temporarily overriding MANIFEST.json.

/etc/funapi-leaderboard/MANIFEST.override.json:

{
  "override": {
    "FunapiLeaderboardServer": {
      "mysql_server_url": "tcp://10.10.10.10:36060",
      "mysql_id": "leaderboard",
      "mysql_pw": "leaderboard",
      ...
    },
    "Redis": {
      "enable_redis": true,
      "redis_mode": "redis",
      "redis_servers": {
        "": {
          "address": "127.0.0.1:6379",
          "auth_pass": ""
        }
      },
      ...
    }
    ...
  }
}

26.1.5. Ranking reset schedule settings

iFun Leaderboard Agent resets rankings at particular intervals as follows:

Period

Settings

Day:

Rankings are reset at a particular time each day.

Week:

Rankings are reset at a particular time and day each week.

You can change the ranking reset schedule using reset_schedules in Setting up iFun Leaderboard (MANIFEST.json). The ranking reset schedule is set by the leaderboard agent itself based on reset_schedules in MANIFEST and operates as follows.

  • If only a daily ranking reset schedule is set, rankings are only reset daily.

  • If only a weekly ranking reset schedule is set, rankings are only reset weekly.

  • If both daily and weekly ranking schedules are set, both operate.

26.1.5.1. Registering reset schedules

Example:

Assuming there is a player_game_score with a leaderboard ID that saves player rankings and a guild_game_score with a leaderboard ID that saves guild rankings, we will register 3 reset schedules: daily user rankings, weekly user rankings, and biweekly guild rankings.

Some data from iFun Leaderboard’s MANIFEST.json file follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
  ...
  "arguments": {
    ...
    "reset_schedules": [
      {
        "leaderboard_id": "player_game_score",
        "period": "day",
        "interval": 1,
        "starts": "2015-01-01 00:00:00",
        "ends": "2020-12-31 23:59:59"
      },
      {
        "leaderboard_id": "player_game_score",
        "period": "week",
        "interval": 1,
        "starts": "2015-01-01 00:00:00",
        "ends": "2020-12-31 23:59:59"
      },
      {
        "leaderboard_id": "guild_game",
        "period": "week",
        "interval": 2,
        "starts": "2015-05-01 00:00:00",
        "ends": "2020-12-31 23:59:59"
      }
    ]
  }
}

Note

Since January 1, 2015 is a Thursday, the second reset schedule resets rankings on Thursday of every week. Likewise, since May 1, 2015 is a Friday, the third reset schedule resets rankings on Friday of every week.

26.1.5.2. Modifying reset schedules

Like registering a reset schedule, you also modify the reset schedule in MANIFEST.json’s reset_schedules. Thus, you only need to change items to be modified in reset_schedules.

26.1.6. (Advanced) Manually deleting/restoring ranking data

26.1.6.1. Data iFun Leaderboard saves in MySQL

Tables are created in the following pattern in MySQL to save ranking data.

{leaderboard_id}_{timespan}

For example, if the leaderboard_id is set as player_game and scores are updated, the following SQL table is output:

  • player_game_alltime

  • player_game_daily

  • player_game_weekly

  • player_game_lastweek

  • player_game_yesterday

Note

lastweek and yesterday are created when the reset schedule resets rankings.

26.1.6.2. Data iFun Leaderboard saves in Redis

Keys are set in the following pattern in Redis to save ranking data.

leaderboard:{leaderboard_id}:{timespan}
leaderboard:{leaderboard_id}:{timespan}:dense
leaderboard:{leaderboard_id}:{timespan}:timestamp
leaderboard:{leaderboard_id}:{player_id}:friends

Note

leaderboard:{leaderboard_id}:{player_id}:friends is created to handle friend rankings and is deleted after it has been handled.

For example, if the leaderboard_id is set as player_game and scores are updated, the following Redis keys are output:

Default keys:

  • leaderboard:player_game:alltime

  • leaderboard:player_game:daily

  • leaderboard:player_game:weekly

  • leaderboard:player_game:lastweek

  • leaderboard:player_game:yesterday

dense keys:

  • leaderboard:player_game:alltime:dense

  • leaderboard:player_game:daily:dense

  • leaderboard:player_game:weekly:dense

  • leaderboard:player_game:lastweek:dense

  • leaderboard:player_game:yesterday:dense

timestamp keys:

  • leaderboard:player_game:alltime:timestamp

  • leaderboard:player_game:daily:timestamp

  • leaderboard:player_game:weekly:timestamp

  • leaderboard:player_game:lastweek:timestamp

  • leaderboard:player_game:yesterday:timestamp

Note

lastweek and yesterday are created when the reset schedule resets rankings.

26.1.6.3. Deleting ranking data

To delete ranking data, you must delete all relevant data in MySQL and Redis.

26.1.6.3.1. Deleting ranking data in MySQL

truncate table or drop table commands are used to delete tables covered in Data iFun Leaderboard saves in MySQL.

For example, if the leaderboard_id is player_game, deletion is as follows.

mysql> truncate table player_game_alltime
mysql> truncate table player_game_daily
mysql> truncate table player_game_weekly
mysql> truncate table player_game_lastweek
mysql> truncate table player_game_yesterday

또는

mysql> drop table player_game_alltime
mysql> drop table player_game_daily
mysql> drop table player_game_weekly
mysql> drop table player_game_lastweek
mysql> drop table player_game_yesterday
26.1.6.3.2. Deleting ranking data in Redis

The del command is used to delete keys covered in Data iFun Leaderboard saves in Redis.

For example, if the leaderboard_id is player_game, deletion is as follows.

127.0.0.1:6379> del leaderboard:player_game:alltime
127.0.0.1:6379> del leaderboard:player_game:daily
127.0.0.1:6379> del leaderboard:player_game:weekly
127.0.0.1:6379> del leaderboard:player_game:lastweek
127.0.0.1:6379> del leaderboard:player_game:yesterday

127.0.0.1:6379> del leaderboard:player_game:alltime:timestamp
127.0.0.1:6379> del leaderboard:player_game:daily:timestamp
127.0.0.1:6379> del leaderboard:player_game:weekly:timestamp
127.0.0.1:6379> del leaderboard:player_game:lastweek:timestamp
127.0.0.1:6379> del leaderboard:player_game:yesterday:timestamp

127.0.0.1:6379> del leaderboard:player_game:alltime:dense
127.0.0.1:6379> del leaderboard:player_game:daily:dense
127.0.0.1:6379> del leaderboard:player_game:weekly:dense
127.0.0.1:6379> del leaderboard:player_game:lastweek:dense
127.0.0.1:6379> del leaderboard:player_game:yesterday:dense

Tip

If you have a Redis server you are only using for leaderboards, you can use the Redis command flushall to more easily delete data.

127.0.0.1:6379> flushall

26.1.6.4. Restoring ranking data through Redis in MySQL

If data was lost from a Redis server, run iFun Leaderboard Agent in recovery mode to restore ranking data saved in MySQL using Redis.

Tip

Recovery mode simply copies data from MySQL using Redis and doesn’t delete existing data from Redis.

Run the leaderboard agent’s recovery mode by inputting the following:

In Ubuntu 14.04 or CentOS 6

$ sudo service funapi-leaderboard stop
$ funapi-leaderboard-launcher --alsologtostderr --recover_leaderboard
$ sudo service funapi-leaderboard start

In Ubuntu 16.04 or CentOS 7

$ sudo systemctl stop funapi-leaderboard
$ funapi-leaderboard-launcher --alsologtostderr --recover_leaderboard
$ sudo systemctl start funapi-leaderboard

Important

While running in recovery mode, the –alsologtostderr option must be added. If you try to run recovery mode without this option, a log telling you to add the program will be output and the program will close.

When recovery mode is run as above, you will be asked whether you want to import and restore data from all leaderboard tables in MySQL.

$ funapi-leaderboard-launcher --alsologtostderr --recover_leaderboard
...
I1221 11:19:49.893380  9231 leaderboard.cc:714] Start recovery mode
I1221 11:19:50.894167  9231 leaderboard.cc:742] The recovery target leaderboard: player_game
I1221 11:19:50.894194  9231 leaderboard.cc:745] The leaderboard will be recovered ranking data from DB to Redis. Do you want to do this? (y, n)

You can confirm recovery by leaderboard ID, not table unit. For example, if you are using leaderboard IDs called player_game and guild_game, the following tables will be present in MySQL, and after confirming whether you want to restore player_game, you can confirm whether you also want to restore guild_game.

mysql> show tables;
+-------------------------------+
| Tables_in_funapi_leaderboard1 |
+-------------------------------+
| player_game_alltime           |
| player_game_daily             |
| player_game_lastweek          |
| player_game_weekly            |
| player_game_yesterday         |
| guild_game_alltime            |
| guild_game_daily              |
| guild_game_lastweek           |
| guild_game_weekly             |
| guild_game_yesterday          |
+-------------------------------+

Tip

To restore everything without confirming, use the option --force_leaderboard_recovery. You can omit --alsologtostderr when using this option, but it is better to add it to see recovery progress.

$ funapi-leaderboard-launcher --force_leaderboard_recovery --alsologtostderr

26.2. Example: Resetting rankings

Let’s look at how to reset rankings through an example. The name player_game is used for competitive groups here.

Tip

To learn more about the functions used below, please see API documentation.

LeaderboardRange::Type must be used as follows according to set values specifying the search range.

Type

Setting begin and end

kAll

Begin and end must be set to 0.

kFromTop

Begin is more than 0 and end must be equal to or larger than begin.

kNearby

Begin may be less than 0 and end must be equal to or larger than begin.

26.2.1. Searching all weekly rankings

26.2.1.1. Synchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const char *kPlayerGameScore = "player_game";


void example() {
  LeaderboardQueryRequest request(
      kPlayerGameScore,
      kWeekly,
      LeaderboardRange(LeaderboardRange::kAll, 0, 0),  // 전체 랭킹을 조회
      LeaderboardQueryRequest::kOrdinal);  // rank 값으로 1, 2, 3, 4 를 사용

  LeaderboardQueryResponse response;
  if (not GetLeaderboardSync(request, &response)) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  LOG(INFO) << "total player count: " << response.total_player_count;

  for (int i = 0; i < response.records.size(); ++i) {
    LOG(INFO) << "# " << response.records[i].rank << " - "
              << response.records[i].percentage << " - "
              << response.records[i].score << " - "
              << response.records[i].player_account.id();
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static string kPlayerGameScore = "player_game";

public static void example()
{
  Leaderboard.QueryRequest request = new Leaderboard.QueryRequest(
    kPlayerGameScore,
    Leaderboard.Timespan.kWeekly,
    new Leaderboard.Range (Leaderboard.RangeType.kAll, 0, 0),  // 전체 랭킹을 조회
    Leaderboard.RankingType.kOrdinal);  // rank 값으로 1, 2, 3, 4 를 사용

  Leaderboard.QueryResponse response;
  if (!Leaderboard.GetLeaderboardSync (request, out response))
  {
    Log.Error ("leaderboard system error");
    return;
  }

  foreach (Leaderboard.Record record in response.Records)
  {
    Log.Info ("rank={0}, percentage={1}, score={2}, id={3}",
              record.Rank, record.Percentage,
              record.Score, record.PlayerAccount.Id);
  }
}

26.2.1.2. Asynchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const char *kPlayerGameScore = "player_game";


void OnResponse(
    const LeaderboardQueryRequest &request,
    const LeaderboardQueryResponse &response,
    const bool &error) {
  if (error) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  LOG(INFO) << "total player count: " << response.total_player_count;

  for (int i = 0; i < response.records.size(); ++i) {
    LOG(INFO) << "# " << response.records[i].rank << " - "
              << response.records[i].percentage << " - "
              << response.records[i].score << " - "
              << response.records[i].player_account.id();
  }
}


void example() {
  LeaderboardQueryRequest request(
      kPlayerGameScore,
      kWeekly,
      LeaderboardRange(LeaderboardRange::kAll, 0, 0),  // 전체 랭킹 조회
      LeaderboardQueryRequest::kOrdinal);  // rank 값으로 1, 2, 3, 4 를 사용

  LeaderboardQueryResponseHandler handler = OnResponse;
  GetLeaderboard(request, handler);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static string kPlayerGameScore = "player_game";

public static void OnResponse(Leaderboard.QueryRequest request,
                              Leaderboard.QueryResponse response,
                              bool error)
{
  if (error) {
    Log.Error ("leaderboard system error");
    return;
  }

  Log.Info("total player count: {0}", response.TotalPlayerCount);

  foreach (Leaderboard.Record record in response.Records)
  {
    Log.Info ("rank={0}, percentage={1}, score={2}, id={3}",
              record.Rank, record.Percentage,
              record.Score, record.PlayerAccount.Id);
  }
}

public static void example()
{
  Leaderboard.QueryRequest request = new Leaderboard.QueryRequest(
    kPlayerGameScore,
    Leaderboard.Timespan.kWeekly,
    new Leaderboard.Range (Leaderboard.RangeType.kAll, 0, 0),  // 전체 랭킹 조회
    Leaderboard.RankingType.kOrdinal);  // rank 값으로 1, 2, 3, 4 사용

  Leaderboard.GetLeaderboard(request, OnResponse);
}

26.2.2. Searching weekly Top 10 rankings

26.2.2.1. Synchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const char *kPlayerGameScore = "player_game";


void example() {
  LeaderboardQueryRequest request(
      kPlayerGameScore,
      kWeekly,
      LeaderboardRange(LeaderboardRange::kFromTop, 0, 9),  // Top 10 을 조회
      LeaderboardQueryRequest::kStdCompetition);  // 1, 2, 2, 4 식으로 동점자를 처리합니다.

  LeaderboardQueryResponse response;
  if (not GetLeaderboardSync(request, &response)) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  LOG(INFO) << "total player count: " << response.total_player_count;

  for (int i = 0; i < response.records.size(); ++i) {
    LOG(INFO) << "# " << response.records[i].rank << " - "
              << response.records[i].percentage << " - "
              << response.records[i].score << " - "
              << response.records[i].player_account.id();
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static string kPlayerGameScore = "player_game";

public static void example()
{
  Leaderboard.QueryRequest request = new Leaderboard.QueryRequest(
    kPlayerGameScore,
    Leaderboard.Timespan.kWeekly,
    new Leaderboard.Range (Leaderboard.RangeType.kFromTop, 0, 9),  // Top 10 을 조회
    Leaderboard.RankingType.kStdCompetition);  // 1, 2, 2, 4 식으로 동점자를 처리

  Leaderboard.QueryResponse response;
  if (!Leaderboard.GetLeaderboardSync (request, out response))
  {
    Log.Error ("leaderboard system error");
    return;
  }

  foreach (Leaderboard.Record record in response.Records)
  {
    Log.Info ("rank={0}, percentage={1}, score={2}, id={3}",
              record.Rank, record.Percentage,
              record.Score, record.PlayerAccount.Id);
  }
}

26.2.2.2. Asynchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const char *kPlayerGameScore = "player_game";


void OnResponse(
    const LeaderboardQueryRequest &request,
    const LeaderboardQueryResponse &response,
    const bool &error) {
  if (error) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  LOG(INFO) << "total player count: " << response.total_player_count;

  for (int i = 0; i < response.records.size(); ++i) {
    LOG(INFO) << "# " << response.records[i].rank << " - "
              << response.records[i].percentage << " - "
              << response.records[i].score << " - "
              << response.records[i].player_account.id();
  }
}


void example() {
  LeaderboardQueryRequest request(
      kPlayerGameScore,
      kWeekly,
      LeaderboardRange(LeaderboardRange::kFromTop, 0, 9),  // Top 10 을 조회
      LeaderboardQueryRequest::kStdCompetition);  // 1, 2, 2, 4 식으로 동점자 처리

  LeaderboardQueryResponseHandler handler = OnResponse;
  GetLeaderboard(request, handler);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static string kPlayerGameScore = "player_game";

public static void OnResponse(Leaderboard.QueryRequest request,
                              Leaderboard.QueryResponse response,
                              bool error)
{
  if (error) {
    Log.Error ("leaderboard system error");
    return;
  }

  Log.Info("total player count: {0}", response.TotalPlayerCount);

  foreach (Leaderboard.Record record in response.Records)
  {
    Log.Info ("rank={0}, percentage={1}, score={2}, id={3}",
              record.Rank, record.Percentage,
              record.Score, record.PlayerAccount.Id);
  }
}

public static void example()
{
  Leaderboard.QueryRequest request = new Leaderboard.QueryRequest(
    kPlayerGameScore,
    Leaderboard.Timespan.kWeekly,
    new Leaderboard.Range (Leaderboard.RangeType.kFromTop, 0, 9),  // Top 10 조회
    Leaderboard.RankingType.kStdCompetition);  // 1, 2, 2, 4 식으로 동점자 처리

  Leaderboard.QueryResponse response;
  Leaderboard.GetLeaderboard(request, OnResponse);
}

26.2.3. Searching rankings near your own

In the example below, we search for users near 5th place. We will search for rankings within a Facebook user group.

26.2.3.1. Synchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const char *kPlayerGameScore = "player_game";


void example() {
  string service_provider = "Facebook"; // service provider
  string player_id = "testuser";        // player id in service provider

  LeaderboardQueryRequest request(
      kPlayerGameScore,
      service_provider,
      player_id,
      kWeekly,
      LeaderboardRange(LeaderboardRange::kNearBy, -5, 5),  // 위 아래 5순위.
      LeaderboardQueryRequest::kDense);  // 동점자가 있을 시 1, 2, 2, 3 형태로 처리

  LeaderboardQueryResponse response;
  if (not GetLeaderboardSync(request, &response)) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  LOG(INFO) << "total player count: " << response.total_player_count;

  for (int i = 0; i < response.records.size(); ++i) {
    LOG(INFO) << "# " << response.records[i].rank << " - "
              << response.records[i].percentage << " - "
              << response.records[i].score << " - "
              << response.records[i].player_account.id();
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static string kPlayerGameScore = "player_game";

public static void example()
{
  string service_provider = "Facebook"; // service provider
  string player_id = "testuser";        // player id in service provider

  Leaderboard.QueryRequest request = new Leaderboard.QueryRequest(
    kPlayerGameScore,
    service_provider,
    player_id,
    Leaderboard.Timespan.kWeekly,
    new Leaderboard.Range (Leaderboard.RangeType.kNearBy, -5, 5),  // 위 아래 5 순위.
    Leaderboard.RankingType.kDense);  // 동점자가 있을 시, 1, 2, 2, 3 형태로 처리

  Leaderboard.QueryResponse response;
  if (!Leaderboard.GetLeaderboardSync (request, out response))
  {
    Log.Error("leaderboard system error");
    return;
  }

  Log.Info("total player count: {0}", response.TotalPlayerCount);

  foreach (Leaderboard.Record record in response.Records)
  {
    Log.Info ("rank={0}, percentage={1}, score={2}, id={3}",
              record.Rank, record.Percentage,
              record.Score, record.PlayerAccount.Id);
  }
}

26.2.3.2. Asynchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const char *kPlayerGameScore = "player_game";


void OnResponse(
    const LeaderboardQueryRequest &request,
    const LeaderboardQueryResponse &response,
    const bool &error) {
  if (error) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  LOG(INFO) << "total player count: " << response.total_player_count;

  for (int i = 0; i < response.records.size(); ++i) {
    LOG(INFO) << "# " << response.records[i].rank << " - "
              << response.records[i].percentage << " - "
              << response.records[i].score << " - "
              << response.records[i].player_account.id();
  }
}


void example() {
  string service_provider = "Facebook"; // service provider
  string player_id = "testuser";        // player id in service provider

  LeaderboardQueryRequest request(
      kPlayerGameScore,
      service_provider,
      player_id,
      kWeekly,
      LeaderboardRange(LeaderboardRange::kNearBy, -5, 5),  // 위 아래 5순위.
      LeaderboardQueryRequest::kDense);  // 동점자를 1, 2, 2, 3 형태로 처리

  LeaderboardQueryResponseHandler handler = OnResponse;
  GetLeaderboard(request, handler);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
static string kPlayerGameScore = "player_game";

public static void OnResponse(Leaderboard.QueryRequest request,
                              Leaderboard.QueryResponse response,
                              bool error)
{
  if (error) {
    Log.Error ("leaderboard system error");
    return;
  }

  Log.Info("total player count: {0}", response.TotalPlayerCount);

  foreach (Leaderboard.Record record in response.Records)
  {
    Log.Info ("rank={0}, percentage={1}, score={2}, id={3}",
              record.Rank, record.Percentage,
              record.Score, record.PlayerAccount.Id);
  }
}

public static void example()
{
  string service_provider = "Facebook"; // service provider
  string player_id = "testuser";        // player id in service provider

  Leaderboard.QueryRequest request = new Leaderboard.QueryRequest(
    kPlayerGameScore,
    service_provider,
    player_id,
    Leaderboard.Timespan.kWeekly,
    new Leaderboard.Range (Leaderboard.RangeType.kNearby, -5, 5),  // 위 아래 5순위.
    Leaderboard.RankingType.kDense);  // 동점자를 1, 2, 2, 3 형태로 처리

  Leaderboard.GetLeaderboard(request, OnResponse);
}

26.2.4. Searching your own rankings

The following is an example of searching for a user’s own rankings. We will search for rankings within a Facebook user group.

26.2.4.1. Synchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const char *kPlayerGameScore = "player_game";


void example() {
  string service_provider = "Facebook"; // service provider
  string player_id = "testuser";        // player id in service provider

  // RankingType 은 입력하지 않았으므로 기본값인 kOrdinal 이 됩니다.
  LeaderboardQueryRequest request(
      kPlayerGameScore,
      service_provider,
      player_id,
      kWeekly,
      LeaderboardRange(LeaderboardRange::kNearBy, 0, 0));  // 자기 랭킹을 조회합니다.

  LeaderboardQueryResponse response;
  if (not GetLeaderboardSync(request, &response)) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  LOG(INFO) << "total player count: " << response.total_player_count;

  // 랭킹 타입을 묵시적 기본값인 kOrdinal 을 사용했으로 rank 값은 1, 2, 3, 4, ... 이 됩니다.
  for (int i = 0; i < response.records.size(); ++i) {
    LOG(INFO) << "# " << response.records[i].rank << " - "
              << response.records[i].percentage << " - "
              << response.records[i].score << " - "
              << response.records[i].player_account.id();
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
static string kPlayerGameScore = "player_game";

public static void example()
{
  string service_provider = "Facebook"; // service provider
  string player_id = "testuser";        // player id in service provider

  // RankingType 은 입력하지 않았으므로 기본값인 kOrdinal 이 됩니다.
  Leaderboard.QueryRequest request = new Leaderboard.QueryRequest(
    kPlayerGameScore,
    service_provider,
    player_id,
    Leaderboard.Timespan.kWeekly,
    new Leaderboard.Range (Leaderboard.RangeType.kNearby, 0, 0)  // 자기 랭킹 조회
  );

  Leaderboard.QueryResponse response;
  if (!Leaderboard.GetLeaderboardSync (request, out response))
  {
    Log.Error("leaderboard system error");
    return;
  }

  Log.Info("total player count: {0}", response.TotalPlayerCount);

  // 랭킹 타입을 묵시적 기본값인 kOrdinal 을 사용했으므로 rank 값은 1, 2, 3, 4, ... 이 됩니다.
  foreach (Leaderboard.Record record in response.Records)
  {
    Log.Info ("rank={0}, percentage={1}, score={2}, id={3}",
              record.Rank, record.Percentage,
              record.Score, record.PlayerAccount.Id);
  }
}

26.2.4.2. Asynchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const char *kPlayerGameScore = "player_game";


void OnResponse(
    const LeaderboardQueryRequest &request,
    const LeaderboardQueryResponse &response,
    const bool &error) {
  if (error) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  LOG(INFO) << "total player count: " << response.total_player_count;

  // 랭킹 타입을 묵시적 기본값인 kOrdinal 로 사용했으므로 rank 값은 1, 2, 3, 4, ... 이 됩니다.
  for (int i = 0; i < response.records.size(); ++i) {
    LOG(INFO) << "# " << response.records[i].rank << " - "
              << response.records[i].percentage << " - "
              << response.records[i].score << " - "
              << response.records[i].player_account.id();
  }
}


void example() {
  string service_provider = "Facebook"; // service provider
  string player_id = "testuser";        // player id in service provider

  // RankingType 은 입력하지 않았으므로 기본값인 kOrdinal 이 됩니다.
  LeaderboardQueryRequest request(
      kPlayerGameScore,
      service_provider,
      player_id,
      kWeekly,
      LeaderboardRange(LeaderboardRange::kNearBy, 0, 0));  // 자기 랭킹을 조회합니다.

  LeaderboardQueryResponseHandler handler = OnResponse;
  GetLeaderboard(request, handler);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using funapi;

static string kPlayerGameScore = "player_game";

public static void OnResponse(Leaderboard.QueryRequest request,
                              Leaderboard.QueryResponse response,
                              bool error)
{
  if (error) {
    Log.Error ("leaderboard system error");
    return;
  }

  Log.Info("total player count: {0}", response.TotalPlayerCount);

  // 랭킹 타입을 묵시적 기본값인 kOrdinal 로 사용했으므로 rank 값은 1, 2, 3, 4, ... 이 됩니다.
  foreach (Leaderboard.Record record in response.Records)
  {
    Log.Info ("rank={0}, percentage={1}, score={2}, id={3}",
              record.Rank, record.Percentage,
              record.Score, record.PlayerAccount.Id);
  }
}

public static void example()
{
  const string kPlayerGameScore = "player_game";

  string service_provider = "Facebook"; // service provider
  string player_id = "testuser";        // player id in service provider

  // RankingType 은 입력하지 않았으므로 기본값인 kOrdinal 이 됩니다.
  Leaderboard.QueryRequest request = new Leaderboard.QueryRequest(
    kPlayerGameScore,
    service_provider,
    player_id,
    Leaderboard.Timespan.kWeekly,
    new Leaderboard.Range (Leaderboard.RangeType.kNearby, 0, 0)  // 자기 랭킹 조회
  );

  Leaderboard.GetLeaderboard(request, OnResponse);
}

26.2.5. Searching friend rankings

The following shows how to search rankings within Facebook friends. Please see Example: Retrieving Facebook friends to learn how to retrieve Facebook friends lists.

Tip

When a LeaderboardQueryRequest is made in the example above and kWeekly is changed to kLastWeek, a request can be made for friend rankings within the past week. Likewise, when changed to kYesterday, a request can be made for friend rankings within the past day.

LeaderboardQueryRequest request(
    kPlayerGameScore, service_provider, player_id, friend_list, kLastWeek);

26.2.5.1. Synchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const char *kPlayerGameScore = "player_game";


void example() {
  string service_provider = "Facebook"; // service provider
  string player_id = "testuser";        // player id in service provider

  PlayerAccountVector friend_list = ...;  // 친구 리스트를 추출했다고 가정하겠습니다.

  // RankingType 은 입력하지 않았으므로 기본값인 kOrdinal 이 됩니다.
  LeaderboardQueryRequest request(
      kPlayerGameScore, service_provider, player_id, friend_list, kWeekly);

  LeaderboardQueryResponse response;
  if (not GetLeaderboardSync(request, &response)) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  LOG(INFO) << "total player count: " << response.total_player_count;

  // 랭킹 타입을 묵시적 기본값인 kOrdinal 로 했으므로 rank 값은 1, 2, 3, 4, ... 이 됩니다.
  for (int i = 0; i < response.records.size(); ++i) {
    LOG(INFO) << "# " << response.records[i].rank << " - "
              << response.records[i].percentage << " - "
              << response.records[i].score << " - "
              << response.records[i].player_account.id();
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
static string kPlayerGameScore = "player_game";

public static void example()
{
  string service_provider = "Facebook"; // service provider
  string player_id = "testuser";        // player id in service provider

  List<PlayerAccount> friend_list = ...;  // 친구 목록을 추출했다고 가정하겠습니다.

  Leaderboard.QueryRequest request = new Leaderboard.QueryRequest(
    kPlayerGameScore,
    service_provider,
    player_id,
    friend_list,
    Leaderboard.Timespan.kWeekly);

  Leaderboard.QueryResponse response;
  if (!Leaderboard.GetLeaderboardSync (request, out response))
  {
    Log.Error("leaderboard system error");
    return;

  }

  Log.Info("total player count: {0}", response.TotalPlayerCount);

  // 랭킹 타입을 묵시적 기본값인 kOrdinal 로 했으므로 rank 값은 1, 2, 3, 4, ... 이 됩니다.
  foreach (Leaderboard.Record record in response.Records)
  {
    Log.Info ("rank={0}, percentage={1}, score={2}, id={3}",
              record.Rank, record.Percentage,
              record.Score, record.PlayerAccount.Id);
  }
}

26.2.5.2. Asynchronous version

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const char *kPlayerGameScore = "player_game";

void OnResponse(
    const LeaderboardQueryRequest &request,
    const LeaderboardQueryResponse &response,
    const bool &error) {
  if (error) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  LOG(INFO) << "total player count: " << response.total_player_count;

  // 랭킹 타입을 묵시적 기본값인 kOrdinal 로 했으므로 rank 값은 1, 2, 3, 4, ... 이 됩니다.
  for (int i = 0; i < response.records.size(); ++i) {
    LOG(INFO) << "# " << response.records[i].rank << " - "
              << response.records[i].percentage << " - "
              << response.records[i].score << " - "
              << response.records[i].player_account.id();
  }
}


void example() {
  string service_provider = "Facebook"; // service provider
  string player_id = "testuser";        // player id in service provider

  PlayerAccountVector friend_list = ...;  // 친구 목록을 추출했다고 가정하겠습니다.

  // RankingType 은 입력하지 않았으므로 기본값인 kOrdinal 이 됩니다.
  LeaderboardQueryRequest request(
      kPlayerGameScore, service_provider, player_id, friend_list, kWeekly);

  LeaderboardQueryResponseHandler handler = OnResponse;
  GetLeaderboard(request, handler);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
static string kPlayerGameScore = "player_game";

public static void OnResponse(Leaderboard.QueryRequest request,
                              Leaderboard.QueryResponse response,
                              bool error)
{
  if (error) {
    Log.Error ("leaderboard system error");
    return;
  }

  Log.Info("total player count: {0}", response.TotalPlayerCount);

  // 랭킹 타입을 묵시적 기본값인 kOrdinal 로 했으므로 rank 값은 1, 2, 3, 4, ... 이 됩니다.
  foreach (Leaderboard.Record record in response.Records)
  {
    Log.Info ("rank={0}, percentage={1}, score={2}, id={3}",
              record.Rank, record.Percentage,
              record.Score, record.PlayerAccount.Id);
  }
}

public static void example()
{
  string service_provider = "Facebook"; // service provider
  string player_id = "testuser";        // player id in service provider

  List<PlayerAccount> friend_list = ...;  // 친구 목록을 추출했다고 가정하겠습니다.

  Leaderboard.QueryRequest request = new Leaderboard.QueryRequest(
    kPlayerGameScore,
    service_provider,
    player_id,
    friend_list,
    Leaderboard.Timespan.kWeekly);

  Leaderboard.GetLeaderboard(request, OnResponse);
}

26.2.6. Searching for several types of ranking at once

In this example, we combine the above searches for top 10 rankings, rankings near your own, your rankings, and friend rankings to search rankings with several criteria in one request.

26.2.6.1. Synchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
const char *kPlayerGameScore = "player_game";


void example() {
  string service_provider = "Facebook"; // service provider
  string player_id = "testuser";        // player id in service provider

  // 한번에 여러 랭킹을 조회할 것이므로 Vector 를 만듭니다.
  LeaderboardQueryRequestVector requests;

  // 우선 TOP 10 랭킹을 조회하기 위한 요청자를 만들고 vector 에 추가합니다.
  requests.push_back(
      LeaderboardQueryRequest(
          kPlayerGameScore,
          kWeekly,
          LeaderboardRange(LeaderboardRange::kFromTop, 0, 9),
          LeaderboardQueryRequest::kStdCompetition));

  // 내 주변 랭킹을 조회하기 위한 요청자를 만들고 vector 에 추가합니다.
  requests.push_back(
      LeaderboardQueryRequest(
          kPlayerGameScore,
          service_provider,
          player_id,
          kWeekly,
          LeaderboardRange(LeaderboardRange::kNearBy, -5, 5),
          LeaderboardQueryRequest::kDense));

  // 내 랭킹을 조회하기 위한 요청자를 만들고 vector 에 추가합니다.
  requests.push_back(
      LeaderboardQueryRequest(
          kPlayerGameScore,
          service_provider,
          player_id,
          kWeekly,
          LeaderboardRange(LeaderboardRange::kNearBy, 0, 0));

  // 친구 목록은 가져왔다고 가정하겠습니다.
  PlayerAccountVector friend_list = ...;

  requests.push_back(
      LeaderboardQueryRequest(
          kPlayerGameScore,
          service_provider,
          player_id,
          friend_list,
          kWeekly);

  // 랭킹을 조회합니다.
  LeaderboardQueryResponseVector responses;
  if (not GetLeaderboardSync(requests, &responses)) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  // responses 를 하나씩 루프돌면서 얻어온 랭킹 결과를 출력합니다.
  BOOST_FOREACH(const LeaderboardQueryResponse &response, responses) {
    LOG(INFO) << "total player count: " << response.total_player_count;

    for (int i = 0; i < response.records.size(); ++i) {
      LOG(INFO) << "# " << response.records[i].rank << " - "
                << response.records[i].percentage << " - "
                << response.records[i].score << " - "
                << response.records[i].player_account.id();
    }
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  static string kPlayerGameScore = "player_game";

  public static void example()
  {
    string service_provider = "Facebook"; // service provider
    string player_id = "testuser";        // player id in service provider

    List<Leaderboard.QueryRequest> requests = new List<Leaderboard.QueryRequest> ();

    // 우선 TOP 10 랭킹을 조회하기 위한 요청자를 만들고 vector 에 추가합니다.
    requests.Add (new Leaderboard.QueryRequest(
        kPlayerGameScore,
        Leaderboard.Timespan.kWeekly,
        // 전체 랭킹을 조회합니다.
        new Leaderboard.Range (Leaderboard.RangeType.kFromTop, 0, 9),
        // 1224 랭킹 타입으로 동점자 순위를 처리합니다.
        Leaderboard.RankingType.kStdCompetition));

    // 내 주변 랭킹을 조회하기 위한 요청자를 만들고 vector 에 추가합니다.
    requests.Add (new Leaderboard.QueryRequest(
        kPlayerGameScore,
        service_provider,
        player_id,
        Leaderboard.Timespan.kWeekly,
        // 위 아래 5명의 랭킹을 조회합니다.
        new Leaderboard.Range (Leaderboard.RangeType.kNearby, -5, 5),
        // 1223 랭킹 타입으로 동점자 순위를 처리합니다.
        Leaderboard.RankingType.kDense));

    // 내 랭킹을 조회하기 위한 요청자를 만들고 list 에 추가합니다.
    requests.Add (new Leaderboard.QueryRequest(
        kPlayerGameScore,
        service_provider,
        player_id,
        Leaderboard.Timespan.kWeekly,
        // 나의 랭킹을 조회합니다.
        new Leaderboard.Range (Leaderboard.RangeType.kNearby, 0, 0)));

    // 친구 목록은 가져왔다고 가정하겠습니다.
    List<PlayerAccount> friend_list = ...;

    requests.Add (new Leaderboard.QueryRequest(
        kPlayerGameScore,
        service_provider,
        player_id,
        friend_list,
        Leaderboard.Timespan.kWeekly));

    List<Leaderboard.QueryResponse> responses;

    // 랭킹을 조회합니다.
    if (!Leaderboard.GetLeaderboard (requests, out responses))
    {
      return;
    }

    foreach (Leaderboard.QueryResponse response in responses)
    {
      foreach (Leaderboard.Record record in response.Records)
      {
        Log.Info ("rank={0}, percentage={1}, score={2}, id={3}",
                  record.Rank, record.Percentage,
                  record.Score, record.PlayerAccount.Id);
      }
    }
  }

26.2.6.2. Asynchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
const char *kPlayerGameScore = "player_game";


void OnResponse(
    const LeaderboardQueryRequestVector &requests,
    const LeaderboardQueryResponseVector &responses,
    const bool &error) {
  if (error) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  // responses 를 하나씩 루프돌면서 얻어온 랭킹 결과를 출력합니다.
  BOOST_FOREACH(const LeaderboardQueryResponse &response, responses) {
    LOG(INFO) << "total player count: " << response.total_player_count;

    for (int i = 0; i < response.records.size(); ++i) {
      LOG(INFO) << "# " << response.records[i].rank << " - "
                << response.records[i].percentage << " - "
                << response.records[i].score << " - "
                << response.records[i].player_account.id();
    }
  }
}


void example() {
  string service_provider = "Facebook"; // service provider
  string player_id = "testuser";        // player id in service provider

  LeaderboardQueryRequestVector requests;

  // 우선 TOP 10 랭킹을 조회하기 위한 요청자를 만들고 vector 에 추가합니다.
  requests.push_back(
      LeaderboardQueryRequest(
          kPlayerGameScore,
          kWeekly,
          LeaderboardRange(LeaderboardRange::kFromTop, 0, 9),
          LeaderboardQueryRequest::kStdCompetition));

  // 내 주변 랭킹을 조회하기 위한 요청자를 만들고 vector 에 추가합니다.
  requests.push_back(
      LeaderboardQueryRequest(
          kPlayerGameScore,
          service_provider,
          player_id,
          kWeekly,
          LeaderboardRange(LeaderboardRange::kNearBy, -5, 5),
          LeaderboardQueryRequest::kDense));

  // 내 랭킹을 조회하기 위한 요청자를 만들고 vector 에 추가합니다.
  requests.push_back(
      LeaderboardQueryRequest(
          kPlayerGameScore,
          service_provider,
          player_id,
          kWeekly,
          LeaderboardRange(LeaderboardRange::kNearBy, 0, 0));

  // 친구 목록은 가져왔다고 가정하겠습니다.
  PlayerAccountVector friend_list = ...;

  requests.push_back(
      LeaderboardQueryRequest(
          kPlayerGameScore,
          service_provider,
          player_id,
          friend_list,
          kWeekly);

  // 랭킹을 조회합니다.
  LeaderboardQueryResponseHandler2 handler = OnResponse;
  GetLeaderboard(requests, handler);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
static string kPlayerGameScore = "player_game";

public static void OnResponse(
    List<Leaderboard.QueryRequest> requests,
    List<Leaderboard.QueryResponse> responses,
    bool error)
{
  if (error) {
    return;
  }

  foreach (Leaderboard.QueryResponse response in responses)
  {
    foreach (Leaderboard.Record record in response.Records)
    {
      Log.Info ("rank={0}, percentage={1}, score={2}, id={3}",
                record.Rank, record.Percentage,
                record.Score, record.PlayerAccount.Id);
    }
  }
}

public static void example()
{
  string service_provider = "Facebook"; // service provider
  string player_id = "testuser";        // player id in service provider

  List<Leaderboard.QueryRequest> requests = new List<Leaderboard.QueryRequest> ();

  // 우선 TOP 10 랭킹을 조회하기 위한 요청자를 만들고 vector 에 추가합니다.
  requests.Add (new Leaderboard.QueryRequest(
      kPlayerGameScore,
      Leaderboard.Timespan.kWeekly,
      // 전체 랭킹을 조회합니다.
      new Leaderboard.Range (Leaderboard.RangeType.kFromTop, 0, 9),
      // 1224 랭킹 타입으로 동점자 순위를 처리합니다.
      Leaderboard.RankingType.kStdCompetition));

  // 내 주변 랭킹을 조회하기 위한 요청자를 만들고 vector 에 추가합니다.
  requests.Add (new Leaderboard.QueryRequest(
      kPlayerGameScore,
      service_provider,
      player_id,
      Leaderboard.Timespan.kWeekly,
      // 위 아래 5명의 랭킹을 조회합니다.
      new Leaderboard.Range (Leaderboard.RangeType.kNearby, -5, 5),
      // 1223 랭킹 타입으로 동점자 순위를 처리합니다.
      Leaderboard.RankingType.kDense));

  // 내 랭킹을 조회하기 위한 요청자를 만들고 list 에 추가합니다.
  requests.Add (new Leaderboard.QueryRequest(
      kPlayerGameScore,
      service_provider,
      player_id,
      Leaderboard.Timespan.kWeekly,
      // 나의 랭킹을 조회합니다.
      new Leaderboard.Range (Leaderboard.RangeType.kNearby, 0, 0)));

  // 친구 목록은 가져왔다고 가정하겠습니다.
  List<PlayerAccount> friend_list = ...;

  requests.Add (new Leaderboard.QueryRequest(
      kPlayerGameScore,
      service_provider,
      player_id,
      friend_list,
      Leaderboard.Timespan.kWeekly));

  // 랭킹을 조회합니다.
  Leaderboard.GetLeaderboard (requests, OnResponse);
}

26.3. Example: Updating scores

There are 4 ways to update scores.

Type

Description

kHighScore

Update only if the given score is the high score.

kIncrement

Increase the current score by as much as the given score.

kDecrement

Decrease the current score by as much as the given score.

kOverwriting

Overwrite the current score with the given score.

Let’s examine these methods of updating scores through examples.

26.3.1. Updating if the current score is the high score

26.3.1.1. Synchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
const char *kPlayerGameScore = "player_game";

void example() {
  string service_provider = "Facebook";
  string player_id = "testuser";
  double score = 10000;

  // kHighScore 를 입력하여 최고 점수인 경우에만 갱신합니다.
  ScoreSubmissionRequest request(
      kPlayerGameScore,
      service_provider,
      player_id,
      score,
      ScoreSubmissionRequest::kHighScore);

  ScoreSubmissionResponse response;
  if (not SubmitScoreSync(request, &response)) {
    // system error
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  // kHighScore 인 경우 최고 점수 여부와 상관없이 입력한 score 와 동일합니다.
  // kIncrement, kDecrement 인 경우 증가, 감소된 점수입니다.
  // kOverwriting 인 경우 입력한 score 와 동일합니다.
  LOG(INFO) << "new score: " << response.new_score;

  // 결과값에 따라 대응합니다.
  switch (response.result) {
    case kNewRecord: {
      // 신기록이네요.
      break;
    }
    case kNewRecordWeekly: {
      // 주간 최고 기록입니다.
      break;
    }
    case kNewRecordDaily: {
      // 일간 최고 기록입니다.
      break;
    }
    case kNone: {
      // no update
      break;
    }
    default: {
      BOOST_ASSERT(false);
    }
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
static string kPlayerGameScore = "player_game";

public void exmaple()
{
  string service_provider = "Facebook";
  string player_id = "testuser";
  double score = 10000;

  // kHighScore 를 입력하여 최고 점수인 경우에만 갱신합니다.
  Leaderboard.ScoreSubmissionRequest request =
      new Leaderboard.ScoreSubmissionRequest(
          kPlayerGameScore,
          service_provider,
          player_id,
          score,
          Leaderboard.ScoreSubmissionType.kHighScore);

  Leaderboard.ScoreSubmissionResponse response;
  if (!Leaderboard.SubmitScoreSync (request, out response))
  {
    Log.Error ("leaderboard system error");
    return;
  }

  // kHighScore 인 경우 최고 점수 여부와 상관없이 입력한 score 와 동일합니다.
  // kIncrement, kDecrement 인 경우 증가, 감소된 점수입니다.
  // kOverwriting 인 경우 입력한 score 와 동일합니다.
  Log.Info ("new score: {0}", response.NewScore);

  // 결과값에 따라 대응합니다.
  switch (response.Result)
  {
    case Leaderboard.ScoreSubmissionResult.kNewRecord:
    {
      // 신기록이네요.
      break;
    }
    case Leaderboard.ScoreSubmissionResult.kNewRecordWeekly:
    {
      // 주간 최고 기록입니다.
      break;
    }
    case Leaderboard.ScoreSubmissionResult.kNewRecordDaily:
    {
      // 일간 최고 기록입니다.
      break;
    }
    case Leaderboard.ScoreSubmissionResult.kNone:
    {
      // no update
      break;
    }
  }
}

26.3.1.2. Asynchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
const char *kPlayerGameScore = "player_game";

void OnScoreSubmitted(
    const ScoreSubmissionRequest &request,
    const ScoreSubmissionResponse &response,
    const bool &error) {
  if (error) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  // kHighScore 인 경우 최고 점수 여부와 상관없이 입력한 score 와 동일합니다.
  // kIncrement, kDecrement 인 경우 증가, 감소된 점수입니다.
  // kOverwriting 인 경우 입력한 score 와 동일합니다.
  LOG(INFO) << "new score: " << response.new_score;

  // 결과값에 따라 대응합니다.
  switch (response.result) {
    case kNewRecord: {
      // 신기록이네요.
      break;
    }
    case kNewRecordWeekly: {
      // 주간 최고 기록입니다.
      break;
    }
    case kNewRecordDaily: {
      // 일간 최고 기록입니다.
      break;
    }
    case kNone: {
      // no update
      break;
    }
    default: {
      BOOST_ASSERT(false);
    }
  }
}


void example() {
  string service_provider = "Facebook";
  string player_id = "testuser";
  double score = 10000;

  // kHighScore 를 입력하여 최고 점수인 경우에만 갱신합니다.
  ScoreSubmissionRequest request(
      kPlayerGameScore,
      service_provider,
      player_id,
      score,
      ScoreSubmissionRequest::kHighScore);

  ScoreSubmissionResponseHandler handler = OnScoreSubmitted;
  SubmitScore(request, handler);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
string kPlayerGameScore = "player_game";

public static void OnScoreSubmitted(
    Leaderboard.ScoreSubmissionRequest request,
    Leaderboard.ScoreSubmissionResponse response,
    bool error)
{
  if (error)
  {
    Log.Error ("leaderboard system error");
    return;
  }

  // kHighScore 인 경우 최고 점수 여부와 상관없이 입력한 score 와 동일합니다.
  // kIncrement, kDecrement 인 경우 증가, 감소된 점수입니다.
  // kOverwriting 인 경우 입력한 score 와 동일합니다.
  Log.Info ("new score: {0}", response.NewScore);

  // 결과값에 따라 대응합니다.
  switch (response.Result)
  {
    case Leaderboard.ScoreSubmissionResult.kNewRecord:
    {
      // 신기록이네요.
      break;
    }
    case Leaderboard.ScoreSubmissionResult.kNewRecordWeekly:
    {
      // 주간 최고 기록입니다.
      break;
    }
    case Leaderboard.ScoreSubmissionResult.kNewRecordDaily:
    {
      // 일간 최고 기록입니다.
      break;
    }
    case Leaderboard.ScoreSubmissionResult.kNone:
    {
      // no update
      break;
    }
  }
}

public static void example()
{
  string service_provider = "Facebook";
  string player_id = "testuser";
  double score = 10000;

  // kHighScore 를 입력하여 최고 점수인 경우에만 갱신합니다.
  Leaderboard.ScoreSubmissionRequest request =
      new Leaderboard.ScoreSubmissionRequest(
          kPlayerGameScore,
          service_provider,
          player_id,
          score,
          Leaderboard.ScoreSubmissionType.kHighScore);

  Leaderboard.SubmitScore (request, OnScoresubmitted);
}

26.3.2. Importing your rankings after score update

Let’s now examine an example of updating the score and importing the player’s ranking with it.

26.3.2.1. Synchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
const char *kPlayerGameScore = "player_game";

void example() {
  string service_provider = "Facebook";
  string player_id = "testuser";
  double score = 10000;

  // kHighScore 를 입력하여 최고 점수인 경우에만 갱신합니다.
  ScoreSubmissionRequest request(
      kPlayerGameScore,
      service_provider,
      player_id,
      score,
      LeaderboardQueryRequest::kOrdinal,
      kWeekly,
      ScoreSubmissionRequest::kHighScore);

  ScoreSubmissionResponse response;
  if (not SubmitScoreSync(request, &response)) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  // kHighScore 인 경우 최고 점수 여부와 상관없이 입력한 score 와 동일합니다.
  // kIncrement, kDecrement 인 경우 증가, 감소된 점수입니다.
  // kOverwriting 인 경우 입력한 score 와 동일합니다.
  // 내 랭킹 정보도 함께 출력합니다.
  LOG(INFO) << "new score: " << response.new_score
            << ", total player count: " << response.total_player_count
            << ", my rank: " << response.rank
            << ", percentage: " << response.percentage;

  // 결과값에 따라 대응합니다.
  switch (response.result) {
    case kNewRecord: {
      // 신기록이네요.
      break;
    }
    case kNewRecordWeekly: {
      // 주간 최고 기록입니다.
      break;
    }
    case kNewRecordDaily: {
      // 일간 최고 기록입니다.
      break;
    }
    case kNone: {
      // no update
      break;
    }
    default: {
      BOOST_ASSERT(false);
    }
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
static string kPlayerGameScore = "player_game";

public static void example()
{
  string service_provider = "Facebook";
  string player_id = "testuser";
  double score = 10000;

  // kHighScore 를 입력하여 최고 점수인 경우에만 갱신합니다.
  Leaderboard.ScoreSubmissionRequest request =
      new Leaderboard.ScoreSubmissionRequest(
          kPlayerGameScore,
          service_provider,
          player_id,
          score,
      Leaderboard.RankingType.kOrdinal,
      Leaderboard.Timespan.kWeekly,
      Leaderboard.ScoreSubmissionType.kHighScore);

  Leaderboard.ScoreSubmissionResponse response;
  if (!Leaderboard.SubmitScoreSync(request, out response))
  {
    Log.Error ("leaderboard system error");
    return;
  }

  // kHighScore 인 경우 최고 점수 여부와 상관없이 입력한 score 와 동일합니다.
  // kIncrement, kDecrement 인 경우 증가, 감소된 점수입니다.
  // kOverwriting 인 경우 입력한 score 와 동일합니다.
  // 내 랭킹 정보도 함께 출력합니다.
  Log.Info (
      "new score: {0}, total player count: {1}, my rank: {2}, percentage: {3}",
      response.NewScore, response.TotalPlayerCount, response.Rank, response.Percentage);

  // 결과값에 따라 대응합니다.
  switch (response.Result)
  {
    case Leaderboard.ScoreSubmissionResult.kNewRecord:
    {
        // 신기록이네요.
        break;
    }
    case Leaderboard.ScoreSubmissionResult.kNewRecordWeekly:
    {
      // 주간 최고 기록입니다.
      break;
    }
    case Leaderboard.ScoreSubmissionResult.kNewRecordDaily:
    {
      // 일간 최고 기록입니다.
      break;
    }
    case Leaderboard.ScoreSubmissionResult.kNone:
    {
      // no update
      break;
    }
  }
}

26.3.2.2. Asynchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
const char *kPlayerGameScore = "player_game";

void OnScoreSubmitted(
    const ScoreSubmissionRequest &request,
    const ScoreSubmissionResponse &response,
    const bool &error) {
  if (error) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  // kHighScore 인 경우 최고 점수 여부와 상관없이 입력한 score 와 동일합니다.
  // kIncrement, kDecrement 인 경우 증가, 감소된 점수입니다.
  // kOverwriting 인 경우 입력한 score 와 동일합니다.
  // 내 랭킹 정보도 함께 출력합니다.
  LOG(INFO) << "new score: " << response.new_score
            << ", total player count: " << response.total_player_count
            << ", my rank: " << response.rank
            << ", percentage: " << response.percentage;

  // 결과값에 따라 대응합니다.
  switch (response.result) {
    case kNewRecord: {
      // 신기록이네요.
      break;
    }
    case kNewRecordWeekly: {
      // 주간 최고 기록입니다.
      break;
    }
    case kNewRecordDaily: {
      // 일간 최고 기록입니다.
      break;
    }
    case kNone: {
      // no update
      break;
    }
    default: {
      BOOST_ASSERT(false);
    }
  }
}


void example() {
  string service_provider = "Facebook";
  string player_id = "testuser";
  double score = 10000;

  // kHighScore 를 입력하여 최고 점수인 경우에만 갱신합니다.
  ScoreSubmissionRequest request(
      kPlayerGameScore,
      service_provider,
      player_id,
      score,
      LeaderboardQueryRequest::kOrdinal,
      kWeekly,
      ScoreSubmissionRequest::kHighScore);

  ScoreSubmissionResponseHandler handler = OnScoreSubmitted;
  SubmitScore(request, handler);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
static string kPlayerGameScore = "player_game";

public static void OnScoreSubmitted(
    Leaderboard.ScoreSubmissionRequest request,
    Leaderboard.ScoreSubmissionResponse response,
    bool error)
{
  // kHighScore 인 경우 최고 점수 여부와 상관없이 입력한 score 와 동일합니다.
  // kIncrement, kDecrement 인 경우 증가, 감소된 점수입니다.
  // kOverwriting 인 경우 입력한 score 와 동일합니다.
  // 내 랭킹 정보도 함께 출력합니다.
  Log.Info (
      "new score: {0}, total player count: {1}, my rank: {2}, percentage: {3}",
      response.NewScore, response.TotalPlayerCount, response.Rank, response.Percentage);

  // 결과값에 따라 대응합니다.
  switch (response.Result)
  {
    case Leaderboard.ScoreSubmissionResult.kNewRecord:
    {
      // 신기록이네요.
      break;
    }
    case Leaderboard.ScoreSubmissionResult.kNewRecordWeekly:
    {
      // 주간 최고 기록입니다.
      break;
    }
    case Leaderboard.ScoreSubmissionResult.kNewRecordDaily:
    {
      // 일간 최고 기록입니다.
      break;
    }
    case Leaderboard.ScoreSubmissionResult.kNone:
    {
      // no update
      break;
    }
  }
}

public static void example()
{
  string service_provider = "Facebook";
  string player_id = "testuser";
  double score = 10000;

  // kHighScore 를 입력하여 최고 점수인 경우에만 갱신합니다.
  Leaderboard.ScoreSubmissionRequest request =
      new Leaderboard.ScoreSubmissionRequest(
          kPlayerGameScore,
          service_provider,
          player_id,
          score,
      Leaderboard.RankingType.kOrdinal,
      Leaderboard.Timespan.kWeekly,
      Leaderboard.ScoreSubmissionType.kHighScore);

  Leaderboard.SubmitScore (request, onScoreSubmitted);
}

26.4. Example: Deleting rankings

When rankings are deleted due to player restriction or kicking out, the DeleteScore() and DeleteScoreSync() functions are called to delete the ranking. When these functions are invoked, AllTime, Daily, Yesterday, Weekly, and LastWeek ranking data is all deleted.

There are two leaderboard IDs in the example below, and user ranking data is deleted in each.

Tip

Rankings are deleted in the server code in the example below. Please see (Advanced) Manually deleting/restoring ranking data to learn how to manually delete rankings in Redis/MySQL.

26.4.1. Synchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const char *kPlayerGameScore = "player_game";
const char *kGuildGameScore = "guild_game";

void example() {
  string service_provider = "Facebook";
  string player_id = "testuser";

  ScoreDeletionRequestVector requests;

  requests.push_back(
      ScoreDeletionRequest(
          kPlayerGameScore,
          service_provider,
          player_id));

  requests.push_back(
      ScoreDeletionRequest(
          kGuildGameScore,
          service_provider,
          player_id));

  ScoreDeletionResponseVector responses;
  if (not DeleteScoreSync(requests, &responses)) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  BOOST_FOREACH(const ScoreDeletionResponse &response, responses) {
    LOG(INFO) << "result: " << response.result;
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
string kPlayerGameScore = "player_game";
string kGuildGameScore = "guild_game";

public static void example()
{
  string service_provider = "Facebook";
  string player_id = "testuser";

  List<Leaderboard.ScoreDeletionRequest> requests =
      new List<Leaderboard.ScoreDeletionRequest> ();

  requests.Add(
      new Leaderboard.ScoreDeletionRequest (
          kPlayerGameScore,
          service_provider,
          player_id));

  requests.Add(
      new Leaderboard.ScoreDeletionRequest (
          kGuildGameScore,
          service_provider,
          player_id));

  List<Leaderboard.ScoreDeletionResponse> responses;
  if (!Leaderboard.DeleteScoreSync (requests, out responses))
  {
    Log.Error ("leaderboard system error");
    return;
  }

  foreach (Leaderboard.ScoreDeletionResponse response in responses)
  {
    Log.Info ("result: {0}", response.Result);
  }
}

26.4.2. Asynchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const char *kPlayerGameScore = "player_game";
const char *kGuildGameScore = "guild_game";


void OnScoreDeleted(
    const ScoreDeletionRequestVector &requests,
    const ScoreDeletionResponseVector &responses,
    const bool &error) {
  if (error) {
    LOG(ERROR) << "leaderboard system error";
    return;
  }

  BOOST_FOREACH(const ScoreDeletionResponse &response, responses) {
    LOG(INFO) << "result: " << response.result;
  }
}


void example() {
  string service_provider = "Facebook";
  string player_id = "testuser";

  ScoreDeletionRequestVector requests;

  requests.push_back(
      ScoreDeletionRequest(
          kPlayerGameScore,
          service_provider,
          player_id));

  requests.push_back(
      ScoreDeletionRequest(
          kGuildGameScore,
          service_provider,
          player_id));

  ScoreDeletionResponseHandler handler = OnScoreDeleted;
  DeleteScore(requests, handler);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
string kPlayerGameScore = "player_game";
string kGuildGameScore = "guild_game";

public static void OnScoreDeleted(
    List<Leaderboard.ScoreDeletionRequest> requests,
    List<Leaderboard.ScoreDeletionResponse> responses,
    bool error)
{
  if (error)
  {
    Log.Error ("leaderboard system error");
    return;
  }

  foreach (Leaderboard.ScoreDeletionResponse response in responses)
  {
    Log.Info ("result: {0}", response.Result);
  }
}

public static void example()
{
  string service_provider = "Facebook";
  string player_id = "testuser";

  List<Leaderboard.ScoreDeletionRequest> requests =
      new List<Leaderboard.ScoreDeletionRequest> ();

  requests.Add (
      new Leaderboard.ScoreDeletionRequest (
          kPlayerGameScore,
          service_provider,
          player_id));

  requests.Add (
      new Leaderboard.ScoreDeletionRequest (
          kGuildGameScore,
          service_provider,
          player_id));

  Leaderboard.DeleteScore (requests, OnScoreDeleted);
}

26.5. Example: Detecting the ranking reset schedule

When the game server and iFun Leaderboard connect, reset_schedules data is automatically imported. Therefore, only the synchronous version of the function that returns cached data is present, and there is no asynchronous version of the function.

You can get ranking reset schedule data registered in Registering reset schedules through the GetLeaderboardResetSchedule() function.

There is a version of this function for getting all reset schedule data and a version for importing only particular reset schedule data. In some cases, reset_schedules is not present in iFun Leaderboard’s MANIFEST.json or the data is incorrect, so false is returned.

Version to return all schedules:

bool GetLeaderboardResetSchedule(
    LeaderboardResetScheduleVector *reset_schedules,
    const string &tag = "");

Version to import particular reset schedule data:

// leaderboard_id 와 period 에 맞는 LeaderboardResetSchedule 을 얻어옵니다.
bool GetLeaderboardResetSchedule(const string &leaderboard_id,
                                 const LeaderboardResetSchedule::Period &period,
                                 LeaderboardResetSchedule *reset_schedule,
                                 const string &tag = "");

Example of use:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
void Example() {
  LeaderboardResetScheduleVector reset_schedules;

  if (not GetLeaderboardResetSchedule(&reset_schedules)) {
    // Failed...
    return;
  }

  BOOST_FOREACH(const LeaderboardResetSchedule &reset_schedule, reset_schedules) {
    LOG(INFO) << "leaderboard id: " << reset_schedule.leaderboard_id
              << ", period: " << reset_schedule.period
              << ", interval: " << reset_schedule.interval
              << ", starts: " << reset_schedule.starts
              << ", end: " << reset_schedule.ends
              << ", latest reset date time: " << reset_schedule.latest_reset_date_time
              << ", upcoming date time" << reset_schedule.upcoming_date_time
              << ", upcoming time: " << reset_schedule.upcoming_time
              << ", next date time" << reset_schedule.next_date_time
              << ", next time: " << reset_schedule.next_time
              << ", expired: " << reset_schedule.expired;
  }
}

You can get ranking reset schedule data registered in Registering reset schedules through the Leaderboard.GetResetSchedule() function.

There is a version of this function for getting all reset schedule data and a version for importing only particular reset schedule data. In some cases, reset_schedules is not present in iFun Leaderboard’s MANIFEST.json or the data is incorrect, so false is returned.

Version to return all schedules:

bool GetResetSchedule(out List<Leaderboard.ResetSchedule> reset_schedules,
                      string tag = "");

Version to import particular reset schedule data:

// leaderboard_id 와 period 에 맞는 Leaderboard.ResetSchedule 을 얻어옵니다.
bool GetResetSchedule(string leaderboard_id,
                      Leaderboard.ResetSchedulePeriod period,
                      out Leaderboard.ResetSchedule reset_schedule,
                      string tag = "");

Example of use:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void Example()
{
  List<Leaderboard.ResetSchedule> reset_schedules;

  if (!Leaderboard.GetResetSchedule(out reset_schedules)) {
    // Failed...
    return;
  }

  foreach (Leaderboard.ResetSchedule reset_schedule in reset_schedules)
  {
    Log.Info("leaderboard_id: {0}, period: {1}, interval: {2}" +
             ", starts: {3}, end: {4}, latest reset date time: {5}" +
             ", upcoming date time: {6}, upcoming time: {7}" +
             ", next date time: {8}, next time: {9}, expired: {10}",
             reset_schedule.LeaderboardId,
             reset_schedule.ResetSchedulePeriod,
             reset_schedule.Interval,
             reset_schedule.Starts,
             reset_schedule.Ends,
             reset_schedule.LatestResetDateTime,
             reset_schedule.UpcomingDateTime,
             reset_schedule.UpcomingTime,
             reset_schedule.NextDateTime,
             reset_schedule.NextTime,
             reset_schedule.Expired);
  }
}

26.6. Example: Checking whether rankings have been reset

This example shows how to check whether the leaderboard has been reset.

26.6.1. Synchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void Example() {
    const string leaderboard_id = "player_score";
    LeaderboardResetSchedule reset_schedule;
    bool success = GetLeaderboardResetSchedule(leaderboard_id,
        LeaderboardResetSchedule::kWeek, &reset_schedule);
    if (not success) {
      return;
    }

    LeaderboardResetScheduleStatusQueryRequest request(
        leaderboard_id,
        LeaderboardResetSchedule::kWeek,
        reset_schedule.latest_reset_date_time);

    LeaderboardResetScheduleStatusQueryResponse response;
    success = GetLeaderboardResetScheduleStatusQuerySync(request, &response);
    if (not success) {
      return;
    }

    LOG(INFO) << "result: " << response.result
              << ", is_reset: " << response.is_reset
              << ", reset_date_time: " << response.reset_date_time;
  }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static void Example()
{
  string leaderboard_id = "player_score";
  Leaderboard.ResetSchedule reset_schedule;
  if (!Leaderboard.GetResetSchedule (leaderboard_id,
                                     Leaderboard.ResetSchedulePeriod.kWeek,
                                     out reset_schedule))
  {
    return;
  }

  Leaderboard.ResetScheduleStatusQueryRequest request =
      new Leaderboard.ResetScheduleStatusQueryRequest (
          leaderboard_id,
          Leaderboard.ResetSchedulePeriod.kWeek,
          reset_schedule.LatestResetDateTime);

  Leaderboard.ResetScheduleStatusQueryResponse response;
  if (!Leaderboard.GetResetScheduleStatusQuerySync (request, out response))
  {
    return;
  }

  Log.Info("is_reset: {0}, reset_date_time: {1}",
           response.IsReset,
           response.ResetDateTime);
}

26.6.2. Asynchronous approach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void OnResponse(const LeaderboardResetScheduleStatusQueryRequest &request,
                const LeaderboardResetScheduleStatusQueryResponse &response,
                const bool &error) {
  if (error) {
    return;
  }

  LOG(INFO) << "result: " << response.result
            << ", is_reset: " << response.is_reset
            << ", reset_date_time: " << response.reset_date_time;
}


void Example() {
    const string leaderboard_id = "player_score";
    LeaderboardResetSchedule reset_schedule;
    bool success = GetLeaderboardResetSchedule(leaderboard_id,
        LeaderboardResetSchedule::kWeek, &reset_schedule);
    if (not success) {
      return;
    }

    LeaderboardResetScheduleStatusQueryRequest request(
        leaderboard_id,
        LeaderboardResetSchedule::kWeek,
        reset_schedule.latest_reset_date_time);

    LeaderboardResetScheduleStatusQueryResponseHandler handler = OnResponse;
    GetLeaderboardResetScheduleStatusQuery(request, handler);
  }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public static void OnResetScheduleStatusReceived(
    Leaderboard.ResetScheduleStatusQueryRequest request,
    Leaderboard.ResetScheduleStatusQueryResponse response,
    bool error)
{
  if (error) {
    return;
  }
  Log.Info("is_reset: {0}, reset_date_time: {1}",
           response.IsReset,
           response.ResetDateTime);
}


public static void Example()
{
  string leaderboard_id = "player_score";
  Leaderboard.ResetSchedule reset_schedule;
  if (!Leaderboard.GetResetSchedule (leaderboard_id,
                                     Leaderboard.ResetSchedulePeriod.kWeek,
                                     out reset_schedule))
  {
    return;
  }

  Leaderboard.ResetScheduleStatusQueryRequest request =
      new Leaderboard.ResetScheduleStatusQueryRequest (
          leaderboard_id,
          Leaderboard.ResetSchedulePeriod.kWeek,
          reset_schedule.LatestResetDateTime);

  Leaderboard.GetResetScheduleStatusQuery (
      request, OnResetScheduleStatusReceived);
}

26.7. Example: Distributing leaderboard requests using many iFun Leaderboard agents

If tags are used for various leaderboard servers entered through the MANIFEST.json Configuration, you can request ranking handling by designating a particular leaderboard agent.

There are two separate, independent leaderboard agents to handle guild rankings and user rankings in the example below.

MANIFEST.json:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
{
  ...
  "dependency": {
    ...

    "LeaderboardClient": {
      "use_leaderboard" : true,
      "leaderboard_agents": {
        "guild" : {
          "address": "192.168.0.50:12820"
        },
        "user": {
          "address": "192.168.0.100:12820"
        }
      }
    }
  }
  ...
}

Note

If a tag doesn’t need to be set up, you can enter “” (empty text string). “” (empty text string) is input as the default value for all function tag parameters. Therefore, even if no tag is input into the function, it can communicate with the leaderboard agent. i.e. “” (empty text string) also works as a tag form.

Guild ranking requests:

Now, let’s request the top 100 from the leaderboard agent handling guild rankings. Synchronous API is used for the sake of this simple example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const string kGuildTag("guild");

void GetGuildRanking() {
  LeaderboardQueryRequest request("guild_ranking", kWeekly,
      LeaderboardRange(LeaderboardRange::kFromTop, 0, 99));

  LeaderboardQueryResponse response;
  if (not GetLeaderboardSync(request, &response, kGuildTag)) {
    return;
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
static string kGuildTag = "guild";

void GetGuildRanking()
{
  Leaderboard.QueryRequest request = new Leaderboard.QueryRequest(
      "guild_ranking",
      Leaderboard.Timespan.kWeekly,
      new Leaderboard.Range(Leaderboard.RangeType.kFromTop, 0, 99));

  Leaderboard.QueryResponse response;
  if (!Leaderboard.GetLeaderboardSync(request, out response, kGuildTag))
  {
    return;
  }
}

User ranking requests:

This time, let’s request the top 100 from the leaderboard agent handling user rankings. Synchronous API is used for the sake of this simple example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const string kUserTag("user");

void GetUserRanking() {
  LeaderboardQueryRequest request("user_ranking", kWeekly,
      LeaderboardRange(LeaderboardRange::kFromTop, 0, 99));

  LeaderboardQueryResponse response;
  if (not GetLeaderboardSync(request, &response, kUserTag)) {
    return;
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
static string kUserTag = "user";

void GetUserRanking()
{
  Leaderboard.QueryRequest request = new Leaderboard.QueryRequest(
      "user_ranking",
      Leaderboard.Timespan.kWeekly,
      new Leaderboard.Range(Leaderboard.RangeType.kFromTop, 0, 99));

  Leaderboard.QueryResponse response;
  if (!Leaderboard.GetLeaderboardSync(request, out response, kUserTag))
  {
    return;
  }
}

26.8. Game server leaderboard parameters

Note

These parameters are iFun Engine game server values using iFun Leaderboard. Please see Setting up iFun Leaderboard (MANIFEST.json) for parameters for iFun Leaderboard itself.

Refer to MANIFEST.JSON File and the explanation below to learn about leaderboard client components.

  • use_leaderboard: Enables communication with iFun Leaderboard agent. If false, runs in test mode and treats all ranking requests as dummy values. (type=bool, default=false)

  • leaderboard_agents: Enter the connection data for leaderboard agents to connect with in the following format.

    "leaderboard_agents": {
      // iFun Leaderboard agent를 tag 로 구분해서 각각의 접속 정보를 입력합니다.
      "<tag>": {
        // iFun Leaderboard 의 MANIFEST.json 에 기술된 server_tcp_port 를 입력합니다.
        "address": "127.0.0.1:12820"
      },
      ...
    }
    

    Note

    Use tags to request handling of rankings from a particular iFun Leaderboard agent. For more details, please see Example: Distributing leaderboard requests using many iFun Leaderboard agents.

    If no tag is input, you can enter “” (empty text string) Tags including “” cannot be duplicated.