I followed eXi’s How To Use Sessions In C++ Wiki Tutorial to implement the online session system in my game but it gives me this error, it compiles but I don’t know where is the error coming from
the LobbyGameMode includes the WaveGameInstance so the problem must be there
here is my WaveGameInstance code
.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "Engine/GameInstance.h"
#include "WaveGameInstance.generated.h"
/**
*
*/
USTRUCT(BlueprintType)
struct FSessionDisplayInfo
{
GENERATED_BODY()
//Always make USTRUCT variables into UPROPERTY()
// any non-UPROPERTY() struct vars are not replicated
// So to simplify your life for later debugging, always use UPROPERTY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = SessionDisplay)
int32 SessionIndex;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = SessionDisplay)
FText SessionName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = SessionDisplay)
int32 CurrentPlayers;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = SessionDisplay)
int32 MaxPlayers;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = SessionDisplay)
int32 Ping;
//Constructor
FSessionDisplayInfo()
{
//Always initialize your USTRUCT variables!
// exception is if you know the variable type has its own default constructor
SessionIndex = 0;
CurrentPlayers = 0;
MaxPlayers = 0;
Ping = 0;
}
};
UCLASS()
class WAVE_API UWaveGameInstance : public UGameInstance
{
GENERATED_BODY()
UWaveGameInstance(const FObjectInitializer& ObjectInitializer);
/**
* Function to host a game!
*
* @Param UserID User that started the request
* @Param SessionName Name of the Session
* @Param bIsLAN Is this is LAN Game?
* @Param bIsPresence "Is the Session to create a presence Session"
* @Param MaxNumPlayers Number of Maximum allowed players on this "Session" (Server)
*/
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;
/**
* Function fired when a session create request has completed
*
* @param SessionName the name of the session this callback is for
* @param bWasSuccessful true if the async action completed without error, false if there was an error
*/
virtual void OnCreateSessionComplete(FName SessionName, bool bWasSuccessful);
/**
* Function fired when a session start request has completed
*
* @param SessionName the name of the session this callback is for
* @param bWasSuccessful true if the async action completed without error, false if there was an error
*/
void OnStartOnlineGameComplete(FName SessionName, bool bWasSuccessful);
/**
* Find an online session
*
* @param UserId user that initiated the request
* @param SessionName name of session this search will generate
* @param bIsLAN are we searching LAN matches
* @param bIsPresence are we searching presence sessions
*/
virtual 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;
/**
* Delegate fired when a session search query has completed
*
* @param bWasSuccessful true if the async action completed without error, false if there was an error
*/
void OnFindSessionsComplete(bool bWasSuccessful);
/**
* Joins a session via a search result
*
* @param SessionName name of session
* @param SearchResult Session to join
*
* @return bool true if successful, false otherwise
*/
bool JoinSession(TSharedPtr<const FUniqueNetId> UserId, FName SessionName, const FOnlineSessionSearchResult& SearchResult);
/** Delegate for joining a session */
FOnJoinSessionCompleteDelegate OnJoinSessionCompleteDelegate;
/** Handle to registered delegate for joining a session */
FDelegateHandle OnJoinSessionCompleteDelegateHandle;
/**
* Delegate fired when a session join request has completed
*
* @param SessionName the name of the session this callback is for
* @param bWasSuccessful true if the async action completed without error, false if there was an error
*/
void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result);
/** Delegate for destroying a session */
FOnDestroySessionCompleteDelegate OnDestroySessionCompleteDelegate;
/** Handle to registered delegate for destroying a session */
FDelegateHandle OnDestroySessionCompleteDelegateHandle;
/**
* Delegate fired when a destroying an online session has completed
*
* @param SessionName the name of the session this callback is for
* @param bWasSuccessful true if the async action completed without error, false if there was an error
*/
virtual void OnDestroySessionComplete(FName SessionName, bool bWasSuccessful);
public:
UFUNCTION(BlueprintCallable, Category = "Network|Test")
void StartOnlineGame(bool bIsLanMatch, int32 MaxPlayers);
UFUNCTION(BlueprintCallable, Category = "Network|Test")
void FindOnlineGames(bool bIsLanMatch);
UFUNCTION(BlueprintCallable, Category = "Network|Test")
void JoinOnlineGame(int32 SessionIndex);
UFUNCTION(BlueprintCallable, Category = "Network|Test")
void DestroySessionAndLeaveGame();
UFUNCTION(BlueprintImplementableEvent, Category = "Network|Test")
void UpdateServerList();
UFUNCTION(BlueprintCallable, Category = "Network|Test")
FORCEINLINE TArray<FSessionDisplayInfo> GetSessionsDisplayInfo() { return SessionUMGDiplayInfo; }
UFUNCTION(BlueprintCallable, Category = "Network|Test")
void SetPlayerNickname(FString Nickname);
FORCEINLINE FString GetPlayerNickname() { return PlayerNickname; }
private:
TSharedPtr<class FOnlineSessionSettings> SessionSettings;
TSharedPtr<class FOnlineSessionSearch> SessionSearch;
UPROPERTY(EditDefaultsOnly)
FName MainMenuMapName;
UPROPERTY(EditDefaultsOnly)
FName LobbyMapName;
TArray<FSessionDisplayInfo> SessionUMGDiplayInfo;
FString PlayerNickname;
//UPROPERTY(EditDefaultsOnly)
//uint32 bIsLanMatch : 1;
};
.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "Wave.h"
#include "WaveGameInstance.h"
UWaveGameInstance::UWaveGameInstance(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
/** Bind function for CREATING a Session */
OnCreateSessionCompleteDelegate = FOnCreateSessionCompleteDelegate::CreateUObject(this, &UWaveGameInstance::OnCreateSessionComplete);
OnStartSessionCompleteDelegate = FOnStartSessionCompleteDelegate::CreateUObject(this, &UWaveGameInstance::OnStartOnlineGameComplete);
/** Bind function for FINDING a Session */
OnFindSessionsCompleteDelegate = FOnFindSessionsCompleteDelegate::CreateUObject(this, &UWaveGameInstance::OnFindSessionsComplete);
/** Bind function for JOINING a Session */
OnJoinSessionCompleteDelegate = FOnJoinSessionCompleteDelegate::CreateUObject(this, &UWaveGameInstance::OnJoinSessionComplete);
/** Bind function for DESTROYING a Session */
OnDestroySessionCompleteDelegate = FOnDestroySessionCompleteDelegate::CreateUObject(this, &UWaveGameInstance::OnDestroySessionComplete);
MainMenuMapName = "MainMenu";
LobbyMapName = "Lobby";
PlayerNickname = TEXT("Player");
//bIsLanMatch = true;
}
bool UWaveGameInstance::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);
//SessionSettings->Set(SETTING_MAPNAME, LobbyMapName, 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 UWaveGameInstance::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 UWaveGameInstance::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");
UGameplayStatics::OpenLevel(GetWorld(), LobbyMapName, true, "listen");
}
}
void UWaveGameInstance::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 UWaveGameInstance::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)));
FSessionDisplayInfo DisplayInfo;
//FText SessionDisplayName =
DisplayInfo.SessionIndex = SearchIdx;
DisplayInfo.SessionName = FText::FromString(SessionSearch->SearchResults[SearchIdx].Session.OwningUserName);
DisplayInfo.CurrentPlayers = SessionSearch->SearchResults[SearchIdx].Session.SessionSettings.NumPublicConnections - SessionSearch->SearchResults[SearchIdx].Session.NumOpenPublicConnections;
DisplayInfo.MaxPlayers = SessionSearch->SearchResults[SearchIdx].Session.SessionSettings.NumPublicConnections;
DisplayInfo.Ping = SessionSearch->SearchResults[SearchIdx].PingInMs;
SessionUMGDiplayInfo.Add(DisplayInfo);
}
}
}
}
UpdateServerList();
}
bool UWaveGameInstance::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 UWaveGameInstance::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 UWaveGameInstance::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(), MainMenuMapName, true);
}
}
}
}
void UWaveGameInstance::StartOnlineGame(bool bIsLanMatch, int32 MaxPlayers)
{
// 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, bIsLanMatch, true, MaxPlayers);
}
void UWaveGameInstance::FindOnlineGames(bool bIsLanMatch)
{
SessionUMGDiplayInfo.Empty();
ULocalPlayer* const Player = GetFirstGamePlayer();
FindSessions(Player->GetPreferredUniqueNetId(), GameSessionName, bIsLanMatch, true);
}
void UWaveGameInstance::JoinOnlineGame(int32 SessionIndex)
{
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[i].Session.OwningUserId != Player->GetPreferredUniqueNetId())
{
SearchResult = SessionSearch->SearchResults[i];
// 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(), GameSessionName, SearchResult);
break;
}
}*/
SearchResult = SessionSearch->SearchResults[SessionIndex];
JoinSession(Player->GetPreferredUniqueNetId(), GameSessionName, SearchResult);
}
}
void UWaveGameInstance::DestroySessionAndLeaveGame()
{
IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();
if (OnlineSub)
{
IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
if (Sessions.IsValid())
{
Sessions->AddOnDestroySessionCompleteDelegate_Handle(OnDestroySessionCompleteDelegate);
Sessions->DestroySession(GameSessionName);
}
}
}
void UWaveGameInstance::SetPlayerNickname(FString Nickname)
{
PlayerNickname = Nickname;
}