void FContentComparisonHelper::RecursiveObjectCollection(UObject* InStartObject, int32 InCurrDepth, int32 InMaxDepth, TMap<UObject*,bool>& OutCollectedReferences) { // Serialize object with reference collector. TArray<UObject*> LocalCollectedReferences; FReferenceFinder ObjectReferenceCollector( LocalCollectedReferences, NULL, false, true, true, true ); ObjectReferenceCollector.FindReferences( InStartObject ); if (InCurrDepth < InMaxDepth) { InCurrDepth++; for (int32 ObjRefIdx = 0; ObjRefIdx < LocalCollectedReferences.Num(); ObjRefIdx++) { UObject* InnerObject = LocalCollectedReferences[ObjRefIdx]; if ((InnerObject != NULL) && (InnerObject->IsA(UFunction::StaticClass()) == false) && (InnerObject->IsA(UPackage::StaticClass()) == false) ) { OutCollectedReferences.Add(InnerObject, true); RecursiveObjectCollection(InnerObject, InCurrDepth, InMaxDepth, OutCollectedReferences); } } InCurrDepth--; } }
void UGatherTextFromAssetsCommandlet::ProcessPackages( const TArray< UPackage* >& PackagesToProcess ) { for( int32 i = 0; i < PackagesToProcess.Num(); ++i ) { UPackage* Package = PackagesToProcess[i]; TArray<UObject*> Objects; GetObjectsWithOuter(Package, Objects); for( int32 j = 0; j < Objects.Num(); ++j ) { UObject* Object = Objects[j]; if ( Object->IsA( UBlueprint::StaticClass() ) ) { UBlueprint* Blueprint = Cast<UBlueprint>( Object ); if( Blueprint->GeneratedClass != NULL ) { ProcessObject( Blueprint->GeneratedClass->GetDefaultObject(), Package ); } else { UE_LOG(LogGatherTextFromAssetsCommandlet, Warning, TEXT("%s - Invalid generated class!"), *Blueprint->GetFullName()); } } else if( Object->IsA( UDialogueWave::StaticClass() ) ) { UDialogueWave* DialogueWave = Cast<UDialogueWave>( Object ); ProcessDialogueWave( DialogueWave ); } ProcessObject( Object, Package ); } } }
FReply FEditorUtilityInstanceDetails::OnExecuteAction(TWeakObjectPtr<UFunction> WeakFunctionPtr) { if (UFunction* Function = WeakFunctionPtr.Get()) { FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "BlutilityAction", "Blutility Action") ); FEditorScriptExecutionGuard ScriptGuard; UClass* MinRequiredClass = Function->GetOuterUClass(); // Execute this function on any objects that support it for (auto SelectedObjectIt = SelectedObjectsList.CreateConstIterator(); SelectedObjectIt; ++SelectedObjectIt) { UObject* Object = (*SelectedObjectIt).Get(); if ((Object != NULL) && (Object->IsA(MinRequiredClass))) { Object->ProcessEvent(Function, NULL); if (UGlobalEditorUtilityBase* BlutilityInstance = Cast<UGlobalEditorUtilityBase>(Object)) { BlutilityInstance->PostExecutionCleanup(); } } } } return FReply::Handled(); }
int32 UObjectLibrary::LoadAssetsFromPaths(const TArray<FString>& Paths) { int32 Count = 0; if (bIsFullyLoaded) { // We already ran this return 0; } bIsFullyLoaded = true; for (int PathIndex = 0; PathIndex < Paths.Num(); PathIndex++) { TArray<UObject*> LoadedObjects; FString Path = Paths[PathIndex]; if (EngineUtils::FindOrLoadAssetsByPath(Path, LoadedObjects, bHasBlueprintClasses ? EngineUtils::ATL_Class : EngineUtils::ATL_Regular)) { for (int32 i = 0; i < LoadedObjects.Num(); ++i) { UObject* Object = LoadedObjects[i]; if (Object == NULL || (ObjectBaseClass && !Object->IsA(ObjectBaseClass))) { // Incorrect type, skip continue; } AddObject(Object); Count++; } } } return Count; }
FArchiveGenerateReferenceGraph::FArchiveGenerateReferenceGraph( FReferenceGraph& OutGraph ) : CurrentObject(NULL), ObjectGraph(OutGraph) { ArIsObjectReferenceCollector = true; ArIgnoreOuterRef = true; // Iterate over each object.. for( FObjectIterator It; It; ++It ) { UObject* Object = *It; // Skip transient and those about to be deleted if( !Object->HasAnyFlags( RF_Transient | RF_PendingKill ) ) { // only serialize non actors objects which have not been visited. // actors are skipped because we have don't need them to show the reference tree // @todo, may need to serialize them later for full reference graph. if( !VisitedObjects.Find( Object ) && !Object->IsA( AActor::StaticClass() ) ) { // Set the current object to the one we are about to serialize CurrentObject = Object; // This object has been visited. Any serializations after this should skip this object VisitedObjects.Add( Object ); Object->Serialize( *this ); } } } }
void USelection::DeselectAll( UClass* InClass ) { // Fast path for deselecting all UObjects with any flags if ( InClass == UObject::StaticClass() ) { InClass = nullptr; } bool bSelectionChanged = false; TSet<FSelectedClassInfo> RemovedClasses; // Remove from the end to minimize memmoves. for ( int32 i = SelectedObjects.Num()-1 ; i >= 0 ; --i ) { UObject* Object = GetSelectedObject(i); if ( !Object ) { // Remove NULLs from SelectedObjects array. SelectedObjects.RemoveAt( i ); } else if( !InClass || Object->IsA( InClass ) ) { // if the object is of type InClass then all objects of that same type will be removed RemovedClasses.Add(FSelectedClassInfo(Object->GetClass())); GSelectedAnnotation.Clear(Object); SelectedObjects.RemoveAt( i ); // Call this after the item has been removed from the selection set. USelection::SelectObjectEvent.Broadcast( Object ); bSelectionChanged = true; } } if( InClass == nullptr ) { SelectedClasses.Empty(); } else { // Remove the passed in class and all child classes that were removed // from the list of currently selected classes RemovedClasses.Add(InClass); SelectedClasses = SelectedClasses.Difference(RemovedClasses); } if ( bSelectionChanged ) { MarkBatchDirty(); if ( !IsBatchSelecting() ) { USelection::SelectionChangedEvent.Broadcast(this); } } }
/** * 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; }
int32 UObjectLibrary::LoadAssetsFromAssetData() { int32 Count = 0; if (bIsFullyLoaded) { // We already ran this return 0; } bIsFullyLoaded = true; for(int32 AssetIdx=0; AssetIdx<AssetDataList.Num(); AssetIdx++) { FAssetData& Data = AssetDataList[AssetIdx]; UObject *LoadedObject = NULL; if (!bHasBlueprintClasses) { LoadedObject = Data.GetAsset(); checkSlow(!LoadedObject || !ObjectBaseClass || LoadedObject->IsA(ObjectBaseClass)); } else { UPackage* Package = Data.GetPackage(); if (Package) { TArray<UObject*> ObjectsInPackage; GetObjectsWithOuter(Package, ObjectsInPackage); for (UObject* PotentialBPGC : ObjectsInPackage) { if (auto LoadedBPGC = Cast<UBlueprintGeneratedClass>(PotentialBPGC)) { checkSlow(!ObjectBaseClass || LoadedBPGC->IsChildOf(ObjectBaseClass)); LoadedObject = LoadedBPGC; break; //there is usually only one BGPC in a package } } } } if (LoadedObject) { AddObject(LoadedObject); Count++; } } return Count; }
void ULiveEditorKismetLibrary::ReplicateChangesToChildren( FName PropertyName, UObject *Archetype ) { if ( Archetype == NULL ) return; //find our child instances from the LiveEditManage lookup cache TArray< TWeakObjectPtr<UObject> > PiePartners; FLiveEditorManager::Get().FindPiePartners( Archetype, PiePartners ); for(TArray< TWeakObjectPtr<UObject> >::TIterator It(PiePartners); It; ++It) { if ( !(*It).IsValid() ) continue; UObject *Object = (*It).Get(); check( Object->IsA(Archetype->GetClass()) ); //little sanity check, but the object cache manages all this for us nLiveEditorKismetLibrary::CopyPropertyFromArchetype( Object, Archetype, PropertyName ); } void *ContainerPtr = Archetype; int32 ArrayIndex = 0; UProperty *Prop = nLiveEditorKismetLibrary::GetPropertyByName( Archetype, Archetype->GetClass(), PropertyName.ToString(), &ContainerPtr, ArrayIndex ); if ( Prop && Prop->IsA( UNumericProperty::StaticClass() ) ) { check( ContainerPtr != NULL ); FString ClassName = Archetype->GetClass()->GetName(); FString ValueString; void *Value = Prop->ContainerPtrToValuePtr<void>(ContainerPtr, ArrayIndex); Prop->ExportTextItem(ValueString, Value, NULL, NULL, 0); FLiveEditorManager::Get().BroadcastValueUpdate( ClassName, PropertyName.ToString(), ValueString ); } /** * Object iteration method should we want to dump the PIEObjectCache * Downside is that it is perceptably slow (though still usable) TArray<UObject*> ObjectsToChange; const bool bIncludeDerivedClasses = true; GetObjectsOfClass(Archetype->GetClass(), ObjectsToChange, bIncludeDerivedClasses); for ( auto ObjIt = ObjectsToChange.CreateIterator(); ObjIt; ++ObjIt ) { UObject *Object = *ObjIt; UWorld *World = GEngine->GetWorldFromContextObject( Object, false ); if ( World == NULL || World->WorldType != EWorldType::PIE ) continue; CopyPropertyFromArchetype( Object, Archetype, PropertyName ); } */ }
void UMatineeTrackAnimControlHelper::OnAddKeyTextEntry(const FAssetData& AssetData, IMatineeBase* Matinee, UInterpTrack* Track) { if (EntryMenu.IsValid()) { EntryMenu.Pin()->Dismiss(); } UObject* SelectedObject = AssetData.GetAsset(); if (SelectedObject && SelectedObject->IsA(UAnimSequence::StaticClass())) { KeyframeAddAnimSequence = CastChecked<UAnimSequence>(AssetData.GetAsset()); Matinee->FinishAddKey(Track, true); } }
FString SObjectNameEditableTextBox::GetObjectDisplayName(TWeakObjectPtr<UObject> Object) { FString Result; if(Object.IsValid()) { UObject* ObjectPtr = Object.Get(); if (ObjectPtr->IsA(AActor::StaticClass())) { Result = ((AActor*)ObjectPtr)->GetActorLabel(); } else { Result = ObjectPtr->GetName(); } } return Result; }
UEdGraph* SGraphTitleBar::GetOuterGraph( UObject* Obj ) { if( Obj ) { UObject* OuterObj = Obj->GetOuter(); if( OuterObj ) { if( OuterObj->IsA( UEdGraph::StaticClass()) ) { return Cast<UEdGraph>(OuterObj); } else { return GetOuterGraph( OuterObj ); } } } return NULL; }
bool FKismetVariableDragDropAction::CanVariableBeDropped(const UProperty* InVariableProperty, const UEdGraph& InGraph) const { bool bCanVariableBeDropped = false; if (InVariableProperty) { UObject* Outer = InVariableProperty->GetOuter(); // Only allow variables to be placed within the same blueprint (otherwise the self context on the dropped node will be invalid) bCanVariableBeDropped = IsFromBlueprint(FBlueprintEditorUtils::FindBlueprintForGraph(&InGraph)); // Local variables have some special conditions for being allowed to be placed if (bCanVariableBeDropped && Outer->IsA(UFunction::StaticClass())) { // Check if the top level graph has the same name as the function, if they do not then the variable cannot be placed in the graph if (FBlueprintEditorUtils::GetTopLevelGraph(&InGraph)->GetFName() != Outer->GetFName()) { bCanVariableBeDropped = false; } } } return bCanVariableBeDropped; }
void SPropertyMenuAssetPicker::OnPaste() { FString DestPath; FPlatformMisc::ClipboardPaste(DestPath); if(DestPath == TEXT("None")) { SetValue(NULL); } else { UObject* Object = LoadObject<UObject>(NULL, *DestPath); bool PassesAllowedClassesFilter = true; if(AllowedClasses.Num()) { PassesAllowedClassesFilter = false; for(int32 i = 0; i < AllowedClasses.Num(); ++i) { if( Object->IsA(AllowedClasses[i]) ) { PassesAllowedClassesFilter = true; break; } } } if( Object && PassesAllowedClassesFilter ) { FAssetData ObjectAssetData(Object); // Check against custom asset filter if (!OnShouldFilterAsset.IsBound() || !OnShouldFilterAsset.Execute(ObjectAssetData)) { SetValue(ObjectAssetData); } } } OnClose.ExecuteIfBound(); }
void SPropertyEditorAsset::OnPaste() { FString DestPath; FPlatformMisc::ClipboardPaste(DestPath); if(DestPath == TEXT("None")) { SetValue(NULL); } else { UObject* Object = LoadObject<UObject>(NULL, *DestPath); if(Object && Object->IsA(ObjectClass)) { // Check against custom asset filter if (!OnShouldFilterAsset.IsBound() || !OnShouldFilterAsset.Execute(FAssetData(Object))) { SetValue(Object); } } } }
void FLiveEditorListenServer::ReplicateChanges( const nLiveEditorListenServer::FNetworkMessage &Message ) { if ( Message.Type == nLiveEditorListenServer::CLASSDEFAULT_OBJECT_PROPERTY ) { //UClass *BaseClass = LoadClass<UObject>( NULL, *Message.Payload.ClassName, NULL, 0, NULL ); UClass *BaseClass = FindObject<UClass>(ANY_PACKAGE, *Message.Payload.ClassName); if ( BaseClass ) { nLiveEditorListenServer::SetPropertyValue( BaseClass->ClassDefaultObject, Message.Payload.PropertyName, Message.Payload.PropertyValue ); TArray< TWeakObjectPtr<UObject> > Replicants; ObjectCache.FindObjectDependants( BaseClass->ClassDefaultObject, Replicants ); for ( TArray< TWeakObjectPtr<UObject> >::TIterator It(Replicants); It; ++It ) { if ( !(*It).IsValid() ) continue; UObject *Object = (*It).Get(); check( Object->IsA(BaseClass) ); nLiveEditorListenServer::SetPropertyValue( Object, Message.Payload.PropertyName, Message.Payload.PropertyValue ); } } } }
int32 UFixupRedirectsCommandlet::Main( const FString& Params ) { // Retrieve list of all packages in .ini paths. TArray<FString> PackageList; FEditorFileUtils::FindAllPackageFiles(PackageList); if( !PackageList.Num() ) { return 0; } // process the commandline FString Token; const TCHAR* CommandLine = *Params; bool bIsQuietMode = false; bool bIsTestOnly = false; bool bShouldRestoreProgress= false; bool bIsSCCDisabled = false; bool bUpdateEnginePackages = false; bool bDeleteRedirects = true; bool bDeleteOnlyReferencedRedirects = false; bool bAutoSubmit = false; bool bSandboxed = IFileManager::Get().IsSandboxEnabled(); while (FParse::Token(CommandLine, Token, 1)) { if (Token == TEXT("-nowarn")) { bIsQuietMode = true; } else if (Token == TEXT("-testonly")) { bIsTestOnly = true; } else if (Token == TEXT("-restore")) { bShouldRestoreProgress = true; } else if (Token == TEXT("-NoAutoCheckout")) { bIsSCCDisabled = true; } else if (Token == TEXT("-AutoSubmit")) { bAutoSubmit = true; } else if (Token == TEXT("-UpdateEnginePackages")) { bUpdateEnginePackages = true; } else if (Token == TEXT("-NoDelete")) { bDeleteRedirects = false; } else if (Token == TEXT("-NoDeleteUnreferenced")) { bDeleteOnlyReferencedRedirects = true; } else if ( Token.Left(1) != TEXT("-") ) { FPackageName::SearchForPackageOnDisk(Token, &GRedirectCollector.FileToFixup); } } if (bSandboxed) { UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Running in a sandbox.")); bIsSCCDisabled = true; bAutoSubmit = false; bDeleteRedirects = false; } bool bShouldSkipErrors = false; // Setup file Output log in the Saved Directory const FString ProjectDir = FPaths::GetPath(FPaths::GetProjectFilePath()); const FString LogFilename = FPaths::Combine(*ProjectDir, TEXT("Saved"), TEXT("FixupRedirect"), TEXT("FixupRedirect.log")); FArchive* LogOutputFile = IFileManager::Get().CreateFileWriter(*LogFilename); LogOutputFile->Logf(TEXT("[Redirects]")); ///////////////////////////////////////////////////////////////////// // Display any script code referenced redirects ///////////////////////////////////////////////////////////////////// TArray<FString> UpdatePackages; TArray<FString> RedirectorsThatCantBeCleaned; if (!bShouldRestoreProgress) { // load all string asset reference targets, and add fake redirectors for them GRedirectCollector.ResolveStringAssetReference(); for (int32 RedirIndex = 0; RedirIndex < GRedirectCollector.Redirections.Num(); RedirIndex++) { FRedirection& Redir = GRedirectCollector.Redirections[RedirIndex]; UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Boot redirector can't be cleaned: %s(%s) -> %s(%s)"), *Redir.RedirectorName, *Redir.RedirectorPackageFilename, *Redir.DestinationObjectName, *Redir.PackageFilename); RedirectorsThatCantBeCleaned.AddUnique(Redir.RedirectorName); } ///////////////////////////////////////////////////////////////////// // Build a list of packages that need to be updated ///////////////////////////////////////////////////////////////////// SET_WARN_COLOR(COLOR_WHITE); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Generating list of tasks:")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("-------------------------")); CLEAR_WARN_COLOR(); int32 GCIndex = 0; // process script code first pass, then everything else for( int32 PackageIndex = 0; PackageIndex < PackageList.Num(); PackageIndex++ ) { const FString& Filename = PackageList[PackageIndex]; // already loaded code, skip them, and skip autosave packages if (Filename.StartsWith(AutoSaveUtils::GetAutoSaveDir(), ESearchCase::IgnoreCase)) { continue; } UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Looking for redirects in %s..."), *Filename); // Assert if package couldn't be opened so we have no chance of messing up saving later packages. UPackage* Package = Cast<UPackage>(LoadPackage(NULL, *Filename, 0)); // load all string asset reference targets, and add fake redirectors for them GRedirectCollector.ResolveStringAssetReference(); if (!Package) { SET_WARN_COLOR(COLOR_RED); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... failed to open %s"), *Filename); CLEAR_WARN_COLOR(); // if we are not in quiet mode, and the user wants to continue, go on const FText Message = FText::Format( NSLOCTEXT("Unreal", "PackageFailedToOpen", "The package '{0}' failed to open. Should this commandlet continue operation, ignoring further load errors? Continuing may have adverse effects (object references may be lost)."), FText::FromString( Filename ) ); if (bShouldSkipErrors || (!bIsQuietMode && GWarn->YesNof( Message ))) { bShouldSkipErrors = true; continue; } else { return 1; } } // all notices here about redirects get this color SET_WARN_COLOR(COLOR_DARK_GREEN); // look for any redirects that were followed that are referenced by this package // (may have been loaded earlier if this package was loaded by script code) // any redirectors referenced by script code can't be cleaned up for (int32 RedirIndex = 0; RedirIndex < GRedirectCollector.Redirections.Num(); RedirIndex++) { FRedirection& Redir = GRedirectCollector.Redirections[RedirIndex]; if (Redir.PackageFilename == Filename) { UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... references %s [-> %s]"), *Redir.RedirectorName, *Redir.DestinationObjectName); // put the source and dest packages in the list to be updated UpdatePackages.AddUnique(Redir.PackageFilename); UpdatePackages.AddUnique(Redir.RedirectorPackageFilename); } } // clear the flag for needing to collect garbage bool bNeedToCollectGarbage = false; // if this package has any redirectors, make sure we update it if (!GRedirectCollector.FileToFixup.Len() || GRedirectCollector.FileToFixup == FPackageName::FilenameToLongPackageName(Filename)) { TArray<UObject *> ObjectsInOuter; GetObjectsWithOuter(Package, ObjectsInOuter); for( int32 Index = 0; Index < ObjectsInOuter.Num(); Index++ ) { UObject* Obj = ObjectsInOuter[Index]; UObjectRedirector* Redir = Cast<UObjectRedirector>(Obj); if (Redir) { // make sure this package is in the list of packages to update UpdatePackages.AddUnique(Filename); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... has redirect %s [-> %s]"), *Redir->GetPathName(), *Redir->DestinationObject->GetFullName()); LogOutputFile->Logf(TEXT("%s = %s"), *Redir->GetPathName(), *Redir->DestinationObject->GetFullName()); // make sure we GC if we found a redirector bNeedToCollectGarbage = true; } } } CLEAR_WARN_COLOR(); // collect garbage every N packages, or if there was any redirectors in the package // (to make sure that redirectors get reloaded properly and followed by the callback) // also, GC after loading a map package, to make sure it's unloaded so that we can track // other packages loading a map package as a dependency if (((++GCIndex) % 50) == 0 || bNeedToCollectGarbage || Package->ContainsMap()) { // collect garbage to close the package CollectGarbage(RF_Native); UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("GC...")); // reset our counter GCIndex = 0; } } // save off restore point FArchive* Ar = IFileManager::Get().CreateFileWriter(*(FPaths::GameSavedDir() + TEXT("Fixup.bin"))); *Ar << GRedirectCollector.Redirections; *Ar << UpdatePackages; *Ar << RedirectorsThatCantBeCleaned; delete Ar; } else { // load restore point FArchive* Ar = IFileManager::Get().CreateFileReader(*(FPaths::GameSavedDir() + TEXT("Fixup.bin"))); if( Ar != NULL ) { *Ar << GRedirectCollector.Redirections; *Ar << UpdatePackages; *Ar << RedirectorsThatCantBeCleaned; delete Ar; } } // unregister the callback so we stop getting redirections added FCoreUObjectDelegates::RedirectorFollowed.RemoveAll(&GRedirectCollector); ///////////////////////////////////////////////////////////////////// // Explain to user what is happening ///////////////////////////////////////////////////////////////////// SET_WARN_COLOR(COLOR_YELLOW); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Files that need to fixed up:")); SET_WARN_COLOR(COLOR_DARK_YELLOW); // print out the working set of packages for (int32 PackageIndex = 0; PackageIndex < UpdatePackages.Num(); PackageIndex++) { UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" %s"), *UpdatePackages[PackageIndex]); } UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); CLEAR_WARN_COLOR(); // if we are only testing, just just quiet before actually doing anything if (bIsTestOnly) { LogOutputFile->Close(); delete LogOutputFile; LogOutputFile = NULL; return 0; } ///////////////////////////////////////////////////////////////////// // Find redirectors that are referenced by packages we cant check out ///////////////////////////////////////////////////////////////////// bool bEngineRedirectorCantBeCleaned = false; ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); // initialize source control if it hasn't been initialized yet if(!bIsSCCDisabled) { SourceControlProvider.Init(); //Make sure we can check out all packages - update their state first SourceControlProvider.Execute(ISourceControlOperation::Create<FUpdateStatus>(), UpdatePackages); } for( int32 PackageIndex = 0; PackageIndex < UpdatePackages.Num(); PackageIndex++ ) { const FString& Filename = UpdatePackages[PackageIndex]; bool bCanEdit = true; if(!bIsSCCDisabled) { FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Filename, EStateCacheUsage::ForceUpdate); if(SourceControlState.IsValid() && !SourceControlState->CanEdit()) { bCanEdit = false; } } else if(IFileManager::Get().IsReadOnly(*Filename)) { bCanEdit = false; } if(!bCanEdit) { const bool bAllowCheckout = bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ); if (!bIsSCCDisabled && bAllowCheckout) { FString PackageName(FPackageName::FilenameToLongPackageName(Filename)); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Filename, EStateCacheUsage::ForceUpdate); if (SourceControlState.IsValid() && SourceControlState->CanCheckout()) { SourceControlProvider.Execute(ISourceControlOperation::Create<FCheckOut>(), SourceControlHelpers::PackageFilename(PackageName)); FSourceControlStatePtr NewSourceControlState = SourceControlProvider.GetState(Filename, EStateCacheUsage::ForceUpdate); bCanEdit = NewSourceControlState.IsValid() && NewSourceControlState->CanEdit(); } } // if the checkout failed for any reason, we can't clean it up if (bAllowCheckout && !bCanEdit) { SET_WARN_COLOR(COLOR_RED); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Couldn't check out/edit %s..."), *Filename); CLEAR_WARN_COLOR(); // load all string asset reference targets, and add fake redirectors for them GRedirectCollector.ResolveStringAssetReference(); // any redirectors that are pointed to by this read-only package can't be fixed up for (int32 RedirIndex = 0; RedirIndex < GRedirectCollector.Redirections.Num(); RedirIndex++) { FRedirection& Redir = GRedirectCollector.Redirections[RedirIndex]; // any redirectors pointed to by this file we don't clean if (Redir.PackageFilename == Filename) { RedirectorsThatCantBeCleaned.AddUnique(Redir.RedirectorName); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... can't fixup references to %s"), *Redir.RedirectorName); // if this redirector is in an Engine package, we need to alert the user, because // Engine packages are shared between games, so if one game still needs the redirector // to be valid, then we can't check in Engine packages after another game has // run that could if (!bEngineRedirectorCantBeCleaned) { FString PackagePath; FPackageName::DoesPackageExist(Redir.RedirectorPackageFilename, NULL, &PackagePath); if (PackagePath.StartsWith(FPaths::EngineDir())) { bEngineRedirectorCantBeCleaned = true; } } } // any redirectors in this file can't be deleted else if (Redir.RedirectorPackageFilename == Filename) { RedirectorsThatCantBeCleaned.AddUnique(Redir.RedirectorName); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... can't delete %s"), *Redir.RedirectorName); } } } else { SET_WARN_COLOR(COLOR_DARK_GREEN); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Checked out %s..."), *Filename); CLEAR_WARN_COLOR(); } } } // warn about an engine package that can't be cleaned up (may want to revert engine packages) if (bEngineRedirectorCantBeCleaned) { UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Engine package redirectors are referenced by packages that can't be cleaned. If you have multiple games, it's recommended you revert any checked out Engine packages.")); bUpdateEnginePackages=false; } UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); ///////////////////////////////////////////////////////////////////// // Load and save packages to save out the proper fixed up references ///////////////////////////////////////////////////////////////////// SET_WARN_COLOR(COLOR_WHITE); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Fixing references to point to proper objects:")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("---------------------------------------------")); CLEAR_WARN_COLOR(); // keep a list of all packages that have ObjectRedirects in them. This is not needed if we aren't deleting redirects TArray<bool> PackagesWithRedirects; // Delete these packages entirely because they are empty TArray<bool> EmptyPackagesToDelete; if ( bDeleteRedirects ) { PackagesWithRedirects.AddZeroed(UpdatePackages.Num()); EmptyPackagesToDelete.AddZeroed(PackageList.Num()); } bool bSCCEnabled = ISourceControlModule::Get().IsEnabled(); // Iterate over all packages, loading and saving to remove all references to ObjectRedirectors (can't delete the Redirects yet) for( int32 PackageIndex = 0; PackageIndex < UpdatePackages.Num(); PackageIndex++ ) { const FString& Filename = UpdatePackages[PackageIndex]; // we can't do anything with packages that are read-only or cant be edited due to source control (we don't want to fixup the redirects) if(bSCCEnabled) { FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(Filename), EStateCacheUsage::ForceUpdate); if(SourceControlState.IsValid() && !SourceControlState->CanEdit()) { continue; } } else if(IFileManager::Get().IsReadOnly(*Filename)) { continue; } // Assert if package couldn't be opened so we have no chance of messing up saving later packages. UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Cleaning %s"), *Filename); UPackage* Package = LoadPackage( NULL, *Filename, LOAD_NoVerify ); // load all string asset reference targets, and add fake redirectors for them GRedirectCollector.ResolveStringAssetReference(); // if it failed to open, we have already dealt with quitting if we are going to, so just skip it if (!Package) { continue; } if ( bDeleteOnlyReferencedRedirects && bDeleteRedirects ) { TArray<UObject *> ObjectsInOuter; GetObjectsWithOuter(Package, ObjectsInOuter); for( int32 Index = 0; Index < ObjectsInOuter.Num(); Index++ ) { UObject* Obj = ObjectsInOuter[Index]; UObjectRedirector* Redir = Cast<UObjectRedirector>(Obj); if (Redir) { PackagesWithRedirects[PackageIndex] = 1; break; } } } // Don't save packages in the engine directory if we did not opt to update engine packages if( bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ) ) { // save out the package UWorld* World = UWorld::FindWorldInPackage(Package); GEditor->SavePackage( Package, World, World ? RF_NoFlags : RF_Standalone, *Filename, GWarn ); } // collect garbage to close the package UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Collecting Garbage...")); CollectGarbage(RF_Native); } UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); if ( bDeleteRedirects ) { ///////////////////////////////////////////////////////////////////// // Delete all redirects that are no longer referenced ///////////////////////////////////////////////////////////////////// SET_WARN_COLOR(COLOR_WHITE); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting redirector objects:")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("----------------------------")); CLEAR_WARN_COLOR(); const TArray<FString>& PackagesToCleaseOfRedirects = bDeleteOnlyReferencedRedirects ? UpdatePackages : PackageList; int32 GCIndex = 0; // Again, iterate over all packages, loading and saving to and this time deleting all for( int32 PackageIndex = 0; PackageIndex < PackagesToCleaseOfRedirects.Num(); PackageIndex++ ) { const FString& Filename = PackagesToCleaseOfRedirects[PackageIndex]; if (bDeleteOnlyReferencedRedirects && PackagesWithRedirects[PackageIndex] == 0) { continue; } // The file should have already been checked out/be writable // If it is read only that means the checkout failed or we are not using source control and we can not write this file. if(bSCCEnabled) { FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(Filename), EStateCacheUsage::ForceUpdate); if(SourceControlState.IsValid() && !SourceControlState->CanEdit()) { UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Skipping file: source control says we cant edit the file %s..."), *Filename); continue; } } else if( IFileManager::Get().IsReadOnly( *Filename )) { UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Skipping read-only file %s..."), *Filename); continue; } UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Wiping %s..."), *Filename); UPackage* Package = LoadPackage( NULL, *Filename, 0 ); // load all string asset reference targets, and add fake redirectors for them GRedirectCollector.ResolveStringAssetReference(); // if it failed to open, we have already dealt with quitting if we are going to, so just skip it if (!Package) { continue; } // assume that all redirs in this file are still-referenced bool bIsDirty = false; // see if this package is completely empty other than purged redirectors and metadata bool bIsEmpty = true; // delete all ObjectRedirects now TArray<UObjectRedirector*> Redirs; // find all redirectors, put into an array so delete doesn't mess up the iterator TArray<UObject *> ObjectsInOuter; GetObjectsWithOuter(Package, ObjectsInOuter); for( int32 Index = 0; Index < ObjectsInOuter.Num(); Index++ ) { UObject* Obj = ObjectsInOuter[Index]; UObjectRedirector *Redir = Cast<UObjectRedirector>(Obj); if (Redir) { int32 Dummy; // if the redirector was marked as uncleanable, skip it if (RedirectorsThatCantBeCleaned.Find(Redir->GetFullName(), Dummy)) { SET_WARN_COLOR(COLOR_DARK_RED); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... skipping still-referenced %s"), *Redir->GetFullName()); CLEAR_WARN_COLOR(); bIsEmpty = false; continue; } bIsDirty = true; SET_WARN_COLOR(COLOR_DARK_GREEN); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... deleting %s"), *Redir->GetFullName()); CLEAR_WARN_COLOR(); Redirs.Add(Redir); } else if ( (!Obj->IsA(UMetaData::StaticClass()) && !Obj->IsA(UField::StaticClass())) || Obj->IsA(UUserDefinedEnum::StaticClass())) // UserDefinedEnum should not be deleted { // This package has a real object bIsEmpty = false; } } if (bIsEmpty && !bDeleteOnlyReferencedRedirects) { EmptyPackagesToDelete[PackageIndex] = 1; } // mark them for deletion. for (int32 RedirIndex = 0; RedirIndex < Redirs.Num(); RedirIndex++) { UObjectRedirector* Redirector = Redirs[RedirIndex]; // Remove standalone flag and mark as pending kill. Redirector->ClearFlags( RF_Standalone | RF_Public ); // We don't need redirectors in the root set, the references should already be fixed up if ( Redirector->IsRooted() ) { Redirector->RemoveFromRoot(); } Redirector->MarkPendingKill(); } Redirs.Empty(); if (bIsDirty) { // Don't save packages in the engine directory if we did not opt to update engine packages if( bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ) ) { // save the package UWorld* World = UWorld::FindWorldInPackage(Package); GEditor->SavePackage( Package, World, World ? RF_NoFlags : RF_Standalone, *Filename, GWarn ); } } // collect garbage every N packages, or if there was any redirectors in the package if (((++GCIndex) % 50) == 0 || bIsDirty || Package->ContainsMap()) { // collect garbage to close the package CollectGarbage(RF_Native); UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("GC...")); // reset our counter GCIndex = 0; } } UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); } else { SET_WARN_COLOR(COLOR_WHITE); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("----------------------------")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Not deleting any redirector objects:")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("----------------------------")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("")); CLEAR_WARN_COLOR(); } ///////////////////////////////////////////////////////////////////// // Clean up any unused trash ///////////////////////////////////////////////////////////////////// SET_WARN_COLOR(COLOR_WHITE); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting empty and unused packages:")); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("---------------------------------------")); CLEAR_WARN_COLOR(); LogOutputFile->Logf(TEXT("")); LogOutputFile->Logf(TEXT("[Deleted]")); // Iterate over packages, attempting to delete unreferenced packages for( int32 PackageIndex = 0; PackageIndex < PackageList.Num(); PackageIndex++ ) { bool bDelete = false; const FString& Filename = PackageList[PackageIndex]; FString PackageName(FPackageName::FilenameToLongPackageName(Filename)); const bool bAllowDelete = bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ); if (bDeleteRedirects && !bDeleteOnlyReferencedRedirects && EmptyPackagesToDelete[PackageIndex] && bAllowDelete) { // this package is completely empty, delete it bDelete = true; } if (bDelete == true) { struct Local { static bool DeleteFromDisk(const FString& InFilename, const FString& InMessage) { SET_WARN_COLOR(COLOR_GREEN); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("%s"), *InMessage); if (!IFileManager::Get().Delete(*InFilename, false, true)) { SET_WARN_COLOR(COLOR_RED); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT(" ... failed to delete from disk."), *InFilename); CLEAR_WARN_COLOR(); return false; } CLEAR_WARN_COLOR(); return true; } }; if (!bIsSCCDisabled) { // get file SCC status FString FileName = SourceControlHelpers::PackageFilename(PackageName); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(FileName, EStateCacheUsage::ForceUpdate); if(SourceControlState.IsValid() && (SourceControlState->IsCheckedOut() || SourceControlState->IsAdded()) ) { UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Revert '%s' from source control..."), *Filename); SourceControlProvider.Execute(ISourceControlOperation::Create<FRevert>(), FileName); SET_WARN_COLOR(COLOR_GREEN); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting '%s' from source control..."), *Filename); CLEAR_WARN_COLOR(); SourceControlProvider.Execute(ISourceControlOperation::Create<FDelete>(), FileName); } else if(SourceControlState.IsValid() && SourceControlState->CanCheckout()) { SET_WARN_COLOR(COLOR_GREEN); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Deleting '%s' from source control..."), *Filename); CLEAR_WARN_COLOR(); SourceControlProvider.Execute(ISourceControlOperation::Create<FDelete>(), FileName); } else if(SourceControlState.IsValid() && SourceControlState->IsCheckedOutOther()) { SET_WARN_COLOR(COLOR_RED); UE_LOG(LogFixupRedirectsCommandlet, Warning, TEXT("Couldn't delete '%s' from source control, someone has it checked out, skipping..."), *Filename); CLEAR_WARN_COLOR(); } else if(SourceControlState.IsValid() && !SourceControlState->IsSourceControlled()) { if(Local::DeleteFromDisk(Filename, FString::Printf(TEXT("'%s' is not in source control, attempting to delete from disk..."), *Filename))) { LogOutputFile->Logf(*Filename); } } else { if(Local::DeleteFromDisk(Filename, FString::Printf(TEXT("'%s' is in an unknown source control state, attempting to delete from disk..."), *Filename))) { LogOutputFile->Logf(*Filename); } } } else { if(Local::DeleteFromDisk(Filename, FString::Printf(TEXT("source control disabled while deleting '%s', attempting to delete from disk..."), *Filename))) { LogOutputFile->Logf(*Filename); } } } } LogOutputFile->Close(); delete LogOutputFile; LogOutputFile = NULL; if (!bIsSCCDisabled && bAutoSubmit) { ///////////////////////////////////////////////////////////////////// // Submit the results to source control ///////////////////////////////////////////////////////////////////// UE_LOG(LogFixupRedirectsCommandlet, Display, TEXT("Submiting the results to source control")); // Work out the list of file to check in TArray <FString> FilesToSubmit; for( int32 PackageIndex = 0; PackageIndex < PackageList.Num(); PackageIndex++ ) { const FString& Filename = PackageList[PackageIndex]; FString PackageName(FPackageName::FilenameToLongPackageName(Filename)); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(SourceControlHelpers::PackageFilename(Filename), EStateCacheUsage::ForceUpdate); if( SourceControlState.IsValid() && (SourceControlState->IsCheckedOut() || SourceControlState->IsAdded() || SourceControlState->IsDeleted()) ) { // Only submit engine packages if we're requested to if( bUpdateEnginePackages || !Filename.StartsWith( FPaths::EngineDir() ) ) { FilesToSubmit.Add(*PackageName); } } } // Check in all changed files const FText Description = NSLOCTEXT("FixupRedirectsCmdlet", "ChangelistDescription", "Fixed up Redirects"); TSharedRef<FCheckIn, ESPMode::ThreadSafe> CheckInOperation = ISourceControlOperation::Create<FCheckIn>(); CheckInOperation->SetDescription( Description ); SourceControlProvider.Execute(CheckInOperation, SourceControlHelpers::PackageFilenames(FilesToSubmit)); // toss the SCC manager ISourceControlModule::Get().GetProvider().Close(); } return 0; }
void SSizeMap::GatherDependenciesRecursively( FAssetRegistryModule& AssetRegistryModule, TSharedPtr<FAssetThumbnailPool>& InAssetThumbnailPool, TMap<FName, TSharedPtr<FTreeMapNodeData>>& VisitedAssetPackageNames, const TArray<FName>& AssetPackageNames, const TSharedPtr<FTreeMapNodeData>& Node, TSharedPtr<FTreeMapNodeData>& SharedRootNode, int32& NumAssetsWhichFailedToLoad ) { for( const FName AssetPackageName : AssetPackageNames ) { // Have we already added this asset to the tree? If so, we'll either move it to a "shared" group or (if it's referenced again by the same // root-level asset) ignore it if( VisitedAssetPackageNames.Contains( AssetPackageName ) ) { // OK, we've determined that this asset has already been referenced by something else in our tree. We'll move it to a "shared" group // so all of the assets that are referenced in multiple places can be seen together. TSharedPtr<FTreeMapNodeData> ExistingNode = VisitedAssetPackageNames[ AssetPackageName ]; // Is the existing node not already under the "shared" group? Note that it might still be (indirectly) under // the "shared" group, in which case we'll still want to move it up to the root since we've figured out that it is // actually shared between multiple assets which themselves may be shared if( ExistingNode->Parent != SharedRootNode.Get() ) { // Don't bother moving any of the assets at the root level into a "shared" bucket. We're only trying to best // represent the memory used when all of the root-level assets have become loaded. It's OK if root-level assets // are referenced by other assets in the set -- we don't need to indicate they are shared explicitly FTreeMapNodeData* ExistingNodeParent = ExistingNode->Parent; check( ExistingNodeParent != nullptr ); const bool bExistingNodeIsAtRootLevel = ExistingNodeParent->Parent == nullptr || RootAssetPackageNames.Contains( AssetPackageName ); if( !bExistingNodeIsAtRootLevel ) { // OK, the current asset (AssetPackageName) is definitely not a root level asset, but its already in the tree // somewhere as a non-shared, non-root level asset. We need to make sure that this Node's reference is not from the // same root-level asset as the ExistingNodeInTree. Otherwise, there's no need to move it to a 'shared' group. FTreeMapNodeData* MyParentNode = Node.Get(); check( MyParentNode != nullptr ); FTreeMapNodeData* MyRootLevelAssetNode = MyParentNode; while( MyRootLevelAssetNode->Parent != nullptr && MyRootLevelAssetNode->Parent->Parent != nullptr ) { MyRootLevelAssetNode = MyRootLevelAssetNode->Parent; } if( MyRootLevelAssetNode->Parent == nullptr ) { // No root asset (Node must be a root level asset itself!) MyRootLevelAssetNode = nullptr; } // Find the existing node's root level asset node FTreeMapNodeData* ExistingNodeRootLevelAssetNode = ExistingNodeParent; while( ExistingNodeRootLevelAssetNode->Parent->Parent != nullptr ) { ExistingNodeRootLevelAssetNode = ExistingNodeRootLevelAssetNode->Parent; } // If we're being referenced by another node within the same asset, no need to move it to a 'shared' group. if( MyRootLevelAssetNode != ExistingNodeRootLevelAssetNode ) { // This asset was already referenced by something else (or was in our top level list of assets to display sizes for) if( !SharedRootNode.IsValid() ) { // Find the root-most tree node FTreeMapNodeData* RootNode = MyParentNode; while( RootNode->Parent != nullptr ) { RootNode = RootNode->Parent; } SharedRootNode = MakeShareable( new FTreeMapNodeData() ); RootNode->Children.Add( SharedRootNode ); SharedRootNode->Parent = RootNode; // Keep back-pointer to parent node } // Reparent the node that we've now determined to be shared ExistingNode->Parent->Children.Remove( ExistingNode ); SharedRootNode->Children.Add( ExistingNode ); ExistingNode->Parent = SharedRootNode.Get(); } } } } else { // This asset is new to us so far! Let's add it to the tree. Later as we descend through references, we might find that the // asset is referenced by something else as well, in which case we'll pull it out and move it to a "shared" top-level box // Don't bother showing code references const FString AssetPackageNameString = AssetPackageName.ToString(); if( !AssetPackageNameString.StartsWith( TEXT( "/Script/" ) ) ) { FTreeMapNodeDataRef ChildTreeMapNode = MakeShareable( new FTreeMapNodeData() ); Node->Children.Add( ChildTreeMapNode ); ChildTreeMapNode->Parent = Node.Get(); // Keep back-pointer to parent node VisitedAssetPackageNames.Add( AssetPackageName, ChildTreeMapNode ); FNodeSizeMapData& NodeSizeMapData = NodeSizeMapDataMap.Add( ChildTreeMapNode ); // Set some defaults for this node. These will be used if we can't actually locate the asset. // @todo sizemap urgent: We need a better indication in the UI when there are one or more missing assets. Because missing assets have a size // of zero, they are nearly impossible to zoom into. At the least, we should have some Output Log spew when assets cannot be loaded NodeSizeMapData.AssetData.AssetName = AssetPackageName; NodeSizeMapData.AssetData.AssetClass = FName( *LOCTEXT( "MissingAsset", "MISSING!" ).ToString() ); NodeSizeMapData.AssetSize = 0; NodeSizeMapData.bHasKnownSize = false; // Find the asset using the asset registry // @todo sizemap: Asset registry-based reference gathering is faster but possibly not as exhaustive (no PostLoad created references, etc.) Maybe should be optional? // @todo sizemap: With AR-based reference gathering, sometimes the size map is missing root level dependencies until you reopen it a few times (Buggy BP) // @todo sizemap: With AR-based reference gathering, reference changes at editor-time do not appear in the Size Map until you restart // @todo sizemap: With AR-based reference gathering, opening the size map for all engine content caused the window to not respond until a restart // @todo sizemap: We don't really need the asset registry given we need to load the objects to figure out their size, unless we make that AR-searchable. // ---> This would allow us to not have to wait for AR initialization. But if we made size AR-searchable, we could run very quickly for large data sets! const bool bUseAssetRegistryForDependencies = false; const FString AssetPathString = AssetPackageNameString + TEXT(".") + FPackageName::GetLongPackageAssetName( AssetPackageNameString ); const FAssetData FoundAssetData = AssetRegistryModule.Get().GetAssetByObjectPath( FName( *AssetPathString ) ); if( FoundAssetData.IsValid() ) { NodeSizeMapData.AssetData = FoundAssetData; // Now actually load up the asset. We need it in memory in order to accurately determine its size. // @todo sizemap: We could async load these packages to make the editor experience a bit nicer (smoother progress) UObject* Asset = StaticLoadObject( UObject::StaticClass(), nullptr, *AssetPathString ); if( Asset != nullptr ) { TArray<FName> ReferencedAssetPackageNames; if( bUseAssetRegistryForDependencies ) { AssetRegistryModule.Get().GetDependencies( AssetPackageName, ReferencedAssetPackageNames ); } else { SizeMapInternals::FAssetReferenceFinder References( Asset ); for( UObject* Object : References.GetReferencedAssets() ) { ReferencedAssetPackageNames.Add( FName( *Object->GetOutermost()->GetPathName() ) ); } } // For textures, make sure we're getting the worst case size, not the size of the currently loaded set of mips // @todo sizemap: We should instead have a special EResourceSizeMode that asks for the worst case size. Some assets (like UTextureCube) currently always report resident mip size, even when asked for inclusive size if( Asset->IsA( UTexture2D::StaticClass() ) ) { NodeSizeMapData.AssetSize = Asset->GetResourceSize( EResourceSizeMode::Inclusive ); } else { NodeSizeMapData.AssetSize = Asset->GetResourceSize( EResourceSizeMode::Exclusive ); } NodeSizeMapData.bHasKnownSize = NodeSizeMapData.AssetSize != UObject::RESOURCE_SIZE_NONE && NodeSizeMapData.AssetSize != 0; if( !NodeSizeMapData.bHasKnownSize ) { // Asset has no meaningful size NodeSizeMapData.AssetSize = 0; // @todo sizemap urgent: Try to serialize to figure out how big it is (not into sub-assets though!) // FObjectMemoryAnalyzer ObjectMemoryAnalyzer( Asset ); } // Now visit all of the assets that we are referencing GatherDependenciesRecursively( AssetRegistryModule, InAssetThumbnailPool, VisitedAssetPackageNames, ReferencedAssetPackageNames, ChildTreeMapNode, SharedRootNode, NumAssetsWhichFailedToLoad ); } else { ++NumAssetsWhichFailedToLoad; } } else { ++NumAssetsWhichFailedToLoad; } } } } }
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; }
int32 SGraphPanel::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const { #if SLATE_HD_STATS SCOPE_CYCLE_COUNTER( STAT_SlateOnPaint_SGraphPanel ); #endif CachedAllottedGeometryScaledSize = AllottedGeometry.Size * AllottedGeometry.Scale; //Style used for objects that are the same between revisions FWidgetStyle FadedStyle = InWidgetStyle; FadedStyle.BlendColorAndOpacityTint(FLinearColor(0.45f,0.45f,0.45f,0.45f)); // First paint the background const UEditorExperimentalSettings& Options = *GetDefault<UEditorExperimentalSettings>(); const FSlateBrush* BackgroundImage = FEditorStyle::GetBrush(TEXT("Graph.Panel.SolidBackground")); PaintBackgroundAsLines(BackgroundImage, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId); const float ZoomFactor = AllottedGeometry.Scale * GetZoomAmount(); FArrangedChildren ArrangedChildren(EVisibility::Visible); ArrangeChildNodes(AllottedGeometry, ArrangedChildren); // Determine some 'global' settings based on current LOD const bool bDrawShadowsThisFrame = GetCurrentLOD() > EGraphRenderingLOD::LowestDetail; // Because we paint multiple children, we must track the maximum layer id that they produced in case one of our parents // wants to an overlay for all of its contents. // Save LayerId for comment boxes to ensure they always appear below nodes & wires const int32 CommentNodeShadowLayerId = LayerId++; const int32 CommentNodeLayerId = LayerId++; // Save a LayerId for wires, which appear below nodes but above comments // We will draw them later, along with the arrows which appear above nodes. const int32 WireLayerId = LayerId++; const int32 NodeShadowsLayerId = LayerId; const int32 NodeLayerId = NodeShadowsLayerId + 1; int32 MaxLayerId = NodeLayerId; const FVector2D NodeShadowSize = GetDefault<UGraphEditorSettings>()->GetShadowDeltaSize(); const UEdGraphSchema* Schema = GraphObj->GetSchema(); // Draw the child nodes { // When drawing a marquee, need a preview of what the selection will be. const FGraphPanelSelectionSet* SelectionToVisualize = &(SelectionManager.SelectedNodes); FGraphPanelSelectionSet SelectionPreview; if ( Marquee.IsValid() ) { ApplyMarqueeSelection(Marquee, SelectionManager.SelectedNodes, SelectionPreview); SelectionToVisualize = &SelectionPreview; } // Context for rendering node infos FKismetNodeInfoContext Context(GraphObj); TArray<FGraphDiffControl::FNodeMatch> NodeMatches; for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex) { FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex]; TSharedRef<SGraphNode> ChildNode = StaticCastSharedRef<SGraphNode>(CurWidget.Widget); // Examine node to see what layers we should be drawing in int32 ShadowLayerId = NodeShadowsLayerId; int32 ChildLayerId = NodeLayerId; // If a comment node, draw in the dedicated comment slots { UObject* NodeObj = ChildNode->GetObjectBeingDisplayed(); if (NodeObj && NodeObj->IsA(UEdGraphNode_Comment::StaticClass())) { ShadowLayerId = CommentNodeShadowLayerId; ChildLayerId = CommentNodeLayerId; } } const bool bNodeIsVisible = FSlateRect::DoRectanglesIntersect( CurWidget.Geometry.GetClippingRect(), MyClippingRect ); if (bNodeIsVisible) { const bool bSelected = SelectionToVisualize->Contains( StaticCastSharedRef<SNodePanel::SNode>(CurWidget.Widget)->GetObjectBeingDisplayed() ); // Handle Node renaming once the node is visible if( bSelected && ChildNode->IsRenamePending() ) { ChildNode->ApplyRename(); } // Draw the node's shadow. if (bDrawShadowsThisFrame || bSelected) { const FSlateBrush* ShadowBrush = ChildNode->GetShadowBrush(bSelected); FSlateDrawElement::MakeBox( OutDrawElements, ShadowLayerId, CurWidget.Geometry.ToInflatedPaintGeometry(NodeShadowSize), ShadowBrush, MyClippingRect ); } // Draw the comments and information popups for this node, if it has any. { const SNodePanel::SNode::FNodeSlot& CommentSlot = ChildNode->GetOrAddSlot( ENodeZone::TopCenter ); float CommentBubbleY = -CommentSlot.Offset.Get().Y; Context.bSelected = bSelected; TArray<FGraphInformationPopupInfo> Popups; { ChildNode->GetNodeInfoPopups(&Context, /*out*/ Popups); } for (int32 PopupIndex = 0; PopupIndex < Popups.Num(); ++PopupIndex) { FGraphInformationPopupInfo& Popup = Popups[PopupIndex]; PaintComment(Popup.Message, CurWidget.Geometry, MyClippingRect, OutDrawElements, ChildLayerId, Popup.BackgroundColor, /*inout*/ CommentBubbleY, InWidgetStyle); } } int32 CurWidgetsMaxLayerId; { UEdGraphNode* NodeObj = Cast<UEdGraphNode>(ChildNode->GetObjectBeingDisplayed()); /** When diffing nodes, nodes that are different between revisions are opaque, nodes that have not changed are faded */ FGraphDiffControl::FNodeMatch NodeMatch = FGraphDiffControl::FindNodeMatch(GraphObjToDiff, NodeObj, NodeMatches); if (NodeMatch.IsValid()) { NodeMatches.Add(NodeMatch); } const bool bNodeIsDifferent = (!GraphObjToDiff || NodeMatch.Diff()); /* When dragging off a pin, we want to duck the alpha of some nodes */ TSharedPtr< SGraphPin > OnlyStartPin = (1 == PreviewConnectorFromPins.Num()) ? PreviewConnectorFromPins[0].FindInGraphPanel(*this) : TSharedPtr< SGraphPin >(); const bool bNodeIsNotUsableInCurrentContext = Schema->FadeNodeWhenDraggingOffPin(NodeObj, OnlyStartPin.IsValid() ? OnlyStartPin.Get()->GetPinObj() : NULL); const FWidgetStyle& NodeStyleToUse = (bNodeIsDifferent && !bNodeIsNotUsableInCurrentContext)? InWidgetStyle : FadedStyle; // Draw the node.O CurWidgetsMaxLayerId = CurWidget.Widget->Paint( Args.WithNewParent(this), CurWidget.Geometry, MyClippingRect, OutDrawElements, ChildLayerId, NodeStyleToUse, ShouldBeEnabled( bParentEnabled ) ); } // Draw the node's overlay, if it has one. { // Get its size const FVector2D WidgetSize = CurWidget.Geometry.Size; { TArray<FOverlayBrushInfo> OverlayBrushes; ChildNode->GetOverlayBrushes(bSelected, WidgetSize, /*out*/ OverlayBrushes); for (int32 BrushIndex = 0; BrushIndex < OverlayBrushes.Num(); ++BrushIndex) { FOverlayBrushInfo& OverlayInfo = OverlayBrushes[BrushIndex]; const FSlateBrush* OverlayBrush = OverlayInfo.Brush; if(OverlayBrush != NULL) { FPaintGeometry BouncedGeometry = CurWidget.Geometry.ToPaintGeometry(OverlayInfo.OverlayOffset, OverlayBrush->ImageSize, 1.f); // Handle bouncing const float BounceValue = FMath::Sin(2.0f * PI * BounceCurve.GetLerpLooping()); BouncedGeometry.DrawPosition += (OverlayInfo.AnimationEnvelope * BounceValue * ZoomFactor); CurWidgetsMaxLayerId++; FSlateDrawElement::MakeBox( OutDrawElements, CurWidgetsMaxLayerId, BouncedGeometry, OverlayBrush, MyClippingRect ); } } } { TArray<FOverlayWidgetInfo> OverlayWidgets = ChildNode->GetOverlayWidgets(bSelected, WidgetSize); for (int32 WidgetIndex = 0; WidgetIndex < OverlayWidgets.Num(); ++WidgetIndex) { FOverlayWidgetInfo& OverlayInfo = OverlayWidgets[WidgetIndex]; if(OverlayInfo.Widget->GetVisibility() == EVisibility::Visible) { // call SlatePrepass as these widgets are not in the 'normal' child hierarchy OverlayInfo.Widget->SlatePrepass(); const FGeometry WidgetGeometry = CurWidget.Geometry.MakeChild(OverlayInfo.OverlayOffset, OverlayInfo.Widget->GetDesiredSize(), 1.f); OverlayInfo.Widget->Paint(Args.WithNewParent(this), WidgetGeometry, MyClippingRect, OutDrawElements, CurWidgetsMaxLayerId, InWidgetStyle, bParentEnabled); } } } } MaxLayerId = FMath::Max( MaxLayerId, CurWidgetsMaxLayerId + 1 ); } } } MaxLayerId += 1; // Draw connections between pins if (Children.Num() > 0 ) { //@TODO: Pull this into a factory like the pin and node ones FConnectionDrawingPolicy* ConnectionDrawingPolicy; { ConnectionDrawingPolicy = Schema->CreateConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); if (!ConnectionDrawingPolicy) { if (Schema->IsA(UAnimationGraphSchema::StaticClass())) { ConnectionDrawingPolicy = new FAnimGraphConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(UAnimationStateMachineSchema::StaticClass())) { ConnectionDrawingPolicy = new FStateMachineConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(UEdGraphSchema_K2::StaticClass())) { ConnectionDrawingPolicy = new FKismetConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(USoundCueGraphSchema::StaticClass())) { ConnectionDrawingPolicy = new FSoundCueGraphConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else if (Schema->IsA(UMaterialGraphSchema::StaticClass())) { ConnectionDrawingPolicy = new FMaterialGraphConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements, GraphObj); } else { ConnectionDrawingPolicy = new FConnectionDrawingPolicy(WireLayerId, MaxLayerId, ZoomFactor, MyClippingRect, OutDrawElements); } } } TArray<TSharedPtr<SGraphPin>> OverridePins; for (const FGraphPinHandle& Handle : PreviewConnectorFromPins) { TSharedPtr<SGraphPin> Pin = Handle.FindInGraphPanel(*this); if (Pin.IsValid()) { OverridePins.Add(Pin); } } ConnectionDrawingPolicy->SetHoveredPins(CurrentHoveredPins, OverridePins, TimeSinceMouseEnteredPin); ConnectionDrawingPolicy->SetMarkedPin(MarkedPin); // Get the set of pins for all children and synthesize geometry for culled out pins so lines can be drawn to them. TMap<TSharedRef<SWidget>, FArrangedWidget> PinGeometries; TSet< TSharedRef<SWidget> > VisiblePins; for (int32 ChildIndex = 0; ChildIndex < Children.Num(); ++ChildIndex) { TSharedRef<SGraphNode> ChildNode = StaticCastSharedRef<SGraphNode>(Children[ChildIndex]); // If this is a culled node, approximate the pin geometry to the corner of the node it is within if (IsNodeCulled(ChildNode, AllottedGeometry)) { TArray< TSharedRef<SWidget> > NodePins; ChildNode->GetPins(NodePins); const FVector2D NodeLoc = ChildNode->GetPosition(); const FGeometry SynthesizedNodeGeometry(GraphCoordToPanelCoord(NodeLoc), AllottedGeometry.AbsolutePosition, FVector2D::ZeroVector, 1.f); for (TArray< TSharedRef<SWidget> >::TConstIterator NodePinIterator(NodePins); NodePinIterator; ++NodePinIterator) { const SGraphPin& PinWidget = static_cast<const SGraphPin&>((*NodePinIterator).Get()); FVector2D PinLoc = NodeLoc + PinWidget.GetNodeOffset(); const FGeometry SynthesizedPinGeometry(GraphCoordToPanelCoord(PinLoc), AllottedGeometry.AbsolutePosition, FVector2D::ZeroVector, 1.f); PinGeometries.Add(*NodePinIterator, FArrangedWidget(*NodePinIterator, SynthesizedPinGeometry)); } // Also add synthesized geometries for culled nodes ArrangedChildren.AddWidget( FArrangedWidget(ChildNode, SynthesizedNodeGeometry) ); } else { ChildNode->GetPins(VisiblePins); } } // Now get the pin geometry for all visible children and append it to the PinGeometries map TMap<TSharedRef<SWidget>, FArrangedWidget> VisiblePinGeometries; { this->FindChildGeometries(AllottedGeometry, VisiblePins, VisiblePinGeometries); PinGeometries.Append(VisiblePinGeometries); } // Draw preview connections (only connected on one end) if (PreviewConnectorFromPins.Num() > 0) { for (const FGraphPinHandle& Handle : PreviewConnectorFromPins) { TSharedPtr< SGraphPin > CurrentStartPin = Handle.FindInGraphPanel(*this); if (!CurrentStartPin.IsValid()) { continue; } const FArrangedWidget* PinGeometry = PinGeometries.Find( CurrentStartPin.ToSharedRef() ); if (PinGeometry != NULL) { FVector2D StartPoint; FVector2D EndPoint; if (CurrentStartPin->GetDirection() == EGPD_Input) { StartPoint = AllottedGeometry.AbsolutePosition + PreviewConnectorEndpoint; EndPoint = FGeometryHelper::VerticalMiddleLeftOf( PinGeometry->Geometry ) - FVector2D(ConnectionDrawingPolicy->ArrowRadius.X, 0); } else { StartPoint = FGeometryHelper::VerticalMiddleRightOf( PinGeometry->Geometry ); EndPoint = AllottedGeometry.AbsolutePosition + PreviewConnectorEndpoint; } ConnectionDrawingPolicy->DrawPreviewConnector(PinGeometry->Geometry, StartPoint, EndPoint, CurrentStartPin.Get()->GetPinObj()); } //@TODO: Re-evaluate this incompatible mojo; it's mutating every pin state every frame to accomplish a visual effect ConnectionDrawingPolicy->SetIncompatiblePinDrawState(CurrentStartPin, VisiblePins); } } else { //@TODO: Re-evaluate this incompatible mojo; it's mutating every pin state every frame to accomplish a visual effect ConnectionDrawingPolicy->ResetIncompatiblePinDrawState(VisiblePins); } // Draw all regular connections ConnectionDrawingPolicy->Draw(PinGeometries, ArrangedChildren); delete ConnectionDrawingPolicy; } // Draw a shadow overlay around the edges of the graph ++MaxLayerId; PaintSurroundSunkenShadow(FEditorStyle::GetBrush(TEXT("Graph.Shadow")), AllottedGeometry, MyClippingRect, OutDrawElements, MaxLayerId); if(ShowGraphStateOverlay.Get()) { const FSlateBrush* BorderBrush = nullptr; if((GEditor->bIsSimulatingInEditor || GEditor->PlayWorld != NULL)) { // Draw a surrounding indicator when PIE is active, to make it clear that the graph is read-only, etc... BorderBrush = FEditorStyle::GetBrush(TEXT("Graph.PlayInEditor")); } else if(!IsEditable.Get()) { // Draw a different border when we're not simulating but the graph is read-only BorderBrush = FEditorStyle::GetBrush(TEXT("Graph.ReadOnlyBorder")); } if(BorderBrush) { // Actually draw the border FSlateDrawElement::MakeBox( OutDrawElements, MaxLayerId, AllottedGeometry.ToPaintGeometry(), BorderBrush, MyClippingRect ); } } // Draw the marquee selection rectangle PaintMarquee(AllottedGeometry, MyClippingRect, OutDrawElements, MaxLayerId); // Draw the software cursor ++MaxLayerId; PaintSoftwareCursor(AllottedGeometry, MyClippingRect, OutDrawElements, MaxLayerId); return MaxLayerId; }
FReply SPropertyEditorClass::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) { TSharedPtr<FClassDragDropOp> ClassOperation = DragDropEvent.GetOperationAs<FClassDragDropOp>(); if (ClassOperation.IsValid()) { // We can only drop one item into the combo box, so drop the first one. FString AssetName = ClassOperation->ClassesToDrop[0]->GetName(); // Set the property, it will be verified as valid. SendToObjects(AssetName); return FReply::Handled(); } TSharedPtr<FUnloadedClassDragDropOp> UnloadedClassOp = DragDropEvent.GetOperationAs<FUnloadedClassDragDropOp>(); if (UnloadedClassOp.IsValid()) { // Check if the asset is loaded, used to see if the context menu should be available bool bAllAssetWereLoaded = true; TArray<FClassPackageData>& AssetArray = *(UnloadedClassOp->AssetsToDrop.Get()); // We can only drop one item into the combo box, so drop the first one. FString& AssetName = AssetArray[0].AssetName; // Check to see if the asset can be found, otherwise load it. UObject* Object = FindObject<UObject>(NULL, *AssetName); if(Object == NULL) { // Check to see if the dropped asset was a blueprint const FString& PackageName = AssetArray[0].GeneratedPackageName; Object = FindObject<UObject>(NULL, *FString::Printf(TEXT("%s.%s"), *PackageName, *AssetName)); if(Object == NULL) { // Load the package. GWarn->BeginSlowTask(LOCTEXT("OnDrop_LoadPackage", "Fully Loading Package For Drop"), true, false); UPackage* Package = LoadPackage(NULL, *PackageName, LOAD_NoRedirects ); if(Package) { Package->FullyLoad(); } GWarn->EndSlowTask(); Object = FindObject<UObject>(Package, *AssetName); } if(Object->IsA(UBlueprint::StaticClass())) { // Get the default object from the generated class. Object = Cast<UBlueprint>(Object)->GeneratedClass->GetDefaultObject(); } } // Set the property, it will be verified as valid. SendToObjects(AssetName); return FReply::Handled(); } return FReply::Unhandled(); }
void FPropertyEditorToolkit::Initialize( const EToolkitMode::Type Mode, const TSharedPtr< class IToolkitHost >& InitToolkitHost, const TArray<UObject*>& ObjectsToEdit ) { CreatePropertyTree(); CreatePropertyTable(); TArray< UObject* > AdjustedObjectsToEdit; for( auto ObjectIter = ObjectsToEdit.CreateConstIterator(); ObjectIter; ++ObjectIter ) { //@todo Remove this and instead extend the blueprints Edit Defaults editor to use a Property Table as well [12/6/2012 Justin.Sargent] UObject* Object = *ObjectIter; if ( Object->IsA( UBlueprint::StaticClass() ) ) { UBlueprint* Blueprint = Cast<UBlueprint>( Object ); // Make sure that the generated class is valid, in case the super has been removed, and this class can't be loaded. if( Blueprint->GeneratedClass != NULL ) { AdjustedObjectsToEdit.Add( Blueprint->GeneratedClass->GetDefaultObject() ); } } else { AdjustedObjectsToEdit.Add( Object ); } } PropertyTable->SetObjects( AdjustedObjectsToEdit ); TableColumnsChanged(); TSharedRef<FTabManager::FLayout> StandaloneDefaultLayout = FTabManager::NewLayout( "Standalone_PropertyEditorToolkit_Layout" ) ->AddArea ( FTabManager::NewPrimaryArea() ->SetOrientation(Orient_Horizontal) ->Split ( FTabManager::NewStack() ->SetSizeCoefficient(0.8f) ->AddTab(GridTabId, ETabState::OpenedTab) ) ->Split ( FTabManager::NewStack() ->SetSizeCoefficient(0.2f) ->SetHideTabWell( true ) ->AddTab(TreeTabId, ETabState::OpenedTab) ) ); const bool bCreateDefaultStandaloneMenu = true; const bool bCreateDefaultToolbar = false; FAssetEditorToolkit::InitAssetEditor( Mode, InitToolkitHost, ApplicationId, StandaloneDefaultLayout, bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, AdjustedObjectsToEdit ); TArray< TWeakObjectPtr<UObject> > AdjustedObjectsToEditWeak; for( auto ObjectIter = AdjustedObjectsToEdit.CreateConstIterator(); ObjectIter; ++ObjectIter ) { AdjustedObjectsToEditWeak.Add(*ObjectIter); } PropertyTree->SetObjectArray( AdjustedObjectsToEditWeak ); PinColor = FSlateColor( FLinearColor( 1, 1, 1, 0 ) ); GEditor->GetTimerManager()->SetTimer( TimerHandle_TickPinColor, FTimerDelegate::CreateSP(this, &FPropertyEditorToolkit::TickPinColorAndOpacity), 0.1f, true ); }
void UEnvironmentQueryGraph::RemoveOrphanedNodes() { UEnvQuery* QueryAsset = CastChecked<UEnvQuery>(GetOuter()); // Obtain a list of all nodes that should be in the asset TSet<UObject*> AllNodes; for (int32 Index = 0; Index < Nodes.Num(); ++Index) { UEnvironmentQueryGraphNode_Option* OptionNode = Cast<UEnvironmentQueryGraphNode_Option>(Nodes[Index]); if (OptionNode) { UEnvQueryOption* OptionInstance = Cast<UEnvQueryOption>(OptionNode->NodeInstance); if (OptionInstance) { AllNodes.Add(OptionInstance); if (OptionInstance->Generator) { AllNodes.Add(OptionInstance->Generator); } } for (int32 SubIdx = 0; SubIdx < OptionNode->Tests.Num(); SubIdx++) { if (OptionNode->Tests[SubIdx] && OptionNode->Tests[SubIdx]->NodeInstance) { AllNodes.Add(OptionNode->Tests[SubIdx]->NodeInstance); } } } } // Obtain a list of all nodes actually in the asset and discard unused nodes TArray<UObject*> AllInners; const bool bIncludeNestedObjects = false; GetObjectsWithOuter(QueryAsset, AllInners, bIncludeNestedObjects); for (auto InnerIt = AllInners.CreateConstIterator(); InnerIt; ++InnerIt) { UObject* Node = *InnerIt; const bool bEQSNode = Node->IsA(UEnvQueryGenerator::StaticClass()) || Node->IsA(UEnvQueryTest::StaticClass()) || Node->IsA(UEnvQueryOption::StaticClass()); if (bEQSNode && !AllNodes.Contains(Node)) { Node->SetFlags(RF_Transient); Node->Rename(NULL, GetTransientPackage(), REN_DontCreateRedirectors | REN_NonTransactional | REN_ForceNoResetLoaders); } } }