/** * 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 UAnimGraphNode_BoneDrivenController::ValidateAnimNodeDuringCompilation(USkeleton* ForSkeleton, FCompilerResultsLog& MessageLog) { if (ForSkeleton->GetReferenceSkeleton().FindBoneIndex(Node.SourceBone.BoneName) == INDEX_NONE) { MessageLog.Warning(*LOCTEXT("NoSourceBone", "@@ - You must pick a source bone as the Driver joint").ToString(), this); } if (Node.SourceComponent == EComponentType::None) { MessageLog.Warning(*LOCTEXT("NoSourceComponent", "@@ - You must pick a source component on the Driver joint").ToString(), this); } if (ForSkeleton->GetReferenceSkeleton().FindBoneIndex(Node.TargetBone.BoneName) == INDEX_NONE) { MessageLog.Warning(*LOCTEXT("NoTargetBone", "@@ - You must pick a target bone as the Driven joint").ToString(), this); } const bool bAffectsTranslation = Node.bAffectTargetTranslationX || Node.bAffectTargetTranslationY || Node.bAffectTargetTranslationZ; const bool bAffectsRotation = Node.bAffectTargetRotationX || Node.bAffectTargetRotationY || Node.bAffectTargetRotationZ; const bool bAffectsScale = Node.bAffectTargetScaleX || Node.bAffectTargetScaleY || Node.bAffectTargetScaleZ; if (!bAffectsTranslation && !bAffectsRotation && !bAffectsScale) { MessageLog.Warning(*LOCTEXT("NoTargetComponent", "@@ - You must pick one or more target components on the Driven joint").ToString(), this); } Super::ValidateAnimNodeDuringCompilation(ForSkeleton, MessageLog); }
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); }
void USimpleConstructionScript::ValidateNodeVariableNames(FCompilerResultsLog& MessageLog) { UBlueprint* Blueprint = GetBlueprint(); check(Blueprint); TSharedPtr<FKismetNameValidator> ParentBPNameValidator; if( Blueprint->ParentClass != NULL ) { UBlueprint* ParentBP = Cast<UBlueprint>(Blueprint->ParentClass->ClassGeneratedBy); if( ParentBP != NULL ) { ParentBPNameValidator = MakeShareable(new FKismetNameValidator(ParentBP)); } } TSharedPtr<FKismetNameValidator> CurrentBPNameValidator = MakeShareable(new FKismetNameValidator(Blueprint)); TArray<USCS_Node*> Nodes = GetAllNodes(); int32 Counter=0; for (int32 NodeIndex=0; NodeIndex < Nodes.Num(); ++NodeIndex) { USCS_Node* Node = Nodes[NodeIndex]; if( Node && Node->ComponentTemplate && Node != DefaultSceneRootNode ) { // Replace missing or invalid component variable names if( Node->VariableName == NAME_None || Node->bVariableNameAutoGenerated_DEPRECATED || !FComponentEditorUtils::IsValidVariableNameString(Node->ComponentTemplate, Node->VariableName.ToString()) ) { FName OldName = Node->VariableName; // Generate a new default variable name for the component. Node->VariableName = GenerateNewComponentName(Node->ComponentTemplate->GetClass()); Node->bVariableNameAutoGenerated_DEPRECATED = false; if( OldName != NAME_None ) { FBlueprintEditorUtils::ReplaceVariableReferences(Blueprint, OldName, Node->VariableName); MessageLog.Warning(*FString::Printf(TEXT("Found a component variable with an invalid name (%s) - changed to %s."), *OldName.ToString(), *Node->VariableName.ToString())); } } else if( ParentBPNameValidator.IsValid() && ParentBPNameValidator->IsValid(Node->VariableName) != EValidatorResult::Ok ) { FName OldName = Node->VariableName; FName NewVariableName = FBlueprintEditorUtils::FindUniqueKismetName(Blueprint, OldName.ToString()); FBlueprintEditorUtils::RenameMemberVariable(Blueprint, OldName, NewVariableName ); MessageLog.Warning(*FString::Printf(TEXT("Found a component variable with a conflicting name (%s) - changed to %s."), *OldName.ToString(), *Node->VariableName.ToString())); } } } }
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); } }
void UAnimGraphNode_ModifyBone::ValidateAnimNodeDuringCompilation(USkeleton* ForSkeleton, FCompilerResultsLog& MessageLog) { if (ForSkeleton->GetReferenceSkeleton().FindBoneIndex(Node.BoneToModify.BoneName) == INDEX_NONE) { MessageLog.Warning(*LOCTEXT("NoBoneToModify", "@@ - You must pick a bone to modify").ToString(), this); } if ((Node.TranslationMode == BMM_Ignore) && (Node.RotationMode == BMM_Ignore) && (Node.ScaleMode == BMM_Ignore)) { MessageLog.Warning(*LOCTEXT("NothingToModify", "@@ - No components to modify selected. Either Rotation, Translation, or Scale should be set to something other than Ignore").ToString(), this); } Super::ValidateAnimNodeDuringCompilation(ForSkeleton, MessageLog); }
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); } }
void FKismetCompilerUtilities::ValidateEnumProperties(UObject* DefaultObject, FCompilerResultsLog& MessageLog) { check(DefaultObject); for (TFieldIterator<UProperty> It(DefaultObject->GetClass()); It; ++It) { const UByteProperty* ByteProperty = Cast<UByteProperty>(*It); if(ByteProperty && !ByteProperty->HasAnyPropertyFlags(CPF_Transient)) { const UEnum* Enum = ByteProperty->GetIntPropertyEnum(); if(Enum) { const uint8 EnumIndex = ByteProperty->GetPropertyValue_InContainer(DefaultObject); const int32 EnumAcceptableMax = Enum->NumEnums() - 1; if(EnumIndex >= EnumAcceptableMax) { MessageLog.Warning( *FString::Printf( *LOCTEXT("InvalidEnumDefaultValue_Error", "Default Enum value '%s' for class '%s' is invalid in object '%s' ").ToString(), *ByteProperty->GetName(), *DefaultObject->GetClass()->GetName(), *DefaultObject->GetName() ) ); } } } } }
static void LogError(UUserDefinedStruct* Struct, FCompilerResultsLog& MessageLog, const FString& ErrorMsg) { MessageLog.Error(*ErrorMsg); if (Struct && Struct->ErrorMessage.IsEmpty()) { Struct->ErrorMessage = ErrorMsg; } }
void UAnimGraphNode_TwistCorrectiveNode::ValidateAnimNodeDuringCompilation(USkeleton* ForSkeleton, FCompilerResultsLog& MessageLog) { if (Node.Curve.Name == NAME_None) { MessageLog.Warning(TEXT("@@ has missing Curve Name."), this); } Super::ValidateAnimNodeDuringCompilation(ForSkeleton, MessageLog); }
/** * 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); } } }
static void CheckOutputsParametersInDelegateSignature(const UFunction* SignatureFunc, const UK2Node * DelegateNode, FCompilerResultsLog& MessageLog) { for (TFieldIterator<UProperty> PropIt(SignatureFunc); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt) { UProperty* FuncParam = *PropIt; if (FuncParam->HasAllPropertyFlags(CPF_OutParm) && !FuncParam->HasAllPropertyFlags(CPF_ConstParm)) { const bool bIsArray = FuncParam->IsA<UArrayProperty>(); // array is always passed by reference, see FKismetCompilerContext::CreatePropertiesFromList const FString MessageStr = FString::Printf( *LOCTEXT("DelegatesDontSupportRef", "Event Dispatcher: No value will be return by reference. Parameter '%s'. Node '@@'").ToString(), *FuncParam->GetName()); if (bIsArray) { MessageLog.Note(*MessageStr,DelegateNode); } else { MessageLog.Warning(*MessageStr, DelegateNode); } } } }
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; }
void FUserDefinedStructureCompilerUtils::DefaultUserDefinedStructs(UObject* Object, FCompilerResultsLog& MessageLog) { if (Object && FStructureEditorUtils::UserDefinedStructEnabled()) { for (TFieldIterator<UProperty> It(Object->GetClass()); It; ++It) { if (const UProperty* Property = (*It)) { uint8* Mem = Property->ContainerPtrToValuePtr<uint8>(Object); if (!FStructureEditorUtils::Fill_MakeStructureDefaultValue(Property, Mem)) { MessageLog.Warning(*FString::Printf(*LOCTEXT("MakeStructureDefaultValue_Error", "MakeStructureDefaultValue parsing error. Object: %s, Property: %s").ToString(), *Object->GetName(), *Property->GetName())); } } } } }
void FKismet2CompilerModule::CompileStructure(class UUserDefinedStruct* Struct, FCompilerResultsLog& Results) { Results.SetSourceName(Struct->GetName()); BP_SCOPED_COMPILER_EVENT_STAT(EKismetCompilerStats_CompileTime); FUserDefinedStructureCompilerUtils::CompileStruct(Struct, Results, true); }
// Compiles a blueprint. void FKismet2CompilerModule::CompileBlueprint(class UBlueprint* Blueprint, const FKismetCompilerOptions& CompileOptions, FCompilerResultsLog& Results, FBlueprintCompileReinstancer* ParentReinstancer, TArray<UObject*>* ObjLoaded) { SCOPE_SECONDS_COUNTER(GBlueprintCompileTime); BP_SCOPED_COMPILER_EVENT_STAT(EKismetCompilerStats_CompileTime); Results.SetSourceName(Blueprint->GetName()); const bool bIsBrandNewBP = (Blueprint->SkeletonGeneratedClass == NULL) && (Blueprint->GeneratedClass == NULL) && (Blueprint->ParentClass != NULL) && !CompileOptions.bIsDuplicationInstigated; for ( IBlueprintCompiler* Compiler : Compilers ) { Compiler->PreCompile(Blueprint); } if (CompileOptions.CompileType != EKismetCompileType::Cpp) { BP_SCOPED_COMPILER_EVENT_STAT(EKismetCompilerStats_CompileSkeletonClass); FBlueprintCompileReinstancer SkeletonReinstancer(Blueprint->SkeletonGeneratedClass); FCompilerResultsLog SkeletonResults; SkeletonResults.bSilentMode = true; FKismetCompilerOptions SkeletonCompileOptions; SkeletonCompileOptions.CompileType = EKismetCompileType::SkeletonOnly; CompileBlueprintInner(Blueprint, SkeletonCompileOptions, SkeletonResults, ObjLoaded); } // If this was a full compile, take appropriate actions depending on the success of failure of the compile if( CompileOptions.IsGeneratedClassCompileType() ) { BP_SCOPED_COMPILER_EVENT_STAT(EKismetCompilerStats_CompileGeneratedClass); // Perform the full compile CompileBlueprintInner(Blueprint, CompileOptions, Results, ObjLoaded); if (Results.NumErrors == 0) { // Blueprint is error free. Go ahead and fix up debug info Blueprint->Status = (0 == Results.NumWarnings) ? BS_UpToDate : BS_UpToDateWithWarnings; Blueprint->BlueprintSystemVersion = UBlueprint::GetCurrentBlueprintSystemVersion(); // Reapply breakpoints to the bytecode of the new class for (int32 Index = 0; Index < Blueprint->Breakpoints.Num(); ++Index) { UBreakpoint* Breakpoint = Blueprint->Breakpoints[Index]; FKismetDebugUtilities::ReapplyBreakpoint(Breakpoint); } } else { // Should never get errors from a brand new blueprint! ensure(!bIsBrandNewBP || (Results.NumErrors == 0)); // There were errors. Compile the generated class to have function stubs Blueprint->Status = BS_Error; static const FBoolConfigValueHelper ReinstanceOnlyWhenNecessary(TEXT("Kismet"), TEXT("bReinstanceOnlyWhenNecessary"), GEngineIni); // Reinstance objects here, so we can preserve their memory layouts to reinstance them again if( ParentReinstancer != NULL ) { ParentReinstancer->UpdateBytecodeReferences(); if(!Blueprint->bIsRegeneratingOnLoad) { ParentReinstancer->ReinstanceObjects(!ReinstanceOnlyWhenNecessary); } } FBlueprintCompileReinstancer StubReinstancer(Blueprint->GeneratedClass); // Toss the half-baked class and generate a stubbed out skeleton class that can be used FCompilerResultsLog StubResults; StubResults.bSilentMode = true; FKismetCompilerOptions StubCompileOptions(CompileOptions); StubCompileOptions.CompileType = EKismetCompileType::StubAfterFailure; CompileBlueprintInner(Blueprint, StubCompileOptions, StubResults, ObjLoaded); StubReinstancer.UpdateBytecodeReferences(); if( !Blueprint->bIsRegeneratingOnLoad ) { StubReinstancer.ReinstanceObjects(!ReinstanceOnlyWhenNecessary); } } } for ( IBlueprintCompiler* Compiler : Compilers ) { Compiler->PostCompile(Blueprint); } UPackage* Package = Blueprint->GetOutermost(); if( Package ) { UMetaData* MetaData = Package->GetMetaData(); MetaData->RemoveMetaDataOutsidePackage(); } }
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; }
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 ); } } } }
/** 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; }