> ## 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.

# 异步 Lobby 和 Matchmaking C++ SDK 操作指南

> 了解 PlayFab Lobby 和 Matchmaking C++ SDK 中的异步操作、轮询和状态变更通知,以及针对多人游戏的最佳实践。

# 异步操作与通知

对于可能较慢或计算量大的操作,PlayFab Lobby 和 Matchmaking SDK 提供了异步 API。异步 API 使你能够在主线程中启动昂贵或较慢的操作,并在你选择的线程上轮询这些操作的完成情况。此轮询机制也用于将 SDK 更新的异步通知传递到你的游戏代码。本页概述 PlayFab Lobby 和 Matchmaking SDK 的异步 API 模式以及针对它们进行编程的最佳实践。

## 基本 API 模式

在 PlayFab Lobby 和 Matchmaking SDK 中,需要了解两种类型的异步 API 模式:

1. [异步操作](#asynchronous-operations)
2. [异步通知](#asynchronous-notifications)

### 异步操作

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

1. 对你选择的适当异步 API 进行常规方法调用。你可能会用到的常见异步操作包括:
   * [PFMultiplayerCreateAndJoinLobby](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pflobby/functions/pfmultiplayercreateandjoinlobby)。
   * [PFMultiplayerJoinLobby](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pflobby/functions/pfmultiplayerjoinlobby)。
   * [PFLobbyPostUpdate](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pflobby/functions/pflobbypostupdate)。
   * [PFMultiplayerCreateMatchmakingTicket](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/functions/pfmultiplayercreatematchmakingticket)。

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

<Warning>
  异步 API 调用的同步返回值**不会**告知你该操作是否已成功完成。有关同步与异步错误的更多信息,请查阅 SDK 的[错误处理文档](/services/playfab/multiplayer/lobby/lobby-and-matchmaking-client-sdk-errors#synchronous-vs-asynchronous-errors)。
</Warning>

3. 通过查找由 [PFMultiplayerStartProcessingLobbyStateChanges()](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pflobby/functions/pfmultiplayerstartprocessinglobbystatechanges) 或 [PFMultiplayerStartProcessingMatchmakingStateChanges](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/functions/pfmultiplayerstartprocessingmatchmakingstatechanges) 提供的关联操作 “完成状态变更” 来轮询异步操作的完成情况。**PFMultiplayerCreateAndJoinLobby()** 关联的 “完成状态变更” 的一个示例是 [PFLobbyCreateAndJoinLobbyCompletedStateChange](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pflobby/structs/pflobbycreateandjoinlobbycompletedstatechange)。有关 “状态变更” 是什么以及它们的工作方式的更详细信息,请参见[状态变更](#state-changes)部分。

4. 检查完成状态变更的 **result** 值,以确定操作是成功还是失败。有关这些错误值的更多详细信息,请参阅 SDK 的[错误处理文档](/services/playfab/multiplayer/lobby/lobby-and-matchmaking-client-sdk-errors#synchronous-vs-asynchronous-errors)。

### 异步通知

某些功能会生成有关 Lobby 和 Matchmaking SDK 变更的异步通知。

常见通知包括:

1. [大厅更新通知](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pflobby/structs/pflobbyupdatedstatechange)。
2. [大厅断开连接通知](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pflobby/structs/pflobbydisconnectingstatechange)。
3. [匹配票据状态变更通知](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/structs/pfmatchmakingticketstatuschangedstatechange)。

这些异步通知将由 SDK 作为 “状态变更” 通过 [PFMultiplayerStartProcessingLobbyStateChanges()](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pflobby/functions/pfmultiplayerstartprocessinglobbystatechanges) 和 [PFMultiplayerStartProcessingMatchmakingStateChanges](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/functions/pfmultiplayerstartprocessingmatchmakingstatechanges) 提供给你。

有关 “状态变更” 是什么以及它们的工作方式的更详细信息,请参见[状态变更](#state-changes)部分。

## 状态变更

Lobby 和 Matchmaking SDK 的异步 API 模型是围绕 [PFLobbyStateChange](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pflobby/structs/pflobbystatechange) 和 [PFMatchmakingStateChange](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/structs/pfmatchmakingstatechange) 结构构建的。**PFLobbyStateChange** 通知你关于大厅子系统的变更,**PFMatchmakingStateChange** 通知你关于匹配子系统的变更。

这些 “状态变更” 是来自 SDK 事件的异步通知。这些通知在内部进行排队,你通过调用 [PFMultiplayerStartProcessingLobbyStateChanges()](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pflobby/functions/pfmultiplayerstartprocessinglobbystatechanges) 和 [PFMultiplayerStartProcessingMatchmakingStateChanges](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/functions/pfmultiplayerstartprocessingmatchmakingstatechanges) 来处理它们。这些函数会以列表形式返回所有已排队的状态变更(对于各自的 API 子系统),你可以逐个进行迭代处理。每个状态变更都有一个对应的 *stateChangeType* 字段,可以检查该字段以确定所收到的是哪种具体的状态变更通知。一旦知道收到的是哪种状态变更,你就可以将通用的 **PFLobbyStateChange** 或 **PFMatchmakingStateChange** 结构强制转换为更具体类型的状态变更结构,以检查该事件的具体数据。

通常,状态变更处理是通过一个简单的 switch 语句实现的,该语句将每个状态变更委托给一个处理程序。

一旦通过 **PFMultiplayerStartProcessingLobbyStateChanges** 或 **PFMultiplayerStartProcessingMatchmakingStateChanges** 处理完状态变更列表,就必须分别将其返回到 [PFMultiplayerFinishProcessingMatchmakingStateChanges()](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/functions/pfmultiplayerfinishprocessingmatchmakingstatechanges) 或 [PFMultiplayerFinishProcessingMatchmakingStateChanges()](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/functions/pfmultiplayerfinishprocessingmatchmakingstatechanges)。

```cpp theme={null}
//
// 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 时,经常需要将多个异步操作作为更大异步流的一部分按顺序运行。

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

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

对于较复杂的流程和游戏逻辑,这种序列化模式可能是合适的。但是,对于较简单的流程,SDK 提供了一种旨在简化游戏代码的替代方式:

SDK 中的许多异步 API 支持在前一个操作完全完成之前对依赖操作进行排队。以前面的例子来说,你可以在还没有看到大厅成功创建之前就为其发送邀请。

实际上,排队允许你将一组异步操作捆绑在一起,一次性启动它们,并将错误处理合并到单个失败点。

```cpp theme={null}
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 争用。

Lobby 和 Matchmaking SDK 允许你通过[控制线程亲和性](#controlling-thread-affinity)来控制异步工作如何运行

### 控制线程亲和性

默认情况下,异步 SDK 工作是在经过精心控制的后台线程上完成的。一些游戏需要粗粒度地控制*后台线程被调度到何处*,以避免 CPU 争用。

对于这些游戏,Lobby 和 Matchmaking SDK 提供了 [PFMultiplayerSetThreadAffinityMask()](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmultiplayer/functions/pfmultiplayersetthreadaffinitymask)。在受支持的平台上,该函数允许你限制 SDK 后台线程可以使用的 CPU 核心。这样一来,你可以保证某些核心保留给你自己的 CPU 工作负载,不受任何争用影响。


## Related topics

- [处理 Lobby 和 Matchmaking C++ SDK 错误](/zh-CN/services/playfab/multiplayer/lobby/lobby-and-matchmaking-client-sdk-errors.md)
- [操作指南](/zh-CN/services/xbox-services/community/reputation/how-to/index.md)
- [异步任务队列设计](/zh-CN/build/core-features/common/async/async-task-queue-design.md)
- [PlayFab Lobby 和 Matchmaking SDK](/zh-CN/services/playfab/multiplayer/lobby/lobby-matchmaking-sdks/lobby-matchmaking-sdks.md)
- [PlayFab Lobby 和 Matchmaking SDK 中的诊断跟踪](/zh-CN/services/playfab/multiplayer/lobby/lobby-matchmaking-sdks/lobby-matchmaking-logging.md)
