How to Find non native Functions and Event Dispatchers (BP Delegates) from C++?

Intro
So I thought I’d try a little experiment and make an Actor Component that could expose it’s BP defined Event Dispatchers to streamline “level scripting”. The idea being when an actor instance is dropped in the world (in editor), the user could click on this new Actor Component on this instanced actor, and assign functions to call from other actors for any given parent Actor’s Event Dispatchers. That was a mouthful, here is the workflow that I have in mind:

PROBLEM:
Imagine a level designer wants to hook up a light switch to a light bulb and have that light bulb turn on and off. Cool, I’ll use an interface and generalize the On and Off states on the light. But because I learn later that they want to be able to have that same switch in different circumstances do different things like

  • When the switch is put into the off position it does the opposite for a particular light
  • A particular switch closes a door, and turns on a light
  • Another particular switch opens a door and turns on a light, and locks some other door

I also want designers on the team to be able to make new BPs without having to make a code class so they may prototype more quickly.

WORKFLOW:
Well what if we had something like this,

THE BELOW IS PLACEHOLDER, NON FUNCTIONAL

I would imagine that some of these fields would be auto populated. Things like the Event Dispatcher names could be auto populated and even once you reference a light or a door it could maybe have a drop down list of potential function you want to call when a particular event dispatcher is triggered.

CODE ATTEMPT

So I managed to make this somewhat work for functions. I learned that I could go through the functions available from a BP class in C++ and call any non native functions. Here is my (very incomplete) attempt at searching for/autopopulating a list of functions on any given actor.

void USomeComponent::ParentPostEditChangeProperty(UObject* ParentObject)
{
	if (!IsValid(ParentObject))
	{
		return;
	}

	for (TFieldIterator<UFunction> func(ParentObject->GetClass()); func; ++func) {
				
		if (func->GetName().Contains("ScriptAssist"))
		{
			UE_LOG(LogTemp, Warning, TEXT("Function found - %s"), *func->GetName());
			
			//Use below when you want to call a function
			//ParentObject->ProcessEvent(*func, nullptr);			
		}
	}
}

Great I can find the names of the functions, and even apply a filter for functions that contain the words “ScriptAssist” in their name, and using the commented code, call them! But I’m really stuck with how to make this work for “Event Dispatchers” that are defined non natively.

Conclusion
Does anyone have any thoughts on if my approach here is a waste of time, or am I onto an interesting workflow paradigm. Or should I just take a different route that involves gameplaytags for actions that can occur or using an Interface with generic functions like: Activate, Deactivate, lock, unlock etc.

I’m not sure what you’re doing that’s finding the native EventDispatchers (because UFunction doesn’t seem like the right match), but it’s very similar to what you’re already doing except the template type you need is FMulticastDelegateProperty. So TFieldIterator< FMulticastDelegateProperty > should get you all the event dispatchers, from Blueprint or Native.

It’s not all that different from the dispatcher bindings that UMG uses when you click that little green plus symbol and they make & bind a custom event for you.
My biggest suggestion (although this does complicate things) would be to base the options on signature and not naming schemes. That way you can’t connect incompatible functions up and hopefully you can detect errors if the signature changes on either end.

While I didn’t do what you’re doing exactly, I did create a custom blueprint node for binding functions to event dispatchers. You see more details about that here.

Oh hey that worked!!!:

I thought I tried that previously and failed.

I was actually surprised that this is what gets printed out when using TFfieldIterator<UFunction>

LogTemp: Warning: Function found - SomethinelseScriptAssist
LogTemp: Warning: Function found - EventScriptAssist__DelegateSignature

Notice the DelegateSignature at the end of the second line, it comes from an “Event Dispatcher” defined in Blueprint.

So now while your first suggestion found me the event dispatchers, I have not been able to successfully call or find a way to force a broadcast.

Here’s what I landed on:

for (TFieldIterator<FMulticastDelegateProperty> func(Parent->GetClass()); func; ++func) 
{
	if (func->GetName().Contains("ScriptAssist"))
	{
		const FMulticastScriptDelegate* temp = func->GetMulticastDelegate(Parent);

		if (temp->IsBound())
		{
			temp->ProcessMulticastDelegate<UObject>(nullptr);
			UE_LOG(LogTemp, Warning, TEXT("We are bound and trying to broadcast %s"), *func->GetName());
			return;
		}
        }
}

but I get errors when running the above code:
0x000001be1a584981 UnrealEditor-SomeGame.dll!FMRSWRecursiveAccessDetector::GetCurrentThreadCallstack() [D:\Engine\Source\Runtime\Core\Public\Misc\MTAccessDetector.h:368]

I think this is a dead end for me :frowning: . Thank you for the quick reply on this, I’ve been spending a couple days on this and just doesn’t look like I’m gonna be able to pull it through :frowning:

I really liked the above solution you have! Cleans up the amount of nodes needed. I may yoink that concept :stuck_out_tongue:

I don’t think that passing nullptr to ProcessMulticastDelegate is valid, even if your delegate doesn’t have any parameters. Manually calling functions or triggering delegates this way is an advanced workflow that is usually more complicated than it’s worth.

That particular node is available in my Starfire Utilities plugin available on github. You can use the whole plugin, extract the K2Node_BindDelegate & SGraphNodeK2BindDelegate source or just use it for reference.

Alright so incase anyone else stumbles upon this posting. I want to point out a couple things.

Now with that said, the above is focused on a programmer authored solution. Which wasn’t my personal goal, mine was more oriented towards allowing content creators OR programmers to setup their OWN delegates/event dispatchers and removing the “middle man” scripting between actors and objects in the world.

What I landed on was something I’m happy with. I can’t post the solution but what I will say is the following things were useful:

The following code lets you parse through functions by comparing it with a provided UFunction

TArray<FString> SomeComponent::FindFunctionNamesWithFunctionSignature(AActor* ObjectToLookAt, UFunction* FunctionForSignature)
{

for (TFieldIterator<UFunction> func(ObjectToLookAt->GetClass()); func; ++func) {
	
	if (!func->GetName().Contains(NamingConvention, ESearchCase::IgnoreCase))
	{
		continue;			
	}

	if (!IsValid(FunctionForSignature))
	{
		continue;
	}

//Make sure to add other code here for verification and error checking
	
	ArrayToReturn.Add(func->GetName());
}

The following code can get you all of the Event dispatchers or Delegates from a provided class

void SomeComponent::SetEventDispatchersBasedOnParent()
{
	AActor* Parent = GetOwner();

	if (!IsValid(Parent))
	{
		return;
	}

	for (TFieldIterator<FMulticastDelegateProperty> func(Parent->GetClass()); func; ++func) {

		if (!func->GetName().Contains(NamingConvention, ESearchCase::IgnoreCase))
		{
			continue;
		}

		EventDispatcherToFunctionMap.FindOrAdd(*func->GetName());
	}
}

Now that you have a list of functions from some object, and a list of event dispatchers/ delegates from another, you can tie them together by doing something like this (This is pseudocode):

//To get a FMulticastScriptDelegate

for (TFieldIterator<FMulticastDelegateProperty> CurrentDelegateProperty(Parent->GetClass()); CurrentDelegateProperty; ++CurrentDelegateProperty) {

//This line grabs the actual value of a delegate property.
FMulticastScriptDelegate* CurrentScriptDelegate = CurrentDelegateProperty->ContainerPtrToValuePtr<FMulticastScriptDelegate>(Parent);

//This line grabs a function by name
UFunction* CurrentFunction = CurrentActorKey->FindFunction(*FunctionsToCallPair.Key);

//Once the above is done youll want to do some comparisons and make sure the two are compatible otherwise you could crash your game

//finally bind the two together

DelegateToAddToParentActor.BindUFunction(CurrentActor, FName(FunctionsToCallPair.Key));
CurrentScriptDelegate->Add(DelegateToAddToParentActor);

}

Anyway sorry about the vagueness, but those are all functions that essentially already available through Unreal and I thought I just share my findings and what was useful for me.

One last note, this line is very helpful when trying to compare and validate UFunctions that have the same signature (more pseudocode):

//Collect Properties from UFunctions
TArray<FProperty*> DelegateParams;
for (TFieldIterator<FProperty> It(DelegateFunction); It; ++It)
{
	if ((It->PropertyFlags & CPF_Parm) && !(It->PropertyFlags & CPF_OutParm))
	{
		DelegateParams.Add(*It);
	}
}

TArray<FProperty*> FunctionParams;
for (TFieldIterator<FProperty> It(FirstFunction); It; ++It)
{
	if (It->PropertyFlags & CPF_OutParm)
	{
            return false;
        }

	if ((It->PropertyFlags & CPF_Parm) && !(It->PropertyFlags & CPF_OutParm))
	{
		FunctionParams.Add(*It);
	}
}

//Make sure your parameters are matching between functions and delegates

FStructUtils::ArePropertiesTheSame(CurrentParam, CurrentDelegateParam, false)

Anyway, those are my findings and the pieces that need to be put together, apologies if someone out there wanted actual code. I cannot provide that but all of those functions above would have helped me get to a solution :slight_smile: