// Copyright Epic Games, Inc. All Rights Reserved. #include "CoreMinimal.h" #include "HAL/PlatformProcess.h" #include "HAL/FileManager.h" #include "Misc/Paths.h" #include "Misc/ScopeLock.h" #include "Misc/Guid.h" #include "Misc/App.h" #include "Async/TaskGraphInterfaces.h" #include "SwarmInterface.h" #include "SwarmMessages.h" #if USE_LOCAL_SWARM_INTERFACE #include "IMessageContext.h" #include "MessageEndpoint.h" #include "MessageEndpointBuilder.h" #include "Sockets.h" #include "SocketSubsystem.h" #include "Interfaces/IPv4/IPv4Address.h" #include "Interfaces/IPv4/IPv4Endpoint.h" #endif namespace NSwarm { /** * The C++ implementation of FSwarmInterface that's not using .NET and works only for local builds */ class FSwarmInterfaceLocalImpl : public FSwarmInterface { public: FSwarmInterfaceLocalImpl( void ); virtual ~FSwarmInterfaceLocalImpl( void ); virtual int32 OpenConnection( FConnectionCallback CallbackFunc, void* CallbackData, TLogFlags LoggingFlags, const TCHAR* OptionsFolder ); virtual int32 CloseConnection( void ); virtual int32 SendMessage( const FMessage& Message ); virtual int32 AddChannel( const TCHAR* FullPath, const TCHAR* ChannelName ); virtual int32 TestChannel( const TCHAR* ChannelName ); virtual int32 OpenChannel( const TCHAR* ChannelName, TChannelFlags ChannelFlags ); virtual int32 CloseChannel( int32 Channel ); virtual int32 WriteChannel( int32 Channel, const void* Data, int32 DataSize ); virtual int32 ReadChannel( int32 Channel, void* Data, int32 DataSize ); virtual int32 OpenJob( const FGuid& JobGuid ); virtual int32 BeginJobSpecification( const FJobSpecification& Specification32, const FJobSpecification& Specification64 ); virtual int32 AddTask( const FTaskSpecification& Specification ); virtual int32 EndJobSpecification( void ); virtual int32 CloseJob( void ); virtual int32 Log( TVerbosityLevel Verbosity, TLogColour TextColour, const TCHAR* Message ); virtual void SetJobGuid( const FGuid& JobGuid ); virtual bool IsJobProcessRunning( int32* OutStatus ); private: int32 PrepareJobFiles(); bool CopyJobFile( const TCHAR* FilePath ); void PrepareTasksList(); #if USE_LOCAL_SWARM_INTERFACE void HandlePingMessage( const FSwarmPingMessage& Message, const TSharedRef& Context ); void HandlePongMessage( const FSwarmPongMessage& Message, const TSharedRef& Context ); void HandleInfoMessage( const FSwarmInfoMessage& Message, const TSharedRef& Context ); void HandleAlertMessage( const FSwarmAlertMessage& Message, const TSharedRef& Context ); void HandleTimingMessage( const FSwarmTimingMessage& Message, const TSharedRef& Context ); void HandleTaskRequestReleaseMessage( const FSwarmTaskRequestReleaseMessage& Message, const TSharedRef& Context ); void HandleTaskRequestReservationMessage( const FSwarmTaskRequestReservationMessage& Message, const TSharedRef& Context ); void HandleTaskRequestSpecificationMessage( const FSwarmTaskRequestSpecificationMessage& Message, const TSharedRef& Context ); void HandleJobStateMessage( const FSwarmJobStateMessage& Message, const TSharedRef& Context ); void HandleTaskStateMessage( const FSwarmTaskStateMessage& Message, const TSharedRef& Context ); void HandleQuitMessage( const FSwarmQuitMessage& Message, const TSharedRef& Context ); #endif FString JobFolder; FJobSpecification JobSpecification; TArray Channels; TArray Tasks; FCriticalSection TasksCriticalSection; FConnectionCallback CallbackFunc; void* CallbackData; #if USE_LOCAL_SWARM_INTERFACE TSharedPtr MessageEndpoint; FMessageAddress Recipient; bool bIsConnected; bool bIsEditor; FProcHandle LightmassProcHandle; template void MessageSendReliable(MessageType* Message, const FMessageAddress& RecipientAddr) { MessageEndpoint->Send(Message, MessageType::StaticStruct(), EMessageFlags::Reliable, nullptr, TArrayBuilder().Add(RecipientAddr), FTimespan::Zero(), FDateTime::MaxValue()); } #endif }; #if USE_LOCAL_SWARM_INTERFACE /** * @return The Swarm singleton */ FSwarmInterface* FSwarmInterface::GInstance = NULL; bool FSwarmInterface::Initialize(const TCHAR* SwarmInterfacePath) { if( GInstance == NULL ) { GInstance = new FSwarmInterfaceLocalImpl(); } FIPv4Endpoint::Initialize(); return true; } FSwarmInterface& FSwarmInterface::Get( void ) { return( *GInstance ); } #endif FSwarmInterfaceLocalImpl::FSwarmInterfaceLocalImpl( void ) : CallbackFunc( NULL ) , CallbackData( NULL ) #if USE_LOCAL_SWARM_INTERFACE , bIsConnected( false ) , bIsEditor( false ) , LightmassProcHandle() #endif { } FSwarmInterfaceLocalImpl::~FSwarmInterfaceLocalImpl( void ) { } #if USE_LOCAL_SWARM_INTERFACE namespace SwarmInterfaceLocalImpl { bool CanUseUMB() { bool bCanUse = false; ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM); if (SocketSubsystem) { // create socket TSharedRef BindAddr = FIPv4Endpoint::Any.ToInternetAddr(); FSocket* Socket = SocketSubsystem->CreateSocket(NAME_DGram, TEXT("TestSocket"), BindAddr->GetProtocolType()); if (Socket) { if (Socket->Bind(*BindAddr)) { if (Socket->SetBroadcast(true) && Socket->SetMulticastLoopback(true)) { // should mirror UDP_MESSAGING_DEFAULT_MULTICAST_ENDPOINT if (Socket->JoinMulticastGroup(*FIPv4Endpoint(FIPv4Address(230, 0, 0, 1), 6666).ToInternetAddr())) { bCanUse = true; } } } SocketSubsystem->DestroySocket(Socket); } } return bCanUse; } } #endif int32 FSwarmInterfaceLocalImpl::OpenConnection( FConnectionCallback InCallbackFunc, void* InCallbackData, TLogFlags LoggingFlags, const TCHAR* OptionsFolder ) { // InCallbackFunc can be NULL // InCallbackData can be NULL CallbackFunc = InCallbackFunc; CallbackData = InCallbackData; #if USE_LOCAL_SWARM_INTERFACE bIsEditor = !FString(FPlatformProcess::ExecutableName()).StartsWith(TEXT("UnrealLightmass")); if (!MessageEndpoint.IsValid()) { MessageEndpoint = FMessageEndpoint::Builder("FSwarmInterfaceLocal") .Handling(this, &FSwarmInterfaceLocalImpl::HandlePingMessage) .Handling(this, &FSwarmInterfaceLocalImpl::HandlePongMessage) .Handling(this, &FSwarmInterfaceLocalImpl::HandleInfoMessage) .Handling(this, &FSwarmInterfaceLocalImpl::HandleAlertMessage) .Handling(this, &FSwarmInterfaceLocalImpl::HandleTimingMessage) .Handling(this, &FSwarmInterfaceLocalImpl::HandleTaskRequestReleaseMessage) .Handling(this, &FSwarmInterfaceLocalImpl::HandleTaskRequestReservationMessage) .Handling(this, &FSwarmInterfaceLocalImpl::HandleTaskRequestSpecificationMessage) .Handling(this, &FSwarmInterfaceLocalImpl::HandleJobStateMessage) .Handling(this, &FSwarmInterfaceLocalImpl::HandleTaskStateMessage) .Handling(this, &FSwarmInterfaceLocalImpl::HandleQuitMessage); if (MessageEndpoint.IsValid()) { MessageEndpoint->Subscribe(); MessageEndpoint->Publish(FMessageEndpoint::MakeMessage(), EMessageScope::Network); // UMB does not allow us to identify early its initialization errors - check that manually. bIsConnected = SwarmInterfaceLocalImpl::CanUseUMB(); } else { UE_LOG(LogInit, Error, TEXT("Could not open local SwarmInterface connection")); } } PrepareTasksList(); return bIsConnected ? 1 : -1; #else return 1; #endif } int32 FSwarmInterfaceLocalImpl::CloseConnection( void ) { #if USE_LOCAL_SWARM_INTERFACE if( LightmassProcHandle.IsValid() ) { FPlatformProcess::TerminateProc(LightmassProcHandle, true); FPlatformProcess::CloseProc(LightmassProcHandle); } Recipient = FMessageAddress(); MessageEndpoint.Reset(); bIsConnected = false; CallbackFunc = NULL; CallbackData = NULL; #endif return 0; } int32 FSwarmInterfaceLocalImpl::SendMessage( const FMessage& Message ) { #if USE_LOCAL_SWARM_INTERFACE const double kMaxTimeToWaitSec = 60; double TimeStartedWaiting = FPlatformTime::Seconds(); while (bIsConnected && !Recipient.IsValid()) { MessageEndpoint->Publish(FMessageEndpoint::MakeMessage(), EMessageScope::Network); FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread); FPlatformProcess::Sleep(0.5f); double TimeWaitingSec = FPlatformTime::Seconds() - TimeStartedWaiting; if (TimeWaitingSec >= kMaxTimeToWaitSec) { UE_LOG(LogInit, Error, TEXT("Timed out waiting for the recipient (TimeWaitingSec = %f)"), TimeWaitingSec); return -1; } } if (Message.Type == MESSAGE_INFO) { FInfoMessage* InfoMessage = (FInfoMessage*)&Message; MessageSendReliable(FMessageEndpoint::MakeMessage(InfoMessage->TextMessage), Recipient); } else if (Message.Type == MESSAGE_ALERT) { FAlertMessage* AlertMessage = (FAlertMessage*)&Message; MessageSendReliable(FMessageEndpoint::MakeMessage(AlertMessage->JobGuid, AlertMessage->AlertLevel, AlertMessage->ObjectGuid, AlertMessage->TypeId, AlertMessage->TextMessage), Recipient); } else if (Message.Type == MESSAGE_TIMING) { FTimingMessage* TimingMessage = (FTimingMessage*)&Message; MessageSendReliable(FMessageEndpoint::MakeMessage(TimingMessage->State, TimingMessage->ThreadNum), Recipient); } else if (Message.Type == MESSAGE_TASK_REQUEST) { FScopeLock ScopedLock(&TasksCriticalSection); if (Tasks.Num() == 0) { FTaskRequestResponse Response(RESPONSE_TYPE_RELEASE); CallbackFunc( (FMessage*)&Response, CallbackData ); } else { FTaskSpecification TaskSpec = Tasks.Pop(); CallbackFunc( (FMessage*)&TaskSpec, CallbackData ); } MessageSendReliable(FMessageEndpoint::MakeMessage(), Recipient); } else if (Message.Type == MESSAGE_TASK_REQUEST_RESPONSE) { FTaskRequestResponse* ResponseMessage = (FTaskRequestResponse*)&Message; if (ResponseMessage->ResponseType == RESPONSE_TYPE_RELEASE) { MessageSendReliable(FMessageEndpoint::MakeMessage(), Recipient); } else if (ResponseMessage->ResponseType == RESPONSE_TYPE_RESERVATION) { MessageSendReliable(FMessageEndpoint::MakeMessage(), Recipient); } else if (ResponseMessage->ResponseType == RESPONSE_TYPE_SPECIFICATION) { FTaskSpecification* SpecificationMessage = (FTaskSpecification*)ResponseMessage; TArray Dependencies; for (uint32 Index = 0; Index < SpecificationMessage->DependencyCount; Index++) { Dependencies.Add(SpecificationMessage->Dependencies[Index]); } MessageSendReliable(FMessageEndpoint::MakeMessage(SpecificationMessage->TaskGuid, SpecificationMessage->Parameters, SpecificationMessage->Flags, Dependencies), Recipient); } } else if (Message.Type == MESSAGE_JOB_STATE) { FJobState* StateMessage = (FJobState*)&Message; MessageSendReliable(FMessageEndpoint::MakeMessage(StateMessage->JobGuid, StateMessage->JobState, StateMessage->JobMessage, StateMessage->JobExitCode, StateMessage->JobRunningTime), Recipient); } else if (Message.Type == MESSAGE_TASK_STATE) { FTaskState* StateMessage = (FTaskState*)&Message; MessageSendReliable(FMessageEndpoint::MakeMessage(StateMessage->TaskGuid, StateMessage->TaskState, StateMessage->TaskMessage, StateMessage->TaskExitCode, StateMessage->TaskRunningTime), Recipient); } else if (Message.Type == MESSAGE_QUIT) { MessageSendReliable(FMessageEndpoint::MakeMessage(), Recipient); } #endif return 0; } #if USE_LOCAL_SWARM_INTERFACE void FSwarmInterfaceLocalImpl::HandlePingMessage( const FSwarmPingMessage& Message, const TSharedRef& Context ) { MessageSendReliable(FMessageEndpoint::MakeMessage(bIsEditor, FPlatformProcess::ComputerName()), Context->GetSender()); } void FSwarmInterfaceLocalImpl::HandlePongMessage( const FSwarmPongMessage& Message, const TSharedRef& Context ) { if (!Recipient.IsValid() && Message.bIsEditor != bIsEditor && Message.ComputerName == FPlatformProcess::ComputerName()) { Recipient = Context->GetSender(); } } void FSwarmInterfaceLocalImpl::HandleInfoMessage( const FSwarmInfoMessage& Message, const TSharedRef& Context ) { if( CallbackFunc ) { FInfoMessage InfoMessage(*Message.TextMessage); CallbackFunc((FMessage*)&InfoMessage, CallbackData); } } void FSwarmInterfaceLocalImpl::HandleAlertMessage( const FSwarmAlertMessage& Message, const TSharedRef& Context ) { if( CallbackFunc ) { FAlertMessage AlertMessage(Message.JobGuid, (TAlertLevel)Message.AlertLevel, Message.ObjectGuid, Message.TypeId, *Message.TextMessage); CallbackFunc((FMessage*)&AlertMessage, CallbackData); } } void FSwarmInterfaceLocalImpl::HandleTimingMessage( const FSwarmTimingMessage& Message, const TSharedRef& Context ) { if( CallbackFunc ) { FTimingMessage TimingMessage((TProgressionState)Message.State, Message.ThreadNum); CallbackFunc((FMessage*)&TimingMessage, CallbackData); } } void FSwarmInterfaceLocalImpl::HandleTaskRequestReleaseMessage( const FSwarmTaskRequestReleaseMessage& Message, const TSharedRef& Context ) { if( CallbackFunc ) { FTaskRequestResponse TaskRequestReleaseMessage(RESPONSE_TYPE_RELEASE); CallbackFunc((FMessage*)&TaskRequestReleaseMessage, CallbackData); } } void FSwarmInterfaceLocalImpl::HandleTaskRequestReservationMessage( const FSwarmTaskRequestReservationMessage& Message, const TSharedRef& Context ) { if( CallbackFunc ) { FTaskRequestResponse TaskRequestReservationMessage(RESPONSE_TYPE_RESERVATION); CallbackFunc((FMessage*)&TaskRequestReservationMessage, CallbackData); } } void FSwarmInterfaceLocalImpl::HandleTaskRequestSpecificationMessage( const FSwarmTaskRequestSpecificationMessage& Message, const TSharedRef& Context ) { if( CallbackFunc ) { FTaskSpecification TaskRequestSpecificationMessage(Message.TaskGuid, *Message.Parameters, (TJobTaskFlags)Message.Flags); TCHAR** Dependencies = (TCHAR**)FMemory::Malloc(Message.Dependencies.Num() * sizeof(TCHAR*)); for (int32 Index = 0; Index < Message.Dependencies.Num(); Index++) { Dependencies[Index] = (TCHAR*)FMemory::Malloc(Message.Dependencies[Index].Len() + 1); FMemory::Memcpy(Dependencies[Index], *Message.Dependencies[Index], Message.Dependencies[Index].Len() + 1); } TaskRequestSpecificationMessage.AddDependencies((const TCHAR**)Dependencies, Message.Dependencies.Num()); CallbackFunc((FMessage*)&TaskRequestSpecificationMessage, CallbackData); for (int32 Index = 0; Index < Message.Dependencies.Num(); Index++) { FMemory::Free(Dependencies[Index]); } FMemory::Free(Dependencies); } } void FSwarmInterfaceLocalImpl::HandleJobStateMessage( const FSwarmJobStateMessage& Message, const TSharedRef& Context ) { if( CallbackFunc ) { FJobState JobStateMessage(Message.Guid, (TJobTaskState)Message.State); CallbackFunc((FMessage*)&JobStateMessage, CallbackData); } } void FSwarmInterfaceLocalImpl::HandleTaskStateMessage( const FSwarmTaskStateMessage& Message, const TSharedRef& Context ) { if( CallbackFunc ) { FTaskState TaskStateMessage(Message.Guid, (TJobTaskState)Message.State); CallbackFunc((FMessage*)&TaskStateMessage, CallbackData); } } void FSwarmInterfaceLocalImpl::HandleQuitMessage( const FSwarmQuitMessage& Message, const TSharedRef& Context ) { if( CallbackFunc ) { FMessage QuitMessage(MESSAGE_QUIT); CallbackFunc(&QuitMessage, CallbackData); } } #endif // USE_LOCAL_SWARM_INTERFACE int32 FSwarmInterfaceLocalImpl::AddChannel( const TCHAR* FullPath, const TCHAR* ChannelName ) { if (FullPath == NULL) { return SWARM_ERROR_INVALID_ARG1; } if (ChannelName == NULL) { return SWARM_ERROR_INVALID_ARG2; } // @todo: seems unused when building locally check(0); return 0; } int32 FSwarmInterfaceLocalImpl::TestChannel( const TCHAR* ChannelName ) { if( ChannelName == NULL ) { return SWARM_ERROR_INVALID_ARG1; } FString FullPath = JobFolder / ChannelName; return FPaths::FileExists(FullPath) ? SWARM_SUCCESS : SWARM_ERROR_FILE_FOUND_NOT; } int32 FSwarmInterfaceLocalImpl::OpenChannel( const TCHAR* ChannelName, TChannelFlags ChannelFlags ) { if( ChannelName == NULL ) { return SWARM_ERROR_INVALID_ARG1; } FString FullPath = JobFolder / ChannelName; FArchive* ChannelFile = (ChannelFlags & SWARM_CHANNEL_ACCESS_WRITE) ? IFileManager::Get().CreateFileWriter(*FullPath, FILEWRITE_AllowRead) : IFileManager::Get().CreateFileReader(*FullPath); if (ChannelFile) { return Channels.Add(ChannelFile); } return SWARM_ERROR_CHANNEL_IO_FAILED; } int32 FSwarmInterfaceLocalImpl::CloseChannel( int32 Channel ) { if( Channel < 0 ) { return SWARM_ERROR_INVALID_ARG1; } FArchive* ChannelFile = Channels[Channel]; ChannelFile->Close(); delete ChannelFile; Channels[Channel] = NULL; return SWARM_SUCCESS; } int32 FSwarmInterfaceLocalImpl::WriteChannel( int32 Channel, const void* Data, int32 DataSize ) { if( Channel < 0 ) { return SWARM_ERROR_INVALID_ARG1; } if( Data == NULL ) { return SWARM_ERROR_INVALID_ARG2; } if( DataSize < 0 ) { return SWARM_ERROR_INVALID_ARG3; } if( Channels[Channel] == NULL) { return SWARM_ERROR_CHANNEL_NOT_FOUND; } Channels[Channel]->Serialize((void*)Data, DataSize); return DataSize; } int32 FSwarmInterfaceLocalImpl::ReadChannel( int32 Channel, void* Data, int32 DataSize ) { if( Channel < 0 ) { return SWARM_ERROR_INVALID_ARG1; } if( Data == NULL ) { return SWARM_ERROR_INVALID_ARG2; } if( DataSize < 0 ) { return SWARM_ERROR_INVALID_ARG3; } if( Channels[Channel] == NULL) { return SWARM_ERROR_CHANNEL_NOT_FOUND; } Channels[Channel]->Serialize((void*)Data, DataSize); return DataSize; } int32 FSwarmInterfaceLocalImpl::OpenJob( const FGuid& JobGuid ) { int32 ErrorCode = SWARM_INVALID; JobFolder = FPaths::GameAgnosticSavedDir() / TEXT("Swarm") / TEXT("SwarmCache") / TEXT("Jobs") / FString::Printf(TEXT("Job-%08X-%08X-%08X-%08X"), JobGuid.A, JobGuid.B, JobGuid.C, JobGuid.D); if (IFileManager::Get().MakeDirectory(*JobFolder, true)) { ErrorCode = SWARM_SUCCESS; } return ErrorCode; } int32 FSwarmInterfaceLocalImpl::BeginJobSpecification( const FJobSpecification& Specification32, const FJobSpecification& Specification64 ) { if( Specification32.ExecutableName == NULL && Specification64.ExecutableName == NULL ) { return SWARM_ERROR_INVALID_ARG; } if( Specification32.Parameters == NULL && Specification64.Parameters == NULL ) { return SWARM_ERROR_INVALID_ARG; } if( (Specification32.RequiredDependencyCount > 0 && Specification32.RequiredDependencies == NULL) || (Specification32.OptionalDependencyCount > 0 && Specification32.OptionalDependencies == NULL) || (Specification64.RequiredDependencyCount > 0 && Specification64.RequiredDependencies == NULL) || (Specification64.OptionalDependencyCount > 0 && Specification64.OptionalDependencies == NULL) ) { return SWARM_ERROR_INVALID_ARG; } JobSpecification = Specification32.ExecutableName ? Specification32 : Specification64; return PrepareJobFiles(); } int32 FSwarmInterfaceLocalImpl::AddTask( const FTaskSpecification& Specification ) { if( Specification.Parameters == NULL ) { return SWARM_ERROR_INVALID_ARG; } if( ( Specification.DependencyCount > 0 ) && ( Specification.Dependencies == NULL ) ) { return SWARM_ERROR_INVALID_ARG; } FString TasksFolder = JobFolder / TEXT("Tasks"); IFileManager::Get().MakeDirectory( *TasksFolder, true ); FString TaskFileName = FString::Printf(TEXT("%08X-%08X-%08X-%08X"), Specification.TaskGuid.A, Specification.TaskGuid.B, Specification.TaskGuid.C, Specification.TaskGuid.D); FArchive* TaskFile = IFileManager::Get().CreateFileWriter( *( TasksFolder / TaskFileName ) ); TaskFile->Serialize((void*)Specification.Parameters, FCString::Strlen(Specification.Parameters) * sizeof(TCHAR)); TaskFile->Close(); delete TaskFile; return 0; } int32 FSwarmInterfaceLocalImpl::EndJobSpecification( void ) { #if USE_LOCAL_SWARM_INTERFACE if (!(JobSpecification.Flags & JOB_FLAG_MANUAL_START)) { FString Parameters = FString(JobSpecification.Parameters) + (FApp::IsEngineInstalled() ? TEXT(" -installed") : TEXT("")); LightmassProcHandle = FPlatformProcess::CreateProc( JobSpecification.ExecutableName, *Parameters, true, false, false, NULL, 0, NULL, NULL); if (LightmassProcHandle.IsValid()) { return 0; } return SWARM_ERROR_CONNECTION_DISCONNECTED; } #endif return 0; } int32 FSwarmInterfaceLocalImpl::CloseJob( void ) { FScopeLock ScopedLock(&TasksCriticalSection); Channels.Empty(); Tasks.Empty(); return SWARM_SUCCESS; } bool FSwarmInterfaceLocalImpl::IsJobProcessRunning( int32* OutStatus ) { #if USE_LOCAL_SWARM_INTERFACE const bool bIsRunning = FPlatformProcess::IsProcRunning(LightmassProcHandle); if (!bIsRunning && OutStatus) { FPlatformProcess::GetProcReturnCode(LightmassProcHandle, OutStatus); } return bIsRunning; #else return false; #endif } int32 FSwarmInterfaceLocalImpl::Log( TVerbosityLevel Verbosity, TLogColour TextColour, const TCHAR* Message ) { if( Message == NULL ) { return SWARM_ERROR_NULL_POINTER; } // @todo // OutputDebugString(Message); return 0; } void FSwarmInterfaceLocalImpl::SetJobGuid( const FGuid& JobGuid ) { JobFolder = FPaths::GameAgnosticSavedDir() / TEXT("Swarm") / TEXT("SwarmCache") / TEXT("Jobs") / FString::Printf(TEXT("Job-%08X-%08X-%08X-%08X"), JobGuid.A, JobGuid.B, JobGuid.C, JobGuid.D); } int32 FSwarmInterfaceLocalImpl::PrepareJobFiles() { // Currently we're running UnrealLightmass directly from Engine/Binaries/, so no need to copy the exe and dependencies #if 0 if( !CopyJobFile(JobSpecification.ExecutableName) ) { return SWARM_ERROR_FILE_FOUND_NOT; } for( uint32 Index = 0; Index < JobSpecification.RequiredDependencyCount; Index++ ) { if( !CopyJobFile(JobSpecification.RequiredDependencies[Index]) ) { return SWARM_ERROR_FILE_FOUND_NOT; } } for( uint32 Index = 0; Index < JobSpecification.OptionalDependencyCount; Index++ ) { if( !CopyJobFile(JobSpecification.OptionalDependencies[Index]) ) { return SWARM_ERROR_FILE_FOUND_NOT; } } #endif return SWARM_SUCCESS; } bool FSwarmInterfaceLocalImpl::CopyJobFile( const TCHAR* FilePath ) { FString FileName = FPaths::GetCleanFilename( FilePath ); return IFileManager::Get().Copy( *(JobFolder / FileName), FilePath ) == COPY_OK; } void FSwarmInterfaceLocalImpl::PrepareTasksList() { FString TasksFolder = JobFolder / TEXT("Tasks"); TArray TaskFiles; IFileManager::Get().FindFiles(TaskFiles, *(TasksFolder / TEXT("*")), true, false); FScopeLock ScopedLock(&TasksCriticalSection); for (int32 FileIndex = 0; FileIndex < TaskFiles.Num(); FileIndex++) { FString& FileName = TaskFiles[FileIndex]; FGuid TaskGuid; FGuid::Parse(FileName, TaskGuid); FTaskSpecification TaskSpec(TaskGuid, TEXT("TaskDesc"), JOB_FLAG_USE_DEFAULTS); Tasks.Push(TaskSpec); } } } // namespace NSwarm