1094 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			1094 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
								 | 
							
								#include "UnrealSharpEditor.h"
							 | 
						|||
| 
								 | 
							
								#include "AssetToolsModule.h"
							 | 
						|||
| 
								 | 
							
								#include "BlueprintCompilationManager.h"
							 | 
						|||
| 
								 | 
							
								#include "BlueprintEditorLibrary.h"
							 | 
						|||
| 
								 | 
							
								#include "CSUnrealSharpEditorCommands.h"
							 | 
						|||
| 
								 | 
							
								#include "DirectoryWatcherModule.h"
							 | 
						|||
| 
								 | 
							
								#include "CSStyle.h"
							 | 
						|||
| 
								 | 
							
								#include "CSUnrealSharpEditorSettings.h"
							 | 
						|||
| 
								 | 
							
								#include "DesktopPlatformModule.h"
							 | 
						|||
| 
								 | 
							
								#include "IDirectoryWatcher.h"
							 | 
						|||
| 
								 | 
							
								#include "IPluginBrowser.h"
							 | 
						|||
| 
								 | 
							
								#include "ISettingsModule.h"
							 | 
						|||
| 
								 | 
							
								#include "LevelEditor.h"
							 | 
						|||
| 
								 | 
							
								#include "SourceCodeNavigation.h"
							 | 
						|||
| 
								 | 
							
								#include "SubobjectDataSubsystem.h"
							 | 
						|||
| 
								 | 
							
								#include "UnrealSharpRuntimeGlue.h"
							 | 
						|||
| 
								 | 
							
								#include "AssetActions/CSAssetTypeAction_CSBlueprint.h"
							 | 
						|||
| 
								 | 
							
								#include "Engine/AssetManager.h"
							 | 
						|||
| 
								 | 
							
								#include "Engine/InheritableComponentHandler.h"
							 | 
						|||
| 
								 | 
							
								#include "Features/IPluginsEditorFeature.h"
							 | 
						|||
| 
								 | 
							
								#include "UnrealSharpCore/CSManager.h"
							 | 
						|||
| 
								 | 
							
								#include "Framework/Notifications/NotificationManager.h"
							 | 
						|||
| 
								 | 
							
								#include "Interfaces/IMainFrameModule.h"
							 | 
						|||
| 
								 | 
							
								#include "Interfaces/IPluginManager.h"
							 | 
						|||
| 
								 | 
							
								#include "Kismet2/BlueprintEditorUtils.h"
							 | 
						|||
| 
								 | 
							
								#include "Kismet2/DebuggerCommands.h"
							 | 
						|||
| 
								 | 
							
								#include "Logging/StructuredLog.h"
							 | 
						|||
| 
								 | 
							
								#include "Misc/ScopedSlowTask.h"
							 | 
						|||
| 
								 | 
							
								#include "Plugins/CSPluginTemplateDescription.h"
							 | 
						|||
| 
								 | 
							
								#include "Slate/CSNewProjectWizard.h"
							 | 
						|||
| 
								 | 
							
								#include "TypeGenerator/Register/CSGeneratedClassBuilder.h"
							 | 
						|||
| 
								 | 
							
								#include "UnrealSharpProcHelper/CSProcHelper.h"
							 | 
						|||
| 
								 | 
							
								#include "Widgets/Notifications/SNotificationList.h"
							 | 
						|||
| 
								 | 
							
								#include "TypeGenerator/CSClass.h"
							 | 
						|||
| 
								 | 
							
								#include "TypeGenerator/CSEnum.h"
							 | 
						|||
| 
								 | 
							
								#include "TypeGenerator/CSScriptStruct.h"
							 | 
						|||
| 
								 | 
							
								#include "UnrealSharpUtilities/UnrealSharpUtils.h"
							 | 
						|||
| 
								 | 
							
								#include "Utils/CSClassUtilities.h"
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#define LOCTEXT_NAMESPACE "FUnrealSharpEditorModule"
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								DEFINE_LOG_CATEGORY(LogUnrealSharpEditor);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								FUnrealSharpEditorModule& FUnrealSharpEditorModule::Get()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									return FModuleManager::LoadModuleChecked<FUnrealSharpEditorModule>("UnrealSharpEditor");
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::StartupModule()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
							 | 
						|||
| 
								 | 
							
									AssetTools.RegisterAssetTypeActions(MakeShared<FCSAssetTypeAction_CSBlueprint>());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									TArray<FString> ProjectPaths;
							 | 
						|||
| 
								 | 
							
									FCSProcHelper::GetAllProjectPaths(ProjectPaths);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									for (const FString& ProjectPath : ProjectPaths)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FString Path = FPaths::GetPath(ProjectPath);
							 | 
						|||
| 
								 | 
							
										AddDirectoryToWatch(Path);
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									Manager = &UCSManager::GetOrCreate();
							 | 
						|||
| 
								 | 
							
									Manager->OnNewStructEvent().AddRaw(this, &FUnrealSharpEditorModule::OnStructRebuilt);
							 | 
						|||
| 
								 | 
							
									Manager->OnNewClassEvent().AddRaw(this, &FUnrealSharpEditorModule::OnClassRebuilt);
							 | 
						|||
| 
								 | 
							
									Manager->OnNewEnumEvent().AddRaw(this, &FUnrealSharpEditorModule::OnEnumRebuilt);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FEditorDelegates::ShutdownPIE.AddRaw(this, &FUnrealSharpEditorModule::OnPIEShutdown);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									TickDelegate = FTickerDelegate::CreateRaw(this, &FUnrealSharpEditorModule::Tick);
							 | 
						|||
| 
								 | 
							
									TickDelegateHandle = FTSTicker::GetCoreTicker().AddTicker(TickDelegate);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (ProjectPaths.IsEmpty())
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										IMainFrameModule::Get().OnMainFrameCreationFinished().AddLambda([this](TSharedPtr<SWindow>, bool)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											SuggestProjectSetup();
							 | 
						|||
| 
								 | 
							
										});
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									// Make managed types not available for edit in the editor
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools"));
							 | 
						|||
| 
								 | 
							
										IAssetTools& AssetToolsRef = AssetToolsModule.Get();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										Manager->ForEachManagedPackage([&AssetToolsRef](const UPackage* Package)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											AssetToolsRef.GetWritableFolderPermissionList()->AddDenyListItem(Package->GetFName(), Package->GetFName());
							 | 
						|||
| 
								 | 
							
										});
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FCSStyle::Initialize();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									RegisterCommands();
							 | 
						|||
| 
								 | 
							
									RegisterMenu();
							 | 
						|||
| 
								 | 
							
								    RegisterPluginTemplates();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									UCSManager& CSharpManager = UCSManager::Get();
							 | 
						|||
| 
								 | 
							
									CSharpManager.LoadPluginAssemblyByName(TEXT("UnrealSharp.Editor"));
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::ShutdownModule()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									FTSTicker::GetCoreTicker().RemoveTicker(TickDelegateHandle);
							 | 
						|||
| 
								 | 
							
									UToolMenus::UnRegisterStartupCallback(this);
							 | 
						|||
| 
								 | 
							
									UToolMenus::UnregisterOwner(this);
							 | 
						|||
| 
								 | 
							
								    UnregisterPluginTemplates();
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnCSharpCodeModified(const TArray<FFileChangeData>& ChangedFiles)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									if (IsHotReloading())
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									const UCSUnrealSharpEditorSettings* Settings = GetDefault<UCSUnrealSharpEditorSettings>();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (FPlayWorldCommandCallbacks::IsInPIE() && Settings->AutomaticHotReloading == OnScriptSave)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										bHasQueuedHotReload = true;
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									for (const FFileChangeData& ChangedFile : ChangedFiles)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FString NormalizedFileName = ChangedFile.Filename;
							 | 
						|||
| 
								 | 
							
										FPaths::NormalizeFilename(NormalizedFileName);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										// Skip ProjectGlue files
							 | 
						|||
| 
								 | 
							
										if (NormalizedFileName.Contains("Glue"))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											continue;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										// Skip generated files in bin and obj folders
							 | 
						|||
| 
								 | 
							
										if (NormalizedFileName.Contains(TEXT("/obj/")))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											continue;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										if (Settings->AutomaticHotReloading == OnModuleChange && NormalizedFileName.EndsWith(".dll") &&
							 | 
						|||
| 
								 | 
							
											NormalizedFileName.Contains(TEXT("/bin/")))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											// A module changed, initiate the reload and return
							 | 
						|||
| 
								 | 
							
											StartHotReload(false);
							 | 
						|||
| 
								 | 
							
											return;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										// Check if the file is a .cs file and not in the bin directory
							 | 
						|||
| 
								 | 
							
										FString Extension = FPaths::GetExtension(NormalizedFileName);
							 | 
						|||
| 
								 | 
							
										if (Extension != "cs" || NormalizedFileName.Contains(TEXT("/bin/")))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											continue;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										// Return on the first .cs file we encounter so we can reload.
							 | 
						|||
| 
								 | 
							
										if (Settings->AutomaticHotReloading != OnScriptSave)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											HotReloadStatus = PendingReload;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
										else
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											StartHotReload(true);
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::StartHotReload(bool bRebuild, bool bPromptPlayerWithNewProject)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									if (HotReloadStatus == FailedToUnload)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										// If we failed to unload an assembly, we can't hot reload until the editor is restarted.
							 | 
						|||
| 
								 | 
							
										bHotReloadFailed = true;
							 | 
						|||
| 
								 | 
							
										UE_LOGFMT(LogUnrealSharpEditor, Error, "Hot reload is disabled until the editor is restarted.");
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									TArray<FString> AllProjects;
							 | 
						|||
| 
								 | 
							
									FCSProcHelper::GetAllProjectPaths(AllProjects);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (AllProjects.IsEmpty())
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										if (bPromptPlayerWithNewProject)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											SuggestProjectSetup();
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									HotReloadStatus = Active;
							 | 
						|||
| 
								 | 
							
									double StartTime = FPlatformTime::Seconds();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FScopedSlowTask Progress(3, LOCTEXT("HotReload", "Reloading C#..."));
							 | 
						|||
| 
								 | 
							
									Progress.MakeDialog();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FString SolutionPath = FCSProcHelper::GetPathToSolution();
							 | 
						|||
| 
								 | 
							
									FString OutputPath = FCSProcHelper::GetUserAssemblyDirectory();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									const UCSUnrealSharpEditorSettings* Settings = GetDefault<UCSUnrealSharpEditorSettings>();
							 | 
						|||
| 
								 | 
							
									FString BuildConfiguration = Settings->GetBuildConfigurationString();
							 | 
						|||
| 
								 | 
							
									ECSLoggerVerbosity LogVerbosity = Settings->LogVerbosity;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FString ExceptionMessage;
							 | 
						|||
| 
								 | 
							
									if (!ManagedUnrealSharpEditorCallbacks.Build(*SolutionPath, *OutputPath, *BuildConfiguration, LogVerbosity, &ExceptionMessage, bRebuild))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
									 	HotReloadStatus = Inactive;
							 | 
						|||
| 
								 | 
							
										bHotReloadFailed = true;
							 | 
						|||
| 
								 | 
							
									 	FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(ExceptionMessage), FText::FromString(TEXT("Building C# Project Failed")));
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									UCSManager& CSharpManager = UCSManager::Get();
							 | 
						|||
| 
								 | 
							
									bool bUnloadFailed = false;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									TArray<FString> ProjectsByLoadOrder;
							 | 
						|||
| 
								 | 
							
									FCSProcHelper::GetProjectNamesByLoadOrder(ProjectsByLoadOrder, true);
							 | 
						|||
| 
								 | 
							
									
							 | 
						|||
| 
								 | 
							
									// Unload all assemblies in reverse order to prevent unloading an assembly that is still being referenced.
							 | 
						|||
| 
								 | 
							
									// For instance, most assemblies depend on ProjectGlue, so it must be unloaded last.
							 | 
						|||
| 
								 | 
							
									// Good info: https://learn.microsoft.com/en-us/dotnet/standard/assembly/unloadability
							 | 
						|||
| 
								 | 
							
									// Note: An assembly is only referenced if any of its types are referenced in code.
							 | 
						|||
| 
								 | 
							
									// Otherwise optimized out, so ProjectGlue can be unloaded first if it's not used.
							 | 
						|||
| 
								 | 
							
									for (int32 i = ProjectsByLoadOrder.Num() - 1; i >= 0; --i)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										const FString& ProjectName = ProjectsByLoadOrder[i];
							 | 
						|||
| 
								 | 
							
										UCSAssembly* Assembly = CSharpManager.FindAssembly(*ProjectName);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										if (IsValid(Assembly) && !Assembly->UnloadAssembly())
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											UE_LOGFMT(LogUnrealSharpEditor, Error, "Failed to unload assembly: {0}", *ProjectName);
							 | 
						|||
| 
								 | 
							
											bUnloadFailed = true;
							 | 
						|||
| 
								 | 
							
											break;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (bUnloadFailed)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										HotReloadStatus = FailedToUnload;
							 | 
						|||
| 
								 | 
							
										bHotReloadFailed = true;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("HotReloadFailure",
							 | 
						|||
| 
								 | 
							
										                                              "One or more assemblies failed to unload. Hot reload will be disabled until the editor restarts.\n\n"
							 | 
						|||
| 
								 | 
							
										                                              "Possible causes: Strong GC handles, running threads, etc."),
							 | 
						|||
| 
								 | 
							
										                     FText::FromString(TEXT("Hot Reload Failed")));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									// Load all assemblies again in the correct order.
							 | 
						|||
| 
								 | 
							
									for (const FString& ProjectName : ProjectsByLoadOrder)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										UCSAssembly* Assembly = CSharpManager.FindAssembly(*ProjectName);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										if (IsValid(Assembly))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											Assembly->LoadAssembly();
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
										else
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											// If the assembly is not loaded. It's a new project, and we need to load it.
							 | 
						|||
| 
								 | 
							
											CSharpManager.LoadUserAssemblyByName(*ProjectName);
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									Progress.EnterProgressFrame(1, LOCTEXT("HotReload", "Refreshing Affected Blueprints..."));
							 | 
						|||
| 
								 | 
							
									RefreshAffectedBlueprints();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									HotReloadStatus = Inactive;
							 | 
						|||
| 
								 | 
							
									bHotReloadFailed = false;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									UE_LOG(LogUnrealSharpEditor, Log, TEXT("Hot reload took %.2f seconds to execute"), FPlatformTime::Seconds() - StartTime);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::InitializeUnrealSharpEditorCallbacks(FCSManagedUnrealSharpEditorCallbacks Callbacks)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									ManagedUnrealSharpEditorCallbacks = Callbacks;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnCreateNewProject()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									OpenNewProjectDialog();
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnCompileManagedCode()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									Get().StartHotReload();
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnReloadManagedCode()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									Get().StartHotReload(false);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnRegenerateSolution()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									if (!FCSProcHelper::InvokeUnrealSharpBuildTool(BUILD_ACTION_GENERATE_SOLUTION))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									OpenSolution();
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnOpenSolution()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									OpenSolution();
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnPackageProject()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									PackageProject();
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnMergeManagedSlnAndNativeSln()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									static FString NativeSolutionPath = FPaths::ProjectDir() / FApp::GetProjectName() + ".sln";
							 | 
						|||
| 
								 | 
							
									static FString ManagedSolutionPath = FPaths::ConvertRelativePathToFull(FCSProcHelper::GetPathToSolution());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (!FPaths::FileExists(NativeSolutionPath))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FString DialogText = FString::Printf(TEXT("Failed to load native solution %s"), *NativeSolutionPath);
							 | 
						|||
| 
								 | 
							
										FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(DialogText));
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (!FPaths::FileExists(ManagedSolutionPath))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FString DialogText = FString::Printf(TEXT("Failed to load managed solution %s"), *ManagedSolutionPath);
							 | 
						|||
| 
								 | 
							
										FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(DialogText));
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									TArray<FString> NativeSlnFileLines;
							 | 
						|||
| 
								 | 
							
									FFileHelper::LoadFileToStringArray(NativeSlnFileLines, *NativeSolutionPath);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									int32 LastEndProjectIdx = 0;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									for (int32 idx = 0; idx < NativeSlnFileLines.Num(); ++idx)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FString Line = NativeSlnFileLines[idx];
							 | 
						|||
| 
								 | 
							
										Line.ReplaceInline(TEXT("\n"), TEXT(""));
							 | 
						|||
| 
								 | 
							
										if (Line == TEXT("EndProject"))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											LastEndProjectIdx = idx;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									TArray<FString> ManagedSlnFileLines;
							 | 
						|||
| 
								 | 
							
									FFileHelper::LoadFileToStringArray(ManagedSlnFileLines, *ManagedSolutionPath);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									TArray<FString> ManagedProjectLines;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									for (int32 idx = 0; idx < ManagedSlnFileLines.Num(); ++idx)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FString Line = ManagedSlnFileLines[idx];
							 | 
						|||
| 
								 | 
							
										Line.ReplaceInline(TEXT("\n"), TEXT(""));
							 | 
						|||
| 
								 | 
							
										if (Line.StartsWith(TEXT("Project(\"{")) || Line.StartsWith(TEXT("EndProject")))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											ManagedProjectLines.Add(Line);
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									for (int32 idx = 0; idx < ManagedProjectLines.Num(); ++idx)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FString Line = ManagedProjectLines[idx];
							 | 
						|||
| 
								 | 
							
										if (Line.StartsWith(TEXT("Project(\"{")) && Line.Contains(TEXT(".csproj")))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											TArray<FString> ProjectStrParts;
							 | 
						|||
| 
								 | 
							
											Line.ParseIntoArray(ProjectStrParts, TEXT(", "));
							 | 
						|||
| 
								 | 
							
											if(ProjectStrParts.Num() == 3 && ProjectStrParts[1].Contains(TEXT(".csproj")))
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												ProjectStrParts[1] = FString("\"Script\\") + ProjectStrParts[1].Mid(1);
							 | 
						|||
| 
								 | 
							
												Line = FString::Join(ProjectStrParts, TEXT(", "));
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
										NativeSlnFileLines.Insert(Line, LastEndProjectIdx + 1 + idx);
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FString MixedSlnPath = NativeSolutionPath.LeftChop(4) + FString(".Mixed.sln");
							 | 
						|||
| 
								 | 
							
									FFileHelper::SaveStringArrayToFile(NativeSlnFileLines, *MixedSlnPath);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnOpenSettings()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									FModuleManager::LoadModuleChecked<ISettingsModule>("Settings").ShowViewer(
							 | 
						|||
| 
								 | 
							
										"Editor", "General", "CSUnrealSharpEditorSettings");
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnOpenDocumentation()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									FPlatformProcess::LaunchURL(TEXT("https://www.unrealsharp.com"), nullptr, nullptr);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnReportBug()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									FPlatformProcess::LaunchURL(TEXT("https://github.com/UnrealSharp/UnrealSharp/issues"), nullptr, nullptr);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnRefreshRuntimeGlue()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									FUnrealSharpRuntimeGlueModule& RuntimeGlueModule = FModuleManager::LoadModuleChecked<FUnrealSharpRuntimeGlueModule>(
							 | 
						|||
| 
								 | 
							
										"UnrealSharpRuntimeGlue");
							 | 
						|||
| 
								 | 
							
									RuntimeGlueModule.ForceRefreshRuntimeGlue();
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::RepairComponents()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(
							 | 
						|||
| 
								 | 
							
										AssetRegistryConstants::ModuleName);
							 | 
						|||
| 
								 | 
							
									AssetRegistryModule.Get().SearchAllAssets(/*bSynchronousSearch =*/true);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									TArray<FAssetData> OutAssetData;
							 | 
						|||
| 
								 | 
							
									AssetRegistryModule.Get().GetAssetsByClass(UBlueprint::StaticClass()->GetClassPathName(), OutAssetData, true);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FScopedSlowTask Progress(OutAssetData.Num());
							 | 
						|||
| 
								 | 
							
									Progress.MakeDialog();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									USubobjectDataSubsystem* SubobjectDataSubsystem = GEngine->GetEngineSubsystem<USubobjectDataSubsystem>();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									for (FAssetData const& Asset : OutAssetData)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										const FString AssetPath = Asset.GetObjectPathString();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										if (!AssetPath.Contains(TEXT("/Game/")))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											continue;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										UBlueprint* LoadedBlueprint = Cast<
							 | 
						|||
| 
								 | 
							
											UBlueprint>(StaticLoadObject(Asset.GetClass(), nullptr, *AssetPath, nullptr));
							 | 
						|||
| 
								 | 
							
										UClass* GeneratedClass = LoadedBlueprint->GeneratedClass;
							 | 
						|||
| 
								 | 
							
										UCSClass* ManagedClass = FCSClassUtilities::GetFirstManagedClass(GeneratedClass);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										if (!ManagedClass)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											continue;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										Progress.EnterProgressFrame(1, FText::FromString(FString::Printf(TEXT("Fixing up Blueprint: %s"), *AssetPath)));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										AActor* ActorCDO = Cast<AActor>(GeneratedClass->GetDefaultObject(false));
							 | 
						|||
| 
								 | 
							
										if (!ActorCDO)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											continue;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										TArray<FSubobjectDataHandle> SubobjectData;
							 | 
						|||
| 
								 | 
							
										SubobjectDataSubsystem->K2_GatherSubobjectDataForBlueprint(LoadedBlueprint, SubobjectData);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										UInheritableComponentHandler* InheritableComponentHandler = LoadedBlueprint->
							 | 
						|||
| 
								 | 
							
											GetInheritableComponentHandler(false);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										if (!InheritableComponentHandler)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											continue;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										TArray<UObject*> Subobjects;
							 | 
						|||
| 
								 | 
							
										ActorCDO->GetDefaultSubobjects(Subobjects);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										TArray<UObject*> MatchingInstances;
							 | 
						|||
| 
								 | 
							
										GetObjectsOfClass(LoadedBlueprint->GeneratedClass, MatchingInstances, true, RF_ClassDefaultObject,
							 | 
						|||
| 
								 | 
							
										                  EInternalObjectFlags::Garbage);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										for (TFieldIterator<FObjectProperty> PropertyIt(ManagedClass, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++
							 | 
						|||
| 
								 | 
							
										     PropertyIt)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											FObjectProperty* Property = *PropertyIt;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											if (!FCSClassUtilities::IsManagedClass(Property->GetOwnerClass()))
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												break;
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											UActorComponent* OldComponentArchetype = Cast<UActorComponent>(
							 | 
						|||
| 
								 | 
							
												Property->GetObjectPropertyValue_InContainer(ActorCDO));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											if (!OldComponentArchetype || !Subobjects.Contains(OldComponentArchetype))
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												continue;
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											Property->SetObjectPropertyValue_InContainer(ActorCDO, nullptr);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											FComponentKey ComponentKey = InheritableComponentHandler->FindKey(OldComponentArchetype->GetFName());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											if (!ComponentKey.IsValid())
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												continue;
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											UActorComponent* NewArchetype = InheritableComponentHandler->GetOverridenComponentTemplate(ComponentKey);
							 | 
						|||
| 
								 | 
							
											CopyProperties(OldComponentArchetype, NewArchetype);
							 | 
						|||
| 
								 | 
							
											FBlueprintEditorUtils::MarkBlueprintAsModified(LoadedBlueprint, Property);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											for (UObject* Instance : MatchingInstances)
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												AActor* ActorInstance = static_cast<AActor*>(Instance);
							 | 
						|||
| 
								 | 
							
												TArray<TObjectPtr<UActorComponent>>& Components = ActorInstance->BlueprintCreatedComponents;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												for (TObjectPtr<UActorComponent>& Component : Components)
							 | 
						|||
| 
								 | 
							
												{
							 | 
						|||
| 
								 | 
							
													if (Component->GetName() == OldComponentArchetype->GetName())
							 | 
						|||
| 
								 | 
							
													{
							 | 
						|||
| 
								 | 
							
														CopyProperties(OldComponentArchetype, Component);
							 | 
						|||
| 
								 | 
							
													}
							 | 
						|||
| 
								 | 
							
												}
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										UBlueprintEditorLibrary::CompileBlueprint(LoadedBlueprint);
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::CopyProperties(UActorComponent* Source, UActorComponent* Target)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									UClass* SourceClass = Source->GetClass();
							 | 
						|||
| 
								 | 
							
									UClass* TargetClass = Target->GetClass();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (SourceClass != TargetClass)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										UE_LOG(LogUnrealSharpEditor, Error, TEXT("Source and Target classes are not the same."));
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									for (TFieldIterator<FProperty> PropertyIt(SourceClass, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FProperty* Property = *PropertyIt;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										if (!Property->HasAnyPropertyFlags(CPF_BlueprintVisible | CPF_Edit))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											continue;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										FString Data;
							 | 
						|||
| 
								 | 
							
										Property->ExportTextItem_InContainer(Data, Source, nullptr, nullptr, PPF_None);
							 | 
						|||
| 
								 | 
							
										Property->ImportText_InContainer(*Data, Target, Target, 0);
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									Target->PostLoad();
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnRepairComponents()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									RepairComponents();
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnExploreArchiveDirectory(FString ArchiveDirectory)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									FPlatformProcess::ExploreFolder(*ArchiveDirectory);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::PackageProject()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									FString ArchiveDirectory = SelectArchiveDirectory();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (ArchiveDirectory.IsEmpty())
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FString ExecutablePath = ArchiveDirectory / FApp::GetProjectName() + ".exe";
							 | 
						|||
| 
								 | 
							
									if (!FPaths::FileExists(ExecutablePath))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FString DialogText = FString::Printf(
							 | 
						|||
| 
								 | 
							
											TEXT(
							 | 
						|||
| 
								 | 
							
												"The executable for project '%s' could not be found in the directory: %s. Please select the root directory where you packaged your game."),
							 | 
						|||
| 
								 | 
							
											FApp::GetProjectName(), *ArchiveDirectory);
							 | 
						|||
| 
								 | 
							
										FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(DialogText));
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FScopedSlowTask Progress(1, LOCTEXT("USharpPackaging", "Packaging Project..."));
							 | 
						|||
| 
								 | 
							
									Progress.MakeDialog();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									TMap<FString, FString> Arguments;
							 | 
						|||
| 
								 | 
							
									Arguments.Add("ArchiveDirectory", FCSUnrealSharpUtils::MakeQuotedPath(ArchiveDirectory));
							 | 
						|||
| 
								 | 
							
									Arguments.Add("BuildConfig", "Release");
							 | 
						|||
| 
								 | 
							
									FCSProcHelper::InvokeUnrealSharpBuildTool(BUILD_ACTION_PACKAGE_PROJECT, Arguments);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FNotificationInfo Info(
							 | 
						|||
| 
								 | 
							
										FText::FromString(
							 | 
						|||
| 
								 | 
							
											FString::Printf(TEXT("Project '%s' has been packaged successfully."), FApp::GetProjectName())));
							 | 
						|||
| 
								 | 
							
									Info.ExpireDuration = 15.0f;
							 | 
						|||
| 
								 | 
							
									Info.bFireAndForget = true;
							 | 
						|||
| 
								 | 
							
									Info.ButtonDetails.Add(FNotificationButtonInfo(
							 | 
						|||
| 
								 | 
							
										LOCTEXT("USharpRunPackagedGame", "Run Packaged Game"),
							 | 
						|||
| 
								 | 
							
										LOCTEXT("", ""),
							 | 
						|||
| 
								 | 
							
										FSimpleDelegate::CreateStatic(&FUnrealSharpEditorModule::RunGame, ExecutablePath),
							 | 
						|||
| 
								 | 
							
										SNotificationItem::CS_None));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									Info.ButtonDetails.Add(FNotificationButtonInfo(
							 | 
						|||
| 
								 | 
							
										LOCTEXT("USharpOpenPackagedGame", "Open Folder"),
							 | 
						|||
| 
								 | 
							
										LOCTEXT("", ""),
							 | 
						|||
| 
								 | 
							
										FSimpleDelegate::CreateStatic(&FUnrealSharpEditorModule::OnExploreArchiveDirectory, ArchiveDirectory),
							 | 
						|||
| 
								 | 
							
										SNotificationItem::CS_None));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									TSharedPtr<SNotificationItem> NotificationItem = FSlateNotificationManager::Get().AddNotification(Info);
							 | 
						|||
| 
								 | 
							
									NotificationItem->SetCompletionState(SNotificationItem::CS_None);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::RunGame(FString ExecutablePath)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									FString OpenSolutionArgs = FString::Printf(TEXT("/c \"%s\""), *ExecutablePath);
							 | 
						|||
| 
								 | 
							
									FPlatformProcess::ExecProcess(TEXT("cmd.exe"), *OpenSolutionArgs, nullptr, nullptr, nullptr);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OpenSolution()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									FString SolutionPath = FPaths::ConvertRelativePathToFull(FCSProcHelper::GetPathToSolution());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (!FPaths::FileExists(SolutionPath))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										OnRegenerateSolution();
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FString ExceptionMessage;
							 | 
						|||
| 
								 | 
							
									if (!ManagedUnrealSharpEditorCallbacks.OpenSolution(*SolutionPath, &ExceptionMessage))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(ExceptionMessage), FText::FromString(TEXT("Opening C# Project Failed")));
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								};
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								FString FUnrealSharpEditorModule::SelectArchiveDirectory()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
							 | 
						|||
| 
								 | 
							
									if (!DesktopPlatform)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										return FString();
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FString DestinationFolder;
							 | 
						|||
| 
								 | 
							
									const void* ParentWindowHandle = FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr);
							 | 
						|||
| 
								 | 
							
									const FString Title = LOCTEXT("USharpChooseArchiveRoot", "Find Archive Root").ToString();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (DesktopPlatform->OpenDirectoryDialog(ParentWindowHandle, Title, FString(), DestinationFolder))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										return FPaths::ConvertRelativePathToFull(DestinationFolder);
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									return FString();
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								TSharedRef<SWidget> FUnrealSharpEditorModule::GenerateUnrealSharpMenu()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									const FCSUnrealSharpEditorCommands& CSCommands = FCSUnrealSharpEditorCommands::Get();
							 | 
						|||
| 
								 | 
							
									FMenuBuilder MenuBuilder(true, UnrealSharpCommands);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									// Build
							 | 
						|||
| 
								 | 
							
									MenuBuilder.BeginSection("Build", LOCTEXT("Build", "Build"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.AddMenuEntry(CSCommands.CompileManagedCode, NAME_None, TAttribute<FText>(), TAttribute<FText>(),
							 | 
						|||
| 
								 | 
							
									                         FSlateIcon(FAppStyle::Get().GetStyleSetName(), "LevelEditor.Recompile"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.AddMenuEntry(CSCommands.ReloadManagedCode, NAME_None, TAttribute<FText>(), TAttribute<FText>(),
							 | 
						|||
| 
								 | 
							
									                         FSlateIcon(FAppStyle::Get().GetStyleSetName(), "LevelEditor.Recompile"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.EndSection();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									// Project
							 | 
						|||
| 
								 | 
							
									MenuBuilder.BeginSection("Project", LOCTEXT("Project", "Project"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.AddMenuEntry(CSCommands.CreateNewProject, NAME_None, TAttribute<FText>(), TAttribute<FText>(),
							 | 
						|||
| 
								 | 
							
									                         FSourceCodeNavigation::GetOpenSourceCodeIDEIcon());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.AddMenuEntry(CSCommands.OpenSolution, NAME_None, TAttribute<FText>(), TAttribute<FText>(),
							 | 
						|||
| 
								 | 
							
									                         FSourceCodeNavigation::GetOpenSourceCodeIDEIcon());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.AddMenuEntry(CSCommands.RegenerateSolution, NAME_None, TAttribute<FText>(), TAttribute<FText>(),
							 | 
						|||
| 
								 | 
							
									                         FSourceCodeNavigation::GetOpenSourceCodeIDEIcon());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.AddMenuEntry(CSCommands.MergeManagedSlnAndNativeSln, NAME_None, TAttribute<FText>(), TAttribute<FText>(),
							 | 
						|||
| 
								 | 
							
															 FSourceCodeNavigation::GetOpenSourceCodeIDEIcon());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.EndSection();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									// Package
							 | 
						|||
| 
								 | 
							
									MenuBuilder.BeginSection("Package", LOCTEXT("Package", "Package"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.AddMenuEntry(CSCommands.PackageProject, NAME_None, TAttribute<FText>(), TAttribute<FText>(),
							 | 
						|||
| 
								 | 
							
									                         FSlateIcon(FAppStyle::Get().GetStyleSetName(), "LevelEditor.Recompile"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.EndSection();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									// Plugin
							 | 
						|||
| 
								 | 
							
									MenuBuilder.BeginSection("Plugin", LOCTEXT("Plugin", "Plugin"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.AddMenuEntry(CSCommands.OpenSettings, NAME_None, TAttribute<FText>(), TAttribute<FText>(),
							 | 
						|||
| 
								 | 
							
									                         FSlateIcon(FAppStyle::Get().GetStyleSetName(), "EditorPreferences.TabIcon"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.AddMenuEntry(CSCommands.OpenDocumentation, NAME_None, TAttribute<FText>(), TAttribute<FText>(),
							 | 
						|||
| 
								 | 
							
									                         FSlateIcon(FAppStyle::Get().GetStyleSetName(), "MainFrame.DocumentationHome"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.AddMenuEntry(CSCommands.ReportBug, NAME_None, TAttribute<FText>(), TAttribute<FText>(),
							 | 
						|||
| 
								 | 
							
									                         FSlateIcon(FAppStyle::Get().GetStyleSetName(), "MainFrame.ReportABug"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.EndSection();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.BeginSection("Glue", LOCTEXT("Glue", "Glue"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.AddMenuEntry(CSCommands.RefreshRuntimeGlue, NAME_None, TAttribute<FText>(), TAttribute<FText>(),
							 | 
						|||
| 
								 | 
							
									                         FSlateIcon(FAppStyle::GetAppStyleSetName(), "SourceControl.Actions.Refresh"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.EndSection();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.BeginSection("Tools", LOCTEXT("Tools", "Tools"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									MenuBuilder.AddMenuEntry(CSCommands.RepairComponents, NAME_None, TAttribute<FText>(), TAttribute<FText>(),
							 | 
						|||
| 
								 | 
							
									                         FSlateIcon(FAppStyle::GetAppStyleSetName(), "SourceControl.Actions.Refresh"));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									return MenuBuilder.MakeWidget();
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OpenNewProjectDialog()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									TSharedRef<SWindow> AddCodeWindow = SNew(SWindow)
							 | 
						|||
| 
								 | 
							
										.Title(LOCTEXT("CreateNewProject", "New C# Project"))
							 | 
						|||
| 
								 | 
							
										.SizingRule(ESizingRule::Autosized)
							 | 
						|||
| 
								 | 
							
										.SupportsMinimize(false);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									TSharedRef<SCSNewProjectDialog> NewProjectDialog = SNew(SCSNewProjectDialog);
							 | 
						|||
| 
								 | 
							
									AddCodeWindow->SetContent(NewProjectDialog);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FSlateApplication::Get().AddWindow(AddCodeWindow);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::SuggestProjectSetup()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									FString DialogText = TEXT("No C# projects were found. Would you like to create a new C# project?");
							 | 
						|||
| 
								 | 
							
									EAppReturnType::Type Result = FMessageDialog::Open(EAppMsgType::YesNo, FText::FromString(DialogText));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (Result == EAppReturnType::No)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									
							 | 
						|||
| 
								 | 
							
									OpenNewProjectDialog();
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								bool FUnrealSharpEditorModule::Tick(float DeltaTime)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									const UCSUnrealSharpEditorSettings* Settings = GetDefault<UCSUnrealSharpEditorSettings>();
							 | 
						|||
| 
								 | 
							
									if (Settings->AutomaticHotReloading == OnEditorFocus && !IsHotReloading() && HasPendingHotReloadChanges() &&
							 | 
						|||
| 
								 | 
							
										FApp::HasFocus())
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										StartHotReload();
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									return true;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::RegisterCommands()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									FCSUnrealSharpEditorCommands::Register();
							 | 
						|||
| 
								 | 
							
									UnrealSharpCommands = MakeShareable(new FUICommandList);
							 | 
						|||
| 
								 | 
							
									UnrealSharpCommands->MapAction(FCSUnrealSharpEditorCommands::Get().CreateNewProject,
							 | 
						|||
| 
								 | 
							
									                               FExecuteAction::CreateStatic(&FUnrealSharpEditorModule::OnCreateNewProject));
							 | 
						|||
| 
								 | 
							
									UnrealSharpCommands->MapAction(FCSUnrealSharpEditorCommands::Get().CompileManagedCode,
							 | 
						|||
| 
								 | 
							
									                               FExecuteAction::CreateStatic(&FUnrealSharpEditorModule::OnCompileManagedCode));
							 | 
						|||
| 
								 | 
							
									UnrealSharpCommands->MapAction(FCSUnrealSharpEditorCommands::Get().ReloadManagedCode,
							 | 
						|||
| 
								 | 
							
									                               FExecuteAction::CreateStatic(&FUnrealSharpEditorModule::OnReloadManagedCode));
							 | 
						|||
| 
								 | 
							
									UnrealSharpCommands->MapAction(FCSUnrealSharpEditorCommands::Get().RegenerateSolution,
							 | 
						|||
| 
								 | 
							
									                               FExecuteAction::CreateRaw(this, &FUnrealSharpEditorModule::OnRegenerateSolution));
							 | 
						|||
| 
								 | 
							
									UnrealSharpCommands->MapAction(FCSUnrealSharpEditorCommands::Get().OpenSolution,
							 | 
						|||
| 
								 | 
							
									                               FExecuteAction::CreateRaw(this, &FUnrealSharpEditorModule::OnOpenSolution));
							 | 
						|||
| 
								 | 
							
									UnrealSharpCommands->MapAction(FCSUnrealSharpEditorCommands::Get().MergeManagedSlnAndNativeSln,
							 | 
						|||
| 
								 | 
							
																   FExecuteAction::CreateStatic(&FUnrealSharpEditorModule::OnMergeManagedSlnAndNativeSln));
							 | 
						|||
| 
								 | 
							
									UnrealSharpCommands->MapAction(FCSUnrealSharpEditorCommands::Get().PackageProject,
							 | 
						|||
| 
								 | 
							
									                               FExecuteAction::CreateStatic(&FUnrealSharpEditorModule::OnPackageProject));
							 | 
						|||
| 
								 | 
							
									UnrealSharpCommands->MapAction(FCSUnrealSharpEditorCommands::Get().OpenSettings,
							 | 
						|||
| 
								 | 
							
									                               FExecuteAction::CreateStatic(&FUnrealSharpEditorModule::OnOpenSettings));
							 | 
						|||
| 
								 | 
							
									UnrealSharpCommands->MapAction(FCSUnrealSharpEditorCommands::Get().OpenDocumentation,
							 | 
						|||
| 
								 | 
							
									                               FExecuteAction::CreateStatic(&FUnrealSharpEditorModule::OnOpenDocumentation));
							 | 
						|||
| 
								 | 
							
									UnrealSharpCommands->MapAction(FCSUnrealSharpEditorCommands::Get().ReportBug,
							 | 
						|||
| 
								 | 
							
									                               FExecuteAction::CreateStatic(&FUnrealSharpEditorModule::OnReportBug));
							 | 
						|||
| 
								 | 
							
									UnrealSharpCommands->MapAction(FCSUnrealSharpEditorCommands::Get().RefreshRuntimeGlue,
							 | 
						|||
| 
								 | 
							
															   FExecuteAction::CreateStatic(&FUnrealSharpEditorModule::OnRefreshRuntimeGlue));
							 | 
						|||
| 
								 | 
							
									UnrealSharpCommands->MapAction(FCSUnrealSharpEditorCommands::Get().RepairComponents,
							 | 
						|||
| 
								 | 
							
									                               FExecuteAction::CreateStatic(&FUnrealSharpEditorModule::OnRepairComponents));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									const FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>("LevelEditor");
							 | 
						|||
| 
								 | 
							
									const TSharedRef<FUICommandList> Commands = LevelEditorModule.GetGlobalLevelEditorActions();
							 | 
						|||
| 
								 | 
							
									Commands->Append(UnrealSharpCommands.ToSharedRef());
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::RegisterMenu()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar.PlayToolBar");
							 | 
						|||
| 
								 | 
							
									FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("PluginTools");
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FToolMenuEntry Entry = FToolMenuEntry::InitComboButton(
							 | 
						|||
| 
								 | 
							
										"UnrealSharp",
							 | 
						|||
| 
								 | 
							
										FUIAction(),
							 | 
						|||
| 
								 | 
							
										FOnGetContent::CreateLambda([this]() { return GenerateUnrealSharpMenu(); }),
							 | 
						|||
| 
								 | 
							
										LOCTEXT("UnrealSharp_Label", "UnrealSharp"),
							 | 
						|||
| 
								 | 
							
										LOCTEXT("UnrealSharp_Tooltip", "List of all UnrealSharp actions"),
							 | 
						|||
| 
								 | 
							
										TAttribute<FSlateIcon>::CreateLambda([this]()
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											return GetMenuIcon();
							 | 
						|||
| 
								 | 
							
										}));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									Section.AddEntry(Entry);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::RegisterPluginTemplates()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    IPluginBrowser& PluginBrowser = IPluginBrowser::Get();
							 | 
						|||
| 
								 | 
							
								    const FString PluginBaseDir = FPaths::ConvertRelativePathToFull(IPluginManager::Get().FindPlugin(UE_PLUGIN_NAME)->GetBaseDir());
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    const FText BlankTemplateName = LOCTEXT("UnrealSharp_BlankLabel", "C++/C# Joint");
							 | 
						|||
| 
								 | 
							
									const FText CSharpOnlyTemplateName = LOCTEXT("UnrealSharp_CSharpOnlyLabel", "C# Only");
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									const FText BlankDescription = LOCTEXT("UnrealSharp_BlankTemplateDesc", "Create a blank plugin with a minimal amount of C++ and C# code.");
							 | 
						|||
| 
								 | 
							
									const FText CSharpOnlyDescription = LOCTEXT("UnrealSharp_CSharpOnlyTemplateDesc", "Create a blank plugin that can only contain content and C# scripts.");
							 | 
						|||
| 
								 | 
							
									
							 | 
						|||
| 
								 | 
							
								    const TSharedRef<FPluginTemplateDescription> BlankTemplate = MakeShared<FCSPluginTemplateDescription>(BlankTemplateName, BlankDescription,
							 | 
						|||
| 
								 | 
							
								        PluginBaseDir / TEXT("Templates") / TEXT("Blank"), true, EHostType::Runtime, ELoadingPhase::Default, true);
							 | 
						|||
| 
								 | 
							
									
							 | 
						|||
| 
								 | 
							
								    const TSharedRef<FPluginTemplateDescription> CSharpOnlyTemplate = MakeShared<FCSPluginTemplateDescription>(CSharpOnlyTemplateName, CSharpOnlyDescription,
							 | 
						|||
| 
								 | 
							
								        PluginBaseDir / TEXT("Templates") / TEXT("CSharpOnly"), true, EHostType::Runtime, ELoadingPhase::Default, false);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    PluginBrowser.RegisterPluginTemplate(BlankTemplate);
							 | 
						|||
| 
								 | 
							
								    PluginBrowser.RegisterPluginTemplate(CSharpOnlyTemplate);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								    PluginTemplates.Add(BlankTemplate);
							 | 
						|||
| 
								 | 
							
								    PluginTemplates.Add(CSharpOnlyTemplate);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::UnregisterPluginTemplates()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
								    IPluginBrowser& PluginBrowser = IPluginBrowser::Get();
							 | 
						|||
| 
								 | 
							
								    for (const TSharedRef<FPluginTemplateDescription>& Template : PluginTemplates)
							 | 
						|||
| 
								 | 
							
								    {
							 | 
						|||
| 
								 | 
							
								        PluginBrowser.UnregisterPluginTemplate(Template);
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnPIEShutdown(bool IsSimulating)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									// Replicate UE behavior, which forces a garbage collection when exiting PIE.
							 | 
						|||
| 
								 | 
							
									ManagedUnrealSharpEditorCallbacks.ForceManagedGC();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (bHasQueuedHotReload)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										bHasQueuedHotReload = false;
							 | 
						|||
| 
								 | 
							
										StartHotReload();
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::AddNewProject(const FString& ModuleName, const FString& ProjectParentFolder, const FString& ProjectRoot, const TMap<FString, FString>& ExtraArguments)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									TMap<FString, FString> Arguments = ExtraArguments;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									TMap<FString, FString> SolutionArguments;
							 | 
						|||
| 
								 | 
							
									SolutionArguments.Add(TEXT("MODULENAME"), ModuleName);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FString ProjectFolder = FPaths::Combine(ProjectParentFolder, ModuleName);
							 | 
						|||
| 
								 | 
							
									FString ModuleFilePath = FPaths::Combine(ProjectFolder, ModuleName + ".cs");
							 | 
						|||
| 
								 | 
							
									
							 | 
						|||
| 
								 | 
							
									FillTemplateFile(TEXT("Module"), SolutionArguments, ModuleFilePath);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									Arguments.Add(TEXT("NewProjectName"), ModuleName);
							 | 
						|||
| 
								 | 
							
									Arguments.Add(TEXT("NewProjectFolder"), FCSUnrealSharpUtils::MakeQuotedPath(FPaths::ConvertRelativePathToFull(ProjectParentFolder)));
							 | 
						|||
| 
								 | 
							
									
							 | 
						|||
| 
								 | 
							
									FString FullProjectRoot = FPaths::ConvertRelativePathToFull(ProjectRoot);
							 | 
						|||
| 
								 | 
							
									Arguments.Add(TEXT("ProjectRoot"), FCSUnrealSharpUtils::MakeQuotedPath(FullProjectRoot));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (!FCSProcHelper::InvokeUnrealSharpBuildTool(BUILD_ACTION_GENERATE_PROJECT, Arguments))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										UE_LOGFMT(LogUnrealSharpEditor, Error, "Failed to generate project %s in %s", *ModuleName, *ProjectParentFolder);
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									
							 | 
						|||
| 
								 | 
							
									OpenSolution();
							 | 
						|||
| 
								 | 
							
									AddDirectoryToWatch(FPaths::Combine(FullProjectRoot, TEXT("Script")));
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FString CsProjPath = FPaths::Combine(ProjectFolder, ModuleName + ".csproj");
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (!FPaths::FileExists(CsProjPath))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										UE_LOGFMT(LogUnrealSharpEditor, Error, "Failed to find .csproj %s in %s", *ModuleName, *ProjectParentFolder);
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									
							 | 
						|||
| 
								 | 
							
									GetManagedUnrealSharpEditorCallbacks().AddProjectToCollection(*CsProjPath);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								bool FUnrealSharpEditorModule::FillTemplateFile(const FString& TemplateName, TMap<FString, FString>& Replacements, const FString& Path)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									const FString FullFileName = FCSProcHelper::GetPluginDirectory() / TEXT("Templates") / TemplateName + TEXT(".cs.template");
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FString OutTemplate;
							 | 
						|||
| 
								 | 
							
									if (!FFileHelper::LoadFileToString(OutTemplate, *FullFileName))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										UE_LOG(LogUnrealSharpEditor, Error, TEXT("Failed to load template file %s"), *FullFileName);
							 | 
						|||
| 
								 | 
							
										return false;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									for (const TPair<FString, FString>& Replacement : Replacements)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FString ReplacementKey = TEXT("%") + Replacement.Key + TEXT("%");
							 | 
						|||
| 
								 | 
							
										OutTemplate = OutTemplate.Replace(*ReplacementKey, *Replacement.Value);
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (!FFileHelper::SaveStringToFile(OutTemplate, *Path))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										UE_LOG(LogUnrealSharpEditor, Error, TEXT("Failed to save %s when trying to create a template"), *Path);
							 | 
						|||
| 
								 | 
							
										return false;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									return true;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnStructRebuilt(UCSScriptStruct* NewStruct)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									RebuiltStructs.Add(NewStruct);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnClassRebuilt(UCSClass* NewClass)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									RebuiltClasses.Add(NewClass);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::OnEnumRebuilt(UCSEnum* NewEnum)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									RebuiltEnums.Add(NewEnum);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								bool FUnrealSharpEditorModule::IsPinAffectedByReload(const FEdGraphPinType& PinType) const
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									UObject* PinSubCategoryObject = PinType.PinSubCategoryObject.Get();
							 | 
						|||
| 
								 | 
							
									if (!IsValid(PinSubCategoryObject) || !Manager->IsManagedType(PinSubCategoryObject))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										return false;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									auto IsPinTypeRebuilt = [this](UObject* PinSubCategoryObject) -> bool
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										if (UCSClass* Class = Cast<UCSClass>(PinSubCategoryObject))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											return RebuiltClasses.Contains(Class);
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										if (UCSEnum* Enum = Cast<UCSEnum>(PinSubCategoryObject))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											return RebuiltEnums.Contains(Enum);
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										if (UCSScriptStruct* Struct = Cast<UCSScriptStruct>(PinSubCategoryObject))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											return RebuiltStructs.Contains(Struct);
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										if (UCSEnum* Enum = Cast<UCSEnum>(PinSubCategoryObject))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											return RebuiltEnums.Contains(Enum);
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										return false;
							 | 
						|||
| 
								 | 
							
									};
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (!IsPinTypeRebuilt(PinSubCategoryObject))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										return false;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									if (PinType.IsMap() && PinType.PinValueType.TerminalSubCategoryObject.IsValid())
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										UObject* MapValueType = PinType.PinValueType.TerminalSubCategoryObject.Get();
							 | 
						|||
| 
								 | 
							
										if (IsValid(MapValueType) && Manager->IsManagedType(MapValueType))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											return IsPinTypeRebuilt(MapValueType);
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									return false;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								bool FUnrealSharpEditorModule::IsNodeAffectedByReload(UEdGraphNode* Node) const
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									if (UK2Node_EditablePinBase* EditableNode = Cast<UK2Node_EditablePinBase>(Node))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										for (const TSharedPtr<FUserPinInfo>& Pin : EditableNode->UserDefinedPins)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											if (IsPinAffectedByReload(Pin->PinType))
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												return true;
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										return false;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									for (UEdGraphPin* Pin : Node->Pins)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										if (IsPinAffectedByReload(Pin->PinType))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											return true;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									return false;
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::AddDirectoryToWatch(const FString& Directory)
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									if (WatchingDirectories.Contains(Directory))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									
							 | 
						|||
| 
								 | 
							
									if (!FPaths::DirectoryExists(Directory))
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FPlatformFileManager::Get().GetPlatformFile().CreateDirectory(*Directory);
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									FDirectoryWatcherModule& DirectoryWatcherModule = FModuleManager::LoadModuleChecked<FDirectoryWatcherModule>("DirectoryWatcher");
							 | 
						|||
| 
								 | 
							
									
							 | 
						|||
| 
								 | 
							
									FDelegateHandle Handle;
							 | 
						|||
| 
								 | 
							
									DirectoryWatcherModule.Get()->RegisterDirectoryChangedCallback_Handle(
							 | 
						|||
| 
								 | 
							
										Directory,
							 | 
						|||
| 
								 | 
							
										IDirectoryWatcher::FDirectoryChanged::CreateRaw(this, &FUnrealSharpEditorModule::OnCSharpCodeModified),
							 | 
						|||
| 
								 | 
							
										Handle);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									WatchingDirectories.Add(Directory);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								void FUnrealSharpEditorModule::RefreshAffectedBlueprints()
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									if (RebuiltStructs.IsEmpty() && RebuiltClasses.IsEmpty() && RebuiltEnums.IsEmpty())
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										// Early out if nothing has changed its structure.
							 | 
						|||
| 
								 | 
							
										return;
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									TArray<UBlueprint*> AffectedBlueprints;
							 | 
						|||
| 
								 | 
							
									for (TObjectIterator<UBlueprint> BlueprintIt; BlueprintIt; ++BlueprintIt)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										UBlueprint* Blueprint = *BlueprintIt;
							 | 
						|||
| 
								 | 
							
										if (!IsValid(Blueprint->GeneratedClass) || FCSClassUtilities::IsManagedClass(Blueprint->GeneratedClass))
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											return;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										TArray<UK2Node*> AllNodes;
							 | 
						|||
| 
								 | 
							
										FBlueprintEditorUtils::GetAllNodesOfClass<UK2Node>(Blueprint, AllNodes);
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										for (UK2Node* Node : AllNodes)
							 | 
						|||
| 
								 | 
							
										{
							 | 
						|||
| 
								 | 
							
											if (IsNodeAffectedByReload(Node))
							 | 
						|||
| 
								 | 
							
											{
							 | 
						|||
| 
								 | 
							
												Node->ReconstructNode();
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										AffectedBlueprints.Add(Blueprint);
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									for (UBlueprint* Blueprint : AffectedBlueprints)
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										FKismetEditorUtilities::CompileBlueprint(Blueprint, EBlueprintCompileOptions::SkipGarbageCollection);
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									RebuiltStructs.Reset();
							 | 
						|||
| 
								 | 
							
									RebuiltClasses.Reset();
							 | 
						|||
| 
								 | 
							
									RebuiltEnums.Reset();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS);
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								FSlateIcon FUnrealSharpEditorModule::GetMenuIcon() const
							 | 
						|||
| 
								 | 
							
								{
							 | 
						|||
| 
								 | 
							
									if (HasHotReloadFailed())
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										return FSlateIcon(FCSStyle::GetStyleSetName(), "UnrealSharp.Toolbar.Fail");
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
									if (HasPendingHotReloadChanges())
							 | 
						|||
| 
								 | 
							
									{
							 | 
						|||
| 
								 | 
							
										return FSlateIcon(FCSStyle::GetStyleSetName(), "UnrealSharp.Toolbar.Modified");
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									return FSlateIcon(FCSStyle::GetStyleSetName(), "UnrealSharp.Toolbar");
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								#undef LOCTEXT_NAMESPACE
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								IMPLEMENT_MODULE(FUnrealSharpEditorModule, UnrealSharpEditor)
							 |