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); } }
/** * Internal version of GetPathName() that eliminates unnecessary copies. */ void UObjectBaseUtility::GetPathName( const UObject* StopOuter, FString& ResultString ) const { if( this != StopOuter && this != NULL ) { UObject* ObjOuter = GetOuter(); if (ObjOuter && ObjOuter != StopOuter ) { ObjOuter->GetPathName( StopOuter, ResultString ); // SUBOBJECT_DELIMITER is used to indicate that this object's outer is not a UPackage if (ObjOuter->GetClass() != UPackage::StaticClass() && ObjOuter->GetOuter()->GetClass() == UPackage::StaticClass()) { ResultString += SUBOBJECT_DELIMITER; } else { ResultString += TEXT("."); } } AppendName(ResultString); } else { ResultString += TEXT("None"); } }
bool FStringAssetReference::SerializeFromMismatchedTag(struct FPropertyTag const& Tag, FArchive& Ar) { if (Tag.Type == NAME_ObjectProperty) { UObject* Object = NULL; Ar << Object; if (Object) { AssetLongPathname = Object->GetPathName(); } else { AssetLongPathname = FString(); } return true; } else if( Tag.Type == NAME_StrProperty ) { FString String; Ar << String; AssetLongPathname = String; return true; } return false; }
void UInterfaceProperty::ExportTextItem( FString& ValueStr, const void* PropertyValue, const void* DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope ) const { FScriptInterface* InterfaceValue = (FScriptInterface*)PropertyValue; UObject* Temp = InterfaceValue->GetObject(); if (0 != (PortFlags & PPF_ExportCpp)) { const FString GetObjectStr = Temp ? FString::Printf(TEXT("LoadObject<UObject>(nullptr, TEXT(\"%s\"))"), *Temp->GetPathName().ReplaceCharWithEscapedChar()) : TEXT(""); ValueStr += FString::Printf(TEXT("TScriptInterface<I%s>(%s)") , (InterfaceClass ? *InterfaceClass->GetName() : TEXT("Interface")) , *GetObjectStr); return; } if( Temp != NULL ) { bool bExportFullyQualified = true; // When exporting from one package or graph to another package or graph, we don't want to fully qualify the name, as it may refer // to a level or graph that doesn't exist or cause a linkage to a node in a different graph UObject* StopOuter = NULL; if (PortFlags & PPF_ExportsNotFullyQualified) { StopOuter = (ExportRootScope || (Parent == NULL)) ? ExportRootScope : Parent->GetOutermost(); bExportFullyQualified = !Temp->IsIn(StopOuter); } // if we want a full qualified object reference, use the pathname, otherwise, use just the object name if (bExportFullyQualified) { StopOuter = NULL; if ( (PortFlags&PPF_SimpleObjectText) != 0 && Parent != NULL ) { StopOuter = Parent->GetOutermost(); } } ValueStr += FString::Printf( TEXT("%s'%s'"), *Temp->GetClass()->GetName(), *Temp->GetPathName(StopOuter) ); } else { ValueStr += TEXT("None"); } }
void UPackageMap::LogDebugInfo(FOutputDevice& Ar) { for (auto It = Cache->ObjectLookup.CreateIterator(); It; ++It) { FNetworkGUID NetGUID = It.Key(); UObject *Obj = It.Value().Object.Get(); UE_LOG(LogCoreNet, Log, TEXT("%s - %s"), *NetGUID.ToString(), Obj ? *Obj->GetPathName() : TEXT("NULL")); } }
void SMaterialEditorViewport::OnSetPreviewMeshFromSelection() { bool bFoundPreviewMesh = false; FEditorDelegates::LoadSelectedAssetsIfNeeded.Broadcast(); UMaterialInterface* MaterialInterface = MaterialEditorPtr.Pin()->GetMaterialInterface(); // Look for a selected asset that can be converted to a mesh component for (FSelectionIterator SelectionIt(*GEditor->GetSelectedObjects()); SelectionIt && !bFoundPreviewMesh; ++SelectionIt) { UObject* TestAsset = *SelectionIt; if (TestAsset->IsAsset()) { if (TSubclassOf<UActorComponent> ComponentClass = FComponentAssetBrokerage::GetPrimaryComponentForAsset(TestAsset->GetClass())) { if (ComponentClass->IsChildOf(UMeshComponent::StaticClass())) { if (USkeletalMesh* SkeletalMesh = Cast<USkeletalMesh>(TestAsset)) { // Special case handling for skeletal meshes, sets the material to be usable with them if (MaterialInterface->GetMaterial()) { bool bNeedsRecompile = false; MaterialInterface->GetMaterial()->SetMaterialUsage(bNeedsRecompile, MATUSAGE_SkeletalMesh); } } SetPreviewAsset(TestAsset); MaterialInterface->PreviewMesh = TestAsset->GetPathName(); bFoundPreviewMesh = true; } } } } if (bFoundPreviewMesh) { FMaterialEditor::UpdateThumbnailInfoPreviewMesh(MaterialInterface); MaterialInterface->MarkPackageDirty(); RefreshViewport(); } else { FSuppressableWarningDialog::FSetupInfo Info(NSLOCTEXT("UnrealEd", "Warning_NoPreviewMeshFound_Message", "You need to select a mesh-based asset in the content browser to preview it."), NSLOCTEXT("UnrealEd", "Warning_NoPreviewMeshFound", "Warning: No Preview Mesh Found"), "Warning_NoPreviewMeshFound"); Info.ConfirmText = NSLOCTEXT("UnrealEd", "Warning_NoPreviewMeshFound_Confirm", "Continue"); FSuppressableWarningDialog NoPreviewMeshWarning( Info ); NoPreviewMeshWarning.ShowModal(); } }
void FRedirectCollector::ResolveStringAssetReference() { while (StringAssetReferences.Num()) { TMultiMap<FString, FString>::TIterator First(StringAssetReferences); FString ToLoad = First.Key(); FString RefFilename = First.Value(); First.RemoveCurrent(); if (ToLoad.Len() > 0) { UE_LOG(LogRedirectors, Log, TEXT("String Asset Reference '%s'"), *ToLoad); StringAssetRefFilenameStack.Push(RefFilename); UObject *Loaded = LoadObject<UObject>(NULL, *ToLoad, NULL, LOAD_None, NULL); StringAssetRefFilenameStack.Pop(); UObjectRedirector* Redirector = dynamic_cast<UObjectRedirector*>(Loaded); if (Redirector) { UE_LOG(LogRedirectors, Log, TEXT(" Found redir '%s'"), *Redirector->GetFullName()); FRedirection Redir; Redir.PackageFilename = RefFilename; 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) { FString Dest = Loaded->GetPathName(); UE_LOG(LogRedirectors, Log, TEXT(" Resolved to '%s'"), *Dest); if (Dest != ToLoad) { StringAssetRemap.Add(ToLoad, Dest); } } else { UE_LOG(LogRedirectors, Log, TEXT(" Not Found!")); } } } }
void UObjectPropertyBase::ExportTextItem( FString& ValueStr, const void* PropertyValue, const void* DefaultValue, UObject* Parent, int32 PortFlags, UObject* ExportRootScope ) const { UObject* Temp = GetObjectPropertyValue(PropertyValue); if (0 != (PortFlags & PPF_ExportCpp)) { FString::Printf(TEXT("%s%s*"), PropertyClass->GetPrefixCPP(), *PropertyClass->GetName()); ValueStr += Temp ? FString::Printf(TEXT("LoadObject<%s%s>(nullptr, TEXT(\"%s\"))") , PropertyClass->GetPrefixCPP() , *PropertyClass->GetName() , *(Temp->GetPathName().ReplaceCharWithEscapedChar())) : TEXT("nullptr"); return; } if( Temp != NULL ) { if (PortFlags & PPF_DebugDump) { ValueStr += Temp->GetFullName(); } else if (Parent && !Parent->HasAnyFlags(RF_ClassDefaultObject) && Temp->IsDefaultSubobject()) { if ((PortFlags & PPF_Delimited) && (!Temp->GetFName().IsValidXName(INVALID_OBJECTNAME_CHARACTERS))) { ValueStr += FString::Printf(TEXT("\"%s\""), *Temp->GetName().ReplaceQuotesWithEscapedQuotes()); } else { ValueStr += Temp->GetName(); } } else { ValueStr += GetExportPath(Temp, Parent, ExportRootScope, PortFlags); } } else { ValueStr += TEXT("None"); } }
void SPropertyEditorEditInline::OnClassPicked(UClass* InClass) { TArray<FObjectBaseAddress> ObjectsToModify; TArray<FString> NewValues; const TSharedRef< FPropertyNode > PropertyNode = PropertyEditor->GetPropertyNode(); FObjectPropertyNode* ObjectNode = PropertyNode->FindObjectItemParent(); if( ObjectNode ) { for ( TPropObjectIterator Itor( ObjectNode->ObjectIterator() ) ; Itor ; ++Itor ) { FString NewValue; if (InClass) { UObject* Object = Itor->Get(); UObject* UseOuter = (InClass->IsChildOf(UClass::StaticClass()) ? Cast<UClass>(Object)->GetDefaultObject() : Object); EObjectFlags MaskedOuterFlags = UseOuter ? UseOuter->GetMaskedFlags(RF_PropagateToSubObjects) : RF_NoFlags; if (UseOuter && UseOuter->HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject)) { MaskedOuterFlags |= RF_ArchetypeObject; } UObject* NewObject = StaticConstructObject(InClass, UseOuter, NAME_None, MaskedOuterFlags, NULL); NewValue = NewObject->GetPathName(); } else { NewValue = FName(NAME_None).ToString(); } NewValues.Add(NewValue); } const TSharedRef< IPropertyHandle > PropertyHandle = PropertyEditor->GetPropertyHandle(); PropertyHandle->SetPerObjectValues( NewValues ); // Force a rebuild of the children when this node changes PropertyNode->RequestRebuildChildren(); ComboButton->SetIsOpen(false); } }
void FBlueprintCompileReinstancer::ReplaceInstancesOfClass(UClass* OldClass, UClass* NewClass, UObject* OriginalCDO, TSet<UObject*>* ObjectsThatShouldUseOldStuff) { USelection* SelectedActors; bool bSelectionChanged = false; TArray<UObject*> ObjectsToReplace; const bool bLogConversions = false; // for debugging // Map of old objects to new objects TMap<UObject*, UObject*> OldToNewInstanceMap; TMap<UClass*, UClass*> OldToNewClassMap; OldToNewClassMap.Add(OldClass, NewClass); TMap<FStringAssetReference, UObject*> ReinstancedObjectsWeakReferenceMap; // actors being replace TArray<FActorReplacementHelper> ReplacementActors; // A list of objects (e.g. Blueprints) that potentially have editors open that we need to refresh TArray<UObject*> PotentialEditorsForRefreshing; // A list of component owners that need their construction scripts re-ran (because a component of theirs has been reinstanced) TSet<AActor*> OwnersToReconstruct; // Set global flag to let system know we are reconstructing blueprint instances TGuardValue<bool> GuardTemplateNameFlag(GIsReconstructingBlueprintInstances, true); struct FObjectRemappingHelper { void OnObjectsReplaced(const TMap<UObject*, UObject*>& InReplacedObjects) { ReplacedObjects.Append(InReplacedObjects); } TMap<UObject*, UObject*> ReplacedObjects; } ObjectRemappingHelper; FDelegateHandle OnObjectsReplacedHandle = GEditor->OnObjectsReplaced().AddRaw(&ObjectRemappingHelper,&FObjectRemappingHelper::OnObjectsReplaced); { BP_SCOPED_COMPILER_EVENT_STAT(EKismetReinstancerStats_ReplaceInstancesOfClass); const bool bIncludeDerivedClasses = false; GetObjectsOfClass(OldClass, ObjectsToReplace, bIncludeDerivedClasses); SelectedActors = GEditor->GetSelectedActors(); SelectedActors->BeginBatchSelectOperation(); SelectedActors->Modify(); // Then fix 'real' (non archetype) instances of the class for (UObject* OldObject : ObjectsToReplace) { // Skip non-archetype instances, EXCEPT for component templates const bool bIsComponent = NewClass->IsChildOf(UActorComponent::StaticClass()); if ((!bIsComponent && OldObject->IsTemplate()) || OldObject->IsPendingKill()) { continue; } UBlueprint* CorrespondingBlueprint = Cast<UBlueprint>(OldObject->GetClass()->ClassGeneratedBy); UObject* OldBlueprintDebugObject = nullptr; // If this object is being debugged, cache it off so we can preserve the 'object being debugged' association if ((CorrespondingBlueprint != nullptr) && (CorrespondingBlueprint->GetObjectBeingDebugged() == OldObject)) { OldBlueprintDebugObject = OldObject; } AActor* OldActor = Cast<AActor>(OldObject); UObject* NewUObject = nullptr; // if the object to replace is an actor... if (OldActor != nullptr) { FVector Location = FVector::ZeroVector; FRotator Rotation = FRotator::ZeroRotator; if (USceneComponent* OldRootComponent = OldActor->GetRootComponent()) { Location = OldActor->GetActorLocation(); Rotation = OldActor->GetActorRotation(); } // If this actor was spawned from an Archetype, we spawn the new actor from the new version of that archetype UObject* OldArchetype = OldActor->GetArchetype(); UWorld* World = OldActor->GetWorld(); AActor* NewArchetype = Cast<AActor>(OldToNewInstanceMap.FindRef(OldArchetype)); // Check that either this was an instance of the class directly, or we found a new archetype for it check(OldArchetype == OldClass->GetDefaultObject() || NewArchetype); // Spawn the new actor instance, in the same level as the original, but deferring running the construction script until we have transferred modified properties ULevel* ActorLevel = OldActor->GetLevel(); UClass** MappedClass = OldToNewClassMap.Find(OldActor->GetClass()); UClass* SpawnClass = MappedClass ? *MappedClass : NewClass; FActorSpawnParameters SpawnInfo; SpawnInfo.OverrideLevel = ActorLevel; SpawnInfo.Template = NewArchetype; SpawnInfo.bNoCollisionFail = true; SpawnInfo.bDeferConstruction = true; // Temporarily remove the deprecated flag so we can respawn the Blueprint in the level const bool bIsClassDeprecated = SpawnClass->HasAnyClassFlags(CLASS_Deprecated); SpawnClass->ClassFlags &= ~CLASS_Deprecated; AActor* NewActor = World->SpawnActor(SpawnClass, &Location, &Rotation, SpawnInfo); // Reassign the deprecated flag if it was previously assigned if (bIsClassDeprecated) { SpawnClass->ClassFlags |= CLASS_Deprecated; } check(NewActor != nullptr); NewUObject = NewActor; // store the new actor for the second pass (NOTE: this detaches // OldActor from all child/parent attachments) // // running the NewActor's construction-script is saved for that // second pass (because the construction-script may reference // another instance that hasn't been replaced yet). ReplacementActors.Add(FActorReplacementHelper(NewActor, OldActor)); ReinstancedObjectsWeakReferenceMap.Add(OldObject, NewUObject); OldActor->DestroyConstructedComponents(); // don't want to serialize components from the old actor // Unregister native components so we don't copy any sub-components they generate for themselves (like UCameraComponent does) OldActor->UnregisterAllComponents(); // Unregister any native components, might have cached state based on properties we are going to overwrite NewActor->UnregisterAllComponents(); UEditorEngine::CopyPropertiesForUnrelatedObjects(OldActor, NewActor); // reset properties/streams NewActor->ResetPropertiesForConstruction(); // register native components NewActor->RegisterAllComponents(); // // clean up the old actor (unselect it, remove it from the world, etc.)... if (OldActor->IsSelected()) { GEditor->SelectActor(OldActor, /*bInSelected =*/false, /*bNotify =*/false); bSelectionChanged = true; } if (GEditor->Layers.IsValid()) // ensure(NULL != GEditor->Layers) ?? While cooking the Layers is NULL. { GEditor->Layers->DisassociateActorFromLayers(OldActor); } World->EditorDestroyActor(OldActor, /*bShouldModifyLevel =*/true); OldToNewInstanceMap.Add(OldActor, NewActor); } else { FName OldName(OldObject->GetFName()); OldObject->Rename(NULL, OldObject->GetOuter(), REN_DoNotDirty | REN_DontCreateRedirectors); NewUObject = NewObject<UObject>(OldObject->GetOuter(), NewClass, OldName); check(NewUObject != nullptr); UEditorEngine::CopyPropertiesForUnrelatedObjects(OldObject, NewUObject); if (UAnimInstance* AnimTree = Cast<UAnimInstance>(NewUObject)) { // Initialising the anim instance isn't enough to correctly set up the skeletal mesh again in a // paused world, need to initialise the skeletal mesh component that contains the anim instance. if (USkeletalMeshComponent* SkelComponent = Cast<USkeletalMeshComponent>(AnimTree->GetOuter())) { SkelComponent->InitAnim(true); } } OldObject->RemoveFromRoot(); OldObject->MarkPendingKill(); OldToNewInstanceMap.Add(OldObject, NewUObject); if (bIsComponent) { UActorComponent* Component = Cast<UActorComponent>(NewUObject); AActor* OwningActor = Component->GetOwner(); if (OwningActor) { OwningActor->ResetOwnedComponents(); // Check to see if they have an editor that potentially needs to be refreshed if (OwningActor->GetClass()->ClassGeneratedBy) { PotentialEditorsForRefreshing.AddUnique(OwningActor->GetClass()->ClassGeneratedBy); } // we need to keep track of actor instances that need // their construction scripts re-ran (since we've just // replaced a component they own) OwnersToReconstruct.Add(OwningActor); } } } // If this original object came from a blueprint and it was in the selected debug set, change the debugging to the new object. if ((CorrespondingBlueprint) && (OldBlueprintDebugObject) && (NewUObject)) { CorrespondingBlueprint->SetObjectBeingDebugged(NewUObject); } if (bLogConversions) { UE_LOG(LogBlueprint, Log, TEXT("Converted instance '%s' to '%s'"), *OldObject->GetPathName(), *NewUObject->GetPathName()); } } } GEditor->OnObjectsReplaced().Remove(OnObjectsReplacedHandle); // Now replace any pointers to the old archetypes/instances with pointers to the new one TArray<UObject*> SourceObjects; TArray<UObject*> DstObjects; OldToNewInstanceMap.GenerateKeyArray(SourceObjects); OldToNewInstanceMap.GenerateValueArray(DstObjects); // Also look for references in new spawned objects. SourceObjects.Append(DstObjects); FReplaceReferenceHelper::IncludeCDO(OldClass, NewClass, OldToNewInstanceMap, SourceObjects, OriginalCDO); FReplaceReferenceHelper::FindAndReplaceReferences(SourceObjects, ObjectsThatShouldUseOldStuff, ObjectsToReplace, OldToNewInstanceMap, ReinstancedObjectsWeakReferenceMap); { BP_SCOPED_COMPILER_EVENT_STAT(EKismetReinstancerStats_ReplacementConstruction); // the process of setting up new replacement actors is split into two // steps (this here, is the second)... // // the "finalization" here runs the replacement actor's construction- // script and is left until late to account for a scenario where the // construction-script attempts to modify another instance of the // same class... if this were to happen above, in the ObjectsToReplace // loop, then accessing that other instance would cause an assert in // UProperty::ContainerPtrToValuePtrInternal() (which appropriatly // complains that the other instance's type doesn't match because it // hasn't been replaced yet... that's why we wait until after // FArchiveReplaceObjectRef to run construction-scripts). for (FActorReplacementHelper& ReplacementActor : ReplacementActors) { ReplacementActor.Finalize(ObjectRemappingHelper.ReplacedObjects); } } SelectedActors->EndBatchSelectOperation(); if (bSelectionChanged) { GEditor->NoteSelectionChange(); } if (GEditor) { // Refresh any editors for objects that we've updated components for for (auto BlueprintAsset : PotentialEditorsForRefreshing) { FBlueprintEditor* BlueprintEditor = static_cast<FBlueprintEditor*>(FAssetEditorManager::Get().FindEditorForAsset(BlueprintAsset, /*bFocusIfOpen =*/false)); if (BlueprintEditor) { BlueprintEditor->RefreshEditors(); } } } // in the case where we're replacing component instances, we need to make // sure to re-run their owner's construction scripts for (AActor* ActorInstance : OwnersToReconstruct) { ActorInstance->RerunConstructionScripts(); } }
/** * Recursively transverses the reference tree */ void OutputReferencedAssets(FOutputDeviceFile& FileAr, int32 CurrentDepth, FString ParentId, UObject* BaseObject, TArray<UObject*>* AssetList) { check(AssetList); const FString ScriptItemString = NSLOCTEXT("UnrealEd", "Script", "Script").ToString(); const FString DefaultsItemString = NSLOCTEXT("UnrealEd", "Defaults", "Defaults").ToString(); for (int32 AssetIndex = 0; AssetIndex < AssetList->Num(); AssetIndex++) { UObject* ReferencedObject = (*AssetList)[AssetIndex]; check(ReferencedObject); // get the list of assets this object is referencing TArray<UObject*>* ReferencedAssets = ReferenceGraph.Find(ReferencedObject); // add a new tree item for this referenced asset FString ItemString; if (ReferencedObject == BaseObject->GetClass()) { ItemString = *ScriptItemString; if (ReferencedAssets == NULL || ReferencedAssets->Num() == 0) { // special case for the "Script" node - don't add it if it doesn't have any children continue; } } else if (ReferencedObject == BaseObject->GetArchetype()) { ItemString = *DefaultsItemString; if (ReferencedAssets == NULL || ReferencedAssets->Num() == 0) { // special case for the "Defaults" node - don't add it if it doesn't have any children continue; } } else { if (CurrentDepth > 0) { ItemString = ReferencedObject->GetPathName(); } else { ItemString = GetObjectNameFromCache(ReferencedObject); } } FString AssetId = FString::Printf(TEXT("%s.%d"), *ParentId, AssetIndex); if (CurrentDepth > 0) { FString TabStr; for (int32 i = 0; i < CurrentDepth; ++i) { TabStr += TEXT("\t"); } FileAr.Logf(TEXT("%s(%s) %s"), *TabStr, *AssetId, *ItemString); } else { OutputDetailsItem(FileAr, AssetId, ReferencedObject, ItemString); } if (ReferencedAssets != NULL) { // If this object is referencing other objects, output those objects OutputReferencedAssets(FileAr, (CurrentDepth == 0)? 0: CurrentDepth + 1, AssetId, ReferencedObject, ReferencedAssets); } } }
UObject * UPackageMap::GetObjectFromNetGUID( FNetworkGUID NetGUID ) { if ( !ensure( NetGUID.IsValid() ) ) { return NULL; } if ( !ensure( !NetGUID.IsDefault() ) ) { return NULL; } FNetGuidCacheObject * CacheObjectPtr = Cache->ObjectLookup.Find( NetGUID ); if ( CacheObjectPtr == NULL ) { // We don't have the object mapped yet return NULL; } UObject * Object = CacheObjectPtr->Object.Get(); if ( Object != NULL ) { check( Object->GetPathName() == CacheObjectPtr->FullPath ); return Object; } if ( CacheObjectPtr->FullPath.IsEmpty() ) { // This probably isn't possible, but making it a warning UE_LOG( LogNetPackageMap, Warning, TEXT( "GetObjectFromNetGUID: No full path for %s" ), *NetGUID.ToString() ); return NULL; } if ( NetGUID.IsDynamic() ) { // Dynamic objects don't have stable names, so we can't possibly reload the same object UE_LOG( LogNetPackageMap, VeryVerbose, TEXT( "GetObjectFromNetGUID: Cannot re-create dynamic object after GC <%s, %s>" ), *NetGUID.ToString(), *CacheObjectPtr->FullPath ); return NULL; } // // The object was previously mapped, but we GC'd it // We need to reload it now // Object = StaticFindObject( UObject::StaticClass(), NULL, *CacheObjectPtr->FullPath, false ); if ( Object == NULL ) { if ( IsNetGUIDAuthority() ) { // The authority shouldn't be loading resources on demand, at least for now. // This could be garbage or a security risk // Another possibility is in dynamic property replication if the server reads the previously serialized state // that has a now destroyed actor in it. UE_LOG( LogNetPackageMap, Warning, TEXT( "GetObjectFromNetGUID: Could not find Object for: NetGUID <%s, %s> (and IsNetGUIDAuthority())" ), *NetGUID.ToString(), *CacheObjectPtr->FullPath ); return NULL; } else { UE_LOG( LogNetPackageMap, Log, TEXT( "GetObjectFromNetGUID: Could not find Object for: NetGUID <%s, %s>"), *NetGUID.ToString(), *CacheObjectPtr->FullPath ); } Object = StaticLoadObject( UObject::StaticClass(), NULL, *CacheObjectPtr->FullPath, NULL, LOAD_NoWarn ); if ( Object ) { UE_LOG( LogNetPackageMap, Log, TEXT( "GetObjectFromNetGUID: StaticLoadObject. Found: %s" ), Object ? *Object->GetName() : TEXT( "NULL" ) ); } else { Object = LoadPackage( NULL, *CacheObjectPtr->FullPath, LOAD_None ); UE_LOG( LogNetPackageMap, Log, TEXT( "GetObjectFromNetGUID: LoadPackage. Found: %s" ), Object ? *Object->GetName() : TEXT( "NULL" ) ); } } else { // If we actually found the object, we probably shouldn't have GC'd it, so that's odd UE_LOG( LogNetPackageMap, Warning, TEXT( "GetObjectFromNetGUID: Object should not be found after GC: <%s, %s>" ), *NetGUID.ToString(), *CacheObjectPtr->FullPath ); } if ( Object == NULL ) { // If we get here, that means we have an invalid path, which shouldn't be possible, but making it a warning UE_LOG( LogNetPackageMap, Warning, TEXT( "GetObjectFromNetGUID: FAILED to re-create object after GC: <%s, %s>" ), *NetGUID.ToString(), *CacheObjectPtr->FullPath ); } else { // Reassign the object pointer for quick access next time CacheObjectPtr->Object = Object; // Make sure the object is in the GUIDToObjectLookup. Cache->NetGUIDLookup.Add( Object, NetGUID ); } return Object; }
bool UGatherTextFromAssetsCommandlet::ProcessTextProperty(UTextProperty* InTextProp, UObject* Object, const FString& ObjectPath, bool bInFixBroken, bool& OutFixed) { bool TextPropertyWasValid = true; OutFixed = false; FText* Data = InTextProp->ContainerPtrToValuePtr<FText>(Object); // Transient check. if( Data->Flags & ETextFlag::Transient ) { UE_LOG(LogGatherTextFromAssetsCommandlet, Warning, TEXT("Transient text found set to %s in %s - %s."), *InTextProp->GetName(), *Object->GetPathName(), *Object->GetName()); TextPropertyWasValid = false; } else { FConflictTracker::FEntry NewEntry; NewEntry.ObjectPath = ObjectPath; NewEntry.SourceString = Data->SourceString; NewEntry.Status = EAssetTextGatherStatus::None; // Fix missing key if broken and allowed. if( !( Data->Key.IsValid() ) || Data->Key->IsEmpty() ) { // Key fix. if (bInFixBroken) { // Create key if needed. if( !( Data->Key.IsValid() ) ) { Data->Key = MakeShareable( new FString() ); } // Generate new GUID for key. *(Data->Key) = FGuid::NewGuid().ToString(); // Fixed. NewEntry.Status = EAssetTextGatherStatus::MissingKey_Resolved; } else { NewEntry.Status = EAssetTextGatherStatus::MissingKey; TextPropertyWasValid = false; } } // Must have valid key. if( Data->Key.IsValid() && !( Data->Key->IsEmpty() ) ) { FContext SearchContext; SearchContext.Key = Data->Key.IsValid() ? *Data->Key : TEXT(""); // Find existing entry from manifest or manifest dependencies. TSharedPtr< FManifestEntry > ExistingEntry = ManifestInfo->GetManifest()->FindEntryByContext( Data->Namespace.IsValid() ? *(Data->Namespace) : TEXT(""), SearchContext ); if( !ExistingEntry.IsValid() ) { FString FileInfo; ExistingEntry = ManifestInfo->FindDependencyEntrybyContext( Data->Namespace.IsValid() ? *(Data->Namespace) : TEXT(""), SearchContext, FileInfo ); } // Entry already exists, check for conflict. if( ExistingEntry.IsValid() ) { // Fix conflict if present and allowed. if( ExistingEntry->Source.Text != ( Data->SourceString.IsValid() ? **(Data->SourceString) : TEXT("") ) ) { if (bInFixBroken) { // Generate new GUID for key. *(Data->Key) = FGuid::NewGuid().ToString(); // Fixed. NewEntry.Status = EAssetTextGatherStatus::IdentityConflict_Resolved; // Conflict resolved, no existing entry. ExistingEntry.Reset(); } else { NewEntry.Status = EAssetTextGatherStatus::IdentityConflict; TextPropertyWasValid = false; } } } // Only add an entry to the manifest if no existing entry exists. if( !( ExistingEntry.IsValid() ) ) { // Check for valid string. if( Data->SourceString.IsValid() && !( Data->SourceString->IsEmpty() ) ) { FString SrcLocation = ObjectPath + TEXT(".") + InTextProp->GetName(); // Adjust the source location if needed. { UClass* Class = Object->GetClass(); UObject* CDO = Class ? Class->GetDefaultObject() : NULL; if( CDO && CDO != Object ) { for( TFieldIterator<UTextProperty> PropIt(CDO->GetClass(), EFieldIteratorFlags::IncludeSuper); PropIt; ++PropIt ) { UTextProperty* TextProp = Cast<UTextProperty>( *(PropIt) ); FText* DataCDO = TextProp->ContainerPtrToValuePtr<FText>( CDO ); if( DataCDO->Key == Data->Key || ( DataCDO->Key.Get() && Data->Key.Get() && ( *(DataCDO->Key) == *(Data->Key) ) ) ) { SrcLocation = CDO->GetPathName() + TEXT(".") + TextProp->GetName(); break; } } } } FContext Context; Context.Key = Data->Key.IsValid() ? *Data->Key : TEXT(""); Context.SourceLocation = SrcLocation; FString EntryDescription = FString::Printf( TEXT("In %s"), *Object->GetFullName()); ManifestInfo->AddEntry(EntryDescription, Data->Namespace.Get() ? *Data->Namespace : TEXT(""), Data->SourceString.Get() ? *(Data->SourceString) : TEXT(""), Context ); } } } // Add to conflict tracker. FConflictTracker::FKeyTable& KeyTable = ConflictTracker.Namespaces.FindOrAdd( Data->Namespace.IsValid() ? *(Data->Namespace) : TEXT("") ); FConflictTracker::FEntryArray& EntryArray = KeyTable.FindOrAdd( Data->Key.IsValid() ? *(Data->Key) : TEXT("") ); EntryArray.Add(NewEntry); OutFixed = (NewEntry.Status == EAssetTextGatherStatus::MissingKey_Resolved || NewEntry.Status == EAssetTextGatherStatus::IdentityConflict_Resolved); } return TextPropertyWasValid; }
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 FRedirectCollector::ResolveStringAssetReference(FString FilterPackage) { TMultiMap<FString, FPackagePropertyPair> SkippedReferences; while (StringAssetReferences.Num()) { TMultiMap<FString, FPackagePropertyPair>::TIterator First(StringAssetReferences); FString ToLoad = First.Key(); FPackagePropertyPair RefFilenameAndProperty = First.Value(); First.RemoveCurrent(); if (FCoreDelegates::LoadStringAssetReferenceInCook.IsBound()) { if (FCoreDelegates::LoadStringAssetReferenceInCook.Execute(ToLoad) == false) { // Skip this reference continue; } } if (!FilterPackage.IsEmpty() && FilterPackage != RefFilenameAndProperty.Package) { // If we have a valid filter and it doesn't match, skip this reference SkippedReferences.Add(ToLoad, RefFilenameAndProperty); continue; } if (ToLoad.Len() > 0) { UE_LOG(LogRedirectors, Verbose, TEXT("String Asset Reference '%s'"), *ToLoad); UE_CLOG(RefFilenameAndProperty.Property.Len(), LogRedirectors, Verbose, TEXT(" Referenced by '%s'"), *RefFilenameAndProperty.Property); StringAssetRefFilenameStack.Push(RefFilenameAndProperty.Package); UObject *Loaded = LoadObject<UObject>(NULL, *ToLoad, NULL, RefFilenameAndProperty.bReferencedByEditorOnlyProperty ? 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.Package; 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) { 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.Property.Len() ? RefFilenameAndProperty.Property : TEXT("Unknown"); UE_LOG(LogRedirectors, Warning, TEXT("String Asset Reference '%s' was not found! (Referencer '%s')"), *ToLoad, *Referencer); } } } // Add any skipped references back into the map for the next time this is called StringAssetReferences = SkippedReferences; }