添加热更
This commit is contained in:
commit
242fb71511
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Binaries
|
||||||
|
Intermediate
|
44
HotPatcher/HotPatcher.uplugin
Normal file
44
HotPatcher/HotPatcher.uplugin
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
BIN
HotPatcher/Resources/ButtonIcon_40x.png
Normal file
BIN
HotPatcher/Resources/ButtonIcon_40x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.3 KiB |
BIN
HotPatcher/Resources/Icon128.png
Normal file
BIN
HotPatcher/Resources/Icon128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 128 KiB |
BIN
HotPatcher/Resources/Payments/alipay.png
Normal file
BIN
HotPatcher/Resources/Payments/alipay.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
BIN
HotPatcher/Resources/Payments/wechatpay.png
Normal file
BIN
HotPatcher/Resources/Payments/wechatpay.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 144 KiB |
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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 );
|
@ -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;
|
||||||
|
};
|
51
HotPatcher/Source/CmdHandler/CmdHandler.Build.cs
Normal file
51
HotPatcher/Source/CmdHandler/CmdHandler.Build.cs
Normal 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 ...
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
149
HotPatcher/Source/CmdHandler/Private/CmdHandler.cpp
Normal file
149
HotPatcher/Source/CmdHandler/Private/CmdHandler.cpp
Normal 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)
|
17
HotPatcher/Source/CmdHandler/Public/CmdHandler.h
Normal file
17
HotPatcher/Source/CmdHandler/Public/CmdHandler.h
Normal 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;
|
||||||
|
};
|
@ -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); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -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;
|
||||||
|
}
|
@ -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"); }
|
||||||
|
};
|
@ -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;
|
||||||
|
}
|
@ -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"); }
|
||||||
|
};
|
@ -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;
|
||||||
|
}
|
@ -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"); }
|
||||||
|
};
|
@ -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;
|
||||||
|
}
|
@ -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;
|
||||||
|
};
|
@ -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;
|
||||||
|
}
|
@ -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;
|
||||||
|
};
|
@ -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;
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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;
|
||||||
|
};
|
198
HotPatcher/Source/HotPatcherCore/HotPatcherCore.Build.cs
Normal file
198
HotPatcher/Source/HotPatcherCore/HotPatcherCore.Build.cs
Normal 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\""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
@ -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
|
@ -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
80
HotPatcher/Source/HotPatcherCore/Private/HotPatcherCore.cpp
Normal file
80
HotPatcher/Source/HotPatcherCore/Private/HotPatcherCore.cpp
Normal 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)
|
@ -0,0 +1,8 @@
|
|||||||
|
#include "HotPatcherDelegates.h"
|
||||||
|
|
||||||
|
FHotPatcherDelegates& FHotPatcherDelegates::Get()
|
||||||
|
{
|
||||||
|
// return the singleton object
|
||||||
|
static FHotPatcherDelegates Singleton;
|
||||||
|
return Singleton;
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
@ -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;
|
||||||
|
};
|
@ -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()
|
||||||
|
};
|
@ -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;
|
||||||
|
};
|
@ -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;
|
||||||
|
};
|
@ -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);
|
||||||
|
};
|
@ -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;
|
||||||
|
};
|
@ -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
|
@ -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;
|
||||||
|
};
|
@ -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;
|
||||||
|
};
|
@ -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;
|
||||||
|
};
|
@ -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;
|
||||||
|
};
|
@ -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;
|
||||||
|
};
|
@ -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);
|
||||||
|
};
|
31
HotPatcher/Source/HotPatcherCore/Public/HotPatcherCore.h
Normal file
31
HotPatcher/Source/HotPatcherCore/Public/HotPatcherCore.h
Normal 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; }
|
||||||
|
};
|
@ -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);
|
||||||
|
};
|
79
HotPatcher/Source/HotPatcherCore/Public/HotPatcherSettings.h
Normal file
79
HotPatcher/Source/HotPatcherCore/Public/HotPatcherSettings.h
Normal 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
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
@ -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;
|
||||||
|
};
|
@ -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;
|
||||||
|
|
||||||
|
};
|
108
HotPatcher/Source/HotPatcherEditor/HotPatcherEditor.Build.cs
Normal file
108
HotPatcher/Source/HotPatcherEditor/HotPatcherEditor.Build.cs
Normal 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"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
};
|
@ -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
|
@ -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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
@ -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
|
@ -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;
|
||||||
|
};
|
||||||
|
|
@ -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
|
@ -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;
|
||||||
|
};
|
||||||
|
|
@ -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
|
@ -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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
@ -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
|
@ -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
|
@ -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;
|
||||||
|
};
|
@ -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
|
@ -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
|
@ -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;
|
||||||
|
|
||||||
|
};
|
@ -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
|
@ -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;
|
||||||
|
};
|
@ -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
|
@ -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
|
||||||
|
|
@ -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
|
@ -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));
|
||||||
|
}
|
@ -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
|
@ -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
|
@ -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
|
@ -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;
|
||||||
|
}
|
@ -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
|
@ -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
|
693
HotPatcher/Source/HotPatcherEditor/Private/HotPatcherEditor.cpp
Normal file
693
HotPatcher/Source/HotPatcherEditor/Private/HotPatcherEditor.cpp
Normal 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)
|
@ -0,0 +1,11 @@
|
|||||||
|
#include "HotPatcherModBaseModule.h"
|
||||||
|
|
||||||
|
void FHotPatcherModBaseModule::StartupModule()
|
||||||
|
{
|
||||||
|
FHotPatcherActionManager::Get().RegisteHotPatcherMod(GetModDesc());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FHotPatcherModBaseModule::ShutdownModule()
|
||||||
|
{
|
||||||
|
FHotPatcherActionManager::Get().UnRegisteHotPatcherMod(GetModDesc());
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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
|
114
HotPatcher/Source/HotPatcherEditor/Private/SHotPatcher.cpp
Normal file
114
HotPatcher/Source/HotPatcherEditor/Private/SHotPatcher.cpp
Normal 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
|
35
HotPatcher/Source/HotPatcherEditor/Private/SHotPatcher.h
Normal file
35
HotPatcher/Source/HotPatcherEditor/Private/SHotPatcher.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
@ -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;
|
||||||
|
}
|
@ -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
Loading…
Reference in New Issue
Block a user