void UK2Node_Message::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); UEdGraphPin* ExecPin = Schema->FindExecutionPin(*this, EGPD_Input); const bool bExecPinConnected = ExecPin && (ExecPin->LinkedTo.Num() > 0); UEdGraphPin* ThenPin = Schema->FindExecutionPin(*this, EGPD_Output); const bool bThenPinConnected = ThenPin && (ThenPin->LinkedTo.Num() > 0); // Skip ourselves if our exec isn't wired up if (bExecPinConnected) { // Make sure our interface is valid if (FunctionReference.GetMemberParentClass(this) == NULL) { CompilerContext.MessageLog.Error(*FString::Printf(*LOCTEXT("MessageNodeInvalid_Error", "Message node @@ has an invalid interface.").ToString()), this); return; } UFunction* MessageNodeFunction = GetTargetFunction(); if (MessageNodeFunction == NULL) { //@TODO: Why do this here in teh compiler, it's already done on AllocateDefaultPins() during on-load node reconstruction MessageNodeFunction = Cast<UFunction>(UK2Node::FindRemappedField(FunctionReference.GetMemberParentClass(this), FunctionReference.GetMemberName())); } if (MessageNodeFunction == NULL) { CompilerContext.MessageLog.Error(*FString::Printf(*LOCTEXT("MessageNodeInvalidFunction_Error", "Unable to find function with name %s for Message node @@.").ToString(), *(FunctionReference.GetMemberName().ToString())), this); return; } // Check to make sure we have a target UEdGraphPin* MessageSelfPin = Schema->FindSelfPin(*this, EGPD_Input); if( !MessageSelfPin || MessageSelfPin->LinkedTo.Num() == 0 ) { CompilerContext.MessageLog.Error(*FString::Printf(*LOCTEXT("MessageNodeSelfPin_Error", "Message node @@ must have a valid target or reference to self.").ToString()), this); return; } // First, create an internal cast-to-interface node UK2Node_DynamicCast* CastToInterfaceNode = CompilerContext.SpawnIntermediateNode<UK2Node_DynamicCast>(this, SourceGraph); CastToInterfaceNode->TargetType = MessageNodeFunction->GetOuterUClass(); CastToInterfaceNode->SetPurity(false); CastToInterfaceNode->AllocateDefaultPins(); UEdGraphPin* CastToInterfaceResultPin = CastToInterfaceNode->GetCastResultPin(); if( !CastToInterfaceResultPin ) { CompilerContext.MessageLog.Error(*LOCTEXT("InvalidInterfaceClass_Error", "Node @@ has an invalid target interface class").ToString(), this); return; } CastToInterfaceResultPin->PinType.PinSubCategoryObject = *CastToInterfaceNode->TargetType; if (ExecPin != nullptr) { UEdGraphPin* CastExecInput = CastToInterfaceNode->GetExecPin(); check(CastExecInput != nullptr); // Wire up the connections CompilerContext.MovePinLinksToIntermediate(*ExecPin, *CastExecInput); } UEdGraphPin* CastToInterfaceSourceObjectPin = CastToInterfaceNode->GetCastSourcePin(); CompilerContext.MovePinLinksToIntermediate(*MessageSelfPin, *CastToInterfaceSourceObjectPin); // Next, create the function call node UK2Node_CallFunction* FunctionCallNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); FunctionCallNode->bIsInterfaceCall = true; FunctionCallNode->FunctionReference = FunctionReference; FunctionCallNode->AllocateDefaultPins(); UEdGraphPin* CastToInterfaceValidPin = CastToInterfaceNode->GetValidCastPin(); check(CastToInterfaceValidPin != nullptr); UEdGraphPin* LastOutCastFaildPin = CastToInterfaceNode->GetInvalidCastPin(); check(LastOutCastFaildPin != nullptr); UEdGraphPin* LastOutCastSuccessPin = CastToInterfaceValidPin; // Wire up the connections if (UEdGraphPin* CallFunctionExecPin = Schema->FindExecutionPin(*FunctionCallNode, EGPD_Input)) { CastToInterfaceValidPin->MakeLinkTo(CallFunctionExecPin); LastOutCastSuccessPin = Schema->FindExecutionPin(*FunctionCallNode, EGPD_Output); } // Self pin UEdGraphPin* FunctionCallSelfPin = Schema->FindSelfPin(*FunctionCallNode, EGPD_Input); CastToInterfaceResultPin->MakeLinkTo(FunctionCallSelfPin); UFunction* ArrayClearFunction = UKismetArrayLibrary::StaticClass()->FindFunctionByName(FName(TEXT("Array_Clear"))); check(ArrayClearFunction); bool const bIsPureFunc = Super::IsNodePure(); // Variable pins - Try to associate variable inputs to the message node with the variable inputs and outputs to the call function node for( int32 i = 0; i < Pins.Num(); i++ ) { UEdGraphPin* CurrentPin = Pins[i]; if( CurrentPin && (CurrentPin->PinType.PinCategory != Schema->PC_Exec) && (CurrentPin->PinName != Schema->PN_Self) ) { // Try to find a match for the pin on the function call node UEdGraphPin* FunctionCallPin = FunctionCallNode->FindPin(CurrentPin->PinName); if( FunctionCallPin ) { // Move pin links if the pin is connected... CompilerContext.MovePinLinksToIntermediate(*CurrentPin, *FunctionCallPin); // when cast fails all return values must be cleared. if (EEdGraphPinDirection::EGPD_Output == CurrentPin->Direction) { UEdGraphPin* VarOutPin = FunctionCallPin; if (bIsPureFunc) { // since we cannot directly use the output from the // function call node (since it is pure, and invoking // it with a null target would cause an error), we // have to use a temporary variable in it's place... UK2Node_TemporaryVariable* TempVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph); TempVar->VariableType = CurrentPin->PinType; TempVar->AllocateDefaultPins(); VarOutPin = TempVar->GetVariablePin(); // nodes using the function's outputs directly, now // use this TempVar node instead CompilerContext.MovePinLinksToIntermediate(*FunctionCallPin, *VarOutPin); // on a successful cast, the temp var is filled with // the function's value, on a failed cast, the var // is filled with a default value (DefaultValueNode, // below)... this is the node for the success case: UK2Node_AssignmentStatement* AssignTempVar = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph); AssignTempVar->AllocateDefaultPins(); // assign the output from the pure function node to // the TempVar (either way, this message node is // returning the TempVar's value, so on a successful // cast we want it to have the function's result) UEdGraphPin* ValueInPin = AssignTempVar->GetValuePin(); Schema->TryCreateConnection(FunctionCallPin, ValueInPin); AssignTempVar->PinConnectionListChanged(ValueInPin); UEdGraphPin* VarInPin = AssignTempVar->GetVariablePin(); Schema->TryCreateConnection(VarOutPin, VarInPin); AssignTempVar->PinConnectionListChanged(VarInPin); // fold this AssignTempVar node into the cast's // success execution chain Schema->TryCreateConnection(AssignTempVar->GetExecPin(), LastOutCastSuccessPin); LastOutCastSuccessPin = AssignTempVar->GetThenPin(); } UK2Node* DefaultValueNode = NULL; UEdGraphPin* DefaultValueThenPin = NULL; if (CurrentPin->PinType.bIsArray) { UK2Node_CallArrayFunction* ClearArray = CompilerContext.SpawnIntermediateNode<UK2Node_CallArrayFunction>(this, SourceGraph); DefaultValueNode = ClearArray; ClearArray->SetFromFunction(ArrayClearFunction); ClearArray->AllocateDefaultPins(); UEdGraphPin* ArrayPin = ClearArray->GetTargetArrayPin(); check(ArrayPin); Schema->TryCreateConnection(ArrayPin, VarOutPin); ClearArray->PinConnectionListChanged(ArrayPin); DefaultValueThenPin = ClearArray->GetThenPin(); } else { UK2Node_AssignmentStatement* AssignDefaultValue = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph); DefaultValueNode = AssignDefaultValue; AssignDefaultValue->AllocateDefaultPins(); Schema->TryCreateConnection(AssignDefaultValue->GetVariablePin(), VarOutPin); AssignDefaultValue->PinConnectionListChanged(AssignDefaultValue->GetVariablePin()); Schema->SetPinDefaultValueBasedOnType(AssignDefaultValue->GetValuePin()); DefaultValueThenPin = AssignDefaultValue->GetThenPin(); } UEdGraphPin* DefaultValueExecPin = DefaultValueNode->GetExecPin(); check(DefaultValueExecPin); Schema->TryCreateConnection(DefaultValueExecPin, LastOutCastFaildPin); LastOutCastFaildPin = DefaultValueThenPin; check(LastOutCastFaildPin); } } else { UE_LOG(LogK2Compiler, Log, TEXT("%s"), *LOCTEXT("NoPinConnectionFound_Error", "Unable to find connection for pin! Check AllocateDefaultPins() for consistency!").ToString()); } } } if( bThenPinConnected ) { check(LastOutCastFaildPin != nullptr); // Failure case for the cast runs straight through to the exit CompilerContext.CopyPinLinksToIntermediate(*ThenPin, *LastOutCastFaildPin); check(LastOutCastSuccessPin != nullptr); // Copy all links from the invalid cast case above to the call function node CompilerContext.MovePinLinksToIntermediate(*ThenPin, *LastOutCastSuccessPin); } } // Break all connections to the original node, so it will be pruned BreakAllNodeLinks(); }
void UK2Node_ConvertAsset::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); UClass* TargetType = GetTargetClass(); if (TargetType && Schema && (2 == Pins.Num())) { const bool bIsAssetClass = IsAssetClassType(); //Create Convert Function auto ConvertToObjectFunc = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); const FName ConvertFunctionName = bIsAssetClass ? GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, Conv_AssetClassToClass) : GET_FUNCTION_NAME_CHECKED(UKismetSystemLibrary, Conv_AssetToObject); ConvertToObjectFunc->FunctionReference.SetExternalMember(ConvertFunctionName, UKismetSystemLibrary::StaticClass()); ConvertToObjectFunc->AllocateDefaultPins(); //Connect input to convert auto InputPin = FindPin(UK2Node_ConvertAssetImpl::InputPinName); const FString ConvertInputName = bIsAssetClass ? FString(TEXT("AssetClass")) : FString(TEXT("Asset")); auto ConvertInput = ConvertToObjectFunc->FindPin(ConvertInputName); bool bIsErrorFree = InputPin && ConvertInput && CompilerContext.MovePinLinksToIntermediate(*InputPin, *ConvertInput).CanSafeConnect(); auto ConvertOutput = ConvertToObjectFunc->GetReturnValuePin(); UEdGraphPin* InnerOutput = nullptr; if (UObject::StaticClass() != TargetType) { //Create Cast Node UK2Node_DynamicCast* CastNode = bIsAssetClass ? CompilerContext.SpawnIntermediateNode<UK2Node_ClassDynamicCast>(this, SourceGraph) : CompilerContext.SpawnIntermediateNode<UK2Node_DynamicCast>(this, SourceGraph); CastNode->SetPurity(true); CastNode->TargetType = TargetType; CastNode->AllocateDefaultPins(); // Connect Object/Class to Cast auto CastInput = CastNode->GetCastSourcePin(); bIsErrorFree &= ConvertOutput && CastInput && Schema->TryCreateConnection(ConvertOutput, CastInput); // Connect output to cast InnerOutput = CastNode->GetCastResultPin(); } else { InnerOutput = ConvertOutput; } auto OutputPin = FindPin(UK2Node_ConvertAssetImpl::OutputPinName); bIsErrorFree &= OutputPin && InnerOutput && CompilerContext.MovePinLinksToIntermediate(*OutputPin, *InnerOutput).CanSafeConnect(); if (!bIsErrorFree) { CompilerContext.MessageLog.Error(*LOCTEXT("InternalConnectionError", "K2Node_ConvertAsset: Internal connection error. @@").ToString(), this); } BreakAllNodeLinks(); } }