Files
UnrealEngineUWP/Engine/Source/Editor/UnrealEd/Private/Cooker/IoStoreCookOnTheFlyRequestManager.cpp
CarlMagnus Nordin 0282a5aef6 Unified Zen and legacy COTF network protocols.
*Reduces code duplication
*Enables the same ODSC flow to be used for both COTF variants
*The client will now autodetect if it should run in Zen mode or not
#rb pj.kack,per.larsson
#preflight 628c79bdf057b981ca479b3e

[CL 20344832 by CarlMagnus Nordin in ue5-main branch]
2022-05-24 02:50:39 -04:00

748 lines
24 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "IoStoreCookOnTheFlyRequestManager.h"
#include "CookOnTheFlyServerInterface.h"
#include "CookOnTheFly.h"
#include "CookOnTheFlyMessages.h"
#include "Modules/ModuleManager.h"
#include "PackageStoreWriter.h"
#include "HAL/PlatformTime.h"
#include "Misc/PackageName.h"
#include "UObject/SavePackage.h"
#include "ProfilingDebugging/CountersTrace.h"
#include "AssetRegistry/IAssetRegistry.h"
#include "Templates/Function.h"
#include "IPAddress.h"
#include "SocketSubsystem.h"
#include "Sockets.h"
#include "Async/Async.h"
#include "Serialization/BufferArchive.h"
#include "NetworkMessage.h"
#include "Engine/Engine.h"
#include "MessageEndpoint.h"
#include "MessageEndpointBuilder.h"
#include "Cooker/ExternalCookOnTheFlyServer.h"
#include "CookOnTheFlyNetServer.h"
class FIoStoreCookOnTheFlyRequestManager final
: public UE::Cook::ICookOnTheFlyRequestManager
{
public:
FIoStoreCookOnTheFlyRequestManager(UE::Cook::ICookOnTheFlyServer& InCookOnTheFlyServer, const IAssetRegistry* AssetRegistry, TSharedRef<UE::Cook::ICookOnTheFlyNetworkServer> InConnectionServer)
: CookOnTheFlyServer(InCookOnTheFlyServer)
, ConnectionServer(InConnectionServer)
, ServiceId(FExternalCookOnTheFlyServer::GenerateServiceId())
{
using namespace UE::Cook;
ConnectionServer->OnClientConnected().AddRaw(this, &FIoStoreCookOnTheFlyRequestManager::OnClientConnected);
ConnectionServer->OnClientDisconnected().AddRaw(this, &FIoStoreCookOnTheFlyRequestManager::OnClientDisconnected);
ConnectionServer->OnRequest(ECookOnTheFlyMessage::GetCookedPackages).BindRaw(this, &FIoStoreCookOnTheFlyRequestManager::HandleClientRequest);
ConnectionServer->OnRequest(ECookOnTheFlyMessage::CookPackage).BindRaw(this, &FIoStoreCookOnTheFlyRequestManager::HandleClientRequest);
ConnectionServer->OnRequest(ECookOnTheFlyMessage::RecookPackages).BindRaw(this, &FIoStoreCookOnTheFlyRequestManager::HandleClientRequest);
MessageEndpoint = FMessageEndpoint::Builder("FCookOnTheFly");
TArray<FAssetData> AllAssets;
AssetRegistry->GetAllAssets(AllAssets, true);
for (const FAssetData& AssetData : AllAssets)
{
AllKnownPackagesMap.Add(FPackageId::FromName(AssetData.PackageName), AssetData.PackageName);
}
}
private:
class FPlatformContext
{
public:
enum class EPackageStatus
{
None,
Cooking,
Cooked,
Failed,
};
struct FPackage
{
EPackageStatus Status = EPackageStatus::None;
FPackageStoreEntryResource Entry;
};
FPlatformContext(FName InPlatformName, IPackageStoreWriter* InPackageWriter)
: PlatformName(InPlatformName)
, PackageWriter(InPackageWriter)
{
}
~FPlatformContext()
{
if (PackageCookedEvent)
{
FPlatformProcess::ReturnSynchEventToPool(PackageCookedEvent);
}
}
FCriticalSection& GetLock()
{
return CriticalSection;
}
void GetCompletedPackages(UE::ZenCookOnTheFly::Messaging::FCompletedPackages& OutCompletedPackages)
{
OutCompletedPackages.CookedPackages.Reserve(OutCompletedPackages.CookedPackages.Num() + Packages.Num());
for (const auto& KV : Packages)
{
if (KV.Value->Status == EPackageStatus::Cooked)
{
OutCompletedPackages.CookedPackages.Add(KV.Value->Entry);
}
else if (KV.Value->Status == EPackageStatus::Failed)
{
OutCompletedPackages.FailedPackages.Add(KV.Key);
}
}
}
EPackageStoreEntryStatus RequestCook(UE::Cook::ICookOnTheFlyServer& InCookOnTheFlyServer, const FPackageId& PackageId, TFunctionRef<FName()> GetPackageName, FPackageStoreEntryResource& OutEntry)
{
FPackage& Package = GetPackage(PackageId);
if (Package.Status == EPackageStatus::Cooked)
{
UE_LOG(LogCookOnTheFly, Verbose, TEXT("0x%llX was already cooked"), PackageId.ValueForDebugging());
OutEntry = Package.Entry;
return EPackageStoreEntryStatus::Ok;
}
else if (Package.Status == EPackageStatus::Failed)
{
UE_LOG(LogCookOnTheFly, Verbose, TEXT("0x%llX was already failed"), PackageId.ValueForDebugging());
return EPackageStoreEntryStatus::Missing;
}
else if (Package.Status == EPackageStatus::Cooking)
{
UE_LOG(LogCookOnTheFly, Verbose, TEXT("0x%llX was already cooking"), PackageId.ValueForDebugging());
return EPackageStoreEntryStatus::Pending;
}
FName PackageName = GetPackageName();
if (PackageName.IsNone())
{
UE_LOG(LogCookOnTheFly, Warning, TEXT("Received cook request for unknown package 0x%llX"), PackageId.ValueForDebugging());
return EPackageStoreEntryStatus::Missing;
}
FString Filename;
if (FPackageName::TryConvertLongPackageNameToFilename(PackageName.ToString(), Filename))
{
UE_LOG(LogCookOnTheFly, Verbose, TEXT("Cooking package 0x%llX '%s'"), PackageId.ValueForDebugging(), *PackageName.ToString());
Package.Status = EPackageStatus::Cooking;
const bool bEnqueued = InCookOnTheFlyServer.EnqueueCookRequest(UE::Cook::FCookPackageRequest{ PlatformName, Filename });
check(bEnqueued);
return EPackageStoreEntryStatus::Pending;
}
else
{
UE_LOG(LogCookOnTheFly, Warning, TEXT("Failed to cook package 0x%llX '%s' (File not found)"), PackageId.ValueForDebugging(), *PackageName.ToString());
Package.Status = EPackageStatus::Failed;
return EPackageStoreEntryStatus::Missing;
}
}
void RequestRecook(UE::Cook::ICookOnTheFlyServer& InCookOnTheFlyServer, const FPackageId& PackageId, const FName& PackageName)
{
FPackage& Package = GetPackage(PackageId);
if (Package.Status != EPackageStatus::Cooked && Package.Status != EPackageStatus::Failed)
{
UE_LOG(LogCookOnTheFly, Verbose, TEXT("Skipping recook of package 0x%llX '%s' that was not cooked"), PackageId.ValueForDebugging(), *PackageName.ToString());
return;
}
FString Filename;
if (FPackageName::TryConvertLongPackageNameToFilename(PackageName.ToString(), Filename))
{
UE_LOG(LogCookOnTheFly, Verbose, TEXT("Recooking package 0x%llX '%s'"), PackageId.ValueForDebugging(), *PackageName.ToString());
Package.Status = EPackageStatus::Cooking;
const bool bEnqueued = InCookOnTheFlyServer.EnqueueCookRequest(UE::Cook::FCookPackageRequest{ PlatformName, Filename });
check(bEnqueued);
}
else
{
UE_LOG(LogCookOnTheFly, Warning, TEXT("Failed to recook package 0x%llX '%s' (File not found)"), PackageId.ValueForDebugging(), *PackageName.ToString());
Package.Status = EPackageStatus::Failed;
}
}
void MarkAsFailed(FPackageId PackageId, UE::ZenCookOnTheFly::Messaging::FCompletedPackages& OutCompletedPackages)
{
UE_LOG(LogCookOnTheFly, Warning, TEXT("0x%llX failed"), PackageId.ValueForDebugging());
FPackage& Package = GetPackage(PackageId);
Package.Status = EPackageStatus::Failed;
OutCompletedPackages.FailedPackages.Add(PackageId);
if (PackageCookedEvent)
{
PackageCookedEvent->Trigger();
}
}
void MarkAsCooked(FPackageId PackageId, const FPackageStoreEntryResource& Entry, UE::ZenCookOnTheFly::Messaging::FCompletedPackages& OutCompletedPackages)
{
UE_LOG(LogCookOnTheFly, Verbose, TEXT("0x%llX cooked"), PackageId.ValueForDebugging());
FPackage& Package = GetPackage(PackageId);
Package.Status = EPackageStatus::Cooked;
Package.Entry = Entry;
OutCompletedPackages.CookedPackages.Add(Entry);
if (PackageCookedEvent)
{
PackageCookedEvent->Trigger();
}
}
void AddExistingPackages(TArrayView<const FPackageStoreEntryResource> Entries, TArrayView<const IPackageStoreWriter::FOplogCookInfo> CookInfos)
{
Packages.Reserve(Entries.Num());
for (int32 EntryIndex = 0, Num = Entries.Num(); EntryIndex < Num; ++EntryIndex)
{
const FPackageStoreEntryResource& Entry = Entries[EntryIndex];
const IPackageStoreWriter::FOplogCookInfo& CookInfo = CookInfos[EntryIndex];
const FPackageId PackageId = Entry.GetPackageId();
FPackage& Package = GetPackage(PackageId);
if (CookInfo.bUpToDate)
{
Package.Status = EPackageStatus::Cooked;
Package.Entry = Entry;
}
else
{
Package.Status = EPackageStatus::None;
}
}
}
FPackage& GetPackage(FPackageId PackageId)
{
TUniquePtr<FPackage>& Package = Packages.FindOrAdd(PackageId, MakeUnique<FPackage>());
check(Package.IsValid());
return *Package;
}
void AddSingleThreadedClient()
{
++SingleThreadedClientsCount;
if (!PackageCookedEvent)
{
PackageCookedEvent = FPlatformProcess::GetSynchEventFromPool();
}
}
void RemoveSingleThreadedClient()
{
check(SingleThreadedClientsCount >= 0);
if (!SingleThreadedClientsCount)
{
FPlatformProcess::ReturnSynchEventToPool(PackageCookedEvent);
PackageCookedEvent = nullptr;
}
}
void WaitForCook()
{
check(PackageCookedEvent);
PackageCookedEvent->Wait();
}
private:
FCriticalSection CriticalSection;
FName PlatformName;
TMap<FPackageId, TUniquePtr<FPackage>> Packages;
IPackageStoreWriter* PackageWriter = nullptr;
int32 SingleThreadedClientsCount = 0;
FEvent* PackageCookedEvent = nullptr;
};
virtual bool Initialize() override
{
using namespace UE::Cook;
TArray<TSharedPtr<FInternetAddr>> ListenAddresses;
if (MessageEndpoint.IsValid() && ConnectionServer->GetAddressList(ListenAddresses))
{
FZenCookOnTheFlyRegisterServiceMessage* RegisterServiceMessage = FMessageEndpoint::MakeMessage<FZenCookOnTheFlyRegisterServiceMessage>();
RegisterServiceMessage->ServiceId = ServiceId;
RegisterServiceMessage->Port = ListenAddresses[0]->GetPort();
MessageEndpoint->Publish(RegisterServiceMessage);
}
return true;
}
virtual void Shutdown() override
{
}
virtual void OnPackageGenerated(const FName& PackageName)
{
FPackageId PackageId = FPackageId::FromName(PackageName);
UE_LOG(LogCookOnTheFly, Verbose, TEXT("Package 0x%llX '%s' generated"), PackageId.ValueForDebugging(), *PackageName.ToString());
FScopeLock _(&AllKnownPackagesCriticalSection);
AllKnownPackagesMap.Add(PackageId, PackageName);
}
void TickRecookPackages()
{
TArray<FPackageId, TInlineAllocator<128>> PackageIds;
{
FScopeLock _(&PackagesToRecookCritical);
if (PackagesToRecook.IsEmpty())
{
return;
}
PackageIds = PackagesToRecook.Array();
PackagesToRecook.Empty();
}
TArray<FName, TInlineAllocator<128>> PackageNames;
PackageNames.Reserve(PackageIds.Num());
CollectGarbage(RF_NoFlags);
for (FPackageId PackageId : PackageIds)
{
FName PackageName = AllKnownPackagesMap.FindRef(PackageId);
if (!PackageName.IsNone())
{
PackageNames.Add(PackageName);
UPackage* Package = FindObjectFast<UPackage>(nullptr, PackageName);
if (Package)
{
UE_LOG(LogCookOnTheFly, Warning, TEXT("Can't recook package '%s'"), *PackageName.ToString());
UEngine::FindAndPrintStaleReferencesToObject(Package, EPrintStaleReferencesOptions::Display);
}
else
{
UE_LOG(LogCookOnTheFly, Verbose, TEXT("Recooking package '%s'"), *PackageName.ToString());
}
}
}
for (const FName& PackageName : PackageNames)
{
CookOnTheFlyServer.MarkPackageDirty(PackageName);
}
ForEachContext([this, &PackageIds, &PackageNames](FPlatformContext& Context)
{
FScopeLock _(&Context.GetLock());
for (int32 PackageIndex = 0; PackageIndex < PackageNames.Num(); ++PackageIndex)
{
const FPackageId& PackageId = PackageIds[PackageIndex];
const FName& PackageName = PackageNames[PackageIndex];
if (!PackageName.IsNone())
{
Context.RequestRecook(CookOnTheFlyServer, PackageId, PackageName);
}
}
return true;
});
}
virtual void Tick() override
{
TickRecookPackages();
}
virtual bool ShouldUseLegacyScheduling() override
{
return false;
}
private:
void OnClientConnected(UE::Cook::ICookOnTheFlyClientConnection& Connection)
{
const ITargetPlatform* TargetPlatform = Connection.GetTargetPlatform();
if (TargetPlatform)
{
IPackageStoreWriter* PackageWriter = CookOnTheFlyServer.GetPackageWriter(TargetPlatform).AsPackageStoreWriter();
check(PackageWriter); // This class should not be used except when COTFS is using an IPackageStoreWriter
FName PlatformName = Connection.GetPlatformName();
FScopeLock _(&ContextsCriticalSection);
if (!PlatformContexts.Contains(PlatformName))
{
TUniquePtr<FPlatformContext>& Context = PlatformContexts.Add(PlatformName, MakeUnique<FPlatformContext>(PlatformName, PackageWriter));
PackageWriter->GetEntries([&Context](TArrayView<const FPackageStoreEntryResource> Entries,
TArrayView<const IPackageStoreWriter::FOplogCookInfo> CookInfos)
{
Context->AddExistingPackages(Entries, CookInfos);
});
PackageWriter->OnEntryCreated().AddRaw(this, &FIoStoreCookOnTheFlyRequestManager::OnPackageStoreEntryCreated);
PackageWriter->OnCommit().AddRaw(this, &FIoStoreCookOnTheFlyRequestManager::OnPackageCooked);
PackageWriter->OnMarkUpToDate().AddRaw(this, &FIoStoreCookOnTheFlyRequestManager::OnPackagesMarkedUpToDate);
}
FPlatformContext& Context = GetContext(PlatformName);
FScopeLock __(&Context.GetLock());
if (Connection.GetIsSingleThreaded())
{
Context.AddSingleThreadedClient();
}
}
UE_LOG(LogCookOnTheFly, Display, TEXT("New client connected"));
{
FScopeLock _(&ClientsCriticalSection);
Clients.Add(&Connection);
}
}
void OnClientDisconnected(UE::Cook::ICookOnTheFlyClientConnection& Connection)
{
{
FScopeLock _(&ClientsCriticalSection);
Clients.Remove(&Connection);
}
FName PlatformName = Connection.GetPlatformName();
if (!PlatformName.IsNone())
{
if (Connection.GetIsSingleThreaded())
{
FPlatformContext& Context = GetContext(PlatformName);
FScopeLock __(&Context.GetLock());
Context.RemoveSingleThreadedClient();
}
}
}
bool HandleClientRequest(UE::Cook::ICookOnTheFlyClientConnection& Connection, const UE::Cook::FCookOnTheFlyRequest& Request)
{
using namespace UE::Cook;
const double StartTime = FPlatformTime::Seconds();
UE_LOG(LogCookOnTheFly, Verbose, TEXT("Received: %s, Size='%lld'"), *Request.GetHeader().ToString(), Request.TotalSize());
FCookOnTheFlyResponse Response(Request);
bool bRequestOk = false;
UE_LOG(LogCookOnTheFly, Verbose, TEXT("New request, Type='%s', Client='%s'"), LexToString(Request.GetHeader().MessageType), *Connection.GetPlatformName().ToString());
switch (Request.GetMessageType())
{
case UE::Cook::ECookOnTheFlyMessage::CookPackage:
bRequestOk = HandleCookPackageRequest(Connection.GetPlatformName(), Connection.GetIsSingleThreaded(), Request, Response);
break;
case UE::Cook::ECookOnTheFlyMessage::GetCookedPackages:
bRequestOk = HandleGetCookedPackagesRequest(Connection.GetPlatformName(), Request, Response);
break;
case UE::Cook::ECookOnTheFlyMessage::RecookPackages:
bRequestOk = HandleRecookPackagesRequest(Connection.GetPlatformName(), Request, Response);
break;
default:
return false;
}
if (!bRequestOk)
{
Response.SetStatus(UE::Cook::ECookOnTheFlyMessageStatus::Error);
}
bRequestOk = Connection.SendMessage(Response);
const double Duration = FPlatformTime::Seconds() - StartTime;
UE_LOG(LogCookOnTheFly, Verbose, TEXT("Request handled, Type='%s', Client='%s', Status='%s', Duration='%.6lfs'"),
LexToString(Request.GetMessageType()),
*Connection.GetPlatformName().ToString(),
bRequestOk ? TEXT("Ok") : TEXT("Failed"),
Duration);
return bRequestOk;
}
bool HandleGetCookedPackagesRequest(const FName& PlatformName, const UE::Cook::FCookOnTheFlyRequest& Request, UE::Cook::FCookOnTheFlyResponse& Response)
{
using namespace UE::Cook;
using namespace UE::ZenCookOnTheFly::Messaging;
if (PlatformName.IsNone())
{
UE_LOG(LogCookOnTheFly, Warning, TEXT("GetCookedPackagesRequest from editor client"));
Response.SetStatus(ECookOnTheFlyMessageStatus::Error);
return true;
}
else
{
FScopeLock _(&ContextsCriticalSection);
FPlatformContext& Context = GetContext(PlatformName);
FScopeLock __(&Context.GetLock());
FCompletedPackages CompletedPackages;
Context.GetCompletedPackages(CompletedPackages);
Response.SetBodyTo(MoveTemp(CompletedPackages));
}
Response.SetStatus(ECookOnTheFlyMessageStatus::Ok);
return true;
}
bool HandleCookPackageRequest(const FName& PlatformName, bool bIsSingleThreaded, const UE::Cook::FCookOnTheFlyRequest& Request, UE::Cook::FCookOnTheFlyResponse& Response)
{
using namespace UE::ZenCookOnTheFly::Messaging;
if (PlatformName.IsNone())
{
UE_LOG(LogCookOnTheFly, Warning, TEXT("CookPackageRequest from editor client"));
Response.SetStatus(UE::Cook::ECookOnTheFlyMessageStatus::Error);
return true;
}
TRACE_CPUPROFILER_EVENT_SCOPE(CookOnTheFly::HandleCookPackageRequest);
FCookPackageRequest CookRequest = Request.GetBodyAs<FCookPackageRequest>();
UE_LOG(LogCookOnTheFly, Verbose, TEXT("Received cook request 0x%llX"), CookRequest.PackageId.ValueForDebugging());
FPlatformContext& Context = GetContext(PlatformName);
{
auto GetPackageNameFunc = [this, &CookRequest]()
{
FScopeLock _(&AllKnownPackagesCriticalSection);
return AllKnownPackagesMap.FindRef(CookRequest.PackageId);
};
FPackageStoreEntryResource Entry;
EPackageStoreEntryStatus PackageStatus = EPackageStoreEntryStatus::Pending;
if (bIsSingleThreaded)
{
for (;;)
{
{
FScopeLock _(&Context.GetLock());
PackageStatus = Context.RequestCook(CookOnTheFlyServer, CookRequest.PackageId, GetPackageNameFunc, Entry);
}
if (PackageStatus != EPackageStoreEntryStatus::Pending)
{
break;
}
Context.WaitForCook();
}
}
else
{
FScopeLock _(&Context.GetLock());
PackageStatus = Context.RequestCook(CookOnTheFlyServer, CookRequest.PackageId, GetPackageNameFunc, Entry);
}
Response.SetBodyTo(FCookPackageResponse{ PackageStatus, MoveTemp(Entry) });
}
Response.SetStatus(UE::Cook::ECookOnTheFlyMessageStatus::Ok);
return true;
}
bool HandleRecookPackagesRequest(const FName& PlatformName, const UE::Cook::FCookOnTheFlyRequest& Request, UE::Cook::FCookOnTheFlyResponse& Response)
{
using namespace UE::ZenCookOnTheFly::Messaging;
TRACE_CPUPROFILER_EVENT_SCOPE(CookOnTheFly::HandleRecookPackagesRequest);
FRecookPackagesRequest RecookRequest = Request.GetBodyAs<FRecookPackagesRequest>();
UE_LOG(LogCookOnTheFly, Display, TEXT("Received recook request for %d packages"), RecookRequest.PackageIds.Num());
{
FScopeLock _(&PackagesToRecookCritical);
for (FPackageId PackageId : RecookRequest.PackageIds)
{
PackagesToRecook.Add(PackageId);
}
}
Response.SetStatus(UE::Cook::ECookOnTheFlyMessageStatus::Ok);
return true;
}
bool BroadcastMessage(const UE::Cook::FCookOnTheFlyMessage& Message, const FName& PlatformName = NAME_None)
{
using namespace UE::Cook;
UE_LOG(LogCookOnTheFly, Verbose, TEXT("Sending: %s, Size='%lld'"), *Message.GetHeader().ToString(), Message.TotalSize());
TArray<ICookOnTheFlyClientConnection*, TInlineAllocator<4>> ClientsToBroadcast;
{
FScopeLock _(&ClientsCriticalSection);
for (ICookOnTheFlyClientConnection* Client : Clients)
{
if (PlatformName.IsNone() || Client->GetPlatformName() == PlatformName)
{
ClientsToBroadcast.Add(Client);
}
}
}
bool bBroadcasted = true;
for (ICookOnTheFlyClientConnection* Client : ClientsToBroadcast)
{
if (!Client->SendMessage(Message))
{
UE_LOG(LogCookOnTheFly, Warning, TEXT("Failed to send message '%s' to client (Platform='%s')"),
LexToString(Message.GetHeader().MessageType), *Client->GetPlatformName().ToString());
bBroadcasted = false;
}
}
return bBroadcasted;
}
void OnPackageStoreEntryCreated(const IPackageStoreWriter::FEntryCreatedEventArgs& EventArgs)
{
FPlatformContext& Context = GetContext(EventArgs.PlatformName);
FScopeLock _(&Context.GetLock());
for (const FPackageId& ImportedPackageId : EventArgs.Entry.ImportedPackageIds)
{
FPackageStoreEntryResource DummyEntry;
auto GetPackageNameFunc = [this, &ImportedPackageId]()
{
FScopeLock _(&AllKnownPackagesCriticalSection);
return AllKnownPackagesMap.FindRef(ImportedPackageId);
};
EPackageStoreEntryStatus PackageStatus = Context.RequestCook(CookOnTheFlyServer, ImportedPackageId, GetPackageNameFunc, DummyEntry);
}
}
void OnPackageCooked(const IPackageStoreWriter::FCommitEventArgs& EventArgs)
{
using namespace UE::Cook;
using namespace UE::ZenCookOnTheFly::Messaging;
TRACE_CPUPROFILER_EVENT_SCOPE(CookOnTheFly::OnPackageCooked);
if (EventArgs.AdditionalFiles.Num())
{
TArray<FString> Filenames;
TArray<FIoChunkId> ChunkIds;
for (const ICookedPackageWriter::FAdditionalFileInfo& FileInfo : EventArgs.AdditionalFiles)
{
UE_LOG(LogCookOnTheFly, Verbose, TEXT("Sending additional cooked file '%s'"), *FileInfo.Filename);
Filenames.Add(FileInfo.Filename);
ChunkIds.Add(FileInfo.ChunkId);
}
FCookOnTheFlyMessage Message(ECookOnTheFlyMessage::FilesAdded);
{
TUniquePtr<FArchive> Ar = Message.WriteBody();
*Ar << Filenames;
*Ar << ChunkIds;
}
BroadcastMessage(Message, EventArgs.PlatformName);
}
FCompletedPackages NewCompletedPackages;
{
TRACE_CPUPROFILER_EVENT_SCOPE(CookOnTheFly::MarkAsCooked);
FPlatformContext& Context = GetContext(EventArgs.PlatformName);
FScopeLock _(&Context.GetLock());
if (EventArgs.EntryIndex >= 0)
{
Context.MarkAsCooked(FPackageId::FromName(EventArgs.PackageName), EventArgs.Entries[EventArgs.EntryIndex], NewCompletedPackages);
}
else
{
Context.MarkAsFailed(FPackageId::FromName(EventArgs.PackageName), NewCompletedPackages);
}
}
BroadcastCompletedPackages(EventArgs.PlatformName, MoveTemp(NewCompletedPackages));
}
void OnPackagesMarkedUpToDate(const IPackageStoreWriter::FMarkUpToDateEventArgs& EventArgs)
{
TRACE_CPUPROFILER_EVENT_SCOPE(CookOnTheFly::OnPackagesMarkedUpToDate);
UE::ZenCookOnTheFly::Messaging::FCompletedPackages NewCompletedPackages;
{
TRACE_CPUPROFILER_EVENT_SCOPE(CookOnTheFly::MarkAsCooked);
FPlatformContext& Context = GetContext(EventArgs.PlatformName);
FScopeLock _(&Context.GetLock());
for (int32 EntryIndex : EventArgs.PackageIndexes)
{
FName PackageName = EventArgs.Entries[EntryIndex].PackageName;
Context.MarkAsCooked(FPackageId::FromName(PackageName), EventArgs.Entries[EntryIndex], NewCompletedPackages);
}
}
BroadcastCompletedPackages(EventArgs.PlatformName, MoveTemp(NewCompletedPackages));
}
void BroadcastCompletedPackages(FName PlatformName, UE::ZenCookOnTheFly::Messaging::FCompletedPackages&& NewCompletedPackages)
{
using namespace UE::Cook;
using namespace UE::ZenCookOnTheFly::Messaging;
if (NewCompletedPackages.CookedPackages.Num() || NewCompletedPackages.FailedPackages.Num())
{
TRACE_CPUPROFILER_EVENT_SCOPE(CookOnTheFly::SendCookedPackagesMessage);
UE_LOG(LogCookOnTheFly, Verbose, TEXT("Sending '%s' message, Cooked='%d', Failed='%d'"),
LexToString(ECookOnTheFlyMessage::PackagesCooked),
NewCompletedPackages.CookedPackages.Num(),
NewCompletedPackages.FailedPackages.Num());
FCookOnTheFlyMessage Message(ECookOnTheFlyMessage::PackagesCooked);
Message.SetBodyTo<FCompletedPackages>(MoveTemp(NewCompletedPackages));
BroadcastMessage(Message, PlatformName);
}
}
FPlatformContext& GetContext(const FName& PlatformName)
{
FScopeLock _(&ContextsCriticalSection);
TUniquePtr<FPlatformContext>& Ctx = PlatformContexts.FindChecked(PlatformName);
check(Ctx.IsValid());
return *Ctx;
}
void ForEachContext(TFunctionRef<bool(FPlatformContext&)> Callback)
{
FScopeLock _(&ContextsCriticalSection);
for (auto& KV : PlatformContexts)
{
TUniquePtr<FPlatformContext>& Ctx = KV.Value;
check(Ctx.IsValid());
if (!Callback(*Ctx))
{
return;
}
}
}
UE::Cook::ICookOnTheFlyServer& CookOnTheFlyServer;
TSharedRef<UE::Cook::ICookOnTheFlyNetworkServer> ConnectionServer;
FCriticalSection ClientsCriticalSection;
TArray<UE::Cook::ICookOnTheFlyClientConnection*> Clients;
TSharedPtr<FMessageEndpoint, ESPMode::ThreadSafe> MessageEndpoint;
const FString ServiceId;
FCriticalSection ContextsCriticalSection;
TMap<FName, TUniquePtr<FPlatformContext>> PlatformContexts;
FCriticalSection AllKnownPackagesCriticalSection;
TMap<FPackageId, FName> AllKnownPackagesMap;
FCriticalSection PackagesToRecookCritical;
TSet<FPackageId> PackagesToRecook;
};
namespace UE { namespace Cook
{
TUniquePtr<ICookOnTheFlyRequestManager> MakeIoStoreCookOnTheFlyRequestManager(ICookOnTheFlyServer& CookOnTheFlyServer, const IAssetRegistry* AssetRegistry, TSharedRef<ICookOnTheFlyNetworkServer> ConnectionServer)
{
return MakeUnique<FIoStoreCookOnTheFlyRequestManager>(CookOnTheFlyServer, AssetRegistry, ConnectionServer);
}
}}