简介
本文档面向当前使用 MPSD 并希望迁移到使用 PlayFab Multiplayer 和 MPA 用于其多人游戏的游戏开发者。本文档将涵盖最常见的多人游戏场景,并提供演示如何将 PlayFab Multiplayer 与 MPA 一起使用的代码片段。多人游戏会话目录 (MPSD) 概述
- 用于共享连接一组用户所需信息的功能齐全的会话服务
- 与 XBOX UI 集成,支持邀请和加入功能
- 与 SmartMatch 匹配完全集成
- 会话派生自预定义的会话模板
- 用于连接检测和会话流的集成功能
- 可作为服务到服务提供
多人游戏活动服务 (MPA) 概述
- 轻量级服务,用于简化玩家活动、邀请和最近玩家的 XBOX Live 集成
- 与 shell 和主机操作系统协作,发送/接受邀请和被加入
- 无会话管理或匹配
- 可作为服务到服务提供
PlayFab Multiplayer 概述
- 包含大厅搜索和浏览功能的完整多人游戏大厅服务
- 具有透明 API 集成的跨平台、实时服务通知
- 支持实时通知的完整匹配服务
初始化
下表显示了 MPSD 和 PlayFab Multiplayer 用于初始化的可比函数列表。| MPSD | PlayFab Multiplayer |
|---|---|
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 Multiplayer 用于处理与会话/大厅相关的事件的可比函数列表。| MPSD | PlayFab Multiplayer |
|---|---|
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 Multiplayer 用于创建和加入会话/大厅的可比函数列表。| MPSD | PlayFab Multiplayer |
|---|---|
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 Multiplayer 用于搜索会话/大厅的可比函数列表。| MPSD | PlayFab Multiplayer |
|---|---|
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 | 与 | string_key1 eq ‘CaptureTheFlag’ and number_key2 lt 10 |
比较字符串属性时,请务必用单引号将比较值包裹起来。例如,“string_key1 eq ‘SOME STRING VALUE’“。数字属性无需包裹。
| 运算符 | 含义 | 示例 |
|---|---|---|
| memberCount | 大厅中的玩家数 | lobby/memberCount eq 5 |
| maxMemberCount | 大厅中允许的最大玩家数 | lobby/maxMemberCount gt 10 |
| memberCountRemaining | 可加入大厅的剩余玩家数 | lobby/memberCountRemaining gt 0 |
| membershipLock | 大厅的锁定状态,必须等于 ‘Unlocked’ 或 ‘Locked’ | 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 Multiplayer 用于加入会话/大厅的可比函数列表。| MPSD | PlayFab Multiplayer |
|---|---|
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 Multiplayer 用于更新会话/大厅的可比函数列表。| MPSD | PlayFab Multiplayer |
|---|---|
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 Multiplayer 的匹配 API 与 MPSD 的匹配 API 相对相似。| MPSD | PlayFab Multiplayer |
|---|---|
| 通过 Smartmatch Hoppers 配置 | 基于匹配队列 |
| Hopper 绑定到 MPSD 会话模板 | 匹配规则应用于队列 |
| 匹配规则应用于 Hopper | 匹配通过匹配票证开始 |
| 匹配需要现有的 MPSD 会话 | 匹配结果是新的 PlayFab 大厅 |
| 匹配结果是新的 MPSD 会话 | 支持 PlayFab Multiplayer 服务器分配 |
匹配队列必须通过 PlayFab Game Manager 配置。
匹配状态更改
下表显示了 MPSD 和 PlayFab Multiplayer 用于处理与匹配相关的事件的可比函数列表。| MPSD | PlayFab Multiplayer |
|---|---|
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 Multiplayer |
|---|---|
| 创建 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 Multiplayer 用于清理和关闭的可比函数列表。| 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。最近玩家 - 示例代码
XblMultiplayerActivityRecentPlayerUpdate update{};
update.xuid = metPlayerXuid;
update.encounterType = XblMultiplayerActivityEncounterType::Default;
HRESULT hr = XblMultiplayerActivityUpdateRecentPlayers(xblContext, &update, 1);
if (FAILED(hr))
{
//...
}
