Runtime content loading system

This commit is contained in:
Oleg Petruny 2024-08-08 12:23:15 +02:00
parent d8933a111b
commit c2ada5cb47
23 changed files with 379 additions and 14 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
Binaries
Build
DerivedDataCache
Intermediate
Saved

View File

@ -1,4 +1,5 @@
[/Script/EngineSettings.GeneralProjectSettings]
ProjectID=09D27A7F49EDD2A17DE1879F3BDEF448
ProjectVersion=0.1
@ -15,6 +16,107 @@ bMoviesAreSkippable=True
bWaitForMoviesToComplete=True
[/Script/UnrealEd.ProjectPackagingSettings]
Build=IfProjectHasCode
BuildConfiguration=PPBC_Development
BuildTarget=
FullRebuild=False
ForDistribution=False
IncludeDebugFiles=False
BlueprintNativizationMethod=Disabled
bIncludeNativizedAssetsInProjectGeneration=False
bExcludeMonolithicEngineHeadersInNativizedCode=False
UsePakFile=True
bUseIoStore=False
bUseZenStore=False
bMakeBinaryConfig=False
bGenerateChunks=True
bGenerateNoChunks=False
bChunkHardReferencesOnly=False
bForceOneChunkPerFile=False
MaxChunkSize=0
bBuildHttpChunkInstallData=False
HttpChunkInstallDataDirectory=(Path="")
WriteBackMetadataToAssetRegistry=Disabled
bWritePluginSizeSummaryJsons=False
bCompressed=True
PackageCompressionFormat=Oodle
bForceUseProjectCompressionFormatIgnoreHardwareOverride=False
PackageAdditionalCompressionOptions=
PackageCompressionMethod=Kraken
PackageCompressionLevel_DebugDevelopment=4
PackageCompressionLevel_TestShipping=4
PackageCompressionLevel_Distribution=7
PackageCompressionMinBytesSaved=1024
PackageCompressionMinPercentSaved=5
bPackageCompressionEnableDDC=False
PackageCompressionMinSizeToConsiderDDC=0
HttpChunkInstallDataVersion=
IncludePrerequisites=True
IncludeAppLocalPrerequisites=False
bShareMaterialShaderCode=True
bDeterministicShaderCodeOrder=False
bSharedMaterialNativeLibraries=True
ApplocalPrerequisitesDirectory=(Path="")
IncludeCrashReporter=False
InternationalizationPreset=English
-CulturesToStage=en
+CulturesToStage=en
LocalizationTargetCatchAllChunkId=0
bCookAll=False
bCookMapsOnly=False
bSkipEditorContent=True
bSkipMovies=False
-IniKeyDenylist=KeyStorePassword
-IniKeyDenylist=KeyPassword
-IniKeyDenylist=rsa.privateexp
-IniKeyDenylist=rsa.modulus
-IniKeyDenylist=rsa.publicexp
-IniKeyDenylist=aes.key
-IniKeyDenylist=SigningPublicExponent
-IniKeyDenylist=SigningModulus
-IniKeyDenylist=SigningPrivateExponent
-IniKeyDenylist=EncryptionKey
-IniKeyDenylist=DevCenterUsername
-IniKeyDenylist=DevCenterPassword
-IniKeyDenylist=IOSTeamID
-IniKeyDenylist=SigningCertificate
-IniKeyDenylist=MobileProvision
-IniKeyDenylist=IniKeyDenylist
-IniKeyDenylist=IniSectionDenylist
+IniKeyDenylist=KeyStorePassword
+IniKeyDenylist=KeyPassword
+IniKeyDenylist=rsa.privateexp
+IniKeyDenylist=rsa.modulus
+IniKeyDenylist=rsa.publicexp
+IniKeyDenylist=aes.key
+IniKeyDenylist=SigningPublicExponent
+IniKeyDenylist=SigningModulus
+IniKeyDenylist=SigningPrivateExponent
+IniKeyDenylist=EncryptionKey
+IniKeyDenylist=DevCenterUsername
+IniKeyDenylist=DevCenterPassword
+IniKeyDenylist=IOSTeamID
+IniKeyDenylist=SigningCertificate
+IniKeyDenylist=MobileProvision
+IniKeyDenylist=IniKeyDenylist
+IniKeyDenylist=IniSectionDenylist
-IniSectionDenylist=HordeStorageServers
-IniSectionDenylist=StorageServers
+IniSectionDenylist=HordeStorageServers
+IniSectionDenylist=StorageServers
+DirectoriesToAlwaysCook=(Path="/Game/Misc/Interactables")
bRetainStagedDirectory=False
CustomStageCopyHandler=
[/Script/Engine.AssetManagerSettings]
-PrimaryAssetTypesToScan=(PrimaryAssetType="Map",AssetBaseClass=/Script/Engine.World,bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game/Maps")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown))
-PrimaryAssetTypesToScan=(PrimaryAssetType="PrimaryAssetLabel",AssetBaseClass=/Script/Engine.PrimaryAssetLabel,bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown))
+PrimaryAssetTypesToScan=(PrimaryAssetType="Map",AssetBaseClass="/Script/Engine.World",bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game/Maps")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown))
+PrimaryAssetTypesToScan=(PrimaryAssetType="PrimaryAssetLabel",AssetBaseClass="/Script/Engine.PrimaryAssetLabel",bHasBlueprintClasses=False,bIsEditorOnly=False,Directories=((Path="/Game")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown))
bOnlyCookProductionAssets=False
bShouldManagerDetermineTypeAndName=False
bShouldGuessTypeAndNameInEditor=True
bShouldAcquireMissingChunksOnLoad=False
bShouldWarnAboutInvalidAssets=True
MetaDataTagsForAssetRegistry=()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -8,7 +8,7 @@ public class Lost_Edge : ModuleRules {
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "OpenCV" });
PrivateDependencyModuleNames.AddRange(new string[] { "EnhancedInput", "UMG", "RHI", "RenderCore", "Lost_EdgeShaders", "TextureCompressor",
PrivateDependencyModuleNames.AddRange(new string[] { "EnhancedInput", "UMG", "RHI", "RenderCore", "Lost_EdgeShaders", "PakFile", //"TextureCompressor",
"LevelSequence", "MovieScene" }); // "Slate", "SlateCore"
// UE_LOG(LogTemp, Log, TEXT("capture: %s"), (capture ? TEXT("true") : TEXT("false")));

View File

@ -0,0 +1,211 @@
// Oleg Petruny proprietary.
#include "ContentLoader.h"
#include "IPlatformFilePak.h"
#include "UObject/Class.h"
#include "UObject/UObjectIterator.h"
namespace
{
constexpr auto mountPath = TEXT("/Game/LoadedContent");
}
UClass* UContentLoader::MountAndRegisterPak(FString pakFilePath)
{
if(pakFilePath.IsEmpty())
return nullptr;
pakFilePath = FPaths::ProjectContentDir() / pakFilePath;
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, FString::Printf(TEXT("ContentLoader: Starting to mount pak: %s"), *pakFilePath));
UE_LOG(LogTemp, Log, TEXT("ContentLoader: Starting to mount pak: %s"), *pakFilePath);
if(!MountPak(*pakFilePath))
return nullptr;
TArray<FString> content;
FString mountPoint = GetPakMountContentPath(pakFilePath, content);
//RegisterMountPoint("/Game/", mountPoint);
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, FString::Printf(TEXT("ContentLoader: Pak '%s' contains %d items"), *FPaths::GetCleanFilename(pakFilePath), content.Num()));
UE_LOG(LogTemp, Log, TEXT("ContentLoader: Pak '%s' contains %d items"), *FPaths::GetCleanFilename(pakFilePath), content.Num());
for(auto& i : content)
{
if(FPaths::GetCleanFilename(i).StartsWith(TEXT("BP_")))
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, FString::Printf(TEXT("ContentLoader: Found BP class: '%s'"), *i));
UE_LOG(LogTemp, Log, TEXT("ContentLoader: Found BP class: '%s'"), *i);
return LoadPakObjClassReference(i);
}
}
return nullptr;
}
void UContentLoader::BeginDestroy()
{
while(mountedFiles.Num() > 0)
UnmountPak(*mountedFiles.begin());
FPlatformFileManager::Get().RemovePlatformFile(GetPakPlatformFile());
UObject::BeginDestroy();
}
FPakPlatformFile* UContentLoader::GetPakPlatformFile()
{
FPakPlatformFile* pakPlatformFile = nullptr;
IPlatformFile& platformFile = FPlatformFileManager::Get().GetPlatformFile();
if(!platformFile.GetLowerLevel())
{
pakPlatformFile = new FPakPlatformFile();
pakPlatformFile->Initialize(&platformFile, TEXT(""));
FPlatformFileManager::Get().SetPlatformFile(*pakPlatformFile);
}
else
{
pakPlatformFile = (FPakPlatformFile*)(FPlatformFileManager::Get().FindPlatformFile(TEXT("PakFile")));
}
if(!pakPlatformFile)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("ContentLoader: PakFileManager not found"));
UE_LOG(LogTemp, Log, TEXT("ContentLoader: PakFileManager not found"));
}
return pakPlatformFile;
}
bool UContentLoader::MountPak(const FString& path)
{
FPakPlatformFile* pakFileMgr = GetPakPlatformFile();
if(!pakFileMgr)
return false;
FString mountPoint = FString::Printf(TEXT("%s/%s"), mountPath, *FPaths::GetBaseFilename(*path));
bool mounted = pakFileMgr->Mount(*path, 0, *mountPoint);
if(!mounted)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FString::Printf(TEXT("ContentLoader: Pak %s mount failed"), *FPaths::GetCleanFilename(path)));
UE_LOG(LogTemp, Log, TEXT("ContentLoader: Pak %s mount failed"), *FPaths::GetCleanFilename(path));
return false;
}
else
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, FString::Printf(TEXT("ContentLoader: Pak %s mounted at %s"), *FPaths::GetCleanFilename(path), *mountPoint));
UE_LOG(LogTemp, Log, TEXT("ContentLoader: Pak %s mounted at %s"), *FPaths::GetCleanFilename(path), *mountPoint);
mountedLock.Lock();
mountedFiles.Add(path);
mountedLock.Unlock();
}
return true;
}
bool UContentLoader::UnmountPak(const FString& path)
{
FPakPlatformFile* pakFileMgr = GetPakPlatformFile();
if(!pakFileMgr)
return false;
bool unmounted = pakFileMgr->Unmount(*path);
if(!unmounted)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FString::Printf(TEXT("ContentLoader: Pak %s unmount failed"), *FPaths::GetCleanFilename(path)));
UE_LOG(LogTemp, Log, TEXT("ContentLoader: Pak %s unmount failed"), *FPaths::GetCleanFilename(path));
return false;
}
else
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, FString::Printf(TEXT("ContentLoader: Pak %s unmounted successfully"), *FPaths::GetCleanFilename(path)));
UE_LOG(LogTemp, Log, TEXT("ContentLoader: Pak %s unmounted successfully"), *FPaths::GetCleanFilename(path));
mountedLock.Lock();
mountedFiles.Remove(path);
mountedLock.Unlock();
}
return true;
}
void UContentLoader::RegisterMountPoint(const FString& rootPath, const FString& contentPath)
{
FPackageName::RegisterMountPoint(rootPath, contentPath);
}
void UContentLoader::UnRegisterMountPoint(const FString& rootPath, const FString& contentPath)
{
FPackageName::UnRegisterMountPoint(rootPath, contentPath);
}
FString UContentLoader::GetPakMountPoint(const FString& pakFilePath)
{
TRefCountPtr<FPakFile> pakFile = new FPakFile(FPlatformFileManager::Get().FindPlatformFile(TEXT("PakFile")), *pakFilePath, false);
if(pakFile.GetReference()->IsValid())
{
return pakFile.GetReference()->GetMountPoint();
}
return {};
}
TArray<FString> UContentLoader::GetPakContent(const FString& pakFilePath, const FString& appendPath)
{
TRefCountPtr<FPakFile> pakFile = new FPakFile(FPlatformFileManager::Get().FindPlatformFile(TEXT("PakFile")), *pakFilePath, false);
TArray<FString> pakContent;
if(pakFile.GetReference()->IsValid())
{
for(FPakFile::FFilenameIterator it{ *pakFile, false }; it; ++it)
{
if(FPaths::GetExtension(it.Filename()) == TEXT("uasset"))
{
pakContent.Add(FString::Printf(TEXT("%s%s"), *appendPath, *it.Filename()));
}
}
}
return pakContent;
}
FString UContentLoader::GetPakMountContentPath(const FString& pakFilePath, TArray<FString>& content)
{
FString contentPath, appendPath;
FString mountPoint = GetPakMountPoint(pakFilePath);
if(mountPoint.Split("/Content/", &contentPath, &appendPath))
{
content = GetPakContent(pakFilePath, appendPath);
return FString::Printf(TEXT("%s/Content/"), *contentPath);
}
else
{
content = GetPakContent(pakFilePath, appendPath);
if(content.Num() > 0)
{
mountPoint = FString::Printf(TEXT("%s%s"), *mountPoint, *content[0]);
if(mountPoint.Split("/Content/", &contentPath, &appendPath))
{
return FString::Printf(TEXT("%s/Content/"), *contentPath);
}
}
}
return {};
}
UClass* UContentLoader::LoadPakObjClassReference(const FString& pakContentPath)
{
FString assetName = FString::Printf(TEXT("/Game/%s.%s_C"), *FPaths::GetBaseFilename(pakContentPath, false), *FPaths::GetBaseFilename(pakContentPath, true));
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, FString::Printf(TEXT("ContentLoader: Loading '%s' class"), *assetName));
UE_LOG(LogTemp, Log, TEXT("ContentLoader: Loading '%s' class"), *assetName);
return StaticLoadClass(UObject::StaticClass(), nullptr, *assetName);
}

View File

@ -0,0 +1,39 @@
// Oleg Petruny proprietary.
#pragma once
#include "UObject/Object.h"
#include "ContentLoader.generated.h"
UCLASS(BlueprintType)
class UContentLoader : public UObject
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "ContentLoader")
UClass* MountAndRegisterPak(FString pakFilePath);
protected:
virtual void BeginDestroy() override;
class FPakPlatformFile* GetPakPlatformFile();
bool MountPak(const FString& path);
bool UnmountPak(const FString& path);
void RegisterMountPoint(const FString& rootPath, const FString& contentPath);
void UnRegisterMountPoint(const FString& rootPath, const FString& contentPath);
FString GetPakMountPoint(const FString& pakFilePath);
TArray<FString> GetPakContent(const FString& pakFilePath, const FString& appendPath);
FString GetPakMountContentPath(const FString& pakFilePath, TArray<FString>& content);
UClass* LoadPakObjClassReference(const FString& pakContentPath);
TSet<FString> mountedFiles;
FCriticalSection mountedLock;
};

View File

@ -9,6 +9,7 @@
#include "Kismet/GameplayStatics.h"
#include "CommonFunctions.h"
#include "ContentLoader.h"
#include "CustomGameUserSettings.h"
#include "Interactable/Activators/InCameraInteractableActivator.h"
#include "Interactable/Activators/RaycastInteractableActivator.h"
@ -22,11 +23,12 @@ UCustomGameInstanceBase* UCustomGameInstanceBase::instance = nullptr;
void UCustomGameInstanceBase::Init()
{
instance = this;
UGameInstance::Init();
// IN FUTURE ASSIGN FROM CONTENT LOADER MEMBER
instance = this;
contentLoader = NewObject<UContentLoader>(this);
interactionsActivators.Add(URaycastInteractableActivator::StaticClass());
interactionsActivators.Add(UInCameraInteractableActivator::StaticClass());

View File

@ -42,4 +42,8 @@ public:
UPROPERTY(VisibleAnywhere)
class USaveData* saveData = nullptr;
protected:
class UContentLoader* contentLoader;
};

View File

@ -18,7 +18,8 @@
UCutsceneManager::UCutsceneManager()
{
_inputContext = { FSoftObjectPath{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/IMC_Cutscene.IMC_Cutscene'") } };
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/IMC_Cutscene.IMC_Cutscene'") };
_inputContext = asset.Object;
if(auto world = GetWorld())
{
if(auto GI = Cast<UCustomGameInstanceBase>(world->GetGameInstance()))

View File

@ -20,7 +20,8 @@
UDialogueManager::UDialogueManager()
{
_inputContext = { FSoftObjectPath{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/IMC_Dialogue.IMC_Dialogue'") } };
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/IMC_Dialogue.IMC_Dialogue'") };
_inputContext = asset.Object;
if(auto world = GetWorld())
{
if(auto GI = Cast<UCustomGameInstanceBase>(world->GetGameInstance()))

View File

@ -11,7 +11,6 @@
#include "RHIGPUReadback.h"
#include "RHIResources.h"
#include "SceneView.h"
#include "TextureCompressorModule.h"
#include "CommonFunctions.h"
#include "Interactable/Interactable.h"
@ -30,7 +29,8 @@ UInteractableScreenCapturer::UInteractableScreenCapturer(const FObjectInitialize
PrimaryComponentTick.bCanEverTick = false;
PrimaryComponentTick.bStartWithTickEnabled = false;
TextureTarget = LoadObject<UTextureRenderTarget2D>(this, TEXT("/Game/Misc/Interactables/T_InteractableScreencapturerDebugInput"));
static ConstructorHelpers::FObjectFinder<UTextureRenderTarget2D> inputTexture{ TEXT("/Script/Engine.TextureRenderTarget2D'/Game/Misc/Interactables/T_InteractableScreencapturerDebugInput.T_InteractableScreencapturerDebugInput'") };
TextureTarget = inputTexture.Object;
//_output = LoadObject<UTextureRenderTarget2D>(this, TEXT("/Game/Misc/Interactables/T_InteractableScreencapturerDebugOutput"));
//TextureTarget = ObjectInitializer.CreateDefaultSubobject<UTextureRenderTarget2D>(this, TEXT("UInteractableScreenCapturer_TextureRenderTarget"));
//TextureTarget->RenderTargetFormat = ETextureRenderTargetFormat::RTF_RGBA8;
@ -49,8 +49,8 @@ UInteractableScreenCapturer::UInteractableScreenCapturer(const FObjectInitialize
CompositeMode = ESceneCaptureCompositeMode::SCCM_Overwrite;
PostProcessBlendWeight = 1;
auto ppMaterial = LoadObject<UMaterial>(this, TEXT("/Game/Misc/Interactables/M_InteractableScreencapturerPP"));
PostProcessSettings.AddBlendable(ppMaterial, 1.0f);
static ConstructorHelpers::FObjectFinder<UMaterial> ppMaterial{ TEXT("/Script/Engine.Material'/Game/Misc/Interactables/M_InteractableScreencapturerPP.M_InteractableScreencapturerPP'") };
PostProcessSettings.AddBlendable(ppMaterial.Object, 1.0f);
PostProcessSettings.Sharpen = 0;
bUseCustomProjectionMatrix = false;
bAlwaysPersistRenderingState = true;

View File

@ -17,7 +17,8 @@ UActivateInteractableModificator::UActivateInteractableModificator(const FObject
{
activatorTypes |= static_cast<uint8>(EActivatorType::Use);
inputMapping = { FSoftObjectPath{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/Interactables/IMC_InteractableActivate.IMC_InteractableActivate'") } };
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/Interactables/IMC_InteractableActivate.IMC_InteractableActivate'") };
inputMapping = asset.Object;
}
void UActivateInteractableModificator::Bind_Implementation(UEnhancedInputComponent* input)

View File

@ -17,7 +17,8 @@ UInventoryInteractableModificator::UInventoryInteractableModificator(const FObje
{
activatorTypes |= static_cast<uint8>(EActivatorType::Use);
inputMapping = { FSoftObjectPath{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/Interactables/IMC_InteractableInventory.IMC_InteractableInventory'") } };
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/Interactables/IMC_InteractableInventory.IMC_InteractableInventory'") };
inputMapping = asset.Object;
}
void UInventoryInteractableModificator::Bind_Implementation(UEnhancedInputComponent* input)

View File

@ -17,7 +17,8 @@ UMoveInteractableModificator::UMoveInteractableModificator(const FObjectInitiali
{
activatorTypes |= static_cast<uint8>(EActivatorType::Use);
inputMapping = { FSoftObjectPath{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/Interactables/IMC_InteractableMove.IMC_InteractableMove'") } };
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/Interactables/IMC_InteractableMove.IMC_InteractableMove'") };
inputMapping = asset.Object;
actor = Cast<AInteractable>(GetOwner());

View File

@ -18,7 +18,8 @@
ASubwaySurfManager::ASubwaySurfManager()
: AMinigame()
{
input = { FSoftObjectPath{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/Minigame/IMC_SubwaySurf.IMC_SubwaySurf'") } };
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/Minigame/IMC_SubwaySurf.IMC_SubwaySurf'") };
input = asset.Object;
auto root = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));