void FCollectionManager::LoadCollections() { const double LoadStartTime = FPlatformTime::Seconds(); int32 NumCollectionsLoaded = 0; for (int32 CacheIdx = 0; CacheIdx < ECollectionShareType::CST_All; ++CacheIdx) { const FString& CollectionFolder = CollectionFolders[CacheIdx]; const FString WildCard = FString::Printf(TEXT("%s/*.%s"), *CollectionFolder, *CollectionExtension); TArray<FString> Filenames; IFileManager::Get().FindFiles(Filenames, *WildCard, true, false); for (int32 FileIdx = 0; FileIdx < Filenames.Num(); ++FileIdx) { const FString Filename = CollectionFolder / Filenames[FileIdx]; FCollection Collection; const bool bUseSCC = ShouldUseSCC(ECollectionShareType::Type(CacheIdx)); if ( Collection.LoadFromFile(Filename, bUseSCC) ) { AddCollection(Collection, ECollectionShareType::Type(CacheIdx)); } else { UE_LOG(LogCollectionManager, Warning, TEXT("Failed to load collection file %s"), *Filename); } } NumCollectionsLoaded += CachedCollections[CacheIdx].Num(); } UE_LOG( LogCollectionManager, Log, TEXT( "Loaded %d collections in %0.6f seconds" ), NumCollectionsLoaded, FPlatformTime::Seconds() - LoadStartTime ); }
bool FCollectionManager::DestroyCollection(FName CollectionName, ECollectionShareType::Type ShareType) { if ( !ensure(ShareType < ECollectionShareType::CST_All) ) { // Bad share type LastError = LOCTEXT("Error_Internal", "There was an internal error."); return false; } FCollection** CollectionPtr = CachedCollections[ShareType].Find(CollectionName); if (CollectionPtr != NULL && ensure(*CollectionPtr != NULL) ) { FCollection* Collection = *CollectionPtr; if ( Collection->DeleteSourceFile(LastError) ) { RemoveCollection(Collection, ShareType); CollectionDestroyedEvent.Broadcast( FCollectionNameType( CollectionName, ShareType ) ); return true; } else { // Failed to delete the source file return false; } } else { // The collection doesn't exist LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist."); return false; } }
bool FCollectionManager::IsCollectionEmpty(FName CollectionName, ECollectionShareType::Type ShareType) { if ( !ensure(ShareType < ECollectionShareType::CST_All) ) { // Bad share type LastError = LOCTEXT("Error_Internal", "There was an internal error."); return true; } FCollection*const* CollectionPtr = CachedCollections[ShareType].Find(CollectionName); FCollection* Collection = NULL; if ( CollectionPtr ) { Collection = *CollectionPtr; ensure(Collection); } if ( Collection ) { return Collection->IsEmpty(); } return true; }
bool FCollectionManager::GetAssetsInCollection(FName CollectionName, ECollectionShareType::Type ShareType, TArray<FName>& AssetsPaths) const { if ( ShareType == ECollectionShareType::CST_All ) { // Asked for all share types, find assets in the specified collection name in any cache bool bFoundAssets = false; for (int32 CacheIdx = 0; CacheIdx < ECollectionShareType::CST_All; ++CacheIdx) { if ( GetAssetsInCollection(CollectionName, ECollectionShareType::Type(CacheIdx), AssetsPaths) ) { bFoundAssets = true; } } return bFoundAssets; } else { FCollection*const* CollectionPtr = CachedCollections[ShareType].Find(CollectionName); FCollection* Collection = NULL; if (CollectionPtr) { Collection = *CollectionPtr; ensure(Collection); } if ( Collection ) { Collection->GetAssetsInCollection(AssetsPaths); return true; } return false; } }
bool FCollectionManager::CreateCollection(FName CollectionName, ECollectionShareType::Type ShareType) { if ( !ensure(ShareType < ECollectionShareType::CST_All) ) { // Bad share type LastError = LOCTEXT("Error_Internal", "There was an internal error."); return false; } // Try to add the collection bool bUseSCC = ShouldUseSCC(ShareType); FString SourceFolder = CollectionFolders[ShareType]; FCollection NewCollection(CollectionName, SourceFolder, CollectionExtension, bUseSCC); FCollection* Collection = AddCollection(NewCollection, ShareType); if ( !Collection ) { // Failed to add the collection, it already exists LastError = LOCTEXT("Error_AlreadyExists", "The collection already exists."); return false; } if ( Collection->Save(LastError) ) { // Collection saved! CollectionCreatedEvent.Broadcast( FCollectionNameType( CollectionName, ShareType ) ); return true; } else { // Collection failed to save, remove it from the cache RemoveCollection(Collection, ShareType); return false; } }
bool FCollectionManager::RemoveFromCollection(FName CollectionName, ECollectionShareType::Type ShareType, const TArray<FName>& ObjectPaths, int32* OutNumRemoved) { if ( OutNumRemoved ) { *OutNumRemoved = 0; } if ( !ensure(ShareType < ECollectionShareType::CST_All) ) { // Bad share type LastError = LOCTEXT("Error_Internal", "There was an internal error."); return false; } FCollection** CollectionPtr = CachedCollections[ShareType].Find(CollectionName); if (CollectionPtr != NULL) { FCollection* Collection = *CollectionPtr; TArray<FName> RemovedAssets; for (int32 ObjectIdx = 0; ObjectIdx < ObjectPaths.Num(); ++ObjectIdx) { const FName& ObjectPath = ObjectPaths[ObjectIdx]; if ( Collection->RemoveAssetFromCollection(ObjectPath) ) { RemovedAssets.Add(ObjectPath); } } if ( RemovedAssets.Num() == 0 ) { // Failed to remove, none of the objects were in the collection LastError = LOCTEXT("Error_NotInCollection", "None of the assets were in the collection."); return false; } if ( Collection->Save(LastError) ) { // Removed and saved if ( OutNumRemoved ) { *OutNumRemoved = RemovedAssets.Num(); } FCollectionNameType CollectionNameType( Collection->GetCollectionName(), ShareType ); AssetsRemovedEvent.Broadcast( CollectionNameType, ObjectPaths ); return true; } else { // Removed but not saved, revert the remove for (int32 AssetIdx = 0; AssetIdx < RemovedAssets.Num(); ++AssetIdx) { Collection->AddAssetToCollection(RemovedAssets[AssetIdx]); } return false; } } else { // Collection not found LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist."); return false; } }
bool FCollectionManager::AddToCollection(FName CollectionName, ECollectionShareType::Type ShareType, const TArray<FName>& ObjectPaths, int32* OutNumAdded) { if ( OutNumAdded ) { *OutNumAdded = 0; } if ( !ensure(ShareType < ECollectionShareType::CST_All) ) { // Bad share type LastError = LOCTEXT("Error_Internal", "There was an internal error."); return false; } FCollection** CollectionPtr = CachedCollections[ShareType].Find(CollectionName); FCollection* Collection = NULL; if (CollectionPtr != NULL) { Collection = *CollectionPtr; } else { // Collection doesn't exist LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist."); return false; } if ( ensure(Collection) ) { int32 NumAdded = 0; for (int32 ObjectIdx = 0; ObjectIdx < ObjectPaths.Num(); ++ObjectIdx) { if ( Collection->AddAssetToCollection(ObjectPaths[ObjectIdx]) ) { NumAdded++; } } if ( NumAdded > 0 ) { if ( Collection->Save(LastError) ) { // Added and saved if ( OutNumAdded ) { *OutNumAdded = NumAdded; } FCollectionNameType CollectionNameType( Collection->GetCollectionName(), ShareType ); AssetsAddedEvent.Broadcast( CollectionNameType, ObjectPaths ); return true; } else { // Added but not saved, revert the add for (int32 ObjectIdx = 0; ObjectIdx < ObjectPaths.Num(); ++ObjectIdx) { Collection->RemoveAssetFromCollection(ObjectPaths[ObjectIdx]); } return false; } } else { // Failed to add, all of the objects were already in the collection LastError = LOCTEXT("Error_AlreadyInCollection", "All of the assets were already in the collection."); return false; } } else { // Collection was never found. Perhaps it was null in the TMap LastError = LOCTEXT("Error_DoesntExist", "The collection doesn't exist."); return false; } }
bool FCollection::DeleteFromSourceControl(FText& OutError) { ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); if ( !ISourceControlModule::Get().IsEnabled() ) { OutError = LOCTEXT("Error_SCCDisabled", "Source control is not enabled. Enable source control in the preferences menu."); return false; } if ( !SourceControlProvider.IsAvailable() ) { OutError = LOCTEXT("Error_SCCNotAvailable", "Source control is currently not available. Check your connection and try again."); return false; } bool bDeletedSuccessfully = false; const int32 DeleteProgressDenominator = 2; int32 DeleteProgressNumerator = 0; const FText CollectionNameText = FText::FromName( CollectionName ); FFormatNamedArguments Args; Args.Add( TEXT("CollectionName"), CollectionNameText ); const FText StatusUpdate = FText::Format( LOCTEXT("DeletingCollection", "Deleting Collection {CollectionName}"), Args ); GWarn->BeginSlowTask( StatusUpdate, true ); GWarn->UpdateProgress(DeleteProgressNumerator++, DeleteProgressDenominator); FString AbsoluteFilename = FPaths::ConvertRelativePathToFull(SourceFilename); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); GWarn->UpdateProgress(DeleteProgressNumerator++, DeleteProgressDenominator); // If checked out locally for some reason, revert if (SourceControlState.IsValid() && (SourceControlState->IsAdded() || SourceControlState->IsCheckedOut() || SourceControlState->IsDeleted())) { if ( !RevertCollection(OutError) ) { // Failed to revert, just bail out GWarn->EndSlowTask(); return false; } // Make sure we get a fresh state from the server SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); } // If not at the head revision, sync up if (SourceControlState.IsValid() && !SourceControlState->IsCurrent()) { if ( !SourceControlProvider.Execute(ISourceControlOperation::Create<FSync>(), AbsoluteFilename) ) { // Could not sync up with the head revision GWarn->EndSlowTask(); OutError = LOCTEXT("Error_SCCSync", "Failed to sync collection to the head revision."); return false; } // Check to see if the file exists at the head revision if ( IFileManager::Get().FileSize(*SourceFilename) < 0 ) { // File was already deleted, consider this a success GWarn->EndSlowTask(); return true; } FCollection NewCollection; if ( !NewCollection.LoadFromFile(SourceFilename, false) ) { // Failed to load the head revision file so it isn't safe to delete it GWarn->EndSlowTask(); OutError = LOCTEXT("Error_SCCBadHead", "Failed to load the collection at the head revision."); return false; } // Loaded the head revision, now merge up so the files are in a consistent state MergeWithCollection(NewCollection); // Make sure we get a fresh state from the server SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); } GWarn->UpdateProgress(DeleteProgressNumerator++, DeleteProgressDenominator); if(SourceControlState.IsValid()) { if(SourceControlState->IsAdded() || SourceControlState->IsCheckedOut()) { OutError = LOCTEXT("Error_SCCDeleteWhileCheckedOut", "Failed to delete collection in source control because it is checked out or open for add."); } else if(SourceControlState->CanCheckout()) { if ( SourceControlProvider.Execute(ISourceControlOperation::Create<FDelete>(), AbsoluteFilename) ) { // Now check in the delete const FText ChangelistDesc = FText::Format( LOCTEXT("CollectionDeletedDesc", "Deleted collection: {CollectionName}"), CollectionNameText ); TSharedRef<FCheckIn, ESPMode::ThreadSafe> CheckInOperation = ISourceControlOperation::Create<FCheckIn>(); CheckInOperation->SetDescription(ChangelistDesc.ToString()); if ( SourceControlProvider.Execute( CheckInOperation, AbsoluteFilename ) ) { // Deleted successfully! bDeletedSuccessfully = true; } else { FText Unused; if ( !RevertCollection(Unused) ) { UE_LOG(LogCollectionManager, Warning, TEXT("Failed to revert collection '%s' after failing to check in the file that was marked for delete."), *CollectionName.ToString()); } OutError = LOCTEXT("Error_SCCCheckIn", "Failed to check in collection."); } } else { OutError = LOCTEXT("Error_SCCDeleteFailed", "Failed to delete collection in source control."); } } else if(!SourceControlState->IsSourceControlled()) { // Not yet in the depot or deleted. We can just delete it from disk. bDeletedSuccessfully = IFileManager::Get().Delete(*AbsoluteFilename); if ( !bDeletedSuccessfully ) { OutError = LOCTEXT("Error_DiskDeleteFailed", "Failed to delete the collection file from disk."); } } else if (!SourceControlState->IsCurrent()) { OutError = LOCTEXT("Error_SCCNotCurrent", "Collection is not at head revision after sync."); } else if(SourceControlState->IsCheckedOutOther()) { OutError = LOCTEXT("Error_SCCCheckedOutOther", "Collection is checked out by another user."); } else { OutError = LOCTEXT("Error_SCCUnknown", "Could not determine source control state."); } } else { OutError = LOCTEXT("Error_SCCInvalid", "Source control state is invalid."); } GWarn->UpdateProgress(DeleteProgressNumerator++, DeleteProgressDenominator); GWarn->EndSlowTask(); return bDeletedSuccessfully; }
bool FCollection::CheckoutCollection(FText& OutError) { if ( !ensure(SourceFilename.Len()) ) { OutError = LOCTEXT("Error_Internal", "There was an internal error."); return false; } ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); if ( !ISourceControlModule::Get().IsEnabled() ) { OutError = LOCTEXT("Error_SCCDisabled", "Source control is not enabled. Enable source control in the preferences menu."); return false; } if ( !SourceControlProvider.IsAvailable() ) { OutError = LOCTEXT("Error_SCCNotAvailable", "Source control is currently not available. Check your connection and try again."); return false; } FString AbsoluteFilename = FPaths::ConvertRelativePathToFull(SourceFilename); FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); bool bSuccessfullyCheckedOut = false; TArray<FString> FilesToBeCheckedOut; FilesToBeCheckedOut.Add(AbsoluteFilename); if (SourceControlState.IsValid() && SourceControlState->IsDeleted()) { // Revert our delete if ( !RevertCollection(OutError) ) { return false; } // Make sure we get a fresh state from the server SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); } // If not at the head revision, sync up if (SourceControlState.IsValid() && !SourceControlState->IsCurrent()) { if ( !SourceControlProvider.Execute(ISourceControlOperation::Create<FSync>(), FilesToBeCheckedOut) ) { // Could not sync up with the head revision OutError = LOCTEXT("Error_SCCSync", "Failed to sync collection to the head revision."); return false; } // Check to see if the file exists at the head revision if ( IFileManager::Get().FileSize(*SourceFilename) < 0 ) { // File was deleted at head... // Just add our changes to an empty list so we can mark it for add FCollection NewCollection; MergeWithCollection(NewCollection); } else { // File found! Load it and merge with our local changes FCollection NewCollection; if ( !NewCollection.LoadFromFile(SourceFilename, false) ) { // Failed to load the head revision file so it isn't safe to delete it OutError = LOCTEXT("Error_SCCBadHead", "Failed to load the collection at the head revision."); return false; } // Loaded the head revision, now merge up so the files are in a consistent state MergeWithCollection(NewCollection); } // Make sure we get a fresh state from the server SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate); } if(SourceControlState.IsValid()) { if(SourceControlState->IsAdded() || SourceControlState->IsCheckedOut()) { // Already checked out or opened for add bSuccessfullyCheckedOut = true; } else if(SourceControlState->CanCheckout()) { // In depot and needs to be checked out bSuccessfullyCheckedOut = (SourceControlProvider.Execute(ISourceControlOperation::Create<FCheckOut>(), FilesToBeCheckedOut) == ECommandResult::Succeeded); if (!bSuccessfullyCheckedOut) { OutError = LOCTEXT("Error_SCCCheckout", "Failed to check out collection."); } } else if(!SourceControlState->IsSourceControlled()) { // Not yet in the depot. Add it. bSuccessfullyCheckedOut = (SourceControlProvider.Execute(ISourceControlOperation::Create<FMarkForAdd>(), FilesToBeCheckedOut) == ECommandResult::Succeeded); if (!bSuccessfullyCheckedOut) { OutError = LOCTEXT("Error_SCCAdd", "Failed to add new collection to source control."); } } else if(!SourceControlState->IsCurrent()) { OutError = LOCTEXT("Error_SCCNotCurrent", "Collection is not at head revision after sync."); } else if(SourceControlState->IsCheckedOutOther()) { OutError = LOCTEXT("Error_SCCCheckedOutOther", "Collection is checked out by another user."); } else { OutError = LOCTEXT("Error_SCCUnknown", "Could not determine source control state."); } } else { OutError = LOCTEXT("Error_SCCInvalid", "Source control state is invalid."); } return bSuccessfullyCheckedOut; }