Files
UnrealEngineUWP/Engine/Source/Editor/BlueprintGraph/Private/K2Node_ActorBoundEvent.cpp
marc audy 718ec962bc Dynamic Multicast delegates can now specify that their storage be stored in a sparse allocation for delegates that are expected to be bound rarely. The memory usage of an unbound sparse delegate is 1 byte instead of 16 bytes (for the empty array), however, a bound sparse delegate will have access overhead to retrieve the invocation list from the sparse object annotation.
The definition of the delegate type must specify the class and property name of the delegate that will use it and the type cannot be used outside of that context or just on the stack.
UMulticastDelegateProperty is now abstract and has 2 subclasses (UMulticastDelegateInlineProperty and UMulticastDelegateSparseProperty). The SparseProperty SignatureFunction will be a USparseDelegateFunction.
The sparse delegates do not work correctly with the python bindings at this point, but this will be visited soon.
#rb Michael.Noland
#jira

#ROBOMERGE-OWNER: ryan.vance
#ROBOMERGE-AUTHOR: marc.audy
#ROBOMERGE-SOURCE: CL 5295832 via CL 5306530 via CL 5306657
#ROBOMERGE-BOT: DEVVR (Main -> Dev-VR)

[CL 5323394 by marc audy in Dev-VR branch]
2019-03-06 18:07:13 -05:00

264 lines
8.6 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "K2Node_ActorBoundEvent.h"
#include "Engine/Level.h"
#include "Engine/LevelScriptActor.h"
#include "Engine/LevelScriptBlueprint.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "KismetCompiler.h"
#include "EventEntryHandler.h"
#include "Kismet2/KismetEditorUtilities.h"
#define LOCTEXT_NAMESPACE "K2Node_ActorBoundEvent"
//////////////////////////////////////////////////////////////////////////
// FKCHandler_ActorBoundEventEntry
class FKCHandler_ActorBoundEventEntry : public FKCHandler_EventEntry
{
public:
FKCHandler_ActorBoundEventEntry(FKismetCompilerContext& InCompilerContext)
: FKCHandler_EventEntry(InCompilerContext)
{
}
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override
{
// Check to make sure that the object the event is bound to is valid
const UK2Node_ActorBoundEvent* BoundEventNode = Cast<UK2Node_ActorBoundEvent>(Node);
if (BoundEventNode && BoundEventNode->EventOwner)
{
FKCHandler_EventEntry::Compile(Context, Node);
}
else
{
CompilerContext.MessageLog.Error(*FString(*LOCTEXT("FindNodeBoundEvent_Error", "Couldn't find object for bound event node @@").ToString()), Node);
}
}
};
UK2Node_ActorBoundEvent::UK2Node_ActorBoundEvent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
FNodeHandlingFunctor* UK2Node_ActorBoundEvent::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
{
return new FKCHandler_ActorBoundEventEntry(CompilerContext);
}
void UK2Node_ActorBoundEvent::ReconstructNode()
{
// We need to fixup our event reference as it may have changed or been redirected
UMulticastDelegateProperty* TargetDelegateProp = GetTargetDelegateProperty();
// If we couldn't find the target delegate, then try to find it in the property remap table
if (!TargetDelegateProp)
{
UMulticastDelegateProperty* NewProperty = FMemberReference::FindRemappedField<UMulticastDelegateProperty>(DelegateOwnerClass, DelegatePropertyName);
if (NewProperty)
{
// Found a remapped property, update the node
TargetDelegateProp = NewProperty;
DelegatePropertyName = NewProperty->GetFName();
CachedNodeTitle.MarkDirty();
}
}
if (TargetDelegateProp && TargetDelegateProp->SignatureFunction)
{
EventReference.SetFromField<UFunction>(TargetDelegateProp->SignatureFunction, false);
}
CachedNodeTitle.MarkDirty();
Super::ReconstructNode();
}
void UK2Node_ActorBoundEvent::DestroyNode()
{
if (EventOwner)
{
// If we have an event owner, remove the delegate referencing this event, if any
const ULevel* TargetLevel = EventOwner->GetLevel();
if (TargetLevel)
{
ALevelScriptActor* LSA = TargetLevel->GetLevelScriptActor();
if (LSA)
{
// Create a delegate of the correct signature to remove
FScriptDelegate Delegate;
Delegate.BindUFunction(LSA, CustomFunctionName);
// Attempt to remove it from the target's MC delegate
if (UMulticastDelegateProperty* TargetDelegate = GetTargetDelegateProperty())
{
TargetDelegate->RemoveDelegate(Delegate, EventOwner);
}
}
}
}
Super::DestroyNode();
}
bool UK2Node_ActorBoundEvent::CanPasteHere(const UEdGraph* TargetGraph) const
{
// By default, to be safe, we don't allow events to be pasted, except under special circumstances (see below)
bool bDisallowPaste = !Super::CanPasteHere(TargetGraph);
if (!bDisallowPaste)
{
const AActor* ReferencedLevelActor = GetReferencedLevelActor();
ULevel* Level = ReferencedLevelActor ? ReferencedLevelActor->GetLevel() : nullptr;
const UBlueprint* LevelBP = Level ? Level->GetLevelScriptBlueprint(true) : nullptr;
if (FBlueprintEditorUtils::FindBlueprintForGraph(TargetGraph) == LevelBP)
{
if (const UK2Node_Event* PreExistingNode = FKismetEditorUtilities::FindBoundEventForActor(GetReferencedLevelActor(), DelegatePropertyName))
{
//UE_LOG(LogBlueprint, Log, TEXT("Cannot paste event node (%s) directly because it is flagged as an internal event."), *GetFName().ToString());
bDisallowPaste = true;
}
}
else
{
///UE_LOG(LogBlueprint, Log, TEXT("Cannot paste event node (%s) directly because it is flagged as an internal event."), *GetFName().ToString());
}
}
return !bDisallowPaste;
}
FText UK2Node_ActorBoundEvent::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
if (EventOwner == nullptr)
{
FFormatNamedArguments Args;
Args.Add(TEXT("DelegatePropertyName"), FText::FromName(DelegatePropertyName));
return FText::Format(LOCTEXT("ActorBoundEventTitleNoOwner", "{DelegatePropertyName} (None)"), Args);
}
else if (CachedNodeTitle.IsOutOfDate(this))
{
FFormatNamedArguments Args;
Args.Add(TEXT("DelegatePropertyName"), FText::FromName(DelegatePropertyName));
Args.Add(TEXT("TargetName"), FText::FromString(EventOwner->GetActorLabel()));
// FText::Format() is slow, so we cache this to save on performance
CachedNodeTitle.SetCachedText(FText::Format(LOCTEXT("ActorBoundEventTitle", "{DelegatePropertyName} ({TargetName})"), Args), this);
}
return CachedNodeTitle;
}
FText UK2Node_ActorBoundEvent::GetTooltipText() const
{
UMulticastDelegateProperty* TargetDelegateProp = GetTargetDelegateProperty();
if (TargetDelegateProp)
{
return TargetDelegateProp->GetToolTipText();
}
else
{
return FText::FromName(DelegatePropertyName);
}
}
FString UK2Node_ActorBoundEvent::GetDocumentationLink() const
{
if (UClass* EventSignatureClass = EventReference.GetMemberParentClass(GetBlueprintClassFromNode()))
{
return FString::Printf(TEXT("Shared/GraphNodes/Blueprint/%s%s"), EventSignatureClass->GetPrefixCPP(), *EventSignatureClass->GetName());
}
return FString();
}
FString UK2Node_ActorBoundEvent::GetDocumentationExcerptName() const
{
return DelegatePropertyName.ToString();
}
AActor* UK2Node_ActorBoundEvent::GetReferencedLevelActor() const
{
return EventOwner;
}
void UK2Node_ActorBoundEvent::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const
{
Super::ValidateNodeDuringCompilation(MessageLog);
// make sure that the actor still exists:
AActor* TargetActor = GetReferencedLevelActor();
if(!TargetActor)
{
MessageLog.Warning(
*NSLOCTEXT("KismetCompiler", "MissingActor_ActorBoundEvent", "@@ is referencing an Actor that no longer exists. Attached logic will never execute.").ToString(),
this
);
}
else if(DelegateOwnerClass == nullptr)
{
MessageLog.Warning(
*NSLOCTEXT("KismetCompiler", "MissingClass_ActorBoundEvent", "@@ is trying to find an Event Dispatcher named @@ in a class that no longer exists. Attached logic will never execute.").ToString(),
this,
*DelegatePropertyName.ToString()
);
}
else if( GetTargetDelegatePropertyFromSkel() == nullptr )
{
MessageLog.Warning(
*NSLOCTEXT("KismetCompiler", "MissingDelegate_ActorBoundEvent", "@@ is referencing an Event Dispatcher named @@ that no longer exists in class @@. Attached logic will never execute.").ToString(),
this,
*DelegatePropertyName.ToString(),
DelegateOwnerClass
);
}
}
void UK2Node_ActorBoundEvent::InitializeActorBoundEventParams(AActor* InEventOwner, const UMulticastDelegateProperty* InDelegateProperty)
{
if (InEventOwner && InDelegateProperty)
{
EventOwner = InEventOwner;
DelegatePropertyName = InDelegateProperty->GetFName();
DelegateOwnerClass = CastChecked<UClass>(InDelegateProperty->GetOuter())->GetAuthoritativeClass();
EventReference.SetFromField<UFunction>(InDelegateProperty->SignatureFunction, false);
CustomFunctionName = FName(*FString::Printf(TEXT("BndEvt__%s_%s_%s"), *InEventOwner->GetName(), *GetName(), *EventReference.GetMemberName().ToString()));
bOverrideFunction = false;
bInternalEvent = true;
CachedNodeTitle.MarkDirty();
}
}
UMulticastDelegateProperty* UK2Node_ActorBoundEvent::GetTargetDelegateProperty() const
{
return FindField<UMulticastDelegateProperty>(DelegateOwnerClass, DelegatePropertyName);
}
UMulticastDelegateProperty* UK2Node_ActorBoundEvent::GetTargetDelegatePropertyFromSkel() const
{
return FindField<UMulticastDelegateProperty>(FBlueprintEditorUtils::GetMostUpToDateClass(DelegateOwnerClass), DelegatePropertyName);
}
bool UK2Node_ActorBoundEvent::IsUsedByAuthorityOnlyDelegate() const
{
const UMulticastDelegateProperty* TargetDelegateProp = FindField<UMulticastDelegateProperty>(DelegateOwnerClass, DelegatePropertyName);
return (TargetDelegateProp && TargetDelegateProp->HasAnyPropertyFlags(CPF_BlueprintAuthorityOnly));
}
void UK2Node_ActorBoundEvent::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
// Fix up legacy nodes that may not yet have a delegate pin
if (Ar.IsLoading())
{
if (Ar.UE4Ver() < VER_UE4_K2NODE_EVENT_MEMBER_REFERENCE)
{
DelegateOwnerClass = EventSignatureClass_DEPRECATED;
}
}
}
#undef LOCTEXT_NAMESPACE