2022-03-29 08:43:59 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "K2Node_WebAPIOperation.h"
# include "BlueprintActionDatabaseRegistrar.h"
2022-05-29 21:30:22 -04:00
# include "BlueprintEditor.h"
2022-03-29 08:43:59 -04:00
# include "BlueprintTypePromotion.h"
# include "EdGraphSchema_K2.h"
2022-05-29 21:30:22 -04:00
# include "K2Node_AddDelegate.h"
# include "K2Node_AssignmentStatement.h"
# include "K2Node_CallFunction.h"
# include "K2Node_CreateDelegate.h"
# include "K2Node_CustomEvent.h"
# include "K2Node_IfThenElse.h"
# include "K2Node_Self.h"
# include "K2Node_TemporaryVariable.h"
2022-03-29 08:43:59 -04:00
# include "KismetCompiler.h"
2022-05-29 21:30:22 -04:00
# include "PropertyPath.h"
2022-03-29 08:43:59 -04:00
# include "ScopedTransaction.h"
# include "ToolMenu.h"
2022-05-29 21:30:22 -04:00
# include "WebAPIBlueprintGraphLog.h"
# include "WebAPIBlueprintGraphUtilities.h"
2022-03-29 08:43:59 -04:00
# include "WebAPIOperationObject.h"
# include "Algo/AllOf.h"
# include "EdGraph/EdGraphNode.h"
# include "EdGraph/EdGraphPin.h"
2022-05-29 21:30:22 -04:00
# include "Kismet/KismetSystemLibrary.h"
2022-03-29 08:43:59 -04:00
# include "Kismet2/BlueprintEditorUtils.h"
2022-05-29 21:30:22 -04:00
# include "Kismet2/KismetEditorUtilities.h"
2022-03-29 08:43:59 -04:00
# define LOCTEXT_NAMESPACE "K2Node_WebAPIAsyncOperation"
2022-05-29 21:30:22 -04:00
namespace UE : : WebAPI : : Private
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
// @note: the next calls are from BaseAsyncTask
static bool ValidDataPin ( const UEdGraphPin * InPin , EEdGraphPinDirection InDirection )
{
const bool bValidDataPin = InPin
& & ! InPin - > bOrphanedPin
& & ( InPin - > PinType . PinCategory ! = UEdGraphSchema_K2 : : PC_Exec ) ;
2022-03-29 08:43:59 -04:00
2022-05-29 21:30:22 -04:00
const bool bProperDirection = InPin & & ( InPin - > Direction = = InDirection ) ;
2022-03-29 08:43:59 -04:00
2022-05-29 21:30:22 -04:00
return bValidDataPin & & bProperDirection ;
}
bool CreateDelegateForNewFunction ( UEdGraphPin * DelegateInputPin , FName FunctionName , UK2Node * CurrentNode , UEdGraph * SourceGraph , FKismetCompilerContext & CompilerContext )
{
const UEdGraphSchema_K2 * Schema = CompilerContext . GetSchema ( ) ;
check ( DelegateInputPin & & Schema & & CurrentNode & & SourceGraph & & ( FunctionName ! = NAME_None ) ) ;
bool bResult = true ;
// WORKAROUND, so we can create delegate from nonexistent function by avoiding check at expanding step
// instead simply: Schema->TryCreateConnection(AddDelegateNode->GetDelegatePin(), CurrentCENode->FindPinChecked(UK2Node_CustomEvent::DelegateOutputName));
UK2Node_Self * SelfNode = CompilerContext . SpawnIntermediateNode < UK2Node_Self > ( CurrentNode , SourceGraph ) ;
SelfNode - > AllocateDefaultPins ( ) ;
UK2Node_CreateDelegate * CreateDelegateNode = CompilerContext . SpawnIntermediateNode < UK2Node_CreateDelegate > ( CurrentNode , SourceGraph ) ;
CreateDelegateNode - > AllocateDefaultPins ( ) ;
bResult & = Schema - > TryCreateConnection ( DelegateInputPin , CreateDelegateNode - > GetDelegateOutPin ( ) ) ;
bResult & = Schema - > TryCreateConnection ( SelfNode - > FindPinChecked ( UEdGraphSchema_K2 : : PN_Self ) , CreateDelegateNode - > GetObjectInPin ( ) ) ;
CreateDelegateNode - > SetFunction ( FunctionName ) ;
return bResult ;
}
bool CopyEventSignature ( UK2Node_CustomEvent * CENode , UFunction * Function , const UEdGraphSchema_K2 * Schema )
{
check ( CENode & & Function & & Schema ) ;
bool bResult = true ;
for ( TFieldIterator < FProperty > PropIt ( Function ) ; PropIt & & ( PropIt - > PropertyFlags & CPF_Parm ) ; + + PropIt )
{
const FProperty * Param = * PropIt ;
if ( ! Param - > HasAnyPropertyFlags ( CPF_OutParm ) | | Param - > HasAnyPropertyFlags ( CPF_ReferenceParm ) )
{
FEdGraphPinType PinType ;
bResult & = Schema - > ConvertPropertyToPinType ( Param , /*out*/ PinType ) ;
bResult & = ( nullptr ! = CENode - > CreateUserDefinedPin ( Param - > GetFName ( ) , PinType , EGPD_Output ) ) ;
}
}
return bResult ;
}
bool HandleDelegateImplementation (
FMulticastDelegateProperty * CurrentProperty , const TArray < FOutputPinAndLocalVariable > & VariableOutputs ,
UEdGraphPin * ProxyObjectPin , UEdGraphPin * & InOutLastThenPin , UEdGraphPin * & OutLastActivatedThenPin ,
UK2Node * CurrentNode , UEdGraph * SourceGraph , FKismetCompilerContext & CompilerContext )
{
bool bIsErrorFree = true ;
const UEdGraphSchema_K2 * Schema = CompilerContext . GetSchema ( ) ;
check ( CurrentProperty & & ProxyObjectPin & & InOutLastThenPin & & CurrentNode & & SourceGraph & & Schema ) ;
UEdGraphPin * PinForCurrentDelegateProperty = CurrentNode - > FindPin ( CurrentProperty - > GetFName ( ) ) ;
if ( ! PinForCurrentDelegateProperty | | ( UEdGraphSchema_K2 : : PC_Exec ! = PinForCurrentDelegateProperty - > PinType . PinCategory ) )
{
FText ErrorMessage = FText : : Format ( LOCTEXT ( " WrongDelegateProperty " , " WebAPIOperation: Cannot find execution pin for delegate " ) , FText : : FromString ( CurrentProperty - > GetName ( ) ) ) ;
CompilerContext . MessageLog . Error ( * ErrorMessage . ToString ( ) , CurrentNode ) ;
return false ;
}
UK2Node_CustomEvent * CurrentCENode = CompilerContext . SpawnIntermediateEventNode < UK2Node_CustomEvent > ( CurrentNode , PinForCurrentDelegateProperty , SourceGraph ) ;
{
UK2Node_AddDelegate * AddDelegateNode = CompilerContext . SpawnIntermediateNode < UK2Node_AddDelegate > ( CurrentNode , SourceGraph ) ;
AddDelegateNode - > SetFromProperty ( CurrentProperty , false , CurrentProperty - > GetOwnerClass ( ) ) ;
AddDelegateNode - > AllocateDefaultPins ( ) ;
bIsErrorFree & = Schema - > TryCreateConnection ( AddDelegateNode - > FindPinChecked ( UEdGraphSchema_K2 : : PN_Self ) , ProxyObjectPin ) ;
bIsErrorFree & = Schema - > TryCreateConnection ( InOutLastThenPin , AddDelegateNode - > FindPinChecked ( UEdGraphSchema_K2 : : PN_Execute ) ) ;
InOutLastThenPin = AddDelegateNode - > FindPinChecked ( UEdGraphSchema_K2 : : PN_Then ) ;
CurrentCENode - > CustomFunctionName = * FString : : Printf ( TEXT ( " %s_%s " ) , * CurrentProperty - > GetName ( ) , * CompilerContext . GetGuid ( CurrentNode ) ) ;
CurrentCENode - > AllocateDefaultPins ( ) ;
bIsErrorFree & = CreateDelegateForNewFunction ( AddDelegateNode - > GetDelegatePin ( ) , CurrentCENode - > GetFunctionName ( ) , CurrentNode , SourceGraph , CompilerContext ) ;
bIsErrorFree & = CopyEventSignature ( CurrentCENode , AddDelegateNode - > GetDelegateSignature ( ) , Schema ) ;
}
OutLastActivatedThenPin = CurrentCENode - > FindPinChecked ( UEdGraphSchema_K2 : : PN_Then ) ;
for ( const FOutputPinAndLocalVariable & OutputPair : VariableOutputs ) // CREATE CHAIN OF ASSIGMENTS
{
UEdGraphPin * PinWithData = CurrentCENode - > FindPin ( OutputPair . OutputPin - > PinName ) ;
if ( PinWithData = = nullptr )
{
continue ;
}
UK2Node_AssignmentStatement * AssignNode = CompilerContext . SpawnIntermediateNode < UK2Node_AssignmentStatement > ( CurrentNode , SourceGraph ) ;
AssignNode - > AllocateDefaultPins ( ) ;
bIsErrorFree & = Schema - > TryCreateConnection ( OutLastActivatedThenPin , AssignNode - > GetExecPin ( ) ) ;
bIsErrorFree & = Schema - > TryCreateConnection ( OutputPair . TempVar - > GetVariablePin ( ) , AssignNode - > GetVariablePin ( ) ) ;
AssignNode - > NotifyPinConnectionListChanged ( AssignNode - > GetVariablePin ( ) ) ;
bIsErrorFree & = Schema - > TryCreateConnection ( AssignNode - > GetValuePin ( ) , PinWithData ) ;
AssignNode - > NotifyPinConnectionListChanged ( AssignNode - > GetValuePin ( ) ) ;
OutLastActivatedThenPin = AssignNode - > GetThenPin ( ) ;
}
bIsErrorFree & = CompilerContext . MovePinLinksToIntermediate ( * PinForCurrentDelegateProperty , * OutLastActivatedThenPin ) . CanSafeConnect ( ) ;
return bIsErrorFree ;
}
2022-03-29 08:43:59 -04:00
}
void UK2Node_WebAPIOperation : : AllocateDefaultPins ( )
{
2022-05-29 21:30:22 -04:00
InvalidatePinTooltips ( ) ;
const UEdGraphSchema_K2 * K2Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
// Exec input/output
if ( ! GetExecPin ( ) )
{
CreatePin ( EGPD_Input , UEdGraphSchema_K2 : : PC_Exec , UEdGraphSchema_K2 : : PN_Execute ) ;
}
if ( ! GetThenPin ( ) )
{
CreatePin ( EGPD_Output , UEdGraphSchema_K2 : : PC_Exec , UEdGraphSchema_K2 : : PN_Then ) ;
}
const UFunction * Function = GetFactoryFunction ( ) ;
TArray < FMulticastDelegateProperty * , TFixedAllocator < 2 > > DelegateProperties = {
UE : : WebAPI : : Operation : : GetPositiveOutcomeDelegate ( OperationClass ) ,
UE : : WebAPI : : Operation : : GetNegativeOutcomeDelegate ( OperationClass )
} ;
if ( OperationAsyncType = = EWebAPIOperationAsyncType : : LatentAction )
{
// Exec outputs for both outcomes
for ( const FMulticastDelegateProperty * DelegateProperty : DelegateProperties )
{
UEdGraphPin * ExecPin = CreatePin ( EGPD_Output , UEdGraphSchema_K2 : : PC_Exec , DelegateProperty - > GetFName ( ) ) ;
ExecPin - > PinToolTip = DelegateProperty - > GetToolTipText ( ) . ToString ( ) ;
ExecPin - > PinFriendlyName = DelegateProperty - > GetDisplayNameText ( ) ;
}
// Response pins
const UFunction * DelegateSignatureFunction = UE : : WebAPI : : Operation : : GetOutcomeDelegateSignatureFunction ( OperationClass ) ;
if ( DelegateSignatureFunction )
{
for ( TFieldIterator < FProperty > ParameterIterator ( DelegateSignatureFunction ) ; ParameterIterator & & ( ParameterIterator - > PropertyFlags & CPF_Parm ) ; + + ParameterIterator )
{
const FProperty * Parameter = * ParameterIterator ;
const bool bIsFunctionInput = ! Parameter - > HasAnyPropertyFlags ( CPF_OutParm ) | | Parameter - > HasAnyPropertyFlags ( CPF_ReferenceParm ) ;
if ( bIsFunctionInput )
{
UEdGraphPin * Pin = CreatePin ( EGPD_Output , NAME_None , Parameter - > GetFName ( ) ) ;
K2Schema - > ConvertPropertyToPinType ( Parameter , /*out*/ Pin - > PinType ) ;
// Check for a display name override
const FString & PinDisplayName = Parameter - > GetMetaData ( FBlueprintMetadata : : MD_DisplayName ) ;
if ( ! PinDisplayName . IsEmpty ( ) )
{
Pin - > PinFriendlyName = FText : : FromString ( PinDisplayName ) ;
}
// Else cleanup name
else
{
FString PinNameStr = Pin - > PinName . ToString ( ) ;
UE : : WebAPI : : Graph : : CleanupPinNameInline ( PinNameStr ) ;
Pin - > PinFriendlyName = FText : : FromString ( PinNameStr ) ;
}
UK2Node_CallFunction : : GeneratePinTooltipFromFunction ( * Pin , DelegateSignatureFunction ) ;
UE : : WebAPI : : Graph : : SplitPin ( Pin ) ;
}
}
}
}
bool bAllPinsGood = true ;
if ( Function )
{
TSet < FName > PinsToHide ;
FBlueprintEditorUtils : : GetHiddenPinsForFunction ( GetGraph ( ) , Function , PinsToHide ) ;
// Input pins
for ( TFieldIterator < FProperty > ParameterIterator ( Function ) ; ParameterIterator & & ( ParameterIterator - > PropertyFlags & CPF_Parm ) ; + + ParameterIterator )
{
const FProperty * Parameter = * ParameterIterator ;
const bool bIsFunctionInput = ! Parameter - > HasAnyPropertyFlags ( CPF_OutParm ) | | Parameter - > HasAnyPropertyFlags ( CPF_ReferenceParm ) ;
if ( ! bIsFunctionInput )
{
// skip function output, it's internal node data
continue ;
}
UEdGraphNode : : FCreatePinParams PinParams ;
PinParams . bIsReference = Parameter - > HasAnyPropertyFlags ( CPF_ReferenceParm ) & & bIsFunctionInput ;
UEdGraphPin * Pin = CreatePin ( EGPD_Input , NAME_None , Parameter - > GetFName ( ) , PinParams ) ;
const bool bPinGood = ( Pin & & K2Schema - > ConvertPropertyToPinType ( Parameter , /*out*/ Pin - > PinType ) ) ;
if ( bPinGood )
{
// Check for a display name override
const FString & PinDisplayName = Parameter - > GetMetaData ( FBlueprintMetadata : : MD_DisplayName ) ;
if ( ! PinDisplayName . IsEmpty ( ) )
{
Pin - > PinFriendlyName = FText : : FromString ( PinDisplayName ) ;
}
// Else cleanup name
else
{
FString PinNameStr = Pin - > PinName . ToString ( ) ;
UE : : WebAPI : : Graph : : CleanupPinNameInline ( PinNameStr ) ;
Pin - > PinFriendlyName = FText : : FromString ( PinNameStr ) ;
}
//Flag pin as read only for const reference property
Pin - > bDefaultValueIsIgnored = Parameter - > HasAllPropertyFlags ( CPF_ConstParm | CPF_ReferenceParm ) & & ( ! Function - > HasMetaData ( FBlueprintMetadata : : MD_AutoCreateRefTerm ) | | Pin - > PinType . IsContainer ( ) ) ;
const bool bAdvancedPin = Parameter - > HasAllPropertyFlags ( CPF_AdvancedDisplay ) ;
Pin - > bAdvancedView = bAdvancedPin ;
if ( bAdvancedPin & & ( ENodeAdvancedPins : : NoPins = = AdvancedPinDisplay ) )
{
AdvancedPinDisplay = ENodeAdvancedPins : : Hidden ;
}
FString ParameterValue ;
if ( K2Schema - > FindFunctionParameterDefaultValue ( Function , Parameter , ParameterValue ) )
{
K2Schema - > SetPinAutogeneratedDefaultValue ( Pin , ParameterValue ) ;
}
else
{
K2Schema - > SetPinAutogeneratedDefaultValueBasedOnType ( Pin ) ;
}
if ( PinsToHide . Contains ( Pin - > PinName ) )
{
Pin - > bHidden = true ;
}
}
UE : : WebAPI : : Graph : : SplitPin ( Pin ) ;
bAllPinsGood = bAllPinsGood & & bPinGood ;
}
}
2022-03-29 08:43:59 -04:00
Super : : AllocateDefaultPins ( ) ;
}
FText UK2Node_WebAPIOperation : : GetNodeTitle ( ENodeTitleType : : Type TitleType ) const
{
2022-05-29 21:30:22 -04:00
const FText FunctionName = UK2Node_CallFunction : : GetUserFacingFunctionName ( GetFactoryFunction ( ) ) ;
2022-03-29 08:43:59 -04:00
FText NamespaceName ;
FText ServiceName ;
if ( const UFunction * Function = GetFactoryFunction ( ) )
{
NamespaceName = Function - > GetOuterUClass ( ) - > GetMetaDataText ( TEXT ( " Namespace " ) ) ;
ServiceName = Function - > GetOuterUClass ( ) - > GetMetaDataText ( TEXT ( " Service " ) ) ;
}
if ( TitleType = = ENodeTitleType : : FullTitle )
{
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " FunctionName " ) , FunctionName ) ;
Args . Add ( TEXT ( " NamespaceName " ) , NamespaceName ) ;
Args . Add ( TEXT ( " ServiceName " ) , ServiceName ) ;
if ( NamespaceName . IsEmpty ( ) & & ServiceName . IsEmpty ( ) )
{
return FText : : Format ( LOCTEXT ( " NodeTitle " , " {FunctionName} " ) , Args ) ;
}
else if ( NamespaceName . IsEmpty ( ) )
{
return FText : : Format ( LOCTEXT ( " NodeTitle_WithNamespace " , " {FunctionName} \n {ServiceName} " ) , Args ) ;
}
else if ( ServiceName . IsEmpty ( ) )
{
return FText : : Format ( LOCTEXT ( " NodeTitle_WithService " , " {FunctionName} \n {NamespaceName} " ) , Args ) ;
}
else
{
return FText : : Format ( LOCTEXT ( " NodeTitle_WithNamespaceAndService " , " {FunctionName} \n {NamespaceName}: {ServiceName} " ) , Args ) ;
}
}
else
{
return FunctionName ;
}
}
2022-05-29 21:30:22 -04:00
FText UK2Node_WebAPIOperation : : GetTooltipText ( ) const
{
FText Tooltip ;
UFunction * Function = GetFactoryFunction ( ) ;
if ( Function = = nullptr )
{
return FText : : Format ( LOCTEXT ( " CallUnknownFunction " , " Call unknown function {0} " ) , FText : : FromName ( OperationAsyncType = = EWebAPIOperationAsyncType : : LatentAction ? LatentFunctionName : DelegatedFunctionName ) ) ;
}
else if ( CachedTooltip . IsOutOfDate ( this ) )
{
FText BaseTooltip = FText : : FromString ( UK2Node_CallFunction : : GetDefaultTooltipForFunction ( Function ) ) ;
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " DefaultTooltip " ) , BaseTooltip ) ;
if ( Function - > HasAllFunctionFlags ( FUNC_BlueprintAuthorityOnly ) )
{
Args . Add (
TEXT ( " ClientString " ) ,
NSLOCTEXT ( " K2Node " , " ServerFunction " , " Authority Only. This function will only execute on the server. " )
) ;
// FText::Format() is slow, so we cache this to save on performance
CachedTooltip . SetCachedText ( FText : : Format ( LOCTEXT ( " WebAPIOperation_SubtitledTooltip " , " {DefaultTooltip} \n \n {ClientString} " ) , Args ) , this ) ;
}
else if ( Function - > HasAllFunctionFlags ( FUNC_BlueprintCosmetic ) )
{
Args . Add (
TEXT ( " ClientString " ) ,
NSLOCTEXT ( " K2Node " , " ClientFunction " , " Cosmetic. This event is only for cosmetic, non-gameplay actions. " )
) ;
// FText::Format() is slow, so we cache this to save on performance
CachedTooltip . SetCachedText ( FText : : Format ( LOCTEXT ( " WebAPIOperation_SubtitledTooltip " , " {DefaultTooltip} \n \n {ClientString} " ) , Args ) , this ) ;
}
else
{
CachedTooltip . SetCachedText ( BaseTooltip , this ) ;
}
}
return CachedTooltip ;
}
void UK2Node_WebAPIOperation : : ReconstructNode ( )
{
if ( IsValid ( ) )
{
// Latent type, so remove input delegate pins
if ( OperationAsyncType = = EWebAPIOperationAsyncType : : LatentAction )
{
for ( UEdGraphPin * Pin : GetRequestDelegatePins ( ) )
{
RemovePin ( Pin ) ;
}
}
// Callback type, so remove (additional) exec pins and response pins
else
{
for ( UEdGraphPin * Pin : GetResponseExecPins ( ) )
{
RemovePin ( Pin ) ;
}
}
}
Super : : ReconstructNode ( ) ;
}
bool UK2Node_WebAPIOperation : : IsCompatibleWithGraph ( const UEdGraph * TargetGraph ) const
{
return UK2Node : : IsCompatibleWithGraph ( TargetGraph ) ;
}
2022-03-29 08:43:59 -04:00
void UK2Node_WebAPIOperation : : GetNodeContextMenuActions ( UToolMenu * Menu , UGraphNodeContextMenuContext * Context ) const
{
Super : : GetNodeContextMenuActions ( Menu , Context ) ;
if ( ! Context - > bIsDebugging )
{
2022-05-29 21:30:22 -04:00
// No conversion options if in a function graph (must always be callbacks!)
if ( GetSchema ( ) - > GetGraphType ( GetGraph ( ) ) ! = EGraphType : : GT_Function )
2022-03-29 08:43:59 -04:00
{
FText MenuEntryTitle = LOCTEXT ( " MakeCallbackTitle " , " Convert to Callback responses " ) ;
FText MenuEntryTooltip = LOCTEXT ( " MakeCallbackTooltip " , " Removes the execution pins and instead adds callbacks. " ) ;
bool bCanToggleAsyncType = true ;
auto CanExecuteAsyncTypeToggle = [ ] ( const bool bInCanToggleAsyncType ) - > bool
{
return bInCanToggleAsyncType ;
} ;
if ( OperationAsyncType = = EWebAPIOperationAsyncType : : Callback )
{
MenuEntryTitle = LOCTEXT ( " MakeLatentActionTitle " , " Convert to Latent Action " ) ;
MenuEntryTooltip = LOCTEXT ( " MakeLatentActionTooltip " , " Adds in branching execution pins so that you can separatly handle the responses. " ) ;
const UEdGraphSchema_K2 * K2Schema = Cast < UEdGraphSchema_K2 > ( GetSchema ( ) ) ;
check ( K2Schema ! = nullptr ) ;
2022-05-29 21:30:22 -04:00
bCanToggleAsyncType = K2Schema - > DoesGraphSupportImpureFunctions ( GetGraph ( ) ) ;
2022-03-29 08:43:59 -04:00
if ( ! bCanToggleAsyncType )
{
MenuEntryTooltip = LOCTEXT ( " CannotMakeLatentActionTooltip " , " This graph does not support latent actions. " ) ;
}
}
FToolMenuSection & Section = Menu - > AddSection ( " K2NodeWebAPIOperation " , LOCTEXT ( " AsyncTypeHeader " , " Async Type " ) ) ;
Section . AddMenuEntry (
" ToggleAsyncType " ,
MenuEntryTitle ,
MenuEntryTooltip ,
FSlateIcon ( ) ,
FUIAction (
FExecuteAction : : CreateUObject ( const_cast < UK2Node_WebAPIOperation * > ( this ) , & UK2Node_WebAPIOperation : : ToggleAsyncType ) ,
FCanExecuteAction : : CreateStatic ( CanExecuteAsyncTypeToggle , bCanToggleAsyncType ) ,
FIsActionChecked ( )
)
) ;
}
}
}
bool UK2Node_WebAPIOperation : : CanPasteHere ( const UEdGraph * TargetGraph ) const
{
return Super : : CanPasteHere ( TargetGraph ) ;
}
2022-05-29 21:30:22 -04:00
void UK2Node_WebAPIOperation : : ExpandNode (
FKismetCompilerContext & CompilerContext ,
UEdGraph * SourceGraph )
{
Super : : ExpandNode ( CompilerContext , SourceGraph ) ;
const UEdGraphSchema_K2 * Schema = CompilerContext . GetSchema ( ) ;
check ( SourceGraph & & Schema ) ;
bool bIsErrorFree = true ;
const UFunction * FactoryFunction = GetFactoryFunction ( ) ;
if ( FactoryFunction = = nullptr )
{
const FName ProxyFunctionName =
OperationAsyncType = = EWebAPIOperationAsyncType : : LatentAction
? LatentFunctionName
: DelegatedFunctionName ;
const FText ClassName = OperationClass ? FText : : FromString ( OperationClass - > GetName ( ) ) : LOCTEXT ( " MissingClassString " , " Unknown Class " ) ;
const FString FormattedMessage = FText : : Format (
LOCTEXT ( " WebOperationErrorFmt " , " WebAPIOperation: Missing function {0} from class {1} for operation @@ " ) ,
FText : : FromString ( ProxyFunctionName . GetPlainNameString ( ) ) ,
ClassName
) . ToString ( ) ;
CompilerContext . MessageLog . Error ( * FormattedMessage , this ) ;
return ;
}
// Create a call to factory the proxy object
UK2Node_CallFunction * CallCreateOperationNode = CompilerContext . SpawnIntermediateNode < UK2Node_CallFunction > ( this , SourceGraph ) ;
CallCreateOperationNode - > SetFromFunction ( FactoryFunction ) ;
CallCreateOperationNode - > AllocateDefaultPins ( ) ;
bIsErrorFree & = CompilerContext . MovePinLinksToIntermediate ( * FindPinChecked ( UEdGraphSchema_K2 : : PN_Execute ) , * CallCreateOperationNode - > FindPinChecked ( UEdGraphSchema_K2 : : PN_Execute ) ) . CanSafeConnect ( ) ;
// Input pins
for ( UEdGraphPin * Pin : Pins )
{
if ( UE : : WebAPI : : Private : : ValidDataPin ( Pin , EGPD_Input ) )
{
UEdGraphPin * DestPin = CallCreateOperationNode - > FindPin ( Pin - > PinName ) ; // match function inputs, to pass data to function from CallFunction node
bIsErrorFree & = DestPin & & CompilerContext . MovePinLinksToIntermediate ( * Pin , * DestPin ) . CanSafeConnect ( ) ;
}
}
{
UEdGraphPin * const OperationObjectPin = CallCreateOperationNode - > GetReturnValuePin ( ) ;
check ( OperationObjectPin ) ;
static const FName AsyncTaskProxyName ( TEXT ( " AsyncTaskProxy " ) ) ;
UEdGraphPin * OutputAsyncTaskProxy = FindPin ( AsyncTaskProxyName ) ;
bIsErrorFree & = ! OutputAsyncTaskProxy | | CompilerContext . MovePinLinksToIntermediate ( * OutputAsyncTaskProxy , * OperationObjectPin ) . CanSafeConnect ( ) ;
bIsErrorFree & = ExpandDefaultToSelfPin ( CompilerContext , SourceGraph , CallCreateOperationNode ) ;
// GATHER OUTPUT PARAMETERS AND PAIR THEM WITH LOCAL VARIABLES
TArray < UE : : WebAPI : : Private : : FOutputPinAndLocalVariable > VariableOutputs ;
bool bPassedFactoryOutputs = false ;
for ( UEdGraphPin * CurrentPin : Pins )
{
if ( ( OutputAsyncTaskProxy ! = CurrentPin ) & & UE : : WebAPI : : Private : : ValidDataPin ( CurrentPin , EGPD_Output ) )
{
if ( ! bPassedFactoryOutputs )
{
UEdGraphPin * DestPin = CallCreateOperationNode - > FindPin ( CurrentPin - > PinName ) ;
bIsErrorFree & = DestPin & & CompilerContext . MovePinLinksToIntermediate ( * CurrentPin , * DestPin ) . CanSafeConnect ( ) ;
}
else
{
const FEdGraphPinType & PinType = CurrentPin - > PinType ;
UK2Node_TemporaryVariable * TempVarOutput = CompilerContext . SpawnInternalVariable (
this , PinType . PinCategory , PinType . PinSubCategory , PinType . PinSubCategoryObject . Get ( ) , PinType . ContainerType , PinType . PinValueType ) ;
bIsErrorFree & = TempVarOutput - > GetVariablePin ( ) & & CompilerContext . MovePinLinksToIntermediate ( * CurrentPin , * TempVarOutput - > GetVariablePin ( ) ) . CanSafeConnect ( ) ;
VariableOutputs . Add ( UE : : WebAPI : : Private : : FOutputPinAndLocalVariable ( CurrentPin , TempVarOutput ) ) ;
}
}
else if ( ! bPassedFactoryOutputs & & CurrentPin & & CurrentPin - > Direction = = EGPD_Output )
{
// the first exec that isn't the node's then pin is the start of the asyc delegate pins
// once we hit this point, we've iterated beyond all outputs for the factory function
bPassedFactoryOutputs = ( CurrentPin - > PinType . PinCategory = = UEdGraphSchema_K2 : : PC_Exec ) & & ( CurrentPin - > PinName ! = UEdGraphSchema_K2 : : PN_Then ) ;
}
}
// FOR EACH DELEGATE DEFINE EVENT, CONNECT IT TO DELEGATE AND IMPLEMENT A CHAIN OF ASSIGMENTS
UEdGraphPin * LastThenPin = CallCreateOperationNode - > FindPinChecked ( UEdGraphSchema_K2 : : PN_Then ) ;
UK2Node_CallFunction * IsValidFuncNode = CompilerContext . SpawnIntermediateNode < UK2Node_CallFunction > ( this , SourceGraph ) ;
const FName IsValidFuncName = GET_FUNCTION_NAME_CHECKED ( UKismetSystemLibrary , IsValid ) ;
IsValidFuncNode - > FunctionReference . SetExternalMember ( IsValidFuncName , UKismetSystemLibrary : : StaticClass ( ) ) ;
IsValidFuncNode - > AllocateDefaultPins ( ) ;
UEdGraphPin * IsValidInputPin = IsValidFuncNode - > FindPinChecked ( TEXT ( " Object " ) ) ;
bIsErrorFree & = Schema - > TryCreateConnection ( OperationObjectPin , IsValidInputPin ) ;
UK2Node_IfThenElse * ValidateProxyNode = CompilerContext . SpawnIntermediateNode < UK2Node_IfThenElse > ( this , SourceGraph ) ;
ValidateProxyNode - > AllocateDefaultPins ( ) ;
bIsErrorFree & = Schema - > TryCreateConnection ( IsValidFuncNode - > GetReturnValuePin ( ) , ValidateProxyNode - > GetConditionPin ( ) ) ;
bIsErrorFree & = Schema - > TryCreateConnection ( LastThenPin , ValidateProxyNode - > GetExecPin ( ) ) ;
LastThenPin = ValidateProxyNode - > GetThenPin ( ) ;
if ( OperationAsyncType = = EWebAPIOperationAsyncType : : LatentAction )
{
bIsErrorFree & = HandleDelegates ( VariableOutputs , OperationObjectPin , LastThenPin , SourceGraph , CompilerContext ) ;
}
if ( CallCreateOperationNode - > FindPinChecked ( UEdGraphSchema_K2 : : PN_Then ) = = LastThenPin )
{
CompilerContext . MessageLog . Error ( * LOCTEXT ( " MissingDelegateProperties " , " WebAPIOperation: Proxy has no delegates defined. @@ " ) . ToString ( ) , this ) ;
return ;
}
// Move the connections from the original node then pin to the last internal then pin
UEdGraphPin * OriginalThenPin = FindPin ( UEdGraphSchema_K2 : : PN_Then ) ;
if ( OriginalThenPin )
{
bIsErrorFree & = CompilerContext . MovePinLinksToIntermediate ( * OriginalThenPin , * LastThenPin ) . CanSafeConnect ( ) ;
}
bIsErrorFree & = CompilerContext . CopyPinLinksToIntermediate ( * LastThenPin , * ValidateProxyNode - > GetElsePin ( ) ) . CanSafeConnect ( ) ;
}
if ( ! bIsErrorFree )
{
CompilerContext . MessageLog . Error ( * LOCTEXT ( " InternalConnectionError " , " WebAPIOperation: Internal connection error. @@ " ) . ToString ( ) , this ) ;
}
// Make sure we caught everything
BreakAllNodeLinks ( ) ;
}
FName UK2Node_WebAPIOperation : : GetCornerIcon ( ) const
{
// Only return the latent action icon if that's the current async type, otherwise empty
return OperationAsyncType = = EWebAPIOperationAsyncType : : LatentAction
? Super : : GetCornerIcon ( )
: NAME_None ;
}
2022-03-29 08:43:59 -04:00
void UK2Node_WebAPIOperation : : PostPlacedNewNode ( )
{
2022-05-29 21:30:22 -04:00
// Prevent latent function in function graph
if ( OperationAsyncType = = EWebAPIOperationAsyncType : : LatentAction & & GetSchema ( ) - > GetGraphType ( GetGraph ( ) ) = = GT_Function )
{
OperationAsyncType = EWebAPIOperationAsyncType : : Callback ;
ReconstructNode ( ) ;
}
2022-03-29 08:43:59 -04:00
Super : : PostPlacedNewNode ( ) ;
}
void UK2Node_WebAPIOperation : : PostPasteNode ( )
{
2022-05-29 21:30:22 -04:00
const bool bIsFunctionGraph = GetSchema ( ) - > GetGraphType ( GetGraph ( ) ) = = GT_Function ;
// Latent action pasted in function graph, forcibly convert to callback style
if ( OperationAsyncType = = EWebAPIOperationAsyncType : : LatentAction & & bIsFunctionGraph )
{
ToggleAsyncType ( ) ;
}
2022-03-29 08:43:59 -04:00
2022-05-29 21:30:22 -04:00
Super : : PostPasteNode ( ) ;
2022-03-29 08:43:59 -04:00
}
void UK2Node_WebAPIOperation : : GetMenuActions ( FBlueprintActionDatabaseRegistrar & ActionRegistrar ) const
{
struct GetMenuActions_Utils
{
static void SetNodeFunc ( UEdGraphNode * NewNode , bool /*bIsTemplateNode*/ , TWeakObjectPtr < UFunction > FunctionPtr )
{
2022-05-29 21:30:22 -04:00
UK2Node_WebAPIOperation * OperationNode = CastChecked < UK2Node_WebAPIOperation > ( NewNode ) ;
2022-03-29 08:43:59 -04:00
if ( FunctionPtr . IsValid ( ) )
{
const UFunction * Func = FunctionPtr . Get ( ) ;
2022-05-29 21:30:22 -04:00
FString LatentFunctionName = Func - > GetName ( ) ;
FString DelegatedFunctionName = LatentFunctionName ;
DelegatedFunctionName . RemoveFromEnd ( TEXT ( " Async " ) ) ;
OperationNode - > OperationClass = Func - > GetOuterUClass ( ) ;
OperationNode - > LatentFunctionName = FName ( LatentFunctionName ) ;
OperationNode - > DelegatedFunctionName = FName ( DelegatedFunctionName ) ;
check ( OperationNode - > CacheOutcomeDelegates ( ) ) ;
2022-03-29 08:43:59 -04:00
}
}
} ;
UClass * NodeClass = GetClass ( ) ;
2022-05-29 21:30:22 -04:00
ActionRegistrar . RegisterClassFactoryActions < UWebAPIOperationObject > (
FBlueprintActionDatabaseRegistrar : : FMakeFuncSpawnerDelegate : : CreateLambda (
[ NodeClass ] ( const UFunction * FactoryFunc ) - > UBlueprintNodeSpawner *
{
// Skip functions without "Async" suffix - these should explicitly not be shown.
// switching implementations between latent (async suffix) and callback (no suffix) is automatic.
if ( ! FactoryFunc - > GetName ( ) . EndsWith ( TEXT ( " Async " ) ) )
{
return nullptr ;
}
2022-03-29 08:43:59 -04:00
2022-05-29 21:30:22 -04:00
UBlueprintNodeSpawner * NodeSpawner = UBlueprintFunctionNodeSpawner : : Create ( FactoryFunc ) ;
check ( NodeSpawner ! = nullptr ) ;
2022-03-29 08:43:59 -04:00
2022-05-29 21:30:22 -04:00
NodeSpawner - > NodeClass = NodeClass ;
const TWeakObjectPtr < UFunction > FunctionPtr = MakeWeakObjectPtr ( const_cast < UFunction * > ( FactoryFunc ) ) ;
NodeSpawner - > CustomizeNodeDelegate = UBlueprintNodeSpawner : : FCustomizeNodeDelegate : : CreateStatic ( GetMenuActions_Utils : : SetNodeFunc , FunctionPtr ) ;
return NodeSpawner ;
} ) ) ;
2022-03-29 08:43:59 -04:00
}
void UK2Node_WebAPIOperation : : PostReconstructNode ( )
{
2022-05-29 21:30:22 -04:00
// Prevent latent function in function graph
if ( OperationAsyncType = = EWebAPIOperationAsyncType : : LatentAction & & GetSchema ( ) - > GetGraphType ( GetGraph ( ) ) = = GT_Function )
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
ToggleAsyncType ( ) ;
return ;
2022-03-29 08:43:59 -04:00
}
2022-05-29 21:30:22 -04:00
Super : : PostReconstructNode ( ) ;
InvalidatePinTooltips ( ) ;
2022-03-29 08:43:59 -04:00
}
void UK2Node_WebAPIOperation : : SetAsyncType ( EWebAPIOperationAsyncType InAsyncType )
{
if ( InAsyncType ! = OperationAsyncType )
{
OperationAsyncType = InAsyncType ;
2022-05-29 21:30:22 -04:00
// Provides opportunity to perform some actions specific to the conversion direction
if ( OperationAsyncType = = EWebAPIOperationAsyncType : : LatentAction )
{
ConvertCallbackToLatent ( ) ;
}
else
{
ConvertLatentToCallback ( ) ;
}
2022-03-29 08:43:59 -04:00
const bool bHasBeenConstructed = ( Pins . Num ( ) > 0 ) ;
if ( bHasBeenConstructed )
{
ReconstructNode ( ) ;
}
}
}
bool UK2Node_WebAPIOperation : : IsValid ( ) const
{
2022-05-29 21:30:22 -04:00
return ( LatentFunctionName ! = NAME_None | | DelegatedFunctionName ! = NAME_None )
& & OperationClass ! = nullptr
2022-03-29 08:43:59 -04:00
& & ( ( bHasCompilerMessage & & ErrorType > = EMessageSeverity : : Info ) | | ! bHasCompilerMessage ) ;
}
2022-05-29 21:30:22 -04:00
const UK2Node_CustomEvent * UK2Node_WebAPIOperation : : GetCustomEventForOutcomeDelegate (
const UK2Node_WebAPIOperation * InNode ,
const FName & InOutcomeName )
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
check ( InNode ) ;
TArray < UEdGraphPin * > DelegatePins = InNode - > GetRequestDelegatePins ( ) ;
if ( UEdGraphPin * * OutcomeDelegatePin = DelegatePins . FindByPredicate (
[ InOutcomeName ] ( const UEdGraphPin * InPin )
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
return InPin - > PinName . ToString ( ) . Contains ( InOutcomeName . ToString ( ) ) ;
} ) )
{
// Found pin, now get connected event
for ( const UEdGraphPin * LinkedPin : ( * OutcomeDelegatePin ) - > LinkedTo )
{
if ( LinkedPin - > GetOwningNode ( ) - > IsA < UK2Node_CustomEvent > ( ) )
{
return Cast < UK2Node_CustomEvent > ( LinkedPin - > GetOwningNode ( ) ) ;
}
}
}
return nullptr ;
2022-03-29 08:43:59 -04:00
}
2022-05-29 21:30:22 -04:00
UFunction * UK2Node_WebAPIOperation : : GetFactoryFunction ( ) const
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
if ( OperationClass = = nullptr )
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
UE_LOG ( LogWebAPIBlueprintGraph , Error , TEXT ( " OperationClass null in %s. Was a class deleted or saved on a non promoted build? " ) , * GetFullName ( ) ) ;
return nullptr ;
}
const FName ProxyFunctionName =
OperationAsyncType = = EWebAPIOperationAsyncType : : LatentAction
? LatentFunctionName
: DelegatedFunctionName ;
FMemberReference FunctionReference ;
FunctionReference . SetExternalMember ( ProxyFunctionName , OperationClass ) ;
UFunction * FactoryFunction = FunctionReference . ResolveMember < UFunction > ( GetBlueprint ( ) ) ;
if ( FactoryFunction = = nullptr )
{
FactoryFunction = OperationClass - > FindFunctionByName ( ProxyFunctionName ) ;
UE_CLOG ( FactoryFunction = = nullptr , LogWebAPIBlueprintGraph , Error , TEXT ( " FactoryFunction %s null in %s. Was a class deleted or saved on a non promoted build? " ) , * ProxyFunctionName . ToString ( ) , * GetFullName ( ) ) ;
}
return FactoryFunction ;
}
void UK2Node_WebAPIOperation : : GetRedirectPinNames (
const UEdGraphPin & Pin ,
TArray < FString > & RedirectPinNames ) const
{
Super : : GetRedirectPinNames ( Pin , RedirectPinNames ) ;
}
bool UK2Node_WebAPIOperation : : ExpandDefaultToSelfPin (
FKismetCompilerContext & InCompilerContext ,
UEdGraph * InSourceGraph ,
UK2Node_CallFunction * InIntermediateProxyNode )
{
if ( InSourceGraph & & InIntermediateProxyNode )
{
// Connect a self reference pin if there is a TScriptInterface default to self
if ( const UFunction * TargetFunc = InIntermediateProxyNode - > GetTargetFunction ( ) )
{
const FString & MetaData = TargetFunc - > GetMetaData ( FBlueprintMetadata : : MD_DefaultToSelf ) ;
if ( ! MetaData . IsEmpty ( ) )
{
// Find the default to self value pin
if ( UEdGraphPin * DefaultToSelfPin = InIntermediateProxyNode - > FindPinChecked ( MetaData , EGPD_Input ) )
{
// If it has no links then spawn a new self node here
if ( DefaultToSelfPin - > PinType . PinCategory = = UEdGraphSchema_K2 : : PC_Interface & & DefaultToSelfPin - > LinkedTo . Num ( ) = = 0 )
{
const UEdGraphSchema_K2 * Schema = InCompilerContext . GetSchema ( ) ;
UK2Node_Self * SelfNode = InCompilerContext . SpawnIntermediateNode < UK2Node_Self > ( this , InSourceGraph ) ;
SelfNode - > AllocateDefaultPins ( ) ;
UEdGraphPin * SelfPin = SelfNode - > FindPinChecked ( UEdGraphSchema_K2 : : PSC_Self ) ;
// Make a connection from this intermediate self pin to here
return Schema - > TryCreateConnection ( DefaultToSelfPin , SelfPin ) ;
}
}
}
}
}
return true ;
}
bool UK2Node_WebAPIOperation : : HandleDelegates (
const TArray < UE : : WebAPI : : Private : : FOutputPinAndLocalVariable > & VariableOutputs ,
UEdGraphPin * ProxyObjectPin ,
UEdGraphPin * & InOutLastThenPin ,
UEdGraph * SourceGraph ,
FKismetCompilerContext & CompilerContext )
{
bool bIsErrorFree = true ;
for ( TFieldIterator < FMulticastDelegateProperty > PropertyIt ( OperationClass ) ; PropertyIt & & bIsErrorFree ; + + PropertyIt )
{
UEdGraphPin * LastActivatedThenPin = nullptr ;
bIsErrorFree & = UE : : WebAPI : : Private : : HandleDelegateImplementation ( * PropertyIt , VariableOutputs , ProxyObjectPin , InOutLastThenPin , LastActivatedThenPin , this , SourceGraph , CompilerContext ) ;
}
return bIsErrorFree ;
}
bool UK2Node_WebAPIOperation : : CacheOutcomeDelegates ( )
{
// If either invalid
if ( ! PositiveDelegateProperty . IsValid ( ) | | ! NegativeDelegateProperty . IsValid ( ) )
{
check ( OperationClass ) ;
static FString PositiveOutcomeNameStr = UE : : WebAPI : : Operation : : PositiveOutcomeName . ToString ( ) ;
static FString NegativeOutcomeNameStr = UE : : WebAPI : : Operation : : NegativeOutcomeName . ToString ( ) ;
for ( TFieldIterator < FMulticastDelegateProperty > PropertyIt ( OperationClass ) ; PropertyIt ; + + PropertyIt )
{
if ( ( * PropertyIt ) - > GetName ( ) . Contains ( PositiveOutcomeNameStr ) )
{
PositiveDelegateProperty = * PropertyIt ;
}
else if ( ( * PropertyIt ) - > GetName ( ) . Contains ( NegativeOutcomeNameStr ) )
{
NegativeDelegateProperty = * PropertyIt ;
}
}
}
check ( PositiveDelegateProperty . IsValid ( ) ) ;
check ( NegativeDelegateProperty . IsValid ( ) ) ;
return true ;
}
void UK2Node_WebAPIOperation : : InvalidatePinTooltips ( )
{
bPinTooltipsValid = false ;
}
UEdGraphPin * UK2Node_WebAPIOperation : : FindPin (
const FName & InName ,
const EEdGraphPinDirection & InDirection ,
const FName & InCategory ,
bool bFindPartial ) const
{
return UE : : WebAPI : : Graph : : FindPin ( this , InName , InDirection , InCategory , bFindPartial ) ;
}
TArray < UEdGraphPin * > UK2Node_WebAPIOperation : : FindPins (
const FString & InName ,
const EEdGraphPinDirection & InDirection ,
bool bOnlySplitPins ) const
{
return UE : : WebAPI : : Graph : : FindPins ( this , InName , InDirection , bOnlySplitPins ) ;
2022-03-29 08:43:59 -04:00
}
TArray < UEdGraphPin * > UK2Node_WebAPIOperation : : GetRequestPins ( ) const
{
2022-05-29 21:30:22 -04:00
TArray < UEdGraphPin * > RequestPins = FindPins ( TEXT ( " Request " ) , EGPD_Input ) ;
2022-03-29 08:43:59 -04:00
ensureMsgf ( ! RequestPins . IsEmpty ( ) , TEXT ( " The function must contain a parameter with \" Request \" in the name. " ) ) ;
return RequestPins ;
}
2022-05-29 21:30:22 -04:00
TArray < UEdGraphPin * > UK2Node_WebAPIOperation : : GetRequestDelegatePins ( ) const
{
return Pins . FilterByPredicate ( [ ] ( const UEdGraphPin * InPin )
{
return InPin - > Direction = = EGPD_Input
& & InPin - > PinType . PinCategory = = UEdGraphSchema_K2 : : PC_Delegate ;
} ) ;
}
2022-03-29 08:43:59 -04:00
TArray < UEdGraphPin * > UK2Node_WebAPIOperation : : GetResponsePins ( ) const
{
2022-05-29 21:30:22 -04:00
return UE : : WebAPI : : Graph : : GetResponsePins ( this ) ;
}
UEdGraphPin * UK2Node_WebAPIOperation : : GetThenPin ( ) const
{
return FindPin ( UEdGraphSchema_K2 : : PN_Then , EEdGraphPinDirection : : EGPD_Output , UEdGraphSchema_K2 : : PC_Exec ) ;
}
TArray < UEdGraphPin * > UK2Node_WebAPIOperation : : GetResponseExecPins ( ) const
{
return Pins . FilterByPredicate ( [ ] ( const UEdGraphPin * InPin )
{
return InPin - > Direction = = EGPD_Output
& & InPin - > PinName . ToString ( ) . StartsWith ( TEXT ( " On " ) )
& & InPin - > PinType . PinCategory = = UEdGraphSchema_K2 : : PC_Exec ;
} ) ;
2022-03-29 08:43:59 -04:00
}
TArray < UEdGraphPin * > UK2Node_WebAPIOperation : : GetErrorResponsePins ( ) const
2022-05-29 21:30:22 -04:00
{
return UE : : WebAPI : : Graph : : GetErrorResponsePins ( this ) ;
2022-03-29 08:43:59 -04:00
}
2022-05-29 21:30:22 -04:00
UK2Node_CustomEvent * UK2Node_WebAPIOperation : : MakeCustomEvent (
const FName & InName ,
const UFunction * InSignature ,
const FVector2D & InPosition )
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
if ( ! GetBlueprint ( ) | | ! GetBlueprint ( ) - > SupportsEventGraphs ( ) )
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
return nullptr ;
2022-03-29 08:43:59 -04:00
}
2022-05-29 21:30:22 -04:00
UEdGraph * Graph = GetGraph ( ) ;
if ( GetSchema ( ) - > GetGraphType ( GetGraph ( ) ) ! = EGraphType : : GT_Ubergraph )
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
Graph = GetBlueprint ( ) - > GetLastEditedUberGraph ( ) ;
if ( ! Graph )
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
FKismetEditorUtilities : : CreateDefaultEventGraphs ( GetBlueprint ( ) ) ;
// Something went wrong, return nullptr...
if ( GetBlueprint ( ) - > UbergraphPages . IsEmpty ( ) )
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
return nullptr ;
2022-03-29 08:43:59 -04:00
}
2022-05-29 21:30:22 -04:00
// ... otherwise retry after adding event graph
else
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
return MakeCustomEvent ( InName , InSignature , InPosition ) ;
2022-03-29 08:43:59 -04:00
}
}
}
2022-05-29 21:30:22 -04:00
const FString FunctionName = FString : : Printf ( TEXT ( " %s_Event " ) , * InName . ToString ( ) ) ;
UK2Node_CustomEvent * CustomEventNode = UK2Node_CustomEvent : : CreateFromFunction ( InPosition , Graph , FunctionName , InSignature , false ) ;
UE : : WebAPI : : Graph : : SplitPins ( UE : : WebAPI : : Graph : : GetResponsePins ( CustomEventNode ) ) ;
UE : : WebAPI : : Graph : : SplitPins ( UE : : WebAPI : : Graph : : GetErrorResponsePins ( CustomEventNode ) ) ;
return CustomEventNode ;
2022-03-29 08:43:59 -04:00
}
2022-05-29 21:30:22 -04:00
/** Named references to Pins. */
struct FLatentActionPinMap
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
public :
explicit FLatentActionPinMap ( const UK2Node_WebAPIOperation * InNode )
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
check ( InNode ) ;
ExecIn = InNode - > GetExecPin ( ) ;
RequestParameters = InNode - > GetRequestPins ( ) ;
ExecOut = InNode - > GetThenPin ( ) ;
ExecPositive = InNode - > FindPin ( UE : : WebAPI : : Operation : : PositiveOutcomeName , EEdGraphPinDirection : : EGPD_Output , UEdGraphSchema_K2 : : PC_Exec , true ) ;
ExecNegative = InNode - > FindPin ( UE : : WebAPI : : Operation : : NegativeOutcomeName , EEdGraphPinDirection : : EGPD_Output , UEdGraphSchema_K2 : : PC_Exec , true ) ;
Responses = InNode - > GetResponsePins ( ) ;
Responses . Append ( InNode - > GetErrorResponsePins ( ) ) ;
}
// Inputs
UEdGraphPin * ExecIn ;
// (Non-split pins)
TArray < UEdGraphPin * > RequestParameters ;
// Outputs
UEdGraphPin * ExecOut ;
UEdGraphPin * ExecPositive ;
UEdGraphPin * ExecNegative ;
// (Non-split pins)
TArray < UEdGraphPin * > Responses ;
UEdGraphPin * GetOutcomeExec ( int32 InIndex ) const
{
return InIndex = = 0 ? ExecPositive : ExecNegative ;
2022-03-29 08:43:59 -04:00
}
2022-05-29 21:30:22 -04:00
bool IsValid ( ) const
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
return ExecIn
& & ( RequestParameters . IsEmpty ( ) ? true : Algo : : AllOf ( RequestParameters ) )
& & ExecOut
& & ExecPositive
& & ExecNegative
& & ( Responses . IsEmpty ( ) ? true : Algo : : AllOf ( Responses ) ) ;
}
} ;
/** Named references to Pins for a single custom event. */
struct FOutcomeEventPinMap
{
public :
FOutcomeEventPinMap ( ) = default ;
explicit FOutcomeEventPinMap ( FName InName , const UK2Node_CustomEvent * InNode , const UFunction * InSignature )
{
check ( InNode ) ;
check ( InSignature ) ;
OutcomeName = InName ;
ExecOut = UE : : WebAPI : : Graph : : FindPin ( InNode , UEdGraphSchema_K2 : : PN_Then , EEdGraphPinDirection : : EGPD_Output , UEdGraphSchema_K2 : : PC_Exec ) ;
DelegatePin = InNode - > FindPinChecked ( InNode - > DelegateOutputName ) ;
Responses = UE : : WebAPI : : Graph : : GetResponsePins ( InNode ) ;
Responses . Append ( UE : : WebAPI : : Graph : : GetErrorResponsePins ( InNode ) ) ;
}
FName OutcomeName = NAME_None ;
// Outputs
UEdGraphPin * ExecOut = nullptr ;
UEdGraphPin * DelegatePin = nullptr ;
// (Non-split pins)
TArray < UEdGraphPin * > Responses = { } ;
bool IsValid ( ) const
{
return ExecOut
& & DelegatePin
& & ( Responses . IsEmpty ( ) ? true : Algo : : AllOf ( Responses ) ) ;
}
} ;
/** Named references to Pins. */
struct FCallbackActionPinMap
{
public :
/** This overload gets the event nodes from the connected delegate pins. */
explicit FCallbackActionPinMap ( const UK2Node_WebAPIOperation * InNode )
: FCallbackActionPinMap ( InNode
, UK2Node_WebAPIOperation : : GetCustomEventForOutcomeDelegate ( InNode , UE : : WebAPI : : Operation : : PositiveOutcomeName )
, UK2Node_WebAPIOperation : : GetCustomEventForOutcomeDelegate ( InNode , UE : : WebAPI : : Operation : : NegativeOutcomeName ) )
{
}
explicit FCallbackActionPinMap (
const UK2Node_WebAPIOperation * InNode ,
const UK2Node_CustomEvent * InPositiveEvent ,
const UK2Node_CustomEvent * InNegativeEvent )
{
check ( InNode ) ;
check ( InPositiveEvent ) ;
check ( InNegativeEvent ) ;
ExecIn = InNode - > GetExecPin ( ) ;
RequestParameters = InNode - > GetRequestPins ( ) ;
CallbackPositive = InNode - > FindPin ( UE : : WebAPI : : Operation : : PositiveOutcomeName , EEdGraphPinDirection : : EGPD_Input , UEdGraphSchema_K2 : : PC_Delegate , true ) ;
CallbackNegative = InNode - > FindPin ( UE : : WebAPI : : Operation : : NegativeOutcomeName , EEdGraphPinDirection : : EGPD_Input , UEdGraphSchema_K2 : : PC_Delegate , true ) ;
check ( InNode - > PositiveDelegateProperty . IsValid ( ) ) ;
DelegateProperty = InNode - > PositiveDelegateProperty . Get ( ) ; // Choose one, doesn't matter
ExecOut = InNode - > GetThenPin ( ) ;
PositiveEventNodeMap = FOutcomeEventPinMap ( UE : : WebAPI : : Operation : : PositiveOutcomeName , InPositiveEvent , DelegateProperty - > SignatureFunction ) ;
NegativeEventNodeMap = FOutcomeEventPinMap ( UE : : WebAPI : : Operation : : NegativeOutcomeName , InNegativeEvent , DelegateProperty - > SignatureFunction ) ;
}
// Inputs
UEdGraphPin * ExecIn ;
// (Non-split pins)
TArray < UEdGraphPin * > RequestParameters ;
// Outcome delegate property (positive and negative outcomes have same signature)
FMulticastDelegateProperty * DelegateProperty ;
UEdGraphPin * CallbackPositive ;
UEdGraphPin * CallbackNegative ;
// Outputs
UEdGraphPin * ExecOut ;
// Event Nodes
FOutcomeEventPinMap PositiveEventNodeMap ;
FOutcomeEventPinMap NegativeEventNodeMap ;
UEdGraphPin * GetCallback ( int32 InIndex ) const
{
return InIndex = = 0 ? CallbackPositive : CallbackNegative ;
}
const FOutcomeEventPinMap & GetEventNodeMap ( int32 InIndex ) const
{
return InIndex = = 0 ? PositiveEventNodeMap : NegativeEventNodeMap ;
}
bool IsValid ( ) const
{
return ExecIn
& & ( RequestParameters . IsEmpty ( ) ? true : Algo : : AllOf ( RequestParameters ) )
& & CallbackPositive
& & CallbackNegative
& & ExecOut ;
}
} ;
void UK2Node_WebAPIOperation : : ConvertLatentToCallback ( )
{
CacheOutcomeDelegates ( ) ;
const FLatentActionPinMap LatentActionPinMap ( this ) ;
check ( LatentActionPinMap . IsValid ( ) ) ;
const static TArray < FName , TFixedAllocator < 2 > > OutcomeNames = { UE : : WebAPI : : Operation : : PositiveOutcomeName , UE : : WebAPI : : Operation : : NegativeOutcomeName } ;
TArray < UEdGraphPin * , TFixedAllocator < 2 > > LatentActionOutcomePins = { LatentActionPinMap . ExecPositive , LatentActionPinMap . ExecNegative } ;
TArray < UK2Node_CustomEvent * , TFixedAllocator < 2 > > OutcomeEvents = { nullptr , nullptr } ;
// @note: First pass requires old pins
for ( int32 Idx = 0 ; Idx < OutcomeNames . Num ( ) ; + + Idx )
{
const UEdGraphPin * OutcomeExecPin = LatentActionOutcomePins [ Idx ] ;
FName OutcomeName = OutcomeNames [ Idx ] ;
// Make custom event
FVector2D OperationNodePosition = { static_cast < double > ( NodePosX ) , static_cast < double > ( NodePosY ) } ;
OperationNodePosition + = FVector2D ( - 200 , OutcomeName = = UE : : WebAPI : : Operation : : PositiveOutcomeName ? 200 : 400 ) ;
UK2Node_CustomEvent * CustomEvent = MakeCustomEvent ( OutcomeName , PositiveDelegateProperty - > SignatureFunction , OperationNodePosition ) ;
OutcomeEvents [ Idx ] = CustomEvent ;
check ( CustomEvent ) ;
// Wire event exec to linked
UEdGraphPin * EventExecPin = UE : : WebAPI : : Graph : : FindPin ( CustomEvent , UEdGraphSchema_K2 : : PN_Then , EEdGraphPinDirection : : EGPD_Output , UEdGraphSchema_K2 : : PC_Exec ) ;
UE : : WebAPI : : Graph : : TransferPinConnections ( OutcomeExecPin , EventExecPin ) ;
}
// Refresh
AllocateDefaultPins ( ) ;
const FCallbackActionPinMap CallbackActionPinMap ( this , OutcomeEvents [ 0 ] , OutcomeEvents [ 1 ] ) ;
check ( CallbackActionPinMap . IsValid ( ) ) ;
// @note: Second pass requires new pins
for ( int32 Idx = 0 ; Idx < OutcomeNames . Num ( ) ; + + Idx )
{
UEdGraphPin * CustomEventDelegatePin = CallbackActionPinMap . GetEventNodeMap ( Idx ) . DelegatePin ;
// Exec pin
UEdGraphPin * OperationDelegatePin = CallbackActionPinMap . GetCallback ( Idx ) ;
OperationDelegatePin - > GetSchema ( ) - > TryCreateConnection ( CustomEventDelegatePin , OperationDelegatePin ) ;
TArray < UEdGraphPin * > PinsInUse = UE : : WebAPI : : Graph : : FilterPinsByRelated ( LatentActionOutcomePins [ Idx ] , LatentActionPinMap . Responses ) ;
UE : : WebAPI : : Graph : : TransferPins ( PinsInUse , CallbackActionPinMap . GetEventNodeMap ( Idx ) . Responses ) ;
}
// Refresh
ReconstructNode ( ) ;
}
void UK2Node_WebAPIOperation : : ConvertCallbackToLatent ( )
{
CacheOutcomeDelegates ( ) ;
const static TArray < FName , TFixedAllocator < 2 > > OutcomeNames = { UE : : WebAPI : : Operation : : PositiveOutcomeName , UE : : WebAPI : : Operation : : NegativeOutcomeName } ;
// @note: overloaded ctor finds events from connected delegate pins
const FCallbackActionPinMap CallbackActionPinMap ( this ) ;
check ( CallbackActionPinMap . IsValid ( ) ) ;
// Refresh
AllocateDefaultPins ( ) ;
const FLatentActionPinMap LatentActionPinMap ( this ) ;
check ( LatentActionPinMap . IsValid ( ) ) ;
for ( int32 Idx = 0 ; Idx < OutcomeNames . Num ( ) ; + + Idx )
{
// Get custom event for this outcome (from previous callback setup)
const FOutcomeEventPinMap & CustomEventPinMap = CallbackActionPinMap . GetEventNodeMap ( Idx ) ;
// Wire custom event exec to latent exec pin
const UEdGraphPin * EventExecPin = CustomEventPinMap . ExecOut ;
UE : : WebAPI : : Graph : : TransferPinConnections ( EventExecPin , LatentActionPinMap . GetOutcomeExec ( Idx ) ) ;
// Get custom event exec pin and find relevant nodes (
TArray < UEdGraphPin * > PinsInUse = UE : : WebAPI : : Graph : : FilterPinsByRelated ( CustomEventPinMap . ExecOut , CustomEventPinMap . Responses ) ;
UE : : WebAPI : : Graph : : TransferPins ( PinsInUse , LatentActionPinMap . Responses ) ;
const bool bNothingLinkedToCustomEvent = Algo : : AllOf ( CustomEventPinMap . Responses ,
[ ] ( const UEdGraphPin * InPin )
{
return InPin - > LinkedTo . IsEmpty ( ) ;
} ) ;
// If nothing linked, delete custom event
if ( bNothingLinkedToCustomEvent )
2022-03-29 08:43:59 -04:00
{
2022-05-29 21:30:22 -04:00
UEdGraphNode * CustomEventNode = CustomEventPinMap . ExecOut - > GetOwningNode ( ) ;
CustomEventNode - > GetGraph ( ) - > RemoveNode ( CustomEventNode ) ;
2022-03-29 08:43:59 -04:00
}
}
2022-05-29 21:30:22 -04:00
// Refresh
ReconstructNode ( ) ;
2022-03-29 08:43:59 -04:00
}
void UK2Node_WebAPIOperation : : ToggleAsyncType ( )
{
const FText TransactionTitle =
OperationAsyncType = = EWebAPIOperationAsyncType : : Callback
? LOCTEXT ( " ToggleAsyncTypeToLatentAction " , " Convert to Latent Action " )
: LOCTEXT ( " ToggleAsyncTypeToCallback " , " Convert to Callback responses " ) ;
const FScopedTransaction Transaction ( TransactionTitle ) ;
Modify ( ) ;
SetAsyncType ( StaticCast < EWebAPIOperationAsyncType > ( FMath : : Abs ( StaticCast < int8 > ( OperationAsyncType ) - 1 ) ) ) ;
}
# undef LOCTEXT_NAMESPACE