Repository: heyomidk/HardReferenceFinder
Branch: master
Commit: 2aacee46427c
Files: 15
Total size: 51.5 KB
Directory structure:
gitextract_ra60czo9/
├── .gitignore
├── HardReferenceFinder.uplugin
├── LICENSE
├── README.md
└── Source/
└── HardReferenceFinder/
├── HardReferenceFinder.Build.cs
├── Private/
│ ├── HardReferenceFinder.cpp
│ ├── HardReferenceFinderSearchData.cpp
│ ├── HardReferenceFinderStyle.cpp
│ ├── HardReferenceFinderSummoner.cpp
│ └── SHardReferenceFinderWindow.cpp
└── Public/
├── HardReferenceFinder.h
├── HardReferenceFinderSearchData.h
├── HardReferenceFinderStyle.h
├── HardReferenceFinderSummoner.h
└── SHardReferenceFinderWindow.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
Binaries
DerivedDataCache
Intermediate
Saved
.vscode
.vs
*.VC.db
*.opensdf
*.opendb
*.sdf
*.sln
*.suo
*.xcodeproj
*.xcworkspace
.idea/
*.uproject.DotSettings.user
Plugins/Developer/
Plugins/HoudiniEngine/
*_bak[0-9].hiplc
*_bak[0-9][0-9].hiplc
*_bak[0-9].hdalc
*_bak[0-9][0-9].hdalc
*.tga~
*.kra~
*.png~
*.blend1
.autosave/
*.resources/
Content/Asian_Village/maps/Asian_Village_Demo_BuiltData.uasset
Content/Japanese_Temple/maps/Japanese_Temple_Demo_BuiltData.uasset
Content/stylized_castle/maps/stylized_castle_demo_BuiltData.uasset
Content/Underworld/Maps/Underworld_demo_BuiltData.uasset
================================================
FILE: HardReferenceFinder.uplugin
================================================
{
"FileVersion": 3,
"Version": 1,
"VersionName": "0.4.0",
"FriendlyName": "HardReferenceFinder",
"Description": "Plugin that shows hard referencing nodes in a blueprint graph.",
"Category": "Other",
"CreatedBy": "Omid Kiarostami",
"CreatedByURL": "",
"DocsURL": "",
"MarketplaceURL": "",
"SupportURL": "",
"CanContainContent": false,
"IsBetaVersion": false,
"IsExperimentalVersion": false,
"Installed": false,
"Modules": [
{
"Name": "HardReferenceFinder",
"Type": "Editor",
"LoadingPhase": "PostEngineInit"
}
]
}
================================================
FILE: LICENSE
================================================
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.
================================================
FILE: README.md
================================================
# Hard Reference Finder
An open-source editor plugin for Unreal Engine 5 that identifies hard references in a blueprint graph.
The plugin allows you to summon a window which links to the various function calls, variables, graph pins, etc that are causing hard package references to other assets. Results are grouped by package and sorted by size, from largest to smallest.
Compatible with Unreal Engine versions 5.3, 5.2, 5.1, 5.0 and 4.27.

# Installation
- Download the zip and unpack it (or clone this repository) to your projects *Plugins* folder.
- Build the game with the plugin.
- If necessary, enable the plugin from the plugins windows.
# Usage
Open any blueprint with a graph or function view, then select *Window -> Hard References* from the toolbar.

# Known Issues
- After modifying a blueprint, you have to compile/save it before 'Refresh' will display the updated list of references.
- Isn't identifying references from:
- function arguments
- properties nested in a struct
- Note: This is still an initial version; the tool is unable to identify the source of some package references in a blueprint. Bug reports/pull requests/methods for detecting unidentified references are appreciated.
================================================
FILE: Source/HardReferenceFinder/HardReferenceFinder.Build.cs
================================================
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class HardReferenceFinder : ModuleRules
{
public HardReferenceFinder(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"Projects",
"InputCore",
"UnrealEd",
"ToolMenus",
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"Kismet",
"AssetRegistry",
"BlueprintGraph",
"AssetTools",
}
);
if (Target.Version.MajorVersion >= 5)
{
PrivateDependencyModuleNames.AddRange(
new string[]
{
"SubobjectEditor",
});
}
if (Target.Version.MajorVersion < 5
|| Target.Version.MajorVersion == 5 && Target.Version.MinorVersion < 1)
{
PrivateDependencyModuleNames.AddRange(
new string[]
{
"EditorStyle", // only used in <UE5.0
});
}
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}
================================================
FILE: Source/HardReferenceFinder/Private/HardReferenceFinder.cpp
================================================
// Copyright Epic Games, Inc. All Rights Reserved.
#include "HardReferenceFinder.h"
#include "HardReferenceFinderStyle.h"
#include "WorkflowOrientedApp/WorkflowTabManager.h"
#include "BlueprintEditor.h"
#include "HardReferenceFinderSummoner.h"
static const FName HardReferenceFinderTabName("HardReferenceFinder");
#define LOCTEXT_NAMESPACE "FHardReferenceFinderModule"
void FHardReferenceFinderModule::StartupModule()
{
FHardReferenceFinderStyle::Initialize();
FHardReferenceFinderStyle::ReloadTextures();
FBlueprintEditorModule& BlueprintEditorModule = FModuleManager::LoadModuleChecked<FBlueprintEditorModule>("Kismet");
BlueprintEditorModule.OnRegisterTabsForEditor().AddRaw(this, &FHardReferenceFinderModule::RegisterBlueprintTabs);
}
void FHardReferenceFinderModule::ShutdownModule()
{
FHardReferenceFinderStyle::Shutdown();
FBlueprintEditorModule& BlueprintEditorModule = FModuleManager::LoadModuleChecked<FBlueprintEditorModule>("Kismet");
BlueprintEditorModule.OnRegisterTabsForEditor().RemoveAll(this);
}
void FHardReferenceFinderModule::RegisterBlueprintTabs(FWorkflowAllowedTabSet& TabFactory, FName ModeName, TSharedPtr<FBlueprintEditor> InBlueprintEditor) const
{
TabFactory.RegisterFactory(MakeShareable(new FHardReferenceFinderSummoner(InBlueprintEditor)));
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FHardReferenceFinderModule, HardReferenceFinder)
================================================
FILE: Source/HardReferenceFinder/Private/HardReferenceFinderSearchData.cpp
================================================
#include "HardReferenceFinderSearchData.h"
#include "AssetToolsModule.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "BlueprintEditor.h"
#include "EdGraph/EdGraph.h"
#include "K2Node_CallFunction.h"
#include "K2Node_DynamicCast.h"
#include "K2Node_FunctionEntry.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Misc/EngineVersionComparison.h"
#include "Styling/SlateIconFinder.h"
#if ENGINE_MAJOR_VERSION < 5
#include "SSCSEditor.h"
#else
#include "SSubobjectEditor.h"
#endif
#define LOCTEXT_NAMESPACE "FHardReferenceFinderModule"
TArray<FHRFTreeViewItemPtr> FHardReferenceFinderSearchData::GatherSearchData(TWeakPtr<FBlueprintEditor> BlueprintEditor)
{
Reset();
TMap<FName, FHRFTreeViewItemPtr> DependentPackageMap;
FAssetRegistryModule& AssetRegistryModule = FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
// Get this blueprints package dependencies from the blueprint editor
TArray<FName> BlueprintDependencies;
GetBlueprintDependencies(BlueprintDependencies, AssetRegistryModule, BlueprintEditor);
// Populate display information from package dependencies
{
TMap<FName, FAssetData> DependencyToAssetDataMap;
GetAssetForPackages(BlueprintDependencies, DependencyToAssetDataMap);
for (auto MapIt = DependencyToAssetDataMap.CreateConstIterator(); MapIt; ++MapIt)
{
const FName& PathName = MapIt.Key();
const FAssetData& AssetData = MapIt.Value();
FString AssetTypeName = GetAssetTypeName(AssetData);
FString FileName = FPaths::GetCleanFilename(PathName.ToString());
FAssetPackageData AssetPackageData;
TryGetAssetPackageData(PathName, AssetPackageData, AssetRegistryModule);
if( FHRFTreeViewItemPtr Header = MakeShared<FHRFTreeViewItem>() )
{
Header->bIsHeader = true;
Header->PackageId = PathName;
Header->Tooltip = FText::FromName(PathName);
Header->Name = FText::FromString(FileName);
Header->SizeOnDisk = GatherAssetSizeByName(PathName, AssetRegistryModule);
Header->SlateIcon = FSlateIcon("EditorStyle", FName( *("ClassIcon." + AssetTypeName)));
FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools"));
if (UClass* AssetClass = AssetData.GetClass())
{
TWeakPtr<IAssetTypeActions> AssetTypeActions = AssetToolsModule.Get().GetAssetTypeActionsForClass(AssetData.GetClass());
if(AssetTypeActions.IsValid())
{
Header->IconColor = AssetTypeActions.Pin()->GetTypeColor();
}
}
DependentPackageMap.Add(PathName, Header);
TreeView.Add(Header);
}
}
}
{
// Search through blueprint nodes for references to the dependent packages
if( UBlueprint* Blueprint = BlueprintEditor.Pin()->GetBlueprintObj() )
{
SearchGraphNodes(DependentPackageMap, AssetRegistryModule, Blueprint->UbergraphPages);
SearchFunctionReferences(DependentPackageMap, AssetRegistryModule, Blueprint);
SearchBlueprintClassProperties(DependentPackageMap, AssetRegistryModule, Blueprint);
SearchSimpleConstructionScript(DependentPackageMap, AssetRegistryModule, Blueprint);
}
}
// If we didn't discover any references to a package make a note
for(FHRFTreeViewItemPtr HeaderItem : TreeView)
{
if(HeaderItem->Children.Num() <= 0)
{
FHRFTreeViewItemPtr ChildItem = MakeShared<FHRFTreeViewItem>();
HeaderItem->Children.Add(ChildItem);
ChildItem->Name = LOCTEXT("UnknownSource", "Unidentified source");
ChildItem->Tooltip = LOCTEXT("UnknownSourceTooltip", "This package is being referenced but the plugin is unable to identify its source.");
}
}
// sort from largest to smallest
TreeView.Sort([](FHRFTreeViewItemPtr Lhs, FHRFTreeViewItemPtr Rhs)
{
return Lhs->SizeOnDisk > Rhs->SizeOnDisk;
});
return TreeView;
}
void FHardReferenceFinderSearchData::Reset()
{
TreeView.Reset();
}
UObject* FHardReferenceFinderSearchData::GetObjectContext(TWeakPtr<FBlueprintEditor> BlueprintEditor) const
{
if(!BlueprintEditor.IsValid())
{
return nullptr;
}
class BlueprintEditorEditingObject_AccessHack : public FBlueprintEditor
{
public:
UObject* GetEditingObject_Expose() const { return GetEditingObject(); }
};
return static_cast<BlueprintEditorEditingObject_AccessHack*>(BlueprintEditor.Pin().Get())->GetEditingObject_Expose();
}
void FHardReferenceFinderSearchData::GetBlueprintDependencies(TArray<FName>& OutPackageDependencies, FAssetRegistryModule& AssetRegistryModule, TWeakPtr<FBlueprintEditor> BlueprintEditor) const
{
UObject* Object = GetObjectContext(BlueprintEditor);
if(Object == nullptr)
{
return;
}
FAssetData ExistingAsset = GetAssetDataForObject(Object);
UE::AssetRegistry::FDependencyQuery Flags(UE::AssetRegistry::EDependencyQuery::Hard);
AssetRegistryModule.GetDependencies(ExistingAsset.PackageName, OutPackageDependencies, UE::AssetRegistry::EDependencyCategory::Package, Flags);
}
void FHardReferenceFinderSearchData::SearchGraphNodes(TMap<FName, FHRFTreeViewItemPtr>& OutPackageMap, const FAssetRegistryModule& AssetRegistryModule, const FEdGraphArray& EdGraphList) const
{
for(UEdGraph* Graph : EdGraphList)
{
if(Graph)
{
for (UEdGraphNode* Node : Graph->Nodes)
{
const UPackage* FunctionPackage = nullptr;
if(const UK2Node_CallFunction* CallFunctionNode = Cast<UK2Node_CallFunction>(Node))
{
FunctionPackage = CallFunctionNode->FunctionReference.GetMemberParentPackage();
}
else if(const UK2Node_DynamicCast* CastNode = Cast<UK2Node_DynamicCast>(Node))
{
if(CastNode->TargetType)
{
FunctionPackage = CastNode->TargetType->GetPackage();
}
}
if( const FHRFTreeViewItemPtr Result = CheckAddPackageResult(OutPackageMap, AssetRegistryModule, FunctionPackage) )
{
Result->Name = Node->GetNodeTitle(ENodeTitleType::ListView);
Result->NodeGuid = Node->NodeGuid;
Result->SlateIcon = Node->GetIconAndTint(Result->IconColor);
}
// Also search the pins of this node for any references to other packages, e.g. the 'Class' pin of a SpawnActor node.
SearchNodePins(OutPackageMap, AssetRegistryModule, Node);
}
}
}
}
void FHardReferenceFinderSearchData::SearchNodePins(TMap<FName, FHRFTreeViewItemPtr>& OutPackageMap, const FAssetRegistryModule& AssetRegistryModule, const UEdGraphNode* Node) const
{
if(Node == nullptr)
{
return;
}
for(const UEdGraphPin* Pin : Node->Pins)
{
if(Pin->bHidden)
{
// skip hidden pins
continue;
}
if(Pin->Direction == EGPD_Input)
{
if(const UObject* PinObject = Pin->DefaultObject)
{
const UPackage* FunctionPackage = PinObject->GetPackage();
if( const FHRFTreeViewItemPtr Result = CheckAddPackageResult(OutPackageMap, AssetRegistryModule, FunctionPackage) )
{
Result->Name = FText::Format(LOCTEXT("FunctionInput","{0} ({1})"), FText::FromString(Pin->GetName()), Node->GetNodeTitle(ENodeTitleType::ListView));
Result->NodeGuid = Node->NodeGuid;
if( const UEdGraphSchema* Schema = Pin->GetSchema() )
{
Result->IconColor = Schema->GetPinTypeColor(Pin->PinType);
}
Result->SlateIcon = FSlateIcon("EditorStyle", "Graph.Pin.Disconnected_VarA");
}
}
}
}
}
void FHardReferenceFinderSearchData::SearchFunctionReferences(TMap<FName, FHRFTreeViewItemPtr>& OutPackageMap, const FAssetRegistryModule& AssetRegistryModule, const UBlueprint* Blueprint) const
{
// @heyomidk This method is still imperfect as there are a number of ways references can be formed from functions
// 1. From graph nodes inside the function (Cast, etc)
// 2. From Local Variables inside the function
// a. Due to the variables type (ex: class is Blueprint)
// b. Due to the default value (ie Subclass of UObject but points to a Blueprint)
// 3. From arguments to a function
// a. Due to the type of an input argument
// b. Due to the type of an output argument
// c. Due to the default value of an output argument
//
// Note that the UFunction* provided by a TFieldRange iterator accounts for reference types 2a/2b, while the cached
// UFunction* in an EdGraphNode finds 2a but not 2b
//
// Note that neither method identifies all type 3 references so this approach may need to be amended/revised
// This gathers type 1/3c references and creates links to the associated graph nodes.
SearchGraphNodes(OutPackageMap, AssetRegistryModule, Blueprint->FunctionGraphs);
// This gathers type 2 references and links them to the function entry node
for( UFunction* Function : TFieldRange<UFunction>(Blueprint->GeneratedClass, EFieldIteratorFlags::ExcludeSuper) )
{
const UK2Node_FunctionEntry* GraphEntryNode = FindGraphNodeForFunction(Blueprint, Function);
if(GraphEntryNode)
{
for( const UObject* ReferencedObject : Function->ScriptAndPropertyObjectReferences)
{
const UPackage* Package = ReferencedObject->GetPackage();
if( const FHRFTreeViewItemPtr Result = CheckAddPackageResult(OutPackageMap, AssetRegistryModule, Package) )
{
Result->Name = FText::Format(LOCTEXT("FunctionReference","{0}"), GraphEntryNode->GetNodeTitle(ENodeTitleType::ListView));
Result->NodeGuid = GraphEntryNode->NodeGuid;
Result->SlateIcon = GraphEntryNode->GetIconAndTint(Result->IconColor);
}
}
}
}
}
void FHardReferenceFinderSearchData::SearchSimpleConstructionScript(TMap<FName, FHRFTreeViewItemPtr>& OutPackageMap, const FAssetRegistryModule& AssetRegistryModule, const UBlueprint* Blueprint) const
{
const USimpleConstructionScript* SimpleConstructionScript = Blueprint->SimpleConstructionScript;
if(SimpleConstructionScript == nullptr)
{
return;
}
const TArray<USCS_Node*>& RootNodes = SimpleConstructionScript->GetAllNodes();
for(const USCS_Node* SCSNode : RootNodes)
{
if(SCSNode == nullptr)
{
continue;
}
const FName VarName = SCSNode->GetVariableName();
TSet<UPackage*> OutReferencedPackages;
FindPackagesInSCSNode(OutReferencedPackages, SCSNode);
for(const UPackage* Package : OutReferencedPackages)
{
if(const FHRFTreeViewItemPtr Result = CheckAddPackageResult(OutPackageMap, AssetRegistryModule, Package))
{
Result->SlateIcon = FSlateIconFinder::FindIconForClass(SCSNode->ComponentClass, TEXT("SCS.Component"));
Result->Name = FText::Format(LOCTEXT("ComponentReference", "{0}"), FText::FromName(VarName));
Result->SCSIdentifier = SCSNode->GetFName();
}
}
}
}
UK2Node_FunctionEntry* FHardReferenceFinderSearchData::FindGraphNodeForFunction(const UBlueprint* Blueprint, UFunction* FunctionToFind) const
{
if(Blueprint == nullptr)
{
return nullptr;
}
if(FunctionToFind == nullptr)
{
return nullptr;
}
// search functions in the Graph
for(UEdGraph* Graph : Blueprint->FunctionGraphs)
{
// find the entry point for the function in the graph
UK2Node_FunctionEntry* GraphEntryNode = nullptr;
for(UEdGraphNode* Node : Graph->Nodes)
{
GraphEntryNode = Cast<UK2Node_FunctionEntry>(Node);
if(GraphEntryNode != nullptr)
{
break;
}
}
if(GraphEntryNode)
{
// See if this matches the provided UFunction
const TSharedPtr<FStructOnScope> NodeFunctionVarCache = GraphEntryNode->GetFunctionVariableCache();
if( NodeFunctionVarCache.IsValid() )
{
if( const UFunction* NodeFunction = Cast<UFunction>(NodeFunctionVarCache->GetStruct()) )
{
const FName NodeFunctionName = NodeFunction->GetFName();
const FName TargetFunctionName = FunctionToFind->GetFName();
if(NodeFunctionName == TargetFunctionName)
{
return GraphEntryNode;
}
}
}
}
}
return nullptr;
}
void FHardReferenceFinderSearchData::FindPackagesInSCSNode(TSet<UPackage*>& OutReferencedPackages, const USCS_Node* SCSNode) const
{
if(SCSNode==nullptr || SCSNode->ComponentClass == nullptr)
{
return;
}
if( UPackage* NodePackage = SCSNode->ComponentClass->GetPackage() )
{
if(NodePackage)
{
OutReferencedPackages.Add(NodePackage);
}
}
for( const FProperty* Property : TFieldRange<FProperty>(SCSNode->ComponentClass, EFieldIteratorFlags::IncludeSuper))
{
FSlateIcon VariableTypeIcon;
TArray<UPackage*> ReferencedPackages = FindPackagesForProperty(VariableTypeIcon, SCSNode->ComponentTemplate, Property );
for(UPackage* Package : ReferencedPackages)
{
OutReferencedPackages.Add(Package);
}
}
}
TArray<UPackage*> FHardReferenceFinderSearchData::FindPackagesForProperty(FSlateIcon& OutResultIcon, const UObject* ContainerPtr, const FProperty* TargetProperty) const
{
TArray<UPackage*> FoundPackages;
if(TargetProperty == nullptr)
{
return FoundPackages;
}
if(ContainerPtr != nullptr)
{
TArray<const FStructProperty*> EncounteredStructProps;
const EPropertyObjectReferenceType ReferenceType = EPropertyObjectReferenceType::Strong;
const bool bHasStrongReferences = TargetProperty->ContainsObjectReference(EncounteredStructProps, ReferenceType);
if(bHasStrongReferences)
{
const void* TargetPropertyAddress = TargetProperty->ContainerPtrToValuePtr<void>(ContainerPtr);
struct PropertyAndAddressTuple
{
const FProperty* Property = nullptr;
const void* ValueAddress = nullptr;
};
TArray<PropertyAndAddressTuple> PropertiesToExamine;
if(const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(TargetProperty))
{
OutResultIcon = FSlateIcon("EditorStyle", "Kismet.VariableList.ArrayTypeIcon");
FScriptArrayHelper ArrayHelper(ArrayProperty, TargetPropertyAddress);
for(int i=0; i<ArrayHelper.Num(); ++i)
{
PropertiesToExamine.Add({ArrayProperty->Inner, ArrayHelper.GetRawPtr(i)});
}
}
else if(const FSetProperty* SetProperty = CastField<FSetProperty>(TargetProperty))
{
OutResultIcon = FSlateIcon("EditorStyle", "Kismet.VariableList.SetTypeIcon");
FScriptSetHelper SetHelper(SetProperty, TargetPropertyAddress);
for(int i=0; i<SetHelper.Num(); ++i)
{
PropertiesToExamine.Add({SetHelper.GetElementProperty(), SetHelper.GetElementPtr(i)});
}
}
else if(const FMapProperty* MapProperty = CastField<FMapProperty>(TargetProperty))
{
OutResultIcon = FSlateIcon("EditorStyle", "Kismet.VariableList.MapValueTypeIcon");
FScriptMapHelper MapHelper(MapProperty, TargetPropertyAddress);
for(int i=0; i<MapHelper.Num(); ++i)
{
PropertiesToExamine.Add({MapHelper.GetKeyProperty(), MapHelper.GetKeyPtr(i)});
PropertiesToExamine.Add({MapHelper.GetValueProperty(), MapHelper.GetValuePtr(i)});
}
}
else
{
OutResultIcon = FSlateIcon("EditorStyle", "Kismet.VariableList.TypeIcon");
PropertiesToExamine.Add({TargetProperty, TargetPropertyAddress});
}
for (const PropertyAndAddressTuple& Tuple : PropertiesToExamine)
{
if( const FObjectPropertyBase* ObjectProperty = CastField<FObjectPropertyBase>(Tuple.Property) )
{
if(const UObject* Object = ObjectProperty->GetObjectPropertyValue(Tuple.ValueAddress))
{
if(UPackage* Package = Object->GetPackage())
{
FoundPackages.AddUnique(Package);
}
}
}
}
}
}
#if UE_VERSION_OLDER_THAN(5,3,0)
typedef const TArray<UObject*>* FObjectArray;
#else
typedef const TArray<TObjectPtr<UObject>>* FObjectArray;
#endif
FObjectArray ScriptAndPropertyObjectReferences = nullptr;
if(const FObjectPropertyBase* ObjectProperty = CastField<FObjectPropertyBase>(TargetProperty))
{
ScriptAndPropertyObjectReferences = &ObjectProperty->PropertyClass->ScriptAndPropertyObjectReferences;
if(ObjectProperty->PropertyClass)
{
if(UPackage* Package = ObjectProperty->PropertyClass->GetPackage())
{
FoundPackages.AddUnique(Package);
}
}
}
else if(const FStructProperty* StructProperty = CastField<FStructProperty>(TargetProperty))
{
ScriptAndPropertyObjectReferences = &StructProperty->Struct->ScriptAndPropertyObjectReferences;
}
if(ScriptAndPropertyObjectReferences)
{
for(const UObject* ObjectReference : *ScriptAndPropertyObjectReferences)
{
if(ObjectReference)
{
if(UPackage* Package = ObjectReference->GetPackage())
{
FoundPackages.AddUnique(Package);
}
}
}
}
return FoundPackages;
}
void FHardReferenceFinderSearchData::SearchBlueprintClassProperties(TMap<FName, FHRFTreeViewItemPtr>& OutPackageMap, const FAssetRegistryModule& AssetRegistryModule, UBlueprint* Blueprint) const
{
for( FProperty* Property : TFieldRange<FProperty>(Blueprint->GeneratedClass, EFieldIteratorFlags::ExcludeSuper))
{
UBlueprint* FoundBlueprint = nullptr;
const FName VarName = Property->GetFName();
const int32 VarIndex = FBlueprintEditorUtils::FindNewVariableIndexAndBlueprint(Blueprint, VarName, FoundBlueprint);
const bool bIsVar = VarIndex != INDEX_NONE;
bool bSkipProperty = false;
if(!bIsVar)
{
if(const FObjectPropertyBase* ObjectProperty = CastField<FObjectPropertyBase>(Property))
{
const bool bIsActorComponentClass = ObjectProperty->PropertyClass->IsChildOf(UActorComponent::StaticClass());
if(bIsActorComponentClass)
{
// Actor components aren't initialized in the CDO so parsing them here isn't exhaustive.
// SearchSimpleConstructionScript() handles these, so skip these properties here.
bSkipProperty = true;
}
}
}
if(!bSkipProperty)
{
FSlateIcon ResultIcon;
TArray<UPackage*> ReferencedPackages = FindPackagesForProperty(ResultIcon, Blueprint->GeneratedClass->GetDefaultObject(), Property);
for(const UPackage* Package : ReferencedPackages)
{
if(const FHRFTreeViewItemPtr Result = CheckAddPackageResult(OutPackageMap, AssetRegistryModule, Package))
{
if(bIsVar)
{
const FBPVariableDescription& Description = Blueprint->NewVariables[VarIndex];
if( UEdGraphSchema_K2 const* Schema = GetDefault<UEdGraphSchema_K2>() )
{
Result->IconColor = Schema->GetPinTypeColor(Description.VarType);
}
Result->SlateIcon = ResultIcon;
Result->Name = FText::Format(LOCTEXT("MemberVariable","{0} (Member Variable)"), FText::FromName(Description.VarName));
Result->Tooltip = LOCTEXT("MemberVariableTooltip","Blueprint member variable");
}
else
{
Result->SlateIcon = ResultIcon;
Result->Name = FText::Format(LOCTEXT("OtherPropertyName", "{0}"), FText::FromName(VarName));
}
}
}
}
}
}
FHRFTreeViewItemPtr FHardReferenceFinderSearchData::CheckAddPackageResult(TMap<FName, FHRFTreeViewItemPtr>& OutPackageMap, const FAssetRegistryModule& AssetRegistryModule, const UPackage* Package) const
{
if( Package )
{
const FName PackageName = Package->GetFName();
if(const FHRFTreeViewItemPtr* FoundHeader = OutPackageMap.Find(PackageName))
{
const FHRFTreeViewItemPtr Header = *FoundHeader;
FAssetPackageData AssetPackageData;
const bool bExists = TryGetAssetPackageData(PackageName, AssetPackageData, AssetRegistryModule);
if(ensure(bExists))
{
FHRFTreeViewItemPtr Link = MakeShared<FHRFTreeViewItem>();
Header->Children.Add(Link);
return Link;
}
}
}
return nullptr;
}
void FHardReferenceFinderSearchData::GetAssetForPackages(const TArray<FName>& PackageNames, TMap<FName, FAssetData>& OutPackageToAssetData) const
{
#if ENGINE_MAJOR_VERSION < 5
FAssetRegistryModule& AssetRegistryModule = FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
FARFilter Filter;
for ( auto PackageIt = PackageNames.CreateConstIterator(); PackageIt; ++PackageIt )
{
const FString& PackageName = (*PackageIt).ToString();
Filter.PackageNames.Add(*PackageIt);
}
TArray<FAssetData> AssetDataList;
AssetRegistryModule.Get().GetAssets(Filter, AssetDataList);
for ( auto AssetIt = AssetDataList.CreateConstIterator(); AssetIt; ++AssetIt )
{
OutPackageToAssetData.Add((*AssetIt).PackageName, *AssetIt);
}
#else
UE::AssetRegistry::GetAssetForPackages(PackageNames, OutPackageToAssetData);
#endif
}
bool FHardReferenceFinderSearchData::TryGetAssetPackageData(FName PathName, FAssetPackageData& OutPackageData, const FAssetRegistryModule& AssetRegistryModule) const
{
#if UE_VERSION_OLDER_THAN(5, 0, 0)
if(const FAssetPackageData* pAssetPackageData = AssetRegistryModule.Get().GetAssetPackageData(PathName))
{
OutPackageData = *pAssetPackageData;
return true;
}
#elif UE_VERSION_OLDER_THAN(5, 1, 0)
const TOptional<FAssetPackageData> pAssetPackageData = AssetRegistryModule.Get().GetAssetPackageDataCopy(PathName);
if (pAssetPackageData.IsSet())
{
OutPackageData = pAssetPackageData.GetValue();
return true;
}
#else
const UE::AssetRegistry::EExists Result = AssetRegistryModule.TryGetAssetPackageData(PathName, OutPackageData);
if(Result == UE::AssetRegistry::EExists::Exists)
{
return true;
}
#endif
return false;
}
FString FHardReferenceFinderSearchData::GetAssetTypeName(const FAssetData& AssetData) const
{
#if UE_VERSION_OLDER_THAN(5, 1, 0)
return AssetData.AssetClass.ToString();
#else
return AssetData.AssetClassPath.GetAssetName().ToString();
#endif
}
FAssetData FHardReferenceFinderSearchData::GetAssetDataForObject(const UObject* Object) const
{
FAssetRegistryModule& AssetRegistryModule = FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
FString ObjectPath = Object->GetPathName();
#if UE_VERSION_OLDER_THAN(5, 1, 0)
return AssetRegistryModule.Get().GetAssetByObjectPath(*ObjectPath);
#else
return AssetRegistryModule.Get().GetAssetByObjectPath(FSoftObjectPath(ObjectPath));
#endif
}
int64 FHardReferenceFinderSearchData::GatherAssetSizeByName(const FName& AssetName, FAssetRegistryModule& AssetRegistryModule) const
{
TSet<FName> Visited;
const int64 Size = GatherAssetSizeRecursive(AssetName, Visited, AssetRegistryModule);
return Size;
}
int64 FHardReferenceFinderSearchData::GatherAssetSizeRecursive(const FName& AssetName, TSet<FName>& OutVisited, FAssetRegistryModule& AssetRegistryModule) const
{
const bool bAlreadyVisited = OutVisited.Contains(AssetName);
if(bAlreadyVisited)
{
return 0;
}
OutVisited.Add(AssetName);
int64 AssetSize = 0;
FAssetPackageData AssetPackageData;
if( TryGetAssetPackageData(AssetName, AssetPackageData, AssetRegistryModule) )
{
AssetSize += AssetPackageData.DiskSize;
}
TArray<FName> Dependencies;
const UE::AssetRegistry::FDependencyQuery Flags(UE::AssetRegistry::EDependencyQuery::Hard);
AssetRegistryModule.GetDependencies(AssetName, Dependencies, UE::AssetRegistry::EDependencyCategory::Package, Flags);
int64 DependencySize = 0;
for(const FName& DependencyName : Dependencies)
{
DependencySize += GatherAssetSizeRecursive(DependencyName, OutVisited, AssetRegistryModule);
}
const int64 TotalSize = AssetSize + DependencySize;
return TotalSize;
}
#undef LOCTEXT_NAMESPACE
================================================
FILE: Source/HardReferenceFinder/Private/HardReferenceFinderStyle.cpp
================================================
// Copyright Epic Games, Inc. All Rights Reserved.
#include "HardReferenceFinderStyle.h"
#include "Styling/SlateStyleRegistry.h"
#include "Framework/Application/SlateApplication.h"
#include "Slate/SlateGameResources.h"
#include "Interfaces/IPluginManager.h"
#define RootToContentDir Style->RootToContentDir
TSharedPtr<FSlateStyleSet> FHardReferenceFinderStyle::StyleInstance = nullptr;
void FHardReferenceFinderStyle::Initialize()
{
if (!StyleInstance.IsValid())
{
StyleInstance = Create();
FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance);
}
}
void FHardReferenceFinderStyle::Shutdown()
{
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance);
ensure(StyleInstance.IsUnique());
StyleInstance.Reset();
}
FName FHardReferenceFinderStyle::GetStyleSetName()
{
static FName StyleSetName(TEXT("HardReferenceFinderStyle"));
return StyleSetName;
}
const FVector2D Icon16x16(16.0f, 16.0f);
const FVector2D Icon20x20(20.0f, 20.0f);
TSharedRef< FSlateStyleSet > FHardReferenceFinderStyle::Create()
{
TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("HardReferenceFinderStyle"));
Style->SetContentRoot(IPluginManager::Get().FindPlugin("HardReferenceFinder")->GetBaseDir() / TEXT("Resources"));
return Style;
}
void FHardReferenceFinderStyle::ReloadTextures()
{
if (FSlateApplication::IsInitialized())
{
FSlateApplication::Get().GetRenderer()->ReloadTextureResources();
}
}
const ISlateStyle& FHardReferenceFinderStyle::Get()
{
return *StyleInstance;
}
================================================
FILE: Source/HardReferenceFinder/Private/HardReferenceFinderSummoner.cpp
================================================
// Copyright Epic Games, Inc. All Rights Reserved.
#include "HardReferenceFinderSummoner.h"
#include "HardReferenceFinderStyle.h"
#include "BlueprintEditor.h"
#include "SHardReferenceFinderWindow.h"
#define LOCTEXT_NAMESPACE "FHardReferenceFinderModule"
static const FName HardReferenceFinderID( TEXT( "HardReferenceFinder") );
FHardReferenceFinderSummoner::FHardReferenceFinderSummoner(TSharedPtr<FAssetEditorToolkit> InHostingApp)
: FWorkflowTabFactory(HardReferenceFinderID, InHostingApp)
{
TabLabel = LOCTEXT("HardReferenceFinderTabTitle", "Hard References");
TabIcon = FSlateIcon("EditorStyle","ContentBrowser.ReferenceViewer");
bIsSingleton = true;
ViewMenuDescription = LOCTEXT("HardReferenceFinderView", "Hard Reference Viewer");
ViewMenuTooltip = LOCTEXT("HardReferenceFinderView_ToolTip", "Shows hard referencing nodes associated with this Blueprint");
}
TSharedRef<SWidget> FHardReferenceFinderSummoner::CreateTabBody(const FWorkflowTabSpawnInfo& Info) const
{
const TSharedPtr<FBlueprintEditor> BlueprintEditorPtr = StaticCastSharedPtr<FBlueprintEditor>(HostingApp.Pin());
return SNew(SHardReferenceFinderWindow, BlueprintEditorPtr);
}
FText FHardReferenceFinderSummoner::GetTabToolTipText(const FWorkflowTabSpawnInfo& Info) const
{
return LOCTEXT("TabTooltip", "Shows the hard referencing nodes in this Blueprint");
}
#undef LOCTEXT_NAMESPACE
================================================
FILE: Source/HardReferenceFinder/Private/SHardReferenceFinderWindow.cpp
================================================
#include "SHardReferenceFinderWindow.h"
#include "BlueprintEditor.h"
#include "BlueprintEditorTabs.h"
#include "GraphEditorSettings.h"
#include "HardReferenceFinderSearchData.h"
#include "Engine/SCS_Node.h"
#include "Engine/SimpleConstructionScript.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "Misc/EngineVersionComparison.h"
#include "Widgets/Input/SButton.h"
#if UE_VERSION_OLDER_THAN(5, 1, 0)
#include "EditorStyleSet.h"
#endif
#define LOCTEXT_NAMESPACE "FHardReferenceFinderModule"
namespace HardReferenceInternals
{
static FText MakeBestSizeString(const SIZE_T SizeInBytes)
{
FText SizeText;
if (SizeInBytes < 1000)
{
// We ended up with bytes, so show a decimal number
SizeText = FText::AsMemory(SizeInBytes, EMemoryUnitStandard::SI);
}
else
{
// Show a fractional number with the best possible units
FNumberFormattingOptions NumberFormattingOptions;
NumberFormattingOptions.MaximumFractionalDigits = 1;
NumberFormattingOptions.MinimumFractionalDigits = 0;
NumberFormattingOptions.MinimumIntegralDigits = 1;
SizeText = FText::AsMemory(SizeInBytes, &NumberFormattingOptions, nullptr, EMemoryUnitStandard::SI);
}
return SizeText;
}
}
void SHardReferenceFinderWindow::Construct(const FArguments& InArgs, TSharedPtr<FBlueprintEditor> InBlueprintGraph)
{
BlueprintGraph = InBlueprintGraph;
ChildSlot[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.Padding(10.f)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.Padding(8.f,4.f)
.VAlign(VAlign_Center)
[
SAssignNew(HeaderText, STextBlock)
]
+SHorizontalBox::Slot()
.AutoWidth()
.HAlign(HAlign_Right)
.Padding(0.f, 0.0f)
[
SNew(SButton)
.OnClicked(this, &SHardReferenceFinderWindow::OnRefreshClicked)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.Padding(0.,3.f)
.AutoWidth()
[
SNew(SImage)
.Image(GetBrush_RefreshIcon())
]
+ SHorizontalBox::Slot()
.Padding(FMargin(8., 0, 0, 0))
.VAlign(VAlign_Center)
.AutoWidth()
[
SNew(STextBlock)
.Text(LOCTEXT("Refresh", "Refresh"))
]
]
]
]
+ SVerticalBox::Slot()
[
SNew(SBorder)
.BorderImage(GetBrush_MenuBackground())
.Padding(FMargin(8.f, 8.f, 4.f, 0.f))
[
SAssignNew(TreeView, SHRFTreeType)
.TreeItemsSource(&TreeViewData)
.OnGetChildren(this, &SHardReferenceFinderWindow::OnGetChildren)
.OnGenerateRow(this, &SHardReferenceFinderWindow::OnGenerateRow)
.OnMouseButtonDoubleClick(this, &SHardReferenceFinderWindow::OnDoubleClickTreeEntry)
]
]
];
InitiateSearch();
}
TSet<FName> SHardReferenceFinderWindow::GetCollapsedPackages() const
{
TSet<FHRFTreeViewItemPtr> ExpandedItems;
TreeView->GetExpandedItems(ExpandedItems);
TSet<FName> CollapsedPackages;
for(const FHRFTreeViewItemPtr Item : TreeViewData)
{
if(!ExpandedItems.Contains(Item))
{
CollapsedPackages.Add(Item->PackageId);
}
}
return CollapsedPackages;
}
void SHardReferenceFinderWindow::InitiateSearch()
{
if(TreeView.IsValid())
{
TSet<FName> UserCollapsedPackages = GetCollapsedPackages();
TreeViewData = SearchData.GatherSearchData(BlueprintGraph);
TreeView->RebuildList();
// expand new items by default, unless they were intentionally collapsed.
for(const FHRFTreeViewItemPtr Item : TreeViewData)
{
const bool bWasCollapsed = UserCollapsedPackages.Contains(Item->PackageId);
const bool bShouldExpandItem = !bWasCollapsed;
TreeView->SetItemExpansion(Item, bShouldExpandItem);
}
}
const FText SummaryText = FText::Format(LOCTEXT("SummaryMessage", "This blueprint makes {0} references to other packages."), SearchData.GetNumPackagesReferenced());
HeaderText->SetText(SummaryText);
}
FReply SHardReferenceFinderWindow::OnRefreshClicked()
{
InitiateSearch();
return FReply::Handled();
}
bool SHardReferenceFinderWindow::BringAttentionToSCSNode(const FName& SCSIdentifier) const
{
if(!SCSIdentifier.IsValid())
{
return false;
}
if(!BlueprintGraph.IsValid())
{
return false;
}
const UBlueprint* Blueprint = BlueprintGraph.Pin()->GetBlueprintObj();
if(Blueprint == nullptr)
{
return false;
}
if(Blueprint->SimpleConstructionScript == nullptr)
{
return false;
}
UBlueprintGeneratedClass* GeneratedClass = Cast<UBlueprintGeneratedClass>(Blueprint->GeneratedClass);
if(GeneratedClass == nullptr)
{
return false;
}
// Open Viewport Tab
BlueprintGraph.Pin()->FocusWindow();
BlueprintGraph.Pin()->GetTabManager()->TryInvokeTab(FBlueprintEditorTabs::SCSViewportID);
// Find and Select the Component in the Viewport tab view
const TArray<USCS_Node*>& Nodes = Blueprint->SimpleConstructionScript->GetAllNodes();
for (USCS_Node* Node : Nodes)
{
if (Node->GetFName() == SCSIdentifier)
{
if (const UActorComponent* Component = Node->GetActualComponentTemplate(GeneratedClass))
{
#if ENGINE_MAJOR_VERSION < 5
BlueprintGraph.Pin()->FindAndSelectSCSEditorTreeNode(Component, false);
#else
BlueprintGraph.Pin()->FindAndSelectSubobjectEditorTreeNode(Component, false);
#endif
}
return true;
}
}
return false;
}
void SHardReferenceFinderWindow::OnDoubleClickTreeEntry(TSharedPtr<FHRFTreeViewItem> Item) const
{
if(Item.IsValid() && BlueprintGraph.IsValid())
{
if( UBlueprint* BlueprintObj = BlueprintGraph.Pin()->GetBlueprintObj() )
{
if( const UEdGraphNode* GraphNode = FBlueprintEditorUtils::GetNodeByGUID(BlueprintObj, Item->NodeGuid) )
{
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(GraphNode);
}
else if(Item->SCSIdentifier.IsValid())
{
BringAttentionToSCSNode(Item->SCSIdentifier);
}
}
}
}
void SHardReferenceFinderWindow::OnGetChildren(FHRFTreeViewItemPtr InItem, TArray<FHRFTreeViewItemPtr>& OutChildren) const
{
OutChildren += InItem->Children;
}
TSharedRef<ITableRow> SHardReferenceFinderWindow::OnGenerateRow(FHRFTreeViewItemPtr Item, const TSharedRef<STableViewBase>& TableViewBase) const
{
if(Item->bIsHeader)
{
const FText SizeText = HardReferenceInternals::MakeBestSizeString(Item->SizeOnDisk);
const FText CategoryHeaderText = FText::Format(LOCTEXT("CategoryHeader", "{1} ({0})"), SizeText, Item->Name);
return SNew(STableRow<TSharedPtr<FName>>, TableViewBase)
.Style( GetStyle_HeaderRow() )
.Padding(FMargin(2.f, 3.f, 2.f, 3.f))
.ToolTipText(Item->Tooltip)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.VAlign(VAlign_Center)
.Padding(FMargin(0.f, 0.f, 8.f, 0.f))
.AutoWidth()
[
SNew(SImage)
.Image(Item->SlateIcon.GetOptionalIcon())
.ColorAndOpacity(Item->IconColor)
]
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(2.f)
[
SNew(STextBlock).Text(CategoryHeaderText)
]
];
}
else
{
return SNew(STableRow<TSharedPtr<FName>>, TableViewBase)
.ToolTipText(Item->Tooltip)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.VAlign(VAlign_Center)
.Padding(FMargin(0.f, 0.f, 8.f, 0.f))
.AutoWidth()
[
SNew(SImage)
.Image(Item->SlateIcon.GetOptionalIcon())
.ColorAndOpacity(Item->IconColor)
]
+SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(2.f)
[
SNew(STextBlock).Text(Item->Name)
]
];
}
}
const FSlateBrush* SHardReferenceFinderWindow::GetBrush_MenuBackground() const
{
#if UE_VERSION_OLDER_THAN(5, 1, 0)
return FEditorStyle::GetBrush("Menu.Background");
#else
return FAppStyle::Get().GetBrush("Brushes.Recessed");
#endif
}
const FSlateBrush* SHardReferenceFinderWindow::GetBrush_RefreshIcon() const
{
#if UE_VERSION_OLDER_THAN(5, 1, 0)
return FEditorStyle::GetBrush("Icons.Refresh");
#else
return FAppStyle::GetBrush("Icons.Refresh");
#endif
}
const FTableRowStyle* SHardReferenceFinderWindow::GetStyle_HeaderRow() const
{
#if UE_VERSION_OLDER_THAN(5, 1, 0)
return &FCoreStyle::Get().GetWidgetStyle<FTableRowStyle>("TableView.Row");
#else
return &FAppStyle::Get().GetWidgetStyle<FTableRowStyle>("ShowParentsTableView.Row");
#endif
}
#undef LOCTEXT_NAMESPACE
================================================
FILE: Source/HardReferenceFinder/Public/HardReferenceFinder.h
================================================
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
class FWorkflowAllowedTabSet;
class FBlueprintEditor;
class FHardReferenceFinderModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
private:
void RegisterBlueprintTabs(FWorkflowAllowedTabSet& TabManager, FName ModeName, TSharedPtr<FBlueprintEditor> InBlueprintEditor) const;
};
================================================
FILE: Source/HardReferenceFinder/Public/HardReferenceFinderSearchData.h
================================================
#pragma once
#include "CoreMinimal.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Templates/SharedPointer.h"
#include "Textures/SlateIcon.h"
class FBlueprintEditor;
class UEdGraph;
class UEdGraphNode;
class UK2Node_FunctionEntry;
class USCS_Node;
class FHRFTreeViewItem : public TSharedFromThis<FHRFTreeViewItem>
{
public:
bool bIsHeader = false;
int SizeOnDisk = 0;
FName PackageId = NAME_None;
FText Name;
FText Tooltip;
FGuid NodeGuid;
FName SCSIdentifier = NAME_None;
FSlateIcon SlateIcon;
FLinearColor IconColor = FLinearColor::White;
TArray<TSharedPtr<FHRFTreeViewItem>> Children;
};
typedef TSharedPtr<FHRFTreeViewItem> FHRFTreeViewItemPtr;
#if ENGINE_MAJOR_VERSION < 5
typedef const TArray<UEdGraph*> FEdGraphArray;
#else
typedef const TArray<TObjectPtr<UEdGraph>> FEdGraphArray;
#endif
class FHardReferenceFinderSearchData
{
public:
TArray<FHRFTreeViewItemPtr> GatherSearchData(TWeakPtr<FBlueprintEditor> BlueprintEditor);
int GetNumPackagesReferenced() const { return TreeView.Num(); }
private:
void Reset();
UObject* GetObjectContext(TWeakPtr<FBlueprintEditor> BlueprintEditor) const;
void GetBlueprintDependencies(TArray<FName>& OutPackageDependencies, FAssetRegistryModule& AssetRegistryModule, TWeakPtr<FBlueprintEditor> BlueprintEditor) const;
void SearchGraphNodes(TMap<FName, FHRFTreeViewItemPtr>& OutPackageMap, const FAssetRegistryModule& AssetRegistryModule, const FEdGraphArray& EdGraphList) const;
void SearchNodePins(TMap<FName, FHRFTreeViewItemPtr>& OutPackageMap, const FAssetRegistryModule& AssetRegistryModule, const UEdGraphNode* Node) const;
void SearchBlueprintClassProperties(TMap<FName, FHRFTreeViewItemPtr>& OutPackageMap, const FAssetRegistryModule& AssetRegistryModule, UBlueprint* Blueprint) const;
void SearchFunctionReferences(TMap<FName, FHRFTreeViewItemPtr>& OutPackageMap, const FAssetRegistryModule& AssetRegistryModule, const UBlueprint* Blueprint) const;
void SearchSimpleConstructionScript(TMap<FName, FHRFTreeViewItemPtr>& OutPackageMap, const FAssetRegistryModule& AssetRegistryModule, const UBlueprint* Blueprint) const;
UK2Node_FunctionEntry* FindGraphNodeForFunction(const UBlueprint* Blueprint, UFunction* FunctionToFind) const;
void FindPackagesInSCSNode(TSet<UPackage*>& OutReferencedPackages,const USCS_Node* SCSNode) const;
TArray<UPackage*> FindPackagesForProperty(FSlateIcon& OutResultIcon, const UObject* ContainerPtr, const FProperty* TargetProperty) const;
FHRFTreeViewItemPtr CheckAddPackageResult(TMap<FName, FHRFTreeViewItemPtr>& OutPackageMap, const FAssetRegistryModule& AssetRegistryModule, const UPackage* Package) const;
void GetAssetForPackages(const TArray<FName>& PackageNames, TMap<FName, FAssetData>& OutPackageToAssetData) const;
bool TryGetAssetPackageData(FName PathName, FAssetPackageData& OutPackageData, const FAssetRegistryModule& AssetRegistryModule) const;
FString GetAssetTypeName(const FAssetData& AssetData) const;
FAssetData GetAssetDataForObject(const UObject* Object) const;
int64 GatherAssetSizeByName(const FName& AssetName, FAssetRegistryModule& AssetRegistryModule) const;
int64 GatherAssetSizeRecursive(const FName& OutFrontier, TSet<FName>& OutVisited, FAssetRegistryModule& AssetRegistryModule) const;
TArray<FHRFTreeViewItemPtr> TreeView;
};
================================================
FILE: Source/HardReferenceFinder/Public/HardReferenceFinderStyle.h
================================================
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Styling/SlateStyle.h"
/** */
class FHardReferenceFinderStyle
{
public:
static void Initialize();
static void Shutdown();
/** reloads textures used by slate renderer */
static void ReloadTextures();
/** @return The Slate style set for the Shooter game */
static const ISlateStyle& Get();
static FName GetStyleSetName();
private:
static TSharedRef< class FSlateStyleSet > Create();
private:
static TSharedPtr< class FSlateStyleSet > StyleInstance;
};
================================================
FILE: Source/HardReferenceFinder/Public/HardReferenceFinderSummoner.h
================================================
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "WorkflowOrientedApp/WorkflowTabFactory.h"
struct FHardReferenceFinderSummoner : public FWorkflowTabFactory
{
FHardReferenceFinderSummoner(TSharedPtr<class FAssetEditorToolkit> InHostingApp);
virtual TSharedRef<SWidget> CreateTabBody(const FWorkflowTabSpawnInfo& Info) const override;
virtual FText GetTabToolTipText(const FWorkflowTabSpawnInfo& Info) const override;
};
================================================
FILE: Source/HardReferenceFinder/Public/SHardReferenceFinderWindow.h
================================================
#pragma once
#include "CoreMinimal.h"
#include "HardReferenceFinderSearchData.h"
#include "Widgets/SCompoundWidget.h"
#include "Widgets/Views/STableViewBase.h"
#include "Widgets/Views/STableRow.h"
#include "Widgets/Views/STreeView.h"
class FBlueprintEditor;
class SHardReferenceFinderWindow : public SCompoundWidget
{
SLATE_BEGIN_ARGS(SHardReferenceFinderWindow) {};
SLATE_END_ARGS()
void Construct(const FArguments& InArgs, TSharedPtr<FBlueprintEditor> InBlueprintGraph);
private:
typedef STreeView<FHRFTreeViewItemPtr> SHRFTreeType;
TSet<FName> GetCollapsedPackages() const;
void InitiateSearch();
FReply OnRefreshClicked();
bool BringAttentionToSCSNode(const FName& SCSIdentifier) const;
void OnDoubleClickTreeEntry(TSharedPtr<FHRFTreeViewItem> Item) const;
void OnGetChildren(FHRFTreeViewItemPtr InItem, TArray< FHRFTreeViewItemPtr >& OutChildren) const;
TSharedRef<ITableRow> OnGenerateRow(FHRFTreeViewItemPtr Item, const TSharedRef<STableViewBase>& TableViewBase) const;
const FSlateBrush* GetBrush_MenuBackground() const;
const FSlateBrush* GetBrush_RefreshIcon() const;
const FTableRowStyle* GetStyle_HeaderRow() const;
/* The graph this window is operating on */
TWeakPtr<FBlueprintEditor> BlueprintGraph;
/* Stores the data from searching the graph for references*/
FHardReferenceFinderSearchData SearchData;
/* Stores the list of items dispalyed by the tree view widget */
TArray<TSharedPtr<FHRFTreeViewItem>> TreeViewData;
/* Holds a reference to the header widget */
TSharedPtr<STextBlock> HeaderText;
/* Holds a reference to the tree view*/
TSharedPtr<SHRFTreeType> TreeView;
};
gitextract_ra60czo9/
├── .gitignore
├── HardReferenceFinder.uplugin
├── LICENSE
├── README.md
└── Source/
└── HardReferenceFinder/
├── HardReferenceFinder.Build.cs
├── Private/
│ ├── HardReferenceFinder.cpp
│ ├── HardReferenceFinderSearchData.cpp
│ ├── HardReferenceFinderStyle.cpp
│ ├── HardReferenceFinderSummoner.cpp
│ └── SHardReferenceFinderWindow.cpp
└── Public/
├── HardReferenceFinder.h
├── HardReferenceFinderSearchData.h
├── HardReferenceFinderStyle.h
├── HardReferenceFinderSummoner.h
└── SHardReferenceFinderWindow.h
SYMBOL INDEX (27 symbols across 10 files)
FILE: Source/HardReferenceFinder/HardReferenceFinder.Build.cs
class HardReferenceFinder (line 5) | public class HardReferenceFinder : ModuleRules
method HardReferenceFinder (line 7) | public HardReferenceFinder(ReadOnlyTargetRules Target) : base(Target)
FILE: Source/HardReferenceFinder/Private/HardReferenceFinderSearchData.cpp
function UObject (line 109) | UObject* FHardReferenceFinderSearchData::GetObjectContext(TWeakPtr<FBlue...
function UK2Node_FunctionEntry (line 281) | UK2Node_FunctionEntry* FHardReferenceFinderSearchData::FindGraphNodeForF...
type PropertyAndAddressTuple (line 373) | struct PropertyAndAddressTuple
function FHRFTreeViewItemPtr (line 524) | FHRFTreeViewItemPtr FHardReferenceFinderSearchData::CheckAddPackageResul...
function FString (line 594) | FString FHardReferenceFinderSearchData::GetAssetTypeName(const FAssetDat...
function FAssetData (line 603) | FAssetData FHardReferenceFinderSearchData::GetAssetDataForObject(const U...
function int64 (line 615) | int64 FHardReferenceFinderSearchData::GatherAssetSizeByName(const FName&...
function int64 (line 622) | int64 FHardReferenceFinderSearchData::GatherAssetSizeRecursive(const FNa...
FILE: Source/HardReferenceFinder/Private/HardReferenceFinderStyle.cpp
function FName (line 29) | FName FHardReferenceFinderStyle::GetStyleSetName()
function ISlateStyle (line 55) | const ISlateStyle& FHardReferenceFinderStyle::Get()
FILE: Source/HardReferenceFinder/Private/HardReferenceFinderSummoner.cpp
function FText (line 30) | FText FHardReferenceFinderSummoner::GetTabToolTipText(const FWorkflowTab...
FILE: Source/HardReferenceFinder/Private/SHardReferenceFinderWindow.cpp
type HardReferenceInternals (line 20) | namespace HardReferenceInternals
function FText (line 22) | static FText MakeBestSizeString(const SIZE_T SizeInBytes)
function FReply (line 148) | FReply SHardReferenceFinderWindow::OnRefreshClicked()
function FSlateBrush (line 289) | const FSlateBrush* SHardReferenceFinderWindow::GetBrush_MenuBackground()...
function FSlateBrush (line 298) | const FSlateBrush* SHardReferenceFinderWindow::GetBrush_RefreshIcon() const
function FTableRowStyle (line 307) | const FTableRowStyle* SHardReferenceFinderWindow::GetStyle_HeaderRow() c...
FILE: Source/HardReferenceFinder/Public/HardReferenceFinder.h
function class (line 11) | class FHardReferenceFinderModule : public IModuleInterface
FILE: Source/HardReferenceFinder/Public/HardReferenceFinderSearchData.h
function class (line 14) | class FHRFTreeViewItem : public TSharedFromThis<FHRFTreeViewItem>
type TSharedPtr (line 29) | typedef TSharedPtr<FHRFTreeViewItem> FHRFTreeViewItemPtr;
type TArray (line 34) | typedef const TArray<TObjectPtr<UEdGraph>> FEdGraphArray;
function class (line 37) | class FHardReferenceFinderSearchData
FILE: Source/HardReferenceFinder/Public/HardReferenceFinderStyle.h
function class (line 9) | class FHardReferenceFinderStyle
FILE: Source/HardReferenceFinder/Public/HardReferenceFinderSummoner.h
function FWorkflowTabFactory (line 8) | struct FHardReferenceFinderSummoner : public FWorkflowTabFactory
FILE: Source/HardReferenceFinder/Public/SHardReferenceFinderWindow.h
function class (line 12) | class SHardReferenceFinderWindow : public SCompoundWidget
Condensed preview — 15 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (58K chars).
[
{
"path": ".gitignore",
"chars": 595,
"preview": "Binaries\nDerivedDataCache\nIntermediate\nSaved\n.vscode\n.vs\n*.VC.db\n*.opensdf\n*.opendb\n*.sdf\n*.sln\n*.suo\n*.xcodeproj\n*.xcwo"
},
{
"path": "HardReferenceFinder.uplugin",
"chars": 543,
"preview": "{\n\t\"FileVersion\": 3,\n\t\"Version\": 1,\n\t\"VersionName\": \"0.4.0\",\n\t\"FriendlyName\": \"HardReferenceFinder\",\n\t\"Description\": \"Pl"
},
{
"path": "LICENSE",
"chars": 7048,
"preview": "Creative Commons Legal Code\n\nCC0 1.0 Universal\n\n CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE\n"
},
{
"path": "README.md",
"chars": 1395,
"preview": "# Hard Reference Finder\n\nAn open-source editor plugin for Unreal Engine 5 that identifies hard references in a blueprint"
},
{
"path": "Source/HardReferenceFinder/HardReferenceFinder.Build.cs",
"chars": 1500,
"preview": "// Copyright Epic Games, Inc. All Rights Reserved.\n\nusing UnrealBuildTool;\n\npublic class HardReferenceFinder : ModuleRul"
},
{
"path": "Source/HardReferenceFinder/Private/HardReferenceFinder.cpp",
"chars": 1386,
"preview": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#include \"HardReferenceFinder.h\"\n#include \"HardReferenceFinderStyle."
},
{
"path": "Source/HardReferenceFinder/Private/HardReferenceFinderSearchData.cpp",
"chars": 22677,
"preview": "#include \"HardReferenceFinderSearchData.h\"\n#include \"AssetToolsModule.h\"\n#include \"AssetRegistry/AssetRegistryModule.h\""
},
{
"path": "Source/HardReferenceFinder/Private/HardReferenceFinderStyle.cpp",
"chars": 1506,
"preview": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#include \"HardReferenceFinderStyle.h\"\n#include \"Styling/SlateStyleRe"
},
{
"path": "Source/HardReferenceFinder/Private/HardReferenceFinderSummoner.cpp",
"chars": 1375,
"preview": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#include \"HardReferenceFinderSummoner.h\"\n#include \"HardReferenceFind"
},
{
"path": "Source/HardReferenceFinder/Private/SHardReferenceFinderWindow.cpp",
"chars": 8187,
"preview": "\n#include \"SHardReferenceFinderWindow.h\"\n#include \"BlueprintEditor.h\"\n#include \"BlueprintEditorTabs.h\"\n#include \"GraphE"
},
{
"path": "Source/HardReferenceFinder/Public/HardReferenceFinder.h",
"chars": 521,
"preview": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"Modules/ModuleManag"
},
{
"path": "Source/HardReferenceFinder/Public/HardReferenceFinderSearchData.h",
"chars": 3298,
"preview": "#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"AssetRegistry/AssetRegistryModule.h\"\n#include \"Templates/SharedPointer"
},
{
"path": "Source/HardReferenceFinder/Public/HardReferenceFinderStyle.h",
"chars": 569,
"preview": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"Styling/SlateStyle."
},
{
"path": "Source/HardReferenceFinder/Public/HardReferenceFinderSummoner.h",
"chars": 477,
"preview": "// Copyright Epic Games, Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"WorkflowOrientedApp"
},
{
"path": "Source/HardReferenceFinder/Public/SHardReferenceFinderWindow.h",
"chars": 1635,
"preview": "#pragma once\n\n#include \"CoreMinimal.h\"\n#include \"HardReferenceFinderSearchData.h\"\n#include \"Widgets/SCompoundWidget.h\"\n"
}
]
About this extraction
This page contains the full source code of the heyomidk/HardReferenceFinder GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 15 files (51.5 KB), approximately 13.9k tokens, and a symbol index with 27 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.