Skip to main content

Implementing the subsystem - Introduction to Session - (Unreal Engine module)

Last updated on October 24, 2024

Session creation and leave flow

Before you begin, take a look at how the session creation and leave flow works:

About the subsystem

You are now ready to implement the Session Essentials using the AccelByte Gaming Services (AGS) Online Subsystem (OSS). In the Byte Wars module project, there is an Online Session class called 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 features.

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

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

...

[OnlineSubsystemAccelByte]
bEnableV2Sessions=true

You can find the SessionEssentialsOnlineSession_Starter class files at the following locations:

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

Take a look at what is provided in the class:

  • In the SessionEssentialsOnlineSession_Starter class CPP file, you will see RegisterOnlineDelegates() and ClearOnlineDelegates() functions. These functions are provided by Unreal Engine's Online Session, UOnlineSession, which is the grandparent of this class. The RegisterOnlineDelegates() will be called on Game Instance's initialization, just before Game Instance Subsystem initialization. While the ClearOnlineDelegates() will be called just before the game closes.

  • The SessionEssentialsOnlineSession_Starter class derives from AccelByteWarsOnlineSessionBase. The AccelByteWarsOnlineSessionBase has basic helpers related to game session services. These classes have several functions to get the AGS OSS interfaces and helpers you can use to implement session-related functions later.

    FOnlineSessionV2AccelBytePtr UAccelByteWarsOnlineSessionBase::GetABSessionInt()
    {
    return StaticCastSharedPtr<FOnlineSessionV2AccelByte>(GetSessionInt());
    }
    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;
    }
    • GetABSessionInt(): a function to get the AGS OSS session interface.
    • GetSession(): a function to get a game session based on the given name.
    • GetSessionType(): a function specific to AGS Session Interface that indicates whether a session is a game session or a party session.
    • GetPredefinedSessionNameFromType(): returns a predefined NAME_GameSession for a game session and NAME_PartySession for a party session. These names were provided by Unreal Engine to differentiate which session is a game session or a party session.
  • Take a look at the SessionEssentialsOnlineSession_Starter class Header file. You will see seven delegates and their getter functions. These delegates will be your 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 FOnUpdateSessionComplete* GetOnUpdateSessionCompleteDelegates() override
    {
    return &OnUpdateSessionCompleteDelegates;
    }

    virtual FOnV2SessionInviteReceived* GetOnSessionInviteReceivedDelegates() override
    {
    return &OnSessionInviteReceivedDelegates;
    }
    virtual FOnSessionParticipantsChange* GetOnSessionParticipantsChange() override
    {
    return &OnSessionParticipantsChangeDelegates;
    }
    private:
    // ...
    FOnCreateSessionComplete OnCreateSessionCompleteDelegates;
    FOnJoinSessionComplete OnJoinSessionCompleteDelegates;
    FOnSendSessionInviteComplete OnSendSessionInviteCompleteDelegates;
    FOnRejectSessionInviteCompleteMulticast OnRejectSessionInviteCompleteDelegates;
    FOnDestroySessionComplete OnLeaveSessionCompleteDelegates;
    FOnUpdateSessionComplete OnUpdateSessionCompleteDelegates;

    FOnV2SessionInviteReceived OnSessionInviteReceivedDelegates;
    FOnSessionParticipantsChange OnSessionParticipantsChangeDelegates;
  • Still in the Header file, you will also see a boolean variable bLeavingSession. This variable will be used for implementation in later modules.

    protected:
    bool bLeavingSession = false;

Leave session

If you look back at the flow diagram, you will notice that the create function will leave the session in some cases, so you will start with leave session.

  1. Start with declaring functions to request session leave. Open the 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. Open the SessionEssentialsOnlineSession_Starter class CPP file and add the code below. 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)
    {
    UE_LOG_SESSIONESSENTIALS(Verbose, TEXT("called"))

    // Abort if the session interface is invalid.
    if (!GetSessionInt())
    {
    UE_LOG_SESSIONESSENTIALS(Warning, TEXT("Session interface null"))
    ExecuteNextTick(FTimerDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnLeaveSessionComplete(SessionName, false);
    }));
    return;
    }

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

    The request function to leave a session is called DestroySession, which leaves that session and destroys the local instance of it. The session destruction itself will be handled by the backend, so you use LeaveSession as the name to make it clearer.

  4. Implement the response callback. Still in the CPP file, add the code below. The callback will call the delegate that will be used for other classes to execute something when the response callback is received.

    void USessionEssentialsOnlineSession_Starter::OnLeaveSessionComplete(FName SessionName, bool bSucceeded)
    {
    UE_LOG_SESSIONESSENTIALS(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. Currently, the response function is not bound to the response of the leave session request. Still in the class CPP file, navigate to USessionEssentialsOnlineSession_Starter::RegisterOnlineDelegates and add the following code:

    void USessionEssentialsOnlineSession_Starter::RegisterOnlineDelegates()
    {
    // ...
    GetSessionInt()->AddOnDestroySessionCompleteDelegate_Handle(
    FOnDestroySessionCompleteDelegate::CreateUObject(this, &ThisClass::OnLeaveSessionComplete));
    // ...
    }
  6. Unbind the bound functions. Still in the class CPP file, navigate to USessionEssentialsOnlineSession_Starter::ClearOnlineDelegates and add the following code:

    void USessionEssentialsOnlineSession_Starter::ClearOnlineDelegates()
    {
    // ...
    GetSessionInt()->ClearOnDestroySessionCompleteDelegates(this);
    // ...
    }

Create session

  1. Declare a function to request session creation. Open the 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. 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 calls 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. Create the implementation for the request function. Open the SessionEssentialsOnlineSession_Starter class CPP file and add the code below. 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 Set up game session, set the SessionType as a game or a party session, and set the 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)
    {
    UE_LOG_SESSIONESSENTIALS(Verbose, TEXT("called"))

    // Abort if the session interface is invalid.
    if (!GetSessionInt())
    {
    UE_LOG_SESSIONESSENTIALS(Warning, TEXT("Session interface is null"))
    ExecuteNextTick(FTimerDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnCreateSessionComplete(SessionName, false);
    }));
    return;
    }
    if (SessionTemplateName.IsEmpty())
    {
    UE_LOG_SESSIONESSENTIALS(Warning, TEXT("Session Template Name can't be empty"))
    ExecuteNextTick(FTimerDelegate::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());
    }

    if (SessionType == EAccelByteV2SessionType::GameSession)
    {
    // Check for DS version override.
    const FString OverriddenDSVersion = UTutorialModuleOnlineUtility::GetDedicatedServerVersionOverride();
    if (!OverriddenDSVersion.IsEmpty())
    {
    SessionSettings.Set(SETTING_GAMESESSION_CLIENTVERSION, OverriddenDSVersion);
    }

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

    // If the session exists locally, then destroy the session first.
    if (GetSession(SessionName))
    {
    UE_LOG_SESSIONESSENTIALS(Log, TEXT("The session exists locally. Leaving session first."))

    // Reset the delegate.
    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_SESSIONESSENTIALS(Warning, TEXT("Failed to execute"))
    ExecuteNextTick(FTimerDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnCreateSessionComplete(SessionName, false);
    }));
    }
    }
    }
  5. 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_SESSIONESSENTIALS(Log, TEXT("succeeded: %s"), *FString(bSucceeded ? "TRUE": "FALSE"))

    OnCreateSessionCompleteDelegates.Broadcast(SessionName, bSucceeded);
    }
  6. As seen in the CreateSession implementation and the diagram flow, call LeaveSession first if the player is a part of a session as in the following code:

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

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

    void USessionEssentialsOnlineSession_Starter::RegisterOnlineDelegates()
    {
    // ...
    GetSessionInt()->AddOnCreateSessionCompleteDelegate_Handle(
    FOnCreateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnCreateSessionComplete));
    // ...
    }
  8. Unbind the callback when the player exits the game. Add the code below into the ClearOnlineDelegates function.

    void USessionEssentialsOnlineSession_Starter::ClearOnlineDelegates()
    {
    // ...
    GetSessionInt()->ClearOnCreateSessionCompleteDelegates(this);
    // ...
    }

Join session

  1. In the SessionEssentialsOnlineSession_Starter class Header file, add a 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. Still in the Header file, add one more function declaration and a delegate to handle the leave session first if the player calling join session is already part of a session:

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

    void USessionEssentialsOnlineSession_Starter::JoinSession(
    const int32 LocalUserNum,
    FName SessionName,
    const FOnlineSessionSearchResult& SearchResult)
    {
    UE_LOG_SESSIONESSENTIALS(Verbose, TEXT("called"))

    // Abort if the session interface is invalid.
    if (!GetSessionInt())
    {
    UE_LOG_SESSIONESSENTIALS(Warning, TEXT("Session interface null"))
    ExecuteNextTick(FTimerDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnJoinSessionComplete(SessionName, EOnJoinSessionCompleteResult::UnknownError);
    }));
    return;
    }

    // If the session exist, then destroy it first and then join the new one.
    if (GetSession(SessionName))
    {
    // Reset the delegate.
    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_SESSIONESSENTIALS(Warning, TEXT("Failed to execute"))
    ExecuteNextTick(FTimerDelegate::CreateWeakLambda(this, [this, SessionName]()
    {
    OnJoinSessionComplete(SessionName, EOnJoinSessionCompleteResult::UnknownError);
    }));
    }
    }
    }
  5. Implement the callback function. Just like the before, this function will simply call the corresponding delegate.

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

    OnJoinSessionCompleteDelegates.Broadcast(SessionName, Result);
    }
  6. Add the following code to the CPP file to implement joining a session after the leave session:

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

    if (bSucceeded)
    {
    if (!GetSessionInt()->JoinSession(LocalUserNum, SessionName, SearchResult))
    {
    UE_LOG_SESSIONESSENTIALS(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 code below:

    void USessionEssentialsOnlineSession_Starter::RegisterOnlineDelegates()
    {
    // ...
    GetSessionInt()->AddOnJoinSessionCompleteDelegate_Handle(
    FOnJoinSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnJoinSessionComplete));
    // ...
    }
  8. Navigate to ClearOnlineDelegates and unbind the response callback.

    void USessionEssentialsOnlineSession_Starter::ClearOnlineDelegates()
    {
    // ...
    GetSessionInt()->ClearOnJoinSessionCompleteDelegates(this);
    // ...
    }

Send and receive session invitations

  1. Open 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. Add this function declaration for the response callback.

    protected:
    // ...
    virtual void OnSendSessionInviteComplete(
    const FUniqueNetId& LocalSenderId,
    FName SessionName,
    bool bSucceeded,
    const FUniqueNetId& InviteeId) override;
  3. Still in the Header file, add a function declaration that will be called upon receiving an invitation.

    protected:
    // ...
    virtual void OnSessionInviteReceived(
    const FUniqueNetId& UserId,
    const FUniqueNetId& FromId,
    const FOnlineSessionInviteAccelByte& Invite) override;
  4. Open the SessionEssentialsOnlineSession_Starter CPP file and add the code below for the SendSessionInvite function. This function will call the corresponding OSS function.

    void USessionEssentialsOnlineSession_Starter::SendSessionInvite(
    const int32 LocalUserNum,
    FName SessionName,
    const FUniqueNetIdPtr Invitee)
    {
    UE_LOG_SESSIONESSENTIALS(Verbose, TEXT("Called"));

    if (!Invitee.IsValid())
    {
    UE_LOG_SESSIONESSENTIALS(Log, TEXT("Invitee net id is invalid. Cancelling operation"));
    return;
    }

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

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

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

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

    OnSessionInviteReceivedDelegates.Broadcast(UserId, FromId, Invite);
    }
  7. 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 the code below:

    void USessionEssentialsOnlineSession_Starter::RegisterOnlineDelegates()
    {
    // ...
    GetABSessionInt()->AddOnSendSessionInviteCompleteDelegate_Handle(
    FOnSendSessionInviteCompleteDelegate::CreateUObject(this, &ThisClass::OnSendSessionInviteComplete));
    // ...
    GetABSessionInt()->AddOnV2SessionInviteReceivedDelegate_Handle(
    FOnV2SessionInviteReceivedDelegate::CreateUObject(this, &ThisClass::OnSessionInviteReceived));
    // ...
    }
  8. Unbind those callbacks. Navigate to the ClearOnlineDelegates function and add the code below:

    void USessionEssentialsOnlineSession_Starter::ClearOnlineDelegates()
    {
    // ...
    GetABSessionInt()->ClearOnSendSessionInviteCompleteDelegates(this);
    // ...
    GetABSessionInt()->ClearOnV2SessionInviteReceivedDelegates(this);
    // ...
    }

Reject session invitation

  1. Open the SessionEssentialsOnlineSession_Starter Header file and add the following function declaration. This function is responsible for requesting 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. Open the SessionEssentialsOnlineSession_Starter CPP file and add the implementation below for the RejectSessionInvite function. The logic retrieves 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)
    {
    UE_LOG_SESSIONESSENTIALS(Verbose, TEXT("Called"));

    const FUniqueNetIdPtr LocalUserNetId = GetLocalPlayerUniqueNetId(GetPlayerControllerByLocalUserNum(LocalUserNum));
    if (!LocalUserNetId.IsValid())
    {
    UE_LOG_SESSIONESSENTIALS(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_SESSIONESSENTIALS(Log, TEXT("succeeded: %s"), *FString(bSucceeded ? TEXT("TRUE") : TEXT("FALSE")))

    OnRejectSessionInviteCompleteDelegates.Broadcast(bSucceeded);
    }

Session participant changed callback

This callback will be called when other players join and leave the session.

  1. Open the SessionEssentialsOnlineSession_Starter Header file and add this declaration:

    protected:
    // ...
    virtual void OnSessionParticipantsChange(FName SessionName, const FUniqueNetId& Member, bool bJoined) override;
  2. Open the SessionEssentialsOnlineSession_Starter CPP file and add the code below. This function will call the corresponding delegate.

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

    OnSessionParticipantsChangeDelegates.Broadcast(SessionName, Member, bJoined);
    }
  3. Bind the callback to the corresponding OSS delegate. Navigate to RegisterOnlineDelegates and add the code below:

    void USessionEssentialsOnlineSession_Starter::RegisterOnlineDelegates()
    {
    // ...
    GetABSessionInt()->AddOnSessionParticipantsChangeDelegate_Handle(
    FOnSessionParticipantsChangeDelegate::CreateUObject(this, &ThisClass::OnSessionParticipantsChange));
    }
  4. Navigate to ClearOnlineDelegates and unbind the callback by adding the code below:

    void USessionEssentialsOnlineSession_Starter::ClearOnlineDelegates()
    {
    // ...
    GetABSessionInt()->ClearOnSessionParticipantsChangeDelegates(this);
    }

Compile your project again and make sure it has no compile errors.

Update session joinability

Sessions have a joinability property that dictates who can join the session. Refer to Introduction to Session to see the available joinability types.

  1. Open the SessionEssentialsOnlineSession_Starter Header file and add this declaration:

    public:
    // ...
    virtual void UpdateSessionJoinability(const FName SessionName, const EAccelByteV2SessionJoinability Joinability) override;
  2. Still in the SessionEssentialsOnlineSession_Starter Header file, add the declaration for the response callback.

    protected:
    // ...
    virtual void OnUpdateSessionComplete(FName SessionName, bool bSucceeded) override;
  3. Open the SessionEssentialsOnlineSession_Starter CPP file and add the implementation below for the UpdateSessionJoinability function. This function will call the corresponding OSS function.

    void USessionEssentialsOnlineSession_Starter::UpdateSessionJoinability(const FName SessionName, const EAccelByteV2SessionJoinability Joinability)
    {
    UE_LOG_SESSIONESSENTIALS(Verbose, TEXT("called"));

    FOnlineSessionV2AccelBytePtr ABSessionInt = GetABSessionInt();
    if (!ABSessionInt)
    {
    UE_LOG_SESSIONESSENTIALS(Warning, TEXT("Session interface is null"));
    return;
    }

    FNamedOnlineSession* Session = ABSessionInt->GetNamedSession(SessionName);
    if (!Session)
    {
    UE_LOG_SESSIONESSENTIALS(Warning, TEXT("The session is invalid"));
    return;
    }

    Session->SessionSettings.Set(SETTING_SESSION_JOIN_TYPE, UEnum::GetValueAsString(Joinability));
    ABSessionInt->UpdateSession(SessionName, Session->SessionSettings);
    }
  4. Still in the CPP file, add the code below for the OnUpdateSessionComplete function. This function will also call the corresponding delegate to let other objects know when the response was received.

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

    OnUpdateSessionCompleteDelegates.Broadcast(SessionName, bSucceeded);
    }
  5. Bind the callback to the corresponding OSS delegate. Navigate to RegisterOnlineDelegates and add the code below:

    void USessionEssentialsOnlineSession_Starter::RegisterOnlineDelegates()
    {
    // ...
    GetSessionInt()->AddOnUpdateSessionCompleteDelegate_Handle(
    FOnUpdateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnUpdateSessionComplete));
    // ...
    }
  6. Navigate to ClearOnlineDelegates and unbind the callback by adding the code below:

    void USessionEssentialsOnlineSession_Starter::ClearOnlineDelegates()
    {
    // ...
    GetSessionInt()->ClearOnUpdateSessionCompleteDelegates(this);
    // ...
    }

Compile your project again and make sure it has no compile errors.

Resources