Files
UnrealEngine/Engine/Source/Editor/UnrealEd/Private/Commandlets/WorldPartitionDataLayerToAssetCommandlet.cpp
2025-05-18 13:04:45 +08:00

757 lines
29 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
UDataLayerToAssetCommandlet.cpp: Commandlet used to convert a partitioned ULevel's data layers to assets
=============================================================================*/
#include "Commandlets/WorldPartitionDataLayerToAssetCommandLet.h"
#include "Algo/Copy.h"
#include "Algo/Accumulate.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "AssetRegistry/IAssetRegistry.h"
#include "AssetToolsModule.h"
#include "DataLayer/DataLayerFactory.h"
#include "Engine/World.h"
#include "Logging/LogVerbosity.h"
#include "ProfilingDebugging/ScopedTimers.h"
#include "WorldPartition/DataLayer/DataLayerInstanceWithAsset.h"
#include "WorldPartition/DataLayer/WorldDataLayers.h"
#include "WorldPartition/WorldPartitionActorDescInstance.h"
#include "WorldPartition/WorldPartitionHelpers.h"
DEFINE_LOG_CATEGORY(LogDataLayerToAssetCommandlet);
void UDataLayerConversionInfo::SetDataLayerToConvert(const UDeprecatedDataLayerInstance* InDataLayerToConvert)
{
DataLayerToConvert = InDataLayerToConvert;
DataLayerAsset->DataLayerType = DataLayerToConvert->GetType();
DataLayerAsset->DebugColor = DataLayerToConvert->GetDebugColor();
}
void UDataLayerConversionInfo::SetDataLayerInstance(UDataLayerInstanceWithAsset* InDataLayerInstance)
{
DataLayerInstance = InDataLayerInstance;
if (DataLayerToConvert != nullptr)
{
DataLayerInstance->DataLayerAsset = DataLayerAsset;
DataLayerInstance->bIsVisible = DataLayerToConvert->bIsVisible;
DataLayerInstance->bIsInitiallyVisible = DataLayerToConvert->bIsInitiallyVisible;
DataLayerInstance->bIsInitiallyLoadedInEditor = DataLayerToConvert->bIsInitiallyLoadedInEditor;
DataLayerInstance->bIsLocked = DataLayerToConvert->bIsLocked;
DataLayerInstance->InitialRuntimeState = DataLayerToConvert->InitialRuntimeState;
}
else
{
// Check the DataLayerInstance was already properly converted
check(DataLayerInstance->DataLayerAsset == DataLayerAsset)
}
}
UDataLayerToAssetCommandletContext::UDataLayerToAssetCommandletContext(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
UDataLayerConversionInfo* UDataLayerToAssetCommandletContext::GetDataLayerConversionInfo(const UDeprecatedDataLayerInstance* DataLayer) const
{
const TObjectPtr<UDataLayerConversionInfo>* Entry = DataLayerConversionInfo.FindByPredicate([&DataLayer](const TObjectPtr<UDataLayerConversionInfo>& Other) { return DataLayer == Other->DataLayerToConvert; });
return Entry != nullptr ? Entry->Get() : nullptr;
}
UDataLayerConversionInfo* UDataLayerToAssetCommandletContext::GetDataLayerConversionInfo(const UDataLayerAsset* DataLayerAsset) const
{
const TObjectPtr<UDataLayerConversionInfo>* Entry = DataLayerConversionInfo.FindByPredicate([&DataLayerAsset](const TObjectPtr<UDataLayerConversionInfo>& Other) { return DataLayerAsset == Other->DataLayerAsset; });
return Entry != nullptr ? Entry->Get() : nullptr;
}
UDataLayerConversionInfo* UDataLayerToAssetCommandletContext::GetDataLayerConversionInfo(const UDataLayerInstanceWithAsset* DataLayerInstance) const
{
const TObjectPtr<UDataLayerConversionInfo>* Entry = DataLayerConversionInfo.FindByPredicate([&DataLayerInstance](const TObjectPtr<UDataLayerConversionInfo>& Other) { return DataLayerInstance == Other->DataLayerInstance; });
return Entry != nullptr ? Entry->Get() : nullptr;
}
UDataLayerConversionInfo* UDataLayerToAssetCommandletContext::GetDataLayerConversionInfo(const FActorDataLayer& ActorDataLayer) const
{
const TObjectPtr<UDataLayerConversionInfo>* Entry = DataLayerConversionInfo.FindByPredicate([&ActorDataLayer](const TObjectPtr<UDataLayerConversionInfo>& Other)
{
return Other->DataLayerToConvert != nullptr && ActorDataLayer.Name == Other->DataLayerToConvert->GetDataLayerFName();
});
return Entry != nullptr ? Entry->Get() : nullptr;
}
UDataLayerConversionInfo* UDataLayerToAssetCommandletContext::StoreExistingDataLayer(FAssetData& AssetData)
{
UDataLayerConversionInfo* ConversionInfo = NewObject<UDataLayerConversionInfo>();
ConversionInfo->DataLayerAsset = CastChecked<UDataLayerAsset>(AssetData.GetAsset());
UE_LOG(LogDataLayerToAssetCommandlet, Verbose, TEXT("Data Layer Asset %s discovered."),
*ConversionInfo->DataLayerAsset->GetFullName());
return DataLayerConversionInfo.Add_GetRef(ConversionInfo).Get();
}
UDataLayerConversionInfo* UDataLayerToAssetCommandletContext::StoreDataLayerAssetConversion(const UDeprecatedDataLayerInstance* DataLayerToConvert, UDataLayerAsset* NewDataLayerAsset)
{
if (UDataLayerConversionInfo* ConversionInfo = GetDataLayerConversionInfo(DataLayerToConvert))
{
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Failed to assign asset %s to data Layer %s. The data layer is already associated to asset %s."),
*NewDataLayerAsset->GetFullName() , *DataLayerToConvert->GetDataLayerShortName() , *ConversionInfo->DataLayerAsset->GetFullName());
return nullptr;
}
UDataLayerConversionInfo* ConversionInfo = GetDataLayerConversionInfo(NewDataLayerAsset);
if (ConversionInfo != nullptr)
{
if (ConversionInfo->DataLayerToConvert != nullptr)
{
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Failed to assign asset %s to data Layer %s. The asset is already associated to data layer %s."),
*NewDataLayerAsset->GetFullName(), *DataLayerToConvert->GetDataLayerShortName(), *ConversionInfo->DataLayerToConvert->GetDataLayerShortName());
return nullptr;
}
}
else
{
ConversionInfo = NewObject<UDataLayerConversionInfo>();
ConversionInfo->DataLayerAsset = NewDataLayerAsset;
DataLayerConversionInfo.Add(ConversionInfo);
}
ConversionInfo->SetDataLayerToConvert(DataLayerToConvert);
UE_LOG(LogDataLayerToAssetCommandlet, Log, TEXT("Data Layer Asset %s is associated to Data Layer %s for conversion"),
*ConversionInfo->DataLayerAsset->GetFullName(), *ConversionInfo->DataLayerToConvert->GetDataLayerShortName());
return ConvertingDataLayerInfo.Add_GetRef(ConversionInfo).Get();
}
UDataLayerConversionInfo* UDataLayerToAssetCommandletContext::StoreDataLayerInstanceConversion(const UDataLayerAsset* DataLayerAsset, UDataLayerInstanceWithAsset* NewDataLayerInstance)
{
if (UDataLayerConversionInfo* ConversionInfo = GetDataLayerConversionInfo(NewDataLayerInstance))
{
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Failed to assign asset %s to data layer instance %s. The instance is already associated to asset %s."),
*DataLayerAsset->GetFullName(), *NewDataLayerInstance->GetDataLayerFName().ToString(), *ConversionInfo->DataLayerAsset->GetFullName());
return nullptr;
}
if (UDataLayerConversionInfo* ConversionInfo = GetDataLayerConversionInfo(DataLayerAsset))
{
check(ConversionInfo->DataLayerInstance == nullptr);
ConversionInfo->SetDataLayerInstance(NewDataLayerInstance);
UE_LOG(LogDataLayerToAssetCommandlet, Log, TEXT("Data Layer Instance %s is associated to Data Layer Asset %s for conversion"),
*ConversionInfo->DataLayerInstance->GetDataLayerFName().ToString(), *ConversionInfo->DataLayerAsset->GetFullName());
return ConversionInfo;
}
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Failed to assign asset %s to data layer instance %s. The asset was not retrieved in conversion data."),
*DataLayerAsset->GetFullName(), *NewDataLayerInstance->GetDataLayerFName().ToString());
return nullptr;
}
bool UDataLayerToAssetCommandletContext::SetPreviousConversions(UDataLayerConversionInfo* CurrentConversion, TArray<TWeakObjectPtr<UDataLayerConversionInfo>>&& PreviousConversions)
{
check(CurrentConversion->CurrentConvertingInfo == nullptr);
check(CurrentConversion->PreviousConversionsInfo.IsEmpty());
CurrentConversion->PreviousConversionsInfo = MoveTemp(PreviousConversions);
uint32 ErrorCount = 0;
for (TWeakObjectPtr<UDataLayerConversionInfo>& PreviousConversion : CurrentConversion->PreviousConversionsInfo)
{
check(PreviousConversion->CurrentConvertingInfo == nullptr);
check(PreviousConversion->PreviousConversionsInfo.IsEmpty());
if (PreviousConversion->DataLayerInstance != nullptr)
{
ErrorCount++;
UE_LOG(LogDataLayerToAssetCommandlet, Error,
TEXT("DataLayer %s was already converted but is still to be converted. Re-Sync Data to a clean conversion or pre-conversion state and re-run the commandlet"),
*CurrentConversion->DataLayerAsset->GetFullName());
}
PreviousConversion->CurrentConvertingInfo = CurrentConversion;
}
ConvertingDataLayerInfo.AddUnique(CurrentConversion);
return ErrorCount == 0;
}
bool UDataLayerToAssetCommandletContext::FindDataLayerConversionInfos(FName DataLayerAssetName, TArray<TWeakObjectPtr<UDataLayerConversionInfo>>& OutConversionInfos) const
{
OutConversionInfos.Empty();
FString SanitizedAssetName = DataLayerAssetName.ToString();
const TCHAR* InvalidPackageChar = INVALID_LONGPACKAGE_CHARACTERS;
while (*InvalidPackageChar)
{
SanitizedAssetName.ReplaceCharInline(*InvalidPackageChar, TCHAR('_'), ESearchCase::CaseSensitive);
++InvalidPackageChar;
}
for (TObjectPtr<UDataLayerConversionInfo> const& ConversionInfo : DataLayerConversionInfo)
{
if (ConversionInfo->DataLayerAsset->GetFName() == FName(SanitizedAssetName))
{
OutConversionInfos.Add(ConversionInfo.Get());
}
}
return !OutConversionInfos.IsEmpty();
}
void UDataLayerToAssetCommandletContext::LogConversionInfos() const
{
if (LogDataLayerToAssetCommandlet.GetVerbosity() >= ELogVerbosity::Verbose)
{
for (const TObjectPtr<UDataLayerConversionInfo>& info : DataLayerConversionInfo)
{
FString ConflictingConversionString = TEXT("");
if(!info->PreviousConversionsInfo.IsEmpty())
{
ConflictingConversionString = FString::JoinBy(info->PreviousConversionsInfo, TEXT(", "), [](const TWeakObjectPtr<UDataLayerConversionInfo>& ConflictingInfo)
{
return ConflictingInfo->DataLayerAsset->GetFullName();
});
}
UE_LOG(LogDataLayerToAssetCommandlet, Verbose, TEXT("[Conversion Info] Data Layer %s\t\tData Layer Asset: %s\t\t\t\t\t\tData Layer Instance: %s\t\tConverting By: %s\t\t\t\t\t\tConflicting Previous Conversion: %s"),
info->DataLayerToConvert != nullptr ? *info->DataLayerToConvert->GetDataLayerShortName() : TEXT("None"),
info->DataLayerAsset != nullptr ? *info->DataLayerAsset->GetFullName() : TEXT("None"),
info->DataLayerInstance != nullptr ? *info->DataLayerInstance->GetDataLayerFName().ToString() : TEXT("None"),
info->CurrentConvertingInfo != nullptr ? *info->CurrentConvertingInfo->DataLayerAsset->GetFullName() : TEXT("None"),
*ConflictingConversionString);
}
}
}
UDataLayerToAssetCommandlet::UDataLayerToAssetCommandlet(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer),
DataLayerFactory(NewObject<UDataLayerFactory>())
{}
int32 UDataLayerToAssetCommandlet::Main(const FString& Params)
{
TRACE_CPUPROFILER_EVENT_SCOPE(UWorldPartitionConvertCommandlet::Main);
UE_SCOPED_TIMER(TEXT("Data Layer Conversion"), LogDataLayerToAssetCommandlet, Display);
FPackageSourceControlHelper PackageHelper;
ON_SCOPE_EXIT
{
if (MainWorld != nullptr)
{
const bool bBroadcastWorldDestroyedEvent = false;
MainWorld->DestroyWorld(bBroadcastWorldDestroyedEvent);
}
};
TArray<FString> Tokens, Switches;
TMap<FString, FString> CommandLineParams;
ParseCommandLine(*Params, Tokens, Switches, CommandLineParams);
if (!InitializeFromCommandLine(Tokens, Switches, CommandLineParams))
{
return EReturnCode::CommandletInitializationError;
}
ConversionFolder = DestinationFolder + "/" + MainWorld->GetName() + "/";
TStrongObjectPtr<UDataLayerToAssetCommandletContext> Context(NewObject<UDataLayerToAssetCommandletContext>());
if (!BuildConversionInfos(Context, PackageHelper))
{
return EReturnCode::DataLayerConversionError;
}
if (!ResolvePreviousConversionsToCurrent(Context))
{
return EReturnCode::DataLayerConversionError;
}
Context->LogConversionInfos();
if (!CreateDataLayerInstances(Context))
{
return EReturnCode::DataLayerConversionError;
}
if (!RemapActorDataLayersToAssets(Context, PackageHelper))
{
return EReturnCode::ActorDataLayerRemappingError;
}
if (!PerformProjectSpecificConversions(Context))
{
return EReturnCode::ProjectSpecificConversionError;
}
if (!DeletePreviousConversionsData(Context, PackageHelper))
{
return EReturnCode::DataLayerConversionError;
}
if (!CommitConversion(Context, PackageHelper))
{
return EReturnCode::DataLayerConversionError;
}
return EReturnCode::Success;
}
bool UDataLayerToAssetCommandlet::InitializeFromCommandLine(TArray<FString>& Tokens, TArray<FString> const& Switches, TMap<FString, FString> const& Params)
{
constexpr TCHAR DestinationFolderSwitch[] = TEXT("DestinationFolder");
if (FString const* DestFolderPtr = Params.Find(DestinationFolderSwitch))
{
DestinationFolder = *DestFolderPtr;
}
else
{
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("No \"%s\" switch specified"), DestinationFolderSwitch);
return false;
}
if (Switches.Contains(TEXT("Verbose")))
{
LogDataLayerToAssetCommandlet.SetVerbosity(ELogVerbosity::Verbose);
WorldPartitionCommandletHelpers::LogWorldPartitionCommandletUtils.SetVerbosity(ELogVerbosity::Verbose);
}
bPerformSavePackages = Switches.Contains(TEXT("NoSave")) == false;
bIgnoreActorLoadingErrors = Switches.Contains(TEXT("IgnoreActorLoadingErrors"));
MainWorld = WorldPartitionCommandletHelpers::LoadAndInitWorld(Tokens[0]);
if (!MainWorld)
{
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Failed to Load %s, Conversion will abort"), *Tokens[0]);
return false;
}
return true;
}
bool UDataLayerToAssetCommandlet::BuildConversionInfos(TStrongObjectPtr<UDataLayerToAssetCommandletContext>& CommandletContext, FPackageSourceControlHelper& PackageHelper)
{
UE_SCOPED_TIMER(TEXT("Retrieving Already Converted Data Layers"), LogDataLayerToAssetCommandlet, Display);
TArray<FAssetData> ExistingDataLayerAssets;
IAssetRegistry& AssetRegistry = FAssetRegistryModule::GetRegistry();
AssetRegistry.GetAssetsByClass(FTopLevelAssetPath(UDataLayerAsset::StaticClass()), ExistingDataLayerAssets);
for (FAssetData& AssetData : ExistingDataLayerAssets)
{
if (IsAssetInConversionFolder(AssetData.GetSoftObjectPath()))
{
CommandletContext->StoreExistingDataLayer(AssetData);
}
}
AWorldDataLayers* WorldDataLayers = MainWorld->GetWorldDataLayers();
TArray<UDataLayerInstance*> DataLayerInstances;
Algo::Copy(WorldDataLayers->DataLayerInstances, DataLayerInstances);
for (int32 i = DataLayerInstances.Num() -1; i >= 0; --i)
{
if(UDataLayerInstanceWithAsset* DataLayerInstance = Cast<UDataLayerInstanceWithAsset>(DataLayerInstances[i]))
{
if (const UDataLayerAsset* DataLayerAsset = DataLayerInstance->GetAsset())
{
if (UDataLayerConversionInfo* ConversionInfo = CommandletContext->GetDataLayerConversionInfo(DataLayerAsset))
{
CommandletContext->StoreDataLayerInstanceConversion(ConversionInfo->DataLayerAsset, DataLayerInstance);
}
}
}
}
uint32 ErrorCount = 0;
for (const UDataLayerInstance* DataLayerInstance : WorldDataLayers->DataLayerInstances)
{
if (const UDeprecatedDataLayerInstance* DataLayerToConvert = Cast<UDeprecatedDataLayerInstance>(DataLayerInstance))
{
if (!CreateConversionFromDataLayer(CommandletContext, DataLayerToConvert, PackageHelper))
{
ErrorCount++;
}
}
}
return ErrorCount == 0;
}
bool UDataLayerToAssetCommandlet::CreateConversionFromDataLayer(TStrongObjectPtr<UDataLayerToAssetCommandletContext>& CommandletContext, const UDeprecatedDataLayerInstance* DataLayer, FPackageSourceControlHelper &PackageHelper)
{
if(TObjectPtr<UDataLayerAsset> DataLayerAsset = GetOrCreateDataLayerAssetForConversion(CommandletContext, FName(DataLayer->GetDataLayerShortName())))
{
if (UDataLayerConversionInfo* ConversionInfo = CommandletContext->StoreDataLayerAssetConversion(DataLayer, DataLayerAsset))
{
if (bPerformSavePackages)
{
return WorldPartitionCommandletHelpers::CheckoutSaveAdd(ConversionInfo->DataLayerAsset->GetPackage(), PackageHelper);
}
return true;
}
}
return false;
}
TObjectPtr<UDataLayerAsset> UDataLayerToAssetCommandlet::GetOrCreateDataLayerAssetForConversion(TStrongObjectPtr<UDataLayerToAssetCommandletContext>& CommandletContext, FName DataLayerAssetName)
{
TObjectPtr<UDataLayerAsset> DataLayerAsset = nullptr;
TArray<TWeakObjectPtr<UDataLayerConversionInfo>> ConversionInfos;
if (CommandletContext->FindDataLayerConversionInfos(DataLayerAssetName, ConversionInfos))
{
for (TWeakObjectPtr<UDataLayerConversionInfo>& ConversionInfo : ConversionInfos)
{
if (IsAssetInConversionFolder(ConversionInfo->DataLayerAsset))
{
check(DataLayerAsset == nullptr);
return CastChecked<UDataLayerAsset>(ConversionInfo->DataLayerAsset);
}
}
}
UE_LOG(LogDataLayerToAssetCommandlet, Log, TEXT("Creating new Data Layer Asset %s in folder %s"),
*DataLayerAssetName.ToString(), *ConversionFolder);
FString SanitizedAssetName = DataLayerAssetName.ToString();
const TCHAR* InvalidPackageChar = INVALID_LONGPACKAGE_CHARACTERS;
while (*InvalidPackageChar)
{
SanitizedAssetName.ReplaceCharInline(*InvalidPackageChar, TCHAR('_'), ESearchCase::CaseSensitive);
++InvalidPackageChar;
}
IAssetTools& AssetTools = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools").Get();
if (UObject* Asset = AssetTools.CreateAsset(SanitizedAssetName, ConversionFolder, UDataLayerAsset::StaticClass(), DataLayerFactory))
{
DataLayerAsset = CastChecked<UDataLayerAsset>(Asset);
}
else
{
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Failed to create asset for %s in folder %s. Consult log for more details"),
*DataLayerAssetName.ToString(), *ConversionFolder);
}
return DataLayerAsset;
}
bool UDataLayerToAssetCommandlet::ResolvePreviousConversionsToCurrent(TStrongObjectPtr<UDataLayerToAssetCommandletContext>& CommandletContext)
{
UE_SCOPED_TIMER(TEXT("Resolving Conflicting Conversions"), LogDataLayerToAssetCommandlet, Display);
UE_LOG(LogDataLayerToAssetCommandlet, Log, TEXT("Resolving Conflicting Conversions"));
uint32 ErrorCount = 0;
for (const TObjectPtr<UDataLayerConversionInfo>& ConversionInfo : CommandletContext->GetDataLayerConversionInfos())
{
if(IsAssetInConversionFolder(ConversionInfo->DataLayerAsset))
{
TArray<TWeakObjectPtr<UDataLayerConversionInfo>> PreviousConversionInfos;
if (CommandletContext->FindDataLayerConversionInfos(ConversionInfo->DataLayerAsset->GetFName(), PreviousConversionInfos))
{
PreviousConversionInfos.Remove(ConversionInfo);
if (!PreviousConversionInfos.IsEmpty())
{
if (!CommandletContext->SetPreviousConversions(ConversionInfo, MoveTemp(PreviousConversionInfos)))
{
ErrorCount++;
}
}
}
}
}
return ErrorCount == 0;
}
bool UDataLayerToAssetCommandlet::RemapActorDataLayersToAssets(TStrongObjectPtr<UDataLayerToAssetCommandletContext>& CommandletContext, FPackageSourceControlHelper& PackageHelper)
{
UE_SCOPED_TIMER(TEXT("Remapping Actors Data Layers"), LogDataLayerToAssetCommandlet, Display);
UE_LOG(LogDataLayerToAssetCommandlet, Log, TEXT("Starting Actor Data Layer Remapping To Data Layer Asset. This can take a while."));
uint32 ErrorCount = 0;
FWorldPartitionHelpers::ForEachActorWithLoading(MainWorld->GetWorldPartition(), [&ErrorCount, &CommandletContext, this, &PackageHelper](const FWorldPartitionActorDescInstance* ActorDescInstance)
{
uint32 ActorConversionErrors = 0;
if (AActor* Actor = ActorDescInstance->GetActor())
{
ActorConversionErrors += RemapDataLayersAssetsFromPreviousConversions(CommandletContext, Actor);
ActorConversionErrors += RemapActorDataLayers(CommandletContext, Actor);
if (!PerformAdditionalActorConversions(CommandletContext, Actor))
{
ActorConversionErrors++;
}
if (ActorConversionErrors == 0 && bPerformSavePackages)
{
if (bPerformSavePackages && Actor->GetExternalPackage()->IsDirty())
{
if (!WorldPartitionCommandletHelpers::CheckoutSaveAdd(Actor->GetExternalPackage(), PackageHelper))
{
ActorConversionErrors++;
}
}
}
}
else
{
const FDataLayerInstanceNames& ActDescDataLayers = ActorDescInstance->GetDataLayerInstanceNames();
if (!ActDescDataLayers.IsEmpty())
{
FString DataLayerString = FString::JoinBy(ActDescDataLayers.ToArray(), TEXT(", "), [](const FName& DataLayerName) { return DataLayerName.ToString(); });
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Actor %s failed to load. Its data layers %s will not be remapped to a data layer asset."),
*ActorDescInstance->GetActorNameString(), *DataLayerString);
if (!bIgnoreActorLoadingErrors)
{
ActorConversionErrors++;
}
}
}
ErrorCount += ActorConversionErrors;
return true;
});
UE_LOG(LogDataLayerToAssetCommandlet, Log, TEXT("Actor Data Layer Remapping To Data Layer Asset Done."));
return ErrorCount == 0;
}
uint32 UDataLayerToAssetCommandlet::RemapActorDataLayers(TStrongObjectPtr<UDataLayerToAssetCommandletContext>& CommandletContext, AActor* Actor)
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
uint32 ErrorCount = 0;
const TArray<FActorDataLayer>& ActorDataLayers = Actor->GetActorDataLayers();
for (int32 i = ActorDataLayers.Num() - 1; i >= 0; --i)
{
const FActorDataLayer& ActorDataLayer = ActorDataLayers[i];
if (UDataLayerConversionInfo* DataLayerConversionInfo = CommandletContext->GetDataLayerConversionInfo(ActorDataLayer))
{
UE_LOG(LogDataLayerToAssetCommandlet, Verbose, TEXT("Data layer %s in Actor %s remapped to data layer asset %s"),
*ActorDataLayer.Name.ToString(), *Actor->GetName(), *DataLayerConversionInfo->DataLayerAsset->GetName());
if (Actor->AddDataLayer(DataLayerConversionInfo->DataLayerInstance))
{
if (!Actor->RemoveDataLayer(DataLayerConversionInfo->DataLayerToConvert))
{
ErrorCount++;
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Failed to remove data layer %s in Actor %s"),
*DataLayerConversionInfo->DataLayerToConvert->GetDataLayerShortName(), *Actor->GetName());
}
}
else
{
ErrorCount++;
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Failed to add data layer asset %s in Actor %s"),
*DataLayerConversionInfo->DataLayerAsset->GetFullName(), *Actor->GetName());
}
}
else
{
ErrorCount++;
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Actor %s is referencing data layer %s but it does not match any data layer asset."),
*Actor->GetName(), *ActorDataLayer.Name.ToString());
}
}
return ErrorCount;
PRAGMA_ENABLE_DEPRECATION_WARNINGS
}
// Multiple run of the commandlet can lead to Actors referencing Data Layer Assets in different folders. Always remap to the newly created asset.
uint32 UDataLayerToAssetCommandlet::RemapDataLayersAssetsFromPreviousConversions(TStrongObjectPtr<UDataLayerToAssetCommandletContext>& CommandletContext, AActor* Actor)
{
uint32 ErrorCount = 0;
TArray<const UDataLayerAsset*> ActorDataLayerAssets = Actor->GetDataLayerAssets();
for (int32 i = ActorDataLayerAssets.Num() - 1; i >= 0; --i)
{
const TObjectPtr<const UDataLayerAsset>& ActorDataLayerAsset = ActorDataLayerAssets[i];
if (UDataLayerConversionInfo* DataLayerConversionInfo = CommandletContext->GetDataLayerConversionInfo(ActorDataLayerAsset.Get()))
{
if (DataLayerConversionInfo->IsAPreviousConversion())
{
if (!DataLayerConversionInfo->GetCurrentConversion()->DataLayerInstance->AddActor(Actor))
{
ErrorCount++;
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Failed to replace data layer asset %s in Actor %s"),
*DataLayerConversionInfo->GetCurrentConversion()->DataLayerAsset->GetFullName(), *Actor->GetName());
}
}
}
}
return ErrorCount;
}
bool UDataLayerToAssetCommandlet::CreateDataLayerInstances(TStrongObjectPtr<UDataLayerToAssetCommandletContext>& CommandletContext)
{
UE_SCOPED_TIMER(TEXT("Creating Data Layer Instances"), LogDataLayerToAssetCommandlet, Display);
AWorldDataLayers* WorldDataLayers = MainWorld->GetWorldDataLayers();
for (const TWeakObjectPtr<UDataLayerConversionInfo>& ConvertingInfo : CommandletContext->GetConvertingDataLayerConversionInfo())
{
if (ConvertingInfo->DataLayerInstance == nullptr)
{
UDataLayerInstanceWithAsset* DataLayerInstance = WorldDataLayers->CreateDataLayer<UDataLayerInstanceWithAsset>(ConvertingInfo->DataLayerAsset);
UE_LOG(LogDataLayerToAssetCommandlet, Log, TEXT("Created new Data Layer Instance %s"),
*DataLayerInstance->GetDataLayerFName().ToString());
CommandletContext->StoreDataLayerInstanceConversion(ConvertingInfo->DataLayerAsset, DataLayerInstance);
}
WorldDataLayers->DeprecatedDataLayerNameToDataLayerInstance.Add(ConvertingInfo->DataLayerToConvert->GetDataLayerFName(), ConvertingInfo->DataLayerInstance);
}
uint32 ErrorCount = 0;
if (!RebuildDataLayerHierarchies(CommandletContext))
{
ErrorCount++;
}
return ErrorCount == 0;
}
bool UDataLayerToAssetCommandlet::RebuildDataLayerHierarchies(TStrongObjectPtr<UDataLayerToAssetCommandletContext>& CommandletContext)
{
UE_SCOPED_TIMER(TEXT("Creating Data Layer Instances Hierarchy"), LogDataLayerToAssetCommandlet, Display);
uint32 ErrorCount = 0;
for (const TWeakObjectPtr<UDataLayerConversionInfo>& ConvertingInfo : CommandletContext->GetConvertingDataLayerConversionInfo())
{
UDataLayerInstance* Child = ConvertingInfo->DataLayerInstance;
if (Child == nullptr)
{
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Failed to retrieve data layer instance when re-building hiearchy for %s"),
*ConvertingInfo->DataLayerAsset->GetFullName());
ErrorCount++;
continue;
}
const UDataLayerInstance* OldParent = ConvertingInfo->DataLayerToConvert->GetParent();
if (OldParent == nullptr)
{
// No Parent, continue
continue;
}
const UDeprecatedDataLayerInstance* DeprecatedParent = Cast<UDeprecatedDataLayerInstance>(OldParent);
if (DeprecatedParent == nullptr)
{
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Deprecated Data Layer Instance %s has %s as a parent. But %s is not depcrated. This is not permitted"),
*Child->GetDataLayerFullName(), *OldParent->GetDataLayerShortName(), *OldParent->GetDataLayerShortName());
ErrorCount++;
continue;
}
UDataLayerConversionInfo* ParentConversionInfo = CommandletContext->GetDataLayerConversionInfo(DeprecatedParent);
if (ParentConversionInfo == nullptr)
{
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Failed to find conversion info for parent %s of %s while rebuilding data layer hierarchy"),
*DeprecatedParent->GetDataLayerFullName(), *Child->GetDataLayerShortName());
ErrorCount++;
continue;
}
if (UDataLayerInstance* NewParent = ParentConversionInfo->DataLayerInstance)
{
if (!Child->SetParent(NewParent))
{
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Failed to set %s as the parent of %s"),
*NewParent->GetDataLayerFullName(), *Child->GetDataLayerShortName());
ErrorCount++;
}
}
else
{
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Failed to retrieve data layer instance for parent %s when re-building hiearchy for %s"),
*DeprecatedParent->GetDataLayerFullName(), *Child->GetDataLayerShortName());
ErrorCount++;
}
}
return ErrorCount == 0;
}
bool UDataLayerToAssetCommandlet::DeletePreviousConversionsData(TStrongObjectPtr<UDataLayerToAssetCommandletContext>& CommandletContext, FPackageSourceControlHelper& PackageHelper)
{
UE_SCOPED_TIMER(TEXT("Delete Conflicting Assets"), LogDataLayerToAssetCommandlet, Display);
uint32 ErrorCount = 0;
for (const TWeakObjectPtr<UDataLayerConversionInfo>& ConvertingInfo : CommandletContext->GetConvertingDataLayerConversionInfo())
{
for (const TWeakObjectPtr<UDataLayerConversionInfo>& PreviousConversion : ConvertingInfo->GetPreviousConversions())
{
if (bPerformSavePackages)
{
if (WorldPartitionCommandletHelpers::Delete(PreviousConversion->DataLayerAsset->GetPackage(), PackageHelper))
{
PreviousConversion->DataLayerAsset = nullptr;
}
else
{
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Failed to delete Data Layer Asset %s from previous conversion. (Conflicting with %s)"),
*PreviousConversion->DataLayerAsset->GetFullName(), *ConvertingInfo->DataLayerAsset->GetFullName());
ErrorCount++;
}
}
}
}
return ErrorCount == 0;
}
bool UDataLayerToAssetCommandlet::CommitConversion(TStrongObjectPtr<UDataLayerToAssetCommandletContext>& CommandletContext, FPackageSourceControlHelper& PackageHelper)
{
uint32 ErrorCount = 0;
AWorldDataLayers* WorldDataLayers = MainWorld->GetWorldDataLayers();
for (const TWeakObjectPtr<UDataLayerConversionInfo>& ConversionInfo : CommandletContext->GetConvertingDataLayerConversionInfo())
{
if (ConversionInfo->DataLayerToConvert != nullptr)
{
// Remove directly from DataLayerInstances as RemoveDataLayer method also cleans DeprecatedDataLayerNameToDataLayerInstance which is used for runtime conversion
if (WorldDataLayers->DataLayerInstances.Remove(ConversionInfo->DataLayerToConvert))
{
UE_LOG(LogDataLayerToAssetCommandlet, Log, TEXT("Deleted old data layer %s, it is now converted to Data Layer Asset %s and Data Layer Instance %s"),
*ConversionInfo->DataLayerToConvert->GetDataLayerShortName(), *ConversionInfo->DataLayerAsset->GetFullName(), *ConversionInfo->DataLayerInstance->GetDataLayerFName().ToString());
}
else
{
UE_LOG(LogDataLayerToAssetCommandlet, Error, TEXT("Failed to delete converting data layer %s."),
*ConversionInfo->DataLayerToConvert->GetDataLayerShortName());
ErrorCount++;
}
}
}
if (ErrorCount == 0 && bPerformSavePackages)
{
if (!WorldPartitionCommandletHelpers::CheckoutSaveAdd(WorldDataLayers->GetExternalPackage(), PackageHelper))
{
return false;
}
}
return true;
}
bool UDataLayerToAssetCommandlet::IsAssetInConversionFolder(const FSoftObjectPath& DataLayerAsset)
{
return DataLayerAsset.GetAssetPathString().StartsWith(ConversionFolder);
}