Failed to find asset in mounted pak

Hello everyone. I’ve been strugling with loading an asset from a mounted pak file and I don’t seem to find the correct way to do this.
I’ve created the function down below that mounts the desired pak (and succeeds) but when loading the assets it always fails.
Funny enough when loading the assets synchronously it finds and load them but for some reason async load always fails to find them (on debug, developement and shipping builds). I will really apreciate if someone could give me some guidance.

To make it clearer here is the testing im doing:

  • Looking for pak in the PersistentDownloadDir
  • Mount the pak to /Game/TestingContent/
  • Reading all the files inside the pak with a visitor (which always work).
  • Using the listed files to generate unreal like soft paths (for instance: “/Game/TestingContent/TestingTexture.TestingTexture”).
  • lod sync succeeds (in editor) but async load fails in both developement and shipping builds.

Heres the code:

void UNigisUtils::MountPakAndStartAssetLoading(const FString & PakFolder, const FString& PakName, const FString& MountPoint, TArray<FString>& AssetStrings, bool bSyncPreloadAssets)
{
	UE_LOG(LogTemp, Log, TEXT("============= START LOADING PAK ============="));
	if (!PakFolder.IsEmpty() && !PakName.IsEmpty() && PakName.Right(3).Equals(TEXT("pak"), ESearchCase::IgnoreCase))
	{
		IPlatformFile* PreviousPlatformFile = nullptr;
		FPakPlatformFile *PlatformFile = nullptr;
		if (FString(FPlatformFileManager::Get().GetPlatformFile().GetName()).Equals(FString(TEXT("PakFile"))))
		{
			PlatformFile = static_cast<FPakPlatformFile*>(&FPlatformFileManager::Get().GetPlatformFile());
		}
		else
		{
			PlatformFile = new FPakPlatformFile();

			if (!PlatformFile->Initialize(&FPlatformFileManager::Get().GetPlatformFile(), TEXT("")))
			{
				UE_LOG(LogTemp, Error, TEXT("FPakPlatformFile failed to initialize"));
				return;
			}
			PreviousPlatformFile = &FPlatformFileManager::Get().GetPlatformFile();
			FPlatformFileManager::Get().SetPlatformFile(*PlatformFile);
		}

		FString FullPakFolder(FPaths::ConvertRelativePathToFull(PakFolder));
		if (!PlatformFile->DirectoryExists(*FullPakFolder))
		{
			UE_LOG(LogTemp, Error, TEXT("Directory not found: %s"), *FullPakFolder);
			return;
		}
		else UE_LOG(LogTemp, Warning, TEXT("Pak folder verified: %s"), *FullPakFolder);

		if (FCoreDelegates::OnMountPak.IsBound())
		{
			// Print files in pak
			struct FilesDump : public IPlatformFile::FDirectoryVisitor
			{
				FString mp_PakName;
				TArray<FString> Files;

				FilesDump(FString &PakfileName)
					: mp_PakName(PakfileName)
				{}

				virtual bool Visit(const TCHAR *FilenameOrDirectory, bool bIsDirectory)
				{
					if (bIsDirectory)
					{
						UE_LOG(LogTemp, Log, TEXT("Found DIR in %s: %s"), *mp_PakName, FilenameOrDirectory);
					}
					else
					{
						const FString Filename(FilenameOrDirectory);
						if (Filename.Contains(TEXT(".umap")) || Filename.Contains(TEXT(".uasset")))
						{
							Files.Add(FilenameOrDirectory);
							UE_LOG(LogTemp, Log, TEXT("Found FILE in %s: %s"), *mp_PakName, FilenameOrDirectory);
						}
					}
					return true;
				}
			};

			FString PakfileName = PakName;
			FilesDump Visitor(PakfileName);
			FString FullPath = FPaths::Combine(PakFolder, PakName);

			FPakFile PakFile(PlatformFile, *FullPath, false);
			if (PakFile.IsValid())
			{
				FString InMountPoint = MountPoint;
				FPaths::MakePathRelativeTo(InMountPoint, *FPaths::ProjectContentDir());
				InMountPoint = FString("/Game/" + InMountPoint);

				PakFile.SetMountPoint(*InMountPoint);
				//if (PlatformFile->Mount(*FullPath, 4, *InMountPoint))
				if(FCoreDelegates::OnMountPak.Execute(FullPath, 4, &Visitor))
				{
					UE_LOG(LogTemp, Log, TEXT("Pak Mount success!"));    
					UE_LOG(LogTemp, Warning, TEXT("Reading Pak Contents..."));
					LoadedAssets.Empty();

					TArray<FString> Files;
					PakFile.FindFilesAtPath(Files, *InMountPoint, true, false, true);

					//PlatformFile->IterateDirectoryRecursively(*MountPoint, Visitor);

					UE_LOG(LogTemp, Warning, TEXT("\n Generating Asset References..."));
					TArray<FSoftObjectPath> ObjectPaths;
					UE_LOG(LogTemp, Log, TEXT("------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"));
					for (FString File : Files)
					{
						FString Filename, FileExtn;
						int32 LastSlashIndex;
						File.FindLastChar(*TEXT("/"), LastSlashIndex);
						FString FileOnly = File.RightChop(LastSlashIndex + 1); //remove the full size since we want the slash gone too.
						UE_LOG(LogTemp, Log, TEXT("File: %s ============ Full Path: %s"), *FileOnly, *File);
						FileOnly.Split(TEXT("."), &Filename, &FileExtn);

						if (FileExtn == TEXT("uasset"))
						{
							File = File.Replace(TEXT("uasset"), *Filename);
							/*int32 ContentFolderIndex = File.Find(TEXT("/Content"), ESearchCase::Type::CaseSensitive, ESearchDir::Type::FromEnd);
							FString RelativePath = File.RightChop(ContentFolderIndex + 8);
							FString AssetPath("/Game" + RelativePath);*/

							FStringAssetReference AssetRef(*File);
							Assets.Add(TAssetPtr<UObject>(AssetRef));
							AssetStrings.Add(File);

							//Add a soft object path to the list
							FSoftObjectPath SoftObjectPath(File);
							ObjectPaths.Add(SoftObjectPath);
							SOPtrs.Add(TSoftObjectPtr<UObject>(SoftObjectPath));

							UE_LOG(LogTemp, Log, TEXT("File: %s, Asset reference: %s"), *FileOnly, *AssetRef.ToString());
						} else UE_LOG(LogTemp, Log, TEXT("File: %s is not an asset file. No reference generated."), *FileOnly)
						
						UE_LOG(LogTemp, Log, TEXT("------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"));
					}
					UE_LOG(LogTemp, Warning, TEXT("\n %d references Generated."), ObjectPaths.Num());

					//Optionally synchronously loads all the references found in the pak
					if (bSyncPreloadAssets)
					{
						UE_LOG(LogTemp, Warning, TEXT("\n Attempting to syncronous load generated references..."));
						for (const FSoftObjectPath ObjectPath : ObjectPaths)
						{
							UObject* LoadedAsset = ObjectPath.TryLoad();
							if (LoadedAsset != nullptr)
							{
								UE_LOG(LogTemp, Log, TEXT("Asset Reference: %s, Succesfully loaded!"), *ObjectPath.ToString());
								LoadedAssets.Add(LoadedAsset);
							}
							else UE_LOG(LogTemp, Error, TEXT("Asset Reference: %s, load failed."), *ObjectPath.ToString());
						}
						OnAssetsLoadComplete.Broadcast(LoadedAssets);
					}
					else
					{
						UE_LOG(LogTemp, Warning, TEXT("\n Starting Async load of assets..."));
						StreamManager.RequestAsyncLoad(ObjectPaths, FStreamableDelegate::CreateUObject(this, &UNigisUtils::ObjectsLoadingComplete));
					}
				}
				else UE_LOG(LogTemp, Error, TEXT("Pak Mount failed."));
			}
			
		}
		else UE_LOG(LogTemp, Error, TEXT("\"OnMountPak\" Core delegate is not bound."));

		// return previous platform file manager to the top of the chain, so Unreal doesn't lose it's references
		if (PreviousPlatformFile != nullptr)
		{
			FPlatformFileManager::Get().SetPlatformFile(*PreviousPlatformFile);
		}
	}
	UE_LOG(LogTemp, Log, TEXT("============= END LOADING PAK ============="));
}

Hi Bariudol,

After doing some Googling, it looks like you’re using code from an answer to this post. Is there a reason why you omitted PlatformFile->InitializeNewAsyncIO(); before calling FPlatformFileManager::SetPlatformFile?

Wow, i completly missed that for some reason. Il try it out. Thanks!

Hello there again. I’ve tried that, and modified the function to use the asset registry to find the assets instead of trying to manually find them myself. In editor the function works great and does find the assets in the pak (obviously they dont load because the content is cooked), but when packaging the game, the asset registry never finds the files in the pak althought the mount is suposedly succesful.

Here is a snipet of the new function that works in editor:

if (bMountSuccessful)
                 {
                     FString MountVirtualPoint = UsedMountPoint;
                     FPaths::MakePathRelativeTo(MountVirtualPoint, *FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()));
                     MountVirtualPoint = MountVirtualPoint.Replace(TEXT("Content/"), TEXT("/Game/"));
                     const FString PackagePath = MountVirtualPoint;
                     MountVirtualPoint.Append(TEXT("/"));
 
                     TArray<FString> Files;
                     PakFile->FindFilesAtPath(Files, *UsedMountPoint, true, false, true);
                     if (Files.Num() > 0)
                     {    
                         FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(FName("AssetRegistry"));
                         IAssetRegistry& assetRegistry = AssetRegistryModule.Get();
 
                         TArray<FString> pathsToScan;
                         pathsToScan.Add(TEXT("/Game"));
                         assetRegistry.ScanPathsSynchronous(pathsToScan, true);
 
                         TArray<FAssetData> AllAssetList;
                         TArray<FAssetData> AssetList;
 
                         assetRegistry.GetAllAssets(AllAssetList);
                         assetRegistry.GetAssetsByPath(TEXT("/Game"), AssetList, true, false);

I can post the whole function if needed. Thank you in advance for any possible help!

Hi Bariudol,

You cannot use the asset registry at runtime because it is essentially an editor-only module. Have you also tried looking at this forum post on loading assets from a pak file? I was also able to find an example of async loading in this plugin mentioned in this post.

@DarkwindRichard The plugin you mention does not work since version 4.15, it is what we used before updating.

I’ve managed, however, to get the function working by using the asset registry, turns out that the paths I was giving to it where simply wrong. I don’t know if its a bug or what, but in editor the mounting and asset registry works only if mounting with a full path (e.g. E:/Project/Content/), but in a packaged game it works only when mounting to a path relative to the engine (e.g …/…/…/Project/Content); or maybe its just a problem with my function. In any case, intended or not, it now works haha. Thanks for your attention!

Hey ! is it possible to have your code ? I’m stuck on this problem since a long time… I would like to load .pak made with UnrealPak.exe into a project from local path and actually assets are not found with asyncload…

Would also love to take a look at the code if possible?