Download

Problem findind sessions with C++ script

Hello evryone,

I’m fairly new to UE4 and C++, and i’m encountering a problem right now on one of my project

I try to setup a very basic HOST/JOIN system for my project

I’m able to create a session and destroy it, however after when i run the game in multiplayer from the editor window, the find sessions function return 0 results

Here is the code of the function


// Fill out your copyright notice in the Description page of Project Settings.

#include "UNWGameInstance.h"
#include "EngineGlobals.h"
#include "Engine/Engine.h"
#include "Kismet/GameplayStatics.h"

UNWGameInstance::UNWGameInstance(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)

{
    /** Bind function for CREATING a Session */
    OnCreateSessionCompleteDelegate = FOnCreateSessionCompleteDelegate::CreateUObject(this, &UNWGameInstance::OnCreateSessionComplete);
    OnStartSessionCompleteDelegate = FOnStartSessionCompleteDelegate::CreateUObject(this, &UNWGameInstance::OnStartOnlineGameComplete);
    /** Bind function for FINDING a Session */
    OnFindSessionsCompleteDelegate = FOnFindSessionsCompleteDelegate::CreateUObject(this, &UNWGameInstance::OnFindSessionsComplete);
    /** Bind function for JOINING a Session */
    OnJoinSessionCompleteDelegate = FOnJoinSessionCompleteDelegate::CreateUObject(this, &UNWGameInstance::OnJoinSessionComplete);
    /** Bind function for DESTROYING a Session */
    OnDestroySessionCompleteDelegate = FOnDestroySessionCompleteDelegate::CreateUObject(this, &UNWGameInstance::OnDestroySessionComplete);
}

bool UNWGameInstance::HostSession(TSharedPtr<const FUniqueNetId> UserId, FName SessionName, bool bIsLAN, bool bIsPresence, int32 MaxNumPlayers)
{
    // Get the Online Subsystem to work with
    IOnlineSubsystem* const OnlineSub = IOnlineSubsystem::Get();
    GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, FString::Printf(TEXT("Called Host Session")));
    if (OnlineSub)
    {
        // Get the Session Interface, so we can call the "CreateSession" function on it
        IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();

        if (Sessions.IsValid() && UserId.IsValid())
        {
            /*
            Fill in all the Session Settings that we want to use.

            There are more with SessionSettings.Set(...);
            For example the Map or the GameMode/Type.
            */
            SessionSettings = MakeShareable(new FOnlineSessionSettings());

            SessionSettings->bIsLANMatch = bIsLAN;
            SessionSettings->bUsesPresence = bIsPresence;
            SessionSettings->NumPublicConnections = MaxNumPlayers;
            SessionSettings->NumPrivateConnections = 0;
            SessionSettings->bAllowInvites = true;
            SessionSettings->bAllowJoinInProgress = true;
            SessionSettings->bShouldAdvertise = true;
            SessionSettings->bAllowJoinViaPresence = true;
            SessionSettings->bAllowJoinViaPresenceFriendsOnly = false;

            SessionSettings->Set(SETTING_MAPNAME, FString("Red Hot"), EOnlineDataAdvertisementType::ViaOnlineService);

            // Set the delegate to the Handle of the SessionInterface
            OnCreateSessionCompleteDelegateHandle = Sessions->AddOnCreateSessionCompleteDelegate_Handle(OnCreateSessionCompleteDelegate);

            // Our delegate should get called when this is complete (doesn't need to be successful!)
            return Sessions->CreateSession(*UserId, SessionName, *SessionSettings);
        }
    }
    else
    {
        GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, TEXT("No OnlineSubsytem found!"));
    }

    return false;
}

void UNWGameInstance::OnCreateSessionComplete(FName SessionName, bool bWasSuccessful)
{
    GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, FString::Printf(TEXT("OnCreateSessionComplete %s, %d"), *SessionName.ToString(), bWasSuccessful));

    // Get the OnlineSubsystem so we can get the Session Interface
    IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();
    if (OnlineSub)
    {
        // Get the Session Interface to call the StartSession function
        IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();

        if (Sessions.IsValid())
        {
            // Clear the SessionComplete delegate handle, since we finished this call
            Sessions->ClearOnCreateSessionCompleteDelegate_Handle(OnCreateSessionCompleteDelegateHandle);
            if (bWasSuccessful)
            {
                // Set the StartSession delegate handle
                OnStartSessionCompleteDelegateHandle = Sessions->AddOnStartSessionCompleteDelegate_Handle(OnStartSessionCompleteDelegate);

                // Our StartSessionComplete delegate should get called after this
                Sessions->StartSession(SessionName);
            }
        }

    }
}

void UNWGameInstance::OnStartOnlineGameComplete(FName SessionName, bool bWasSuccessful)
{
    GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, FString::Printf(TEXT("OnStartSessionComplete %s, %d"), *SessionName.ToString(), bWasSuccessful));

    // Get the Online Subsystem so we can get the Session Interface
    IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();
    if (OnlineSub)
    {
        // Get the Session Interface to clear the Delegate
        IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
        if (Sessions.IsValid())
        {
            // Clear the delegate, since we are done with this call
            Sessions->ClearOnStartSessionCompleteDelegate_Handle(OnStartSessionCompleteDelegateHandle);
        }
    }

    // If the start was successful, we can open a NewMap if we want. Make sure to use "listen" as a parameter!
    if (bWasSuccessful)
    {
        UGameplayStatics::OpenLevel(GetWorld(), "StageLevel", true, "listen");
    }
}

void UNWGameInstance::FindSessions(TSharedPtr<const FUniqueNetId> UserId, bool bIsLAN, bool bIsPresence)
{
    UE_LOG(LogTemp, Log, TEXT("Finding sessions"));
    // Get the OnlineSubsystem we want to work with
    IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();

    if (OnlineSub)
    {
        // Get the SessionInterface from our OnlineSubsystem
        IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();

        if (Sessions.IsValid() && UserId.IsValid())
        {
            /*
            Fill in all the SearchSettings, like if we are searching for a LAN game and how many results we want to have!
            */
            SessionSearch = MakeShareable(new FOnlineSessionSearch());

            SessionSearch->bIsLanQuery = bIsLAN;
            SessionSearch->MaxSearchResults = 20;
            SessionSearch->PingBucketSize = 1000;

            // We only want to set this Query Setting if "bIsPresence" is true
            if (bIsPresence)
            {
                SessionSearch->QuerySettings.Set(SEARCH_PRESENCE, bIsPresence, EOnlineComparisonOp::Equals);
            }

            TSharedRef<FOnlineSessionSearch> SearchSettingsRef = SessionSearch.ToSharedRef();

            // Set the Delegate to the Delegate Handle of the FindSession function
            OnFindSessionsCompleteDelegateHandle = Sessions->AddOnFindSessionsCompleteDelegate_Handle(OnFindSessionsCompleteDelegate);

            // Finally call the SessionInterface function. The Delegate gets called once this is finished
            Sessions->FindSessions(*UserId, SearchSettingsRef);
        }
    }
    else
    {
        // If something goes wrong, just call the Delegate Function directly with "false".
        OnFindSessionsComplete(false);
    }
}

void UNWGameInstance::OnFindSessionsComplete(bool bWasSuccessful)
{
    GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, FString::Printf(TEXT("OFindSessionsComplete bSuccess: %d"), bWasSuccessful));
    GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Blue, FString::Printf(TEXT("OFindSessionsComplete bSuccess: %d"), bWasSuccessful));
    GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Green, FString::Printf(TEXT("OFindSessionsComplete bSuccess: %d"), bWasSuccessful));
    GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, FString::Printf(TEXT("Ping Bucket size: %d"), SessionSearch->PingBucketSize));

    // Get OnlineSubsystem we want to work with
    IOnlineSubsystem* const OnlineSub = IOnlineSubsystem::Get();
    if (OnlineSub)
    {
        // Get SessionInterface of the OnlineSubsystem
        IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
        if (Sessions.IsValid())
        {
            // Clear the Delegate handle, since we finished this call
            Sessions->ClearOnFindSessionsCompleteDelegate_Handle(OnFindSessionsCompleteDelegateHandle);

            // Just debugging the Number of Search results. Can be displayed in UMG or something later on
            GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, FString::Printf(TEXT("Num Search Results: %d"), SessionSearch->SearchResults.Num()));

            // If we have found at least 1 session, we just going to debug them. You could add them to a list of UMG Widgets, like it is done in the BP version!
            if (SessionSearch->SearchResults.Num() > 0)
            {
                // "SessionSearch->SearchResults" is an Array that contains all the information. You can access the Session in this and get a lot of information.
                // This can be customized later on with your own classes to add more information that can be set and displayed
                for (int32 SearchIdx = 0; SearchIdx < SessionSearch->SearchResults.Num(); SearchIdx++)
                {
                    // OwningUserName is just the SessionName for now. I guess you can create your own Host Settings class and GameSession Class and add a proper GameServer Name here.
                    // This is something you can't do in Blueprint for example!
                    GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, FString::Printf(TEXT("Session Number: %d | Sessionname: %s "), SearchIdx + 1, *(SessionSearch->SearchResults[SearchIdx].Session.OwningUserName)));
                }
            }
        }
    }
}

bool UNWGameInstance::JoinSession(TSharedPtr<const FUniqueNetId> UserId, FName SessionName, const FOnlineSessionSearchResult& SearchResult)
{
    // Return bool
    bool bSuccessful = false;

    // Get OnlineSubsystem we want to work with
    IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();

    if (OnlineSub)
    {
        // Get SessionInterface from the OnlineSubsystem
        IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();

        if (Sessions.IsValid() && UserId.IsValid())
        {
            // Set the Handle again
            OnJoinSessionCompleteDelegateHandle = Sessions->AddOnJoinSessionCompleteDelegate_Handle(OnJoinSessionCompleteDelegate);

            // Call the "JoinSession" Function with the passed "SearchResult". The "SessionSearch->SearchResults" can be used to get such a
            // "FOnlineSessionSearchResult" and pass it. Pretty straight forward!
            bSuccessful = Sessions->JoinSession(*UserId, SessionName, SearchResult);
        }
    }

    return bSuccessful;
}

void UNWGameInstance::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
    GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, FString::Printf(TEXT("OnJoinSessionComplete %s, %d"), *SessionName.ToString(), static_cast<int32>(Result)));

    // Get the OnlineSubsystem we want to work with
    IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();
    if (OnlineSub)
    {
        // Get SessionInterface from the OnlineSubsystem
        IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();

        if (Sessions.IsValid())
        {
            // Clear the Delegate again
            Sessions->ClearOnJoinSessionCompleteDelegate_Handle(OnJoinSessionCompleteDelegateHandle);

            // Get the first local PlayerController, so we can call "ClientTravel" to get to the Server Map
            // This is something the Blueprint Node "Join Session" does automatically!
            APlayerController * const PlayerController = GetFirstLocalPlayerController();

            // We need a FString to use ClientTravel and we can let the SessionInterface contruct such a
            // String for us by giving him the SessionName and an empty String. We want to do this, because
            // Every OnlineSubsystem uses different TravelURLs
            FString TravelURL;

            if (PlayerController && Sessions->GetResolvedConnectString(SessionName, TravelURL))
            {
                // Finally call the ClienTravel. If you want, you could print the TravelURL to see
                // how it really looks like
                PlayerController->ClientTravel(TravelURL, ETravelType::TRAVEL_Absolute);
            }
        }
    }
}

void UNWGameInstance::OnDestroySessionComplete(FName SessionName, bool bWasSuccessful)
{
    GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, FString::Printf(TEXT("OnDestroySessionComplete %s, %d"), *SessionName.ToString(), bWasSuccessful));

    // Get the OnlineSubsystem we want to work with
    IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();
    if (OnlineSub)
    {
        // Get the SessionInterface from the OnlineSubsystem
        IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();

        if (Sessions.IsValid())
        {
            // Clear the Delegate
            Sessions->ClearOnDestroySessionCompleteDelegate_Handle(OnDestroySessionCompleteDelegateHandle);

            // If it was successful, we just load another level (could be a MainMenu!)
            if (bWasSuccessful)
            {
                UGameplayStatics::OpenLevel(GetWorld(), "MenuLevel", true);
            }
        }
    }
}

void UNWGameInstance::StartOnlineGame()
{
    UE_LOG(LogTemp, Log, TEXT("Inside Start Online Game"));
    // Creating a local player where we can get the UserID from
    ULocalPlayer* const Player = GetFirstGamePlayer();
    GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, FString::Printf(TEXT("Called StartOnlineGame")));

    // Call our custom HostSession function. GameSessionName is a GameInstance variable
    HostSession(Player->GetPreferredUniqueNetId(), "RedHot", true, true, 4);
}

void UNWGameInstance::FindOnlineGames()
{
    ULocalPlayer* const Player = GetFirstGamePlayer();

    FindSessions(Player->GetPreferredUniqueNetId(), true, true);
}

void UNWGameInstance::JoinOnlineGame()
{
    ULocalPlayer* const Player = GetFirstGamePlayer();

    // Just a SearchResult where we can save the one we want to use, for the case we find more than one!
    FOnlineSessionSearchResult SearchResult;

    // If the Array is not empty, we can go through it
    if (SessionSearch->SearchResults.Num() > 0)
    {
        for (int32 i = 0; i < SessionSearch->SearchResults.Num(); i++)
        {
            // To avoid something crazy, we filter sessions from ourself
            if (SessionSearch->SearchResults*.Session.OwningUserId != Player->GetPreferredUniqueNetId())
            {
                SearchResult = SessionSearch->SearchResults*;

                // Once we found sounce a Session that is not ours, just join it. Instead of using a for loop, you could
                // use a widget where you click on and have a reference for the GameSession it represents which you can use
                // here
                JoinSession(Player->GetPreferredUniqueNetId(), "RedHot", SearchResult);
                break;
            }
        }
    }
    else {
        UE_LOG(LogTemp, Log, TEXT("No session to join"));
    }
}

void UNWGameInstance::DestroySessionAndLeaveGame()
{
    IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();
    if (OnlineSub)
    {
        IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();

        if (Sessions.IsValid())
        {
            Sessions->AddOnDestroySessionCompleteDelegate_Handle(OnDestroySessionCompleteDelegate);

            Sessions->DestroySession("RedHot");
        }
    }
}

I’m running the project with OnlineSubSystem NULL and creating a LAN session

Can someone help me on this one?

Thanks a lot

Hello evryone I found my mistake,

For anyone having an issue similar to mine,finding sessions does not work in editor mode, your have to launch multiple instances of the game by going to the project folder > right clicking on the .uproject file > Launch Game

Now you will be able to create a session in one of the instance and find it on the other

cheers