异步操作和通知

对于速度缓慢或计算成本高昂的操作,PlayFab 大厅和匹配 SDK 会公开异步 API。 异步 API 使你能够在主线程上开始昂贵或缓慢的操作,并在所选线程上轮询这些操作的完成情况。 同样的轮询机制还用于向游戏代码提供 SDK 更新的异步通知。 本页概述了 PlayFab 大厅和匹配 SDK 的异步 API 模式和针对它们进行编程的最佳做法。

基本 API 模式

PlayFab 大厅和匹配 SDK 中需要注意两种类型的异步 API 模式:

  1. 异步操作
  2. 异步通知

异步操作

使用 SDK 的异步 API 非常简单。 启动和完成异步操作的一般模式如下:

  1. 定期对所选的相应异步 API 进行方法调用。 可能使用的常见异步操作包括:

  2. 使用 SUCCEEDED()FAILED() 宏检查 API 的 HRESULT 返回值。 此同步返回的值将告知操作是否已成功启动。

警告

异步 API 调用的同步返回值会告知操作是否已成功完成。 有关同步与异步错误的更多信息,请查看 SDK 的错误处理文档

  1. 通过查找由 PFMultiplayerStartProcessingLobbyStateChanges()PFMultiplayerStartProcessingMatchmakingStateChanges 提供的关联操作的“完成状态更改”来轮询异步操作的完成情况。 与 PFMultiplayerCreateAndJoinLobby() 关联的“完成状态更改”示例是 PFLobbyCreateAndJoinLobbyCompletedStateChange。 有关什么是“状态更改”及其工作原理的更详细信息,请参阅的状态更改部分。

  2. 检查完成状态更改的结果值,从而确定操作是成功还是失败。 有关这些错误值的更多详细信息,请参阅 SDK 的错误处理文档

异步通知

某些功能将生成有关大厅和匹配 SDK 更改的异步通知。

常见通知包括:

  1. 大厅更新通知
  2. 大厅断开连接通知
  3. 匹配票证状态更改通知

这些异步通知将由 SDK 作为“状态更改”通过 PFMultiplayerStartProcessingLobbyStateChanges()PFMultiplayerStartProcessingMatchmakingStateChanges 提供给你。

有关什么是“状态更改”及其工作原理的更详细信息,请参阅的状态更改部分。

状态更改

大厅和匹配 SDK 的异步 API 模型是围绕 PFLobbyStateChangePFMatchmakingStateChange 结构构建的。 PFLobbyStateChanges 会通知你大厅子系统的更改,PFMatchmakingStateChanges 则会通知你有关匹配子系统的更改。

这些“状态更改”是 SDK 的事件的异步通知。 这些通知在内部排队,通过调用 PFMultiplayerStartProcessingLobbyStateChanges()PFMultiplayerStartProcessingMatchmakingStateChanges 来处理这些通知。 这些函数将以列表的形式返回所有排队的状态更改(针对其各自的 API 子系统),可以单独迭代和处理这些更改。 每个状态更改都有一个相应的 stateChangeType 字段,可以检查该字段以确定要接收有关哪个特定状态更改的通知。 知道获得哪种状态更改通知后,就可以将通用 PFLobbyStateChangePFMatchmakingStateChange 结构转换为更具体类型的状态更改结构,以检查该事件的特定数据。

通常,状态更改处理作为一个简单的 switch 语句来实现,该语句将每个状态更改委托给处理程序。

PFMultiplayerStartProcessingLobbyStateChangesPFMultiplayerStartProcessingMatchmakingStateChanges 处理状态更改列表后,必须分别将其返回到 PFMultiplayerFinishProcessingMatchmakingStateChanges()PFMultiplayerFinishProcessingMatchmakingStateChanges()

//
// Process Lobby state changes
//
uint32_t lobbyStateChangeCount;
const PFLobbyStateChange * const * lobbyStateChanges;
HRESULT hr = PFMultiplayerStartProcessingLobbyStateChanges(m_pfmHandle, &lobbyStateChangeCount, &lobbyStateChanges);
if (FAILED(hr))
{
    return hr;
}

for (uint32_t i = 0; i < lobbyStateChangeCount; ++i)
{
    const PFLobbyStateChange* stateChange = lobbyStateChanges[i];
    switch (stateChange->stateChangeType)
    {
        case PFLobbyStateChangeType::CreateAndJoinLobbyCompleted:
        {
            HandleCreateAndJoinLobbyCompleted(
                static_cast<const PFLobbyCreateAndJoinLobbyCompletedStateChange*>(stateChange));
            break;
        }
        // add other state change handlers here
    }
}

hr = PFMultiplayerFinishProcessingLobbyStateChanges(m_pfmHandle, lobbyStateChangeCount, lobbyStateChanges);
if (FAILED(hr))
{
    return hr;
}

//
// Process Match state changes
//
uint32_t matchStateChangeCount;
const PFMatchmakingStateChange * const * matchStateChanges;
hr = PFMultiplayerStartProcessingMatchmakingStateChanges(m_pfmHandle, &matchStateChangeCount, &matchStateChanges);
if (FAILED(hr))
{
    return hr;
}

for (uint32_t i = 0; i < matchStateChangeCount; ++i)
{
    const PFMatchmakingStateChange* stateChange = matchStateChanges[i];
    switch (stateChange->stateChangeType)
    {
        case PFMatchmakingStateChangeType::TicketStatusChanged:
        {
            HandleMatchmakingTicketStatusChanged(
                static_cast<const PFMatchmakingTicketStatusChangedStateChange*>(stateChange));
            break;
        }
        // add other state change handlers here
    }
}

hr = PFMultiplayerFinishProcessingMatchmakingStateChanges(m_pfmHandle, matchStateChangeCount, matchStateChanges);
if (FAILED(hr))
{
    return hr;
}

异步操作上下文

每个异步 API 都包含一个 void* asyncContext 参数。 此值是传递参数,由 PFMultiplayerStartProcessingLobbyStateChanges()PFMultiplayerStartProcessingMatchmakingStateChanges() 提供后,将在此 API 调用的关联完成状态更改上进行设置。

此值提供了一种机制,用于将任意指针大小的上下文附加到异步 API 调用。 这些上下文可在许多方案中使用,包括:

  1. 将特定于游戏的数据与 SDK 调用相关联
  2. 将多个异步操作与共享标识符绑定在一起

使用 SDK 不需要这些异步上下文,但可以使某些游戏逻辑更易于写入。

操作队列

通常,在使用异步 API 时,多个异步操作需要作为较大异步流的一部分按顺序运行。

在在大厅和配对 SDK 中,一个示例是创建大厅并向好友发送该大厅的邀请。 序列化后,此流如下所示:

  1. 调用 PFMultiplayerCreateAndJoinLobby() 创建并加入 PlayFab 大厅。
  2. 等待 PFLobbyCreateAndJoinLobbyCompletedStateChange 反映大厅已成功创建并加入。
  3. 为每个受邀好友调用 PFLobbySendInvite()
  4. 等待 PFLobbySendInviteCompletedStateChange 反映邀请已成功发送。

对于更复杂的流和游戏逻辑,此序列化模式可以适用。 但是,对于更简单的流,SDK 提供了一种用于简化游戏代码的替代方法:

SDK 中的许多异步 API 都支持在上一个操作完全完成之前对依赖操作进行队列。 在前面的示例中,可以在看到大厅成功创建之前发送大厅邀请。

实际上,队列允许将一组异步操作捆绑在一起,同时将其全部启动,并将错误处理合并到单个故障点。

PFLobbyHandle newLobby;
HRESULT hr = PFMultiplayerCreateAndJoinLobby(m_pfmHandle, myPlayerEntityId, newLobbyConfiguration, nullptr, nullptr, &newLobby);
if (SUCCEEDED(hr))
{
    for (size_t i = 0; SUCCEEDED(hr) && i < friends.size(); ++i)
    {
        hr = PFLobbySendInvite(m_pfmHandle, myPlayerEntityId, friends[i], nullptr);
    }

    if (SUCCEEDED(hr))
    {
        m_lobby = newLobby;
        Log("Created lobby and invited %zu friends!", friends.size());
    }
    else
    {
        // For the purposes of demonstration, we could have a policy that we shouldn't bother with any lobbies where
        // we couldn't invite all of our friends.
        (void) PFLobbyLeave(newLobby, myPlayerEntityId, nullptr);
    }
}

控制异步工作

游戏有时需要控制在何处执行异步工作,以避免库与其游戏的核心 CPU 工作负载之间的 CPU 争用。

使用大厅和匹配 SDK,可以通过控制线程相关性来控制异步工作的运行方式

控制线程相关性

默认情况下,异步 SDK 工作是在精心控制的后台线程上完成的。 某些游戏需要大致控制这些后台线程的计划运行位置,以避免 CPU 争用。

对于这些游戏,大厅和匹配 SDK 提供 PFMultiplayerSetThreadAffinityMask()。 在受支持的平台上,这让你可以限制哪些 CPU 核心将用于 SDK 的后台线程。 这样,你就可以保证为自己的 CPU 工作负载保留某些核心,而不会出现任何争用。