Multicast Delegate won't work when Broadcast is called from an Tick function

I have another post that I believe is related to this issue - so solving this will solve that problem.

I want to be able to call a multicast delegate in blueprints from c++ - specifically from Tick(). This works in PIE and runtime, but doesn’t work in the Editor - and I kinda need it to work there.

The Multicast delegate lives on an actor component who can tick in editor, and any actor can implement that function because it’s BlueprintAssignable.

I’ve tried calling broadcast on this delegate directly from the component - and I’ve also tried creating a subsystem and inheriting FTickableGameObject to have a global tick that calls broadcast on the component and they both only work in PIE.

Interestingly, if I call broadcast directly from a test function that is CallInEditor it does work as intended.

Kinda at a loss here, any help appreciated!

Sounds like you haven’t enabled the actor to tick in editor. To do that just override the virtual bool ShouldTickIfViewportsOnly() const to return true.

That is only needed for actors who want to implement editor tick. Components just need these conditions which I am already using. I put it on an actor that has can tick in editor with same results.

PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bTickEvenWhenPaused = true;
bTickInEditor = true;

So I have discovered the issue, but now I can figure out how to make these delegates CallInEditor by default. Hopefully someone at Epic can weigh in on this behavior?

When ScriptDelegates are executed they check for a bunch of different factors - but any of them can be true to allow execution. This is specific to actors, which explains difference in behavior between components and actors.

void AActor::ProcessEvent(UFunction* Function, void* Parameters)
{
	LLM_SCOPE(ELLMTag::EngineMisc);

#if !UE_BUILD_SHIPPING
	if (!ProcessEventDelegate.IsBound())
#endif
	{
		// Apply UObject::ProcessEvent's early outs before doing any other work
		// If the process event delegate is bound, we need to allow the process to play out
		if ((Function->FunctionFlags & FUNC_Native) == 0 && (Function->Script.Num() == 0))
		{
			return;
		}
	}

	#if WITH_EDITOR
	static const FName CallInEditorMeta(TEXT("CallInEditor"));
	const bool bAllowScriptExecution = GAllowActorScriptExecutionInEditor || Function->GetBoolMetaData(CallInEditorMeta);
	#else
	const bool bAllowScriptExecution = GAllowActorScriptExecutionInEditor;
	#endif
	UWorld* MyWorld = GetWorld();
	if( ((MyWorld && (MyWorld->AreActorsInitialized() || bAllowScriptExecution)) || HasAnyFlags(RF_ClassDefaultObject)) && !IsGarbageCollecting() )
	{
#if !UE_BUILD_SHIPPING
		if (!ProcessEventDelegate.IsBound() || !ProcessEventDelegate.Execute(this, Function, Parameters))
		{
			Super::ProcessEvent(Function, Parameters);
		}
#else
		Super::ProcessEvent(Function, Parameters);
#endif
	}
}

Specifically I need bAllowScriptExecution to be true for my dynamic multicast delegate, but it’s always false, even when the Delegate is marked with CallInEditor (I don’t think it actually works but it does compile).

How exactly do delegates decide if they are being executed by an bAllowScriptExecution=true scenario?

This will probably have unknown larger consequences for me later, but I was able to make this work by using this line from some other parts of the engine…

TGuardValue<bool> AutoRestore(GAllowActorScriptExecutionInEditor, true);

Ideally I would like to find a way to make the delegate itself have the CallInEditor meta tag but this also allows me to override the default behavior by doing setting GAllowActorScriptExecutionInEditor = true; However, this also means I’m disabling the script guard for other systems that may rely on this being false to prevent crashes if they are not checking for other conditions prior.

:man_shrugging:

Did you try to mark your function with UFUNCTION tag and use the CallInEditor specifier? It seems that’s exactly what you’d want.

1 Like

It’s not a function it’s a delegate. But yes, the function being called at all stages have been marked UFUNCTION(CallInEditor). The problem is and was still the script guard that wraps calling function on AActor.

Maybe it’s a bug, but even a delegate with a bound function being marked as callineditor still show up in the script guard as not containing that meta. So my solution was disabling the script guard momentarily to let the delegate fire.

Right, sorry, I was thinking about delegates but writing about functions. Delegates use UDELEGATE macro specifier is what I meant to say as you didn’t mention which specifier you tried.

Though it’s hard to imagine that would help as from what it looks like in the source, it checks for metadata key only on the bound function. Unless the compiler relies on both the Delegate and the bound function to use CallInEditor meta and just throws it out otherwise, but that would just be weird.

Just as i posted that i had a look at GetBoolMetaData, actually it seems like it would expect CallInEditor to be part of metadata specifier. So possibly you need to use this in UFUNCTION

meta = (CallInEditor = “true”)

I looked into this also - what ends up happening is AActor::Process ← cant remember the exact name. Looks for this meta on the Delegate. If I have this open in VS I can see the UFunction* is in fact the delegate -and it’s asking to see if the delegate has that meta, which for some reason is always false even when the delegate is marked as such. Basically what I’m saying is that function in AActor sees delegates and UFUNCTION marked functions as all being UFunction* at the end of the day.

Maybe it’s possible to not use the delegate macro and directly declare them to add the meta? I’m unsure.

I get what you mean now. Just looking at the source I’m not seeing anything obvious, unfortunately. If it doesn’t show up amongst the metadata keys might be worthwhile to debug the compiler and see why this metadata is being stripped. Good luck!

Thank you! :kissing_heart:

1 Like

:slight_smile: