问题:editor 下如何调试 pak 的 mount 流程?
- mount pak
一般只要如下调用就可以:
FPakPlatformFile* PakPlatform = (FPakPlatformFile*)(FPlatformFileManager::Get().FindPlatformFile(FPakPlatformFile::GetTypeName()));
if (PakPlatform)
{
PakPlatform->Mount(*pakPath, PakOrder)
}
- 但有两种情况下,PakPlatform 会为 Null
1,当打包时(package)时没有选 -pak 并且项目的 content 目录下没有 pak 文件
2,editor 模式下 - 解决方法
第一种情况好解决,打包时加上 -pak 参数就行(我在这被坑了很久);第二种情况就麻烦点,需要手动将 PakPlatform 插入责任链条中,大概的代码如下:
FPakPlatformFile* HotPatchServer::CheckAndInitPakPlatform()
{
FPakPlatformFile* HandlePakPlatform = (FPakPlatformFile*)(FPlatformFileManager::Get().FindPlatformFile(FPakPlatformFile::GetTypeName()));
if (!HandlePakPlatform)
{
UE_LOG(LogTemp, Warning, TEXT("CheckAndInitPakPlatform FPakPlatformFile == NULL"));
// 方便 Editor 调试 mount 流程
#if WITH_EDITOR
if (EnabledEditorHotPatch)
{
IPlatformFileModule* PlatformFileModule = FModuleManager::LoadModulePtr<IPlatformFileModule>(FPakPlatformFile::GetTypeName());
if (PlatformFileModule != NULL)
{
HandlePakPlatform = (FPakPlatformFile*)(PlatformFileModule->GetPlatformFile());
IPlatformFile& TopmostPlatformFile = FPlatformFileManager::Get().GetPlatformFile();
const TCHAR* Name = TEXT("");
for (IPlatformFile* ChainElement = &TopmostPlatformFile; ChainElement; ChainElement = ChainElement->GetLowerLevel())
{
UE_LOG(LogTemp, Warning, TEXT("CheckAndInitPakPlatform 寻找合适插入位置 Name = %s"), ChainElement->GetName());
if (ChainElement->GetLowerLevel() == NULL)
{
HandlePakPlatform->Initialize(ChainElement, TEXT(""));
HandlePakPlatform->InitializeAfterSetActive();
Name = ChainElement->GetName();
break;
}
}
for (IPlatformFile* ChainElement = &TopmostPlatformFile; ChainElement; ChainElement = ChainElement->GetLowerLevel())
{
UE_LOG(LogTemp, Warning, TEXT("CheckAndInitPakPlatform 寻找被插队的节点 Name = %s"), ChainElement->GetName());
if (ChainElement->GetLowerLevel() != NULL && FCString::Stricmp(ChainElement->GetLowerLevel()->GetName(), Name) == 0 && FCString::Stricmp(FPakPlatformFile::GetTypeName(), Name) != 0)
{
UE_LOG(LogTemp, Warning, TEXT("CheckAndInitPakPlatform 寻找到了需要插入的节点 Name = %s OldGetLowerLevel = %s NewLowerLevel = %s"), ChainElement->GetName(), ChainElement->GetName(), HandlePakPlatform->GetName());
ChainElement->SetLowerLevel(HandlePakPlatform);
break;
}
}
if (TopmostPlatformFile.GetLowerLevel() == NULL)
{
UE_LOG(LogTemp, Warning, TEXT("CheckAndInitPakPlatform 没寻找到被插队的节点 HandlePakPlatform 被放在最上层!"));
FPlatformFileManager::Get().SetPlatformFile(*HandlePakPlatform);
}
}
}
#endif
}
else
{
UE_LOG(LogTemp, Warning, TEXT("CheckAndInitPakPlatform FPakPlatformFile is OK"));
}
return HandlePakPlatform;
}
问题:editor 挂载(mount)加密后的 pak 报错?
-
pak 为何要加密?
一般正式上线时,为了资源不被轻松破解,一般都会选择将 pak 加密。
-
如何加密?
如下图所示:
-
那为啥在打出的包能正常解密,而在 editor 下却会失败呢?
一开始我也很疑惑,经过多次查找下来,在 Engine/Source/Programs/UnrealBuildTool/Configuration/TargetRules.cs 中发现了如下代码:
具体模版内容为:
以上可以总结为:在打包时(编译项目时),会根据项目的设置,自动生成对应的解密代码。
-
editor 下如何解密?
其实也比较简单,就像上面的代码一样,加一个解密的 callback 就可以了,具体的代码如下:
bool bEncryptPakIndex;
bool bEncryptPakIniFiles;
bool bEncryptUAssetFiles;
bool bEncryptAllAssetFiles;
FString GSetting = FString::Printf(TEXT("%sDefaultCrypto.ini"), *FPaths::SourceConfigDir());
GConfig->GetBool(TEXT("/Script/CryptoKeys.CryptoKeysSettings"), TEXT("bEncryptPakIndex"), bEncryptPakIndex, GSetting);
GConfig->GetBool(TEXT("/Script/CryptoKeys.CryptoKeysSettings"), TEXT("bEncryptPakIniFiles"), bEncryptPakIniFiles, GSetting);
GConfig->GetBool(TEXT("/Script/CryptoKeys.CryptoKeysSettings"), TEXT("bEncryptUAssetFiles"), bEncryptUAssetFiles, GSetting);
GConfig->GetBool(TEXT("/Script/CryptoKeys.CryptoKeysSettings"), TEXT("bEncryptAllAssetFiles"), bEncryptAllAssetFiles, GSetting);
if (bEncryptPakIndex || bEncryptPakIniFiles || bEncryptUAssetFiles || bEncryptAllAssetFiles) {
FString EncryptionKey;
GConfig->GetString(TEXT("/Script/CryptoKeys.CryptoKeysSettings"), TEXT("EncryptionKey"), EncryptionKey, GSetting);
UE_LOG(LogTemp, Warning, TEXT("CheckAndInitPakPlatform EncryptionKey = %s"), &EncryptionKey);
FCoreDelegates::GetPakEncryptionKeyDelegate().BindLambda([EncryptionKey](uint8* key) {
TArray<uint8> KeyBase64Array;
FBase64::Decode(EncryptionKey, KeyBase64Array);
FMemory::Memcpy(key, KeyBase64Array.GetData(), FAES::FAESKey::KeySize);
});
}
结合上面的代码:
FPakPlatformFile* HotPatchServer::CheckAndInitPakPlatform()
{
FPakPlatformFile* HandlePakPlatform = (FPakPlatformFile*)(FPlatformFileManager::Get().FindPlatformFile(FPakPlatformFile::GetTypeName()));
if (!HandlePakPlatform)
{
UE_LOG(LogTemp, Warning, TEXT("CheckAndInitPakPlatform FPakPlatformFile == NULL"));
// 方便 Editor 调试 mount 流程
#if WITH_EDITOR
if (EnabledEditorHotPatch)
{
bool bEncryptPakIndex;
bool bEncryptPakIniFiles;
bool bEncryptUAssetFiles;
bool bEncryptAllAssetFiles;
FString GSetting = FString::Printf(TEXT("%sDefaultCrypto.ini"), *FPaths::SourceConfigDir());
GConfig->GetBool(TEXT("/Script/CryptoKeys.CryptoKeysSettings"), TEXT("bEncryptPakIndex"), bEncryptPakIndex, GSetting);
GConfig->GetBool(TEXT("/Script/CryptoKeys.CryptoKeysSettings"), TEXT("bEncryptPakIniFiles"), bEncryptPakIniFiles, GSetting);
GConfig->GetBool(TEXT("/Script/CryptoKeys.CryptoKeysSettings"), TEXT("bEncryptUAssetFiles"), bEncryptUAssetFiles, GSetting);
GConfig->GetBool(TEXT("/Script/CryptoKeys.CryptoKeysSettings"), TEXT("bEncryptAllAssetFiles"), bEncryptAllAssetFiles, GSetting);
if (bEncryptPakIndex || bEncryptPakIniFiles || bEncryptUAssetFiles || bEncryptAllAssetFiles) {
FString EncryptionKey;
GConfig->GetString(TEXT("/Script/CryptoKeys.CryptoKeysSettings"), TEXT("EncryptionKey"), EncryptionKey, GSetting);
UE_LOG(LogTemp, Warning, TEXT("CheckAndInitPakPlatform EncryptionKey = %s"), &EncryptionKey);
FCoreDelegates::GetPakEncryptionKeyDelegate().BindLambda([EncryptionKey](uint8* key) {
TArray<uint8> KeyBase64Array;
FBase64::Decode(EncryptionKey, KeyBase64Array);
FMemory::Memcpy(key, KeyBase64Array.GetData(), FAES::FAESKey::KeySize);
});
}
IPlatformFileModule* PlatformFileModule = FModuleManager::LoadModulePtr<IPlatformFileModule>(FPakPlatformFile::GetTypeName());
if (PlatformFileModule != NULL)
{
HandlePakPlatform = (FPakPlatformFile*)(PlatformFileModule->GetPlatformFile());
IPlatformFile& TopmostPlatformFile = FPlatformFileManager::Get().GetPlatformFile();
const TCHAR* Name = TEXT("");
for (IPlatformFile* ChainElement = &TopmostPlatformFile; ChainElement; ChainElement = ChainElement->GetLowerLevel())
{
UE_LOG(LogTemp, Warning, TEXT("CheckAndInitPakPlatform 寻找合适插入位置 Name = %s"), ChainElement->GetName());
if (ChainElement->GetLowerLevel() == NULL)
{
HandlePakPlatform->Initialize(ChainElement, TEXT(""));
HandlePakPlatform->InitializeAfterSetActive();
Name = ChainElement->GetName();
break;
}
}
for (IPlatformFile* ChainElement = &TopmostPlatformFile; ChainElement; ChainElement = ChainElement->GetLowerLevel())
{
UE_LOG(LogTemp, Warning, TEXT("CheckAndInitPakPlatform 寻找被插队的节点 Name = %s"), ChainElement->GetName());
if (ChainElement->GetLowerLevel() != NULL && FCString::Stricmp(ChainElement->GetLowerLevel()->GetName(), Name) == 0 && FCString::Stricmp(FPakPlatformFile::GetTypeName(), Name) != 0)
{
UE_LOG(LogTemp, Warning, TEXT("CheckAndInitPakPlatform 寻找到了需要插入的节点 Name = %s OldGetLowerLevel = %s NewLowerLevel = %s"), ChainElement->GetName(), ChainElement->GetName(), HandlePakPlatform->GetName());
ChainElement->SetLowerLevel(HandlePakPlatform);
break;
}
}
if (TopmostPlatformFile.GetLowerLevel() == NULL)
{
UE_LOG(LogTemp, Warning, TEXT("CheckAndInitPakPlatform 没寻找到被插队的节点 HandlePakPlatform 被放在最上层!"));
FPlatformFileManager::Get().SetPlatformFile(*HandlePakPlatform);
}
}
}
#endif
}
else
{
UE_LOG(LogTemp, Warning, TEXT("CheckAndInitPakPlatform FPakPlatformFile is OK"));
}
return HandlePakPlatform;
}