// 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* Entry = DataLayerConversionInfo.FindByPredicate([&DataLayer](const TObjectPtr& Other) { return DataLayer == Other->DataLayerToConvert; }); return Entry != nullptr ? Entry->Get() : nullptr; } UDataLayerConversionInfo* UDataLayerToAssetCommandletContext::GetDataLayerConversionInfo(const UDataLayerAsset* DataLayerAsset) const { const TObjectPtr* Entry = DataLayerConversionInfo.FindByPredicate([&DataLayerAsset](const TObjectPtr& Other) { return DataLayerAsset == Other->DataLayerAsset; }); return Entry != nullptr ? Entry->Get() : nullptr; } UDataLayerConversionInfo* UDataLayerToAssetCommandletContext::GetDataLayerConversionInfo(const UDataLayerInstanceWithAsset* DataLayerInstance) const { const TObjectPtr* Entry = DataLayerConversionInfo.FindByPredicate([&DataLayerInstance](const TObjectPtr& Other) { return DataLayerInstance == Other->DataLayerInstance; }); return Entry != nullptr ? Entry->Get() : nullptr; } UDataLayerConversionInfo* UDataLayerToAssetCommandletContext::GetDataLayerConversionInfo(const FActorDataLayer& ActorDataLayer) const { const TObjectPtr* Entry = DataLayerConversionInfo.FindByPredicate([&ActorDataLayer](const TObjectPtr& Other) { return Other->DataLayerToConvert != nullptr && ActorDataLayer.Name == Other->DataLayerToConvert->GetDataLayerFName(); }); return Entry != nullptr ? Entry->Get() : nullptr; } UDataLayerConversionInfo* UDataLayerToAssetCommandletContext::StoreExistingDataLayer(FAssetData& AssetData) { UDataLayerConversionInfo* ConversionInfo = NewObject(); ConversionInfo->DataLayerAsset = CastChecked(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(); 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>&& PreviousConversions) { check(CurrentConversion->CurrentConvertingInfo == nullptr); check(CurrentConversion->PreviousConversionsInfo.IsEmpty()); CurrentConversion->PreviousConversionsInfo = MoveTemp(PreviousConversions); uint32 ErrorCount = 0; for (TWeakObjectPtr& 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>& 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 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& info : DataLayerConversionInfo) { FString ConflictingConversionString = TEXT(""); if(!info->PreviousConversionsInfo.IsEmpty()) { ConflictingConversionString = FString::JoinBy(info->PreviousConversionsInfo, TEXT(", "), [](const TWeakObjectPtr& 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()) {} 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 Tokens, Switches; TMap CommandLineParams; ParseCommandLine(*Params, Tokens, Switches, CommandLineParams); if (!InitializeFromCommandLine(Tokens, Switches, CommandLineParams)) { return EReturnCode::CommandletInitializationError; } ConversionFolder = DestinationFolder + "/" + MainWorld->GetName() + "/"; TStrongObjectPtr Context(NewObject()); 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& Tokens, TArray const& Switches, TMap 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& CommandletContext, FPackageSourceControlHelper& PackageHelper) { UE_SCOPED_TIMER(TEXT("Retrieving Already Converted Data Layers"), LogDataLayerToAssetCommandlet, Display); TArray 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 DataLayerInstances; Algo::Copy(WorldDataLayers->DataLayerInstances, DataLayerInstances); for (int32 i = DataLayerInstances.Num() -1; i >= 0; --i) { if(UDataLayerInstanceWithAsset* DataLayerInstance = Cast(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(DataLayerInstance)) { if (!CreateConversionFromDataLayer(CommandletContext, DataLayerToConvert, PackageHelper)) { ErrorCount++; } } } return ErrorCount == 0; } bool UDataLayerToAssetCommandlet::CreateConversionFromDataLayer(TStrongObjectPtr& CommandletContext, const UDeprecatedDataLayerInstance* DataLayer, FPackageSourceControlHelper &PackageHelper) { if(TObjectPtr 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 UDataLayerToAssetCommandlet::GetOrCreateDataLayerAssetForConversion(TStrongObjectPtr& CommandletContext, FName DataLayerAssetName) { TObjectPtr DataLayerAsset = nullptr; TArray> ConversionInfos; if (CommandletContext->FindDataLayerConversionInfos(DataLayerAssetName, ConversionInfos)) { for (TWeakObjectPtr& ConversionInfo : ConversionInfos) { if (IsAssetInConversionFolder(ConversionInfo->DataLayerAsset)) { check(DataLayerAsset == nullptr); return CastChecked(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("AssetTools").Get(); if (UObject* Asset = AssetTools.CreateAsset(SanitizedAssetName, ConversionFolder, UDataLayerAsset::StaticClass(), DataLayerFactory)) { DataLayerAsset = CastChecked(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& CommandletContext) { UE_SCOPED_TIMER(TEXT("Resolving Conflicting Conversions"), LogDataLayerToAssetCommandlet, Display); UE_LOG(LogDataLayerToAssetCommandlet, Log, TEXT("Resolving Conflicting Conversions")); uint32 ErrorCount = 0; for (const TObjectPtr& ConversionInfo : CommandletContext->GetDataLayerConversionInfos()) { if(IsAssetInConversionFolder(ConversionInfo->DataLayerAsset)) { TArray> 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& 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& CommandletContext, AActor* Actor) { PRAGMA_DISABLE_DEPRECATION_WARNINGS uint32 ErrorCount = 0; const TArray& 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& CommandletContext, AActor* Actor) { uint32 ErrorCount = 0; TArray ActorDataLayerAssets = Actor->GetDataLayerAssets(); for (int32 i = ActorDataLayerAssets.Num() - 1; i >= 0; --i) { const TObjectPtr& 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& CommandletContext) { UE_SCOPED_TIMER(TEXT("Creating Data Layer Instances"), LogDataLayerToAssetCommandlet, Display); AWorldDataLayers* WorldDataLayers = MainWorld->GetWorldDataLayers(); for (const TWeakObjectPtr& ConvertingInfo : CommandletContext->GetConvertingDataLayerConversionInfo()) { if (ConvertingInfo->DataLayerInstance == nullptr) { UDataLayerInstanceWithAsset* DataLayerInstance = WorldDataLayers->CreateDataLayer(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& CommandletContext) { UE_SCOPED_TIMER(TEXT("Creating Data Layer Instances Hierarchy"), LogDataLayerToAssetCommandlet, Display); uint32 ErrorCount = 0; for (const TWeakObjectPtr& 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(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& CommandletContext, FPackageSourceControlHelper& PackageHelper) { UE_SCOPED_TIMER(TEXT("Delete Conflicting Assets"), LogDataLayerToAssetCommandlet, Display); uint32 ErrorCount = 0; for (const TWeakObjectPtr& ConvertingInfo : CommandletContext->GetConvertingDataLayerConversionInfo()) { for (const TWeakObjectPtr& 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& CommandletContext, FPackageSourceControlHelper& PackageHelper) { uint32 ErrorCount = 0; AWorldDataLayers* WorldDataLayers = MainWorld->GetWorldDataLayers(); for (const TWeakObjectPtr& 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); }