// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Async/Future.h" #include "Async/Mutex.h" #include "Containers/StringFwd.h" #include "Containers/StringView.h" #include "Containers/UnrealString.h" #include "Dom/JsonObject.h" #include "Experimental/ZenGlobals.h" #include "HAL/Platform.h" #include "HAL/PlatformCrt.h" #include "Misc/Optional.h" #include "Misc/TVariant.h" #include "Policies/PrettyJsonPrintPolicy.h" #include "Serialization/JsonWriter.h" #include "Templates/PimplPtr.h" #include "Templates/UniquePtr.h" #if UE_WITH_ZEN # include "ZenStatistics.h" #endif #define UE_API ZEN_API struct FAnalyticsEventAttribute; class FCbFieldView; namespace UE::Zen { struct FServiceConnectSettings { FString HostName; uint16 Port = 8558; }; struct FServicePluginSettings { FString Name; FString AbsPath; TMap Options; UE_API bool ReadFromConfig(const FString& InPluginName); UE_API bool ReadFromCompactBinary(FCbFieldView Field); UE_API void WriteToCompactBinary(FCbWriter& Writer) const; }; struct FServiceAutoLaunchSettings { FString DataPath; FString ExtraArgs; uint16 DesiredPort = 8558; TArray Plugins; bool bShowConsole = false; bool bIsDefaultDataPath = false; bool bLimitProcessLifetime = false; bool bAllowRemoteNetworkService = false; bool bSendUnattendedBugReports = false; bool bIsDefaultSharedRunContext = true; enum class EInstallMode { Copy, Link } InstallMode = EInstallMode::Copy; }; struct FServiceSettings { TVariant SettingsVariant; inline bool IsAutoLaunch() const { return SettingsVariant.IsType(); } inline bool IsConnectExisting() const { return SettingsVariant.IsType(); } inline bool IsRequirePlugins() const { return IsAutoLaunch() && !SettingsVariant.Get().Plugins.IsEmpty(); } UE_API bool ReadFromConfig(); UE_API bool ReadFromCompactBinary(FCbFieldView Field); UE_API bool ReadFromURL(FStringView InstanceURL); UE_API void WriteToCompactBinary(FCbWriter& Writer) const; private: bool TryApplyAutoLaunchOverride(); }; struct FZenWorkspaces { struct Share { FString Id; FString Dir; FString Alias; }; struct Workspace { FString Id; FString BaseDir; bool bDynamicShare; TArray WorkspaceShares; }; TArray ZenWorkspaces; bool bIsValid = false; bool operator==(const FZenWorkspaces& Other) const { if (ZenWorkspaces.Num() != Other.ZenWorkspaces.Num()) { return false; } for (int32 Idx = 0; Idx < ZenWorkspaces.Num(); ++Idx) { const Workspace& CurrentWorkspace = ZenWorkspaces[Idx]; const Workspace& OtherWorkspace = Other.ZenWorkspaces[Idx]; if (CurrentWorkspace.WorkspaceShares.Num() != OtherWorkspace.WorkspaceShares.Num()) { return false; } if (CurrentWorkspace.Id != OtherWorkspace.Id || CurrentWorkspace.BaseDir != OtherWorkspace.BaseDir || CurrentWorkspace.bDynamicShare != OtherWorkspace.bDynamicShare) { return false; } for (int32 ShareIdx = 0; ShareIdx < CurrentWorkspace.WorkspaceShares.Num(); ++ShareIdx) { const Share& CurrentShare = CurrentWorkspace.WorkspaceShares[ShareIdx]; const Share& OtherShare = OtherWorkspace.WorkspaceShares[ShareIdx]; if (CurrentShare.Id != OtherShare.Id || CurrentShare.Dir != OtherShare.Dir || CurrentShare.Alias != OtherShare.Alias) { return false; } } } return true; } }; } #if UE_WITH_ZEN namespace UE::Zen { namespace Private { UE_API bool IsLocalAutoLaunched(FStringView InstanceURL); UE_API bool GetLocalDataCachePathOverride(FString& OutDataPath); } class FZenServiceInstance; class FZenLocalServiceRunContext; UE_API bool TryGetLocalServiceRunContext(FZenLocalServiceRunContext& OutContext); class FZenLocalServiceRunContext { public: FString GetExecutable() const { return Executable; } FString GetCommandlineArguments() const { return CommandlineArguments; } FString GetWorkingDirectory() const { return WorkingDirectory; } FString GetDataPath() const { return DataPath; } bool GetShowConsole() const { return bShowConsole; } bool GetLimitProcessLifetime() const { return bLimitProcessLifetime; } friend inline bool operator==(FZenLocalServiceRunContext LHS, FZenLocalServiceRunContext RHS) { return (LHS.Executable == RHS.Executable) && (LHS.CommandlineArguments == RHS.CommandlineArguments) && (LHS.WorkingDirectory == RHS.WorkingDirectory) && (LHS.DataPath == RHS.DataPath) && (LHS.bLimitProcessLifetime == RHS.bLimitProcessLifetime); } private: FString Executable; FString CommandlineArguments; FString WorkingDirectory; FString DataPath; bool bShowConsole = false; bool bLimitProcessLifetime = false; bool ReadFromJson(FJsonObject& JsonObject); void WriteToJson(TJsonWriter>& Writer) const; bool ReadFromJsonFile(const TCHAR* Filename); bool WriteToJsonFile(const TCHAR* Filename) const; friend class FZenServiceInstance; friend UE_API bool TryGetLocalServiceRunContext(FZenLocalServiceRunContext& OutContext); }; UE_API bool IsLocalServiceRunning(const TCHAR* DataPath, uint16* OutPort = nullptr); UE_API bool StartLocalService(const FZenLocalServiceRunContext& Context); UE_API bool StopLocalService(const TCHAR* DataPath, double MaximumWaitDurationSeconds = 25.0); UE_API FString GetLocalServiceInstallPath(); UE_API FString GetLocalServiceInstallVersion(bool bDetailed = true); UE_API FString GetLocalInstallUtilityPath(); struct FGCStatus { FString Description; }; class FZenServiceEndpoint { public: enum class ESocketType : uint8 { Tcp, Unix, }; FZenServiceEndpoint() = default; FZenServiceEndpoint(FStringView InName, uint16 InPort); ESocketType GetSocketType() const { return SocketType; } FStringView GetName() const { return Name; } UE_API FStringView GetHostName() const; UE_API FString GetURL() const; uint16 GetPort() const { return Port; } private: FString Name = TEXT("localhost"); uint16 Port = 8558; ESocketType SocketType = ESocketType::Tcp; }; /** * Type used to declare usage of a Zen server instance whether the shared default instance or a unique non-default instance. * Used to help manage launch, and optionally in the future, shutdown a shared default instance. Use the default constructor * to reference the default instance (which may be launched on demand), or use the non-default constructors with a specific * URL or HostName/Port pair which is required to pre-exist (will not be auto-launched). */ class FScopeZenService { public: UE_API FScopeZenService(); UE_API FScopeZenService(FStringView InstanceURL); UE_API FScopeZenService(FServiceSettings&& InSettings); UE_API ~FScopeZenService(); const FZenServiceInstance& GetInstance() const { return *ServiceInstance; } FZenServiceInstance& GetInstance() { return *ServiceInstance; } private: FZenServiceInstance* ServiceInstance; TUniquePtr UniqueNonDefaultInstance; }; /** * Gets the default Zen service instance. The default instance is configured through ZenServiceInstance INI section. * The default instance can (depending on configuration): * - Auto-launched on demand (optionally elevated on Windows) * - Be copied out of the workspace tree before execution * - Shared between multiple tools running concurrently (implemented by launching multiple instances and expecting them to communicate and shutdown as needed) * - Instigate self-shutdown when all processes that requested it have terminated * - Use a subdirectory of the existing local DDC cache path as Zen's data path * - Use an upstream Zen service * - Be overridden by commandline arguments to reference an existing running instance instead of auto-launching a new instance. * Note that no assumptions should be made about the hostname/port of the default instance. Calling code should instead expect * that the instance is authoritative over the hostname/port that it will end up using and should query that information from the * instance as needed. */ UE_API FZenServiceInstance& GetDefaultServiceInstance(); UE_API bool IsDefaultServicePresent(); /** * A representation of a Zen service instance. Generally not accessed directly, but via FScopeZenService. */ class FZenServiceInstance { public: UE_API FZenServiceInstance(); UE_API FZenServiceInstance(FStringView InstanceURL); UE_API FZenServiceInstance(FServiceSettings&& InSettings); UE_API ~FZenServiceInstance(); UE_API const FString GetPath() const; inline const FServiceSettings& GetServiceSettings() const { return Settings; } UE_API bool IsServiceRunning(); UE_API bool IsServiceReady(); bool IsServiceRunningLocally() const { return bIsRunningLocally; } const FZenServiceEndpoint& GetEndpoint() const { return Endpoint; } UE_API bool TryRecovery(); UE_API bool GetCacheStats(FZenCacheStats& Stats); UE_API bool GetProjectStats(FZenProjectStats& Stats); UE_API bool GatherAnalytics(TArray& Attributes); UE_API int32 GetWorkspaceCount() const; UE_API bool GetWorkspaces(FZenWorkspaces& Workspaces) const; UE_API bool GetGCStatus(FGCStatus& Status); UE_API bool RequestGC(const bool* OverrideCollectSmallObjects = nullptr, const uint32* OverrideMaxCacheDuration = nullptr); UE_API bool AddSponsorProcessIDs(TArrayView SponsorProcessIDs); static UE_API uint16 GetAutoLaunchedPort(); private: void Initialize(); bool ConditionalUpdateLocalInstall(FServiceAutoLaunchSettings::EInstallMode InstallMode); bool WriteLocalPluginsConfig(); static bool AutoLaunch(const FServiceAutoLaunchSettings& InSettings, const FString& ExecutablePath, const FString& ExecutionContextFilePath, FZenServiceEndpoint& OutEndpoint); bool PingService(uint32_t AttemptCount); mutable TFuture> GCStatusRequest; mutable TOptional LastGCStatus; mutable uint64 LastGCStatusTime = 0; mutable TFuture CacheStatsRequest; mutable uint64 LastCacheStatsTime = 0; mutable FMutex LastCacheStatsMutex; mutable FZenCacheStats LastCacheStats; mutable TFuture ProjectStatsRequest; mutable uint64 LastProjectStatsTime = 0; mutable FMutex LastProjectStatsMutex; mutable FZenProjectStats LastProjectStats; mutable TFuture WorkspacesRequest; FServiceSettings Settings; FZenServiceEndpoint Endpoint; static uint16 AutoLaunchedPort; static uint32 AutoLaunchedPid; bool bHasLaunchedLocal = false; bool bIsRunningLocally = true; }; } // namespace UE::Zen #endif // UE_WITH_ZEN #undef UE_API