다음을 통해 공유


로비와 파티 통합

PlayFab 로비와 PlayFab 파티는 네트워크 세션에 참가하기 전에 플레이어 그룹을 만들고 오케스트레이션하는 데 함께 사용되는 경우가 많습니다. 이 문서에서는 C++ 파티 라이브러리와 C++ 멀티플레이어 라이브러리를 사용하여 파티 네트워크 설명자를 공유하기 위한 편리한 메커니즘으로 로비를 사용하는 방법을 설명합니다.

파티 및 멀티플레이어 SDK 설정

시작하려면 파티 및 로비 빠른 시작을 참조하세요.

파티

파티 빠른 시작

로비

로비 빠른 시작

PlayFab 멀티 플레이어 SDK 초기화

PlayFab 멀티플레이어 SDK를 초기화하려면 로비 빠른 시작을 참조하세요.

직렬화된 네트워크 설명자 문자열을 사용하여 로비 만들기

로비를 만들고 파티 네트워크 설명자를 설정하여 로비 속성의 텍스트 형식으로 파티 네트워크 설명자를 설정합니다. 파티 네트워크 설명자를 char 문자열로 변환하려면 PartyManager::SerializeNetworkDescriptor()를 사용합니다.

아래 정의는 로비 속성과 검색에 사용되는 키와 값입니다.

const char* c_propertyKey_LobbyName{ "LobbyName" };
const char* c_propertyKey_PartyDescriptor{ "PartyDescriptor" };

const char* c_searchKey_LobbyGroup{ "string_key1" };    // For Key to identify lobby group
const char* c_searchValue_LobbyGroup{ "PartySample" };  // Use a identifiable string for search lobby group

// The reason for setting this value is to get the lobby name only from the search results, 
// and in practice, change it appropriately for the your game.
const char* c_searchKey_LobbyName{ "string_key2" };
  1. PFMultiplayerCreateAndJoinLobby를 호출합니다.
  2. PFLobbyCreateAndJoinLobbyCompletedStateChange에 대해 PFMultiplayerStartProcessingLobbyStateChanges를 주기적으로 폴링하여 비동기 완료를 확인합니다.
char descriptor[c_maxSerializedNetworkDescriptorStringLength + 1] = {};
// Serialize our local network descriptor for other peers to use
PartyError err = PartyManager::SerializeNetworkDescriptor(&networkDescriptor, descriptor);
if (PARTY_FAILED(err))
{
    // handle immediate create failure
    DEBUGLOG("SerializeNetworkDescriptor failed! %s\n", GetErrorMessage(err));
    return false;
}

std::vector<const char*> lobbyPropertyKeys;
std::vector<const char*> lobbyPropertyValues;
lobbyPropertyKeys.push_back(c_propertyKey_LobbyName);
lobbyPropertyValues.push_back(lobbyName);
lobbyPropertyKeys.push_back(c_propertyKey_PartyDescriptor);
lobbyPropertyValues.push_back(descriptor);

std::vector<const char*> searchPropertyKeys;
std::vector<const char*> searchPropertyValues;
searchPropertyKeys.push_back(c_searchKey_LobbyGroup);
searchPropertyValues.push_back(c_searchValue_LobbyGroup);
searchPropertyKeys.push_back(c_searchKey_LobbyName);
searchPropertyValues.push_back(lobbyName);

PFLobbyCreateConfiguration lobbyConfiguration{};
lobbyConfiguration.maxMemberCount = 16;
lobbyConfiguration.ownerMigrationPolicy = PFLobbyOwnerMigrationPolicy::Automatic;
lobbyConfiguration.accessPolicy = PFLobbyAccessPolicy::Public;
lobbyConfiguration.lobbyPropertyCount = static_cast<uint32_t>(lobbyPropertyKeys.size());
lobbyConfiguration.lobbyPropertyKeys = lobbyPropertyKeys.data();
lobbyConfiguration.lobbyPropertyValues = lobbyPropertyValues.data();
lobbyConfiguration.searchPropertyCount = static_cast<uint32_t>(searchPropertyKeys.size());
lobbyConfiguration.searchPropertyKeys = searchPropertyKeys.data();
lobbyConfiguration.searchPropertyValues = searchPropertyValues.data();

PFLobbyJoinConfiguration memberConfiguration{};

PFLobbyHandle lobby;
HRESULT hr = PFMultiplayerCreateAndJoinLobby(m_pfmHandle, &userEntity, &lobbyConfiguration, &memberConfiguration, nullptr, &lobby);
if (FAILED(hr))
{
    // handle immediate create failure
    DEBUGLOG("PFMultiplayerCreateAndJoinLobby failed! %s\n", PFMultiplayerGetErrorMessage(hr));
    return false;
}

// NOTE: to simplify this quickstart, we'll synchronously block waiting waiting for the CreateAndJoinLobby operation
// to finish. In a real implementation, this polling would be done asynchronously on a background thread/worker.
bool createAndJoinLobbyFinished = false;
while (!createAndJoinLobbyFinished)
{
    uint32_t lobbyStateChangeCount;
    const PFLobbyStateChange* const* lobbyStateChanges;
    HRESULT hr = PFMultiplayerStartProcessingLobbyStateChanges(m_pfmHandle, &lobbyStateChangeCount, &lobbyStateChanges);
    if (FAILED(hr))
    {
        // handle the failure
        DEBUGLOG("PFMultiplayerStartProcessingLobbyStateChanges failed! %s\n", PFMultiplayerGetErrorMessage(hr));
        return false;
    }

    for (uint32_t i = 0; i < lobbyStateChangeCount; ++i)
    {
        const PFLobbyStateChange* stateChange = lobbyStateChanges[i];
        switch (stateChange->stateChangeType)
        {
        case PFLobbyStateChangeType::CreateAndJoinLobbyCompleted:
        {
            auto createAndJoinStateChange =
                static_cast<const PFLobbyCreateAndJoinLobbyCompletedStateChange*>(stateChange);

            if (SUCCEEDED(createAndJoinStateChange->result))
            {
                // lobby successfully created!
                DEBUGLOG("Lobby 0x%p successfully created!\n", createAndJoinStateChange->lobby);
            }
            createAndJoinLobbyFinished = true;
            break;
        }
        }
    }

    hr = PFMultiplayerFinishProcessingLobbyStateChanges(m_pfmHandle, lobbyStateChangeCount, lobbyStateChanges);
    if (FAILED(hr))
    {
        DEBUGLOG("PFMultiplayerFinishProcessingLobbyStateChanges failed! %s\n", PFMultiplayerGetErrorMessage(hr));
        return false;
    }
}

로비 찾기

로비 찾기 사용 방법에 대한 자세한 내용은 다음 링크 로비 찾기 연결

이제 로비 찾기 API를 사용하여 위에서 생성한 로비를 검색하고 c_searchValue_LobbyType을(를) 사용하여 직렬화된 네트워크 설명자를 얻을 수 있습니다.

  1. PFMultiplayerFindLobbies 호출
std::string filterString;
filterString.append(c_searchKey_LobbyGroup);
filterString.append(" eq '");
filterString.append(c_searchValue_LobbyGroup);
filterString.append("'");

PFLobbySearchConfiguration searchConfiguration = { 0 };
searchConfiguration.filterString = filterString.c_str();

HRESULT hr = PFMultiplayerFindLobbies(m_pfmHandle, &localUser, &searchConfiguration, nullptr);
if (FAILED(hr))
{
    // handle immediate find lobbies failure
    printf("PFMultiplayerFindLobbies failed! %s\n", PFMultiplayerGetErrorMessage(hr));
    return false;
}

// NOTE: to simplify this quickstart, we'll synchronously block waiting waiting for the FindLobbies operation
// to finish. In a real implementation, this polling would be done asynchronously on a background thread/worker.
bool findLobbiesFinished = false;
while (!findLobbiesFinished)
{
    uint32_t lobbyStateChangeCount;
    const PFLobbyStateChange* const* lobbyStateChanges;
    HRESULT hr = PFMultiplayerStartProcessingLobbyStateChanges(m_pfmHandle, &lobbyStateChangeCount, &lobbyStateChanges);
    if (FAILED(hr))
    {
        // handle the failure
        printf("PFMultiplayerStartProcessingLobbyStateChanges failed! %s\n", PFMultiplayerGetErrorMessage(hr));
        return false;
    }

    for (uint32_t i = 0; i < lobbyStateChangeCount; ++i)
    {
        const PFLobbyStateChange* stateChange = lobbyStateChanges[i];
        switch (stateChange->stateChangeType)
        {
            case PFLobbyStateChangeType::FindLobbiesCompleted:
            {
                auto findLobbiesStateChange =
                    static_cast<const PFLobbyFindLobbiesCompletedStateChange*>(stateChange);

                if (FAILED(findLobbiesStateChange->result))
                {
                    printf("PFLobbyStateChangeType::FindLobbiesCompleted failed! %s\n", PFMultiplayerGetErrorMessage(findLobbiesStateChange->result));
                    break;
                }

                for (uint32_t i = 0; i < findLobbiesStateChange->searchResultCount; ++i)
                {
                    const PFLobbySearchResult& searchResult = findLobbiesStateChange->searchResults[i];

                    // Use searchResult.connectionString for connecting a lobby later.
                    MyGame::GuiPostLobbySearchResultRow(searchResult); // defined elsewhere
                }
                findLobbiesFinished = true;
                break;
            }
        }
    }

    hr = PFMultiplayerFinishProcessingLobbyStateChanges(m_pfmHandle, lobbyStateChangeCount, lobbyStateChanges);
    if (FAILED(hr))
    {
        printf("PFMultiplayerFinishProcessingLobbyStateChanges failed! %s\n", PFMultiplayerGetErrorMessage(hr));
        return false;
    }

로비에 참여

이제 이전 "로비 찾기" 작업에서 얻은 PFLobbySearchResult의 일부인 connectionString을 사용하여 로비에 참여합니다.

  1. PFMultiplayerJoinLobby 호출
// Fill in the member properties of user for referencing in the game.
const char* playerColorPropertyKey = "PlayerColor";
const char* playerColorPropertyValue = MyGame::GetPlayerColorString(localUser);
std::vector<const char*> memberPropertyKeys;
std::vector<const char*> memberPropertyValues;
memberPropertyKeys.push_back(playerColorPropertyKey);
memberPropertyValues.push_back(playerColorPropertyValue);

PFLobbyJoinConfiguration joinConfig;
joinConfig.memberPropertyCount = static_cast<uint32_t>(memberPropertyKeys.size());
joinConfig.memberPropertyKeys = memberPropertyKeys.data();
joinConfig.memberPropertyValues = memberPropertyValues.data();

// Join the lobby using the connection string
HRESULT hr = PFMultiplayerJoinLobby(m_pfmHandle, &localUser, connectionString, &joinConfig, nullptr, &m_lobby);
if (FAILED(hr))
{
    // handle immediate join lobby failure
    DEBUGLOG("PFMultiplayerJoinLobby failed! %s\n", PFMultiplayerGetErrorMessage(hr));
    return false;
}

// NOTE: to simplify this quickstart, we'll synchronously block waiting waiting for the JoinLobby operation
// to finish. In a real implementation, this polling would be done asynchronously on a background thread/worker.
bool joinLobbyFinished = false;
while (!joinLobbyFinished)
{
    uint32_t lobbyStateChangeCount;
    const PFLobbyStateChange* const* lobbyStateChanges;
    HRESULT hr = PFMultiplayerStartProcessingLobbyStateChanges(m_pfmHandle, &lobbyStateChangeCount, &lobbyStateChanges);
    if (FAILED(hr))
    {
        // handle the failure
        DEBUGLOG("PFMultiplayerStartProcessingLobbyStateChanges failed! %s\n", PFMultiplayerGetErrorMessage(hr));
        return false;
    }

    for (uint32_t i = 0; i < lobbyStateChangeCount; ++i)
    {
        const PFLobbyStateChange* stateChange = lobbyStateChanges[i];
        switch (stateChange->stateChangeType)
        {
        case PFLobbyStateChangeType::JoinLobbyCompleted:
        {
            auto joinStateChange =
                static_cast<const PFLobbyJoinLobbyCompletedStateChange*>(stateChange);

            if (SUCCEEDED(joinStateChange->result))
            {
                // lobby successfully joined!
                m_lobby = joinStateChange->lobby;

                uint32_t propertyCount;
                const char* const* keys;
                HRESULT hr = PFLobbyGetLobbyPropertyKeys(joinStateChange->lobby, &propertyCount, &keys);
                if (SUCCEEDED(hr))
                {
                    std::string descriptor;
                    for (uint32_t idx = 0; idx < propertyCount; idx++)
                    {
                        const char* value;
                        hr = PFLobbyGetLobbyProperty(joinStateChange->lobby, keys[idx], &value);
                        if (SUCCEEDED(hr))
                        {
                            if (strcmp(keys[idx], c_propertyKey_PartyDescriptor) == 0 && value)
                            {
                                descriptor = value;
                            }
                        }
                    }
                    if (!descriptor.empty())
                    {
                        partyDescriptor = descriptor;
                    }
                    else
                    {
                        // report asynchronous failure
                        DEBUGLOG("Failed to join lobby 0x%p! No Party descriptor found\n",
                            joinStateChange->lobby);
                    }
                }
            }
            joinLobbyFinished = true;
            break;
        }
        }
    }

    hr = PFMultiplayerFinishProcessingLobbyStateChanges(m_pfmHandle, lobbyStateChangeCount, lobbyStateChanges);
    if (FAILED(hr))
    {
        DEBUGLOG("PFMultiplayerFinishProcessingLobbyStateChanges failed! %s\n", PFMultiplayerGetErrorMessage(hr));
        return false;
    }
}

다음 단계

파티 네트워크에 연결하려면 파티 빠른 시작: 파티 네트워크에 연결에 대해 자세히 알아보세요.

참고 항목

파티 SDK

멀티플레이어 SDK