添加热更

This commit is contained in:
jiegeaiai 2024-09-09 08:17:43 +08:00
commit 242fb71511
204 changed files with 27827 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
Binaries
Intermediate

View File

@ -0,0 +1,44 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "HotPatcher",
"Description": "",
"Category": "HotPatcher",
"CreatedBy": "imzlp",
"CreatedByURL": "https://imzlp.com/",
"DocsURL": "https://imzlp.com/posts/17590/",
"MarketplaceURL": "",
"SupportURL": "",
"CanContainContent": true,
"IsBetaVersion": false,
"IsExperimentalVersion": false,
"Installed": false,
"Modules": [
{
"Name": "HotPatcherEditor",
"Type": "Editor",
"LoadingPhase": "Default"
},
{
"Name": "HotPatcherCore",
"Type": "Editor",
"LoadingPhase": "PreDefault"
},
{
"Name": "HotPatcherRuntime",
"Type": "Runtime",
"LoadingPhase": "PreDefault"
},
{
"Name": "BinariesPatchFeature",
"Type": "Runtime",
"LoadingPhase": "PreDefault"
},
{
"Name": "CmdHandler",
"Type": "Developer",
"LoadingPhase": "PostConfigInit"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

View File

@ -0,0 +1,37 @@
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
using System;
using System.IO;
using UnrealBuildTool;
public class BinariesPatchFeature : ModuleRules
{
public BinariesPatchFeature(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
Path.Combine(EngineDirectory,"Source/Runtime/Launch"),
Path.Combine(ModuleDirectory,"Public"),
Path.Combine(ModuleDirectory,"../HotPatcherRuntime/Public/Templates")
// ... add public include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
"Engine",
"Projects",
"Json",
"JsonUtilities",
// ... add other public dependencies that you statically link with here ...
}
);
bLegacyPublicIncludePaths = false;
OptimizeCode = CodeOptimization.InShippingBuildsOnly;
}
}

View File

@ -0,0 +1,42 @@
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "BinariesPatchFeature.h"
#include "HotPatcherTemplateHelper.hpp"
#include "Resources/Version.h"
#include "Features/IModularFeatures.h"
#include "Misc/EnumRange.h"
#include "Modules/ModuleManager.h"
#include "UObject/Class.h"
DECAL_GETCPPTYPENAME_SPECIAL(EBinariesPatchFeature)
void OnBinariesModularFeatureRegistered(const FName& Type, IModularFeature* ModularFeature)
{
if(!Type.ToString().Equals(BINARIES_DIFF_PATCH_FEATURE_NAME,ESearchCase::IgnoreCase))
return;
IBinariesDiffPatchFeature* Feature = static_cast<IBinariesDiffPatchFeature*>(ModularFeature);
THotPatcherTemplateHelper::AppendEnumeraters<EBinariesPatchFeature>(TArray<FString>{Feature->GetFeatureName()});
}
void OnBinariesModularFeatureUnRegistered(const FName& Type, IModularFeature* ModularFeature)
{
}
void FBinariesPatchFeatureModule::StartupModule()
{
TArray<IBinariesDiffPatchFeature*> RegistedFeatures = IModularFeatures::Get().GetModularFeatureImplementations<IBinariesDiffPatchFeature>(BINARIES_DIFF_PATCH_FEATURE_NAME);
for(const auto& Featue:RegistedFeatures)
{
THotPatcherTemplateHelper::AppendEnumeraters<EBinariesPatchFeature>(TArray<FString>{Featue->GetFeatureName()});
}
IModularFeatures::Get().OnModularFeatureRegistered().AddStatic(&OnBinariesModularFeatureRegistered);
IModularFeatures::Get().OnModularFeatureUnregistered().AddStatic(&OnBinariesModularFeatureUnRegistered);
}
void FBinariesPatchFeatureModule::ShutdownModule()
{
}
IMPLEMENT_MODULE( FBinariesPatchFeatureModule, BinariesPatchFeature );

View File

@ -0,0 +1,33 @@
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Features/IModularFeature.h"
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
#include "Misc/EnumRange.h"
#include "BinariesPatchFeature.generated.h"
#define BINARIES_DIFF_PATCH_FEATURE_NAME TEXT("BinariesDiffPatchFeatures")
UENUM(BlueprintType)
enum class EBinariesPatchFeature:uint8
{
None,
Count UMETA(Hidden)
};
ENUM_RANGE_BY_COUNT(EBinariesPatchFeature, EBinariesPatchFeature::Count);
struct IBinariesDiffPatchFeature: public IModularFeature
{
virtual ~IBinariesDiffPatchFeature(){};
virtual bool CreateDiff(const TArray<uint8>& NewData, const TArray<uint8>& OldData, TArray<uint8>& OutPatch) = 0;
virtual bool PatchDiff(const TArray<uint8>& OldData, const TArray<uint8>& PatchData, TArray<uint8>& OutNewData) = 0;
virtual FString GetFeatureName()const = 0;
};
class FBinariesPatchFeatureModule : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View File

@ -0,0 +1,51 @@
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System;
using System.Collections.Generic;
using System.IO;
public class CmdHandler : ModuleRules
{
public CmdHandler(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
bLegacyPublicIncludePaths = false;
OptimizeCode = CodeOptimization.InShippingBuildsOnly;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"Json",
"SandboxFile",
"JsonUtilities",
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"InputCore",
"CoreUObject",
"Engine",
// ... add private dependencies that you statically link with here ...
}
);
}
}

View File

@ -0,0 +1,149 @@
// Copyright 2019 Lipeng Zha, Inc. All Rights Reserved.
#include "CmdHandler.h"
#include "Misc/CommandLine.h"
#include "Misc/ConfigCacheIni.h"
DEFINE_LOG_CATEGORY(LogCmdHandler);
bool OverrideConfigValue(const FString& FileName,const FString& Section,const FString& Key,int32 NewValue)
{
bool bRet = false;
int32 DefaultValue;
if(GConfig->GetInt( *Section, *Key, DefaultValue, FileName ))
{
GConfig->SetInt( *Section, *Key, NewValue, FileName );
UE_LOG(LogCmdHandler, Display, TEXT("Override Section: %s key: %s from %d to %d."), *Section,*Key,DefaultValue,NewValue);
bRet = true;
}
else
{
UE_LOG(LogCmdHandler, Warning, TEXT("Override Section: %s key: %s is not found!"), *Section,*Key);
}
return bRet;
}
#if WITH_EDITOR
static bool bDDCUrl = false;
static FString MultiCookerDDCBackendName = TEXT("MultiCookerDDC");
bool AddMultiCookerBackendToConfig(const FString& DDCAddr)
{
bool bStatus = false;
UE_LOG(LogCmdHandler,Display,TEXT("-ddcurl: %s"),*DDCAddr);
if(DDCAddr.IsEmpty() || DDCAddr.StartsWith(TEXT(" ")))
{
UE_LOG(LogCmdHandler, Warning, TEXT("not use MultiCookerDDC"));
return bStatus;
}
auto EngineIniIns = GConfig->FindConfigFile(GEngineIni);
auto MultiCookerDDCBackendSection = EngineIniIns->FindOrAddSection(MultiCookerDDCBackendName);
MultiCookerDDCBackendSection->Empty();
auto UpdateKeyLambda = [](FConfigSection* Section,const FString& Key,const FString& Value)
{
if(Section->Find(*Key))
{
Section->Remove(*Key);
}
UE_LOG(LogCmdHandler, Display, TEXT("Override Section MultiCookerDDC key: %s to %s."),*Key,*Value);
Section->Add(*Key,FConfigValue(*Value));
};
UpdateKeyLambda(MultiCookerDDCBackendSection,TEXT("MinimumDaysToKeepFile"),TEXT("7"));
UpdateKeyLambda(MultiCookerDDCBackendSection,TEXT("Root"),TEXT("(Type=KeyLength, Length=120, Inner=AsyncPut)"));
UpdateKeyLambda(MultiCookerDDCBackendSection,TEXT("AsyncPut"),TEXT("(Type=AsyncPut, Inner=Hierarchy)"));
UpdateKeyLambda(MultiCookerDDCBackendSection,TEXT("Hierarchy"),TEXT("(Type=Hierarchical, Inner=Boot, Inner=Pak, Inner=EnginePak, Inner=Local, Inner=Shared)"));
UpdateKeyLambda(MultiCookerDDCBackendSection,TEXT("Boot"),TEXT("(Type=Boot, Filename=\"%GAMEDIR%DerivedDataCache/Boot.ddc\", MaxCacheSize=512)"));
FString DDC = FString::Printf(
TEXT("(Type=FileSystem, ReadOnly=false, Clean=false, Flush=false, DeleteUnused=false, UnusedFileAge=10, FoldersToClean=10, MaxFileChecksPerSec=1, Path=%s, EnvPathOverride=UE-SharedDataCachePath, EditorOverrideSetting=SharedDerivedDataCache)"),
*DDCAddr
);
UpdateKeyLambda(MultiCookerDDCBackendSection,TEXT("Shared"),DDC);
FString DDCBackendName;
if(!FParse::Value(FCommandLine::Get(),TEXT("-ddc="),DDCBackendName))
{
DDCBackendName = MultiCookerDDCBackendName;
FCommandLine::Append(*FString::Printf(TEXT(" -ddc=%s"),*DDCBackendName));
UE_LOG(LogCmdHandler, Display, TEXT("Append cmd: %s"),FCommandLine::Get());
bStatus = true;
}
UE_LOG(LogCmdHandler, Display, TEXT("Use DDCBackend: %s"),*DDCBackendName);
return bStatus;
}
void OverrideEditorEnv()
{
int32 OverrideNumUnusedShaderCompilingThreads = 3;
if(FParse::Value(FCommandLine::Get(), TEXT("-MaxShaderWorker="), OverrideNumUnusedShaderCompilingThreads))
{
OverrideNumUnusedShaderCompilingThreads = FMath::Max(OverrideNumUnusedShaderCompilingThreads,3);
const int32 NumVirtualCores = FPlatformMisc::NumberOfCoresIncludingHyperthreads();
OverrideNumUnusedShaderCompilingThreads = NumVirtualCores - OverrideNumUnusedShaderCompilingThreads;
}
GConfig->SetInt( TEXT("DevOptions.Shaders"), TEXT("NumUnusedShaderCompilingThreads"), OverrideNumUnusedShaderCompilingThreads, GEngineIni);
OverrideConfigValue(GEngineIni,TEXT("DevOptions.Shaders"),TEXT("NumUnusedShaderCompilingThreads"), OverrideNumUnusedShaderCompilingThreads);
int32 OverrideMaxShaderJobBatchSize = 10;
FParse::Value(FCommandLine::Get(), TEXT("-MaxShaderJobBatchSize="), OverrideMaxShaderJobBatchSize);
OverrideConfigValue( GEngineIni,TEXT("DevOptions.Shaders"), TEXT("MaxShaderJobBatchSize"), OverrideMaxShaderJobBatchSize);
int32 OverrideWorkerProcessPriority;
if(FParse::Value(FCommandLine::Get(), TEXT("-SCWPriority="), OverrideWorkerProcessPriority))
{
OverrideConfigValue( GEngineIni,TEXT("DevOptions.Shaders"), TEXT("WorkerProcessPriority"), OverrideWorkerProcessPriority);
}
FString DDCURL;
if(FParse::Value(FCommandLine::Get(),TEXT("-ddcurl="),DDCURL) && !DDCURL.IsEmpty())
{
bDDCUrl = AddMultiCookerBackendToConfig(DDCURL);
}
}
void UnOverrideEditorEnv()
{
if(bDDCUrl)
{
auto EngineIniIns = GConfig->FindConfigFile(GEngineIni);
EngineIniIns->Remove(MultiCookerDDCBackendName);
}
}
#else
void OverrideRuntimeEnv()
{
}
void UnOverrideRuntimeEnv()
{
}
#endif
void FCmdHandlerModule::StartupModule()
{
#if WITH_EDITOR
OverrideEditorEnv();
#else
OverrideRuntimeEnv();
#endif
}
void FCmdHandlerModule::ShutdownModule()
{
#if WITH_EDITOR
UnOverrideEditorEnv();
#else
UnOverrideRuntimeEnv();
#endif
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FCmdHandlerModule, CmdHandler)

View File

@ -0,0 +1,17 @@
// Copyright 2019 Lipeng Zha, Inc. All Rights Reserved.
#pragma once
// engine header
#include "CoreMinimal.h"
DECLARE_LOG_CATEGORY_EXTERN(LogCmdHandler,All,All);
class FCmdHandlerModule : public IModuleInterface
{
public:
static FCmdHandlerModule& Get();
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View File

@ -0,0 +1,198 @@
#pragma once
#include "CommandletHelper.h"
#include "ETargetPlatform.h"
#include "FlibPatchParserHelper.h"
#include "ThreadUtils/FProcWorkerThread.hpp"
#include "HotPatcherLog.h"
#include "Engine/Engine.h"
void CommandletHelper::ReceiveMsg(const FString& InMsgType,const FString& InMsg)
{
UE_LOG(LogHotPatcherCommandlet,Display,TEXT("%s:%s"),*InMsgType,*InMsg);
}
void CommandletHelper::ReceiveShowMsg(const FString& InMsg)
{
UE_LOG(LogHotPatcherCommandlet,Display,TEXT("%s"),*InMsg);
}
TArray<FString> CommandletHelper::ParserPatchConfigByCommandline(const FString& Commandline,const FString& Token)
{
TArray<FString> result;
TMap<FString, FString> KeyValues = THotPatcherTemplateHelper::GetCommandLineParamsMap(Commandline);
if(KeyValues.Find(Token))
{
FString AddPakListInfo = *KeyValues.Find(Token);
AddPakListInfo.ParseIntoArray(result,TEXT(","));
}
return result;
}
TArray<ETargetPlatform> CommandletHelper::ParserPlatforms(const FString& Commandline, const FString& Token)
{
TArray<ETargetPlatform> result;
for(auto& PlatformName:ParserPatchConfigByCommandline(Commandline,Token))
{
ETargetPlatform Platform = ETargetPlatform::None;
THotPatcherTemplateHelper::GetEnumValueByName(PlatformName,Platform);
if(Platform != ETargetPlatform::None)
{
result.AddUnique(Platform);
}
}
return result;
}
TArray<FDirectoryPath> CommandletHelper::ParserPatchFilters(const FString& Commandline,const FString& FilterName)
{
TArray<FDirectoryPath> Result;
for(auto& FilterPath:ParserPatchConfigByCommandline(Commandline,FString::Printf(TEXT("Add%s"),*FilterName)))
{
FDirectoryPath Path;
Path.Path = FilterPath;
Result.Add(Path);
}
return Result;
}
static bool IsRequestingExit()
{
#if ENGINE_MAJOR_VERSION > 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION > 23)
return IsEngineExitRequested();
#else
return GIsRequestingExit;
#endif
}
static void RequestEngineExit()
{
#if ENGINE_MAJOR_VERSION > 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION > 23)
RequestEngineExit(TEXT("GeneralCommandlet ComWrapperShutdownEvent"));
#else
GIsRequestingExit = true;
#endif
}
void CommandletHelper::MainTick(TFunction<bool()> IsRequestExit)
{
GIsRunning = true;
//@todo abstract properly or delete
#if PLATFORM_WINDOWS// Windows only
// Used by the .com wrapper to notify that the Ctrl-C handler was triggered.
// This shared event is checked each tick so that the log file can be cleanly flushed.
FEvent* ComWrapperShutdownEvent = FPlatformProcess::GetSynchEventFromPool(true);
#endif
// main loop
FDateTime LastConnectionTime = FDateTime::UtcNow();
while (GIsRunning &&
// !IsRequestingExit() &&
!IsRequestExit())
{
GEngine->UpdateTimeAndHandleMaxTickRate();
GEngine->Tick(FApp::GetDeltaTime(), false);
// update task graph
FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread);
// execute deferred commands
for (int32 DeferredCommandsIndex=0; DeferredCommandsIndex<GEngine->DeferredCommands.Num(); DeferredCommandsIndex++)
{
GEngine->Exec( GWorld, *GEngine->DeferredCommands[DeferredCommandsIndex], *GLog);
}
GEngine->DeferredCommands.Empty();
// flush log
GLog->FlushThreadedLogs();
#if PLATFORM_WINDOWS
if (ComWrapperShutdownEvent->Wait(0))
{
RequestEngineExit();
}
#endif
}
//@todo abstract properly or delete
#if PLATFORM_WINDOWS
FPlatformProcess::ReturnSynchEventToPool(ComWrapperShutdownEvent);
ComWrapperShutdownEvent = nullptr;
#endif
GIsRunning = false;
}
bool CommandletHelper::GetCommandletArg(const FString& Token,FString& OutValue)
{
OutValue.Empty();
FString Value;
bool bHasToken = FParse::Value(FCommandLine::Get(), *Token, Value);
if(bHasToken && !Value.IsEmpty())
{
OutValue = Value;
}
return bHasToken && !OutValue.IsEmpty();
}
bool CommandletHelper::IsCookCommandlet()
{
bool bIsCookCommandlet = false;
if(::IsRunningCommandlet())
{
FString CommandletName;
bool bIsCommandlet = CommandletHelper::GetCommandletArg(TEXT("-run="),CommandletName); //FParse::Value(FCommandLine::Get(), TEXT("-run="), CommandletName);
if(bIsCommandlet && !CommandletName.IsEmpty())
{
bIsCookCommandlet = CommandletName.Equals(TEXT("cook"),ESearchCase::IgnoreCase);
}
}
return bIsCookCommandlet;
}
TArray<ETargetPlatform> CommandletHelper::GetCookCommandletTargetPlatforms()
{
TArray<ETargetPlatform> TargetPlatforms;
{
FString PlatformName;
if(CommandletHelper::GetCommandletArg(TEXT("-TargetPlatform="),PlatformName))
{
ETargetPlatform TargetPlatform;
THotPatcherTemplateHelper::GetEnumValueByName(PlatformName,TargetPlatform);
TargetPlatforms.AddUnique(TargetPlatform);
}
}
return TargetPlatforms;
}
TArray<FString> CommandletHelper::GetCookCommandletTargetPlatformName()
{
TArray<FString> result;
TArray<ETargetPlatform> Platforms = CommandletHelper::GetCookCommandletTargetPlatforms();
for(const auto& Platform:Platforms)
{
result.AddUnique(THotPatcherTemplateHelper::GetEnumNameByValue(Platform));
}
return result;
}
void CommandletHelper::ModifyTargetPlatforms(const FString& InParams,const FString& InToken,TArray<ETargetPlatform>& OutTargetPlatforms,bool Replace)
{
TArray<ETargetPlatform> TargetPlatforms = CommandletHelper::ParserPlatforms(InParams,InToken);
if(TargetPlatforms.Num())
{
if(Replace){
OutTargetPlatforms = TargetPlatforms;
}else{
for(ETargetPlatform Platform:TargetPlatforms){ OutTargetPlatforms.AddUnique(Platform); }
}
}
};

View File

@ -0,0 +1,82 @@
#include "HotAssetScannerCommandlet.h"
#include "CommandletHelper.h"
// engine header
#include "CoreMinimal.h"
#include "FlibPatchParserHelper.h"
#include "Async/ParallelFor.h"
#include "Misc/FileHelper.h"
#include "Misc/CommandLine.h"
#include "Misc/Paths.h"
#include "BaseTypes/FPackageTracker.h"
DEFINE_LOG_CATEGORY(LogHotAssetScannerCommandlet);
int32 UHotAssetScannerCommandlet::Main(const FString& Params)
{
SCOPED_NAMED_EVENT_TEXT("UHotAssetScannerCommandlet::Main",FColor::Red);
Super::Main(Params);
UE_LOG(LogHotAssetScannerCommandlet, Display, TEXT("UHotAssetScannerCommandlet::Main"));
FString config_path;
bool bStatus = FParse::Value(*Params, *FString(PATCHER_CONFIG_PARAM_NAME).ToLower(), config_path);
if (!bStatus)
{
UE_LOG(LogHotAssetScannerCommandlet, Warning, TEXT("not -config=xxxx.json params."));
return -1;
}
if (bStatus && !FPaths::FileExists(config_path))
{
UE_LOG(LogHotAssetScannerCommandlet, Error, TEXT("cofnig file %s not exists."), *config_path);
return -1;
}
if(IsRunningCommandlet())
{
// load asset registry
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
AssetRegistryModule.Get().SearchAllAssets(true);
}
TSharedPtr<FAssetScanConfig> AssetScanConfig = MakeShareable(new FAssetScanConfig);
FString JsonContent;
if (FPaths::FileExists(config_path) && FFileHelper::LoadFileToString(JsonContent, *config_path))
{
THotPatcherTemplateHelper::TDeserializeJsonStringAsStruct(JsonContent,*AssetScanConfig);
}
TMap<FString, FString> KeyValues = THotPatcherTemplateHelper::GetCommandLineParamsMap(Params);
THotPatcherTemplateHelper::ReplaceProperty(*AssetScanConfig, KeyValues);
FString FinalConfig;
THotPatcherTemplateHelper::TSerializeStructAsJsonString(*AssetScanConfig,FinalConfig);
UE_LOG(LogHotAssetScannerCommandlet, Display, TEXT("%s"), *FinalConfig);
FHotPatcherVersion CurrentVersion;
{
CurrentVersion.VersionId = TEXT("HotAssetScanner");
CurrentVersion.Date = FDateTime::UtcNow().ToString();
CurrentVersion.BaseVersionId = TEXT("");
UFlibPatchParserHelper::RunAssetScanner(*AssetScanConfig,CurrentVersion);
}
FString SearchResult;
FString SaveSearchResultPath = FPaths::ConvertRelativePathToFull(FPaths::Combine(FPaths::ProjectSavedDir(),FString::Printf(TEXT("HotPatcher/MultiCooker/%s/SearchCookerAssets.json"),FApp::GetProjectName())));
if(THotPatcherTemplateHelper::TSerializeStructAsJsonString(CurrentVersion,SearchResult))
{
FFileHelper::SaveStringToFile(SearchResult,*SaveSearchResultPath);
}
UE_LOG(LogHotAssetScannerCommandlet,Display,TEXT("HotAssetScanner Misstion is Finished!"));
if(FParse::Param(FCommandLine::Get(), TEXT("wait")))
{
system("pause");
}
return 0;
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "HotPatcherCommandletBase.h"
#include "Commandlets/Commandlet.h"
#include "HotAssetScannerCommandlet.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogHotAssetScannerCommandlet, All, All);
UCLASS()
class UHotAssetScannerCommandlet :public UHotPatcherCommandletBase
{
GENERATED_BODY()
public:
virtual int32 Main(const FString& Params)override;
virtual FString GetCmdletName()const override{ return TEXT("AssetScannerCmdlet"); }
};

View File

@ -0,0 +1,72 @@
#include "HotCookerCommandlet.h"
#include "ThreadUtils/FProcWorkerThread.hpp"
#include "FCookerConfig.h"
#include "FlibPatchParserHelper.h"
#include "CommandletBase/CommandletHelper.h"
// / engine header
#include "CoreMinimal.h"
#include "HotPatcherCore.h"
#include "Misc/FileHelper.h"
#include "Misc/CommandLine.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Misc/Paths.h"
#define COOKER_CONFIG_PARAM_NAME TEXT("-config=")
DEFINE_LOG_CATEGORY(LogHotCookerCommandlet);
TSharedPtr<FProcWorkerThread> CookerProc;
int32 UHotCookerCommandlet::Main(const FString& Params)
{
Super::Main(Params);
UE_LOG(LogHotCookerCommandlet, Display, TEXT("UHotCookerCommandlet::Main"));
FString config_path;
bool bStatus = FParse::Value(*Params, *FString(COOKER_CONFIG_PARAM_NAME).ToLower(), config_path);
if (!bStatus)
{
UE_LOG(LogHotCookerCommandlet, Error, TEXT("not -config=xxxx.json params."));
return -1;
}
if (!FPaths::FileExists(config_path))
{
UE_LOG(LogHotCookerCommandlet, Error, TEXT("cofnig file %s not exists."), *config_path);
return -1;
}
FString JsonContent;
if (FFileHelper::LoadFileToString(JsonContent, *config_path))
{
UE_LOG(LogHotCookerCommandlet, Display, TEXT("%s"), *JsonContent);
FCookerConfig CookConfig;
THotPatcherTemplateHelper::TDeserializeJsonStringAsStruct(JsonContent,CookConfig);
TMap<FString, FString> KeyValues = THotPatcherTemplateHelper::GetCommandLineParamsMap(Params);
THotPatcherTemplateHelper::ReplaceProperty(CookConfig, KeyValues);
if (CookConfig.bCookAllMap)
{
CookConfig.CookMaps = UFlibPatchParserHelper::GetAvailableMaps(UKismetSystemLibrary::GetProjectDirectory(), ENABLE_COOK_ENGINE_MAP, ENABLE_COOK_PLUGIN_MAP, true);
}
FString CookCommand;
UFlibPatchParserHelper::GetCookProcCommandParams(CookConfig, CookCommand);
UE_LOG(LogHotCookerCommandlet, Display, TEXT("CookCommand:%s %s"), *CookConfig.EngineBin,*CookCommand);
if (FPaths::FileExists(CookConfig.EngineBin) && FPaths::FileExists(CookConfig.ProjectPath))
{
CookerProc = MakeShareable(new FProcWorkerThread(TEXT("CookThread"), CookConfig.EngineBin, CookCommand));
CookerProc->ProcOutputMsgDelegate.BindStatic(&::ReceiveOutputMsg);
CookerProc->Execute();
CookerProc->Join();
}
}
if(FParse::Param(FCommandLine::Get(), TEXT("wait")))
{
system("pause");
}
return 0;
}

View File

@ -0,0 +1,18 @@
#pragma once
#include "Commandlets/Commandlet.h"
#include "CommandletBase/HotPatcherCommandletBase.h"
#include "HotCookerCommandlet.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogHotCookerCommandlet, Log, All);
UCLASS()
class UHotCookerCommandlet :public UHotPatcherCommandletBase
{
GENERATED_BODY()
public:
virtual int32 Main(const FString& Params)override;
virtual FString GetCmdletName()const override{ return TEXT("OriginalCookerCmdlet"); }
};

View File

@ -0,0 +1,88 @@
#include "HotGlobalShaderCommandlet.h"
#include "CommandletHelper.h"
// engine header
#include "CoreMinimal.h"
#include "FlibHotPatcherCoreHelper.h"
#include "Async/ParallelFor.h"
#include "Cooker/MultiCooker/FlibHotCookerHelper.h"
#include "Kismet/KismetStringLibrary.h"
#include "Misc/FileHelper.h"
#include "Misc/CommandLine.h"
#include "Misc/Paths.h"
#include "ShaderLibUtils/FlibShaderCodeLibraryHelper.h"
#define PLATFORMS_PARAM_NAME TEXT("-platforms=")
#define SAVETO_PARAM_NAME TEXT("-saveto=")
DEFINE_LOG_CATEGORY(LogHotGlobalShaderCommandlet);
int32 UHotGlobalShaderCommandlet::Main(const FString& Params)
{
SCOPED_NAMED_EVENT_TEXT("UHotGlobalShaderCommandlet::Main",FColor::Red);
Super::Main(Params);
UE_LOG(LogHotGlobalShaderCommandlet, Display, TEXT("UHotGlobalShaderCommandlet::Main"));
FString PlatformNameStr;
bool bStatus = FParse::Value(*Params, *FString(PLATFORMS_PARAM_NAME).ToLower(), PlatformNameStr);
if (!bStatus)
{
UE_LOG(LogHotGlobalShaderCommandlet, Warning, TEXT("not -platforms=Android_ASTC+IOS params."));
return -1;
}
FString SaveShaderToDir;
if (!FParse::Value(*Params, *FString(SAVETO_PARAM_NAME).ToLower(), SaveShaderToDir))
{
SaveShaderToDir = FPaths::ConvertRelativePathToFull(FPaths::Combine(FPaths::ProjectSavedDir(),TEXT("Cooked")));
}
TArray<FString> PlatformNames = UKismetStringLibrary::ParseIntoArray(PlatformNameStr,TEXT("+"),false);
TArray<ETargetPlatform> Platforms;
for(const auto& PlatformName:PlatformNames)
{
ETargetPlatform Platform;
if(THotPatcherTemplateHelper::GetEnumValueByName(PlatformName,Platform))
{
Platforms.AddUnique(Platform);
}
}
UE_LOG(LogHotGlobalShaderCommandlet,Display,TEXT("Compile Global Shader for %s, save to %s"),*PlatformNameStr,*SaveShaderToDir);
{
SCOPED_NAMED_EVENT_TEXT("Compile Global Shader",FColor::Red);
FString Name = TEXT("Global");
auto GlobalShaderCollectionProxy = UFlibHotCookerHelper::CreateCookShaderCollectionProxyByPlatform(
Name,
Platforms,
true,
true,
true,
SaveShaderToDir,
true
);
if(GlobalShaderCollectionProxy.IsValid())
{
GlobalShaderCollectionProxy->Init();
}
// compile Global Shader
UFlibHotPatcherCoreHelper::SaveGlobalShaderMapFiles(UFlibHotPatcherCoreHelper::GetTargetPlatformsByNames(Platforms),SaveShaderToDir);
UFlibShaderCodeLibraryHelper::WaitShaderCompilingComplete();
UFlibHotPatcherCoreHelper::WaitForAsyncFileWrites();
if(GlobalShaderCollectionProxy.IsValid())
{
GlobalShaderCollectionProxy->Shutdown();
}
}
UE_LOG(LogHotGlobalShaderCommandlet,Display,TEXT("HotGlobalShader Misstion is Finished!"));
if(FParse::Param(FCommandLine::Get(), TEXT("wait")))
{
system("pause");
}
return 0;
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "HotPatcherCommandletBase.h"
#include "Commandlets/Commandlet.h"
#include "HotGlobalShaderCommandlet.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogHotGlobalShaderCommandlet, All, All);
UCLASS()
class UHotGlobalShaderCommandlet :public UHotPatcherCommandletBase
{
GENERATED_BODY()
public:
virtual int32 Main(const FString& Params)override;
virtual FString GetCmdletName()const override{ return TEXT("GlobalShaderCmdlet"); }
};

View File

@ -0,0 +1,74 @@
#include "HotSingleCookerCommandlet.h"
#include "CommandletBase/CommandletHelper.h"
// engine header
#include "CoreMinimal.h"
#include "Cooker/MultiCooker/FSingleCookerSettings.h"
#include "Cooker/MultiCooker/SingleCookerProxy.h"
#include "HAL/ExceptionHandling.h"
#include "Misc/FileHelper.h"
#include "Misc/CommandLine.h"
#include "Misc/Paths.h"
DEFINE_LOG_CATEGORY(LogHotSingleCookerCommandlet);
int32 UHotSingleCookerCommandlet::Main(const FString& Params)
{
Super::Main(Params);
SCOPED_NAMED_EVENT_TEXT("UHotSingleCookerCommandlet::Main",FColor::Red);
UE_LOG(LogHotSingleCookerCommandlet, Display, TEXT("UHotSingleCookerCommandlet::Main"));
if(CmdConfigPath.IsEmpty())
{
return -1;
}
ExportSingleCookerSetting = MakeShareable(new FSingleCookerSettings);
FString JsonContent;
if (FPaths::FileExists(CmdConfigPath) && FFileHelper::LoadFileToString(JsonContent, *CmdConfigPath))
{
THotPatcherTemplateHelper::TDeserializeJsonStringAsStruct(JsonContent,*ExportSingleCookerSetting);
}
TMap<FString, FString> KeyValues = THotPatcherTemplateHelper::GetCommandLineParamsMap(Params);
THotPatcherTemplateHelper::ReplaceProperty(*ExportSingleCookerSetting, KeyValues);
CommandletHelper::ModifyTargetPlatforms(Params,TARGET_PLATFORMS_OVERRIDE,ExportSingleCookerSetting->CookTargetPlatforms,true);
if(ExportSingleCookerSetting->bDisplayConfig)
{
FString FinalConfig;
THotPatcherTemplateHelper::TSerializeStructAsJsonString(*ExportSingleCookerSetting,FinalConfig);
}
// if(IsRunningCommandlet() && ExportSingleCookerSetting->bPackageTracker && ExportSingleCookerSetting->bCookPackageTrackerAssets)
// {
// SCOPED_NAMED_EVENT_TEXT("SearchAllAssets",FColor::Red);
// // load asset registry
// FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
// AssetRegistryModule.Get().SearchAllAssets(true);
// }
UE_LOG(LogHotSingleCookerCommandlet, Display, TEXT("Cooker %s Id %d,Assets Num %d"), *ExportSingleCookerSetting->MissionName,ExportSingleCookerSetting->MissionID,ExportSingleCookerSetting->CookAssets.Num());
GAlwaysReportCrash = false;
USingleCookerProxy* SingleCookerProxy = NewObject<USingleCookerProxy>();
SingleCookerProxy->AddToRoot();
SingleCookerProxy->Init(ExportSingleCookerSetting.Get());
bool bExportStatus = SingleCookerProxy->DoExport();
CommandletHelper::MainTick([SingleCookerProxy]()->bool
{
return SingleCookerProxy->IsFinsihed();
});
SingleCookerProxy->Shutdown();
UE_LOG(LogHotSingleCookerCommandlet,Display,TEXT("Single Cook Misstion %s %d is %s!"),*ExportSingleCookerSetting->MissionName,ExportSingleCookerSetting->MissionID,bExportStatus?TEXT("Successed"):TEXT("Failure"));
if(FParse::Param(FCommandLine::Get(), TEXT("wait")))
{
system("pause");
}
return bExportStatus ? 0 : -1;
}

View File

@ -0,0 +1,29 @@
#pragma once
#include "Commandlets/Commandlet.h"
#include "CommandletBase/HotPatcherCommandletBase.h"
#include "Cooker/MultiCooker/FSingleCookerSettings.h"
#include "HotSingleCookerCommandlet.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogHotSingleCookerCommandlet, All, All);
UCLASS()
class UHotSingleCookerCommandlet :public UHotPatcherCommandletBase
{
GENERATED_BODY()
public:
virtual int32 Main(const FString& Params)override;
virtual FString GetCmdletName()const override
{
FString Result = TEXT("SingleCookerCmdlet");
if(ExportSingleCookerSetting.IsValid())
{
Result = FString::Printf(TEXT("%s_%s"),*Result,*ExportSingleCookerSetting.Get()->MissionName);
}
return Result;
}
protected:
TSharedPtr<FSingleCookerSettings> ExportSingleCookerSetting;
};

View File

@ -0,0 +1,84 @@
#include "HotPatcherCommandlet.h"
// #include "CreatePatch/FExportPatchSettingsEx.h"
#include "CreatePatch/PatcherProxy.h"
#include "CommandletHelper.h"
// engine header
#include "CoreMinimal.h"
#include "FlibHotPatcherCoreHelper.h"
#include "Misc/FileHelper.h"
#include "Misc/CommandLine.h"
#include "Misc/Paths.h"
DEFINE_LOG_CATEGORY(LogHotPatcherCommandlet);
int32 UHotPatcherCommandlet::Main(const FString& Params)
{
Super::Main(Params);
FCommandLine::Append(TEXT(" -buildmachine"));
GIsBuildMachine = true;
UE_LOG(LogHotPatcherCommandlet, Display, TEXT("UHotPatcherCommandlet::Main"));
FString config_path;
bool bStatus = FParse::Value(*Params, *FString(PATCHER_CONFIG_PARAM_NAME).ToLower(), config_path);
if (!bStatus)
{
UE_LOG(LogHotPatcherCommandlet, Error, TEXT("not -config=xxxx.json params."));
return -1;
}
config_path = UFlibPatchParserHelper::ReplaceMark(config_path);
if (!FPaths::FileExists(config_path))
{
UE_LOG(LogHotPatcherCommandlet, Error, TEXT("cofnig file %s not exists."), *config_path);
return -1;
}
FString JsonContent;
bool bExportStatus = false;
if (FFileHelper::LoadFileToString(JsonContent, *config_path))
{
TSharedPtr<FExportPatchSettings> ExportPatchSetting = MakeShareable(new FExportPatchSettings);
THotPatcherTemplateHelper::TDeserializeJsonStringAsStruct(JsonContent,*ExportPatchSetting);
// adaptor old version config
UFlibHotPatcherCoreHelper::AdaptorOldVersionConfig(ExportPatchSetting->GetAssetScanConfigRef(),JsonContent);
TMap<FString, FString> KeyValues = THotPatcherTemplateHelper::GetCommandLineParamsMap(Params);
THotPatcherTemplateHelper::ReplaceProperty(*ExportPatchSetting, KeyValues);
TArray<ETargetPlatform> AddPlatforms = CommandletHelper::ParserPlatforms(Params,ADD_PATCH_PLATFORMS);
if(AddPlatforms.Num())
{
for(auto& Platform:AddPlatforms)
{
ExportPatchSetting->PakTargetPlatforms.AddUnique(Platform);
}
}
CommandletHelper::ModifyTargetPlatforms(Params,TARGET_PLATFORMS_OVERRIDE,ExportPatchSetting->PakTargetPlatforms,true);
ExportPatchSetting->GetAssetScanConfigRef().AssetIncludeFilters.Append(CommandletHelper::ParserPatchFilters(Params,TEXT("AssetIncludeFilters")));
ExportPatchSetting->GetAssetScanConfigRef().AssetIgnoreFilters.Append(CommandletHelper::ParserPatchFilters(Params,TEXT("AssetIgnoreFilters")));
FString FinalConfig;
THotPatcherTemplateHelper::TSerializeStructAsJsonString(*ExportPatchSetting,FinalConfig);
// UE_LOG(LogHotPatcherCommandlet, Display, TEXT("%s"), *FinalConfig);
UPatcherProxy* PatcherProxy = NewObject<UPatcherProxy>();
PatcherProxy->AddToRoot();
PatcherProxy->Init(ExportPatchSetting.Get());
PatcherProxy->OnPaking.AddStatic(&::CommandletHelper::ReceiveMsg);
PatcherProxy->OnShowMsg.AddStatic(&::CommandletHelper::ReceiveShowMsg);
bExportStatus = PatcherProxy->DoExport();
UE_LOG(LogHotPatcherCommandlet,Display,TEXT("Export Patch Misstion is %s, return %d!"),bExportStatus?TEXT("Successed"):TEXT("Failure"),(int32)!bExportStatus);
}
if(FParse::Param(FCommandLine::Get(), TEXT("wait")))
{
system("pause");
}
return (int32)!bExportStatus;
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "HotPatcherCommandletBase.h"
#include "Commandlets/Commandlet.h"
#include "HotPatcherCommandlet.generated.h"
// DECLARE_LOG_CATEGORY_EXTERN(LogHotPatcherCommandlet, All, All);
UCLASS()
class UHotPatcherCommandlet :public UHotPatcherCommandletBase
{
GENERATED_BODY()
public:
virtual int32 Main(const FString& Params)override;
};

View File

@ -0,0 +1,129 @@
#include "HotPatcherCommandletBase.h"
#include "CreatePatch/FExportReleaseSettings.h"
#include "CreatePatch/ReleaseProxy.h"
#include "CommandletHelper.h"
// engine header
#include "CoreMinimal.h"
#include "HotPatcherCore.h"
#include "Misc/FileHelper.h"
#include "Misc/CommandLine.h"
#include "Misc/Paths.h"
#if PLATFORM_WINDOWS && WITH_EDITOR
#include "Windows/AllowWindowsPlatformTypes.h"
#include "Windows.h"
#include "Windows/HideWindowsPlatformTypes.h"
#endif
DEFINE_LOG_CATEGORY(LogHotPatcherCommandletBase);
static const bool bNoDDC = FParse::Param(FCommandLine::Get(), TEXT("NoDDC"));
struct FObjectTrackerTagCleaner:public FPackageTrackerBase
{
FObjectTrackerTagCleaner(UHotPatcherCommandletBase* InCommandletIns):CommandletIns(InCommandletIns){}
virtual void NotifyUObjectCreated(const UObjectBase* Object, int32 Index) override
{
auto ObjectIns = const_cast<UObject*>(static_cast<const UObject*>(Object));
if((ObjectIns && bNoDDC) || CommandletIns->IsSkipObject(ObjectIns))
{
ObjectIns->ClearFlags(RF_NeedPostLoad);
ObjectIns->ClearFlags(RF_NeedPostLoadSubobjects);
}
}
protected:
UHotPatcherCommandletBase* CommandletIns;
};
FString UHotPatcherCommandletBase::GetCrashDir()
{
return FPaths::ConvertRelativePathToFull(FPaths::Combine(FPaths::ProjectDir(),TEXT("Saved/HotPatcher/Crashs")));
}
void UHotPatcherCommandletBase::CleanCrashDir()
{
if(FPaths::DirectoryExists(GetCrashDir()))
{
IFileManager::Get().DeleteDirectory(*GetCrashDir(),false,true);
}
}
void UHotPatcherCommandletBase::OnHandleSystemError()
{
FString CrashTagFile = FPaths::Combine(GetCrashDir(),FString::Printf(TEXT("%s.txt"),*GetCmdletName()));
bool bSaveCrashTag = FFileHelper::SaveStringToFile(GetCmdletName(),*CrashTagFile);
UE_LOG(LogHotPatcherCommandletBase,Display,TEXT("%s Catch SystemError,Save CrashTag to %s(%s)"),*GetCmdletName(),*CrashTagFile,bSaveCrashTag ? TEXT("TRUE"):TEXT("FALSE"));
}
void UHotPatcherCommandletBase::Update(const FString& Params)
{
FString CommandletName;
bool bIsCommandlet = FParse::Value(FCommandLine::Get(), TEXT("-run="), CommandletName);
if(GetDefault<UHotPatcherSettings>()->bServerlessCounterInCmdlet)
{
Counter = MakeShareable(new FCountServerlessWrapper);
FServerRequestInfo RequestInfo = FCountServerlessWrapper::MakeServerRequestInfo();
auto ProjectInfo = FCountServerlessWrapper::MakeCurrentProject();
ProjectInfo.PluginVersion = FString::Printf(TEXT("%d.%d"),GToolMainVersion,GToolPatchVersion);
if(bIsCommandlet)
{
ProjectInfo.ProjectName = FString::Printf(TEXT("%s_%s"),*ProjectInfo.ProjectName,*CommandletName);
}
Counter->Init(RequestInfo,ProjectInfo);
Counter->Processor();
}
}
void UHotPatcherCommandletBase::MaybeMarkPackageAsAlreadyLoaded(UPackage* Package)
{
if (bNoDDC || IsSkipPackage(Package))
{
Package->SetPackageFlags(PKG_ReloadingForCooker);
Package->SetPackageFlags(PKG_FilterEditorOnly);
Package->SetPackageFlags(PKG_EditorOnly);
}
}
int32 UHotPatcherCommandletBase::Main(const FString& Params)
{
Update(Params);
#if SUPPORT_NO_DDC
// for Object Create Tracking,Optimize Asset searching, dont execute UObject::PostLoad
ObjectTrackerTagCleaner = MakeShareable(new FObjectTrackerTagCleaner(this));
FCoreUObjectDelegates::PackageCreatedForLoad.AddUObject(this,&UHotPatcherCommandletBase::MaybeMarkPackageAsAlreadyLoaded);
#endif
#if PLATFORM_WINDOWS && WITH_EDITOR
{
SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
UE_LOG(LogHotPatcher,Display,TEXT("Set Commandlet Priority to REALTIME_PRIORITY_CLASS."));
}
#endif
bool bStatus = FParse::Value(*Params, *FString(PATCHER_CONFIG_PARAM_NAME).ToLower(), CmdConfigPath);
if (!bStatus)
{
UE_LOG(LogHotPatcherCommandletBase, Warning, TEXT("not -config=xxxx.json params."));
return -1;
}
CmdConfigPath = UFlibPatchParserHelper::ReplaceMark(CmdConfigPath);
if (bStatus && !FPaths::FileExists(CmdConfigPath))
{
UE_LOG(LogHotPatcherCommandletBase, Error, TEXT("cofnig file %s not exists."), *CmdConfigPath);
return -1;
}
FCoreDelegates::OnHandleSystemError.AddLambda([this](){ this->OnHandleSystemError(); });
if(IsRunningCommandlet() && !FParse::Param(FCommandLine::Get(), TEXT("NoSearchAllAssets")))
{
SCOPED_NAMED_EVENT_TEXT("SearchAllAssets",FColor::Red);
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
AssetRegistryModule.Get().SearchAllAssets(true);
}
return 0;
}

View File

@ -0,0 +1,140 @@
#include "HotReleaseCommandlet.h"
#include "CreatePatch/FExportReleaseSettings.h"
#include "CreatePatch/ReleaseProxy.h"
#include "CommandletHelper.h"
#include "FlibHotPatcherCoreHelper.h"
// engine header
#include "CoreMinimal.h"
#include "Misc/FileHelper.h"
#include "Misc/CommandLine.h"
#include "Misc/Paths.h"
DEFINE_LOG_CATEGORY(LogHotReleaseCommandlet);
TArray<FPlatformPakListFiles> ParserPlatformFilesByCmd(const FString& Commandline,const FString& Key,TFunction<void(FPlatformPakListFiles&,const FFilePath& File)> FileCallback)
{
TArray<FPlatformPakListFiles> result;
TMap<FString, FString> KeyValues = THotPatcherTemplateHelper::GetCommandLineParamsMap(Commandline);
if(KeyValues.Find(Key))
{
FString AddPakListInfo = *KeyValues.Find(Key);
TArray<FString> PlatformPakLists;
AddPakListInfo.ParseIntoArray(PlatformPakLists,TEXT(","));
for(auto& PlatformPakList:PlatformPakLists)
{
TArray<FString> PlatformPakListToken;
PlatformPakList.ParseIntoArray(PlatformPakListToken,TEXT("+"));
if(PlatformPakListToken.Num() >= 2)
{
FString PlatformName = PlatformPakListToken[0];
FPlatformPakListFiles PlatformPakListItem;
THotPatcherTemplateHelper::GetEnumValueByName(PlatformName,PlatformPakListItem.TargetPlatform);
for(int32 index=1;index<PlatformPakListToken.Num();++index)
{
FString PakListPath = PlatformPakListToken[index];
if(FPaths::FileExists(PakListPath))
{
FFilePath PakFilePath;
PakFilePath.FilePath = PakListPath;
FileCallback(PlatformPakListItem,PakFilePath);
}
}
result.Add(PlatformPakListItem);
}
}
}
return result;
}
#define ADD_PLATFORM_PAK_LIST TEXT("AddPlatformPakList")
#define ADD_PLATFORM_PAK_FILES TEXT("AddPlatformPakFiles")
TArray<FPlatformPakListFiles> ParserPlatformPakList(const FString& Commandline)
{
TArray<FPlatformPakListFiles> result;
result.Append(ParserPlatformFilesByCmd(Commandline,ADD_PLATFORM_PAK_LIST,[](FPlatformPakListFiles& PlatformPakListItem,const FFilePath& File)
{
PlatformPakListItem.PakResponseFiles.Add(File);
}));
result.Append(ParserPlatformFilesByCmd(Commandline,ADD_PLATFORM_PAK_FILES,[](FPlatformPakListFiles& PlatformPakListItem,const FFilePath& File)
{
PlatformPakListItem.PakFiles.Add(File);
}));
return result;
}
int32 UHotReleaseCommandlet::Main(const FString& Params)
{
Super::Main(Params);
UE_LOG(LogHotReleaseCommandlet, Display, TEXT("UHotReleaseCommandlet::Main:%s"), *Params);
FString config_path;
bool bStatus = FParse::Value(*Params, *FString(PATCHER_CONFIG_PARAM_NAME).ToLower(), config_path);
if (!bStatus)
{
UE_LOG(LogHotReleaseCommandlet, Warning, TEXT("not -config=xxxx.json params."));
// return -1;
}
config_path = UFlibPatchParserHelper::ReplaceMark(config_path);
if (bStatus && !FPaths::FileExists(config_path))
{
UE_LOG(LogHotReleaseCommandlet, Error, TEXT("config file %s not exists."), *config_path);
return -1;
}
if(IsRunningCommandlet())
{
// load asset registry
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
AssetRegistryModule.Get().SearchAllAssets(true);
}
TSharedPtr<FExportReleaseSettings> ExportReleaseSetting = MakeShareable(new FExportReleaseSettings);
FString JsonContent;
if (FPaths::FileExists(config_path) && FFileHelper::LoadFileToString(JsonContent, *config_path))
{
THotPatcherTemplateHelper::TDeserializeJsonStringAsStruct(JsonContent,*ExportReleaseSetting);
// adaptor old version config
UFlibHotPatcherCoreHelper::AdaptorOldVersionConfig(ExportReleaseSetting->GetAssetScanConfigRef(),JsonContent);
}
TMap<FString, FString> KeyValues = THotPatcherTemplateHelper::GetCommandLineParamsMap(Params);
THotPatcherTemplateHelper::ReplaceProperty(*ExportReleaseSetting, KeyValues);
// 从命令行分析PlatformPakList
TArray<FPlatformPakListFiles> ReadPakList = ParserPlatformPakList(Params);
if(ReadPakList.Num())
{
ExportReleaseSetting->PlatformsPakListFiles = ReadPakList;
}
if(ReadPakList.Num() && ExportReleaseSetting->IsBackupMetadata())
{
for(const auto& PlatformPakList:ReadPakList)
{
ExportReleaseSetting->BackupMetadataPlatforms.AddUnique(PlatformPakList.TargetPlatform);
}
}
FString FinalConfig;
THotPatcherTemplateHelper::TSerializeStructAsJsonString(*ExportReleaseSetting,FinalConfig);
UE_LOG(LogHotReleaseCommandlet, Display, TEXT("%s"), *FinalConfig);
UReleaseProxy* ReleaseProxy = NewObject<UReleaseProxy>();
ReleaseProxy->AddToRoot();
ReleaseProxy->Init(ExportReleaseSetting.Get());
ReleaseProxy->OnPaking.AddStatic(&::CommandletHelper::ReceiveMsg);
ReleaseProxy->OnShowMsg.AddStatic(&::CommandletHelper::ReceiveShowMsg);
bool bExportStatus = ReleaseProxy->DoExport();
UE_LOG(LogHotReleaseCommandlet,Display,TEXT("Export Release Misstion is %s!"),bExportStatus?TEXT("Successed"):TEXT("Failure"));
if(FParse::Param(FCommandLine::Get(), TEXT("wait")))
{
system("pause");
}
return (int32)!bExportStatus;
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "HotPatcherCommandletBase.h"
#include "Commandlets/Commandlet.h"
#include "HotReleaseCommandlet.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogHotReleaseCommandlet, All, All);
UCLASS()
class UHotReleaseCommandlet :public UHotPatcherCommandletBase
{
GENERATED_BODY()
public:
virtual int32 Main(const FString& Params)override;
};

View File

@ -0,0 +1,198 @@
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System;
using System.Collections.Generic;
using System.IO;
public class HotPatcherCore : ModuleRules
{
public HotPatcherCore(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
bLegacyPublicIncludePaths = false;
OptimizeCode = CodeOptimization.InShippingBuildsOnly;
PublicIncludePaths.AddRange(
new string[] {
Path.Combine(ModuleDirectory,"Public/CommandletBase")
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"UnrealEd",
"UMG",
"UMGEditor",
"Core",
"Json",
"LevelSequence",
"ContentBrowser",
"SandboxFile",
"JsonUtilities",
"TargetPlatform",
"DesktopPlatform",
"Projects",
"Settings",
"HTTP",
"RHI",
"EngineSettings",
"AssetRegistry",
"PakFileUtilities",
"HotPatcherRuntime",
"BinariesPatchFeature"
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"DesktopPlatform",
"InputCore",
"CoreUObject",
"Engine",
"Sockets",
"DerivedDataCache"
// ... add private dependencies that you statically link with here ...
}
);
if (Target.Version.MajorVersion > 4 || Target.Version.MinorVersion > 21)
{
PrivateDependencyModuleNames.Add("RenderCore");
}
else
{
PrivateDependencyModuleNames.Add("ShaderCore");
}
if (Target.Version.MajorVersion > 4 || Target.Version.MinorVersion > 23)
{
PublicDependencyModuleNames.AddRange(new string[]{
"TraceLog"
});
}
// // only in UE5
if (Target.Version.MajorVersion > 4)
{
PublicDependencyModuleNames.AddRange(new string[]
{
"DeveloperToolSettings"
});
}
switch (Target.Configuration)
{
case UnrealTargetConfiguration.Debug:
{
PublicDefinitions.Add("COMPILER_CONFIGURATION_NAME=\"Debug\"");
break;
}
case UnrealTargetConfiguration.DebugGame:
{
PublicDefinitions.Add("COMPILER_CONFIGURATION_NAME=\"DebugGame\"");
break;
}
case UnrealTargetConfiguration.Development:
{
PublicDefinitions.Add("COMPILER_CONFIGURATION_NAME=\"Development\"");
break;
}
default:
{
PublicDefinitions.Add("COMPILER_CONFIGURATION_NAME=\"\"");
break;
}
};
System.Func<string, bool,bool> AddPublicDefinitions = (string MacroName,bool bEnable) =>
{
PublicDefinitions.Add(string.Format("{0}={1}",MacroName, bEnable ? 1 : 0));
return true;
};
bool bIOStoreSupport = Target.Version.MajorVersion > 4 || Target.Version.MinorVersion > 25;
if (bIOStoreSupport)
{
PublicDependencyModuleNames.AddRange(new string[]
{
"IoStoreUtilities"
});
}
AddPublicDefinitions("WITH_IO_STORE_SUPPORT", bIOStoreSupport);
AddPublicDefinitions("ENABLE_COOK_LOG", true);
AddPublicDefinitions("ENABLE_COOK_ENGINE_MAP", false);
AddPublicDefinitions("ENABLE_COOK_PLUGIN_MAP", false);
BuildVersion Version;
BuildVersion.TryRead(BuildVersion.GetDefaultFileName(), out Version);
AddPublicDefinitions("WITH_EDITOR_SECTION", Version.MajorVersion > 4 || Version.MinorVersion > 24);
System.Console.WriteLine("MajorVersion {0} MinorVersion: {1} PatchVersion {2}",Target.Version.MajorVersion,Target.Version.MinorVersion,Target.Version.PatchVersion);
// !!! Please make sure to modify the engine if necessary, otherwise it will cause a crash
AddPublicDefinitions("SUPPORT_NO_DDC", true);
// Game feature
bool bEnableGameFeature = true;
if (bEnableGameFeature || (Target.Version.MajorVersion > 4 || Target.Version.MinorVersion > 26))
{
PublicDependencyModuleNames.AddRange(new string[]
{
// "GameFeatures",
// "ModularGameplay",
});
}
bool bEnablePackageContext = true;
AddPublicDefinitions("WITH_PACKAGE_CONTEXT", (Version.MajorVersion > 4 || Version.MinorVersion > 23) && bEnablePackageContext);
if (Version.MajorVersion > 4 || Version.MinorVersion > 26)
{
PublicDependencyModuleNames.AddRange(new string[]
{
"IoStoreUtilities",
// "UnrealEd"
});
}
bool bGenerateChunksManifest = false;
AddPublicDefinitions("GENERATE_CHUNKS_MANIFEST", bGenerateChunksManifest);
if (bGenerateChunksManifest)
{
PublicIncludePaths.AddRange(new string[]
{
Path.Combine(EngineDirectory,"Source/Editor/UnrealEd/Public"),
Path.Combine(EngineDirectory,"Source/Editor/UnrealEd/Private"),
});
}
if (Version.MajorVersion > 4)
{
PublicIncludePaths.AddRange(new List<string>()
{
// Path.Combine(EngineDirectory,"Source/Developer/IoStoreUtilities/Internal"),
// Path.Combine(EngineDirectory,"Source/Editor/UnrealEd/Private/Cooker"),
// Path.Combine(EngineDirectory,"Source/Editor/UnrealEd/Private"),
Path.Combine(EngineDirectory,"Source/Runtime/CoreUObject/Internal/Serialization"),
});
}
PublicDefinitions.AddRange(new string[]
{
"TOOL_NAME=\"HotPatcher\"",
"CURRENT_VERSION_ID=81",
"CURRENT_PATCH_ID=0",
"REMOTE_VERSION_FILE=\"https://imzlp.com/opensource/version.json\""
});
}
}

View File

@ -0,0 +1,31 @@
#include "Cooker/MultiCooker/FSingleCookerSettings.h"
#include "FlibPatchParserHelper.h"
FSingleCookerSettings::FSingleCookerSettings()
{
OverrideNumberOfAssetsPerFrame.Add(UWorld::StaticClass(),2);
}
FString FSingleCookerSettings::GetStorageCookedAbsDir() const
{
return UFlibPatchParserHelper::ReplaceMark(StorageCookedDir);
}
FString FSingleCookerSettings::GetStorageMetadataAbsDir() const
{
return UFlibPatchParserHelper::ReplaceMark(StorageMetadataDir);
}
bool FSingleCookerSettings::IsSkipAsset(const FString& PackageName)
{
bool bRet = false;
for(const auto& SkipCookContent:SkipCookContents)
{
if(PackageName.StartsWith(SkipCookContent))
{
bRet = true;
break;
}
}
return bRet;
}

View File

@ -0,0 +1,114 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Cooker/MultiCooker/FlibHotCookerHelper.h"
#include "FlibHotPatcherCoreHelper.h"
#include "FlibPatchParserHelper.h"
#include "Cooker/MultiCooker/FCookShaderCollectionProxy.h"
#include "Interfaces/IPluginManager.h"
#include "ShaderLibUtils/FlibShaderCodeLibraryHelper.h"
// FString UFlibHotCookerHelper::GetMultiCookerBaseDir()
// {
// FString SaveConfigTo = FPaths::ConvertRelativePathToFull(
// FPaths::Combine(
// FPaths::ProjectSavedDir(),
// TEXT("HotPatcher/MultiCooker"),
// FApp::GetProjectName()
// ));
// return SaveConfigTo;
// }
// FString UFlibHotCookerHelper::GetCookerProcConfigPath(const FString& MissionName, int32 MissionID)
// {
// FString SaveConfigTo = FPaths::Combine(
// UFlibHotCookerHelper::GetMultiCookerBaseDir(),
// // FString::Printf(TEXT("%s_Cooker_%d.json"),*MissionName,MissionID)
// FString::Printf(TEXT("%s.json"),*MissionName)
// );
// return SaveConfigTo;
// }
FString UFlibHotCookerHelper::GetCookedDir()
{
return FPaths::Combine(
TEXT("[PROJECTDIR]"),
TEXT("Saved"),TEXT("Cooked"));
}
FString UFlibHotCookerHelper::GetCookerBaseDir()
{
FString SaveConfigTo = FPaths::Combine(
TEXT("[PROJECTDIR]"),
TEXT("Saved"),
TEXT("HotPatcher/MultiCooker"),
FApp::GetProjectName()
);
return SaveConfigTo;
}
FString UFlibHotCookerHelper::GetCookerProcFailedResultPath(const FString& BaseDir,const FString& MissionName, int32 MissionID)
{
FString SaveConfigTo = FPaths::Combine(
BaseDir,
FString::Printf(TEXT("%s_FailedAssets.json"),*MissionName)
);
return SaveConfigTo;
}
FString UFlibHotCookerHelper::GetProfilingCmd()
{
return FString::Printf(TEXT("-tracehost=127.0.0.1 -trace=cpu,memory,loadtime -statnamedevents implies -llm"));
}
TSharedPtr<FCookShaderCollectionProxy> UFlibHotCookerHelper::CreateCookShaderCollectionProxyByPlatform(
const FString& ShaderLibraryName,
TArray<ETargetPlatform> Platforms,
bool bShareShader,
bool bNativeShader,
bool bMaster,
const FString& InSavePath,
bool bCleanSavePath)
{
SCOPED_NAMED_EVENT_TEXT("UFlibHotCookerHelper::CreateCookShaderCollectionProxyByPlatform",FColor::Red);
TSharedPtr<FCookShaderCollectionProxy> CookShaderCollection;
TArray<FString> PlatformNames;
for(const auto& Platform:Platforms)
{
PlatformNames.AddUnique(THotPatcherTemplateHelper::GetEnumNameByValue(Platform));
}
FString ActualLibraryName = UFlibShaderCodeLibraryHelper::GenerateShaderCodeLibraryName(FApp::GetProjectName(),false);
if(bCleanSavePath)
{
UFlibHotPatcherCoreHelper::DeleteDirectory(InSavePath);
}
CookShaderCollection = MakeShareable(
new FCookShaderCollectionProxy(
PlatformNames,
ShaderLibraryName,
bShareShader,
bNativeShader,
bMaster,
InSavePath
));
return CookShaderCollection;
}
bool UFlibHotCookerHelper::IsAppleMetalPlatform(ITargetPlatform* TargetPlatform)
{
SCOPED_NAMED_EVENT_TEXT("IsAppleMetalPlatform",FColor::Red);
bool bIsMatched = false;
TArray<FString> ApplePlatforms = {TEXT("IOS"),TEXT("Mac"),TEXT("TVOS")};
for(const auto& Platform:ApplePlatforms)
{
if(TargetPlatform->PlatformName().StartsWith(Platform,ESearchCase::IgnoreCase))
{
bIsMatched = true;
break;
}
}
return bIsMatched;
}

View File

@ -0,0 +1,859 @@
#include "Cooker/MultiCooker/SingleCookerProxy.h"
#include "FlibHotPatcherCoreHelper.h"
#include "HotPatcherCore.h"
#include "HotPatcherRuntime.h"
#include "Cooker/MultiCooker/FlibHotCookerHelper.h"
#include "ShaderCompiler.h"
#include "UObject/UObjectHash.h"
#include "UObject/UObjectIterator.h"
#include "Async/ParallelFor.h"
#include "ShaderLibUtils/FlibShaderCodeLibraryHelper.h"
#include "ThreadUtils/FThreadUtils.hpp"
#include "Cooker/MultiCooker/FCookShaderCollectionProxy.h"
#include "Engine/Engine.h"
#include "Misc/ScopeExit.h"
#include "Engine/AssetManager.h"
#include "Interfaces/ITargetPlatform.h"
#if WITH_PACKAGE_CONTEXT
// // engine header
#include "UObject/SavePackage.h"
#endif
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION > 25
#include "Serialization/BulkDataManifest.h"
#endif
bool IsAlwayPostLoadClasses(UPackage* Package, UObject* Object)
{
return true;
}
void FFreezePackageTracker::NotifyUObjectCreated(const UObjectBase* Object, int32 Index)
{
// UObject* ObjectOuter = Object->GetOuter();
auto ObjectOuter = const_cast<UObject*>(static_cast<const UObject*>(Object));
UPackage* Package = ObjectOuter ? ObjectOuter->GetOutermost() : nullptr;
if(Package)
{
FName AssetPathName = FName(*Package->GetPathName());
if(!CookerAssetsSet.Contains(AssetPathName) && AllAssetsSet.Contains(AssetPathName) && !IsAlwayPostLoadClasses(Package, ObjectOuter))
{
PackageObjectsMap.FindOrAdd(AssetPathName).Add(ObjectOuter);
FreezeObjects.Add(ObjectOuter);
ObjectOuter->ClearFlags(RF_NeedPostLoad);
ObjectOuter->ClearFlags(RF_NeedPostLoadSubobjects);
}
}
}
#define NO_POSTLOAD_CACHE_DDC_OPTION TEXT("-NoPostLoadCacheDDC")
void USingleCookerProxy::Init(FPatcherEntitySettingBase* InSetting)
{
SCOPED_NAMED_EVENT_TEXT("Init",FColor::Red);
Super::Init(InSetting);
// IConsoleVariable* StreamableDelegateDelayFrames = IConsoleManager::Get().FindConsoleVariable(TEXT("s.StreamableDelegateDelayFrames"));
// StreamableDelegateDelayFrames->Set(0);
UFlibHotPatcherCoreHelper::DumpActiveTargetPlatforms();
FString Cmdline = FCommandLine::Get();
if(!Cmdline.Contains(NO_POSTLOAD_CACHE_DDC_OPTION,ESearchCase::IgnoreCase))
{
FCommandLine::Append(*FString::Printf(TEXT(" %s"),NO_POSTLOAD_CACHE_DDC_OPTION));
UE_LOG(LogHotPatcher,Display,TEXT("Append %s to Cmdline."),NO_POSTLOAD_CACHE_DDC_OPTION);
}
#if WITH_PACKAGE_CONTEXT
if(GetSettingObject()->bOverrideSavePackageContext)
{
PlatformSavePackageContexts = GetSettingObject()->PlatformSavePackageContexts;
}
else
{
PlatformSavePackageContexts = UFlibHotPatcherCoreHelper::CreatePlatformsPackageContexts(
GetSettingObject()->CookTargetPlatforms,
GetSettingObject()->IoStoreSettings.bIoStore,
GetSettingObject()->GetStorageCookedAbsDir()
);
}
#endif
InitShaderLibConllections();
// cook package tracker
if(GetSettingObject()->bPackageTracker)
{
// all cooker assets
ExixtPackagePathSet.PackagePaths.Append(GetSettingObject()->SkipLoadedAssets);
// current cooker assets
ExixtPackagePathSet.PackagePaths.Append(GetCookerAssets());
PackageTracker = MakeShareable(new FPackageTracker(ExixtPackagePathSet.PackagePaths));
OtherCookerPackageTracker = MakeShareable(new FOtherCookerPackageTracker(GetCookerAssets(),GetSettingObject()->SkipLoadedAssets));
// FreezePackageTracker = MakeShareable(new FFreezePackageTracker(GetCookerAssets(),GetSettingObject()->SkipLoadedAssets));
}
UFlibHotPatcherCoreHelper::DeleteDirectory(GetSettingObject()->GetStorageMetadataAbsDir());
GetCookFailedAssetsCollection().MissionName = GetSettingObject()->MissionName;
GetCookFailedAssetsCollection().MissionID = GetSettingObject()->MissionID;
GetCookFailedAssetsCollection().CookFailedAssets.Empty();
OnAssetCooked.AddUObject(this,&USingleCookerProxy::OnAssetCookedHandle);
}
int32 USingleCookerProxy::MakeCookQueue(FCookCluster& InCluser)
{
SCOPED_NAMED_EVENT_TEXT("MakeCookQueue",FColor::Red);
int32 MakeClusterCount = 0;
FString DumpCookerInfo;
DumpCookerInfo.Append(TEXT("\n-----------------------------Dump Cooker-----------------------------\n"));
DumpCookerInfo.Append(FString::Printf(TEXT("\tTotal Asset: %d\n"),InCluser.AssetDetails.Num()));
DumpCookerInfo.Append(FString::Printf(TEXT("\tbForceCookInOneFrame: %s\n"),GetSettingObject()->bForceCookInOneFrame ? TEXT("true"):TEXT("false")));
FString PlatformsStr;
for(auto Platform:InCluser.Platforms)
{
PlatformsStr += THotPatcherTemplateHelper::GetEnumNameByValue(Platform);
PlatformsStr.Append(TEXT(","));
}
PlatformsStr.RemoveFromEnd(TEXT(","));
DumpCookerInfo.Append(FString::Printf(TEXT("\tTarget Platforms: %s\n"),*PlatformsStr));
const int32 NumberOfAssetsPerFrame = GetSettingObject()->GetNumberOfAssetsPerFrame();
for(auto Class:GetPreCacheClasses())
{
TArray<FAssetDetail> ObjectAssets = UFlibAssetManageHelper::GetAssetDetailsByClass(InCluser.AssetDetails,Class,true);
if(ObjectAssets.Num())
{
DumpCookerInfo.Append(FString::Printf(TEXT("\t%s -- %d\n"),*Class->GetName(),ObjectAssets.Num()));
}
int32 ClassesNumberOfAssetsPerFrame = GetClassAssetNumOfPerCluster(Class);
while(ObjectAssets.Num())
{
int32 ClusterAssetNum = ClassesNumberOfAssetsPerFrame < 1 ? ObjectAssets.Num() : ClassesNumberOfAssetsPerFrame;
int32 NewClusterAssetNum = FMath::Min(ClusterAssetNum,ObjectAssets.Num());
TArray<FAssetDetail> CulsterObjectAssets(ObjectAssets.GetData(),NewClusterAssetNum);
FCookCluster NewCluster;
NewCluster.AssetDetails = std::move(CulsterObjectAssets);
ObjectAssets.RemoveAt(0,NewClusterAssetNum);
NewCluster.Platforms = GetSettingObject()->CookTargetPlatforms;
NewCluster.bPreGeneratePlatformData = GetSettingObject()->bPreGeneratePlatformData;
NewCluster.CookActionCallback.OnAssetCooked = GetOnPackageSavedCallback();
NewCluster.CookActionCallback.OnCookBegin = GetOnCookAssetBeginCallback();
CookCluserQueue.Enqueue(NewCluster);
++MakeClusterCount;
}
}
if(InCluser.AssetDetails.Num())
{
int32 OtherAssetNumPerFrame = NumberOfAssetsPerFrame < 1 ? InCluser.AssetDetails.Num() : NumberOfAssetsPerFrame;
int32 SplitNum = (InCluser.AssetDetails.Num() / OtherAssetNumPerFrame) + 1;
const TArray<TArray<FAssetDetail>> SplitedAssets= THotPatcherTemplateHelper::SplitArray(InCluser.AssetDetails,SplitNum);
for(const auto& AssetDetails:SplitedAssets)
{
FCookCluster NewCluster;
NewCluster.AssetDetails = std::move(AssetDetails);
NewCluster.Platforms = GetSettingObject()->CookTargetPlatforms;
NewCluster.bPreGeneratePlatformData = GetSettingObject()->bPreGeneratePlatformData;
NewCluster.CookActionCallback.OnAssetCooked = GetOnPackageSavedCallback();
NewCluster.CookActionCallback.OnCookBegin = GetOnCookAssetBeginCallback();
CookCluserQueue.Enqueue(NewCluster);
++MakeClusterCount;
}
DumpCookerInfo.Append(FString::Printf(TEXT("\tOther Assets -- %d, make %d cluster.\n"),InCluser.AssetDetails.Num(),SplitedAssets.Num()));
}
DumpCookerInfo.Append(TEXT("---------------------------------------------------------------------\n"));
UE_LOG(LogHotPatcher,Display,TEXT("%s"),*DumpCookerInfo);
return MakeClusterCount;
}
void USingleCookerProxy::CleanClusterCachedPlatformData(const FCookCluster& CookCluster)
{
SCOPED_NAMED_EVENT_TEXT("CleanClusterCachedPlatformData",FColor::Red);
TArray<ITargetPlatform*> TargetPlatforms = UFlibHotPatcherCoreHelper::GetTargetPlatformsByNames(CookCluster.Platforms);
TArray<UPackage*> PreCachePackages = UFlibAssetManageHelper::LoadPackagesForCooking(CookCluster.AsSoftObjectPaths(),GetSettingObject()->bConcurrentSave);
for(auto Package:PreCachePackages)
{
TArray<UObject*> ExportMap;
GetObjectsWithOuter(Package,ExportMap,true);
for(const auto& ExportObj:ExportMap)
{
#if ENGINE_MINOR_VERSION < 26
FScopedNamedEvent CacheExportEvent(FColor::Red,*FString::Printf(TEXT("%s"),*ExportObj->GetName()));
#endif
if (ExportObj->HasAnyFlags(RF_Transient))
{
// UE_LOG(LogHotPatcherCoreHelper, Display, TEXT("%s is PreCached."),*ExportObj->GetFullName());
continue;
}
ExportObj->ClearAllCachedCookedPlatformData();
}
#if ENGINE_MAJOR_VERSION > 4
Package->MarkAsGarbage();
#else
Package->MarkPendingKill();
#endif
}
}
void USingleCookerProxy::PreGeneratePlatformData(const FCookCluster& CookCluster)
{
SCOPED_NAMED_EVENT_TEXT("PreGeneratePlatformData",FColor::Red);
FExecTimeRecoder PreGeneratePlatformDataTimer(TEXT("PreGeneratePlatformData"));
TArray<ITargetPlatform*> TargetPlatforms = UFlibHotPatcherCoreHelper::GetTargetPlatformsByNames(CookCluster.Platforms);
bool bConcurrentSave = GetSettingObject()->bConcurrentSave;
TSet<UObject*> ProcessedObjects;
TSet<UObject*> PendingCachePlatformDataObjects;
auto PreCachePlatformDataForPackages = [&](TArray<UPackage*>& PreCachePackages)
{
uint32 TotalPackagesNum = PreCachePackages.Num();
for(int32 Index = PreCachePackages.Num() - 1 ;Index >= 0;--Index)
{
UPackage* CurrentPackage = PreCachePackages[Index];
if(GCookLog)
{
UE_LOG(LogHotPatcher,Log,TEXT("PreCache %s, pending %d total %d"),*CurrentPackage->GetPathName(),PreCachePackages.Num()-1,TotalPackagesNum);
}
UFlibHotPatcherCoreHelper::CacheForCookedPlatformData(
TArray<UPackage*>{CurrentPackage},
TargetPlatforms,
ProcessedObjects,
PendingCachePlatformDataObjects,
bConcurrentSave,
GetSettingObject()->bWaitEachAssetCompleted,
[&](UPackage* Package,UObject* ExportObj)
{
if(FreezePackageTracker.IsValid() && FreezePackageTracker->IsFreezed(ExportObj))
{
ExportObj->SetFlags(RF_NeedPostLoad);
ExportObj->SetFlags(RF_NeedPostLoadSubobjects);
ExportObj->ConditionalPostLoad();
}
});
PreCachePackages.RemoveAtSwap(Index,1,false);
}
};
TArray<UPackage*> PreCachePackages = UFlibAssetManageHelper::LoadPackagesForCooking(CookCluster.AsSoftObjectPaths(),GetSettingObject()->bConcurrentSave);
if(CookCluster.bPreGeneratePlatformData)
{
PreCachePlatformDataForPackages(PreCachePackages);
}
UFlibHotPatcherCoreHelper::WaitObjectsCachePlatformDataComplete(ProcessedObjects,PendingCachePlatformDataObjects,TargetPlatforms);
}
void USingleCookerProxy::DumpCluster(const FCookCluster& CookCluster, bool bWriteToLog)
{
SCOPED_NAMED_EVENT_TEXT("DumpCluster",FColor::Red);
FString Dumper;
Dumper.Append("\n--------------------Dump Cook Cluster--------------------\n");
Dumper.Append(FString::Printf(TEXT("\tAsset Number: %d\n"),CookCluster.AssetDetails.Num()));
FString PlatformsStr;
{
for(const auto& Platform:CookCluster.Platforms)
{
PlatformsStr = FString::Printf(TEXT("%s+%s"),*PlatformsStr,*THotPatcherTemplateHelper::GetEnumNameByValue(Platform));
}
PlatformsStr.RemoveFromStart(TEXT("+"));
}
Dumper.Append(FString::Printf(TEXT("\tCook Platforms: %s\n"),*PlatformsStr));
Dumper.Append(FString::Printf(TEXT("\tPreGeneratePlatformData: %s\n"),CookCluster.bPreGeneratePlatformData?TEXT("true"):TEXT("false")));
Dumper.Append(TEXT("\tCluster Assets:\n"));
for(const auto& Asset:CookCluster.AssetDetails)
{
Dumper.Append(FString::Printf(TEXT("\t%s %s\n"),*Asset.AssetType.ToString(),*Asset.PackagePath.ToString()));
}
Dumper.Append("---------------------------------------------------------\n");
if(bWriteToLog)
{
UE_LOG(LogHotPatcher,Display,TEXT("%s"),*Dumper);
}
}
void USingleCookerProxy::ExecCookCluster(const FCookCluster& CookCluster)
{
SCOPED_NAMED_EVENT_TEXT("ExecCookCluster",FColor::Red);
// for GC
{
UE_LOG(LogHotPatcher,Display,TEXT("ExecuteCookCluster Try GC..."));
GEngine->ForceGarbageCollection(false);
CollectGarbage(RF_NoFlags, false);
}
CookedClusterCount++;
UE_LOG(LogHotPatcher,Display,TEXT("ExecuteCookCluster %d with %d assets, total cluster %d"),CookedClusterCount,CookCluster.AssetDetails.Num(),ClusterCount);
FExecTimeRecoder ExecCookClusterTimer(TEXT("ExecCookCluster"));
if(!CookCluster.AssetDetails.Num())
{
return;
}
bool bContainShaderCluster = UFlibHotPatcherCoreHelper::AssetDetailsHasClasses(CookCluster.AssetDetails,UFlibHotPatcherCoreHelper::GetAllMaterialClassesNames());
DumpCluster(CookCluster,GCookLog);
TArray<ITargetPlatform*> TargetPlatforms = UFlibHotPatcherCoreHelper::GetTargetPlatformsByNames(CookCluster.Platforms);
FString CookBaseDir = GetSettingObject()->GetStorageCookedAbsDir();
CleanOldCooked(CookBaseDir,CookCluster.AsSoftObjectPaths(),CookCluster.Platforms);
#if WITH_PACKAGE_CONTEXT
TMap<FString, FSavePackageContext*> SavePackageContextsNameMapping = GetPlatformSavePackageContextsNameMapping();
#endif
TSharedPtr<FClassesPackageTracker> ClassesPackageTracker = MakeShareable(new FClassesPackageTracker);
TArray<UPackage*> PreCachePackages = UFlibAssetManageHelper::LoadPackagesForCooking(CookCluster.AsSoftObjectPaths(),GetSettingObject()->bConcurrentSave);
bool bCanConcurrentSave = GetSettingObject()->bConcurrentSave && CookCluster.bPreGeneratePlatformData;
// pre cahce platform data
if(CookCluster.bPreGeneratePlatformData)
{
PreGeneratePlatformData(CookCluster);
}
// for cooking
{
TMap<FName,TMap<FName,FString>> PackageCookedSavePaths;
for(const auto& Package:PreCachePackages)
{
FName PackagePathName = *Package->GetPathName();
PackageCookedSavePaths.Add(PackagePathName,TMap<FName,FString>{});
for(auto Platform:TargetPlatforms)
{
FString CookedSavePath = UFlibHotPatcherCoreHelper::GetAssetCookedSavePath(CookBaseDir,PackagePathName.ToString(), Platform->PlatformName());
PackageCookedSavePaths.Find(PackagePathName)->Add(*Platform->PlatformName(),CookedSavePath);
}
}
TMap<ETargetPlatform,ITargetPlatform*> PlatformMaps;
for(auto Platform:TargetPlatforms)
{
ETargetPlatform EnumPlatform;
THotPatcherTemplateHelper::GetEnumValueByName(*Platform->PlatformName(),EnumPlatform);
PlatformMaps.Add(EnumPlatform,Platform);
}
auto CookPackageLambda = [&](int32 AssetIndex)
{
// FExecTimeRecoder CookTimer(PreCachePackages[AssetIndex]->GetFullName());
UFlibHotPatcherCoreHelper::CookPackage(
PreCachePackages[AssetIndex],
PlatformMaps,
CookCluster.CookActionCallback,
#if WITH_PACKAGE_CONTEXT
SavePackageContextsNameMapping,
#endif
*PackageCookedSavePaths.Find(*PreCachePackages[AssetIndex]->GetPathName()),
GetSettingObject()->bConcurrentSave
);
};
if(bCanConcurrentSave)
{
for(auto& Platform:TargetPlatforms)
{
ETargetPlatform TargetPlatform;
THotPatcherTemplateHelper::GetEnumValueByName(Platform->PlatformName(),TargetPlatform);
}
GIsSavingPackage = true;
ParallelFor(PreCachePackages.Num(), CookPackageLambda,GForceSingleThread ? true : !bCanConcurrentSave);
GIsSavingPackage = false;
}
else
{
for(int32 index = 0;index<PreCachePackages.Num();++index)
{
CookPackageLambda(index);
}
}
}
// clean cached ddd / release memory
// CleanClusterCachedPlatformData(CookCluster);
UFlibShaderCodeLibraryHelper::WaitShaderCompilingComplete();
UFlibHotPatcherCoreHelper::WaitDistanceFieldAsyncQueueComplete();
UFlibHotPatcherCoreHelper::WaitForAsyncFileWrites();
UFlibHotPatcherCoreHelper::WaitDDCComplete();
}
void USingleCookerProxy::Tick(float DeltaTime)
{
SCOPED_NAMED_EVENT_TEXT("USingleCookerProxy::Tick",FColor::Red);
if(!GetSettingObject() || GetSettingObject()->bForceCookInOneFrame|| !IsAlready())
{
return;
}
if(!CookCluserQueue.IsEmpty())
{
FCookCluster CookCluster;
CookCluserQueue.Dequeue(CookCluster);
ExecCookCluster(CookCluster);
}
static bool bCookedPackageTracker = false;
if(!bCookedPackageTracker && CookCluserQueue.IsEmpty() && GetSettingObject()->bPackageTracker && GetSettingObject()->bCookPackageTrackerAssets)
{
FCookCluster PackageTrackerCluster = GetPackageTrackerAsCluster();
if(PackageTrackerCluster.AssetDetails.Num())
{
ClusterCount += MakeCookQueue(PackageTrackerCluster);
bCookedPackageTracker = true;
}
}
}
TStatId USingleCookerProxy::GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(USingleCookerProxy, STATGROUP_Tickables);
}
bool USingleCookerProxy::IsTickable() const
{
return IsAlready() && !const_cast<USingleCookerProxy*>(this)->GetSettingObject()->bForceCookInOneFrame;
}
void USingleCookerProxy::Shutdown()
{
SCOPED_NAMED_EVENT_TEXT("USingleCookerProxy::Shutdown",FColor::Red);
{
SCOPED_NAMED_EVENT_TEXT("USingleCookerProxy::WaitCookerFinished",FColor::Red);
// Wait for all shaders to finish compiling
UFlibShaderCodeLibraryHelper::WaitShaderCompilingComplete();
UFlibHotPatcherCoreHelper::WaitForAsyncFileWrites();
}
ShutdowShaderLibCollections();
FString StorageMetadataAbsDir = GetSettingObject()->GetStorageMetadataAbsDir();
// serialize cook failed assets to json
if(GetCookFailedAssetsCollection().CookFailedAssets.Num())
{
FString FailedJsonString;
THotPatcherTemplateHelper::TSerializeStructAsJsonString(GetCookFailedAssetsCollection(),FailedJsonString);
FString SaveTo = UFlibHotCookerHelper::GetCookerProcFailedResultPath(StorageMetadataAbsDir,GetSettingObject()->MissionName,GetSettingObject()->MissionID);
SaveTo = UFlibPatchParserHelper::ReplaceMark(SaveTo);
UE_LOG(LogHotPatcher,Warning,TEXT("Single Cooker Proxy Cook Failed Assets %s:\n%s\nSerialize to %s"),*GetSettingObject()->MissionName,*FailedJsonString,*SaveTo);
FFileHelper::SaveStringToFile(FailedJsonString,*SaveTo);
}
if(PackageTracker)
{
auto SerializePackageSetToString = [](const TSet<FName>& Packages)->FString
{
FString OutString;
FPackagePathSet AdditionalPackageSet;
AdditionalPackageSet.PackagePaths.Append(Packages);
if(AdditionalPackageSet.PackagePaths.Num())
{
THotPatcherTemplateHelper::TSerializeStructAsJsonString(AdditionalPackageSet,OutString);
}
return OutString;
};
const TSet<FName>& AllLoadedPackagePaths = PackageTracker->GetLoadedPackages();
const TSet<FName>& AdditionalPackageSet = PackageTracker->GetPendingPackageSet();
const TSet<FName> LoadedOtherCookerAssets = OtherCookerPackageTracker->GetLoadOtherCookerAssets();
if(AllLoadedPackagePaths.Num())
{
FFileHelper::SaveStringToFile(SerializePackageSetToString(AllLoadedPackagePaths),*FPaths::Combine(StorageMetadataAbsDir,TEXT("AllLoadedPackageSet.json")));
}
if(AdditionalPackageSet.Num())
{
FFileHelper::SaveStringToFile(SerializePackageSetToString(AdditionalPackageSet),*FPaths::Combine(StorageMetadataAbsDir,TEXT("AdditionalPackageSet.json")));
}
if(LoadedOtherCookerAssets.Num())
{
FFileHelper::SaveStringToFile(SerializePackageSetToString(LoadedOtherCookerAssets),*FPaths::Combine(StorageMetadataAbsDir,TEXT("OtherCookerAssetPackageSet.json")));
}
}
BulkDataManifest();
IoStoreManifest();
bAlready = false;
Super::Shutdown();
}
void USingleCookerProxy::BulkDataManifest()
{
SCOPED_NAMED_EVENT_TEXT("BulkDataManifest",FColor::Red);
// save bulk data manifest
for(auto& Platform:GetSettingObject()->CookTargetPlatforms)
{
if(GetSettingObject()->IoStoreSettings.bStorageBulkDataInfo)
{
#if WITH_PACKAGE_CONTEXT
UFlibHotPatcherCoreHelper::SavePlatformBulkDataManifest(GetPlatformSavePackageContexts(),Platform);
#endif
}
}
}
void USingleCookerProxy::IoStoreManifest()
{
SCOPED_NAMED_EVENT_TEXT("IoStoreManifest",FColor::Red);
if(GetSettingObject()->IoStoreSettings.bIoStore)
{
TSet<ETargetPlatform> Platforms;
for(auto Platform:GlobalCluser.Platforms)
{
Platforms.Add(Platform);
}
for(auto Platform:Platforms)
{
FString PlatformName = THotPatcherTemplateHelper::GetEnumNameByValue(Platform);
TimeRecorder StorageCookOpenOrderTR(FString::Printf(TEXT("Storage CookOpenOrder.txt for %s, time:"),*PlatformName));
struct CookOpenOrder
{
CookOpenOrder()=default;
CookOpenOrder(const FString& InPath,int32 InOrder):uasset_relative_path(InPath),order(InOrder){}
FString uasset_relative_path;
int32 order;
};
auto MakeCookOpenOrder = [](const TArray<FName>& Assets)->TArray<CookOpenOrder>
{
TArray<CookOpenOrder> result;
TArray<FAssetData> AssetsData;
TArray<FString> AssetPackagePaths;
for (auto AssetPackagePath : Assets)
{
FSoftObjectPath ObjectPath{ AssetPackagePath.ToString() };
TArray<FAssetData> AssetData;
UFlibAssetManageHelper::GetSpecifyAssetData(ObjectPath.GetLongPackageName(),AssetData,true);
AssetsData.Append(AssetData);
}
// UFlibAssetManageHelper::GetAssetsData(AssetPackagePaths,AssetsData);
for(int32 index=0;index<AssetsData.Num();++index)
{
UPackage* Package = AssetsData[index].GetPackage();
FString LocalPath;
const FString* PackageExtension = Package->ContainsMap() ? &FPackageName::GetMapPackageExtension() : &FPackageName::GetAssetPackageExtension();
FPackageName::TryConvertLongPackageNameToFilename(AssetsData[index].PackageName.ToString(), LocalPath, *PackageExtension);
result.Emplace(LocalPath,index);
}
return result;
};
TArray<CookOpenOrder> CookOpenOrders = MakeCookOpenOrder(GetPlatformCookAssetOrders(Platform));
auto SaveCookOpenOrder = [](const TArray<CookOpenOrder>& CookOpenOrders,const FString& File)
{
TArray<FString> result;
for(const auto& OrderFile:CookOpenOrders)
{
result.Emplace(FString::Printf(TEXT("\"%s\" %d"),*OrderFile.uasset_relative_path,OrderFile.order));
}
FFileHelper::SaveStringArrayToFile(result,*FPaths::ConvertRelativePathToFull(File));
};
SaveCookOpenOrder(CookOpenOrders,FPaths::Combine(GetSettingObject()->GetStorageMetadataAbsDir(),GetSettingObject()->MissionName,PlatformName,TEXT("CookOpenOrder.txt")));
}
}
}
void USingleCookerProxy::InitShaderLibConllections()
{
SCOPED_NAMED_EVENT_TEXT("USingleCookerProxy::InitShaderLibConllections",FColor::Red);
FString SavePath = GetSettingObject()->GetStorageMetadataAbsDir();
PlatformCookShaderCollection = UFlibHotCookerHelper::CreateCookShaderCollectionProxyByPlatform(
GetSettingObject()->ShaderLibName,
GetSettingObject()->CookTargetPlatforms,
GetSettingObject()->ShaderOptions.bSharedShaderLibrary,
GetSettingObject()->ShaderOptions.bNativeShader,
true,
SavePath,
true
);
if(PlatformCookShaderCollection.IsValid())
{
PlatformCookShaderCollection->Init();
}
}
void USingleCookerProxy::ShutdowShaderLibCollections()
{
SCOPED_NAMED_EVENT_TEXT("USingleCookerProxy::ShutdowShaderLibCollections",FColor::Red);
if(GetSettingObject()->ShaderOptions.bSharedShaderLibrary)
{
if(PlatformCookShaderCollection.IsValid())
{
PlatformCookShaderCollection->Shutdown();
}
}
}
FCookCluster USingleCookerProxy::GetPackageTrackerAsCluster()
{
SCOPED_NAMED_EVENT_TEXT("GetPackageTrackerAsCluster",FColor::Red);
FCookCluster PackageTrackerCluster;
PackageTrackerCluster.Platforms = GetSettingObject()->CookTargetPlatforms;
PackageTrackerCluster.bPreGeneratePlatformData = GetSettingObject()->bPreGeneratePlatformData;
PackageTrackerCluster.CookActionCallback.OnAssetCooked = GetOnPackageSavedCallback();
PackageTrackerCluster.CookActionCallback.OnCookBegin = GetOnCookAssetBeginCallback();
if(PackageTracker && GetSettingObject()->bCookPackageTrackerAssets)
{
PackageTrackerCluster.AssetDetails.Empty();
for(FName LongPackageName:PackageTracker->GetPendingPackageSet())
{
if(!FPackageName::DoesPackageExist(LongPackageName.ToString()))
{
continue;
}
// make asset data to asset registry
FString PackagePath = UFlibAssetManageHelper::LongPackageNameToPackagePath(LongPackageName.ToString());
FSoftObjectPath ObjectPath(PackagePath);
UFlibAssetManageHelper::UpdateAssetRegistryData(ObjectPath.GetLongPackageName());
FAssetData AssetData;
if(UAssetManager::Get().GetAssetDataForPath(ObjectPath,AssetData))
{
FAssetDetail AssetDetail;
if(UFlibAssetManageHelper::ConvFAssetDataToFAssetDetail(AssetData,AssetDetail))
{
PackageTrackerCluster.AssetDetails.Emplace(AssetDetail);
}
}
else
{
UE_LOG(LogHotPatcher,Warning,TEXT("[GetPackageTrackerAsCluster] Get %s AssetData Failed!"),*LongPackageName.ToString());
}
}
}
return PackageTrackerCluster;
};
FCookActionResultEvent USingleCookerProxy::GetOnPackageSavedCallback()
{
SCOPED_NAMED_EVENT_TEXT("GetOnPackageSavedCallback",FColor::Red);
const FCookActionResultEvent PackageSavedCallback = [this](const FSoftObjectPath& PackagePath,ETargetPlatform Platform,ESavePackageResult Result)
{
OnAssetCooked.Broadcast(PackagePath,Platform,Result);
};
return PackageSavedCallback;
}
FCookActionEvent USingleCookerProxy::GetOnCookAssetBeginCallback()
{
const FCookActionEvent CookAssetBegin = [this](const FSoftObjectPath& PackagePath,ETargetPlatform Platform)
{
OnCookAssetBegin.Broadcast(PackagePath,Platform);
};
return CookAssetBegin;
}
TSet<FName> USingleCookerProxy::GetCookerAssets()
{
static bool bInited = false;
static TSet<FName> PackagePaths;
if(!bInited)
{
for(const auto& Asset:GetSettingObject()->CookAssets)
{
PackagePaths.Add(*UFlibAssetManageHelper::PackagePathToLongPackageName(Asset.PackagePath.ToString()));
}
bInited = true;
}
return PackagePaths;
}
bool USingleCookerProxy::DoExport()
{
SCOPED_NAMED_EVENT_TEXT("DoExport",FColor::Red);
GlobalCluser.AssetDetails = GetSettingObject()->CookAssets;;
GlobalCluser.Platforms = GetSettingObject()->CookTargetPlatforms;;
GlobalCluser.CookActionCallback.OnAssetCooked = GetOnPackageSavedCallback();;
GlobalCluser.CookActionCallback.OnCookBegin = GetOnCookAssetBeginCallback();
GlobalCluser.bPreGeneratePlatformData = GetSettingObject()->bPreGeneratePlatformData;
UFlibAssetManageHelper::ReplaceReditector(GlobalCluser.AssetDetails);
UFlibAssetManageHelper::RemoveInvalidAssets(GlobalCluser.AssetDetails);
ClusterCount = MakeCookQueue(GlobalCluser);
// force cook all in onece frame
if(GetSettingObject()->bForceCookInOneFrame)
{
while(!CookCluserQueue.IsEmpty())
{
FCookCluster CookCluster;
CookCluserQueue.Dequeue(CookCluster);
ExecCookCluster(CookCluster);
}
if(CookCluserQueue.IsEmpty())
{
ExecCookCluster(GetPackageTrackerAsCluster());
}
}
bAlready = true;
return !HasError();
}
void USingleCookerProxy::CleanOldCooked(const FString& CookBaseDir,const TArray<FSoftObjectPath>& ObjectPaths,const TArray<ETargetPlatform>& CookPlatforms)
{
SCOPED_NAMED_EVENT_TEXT("CleanOldCooked",FColor::Red);
TArray<ITargetPlatform*> CookPlatfotms = UFlibHotPatcherCoreHelper::GetTargetPlatformsByNames(CookPlatforms);
{
SCOPED_NAMED_EVENT_TEXT("Delete Old Cooked Files",FColor::Red);
TArray<FString> PaddingDeleteFiles;
// FCriticalSection LocalSynchronizationObject;
// for(const auto& Asset:ObjectPaths)
ParallelFor(ObjectPaths.Num(),[&](int32 Index)
{
FString PackageName = ObjectPaths[Index].GetLongPackageName();
for(const auto& TargetPlatform:CookPlatfotms)
{
FString CookedSavePath = UFlibHotPatcherCoreHelper::GetAssetCookedSavePath(CookBaseDir,PackageName, TargetPlatform->PlatformName());
// FScopeLock Lock(&LocalSynchronizationObject);
PaddingDeleteFiles.AddUnique(CookedSavePath);
}
},true);
ParallelFor(PaddingDeleteFiles.Num(),[PaddingDeleteFiles](int32 Index)
{
FString FileName = PaddingDeleteFiles[Index];
if(!FileName.IsEmpty() && FPaths::FileExists(FileName))
{
IFileManager::Get().Delete(*FileName,true,true,true);
}
},GForceSingleThread);
}
}
bool USingleCookerProxy::HasError()
{
SCOPED_NAMED_EVENT_TEXT("USingleCookerProxy::HasError",FColor::Red);
TArray<ETargetPlatform> TargetPlatforms;
GetCookFailedAssetsCollection().CookFailedAssets.GetKeys(TargetPlatforms);
return !!TargetPlatforms.Num();
}
void USingleCookerProxy::OnAssetCookedHandle(const FSoftObjectPath& PackagePath, ETargetPlatform Platform,
ESavePackageResult Result)
{
FScopeLock Lock(&SynchronizationObject);
SCOPED_NAMED_EVENT_TEXT("OnAssetCookedHandle",FColor::Red);
FString PlatformName = FString::Printf(TEXT("%lld"),(int64)Platform);
if(GetPlatformNameMapping().Contains(Platform))
{
PlatformName = GetPlatformNameMapping().Find(Platform)->ToString();;
}
FString SavePackageResultStr = UFlibHotPatcherCoreHelper::GetSavePackageResultStr(Result);
FName AssetPathName = PackagePath.GetAssetPathName();
FString AssetPath = PackagePath.GetAssetPathString();
GetPaendingCookAssetsSet().Remove(AssetPathName);
switch(Result)
{
case ESavePackageResult::Success:
{
GetPlatformCookAssetOrders(Platform).Add(AssetPathName);
break;
}
case ESavePackageResult::Error:
case ESavePackageResult::MissingFile:
{
UE_LOG(LogHotPatcher,Error,TEXT("CookError %s for %s (%s)!"),*AssetPath,*PlatformName,*SavePackageResultStr);
GetCookFailedAssetsCollection().CookFailedAssets.FindOrAdd(Platform).PackagePaths.Add(AssetPathName);
break;
}
default:
{
UE_LOG(LogHotPatcher,Warning,TEXT("CookWarning %s for %s Failed (%s)!"),*AssetPath,*PlatformName, *SavePackageResultStr);
}
}
}
bool USingleCookerProxy::IsFinsihed()
{
return !GetPaendingCookAssetsSet().Num() && CookCluserQueue.IsEmpty();
}
#if WITH_PACKAGE_CONTEXT
TMap<ETargetPlatform, FSavePackageContext*> USingleCookerProxy::GetPlatformSavePackageContextsRaw()
{
FScopeLock Lock(&SynchronizationObject);
SCOPED_NAMED_EVENT_TEXT("USingleCookerProxy::GetPlatformSavePackageContextsRaw",FColor::Red);
TMap<ETargetPlatform,FSavePackageContext*> result;
TArray<ETargetPlatform> Keys;
GetPlatformSavePackageContexts().GetKeys(Keys);
for(const auto& Key:Keys)
{
result.Add(Key,GetPlatformSavePackageContexts().Find(Key)->Get());
}
return result;
}
TMap<FString, FSavePackageContext*> USingleCookerProxy::GetPlatformSavePackageContextsNameMapping()
{
FScopeLock Lock(&SynchronizationObject);
SCOPED_NAMED_EVENT_TEXT("USingleCookerProxy::GetPlatformSavePackageContextsNameMapping",FColor::Red);
TMap<FString,FSavePackageContext*> result;
TArray<ETargetPlatform> Keys;
GetPlatformSavePackageContexts().GetKeys(Keys);
for(const auto& Key:Keys)
{
result.Add(THotPatcherTemplateHelper::GetEnumNameByValue(Key),GetPlatformSavePackageContexts().Find(Key)->Get());
}
return result;
}
#endif
TArray<FName>& USingleCookerProxy::GetPlatformCookAssetOrders(ETargetPlatform Platform)
{
FScopeLock Lock(&SynchronizationObject);
return CookAssetOrders.FindOrAdd(Platform);
}
TSet<FName> USingleCookerProxy::GetAdditionalAssets()
{
SCOPED_NAMED_EVENT_TEXT("GetAdditionalAssets",FColor::Red);
if(GetSettingObject()->bPackageTracker && PackageTracker.IsValid())
{
return PackageTracker->GetPendingPackageSet();
}
return TSet<FName>{};
}
// pre cache asset type order
TArray<UClass*> USingleCookerProxy::GetPreCacheClasses() const
{
return UFlibHotPatcherCoreHelper::GetPreCacheClasses();
}
int32 USingleCookerProxy::GetClassAssetNumOfPerCluster(UClass* Class)
{
int32 ClassesNumberOfAssetsPerFrame = GetSettingObject()->GetNumberOfAssetsPerFrame();
for(const auto& OverrideClasses:GetSettingObject()->GetOverrideNumberOfAssetsPerFrame())
{
if(OverrideClasses.Key == Class)
{
ClassesNumberOfAssetsPerFrame = OverrideClasses.Value;
break;
}
}
return ClassesNumberOfAssetsPerFrame;
}

View File

@ -0,0 +1,360 @@
#include "Cooker/PackageWriter/HotPatcherPackageWriter.h"
#if WITH_PACKAGE_CONTEXT && ENGINE_MAJOR_VERSION > 4
#include "AssetRegistry/IAssetRegistry.h"
#include "Async/Async.h"
#include "Serialization/LargeMemoryWriter.h"
#include "UObject/SavePackage.h"
void FHotPatcherPackageWriter::Initialize(const FCookInfo& Info){}
void FHotPatcherPackageWriter::AddToExportsSize(int64& ExportsSize)
{
TPackageWriterToSharedBuffer<ICookedPackageWriter>::AddToExportsSize(ExportsSize);
}
void FHotPatcherPackageWriter::BeginPackage(const FBeginPackageInfo& Info)
{
TPackageWriterToSharedBuffer<ICookedPackageWriter>::BeginPackage(Info);
}
void FHotPatcherPackageWriter::BeginCook(){}
void FHotPatcherPackageWriter::EndCook(){}
// void FHotPatcherPackageWriter::Flush()
// {
// UPackage::WaitForAsyncFileWrites();
// }
TUniquePtr<FAssetRegistryState> FHotPatcherPackageWriter::LoadPreviousAssetRegistry()
{
return TUniquePtr<FAssetRegistryState>();
}
FCbObject FHotPatcherPackageWriter::GetOplogAttachment(FName PackageName, FUtf8StringView AttachmentKey)
{
return FCbObject();
}
void FHotPatcherPackageWriter::RemoveCookedPackages(TArrayView<const FName> PackageNamesToRemove)
{
}
void FHotPatcherPackageWriter::RemoveCookedPackages()
{
UPackage::WaitForAsyncFileWrites();
}
void FHotPatcherPackageWriter::MarkPackagesUpToDate(TArrayView<const FName> UpToDatePackages)
{
}
bool FHotPatcherPackageWriter::GetPreviousCookedBytes(const FPackageInfo& Info, FPreviousCookedBytesData& OutData)
{
return ICookedPackageWriter::GetPreviousCookedBytes(Info, OutData);
}
void FHotPatcherPackageWriter::CompleteExportsArchiveForDiff(const FPackageInfo& Info,
FLargeMemoryWriter& ExportsArchive)
{
ICookedPackageWriter::CompleteExportsArchiveForDiff(Info, ExportsArchive);
}
void FHotPatcherPackageWriter::CollectForSavePackageData(FRecord& Record, FCommitContext& Context)
{
Context.ExportsBuffers.AddDefaulted(Record.Packages.Num());
for (FPackageWriterRecords::FWritePackage& Package : Record.Packages)
{
Context.ExportsBuffers[Package.Info.MultiOutputIndex].Add(FExportBuffer{ Package.Buffer, MoveTemp(Package.Regions) });
}
}
void FHotPatcherPackageWriter::CollectForSaveBulkData(FRecord& Record, FCommitContext& Context)
{
for (FBulkDataRecord& BulkRecord : Record.BulkDatas)
{
if (BulkRecord.Info.BulkDataType == FBulkDataInfo::AppendToExports)
{
if (Record.bCompletedExportsArchiveForDiff)
{
// Already Added in CompleteExportsArchiveForDiff
continue;
}
Context.ExportsBuffers[BulkRecord.Info.MultiOutputIndex].Add(FExportBuffer{ BulkRecord.Buffer, MoveTemp(BulkRecord.Regions) });
}
else
{
FWriteFileData& OutputFile = Context.OutputFiles.Emplace_GetRef();
OutputFile.Filename = BulkRecord.Info.LooseFilePath;
OutputFile.Buffer = FCompositeBuffer(BulkRecord.Buffer);
OutputFile.Regions = MoveTemp(BulkRecord.Regions);
OutputFile.bIsSidecar = true;
OutputFile.bContributeToHash = BulkRecord.Info.MultiOutputIndex == 0; // Only caculate the main package output hash
}
}
}
void FHotPatcherPackageWriter::CollectForSaveLinkerAdditionalDataRecords(FRecord& Record, FCommitContext& Context)
{
if (Record.bCompletedExportsArchiveForDiff)
{
// Already Added in CompleteExportsArchiveForDiff
return;
}
for (FLinkerAdditionalDataRecord& AdditionalRecord : Record.LinkerAdditionalDatas)
{
Context.ExportsBuffers[AdditionalRecord.Info.MultiOutputIndex].Add(FExportBuffer{ AdditionalRecord.Buffer, MoveTemp(AdditionalRecord.Regions) });
}
}
void FHotPatcherPackageWriter::CollectForSaveAdditionalFileRecords(FRecord& Record, FCommitContext& Context)
{
for (FAdditionalFileRecord& AdditionalRecord : Record.AdditionalFiles)
{
FWriteFileData& OutputFile = Context.OutputFiles.Emplace_GetRef();
OutputFile.Filename = AdditionalRecord.Info.Filename;
OutputFile.Buffer = FCompositeBuffer(AdditionalRecord.Buffer);
OutputFile.bIsSidecar = true;
OutputFile.bContributeToHash = AdditionalRecord.Info.MultiOutputIndex == 0; // Only calculate the main package output hash
}
}
void FHotPatcherPackageWriter::CollectForSaveExportsFooter(FRecord& Record, FCommitContext& Context)
{
if (Record.bCompletedExportsArchiveForDiff)
{
// Already Added in CompleteExportsArchiveForDiff
return;
}
uint32 FooterData = PACKAGE_FILE_TAG;
FSharedBuffer Buffer = FSharedBuffer::Clone(&FooterData, sizeof(FooterData));
for (FPackageWriterRecords::FWritePackage& Package : Record.Packages)
{
Context.ExportsBuffers[Package.Info.MultiOutputIndex].Add(FExportBuffer{ Buffer, TArray<FFileRegion>() });
}
}
void FHotPatcherPackageWriter::CollectForSaveExportsBuffers(FRecord& Record, FCommitContext& Context)
{
check(Context.ExportsBuffers.Num() == Record.Packages.Num());
for (FPackageWriterRecords::FWritePackage& Package : Record.Packages)
{
TArray<FExportBuffer>& ExportsBuffers = Context.ExportsBuffers[Package.Info.MultiOutputIndex];
check(ExportsBuffers.Num() > 0);
// Split the ExportsBuffer into (1) Header and (2) Exports + AllAppendedData
int64 HeaderSize = Package.Info.HeaderSize;
FExportBuffer& HeaderAndExportsBuffer = ExportsBuffers[0];
FSharedBuffer& HeaderAndExportsData = HeaderAndExportsBuffer.Buffer;
// Header (.uasset/.umap)
{
FWriteFileData& OutputFile = Context.OutputFiles.Emplace_GetRef();
OutputFile.Filename = Package.Info.LooseFilePath;
OutputFile.Buffer = FCompositeBuffer(
FSharedBuffer::MakeView(HeaderAndExportsData.GetData(), HeaderSize, HeaderAndExportsData));
OutputFile.bIsSidecar = false;
OutputFile.bContributeToHash = Package.Info.MultiOutputIndex == 0; // Only calculate the main package output hash
}
// Exports + AllAppendedData (.uexp)
{
FWriteFileData& OutputFile = Context.OutputFiles.Emplace_GetRef();
OutputFile.Filename = FPaths::ChangeExtension(Package.Info.LooseFilePath, LexToString(EPackageExtension::Exports));
OutputFile.bIsSidecar = false;
OutputFile.bContributeToHash = Package.Info.MultiOutputIndex == 0; // Only caculate the main package output hash
int32 NumBuffers = ExportsBuffers.Num();
TArray<FSharedBuffer> BuffersForComposition;
BuffersForComposition.Reserve(NumBuffers);
const uint8* ExportsStart = static_cast<const uint8*>(HeaderAndExportsData.GetData()) + HeaderSize;
BuffersForComposition.Add(FSharedBuffer::MakeView(ExportsStart, HeaderAndExportsData.GetSize() - HeaderSize,
HeaderAndExportsData));
OutputFile.Regions.Append(MoveTemp(HeaderAndExportsBuffer.Regions));
for (FExportBuffer& ExportsBuffer : TArrayView<FExportBuffer>(ExportsBuffers).Slice(1, NumBuffers - 1))
{
BuffersForComposition.Add(ExportsBuffer.Buffer);
OutputFile.Regions.Append(MoveTemp(ExportsBuffer.Regions));
}
OutputFile.Buffer = FCompositeBuffer(BuffersForComposition);
// Adjust regions so they are relative to the start of the uexp file
for (FFileRegion& Region : OutputFile.Regions)
{
Region.Offset -= HeaderSize;
}
}
}
}
TFuture<FMD5Hash> FHotPatcherPackageWriter::AsyncSave(FRecord& Record, const FCommitPackageInfo& Info)
{
FCommitContext Context{ Info };
// The order of these collection calls is important, both for ExportsBuffers (affects the meaning of offsets
// to those buffers) and for OutputFiles (affects the calculation of the Hash for the set of PackageData)
// The order of ExportsBuffers must match CompleteExportsArchiveForDiff.
CollectForSavePackageData(Record, Context);
CollectForSaveBulkData(Record, Context);
CollectForSaveLinkerAdditionalDataRecords(Record, Context);
CollectForSaveAdditionalFileRecords(Record, Context);
CollectForSaveExportsFooter(Record, Context);
CollectForSaveExportsBuffers(Record, Context);
return AsyncSaveOutputFiles(Record, Context);
}
TFuture<FMD5Hash> FHotPatcherPackageWriter::AsyncSaveOutputFiles(FRecord& Record, FCommitContext& Context)
{
if (!EnumHasAnyFlags(Context.Info.WriteOptions, EWriteOptions::Write | EWriteOptions::ComputeHash))
{
return TFuture<FMD5Hash>();
}
UE::SavePackageUtilities::IncrementOutstandingAsyncWrites();
FMD5Hash OutputHash;
FMD5 AccumulatedHash;
for (FWriteFileData& OutputFile : Context.OutputFiles)
{
OutputFile.Write(AccumulatedHash, Context.Info.WriteOptions);
}
OutputHash.Set(AccumulatedHash);
return Async(EAsyncExecution::TaskGraph,[OutputHash]()mutable ->FMD5Hash
{
UE::SavePackageUtilities::DecrementOutstandingAsyncWrites();
return OutputHash;
});
}
FDateTime FHotPatcherPackageWriter::GetPreviousCookTime() const
{
FString MetadataDirectoryPath = FPaths::ProjectDir() / TEXT("Metadata");
const FString PreviousAssetRegistry = FPaths::Combine(MetadataDirectoryPath, GetDevelopmentAssetRegistryFilename());
return IFileManager::Get().GetTimeStamp(*PreviousAssetRegistry);
}
void FHotPatcherPackageWriter::CommitPackageInternal(FPackageRecord&& Record,
const IPackageWriter::FCommitPackageInfo& Info)
{
FRecord& InRecord = static_cast<FRecord&>(Record);
TFuture<FMD5Hash> CookedHash;
if (Info.Status == ECommitStatus::Success)
{
CookedHash = AsyncSave(InRecord, Info);
}
}
/** Version of the superclass's per-package record that includes our class-specific data. */
struct FHotRecord : public FPackageWriterRecords::FPackage
{
};
FPackageWriterRecords::FPackage* FHotPatcherPackageWriter::ConstructRecord()
{
return new FHotRecord();
}
static void WriteToFile(const FString& Filename, const FCompositeBuffer& Buffer)
{
IFileManager& FileManager = IFileManager::Get();
struct FFailureReason
{
uint32 LastErrorCode = 0;
bool bSizeMatchFailed = false;
};
TOptional<FFailureReason> FailureReason;
for (int32 Tries = 0; Tries < 3; ++Tries)
{
FArchive* Ar = FileManager.CreateFileWriter(*Filename);
if (!Ar)
{
if (!FailureReason)
{
FailureReason = FFailureReason{ FPlatformMisc::GetLastError(), false };
}
continue;
}
int64 DataSize = 0;
for (const FSharedBuffer& Segment : Buffer.GetSegments())
{
int64 SegmentSize = static_cast<int64>(Segment.GetSize());
Ar->Serialize(const_cast<void*>(Segment.GetData()), SegmentSize);
DataSize += SegmentSize;
}
delete Ar;
if (FileManager.FileSize(*Filename) != DataSize)
{
if (!FailureReason)
{
FailureReason = FFailureReason{ 0, true };
}
FileManager.Delete(*Filename);
continue;
}
return;
}
TCHAR LastErrorText[1024];
if (FailureReason && FailureReason->bSizeMatchFailed)
{
FCString::Strcpy(LastErrorText, TEXT("Unexpected file size. Another operation is modifying the file, or the write operation failed to write completely."));
}
else if (FailureReason && FailureReason->LastErrorCode != 0)
{
FPlatformMisc::GetSystemErrorMessage(LastErrorText, UE_ARRAY_COUNT(LastErrorText), FailureReason->LastErrorCode);
}
else
{
FCString::Strcpy(LastErrorText, TEXT("Unknown failure reason."));
}
UE_LOG(LogTemp, Fatal, TEXT("SavePackage Async write %s failed: %s"), *Filename, LastErrorText);
}
void FHotPatcherPackageWriter::FWriteFileData::Write(FMD5& AccumulatedHash, EWriteOptions WriteOptions) const
{
//@todo: FH: Should we calculate the hash of both output, currently only the main package output hash is calculated
if (EnumHasAnyFlags(WriteOptions, EWriteOptions::ComputeHash) && bContributeToHash)
{
for (const FSharedBuffer& Segment : Buffer.GetSegments())
{
AccumulatedHash.Update(static_cast<const uint8*>(Segment.GetData()), Segment.GetSize());
}
}
if ((bIsSidecar && EnumHasAnyFlags(WriteOptions, EWriteOptions::WriteSidecars)) ||
(!bIsSidecar && EnumHasAnyFlags(WriteOptions, EWriteOptions::WritePackage)))
{
const FString* WriteFilename = &Filename;
FString FilenameBuffer;
if (EnumHasAnyFlags(WriteOptions, EWriteOptions::SaveForDiff))
{
FilenameBuffer = FPaths::Combine(FPaths::GetPath(Filename),
FPaths::GetBaseFilename(Filename) + TEXT("_ForDiff") + FPaths::GetExtension(Filename, true));
WriteFilename = &FilenameBuffer;
}
WriteToFile(*WriteFilename, Buffer);
if (Regions.Num() > 0)
{
TArray<uint8> Memory;
FMemoryWriter Ar(Memory);
FFileRegion::SerializeFileRegions(Ar, const_cast<TArray<FFileRegion>&>(Regions));
WriteToFile(*WriteFilename + FFileRegion::RegionsFileExtension,
FCompositeBuffer(FSharedBuffer::MakeView(Memory.GetData(), Memory.Num())));
}
}
}
#endif

View File

@ -0,0 +1,99 @@
#pragma once
#include "CoreMinimal.h"
#include "Resources/Version.h"
#if WITH_PACKAGE_CONTEXT && ENGINE_MAJOR_VERSION > 4
#include "Serialization/PackageWriter.h"
#include "PackageWriterToSharedBuffer.h"
class FHotPatcherPackageWriter:public TPackageWriterToSharedBuffer<ICookedPackageWriter>
{
public:
virtual FCookCapabilities GetCookCapabilities() const override
{
FCookCapabilities Result;
Result.bDiffModeSupported = true;
return Result;
}
virtual void BeginPackage(const FBeginPackageInfo& Info) override;
virtual void AddToExportsSize(int64& ExportsSize) override;
virtual FDateTime GetPreviousCookTime() const override;
virtual void Initialize(const FCookInfo& Info) override;
virtual void BeginCook() override;
virtual void EndCook() override;
// virtual void Flush() override;
virtual TUniquePtr<FAssetRegistryState> LoadPreviousAssetRegistry()override;
virtual FCbObject GetOplogAttachment(FName PackageName, FUtf8StringView AttachmentKey) override;
virtual void RemoveCookedPackages(TArrayView<const FName> PackageNamesToRemove) override;
virtual void RemoveCookedPackages() override;
virtual void MarkPackagesUpToDate(TArrayView<const FName> UpToDatePackages) override;
virtual bool GetPreviousCookedBytes(const FPackageInfo& Info, FPreviousCookedBytesData& OutData) override;
virtual void CompleteExportsArchiveForDiff(const FPackageInfo& Info, FLargeMemoryWriter& ExportsArchive) override;
virtual void CommitPackageInternal(FPackageRecord&& Record,const IPackageWriter::FCommitPackageInfo& Info)override;
virtual FPackageWriterRecords::FPackage* ConstructRecord() override;
// virtual TFuture<FMD5Hash> CommitPackage(FCommitPackageInfo&& Info)override;
// virtual void WritePackageData(const FPackageInfo& Info, FLargeMemoryWriter& ExportsArchive, const TArray<FFileRegion>& FileRegions) override;
// virtual void WriteBulkData(const FBulkDataInfo& Info, const FIoBuffer& BulkData, const TArray<FFileRegion>& FileRegions) override;
// virtual void WriteLinkerAdditionalData(const FLinkerAdditionalDataInfo& Info, const FIoBuffer& Data, const TArray<FFileRegion>& FileRegions) override;
private:
/** Version of the superclass's per-package record that includes our class-specific data. */
struct FRecord : public FPackageWriterRecords::FPackage
{
bool bCompletedExportsArchiveForDiff = false;
};
/** Buffers that are combined into the HeaderAndExports file (which is then split into .uasset + .uexp or .uoasset + .uoexp). */
struct FExportBuffer
{
FSharedBuffer Buffer;
TArray<FFileRegion> Regions;
};
/**
* The data needed to asynchronously write one of the files (.uasset, .uexp, .ubulk, any optional and any additional),
* without reference back to other data on this writer.
*/
struct FWriteFileData
{
FString Filename;
FCompositeBuffer Buffer;
TArray<FFileRegion> Regions;
bool bIsSidecar;
bool bContributeToHash = true;
void Write(FMD5& AccumulatedHash, EWriteOptions WriteOptions) const;
};
/** Stack data for the helper functions of CommitPackageInternal. */
struct FCommitContext
{
const FCommitPackageInfo& Info;
TArray<TArray<FExportBuffer>> ExportsBuffers;
TArray<FWriteFileData> OutputFiles;
};
void CollectForSavePackageData(FRecord& Record, FCommitContext& Context);
void CollectForSaveBulkData(FRecord& Record, FCommitContext& Context);
void CollectForSaveLinkerAdditionalDataRecords(FRecord& Record, FCommitContext& Context);
void CollectForSaveAdditionalFileRecords(FRecord& Record, FCommitContext& Context);
void CollectForSaveExportsFooter(FRecord& Record, FCommitContext& Context);
void CollectForSaveExportsBuffers(FRecord& Record, FCommitContext& Context);
TMap<FName, TRefCountPtr<FPackageHashes>>& GetPackageHashes() override
{
return AllPackageHashes;
}
// If EWriteOptions::ComputeHash is not set, the package will not get added to this.
TMap<FName, TRefCountPtr<FPackageHashes>> AllPackageHashes;
TFuture<FMD5Hash> AsyncSave(FRecord& Record, const FCommitPackageInfo& Info);
TFuture<FMD5Hash> AsyncSaveOutputFiles(FRecord& Record, FCommitContext& Context);
};
#endif

View File

@ -0,0 +1,207 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "HotWorldPartitionCookPackageSplitter.h"
#if WITH_EDITOR && ENGINE_MAJOR_VERSION > 4
#include "Misc/ConfigCacheIni.h"
#include "WorldPartition/WorldPartitionRuntimeLevelStreamingCell.h"
#include "WorldPartition/WorldPartitionLevelStreamingDynamic.h"
#include "WorldPartition/WorldPartitionRuntimeCell.h"
#include "WorldPartition/WorldPartitionRuntimeHash.h"
#include "WorldPartition/WorldPartitionSubsystem.h"
#include "WorldPartition/WorldPartition.h"
#include "WorldPartition/WorldPartitionLog.h"
#include "Editor.h"
// Register FHotWorldPartitionCookPackageSplitter for UWorld class
// REGISTER_COOKPACKAGE_SPLITTER(FHotWorldPartitionCookPackageSplitter, UWorld);
bool FHotWorldPartitionCookPackageSplitter::ShouldSplit(UObject* SplitData)
{
UWorld* World = Cast<UWorld>(SplitData);
return World && World->IsPartitionedWorld();
}
FHotWorldPartitionCookPackageSplitter::FHotWorldPartitionCookPackageSplitter()
{
}
FHotWorldPartitionCookPackageSplitter::~FHotWorldPartitionCookPackageSplitter()
{
check(!ReferencedWorld);
}
void FHotWorldPartitionCookPackageSplitter::Teardown(ETeardown Status)
{
if (bInitializedWorldPartition)
{
if (UWorld* LocalWorld = ReferencedWorld.Get())
{
UWorldPartition* WorldPartition = LocalWorld->PersistentLevel->GetWorldPartition();
if (WorldPartition)
{
WorldPartition->Uninitialize();
}
}
bInitializedWorldPartition = false;
}
if (bInitializedPhysicsSceneForSave)
{
GEditor->CleanupPhysicsSceneThatWasInitializedForSave(ReferencedWorld.Get(), bForceInitializedWorld);
bInitializedPhysicsSceneForSave = false;
bForceInitializedWorld = false;
}
ReferencedWorld = nullptr;
}
void FHotWorldPartitionCookPackageSplitter::AddReferencedObjects(FReferenceCollector& Collector)
{
Collector.AddReferencedObject(ReferencedWorld);
}
FString FHotWorldPartitionCookPackageSplitter::GetReferencerName() const
{
return TEXT("FHotWorldPartitionCookPackageSplitter");
}
UWorld* FHotWorldPartitionCookPackageSplitter::ValidateDataObject(UObject* SplitData)
{
UWorld* PartitionedWorld = CastChecked<UWorld>(SplitData);
check(PartitionedWorld);
check(PartitionedWorld->PersistentLevel);
check(PartitionedWorld->IsPartitionedWorld());
return PartitionedWorld;
}
const UWorld* FHotWorldPartitionCookPackageSplitter::ValidateDataObject(const UObject* SplitData)
{
return ValidateDataObject(const_cast<UObject*>(SplitData));
}
TArray<ICookPackageSplitter::FGeneratedPackage> FHotWorldPartitionCookPackageSplitter::GetGenerateList(const UPackage* OwnerPackage, const UObject* OwnerObject)
{
// TODO: Make WorldPartition functions const so we can honor the constness of the OwnerObject in this API function
const UWorld* ConstPartitionedWorld = ValidateDataObject(OwnerObject);
UWorld* PartitionedWorld = const_cast<UWorld*>(ConstPartitionedWorld);
// Store the World pointer to declare it to GarbageCollection; we do not want to allow the World to be Garbage Collected
// until we have finished all of our PreSaveGeneratedPackage calls, because we store information on the World
// that is necessary for populate
ReferencedWorld = PartitionedWorld;
check(!bInitializedPhysicsSceneForSave && !bForceInitializedWorld);
bInitializedPhysicsSceneForSave = GEditor->InitializePhysicsSceneForSaveIfNecessary(PartitionedWorld, bForceInitializedWorld);
// Manually initialize WorldPartition
UWorldPartition* WorldPartition = PartitionedWorld->PersistentLevel->GetWorldPartition();
// We expect the WorldPartition has not yet been initialized
ensure(!WorldPartition->IsInitialized());
WorldPartition->Initialize(PartitionedWorld, FTransform::Identity);
bInitializedWorldPartition = true;
WorldPartition->BeginCook(CookContext);
bool bIsSuccess = true;
for (IWorldPartitionCookPackageGenerator* CookPackageGenerator : CookContext.GetCookPackageGenerators())
{
bIsSuccess &= CookPackageGenerator->GatherPackagesToCook(CookContext);
}
UE_LOG(LogHotWorldPartition, Log, TEXT("[Cook] Gathered %u packages to generate from %u Generators."), CookContext.NumPackageToGenerate(), CookContext.NumGenerators());
TArray<ICookPackageSplitter::FGeneratedPackage> PackagesToGenerate;
BuildPackagesToGenerateList(PackagesToGenerate);
UE_LOG(LogHotWorldPartition, Log, TEXT("[Cook] Sending %u packages to be generated."), PackagesToGenerate.Num());
return PackagesToGenerate;
}
bool FHotWorldPartitionCookPackageSplitter::PopulateGeneratedPackage(UPackage* OwnerPackage, UObject* OwnerObject,
const FGeneratedPackageForPopulate& GeneratedPackage, TArray<UObject*>& OutObjectsToMove,
TArray<UPackage*>& OutModifiedPackages)
{
UE_LOG(LogHotWorldPartition, Verbose, TEXT("[Cook][PopulateGeneratedPackage] Processing %s"), *FWorldPartitionCookPackage::MakeGeneratedFullPath(GeneratedPackage.GeneratedRootPath, GeneratedPackage.RelativePath));
bool bIsSuccess = true;
IWorldPartitionCookPackageGenerator* CookPackageGenerator = nullptr;
FWorldPartitionCookPackage* CookPackage = nullptr;
TArray<UPackage*> ModifiedPackages;
if (CookContext.GetCookPackageGeneratorAndPackage(GeneratedPackage.GeneratedRootPath, GeneratedPackage.RelativePath, CookPackageGenerator, CookPackage))
{
bIsSuccess = CookPackageGenerator->PopulateGeneratedPackageForCook(CookContext, *CookPackage, ModifiedPackages);
}
else
{
UE_LOG(LogHotWorldPartition, Error, TEXT("[Cook][PopulateGeneratedPackage] Could not find WorldPartitionCookPackage for %s"), *FWorldPartitionCookPackage::MakeGeneratedFullPath(GeneratedPackage.GeneratedRootPath, GeneratedPackage.RelativePath));
bIsSuccess = false;
}
UE_LOG(LogHotWorldPartition, Verbose, TEXT("[Cook][PopulateGeneratedPackage] Gathered %u modified packages for %s"), ModifiedPackages.Num() , *FWorldPartitionCookPackage::MakeGeneratedFullPath(GeneratedPackage.GeneratedRootPath, GeneratedPackage.RelativePath));
OutModifiedPackages = MoveTemp(ModifiedPackages);
return bIsSuccess;
}
bool FHotWorldPartitionCookPackageSplitter::PopulateGeneratorPackage(UPackage* OwnerPackage, UObject* OwnerObject,
const TArray<ICookPackageSplitter::FGeneratedPackageForPreSave>& GeneratedPackages, TArray<UObject*>& OutObjectsToMove,
TArray<UPackage*>& OutModifiedPackages)
{
UE_LOG(LogHotWorldPartition, Log, TEXT("[Cook][PopulateGeneratorPackage] Processing %u packages"), GeneratedPackages.Num());
bool bIsSuccess = true;
if (GeneratedPackages.Num() != CookContext.NumPackageToGenerate())
{
UE_LOG(LogHotWorldPartition, Error, TEXT("[Cook][PopulateGeneratorPackage] Receieved %u generated packages. Was expecting %u"), GeneratedPackages.Num(), CookContext.NumPackageToGenerate());
bIsSuccess = false;
}
TArray<UPackage*> ModifiedPackages;
for (IWorldPartitionCookPackageGenerator* CookPackageGenerator : CookContext.GetCookPackageGenerators())
{
bIsSuccess &= CookPackageGenerator->PrepareGeneratorPackageForCook(CookContext, ModifiedPackages);
if (const TArray<FWorldPartitionCookPackage*>* CookPackages = CookContext.GetCookPackages(CookPackageGenerator))
{
bIsSuccess &= CookPackageGenerator->PopulateGeneratorPackageForCook(CookContext, *CookPackages, ModifiedPackages);
}
}
UE_LOG(LogHotWorldPartition, Log, TEXT("[Cook][PopulateGeneratorPackage] Gathered %u modified packages"), ModifiedPackages.Num());
OutModifiedPackages = MoveTemp(ModifiedPackages);
return bIsSuccess;
}
void FHotWorldPartitionCookPackageSplitter::OnOwnerReloaded(UPackage* OwnerPackage, UObject* OwnerObject)
{
// It should not be possible for the owner to reload due to garbage collection while we are active and keeping it referenced
check(!ReferencedWorld);
}
void FHotWorldPartitionCookPackageSplitter::BuildPackagesToGenerateList(TArray<ICookPackageSplitter::FGeneratedPackage>& PackagesToGenerate) const
{
for (const IWorldPartitionCookPackageGenerator* CookPackageGenerator : CookContext.GetCookPackageGenerators())
{
if (const TArray<FWorldPartitionCookPackage*>* CookPackages = CookContext.GetCookPackages(CookPackageGenerator))
{
PackagesToGenerate.Reserve(CookPackages->Num());
for (const FWorldPartitionCookPackage* CookPackage : *CookPackages)
{
ICookPackageSplitter::FGeneratedPackage& GeneratedPackage = PackagesToGenerate.Emplace_GetRef();
GeneratedPackage.GeneratedRootPath = CookPackage->Root;
GeneratedPackage.RelativePath = CookPackage->RelativePath;
CookPackage->Type == FWorldPartitionCookPackage::EType::Level ? GeneratedPackage.SetCreateAsMap(true) : GeneratedPackage.SetCreateAsMap(false);
// @todo_ow: Set dependencies once we get iterative cooking working
}
}
}
}
#endif

View File

@ -0,0 +1,53 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Resources/Version.h"
#if WITH_EDITOR && ENGINE_MAJOR_VERSION > 4
#include "CookPackageSplitter.h"
#include "Engine/World.h"
#include "UObject/WeakObjectPtrTemplates.h"
#include "WorldPartition/Cook/WorldPartitionCookPackageContext.h"
class FHotWorldPartitionCookPackageSplitter : public FGCObject, public ICookPackageSplitter
{
public:
//~ Begin of ICookPackageSplitter
static bool ShouldSplit(UObject* SplitData);
FHotWorldPartitionCookPackageSplitter();
virtual ~FHotWorldPartitionCookPackageSplitter();
virtual void Teardown(ETeardown Status) override;
virtual bool UseInternalReferenceToAvoidGarbageCollect() override { return true; }
virtual TArray<ICookPackageSplitter::FGeneratedPackage> GetGenerateList(const UPackage* OwnerPackage, const UObject* OwnerObject) override;
virtual bool PopulateGeneratedPackage(UPackage* OwnerPackage, UObject* OwnerObject,
const FGeneratedPackageForPopulate& GeneratedPackage, TArray<UObject*>& OutObjectsToMove, TArray<UPackage*>& OutModifiedPackages) override;
virtual bool PopulateGeneratorPackage(UPackage* OwnerPackage, UObject* OwnerObject,
const TArray<ICookPackageSplitter::FGeneratedPackageForPreSave>& GeneratedPackages, TArray<UObject*>& OutObjectsToMove,
TArray<UPackage*>& OutModifiedPackages) override;
virtual void OnOwnerReloaded(UPackage* OwnerPackage, UObject* OwnerObject) override;
//~ End of ICookPackageSplitter
private:
/** FGCObject interface */
virtual void AddReferencedObjects(FReferenceCollector& Collector) override;
virtual FString GetReferencerName() const override;
const UWorld* ValidateDataObject(const UObject* SplitData);
UWorld* ValidateDataObject(UObject* SplitData);
void BuildPackagesToGenerateList(TArray<ICookPackageSplitter::FGeneratedPackage>& PackagesToGenerate) const;
bool MapGeneratePackageToCookPackage(const TArray<ICookPackageSplitter::FGeneratedPackageForPreSave>& GeneratedPackages);
TObjectPtr<UWorld> ReferencedWorld = nullptr;
FWorldPartitionCookPackageContext CookContext;
bool bInitializedWorldPartition = false;
bool bForceInitializedWorld = false;
bool bInitializedPhysicsSceneForSave = false;
};
#endif

View File

@ -0,0 +1,84 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "WorldPartitionCookPackageContext.h"
#if WITH_EDITOR && !UE_VERSION_OLDER_THAN(5,1,0)
#include "WorldPartition/WorldPartitionLog.h"
#include "WorldPartition/Cook/WorldPartitionCookPackageGenerator.h"
DEFINE_LOG_CATEGORY(LogHotWorldPartition)
FWorldPartitionCookPackageContext::FWorldPartitionCookPackageContext()
{
}
void FWorldPartitionCookPackageContext::RegisterPackageCookPackageGenerator(IWorldPartitionCookPackageGenerator* CookPackageGenerator)
{
check(!CookPackageGenerators.Contains(CookPackageGenerator));
CookPackageGenerators.Add(CookPackageGenerator);
check(!PackagesToCookByGenerator.Contains(CookPackageGenerator));
}
const FWorldPartitionCookPackage* FWorldPartitionCookPackageContext::AddLevelStreamingPackageToGenerate(IWorldPartitionCookPackageGenerator* CookPackageGenerator, const FString& Root, const FString& RelativePath)
{
return AddPackageToGenerateInternal(CookPackageGenerator, Root, RelativePath, FWorldPartitionCookPackage::EType::Level);
}
const FWorldPartitionCookPackage* FWorldPartitionCookPackageContext::AddGenericPackageToGenerate(IWorldPartitionCookPackageGenerator* CookPackageGenerator, const FString& Root, const FString& RelativePath)
{
return AddPackageToGenerateInternal(CookPackageGenerator, Root, RelativePath, FWorldPartitionCookPackage::EType::Generic);
}
bool FWorldPartitionCookPackageContext::GetCookPackageGeneratorAndPackage(const FString& PackageRoot, const FString& PackageRelativePath, IWorldPartitionCookPackageGenerator*& CookPackageGenerator, FWorldPartitionCookPackage*& CookPackage)
{
FWorldPartitionCookPackage::IDType PackageId = FWorldPartitionCookPackage::MakeCookPackageID(PackageRoot, PackageRelativePath);
if (IWorldPartitionCookPackageGenerator** GeneratorPtr = CookGeneratorByPackageId.Find(PackageId))
{
if (TUniquePtr<FWorldPartitionCookPackage>* PackagePtr = PackagesToCookById.Find(PackageId))
{
check((*PackagePtr)->Root.Equals(PackageRoot, ESearchCase::IgnoreCase) && (*PackagePtr)->RelativePath.Equals(PackageRelativePath, ESearchCase::IgnoreCase));
CookPackageGenerator = *GeneratorPtr;
CookPackage = (*PackagePtr).Get();
return true;
}
}
return false;
}
const FWorldPartitionCookPackage* FWorldPartitionCookPackageContext::AddPackageToGenerateInternal(IWorldPartitionCookPackageGenerator* CookPackageGenerator, const FString& Root, const FString& RelativePath, FWorldPartitionCookPackage::EType Type)
{
if (CookPackageGenerators.Contains(CookPackageGenerator))
{
FWorldPartitionCookPackage::IDType PackageId = FWorldPartitionCookPackage::MakeCookPackageID(FWorldPartitionCookPackage::SanitizePathComponent(Root), FWorldPartitionCookPackage::SanitizePathComponent(RelativePath));
TUniquePtr<FWorldPartitionCookPackage>* ExistingPackage = PackagesToCookById.Find(PackageId);
if (ExistingPackage == nullptr)
{
TUniquePtr<FWorldPartitionCookPackage>& CookPackage = PackagesToCookById.Emplace(PackageId, MakeUnique<FWorldPartitionCookPackage>(FWorldPartitionCookPackage::SanitizePathComponent(Root), FWorldPartitionCookPackage::SanitizePathComponent(RelativePath), Type));
check(PackageId == CookPackage->PackageId);
CookGeneratorByPackageId.Add(PackageId, CookPackageGenerator);
TArray<FWorldPartitionCookPackage*>& PackagesToCookForHandler = PackagesToCookByGenerator.FindOrAdd(CookPackageGenerator);
PackagesToCookForHandler.Add(CookPackage.Get());
UE_LOG(LogHotWorldPartition, Verbose, TEXT("[Cook] Added Package %s with ID %llu in context"), *CookPackage->GetFullGeneratedPath(), PackageId);
return CookPackage.Get();
}
else
{
UE_LOG(LogHotWorldPartition, Error, TEXT("[Cook] Trying to add package %s in context but there is already a package to generate with the same ID (%llu). Other package: %s Id %llu"),
*FWorldPartitionCookPackage::MakeGeneratedFullPath(Root, RelativePath), PackageId, *(*ExistingPackage)->GetFullGeneratedPath(), (*ExistingPackage)->PackageId);
}
}
else
{
UE_LOG(LogHotWorldPartition, Error, TEXT("[Cook] Trying to add package %s in context, but its generator is not registered."), *FWorldPartitionCookPackage::MakeGeneratedFullPath(Root, RelativePath));
}
return nullptr;
}
#endif

View File

@ -0,0 +1,48 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Misc/EngineVersionComparison.h"
#if WITH_EDITOR && !UE_VERSION_OLDER_THAN(5,1,0)
#include "WorldPartition/Cook/WorldPartitionCookPackageContextInterface.h"
#include "WorldPartition/Cook/WorldPartitionCookPackage.h"
DECLARE_LOG_CATEGORY_EXTERN(LogHotWorldPartition,All,All);
class IWorldPartitionCookPackageGenerator;
class FWorldPartitionCookPackageContext : public IWorldPartitionCookPackageContext
{
public:
FWorldPartitionCookPackageContext();
//~ Begin IWorldPartitionCookPackageContext Interface
virtual void RegisterPackageCookPackageGenerator(IWorldPartitionCookPackageGenerator* CookPackageGenerator) override;
virtual void UnregisterPackageCookPackageGenerator(IWorldPartitionCookPackageGenerator* CookPackageGenerator) override { check(0); /*No use case*/ }
virtual const FWorldPartitionCookPackage* AddLevelStreamingPackageToGenerate(IWorldPartitionCookPackageGenerator* CookPackageGenerator, const FString& Root, const FString& RelativePath) override;
virtual const FWorldPartitionCookPackage* AddGenericPackageToGenerate(IWorldPartitionCookPackageGenerator* CookPackageGenerator, const FString& Root, const FString& RelativePath) override;
//~ End IWorldPartitionCookPackageContext Interface
const TArray<FWorldPartitionCookPackage*>* GetCookPackages(const IWorldPartitionCookPackageGenerator* CookPackageGenerator) const { return PackagesToCookByGenerator.Find(CookPackageGenerator); }
bool GetCookPackageGeneratorAndPackage(const FString& PackageRoot, const FString& PackageRelativePath, IWorldPartitionCookPackageGenerator*& CookPackageGenerator, FWorldPartitionCookPackage*& CookPackage);
uint32 NumPackageToGenerate() const { return PackagesToCookById.Num(); }
uint32 NumGenerators() const { return CookPackageGenerators.Num(); }
TArray<IWorldPartitionCookPackageGenerator*>& GetCookPackageGenerators() { return CookPackageGenerators; }
const TArray<IWorldPartitionCookPackageGenerator*>& GetCookPackageGenerators() const { return CookPackageGenerators; }
private:
const FWorldPartitionCookPackage* AddPackageToGenerateInternal(IWorldPartitionCookPackageGenerator* CookPackageGenerator, const FString& Root, const FString& RelativePath, FWorldPartitionCookPackage::EType Type);
TArray<IWorldPartitionCookPackageGenerator*> CookPackageGenerators;
TMap<FWorldPartitionCookPackage::IDType, TUniquePtr<FWorldPartitionCookPackage>> PackagesToCookById;
TMap<FWorldPartitionCookPackage::IDType, IWorldPartitionCookPackageGenerator*> CookGeneratorByPackageId;
TMap<IWorldPartitionCookPackageGenerator*, TArray<FWorldPartitionCookPackage*>> PackagesToCookByGenerator;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,242 @@
#include "CreatePatch/ReleaseProxy.h"
#include "CreatePatch/ScopedSlowTaskContext.h"
#include "FHotPatcherVersion.h"
#include "FlibPatchParserHelper.h"
#include "HotPatcherLog.h"
// engine header
#include "FlibHotPatcherCoreHelper.h"
#include "Misc/DateTime.h"
#include "CoreGlobals.h"
#include "Logging/LogMacros.h"
#include "CreatePatch/HotPatcherContext.h"
#define LOCTEXT_NAMESPACE "UExportRelease"
namespace ReleaseWorker
{
// inport from paklist
bool ImportPakListWorker(FHotPatcherReleaseContext& Context);
bool ImportProjectSettingsWorker(FHotPatcherReleaseContext& Context);
// scan new release
bool ExportNewReleaseWorker(FHotPatcherReleaseContext& Context);
// save release asset info
bool SaveReleaseVersionWorker(FHotPatcherReleaseContext& Context);
bool SaveReleaseConfigWorker(FHotPatcherReleaseContext& Context);
// backup Metadata
bool BakcupMetadataWorker(FHotPatcherReleaseContext& Context);
// backup config
bool BakcupProjectConfigWorker(FHotPatcherReleaseContext& Context);
// display release summary
bool ReleaseSummaryWorker(FHotPatcherReleaseContext& Context);
};
bool UReleaseProxy::DoExport()
{
// TimeRecorder TotalTimeTR(TEXT("Generate the release total time"));
GetSettingObject()->Init();
bool bRet = true;
FHotPatcherReleaseContext ReleaseContext;
ReleaseContext.ContextSetting = GetSettingObject();
ReleaseContext.UnrealPakSlowTask = NewObject<UScopedSlowTaskContext>();
ReleaseContext.UnrealPakSlowTask->AddToRoot();
ReleaseContext.Init();
ReleaseContext.ReleaseProxy = this;
TArray<TFunction<bool(FHotPatcherReleaseContext&)>> ReleaseWorker;
ReleaseWorker.Emplace(&::ReleaseWorker::SaveReleaseConfigWorker);
ReleaseWorker.Emplace(&::ReleaseWorker::ImportPakListWorker);
ReleaseWorker.Emplace(&::ReleaseWorker::ImportProjectSettingsWorker);
ReleaseWorker.Emplace(&::ReleaseWorker::ExportNewReleaseWorker);
ReleaseWorker.Emplace(&::ReleaseWorker::SaveReleaseVersionWorker);
ReleaseWorker.Emplace(&::ReleaseWorker::BakcupMetadataWorker);
ReleaseWorker.Emplace(&::ReleaseWorker::BakcupProjectConfigWorker);
ReleaseWorker.Emplace(&::ReleaseWorker::ReleaseSummaryWorker);
ReleaseContext.UnrealPakSlowTask->init((float)ReleaseWorker.Num());
for(TFunction<bool(FHotPatcherReleaseContext&)> Worker:ReleaseWorker)
{
if(Worker(ReleaseContext))
{
continue;
}
else
{
bRet = false;
break;
}
}
ReleaseContext.UnrealPakSlowTask->Final();
ReleaseContext.Shurdown();
return bRet;
}
namespace ReleaseWorker
{
bool ImportPakListWorker(FHotPatcherReleaseContext& Context)
{
FText DiaLogMsg = NSLOCTEXT("ImportPakListWorker", "ImportPakListWorker", "Import Pak List.");
Context.UnrealPakSlowTask->EnterProgressFrame(1.0, DiaLogMsg);
TimeRecorder TotalTimeTR(TEXT("Import Paklist"));
if(Context.GetSettingObject()->ByPakList)
{
if(Context.GetSettingObject()->PlatformsPakListFiles.Num())
{
Context.GetSettingObject()->ImportPakLists();
}
}
return true;
}
bool ImportProjectSettingsWorker(FHotPatcherReleaseContext& Context)
{
if(Context.GetSettingObject()->IsImportProjectSettings())
{
UFlibHotPatcherCoreHelper::ImportProjectSettingsToScannerConfig(Context.GetSettingObject()->GetAssetScanConfigRef());
ETargetPlatform AddProjectDirToTarget = ETargetPlatform::AllPlatforms;
TSet<ETargetPlatform> AllConfigPlatformSet;
for(const auto& PlatformsPakListFile:Context.GetSettingObject()->PlatformsPakListFiles)
{
AllConfigPlatformSet.Add(PlatformsPakListFile.TargetPlatform);
}
if(AllConfigPlatformSet.Num())
{
AddProjectDirToTarget = AllConfigPlatformSet.Num() > 1 ? ETargetPlatform::AllPlatforms : AllConfigPlatformSet.Array()[0];
}
UFlibHotPatcherCoreHelper::ImportProjectNotAssetDir(Context.GetSettingObject()->GetAddExternAssetsToPlatform(),AddProjectDirToTarget);
}
return true;
}
bool ExportNewReleaseWorker(FHotPatcherReleaseContext& Context)
{
TimeRecorder TotalTimeTR(TEXT("Export Release Version Info"));
TArray<FString> AllSkipContent;
if(Context.GetSettingObject()->IsForceSkipContent())
{
AllSkipContent = Context.GetSettingObject()->GetAllSkipContents();
}
FText DiaLogMsg = FText::Format(NSLOCTEXT("AnalysisRelease", "AnalysisReleaseVersionInfo", "Analysis Release {0} Assets info."), FText::FromString(Context.GetSettingObject()->GetVersionId()));
Context.UnrealPakSlowTask->EnterProgressFrame(1.0, DiaLogMsg);
{
Context.NewReleaseVersion.VersionId = Context.GetSettingObject()->GetVersionId();
Context.NewReleaseVersion.Date = FDateTime::UtcNow().ToString();
Context.NewReleaseVersion.BaseVersionId = TEXT("");
}
UFlibPatchParserHelper::RunAssetScanner(Context.GetSettingObject()->GetAssetScanConfig(),Context.NewReleaseVersion);
UFlibPatchParserHelper::ExportExternAssetsToPlatform(Context.GetSettingObject()->GetAddExternAssetsToPlatform(),Context.NewReleaseVersion,true,Context.GetSettingObject()->GetHashCalculator());
return true;
}
// save release asset info
bool SaveReleaseVersionWorker(FHotPatcherReleaseContext& Context)
{
TimeRecorder TR(TEXT("Save new release version info"));
FText DiaLogMsg = FText::Format(NSLOCTEXT("ExportReleaseJson", "ExportReleaseVersionInfoJson", "Export Release {0} Assets info to file."), FText::FromString(Context.GetSettingObject()->GetVersionId()));
Context.UnrealPakSlowTask->EnterProgressFrame(1.0, DiaLogMsg);
FString SaveToJson;
if (THotPatcherTemplateHelper::TSerializeStructAsJsonString(Context.NewReleaseVersion, SaveToJson))
{
FString SaveToFile = FPaths::Combine(
FPaths::Combine(Context.GetSettingObject()->GetSaveAbsPath(), Context.GetSettingObject()->GetVersionId()),
FString::Printf(TEXT("%s_Release.json"), *Context.GetSettingObject()->GetVersionId())
);
bool runState = UFlibAssetManageHelper::SaveStringToFile(SaveToFile, SaveToJson);
if (runState)
{
auto Message = LOCTEXT("ExportReleaseSuccessNotification", "Succeed to export HotPatcher Release Version.");
if(IsRunningCommandlet())
{
Context.OnShowMsg.Broadcast(Message.ToString());
}
else
{
FHotPatcherDelegates::Get().GetNotifyFileGenerated().Broadcast(Message, SaveToFile);
}
}
UE_LOG(LogHotPatcher, Log, TEXT("HotPatcher Export RELEASE is %s."), runState ? TEXT("Success") : TEXT("FAILD"));
}
return true;
}
bool SaveReleaseConfigWorker(FHotPatcherReleaseContext& Context)
{
TimeRecorder TR(TEXT("Save new release config"));
FText DiaLogMsg = FText::Format(NSLOCTEXT("ExportReleaseConfig", "ExportReleaseConfigJson", "Export Release {0} Configuration to file."), FText::FromString(Context.GetSettingObject()->GetVersionId()));
Context.UnrealPakSlowTask->EnterProgressFrame(1.0, DiaLogMsg);
FString ConfigJson;
if (THotPatcherTemplateHelper::TSerializeStructAsJsonString(*Context.GetSettingObject(),ConfigJson))
{
FString SaveToFile = FPaths::Combine(
FPaths::Combine(Context.GetSettingObject()->GetSaveAbsPath(), Context.GetSettingObject()->GetVersionId()),
FString::Printf(TEXT("%s_ReleaseConfig.json"), *Context.GetSettingObject()->GetVersionId())
);
bool runState = UFlibAssetManageHelper::SaveStringToFile(SaveToFile, ConfigJson);
if (runState)
{
auto Message = LOCTEXT("ExportReleaseConfigSuccessNotification", "Succeed to export HotPatcher Release Config.");
if(::IsRunningCommandlet())
{
Context.OnShowMsg.Broadcast(Message.ToString());
}
else
{
FHotPatcherDelegates::Get().GetNotifyFileGenerated().Broadcast(Message, SaveToFile);
}
}
UE_LOG(LogHotPatcher, Log, TEXT("HotPatcher Export RELEASE CONFIG is %s."), runState ? TEXT("Success") : TEXT("FAILD"));
}
return true;
}
// backup Metadata
bool BakcupMetadataWorker(FHotPatcherReleaseContext& Context)
{
TimeRecorder TR(TEXT("Backup Metadata"));
FText DiaLogMsg = FText::Format(NSLOCTEXT("BackupMetadata", "BackupMetadata", "Backup Release {0} Metadatas."), FText::FromString(Context.GetSettingObject()->GetVersionId()));
Context.UnrealPakSlowTask->EnterProgressFrame(1.0, DiaLogMsg);
if(Context.GetSettingObject()->IsBackupMetadata())
{
UFlibHotPatcherCoreHelper::BackupMetadataDir(
FPaths::ProjectDir(),
FApp::GetProjectName(),
Context.GetSettingObject()->GetBackupMetadataPlatforms(),
FPaths::Combine(Context.GetSettingObject()->GetSaveAbsPath(),
Context.GetSettingObject()->GetVersionId())
);
}
return true;
}
// backup project config
bool BakcupProjectConfigWorker(FHotPatcherReleaseContext& Context)
{
TimeRecorder TR(TEXT("Backup Config"));
FText DiaLogMsg = FText::Format(NSLOCTEXT("BackupProjectConfig", "BackupProjectConfig", "Backup Release {0} Configs."), FText::FromString(Context.GetSettingObject()->GetVersionId()));
Context.UnrealPakSlowTask->EnterProgressFrame(1.0, DiaLogMsg);
if(Context.GetSettingObject()->IsBackupProjectConfig())
{
UFlibHotPatcherCoreHelper::BackupProjectConfigDir(
FPaths::ProjectDir(),
FPaths::Combine(Context.GetSettingObject()->GetSaveAbsPath(),Context.GetSettingObject()->GetVersionId())
);
}
return true;
}
bool ReleaseSummaryWorker(FHotPatcherReleaseContext& Context)
{
FText DiaLogMsg = NSLOCTEXT("ReleaseSummaryWorker", "ReleaseSummaryWorker", "Release Summary.");
Context.UnrealPakSlowTask->EnterProgressFrame(1.0, DiaLogMsg);
TimeRecorder TR(TEXT("Generate Release Summary"));
Context.OnShowMsg.Broadcast(UFlibHotPatcherCoreHelper::ReleaseSummary(Context.NewReleaseVersion));
return true;
}
};
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,196 @@
#pragma once
#include "FCountServerlessWrapper.h"
#include "Misc/App.h"
#include "Misc/Base64.h"
#include "HttpModule.h"
#include "SocketSubsystem.h"
#include "Interfaces/IHttpRequest.h"
#include "Interfaces/IHttpResponse.h"
#include "Serialization/JsonReader.h"
#include "Serialization/JsonSerializer.h"
#include "Resources/Version.h"
#include "CoreGlobals.h"
#include "HttpManager.h"
FProjectVersionDesc FCountServerlessWrapper::MakeCurrentProject()
{
FProjectVersionDesc result;
result.ProjectName = FApp::GetProjectName();
result.GameName = GConfig->GetStr(TEXT("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings"),TEXT("ApplicationDisplayName"),GEngineIni);
result.EngineVersion = FString::Printf(TEXT("%d.%d.%d"),ENGINE_MAJOR_VERSION,ENGINE_MINOR_VERSION,ENGINE_PATCH_VERSION);
static FString HostName;
if(HostName.IsEmpty())
{
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetHostName(HostName);
}
result.UserName = HostName;
return result;
}
#define COUNTER_API_HOST TEXT("https://appcounter.imzlp.com/1.1/classes/HotPatcher")
#define COUNTER_APPID TEXT("Y1diamRWR3pkRlVWcEtNSWkwMFFGdGVxLWd6R3pvSHN6")
#define COUNTER_KEY TEXT("M1VrUldPaHhsRThGcnBKSUZpNlUxNExI")
FServerRequestInfo FCountServerlessWrapper::MakeServerRequestInfo()
{
FServerRequestInfo Info;
Info.Host = COUNTER_API_HOST;
Info.Key = COUNTER_KEY;
Info.AppId = COUNTER_APPID;
return Info;
}
void CancelRequest(FHttpRequestPtr& Request)
{
if(FHttpModule::Get().GetHttpManager().IsValidRequest(Request.Get()))
{
Request->CancelRequest();
}
}
FCountServerlessWrapper::~FCountServerlessWrapper()
{
CancelRequest(ObjectIDRequest);
CancelRequest(ToServerRequest);
}
void FCountServerlessWrapper::Processor()
{
if(!UE_BUILD_SHIPPING)
{
RequestObjectID();
}
}
void FCountServerlessWrapper::RequestObjectID()
{
CancelRequest(ObjectIDRequest);
FHttpModule::Get().SetHttpTimeout(5.0);
ObjectIDRequest = FHttpModule::Get().CreateRequest();
ObjectIDRequest->OnProcessRequestComplete().BindRaw(this, &FCountServerlessWrapper::OnObjectIdReceived);
ObjectIDRequest->SetURL(RequestInfo.Host);
ObjectIDRequest->SetHeader(TEXT("X-LC-Id"),Decode(RequestInfo.AppId));
ObjectIDRequest->SetHeader(TEXT("X-LC-Key"),Decode(RequestInfo.Key));
ObjectIDRequest->SetHeader(TEXT("Content-Type"),TEXT("application/json"));
ObjectIDRequest->SetVerb(TEXT("GET"));
ObjectIDRequest->ProcessRequest();
}
void FCountServerlessWrapper::OnObjectIdReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess)
{
TMap<FString,FString> NameIdMaps;
int32 Count = 0;
if(bSuccess)
{
FString ResponseStr = Response->GetContentAsString();
TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(ResponseStr);
TSharedPtr<FJsonObject> DeserializeJsonObject;
if (FJsonSerializer::Deserialize(JsonReader, DeserializeJsonObject))
{
const TArray< TSharedPtr<FJsonValue>>* JsonValues;
bool bDeserialize = DeserializeJsonObject->TryGetArrayField(TEXT("results"),JsonValues);
if(bDeserialize)
{
for(TSharedPtr<FJsonValue> JsonValue:*JsonValues)
{
auto ChildJsonValue =JsonValue->AsObject();
FString ProjectName = ChildJsonValue->GetStringField(TEXT("ProjectName"));
FString objectId = ChildJsonValue->GetStringField(TEXT("objectId"));
if(!NameIdMaps.Contains(ProjectName))
{
Count++;
}
NameIdMaps.Add(ProjectName,objectId);
}
}
}
if(NameIdMaps.Contains(FApp::GetProjectName()))
{
ToServerRequest = UpdateToServer(Desc,*NameIdMaps.Find(FApp::GetProjectName()));
}
else if(!!Count)
{
ToServerRequest = CreateToServer(Desc);
}
}
}
FHttpRequestPtr FCountServerlessWrapper::UpdateToServer(const FProjectVersionDesc& InDesc,const FString& ObjectID)
{
FString ContentJsonStr = FString::Printf(
TEXT("{\"ProjectName\": \"%s\",\"GameName\":\"%s\",\"EngineVersion\":\"%s\",\"PluginVersion\":\"%s\",\"UserName\":\"%s\",%s}"),
*InDesc.ProjectName,
*InDesc.GameName,
*InDesc.EngineVersion,
*InDesc.PluginVersion,
*InDesc.UserName,
TEXT("\"Count\":{\"__op\":\"Increment\",\"amount\":1}")
);
FHttpRequestPtr CreateToServerRequest = FHttpModule::Get().CreateRequest();
FString UpdateObjectURL = FString::Printf(TEXT("%s/%s"),*RequestInfo.Host,*ObjectID);
CreateToServerRequest->SetURL(UpdateObjectURL);
CreateToServerRequest->SetHeader(TEXT("X-LC-Id"),Decode(RequestInfo.AppId));
CreateToServerRequest->SetHeader(TEXT("X-LC-Key"),Decode(RequestInfo.Key));
CreateToServerRequest->SetHeader(TEXT("Content-Type"),TEXT("application/json"));
CreateToServerRequest->SetContentAsString(ContentJsonStr);
CreateToServerRequest->SetVerb(TEXT("PUT"));
CreateToServerRequest->OnProcessRequestComplete().BindRaw(this, &FCountServerlessWrapper::OnUpdateToServerReceived);
CreateToServerRequest->ProcessRequest();
return CreateToServerRequest;
}
void FCountServerlessWrapper::OnUpdateToServerReceived(FHttpRequestPtr Request, FHttpResponsePtr Response,
bool bSuccess)
{
if(bSuccess)
{
FString ResponseStr = Response->GetContentAsString();
}
}
FHttpRequestPtr FCountServerlessWrapper::CreateToServer(const FProjectVersionDesc& InDesc)
{
FString ContentJsonStr = FString::Printf(
TEXT("{\"ProjectName\": \"%s\",\"GameName\":\"%s\",\"EngineVersion\":\"%s\",\"PluginVersion\":\"%s\",\"UserName\":\"%s\",\"Count\":%d}"),
*InDesc.ProjectName,
*InDesc.GameName,
*InDesc.EngineVersion,
*InDesc.PluginVersion,
*InDesc.UserName,
1
);
FHttpRequestPtr UpdateToServerRequest = FHttpModule::Get().CreateRequest();
UpdateToServerRequest->SetURL(RequestInfo.Host);
UpdateToServerRequest->SetHeader(TEXT("X-LC-Id"),Decode(RequestInfo.AppId));
UpdateToServerRequest->SetHeader(TEXT("X-LC-Key"),Decode(RequestInfo.Key));
UpdateToServerRequest->SetHeader(TEXT("Content-Type"),TEXT("application/json"));
UpdateToServerRequest->SetContentAsString(ContentJsonStr);
UpdateToServerRequest->SetVerb(TEXT("POST"));
UpdateToServerRequest->OnProcessRequestComplete().BindRaw(this, &FCountServerlessWrapper::CreateToServerReceived);
UpdateToServerRequest->ProcessRequest();
return UpdateToServerRequest;
}
void FCountServerlessWrapper::CreateToServerReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess)
{
if(bSuccess)
{
FString ResponseStr = Response->GetContentAsString();
}
}
FString FCountServerlessWrapper::Decode(const FString& Encode)
{
FString DecodeStr;
if(FBase64::Decode(Encode,DecodeStr))
{
return DecodeStr;
}
else
{
return Encode;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,80 @@
// Copyright 2019 Lipeng Zha, Inc. All Rights Reserved.
#include "HotPatcherCore.h"
#include "HotPatcherSettings.h"
// ENGINE HEADER
#include "AssetToolsModule.h"
#include "CommandletHelper.h"
#include "ContentBrowserModule.h"
#include "IContentBrowserSingleton.h"
#include "Misc/MessageDialog.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "DesktopPlatformModule.h"
#include "FlibHotPatcherCoreHelper.h"
#include "HotPatcherLog.h"
#include "ISettingsModule.h"
#include "LevelEditor.h"
#include "HAL/FileManager.h"
#include "Interfaces/IPluginManager.h"
#include "Kismet/KismetTextLibrary.h"
#include "PakFileUtilities.h"
#include "Cooker/MultiCooker/SingleCookerProxy.h"
#include "CreatePatch/PatcherProxy.h"
#include "Settings/ProjectPackagingSettings.h"
#include "ThreadUtils/FProcWorkerThread.hpp"
FExportPatchSettings* GPatchSettings = nullptr;
FExportReleaseSettings* GReleaseSettings = nullptr;
bool GCookLog = (bool)ENABLE_COOK_LOG;
FString GToolName = TOOL_NAME;
int32 GToolMainVersion = CURRENT_VERSION_ID;
int32 GToolPatchVersion = CURRENT_PATCH_ID;
FString GRemoteVersionFile = REMOTE_VERSION_FILE;
static const FName HotPatcherTabName("HotPatcher");
#define LOCTEXT_NAMESPACE "FHotPatcherCoreModule"
void ReceiveOutputMsg(FProcWorkerThread* Worker,const FString& InMsg)
{
FString FindItem(TEXT("Display:"));
int32 Index= InMsg.Len() - InMsg.Find(FindItem)- FindItem.Len();
if (InMsg.Contains(TEXT("Error:")))
{
UE_LOG(LogHotPatcher, Error, TEXT("%s"), *InMsg);
}
else if (InMsg.Contains(TEXT("Warning:")))
{
UE_LOG(LogHotPatcher, Warning, TEXT("%s"), *InMsg);
}
else
{
UE_LOG(LogHotPatcher, Display, TEXT("%s"), *InMsg.Right(Index));
}
}
FHotPatcherCoreModule& FHotPatcherCoreModule::Get()
{
FHotPatcherCoreModule& Module = FModuleManager::GetModuleChecked<FHotPatcherCoreModule>("HotPatcherCore");
return Module;
}
void FHotPatcherCoreModule::StartupModule()
{
FParse::Bool(FCommandLine::Get(),TEXT("-cooklog"),GCookLog);
UE_LOG(LogHotPatcher,Log,TEXT("GCookLog is %s!!!"),GCookLog ? TEXT("TRUE"): TEXT("FALSE"));
}
void FHotPatcherCoreModule::ShutdownModule()
{
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FHotPatcherCoreModule, HotPatcherCore)

View File

@ -0,0 +1,8 @@
#include "HotPatcherDelegates.h"
FHotPatcherDelegates& FHotPatcherDelegates::Get()
{
// return the singleton object
static FHotPatcherDelegates Singleton;
return Singleton;
}

View File

@ -0,0 +1,87 @@
#include "Cooker/MultiCooker/FCookShaderCollectionProxy.h"
#include "FlibHotPatcherCoreHelper.h"
#include "Cooker/MultiCooker/FlibHotCookerHelper.h"
#include "ShaderLibUtils//FlibShaderCodeLibraryHelper.h"
#include "Interfaces/ITargetPlatform.h"
#include "Resources/Version.h"
FCookShaderCollectionProxy::FCookShaderCollectionProxy(const TArray<FString>& InPlatformNames,const FString& InLibraryName,bool bInShareShader,bool InIsNative,bool bInMaster,const FString& InSaveBaseDir)
:PlatformNames(InPlatformNames),LibraryName(InLibraryName),bShareShader(bInShareShader),bIsNative(InIsNative),bMaster(bInMaster),SaveBaseDir(InSaveBaseDir){}
FCookShaderCollectionProxy::~FCookShaderCollectionProxy(){}
void FCookShaderCollectionProxy::Init()
{
if(bShareShader)
{
SHADER_COOKER_CLASS::InitForCooking(bIsNative);
for(const auto& PlatformName:PlatformNames)
{
ITargetPlatform* TargetPlatform = UFlibHotPatcherCoreHelper::GetPlatformByName(PlatformName);
TargetPlatforms.AddUnique(TargetPlatform);
TArray<FName> ShaderFormats = UFlibShaderCodeLibraryHelper::GetShaderFormatsByTargetPlatform(TargetPlatform);
if (ShaderFormats.Num() > 0)
{
#if ENGINE_MAJOR_VERSION > 4 || ENGINE_MINOR_VERSION > 25
TArray<SHADER_COOKER_CLASS::FShaderFormatDescriptor> ShaderFormatsWithStableKeys = UFlibShaderCodeLibraryHelper::GetShaderFormatsWithStableKeys(ShaderFormats);
#else
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION > 22
TArray<TPair<FName, bool>> ShaderFormatsWithStableKeys;
for (FName& Format : ShaderFormats)
{
ShaderFormatsWithStableKeys.Push(MakeTuple(Format, true));
}
#else
TArray<FName> ShaderFormatsWithStableKeys = ShaderFormats;
#endif
#endif
SHADER_COOKER_CLASS::CookShaderFormats(ShaderFormatsWithStableKeys);
}
}
FShaderCodeLibrary::OpenLibrary(LibraryName,TEXT(""));
}
}
void FCookShaderCollectionProxy::Shutdown()
{
SCOPED_NAMED_EVENT_TEXT("FCookShaderCollectionProxy::Shutdown",FColor::Red);
if(bShareShader)
{
for(const auto& TargetPlatform:TargetPlatforms)
{
FString PlatformName = TargetPlatform->PlatformName();
bSuccessed = UFlibShaderCodeLibraryHelper::SaveShaderLibrary(TargetPlatform, NULL, LibraryName,SaveBaseDir,bMaster);
#if ENGINE_MAJOR_VERSION < 5 && ENGINE_MINOR_VERSION <= 26
if(bIsNative)
{
FString ShaderCodeDir = FPaths::Combine(SaveBaseDir,PlatformName);
bSuccessed = bSuccessed && FShaderCodeLibrary::PackageNativeShaderLibrary(ShaderCodeDir,UFlibShaderCodeLibraryHelper::GetShaderFormatsByTargetPlatform(TargetPlatform));
}
#endif
// rename StarterContent_SF_METAL.0.metallib to startercontent_sf_metal.0.metallib
if(bSuccessed && bIsNative && UFlibHotCookerHelper::IsAppleMetalPlatform(TargetPlatform))
{
TArray<FString> FoundShaderLibs = UFlibShaderCodeLibraryHelper::FindCookedShaderLibByPlatform(PlatformName,FPaths::Combine(SaveBaseDir,TargetPlatform->PlatformName()));
for(const auto& Shaderlib:FoundShaderLibs)
{
if(Shaderlib.EndsWith(TEXT("metallib"),ESearchCase::IgnoreCase) || Shaderlib.EndsWith(TEXT("metalmap"),ESearchCase::IgnoreCase))
{
FString Path = FPaths::GetPath(Shaderlib);
FString Name = FPaths::GetBaseFilename(Shaderlib,true);
FString Extersion = FPaths::GetExtension(Shaderlib,true);
Name = FString::Printf(TEXT("%s%s"),*Name,*Extersion);
Name.ToLowerInline();
if(!Shaderlib.EndsWith(Name,ESearchCase::CaseSensitive))
{
IFileManager::Get().Move(*FPaths::Combine(Path,Name),*Shaderlib,false);
}
}
}
}
}
FShaderCodeLibrary::CloseLibrary(LibraryName);
FShaderCodeLibrary::Shutdown();
}
}

View File

@ -0,0 +1,294 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "ShaderLibUtils//FlibShaderCodeLibraryHelper.h"
#include "HotPatcherLog.h"
#include "FlibHotPatcherCoreHelper.h"
#include "HotPatcherCore.h"
#include "ShaderCompiler.h"
#include "IPlatformFileSandboxWrapper.h"
#include "Interfaces/IPluginManager.h"
#include "Interfaces/ITargetPlatform.h"
#include "Misc/EngineVersionComparison.h"
#define REMAPPED_PLUGINS TEXT("RemappedPlugins")
FString UFlibShaderCodeLibraryHelper::ShaderExtension = TEXT(".ushaderbytecode");
FString UFlibShaderCodeLibraryHelper::ShaderAssetInfoExtension = TEXT(".assetinfo.json");
FString UFlibShaderCodeLibraryHelper::StableExtension = TEXT(".scl.csv");
// FMergeShaderCollectionProxy::FMergeShaderCollectionProxy(const TArray<FShaderCodeFormatMap>& InShaderCodeFiles):ShaderCodeFiles(InShaderCodeFiles)
// {
// Init();
// }
// FMergeShaderCollectionProxy::~FMergeShaderCollectionProxy()
// {
// Shutdown();
// }
//
// void FMergeShaderCollectionProxy::Init()
// {
// for(const auto& ShaderCodeFoemat:ShaderCodeFiles)
// {
// TArray<FName> ShaderFormatNames = UFlibShaderCodeLibraryHelper::GetShaderFormatsByTargetPlatform(ShaderCodeFoemat.Platform);
//
// for(const auto& ShaderFormatName:ShaderFormatNames)
// {
// EShaderPlatform ShaderPlatform= ::ShaderFormatToLegacyShaderPlatform(ShaderFormatName);
//
// if(ShaderCodeFoemat.ShaderCodeTypeFilesMap.Contains(ShaderFormatName.ToString()))
// {
// SHADER_COOKER_CLASS::InitForCooking(ShaderCodeFoemat.bIsNative);
// SHADER_COOKER_CLASS::InitForRuntime(ShaderPlatform);
// TArray<FName> CurrentPlatforomShaderTypeNames;
// {
// TArray<FString> PlatforomShaderTypeNames;
// ShaderCodeFoemat.ShaderCodeTypeFilesMap.GetKeys(PlatforomShaderTypeNames);
// for(const auto& Name:PlatforomShaderTypeNames)
// {
// CurrentPlatforomShaderTypeNames.AddUnique(*Name);
// }
// }
//
// #if ENGINE_MAJOR_VERSION > 4 || ENGINE_MINOR_VERSION > 25
// TArray<SHADER_COOKER_CLASS::FShaderFormatDescriptor> ShaderFormatsWithStableKeys = UFlibShaderCodeLibraryHelper::GetShaderFormatsWithStableKeys(CurrentPlatforomShaderTypeNames);
// #else
// TArray<TPair<FName, bool>> ShaderFormatsWithStableKeys;
// for (FName& Format : ShaderFormatNames)
// {
// ShaderFormatsWithStableKeys.Push(MakeTuple(Format, true));
// }
// #endif
//
// SHADER_COOKER_CLASS::CookShaderFormats(ShaderFormatsWithStableKeys);
// FShaderCodeFormatMap::FShaderFormatNameFiles ShaderFormatNameFiles = *ShaderCodeFoemat.ShaderCodeTypeFilesMap.Find(ShaderFormatName.ToString());
// for(const auto& File:ShaderFormatNameFiles.Files)
// {
// FString Path = FPaths::GetPath(File);
// FShaderCodeLibrary::OpenLibrary(ShaderFormatNameFiles.ShaderName,Path);
// }
// if(!UFlibShaderCodeLibraryHelper::SaveShaderLibrary(ShaderCodeFoemat.Platform, NULL, FApp::GetProjectName(),ShaderCodeFoemat.SaveBaseDir,true))
// {
// UE_LOG(LogHotPatcherCoreHelper,Display,TEXT("SaveShaderLibrary %s for %s Failed!"),*FApp::GetProjectName(),*ShaderCodeFoemat.Platform->PlatformName() );
// }
// SHADER_COOKER_CLASS::Shutdown();
// }
// }
// }
// }
//
// void FMergeShaderCollectionProxy::Shutdown()
// {
// FShaderCodeLibrary::Shutdown();
// }
#if ENGINE_MAJOR_VERSION > 4 || ENGINE_MINOR_VERSION > 25
TArray<SHADER_COOKER_CLASS::FShaderFormatDescriptor> UFlibShaderCodeLibraryHelper::GetShaderFormatsWithStableKeys(
const TArray<FName>& ShaderFormats,bool bNeedShaderStableKeys/* = true*/,bool bNeedsDeterministicOrder/* = true*/)
{
TArray<SHADER_COOKER_CLASS::FShaderFormatDescriptor> ShaderFormatsWithStableKeys;
for (const FName& Format : ShaderFormats)
{
SHADER_COOKER_CLASS::FShaderFormatDescriptor NewDesc;
NewDesc.ShaderFormat = Format;
NewDesc.bNeedsStableKeys = bNeedShaderStableKeys;
NewDesc.bNeedsDeterministicOrder = bNeedsDeterministicOrder;
ShaderFormatsWithStableKeys.Push(NewDesc);
}
return ShaderFormatsWithStableKeys;
}
#endif
TArray<FName> UFlibShaderCodeLibraryHelper::GetShaderFormatsByTargetPlatform(ITargetPlatform* TargetPlatform)
{
TArray<FName> ShaderFormats;
TargetPlatform->GetAllTargetedShaderFormats(ShaderFormats);
return ShaderFormats;
}
FString UFlibShaderCodeLibraryHelper::GenerateShaderCodeLibraryName(FString const& Name, bool bIsIterateSharedBuild)
{
FString ActualName = (!bIsIterateSharedBuild) ? Name : Name + TEXT("_SC");
return ActualName;
}
bool UFlibShaderCodeLibraryHelper::SaveShaderLibrary(const ITargetPlatform* TargetPlatform,TArray<FName> ShaderFormats, const FString& ShaderCodeDir,const FString& RootMetaDataPath, bool bMaster)
{
bool bSaved = false;
// TargetPlatform->GetAllTargetedShaderFormats(ShaderFormats);
if (ShaderFormats.Num() > 0)
{
FString TargetPlatformName = TargetPlatform->PlatformName();
TArray<FString> PlatformSCLCSVPaths;// = OutSCLCSVPaths.FindOrAdd(FName(TargetPlatformName));
#if ENGINE_MAJOR_VERSION > 4 || ENGINE_MINOR_VERSION > 25
#if ENGINE_MAJOR_VERSION > 4 || ENGINE_MINOR_VERSION > 26
FString ErrorString;
#if !UE_VERSION_OLDER_THAN(5,1,0)
bool bOutHasData = false;
#endif
bSaved = SHADER_COOKER_CLASS::SaveShaderLibraryWithoutChunking(TargetPlatform, FApp::GetProjectName(), ShaderCodeDir, RootMetaDataPath, PlatformSCLCSVPaths, ErrorString
#if !UE_VERSION_OLDER_THAN(5,1,0)
,bOutHasData
#endif
);
#else
TArray<TSet<FName>> ChunkAssignments;
bSaved = FShaderCodeLibrary::SaveShaderCode(ShaderCodeDir, RootMetaDataPath, ShaderFormats, PlatformSCLCSVPaths, &ChunkAssignments);
#endif
#else
if(bMaster)
{
bSaved = FShaderCodeLibrary::SaveShaderCodeMaster(ShaderCodeDir, RootMetaDataPath, ShaderFormats, PlatformSCLCSVPaths);
}
else
{
bSaved = FShaderCodeLibrary::SaveShaderCodeChild(ShaderCodeDir, RootMetaDataPath, ShaderFormats);
}
#endif
}
return bSaved;
}
bool UFlibShaderCodeLibraryHelper::SaveShaderLibrary(const ITargetPlatform* TargetPlatform, const TArray<TSet<FName>>* ChunkAssignments, FString const& Name, const FString&
SaveBaseDir, bool bMaster)
{
bool bSaved = false;
FString ActualName = GenerateShaderCodeLibraryName(Name, false);
FString BasePath = FPaths::ProjectContentDir();
FString ShaderCodeDir = FPaths::Combine(SaveBaseDir,TargetPlatform->PlatformName());
const FString RootMetaDataPath = ShaderCodeDir / TEXT("Metadata") / TEXT("PipelineCaches");
// note that shader formats can be shared across the target platforms
TArray<FName> ShaderFormats;
TargetPlatform->GetAllTargetedShaderFormats(ShaderFormats);
if (ShaderFormats.Num() > 0)
{
bSaved = UFlibShaderCodeLibraryHelper::SaveShaderLibrary(TargetPlatform,ShaderFormats,ShaderCodeDir,RootMetaDataPath,bMaster);
// FString TargetPlatformName = TargetPlatform->PlatformName();
// TArray<FString> PlatformSCLCSVPaths;// = OutSCLCSVPaths.FindOrAdd(FName(TargetPlatformName));
//
// #if ENGINE_MAJOR_VERSION > 4 || ENGINE_MINOR_VERSION > 25
// #if ENGINE_MAJOR_VERSION > 4 || ENGINE_MINOR_VERSION > 26
// FString ErrorString;
// bSaved = SHADER_COOKER_CLASS::SaveShaderLibraryWithoutChunking(TargetPlatform, FApp::GetProjectName(), ShaderCodeDir, RootMetaDataPath, PlatformSCLCSVPaths, ErrorString);
// #else
// bSaved = FShaderCodeLibrary::SaveShaderCode(ShaderCodeDir, RootMetaDataPath, ShaderFormats, PlatformSCLCSVPaths, ChunkAssignments);
// #endif
// #else
// if(bMaster)
// {
// bSaved = FShaderCodeLibrary::SaveShaderCodeMaster(ShaderCodeDir, RootMetaDataPath, ShaderFormats, PlatformSCLCSVPaths);
// }
// else
// {
// bSaved = FShaderCodeLibrary::SaveShaderCodeChild(ShaderCodeDir, RootMetaDataPath, ShaderFormats);
// }
// #endif
}
return bSaved;
}
TArray<FString> UFlibShaderCodeLibraryHelper::FindCookedShaderLibByShaderFrmat(const FString& ShaderFormatName,const FString& Directory)
{
TArray<FString> result;
TArray<FString > FoundShaderFiles;
IFileManager::Get().FindFiles(FoundShaderFiles,*Directory,TEXT("ushaderbytecode"));
FString FormatExtersion = FString::Printf(TEXT("*%s*.ushaderbytecode"),*ShaderFormatName);
for(const auto& ShaderFile:FoundShaderFiles)
{
if(ShaderFile.MatchesWildcard(FormatExtersion))
{
result.Add(ShaderFile);
}
}
return result;
}
TArray<FString> UFlibShaderCodeLibraryHelper::FindCookedShaderLibByPlatform(const FString& PlatfomName,const FString& Directory, bool bRecursive)
{
SCOPED_NAMED_EVENT_TEXT("UFlibShaderCodeLibraryHelper::FindCookedShaderLibByPlatform",FColor::Red);
TArray<FString> FoundFiles;
auto GetMetalShaderFormatLambda = [](const FString& Directory,const FString& Extersion, bool bRecursive)
{
TArray<FString> FoundMetallibFiles;
if(bRecursive)
{
IFileManager::Get().FindFilesRecursive(FoundMetallibFiles,*Directory,*Extersion,true,false, false);
}
else
{
IFileManager::Get().FindFiles(FoundMetallibFiles,*Directory,*Extersion);
}
return FoundMetallibFiles;
};
if(PlatfomName.StartsWith(TEXT("IOS"),ESearchCase::IgnoreCase) || PlatfomName.StartsWith(TEXT("Mac"),ESearchCase::IgnoreCase))
{
FoundFiles.Append(GetMetalShaderFormatLambda(Directory,TEXT("metallib"),bRecursive));
FoundFiles.Append(GetMetalShaderFormatLambda(Directory,TEXT("metalmap"),bRecursive));
}
if(!FoundFiles.Num())
{
FoundFiles.Append(GetMetalShaderFormatLambda(Directory,TEXT("ushaderbytecode"),bRecursive));
}
for(auto& File:FoundFiles)
{
File = FPaths::Combine(Directory,File);
}
return FoundFiles;
}
void UFlibShaderCodeLibraryHelper::WaitShaderCompilingComplete()
{
// Wait for all shaders to finish compiling
if (GShaderCompilingManager)
{
SCOPED_NAMED_EVENT_TEXT("WaitShaderCompileComplete",FColor::Red);
while(GShaderCompilingManager->IsCompiling())
{
GShaderCompilingManager->ProcessAsyncResults(false, false);
// int32 CurrentNumRemaingingJobs = GShaderCompilingManager->GetNumRemainingJobs();
// if(GCookLog && LastRemainingJob != CurrentNumRemaingingJobs)
// {
// UE_LOG(LogHotPatcher,Display,TEXT("Remaining Shader %d"),CurrentNumRemaingingJobs);
// LastRemainingJob = CurrentNumRemaingingJobs;
// }
// GShaderCompilingManager->FinishAllCompilation();
FPlatformProcess::Sleep(0.5f);
GLog->Flush();
}
// One last process to get the shaders that were compiled at the very end
GShaderCompilingManager->ProcessAsyncResults(false, false);
GShaderCompilingManager->FinishAllCompilation();
}
}
void UFlibShaderCodeLibraryHelper::CleanShaderWorkerDir()
{
if (FPaths::DirectoryExists(FPaths::ShaderWorkingDir()) && !IFileManager::Get().DeleteDirectory(*FPaths::ShaderWorkingDir(), false, true))
{
UE_LOG(LogHotPatcher, Warning, TEXT("Could not delete the shader compiler working directory '%s'."), *FPaths::ShaderWorkingDir());
}
}
void UFlibShaderCodeLibraryHelper::CancelMaterialShaderCompile(UMaterialInterface* MaterialInterface)
{
if(MaterialInterface)
{
UMaterial* Material = MaterialInterface->GetMaterial();
for (int32 FeatureLevel = 0; FeatureLevel < ERHIFeatureLevel::Num; ++FeatureLevel)
{
if (FMaterialResource* Res = Material->GetMaterialResource((ERHIFeatureLevel::Type)FeatureLevel))
{
Res->CancelCompilation();
}
}
}
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "CoreMinimal.h"
#include "ETargetPlatform.h"
#define PATCHER_CONFIG_PARAM_NAME TEXT("-config=")
#define ADD_PATCH_PLATFORMS TEXT("AddPatchPlatforms")
#define TARGET_PLATFORMS_OVERRIDE TEXT("TargetPlatformsOverride")
DECLARE_LOG_CATEGORY_EXTERN(LogHotPatcherCommandlet, All, All);
namespace CommandletHelper
{
HOTPATCHERCORE_API void ReceiveMsg(const FString& InMsgType,const FString& InMsg);
HOTPATCHERCORE_API void ReceiveShowMsg(const FString& InMsg);
HOTPATCHERCORE_API TArray<FString> ParserPatchConfigByCommandline(const FString& Commandline,const FString& Token);
HOTPATCHERCORE_API TArray<ETargetPlatform> ParserPlatforms(const FString& Commandline, const FString& Token);
HOTPATCHERCORE_API TArray<FDirectoryPath> ParserPatchFilters(const FString& Commandline,const FString& FilterName);
HOTPATCHERCORE_API void MainTick(TFunction<bool()> IsRequestExit);
HOTPATCHERCORE_API bool GetCommandletArg(const FString& Token,FString& OutValue);
HOTPATCHERCORE_API bool IsCookCommandlet();
HOTPATCHERCORE_API TArray<ETargetPlatform> GetCookCommandletTargetPlatforms();
HOTPATCHERCORE_API TArray<FString> GetCookCommandletTargetPlatformName();
HOTPATCHERCORE_API void ModifyTargetPlatforms(const FString& InParams,const FString& InToken,TArray<ETargetPlatform>& OutTargetPlatforms,bool Replace);
}

View File

@ -0,0 +1,30 @@
#pragma once
#include "FCountServerlessWrapper.h"
#include "Commandlets/Commandlet.h"
#include "HotPatcherCommandletBase.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogHotPatcherCommandletBase, All, All);
UCLASS()
class HOTPATCHERCORE_API UHotPatcherCommandletBase :public UCommandlet
{
GENERATED_BODY()
public:
virtual int32 Main(const FString& Params)override;
virtual bool IsSkipObject(UObject* Object){ return false; }
virtual bool IsSkipPackage(UPackage* Package){ return false; }
virtual FString GetCmdletName()const { return TEXT("HotPatcherCmdletBase"); }
static FString GetCrashDir();
static void CleanCrashDir();
protected:
void OnHandleSystemError();
protected:
void Update(const FString& Params);
void MaybeMarkPackageAsAlreadyLoaded(UPackage* Package);
TSharedPtr<struct FObjectTrackerTagCleaner> ObjectTrackerTagCleaner;
FString CmdConfigPath;
TSharedPtr<FCountServerlessWrapper> Counter;
};

View File

@ -0,0 +1,11 @@
#pragma once
// engine
#include "CoreMinimal.h"
#include "Engine/EngineTypes.h"
#include "HotPatcherCookerSettingBase.generated.h"
USTRUCT(BlueprintType)
struct HOTPATCHERCORE_API FHotPatcherCookerSettingBase: public FPatcherEntitySettingBase
{
GENERATED_USTRUCT_BODY()
};

View File

@ -0,0 +1,24 @@
#pragma once
#include "Resources/Version.h"
#include "ShaderCodeLibrary.h"
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Misc/AES.h"
struct FCookShaderCollectionProxy
{
FCookShaderCollectionProxy(const TArray<FString>& InPlatformNames,const FString& InLibraryName,bool bShareShader,bool InIsNative,bool bInMaster,const FString& InSaveBaseDir);
virtual ~FCookShaderCollectionProxy();
virtual void Init();
virtual void Shutdown();
virtual bool IsSuccessed()const { return bSuccessed; }
private:
TArray<ITargetPlatform*> TargetPlatforms;
TArray<FString> PlatformNames;
FString LibraryName;
bool bShareShader;
bool bIsNative;
bool bMaster;
FString SaveBaseDir;
bool bSuccessed = false;
};

View File

@ -0,0 +1,150 @@
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#pragma once
// project header
#include "HotPatcherLog.h"
#include "FlibPatchParserHelper.h"
#include "HotPatcherLog.h"
#include "Cooker/HotPatcherCookerSettingBase.h"
// engine header
#include "Misc/FileHelper.h"
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "UObject/Object.h"
#include "Engine/EngineTypes.h"
#include "Kismet/KismetStringLibrary.h"
#include "Serialization/JsonSerializer.h"
#include "Serialization/JsonWriter.h"
#include "FSingleCookerSettings.generated.h"
USTRUCT(BlueprintType)
struct HOTPATCHERCORE_API FCookerShaderOptions
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bSharedShaderLibrary = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bNativeShader = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cooker",meta=(EditCondition="bSharedShaderLibrary"))
bool bMergeShaderLibrary = false;
};
USTRUCT(BlueprintType)
struct HOTPATCHERCORE_API FSingleCookerSettings:public FHotPatcherCookerSettingBase
{
GENERATED_BODY()
public:
FSingleCookerSettings();
UPROPERTY(EditAnywhere,BlueprintReadWrite)
FString MissionName;
UPROPERTY(EditAnywhere,BlueprintReadWrite)
int32 MissionID = -1;
UPROPERTY(EditAnywhere,BlueprintReadWrite)
bool bShaderCooker = false;
UPROPERTY(EditAnywhere,BlueprintReadWrite)
FString ShaderLibName;
UPROPERTY(EditAnywhere,BlueprintReadWrite)
TArray<FAssetDetail> CookAssets;
// skip loaded assets when cooking(package path)
UPROPERTY(EditAnywhere,BlueprintReadWrite)
TSet<FName> SkipLoadedAssets;
// Directories or asset package path
UPROPERTY(EditAnywhere,BlueprintReadWrite)
TArray<FString> SkipCookContents;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<UClass*> ForceSkipClasses;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cooker")
TArray<ETargetPlatform> CookTargetPlatforms;
// track load asset when cooking
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cooker")
bool bPackageTracker = true;
// cook load in cooking assets by SingleCookder side
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cooker",meta=(EditCondition="bPackageTracker"))
bool bCookPackageTrackerAssets = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cooker")
FCookerShaderOptions ShaderOptions;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cooker")
bool bSerializeAssetRegistry = false;
// UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cooker")
// FAssetRegistryOptions SerializeAssetRegistryOptions;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cooker")
FIoStoreSettings IoStoreSettings;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cooker")
bool bForceCookInOneFrame = false;
FORCEINLINE int32 GetNumberOfAssetsPerFrame()const{ return NumberOfAssetsPerFrame; }
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cooker",meta=(EditCondition="!bForceCookInOneFrame",ClampMin=0))
int32 NumberOfAssetsPerFrame = 100;
TMap<UClass*,int32> OverrideNumberOfAssetsPerFrame;
FORCEINLINE const TMap<UClass*,int32>& GetOverrideNumberOfAssetsPerFrame()const{ return OverrideNumberOfAssetsPerFrame; }
// UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cooker")
bool bAsyncLoad = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cooker",meta=(EditCondition="!bAsyncLoad"))
bool bPreGeneratePlatformData = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cooker",meta=(EditCondition="bPreGeneratePlatformData"))
bool bWaitEachAssetCompleted = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cooker",meta=(EditCondition="bPreGeneratePlatformData"))
bool bConcurrentSave = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Debug")
bool bDisplayConfig = false;
FString GetStorageCookedAbsDir()const;
UPROPERTY(EditAnywhere,BlueprintReadWrite)
FString StorageCookedDir;
FString GetStorageMetadataAbsDir()const;
UPROPERTY(EditAnywhere,BlueprintReadWrite)
FString StorageMetadataDir;
// UPROPERTY(EditAnywhere,BlueprintReadWrite,Category = "SavePackageContext")
#if WITH_PACKAGE_CONTEXT
bool bOverrideSavePackageContext = false;
TMap<ETargetPlatform,TSharedPtr<FSavePackageContext>> PlatformSavePackageContexts;
#endif
bool IsSkipAsset(const FString& PackageName);
};
USTRUCT(BlueprintType)
struct FPackagePathSet
{
GENERATED_BODY()
UPROPERTY()
TSet<FName> PackagePaths;
};
//
// USTRUCT(BlueprintType)
// struct HOTPATCHERCORE_API FAssetsCollection
// {
// GENERATED_BODY()
// UPROPERTY(EditAnywhere,BlueprintReadWrite)
// ETargetPlatform TargetPlatform = ETargetPlatform::None;
// UPROPERTY(EditAnywhere,BlueprintReadWrite)
// TArray<FAssetDetail> Assets;
// };
USTRUCT(BlueprintType)
struct HOTPATCHERCORE_API FCookerFailedCollection
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere,BlueprintReadWrite)
FString MissionName;
UPROPERTY(EditAnywhere,BlueprintReadWrite)
int32 MissionID = -1;
UPROPERTY(EditAnywhere,BlueprintReadWrite)
TMap<ETargetPlatform,FPackagePathSet> CookFailedAssets;
};

View File

@ -0,0 +1,33 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "Resources/Version.h"
#include "CoreMinimal.h"
#include "ETargetPlatform.h"
#include "Cooker/MultiCooker/FCookShaderCollectionProxy.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "FlibHotCookerHelper.generated.h"
/**
*
*/
UCLASS()
class HOTPATCHERCORE_API UFlibHotCookerHelper : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
static FString GetCookedDir();
static FString GetCookerBaseDir();
// static FString GetCookerProcConfigPath(const FString& MissionName,int32 MissionID);
static FString GetCookerProcFailedResultPath(const FString& BaseDir,const FString& MissionName, int32 MissionID);
static FString GetProfilingCmd();
static TSharedPtr<FCookShaderCollectionProxy> CreateCookShaderCollectionProxyByPlatform(
const FString& ShaderLibraryName,
TArray<ETargetPlatform> Platforms,
bool bShareShader,
bool bNativeShader,
bool bMaster,
const FString& InSavePath,
bool bCleanSavePath = true);
static bool IsAppleMetalPlatform(ITargetPlatform* TargetPlatform);
};

View File

@ -0,0 +1,195 @@
#pragma once
#include "FSingleCookerSettings.h"
#include "Cooker/HotPatcherCookerSettingBase.h"
#include "CreatePatch/HotPatcherProxyBase.h"
#include "HotPatcherBaseTypes.h"
#include "BaseTypes/FPackageTracker.h"
// engine header
#include "Templates/SharedPointer.h"
#include "Containers/Queue.h"
#include "TickableEditorObject.h"
#include "ThreadUtils/FThreadUtils.hpp"
#include "SingleCookerProxy.generated.h"
bool IsAlwayPostLoadClasses(UPackage* Package, UObject* Object);
struct FFreezePackageTracker : public FPackageTrackerBase
{
public:
FFreezePackageTracker(const TSet<FName>& InCookerAssetsSet,const TSet<FName>& InAllAssets):CookerAssetsSet(InCookerAssetsSet),AllAssetsSet(InAllAssets){}
virtual ~FFreezePackageTracker(){}
virtual void NotifyUObjectCreated(const class UObjectBase *Object, int32 Index) override;
virtual void NotifyUObjectDeleted(const UObjectBase* Object, int32 Index) override
{
auto ObjectOuter = const_cast<UObject*>(static_cast<const UObject*>(Object));
if(FreezeObjects.Contains(ObjectOuter))
{
FreezeObjects.Remove(ObjectOuter);
}
}
bool IsFreezed(UObject* Object)const
{
return FreezeObjects.Contains(Object);
}
protected:
TSet<FName> CookerAssetsSet;
TSet<FName> AllAssetsSet;
TSet<UObject*> FreezeObjects;
TMultiMap<FName,TArray<UObject*>> PackageObjectsMap;
// TArray<UObject*> FreezeObjects;
};
struct FOtherCookerPackageTracker : public FPackageTrackerBase
{
FOtherCookerPackageTracker(const TSet<FName>& InCookerAssets,const TSet<FName>& InAllCookerAssets):CookerAssets(InCookerAssets),AllCookerAssets(InAllCookerAssets){};
virtual ~FOtherCookerPackageTracker(){}
FORCEINLINE virtual void OnPackageCreated(UPackage* Package) override
{
FName AssetPathName = FName(*Package->GetPathName());
if(!CookerAssets.Contains(AssetPathName) && AllCookerAssets.Contains(AssetPathName))
{
LoadOtherCookerAssets.Add(AssetPathName);
UE_LOG(LogHotPatcher,Display,TEXT("[OtherCookerPackageTracker] %s "),*AssetPathName.ToString());
}
}
FORCEINLINE const TSet<FName>& GetLoadOtherCookerAssets()const { return LoadOtherCookerAssets; }
protected:
TSet<FName> CookerAssets;
TSet<FName> AllCookerAssets;
TSet<FName> LoadOtherCookerAssets;
};
DECLARE_MULTICAST_DELEGATE(FSingleCookerEvent);
DECLARE_MULTICAST_DELEGATE_TwoParams(FSingleCookActionEvent,const FSoftObjectPath&,ETargetPlatform);
DECLARE_MULTICAST_DELEGATE_ThreeParams(FSingleCookResultEvent,const FSoftObjectPath&,ETargetPlatform,ESavePackageResult);
USTRUCT()
struct FCookCluster
{
GENERATED_BODY()
UPROPERTY()
TArray<FAssetDetail> AssetDetails;
UPROPERTY()
TArray<ETargetPlatform> Platforms;
UPROPERTY()
bool bPreGeneratePlatformData = false;
FORCEINLINE_DEBUGGABLE TArray<FSoftObjectPath> AsSoftObjectPaths()const
{
TArray<FSoftObjectPath> SoftObjectPaths;
for(const auto& AssetDetail:AssetDetails)
{
if(AssetDetail.IsValid())
{
SoftObjectPaths.Emplace(AssetDetail.PackagePath.ToString());
}
}
return SoftObjectPaths;
}
FCookActionCallback CookActionCallback;
};
UCLASS()
class HOTPATCHERCORE_API USingleCookerProxy:public UHotPatcherProxyBase, public FTickableEditorObject
{
GENERATED_BODY()
public:
virtual void Init(FPatcherEntitySettingBase* InSetting)override;
virtual void Shutdown() override;
virtual bool DoExport()override;
bool IsFinsihed();
bool HasError();
virtual FSingleCookerSettings* GetSettingObject()override {return (FSingleCookerSettings*)(Setting);};
public: // base func
virtual void Tick(float DeltaTime) override;
TStatId GetStatId() const override;
bool IsTickable() const override;
public: // core interface
int32 MakeCookQueue(FCookCluster& InCluser);
void PreGeneratePlatformData(const FCookCluster& CookCluster);
void CleanClusterCachedPlatformData(const FCookCluster& CookCluster);
void ExecCookCluster(const FCookCluster& Cluster);
FCookerFailedCollection& GetCookFailedAssetsCollection(){return CookFailedAssetsCollection;};
void CleanOldCooked(const FString& CookBaseDir,const TArray<FSoftObjectPath>& ObjectPaths,const TArray<ETargetPlatform>& CookPlatforms);
// cook classes order
TArray<UClass*> GetPreCacheClasses()const;
public: // callback
FCookActionResultEvent GetOnPackageSavedCallback();
FCookActionEvent GetOnCookAssetBeginCallback();
public: // packae tracker interface
TSet<FName> GetCookerAssets();
TSet<FName> GetAdditionalAssets();
FCookCluster GetPackageTrackerAsCluster();
FORCEINLINE_DEBUGGABLE TSet<FName>& GetPaendingCookAssetsSet(){ return PaendingCookAssetsSet; }
TArray<FName>& GetPlatformCookAssetOrders(ETargetPlatform Platform);
protected: // on asset cooked call func
void OnAssetCookedHandle(const FSoftObjectPath& PackagePath,ETargetPlatform Platform,ESavePackageResult Result);
protected: // metadata func
void BulkDataManifest();
void IoStoreManifest();
void InitShaderLibConllections();
void ShutdowShaderLibCollections();
private: // package context
#if WITH_PACKAGE_CONTEXT
FORCEINLINE TMap<ETargetPlatform,TSharedPtr<FSavePackageContext>>& GetPlatformSavePackageContexts() {return PlatformSavePackageContexts;}
TMap<ETargetPlatform,FSavePackageContext*> GetPlatformSavePackageContextsRaw();
TMap<FString, FSavePackageContext*> GetPlatformSavePackageContextsNameMapping();
TMap<ETargetPlatform,TSharedPtr<FSavePackageContext>> PlatformSavePackageContexts;
#endif
public: // static function
static void DumpCluster(const FCookCluster& CookCluster, bool bWriteToLog);
public:
int32 GetClassAssetNumOfPerCluster(UClass* Class);
public: // delegate
FSingleCookerEvent OnCookBegin;
FSingleCookerEvent OnCookFinished;
FSingleCookActionEvent OnCookAssetBegin;
FSingleCookResultEvent OnAssetCooked;
private: // cook assets manager
FCookCluster GlobalCluser;
TQueue<FCookCluster> CookCluserQueue;
FCookerFailedCollection CookFailedAssetsCollection;
private: // metadate
TSharedPtr<struct FCookShaderCollectionProxy> PlatformCookShaderCollection;
TMap<ETargetPlatform,TArray<FName>> CookAssetOrders;
private: // package tracker
TSet<FName> PaendingCookAssetsSet;
TSharedPtr<FPackageTracker> PackageTracker;
TSharedPtr<FOtherCookerPackageTracker> OtherCookerPackageTracker;
TSharedPtr<FFreezePackageTracker> FreezePackageTracker;
FPackagePathSet ExixtPackagePathSet;
private: // worker flow control
/** Critical section for synchronizing access to sink. */
mutable FCriticalSection SynchronizationObject;
TSharedPtr<FThreadWorker> WaitThreadWorker;
bool IsAlready()const{ return bAlready; }
bool bAlready = false;
private:
int32 ClusterCount = 0;
int32 CookedClusterCount = 0;
};

View File

@ -0,0 +1,86 @@
#pragma once
// project header
#include "CreatePatch/HotPatcherSettingBase.h"
#include "HotPatcherLog.h"
#include "CreatePatch/HotPatcherContext.h"
#include "CreatePatch/TimeRecorder.h"
#include "HotPatcherDelegates.h"
// engine header
#include "CoreMinimal.h"
#include "HotPatcherProxyBase.generated.h"
UCLASS()
class HOTPATCHERCORE_API UHotPatcherProxyBase : public UObject
{
public:
GENERATED_BODY()
virtual void Init(FPatcherEntitySettingBase* InSetting);
virtual void Shutdown();
FORCEINLINE virtual bool DoExport(){return false;};
FORCEINLINE virtual FPatcherEntitySettingBase* GetSettingObject(){return Setting;};
IAssetRegistry* GetAssetRegistry()const { return AssetRegistry; }
protected:
FORCEINLINE virtual void SetProxySettings(FPatcherEntitySettingBase* InSetting)
{
Setting = InSetting;
}
const TMap<ETargetPlatform,FName>& GetPlatformNameMapping(){ return PlatformNameMapping; }
public:
#if WITH_PACKAGE_CONTEXT
// virtual void InitPlatformPackageContexts();
FORCEINLINE TMap<ETargetPlatform,TSharedPtr<FSavePackageContext>> GetPlatformSavePackageContexts()const {return PlatformSavePackageContexts;}
FORCEINLINE TMap<ETargetPlatform,FSavePackageContext*> GetPlatformSavePackageContextsRaw()const;
TMap<ETargetPlatform,TSharedPtr<FSavePackageContext>> PlatformSavePackageContexts;
#endif
public:
FExportPakProcess OnPaking;
FExportPakShowMsg OnShowMsg;
protected:
FPatcherEntitySettingBase* Setting;
IAssetRegistry* AssetRegistry = NULL;
TMap<ETargetPlatform,FName> PlatformNameMapping;
};
inline void UHotPatcherProxyBase::Init(FPatcherEntitySettingBase* InSetting)
{
SetProxySettings(InSetting);
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
AssetRegistry = &AssetRegistryModule.Get();
UEnum* UEnumIns = THotPatcherTemplateHelper::GetUEnum<ETargetPlatform>();
for (int64 EnumIndex = 0;EnumIndex < UEnumIns->GetMaxEnumValue();++EnumIndex)
{
if(UEnumIns->IsValidEnumValue(EnumIndex))
{
FName EnumtorName = UEnumIns->GetNameByValue(EnumIndex);
PlatformNameMapping.Add((ETargetPlatform)EnumIndex,EnumtorName);
}
}
}
inline void UHotPatcherProxyBase::Shutdown()
{
AssetRegistry = nullptr;
}
#if WITH_PACKAGE_CONTEXT
FORCEINLINE TMap<ETargetPlatform, FSavePackageContext*> UHotPatcherProxyBase::GetPlatformSavePackageContextsRaw() const
{
TMap<ETargetPlatform,FSavePackageContext*> result;
TArray<ETargetPlatform> Keys;
GetPlatformSavePackageContexts().GetKeys(Keys);
for(const auto& Key:Keys)
{
result.Add(Key,GetPlatformSavePackageContexts().Find(Key)->Get());
}
return result;
}
#endif

View File

@ -0,0 +1,18 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
class IPatchableInterface
{
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
virtual void ImportConfig()=0;
virtual void ImportProjectConfig()=0;
virtual void ExportConfig()const=0;
virtual void ResetConfig() = 0;
virtual void DoGenerate()=0;
virtual FString GetMissionName()=0;
};

View File

@ -0,0 +1,54 @@
#pragma once
#include "CreatePatch/FExportPatchSettings.h"
#include "FPatchVersionDiff.h"
#include "HotPatcherProxyBase.h"
#include "ThreadUtils/FThreadUtils.hpp"
// engine header
#include "Interfaces/ITargetPlatformManagerModule.h"
#include "Interfaces/ITargetPlatform.h"
#include "Interfaces/ITargetPlatform.h"
#include "Templates/SharedPointer.h"
#include "IDetailsView.h"
#include "PropertyEditorModule.h"
#include "Cooker/MultiCooker/FCookShaderCollectionProxy.h"
#include "Widgets/Text/SMultiLineEditableText.h"
#include "PatcherProxy.generated.h"
using FPatchWorkerType = TFunction<bool(FHotPatcherPatchContext&)>;
using FPatchWorkers = TMap<FString,FPatchWorkerType>;
DECLARE_MULTICAST_DELEGATE_FourParams(FOnPakListGenerated,FHotPatcherPatchContext&,FChunkInfo&,ETargetPlatform,TArray<FPakCommand>&);
DECLARE_MULTICAST_DELEGATE_FourParams(FAddPatchWorkerEvent,FHotPatcherPatchContext&,FChunkInfo&,ETargetPlatform,TArray<FPakCommand>&);
UCLASS()
class HOTPATCHERCORE_API UPatcherProxy:public UHotPatcherProxyBase
{
GENERATED_UCLASS_BODY()
public:
virtual void Init(FPatcherEntitySettingBase* InSetting)override;
virtual void Shutdown() override;
virtual bool DoExport() override;
virtual FExportPatchSettings* GetSettingObject()override{ return (FExportPatchSettings*)Setting; }
bool CanExportPatch() const;
FORCEINLINE_DEBUGGABLE void AddPatchWorker(const FString& WorkerName,const FPatchWorkerType& Worker)
{
PatchWorkers.Add(WorkerName,Worker);
}
FORCEINLINE const FPatchWorkers& GetPatchWorkers()const{ return PatchWorkers; }
FORCEINLINE FPatherResult& GetPatcherResult(){ return PatcherResult; }
public:
FOnPakListGenerated OnPakListGenerated;
protected:
FPatchWorkers PatchWorkers;
private:
TSharedPtr<FHotPatcherPatchContext> PatchContext;
FPatherResult PatcherResult;
};

View File

@ -0,0 +1,22 @@
#pragma once
#include "CreatePatch/FExportReleaseSettings.h"
#include "HotPatcherProxyBase.h"
// ENGINE HEADER
#include "CoreMinimal.h"
#include "CoreGlobals.h"
#include "ReleaseProxy.generated.h"
UCLASS()
class HOTPATCHERCORE_API UReleaseProxy:public UHotPatcherProxyBase
{
public:
GENERATED_BODY()
virtual bool DoExport() override ;
FORCEINLINE bool IsRunningCommandlet()const{return ::IsRunningCommandlet();}
FORCEINLINE virtual FExportReleaseSettings* GetSettingObject()override { return (FExportReleaseSettings*)Setting; }
private:
FExportReleaseSettings* ExportReleaseSettings;
};

View File

@ -0,0 +1,15 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "IDetailCustomization.h"
class FReleaseSettingsDetails : public IDetailCustomization
{
public:
/** Makes a new instance of this detail layout class for a specific detail view requesting it */
static TSharedRef<IDetailCustomization> MakeInstance();
/** IDetailCustomization interface */
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
};

View File

@ -0,0 +1,52 @@
#pragma once
#include "CoreMinimal.h"
#include "Interfaces/IHttpRequest.h"
#include "Interfaces/IHttpResponse.h"
struct HOTPATCHERCORE_API FProjectVersionDesc
{
FString ProjectName;
FString EngineVersion;
FString PluginVersion;
FString GameName;
FString UserName;
};
struct HOTPATCHERCORE_API FServerRequestInfo
{
FString Host;
FString AppId;
FString Key;
};
struct HOTPATCHERCORE_API FCountServerlessWrapper
{
void Init(const FServerRequestInfo& InRequestInfo,const FProjectVersionDesc& InDesc)
{
RequestInfo = InRequestInfo;
Desc = InDesc;
}
~FCountServerlessWrapper();
void Processor();
public:
static FProjectVersionDesc MakeCurrentProject();
static FServerRequestInfo MakeServerRequestInfo();
protected:
void RequestObjectID();
void OnObjectIdReceived( FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess);
FHttpRequestPtr UpdateToServer(const FProjectVersionDesc& Desc,const FString& ObjectID);
void OnUpdateToServerReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess);
FHttpRequestPtr CreateToServer(const FProjectVersionDesc& Desc);
void CreateToServerReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess);
FString Decode(const FString& Encode);
protected:
FServerRequestInfo RequestInfo;
FProjectVersionDesc Desc;
FHttpRequestPtr ObjectIDRequest;
FHttpRequestPtr ToServerRequest;
};

View File

@ -0,0 +1,296 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "BaseTypes/FExternFileInfo.h"
#include "BaseTypes/FExternDirectoryInfo.h"
#include "BaseTypes/FPatcherSpecifyAsset.h"
#include "BaseTypes/HotPatcherBaseTypes.h"
#include "BaseTypes/FHotPatcherVersion.h"
#include "BaseTypes/FChunkInfo.h"
#include "BaseTypes/ETargetPlatform.h"
#include "CreatePatch/FExportPatchSettings.h"
// engine header
#include "Templates/SharedPointer.h"
#include "Dom/JsonObject.h"
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#if WITH_PACKAGE_CONTEXT
#include "UObject/SavePackage.h"
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION > 25
#include "Serialization/BulkDataManifest.h"
#endif
#endif
#include "FlibHotPatcherCoreHelper.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogHotPatcherCoreHelper, Log, All);
struct FExportPatchSettings;
struct FExecTimeRecoder
{
FExecTimeRecoder(const FString& InDispalyStr):DispalyStr(InDispalyStr)
{
BeginTime = FDateTime::Now();
}
~FExecTimeRecoder()
{
EndTime = FDateTime::Now();
FTimespan ClusterExecTime = EndTime - BeginTime;
UE_LOG(LogHotPatcherCoreHelper,Display,TEXT("%s Time : %fs"),*DispalyStr,ClusterExecTime.GetTotalSeconds())
}
FString DispalyStr;
FDateTime BeginTime;
FDateTime EndTime;
};
struct FProjectPackageAssetCollection
{
TArray<FDirectoryPath> DirectoryPaths;
TArray<FDirectoryPath> NeverCookPaths;
TArray<FSoftObjectPath> NeedCookPackages;
TArray<FSoftObjectPath> NeverCookPackages;
};
/**
*
*/
UCLASS()
class HOTPATCHERCORE_API UFlibHotPatcherCoreHelper : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "HotPatcherCore")
static TArray<FString> GetAllCookOption();
static void CheckInvalidCookFilesByAssetDependenciesInfo(
const FString& InProjectAbsDir,
const FString& OverrideCookedDir,
const FString& InPlatformName,
const FAssetDependenciesInfo& InAssetDependencies,
TArray<FAssetDetail>& OutValidAssets,
TArray<FAssetDetail>& OutInvalidAssets);
static FChunkInfo MakeChunkFromPatchSettings(struct FExportPatchSettings const* InPatchSetting);
static FChunkInfo MakeChunkFromPatchVerison(const FHotPatcherVersion& InPatchVersion);
static FString GetAssetCookedSavePath(const FString& BaseDir, const FString PacakgeName, const FString& Platform);
static FString GetProjectCookedDir();
#if WITH_PACKAGE_CONTEXT
static FSavePackageContext* CreateSaveContext(const ITargetPlatform* TargetPlatform,bool bUseZenLoader,const FString& OverrideCookedDir);
static TMap<ETargetPlatform,TSharedPtr<FSavePackageContext>> CreatePlatformsPackageContexts(const TArray<ETargetPlatform>& Platforms,bool bIoStore,const FString& OverrideCookedDir);
static bool SavePlatformBulkDataManifest(TMap<ETargetPlatform, TSharedPtr<FSavePackageContext>>&PlatformSavePackageContexts,ETargetPlatform Platform);
#endif
//UFUNCTION(BlueprintCallable)
static void CookAssets(
const TArray<FSoftObjectPath>& Assets,
const TArray<ETargetPlatform>& Platforms,
FCookActionCallback CookActionCallback,
#if WITH_PACKAGE_CONTEXT
class TMap<ETargetPlatform, FSavePackageContext*> PlatformSavePackageContext = TMap<
ETargetPlatform, FSavePackageContext*>{},
#endif
const FString& InSavePath = FPaths::Combine(FPaths::ConvertRelativePathToFull(FPaths::ProjectSavedDir()),
TEXT("Cooked"))
);
static bool CookPackage(
const FSoftObjectPath& AssetObjectPath,
TMap<ETargetPlatform,ITargetPlatform*> CookPlatforms,
FCookActionCallback CookActionCallback,
#if WITH_PACKAGE_CONTEXT
class TMap<FString,FSavePackageContext*> PlatformSavePackageContext = TMap<FString,FSavePackageContext*>{},
#endif
const FString& InSavePath = FPaths::Combine(FPaths::ConvertRelativePathToFull(FPaths::ProjectSavedDir()),TEXT("Cooked")),
bool bStorageConcurrent = false
);
static bool CookPackage(
UPackage* Package,
TMap<ETargetPlatform,ITargetPlatform*> CookPlatforms,
FCookActionCallback CookActionCallback,
#if WITH_PACKAGE_CONTEXT
class TMap<FString,FSavePackageContext*> PlatformSavePackageContext,
#endif
const FString& InSavePath,
bool bStorageConcurrent
);
static bool CookPackage(
UPackage* Package,
TMap<ETargetPlatform,ITargetPlatform*> CookPlatforms,
FCookActionCallback CookActionCallback,
#if WITH_PACKAGE_CONTEXT
class TMap<FString,FSavePackageContext*> PlatformSavePackageContext,
#endif
const TMap<FName,FString>& CookedPlatformSavePaths,
bool bStorageConcurrent
);
static void CookChunkAssets(
TArray<FAssetDetail> Assets,
const TArray<ETargetPlatform>& Platforms,
FCookActionCallback CookActionCallback,
#if WITH_PACKAGE_CONTEXT
class TMap<ETargetPlatform,FSavePackageContext*> PlatformSavePackageContext = TMap<ETargetPlatform,FSavePackageContext*>{},
#endif
const FString& InSavePath = FPaths::Combine(FPaths::ConvertRelativePathToFull(FPaths::ProjectSavedDir()),TEXT("Cooked"))
);
static ITargetPlatform* GetTargetPlatformByName(const FString& PlatformName);
static TArray<ITargetPlatform*> GetTargetPlatformsByNames(const TArray<ETargetPlatform>& PlatformNames);
UFUNCTION(BlueprintCallable, Category = "HotPatcherCore")
static FString GetUnrealPakBinary();
UFUNCTION(BlueprintCallable, Category = "HotPatcherCore")
static FString GetUECmdBinary();
static FProcHandle DoUnrealPak(TArray<FString> UnrealPakCommandletOptions, bool block);
static FString GetMetadataDir(const FString& ProjectDir,const FString& ProjectName,ETargetPlatform Platform);
static void CleanDefaultMetadataCache(const TArray<ETargetPlatform>& TargetPlatforms);
static void BackupMetadataDir(const FString& ProjectDir,const FString& ProjectName,const TArray<ETargetPlatform>& Platforms,const FString& OutDir);
static void BackupProjectConfigDir(const FString& ProjectDir,const FString& OutDir);
static FString ReleaseSummary(const FHotPatcherVersion& NewVersion);
static FString PatchSummary(const FPatchVersionDiff& DiffInfo);
static FString ReplacePakRegular(const FReplacePakRegular& RegularConf, const FString& InRegular);
static bool CheckSelectedAssetsCookStatus(const FString& OverrideCookedDir,const TArray<FString>& PlatformNames, const FAssetDependenciesInfo& SelectedAssets, FString& OutMsg);
static bool CheckPatchRequire(const FString& OverrideCookedDir,const FPatchVersionDiff& InDiff,const TArray<FString>& PlatformNames,FString& OutMsg);
// WindowsNoEditor to Windows
static FString Conv2IniPlatform(const FString& Platform);
static TArray<FString> GetSupportPlatforms();
static FString GetEncryptSettingsCommandlineOptions(const FPakEncryptSettings& EncryptSettings,const FString& PlatformName);
static ITargetPlatform* GetPlatformByName(const FString& Name);
/*
* 0x1 Add
* 0x2 Modyfy
*/
static void AnalysisDependenciesAssets(const FHotPatcherVersion& Base, const FHotPatcherVersion& New,FPatchVersionDiff& PakDiff,UClass* SearchClass,TArray<UClass*> DependenciesClass,bool bRecursive,int32 flags = 0x1|0x2);
static void AnalysisWidgetTree(const FHotPatcherVersion& Base, const FHotPatcherVersion& New,FPatchVersionDiff& PakDiff,int32 flags = 0x1|0x2);
static void AnalysisMaterialInstance(const FHotPatcherVersion& Base, const FHotPatcherVersion& New,FPatchVersionDiff& PakDiff,int32 flags= 0x1|0x2);
static FPatchVersionDiff DiffPatchVersionWithPatchSetting(const struct FExportPatchSettings& PatchSetting, const FHotPatcherVersion& Base, const FHotPatcherVersion& New);
// CurrenrVersionChunk中的过滤器会进行依赖分析TotalChunk的不会目的是让用户可以自己控制某个文件夹打包到哪个Pak里而不会对该文件夹下的资源进行依赖分析
static FChunkAssetDescribe DiffChunkWithPatchSetting(
const struct FExportPatchSettings& PatchSetting,
const FChunkInfo& CurrentVersionChunk,
const FChunkInfo& TotalChunk
//,TMap<FString, FAssetDependenciesInfo>& ScanedCaches
);
static FChunkAssetDescribe DiffChunkByBaseVersionWithPatchSetting(
const struct FExportPatchSettings& PatchSetting,
const FChunkInfo& CurrentVersionChunk,
const FChunkInfo& TotalChunk,
const FHotPatcherVersion& BaseVersion
//,TMap<FString, FAssetDependenciesInfo>& ScanedCaches
);
static bool SerializeAssetRegistryByDetails(IAssetRegistry* AssetRegistry, const FString& PlatformName, const TArray<FAssetDetail>& AssetDetails, const FString& SavePath, FAssetRegistrySerializationOptions SaveOptions);
static bool SerializeAssetRegistryByDetails(IAssetRegistry* AssetRegistry, const FString& PlatformName, const TArray<FAssetDetail>& AssetDetails, const FString& SavePath);
static bool SerializeAssetRegistry(IAssetRegistry* AssetRegistry, const FString& PlatformName, const TArray<FString>& PackagePaths, const FString& SavePath, FAssetRegistrySerializationOptions
SaveOptions);
static FHotPatcherVersion MakeNewRelease(const FHotPatcherVersion& InBaseVersion, const FHotPatcherVersion& InCurrentVersion, FExportPatchSettings* InPatchSettings);
static FHotPatcherVersion MakeNewReleaseByDiff(const FHotPatcherVersion& InBaseVersion, const FPatchVersionDiff& InDiff, FExportPatchSettings* InPatchSettings = NULL);
public:
// In: "C:\Users\lipengzha\Documents\UnrealProjects\Blank425\Intermediate\Staging\Blank425.upluginmanifest" "../../../Blank425/Plugins/Blank425.upluginmanifest
// To: upluginmanifest
static FString GetPakCommandExtersion(const FString& InCommand);
// [Pak]
// +ExtensionsToNotUsePluginCompression=uplugin
// +ExtensionsToNotUsePluginCompression=upluginmanifest
// +ExtensionsToNotUsePluginCompression=uproject
// +ExtensionsToNotUsePluginCompression=ini
// +ExtensionsToNotUsePluginCompression=icu
// +ExtensionsToNotUsePluginCompression=res
static TArray<FString> GetExtensionsToNotUsePluginCompressionByGConfig();
static void AppendPakCommandOptions(
TArray<FString>& OriginCommands,
const TArray<FString>& Options,
bool bAppendAllMatch,
const TArray<FString>& AppendFileExtersions,
const TArray<FString>& IgnoreFormats,
const TArray<FString>& InIgnoreOptions);
static FProjectPackageAssetCollection ImportProjectSettingsPackages();
static void WaitDistanceFieldAsyncQueueComplete();
static void WaitForAsyncFileWrites();
static void WaitDDCComplete();
static bool IsCanCookPackage(const FString& LongPackageName);
static void ImportProjectSettingsToScannerConfig(FAssetScanConfig& AssetScanConfig);
static void ImportProjectNotAssetDir(TArray<FPlatformExternAssets>& PlatformExternAssets, ETargetPlatform AddToTargetPlatform);
static TArray<FExternDirectoryInfo> GetProjectNotAssetDirConfig();
static void CacheForCookedPlatformData(
const TArray<UPackage*>& Packages,
TArray<ITargetPlatform*> TargetPlatforms,
TSet<UObject*>& ProcessedObjs,
TSet<UObject*>& PendingCachePlatformDataObjects,
bool bStorageConcurrent = false,
bool bWaitComplete = false,
TFunction<void(UPackage*,UObject*)> OnPreCacheObjectWithOuter=nullptr
);
static void CacheForCookedPlatformData(
const TArray<FSoftObjectPath>& ObjectPaths,
TArray<ITargetPlatform*> TargetPlatforms,
TSet<UObject*>& ProcessedObjs,
TSet<UObject*>& PendingCachePlatformDataObjects,
bool bStorageConcurrent,
bool bWaitComplete = false,
TFunction<void(UPackage*,UObject*)> OnPreCacheObjectWithOuter = nullptr
);
static void WaitObjectsCachePlatformDataComplete(TSet<UObject*>& CachedPlatformDataObjects,TSet<UObject*>& PendingCachePlatformDataObjects,TArray<ITargetPlatform*> TargetPlatforms);
static uint32 GetCookSaveFlag(UPackage* Package,bool bUnversioned = true,bool bStorageConcurrent = false,bool CookLinkerDiff = false);
static EObjectFlags GetObjectFlagForCooked(UPackage* Package);
static TArray<FAssetDetail> GetReferenceRecursivelyByClassName(const FAssetDetail& AssetDetail,const TArray<FString>& AssetTypeNames,const TArray<EAssetRegistryDependencyTypeEx>& RefType,bool bRecursive = true);
static TArray<UClass*> GetDerivedClasses(UClass* BaseClass,bool bRecursive,bool bContainSelf = false);
static void DeleteDirectory(const FString& Dir);
static int32 GetMemoryMappingAlignment(const FString& PlatformName);
static void SaveGlobalShaderMapFiles(const TArrayView<const ITargetPlatform* const>& Platforms,const FString& BaseOutputDir);
static FString GetSavePackageResultStr(ESavePackageResult Result);
static void AdaptorOldVersionConfig(FAssetScanConfig& ScanConfig,const FString& JsonContent);
static bool GetIniPlatformName(const FString& PlatformName,FString& OutIniPlatformName);
// need add UNREALED_API to FAssetRegistryGenerator
// all chunksinfo.csv / pakchunklist.txt / assetregistry.bin
static bool SerializeChunksManifests(ITargetPlatform* TargetPlatform, const TSet<FName>&, const TSet<FName>&, bool bGenerateStreamingInstallManifest = true);
static TArray<UClass*> GetClassesByNames(const TArray<FName>& ClassesNames);
static TArray<UClass*> GetAllMaterialClasses();
static bool IsMaterialClasses(UClass* Class);
static bool IsMaterialClassName(FName ClassName);
static bool AssetDetailsHasClasses(const TArray<FAssetDetail>& AssetDetails,TSet<FName> ClasssName);
static TSet<FName> GetAllMaterialClassesNames();
static TArray<UClass*> GetPreCacheClasses();
static void DumpActiveTargetPlatforms();
static FString GetPlatformsStr(TArray<ETargetPlatform> Platforms);
};

View File

@ -0,0 +1,31 @@
// Copyright 2019 Lipeng Zha, Inc. All Rights Reserved.
#pragma once
#include "HotPatcherSettings.h"
// engine header
#include "CoreMinimal.h"
HOTPATCHERCORE_API extern struct FExportPatchSettings* GPatchSettings;
HOTPATCHERCORE_API extern struct FExportReleaseSettings* GReleaseSettings;
extern HOTPATCHERCORE_API bool GCookLog;
extern HOTPATCHERCORE_API FString GToolName;
extern HOTPATCHERCORE_API FString GRemoteVersionFile;
extern HOTPATCHERCORE_API int32 GToolMainVersion;
extern HOTPATCHERCORE_API int32 GToolPatchVersion;
HOTPATCHERCORE_API void ReceiveOutputMsg(class FProcWorkerThread* Worker,const FString& InMsg);
DECLARE_MULTICAST_DELEGATE_TwoParams(FNotificationEvent,FText,const FString&)
class HOTPATCHERCORE_API FHotPatcherCoreModule : public IModuleInterface
{
public:
static FHotPatcherCoreModule& Get();
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
virtual int32 GetMainVersion()const{ return CURRENT_VERSION_ID; }
virtual int32 GetPatchVersion()const { return CURRENT_PATCH_ID; }
};

View File

@ -0,0 +1,15 @@
#pragma once
#include "CoreMinimal.h"
#include "GameDelegates.h"
DECLARE_MULTICAST_DELEGATE_TwoParams(FNotificationEvent,FText,const FString&)
class HOTPATCHERCORE_API FHotPatcherDelegates
{
public:
/** Return a single FGameDelegates object */
static FHotPatcherDelegates& Get();
DEFINE_GAME_DELEGATE_TYPED(NotifyFileGenerated,FNotificationEvent);
};

View File

@ -0,0 +1,79 @@
#pragma once
#include "ETargetPlatform.h"
#include "CreatePatch/FExportPatchSettings.h"
#include "FlibPatchParserHelper.h"
#include "CoreMinimal.h"
#include "Kismet/KismetTextLibrary.h"
#include "HotPatcherSettings.generated.h"
#define LOCTEXT_NAMESPACE "UHotPatcherSettings"
USTRUCT()
struct FPakExternalInfo
{
GENERATED_BODY()
FPakExternalInfo()=default;
FPakExternalInfo(const FPakExternalInfo&)=default;
UPROPERTY(EditAnywhere)
FString PakName;
UPROPERTY(EditAnywhere)
TArray<ETargetPlatform> TargetPlatforms;
UPROPERTY(EditAnywhere)
FPlatformExternAssets AddExternAssetsToPlatform;
};
UCLASS(config = Game, defaultconfig)
class HOTPATCHERCORE_API UHotPatcherSettings:public UObject
{
GENERATED_UCLASS_BODY()
public:
UPROPERTY(EditAnywhere, config, Category = "Editor")
bool bWhiteListCookInEditor = false;
UPROPERTY(EditAnywhere, config, Category = "Editor")
TArray<ETargetPlatform> PlatformWhitelists;
FString GetTempSavedDir()const;
FString GetHPLSavedDir()const;
UPROPERTY(EditAnywhere, config, Category = "ConfigTemplate")
FExportPatchSettings TempPatchSetting;
UPROPERTY(EditAnywhere, config, Category = "Preset")
TArray<FExportPatchSettings> PresetConfigs;
UPROPERTY(EditAnywhere, config, Category = "Preview")
bool bPreviewTooltips = true;
UPROPERTY(EditAnywhere, config, Category = "Preview")
bool bExternalFilesCheck = false;
UPROPERTY(config)
bool bServerlessCounter = true;
UPROPERTY(EditAnywhere, config, Category = "Advanced")
bool bServerlessCounterInCmdlet = false;
};
FORCEINLINE FString UHotPatcherSettings::GetTempSavedDir()const
{
return UFlibPatchParserHelper::ReplaceMark(TempPatchSetting.SavePath.Path);
}
FORCEINLINE UHotPatcherSettings::UHotPatcherSettings(const FObjectInitializer& Initializer):Super(Initializer)
{
auto ResetTempSettings = [](FExportPatchSettings& InTempPatchSetting)
{
InTempPatchSetting.bByBaseVersion=false;
// TempPatchSetting.bStorageAssetDependencies = false;
InTempPatchSetting.bStorageDiffAnalysisResults=false;
InTempPatchSetting.bStorageDeletedAssetsToNewReleaseJson = false;
InTempPatchSetting.bStorageConfig = false;
InTempPatchSetting.bStorageNewRelease = false;
InTempPatchSetting.bStoragePakFileInfo = false;
InTempPatchSetting.bCookPatchAssets = true;
InTempPatchSetting.CookShaderOptions.bSharedShaderLibrary = false;
InTempPatchSetting.CookShaderOptions.bNativeShader = false;
InTempPatchSetting.EncryptSettings.bUseDefaultCryptoIni = true;
InTempPatchSetting.SavePath.Path = TEXT("[PROJECTDIR]/Saved/HotPatcher/Paks");
};
ResetTempSettings(TempPatchSetting);
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,83 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "Resources/Version.h"
#include "ShaderCodeLibrary.h"
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "FlibShaderCodeLibraryHelper.generated.h"
#if ENGINE_MAJOR_VERSION > 4 || ENGINE_MINOR_VERSION > 26
#define SHADER_COOKER_CLASS FShaderLibraryCooker
#else
#define SHADER_COOKER_CLASS FShaderCodeLibrary
#endif
struct FShaderCodeFormatMap
{
ITargetPlatform* Platform;
bool bIsNative;
FString SaveBaseDir;
struct FShaderFormatNameFiles
{
FString ShaderName;
TArray<FString> Files;
};
// etc GLSL_ES3_1_ANDROID something files
// SF_METAL something files
TMap<FString,FShaderFormatNameFiles> ShaderCodeTypeFilesMap;
};
//
// struct FMergeShaderCollectionProxy
// {
// FMergeShaderCollectionProxy(const TArray<FShaderCodeFormatMap>& InShaderCodeFiles);
// virtual ~FMergeShaderCollectionProxy();
// void Init();
// void Shutdown();
// private:
// TArray<FShaderCodeFormatMap> ShaderCodeFiles;
// };
/**
*
*/
UCLASS()
class HOTPATCHERCORE_API UFlibShaderCodeLibraryHelper : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
#if ENGINE_MAJOR_VERSION > 4 || ENGINE_MINOR_VERSION > 25
static TArray<SHADER_COOKER_CLASS::FShaderFormatDescriptor> GetShaderFormatsWithStableKeys(const TArray<FName>& ShaderFormats,bool bNeedShaderStableKeys = true,bool bNeedsDeterministicOrder = false);
#endif
static TArray<FName> GetShaderFormatsByTargetPlatform(ITargetPlatform* TargetPlatform);
static FString GenerateShaderCodeLibraryName(FString const& Name, bool bIsIterateSharedBuild);
static bool SaveShaderLibrary(const ITargetPlatform* TargetPlatform,TArray<FName> ShaderFormats, const FString& ShaderCodeDir,const FString& RootMetaDataPath, bool bMaster);
static bool SaveShaderLibrary(const ITargetPlatform* TargetPlatform, const TArray<TSet<FName>>* ChunkAssignments, FString const& Name, const FString&
SaveBaseDir, bool bMaster);
static TArray<FString> FindCookedShaderLibByPlatform(const FString& PlatfomName,const FString& Directory,bool bRecursive = false);
static TArray<FString> FindCookedShaderLibByShaderFrmat(const FString& ShaderFormatName,const FString& Directory);
static void WaitShaderCompilingComplete();
static void CancelMaterialShaderCompile(UMaterialInterface* MaterialInterface);
static void CleanShaderWorkerDir();
static FString ShaderExtension;
static FString ShaderAssetInfoExtension;
static FString StableExtension;
FORCEINLINE static FString GetCodeArchiveFilename(const FString& BaseDir, const FString& LibraryName, FName Platform)
{
return BaseDir / FString::Printf(TEXT("ShaderArchive-%s-"), *LibraryName) + Platform.ToString() + ShaderExtension;
}
FORCEINLINE static FString GetShaderAssetInfoFilename(const FString& BaseDir, const FString& LibraryName, FName Platform)
{
return BaseDir / FString::Printf(TEXT("ShaderAssetInfo-%s-"), *LibraryName) + Platform.ToString() + ShaderAssetInfoExtension;
}
FORCEINLINE static FString GetStableInfoArchiveFilename(const FString& BaseDir, const FString& LibraryName, FName Platform)
{
return BaseDir / FString::Printf(TEXT("ShaderStableInfo-%s-"), *LibraryName) + Platform.ToString() + StableExtension;
}
};

View File

@ -0,0 +1,141 @@
#pragma once
#include "FThreadUtils.hpp"
#include "CoreMinimal.h"
#include "Misc/Paths.h"
#include "GenericPlatform/GenericPlatformProcess.h"
class FProcWorkerThread;
DECLARE_DELEGATE_TwoParams(FOutputMsgDelegate,FProcWorkerThread*,const FString&);
DECLARE_MULTICAST_DELEGATE_OneParam(FProcStatusDelegate,FProcWorkerThread*);
class FProcWorkerThread : public FThreadWorker
{
public:
explicit FProcWorkerThread(const TCHAR *InThreadName,const FString& InProgramPath,const FString& InParams)
: FThreadWorker(InThreadName, []() {}), mProgramPath(InProgramPath), mPragramParams(InParams)
{}
virtual uint32 Run()override
{
if (FPaths::FileExists(mProgramPath))
{
FPlatformProcess::CreatePipe(mReadPipe, mWritePipe);
// std::cout << TCHAR_TO_ANSI(*mProgramPath) << " " << TCHAR_TO_ANSI(*mPragramParams) << std::endl;
mProcessHandle = FPlatformProcess::CreateProc(*mProgramPath, *mPragramParams, false, true, true, &mProcessID, 1, NULL, mWritePipe,mReadPipe);
if (mProcessHandle.IsValid() && FPlatformProcess::IsApplicationRunning(mProcessID))
{
if (ProcBeginDelegate.IsBound())
ProcBeginDelegate.Broadcast(this);
}
FString Line;
while (mProcessHandle.IsValid() && FPlatformProcess::IsApplicationRunning(mProcessID))
{
FString NewLine = FPlatformProcess::ReadPipe(mReadPipe);
if (NewLine.Len() > 0)
{
// process the string to break it up in to lines
Line += NewLine;
TArray<FString> StringArray;
int32 count = Line.ParseIntoArray(StringArray, TEXT("\n"), true);
if (count > 1)
{
for (int32 Index = 0; Index < count - 1; ++Index)
{
StringArray[Index].TrimEndInline();
ProcOutputMsgDelegate.ExecuteIfBound(this,StringArray[Index]);
}
Line = StringArray[count - 1];
if (NewLine.EndsWith(TEXT("\n")))
{
Line += TEXT("\n");
}
}
}
FPlatformProcess::Sleep(0.2f);
}
bool bRunSuccessfuly = false;
int32 ProcReturnCode;
if (FPlatformProcess::GetProcReturnCode(mProcessHandle,&ProcReturnCode))
{
if (ProcReturnCode == 0)
{
bRunSuccessfuly = true;
}
}
ProcOutputMsgDelegate.ExecuteIfBound(this,FString::Printf(TEXT("ProcWorker %s return value %d."),*mThreadName,ProcReturnCode));
mThreadStatus = EThreadStatus::Completed;
if (bRunSuccessfuly)
{
if(ProcSuccessedDelegate.IsBound())
{
ProcSuccessedDelegate.Broadcast(this);
}
}
else
{
if (ProcFaildDelegate.IsBound())
{
ProcFaildDelegate.Broadcast(this);
}
}
}
return 0;
}
virtual void Exit()override
{
FThreadWorker::Exit();
}
virtual void Cancel()override
{
if (GetThreadStatus() != EThreadStatus::Busy)
{
if (CancelDelegate.IsBound())
CancelDelegate.Broadcast();
return;
}
mThreadStatus = EThreadStatus::Canceling;
if (mProcessHandle.IsValid() && FPlatformProcess::IsApplicationRunning(mProcessID))
{
FPlatformProcess::TerminateProc(mProcessHandle, true);
if (ProcFaildDelegate.IsBound())
{
ProcFaildDelegate.Broadcast(this);
}
mProcessHandle.Reset();
mProcessID = 0;
}
mThreadStatus = EThreadStatus::Canceled;
if (CancelDelegate.IsBound())
{
CancelDelegate.Broadcast();
}
}
virtual uint32 GetProcesId()const { return mProcessID; }
virtual FProcHandle GetProcessHandle()const { return mProcessHandle; }
public:
FProcStatusDelegate ProcBeginDelegate;
FProcStatusDelegate ProcSuccessedDelegate;
FProcStatusDelegate ProcFaildDelegate;
FOutputMsgDelegate ProcOutputMsgDelegate;
private:
FRunnableThread* mThread;
FString mProgramPath;
FString mPragramParams;
void* mReadPipe;
void* mWritePipe;
uint32 mProcessID;
FProcHandle mProcessHandle;
};

View File

@ -0,0 +1,83 @@
#pragma once
#include "HAL/Runnable.h"
#include "HAL/RunnableThread.h"
DECLARE_MULTICAST_DELEGATE(FThreadWorkerStatusDelegate);
namespace EThreadStatus
{
enum Type
{
InActive,
Busy,
Canceling,
Canceled,
Completed
};
}
class FThreadWorker : public FRunnable
{
public:
using FCallback = TFunction<void()>;
explicit FThreadWorker(const TCHAR *InThreadName, const FCallback& InRunFunc)
:mThreadName(InThreadName),mRunFunc(InRunFunc),mThreadStatus(EThreadStatus::InActive)
{}
virtual void Execute()
{
if (GetThreadStatus() == EThreadStatus::InActive)
{
mThread = FRunnableThread::Create(this, *mThreadName);
if (mThread)
{
mThreadStatus = EThreadStatus::Busy;
}
}
}
virtual void Join()
{
mThread->WaitForCompletion();
}
virtual uint32 Run()override
{
mRunFunc();
return 0;
}
virtual void Stop()override
{
Cancel();
}
virtual void Cancel()
{
mThreadStatus = EThreadStatus::Canceled;
}
virtual void Exit()override
{
mThreadStatus = EThreadStatus::Completed;
}
virtual EThreadStatus::Type GetThreadStatus()const
{
return mThreadStatus;
}
public:
FThreadWorkerStatusDelegate CancelDelegate;
FORCEINLINE FString GetThreadName()const {return mThreadName;}
virtual bool IsCompleted()const { return GetThreadStatus() == EThreadStatus::Completed || GetThreadStatus() == EThreadStatus::Canceled;}
protected:
FString mThreadName;
FCallback mRunFunc;
FRunnableThread* mThread;
volatile EThreadStatus::Type mThreadStatus;
private:
FThreadWorker(const FThreadWorker&) = delete;
FThreadWorker& operator=(const FThreadWorker&) = delete;
};

View File

@ -0,0 +1,108 @@
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System;
using System.Collections.Generic;
using System.IO;
public class HotPatcherEditor : ModuleRules
{
public HotPatcherEditor(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
bLegacyPublicIncludePaths = false;
OptimizeCode = CodeOptimization.InShippingBuildsOnly;
PublicIncludePaths.AddRange(new string[] { });
PrivateIncludePaths.AddRange(new string[] {});
PublicDependencyModuleNames.AddRange(
new string[]
{
"UnrealEd",
"UMG",
"UMGEditor",
"Core",
"Json",
"ContentBrowser",
"SandboxFile",
"JsonUtilities",
"TargetPlatform",
"DesktopPlatform",
"Projects",
"Settings",
"EditorStyle",
"HTTP",
"RHI",
"EngineSettings",
"AssetRegistry",
"PakFileUtilities",
"HotPatcherRuntime",
"BinariesPatchFeature",
"HotPatcherCore"
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Core",
"UnrealEd",
"Projects",
"DesktopPlatform",
"InputCore",
"LevelEditor",
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"RenderCore"
// ... add private dependencies that you statically link with here ...
}
);
if (Target.Version.MajorVersion > 4 || Target.Version.MinorVersion > 23)
{
PublicDependencyModuleNames.AddRange(new string[]{
"ToolMenus",
"TraceLog"
});
}
System.Func<string, bool,bool> AddPublicDefinitions = (string MacroName,bool bEnable) =>
{
PublicDefinitions.Add(string.Format("{0}={1}",MacroName, bEnable ? 1 : 0));
return true;
};
AddPublicDefinitions("ENABLE_COOK_ENGINE_MAP", false);
AddPublicDefinitions("ENABLE_COOK_PLUGIN_MAP", false);
BuildVersion Version;
BuildVersion.TryRead(BuildVersion.GetDefaultFileName(), out Version);
AddPublicDefinitions("WITH_EDITOR_SECTION", Version.MajorVersion > 4 || Version.MinorVersion > 24);
System.Console.WriteLine("MajorVersion {0} MinorVersion: {1} PatchVersion {2}",Target.Version.MajorVersion,Target.Version.MinorVersion,Target.Version.PatchVersion);
PublicDefinitions.AddRange(new string[]
{
"ENABLE_ORIGINAL_COOKER=0"
});
PublicDefinitions.AddRange(new string[]
{
"ENABLE_UPDATER_CHECK=1"
});
bool bEnablePackageContext = true;
AddPublicDefinitions("WITH_PACKAGE_CONTEXT", (Version.MajorVersion > 4 || Version.MinorVersion > 23) && bEnablePackageContext);
if (Version.MajorVersion > 4 || Version.MinorVersion > 26)
{
PublicDependencyModuleNames.AddRange(new string[]
{
"IoStoreUtilities",
"UnrealEd"
});
}
}
}

View File

@ -0,0 +1,30 @@
#pragma once
#include "Dom/JsonObject.h"
#include "Templates/SharedPointer.h"
#include "Model/FHotPatcherContextBase.h"
#include "Model/FOriginalCookerContext.h"
struct IOriginalCookerChildWidget
{
virtual TSharedPtr<FJsonObject> SerializeAsJson()const {return nullptr;};
virtual void DeSerializeFromJsonObj(TSharedPtr<FJsonObject>const & InJsonObject){};
virtual FString GetSerializeName()const { return TEXT(""); };
virtual void Reset() {};
virtual void SetContext(TSharedPtr<FHotPatcherContextBase> InContext)
{
mContext = InContext;
}
TSharedPtr<FHotPatcherContextBase> GetContext()const { return mContext; };
virtual FOriginalCookerContext* GetCookerContextPtr()const
{
FOriginalCookerContext* Ptr = nullptr;
if(GetContext().IsValid())
{
Ptr = (FOriginalCookerContext*)mContext.Get();
}
return Ptr;
}
virtual ~IOriginalCookerChildWidget(){}
protected:
TSharedPtr<FHotPatcherContextBase> mContext;
};

View File

@ -0,0 +1,156 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
// #include "HotPatcherPrivatePCH.h"
#include "SHotPatcherCookMaps.h"
#include "SProjectCookMapListRow.h"
#include "Widgets/Input/SHyperlink.h"
#include "Widgets/Layout/SSeparator.h"
#include "FlibPatchParserHelper.h"
#include "Kismet/KismetSystemLibrary.h"
#define LOCTEXT_NAMESPACE "SHotPatcherCookMaps"
void SHotPatcherCookMaps::Construct(const FArguments& InArgs, TSharedPtr<FHotPatcherContextBase> InContext)
{
SetContext(InContext);
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.FillHeight(1.0)
.Padding(0.0f, 2.0f, 0.0f, 0.0f)
[
// map list
SAssignNew(MapListView, SListView<TSharedPtr<FString> >)
.HeaderRow
(
SNew(SHeaderRow)
.Visibility(EVisibility::Collapsed)
+ SHeaderRow::Column("MapName")
.DefaultLabel(LOCTEXT("MapListMapNameColumnHeader", "Map"))
.FillWidth(1.0f)
)
.ItemHeight(16.0f)
.ListItemsSource(&MapList)
.OnGenerateRow(this, &SHotPatcherCookMaps::HandleMapListViewGenerateRow)
.SelectionMode(ESelectionMode::None)
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 6.0f, 0.0f, 4.0f)
[
SNew(SSeparator)
.Orientation(Orient_Horizontal)
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
.HAlign(HAlign_Right)
[
SNew(STextBlock)
.Text(LOCTEXT("SelectLabel", "Select:"))
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(8.0f, 0.0f)
[
// all Maps hyper-link
SNew(SHyperlink)
.OnNavigate(this, &SHotPatcherCookMaps::HandleAllMapHyperlinkNavigate, true)
.Text(LOCTEXT("AllMapHyperlinkLabel", "All"))
.ToolTipText(LOCTEXT("AllMapButtonTooltip", "Select all available Maps."))
]
+ SHorizontalBox::Slot()
.AutoWidth()
[
// no Maps hyper-link
SNew(SHyperlink)
.OnNavigate(this, &SHotPatcherCookMaps::HandleAllMapHyperlinkNavigate, false)
.Text(LOCTEXT("NoMapsHyperlinkLabel", "None"))
.ToolTipText(LOCTEXT("NoMapsHyperlinkTooltip", "Deselect all Maps."))
]
]
];
RefreshMapList();
}
TSharedPtr<FJsonObject> SHotPatcherCookMaps::SerializeAsJson() const
{
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject);
TArray<FString> SelectedPlatformList = GetCookerContextPtr()->GetAllSelectedCookMap();
TArray<TSharedPtr<FJsonValue>> SelectedMapsJsonList;
for (const auto& Platform : SelectedPlatformList)
{
SelectedMapsJsonList.Add(MakeShareable(new FJsonValueString(Platform)));
}
JsonObject->SetArrayField(TEXT("CookMaps"), SelectedMapsJsonList);
JsonObject->SetBoolField(TEXT("bCookAllMap"), IsCookAllMap());
return JsonObject;
}
void SHotPatcherCookMaps::DeSerializeFromJsonObj(TSharedPtr<FJsonObject>const & InJsonObject)
{
TArray<TSharedPtr<FJsonValue>> SelectedMapsJsonList = InJsonObject->GetArrayField(TEXT("CookMaps"));
bool IsCookAllMap = InJsonObject->GetBoolField(TEXT("bCookAllMap"));
if (!IsCookAllMap)
{
TArray<TSharedPtr<FString>> SelectedMaps;
for (const auto& PlatformJson : SelectedMapsJsonList)
{
FString Map = PlatformJson->AsString();
SelectedMaps.Add(MakeShareable(new FString(Map)));
GetCookerContextPtr()->AddSelectedCookMap(Map);
}
}
else
{
HandleAllMapHyperlinkNavigate(IsCookAllMap);
}
}
FString SHotPatcherCookMaps::GetSerializeName()const
{
return TEXT("Maps");
}
void SHotPatcherCookMaps::Reset()
{
GetCookerContextPtr()->ClearAllMap();
}
void SHotPatcherCookMaps::RefreshMapList()
{
MapList.Reset();
TArray<FString> AvailableMaps = UFlibPatchParserHelper::GetAvailableMaps(UKismetSystemLibrary::GetProjectDirectory(), ENABLE_COOK_ENGINE_MAP, ENABLE_COOK_PLUGIN_MAP, true);
for (int32 AvailableMapIndex = 0; AvailableMapIndex < AvailableMaps.Num(); ++AvailableMapIndex)
{
FString& Map = AvailableMaps[AvailableMapIndex];
MapList.Add(MakeShareable(new FString(Map)));
}
MapListView->RequestListRefresh();
}
TSharedRef<ITableRow> SHotPatcherCookMaps::HandleMapListViewGenerateRow(TSharedPtr<FString> InItem, const TSharedRef<STableViewBase>& OwnerTable)
{
return SNew(SProjectCookMapListRow, mContext)
.MapName(InItem)
.OwnerTableView(OwnerTable);
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,72 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Model/FOriginalCookerContext.h"
#include "Templates/SharedPointer.h"
#include "FlibPatchParserHelper.h"
#include "Kismet/KismetSystemLibrary.h"
// project header
#include "IOriginalCookerChildWidget.h"
/**
* Implements the cooked Maps panel.
*/
class SHotPatcherCookMaps
: public SCompoundWidget,public IOriginalCookerChildWidget
{
public:
SLATE_BEGIN_ARGS(SHotPatcherCookMaps) { }
SLATE_END_ARGS()
public:
/**
* Constructs the widget.
*
* @param InArgs The Slate argument list.
*/
void Construct( const FArguments& InArgs,TSharedPtr<FHotPatcherContextBase> InContext);
public:
virtual TSharedPtr<FJsonObject> SerializeAsJson()const override;
virtual void DeSerializeFromJsonObj(TSharedPtr<FJsonObject>const & InJsonObject)override;
virtual FString GetSerializeName()const override;
virtual void Reset() override;
bool IsCookAllMap()const { return MapList.Num() == GetCookerContextPtr()->GetAllSelectedCookMap().Num(); }
protected:
// Callback for clicking the 'Select All Maps' button.
void HandleAllMapHyperlinkNavigate(bool AllMap)
{
if (mContext.IsValid())
{
if (AllMap)
{
TArray<FString> Maps = UFlibPatchParserHelper::GetAvailableMaps(UKismetSystemLibrary::GetProjectDirectory(),false,false,true);
for (int32 MapIndex = 0; MapIndex < Maps.Num(); ++MapIndex)
{
GetCookerContextPtr()->AddSelectedCookMap(Maps[MapIndex]);
}
}
else {
GetCookerContextPtr()->ClearAllMap();
}
}
}
TSharedRef<ITableRow> HandleMapListViewGenerateRow(TSharedPtr<FString> InItem, const TSharedRef<STableViewBase>& OwnerTable);
void RefreshMapList();
private:
/** Holds the map list. */
TArray<TSharedPtr<FString> > MapList;
/** Holds the map list view. */
TSharedPtr<SListView<TSharedPtr<FString> > > MapListView;
};

View File

@ -0,0 +1,158 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
// #include "HotPatcherPrivatePCH.h"
#include "SHotPatcherCookSetting.h"
#include "SProjectCookSettingsListRow.h"
#include "Widgets/Input/SHyperlink.h"
#include "Widgets/Layout/SSeparator.h"
#include "Kismet/KismetTextLibrary.h"
#include "FlibHotPatcherCoreHelper.h"
#define LOCTEXT_NAMESPACE "SHotPatcherCookSetting"
void SHotPatcherCookSetting::Construct(const FArguments& InArgs, TSharedPtr<FHotPatcherContextBase> InContext)
{
SetContext(InContext);
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.FillHeight(1.0)
.Padding(0.0f, 2.0f, 0.0f, 0.0f)
[
// map list
SAssignNew(SettingListView, SListView<TSharedPtr<FString> >)
.HeaderRow
(
SNew(SHeaderRow)
.Visibility(EVisibility::Collapsed)
+ SHeaderRow::Column("SettingName")
.DefaultLabel(LOCTEXT("SettingListSettingNameColumnHeader", "Setting"))
.FillWidth(1.0f)
)
.ItemHeight(16.0f)
.ListItemsSource(&SettingList)
.OnGenerateRow(this, &SHotPatcherCookSetting::HandleCookSettingListViewGenerateRow)
.SelectionMode(ESelectionMode::None)
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f,2.0f,0.0f,0.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("ProjectCookSettingExParama", "Other Options:"))
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 2.0f, 0.0f, 0.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.Padding(0.0f, 2.0f, 0.0f, 0.0f)
.FillWidth(1.0f)
[
SAssignNew(ExternSettingTextBox, SEditableTextBox)
]
]
];
if(GetCookerContextPtr())
{
GetCookerContextPtr()->OnRequestExSettings.BindRaw(this, &SHotPatcherCookSetting::HandleRequestExSettings);
}
RefreshSettingsList();
}
TSharedPtr<FJsonObject> SHotPatcherCookSetting::SerializeAsJson() const
{
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject);
TArray<FString> SelectedCookSettingList = GetCookerContextPtr()->GetAllSelectedSettings();
TArray<TSharedPtr<FJsonValue>> CookSettingJsonList;
for (const auto& Platform : SelectedCookSettingList)
{
CookSettingJsonList.Add(MakeShareable(new FJsonValueString(Platform)));
}
JsonObject->SetArrayField(TEXT("CookSettings"), CookSettingJsonList);
FString FinalOptions = ExternSettingTextBox->GetText().ToString();
for (const auto& DefaultCookParam : GetDefaultCookParams())
{
if (!FinalOptions.Contains(DefaultCookParam))
{
FinalOptions.Append(TEXT(" ") + DefaultCookParam);
}
}
JsonObject->SetStringField(TEXT("Options"), FinalOptions);
return JsonObject;
}
void SHotPatcherCookSetting::DeSerializeFromJsonObj(TSharedPtr<FJsonObject>const & InJsonObject)
{
TArray<TSharedPtr<FJsonValue>> CookSettingJsonList = InJsonObject->GetArrayField(TEXT("CookSettings"));
TArray<TSharedPtr<FString>> SelectedCookSettingList;
for (const auto& SettingItem : CookSettingJsonList)
{
FString Setting = SettingItem->AsString();
SelectedCookSettingList.Add(MakeShareable(new FString(Setting)));
GetCookerContextPtr()->AddSelectedSetting(Setting);
}
ExternSettingTextBox->SetText(UKismetTextLibrary::Conv_StringToText(InJsonObject->GetStringField("Options")));
}
FString SHotPatcherCookSetting::GetSerializeName()const
{
return TEXT("Settings");
}
void SHotPatcherCookSetting::Reset()
{
GetCookerContextPtr()->ClearAllSettings();
ExternSettingTextBox->SetText(UKismetTextLibrary::Conv_StringToText(TEXT("")));
}
TSharedRef<ITableRow> SHotPatcherCookSetting::HandleCookSettingListViewGenerateRow(TSharedPtr<FString> InItem, const TSharedRef<STableViewBase>& OwnerTable)
{
return SNew(SProjectCookSettingsListRow, mContext)
.SettingName(InItem)
.OwnerTableView(OwnerTable);
}
void SHotPatcherCookSetting::RefreshSettingsList()
{
SettingList.Reset();
TArray<FString> AllSettings = UFlibHotPatcherCoreHelper::GetAllCookOption();
for (int32 OptionIndex = 0; OptionIndex < AllSettings.Num(); ++OptionIndex)
{
FString& Option = AllSettings[OptionIndex];
SettingList.Add(MakeShareable(new FString(Option)));
}
SettingListView->RequestListRefresh();
}
void SHotPatcherCookSetting::HandleRequestExSettings(TArray<FString>& OutExSettings)
{
OutExSettings.Reset();
FString ExOptins = ExternSettingTextBox->GetText().ToString();
OutExSettings.Add(ExOptins);
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,60 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Interfaces/ITargetPlatformManagerModule.h"
#include "Interfaces/ITargetPlatform.h"
#include "Model/FOriginalCookerContext.h"
#include "Templates/SharedPointer.h"
#include "Widgets/Input/SEditableTextBox.h"
// project header
#include "IOriginalCookerChildWidget.h"
/**
* Implements the cooked platforms panel.
*/
class SHotPatcherCookSetting
: public SCompoundWidget,public IOriginalCookerChildWidget
{
public:
SLATE_BEGIN_ARGS(SHotPatcherCookSetting) { }
SLATE_END_ARGS()
public:
/**
* Constructs the widget.
*
* @param InArgs The Slate argument list.
*/
void Construct( const FArguments& InArgs,TSharedPtr<FHotPatcherContextBase> InContext);
public:
virtual TSharedPtr<FJsonObject> SerializeAsJson()const override;
virtual void DeSerializeFromJsonObj(TSharedPtr<FJsonObject>const & InJsonObject)override;
virtual FString GetSerializeName()const override;
virtual void Reset() override;
protected:
TSharedRef<ITableRow> HandleCookSettingListViewGenerateRow(TSharedPtr<FString> InItem, const TSharedRef<STableViewBase>& OwnerTable);
void RefreshSettingsList();
void HandleRequestExSettings(TArray<FString>& OutExSettings);
// FReply ConfirmExCookSetting();
TArray<FString> GetDefaultCookParams() const
{
return TArray<FString>{"-NoLogTimes", "-UTF8Output"};
}
private:
/** Holds the map list. */
TArray<TSharedPtr<FString> > SettingList;
/** Holds the map list view. */
TSharedPtr<SListView<TSharedPtr<FString> > > SettingListView;
// Extern Cook Setting Param
TSharedPtr<SEditableTextBox> ExternSettingTextBox;
};

View File

@ -0,0 +1,137 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "SHotPatcherCookSpecifyCookFilter.h"
#include "FlibAssetManageHelper.h"
#include "SProjectCookMapListRow.h"
#include "Widgets/Input/SHyperlink.h"
#include "Widgets/Layout/SSeparator.h"
#include "FlibPatchParserHelper.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Misc/EngineVersionComparison.h"
#if !UE_VERSION_OLDER_THAN(5,1,0)
typedef FAppStyle FEditorStyle;
#endif
#define LOCTEXT_NAMESPACE "SHotPatcherCookSpecifyCookFilter"
void SHotPatcherCookSpecifyCookFilter::Construct(const FArguments& InArgs, TSharedPtr<FHotPatcherContextBase> InContext)
{
SetContext(InContext);
GetCookerContextPtr()->OnRequestSpecifyCookFilter.BindRaw(this, &SHotPatcherCookSpecifyCookFilter::HandleRequestSpecifyCookFilter);
CreateFilterListView();
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(FEditorStyle::GetMargin("StandardDialog.ContentPadding"))
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.VAlign(VAlign_Center)
[
SettingsView->AsShared()
]
]
];
SpecifyCookFilterSetting = USpecifyCookFilterSetting::Get();
SettingsView->SetObject(SpecifyCookFilterSetting);
}
TSharedPtr<FJsonObject> SHotPatcherCookSpecifyCookFilter::SerializeAsJson() const
{
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject);
TArray<TSharedPtr<FJsonValue>> CookFiltersJsonValueList;
for (const auto& Filter : GetSpecifyCookFilterSetting()->GetAlwayCookFilters())
{
FString FilterPath = Filter.Path;
CookFiltersJsonValueList.Add(MakeShareable(new FJsonValueString(FilterPath)));
}
JsonObject->SetArrayField(TEXT("CookFilter"), CookFiltersJsonValueList);
return JsonObject;
}
void SHotPatcherCookSpecifyCookFilter::DeSerializeFromJsonObj(TSharedPtr<FJsonObject>const & InJsonObject)
{
TArray<TSharedPtr<FJsonValue>> CookFiltersJsonValueList = InJsonObject->GetArrayField(TEXT("CookFilter"));
for (const auto& FilterJsonValue : CookFiltersJsonValueList)
{
FDirectoryPath FilterDirPath;
FilterDirPath.Path = FilterJsonValue->AsString();
GetSpecifyCookFilterSetting()->GetAlwayCookFilters().Add(FilterDirPath);
}
}
FString SHotPatcherCookSpecifyCookFilter::GetSerializeName()const
{
return TEXT("Filters");
}
void SHotPatcherCookSpecifyCookFilter::Reset()
{
GetSpecifyCookFilterSetting()->GetAlwayCookFilters().Reset();
}
USpecifyCookFilterSetting* SHotPatcherCookSpecifyCookFilter::GetSpecifyCookFilterSetting() const
{
return SpecifyCookFilterSetting;
}
TArray<FDirectoryPath> SHotPatcherCookSpecifyCookFilter::GetAlwayCookDirectory() const
{
if (GetSpecifyCookFilterSetting())
{
return GetSpecifyCookFilterSetting()->GetAlwayCookFilters();
}
else
{
return TArray<FDirectoryPath>{};
}
}
TArray<FString> SHotPatcherCookSpecifyCookFilter::GetAlwayCookAbsDirectory()
{
TArray<FString> AlwayCookDirAbsPathList;
for (const auto& AlwayCookRelativeDir : GetAlwayCookDirectory())
{
FString AbsPath;
if (UFlibAssetManageHelper::ConvRelativeDirToAbsDir(AlwayCookRelativeDir.Path, AbsPath))
{
AlwayCookDirAbsPathList.AddUnique(AbsPath);
}
}
return AlwayCookDirAbsPathList;
}
void SHotPatcherCookSpecifyCookFilter::HandleRequestSpecifyCookFilter(TArray<FDirectoryPath>& OutCookDir)
{
OutCookDir = GetAlwayCookDirectory();
}
void SHotPatcherCookSpecifyCookFilter::CreateFilterListView()
{
// Create a property view
FPropertyEditorModule& EditModule = FModuleManager::Get().GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
FDetailsViewArgs DetailsViewArgs;
DetailsViewArgs.bUpdatesFromSelection = true;
DetailsViewArgs.bLockable = true;
DetailsViewArgs.NameAreaSettings = FDetailsViewArgs::ComponentsAndActorsUseNameArea;
DetailsViewArgs.bCustomNameAreaLocation = false;
DetailsViewArgs.bCustomFilterAreaLocation = true;
DetailsViewArgs.DefaultsOnlyVisibility = EEditDefaultsOnlyNodeVisibility::Hide;
SettingsView = EditModule.CreateDetailView(DetailsViewArgs);
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,56 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Model/FOriginalCookerContext.h"
#include "Templates/SharedPointer.h"
#include "FlibPatchParserHelper.h"
#include "SpecifyCookFilterSetting.h"
// engine header
#include "IDetailsView.h"
#include "Templates/SharedPointer.h"
#include "PropertyEditorModule.h"
#include "Kismet/KismetSystemLibrary.h"
// project header
#include "IOriginalCookerChildWidget.h"
/**
* Implements the cooked Maps panel.
*/
class SHotPatcherCookSpecifyCookFilter
: public SCompoundWidget,public IOriginalCookerChildWidget
{
public:
SLATE_BEGIN_ARGS(SHotPatcherCookSpecifyCookFilter) { }
SLATE_END_ARGS()
public:
/**
* Constructs the widget.
*
* @param InArgs The Slate argument list.
*/
void Construct( const FArguments& InArgs,TSharedPtr<FHotPatcherContextBase> InContext);
public:
virtual TSharedPtr<FJsonObject> SerializeAsJson()const override;
virtual void DeSerializeFromJsonObj(TSharedPtr<FJsonObject>const & InJsonObject)override;
virtual FString GetSerializeName()const override;
virtual void Reset() override;
public:
USpecifyCookFilterSetting* GetSpecifyCookFilterSetting()const;
TArray<FDirectoryPath> GetAlwayCookDirectory()const;
TArray<FString> GetAlwayCookAbsDirectory();
void HandleRequestSpecifyCookFilter(TArray<FDirectoryPath>& OutCookDir);
protected:
void CreateFilterListView();
private:
/** Settings view ui element ptr */
TSharedPtr<IDetailsView> SettingsView;
USpecifyCookFilterSetting* SpecifyCookFilterSetting;
};

View File

@ -0,0 +1,130 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
// #include "HotPatcherPrivatePCH.h"
#include "SHotPatcherCookedPlatforms.h"
#include "SHotPatcherPlatformListRow.h"
#include "Widgets/Input/SHyperlink.h"
#include "Widgets/Layout/SSeparator.h"
#define LOCTEXT_NAMESPACE "SHotPatcherCookedPlatforms"
void SHotPatcherCookedPlatforms::Construct(const FArguments& InArgs, TSharedPtr<FHotPatcherContextBase> InContext)
{
mContext = InContext;
MakePlatformMenu();
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.MaxHeight(256.0f)
[
// platform menu
SAssignNew(PlatformListView, SListView<TSharedPtr<FString> >)
.HeaderRow(
SNew(SHeaderRow)
.Visibility(EVisibility::Collapsed)
+ SHeaderRow::Column("PlatformName")
.DefaultLabel(LOCTEXT("PlatformListPlatformNameColumnHeader", "Platform"))
.FillWidth(1.0f)
)
.ItemHeight(16.0f)
.ListItemsSource(&PlatformList)
.OnGenerateRow(this, &SHotPatcherCookedPlatforms::HandlePlatformListViewGenerateRow)
.SelectionMode(ESelectionMode::None)
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0f, 6.0f, 0.0f, 4.0f)
[
SNew(SSeparator)
.Orientation(Orient_Horizontal)
]
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
.HAlign(HAlign_Right)
[
SNew(STextBlock)
.Text(LOCTEXT("SelectLabel", "Select:"))
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(8.0f, 0.0f)
[
// all platforms hyper-link
SNew(SHyperlink)
.OnNavigate(this, &SHotPatcherCookedPlatforms::HandleAllPlatformsHyperlinkNavigate, true)
.Text(LOCTEXT("AllPlatformsHyperlinkLabel", "All"))
.ToolTipText(LOCTEXT("AllPlatformsButtonTooltip", "Select all available platforms."))
]
+ SHorizontalBox::Slot()
.AutoWidth()
[
// no platforms hyper-link
SNew(SHyperlink)
.OnNavigate(this, &SHotPatcherCookedPlatforms::HandleAllPlatformsHyperlinkNavigate, false)
.Text(LOCTEXT("NoPlatformsHyperlinkLabel", "None"))
.ToolTipText(LOCTEXT("NoPlatformsHyperlinkTooltip", "Deselect all platforms."))
]
]
];
}
TSharedPtr<FJsonObject> SHotPatcherCookedPlatforms::SerializeAsJson() const
{
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject);
TArray<FString> SelectedPlatformList = GetCookerContextPtr()->GetAllSelectedPlatform();
TArray<TSharedPtr<FJsonValue>> PlatformJsonList;
for (const auto& Platform : SelectedPlatformList)
{
PlatformJsonList.Add(MakeShareable(new FJsonValueString(Platform)));
}
JsonObject->SetArrayField(TEXT("CookPlatforms"), PlatformJsonList);
return JsonObject;
}
void SHotPatcherCookedPlatforms::DeSerializeFromJsonObj(TSharedPtr<FJsonObject>const & InJsonObject)
{
TArray<TSharedPtr<FJsonValue>> PlatformJsonList = InJsonObject->GetArrayField(TEXT("CookPlatforms"));
TArray<TSharedPtr<FString>> SelectedPlatform;
for (const auto& PlatformJson : PlatformJsonList)
{
FString Platform = PlatformJson->AsString();
SelectedPlatform.Add(MakeShareable(new FString(Platform)));
GetCookerContextPtr()->AddSelectedCookPlatform(Platform);
}
}
FString SHotPatcherCookedPlatforms::GetSerializeName()const
{
return TEXT("Platforms");
}
void SHotPatcherCookedPlatforms::Reset()
{
GetCookerContextPtr()->ClearAllPlatform();
}
TSharedRef<ITableRow> SHotPatcherCookedPlatforms::HandlePlatformListViewGenerateRow(TSharedPtr<FString> InItem, const TSharedRef<STableViewBase>& OwnerTable)
{
return SNew(SHotPatcherPlatformListRow,mContext)
.PlatformName(InItem)
.OwnerTableView(OwnerTable);
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,96 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Interfaces/ITargetPlatformManagerModule.h"
#include "Interfaces/ITargetPlatform.h"
#include "Model/FOriginalCookerContext.h"
#include "SHotPatcherWidgetBase.h"
#include "Templates/SharedPointer.h"
#include "Dom/JsonObject.h"
// project header
#include "IOriginalCookerChildWidget.h"
/**
* Implements the cooked platforms panel.
*/
class SHotPatcherCookedPlatforms
: public SCompoundWidget,public IOriginalCookerChildWidget
{
public:
SLATE_BEGIN_ARGS(SHotPatcherCookedPlatforms) { }
SLATE_END_ARGS()
public:
/**
* Constructs the widget.
*
* @param InArgs The Slate argument list.
*/
void Construct( const FArguments& InArgs,TSharedPtr<FHotPatcherContextBase> InContext);
public:
virtual TSharedPtr<FJsonObject> SerializeAsJson()const override;
virtual void DeSerializeFromJsonObj(TSharedPtr<FJsonObject>const & InJsonObject)override;
virtual FString GetSerializeName()const override;
virtual void Reset() override;
protected:
/**
* Builds the platform menu.
*
* @return Platform menu widget.
*/
void MakePlatformMenu( )
{
TArray<ITargetPlatform*> Platforms = GetTargetPlatformManager()->GetTargetPlatforms();
if (Platforms.Num() > 0)
{
PlatformList.Reset();
for (int32 PlatformIndex = 0; PlatformIndex < Platforms.Num(); ++PlatformIndex)
{
FString PlatformName = Platforms[PlatformIndex]->PlatformName();
PlatformList.Add(MakeShareable(new FString(PlatformName)));
}
}
}
private:
// Callback for clicking the 'Select All Platforms' button.
void HandleAllPlatformsHyperlinkNavigate( bool AllPlatforms )
{
if (mContext.IsValid())
{
if (AllPlatforms)
{
TArray<ITargetPlatform*> Platforms = GetTargetPlatformManager()->GetTargetPlatforms();
for (int32 PlatformIndex = 0; PlatformIndex < Platforms.Num(); ++PlatformIndex)
{
GetCookerContextPtr()->AddSelectedCookPlatform(Platforms[PlatformIndex]->PlatformName());
}
}
else {
GetCookerContextPtr()->ClearAllPlatform();
}
}
}
// Handles generating a row widget in the map list view.
TSharedRef<ITableRow> HandlePlatformListViewGenerateRow( TSharedPtr<FString> InItem, const TSharedRef<STableViewBase>& OwnerTable );
private:
// Holds the platform list.
TArray<TSharedPtr<FString> > PlatformList;
// Holds the platform list view.
TSharedPtr<SListView<TSharedPtr<FString> > > PlatformListView;
};

View File

@ -0,0 +1,114 @@
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Misc/Attribute.h"
#include "Widgets/SNullWidget.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SWidget.h"
#include "Layout/Margin.h"
#include "Styling/SlateTypes.h"
#include "Widgets/Views/STableRow.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Views/SListView.h"
#include "Widgets/Input/SCheckBox.h"
#include "Model/FOriginalCookerContext.h"
#include "Templates/SharedPointer.h"
#define LOCTEXT_NAMESPACE "SHotPatcherPlatformListRow"
/**
* Implements a row widget for map list.
*/
class SHotPatcherPlatformListRow
: public SMultiColumnTableRow<TSharedPtr<FString> >,IOriginalCookerChildWidget
{
public:
SLATE_BEGIN_ARGS(SHotPatcherPlatformListRow) { }
SLATE_ATTRIBUTE(FString, HighlightString)
SLATE_ARGUMENT(TSharedPtr<STableViewBase>, OwnerTableView)
SLATE_ARGUMENT(TSharedPtr<FString>, PlatformName)
SLATE_END_ARGS()
public:
/**
* Constructs the widget.
*
* @param InArgs The construction arguments.
* @param InProfileManager The profile manager to use.
*/
void Construct( const FArguments& InArgs, TSharedPtr<FHotPatcherContextBase> InContext)
{
HighlightString = InArgs._HighlightString;
PlatformName = InArgs._PlatformName;
SetContext(InContext);
SMultiColumnTableRow<TSharedPtr<FString> >::Construct(FSuperRowType::FArguments(), InArgs._OwnerTableView.ToSharedRef());
}
public:
/**
* Generates the widget for the specified column.
*
* @param ColumnName The name of the column to generate the widget for.
* @return The widget.
*/
virtual TSharedRef<SWidget> GenerateWidgetForColumn( const FName& ColumnName ) override
{
if (ColumnName == "PlatformName")
{
return SNew(SCheckBox)
.IsChecked(this, &SHotPatcherPlatformListRow::HandleCheckBoxIsChecked)
.OnCheckStateChanged(this, &SHotPatcherPlatformListRow::HandleCheckBoxCheckStateChanged)
.Padding(FMargin(6.0, 2.0))
[
SNew(STextBlock)
.Text(FText::FromString(*PlatformName))
];
}
return SNullWidget::NullWidget;
}
private:
// Callback for changing the checked state of the check box.
void HandleCheckBoxCheckStateChanged( ECheckBoxState NewState )
{
if (NewState == ECheckBoxState::Checked)
{
GetCookerContextPtr()->AddSelectedCookPlatform(*PlatformName);
}
else {
GetCookerContextPtr()->RemoveSelectedCookPlatform(*PlatformName);
}
}
// Callback for determining the checked state of the check box.
ECheckBoxState HandleCheckBoxIsChecked( ) const
{
if (mContext.IsValid())
{
if (GetCookerContextPtr()->GetAllSelectedPlatform().Contains(*PlatformName))
{
return ECheckBoxState::Checked;
}
}
return ECheckBoxState::Unchecked;
}
private:
// Holds the highlight string for the log message.
TAttribute<FString> HighlightString;
// Holds the platform's name.
TSharedPtr<FString> PlatformName;
};
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,351 @@
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "SOriginalCookWidget.h"
#include "SHotPatcherCookedPlatforms.h"
#include "SHotPatcherCookSetting.h"
#include "SHotPatcherCookMaps.h"
#include "SOriginalCookWidget.h"
#include "FlibPatchParserHelper.h"
#include "ThreadUtils/FProcWorkerThread.hpp"
#include "FlibHotPatcherCoreHelper.h"
// Engine Header
#include "FlibHotPatcherEditorHelper.h"
#include "Serialization/JsonWriter.h"
#include "Serialization/JsonReader.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Layout/SExpandableArea.h"
#include "Widgets/Notifications/SNotificationList.h"
#include "Async/Async.h"
#define LOCTEXT_NAMESPACE "SProjectCookPage"
DEFINE_LOG_CATEGORY(LogCookPage);
/* SProjectCookPage interface
*****************************************************************************/
void SOriginalCookWidget::Construct(const FArguments& InArgs, TSharedPtr<FHotPatcherContextBase> InContext)
{
OriginalCookerContext = MakeShareable(new FOriginalCookerContext);
SetContext(OriginalCookerContext);
MissionNotifyProay = NewObject<UMissionNotificationProxy>();
MissionNotifyProay->AddToRoot();
MissionNotifyProay->SetMissionName(TEXT("Cook"));
MissionNotifyProay->SetMissionNotifyText(
LOCTEXT("CookNotificationInProgress", "Cook in progress"),
LOCTEXT("RunningCookNotificationCancelButton", "Cancel"),
LOCTEXT("CookSuccessedNotification", "Cook Finished!"),
LOCTEXT("CookFaildNotification", "Cook Faild!")
);
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0, 8.0, 0.0, 0.0)
[
SNew(SExpandableArea)
.AreaTitle(LOCTEXT("CookPlatforms", "Platforms"))
.InitiallyCollapsed(true)
.Padding(8.0)
.BodyContent()
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SAssignNew(Platforms, SHotPatcherCookedPlatforms, OriginalCookerContext)
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0, 8.0, 0.0, 0.0)
[
SNew(SExpandableArea)
.AreaTitle(LOCTEXT("CookMaps", "Map(s)"))
.InitiallyCollapsed(true)
.Padding(8.0)
.BodyContent()
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SAssignNew(CookMaps, SHotPatcherCookMaps, OriginalCookerContext)
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0, 8.0, 0.0, 0.0)
[
SNew(SExpandableArea)
.AreaTitle(LOCTEXT("CookFilter", "Filter(s)"))
.InitiallyCollapsed(true)
.Padding(8.0)
.BodyContent()
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SAssignNew(CookFilters, SHotPatcherCookSpecifyCookFilter, OriginalCookerContext)
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0, 8.0, 0.0, 0.0)
[
SNew(SExpandableArea)
.AreaTitle(LOCTEXT("CookSetting", "Settings"))
.InitiallyCollapsed(true)
.Padding(8.0)
.BodyContent()
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SAssignNew(CookSettings, SHotPatcherCookSetting, OriginalCookerContext)
]
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0, 8.0, 0.0, 0.0)
+ SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Right)
.Padding(4, 4, 10, 4)
[
SNew(SButton)
.Text(LOCTEXT("RunCook", "Cook Content"))
.OnClicked(this, &SOriginalCookWidget::RunCook)
.IsEnabled(this, &SOriginalCookWidget::CanExecuteCook)
]
];
}
#include "DesktopPlatformModule.h"
namespace FPlatformUtils
{
TArray<FString> OpenFileDialog()
{
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
TArray<FString> SelectedFiles;
if (DesktopPlatform)
{
const bool bOpened = DesktopPlatform->OpenFileDialog(
nullptr,
LOCTEXT("OpenCookConfigDialog", "Open .json").ToString(),
FString(TEXT("")),
TEXT(""),
TEXT("CookConfig json (*.json)|*.json"),
EFileDialogFlags::None,
SelectedFiles
);
}
return SelectedFiles;
}
TArray<FString> SaveFileDialog()
{
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
TArray<FString> SaveFilenames;
if (DesktopPlatform)
{
const bool bOpened = DesktopPlatform->SaveFileDialog(
nullptr,
LOCTEXT("SvedCookConfig", "Save .json").ToString(),
FString(TEXT("")),
TEXT(""),
TEXT("CookConfig json (*.json)|*.json"),
EFileDialogFlags::None,
SaveFilenames
);
}
return SaveFilenames;
}
TSharedPtr<FJsonObject> DeserializeAsJsonObject(const FString& InContent)
{
TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(InContent);
TSharedPtr<FJsonObject> JsonObject;
FJsonSerializer::Deserialize(JsonReader, JsonObject);
return JsonObject;
}
}
#include "Misc/FileHelper.h"
FReply SOriginalCookWidget::DoImportConfig()
{
ImportConfig();
return FReply::Handled();
}
FReply SOriginalCookWidget::DoExportConfig() const
{
ExportConfig();
return FReply::Handled();
}
FReply SOriginalCookWidget::DoResetConfig()
{
ResetConfig();
return FReply::Handled();
}
bool SOriginalCookWidget::CanExecuteCook()const
{
bool bCanCook = !!GetCookerContextPtr()->GetAllSelectedPlatform().Num() &&
(
!!GetCookerContextPtr()->GetAllSelectedCookMap().Num() ||
!!GetCookerContextPtr()->GetAlwayCookFilters().Num() ||
GetCookerContextPtr()->GetAllSelectedSettings().Contains(TEXT("CookAll"))
);
return !InCooking && bCanCook;
}
FReply SOriginalCookWidget::RunCook()const
{
FCookerConfig CookConfig = GetCookerContextPtr()->GetCookConfig();
if (FPaths::FileExists(CookConfig.EngineBin) && FPaths::FileExists(CookConfig.ProjectPath))
{
FString CookCommand;
UFlibPatchParserHelper::GetCookProcCommandParams(CookConfig, CookCommand);
UE_LOG(LogCookPage, Log, TEXT("The Cook Mission is Staring..."));
UE_LOG(LogCookPage, Log, TEXT("CookCommand:%s %s"),*CookConfig.EngineBin,*CookCommand);
RunCookProc(CookConfig.EngineBin, CookCommand);
}
return FReply::Handled();
}
void SOriginalCookWidget::CancelCookMission()
{
if (mCookProcWorkingThread.IsValid() && mCookProcWorkingThread->GetThreadStatus() == EThreadStatus::Busy)
{
mCookProcWorkingThread->Cancel();
}
UE_LOG(LogCookPage, Error, TEXT("The Cook Mission is canceled."));
}
void SOriginalCookWidget::RunCookProc(const FString& InBinPath, const FString& InCommand)const
{
if (mCookProcWorkingThread.IsValid() && mCookProcWorkingThread->GetThreadStatus()==EThreadStatus::Busy)
{
mCookProcWorkingThread->Cancel();
}
else
{
mCookProcWorkingThread = MakeShareable(new FProcWorkerThread(TEXT("CookThread"), InBinPath, InCommand));
mCookProcWorkingThread->ProcOutputMsgDelegate.BindUObject(MissionNotifyProay,&UMissionNotificationProxy::ReceiveOutputMsg);
mCookProcWorkingThread->ProcBeginDelegate.AddUObject(MissionNotifyProay,&UMissionNotificationProxy::SpawnRuningMissionNotification);
mCookProcWorkingThread->ProcSuccessedDelegate.AddUObject(MissionNotifyProay,&UMissionNotificationProxy::SpawnMissionSuccessedNotification);
mCookProcWorkingThread->ProcFaildDelegate.AddUObject(MissionNotifyProay,&UMissionNotificationProxy::SpawnMissionFaildNotification);
MissionNotifyProay->MissionCanceled.AddRaw(const_cast<SOriginalCookWidget*>(this),&SOriginalCookWidget::CancelCookMission);
mCookProcWorkingThread->Execute();
}
}
TSharedPtr<FJsonObject> SOriginalCookWidget::SerializeAsJson() const
{
FCookerConfig CookConfig = GetCookerContextPtr()->GetCookConfig();
TSharedPtr<FJsonObject> CookConfigJsonObj;
THotPatcherTemplateHelper::TSerializeStructAsJsonObject(CookConfig,CookConfigJsonObj);
return CookConfigJsonObj;
}
void SOriginalCookWidget::DeSerializeFromJsonObj(TSharedPtr<FJsonObject>const & InJsonObject)
{
for (const auto& SerializableItem : GetSerializableItems())
{
// TSharedPtr<FJsonObject> ItemJsonObject = InJsonObject->GetObjectField(SerializableItem->GetSerializeName());
SerializableItem->DeSerializeFromJsonObj(InJsonObject);
}
}
FString SOriginalCookWidget::GetSerializeName()const
{
return TEXT("CookPage");
}
FString SOriginalCookWidget::SerializeAsString()const
{
FString result;
auto JsonWriter = TJsonWriterFactory<>::Create(&result);
FJsonSerializer::Serialize(SerializeAsJson().ToSharedRef(), JsonWriter);
return result;
}
void SOriginalCookWidget::Reset()
{
for (const auto& SerializableItem : GetSerializableItems())
{
SerializableItem->Reset();
}
}
void SOriginalCookWidget::ImportConfig()
{
SHotPatcherCookerBase::ImportConfig();
TArray<FString> SelectedFiles = FPlatformUtils::OpenFileDialog();
if (!!SelectedFiles.Num())
{
FString LoadFile = SelectedFiles[0];
if (FPaths::FileExists(LoadFile))
{
FString ReadContent;
if (FFileHelper::LoadFileToString(ReadContent, *LoadFile))
{
DeSerializeFromJsonObj(FPlatformUtils::DeserializeAsJsonObject(ReadContent));
}
}
}
}
void SOriginalCookWidget::ExportConfig() const
{
SHotPatcherCookerBase::ExportConfig();
TArray<FString> SaveFiles = FPlatformUtils::SaveFileDialog();
if (!!SaveFiles.Num())
{
FString SaveToFile = SaveFiles[0].EndsWith(TEXT(".json")) ? SaveFiles[0] : SaveFiles[0].Append(TEXT(".json"));
if (FFileHelper::SaveStringToFile(SerializeAsString(), *SaveToFile))
{
FText Msg = LOCTEXT("SavedCookerConfig", "Successd to Export the Cooker Config.");
UFlibHotPatcherEditorHelper::CreateSaveFileNotify(Msg, SaveToFile);
}
}
}
void SOriginalCookWidget::ResetConfig()
{
for (const auto& SerializableItem : GetSerializableItems())
{
SerializableItem->Reset();
}
SHotPatcherCookerBase::ResetConfig();
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,87 @@
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Cooker/SHotPatcherCookerBase.h"
#include "CoreMinimal.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SCompoundWidget.h"
#include "Model/FOriginalCookerContext.h"
#include "ThreadUtils/FProcWorkerThread.hpp"
#include "IOriginalCookerChildWidget.h"
#include "MissionNotificationProxy.h"
#include "SHotPatcherCookedPlatforms.h"
#include "SHotPatcherCookMaps.h"
#include "SHotPatcherCookSetting.h"
#include "SHotPatcherCookSpecifyCookFilter.h"
DECLARE_LOG_CATEGORY_EXTERN(LogCookPage, Log, All);
/**
* Implements the profile page for the session launcher wizard.
*/
class SOriginalCookWidget
: public SHotPatcherCookerBase,public IOriginalCookerChildWidget
{
public:
SLATE_BEGIN_ARGS(SOriginalCookWidget) { }
SLATE_END_ARGS()
public:
/**
* Constructs the widget.
*
* @param InArgs The Slate argument list.
*/
void Construct( const FArguments& InArgs,TSharedPtr<FHotPatcherContextBase> InContext);
public:
virtual TSharedPtr<FJsonObject> SerializeAsJson()const override;
virtual void DeSerializeFromJsonObj(TSharedPtr<FJsonObject>const & InJsonObject)override;
virtual FString GetSerializeName()const override;
virtual void Reset() override;
virtual void ImportConfig() override;
virtual void ExportConfig()const override;
virtual void ResetConfig() override;
protected:
FReply DoImportConfig();
FReply DoExportConfig()const;
FReply DoResetConfig();
bool CanExecuteCook()const;
void RunCookProc(const FString& InBinPath, const FString& InCommand)const;
FReply RunCook()const;
protected:
TArray<FString> GetDefaultCookParams()const;
void CancelCookMission();
TArray<IOriginalCookerChildWidget*> GetSerializableItems()const
{
TArray<IOriginalCookerChildWidget*> List;
List.Add(Platforms.Get());
List.Add(CookMaps.Get());
List.Add(CookFilters.Get());
List.Add(CookSettings.Get());
return List;
}
FString SerializeAsString()const;
private:
UMissionNotificationProxy* MissionNotifyProay = nullptr;
bool InCooking=false;
/** The pending progress message */
TWeakPtr<SNotificationItem> PendingProgressPtr;
mutable TSharedPtr<FProcWorkerThread> mCookProcWorkingThread;
TSharedPtr<SHotPatcherCookedPlatforms> Platforms;
TSharedPtr<SHotPatcherCookMaps> CookMaps;
TSharedPtr<SHotPatcherCookSpecifyCookFilter> CookFilters;
TSharedPtr<SHotPatcherCookSetting> CookSettings;
TSharedPtr<FOriginalCookerContext> OriginalCookerContext;
};

View File

@ -0,0 +1,119 @@
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Misc/Attribute.h"
#include "Widgets/SNullWidget.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SWidget.h"
#include "Layout/Margin.h"
#include "Styling/SlateTypes.h"
#include "Widgets/Views/STableRow.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SCheckBox.h"
#include "Widgets/Views/SListView.h"
#include "Model/FOriginalCookerContext.h"
#define LOCTEXT_NAMESPACE "SProjectCookMapListRow"
/**
* Implements a row widget for map list.
*/
class SProjectCookMapListRow
: public SMultiColumnTableRow<TSharedPtr<FString> >,IOriginalCookerChildWidget
{
public:
SLATE_BEGIN_ARGS(SProjectCookMapListRow) { }
SLATE_ATTRIBUTE(FString, HighlightString)
SLATE_ARGUMENT(TSharedPtr<STableViewBase>, OwnerTableView)
SLATE_ARGUMENT(TSharedPtr<FString>, MapName)
SLATE_END_ARGS()
public:
/**
* Constructs the widget.
*
* @param InArgs The construction arguments.
* @param InProfileManager The profile manager to use.
*/
void Construct( const FArguments& InArgs, const TSharedPtr<FHotPatcherContextBase>& InModel )
{
HighlightString = InArgs._HighlightString;
MapName = InArgs._MapName;
SetContext(InModel);
SMultiColumnTableRow<TSharedPtr<FString> >::Construct(FSuperRowType::FArguments(), InArgs._OwnerTableView.ToSharedRef());
}
public:
/**
* Generates the widget for the specified column.
*
* @param ColumnName The name of the column to generate the widget for.
* @return The widget.
*/
virtual TSharedRef<SWidget> GenerateWidgetForColumn( const FName& ColumnName ) override
{
if (ColumnName == "MapName")
{
return SNew(SCheckBox)
.IsChecked(this, &SProjectCookMapListRow::HandleCheckBoxIsChecked)
.OnCheckStateChanged(this, &SProjectCookMapListRow::HandleCheckBoxCheckStateChanged)
.Padding(FMargin(6.0, 2.0))
[
SNew(STextBlock)
.Text(FText::FromString(*MapName))
];
}
return SNullWidget::NullWidget;
}
private:
// Callback for changing the checked state of the check box.
void HandleCheckBoxCheckStateChanged( ECheckBoxState NewState )
{
if (mContext.IsValid())
{
if (NewState == ECheckBoxState::Checked)
{
GetCookerContextPtr()->AddSelectedCookMap(*MapName);
}
else
{
GetCookerContextPtr()->RemoveSelectedCookMap(*MapName);
}
}
}
// Callback for determining the checked state of the check box.
ECheckBoxState HandleCheckBoxIsChecked( ) const
{
if (mContext.IsValid() && GetCookerContextPtr()->GetAllSelectedCookMap().Contains(*MapName))
{
return ECheckBoxState::Checked;
}
return ECheckBoxState::Unchecked;
}
private:
// Holds the highlight string for the log message.
TAttribute<FString> HighlightString;
// Holds the map's name.
TSharedPtr<FString> MapName;
};
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,118 @@
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Misc/Attribute.h"
#include "Widgets/SNullWidget.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SWidget.h"
#include "Layout/Margin.h"
#include "Styling/SlateTypes.h"
#include "Widgets/Views/STableRow.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SCheckBox.h"
#include "Widgets/Views/SListView.h"
#include "Model/FOriginalCookerContext.h"
#define LOCTEXT_NAMESPACE "SProjectCookSettingsListRow"
/**
* Implements a row widget for map list.
*/
class SProjectCookSettingsListRow
: public SMultiColumnTableRow<TSharedPtr<FString> >,public IOriginalCookerChildWidget
{
public:
SLATE_BEGIN_ARGS(SProjectCookSettingsListRow) { }
SLATE_ATTRIBUTE(FString, HighlightString)
SLATE_ARGUMENT(TSharedPtr<STableViewBase>, OwnerTableView)
SLATE_ARGUMENT(TSharedPtr<FString>, SettingName)
SLATE_END_ARGS()
public:
/**
* Constructs the widget.
*
* @param InArgs The construction arguments.
* @param InProfileManager The profile manager to use.
*/
void Construct( const FArguments& InArgs, const TSharedPtr<FHotPatcherContextBase>& InModel )
{
HighlightString = InArgs._HighlightString;
SettingName = InArgs._SettingName;
SetContext(InModel);
SMultiColumnTableRow<TSharedPtr<FString> >::Construct(FSuperRowType::FArguments(), InArgs._OwnerTableView.ToSharedRef());
}
public:
/**
* Generates the widget for the specified column.
*
* @param ColumnName The name of the column to generate the widget for.
* @return The widget.
*/
virtual TSharedRef<SWidget> GenerateWidgetForColumn( const FName& ColumnName ) override
{
if (ColumnName == "SettingName")
{
return SNew(SCheckBox)
.IsChecked(this, &SProjectCookSettingsListRow::HandleCheckBoxIsChecked)
.OnCheckStateChanged(this, &SProjectCookSettingsListRow::HandleCheckBoxCheckStateChanged)
.Padding(FMargin(6.0, 2.0))
[
SNew(STextBlock)
.Text(FText::FromString(*SettingName))
];
}
return SNullWidget::NullWidget;
}
private:
// Callback for changing the checked state of the check box.
void HandleCheckBoxCheckStateChanged( ECheckBoxState NewState )
{
if (mContext.IsValid())
{
if (NewState == ECheckBoxState::Checked)
{
GetCookerContextPtr()->AddSelectedSetting(*SettingName);
}
else
{
GetCookerContextPtr()->RemoveSelectedSetting(*SettingName);
}
}
}
// Callback for determining the checked state of the check box.
ECheckBoxState HandleCheckBoxIsChecked( ) const
{
if (mContext.IsValid() && GetCookerContextPtr()->GetAllSelectedSettings().Contains(*SettingName))
{
return ECheckBoxState::Checked;
}
return ECheckBoxState::Unchecked;
}
private:
// Holds the highlight string for the log message.
TAttribute<FString> HighlightString;
// Holds the map's name.
TSharedPtr<FString> SettingName;
};
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,46 @@
#pragma once
// engine header
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "UObject/Object.h"
#include "Engine/EngineTypes.h"
#include "Dom/JsonObject.h"
#include "Serialization/JsonWriter.h"
#include "Serialization/JsonSerializer.h"
#include "SpecifyCookFilterSetting.generated.h"
/** Singleton wrapper to allow for using the setting structure in SSettingsView */
UCLASS()
class USpecifyCookFilterSetting : public UObject
{
GENERATED_BODY()
public:
USpecifyCookFilterSetting() {}
FORCEINLINE static USpecifyCookFilterSetting* Get()
{
static bool bInitialized = false;
// This is a singleton, use default object
USpecifyCookFilterSetting* DefaultSettings = GetMutableDefault<USpecifyCookFilterSetting>();
if (!bInitialized)
{
bInitialized = true;
}
return DefaultSettings;
}
FORCEINLINE TArray<FDirectoryPath>& GetAlwayCookFilters(){return AlwayCookFilters;}
FORCEINLINE TArray<FSoftObjectPath>& GetCookAssets(){return CookAssets;}
protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite,Category = "Directory",meta = (RelativeToGameContentDir, LongPackageName))
TArray<FDirectoryPath> AlwayCookFilters;
// UPROPERTY(EditAnywhere, BlueprintReadWrite,Category = "Assets")
TArray<FSoftObjectPath> CookAssets;
//UPROPERTY(EditAnywhere, BlueprintReadWrite,Category = "CookDirectoryFilter",meta = (RelativeToGameContentDir, LongPackageName))
// TArray<FDirectoryPath> NeverCookFilters;
};

View File

@ -0,0 +1,174 @@
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "SCookersPage.h"
#include "Cooker/OriginalCooker/SOriginalCookWidget.h"
// #include "Cooker/MultiCooker/SHotPatcherMultiCookerPage.h"
// engine header
#include "Framework/Commands/UIAction.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Layout/SExpandableArea.h"
#include "HotPatcherEditor.h"
#define LOCTEXT_NAMESPACE "SProjectCookerPage"
/* SProjectCookerPage interface
*****************************************************************************/
void SCookersPage::Construct(const FArguments& InArgs, TSharedPtr<FHotPatcherContextBase> InContext)
{
SetContext(InContext);
FHotPatcherEditorModule& EditorModule = FHotPatcherEditorModule::Get();
// create cook modes menu
FMenuBuilder PatchModeMenuBuilder(true, NULL);
{
TMap<FString,FHotPatcherAction>* Cookers = FHotPatcherActionManager::Get().GetHotPatcherActions().Find(GetPageName());
if(Cookers)
{
for(const auto& Cooker:*Cookers)
{
if(FHotPatcherActionManager::Get().IsSupportEditorAction(Cooker.Key))
{
FUIAction Action = FExecuteAction::CreateSP(this, &SCookersPage::HandleHotPatcherMenuEntryClicked, Cooker.Key,Cooker.Value.ActionCallback);
PatchModeMenuBuilder.AddMenuEntry(Cooker.Value.ActionName, Cooker.Value.ActionToolTip, Cooker.Value.Icon, Action);
}
}
}
}
auto Widget = SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("WhichProjectToUseText", "How would you like to Cook the content?"))
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(8.0, 0.0, 0.0, 0.0)
[
SNew(SButton)
.Text(LOCTEXT("ImportProjectConfig", "Import Project Config"))
.OnClicked(this,&SHotPatcherPageBase::DoImportProjectConfig)
.Visibility(this,&SCookersPage::HandleImportProjectConfigVisibility)
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(8.0, 0.0, 0.0, 0.0)
[
SNew(SButton)
.Text(LOCTEXT("ImportConfig", "Import"))
.OnClicked(this,&SHotPatcherPageBase::DoImportConfig)
.Visibility(this,&SCookersPage::HandleOperatorConfigVisibility)
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(5.0, 0.0, 0.0, 0.0)
[
SNew(SButton)
.Text(LOCTEXT("ExportConfig", "Export"))
.OnClicked(this,&SHotPatcherPageBase::DoExportConfig)
.Visibility(this, &SCookersPage::HandleOperatorConfigVisibility)
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(5.0, 0.0, 0.0, 0.0)
[
SNew(SButton)
.Text(LOCTEXT("ResetConfig", "Reset"))
.OnClicked(this, &SHotPatcherPageBase::DoResetConfig)
.Visibility(this, &SCookersPage::HandleOperatorConfigVisibility)
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(5.0, 0.0, 0.0, 0.0)
[
// cooking mode menu
SNew(SComboButton)
.ButtonContent()
[
SNew(STextBlock)
.Text(this, &SCookersPage::HandleCookerModeComboButtonContentText)
]
.ContentPadding(FMargin(6.0, 2.0))
.MenuContent()
[
PatchModeMenuBuilder.MakeWidget()
]
]
];
TMap<FString,FHotPatcherAction>* Cookers = FHotPatcherActionManager::Get().GetHotPatcherActions().Find(GetPageName());
if(Cookers)
{
for(const auto& Cooker:*Cookers)
{
if(Cooker.Value.RequestWidget)
{
TSharedRef<SHotPatcherWidgetInterface> Action = Cooker.Value.RequestWidget(GetContext());
Widget->AddSlot().AutoHeight().Padding(0.0, 8.0, 0.0, 0.0)
[
Action
];
ActionWidgetMap.Add(*Cooker.Key,Action);
}
}
}
ChildSlot
[
Widget
];
if(FHotPatcherAction* DefaultAction = FHotPatcherActionManager::Get().GetTopActionByCategory(GetPageName()))
{
HandleHotPatcherMenuEntryClicked(UKismetTextLibrary::Conv_TextToString(DefaultAction->ActionName.Get()),nullptr);
}
}
EVisibility SCookersPage::HandleOperatorConfigVisibility()const
{
return EVisibility::Visible;
}
EVisibility SCookersPage::HandleImportProjectConfigVisibility() const
{
EVisibility rVisibility = EVisibility::Hidden;
if(GetContext()->GetModeName().IsEqual(TEXT("ByOriginal")))
{
rVisibility = EVisibility::Visible;
}
return rVisibility;
}
void SCookersPage::HandleHotPatcherMenuEntryClicked(FString InModeName,TFunction<void(void)> ActionCallback)
{
if(ActionCallback)
{
ActionCallback();
}
if (GetContext().IsValid())
{
GetContext()->SetModeByName(FName(*InModeName));
}
}
FText SCookersPage::HandleCookerModeComboButtonContentText() const
{
if (GetContext().IsValid())
{
return FText::FromString(GetContext()->GetModeName().ToString());
}
return FText();
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,47 @@
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SCompoundWidget.h"
#include "Model/FPatchersModeContext.h"
#include "Cooker/SHotPatcherCookerBase.h"
#include "SHotPatcherPageBase.h"
// engine header
#include "Interfaces/ITargetPlatform.h"
#include "Templates/SharedPointer.h"
#include "IDetailsView.h"
#include "PropertyEditorModule.h"
#include "Model/FOriginalCookerContext.h"
/**
* Implements the profile page for the session launcher wizard.
*/
class SCookersPage
: public SHotPatcherPageBase
{
public:
SLATE_BEGIN_ARGS(SCookersPage) { }
SLATE_END_ARGS()
public:
/**
* Constructs the widget.
*
* @param InArgs The Slate argument list.
*/
void Construct( const FArguments& InArgs,TSharedPtr<FHotPatcherContextBase> InContext);
virtual FString GetPageName() const override{ return TEXT("Cooker"); }
public:
FText HandleCookerModeComboButtonContentText() const;
void HandleHotPatcherMenuEntryClicked(FString InModeName,TFunction<void(void)> ActionCallback);
EVisibility HandleOperatorConfigVisibility()const;
EVisibility HandleImportProjectConfigVisibility()const;
//
// private:
// TSharedPtr<FOriginalCookerContext> OriginalCookerContext;
};

View File

@ -0,0 +1,162 @@
#include "AssetTypeActions_PrimaryAssetLabel.h"
#include "Engine/PrimaryAssetLabel.h"
#include "HotPatcherEditor.h"
#include "FlibAssetManageHelper.h"
#include "Misc/EngineVersionComparison.h"
#if !UE_VERSION_OLDER_THAN(5,1,0)
typedef FAppStyle FEditorStyle;
#endif
#if WITH_EDITOR_SECTION
#include "ToolMenuSection.h"
void FAssetTypeActions_PrimaryAssetLabel::GetActions(const TArray<UObject*>& InObjects,
FToolMenuSection& Section)
{
#if !UE_VERSION_OLDER_THAN(5,1,0)
FName StyleSetName = FEditorStyle::GetAppStyleSetName();
#else
FName StyleSetName = FEditorStyle::GetStyleSetName();
#endif
auto Labels = GetTypedWeakObjectPtrs<UPrimaryAssetLabel>(InObjects);
const FSlateIcon Icon = FSlateIcon(StyleSetName, "ContentBrowser.AssetActions.Duplicate");
Section.AddMenuEntry(
"ObjectContext_AddToPatchIncludeFilters",
NSLOCTEXT("AssetTypeActions_PrimaryAssetLabel", "ObjectContext_AddToPatchIncludeFilters", "Add To Patch Include Filters"),
NSLOCTEXT("AssetTypeActions_PrimaryAssetLabel", "ObjectContext_AddToPatchIncludeFiltersTooltip", "Add the label to HotPatcher Include Filters."),
Icon,
FUIAction(
FExecuteAction::CreateSP(this, &FAssetTypeActions_PrimaryAssetLabel::ExecuteAddToPatchIncludeFilter, Labels)
));
Section.AddMenuEntry(
"ObjectContext_AddToChunkConfig",
NSLOCTEXT("AssetTypeActions_PrimaryAssetLabel", "ObjectContext_AddToChunkConfig", "Add To Chunk Config"),
NSLOCTEXT("AssetTypeActions_PrimaryAssetLabel", "ObjectContext_AddToChunkConfigTooltip", "Add Label To Chunk Config"),
Icon,
FUIAction(
FExecuteAction::CreateSP(this, &FAssetTypeActions_PrimaryAssetLabel::ExecuteAddToChunkConfig, Labels)
));
Section.AddSubMenu(
"ObjectContext_CookAndPakActionsSubMenu",
NSLOCTEXT("AssetTypeActions_PrimaryAssetLabel", "ObjectContext_CookAndPakActionsSubMenu","Cook And Pak Label Actions"),
NSLOCTEXT("AssetTypeActions_PrimaryAssetLabel", "ObjectContext_CookAndPakActionsSubMenu","Cook And Pak Label Actions"),
FNewToolMenuDelegate::CreateRaw(this, &FAssetTypeActions_PrimaryAssetLabel::MakeCookAndPakActionsSubMenu,Labels),
FUIAction(
FExecuteAction()
),
EUserInterfaceActionType::Button,
false,
FSlateIcon(StyleSetName, "ContentBrowser.SaveAllCurrentFolder")
);
}
TArray<FPatcherSpecifyAsset> GetLabelsAssets(TArray<TWeakObjectPtr<UPrimaryAssetLabel>> Objects)
{
TArray<FPatcherSpecifyAsset> LabelAsstes;
for(auto& Label:Objects)
{
if( Label->Rules.CookRule == EPrimaryAssetCookRule::NeverCook )
continue;
for(const auto& Asset:Label->ExplicitAssets)
{
FPatcherSpecifyAsset CurrentAsset;
CurrentAsset.Asset = Asset.ToSoftObjectPath();
CurrentAsset.bAnalysisAssetDependencies = Label->bLabelAssetsInMyDirectory;
CurrentAsset.AssetRegistryDependencyTypes.AddUnique(EAssetRegistryDependencyTypeEx::Packages);
LabelAsstes.AddUnique(CurrentAsset);
}
}
return LabelAsstes;
}
TArray<FDirectoryPath> GetLabelsDirs(TArray<TWeakObjectPtr<UPrimaryAssetLabel>> Objects)
{
TArray<FDirectoryPath> Dirs;
for(auto& Label:Objects)
{
if(Label->bLabelAssetsInMyDirectory && (Label->Rules.CookRule != EPrimaryAssetCookRule::NeverCook))
{
FString PathName = Label->GetPathName();
TArray<FString> DirNames;
PathName.ParseIntoArray(DirNames,TEXT("/"));
FString FinalPath;
for(size_t index = 0;index < DirNames.Num() - 1;++index)
{
FinalPath += TEXT("/") + DirNames[index];
}
FDirectoryPath CurrentDir;
CurrentDir.Path = FinalPath;
Dirs.Add(CurrentDir);
}
}
return Dirs;
}
void FAssetTypeActions_PrimaryAssetLabel::ExecuteAddToPatchIncludeFilter(
TArray<TWeakObjectPtr<UPrimaryAssetLabel>> Objects)
{
FHotPatcherEditorModule::Get().OpenDockTab();
if(GPatchSettings)
{
GPatchSettings->GetAssetScanConfigRef().IncludeSpecifyAssets.Append(GetLabelsAssets(Objects));
GPatchSettings->GetAssetScanConfigRef().AssetIncludeFilters.Append(GetLabelsDirs(Objects));
}
}
TArray<FChunkInfo> GetChunksByAssetLabels(TArray<TWeakObjectPtr<UPrimaryAssetLabel>> Objects)
{
TArray<FChunkInfo> Chunks;
for(const auto& Object:Objects)
{
FChunkInfo Chunk;
Chunk.AssetIgnoreFilters = GetLabelsDirs(TArray<TWeakObjectPtr<UPrimaryAssetLabel>>{Object});
Chunk.IncludeSpecifyAssets = GetLabelsAssets(TArray<TWeakObjectPtr<UPrimaryAssetLabel>>{Object});
Chunk.bAnalysisFilterDependencies = Object->Rules.bApplyRecursively;
FString LongPackageName = UFlibAssetManageHelper::PackagePathToLongPackageName(Object->GetPathName());
{
TArray<FString> DirNames;
LongPackageName.ParseIntoArray(DirNames,TEXT("/"));
Chunk.ChunkName = DirNames[DirNames.Num()-1];
}
}
return Chunks;
}
void FAssetTypeActions_PrimaryAssetLabel::ExecuteAddToChunkConfig(TArray<TWeakObjectPtr<UPrimaryAssetLabel>> Objects)
{
FHotPatcherEditorModule::Get().OpenDockTab();
if(GPatchSettings)
{
GPatchSettings->bEnableChunk = true;
GPatchSettings->ChunkInfos.Append(GetChunksByAssetLabels(Objects));
}
}
void FAssetTypeActions_PrimaryAssetLabel::MakeCookAndPakActionsSubMenu(UToolMenu* Menu,TArray<TWeakObjectPtr<UPrimaryAssetLabel>> Objects)
{
FToolMenuSection& Section = Menu->AddSection("CookAndPakActionsSection");
UHotPatcherSettings* Settings = GetMutableDefault<UHotPatcherSettings>();
Settings->ReloadConfig();
for (auto Platform : FHotPatcherEditorModule::Get().GetAllowCookPlatforms())
{
if(Settings->bWhiteListCookInEditor && !Settings->PlatformWhitelists.Contains(Platform))
continue;
Section.AddMenuEntry(
FName(*THotPatcherTemplateHelper::GetEnumNameByValue(Platform)),
FText::Format(NSLOCTEXT("Platform","Platform", "{0}"), UKismetTextLibrary::Conv_StringToText(THotPatcherTemplateHelper::GetEnumNameByValue(Platform))),
FText(),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateRaw(this, &FAssetTypeActions_PrimaryAssetLabel::OnCookAndPakPlatform, Platform,Objects)
)
);
}
}
void FAssetTypeActions_PrimaryAssetLabel::OnCookAndPakPlatform(ETargetPlatform Platform,TArray<TWeakObjectPtr<UPrimaryAssetLabel>> Objects)
{
FHotPatcherEditorModule::Get().CookAndPakByAssetsAndFilters(GetLabelsAssets(Objects),GetLabelsDirs(Objects),TArray<ETargetPlatform>{Platform},true);
}
#endif

View File

@ -0,0 +1,37 @@
#pragma once
#if WITH_EDITOR_SECTION
#include "ETargetPlatform.h"
#include "CoreMinimal.h"
#include "Engine/StaticMesh.h"
#include "Toolkits/IToolkitHost.h"
#include "AssetTypeActions_Base.h"
#include "Engine/PrimaryAssetLabel.h"
struct FToolMenuSection;
class FMenuBuilder;
class FAssetTypeActions_PrimaryAssetLabel : public FAssetTypeActions_Base
{
public:
// IAssetTypeActions Implementation
virtual FText GetName() const override { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_PrimaryAssetLabel", "Primary Asset Label"); }
virtual FColor GetTypeColor() const override { return FColor(0, 255, 255); }
virtual UClass* GetSupportedClass() const override { return UPrimaryAssetLabel::StaticClass(); }
virtual bool HasActions( const TArray<UObject*>& InObjects ) const override { return true; }
virtual void GetActions(const TArray<UObject*>& InObjects, FToolMenuSection& Section) override;
// virtual void OpenAssetEditor( const TArray<UObject*>& InObjects, TSharedPtr<class IToolkitHost> EditWithinLevelEditor = TSharedPtr<IToolkitHost>() ) override;
virtual uint32 GetCategories() override { return EAssetTypeCategories::Basic; }
// virtual class UThumbnailInfo* GetThumbnailInfo(UObject* Asset) const override;
virtual bool IsImportedAsset() const override { return true; }
// virtual void GetResolvedSourceFilePaths(const TArray<UObject*>& TypeAssets, TArray<FString>& OutSourceFilePaths) const override;
// End IAssetTypeActions
private:
void ExecuteAddToPatchIncludeFilter(TArray<TWeakObjectPtr<UPrimaryAssetLabel>> Objects);
void ExecuteAddToChunkConfig(TArray<TWeakObjectPtr<UPrimaryAssetLabel>> Objects);
void MakeCookAndPakActionsSubMenu(class UToolMenu* Menu,TArray<TWeakObjectPtr<UPrimaryAssetLabel>> Objects);
void OnCookAndPakPlatform(ETargetPlatform Platform,TArray<TWeakObjectPtr<UPrimaryAssetLabel>> Objects);
};
#endif

View File

@ -0,0 +1,74 @@
#include "CreatePatch/ReleaseSettingsDetails.h"
#include "CreatePatch/FExportReleaseSettings.h"
// engine header
#include "DetailLayoutBuilder.h"
#include "DetailCategoryBuilder.h"
#include "DetailWidgetRow.h"
#include "Widgets/Input/SButton.h"
#define LOCTEXT_NAMESPACE "ReleaseSettingsDetails"
TSharedRef<IDetailCustomization> FReleaseSettingsDetails::MakeInstance()
{
return MakeShareable(new FReleaseSettingsDetails());
}
void FReleaseSettingsDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
TArray< TSharedPtr<FStructOnScope> > StructBeingCustomized;
DetailBuilder.GetStructsBeingCustomized(StructBeingCustomized);
check(StructBeingCustomized.Num() == 1);
FExportReleaseSettings* ReleaseSettingsIns = (FExportReleaseSettings*)StructBeingCustomized[0].Get()->GetStructMemory();
IDetailCategoryBuilder& VersionCategory = DetailBuilder.EditCategory("Version",FText::GetEmpty(),ECategoryPriority::Default);
VersionCategory.SetShowAdvanced(true);
VersionCategory.AddCustomRow(LOCTEXT("ImportPakLists", "Import Pak Lists"),true)
.ValueContent()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.Padding(0)
.AutoWidth()
[
SNew(SButton)
.Text(LOCTEXT("Import", "Import"))
.ToolTipText(LOCTEXT("ImportPakLists_Tooltip", "Import Pak Lists"))
.IsEnabled_Lambda([this,ReleaseSettingsIns]()->bool
{
return ReleaseSettingsIns->IsByPakList();
})
.OnClicked_Lambda([this, ReleaseSettingsIns]()
{
if (ReleaseSettingsIns)
{
ReleaseSettingsIns->ImportPakLists();
}
return(FReply::Handled());
})
]
+ SHorizontalBox::Slot()
.Padding(5,0,0,0)
.AutoWidth()
[
SNew(SButton)
.Text(LOCTEXT("Clear", "Clear"))
.ToolTipText(LOCTEXT("ClearPakLists_Tooltip", "Clear Pak Lists"))
.IsEnabled_Lambda([this,ReleaseSettingsIns]()->bool
{
return ReleaseSettingsIns->IsByPakList();
})
.OnClicked_Lambda([this, ReleaseSettingsIns]()
{
if (ReleaseSettingsIns)
{
ReleaseSettingsIns->ClearImportedPakList();
}
return(FReply::Handled());
})
]
];
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,56 @@
#include "CreatePatch/SHotPatcherInformations.h"
// engine header
#include "Widgets/Input/SHyperlink.h"
#include "Widgets/Layout/SSeparator.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Layout/SScrollBox.h"
#include "Widgets/Layout/SGridPanel.h"
#include "Widgets/Layout/SHeader.h"
#include "Internationalization/Internationalization.h"
#include "Widgets/Layout/SExpandableArea.h"
void SHotPatcherInformations::Construct(const FArguments& InArgs)
{
ChildSlot
[
SAssignNew(DiffAreaWidget, SExpandableArea)
.AreaTitle(FText::FromString(TEXT("Informations")))
.InitiallyCollapsed(false)
.Padding(8.0)
.BodyContent()
[
SNew(SOverlay)
+ SOverlay::Slot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.VAlign(VAlign_Fill)
.FillWidth(1.0)
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.HAlign(HAlign_Left)
.Padding(4, 4, 10, 4)
[
SAssignNew(MulitText, SMultiLineEditableText)
.IsReadOnly(true)
]
]
]
]
];
}
void SHotPatcherInformations::SetExpanded(bool InExpand)
{
DiffAreaWidget->SetExpanded_Animated(InExpand);
}
void SHotPatcherInformations::SetContent(const FString& InContent)
{
MulitText->SetText(FText::FromString(InContent));
}

View File

@ -0,0 +1,681 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
// #include "HotPatcherPrivatePCH.h"
#include "CreatePatch/SHotPatcherPatchWidget.h"
#include "CreatePatch/FExportPatchSettings.h"
#include "CreatePatch/PatcherProxy.h"
#include "CreatePatch/ScopedSlowTaskContext.h"
#include "FlibHotPatcherCoreHelper.h"
#include "FlibPatchParserHelper.h"
#include "FHotPatcherVersion.h"
#include "FlibAssetManageHelper.h"
#include "FPakFileInfo.h"
#include "ThreadUtils/FThreadUtils.hpp"
#include "HotPatcherLog.h"
#include "HotPatcherEditor.h"
// engine header
#include "FlibHotPatcherEditorHelper.h"
#include "Misc/FileHelper.h"
#include "Widgets/Input/SHyperlink.h"
#include "Widgets/Layout/SSeparator.h"
#include "Widgets/Text/SMultiLineEditableText.h"
#include "Kismet/KismetStringLibrary.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Misc/SecureHash.h"
#include "HAL/FileManager.h"
#include "PakFileUtilities.h"
#include "Kismet/KismetTextLibrary.h"
#include "Misc/EngineVersionComparison.h"
#if !UE_VERSION_OLDER_THAN(5,1,0)
typedef FAppStyle FEditorStyle;
#endif
#define LOCTEXT_NAMESPACE "SHotPatcherCreatePatch"
void SHotPatcherPatchWidget::Construct(const FArguments& InArgs, TSharedPtr<FHotPatcherContextBase> InCreatePatchModel)
{
ExportPatchSetting = MakeShareable(new FExportPatchSettings);
GPatchSettings = ExportPatchSetting.Get();
CreateExportFilterListView();
mContext = InCreatePatchModel;
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(FEditorStyle::GetMargin("StandardDialog.ContentPadding"))
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.VAlign(VAlign_Center)
[
SettingsView->GetWidget()->AsShared()
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Right)
.Padding(4, 4, 10, 4)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.AutoWidth()
.Padding(0, 0, 4, 0)
[
SNew(SButton)
.Text(LOCTEXT("AddToPreset", "AddToPreset"))
.OnClicked(this, &SHotPatcherPatchWidget::DoAddToPreset)
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.AutoWidth()
.Padding(0, 0, 4, 0)
[
SNew(SButton)
.Text(LOCTEXT("PreviewChunk", "PreviewChunk"))
.IsEnabled(this, &SHotPatcherPatchWidget::CanPreviewChunk)
.OnClicked(this, &SHotPatcherPatchWidget::DoPreviewChunk)
.Visibility(this, &SHotPatcherPatchWidget::VisibilityPreviewChunkButtons)
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.AutoWidth()
.Padding(0, 0, 4, 0)
[
SNew(SButton)
.Text(LOCTEXT("Diff", "Diff"))
.IsEnabled(this, &SHotPatcherPatchWidget::CanDiff)
.OnClicked(this, &SHotPatcherPatchWidget::DoDiff)
.Visibility(this, &SHotPatcherPatchWidget::VisibilityDiffButtons)
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.AutoWidth()
.Padding(0, 0, 4, 0)
[
SNew(SButton)
.Text(LOCTEXT("ClearDiff", "ClearDiff"))
.IsEnabled(this, &SHotPatcherPatchWidget::CanDiff)
.OnClicked(this, &SHotPatcherPatchWidget::DoClearDiff)
.Visibility(this, &SHotPatcherPatchWidget::VisibilityDiffButtons)
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.AutoWidth()
.Padding(0, 0, 4, 0)
[
SNew(SButton)
.Text(LOCTEXT("PreviewPatch", "PreviewPatch"))
.IsEnabled(this, &SHotPatcherPatchWidget::CanPreviewPatch)
.OnClicked(this, &SHotPatcherPatchWidget::DoPreviewPatch)
.ToolTipText(this,&SHotPatcherPatchWidget::GetGenerateTooltipText)
]
+ SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.AutoWidth()
.Padding(0, 0, 4, 0)
[
SNew(SButton)
.Text(LOCTEXT("GeneratePatch", "GeneratePatch"))
.ToolTipText(this,&SHotPatcherPatchWidget::GetGenerateTooltipText)
.IsEnabled(this, &SHotPatcherPatchWidget::CanExportPatch)
.OnClicked(this, &SHotPatcherPatchWidget::DoExportPatch)
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.Padding(4, 4, 10, 4)
[
SAssignNew(DiffWidget, SHotPatcherInformations)
.Visibility(EVisibility::Collapsed)
]
];
}
void SHotPatcherPatchWidget::ImportConfig()
{
UE_LOG(LogHotPatcher, Log, TEXT("Patch Import Config"));
TArray<FString> Files = this->OpenFileDialog();
if (!Files.Num()) return;
FString LoadFile = Files[0];
FString JsonContent;
if (UFlibAssetManageHelper::LoadFileToString(LoadFile, JsonContent))
{
THotPatcherTemplateHelper::TDeserializeJsonStringAsStruct(JsonContent,*ExportPatchSetting);
// adaptor old version config
UFlibHotPatcherCoreHelper::AdaptorOldVersionConfig(ExportPatchSetting->GetAssetScanConfigRef(),JsonContent);
SettingsView->GetDetailsView()->ForceRefresh();
}
}
void SHotPatcherPatchWidget::ExportConfig()const
{
UE_LOG(LogHotPatcher, Log, TEXT("Patch Export Config"));
TArray<FString> Files = this->SaveFileDialog();
if (!Files.Num()) return;
FString SaveToFile = Files[0].EndsWith(TEXT(".json")) ? Files[0] : Files[0].Append(TEXT(".json"));
if (ExportPatchSetting)
{
if (ExportPatchSetting->IsSaveConfig())
{
FString SerializedJsonStr;
THotPatcherTemplateHelper::TSerializeStructAsJsonString(*ExportPatchSetting,SerializedJsonStr);
if (FFileHelper::SaveStringToFile(SerializedJsonStr, *SaveToFile))
{
FText Msg = LOCTEXT("SavedPatchConfigMas", "Successd to Export the Patch Config.");
UFlibHotPatcherEditorHelper::CreateSaveFileNotify(Msg, SaveToFile);
}
}
}
}
void SHotPatcherPatchWidget::ResetConfig()
{
UE_LOG(LogHotPatcher, Log, TEXT("Patch Clear Config"));
FString DefaultSettingJson;
THotPatcherTemplateHelper::TSerializeStructAsJsonString(*FExportPatchSettings::Get(),DefaultSettingJson);
THotPatcherTemplateHelper::TDeserializeJsonStringAsStruct(DefaultSettingJson,*ExportPatchSetting);
SettingsView->GetDetailsView()->ForceRefresh();
}
void SHotPatcherPatchWidget::DoGenerate()
{
DoExportPatch();
}
bool SHotPatcherPatchWidget::InformationContentIsVisibility() const
{
return DiffWidget->GetVisibility() == EVisibility::Visible;
}
void SHotPatcherPatchWidget::SetInformationContent(const FString& InContent)const
{
DiffWidget->SetContent(InContent);
}
void SHotPatcherPatchWidget::SetInfomationContentVisibility(EVisibility InVisibility)const
{
DiffWidget->SetVisibility(InVisibility);
}
void SHotPatcherPatchWidget::ImportProjectConfig()
{
SHotPatcherWidgetBase::ImportProjectConfig();
bool bUseIoStore = false;
bool bAllowBulkDataInIoStore = false;
GConfig->GetBool(TEXT("/Script/UnrealEd.ProjectPackagingSettings"),TEXT("bUseIoStore"),bUseIoStore,GGameIni);
GConfig->GetBool(TEXT("Core.System"),TEXT("AllowBulkDataInIoStore"),bAllowBulkDataInIoStore,GEngineIni);
GetConfigSettings()->IoStoreSettings.bIoStore = bUseIoStore;
GetConfigSettings()->IoStoreSettings.bAllowBulkDataInIoStore = bAllowBulkDataInIoStore;
#if ENGINE_MAJOR_VERSION > 4
bool bMakeBinaryConfig = false;
GConfig->GetBool(TEXT("/Script/UnrealEd.ProjectPackagingSettings"),TEXT("bMakeBinaryConfig"),bMakeBinaryConfig,GEngineIni);
GetConfigSettings()->bMakeBinaryConfig = bMakeBinaryConfig;
#endif
FString PakFileCompressionFormats;
GConfig->GetString(TEXT("/Script/UnrealEd.ProjectPackagingSettings"),TEXT("PakFileCompressionFormats"),PakFileCompressionFormats,GGameIni);
if(!PakFileCompressionFormats.IsEmpty())
{
PakFileCompressionFormats = FString::Printf(TEXT("-compressionformats=%s"),*PakFileCompressionFormats);
GetConfigSettings()->DefaultCommandletOptions.AddUnique(PakFileCompressionFormats);
}
FString PakFileAdditionalCompressionOptions;
GConfig->GetString(TEXT("/Script/UnrealEd.ProjectPackagingSettings"),TEXT("PakFileAdditionalCompressionOptions"),PakFileAdditionalCompressionOptions,GGameIni);
if(!PakFileAdditionalCompressionOptions.IsEmpty())
GetConfigSettings()->DefaultCommandletOptions.AddUnique(PakFileAdditionalCompressionOptions);
}
void SHotPatcherPatchWidget::ShowMsg(const FString& InMsg)const
{
auto ErrorMsgShowLambda = [this](const FString& InErrorMsg)->bool
{
bool bHasError = false;
if (!InErrorMsg.IsEmpty())
{
this->SetInformationContent(InErrorMsg);
this->SetInfomationContentVisibility(EVisibility::Visible);
bHasError = true;
}
else
{
if (this->InformationContentIsVisibility())
{
this->SetInformationContent(TEXT(""));
this->SetInfomationContentVisibility(EVisibility::Collapsed);
}
}
return bHasError;
};
ErrorMsgShowLambda(InMsg);
}
bool SHotPatcherPatchWidget::CanDiff()const
{
bool bCanDiff = false;
if (ExportPatchSetting)
{
bool bHasBase = !ExportPatchSetting->GetBaseVersion().IsEmpty() && FPaths::FileExists(ExportPatchSetting->GetBaseVersion());
bool bHasVersionId = !ExportPatchSetting->GetVersionId().IsEmpty();
bool bHasFilter = !!ExportPatchSetting->GetAssetIncludeFilters().Num();
bool bHasSpecifyAssets = !!ExportPatchSetting->GetIncludeSpecifyAssets().Num();
bCanDiff = bHasBase && bHasVersionId && (bHasFilter || bHasSpecifyAssets);
}
return bCanDiff;
}
FReply SHotPatcherPatchWidget::DoDiff()const
{
FString BaseVersionContent;
FHotPatcherVersion BaseVersion;
bool bDeserializeStatus = false;
if (ExportPatchSetting->IsByBaseVersion())
{
if (UFlibAssetManageHelper::LoadFileToString(ExportPatchSetting->GetBaseVersion(), BaseVersionContent))
{
bDeserializeStatus = THotPatcherTemplateHelper::TDeserializeJsonStringAsStruct(BaseVersionContent, BaseVersion);
}
if (!bDeserializeStatus)
{
UE_LOG(LogHotPatcher, Error, TEXT("Deserialize Base Version Faild!"));
return FReply::Handled();
}
}
ExportPatchSetting->Init();
FHotPatcherVersion CurrentVersion;
// UFlibPatchParserHelper::ExportReleaseVersionInfo(
// ExportPatchSetting->GetVersionId(),
// BaseVersion.VersionId,
// FDateTime::UtcNow().ToString(),
// UFlibAssetManageHelper::DirectoryPathsToStrings(ExportPatchSetting->GetAssetIncludeFilters()),
// UFlibAssetManageHelper::DirectoryPathsToStrings(ExportPatchSetting->GetAssetIgnoreFilters()),
// ExportPatchSetting->GetAllSkipContents(),
// ExportPatchSetting->GetForceSkipClasses(),
// ExportPatchSetting->GetAssetRegistryDependencyTypes(),
// ExportPatchSetting->GetIncludeSpecifyAssets(),
// ExportPatchSetting->GetAddExternAssetsToPlatform(),
// ExportPatchSetting->IsIncludeHasRefAssetsOnly()
// );
CurrentVersion.VersionId = ExportPatchSetting->GetVersionId();
CurrentVersion.BaseVersionId = BaseVersion.VersionId;
CurrentVersion.Date = FDateTime::UtcNow().ToString();
UFlibPatchParserHelper::RunAssetScanner(ExportPatchSetting->GetAssetScanConfig(),CurrentVersion);
UFlibPatchParserHelper::ExportExternAssetsToPlatform(ExportPatchSetting->GetAddExternAssetsToPlatform(),CurrentVersion,true,ExportPatchSetting->GetHashCalculator());
FPatchVersionDiff VersionDiffInfo = UFlibHotPatcherCoreHelper::DiffPatchVersionWithPatchSetting(*ExportPatchSetting, BaseVersion, CurrentVersion);
bool bShowDeleteAsset = false;
FString SerializeDiffInfo;
THotPatcherTemplateHelper::TSerializeStructAsJsonString(VersionDiffInfo,SerializeDiffInfo);
SetInformationContent(SerializeDiffInfo);
SetInfomationContentVisibility(EVisibility::Visible);
return FReply::Handled();
}
FReply SHotPatcherPatchWidget::DoClearDiff()const
{
SetInformationContent(TEXT(""));
SetInfomationContentVisibility(EVisibility::Collapsed);
return FReply::Handled();
}
EVisibility SHotPatcherPatchWidget::VisibilityDiffButtons() const
{
bool bHasBase = false;
if (ExportPatchSetting && ExportPatchSetting->IsByBaseVersion())
{
FString BaseVersionFile = ExportPatchSetting->GetBaseVersion();
bHasBase = !BaseVersionFile.IsEmpty() && FPaths::FileExists(BaseVersionFile);
}
if (bHasBase && CanExportPatch())
{
return EVisibility::Visible;
}
else {
return EVisibility::Collapsed;
}
}
FReply SHotPatcherPatchWidget::DoPreviewChunk() const
{
FHotPatcherVersion BaseVersion;
if (ExportPatchSetting->IsByBaseVersion() && !ExportPatchSetting->GetBaseVersionInfo(BaseVersion))
{
UE_LOG(LogHotPatcher, Error, TEXT("Deserialize Base Version Faild!"));
return FReply::Handled();
}
else
{
// 在不进行外部文件diff的情况下清理掉基础版本的外部文件
if (!ExportPatchSetting->IsEnableExternFilesDiff())
{
BaseVersion.PlatformAssets.Empty();
}
}
ExportPatchSetting->Init();
UFlibAssetManageHelper::UpdateAssetMangerDatabase(true);
FChunkInfo NewVersionChunk = UFlibHotPatcherCoreHelper::MakeChunkFromPatchSettings(ExportPatchSetting.Get());
FHotPatcherVersion CurrentVersion = UFlibPatchParserHelper::ExportReleaseVersionInfoByChunk(
ExportPatchSetting->GetVersionId(),
BaseVersion.VersionId,
FDateTime::UtcNow().ToString(),
NewVersionChunk,
ExportPatchSetting->IsIncludeHasRefAssetsOnly(),
ExportPatchSetting->AssetScanConfig.bAnalysisFilterDependencies,
ExportPatchSetting->GetHashCalculator()
);
FString CurrentVersionSavePath = ExportPatchSetting->GetCurrentVersionSavePath();
FPatchVersionDiff VersionDiffInfo = UFlibHotPatcherCoreHelper::DiffPatchVersionWithPatchSetting(*ExportPatchSetting, BaseVersion, CurrentVersion);
TArray<FChunkInfo> PatchChunks = ExportPatchSetting->GetChunkInfos();
// create default chunk
if(ExportPatchSetting->IsCreateDefaultChunk())
{
FChunkInfo TotalChunk = UFlibPatchParserHelper::CombineChunkInfos(ExportPatchSetting->GetChunkInfos());
FChunkAssetDescribe ChunkDiffInfo = UFlibHotPatcherCoreHelper::DiffChunkWithPatchSetting(
*ExportPatchSetting,
NewVersionChunk,
TotalChunk
);
if(ChunkDiffInfo.HasValidAssets())
{
PatchChunks.Add(ChunkDiffInfo.AsChunkInfo(TEXT("Default")));
}
}
FString ShowMsg;
for (const auto& Chunk : PatchChunks)
{
FChunkAssetDescribe ChunkAssetsDescrible = UFlibPatchParserHelper::CollectFChunkAssetsDescribeByChunk(ExportPatchSetting.Get(), VersionDiffInfo,Chunk, ExportPatchSetting->GetPakTargetPlatforms());
ShowMsg.Append(FString::Printf(TEXT("Chunk:%s\n"), *Chunk.ChunkName));
auto AppendFilesToMsg = [&ShowMsg](const FString& CategoryName, const TArray<FName>& InFiles)
{
if (!!InFiles.Num())
{
ShowMsg.Append(FString::Printf(TEXT("%s:\n"), *CategoryName));
for (const auto& File : InFiles)
{
ShowMsg.Append(FString::Printf(TEXT("\t%s\n"), *File.ToString()));
}
}
};
AppendFilesToMsg(TEXT("UE Assets"), ChunkAssetsDescrible.GetAssetsStrings());
for(auto Platform:ExportPatchSetting->GetPakTargetPlatforms())
{
TArray<FName> PlatformExFiles;
FString PlatformName = THotPatcherTemplateHelper::GetEnumNameByValue(Platform,false);
PlatformExFiles.Append(ChunkAssetsDescrible.GetExternalFileNames(Platform));
AppendFilesToMsg(PlatformName, PlatformExFiles);
}
AppendFilesToMsg(TEXT("Internal Files"), ChunkAssetsDescrible.GetInternalFileNames());
ShowMsg.Append(TEXT("\n"));
}
if (!ShowMsg.IsEmpty())
{
this->ShowMsg(ShowMsg);
}
return FReply::Handled();
}
bool SHotPatcherPatchWidget::CanPreviewChunk() const
{
return ExportPatchSetting->IsEnableChunk();
}
EVisibility SHotPatcherPatchWidget::VisibilityPreviewChunkButtons() const
{
if (CanPreviewChunk())
{
return EVisibility::Visible;
}
else {
return EVisibility::Collapsed;
}
}
bool SHotPatcherPatchWidget::CanExportPatch()const
{
return UFlibPatchParserHelper::IsValidPatchSettings(ExportPatchSetting.Get(),GetDefault<UHotPatcherSettings>()->bExternalFilesCheck);
}
FReply SHotPatcherPatchWidget::DoExportPatch()
{
TSharedPtr<FExportPatchSettings> PatchSettings = MakeShareable(new FExportPatchSettings);
*PatchSettings = *GetConfigSettings();
FHotPatcherEditorModule::Get().CookAndPakByPatchSettings(PatchSettings,PatchSettings->IsStandaloneMode());
return FReply::Handled();
}
FText SHotPatcherPatchWidget::GetGenerateTooltipText() const
{
FString FinalString;
if (GetMutableDefault<UHotPatcherSettings>()->bPreviewTooltips && ExportPatchSetting)
{
bool bHasBase = false;
if (ExportPatchSetting->IsByBaseVersion())
bHasBase = !ExportPatchSetting->GetBaseVersion().IsEmpty() && FPaths::FileExists(ExportPatchSetting->GetBaseVersion());
else
bHasBase = true;
bool bHasVersionId = !ExportPatchSetting->GetVersionId().IsEmpty();
bool bHasFilter = !!ExportPatchSetting->GetAssetIncludeFilters().Num();
bool bHasSpecifyAssets = !!ExportPatchSetting->GetIncludeSpecifyAssets().Num();
// bool bHasExternFiles = !!ExportPatchSetting->GetAddExternFiles().Num();
// bool bHasExDirs = !!ExportPatchSetting->GetAddExternDirectory().Num();
bool bHasExternFiles = true;
if(GetDefault<UHotPatcherSettings>()->bExternalFilesCheck)
{
bHasExternFiles = !!ExportPatchSetting->GetAllPlatfotmExternFiles().Num();
}
bool bHasExDirs = !!ExportPatchSetting->GetAddExternAssetsToPlatform().Num();
bool bHasSavePath = !ExportPatchSetting->GetSaveAbsPath().IsEmpty();
bool bHasPakPlatfotm = !!ExportPatchSetting->GetPakTargetPlatforms().Num();
bool bHasAnyPakFiles = (
bHasFilter || bHasSpecifyAssets || bHasExternFiles || bHasExDirs ||
ExportPatchSetting->IsIncludeEngineIni() ||
ExportPatchSetting->IsIncludePluginIni() ||
ExportPatchSetting->IsIncludeProjectIni()
);
struct FStatus
{
FStatus(bool InMatch,const FString& InDisplay):bMatch(InMatch)
{
Display = FString::Printf(TEXT("%s:%s"),*InDisplay,InMatch?TEXT("true"):TEXT("false"));
}
FString GetDisplay()const{return Display;}
bool bMatch;
FString Display;
};
TArray<FStatus> AllStatus;
AllStatus.Emplace(bHasBase,TEXT("BaseVersion"));
AllStatus.Emplace(bHasVersionId,TEXT("HasVersionId"));
AllStatus.Emplace(bHasAnyPakFiles,TEXT("HasAnyPakFiles"));
AllStatus.Emplace(bHasPakPlatfotm,TEXT("HasPakPlatfotm"));
AllStatus.Emplace(bHasSavePath,TEXT("HasSavePath"));
for(const auto& Status:AllStatus)
{
FinalString+=FString::Printf(TEXT("%s\n"),*Status.GetDisplay());
}
}
return UKismetTextLibrary::Conv_StringToText(FinalString);
}
bool SHotPatcherPatchWidget::CanPreviewPatch() const
{
bool bHasFilter = !!ExportPatchSetting->GetAssetIncludeFilters().Num();
bool bHasSpecifyAssets = !!ExportPatchSetting->GetIncludeSpecifyAssets().Num();
auto HasExFilesLambda = [this]()
{
bool result = false;
const TMap<ETargetPlatform,FPlatformExternFiles>& ExFiles = ExportPatchSetting->GetAllPlatfotmExternFiles(false);
if(!!ExFiles.Num())
{
TArray<ETargetPlatform> Platforms;
ExFiles.GetKeys(Platforms);
for(const auto& Platform:Platforms)
{
if(!!ExFiles.Find(Platform)->ExternFiles.Num())
{
result=true;
break;
}
}
}
return result;
};
bool bHasExternFiles = true;
if(GetDefault<UHotPatcherSettings>()->bExternalFilesCheck)
{
bHasExternFiles = HasExFilesLambda();
}
bool bHasAnyPakFiles = (
bHasFilter || bHasSpecifyAssets || bHasExternFiles ||
ExportPatchSetting->IsIncludeEngineIni() ||
ExportPatchSetting->IsIncludePluginIni() ||
ExportPatchSetting->IsIncludeProjectIni()
);
return bHasFilter || bHasSpecifyAssets || bHasExternFiles || bHasAnyPakFiles;
}
FReply SHotPatcherPatchWidget::DoPreviewPatch()
{
ExportPatchSetting->Init();
FChunkInfo DefaultChunk;
FHotPatcherVersion BaseVersion;
if (ExportPatchSetting->IsByBaseVersion())
{
ExportPatchSetting->GetBaseVersionInfo(BaseVersion);
DefaultChunk = UFlibHotPatcherCoreHelper::MakeChunkFromPatchVerison(BaseVersion);
if (!ExportPatchSetting->IsEnableExternFilesDiff())
{
BaseVersion.PlatformAssets.Empty();
}
}
FChunkInfo NewVersionChunk = UFlibHotPatcherCoreHelper::MakeChunkFromPatchSettings(ExportPatchSetting.Get());
FChunkAssetDescribe ChunkAssetsDescrible = UFlibHotPatcherCoreHelper::DiffChunkByBaseVersionWithPatchSetting(*ExportPatchSetting.Get(),NewVersionChunk, DefaultChunk, BaseVersion);
TArray<FName> AllUnselectedAssets = ChunkAssetsDescrible.GetAssetsStrings();
TArray<FName> UnSelectedInternalFiles = ChunkAssetsDescrible.GetInternalFileNames();
FString TotalMsg;
auto ChunkCheckerMsg = [&TotalMsg](const FString& Category, const TArray<FName>& InAssetList)
{
if (!!InAssetList.Num())
{
TotalMsg.Append(FString::Printf(TEXT("\n%s:\n"), *Category));
for (const auto& Asset : InAssetList)
{
TotalMsg.Append(FString::Printf(TEXT("\t%s\n"), *Asset.ToString()));
}
}
};
ChunkCheckerMsg(TEXT("Unreal Asset"), AllUnselectedAssets);
ChunkCheckerMsg(TEXT("External Files"), TArray<FName>{});
for(auto Platform:ExportPatchSetting->GetPakTargetPlatforms())
{
TArray<FName> PlatformExFiles;
FString PlatformName = THotPatcherTemplateHelper::GetEnumNameByValue(Platform,false);
PlatformExFiles.Append(ChunkAssetsDescrible.GetExternalFileNames(Platform));
PlatformExFiles.Append(ChunkAssetsDescrible.GetExternalFileNames(ETargetPlatform::AllPlatforms));
ChunkCheckerMsg(PlatformName, PlatformExFiles);
}
ChunkCheckerMsg(TEXT("Internal Files"), UnSelectedInternalFiles);
if (!TotalMsg.IsEmpty())
{
ShowMsg(FString::Printf(TEXT("Patch Assets:\n%s"), *TotalMsg));
return FReply::Handled();
}
return FReply::Handled();
}
FReply SHotPatcherPatchWidget::DoAddToPreset() const
{
UHotPatcherSettings* Settings = GetMutableDefault<UHotPatcherSettings>();
Settings->PresetConfigs.Add(*const_cast<SHotPatcherPatchWidget*>(this)->GetConfigSettings());
Settings->SaveConfig();
return FReply::Handled();
}
void SHotPatcherPatchWidget::CreateExportFilterListView()
{
// Create a property view
FPropertyEditorModule& EditModule = FModuleManager::Get().GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
FDetailsViewArgs DetailsViewArgs;
{
DetailsViewArgs.bAllowSearch = true;
DetailsViewArgs.bHideSelectionTip = true;
DetailsViewArgs.bLockable = false;
DetailsViewArgs.bSearchInitialKeyFocus = true;
DetailsViewArgs.bUpdatesFromSelection = false;
DetailsViewArgs.NotifyHook = nullptr;
DetailsViewArgs.bShowOptions = true;
DetailsViewArgs.bShowModifiedPropertiesOption = false;
DetailsViewArgs.bShowScrollBar = false;
DetailsViewArgs.bShowOptions = true;
}
FStructureDetailsViewArgs StructureViewArgs;
{
StructureViewArgs.bShowObjects = true;
StructureViewArgs.bShowAssets = true;
StructureViewArgs.bShowClasses = true;
StructureViewArgs.bShowInterfaces = true;
}
SettingsView = EditModule.CreateStructureDetailView(DetailsViewArgs, StructureViewArgs, nullptr);
FStructOnScope* Struct = new FStructOnScope(FExportPatchSettings::StaticStruct(), (uint8*)ExportPatchSetting.Get());
SettingsView->SetStructureData(MakeShareable(Struct));
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,232 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
// #include "HotPatcherPrivatePCH.h"
#include "CreatePatch/SHotPatcherReleaseWidget.h"
#include "FlibHotPatcherCoreHelper.h"
#include "FlibAssetManageHelper.h"
#include "AssetManager/FAssetDependenciesInfo.h"
#include "FHotPatcherVersion.h"
#include "HotPatcherLog.h"
#include "CreatePatch/ReleaseSettingsDetails.h"
#include "CreatePatch/FExportReleaseSettings.h"
#include "HotPatcherEditor.h"
// engine header
#include "FlibHotPatcherEditorHelper.h"
#include "CreatePatch/ReleaseProxy.h"
#include "Widgets/Input/SHyperlink.h"
#include "Widgets/Layout/SSeparator.h"
#include "HAL/FileManager.h"
#include "Kismet/KismetTextLibrary.h"
#include "Misc/FileHelper.h"
#include "Misc/ScopedSlowTask.h"
#include "Misc/EngineVersionComparison.h"
#if !UE_VERSION_OLDER_THAN(5,1,0)
typedef FAppStyle FEditorStyle;
#endif
#define LOCTEXT_NAMESPACE "SHotPatcherExportRelease"
void SHotPatcherReleaseWidget::Construct(const FArguments& InArgs, TSharedPtr<FHotPatcherContextBase> InCreatePatchModel)
{
ExportReleaseSettings = MakeShareable(new FExportReleaseSettings);
GReleaseSettings = ExportReleaseSettings.Get();
CreateExportFilterListView();
mContext = InCreatePatchModel;
ChildSlot
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(FEditorStyle::GetMargin("StandardDialog.ContentPadding"))
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.VAlign(VAlign_Center)
[
SettingsView->GetWidget()->AsShared()
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(0.0, 8.0, 0.0, 0.0)
+ SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Right)
.Padding(4, 4, 10, 4)
[
SNew(SButton)
.Text(LOCTEXT("GenerateRelease", "Export Release"))
.OnClicked(this,&SHotPatcherReleaseWidget::DoExportRelease)
.IsEnabled(this,&SHotPatcherReleaseWidget::CanExportRelease)
.ToolTipText(this,&SHotPatcherReleaseWidget::GetGenerateTooltipText)
]
];
}
void SHotPatcherReleaseWidget::ImportConfig()
{
UE_LOG(LogHotPatcher, Log, TEXT("Release Import Config"));
TArray<FString> Files = this->OpenFileDialog();
if (!Files.Num()) return;
FString LoadFile = Files[0];
FString JsonContent;
if (UFlibAssetManageHelper::LoadFileToString(LoadFile, JsonContent))
{
// UFlibHotPatcherCoreHelper::DeserializeReleaseConfig(ExportReleaseSettings, JsonContent);
THotPatcherTemplateHelper::TDeserializeJsonStringAsStruct(JsonContent,*ExportReleaseSettings);
// adaptor old version config
UFlibHotPatcherCoreHelper::AdaptorOldVersionConfig(ExportReleaseSettings->GetAssetScanConfigRef(),JsonContent);
SettingsView->GetDetailsView()->ForceRefresh();
}
}
void SHotPatcherReleaseWidget::ExportConfig()const
{
UE_LOG(LogHotPatcher, Log, TEXT("Release Export Config"));
TArray<FString> Files = this->SaveFileDialog();
if (!Files.Num()) return;
FString SaveToFile = Files[0].EndsWith(TEXT(".json")) ? Files[0] : Files[0].Append(TEXT(".json"));
if (ExportReleaseSettings)
{
FString SerializedJsonStr;
THotPatcherTemplateHelper::TSerializeStructAsJsonString(*ExportReleaseSettings,SerializedJsonStr);
if (FFileHelper::SaveStringToFile(SerializedJsonStr, *SaveToFile))
{
FText Msg = LOCTEXT("SavedPatchConfigMas", "Successd to Export the Patch Config.");
UFlibHotPatcherEditorHelper::CreateSaveFileNotify(Msg, SaveToFile);
}
}
}
void SHotPatcherReleaseWidget::ResetConfig()
{
UE_LOG(LogHotPatcher, Log, TEXT("Release Clear Config"));
FString DefaultSettingJson;
THotPatcherTemplateHelper::TSerializeStructAsJsonString(*FExportReleaseSettings::Get(),DefaultSettingJson);
THotPatcherTemplateHelper::TDeserializeJsonStringAsStruct(DefaultSettingJson,*ExportReleaseSettings);
SettingsView->GetDetailsView()->ForceRefresh();
}
void SHotPatcherReleaseWidget::DoGenerate()
{
UE_LOG(LogHotPatcher, Log, TEXT("Release DoGenerate"));
}
void SHotPatcherReleaseWidget::CreateExportFilterListView()
{
// Create a property view
FPropertyEditorModule& EditModule = FModuleManager::Get().GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
FDetailsViewArgs DetailsViewArgs;
{
DetailsViewArgs.bAllowSearch = true;
DetailsViewArgs.bHideSelectionTip = true;
DetailsViewArgs.bLockable = false;
DetailsViewArgs.bSearchInitialKeyFocus = true;
DetailsViewArgs.bUpdatesFromSelection = false;
DetailsViewArgs.NotifyHook = nullptr;
DetailsViewArgs.bShowOptions = true;
DetailsViewArgs.bShowModifiedPropertiesOption = false;
DetailsViewArgs.bShowScrollBar = false;
DetailsViewArgs.bShowOptions = true;
DetailsViewArgs.bUpdatesFromSelection= true;
}
FStructureDetailsViewArgs StructureViewArgs;
{
StructureViewArgs.bShowObjects = true;
StructureViewArgs.bShowAssets = true;
StructureViewArgs.bShowClasses = true;
StructureViewArgs.bShowInterfaces = true;
}
SettingsView = EditModule.CreateStructureDetailView(DetailsViewArgs, StructureViewArgs, nullptr);
FStructOnScope* Struct = new FStructOnScope(FExportReleaseSettings::StaticStruct(), (uint8*)ExportReleaseSettings.Get());
SettingsView->GetOnFinishedChangingPropertiesDelegate().AddRaw(ExportReleaseSettings.Get(),&FExportReleaseSettings::OnFinishedChangingProperties);
SettingsView->GetDetailsView()->RegisterInstancedCustomPropertyLayout(FExportReleaseSettings::StaticStruct(),FOnGetDetailCustomizationInstance::CreateStatic(&FReleaseSettingsDetails::MakeInstance));
SettingsView->SetStructureData(MakeShareable(Struct));
}
bool SHotPatcherReleaseWidget::CanExportRelease()const
{
bool bCanExport=false;
if (ExportReleaseSettings)
{
bool bHasVersion = !ExportReleaseSettings->GetVersionId().IsEmpty();
bool bHasFilter = !!ExportReleaseSettings->GetAssetIncludeFilters().Num();
bool bHasSpecifyAssets = !!ExportReleaseSettings->GetSpecifyAssets().Num();
bool bHasSavePath = !(ExportReleaseSettings->GetSaveAbsPath().IsEmpty());
bool bHasPakInfo = !!ExportReleaseSettings->GetPlatformsPakListFiles().Num();
bCanExport = bHasVersion && (ExportReleaseSettings->IsImportProjectSettings() || bHasFilter || bHasSpecifyAssets || bHasPakInfo) && bHasSavePath;
}
return bCanExport;
}
FReply SHotPatcherReleaseWidget::DoExportRelease()
{
if(!GetConfigSettings()->IsStandaloneMode())
{
UReleaseProxy* ReleaseProxy = NewObject<UReleaseProxy>();
ReleaseProxy->AddToRoot();
ReleaseProxy->Init(ExportReleaseSettings.Get());
ReleaseProxy->DoExport();
}
else
{
FString CurrentConfig;
THotPatcherTemplateHelper::TSerializeStructAsJsonString(*GetConfigSettings(),CurrentConfig);
FString SaveConfigTo = FPaths::ConvertRelativePathToFull(FPaths::Combine(FPaths::ProjectSavedDir(),TEXT("HotPatcher"),TEXT("ReleaseConfig.json")));
FFileHelper::SaveStringToFile(CurrentConfig,*SaveConfigTo);
FString NoShaderCompile = GetConfigSettings()->bNoShaderCompile ? TEXT("-NoShaderCompile") : TEXT("");
FString MissionCommand = FString::Printf(TEXT("\"%s\" -run=HotRelease -config=\"%s\" %s %s"),*UFlibPatchParserHelper::GetProjectFilePath(),*SaveConfigTo,*GetConfigSettings()->GetCombinedAdditionalCommandletArgs(),*NoShaderCompile);
UE_LOG(LogHotPatcher,Log,TEXT("HotPatcher %s Mission: %s %s"),*GetMissionName(),*UFlibHotPatcherCoreHelper::GetUECmdBinary(),*MissionCommand);
FHotPatcherEditorModule::Get().RunProcMission(UFlibHotPatcherCoreHelper::GetUECmdBinary(),MissionCommand,GetMissionName());
}
return FReply::Handled();
}
FText SHotPatcherReleaseWidget::GetGenerateTooltipText() const
{
FString FinalString;
if (GetMutableDefault<UHotPatcherSettings>()->bPreviewTooltips && ExportReleaseSettings)
{
bool bHasVersion = !ExportReleaseSettings->GetVersionId().IsEmpty();
bool bHasFilter = !!ExportReleaseSettings->GetAssetIncludeFilters().Num();
bool bHasSpecifyAssets = !!ExportReleaseSettings->GetSpecifyAssets().Num();
bool bHasExternFiles = !!ExportReleaseSettings->GetAddExternAssetsToPlatform().Num();
bool bHasPakInfo = !!ExportReleaseSettings->GetPlatformsPakListFiles().Num();
bool bHasSavePath = !(ExportReleaseSettings->GetSaveAbsPath().IsEmpty());
struct FStatus
{
FStatus(bool InMatch,const FString& InDisplay):bMatch(InMatch)
{
Display = FString::Printf(TEXT("%s:%s"),*InDisplay,InMatch?TEXT("true"):TEXT("false"));
}
FString GetDisplay()const{return Display;}
bool bMatch;
FString Display;
};
TArray<FStatus> AllStatus;
AllStatus.Emplace(bHasVersion,TEXT("HasVersion"));
AllStatus.Emplace((ExportReleaseSettings->IsImportProjectSettings()||bHasFilter||bHasSpecifyAssets||bHasExternFiles||bHasPakInfo),TEXT("ImportProjectSettings or HasFilter or HasSpecifyAssets or bHasExternFiles or bHasPakInfo"));
AllStatus.Emplace(bHasSavePath,TEXT("HasSavePath"));
for(const auto& Status:AllStatus)
{
FinalString+=FString::Printf(TEXT("%s\n"),*Status.GetDisplay());
}
}
return UKismetTextLibrary::Conv_StringToText(FinalString);
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,169 @@
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "CreatePatch/SPatchersPage.h"
#include "CreatePatch/SHotPatcherPatchWidget.h"
#include "CreatePatch/SHotPatcherReleaseWidget.h"
// engine header
#include "HotPatcherEditor.h"
#include "Framework/Commands/UIAction.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Layout/SExpandableArea.h"
#define LOCTEXT_NAMESPACE "SProjectCreatePatchPage"
/* SProjectCreatePatchPage interface
*****************************************************************************/
void SPatchersPage::Construct(const FArguments& InArgs, TSharedPtr<FHotPatcherContextBase> InContext)
{
SetContext(InContext);
// create cook modes menu
FMenuBuilder PatchModeMenuBuilder(true, NULL);
TMap<FString,FHotPatcherAction>* Patchers = FHotPatcherActionManager::Get().GetHotPatcherActions().Find(GetPageName());
if(Patchers)
{
for(const auto& Action:*Patchers)
{
if(FHotPatcherActionManager::Get().IsSupportEditorAction(Action.Key))
{
FUIAction UIAction = FExecuteAction::CreateSP(this, &SPatchersPage::HandleHotPatcherMenuEntryClicked, Action.Value.ActionName.Get().ToString(),Action.Value.ActionCallback);
PatchModeMenuBuilder.AddMenuEntry(Action.Value.ActionName, Action.Value.ActionToolTip, Action.Value.Icon, UIAction);
}
}
}
auto Widget = SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.FillWidth(1.0)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("WhichProjectToUseText", "How would you like to Create Patch the content?"))
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(8.0, 0.0, 0.0, 0.0)
[
SNew(SButton)
.Text(LOCTEXT("ImportProjectConfig", "Import Project Config"))
.OnClicked(this,&SPatchersPage::DoImportProjectConfig)
.Visibility(this,&SPatchersPage::HandleImportProjectConfigVisibility)
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(8.0, 0.0, 0.0, 0.0)
[
SNew(SButton)
.Text(LOCTEXT("ImportConfig", "Import"))
.OnClicked(this,&SPatchersPage::DoImportConfig)
.Visibility(this,&SPatchersPage::HandleOperatorConfigVisibility)
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(5.0, 0.0, 0.0, 0.0)
[
SNew(SButton)
.Text(LOCTEXT("ExportConfig", "Export"))
.OnClicked(this,&SPatchersPage::DoExportConfig)
.Visibility(this, &SPatchersPage::HandleOperatorConfigVisibility)
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(5.0, 0.0, 0.0, 0.0)
[
SNew(SButton)
.Text(LOCTEXT("ResetConfig", "Reset"))
.OnClicked(this, &SPatchersPage::DoResetConfig)
.Visibility(this, &SPatchersPage::HandleOperatorConfigVisibility)
]
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(5.0, 0.0, 0.0, 0.0)
[
// cooking mode menu
SNew(SComboButton)
.ButtonContent()
[
SNew(STextBlock)
.Text(this, &SPatchersPage::HandlePatchModeComboButtonContentText)
]
.ContentPadding(FMargin(6.0, 2.0))
.MenuContent()
[
PatchModeMenuBuilder.MakeWidget()
]
]
];
if(Patchers)
{
for(const auto& Patcher:*Patchers)
{
if(Patcher.Value.RequestWidget)
{
TSharedRef<SHotPatcherWidgetInterface> Action = Patcher.Value.RequestWidget(GetContext());
Widget->AddSlot().AutoHeight().Padding(0.0, 8.0, 0.0, 0.0)
[
Action
];
ActionWidgetMap.Add(*Patcher.Key,Action);
}
}
}
ChildSlot
[
Widget
];
if(FHotPatcherAction* DefaultAction = FHotPatcherActionManager::Get().GetTopActionByCategory(GetPageName()))
{
HandleHotPatcherMenuEntryClicked(UKismetTextLibrary::Conv_TextToString(DefaultAction->ActionName.Get()),nullptr);
}
}
EVisibility SPatchersPage::HandleOperatorConfigVisibility()const
{
return EVisibility::Visible;
}
EVisibility SPatchersPage::HandleImportProjectConfigVisibility() const
{
TArray<FString> EnableImportActions = {TEXT("ByPatch"),TEXT("ByRelease")};
bool bEnable = EnableImportActions.Contains(GetContext()->GetModeName().ToString());
return bEnable ? EVisibility::Visible : EVisibility::Hidden;
}
void SPatchersPage::HandleHotPatcherMenuEntryClicked(FString InModeName,TFunction<void(void)> ActionCallback)
{
if(ActionCallback)
{
ActionCallback();
}
if (GetContext().IsValid())
{
GetContext()->SetModeByName(FName(*InModeName));
}
}
FText SPatchersPage::HandlePatchModeComboButtonContentText() const
{
FText ret;
if (GetContext().IsValid())
{
ret = FText::FromString(GetContext()->GetModeName().ToString());
}
return ret;
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,72 @@
#include "FlibHotPatcherEditorHelper.h"
#include "DesktopPlatformModule.h"
#include "IDesktopPlatform.h"
#include "Async/Async.h"
void UFlibHotPatcherEditorHelper::CreateSaveFileNotify(const FText& InMsg, const FString& InSavedFile,
SNotificationItem::ECompletionState NotifyType)
{
AsyncTask(ENamedThreads::GameThread,[InMsg,InSavedFile,NotifyType]()
{
auto Message = InMsg;
FNotificationInfo Info(Message);
Info.bFireAndForget = true;
Info.ExpireDuration = 5.0f;
Info.bUseSuccessFailIcons = false;
Info.bUseLargeFont = false;
const FString HyperLinkText = InSavedFile;
Info.Hyperlink = FSimpleDelegate::CreateLambda(
[](FString SourceFilePath)
{
FPlatformProcess::ExploreFolder(*SourceFilePath);
},
HyperLinkText
);
Info.HyperlinkText = FText::FromString(HyperLinkText);
FSlateNotificationManager::Get().AddNotification(Info)->SetCompletionState(NotifyType);
});
}
TArray<FString> UFlibHotPatcherEditorHelper::SaveFileDialog(const FString& DialogTitle, const FString& DefaultPath,
const FString& DefaultFile, const FString& FileTypes, uint32 Flags)
{
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
TArray<FString> SaveFilenames;
if (DesktopPlatform)
{
const bool bOpened = DesktopPlatform->SaveFileDialog(
nullptr,
DialogTitle,
DefaultPath,
DefaultFile,
FileTypes,
Flags,
SaveFilenames
);
}
return SaveFilenames;
}
TArray<FString> UFlibHotPatcherEditorHelper::OpenFileDialog(const FString& DialogTitle, const FString& DefaultPath,
const FString& DefaultFile, const FString& FileTypes, uint32 Flags)
{
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
TArray<FString> SelectedFiles;
if (DesktopPlatform)
{
const bool bOpened = DesktopPlatform->OpenFileDialog(
nullptr,
DialogTitle,
DefaultPath,
DefaultFile,
FileTypes,
Flags,
SelectedFiles
);
}
return SelectedFiles;
}

View File

@ -0,0 +1,281 @@
#include "HotPatcherActionManager.h"
#include "HotPatcherCore.h"
#include "HotPatcherEditor.h"
#include "Kismet/KismetTextLibrary.h"
#include "CreatePatch/SHotPatcherPatchWidget.h"
#include "CreatePatch/SHotPatcherReleaseWidget.h"
#include "Cooker/OriginalCooker/SOriginalCookWidget.h"
#include "SVersionUpdater/FVersionUpdaterManager.h"
#define LOCTEXT_NAMESPACE "FHotPatcherActionManager"
FHotPatcherActionManager FHotPatcherActionManager::Manager;
void FHotPatcherActionManager::Init()
{
FVersionUpdaterManager::Get().ModCurrentVersionGetter = [this](const FString& ActionName)->float
{
float CurrentVersion = 0.f;
FHotPatcherModDesc ModDesc;
if(GetModDescByName(ActionName,ModDesc))
{
CurrentVersion = ModDesc.CurrentVersion;
}
return CurrentVersion;
};
FVersionUpdaterManager::Get().ModIsActivteCallback = [this](const FString& ModName)->bool
{
return IsActiviteMod(*ModName);
};
auto HotPatcherModDescMap2ChildModDesc = [](const TMap<FName,FHotPatcherModDesc>& HotPatcherModDescMap)->TArray<FChildModDesc>
{
TArray<FChildModDesc> result;
for(const auto& HotPatcherMod:HotPatcherModDescMap)
{
FChildModDesc ChildModDesc;
ChildModDesc.ModName = HotPatcherMod.Value.ModName;
ChildModDesc.CurrentVersion = HotPatcherMod.Value.CurrentVersion;
ChildModDesc.MinToolVersion = HotPatcherMod.Value.MinHotPatcherVersion;
ChildModDesc.bIsBuiltInMod = HotPatcherMod.Value.bIsBuiltInMod;
ChildModDesc.Description = HotPatcherMod.Value.Description;
ChildModDesc.URL = HotPatcherMod.Value.URL;
ChildModDesc.UpdateURL = HotPatcherMod.Value.UpdateURL;
result.Add(ChildModDesc);
}
return result;
};
FVersionUpdaterManager::Get().RequestLocalRegistedMods = [this,HotPatcherModDescMap2ChildModDesc]()->TArray<FChildModDesc>
{
return HotPatcherModDescMap2ChildModDesc(ModsDesc);
};
FVersionUpdaterManager::Get().RequestUnsupportLocalMods = [this,HotPatcherModDescMap2ChildModDesc]()->TArray<FChildModDesc>
{
return HotPatcherModDescMap2ChildModDesc(UnSupportModsDesc);
};
SetupDefaultActions();
FVersionUpdaterManager::Get().RequestRemoveVersion(GRemoteVersionFile);
}
void FHotPatcherActionManager::Shutdown()
{
FVersionUpdaterManager::Get().Reset();
}
void FHotPatcherActionManager::RegisterCategory(const FHotPatcherCategory& Category)
{
HotPatcherCategorys.Emplace(Category.CategoryName,Category);
}
void FHotPatcherActionManager::RegisteHotPatcherAction(const FString& Category, const FString& ActionName,
const FHotPatcherAction& Action)
{
TMap<FString,FHotPatcherAction>& CategoryIns = HotPatcherActions.FindOrAdd(Category);
CategoryIns.Add(ActionName,Action);
CategoryIns.ValueSort([](const FHotPatcherAction& R,const FHotPatcherAction& L)->bool
{
return R.Priority > L.Priority;
});
FHotPatcherActionDesc Desc;
Desc.Category = Category;
Desc.ActionName = ActionName;
Desc.ToolTip = Action.ActionToolTip.Get().ToString();
Desc.Priority = Action.Priority;
Desc.RequestWidgetPtr = Action.RequestWidget;
ActionsDesc.Add(ActionName,Desc);
OnHotPatcherActionRegisted.Broadcast(Category,ActionName,Action);
}
void FHotPatcherActionManager::RegisteHotPatcherAction(const FHotPatcherActionDesc& NewAction)
{
THotPatcherTemplateHelper::AppendEnumeraters<EHotPatcherCookActionMode>(TArray<FString>{NewAction.ActionName});
FHotPatcherAction NewPatcherAction
(
*NewAction.ActionName,
*NewAction.ModName,
UKismetTextLibrary::Conv_StringToText(NewAction.ActionName),
UKismetTextLibrary::Conv_StringToText(NewAction.ToolTip),
FSlateIcon(),
nullptr,
NewAction.RequestWidgetPtr,
NewAction.Priority
);
FHotPatcherActionManager::Get().RegisteHotPatcherAction(NewAction.Category,NewAction.ActionName,NewPatcherAction);
}
void FHotPatcherActionManager::RegisteHotPatcherMod(const FHotPatcherModDesc& ModDesc)
{
if(ModDesc.MinHotPatcherVersion > GetHotPatcherVersion())
{
UE_LOG(LogHotPatcherEdotor,Warning,TEXT("%s Min Support Version is %.1f,Current HotPatcher Version is %.1f"),*ModDesc.ModName,ModDesc.MinHotPatcherVersion,GetHotPatcherVersion());
UnSupportModsDesc.Add(*ModDesc.ModName,ModDesc);
}
else
{
for(const auto& ActionDesc:ModDesc.ModActions)
{
FHotPatcherActionManager::Get().RegisteHotPatcherAction(ActionDesc);
}
ModsDesc.Add(*ModDesc.ModName,ModDesc);
}
}
void FHotPatcherActionManager::UnRegisteHotPatcherMod(const FHotPatcherModDesc& ModDesc)
{
for(const auto& ActionDesc:ModDesc.ModActions)
{
FHotPatcherActionManager::Get().UnRegisteHotPatcherAction(ActionDesc.Category,ActionDesc.ActionName);
}
ModsDesc.Remove(*ModDesc.ModName);
}
void FHotPatcherActionManager::UnRegisteHotPatcherAction(const FString& Category, const FString& ActionName)
{
TMap<FString,FHotPatcherAction>& CategoryIns = HotPatcherActions.FindOrAdd(Category);
FHotPatcherAction Action;
if(CategoryIns.Contains(ActionName))
{
Action = *CategoryIns.Find(ActionName);
CategoryIns.Remove(ActionName);
}
ActionsDesc.Remove(*ActionName);
OnHotPatcherActionUnRegisted.Broadcast(Category,ActionName,Action);
}
float FHotPatcherActionManager::GetHotPatcherVersion() const
{
FString Version = FString::Printf(
TEXT("%d.%d"),
FHotPatcherCoreModule::Get().GetMainVersion(),
FHotPatcherCoreModule::Get().GetPatchVersion()
);
return UKismetStringLibrary::Conv_StringToFloat(Version);
}
FHotPatcherAction* FHotPatcherActionManager::GetTopActionByCategory(const FString CategoryName)
{
FHotPatcherAction* result = nullptr;
TMap<FString,FHotPatcherAction>* CategoryIns = HotPatcherActions.Find(CategoryName);
if(CategoryIns)
{
for(auto& Pair:*CategoryIns)
{
if(result)
{
break;
}
result = &Pair.Value;
}
}
return result;
}
bool FHotPatcherActionManager::IsActiviteMod(const FString& ModName)
{
return ModsDesc.Contains(*ModName);
}
bool FHotPatcherActionManager::IsSupportEditorAction(FString ActionName)
{
FHotPatcherActionDesc Desc;
bool bDescStatus = GetActionDescByName(ActionName,Desc);
return IsActiveAction(ActionName) && (bDescStatus && Desc.RequestWidgetPtr);
}
bool FHotPatcherActionManager::IsActiveAction(FString ActionName)
{
if(FVersionUpdaterManager::Get().IsRequestFinished())
{
bool bActiveInRemote = false;
FRemteVersionDescrible* HotPatcherRemoteVersion = FVersionUpdaterManager::Get().GetRemoteVersionByName(TEXT("HotPatcher"));
if(HotPatcherRemoteVersion)
{
if(HotPatcherRemoteVersion->b3rdMods)
{
bActiveInRemote = true;
}
else
{
for(auto ActionCategory:HotPatcherRemoteVersion->ActiveActions)
{
if(ActionCategory.Value.Contains(*ActionName))
{
bActiveInRemote = true;
break;
}
}
}
}
return bActiveInRemote;
}
return true;
}
bool FHotPatcherActionManager::GetActionDescByName(const FString& ActionName, FHotPatcherActionDesc& Desc)
{
bool bStatus = ActionsDesc.Contains(ActionName);
if(bStatus)
{
Desc = *ActionsDesc.Find(ActionName);
}
return bStatus;
};
bool FHotPatcherActionManager::GetModDescByName(const FString& ModName, FHotPatcherModDesc& ModDesc)
{
bool bStatus = ModsDesc.Contains(*ModName);
if(bStatus)
{
ModDesc = *ModsDesc.Find(*ModName);
}
return bStatus;
}
#define HOTPATCHEER_CORE_MODENAME TEXT("HotPatcherCore")
#define CMDHANDLER_MODENAME TEXT("CmdHandler")
void FHotPatcherActionManager::SetupDefaultActions()
{
TArray<FHotPatcherModDesc> BuiltInMods;
FHotPatcherModDesc HotPatcherCoreMod;
HotPatcherCoreMod.ModName = HOTPATCHEER_CORE_MODENAME;
HotPatcherCoreMod.bIsBuiltInMod = true;
HotPatcherCoreMod.CurrentVersion = 1.0;
HotPatcherCoreMod.Description = TEXT("Unreal Engine Asset Manage and Package Solution.");
HotPatcherCoreMod.URL = TEXT("https://imzlp.com/posts/17590/");
HotPatcherCoreMod.UpdateURL = TEXT("https://github.com/hxhb/HotPatcher");
HotPatcherCoreMod.ModActions.Emplace(
TEXT("Patcher"),HOTPATCHEER_CORE_MODENAME,TEXT("ByRelease"), TEXT("Export Release ALL Asset Dependencies."),
CREATE_ACTION_WIDGET_LAMBDA(SHotPatcherReleaseWidget,TEXT("ByRelease")),
0
);
HotPatcherCoreMod.ModActions.Emplace(
TEXT("Patcher"),HOTPATCHEER_CORE_MODENAME,TEXT("ByPatch"), TEXT("Create an Patch form Release version."),
CREATE_ACTION_WIDGET_LAMBDA(SHotPatcherPatchWidget,TEXT("ByPatch")),
1
);
#if ENABLE_ORIGINAL_COOKER
HotPatcherCoreMod.ModActions.Emplace(
TEXT("Cooker"),TEXT("HotPatcherCore"),HOTPATCHEER_CORE_MODENAME,TEXT("Use single-process Cook Content(UE Default)"),
CREATE_ACTION_WIDGET_LAMBDA(SOriginalCookWidget,TEXT("ByOriginal")),
0
);
#endif
BuiltInMods.Add(HotPatcherCoreMod);
for(const auto& Mod:BuiltInMods)
{
RegisteHotPatcherMod(Mod);
}
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,15 @@
// Copyright 2019 Lipeng Zha, Inc. All Rights Reserved.
#include "HotPatcherCommands.h"
#define LOCTEXT_NAMESPACE "FHotPatcherModule"
void FHotPatcherCommands::RegisterCommands()
{
UI_COMMAND(PluginAction, "HotPatcher", "Hot Patch Manager", EUserInterfaceActionType::Button, FInputChord{});
UI_COMMAND(CookSelectedAction, "Cook Actions...", "Cook the selected assets", EUserInterfaceActionType::Button, FInputChord{});
UI_COMMAND(CookAndPakSelectedAction, "Cook And Pak Actions...", "Cook and Pak the selected assets", EUserInterfaceActionType::Button, FInputChord{});
UI_COMMAND(AddToPakSettingsAction, "Add To Patch Settings...", "Add the selected assets to Patch Settings", EUserInterfaceActionType::Button, FInputChord{});
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,693 @@
// Copyright 2019 Lipeng Zha, Inc. All Rights Reserved.
#include "HotPatcherEditor.h"
#include "HotPatcherStyle.h"
#include "HotPatcherCommands.h"
#include "SHotPatcher.h"
#include "HotPatcherSettings.h"
#include "Templates/HotPatcherTemplateHelper.hpp"
#include "Cooker/MultiCooker/SingleCookerProxy.h"
#include "CreatePatch/PatcherProxy.h"
#include "Cooker/MultiCooker/FlibHotCookerHelper.h"
#include "HotPatcherActionManager.h"
#include "FlibHotPatcherCoreHelper.h"
#include "FlibHotPatcherEditorHelper.h"
#include "HotPatcherLog.h"
// ENGINE HEADER
#include "AssetToolsModule.h"
#include "ContentBrowserModule.h"
#include "IContentBrowserSingleton.h"
#include "Misc/MessageDialog.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "DesktopPlatformModule.h"
#include "ISettingsModule.h"
#include "LevelEditor.h"
#include "HAL/FileManager.h"
#include "Interfaces/IPluginManager.h"
#include "Kismet/KismetTextLibrary.h"
#include "PakFileUtilities.h"
#include "Misc/EngineVersionComparison.h"
#if !UE_VERSION_OLDER_THAN(5,1,0)
typedef FAppStyle FEditorStyle;
#endif
#if ENGINE_MAJOR_VERSION < 5
#include "Widgets/Docking/SDockableTab.h"
#endif
#if WITH_EDITOR_SECTION
#include "ToolMenus.h"
#include "ToolMenuDelegates.h"
#include "ContentBrowserMenuContexts.h"
#include "CreatePatch/AssetActions/AssetTypeActions_PrimaryAssetLabel.h"
#endif
static const FName HotPatcherTabName("HotPatcher");
DEFINE_LOG_CATEGORY(LogHotPatcherEdotor)
#define LOCTEXT_NAMESPACE "FHotPatcherEditorModule"
void MakeProjectSettingsForHotPatcher()
{
if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
{
SettingsModule->RegisterSettings("Project", "Plugins", "Hot Patcher",
LOCTEXT("HotPatcherSettingsName", "Hot Patcher"),
LOCTEXT("HotPatcherSettingsDescroption", "Configure the HotPatcher plugin"),
GetMutableDefault<UHotPatcherSettings>());
}
}
FHotPatcherEditorModule& FHotPatcherEditorModule::Get()
{
FHotPatcherEditorModule& Module = FModuleManager::GetModuleChecked<FHotPatcherEditorModule>("HotPatcherEditor");
return Module;
}
void FHotPatcherEditorModule::StartupModule()
{
UE_LOG(LogHotPatcherEdotor,Display,TEXT("FHotPatcherEditorModule StartupModule"));
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
FHotPatcherStyle::Initialize();
FHotPatcherStyle::ReloadTextures();
FHotPatcherCommands::Register();
FHotPatcherActionManager::Get().Init();
#if !UE_VERSION_OLDER_THAN(5,1,0)
StyleSetName = FEditorStyle::GetAppStyleSetName().ToString();
#else
StyleSetName = FEditorStyle::GetStyleSetName().ToString();
#endif
FHotPatcherDelegates::Get().GetNotifyFileGenerated().AddLambda([](FText Msg,const FString& File)
{
UFlibHotPatcherEditorHelper::CreateSaveFileNotify(Msg,File);
});
if(::IsRunningCommandlet())
return;
PRAGMA_DISABLE_DEPRECATION_WARNINGS
FCoreUObjectDelegates::OnObjectSaved.AddRaw(this,&FHotPatcherEditorModule::OnObjectSaved);
PRAGMA_ENABLE_DEPRECATION_WARNINGS
MakeProjectSettingsForHotPatcher();
MissionNotifyProay = NewObject<UMissionNotificationProxy>();
MissionNotifyProay->AddToRoot();
PluginCommands = MakeShareable(new FUICommandList);
PluginCommands->MapAction(
FHotPatcherCommands::Get().PluginAction,
FExecuteAction::CreateRaw(this, &FHotPatcherEditorModule::PluginButtonClicked),
FCanExecuteAction());
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
{
TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender());
MenuExtender->AddMenuExtension("WindowLayout", EExtensionHook::After, PluginCommands, FMenuExtensionDelegate::CreateRaw(this, &FHotPatcherEditorModule::AddMenuExtension));
LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender);
#ifndef DISABLE_PLUGIN_TOOLBAR_MENU
// settings
TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender);
#if ENGINE_MAJOR_VERSION > 4
FName ExtensionHook = "Play";
#else
FName ExtensionHook = "Settings";
#endif
ToolbarExtender->AddToolBarExtension(ExtensionHook, EExtensionHook::After, PluginCommands, FToolBarExtensionDelegate::CreateRaw(this, &FHotPatcherEditorModule::AddToolbarExtension));
LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender);
#endif
}
TSharedRef<FUICommandList> CommandList = LevelEditorModule.GetGlobalLevelEditorActions();
CommandList->UnmapAction(FHotPatcherCommands::Get().CookSelectedAction);
CommandList->UnmapAction(FHotPatcherCommands::Get().CookAndPakSelectedAction);
CommandList->UnmapAction(FHotPatcherCommands::Get().AddToPakSettingsAction);
#if WITH_EDITOR_SECTION
ExtendContentBrowserAssetSelectionMenu();
ExtendContentBrowserPathSelectionMenu();
CreateRootMenu();
FAssetToolsModule::GetModule().Get().RegisterAssetTypeActions(MakeShareable(new FAssetTypeActions_PrimaryAssetLabel));
#endif
}
void FHotPatcherEditorModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
FHotPatcherActionManager::Get().Shutdown();
FHotPatcherCommands::Unregister();
FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(HotPatcherTabName);
FHotPatcherStyle::Shutdown();
}
void FHotPatcherEditorModule::OpenDockTab()
{
if(!DockTab.IsValid() || !GPatchSettings)
{
PluginButtonClicked();
}
}
void FHotPatcherEditorModule::PluginButtonClicked()
{
if (!DockTab.IsValid())
{
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(HotPatcherTabName, FOnSpawnTab::CreateRaw(this, &FHotPatcherEditorModule::OnSpawnPluginTab))
.SetDisplayName(LOCTEXT("FHotPatcherTabTitle", "HotPatcher"))
.SetMenuType(ETabSpawnerMenuType::Hidden);
}
FGlobalTabmanager::Get()->InvokeTab(HotPatcherTabName);
}
void FHotPatcherEditorModule::OnTabClosed(TSharedRef<SDockTab> InTab)
{
FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(HotPatcherTabName);
DockTab.Reset();
}
void FHotPatcherEditorModule::AddMenuExtension(FMenuBuilder& Builder)
{
Builder.AddMenuEntry(FHotPatcherCommands::Get().PluginAction);
}
void FHotPatcherEditorModule::AddToolbarExtension(FToolBarBuilder& Builder)
{
Builder.AddToolBarButton(FHotPatcherCommands::Get().PluginAction);
}
TArray<FAssetData> FHotPatcherEditorModule::GetSelectedAssetsInBrowserContent()
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
TArray<FAssetData> AssetsData;
ContentBrowserModule.Get().GetSelectedAssets(AssetsData);
return AssetsData;
}
TArray<FString> FHotPatcherEditorModule::GetSelectedFolderInBrowserContent()
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
TArray<FString> Folders;
ContentBrowserModule.Get().GetSelectedFolders(Folders);
return Folders;
}
TSharedRef<class SDockTab> FHotPatcherEditorModule::OnSpawnPluginTab(const class FSpawnTabArgs& InSpawnTabArgs)
{
return SAssignNew(DockTab,SDockTab)
.TabRole(ETabRole::NomadTab)
.Label(LOCTEXT("HotPatcherTab", "Hot Patcher"))
.ToolTipText(LOCTEXT("HotPatcherTabTextToolTip", "Hot Patcher"))
.OnTabClosed(SDockTab::FOnTabClosedCallback::CreateRaw(this,&FHotPatcherEditorModule::OnTabClosed))
.Clipping(EWidgetClipping::ClipToBounds)
[
SNew(SHotPatcher)
];
}
#if WITH_EDITOR_SECTION
void FHotPatcherEditorModule::CreateRootMenu()
{
UToolMenu* RootMenu = UToolMenus::Get()->ExtendMenu("ContentBrowser.AddNewContextMenu");
FToolMenuSection& RootSection = RootMenu->AddSection("HotPatcherUtilities", LOCTEXT("HotPatcherUtilities", "HotPatcherUtilities"));;
RootSection.AddSubMenu(
"PatcherPresetsActionsSubMenu",
LOCTEXT("PatcherPresetsActionsSubMenuLabel", "Preset Actions"),
LOCTEXT("PatcherPresetsActionsSubMenuToolTip", "Preset Actions"),
FNewToolMenuDelegate::CreateRaw(this, &FHotPatcherEditorModule::MakeHotPatcherPresetsActionsSubMenu),
FUIAction(
FExecuteAction()
),
EUserInterfaceActionType::Button,
false,
FSlateIcon(*StyleSetName, "ContentBrowser.SaveAllCurrentFolder")
);
}
void FHotPatcherEditorModule::CreateAssetContextMenu(FToolMenuSection& InSection)
{
InSection.AddSubMenu(
"CookActionsSubMenu",
LOCTEXT("CookActionsSubMenuLabel", "Cook Actions"),
LOCTEXT("CookActionsSubMenuToolTip", "Cook actions"),
FNewToolMenuDelegate::CreateRaw(this, &FHotPatcherEditorModule::MakeCookActionsSubMenu),
FUIAction(
FExecuteAction()
),
EUserInterfaceActionType::Button,
false,
FSlateIcon(*StyleSetName, "ContentBrowser.AssetActions")
);
InSection.AddSubMenu(
"CookAndPakActionsSubMenu",
LOCTEXT("CookAndPakActionsSubMenuLabel", "Cook And Pak Actions"),
LOCTEXT("CookAndPakActionsSubMenuToolTip", "Cook And Pak actions"),
FNewToolMenuDelegate::CreateRaw(this, &FHotPatcherEditorModule::MakeCookAndPakActionsSubMenu),
FUIAction(
FExecuteAction()
),
EUserInterfaceActionType::Button,
false,
FSlateIcon(*StyleSetName, "ContentBrowser.SaveAllCurrentFolder")
);
// InSection.AddMenuEntry(FHotPatcherCommands::Get().AddToPakSettingsAction);
InSection.AddDynamicEntry("AddToPatchSettings", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection)
{
const TAttribute<FText> Label = LOCTEXT("CookUtilities_AddToPatchSettings", "Add To Patch Settings");
const TAttribute<FText> ToolTip = LOCTEXT("CookUtilities_AddToPatchSettingsTooltip", "Add Selected Assets To HotPatcher Patch Settings");
const FSlateIcon Icon = FSlateIcon(*StyleSetName, "ContentBrowser.AssetActions.Duplicate");
const FToolMenuExecuteAction UIAction = FToolMenuExecuteAction::CreateRaw(this,&FHotPatcherEditorModule::OnAddToPatchSettings);
InSection.AddMenuEntry("CookUtilities_AddToPatchSettings", Label, ToolTip, Icon, UIAction);
}));
}
void FHotPatcherEditorModule::ExtendContentBrowserAssetSelectionMenu()
{
UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("ContentBrowser.AssetContextMenu");
FToolMenuSection& Section = Menu->FindOrAddSection(TEXT("AssetContextCookUtilities"));//Menu->FindOrAddSection("AssetContextReferences");
FToolMenuEntry& Entry = Section.AddDynamicEntry("AssetManagerEditorViewCommands", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection)
{
UContentBrowserAssetContextMenuContext* Context = InSection.FindContext<UContentBrowserAssetContextMenuContext>();
if (Context)
{
CreateAssetContextMenu(InSection);
}
}));
}
void FHotPatcherEditorModule::ExtendContentBrowserPathSelectionMenu()
{
#if ENGINE_MAJOR_VERSION > 4 || ENGINE_MINOR_VERSION > 24
UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("ContentBrowser.FolderContextMenu");
FToolMenuSection& Section = Menu->FindOrAddSection("PathContextCookUtilities");
FToolMenuEntry& Entry = Section.AddDynamicEntry("AssetManagerEditorViewCommands", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection)
{
UContentBrowserFolderContext* Context = InSection.FindContext<UContentBrowserFolderContext>();
if (Context)
{
CreateAssetContextMenu(InSection);
}
}));
#endif
}
void FHotPatcherEditorModule::MakeCookActionsSubMenu(UToolMenu* Menu)
{
FToolMenuSection& Section = Menu->AddSection("CookActionsSection");
for (auto Platform : GetAllowCookPlatforms())
{
Section.AddMenuEntry(
FName(*THotPatcherTemplateHelper::GetEnumNameByValue(Platform)),
FText::Format(LOCTEXT("Platform", "{0}"), UKismetTextLibrary::Conv_StringToText(THotPatcherTemplateHelper::GetEnumNameByValue(Platform))),
FText(),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateRaw(this, &FHotPatcherEditorModule::OnCookPlatform, Platform)
)
);
}
}
void FHotPatcherEditorModule::MakeCookAndPakActionsSubMenu(UToolMenu* Menu)
{
FToolMenuSection& Section = Menu->AddSection("CookAndPakActionsSection");
UHotPatcherSettings* Settings = GetMutableDefault<UHotPatcherSettings>();
Settings->ReloadConfig();
for (ETargetPlatform Platform : GetAllowCookPlatforms())
{
FString PlatformName = THotPatcherTemplateHelper::GetEnumNameByValue(Platform);
FToolMenuEntry& PlatformEntry = Section.AddSubMenu(FName(*PlatformName),
FText::Format(LOCTEXT("Platform", "{0}"), UKismetTextLibrary::Conv_StringToText(THotPatcherTemplateHelper::GetEnumNameByValue(Platform))),
FText(),
FNewMenuDelegate::CreateLambda([=](FMenuBuilder& SubMenuBuilder){
SubMenuBuilder.AddMenuEntry(
LOCTEXT("AnalysisDependencies", "AnalysisDependencies"), FText(),
FSlateIcon(*StyleSetName,TEXT("WorldBrowser.LevelsMenuBrush")),
FUIAction(FExecuteAction::CreateRaw(this, &FHotPatcherEditorModule::OnCookAndPakPlatform, Platform,true)), NAME_None, EUserInterfaceActionType::Button);
SubMenuBuilder.AddMenuEntry(
LOCTEXT("NoDependencies", "NoDependencies"), FText(),
FSlateIcon(*StyleSetName,TEXT("Level.SaveIcon16x")),
FUIAction(FExecuteAction::CreateRaw(this, &FHotPatcherEditorModule::OnCookAndPakPlatform, Platform,false)), NAME_None, EUserInterfaceActionType::Button);
}
));
// Section.AddMenuEntry(
// FName(*THotPatcherTemplateHelper::GetEnumNameByValue(Platform)),
// FText::Format(LOCTEXT("Platform", "{0}"), UKismetTextLibrary::Conv_StringToText(THotPatcherTemplateHelper::GetEnumNameByValue(Platform))),
// FText(),
// FSlateIcon(),
// FUIAction(
// FExecuteAction::CreateRaw(this, &FHotPatcherEditorModule::OnCookAndPakPlatform, Platform)
// )
//);
}
}
void FHotPatcherEditorModule::MakeHotPatcherPresetsActionsSubMenu(UToolMenu* Menu)
{
FToolMenuSection& Section = Menu->AddSection("HotPatcherPresetsActionsSection");
UHotPatcherSettings* Settings = GetMutableDefault<UHotPatcherSettings>();
Settings->ReloadConfig();
for (FExportPatchSettings PakConfig : Settings->PresetConfigs)
{
Section.AddSubMenu(FName(*PakConfig.VersionId),
FText::Format(LOCTEXT("PakExternal_VersionID", "{0}"), UKismetTextLibrary::Conv_StringToText(PakConfig.VersionId)),
FText(),
FNewMenuDelegate::CreateLambda([=](FMenuBuilder& SubMenuBuilder)
{
for (ETargetPlatform Platform : GetAllowCookPlatforms())
{
FString PlatformName = THotPatcherTemplateHelper::GetEnumNameByValue(Platform);
SubMenuBuilder.AddMenuEntry(
FText::Format(LOCTEXT("PakPresetPlatform", "{0}"), UKismetTextLibrary::Conv_StringToText(PlatformName)),
FText(),
FSlateIcon(*StyleSetName,TEXT("WorldBrowser.LevelsMenuBrush")),
FUIAction(FExecuteAction::CreateRaw(this, &FHotPatcherEditorModule::OnPakPreset,PakConfig, Platform)), NAME_None, EUserInterfaceActionType::Button);
}
}));
}
}
void FHotPatcherEditorModule::OnAddToPatchSettings(const FToolMenuContext& MenuContent)
{
OpenDockTab();
TArray<FAssetData> AssetsData = GetSelectedAssetsInBrowserContent();
TArray<FString> FolderList = GetSelectedFolderInBrowserContent();
TArray<FDirectoryPath> FolderDirectorys;
for(const auto& Folder:FolderList)
{
FDirectoryPath Path;
Path.Path = Folder;
FolderDirectorys.Add(Path);
}
TArray<FPatcherSpecifyAsset> AssetsSoftPath;
for(const auto& AssetData:AssetsData)
{
FPatcherSpecifyAsset PatchSettingAssetElement;
FSoftObjectPath AssetObjectPath;
AssetObjectPath.SetPath(AssetData.ObjectPath.ToString());
PatchSettingAssetElement.Asset = AssetObjectPath;
AssetsSoftPath.AddUnique(PatchSettingAssetElement);
}
GPatchSettings->GetAssetScanConfigRef().IncludeSpecifyAssets.Append(AssetsSoftPath);
GPatchSettings->GetAssetScanConfigRef().AssetIncludeFilters.Append(FolderDirectorys);
}
void FHotPatcherEditorModule::OnPakPreset(FExportPatchSettings Config)
{
TSharedPtr<FExportPatchSettings> TmpPatchSettings = MakeShareable(new FExportPatchSettings);
*TmpPatchSettings = Config;
CookAndPakByPatchSettings(TmpPatchSettings,TmpPatchSettings->IsStandaloneMode());
}
#endif
void FHotPatcherEditorModule::OnCookPlatform(ETargetPlatform Platform)
{
UHotPatcherSettings* Settings = GetMutableDefault<UHotPatcherSettings>();
Settings->ReloadConfig();
TArray<FAssetData> AssetsDatas = GetSelectedAssetsInBrowserContent();
TArray<FString> SelectedFolder = GetSelectedFolderInBrowserContent();
TArray<FAssetData> FolderAssetDatas;
UFlibAssetManageHelper::GetAssetDataInPaths(SelectedFolder,FolderAssetDatas);
AssetsDatas.Append(FolderAssetDatas);
TArray<FAssetDetail> AllSelectedAssetDetails;
for(const auto& AssetData:AssetsDatas)
{
AllSelectedAssetDetails.AddUnique(UFlibAssetManageHelper::GetAssetDetailByPackageName(AssetData.PackageName.ToString()));
}
auto CookNotifyLambda = [](const FString& PackageName,ETargetPlatform Platform,bool bSuccessed)
{
FString CookedDir = FPaths::ConvertRelativePathToFull(FPaths::Combine(FPaths::ProjectSavedDir(),TEXT("Cooked")));
// FString PackageName = UFlibAssetManageHelper::PackagePathToLongPackageName(PackagePath);
FString CookedSavePath = UFlibHotPatcherCoreHelper::GetAssetCookedSavePath(CookedDir,PackageName, THotPatcherTemplateHelper::GetEnumNameByValue(Platform));
auto Msg = FText::Format(
LOCTEXT("CookAssetsNotify", "Cook {0} for {1} {2}!"),
UKismetTextLibrary::Conv_StringToText(PackageName),
UKismetTextLibrary::Conv_StringToText(THotPatcherTemplateHelper::GetEnumNameByValue(Platform)),
bSuccessed ? UKismetTextLibrary::Conv_StringToText(TEXT("Successfuly")):UKismetTextLibrary::Conv_StringToText(TEXT("Faild"))
);
SNotificationItem::ECompletionState CookStatus = bSuccessed ? SNotificationItem::CS_Success:SNotificationItem::CS_Fail;
UFlibHotPatcherEditorHelper::CreateSaveFileNotify(Msg,CookedSavePath,CookStatus);
};
FSingleCookerSettings EmptySetting;
EmptySetting.MissionID = 0;
EmptySetting.MissionName = FString::Printf(TEXT("%s_Cooker_%d"),FApp::GetProjectName(),EmptySetting.MissionID);
EmptySetting.ShaderLibName = FString::Printf(TEXT("%s_Shader_%d"),FApp::GetProjectName(),EmptySetting.MissionID);
EmptySetting.CookTargetPlatforms = TArray<ETargetPlatform>{Platform};
EmptySetting.CookAssets = AllSelectedAssetDetails;
// EmptySetting.ShaderLibName = FApp::GetProjectName();
EmptySetting.bPackageTracker = false;
EmptySetting.ShaderOptions.bSharedShaderLibrary = false;
EmptySetting.IoStoreSettings.bIoStore = false;
EmptySetting.bSerializeAssetRegistry = false;
EmptySetting.bDisplayConfig = false;
EmptySetting.bForceCookInOneFrame = true;
EmptySetting.StorageCookedDir = FPaths::Combine(FPaths::ConvertRelativePathToFull(FPaths::ProjectSavedDir()),TEXT("Cooked"));
EmptySetting.StorageMetadataDir = FPaths::Combine(UFlibHotCookerHelper::GetCookerBaseDir(),EmptySetting.MissionName);
USingleCookerProxy* SingleCookerProxy = NewObject<USingleCookerProxy>();
SingleCookerProxy->AddToRoot();
SingleCookerProxy->Init(&EmptySetting);
SingleCookerProxy->OnAssetCooked.AddLambda([CookNotifyLambda](const FSoftObjectPath& ObjectPath,ETargetPlatform Platform,ESavePackageResult Result)
{
CookNotifyLambda(ObjectPath.GetAssetPathString(),Platform,Result == ESavePackageResult::Success);
});
bool bExportStatus = SingleCookerProxy->DoExport();
SingleCookerProxy->Shutdown();
}
void FHotPatcherEditorModule::OnPakPreset(FExportPatchSettings Config, ETargetPlatform Platform)
{
Config.PakTargetPlatforms.Empty();
Config.PakTargetPlatforms.Add(Platform);
OnPakPreset(Config);
}
void FHotPatcherEditorModule::CookAndPakByAssetsAndFilters(TArray<FPatcherSpecifyAsset> IncludeAssets,TArray<FDirectoryPath> IncludePaths,TArray<ETargetPlatform> Platforms,bool bForceStandalone)
{
FString Name = FDateTime::Now().ToString();
PatchSettings = MakeShareable(new FExportPatchSettings);
*PatchSettings = MakeTempPatchSettings(Name,IncludePaths,IncludeAssets,TArray<FPlatformExternAssets>{},Platforms,true);
CookAndPakByPatchSettings(PatchSettings,bForceStandalone);
}
void FHotPatcherEditorModule::CookAndPakByPatchSettings(TSharedPtr<FExportPatchSettings> InPatchSettings,bool bForceStandalone)
{
if(bForceStandalone || InPatchSettings->IsStandaloneMode())
{
FString CurrentConfig;
THotPatcherTemplateHelper::TSerializeStructAsJsonString(*InPatchSettings.Get(),CurrentConfig);
FString SaveConfigTo = FPaths::ConvertRelativePathToFull(FPaths::Combine(FPaths::ProjectSavedDir(),TEXT("HotPatcher"),FString::Printf(TEXT("%s_PatchConfig.json"),*InPatchSettings->VersionId)));
FFileHelper::SaveStringToFile(CurrentConfig,*SaveConfigTo);
FString MissionCommand = FString::Printf(TEXT("\"%s\" -run=HotPatcher -config=\"%s\" %s"),*UFlibPatchParserHelper::GetProjectFilePath(),*SaveConfigTo,*InPatchSettings->GetCombinedAdditionalCommandletArgs());
UE_LOG(LogHotPatcher,Log,TEXT("HotPatcher %s Mission: %s %s"),*InPatchSettings->VersionId,*UFlibHotPatcherCoreHelper::GetUECmdBinary(),*MissionCommand);
FText DisplayText = UKismetTextLibrary::Conv_StringToText(FString::Printf(TEXT("Packaging %s for %s..."),*InPatchSettings->VersionId,*UFlibHotPatcherCoreHelper::GetPlatformsStr(InPatchSettings->PakTargetPlatforms)));
RunProcMission(UFlibHotPatcherCoreHelper::GetUECmdBinary(),MissionCommand,InPatchSettings->VersionId,DisplayText);
}
else
{
UPatcherProxy* PatcherProxy = NewObject<UPatcherProxy>();
PatcherProxy->AddToRoot();
Proxys.Add(PatcherProxy);
PatcherProxy->Init(InPatchSettings.Get());
PatcherProxy->DoExport();
}
}
void FHotPatcherEditorModule::OnCookAndPakPlatform(ETargetPlatform Platform, bool bAnalysicDependencies)
{
UHotPatcherSettings* Settings = GetMutableDefault<UHotPatcherSettings>();
Settings->ReloadConfig();
TArray<FAssetData> AssetsDatas = GetSelectedAssetsInBrowserContent();
TArray<FString> SelectedFolder = GetSelectedFolderInBrowserContent();
TArray<FDirectoryPath> IncludePaths;
TArray<FPatcherSpecifyAsset> IncludeAssets;
for(auto& Folder:SelectedFolder)
{
FDirectoryPath CurrentPath;
if(Folder.StartsWith(TEXT("/All/")))
{
Folder.RemoveFromStart(TEXT("/All"));
}
CurrentPath.Path = Folder;
IncludePaths.Add(CurrentPath);
}
for(const auto& Asset:AssetsDatas)
{
FPatcherSpecifyAsset CurrentAsset;
CurrentAsset.Asset = Asset.ToSoftObjectPath();
CurrentAsset.bAnalysisAssetDependencies = bAnalysicDependencies;
CurrentAsset.AssetRegistryDependencyTypes.AddUnique(EAssetRegistryDependencyTypeEx::Packages);
IncludeAssets.AddUnique(CurrentAsset);
}
CookAndPakByAssetsAndFilters(IncludeAssets,IncludePaths,TArray<ETargetPlatform>{Platform});
}
void FHotPatcherEditorModule::OnObjectSaved(UObject* ObjectSaved)
{
if (GIsCookerLoadingPackage)
{
// This is the cooker saving a cooked package, ignore
return;
}
UPackage* Package = ObjectSaved->GetOutermost();
if (Package == nullptr || Package == GetTransientPackage())
{
return;
}
// Register the package filename as modified. We don't use the cache because the file may not exist on disk yet at this point
const FString PackageFilename = FPackageName::LongPackageNameToFilename(Package->GetName(), Package->ContainsMap() ? FPackageName::GetMapPackageExtension() : FPackageName::GetAssetPackageExtension());
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
AssetRegistryModule.Get().ScanFilesSynchronous(TArray<FString>{PackageFilename},false);
}
FExportPatchSettings FHotPatcherEditorModule::MakeTempPatchSettings(
const FString& Name,
const TArray<FDirectoryPath>& AssetIncludeFilters,
const TArray<FPatcherSpecifyAsset>& IncludeSpecifyAssets,
const TArray<FPlatformExternAssets>& ExternFiles,
const TArray<ETargetPlatform>& PakTargetPlatforms,
bool bCook
)
{
UHotPatcherSettings* Settings = GetMutableDefault<UHotPatcherSettings>();
Settings->ReloadConfig();
FExportPatchSettings TempSettings = Settings->TempPatchSetting;
TempSettings.VersionId = Name;
TempSettings.GetAssetScanConfigRef().AssetIncludeFilters = AssetIncludeFilters;
TempSettings.GetAssetScanConfigRef().IncludeSpecifyAssets = IncludeSpecifyAssets;
TempSettings.AddExternAssetsToPlatform = ExternFiles;
TempSettings.bCookPatchAssets = bCook;
TempSettings.PakTargetPlatforms = PakTargetPlatforms;
TempSettings.SavePath.Path = Settings->GetTempSavedDir();
return TempSettings;
}
TArray<ETargetPlatform> FHotPatcherEditorModule::GetAllCookPlatforms() const
{
TArray<ETargetPlatform> TargetPlatforms;//{ETargetPlatform::Android_ASTC,ETargetPlatform::IOS,ETargetPlatform::WindowsNoEditor};
#if ENGINE_MAJOR_VERSION > 4 || ENGINE_MINOR_VERSION > 21
UEnum* FoundEnum = StaticEnum<ETargetPlatform>();
#else
FString EnumTypeName = ANSI_TO_TCHAR(THotPatcherTemplateHelper::GetCPPTypeName<ETargetPlatform>().c_str());
UEnum* FoundEnum = FindObject<UEnum>(ANY_PACKAGE, *EnumTypeName, true);
#endif
if (FoundEnum)
{
for(int32 index =1;index < FoundEnum->GetMaxEnumValue();++index)
{
TargetPlatforms.AddUnique((ETargetPlatform)(index));
}
}
return TargetPlatforms;
}
TArray<ETargetPlatform> FHotPatcherEditorModule::GetAllowCookPlatforms() const
{
TArray<ETargetPlatform> results;
UHotPatcherSettings* Settings = GetMutableDefault<UHotPatcherSettings>();
Settings->ReloadConfig();
for (auto Platform : GetAllCookPlatforms())
{
if(Settings->bWhiteListCookInEditor && !Settings->PlatformWhitelists.Contains(Platform))
continue;
FString PlatformName = THotPatcherTemplateHelper::GetEnumNameByValue(Platform);
if(PlatformName.StartsWith(TEXT("All")))
continue;
results.AddUnique(Platform);
}
return results;
}
void FHotPatcherEditorModule::OnCookPlatformForExterner(ETargetPlatform Platform)
{
FHotPatcherEditorModule::Get().OnCookPlatform(Platform);
}
TSharedPtr<FProcWorkerThread> FHotPatcherEditorModule::RunProcMission(const FString& Bin, const FString& Command, const FString& MissionName, const FText& NotifyTextOverride)
{
if (mProcWorkingThread.IsValid() && mProcWorkingThread->GetThreadStatus()==EThreadStatus::Busy)
{
mProcWorkingThread->Cancel();
}
else
{
mProcWorkingThread = MakeShareable(new FProcWorkerThread(*FString::Printf(TEXT("PakPresetThread_%s"),*MissionName), Bin, Command));
mProcWorkingThread->ProcOutputMsgDelegate.BindUObject(MissionNotifyProay,&UMissionNotificationProxy::ReceiveOutputMsg);
mProcWorkingThread->ProcBeginDelegate.AddUObject(MissionNotifyProay,&UMissionNotificationProxy::SpawnRuningMissionNotification);
mProcWorkingThread->ProcSuccessedDelegate.AddUObject(MissionNotifyProay,&UMissionNotificationProxy::SpawnMissionSuccessedNotification);
mProcWorkingThread->ProcFaildDelegate.AddUObject(MissionNotifyProay,&UMissionNotificationProxy::SpawnMissionFaildNotification);
MissionNotifyProay->SetMissionName(*FString::Printf(TEXT("%s"),*MissionName));
FText DisplayText = NotifyTextOverride;
if(DisplayText.IsEmpty())
{
DisplayText = FText::FromString(FString::Printf(TEXT("%s in progress"),*MissionName));
}
MissionNotifyProay->SetMissionNotifyText(
DisplayText,
LOCTEXT("RunningCookNotificationCancelButton", "Cancel"),
FText::FromString(FString::Printf(TEXT("%s Mission Finished!"),*MissionName)),
FText::FromString(FString::Printf(TEXT("%s Failed!"),*MissionName))
);
MissionNotifyProay->MissionCanceled.AddLambda([this]()
{
if (mProcWorkingThread.IsValid() && mProcWorkingThread->GetThreadStatus() == EThreadStatus::Busy)
{
mProcWorkingThread->Cancel();
}
});
mProcWorkingThread->Execute();
}
return mProcWorkingThread;
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FHotPatcherEditorModule, HotPatcherEditor)

View File

@ -0,0 +1,11 @@
#include "HotPatcherModBaseModule.h"
void FHotPatcherModBaseModule::StartupModule()
{
FHotPatcherActionManager::Get().RegisteHotPatcherMod(GetModDesc());
}
void FHotPatcherModBaseModule::ShutdownModule()
{
FHotPatcherActionManager::Get().UnRegisteHotPatcherMod(GetModDesc());
}

View File

@ -0,0 +1,69 @@
// Copyright 2019 Lipeng Zha, Inc. All Rights Reserved.
#include "HotPatcherStyle.h"
#include "HotPatcherEditor.h"
#include "Framework/Application/SlateApplication.h"
#include "Styling/SlateStyleRegistry.h"
#include "Slate/SlateGameResources.h"
#include "Interfaces/IPluginManager.h"
TSharedPtr< FSlateStyleSet > FHotPatcherStyle::StyleInstance = NULL;
void FHotPatcherStyle::Initialize()
{
if (!StyleInstance.IsValid())
{
StyleInstance = Create();
FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance);
}
}
void FHotPatcherStyle::Shutdown()
{
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance);
ensure(StyleInstance.IsUnique());
StyleInstance.Reset();
}
FName FHotPatcherStyle::GetStyleSetName()
{
static FName StyleSetName(TEXT("HotPatcherStyle"));
return StyleSetName;
}
#define IMAGE_BRUSH( RelativePath, ... ) FSlateImageBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ )
#define BOX_BRUSH( RelativePath, ... ) FSlateBoxBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ )
#define BORDER_BRUSH( RelativePath, ... ) FSlateBorderBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ )
#define TTF_FONT( RelativePath, ... ) FSlateFontInfo( Style->RootToContentDir( RelativePath, TEXT(".ttf") ), __VA_ARGS__ )
#define OTF_FONT( RelativePath, ... ) FSlateFontInfo( Style->RootToContentDir( RelativePath, TEXT(".otf") ), __VA_ARGS__ )
const FVector2D Icon16x16(16.0f, 16.0f);
const FVector2D Icon20x20(20.0f, 20.0f);
const FVector2D Icon40x40(40.0f, 40.0f);
TSharedRef< FSlateStyleSet > FHotPatcherStyle::Create()
{
TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("HotPatcherStyle"));
Style->SetContentRoot(IPluginManager::Get().FindPlugin("HotPatcher")->GetBaseDir() / TEXT("Resources"));
Style->Set("HotPatcher.PluginAction", new IMAGE_BRUSH(TEXT("ButtonIcon_40x"), Icon40x40));
return Style;
}
#undef IMAGE_BRUSH
#undef BOX_BRUSH
#undef BORDER_BRUSH
#undef TTF_FONT
#undef OTF_FONT
void FHotPatcherStyle::ReloadTextures()
{
if (FSlateApplication::IsInitialized())
{
FSlateApplication::Get().GetRenderer()->ReloadTextureResources();
}
}
const ISlateStyle& FHotPatcherStyle::Get()
{
return *StyleInstance;
}

View File

@ -0,0 +1,130 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "MissionNotificationProxy.h"
#include "Async/Async.h"
#include "Framework/Notifications/NotificationManager.h"
#include "ThreadUtils/FProcWorkerThread.hpp"
#include "Widgets/Notifications/SNotificationList.h"
DEFINE_LOG_CATEGORY_STATIC(LogMissionNotificationProxy, All, All);
#define LOCTEXT_NAMESPACE "MissionNotificationPorxy"
UMissionNotificationProxy::UMissionNotificationProxy(const FObjectInitializer& Initializer):Super(Initializer)
{}
void UMissionNotificationProxy::SetMissionName(FName NewMissionName)
{
MissionName = NewMissionName; // TEXT("Cook");
}
void UMissionNotificationProxy::SetMissionNotifyText(const FText& RunningText, const FText& CancelText,
const FText& SuccessedText, const FText& FaildText)
{
// running
RunningNotifyText = RunningText; // LOCTEXT("CookNotificationInProgress", "Cook in progress");
// running cancel
RunningNofityCancelText = CancelText; // LOCTEXT("RunningCookNotificationCancelButton", "Cancel");
// mission successed
MissionSuccessedNotifyText = SuccessedText; // LOCTEXT("CookSuccessedNotification", "Cook Finished!");
// mission failed
MissionFailedNotifyText = FaildText; // LOCTEXT("CookFaildNotification", "Cook Faild!");
}
void UMissionNotificationProxy::ReceiveOutputMsg(FProcWorkerThread* Worker,const FString& InMsg)
{
FString FindItem(TEXT("Display:"));
int32 Index = InMsg.Len() - InMsg.Find(FindItem) - FindItem.Len();
if (InMsg.Contains(TEXT("Error:")))
{
UE_LOG(LogMissionNotificationProxy, Error, TEXT("%s"), *InMsg);
}
else if (InMsg.Contains(TEXT("Warning:")))
{
UE_LOG(LogMissionNotificationProxy, Warning, TEXT("%s"), *InMsg.Right(Index));
}
else
{
UE_LOG(LogMissionNotificationProxy, Display, TEXT("%s"), *InMsg);
}
}
void UMissionNotificationProxy::SpawnRuningMissionNotification(FProcWorkerThread* ProcWorker)
{
UMissionNotificationProxy* MissionProxy=this;
AsyncTask(ENamedThreads::GameThread, [MissionProxy]()
{
if (MissionProxy->PendingProgressPtr.IsValid())
{
MissionProxy->PendingProgressPtr.Pin()->ExpireAndFadeout();
}
FNotificationInfo Info(MissionProxy->RunningNotifyText);
Info.bFireAndForget = false;
Info.Hyperlink = FSimpleDelegate::CreateStatic([](){ FGlobalTabmanager::Get()->InvokeTab(FName("OutputLog")); });
Info.HyperlinkText = LOCTEXT("ShowOutputLogHyperlink", "Show Output Log");
Info.ButtonDetails.Add(FNotificationButtonInfo(MissionProxy->RunningNofityCancelText, FText(),
FSimpleDelegate::CreateLambda([MissionProxy]() {MissionProxy->CancelMission(); }),
SNotificationItem::CS_Pending
));
MissionProxy->PendingProgressPtr = FSlateNotificationManager::Get().AddNotification(Info);
MissionProxy->PendingProgressPtr.Pin()->SetCompletionState(SNotificationItem::CS_Pending);
MissionProxy->bRunning = true;
});
}
void UMissionNotificationProxy::SpawnMissionSuccessedNotification(FProcWorkerThread* ProcWorker)
{
UMissionNotificationProxy* MissionProxy=this;
AsyncTask(ENamedThreads::GameThread, [MissionProxy]() {
TSharedPtr<SNotificationItem> NotificationItem = MissionProxy->PendingProgressPtr.Pin();
if (NotificationItem.IsValid())
{
NotificationItem->SetText(MissionProxy->MissionSuccessedNotifyText);
NotificationItem->SetCompletionState(SNotificationItem::CS_Success);
NotificationItem->ExpireAndFadeout();
MissionProxy->PendingProgressPtr.Reset();
}
MissionProxy->bRunning = false;
UE_LOG(LogMissionNotificationProxy, Log, TEXT("The %s Mission is Successfuly."),*MissionProxy->MissionName.ToString());
});
}
void UMissionNotificationProxy::SpawnMissionFaildNotification(FProcWorkerThread* ProcWorker)
{
UMissionNotificationProxy* MissionProxy = this;
AsyncTask(ENamedThreads::GameThread, [MissionProxy]() {
TSharedPtr<SNotificationItem> NotificationItem = MissionProxy->PendingProgressPtr.Pin();
if (NotificationItem.IsValid())
{
NotificationItem->SetText(MissionProxy->MissionFailedNotifyText);
NotificationItem->SetCompletionState(SNotificationItem::CS_Fail);
NotificationItem->ExpireAndFadeout();
MissionProxy->PendingProgressPtr.Reset();
MissionProxy->bRunning = false;
UE_LOG(LogMissionNotificationProxy, Error, TEXT("The %s Mission is faild."),*MissionProxy->MissionName.ToString())
}
});
}
void UMissionNotificationProxy::CancelMission()
{
MissionCanceled.Broadcast();
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,114 @@
#include "SHotPatcher.h"
#include "CreatePatch/SPatchersPage.h"
#include "Cooker/SCookersPage.h"
#include "SVersionUpdater/SVersionUpdaterWidget.h"
// engine header
#include "HotPatcherCore.h"
#include "Widgets/Layout/SScrollBox.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Layout/SGridPanel.h"
#include "Widgets/Layout/SSeparator.h"
#include "Widgets/Notifications/SNotificationList.h"
#include "HAL/PlatformFile.h"
#include "Json.h"
#include "Model/FCookersModeContext.h"
#define LOCTEXT_NAMESPACE "SHotPatcher"
void SHotPatcher::Construct(const FArguments& InArgs)
{
TMap<FString,FHotPatcherCategory> HotPatcherCategorys;
HotPatcherCategorys.Add(TEXT("Cooker"),FHotPatcherCategory(TEXT("Cooker"),SNew(SCookersPage, MakeShareable(new FCookersModeContext))));
HotPatcherCategorys.Add(TEXT("Patcher"),FHotPatcherCategory(TEXT("Patcher"),SNew(SPatchersPage, MakeShareable(new FPatchersModeContext))));
HotPatcherCategorys.Append(FHotPatcherActionManager::Get().GetHotPatcherCategorys());
float HotPatcherVersion = FHotPatcherActionManager::Get().GetHotPatcherVersion();
ChildSlot
[
SNew(SOverlay)
+ SOverlay::Slot()
.HAlign(HAlign_Fill)
[
SNew(SScrollBox)
+ SScrollBox::Slot()
.Padding(0.0f, 10.0f, 8.0f, 0.0f)
[
SNew(SVerticalBox)
#if ENABLE_UPDATER_CHECK
+SVerticalBox::Slot()
.AutoHeight()
[
SAssignNew(VersionUpdaterWidget,SVersionUpdaterWidget)
.ToolName(FText::FromString(GToolName))
.ToolVersion(HotPatcherVersion)
.DeveloperName(FText::FromString(TEXT("lipengzha")))
.DeveloperWebsite(FText::FromString(TEXT("https://imzlp.com")))
.UpdateWebsite(FText::FromString(TEXT("https://imzlp.com/posts/17590/")))
.CurrentVersion(GToolMainVersion)
.PatchVersion(GToolPatchVersion)
]
#endif
+SVerticalBox::Slot()
.Padding(0,10,0,0)
.AutoHeight()
[
SAssignNew(ActionPageGridPanel,SGridPanel)
.FillColumn(1, 1.0f)
]
]
]
];
int32 RowNum = 0;
for(auto ActionPage:HotPatcherCategorys)
{
FString ActionCategoryName = ActionPage.Key;
if(TMap<FString,FHotPatcherAction>* ActionCategory = FHotPatcherActionManager::Get().GetHotPatcherActions().Find(ActionCategoryName))
{
bool bHasActiveActions = false;
for(auto Action:*ActionCategory)
{
bHasActiveActions = FHotPatcherActionManager::Get().IsActiveAction(Action.Key);
if(bHasActiveActions)
{
break;
}
}
if(bHasActiveActions)
{
ActionPageGridPanel->AddSlot(0,RowNum)
.Padding(8.0f, 10.0f, 0.0f, 0.0f)
.VAlign(VAlign_Top)
[
SNew(STextBlock)
.Font(FCoreStyle::GetDefaultFontStyle(*ActionCategoryName, 13))
.Text(UKismetTextLibrary::Conv_StringToText(ActionCategoryName))
];
ActionPageGridPanel->AddSlot(1, RowNum)
.Padding(25.0f, 0.0f, 8.0f, 0.0f)
[
ActionPage.Value.Widget
];
++RowNum;
if(RowNum < (HotPatcherCategorys.Num()*2-1))
{
ActionPageGridPanel->AddSlot(0, RowNum)
.ColumnSpan(3)
.Padding(0.0f, 16.0f)
[
SNew(SSeparator)
.Orientation(Orient_Horizontal)
];
++RowNum;
}
}
}
}
}
TSharedPtr<SNotificationList> SHotPatcher::GetNotificationListPtr() const
{
return NotificationListPtr;
}
#undef LOCTEXT_NAMESPACE

View File

@ -0,0 +1,35 @@
#pragma once
#include "HotPatcherEditor.h"
#include "CoreMinimal.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SCompoundWidget.h"
#include "SVersionUpdater/SVersionUpdaterWidget.h"
#include "Widgets/Layout/SGridPanel.h"
class SHotPatcher : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SHotPatcher)
{}
SLATE_END_ARGS()
public:
/**
* Construct the widget
*
* @param InArgs A declaration from which to construct the widget
*/
void Construct(const FArguments& InArgs);
TSharedPtr<SNotificationList> GetNotificationListPtr()const;
private:
/** The list of active system messages */
TSharedPtr<SNotificationList> NotificationListPtr;
// TArray<FHotPatcherCategory> CategoryPages;
TSharedPtr<SVersionUpdaterWidget> VersionUpdaterWidget;
TSharedPtr<SGridPanel> ActionPageGridPanel;
};

View File

@ -0,0 +1,49 @@
#include "SHotPatcherPageBase.h"
FReply SHotPatcherPageBase::DoImportConfig()const
{
if (GetActiveAction().IsValid())
{
GetActiveAction()->ImportConfig();
}
return FReply::Handled();
}
FReply SHotPatcherPageBase::DoImportProjectConfig() const
{
if (GetActiveAction().IsValid())
{
GetActiveAction()->ImportProjectConfig();
}
return FReply::Handled();
}
FReply SHotPatcherPageBase::DoExportConfig()const
{
if (GetActiveAction().IsValid())
{
GetActiveAction()->ExportConfig();
}
return FReply::Handled();
}
FReply SHotPatcherPageBase::DoResetConfig()const
{
if (GetActiveAction().IsValid())
{
GetActiveAction()->ResetConfig();
}
return FReply::Handled();
}
TSharedPtr<SHotPatcherWidgetInterface> SHotPatcherPageBase::GetActiveAction()const
{
TSharedPtr<SHotPatcherWidgetInterface> result;
if (GetContext().IsValid())
{
if(ActionWidgetMap.Contains(GetContext()->GetModeName()))
{
result = *ActionWidgetMap.Find(GetContext()->GetModeName());
}
}
return result;
}

View File

@ -0,0 +1,74 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
// #include "HotPatcherPrivatePCH.h"
#include "SHotPatcherWidgetBase.h"
#include "FlibHotPatcherCoreHelper.h"
#include "FlibPatchParserHelper.h"
#include "FHotPatcherVersion.h"
#include "FlibAssetManageHelper.h"
#include "FPakFileInfo.h"
// engine header
#include "IDesktopPlatform.h"
#include "DesktopPlatformModule.h"
#include "FlibHotPatcherEditorHelper.h"
#include "Misc/FileHelper.h"
#include "Widgets/Input/SHyperlink.h"
#include "Widgets/Layout/SSeparator.h"
#include "Widgets/Text/SMultiLineEditableText.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Misc/SecureHash.h"
#include "Misc/ScopedSlowTask.h"
#include "HAL/FileManager.h"
#include "Kismet/KismetTextLibrary.h"
#define LOCTEXT_NAMESPACE "SHotPatcherCreatePatch"
void SHotPatcherWidgetBase::Construct(const FArguments& InArgs, TSharedPtr<FHotPatcherContextBase> InContext)
{
SetContext(InContext);
// InitMissionNotificationProxy();
}
void SHotPatcherWidgetBase::ImportProjectConfig()
{
// import uasset
UFlibHotPatcherCoreHelper::ImportProjectSettingsToScannerConfig(GetConfigSettings()->GetAssetScanConfigRef());
UFlibHotPatcherCoreHelper::ImportProjectNotAssetDir(GetConfigSettings()->GetAddExternAssetsToPlatform(),ETargetPlatform::AllPlatforms);
}
FText SHotPatcherWidgetInterface::GetGenerateTooltipText() const
{
return UKismetTextLibrary::Conv_StringToText(TEXT(""));
}
TArray<FString> SHotPatcherWidgetInterface::OpenFileDialog()const
{
TArray<FString> SelectedFiles;
SelectedFiles = UFlibHotPatcherEditorHelper::OpenFileDialog(
LOCTEXT("OpenHotPatchConfigDialog", "Open .json").ToString(),
FString(TEXT("")),
TEXT(""),
TEXT("HotPatcher json (*.json)|*.json"),
EFileDialogFlags::None
);
return SelectedFiles;
}
TArray<FString> SHotPatcherWidgetInterface::SaveFileDialog()const
{
TArray<FString> SaveFilenames;
SaveFilenames = UFlibHotPatcherEditorHelper::SaveFileDialog(
LOCTEXT("SvedHotPatcherConfig", "Save .json").ToString(),
FString(TEXT("")),
TEXT(""),
TEXT("HotPatcher json (*.json)|*.json"),
EFileDialogFlags::None
);
return SaveFilenames;
}
#undef LOCTEXT_NAMESPACE

Some files were not shown because too many files have changed in this diff Show More