Can I call timer handler before delay passed?

I set timer with FTimerManager like this.

FTimerManager& TimerManager = GetTimerManager();
TimerManager.SetTimer(Handler, Callback, 1.0f, false);

I have two questions.

  1. In some cases, I want to Callback function before 1 sec passed. Can I do with Handler?
  2. I want to call this timer only once. After Callback function called, I expects Handler might be Invalidate but it’s validate. Of course, FTimerManager::InternalClearTimer() can’t find FTimerData* by FindTimer() so it’s not big problem, but if I know it’s only for once, I don’t want to call FindTimer() in ClearTimer(). Can I tell Callback function is called or not with TimerHandle?

Hello.

Clean-ish solution to do that would require to modify the engine (FTimerManager class to be specific).You’d need to expose a function that either returns non-const pointer toFTimerData (and then call Execute on inner TimerDelegate) or does that for your without exposing such data - this would probably be safer solution.

As an ugly workaround without modifying the engine you’d have to duplicate some data from FTimerManager.
You’d have to have storage that would hold map timer handle → delegate, and custom functions for setting up/clearing/firing timers.

Storage would be something like this:

    struct FStorageEntry 
	{
		FStorageEntry(const FTimerHandle& InHandle, const FTimerDelegate& InDelegate)
			: Handle(InHandle)
			, Delegate(InDelegate)
		{}

		FTimerHandle Handle;
		FTimerDelegate Delegate;
	};

	// TMap<FTimerHandle, FTimerDelegate> in most cases would be more efficient here
	// but FTimerHandle cannot be used as key unfortunately.
	TArray<FStorageEntry> Storage;

And setting up/execution functions:

void UCustomTimerManager::SetupTimer(FTimerHandle& OutTimwer)
{
	if( GEngine )
	{
		GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Yellow, TEXT("Setting up timer"));
	}

	UWorld* World = GetWorld();
	if( !World ) return;

	FTimerDelegate Delegate;
	Delegate.BindUObject(this, &UCustomTimerManager::TimerFunction);

	World->GetTimerManager().SetTimer(OutTimwer, Delegate, 10.f, false);

	Storage.Add(FStorageEntry(OutTimwer, Delegate));
}

void UCustomTimerManager::ExecuteTimer(FTimerHandle& InOutTimerHandle)
{
	if( GEngine )
	{
		GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Yellow, TEXT("Executing timer"));
	}

	if( !InOutTimerHandle.IsValid() ) return;

	UWorld* World = GetWorld(); // you should make sure this manager can actually reference to world
	if( !World ) return;

	for( int32 It = 0; It < Storage.Num(); ++It )
	{
		if( Storage[It].Handle == InOutTimerHandle )
		{
			Storage[It].Delegate.Execute();

			Storage.RemoveAt(It);
			World->GetTimerManager().ClearTimer(InOutTimerHandle);

			break;
		}
	}
}

// Just for testing purposes
void UCustomTimerManager::TimerFunction()
{
	if( GEngine )
	{
		GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Yellow, TEXT("Timer function executed"));
	}
}

This is not a complete solution ofc, but you should get the idea from that. Tested and works.

Personally, I’d prefer to modify the engine as 2nd solution is kind of ugly imo.

Cheers.

Thanks for your idea. :slight_smile:

You’re welcome. :]