异步操作和通知
对于速度缓慢或计算成本高昂的操作,PlayFab 大厅和匹配 SDK 会公开异步 API。 异步 API 使你能够在主线程上开始昂贵或缓慢的操作,并在所选线程上轮询这些操作的完成情况。 同样的轮询机制还用于向游戏代码提供 SDK 更新的异步通知。 本页概述了 PlayFab 大厅和匹配 SDK 的异步 API 模式和针对它们进行编程的最佳做法。
基本 API 模式
PlayFab 大厅和匹配 SDK 中需要注意两种类型的异步 API 模式:
异步操作
使用 SDK 的异步 API 非常简单。 启动和完成异步操作的一般模式如下:
定期对所选的相应异步 API 进行方法调用。 可能使用的常见异步操作包括:
使用 SUCCEEDED() 或 FAILED() 宏检查 API 的 HRESULT 返回值。 此同步返回的值将告知操作是否已成功启动。
警告
异步 API 调用的同步返回值不会告知操作是否已成功完成。 有关同步与异步错误的更多信息,请查看 SDK 的错误处理文档。
通过查找由 PFMultiplayerStartProcessingLobbyStateChanges() 或 PFMultiplayerStartProcessingMatchmakingStateChanges 提供的关联操作的“完成状态更改”来轮询异步操作的完成情况。 与 PFMultiplayerCreateAndJoinLobby() 关联的“完成状态更改”示例是 PFLobbyCreateAndJoinLobbyCompletedStateChange。 有关什么是“状态更改”及其工作原理的更详细信息,请参阅的状态更改部分。
检查完成状态更改的结果值,从而确定操作是成功还是失败。 有关这些错误值的更多详细信息,请参阅 SDK 的错误处理文档。
异步通知
某些功能将生成有关大厅和匹配 SDK 更改的异步通知。
常见通知包括:
这些异步通知将由 SDK 作为“状态更改”通过 PFMultiplayerStartProcessingLobbyStateChanges() 和 PFMultiplayerStartProcessingMatchmakingStateChanges 提供给你。
有关什么是“状态更改”及其工作原理的更详细信息,请参阅的状态更改部分。
状态更改
大厅和匹配 SDK 的异步 API 模型是围绕 PFLobbyStateChange 和 PFMatchmakingStateChange 结构构建的。 PFLobbyStateChanges 会通知你大厅子系统的更改,PFMatchmakingStateChanges 则会通知你有关匹配子系统的更改。
这些“状态更改”是 SDK 的事件的异步通知。 这些通知在内部排队,通过调用 PFMultiplayerStartProcessingLobbyStateChanges() 和 PFMultiplayerStartProcessingMatchmakingStateChanges 来处理这些通知。 这些函数将以列表的形式返回所有排队的状态更改(针对其各自的 API 子系统),可以单独迭代和处理这些更改。 每个状态更改都有一个相应的 stateChangeType 字段,可以检查该字段以确定要接收有关哪个特定状态更改的通知。 知道获得哪种状态更改通知后,就可以将通用 PFLobbyStateChange 或 PFMatchmakingStateChange 结构转换为更具体类型的状态更改结构,以检查该事件的特定数据。
通常,状态更改处理作为一个简单的 switch 语句来实现,该语句将每个状态更改委托给处理程序。
从 PFMultiplayerStartProcessingLobbyStateChanges 或 PFMultiplayerStartProcessingMatchmakingStateChanges 处理状态更改列表后,必须分别将其返回到 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 调用。 这些上下文可在许多方案中使用,包括:
- 将特定于游戏的数据与 SDK 调用相关联
- 将多个异步操作与共享标识符绑定在一起
使用 SDK 不需要这些异步上下文,但可以使某些游戏逻辑更易于写入。
操作队列
通常,在使用异步 API 时,多个异步操作需要作为较大异步流的一部分按顺序运行。
在在大厅和配对 SDK 中,一个示例是创建大厅并向好友发送该大厅的邀请。 序列化后,此流如下所示:
- 调用 PFMultiplayerCreateAndJoinLobby() 创建并加入 PlayFab 大厅。
- 等待 PFLobbyCreateAndJoinLobbyCompletedStateChange 反映大厅已成功创建并加入。
- 为每个受邀好友调用 PFLobbySendInvite()。
- 等待 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 工作负载保留某些核心,而不会出现任何争用。