FText FSimpleAssetEditor::GetToolkitName() const { const TArray<UObject*>& EditingObjs = GetEditingObjects(); check( EditingObjs.Num() > 0 ); FFormatNamedArguments Args; Args.Add( TEXT("ToolkitName"), GetBaseToolkitName() ); if( EditingObjs.Num() == 1 ) { const UObject* EditingObject = EditingObjs[ 0 ]; const bool bDirtyState = EditingObject->GetOutermost()->IsDirty(); Args.Add( TEXT("ObjectName"), FText::FromString( EditingObject->GetName() ) ); Args.Add( TEXT("DirtyState"), bDirtyState ? FText::FromString( TEXT( "*" ) ) : FText::GetEmpty() ); return FText::Format( LOCTEXT("ToolkitTitle", "{ObjectName}{DirtyState} - {ToolkitName}"), Args ); } else { bool bDirtyState = false; UClass* SharedBaseClass = nullptr; for( int32 x = 0; x < EditingObjs.Num(); ++x ) { UObject* Obj = EditingObjs[ x ]; check( Obj ); UClass* ObjClass = Cast<UClass>(Obj); if (ObjClass == nullptr) { ObjClass = Obj->GetClass(); } check( ObjClass ); // Initialize with the class of the first object we encounter. if( SharedBaseClass == nullptr ) { SharedBaseClass = ObjClass; } // If we've encountered an object that's not a subclass of the current best baseclass, // climb up a step in the class hierarchy. while( !ObjClass->IsChildOf( SharedBaseClass ) ) { SharedBaseClass = SharedBaseClass->GetSuperClass(); } // If any of the objects are dirty, flag the label bDirtyState |= Obj->GetOutermost()->IsDirty(); } check(SharedBaseClass); Args.Add( TEXT("NumberOfObjects"), EditingObjs.Num() ); Args.Add( TEXT("ClassName"), FText::FromString( SharedBaseClass->GetName() ) ); Args.Add( TEXT("DirtyState"), bDirtyState ? FText::FromString( TEXT( "*" ) ) : FText::GetEmpty() ); return FText::Format( LOCTEXT("ToolkitTitle_EditingMultiple", "{NumberOfObjects} {ClassName}{DirtyState} - {ToolkitName}"), Args ); } }
void FAssetEditorManager::SaveOpenAssetEditors(bool bOnShutdown) { if(!bSavingOnShutdown) { TArray<FString> OpenAssets; // Don't save a list of assets to restore if we are running under a debugger if(!FPlatformMisc::IsDebuggerPresent()) { for (auto EditorPair : OpenedEditors) { IAssetEditorInstance* Editor = EditorPair.Key; if (Editor != NULL) { UObject* EditedObject = EditorPair.Value; if(EditedObject != NULL) { // only record assets that have a valid saved package UPackage* Package = EditedObject->GetOutermost(); if(Package != NULL && Package->GetFileSize() != 0) { OpenAssets.Add(EditedObject->GetPathName()); } } } } } GConfig->SetArray(TEXT("AssetEditorManager"), TEXT("OpenAssetsAtExit"), OpenAssets, GEditorPerProjectIni); GConfig->SetBool(TEXT("AssetEditorManager"), TEXT("CleanShutdown"), bOnShutdown, GEditorPerProjectIni); GConfig->Flush(false, GEditorPerProjectIni); } }
bool USelection::Modify(bool bAlwaysMarkDirty/* =true */) { // If the selection currently contains any PIE objects we should not be including it in the transaction buffer for (TWeakObjectPtr<UObject>& ObjectPtr : SelectedObjects) { UObject* Object = ObjectPtr.Get(); if (Object && Object->GetOutermost()->HasAnyPackageFlags(PKG_PlayInEditor | PKG_ContainsScript | PKG_CompiledIn)) { return false; } } return Super::Modify(bAlwaysMarkDirty); }
bool FTransaction::FObjectRecord::ContainsPieObject() const { { UObject* Obj = Object.Object; if(Obj && Obj->GetOutermost()->HasAnyPackageFlags(PKG_PlayInEditor)) { return true; } } for (const FPersistentObjectRef& ReferencedObject : ReferencedObjects) { const UObject* Obj = ReferencedObject.Object; if( Obj && Obj->GetOutermost()->HasAnyPackageFlags(PKG_PlayInEditor)) { return true; } } return false; }
FGuid FSequencerActorBindingManager::FindGuidForObject( const UMovieScene& MovieScene, UObject& Object ) const { FGuid ObjectGuid = FindSpawnableGuidForPuppetObject( &Object ); if( !ObjectGuid.IsValid() ) { // Spawnable // Is this a game preview object? const bool bIsGamePreviewObject = !!( Object.GetOutermost()->PackageFlags & PKG_PlayInEditor ); if( bIsGamePreviewObject ) { // OK, so someone is asking for a handle to an object from a game preview session, probably because // they want to capture keys during live simulation. // Check to see if we already have a puppet that was generated from a recording of this game preview object // @todo sequencer livecapture: We could support recalling counterpart by full name instead of weak pointer, to allow "overdubbing" of previously recorded actors, when the new actors in the current play session have the same path name // @todo sequencer livecapture: Ideally we could capture from editor-world actors that are "puppeteered" as well (real time) const FMovieSceneSpawnable* FoundSpawnable = MovieScene.FindSpawnableForCounterpart( &Object ); if( FoundSpawnable != NULL ) { ObjectGuid = FoundSpawnable->GetGuid(); } } else { BindToPlayMovieSceneNode( false ); // Possessable // When editing within the level editor, make sure we're bound to the level script node which contains data about possessables. if( PlayMovieSceneNode.IsValid() ) { ObjectGuid = PlayMovieSceneNode->FindGuidForObject( &Object ); } } } return ObjectGuid; }
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); } }
/** * Nulls out references to a given object * * @param InObject - Object to null references to */ void NullReferencesToObject(UObject* InObject) { TArray<UObject*> ReplaceableObjects; TMap<UObject*, UObject*> ReplacementMap; ReplacementMap.Add(InObject, NULL); ReplacementMap.GenerateKeyArray(ReplaceableObjects); // Find all the properties (and their corresponding objects) that refer to any of the objects to be replaced TMap< UObject*, TArray<UProperty*> > ReferencingPropertiesMap; for (FObjectIterator ObjIter; ObjIter; ++ObjIter) { UObject* CurObject = *ObjIter; // Find the referencers of the objects to be replaced FFindReferencersArchive FindRefsArchive(CurObject, ReplaceableObjects); // Inform the object referencing any of the objects to be replaced about the properties that are being forcefully // changed, and store both the object doing the referencing as well as the properties that were changed in a map (so that // we can correctly call PostEditChange later) TMap<UObject*, int32> CurNumReferencesMap; TMultiMap<UObject*, UProperty*> CurReferencingPropertiesMMap; if (FindRefsArchive.GetReferenceCounts(CurNumReferencesMap, CurReferencingPropertiesMMap) > 0) { TArray<UProperty*> CurReferencedProperties; CurReferencingPropertiesMMap.GenerateValueArray(CurReferencedProperties); ReferencingPropertiesMap.Add(CurObject, CurReferencedProperties); for (TArray<UProperty*>::TConstIterator RefPropIter(CurReferencedProperties); RefPropIter; ++RefPropIter) { CurObject->PreEditChange(*RefPropIter); } } } // Iterate over the map of referencing objects/changed properties, forcefully replacing the references and then // alerting the referencing objects the change has completed via PostEditChange int32 NumObjsReplaced = 0; for (TMap< UObject*, TArray<UProperty*> >::TConstIterator MapIter(ReferencingPropertiesMap); MapIter; ++MapIter) { ++NumObjsReplaced; UObject* CurReplaceObj = MapIter.Key(); const TArray<UProperty*>& RefPropArray = MapIter.Value(); FArchiveReplaceObjectRef<UObject> ReplaceAr(CurReplaceObj, ReplacementMap, false, true, false); for (TArray<UProperty*>::TConstIterator RefPropIter(RefPropArray); RefPropIter; ++RefPropIter) { FPropertyChangedEvent PropertyEvent(*RefPropIter); CurReplaceObj->PostEditChangeProperty(PropertyEvent); } if (!CurReplaceObj->HasAnyFlags(RF_Transient) && CurReplaceObj->GetOutermost() != GetTransientPackage()) { if (!CurReplaceObj->RootPackageHasAnyFlags(PKG_CompiledIn)) { CurReplaceObj->MarkPackageDirty(); } } } }
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 FRedirectCollector::ResolveStringAssetReference(FString FilterPackage) { SCOPE_REDIRECT_TIMER(ResolveTimeTotal); FName FilterPackageFName = NAME_None; if (!FilterPackage.IsEmpty()) { FilterPackageFName = FName(*FilterPackage); } TMultiMap<FName, FPackagePropertyPair> SkippedReferences; SkippedReferences.Empty(StringAssetReferences.Num()); while ( StringAssetReferences.Num()) { TMultiMap<FName, FPackagePropertyPair> CurrentReferences; Swap(StringAssetReferences, CurrentReferences); for (const auto& CurrentReference : CurrentReferences) { const FName& ToLoadFName = CurrentReference.Key; const FPackagePropertyPair& RefFilenameAndProperty = CurrentReference.Value; if ((FilterPackageFName != NAME_None) && // not using a filter (FilterPackageFName != RefFilenameAndProperty.GetCachedPackageName()) && // this is the package we are looking for (RefFilenameAndProperty.GetCachedPackageName() != NAME_None) // if we have an empty package name then process it straight away ) { // If we have a valid filter and it doesn't match, skip this reference SkippedReferences.Add(ToLoadFName, RefFilenameAndProperty); continue; } const FString ToLoad = ToLoadFName.ToString(); if (FCoreDelegates::LoadStringAssetReferenceInCook.IsBound()) { SCOPE_REDIRECT_TIMER(ResolveTimeDelegate); if (FCoreDelegates::LoadStringAssetReferenceInCook.Execute(ToLoad) == false) { // Skip this reference continue; } } if (ToLoad.Len() > 0 ) { SCOPE_REDIRECT_TIMER(ResolveTimeLoad); UE_LOG(LogRedirectors, Verbose, TEXT("String Asset Reference '%s'"), *ToLoad); UE_CLOG(RefFilenameAndProperty.GetProperty().ToString().Len(), LogRedirectors, Verbose, TEXT(" Referenced by '%s'"), *RefFilenameAndProperty.GetProperty().ToString()); StringAssetRefFilenameStack.Push(RefFilenameAndProperty.GetPackage().ToString()); UObject *Loaded = LoadObject<UObject>(NULL, *ToLoad, NULL, RefFilenameAndProperty.GetReferencedByEditorOnlyProperty() ? LOAD_EditorOnly : LOAD_None, NULL); StringAssetRefFilenameStack.Pop(); UObjectRedirector* Redirector = dynamic_cast<UObjectRedirector*>(Loaded); if (Redirector) { UE_LOG(LogRedirectors, Verbose, TEXT(" Found redir '%s'"), *Redirector->GetFullName()); FRedirection Redir; Redir.PackageFilename = RefFilenameAndProperty.GetPackage().ToString(); Redir.RedirectorName = Redirector->GetFullName(); Redir.RedirectorPackageFilename = Redirector->GetLinker()->Filename; CA_SUPPRESS(28182) Redir.DestinationObjectName = Redirector->DestinationObject->GetFullName(); Redirections.AddUnique(Redir); Loaded = Redirector->DestinationObject; } if (Loaded) { if (FCoreUObjectDelegates::PackageLoadedFromStringAssetReference.IsBound()) { FCoreUObjectDelegates::PackageLoadedFromStringAssetReference.Broadcast(Loaded->GetOutermost()->GetFName()); } FString Dest = Loaded->GetPathName(); UE_LOG(LogRedirectors, Verbose, TEXT(" Resolved to '%s'"), *Dest); if (Dest != ToLoad) { StringAssetRemap.Add(ToLoad, Dest); } } else { const FString Referencer = RefFilenameAndProperty.GetProperty().ToString().Len() ? RefFilenameAndProperty.GetProperty().ToString() : TEXT("Unknown"); int32 DotIndex = ToLoad.Find(TEXT(".")); FString PackageName = DotIndex != INDEX_NONE ? ToLoad.Left(DotIndex) : ToLoad; if (FLinkerLoad::IsKnownMissingPackage(FName(*PackageName)) == false) { UE_LOG(LogRedirectors, Warning, TEXT("String Asset Reference '%s' was not found! (Referencer '%s')"), *ToLoad, *Referencer); } } } } } check(StringAssetReferences.Num() == 0); // Add any skipped references back into the map for the next time this is called Swap(StringAssetReferences, SkippedReferences); // we shouldn't have any references left if we decided to resolve them all check((StringAssetReferences.Num() == 0) || (FilterPackageFName != NAME_None)); }
void FContentDirectoryMonitor::ProcessModifications(const DirectoryWatcher::FTimeLimit& TimeLimit, TArray<UPackage*>& OutPackagesToSave, FReimportFeedbackContext& Context) { auto* ReimportManager = FReimportManager::Instance(); for (int32 Index = 0; Index < ModifiedFiles.Num(); ++Index) { Context.MainTask->EnterProgressFrame(); auto& Change = ModifiedFiles[Index]; const FString FullFilename = Cache.GetDirectory() + Change.Filename.Get(); // Move the asset before reimporting it. We always reimport moved assets to ensure that their import path is up to date if (Change.Action == DirectoryWatcher::EFileAction::Moved) { const FString OldFilename = Cache.GetDirectory() + Change.MovedFromFilename.Get(); const auto Assets = Utils::FindAssetsPertainingToFile(*Registry, OldFilename); if (Assets.Num() == 1) { UObject* Asset = Assets[0].GetAsset(); if (Asset && Utils::ExtractSourceFilePaths(Asset).Num() == 1) { UPackage* ExistingPackage = Asset->GetOutermost(); const bool bAssetWasDirty = IsAssetDirty(Asset); const FString NewAssetName = ObjectTools::SanitizeObjectName(FPaths::GetBaseFilename(Change.Filename.Get())); const FString PackagePath = PackageTools::SanitizePackageName(MountedContentPath / FPaths::GetPath(Change.Filename.Get())); const FString FullDestPath = PackagePath / NewAssetName; if (ExistingPackage && ExistingPackage->FileName.ToString() == FullDestPath) { // No need to process this asset - it's already been moved to the right location Cache.CompleteTransaction(MoveTemp(Change)); continue; } const FText SrcPathText = FText::FromString(Assets[0].PackageName.ToString()), DstPathText = FText::FromString(FullDestPath); if (FPackageName::DoesPackageExist(*FullDestPath)) { Context.AddMessage(EMessageSeverity::Warning, FText::Format(LOCTEXT("MoveWarning_ExistingAsset", "Can't move {0} to {1} - one already exists."), SrcPathText, DstPathText)); } else { TArray<FAssetRenameData> RenameData; RenameData.Emplace(Asset, PackagePath, NewAssetName); Context.AddMessage(EMessageSeverity::Info, FText::Format(LOCTEXT("Success_MovedAsset", "Moving asset {0} to {1}."), SrcPathText, DstPathText)); FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get().RenameAssets(RenameData); TArray<FString> Filenames; Filenames.Add(FullFilename); // Update the reimport file names FReimportManager::Instance()->UpdateReimportPaths(Asset, Filenames); Asset->MarkPackageDirty(); if (!bAssetWasDirty) { OutPackagesToSave.Add(Asset->GetOutermost()); } } } } } else { // Modifications or additions are treated the same by this point for (const auto& AssetData : Utils::FindAssetsPertainingToFile(*Registry, FullFilename)) { if (UObject* Asset = AssetData.GetAsset()) { ReimportAsset(Asset, FullFilename, OutPackagesToSave, Context); } } } // Let the cache know that we've dealt with this change Cache.CompleteTransaction(MoveTemp(Change)); if (TimeLimit.Exceeded()) { ModifiedFiles.RemoveAt(0, Index + 1); return; } } ModifiedFiles.Empty(); }
/** * Exec handler, parsing the passed in command * * @param InWorld World Context * @param Cmd Command to parse * @param Ar output device used for logging */ bool FDebugToolExec::Exec( UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar ) { // these commands are only allowed in standalone games #if UE_BUILD_SHIPPING || UE_BUILD_TEST if (GEngine->GetNetMode(InWorld) != NM_Standalone || (GEngine->GetWorldContextFromWorldChecked(InWorld).PendingNetGame != NULL)) { return 0; } // Edits the class defaults. else #endif if( FParse::Command(&Cmd,TEXT("EDITDEFAULT")) ) { // not allowed in the editor as this command can have far reaching effects such as impacting serialization if (!GIsEditor) { UClass* Class = NULL; if( ParseObject<UClass>( Cmd, TEXT("CLASS="), Class, ANY_PACKAGE ) == false ) { TCHAR ClassName[256]; if ( FParse::Token(Cmd,ClassName,ARRAY_COUNT(ClassName), 1) ) { Class = FindObject<UClass>( ANY_PACKAGE, ClassName); } } if (Class) { EditObject(Class->GetDefaultObject(), true); } else { Ar.Logf( TEXT("Missing class") ); } } return 1; } else if (FParse::Command(&Cmd,TEXT("EDITOBJECT"))) { UClass* searchClass = NULL; UObject* foundObj = NULL; // Search by class. if (ParseObject<UClass>(Cmd, TEXT("CLASS="), searchClass, ANY_PACKAGE)) { // pick the first valid object for (FObjectIterator It(searchClass); It && foundObj == NULL; ++It) { if (!It->IsPendingKill() && !It->IsTemplate()) { foundObj = *It; } } } // Search by name. else { FName searchName; FString SearchPathName; if ( FParse::Value(Cmd, TEXT("NAME="), searchName) ) { // Look for actor by name. for( TObjectIterator<UObject> It; It && foundObj == NULL; ++It ) { if (It->GetFName() == searchName) { foundObj = *It; } } } else if ( FParse::Token(Cmd,SearchPathName, true) ) { foundObj = FindObject<UObject>(ANY_PACKAGE,*SearchPathName); } } // Bring up an property editing window for the found object. if (foundObj != NULL) { // not allowed in the editor unless it is a PIE object as this command can have far reaching effects such as impacting serialization if (!GIsEditor || ((!foundObj->IsTemplate() && (foundObj->GetOutermost()->PackageFlags & PKG_PlayInEditor)))) { EditObject(foundObj, true); } } else { Ar.Logf(TEXT("Target not found")); } return 1; } else if (FParse::Command(&Cmd,TEXT("EDITARCHETYPE"))) { UObject* foundObj = NULL; // require fully qualified path name FString SearchPathName; if (FParse::Token(Cmd, SearchPathName, true)) { foundObj = FindObject<UObject>(ANY_PACKAGE,*SearchPathName); } // Bring up an property editing window for the found object. if (foundObj != NULL) { // not allowed in the editor unless it is a PIE object as this command can have far reaching effects such as impacting serialization if (!GIsEditor || ((!foundObj->IsTemplate() && (foundObj->GetOutermost()->PackageFlags & PKG_PlayInEditor)))) { EditObject(foundObj, false); } } else { Ar.Logf(TEXT("Target not found")); } return 1; } // Edits an objects properties or copies them to the clipboard. else if( FParse::Command(&Cmd,TEXT("EDITACTOR")) ) { UClass* Class = NULL; AActor* Found = NULL; if (FParse::Command(&Cmd, TEXT("TRACE"))) { APlayerController* PlayerController = InWorld->GetFirstPlayerController(); if (PlayerController != NULL) { // Do a trace in the player's facing direction and edit anything that's hit. FVector PlayerLocation; FRotator PlayerRotation; PlayerController->GetPlayerViewPoint(PlayerLocation, PlayerRotation); FHitResult Hit(1.0f); PlayerController->GetWorld()->LineTraceSingle(Hit, PlayerLocation, PlayerLocation + PlayerRotation.Vector() * 10000.f, ECC_Pawn, FCollisionQueryParams(NAME_None, true, PlayerController->GetPawn())); Found = Hit.GetActor(); } } // Search by class. else if( ParseObject<UClass>( Cmd, TEXT("CLASS="), Class, ANY_PACKAGE ) && Class->IsChildOf(AActor::StaticClass()) ) { UGameEngine* GameEngine = Cast<UGameEngine>(GEngine); // Look for the closest actor of this class to the player. FVector PlayerLocation(0.0f); APlayerController* PlayerController = InWorld->GetFirstPlayerController(); if (PlayerController != NULL) { FRotator DummyRotation; PlayerController->GetPlayerViewPoint(PlayerLocation, DummyRotation); } float MinDist = FLT_MAX; for( TActorIterator<AActor> It(InWorld, Class); It; ++It ) { if ( !It->IsPendingKill() ) { float const Dist = (PlayerController && It->GetRootComponent()) ? FVector::Dist(It->GetActorLocation(), PlayerLocation) : 0.f; if (Dist < MinDist) { MinDist = Dist; Found = *It; } } } } // Search by name. else { FName ActorName; if( FParse::Value( Cmd, TEXT("NAME="), ActorName ) ) { // Look for actor by name. for( FActorIterator It(InWorld); It; ++It ) { if( It->GetFName() == ActorName ) { Found = *It; break; } } } } // Bring up an property editing window for the found object. if( Found ) { // not allowed in the editor unless it is a PIE object as this command can have far reaching effects such as impacting serialization if (!GIsEditor || ((!Found->IsTemplate() && (Found->GetOutermost()->PackageFlags & PKG_PlayInEditor)))) { EditObject(Found, true); } } else { Ar.Logf( TEXT("Target not found") ); } return 1; } else { return 0; } }