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

Unreal Engine Module - Introduction to Multiplayer Session - Implementing the subsystem

Last updated on January 13, 2024

Session creation and leave flow

Before we begin, let's first understand how the Session creation and leave flow works.

About the subsystem

Now, you are ready to create the Session Essentials implementation using the AccelByte Online Subsystem (OSS). In the Byte Wars module starter project, we have created an Online Session class called the SessionEssentialsOnlineSession_Starter to handle the session implementation. This Online Session class provides necessary declarations and definitions, so you can begin using them to implement Session functionalities.

Before we discuss the Online Session, make sure your /Config/DefaultEngine.ini already has bEnableV2Sessions enabled. This is necessary, so your game can use matchmaking features provided by the AccelByte OSS.

[OnlineSubsystemAccelByte]
bEnabled=true
bMultipleLocalUsersEnabled=false
bAutoLobbyConnectAfterLoginSuccess=true

...

[OnlineSubsystemAccelByte]
bEnableV2Sessions=true

On to the Online Session, you can find the SessionEssentialsOnlineSession_Starter class files at the following:

  • Header file is in /Source/AccelByteWars/TutorialModules/Play/SessionEssentials/SessionEssentialsOnlineSession_Starter.h.
  • CPP file is in /Source/AccelByteWars/TutorialModules/Play/SessionEssentials/SessionEssentialsOnlineSession_Starter.cpp.

Let's take a look at what we have provided in the class.

  • In the SessionEssentialsOnlineSession_Starter class CPP file, you will see RegisterOnlineDelegates and ClearOnlineDelegates functions. Those functions are provided by Unreal Engine's Online Session, UOnlineSession, the parent of this class. RegisterOnlineDelegates will be called on Game Instance's initialization, just before Game Instance Subsystem initialization. ClearOnlineDelegates will be called just before game close.

  • The parent of this class that is provided by Unreal Engine, the UOnlineSessionClient, come with a function to call the Session Interface, gateway to use the actual session functionality.

    IOnlineSessionPtr UOnlineSessionClient::GetSessionInt()
    {
    UWorld* World = GetWorld();
    if (World == nullptr)
    {
    UE_LOG_ONLINE(Warning, TEXT("UOnlineSessionClient::GetSessionInt: Called with NULL world."));
    return nullptr;
    }

    return Online::GetSessionInterface(World);
    }
  • AccelByte's Session Interface have some functions that is not abstracted by Unreal Engine's Session Interface. We have also provided a function to get the AccelByte's Session Interface in the parent class.

    FOnlineSessionV2AccelBytePtr UAccelByteWarsOnlineSessionBase::GetABSessionInt()
    {
    return StaticCastSharedPtr<FOnlineSessionV2AccelByte>(GetSessionInt());
    }
  • Still in the CPP file, you will see some helper functions:

    FNamedOnlineSession* USessionEssentialsOnlineSession_Starter::GetSession(const FName SessionName)
    {
    return GetSessionInt()->GetNamedSession(SessionName);
    }

    EAccelByteV2SessionType USessionEssentialsOnlineSession_Starter::GetSessionType(const FName SessionName)
    {
    const FNamedOnlineSession* OnlineSession = GetSession(SessionName);
    if (!OnlineSession)
    {
    return EAccelByteV2SessionType::Unknown;
    }

    const FOnlineSessionSettings& OnlineSessionSettings = OnlineSession->SessionSettings;

    return GetABSessionInt()->GetSessionTypeFromSettings(OnlineSessionSettings);
    }

    FName USessionEssentialsOnlineSession_Starter::GetPredefinedSessionNameFromType(const EAccelByteV2SessionType SessionType)
    {
    FName SessionName = FName();

    switch (SessionType)
    {
    case EAccelByteV2SessionType::GameSession:
    SessionName = GameSessionName;
    break;
    case EAccelByteV2SessionType::PartySession:
    SessionName = PartySessionName;
    break;
    default: ;
    }

    return SessionName;
    }
    • GetSession: as the name implies, it will simply get the session based on the given name.
    • GetSessionType: an AccelByte's Session Interface specific function that indicates whether a session is game session or party session.
    • GetPredefinedSessionNameFromType: simply returns a predefined NAME_GameSession for game session and NAME_PartySession for party session. Those names were provided by Unreal Engine to differentiate which session is game session or party session.
  • Now, we will be looking at SessionEssentialsOnlineSession_Starter class header file. You will see seven delegates and its getter functions. These delegates will be our way to connect the UI to the response call when making a request.

    public:
    virtual FOnCreateSessionComplete* GetOnCreateSessionCompleteDelegates() override
    {
    return &OnCreateSessionCompleteDelegates;
    }
    virtual FOnJoinSessionComplete* GetOnJoinSessionCompleteDelegates() override
    {
    return &OnJoinSessionCompleteDelegates;
    }
    virtual FOnSendSessionInviteComplete* GetOnSendSessionInviteCompleteDelegates() override
    {
    return &OnSendSessionInviteCompleteDelegates;
    }
    virtual FOnRejectSessionInviteCompleteMulticast* GetOnRejectSessionInviteCompleteDelegate() override
    {
    return &OnRejectSessionInviteCompleteDelegates;
    }
    virtual FOnDestroySessionComplete* GetOnLeaveSessionCompleteDelegates() override
    {
    return &OnLeaveSessionCompleteDelegates;
    }

    virtual FOnV2SessionInviteReceived* GetOnSessionInviteReceivedDelegates() override
    {
    return &OnSessionInviteReceivedDelegates;
    }
    virtual FOnSessionParticipantsChange* GetOnSessionParticipantsChange() override
    {
    return &OnSessionParticipantsChangeDelegates;
    }

    private:
    FOnCreateSessionComplete OnCreateSessionCompleteDelegates;
    FOnJoinSessionComplete OnJoinSessionCompleteDelegates;
    FOnSendSessionInviteComplete OnSendSessionInviteCompleteDelegates;
    FOnRejectSessionInviteCompleteMulticast OnRejectSessionInviteCompleteDelegates;
    FOnDestroySessionComplete OnLeaveSessionCompleteDelegates;

    FOnV2SessionInviteReceived OnSessionInviteReceivedDelegates;
    FOnSessionParticipantsChange OnSessionParticipantsChangeDelegates;
  • Still in the header file, you will also see a boolean variable. This variable will be useful for later implementation. It won't affect anything in this module.

    protected:
    bool bLeavingSession = false;

Leave session

If you look back at the flow diagram, notice that the create function will call Leave Session in some cases. Hence, we are starting with the Leave session.

  1. Start with declaring functions to request session leave. Open SessionEssentialsOnlineSession_Starter class header file and add the following function declarations.

    public:
    virtual void LeaveSession(FName SessionName) override;
  2. Still in the header file, add one more function declaration for the response callback.

    protected:
    virtual void OnLeaveSessionComplete(FName SessionName, bool bSucceeded) override;
  3. Next, open the SessionEssentialsOnlineSession_Starter class CPP file and add the following implementations. Before calling the request function, verify whether the player is a part of a session or not by using the utility function, GetSession(). We will only trigger the Leave Session request if the player is in a session.

    void USessionEssentialsOnlineSession_Starter::LeaveSession(FName SessionName)
    {
    // safety
    if (!GetSessionInt())
    {
    UE_LOG_ONLINESESSION(Warning, TEXT("Session interface null"))
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnLeaveSessionComplete(SessionName, false);
    }));
    return;
    }

    if (GetSession(SessionName))
    {
    if (!GetABSessionInt()->DestroySession(SessionName))
    {
    UE_LOG_ONLINESESSION(Warning, TEXT("Failed to execute"))
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, SessionName] ()
    {
    OnLeaveSessionComplete(SessionName, false);
    }));
    }
    else
    {
    bLeavingSession = true;
    }
    }
    else
    {
    UE_LOG_ONLINESESSION(Log, TEXT("Not in session"))
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnLeaveSessionComplete(SessionName, true);
    }));
    }
    }
    Note

    The request function to leave session called DestroySession. What it does internally is only leave that session and destroy the local instance of it. The session destruction itself will be handled by backend. Hence, we use LeaveSession as the name to make it more clear.

  4. Now, you are going to implement the response callback. Still in the CPP file, add the following implementation. The callback will simply call the delegate that will be used for other class to execute something when the response callback received.

    void USessionEssentialsOnlineSession_Starter::OnLeaveSessionComplete(FName SessionName, bool bSucceeded)
    {
    UE_LOG_ONLINESESSION(Log, TEXT("succeeded: %s"), *FString(bSucceeded ? "TRUE": "FALSE"))

    bLeavingSession = false;
    OnLeaveSessionCompleteDelegates.Broadcast(SessionName, bSucceeded);
    }
  5. Notice that the response function, OnLeaveSessionComplete has not been called anywhere, yet. At this state, the response function is not yet bound to the response of Leave Session request. Still in the class CPP file, navigate to USessionEssentialsOnlineSession_Starter::RegisterOnlineDelegates and add the highlighted lines on the following codes.

    void USessionEssentialsOnlineSession_Starter::RegisterOnlineDelegates()
    {
    Super::RegisterOnlineDelegates();

    GetSessionInt()->AddOnDestroySessionCompleteDelegate_Handle (FOnDestroySessionCompleteDelegate::CreateUObject(this, &ThisClass::OnLeaveSessionComplete));
    }
  6. Last step of this section, unbind those bound functions. Still in the class CPP file, navigate to USessionEssentialsOnlineSession_Starter::ClearOnlineDelegates and add the highlighted lines on the following codes.

    void USessionEssentialsOnlineSession_Starter::ClearOnlineDelegates()
    {
    Super::ClearOnlineDelegates();

    GetSessionInt()->ClearOnDestroySessionCompleteDelegates(this);
    }
  7. Voilà! Leave session functionality is primed.

Create session

  1. Begin by declaring function to request session creation. Open SessionEssentialsOnlineSession_Starter class header file and add the following function declarations.

    public:
    virtual void CreateSession(
    const int32 LocalUserNum,
    FName SessionName,
    FOnlineSessionSettings SessionSettings,
    const EAccelByteV2SessionType SessionType,
    const FString& SessionTemplateName) override;
  2. Next, add another function declaration for the response callback.

    protected:
    virtual void OnCreateSessionComplete(FName SessionName, bool bSucceeded) override;
  3. Still in the header file, add one more function declaration and a delegate handle variable. This function and delegate handle will be used to call LeaveSession when the player call Create session but is a part of another session.

    private:
    void OnLeaveSessionForCreateSessionComplete(
    FName SessionName,
    bool bSucceeded,
    const int32 LocalUserNum,
    const FOnlineSessionSettings SessionSettings);
    FDelegateHandle OnLeaveSessionForCreateSessionCompleteDelegateHandle;
  4. Next, create the implementation for the request function. Open the SessionEssentialsOnlineSession_Starter class CPP file and add the following code. Before session creation can be requested, session settings need to be set up first: Set the SessionTemplateName that will be used to create the session with the session template name you have set up in the Setting up game sessions; Set up the SessionType as game or party session; And setup server name as preparation for the next module.

    void USessionEssentialsOnlineSession_Starter::CreateSession(
    const int32 LocalUserNum,
    FName SessionName,
    FOnlineSessionSettings SessionSettings,
    const EAccelByteV2SessionType SessionType,
    const FString& SessionTemplateName)
    {
    // safety
    if (!GetSessionInt())
    {
    UE_LOG_ONLINESESSION(Warning, TEXT("Session interface is null"))
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnCreateSessionComplete(SessionName, false);
    }));
    return;
    }
    if (SessionTemplateName.IsEmpty())
    {
    UE_LOG_ONLINESESSION(Warning, TEXT("Session Template Name can't be empty"))
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnCreateSessionComplete(SessionName, false);
    }));
    return;
    }

    #pragma region "Setup Session Settings"
    // Session Template Name
    SessionSettings.Set(SETTING_SESSION_TEMPLATE_NAME, SessionTemplateName);

    // Session type
    if (SessionType != EAccelByteV2SessionType::Unknown)
    {
    SessionSettings.Set(SETTING_SESSION_TYPE, GetPredefinedSessionNameFromType (SessionType).ToString());
    }

    // Set local server name for matchmaking request if any.
    // This is useful if you want to try matchmaking using local dedicated server.
    if (SessionType == EAccelByteV2SessionType::GameSession)
    {
    FString ServerName;
    FParse::Value(FCommandLine::Get(), TEXT("-ServerName="), ServerName);
    if (!ServerName.IsEmpty())
    {
    UE_LOG_ONLINESESSION(Log, TEXT("Requesting to use server with name: %s"), *ServerName)
    SessionSettings.Set(SETTING_GAMESESSION_SERVERNAME, ServerName);
    }
    }
    #pragma endregion

    // if session exist locally -> destroy session first
    if (GetSession(SessionName))
    {
    UE_LOG_ONLINESESSION(Log, TEXT("Session exist locally, leaving session first"))

    // remove from delegate if exist
    if (OnLeaveSessionForCreateSessionCompleteDelegateHandle.IsValid())
    {
    OnLeaveSessionCompleteDelegates.Remove (OnLeaveSessionForCreateSessionCompleteDelegateHandle);
    OnLeaveSessionForCreateSessionCompleteDelegateHandle.Reset();
    }

    OnLeaveSessionForCreateSessionCompleteDelegateHandle = OnLeaveSessionCompleteDelegates.AddUObject(this, & ThisClass::OnLeaveSessionForCreateSessionComplete, LocalUserNum, SessionSettings);
    LeaveSession(SessionName);
    }
    else
    {
    if (!GetSessionInt()->CreateSession(LocalUserNum, SessionName, SessionSettings))
    {
    UE_LOG_ONLINESESSION(Warning, TEXT("Failed to execute"))
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, SessionName] ()
    {
    OnCreateSessionComplete(SessionName, false);
    }));
    }
    }
    }
  5. Next step, implement the response callback function. This function will call the delegate that tells the caller's class that the request has finished executing.

    void USessionEssentialsOnlineSession_Starter::OnCreateSessionComplete(FName SessionName, bool bSucceeded)
    {
    UE_LOG_ONLINESESSION(Log, TEXT("succeeded: %s"), *FString(bSucceeded ? "TRUE": "FALSE"))

    OnCreateSessionCompleteDelegates.Broadcast(SessionName, bSucceeded);
    }
  6. As seen on the CreateSession implementation and the diagram flow, call LeaveSession first if the player is a part of a session. We will now implement that functionality.

    void USessionEssentialsOnlineSession_Starter::OnLeaveSessionForCreateSessionComplete(
    FName SessionName,
    bool bSucceeded,
    const int32 LocalUserNum,
    const FOnlineSessionSettings SessionSettings)
    {
    UE_LOG_ONLINESESSION(Log, TEXT("succeeded: %s"), *FString(bSucceeded ? "TRUE": "FALSE"))
    OnLeaveSessionCompleteDelegates.Remove(OnLeaveSessionForCreateSessionCompleteDelegateHandle);

    if (bSucceeded)
    {
    if (!GetSessionInt()->CreateSession(LocalUserNum, SessionName, SessionSettings))
    {
    UE_LOG_ONLINESESSION(Warning, TEXT("Failed to execute"))
    OnCreateSessionComplete(SessionName, false);
    }
    }
    else
    {
    // Leave Session failed, execute complete delegate as failed
    OnCreateSessionComplete(SessionName, false);
    }
    }
  7. Now, bind the OnCreateSessionComplete function to the response callback. Navigate to the RegisterOnlineDelegates in the CPP file, and add the highlighted line in the following code.

    void USessionEssentialsOnlineSession_Starter::RegisterOnlineDelegates()
    {
    Super::RegisterOnlineDelegates();

    GetSessionInt()->AddOnDestroySessionCompleteDelegate_Handle (FOnDestroySessionCompleteDelegate::CreateUObject(this, &ThisClass::OnLeaveSessionComplete));
    GetSessionInt()->AddOnCreateSessionCompleteDelegate_Handle(FOnCreateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnCreateSessionComplete));
    }
  8. Lastly, unbind the callback when the player exits the game. Add the highlighted code into the ClearOnlineDelegates function.

    void USessionEssentialsOnlineSession_Starter::ClearOnlineDelegates()
    {
    Super::ClearOnlineDelegates();

    GetSessionInt()->ClearOnDestroySessionCompleteDelegates(this);
    GetSessionInt()->ClearOnCreateSessionCompleteDelegates(this);
    }
  9. Create session is now ready to be used.

Join session

  1. Start from the SessionEssentialsOnlineSession_Starter class header file. Add function declaration for the request function.

    public:
    virtual void JoinSession(
    const int32 LocalUserNum,
    FName SessionName,
    const FOnlineSessionSearchResult& SearchResult) override;
  2. Add another declaration for the response callback function.

    protected:
    virtual void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result) override;
  3. Last thing in the header file, add one more function declaration and a delegate handle for the leave session first if the player calling join session is a part of a session.

    private:
    void OnLeaveSessionForJoinSessionComplete(
    FName SessionName,
    bool bSucceeded,
    const int32 LocalUserNum,
    const FOnlineSessionSearchResult SearchResult);
    FDelegateHandle OnLeaveSessionForJoinSessionCompleteDelegateHandle;
  4. Moving on to the implementation. Open the SessionEssentialsOnlineSession_Starter class CPP file and add the following codes. For join session implementation, this function will simply call the JoinSession, without any setup. But, if the player is a part of a session, call LeaveSession first.

    void USessionEssentialsOnlineSession_Starter::JoinSession(
    const int32 LocalUserNum,
    FName SessionName,
    const FOnlineSessionSearchResult& SearchResult)
    {
    // safety
    if (!GetSessionInt())
    {
    UE_LOG_ONLINESESSION(Warning, TEXT("Session interface null"))
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnJoinSessionComplete(SessionName, EOnJoinSessionCompleteResult::UnknownError);
    }));
    return;
    }

    // If session exist -> destroy first then join
    if (GetSession(SessionName))
    {
    // remove from delegate if exist
    if (OnLeaveSessionForJoinSessionCompleteDelegateHandle.IsValid())
    {
    OnLeaveSessionCompleteDelegates.Remove(OnLeaveSessionForJoinSessionCompleteDelegateHandle);
    OnLeaveSessionForJoinSessionCompleteDelegateHandle.Reset();
    }

    OnLeaveSessionForJoinSessionCompleteDelegateHandle = OnLeaveSessionCompleteDelegates.AddUObject(this, &ThisClass::OnLeaveSessionForJoinSessionComplete, LocalUserNum, SearchResult);
    LeaveSession(SessionName);
    }
    else
    {
    if (!GetSessionInt()->JoinSession(LocalUserNum, SessionName, SearchResult))
    {
    UE_LOG_ONLINESESSION(Warning, TEXT("Failed to execute"))
    ExecuteNextTick(FSimpleDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnJoinSessionComplete(SessionName, EOnJoinSessionCompleteResult::UnknownError);
    }));
    }
    }
    }
  5. Next step, the callback function implementation. Just like the previous functionality, this function will simply call the corresponding delegate.

    void USessionEssentialsOnlineSession_Starter::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
    {
    UE_LOG_ONLINESESSION(Log, TEXT("succeeded: %s"), *FString(Result == EOnJoinSessionCompleteResult::Success ? "TRUE" : "FALSE"))

    OnJoinSessionCompleteDelegates.Broadcast(SessionName, Result);
    }
  6. Now, on to the join session after leave session functionality. Add the following code to the CPP file.

    void USessionEssentialsOnlineSession_Starter::OnLeaveSessionForJoinSessionComplete(
    FName SessionName,
    bool bSucceeded,
    const int32 LocalUserNum,
    const FOnlineSessionSearchResult SearchResult)
    {
    UE_LOG_ONLINESESSION(Log, TEXT("succeeded: %s"), *FString(bSucceeded ? "TRUE": "FALSE"))
    OnLeaveSessionCompleteDelegates.Remove(OnLeaveSessionForJoinSessionCompleteDelegateHandle);

    if (bSucceeded)
    {
    if (!GetSessionInt()->JoinSession(LocalUserNum, SessionName, SearchResult))
    {
    UE_LOG_ONLINESESSION(Warning, TEXT("failed to execute"))
    OnJoinSessionComplete(SessionName, EOnJoinSessionCompleteResult::UnknownError);
    }
    }
    else
    {
    // Leave Session failed, execute complete delegate as failed
    OnJoinSessionComplete(SessionName, EOnJoinSessionCompleteResult::UnknownError);
    }
    }
  7. Still in the CPP file, navigate to RegisterOnlineDelegates function and bind the response callback to the corresponding OSS delegate by adding the highlighted code below.

    void USessionEssentialsOnlineSession_Starter::RegisterOnlineDelegates()
    {
    Super::RegisterOnlineDelegates();

    GetSessionInt()->AddOnDestroySessionCompleteDelegate_Handle (FOnDestroySessionCompleteDelegate::CreateUObject(this, &ThisClass::OnLeaveSessionComplete));
    GetSessionInt()->AddOnCreateSessionCompleteDelegate_Handle(FOnCreateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnCreateSessionComplete));
    GetSessionInt()->AddOnJoinSessionCompleteDelegate_Handle(FOnJoinSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnJoinSessionComplete));
    }
  8. Next, navigate to ClearOnlineDelegates and unbind the response callback.

    void USessionEssentialsOnlineSession_Starter::ClearOnlineDelegates()
    {
    Super::ClearOnlineDelegates();

    GetSessionInt()->ClearOnDestroySessionCompleteDelegates(this);
    GetSessionInt()->ClearOnCreateSessionCompleteDelegates(this);
    GetSessionInt()->ClearOnJoinSessionCompleteDelegates(this);
    }
  9. Join session implementation, is now finished.

Send and receive session invitation

  1. Begin by opening the SessionEssentialsOnlineSession_Starter header file. Add this function declaration for the request function.

    public:
    virtual void SendSessionInvite(const int32 LocalUserNum, FName SessionName, const FUniqueNetIdPtr Invitee) override;
  2. Continuing on the declarations, add this function declaration for the response callback.

    protected:
    virtual void OnSendSessionInviteComplete(
    const FUniqueNetId& LocalSenderId,
    FName SessionName,
    bool bSucceeded,
    const FUniqueNetId& InviteeId) override;
  3. Last step inside the header file, add function declaration that will be called upon receiving invitation.

    protected:
    virtual void OnSessionInviteReceived(
    const FUniqueNetId& UserId,
    const FUniqueNetId& FromId,
    const FOnlineSessionInviteAccelByte& Invite) override;
  4. On to the next step, the implementation. Open the SessionEssentialsOnlineSession_Starter CPP file and add the following codes for the SendSessionInvite function. This function will simply call the corresponding OSS function.

    void USessionEssentialsOnlineSession_Starter::SendSessionInvite(
    const int32 LocalUserNum,
    FName SessionName,
    const FUniqueNetIdPtr Invitee)
    {
    if (!Invitee.IsValid())
    {
    UE_LOG_ONLINESESSION(Log, TEXT("Invitee net id is invalid. Cancelling operation"));
    return;
    }

    GetABSessionInt()->SendSessionInviteToFriend(LocalUserNum, SessionName, *Invitee.Get());
    }
  5. Still in the CPP file, add the following codes for the OnSendSessionInviteComplete function. This function will also just call the corresponding delegate to let other objects knows when the response was received.

    void USessionEssentialsOnlineSession_Starter::OnSendSessionInviteComplete(
    const FUniqueNetId& LocalSenderId,
    FName SessionName,
    bool bSucceeded,
    const FUniqueNetId& InviteeId)
    {
    UE_LOG_ONLINESESSION(Log, TEXT("succeeded: %s"), *FString(bSucceeded ? "TRUE" : "FALSE"))

    OnSendSessionInviteCompleteDelegates.Broadcast(LocalSenderId, SessionName, bSucceeded, InviteeId);
    }
  6. Next, the implementation for the invitation received function. Just like the OnSendSessionInviteComplete, this function will simply call the corresponding delegate.

    void USessionEssentialsOnlineSession_Starter::OnSessionInviteReceived(
    const FUniqueNetId& UserId,
    const FUniqueNetId& FromId,
    const FOnlineSessionInviteAccelByte& Invite)
    {
    UE_LOG_ONLINESESSION(Log, TEXT("from: %s"), *FromId.ToDebugString())

    OnSessionInviteReceivedDelegates.Broadcast(UserId, FromId, Invite);
    }
  7. Now, bind the response callback and the invitation received to the corresponding OSS delegate. Still on the CPP file, navigate to the RegisterOnlineDelegates function and add these highlighted codes.

    void USessionEssentialsOnlineSession_Starter::RegisterOnlineDelegates()
    {
    Super::RegisterOnlineDelegates();

    GetSessionInt()->AddOnDestroySessionCompleteDelegate_Handle (FOnDestroySessionCompleteDelegate::CreateUObject(this, &ThisClass::OnLeaveSessionComplete));
    GetSessionInt()->AddOnCreateSessionCompleteDelegate_Handle(FOnCreateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnCreateSessionComplete));
    GetSessionInt()->AddOnJoinSessionCompleteDelegate_Handle(FOnJoinSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnJoinSessionComplete));
    GetABSessionInt()->AddOnSendSessionInviteCompleteDelegate_Handle(FOnSendSessionInviteCompleteDelegate::CreateUObject(this, &ThisClass::OnSendSessionInviteComplete));
    GetABSessionInt()->AddOnV2SessionInviteReceivedDelegate_Handle(FOnV2SessionInviteReceivedDelegate::CreateUObject(this, &ThisClass::OnSessionInviteReceived));
    }
  8. Finally, unbind those callbacks. Navigate to the ClearOnlineDelegates function and add these highlighted codes.

    void USessionEssentialsOnlineSession_Starter::ClearOnlineDelegates()
    {
    Super::ClearOnlineDelegates();

    GetSessionInt()->ClearOnDestroySessionCompleteDelegates(this);
    GetSessionInt()->ClearOnCreateSessionCompleteDelegates(this);
    GetSessionInt()->ClearOnJoinSessionCompleteDelegates(this);
    GetABSessionInt()->ClearOnSendSessionInviteCompleteDelegates(this);
    GetABSessionInt()->ClearOnV2SessionInviteReceivedDelegates(this);
    }
  9. Send session invitation is set and ready to be used.

Reject session invitation

  1. Open the SessionEssentialsOnlineSession_Starter header file and add the following function declaration. This function is responsible to request the reject functionality.

    public:
    virtual void RejectSessionInvite(const int32 LocalUserNum, const FOnlineSessionInviteAccelByte& Invite) override;
  2. Still in the SessionEssentialsOnlineSession_Starter header file, add the declaration for the response callback.

    protected:
    virtual void OnRejectSessionInviteComplete(bool bSucceeded) override;
  3. Onward to the implementation. Open the SessionEssentialsOnlineSession_Starter CPP file and add this implementation for the RejectSessionInvite function. The implementation for this function is quite straight forward. There's only a logic to retrieve the local user's unique ID before calling the corresponding OSS function. Notice that, instead of binding the response callback to a delegate, the RejectInvite OSS function takes the response callback as the parameter, removing the need to separately bind and unbind the response delegate.

    void USessionEssentialsOnlineSession_Starter::RejectSessionInvite(
    const int32 LocalUserNum,
    const FOnlineSessionInviteAccelByte& Invite)
    {
    const FUniqueNetIdPtr LocalUserNetId = GetLocalPlayerUniqueNetId(GetPlayerControllerByLocalUserNum(LocalUserNum));
    if (!LocalUserNetId.IsValid())
    {
    UE_LOG_ONLINESESSION(Log, TEXT("Local User net id is invalid. Cancelling operation"));
    return;
    }

    GetABSessionInt()->RejectInvite(
    *LocalUserNetId.Get(),
    Invite,
    FOnRejectSessionInviteComplete::CreateUObject(this, &ThisClass::OnRejectSessionInviteComplete));
    }
  4. Still in the CPP file, add this implementation for the response callback.

    void USessionEssentialsOnlineSession_Starter::OnRejectSessionInviteComplete(bool bSucceeded)
    {
    UE_LOG_ONLINESESSION(Log, TEXT("succeeded: %s"), *FString(bSucceeded ? "TRUE" : "FALSE"))

    OnRejectSessionInviteCompleteDelegates.Broadcast(bSucceeded);
    }
  5. Reject session invitation, implemented.

Session participant changed callback

This callback will be called when other player joins and leaves the session.

  1. Start with function declaration. Open the SessionEssentialsOnlineSession_Starter header file and add this declaration.

    protected:
    virtual void OnSessionParticipantsChange(FName SessionName, const FUniqueNetId& Member, bool bJoined) override;
  2. Next step, implementation. Open the SessionEssentialsOnlineSession_Starter CPP file and add the following codes. This function will also simply call the corresponding delegate.

    void USessionEssentialsOnlineSession_Starter::OnSessionParticipantsChange(
    FName SessionName,
    const FUniqueNetId& Member,
    bool bJoined)
    {
    UE_LOG_ONLINESESSION(
    Log,
    TEXT("session name: %s | Member: %s [%s]"),
    *SessionName.ToString(),
    *Member.ToDebugString(),
    *FString(bJoined ? "Joined" : "Left"))

    OnSessionParticipantsChangeDelegates.Broadcast(SessionName, Member, bJoined);
    }
  3. Now, bind the callback to the corresponding OSS delegate. Navigate to RegisterOnlineDelegates and add this highlighted code.

    void USessionEssentialsOnlineSession_Starter::RegisterOnlineDelegates()
    {
    Super::RegisterOnlineDelegates();

    GetSessionInt()->AddOnDestroySessionCompleteDelegate_Handle (FOnDestroySessionCompleteDelegate::CreateUObject(this, &ThisClass::OnLeaveSessionComplete));
    GetSessionInt()->AddOnCreateSessionCompleteDelegate_Handle(FOnCreateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnCreateSessionComplete));
    GetSessionInt()->AddOnJoinSessionCompleteDelegate_Handle(FOnJoinSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnJoinSessionComplete));
    GetABSessionInt()->AddOnSendSessionInviteCompleteDelegate_Handle(FOnSendSessionInviteCompleteDelegate::CreateUObject(this, &ThisClass::OnSendSessionInviteComplete));
    GetABSessionInt()->AddOnV2SessionInviteReceivedDelegate_Handle(FOnV2SessionInviteReceivedDelegate::CreateUObject(this, &ThisClass::OnSessionInviteReceived));
    GetABSessionInt()->AddOnSessionParticipantsChangeDelegate_Handle(FOnSessionParticipantsChangeDelegate::CreateUObject(this, &ThisClass::OnSessionParticipantsChange));
    }
  4. On to the final stretch. Navigate to ClearOnlineDelegates and unbind the callback by adding this highlighted code.

    void USessionEssentialsOnlineSession_Starter::ClearOnlineDelegates()
    {
    Super::ClearOnlineDelegates();

    GetSessionInt()->ClearOnDestroySessionCompleteDelegates(this);
    GetSessionInt()->ClearOnCreateSessionCompleteDelegates(this);
    GetSessionInt()->ClearOnJoinSessionCompleteDelegates(this);
    GetABSessionInt()->ClearOnSendSessionInviteCompleteDelegates(this);
    GetABSessionInt()->ClearOnV2SessionInviteReceivedDelegates(this);
    GetABSessionInt()->ClearOnSessionParticipantsChangeDelegates(this);
    }
  5. Congratulations! You have reached the end of the subsystem implementation section. Compile your project again and make sure it has no compile errors.

Resources