You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb none #lockdown nick.penwarden Change 3046743 on 2016/07/12 by Mark.Satterthwaite Revert Metal workaround for AtmosphericFog rendering on Intel & AMD from 2897082 and instead change the MetalBackend to emit a precise::sqrt(max(0.0, value)) instruction instead of sqrt(value) to avoid the NaN from -ve values. This may still be technically incorrect versus D3D, but it matches the existing OpenGL appearance. #rb ben.woodhouse #jira UE-33028 Change 3046820 on 2016/07/12 by Peter.Sauerbrei PR#2594 - fix for analog input, courtesy of CleanCut #rb daniel.lamb Change 3046826 on 2016/07/12 by Peter.Sauerbrei PR#2561 - addition of code to limit architecture in required caps for IOS, courtesy of derekvanvliet #rb daniel.lamb Change 3046835 on 2016/07/12 by Peter.Sauerbrei PR#2559 - Increase the stack size on IOS and Mac, courtesy of derekvanvliet PR#2552 - Addition for Apple ReplayKit Framework, courtesy of JoshuaKaiser #rb daniel.lamb Change 3046838 on 2016/07/12 by Peter.Sauerbrei PR#2548 - Adding Log information when an unsupported audio type is used, courtesy of derekvanvliet #rb daniel.lamb Change 3046854 on 2016/07/12 by Peter.Sauerbrei PR#2547 - fix for unrecognize selector crash on iOS, couretesy of derekvanvliet PR#2384 - prevent crashes when initializing push notifications on IOS 7, courtesy of alk3ovation #rb daniel.lamb Change 3046858 on 2016/07/12 by Peter.Sauerbrei PR#2475, #1868 - fix for mapping of iOS device name, courtesy of wingedrobin, derekvanvliet PR#2567 - fix name of IPhoneSE in names array, courtesy of rohanliston #rb daniel.lamb Change 3046862 on 2016/07/12 by Peter.Sauerbrei fix for type in tooltip #jira UE-27123 #rb daniel.lamb Change 3046919 on 2016/07/12 by Daniel.Lamb Stop texture derived data from loading it's bulk data when the linker is destoryed. #rb Peter.Sauerbrei Change 3046922 on 2016/07/12 by Daniel.Lamb Updated the default cooker gc settings so that it can have more resources. Added support for cooker markup package and objects as (new flag) disregard for gc if it's still in use by the cooker. Changed the way reentry data is stored in the cooker. Cook only editor content flag in project settings now works again. #rb Josh.Adams #test cook Paragon Change 3046924 on 2016/07/12 by Daniel.Lamb Added support for encrypting ini files. Added new project setting in the editor and setting in ufe. Also added ForDistribution flag to ufe. #rb Peter.Sauerbrei Change 3046936 on 2016/07/12 by Mark.Satterthwaite Fix compute shader TLV clear for async. compute on Mac. #rb chris.babcock Change3047207on 2016/07/12 by Mark.Satterthwaite It is illegal to use a reference to an element within a TMap to initialise a new value that is to be added to the TMap as it causes heap-use-after-free. #rb chris.babcock Change 3047208 on 2016/07/12 by Mark.Satterthwaite When removing a vertex don't attempt to copy from one element beyond the end of the array to fill the last element - that's a heap-buffer-overflow and is unnecessary because that element will no longer be used. #rb chris.babcock Change 3047209 on 2016/07/12 by Mark.Satterthwaite Don't attempt to update Metal class counts if the MetalRHI is uninitalised - it will attempt to double-free the TMap. #rb chris.babcock Change 3047641 on 2016/07/13 by Lee.Clark PS4 - Improve SDK Version checking messages #rb none Change 3047663 on 2016/07/13 by Keith.Judge Orion - Various minor PS4-only things activated for XB1. #rb none Change 3047664 on 2016/07/13 by Keith.Judge XB1 - Fix analysis warning of shadowing a member variable. #rb none Change 3047784 on 2016/07/13 by Keith.Judge Xbox One - Memory and perf saving in query handling. Store 8 queries per allocation, rather than 1 so we're making the maximum use of the 256byte allocation granularity. #rb None Change 3047834 on 2016/07/13 by Keith.Judge XB1 - Release underlying memory of 3D textures when destroying them. Oops! #rb none Change 3048190 on 2016/07/13 by Josh.Adams - Now leave around the ASTC encoder input file on error, for reproing outside of the engine #rb none Change 3048256 on 2016/07/13 by Daniel.Lamb Removed warning about missing file when using cook on the fly. #rb Peter.Sauerbrei Change 3048409 on 2016/07/13 by Daniel.Lamb Improved output for saving packages in unattended builds. #rb Jonathan.Fitzpatrick Change 3048763 on 2016/07/13 by Peter.Sauerbrei switch AppleTV to tvOS in the editor #jira UE-30532 #rb michael.trepka Change 3049608 on 2016/07/14 by Keith.Judge XB1 - Optimize vertex/index buffer dynamic memory usage. #rb none Change 3049609 on 2016/07/14 by Keith.Judge Xbox One CPU Perf - Add _RenderThread versions of Lock/Unlock Texture 2D to stop more RHI thread stalls. #rb None Change 3049610 on 2016/07/14 by Keith.Judge Xbox One - Reduce latency of deferred deletions to two frames. #rb None Change 3049730 on 2016/07/14 by Keith.Judge Xbox One - Disable _RenderThread versions of Lock/Unlock Texture 2D for now as they're causing hangs. #rb None Change 3049732 on 2016/07/14 by Keith.Judge Xbox One - Add critical section to the query slot incrementing code as this wa causing a hang after running for a while as it can be done on any of the parallel rendering threads (not just the RHI thread. Also remove optimization pragmas accidentally left in. #rb none Change 3049791 on 2016/07/14 by Keith.Judge Xbox One - Made the occlusion query multithreading even more robust. Can play for ages now in a large level without a crash. #rb None Change 3049968 on 2016/07/14 by Jeremiah.Waldron Adding AndroidDisableThreadedRendering CVar and device profiles for 4 specific devices that need to have threaded rendering disabled on them due to swap buffer issues. Leaving previous checks in FAndroidMisc::AllowRenderThread as they are, but any new devices that need threaded rendering disabled should use the CVar #jira UE-24954, UE-27685, UE-20067 #rb chris.babcock Change 3050428 on 2016/07/14 by Jeremiah.Waldron Fix for application window being terminated if an AlertDialog is showing onPause Repro'd and fix tested on Samsung Galaxy Note 3 #android #jira UE-32998 #rb chris.babcock Change 3050642 on 2016/07/14 by Peter.Sauerbrei fix for invalid generated plist #rb daniel.lamb Change 3050718 on 2016/07/14 by Josh.Adams Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform) #rb none Change 3051327 on 2016/07/15 by Keith.Judge Xbox One - Save memory when locking 2D textures by only allocating a linear copy of the mip/array slice we're locking, rather than the entire mip chain. I'll do the same for 3D textures next. #rb None Change 3051346 on 2016/07/15 by Keith.Judge Xbox One - Same memory savings for UpdateTexture2D/3D. Only allocate for the mip/slice that we're updating, not the entire texture. #rb None. Change 3051530 on 2016/07/15 by Nick.Shin github: minor typo fixes #jira UE-32129 - GitHub 2513 : Update default output of HTML5 packaging #rb none Change 3053631 on 2016/07/18 by Mark.Satterthwaite Don't attempt to bind a 2D texture to the FOnePassPointShadowProjectionShaderParameters because it just won't work on Metal - instead bind the black-cube. This fixes validation errors that prevent running projects under the debugger. #codereview daniel.wright #rb josh.adams #jira UE-33350 Change 3053816 on 2016/07/18 by Mark.Satterthwaite Fixes for iOS Metal: - Depth-clip mode was erroneously exported on iOS SDK 9, it wasn't ever actually available. - Stencil texture views are only required on Mac. - State cache shouldn't suggest a render target change is required if the current state is clear and the new state is load/don't care as this breaks iOS rendering with MSAA. - Instead the debug submissions should just directly invoke submit and switch to rendering so that its SetRenderTarget call always succeeds. #rb michael.trepka Change 3053818 on 2016/07/18 by Mark.Satterthwaite Explicit casts for Metal precise::sqrt required for iOS to work with ffast-math workaround. #rb michael.trepka Change 3054426 on 2016/07/18 by Dmitry.Rekman Fix case-sensitive compilation problems (UE-33420). #codereview Olaf.Piesche #rb none Change 3054434 on 2016/07/18 by Mark.Satterthwaite Silence delete-non-virtual-dtor warnings on iOS as we do on Mac. #rb none Change3054719on 2016/07/18 by Jeremiah.Waldron Adding ShowHiddenAlertDialog JNI function to be called from native code after the render thread is resumed after pausing. Tested locally on Galaxy Note 3. Tested on LG G4 by nick.shin. Tested on Galaxy S6 by chris.babcock #jira UE-32998 #android #rb chris.babcock Change 3054742 on 2016/07/18 by Josh.Adams Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform) #rb none Change 3054850 on 2016/07/18 by Dmitry.Rekman Replace Fatal->Error so messagebox can be shown (UE-22818). - Incorporates PR #1714 by zaps166. #rb none #tests Tried to create an invalid context, made sure messagebox is popping up. Change 3055317 on 2016/07/19 by Lee.Clark PS4 - Fix render target memory allocation #jira UE-32988 #rb Marcus.Wassmer Change 3055682 on 2016/07/19 by Brent.Pease + Fix Debug builds by removing force inline attribute only on debug builds to prevent a warning that is treated as an error #rb michael.trepka Change 3056065 on 2016/07/19 by Josh.Adams Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform) #rb none Change 3056256 on 2016/07/19 by Chris.Babcock Add optional log spew filtering callback to Run #jira UE-33468 #ue4 #android #rb Ben.Marsh #codereview Jack.Porter Change 3056727 on 2016/07/19 by Chris.Babcock Added addition scope (plus.login) to Google Play Games builder #jira UE-33480 #ue4 #android #rb none #codereview ryan.gerleve Change 3056811 on 2016/07/19 by Jeff.Campeau Xbox One now accepts client configs. #rb none Change3057152on 2016/07/20 by Dmitry.Rekman Linux: use libc++ instead of libstdc++. - Needed to solve problems with third-party C++ libraries (e.g. WebRTC). - Bundled libc++ 3.8.1 (TPS cleared). - Turned off ICU compilation (needs recompile against libc++). - Some libraries (e.g. FBX sdk) still need libstdc++, so in practice it is going to be a mix. #rb none #tests Built and ran a number of Linux targets. Change 3057362 on 2016/07/20 by Keith.Judge XB1 - Fix busted merge from yesterday #rb None Change 3057647 on 2016/07/20 by Josh.Adams Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform) #rb none Change 3057655 on 2016/07/20 by Daniel.Lamb Added test cooking flag. #rb Peter.Sauerbrei #test Cook paragon. Change 3058779 on 2016/07/20 by Dmitry.Rekman Fix crash on cooker exit (UE-33583). - Global/static tickable objects could outlive the collection and trigger asserts when removing themselves from it. #rb Josh.Adams #codereview Josh.Adams, Jamie.Dale #tests Compiled and ran Linux editor. #lockdown Josh.Adams Change 3058835 on 2016/07/20 by Chris.Babcock Enable GooglePlay and GameCenter plugins by default #jira UE-33605 #ue4 #android #ios #rb mark.satterthwaite #codereview Peter.Sauerbrei #lockdown Josh.Adams Change 3058847 on 2016/07/20 by Chris.Babcock Fix Android device rule for AlcatelPixi3 #jira UE-33606 #ue4 #android #rb none #codereview Jeremiah.Walron #lockdown Josh.Adams Change 3059693 on 2016/07/21 by Josh.Adams Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform) #rb none #lockdown nick.penwarden Change 3060139 on 2016/07/21 by Chris.Babcock Fix proguard entry for Android mediaplayer tracks #jira UE-33644 #ue4 #android #rb Josh.Adams #lockdown Josh.Adams Change 3061151 on 2016/07/22 by Niklas.Smedberg Fast ASTC texture compression, using ISPC. #jira UE-32308 #rb chris.babcock #lockdown josh.adams Change 3061428 on 2016/07/22 by Peter.Sauerbrei Back out changelist 3061151 as it wasn't approved for submission #rb none #lockdown josh.adams Change 3061436 on 2016/07/22 by Lee.Clark PS4 - Back out render target mem allocation changes and put in a temp hack #jira UE-33657 #codereview Marcus.Wassmer #lockdown josh.adams #rb none [CL 3061637 by Josh Adams in Main branch]
1035 lines
31 KiB
C++
1035 lines
31 KiB
C++
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "NetworkFileSystemPrivatePCH.h"
|
|
#include "PackageName.h"
|
|
#include "TargetPlatform.h"
|
|
|
|
|
|
/* FNetworkFileServerClientConnection structors
|
|
*****************************************************************************/
|
|
|
|
FNetworkFileServerClientConnection::FNetworkFileServerClientConnection( const FFileRequestDelegate& InFileRequestDelegate,
|
|
const FRecompileShadersDelegate& InRecompileShadersDelegate, const TArray<ITargetPlatform*>& InActiveTargetPlatforms )
|
|
: LastHandleId(0)
|
|
, Sandbox(NULL)
|
|
, ActiveTargetPlatforms(InActiveTargetPlatforms)
|
|
{
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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, bool bLowerCaseFiles)
|
|
{
|
|
TMap<FString, FDateTime> FixedFiletimes;
|
|
FString SandboxEngine = Sandbox->ConvertToSandboxPath(*LocalEngineDir);
|
|
if (SandboxEngine.EndsWith(TEXT("/"), ESearchCase::CaseSensitive) == false)
|
|
{
|
|
SandboxEngine += 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);
|
|
|
|
if (bLowerCaseFiles)
|
|
{
|
|
Fixed = Fixed.ToLower();
|
|
}
|
|
FixedFiletimes.Add(Fixed, It.Value());
|
|
}
|
|
return FixedFiletimes;
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
static FCriticalSection SocketCriticalSection;
|
|
|
|
bool FNetworkFileServerClientConnection::ProcessPayload(FArchive& Ar)
|
|
{
|
|
FBufferArchive Out;
|
|
bool Result = true;
|
|
|
|
// 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
|
|
checkf(Msg == NFS_Messages::GetFileList || Msg == NFS_Messages::Heartbeat || Sandbox != NULL, TEXT("The first client message MUST be GetFileList, not %d"), (int32)Msg);
|
|
|
|
// process the message!
|
|
bool bSendUnsolicitedFiles = false;
|
|
|
|
{
|
|
FScopeLock SocketLock(&SocketCriticalSection);
|
|
|
|
switch (Msg)
|
|
{
|
|
case NFS_Messages::OpenRead:
|
|
ProcessOpenFile(Ar, Out, false);
|
|
break;
|
|
|
|
case NFS_Messages::OpenWrite:
|
|
ProcessOpenFile(Ar, Out, true);
|
|
break;
|
|
|
|
case NFS_Messages::Read:
|
|
ProcessReadFile(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::Write:
|
|
ProcessWriteFile(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::Seek:
|
|
ProcessSeekFile(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::Close:
|
|
ProcessCloseFile(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::MoveFile:
|
|
ProcessMoveFile(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::DeleteFile:
|
|
ProcessDeleteFile(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::GetFileInfo:
|
|
ProcessGetFileInfo(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::CopyFile:
|
|
ProcessCopyFile(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::SetTimeStamp:
|
|
ProcessSetTimeStamp(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::SetReadOnly:
|
|
ProcessSetReadOnly(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::CreateDirectory:
|
|
ProcessCreateDirectory(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::DeleteDirectory:
|
|
ProcessDeleteDirectory(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::DeleteDirectoryRecursively:
|
|
ProcessDeleteDirectoryRecursively(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::ToAbsolutePathForRead:
|
|
ProcessToAbsolutePathForRead(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::ToAbsolutePathForWrite:
|
|
ProcessToAbsolutePathForWrite(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::ReportLocalFiles:
|
|
ProcessReportLocalFiles(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::GetFileList:
|
|
Result = ProcessGetFileList(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::Heartbeat:
|
|
ProcessHeartbeat(Ar, Out);
|
|
break;
|
|
|
|
case NFS_Messages::SyncFile:
|
|
ProcessSyncFile(Ar, Out);
|
|
bSendUnsolicitedFiles = true;
|
|
break;
|
|
|
|
case NFS_Messages::RecompileShaders:
|
|
ProcessRecompileShaders(Ar, Out);
|
|
break;
|
|
|
|
default:
|
|
|
|
UE_LOG(LogFileServer, Error, TEXT("Bad incomming message tag (%d)."), (int32)Msg);
|
|
}
|
|
}
|
|
|
|
|
|
// send back a reply if the command wrote anything back out
|
|
if (Out.Num() && Result )
|
|
{
|
|
int32 NumUnsolictedFiles = 0;
|
|
|
|
|
|
if (bSendUnsolicitedFiles)
|
|
{
|
|
int64 MaxMemoryAllowed = 50 * 1024 * 1024;
|
|
for (const auto& Filename : UnsolictedFiles)
|
|
{
|
|
// get file timestamp and send it to client
|
|
FDateTime ServerTimeStamp = Sandbox->GetTimeStamp(*Filename);
|
|
|
|
TArray<uint8> Contents;
|
|
// open file
|
|
int64 FileSize = Sandbox->FileSize(*Filename);
|
|
|
|
if (MaxMemoryAllowed > FileSize)
|
|
{
|
|
MaxMemoryAllowed -= FileSize;
|
|
++NumUnsolictedFiles;
|
|
}
|
|
}
|
|
Out << NumUnsolictedFiles;
|
|
}
|
|
|
|
UE_LOG(LogFileServer, Verbose, TEXT("Returning payload with %d bytes"), Out.Num());
|
|
|
|
// send back a reply
|
|
Result &= SendPayload( Out );
|
|
|
|
TArray<FString> UnprocessedUnsolictedFiles;
|
|
UnprocessedUnsolictedFiles.Empty(NumUnsolictedFiles);
|
|
|
|
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.RemoveAt(0, NumUnsolictedFiles);
|
|
}
|
|
}
|
|
|
|
UE_LOG(LogFileServer, Verbose, TEXT("Done Processing payload with Cmd %d Total Size sending %d "), Cmd,Out.TotalSize());
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
// 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
|
|
|
|
ConvertClientFilenameToServerFilename(Filename);
|
|
|
|
if (bIsWriting)
|
|
{
|
|
// Make sure the directory exists...
|
|
Sandbox->CreateDirectoryTree(*(FPaths::GetPath(Filename)));
|
|
}
|
|
|
|
TArray<FString> NewUnsolictedFiles;
|
|
FileRequestDelegate.ExecuteIfBound(Filename, ConnectedPlatformName, NewUnsolictedFiles);
|
|
|
|
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 );
|
|
|
|
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;
|
|
FileRequestDelegate.ExecuteIfBound(Filename, ConnectedPlatformName, NewUnsolictedFiles);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
|
|
bool FNetworkFileServerClientConnection::ProcessGetFileList( FArchive& In, FArchive& Out )
|
|
{
|
|
// 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("");
|
|
|
|
bool bSendLowerCase = false;
|
|
|
|
// 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)
|
|
{
|
|
ConnectedPlatformName = TargetPlatformNames[0];
|
|
}
|
|
// we only need to care about validating the connected platform if there are active targetplatforms
|
|
else
|
|
{
|
|
// figure out the best matching target platform for the set of valid ones
|
|
for (int32 TPIndex = 0; TPIndex < TargetPlatformNames.Num() && ConnectedPlatformName == TEXT(""); TPIndex++)
|
|
{
|
|
UE_LOG(LogFileServer, Display, TEXT(" Possible Target Platform from client: %s"), *TargetPlatformNames[TPIndex]);
|
|
|
|
// look for a matching target platform
|
|
for (int32 ActiveTPIndex = 0; ActiveTPIndex < ActiveTargetPlatforms.Num(); ActiveTPIndex++)
|
|
{
|
|
UE_LOG(LogFileServer, Display, TEXT(" Checking against: %s"), *ActiveTargetPlatforms[ActiveTPIndex]->PlatformName());
|
|
if (ActiveTargetPlatforms[ActiveTPIndex]->PlatformName() == TargetPlatformNames[TPIndex])
|
|
{
|
|
bSendLowerCase = ActiveTargetPlatforms[ActiveTPIndex]->SendLowerCaseFilePaths();
|
|
ConnectedPlatformName = ActiveTargetPlatforms[ActiveTPIndex]->PlatformName();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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++)
|
|
{
|
|
UE_LOG(LogFileServer, Warning, TEXT(" Target platforms from client: %s"), *TargetPlatformNames[TPIndex]);
|
|
}
|
|
for (int32 ActiveTPIndex = 0; ActiveTPIndex < ActiveTargetPlatforms.Num(); ActiveTPIndex++)
|
|
{
|
|
UE_LOG(LogFileServer, Warning, TEXT(" Active target platforms on server: %s"), *ActiveTargetPlatforms[ActiveTPIndex]->PlatformName());
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
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());
|
|
SandboxDirectory = FPaths::Combine(*ProjectDir, TEXT("Saved"), TEXT("Cooked"), *ConnectedPlatformName);
|
|
if( bIsStreamingRequest )
|
|
{
|
|
RootDirectories.Add(ProjectDir);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FPaths::GetExtension(GameName) == FProjectDescriptor::GetExtension())
|
|
{
|
|
SandboxDirectory = FPaths::Combine(*FPaths::GetPath(GameName), TEXT("Saved"), TEXT("Cooked"), *ConnectedPlatformName);
|
|
}
|
|
else
|
|
{
|
|
//@todo: This assumes the game is located in the UE4 Root directory
|
|
SandboxDirectory = FPaths::Combine(*FPaths::GetRelativePathToRoot(), *GameName, TEXT("Saved"), TEXT("Cooked"), *ConnectedPlatformName);
|
|
}
|
|
}
|
|
// Convert to full path so that the sandbox wrapper doesn't re-base to Saved/Sandboxes
|
|
SandboxDirectory = FPaths::ConvertRelativePathToFull(SandboxDirectory);
|
|
|
|
// 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]));
|
|
}
|
|
|
|
TArray<FString> DirectoriesToAlwaysStageAsUFS;
|
|
if ( GConfig->GetArray(TEXT("/Script/UnrealEd.ProjectPackagingSettings"), TEXT("DirectoriesToAlwaysStageAsUFS"), DirectoriesToAlwaysStageAsUFS, GGameIni) )
|
|
{
|
|
for ( const auto& DirectoryToAlwaysStage : DirectoriesToAlwaysStageAsUFS )
|
|
{
|
|
RootDirectories.Add( DirectoryToAlwaysStage );
|
|
}
|
|
}
|
|
|
|
// 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")));
|
|
DirectoriesToSkip.Add(FString(RootDirectories[DirIndex] / TEXT("Saved/Cooked")));
|
|
DirectoriesToSkip.Add(FString(RootDirectories[DirIndex] / TEXT("Saved/ShaderDebugInfo")));
|
|
DirectoriesToSkip.Add(FString(RootDirectories[DirIndex] / TEXT("Saved/StagedBuilds")));
|
|
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
|
|
int32 PackageFileUE4Version = GPackageFileUE4Version;
|
|
Out << PackageFileUE4Version;
|
|
int32 PackageFileLicenseeUE4Version = GPackageFileLicenseeUE4Version;
|
|
Out << PackageFileLicenseeUE4Version;
|
|
|
|
// Send *our* engine and game dirs
|
|
Out << LocalEngineDir;
|
|
Out << LocalGameDir;
|
|
|
|
// return the files and their timestamps
|
|
TMap<FString, FDateTime> FixedTimes = FixupSandboxPathsForClient(Sandbox, Visitor.FileTimes, LocalEngineDir, LocalGameDir, bSendLowerCase);
|
|
Out << FixedTimes;
|
|
|
|
// Do it again, preventing access to non-cooked files
|
|
if( bIsStreamingRequest == false )
|
|
{
|
|
TArray<FString> RootContentPaths;
|
|
FPackageName::QueryRootContentPaths(RootContentPaths);
|
|
TArray<FString> ContentFolders;
|
|
for (const auto& RootPath : RootContentPaths)
|
|
{
|
|
const FString& ContentFolder = FPackageName::LongPackageNameToFilename(RootPath);
|
|
|
|
FString ConnectedContentFolder = ContentFolder;
|
|
ConnectedContentFolder.ReplaceInline(*LocalEngineDir, *ConnectedEngineDir);
|
|
|
|
int32 ReplaceCount = 0;
|
|
|
|
// If one path is relative and the other isn't, convert both to absolute paths before trying to replace
|
|
if (FPaths::IsRelative(LocalGameDir) != FPaths::IsRelative(ConnectedContentFolder))
|
|
{
|
|
FString AbsoluteLocalGameDir = FPaths::ConvertRelativePathToFull(LocalGameDir);
|
|
FString AbsoluteConnectedContentFolder = FPaths::ConvertRelativePathToFull(ConnectedContentFolder);
|
|
ReplaceCount = AbsoluteConnectedContentFolder.ReplaceInline(*AbsoluteLocalGameDir, *ConnectedGameDir);
|
|
if (ReplaceCount > 0)
|
|
{
|
|
ConnectedContentFolder = AbsoluteConnectedContentFolder;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ReplaceCount = ConnectedContentFolder.ReplaceInline(*LocalGameDir, *ConnectedGameDir);
|
|
}
|
|
|
|
if (ReplaceCount == 0)
|
|
{
|
|
int32 GameDirOffset = ConnectedContentFolder.Find(ConnectedGameDir, ESearchCase::IgnoreCase, ESearchDir::FromEnd);
|
|
if (GameDirOffset != INDEX_NONE)
|
|
{
|
|
ConnectedContentFolder = ConnectedContentFolder.RightChop(GameDirOffset);
|
|
}
|
|
}
|
|
|
|
ContentFolders.Add(ConnectedContentFolder);
|
|
}
|
|
Out << ContentFolders;
|
|
|
|
// 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
|
|
FixedTimes = FixupSandboxPathsForClient(Sandbox, VisitorForCacheDates.FileTimes, LocalEngineDir, LocalGameDir, bSendLowerCase);
|
|
Out << FixedTimes;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
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
|
|
*****************************************************************************/
|
|
|
|
bool 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
|
|
}
|
|
else
|
|
{
|
|
if (!File->Size())
|
|
{
|
|
UE_LOG(LogFileServer, Warning, TEXT("Sending empty file %s...."), *Filename);
|
|
}
|
|
else
|
|
{
|
|
// read it
|
|
Contents.AddUninitialized(File->Size());
|
|
File->Read(Contents.GetData(), Contents.Num());
|
|
}
|
|
|
|
// 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);
|
|
return true;
|
|
}
|
|
|
|
|
|
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;
|
|
In << RecompileData.bCompileChangedShaders;
|
|
|
|
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;
|
|
|
|
ConvertClientFilenameToServerFilename(Filename);
|
|
|
|
//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;
|
|
|
|
FileRequestDelegate.ExecuteIfBound(Filename, ConnectedPlatformName, NewUnsolictedFiles);
|
|
|
|
for (int32 Index = 0; Index < NewUnsolictedFiles.Num(); Index++)
|
|
{
|
|
if (NewUnsolictedFiles[Index] != Filename)
|
|
{
|
|
UnsolictedFiles.AddUnique(NewUnsolictedFiles[Index]);
|
|
}
|
|
}
|
|
|
|
PackageFile(Filename, Out);
|
|
}
|
|
FString FNetworkFileServerClientConnection::GetDescription() const
|
|
{
|
|
return FString("Client For " ) + ConnectedPlatformName;
|
|
}
|
|
|
|
|