> For the complete documentation index, see [llms.txt](/llms.txt)

# GS2-Ranking2

랭킹 기능



**Tip**

GS2-Ranking2 는 이름 그대로 버전2에 해당하는 마이크로서비스입니다.

이전 버전에 관한 문서는 [GS2-Ranking]() 을 참조하세요.

게임의 점수나 클리어 타임을 겨루는 랭킹 기능을 실현합니다.

GS2-Ranking2 는 다음 3가지 종류의 모드를 제공합니다.

- 글로벌 랭킹
- 클러스터 랭킹
- 구독 랭킹

게임에 필요한 랭킹 기능의 대부분은 이 중 하나의 모드로 요건을 충족할 수 있을 것입니다.

## 랭킹 모드

### 글로벌 랭킹

글로벌 랭킹은 모든 플레이어와 경쟁하기 위한 랭킹 기능을 제공합니다.
GS2-Ranking2 에서는 상위 1000위 플레이어만 랭킹에 참가할 수 있으며, 1000위 이하 플레이어의 점수는 전송하더라도 랭킹에 등록되지 않습니다.

이전 버전인 GS2-Ranking 이 1억 명이 넘는 플레이어의 정확한 순위를 반환할 수 있는 형태로 제공되었기 때문에, 이전 버전과 비교해 사양이 크게 변경되었습니다.
GS2-Ranking 에서는 많은 점수를 다룰 수 있는 대신, 15분~24시간 범위에서 지정한 집계 간격으로 집계가 이루어질 때까지 랭킹 등록이 이루어지지 않는 사양으로, 집계할 때마다 참가 인원 수에 따른 비용이 발생했습니다.

GS2-Ranking 을 이용한 개발자들의 피드백을 받아 GS2-Ranking2 는 재설계되었습니다.
구체적으로 다음과 같은 사항을 중요한 피드백으로 받아들여 재설계했습니다.
- 대부분의 사용 사례에서 상위 플레이어만 표시할 수 있으면 충분하다
- 순위 변화를 즉시 랭킹에 반영하고 싶다
- 플레이어 증가에 따른 랭킹 집계 비용 증가는 바람직하지 않다

그 결과, 앞서 설명한 대로 상위 1000명의 플레이어만 랭킹에 참가할 수 있고, 1001위 이하의 플레이어는 "순위권 밖"으로 처리하는 구조가 되었습니다.
대신 점수 등록 직후 바로 랭킹에 반영되며, 일반적인 API 요청 비용 이상의 추가 비용은 발생하지 않습니다.

### 클러스터 랭킹

대체로 글로벌 랭킹과 동일한 사양이지만, 유일하게 다른 점은 클러스터ID별로 서로 다른 랭킹이 생성된다는 점입니다.

클러스터ID에 GS2-Guild 의 길드ID를 지정함으로써 길드 멤버끼리 경쟁하는 랭킹을 구현하는 데 사용할 수 있습니다.

### 클러스터 참가 판정

클러스터 랭킹에서는 클러스터의 종류를 정의해 두면 점수 등록 시 클러스터에 참가하고 있는지 확인한 후 점수 등록을 실행할 수 있습니다.

예를 들어 GS2-Guild 의 길드를 클러스터로 하는 랭킹을 구현하는 경우, 랭킹 모드 설정에서 클러스터 종류에 "Gs2Guild::Guild"를 지정하면
점수 등록 시 클러스터ID로 지정된 길드에 점수를 등록하려는 플레이어가 멤버로 등록되어 있는지 확인한 후 점수를 등록하도록 할 수 있습니다.

### 구독 랭킹

GS2-Ranking 의 스코프 랭킹과 유사한 사양의 랭킹 기능입니다.
다른 플레이어를 구독함으로써 자신의 랭킹 보드에 다른 플레이어의 최신 점수를 포함시킬 수 있습니다.

친구 간 랭킹처럼 플레이어 간 비대칭성이 강한 랭킹을 구현하기 위해 사용합니다.

### 구독 랭킹에서의 반영 지연

점수 등록을 실행하면, 해당 플레이어를 구독하고 있는 플레이어의 랭킹에 비동기로 점수 등록이 실행됩니다.
이 처리는 일반적으로 1초 이내에 실행되지만, 비동기 처리이기 때문에 점수가 반영될 때까지 약간의 지연이 발생합니다.

## 시즌

각 랭킹에는 점수 등록을 받는 기간으로 GS2-Schedule 의 이벤트를 연결할 수 있습니다.
GS2-Schedule 의 이벤트에는 반복 설정이 가능하며, 각 랭킹은 이벤트가 반복될 때마다 초기화됩니다.

이 기능을 구현하기 위해 GS2-Ranking2 는 각 랭킹에 `시즌` 이라는 속성을 가지고 있습니다.
랭킹 결과는 시즌별로 저장되며, 과거 시즌의 결과를 언제든지 참조할 수 있습니다.

## 랭킹 보상

글로벌 랭킹·클러스터 랭킹에서는 랭킹 순위 보상을 설정할 수 있습니다.
보상을 설정하려면 `순위 임계값` 과 `보상 내용` 을 설정합니다.

임계값에 3을 지정하면 1, 2, 3위 플레이어에게 보상이 지급되고,
이어서 10을 지정하면 4, 5, 6, 7, 8, 9, 10위 플레이어에게 보상을 설정할 수 있습니다.

### 순위권 밖 플레이어에 대한 보상

1001을 임계값으로 지정하면 순위권 밖 플레이어에 대한 보상을 설정할 수 있습니다.
1001을 임계값으로 하는 랭킹 보상 설정은 선택 사항이며, 지정하지 않은 경우 순위권 밖 플레이어는 보상을 받을 수 없습니다.

### 과거 시즌 보상 수령

과거 시즌의 랭킹 보상은 보상 수령 API에 과거 시즌 번호를 지정하여 호출함으로써 언제든지 받을 수 있습니다.

## 점수의 유효 범위

점수로 등록을 허용할 값의 범위를 설정할 수 있습니다.
이를 통해 명백히 부적절한 점수 등록이 있을 때 등록 처리를 하지 않고 폐기할 수 있습니다.

## 기간 설정

### 점수 등록 가능 기간

점수 등록을 받는 기간 설정으로 GS2-Schedule 의 이벤트를 연결할 수 있습니다.
점수 접수 기간 외에 점수를 전송해도 점수는 폐기됩니다.

### 랭킹 데이터 접근 가능 기간

랭킹 데이터에 접근할 수 있는 기간으로 GS2-Schedule 의 이벤트를 연결할 수 있습니다.
이벤트 종료 후에는 점수 조회도 할 수 없게 하는 경우 등에 활용할 수 있습니다.

## 트랜잭션 액션

GS2-Ranking2 에서는 다음과 같은 트랜잭션 액션을 제공합니다.

- 검증 액션: 글로벌·클러스터·구독 각 랭킹의 점수 검증
- 소비 액션: 랭킹 보상 수령 이력 기록

"랭킹 점수 검증"을 검증 액션으로 이용함으로써, 특정 점수 이상을 기록한 플레이어만 구매할 수 있는 상품이나 도전할 수 있는 퀘스트와 같은 제한을 트랜잭션 내에서 안전하게 설정할 수 있습니다. 이를 통해 상위 랭커 한정의 특별 보상 획득 이벤트 등을 확실하게 구현할 수 있습니다.

## 마스터 데이터 운용
마스터 데이터를 등록함으로써 마이크로서비스에서 사용할 수 있는 데이터나 동작을 설정할 수 있습니다.

마스터 데이터의 종류에는 다음이 있습니다.

- `RankingModel`: 랭킹 모드나 보상 임계값 설정

마스터 데이터 등록은 관리 콘솔에서 등록하는 것 외에도, GitHub에서 데이터를 반영하거나 GS2-Deploy를 사용해 CI에서 등록하는 워크플로우를 구성할 수 있습니다.

## 구현 예제

### 점수 등록

이 API는 편의성 관점에서 ApplicationAccess 로 호출할 수 있게 되어 있습니다.
그러나 임의의 점수로 전송할 수 있다는 것은 취약점이 됩니다.

따라서 가능하다면 이 API를 클라이언트에서 호출할 수 없도록 설정하고, 신뢰할 수 있는 발신처로부터만 점수 등록을 받을 수 있도록 해야 합니다.

예를 들어 아이템 소지 수량 랭킹을 구현하고 싶다면, GS2-Inventory 의 아이템 획득 시 트리거되는 스크립트에서 점수로 아이템 소지 수량을 등록하는 편이 더 안전하게 처리할 수 있습니다.

#### 글로벌 랭킹



**Unity**
```csharp

    var result = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).GlobalRankingModel(
        rankingName: "ranking-0001"
    ).GlobalRankingSeason(
        gameSession: GameSession,
        season: null // current season
    ).PutGlobalRankingAsync(
        score: 100L,
        metadata: null
    );
    var item = await result.ModelAsync();
```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->GlobalRankingModel(
        "ranking-0001" // rankingName
    )->GlobalRankingSeason(
        GameSession,
        TOptional<int64>() // current season
    )->PutGlobalRanking(
        100L, // score
        TOptional<FString>() // metadata
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();
```
**Godot**
```gdscript

var domain = ez.ranking2.namespace_(
        "namespace-0001"
    ).global_ranking_model(
        "ranking-0001"
    ).global_ranking_season(
        0,
        null
    )

var async_result = await domain.put_global_ranking(
    100, # score
    null # metadata
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


#### 클러스터 랭킹



**Unity**
```csharp

    var result = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).ClusterRankingModel(
        rankingName: "ranking-0001"
    ).ClusterRankingSeason(
        clusterName: "cluster-0001",
        gameSession: GameSession,
        season: null // current season
    ).PutClusterRankingAsync(
        score: 100L,
        metadata: null
    );
    var item = await result.ModelAsync();

```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->ClusterRankingModel(
        "ranking-0001" // rankingName
    )->ClusterRankingSeason(
        "cluster-0001", // clusterName
        GameSession,
        TOptional<int64>() // current season
    )->PutClusterRanking(
        100L, // score
        TOptional<FString>() // metadata
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```
**Godot**
```gdscript

var domain = ez.ranking2.namespace_(
        "namespace-0001"
    ).cluster_ranking_model(
        "ranking-0001"
    ).cluster_ranking_season(
        "cluster-0001",
        0,
        null
    )

var async_result = await domain.put_cluster_ranking(
    100, # score
    null # metadata
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


#### 구독 랭킹



**Unity**
```csharp

    var result = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).SubscribeRankingModel(
        rankingName: "ranking-0001"
    ).SubscribeRankingSeason(
        gameSession: GameSession,
        season: null // current season
    ).PutSubscribeRankingAsync(
        score: 100L,
        metadata: null
    );
    var item = await result.ModelAsync();

```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->SubscribeRankingModel(
        "ranking-0001" // rankingName
    )->SubscribeRankingSeason(
        GameSession,
        TOptional<int64>() // current season
    )->PutSubscribeRanking(
        100L, // score
        TOptional<FString>() // metadata
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```
**Godot**
```gdscript

var domain = ez.ranking2.namespace_(
        "namespace-0001"
    ).subscribe_ranking_model(
        "ranking-0001"
    ).subscribe_ranking_season(
        0,
        null
    )

var async_result = await domain.put_subscribe_ranking(
    100, # score
    null # metadata
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


### 순위 조회

#### 글로벌 랭킹



**Unity**
```csharp

    var result = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).GlobalRankingModel(
        rankingName: "ranking-0001"
    ).GlobalRankingSeason(
        gameSession: GameSession,
        season: null // current season
    ).GetGlobalRankingRankAsync(
    );
    var item = await result.ModelAsync();

```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->GlobalRankingModel(
        "ranking-0001" // rankingName
    )->GlobalRankingSeason(
        GameSession,
        TOptional<int64>() // current season
    )->GetGlobalRankingRank(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```
**Godot**
```gdscript

var domain = ez.ranking2.namespace_(
        "namespace-0001"
    ).global_ranking_model(
        "ranking-0001"
    ).global_ranking_season(
        null,
        null
    ).global_ranking_data(
        null
    )

var async_result = await domain.get_global_ranking_rank(
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


#### 클러스터 랭킹



**Unity**
```csharp

    var result = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).ClusterRankingModel(
        rankingName: "ranking-0001"
    ).ClusterRankingSeason(
        clusterName: "cluster-0001",
        gameSession: GameSession,
        season: null // current season
    ).GetClusterRankingRankAsync(
    );
    var item = await result.ModelAsync();

```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->ClusterRankingModel(
        "ranking-0001" // rankingName
    )->ClusterRankingSeason(
        "cluster-0001", // clusterName
        GameSession,
        TOptional<int64>() // current season
    )->GetClusterRankingRank(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```
**Godot**
```gdscript

var domain = ez.ranking2.namespace_(
        "namespace-0001"
    ).cluster_ranking_model(
        "ranking-0001"
    ).cluster_ranking_season(
        "cluster-0001",
        null,
        null
    ).cluster_ranking_data(
        null
    )

var async_result = await domain.get_cluster_ranking_rank(
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


#### 구독 랭킹



**Unity**
```csharp

    var result = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).SubscribeRankingModel(
        rankingName: "ranking-0001"
    ).SubscribeRankingSeason(
        gameSession: GameSession,
        season: null // current season
    ).SubscribeRankingData(
        scorerUserId: "user-0001"
    ).GetSubscribeRankingRankAsync(
    );
    var item = await result.ModelAsync();

```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->SubscribeRankingModel(
        "ranking-0001" // rankingName
    )->SubscribeRankingSeason(
        GameSession,
        TOptional<int64>() // current season
    )->SubscribeRankingData(
        "user-0001" // scorerUserId
    )->GetSubscribeRankingRank(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```
**Godot**
```gdscript

var domain = ez.ranking2.namespace_(
        "namespace-0001"
    ).subscribe_ranking_model(
        "ranking-0001"
    ).subscribe_ranking_season(
        null,
        null
    ).subscribe_ranking_data(
        null
    )

var async_result = await domain.get_subscribe_ranking_rank(
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


### 랭킹 조회

#### 글로벌 랭킹



**Unity**
```csharp

    var items = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).GlobalRankingModel(
        rankingName: "ranking-0001"
    ).GlobalRankingSeason(
        gameSession: GameSession,
        season: null // current season
    ).GlobalRankingsAsync(
    ).ToListAsync();

```
**Unreal Engine**
```cpp

    const auto It = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->GlobalRankingModel(
        "ranking-0001" // rankingName
    )->GlobalRankingSeason(
        GameSession,
        TOptional<int64>() // current season
    )->GlobalRankings(
    );
    TArray<Gs2::UE5::Ranking2::Model::FEzGlobalRankingDataPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

```
**Godot**
```gdscript

var iterator = ez.ranking2.namespace_(
        "namespace-0001"
    ).global_ranking_model(
        "ranking-0001"
    ).global_ranking_season(
        game_session,
        null
    ).global_rankings(
    )

var async_result = await iterator.load()
if async_result.error != null:
    # 오류를 처리
    push_error(str(async_result.error))
    return

var items = async_result.result

```


#### 클러스터 랭킹



**Unity**
```csharp

    var items = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).ClusterRankingModel(
        rankingName: "ranking-0001"
    ).ClusterRankingSeason(
        clusterName: "cluster-0001",
        gameSession: GameSession,
        season: null // current season
    ).ClusterRankingsAsync(
    ).ToListAsync();


```
**Unreal Engine**
```cpp

    const auto It = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->ClusterRankingModel(
        "ranking-0001" // rankingName
    )->ClusterRankingSeason(
        "cluster-0001", // clusterName
        GameSession,
        TOptional<int64>() // current season
    )->ClusterRankings(
    );
    TArray<Gs2::UE5::Ranking2::Model::FEzClusterRankingDataPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }


```
**Godot**
```gdscript

var iterator = ez.ranking2.namespace_(
        "namespace-0001"
    ).cluster_ranking_model(
        "ranking-0001"
    ).cluster_ranking_season(
        "cluster-0001",
        game_session,
        null
    ).cluster_rankings(
    )

var async_result = await iterator.load()
if async_result.error != null:
    # 오류를 처리
    push_error(str(async_result.error))
    return

var items = async_result.result

```


#### 구독 랭킹



**Unity**
```csharp

    var items = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).SubscribeRankingModel(
        rankingName: "ranking-0001"
    ).SubscribeRankingSeason(
        gameSession: GameSession,
        season: null // current season
    ).SubscribeRankingsAsync(
    ).ToListAsync();


```
**Unreal Engine**
```cpp

    const auto It = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->SubscribeRankingModel(
        "ranking-0001" // rankingName
    )->SubscribeRankingSeason(
        GameSession,
        TOptional<int64>() // current season
    )->SubscribeRankings(
    );
    TArray<Gs2::UE5::Ranking2::Model::FEzSubscribeRankingDataPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }


```
**Godot**
```gdscript

var iterator = ez.ranking2.namespace_(
        "namespace-0001"
    ).subscribe_ranking_model(
        "ranking-0001"
    ).subscribe_ranking_season(
        game_session,
        null
    ).subscribe_rankings(
    )

var async_result = await iterator.load()
if async_result.error != null:
    # 오류를 처리
    push_error(str(async_result.error))
    return

var items = async_result.result

```


### 랭킹 보상 받기

랭킹 보상은 보상 임계값에 해당하는 플레이어가 API를 호출함으로써 받을 수 있습니다.
수령 처리는 트랜잭션으로 실행되며, 설정된 입수 액션이 순서대로 평가됩니다.

과거 시즌의 보상을 받는 경우에는 `season` 에 대상 시즌 번호를 지정합니다.

#### 글로벌 랭킹



**Unity**
```csharp

    var transaction = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).GlobalRankingModel(
        rankingName: "ranking-0001"
    ).GlobalRankingSeason(
        gameSession: GameSession,
        season: null // current season
    ).GlobalRankingReceivedReward(
    ).ReceiveGlobalRankingRewardAsync(
    );
    await transaction.WaitAsync();
```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->GlobalRankingModel(
        "ranking-0001" // rankingName
    )->GlobalRankingSeason(
        GameSession,
        TOptional<int64>() // current season
    )->GlobalRankingReceivedReward(
    )->ReceiveGlobalRankingReward(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

    const auto Transaction = Future->GetTask().Result();
    const auto Future2 = Transaction->Wait();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError()) return false;
```
**Godot**
```gdscript

var domain = ez.ranking2.namespace_(
        "namespace-0001"
    ).global_ranking_model(
        "ranking-0001"
    ).global_ranking_season(
        0,
        null
    ).global_ranking_received_reward(
    )

var async_result = await domain.receive_global_ranking_reward(
    null # config
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


#### 클러스터 랭킹



**Unity**
```csharp

    var transaction = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).ClusterRankingModel(
        rankingName: "ranking-0001"
    ).ClusterRankingSeason(
        clusterName: "cluster-0001",
        gameSession: GameSession,
        season: null // current season
    ).ClusterRankingReceivedReward(
    ).ReceiveClusterRankingRewardAsync(
    );
    await transaction.WaitAsync();
```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->ClusterRankingModel(
        "ranking-0001" // rankingName
    )->ClusterRankingSeason(
        "cluster-0001", // clusterName
        GameSession,
        TOptional<int64>() // current season
    )->ClusterRankingReceivedReward(
    )->ReceiveClusterRankingReward(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

    const auto Transaction = Future->GetTask().Result();
    const auto Future2 = Transaction->Wait();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError()) return false;
```
**Godot**
```gdscript

var domain = ez.ranking2.namespace_(
        "namespace-0001"
    ).cluster_ranking_model(
        "ranking-0001"
    ).cluster_ranking_season(
        "cluster-0001",
        0,
        null
    ).cluster_ranking_received_reward(
    )

var async_result = await domain.receive_cluster_ranking_reward(
    null # config
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


## 상세 레퍼런스

[GS2-Ranking2 API 레퍼런스](../../api_reference/ranking2)



