UWorld* UUserWidget::GetWorld() const { if ( HasAllFlags(RF_ClassDefaultObject) ) { // If we are a CDO, we must return nullptr instead of calling Outer->GetWorld() to fool UObject::ImplementsGetWorld. return nullptr; } // Use the Player Context's world, if a specific player context is given, otherwise fall back to // following the outer chain. if ( PlayerContext.IsValid() ) { if ( UWorld* World = PlayerContext.GetWorld() ) { return World; } } // Could be a GameInstance, could be World, could also be a WidgetTree, so we're just going to follow // the outer chain to find the world we're in. UObject* Outer = GetOuter(); while ( Outer ) { UWorld* World = Outer->GetWorld(); if ( World ) { return World; } Outer = Outer->GetOuter(); } return nullptr; }
FExportObjectInnerContext::FExportObjectInnerContext(TArray<UObject*>& ObjsToIgnore) { // For each object . . . for ( TObjectIterator<UObject> It ; It ; ++It ) { UObject* InnerObj = *It; if ( !InnerObj->IsPendingKill() ) { if ( !ObjsToIgnore.Contains(InnerObj) ) { UObject* OuterObj = InnerObj->GetOuter(); if ( OuterObj && !OuterObj->IsPendingKill() ) { InnerList* Inners = ObjectToInnerMap.Find( OuterObj ); if ( Inners ) { // Add object to existing inner list. Inners->Add( InnerObj ); } else { // Create a new inner list for the outer object. InnerList& InnersForOuterObject = ObjectToInnerMap.Add( OuterObj, InnerList() ); InnersForOuterObject.Add( InnerObj ); } } } } } }
bool FFrontendFilter_InUseByLoadedLevels::PassesFilter(FAssetFilterType InItem) const { bool bObjectInUse = false; if ( InItem.IsAssetLoaded() ) { UObject* Asset = InItem.GetAsset(); const bool bUnreferenced = !Asset->HasAnyMarks( OBJECTMARK_TagExp ); const bool bIndirectlyReferencedObject = Asset->HasAnyMarks( OBJECTMARK_TagImp ); const bool bRejectObject = Asset->GetOuter() == NULL || // Skip objects with null outers Asset->HasAnyFlags( RF_Transient ) || // Skip transient objects (these shouldn't show up in the CB anyway) Asset->IsPendingKill() || // Objects that will be garbage collected bUnreferenced || // Unreferenced objects bIndirectlyReferencedObject; // Indirectly referenced objects if( !bRejectObject && Asset->HasAnyFlags( RF_Public ) ) { // The object is in use bObjectInUse = true; } } return bObjectInUse; }
/** * Internal version of GetPathName() that eliminates unnecessary copies. */ void UObjectBaseUtility::GetPathName( const UObject* StopOuter, FString& ResultString ) const { if( this != StopOuter && this != NULL ) { UObject* ObjOuter = GetOuter(); if (ObjOuter && ObjOuter != StopOuter ) { ObjOuter->GetPathName( StopOuter, ResultString ); // SUBOBJECT_DELIMITER is used to indicate that this object's outer is not a UPackage if (ObjOuter->GetClass() != UPackage::StaticClass() && ObjOuter->GetOuter()->GetClass() == UPackage::StaticClass()) { ResultString += SUBOBJECT_DELIMITER; } else { ResultString += TEXT("."); } } AppendName(ResultString); } else { ResultString += TEXT("None"); } }
void UEdGraphPin::PostLoad() { Super::PostLoad(); static FName GameplayTagName = TEXT("GameplayTag"); static FName GameplayTagContainerName = TEXT("GameplayTagContainer"); static FName GameplayTagsPathName = TEXT("/Script/GameplayTags"); if (PinType.PinSubCategoryObject.IsValid()) { UObject* PinSubCategoryObject = PinType.PinSubCategoryObject.Get(); if (PinSubCategoryObject->GetOuter()->GetFName() == GameplayTagsPathName) { if (PinSubCategoryObject->GetFName() == GameplayTagName) { // Pins of type FGameplayTag were storing "()" for empty arrays and then importing that into ArrayProperty and expecting an empty array. // That it was working was a bug and has been fixed, so let's fixup pins. A pin that wants an array size of 1 will always fill the parenthesis // so there is no worry about breaking those cases. if (DefaultValue == TEXT("()")) { DefaultValue.Empty(); } } else if (PinSubCategoryObject->GetFName() == GameplayTagContainerName) { // Pins of type FGameplayTagContainer were storing "GameplayTags=()" for empty arrays, which equates to having a single item, default generated as detailed above for FGameplayTag. // The solution is to replace occurances with an empty string, due to the item being a struct, we can't just empty the value and must replace only the section we need. DefaultValue.ReplaceInline(TEXT("GameplayTags=()"), TEXT("GameplayTags=")); } } } }
void UK2Node_MacroInstance::PostPasteNode() { const UBlueprint* InstanceOwner = GetBlueprint(); // Find the owner of the macro graph const UEdGraph* MacroGraph = MacroGraphReference.GetGraph(); UObject* MacroOwner = MacroGraph->GetOuter(); UBlueprint* MacroOwnerBP = NULL; while(MacroOwner) { MacroOwnerBP = Cast<UBlueprint>(MacroOwner); if(MacroOwnerBP) { break; } MacroOwner = MacroOwner->GetOuter(); } if((MacroOwnerBP != NULL) && (MacroOwnerBP->BlueprintType != BPTYPE_MacroLibrary) && (MacroOwnerBP != InstanceOwner)) { // If this is a graph from another blueprint that is NOT a library, disallow the connection! MacroGraphReference.SetGraph(NULL); } Super::PostPasteNode(); }
// Keep sync with FTypeSingletonCache::GenerateSingletonName static FString GenerateZConstructor(UField* Item) { FString Result; if (!ensure(Item)) { return Result; } for (UObject* Outer = Item; Outer; Outer = Outer->GetOuter()) { if (!Result.IsEmpty()) { Result = TEXT("_") + Result; } if (Cast<UClass>(Outer) || Cast<UScriptStruct>(Outer)) { FString OuterName = FEmitHelper::GetCppName(CastChecked<UField>(Outer), true); Result = OuterName + Result; // Structs can also have UPackage outer. if (Cast<UClass>(Outer) || Cast<UPackage>(Outer->GetOuter())) { break; } } else { Result = Outer->GetName() + Result; } } // Can't use long package names in function names. if (Result.StartsWith(TEXT("/Script/"), ESearchCase::CaseSensitive)) { Result = FPackageName::GetShortName(Result); } const FString ClassString = Item->IsA<UClass>() ? TEXT("UClass") : TEXT("UScriptStruct"); return FString(TEXT("Z_Construct_")) + ClassString + TEXT("_") + Result + TEXT("()"); }
/** * Walks up the list of outers until it finds the highest one. * * @return outermost non NULL Outer. */ UPackage* UObjectBaseUtility::GetOutermost() const { UObject* Top = (UObject*)this; for (;;) { UObject* CurrentOuter = Top->GetOuter(); if (!CurrentOuter) { return CastChecked<UPackage>(Top); } Top = CurrentOuter; } }
const UBlackboardData* FBlackboardSelectorDetails::FindBlackboardAsset(UObject* InObj) { for (UObject* TestOb = InObj; TestOb; TestOb = TestOb->GetOuter()) { UBTNode* NodeOb = Cast<UBTNode>(TestOb); if (NodeOb) { return NodeOb->GetBlackboardAsset(); } } return NULL; }
void FObjectInstancingGraph::RetrieveObjectInstances( UObject* SearchOuter, TArray<UObject*>& out_Objects ) { if ( HasDestinationRoot() && SearchOuter != NULL && (SearchOuter == DestinationRoot || SearchOuter->IsIn(DestinationRoot)) ) { for ( TMap<UObject*,UObject*>::TIterator It(SourceToDestinationMap); It; ++It ) { UObject* InstancedObject = It.Value(); if ( InstancedObject->GetOuter() == SearchOuter ) { out_Objects.AddUnique(InstancedObject); } } } }
//------------------------------------------------------------------------------ static void LinkerPlaceholderObjectImpl::BuildPropertyChain(const UProperty* LeafProperty, TArray<const UProperty*>& ChainOut) { ChainOut.Empty(); ChainOut.Add(LeafProperty); UClass* ClassOwner = LeafProperty->GetOwnerClass(); UObject* PropertyOuter = LeafProperty->GetOuter(); while ((PropertyOuter != nullptr) && (PropertyOuter != ClassOwner)) { if (const UProperty* PropertyOwner = Cast<const UProperty>(PropertyOuter)) { ChainOut.Add(PropertyOwner); } PropertyOuter = PropertyOuter->GetOuter(); } }
AActor* UActorComponent::GetOwner() const { // walk up outer chain to find an Actor UObject* Obj = GetOuter(); while(Obj != NULL) { AActor* Actor = Cast<AActor>(Obj); if(Actor != NULL) { return Actor; } Obj = Obj->GetOuter(); } return NULL; }
// Recover a corrupted blueprint void FKismet2CompilerModule::RecoverCorruptedBlueprint(class UBlueprint* Blueprint) { UPackage* Package = Blueprint->GetOutermost(); // Get rid of any stale classes for (FObjectIterator ObjIt; ObjIt; ++ObjIt) { UObject* TestObject = *ObjIt; if (TestObject->GetOuter() == Package) { // This object is in the blueprint package; is it expected? if (UClass* TestClass = Cast<UClass>(TestObject)) { if ((TestClass != Blueprint->SkeletonGeneratedClass) && (TestClass != Blueprint->GeneratedClass)) { // Unexpected UClass FKismetCompilerUtilities::ConsignToOblivion(TestClass, false); } } } } CollectGarbage( GARBAGE_COLLECTION_KEEPFLAGS ); }
void UBlueprint::DebuggingWorldRegistrationHelper(UObject* ObjectProvidingWorld, UObject* ValueToRegister) { if (ObjectProvidingWorld != NULL) { // Fix up the registration with the world UWorld* ObjWorld = NULL; UObject* ObjOuter = ObjectProvidingWorld->GetOuter(); while (ObjOuter != NULL) { ObjWorld = Cast<UWorld>(ObjOuter); if (ObjWorld != NULL) { break; } ObjOuter = ObjOuter->GetOuter(); } if (ObjWorld != NULL) { ObjWorld->NotifyOfBlueprintDebuggingAssociation(this, ValueToRegister); } } }
inline UObject* BuildSubobjectKey(UObject* InObj, TArray<FName>& OutHierarchyNames) { auto UseOuter = [](const UObject* Obj) { if (Obj == nullptr) { return false; } const bool bIsCDO = Obj->HasAllFlags(RF_ClassDefaultObject); const UObject* CDO = bIsCDO ? Obj : nullptr; const bool bIsClassCDO = (CDO != nullptr) ? (CDO->GetClass()->ClassDefaultObject == CDO) : false; if(!bIsClassCDO && CDO) { // Likely a trashed CDO, try to recover. Only known cause of this is // ambiguous use of DSOs: CDO = CDO->GetClass()->ClassDefaultObject; } const UActorComponent* AsComponent = Cast<UActorComponent>(Obj); const bool bIsDSO = Obj->HasAnyFlags(RF_DefaultSubObject); const bool bIsSCSComponent = AsComponent && AsComponent->IsCreatedByConstructionScript(); return (bIsCDO && bIsClassCDO) || bIsDSO || bIsSCSComponent; }; UObject* Outermost = nullptr; UObject* Iter = InObj; while (UseOuter(Iter)) { OutHierarchyNames.Add(Iter->GetFName()); Iter = Iter->GetOuter(); Outermost = Iter; } return Outermost; }
// Exports a set of nodes to text void FEdGraphUtilities::ExportNodesToText(TSet<UObject*> NodesToExport, /*out*/ FString& ExportedText) { // Clear the mark state for saving. UnMarkAllObjects(EObjectMark(OBJECTMARK_TagExp | OBJECTMARK_TagImp)); FStringOutputDevice Archive; const FExportObjectInnerContext Context; // Export each of the selected nodes UObject* LastOuter = NULL; for (TSet<UObject*>::TConstIterator NodeIt(NodesToExport); NodeIt; ++NodeIt) { UObject* Node = *NodeIt; // The nodes should all be from the same scope UObject* ThisOuter = Node->GetOuter(); check((LastOuter == ThisOuter) || (LastOuter == NULL)); LastOuter = ThisOuter; UExporter::ExportToOutputDevice(&Context, Node, NULL, Archive, TEXT("copy"), 0, PPF_ExportsNotFullyQualified|PPF_Copy|PPF_Delimited, false, ThisOuter); } ExportedText = Archive; }
FExportObjectInnerContext::FExportObjectInnerContext() { // For each object . . . for ( TObjectIterator<UObject> It ; It ; ++It ) { UObject* InnerObj = *It; UObject* OuterObj = InnerObj->GetOuter(); if ( OuterObj ) { InnerList* Inners = ObjectToInnerMap.Find( OuterObj ); if ( Inners ) { // Add object to existing inner list. Inners->Add( InnerObj ); } else { // Create a new inner list for the outer object. InnerList& InnersForOuterObject = ObjectToInnerMap.Add( OuterObj, InnerList() ); InnersForOuterObject.Add( InnerObj ); } } } }
/** * Parse and import text as property values for the object specified. This function should never be called directly - use ImportObjectProperties instead. * * @param ObjectStruct the struct for the data we're importing * @param DestData the location to import the property values to * @param SourceText pointer to a buffer containing the values that should be parsed and imported * @param SubobjectRoot when dealing with nested subobjects, corresponds to the top-most outer that * is not a subobject/template * @param SubobjectOuter the outer to use for creating subobjects/components. NULL when importing structdefaultproperties * @param Warn output device to use for log messages * @param Depth current nesting level * @param InstanceGraph contains the mappings of instanced objects and components to their templates * * @return NULL if the default values couldn't be imported */ static const TCHAR* ImportProperties( uint8* DestData, const TCHAR* SourceText, UStruct* ObjectStruct, UObject* SubobjectRoot, UObject* SubobjectOuter, FFeedbackContext* Warn, int32 Depth, FObjectInstancingGraph& InstanceGraph, const TMap<FName, AActor*>* ActorRemapper ) { check(!GIsUCCMakeStandaloneHeaderGenerator); check(ObjectStruct!=NULL); check(DestData!=NULL); if ( SourceText == NULL ) return NULL; // Cannot create subobjects when importing struct defaults, or if SubobjectOuter (used as the Outer for any subobject declarations encountered) is NULL bool bSubObjectsAllowed = !ObjectStruct->IsA(UScriptStruct::StaticClass()) && SubobjectOuter != NULL; // true when DestData corresponds to a subobject in a class default object bool bSubObject = false; UClass* ComponentOwnerClass = NULL; if ( bSubObjectsAllowed ) { bSubObject = SubobjectRoot != NULL && SubobjectRoot->HasAnyFlags(RF_ClassDefaultObject); if ( SubobjectRoot == NULL ) { SubobjectRoot = SubobjectOuter; } ComponentOwnerClass = SubobjectOuter != NULL ? SubobjectOuter->IsA(UClass::StaticClass()) ? CastChecked<UClass>(SubobjectOuter) : SubobjectOuter->GetClass() : NULL; } // The PortFlags to use for all ImportText calls uint32 PortFlags = PPF_Delimited | PPF_CheckReferences; if (GIsImportingT3D) { PortFlags |= PPF_AttemptNonQualifiedSearch; } FString StrLine; TArray<FDefinedProperty> DefinedProperties; // Parse all objects stored in the actor. // Build list of all text properties. bool ImportedBrush = 0; int32 LinesConsumed = 0; while (FParse::LineExtended(&SourceText, StrLine, LinesConsumed, true)) { // remove extra whitespace and optional semicolon from the end of the line { int32 Length = StrLine.Len(); while ( Length > 0 && (StrLine[Length - 1] == TCHAR(';') || StrLine[Length - 1] == TCHAR(' ') || StrLine[Length - 1] == 9) ) { Length--; } if (Length != StrLine.Len()) { StrLine = StrLine.Left(Length); } } if ( ContextSupplier != NULL ) { ContextSupplier->CurrentLine += LinesConsumed; } if (StrLine.Len() == 0) { continue; } const TCHAR* Str = *StrLine; int32 NewLineNumber; if( FParse::Value( Str, TEXT("linenumber="), NewLineNumber ) ) { if ( ContextSupplier != NULL ) { ContextSupplier->CurrentLine = NewLineNumber; } } else if( GetBEGIN(&Str,TEXT("Brush")) && ObjectStruct->IsChildOf(ABrush::StaticClass()) ) { // If SubobjectOuter is NULL, we are importing defaults for a UScriptStruct's defaultproperties block if ( !bSubObjectsAllowed ) { Warn->Logf(ELogVerbosity::Error, TEXT("BEGIN BRUSH: Subobjects are not allowed in this context")); return NULL; } // Parse brush on this line. TCHAR BrushName[NAME_SIZE]; if( FParse::Value( Str, TEXT("Name="), BrushName, NAME_SIZE ) ) { // If an initialized brush with this name already exists in the level, rename the existing one. // It is deemed to be initialized if it has a non-zero poly count. // If it is uninitialized, the existing object will have been created by a forward reference in the import text, // and it will now be redefined. This relies on the behavior that NewObject<> will return an existing pointer // if an object with the same name and outer is passed. UModel* ExistingBrush = FindObject<UModel>( SubobjectRoot, BrushName ); if (ExistingBrush && ExistingBrush->Polys && ExistingBrush->Polys->Element.Num() > 0) { ExistingBrush->Rename(); } // Create model. UModelFactory* ModelFactory = NewObject<UModelFactory>(); ModelFactory->FactoryCreateText( UModel::StaticClass(), SubobjectRoot, FName(BrushName, FNAME_Add, true), RF_NoFlags, NULL, TEXT("t3d"), SourceText, SourceText+FCString::Strlen(SourceText), Warn ); ImportedBrush = 1; } } else if (GetBEGIN(&Str, TEXT("Foliage"))) { UFoliageType* SourceFoliageType; FName ComponentName; if (SubobjectRoot && ParseObject<UFoliageType>(Str, TEXT("FoliageType="), SourceFoliageType, ANY_PACKAGE) && FParse::Value(Str, TEXT("Component="), ComponentName) ) { UPrimitiveComponent* ActorComponent = FindObjectFast<UPrimitiveComponent>(SubobjectRoot, ComponentName); if (ActorComponent && ActorComponent->GetComponentLevel()) { AInstancedFoliageActor* IFA = AInstancedFoliageActor::GetInstancedFoliageActorForLevel(ActorComponent->GetComponentLevel(), true); FFoliageMeshInfo* MeshInfo = nullptr; UFoliageType* FoliageType = IFA->AddFoliageType(SourceFoliageType, &MeshInfo); const TCHAR* StrPtr; FString TextLine; while (MeshInfo && FParse::Line(&SourceText, TextLine)) { StrPtr = *TextLine; if (GetEND(&StrPtr, TEXT("Foliage"))) { break; } // Parse the instance properties FFoliageInstance Instance; FString Temp; if (FParse::Value(StrPtr, TEXT("Location="), Temp, false)) { GetFVECTOR(*Temp, Instance.Location); } if (FParse::Value(StrPtr, TEXT("Rotation="), Temp, false)) { GetFROTATOR(*Temp, Instance.Rotation, 1); } if (FParse::Value(StrPtr, TEXT("PreAlignRotation="), Temp, false)) { GetFROTATOR(*Temp, Instance.PreAlignRotation, 1); } if (FParse::Value(StrPtr, TEXT("DrawScale3D="), Temp, false)) { GetFVECTOR(*Temp, Instance.DrawScale3D); } FParse::Value(StrPtr, TEXT("Flags="), Instance.Flags); // Add the instance MeshInfo->AddInstance(IFA, FoliageType, Instance, ActorComponent); } } } } else if( GetBEGIN(&Str,TEXT("Object"))) { // If SubobjectOuter is NULL, we are importing defaults for a UScriptStruct's defaultproperties block if ( !bSubObjectsAllowed ) { Warn->Logf(ELogVerbosity::Error, TEXT("BEGIN OBJECT: Subobjects are not allowed in this context")); return NULL; } // Parse subobject default properties. // Note: default properties subobjects have compiled class as their Outer (used for localization). UClass* TemplateClass = NULL; bool bInvalidClass = false; ParseObject<UClass>(Str, TEXT("Class="), TemplateClass, ANY_PACKAGE, &bInvalidClass); if (bInvalidClass) { Warn->Logf(ELogVerbosity::Error,TEXT("BEGIN OBJECT: Invalid class specified: %s"), *StrLine); return NULL; } // parse the name of the template FName TemplateName = NAME_None; FParse::Value(Str,TEXT("Name="),TemplateName); if(TemplateName == NAME_None) { Warn->Logf(ELogVerbosity::Error,TEXT("BEGIN OBJECT: Must specify valid name for subobject/component: %s"), *StrLine); return NULL; } // points to the parent class's template subobject/component, if we are overriding a subobject/component declared in our parent class UObject* BaseTemplate = NULL; bool bRedefiningSubobject = false; if( TemplateClass ) { } else { // next, verify that a template actually exists in the parent class UClass* ParentClass = ComponentOwnerClass->GetSuperClass(); check(ParentClass); UObject* ParentCDO = ParentClass->GetDefaultObject(); check(ParentCDO); BaseTemplate = StaticFindObjectFast(UObject::StaticClass(), SubobjectOuter, TemplateName); bRedefiningSubobject = (BaseTemplate != NULL); if (BaseTemplate == NULL) { BaseTemplate = StaticFindObjectFast(UObject::StaticClass(), ParentCDO, TemplateName); } if ( BaseTemplate == NULL ) { // wasn't found Warn->Logf(ELogVerbosity::Error, TEXT("BEGIN OBJECT: No base template named %s found in parent class %s: %s"), *TemplateName.ToString(), *ParentClass->GetName(), *StrLine); return NULL; } TemplateClass = BaseTemplate->GetClass(); } // because the outer won't be a default object checkSlow(TemplateClass != NULL); if (bRedefiningSubobject) { // since we're redefining an object in the same text block, only need to import properties again SourceText = ImportObjectProperties( (uint8*)BaseTemplate, SourceText, TemplateClass, SubobjectRoot, BaseTemplate, Warn, Depth + 1, ContextSupplier ? ContextSupplier->CurrentLine : 0, &InstanceGraph, ActorRemapper ); } else { UObject* Archetype = NULL; UObject* ComponentTemplate = NULL; // Since we are changing the class we can't use the Archetype, // however that is fine since we will have been editing the CDO anyways if (!FBlueprintEditorUtils::IsAnonymousBlueprintClass(SubobjectOuter->GetClass())) { // if an archetype was specified in the Begin Object block, use that as the template for the ConstructObject call. FString ArchetypeName; if (FParse::Value(Str, TEXT("Archetype="), ArchetypeName)) { // if given a name, break it up along the ' so separate the class from the name FString ObjectClass; FString ObjectPath; if ( FPackageName::ParseExportTextPath(ArchetypeName, &ObjectClass, &ObjectPath) ) { // find the class UClass* ArchetypeClass = (UClass*)StaticFindObject(UClass::StaticClass(), ANY_PACKAGE, *ObjectClass); if (ArchetypeClass) { // if we had the class, find the archetype Archetype = StaticFindObject(ArchetypeClass, ANY_PACKAGE, *ObjectPath); } } } } if (SubobjectOuter->HasAnyFlags(RF_ClassDefaultObject)) { if (!Archetype) // if an archetype was specified explicitly, we will stick with that { Archetype = ComponentOwnerClass->GetDefaultSubobjectByName(TemplateName); if(Archetype) { if ( BaseTemplate == NULL ) { // BaseTemplate should only be NULL if the Begin Object line specified a class Warn->Logf(ELogVerbosity::Error, TEXT("BEGIN OBJECT: The component name %s is already used (if you want to override the component, don't specify a class): %s"), *TemplateName.ToString(), *StrLine); return NULL; } // the component currently in the component template map and the base template should be the same checkf(Archetype==BaseTemplate,TEXT("OverrideComponent: '%s' BaseTemplate: '%s'"), *Archetype->GetFullName(), *BaseTemplate->GetFullName()); } } } else // handle the non-template case (subobjects and non-template components) { // don't allow Actor-derived subobjects if ( TemplateClass->IsChildOf(AActor::StaticClass()) ) { Warn->Logf(ELogVerbosity::Error,TEXT("Cannot create subobjects from Actor-derived classes: %s"), *StrLine); return NULL; } ComponentTemplate = FindObject<UObject>(SubobjectOuter, *TemplateName.ToString()); if (ComponentTemplate != NULL) { // if we're overriding a subobject declared in a parent class, we should already have an object with that name that // was instanced when ComponentOwnerClass's CDO was initialized; if so, it's archetype should be the BaseTemplate. If it // isn't, then there are two unrelated subobject definitions using the same name. if ( ComponentTemplate->GetArchetype() != BaseTemplate ) { } else if ( BaseTemplate == NULL ) { // BaseTemplate should only be NULL if the Begin Object line specified a class Warn->Logf(ELogVerbosity::Error, TEXT("BEGIN OBJECT: A subobject named %s is already declared in a parent class. If you intended to override that subobject, don't specify a class in the derived subobject definition: %s"), *TemplateName.ToString(), *StrLine); return NULL; } } } // Propagate object flags to the sub object. EObjectFlags NewFlags = SubobjectOuter->GetMaskedFlags( RF_PropagateToSubObjects ); if (!Archetype) // no override and we didn't find one from the class table, so go with the base { Archetype = BaseTemplate; } UObject* OldComponent = NULL; if (ComponentTemplate) { bool bIsOkToReuse = ComponentTemplate->GetClass() == TemplateClass && ComponentTemplate->GetOuter() == SubobjectOuter && ComponentTemplate->GetFName() == TemplateName && (ComponentTemplate->GetArchetype() == Archetype || !Archetype); if (!bIsOkToReuse) { UE_LOG(LogEditorObject, Log, TEXT("Could not reuse component instance %s, name clash?"), *ComponentTemplate->GetFullName()); ComponentTemplate->Rename(); // just abandon the existing component, we are going to create OldComponent = ComponentTemplate; ComponentTemplate = NULL; } } if (!ComponentTemplate) { ComponentTemplate = NewObject<UObject>( SubobjectOuter, TemplateClass, TemplateName, NewFlags, Archetype, !!SubobjectOuter, &InstanceGraph ); } else { // We do not want to set RF_Transactional for construction script created components, so we have to monkey with things here if (NewFlags & RF_Transactional) { UActorComponent* Component = Cast<UActorComponent>(ComponentTemplate); if (Component && Component->IsCreatedByConstructionScript()) { NewFlags &= ~RF_Transactional; } } // Make sure desired flags are set - existing object could be pending kill ComponentTemplate->ClearFlags(RF_AllFlags); ComponentTemplate->SetFlags(NewFlags); } // replace all properties in this subobject outer' class that point to the original subobject with the new subobject TMap<UObject*, UObject*> ReplacementMap; if (Archetype) { checkSlow(ComponentTemplate->GetArchetype() == Archetype); ReplacementMap.Add(Archetype, ComponentTemplate); InstanceGraph.AddNewInstance(ComponentTemplate); } if (OldComponent) { ReplacementMap.Add(OldComponent, ComponentTemplate); } FArchiveReplaceObjectRef<UObject> ReplaceAr(SubobjectOuter, ReplacementMap, false, false, true); // import the properties for the subobject SourceText = ImportObjectProperties( (uint8*)ComponentTemplate, SourceText, TemplateClass, SubobjectRoot, ComponentTemplate, Warn, Depth+1, ContextSupplier ? ContextSupplier->CurrentLine : 0, &InstanceGraph, ActorRemapper ); } } else if( FParse::Command(&Str,TEXT("CustomProperties"))) { check(SubobjectOuter); SubobjectOuter->ImportCustomProperties(Str, Warn); } else if( GetEND(&Str,TEXT("Actor")) || GetEND(&Str,TEXT("DefaultProperties")) || GetEND(&Str,TEXT("structdefaultproperties")) || (GetEND(&Str,TEXT("Object")) && Depth) ) { // End of properties. break; } else if( GetREMOVE(&Str,TEXT("Component")) ) { checkf(false, TEXT("Remove component is illegal in pasted text")); } else { // Property. UProperty::ImportSingleProperty(Str, DestData, ObjectStruct, SubobjectOuter, PortFlags, Warn, DefinedProperties); } } if (ActorRemapper) { for (const auto& DefinedProperty : DefinedProperties) { RemapProperty(DefinedProperty.Property, DefinedProperty.Index, *ActorRemapper, DestData); } } // Prepare brush. if( ImportedBrush && ObjectStruct->IsChildOf<ABrush>() && !ObjectStruct->IsChildOf<AVolume>() ) { check(GIsEditor); ABrush* Actor = (ABrush*)DestData; check(Actor->GetBrushComponent()); if( Actor->GetBrushComponent()->Mobility == EComponentMobility::Static ) { // Prepare static brush. Actor->SetNotForClientOrServer(); } else { // Prepare moving brush. FBSPOps::csgPrepMovingBrush( Actor ); } } return SourceText; }
/** * Walks up the list of outers until it finds the highest one. * * @return outermost non NULL Outer. */ UPackage* UObjectBaseUtility::GetOutermost() const { UObject* Top; for( Top = (UObject*)this ; Top && Top->GetOuter() ; Top = Top->GetOuter() ); return CastChecked<UPackage>(Top); }
/** * Traverses the outer chain searching for the next object of a certain type. (T must be derived from UObject) * * @param Target class to search for * @return a pointer to the first object in this object's Outer chain which is of the correct type. */ UObject* UObjectBaseUtility::GetTypedOuter(UClass* Target) const { UObject* Result = NULL; for ( UObject* NextOuter = GetOuter(); Result == NULL && NextOuter != NULL; NextOuter = NextOuter->GetOuter() ) { if ( NextOuter->IsA(Target) ) { Result = NextOuter; } } return Result; }
UObject* UObjectPropertyBase::FindImportedObject( const UProperty* Property, UObject* OwnerObject, UClass* ObjectClass, UClass* RequiredMetaClass, const TCHAR* Text, uint32 PortFlags/*=0*/ ) { UObject* Result = NULL; check( ObjectClass->IsChildOf(RequiredMetaClass) ); bool AttemptNonQualifiedSearch = (PortFlags & PPF_AttemptNonQualifiedSearch) != 0; // if we are importing default properties, first look for a matching subobject by // looking through the archetype chain at each outer and stop once the outer chain reaches the owning class's default object if (PortFlags & PPF_ParsingDefaultProperties) { for (UObject* SearchStart = OwnerObject; Result == NULL && SearchStart != NULL; SearchStart = SearchStart->GetOuter()) { UObject* ScopedSearchRoot = SearchStart; while (Result == NULL && ScopedSearchRoot != NULL) { Result = StaticFindObject(ObjectClass, ScopedSearchRoot, Text); // don't think it's possible to get a non-subobject here, but it doesn't hurt to check if (Result != NULL && !Result->IsTemplate(RF_ClassDefaultObject)) { Result = NULL; } ScopedSearchRoot = ScopedSearchRoot->GetArchetype(); } if (SearchStart->HasAnyFlags(RF_ClassDefaultObject)) { break; } } } // if we have a parent, look in the parent, then it's outer, then it's outer, ... // this is because exported object properties that point to objects in the level aren't // fully qualified, and this will step up the nested object chain to solve any name // collisions within a nested object tree UObject* ScopedSearchRoot = OwnerObject; while (Result == NULL && ScopedSearchRoot != NULL) { Result = StaticFindObject(ObjectClass, ScopedSearchRoot, Text); // disallow class default subobjects here while importing defaults // this prevents the use of a subobject name that doesn't exist in the scope of the default object being imported // from grabbing some other subobject with the same name and class in some other arbitrary default object if (Result != NULL && (PortFlags & PPF_ParsingDefaultProperties) && Result->IsTemplate(RF_ClassDefaultObject)) { Result = NULL; } ScopedSearchRoot = ScopedSearchRoot->GetOuter(); } if (Result == NULL) { // attempt to find a fully qualified object Result = StaticFindObject(ObjectClass, NULL, Text); if (Result == NULL) { // match any object of the correct class whose path contains the specified path Result = StaticFindObject(ObjectClass, ANY_PACKAGE, Text); // disallow class default subobjects here while importing defaults if (Result != NULL && (PortFlags & PPF_ParsingDefaultProperties) && Result->IsTemplate(RF_ClassDefaultObject)) { Result = NULL; } } } // if we haven;t found it yet, then try to find it without a qualified name if (!Result) { const TCHAR* Dot = FCString::Strrchr(Text, '.'); if (Dot && AttemptNonQualifiedSearch) { // search with just the object name Result = FindImportedObject(Property, OwnerObject, ObjectClass, RequiredMetaClass, Dot + 1); } FString NewText(Text); // if it didn't have a dot, then maybe they just gave a uasset package name if (!Dot && !Result) { int32 LastSlash = NewText.Find(TEXT("/"), ESearchCase::CaseSensitive, ESearchDir::FromEnd); if (LastSlash >= 0) { NewText += TEXT("."); NewText += (Text + LastSlash + 1); Dot = FCString::Strrchr(*NewText, '.'); } } // If we still can't find it, try to load it. (Only try to load fully qualified names) if(!Result && Dot) { #if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING FLinkerLoad* Linker = (OwnerObject != nullptr) ? OwnerObject->GetClass()->GetLinker() : nullptr; const bool bDeferAssetImports = (Linker != nullptr) && (Linker->LoadFlags & LOAD_DeferDependencyLoads); if (bDeferAssetImports) { Result = Linker->RequestPlaceholderValue(ObjectClass, Text); } if (Result == nullptr) #endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING { uint32 LoadFlags = LOAD_NoWarn | LOAD_FindIfFail; UE_LOG(LogProperty, Verbose, TEXT("FindImportedObject is attempting to import [%s] (class = %s) with StaticLoadObject"), Text, *GetFullNameSafe(ObjectClass)); Result = StaticLoadObject(ObjectClass, NULL, Text, NULL, LoadFlags, NULL); #if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS check(!bDeferAssetImports || !Result || !FBlueprintSupport::IsInBlueprintPackage(Result)); #endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS } } } // if we found an object, and we have a parent, make sure we are in the same package if the found object is private, unless it's a cross level property if (Result && !Result->HasAnyFlags(RF_Public) && OwnerObject && Result->GetOutermost() != OwnerObject->GetOutermost()) { const UObjectPropertyBase* ObjectProperty = dynamic_cast<const UObjectPropertyBase*>(Property); if ( !ObjectProperty || !ObjectProperty->AllowCrossLevel()) { UE_LOG(LogProperty, Warning, TEXT("Illegal TEXT reference to a private object in external package (%s) from referencer (%s). Import failed..."), *Result->GetFullName(), *OwnerObject->GetFullName()); Result = NULL; } } check(!Result || Result->IsA(RequiredMetaClass)); return Result; }
void SBlueprintEditorSelectedDebugObjectWidget::GenerateDebugObjectNames(bool bRestoreSelection) { TSharedPtr<FString> OldSelection; // Store off the old selection if (bRestoreSelection && DebugObjectsComboBox.IsValid()) { OldSelection = DebugObjectsComboBox->GetSelectedItem(); } // Empty the lists of actors and regenerate them DebugObjects.Empty(); DebugObjectNames.Empty(); DebugObjects.Add(NULL); DebugObjectNames.Add(MakeShareable(new FString(GetNoDebugString()))); // Grab custom objects that should always be visible, regardless of the world TArray<FCustomDebugObject> CustomDebugObjects; BlueprintEditor.Pin()->GetCustomDebugObjects(/*inout*/ CustomDebugObjects); for (const FCustomDebugObject& Entry : CustomDebugObjects) { if (Entry.NameOverride.IsEmpty()) { AddDebugObject(Entry.Object); } else { AddDebugObjectWithName(Entry.Object, Entry.NameOverride); } } // Check for a specific debug world. If DebugWorld=NULL we take that as "any PIE world" UWorld* DebugWorld = NULL; if (DebugWorldsComboBox.IsValid()) { TSharedPtr<FString> CurrentWorldSelection = DebugWorldsComboBox->GetSelectedItem(); int32 SelectedIndex = DebugWorldNames.Find(CurrentWorldSelection); if (SelectedIndex > 0 && DebugWorldNames.IsValidIndex(SelectedIndex)) { DebugWorld = DebugWorlds[SelectedIndex].Get(); } } UWorld* PreviewWorld = NULL; TSharedPtr<SSCSEditorViewport> PreviewViewportPtr = BlueprintEditor.Pin()->GetSCSViewport(); if (PreviewViewportPtr.IsValid()) { PreviewWorld = PreviewViewportPtr->GetPreviewScene().GetWorld(); } for (TObjectIterator<UObject> It; It; ++It) { UObject* TestObject = *It; // Skip Blueprint preview objects (don't allow them to be selected for debugging) if (PreviewWorld != NULL && TestObject->IsIn(PreviewWorld)) { continue; } const bool bPassesFlags = !TestObject->HasAnyFlags(RF_PendingKill | RF_ClassDefaultObject); const bool bGeneratedByBlueprint = TestObject->GetClass()->ClassGeneratedBy == GetBlueprintObj(); if (bPassesFlags && bGeneratedByBlueprint) { UObject *ObjOuter = TestObject; UWorld *ObjWorld = NULL; while (ObjWorld == NULL && ObjOuter != NULL) { ObjOuter = ObjOuter->GetOuter(); ObjWorld = Cast<UWorld>(ObjOuter); } // Object not in any world if (!ObjWorld) { continue; } // Make check on owning level (not streaming level) if (ObjWorld->PersistentLevel && ObjWorld->PersistentLevel->OwningWorld) { ObjWorld = ObjWorld->PersistentLevel->OwningWorld; } // We have a specific debug world and the object isnt in it if (DebugWorld && ObjWorld != DebugWorld) { continue; } // We don't have a specific debug world, but the object isnt in a PIE world if (ObjWorld->WorldType != EWorldType::PIE) { continue; } AddDebugObject(TestObject); } } // Attempt to restore the old selection if (bRestoreSelection && DebugObjectsComboBox.IsValid()) { bool bMatchFound = false; for (int32 ObjectIndex = 0; ObjectIndex < DebugObjectNames.Num(); ++ObjectIndex) { if (*DebugObjectNames[ObjectIndex] == *OldSelection) { DebugObjectsComboBox->SetSelectedItem(DebugObjectNames[ObjectIndex]); bMatchFound = true; break; } } // No match found, use the default option if (!bMatchFound) { DebugObjectsComboBox->SetSelectedItem(DebugObjectNames[0]); } } // Finally ensure we have a valid selection if (DebugObjectsComboBox.IsValid()) { TSharedPtr<FString> CurrentSelection = DebugObjectsComboBox->GetSelectedItem(); if (DebugObjectNames.Find(CurrentSelection) == INDEX_NONE) { if (DebugObjectNames.Num() > 0) { DebugObjectsComboBox->SetSelectedItem(DebugObjectNames[0]); } else { DebugObjectsComboBox->ClearSelection(); } } DebugObjectsComboBox->RefreshOptions(); } }
/** * Exports the property values for the specified object as text to the output device. * * @param Context Context from which the set of 'inner' objects is extracted. If NULL, an object iterator will be used. * @param Out the output device to send the exported text to * @param ObjectClass the class of the object to dump properties for * @param Object the address of the object to dump properties for * @param Indent number of spaces to prepend to each line of output * @param DiffClass the class to use for comparing property values when delta export is desired. * @param Diff the address of the object to use for determining whether a property value should be exported. If the value in Object matches the corresponding * value in Diff, it is not exported. Specify NULL to export all properties. * @param Parent the UObject corresponding to Object * @param PortFlags flags used for modifying the output and/or behavior of the export */ void ExportProperties ( const FExportObjectInnerContext* Context, FOutputDevice& Out, UClass* ObjectClass, uint8* Object, int32 Indent, UClass* DiffClass, uint8* Diff, UObject* Parent, uint32 PortFlags, UObject* ExportRootScope ) { FString ThisName = TEXT("(none)"); check(ObjectClass != NULL); for( UProperty* Property = ObjectClass->PropertyLink; Property; Property = Property->PropertyLinkNext ) { if (!Property->ShouldPort(PortFlags)) continue; ThisName = Property->GetName(); UArrayProperty* ArrayProperty = Cast<UArrayProperty>(Property); UObjectPropertyBase* ExportObjectProp = (Property->PropertyFlags & CPF_ExportObject) != 0 ? Cast<UObjectPropertyBase>(Property) : NULL; const uint32 ExportFlags = PortFlags | PPF_Delimited; if ( ArrayProperty != NULL ) { // Export dynamic array. UProperty* InnerProp = ArrayProperty->Inner; ExportObjectProp = (Property->PropertyFlags & CPF_ExportObject) != 0 ? Cast<UObjectPropertyBase>(InnerProp) : NULL; // This is used as the default value in the case of an array property that has // fewer elements than the exported object. uint8* StructDefaults = NULL; UStructProperty* StructProperty = Cast<UStructProperty>(InnerProp); if ( StructProperty != NULL ) { checkSlow(StructProperty->Struct); StructDefaults = (uint8*)FMemory::Malloc(StructProperty->Struct->GetStructureSize()); StructProperty->InitializeValue(StructDefaults); } for( int32 PropertyArrayIndex=0; PropertyArrayIndex<Property->ArrayDim; PropertyArrayIndex++ ) { void* Arr = Property->ContainerPtrToValuePtr<void>(Object, PropertyArrayIndex); FScriptArrayHelper ArrayHelper(ArrayProperty, Arr); void* DiffArr = NULL; if( DiffClass ) { DiffArr = Property->ContainerPtrToValuePtrForDefaults<void>(DiffClass, Diff, PropertyArrayIndex); } // we won't use this if DiffArr is NULL, but we have to set it up to something FScriptArrayHelper DiffArrayHelper(ArrayProperty, DiffArr); bool bAnyElementDiffered = false; for( int32 DynamicArrayIndex=0; DynamicArrayIndex<ArrayHelper.Num(); DynamicArrayIndex++ ) { FString Value; // compare each element's value manually so that elements which match the NULL value for the array's inner property type // but aren't in the diff array are still exported uint8* SourceData = ArrayHelper.GetRawPtr(DynamicArrayIndex); uint8* DiffData = DiffArr && DynamicArrayIndex < DiffArrayHelper.Num() ? DiffArrayHelper.GetRawPtr(DynamicArrayIndex) : StructDefaults; bool bExportItem = DiffData == NULL || (DiffData != SourceData && !InnerProp->Identical(SourceData, DiffData, ExportFlags)); if ( bExportItem ) { bAnyElementDiffered = true; InnerProp->ExportTextItem(Value, SourceData, DiffData, Parent, ExportFlags, ExportRootScope); if(ExportObjectProp) { UObject* Obj = ExportObjectProp->GetObjectPropertyValue(ArrayHelper.GetRawPtr(DynamicArrayIndex)); check(!Obj || Obj->IsValidLowLevel()); if( Obj && !Obj->HasAnyMarks(OBJECTMARK_TagImp) ) { // only export the BEGIN OBJECT block for a component if Parent is the component's Outer....when importing subobject definitions, // (i.e. BEGIN OBJECT), whichever BEGIN OBJECT block a component's BEGIN OBJECT block is located within is the object that will be // used as the Outer to create the component // Is this an array of components? if ( InnerProp->HasAnyPropertyFlags(CPF_InstancedReference) ) { if ( Obj->GetOuter() == Parent ) { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } else { // set the OBJECTMARK_TagExp flag so that the calling code knows we wanted to export this object Obj->Mark(OBJECTMARK_TagExp); } } else { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } } } Out.Logf( TEXT("%s%s(%i)=%s\r\n"), FCString::Spc(Indent), *Property->GetName(), DynamicArrayIndex, *Value ); } // if some other element has already been determined to differ from the defaults, then export this item with no data so that // the different array's size is maintained on import (this item will get the default values for that index, if any) // however, if no elements of the array have changed, we still don't want to export anything // so that the array size will also be taken from the defaults, which won't be the case if any element is exported else if (bAnyElementDiffered) { Out.Logf( TEXT("%s%s(%i)=()\r\n"), FCString::Spc(Indent), *Property->GetName(), DynamicArrayIndex ); } } } if (StructDefaults) { StructProperty->DestroyValue(StructDefaults); FMemory::Free(StructDefaults); } } else { for( int32 PropertyArrayIndex=0; PropertyArrayIndex<Property->ArrayDim; PropertyArrayIndex++ ) { FString Value; // Export single element. uint8* DiffData = (DiffClass && Property->IsInContainer(DiffClass->GetPropertiesSize())) ? Diff : NULL; if( Property->ExportText_InContainer( PropertyArrayIndex, Value, Object, DiffData, Parent, ExportFlags, ExportRootScope ) ) { if ( ExportObjectProp ) { UObject* Obj = ExportObjectProp->GetObjectPropertyValue(Property->ContainerPtrToValuePtr<void>(Object, PropertyArrayIndex)); if( Obj && !Obj->HasAnyMarks(OBJECTMARK_TagImp) ) { // only export the BEGIN OBJECT block for a component if Parent is the component's Outer....when importing subobject definitions, // (i.e. BEGIN OBJECT), whichever BEGIN OBJECT block a component's BEGIN OBJECT block is located within is the object that will be // used as the Outer to create the component if ( Property->HasAnyPropertyFlags(CPF_InstancedReference) ) { if ( Obj->GetOuter() == Parent ) { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } else { // set the OBJECTMARK_TagExp flag so that the calling code knows we wanted to export this object Obj->Mark(OBJECTMARK_TagExp); } } else { // Don't export more than once. Obj->Mark(OBJECTMARK_TagImp); UExporter::ExportToOutputDevice( Context, Obj, NULL, Out, TEXT("T3D"), Indent, PortFlags ); } } } if( Property->ArrayDim == 1 ) { Out.Logf( TEXT("%s%s=%s\r\n"), FCString::Spc(Indent), *Property->GetName(), *Value ); } else { Out.Logf( TEXT("%s%s(%i)=%s\r\n"), FCString::Spc(Indent), *Property->GetName(), PropertyArrayIndex, *Value ); } } } } } // Allows to import/export C++ properties in case the automatic unreal script mesh wouldn't work. Parent->ExportCustomProperties(Out, Indent); }
void UExporter::EmitBeginObject( FOutputDevice& Ar, UObject* Obj, uint32 PortFlags ) { check(Obj); // figure out how to export bool bIsExportingDefaultObject = Obj->HasAnyFlags(RF_ClassDefaultObject) || Obj->GetArchetype()->HasAnyFlags(RF_ClassDefaultObject); // start outputting the string for the Begin Object line Ar.Logf(TEXT("%sBegin Object"), FCString::Spc(TextIndent)); if (!(PortFlags & PPF_SeparateDefine)) { Ar.Logf(TEXT(" Class=%s"), *Obj->GetClass()->GetName()); } // always need a name, adding "" for space handling Ar.Logf(TEXT(" Name=\"%s\""), *Obj->GetName()); if (!(PortFlags & PPF_SeparateDefine)) { // do we want the archetype string? if (!bIsExportingDefaultObject) { UObject* Archetype = Obj->GetArchetype(); Ar.Logf(TEXT(" Archetype=%s"), *UObjectPropertyBase::GetExportPath(Archetype, Archetype->GetOutermost(), Archetype->GetOuter(), PortFlags)); } } // end in a return Ar.Logf(TEXT("\r\n")); if ( bEnableDebugBrackets ) { Ar.Logf(TEXT("%s{%s"), FCString::Spc(TextIndent), LINE_TERMINATOR); } }
UObject* FBlueprintNativeCodeGenModule::FindReplacedNameAndOuter(UObject* Object, FName& OutName) const { OutName = NAME_None; UObject* Outer = nullptr; UActorComponent* ActorComponent = Cast<UActorComponent>(Object); if (ActorComponent) { //if is child of a BPGC and not child of a CDO UBlueprintGeneratedClass* BPGC = nullptr; for (UObject* OuterObject = ActorComponent->GetOuter(); OuterObject && !BPGC; OuterObject = OuterObject->GetOuter()) { if (OuterObject->HasAnyFlags(RF_ClassDefaultObject)) { return Outer; } BPGC = Cast<UBlueprintGeneratedClass>(OuterObject); } for (UBlueprintGeneratedClass* SuperBPGC = BPGC; SuperBPGC && (OutName == NAME_None); SuperBPGC = Cast<UBlueprintGeneratedClass>(SuperBPGC->GetSuperClass())) { if (SuperBPGC->InheritableComponentHandler) { FComponentKey FoundKey = SuperBPGC->InheritableComponentHandler->FindKey(ActorComponent); if (FoundKey.IsValid()) { OutName = FoundKey.IsSCSKey() ? FoundKey.GetSCSVariableName() : ActorComponent->GetFName(); Outer = BPGC->GetDefaultObject(false); break; } } if (SuperBPGC->SimpleConstructionScript) { for (auto Node : SuperBPGC->SimpleConstructionScript->GetAllNodes()) { if (Node->ComponentTemplate == ActorComponent) { OutName = Node->VariableName; if (OutName != NAME_None) { Outer = BPGC->GetDefaultObject(false); break; } } } } } } if (Outer && (EReplacementResult::ReplaceCompletely == IsTargetedForReplacement(Object->GetClass()))) { UE_LOG(LogBlueprintCodeGen, Log, TEXT("Object '%s' has replaced name '%s' and outer: '%s'"), *GetPathNameSafe(Object), *OutName.ToString(), *GetPathNameSafe(Outer)); return Outer; } return nullptr; }