void FAssetFixUpRedirectors::LoadReferencingPackages(TArray<FRedirectorRefs>& RedirectorsToFix, TArray<UPackage*>& OutReferencingPackagesToSave) const { FScopedSlowTask SlowTask( RedirectorsToFix.Num(), LOCTEXT( "LoadingReferencingPackages", "Loading Referencing Packages..." ) ); SlowTask.MakeDialog(); ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); // Load all packages that reference each redirector, if possible for ( auto RedirectorRefsIt = RedirectorsToFix.CreateIterator(); RedirectorRefsIt; ++RedirectorRefsIt ) { SlowTask.EnterProgressFrame(1); FRedirectorRefs& RedirectorRefs = *RedirectorRefsIt; if ( ISourceControlModule::Get().IsEnabled() ) { FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(RedirectorRefs.Redirector->GetOutermost(), EStateCacheUsage::Use); const bool bValidSCCState = !SourceControlState.IsValid() || SourceControlState->IsAdded() || SourceControlState->IsCheckedOut() || SourceControlState->CanCheckout() || !SourceControlState->IsSourceControlled() || SourceControlState->IsIgnored(); if ( !bValidSCCState ) { RedirectorRefs.bRedirectorValidForFixup = false; RedirectorRefs.FailureReason = LOCTEXT("RedirectorFixupFailed_BadSCC", "Redirector could not be checked out or marked for delete"); } } // Load all referencers for ( auto PackageNameIt = RedirectorRefs.ReferencingPackageNames.CreateConstIterator(); PackageNameIt; ++PackageNameIt ) { const FString PackageName = (*PackageNameIt).ToString(); // Find the package in memory. If it is not in memory, try to load it UPackage* Package = FindPackage(NULL, *PackageName); if ( !Package ) { Package = LoadPackage(NULL, *PackageName, LOAD_None); } if ( Package ) { if ( Package->HasAnyPackageFlags(PKG_CompiledIn) ) { // This is a script reference RedirectorRefs.bRedirectorValidForFixup = false; RedirectorRefs.FailureReason = FText::Format(LOCTEXT("RedirectorFixupFailed_CodeReference", "Redirector is referenced by code. Package: {0}"), FText::FromString(PackageName)); } else { // If we found a valid package, mark it for save OutReferencingPackagesToSave.AddUnique(Package); } } } } }
/** * Helper function designed to determine if the provided package should be backed up or not. * The function checks for many conditions, such as if the package is too large to backup, * if the package has a particular attribute that should prevent it from being backed up (such * as being marked for PIE-use), if cooking is in progress, etc. * * @param InPackage Package which should be checked to see if its valid for backing-up * @param OutFileName File name of the package on disk if the function determines the package * already existed * * @return true if the package is valid for backing-up; false otherwise */ bool FAutoPackageBackup::ShouldBackupPackage( const UPackage& InPackage, FString& OutFilename ) { // Check various conditions to see if the package is a valid candidate for backing up bool bShouldBackup = GIsEditor // Backing up packages only makes sense in the editor && !IsRunningCommandlet() // Don't backup saves resulting from commandlets && IsPackageBackupEnabled() // Ensure that the package backup is enabled in the first place && (InPackage.HasAnyPackageFlags(PKG_PlayInEditor) == false) // Don't back up PIE packages && (InPackage.HasAnyPackageFlags(PKG_ContainsScript) == false); // Don't back up script packages if( bShouldBackup ) { GWarn->StatusUpdate( -1, -1, NSLOCTEXT("UnrealEd", "PackageBackup_ValidityWarning", "Determining asset backup validity...") ); bShouldBackup = FPackageName::DoesPackageExist( InPackage.GetName(), NULL, &OutFilename ); // Make sure the file already exists (no sense in backing up a new package) } // If the package passed the initial backup checks, proceed to check more specific conditions // that might disqualify the package from being backed up const int32 FileSizeOfBackup = IFileManager::Get().FileSize( *OutFilename ); if ( bShouldBackup ) { // Ensure that the size the backup would require is less than that of the maximum allowed // space for backups bShouldBackup = FileSizeOfBackup <= GetMaxAllowedBackupSpace(); } // If all of the prior checks have passed, now see if the package has been backed up // too recently to be considered for an additional backup if ( bShouldBackup ) { // Ensure that the autosave/backup directory exists const FString& BackupSaveDir = GetBackupDirectory(); IFileManager::Get().MakeDirectory( *BackupSaveDir, 1 ); // Find all of the files in the backup directory TArray<FString> FilesInBackupDir; IFileManager::Get().FindFilesRecursive(FilesInBackupDir, *BackupSaveDir, TEXT("*.*"), true, false); // Extract the base file name and extension from the passed-in package file name FString ExistingBaseFileName = FPaths::GetBaseFilename(OutFilename); FString ExistingFileNameExtension = FPaths::GetExtension(OutFilename); bool bFoundExistingBackup = false; int32 DirectorySize = 0; FDateTime LastBackupTimeStamp = FDateTime::MinValue(); TArray<FBackupFileInfo> BackupFileInfoArray; // Check every file in the backup directory for matches against the passed-in package // (Additionally keep statistics on all backup files for potential maintenance) for ( TArray<FString>::TConstIterator FileIter( FilesInBackupDir ); FileIter; ++FileIter ) { const FString CurBackupFileName = FString( *FileIter ); // Create a new backup file info struct for keeping information about each backup file const int32 FileInfoIndex = BackupFileInfoArray.AddZeroed(); FBackupFileInfo& CurBackupFileInfo = BackupFileInfoArray[ FileInfoIndex ]; // Record the backup file's name, size, and timestamp CurBackupFileInfo.FileName = CurBackupFileName; CurBackupFileInfo.FileSize = IFileManager::Get().FileSize( *CurBackupFileName ); // If we failed to get a timestamp or a valid size, something has happened to the file and it shouldn't be considered CurBackupFileInfo.FileTimeStamp = IFileManager::Get().GetTimeStamp(*CurBackupFileName); if (CurBackupFileInfo.FileTimeStamp == FDateTime::MinValue() || CurBackupFileInfo.FileSize == -1) { BackupFileInfoArray.RemoveAt( BackupFileInfoArray.Num() - 1 ); continue; } // Calculate total directory size by adding the size of this backup file DirectorySize += CurBackupFileInfo.FileSize; FString CurBackupBaseFileName = FPaths::GetBaseFilename(CurBackupFileName); FString CurBackupFileNameExtension = FPaths::GetExtension(CurBackupFileName); // The base file name of the backup file is going to include an underscore followed by a timestamp, so they must be removed for comparison's sake CurBackupBaseFileName = CurBackupBaseFileName.Left( CurBackupBaseFileName.Find( TEXT("_"), ESearchCase::CaseSensitive, ESearchDir::FromEnd ) ); // If the base file names and extensions match, we've found a backup if ( CurBackupBaseFileName == ExistingBaseFileName && CurBackupFileNameExtension == ExistingFileNameExtension ) { bFoundExistingBackup = true; // Keep track of the most recent matching time stamp so we can check if the passed-in package // has been backed up too recently if ( CurBackupFileInfo.FileTimeStamp > LastBackupTimeStamp ) { LastBackupTimeStamp = CurBackupFileInfo.FileTimeStamp; } } } // If there was an existing backup, check to see if it was created too recently to allow another backup if ( bFoundExistingBackup ) { // Check the difference in timestamp seconds against the backup interval; if not enough time has elapsed since // the last backup, we don't want to make another one if ((FDateTime::UtcNow() - LastBackupTimeStamp).GetTotalSeconds() < GetBackupInterval()) { bShouldBackup = false; } } // If every other check against the package has succeeded for backup purposes, ensure there is enough directory space // available in the backup directory, as adding the new backup might use more space than the user allowed for backups. // If the backup file size + the current directory size exceeds the max allowed space, deleted old backups until there // is sufficient space. If enough space can't be freed for whatever reason, then no back-up will be created. if ( bShouldBackup && ( FileSizeOfBackup + DirectorySize > GetMaxAllowedBackupSpace() ) ) { bShouldBackup = PerformBackupSpaceMaintenance( BackupFileInfoArray, DirectorySize, FileSizeOfBackup ); } } return bShouldBackup; }
// // Find or create the linker for a package. // FLinkerLoad* GetPackageLinker ( UPackage* InOuter, const TCHAR* InLongPackageName, uint32 LoadFlags, UPackageMap* Sandbox, FGuid* CompatibleGuid ) { // See if there is already a linker for this package. auto Result = FLinkerLoad::FindExistingLinkerForPackage(InOuter); // Try to load the linker. // See if the linker is already loaded. if (Result) { return Result; } FString NewFilename; if( !InLongPackageName ) { // Resolve filename from package name. if( !InOuter ) { // try to recover from this instead of throwing, it seems recoverable just by doing this FText ErrorText(LOCTEXT("PackageResolveFailed", "Can't resolve asset name")); LogGetPackageLinkerError(Result, InLongPackageName, ErrorText, ErrorText, InOuter, LoadFlags); return nullptr; } // Allow delegates to resolve this package FString PackageName = InOuter->GetName(); // Do not resolve packages that are in memory if (!InOuter->HasAnyPackageFlags(PKG_InMemoryOnly)) { PackageName = FPackageName::GetDelegateResolvedPackagePath(InOuter->GetName()); } // Verify that the file exists. const bool DoesPackageExist = DoesPackageExistForGetPackageLinker(PackageName, CompatibleGuid, NewFilename); if ( !DoesPackageExist ) { // In memory-only packages have no linker and this is ok. if (!(LoadFlags & LOAD_AllowDll) && !InOuter->HasAnyPackageFlags(PKG_InMemoryOnly) && !FLinkerLoad::IsKnownMissingPackage(InOuter->GetFName())) { FUObjectThreadContext& ThreadContext = FUObjectThreadContext::Get(); FFormatNamedArguments Arguments; Arguments.Add(TEXT("AssetName"), FText::FromString(PackageName)); Arguments.Add(TEXT("PackageName"), FText::FromString(ThreadContext.SerializedPackageLinker ? *(ThreadContext.SerializedPackageLinker->Filename) : TEXT("NULL"))); LogGetPackageLinkerError(Result, ThreadContext.SerializedPackageLinker ? *ThreadContext.SerializedPackageLinker->Filename : nullptr, FText::Format(LOCTEXT("PackageNotFound", "Can't find file for asset '{AssetName}' while loading {PackageName}."), Arguments), LOCTEXT("PackageNotFoundShort", "Can't find file for asset."), InOuter, LoadFlags); } return nullptr; } } else { FString PackageName = InLongPackageName; if (!FPackageName::TryConvertFilenameToLongPackageName(InLongPackageName, PackageName)) { // try to recover from this instead of throwing, it seems recoverable just by doing this FText ErrorText(LOCTEXT("PackageResolveFailed", "Can't resolve asset name")); LogGetPackageLinkerError(Result, InLongPackageName, ErrorText, ErrorText, InOuter, LoadFlags); return nullptr; } // Allow delegates to resolve this path PackageName = FPackageName::GetDelegateResolvedPackagePath(PackageName); UPackage* ExistingPackage = FindObject<UPackage>(nullptr, *PackageName); if (ExistingPackage) { if (!ExistingPackage->GetOuter() && ExistingPackage->HasAnyPackageFlags(PKG_InMemoryOnly)) { // This is a memory-only in package and so it has no linker and this is ok. return nullptr; } } // Verify that the file exists. const bool DoesPackageExist = DoesPackageExistForGetPackageLinker(PackageName, CompatibleGuid, NewFilename); if( !DoesPackageExist ) { if (!FLinkerLoad::IsKnownMissingPackage(InLongPackageName)) { FFormatNamedArguments Arguments; Arguments.Add(TEXT("Filename"), FText::FromString(InLongPackageName)); // try to recover from this instead of throwing, it seems recoverable just by doing this LogGetPackageLinkerError(Result, InLongPackageName, FText::Format(LOCTEXT("FileNotFound", "Can't find file '{Filename}'"), Arguments), LOCTEXT("FileNotFoundShort", "Can't find file"), InOuter, LoadFlags); } return nullptr; } // Create the package with the provided long package name. UPackage* FilenamePkg = (ExistingPackage ? ExistingPackage : CreatePackage(nullptr, *PackageName)); if (FilenamePkg != ExistingPackage && (LoadFlags & LOAD_PackageForPIE)) { FilenamePkg->SetPackageFlags(PKG_PlayInEditor); } // If no package specified, use package from file. if (!InOuter) { if( !FilenamePkg ) { FFormatNamedArguments Arguments; Arguments.Add(TEXT("Filename"), FText::FromString(InLongPackageName)); LogGetPackageLinkerError(Result, InLongPackageName, FText::Format(LOCTEXT("FilenameToPackage", "Can't convert filename '{Filename}' to asset name"), Arguments), LOCTEXT("FilenameToPackageShort", "Can't convert filename to asset name"), InOuter, LoadFlags); return nullptr; } InOuter = FilenamePkg; Result = FLinkerLoad::FindExistingLinkerForPackage(InOuter); } else if (InOuter != FilenamePkg) //!!should be tested and validated in new UnrealEd { // Loading a new file into an existing package, so reset the loader. //UE_LOG(LogLinker, Log, TEXT("New File, Existing Package (%s, %s)"), *InOuter->GetFullName(), *FilenamePkg->GetFullName() ); ResetLoaders( InOuter ); } } #if 0 // Make sure the package is accessible in the sandbox. if( Sandbox && !Sandbox->SupportsPackage(InOuter) ) { FFormatNamedArguments Arguments; Arguments.Add(TEXT("AssetName"), FText::FromString(InOuter->GetName())); LogGetPackageLinkerError(Result, InLongPackageName, FText::Format(LOCTEXT("Sandbox", "Asset '{AssetName}' is not accessible in this sandbox"), Arguments), LOCTEXT("SandboxShort", "Asset is not accessible in this sandbox"), InOuter, LoadFlags); return nullptr; } #endif // Create new linker. if( !Result ) { check(IsLoading()); // we will already have found the filename above check(NewFilename.Len() > 0); Result = FLinkerLoad::CreateLinker( InOuter, *NewFilename, LoadFlags ); } // Verify compatibility. if (Result && CompatibleGuid && Result->Summary.Guid != *CompatibleGuid) { FFormatNamedArguments Arguments; Arguments.Add(TEXT("AssetName"), FText::FromString(InOuter->GetName())); // This should never fire, because FindPackageFile should never return an incompatible file LogGetPackageLinkerError(Result, InLongPackageName, FText::Format(LOCTEXT("PackageVersion", "Asset '{AssetName}' version mismatch"), Arguments), LOCTEXT("PackageVersionShort", "Asset version mismatch"), InOuter, LoadFlags); return nullptr; } return Result; }