2021-06-15 16:38:03 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "StorageServerPlatformFile.h"
# include "StorageServerIoDispatcherBackend.h"
# include "HAL/IPlatformFileModule.h"
2023-04-21 08:56:22 -04:00
# include "HAL/FileManagerGeneric.h"
2021-07-14 10:40:16 -04:00
# include "Misc/CommandLine.h"
2021-06-15 16:38:03 -04:00
# include "Misc/Paths.h"
# include "Misc/ScopeRWLock.h"
# include "StorageServerConnection.h"
# include "Modules/ModuleManager.h"
# include "Misc/StringBuilder.h"
# include "Algo/Replace.h"
2021-09-28 04:00:33 -04:00
# include "StorageServerPackageStore.h"
# include "CookOnTheFlyPackageStore.h"
2021-09-28 05:00:12 -04:00
# include "Misc/CoreDelegates.h"
2021-06-15 16:38:03 -04:00
# include "Modules/ModuleManager.h"
# include "CookOnTheFly.h"
2023-04-21 08:56:22 -04:00
# include "Serialization/CompactBinarySerialization.h"
2021-06-15 16:38:03 -04:00
DEFINE_LOG_CATEGORY_STATIC ( LogStorageServerPlatformFile , Log , All ) ;
# if !UE_BUILD_SHIPPING
FStorageServerFileSystemTOC : : ~ FStorageServerFileSystemTOC ( )
{
FWriteScopeLock _ ( TocLock ) ;
for ( auto & KV : Directories )
{
delete KV . Value ;
}
}
FStorageServerFileSystemTOC : : FDirectory * FStorageServerFileSystemTOC : : AddDirectoriesRecursive ( const FString & DirectoryPath )
{
FDirectory * Directory = new FDirectory ( ) ;
Directories . Add ( DirectoryPath , Directory ) ;
FString ParentDirectoryPath = FPaths : : GetPath ( DirectoryPath ) ;
FDirectory * ParentDirectory ;
if ( ParentDirectoryPath . IsEmpty ( ) )
{
ParentDirectory = & Root ;
}
else
{
ParentDirectory = Directories . FindRef ( ParentDirectoryPath ) ;
if ( ! ParentDirectory )
{
ParentDirectory = AddDirectoriesRecursive ( ParentDirectoryPath ) ;
}
}
ParentDirectory - > Directories . Add ( DirectoryPath ) ;
return Directory ;
}
2021-08-19 06:42:05 -04:00
void FStorageServerFileSystemTOC : : AddFile ( const FIoChunkId & FileChunkId , FStringView PathView )
2021-06-15 16:38:03 -04:00
{
FWriteScopeLock _ ( TocLock ) ;
2021-08-19 06:42:05 -04:00
const int32 FileIndex = Files . Num ( ) ;
FFile & NewFile = Files . AddDefaulted_GetRef ( ) ;
NewFile . FileChunkId = FileChunkId ;
NewFile . FilePath = PathView ;
FilePathToIndexMap . Add ( NewFile . FilePath , FileIndex ) ;
FString DirectoryPath = FPaths : : GetPath ( NewFile . FilePath ) ;
2021-06-15 16:38:03 -04:00
FDirectory * Directory = Directories . FindRef ( DirectoryPath ) ;
if ( ! Directory )
{
Directory = AddDirectoriesRecursive ( DirectoryPath ) ;
}
2021-08-19 06:42:05 -04:00
Directory - > Files . Add ( FileIndex ) ;
2021-06-15 16:38:03 -04:00
}
bool FStorageServerFileSystemTOC : : FileExists ( const FString & Path )
{
FReadScopeLock _ ( TocLock ) ;
return FilePathToIndexMap . Contains ( Path ) ;
}
bool FStorageServerFileSystemTOC : : DirectoryExists ( const FString & Path )
{
FReadScopeLock _ ( TocLock ) ;
return Directories . Contains ( Path ) ;
}
2021-08-19 06:42:05 -04:00
const FIoChunkId * FStorageServerFileSystemTOC : : GetFileChunkId ( const FString & Path )
2021-06-15 16:38:03 -04:00
{
FReadScopeLock _ ( TocLock ) ;
2021-08-19 06:42:05 -04:00
if ( const int32 * FileIndex = FilePathToIndexMap . Find ( Path ) )
{
return & Files [ * FileIndex ] . FileChunkId ;
}
return nullptr ;
2021-06-15 16:38:03 -04:00
}
2021-08-19 06:42:05 -04:00
bool FStorageServerFileSystemTOC : : IterateDirectory ( const FString & Path , TFunctionRef < bool ( const FIoChunkId & , const TCHAR * ) > Callback )
2021-06-15 16:38:03 -04:00
{
UE_LOG ( LogStorageServerPlatformFile , Verbose , TEXT ( " IterateDirectory '%s' " ) , * Path ) ;
FReadScopeLock _ ( TocLock ) ;
FDirectory * Directory = Directories . FindRef ( Path ) ;
if ( ! Directory )
{
return false ;
}
for ( int32 FileIndex : Directory - > Files )
{
2021-08-19 06:42:05 -04:00
const FFile & File = Files [ FileIndex ] ;
if ( ! Callback ( File . FileChunkId , * File . FilePath ) )
2021-06-15 16:38:03 -04:00
{
return false ;
}
}
for ( const FString & ChildDirectoryPath : Directory - > Directories )
{
2021-08-19 06:42:05 -04:00
if ( ! Callback ( FIoChunkId ( ) , * ChildDirectoryPath ) )
2021-06-15 16:38:03 -04:00
{
return false ;
}
}
return true ;
}
class FStorageServerFileHandle
: public IFileHandle
{
enum
{
BufferSize = 64 < < 10
} ;
FStorageServerPlatformFile & Owner ;
2021-08-19 06:42:05 -04:00
FIoChunkId FileChunkId ;
2021-06-15 16:38:03 -04:00
FString Filename ;
int64 FilePos = 0 ;
int64 FileSize = - 1 ;
int64 BufferStart = - 1 ;
int64 BufferEnd = - 1 ;
uint8 Buffer [ BufferSize ] ;
public :
2021-08-19 06:42:05 -04:00
FStorageServerFileHandle ( FStorageServerPlatformFile & InOwner , FIoChunkId InFileChunkId , const TCHAR * InFilename )
2021-06-15 16:38:03 -04:00
: Owner ( InOwner )
2021-08-19 06:42:05 -04:00
, FileChunkId ( InFileChunkId )
2021-06-15 16:38:03 -04:00
, Filename ( InFilename )
{
}
~ FStorageServerFileHandle ( )
{
}
virtual int64 Size ( ) override
{
if ( FileSize < 0 )
{
2021-08-19 06:42:05 -04:00
const FFileStatData FileStatData = Owner . SendGetStatDataMessage ( FileChunkId ) ;
2021-06-15 16:38:03 -04:00
if ( FileStatData . bIsValid )
{
FileSize = FileStatData . FileSize ;
}
else
{
UE_LOG ( LogStorageServerPlatformFile , Warning , TEXT ( " Failed to obtain size of file '%s' " ) , * Filename ) ;
FileSize = 0 ;
}
}
return FileSize ;
}
virtual int64 Tell ( ) override
{
return FilePos ;
}
virtual bool Seek ( int64 NewPosition ) override
{
FilePos = NewPosition ;
return true ;
}
virtual bool SeekFromEnd ( int64 NewPositionRelativeToEnd = 0 ) override
{
return Seek ( Size ( ) + NewPositionRelativeToEnd ) ;
}
virtual bool Read ( uint8 * Destination , int64 BytesToRead ) override
{
if ( BytesToRead = = 0 )
{
return true ;
}
if ( BytesToRead > BufferSize )
{
2021-08-19 06:42:05 -04:00
const int64 BytesRead = Owner . SendReadMessage ( Destination , FileChunkId , FilePos , BytesToRead ) ;
2021-06-15 16:38:03 -04:00
if ( BytesRead = = BytesToRead )
{
FilePos + = BytesRead ;
return true ;
}
}
if ( FilePos < BufferStart | | BufferEnd < FilePos + BytesToRead )
{
2021-08-19 06:42:05 -04:00
const int64 BytesRead = Owner . SendReadMessage ( Buffer , FileChunkId , FilePos , BufferSize ) ;
2021-06-15 16:38:03 -04:00
BufferStart = FilePos ;
BufferEnd = BufferStart + BytesRead ;
}
int64 BufferOffset = FilePos - BufferStart ;
check ( BufferEnd > BufferOffset ) ;
int64 BytesToReadFromBuffer = FMath : : Min ( BufferEnd - BufferOffset , BytesToRead ) ;
FMemory : : Memcpy ( Destination , Buffer + BufferOffset , BytesToReadFromBuffer ) ;
if ( BytesToReadFromBuffer = = BytesToRead )
{
FilePos + = BytesToReadFromBuffer ;
return true ;
}
return false ;
}
virtual bool Write ( const uint8 * Source , int64 BytesToWrite ) override
{
check ( false ) ;
return false ;
}
virtual bool Flush ( const bool bFullFlush = false ) override
{
return false ;
}
virtual bool Truncate ( int64 NewSize ) override
{
return false ;
}
} ;
FStorageServerPlatformFile : : FStorageServerPlatformFile ( )
{
}
FStorageServerPlatformFile : : ~ FStorageServerPlatformFile ( )
{
}
2023-04-21 08:56:22 -04:00
TUniquePtr < FArchive > FStorageServerPlatformFile : : TryFindProjectStoreMarkerFile ( IPlatformFile * Inner ) const
{
if ( Inner = = nullptr )
{
return nullptr ;
}
2023-08-15 14:44:15 -04:00
FString RelativeStagedPath = TEXT ( " ../../../ " ) ;
FString RootPath = FPaths : : RootDir ( ) ;
FString PlatformName = FPlatformProperties : : PlatformName ( ) ;
FString CookedOutputPath = FPaths : : Combine ( FPaths : : ProjectDir ( ) , TEXT ( " Saved " ) , TEXT ( " Cooked " ) , PlatformName ) ;
TArray < FString > PotentialProjectStorePaths ;
PotentialProjectStorePaths . Add ( RelativeStagedPath ) ;
PotentialProjectStorePaths . Add ( CookedOutputPath ) ;
PotentialProjectStorePaths . Add ( RootPath ) ;
for ( const FString & ProjectStorePath : PotentialProjectStorePaths )
2023-04-21 08:56:22 -04:00
{
2023-08-15 14:44:15 -04:00
FString ProjectMarkerPath = ProjectStorePath / TEXT ( " .projectstore " ) ;
if ( IFileHandle * ProjectStoreMarkerHandle = Inner - > OpenRead ( * ProjectMarkerPath ) ; ProjectStoreMarkerHandle ! = nullptr )
{
UE_LOG ( LogStorageServerPlatformFile , Display , TEXT ( " Found '%s' " ) , * ProjectMarkerPath ) ;
return TUniquePtr < FArchive > ( new FArchiveFileReaderGeneric ( ProjectStoreMarkerHandle , * ProjectMarkerPath , ProjectStoreMarkerHandle - > Size ( ) ) ) ;
}
2023-04-21 08:56:22 -04:00
}
return nullptr ;
}
2021-06-15 16:38:03 -04:00
bool FStorageServerPlatformFile : : ShouldBeUsed ( IPlatformFile * Inner , const TCHAR * CmdLine ) const
{
2023-04-03 01:51:40 -04:00
# if WITH_COTF
UE : : Cook : : ICookOnTheFlyModule & CookOnTheFlyModule = FModuleManager : : LoadModuleChecked < UE : : Cook : : ICookOnTheFlyModule > ( TEXT ( " CookOnTheFly " ) ) ;
TSharedPtr < UE : : Cook : : ICookOnTheFlyServerConnection > DefaultConnection = CookOnTheFlyModule . GetDefaultServerConnection ( ) ;
if ( DefaultConnection . IsValid ( ) & & ! DefaultConnection - > GetZenProjectName ( ) . IsEmpty ( ) )
{
2023-08-15 14:44:15 -04:00
HostAddrs . Append ( DefaultConnection - > GetZenHostNames ( ) ) ;
2023-04-03 01:51:40 -04:00
HostPort = DefaultConnection - > GetZenHostPort ( ) ;
return true ;
}
# endif
2023-04-21 08:56:22 -04:00
TUniquePtr < FArchive > ProjectStoreMarkerReader = TryFindProjectStoreMarkerFile ( Inner ) ;
if ( ProjectStoreMarkerReader ! = nullptr )
{
FCbObject ProjectStoreObject = LoadCompactBinary ( * ProjectStoreMarkerReader ) . AsObject ( ) ;
if ( FCbFieldView ZenServerField = ProjectStoreObject [ " zenserver " ] )
{
if ( FUtf8StringView HostName = ZenServerField [ " hostname " ] . AsString ( ) ; ! HostName . IsEmpty ( ) )
{
HostAddrs . Add ( FString ( HostName ) ) ;
}
2023-06-16 01:10:08 -04:00
FCbArrayView RemoteHostNames = ZenServerField [ " remotehostnames " ] . AsArrayView ( ) ;
for ( FCbFieldView RemoteHostName : RemoteHostNames )
{
if ( FUtf8StringView HostName = RemoteHostName . AsString ( ) ; ! HostName . IsEmpty ( ) )
{
HostAddrs . Add ( FString ( HostName ) ) ;
}
}
2023-04-21 08:56:22 -04:00
HostPort = ZenServerField [ " hostport " ] . AsUInt16 ( HostPort ) ;
2023-08-15 14:44:15 -04:00
UE_LOG ( LogStorageServerPlatformFile , Display , TEXT ( " Using connection settings from .projectstore: HostAddrs='%s' and HostPort='%d' " ) , * FString : : Join ( HostAddrs , TEXT ( " + " ) ) , HostPort ) ;
2023-04-21 08:56:22 -04:00
}
}
2021-07-14 10:40:16 -04:00
FString Host ;
if ( FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " -ZenStoreHost= " ) , Host ) )
{
2023-08-15 14:44:15 -04:00
UE_LOG ( LogStorageServerPlatformFile , Display , TEXT ( " Adding connection settings from command line: -ZenStoreHost='%s' " ) , * Host ) ;
2021-07-14 10:40:16 -04:00
if ( ! Host . ParseIntoArray ( HostAddrs , TEXT ( " + " ) , true ) )
{
HostAddrs . Add ( Host ) ;
}
}
2023-08-15 14:44:15 -04:00
if ( FParse : : Value ( CmdLine , TEXT ( " -ZenStorePort= " ) , HostPort ) )
{
UE_LOG ( LogStorageServerPlatformFile , Display , TEXT ( " Using connection settings from command line: -ZenStorePort='%d' " ) , HostPort ) ;
}
2021-07-14 10:40:16 -04:00
return HostAddrs . Num ( ) > 0 ;
2021-06-15 16:38:03 -04:00
}
bool FStorageServerPlatformFile : : Initialize ( IPlatformFile * Inner , const TCHAR * CmdLine )
{
LowerLevel = Inner ;
2021-07-14 10:40:16 -04:00
if ( HostAddrs . Num ( ) > 0 )
2021-06-15 16:38:03 -04:00
{
2021-12-10 18:06:39 -05:00
// Don't initialize the connection yet because we want to incorporate project file path information into the initialization.
2023-04-21 08:56:22 -04:00
TUniquePtr < FArchive > ProjectStoreMarkerReader = TryFindProjectStoreMarkerFile ( Inner ) ;
if ( ProjectStoreMarkerReader ! = nullptr )
{
FCbObject ProjectStoreObject = LoadCompactBinary ( * ProjectStoreMarkerReader ) . AsObject ( ) ;
if ( FCbFieldView ZenServerField = ProjectStoreObject [ " zenserver " ] )
{
ServerProject = FString ( ZenServerField [ " projectid " ] . AsString ( ) ) ;
ServerPlatform = FString ( ZenServerField [ " oplogid " ] . AsString ( ) ) ;
2023-08-15 14:44:15 -04:00
UE_LOG ( LogStorageServerPlatformFile , Display , TEXT ( " Using settings from .projectstore: ServerProject='%s' and ServerPlatform='%s' " ) , * ServerProject , * ServerPlatform ) ;
2023-04-21 08:56:22 -04:00
}
}
2023-08-15 14:44:15 -04:00
if ( FParse : : Value ( CmdLine , TEXT ( " -ZenStoreProject= " ) , ServerProject ) )
{
UE_LOG ( LogStorageServerPlatformFile , Display , TEXT ( " Using settings from command line: -ZenStoreProject='%s' " ) , * ServerProject ) ;
}
if ( FParse : : Value ( CmdLine , TEXT ( " -ZenStorePlatform= " ) , ServerPlatform ) )
{
UE_LOG ( LogStorageServerPlatformFile , Display , TEXT ( " Using settings from command line: -ZenStorePlatform='%s' " ) , * ServerPlatform ) ;
}
2021-12-10 18:06:39 -05:00
return true ;
}
return false ;
}
void FStorageServerPlatformFile : : InitializeAfterProjectFilePath ( )
{
2022-05-24 02:50:39 -04:00
# if WITH_COTF
UE : : Cook : : ICookOnTheFlyModule & CookOnTheFlyModule = FModuleManager : : LoadModuleChecked < UE : : Cook : : ICookOnTheFlyModule > ( TEXT ( " CookOnTheFly " ) ) ;
CookOnTheFlyServerConnection = CookOnTheFlyModule . GetDefaultServerConnection ( ) ;
if ( CookOnTheFlyServerConnection )
{
CookOnTheFlyServerConnection - > OnMessage ( ) . AddRaw ( this , & FStorageServerPlatformFile : : OnCookOnTheFlyMessage ) ;
ServerProject = CookOnTheFlyServerConnection - > GetZenProjectName ( ) ;
ServerPlatform = CookOnTheFlyServerConnection - > GetPlatformName ( ) ;
}
# endif
2021-12-10 18:06:39 -05:00
Connection . Reset ( new FStorageServerConnection ( ) ) ;
const TCHAR * ProjectOverride = ServerProject . IsEmpty ( ) ? nullptr : * ServerProject ;
const TCHAR * PlatformOverride = ServerPlatform . IsEmpty ( ) ? nullptr : * ServerPlatform ;
2023-04-03 01:51:40 -04:00
if ( Connection - > Initialize ( HostAddrs , HostPort , ProjectOverride , PlatformOverride ) )
2021-12-10 18:06:39 -05:00
{
if ( SendGetFileListMessage ( ) )
2021-06-15 16:38:03 -04:00
{
2021-12-10 18:06:39 -05:00
FIoDispatcher & IoDispatcher = FIoDispatcher : : Get ( ) ;
TSharedRef < FStorageServerIoDispatcherBackend > IoDispatcherBackend = MakeShared < FStorageServerIoDispatcherBackend > ( * Connection . Get ( ) ) ;
IoDispatcher . Mount ( IoDispatcherBackend ) ;
2021-09-28 04:00:33 -04:00
# if WITH_COTF
2022-06-01 02:12:33 -04:00
if ( CookOnTheFlyServerConnection )
{
FPackageStore : : Get ( ) . Mount ( MakeShared < FCookOnTheFlyPackageStoreBackend > ( * CookOnTheFlyServerConnection . Get ( ) ) ) ;
}
else
2021-09-28 04:00:33 -04:00
# endif
2022-06-01 02:12:33 -04:00
{
FPackageStore : : Get ( ) . Mount ( MakeShared < FStorageServerPackageStoreBackend > ( * Connection . Get ( ) ) ) ;
}
2021-06-15 16:38:03 -04:00
}
else
{
2021-12-10 18:06:39 -05:00
UE_LOG ( LogStorageServerPlatformFile , Fatal , TEXT ( " Failed to get file list from Zen at '%s' " ) , * Connection - > GetHostAddr ( ) ) ;
2021-06-15 16:38:03 -04:00
}
}
2021-12-10 18:06:39 -05:00
else
2021-06-15 16:38:03 -04:00
{
2021-12-10 18:06:39 -05:00
UE_LOG ( LogStorageServerPlatformFile , Fatal , TEXT ( " Failed to initialize connection " ) ) ;
2021-06-15 16:38:03 -04:00
}
}
bool FStorageServerPlatformFile : : FileExists ( const TCHAR * Filename )
{
TStringBuilder < 1024 > StorageServerFilename ;
if ( MakeStorageServerPath ( Filename , StorageServerFilename ) & & ServerToc . FileExists ( * StorageServerFilename ) )
{
return true ;
}
return LowerLevel - > FileExists ( Filename ) ;
}
FDateTime FStorageServerPlatformFile : : GetTimeStamp ( const TCHAR * Filename )
{
TStringBuilder < 1024 > StorageServerFilename ;
if ( MakeStorageServerPath ( Filename , StorageServerFilename ) )
{
2021-08-19 06:42:05 -04:00
if ( const FIoChunkId * FileChunkId = ServerToc . GetFileChunkId ( * StorageServerFilename ) )
2021-06-15 16:38:03 -04:00
{
2021-08-19 06:42:05 -04:00
const FFileStatData FileStatData = SendGetStatDataMessage ( * FileChunkId ) ;
2021-06-15 16:38:03 -04:00
check ( FileStatData . bIsValid ) ;
return FileStatData . ModificationTime ;
}
}
return LowerLevel - > GetTimeStamp ( Filename ) ;
}
FDateTime FStorageServerPlatformFile : : GetAccessTimeStamp ( const TCHAR * Filename )
{
TStringBuilder < 1024 > StorageServerFilename ;
if ( MakeStorageServerPath ( Filename , StorageServerFilename ) )
{
2021-08-19 06:42:05 -04:00
if ( const FIoChunkId * FileChunkId = ServerToc . GetFileChunkId ( * StorageServerFilename ) )
2021-06-15 16:38:03 -04:00
{
2021-08-19 06:42:05 -04:00
const FFileStatData FileStatData = SendGetStatDataMessage ( * FileChunkId ) ;
2021-06-15 16:38:03 -04:00
check ( FileStatData . bIsValid ) ;
return FileStatData . AccessTime ;
}
}
return LowerLevel - > GetAccessTimeStamp ( Filename ) ;
}
int64 FStorageServerPlatformFile : : FileSize ( const TCHAR * Filename )
{
TStringBuilder < 1024 > StorageServerFilename ;
if ( MakeStorageServerPath ( Filename , StorageServerFilename ) )
{
2021-08-19 06:42:05 -04:00
if ( const FIoChunkId * FileChunkId = ServerToc . GetFileChunkId ( * StorageServerFilename ) )
2021-06-15 16:38:03 -04:00
{
2021-08-19 06:42:05 -04:00
const FFileStatData FileStatData = SendGetStatDataMessage ( * FileChunkId ) ;
2021-06-15 16:38:03 -04:00
check ( FileStatData . bIsValid ) ;
return FileStatData . FileSize ;
}
}
return LowerLevel - > FileSize ( Filename ) ;
}
bool FStorageServerPlatformFile : : IsReadOnly ( const TCHAR * Filename )
{
TStringBuilder < 1024 > StorageServerFilename ;
if ( MakeStorageServerPath ( Filename , StorageServerFilename ) & & ServerToc . FileExists ( * StorageServerFilename ) )
{
return true ;
}
return LowerLevel - > IsReadOnly ( Filename ) ;
}
FFileStatData FStorageServerPlatformFile : : GetStatData ( const TCHAR * FilenameOrDirectory )
{
TStringBuilder < 1024 > StorageServerFilenameOrDirectory ;
if ( MakeStorageServerPath ( FilenameOrDirectory , StorageServerFilenameOrDirectory ) )
{
2021-08-19 06:42:05 -04:00
if ( const FIoChunkId * FileChunkId = ServerToc . GetFileChunkId ( * StorageServerFilenameOrDirectory ) )
2021-06-15 16:38:03 -04:00
{
2021-08-19 06:42:05 -04:00
return SendGetStatDataMessage ( * FileChunkId ) ;
2021-06-15 16:38:03 -04:00
}
else if ( ServerToc . DirectoryExists ( * StorageServerFilenameOrDirectory ) )
{
return FFileStatData (
FDateTime : : MinValue ( ) ,
FDateTime : : MinValue ( ) ,
FDateTime : : MinValue ( ) ,
0 ,
true ,
true ) ;
}
}
return LowerLevel - > GetStatData ( FilenameOrDirectory ) ;
}
2021-08-19 06:42:05 -04:00
IFileHandle * FStorageServerPlatformFile : : InternalOpenFile ( const FIoChunkId & FileChunkId , const TCHAR * LocalFilename )
2021-06-15 16:38:03 -04:00
{
2021-08-19 06:42:05 -04:00
return new FStorageServerFileHandle ( * this , FileChunkId , LocalFilename ) ;
2021-06-15 16:38:03 -04:00
}
IFileHandle * FStorageServerPlatformFile : : OpenRead ( const TCHAR * Filename , bool bAllowWrite )
{
TStringBuilder < 1024 > StorageServerFilename ;
if ( MakeStorageServerPath ( Filename , StorageServerFilename ) )
{
2021-08-19 06:42:05 -04:00
if ( const FIoChunkId * FileChunkId = ServerToc . GetFileChunkId ( * StorageServerFilename ) )
2021-06-15 16:38:03 -04:00
{
2021-08-19 06:42:05 -04:00
return InternalOpenFile ( * FileChunkId , Filename ) ;
2021-06-15 16:38:03 -04:00
}
}
return LowerLevel - > OpenRead ( Filename , bAllowWrite ) ;
}
bool FStorageServerPlatformFile : : IterateDirectory ( const TCHAR * Directory , IPlatformFile : : FDirectoryVisitor & Visitor )
{
TStringBuilder < 1024 > StorageServerDirectory ;
bool bResult = false ;
2021-06-22 09:34:33 -04:00
if ( MakeStorageServerPath ( Directory , StorageServerDirectory ) & & ServerToc . DirectoryExists ( * StorageServerDirectory ) )
2021-06-15 16:38:03 -04:00
{
2021-08-19 06:42:05 -04:00
bResult | = ServerToc . IterateDirectory ( * StorageServerDirectory , [ this , & Visitor ] ( const FIoChunkId & FileChunkId , const TCHAR * FilenameOrDirectory )
2021-06-15 16:38:03 -04:00
{
TStringBuilder < 1024 > LocalPath ;
bool bConverted = MakeLocalPath ( FilenameOrDirectory , LocalPath ) ;
check ( bConverted ) ;
2021-08-19 06:42:05 -04:00
const bool bDirectory = ! FileChunkId . IsValid ( ) ;
return Visitor . Visit ( * LocalPath , bDirectory ) ;
2021-06-15 16:38:03 -04:00
} ) ;
}
2021-06-22 09:34:33 -04:00
else
{
bResult | = LowerLevel - > IterateDirectory ( Directory , Visitor ) ;
}
2021-06-15 16:38:03 -04:00
return bResult ;
}
bool FStorageServerPlatformFile : : IterateDirectoryStat ( const TCHAR * Directory , FDirectoryStatVisitor & Visitor )
{
TStringBuilder < 1024 > StorageServerDirectory ;
bool bResult = false ;
2021-06-22 09:34:33 -04:00
if ( MakeStorageServerPath ( Directory , StorageServerDirectory ) & & ServerToc . DirectoryExists ( * StorageServerDirectory ) )
2021-06-15 16:38:03 -04:00
{
2021-08-19 06:42:05 -04:00
bResult | = ServerToc . IterateDirectory ( * StorageServerDirectory , [ this , & Visitor ] ( const FIoChunkId & FileChunkId , const TCHAR * ServerFilenameOrDirectory )
2021-06-15 16:38:03 -04:00
{
TStringBuilder < 1024 > LocalPath ;
bool bConverted = MakeLocalPath ( ServerFilenameOrDirectory , LocalPath ) ;
check ( bConverted ) ;
FFileStatData FileStatData ;
2021-08-19 06:42:05 -04:00
if ( FileChunkId . IsValid ( ) )
2021-06-15 16:38:03 -04:00
{
2021-08-19 06:42:05 -04:00
FileStatData = SendGetStatDataMessage ( FileChunkId ) ;
2021-06-15 16:38:03 -04:00
check ( FileStatData . bIsValid ) ;
}
else
{
FileStatData = FFileStatData (
FDateTime : : MinValue ( ) ,
FDateTime : : MinValue ( ) ,
FDateTime : : MinValue ( ) ,
0 ,
true ,
true ) ;
}
return Visitor . Visit ( * LocalPath , FileStatData ) ;
} ) ;
}
2021-06-22 09:34:33 -04:00
else
{
bResult | = LowerLevel - > IterateDirectoryStat ( Directory , Visitor ) ;
}
2021-06-15 16:38:03 -04:00
return bResult ;
}
bool FStorageServerPlatformFile : : DirectoryExists ( const TCHAR * Directory )
{
TStringBuilder < 1024 > StorageServerDirectory ;
if ( MakeStorageServerPath ( Directory , StorageServerDirectory ) & & ServerToc . DirectoryExists ( * StorageServerDirectory ) )
{
return true ;
}
return LowerLevel - > DirectoryExists ( Directory ) ;
}
FString FStorageServerPlatformFile : : GetFilenameOnDisk ( const TCHAR * Filename )
{
TStringBuilder < 1024 > StorageServerFilename ;
if ( MakeStorageServerPath ( Filename , StorageServerFilename ) & & ServerToc . FileExists ( * StorageServerFilename ) )
{
UE_LOG ( LogStorageServerPlatformFile , Warning , TEXT ( " Attempting to get disk filename of remote file '%s' " ) , Filename ) ;
return Filename ;
}
return LowerLevel - > GetFilenameOnDisk ( Filename ) ;
}
bool FStorageServerPlatformFile : : DeleteFile ( const TCHAR * Filename )
{
TStringBuilder < 1024 > StorageServerFilename ;
if ( MakeStorageServerPath ( Filename , StorageServerFilename ) & & ServerToc . FileExists ( * StorageServerFilename ) )
{
return false ;
}
return LowerLevel - > DeleteFile ( Filename ) ;
}
bool FStorageServerPlatformFile : : MoveFile ( const TCHAR * To , const TCHAR * From )
{
TStringBuilder < 1024 > StorageServerTo ;
if ( MakeStorageServerPath ( To , StorageServerTo ) & & ServerToc . FileExists ( * StorageServerTo ) )
{
return false ;
}
TStringBuilder < 1024 > StorageServerFrom ;
if ( MakeStorageServerPath ( From , StorageServerFrom ) )
{
2021-08-19 06:42:05 -04:00
if ( const FIoChunkId * FromFileChunkId = ServerToc . GetFileChunkId ( * StorageServerFrom ) )
2021-06-15 16:38:03 -04:00
{
TUniquePtr < IFileHandle > ToFile ( LowerLevel - > OpenWrite ( To , false , false ) ) ;
if ( ! ToFile )
{
return false ;
}
2021-08-19 06:42:05 -04:00
TUniquePtr < IFileHandle > FromFile ( InternalOpenFile ( * FromFileChunkId , * StorageServerFrom ) ) ;
2021-06-15 16:38:03 -04:00
if ( ! FromFile )
{
return false ;
}
const int64 BufferSize = 64 < < 10 ;
TArray < uint8 > Buffer ;
Buffer . SetNum ( BufferSize ) ;
int64 BytesLeft = FromFile - > Size ( ) ;
while ( BytesLeft )
{
int64 BytesToWrite = FMath : : Min ( BufferSize , BytesLeft ) ;
if ( ! FromFile - > Read ( Buffer . GetData ( ) , BytesToWrite ) )
{
return false ;
}
if ( ! ToFile - > Write ( Buffer . GetData ( ) , BytesToWrite ) )
{
return false ;
}
BytesLeft - = BytesToWrite ;
}
return true ;
}
}
return LowerLevel - > MoveFile ( To , From ) ;
}
bool FStorageServerPlatformFile : : SetReadOnly ( const TCHAR * Filename , bool bNewReadOnlyValue )
{
TStringBuilder < 1024 > StorageServerFilename ;
if ( MakeStorageServerPath ( Filename , StorageServerFilename ) & & ServerToc . FileExists ( * StorageServerFilename ) )
{
return bNewReadOnlyValue ;
}
return LowerLevel - > SetReadOnly ( Filename , bNewReadOnlyValue ) ;
}
void FStorageServerPlatformFile : : SetTimeStamp ( const TCHAR * Filename , FDateTime DateTime )
{
TStringBuilder < 1024 > StorageServerFilename ;
if ( MakeStorageServerPath ( Filename , StorageServerFilename ) & & ServerToc . FileExists ( * StorageServerFilename ) )
{
return ;
}
LowerLevel - > SetTimeStamp ( Filename , DateTime ) ;
}
IFileHandle * FStorageServerPlatformFile : : OpenWrite ( const TCHAR * Filename , bool bAppend , bool bAllowRead )
{
TStringBuilder < 1024 > StorageServerFilename ;
if ( MakeStorageServerPath ( Filename , StorageServerFilename ) & & ServerToc . FileExists ( * StorageServerFilename ) )
{
return nullptr ;
}
return LowerLevel - > OpenWrite ( Filename , bAppend , bAllowRead ) ;
}
bool FStorageServerPlatformFile : : CreateDirectory ( const TCHAR * Directory )
{
TStringBuilder < 1024 > StorageServerDirectory ;
if ( MakeStorageServerPath ( Directory , StorageServerDirectory ) & & ServerToc . DirectoryExists ( * StorageServerDirectory ) )
{
return true ;
}
return LowerLevel - > CreateDirectory ( Directory ) ;
}
bool FStorageServerPlatformFile : : DeleteDirectory ( const TCHAR * Directory )
{
TStringBuilder < 1024 > StorageServerDirectory ;
if ( MakeStorageServerPath ( Directory , StorageServerDirectory ) & & ServerToc . DirectoryExists ( * StorageServerDirectory ) )
{
return false ;
}
return LowerLevel - > DeleteDirectory ( Directory ) ;
}
2022-02-03 07:25:17 -05:00
FString FStorageServerPlatformFile : : ConvertToAbsolutePathForExternalAppForRead ( const TCHAR * Filename )
{
# if PLATFORM_DESKTOP && (UE_GAME || UE_SERVER)
TStringBuilder < 1024 > Result ;
// New code should not end up in here and should instead be written in such a
// way that data can be served from a (remote) server.
// Some data must exist in files on disk such that it can be accessed by external
// APIs. Any such data required by a title should have been written to Saved/Cooked
// at cook time. If a file prefix with UE's canonical ../../../ is requested we
// look inside Saved/Cooked. A read-only filesystem overlay if you will.
static FString * CookedDir = nullptr ;
if ( CookedDir = = nullptr )
{
static FString Inner ;
CookedDir = & Inner ;
Result < < * FPaths : : ProjectDir ( ) ;
Result < < TEXT ( " Saved/Cooked/ " ) ;
Result < < FPlatformProperties : : PlatformName ( ) ;
Result < < TEXT ( " / " ) ;
Inner = Result . ToString ( ) ;
}
else
{
Result < < * ( * CookedDir ) ;
}
const TCHAR * DotSlashSkip = Filename ;
for ( ; * DotSlashSkip = = ' . ' | | * DotSlashSkip = = ' / ' ; + + DotSlashSkip ) ;
if ( PTRINT ( DotSlashSkip - Filename ) = = 9 ) // 9 == ../../../
{
Result < < DotSlashSkip ;
if ( LowerLevel - > FileExists ( Result . ToString ( ) ) )
{
return FString ( Result . GetData ( ) , Result . Len ( ) ) ;
}
}
# endif
return LowerLevel - > ConvertToAbsolutePathForExternalAppForRead ( Filename ) ;
}
2021-06-15 16:38:03 -04:00
bool FStorageServerPlatformFile : : MakeStorageServerPath ( const TCHAR * LocalFilenameOrDirectory , FStringBuilderBase & OutPath ) const
{
FStringView LocalEngineDirView ( FPlatformMisc : : EngineDir ( ) ) ;
FStringView LocalProjectDirView ( FPlatformMisc : : ProjectDir ( ) ) ;
FStringView LocalFilenameOrDirectoryView ( LocalFilenameOrDirectory ) ;
bool bValid = false ;
if ( LocalFilenameOrDirectoryView . StartsWith ( LocalEngineDirView , ESearchCase : : IgnoreCase ) )
{
OutPath . Append ( ServerEngineDirView ) ;
OutPath . Append ( LocalFilenameOrDirectoryView . RightChop ( LocalEngineDirView . Len ( ) ) ) ;
bValid = true ;
}
else if ( LocalFilenameOrDirectoryView . StartsWith ( LocalProjectDirView , ESearchCase : : IgnoreCase ) )
{
OutPath . Append ( ServerProjectDirView ) ;
OutPath . Append ( LocalFilenameOrDirectoryView . RightChop ( LocalProjectDirView . Len ( ) ) ) ;
bValid = true ;
}
if ( bValid )
{
Algo : : Replace ( MakeArrayView ( OutPath ) , ' \\ ' , ' / ' ) ;
OutPath . RemoveSuffix ( LocalFilenameOrDirectoryView . EndsWith ( ' / ' ) ? 1 : 0 ) ;
}
return bValid ;
}
bool FStorageServerPlatformFile : : MakeLocalPath ( const TCHAR * ServerFilenameOrDirectory , FStringBuilderBase & OutPath ) const
{
FStringView ServerFilenameOrDirectoryView ( ServerFilenameOrDirectory ) ;
if ( ServerFilenameOrDirectoryView . StartsWith ( ServerEngineDirView , ESearchCase : : IgnoreCase ) )
{
OutPath . Append ( FPlatformMisc : : EngineDir ( ) ) ;
OutPath . Append ( ServerFilenameOrDirectoryView . RightChop ( ServerEngineDirView . Len ( ) ) ) ;
return true ;
}
else if ( ServerFilenameOrDirectoryView . StartsWith ( ServerProjectDirView , ESearchCase : : IgnoreCase ) )
{
OutPath . Append ( FPlatformMisc : : ProjectDir ( ) ) ;
OutPath . Append ( ServerFilenameOrDirectoryView . RightChop ( ServerProjectDirView . Len ( ) ) ) ;
return true ;
}
return false ;
}
bool FStorageServerPlatformFile : : SendGetFileListMessage ( )
{
TRACE_CPUPROFILER_EVENT_SCOPE ( StorageServerPlatformFileGetFileList ) ;
Connection - > FileManifestRequest ( [ & ] ( FIoChunkId Id , FStringView Path )
{
2021-08-19 06:42:05 -04:00
ServerToc . AddFile ( Id , Path ) ;
2021-06-15 16:38:03 -04:00
} ) ;
return true ;
}
2021-08-19 06:42:05 -04:00
FFileStatData FStorageServerPlatformFile : : SendGetStatDataMessage ( const FIoChunkId & FileChunkId )
2021-06-15 16:38:03 -04:00
{
TRACE_CPUPROFILER_EVENT_SCOPE ( StorageServerPlatformFileGetStatData ) ;
2021-08-19 06:42:05 -04:00
const int64 FileSize = Connection - > ChunkSizeRequest ( FileChunkId ) ;
2021-06-15 16:38:03 -04:00
if ( FileSize < 0 )
{
return FFileStatData ( ) ;
}
FDateTime CreationTime = FDateTime : : Now ( ) ;
FDateTime AccessTime = FDateTime : : Now ( ) ;
FDateTime ModificationTime = FDateTime : : Now ( ) ;
return FFileStatData ( CreationTime , AccessTime , ModificationTime , FileSize , false , true ) ;
}
2021-08-19 06:42:05 -04:00
int64 FStorageServerPlatformFile : : SendReadMessage ( uint8 * Destination , const FIoChunkId & FileChunkId , int64 Offset , int64 BytesToRead )
2021-06-15 16:38:03 -04:00
{
TRACE_CPUPROFILER_EVENT_SCOPE ( StorageServerPlatformFileRead ) ;
int64 BytesRead = 0 ;
2022-01-14 05:00:42 -05:00
Connection - > ReadChunkRequest ( FileChunkId , Offset , BytesToRead , [ Destination , Offset , BytesToRead , & BytesRead ] ( FStorageServerResponse & Response )
2021-06-15 16:38:03 -04:00
{
2022-01-14 05:00:42 -05:00
BytesRead = Response . SerializeChunkTo ( MakeMemoryView ( Destination , BytesToRead ) , Offset ) ;
2021-06-15 16:38:03 -04:00
} ) ;
return BytesRead ;
}
2022-01-28 08:30:42 -05:00
bool FStorageServerPlatformFile : : SendMessageToServer ( const TCHAR * Message , IPlatformFile : : IFileServerMessageHandler * Handler )
{
# if WITH_COTF
2022-04-06 02:31:41 -04:00
if ( ! CookOnTheFlyServerConnection - > IsConnected ( ) )
{
return false ;
}
2022-01-28 08:30:42 -05:00
if ( FCString : : Stricmp ( Message , TEXT ( " RecompileShaders " ) ) = = 0 )
{
UE : : Cook : : FCookOnTheFlyRequest Request ( UE : : Cook : : ECookOnTheFlyMessage : : RecompileShaders ) ;
{
TUniquePtr < FArchive > Ar = Request . WriteBody ( ) ;
Handler - > FillPayload ( * Ar ) ;
}
UE : : Cook : : FCookOnTheFlyResponse Response = CookOnTheFlyServerConnection - > SendRequest ( Request ) . Get ( ) ;
if ( Response . IsOk ( ) )
{
TUniquePtr < FArchive > Ar = Response . ReadBody ( ) ;
Handler - > ProcessResponse ( * Ar ) ;
}
return Response . IsOk ( ) ;
}
# endif
return false ;
}
2021-06-15 16:38:03 -04:00
# if WITH_COTF
void FStorageServerPlatformFile : : OnCookOnTheFlyMessage ( const UE : : Cook : : FCookOnTheFlyMessage & Message )
{
switch ( Message . GetHeader ( ) . MessageType )
{
case UE : : Cook : : ECookOnTheFlyMessage : : FilesAdded :
{
UE_LOG ( LogCookOnTheFly , Verbose , TEXT ( " Received '%s' message " ) , LexToString ( Message . GetHeader ( ) . MessageType ) ) ;
TArray < FString > Filenames ;
TArray < FIoChunkId > ChunkIds ;
{
TUniquePtr < FArchive > Ar = Message . ReadBody ( ) ;
* Ar < < Filenames ;
* Ar < < ChunkIds ;
}
check ( Filenames . Num ( ) = = ChunkIds . Num ( ) ) ;
for ( int32 Idx = 0 , Num = Filenames . Num ( ) ; Idx < Num ; + + Idx )
{
UE_LOG ( LogCookOnTheFly , Verbose , TEXT ( " Adding file '%s' " ) , * Filenames [ Idx ] ) ;
2021-08-19 06:42:05 -04:00
ServerToc . AddFile ( ChunkIds [ Idx ] , Filenames [ Idx ] ) ;
2021-06-15 16:38:03 -04:00
}
break ;
}
}
}
# endif
class FStorageServerClientFileModule
: public IPlatformFileModule
{
public :
virtual IPlatformFile * GetPlatformFile ( ) override
{
static TUniquePtr < IPlatformFile > AutoDestroySingleton = MakeUnique < FStorageServerPlatformFile > ( ) ;
return AutoDestroySingleton . Get ( ) ;
}
} ;
IMPLEMENT_MODULE ( FStorageServerClientFileModule , StorageServerClient ) ;
# endif