Xbox Live 帮助程序库概述

PlayFab Party 的 Xbox Live 帮助程序库旨在帮助使用 PlayFab Party 满足 Xbox Live 策略 与通信(XR-015 和 XR-045)相关的游戏。 上提供了 Xbox Live 帮助程序 Nuget.org

与 PlayFab Party 库的兼容性

我们虽然努力尽量减少 API 中的中断更改,但对 PlayFab Party API 的一些更改可能会导致 Xbox Live Helper 库返回错误值。 请参阅下表,确保库的版本兼容。

Xbox Live 帮助程序库
版本
PlayFab Party 版本
1.0.1
PlayFab Party 版本
1.3.0+
1.0.1
1.1.0
1.2.0
1.2.5

跟踪 Xbox Live 用户

必须明确告知 PlayFab Party Xbox Live 帮助程序库当前参与群会话的 Xbox Live 用户。 建议游戏通过侦听其 多人游戏会话文档 的更改,并通过 PartyXblManager::CreateLocalChatUserPartyXblManager::CreateRemoteChatUser在 Xbox Live 帮助程序库中反映该名单来通知库。

对于本地用户:

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 用户已存在 PartyLocalLocalLocControl,则可以通过 SetCustomContext 方法将其与此 PartyXblLocalLocalLocUser 关联。

    localChatControl->SetCustomContext(localChatUser);
    localChatUser->SetCustomContext(localChatControl);

否则,可以使用这个新的 PartyXblLocalChatUser 生成聊天控件并将其关联。 请参阅 Creating PartyLocalChatControls from PartyXblLocalChatUsers,了解详细信息。

远程用户:

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 用户已存在 PartyCusControl,可通过 SetCustomContext 方法将其与此 PartyXblTextUser 关联。

    remoteChatControl->SetCustomContext(remoteChatUser);
    remoteChatUser->SetCustomContext(remoteChatControl);

请记住,对会话文档的更新和远程聊天控件列表的更新可能排序错误,且在处理远程聊天控件的 PartyChatControlCreatedStateChange 更新时,您可能需要类似的关联逻辑。

对于本地和远程聊天用户,请记住,核心方库通过 PlayFab 实体 ID 识别用户和聊天控件,其中 Xbox Live 帮助程序库标识具有 Xbox 用户 ID 的聊天用户。 因此,这两者间的建议经常是必需的。 有关详细信息,请参阅 Xbox Live 用户 ID 和 PlayFab 实体 ID 之间的映射

从 PartyXblLocalChatUsers 创建 PartyLocalChatControers

PartyXblLocalChatUser对象通常仅在与方库中的 PartyLocalUserPartyLocalChatControl 对象关联时有用。 生成对象PartyLocalUserPartyLocalChatControl对象时,需要标题来登录其用户以播放Fab 并检索其用户entityIdtitlePlayerEntityToken。 可通过 PlayFab CPP SDK执行登录,但如果标题想要使用 Xbox Live 凭据登录 PlayFab,它们可能会使用 PartyXblManager::LoginToPlayFab 来避免提取额外的相关性。

以下示例显示 Xbox Live Helper 库如何帮助您从 PartyXblLocalChatUser 对象创建 PartyLocalUserPartyLocalChatControl 对象。 有关创建PartyXblLocalChatUser对象的详细信息,请参阅跟踪 Xbox Live 用户

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

调用PartyXblManager::LoginToPlayFab后不久,您将收到一个PartyXblLoginToPlayFabCompletedStateChange包含登录操作结果的消息。

    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 用户某些与方聊天会话相关的辅助功能首选项。 标题可以使用此信息,通过同时启用部分方可访问性功能,为播放器提供更好的体验。

    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 限制最严格的策略,Xbox Live 帮助程序库有助于实现这一点。 每当此值发生更改时, PartyXblRequiredChatPermissionInfoChangedStateChange 都会生成一个数值。 可通过 PartyChatPermissionOptions 呼叫获取已更新 PartyXblLocalChatUser::GetRequiredChatPermissionInfo()

    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 隐私服务进行通信,跟踪每个远程聊天用户(相对于每个本地聊天用户)的隐私和权限设置。 此外,库将通过订阅 实时活动 更新,侦听对这些设置的更改。 当添加了新的远程聊天用户,或者当本地聊天用户和现有远程聊天用户之间的隐私和特权关系发生更改时,将生成 PartyXblRequiredChatPermissionInfoChangedStateChange ,通知你 PartyChatPermissionOptions 值现已可用。

    // 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 当前对此聊天权限没有限制。
确定 确定本地用户的通信权限和隐私设置时,通信将受到限制
权限 由于本地用户的通信权限,通信将受到限制。
隐私 由于本地用户相对于目标聊天用户的隐私设置,通信将受到限制。
InvalidTargetUser 由于 Xbox Live 服务未将目标用户识别为有效,通信将受到限制。
XboxLiveServiceError 由于 Xbox Live 服务的问题,无法成功确定所需的聊天权限。
未知错误 由于未知内部错误,无法成功确定所需的聊天权限。

尊重跨网络通信权限

支持跨网络播放以及 Xbox Live 和非 Xbox Live 播放器之间的通信的标题需要先检查通信权限,才能允许这些播放器之间的通信。 Xbox Live 帮助程序库通过 PartyXblLocalChatUser::GetCrossNetworkCommunicationPrivacySetting()提供此信息。 此方法返回一个 PartyXblCrossNetworkCommunicationPrivacySetting 值的 3 个可能数值的数值:

PartyXblCrossNetworkCommunicationPrivacySetting 解释
允许 此 Xbox Live 用户的权限设置为允许与所有跨网络播放器通信。
好友 此 Xbox Live 用户的权限设置为仅允许与跨网络好友通信。
禁止 此 Xbox Live 用户的权限不允许与跨网络播放器进行任何通信。
    PartyXblCrossNetworkCommunicationPrivacySetting crossNetworkSetting;
    localChatUser->GetCrossNetworkCommunicationPrivacySetting(&crossNetworkSetting);

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

有关 XR-015 及其关于跨网络播放和通信详细信息,可在 此处了解

Xbox Live 用户 ID 和 PlayFab 实体 ID 之间的映射

使用 PlayFab Party 的许多 Xbox Live 游戏需要在 Xbox Live 用户 ID(在整个 Xbox Live 生态系统中使用)和 PlayFab 实体 ID(由 PlayFab Party 使用)之间进行转换。 使用 PartyXblManager::GetEntityIdsFromXboxLiveUserIds,标题可以检索与给定 Xbox Live 用户 ID 列表对应的 PlayFab 实体 ID 列表。 通过使用外部名册服务,例如多人游戏会话目录,游戏应该已经拥有 Xbox Live 用户 ID 列表。 通过将名单中的 Xbox Live 用户 ID 与其 PlayFab 实体 ID 关联,我们可以构建与游戏会话名单对应的所有 PlayFab 实体 ID 的映射。 此映射随后可用于将对象 PartyEndpointPartyChatControl Xbox Live 用户关联。

注意

如果每个 Xbox Live 用户 ID 已链接到 PlayFab 帐户,则每个 Xbox Live 用户 ID 将仅映射到一个 PlayFab 实体 ID。 首次为给定 Xbox 用户调用 PlayFab 帐户 PartyXblManager::LoginToPlayFab 自动创建并链接该帐户。 或者,PlayFab SDK 的消费者可以使用 LoginWithXbox API 实现相同的结果。

本地PartyXblLocalChatUser将用于与 PlayFab 进行身份验证。 如果用户以前未登录到 PlayFab 并调用 PartyXblManager::LoginToPlayFab,则 Xbox Live Helper 库需要在后台对该用户进行身份验证。

    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包含操作结果的消息。 此结果可用于构造或更新映射。

    // 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);

使用此类映射,当方对象代表 Xbox Live 用户时,标题可识别。

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 身份验证库 (XAL) 来满足这些请求。 将此操作卸载到标题可确保它完全控制任何 UI 处理和与用户身份验证相关联的同意提示。

    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 身份验证库文档。

拥有令牌和签名后,可以通过呼叫 PartyXblManager::CompleteGetTokenAndSignatureRequest() ,与状态更改为标题提供的相同 correlationId ,向 Xbox Live 帮助程序库提供这些令牌和签名。