게임의 플레이어 기반이 성장함에 따라 온라인 게임에서 사회적 상호 작용의 근본적인 문제가 나타납니다. 글로벌 순위표와 일반적으로 순위표는 특정 기술이나 점수 수준에서 거의 움직이지 않는 정적인 상태가 됩니다. 이로 인해 새로운 플레이어의 참여가 없거나 부족하여 게임이 계속되지 않습니다. 이에 대한 한 가지 해결책은 버킷으로 구성된 순위표를 만드는 것입니다.
버킷으로 구성된 순위표에서 플레이어는 다른 모든 플레이어와 게임을 하지 않고 대신 다른 플레이어(대체로 25~50명)의 제한된 보기를 봅니다. 이들은 순위표가 만료되거나 다른 시작 시간으로 이월될 때까지 이 그룹과 경쟁합니다. 이러한 소규모 플레이어 그룹은 버킷으로 구성된 순위표 이름이 유래된 “플레이어 버킷” 또는 코호트로 알려져 있습니다.
버킷으로 구성된 순위표는 Rovio의 Angry Birds 프랜차이즈에 있는 여러 타이틀을 포함하여 많은 인기 게임에서 볼 수 있습니다.
Nakama의 저장소 엔진, 순위표 및 토너먼트 기능을 사용하면 서버 런타임 코드, 특히 RPC 함수를 사용하여 게임에서 버킷으로 구성된 순위표를 구현할 수 있습니다.
Nakama 순위표 API를 사용하면 이미 해당 플레이어만 포함된 순위표의 “버킷 보기"를 생성하는 데 사용되는 필터가 되는 일련의 사용자 ID(또는 사용자 이름)를 전달할 수 있습니다.
이 경우 특정 플레이어에 대해 설정된 사용자 ID를 구성하는 방법과 개인화되거나 다른 게임 기준을 따르는지 여부가 결정되어야 합니다. 이것은 구현 성공의 핵심이며 게임의 특정 메커니즘에 따라 다릅니다. 비슷한 순위의 플레이어, VIP 플레이어, 사용자의 친구, 같은 지역의 플레이어로 구성된 “보기"가 필요할 수 있습니다.
여기의 예에서는 특정 플레이어에게 표시되는 버킷으로 사용자의 “임의” 선택을 사용하겠습니다. 먼저 코드의 주요 부분을 살펴보고 마지막에 참조용으로 전체 파일을 제공하겠습니다.
funcInitModule(ctxcontext.Context,loggerruntime.Logger,db*sql.DB,nkruntime.NakamaModule,initializerruntime.Initializer)error{// Set up the bucketed leaderboards tournament
id:="bucketed_weekly"authoritative:=truesortOrder:="desc"operator:="incr"resetSchedule:="0 0 * * 0"metadata:=map[string]interface{}{}title:="Bucketed Weekly #1"description:=""category:=1startTime:=0endTime:=0duration:=604800maxSize:=100000000maxNumScore:=100000000joinRequired:=falseiferr:=nk.TournamentCreate(ctx,id,authoritative,sortOrder,operator,resetSchedule,metadata,title,description,category,startTime,endTime,duration,maxSize,maxNumScore,joinRequired);err!=nil{returnerr}iferr:=initializer.RegisterRpc("get_bucket_records",rpcGetBucketRecordsFn([]string{id},2000));err!=nil{returnerr}returnnil}
// Get a user's bucket (records) and generate a new bucket if needed
funcRpcGetBucketRecordsFn(ids[]string,bucketSizeint)func(context.Context,runtime.Logger,*sql.DB,runtime.NakamaModule,string)(string,error){returnfunc(ctxcontext.Context,loggerruntime.Logger,db*sql.DB,nkruntime.NakamaModule,payloadstring)(string,error){iflen(payload)>0{return"",ErrNoInputAllowed}userID,ok:=ctx.Value(runtime.RUNTIME_CTX_USER_ID).(string)if!ok{return"",ErrNoUserIdFound}collection:="buckets"key:="bucket"objects,err:=nk.StorageRead(ctx,[]*runtime.StorageRead{{Collection:collection,Key:key,UserID:userID,},})iferr!=nil{logger.Error("nk.StorageRead error: %v",err)return"",ErrInternalError}// Fetch any existing bucket or create one if none exist
userBucket:=&userBucketStorageObject{ResetTimeUnix:0,UserIDs:[]string{}}iflen(objects)>0{iferr:=json.Unmarshal([]byte(objects[0].GetValue()),userBucket);err!=nil{logger.Error("json.Unmarshal error: %v",err)return"",ErrUnmarshal}}// Fetch the leaderboard
leaderboards,err:=nk.LeaderboardsGetId(ctx,ids)iferr!=nil{logger.Error("nk.LeaderboardsGetId error: %v",err)return"",ErrInternalError}
계속 진행하기 전에 순위표가 재설정되었는지 또는 상대방이 없는지 확인합니다. 어느 경우든 새로운 상대방 세트를 생성해야 합니다.
Server
1
2
3
4
5
6
// Leaderboard has reset or no current bucket exists for user
ifuserBucket.ResetTimeUnix!=leaderboards[0].GetPrevReset()||len(userBucket.UserIDs)<1{logger.Debug("rpcGetBucketRecordsFn new bucket for %q",userID)// Code below goes here...
}
Server
1
2
3
4
5
6
// Leaderboard has reset or no current bucket exists for user
if(userBucket.resetTimeUnix!=leaderboards[0].endActive||userBucket.userIds.length<1){logger.debug(`RpcGetBucketRecordsFn new bucket for ${ctx.userId}`);// Code below goes here
}
Code snippet for this language Lua has not been found. Please choose another language to show equivalent examples.
임의 상대방 목록을 생성하기 위해 Nakama 3.5.0에서 사용할 수 있는 UsersGetRandom 함수를 사용합니다.
Server
1
2
3
4
5
6
7
8
9
10
11
12
userBucket.UserIDs=nillogger.Debug("rpcGetBucketRecordsFn new bucket for %q",userID)users,err:=nk.UsersGetRandom(ctx,bucketSize)iferr!=nil{logger.Error("Error getting random users.")return"",ErrInternalError}for_,user:=rangeusers{userBucket.UserIDs=append(userBucket.UserIDs,user.Id)}
// Set the Reset and Bucket end times to be in sync
userBucket.ResetTimeUnix=leaderboards[0].GetNextReset()value,err:=json.Marshal(userBucket)iferr!=nil{return"",ErrMarshal}// Store generated bucket for the user
if_,err:=nk.StorageWrite(ctx,[]*runtime.StorageWrite{{Collection:collection,Key:key,PermissionRead:0,PermissionWrite:0,UserID:userID,Value:string(value),},});err!=nil{logger.Error("nk.StorageWrite error: %v",err)return"",ErrInternalError}
Server
1
2
3
4
5
6
7
8
9
10
11
12
// Set the Reset and Bucket end times to be in sync
userBucket.resetTimeUnix=leaderboards[0].endActive;// Store generated bucket for the user
nk.storageWrite([{collection,key,userId: ctx.userId,value: userBucket,permissionRead: 0,permissionWrite: 0}]);
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
// Add self to the list of leaderboard records to fetch
userBucket.UserIDs=append(userBucket.UserIDs,userID)_,records,_,_,err:=nk.LeaderboardRecordsList(ctx,ids[0],userBucket.UserIDs,bucketSize,"",0)iferr!=nil{logger.Error("nk.LeaderboardRecordsList error: %v",err)return"",ErrInternalError}result:=&api.LeaderboardRecordList{Records:records}encoded,err:=json.Marshal(result)iferr!=nil{return"",ErrMarshal}logger.Debug("rpcGetBucketRecordsFn resp: %s",encoded)returnstring(encoded),nil
Server
1
2
3
4
5
6
7
8
9
10
// Add self to the list of leaderboard records to fetch
userBucket.userIds.push(ctx.userId);// Get the leaderboard records
constrecords=nk.tournamentRecordsList(ids[0],userBucket.userIds,bucketSize);constresult=JSON.stringify(records);logger.debug(`RpcGetBucketRecordsFn resp: ${result}`);returnJSON.stringify(records);
Code snippet for this language Lua has not been found. Please choose another language to show equivalent examples.