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

# Matchmaking SDK 快速入门

> PlayFab Multiplayer SDK 匹配客户端流程的快速入门演练:初始化库、提交工单并接收对局结果。

本快速入门指南将引导你完成使用 PlayFab Multiplayer SDK 为游戏添加匹配功能的整个流程。

本教程演示如何向特定队列提交工单以查找对局。一个队列可能对应一种游戏模式或多种游戏模式(例如,同一队列中的“夺旗”模式和“山丘之王”模式)。

匹配服务负责在队列中的工单之间寻找对局。找到对局后,你的游戏必须处理将玩家连接在一起以进行游戏。

<Note>
  PlayFab Multiplayer SDK 还提供了 PlayFab Lobby 的 API。 \* 有关 C++ API 的更多信息,请参阅 [Lobby SDK 快速入门](/services/playfab/multiplayer/lobby/lobby-getting-started) \* 有关 Unity API 的更多信息,请参阅 [Unity 快速入门](/services/playfab/multiplayer/lobby/lobby-matchmaking-sdks/multiplayer-unity-sdk-getting-started) \* 有关 Unreal API 的更多信息,请参阅 [Unreal 快速入门](/services/playfab/multiplayer/networking/party-unreal-engine-oss-quickstart)
</Note>

## 先决条件

你需要一个 [PlayFab 帐户](https://developer.playfab.com)才能使用 PlayFab Matchmaking。有关创建帐户的说明,请参阅[快速入门:Game Manager](/services/playfab/live-service-management/gamemanager/quickstart)。

## 在 Game Manager 中配置匹配队列

该库会将为在 Game Manager 中配置的队列创建工单的用户匹配在一起。有关如何设置的详细信息,请参阅[配置匹配队列](/services/playfab/multiplayer/matchmaking/config-queues)。

## 下载并设置 PlayFab Multiplayer SDK

下载适合你平台的 [C/C++ SDK](/services/playfab/multiplayer/lobby/lobby-matchmaking-sdks/lobby-matchmaking-sdks),并将提供程序头文件和库文件集成到你的 build 中。

<Note>
  本快速入门主要关注使用 C/C++ SDK。有关 Unity 和 Unreal 接口,请参阅以下文章: \* [Unity 快速入门](/services/playfab/multiplayer/lobby/lobby-matchmaking-sdks/multiplayer-unity-sdk-getting-started) \* [Unreal 快速入门](/services/playfab/multiplayer/networking/party-unreal-engine-oss-quickstart)
</Note>

## 登录 PlayFab 实体

要使用 PlayFab Lobby SDK,你需要使用 PlayFab 实体密钥和实体令牌对客户端进行身份验证。通过 [LoginWithCustomId](https://learn.microsoft.com/en-us/rest/api/playfab/client/authentication/login-with-custom-id) REST API 登录来获取一对 PlayFab 实体密钥和令牌。此 API 也可以通过 [PlayFab REST SDK](/services/playfab/sdks/playfab-sdk-intro) 以 C/C++ 投影方式使用。

<Note>
  LoginWithCustomId 是快速开始使用 PlayFab 功能的一种方式,但并不适合作为你正式发布时使用的登录机制。有关登录指南,请参阅[登录基础与最佳实践](/services/playfab/identity/player-identity/login/login-basics-best-practices)。
</Note>

## 初始化 PlayFab Multiplayer SDK

按照以下基本步骤初始化 PlayFab Multiplayer SDK:

1. 通过调用 [PFMultiplayerInitialize](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmultiplayer/functions/pfmultiplayerinitialize) 初始化 SDK
2. 通过调用 [PFMultiplayerSetEntityToken](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmultiplayer/functions/pfmultiplayersetentitytoken) 设置库代表玩家使用的实体密钥和令牌。

```cpp theme={null}
static PFMultiplayerHandle g_pfmHandle = nullptr;
...
...
HRESULT hr = S_OK;

// Initialize the PFMultiplayer library.
hr = PFMultiplayerInitialize(titleId, &g_pfmHandle);
if (FAILED(hr))
{
    // handle initialize failure
}

// Set an entity token for a local user. The token is used to authenticate PlayFab operations on behalf of this user. 
// Tokens can expire, and this API token should be called again when this token is refreshed.
hr = PFMultiplayerSetEntityToken(g_pfmHandle, localUserEntity, entityToken);
if (FAILED(hr))
{
    // handle set entity token failure
}
```

## 创建匹配工单

使用 [PFMultiplayerCreateMatchmakingTicket](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/functions/pfmultiplayercreatematchmakingticket) 创建匹配工单,在其中指定应作为对局一部分的所有本地用户,以及你希望与这些用户关联的任何属性。

此函数还接受一个 [PFMatchmakingTicketConfiguration](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/structs/pfmatchmakingticketconfiguration),你可以在其中指定工单对应的队列、工单的超时,以及要匹配到此工单中的任何远程用户。

### 单个本地用户的匹配

你可以通过调用 **PFMultiplayerCreateMatchmakingTicket** 为单个本地用户启动匹配。

```cpp theme={null}
const char* yourQueueName = ...; // This is the name of the queue you configured in Game Manager.

PFMatchmakingTicketConfiguration configuration{};
configuration.timeoutInSeconds = 120;
configuration.queueName = yourQueueName;

const char* attributes = "\"{\"color\":\"blue\", \"role\":\"tank\"}\"";

const PFMatchmakingTicket* ticket;
HRESULT hr = PFMultiplayerCreateMatchmakingTicket(
    g_pfmHandle,
    1, // number of local users
    localUserEntity,
    &attributes,
    &configuration,
    nullptr, // optional asyncContext
    &ticket);
RETURN_IF_FAILED(hr);
```

### 与一组远程用户的匹配

要开始与远程用户的组匹配,可将某个客户端视为组长。让组长使用 **PFMultiplayerCreateMatchmakingTicket** 创建工单,并通过 **configuration** 参数指定组中的其他用户。工单创建后,调用 **GetTicketId** 以获取工单 ID。通过外部机制(例如网络网格或共享的 PlayFab Lobby)将此 ID 发送给每个其他用户,并让每个客户端使用工单 ID 调用 **PFMultiplayerJoinMatchmakingTicketFromId** 来加入该匹配工单。工单状态在等待指定玩家加入时将为 **PFMatchmakingTicketStatus::WaitingForPlayers**,当所有玩家加入工单后将变为 **PFMatchmakingTicketStatus::WaitingForMatch**。

```cpp theme={null}
// Creating the ticket on the leader's client

const char* remoteMemberEntityId1 = ...;
const char* remoteMemberEntityId2 = ...;

std::vector<PFEntityKey> remoteMatchMemberEntityKeys;
remoteMatchMemberEntityKeys.push_back({ remoteMemberEntityId1, "title_player_account" });
remoteMatchMemberEntityKeys.push_back({ remoteMemberEntityId2, "title_player_account" });

const char* yourQueueName = ...; // This is the name of the queue you configured in Game Manager.

PFMatchmakingTicketConfiguration configuration{};
configuration.timeoutInSeconds = 120;
configuration.queueName = yourQueueName;
configuration.membersToMatchWithCount = 2; // number of remote members to match with
configuration.membersToMatchWith = remoteMatchMemberEntityKeys.data();

const char* attributes = "\"{\"color\":\"blue\", \"role\":\"tank\"}\"";

const PFMatchmakingTicket* ticket;
HRESULT hr = PFMultiplayerCreateMatchmakingTicket(
    g_pfmHandle,
    1, // number of local users
    localUserEntity,
    &attributes,
    &configuration,
    nullptr, // optional asyncContext
    &ticket);
RETURN_IF_FAILED(hr);

// Getting the ticket ID

PCSTR ticketId;
hr = PFMatchmakingTicketGetTicketId(ticket, &ticketId);
RETURN_IF_FAILED(hr);
```

```cpp theme={null}
// Joining the ticket on the other players' clients

const char* attributes = "\"{\"color\":\"blue\", \"role\":\"healer\"}\"";
const char* yourQueueName = ...; // This is the name of the queue you configured in Game Manager.

const PFMatchmakingTicket* ticket;
HRESULT hr = PFMultiplayerJoinMatchmakingTicketFromId(
    g_pfmHandle,
    1, // number of local users
    localUserEntity,
    &attributes,
    ticketId,
    yourQueueName,
    nullptr, // optional asyncContext
    &ticket);
```

### 多个本地用户的匹配

在与多个本地用户进行匹配时,不是向 **PFMultiplayerCreateMatchmakingTicket** 或 **PFMultiplayerJoinMatchmakingTicketFromId** 函数传入一个 **PFEntityKey**,而是需要传入一个密钥列表。同样,你需要为每个用户传入一个属性列表。每个列表条目位置应彼此对应。也就是说,属性列表中的第一个条目应是 **PFEntityKey** 列表中第一位玩家的属性。

```cpp theme={null}
const char* yourQueueName = ...; // This is the name of the queue you configured in Game Manager.

PFMatchmakingTicketConfiguration configuration{};
configuration.timeoutInSeconds = 120;
configuration.queueName = queueName;

std::vector<PFEntityKey> localMatchMemberEntityKeys{ ... };
std::vector<PCSTR> localMatchMemberAttributes{ ... };

const PFMatchmakingTicket* ticket;
HRESULT hr = PFMultiplayerCreateMatchmakingTicket(
    g_pfmHandle,
    static_cast<uint32_t>(localMatchMemberEntityKeys.size())
    localMatchMemberEntityKeys.data(),
    localMatchMemberAttributes.data(),
    &configuration,
    nullptr, // optional asyncContext
    &ticket);
RETURN_IF_FAILED(hr);
```

## 检查匹配工单的状态

必须通过调用 [PFMultiplayerStartProcessingMatchmakingStateChanges](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/functions/pfmultiplayerstartprocessingmatchmakingstatechanges) 来接收状态变更,并在处理完这些状态变更后调用 [PFMultiplayerFinishProcessingMatchmakingStateChanges](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/functions/pfmultiplayerfinishprocessingmatchmakingstatechanges),以检查工单的更新。

每当工单的状态发生变化时,SDK 都会返回一个 **TicketStatusChanged** 状态变更;当匹配完成时,SDK 会返回一个 **TicketCompleted** 状态变更。

### 使用 Matchmaking client SDK 的示例

```cpp theme={null}
HRESULT hrTicketError = S_OK;

uint32_t stateChangeCount;
const PFMatchmakingStateChange * const * stateChanges;
hr = PFMultiplayerStartProcessingMatchmakingStateChanges(g_pfmHandle, &stateChangeCount, &stateChanges);
RETURN_IF_FAILED(hr);

for (uint32_t i = 0; i < stateChangeCount; ++i)
{
    const PFMatchmakingStateChange& stateChange = *stateChanges[i];

    switch (stateChange.stateChangeType)
    {
        case PFMatchmakingStateChangeType::TicketStatusChanged:
        {
            const auto& ticketStatusChanged = static_cast<const PFMatchmakingTicketStatusChangedStateChange&>(stateChange);

            PFMatchmakingTicketStatus status;
            if (SUCCEEDED(PFMatchmakingTicketGetStatus(ticketStatusChanged.ticket, &status)))
            {
                printf("Ticket status is now: %i.\n", status);
            }

            break;
        }
        case PFMatchmakingStateChangeType::TicketCompleted:
        {
            const auto& ticketCompleted = static_cast<const PFMatchmakingTicketCompletedStateChange&>(stateChange);

            printf("PFMatchmaking completed with Result 0x%08x.\n", ticketCompleted.result);

            if (FAILED(ticketCompleted.result))
            {
                // On failure, we must record the HRESULT so we can return the state change(s) and then bail
                // out of this function.
                hrTicketError = ticketCompleted.result;
            }

            break;
        }
    }
}

hr = PFMultiplayerFinishProcessingMatchmakingStateChanges(g_pfmHandle, stateChangeCount, stateChanges);
RETURN_IF_FAILED(hr);

// Now that we've returned the state change(s), bail out if we detected ticket failure.
RETURN_IF_FAILED(hrTicketError);
```

## 获取对局

在收到 **PFMatchmakingStateChangeType::TicketCompleted** 状态变更后,调用 [PFMatchmakingTicketGetMatch](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/functions/pfmatchmakingticketgetmatch) 以获取对局的详细信息。这些详细信息将包含对局 ID、已匹配到一起的用户、对局的首选区域,以及与对局关联的大厅编排字符串。

从 **PFMatchmakingMatchDetails** 结构中检索所需的信息后,应使用 [PFMultiplayerDestroyMatchmakingTicket](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/functions/pfmultiplayerdestroymatchmakingticket) 销毁工单。

### 使用 Matchmaking client SDK 的示例

```cpp theme={null}
const PFMatchmakingMatchDetails* match;
HREULT hr = PFMatchmakingTicketGetMatch(ticket, &match);
RETURN_IF_FAILED(hr);

std::string matchId = match->matchId;
std::string lobbyArrangementString = match->lobbyArrangementString;

PFMultiplayerDestroyMatchmakingTicket(g_pfmHandle, ticket);
```

## 取消匹配工单

如果你的客户端出于某种原因希望在 `PFMatchmakingTicketConfiguration` 中设置的超时之前取消匹配过程,请使用工单句柄调用 [PFMatchmakingTicketCancel](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/functions/pfmatchmakingticketcancel)。

调用此 API 并不保证工单会被取消。工单可能在取消操作被处理之前已经完成,或者取消请求可能因网络或服务错误而失败。如果希望在继续之前确认工单取消已完成,你仍可以处理匹配状态变更以获取工单的结果。否则,你可以立即调用 [PFMultiplayerDestroyMatchmakingTicket](/services/playfab/multiplayer/lobby/playfabmultiplayerreference-cpp/pfmatchmaking/functions/pfmultiplayerdestroymatchmakingticket)。

### 使用 Matchmaking client SDK 的示例

```cpp theme={null}
HRESULT hr = PFMatchmakingTicketCancel(ticket);

PFMultiplayerDestroyMatchmakingTicket(g_pfmHandle, ticket);
```

## (可选)将玩家连接到 Lobby 中

玩家匹配后,他们可以一起加入一个大厅。已匹配工单的 **PFMatchmakingMatchDetails** 包含一个 **lobbyArrangementString** 字段,可用于将用户加入同一个 Lobby。

有关 Lobby 与 Matchmaking 如何协同工作的更多信息,请参阅[结合使用 lobby 与 matchmaking](/services/playfab/multiplayer/lobby/lobby-and-matchmaking)。

有关 PlayFab Lobbies 的更多信息,请参阅 [PlayFab Lobby 概览](/services/playfab/multiplayer/lobby)。

### 使用 Matchmaking client SDK 的示例

```cpp theme={null}
const PFMatchmakingMatchDetails* match;
HREULT hr = PFMatchmakingTicketGetMatch(ticket, &match);
RETURN_IF_FAILED(hr);

std::string matchId = match->matchId;
std::string lobbyArrangementString = match->lobbyArrangementString;

PFMultiplayerDestroyMatchmakingTicket(g_pfmHandle, ticket);

PFLobbyHandle lobby;
RETURN_IF_FAILED_HR(PFMultiplayerJoinArrangedLobby(
    m_pfmHandle,
    &joiningUser,
    lobbyArrangementString,
    &joinConfig,
    nullptr, // optional asyncContext
    &lobby));
```

## 结论

使用本快速入门,你现在应该在游戏中拥有一个成功的匹配流程。此外,你还应考虑以下方面:

* 你的游戏如何处理组的形成。
* 玩家等待对局时你的游戏显示什么。
* 如何处理失败和重试。

## 另请参阅

* [Lobby SDK](/services/playfab/multiplayer/lobby/lobby-getting-started)


## Related topics

- [Lobby SDK 快速入门](/zh-CN/services/playfab/multiplayer/lobby/lobby-getting-started.md)
- [PlayFab Lobby 和 Matchmaking SDK](/zh-CN/services/playfab/multiplayer/lobby/lobby-matchmaking-sdks/lobby-matchmaking-sdks.md)
- [Matchmaking 快速入门](/zh-CN/services/playfab/multiplayer/matchmaking/quickstart.md)
- [Multiplayer Unity 插件快速入门](/zh-CN/services/playfab/multiplayer/lobby/lobby-matchmaking-sdks/multiplayer-unity-plugin-quickstart.md)
- [快速入门](/zh-CN/services/playfab/economy-monetization/economy-v2/quickstart.md)
