Skip to content

Instantly share code, notes, and snippets.

@gamerxl
Last active January 17, 2024 10:34
Show Gist options
  • Save gamerxl/9c5976ba131ed3875385204cc7cb2c3b to your computer and use it in GitHub Desktop.
Save gamerxl/9c5976ba131ed3875385204cc7cb2c3b to your computer and use it in GitHub Desktop.
A blueprint library for unreal engine with functions available in blueprints to scan blueprints which implementing nodes or in other words whether it contains logic in its event and function graphs. It can be for instance used in editor widget utilities for utilties to refactor your blueprints e.g. move blueprints to c++.
#include "BlueprintScannerBlueprintLibrary.h"
#if WITH_EDITOR
#include "AssetRegistry/IAssetRegistry.h"
#include "K2Node_FunctionEntry.h"
#include "K2Node_FunctionResult.h"
bool UBlueprintScannerBlueprintLibrary::DoesBlueprintImplementNodes(UBlueprint* Blueprint, const bool bVerbose)
{
if (bVerbose)
{
UE_LOG(LogTemp, Log, TEXT("%s: Blueprint: %s"), *FString(__FUNCTION__), *Blueprint->GetName());
}
bool Result = false;
// Check the event graphs for nodes.
for (UEdGraph* Graph : Blueprint->EventGraphs)
{
if (bVerbose)
{
UE_LOG(LogTemp, Log, TEXT("%s: EventGraph: %s"), *FString(__FUNCTION__), *Graph->GetName());
}
for (const TObjectPtr<UEdGraphNode>& Node : Graph->Nodes)
{
if (bVerbose)
{
UE_LOG(LogTemp, Log, TEXT("%s: Node: %s"), *FString(__FUNCTION__), *Node->GetName());
}
if (Node->IsNodeEnabled())
{
Result = true;
}
}
}
// Check the function graphs for nodes. Handles construction scripts differently.
for (UEdGraph* Graph : Blueprint->FunctionGraphs)
{
if (bVerbose)
{
UE_LOG(LogTemp, Log, TEXT("%s: FunctionGraph: %s"), *FString(__FUNCTION__), *Graph->GetName());
}
if (Graph->GetName() == "UserConstructionScript")
{
for (const TObjectPtr<UEdGraphNode>& Node : Graph->Nodes)
{
if (bVerbose)
{
UE_LOG(LogTemp, Log, TEXT("%s: Node: %s"), *FString(__FUNCTION__), *Node->GetName());
}
}
if (Graph->Nodes.Num() > 1)
{
Result = true;
}
continue;
}
// Only check for the nodes in between a function entry and return node.
TArray<TObjectPtr<UEdGraphNode>> NodesInBetween;
bool bOverridesClass = false;
for (const TObjectPtr<UEdGraphNode>& Node : Graph->Nodes)
{
const bool bEntryNode = Node->IsA(UK2Node_FunctionEntry::StaticClass());
if (!bEntryNode && !Node->IsA(UK2Node_FunctionResult::StaticClass()))
{
NodesInBetween.AddUnique(Node);
continue;
}
if (bEntryNode)
{
// Check if the function overrides class.
if (const UK2Node_FunctionEntry* EntryNode = Cast<UK2Node_FunctionEntry>(Node);
EntryNode->FunctionReference.ResolveMember<UFunction>(EntryNode->GetBlueprintClassFromNode()))
{
bOverridesClass = true;
}
}
}
for (const TObjectPtr<UEdGraphNode>& Node : NodesInBetween)
{
if (bVerbose)
{
UE_LOG(LogTemp, Log, TEXT("%s: Node: %s"), *FString(__FUNCTION__), *Node->GetName());
}
// Check whether the function has enabled nodes and does not override class or at least has only one node.
if (Node->IsNodeEnabled() && !bOverridesClass || NodesInBetween.Num() > 1)
{
Result = true;
}
}
}
// Check for blueprint variables.
for (const FBPVariableDescription VariableDescription : Blueprint->NewVariables)
{
if (bVerbose)
{
UE_LOG(LogTemp, Log, TEXT("%s: Node: %s"), *FString(__FUNCTION__), *VariableDescription.FriendlyName);
}
Result = true;
}
return Result;
}
TArray<FString> UBlueprintScannerBlueprintLibrary::FindBlueprintsImplementingNodes(const TArray<FString> PackagePaths, const bool bVerbose)
{
TArray<FString> Result;
for (FAssetData BlueprintAssetData : FindBlueprintAssets(PackagePaths))
{
if (UBlueprint* BlueprintAsset = CastChecked<UBlueprint>(BlueprintAssetData.GetAsset());
DoesBlueprintImplementNodes(BlueprintAsset, bVerbose))
{
Result.AddUnique(BlueprintAssetData.PackageName.ToString());
}
}
Result.Sort();
return Result;
}
TArray<FAssetData> UBlueprintScannerBlueprintLibrary::FindBlueprintAssets(TArray<FString> PackagePaths)
{
TArray<FString> Result;
FARFilter GameOnlyFilter;
GameOnlyFilter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName());
GameOnlyFilter.ClassPaths.Add(UBlueprintGeneratedClass::StaticClass()->GetClassPathName());
GameOnlyFilter.ClassPaths.Add(UObjectRedirector::StaticClass()->GetClassPathName());
for (FString PackagePath : PackagePaths)
{
GameOnlyFilter.PackagePaths.AddUnique(FName(PackagePath));
}
GameOnlyFilter.bIncludeOnlyOnDiskAssets = true;
GameOnlyFilter.bRecursiveClasses = true;
GameOnlyFilter.bRecursivePaths = true;
TArray<FAssetData> BlueprintAssetDataList;
IAssetRegistry::Get()->GetAssets(GameOnlyFilter, BlueprintAssetDataList);
return BlueprintAssetDataList;
}
#endif
#pragma once
#if WITH_EDITOR
#include "CoreMinimal.h"
#include "BlueprintScannerBlueprintLibrary.generated.h"
UCLASS()
class UBlueprintScannerBlueprintLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
// Returns if a blueprint implements nodes or in other words whether it contains logic in its event and function graphs.
UFUNCTION(BlueprintCallable)
static bool DoesBlueprintImplementNodes(UBlueprint* Blueprint, const bool bVerbose = false);
// Returns blueprints which implementing nodes.
// It searches in the provided package paths for blueprints which implementing nodes or in other words contains logic in its event and function graphs.
UFUNCTION(BlueprintCallable)
static TArray<FString> FindBlueprintsImplementingNodes(const TArray<FString> PackagePaths, const bool bVerbose = false);
protected:
static TArray<FAssetData> FindBlueprintAssets(TArray<FString> PackagePaths);
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment