Adding extra FieldNotify fields/functions cause bugs

When you modify which fields are notified as “changed” when a FieldNotify variable in a blueprint is changed, other blueprints that have “Set w/ Broadcast” nodes for the modified FieldNotify variable will not be updated and will not trigger Changed events.

For example, given two blueprints BP_HasVariable and BP_SetsVariable .

BP_HasVariable has a field notify variable MyFieldNotifyVariable and a field notify function MyFieldNotifyFunction. Originally, the MyFieldNotifyVariable is set to only broadcast that it was changed when it changes.

BP_SetsVariable has a function on it that calls “Set w/Broadcast” on BP_HasVariable with MyFieldNotifyVariable as the target.

When BP_SetsVariable is compiled, the list of fields that should be broadcast on BP_HasVariable is baked into the compiled BP_SetsVariable blueprint. That list is now only MyFieldNotifyVariable.

I now go to BP_HasVariable and change the FieldNotify settings for MyFieldNotifyVariable to also include MyFieldNotifyFunction. This updates the metadata of the field on BP_HasVariable but does not affect the invocation of “Set w/Broadcast” in BP_SetsVariable.

I now have a tricky bug in my game, because when the code in BP_SetsVariable is executed, it does not do what I expected it to do, it only triggers MyFieldNotifyVariable changed, not MyFieldNotifyFunction.

To fix this, I must track down all blueprints with “Set w/Broadcast” nodes that refer to my modified variable and recompile them. (This bug also applies to BroadcastFieldChanged nodes).

It is ultimately caused by the list of fieldnames to be broadcast being compiled into the invocation of the setter. Which can go out of date and the developer will be none the wiser.

The code that causes this lives in and around this function: FKismetCompilerUtilities::GenerateFieldNotificationSetNode

Steps to Reproduce
Attached is a 5.6.1 blueprint project. I’ve also attached a video demonstrating the bug.

To work around this locally, we updated the SFieldNotificationCheckList::OnVariableCheckBoxChanged code to mark all referencing blueprints as “changed”.

// Brute force mark all dependent blueprints modified if they have setter nodes for this member
 
// Use asset registry to find all referencing blueprints
TArray<FAssetIdentifier> Referencers;
const FAssetIdentifier Source(BlueprintObj->GetPackage()->GetFName(), NAME_None);
IAssetRegistry::Get()->GetReferencers(Source, Referencers);
 
TArray<UBlueprint*> OtherBlueprints;
for (const FAssetIdentifier& AssetIdentifier : Referencers)
{
	TArray<FAssetData> Assets;
	IAssetRegistry::Get()->GetAssetsByPackageName(AssetIdentifier.PackageName, Assets);
 
	for (const FAssetData& AssetData : Assets)
	{
		if (AssetData.GetClass() == UBlueprint::StaticClass())
		{
			OtherBlueprints.Add(Cast<UBlueprint>(AssetData.GetAsset()));
		}
	}
}
 
// Mark all the blueprints modified if they have broadcasting setter nodes that refer to a variable with this ones name. 
for (UBlueprint* OtherBlueprint : OtherBlueprints)
{
	TArray<UK2Node_VariableSet*> SetNodes;
	FBlueprintEditorUtils::GetAllNodesOfClass(OtherBlueprint, SetNodes);
	
	for (const UK2Node_VariableSet* SetNode : SetNodes)
	{
		if (SetNode->HasFieldNotificationBroadcast() && SetNode->VariableReference.GetMemberName() == FieldName)
		{
			FBlueprintEditorUtils::MarkBlueprintAsModified(OtherBlueprint);
			break;
		}
	}
}

Hi,

Good catch, and thanks for sharing your solution. Iterating over every node in referencing blueprints is a bit heavy-handed, but I’m not sure there’s any way around it and hopefully the impact won’t be too noticeable since it should only trigger when those notifies are changed. I’ve checked in your change at CL#46444032 if you want to grab that and avoid a merge conflict down the line.

Best,

Cody