I’ve been using [eXi’s sessions guide][1] to try getting multiplayer working. I’ve rigged his code up to a number of Blueprint nodes that I’m using in UMG based off of the Multiplayer Shootout’s session widgets.
I’m pretty sure I can host games alright, but whenever I try to find the games and use resulting SearchResults array to generate a bunch of SessionRow widgets, nothing happens. The messages in the delegate function shows me that any sessions I’ve created have been found and recognized. However, none of my widgets are spawned. I’ve made sure I can spawn these widgets, so it’s not a problem there.
Screenies/snippets below:
Host and Find session code from gameinstance.h:
bool HostSession(TSharedPtr<const FUniqueNetId> UserId, FName SessionName, bool bIsLAN, bool bIsPresence, int32 MaxNumPlayers);
/* Delegate called when session created */
FOnCreateSessionCompleteDelegate OnCreateSessionCompleteDelegate;
/* Delegate called when session started */
FOnStartSessionCompleteDelegate OnStartSessionCompleteDelegate;
/** Handles to registered delegates for creating/starting a session */
FDelegateHandle OnCreateSessionCompleteDelegateHandle;
FDelegateHandle OnStartSessionCompleteDelegateHandle;
TSharedPtr<class FOnlineSessionSettings> SessionSettings;
virtual void OnCreateSessionComplete(FName SessionName, bool bWasSuccessful);
void OnStartOnlineGameComplete(FName SessionName, bool bWasSuccessful);
void FindSessions(TSharedPtr<const FUniqueNetId> UserId, FName SessionName, bool bIsLAN, bool bIsPresence);
/** Delegate for searching for sessions */
FOnFindSessionsCompleteDelegate OnFindSessionsCompleteDelegate;
/** Handle to registered delegate for searching a session */
FDelegateHandle OnFindSessionsCompleteDelegateHandle;
TSharedPtr<class FOnlineSessionSearch> SessionSearch;
void OnFindSessionsComplete(bool bWasSuccessful);
*snip*
UFUNCTION(BlueprintCallable, Category = "Network|Test")
void StartOnlineGame(FName sessionName, bool isLan, int32 maxPlayerNum);
UFUNCTION(BlueprintCallable, Category = "Network|Test")
TArray<FSessionSearchWrapperStruct> FindOnlineGames(bool isLan);
and the .cpp:
bool UBaseGameInstance_Network::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();
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("NewMap"), 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 UBaseGameInstance_Network::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 UBaseGameInstance_Network::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(), "NewMap", true, "listen");
}
}
void UBaseGameInstance_Network::FindSessions(TSharedPtr<const FUniqueNetId> UserId, FName SessionName, bool bIsLAN, bool bIsPresence)
{
// 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 = 50;
// 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 UBaseGameInstance_Network::OnFindSessionsComplete(bool bWasSuccessful)
{
GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, FString::Printf(TEXT("OFindSessionsComplete bSuccess: %d"), bWasSuccessful));
// 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)));
}}}}}
*snip*
void UBaseGameInstance_Network::StartOnlineGame(FName sessionName, bool isLan, int32 maxPlayerNum)
{
// Creating a local player where we can get the UserID from
ULocalPlayer* const Player = GetFirstGamePlayer();
// Call our custom HostSession function. GameSessionName is a GameInstance variable
HostSession(Player->GetPreferredUniqueNetId(), GameSessionName, isLan, true, maxPlayerNum);
}
TArray<FSessionSearchWrapperStruct> UBaseGameInstance_Network::FindOnlineGames(bool isLan)
{
ULocalPlayer* const Player = GetFirstGamePlayer();
TArray<FSessionSearchWrapperStruct> results;
FindSessions(Player->GetPreferredUniqueNetId(), GameSessionName, isLan, true);
for (int i = 0; i < SessionSearch->SearchResults.Num(); i++)
{
FSessionSearchWrapperStruct tempStruct;
tempStruct.searchResult = SessionSearch->SearchResults[i];
results.Add(tempStruct);
}
return results;
}
and screenshots of the place where I’m calling the code:
One thing I’ve noticed is that whenever I call this macro, it finishes and then, about five seconds after I get the message that it’s finished, I get the message from the FindSessions delegate listing all the sessions that have been found. I’m not sure if that affects anything since nothing really happens in the delegate function, but that might be some kind of problem.
EDIT: Here’s the FSessionSearchWrapper code. It’s a pretty simple struct but figured I should post it just in case it’s causing problems.
SessionSearchWrapper.h:
#define LOCTEXT_NAMESPACE "sessionsearchwrapper"
#pragma once
#include "Object.h"
#include "SessionSearchWrapper.generated.h"
/**
*
*/
USTRUCT(BlueprintType)
struct MEGARISK_API FSessionSearchWrapperStruct
{
GENERATED_USTRUCT_BODY()
//UPROPERTY(BlueprintReadWrite, Category = "sessionstuff")
FOnlineSessionSearchResult searchResult;
UPROPERTY(BlueprintReadWrite, Category = "sessionstuff")
FName ServerName;
};
#undef LOCTEXT_NAMESPACE
The .cpp is empty.