void UK2Node_CallArrayFunction::GetArrayPins(TArray< FArrayPropertyPinCombo >& OutArrayPinInfo ) const { OutArrayPinInfo.Empty(); UFunction* TargetFunction = GetTargetFunction(); check(TargetFunction); FString ArrayPointerMetaData = TargetFunction->GetMetaData(FBlueprintMetadata::MD_ArrayParam); TArray<FString> ArrayPinComboNames; ArrayPointerMetaData.ParseIntoArray(ArrayPinComboNames, TEXT(","), true); for(auto Iter = ArrayPinComboNames.CreateConstIterator(); Iter; ++Iter) { TArray<FString> ArrayPinNames; Iter->ParseIntoArray(ArrayPinNames, TEXT("|"), true); FArrayPropertyPinCombo ArrayInfo; ArrayInfo.ArrayPin = FindPin(ArrayPinNames[0]); if(ArrayPinNames.Num() > 1) { ArrayInfo.ArrayPropPin = FindPin(ArrayPinNames[1]); } if(ArrayInfo.ArrayPin) { OutArrayPinInfo.Add(ArrayInfo); } } }
bool UK2Node_CallArrayFunction::IsWildcardProperty(UFunction* InArrayFunction, const UProperty* InProperty) { if(InArrayFunction && InProperty) { FString ArrayPointerMetaData = InArrayFunction->GetMetaData(FBlueprintMetadata::MD_ArrayParam); TArray<FString> ArrayPinComboNames; ArrayPointerMetaData.ParseIntoArray(ArrayPinComboNames, TEXT(","), true); for(auto Iter = ArrayPinComboNames.CreateConstIterator(); Iter; ++Iter) { TArray<FString> ArrayPinNames; Iter->ParseIntoArray(ArrayPinNames, TEXT("|"), true); if(ArrayPinNames[0] == InProperty->GetName()) { return true; } } } return false; }
/** Tests to see if a pin is schema compatible with a property */ bool FKismetCompilerUtilities::IsTypeCompatibleWithProperty(UEdGraphPin* SourcePin, UProperty* Property, FCompilerResultsLog& MessageLog, const UEdGraphSchema_K2* Schema, UClass* SelfClass) { check(SourcePin != NULL); const FEdGraphPinType& Type = SourcePin->PinType; const EEdGraphPinDirection Direction = SourcePin->Direction; const FString& PinCategory = Type.PinCategory; const FString& PinSubCategory = Type.PinSubCategory; const UObject* PinSubCategoryObject = Type.PinSubCategoryObject.Get(); UProperty* TestProperty = NULL; const UFunction* OwningFunction = Cast<UFunction>(Property->GetOuter()); if( Type.bIsArray ) { // For arrays, the property we want to test against is the inner property if( UArrayProperty* ArrayProp = Cast<UArrayProperty>(Property) ) { if(OwningFunction) { // Check for the magic ArrayParm property, which always matches array types FString ArrayPointerMetaData = OwningFunction->GetMetaData(TEXT("ArrayParm")); TArray<FString> ArrayPinComboNames; ArrayPointerMetaData.ParseIntoArray(&ArrayPinComboNames, TEXT(","), true); for(auto Iter = ArrayPinComboNames.CreateConstIterator(); Iter; ++Iter) { TArray<FString> ArrayPinNames; Iter->ParseIntoArray(&ArrayPinNames, TEXT("|"), true); if( ArrayPinNames[0] == SourcePin->PinName ) { return true; } } } TestProperty = ArrayProp->Inner; } else { MessageLog.Error(*LOCTEXT("PinSpecifiedAsArray_Error", "Pin @@ is specified as an array, but does not have a valid array property.").ToString(), SourcePin); return false; } } else { // For scalars, we just take the passed in property TestProperty = Property; } // Check for the early out...if this is a type dependent parameter in an array function if ( (OwningFunction != NULL) && OwningFunction->HasMetaData(TEXT("ArrayParm")) ) { // Check to see if this param is type dependent on an array parameter const FString DependentParams = OwningFunction->GetMetaData(TEXT("ArrayTypeDependentParams")); TArray<FString> DependentParamNames; DependentParams.ParseIntoArray(&DependentParamNames, TEXT(","), true); if (DependentParamNames.Find(SourcePin->PinName) != INDEX_NONE) { //@todo: This assumes that the wildcard coersion has done its job...I'd feel better if there was some easier way of accessing the target array type return true; } } int32 NumErrorsAtStart = MessageLog.NumErrors; // First check the type bool bTypeMismatch = false; bool bSubtypeMismatch = false; FString DesiredSubType(TEXT("")); if (PinCategory == Schema->PC_Boolean) { UBoolProperty* SpecificProperty = Cast<UBoolProperty>(TestProperty); bTypeMismatch = (SpecificProperty == NULL); } else if (PinCategory == Schema->PC_Byte) { UByteProperty* SpecificProperty = Cast<UByteProperty>(TestProperty); bTypeMismatch = (SpecificProperty == NULL); } else if (PinCategory == Schema->PC_Class) { const UClass* ClassType = (PinSubCategory == Schema->PSC_Self) ? SelfClass : Cast<const UClass>(PinSubCategoryObject); if (ClassType == NULL) { MessageLog.Error(*FString::Printf(*LOCTEXT("FindClassForPin_Error", "Failed to find class for pin @@").ToString()), SourcePin); } else { const UClass* MetaClass = NULL; if (auto ClassProperty = Cast<UClassProperty>(TestProperty)) { MetaClass = ClassProperty->MetaClass; } else if (auto AssetClassProperty = Cast<UAssetClassProperty>(TestProperty)) { MetaClass = AssetClassProperty->MetaClass; } if (MetaClass != NULL) { DesiredSubType = MetaClass->GetName(); const UClass* OutputClass = (Direction == EGPD_Output) ? ClassType : MetaClass; const UClass* InputClass = (Direction == EGPD_Output) ? MetaClass : ClassType; // It matches if it's an exact match or if the output class is more derived than the input class bTypeMismatch = bSubtypeMismatch = !((OutputClass == InputClass) || (OutputClass->IsChildOf(InputClass))); } else { bTypeMismatch = true; } } } else if (PinCategory == Schema->PC_Float) { UFloatProperty* SpecificProperty = Cast<UFloatProperty>(TestProperty); bTypeMismatch = (SpecificProperty == NULL); } else if (PinCategory == Schema->PC_Int) { UIntProperty* SpecificProperty = Cast<UIntProperty>(TestProperty); bTypeMismatch = (SpecificProperty == NULL); } else if (PinCategory == Schema->PC_Name) { UNameProperty* SpecificProperty = Cast<UNameProperty>(TestProperty); bTypeMismatch = (SpecificProperty == NULL); } else if (PinCategory == Schema->PC_Delegate) { const UFunction* SignatureFunction = Cast<const UFunction>(PinSubCategoryObject); const UDelegateProperty* PropertyDelegate = Cast<const UDelegateProperty>(TestProperty); bTypeMismatch = !(SignatureFunction && PropertyDelegate && PropertyDelegate->SignatureFunction && PropertyDelegate->SignatureFunction->IsSignatureCompatibleWith(SignatureFunction)); } else if (PinCategory == Schema->PC_Object) { const UClass* ObjectType = (PinSubCategory == Schema->PSC_Self) ? SelfClass : Cast<const UClass>(PinSubCategoryObject); if (ObjectType == NULL) { MessageLog.Error(*FString::Printf(*LOCTEXT("FindClassForPin_Error", "Failed to find class for pin @@").ToString()), SourcePin); } else { UObjectPropertyBase* ObjProperty = Cast<UObjectPropertyBase>(TestProperty); if (ObjProperty != NULL && ObjProperty->PropertyClass) { DesiredSubType = ObjProperty->PropertyClass->GetName(); const UClass* OutputClass = (Direction == EGPD_Output) ? ObjectType : ObjProperty->PropertyClass; const UClass* InputClass = (Direction == EGPD_Output) ? ObjProperty->PropertyClass : ObjectType; // It matches if it's an exact match or if the output class is more derived than the input class bTypeMismatch = bSubtypeMismatch = !((OutputClass == InputClass) || (OutputClass->IsChildOf(InputClass))); } else if (UInterfaceProperty* IntefaceProperty = Cast<UInterfaceProperty>(TestProperty)) { UClass const* InterfaceClass = IntefaceProperty->InterfaceClass; if (InterfaceClass == NULL) { bTypeMismatch = true; } else { DesiredSubType = InterfaceClass->GetName(); bTypeMismatch = ObjectType->ImplementsInterface(InterfaceClass); } } else { bTypeMismatch = true; } } } else if (PinCategory == Schema->PC_String) { UStrProperty* SpecificProperty = Cast<UStrProperty>(TestProperty); bTypeMismatch = (SpecificProperty == NULL); } else if (PinCategory == Schema->PC_Text) { UTextProperty* SpecificProperty = Cast<UTextProperty>(TestProperty); bTypeMismatch = (SpecificProperty == NULL); } else if (PinCategory == Schema->PC_Struct) { const UScriptStruct* StructType = Cast<const UScriptStruct>(PinSubCategoryObject); if (StructType == NULL) { MessageLog.Error(*FString::Printf(*LOCTEXT("FindStructForPin_Error", "Failed to find struct for pin @@").ToString()), SourcePin); } else { UStructProperty* StructProperty = Cast<UStructProperty>(TestProperty); if (StructProperty != NULL) { DesiredSubType = StructProperty->Struct->GetName(); bSubtypeMismatch = bTypeMismatch = (StructType != StructProperty->Struct); } else { bTypeMismatch = true; } } } else { MessageLog.Error(*FString::Printf(*LOCTEXT("UnsupportedTypeForPin", "Unsupported type (%s) on @@").ToString(), *UEdGraphSchema_K2::TypeToString(Type)), SourcePin); } if (bTypeMismatch) { MessageLog.Error(*FString::Printf(*LOCTEXT("TypeDoesNotMatchPropertyOfType_Error", "@@ of type %s doesn't match the property %s of type %s").ToString(), *UEdGraphSchema_K2::TypeToString(Type), *Property->GetName(), *UEdGraphSchema_K2::TypeToString(Property)), SourcePin); } // Now check the direction if (Property->HasAnyPropertyFlags(CPF_Parm)) { // Parameters are directional const bool bOutParam = (Property->HasAnyPropertyFlags(CPF_OutParm | CPF_ReturnParm) && !(Property->HasAnyPropertyFlags(CPF_ReferenceParm))); if ( ((SourcePin->Direction == EGPD_Input) && bOutParam) || ((SourcePin->Direction == EGPD_Output) && !bOutParam)) { MessageLog.Error(*FString::Printf(*LOCTEXT("DirectionMismatchParameter_Error", "The direction of @@ doesn't match the direction of parameter %s").ToString(), *Property->GetName()), SourcePin); } if (Property->HasAnyPropertyFlags(CPF_ReferenceParm)) { TArray<FString> AutoEmittedTerms; Schema->GetAutoEmitTermParameters(OwningFunction, AutoEmittedTerms); const bool bIsAutoEmittedTerm = AutoEmittedTerms.Contains(SourcePin->PinName); // Make sure reference parameters are linked, except for FTransforms, which have a special node handler that adds an internal constant term if (!bIsAutoEmittedTerm && (SourcePin->LinkedTo.Num() == 0) && (!SourcePin->PinType.PinSubCategoryObject.IsValid() || SourcePin->PinType.PinSubCategoryObject.Get()->GetName() != TEXT("Transform")) ) { MessageLog.Error(*LOCTEXT("PassLiteral_Error", "Cannot pass a literal to @@. Connect a variable to it instead.").ToString(), SourcePin); } } } return NumErrorsAtStart == MessageLog.NumErrors; }