2014-03-14 14:13:41 -04:00
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
# include "NetworkFileSystemPrivatePCH.h"
# include "PackageName.h"
# include "TargetPlatform.h"
/* FNetworkFileServerClientConnection structors
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-06-12 17:02:52 -04:00
FNetworkFileServerClientConnection : : FNetworkFileServerClientConnection ( const FFileRequestDelegate & InFileRequestDelegate ,
2014-04-23 18:33:50 -04:00
const FRecompileShadersDelegate & InRecompileShadersDelegate , const TArray < ITargetPlatform * > & InActiveTargetPlatforms )
2014-03-14 14:13:41 -04:00
: LastHandleId ( 0 )
, Sandbox ( NULL )
2014-04-23 18:33:50 -04:00
, ActiveTargetPlatforms ( InActiveTargetPlatforms )
2014-03-14 14:13:41 -04:00
{
if ( InFileRequestDelegate . IsBound ( ) )
{
FileRequestDelegate = InFileRequestDelegate ;
}
if ( InRecompileShadersDelegate . IsBound ( ) )
{
RecompileShadersDelegate = InRecompileShadersDelegate ;
}
}
FNetworkFileServerClientConnection : : ~ FNetworkFileServerClientConnection ( )
{
// close all the files the client had opened through us when the client disconnects
for ( TMap < uint64 , IFileHandle * > : : TIterator It ( OpenFiles ) ; It ; + + It )
{
delete It . Value ( ) ;
}
delete Sandbox ;
Sandbox = NULL ;
}
/* FStreamingNetworkFileServerConnection implementation
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void FNetworkFileServerClientConnection : : ConvertClientFilenameToServerFilename ( FString & FilenameToConvert )
{
if ( FilenameToConvert . StartsWith ( ConnectedEngineDir ) )
{
FilenameToConvert = FilenameToConvert . Replace ( * ConnectedEngineDir , * ( FPaths : : EngineDir ( ) ) ) ;
}
else if ( FilenameToConvert . StartsWith ( ConnectedGameDir ) )
{
if ( FPaths : : IsProjectFilePathSet ( ) )
{
FilenameToConvert = FilenameToConvert . Replace ( * ConnectedGameDir , * ( FPaths : : GetPath ( FPaths : : GetProjectFilePath ( ) ) + TEXT ( " / " ) ) ) ;
}
else
{
# if !IS_PROGRAM
// UnrealFileServer has a GameDir of ../../../Engine/Programs/UnrealFileServer.
// We do *not* want to replace the directory in that case.
FilenameToConvert = FilenameToConvert . Replace ( * ConnectedGameDir , * ( FPaths : : GameDir ( ) ) ) ;
# endif
}
}
}
2014-05-29 17:27:31 -04:00
/**
* Fixup sandbox paths to match what package loading will request on the client side . e . g .
* Sandbox path : " ../../../Elemental/Content/Elemental/Effects/FX_Snow_Cracks/Crack_02/Materials/M_SnowBlast.uasset ->
* client path : " ../../../Samples/Showcases/Elemental/Content/Elemental/Effects/FX_Snow_Cracks/Crack_02/Materials/M_SnowBlast.uasset "
* This ensures that devicelocal - cached files will be properly timestamp checked before deletion .
*/
static TMap < FString , FDateTime > FixupSandboxPathsForClient ( FSandboxPlatformFile * Sandbox , const TMap < FString , FDateTime > & SandboxPaths , const FString & LocalEngineDir , const FString & LocalGameDir )
{
TMap < FString , FDateTime > FixedFiletimes ;
FString SandboxEngine = Sandbox - > ConvertToSandboxPath ( * LocalEngineDir ) + TEXT ( " / " ) ;
// we need to add an extra bit to the game path to make the sandbox convert it correctly (investigate?)
// @todo: double check this
FString SandboxGame = Sandbox - > ConvertToSandboxPath ( * ( LocalGameDir + TEXT ( " a.txt " ) ) ) . Replace ( TEXT ( " a.txt " ) , TEXT ( " " ) ) ;
// since the sandbox remaps from A/B/C to C, and the client has no idea of this, we need to put the files
// into terms of the actual LocalGameDir, which is all that the client knows about
for ( TMap < FString , FDateTime > : : TConstIterator It ( SandboxPaths ) ; It ; + + It )
{
FString Fixed = Sandbox - > ConvertToSandboxPath ( * It . Key ( ) ) ;
Fixed = Fixed . Replace ( * SandboxEngine , * LocalEngineDir ) ;
Fixed = Fixed . Replace ( * SandboxGame , * LocalGameDir ) ;
FixedFiletimes . Add ( Fixed , It . Value ( ) ) ;
}
return FixedFiletimes ;
}
2014-03-14 14:13:41 -04:00
void FNetworkFileServerClientConnection : : ConvertServerFilenameToClientFilename ( FString & FilenameToConvert )
{
if ( FilenameToConvert . StartsWith ( FPaths : : EngineDir ( ) ) )
{
FilenameToConvert = FilenameToConvert . Replace ( * ( FPaths : : EngineDir ( ) ) , * ConnectedEngineDir ) ;
}
else if ( FPaths : : IsProjectFilePathSet ( ) )
{
if ( FilenameToConvert . StartsWith ( FPaths : : GetPath ( FPaths : : GetProjectFilePath ( ) ) ) )
{
FilenameToConvert = FilenameToConvert . Replace ( * ( FPaths : : GetPath ( FPaths : : GetProjectFilePath ( ) ) + TEXT ( " / " ) ) , * ConnectedGameDir ) ;
}
}
# if !IS_PROGRAM
else if ( FilenameToConvert . StartsWith ( FPaths : : GameDir ( ) ) )
{
// UnrealFileServer has a GameDir of ../../../Engine/Programs/UnrealFileServer.
// We do *not* want to replace the directory in that case.
FilenameToConvert = FilenameToConvert . Replace ( * ( FPaths : : GameDir ( ) ) , * ConnectedGameDir ) ;
}
# endif
}
2014-03-15 01:14:25 -04:00
static FCriticalSection SocketCriticalSection ;
2014-08-08 19:46:54 -04:00
bool FNetworkFileServerClientConnection : : ProcessPayload ( FArchive & Ar )
2014-03-14 14:13:41 -04:00
{
2014-08-08 19:46:54 -04:00
FBufferArchive Out ;
2014-07-23 15:31:40 -04:00
bool Result = true ;
2014-03-14 14:13:41 -04:00
// first part of the payload is always the command
uint32 Cmd ;
Ar < < Cmd ;
UE_LOG ( LogFileServer , Verbose , TEXT ( " Processing payload with Cmd %d " ) , Cmd ) ;
// what type of message is this?
NFS_Messages : : Type Msg = NFS_Messages : : Type ( Cmd ) ;
// make sure the first thing is GetFileList which initializes the game/platform
2014-06-17 20:48:04 -04:00
checkf ( Msg = = NFS_Messages : : GetFileList | | Msg = = NFS_Messages : : Heartbeat | | Sandbox ! = NULL , TEXT ( " The first client message MUST be GetFileList, not %d " ) , ( int32 ) Msg ) ;
2014-03-14 14:13:41 -04:00
// process the message!
bool bSendUnsolicitedFiles = false ;
{
2014-03-15 01:14:25 -04:00
FScopeLock SocketLock ( & SocketCriticalSection ) ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
switch ( Msg )
{
case NFS_Messages : : OpenRead :
ProcessOpenFile ( Ar , Out , false ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : OpenWrite :
ProcessOpenFile ( Ar , Out , true ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : Read :
ProcessReadFile ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : Write :
ProcessWriteFile ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : Seek :
ProcessSeekFile ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : Close :
ProcessCloseFile ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : MoveFile :
ProcessMoveFile ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : DeleteFile :
ProcessDeleteFile ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : GetFileInfo :
ProcessGetFileInfo ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : CopyFile :
ProcessCopyFile ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : SetTimeStamp :
ProcessSetTimeStamp ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : SetReadOnly :
ProcessSetReadOnly ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : CreateDirectory :
ProcessCreateDirectory ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : DeleteDirectory :
ProcessDeleteDirectory ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : DeleteDirectoryRecursively :
ProcessDeleteDirectoryRecursively ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : ToAbsolutePathForRead :
ProcessToAbsolutePathForRead ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : ToAbsolutePathForWrite :
ProcessToAbsolutePathForWrite ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : ReportLocalFiles :
ProcessReportLocalFiles ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : GetFileList :
2014-07-23 15:31:40 -04:00
Result = ProcessGetFileList ( Ar , Out ) ;
2014-03-15 01:14:25 -04:00
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : Heartbeat :
ProcessHeartbeat ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : SyncFile :
ProcessSyncFile ( Ar , Out ) ;
bSendUnsolicitedFiles = true ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
case NFS_Messages : : RecompileShaders :
ProcessRecompileShaders ( Ar , Out ) ;
break ;
2014-03-14 14:13:41 -04:00
2014-03-15 01:14:25 -04:00
default :
UE_LOG ( LogFileServer , Error , TEXT ( " Bad incomming message tag (%d). " ) , ( int32 ) Msg ) ;
}
2014-03-14 14:13:41 -04:00
}
2014-08-08 19:46:54 -04:00
2014-03-14 14:13:41 -04:00
// send back a reply if the command wrote anything back out
2014-08-08 19:46:54 -04:00
if ( Out . Num ( ) & & Result )
2014-03-14 14:13:41 -04:00
{
int32 NumUnsolictedFiles = UnsolictedFiles . Num ( ) ;
2014-08-08 19:46:54 -04:00
if ( bSendUnsolicitedFiles )
2014-06-18 15:52:39 -04:00
{
2014-08-08 19:46:54 -04:00
Out < < NumUnsolictedFiles ;
}
UE_LOG ( LogFileServer , Verbose , TEXT ( " Returning payload with %d bytes " ) , Out . Num ( ) ) ;
// send back a reply
Result & = SendPayload ( Out ) ;
if ( bSendUnsolicitedFiles & & Result )
{
for ( int32 Index = 0 ; Index < NumUnsolictedFiles ; Index + + )
{
FBufferArchive OutUnsolicitedFile ;
PackageFile ( UnsolictedFiles [ Index ] , OutUnsolicitedFile ) ;
UE_LOG ( LogFileServer , Display , TEXT ( " Returning unsolicited file %s with %d bytes " ) , * UnsolictedFiles [ Index ] , OutUnsolicitedFile . Num ( ) ) ;
Result & = SendPayload ( OutUnsolicitedFile ) ;
}
UnsolictedFiles . Empty ( ) ;
2014-06-12 17:02:52 -04:00
}
2014-06-18 15:52:39 -04:00
}
2014-03-14 14:13:41 -04:00
2014-06-12 17:02:52 -04:00
UE_LOG ( LogFileServer , Verbose , TEXT ( " Done Processing payload with Cmd %d Total Size sending %d " ) , Cmd , Out . TotalSize ( ) ) ;
2014-07-23 15:31:40 -04:00
return Result ;
2014-03-14 14:13:41 -04:00
}
void FNetworkFileServerClientConnection : : ProcessOpenFile ( FArchive & In , FArchive & Out , bool bIsWriting )
{
// Get filename
FString Filename ;
In < < Filename ;
bool bAppend = false ;
bool bAllowRead = false ;
if ( bIsWriting )
{
In < < bAppend ;
In < < bAllowRead ;
}
2014-08-08 19:46:54 -04:00
// todo: clients from the same ip address "could" be trying to write to the same file in the same sandbox (for example multiple windows clients)
// should probably have the sandbox write to separate files for each client
// not important for now
2014-03-14 14:13:41 -04:00
ConvertClientFilenameToServerFilename ( Filename ) ;
if ( bIsWriting )
{
// Make sure the directory exists...
Sandbox - > CreateDirectoryTree ( * ( FPaths : : GetPath ( Filename ) ) ) ;
}
TArray < FString > NewUnsolictedFiles ;
2014-04-23 16:44:02 -04:00
FileRequestDelegate . ExecuteIfBound ( Filename , ConnectedPlatformName , NewUnsolictedFiles ) ;
2014-03-14 14:13:41 -04:00
FDateTime ServerTimeStamp = Sandbox - > GetTimeStamp ( * Filename ) ;
int64 ServerFileSize = 0 ;
IFileHandle * File = bIsWriting ? Sandbox - > OpenWrite ( * Filename , bAppend , bAllowRead ) : Sandbox - > OpenRead ( * Filename ) ;
if ( ! File )
{
UE_LOG ( LogFileServer , Display , TEXT ( " Open request for %s failed for file %s. " ) , bIsWriting ? TEXT ( " Writing " ) : TEXT ( " Reading " ) , * Filename ) ;
ServerTimeStamp = FDateTime : : MinValue ( ) ; // if this was a directory, this will make sure it is not confused with a zero byte file
}
else
{
ServerFileSize = File - > Size ( ) ;
}
uint64 HandleId = + + LastHandleId ;
OpenFiles . Add ( HandleId , File ) ;
2014-07-23 15:31:40 -04:00
2014-03-14 14:13:41 -04:00
Out < < HandleId ;
Out < < ServerTimeStamp ;
Out < < ServerFileSize ;
}
void FNetworkFileServerClientConnection : : ProcessReadFile ( FArchive & In , FArchive & Out )
{
// Get Handle ID
uint64 HandleId = 0 ;
In < < HandleId ;
int64 BytesToRead = 0 ;
In < < BytesToRead ;
int64 BytesRead = 0 ;
IFileHandle * File = FindOpenFile ( HandleId ) ;
if ( File )
{
uint8 * Dest = ( uint8 * ) FMemory : : Malloc ( BytesToRead ) ;
if ( File - > Read ( Dest , BytesToRead ) )
{
BytesRead = BytesToRead ;
Out < < BytesRead ;
Out . Serialize ( Dest , BytesRead ) ;
}
else
{
Out < < BytesRead ;
}
FMemory : : Free ( Dest ) ;
}
else
{
Out < < BytesRead ;
}
}
void FNetworkFileServerClientConnection : : ProcessWriteFile ( FArchive & In , FArchive & Out )
{
// Get Handle ID
uint64 HandleId = 0 ;
In < < HandleId ;
int64 BytesWritten = 0 ;
IFileHandle * File = FindOpenFile ( HandleId ) ;
if ( File )
{
int64 BytesToWrite = 0 ;
In < < BytesToWrite ;
uint8 * Source = ( uint8 * ) FMemory : : Malloc ( BytesToWrite ) ;
In . Serialize ( Source , BytesToWrite ) ;
if ( File - > Write ( Source , BytesToWrite ) )
{
BytesWritten = BytesToWrite ;
}
FMemory : : Free ( Source ) ;
}
Out < < BytesWritten ;
}
void FNetworkFileServerClientConnection : : ProcessSeekFile ( FArchive & In , FArchive & Out )
{
// Get Handle ID
uint64 HandleId = 0 ;
In < < HandleId ;
int64 NewPosition ;
In < < NewPosition ;
int64 SetPosition = - 1 ;
IFileHandle * File = FindOpenFile ( HandleId ) ;
if ( File & & File - > Seek ( NewPosition ) )
{
SetPosition = File - > Tell ( ) ;
}
Out < < SetPosition ;
}
void FNetworkFileServerClientConnection : : ProcessCloseFile ( FArchive & In , FArchive & Out )
{
// Get Handle ID
uint64 HandleId = 0 ;
In < < HandleId ;
uint32 Closed = 0 ;
IFileHandle * File = FindOpenFile ( HandleId ) ;
if ( File )
{
Closed = 1 ;
OpenFiles . Remove ( HandleId ) ;
delete File ;
}
Out < < Closed ;
}
void FNetworkFileServerClientConnection : : ProcessGetFileInfo ( FArchive & In , FArchive & Out )
{
// Get filename
FString Filename ;
In < < Filename ;
ConvertClientFilenameToServerFilename ( Filename ) ;
FFileInfo Info ;
Info . FileExists = Sandbox - > FileExists ( * Filename ) ;
// if the file exists, cook it if necessary (the FileExists flag won't change value based on this callback)
// without this, the server can return the uncooked file size, which can cause reads off the end
if ( Info . FileExists )
{
TArray < FString > NewUnsolictedFiles ;
2014-04-23 16:44:02 -04:00
FileRequestDelegate . ExecuteIfBound ( Filename , ConnectedPlatformName , NewUnsolictedFiles ) ;
2014-03-14 14:13:41 -04:00
}
// get the rest of the info
Info . ReadOnly = Sandbox - > IsReadOnly ( * Filename ) ;
Info . Size = Sandbox - > FileSize ( * Filename ) ;
Info . TimeStamp = Sandbox - > GetTimeStamp ( * Filename ) ;
Info . AccessTimeStamp = Sandbox - > GetAccessTimeStamp ( * Filename ) ;
Out < < Info . FileExists ;
Out < < Info . ReadOnly ;
Out < < Info . Size ;
Out < < Info . TimeStamp ;
Out < < Info . AccessTimeStamp ;
}
void FNetworkFileServerClientConnection : : ProcessMoveFile ( FArchive & In , FArchive & Out )
{
FString From ;
In < < From ;
FString To ;
In < < To ;
ConvertClientFilenameToServerFilename ( From ) ;
ConvertClientFilenameToServerFilename ( To ) ;
uint32 Success = Sandbox - > MoveFile ( * To , * From ) ;
Out < < Success ;
}
void FNetworkFileServerClientConnection : : ProcessDeleteFile ( FArchive & In , FArchive & Out )
{
FString Filename ;
In < < Filename ;
ConvertClientFilenameToServerFilename ( Filename ) ;
uint32 Success = Sandbox - > DeleteFile ( * Filename ) ;
Out < < Success ;
}
void FNetworkFileServerClientConnection : : ProcessReportLocalFiles ( FArchive & In , FArchive & Out )
{
// get the list of files on the other end
TMap < FString , FDateTime > ClientFileTimes ;
In < < ClientFileTimes ;
// go over them and compare times to this side
TArray < FString > OutOfDateFiles ;
for ( TMap < FString , FDateTime > : : TIterator It ( ClientFileTimes ) ; It ; + + It )
{
FString ClientFile = It . Key ( ) ;
ConvertClientFilenameToServerFilename ( ClientFile ) ;
// get the local timestamp
FDateTime Timestamp = Sandbox - > GetTimeStamp ( * ClientFile ) ;
// if it's newer than the client/remote timestamp, it's newer here, so tell the other side it's out of date
if ( Timestamp > It . Value ( ) )
{
OutOfDateFiles . Add ( ClientFile ) ;
}
}
UE_LOG ( LogFileServer , Display , TEXT ( " There were %d out of date files " ) , OutOfDateFiles . Num ( ) ) ;
}
/** Copies file. */
void FNetworkFileServerClientConnection : : ProcessCopyFile ( FArchive & In , FArchive & Out )
{
FString To ;
FString From ;
In < < To ;
In < < From ;
ConvertClientFilenameToServerFilename ( To ) ;
ConvertClientFilenameToServerFilename ( From ) ;
bool Success = Sandbox - > CopyFile ( * To , * From ) ;
Out < < Success ;
}
void FNetworkFileServerClientConnection : : ProcessSetTimeStamp ( FArchive & In , FArchive & Out )
{
FString Filename ;
FDateTime Timestamp ;
In < < Filename ;
In < < Timestamp ;
ConvertClientFilenameToServerFilename ( Filename ) ;
Sandbox - > SetTimeStamp ( * Filename , Timestamp ) ;
// Need to sends something back otherwise the response won't get sent at all.
bool Success = true ;
Out < < Success ;
}
void FNetworkFileServerClientConnection : : ProcessSetReadOnly ( FArchive & In , FArchive & Out )
{
FString Filename ;
bool bReadOnly ;
In < < Filename ;
In < < bReadOnly ;
ConvertClientFilenameToServerFilename ( Filename ) ;
bool Success = Sandbox - > SetReadOnly ( * Filename , bReadOnly ) ;
Out < < Success ;
}
void FNetworkFileServerClientConnection : : ProcessCreateDirectory ( FArchive & In , FArchive & Out )
{
FString Directory ;
In < < Directory ;
ConvertClientFilenameToServerFilename ( Directory ) ;
bool bSuccess = Sandbox - > CreateDirectory ( * Directory ) ;
Out < < bSuccess ;
}
void FNetworkFileServerClientConnection : : ProcessDeleteDirectory ( FArchive & In , FArchive & Out )
{
FString Directory ;
In < < Directory ;
ConvertClientFilenameToServerFilename ( Directory ) ;
bool bSuccess = Sandbox - > DeleteDirectory ( * Directory ) ;
Out < < bSuccess ;
}
void FNetworkFileServerClientConnection : : ProcessDeleteDirectoryRecursively ( FArchive & In , FArchive & Out )
{
FString Directory ;
In < < Directory ;
ConvertClientFilenameToServerFilename ( Directory ) ;
bool bSuccess = Sandbox - > DeleteDirectoryRecursively ( * Directory ) ;
Out < < bSuccess ;
}
void FNetworkFileServerClientConnection : : ProcessToAbsolutePathForRead ( FArchive & In , FArchive & Out )
{
FString Filename ;
In < < Filename ;
ConvertClientFilenameToServerFilename ( Filename ) ;
Filename = Sandbox - > ConvertToAbsolutePathForExternalAppForRead ( * Filename ) ;
Out < < Filename ;
}
void FNetworkFileServerClientConnection : : ProcessToAbsolutePathForWrite ( FArchive & In , FArchive & Out )
{
FString Filename ;
In < < Filename ;
ConvertClientFilenameToServerFilename ( Filename ) ;
Filename = Sandbox - > ConvertToAbsolutePathForExternalAppForWrite ( * Filename ) ;
Out < < Filename ;
}
2014-07-23 15:31:40 -04:00
bool FNetworkFileServerClientConnection : : ProcessGetFileList ( FArchive & In , FArchive & Out )
2014-03-14 14:13:41 -04:00
{
// get the list of directories to process
TArray < FString > TargetPlatformNames ;
FString GameName ;
FString EngineRelativePath ;
FString GameRelativePath ;
TArray < FString > RootDirectories ;
bool bIsStreamingRequest = false ;
In < < TargetPlatformNames ;
In < < GameName ;
In < < EngineRelativePath ;
In < < GameRelativePath ;
In < < RootDirectories ;
In < < bIsStreamingRequest ;
ConnectedPlatformName = TEXT ( " " ) ;
2014-09-08 11:51:08 -04:00
2014-09-08 14:51:47 -04:00
// if we didn't find one (and this is a dumb server - no active platforms), then just use what was sent
if ( ActiveTargetPlatforms . Num ( ) = = 0 )
2014-03-14 14:13:41 -04:00
{
2014-09-08 14:51:47 -04:00
ConnectedPlatformName = TargetPlatformNames [ 0 ] ;
2014-03-14 14:13:41 -04:00
}
2014-09-08 14:51:47 -04:00
// we only need to care about validating the connected platform if there are active targetplatforms
else
2014-03-14 14:13:41 -04:00
{
2014-09-08 14:51:47 -04:00
// figure out the best matching target platform for the set of valid ones
for ( int32 TPIndex = 0 ; TPIndex < TargetPlatformNames . Num ( ) & & ConnectedPlatformName = = TEXT ( " " ) ; TPIndex + + )
2014-07-23 15:31:40 -04:00
{
2014-09-08 14:51:47 -04:00
UE_LOG ( LogFileServer , Display , TEXT ( " Possible Target Platform from client: %s " ) , * TargetPlatformNames [ TPIndex ] ) ;
2014-09-08 11:51:08 -04:00
2014-09-08 14:51:47 -04:00
// look for a matching target platform
2014-09-08 11:51:08 -04:00
for ( int32 ActiveTPIndex = 0 ; ActiveTPIndex < ActiveTargetPlatforms . Num ( ) ; ActiveTPIndex + + )
{
2014-09-08 14:51:47 -04:00
UE_LOG ( LogFileServer , Display , TEXT ( " Checking against: %s " ) , * ActiveTargetPlatforms [ ActiveTPIndex ] - > PlatformName ( ) ) ;
if ( ActiveTargetPlatforms [ ActiveTPIndex ] - > PlatformName ( ) = = TargetPlatformNames [ TPIndex ] )
{
ConnectedPlatformName = ActiveTargetPlatforms [ ActiveTPIndex ] - > PlatformName ( ) ;
break ;
}
}
2014-09-29 19:42:27 -04:00
}
2014-09-08 14:51:47 -04:00
2014-09-29 19:42:27 -04:00
// if we didn't find one, reject client and also print some warnings
if ( ConnectedPlatformName = = TEXT ( " " ) )
{
// reject client we can't cook/compile shaders for you!
UE_LOG ( LogFileServer , Warning , TEXT ( " Unable to find target platform for client, terminating client connection! " ) ) ;
for ( int32 TPIndex = 0 ; TPIndex < TargetPlatformNames . Num ( ) & & ConnectedPlatformName = = TEXT ( " " ) ; TPIndex + + )
2014-09-08 14:51:47 -04:00
{
2014-09-29 19:42:27 -04:00
UE_LOG ( LogFileServer , Warning , TEXT ( " Target platforms from client: %s " ) , * TargetPlatformNames [ TPIndex ] ) ;
2014-09-08 11:51:08 -04:00
}
2014-09-29 19:42:27 -04:00
for ( int32 ActiveTPIndex = 0 ; ActiveTPIndex < ActiveTargetPlatforms . Num ( ) ; ActiveTPIndex + + )
{
UE_LOG ( LogFileServer , Warning , TEXT ( " Active target platforms on server: %s " ) , * ActiveTargetPlatforms [ ActiveTPIndex ] - > PlatformName ( ) ) ;
}
return false ;
2014-09-08 11:51:08 -04:00
}
2014-03-14 14:13:41 -04:00
}
2014-07-23 15:31:40 -04:00
2014-03-14 14:13:41 -04:00
ConnectedEngineDir = EngineRelativePath ;
ConnectedGameDir = GameRelativePath ;
FString LocalEngineDir = FPaths : : EngineDir ( ) ;
FString LocalGameDir = FPaths : : GameDir ( ) ;
if ( FPaths : : IsProjectFilePathSet ( ) )
{
LocalGameDir = FPaths : : GetPath ( FPaths : : GetProjectFilePath ( ) ) + TEXT ( " / " ) ;
}
UE_LOG ( LogFileServer , Display , TEXT ( " Connected EngineDir = %s " ) , * ConnectedEngineDir ) ;
UE_LOG ( LogFileServer , Display , TEXT ( " Local EngineDir = %s " ) , * LocalEngineDir ) ;
UE_LOG ( LogFileServer , Display , TEXT ( " Connected GameDir = %s " ) , * ConnectedGameDir ) ;
UE_LOG ( LogFileServer , Display , TEXT ( " Local GameDir = %s " ) , * LocalGameDir ) ;
// Remap the root directories requested...
for ( int32 RootDirIdx = 0 ; RootDirIdx < RootDirectories . Num ( ) ; RootDirIdx + + )
{
FString CheckRootDir = RootDirectories [ RootDirIdx ] ;
ConvertClientFilenameToServerFilename ( CheckRootDir ) ;
RootDirectories [ RootDirIdx ] = CheckRootDir ;
}
// figure out the sandbox directory
// @todo: This should use FPlatformMisc::SavedDirectory(GameName)
FString SandboxDirectory ;
if ( FPaths : : IsProjectFilePathSet ( ) )
{
FString ProjectDir = FPaths : : GetPath ( FPaths : : GetProjectFilePath ( ) ) ;
2014-05-29 16:43:14 -04:00
SandboxDirectory = FPaths : : Combine ( * ProjectDir , TEXT ( " Saved " ) , TEXT ( " Cooked " ) , * ConnectedPlatformName ) ;
2014-03-14 14:13:41 -04:00
if ( bIsStreamingRequest )
{
RootDirectories . Add ( ProjectDir ) ;
}
}
else
{
2014-09-08 13:51:36 -04:00
if ( FPaths : : GetExtension ( GameName ) = = FProjectDescriptor : : GetExtension ( ) )
2014-03-14 14:13:41 -04:00
{
2014-05-29 16:43:14 -04:00
SandboxDirectory = FPaths : : Combine ( * FPaths : : GetPath ( GameName ) , TEXT ( " Saved " ) , TEXT ( " Cooked " ) , * ConnectedPlatformName ) ;
2014-03-14 14:13:41 -04:00
}
else
{
//@todo: This assumes the game is located in the UE4 Root directory
2014-05-29 16:43:14 -04:00
SandboxDirectory = FPaths : : Combine ( * FPaths : : GetRelativePathToRoot ( ) , * GameName , TEXT ( " Saved " ) , TEXT ( " Cooked " ) , * ConnectedPlatformName ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-05-29 16:43:14 -04:00
// Convert to full path so that the sandbox wrapper doesn't re-base to Saved/Sandboxes
SandboxDirectory = FPaths : : ConvertRelativePathToFull ( SandboxDirectory ) ;
2014-03-14 14:13:41 -04:00
// delete any existing one first, in case game name somehow changed and client is re-asking for files (highly unlikely)
delete Sandbox ;
Sandbox = new FSandboxPlatformFile ( false ) ;
Sandbox - > Initialize ( & FPlatformFileManager : : Get ( ) . GetPlatformFile ( ) , * FString : : Printf ( TEXT ( " -sandbox= \" %s \" " ) , * SandboxDirectory ) ) ;
// make sure the global shaders are up to date before letting the client read any shaders
// @todo: This will probably add about 1/2 second to the boot-up time of the client while the server does this
// @note: We assume the delegate will write to the proper sandbox directory, should we pass in SandboxDirectory, or Sandbox?
FShaderRecompileData RecompileData ;
RecompileData . PlatformName = ConnectedPlatformName ;
// All target platforms
RecompileData . ShaderPlatform = - 1 ;
RecompileData . ModifiedFiles = NULL ;
RecompileData . MeshMaterialMaps = NULL ;
RecompileShadersDelegate . ExecuteIfBound ( RecompileData ) ;
UE_LOG ( LogFileServer , Display , TEXT ( " Getting files for %d directories, game = %s, platform = %s " ) , RootDirectories . Num ( ) , * GameName , * ConnectedPlatformName ) ;
UE_LOG ( LogFileServer , Display , TEXT ( " Sandbox dir = %s " ) , * SandboxDirectory ) ;
for ( int32 DumpIdx = 0 ; DumpIdx < RootDirectories . Num ( ) ; DumpIdx + + )
{
UE_LOG ( LogFileServer , Display , TEXT ( " \t %s " ) , * ( RootDirectories [ DumpIdx ] ) ) ;
}
// list of directories to skip
TArray < FString > DirectoriesToSkip ;
TArray < FString > DirectoriesToNotRecurse ;
// @todo: This should really be FPlatformMisc::GetSavedDirForGame(ClientGameName), etc
for ( int32 DirIndex = 0 ; DirIndex < RootDirectories . Num ( ) ; DirIndex + + )
{
DirectoriesToSkip . Add ( FString ( RootDirectories [ DirIndex ] / TEXT ( " Saved/Backup " ) ) ) ;
DirectoriesToSkip . Add ( FString ( RootDirectories [ DirIndex ] / TEXT ( " Saved/Config " ) ) ) ;
DirectoriesToSkip . Add ( FString ( RootDirectories [ DirIndex ] / TEXT ( " Saved/Logs " ) ) ) ;
DirectoriesToSkip . Add ( FString ( RootDirectories [ DirIndex ] / TEXT ( " Saved/Sandboxes " ) ) ) ;
2014-05-29 17:46:11 -04:00
DirectoriesToSkip . Add ( FString ( RootDirectories [ DirIndex ] / TEXT ( " Saved/Cooked " ) ) ) ;
2014-05-08 20:13:47 -04:00
DirectoriesToSkip . Add ( FString ( RootDirectories [ DirIndex ] / TEXT ( " Saved/ShaderDebugInfo " ) ) ) ;
2014-03-14 14:13:41 -04:00
DirectoriesToSkip . Add ( FString ( RootDirectories [ DirIndex ] / TEXT ( " Intermediate " ) ) ) ;
DirectoriesToSkip . Add ( FString ( RootDirectories [ DirIndex ] / TEXT ( " Documentation " ) ) ) ;
DirectoriesToSkip . Add ( FString ( RootDirectories [ DirIndex ] / TEXT ( " Extras " ) ) ) ;
DirectoriesToSkip . Add ( FString ( RootDirectories [ DirIndex ] / TEXT ( " Binaries " ) ) ) ;
DirectoriesToSkip . Add ( FString ( RootDirectories [ DirIndex ] / TEXT ( " Source " ) ) ) ;
DirectoriesToNotRecurse . Add ( FString ( RootDirectories [ DirIndex ] / TEXT ( " DerivedDataCache " ) ) ) ;
}
// use the timestamp grabbing visitor (include directories)
FLocalTimestampDirectoryVisitor Visitor ( * Sandbox , DirectoriesToSkip , DirectoriesToNotRecurse , true ) ;
for ( int32 DirIndex = 0 ; DirIndex < RootDirectories . Num ( ) ; DirIndex + + )
{
Sandbox - > IterateDirectory ( * RootDirectories [ DirIndex ] , Visitor ) ;
}
// report the package version information
// The downside of this is that ALL cooked data will get tossed on package version changes
Out < < GPackageFileUE4Version ;
Out < < GPackageFileLicenseeUE4Version ;
// Send *our* engine and game dirs
Out < < LocalEngineDir ;
Out < < LocalGameDir ;
// return the files and their timestamps
2014-05-29 17:27:31 -04:00
TMap < FString , FDateTime > FixedTimes = FixupSandboxPathsForClient ( Sandbox , Visitor . FileTimes , LocalEngineDir , LocalGameDir ) ;
Out < < FixedTimes ;
2014-03-14 14:13:41 -04:00
// Do it again, preventing access to non-cooked files
if ( bIsStreamingRequest = = false )
{
// Do it again, preventing access to non-cooked files
const int32 NUM_EXCLUSION_WILDCARDS = 2 ;
FString ExclusionWildcard [ NUM_EXCLUSION_WILDCARDS ] ;
ExclusionWildcard [ 0 ] = FString ( TEXT ( " * " ) ) + FPackageName : : GetAssetPackageExtension ( ) ;
ExclusionWildcard [ 1 ] = FString ( TEXT ( " * " ) ) + FPackageName : : GetMapPackageExtension ( ) ;
for ( int32 i = 0 ; i < NUM_EXCLUSION_WILDCARDS ; + + i )
{
Sandbox - > AddExclusion ( * ExclusionWildcard [ i ] ) ;
UE_LOG ( LogFileServer , Display , TEXT ( " Excluding %s from non-sandboxed directories " ) ,
* ExclusionWildcard [ i ] ) ;
}
FLocalTimestampDirectoryVisitor VisitorForCacheDates ( * Sandbox , DirectoriesToSkip , DirectoriesToNotRecurse , true ) ;
for ( int32 DirIndex = 0 ; DirIndex < RootDirectories . Num ( ) ; DirIndex + + )
{
Sandbox - > IterateDirectory ( * RootDirectories [ DirIndex ] , VisitorForCacheDates ) ;
}
// return the cached files and their timestamps
2014-05-29 17:27:31 -04:00
FixedTimes = FixupSandboxPathsForClient ( Sandbox , VisitorForCacheDates . FileTimes , LocalEngineDir , LocalGameDir ) ;
Out < < FixedTimes ;
2014-03-14 14:13:41 -04:00
}
2014-07-23 15:31:40 -04:00
return true ;
2014-03-14 14:13:41 -04:00
}
void FNetworkFileServerClientConnection : : ProcessHeartbeat ( FArchive & In , FArchive & Out )
{
// Protect the array
FScopeLock Lock ( & ModifiedFilesSection ) ;
// return the list of modified files
Out < < ModifiedFiles ;
// @todo: note the last received time, and toss clients that don't heartbeat enough!
// @todo: Right now, there is no directory watcher adding to ModifiedFiles. It had to be pulled from this thread (well, the ModuleManager part)
// We should have a single directory watcher that pushes the changes to all the connections - or possibly pass in a shared DirectoryWatcher
// and have each connection set up a delegate (see p4 history for HandleDirectoryWatcherDirectoryChanged)
}
/* FStreamingNetworkFileServerConnection callbacks
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void FNetworkFileServerClientConnection : : PackageFile ( FString & Filename , FArchive & Out )
{
// get file timestamp and send it to client
FDateTime ServerTimeStamp = Sandbox - > GetTimeStamp ( * Filename ) ;
TArray < uint8 > Contents ;
// open file
IFileHandle * File = Sandbox - > OpenRead ( * Filename ) ;
if ( ! File )
{
ServerTimeStamp = FDateTime : : MinValue ( ) ; // if this was a directory, this will make sure it is not confused with a zero byte file
2014-07-23 15:31:40 -04:00
UE_LOG ( LogFileServer , Warning , TEXT ( " Request for missing file %s. " ) , * Filename ) ;
2014-03-14 14:13:41 -04:00
}
else
{
if ( ! File - > Size ( ) )
{
UE_LOG ( LogFileServer , Error , TEXT ( " Sending empty file %s.... " ) , * Filename ) ;
}
else
{
// read it
Contents . AddUninitialized ( File - > Size ( ) ) ;
2014-09-29 04:23:44 -04:00
File - > Read ( Contents . GetData ( ) , Contents . Num ( ) ) ;
2014-03-14 14:13:41 -04:00
}
// close it
delete File ;
UE_LOG ( LogFileServer , Display , TEXT ( " Read %s, %d bytes " ) , * Filename , Contents . Num ( ) ) ;
}
Out < < Filename ;
Out < < ServerTimeStamp ;
uint64 FileSize = Contents . Num ( ) ;
Out < < FileSize ;
Out . Serialize ( Contents . GetData ( ) , FileSize ) ;
}
void FNetworkFileServerClientConnection : : ProcessRecompileShaders ( FArchive & In , FArchive & Out )
{
TArray < FString > RecompileModifiedFiles ;
TArray < uint8 > MeshMaterialMaps ;
FShaderRecompileData RecompileData ;
RecompileData . PlatformName = ConnectedPlatformName ;
RecompileData . ModifiedFiles = & RecompileModifiedFiles ;
RecompileData . MeshMaterialMaps = & MeshMaterialMaps ;
// tell other side all the materials to load, by pathname
In < < RecompileData . MaterialsToLoad ;
In < < RecompileData . ShaderPlatform ;
In < < RecompileData . SerializedShaderResources ;
2014-06-23 10:23:52 -04:00
In < < RecompileData . bCompileChangedShaders ;
2014-03-14 14:13:41 -04:00
RecompileShadersDelegate . ExecuteIfBound ( RecompileData ) ;
// tell other side what to do!
Out < < RecompileModifiedFiles ;
Out < < MeshMaterialMaps ;
}
void FNetworkFileServerClientConnection : : ProcessSyncFile ( FArchive & In , FArchive & Out )
{
// get filename
FString Filename ;
In < < Filename ;
2014-07-23 15:31:40 -04:00
2014-03-14 14:13:41 -04:00
ConvertClientFilenameToServerFilename ( Filename ) ;
2014-07-23 15:31:40 -04:00
2014-03-14 14:13:41 -04:00
//FString AbsFile(FString(*Sandbox->ConvertToAbsolutePathForExternalApp(*Filename)).MakeStandardFilename());
// ^^ we probably in general want that filename, but for cook on the fly, we want the un-sandboxed name
TArray < FString > NewUnsolictedFiles ;
2014-04-23 16:44:02 -04:00
FileRequestDelegate . ExecuteIfBound ( Filename , ConnectedPlatformName , NewUnsolictedFiles ) ;
2014-03-14 14:13:41 -04:00
for ( int32 Index = 0 ; Index < NewUnsolictedFiles . Num ( ) ; Index + + )
{
if ( NewUnsolictedFiles [ Index ] ! = Filename )
{
UnsolictedFiles . AddUnique ( NewUnsolictedFiles [ Index ] ) ;
}
}
PackageFile ( Filename , Out ) ;
2014-06-12 17:02:52 -04:00
}
FString FNetworkFileServerClientConnection : : GetDescription ( ) const
{
return FString ( " Client For " ) + ConnectedPlatformName ;
}