253 lines
9.1 KiB
C++
253 lines
9.1 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "VariableSetHandler.h"
|
|
|
|
#include "BPTerminal.h"
|
|
#include "Containers/Array.h"
|
|
#include "Containers/EnumAsByte.h"
|
|
#include "Containers/Map.h"
|
|
#include "Containers/UnrealString.h"
|
|
#include "EdGraph/EdGraph.h"
|
|
#include "EdGraph/EdGraphNode.h"
|
|
#include "EdGraph/EdGraphPin.h"
|
|
#include "EdGraphSchema_K2.h"
|
|
#include "EdGraphUtilities.h"
|
|
#include "Engine/MemberReference.h"
|
|
#include "GameFramework/Actor.h"
|
|
#include "HAL/PlatformMath.h"
|
|
#include "Internationalization/Internationalization.h"
|
|
#include "Internationalization/Text.h"
|
|
#include "K2Node_CallFunction.h"
|
|
#include "K2Node_Variable.h"
|
|
#include "K2Node_VariableSet.h"
|
|
#include "Kismet2/CompilerResultsLog.h"
|
|
#include "KismetCompiledFunctionContext.h"
|
|
#include "KismetCompiler.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
#include "PushModelHelpers.h"
|
|
#include "Templates/Casts.h"
|
|
#include "UObject/Class.h"
|
|
#include "UObject/ObjectMacros.h"
|
|
#include "UObject/UnrealNames.h"
|
|
#include "UObject/UnrealType.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "VariableSetHandler"
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FKCHandler_VariableSet
|
|
|
|
void FKCHandler_VariableSet::RegisterNet(FKismetFunctionContext& Context, UEdGraphPin* Net)
|
|
{
|
|
// This net is a variable write
|
|
ResolveAndRegisterScopedTerm(Context, Net, Context.VariableReferences);
|
|
}
|
|
|
|
void FKCHandler_VariableSet::RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node)
|
|
{
|
|
if(UK2Node_Variable* SetterNode = Cast<UK2Node_Variable>(Node))
|
|
{
|
|
SetterNode->CheckForErrors(CompilerContext.GetSchema(), Context.MessageLog);
|
|
|
|
// Report an error that the local variable could not be found
|
|
if(SetterNode->VariableReference.IsLocalScope() && SetterNode->GetPropertyForVariable() == nullptr)
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("VariableName"), FText::FromName(SetterNode->VariableReference.GetMemberName()));
|
|
|
|
if(SetterNode->VariableReference.GetMemberScopeName() != Context.Function->GetName())
|
|
{
|
|
Args.Add(TEXT("ScopeName"), FText::FromString(SetterNode->VariableReference.GetMemberScopeName()));
|
|
CompilerContext.MessageLog.Warning(*FText::Format(LOCTEXT("LocalVariableNotFoundInScope_Error", "Unable to find local variable with name '{VariableName}' for @@, scope expected: @@, scope found: {ScopeName}"), Args).ToString(), Node, Node->GetGraph());
|
|
}
|
|
else
|
|
{
|
|
CompilerContext.MessageLog.Warning(*FText::Format(LOCTEXT("LocalVariableNotFound_Error", "Unable to find local variable with name '{VariableName}' for @@"), Args).ToString(), Node);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (UEdGraphPin* Net : Node->Pins)
|
|
{
|
|
if (!Net->bOrphanedPin && (Net->Direction == EGPD_Input) && !CompilerContext.GetSchema()->IsMetaPin(*Net))
|
|
{
|
|
if (ValidateAndRegisterNetIfLiteral(Context, Net))
|
|
{
|
|
RegisterNet(Context, Net);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FKCHandler_VariableSet::InnerAssignment(FKismetFunctionContext& Context, UEdGraphNode* Node, UEdGraphPin* VariablePin, UEdGraphPin* ValuePin)
|
|
{
|
|
FBPTerminal** VariableTerm = Context.NetMap.Find(VariablePin);
|
|
if (VariableTerm == nullptr)
|
|
{
|
|
VariableTerm = Context.NetMap.Find(FEdGraphUtilities::GetNetFromPin(VariablePin));
|
|
}
|
|
|
|
FBPTerminal** ValueTerm = Context.LiteralHackMap.Find(ValuePin);
|
|
if (ValueTerm == nullptr)
|
|
{
|
|
ValueTerm = Context.NetMap.Find(FEdGraphUtilities::GetNetFromPin(ValuePin));
|
|
}
|
|
|
|
if (VariableTerm && ValueTerm)
|
|
{
|
|
FKismetCompilerUtilities::CreateObjectAssignmentStatement(Context,
|
|
Node,
|
|
*ValueTerm,
|
|
*VariableTerm,
|
|
(UsesVariablePinAsKey() ? VariablePin : nullptr));
|
|
|
|
if (!(*VariableTerm)->IsTermWritable())
|
|
{
|
|
// If the term is not explicitly marked as read-only, then we're attempting to set a variable on a const target
|
|
if(!(*VariableTerm)->AssociatedVarProperty->HasAnyPropertyFlags(CPF_BlueprintReadOnly))
|
|
{
|
|
if(Context.EnforceConstCorrectness())
|
|
{
|
|
CompilerContext.MessageLog.Error(*LOCTEXT("WriteToReadOnlyContext_Error", "Variable @@ is read-only within this context and cannot be set to a new value").ToString(), VariablePin);
|
|
}
|
|
else
|
|
{
|
|
// Warn, but still allow compilation to succeed
|
|
CompilerContext.MessageLog.Warning(*LOCTEXT("WriteToReadOnlyContext_Warning", "Variable @@ is considered to be read-only within this context and should not be set to a new value").ToString(), VariablePin);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CompilerContext.MessageLog.Error(*LOCTEXT("WriteConst_Error", "Cannot write to const @@").ToString(), VariablePin);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (VariablePin != ValuePin)
|
|
{
|
|
CompilerContext.MessageLog.Error(*LOCTEXT("ResolveValueIntoVariablePin_Error", "Failed to resolve term @@ passed into @@").ToString(), ValuePin, VariablePin);
|
|
}
|
|
else
|
|
{
|
|
CompilerContext.MessageLog.Error(*LOCTEXT("ResolveTermPassed_Error", "Failed to resolve term passed into @@").ToString(), VariablePin);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FKCHandler_VariableSet::GenerateAssigments(FKismetFunctionContext& Context, UEdGraphNode* Node)
|
|
{
|
|
// SubCategory is an object type or "" for the stack frame, default scope is Self
|
|
// Each input pin is the name of a variable
|
|
|
|
// Each input pin represents an assignment statement
|
|
for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
|
|
{
|
|
UEdGraphPin* Pin = Node->Pins[PinIndex];
|
|
|
|
if (CompilerContext.GetSchema()->IsMetaPin(*Pin))
|
|
{
|
|
}
|
|
else if (Pin->Direction == EGPD_Input)
|
|
{
|
|
InnerAssignment(Context, Node, Pin, Pin);
|
|
}
|
|
else
|
|
{
|
|
CompilerContext.MessageLog.Error(*LOCTEXT("ExpectedOnlyInputPins_Error", "Expected only input pins on @@ but found @@").ToString(), Node, Pin);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FKCHandler_VariableSet::Compile(FKismetFunctionContext& Context, UEdGraphNode* Node)
|
|
{
|
|
GenerateAssigments(Context, Node);
|
|
|
|
// Generate the output impulse from this node
|
|
GenerateSimpleThenGoto(Context, *Node);
|
|
}
|
|
|
|
void FKCHandler_VariableSet::Transform(FKismetFunctionContext& Context, UEdGraphNode* Node)
|
|
{
|
|
// Expands node out to include a (local) call to the RepNotify function if necessary
|
|
UK2Node_VariableSet* SetNotify = Cast<UK2Node_VariableSet>(Node);
|
|
|
|
// If property is HasFieldNotificationBroadcast, then the net code and broadcast will be executed in native code.
|
|
if (SetNotify && !SetNotify->HasFieldNotificationBroadcast())
|
|
{
|
|
if (SetNotify->ShouldFlushDormancyOnSet())
|
|
{
|
|
// Create CallFuncNode
|
|
UK2Node_CallFunction* CallFuncNode = Node->GetGraph()->CreateIntermediateNode<UK2Node_CallFunction>();
|
|
CallFuncNode->FunctionReference.SetExternalMember(NAME_FlushNetDormancy, AActor::StaticClass() );
|
|
CallFuncNode->AllocateDefaultPins();
|
|
|
|
// Copy self pin
|
|
UEdGraphPin* NewSelfPin = CallFuncNode->FindPinChecked(UEdGraphSchema_K2::PN_Self);
|
|
UEdGraphPin* OldSelfPin = Node->FindPinChecked(UEdGraphSchema_K2::PN_Self);
|
|
NewSelfPin->CopyPersistentDataFromOldPin(*OldSelfPin);
|
|
|
|
// link new CallFuncNode -> Set Node
|
|
UEdGraphPin* OldExecPin = Node->FindPin(UEdGraphSchema_K2::PN_Execute);
|
|
check(OldExecPin);
|
|
|
|
UEdGraphPin* NewExecPin = CallFuncNode->GetExecPin();
|
|
if (ensure(NewExecPin))
|
|
{
|
|
NewExecPin->CopyPersistentDataFromOldPin(*OldExecPin);
|
|
OldExecPin->BreakAllPinLinks();
|
|
CallFuncNode->GetThenPin()->MakeLinkTo(OldExecPin);
|
|
}
|
|
}
|
|
|
|
if (SetNotify->HasLocalRepNotify())
|
|
{
|
|
UK2Node_CallFunction* CallFuncNode = Node->GetGraph()->CreateIntermediateNode<UK2Node_CallFunction>();
|
|
CallFuncNode->FunctionReference.SetExternalMember(SetNotify->GetRepNotifyName(), SetNotify->GetVariableSourceClass() );
|
|
CallFuncNode->AllocateDefaultPins();
|
|
|
|
// Copy self pin
|
|
UEdGraphPin* NewSelfPin = CallFuncNode->FindPinChecked(UEdGraphSchema_K2::PN_Self);
|
|
UEdGraphPin* OldSelfPin = Node->FindPinChecked(UEdGraphSchema_K2::PN_Self);
|
|
NewSelfPin->CopyPersistentDataFromOldPin(*OldSelfPin);
|
|
|
|
// link Set Node -> new CallFuncNode
|
|
UEdGraphPin* OldThenPin = Node->FindPin(UEdGraphSchema_K2::PN_Then);
|
|
check(OldThenPin);
|
|
|
|
UEdGraphPin* NewThenPin = CallFuncNode->GetThenPin();
|
|
if (ensure(NewThenPin))
|
|
{
|
|
// Link Set Node -> Notify
|
|
NewThenPin->CopyPersistentDataFromOldPin(*OldThenPin);
|
|
OldThenPin->BreakAllPinLinks();
|
|
OldThenPin->MakeLinkTo(CallFuncNode->GetExecPin());
|
|
}
|
|
}
|
|
|
|
if (SetNotify->IsNetProperty())
|
|
{
|
|
/**
|
|
* This code is for property dirty tracking.
|
|
* It works by injecting in extra nodes while compiling that will call UNetPushModelHelpers::MarkPropertyDirtyFromRepIndex.
|
|
* See FKCPushModelHelpers::ConstructMarkDirtyNodeForProperty for node generation.
|
|
*/
|
|
if (FProperty * Property = SetNotify->GetPropertyForVariable())
|
|
{
|
|
if (UEdGraphNode * MarkPropertyDirtyNode = FKCPushModelHelpers::ConstructMarkDirtyNodeForProperty(Context, Property, Node->FindPinChecked(UEdGraphSchema_K2::PN_Self)))
|
|
{
|
|
// Hook up our exec pins.
|
|
UEdGraphPin* OldThenPin = Node->FindPinChecked(UEdGraphSchema_K2::PN_Then);
|
|
UEdGraphPin* NewThenPin = MarkPropertyDirtyNode->FindPinChecked(UEdGraphSchema_K2::PN_Then);
|
|
UEdGraphPin* NewInPin = MarkPropertyDirtyNode->FindPinChecked(UEdGraphSchema_K2::PN_Execute);
|
|
|
|
NewThenPin->CopyPersistentDataFromOldPin(*OldThenPin);
|
|
OldThenPin->BreakAllPinLinks();
|
|
OldThenPin->MakeLinkTo(NewInPin);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|