Xbox マルチプレイヤー シナリオでは、マルチプレイヤー セッション ディレクトリ (MPSD) サービスと MPSD ドキュメントの使用が不可欠です。 MPSD ドキュメントは、現在のゲーム セッションのロスターとして機能し、マッチメイキング、プラットフォームの招待、最近のプレイヤー リスト、参加準備などのマルチプレイヤー エクスペリエンスを促進します。
このドキュメントでは、MPSD を必要とする一般的なマルチプレイヤー フローに PlayFab パーティーを組み込む方法について説明します。
このドキュメントでは、MPSD とそのすべての機能について詳しく説明していません。 詳細については、「MPSD documentation (MPSD 説明書)」を参照してください。
マッチメイキング
マッチメイキングと MPSD を PlayFab パーティーと併用する方法の簡略化されたフローを次に示します。
プレイヤーは、マッチメイキング セッション間で一緒にプレイするグループを表す MPSD セッションを作成して集まります。 プレイヤーは、Xbox の招待機能と参加機能を使用して、このセッションに集まります。
これらのプレイヤー グループはマッチメイキング サービスにチケットを送信し、互換性のあるプレイヤー グループをマッチメイキング セッションに収集します。 このマッチメイキング セッション自体は、プレイヤーが参加する新しいセッション ドキュメントによって表されます。 プレイヤーは、このセッション ドキュメントの変更をリッスンする必要もあります。
マッチメイキング セッションが終了し、ロスターがロックされたら、タイトルはマッチメイキング セッションのメンバーの 1 人を選択して PlayFab Party ネットワークを設定する必要があります。 パーティー ネットワーク作成者を選択するための簡単な方法は、マッチメイキングされた MPSD セッション ドキュメントの最初のメンバーを使用することです。
選択したメンバーは、マッチメイキング セッションのメンバーのみにネットワーク アクセスを制限する初期
PartyInvitationを使用してネットワークを作成します。 ネットワークの作成が正常に完了したら、選択したメンバーは、結果として得られるネットワーク記述子とパーティーの招待をセッション・プロパティとしてセッション・ドキュメントに投稿し、他のメンバーが使用できるようにする必要があります。void OnMatchmakingSessionFinalized( uint32_t usersInSessionCount, const uint64_t* usersInSession ) { PartyInvitationConfiguration initialInvite{}; initialInvite.identifier = nullptr; // let Party select the invitation identifier for simplicity initialInvite.revocability = PartyInvitationRevocability::Anyone; // must be revocable by anyone // the updated invite should contain all users in the matchmaking session std::vector<PartyString> entityIdsInSession; for (uint32_t i = 0; i < usersInSessionCount; ++i) { uint64_t xboxUserId = usersInSession[i]; // Call title-defined xuid->entityid mapping helper PartyString xboxUserEntityId = GetEntityIdFromXboxUserId(xboxUserId); if (xboxUserEntityId != nullptr) { entityIdsInSession.push_back(xboxUserEntityId); } else { DEBUGLOG("User %llu did not have a matching entity ID.", xboxUserId); } } initialInvite.entityIdCount = entityIdsInSession.size(); initialInvite.entityIds = entityIdsInSession.data(); // This is an asynchronous call. It will be completed when StartProcessingStateChanges generates a // PartyCreateNewNetworkCompletedStateChange struct PartyError error = PartyManager::GetSingleton().CreateNewNetwork( m_localPartyUser, &networkConfiguration, 0, nullptr, &initialInvite, nullptr, nullptr, nullptr); if (FAILED(error)) { DEBUGLOG("PartyManager::CreateNetwork failed! 0x%08x\n", error); return; } } void HandleCreateNewNetworkCompleted( const PartyCreateNewNetworkCompletedStateChange& createNewNetworkCompletedStateChange ) { if (createNewNetworkCompletedStateChange.result == PartyStateChangeResult::Succeeded) { // The network was created successfully! Post the networks descriptor and invitation char serializedDescriptor[c_maxSerializedNetworkDescriptorStringLength + 1]; PartyError error = PartyManager::SerializeNetworkDescriptor( &createNewNetworkCompletedStateChange.networkDescriptor, serializedDescriptor); if (PARTY_FAILED(error)) { DEBUGLOG("PartyManager::SerializeNetworkDescriptor failed: 0x%08x\n", error); return; } UpdateSessionProperty( "PartyNetworkDescriptor", // arbitrary property name serializedDescriptor); UpdateSessionProperty( "PartyInitialInvitation", // arbitrary property name createNewNetworkCompletedStateChange.appliedInitialInvitationIdentifier); } else { // The network was not created successfully. // Please refer to CreateNewNetwork reference documentation for retry guidance } }セッション・ドキュメントが更新されたことを各メンバーが確認すると、ネットワーク記述子と招待を使って、ネットワークに接続、参加できます。
void OnNetworkInformationPostedToSessionDocument( PartyString serializedNetworkDescriptor, PartyString invitationId ) { PartyNetworkDescriptor networkDescriptor; PartyError error = PartyManager::DeserializeNetworkDescriptor(serializedNetworkDescriptor, &networkDescriptor); if (PARTY_FAILED(error)) { DEBUGLOG("PartyManager::DeserializeNetworkDescriptor failed: 0x%08x\n", error); return; } // attempt to connect to the network PartyNetwork* network; error = PartyManager::GetSingleton().ConnectToNetwork( &networkDescriptor, nullptr, &network); if (PARTY_FAILED(error)) { DEBUGLOG("PartyManager::ConnectToNetwork failed: 0x%08x\n", error); return; } // immediately queue an authentication on the network we've attempted to connect to. error = network->AuthenticateLocalUser( m_localUser, invitationId, nullptr); if (PARTY_FAILED(error)) { DEBUGLOG("PartyNetwork::AuthenticateLocalUser failed: 0x%08x\n", error); return; } }
注意
ここでは、マッチメイキングと MPSD を PlayFab パーティーに組み込む 1 つのフローを示しました。 このフローの基本的な考え方は、MPSD を使った他のフローに拡張できますが、考えられるすべてのフローを提示することは、このドキュメントの範囲外です。 詳細については、「full MPSD documentation (MPSDの完全な説明書)」を参照してください。
プラットフォームの招待
Xbox プラットフォームの招待を PlayFab パーティーに組み込む方法のフローを次に示します。
PlayerA は MPSD セッション ドキュメントを作成し、セッションの変更をリッスンし、パーティー ネットワークを作成します。 パーティー ネットワークの作成が完了すると、*PlayerA * は、ネットワーク記述子と初期招待 (必要な場合) を MPSD セッション ドキュメントにポストします。
void OnSessionDocumentCreated() { // This is an asynchronous call. It will be completed when StartProcessingStateChanges generates a // PartyCreateNewNetworkCompletedStateChange struct PartyError error = PartyManager::GetSingleton().CreateNewNetwork( m_localPartyUser, &networkConfiguration, 0, nullptr, nullptr, nullptr, nullptr, nullptr); if (FAILED(error)) { DEBUGLOG("PartyManager::CreateNetwork failed! 0x%08x\n", error); return; } } void HandleCreateNewNetworkCompleted( const PartyCreateNewNetworkCompletedStateChange& createNewNetworkCompletedStateChange ) { if (createNewNetworkCompletedStateChange.result == PartyStateChangeResult::Succeeded) { // The network was created successfully! Post the networks descriptor and invitation char serializedDescriptor[c_maxSerializedNetworkDescriptorStringLength + 1]; PartyError error = PartyManager::SerializeNetworkDescriptor( &createNewNetworkCompletedStateChange.networkDescriptor, serializedDescriptor); if (PARTY_FAILED(error)) { DEBUGLOG("PartyManager::SerializeNetworkDescriptor failed: 0x%08x\n", error); return; } UpdateSessionProperty( "PartyNetworkDescriptor", // arbitrary property name serializedDescriptor); } else { // The network was not created successfully. // Please refer to CreateNewNetwork reference documentation for retry guidance } }PlayerA が PlayerB をパーティー ネットワークに招待する場合、PlayerA は、ゲーム内またはコンソール UI で PlayerB に対してプラットフォームの招待を開始します。
PlayerB はプラットフォームの招待を受け取ります。これには PlayerB が PlayerA の MPSD セッション ドキュメントを検索するために使用できる「招待ハンドル」が含まれています。
PlayerB はセッション ドキュメントに参加し、変更をリッスンします。
PlayerA は、セッション ドキュメントに PlayerB が参加したことを確認します。 PlayerA は、PlayerB が使用するための新しい招待を作成し、その招待をセッション ドキュメントに投稿します。
void OnUserJoinedSessionDocument( PartyNetwork* network, uint64_t newSessionMemberXboxUserId ) { std::string newMemberIdString = std::to_string(newSessionMemberXboxUserId); // Specify our own invitation id so we don't have to query for it after the invitation has been created. // Here we will specify the invite id with the format "InviterXboxUserID_InviteeXboxUserID" so that we can // ensure this invitation ID doesn't clash with the invitations other members might try and create for this user. std::string invitationId = std::to_string(m_localXboxUserId) + "_" + newMemberIdString; PartyInvitationConfiguration newInvite{}; newInvite.identifier = invitationId.c_str(); newInvite.revocability = PartyInvitationRevocability::Creator; // must be revocable by the creator only // Call title-defined xuid->entityid mapping helper PartyString newSessionMemberEntityId = GetEntityIdFromXboxUserId(newSessionMemberXboxUserId); newInvite.entityIdCount = 1; newInvite.entityIds = &newSessionMemberEntityId; // Create a new invitation which includes all of the users currently in the document PartyInvitation* newInvitation; PartyError error = network->CreateInvitation( m_localUser, &newInvite, nullptr, &newInvitation); if (PARTY_FAILED(error)) { DEBUGLOG("PartyNetwork(0x%p)::CreateInvitation failed! (error=0x%x)", network, error); return; } // Post the invitation to the local user's member property store in the session document, key'd by the invitee's // xbox user id. This will let the invitee recognize when an invitation is intended for them. UpdateMemberProperty( newMemberIdString.c_str(), invitationId.c_str()); }PlayerB は、セッション ドキュメントに投稿された招待を確認し、それを使用してパーティー ネットワークに参加します。
void OnRemoteMemberPropertyUpdated( PartyString memberPropertyKey, PartyString memberPropertyValue ) { // The member property update signifies a new invitation, if the remote member updated a property that matches // our xbox user id. if (memberPropertyKey == std::to_string(m_localXboxUserId)) { OnUserInvitationPostedToSessionDocument(memberPropertyValue); } // ... } void OnUserInvitationPostedToSessionDocument( PartyString invitationId ) { // The network descriptor should have already been posted to the session document before the invitation. // Call title-defined function to pull it from the session document. PartyNetworkDescriptor networkDescriptor = QueryNetworkDescriptorFromSessionDocument(); // attempt to connect to the network PartyNetwork* network; error = PartyManager::GetSingleton().ConnectToNetwork( &networkDescriptor, nullptr, &network); if (PARTY_FAILED(error)) { DEBUGLOG("PartyManager::ConnectToNetwork failed: 0x%08x\n", error); return; } // immediately queue an authentication on the network we've attempted to connect to. error = network->AuthenticateLocalUser( m_localUser, invitationId, nullptr); if (PARTY_FAILED(error)) { DEBUGLOG("PartyNetwork::AuthenticateLocalUser failed: 0x%08x\n", error); return; } }Important
PartyNetwork::CreateInvitation を介して作成された招待は、それを作成した PartyLocalUser がネットワークから退出すると無効になります。 そのため、新しいユーザーがセッション ドキュメントに自分自身を追加し招待したユーザーが退出した場合は、新しいユーザーがセッション ドキュメントから自分自身を削除し、別のユーザーから再招待されるまで待つ必要があります。
途中参加
進行中のゲーム セッションへの参加は、platform invite (プラットフォームの招待) のシナリオとよく似ています。 主な違いは、PlayerA が PlayerB に「招待ハンドル」を送信する代わりに、PlayerB がプラットフォーム UI から途中参加を開始して「参加ハンドル」を取得することです。 この「参加ハンドル」を使用すると、PlayerB はセッション ドキュメントに参加し、変更をリッスンします。 PlayerA は、新しいパーティーの招待を作成してセッション ドキュメントに投稿することで応答します。 PlayerB は、ネットワーク記述子とこの新しい招待を確認し、それを使用してパーティー ネットワークに参加します。
Important
PartyNetwork::CreateInvitation を介して作成された招待は、それを作成した PartyLocalUser がネットワークから退出すると無効になります。 したがって、新しいユーザーが途中参加フローからパーティーへの招待を受け取ったにもかかわらず、それを使用できない場合は、それを作成したユーザーが残っている場合は、新しいユーザーがセッション ドキュメントから自分自身を削除し、後で再参加することをお勧めします。 これにより、セッションの別のメンバーがフローを再起動し、このユーザーの新しいパーティーへの招待を生成できるようになります。
切断とクリーンアップ
プレイヤーがパーティー ネットワークから退出、または切断された場合は、そのパーティー ネットワークに関連付けられている MPSD セッションからも自分自身を削除する必要があります。 PartyNetwork::LeaveNetwork 操作によって開始されないパーティー ネットワーク切断は、致命的と見なされます。 致命的な切断が発生した後、プレイヤーはネットワークへの再接続と再認証を試みることができますが、同時に MPSD セッションに再び参加する必要があります。
MPSD セッションへのプレイヤーの接続が一時的に中断されると、そのセッションから切断される可能性があります。 プレイヤーはセッションへの再参加を試みることができますが、失敗した場合は、PartyNetwork::LeaveNetwork を呼び出して、パーティー ネットワークから自分自身を削除する必要があります。
注意
パーティー ネットワークと MPSD セッションでは、切断を検出するメカニズムやヒューリスティックが異なります。 プレイヤーがパーティー ネットワークと MPSD セッションの両方から切断されるシナリオでも、これらの切断イベントは独立しており、時間内に互いに近い場所で発生することは保証されません。 タイトルは、プレイヤーがパーティー ネットワークまたは MPSD セッションのいずれかのみから切断されるというシナリオに対応する必要があります。
ゲームがシャットダウンすると、プレイヤーはパーティー ネットワークと MPSD ドキュメントから自動的に切断され、これ以上クリーンアップする必要はありません。