I’m trying to use a UBlueprintAsyncActionBase class to destroy an EOS/EOSPlus session. It does destroy the session but my bound function OnSessionDestroyed is never called. I’m creating the session in the same way and it works as expected. Am I doing something wrong or is this a bug? My code:
DestroyAsyncSession.h
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "DestroyAsyncSession.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDestroySessionDelegate); // Delegate declaration for destroying a session
UCLASS()
class UDestroyAsyncSession : public UBlueprintAsyncActionBase
{
GENERATED_UCLASS_BODY()
public:
UPROPERTY(BlueprintAssignable)
FDestroySessionDelegate OnSuccess; // Delegate for successfuly destroying a session
UPROPERTY(BlueprintAssignable)
FDestroySessionDelegate OnFailure; // Delegate for failure to destroy a session
virtual void Activate() override; // Called to trigger the action once the delegates have been bound
UFUNCTION(BlueprintCallable, Category = "Async Sessions", meta = (BlueprintInternalUseOnly = "true", ToolTip = "Destroys a game session."))
static UDestroyAsyncSession* DestroySessionAsync(FName SessionName); // Creates the async task for destroying a session
private:
FName sessionName; // The name of the session
class IOnlineSubsystem* OnlineSubsystem; // Reference to the online subsytem
void OnSessionDestroyed(FName SessionName, bool bWasSuccessful); // Bound to OnDestroySessionCompleteDelegates & called when the session destruction has completed
};
DestroyAsyncSession.cpp
#include "DestroyAsyncSession.h"
#include "Interfaces/OnlineSessionInterface.h"
#include "Interfaces/OnlineIdentityInterface.h"
#include "OnlineSubsystem.h"
#include "OnlineSessionSettings.h"
// Constructor with default values for creating async sessions
UDestroyAsyncSession::UDestroyAsyncSession(const FObjectInitializer& ObjectInitializer) :Super(ObjectInitializer), sessionName(FName("NULL_SESSION"))
{
OnlineSubsystem = IOnlineSubsystem::Get(); // Get a reference to the online subsystem
}
// Called to trigger the action once the delegates have been bound
void UDestroyAsyncSession::Activate()
{
if (OnlineSubsystem) // If we have a reference to the online subsystem
{
if (IOnlineIdentityPtr OnlineIdentity = OnlineSubsystem->GetIdentityInterface()) // Try to get a reference to the identity interface
{
if (OnlineIdentity->GetLoginStatus(0) == ELoginStatus::LoggedIn) // If the player is logged in
{
if (IOnlineSessionPtr Session = OnlineSubsystem->GetSessionInterface()) // Try to get a reference to the sessions interface
{
Session->OnDestroySessionCompleteDelegates.AddUObject(this, &UDestroyAsyncSession::OnSessionDestroyed); // Bind the destroy session delegate to out OnSessionDestroyed function
Session->DestroySession(sessionName); // Destroy the session
}
}
}
}
}
// Creates the async task for destroying a session
UDestroyAsyncSession* UDestroyAsyncSession::DestroySessionAsync(FName SessionName)
{
UDestroyAsyncSession* BPNode = NewObject<UDestroyAsyncSession>(); // Create an async node
BPNode->sessionName = SessionName; // Set the session name
return BPNode; // Return the async node
}
// Bound to OnDestroySessionCompleteDelegates & called when the session destruction has completed
void UDestroyAsyncSession::OnSessionDestroyed(FName SessionName, bool bWasSuccessful)
{
if (bWasSuccessful) // If the session was destroyed
{
UE_LOG(LogTemp, Warning, TEXT("Session Destroyed! %s"), *SessionName.ToString());
OnSuccess.Broadcast(); // Call on success execution pin
}
else // If the session destruction failed
{
UE_LOG(LogTemp, Error, TEXT("Failed to destroy Session!"));
OnFailure.Broadcast(); // Call on failure execution pin
}
if (OnlineSubsystem)
{
if (IOnlineSessionPtr Session = OnlineSubsystem->GetSessionInterface())
{
Session->ClearOnDestroySessionCompleteDelegates(this);
}
}
}
After further testing I’ve found that this only occurs when I’m logged into Epic Games/Steam to test. When I’m logged out the above code works as expected.
I’m a bit unsure if this is it, but you may need to call UBlueprintAsyncActionBase::RegisterWithGameInstance on the action instance in UDestroyAsyncSession::DestroySessionAsync to prevent garbage collection of the action object.
I’ve only used the old OnlineSubsystem where (I think) fake sessions are created synchronously. If the new EOS does the same it could explain why it works offline but not when logged in.
Thanks for the suggestion. I updated my code to this:
UDestroyAsyncSession* UDestroyAsyncSession::DestroySessionAsync(USvsGameInstance* GameInstance)
{
UDestroyAsyncSession* BPNode = NewObject<UDestroyAsyncSession>(); // Create an async node
BPNode->gameInstance = GameInstance; // Set the reference to the game instance
BPNode->sessionName = GameInstance->CurrentSessionName; // Set the session name
BPNode->RegisterWithGameInstance(GameInstance); // Register with the game instance
return BPNode; // Return the async node
}
Hello !
I am just a fellow struggling dev but I think I know why this callback is not triggered. The reason it does not work with steam is due to the oss EOSPlus not binding some callbacks from the OnlineSessionInterface to the oss EOS triggers.
It happens in OnlineSessionEOSPlus.cpp L24. The following callbacks are properly handled, but OnDestroySessionComplete is missing. I’d suggest to either add it here yourself, or to try to use the methods already available:
IOnlineSessionPtr PrimaryInterface = BaseSessionInterface;
if (bUseEOSSessions)
{
PrimaryInterface = EOSSessionInterface;
EOSSessionInterface->AddOnSessionUserInviteAcceptedDelegate_Handle(FOnSessionUserInviteAcceptedDelegate::CreateRaw(this, &FOnlineSessionEOSPlus::OnSessionUserInviteAcceptedEOS));
EOSSessionInterface->AddOnSessionInviteReceivedDelegate_Handle(FOnSessionInviteReceivedDelegate::CreateRaw(this, &FOnlineSessionEOSPlus::OnSessionInviteReceivedEOS));
}
// All of these depend upon which is our primary session interface
PrimaryInterface->AddOnSessionFailureDelegate_Handle(FOnSessionFailureDelegate::CreateRaw(this, &FOnlineSessionEOSPlus::OnSessionFailure));
PrimaryInterface->AddOnStartSessionCompleteDelegate_Handle(FOnUpdateSessionCompleteDelegate::CreateRaw(this, &FOnlineSessionEOSPlus::OnStartSessionComplete));
PrimaryInterface->AddOnUpdateSessionCompleteDelegate_Handle(FOnUpdateSessionCompleteDelegate::CreateRaw(this, &FOnlineSessionEOSPlus::OnUpdateSessionComplete));
PrimaryInterface->AddOnEndSessionCompleteDelegate_Handle(FOnEndSessionCompleteDelegate::CreateRaw(this, &FOnlineSessionEOSPlus::OnEndSessionComplete));
PrimaryInterface->AddOnFindSessionsCompleteDelegate_Handle(FOnFindSessionsCompleteDelegate::CreateRaw(this, &FOnlineSessionEOSPlus::OnFindSessionsComplete));
PrimaryInterface->AddOnCancelFindSessionsCompleteDelegate_Handle(FOnCancelFindSessionsCompleteDelegate::CreateRaw(this, &FOnlineSessionEOSPlus::OnCancelFindSessionsComplete));
PrimaryInterface->AddOnPingSearchResultsCompleteDelegate_Handle(FOnPingSearchResultsCompleteDelegate::CreateRaw(this, &FOnlineSessionEOSPlus::OnPingSearchResultsComplete));
PrimaryInterface->AddOnJoinSessionCompleteDelegate_Handle(FOnJoinSessionCompleteDelegate::CreateRaw(this, &FOnlineSessionEOSPlus::OnJoinSessionComplete));
Hey,
Sorry to hear it does not work. The last option I can think of would be to pass your callback directly as an argument of DestroySession(), that’s how I managed to do it.
Similar issue in UE 5.4. All other delegates work, just not the OnDestroySessionComplete Delegate. Passing a FOnDestroySessionCompleteDelegate worked for the Steam OSS using EOS Plus. The standard method of binding the delegate ironically works with the NULL OSS though. Not sure about EOS with EOS Plus, will have to check in the future.