Description

When duplicating a blueprint interface, the GraphGuids of the original BP interface's function graphs are unchanged: function graphs across the two BP interfaces now have the same GraphGuid. This is considered working as intended according to comments under [Link Removed]: GraphGuid uniqueness is only guaranteed within the same blueprint.

However, when a blueprint is compiled,

void ConformInterfaceByGUID() 

is called whose purpose is to ensure that all interface functions are implemented by the blueprint. The code there assumes that a matching GraphGuid == InterfaceGuid alone is sufficient to conclude that the blueprint's function graph implements a specific interface's function. When a blueprint implements two interfaces that have graphs with the same Guid, this section falsely concludes a blueprint graph implements this interface function, rather than another interface's function with the same GraphGuid. It then concludes the blueprint function graph's name is outdated and renames the graph:

if(InterfaceGraph != nullptr && InterfaceGraph->GraphGuid == BlueprintGraph->InterfaceGuid && InterfaceGraph->GetFName() != BlueprintGraph->GetFName())
{
                FBlueprintEditorUtils::RenameGraph(BlueprintGraph, InterfaceGraph->GetFName().ToString());
                FBlueprintEditorUtils::RefreshGraphNodes(BlueprintGraph);
                break;
} 

It also triggers a refresh of all graph nodes.

Side effect 1: non-fatal

If any of those nodes triggers another call to ConformInterfaceByGUID, then this results in a ConformInterfaceByGUID loop: due to the two interfaces fighting over function graphs the ConformInterfaceByGUID will keep making changes, triggering a graph refresh, triggering ConformInterfaceByGUID, and so on. Because some of this behavior is deferred to the next frame (actually, widget paint), this results in non-fatal broken behavior.

Side effect 2: fatal

When a blueprint implements both interfaces and you rename one of the interface functions without compiling it, then compiling the blueprint leads to ConformInterfaceByGUID being recursively called until stack overflow.

Steps to Reproduce

Shared repro steps:

  • Create a blueprint interface BPI_Interface1
  • Create a function MyFunction in that interface that returns a Boolean (force implementation to be a graph instead of event)
  • Duplicate BPI_Interface1, naming it BPI_Interface2
  • In the duplicated asset, rename MyFunction to MyRenamedFunction
  • Create an actor blueprint BP_MyActor
  • Under class settings, add BPI_Interface1 to Implemented Interfaces
  • Continue to repro steps A or B

Repro steps continued (A) - non-fatal:

  • In the implementation of MyFunction, create a reroute node that reroutes a variable to an (editable when not connected) input pin. For example:
    • Drag a connection off of the Boolean return value and promote it to a variable
    • Double click the connection to create a reroute node
  • Under class settings, add BPI_Interface2 to Implemented Interfaces
  • Observe: Graphs fail to render. The asset is dirtied every frame. Saving the asset results in the asset becoming dirty immediately.
  • Expected: Blueprint editor should keep working as usual. The asset should not become dirtied immediately after save.

Repro steps continued (B) - fatal:

  • Add BPI_Interface1 and BPI_Interface2 to BP_MyActor's implemented interfaces immediately.
  • Switch to BPI_Interface1. Rename MyFunction in BPI_Interface1 without recompiling the interface.
  • Switch back to BP_MyActor's blueprint editor and either:
    • Compile it to trigger a ConformInterfaceByGUID() call
    • or no steps needed if it's already in a ConformInterfaceByGUID() loop from steps A
  • Observe: Stack overflow from recursion (see callstack)
Callstack

Non-fatal (repro steps A):
> UnrealEditor-Kismet.dll!SMyBlueprint::OnObjectPropertyChanged(UObject * InObject, FPropertyChangedEvent & InPropertyChangedEvent) Line 3119 C++
  [Inline Frame] UnrealEditor-Kismet.dll!Invoke(void(SMyBlueprint::*)(UObject *, FPropertyChangedEvent &)) Line 66 C++
  [Inline Frame] UnrealEditor-Kismet.dll!UE::Core::Private::Tuple::TTupleBase<TIntegerSequence<unsigned int>>::ApplyAfter(void(SMyBlueprint::*)(UObject *, FPropertyChangedEvent &) &) Line 309 C++
  UnrealEditor-Kismet.dll!TBaseRawMethodDelegateInstance<0,SMyBlueprint,void __cdecl(UObject *,FPropertyChangedEvent &),FDefaultDelegateUserPolicy>::ExecuteIfSafe(UObject * <Params_0>, FPropertyChangedEvent & <Params_1>) Line 535 C++
  [Inline Frame] UnrealEditor-CoreUObject.dll!TMulticastDelegateBase<FDefaultDelegateUserPolicy>::Broadcast(UObject *) Line 254 C++
  [Inline Frame] UnrealEditor-CoreUObject.dll!TMulticastDelegate<void __cdecl(UObject *,FPropertyChangedEvent &),FDefaultDelegateUserPolicy>::Broadcast(UObject *) Line 956 C++
  UnrealEditor-CoreUObject.dll!UObject::PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent) Line 443 C++
  UnrealEditor-UnrealEd.dll!FBlueprintEditorUtils::MarkBlueprintAsModified(UBlueprint * Blueprint, FPropertyChangedEvent PropertyChangedEvent) Line 1937 C++
  UnrealEditor-BlueprintGraph.dll!UEdGraphSchema_K2::ResetPinToAutogeneratedDefaultValue(UEdGraphPin * Pin, bool bCallModifyCallbacks) Line 5359 C++
  UnrealEditor-BlueprintGraph.dll!UK2Node::PinConnectionListChanged(UEdGraphPin * Pin) Line 601 C++
  UnrealEditor-BlueprintGraph.dll!UK2Node_Knot::PropagatePinTypeFromDirection(bool bFromInput) Line 181 C++
  UnrealEditor-BlueprintGraph.dll!UK2Node_Knot::PropagatePinType() Line 122 C++
  UnrealEditor-BlueprintGraph.dll!UK2Node_Knot::PostReconstructNode() Line 91 C++
  UnrealEditor-BlueprintGraph.dll!UK2Node::ReconstructNode() Line 758 C++
  UnrealEditor-BlueprintGraph.dll!UEdGraphSchema_K2::ReconstructNode(UEdGraphNode & TargetNode, bool bIsBatchRequest) Line 4766 C++
  UnrealEditor-UnrealEd.dll!FBlueprintEditorUtils::RefreshGraphNodes(const UEdGraph * Graph) Line 763 C++
  UnrealEditor-UnrealEd.dll!ConformInterfaceByGUID(const UBlueprint * Blueprint, const FBPInterfaceDescription & CurrentInterfaceDesc) Line 7124 C++
  UnrealEditor-UnrealEd.dll!FBlueprintEditorUtils::ConformImplementedInterfaces(UBlueprint * Blueprint) Line 7399 C++
  UnrealEditor-Kismet.dll!SMyBlueprint::Refresh() Line 1164 C++
  UnrealEditor-SlateCore.dll!SWidget::Paint(const FPaintArgs & Args, const FGeometry & AllottedGeometry, const FSlateRect & MyCullingRect, FSlateWindowElementList & OutDrawElements, int LayerId, const FWidgetStyle & InWidgetStyle, bool bParentEnabled) Line 1483 C++
  UnrealEditor-SlateCore.dll!SCompoundWidget::OnPaint(const FPaintArgs & Args, const FGeometry & AllottedGeometry, const FSlateRect & MyCullingRect, FSlateWindowElementList & OutDrawElements, int LayerId, const FWidgetStyle & InWidgetStyle, bool bParentEnabled) Line 46 C++
  UnrealEditor-Slate.dll!SBorder::OnPaint(const FPaintArgs & Args, const FGeometry & AllottedGeometry, const FSlateRect & MyCullingRect, FSlateWindowElementList & OutDrawElements, int LayerId, const FWidgetStyle & InWidgetStyle, bool bParentEnabled) Line 129 C++
  UnrealEditor-SlateCore.dll!SWidget::Paint(const FPaintArgs & Args, const FGeometry & AllottedGeometry, const FSlateRect & MyCullingRect, FSlateWindowElementList & OutDrawElements, int LayerId, const FWidgetStyle & InWidgetStyle, bool bParentEnabled) Line 1622 C++
 

Stack overflow (repro steps B):
  UnrealEditor-Kismet.dll!FBlueprintEditor::RefreshEditors(ERefreshBlueprintEditorReason::Type Reason) Line 1045 C++
  UnrealEditor-Kismet.dll!FBlueprintEditor::OnBlueprintChangedImpl(UBlueprint * InBlueprint, bool bIsJustBeingCompiled) Line 4071 C++
  [Inline Frame] UnrealEditor-Kismet.dll!Invoke(void(FBlueprintEditor::*)(UBlueprint *)) Line 66 C++
  [Inline Frame] UnrealEditor-Kismet.dll!UE::Core::Private::Tuple::TTupleBase<TIntegerSequence<unsigned int>>::ApplyAfter(void(FBlueprintEditor::*)(UBlueprint *) &) Line 309 C++
  UnrealEditor-Kismet.dll!TBaseSPMethodDelegateInstance<0,FBlueprintEditor,1,void __cdecl(UBlueprint *),FDefaultDelegateUserPolicy>::ExecuteIfSafe(UBlueprint * <Params_0>) Line 298 C++
  [Inline Frame] UnrealEditor-UnrealEd.dll!TMulticastDelegateBase<FDefaultDelegateUserPolicy>::Broadcast(UBlueprint *) Line 254 C++
  UnrealEditor-UnrealEd.dll!TMulticastDelegate<void __cdecl(UBlueprint *),FDefaultDelegateUserPolicy>::Broadcast(UBlueprint * <Params_0>) Line 956 C++
  [Inline Frame] UnrealEditor-UnrealEd.dll!UBlueprint::BroadcastChanged() Line 670 C++
  UnrealEditor-UnrealEd.dll!FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(UBlueprint * Blueprint) Line 1884 C++
  UnrealEditor-UnrealEd.dll!FBlueprintEditorUtils::RenameGraph(UEdGraph * Graph, const FString & NewNameStr) Line 2717 C++
  UnrealEditor-UnrealEd.dll!ConformInterfaceByGUID(const UBlueprint * Blueprint, const FBPInterfaceDescription & CurrentInterfaceDesc) Line 7122 C++
  UnrealEditor-UnrealEd.dll!FBlueprintEditorUtils::ConformImplementedInterfaces(UBlueprint * Blueprint) Line 7399 C++
  UnrealEditor-Kismet.dll!SMyBlueprint::Refresh() Line 1164 C++
  UnrealEditor-Kismet.dll!FBlueprintEditor::RefreshEditors(ERefreshBlueprintEditorReason::Type Reason) Line 1045 C++
  UnrealEditor-Kismet.dll!FBlueprintEditor::OnBlueprintChangedImpl(UBlueprint * InBlueprint, bool bIsJustBeingCompiled) Line 4071 C++
  [Inline Frame] UnrealEditor-Kismet.dll!Invoke(void(FBlueprintEditor::*)(UBlueprint *)) Line 66 C++
  [Inline Frame] UnrealEditor-Kismet.dll!UE::Core::Private::Tuple::TTupleBase<TIntegerSequence<unsigned int>>::ApplyAfter(void(FBlueprintEditor::*)(UBlueprint *) &) Line 309 C++
  UnrealEditor-Kismet.dll!TBaseSPMethodDelegateInstance<0,FBlueprintEditor,1,void __cdecl(UBlueprint *),FDefaultDelegateUserPolicy>::ExecuteIfSafe(UBlueprint * <Params_0>) Line 298 C++
  [Inline Frame] UnrealEditor-UnrealEd.dll!TMulticastDelegateBase<FDefaultDelegateUserPolicy>::Broadcast(UBlueprint *) Line 254 C++
  UnrealEditor-UnrealEd.dll!TMulticastDelegate<void __cdecl(UBlueprint *),FDefaultDelegateUserPolicy>::Broadcast(UBlueprint * <Params_0>) Line 956 C++
  [Inline Frame] UnrealEditor-UnrealEd.dll!UBlueprint::BroadcastChanged() Line 670 C++
  UnrealEditor-UnrealEd.dll!FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(UBlueprint * Blueprint) Line 1884 C++
  UnrealEditor-UnrealEd.dll!FBlueprintEditorUtils::RenameGraph(UEdGraph * Graph, const FString & NewNameStr) Line 2717 C++
  UnrealEditor-UnrealEd.dll!ConformInterfaceByGUID(const UBlueprint * Blueprint, const FBPInterfaceDescription & CurrentInterfaceDesc) Line 7122 C++
  UnrealEditor-UnrealEd.dll!FBlueprintEditorUtils::ConformImplementedInterfaces(UBlueprint * Blueprint) Line 7399 C++
  UnrealEditor-Kismet.dll!SMyBlueprint::Refresh() Line 1164 C++
  UnrealEditor-Kismet.dll!FBlueprintEditor::RefreshEditors(ERefreshBlueprintEditorReason::Type Reason) Line 1045 C++
  UnrealEditor-Kismet.dll!FBlueprintEditor::OnBlueprintChangedImpl(UBlueprint * InBlueprint, bool bIsJustBeingCompiled) Line 4071 C++
  [Inline Frame] UnrealEditor-Kismet.dll!Invoke(void(FBlueprintEditor::*)(UBlueprint *)) Line 66 C++
  [Inline Frame] UnrealEditor-Kismet.dll!UE::Core::Private::Tuple::TTupleBase<TIntegerSequence<unsigned int>>::ApplyAfter(void(FBlueprintEditor::*)(UBlueprint *) &) Line 309 C++
  UnrealEditor-Kismet.dll!TBaseSPMethodDelegateInstance<0,FBlueprintEditor,1,void __cdecl(UBlueprint *),FDefaultDelegateUserPolicy>::ExecuteIfSafe(UBlueprint * <Params_0>) Line 298 C++
  [Inline Frame] UnrealEditor-UnrealEd.dll!TMulticastDelegateBase<FDefaultDelegateUserPolicy>::Broadcast(UBlueprint *) Line 254 C++
  UnrealEditor-UnrealEd.dll!TMulticastDelegate<void __cdecl(UBlueprint *),FDefaultDelegateUserPolicy>::Broadcast(UBlueprint * <Params_0>) Line 956 C++
  [Inline Frame] UnrealEditor-UnrealEd.dll!UBlueprint::BroadcastChanged() Line 670 C++
  UnrealEditor-UnrealEd.dll!FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(UBlueprint * Blueprint) Line 1884 C++
  UnrealEditor-UnrealEd.dll!FBlueprintEditorUtils::RenameGraph(UEdGraph * Graph, const FString & NewNameStr) Line 2717 C++
  UnrealEditor-UnrealEd.dll!ConformInterfaceByGUID(const UBlueprint * Blueprint, const FBPInterfaceDescription & CurrentInterfaceDesc) Line 7122 C++
  UnrealEditor-UnrealEd.dll!FBlueprintEditorUtils::ConformImplementedInterfaces(UBlueprint * Blueprint) Line 7399 C++
  UnrealEditor-Kismet.dll!SMyBlueprint::Refresh() Line 1164 C++
 

 

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-208678 in the post.

6
Login to Vote

Unresolved
ComponentUE - Gameplay - Blueprint
Affects Versions5.15.25.3
Target Fix5.5
CreatedMar 1, 2024
UpdatedMar 19, 2024