簡介
本文件適用於目前使用 MPSD,並想要改用 PlayFab Multiplayer 與 MPA 來開發多人遊戲的遊戲開發人員。本文件將涵蓋最常見的多人遊戲情境,並提供示範如何搭配 MPA 使用 PlayFab Multiplayer 的程式碼片段。多人遊戲工作階段目錄 (MPSD) 概觀
- 功能完整的工作階段服務,用於分享連接一群使用者所需的資訊
- 與 XBOX UI 整合,提供邀請與加入功能
- 與 SmartMatch 配對完全整合
- 工作階段衍生自預先定義的工作階段範本
- 整合了連線偵測與工作階段流程功能
- 可作為服務對服務使用
多人遊戲活動服務 (MPA) 概觀
- 輕量服務,可簡化玩家活動、邀請與最近一起玩的玩家的 XBOX Live 整合
- 在傳送/接受邀請以及被加入時,與殼層和主機作業系統協調
- 不提供工作階段管理或配對
- 可作為服務對服務使用
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 子句可用於任何搜尋數字索引鍵,或數值型的預先定義搜尋索引鍵。若要依最接近某個數字排序,可以使用 distance 名稱,依與指定數字搜尋索引鍵的距離排序。distance 排序不能搭配遞增或遞減使用。此欄位只支援一個排序子句或一個 distance 子句。如果未提供排序,或指定的排序需要決勝條件,預設排序會依建立時間遞減排序。| 範例 | 意義 |
|---|---|
| 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 Hopper 設定 | 以配對佇列為基礎 |
| 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 的呼叫。RecentPlayers - 範例程式碼
XblMultiplayerActivityRecentPlayerUpdate update{};
update.xuid = metPlayerXuid;
update.encounterType = XblMultiplayerActivityEncounterType::Default;
HRESULT hr = XblMultiplayerActivityUpdateRecentPlayers(xblContext, &update, 1);
if (FAILED(hr))
{
//...
}
