void SDetailsViewBase::RestoreExpandedItems(TSharedRef<FPropertyNode> InitialStartNode)
{
	TSharedPtr<FPropertyNode> StartNode = InitialStartNode;

	ExpandedDetailNodes.Empty();

	FString ExpandedCustomItems;

	UStruct* BestBaseStruct = StartNode->FindComplexParent()->GetBaseStructure();

	//while a valid class, and we're either the same as the base class (for multiple actors being selected and base class is AActor) OR we're not down to AActor yet)
	TArray<FString> DetailPropertyExpansionStrings;
	for (UStruct* Struct = BestBaseStruct; Struct && ((BestBaseStruct == Struct) || (Struct != AActor::StaticClass())); Struct = Struct->GetSuperStruct())
	{
		GConfig->GetSingleLineArray(TEXT("DetailPropertyExpansion"), *Struct->GetName(), DetailPropertyExpansionStrings, GEditorPerProjectIni);
	}

	TSet<FString> ExpandedPropertyItems;
	ExpandedPropertyItems.Append(DetailPropertyExpansionStrings);
	SetExpandedItems(StartNode, ExpandedPropertyItems);

	if (BestBaseStruct)
	{
		GConfig->GetString(TEXT("DetailCustomWidgetExpansion"), *BestBaseStruct->GetName(), ExpandedCustomItems, GEditorPerProjectIni);
		TArray<FString> ExpandedCustomItemsArray;
		ExpandedCustomItems.ParseIntoArray(ExpandedCustomItemsArray, TEXT(","), true);

		ExpandedDetailNodes.Append(ExpandedCustomItemsArray);
	}
}
void FGatherConvertedClassDependencies::GatherAssetReferencedByUDSDefaultValue(TSet<UObject*>& Dependencies, UUserDefinedStruct* Struct)
{
	if (Struct)
	{
		FStructOnScope StructOnScope(Struct);
		Struct->InitializeDefaultValue(StructOnScope.GetStructMemory());
		FArchiveReferencesInStructIntance ArchiveReferencesInStructIntance;
		Struct->SerializeItem(ArchiveReferencesInStructIntance, StructOnScope.GetStructMemory(), nullptr);
		Dependencies.Append(ArchiveReferencesInStructIntance.References);
	}
}
Пример #3
0
// Imports a set of previously exported nodes into a graph
void FEdGraphUtilities::ImportNodesFromText(UEdGraph* DestinationGraph, const FString& TextToImport, /*out*/ TSet<UEdGraphNode*>& ImportedNodeSet)
{
	// Turn the text buffer into objects
	FGraphObjectTextFactory Factory(DestinationGraph);
	Factory.ProcessBuffer(DestinationGraph, RF_Transactional, TextToImport);

	// Fix up pin cross-links, etc...
	FEdGraphUtilities::PostProcessPastedNodes(Factory.SpawnedNodes);

	ImportedNodeSet.Append(Factory.SpawnedNodes);
}
Пример #4
0
bool FVertexSnappingImpl::SnapLocationToNearestVertex( FVector& Location, const FVector2D& MouseLocation, FLevelEditorViewportClient* ViewportClient, FVector& OutVertexNormal, bool bDrawVertexHelpers )
{
	bool bSnapped = false;

	// Make a box around the actor which is the area we are allowed to snap in
	FBox AllowedSnappingBox = FBox( Location-VertexSnappingConstants::MaxSnappingDistance, Location+VertexSnappingConstants::MaxSnappingDistance );

	FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
		ViewportClient->Viewport, 
		ViewportClient->GetScene(),
		ViewportClient->EngineShowFlags )
		.SetRealtimeUpdate( ViewportClient->IsRealtime() ));

	FSceneView* View = ViewportClient->CalcSceneView( &ViewFamily );

	TArray<FSnapActor> ActorsInBox;
	TSet<TWeakObjectPtr<AActor> > ActorsToIgnore;
	// Ignore actors currently being moved
	ActorsToIgnore.Append( ViewportClient->GetDropPreviewActors() );

	GetPossibleSnapActors( AllowedSnappingBox, MouseLocation.IntPoint(), ViewportClient, View, EAxisList::Screen, ActorsToIgnore, ActorsInBox );

	FViewportCursorLocation Cursor(View, ViewportClient, MouseLocation.X, MouseLocation.Y );

	FPlane ActorPlane( Location, Cursor.GetDirection() );

	FVertexSnappingArgs Args
	( 
		ActorPlane, 
		Location,
		ViewportClient,
		View,
		Cursor.GetCursorPos(),
		EAxisList::Screen,
		bDrawVertexHelpers
	);
	
	// Snap to the nearest vertex
	FSnappingVertex ClosestVertex = GetClosestVertex( ActorsInBox, Args );

	Location = ClosestVertex.Position;
	OutVertexNormal = ClosestVertex.Normal;
	bSnapped = true;

	return bSnapped;
}
Пример #5
0
bool SDetailsView::ShouldSetNewObjects( const TArray< TWeakObjectPtr< UObject > >& InObjects ) const
{
	bool bShouldSetObjects = false;

	const bool bHadBSPBrushSelected = SelectedActorInfo.bHaveBSPBrush;
	if( bHadBSPBrushSelected == true )
	{
		// If a BSP brush was selected we need to refresh because surface could have been selected and the object set not updated
		bShouldSetObjects = true;
	}
	else if( InObjects.Num() != RootPropertyNode->GetNumObjects() )
	{
		// If the object arrys differ in size then at least one object is different so we must reset
		bShouldSetObjects = true;
	}
	else
	{
		// Check to see if the objects passed in are different. If not we do not need to set anything
		TSet< TWeakObjectPtr< UObject > > NewObjects;
		NewObjects.Append( InObjects );
		for ( TPropObjectIterator Itor( RootPropertyNode->ObjectIterator() ); Itor; ++Itor )
		{
			TWeakObjectPtr<UObject> Object = *Itor;
			if( Object.IsValid() && !NewObjects.Contains( Object ) )
			{
				// An existing object is not in the list of new objects to set
				bShouldSetObjects = true;
				break;
			}
			else if( !Object.IsValid() )
			{
				// An existing object is invalid
				bShouldSetObjects = true;
				break;
			}
		}
	}

	return bShouldSetObjects;
}
bool FPluginManager::ConfigureEnabledPlugins()
{
	if(!bHaveConfiguredEnabledPlugins)
	{
		// Don't need to run this again
		bHaveConfiguredEnabledPlugins = true;

		// If a current project is set, check that we know about any plugin that's explicitly enabled
		const FProjectDescriptor *Project = IProjectManager::Get().GetCurrentProject();
		const bool bHasProjectFile = Project != nullptr;

		// Get all the enabled plugin names
		TArray< FString > EnabledPluginNames;
#if IS_PROGRAM
		// Programs can also define the list of enabled plugins in ini
		GConfig->GetArray(TEXT("Plugins"), TEXT("ProgramEnabledPlugins"), EnabledPluginNames, GEngineIni);
#endif
#if !IS_PROGRAM || HACK_HEADER_GENERATOR
		if (!FParse::Param(FCommandLine::Get(), TEXT("NoEnginePlugins")))
		{
			FProjectManager::Get().GetEnabledPlugins(EnabledPluginNames);
		}
#endif

		// Build a set from the array
		TSet< FString > AllEnabledPlugins;
		AllEnabledPlugins.Append(MoveTemp(EnabledPluginNames));

		// Enable all the plugins by name
		for (const TSharedRef< FPlugin > Plugin : AllPlugins)
		{
			if (AllEnabledPlugins.Contains(Plugin->Name))
			{
				Plugin->bEnabled = (!IS_PROGRAM || !bHasProjectFile) || IsPluginSupportedByCurrentTarget(Plugin);
				if (!Plugin->bEnabled)
				{
					AllEnabledPlugins.Remove(Plugin->Name);
				}
			}
		}

		if (bHasProjectFile)
		{
			// Take a copy of the Project's plugins as we may remove some
			TArray<FPluginReferenceDescriptor> PluginsCopy = Project->Plugins;
			for(const FPluginReferenceDescriptor& Plugin: PluginsCopy)
			{
				if ((Plugin.bEnabled && !FindPluginInstance(Plugin.Name).IsValid()) &&
					 (!IS_PROGRAM || AllEnabledPlugins.Contains(Plugin.Name))) // skip if this is a program and the plugin is not enabled
				{
					FText Caption(LOCTEXT("PluginMissingCaption", "Plugin missing"));
					if(Plugin.MarketplaceURL.Len() > 0)
					{
						if(FMessageDialog::Open(EAppMsgType::YesNo, FText::Format(LOCTEXT("PluginMissingError", "This project requires the {0} plugin.\n\nWould you like to download it from the the Marketplace?"), FText::FromString(Plugin.Name)), &Caption) == EAppReturnType::Yes)
						{
							FString Error;
							FPlatformProcess::LaunchURL(*Plugin.MarketplaceURL, nullptr, &Error);
							if(Error.Len() > 0) FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(Error));
							return false;
						}
					}
					else
					{
						FString Description = (Plugin.Description.Len() > 0) ? FString::Printf(TEXT("\n\n%s"), *Plugin.Description) : FString();
						FMessageDialog::Open(EAppMsgType::Ok, FText::Format(LOCTEXT("PluginRequiredError", "This project requires the {0} plugin. {1}"), FText::FromString(Plugin.Name), FText::FromString(Description)), &Caption);
						
						if (FMessageDialog::Open(EAppMsgType::YesNo, FText::Format(LOCTEXT("PluginMissingDisable", "Would you like to disable {0}? You will no longer be able to open any assets created using it."), FText::FromString(Plugin.Name)), &Caption) == EAppReturnType::No)
						{
							return false;
						}

						FText FailReason;
						if (!IProjectManager::Get().SetPluginEnabled(*Plugin.Name, false, FailReason))
						{
							FMessageDialog::Open(EAppMsgType::Ok, FailReason);
						}
					}
				}
			}
		}

		// If we made it here, we have all the required plugins
		bHaveAllRequiredPlugins = true;

		for(const TSharedRef<FPlugin>& Plugin: AllPlugins)
		{
			if (Plugin->bEnabled)
			{
				// Add the plugin binaries directory
				const FString PluginBinariesPath = FPaths::Combine(*FPaths::GetPath(Plugin->FileName), TEXT("Binaries"), FPlatformProcess::GetBinariesSubdirectory());
				FModuleManager::Get().AddBinariesDirectory(*PluginBinariesPath, Plugin->LoadedFrom == EPluginLoadedFrom::GameProject);

#if !IS_MONOLITHIC
				// Only check this when in a non-monolithic build where modules could be in separate binaries
				if (Project != NULL && Project->Modules.Num() == 0)
				{
					// Content only project - check whether any plugins are incompatible and offer to disable instead of trying to build them later
					TArray<FString> IncompatibleFiles;
					if (!FModuleDescriptor::CheckModuleCompatibility(Plugin->Descriptor.Modules, Plugin->LoadedFrom == EPluginLoadedFrom::GameProject, IncompatibleFiles))
					{
						// Ask whether to disable plugin if incompatible
						FText Caption(LOCTEXT("IncompatiblePluginCaption", "Plugin missing or incompatible"));
						if (FMessageDialog::Open(EAppMsgType::YesNo, FText::Format(LOCTEXT("IncompatiblePluginText", "Missing or incompatible modules in {0} plugin - would you like to disable it? You will no longer be able to open any assets created using it."), FText::FromString(Plugin->Name)), &Caption) == EAppReturnType::No)
						{
							return false;
						}

						FText FailReason;
						if (!IProjectManager::Get().SetPluginEnabled(*Plugin->Name, false, FailReason))
						{
							FMessageDialog::Open(EAppMsgType::Ok, FailReason);
						}
					}
				}
#endif //!IS_MONOLITHIC

			// Build the list of content folders
				if (Plugin->Descriptor.bCanContainContent)
				{
					if (auto EngineConfigFile = GConfig->Find(GEngineIni, false))
					{
						if (auto CoreSystemSection = EngineConfigFile->Find(TEXT("Core.System")))
						{
							CoreSystemSection->AddUnique("Paths", Plugin->GetContentDir());
						}
					}
				}

				// Load Default<PluginName>.ini config file if it exists
				FString PluginConfigDir = FPaths::GetPath(Plugin->FileName) / TEXT("Config/");
				FConfigFile PluginConfig;
				FConfigCacheIni::LoadExternalIniFile(PluginConfig, *Plugin->Name, *FPaths::EngineConfigDir(), *PluginConfigDir, true);
				if (PluginConfig.Num() > 0)
				{
					FString PlaformName = FPlatformProperties::PlatformName();
					FString PluginConfigFilename = FString::Printf(TEXT("%s%s/%s.ini"), *FPaths::GeneratedConfigDir(), *PlaformName, *Plugin->Name);
					FConfigFile& NewConfigFile = GConfig->Add(PluginConfigFilename, FConfigFile());
					NewConfigFile.AddMissingProperties(PluginConfig);
					NewConfigFile.Write(PluginConfigFilename);
				}
			}
		}
		
		// Mount all the plugin content folders and pak files
		TArray<FString>	FoundPaks;
		FPakFileSearchVisitor PakVisitor(FoundPaks);
		IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
		for(TSharedRef<IPlugin> Plugin: GetEnabledPlugins())
		{
			if (Plugin->CanContainContent() && ensure(RegisterMountPointDelegate.IsBound()))
			{
				FString ContentDir = Plugin->GetContentDir();
				RegisterMountPointDelegate.Execute(Plugin->GetMountedAssetPath(), ContentDir);

				// Pak files are loaded from <PluginName>/Content/Paks/<PlatformName>
				if (FPlatformProperties::RequiresCookedData())
				{
					FoundPaks.Reset();
					PlatformFile.IterateDirectoryRecursively(*(ContentDir / TEXT("Paks") / FPlatformProperties::PlatformName()), PakVisitor);
					for (const auto& PakPath : FoundPaks)
					{
						if (FCoreDelegates::OnMountPak.IsBound())
						{
							FCoreDelegates::OnMountPak.Execute(PakPath, 0);
						}
					}
				}
			}
		}
	}
	return bHaveAllRequiredPlugins;
}
Пример #7
0
bool SDetailsView::ShouldSetNewObjects(const TArray< TWeakObjectPtr< UObject > >& InObjects) const
{
	bool bShouldSetObjects = false;

	const bool bHadBSPBrushSelected = SelectedActorInfo.bHaveBSPBrush;
	if( bHadBSPBrushSelected == true )
	{
		// If a BSP brush was selected we need to refresh because surface could have been selected and the object set not updated
		bShouldSetObjects = true;
	}
	else if( InObjects.Num() != GetNumObjects() )
	{
		// If the object arrays differ in size then at least one object is different so we must reset
		bShouldSetObjects = true;
	}
	else if(InObjects.Num() == 0)
	{
		// User is likely resetting details panel
		bShouldSetObjects = true;
	}
	else
	{
		// Check to see if the objects passed in are different. If not we do not need to set anything
		TSet< TWeakObjectPtr< UObject > > NewObjects;
		NewObjects.Append(InObjects);

		if(DetailsViewArgs.bAllowMultipleTopLevelObjects)
		{
			
			// For multiple top level node support, if the single object in each node is not found in the new object set
			// then we need to refresh
			for(int32 RootNodeIndex = 0; RootNodeIndex < RootPropertyNodes.Num(); ++RootNodeIndex)
			{
				FObjectPropertyNode* RootPropertyNode = RootPropertyNodes[RootNodeIndex]->AsObjectNode();
				
				if(RootPropertyNode && RootPropertyNode->GetNumObjects() > 0)
				{
					if(!NewObjects.Contains(RootPropertyNode->GetUObject(0)))
					{
						bShouldSetObjects = true;
						break;
					}
				}
				else
				{
					bShouldSetObjects = true;
					break;
				}
			}
		}
		else
		{

			ensure(RootPropertyNodes.Num() == 1);
			FObjectPropertyNode* RootPropertyNode = RootPropertyNodes[0]->AsObjectNode();
			if( RootPropertyNode )
			{
				for(TPropObjectIterator Itor(RootPropertyNode->ObjectIterator()); Itor; ++Itor)
				{
					TWeakObjectPtr<UObject> Object = *Itor;
					if(Object.IsValid() && !NewObjects.Contains(Object))
					{
						// An existing object is not in the list of new objects to set
						bShouldSetObjects = true;
						break;
					}
					else if(!Object.IsValid())
					{
						// An existing object is invalid
						bShouldSetObjects = true;
						break;
					}
				}
			}
			else
			{
				bShouldSetObjects = true;
			}
		}
	}
	
	if (!bShouldSetObjects && AssetSelectionUtils::IsAnySurfaceSelected(nullptr))
	{
		bShouldSetObjects = true;
	}

	return bShouldSetObjects;
}
bool FBuildDataCompactifier::Compactify(const TArray<FString>& ManifestsToKeep, const float DataAgeThreshold) const
{
	GLog->Logf(TEXT("Running Compactify on %s%s"), *CloudDir, bPreview ? TEXT(". Preview mode. NO action will be taken.") : bNoPatchDelete ? TEXT(". NoPatchDelete mode. NO patch data will be deleted.") : TEXT(""));
	if (ManifestsToKeep.Num() > 0)
	{
		GLog->Logf(TEXT("Preserving manifest files: %s"), *FString::Join(ManifestsToKeep, TEXT(", ")));
	}
	GLog->Logf(TEXT("Minimum age of deleted chunks: %.3f days"), DataAgeThreshold);

	// We'll work out the date of the oldest unreferenced file we'll keep
	FDateTime Cutoff = FDateTime::UtcNow() - FTimespan::FromDays(DataAgeThreshold);

	// We'll get ALL files first, so we can use the count to preallocate space within the data filenames array to save excessive reallocs
	TArray<FString> AllFiles;
	const bool bFindFiles = true;
	const bool bFindDirectories = false;
	IFileManager::Get().FindFilesRecursive(AllFiles, *CloudDir, TEXT("*.*"), bFindFiles, bFindDirectories);

	TSet<FGuid> ReferencedGuids; // The master list of *ALL* referenced chunk / file data Guids

	TArray<FString> ManifestFilenames;
	TArray<FString> DeletedManifestFilenames;
	TArray<FGuid> DataGuids; // The Guids associated with the data files from a single manifest
	int32 NumDataFiles = 0;
	uint64 ManifestBytesDeleted = 0;
	
	// Preallocate enough storage in DataGuids, to stop repeatedly expanding the allocation
	DataGuids.Reserve(AllFiles.Num());

	EnumerateManifests(ManifestFilenames);

	if (!DoAllManifestsExist(ManifestFilenames, ManifestsToKeep))
	{
		// At least one of the manifests we want to keep does not exist. This is an error condition
		GLog->Log(ELogVerbosity::Error, TEXT("Not all manifests to keep exist. Aborting operation"));
		return false;
	}

	if (!DeleteNonReferencedManifests(ManifestFilenames, ManifestsToKeep, DeletedManifestFilenames, ManifestBytesDeleted))
	{
		// An error occurred deleting one or more of the manifest files. This is an error condition
		GLog->Log(ELogVerbosity::Error, TEXT("Could not delete one or more manifest files. Aborting operation"));
		return false;
	}

	// If we don't have any manifest files, we'll treat that as an error condition
	if (ManifestFilenames.Num() == 0)
	{
		GLog->Log(ELogVerbosity::Warning, TEXT("Could not find any manifest files. Aborting operation."));
		return true;  // We're still going to return a success code, as this isn't a hard error
	}

	// Process each remaining manifest, and build up a list of all referenced files
	for (const auto& ManifestFilename : ManifestFilenames)
	{
		const FString ManifestPath = CloudDir / ManifestFilename;
		GLog->Logf(TEXT("Extracting chunk filenames from %s"), *ManifestFilename);

		// Load the manifest data from the manifest
		FBuildPatchAppManifest Manifest;
		if (Manifest.LoadFromFile(ManifestPath))
		{
			// Work out all data Guids referenced in the manifest, and add them to our list of files to keep
			DataGuids.Empty();
			Manifest.GetDataList(DataGuids);

			GLog->Logf(TEXT("Extracted %d chunks from %s. Unioning with %d existing chunks"), DataGuids.Num(), *ManifestFilename, NumDataFiles);
			NumDataFiles += DataGuids.Num();

			// We're going to keep all the Guids so we know which files to keep later
			ReferencedGuids.Append(DataGuids);
		}
		else
		{
			// We failed to read from the manifest file.  This is an error which should halt progress and return a non-zero exit code
			FString ErrorMessage = FString::Printf(TEXT("Could not parse manifest file %s"), *ManifestFilename);
			GLog->Log(ELogVerbosity::Error, *ErrorMessage);
			return false;
		}
	}

	GLog->Logf(TEXT("Compactify walking %s to touch referenced chunks, remove all aged unreferenced chunks and compute statistics."), *CloudDir);

	uint32 FilesProcessed = 0;
	uint32 FilesTouched = 0;
	uint32 FilesSkipped = 0;
	uint32 NonPatchFilesProcessed = 0;
	uint32 FilesDeleted = DeletedManifestFilenames.Num();
	uint64 BytesProcessed = 0;
	uint64 BytesTouched = 0;
	uint64 BytesSkipped = 0;
	uint64 NonPatchBytesProcessed = 0;
	uint64 BytesDeleted = ManifestBytesDeleted;
	uint64 CurrentFileSize;
	uint32 TouchFailureCount = 0;
	uint64 BytesFailedTouch = 0;
	FGuid FileGuid;
	FDateTime Now = FDateTime::UtcNow();
	const uint32 TouchFailureErrorThreshold = FMath::DivideAndRoundUp((uint32)AllFiles.Num(), BuildDataCompactifierDefs::TouchFailureThresholdPercentage);

	for (const auto& File : AllFiles)
	{
		CurrentFileSize = IFileManager::Get().FileSize(*File);
		if (CurrentFileSize >= 0)
		{
			++FilesProcessed;
			BytesProcessed += CurrentFileSize;

			if (!GetPatchDataGuid(File, FileGuid))
			{
				FString CleanFilename = FPaths::GetCleanFilename(File);
				if (!ManifestFilenames.Contains(CleanFilename) && !DeletedManifestFilenames.Contains(CleanFilename))
				{
					++NonPatchFilesProcessed;
					NonPatchBytesProcessed += CurrentFileSize;
				}
				continue;
			}

			if (ReferencedGuids.Contains(FileGuid))
			{
				// This is a valid, referenced file, so we need to touch it with the current date
				++FilesTouched;
				BytesTouched += CurrentFileSize;
				if (!bPreview)
				{
					if (!SafeSetTimeStamp(*File, Now))
					{
						++TouchFailureCount;
						BytesFailedTouch += CurrentFileSize;
						GLog->Logf(ELogVerbosity::Warning, TEXT("Failed to set timestamp on file %s"), *File);
						if (TouchFailureCount >= TouchFailureErrorThreshold)
						{
							GLog->Logf(ELogVerbosity::Error, TEXT("Failed to set timestamp on %u%% of files"), BuildDataCompactifierDefs::TouchFailureThresholdPercentage);
							return false;
						}
					}
				}
			}
			else if (IFileManager::Get().GetTimeStamp(*File) < Cutoff)
			{
				// This file is not referenced by any manifest, is a data file, and is older than we want to keep ...
				// Let's get rid of it!
				DeleteFile(File);
				++FilesDeleted;
				BytesDeleted += CurrentFileSize;
			}
			else
			{
				++FilesSkipped;
				BytesSkipped += CurrentFileSize;
			}
		}
		else
		{
			GLog->Logf(TEXT("Warning. Could not determine size of %s. Perhaps it has been removed by another process."), *File);
		}
	}

	FilesTouched -= TouchFailureCount;
	BytesTouched -= BytesFailedTouch;

	GLog->Logf(TEXT("Compactify of %s complete!"), *CloudDir);
	GLog->Logf(TEXT("Compactify found %u files totalling %s."), FilesProcessed, *HumanReadableSize(BytesProcessed));
	if (NonPatchFilesProcessed > 0)
	{
		GLog->Logf(TEXT("Of these, %u (totalling %s) were not chunk/manifest files."), NonPatchFilesProcessed, *HumanReadableSize(NonPatchBytesProcessed));
	}
	if (bNoPatchDelete)
	{
		GLog->Logf(TEXT("Compactify skipped deleting %u unreferenced chunk/manifest files (totalling %s) due to -nopatchdelete being specified."), FilesDeleted, *HumanReadableSize(BytesDeleted));
	}
	else
	{
		GLog->Logf(TEXT("Compactify deleted %u chunk/manifest files totalling %s."), FilesDeleted, *HumanReadableSize(BytesDeleted));
	}
	GLog->Logf(TEXT("Compactify touched %u chunk files totalling %s."), FilesTouched, *HumanReadableSize(BytesTouched));
	if (TouchFailureCount > 0)
	{
		GLog->Logf(TEXT("Compactify failed to touch %u files toatlling %s."), TouchFailureCount, *HumanReadableSize(BytesFailedTouch));
	}
	GLog->Logf(TEXT("Compactify skipped %u unreferenced chunk files (totalling %s) which have not yet aged out."), FilesSkipped, *HumanReadableSize(BytesSkipped));
	return true;
}
Пример #9
0
void FFoliageInstanceBaseCache::CompactInstanceBaseCache(AInstancedFoliageActor* IFA)
{
	UWorld* World = IFA->GetWorld();
	if (!World || World->IsGameWorld())
	{
		return;
	}

	FFoliageInstanceBaseCache& Cache = IFA->InstanceBaseCache;
	
	TSet<FFoliageInstanceBaseId> BasesInUse;
	for (auto& Pair : IFA->FoliageMeshes)
	{
		for (const auto& Pair : Pair.Value->ComponentHash)
		{
			if (Pair.Key != FFoliageInstanceBaseCache::InvalidBaseId)
			{
				BasesInUse.Add(Pair.Key);
			}
		}
	}
	
	// Look for any removed maps
	TSet<FFoliageInstanceBasePtr> InvalidBasePtrs;
	for (auto& Pair : Cache.InstanceBaseLevelMap)
	{
		const auto& WorldAsset = Pair.Key;
		
		bool bExists = (WorldAsset == World);
		// Check sub-levels
		if (!bExists)
		{
			const FName PackageName = FName(*FPackageName::ObjectPathToPackageName(WorldAsset.ToStringReference().ToString()));
			if (World->WorldComposition)
			{
				bExists = World->WorldComposition->DoesTileExists(PackageName);
			}
			else
			{
				bExists = (World->GetLevelStreamingForPackageName(PackageName) != nullptr);
			}
		}

		if (!bExists)
		{
			InvalidBasePtrs.Append(Pair.Value);
			Cache.InstanceBaseLevelMap.Remove(Pair.Key);
		}
		else
		{
			// Remove dead links
			for (int32 i = Pair.Value.Num()-1; i >= 0; --i)
			{
				// Base needs to be removed if it's not in use by existing instances or component was removed
				if (Pair.Value[i].IsNull() || !BasesInUse.Contains(Cache.GetInstanceBaseId(Pair.Value[i])))
				{
					InvalidBasePtrs.Add(Pair.Value[i]);
					Pair.Value.RemoveAt(i);
				}
			}

			if (Pair.Value.Num() == 0)
			{
				Cache.InstanceBaseLevelMap.Remove(Pair.Key);
			}
		}
	}
	
	TSet<FFoliageInstanceBaseId> InvalidBaseIds;
	Cache.InstanceBaseInvMap.Empty();
	// Look for any removed base components
	for (const auto& Pair : Cache.InstanceBaseMap)
	{
		const FFoliageInstanceBaseInfo& BaseInfo = Pair.Value;
		if (InvalidBasePtrs.Contains(BaseInfo.BasePtr))
		{
			InvalidBaseIds.Add(Pair.Key);
			Cache.InstanceBaseMap.Remove(Pair.Key);
		}
		else
		{
			// Regenerate inverse map
			check(!Cache.InstanceBaseInvMap.Contains(BaseInfo.BasePtr));
			Cache.InstanceBaseInvMap.Add(BaseInfo.BasePtr, Pair.Key);
		}
	}

	if (InvalidBaseIds.Num())
	{
		for (auto& Pair : IFA->FoliageMeshes)
		{
			auto& MeshInfo = Pair.Value;
			MeshInfo->ComponentHash.Empty();
			int32 InstanceIdx = 0;
			
			for (FFoliageInstance& Instance : MeshInfo->Instances)
			{
				if (InvalidBaseIds.Contains(Instance.BaseId))
				{
					Instance.BaseId = FFoliageInstanceBaseCache::InvalidBaseId;
				}

				MeshInfo->ComponentHash.FindOrAdd(Instance.BaseId).Add(InstanceIdx);
				InstanceIdx++;
			}
		}

		Cache.InstanceBaseMap.Compact();
		Cache.InstanceBaseLevelMap.Compact();
	}
}