多人游戏任务

本主题介绍如何实现与使用 2015 多人游戏相关的特定任务。

本主题涵盖以下内容:

订阅多人游戏会话目录 (MPSD) 会话更改通知

注意

订阅会话更改需要关联的玩家在会话中处于活动状态。 connectionRequiredForActiveMembers字段还必须在会话的/constants/system/capabilities对象中设置为true。 此字段通常在会话模板中设置。 有关详细信息,请查看多人游戏会话模板多人游戏会话目录概述

若要接收 MPSD 会话更改通知,游戏可以使用以下过程。

  1. 对同一用户的所有调用使用相同的 XblContextHandle 对象。 订阅与此对象的生存期相关。 如果有多个本地用户,请为每个用户使用单独的 XblContextHandle 对象。

  2. XblMultiplayerAddSessionChangedHandlerXblMultiplayerSessionSubscriptionLostHandler 实施事件处理程序。

  3. 如果订阅了多个用户的更改,请将代码添加到 XblMultiplayerAddSessionChangedHandler 事件处理程序,以避免不必要的工作。 使用 XblMultiplayerSessionChangeEventArgs::Branch 属性和 XblMultiplayerSessionChangeEventArgs::ChangeNumber 属性。 使用这些属性可以跟踪上次看到的更改,并忽略较旧的更改。

  4. 调用 XblMultiplayerSetSubscriptionsEnabled 以允许订阅。

  5. 创建本地会话对象,然后以活动身份加入该会话。

  6. 调用每个用户XblMultiplayerAddSessionChangedHandler,并传递要通知的会话更改类型。

  7. 如本主题的 "更新 MPSD 会话 "部分中所述,将会话写入 MPSD。

以下流程图显示如何通过订阅上一过程中描述的事件来启动多人游戏。

流程图的图像,显示如何通过订阅上一过程中描述的事件来启动多人游戏。

分析重复会话更改通知

当有多个用户订阅同一会话的通知时,对该会话的每次更改都会为每个用户触发一次单击。 这些肩部点击除一个外,其他所有都是重复的。

尽管我们仍建议游戏将会话中的每个用户订阅到通知,但游戏应忽略已收到通知的任何更改。 为此,可以使用 BranchChangeNumber 属性。

若要检测多个肩部点击,游戏应执行以下操作:

  • 为计算的每个 ChangeNumber属性值存储最新的 Branch 属性值。

  • 如果肩部点击的 ChangeNumber 属性值高于该 Branch 属性值的最新存储值,请处理肩部点击,然后更新最新的 ChangeNumber 属性值。

  • 如果肩触点没有较高的ChangeNumber属性值,则跳过处理该 Branch 属性值,请跳过处理肩部点击。 已处理该会话更改。

注意

ChangeNumber 属性值需要由 Branch 属性值跟踪,而不是通过会话跟踪。 Branch 属性值可以在会话的生存期内更改,重置ChangeNumber 属性值。

返回到本主题顶部。

创建 MPSD 会话

注意

默认情况下,MPSD 会话在第一个成员加入时创建。 如果您的作品希望作品在加入时存在或不存在,它可以在更新期间将适当的写入模式值传递到写入方法中。

游戏必须执行以下操作才能创建新会话。

  1. 创建一个新的 XblContextHandle 对象。 你的作品将创建此对象一次,将其存储下来,然后根据需要在整个源代码中重复使用它。 必须使用完全相同的上下文,尤其是在使用会话订阅时。

  2. 使用 XblMultiplayerSessionCreateHandle 创建新的 XblMultiplayerSessionHandle,以准备 MPSD 创建新会话所需的所有会话数据。

  3. 在将会话写入 MPSD 之前进行必要的更改。 例如,当通过调用 XblMultiplayerSessionJoin 将成员加入会话时,客户端将添加隐藏的本地请求数据,告知 MPSD 在调用时加入以更新会话。

  4. 完成本地更改后,按照本主题的 "更新 MPSD 会话 "部分中所述,将它们写入 MPSD。

  5. 从 MPSD 接收新的 XblMultiplayerSessionHandle 对象,其中已填充了多个字段。

  6. 今后使用新的会话对象。 放弃包含创建新会话的隐藏请求的旧副本。

示例

平面 C API

auto asyncBlock = std::make_unique<XAsyncBlock>();
asyncBlock->queue = queue;
asyncBlock->context = nullptr;
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
{
    std::unique_ptr<XAsyncBlock> asyncBlockPtr{ asyncBlock }; // Take over ownership of the XAsyncBlock*.

    XblMultiplayerSessionHandle sessionHandle;
    HRESULT hr = XblMultiplayerWriteSessionResult(asyncBlock, &sessionHandle);
    if (SUCCEEDED(hr))
    {
        // Process multiplayer session handle.
    }
    else
    {
        // Handle failure.
    }
};

XblMultiplayerSessionReference ref;
pal::strcpy(ref.Scid, sizeof(ref.Scid), SCID);
pal::strcpy(ref.SessionTemplateName, sizeof(ref.SessionTemplateName), SESSION_TEMPLATE_NAME);
pal::strcpy(ref.SessionName, sizeof(ref.SessionName), SESSION_NAME);

XblMultiplayerSessionInitArgs args = {};

XblMultiplayerSessionHandle sessionHandle = XblMultiplayerSessionCreateHandle(XUID, &ref, &args);

auto hr = XblMultiplayerSessionJoin(
    sessionHandle,
    memberCustomConstantsJson.c_str(),
    initializeRequested,
    joinWithActiveStatus);
    
 hr = XblMultiplayerWriteSessionAsync(xblContextHandle, sessionHandle, XblMultiplayerSessionWriteMode::CreateNew, asyncBlock.get());
if (SUCCEEDED(hr))
{
    // The call succeeded, so release the std::unique_ptr ownership of XAsyncBlock* because the callback will take over ownership.
    // If the call fails, std::unique_ptr will keep ownership and delete XAsyncBlock*.
    asyncBlock.release();
}

有关详细信息,请参阅以下内容:

返回到本主题顶部。

为 MPSD 会话设置仲裁程序

游戏使用以下过程为已创建的会话设置仲裁程序。

注意

成员(潜在主机)的设备令牌在成员加入会话并包含其安全设备地址之前不可用。

  1. 通过调用 XblMultiplayerSessionMembers,从 MPSD 检索主机候选项的设备令牌。

    注意

    如果会话是由 SmartMatch 匹配创建的,则客户端可以通过调用 XblMultiplayerSessionHostCandidates 来使用 MPSD 中可用的主机候选项。

  2. 从候选主机列表中选择所需的主机。

  3. 调用 XblMultiplayerSessionSetHostDeviceToken 以在 MPSD 的本地缓存中设置设备令牌。 如果设置主机设备令牌的调用成功,则本地设备令牌将替换主机的令牌。

  4. 如果在尝试设置主机设备令牌时收到 HTTP/412 状态代码,请查询会话数据。 查看主机设备令牌是否适用于本地控制台。 如果不是本地主机,则已将另一个主机指定为仲裁程序。

    注意

    客户端应将 HTTP/412 状态代码与其他 HTTP 代码分开处理,因为 HTTP/412 不指示标准故障。 有关此状态代码的详细信息,请参Multiplayer 会话状态代码

  5. 按照本主题中的 "更新 MPSD 会话 "部分中所述,更新 MPSD 中的会话。

    注意

    如果您没有更好的算法,客户端可以实现贪婪算法,在此算法中,如果尚未设置主机,每个候选主机都将尝试将自己设置为主机。 有关详细信息,请参阅多人游戏会话高级主题主题中的 "会话仲裁程序 "部分。

返回到本主题顶部。

管理作品激活

Xbox One(或更高版本)在协议激活期间触发 CoreApplicationView.Activated 事件。 在多人游戏 API 的上下文中,当用户接受邀请或加入其他用户时,将触发此事件。 这些操作通过将加入的用户与目标用户一起玩游戏来触发游戏必须响应的激活。

注意

您的作品在任何时候都应获得新的激活参数,并且永远不应按照长度编码。

作品必须执行以下主要步骤来处理作品激活。

  1. CoreApplicationView.Activated 事件设置事件处理程序。 每当发生协议激活时,此处理程序都会触发,即使作品已在运行。

  2. 在作品激活时,请开始一个会话并订阅会话更改通知。 有关详细信息,请参阅本主题中的 "订阅 MPSD 会话更改通知 "。

  3. 以活动状态将用户加入会话。 有关详细信息,请参阅本主题中的从游戏激活加入 MPSD 会话

  4. 将大厅会话设置为通过配置文件 UI 公开的活动会话。 有关详细信息,请参阅本主题中的设置用户的当前活动

  5. 以活动状态将用户加入游戏会话。 用户现在可以连接到对等方并进入游戏或大厅。

以下流程图显示如何处理游戏激活。

显示如何处理游戏激活的流程图的图像。

返回到本主题顶部。

让用户可加入

要让用户可以加入,作品必须执行以下操作:

  1. 创建会话对象,然后根据需要修改属性。

  2. 以活动状态将用户加入会话。 有关详细信息,请参阅本主题中的从游戏激活加入 MPSD 会话

  3. 确定是否已将用户指定为会话仲裁程序。

  4. 如果用户不是仲裁程序,请转到步骤 7。

  5. 如果用户是仲裁程序,则调用 XblMultiplayerSessionSetHostDeviceToken

  6. 尝试通过调用 XblMultiplayerWriteSessionAsync 来写入会话。

  7. 将会话设置为活动会话。 有关详细信息,请参阅本主题中的设置用户的当前活动

以下流程图显示了允许其他玩家在游戏期间加入用户的步骤。

流程图的图像,其中显示了允许其他玩家在游戏期间加入用户的步骤。

返回到本主题顶部。

发送游戏邀请

游戏可让玩家通过以下方式发送游戏邀请。

  • 为大厅会话发送邀请。
  • 通过将通用 Xbox 平台邀请 UI 与游戏会话引用配合使用来发送邀请。

要为玩家发送游戏邀请,游戏必须执行以下操作:

  1. 使邀请游戏玩家可加入。 有关详细信息,请参阅本主题中的使用户可加入

  2. 确定是通过大厅会话还是通过邀请 UI 发送邀请。

  3. 如果正在使用大厅会话,请通过调用 XblMultiplayerSendInvitesAsync 发送邀请。 此方法可能需要调用 XGameUiShowPlayerPickerAsync 来构建游戏内 UI名单。

  4. 如果正在使用邀请 UI,请调用 XGameUiShowSendGameInviteAsync 以显示邀请 UI。

  5. 在远程玩家加入后,为本地玩家处理 XblMultiplayerAddSessionChangedHandler

  6. 对于远程玩家,实现游戏激活代码。 有关详细信息,请参阅本主题中的管理游戏激活

以下流程图显示如何发送邀请。

显示如何发送邀请的流程图的图像。

返回到本主题顶部。

从大厅会话加入游戏会话

如果Windows 10设备上的游戏会话不是大型会话,则必须将 userAuthorizationStyle 功能设置为 true。 因此,joinRestriction 属性不能为none,这意味着该会话不能直接公开联接。

常见方案是创建大厅会话来收集玩家,然后将这些玩家移动到游戏会话或匹配会话中。 但是,如果游戏会话不可公开加入,则游戏客户端无法加入游戏会话,除非它们满足joinRestriction设置。 在大多数情况下,此方案的限制性太强。

解决办法是使用转移句柄来链接大厅会话和游戏会话。 作品可通过执行以下操作来实现此目的:

  1. 创建游戏会话时,请使用 XblMultiplayerSetTransferHandleAsync API 创建可链接大厅会话和游戏会话的传输句柄。

  2. 将传输句柄 GUID 存储在大厅会话中,而不是在游戏会话的会话参考中。

  3. 当游戏想要将成员从大厅会话移动到游戏会话时,每个客户端都使用大厅会话中的传输句柄通过使用 XblMultiplayerWriteSessionByHandleAsync API 加入游戏会话。

  4. MPSD 查找大厅会话,以验证尝试使用传输句柄加入游戏会话的成员是否也在大厅会话中。

  5. 如果成员位于大厅会话中,则他们可以访问游戏会话。

返回到本主题顶部。

从作品激活加入 MPSD 会话

当用户选择加入好友的活动或使用 Xbox shell UI 接受邀请时,游戏将使用指示用户要加入的会话的参数激活。 作品必须处理此激活并将用户添加到相应的会话中。

下面是游戏应遵循的步骤。

  1. 实现 CoreApplicationView.Activated 事件的事件处理程序。 它通知游戏的激活。

  2. 当处理程序激活时,请检查 IActivatedEventArgs.Kind 属性。 如果设置为 Protocol,请将事件参数强制转换为 ProtocolActivatedEventArgs 类。

  3. 检查 ProtocolActivatedEventArgs 对象。 如果 ProtocolActivatedEventArgs.Uri 属性中指示的 URI 匹配 inviteHandleAccept (对应于接受的邀请)或 activityHandleJoin (通过 shell UI 对应联接),则分析 URI 的查询字符串。 它格式化为具有键/值对的正常 URI 查询字符串,提取以下字段。

    • 对于已接受的邀请:
      1. handle
      2. invitedXuid
      3. senderXuid
    • 对于加入:
      1. handle
      2. joinerXuid
      3. joineeXuid
  4. 启动作品的多人游戏代码,该代码应包括调用 XblMultiplayerSetSubscriptionsEnabled

  5. 调用 XblMultiplayerSessionCreateHandle 以创建本地 XblMultiplayerSessionHandle 对象。

  6. 调用 XblMultiplayerSessionJoin 以加入会话。 使用以下参数设置,使联接设置为活动。

    • memberCustomConstantsJson = null
    • initializeRequested = false
    • joinWithActiveStatus = true
  7. 当加入后会话发生更改时,请调用 XblMultiplayerSessionSetSessionChangeSubscription 以进行即时点击。

  8. 使用步骤 3 中所述获取的句柄调用 XblMultiplayerWriteSessionByHandleAsync。 用户现在是会话的成员,可以使用会话中的数据连接到游戏。

返回到本主题顶部。

设置用户的当前活动

用户的当前活动显示在游戏的 Xbox 仪表板用户体验中。 用户的活动可以通过会话或通过作品激活设置。 在后面的情况下,用户通过匹配或通过开始游戏来进入会话。

注意

可调用 XblMultiplayerClearActivityAsync 来删除通过会话设置的活动。

若要将会话设置为用户的当前活动,游戏将调用XblMultiplayerSetActivityAsync。 它传递会话的会话引用。

若要通过游戏激活设置用户的当前活动,请参阅本主题中的 游戏激活加入 MPSD 会话

返回到本主题顶部。

更新 MPSD 会话

注意

当游戏使用多人游戏 API 更新现有会话时,请记住,它使用本地副本,直到它调用写入会话。

若要更新现有会话,游戏必须执行以下操作:

  1. 根据需要对当前会话进行更改,例如,通过调用 XblMultiplayerSessionLeave

  2. 进行所有更改后,使用这些方法中的任何一种方法将本地更改写入 MPSD。

    如果要写入其他游戏也可以修改的共享部分,请将写入模式设置为 XblMultiplayerSessionWriteMode::SynchronizedUpdate。 有关详细信息,请参阅多人游戏会话目录概述主题中的 会话更新的同步部分。

    写入方法将加入写入服务器并获取最新会话,从此最新会话发现其他会话成员及其主机的安全设备地址 (SDA)。 有关在这些主机之间建立网络连接的详细信息,请参阅 Xbox One 上的 Winsock 简介。

  3. 放弃旧的本地会话对象。 使用新检索的会话对象,以便将来的操作基于最新的已知会话状态。

返回到本主题顶部。

离开 MPSD 会话

若要允许用户离开会话,游戏必须执行以下操作:

  1. 为游戏会话调用 XblMultiplayerSessionLeave

  2. 按照本文中的更新 MPSD 会话部分所述更新 MPSD 中的游戏会话。

  3. 如有必要,请调用大厅会话的 XblMultiplayerSessionLeave 方法,然后更新该会话。

  4. 如果大厅会话需要,请通过调用 XblMultiplayerRemoveSubscriptionLostHandlerXblMultiplayerRemoveSessionChangedHandler 取消注册来关闭多人游戏 API。

以下流程图显示如何退出会话并关闭多人游戏。

显示如何退出会话并关闭多人游戏的流程图的图像。

返回到本主题顶部。

在匹配期间填充会话空位

要在匹配期间填充票证会话的空位,作品必须按照以下类似步骤操作:

  1. 访问匹配期间创建的票证会话的最新会话状态。

  2. 从大厅会话中添加可用于玩游戏的玩家。

  3. 确定票证会话是否已满。

  4. 如果会话已满,请继续玩游戏。

  5. 如果会话尚未满,请按照本主题中的 "创建匹配票证"中所述创建匹配票证。 请务必创建 preserveSession 参数设置为 Always的票证。

  6. 继续匹配。 有关详细信息,请参阅匹配概述

以下流程图显示如何在匹配期间填充打开的会话槽。

流程图的图像,显示如何在匹配期间填充打开的会话槽。

返回到本主题顶部。

创建匹配票证

若要创建匹配票证,匹配球探必须执行以下操作:

  1. 调用 XblMatchmakingCreateMatchTicketAsync,并传入对票证会话的引用。 该方法从 MPSD 读取票证会话,并开始对会话中的用户进行匹配。 方法在内部调用 POST (/serviceconfigs/{scid}/hoppers/{hoppername})

  2. 如果匹配服务要将会话的成员匹配到新会话或其他现有会话中,请将 preserveSession 参数设置为 Never。 将 preserveSession 参数设置为 Always 以允许游戏将现有游戏会话重用为票证会话以继续玩游戏。 然后,匹配服务可以确保保留提交的会话并将任何匹配的玩家添加到该会话。

  3. 使用CreateMatchTicketResponse对象中返回的 XblCreateMatchTicketResponse::EstimatedWaitTime来设置用户对匹配时间的期望。

  4. 使用响应对象中返回的 XblCreateMatchTicketResponse::MatchTicketId,根据需要通过删除票证来取消会话的匹配。 若要删除票证,请使用 XblMatchmakingDeleteMatchTicketAsync

返回到本主题顶部。

获取匹配票证状态

您的游戏应执行以下操作来检索匹配票证状态。

  1. 获取票证会话的 XblMultiplayerSessionHandle 对象。

  2. 调用 XblMultiplayerSessionMatchmakingServer 以访问匹配中使用的 XblMultiplayerMatchmakingServer 对象。

  3. 检查 XblMultiplayerMatchmakingServer 对象以确定匹配过程的状态、会话的典型等待时间以及目标会话引用(如果已找到匹配)。