> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# PlayFab Party 的 XBOX Live 協助程式庫

> 使用 PartyXboxLive 協助程式庫，將 XBOX Live 身分識別、聊天權限和隱私權設定與 PlayFab Party 語音和網路功能銜接。

# XBOX Live 協助程式庫概觀

PlayFab Party 的 XBOX Live 協助程式庫旨在協助使用 PlayFab Party 的遊戲符合與通訊相關的 [XBOX Live 原則](https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/live/get-started/live-xbl-overview) (XR-015 和 XR-045)。XBOX Live 協助程式庫可在 [Nuget.org](https://www.nuget.org/profiles/PlayFab) 上取得。

## 與 PlayFab Party 程式庫的相容性

雖然我們努力將 API 中的重大變更降到最低，但對 PlayFab Party API 所做的某些變更可能會導致 XBOX Live 協助程式庫傳回錯誤的值。請參閱下表，確保您的程式庫版本相容。

| XBOX Live 協助程式庫 <br /> 版本 | PlayFab Party 版本 <br /> 1.0.1 | PlayFab Party 版本 <br /> 1.3.0+ |
| - | :-: | :-: |
| **1.0.1** | ✔ | |
| **1.1.0** | ✔ | |
| **1.2.0** | | ✔ |
| **1.2.5** | | ✔ |

<a id="keeping-track-of-xbox-live-users" />

## 追蹤 XBOX Live 使用者

必須明確告知 PlayFab Party XBOX Live 協助程式庫目前參與 Party 工作階段的 XBOX Live 使用者。建議遊戲接聽其[多人遊戲工作階段文件](https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/live/get-started/live-xbl-overview)的變更，並透過 `PartyXblManager::CreateLocalChatUser` 和 `PartyXblManager::CreateRemoteChatUser` 在 XBOX Live 協助程式庫中反映該名單，以告知程式庫。

對於本機使用者：

```cpp theme={null}
void
OnLocalXboxUserAddedToMPSD(
    uint64_t xboxUserId
    )
{
    PartyXblLocalChatUser* localChatUser;
    PartyError err = PartyXblManager::GetSingleton().CreateLocalChatUser(xboxUserId, nullptr, &localChatUser);
    if (PARTY_FAILED(err))
    {
        DEBUGLOG("CreateLocalChatUser failed: %s\n", PartyXblManager::GetErrorMessage(err));
        return;
    }
}
```

此時，如果對應的本機 XBOX 使用者已有 PartyLocalChatControl，您可以透過 SetCustomContext 方法將其與此 PartyXblLocalChatUser 相關聯。

```cpp theme={null}
    localChatControl->SetCustomContext(localChatUser);
    localChatUser->SetCustomContext(localChatControl);
```

否則，您可以使用這個新的 PartyXblLocalChatUser 產生聊天控制項，然後再將兩者相關聯。如需詳細資訊，請參閱[從 PartyXblLocalChatUsers 建立 PartyLocalChatControls](#creating-partylocalchatcontrols-from-partyxbllocalchatusers)。

對於遠端使用者：

```cpp theme={null}
void
OnRemoteXboxUserAddedToMPSD(
    uint64_t xboxUserId
    )
{
    PartyXblChatUser* remoteChatUser;
    PartyError err = PartyXblManager::GetSingleton().CreateRemoteChatUser(remoteXboxUserId, &remoteChatUser);
    if (PARTY_FAILED(err))
    {
        DEBUGLOG("CreateRemoteChatUser failed: %s\n", PartyXblManager::GetErrorMessage(err));
        return;
    }
```

此時，如果對應的遠端 XBOX 使用者已有 PartyChatControl，您可以透過 SetCustomContext 方法將其與此 PartyXblChatUser 相關聯。

```cpp theme={null}
    remoteChatControl->SetCustomContext(remoteChatUser);
    remoteChatUser->SetCustomContext(remoteChatControl);
```

請記住，工作階段文件的更新與遠端聊天控制項清單的更新可能不會依序發生，在處理遠端聊天控制項的 `PartyChatControlCreatedStateChange` 更新時，您可能需要類似的關聯邏輯。

對於本機和遠端聊天使用者，請務必記住，核心 Party 程式庫是透過 PlayFab 實體識別碼來識別使用者和聊天控制項，而 XBOX Live 協助程式庫則是以 XBOX 使用者識別碼識別聊天使用者。因此，通常需要在兩者之間進行轉換。如需詳細資訊，請參閱 [XBOX Live 使用者識別碼與 PlayFab 實體識別碼之間的對應](#mapping-between-xbox-live-user-ids-and-playfab-entity-ids)。

<a id="creating-partylocalchatcontrols-from-partyxbllocalchatusers" />

## 從 PartyXblLocalChatUsers 建立 PartyLocalChatControls

`PartyXblLocalChatUser` 物件通常只有在與 Party 程式庫中的 `PartyLocalUser` 和 `PartyLocalChatControl` 物件相關聯時才有用。產生 `PartyLocalUser` 和 `PartyLocalChatControl` 物件時，遊戲需要將其使用者登入 PlayFab，並擷取使用者的 `entityId` 和 `titlePlayerEntityToken`。登入可以透過 [PlayFab CPP SDK](/zh-TW/services/playfab/sdks/playfab-cpp) 執行，但如果遊戲打算使用 XBOX Live 認證登入 PlayFab，可以使用 `PartyXblManager::LoginToPlayFab`，以避免引入額外的相依性。

下列範例顯示 XBOX Live 協助程式庫如何協助您從 `PartyXblLocalChatUser` 物件建立 `PartyLocalUser` 和 `PartyLocalChatControl` 物件。如需建立 `PartyXblLocalChatUser` 物件的詳細資訊，請參閱[追蹤 XBOX Live 使用者](#keeping-track-of-xbox-live-users)。

```cpp theme={null}
    err = PartyXblManager::GetSingleton().LoginToPlayFab(localChatUser, nullptr);
    if (PARTY_FAILED(err))
    {
        DEBUGLOG("LoginToPlayFab failed: %s\n", PartyXblManager::GetErrorMessage(err));
        return;
    }
```

呼叫 `PartyXblManager::LoginToPlayFab` 後不久，您會收到包含登入作業結果的 `PartyXblLoginToPlayFabCompletedStateChange`。

```cpp theme={null}
    PartyLocalUser* partyLocalUser;
    if (stateChange->stateChangeType == PartyXblStateChangeType::LoginToPlayFabCompleted)
    {
        auto loginToPlayFabCompleted = static_cast<PartyXblLoginToPlayFabCompletedStateChange*>(stateChange);
        if (loginToPlayFabCompleted->result == PartyXblStateChangeResult::Succeeded)
        {
            err = PartyManager::GetSingleton().CreateLocalUser(
                loginToPlayFabCompleted->entityId,
                loginToPlayFabCompleted->titlePlayerEntityToken,
                partyLocalUser));
            if (PARTY_FAILED(err))
            {
                DEBUGLOG("CreateLocalUser failed: %s\n", PartyManager::GetErrorMessage(err));
                return;
            }
        }
    }

    PartyLocalDevice* localDevice;
    err = PartyManager::GetSingleton().GetLocalDevice(&localDevice);
    if (PARTY_FAILED(err))
    {
        DEBUGLOG("GetLocalDevice failed: %s\n", PartyManager::GetErrorMessage(err));
        return;
    }

    PartyLocalChatControl* localChatControl;
    err = localDevice->CreateChatControl(partyLocalUser, nullptr, nullptr, &localChatControl);
    if (PARTY_FAILED(err))
    {
        DEBUGLOG("CreateChatControl failed: %s\n", PartyManager::GetErrorMessage(err));
        return;
    }

    // We can use the custom context on the PartyXblLocalChatUser to store the PartyLocalChatControl for easy access
    // in the future.
    localChatUser->SetCustomContext(localChatControl);
```

## 尊重 XBOX Live 使用者的協助工具喜好設定

`PartyXblLocalChatUser` 物件會公開 XBOX Live 使用者與 Party 聊天工作階段相關的部分協助工具喜好設定。遊戲可以使用此資訊立即啟用 Party 的部分協助工具功能，為玩家提供更好的體驗。

```cpp theme={null}
    PartyXblAccessibilitySettings accessibilitySettings;
    err = localChatUser->GetAccessibilitySettings(&accessibilitySettings);
    if (PARTY_FAILED(err))
    {
        DEBUGLOG("GetAccessibilitySettings failed: %s\n", PartyXblManager::GetErrorMessage(err));
        return;
    }

    if (accessibilitySettings.speechToTextEnabled)
    {
        PartyVoiceChatTranscriptionOptions option = PartyVoiceChatTranscriptionOptions::TranscribeOtherChatControlsWithMatchingLanguages;
        m_localChatControl->SetTranscriptionOptions(option, nullptr);
    }
```

## 尊重 XBOX Live 使用者的隱私權設定和權限

根據 XBOX Live 原則，當使用者的隱私權或權限不允許時，遊戲不得允許透過 XBOX Live 進行通訊。XBOX Live 協助程式庫可讓您查詢 XBOX Live 原則所允許之兩位使用者之間最嚴格的 `PartyChatPermissionOptions`，以協助您達成此目標。每當此值變更時，程式庫都會產生 `PartyXblRequiredChatPermissionInfoChangedStateChange`。可以呼叫 `PartyXblLocalChatUser::GetRequiredChatPermissionInfo()` 取得更新的 `PartyChatPermissionOptions`。

```cpp theme={null}
    PartyXblChatUser* remoteChatUser;
    PartyError err = PartyXblManager::GetSingleton().CreateRemoteChatUser(remoteXboxUserId, &remoteChatUser);
    if (PARTY_FAILED(err))
    {
        DEBUGLOG("CreateRemoteChatUser failed: %s\n", PartyXblManager::GetErrorMessage(err));
        return;
    }

    // Once the chat control representing this remote Xbox Live user joins a network, we can use the custom context
    // on the PartyXblChatUser to store the chat control object for quick access in the future.
    remoteChatUser->SetCustomContext(m_remotePartyChatControl);
```

XBOX Live 協助程式庫會與 XBOX Live 隱私權服務通訊，追蹤每位遠端聊天使用者相對於每位本機聊天使用者的隱私權和權限設定。此外，程式庫會訂閱[即時活動](/zh-TW/services/xbox-services/fundamentals/rta/live-rta-nav)更新，以接聽這些設定的變更。當新增新的遠端聊天使用者，或本機聊天使用者與現有遠端聊天使用者之間的隱私權和權限關係變更時，會產生 `PartyXblRequiredChatPermissionInfoChangedStateChange`，通知您現在有更新的 `PartyChatPermissionOptions` 值可用。

```cpp theme={null}
    // Wait for PartyXblRequiredChatPermissionInfoChangedStateChange
    if (stateChange->stateChangeType == PartyXblStateChangeType::RequiredChatPermissionInfoChanged)
    {
        auto chatPermissionChanged = static_cast<PartyXblRequiredChatPermissionInfoChangedStateChange*>(stateChange);

        PartyXblLocalChatUser* localChatUser = chatPermissionChanged->localChatUser;
        PartyXblChatUser* targetChatUser = chatPermissionChanged->targetChatUser;

        PartyXblChatPermissionInfo chatPermissionInfo;
        PartyError err = localChatUser->GetRequiredChatPermissionInfo(targetChatUser, &chatPermissionInfo);
        if (PARTY_FAILED(err))
        {
            DEBUGLOG("GetRequiredChatPermissionInfo failed: %s\n", PartyXblManager::GetErrorMessage(err));
            return;
        }

        PartyLocalChatControl* localChatControl;
        localChatUser->GetCustomContext(reinterpret_cast<void**>(&localChatControl));
        if (PARTY_FAILED(err))
        {
            DEBUGLOG("GetCustomContext failed: %s\n", PartyXblManager::GetErrorMessage(err));
            return;
        }

        PartyChatControl* targetChatControl;
        targetChatUser->GetCustomContext(reinterpret_cast<void**>(&targetChatControl));
        if (PARTY_FAILED(err))
        {
            DEBUGLOG("GetCustomContext failed: %s\n", PartyXblManager::GetErrorMessage(err));
            return;
        }

        localChatControl->SetPermission(targetChatControl, chatPermissionInfo.chatPermissionMask);
        if (PARTY_FAILED(err))
        {
            DEBUGLOG("SetPermission failed: %s\n", PartyXblManager::GetErrorMessage(err));
            return;
        }
    }
```

`PartyXblChatPermissionInfo` 結構包含兩項資訊：

* `PartyChatPermissionOptions` 遮罩，可以直接傳遞給 `PartyLocalChatControl::SetPermission()`，或者如果您已經有想要使用的 `PartyChatPermissionOptions` 值，但想確保遵守 XBOX Live 原則，也可以將其用作二進位遮罩。
* `PartyXblChatPermissionMaskReason` 值，提供有關 `PartyXblChatPermissionInfo::chatPermissionMask` 值的額外資訊

| **PartyXblChatPermissionMaskReason** | **說明** |
| :- | :- |
| NoRestriction | 目前沒有任何限制套用至此聊天權限。 |
| Determining | 在判斷本機使用者的通訊權限和隱私權設定期間，通訊會受到限制 |
| Privilege | 由於本機使用者的通訊權限，通訊受到限制。 |
| Privacy | 由於本機使用者對目標聊天使用者的隱私權設定，通訊受到限制。 |
| InvalidTargetUser | 由於 XBOX Live 服務無法將目標使用者識別為有效，通訊受到限制。 |
| XboxLiveServiceError | 由於 XBOX Live 服務的問題，無法成功判斷所需的聊天權限。 |
| UnknownError | 由於未知的內部錯誤，無法成功判斷所需的聊天權限。 |

## 尊重跨網路通訊權限

支援 XBOX Live 和非 XBOX Live 玩家之間跨網路遊玩和通訊的遊戲，需要在允許這些玩家之間通訊之前檢查通訊權限。XBOX Live 協助程式庫透過 `PartyXblLocalChatUser::GetCrossNetworkCommunicationPrivacySetting()` 提供此資訊。此方法會傳回具有三個可能值的 `PartyXblCrossNetworkCommunicationPrivacySetting` 列舉：

| **PartyXblCrossNetworkCommunicationPrivacySetting** | **說明** |
| :- | :- |
| Allowed | 此 XBOX Live 使用者的權限設定為允許與所有跨網路玩家通訊。 |
| FriendsOnly | 此 XBOX Live 使用者的權限設定為僅允許與跨網路好友通訊。 |
| Disallowed | 此 XBOX Live 使用者的權限不允許與跨網路玩家進行任何通訊。 |

```cpp theme={null}
    PartyXblCrossNetworkCommunicationPrivacySetting crossNetworkSetting;
    localChatUser->GetCrossNetworkCommunicationPrivacySetting(&crossNetworkSetting);

    if (crossNetworkSetting == PartyXblCrossNetworkCommunicationPrivacySetting::Disallowed)
    {
        m_localChatControl->SetPermissions(crossNetworkChatControl, PartyChatPermissionOptions::None);
    }
```

如需 XR-015 及其與跨網路遊玩和通訊之關係的詳細資訊，請參閱[這裡](https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/live/get-started/live-xbl-overview)

<a id="mapping-between-xbox-live-user-ids-and-playfab-entity-ids" />

## XBOX Live 使用者識別碼與 PlayFab 實體識別碼之間的對應

許多使用 PlayFab Party 的 XBOX Live 遊戲需要在 XBOX Live 使用者識別碼 (在整個 XBOX Live 生態系統中使用) 和 PlayFab 實體識別碼 (由 PlayFab Party 使用) 之間進行轉換。使用 `PartyXblManager::GetEntityIdsFromXboxLiveUserIds`，遊戲可以擷取與指定 XBOX Live 使用者識別碼清單對應的 PlayFab 實體識別碼清單。遊戲應該已經透過外部名單服務 (例如 [Multiplayer Session Directory](https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/live/get-started/live-xbl-overview)) 取得 XBOX Live 使用者識別碼清單。藉由將名單中的 XBOX Live 使用者識別碼與其 PlayFab 實體識別碼相關聯，我們可以建構與遊戲工作階段名單對應的所有 PlayFab 實體識別碼的對應。接著可以使用此對應，將 `PartyEndpoint` 和 `PartyChatControl` 物件與其對應的 XBOX Live 使用者相關聯。

<Note>
  只有在 XBOX Live 使用者已連結至 PlayFab 帳戶時，每個 XBOX Live 使用者識別碼才會對應到 PlayFab 實體識別碼。第一次為指定的 XBOX 使用者呼叫 `PartyXblManager::LoginToPlayFab` 時，會自動建立並連結 PlayFab 帳戶。或者，PlayFab SDK 的取用者也可以使用 [LoginWithXbox](https://learn.microsoft.com/en-us/rest/api/playfab/client/authentication/login-with-xbox?view=playfab-rest\&preserve-view=true) API 達成相同的結果。
</Note>

本機 `PartyXblLocalChatUser` 將用於向 PlayFab 進行驗證。如果使用者先前未透過呼叫 `PartyXblManager::LoginToPlayFab` 登入 PlayFab，XBOX Live 協助程式庫將需要在背景中驗證使用者。

```cpp theme={null}
    uint32_t userCount;
    PartyXblChatUserArray chatUsers;
    PartyError err = PartyXblManager::GetSingleton().GetChatUsers(&userCount, &users);
    if (PARTY_FAILED(err))
    {
        DEBUGLOG("GetChatUsers failed: %s\n", PartyXblManager::GetErrorMessage(err));
        return;
    }

    // The list of remote Xbox Live User IDs. This can be populated with arbitrary IDs
    // std::vector<uint64_t> remoteXboxLiveUserIds = {2533274792693551, 2814659110958830};
    //
    // but can also be pulled from the list of remote chat users
    std::vector<uint64_t> remoteXboxLiveUserIds;
    for (uint32_t i = 0; i < userCount; ++i)
    {
        PartyXblChatUser* chatUser = users[i];

        PartyXblLocalChatUser* localChatUser;
        err = chatUser->GetLocal(&localChatUser);
        if (PARTY_FAILED(err))
        {
            DEBUGLOG("PartyChatUser(0x%p)::GetLocal failed: %s\n", chatUser, PartyXblManager::GetErrorMessage(err));
            return;
        }

        if (localChatUser != nullptr)
        {
            continue; // ignore local users
        }

        uint64_t userId;
        err = chatUser->GetXboxUserId(&userId);
        if (PARTY_FAILED(err))
        {
            DEBUGLOG("PartyChatUser(0x%p)::GetXboxUserId failed: %s\n", chatUser, PartyXblManager::GetErrorMessage(err));
            return;
        }

        remoteXboxLiveUserIds.push_back(userId);
    }

    err = PartyXblManager::GetSingleton().GetEntityIdsFromXboxLiveUserIds(
        remoteXboxLiveUserIds.size(),
        remoteXboxLiveUserIds.data(),
        localChatUser,
        nullptr);
    if (PARTY_FAILED(err))
    {
        DEBUGLOG("GetEntityIdsFromXboxLiveUserIds failed: %s\n", PartyXblManager::GetErrorMessage(err));
        return;
    }
```

呼叫 `PartyXblManager::GetEntityIdsFromXboxLiveUserIds` 後不久，您會收到包含作業結果的 `PartyXblGetEntityIdsFromXboxLiveUserIdsCompletedStateChange`。此結果可用來建構或更新您的對應。

```cpp theme={null}
    // Wait for PartyXblGetEntityIdsFromXboxLiveUserIdsCompletedStateChange
    if (stateChange->stateChangeType == PartyXblStateChangeType::GetEntityIdsFromXboxLiveUserIdsCompleted)
    {
        std::vector<std::pair<uint64_t, std::string>> cachedXboxUserIdToPlayFabEntityIdMap;

        auto getEntityIdsFromXboxLiveUserIdsResult = static_cast<PartyXblGetEntityIdsFromXboxLiveUserIdsCompletedStateChange*>(stateChange);
        for (uint32_t i = 0; i < getEntityIdsFromXboxLiveUserIdsResult.entityIdMappingCount; ++i)
        {
            const PartyXblXboxUserIdToPlayFabEntityIdMapping& idMapping = getEntityIdsFromXboxLiveUserIdsResult.entityIdMappings[i];

            Log("   Xbox Live User ID: %llu", idMapping.xboxLiveUserId);
            if (strlen(idMapping.playfabEntityId)) != 0)
            {
                Log("    PlayFab Entity ID: %s", idMapping.playfabEntityId);
                cachedXboxUserIdToPlayFabEntityIdMap.emplace_back(idMapping.xboxLiveUserId, idMapping.playfabEntityId);
            }
            else
            {
                // This Xbox Live User did not have a linked PlayFab Account.
                Log("    PlayFab Entity ID: NOT FOUND");
            }
        }

        m_cachedXboxUserIdToPlayFabEntityIdMap = std::move(cachedXboxUserIdToPlayFabEntityIdMap);
```

有了這樣的對應，遊戲就能辨識 Party 物件何時代表 XBOX Live 使用者。

```cpp theme={null}
uint64_t
GetXboxUserIdFromPlayFabEntityId(
    PartyString entityId
    )
{
    for (const std::pair<uint64_t, std::string>& idMapping : m_cachedXboxUserIdToPlayFabEntityIdMap)
    {
        const std::string& entityIdForXboxUserId = idMapping.second;
        if (entityIdForXboxUserId == entityId)
        {
            return idMapping.first;
        }
    }

    // Failed to find a matching Xbox User ID. This Entity ID does not represent an Xbox Live user.
    return 0;
}
```

## Windows 的特殊考量

在 Windows 上，XBOX Live 協助程式庫需要遊戲的協助才能取得 XBOX Live 權杖。程式庫會藉由產生 `PartyXblTokenAndSignatureRequestedStateChange` 來要求權杖。遊戲可以使用 [XBOX 驗證程式庫](https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/live/get-started/live-xbl-overview) (XAL) 來完成這些要求。將此工作交由遊戲處理，可確保遊戲完全掌控通常與使用者驗證相關聯的任何 UI 處理和同意提示。

```cpp theme={null}
    if (stateChange->stateChangeType == PartyXblStateChangeType::TokenAndSignatureRequested)
    {
        auto tokenAndSignatureRequested = static_cast<PartyXblTokenAndSignatureRequestedStateChange*>(stateChange);

        // Convert the headers to the format XAL expect
        std::vector<XalHttpHeader> xalHeaders;
        for (uint32_t i = 0; i < stateChange.headerCount; ++i)
        {
            xalHeaders.push_back({stateChange.headers[i].name, stateChange.headers[i].value});
        }

        XalUserGetTokenAndSignatureArgs args = {};
        args.method = stateChange.method;
        args.url = stateChange.url;
        args.headerCount = static_cast<uint32_t>(xalHeaders.size());
        args.headers = xalHeaders.data();
        args.bodySize = stateChange.bodySize;
        args.body = static_cast<const uint8_t*>(stateChange.body);
        args.forceRefresh = stateChange.forceRefresh != 0;
        args.allUsers = stateChange.allUsers != 0;

        XAsyncBlock* asyncBlock = new XAsyncBlock;
        asyncBlock->queue = m_queue;
        HRESULT hr = XalUserGetTokenAndSignatureSilentlyAsync(m_userHandle, &args, asyncBlock);
    }
```

如需如何使用 XAL 擷取權杖和簽章的更多指引，請參閱 [XBOX 驗證程式庫文件。](https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/live/get-started/live-xbl-overview)

取得權杖和簽章之後，可以使用透過狀態變更提供給遊戲的相同 `correlationId` 呼叫 `PartyXblManager::CompleteGetTokenAndSignatureRequest()`，將其提供給 XBOX Live 協助程式庫。


## Related topics

- [PlayFab Party XBOX Live 協助程式版本資訊](/zh-TW/services/playfab/multiplayer/networking/party-xboxlive-relnotes.md)
- [XBOX 需求](/zh-TW/services/playfab/multiplayer/networking/xbox-requirements.md)
- [XBOX 服務遊戲的 PlayFab 整合](/zh-TW/services/xbox-services/playfab-integration.md)
- [快速入門 - 適用於 C# 的 PlayFab 用戶端程式庫](/zh-TW/services/playfab/sdks/c-sharp/quickstart.md)
- [PlayFab Party XBOX Live Helper 錯誤碼](/zh-TW/services/playfab/multiplayer/networking/xblreference/partyxblerrors.md)
