Block function execution for time then resume execution

Hi all, I’m currently working with the CommonConversation plugin, where I’ve created a Task which is designed to take a UDialogueWave and play the dialogue audio, and wait until the dialogue audio has completed playing before moving to the next node in the Conversation graph…

So for those unfamiliar with the CommonConversation Tasks yet, when the Task node is reached, it calls ExecuteTaskNode on the custom task, for which must return an FConversationTaskResult once it completes execution telling the graph whether it can continue advancement, abort advancement, etc.

What I need to do is block returning any result until the audio has completed playing, and then return an AdvanceConversation result… My current thought, is to create an FTimerDelegate to execute a function when the audio completes, but of course timed delegates are non-blocking… So I then looked into TFuture’s, could I call the timed delegate, set the value of the future inside the delegate function, and meanwhile in my ExecuteTaskNode call a TFuture::Wait() to block execution until the value of the future is set by the delegate, like so…

// Snippet from ExecuteTaskNode
FTimerDelegate Delegate;
Delegate.BindUFunction(this, FName("OnDialogueCompleted"));

if (AudioComponent->Sound->GetDuration() > 0.f)
{
	FTimerHandle TimerHandle;
	GetWorld()->GetTimerManager().SetTimer(TimerHandle, Delegate, AudioComponent->Sound->GetDuration(), false);
	FinalizeDialogue.Wait();
}

if (FinalizeDialogue.IsReady())
{
	return FinalizeDialogue.Get();
}
void UPlayDialogueTask::OnDialogueCompleted()
{
	FinalizeDialogue = FConversationTaskResult::AdvanceConversation();
}

But as it would turn out, you can’t just set TFuture’s values manually, seems like it has to be set by Async()… I wonder if then I could just use TFuture::WaitFor() and just use the duration provided by the AudioComponent to block execution for that long, and then continue execution… e.g.

if (AudioComponent->Sound->GetDuration() > 0.f)
{
	// This should always return false
	if (FinalizeDialogue.WaitFor(AudioComponent->Sound->GetDuration()))
	{
		return FinalizeDialogue.Get();
	}
	return FConversationTaskResult::AdvanceConversation();
}

Any thoughts / suggestions out there?

Perhaps you could piggy back off the LatentActionManager to make it a latent call (not sure if this is supported in the commonConversation plugin)

here is how the delay node carries it out


void UKismetSystemLibrary::Delay(const UObject* WorldContextObject, float Duration, FLatentActionInfo LatentInfo )
{
	if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
	{
		FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
		if (LatentActionManager.FindExistingAction<FDelayAction>(LatentInfo.CallbackTarget, LatentInfo.UUID) == NULL)
		{
			LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FDelayAction(Duration, LatentInfo));
		}
	}
}

In theory you could have async tasks assigned through the LatentActionManager with the correct callback

seems to need at least
#include “LatentActions.h”

Hm so use LatentActionManager to call my async function within the callback function, while the ExecuteTaskNode performs a TFuture::Wait() until the LatentActionManager resolves the future?

Alternatively now that you’ve given me the idea of using the callback function to call the async function (if that’s what you meant), I could return to just using the TimerManager to call a function which calls the async to set the future while the Execute function waits… I’ll have to do a bit of testing