EOS/EOSPlus OnDestroySessionCompleteDelegates Not Calling Bound Function

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’ve created a further post over on EOS Help

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
}

Sadly nothing seems to have changed.

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));

Good luck o/

Hi!
Thanks for pointing this out, it does seem to be the source of the problem. I’ve tried adding the handle & callback myself but with no success.

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.

I have the following attribute in my .h file

FOnDestroySessionCompleteDelegate SessionDestroyComplete;

Then I bind my method to it in an initialization method (called once)

SessionDestroyComplete.BindUObject(this, &UDestroyAsyncSession::OnSessionDestroyed);

And finally, when calling the DestroySession method :

Session->DestroySession(sessionName, SessionDestroyComplete);
1 Like

Hey @FlorianZeni,
This worked! Thanks you so much for your help!

1 Like