친구 및 그룹 순위표 #

Nakama 순위표를 통해 플레이어는 경쟁 활동에 참여할 수 있습니다. 게임이 진행됨에 따라, 플레이어가 순위를 높이기 어려워지기 때문에 순위표가 고정된 것처럼 느껴질 수 있습니다. 다음을 통해 문제를 해결할 수 있습니다:

순위표 목록 API를 사용하여 사용자 ID 목록을 전달하여 사용자 지정 순위표 보기를 생성할 수 있습니다.

다음의 코드 샘플은 그룹의 구성원 또는 사용자의 친구에 대한 순위표 레코드를 얻을 수 있는 방법에 대해서 설명합니다.

순위표 생성하기 #

매주 월요일 00:00에 재설정되는 순위표를 서버에 생성합니다.

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func InitModule(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, initializer runtime.Initializer) error {
  // Create a weekly leaderboard
  id := "weekly_leaderboard"
  authoritative := false
  sortOrder := "desc"
  operator := "incr"
  resetSchedule := "0 0 * * 1"
  metadata := make(map[string]interface{})

  if err := nk.LeaderboardCreate(ctx, id, authoritative, sortOrder, operator, resetSchedule, metadata); err != nil {
    logger.Error("error creating leaderboard")
    return err
  }
}
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let InitModule: nkruntime.InitModule = function (ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, initializer: nkruntime.Initializer) {
  const id = 'weekly_leaderboard';
  const authoritative = false;
  const sortOrder = nkruntime.SortOrder.DESCENDING;
  const operator = nkruntime.Operator.INCREMENTAL;
  const resetSchedule = '0 0 * * 1';
  const metadata = {};

  nk.leaderboardCreate(id, authoritative, sortOrder, operator, resetSchedule, metadata);
}
Code snippet for this language Lua has not been found. Please choose another language to show equivalent examples.

순위표에 대한 사용자 지정 보기 #

RPC에 대한 페이로드 구조를 정의합니다:

Server
1
2
3
4
5
6
type leaderboardRecord struct {
  Username string `json:"username"`
  UserId   string `json:"userId"`
  Score    int    `json:"score"`
  Rank     int    `json:"rank"`
}
Server
1
2
3
4
5
6
interface LeaderboardRecord {
  username: string,
  userId: string,
  score: number,
  rank: number
}
Code snippet for this language Lua has not been found. Please choose another language to show equivalent examples.

일련의 사용자 ID를 사용하여 일련의 레코드와 함께 사용자 점수를 기반으로 상대 순위값을 반환하는 도우미 기능을 생성합니다.

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
func getLeaderboardForUsers(leaderboardId string, userIds []string, ctx context.Context, logger runtime.Logger, nk runtime.NakamaModule) ([]leaderboardRecord, error) {
  // Get all leaderboard records for user Ids
  _, records, _, _, err := nk.LeaderboardRecordsList(ctx, leaderboardId, userIds, 0, "", 0)
  if err != nil {
    return nil, err
  }

  // Create result slice and add a rank value
  results := []leaderboardRecord{}
  for i, record := range records {
    r := leaderboardRecord{
      Username: record.Username.Value,
      UserId:   record.OwnerId,
      Score:    int(record.Score),
      Rank:     len(records) - i,
    }
    results = append(results, r)
  }

  return results, nil
}
Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const getLeaderboardForUsers = function (leaderboardId: string, userIds: string[], logger: nkruntime.Logger, nk: nkruntime.Nakama): LeaderboardRecord[] {
  const recordsList = nk.leaderboardRecordsList(leaderboardId, userIds);
  const results: LeaderboardRecord[] = [];

  recordsList.records.forEach(function (leaderboardRecord, i) {
    results.push({
      username: leaderboardRecord.username,
      userId: leaderboardRecord.ownerId,
      score: leaderboardRecord.score,
      rank: recordsList.records.length - i
    });
  });

  return results;
}
Code snippet for this language Lua has not been found. Please choose another language to show equivalent examples.

그룹 순위표 보기 #

일반적인 기능을 통해 사용자 목록에 대한 순위표를 얻을 수 있기 때문에 그룹에 대한 순위표를 얻을 수 있는 기능을 생성합니다.

RPC에 대한 페이로드 구조를 정의합니다:

Server
1
2
3
4
type groupLeaderboardRecordsPayload struct {
  GroupId       string `json:"groupId"`
  LeaderboardId string `json:"leaderboardId"`
}
Server
1
2
3
4
interface groupLeaderboardRecordsPayload {
  groupId: string,
  leaderboardId: string
}
Code snippet for this language Lua has not been found. Please choose another language to show equivalent examples.

그룹에서 구성원의 목록과 순위표를 얻을 수 있는 기능을 생성합니다:

Server
 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
func getGroupLeaderboardRecords(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
  // Unmarshal the payload
  data := &groupLeaderboardRecordsPayload{}
  if err := json.Unmarshal([]byte(payload), data); err != nil {
    logger.Error("error unmarshaling payload")
    return "", err
  }

  // Get leaderboard
  leaderboards, err := nk.LeaderboardsGetId(ctx, []string{data.LeaderboardId})
  if err != nil {
    logger.Error("error getting leaderboards")
    return "", err
  }

  if len(leaderboards) == 0 {
    errorMessage := fmt.Sprintf("error finding leaderboard: %s", data.LeaderboardId)
    logger.Error(errorMessage)
    return "", errors.New(errorMessage)
  }

  // Get group members
  members, _, err := nk.GroupUsersList(ctx, data.GroupId, 100, nil, "")
  if err != nil {
    logger.Error("error getting group members")
    return "", err
  }

  // Get a slice of memberIds
  memberIds := []string{}
  for _, member := range members {
    memberIds = append(memberIds, member.User.Id)
  }

  // Get all leaderboard records for users
  results, err := getLeaderboardForUsers(leaderboards[0].Id, memberIds, ctx, logger, nk)
  if err != nil {
    logger.Error("error getting leaderboard records")
    return "", err
  }

  // Return the leaderboard records to the user
  bytes, err := json.Marshal(results)
  if err != nil {
    logger.Error("error marshaling result")
    return "", err
  }

  return string(bytes), nil
}
Server
 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 getGroupLeaderboardRecords: nkruntime.RpcFunction = function (ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, payload: string): string | void {
  // Parse the payload
  const data = <groupLeaderboardRecordsPayload> JSON.parse(payload);
  
  if (!data) {
    throw new Error('Error parsing payload');
  }
  
  // Get leaderboard
  const leaderboards = nk.leaderboardsGetId([data.leaderboardId]);
  
  if (leaderboards.length == 0) {
    throw new Error(`Error finding leaderboard: ${data.leaderboardId}`);
  }
  
  // Get group members
  const members = nk.groupUsersList(data.groupId, 100);
  
  // Get a slice of memberIds
  const memberIds: string[] = [];
  members.groupUsers.forEach(function (groupUser) {
    memberIds.push(groupUser.user.userId);
  });
  
  // Get all leaderboard records for users
  const results = getLeaderboardForUsers(leaderboards[0].id, memberIds, logger, nk);
  
  // Return the leaderboard records to the user
  return JSON.stringify(results);
};
Code snippet for this language Lua has not been found. Please choose another language to show equivalent examples.

기능을 등록하고 클라이언트에서 호출할 수 있는 원격 프로시저로 노출시킵니다:

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func InitModule(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, initializer runtime.Initializer) error {
  // ...

  // Register RPC to retrieve a custom view over a leaderboard based on group members
  if err := initializer.RegisterRpc("getGroupLeaderboardRecords", getGroupLeaderboardRecords); err != nil {
    logger.Error(`error registering "getGroupLeaderboardRecords" rpc`)
    return err
  }

  return nil
}
Server
1
2
3
4
5
6
let InitModule: nkruntime.InitModule = function (ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, initializer: nkruntime.Initializer) {
  // ... 

  // Register RPC to retrieve a custom view over a leaderboard based on group members
  initializer.registerRpc('getGroupLeaderboardRecords', getGroupLeaderboardRecords);
}
Code snippet for this language Lua has not been found. Please choose another language to show equivalent examples.

친구 순위표 보기 #

이와 유사하게, 사용자의 친구에 대한 순위표를 얻을 수 있는 기능을 생성합니다.

RPC에 대한 페이로드 구조를 정의합니다:

Server
1
2
3
type friendsLeaderboardRecordsPayload struct {
  LeaderboardId string `json:"leaderboardId"`
}
Server
1
2
3
interface friendsLeaderboardReocrdsPayload {
  leaderboardId: string
}
Code snippet for this language Lua has not been found. Please choose another language to show equivalent examples.

사용자의 친구 목록과 순위표를 얻을 수 있는 기능을 생성합니다:

Server
 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
func getFriendLeaderboardRecords(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
  // Unmarshal the payload
  data := &friendsLeaderboardRecordsPayload{}
  if err := json.Unmarshal([]byte(payload), data); err != nil {
    logger.Error("error unmarshaling payload")
    return "", err
  }

  // Get leaderboard
  leaderboards, err := nk.LeaderboardsGetId(ctx, []string{data.LeaderboardId})
  if err != nil {
    logger.Error("error getting leaderboards")
    return "", err
  }

  if len(leaderboards) == 0 {
    errorMessage := fmt.Sprintf("error finding leaderboard: %s", data.LeaderboardId)
    logger.Error(errorMessage)
    return "", errors.New(errorMessage)
  }

  // Get user id from context
  userId, ok := ctx.Value(runtime.RUNTIME_CTX_USER_ID).(string)
  if !ok {
    errorMessage := fmt.Sprintf("error getting user id from context")
    logger.Error(errorMessage)
    return "", errors.New(errorMessage)
  }

  // Get friends (where state is 0 - mutual friends)
  state := 0
  friends, _, err := nk.FriendsList(ctx, userId, 100, &state, "")
  if err != nil {
    logger.Error("error getting friends")
    return "", err
  }

  // Get a slice of memberIds
  friendIds := []string{}
  for _, member := range friends {
    friendIds = append(friendIds, member.User.Id)
  }

  // Get all leaderboard records for users
  results, err := getLeaderboardForUsers(leaderboards[0].Id, friendIds, ctx, logger, nk)
  if err != nil {
    logger.Error("error getting leaderboard records")
    return "", err
  }

  // Return the leaderboard records to the user
  bytes, err := json.Marshal(results)
  if err != nil {
    logger.Error("error marshaling result")
    return "", err
  }

  return string(bytes), nil
}
Server
 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 getFriendLeaderboardRecords: nkruntime.RpcFunction = function (ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, payload: string): string | void {
  // Parse the payload
  const data = <friendsLeaderboardReocrdsPayload> JSON.parse(payload);

  if (!data) {
    throw new Error('Error parsing payload');
  }

  // Get leaderboard
  const leaderboards = nk.leaderboardsGetId([data.leaderboardId]);

  if (leaderboards.length == 0) {
    throw new Error(`Error finding leaderboard: ${data.leaderboardId}`);
  }

  // Get friends (where state is 0 - mutual friends)
  const friendsList = nk.friendsList(ctx.userId, 100, 0);

  // Get a slice of friendIds
  const friendIds: string[] = [];
  friendsList.friends.forEach(function (friend) {
    friendIds.push(friend.user.userId);
  });

  // Get all leaderboard records for users
  const results = getLeaderboardForUsers(leaderboards[0].id, friendIds, logger, nk);

  // Return the leaderboard records to the user
  return JSON.stringify(results);
};
Code snippet for this language Lua has not been found. Please choose another language to show equivalent examples.

기능을 등록하고 클라이언트에서 호출할 수 있는 원격 프로시저로 노출시킵니다:

Server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func InitModule(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, initializer runtime.Initializer) error {
  // ...

  // Register RPC to retrieve a custom view over a leaderboard based on friends
  if err := initializer.RegisterRpc("getFriendLeaderboardRecords", getFriendLeaderboardRecords); err != nil {
    logger.Error(`error registering "getFriendLeaderboardRecords" rpc`)
    return err
  }

  return nil
}
Server
1
2
3
4
5
6
let InitModule: nkruntime.InitModule = function (ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, initializer: nkruntime.Initializer) {
  // ...

  // Register RPC to retrieve a custom view over a leaderboard based on friends
  initializer.registerRpc('getFriendLeaderboardRecords', getFriendLeaderboardRecords);
}
Code snippet for this language Lua has not been found. Please choose another language to show equivalent examples.