メインコンテンツまでスキップ

OSS を使用してパーティでプレイする - パーティでプレイする - (Unreal Engine モジュール)

Last updated on February 4, 2026

注釈:本資料はAI技術を用いて翻訳されています。

サブシステムを理解する

このセクションでは、AccelByte Gaming Services (AGS) Online Subsystem (OSS) を使用してパーティでプレイする機能を実装する方法を学びます。Byte Wars プロジェクトには、PlayWithPartySubsystem という名前のゲームインスタンスサブシステムクラスがすでに作成されています。このサブシステムには、パーティプレイの完全な実装が含まれています。このチュートリアルでは、そのサブシステムのスターター版を使用して、パーティでプレイする機能をゼロから実装します。

プロジェクトの準備

このチュートリアルを進めるには、まずスターターモードを有効にする必要があります。これを行うには、プロジェクトをビルドして Unreal Engine エディタで開きます。エディタで /Content/TutorialModules/PlayingWithParty に移動します。そこに DA_PlayingWithParty というデータアセットがあります。これを開いて Is Starter Mode Active を有効にします。その後、データアセットを再度保存します。

Setup Tutorial Module Data Asset Unreal Byte Wars play with party

Activate Tutorial Module Data Asset starter mode Unreal Byte Wars play with party

スターターパックの内容を理解する

それでは、プロジェクトのソースコードに戻りましょう。PlayWithPartySubsystem_Starter という名前の準備されたスターターサブシステムクラスがあります。このクラスは リソース セクションで利用可能で、以下のファイルで構成されています:

  • ヘッダーファイル: /Source/AccelByteWars/TutorialModules/PlayingWithParty/PlayWithPartySubsystem_Starter.h
  • CPP ファイル: /Source/AccelByteWars/TutorialModules/PlayingWithParty/PlayWithPartySubsystem_Starter.cpp

PlayWithPartySubsystem_Starter クラスには、以下の機能が提供されています:

  • GetSessionInterface()GetIdentityInterface() という名前の AGS OSS インターフェースゲッター関数。これらのインターフェースを使用して、パーティでプレイする機能を実装します。

    FOnlineSessionV2AccelBytePtr UPlayWithPartySubsystem_Starter::GetSessionInterface() const
    {
    const UWorld* World = GetWorld();
    if (!ensure(World))
    {
    return nullptr;
    }

    return StaticCastSharedPtr<FOnlineSessionV2AccelByte>(Online::GetSessionInterface(World));
    }
    FOnlineIdentityAccelBytePtr UPlayWithPartySubsystem_Starter::GetIdentityInterface() const
    {
    const UWorld* World = GetWorld();
    if (!ensure(World))
    {
    return nullptr;
    }

    return StaticCastSharedPtr<FOnlineIdentityAccelByte>(Online::GetIdentityInterface(World));
    }
  • パーティを参加させるための現在アクティブなオンラインセッションへの参照が必要です。このために、GetOnlineSession() という名前のゲッター関数が提供されています。オンラインセッションとパーティセッションの詳細については、セッションの概要パーティの概要 を参照してください。

    UAccelByteWarsOnlineSessionBase* UPlayWithPartySubsystem_Starter::GetOnlineSession() const
    {
    if (!GetGameInstance())
    {
    return nullptr;
    }

    return Cast<UAccelByteWarsOnlineSessionBase>(GetGameInstance()->GetOnlineSession());
    }
  • 以下は、プロンプトと通知を表示するためのヘルパー関数です。これは、パーティでプレイすることに関連するイベントについてプレイヤーにメッセージを表示するために必要です。例えば、パーティリーダーがパーティマッチメイキングを開始したことを示すメッセージプロンプトなどです。

    UPromptSubsystem* UPlayWithPartySubsystem_Starter::GetPromptSubystem()
    {
    if (UAccelByteWarsGameInstance* GameInstance = Cast<UAccelByteWarsGameInstance>(GetGameInstance()))
    {
    return GameInstance->GetSubsystem<UPromptSubsystem>();
    }

    return nullptr;
    }
  • PlayWithPartySubsystem_Starter は、/Source/AccelByteWars/TutorialModules/PlayingWithParty/PlayWithPartyModels.h にある PlayWithPartyModels クラスのいくつかの定数を使用します。このファイルには、通知とプロンプトメッセージを表示するために使用できる文字列定数が含まれています。また、パーティメンバーのゲームセッション ID を保存するために使用する PARTY_MEMBERS_GAME_SESSION_ID という名前の定数も含まれています。

    #define PARTY_MEMBERS_GAME_SESSION_ID "PARTY_MEMBERS_GAME_SESSION_ID"

    #define ACCELBYTEWARS_LOCTEXT_NAMESPACE "AccelByteWars"

    #define PARTY_MATCHMAKING_STARTED_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Party Matchmaking Started", "Party Matchmaking Started by Party Leader")
    #define PARTY_MATCHMAKING_SUCCESS_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Party Matchmaking Success", "Party Matchmaking Found, Joining Match")
    #define PARTY_MATCHMAKING_FAILED_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Party Matchmaking Failed", "Party Matchmaking failed")
    #define PARTY_MATCHMAKING_CANCELED_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Party Matchmaking Canceled", "Party Matchmaking is canceled by party leader")
    #define PARTY_MATCHMAKING_EXPIRED_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Party Matchmaking Expired", "Party Matchmaking expired")
    #define PARTY_MATCHMAKING_SAFEGUARD_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Party Matchmaking Safeguard", "Matchmaking with this game mode is not supported when in a party")

    #define JOIN_PARTY_GAME_SESSION_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Join Party Game Session", "Joining Party Leader Game Session")
    #define JOIN_PARTY_GAME_SESSION_FAILED_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Join Party Game Session Failed", "Failed to Join Party Game Session")
    #define JOIN_PARTY_GAME_SESSION_CANCELED_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Join Party Game Session Canceled", "Party game session is canceled by party leader")
    #define JOIN_PARTY_GAME_SESSION_WAIT_SERVER_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Join Party Game Session Wait Server", "Joined Party Game Session. Waiting for Server")
    #define JOIN_PARTY_GAME_SESSION_SERVER_ERROR_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Join Party Game Session Server Error", "Party Game Session failure. Cannot find game server.")
    #define JOIN_PARTY_GAME_SESSION_SAFEGUARD_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Join Party Game Session Safeguard", "Cannot join session. Insufficient slots to join with party")

    #define PARTY_GAME_SESSION_LEADER_SAFEGUARD_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Party Game Session Leader Safeguard", "Cannot play online session since party members are on other session")
    #define PARTY_GAME_SESSION_MEMBER_SAFEGUARD_MESSAGE NSLOCTEXT(ACCELBYTEWARS_LOCTEXT_NAMESPACE, "Party Game Session Member Safeguard", "Only party leader can start online session")

パーティでのマッチメイキングを実装する

このセクションでは、パーティでのマッチメイキングを実装します。

AGS OSS には、パーティでのマッチメイキングを自動的に処理する組み込み関数があるため、このセクションでは、パーティマッチメイキングイベントについてプレイヤーにプロンプトを表示するための関連コールバックを実装するだけで済みます。これらのプロンプトはパーティメンバーにのみ表示されます。パーティリーダーはマッチメイキングを開始するため、プロンプトを表示する必要はありません。

  1. PlayWithPartySubsystem_Starter クラスのヘッダーファイルを開き、以下の関数宣言を作成します。

    protected:
    void OnStartPartyMatchmakingComplete();
    void OnPartyMatchmakingComplete(FName SessionName, bool bSucceeded);
    void OnPartyMatchmakingCanceled();
    void OnPartyMatchmakingExpired(TSharedPtr<FOnlineSessionSearchAccelByte> SearchHandler);
  2. OnStartPartyMatchmakingComplete() から始めて、これらの関数を定義します。PlayWithPartySubsystem_Starter クラスの CPP ファイルを開き、以下のコードを追加します。この関数は、パーティマッチメイキングが開始されたことを示す通知を表示します。通知はパーティメンバーにのみ表示されます。

    void UPlayWithPartySubsystem_Starter::OnStartPartyMatchmakingComplete()
    {
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on start party matchmaking completed. Interfaces or online session are not valid."));
    return;
    }

    // Abort if not a party matchmaking.
    if (!GetSessionInterface()->IsInPartySession() ||
    GetOnlineSession()->GetPartyMembers().Num() <= 1)
    {
    return;
    }

    UE_LOG_PLAYINGWITHPARTY(Log, TEXT("Party matchmaking started."));

    /* Show notification that the party matchmaking is started.
    * Only show the notification if the player is a party member.*/
    FUniqueNetIdPtr UserId = nullptr;
    if (GetIdentityInterface())
    {
    UserId = GetIdentityInterface()->GetUniquePlayerId(0);
    }
    if (GetOnlineSession()->IsPartyLeader(UserId))
    {
    return;
    }

    if (GetPromptSubystem())
    {
    GetPromptSubystem()->HideLoading();
    GetPromptSubystem()->ShowLoading(PARTY_MATCHMAKING_STARTED_MESSAGE);
    }
    }
  3. OnPartyMatchmakingComplete() 関数を定義します。PlayWithPartySubsystem_Starter クラスの CPP ファイルに、以下のコードを追加します。この関数は、マッチメイキングが成功したかどうかをパーティに示すプロンプトを表示します。プロンプトはパーティメンバーにのみ表示されます。

    void UPlayWithPartySubsystem_Starter::OnPartyMatchmakingComplete(FName SessionName, bool bSucceeded)
    {
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on party matchmaking completed. Interfaces or online session are not valid."));
    return;
    }

    // Abort if not a party matchmaking.
    if (!GetSessionInterface()->IsInPartySession() ||
    GetOnlineSession()->GetPartyMembers().Num() <= 1 ||
    !GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession).IsEqual(SessionName))
    {
    return;
    }

    if (bSucceeded)
    {
    UE_LOG_PLAYINGWITHPARTY(Log, TEXT("Party matchmaking found. Currently joining the match."));
    }
    else
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Party matchmaking failed."));
    }

    /* Show notification that the party matchmaking is completed.
    * Only show the notification if the player is a party member.*/
    FUniqueNetIdPtr UserId = nullptr;
    if (GetIdentityInterface())
    {
    UserId = GetIdentityInterface()->GetUniquePlayerId(0);
    }
    if (GetOnlineSession()->IsPartyLeader(UserId))
    {
    return;
    }

    if (GetPromptSubystem())
    {
    if (bSucceeded)
    {
    GetPromptSubystem()->HideLoading();
    GetPromptSubystem()->ShowLoading(PARTY_MATCHMAKING_SUCCESS_MESSAGE);
    }
    else
    {
    GetPromptSubystem()->HideLoading();
    GetPromptSubystem()->PushNotification(PARTY_MATCHMAKING_FAILED_MESSAGE, FString(""));
    }
    }
    }
  4. OnPartyMatchmakingCanceled() 関数を定義します。この関数は、パーティマッチメイキングがキャンセルされたことを示す通知を表示します。通知はパーティメンバーにのみ表示されます。

    void UPlayWithPartySubsystem_Starter::OnPartyMatchmakingCanceled()
    {
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on party matchmaking canceled. Interfaces or online session are not valid."));
    return;
    }

    // Abort if not a party matchmaking.
    if (!GetSessionInterface()->IsInPartySession() ||
    GetOnlineSession()->GetPartyMembers().Num() <= 1)
    {
    return;
    }

    UE_LOG_PLAYINGWITHPARTY(Log, TEXT("Party Matchmaking is canceled."));

    /* Show notification that the party matchmaking is canceled.
    * Only show the notification if the player is a party member.*/
    FUniqueNetIdPtr UserId = nullptr;
    if (GetIdentityInterface())
    {
    UserId = GetIdentityInterface()->GetUniquePlayerId(0);
    }
    if (GetOnlineSession()->IsPartyLeader(UserId))
    {
    return;
    }

    if (GetPromptSubystem())
    {
    GetPromptSubystem()->HideLoading();
    GetPromptSubystem()->PushNotification(PARTY_MATCHMAKING_CANCELED_MESSAGE, FString(""));
    }
    }
  5. OnPartyMatchmakingExpired() を定義します。この関数は、パーティマッチメイキングが期限切れになったことを示す通知を表示します。通知はパーティメンバーにのみ表示されます。

    void UPlayWithPartySubsystem_Starter::OnPartyMatchmakingExpired(TSharedPtr<FOnlineSessionSearchAccelByte> SearchHandler)
    {
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on party matchmaking expired. Interfaces or online session are not valid."));
    return;
    }

    // Abort if not a party matchmaking.
    if (!GetSessionInterface()->IsInPartySession() ||
    GetOnlineSession()->GetPartyMembers().Num() <= 1)
    {
    return;
    }

    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Party matchmaking expired."));

    /* Show notification that the party matchmaking is expired.
    * Only show the notification if the player is a party member.*/
    FUniqueNetIdPtr UserId = nullptr;
    if (GetIdentityInterface())
    {
    UserId = GetIdentityInterface()->GetUniquePlayerId(0);
    }
    if (GetOnlineSession()->IsPartyLeader(UserId))
    {
    return;
    }

    if (GetPromptSubystem())
    {
    GetPromptSubystem()->HideLoading();
    GetPromptSubystem()->PushNotification(PARTY_MATCHMAKING_EXPIRED_MESSAGE, FString(""));
    }
    }
  6. これらの関数をそれぞれのオンラインセッションイベントにバインドします。これは、サブシステムが初期化されたときに最初に呼び出される、事前定義された Initialize() 関数に以下のコードを追加することで行えます。

    void UPlayWithPartySubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
    {
    // ...
    if (GetSessionInterface())
    {
    // Bind party matchmaking events.
    GetSessionInterface()->OnMatchmakingStartedDelegates.AddUObject(this, &ThisClass::OnStartPartyMatchmakingComplete);
    GetSessionInterface()->OnMatchmakingCompleteDelegates.AddUObject(this, &ThisClass::OnPartyMatchmakingComplete);
    GetSessionInterface()->OnMatchmakingCanceledDelegates.AddUObject(this, &ThisClass::OnPartyMatchmakingCanceled);
    GetSessionInterface()->OnMatchmakingExpiredDelegates.AddUObject(this, &ThisClass::OnPartyMatchmakingExpired);
    // ...
    }
    // ...
    }
  7. サブシステムが非初期化されたときに、これらの関数をアンバインドします。事前定義された Deinitialize() 関数に以下のコードを追加します。

    void UPlayWithPartySubsystem_Starter::Deinitialize()
    {
    // ...
    if (GetSessionInterface())
    {
    // Unbind party matchmaking events.
    GetSessionInterface()->OnMatchmakingStartedDelegates.RemoveAll(this);
    GetSessionInterface()->OnMatchmakingCompleteDelegates.RemoveAll(this);
    GetSessionInterface()->OnMatchmakingCanceledDelegates.RemoveAll(this);
    GetSessionInterface()->OnMatchmakingExpiredDelegates.RemoveAll(this);
    // ...
    }
    // ...
    }

パーティでゲームセッションをプレイする実装

Byte Wars の Create Session、Match Session、Browse Match 機能は、本質的にゲームセッションです。このセクションでは、パーティでゲームセッションをプレイする機能を実装します。

まず、パーティをゲームセッションに参加させる方法を理解しましょう。パーティリーダーがゲームセッションを作成または参加します。パーティリーダーがゲームセッションに正常に入ると、パーティリーダーはパーティメンバーにゲームセッション招待を送信します。その後、パーティメンバーはその招待を通じてパーティリーダーのゲームセッションに参加します。

  1. パーティゲームセッション招待を処理する関数を作成します。PlayWithPartySubsystem_Starter クラスのヘッダーファイルを開き、以下のコードを追加します:

    protected:
    // ...
    void InvitePartyMembersToJoinPartyGameSession(const FUniqueNetIdPtr LeaderUserId);
    void OnPartyGameSessionInviteReceived(const FUniqueNetId& UserId, const FUniqueNetId& FromId, const FOnlineSessionInviteAccelByte& Invite);
  2. InvitePartyMembersToJoinPartyGameSession() から始めて、これらの関数を定義します。PlayWithPartySubsystem_Starter クラスの CPP ファイルに、以下のコードを追加します。この関数は、パーティリーダーを除くパーティメンバーにゲームセッション招待を送信します。

    void UPlayWithPartySubsystem_Starter::InvitePartyMembersToJoinPartyGameSession(const FUniqueNetIdPtr LeaderUserId)
    {
    if (!LeaderUserId)
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot invite party members to join party game session. Party leader is not valid."));
    return;
    }

    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on invite party members to join party game session. Interfaces or online session are not valid."));
    return;
    }

    if (!GetSessionInterface()->IsInPartySession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot invite party members to join party game session. Inviter is not in a party."));
    return;
    }

    if (!GetOnlineSession()->IsPartyLeader(LeaderUserId))
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot invite party members to join party game session. Inviter is not the party leader."));
    return;
    }

    // Not necessary to send party game session invitation if there is only one member.
    if (GetOnlineSession()->GetPartyMembers().Num() <= 1)
    {
    return;
    }

    // Send party game session invitation to each party members.
    for (auto& Member : GetOnlineSession()->GetPartyMembers())
    {
    if (GetOnlineSession()->IsPartyLeader(Member))
    {
    continue;
    }

    if (FUniqueNetIdAccelByteUserPtr MemberABId = StaticCastSharedRef<const FUniqueNetIdAccelByteUser>(Member))
    {
    UE_LOG_PLAYINGWITHPARTY(Log, TEXT("Send party game session invitation to: %s."), *MemberABId->GetAccelByteId());
    GetSessionInterface()->SendSessionInviteToFriend(
    LeaderUserId.ToSharedRef().Get(),
    GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession),
    Member.Get());
    }
    }
    }
  3. OnPartyGameSessionInviteReceived() 関数を定義します。引き続き PlayWithPartySubsystem_Starter クラスの CPP ファイルに、以下のコードを追加します。この関数は、パーティリーダーからのゲームセッション招待を受け入れ、パーティメンバーがパーティリーダーのゲームセッションに参加できるようにします。

    void UPlayWithPartySubsystem_Starter::OnPartyGameSessionInviteReceived(const FUniqueNetId& UserId, const FUniqueNetId& FromId, const FOnlineSessionInviteAccelByte& Invite)
    {
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on party game session invite received. Interfaces or online session are not valid."));
    return;
    }

    // Abort if not a party game session and if the invitation is not from the party leader.
    if (Invite.SessionType != EAccelByteV2SessionType::GameSession ||
    !GetSessionInterface()->IsInPartySession() ||
    !GetOnlineSession()->IsPartyLeader(FromId.AsShared()) ||
    GetOnlineSession()->GetPartyMembers().Num() <= 1)
    {
    return;
    }

    // Abort if the receiver is the party leader.
    if (GetOnlineSession()->IsPartyLeader(UserId.AsShared()))
    {
    return;
    }

    // Join party game session.
    const APlayerController* PC = GetOnlineSession()->GetPlayerControllerByUniqueNetId(UserId.AsShared());
    if (!PC)
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot join a party game session invitation from party leader. PlayerController is not valid."));
    return;
    }

    const int32 LocalUserNum = GetOnlineSession()->GetLocalUserNumFromPlayerController(PC);

    UE_LOG_PLAYINGWITHPARTY(Log, TEXT("Received a party game session invitation from party leader. Joining the party game session."));

    if (GetPromptSubystem())
    {
    GetPromptSubystem()->HideLoading();
    GetPromptSubystem()->ShowLoading(JOIN_PARTY_GAME_SESSION_MESSAGE);
    }

    GetOnlineSession()->JoinSession(LocalUserNum,
    GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession),
    Invite.Session);
    }
  4. パーティゲームセッションイベントを処理する関数を作成します。PlayWithPartySubsystem_Starter クラスのヘッダーファイルを開き、以下のコードを追加します:

    protected:
    // ...
    void UpdatePartyMemberGameSession(const FUniqueNetIdPtr MemberUserId, const bool bResetGameSessionId = false);

    bool IsGameSessionDifferFromParty(const FUniqueNetIdPtr MemberUserId);

    void OnCreatePartyGameSessionComplete(FName SessionName, bool bSucceeded);
    void OnJoinPartyGameSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result);
    void OnLeavePartyGameSessionComplete(FName SessionName, bool bSucceeded);
    void OnPartyGameSessionUpdateReceived(FName SessionName);
  5. デフォルトでは、パーティマッチメイキングとは異なり、任意のパーティメンバーがゲームセッションを作成または参加できるため、パーティリーダーがゲームセッションを開始する必要はありません。しかし、このチュートリアルでは、パーティリーダーのみがゲームセッションを開始できるようにします。また、各パーティメンバーが他のゲームセッションにいないときにのみ、パーティゲームセッションを開始できるようにします。これを行うには、各パーティメンバーのゲームセッション ID をパーティセッション設定に保存する必要があります。こうすることで、パーティメンバーが別のゲームセッションにいるかどうかを判断できます。これが UpdatePartyMemberGameSession() が行うことです。PlayWithPartySubsystem_Starter クラスの CPP ファイルを開き、以下のコードを追加します:

    void UPlayWithPartySubsystem_Starter::UpdatePartyMemberGameSession(const FUniqueNetIdPtr MemberUserId, const bool bResetGameSessionId)
    {
    if (!MemberUserId)
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot update party member game session. Party member is not valid."));
    return;
    }

    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot update party member game session. Interfaces or online session are not valid."));
    return;
    }

    if (!GetSessionInterface()->IsInPartySession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot update party member game session. Party member is not in a party."));
    return;
    }

    GetSessionInterface()->RefreshSession(
    GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession),
    FOnRefreshSessionComplete::CreateWeakLambda(this, [this, MemberUserId, bResetGameSessionId](bool bWasSuccessful)
    {
    if (!bWasSuccessful)
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot update party member game session. Failed to refresh party session."));
    return;
    }

    FNamedOnlineSession* GameSession = GetSessionInterface()->GetNamedSession(
    GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession));

    // Get game session id only if it has dedicated server or host.
    FString GameSessionId = TEXT("");
    if (GameSession && !bResetGameSessionId)
    {
    FString ServerAddress = TEXT("");
    GetSessionInterface()->GetResolvedConnectString(GameSession->SessionName, ServerAddress);
    const bool bIsP2PHost = GetSessionInterface()->IsPlayerP2PHost(MemberUserId.ToSharedRef().Get(), GameSession->SessionName);

    if (!ServerAddress.IsEmpty() || bIsP2PHost)
    {
    GameSessionId = GameSession->GetSessionIdStr();
    }
    }

    FNamedOnlineSession* PartySession = GetSessionInterface()->GetPartySession();
    if (!PartySession)
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot update party member game session. Party session is not valid."));
    return;
    }

    // Construct party game session data.
    const FUniqueNetIdAccelByteUserRef MemberUserABId = StaticCastSharedRef<const FUniqueNetIdAccelByteUser>(MemberUserId.ToSharedRef());
    TSharedPtr<FJsonObject> MembersGameSessionId = MakeShareable(new FJsonObject);
    FOnlineSessionSetting PartyGameSessionSetting;
    if (PartySession->SessionSettings.Settings.Contains(PARTY_MEMBERS_GAME_SESSION_ID))
    {
    PartyGameSessionSetting = PartySession->SessionSettings.Settings[PARTY_MEMBERS_GAME_SESSION_ID];
    TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(PartyGameSessionSetting.Data.ToString());
    if (!FJsonSerializer::Deserialize(JsonReader, MembersGameSessionId))
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot update party member game session. Failed to parse party members game session."));
    return;
    }
    }

    // Update party member game session id.
    if (!MembersGameSessionId)
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot update party member game session. Failed to parse party members game session."));
    return;
    }
    MembersGameSessionId->RemoveField(MemberUserABId->GetAccelByteId());
    if (!GameSessionId.IsEmpty())
    {
    MembersGameSessionId->SetStringField(MemberUserABId->GetAccelByteId(), GameSessionId);
    }

    // Remove invalid party member data.
    for (auto Pair : MembersGameSessionId->Values)
    {
    bool bIsValidMember = false;

    for (auto& ValidMember : PartySession->RegisteredPlayers)
    {
    const FUniqueNetIdAccelByteUserRef ValidMemberUserABId = StaticCastSharedRef<const FUniqueNetIdAccelByteUser>(ValidMember);
    if (Pair.Key.Equals(ValidMemberUserABId->GetAccelByteId()))
    {
    bIsValidMember = true;
    break;
    }
    }

    if (!bIsValidMember)
    {
    MembersGameSessionId->RemoveField(Pair.Key);
    }
    }

    // Update party game session data to the party session settings.
    FString MembersGameSessionIdStr;
    TSharedRef<TJsonWriter<TCHAR>> JsonWriter = TJsonWriterFactory<TCHAR>::Create(&MembersGameSessionIdStr);
    if (!FJsonSerializer::Serialize(MembersGameSessionId.ToSharedRef(), JsonWriter))
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot update party member game session. Failed to parse party members game session."));
    return;
    }
    PartyGameSessionSetting.Data = MembersGameSessionIdStr;

    // Update party game session to store party game session data.
    PartySession->SessionSettings.Settings.Remove(PARTY_MEMBERS_GAME_SESSION_ID);
    PartySession->SessionSettings.Settings.Add(PARTY_MEMBERS_GAME_SESSION_ID, PartyGameSessionSetting);
    GetSessionInterface()->UpdateSession(
    GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession),
    PartySession->SessionSettings);
    }
    ));
    }
  6. IsGameSessionDifferFromParty() 関数を定義します。これは、現在のプレイヤーが他のパーティメンバーとは異なるゲームセッションを持っているかどうかを確認するヘルパー関数です。このヘルパー関数は、パーティメンバーが他のゲームセッションにいない場合にのみパーティゲームセッションを開始できるようにするために、後で使用します。

    bool UPlayWithPartySubsystem_Starter::IsGameSessionDifferFromParty(const FUniqueNetIdPtr MemberUserId)
    {
    if (!MemberUserId)
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot check whether the game session is differ from party. Party member is not valid."));
    return false;
    }

    bool bResult = false;

    // Abort if interfaces and data is not valid.
    if (!GetSessionInterface() || !GetOnlineSession() || !MemberUserId)
    {
    return bResult;
    }

    // Abort if not in a party session.
    FNamedOnlineSession* PartySession = GetSessionInterface()->GetPartySession();
    if (!PartySession)
    {
    return bResult;
    }

    // Get current game session id.
    FString GameSessionId;
    FNamedOnlineSession* GameSession = GetSessionInterface()->GetNamedSession(
    GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession));
    if (GameSession)
    {
    GameSessionId = GameSession->GetSessionIdStr();
    }

    // Get party game session data.
    FOnlineSessionSetting* PartyGameSessionSetting = PartySession->SessionSettings.Settings.Find(PARTY_MEMBERS_GAME_SESSION_ID);
    if (!PartyGameSessionSetting)
    {
    return bResult;
    }

    TSharedPtr<FJsonObject> MembersGameSessionId = MakeShareable(new FJsonObject);
    TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(PartyGameSessionSetting->Data.ToString());
    if (!FJsonSerializer::Deserialize(JsonReader, MembersGameSessionId))
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot check whether the game session is differ from party. Failed to parse party members game session."));
    return bResult;
    }
    if (!MembersGameSessionId)
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot check whether the game session is differ from party. Failed to parse party members game session."));
    return bResult;
    }

    FString MemberGameSessionIdStr;
    for (auto& Member : GetOnlineSession()->GetPartyMembers())
    {
    // Not necessary to check the player itself.
    if (Member.Get() == MemberUserId.ToSharedRef().Get())
    {
    continue;
    }

    // Check if the current game session is the same as the party.
    const FUniqueNetIdAccelByteUserRef MemberABId = StaticCastSharedRef<const FUniqueNetIdAccelByteUser>(Member);
    if (!MembersGameSessionId->TryGetStringField(MemberABId->GetAccelByteId(), MemberGameSessionIdStr))
    {
    continue;
    }
    if (!GameSessionId.Equals(MemberGameSessionIdStr))
    {
    bResult = true;
    break;
    }
    }

    return bResult;
    }
  7. OnCreatePartyGameSessionComplete() 関数を定義します。引き続き PlayWithPartySubsystem_Starter クラスの CPP ファイルに、以下のコードを追加します。パーティリーダーがゲームセッションを作成すると、この関数は招待を送信して、パーティメンバーがパーティゲームセッションに参加できるようにします。また、UpdatePartyMemberGameSession() 関数を使用して、パーティセッションに保存されるゲームセッション ID を更新します。

    void UPlayWithPartySubsystem_Starter::OnCreatePartyGameSessionComplete(FName SessionName, bool bSucceeded)
    {
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on create party game session completed. Interfaces or online session are not valid."));
    return;
    }

    // Abort if not a party game session.
    if (!GetSessionInterface()->IsInPartySession() ||
    !GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession).IsEqual(SessionName))
    {
    return;
    }

    // Abort if failed to create a party game session.
    if (!bSucceeded)
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Failed to create party game session."));
    return;
    }

    UE_LOG_PLAYINGWITHPARTY(Log, TEXT("Success to create party game session."));

    // Update party member game session id.
    FUniqueNetIdPtr UserId = nullptr;
    if (GetIdentityInterface())
    {
    UserId = GetIdentityInterface()->GetUniquePlayerId(0);

    UpdatePartyMemberGameSession(UserId);
    }

    // Invite party members to join the party game session.
    if (GetOnlineSession()->IsPartyLeader(UserId))
    {
    InvitePartyMembersToJoinPartyGameSession(UserId);
    }
    }
  8. OnJoinPartyGameSessionComplete() 関数を定義します。パーティリーダーがゲームセッションに参加すると、この関数は招待を送信して、パーティメンバーが同じパーティゲームセッションに参加できるようにします。また、UpdatePartyMemberGameSession() 関数を使用して、パーティセッションに保存されるゲームセッション ID を更新します。

    void UPlayWithPartySubsystem_Starter::OnJoinPartyGameSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
    {
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on join party game session completed. Interfaces or online session are not valid."));
    return;
    }

    // Abort if not a party game session.
    if (!GetSessionInterface()->IsInPartySession() ||
    !GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession).IsEqual(SessionName))
    {
    return;
    }

    const bool bSucceeded = (Result == EOnJoinSessionCompleteResult::Type::Success);
    if (bSucceeded)
    {
    UE_LOG_PLAYINGWITHPARTY(Log, TEXT("Success to join party game session."));
    }
    else
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Failed to join party game session."));
    }

    // Update party member game session id.
    FUniqueNetIdPtr UserId = nullptr;
    if (GetIdentityInterface() && bSucceeded)
    {
    UserId = GetIdentityInterface()->GetUniquePlayerId(0);

    UpdatePartyMemberGameSession(UserId);
    }

    // Send invitation to other party members if the one who joined the session is party leader.
    if (GetOnlineSession()->IsPartyLeader(UserId) && bSucceeded)
    {
    InvitePartyMembersToJoinPartyGameSession(UserId);
    }
    // Show relevant notification if the one who joined the session is party member.
    else if (GetPromptSubystem())
    {
    GetPromptSubystem()->HideLoading();

    FString ServerAddress = TEXT("");
    GetSessionInterface()->GetResolvedConnectString(SessionName, ServerAddress);

    if (bSucceeded && ServerAddress.IsEmpty())
    {
    GetPromptSubystem()->ShowLoading(JOIN_PARTY_GAME_SESSION_WAIT_SERVER_MESSAGE);
    }
    else
    {
    GetPromptSubystem()->PushNotification(JOIN_PARTY_GAME_SESSION_FAILED_MESSAGE, FString(""));
    }
    }
    }
  9. OnLeavePartyGameSessionComplete() 関数を定義します。この関数も UpdatePartyMemberGameSession() 関数を使用して、パーティセッションに保存されるゲームセッション ID を更新します。パーティメンバーがゲームセッションを離れたときに呼び出されるため、ゲームセッション ID は空です。したがって、この場合、UpdatePartyMemberGameSession() を呼び出すと、パーティセッションからパーティメンバーのゲームセッション ID がクリアされます。

    void UPlayWithPartySubsystem_Starter::OnLeavePartyGameSessionComplete(FName SessionName, bool bSucceeded)
    {
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on leave party game session completed. Interfaces or online session are not valid."));
    return;
    }

    // Abort if not a party game session.
    if (!GetSessionInterface()->IsInPartySession() ||
    !GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession).IsEqual(SessionName))
    {
    return;
    }

    // Abort if failed to leave a party game session.
    if (!bSucceeded)
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Failed to leave party game session."));
    return;
    }

    UE_LOG_PLAYINGWITHPARTY(Log, TEXT("Success to leave party game session."));

    // Update party member game session id.
    FUniqueNetIdPtr UserId = nullptr;
    if (GetIdentityInterface())
    {
    UserId = GetIdentityInterface()->GetUniquePlayerId(0);

    UpdatePartyMemberGameSession(UserId);
    }
    }
  10. 次に、OnPartyGameSessionUpdateReceived() 関数を定義します。この関数は、パーティメンバーがキックされて新しいパーティリーダーが昇格した場合など、パーティセッションが更新されたときに呼び出されます。その場合、UpdatePartyMemberGameSession() 関数を呼び出して、パーティセッションに保存されるゲームセッション ID を更新する必要があります。

    void UPlayWithPartySubsystem_Starter::OnPartyGameSessionUpdateReceived(FName SessionName)
    {
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on party game session update received. Interfaces or online session are not valid."));
    return;
    }

    // Abort if not a party game session.
    if (!GetSessionInterface()->IsInPartySession() ||
    !GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession).IsEqual(SessionName))
    {
    return;
    }

    // Update party member game session id.
    FUniqueNetIdPtr UserId = nullptr;
    if (GetIdentityInterface())
    {
    UserId = GetIdentityInterface()->GetUniquePlayerId(0);

    UpdatePartyMemberGameSession(UserId);
    }
    }
  11. これらの関数をそれぞれのオンラインセッションイベントにバインドします。これは、サブシステムが初期化されたときに最初に呼び出される、事前定義された Initialize() 関数に以下のコードを追加することで行えます。

    void UPlayWithPartySubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
    {
    // ...
    if (GetSessionInterface())
    {
    // ...
    // Bind party game session events.
    GetSessionInterface()->OnCreateSessionCompleteDelegates.AddUObject(this, &ThisClass::OnCreatePartyGameSessionComplete);
    GetSessionInterface()->OnJoinSessionCompleteDelegates.AddUObject(this, &ThisClass::OnJoinPartyGameSessionComplete);
    GetSessionInterface()->OnV2SessionInviteReceivedDelegates.AddUObject(this, &ThisClass::OnPartyGameSessionInviteReceived);
    GetSessionInterface()->OnDestroySessionCompleteDelegates.AddUObject(this, &ThisClass::OnLeavePartyGameSessionComplete);
    GetSessionInterface()->OnSessionUpdateReceivedDelegates.AddUObject(this, &ThisClass::OnPartyGameSessionUpdateReceived);
    // ...
    }
    // ...
    }
  12. サブシステムが非初期化されたときに、これらの関数をアンバインドします。事前定義された Deinitialize() 関数に以下のコードを追加します:

    void UPlayWithPartySubsystem_Starter::Deinitialize()
    {
    // ...
    if (GetSessionInterface())
    {
    // ...
    // Unbind party game session events.
    GetSessionInterface()->OnCreateSessionCompleteDelegates.RemoveAll(this);
    GetSessionInterface()->OnJoinSessionCompleteDelegates.RemoveAll(this);
    GetSessionInterface()->OnV2SessionInviteReceivedDelegates.RemoveAll(this);
    GetSessionInterface()->OnDestroySessionCompleteDelegates.RemoveAll(this);
    GetSessionInterface()->OnSessionUpdateReceivedDelegates.RemoveAll(this);
    // ...
    }
    // ...
    }

パーティでプレイする際の失敗を処理する

Byte Wars では、各パーティメンバーが他のゲームセッションにいないときにパーティゲームセッションを開始したいと考えています。そのため、パーティメンバー間でゲームセッション ID が同期していることを確認する必要があります。そのため、UpdatePartyMemberGameSession() 関数が作成されています。

時には、ネットワークの問題や専用サーバーの取得に失敗するなどの理由で、パーティでプレイすることが失敗する場合があります。これらのイベントが発生した場合、パーティメンバー間でゲームセッション ID が引き続き同期していることを確認する必要があります。このセクションでは、その処理方法を学びます。

  1. PlayWithPartySubsystem_Starter クラスのヘッダーファイルを開き、以下の関数を宣言します。

    protected:
    // ...
    void OnPartyGameSessionFailure(const FUniqueNetId& UserId, ESessionFailure::Type FailureType);
    void OnPartyGameSessionUpdateConflictError(FName SessionName, FOnlineSessionSettings FailedSessionSettings);
    void OnPartyGameSessionServerUpdate(FName SessionName);
    void OnPartyGameSessionServerError(FName SessionName, const FString& ErrorMessage);

    // ...
    void OnPartyGameSessionParticipantLeft(FName SessionName, const FUniqueNetId& UserId, EOnSessionParticipantLeftReason Reason);
    // ...

    void OnNetworkFailure(UWorld* World, UNetDriver* NetDriver, ENetworkFailure::Type FailureType, const FString& Message);
  2. OnPartyGameSessionFailure() から始めて、これらの関数を定義します。PlayWithPartySubsystem_Starter クラスの CPP ファイルに、以下のコードを追加します。この関数は、ゲームセッションの接続性や使用に影響を与える予期しないエラーが発生したときに、UpdatePartyMemberGameSession() 関数を使用してパーティセッションに保存されるゲームセッション ID を更新します。

    void UPlayWithPartySubsystem_Starter::OnPartyGameSessionFailure(const FUniqueNetId& UserId, ESessionFailure::Type FailureType)
    {
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on party game session update conflict error. Interfaces or online session are not valid."));
    return;
    }

    // Abort if not a party game session.
    if (!GetSessionInterface()->IsInPartySession())
    {
    return;
    }

    // Update party member game session id.
    if (UserId.IsValid())
    {
    UpdatePartyMemberGameSession(UserId.AsShared());
    }
    }
  3. OnPartyGameSessionUpdateConflictError() を定義して、バージョンの不一致によりセッション更新が失敗したときに、パーティセッションに保存されているゲームセッション ID を更新します。

    void UPlayWithPartySubsystem_Starter::OnPartyGameSessionUpdateConflictError(FName SessionName, FOnlineSessionSettings FailedSessionSettings)
    {
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on party game session update conflict error. Interfaces or online session are not valid."));
    return;
    }

    // Abort if not a party game session.
    if (!GetSessionInterface()->IsInPartySession() ||
    !GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession).IsEqual(SessionName))
    {
    return;
    }

    // Update party member game session id.
    FUniqueNetIdPtr UserId = nullptr;
    if (GetIdentityInterface())
    {
    UserId = GetIdentityInterface()->GetUniquePlayerId(0);

    UpdatePartyMemberGameSession(UserId);
    }
    }
  4. OnPartyGameSessionServerUpdate() を定義して、パーティゲームセッションが移動先のサーバー情報を受信したときに、パーティセッションに保存されているゲームセッション ID を更新します。

    void UPlayWithPartySubsystem_Starter::OnPartyGameSessionServerUpdate(FName SessionName)
    {
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on party game session server update event. Interfaces or online session are not valid."));
    return;
    }

    // Abort if not a party game session.
    if (!GetSessionInterface()->IsInPartySession() ||
    !GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession).IsEqual(SessionName))
    {
    return;
    }

    // Update party member game session id.
    FUniqueNetIdPtr UserId = nullptr;
    if (GetIdentityInterface())
    {
    UserId = GetIdentityInterface()->GetUniquePlayerId(0);
    UpdatePartyMemberGameSession(UserId);
    }
    }
  5. OnPartyGameSessionServerError() を定義して、パーティゲームセッションが移動先のサーバー情報の取得に失敗したときに、パーティセッションに保存されているゲームセッション ID を更新します。

    void UPlayWithPartySubsystem_Starter::OnPartyGameSessionServerError(FName SessionName, const FString& ErrorMessage)
    {
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on party game session server error event. Interfaces or online session are not valid."));
    return;
    }

    // Abort if not a party game session.
    if (!GetSessionInterface()->IsInPartySession() ||
    !GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession).IsEqual(SessionName))
    {
    return;
    }

    // Reset party member game session id.
    FUniqueNetIdPtr UserId = nullptr;
    if (GetIdentityInterface())
    {
    UserId = GetIdentityInterface()->GetUniquePlayerId(0);
    UpdatePartyMemberGameSession(UserId, true);
    }

    // Hide loading party game session related prompts if any.
    if (GetPromptSubystem())
    {
    GetPromptSubystem()->HideLoading();
    GetPromptSubystem()->PushNotification(JOIN_PARTY_GAME_SESSION_SERVER_ERROR_MESSAGE, FString(""));
    }
    }
  6. OnNetworkFailure() を定義して、ネットワーク接続エラー(例: ネットワークタイムアウト、ホストへの接続が切断された)が発生したときに、パーティセッションに保存されているゲームセッション ID を更新します。

    void UPlayWithPartySubsystem_Starter::OnNetworkFailure(UWorld* World, UNetDriver* NetDriver, ENetworkFailure::Type FailureType, const FString& Message)
    {
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on-network failure event. Interfaces or online session are not valid."));
    return;
    }

    // Abort if not a party game session.
    if (!GetSessionInterface()->IsInPartySession())
    {
    return;
    }

    // Reset party member game session id.
    FUniqueNetIdPtr UserId = nullptr;
    if (GetIdentityInterface())
    {
    UserId = GetIdentityInterface()->GetUniquePlayerId(0);
    UpdatePartyMemberGameSession(UserId, true);
    }
    }
  7. 以下のコードを追加して、OnPartyGameSessionParticipantLeft() を定義します。この関数は、パーティメンバーがパーティゲームセッションに参加した後、メンバーがそのゲームセッションを離れたときに呼び出されます。これが発生したとき、ゲームセッションを離れたメンバーがパーティリーダーであり、ゲームセッションが移動先のサーバー情報を受信していない場合、パーティゲームセッションの作成が完了していないことを意味します。したがって、パーティメンバーはそのゲームセッションを離れる必要があります。

    void UPlayWithPartySubsystem_Starter::OnPartyGameSessionParticipantLeft(FName SessionName, const FUniqueNetId& UserId, EOnSessionParticipantLeftReason Reason)
    #endif
    {
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot handle on party game session participant removed event. Interfaces or online session are not valid."));
    return;
    }

    // Abort if not a party game session.
    if (!GetSessionInterface()->IsInPartySession() ||
    !GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession).IsEqual(SessionName))
    {
    return;
    }

    FString ServerAddress = TEXT("");
    GetSessionInterface()->GetResolvedConnectString(SessionName, ServerAddress);

    const FUniqueNetIdPtr PartyLeaderUserId = GetOnlineSession()->GetPartyLeader();
    const bool bIsLeaverThePartyLeader = PartyLeaderUserId && UserId == PartyLeaderUserId.ToSharedRef().Get();
    const bool bIsGameSessionReceivedServer = !ServerAddress.IsEmpty();

    /* If the party leader leaves the game session before it has received the server,
    * the party member must leave the game session, too.
    * This is to sync game session creation between the party leader and party members. */
    if (bIsLeaverThePartyLeader && !bIsGameSessionReceivedServer)
    {
    // Push notification to the party that the game session is canceled.
    if (GetPromptSubystem())
    {
    GetPromptSubystem()->HideLoading();
    GetPromptSubystem()->PushNotification(JOIN_PARTY_GAME_SESSION_CANCELED_MESSAGE, FString(""));
    }

    // Leave party game session.
    GetOnlineSession()->LeaveSession(SessionName);
    }
    }
  8. 上記の関数をそれぞれのオンラインセッションイベントにバインドします。これは、サブシステムが初期化されたときに最初に呼び出される、事前定義された Initialize() 関数に以下のコードを追加することで行えます。

    void UPlayWithPartySubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
    {
    // ...
    if (GetSessionInterface())
    {
    // ...
    GetSessionInterface()->OnSessionFailureDelegates.AddUObject(this, &ThisClass::OnPartyGameSessionFailure);
    GetSessionInterface()->OnSessionUpdateConflictErrorDelegates.AddUObject(this, &ThisClass::OnPartyGameSessionUpdateConflictError);
    GetSessionInterface()->OnSessionServerUpdateDelegates.AddUObject(this, &ThisClass::OnPartyGameSessionServerUpdate);
    GetSessionInterface()->OnSessionServerErrorDelegates.AddUObject(this, &ThisClass::OnPartyGameSessionServerError);

    // ...
    GetSessionInterface()->OnSessionParticipantLeftDelegates.AddUObject(this, &ThisClass::OnPartyGameSessionParticipantLeft);
    // ...
    }

    // Handle network failure.
    if (GEngine)
    {
    GEngine->NetworkFailureEvent.AddUObject(this, &ThisClass::OnNetworkFailure);
    }
    // ...
    }
  9. サブシステムが非初期化されたときに、これらの関数をアンバインドします。事前定義された Deinitialize() 関数に以下のコードを追加します:

    void UPlayWithPartySubsystem_Starter::Deinitialize()
    {
    // ...
    if (GetSessionInterface())
    {
    // ...
    GetSessionInterface()->OnSessionFailureDelegates.RemoveAll(this);
    GetSessionInterface()->OnSessionUpdateConflictErrorDelegates.RemoveAll(this);
    GetSessionInterface()->OnSessionServerUpdateDelegates.RemoveAll(this);
    GetSessionInterface()->OnSessionServerErrorDelegates.RemoveAll(this);

    // ...
    GetSessionInterface()->OnSessionParticipantLeftDelegates.RemoveAll(this);
    // ...
    }

    // Remove network failure handler
    if (GEngine)
    {
    GEngine->NetworkFailureEvent.RemoveAll(this);
    }
    // ...
    }

リソース