├── .gitignore ├── LICENSE ├── README.md ├── RefreshAllNodes.uplugin ├── Resources └── Icon128.png ├── Source └── RefreshAllNodes │ ├── Private │ ├── RefreshAllNodes.cpp │ ├── RefreshAllNodesSettings.cpp │ ├── RefreshAllNodesSettings.h │ └── RefreshPluginCommands.cpp │ ├── Public │ ├── RefreshAllNodes.h │ └── RefreshPluginCommands.h │ └── RefreshAllNodes.Build.cs └── docs ├── Configuration.png ├── ContextMenuButton.png ├── Icon512.png └── MenuButton.png /.gitignore: -------------------------------------------------------------------------------- 1 | Intermediate/ 2 | Binaries/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 NachoMonkey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Refresh All Nodes 2 | 3 | *Unreal Engine plugin that refreshes all blueprint nodes in every blueprint* 4 | 5 | **The latest RefreshAllNodes version only supports UE versions 5.1 and later.** 6 | 7 | For use with UE 5.0 and earlier, use the [RefreshAllNodes v1.4 release](https://github.com/nachomonkey/RefreshAllNodes/releases/tag/v1.4%2B1-UE5.0.3) 8 | 9 | 10 | 11 | ## Installation 12 | 13 | ### Pre-compiled 14 | 15 | *The pre-compiled releases of this plugin are compiled running Windows 10 (64-bit).* 16 | 17 | * Download a release that matches your version of Unreal Engine [from here](https://github.com/nachomonkey/RefreshAllNodes/releases), and extract it into your project's Plugins directory. 18 | 19 | * Relaunch the editor. 20 | 21 | ### Compiling from source 22 | 23 | * Clone or download the repository into your project's `Plugins` directory and relaunch the editor. 24 | 25 | * A message should appear asking to compile the plugin. 26 | 27 | ## Usage 28 | 29 | This plugin adds the **`Refresh All Blueprint Nodes`** button to the Blueprints toolbar menu: 30 | 31 | ![The button is shown in the Blueprints toolbar menu](docs/MenuButton.png) 32 | 33 | As stated, the button will search for your Blueprints, open, them, and excecute the built-in "Refresh all nodes" function. By default, all blueprints are then compiled. 34 | 35 | The **`Refresh Blueprints`** action can be found in the Content Browser's context menu to refresh blueprints in certain folders: 36 | 37 | ![The button is shown in the Content Browser context menu](docs/ContextMenuButton.png) 38 | 39 | * *Note: Refreshing blueprint nodes may cause node breakages or change variable types, especially in certain circumstances following a `HotReload`.* 40 | * *Note: I have not used RefreshAllNodes with Source Control, so it may cause undesired behavior by resaving every blueprint.* 41 | 42 | 43 | ### Configuration 44 | 45 | The plugin can be configured under **`Project Settings` -> `Plugins` -> `Refresh All Nodes`** 46 | 47 | ![Configuration](docs/Configuration.png) 48 | 49 | * Compile Blueprints: If checked, the plugin will compiled the blueprints after refreshing them. Enabling compilation will allow the plugin to catch errors in the blueprints, but it will take more time to process. 50 | 51 | * Refresh Level Blueprints: If checked, the plugin will search for level blueprints. This will cause the corresponding levels to be opened and saved, which can consume extra memory. 52 | * Refresh Game Blueprints: If checked, the plugin will refresh blueprints found in the project's Content folder. 53 | * Refresh Engine Blueprints: If checked, the plugin will refresh blueprints found in the engine's Content folder. 54 | * Additional Blueprint Paths: Array of additional paths to search in. Most useful for plugins. Add the name of the plugin to refresh its blueprints. 55 | * Exclude Blueprint Paths: Array of paths to not refresh blueprints in. Useful for excluding blueprints that are expensive to load. 56 | 57 | ## Limitations 58 | 59 | This plugin's ability to refresh nodes is limited to Unreal Engine's built-in "Refresh All Nodes" function. The purpose of this plugin is only to provide an easy way to perform such action on Blueprint assets. This plugin is not responsible for any damage to Blueprints or any data loss. 60 | 61 | # License 62 | 63 | RefreshAllNodes is free software, licensed under the MIT License as contained in the [LICENSE](LICENSE) file. 64 | -------------------------------------------------------------------------------- /RefreshAllNodes.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 5, 4 | "VersionName": "1.5", 5 | "FriendlyName": "RefreshAllNodes", 6 | "Description": "Editor plugin that adds ways to refresh and compile blueprints as a batch operation", 7 | "Category": "Blueprints", 8 | "CreatedBy": "NachoMonkey", 9 | "CreatedByURL": "https://github.com/nachomonkey", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": false, 14 | "IsBetaVersion": false, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "Modules": [ 18 | { 19 | "Name": "RefreshAllNodes", 20 | "Type": "Editor", 21 | "LoadingPhase": "PostEngineInit", 22 | "AdditionalDependencies": [ 23 | "Engine", 24 | "CoreUObject" 25 | ] 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachomonkey/RefreshAllNodes/2f40bb9320095600b6e5a3b756b645745c2a4404/Resources/Icon128.png -------------------------------------------------------------------------------- /Source/RefreshAllNodes/Private/RefreshAllNodes.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "RefreshAllNodes.h" 4 | #include "RefreshAllNodesSettings.h" 5 | #include "RefreshPluginCommands.h" 6 | #include "Framework/Commands/Commands.h" 7 | #include "AssetRegistry/AssetRegistryModule.h" 8 | #include "ContentBrowserModule.h" 9 | #include "IContentBrowserSingleton.h" 10 | #include "SlateBasics.h" 11 | #include "Framework/MultiBox/MultiBoxExtender.h" 12 | #include "Kismet2/KismetEditorUtilities.h" 13 | #include "Kismet2/BlueprintEditorUtils.h" 14 | #include "Kismet2/CompilerResultsLog.h" 15 | #include "Kismet/KismetStringLibrary.h" 16 | #include "PackageTools.h" 17 | #include "Engine/Blueprint.h" 18 | #include "Engine/LevelScriptBlueprint.h" 19 | #include "FileHelpers.h" 20 | 21 | #include "Dialog/SCustomDialog.h" 22 | #include "Widgets/Input/SHyperlink.h" 23 | 24 | #include "Framework/Notifications/NotificationManager.h" 25 | #include "Widgets/Notifications/SNotificationList.h" 26 | 27 | DEFINE_LOG_CATEGORY(LogRefreshAllNodes); 28 | 29 | // Formats text, accounts for proper grammar with plurals 30 | #define BLUEPRINTS_TEXT(x) FText::Format(FText::FromString("{0} blueprint{1}"), x, (x == 1) ? FText::GetEmpty() : FText::FromString("s")) 31 | 32 | 33 | // Create a dialog that lists the blueprints with errors and lets them be opened 34 | static void ShowProblemBlueprintsDialog(TArray ErroredBlueprints) { 35 | struct Local { 36 | static void OnHyperlinkClicked( TWeakObjectPtr InBlueprint, TSharedPtr InDialog ) { 37 | if (UBlueprint* BlueprintToEdit = InBlueprint.Get()) { 38 | GEditor->EditObject(BlueprintToEdit); 39 | } 40 | 41 | if (InDialog.IsValid()) { 42 | // Opening the blueprint editor above may end up creating an invisible new window on top of the dialog, 43 | // thus making it not interactable, so we have to force the dialog back to the front 44 | InDialog->BringToFront(true); 45 | } 46 | } 47 | }; 48 | 49 | TSharedRef DialogContents = SNew(SVerticalBox) 50 | + SVerticalBox::Slot() 51 | .Padding(0, 0, 0, 16) 52 | [ 53 | SNew(STextBlock) 54 | .Text(FText::FromString("The following blueprints failed to compile:")) 55 | ]; 56 | 57 | TSharedPtr CustomDialog; 58 | 59 | for (UBlueprint* Blueprint : ErroredBlueprints) { 60 | TWeakObjectPtr BlueprintPtr = Blueprint; 61 | 62 | DialogContents->AddSlot() 63 | .AutoHeight() 64 | .HAlign(HAlign_Left) 65 | [ 66 | SNew(SHyperlink) 67 | .Style(FAppStyle::Get(), "Common.GotoBlueprintHyperlink") 68 | .OnNavigate(FSimpleDelegate::CreateLambda([BlueprintPtr, &CustomDialog]() { Local::OnHyperlinkClicked(BlueprintPtr, CustomDialog); })) 69 | .Text(FText::FromString(Blueprint->GetName())) 70 | .ToolTipText(NSLOCTEXT("SourceHyperlink", "EditBlueprint_ToolTip", "Click to edit the blueprint")) 71 | ]; 72 | } 73 | 74 | DialogContents->AddSlot() 75 | .Padding(0, 16, 0, 0) 76 | [ 77 | SNew(STextBlock) 78 | .Text(FText::FromString("Clicked blueprints will open once this dialog is closed.")) 79 | ]; 80 | 81 | CustomDialog = SNew(SCustomDialog) 82 | .Title(FText::FromString("Blueprint Compilation Errors")) 83 | .Icon(FAppStyle::Get().GetBrush("NotificationList.DefaultMessage")) 84 | .Content() [ DialogContents ] 85 | .Buttons( { SCustomDialog::FButton(FText::FromString("Dismiss")) } ); 86 | CustomDialog->ShowModal(); 87 | } 88 | 89 | void FRefreshAllNodesModule::StartupModule() { 90 | FRefreshPluginCommands::Register(); 91 | 92 | RegisterLevelEditorButton(); 93 | RegisterPathViewContextMenuButton(); 94 | } 95 | 96 | void FRefreshAllNodesModule::ShutdownModule() { 97 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 98 | // we call this function before unloading the module. 99 | 100 | LevelEditorExtender->RemoveExtension(LevelEditorExtension.ToSharedRef()); 101 | LevelEditorExtension.Reset(); 102 | ContentBrowserExtension.Reset(); 103 | LevelEditorExtender.Reset(); 104 | } 105 | 106 | void FRefreshAllNodesModule::RegisterLevelEditorButton() { 107 | TSharedPtr CommandList = MakeShareable(new FUICommandList()); 108 | 109 | CommandList->MapAction(FRefreshPluginCommands::Get().RefreshAllButton, FExecuteAction::CreateRaw(this, &FRefreshAllNodesModule::RefreshAllButton_Clicked), FCanExecuteAction()); 110 | 111 | LevelEditorExtender = MakeShareable(new FExtender()); 112 | FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); 113 | 114 | LevelEditorExtension = LevelEditorExtender->AddMenuExtension("WorldSettingsClasses", EExtensionHook::After, CommandList, FMenuExtensionDelegate::CreateRaw(this, &FRefreshAllNodesModule::AddLevelEditorMenuEntry)); 115 | 116 | auto& MenuExtenders = LevelEditorModule.GetAllLevelEditorToolbarBlueprintsMenuExtenders(); 117 | MenuExtenders.Add(LevelEditorExtender); 118 | 119 | LevelEditorModule.GetGlobalLevelEditorActions()->Append(CommandList.ToSharedRef()); 120 | } 121 | 122 | void FRefreshAllNodesModule::RegisterPathViewContextMenuButton() { 123 | FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); 124 | 125 | ContentBrowserModule.GetAllPathViewContextMenuExtenders().Add(FContentBrowserMenuExtender_SelectedPaths::CreateRaw(this, &FRefreshAllNodesModule::CreateContentBrowserExtender)); 126 | } 127 | 128 | TSharedRef FRefreshAllNodesModule::CreateContentBrowserExtender(const TArray& SelectedPaths) { 129 | SelectedFolders = SelectedPaths; 130 | 131 | TSharedPtr CommandList = MakeShareable(new FUICommandList()); 132 | CommandList->MapAction(FRefreshPluginCommands::Get().RefreshPathButton, FExecuteAction::CreateRaw(this, &FRefreshAllNodesModule::RefreshPathButton_Clicked), FCanExecuteAction()); 133 | 134 | TSharedPtr ContentBrowserExtender = MakeShareable(new FExtender()); 135 | 136 | ContentBrowserExtension = ContentBrowserExtender->AddMenuExtension("PathViewFolderOptions", EExtensionHook::After, CommandList, FMenuExtensionDelegate::CreateRaw(this, &FRefreshAllNodesModule::AddPathViewContextMenuEntry)); 137 | return ContentBrowserExtender.ToSharedRef(); 138 | } 139 | 140 | void FRefreshAllNodesModule::AddLevelEditorMenuEntry(FMenuBuilder &Builder) { 141 | FSlateIcon IconBrush = FSlateIcon(FAppStyle::GetAppStyleSetName(), "EditorViewport.RotateMode"); 142 | 143 | Builder.BeginSection("RefreshBlueprints", FText::FromString("Refresh All Nodes")); 144 | Builder.AddMenuEntry(FRefreshPluginCommands::Get().RefreshAllButton, FName(""), FText::FromString("Refresh All Blueprint Nodes"), FText::FromString("Refresh all nodes in every blueprint"), IconBrush); 145 | Builder.EndSection(); 146 | } 147 | 148 | void FRefreshAllNodesModule::AddPathViewContextMenuEntry(FMenuBuilder& Builder) { 149 | FSlateIcon IconBrush = FSlateIcon(FAppStyle::GetAppStyleSetName(), "EditorViewport.RotateMode"); 150 | 151 | Builder.AddMenuEntry(FRefreshPluginCommands::Get().RefreshPathButton, FName(""), FText::FromString("Refresh Blueprints"), FText::FromString("Refresh all nodes in blueprints under this folder"), IconBrush); 152 | } 153 | 154 | 155 | void FRefreshAllNodesModule::RefreshPathButton_Clicked() { 156 | // This function is called when the button in the Content Browser right-click context menu is pressed 157 | 158 | FARFilter Filter; 159 | Filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName()); 160 | Filter.bRecursiveClasses = true; 161 | Filter.bRecursivePaths = true; 162 | 163 | const URefreshAllNodesSettings* Settings = GetDefault(); 164 | if (Settings->bRefreshLevelBlueprints) { // Search for UWorld objects if we're searching for level blueprints 165 | Filter.ClassPaths.Add(UWorld::StaticClass()->GetClassPathName()); 166 | } 167 | 168 | for (const FString& FolderPath : SelectedFolders) { 169 | Filter.PackagePaths.Add(*FolderPath); 170 | } 171 | 172 | FindAndRefreshBlueprints(Filter, false); 173 | } 174 | 175 | void FRefreshAllNodesModule::RefreshAllButton_Clicked() { 176 | // This function is called when the main "Refresh All Blueprint Nodes" button in pressed 177 | 178 | FARFilter Filter; 179 | Filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName()); 180 | Filter.bRecursiveClasses = true; 181 | Filter.bRecursivePaths = true; 182 | 183 | const URefreshAllNodesSettings* Settings = GetDefault(); 184 | 185 | for (FName Path : Settings->AdditionalBlueprintPaths) { 186 | Filter.PackagePaths.Add(UKismetStringLibrary::Conv_StringToName("/" + Path.ToString())); 187 | } 188 | 189 | if (Settings->bRefreshLevelBlueprints) { // Search for UWorld objects if we're searching for level blueprints 190 | Filter.ClassPaths.Add(UWorld::StaticClass()->GetClassPathName()); 191 | } if (Settings->bRefreshGameBlueprints) { 192 | Filter.PackagePaths.Add("/Game"); 193 | } if (Settings->bRefreshEngineBlueprints) { 194 | Filter.PackagePaths.Add("/Engine"); 195 | } 196 | 197 | FindAndRefreshBlueprints(Filter); 198 | } 199 | 200 | void FRefreshAllNodesModule::FindAndRefreshBlueprints(const FARFilter& Filter, bool bShouldExclude) { 201 | const URefreshAllNodesSettings* Settings = GetDefault(); 202 | 203 | TArray AssetData; 204 | TArray PackagesToSave; 205 | 206 | // ProblemBlueprints is filled with blueprints when there are errors, but only emptied here... 207 | // It really should be emptied after the ProblemNotification fades out. 208 | ProblemBlueprints.Empty(); 209 | 210 | FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); 211 | 212 | // Search for applicable assets (UBlueprints, possibly UWorlds) 213 | AssetRegistryModule.Get().GetAssets(Filter, AssetData); 214 | int NumAssets = AssetData.Num(); 215 | 216 | TSharedPtr RefreshingNotification; 217 | 218 | // Create different popups depending on whether there are blueprints to refresh or not 219 | if (NumAssets) { 220 | FNotificationInfo Info(FText::Format(FText::FromString("Refreshing {0}..."), BLUEPRINTS_TEXT(NumAssets))); 221 | Info.ExpireDuration = 5; 222 | Info.bFireAndForget = false; 223 | RefreshingNotification = FSlateNotificationManager::Get().AddNotification(Info); 224 | RefreshingNotification->SetCompletionState(SNotificationItem::CS_Pending); 225 | } else { 226 | FNotificationInfo Info(FText::FromString("No blueprints were refreshed")); 227 | Info.ExpireDuration = 1.5f; 228 | 229 | FSlateNotificationManager::Get().AddNotification(Info); 230 | } 231 | 232 | // Loop through the assets, get to the blueprints, and refresh them 233 | for (FAssetData Data : AssetData) { 234 | bool bShouldSkip = false; 235 | 236 | FString AssetPathString = Data.GetObjectPathString(); 237 | 238 | // Skip if this is in an excluded path and we are refreshing all blueprints 239 | if (bShouldExclude) { 240 | for (FName Path : Settings->ExcludeBlueprintPaths) { 241 | if (AssetPathString.StartsWith(Path.ToString(), ESearchCase::CaseSensitive)) { 242 | bShouldSkip = true; 243 | break; 244 | } 245 | } 246 | } 247 | if (bShouldSkip) { 248 | continue; 249 | } 250 | 251 | TWeakObjectPtr Blueprint = Cast(Data.GetAsset()); 252 | 253 | // Try casting to a UWorld (to get level blueprint) 254 | if (Blueprint == nullptr) { 255 | TWeakObjectPtr World = Cast(Data.GetAsset()); 256 | if (World != nullptr) { 257 | TWeakObjectPtr Level = World->GetCurrentLevel(); 258 | 259 | if (Level != nullptr) { 260 | // Use the level blueprint 261 | Blueprint = Level->GetLevelScriptBlueprint(true); 262 | } 263 | } 264 | } 265 | 266 | // Skip if there is no blueprint 267 | if (Blueprint == nullptr) { 268 | continue; 269 | } 270 | 271 | UE_LOG(LogRefreshAllNodes, Display, TEXT("Refreshing Blueprint: %s"), *AssetPathString); 272 | 273 | // Refresh all nodes in this blueprint 274 | FBlueprintEditorUtils::RefreshAllNodes(Blueprint.Get()); 275 | 276 | if (Settings->bCompileBlueprints) { 277 | 278 | // Compile blueprint 279 | UE_LOG(LogRefreshAllNodes, Display, TEXT("Compiling Blueprint: %s"), *AssetPathString); 280 | FKismetEditorUtilities::CompileBlueprint(Blueprint.Get(), EBlueprintCompileOptions::BatchCompile | EBlueprintCompileOptions::SkipSave); 281 | 282 | // Check if the blueprint failed to compile 283 | if (!Blueprint->IsUpToDate() && Blueprint->Status != BS_Unknown) { 284 | UE_LOG(LogRefreshAllNodes, Error, TEXT("Failed to compile %s"), *AssetPathString); 285 | ProblemBlueprints.Add(Blueprint.Get()); 286 | } 287 | } 288 | 289 | PackagesToSave.Add(Data.GetPackage()); 290 | } 291 | 292 | // Save the refreshed blueprints 293 | bool bSuccess = UEditorLoadingAndSavingUtils::SavePackages(PackagesToSave, true); 294 | 295 | // If the saving fails, log and error and raise a notification 296 | if (!bSuccess) { 297 | UE_LOG(LogRefreshAllNodes, Error, TEXT("Failed to save packages")); 298 | FNotificationInfo Info(FText::FromString("Failed to save packages")); 299 | Info.ExpireDuration = 10.f; 300 | 301 | FSlateNotificationManager::Get().AddNotification(Info)->SetCompletionState(SNotificationItem::CS_Fail); 302 | } 303 | 304 | // Set the popup to "success" state 305 | if (RefreshingNotification.IsValid()) { 306 | RefreshingNotification->SetText(FText::Format(FText::FromString("Refreshed {0}"), BLUEPRINTS_TEXT(NumAssets))); 307 | RefreshingNotification->SetCompletionState(SNotificationItem::CS_Success); 308 | RefreshingNotification->ExpireAndFadeout(); 309 | } 310 | 311 | // If there were errors in compilation, create a new popup with an option to see which blueprints failed to compile 312 | if (ProblemBlueprints.Num()) { 313 | auto ShowBlueprints = [this] 314 | { 315 | if (ProblemBlueprints.Num()) { 316 | ShowProblemBlueprintsDialog(ProblemBlueprints); 317 | } 318 | }; 319 | 320 | FNotificationInfo Info(FText::Format(FText::FromString("{0} failed to compile"), BLUEPRINTS_TEXT(ProblemBlueprints.Num()))); 321 | Info.ExpireDuration = 15; 322 | Info.Image = FAppStyle::GetBrush("Icons.Warning"); 323 | 324 | TSharedPtr ProblemNotification; 325 | ProblemNotification = FSlateNotificationManager::Get().AddNotification(Info); 326 | ProblemNotification->SetHyperlink(FSimpleDelegate::CreateLambda(ShowBlueprints), FText::FromString("Show blueprints")); 327 | } 328 | } 329 | 330 | IMPLEMENT_MODULE(FRefreshAllNodesModule, RefreshAllNodes) 331 | -------------------------------------------------------------------------------- /Source/RefreshAllNodes/Private/RefreshAllNodesSettings.cpp: -------------------------------------------------------------------------------- 1 | #include "RefreshAllNodesSettings.h" 2 | 3 | #define LOCTEXT_NAMESPACE "RefreshAllNodes" 4 | 5 | URefreshAllNodesSettings::URefreshAllNodesSettings() { 6 | CategoryName = TEXT("Plugins"); 7 | SectionName = TEXT("Refresh All Nodes"); 8 | 9 | bRefreshGameBlueprints = true; 10 | bCompileBlueprints = true; 11 | } 12 | 13 | #if WITH_EDITOR 14 | 15 | FText URefreshAllNodesSettings::GetSectionText() const { 16 | return LOCTEXT("SettingsDisplayName", "Refresh All Nodes"); 17 | } 18 | 19 | #endif 20 | 21 | #undef LOCTEXT_NAMESPACE 22 | -------------------------------------------------------------------------------- /Source/RefreshAllNodes/Private/RefreshAllNodesSettings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "Engine/EngineTypes.h" 5 | #include "Engine/DeveloperSettings.h" 6 | #include "RefreshAllNodesSettings.generated.h" 7 | 8 | /** 9 | * Configure the Refresh All Nodes plugin 10 | */ 11 | UCLASS(config=Engine, defaultconfig) 12 | class URefreshAllNodesSettings : public UDeveloperSettings 13 | { 14 | GENERATED_BODY() 15 | 16 | public: 17 | URefreshAllNodesSettings(); 18 | 19 | #if WITH_EDITOR 20 | virtual FText GetSectionText() const override; 21 | #endif 22 | /** Determines whether blueprints should be compiled after being refreshed. 23 | * Enabling compilation will allow the plugin to catch errors in the blueprints, 24 | * but it will take more time to process. 25 | */ 26 | UPROPERTY(config, EditAnywhere, Category=Behavior) 27 | bool bCompileBlueprints; 28 | 29 | /** Should the plugin refresh level blueprints? */ 30 | UPROPERTY(config, EditAnywhere, Category=Search) 31 | bool bRefreshLevelBlueprints; 32 | 33 | /** Should the plugin refresh blueprints in this project's game content folder? */ 34 | UPROPERTY(config, EditAnywhere, Category=Search) 35 | bool bRefreshGameBlueprints; 36 | 37 | /** Should the plugin refresh blueprints in the engine's content folder? */ 38 | UPROPERTY(config, EditAnywhere, Category=Search) 39 | bool bRefreshEngineBlueprints; 40 | 41 | /** 42 | * Additional paths to search in for blueprints to refresh, good for plugins 43 | * Example: Add "Paper2D" to not search the Paper2D plugin for blueprints to refresh. 44 | */ 45 | UPROPERTY(config, EditAnywhere, Category=Search) 46 | TArray AdditionalBlueprintPaths; 47 | 48 | /** 49 | * Blueprint paths to not search in for blueprints to refresh, good for 50 | * blueprints with little dependencies that take lots of resources to 51 | * load. Example: Add "/Game/Marketplace" to not search the "Marketplace" 52 | * directory in the Content folder. 53 | */ 54 | UPROPERTY(config, EditAnywhere, Category=Search) 55 | TArray ExcludeBlueprintPaths; 56 | }; 57 | -------------------------------------------------------------------------------- /Source/RefreshAllNodes/Private/RefreshPluginCommands.cpp: -------------------------------------------------------------------------------- 1 | #include "RefreshPluginCommands.h" 2 | #include "RefreshAllNodes.h" 3 | 4 | void FRefreshPluginCommands::RegisterCommands() { 5 | #define LOCTEXT_NAMESPACE "" 6 | UI_COMMAND(RefreshAllButton, "Refresh All Blueprint Nodes", "Refresh all nodes in every blueprint", EUserInterfaceActionType::Button, FInputChord()); 7 | UI_COMMAND(RefreshPathButton, "Refresh Blueprints", "Refresh all nodes in blueprints under this folder", EUserInterfaceActionType::Button, FInputChord()); 8 | #undef LOCTEXT_NAMESPACE 9 | } 10 | -------------------------------------------------------------------------------- /Source/RefreshAllNodes/Public/RefreshAllNodes.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | #include "ContentBrowserDelegates.h" 8 | #include "LevelEditor.h" 9 | 10 | DECLARE_LOG_CATEGORY_EXTERN(LogRefreshAllNodes, Log, All); 11 | 12 | #define LOCTEXT_NAMESPACE "RefreshAllNodes" 13 | 14 | class FRefreshAllNodesModule : public IModuleInterface 15 | { 16 | public: 17 | 18 | /** IModuleInterface implementation */ 19 | virtual void StartupModule() override; 20 | virtual void ShutdownModule() override; 21 | 22 | TSharedPtr LevelEditorExtender; 23 | TSharedPtr LevelEditorExtension; 24 | TSharedPtr ContentBrowserExtension; 25 | 26 | TArray SelectedFolders; 27 | 28 | UPROPERTY() 29 | TArray ProblemBlueprints; 30 | 31 | void RegisterLevelEditorButton(); 32 | void RegisterPathViewContextMenuButton(); 33 | 34 | TSharedRef CreateContentBrowserExtender(const TArray& SelectedPaths); 35 | 36 | void RefreshAllButton_Clicked(); 37 | void RefreshPathButton_Clicked(); 38 | 39 | void FindAndRefreshBlueprints(const FARFilter& Filter, bool bShouldExclude = true); 40 | 41 | void AddLevelEditorMenuEntry(FMenuBuilder &Builder); 42 | void AddPathViewContextMenuEntry(FMenuBuilder &Builder); 43 | }; 44 | 45 | #undef LOCTEXT_NAMESPACE 46 | -------------------------------------------------------------------------------- /Source/RefreshAllNodes/Public/RefreshPluginCommands.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Framework/Commands/Commands.h" 4 | #include "EditorStyleSet.h" 5 | 6 | class FRefreshPluginCommands : public TCommands { 7 | 8 | public: 9 | FRefreshPluginCommands() 10 | : TCommands(FName(TEXT("RefreshPlugin")), FText::FromString("RefreshAllNodes Commands"), NAME_None, FAppStyle::GetAppStyleSetName()) 11 | { 12 | }; 13 | 14 | virtual void RegisterCommands() override; 15 | 16 | TSharedPtr RefreshAllButton; 17 | TSharedPtr RefreshPathButton; 18 | }; 19 | -------------------------------------------------------------------------------- /Source/RefreshAllNodes/RefreshAllNodes.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class RefreshAllNodes : ModuleRules 6 | { 7 | public RefreshAllNodes(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange(new string[] { 12 | "Core", "CoreUObject", "Engine", "InputCore", "ContentBrowser"}); 13 | 14 | PrivateDependencyModuleNames.AddRange(new string[] { 15 | "UnrealEd", "Slate", "SlateCore", "EditorStyle", "ToolWidgets", "DeveloperSettings"}); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /docs/Configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachomonkey/RefreshAllNodes/2f40bb9320095600b6e5a3b756b645745c2a4404/docs/Configuration.png -------------------------------------------------------------------------------- /docs/ContextMenuButton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachomonkey/RefreshAllNodes/2f40bb9320095600b6e5a3b756b645745c2a4404/docs/ContextMenuButton.png -------------------------------------------------------------------------------- /docs/Icon512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachomonkey/RefreshAllNodes/2f40bb9320095600b6e5a3b756b645745c2a4404/docs/Icon512.png -------------------------------------------------------------------------------- /docs/MenuButton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nachomonkey/RefreshAllNodes/2f40bb9320095600b6e5a3b756b645745c2a4404/docs/MenuButton.png --------------------------------------------------------------------------------