简介
本文档适用于当前正在使用 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
¤tUserEntityKey, // 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
¤tUserEntityKey, // 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))
{
//...
}