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

すべてを統合する - パーティでプレイする - (Unreal Engine モジュール)

Last updated on July 1, 2024

Connect the UI to the play with party flow

You have created playing with party implementation in the PlayWithPartySubsystem_Starter subsystem. That subsystem already has functions to display notifications and prompts based on events related to playing with a party. As mentioned before, however, we only want the game to start the game session with the party if the one who initiated it is the party leader, and only if other party members are not in other game sessions. To do this, we will need some functions to perform validation.

  1. Open the PlayWithPartySubsystem_Starter class Header file and declare the following functions:

    protected:
    bool ValidateToStartPartyGameSession();
    bool ValidateToJoinPartyGameSession(const FOnlineSessionSearchResult& SessionSearchResult);
    bool ValidateToStartPartyMatchmaking(const EGameModeType GameModeType);
  2. Declare the ValidateToStartPartyGameSession() function first. In the PlayWithPartySubsystem_Starter class CPP file, add the code below. This function checks if the game session initiator is the party leader. It also checks if other party members are in other game sessions.

    bool UPlayWithPartySubsystem_Starter::ValidateToStartPartyGameSession()
    {
    // Safety.
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot validate to start party game session. Interfaces or online session are not valid."));
    return false;
    }

    // If not in party session, no need to validate.
    const FNamedOnlineSession* PartySession = GetSessionInterface()->GetNamedSession(
    GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession));
    if (!PartySession)
    {
    UE_LOG_PLAYINGWITHPARTY(Log, TEXT("No need to validate to start party game session. Player is not in a party."));
    return true;
    }
    const TSharedPtr<FOnlineSessionInfoAccelByteV2> PartySessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(PartySession->SessionInfo);
    if (!PartySessionInfo)
    {
    UE_LOG_PLAYINGWITHPARTY(Log, TEXT("No need to validate to start party game session. Player is not in a party."));
    return true;
    }

    // Get current player.
    FUniqueNetIdPtr UserId = nullptr;
    if (GetIdentityInterface())
    {
    UserId = GetIdentityInterface()->GetUniquePlayerId(0);
    }

    // Only party leader is able to start party game session.
    if (!GetOnlineSession()->IsPartyLeader(UserId))
    {
    if (GetPromptSubystem())
    {
    GetPromptSubystem()->PushNotification(PARTY_GAME_SESSION_MEMBER_SAFEGUARD_MESSAGE, FString(""));
    }
    return false;
    }

    // Only able to start party game session if other party members are not in other game session.
    bool bResult = !IsGameSessionDifferFromParty(UserId);

    /* Show notification that unable to start any game session
    * if other party members are in other game session.*/
    if (!bResult && GetPromptSubystem())
    {
    GetPromptSubystem()->PushNotification(PARTY_GAME_SESSION_LEADER_SAFEGUARD_MESSAGE, FString(""));
    }

    return bResult;
    }
  3. Declare the ValidateToJoinPartyGameSession() function. In the PlayWithPartySubsystem_Starter class CPP file, add the code below. This function has the same behavior as ValidateToStartSession(). It also checks if the game session to be joined has sufficient space to join.

    bool UPlayWithPartySubsystem_Starter::ValidateToJoinPartyGameSession(const FOnlineSessionSearchResult& SessionSearchResult)
    {
    if (!ValidateToStartPartyGameSession())
    {
    return false;
    }

    // Safety.
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot validate to join party game session. Interfaces or online session are not valid."));
    return false;
    }

    // If not in party session, no need to validate.
    const FNamedOnlineSession* PartySession = GetSessionInterface()->GetNamedSession(
    GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession));
    if (!PartySession)
    {
    UE_LOG_PLAYINGWITHPARTY(Log, TEXT("No need to validate to join party game session. Player is not in a party."));
    return true;
    }
    const TSharedPtr<FOnlineSessionInfoAccelByteV2> PartySessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(PartySession->SessionInfo);
    if (!PartySessionInfo)
    {
    UE_LOG_PLAYINGWITHPARTY(Log, TEXT("No need to validate to join party game session. Player is not in a party."));
    return true;
    }

    TSharedPtr<FOnlineSessionInfoAccelByteV2> SessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(SessionSearchResult.Session.SessionInfo);
    if (!SessionInfo)
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot validate to join party game session. Session is not valid."));
    return false;
    }

    TSharedPtr<FAccelByteModelsV2BaseSession> SessionData = SessionInfo->GetBackendSessionData();
    if (!SessionData)
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot validate to join session. Session data is not valid."));
    return false;
    }

    // Check if session slots is sufficient to join with party
    int32 ActiveMemberCount = SessionData->Members.FilterByPredicate([](FAccelByteModelsV2SessionUser Temp)
    {
    return Temp.StatusV2 == EAccelByteV2SessionMemberStatus::JOINED;
    }).Num();

    bool bResult =
    (SessionSearchResult.Session.SessionSettings.NumPublicConnections - ActiveMemberCount) >=
    GetOnlineSession()->GetPartyMembers().Num();

    // Notify that no more slots to join the session.
    if (!bResult && GetPromptSubystem())
    {
    GetPromptSubystem()->PushNotification(JOIN_PARTY_GAME_SESSION_SAFEGUARD_MESSAGE, FString(""));
    }

    return bResult;
    }
  4. Declare the ValidateToStartPartyMatchmaking() function. In the PlayWithPartySubsystem_Starter class CPP file, add the code below. This function also has the same behavior as ValidateToStartSession(). It also checks if the game mode supports playing with a party. In this case, Byte Wars does not support party matchmaking for Elimination mode since it requires teams of only one member.

    bool UPlayWithPartySubsystem_Starter::ValidateToStartPartyMatchmaking(const EGameModeType GameModeType)
    {
    if (!ValidateToStartPartyGameSession())
    {
    return false;
    }

    // Safety.
    if (!GetSessionInterface() || !GetOnlineSession())
    {
    UE_LOG_PLAYINGWITHPARTY(Warning, TEXT("Cannot validate to start party matchmaking. Interfaces or online session are not valid."));
    return false;
    }

    // If not in party session, no need to validate.
    const FNamedOnlineSession* PartySession = GetSessionInterface()->GetNamedSession(
    GetOnlineSession()->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::PartySession));
    if (!PartySession)
    {
    UE_LOG_PLAYINGWITHPARTY(Log, TEXT("No need to validate to start party matchmaking. Player is not in a party."));
    return true;
    }
    const TSharedPtr<FOnlineSessionInfoAccelByteV2> PartySessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(PartySession->SessionInfo);
    if (!PartySessionInfo)
    {
    UE_LOG_PLAYINGWITHPARTY(Log, TEXT("No need to validate to start party matchmaking. Player is not in a party."));
    return true;
    }

    // Check whether matchmaking with party is supported using the specified game mode.
    if (GetOnlineSession()->GetPartyMembers().Num() <= 1)
    {
    return true;
    }
    bool bResult = (GameModeType != EGameModeType::FFA);
    UE_LOG_PLAYINGWITHPARTY(Log, TEXT("Validating to start matchmaking with party in %s game mode."), GameModeType == EGameModeType::FFA ? TEXT("Elimination") : TEXT("Team Deathmatch"));

    // Notify cannot matchmaking using the specified game mode.
    if (!bResult && GetPromptSubystem())
    {
    GetPromptSubystem()->PushNotification(PARTY_MATCHMAKING_SAFEGUARD_MESSAGE, FString(""));
    }

    return bResult;
    }
  5. Bind those events to be called when the player tries to access user interfaces (UI) that are related to a Byte Wars online session. To do this, add the following code in the predefined Initialize() function, which is the first function to be called when the subsystem is initialized:

    void UPlayWithPartySubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
    {
    Super::Initialize(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);

    // 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);

    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()->OnSessionParticipantRemovedDelegates.AddUObject(this, &ThisClass::OnPartyGameSessionParticipantRemoved);
    }

    // Handle network failure.
    if (GEngine)
    {
    GEngine->NetworkFailureEvent.AddUObject(this, &ThisClass::OnNetworkFailure);
    }

    // Add party validation to online session related UIs.
    if (GetOnlineSession())
    {
    GetOnlineSession()->ValidateToStartSession.Unbind();
    GetOnlineSession()->ValidateToStartSession.BindUObject(this, &ThisClass::ValidateToStartPartyGameSession);

    GetOnlineSession()->ValidateToStartMatchmaking.Unbind();
    GetOnlineSession()->ValidateToStartMatchmaking.BindUObject(this, &ThisClass::ValidateToStartPartyMatchmaking);

    GetOnlineSession()->ValidateToJoinSession.Unbind();
    GetOnlineSession()->ValidateToJoinSession.BindUObject(this, &ThisClass::ValidateToJoinPartyGameSession);
    }
    }
  6. Unbind the functions when the subsystem is deinitialized. Add the following code in the predefined Deinitialize() function:

    void UPlayWithPartySubsystem_Starter::Deinitialize()
    {
    Super::Deinitialize();

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

    // 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);

    GetSessionInterface()->OnSessionFailureDelegates.RemoveAll(this);
    GetSessionInterface()->OnSessionUpdateConflictErrorDelegates.RemoveAll(this);
    GetSessionInterface()->OnSessionServerUpdateDelegates.RemoveAll(this);
    GetSessionInterface()->OnSessionServerErrorDelegates.RemoveAll(this);
    GetSessionInterface()->OnSessionParticipantRemovedDelegates.RemoveAll(this);
    }

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

    // Remove party validation to online session related UIs.
    if (GetOnlineSession())
    {
    if (GetOnlineSession()->ValidateToStartSession.GetUObject() == this)
    {
    GetOnlineSession()->ValidateToStartSession.Unbind();
    }

    if (GetOnlineSession()->ValidateToStartMatchmaking.GetUObject() == this)
    {
    GetOnlineSession()->ValidateToStartMatchmaking.Unbind();
    }

    if (GetOnlineSession()->ValidateToJoinSession.GetUObject() == this)
    {
    GetOnlineSession()->ValidateToJoinSession.Unbind();
    }
    }
    }
  7. When the player tries to access UIs that are related to Byte Wars online sessions, it will validate first before performing any features related to playing with a party. Those UIs are for when the player tries to access Quick Play for matchmaking, creating a party session or match session, and also browsing game sessions.

    • The ValidateToStartPartyGameSession() function will be called when the player clicks on the Play Online > Create A Session > Create Elimination and Play Online > Create Match Session > [any game mode button] > [any network mode button].

    • The ValidateToJoinPartyGameSession() function will be called when the player clicks on the Join button from Play Online > Browse Matches.

    • The ValidateToStartPartyMatchmaking() function will be called when the player selects Server Type to matchmaking from Play Online > Quick Play > [any game mode button] > [any network mode button].

  8. You will see a notification saying either Cannot matchmake in elimination mode when in party on Quick Play > Elimination > [any network mode button] or Only Party Leader can start Online Session on Create Match Session > [any game mode button] > [any network mode button].

Resources