You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Overview: - An "override flag" is an inline toggle-style Boolean edit condition. These are implicitly set to true at runtime within the output struct value if any member that's bound to it is exposed as a visible input pin on a MakeStruct node. - For context, the Property Editor implicitly sets an override flag to true at edit time when a member that's bound to it is enabled for editing. These members are not otherwise labeled/exposed for direct editing. - An override flag is meant to signal to a system that the user wishes to use the bound member's value in place of the current value (whatever that may be) when the full struct value is applied. Examples: FPostProcessSettings, FMovieSceneSequencePlaybackSettings, etc. Previous UX: - All boolean edit conditions were being treated as override flags on a MakeStruct node. - Any inline toggle edit condition that did not begin with "bOverride_" or whose suffix otherwise did not match another value member could be exposed as an input on a MakeStruct node. - Override flags exposed as inputs would always be set to TRUE at runtime regardless of input if it was declared at the top of the struct and if a member value bound to it was also exposed as an input pin. After this change: - Only inline toggle edit conditions and legacy struct members that follow the "bOverride_" naming convention will be treated as an override flag on a MakeStruct node. - Inline toggle edit conditions can no longer be exposed directly as an input on a MakeStruct node. The intent was to bring the MakeStruct node UX closer to parity with the Property Editor UX. Additional notes: - Members that follow the legacy "bOverride_" naming convention were already being excluded from the optional input pin set on MakeStruct nodes if another member property name also matched its suffix. These have historically been excluded from ALL optional pin sets that utilize any FOptionalPinManager subtype (regardless of node type), so there was no change here. - Existing MakeStruct nodes that may have already exposed inline toggle edit condition members as input pins will now orphan those pins on load if connected or if set to a non-default value (true). The "correct" way to set an override flag is by choosing to expose a member that's bound to the override condition as an input. - Existing BreakStruct nodes are unchanged currently. Meaning, inline edit conditions that don't follow the legacy "bOverride_" convention can still be optionally exposed as an output pin. This UX was preserved as existing Blueprint logic could conceivably rely on the value of an override flag. - Only one implicit assignment is now emitted for each override flag binding. Previously, we were emitting one assignment statement per bound property, so it could result in redundant assignments to the same flag if more than one property was bound to it. #jira UE-147873 #rb Ben.Zeigler, Sebastian.Nordgren #preflight 632c7353c7791417aa87f3bf [CL 22164359 by phillip kavan in ue5-main branch]
249 lines
9.9 KiB
C++
249 lines
9.9 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MakeStructHandler.h"
|
|
|
|
#include "BPTerminal.h"
|
|
#include "BlueprintCompiledStatement.h"
|
|
#include "Containers/Array.h"
|
|
#include "Containers/EnumAsByte.h"
|
|
#include "Containers/IndirectArray.h"
|
|
#include "Containers/Map.h"
|
|
#include "Containers/UnrealString.h"
|
|
#include "EdGraph/EdGraphNode.h"
|
|
#include "EdGraph/EdGraphPin.h"
|
|
#include "EdGraphSchema_K2.h"
|
|
#include "EdGraphUtilities.h"
|
|
#include "HAL/Platform.h"
|
|
#include "HAL/PlatformCrt.h"
|
|
#include "Internationalization/Internationalization.h"
|
|
#include "Internationalization/Text.h"
|
|
#include "K2Node.h"
|
|
#include "K2Node_MakeStruct.h"
|
|
#include "Kismet2/CompilerResultsLog.h"
|
|
#include "KismetCompiledFunctionContext.h"
|
|
#include "KismetCompiler.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
#include "Templates/Casts.h"
|
|
#include "UObject/Class.h"
|
|
#include "UObject/NameTypes.h"
|
|
#include "UObject/Object.h"
|
|
#include "UObject/ObjectMacros.h"
|
|
#include "UObject/ObjectPtr.h"
|
|
#include "UObject/UObjectGlobals.h"
|
|
#include "UObject/UnrealType.h"
|
|
#include "UObject/WeakObjectPtrTemplates.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "FKCHandler_MakeStruct"
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FKCHandler_MakeStruct
|
|
|
|
UEdGraphPin* FKCHandler_MakeStruct::FindStructPinChecked(UEdGraphNode* Node) const
|
|
{
|
|
check(Node);
|
|
UEdGraphPin* OutputPin = nullptr;
|
|
for (UEdGraphPin* Pin : Node->Pins)
|
|
{
|
|
if (Pin && (EGPD_Output == Pin->Direction) && !CompilerContext.GetSchema()->IsMetaPin(*Pin))
|
|
{
|
|
OutputPin = Pin;
|
|
break;
|
|
}
|
|
}
|
|
check(OutputPin);
|
|
return OutputPin;
|
|
}
|
|
|
|
FKCHandler_MakeStruct::FKCHandler_MakeStruct(FKismetCompilerContext& InCompilerContext)
|
|
: FNodeHandlingFunctor(InCompilerContext)
|
|
, bAutoGenerateGotoForPure(true)
|
|
{
|
|
}
|
|
|
|
void FKCHandler_MakeStruct::RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* InNode)
|
|
{
|
|
UK2Node_MakeStruct* Node = CastChecked<UK2Node_MakeStruct>(InNode);
|
|
if (nullptr == Node->StructType)
|
|
{
|
|
CompilerContext.MessageLog.Error(*LOCTEXT("MakeStruct_UnknownStructure_Error", "Unknown structure to break for @@").ToString(), Node);
|
|
return;
|
|
}
|
|
|
|
if (!UK2Node_MakeStruct::CanBeMade(Node->StructType, Node->IsIntermediateNode()))
|
|
{
|
|
CompilerContext.MessageLog.Error(*LOCTEXT("MakeStruct_Error", "The structure @@ is not a BlueprintType. ").ToString(), Node);
|
|
return;
|
|
}
|
|
|
|
FNodeHandlingFunctor::RegisterNets(Context, Node);
|
|
|
|
UEdGraphPin* OutputPin = FindStructPinChecked(Node);
|
|
UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(OutputPin);
|
|
check(Net);
|
|
FBPTerminal** FoundTerm = Context.NetMap.Find(Net);
|
|
FBPTerminal* Term = FoundTerm ? *FoundTerm : nullptr;
|
|
|
|
if (Term == nullptr)
|
|
{
|
|
CompilerContext.MessageLog.Error(*LOCTEXT("MakeStruct_NoTerm_Error", "Failed to generate a term for the @@ pin; was it a struct reference that was left unset?").ToString(), OutputPin);
|
|
}
|
|
else
|
|
{
|
|
UStruct* StructInTerm = Cast<UStruct>(Term->Type.PinSubCategoryObject.Get());
|
|
if (nullptr == StructInTerm || !StructInTerm->IsChildOf(Node->StructType))
|
|
{
|
|
CompilerContext.MessageLog.Error(*LOCTEXT("MakeStruct_NoMatch_Error", "Structures don't match for @@").ToString(), Node);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void FKCHandler_MakeStruct::RegisterNet(FKismetFunctionContext& Context, UEdGraphPin* Net)
|
|
{
|
|
if (!Net->bDefaultValueIsIgnored)
|
|
{
|
|
FBPTerminal* Term = Context.CreateLocalTerminalFromPinAutoChooseScope(Net, Context.NetNameMap->MakeValidName(Net));
|
|
Context.NetMap.Add(Net, Term);
|
|
}
|
|
}
|
|
|
|
FBPTerminal* FKCHandler_MakeStruct::RegisterLiteral(FKismetFunctionContext& Context, UEdGraphPin* Net)
|
|
{
|
|
FBPTerminal* ReturnTerm = nullptr;
|
|
if (!Net->bDefaultValueIsIgnored)
|
|
{
|
|
ReturnTerm = FNodeHandlingFunctor::RegisterLiteral(Context, Net);
|
|
}
|
|
return ReturnTerm;
|
|
}
|
|
|
|
void FKCHandler_MakeStruct::Compile(FKismetFunctionContext& Context, UEdGraphNode* InNode)
|
|
{
|
|
UK2Node_MakeStruct* Node = CastChecked<UK2Node_MakeStruct>(InNode);
|
|
if (NULL == Node->StructType)
|
|
{
|
|
CompilerContext.MessageLog.Error(*LOCTEXT("MakeStruct_UnknownStructure_Error", "Unknown structure to break for @@").ToString(), Node);
|
|
return;
|
|
}
|
|
|
|
UEdGraphPin* StructPin = FindStructPinChecked(Node);
|
|
UEdGraphPin* OutputStructNet = FEdGraphUtilities::GetNetFromPin(StructPin);
|
|
FBPTerminal** FoundTerm = Context.NetMap.Find(OutputStructNet);
|
|
FBPTerminal* OutputStructTerm = FoundTerm ? *FoundTerm : NULL;
|
|
check(OutputStructTerm);
|
|
|
|
// A set of edit condition properties that should be set to signal an override state
|
|
// when the condition is not exposed as a unique input alongside any bound properties.
|
|
TMap<FProperty*, bool> OverrideProperties;
|
|
|
|
const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
|
|
|
|
for (UEdGraphPin* Pin : Node->Pins)
|
|
{
|
|
if (Pin && !Pin->bOrphanedPin && (Pin != StructPin) && (Pin->Direction == EGPD_Input) && !Schema->IsMetaPin(*Pin))
|
|
{
|
|
FProperty* BoundProperty = FindFProperty<FProperty>(Node->StructType, Pin->PinName);
|
|
check(BoundProperty);
|
|
|
|
// If the pin is not connectible, do not forward the net
|
|
if (!Pin->bNotConnectable)
|
|
{
|
|
if (FBPTerminal** FoundSrcTerm = Context.NetMap.Find(FEdGraphUtilities::GetNetFromPin(Pin)))
|
|
{
|
|
FBPTerminal* SrcTerm = FoundSrcTerm ? *FoundSrcTerm : nullptr;
|
|
check(SrcTerm);
|
|
|
|
FBPTerminal* DstTerm = Context.CreateLocalTerminal();
|
|
DstTerm->CopyFromPin(Pin, Context.NetNameMap->MakeValidName(Pin));
|
|
DstTerm->AssociatedVarProperty = BoundProperty;
|
|
DstTerm->Context = OutputStructTerm;
|
|
|
|
FKismetCompilerUtilities::CreateObjectAssignmentStatement(Context, Node, SrcTerm, DstTerm);
|
|
}
|
|
}
|
|
|
|
// Determine if the property bound to the input pin has an associated override flag that's linked via
|
|
// an edit condition. If we find an override property and it's not included in the optional pin set,
|
|
// it signals that we need to implicitly set it at runtime since the value bound to it is exposed as
|
|
// an input. This convention allows us to roughly emulate the Property Editor which injects the override
|
|
// flag's value when the user modifies/exposes the bound property's value as an override at edit time.
|
|
//
|
|
// Note: This API returns a Boolean flag type only. Other condition types are not supported since the
|
|
// "override" concept involves a direct association with a Boolean edit condition. Override properties
|
|
// must also be visible to the BP runtime as well as writable, or the terms used in the implicit
|
|
// assignment statement below may otherwise fail to compile.
|
|
FBoolProperty* OverrideProperty = Node->GetOverrideConditionForProperty(BoundProperty);
|
|
if (OverrideProperty && OverrideProperty->HasAllPropertyFlags(CPF_BlueprintVisible) && !OverrideProperty->HasAllPropertyFlags(CPF_BlueprintReadOnly))
|
|
{
|
|
// Need to dig up what the state of the override property should be
|
|
const FOptionalPinFromProperty* BoundPropertyEntryPtr = Node->ShowPinForProperties.FindByPredicate([BoundProperty](const FOptionalPinFromProperty& PropertyEntry)
|
|
{
|
|
// If we are showing the pin, then we are overriding the property
|
|
return PropertyEntry.bHasOverridePin && PropertyEntry.bShowPin && PropertyEntry.PropertyName == BoundProperty->GetFName();
|
|
});
|
|
|
|
if (BoundPropertyEntryPtr)
|
|
{
|
|
const FOptionalPinFromProperty& PropertyEntry = *BoundPropertyEntryPtr;
|
|
|
|
if (!PropertyEntry.bIsOverridePinVisible || (PropertyEntry.bIsOverridePinVisible && !PropertyEntry.bIsOverrideEnabled && PropertyEntry.bIsSetValuePinVisible))
|
|
{
|
|
CompilerContext.MessageLog.Warning(*LOCTEXT("MakeStruct_InvalidOverrideSetting", "Selected override setting on @@ in @@ is no longer a supported workflow and it is advised that you refactor your Blueprint to not use it!").ToString(), Pin, Node->GetBlueprint());
|
|
}
|
|
|
|
// This flag is a bit of a misnomer - it's generally true, except for assets saved prior to the convention described above, in which case the pin
|
|
// may have been explicitly hidden by the user, and thus this flag would be false to indicate that. For backwards-compatibility, we don't want to
|
|
// implicitly set the value in that case, because it means the user had explicitly opted not to show it, which implies it was not previously set.
|
|
if (PropertyEntry.bIsOverridePinVisible)
|
|
{
|
|
// Note: bIsOverrideEnabled is generally true, but may be false for older assets that were saved with an exposed override pin with a default
|
|
// value prior to the change that excludes all override properties from the optional input pin set.
|
|
OverrideProperties.Add(OverrideProperty, PropertyEntry.bIsOverrideEnabled);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle injecting the override property values into the node
|
|
TArray<FBlueprintCompiledStatement*>& StatementList = Context.StatementsPerNode.FindOrAdd(Node);
|
|
for (auto OverridePropIt = OverrideProperties.CreateConstIterator(); OverridePropIt; ++OverridePropIt)
|
|
{
|
|
FProperty* OverrideProperty = OverridePropIt.Key();
|
|
const bool bIsOverrideEnabled = OverridePropIt.Value();
|
|
|
|
FEdGraphPinType PinType;
|
|
Schema->ConvertPropertyToPinType(OverrideProperty, /*out*/ PinType);
|
|
|
|
// Create the term in the list
|
|
FBPTerminal* OverrideTerm = new FBPTerminal();
|
|
Context.VariableReferences.Add(OverrideTerm);
|
|
OverrideTerm->Type = PinType;
|
|
OverrideTerm->AssociatedVarProperty = OverrideProperty;
|
|
OverrideTerm->Context = OutputStructTerm;
|
|
|
|
FBlueprintCompiledStatement* AssignBoolStatement = new FBlueprintCompiledStatement();
|
|
AssignBoolStatement->Type = KCST_Assignment;
|
|
|
|
// Literal Bool Term to set the OverrideProperty to
|
|
FBPTerminal* BoolTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal);
|
|
BoolTerm->Type.PinCategory = UEdGraphSchema_K2::PC_Boolean;
|
|
BoolTerm->bIsLiteral = true;
|
|
BoolTerm->Name = bIsOverrideEnabled ? TEXT("true") : TEXT("false");
|
|
|
|
// Assigning the OverrideProperty to the literal bool term
|
|
AssignBoolStatement->LHS = OverrideTerm;
|
|
AssignBoolStatement->RHS.Add(BoolTerm);
|
|
|
|
Context.AllGeneratedStatements.Add(AssignBoolStatement);
|
|
StatementList.Add(AssignBoolStatement);
|
|
}
|
|
|
|
if (bAutoGenerateGotoForPure && !Node->IsNodePure())
|
|
{
|
|
GenerateSimpleThenGoto(Context, *Node);
|
|
}
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|