다음을 통해 공유


멀티플레이어 세션 검색

이 항목을 사용하여 사용자가 멀티플레이어 세션 검색 기능을 사용하여 지정된 기준에 맞는 오픈 멀티플레이어 게임 세션 목록을 쿼리할 수 있습니다.

멀티플레이어 세션 검색은 2016년 11월에 도입되었습니다. 세션 검색 시나리오에서는 사용자가 참가할 수 있는 게임 세션들의 목록을 검색할 수 있습니다. 이 목록의 각 세션 항목에는 게임에 관한 몇 가지 추가 메타데이터가 포함되어 있어서 사용자가 어떤 세션에 참가할지 선택할 때 도움이 될 수 있습니다. 또한 사용자는 메타데이터를 사용하여 세션 목록을 필터링할 수 있습니다. 사용자는 자신의 마음에 드는 게임 세션을 확인한 후 세션에 참여할 수 있습니다.

사용자는 또한 새로운 게임 세션을 만들고 세션 찾아보기 기능을 사용하여 매치 메이킹에 의존하지 않고 추가 사용자를 모집할 수 있습니다. 멀티플레이어 세션 검색과 기존의 매치메이킹은 다릅니다. 멀티플레이어 세션 검색을 사용하여 사용자는 참여할 게임 세션을 선택합니다. 매치 메이킹을 통해 사용자는 사용자를 자동으로 적절한 게임 세션에 배치되는 "게임 찾기" 옵션을 선택합니다.

멀티플레이어 세션 검색은 수동적이고 느린 프로세스로서 사용자를 위해 언제나 가장 적합한 게임 환경을 선택하는 것은 아니지만, 사용자는 더 많은 제어 권한을 갖게 되며, 검색 기능은 개인적으로 더 나은 게임 선택으로 인식될 수 있습니다. 게임에 멀티플레이어 세션 검색과 매치 메이킹 시나리오를 모두 포함시키는 것이 일반적입니다. 일반적으로 매치 메이킹은 일반적으로 플레이되는 게임 모드에 사용되고 멀티플레이어 세션 검색은 사용자 지정 게임에 사용됩니다.

시나리오 예제

존은 배틀 아레나 멀티플레이어 게임을 하고 싶지만, 모든 사용자가 무작위로 영웅을 선택하는 게임을 하고 싶습니다. 존은 멀티플레이어 세션 브라우저를 사용하여 오픈 게임 세션 목록을 검색하고 설명에 "랜덤 히어로"가 포함된 세션을 찾을 수 있습니다.

타이틀의 UI에 옵션이 있으면 '랜덤 히어로' 게임 모드를 선택하고 태그가 지정된 세션만 검색하여 '랜덤 히어로' 게임을 표시할 수 있습니다. 존이 좋아하는 게임 세션을 찾으면 그 세션에 참여할 수 있습니다. 충분한 인원이 게임에 참여하면 게임 세션의 호스트가 게임을 시작할 수 있습니다.

역할

멀티플레이어 세션 검색 기능에 있는 세션에서 특정 역할에 대한 사용자를 모집한다고 지정할 수 있습니다. 예를 들어 사용자가 게임 세션을 만들 때 5개 이하의 공격 클래스가 포함되도록 지정할 수 있지만, 게임 세션에는 두 개 이상의 치유자 역할과 최소 1명의 탱크 역할이 포함되어야 합니다.

다른 사용자가 세션을 신청할 때 역할을 미리 선택할 수 있습니다. 그러면 서비스가 선택한 역할에 사용할 수 있는 열린 슬롯 수에 따라 세션에 참여할 수 있는지 여부를 결정합니다. 친구가 가입할 수 있도록 슬롯 2개를 예약하려는 경우 세션의 '친구' 역할을 지정할 수 있으며, 호스트와 친구가 된 사용자만 '친구' 역할 전용 슬롯 2개를 채울 수 있습니다.

역할에 관한 자세한 내용은 멀티 플레이어 역할을 참조하세요.

멀티플레이어 세션 검색 작동 방식

세션 검색은 검색 핸들을 사용하여 작동합니다. 검색 핸들은 세션에 대한 참조뿐 아니라 세션에 관한 검색 특성과 같은 추가 메타데이터를 포함하는 데이터 패킷입니다.

타이틀은 멀티플레이어 세션 검색에 적합한 새 게임 세션을 만들 때 세션에 대한 검색 핸들을 만듭니다. 이 검색 핸들은 타이틀의 검색 핸들을 보관하는 MPSD(멀티 플레이어 서비스 디렉터리)에 저장됩니다. 타이틀은 세션 목록을 검색해야 할 경우 MPSD에 검색 쿼리를 전송할 수 있으며, 그러면 MPSD는 검색 기준에 맞는 검색 핸들의 목록을 반환합니다. 그러면 타이틀은 이 세션 목록을 사용하여 참여 가능한 게임 목록을 사용자에게 표시합니다. 세션이 가득 찼거나 가입할 수 없는 경우 타이틀이 MPSD에서 검색 핸들을 제거하여 세션이 멀티플레이어 세션 검색 쿼리에 표시되지 않도록 할 수 있습니다.

참고 항목

검색 핸들은 사용자에게 세션 목록을 표시할 때 사용됩니다. 배경 매치 메이킹에는 검색 핸들을 사용하지 마세요. 배경 매치 메이킹의 경우 SmartMatch를 사용하는 것이 좋습니다. 자세한 내용은 멀티 플레이어 관리자를 사용하는 SmartMatch를 사용하여 멀티 플레이어 게임 찾기를 참조하세요.

멀티플레이어 세션 검색을 위한 세션 설정

세션에 검색 핸들을 사용하려면 세션에 searchableuserAuthorizationStyle이(가) true(으)로 설정되어 있어야 합니다. userAuthorizationStyle 기능은 UWP 게임에서만 필요하지만, 향후 이식성이 보장되도록 Microsoft Game Development Kit GDK(게임 개발 키트) 게임을 포함한 모든 Xbox 게임에서 이를 구현하는 것이 좋습니다. 이렇게 하면 타이틀의 향후 포팅 가능성을 보장할 수 있습니다.

참고 항목

userAuthorizationStyle을(를) true(으)로 구성하면 세션의 readRestrictionjoinRestriction이(가) none 대신 local(으)로 변경됩니다. 즉, 타이틀이 게임 세션에 참가하기 위해서는 검색 또는 전송 핸들을 사용해야 합니다.

Xbox 서비스를 구성할 때 세션 템플릿에서 이러한 기능을 설정할 수 있습니다.

참고 항목

멀티플레이어 세션 검색의 경우 실제 게임 실행에 사용되는 세션에만 검색 핸들을 만드세요. 이 핸들은 로비 세션에 사용하지 마세요.

게임 세션 소유권

SmartMatch 또는 친구 전용 세션과 같은 많은 게임 세션 유형에는 호스트나 소유자가 필요하지 않습니다. 그러나 멀티플레이어 세션 검색 게임을 설정할 때 소유자가 필요할 수 있습니다. 소유자 관리 세션이 있으면 세션에서 다른 구성원을 제거하거나 다른 구성원의 소유권 상태를 변경하는 등 호스트에 몇 가지 이점이 제공됩니다. 세션에 소유자를 사용하려면 세션에 hasOwners이(가) true(으)로 설정되어 있어야 합니다. 멀티 플레이어 역할을 사용할 경우, 소유자만이 사용자에게 역할을 할당할 수 있도록 설정할 수 있습니다.

참고 항목

세션 소유자가 Xbox Live 구성원을 차단한 경우 해당 구성원은 세션에 참여할 수 없습니다.

모든 소유자가 세션에서 나갈 경우에는 서비스가 그 세션에 정의된 ownershipPolicy.migration 정책에 따라 세션에 조치를 취합니다. 이 정책이 "oldest(가장 오래된)"로 되어 있으면 세션에 가장 오래 있었던 사용자가 새로운 소유자로 설정됩니다. 정책이 "종료 세션"(기본값, 제공되지 않은 경우)인 경우 서비스는 세션을 종료하고 세션에서 나머지 모든 사용자를 제거합니다.

검색 핸들

검색 핸들은 MPSD에 JSON 구조로 저장됩니다. 검색 핸들에는 세션에 대한 참조가 포함될 뿐 아니라 검색 특성이라고 하는 검색에 대한 추가 메타데이터도 포함됩니다. 세션은 언제라도 자신을 위해 만들어진 검색 핸들 한 개만 지닐 수 있습니다. Xbox 서비스 API(XSAPI)를 사용하여 세션에서 검색 핸들을 만들려면 XblMultiplayerCreateSearchHandleAsync 메서드를 호출할 수 있습니다.

검색 특성

검색 특성은 다음과 같은 요소로 구성됩니다.

  • tags은(는) 해시태그와 같은 게임 세션을 분류하는 데 사용할 수 있는 문자열 설명자입니다.

    • 태그는 문자로 시작해야 하고 공백이 없어야 하며 100자 미만이어야 합니다.
    • 태그의 예: "ProRankOnly", "norocketlaunchers", "cityMaps".
  • strings은(는) 텍스트 변수입니다.

    • 문자열 이름은 문자로 시작해야 하고 공백이 없어야 하며 100자 미만이어야 합니다.
    • 문자열 메타데이터의 예: "Weapons"="knives+pistols+rifles", "MapName"="UrbanCityAssault", "description"="Fun casual game, new people welcome".
  • numbers은(는) 숫자 변수입니다. XSAPI는 부동 소수점 유형의 숫자값을 검색합니다.

    • 숫자 이름은 문자로 시작해야 하고 공백이 없어야 하며 100자 미만이어야 합니다.
    • 숫자 메타데이터의 예: "MinLevel" = 25, "MaxRank" = 10.

참고 항목

태그와 문자열 값의 문자 대/소문자는 서비스에서 보존되지만 태그를 쿼리할 때는 tolower() 함수를 사용해야 합니다. 이것은 태그와 문자열 값에 대문자 문자가 포함되어 있더라도 현재 모두 소문자로 취급되고 있음을 의미합니다.

XSAPI에서 XblMultiplayerCreateSearchHandleAsync 메서드를 사용하여 태그 및 메타데이터와 같은 검색 특성을 설정할 수 있습니다.

추가 세부 정보

검색 핸들을 검색할 때 세션 닫힘 여부 및 참가 제한이 있는 경우 등 세션에 대한 유용한 데이터도 결과에 포함됩니다. XSAPI에서는 검색 특성과 함께 이러한 세부 정보가 검색 쿼리 후 반환되는 XblMultiplayerSearchHandle 결과에 포함됩니다.

검색 핸들 제거

예를 들어 세션이 가득 찼거나 닫혀서 멀티플레이어 세션 검색에서 특정 세션을 제거하고 싶을 경우, 해당 검색 핸들을 삭제할 수 있습니다. XSAPI에서 Xbl MultiplayerDeleteSearchHandleAsync 메서드를 사용하여 검색 핸들을 제거합니다.

메타데이터를 사용하여 검색 핸들 생성

다음의 코드는 Xbox 멀티 플레이어 API를 사용하여 세션에서 검색 핸들을 만드는 방법을 보여 줍니다.

플랫 C

size_t tagsCount = 1;
XblMultiplayerSessionTag tags[1] = {};
tags[0] = XblMultiplayerSessionTag{ "SessionTag" };

size_t numberAttributesCount = 1;
XblMultiplayerSessionNumberAttribute numberAttributes[1] = {};
numberAttributes[0] = XblMultiplayerSessionNumberAttribute{ "numberattributename", 1.1 };

size_t strAttributesCount = 1;
XblMultiplayerSessionStringAttribute strAttributes[1] = {};
strAttributes[0] = XblMultiplayerSessionStringAttribute{ "stringattributename", "string attribute value" };

auto asyncBlock = std::make_unique<XAsyncBlock>();
asyncBlock->queue = queue;
asyncBlock->context = nullptr;
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
{
    std::unique_ptr<XAsyncBlock> asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
    XblMultiplayerSearchHandle searchHandle{ nullptr };
    HRESULT hr = XblMultiplayerCreateSearchHandleResult(asyncBlock, &searchHandle);

    if (SUCCEEDED(hr))
    {
        const char* handleId{ nullptr };
        XblMultiplayerSearchHandleGetId(searchHandle, &handleId);
    }
};

HRESULT hr = XblMultiplayerCreateSearchHandleAsync(
    xblContextHandle,
    &xblMultiplayerSessionReference,
    tags,
    tagsCount,
    numberAttributes,
    numberAttributesCount,
    strAttributes,
    strAttributesCount,
    asyncBlock.get()
);

if (SUCCEEDED(hr))
{
    // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership.
    // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock*
    asyncBlock.release();
}

코드 예제에 사용된 기능에 대한 자세한 내용은 multiplayer_c를 참조하세요.

세션용 검색 쿼리 만들기

검색 핸들 목록을 검색할 때, 검색 쿼리를 사용하여 결과를 특정 기준을 충족하는 세션으로 제한할 수 있습니다.

이 검색 쿼리 구문은 다음 연산자들만 지원되는 OData 유형의 구문입니다.

연산자 설명
eq 같음
ne 같지 않음
gt 보다 큼
ge 보다 크거나 같음
lt 미만
le 보다 작거나 같음
and 논리곱
or 논리합(아래의 '참고' 참조)

참고 항목

람다 식과 tolower 정식 함수도 사용할 수 있습니다. 현재 지원되는 다른 OData 기능은 없습니다.

태그 또는 문자열 값을 검색할 때는 검색 쿼리에서 tolower 함수를 사용해야 합니다. 이 서비스는 소문자 문자열 검색만 지원합니다. Xbox 서비스는 검색 쿼리와 일치하는 처음 100개의 결과만을 반환합니다. 결과가 너무 광범위한 경우 사용자가 검색 쿼리를 구체화할 수 있는지 확인합니다.

참고 항목

논리 OR 문은 필터 문자열 쿼리에서 지원됩니다. 그러나 OR은(는) 하나만 허용되며 쿼리의 루트에 있어야 합니다. 쿼리에 OR 문을 여러 개 사용할 수 없을 뿐 아니라 OR이(가) 쿼리 구조의 최상위 수준에 없게 되는 쿼리는 생성할 수 없습니다.

검색 핸들 쿼리의 예

RESTful 호출에서는 모든 검색 핸들에 대한 쿼리에서 실행되는 OData Filter 언어 문자열을 "Filter(필터)"에서 지정합니다. 멀티 플레이어 2015 API의 경우 XblMultiplayerGetSearchHandlesAsync 메서드의 searchFilter 매개 변수를 사용하여 검색 필터 문자열을 지정할 수 있습니다.

현재 지원되는 필터 시나리오는 다음과 같습니다.

필터링 기준 검색 필터 문자열
단일 멤버 xuid '1234566' "session/memberXuids/any(d:d eq '1234566')"
단일 사용자 xuid '1234566' "session/ownerXuids/any(d:d eq '1234566')"
'classb'와 같은 문자열 'forzacarclass' "tolower(strings/forzacarclass) eq 'classb'"
6과 같은 숫자 'forzaskill' "numbers/forzaskill eq 6"
1.5보다 큰 숫자 'halokdratio' "numbers/halokdratio gt 1.5"
태그 'coolpeopleonly' "tags/any(d:tolower(d) eq 'coolpeopleonly')"
태그 'cursingallowed'가 포함되지 않은 세션 "tags/any(d:tolower(d) ne 'cursingallowed')"
0과 같은 숫자 'rank'가 포함되지 않은 세션 "numbers/rank ne 0"
'classa'와 같은 문자열 'forzacarclass'가 포함되지 않은 세션 "tolower(strings/forzacarclass) ne 'classa'"
태그 'coolpeopleonly', 그리고 7.5와 같은 숫자 'halokdratio' "tags/any(d:tolower(d) eq 'coolpeopleonly') eq true and numbers/halokdratio eq 7.5"
1.5보다 크거나 같은 숫자 'halodkratio', 60 미만의 숫자 'rank', 5보다 작거나 같은 숫자 'customnumbervalue' "numbers/halokdratio ge 1.5 and numbers/rank lt 60 and numbers/customnumbervalue le 5"
도전 과제 ID '123456' "achievementIds/any(d:d eq '123456')"
언어 코드 'en' "language eq 'en'"
예약된 시간, 지정된 시간보다 작거나 같은 모든 예약된 시간을 반환 "session/scheduledTime le '2009-06-15T13:45:30.0900000Z'"
게시된 시간, 지정된 시간보다 작은 모든 게시된 시간을 반환 "session/postedTime lt '2009-06-15T13:45:30.0900000Z'"
세션 등록 상태 "session/registrationState eq 'registered'"
세션 멤버 수가 5와 같은 경우 "session/membersCount eq 5"
세션 멤버 목표 수가 1보다 큰 경우 "session/targetMembersCount gt 1"
세션 멤버의 최대수가 3 미만인 경우 "session/maxMembersCount lt 3"
세션 멤버 목표 수와 세션 멤버의 수의 차이가 5보다 작거나 같은 경우 "session/targetMembersCountRemaining le 5"
세션 멤버의 최대수와 세션 멤버의 수의 차이가 2보다 큰 경우 "session/maxMembersCountRemaining gt 2"
세션 멤버 목표 수와 세션 멤버의 수의 차이가 15보다 작거나 같은 경우.
역할에 목표가 지정되어 있지 않은 경우, 이 쿼리는 세션 멤버의 최대수와 세션 멤버의 수의 차이와 비교하여 필터링합니다.
"session/needs le 15"
역할 유형 "lfg"로 "확인된" 역할, 그 역할을 지닌 멤버의 수가 5와 같은 경우 "session/roles/lfg/confirmed/count eq 5"
역할 유형 "lfg"로 "확인된" 역할, 그 역할의 목표수가 1보다 큰 경우.
이 역할에 목표수가 지정되어 있지 않은 경우, 역할의 최대수가 대신 사용됩니다.
"session/roles/lfg/confirmed/target gt 1"
역할 유형 "lfg"로 "확인된" 역할, 역할의 목표수와 그 역할을 지닌 멤버의 수의 차이가 15보다 작거나 같은 경우.
역할에 목표가 지정되어 있지 않은 경우, 이 쿼리는 역할의 최대수와 그 역할을 지닌 멤버의 수의 차이와 비교하여 필터링합니다.
"session/roles/lfg/confirmed/needs le 15"
특정 키워드를 포함하는 세션을 가리키는 모든 검색 핸들 "session/keywords/any(d:tolower(d) eq 'level2')"
특정 SCID에 속해 있는 세션을 가리키는 모든 검색 핸들 "session/scid eq '151512315'"
특정 템플릿 이름을 사용하는 세션을 가리키는 모든 검색 핸들 "session/templateName eq 'mytemplate1'"
태그 'elite'가 있거나 15보다 큰 숫자 'guns'와 'purple'와 같은 문자열 'clan'이 있는 모든 검색 핸들 "tags/any(a:tolower(a) eq 'elite') or number/guns gt 15 and string/clan eq 'purple'"

검색 결과 새로 고침

게임에서는 세션 목록을 자동으로 새로 고치지 않아야 합니다. 사용자가 UI를 사용하여 목록을 수동으로 새로 고칠 수 있는 옵션을 제공합니다. 이 옵션은 사용자가 검색 기준을 세분화하여 결과를 더욱 정확히 필터링하는 경우에 유용합니다. 사용자가 세션에 참가하려고 하지만 세션이 가득 차 있거나 닫힌 경우라면 물론 게임이 검색 결과를 새로 고치는 것이 좋습니다.

참고 항목

검색이 새로 고침을 너무 많이 하면 서비스 제한으로 이어질 수 있습니다. 타이틀은 쿼리를 새로 고칠 수 있는 속도를 제한해야 합니다.

서비스 호출량을 줄이기 위해, 빠르게 변화하는 세션 특성들을 저장하고 쿼리할 때 사용할 수 있는 사용자 지정 세션 속성이 검색 핸들에 포함되어 있습니다. 이러한 특성은 검색 특성에 저장하면 안 됩니다.

검색 핸들 쿼리

다음 코드는 검색 핸들을 쿼리하는 방법을 보여줍니다. API는 쿼리에 부합하는 모든 검색 핸들을 나타내는 XblMultiplayerSearchHandle 개체의 모음을 반환합니다.

플랫 C

auto asyncBlock = std::make_unique<XAsyncBlock>();
asyncBlock->queue = queue;
asyncBlock->context = nullptr;
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
{
    std::unique_ptr<XAsyncBlock> asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*
    size_t resultCount{ 0 };
    auto hr = XblMultiplayerGetSearchHandlesResultCount(asyncBlock, &resultCount);
    if (SUCCEEDED(hr) && resultCount > 0)
    {
        auto handles = new XblMultiplayerSearchHandle[resultCount];

        hr = XblMultiplayerGetSearchHandlesResult(asyncBlock, handles, resultCount);

        if (SUCCEEDED(hr))
        {
            // Process handles
            for (auto i = 0u; i < resultCount; ++i)
            {
                const char* handleId{ nullptr };
                XblMultiplayerSearchHandleGetId(handles[i], &handleId);

                XblMultiplayerSearchHandleCloseHandle(handles[i]);
            }
        }
    }

};

const char* sessionName{ "MinGameSession" };
const char* orderByAttribute{ nullptr };
bool orderAscending{ false };
const char* searchFilter{ nullptr };
const char* socialGroup{ nullptr };

HRESULT hr = XblMultiplayerGetSearchHandlesAsync(
    xblContextHandle,
    scid,
    sessionName,
    orderByAttribute,
    orderAscending,
    searchFilter,
    socialGroup,
    asyncBlock.get()
);
if (SUCCEEDED(hr))
{
    // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* since the callback will take over ownership.
    // If the call fails, the std::unique_ptr will keep ownership and delete the XAsyncBlock*
    asyncBlock.release();
}

코드 예제에 사용된 기능에 대한 자세한 내용은 multiplayer_c를 참조하세요.

검색 핸들을 사용하여 세션 참가

사용자가 가입할 세션에 대한 검색 핸들을 검색한 후 Xbl MultiplayerWriteSessionByHandleAsync를 사용하여 세션에 자신을 추가할 수 있는지 확인합니다.

참고 항목

Xbl MultiplayerWriteSessionAsync 메서드를 사용하여 멀티플레이어 세션 찾아보기 세션에 가입할 수 없습니다.