从 MPSD 转向 PlayFab 多人游戏和 MPA

简介

本文档适用于当前正在使用 MPSD 并希望转为对多人游戏使用 PlayFab 多人游戏和 MPA 的游戏开发人员。 本文档将介绍最常见的多人游戏方案,并提供代码片段来演示如何将 PlayFab 多人游戏与 MPA 配合使用。

多人游戏会话目录 (MPSD) 概述

  • 功能齐全的会话服务,可用于共享连接一组用户所需的信息
  • 与 Xbox UI 集成,提供邀请和加入功能
  • 与 SmartMatch 匹配完全集成
  • 会话派生自预定义的会话模板
  • 集成的连接检测和会话流功能
  • 以服务到服务的形式提供

多人游戏活动服务 (MPA) 概述

  • 轻型服务,可简化玩家活动、邀请和最近互动玩家的 Xbox Live 集成
  • 在发送/接收邀请并加入方面与 shell 和主机操作系统协调。
  • 无会话管理或匹配
  • 以服务到服务的形式提供

PlayFab 多人游戏概述

  • 全面的多人游戏大厅服务,包括大厅搜索和浏览功能
  • 跨平台实时服务通知,具备透明的 API 集成功能
  • 全面的匹配服务,支持实时通知

初始化

下表显示了 MPSD 和 PlayFab 多人游戏用于初始化的可比较函数的列表。

MPSD PlayFab 多人游戏
XblMultiplayerAddSubscriptionLostHandler PFMultiplayerInitialize
XblMultiplayerAddConnectionIdChangedHandler PFMultiplayerSetEntityToken
XblMultiplayerSetSubscriptionsEnabled
XblMultiplayerSessionCurrentUserSetStatus

初始化 - 示例代码

使用 PlayFab titleID 初始化库,并设置在 PlayFab 服务登录期间收到的实体令牌。

PFMultiplayerHandle pfmHandle{};
HRESULT hr = PFMultiplayerInitialize(pfTitleId, &pfmHandle);
if (FAILED(hr))
{
    //...
}

hr = PFMultiplayerSetEntityToken(pfmHandle, &entityKey, entityToken);
if (FAILED(hr))
{
    //...
}

大厅状态更改

下表显示了 MPSD 和 PlayFab 多人游戏用于处理会话/大厅相关事件的可比较函数的列表。

MPSD PlayFab 多人游戏
XblMultiplayerSessionSubscribedChangeTypes PFMultiplayerStartProcessingLobbyStateChanges
XblMultiplayerSessionChangedHandler PFMultiplayerFinishProcessingLobbyStateChanges
XblMultiplayerSessionSubscriptionLostHandler

大厅状态更改 - 示例代码

通知库你正在开始处理状态更改。 处理每个排队状态更改,然后通知库状态更改已处理完毕。

HRESULT hr = PFMultiplayerStartProcessingLobbyStateChanges(MultiplayerHandle, &StateChangeCount, &StateChanges);
if (FAILED(hr))
{
    //...
}

for (uint32_t i = 0; i < StateChangeCount; ++i)
{
    const PFLobbyStateChange& Change = *StateChanges[i];

    switch (Change.stateChangeType)
    {
    case PFLobbyStateChangeType::CreateAndJoinLobbyCompleted:        /*...*/ break;
    case PFLobbyStateChangeType::JoinLobbyCompleted:                 /*...*/ break;
    case PFLobbyStateChangeType::MemberAdded:                        /*...*/ break;
    case PFLobbyStateChangeType::AddMemberCompleted:                 /*...*/ break;
    case PFLobbyStateChangeType::MemberRemoved:                      /*...*/ break;
    case PFLobbyStateChangeType::ForceRemoveMemberCompleted:         /*...*/ break;
    case PFLobbyStateChangeType::LeaveLobbyCompleted:                /*...*/ break;
    case PFLobbyStateChangeType::Updated:                            /*...*/ break;
    case PFLobbyStateChangeType::PostUpdateCompleted:                /*...*/ break;
    case PFLobbyStateChangeType::Disconnecting:                      /*...*/ break;
    case PFLobbyStateChangeType::Disconnected:                       /*...*/ break;
    case PFLobbyStateChangeType::JoinArrangedLobbyCompleted:         /*...*/ break;
    case PFLobbyStateChangeType::FindLobbiesCompleted:               /*...*/ break;
    case PFLobbyStateChangeType::InviteReceived:                     /*...*/ break;
    case PFLobbyStateChangeType::InviteListenerStatusChanged:        /*...*/ break;
    case PFLobbyStateChangeType::SendInviteCompleted:                /*...*/ break;
    case PFLobbyStateChangeType::CreateAndClaimServerLobbyCompleted: /*...*/ break;
    case PFLobbyStateChangeType::ClaimServerLobbyCompleted:          /*...*/ break;
    case PFLobbyStateChangeType::ServerPostUpdateCompleted:          /*...*/ break;
    case PFLobbyStateChangeType::ServerDeleteLobbyCompleted:         /*...*/ break;
    }
}

hr = PFMultiplayerFinishProcessingLobbyStateChanges(MultiplayerHandle, StateChangeCount, StateChanges);
if (FAILED(hr))
{
    //...
}

创建大厅

下表显示了 MPSD 和 PlayFab 多人游戏用于创建和加入会话/大厅的可比较函数的列表。

MPSD PlayFab 多人游戏
XblMultiplayerSessionReferenceCreate PFMultiplayerCreateAndJoinLobby
XblMultiplayerSessionCreateHandle
XblMultiplayerSessionJoin
XblMultiplayerAddSessionChangedHandler
XblMultiplayerSessionSetSessionChangeSubscription
XblMultiplayerSessionSetHostDeviceToken
XblMultiplayerWriteSessionAsync

注意

无需在 PlayFab Game Manager 中进行其他设置或配置即可创建大厅。 所有配置都可以在代码中完成。

创建大厅 - 示例代码

配置大厅并设置任何初始大厅属性或成员属性,然后创建并加入大厅。

PFLobbyCreateConfiguration createConfig{};
createConfig.maxMemberCount = 4;
createConfig.ownerMigrationPolicy = PFLobbyOwnerMigrationPolicy::Automatic;
createConfig.accessPolicy = PFLobbyAccessPolicy::Public;

const char* memberPropertyKeys[] { "favoriteColor" };
const char* memberPropertyValues[] { "blue" };

PFLobbyJoinConfiguration joinConfig{};
joinConfig.memberPropertyCount = 1;
joinConfig.memberPropertyKeys = memberPropertyKeys;
joinConfig.memberPropertyValues = memberPropertyValues;

PFLobbyHandle myLobby{};

HRESULT hr = PFMultiplayerCreateAndJoinLobby(
    pfmHandle,           // PFMultiplayerHandle
    &localUserEntityKey, // local user 
    &createConfig,       // create config
    &joinConfig,         // join config
    nullptr,             // async context (optional)   
    &myLobby);           // lobby handle

if (FAILED(hr))
{
    //...
}

查找大厅

下表显示了 MPSD 和 PlayFab 多人游戏用于搜索会话/大厅的可比较函数的列表。

MPSD PlayFab 多人游戏
XblMultiplayerGetSearchHandlesAsync PFMultiplayerFindLobbies
XblMultiplayerSearchHandleGetId
XblMultiplayerSearchHandleGetCustomSessionPropertiesJson
XblMultiplayerSearchHandleGetMemberCounts
XblMultiplayerSearchHandleGetSessionClosed

查找大厅 - 示例代码

设置搜索配置,然后搜索大厅。

PFLobbySearchConfiguration searchConfiguration{};
searchConfiguration.filterString = filterString.c_str(); // filtering
searchConfiguration.sortString = sortString.c_str();     // sorting
searchConfiguration.clientSearchResultCount = 50;        // limits the number of results
searchConfiguration.friendsFilter;                       // return only lobbies with friends in them

HRESULT hr = PFMultiplayerFindLobbies(
    pfmHandle,          // PFMultiplayerHandle
    &localUserEntityKey,  // local user
    &searchConfiguration, // search config
    nullptr);             // async context (optional)

if (FAILED(hr))
{
    //...
} 

然后,返回事件的状态更改后,处理任何搜索结果。

const auto& stateChange = static_cast<const PFLobbyFindLobbiesCompletedStateChange&>(change);
if (SUCCEEDED(stateChange.result))
{
    for (uint32_t i = 0; i < stateChange.searchResultCount; ++i)
    {
        const PFLobbySearchResult& searchResult = stateChange.searchResults[i];
        searchResult.lobbyId;            // lobby id
        searchResult.connectionString;   // connection string
        searchResult.ownerEntity;        // lobby host
        searchResult.maxMemberCount;     // lobby size
        searchResult.currentMemberCount; // players in lobby
        
        for (uint32_t j = 0; j < searchResult.searchPropertyCount; ++j) 
        {
            const char* searchPropertyKey = searchResult.searchPropertyKeys[j];
            const char* searchPropertyValue = searchResult.searchPropertyValues[j];
            /*...*/
        }
        
        for (uint32_t k = 0; k < searchResult.friendCount; ++k) 
        {
            PFEntityKey friendEntityKey = searchResult.friends[k];
            /*...*/
        }
    }
}
else
{
    //...
}

大厅搜索密钥

定义自定义搜索属性时,只允许使用一组受限的密钥。

  • 对于字符串属性,支持以下键:string_key1、string_key2、[...] string_key30
  • 对于数值属性,支持以下键:number_key1、number_key2、[...] number_key30

大厅搜索运算符

FindLobbies API 的查询字符串以类似于 OData 的语法进行结构化。 筛选器字符串最大为 600 个字符。

这些 OData 运算符可用于撰写查询字符串。 运算符区分大小写。

运算符 含义 示例
eq 等于 string_key1 eq “CaptureTheFlag”
lt 小于 number_key2 lt 10
le 小于等于 number_key2 le 10
gt 大于 number_key3 gt 100
ge 大于等于 number_key3 ge 100
ne 不等于 string_key1 ne 'CaptureTheFlag'
and and string_key1 eq 'CaptureTheFlag' and number_key2 lt 10

注意

比较字符串属性时,请务必将比较值括在单引号中。 例如,“string_key1 eq 'SOME STRING VALUE'”。 无需包装数值属性。

还有预定义的运算符可供使用。 指定时,它们必须以“lobby/”作为前缀。

运算符 含义 示例
memberCount 大厅中的玩家数 lobby/memberCount eq 5
maxMemberCount 大厅中允许的最大玩家数。 lobby/maxMemberCount gt 10
memberCountRemaining 可加入大厅的剩余玩家数 lobby/memberCountRemaining gt 0
membershipLock 大厅的锁定状态,必须为“已解锁”或“已锁定” lobby/membershipLock eq 'Unlocked'
amOwner 所有者的大厅,需要为“true” lobby/amOwner eq 'true'
amMember 你所属的大厅,必须为“true” lobby/amMember eq 'true'
amServer 服务器已加入客户端拥有的大厅,需要为“true” lobby/amServer eq 'true'

对搜索结果进行排序

OData 样式字符串,包含此查询的升序(“asc”)或降序(“desc”)。 OrderBy 子句可用于任何搜索编号键或数值的预定义搜索键。 若要按最接近数字的距离进行排序,可以使用名字对象距离按与给定数字搜索键的距离进行排序。 不能对距离排序使用升序或降序。 此字段仅支持一个排序子句或一个距离子句。 如果未提供排序,或者给定排序需要中断,则默认排序将根据创建时间降序。

示例 含义
number_key1 asc 按数字搜索键升序排序
lobby/memberCount desc 按数字搜索键降序排序
distance(number_key1 = 5) 根据与给定数字的距离排序
按创建时间降序排序

对搜索结果进行排序和筛选 - 示例代码

PFLobbySearchConfiguration searchConfiguration{};​
​
PFLobbySearchFriendsFilter friendsFilter{};    ​
friendsFilter.includeXboxFriendsToken = MyGame::GetLocalUserXboxToken();​
searchConfiguration.friendsFilter = &friendsFilter;​

// Create filter string for ranked deathmatch with skill between 10-20​
std::string filterString;​
filterString +=  "string_key1 eq DeathMatch and ";​ 
filterString +=  "string_key2 eq Ranked and ";​
filterString +=  "number_key1 -ge 10 and ";​
filterString +=  "number_key1 -le 20";​
​
// Create sort string based on skill level​
std::string sortString;​
sortString += std::string("distance{number_key1=" + std::to_string(playerSkill.c_str()) + "}";​

searchConfiguration.filterString = filterString.c_str();​
searchConfiguration.sortString = sortString.c_str();​
searchConfiguration.clientSearchResultCount = 10;        // limits the number of results​

加入大厅

下表显示了 MPSD 和 PlayFab 多人游戏用于加入会话/大厅的可比较函数的列表。

MPSD PlayFab 多人游戏
XblMultiplayerGetSessionByHandleAsync PFMultiplayerJoinLobby
XblMultiplayerSessionJoin
XblMultiplayerAddSessionChangedHandler
XblMultiplayerSessionSetSessionChangeSubscription
XblMultiplayerSessionCurrentUserSetStatus
XblMultiplayerWriteSessionByHandleAsync
XblMultiplayerSessionCloseHandle

注意

加入大厅需要连接字符串。 通常,大厅的主机会在其活动上设置此连接字符串,或通过邀请发送此字符串。 若要获取连接字符串,必须调用 PFLobbyGetConnectionString

加入大厅 - 示例代码

设置初始加入配置,然后加入大厅。

const char* memberPropertyKeys[] { "number", "name"};
const char* memberPropertyValues[] { "8675309", "Jenny"};

PFLobbyJoinConfiguration joinConfig{};
joinConfig.memberPropertyCount = 2;
joinConfig.memberPropertyKeys = memberPropertyKeys;
joinConfig.memberPropertyValues = memberPropertyValues;

PFLobbyHandle myLobby{};

HRESULT hr = PFMultiplayerJoinLobby(
    pfmHandle,             // PFMultiplayerHandle
    &localUserEntityKey,   // local user
    lobbyConnectionString, // connection string
    &joinConfig,           // join config
    nullptr,               // async context (optional)
    &myLobby);             // handle to the lobby

if (FAILED(hr))
{
    //...
} 

更新大厅

下表显示了 MPSD 和 PlayFab 多人游戏用于更新会话/大厅的可比较函数的列表。

MPSD PlayFab 多人游戏
XblMultiplayerGetSessionByHandleAsync PFLobbyPostUpdate
XblMultiplayerWriteSessionByHandleAsync
XblMultiplayerSessionCloseHandle

注意

PFLobbyPostUpdate 可用于更新大厅属性和成员属性。 可通过对该函数进行单次调用来更新一种或两种类型的属性。

更新大厅 - 示例代码(大厅属性)

const char* lobbyPropertyKeys[] { "exampleKey_1", "exampleKey_2" };
const char* lobbyPropertyValues[] { "exampleValue_1234", "exampleValue_ABCD" };

PFLobbyDataUpdate lobbyUpdateData{};
lobbyUpdateData.lobbyPropertyCount = 2;
lobbyUpdateData.lobbyPropertyKeys = lobbyPropertyKeys;
lobbyUpdateData.lobbyPropertyValues = lobbyPropertyValues;

HRESULT hr = PFLobbyPostUpdate(
    myLobby,             // handle to the lobby
    &localUserEntityKey, // local user
    &lobbyUpdateData,    // update data for the lobby
    nullptr,             // update data for a member
    nullptr);            // async context (optional)

if (FAILED(hr))
{
    //...
} 

更新大厅 - 示例代码(成员属性)

const char* memberPropertyKeys[] { "favoriteColor" };
const char* memberPropertyValues[] { "yellow" };

PFLobbyMemberDataUpdate memberUpdateData{};
memberUpdateData.lobbyPropertyCount = 1;
memberUpdateData.lobbyPropertyKeys = memberPropertyKeys;
memberUpdateData.lobbyPropertyValues = memberPropertyKeys;

HRESULT hr = PFLobbyPostUpdate(
    myLobby,             // handle to the lobby
    &localUserEntityKey, // local user
    nullptr,             // update data for the lobby
    & memberUpdateData   // update data for a member
    nullptr);            // async context (optional)

if (FAILED(hr))
{
    //...
}

匹配

PlayFab 多人游戏的匹配 API 与 MPSD 的匹配 API 比较相似。

MPSD PlayFab 多人游戏
通过 SmartMatch Hopper 进行配置 基于匹配队列
Hopper 绑定到 MPSD 会话模板 匹配规则适用于队列
匹配规则适用于 Hopper 匹配从匹配票证开始
匹配需要现有的 MPSD 会话 匹配结果为新的 PlayFab 大厅
匹配结果是新的 MPSD 会话 支持 PlayFab 多人游戏服务器分配

注意

必须通过 PlayFab Game Manager 配置匹配队列。

匹配状态更改

下表显示了 MPSD 和 PlayFab 多人游戏用于处理匹配相关事件的可比较函数的列表。

MPSD PlayFab 多人游戏
XblMultiplayerSessionSubscribedChangeTypes PFMultiplayerStartProcessingMatchmakingStateChanges
XblMultiplayerSessionChangedHandler PFMultiplayerFinishProcessingMatchmakingStateChanges
XblMultiplayerSessionSubscriptionLostHandler

匹配状态更改 - 示例代码

通知库你正在开始处理状态更改。 处理每个排队状态更改,然后通知库状态更改已处理完毕。

uint32_t stateChangeCount = 0;
const PFMatchmakingStateChange* const* stateChanges = nullptr;

HRESULT hr = PFMultiplayerStartProcessingMatchmakingStateChanges(pfmHandle, &stateChangeCount, &stateChanges);
if (FAILED(hr))
{
    //...
}

for (uint32 i = 0; i < stateChangeCount; ++i)
{
    const PFMatchmakingStateChange& change = *stateChanges[i];
    
    switch (change.stateChangeType)
    {
    case PFMatchmakingStateChangeType::TicketStatusChanged: /*...*/ break;
    case PFMatchmakingStateChangeType::TicketCompleted:     /*...*/ break;
    }
}

hr = PFMultiplayerFinishProcessingMatchmakingStateChanges(pfmHandle, stateChangeCount, stateChanges);
if (FAILED(hr))
{
    //...
}

开始匹配

MPSD PlayFab 多人游戏
创建 MPSD 会话调用… PFMultiplayerCreateMatchmakingTicket
XblMatchmakingCreateMatchTicketAsync PFMultiplayerJoinMatchmakingTicketFromId
XblMatchmakingCreateMatchTicketResult PFMatchmakingTicketGetStatus
XblMultiplayerSessionMatchmakingServer PFMatchmakingTicketGetMatch
加入 MPSD 会话调用… PFMultiplayerJoinArrangedLobby

开始匹配 - 示例代码

PFMatchmakingTicketConfiguration matchTicketConfig{};
matchTicketConfig.timeoutInSeconds;        // how long to attempt matchmaking
matchTicketConfig.queueName;               // matchmaking queue name
matchTicketConfig.membersToMatchWithCount; // num remote players to go into matchmaking with
matchTicketConfig.membersToMatchWith;      // remote players to go into matchmaking with

HRESULT hr = PFMultiplayerCreateMatchmakingTicket(
    pfmHandle,                   // PFMultiplayerHandle
    1,                           // local user count
    &currentUserEntityKey,       // local users
    nullptr,                     // local user attributes (optional)
    &ticketConfig,               // ticket config
    nullptr,                     // async context (optional)
    &m_activeMatchmakingTicket); // matchmaking ticket

if (FAILED(hr))
{
    //...
} 

hr = PFMultiplayerJoinMatchmakingTicketFromId(
    pfmHandle,                   // PFMultiplayerHandle
    1,                           // local user count
    &currentUserEntityKey,       // local users
    nullptr,                     // local user attributes (optional)
    ticketId,                    // matchmaking ticket to join
    queueName,                   // matchmaking queue name
    nullptr,                     // async context (optional)
    &m_activeMatchmakingTicket); // matchmaking ticket

if (FAILED(hr))
{
    //...
}

注意

只有在 membersToMatchWith 字段中指定的所有成员都已加入后,才会开始匹配。

然后,找到匹配项并返回状态更改后,加入已安排的大厅。

const auto& stateChange = static_cast<const PFMatchmakingTicketCompletedStateChange&>(change);
if (SUCCEEDED(stateChange.result))
{
    PFMatchmakingTicketStatus status{};
    HRESULT hr = PFMatchmakingTicketGetStatus(stateChange.ticket, &status);
    if (SUCCEEDED(hr))
    {
        if (status == PFMatchmakingTicketStatus::Matched)
        {
            const PFMatchmakingMatchDetails* matchDetails = nullptr;
            hr = PFMatchmakingTicketGetMatch(stateChange.ticket, &matchDetails);
            if (SUCCEEDED(hr))
            {
                const char* memberPropertyKeys[] { "favoriteCheese" };
                const char* memberPropertyValues[] { "Wensleydale" };

                PFLobbyArrangedJoinConfiguration joinConfig{};
                joinConfig.accessPolicy = PFLobbyAccessPolicy::Private;
                joinConfig.maxMemberCount = 4;
                joinConfig.ownerMigrationPolicy = PFLobbyOwnerMigrationPolicy::Automatic;
                joinConfig.memberPropertyCount = 1;
                joinConfig.memberPropertyKeys = memberPropertyKeys;
                joinConfig.memberPropertyValues = memberPropertyValues;
                
                PFLobbyHandle myLobby{};

                hr = PFMultiplayerJoinArrangedLobby(
                    pfmHandle,                            // PFMultiplayerHandle
                    &localUserEntityKey,                  // local user
                    matchDetails->lobbyArrangementString, // connection string
                    &config,                              // join config
                    nullptr,                              // async context (optional)
                    &myLobby);                            // handle to the lobby

                if (FAILED(hr))
                {
                    //...
                }
            }
        }
    }
}

清理

下表显示了 MPSD 和 PlayFab 多人游戏用于清理和关闭的可比较函数的列表。

MPSD MPA
XblMultiplayerRemoveSubscriptionLostHandler PFMultiplayerUninitialize
XblMultiplayerRemoveConnectionIdChangedHandler
XblMultiplayerSetSubscriptionsEnabled

注意

在调用 PFMultiplayerUninitialize 之前,请确保保留任何活动大厅并销毁任何进行中的匹配票证。

清理 - 示例代码

HRESULT hr = PFMultiplayerUninitialize(pfmHandle);
if (FAILED(hr))
{
    //...
}

活动

下表显示了 MPSD 和 MPA 用于管理活动的可比较函数的列表。

MPSD MPA
XblMultiplayerSetActivityAsync XblMultiplayerActivitySetActivityAsync
XblMultiplayerClearActivityAsync XblMultiplayerActivityDeleteActivityAsync
XblMultiplayerActivityGetActivityAsync
XblMultiplayerActivityGetActivityResultSize
XblMultiplayerActivityGetActivityResult

注意

设置活动或发送邀请时,请确保使用从 PFLobbyGetConnectionString 传回的连接字符串。

活动 - 示例代码

const char* connectionString;
HRESULT hr = PFLobbyGetConnectionString(myLobby, &connectionString);
if (FAILED(hr))
{
    //...
}

uint32_t maxPlayerCount;
hr = PFLobbyGetMaxMemberCount(myLobby, &maxPlayerCount);
if (FAILED(hr))
{
    //...
}

uint32_t lobbyMemberCount;
const PFEntityKey* lobbyMembers;
hr = PFLobbyGetMembers(myLobby, &lobbyMemberCount, &lobbyMembers);
if (FAILED(hr))
{
    //...
}

const char* lobbyId;
hr = PFLobbyGetLobbyId(myLobby, &lobbyId);
if (FAILED(hr))
{
    //...
}

PFLobbyAccessPolicy pfAccessPolicy;
hr = PFLobbyGetAccessPolicy(LobbyHandle, &pfAccessPolicy);
if (FAILED(hr))
{
    //...
}

XblMultiplayerActivityJoinRestriction joinRestriction = XblMultiplayerActivityJoinRestriction::InviteOnly;

switch (pfAccessPolicy)
{
case PFLobbyAccessPolicy::Public:  joinRestriction = XblMultiplayerActivityJoinRestriction::Public; break;
case PFLobbyAccessPolicy::Friends: joinRestriction = XblMultiplayerActivityJoinRestriction::Followed; break;
case PFLobbyAccessPolicy::Private: joinRestriction = XblMultiplayerActivityJoinRestriction::InviteOnly; break;
}

XblMultiplayerActivityInfo info{};
info.connectionString = connectionString;
info.joinRestriction = joinRestriction;
info.maxPlayers = maxPlayerCount;
info.currentPlayers = lobbyMemberCount;
info.groupId = lobbyId;
info.xuid = myXuid;

auto async = std::make_unique<XAsyncBlock>();
async->queue = queue;
async->callback = [](XAsyncBlock* async) 
{
    std::unique_ptr<XAsyncBlock> asyncBlockPtr{ async };
    HRESULT hr = XAsyncGetStatus(async, false);
    if(FAILED(hr))
    {
        //...
    }
};

HRESULT hr = XblMultiplayerActivitySetActivityAsync(
    xblContext,    // XblContextHandle
    &info,         // XblMultiplayerActivityInfo
    false,         // Allow cross-platform joins
    async.get()    // XAsyncBlock
);

if (SUCCEEDED(hr)) 
{
    async.release();
}
else
{
    //...
}

邀请

下表显示了 MPSD 和 MPA 用于发送和接收邀请的可比较函数的列表。

MPSD MPA
XGameInviteRegisterForEvent XGameInviteRegisterForEvent
XGameInviteUnregisterForEvent XGameInviteUnregisterForEvent
XblMultiplayerSendInvitesAsync XblMultiplayerActivitySendInvitesAsync
XGameUiShowSendGameInviteAsync XGameUiShowMultiplayerActivityGameInviteAsync

邀请 - 示例代码(游戏 UI)

const char* connectionString;
HRESULT hr = PFLobbyGetConnectionString(myLobby, &connectionString);
if (SUCCEEDED(hr))
{
    auto async = std::make_unique<XAsyncBlock>();
    async->queue = queue;
    async->callback = [](XAsyncBlock* async)
    {
        std::unique_ptr<XAsyncBlock> asyncBlockPtr{ async };
        HRESULT hr = XAsyncGetStatus(async, false);
        if(FAILED(hr))
        {
            //...   
        }
    };
    
    HRESULT hr = XblMultiplayerActivitySendInvitesAsync(
        xblContext,       // XblContextHandle 
        &xuid,            // recipient
        1,                // number of invited XUIDs
        true,             // allow cross-platform joins
        connectionString, // use lobby connection string
        async.get());

    if (SUCCEEDED(hr))
    {
        async.release();
    }
    else
    {
        //...
    }
}
else
{
    //...
}

邀请 - 示例代码 (Xbox UI)

auto async = std::make_unique<XAsyncBlock>();
async->queue = queue;
async->callback = [](XAsyncBlock* async)
{
    std::unique_ptr<XAsyncBlock> async{ async };
    HRESULT hr = XGameUiShowMultiplayerActivityGameInviteResult(async);
    if(FAILED(hr))
    {
        //...   
    }
};

HRESULT hr = XGameUiShowMultiplayerActivityGameInviteAsync(
    async.get(), // XAsyncBlock
    user.get()   // XUserHandle that is sending the invite
);

if (SUCCEEDED(hr))
{
    async.release();
}
else
{
    //...
}

注意

XGameUiShowMultiplayerActivityGameInviteResult 使用当前设置的活动。 在使用此函数之前,必须使用 XblMultiplayerActivitySetActivityAsync 设置活动。

最近互动玩家

下表显示了使用 MPSD 和 MPA 时如何管理“最近互动玩家”列表。

MPSD MPA
玩家必须在同一 MPSD 会话中 XblMultiplayerActivityUpdateRecentPlayers
会话的 gameplay 属性设置为 true XblMultiplayerActivityFlushRecentPlayersAsync
两个玩家都标记为活动

注意

为了避免限制,最佳做法是批量调用 XblMultiplayerActivityUpdateRecentPlayers

RecentPlayers - 示例代码

XblMultiplayerActivityRecentPlayerUpdate update{};
update.xuid = metPlayerXuid;
update.encounterType = XblMultiplayerActivityEncounterType::Default;

HRESULT hr = XblMultiplayerActivityUpdateRecentPlayers(xblContext, &update, 1);
if (FAILED(hr))
{
    //...
}