void UAnimGraphNode_PoseHandler::ValidateAnimNodeDuringCompilation(USkeleton* ForSkeleton, FCompilerResultsLog& MessageLog) { UPoseAsset* PoseAssetToCheck = GetPoseHandlerNode()->PoseAsset; UEdGraphPin* PoseAssetPin = FindPin(GET_MEMBER_NAME_STRING_CHECKED(FAnimNode_PoseHandler, PoseAsset)); if (PoseAssetPin != nullptr && PoseAssetToCheck == nullptr) { PoseAssetToCheck = Cast<UPoseAsset>(PoseAssetPin->DefaultObject); } if (PoseAssetToCheck == nullptr) { if (PoseAssetPin == nullptr || PoseAssetPin->LinkedTo.Num() == 0) { MessageLog.Error(TEXT("@@ references an unknown poseasset"), this); } } else { USkeleton* SeqSkeleton = PoseAssetToCheck->GetSkeleton(); if (SeqSkeleton && // if PoseAsset doesn't have skeleton, it might be due to PoseAsset not loaded yet, @todo: wait with anim blueprint compilation until all assets are loaded? !SeqSkeleton->IsCompatible(ForSkeleton)) { MessageLog.Error(TEXT("@@ references poseasset that uses different skeleton @@"), this, SeqSkeleton); } } Super::ValidateAnimNodeDuringCompilation(ForSkeleton, MessageLog); }
/** * Compares the properties of two UObject instances * @param OriginalObj The first UObject to compare * @param CompareObj The second UObject to compare * @param Results The results log to record errors or warnings * * @return True of the blueprints are the same, false otherwise (see the Results log for more details) */ static bool CompareObjects(UObject* OriginalObj, UObject* CompareObj, FCompilerResultsLog& Results) { bool bMatch = false; // ensure we have something sensible to compare if (OriginalObj == NULL) { Results.Error( TEXT("Original object is null") ); return false; } else if (CompareObj == NULL) { Results.Error( TEXT("Compare object is null") ); return false; } else if (OriginalObj == CompareObj) { Results.Error( TEXT("Objects to compare are the same") ); return false; } TMap<FString, FString> ObjProperties; GetObjProperties(OriginalObj, ObjProperties); TMap<FString, FString> CmpProperties; GetObjProperties(CompareObj, CmpProperties); return ComparePropertyMaps(OriginalObj->GetFName(), ObjProperties, CompareObj->GetFName(), CmpProperties, Results); }
void UK2Node_AddComponent::ValidateNodeDuringCompilation(FCompilerResultsLog& MessageLog) const { Super::ValidateNodeDuringCompilation(MessageLog); UActorComponent* Template = GetTemplateFromNode(); if (Template) { UClass* TemplateClass = Template->GetClass(); if (!TemplateClass->IsChildOf(UActorComponent::StaticClass()) || TemplateClass->HasAnyClassFlags(CLASS_Abstract) || !TemplateClass->HasMetaData(FBlueprintMetadata::MD_BlueprintSpawnableComponent) ) { FFormatNamedArguments Args; Args.Add(TEXT("TemplateClass"), FText::FromString(TemplateClass->GetName())); Args.Add(TEXT("NodeTitle"), GetNodeTitle(ENodeTitleType::FullTitle)); MessageLog.Error(*FText::Format(NSLOCTEXT("KismetCompiler", "InvalidComponentTemplate_Error", "Invalid class '{TemplateClass}' used as template by '{NodeTitle}' for @@"), Args).ToString(), this); } if (UChildActorComponent const* ChildActorComponent = Cast<UChildActorComponent const>(Template)) { UBlueprint const* Blueprint = GetBlueprint(); UClass const* ChildActorClass = ChildActorComponent->GetChildActorClass(); if (ChildActorClass == Blueprint->GeneratedClass) { UEdGraph const* ParentGraph = GetGraph(); UEdGraphSchema_K2 const* K2Schema = GetDefault<UEdGraphSchema_K2>(); if (K2Schema->IsConstructionScript(ParentGraph)) { FFormatNamedArguments Args; Args.Add(TEXT("ChildActorClass"), FText::FromString(ChildActorClass->GetName())); MessageLog.Error(*FText::Format(NSLOCTEXT("KismetCompiler", "AddSelfComponent_Error", "@@ cannot add a '{ChildActorClass}' component in the construction script (could cause infinite recursion)."), Args).ToString(), this); } } else if (ChildActorClass != nullptr) { AActor const* ChildActor = Cast<AActor>(ChildActorClass->ClassDefaultObject); check(ChildActor != nullptr); USceneComponent* RootComponent = ChildActor->GetRootComponent(); if ((RootComponent != nullptr) && (RootComponent->Mobility == EComponentMobility::Static) && (ChildActorComponent->Mobility != EComponentMobility::Static)) { FFormatNamedArguments Args; Args.Add(TEXT("ChildActorClass"), FText::FromString(ChildActorClass->GetName())); MessageLog.Error(*FText::Format(NSLOCTEXT("KismetCompiler", "AddStaticChildActorComponent_Error", "@@ cannot add a '{ChildActorClass}' component as it has static mobility, and the ChildActorComponent does not."), Args).ToString(), this); } } } } else { FFormatNamedArguments Args; Args.Add(TEXT("NodeTitle"), GetNodeTitle(ENodeTitleType::FullTitle)); MessageLog.Error(*FText::Format(NSLOCTEXT("KismetCompiler", "MissingComponentTemplate_Error", "Unknown template referenced by '{NodeTitle}' for @@"), Args).ToString(), this); } }
static void CreateVariables(UBlueprintGeneratedStruct* Struct, TArray<FBPVariableDescription>& Fields, const class UEdGraphSchema_K2* Schema, FCompilerResultsLog& MessageLog) { check(Struct && Schema); //FKismetCompilerUtilities::LinkAddedProperty push property to begin, so we revert the order for(int32 VarDescIdx = Fields.Num() - 1; VarDescIdx >= 0; --VarDescIdx) { FBPVariableDescription& VarDesc = Fields[VarDescIdx]; VarDesc.bInvalidMember = true; FString ErrorMsg; if(!FStructureEditorUtils::CanHaveAMemberVariableOfType(Struct, VarDesc.VarType, &ErrorMsg)) { MessageLog.Error(*FString::Printf(*LOCTEXT("StructureGeneric_Error", "Structure: %s Error: %s").ToString(), *Struct->GetPathName(), *ErrorMsg)); continue; } UProperty* NewProperty = FKismetCompilerUtilities::CreatePropertyOnScope(Struct, VarDesc.VarName, VarDesc.VarType, NULL, 0, Schema, MessageLog); if (NewProperty != NULL) { FKismetCompilerUtilities::LinkAddedProperty(Struct, NewProperty); } else { MessageLog.Error(*FString::Printf(*LOCTEXT("VariableInvalidType_Error", "The variable %s declared in %s has an invalid type %s").ToString(), *VarDesc.VarName.ToString(), *Struct->GetName(), *UEdGraphSchema_K2::TypeToString(VarDesc.VarType))); continue; } NewProperty->SetPropertyFlags(VarDesc.PropertyFlags); NewProperty->SetMetaData(TEXT("FriendlyName"), *VarDesc.FriendlyName); NewProperty->SetMetaData(TEXT("DisplayName"), *VarDesc.FriendlyName); NewProperty->SetMetaData(TEXT("Category"), *VarDesc.Category.ToString()); NewProperty->RepNotifyFunc = VarDesc.RepNotifyFunc; if (!VarDesc.DefaultValue.IsEmpty()) { NewProperty->SetMetaData(TEXT("MakeStructureDefaultValue"), *VarDesc.DefaultValue); } // Set metadata on property for (const FBPVariableMetaDataEntry& Entry : VarDesc.MetaDataArray) { NewProperty->SetMetaData(Entry.DataKey, *Entry.DataValue); } VarDesc.bInvalidMember = false; } }
void UK2Node_ForEachElementInEnum::ValidateNodeDuringCompilation(FCompilerResultsLog& MessageLog) const { if (!Enum) { MessageLog.Error(*NSLOCTEXT("K2Node", "ForEachElementInEnum_NoEnumError", "No Enum in @@").ToString(), this); } }
static void LogError(UUserDefinedStruct* Struct, FCompilerResultsLog& MessageLog, const FString& ErrorMsg) { MessageLog.Error(*ErrorMsg); if (Struct && Struct->ErrorMessage.IsEmpty()) { Struct->ErrorMessage = ErrorMsg; } }
/** * Compare two object property maps * @param OrigName The name of the original object being compared against * @param OrigMap The property map for the object * @param CmpName The name of the object to compare * @param CmpMap The property map for the object to compare */ static bool ComparePropertyMaps(FName OrigName, TMap<FString, FString>& OrigMap, FName CmpName, FPropertiesMap& CmpMap, FCompilerResultsLog& Results) { if (OrigMap.Num() != CmpMap.Num()) { Results.Error( *FString::Printf(TEXT("Objects have a different number of properties (%d vs %d)"), OrigMap.Num(), CmpMap.Num()) ); return false; } bool bMatch = true; for (auto PropIt = OrigMap.CreateIterator(); PropIt; ++PropIt) { FString Key = PropIt.Key(); FString Val = PropIt.Value(); const FString* CmpValue = CmpMap.Find(Key); // Value is missing if (CmpValue == NULL) { bMatch = false; Results.Error( *FString::Printf(TEXT("Property is missing in object being compared: (%s %s)"), *Key, *Val) ); break; } else if (Val != *CmpValue) { // string out object names and retest FString TmpCmp(*CmpValue); TmpCmp.ReplaceInline(*CmpName.ToString(), TEXT("")); FString TmpVal(Val); TmpVal.ReplaceInline(*OrigName.ToString(), TEXT("")); if (TmpCmp != TmpVal) { bMatch = false; Results.Error( *FString::Printf(TEXT("Object properties do not match: %s (%s vs %s)"), *Key, *Val, *(*CmpValue)) ); break; } } } return bMatch; }
void UK2Node_Variable::CheckForErrors(const UEdGraphSchema_K2* Schema, FCompilerResultsLog& MessageLog) { if(!VariableReference.IsSelfContext() && VariableReference.GetMemberParentClass(GetBlueprintClassFromNode()) != NULL) { // Check to see if we're not a self context, if we have a valid context. It may have been purged because of a dead execution chain UEdGraphPin* ContextPin = Schema->FindSelfPin(*this, EGPD_Input); if((ContextPin != NULL) && (ContextPin->LinkedTo.Num() == 0) && (ContextPin->DefaultObject == NULL)) { MessageLog.Error(*LOCTEXT("VarNodeError_InvalidVarTarget", "Variable node @@ uses an invalid target. It may depend on a node that is not connected to the execution chain, and got purged.").ToString(), this); } } }
void UK2Node_SetFieldsInStruct::ValidateNodeDuringCompilation(FCompilerResultsLog& MessageLog) const { Super::ValidateNodeDuringCompilation(MessageLog); if (UEdGraphPin* FoundPin = FindPinChecked(SetFieldsInStructHelper::StructRefPinName())) { if (FoundPin->LinkedTo.Num() <= 0) { FText ErrorMessage = LOCTEXT("SetStructFields_NoStructRefError", "The @@ pin must be connected to the struct that you wish to set."); MessageLog.Error(*ErrorMessage.ToString(), FoundPin); } } }
// Compiles a blueprint. void FKismet2CompilerModule::CompileBlueprintInner(class UBlueprint* Blueprint, const FKismetCompilerOptions& CompileOptions, FCompilerResultsLog& Results, TArray<UObject*>* ObjLoaded) { FBlueprintIsBeingCompiledHelper BeingCompiled(Blueprint); Blueprint->CurrentMessageLog = &Results; // Early out if blueprint parent is missing if (Blueprint->ParentClass == NULL) { Results.Error(*LOCTEXT("KismetCompileError_MalformedParentClasss", "Blueprint @@ has missing or NULL parent class.").ToString(), Blueprint); } else { // Loop through all external compiler delegates attempting to compile the blueprint. bool Compiled = false; for ( IBlueprintCompiler* Compiler : Compilers ) { if ( Compiler->CanCompile(Blueprint) ) { Compiled = true; Compiler->Compile(Blueprint, CompileOptions, Results, ObjLoaded); break; } } // if no one handles it, then use the default blueprint compiler. if ( !Compiled ) { if ( UAnimBlueprint* AnimBlueprint = Cast<UAnimBlueprint>(Blueprint) ) { FAnimBlueprintCompiler Compiler(AnimBlueprint, Results, CompileOptions, ObjLoaded); Compiler.Compile(); check(Compiler.NewClass); } else { FKismetCompilerContext Compiler(Blueprint, Results, CompileOptions, ObjLoaded); Compiler.Compile(); check(Compiler.NewClass); } } } Blueprint->CurrentMessageLog = NULL; }
bool FDelegateEditorBinding::IsBindingValid(UClass* BlueprintGeneratedClass, UWidgetBlueprint* Blueprint, FCompilerResultsLog& MessageLog) const { FDelegateRuntimeBinding RuntimeBinding = ToRuntimeBinding(Blueprint); // First find the target widget we'll be attaching the binding to. if ( UWidget* TargetWidget = Blueprint->WidgetTree->FindWidget(FName(*ObjectName)) ) { // Next find the underlying delegate we're actually binding to, if it's an event the name will be the same, // for properties we need to lookup the delegate property we're actually going to be binding to. UDelegateProperty* BindableProperty = FindField<UDelegateProperty>(TargetWidget->GetClass(), FName(*( PropertyName.ToString() + TEXT("Delegate") ))); UDelegateProperty* EventProperty = FindField<UDelegateProperty>(TargetWidget->GetClass(), PropertyName); bool bNeedsToBePure = BindableProperty ? true : false; UDelegateProperty* DelegateProperty = BindableProperty ? BindableProperty : EventProperty; // Locate the delegate property on the widget that's a delegate for a property we want to bind. if ( DelegateProperty ) { if ( !SourcePath.IsEmpty() ) { FText ValidationError; if ( SourcePath.Validate(DelegateProperty, ValidationError) == false ) { FText const ErrorFormat = LOCTEXT("BindingError", "Binding: Property '@@' on Widget '@@': %s"); MessageLog.Error(*FString::Printf(*ErrorFormat.ToString(), *ValidationError.ToString()), DelegateProperty, TargetWidget); return false; } return true; } else { // On our incoming blueprint generated class, try and find the function we claim exists that users // are binding their property too. if ( UFunction* Function = BlueprintGeneratedClass->FindFunctionByName(RuntimeBinding.FunctionName, EIncludeSuperFlag::IncludeSuper) ) { // Check the signatures to ensure these functions match. if ( Function->IsSignatureCompatibleWith(DelegateProperty->SignatureFunction, UFunction::GetDefaultIgnoredSignatureCompatibilityFlags() | CPF_ReturnParm) ) { // Only allow binding pure functions to property bindings. if ( bNeedsToBePure && !Function->HasAnyFunctionFlags(FUNC_Const | FUNC_BlueprintPure) ) { FText const ErrorFormat = LOCTEXT("BindingNotBoundToPure", "Binding: property '@@' on widget '@@' needs to be bound to a pure function, '@@' is not pure."); MessageLog.Error(*ErrorFormat.ToString(), DelegateProperty, TargetWidget, Function); return false; } return true; } else { FText const ErrorFormat = LOCTEXT("BindingFunctionSigDontMatch", "Binding: property '@@' on widget '@@' bound to function '@@', but the sigatnures don't match. The function must return the same type as the property and have no parameters."); MessageLog.Error(*ErrorFormat.ToString(), DelegateProperty, TargetWidget, Function); } } else { //TODO Bindable property removed. } } } else { // TODO Bindable Property Removed } } else { //TODO Ignore missing widgets } 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; }
void UK2Node_DynamicCast::ValidateNodeDuringCompilation(FCompilerResultsLog& MessageLog) const { Super::ValidateNodeDuringCompilation(MessageLog); UEdGraphPin* SourcePin = GetCastSourcePin(); if (SourcePin->LinkedTo.Num() > 0) { const UClass* SourceType = *TargetType; if (SourceType == nullptr) { return; } for (UEdGraphPin* CastInput : SourcePin->LinkedTo) { const FEdGraphPinType& SourcePinType = CastInput->PinType; if (SourcePinType.PinCategory != UEdGraphSchema_K2::PC_Object) { // all other types should have been rejected by IsConnectionDisallowed() continue; } UClass* SourceClass = Cast<UClass>(SourcePinType.PinSubCategoryObject.Get()); if ((SourceClass == nullptr) && (SourcePinType.PinSubCategory == UEdGraphSchema_K2::PSC_Self)) { if (UK2Node* K2Node = Cast<UK2Node>(CastInput->GetOwningNode())) { SourceClass = K2Node->GetBlueprint()->GeneratedClass; } } if (SourceClass == nullptr) { const FString SourcePinName = CastInput->PinFriendlyName.IsEmpty() ? CastInput->PinName : CastInput->PinFriendlyName.ToString(); FText const ErrorFormat = LOCTEXT("BadCastInput", "'%s' does not have a clear object type (invalid input into @@)."); MessageLog.Error( *FString::Printf(*ErrorFormat.ToString(), *SourcePinName), this ); continue; } if (SourceClass == SourceType) { const FString SourcePinName = CastInput->PinFriendlyName.IsEmpty() ? CastInput->PinName : CastInput->PinFriendlyName.ToString(); FText const WarningFormat = LOCTEXT("EqualObjectCast", "'%s' is already a '%s', you don't need @@."); MessageLog.Warning( *FString::Printf(*WarningFormat.ToString(), *SourcePinName, *TargetType->GetDisplayNameText().ToString()), this ); } else if (SourceClass->IsChildOf(SourceType)) { const FString SourcePinName = CastInput->PinFriendlyName.IsEmpty() ? CastInput->PinName : CastInput->PinFriendlyName.ToString(); FText const WarningFormat = LOCTEXT("UnneededObjectCast", "'%s' is already a '%s' (which inherits from '%s'), so you don't need @@."); MessageLog.Warning( *FString::Printf(*WarningFormat.ToString(), *SourcePinName, *SourceClass->GetDisplayNameText().ToString(), *TargetType->GetDisplayNameText().ToString()), this ); } else if (!SourceType->IsChildOf(SourceClass) && !FKismetEditorUtilities::IsClassABlueprintInterface(SourceType)) { FText const WarningFormat = LOCTEXT("DisallowedObjectCast", "'%s' does not inherit from '%s' (@@ would always fail)."); MessageLog.Warning( *FString::Printf(*WarningFormat.ToString(), *TargetType->GetDisplayNameText().ToString(), *SourceClass->GetDisplayNameText().ToString()), this ); } } } }