// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Modules/ModuleInterface.h" #include "Async/Future.h" #include "Misc/Timespan.h" #include "Serialization/MemoryReader.h" #include "Serialization/MemoryWriter.h" #include "Delegates/Delegate.h" class FArrayReader; class FInternetAddr; COOKONTHEFLY_API DECLARE_LOG_CATEGORY_EXTERN(LogCookOnTheFly, Log, All); namespace UE { namespace Cook { /** * Flags and message types to be used with the cook-on-the-fly server. * * The first 8 bits specifies the message type, i.e request, response or a on-way message. */ enum class ECookOnTheFlyMessage : uint32 { /* Represents no message. */ None = 0x00, /* A request message. */ Request = 0x02, /* A response message. */ Response = 0x04, TypeFlags = 0x0F, /* Request to cook a package. */ CookPackage = 0x20, /* Get all currenlty cooked packages. */ GetCookedPackages = 0x30, /* Request to recompile shaders. */ RecompileShaders = 0x40, /* One way message indicating that one or more packages has been cooked. */ PackagesCooked = 0x50, /* One way message indicating that one or more files has been added. */ FilesAdded = 0x60, /* Request to recook packages. */ RecookPackages = 0x70, /* Legacy message for NetworkPlatformFile */ NetworkPlatformFile = 0x80, }; ENUM_CLASS_FLAGS(ECookOnTheFlyMessage); /** * Returns a string from the specified message. */ inline const TCHAR* LexToString(ECookOnTheFlyMessage Message) { EnumRemoveFlags(Message, ECookOnTheFlyMessage::TypeFlags); switch (Message) { case ECookOnTheFlyMessage::None: return TEXT("None"); case ECookOnTheFlyMessage::CookPackage: return TEXT("CookPackage"); case ECookOnTheFlyMessage::GetCookedPackages: return TEXT("GetCookedPackages"); case ECookOnTheFlyMessage::RecompileShaders: return TEXT("RecompileShaders"); case ECookOnTheFlyMessage::PackagesCooked: return TEXT("PackagesCooked"); case ECookOnTheFlyMessage::FilesAdded: return TEXT("FilesAdded"); case ECookOnTheFlyMessage::RecookPackages: return TEXT("RecookPackages"); case ECookOnTheFlyMessage::NetworkPlatformFile: return TEXT("NetworkPlatformFile"); default: return TEXT("Unknown"); }; } /** * Cook-on-the-fly message status. */ enum class ECookOnTheFlyMessageStatus : uint32 { /** No status. */ None, /** The message is successful. */ Ok, /** The message failed. */ Error }; /** * Returns a string from the specified message status. */ inline const TCHAR* LexToString(ECookOnTheFlyMessageStatus Status) { switch (Status) { case ECookOnTheFlyMessageStatus::None: return TEXT("None"); case ECookOnTheFlyMessageStatus::Ok: return TEXT("Ok"); case ECookOnTheFlyMessageStatus::Error: return TEXT("Error"); default: return TEXT("Unknown"); } } /** * Cook-on-the-fly message header. */ struct FCookOnTheFlyMessageHeader { /** Type of message */ ECookOnTheFlyMessage MessageType = ECookOnTheFlyMessage::None; /** The message status. */ ECookOnTheFlyMessageStatus MessageStatus = ECookOnTheFlyMessageStatus::Ok; /** Correlation id, used to match response with request. */ uint32 CorrelationId = 0; /** When the message was sent. */ int64 Timestamp = 0; COOKONTHEFLY_API FString ToString() const; COOKONTHEFLY_API friend FArchive& operator<<(FArchive& Ar, FCookOnTheFlyMessageHeader& Header); }; /** * Cook-on-the-fly message. */ class FCookOnTheFlyMessage { public: /** Creates a new instance of a cook-on-the-fly message. */ FCookOnTheFlyMessage() = default; /** Creates a new instance of a cook-on-the-fly message with the specified message type. */ explicit FCookOnTheFlyMessage(ECookOnTheFlyMessage MessageType) { Header.MessageType = MessageType; } ECookOnTheFlyMessage GetMessageType() const { ECookOnTheFlyMessage MessageType = Header.MessageType; EnumRemoveFlags(MessageType, ECookOnTheFlyMessage::TypeFlags); return MessageType; } /** Returns the message header. */ FCookOnTheFlyMessageHeader& GetHeader() { return Header; } /** Returns the message header. */ const FCookOnTheFlyMessageHeader& GetHeader() const { return Header; } /** Set a new message header. */ void SetHeader(const FCookOnTheFlyMessageHeader& InHeader) { Header = InHeader; } /** Sets the message status. */ void SetStatus(ECookOnTheFlyMessageStatus InStatus) { Header.MessageStatus = InStatus; } /** Returns the message status. */ ECookOnTheFlyMessageStatus GetStatus() const { return Header.MessageStatus; } /** Returns whether the message stauts is OK. */ bool IsOk() const { return Header.MessageStatus == ECookOnTheFlyMessageStatus::Ok; } /** Set the message body. */ COOKONTHEFLY_API void SetBody(TArray InBody); /** Set body to serializable type. */ template void SetBodyTo(BodyType InBody) { Body.Empty(); FMemoryWriter Ar(Body); Ar << InBody; } /** Returns the message body. */ TArray& GetBody() { return Body; } /** Returns the message body. */ const TArray& GetBody() const { return Body; } /** Serialize the body as the specified type. */ template BodyType GetBodyAs() const { BodyType Type; FMemoryReader Ar(Body); Ar << Type; return MoveTemp(Type); } /** Returns the total size of the message header and message body. */ int64 TotalSize() const { return sizeof(FCookOnTheFlyMessageHeader) + Body.Num(); } /** Creates an archive for ready the message body. */ COOKONTHEFLY_API TUniquePtr ReadBody() const; /** Creates an archive for writing the message body. */ COOKONTHEFLY_API TUniquePtr WriteBody(); COOKONTHEFLY_API friend FArchive& operator<<(FArchive& Ar, FCookOnTheFlyMessage& Message); protected: FCookOnTheFlyMessageHeader Header; TArray Body; }; /** * Cook-on-the-fly request. */ class FCookOnTheFlyRequest : public FCookOnTheFlyMessage { public: /** Creates a new instance of a cook-on-the-fly request. */ FCookOnTheFlyRequest() = default; /** Creates a new instance of a cook-on-the-fly request with the specified request type. */ explicit FCookOnTheFlyRequest(ECookOnTheFlyMessage MessageType) : FCookOnTheFlyMessage(MessageType | ECookOnTheFlyMessage::Request) { } }; /** * Cook-on-the-fly response. */ class FCookOnTheFlyResponse : public FCookOnTheFlyMessage { public: /** Creates a new instance of a cook-on-the-fly response. */ FCookOnTheFlyResponse() = default; /** Creates a new instance of a cook-on-the-fly response with the specified response type. */ explicit FCookOnTheFlyResponse(ECookOnTheFlyMessage MessageType) : FCookOnTheFlyMessage(MessageType | ECookOnTheFlyMessage::Response) { } /** Creates a new instance of a cook-on-the-fly response for the specified request. */ explicit FCookOnTheFlyResponse(const FCookOnTheFlyRequest& Request) : FCookOnTheFlyMessage(Request.GetMessageType()) { Header.MessageType |= ECookOnTheFlyMessage::Response; Header.CorrelationId = Request.GetHeader().CorrelationId; } }; /** * Cook-on-the-fly host address. */ struct FCookOnTheFlyHostOptions { /** Host address. */ TArray Hosts; /** How long to wait for the server to start. */ FTimespan ServerStartupWaitTime; }; class ICookOnTheFlyServerConnection { public: virtual ~ICookOnTheFlyServerConnection() { } virtual const FString& GetHost() const = 0; virtual const FString& GetZenProjectName() const = 0; virtual const FString& GetPlatformName() const = 0; virtual const TArray GetZenHostNames() const = 0; virtual const uint16 GetZenHostPort() const = 0; /** * Returns whether connected to the cook-on-the-fly server. */ virtual bool IsConnected() const = 0; virtual bool IsSingleThreaded() const = 0; /** * Sends a request to the server. * * @param Request The request message to send. */ virtual TFuture SendRequest(FCookOnTheFlyRequest& Request) = 0; /** * Event triggered when a new message has been sent from the server. */ DECLARE_EVENT_OneParam(ICookOnTheFlyServerConnection, FMessageEvent, const FCookOnTheFlyMessage&); virtual FMessageEvent& OnMessage() = 0; }; /** * Cook-on-the-fly module */ class ICookOnTheFlyModule : public IModuleInterface { public: virtual ~ICookOnTheFlyModule() { } virtual TSharedPtr GetDefaultServerConnection() = 0; /** * Connect to the cook-on-the-fly server. * * @param HostOptions Cook-on-the-fly host options. */ virtual TUniquePtr ConnectToServer(const FCookOnTheFlyHostOptions& HostOptions) = 0; }; }} // namespace UE::Cook