Documentation index for AI agents

GS2-Matchmaking

매치메이킹 기능

플레이어를 조건에 따라 그룹화하는 기능입니다. 대전 상대를 찾기 위해 사용할 수 있습니다.

스크립트 트리거

네임스페이스에 createGatheringTriggerScriptId · completeMatchmakingTriggerScriptId · changeRatingScript 를 설정하면, 개더링 생성 완료 시·매칭 완료 시·레이팅 변동 시에 커스텀 스크립트를 실행할 수 있습니다.

설정 가능한 주요 이벤트 트리거와 스크립트 설정명은 다음과 같습니다.

  • createGatheringTriggerScriptId: 개더링 생성 완료 시
  • completeMatchmakingTriggerScriptId: 매칭 완료 시
  • changeRatingScript: 레이팅 변동 시

푸시 알림

설정 가능한 주요 푸시 알림과 설정명은 다음과 같습니다.

  • joinNotification: 매칭 참가 시 알림
  • leaveNotification: 매칭 이탈 시 알림
  • completeNotification: 매칭 완료 시 알림
  • changeRatingNotification: 레이팅 변동 시 알림

모두 GS2-Gateway 를 통해 알림을 보낼 수 있으며, 오프라인 단말로의 모바일 푸시 전송도 가능합니다.

마스터 데이터 운용

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

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

  • RatingModel: 레이팅 계산 설정
  • SeasonModel: 시즌 기간 설정

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

추가 설정

enableDisconnectDetection 을 활성화하면 매치메이킹 중 연결 끊김 감지를 수행하며, disconnectDetectionTimeoutSeconds 로 타임아웃을 조정할 수 있습니다. 또한 enableCollaborateSeasonRating 을 설정하면 매치메이킹 완료 시 GS2-SeasonRating과 연계하여 결과를 받는 세션을 생성할 수 있습니다.

트랜잭션 액션

GS2-Matchmaking 에서는 다음의 트랜잭션 액션을 제공합니다.

  • 검증 액션: 영속 개더링 참가 상태 검증

“영속 개더링 참가 상태 검증"을 검증 액션으로 사용하면, 특정 시즌 개더링(팀이나 클러스터)에 소속된 플레이어만 받을 수 있는 보상 설정 등이 가능해집니다. 이를 통해 시즌별 그룹 대항 이벤트나, 특정 커뮤니티 한정 인센티브 지급과 같은 정책을 트랜잭션 내에서 안전하게 제어할 수 있습니다.

유스케이스별 설계 가이드

파티 토큰을 사용한 매치메이킹

매치메이킹 완료 후 1명이 이탈하여 충원하고 싶은 경우나, 사전에 파티를 편성한 후 매치메이킹을 하고 싶은 경우에는 “파티 토큰"을 사용합니다.

파티 멤버는 각각 플레이어 토큰(유효기간 3분)을 발급하여 파티 대표자에게 전송합니다. 대표자는 파티 토큰 발급 API에 이를 전송함으로써 파티 토큰(유효기간 10분)을 획득하고, 개더링 생성 또는 개더링 검색을 호출합니다. 이를 통해 파티 멤버 전원이 참가한 상태의 개더링을 생성하거나 참가할 수 있습니다.

파티 간 매치메이킹

예를 들어 4 vs 4 대전에서 사전에 편성한 파티를 분산시키고 싶지 않은 경우, 먼저 4인 매치메이킹을 실행하여 파티 단위의 개더링을 생성하고, 그 후 각 파티의 대표자가 정원 2명의 매치메이킹을 수행함으로써 파티 간 매치메이킹을 구현할 수 있습니다.

매치메이킹 중 플레이어 간 통신

매치메이킹 완료 전에 플레이어 간의 소통이 필요한 경우, 개더링 생성 시 다음 2가지 방법을 사용할 수 있습니다.

  • GS2-Chat 와 연계한 채팅방 생성: 메타데이터의 저빈도(초당 3회 이내) 교환에 적합합니다
  • GS2-Realtime 과 연계한 게임 서버 기동: 고빈도 통신이나, 인원이 부족한 상태에서 게임을 진행시키고 싶은 경우에 적합합니다

매치메이킹 완료 후 처리

매치메이킹이 완료되면 다음 방법으로 플레이어에게 알릴 수 있습니다.

  • GS2-Gateway 를 사용한 게임 내 푸시 알림
  • GS2-JobQueue 에 의한 작업 등록

또한 GS2-Script 를 사용하여 임의의 스크립트를 실행할 수도 있습니다. GS2-Realtime 을 사용하는 경우, 매치메이킹 완료 후 실행되는 GS2-Script 에서 GS2-Realtime 의 개더링을 생성하고, IP 주소·포트 정보를 푸시 알림 또는 잡 큐를 사용해 통지함으로써 게임 서버로 유도할 수 있습니다.

블랙리스트를 이용한 매칭 제외

개더링 생성 시·검색 시에 사용자ID 블랙리스트를 지정할 수 있습니다. 검색 시 지정한 블랙리스트는 참가한 개더링의 블랙리스트에 추가됩니다. 자신이 블랙리스트에 포함되어 있는 경우 매치메이킹 대상에서 제외됩니다.

스탠다드 매치메이킹

개더링

개더링은 매치메이커에 의해 그룹화된 플레이어의 집합입니다. 개더링의 라이프사이클은 최초에 플레이어에 의해 명시적으로 생성됩니다. 다른 플레이어가 매치메이킹을 실행하여 조건에 부합하면 개더링에 참가하게 됩니다.

개더링 생성 시 지정한 인원수의 플레이어가 모이면, 매치메이킹 결과를 플레이어에게 통지하고 GS2-Matchmaking 상에서 개더링은 삭제됩니다.

속성

플레이어는 최대 5개의 속성값을 가질 수 있습니다. 개더링을 생성할 때는 모집할 플레이어의 각 속성값 범위를 설정하여 개더링을 생성합니다.

예를 들어, 다음과 같은 속성값을 가진 플레이어가 있다고 합시다.

속성명속성값
GameMode1
Stage10
Level5

GameMode 는 게임 내에서 전 세계 플레이어를 매치메이킹 대상으로 하는지(1) 아니면 각 리전별로 매치메이킹하는지(2=JP, 3=US, 4=EU)를 나타내는, 희망하는 게임 모드를 나타내는 값이라고 합시다. Stage 는 대전에 사용하고 싶은 게임 내 스테이지의 종류를 나타낸다고 합니다. 마지막으로 Level 은 플레이어의 실력 수준입니다.

이 플레이어가 대전 상대를 찾기 위해 개더링을 생성하는 경우, 개더링에 어떤 조건을 설정하는 것이 좋을지 생각해봅시다.

속성명속성값(최소)속성값(최대)
GameMode11
Stage1010
Level28

위와 같은 범위 설정이 좋을 것입니다. GameMode 나 Stage 는 완전히 일치하지 않으면 플레이어가 원하는 조건으로 플레이하게 할 수 없습니다. Level 은 플레이어 레벨의 전후 3에 해당하는 플레이어를 대상으로 합시다.

이렇게 개더링을 생성하면, 조건에 맞는 플레이어가 매치메이킹 리퀘스트를 보낼 때 개더링에 참가하게 됩니다.

역할

개더링을 생성할 때는 모집 인원수를 설정해야 합니다. 모집 인원수를 설정할 때, 역할별로 모집 인원수를 설정할 수 있습니다. 특히 게임 내에 역할이 없는 경우에는 default 라는 이름의 역할 하나만 설정하고, 그 역할의 모집 인원수에 모으고 싶은 인원수를 지정합니다.

역할 지정이 필요해지는 경우는 게임 내에 역할이 존재하는 게임입니다. 예를 들어 MMORPG 에서는 플레이어의 직업에 따라 탱커·힐러·DPS 와 같은 게임 내 역할이 존재합니다. 그리고 매치메이킹을 수행한 결과 힐러 4명이 모이더라도 콘텐츠는 공략할 수 없습니다. 각 역할이 균형있게 매치메이킹되어야 합니다.

이러한 사양에서는 다음과 같은 역할과 모집 인원수를 설정합니다.

역할명모집 인원수
탱커1
힐러1
DPS2

그리고 플레이어는 매치메이킹 리퀘스트를 보낼 때 속성값뿐만 아니라 자신의 역할도 설정합니다. 이렇게 하면 역할별로 모집 인원수 슬롯이 관리되어 매치메이킹이 실행됩니다.

역할의 별칭

더욱 복잡한 조건 설정을 바탕으로 역할 기반 매치메이킹을 수행하는 예를 소개합니다. DPS 에는 근거리DPS 와 원거리DPS 2종류가 있다고 합시다. 기본적인 매치메이킹 조건은 앞서 설명한 대로면 되지만, 개더링 생성자의 고집으로 근거리DPS 와 원거리DPS 가 한 명씩 들어와 주었으면 한다고 합시다.

이 경우, 조건 설정은 다음과 같이 됩니다.

역할명모집 인원수
탱커1
힐러1
근거리DPS1
원거리DPS1

그리고 플레이어는 매치메이킹을 할 때 “DPS” 가 아니라 “근거리DPS” 혹은 “원거리DPS” 를 지정합니다. 하지만 이대로라면 “DPS 가 2명 모이기만 하면 근거리든 원거리든 상관없다” 는 사람과는 매치메이킹할 수 없게 되어버립니다.

이때 사용할 수 있는 기능이 역할의 별칭입니다. 플레이어가 역할에 “근거리DPS” 혹은 “원거리DPS” 중 하나를 설정한다는 부분은 변하지 않지만, 개더링을 생성할 때의 조건 설정이 달라집니다.

역할명별칭모집 인원수
탱커[]1
힐러[]1
DPS[근거리DPS, 원거리DPS]2

이렇게 하면, DPS 의 모집 슬롯에는 역할이 “근거리DPS” 나 “원거리DPS” 인 플레이어도 들어올 수 있게 됩니다.

레이팅 계산

플레이어의 실력을 나타내는 레이팅 값을 계산하는 기능이 준비되어 있습니다. 레이트 값은 초기값이 1500이며, 게임 결과에 따라 값이 오르내립니다.

레이트 값의 차이가 큰 플레이어끼리 대전하여 레이트 값이 높은 플레이어가 지면, 레이트 값이 높았던 패배한 플레이어의 레이트 값은 크게 하락하고, 레이트 값이 낮았던 승리한 플레이어의 레이트 값은 크게 상승합니다. 이러한 특성에 의해 플레이어의 실력을 반영한 레이트 값을 계산합니다.

투표

레이트 값을 변경하려면 투표 처리가 필요합니다. 대전이 끝나고 순위가 확정되면, 각 플레이어는 게임 결과를 서버에 전송합니다. 서버는 개더링별로 투표를 받아, 모든 플레이어가 투표를 마치거나 최초의 플레이어가 투표한 후 5분이 경과하면 투표를 마감합니다.

서버에서는 투표 내용으로 다수결을 취해 최종 결과를 확정합니다. 이 프로세스에 의해, 거짓 투표를 하는 악의적인 플레이어가 있더라도 결과에 반영되지 않도록 작용합니다. 다수결에 의해 확정된 순위를 바탕으로 새로운 레이트 값이 계산되어 반영됩니다.

투표 결과의 분열

서버가 다수결을 취하려고 할 때 결과가 동수가 되어 최종 결과를 확정할 수 없는 경우에는 레이트 계산이 이루어지지 않습니다. 그렇기 때문에, 1대1 게임에서는 올바른 레이트 값을 구하기가 어렵습니다. 이 문제를 해결하려면, 뒤에서 대전에 직접 관여하지 않는 3번째 플레이어를 매치메이킹하여 그 플레이어가 제3자 관점에서 투표하도록 하는 등의 방법이 필요합니다.

개더링의 유효기간

개더링은 원칙적으로 한번 생성되면, 매치메이킹이 성립하거나 모든 플레이어가 개더링에서 빠질 때까지 삭제되지 않습니다. 특히 플레이어가 적은 게임에서 매치메이킹 중에 플레이어가 취소 API를 호출하지 않고 게임을 종료한 경우 다른 플레이어가 매치메이킹 리퀘스트를 보내 매치메이킹이 성립하더라도 이미 개더링을 생성한 플레이어가 없는 상황이 발생할 수 있습니다.

이러한 문제를 간단히 해결하는 수단으로, 개더링 생성 시 개더링의 유효기간을 설정할 수 있습니다. 지정한 시각 또는 생성 시각으로부터의 경과 시간을 지정하면, 그 시각이 되면 개더링은 삭제됩니다. 이때, 이미 개더링에 참가 중인 플레이어는 자동으로 퇴장 처리가 이루어집니다.

구현 예제

개더링 생성

    var result = await gs2.Matchmaking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).CreateGatheringAsync(
        player: new Gs2.Unity.Gs2Matchmaking.Model.EzPlayer {
             Attributes = new [] {
                new Gs2.Unity.Gs2Matchmaking.Model.EzAttribute {
                    Name = "stage",
                    Value = 1,
                },
                new Gs2.Unity.Gs2Matchmaking.Model.EzAttribute {
                    Name = "level",
                    Value = 10,
                },
            },
        },
        attributeRanges: new [] {
            new Gs2.Unity.Gs2Matchmaking.Model.EzAttributeRange {
                Name = "stage",
                Min = 1,
                Max = 1,
            },
            new Gs2.Unity.Gs2Matchmaking.Model.EzAttributeRange {
                Name = "level",
                Min = 0,
                Max = 10,
            },
        },
        capacityOfRoles: new [] {
            new Gs2.Unity.Gs2Matchmaking.Model.EzCapacityOfRole {
                RoleName = "default",
                Capacity = 4,
            },
        },
    );
    var item = await result.ModelAsync();
    const auto Future = Gs2->Matchmaking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->CreateGathering(
        MakeShared<Gs2::Matchmaking::Model::FPlayer>()
            ->WithAttributes([]
            {
                const auto v = MakeShared<TArray<TSharedPtr<Gs2::Matchmaking::Model::FAttribute>>>();
                v->Add(MakeShared<Gs2::Matchmaking::Model::FAttribute>()
                    ->WithName(TOptional<FString>("stage"))
                    ->WithValue(TOptional<int32>(1)));
                v->Add(MakeShared<Gs2::Matchmaking::Model::FAttribute>()
                    ->WithName(TOptional<FString>("level"))
                    ->WithValue(TOptional<int32>(10)));
                return v;
            }()),
        []
        {
            const auto v = MakeShared<TArray<TSharedPtr<Gs2::Matchmaking::Model::FAttributeRange>>>();
            v->Add(MakeShared<Gs2::Matchmaking::Model::FAttributeRange>()
                ->WithName(TOptional<FString>("stage"))
                ->WithMin(TOptional<int32>(1))
                ->WithMax(TOptional<int32>(1)));
            v->Add(MakeShared<Gs2::Matchmaking::Model::FAttributeRange>()
                ->WithName(TOptional<FString>("level"))
                ->WithMin(TOptional<int32>(0))
                ->WithMax(TOptional<int32>(10)));
            return v;
        }(), // attributeRanges
        []
        {
            const auto v = MakeShared<TArray<TSharedPtr<Gs2::Matchmaking::Model::FCapacityOfRole>>>();
            v->Add(MakeShared<Gs2::Matchmaking::Model::FCapacityOfRole>()
                ->WithRoleName(TOptional<FString>("default"))
                ->WithCapacity(TOptional<int32>(4)));
            return v;
        }(), // capacityOfRoles
        nullptr, // allowUserIds
        nullptr, // expiresAt
        nullptr // expiresAtTimeSpan
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
var domain = ez.matchmaking.namespace_(
        "namespace-0001"
    ).me(game_session)

var async_result = await domain.create_gathering(
    (Gs2MatchmakingPlayer.new()
        .with_attributes([
            (Gs2MatchmakingEzAttribute.new()
                .with_name("stage")
                .with_value(1)),
            (Gs2MatchmakingEzAttribute.new()
                .with_name("level")
                .with_value(10)),
        ])), # player
    [
        (Gs2MatchmakingEzAttributeRange.new()
            .with_name("stage")
            .with_min(1)
            .with_max(1)),
        (Gs2MatchmakingEzAttributeRange.new()
            .with_name("level")
            .with_min(0)
            .with_max(10)),
    ], # attribute_ranges
    [
        (Gs2MatchmakingEzCapacityOfRole.new()
            .with_role_name("default")
            .with_capacity(4)),
    ], # capacity_of_roles
    null, # allow_user_ids
    null, # expires_at
    null # expires_at_time_span
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

매치메이킹 처리 실행

    var items = await gs2.Matchmaking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DoMatchmakingAsync(
        player: new Gs2.Unity.Gs2Matchmaking.Model.EzPlayer {
             Attributes = new [] {
                new Gs2.Unity.Gs2Matchmaking.Model.EzAttribute {
                    Name = "stage",
                    Value = 1,
                },
                new Gs2.Unity.Gs2Matchmaking.Model.EzAttribute {
                    Name = "level",
                    Value = 10,
                },
            },
        },
    ).ToListAsync();
    const auto It = Gs2->Matchmaking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->DoMatchmaking( // player
    );
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }
var iterator = ez.matchmaking.namespace_(
    "namespace-0001"
).me(game_session).do_matchmaking(
    Gs2MatchmakingEzPlayer.new()
)
var async_result = await iterator.wait()
if async_result.error != null:
    push_error(str(async_result.error))
    return
var gathering = async_result.result

개더링에서 퇴장

    var result = await gs2.Matchmaking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Gathering(
        gatheringName: "gathering-0001"
    ).CancelMatchmakingAsync(
    );
    const auto Future = Gs2->Matchmaking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Gathering(
        "gathering-0001" // gatheringName
    )->CancelMatchmaking(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
var domain = ez.matchmaking.namespace_(
        "namespace-0001"
    ).me(game_session).gathering(
        "gathering-0001"
    )

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

var result = async_result.result

레이트 값 조회

    var item = await gs2.Matchmaking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Rating(
        ratingName: "rating-0001"
    ).ModelAsync();
    const auto Domain = Gs2->Matchmaking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Rating(
        "rating-0001" // ratingName
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
    const auto Item = Future->GetTask().Result();
var domain = ez.matchmaking.namespace_(
        "namespace-0001"
    ).me(game_session).rating(
        "rating-0001"
    )

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

var result = async_result.result

투표용지 조회

    var item = await gs2.Matchmaking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Ballot(
        ratingName: "rating-0001",
        gatheringName: "gathering-0001",
        numberOfPlayer: 4,
        keyId: "grn:gs2:{region}:{yourOwnerId}:key:namespace-0001:key:key-0001"
    ).ModelAsync();

    var body = result.Body;
    var signature = result.Signature;
    const auto Domain = Gs2->Matchmaking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Ballot(
        "rating-0001", // ratingName
        "gathering-0001", // gatheringName
        4, // numberOfPlayer
        "key-0001" // keyId
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
    const auto Item = Future->GetTask().Result();
var domain = ez.matchmaking.namespace_(
        "namespace-0001"
    ).me(game_session).ballot(
        "rating-0001",
        "gathering-0001",
        4,
        "key-0001"
    )

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

var result = async_result.result

투표 실행

    var result = await gs2.Matchmaking.Namespace(
        namespaceName: "namespace-0001"
    ).VoteAsync(
        ballotBody: "ballotBody",
        ballotSignature: "ballotSignature",
        gameResults: new [] {
            new Gs2.Unity.Gs2Matchmaking.Model.EzGameResult
            {
                Rank = 1,
                UserId = "user-0001",
            },
            new Gs2.Unity.Gs2Matchmaking.Model.EzGameResult
            {
                Rank = 2,
                UserId = "user-0002",
            },
            new Gs2.Unity.Gs2Matchmaking.Model.EzGameResult
            {
                Rank = 2,
                UserId = "user-0003",
            },
            new Gs2.Unity.Gs2Matchmaking.Model.EzGameResult
            {
                Rank = 3,
                UserId = "user-0004",
            },
        }
    );
    const auto Future = Gs2->Matchmaking->Namespace(
        "namespace-0001" // namespaceName
    )->Vote(
        "ballotBody",
        "ballotSignature",
        []
        {
            const auto v = MakeShared<TArray<TSharedPtr<Gs2::Matchmaking::Model::FGameResult>>>();
            v->Add({'rank': 1, 'userId': 'user-0001'});
            v->Add({'rank': 2, 'userId': 'user-0002'});
            v->Add({'rank': 2, 'userId': 'user-0003'});
            v->Add({'rank': 3, 'userId': 'user-0004'});
            return v;
        }() // gameResults
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
var domain = ez.matchmaking.namespace_(
        "namespace-0001"
    )

var async_result = await domain.vote(
    "ballotBody...", # ballot_body
    "ballotSignature...", # ballot_signature
    [
        Gs2MatchmakingEzGameResult.new()
            .with_rank(1)
            .with_user_id("user-0001"),
        Gs2MatchmakingEzGameResult.new()
            .with_rank(2)
            .with_user_id("user-0002"),
        Gs2MatchmakingEzGameResult.new()
            .with_rank(2)
            .with_user_id("user-0003"),
        Gs2MatchmakingEzGameResult.new()
            .with_rank(3)
            .with_user_id("user-0004"),
    ], # game_results
    "key-0001" # key_id
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

자주 묻는 질문

존재하는 개더링을 목록으로 표시하여 플레이어가 선택하게 하고 싶다

GS2-Matchmaking 에서는 그러한 기능을 제공하지 않습니다. 이는 기술적인 이유가 아니라, 게임 개발자의 한 사람으로서 플레이어의 게임 체험을 해치지 않기 위한 정책에 기인합니다.

존재하는 개더링에서 선택하는 방식이라면, 플레이어에게 최선의 개더링에 참가하는 방법을 제시할 수 있습니다. 하지만 개더링 목록을 가져온 후 실제로 참가 조작을 할 때까지 사용자 조작에 의한 지연이 발생합니다. 이 지연으로 인해, 참가하고 싶은 개더링을 선택했을 때 이미 개더링이 정원에 도달해 버린 경우를 피할 수 없습니다.

GS2 개발자는 과거에 이러한 게임을 다수 플레이하며 매우 큰 불만을 느껴왔습니다. GS2-Matchmaking 에서는 플레이어 스스로 개더링을 선택하게 하지 않아도, 자동으로 최적의 개더링을 찾아낼 수 있을 만큼의 구조를 제공하고 있다는 자부심이 있습니다. 그렇기 때문에, 이러한 기능은 제공하지 않습니다.

개더링에 비밀번호를 설정하고 싶다

비밀번호를 속성값으로 설정하여 매치메이킹 조건의 일부로 사용해 주세요.

시즌 매치메이킹

시즌 매치메이킹은 특정 기간 동안 영속적인 개더링을 생성합니다. 리얼타임 대전을 위한 매치메이킹이라기보다는, 특정 기간 동안 영속화되는 클러스터를 형성하고 GS2-Ranking2 의 클러스터 랭킹을 조합하여, 개더링 내에서의 랭킹을 구현하는 것이 일반적인 용법입니다.

시즌

시즌 매치메이킹을 이용하려면, GS2-Schedule 의 이벤트를 지정하여 기간을 설정합니다. 이벤트에 반복 설정이 있는 경우, 반복될 때마다 새로운 개더링이 형성됩니다.

티어

시즌 매치메이킹에 GS2-Experience 를 조합하면 특정 랭크끼리 매치메이킹할 수 있습니다. GS2-Ranking2 의 클러스터 랭킹에는 랭킹 보상을 설정하는 기능이 있어, 랭킹 상위 플레이어에게 경험치를 부여함으로써 상위 티어로 올라갈 수 있도록 할 수 있습니다.

최대 인원수

시즌 매치메이킹에서는 최대 1000명의 플레이어를 매치메이킹할 수 있습니다.

매치메이킹 조건

시즌 매치메이킹에서는 스탠다드 매치메이킹과 같은 복잡한 검색 조건을 설정할 수 없습니다. 검색 조건에 사용할 수 있는 파라미터는 단 하나, GS2-Experience 의 랭크뿐이며, 값은 API 인수로 받지 않고 GS2-Matchmaking 이 내부 처리에서 GS2-Experience 로부터 값을 가져옵니다.

GS2-Experience 와의 연계 기능을 사용하지 않는 경우에는 모든 플레이어를 대상으로 매치메이킹이 이루어집니다.

영속 개더링에서의 퇴장

스탠다드 매치메이킹에서는 매치메이킹된 개더링에서 퇴장할 수 있었습니다. 하지만 시즌 매치메이킹에서는 퇴장할 수 없으며, 이미 매치메이킹된 적이 있는 상태에서 매치메이킹 리퀘스트를 보내더라도 이미 참가 중인 개더링이 응답됩니다.

즉, 시즌 기간 동안에는 원칙적으로 다른 개더링으로 다시 매치메이킹할 수단이 없습니다. 단 하나의 예외가 있는데, 그것은 영속 개더링을 삭제한 경우로, 영속 개더링을 삭제하면 참가 중이던 플레이어는 다시 매치메이킹할 때 새로운 개더링으로 매치메이킹됩니다.

매치메이킹 처리의 우선순위

시즌 매치메이킹에서는 매치메이킹 시 동일 티어의 영속 개더링 중 규정 인원수에 도달하지 않은 개더링을 찾아 개더링에 참가합니다. 참가 가능한 개더링이 존재하지 않는 경우에는 자동으로 개더링을 생성하고, 참가 처리를 수행한 후 API를 응답합니다.

참가 가능한 개더링이 여러 개 존재하는 경우에는 참가 인원이 더 많은 개더링이 우선됩니다.

매치메이킹 처리가 경합할 때의 동작

매치메이킹 참가 처리는 일반적으로 수 밀리초 이하로 처리되지만, 그럼에도 동시에 같은 개더링이 참가 처리 후보로 선별되는 경우가 있을 수 있습니다. 이러한 경우, 규정 인원수보다 많은 플레이어가 참가해버리는 것을 방지하기 위해 GS2-Matchmaking 은 개더링의 잠금을 획득한 후 플레이어의 참가 처리를 수행합니다. 그렇기 때문에, 참가 가능한 개더링이 있더라도 다른 플레이어의 참가 처리 중인 짧은 기간 동안은 매치메이킹 대상이 되지 않고 다른 후보가 선택됩니다.

이 사양으로 인해, 1000명을 최대 수용 가능한 영속 개더링을 준비하고, 동일 티어의 플레이어 1000명이 완전히 동시에 매치메이킹 리퀘스트를 보낸 경우, 1000명이 참가하는 하나의 영속 개더링이 만들어진다고는 단정할 수 없으며, 여러 영속 개더링으로 나뉘어 매치메이킹될 가능성이 있습니다. 이때, 가능한 한 참가 인원이 많은 개더링에 우선적으로 플레이어를 참가시켜 나가므로, 일반적으로 다음과 같은 결과가 예상됩니다.

개더링A의 참가 인원개더링B의 참가 인원개더링C의 참가 인원
1000--
9991-
99811
99721
99541
9955-
99082

이 결과는 어디까지나 하나의 예시이며, 다른 패턴도 발생할 수 있습니다.

상세 레퍼런스