> ## 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**               |                               |                ✔               |

## 跟踪 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 来生成聊天控件并将它们关联起来。有关详情,请参阅[从 PartyXblLocalChatUser 创建 PartyLocalChatControl](#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 Entity ID 标识用户和聊天控件,而 XBOX Live 辅助库通过 XBOX User ID 标识聊天用户。因此,通常需要在两者之间进行转换。有关详情,请参阅 [XBOX Live 用户 ID 和 PlayFab Entity ID 之间的映射](#mapping-between-xbox-live-user-ids-and-playfab-entity-ids)。

## 从 PartyXblLocalChatUser 创建 PartyLocalChatControl

只有当 `PartyXblLocalChatUser` 对象与 Party 库中的 `PartyLocalUser` 和 `PartyLocalChatControl` 对象关联时才通常有用。生成 `PartyLocalUser` 和 `PartyLocalChatControl` 对象需要标题将其用户登录 PlayFab 并检索该用户的 `entityId` 和 `titlePlayerEntityToken`。登录可以通过 [PlayFab CPP SDK](/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 隐私服务通信,跟踪每个远程聊天用户相对于每个本地聊天用户的隐私和特权设置。此外,该库会通过订阅[实时活动](/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)

## XBOX Live 用户 ID 和 PlayFab Entity ID 之间的映射

许多使用 PlayFab Party 的 XBOX Live 标题需要在 XBOX Live 用户 ID(在整个 XBOX Live 生态系统中使用)和 PlayFab Entity ID(由 PlayFab Party 使用)之间进行转换。使用 `PartyXblManager::GetEntityIdsFromXboxLiveUserIds`,标题可以检索与给定 XBOX Live 用户 ID 列表对应的 PlayFab Entity ID 列表。标题应通过使用外部名册服务(如[多人会话目录](https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/live/get-started/live-xbl-overview))已获得 XBOX Live 用户 ID 列表。通过将名册中的 XBOX Live 用户 ID 与它们的 PlayFab Entity ID 关联,我们可以构造一个映射,包含与你的游戏会话名册对应的所有 PlayFab Entity ID。然后可以使用此映射将 `PartyEndpoint` 和 `PartyChatControl` 对象与其对应的 XBOX Live 用户关联。

<Note>
  每个 XBOX Live 用户 ID 只有在此 XBOX Live 用户已链接到 PlayFab 账户时才会映射到 PlayFab Entity ID。首次为给定 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 Authentication Library](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 Authentication Library 文档。](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-CN/services/playfab/multiplayer/networking/party-xboxlive-relnotes.md)
- [适用于 Unreal 的 Azure PlayFab Party Online Subsystem](/zh-CN/build/console-features/networking/game-mesh/party-unreal-online-subsystem.md)
- [PlayFab Party XBOX Live 帮助库错误代码](/zh-CN/services/playfab/multiplayer/networking/xblreference/partyxblerrors.md)
- [适用于 Unity 的 Azure PlayFab Party SDK](/zh-CN/build/console-features/networking/game-mesh/party-unity-plugin.md)
- [适用于 XBOX 服务标题的 PlayFab 集成](/zh-CN/services/xbox-services/playfab-integration.md)
