| @ -0,0 +1,17 @@ | ||||
| #include "CSBlueprintCompiler.h" | ||||
| #include "CSCompilerContext.h" | ||||
| #include "TypeGenerator/CSBlueprint.h" | ||||
|  | ||||
| bool FCSBlueprintCompiler::CanCompile(const UBlueprint* Blueprint) | ||||
| { | ||||
| 	return Blueprint->IsA<UCSBlueprint>(); | ||||
| } | ||||
|  | ||||
| void FCSBlueprintCompiler::Compile(UBlueprint* Blueprint, const FKismetCompilerOptions& CompileOptions, FCompilerResultsLog& Results) | ||||
| { | ||||
| 	if (UCSBlueprint* CSBlueprint = CastChecked<UCSBlueprint>(Blueprint)) | ||||
| 	{ | ||||
| 		FCSCompilerContext Compiler(CSBlueprint, Results, CompileOptions); | ||||
| 		Compiler.Compile(); | ||||
| 	} | ||||
| } | ||||
| @ -0,0 +1,16 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "CoreMinimal.h" | ||||
| #include "KismetCompilerModule.h" | ||||
|  | ||||
| class UCSClass; | ||||
| class UCSBlueprint; | ||||
|  | ||||
| class FCSBlueprintCompiler : public IBlueprintCompiler | ||||
| { | ||||
| public: | ||||
| 	// IBlueprintCompiler interface | ||||
| 	virtual bool CanCompile(const UBlueprint* Blueprint) override; | ||||
| 	virtual void Compile(UBlueprint* Blueprint, const FKismetCompilerOptions& CompileOptions, FCompilerResultsLog& Results) override; | ||||
| 	// End of IBlueprintCompiler interface | ||||
| }; | ||||
| @ -0,0 +1,289 @@ | ||||
| #include "CSCompilerContext.h" | ||||
|  | ||||
| #include "BlueprintActionDatabase.h" | ||||
| #include "ISettingsModule.h" | ||||
| #include "BehaviorTree/Tasks/BTTask_BlueprintBase.h" | ||||
| #include "Blueprint/StateTreeTaskBlueprintBase.h" | ||||
| #include "Engine/SCS_Node.h" | ||||
| #include "Engine/SimpleConstructionScript.h" | ||||
| #include "TypeGenerator/CSBlueprint.h" | ||||
| #include "TypeGenerator/CSClass.h" | ||||
| #include "TypeGenerator/CSSkeletonClass.h" | ||||
| #include "TypeGenerator/Factories/CSFunctionFactory.h" | ||||
| #include "TypeGenerator/Factories/CSPropertyFactory.h" | ||||
| #include "TypeGenerator/Factories/PropertyGenerators/CSPropertyGenerator.h" | ||||
| #include "TypeGenerator/Register/CSGeneratedClassBuilder.h" | ||||
| #include "TypeGenerator/Register/CSMetaDataUtils.h" | ||||
| #include "TypeGenerator/Register/CSSimpleConstructionScriptBuilder.h" | ||||
| #include "TypeGenerator/Register/MetaData/CSClassMetaData.h" | ||||
| #include "TypeGenerator/Register/TypeInfo/CSClassInfo.h" | ||||
| #include "UnrealSharpEditor/CSUnrealSharpEditorSettings.h" | ||||
| #include "UnrealSharpUtilities/UnrealSharpUtils.h" | ||||
| #include "Utils/CSClassUtilities.h" | ||||
|  | ||||
| FCSCompilerContext::FCSCompilerContext(UCSBlueprint* Blueprint, FCompilerResultsLog& InMessageLog, const FKismetCompilerOptions& InCompilerOptions): | ||||
| 	FKismetCompilerContext(Blueprint, InMessageLog, InCompilerOptions) | ||||
| { | ||||
| 	 | ||||
| } | ||||
|  | ||||
| void FCSCompilerContext::FinishCompilingClass(UClass* Class) | ||||
| { | ||||
| 	bool bIsSkeletonClass = FCSClassUtilities::IsSkeletonType(Class); | ||||
| 	 | ||||
| 	if (!bIsSkeletonClass) | ||||
| 	{ | ||||
| 		// The skeleton class shouldn't be using the managed constructor since it's not tied to an assembly | ||||
| 		Class->ClassConstructor = &UCSGeneratedClassBuilder::ManagedObjectConstructor; | ||||
| 	} | ||||
|  | ||||
| 	Super::FinishCompilingClass(Class); | ||||
|  | ||||
| 	TSharedPtr<FCSClassMetaData> TypeMetaData = GetClassInfo()->GetTypeMetaData<FCSClassMetaData>(); | ||||
|  | ||||
| 	// Super call overrides the class flags, so we need to set after that | ||||
| 	Class->ClassFlags |= TypeMetaData->ClassFlags; | ||||
| 	 | ||||
| 	if (NeedsToFakeNativeClass(Class) && !bIsSkeletonClass) | ||||
| 	{ | ||||
| 		// There are systems in Unreal (BehaviorTree, StateTree) which uses the AssetRegistry to find BP classes, since our C# classes are not assets, | ||||
| 		// we need to fake that they're native classes in editor in order to be able to find them.  | ||||
|  | ||||
| 		// The functions that are used to find classes are: | ||||
| 		// FGraphNodeClassHelper::BuildClassGraph() | ||||
| 		// FStateTreeNodeClassCache::CacheClasses() | ||||
| 		Class->ClassFlags |= CLASS_Native; | ||||
| 	} | ||||
| 	 | ||||
| 	UCSGeneratedClassBuilder::SetConfigName(Class, TypeMetaData); | ||||
| 	TryInitializeAsDeveloperSettings(Class); | ||||
| 	ApplyMetaData(); | ||||
| } | ||||
|  | ||||
| void FCSCompilerContext::OnPostCDOCompiled(const UObject::FPostCDOCompiledContext& Context) | ||||
| { | ||||
| 	FKismetCompilerContext::OnPostCDOCompiled(Context); | ||||
| 	 | ||||
| 	UCSGeneratedClassBuilder::SetupDefaultTickSettings(NewClass->GetDefaultObject(), NewClass); | ||||
| 	 | ||||
| 	UCSClass* Class = GetMainClass(); | ||||
| 	if (Class == NewClass) | ||||
| 	{ | ||||
| 		UCSGeneratedClassBuilder::TryRegisterDynamicSubsystem(Class); | ||||
| 		 | ||||
| 		if (GEditor) | ||||
| 		{ | ||||
| 			FBlueprintActionDatabase::Get().RefreshClassActions(Class); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void FCSCompilerContext::CreateClassVariablesFromBlueprint() | ||||
| { | ||||
| 	TSharedPtr<FCSClassInfo> ClassInfo = GetMainClass()->GetManagedTypeInfo<FCSClassInfo>(); | ||||
| 	const TArray<FCSPropertyMetaData>& Properties = ClassInfo->GetTypeMetaData<FCSClassMetaData>()->Properties; | ||||
|  | ||||
| 	NewClass->PropertyGuids.Empty(Properties.Num()); | ||||
| 	TryValidateSimpleConstructionScript(ClassInfo); | ||||
| 	 | ||||
| 	FCSPropertyFactory::CreateAndAssignProperties(NewClass, Properties, [this](const FProperty* NewProperty) | ||||
| 	{ | ||||
| 		FName PropertyName = NewProperty->GetFName(); | ||||
| 		FGuid PropertyGuid = FCSUnrealSharpUtils::ConstructGUIDFromName(PropertyName); | ||||
| 		NewClass->PropertyGuids.Add(PropertyName, PropertyGuid); | ||||
| 	}); | ||||
|  | ||||
| 	// Create dummy variables for the blueprint. | ||||
| 	// They should not get compiled, just there for metadata for different Unreal modules. | ||||
| 	CreateDummyBlueprintVariables(Properties); | ||||
| } | ||||
|  | ||||
| void FCSCompilerContext::CleanAndSanitizeClass(UBlueprintGeneratedClass* ClassToClean, UObject*& InOldCDO) | ||||
| { | ||||
| 	FKismetCompilerContext::CleanAndSanitizeClass(ClassToClean, InOldCDO); | ||||
| 	NewClass->FieldNotifies.Reset(); | ||||
| 	 | ||||
| 	TryDeinitializeAsDeveloperSettings(InOldCDO); | ||||
|  | ||||
| 	// Too late to generate functions in CreateFunctionList for child blueprints | ||||
| 	GenerateFunctions(); | ||||
| } | ||||
|  | ||||
| void FCSCompilerContext::SpawnNewClass(const FString& NewClassName) | ||||
| { | ||||
| 	UCSClass* MainClass = GetMainClass(); | ||||
| 	UCSSkeletonClass* NewSkeletonClass = NewObject<UCSSkeletonClass>(Blueprint->GetOutermost(), FName(*NewClassName), RF_Public | RF_Transactional); | ||||
| 	NewSkeletonClass->SetGeneratedClass(MainClass); | ||||
| 	NewClass = NewSkeletonClass; | ||||
| 	 | ||||
| 	// Skeleton class doesn't generate functions on the first pass. | ||||
| 	// It's done in CleanAndSanitizeClass which doesn't run when the skeleton class is created | ||||
| 	GenerateFunctions(); | ||||
| } | ||||
|  | ||||
| void FCSCompilerContext::AddInterfacesFromBlueprint(UClass* Class) | ||||
| { | ||||
| 	UCSGeneratedClassBuilder::ImplementInterfaces(Class, GetTypeMetaData()->Interfaces); | ||||
| } | ||||
|  | ||||
| void FCSCompilerContext::TryValidateSimpleConstructionScript(const TSharedPtr<const FCSClassInfo>& ClassInfo) const | ||||
| { | ||||
| 	const TArray<FCSPropertyMetaData>& Properties = GetTypeMetaData()->Properties; | ||||
| 	FCSSimpleConstructionScriptBuilder::BuildSimpleConstructionScript(Blueprint->GeneratedClass, &Blueprint->SimpleConstructionScript, Properties); | ||||
| 	 | ||||
| 	if (!Blueprint->SimpleConstructionScript) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	 | ||||
| 	TArray<USCS_Node*> Nodes; | ||||
| 	for (const FCSPropertyMetaData& Property : Properties) | ||||
| 	{ | ||||
| 		if (Property.Type->PropertyType != ECSPropertyType::DefaultComponent) | ||||
| 		{ | ||||
| 			continue; | ||||
| 		} | ||||
| 		 | ||||
| 		USimpleConstructionScript* SCS = Blueprint->SimpleConstructionScript; | ||||
| 		USCS_Node* Node = SCS->FindSCSNode(Property.Name); | ||||
| 		Nodes.Add(Node); | ||||
| 	} | ||||
|  | ||||
| 	// Remove all nodes that are not part of the class anymore. | ||||
| 	int32 NumNodes = Blueprint->SimpleConstructionScript->GetAllNodes().Num(); | ||||
| 	TArray<USCS_Node*> AllNodes = Blueprint->SimpleConstructionScript->GetAllNodes(); | ||||
| 	for (int32 i = NumNodes - 1; i >= 0; --i) | ||||
| 	{ | ||||
| 		USCS_Node* Node = AllNodes[i]; | ||||
| 		if (!Nodes.Contains(Node)) | ||||
| 		{ | ||||
| 			Blueprint->SimpleConstructionScript->RemoveNode(Node); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	Blueprint->SimpleConstructionScript->ValidateNodeTemplates(MessageLog); | ||||
| 	Blueprint->SimpleConstructionScript->ValidateNodeVariableNames(MessageLog); | ||||
| } | ||||
|  | ||||
| void FCSCompilerContext::GenerateFunctions() const | ||||
| { | ||||
| 	TSharedPtr<const FCSClassMetaData> TypeMetaData = GetTypeMetaData(); | ||||
|  | ||||
| 	if (TypeMetaData->VirtualFunctions.IsEmpty() && TypeMetaData->Functions.IsEmpty()) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	 | ||||
| 	FCSFunctionFactory::GenerateVirtualFunctions(NewClass, TypeMetaData); | ||||
| 	FCSFunctionFactory::GenerateFunctions(NewClass, TypeMetaData->Functions); | ||||
| } | ||||
|  | ||||
| UCSClass* FCSCompilerContext::GetMainClass() const | ||||
| { | ||||
| 	return CastChecked<UCSClass>(Blueprint->GeneratedClass); | ||||
| } | ||||
|  | ||||
| TSharedPtr<const FCSClassInfo> FCSCompilerContext::GetClassInfo() const | ||||
| { | ||||
| 	return GetMainClass()->GetManagedTypeInfo<FCSClassInfo>(); | ||||
| } | ||||
|  | ||||
| TSharedPtr<const FCSClassMetaData> FCSCompilerContext::GetTypeMetaData() const | ||||
| { | ||||
| 	return GetClassInfo()->GetTypeMetaData<FCSClassMetaData>(); | ||||
| } | ||||
|  | ||||
| bool FCSCompilerContext::IsDeveloperSettings() const | ||||
| { | ||||
| 	return Blueprint->GeneratedClass == NewClass && NewClass->IsChildOf<UDeveloperSettings>(); | ||||
| } | ||||
|  | ||||
| void FCSCompilerContext::TryInitializeAsDeveloperSettings(const UClass* Class) const | ||||
| { | ||||
| 	if (!IsDeveloperSettings()) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	UDeveloperSettings* Settings = static_cast<UDeveloperSettings*>(Class->GetDefaultObject()); | ||||
| 	ISettingsModule& SettingsModule = FModuleManager::GetModuleChecked<ISettingsModule>("Settings"); | ||||
| 		 | ||||
| 	SettingsModule.RegisterSettings(Settings->GetContainerName(), Settings->GetCategoryName(), Settings->GetSectionName(), | ||||
| 		Settings->GetSectionText(), | ||||
| 		Settings->GetSectionDescription(), | ||||
| 		Settings); | ||||
|  | ||||
| 	Settings->LoadConfig(); | ||||
| } | ||||
|  | ||||
| void FCSCompilerContext::TryDeinitializeAsDeveloperSettings(UObject* Settings) const | ||||
| { | ||||
| 	if (!IsValid(Settings) || !IsDeveloperSettings()) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	ISettingsModule& SettingsModule = FModuleManager::GetModuleChecked<ISettingsModule>("Settings"); | ||||
| 	UDeveloperSettings* DeveloperSettings = static_cast<UDeveloperSettings*>(Settings); | ||||
| 	SettingsModule.UnregisterSettings(DeveloperSettings->GetContainerName(), DeveloperSettings->GetCategoryName(), DeveloperSettings->GetSectionName()); | ||||
| } | ||||
|  | ||||
| void FCSCompilerContext::ApplyMetaData() | ||||
| { | ||||
| 	TSharedPtr<const FCSClassMetaData> TypeMetaData = GetTypeMetaData(); | ||||
| 		 | ||||
| 	static FString DisplayNameKey = TEXT("DisplayName"); | ||||
| 	if (!NewClass->HasMetaData(*DisplayNameKey)) | ||||
| 	{ | ||||
| 		NewClass->SetMetaData(*DisplayNameKey, *Blueprint->GetName()); | ||||
| 	} | ||||
| 		 | ||||
| 	if (GetDefault<UCSUnrealSharpEditorSettings>()->bSuffixGeneratedTypes) | ||||
| 	{ | ||||
| 		FString DisplayName = NewClass->GetMetaData(*DisplayNameKey); | ||||
| 		DisplayName += TEXT(" (C#)"); | ||||
| 		NewClass->SetMetaData(*DisplayNameKey, *DisplayName); | ||||
| 	} | ||||
|  | ||||
| 	FCSMetaDataUtils::ApplyMetaData(TypeMetaData->MetaData, NewClass); | ||||
| } | ||||
|  | ||||
| bool FCSCompilerContext::NeedsToFakeNativeClass(UClass* Class) | ||||
| { | ||||
| 	static TArray ParentClasses = | ||||
| 	{ | ||||
| 		UBTTask_BlueprintBase::StaticClass(), | ||||
| 		UStateTreeTaskBlueprintBase::StaticClass(), | ||||
| 	}; | ||||
|  | ||||
| 	for (UClass* ParentClass  : ParentClasses) | ||||
| 	{ | ||||
| 		if (Class->IsChildOf(ParentClass)) | ||||
| 		{ | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| void FCSCompilerContext::CreateDummyBlueprintVariables(const TArray<FCSPropertyMetaData>& Properties) const | ||||
| { | ||||
| 	Blueprint->NewVariables.Empty(Properties.Num()); | ||||
|  | ||||
| 	for (const FCSPropertyMetaData& PropertyMetaData : Properties) | ||||
| 	{ | ||||
| 		FBPVariableDescription VariableDescription; | ||||
| 		VariableDescription.FriendlyName = PropertyMetaData.Name.ToString(); | ||||
| 		VariableDescription.VarName = PropertyMetaData.Name; | ||||
|  | ||||
| 		for (const TTuple<FString, FString>& MetaData : PropertyMetaData.MetaData) | ||||
| 		{ | ||||
| 			VariableDescription.SetMetaData(*MetaData.Key, MetaData.Value); | ||||
| 		} | ||||
|  | ||||
| 		Blueprint->NewVariables.Add(VariableDescription); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| #undef LOCTEXT_NAMESPACE | ||||
| @ -0,0 +1,43 @@ | ||||
| #pragma once | ||||
| #include "KismetCompiler.h" | ||||
|  | ||||
| struct FCSClassMetaData; | ||||
| struct FCSPropertyMetaData; | ||||
| struct FCSClassInfo; | ||||
| class UCSClass; | ||||
| class UCSBlueprint; | ||||
|  | ||||
| class FCSCompilerContext : public FKismetCompilerContext | ||||
| { | ||||
| public: | ||||
|  | ||||
| 	FCSCompilerContext(UCSBlueprint* Blueprint, FCompilerResultsLog& InMessageLog, const FKismetCompilerOptions& InCompilerOptions); | ||||
|  | ||||
| 	// FKismetCompilerContext interface | ||||
| 	virtual void FinishCompilingClass(UClass* Class) override; | ||||
| 	virtual void OnPostCDOCompiled(const UObject::FPostCDOCompiledContext& Context) override; | ||||
| 	virtual void CreateClassVariablesFromBlueprint() override; | ||||
| 	virtual void CleanAndSanitizeClass(UBlueprintGeneratedClass* ClassToClean, UObject*& OldCDO) override; | ||||
| 	virtual void SpawnNewClass(const FString& NewClassName) override; | ||||
| 	virtual void AddInterfacesFromBlueprint(UClass* Class) override; | ||||
| 	// End of FKismetCompilerContext interface | ||||
| protected: | ||||
| 	typedef FKismetCompilerContext Super; | ||||
| private: | ||||
| 	void TryValidateSimpleConstructionScript(const TSharedPtr<const FCSClassInfo>& ClassInfo) const; | ||||
| 	void GenerateFunctions() const; | ||||
| 	UCSClass* GetMainClass() const; | ||||
| 	 | ||||
| 	TSharedPtr<const FCSClassInfo> GetClassInfo() const; | ||||
| 	TSharedPtr<const FCSClassMetaData> GetTypeMetaData() const; | ||||
|  | ||||
| 	bool IsDeveloperSettings() const; | ||||
| 	void TryInitializeAsDeveloperSettings(const UClass* Class) const; | ||||
| 	void TryDeinitializeAsDeveloperSettings(UObject* Settings) const; | ||||
| 	void ApplyMetaData(); | ||||
|  | ||||
| 	static bool NeedsToFakeNativeClass(UClass* Class); | ||||
|  | ||||
| 	void CreateDummyBlueprintVariables(const TArray<FCSPropertyMetaData>& Properties) const; | ||||
| }; | ||||
|  | ||||
| @ -0,0 +1,36 @@ | ||||
| using UnrealBuildTool; | ||||
|  | ||||
| public class UnrealSharpCompiler : ModuleRules | ||||
| { | ||||
|     public UnrealSharpCompiler(ReadOnlyTargetRules Target) : base(Target) | ||||
|     { | ||||
|         PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; | ||||
|  | ||||
|         PublicDependencyModuleNames.AddRange( | ||||
|             new string[] | ||||
|             { | ||||
|                 "Core", | ||||
|             } | ||||
|         ); | ||||
|  | ||||
|         PrivateDependencyModuleNames.AddRange( | ||||
|             new string[] | ||||
|             { | ||||
|                 "CoreUObject", | ||||
|                 "Engine", | ||||
|                 "Slate", | ||||
|                 "SlateCore",  | ||||
|                 "UnrealSharpCore", | ||||
|                 "KismetCompiler", | ||||
|                 "Kismet", | ||||
|                 "BlueprintGraph", | ||||
|                 "UnrealEd", | ||||
|                 "DeveloperSettings",  | ||||
|                 "UnrealSharpEditor",  | ||||
|                 "AIModule",  | ||||
|                 "StateTreeModule",  | ||||
|                 "UnrealSharpUtilities" | ||||
|             } | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,176 @@ | ||||
| #include "UnrealSharpCompiler.h" | ||||
|  | ||||
| #include "BlueprintActionDatabase.h" | ||||
| #include "BlueprintCompilationManager.h" | ||||
| #include "CSBlueprintCompiler.h" | ||||
| #include "CSCompilerContext.h" | ||||
| #include "CSManager.h" | ||||
| #include "KismetCompiler.h" | ||||
| #include "TypeGenerator/CSBlueprint.h" | ||||
| #include "TypeGenerator/CSClass.h" | ||||
| #include "TypeGenerator/CSEnum.h" | ||||
| #include "TypeGenerator/CSInterface.h" | ||||
| #include "TypeGenerator/CSScriptStruct.h" | ||||
|  | ||||
| #define LOCTEXT_NAMESPACE "FUnrealSharpCompilerModule" | ||||
|  | ||||
| DEFINE_LOG_CATEGORY(LogUnrealSharpCompiler); | ||||
|  | ||||
| void FUnrealSharpCompilerModule::StartupModule() | ||||
| { | ||||
| 	UCSManager& CSManager = UCSManager::GetOrCreate(); | ||||
| 	 | ||||
| 	FKismetCompilerContext::RegisterCompilerForBP(UCSBlueprint::StaticClass(), [](UBlueprint* InBlueprint, FCompilerResultsLog& InMessageLog, const FKismetCompilerOptions& InCompileOptions) | ||||
| 	{ | ||||
| 		return MakeShared<FCSCompilerContext>(CastChecked<UCSBlueprint>(InBlueprint), InMessageLog, InCompileOptions); | ||||
| 	}); | ||||
| 	 | ||||
| 	IKismetCompilerInterface& KismetCompilerModule = FModuleManager::LoadModuleChecked<IKismetCompilerInterface>("KismetCompiler"); | ||||
| 	KismetCompilerModule.GetCompilers().Add(&BlueprintCompiler); | ||||
| 	 | ||||
| 	CSManager.OnNewClassEvent().AddRaw(this, &FUnrealSharpCompilerModule::OnNewClass); | ||||
| 	CSManager.OnNewEnumEvent().AddRaw(this, &FUnrealSharpCompilerModule::OnNewEnum); | ||||
| 	CSManager.OnNewStructEvent().AddRaw(this, &FUnrealSharpCompilerModule::OnNewStruct); | ||||
| 	CSManager.OnNewInterfaceEvent().AddRaw(this, &FUnrealSharpCompilerModule::OnNewInterface); | ||||
| 	 | ||||
| 	CSManager.OnProcessedPendingClassesEvent().AddRaw(this, &FUnrealSharpCompilerModule::RecompileAndReinstanceBlueprints); | ||||
| 	CSManager.OnManagedAssemblyLoadedEvent().AddRaw(this, &FUnrealSharpCompilerModule::OnManagedAssemblyLoaded); | ||||
|  | ||||
| 	// Try to recompile and reinstance all blueprints when the module is loaded. | ||||
| 	CSManager.ForEachManagedPackage([this](const UPackage* Package) | ||||
| 	{ | ||||
| 		ForEachObjectWithPackage(Package, [this](UObject* Object) | ||||
| 		{ | ||||
| 			if (UBlueprint* Blueprint = Cast<UBlueprint>(Object)) | ||||
| 			{ | ||||
| 				OnNewClass(static_cast<UCSClass*>(Blueprint->GeneratedClass.Get())); | ||||
| 			} | ||||
| 			return true; | ||||
| 		}, false); | ||||
| 	}); | ||||
| 	 | ||||
| 	RecompileAndReinstanceBlueprints(); | ||||
| } | ||||
|  | ||||
| void FUnrealSharpCompilerModule::ShutdownModule() | ||||
| { | ||||
|      | ||||
| } | ||||
|  | ||||
| void FUnrealSharpCompilerModule::RecompileAndReinstanceBlueprints() | ||||
| { | ||||
| 	if (UCSManager::Get().IsLoadingAnyAssembly()) | ||||
| 	{ | ||||
| 		// Wait until all assemblies are loaded, so we can recompile all blueprints at once. | ||||
| 		return; | ||||
| 	} | ||||
| 	 | ||||
| 	if (ManagedComponentsToCompile.IsEmpty() && ManagedClassesToCompile.IsEmpty()) | ||||
| 	{ | ||||
| 		// Nothing to compile. | ||||
| 		return; | ||||
| 	} | ||||
| 	 | ||||
| 	auto CompileBlueprints = [this](TArray<UBlueprint*>& Blueprints) | ||||
| 	{ | ||||
| 		if (Blueprints.IsEmpty()) | ||||
| 		{ | ||||
| 			return; | ||||
| 		} | ||||
| 		 | ||||
| 		for (int32 i = 0; i < Blueprints.Num(); ++i) | ||||
| 		{ | ||||
| 			UBlueprint* Blueprint = Blueprints[i]; | ||||
|  | ||||
| 			if (!Blueprint) | ||||
| 			{ | ||||
| 				UE_LOGFMT(LogUnrealSharpCompiler, Error, "Blueprint is null, skipping compilation."); | ||||
| 				continue; | ||||
| 			} | ||||
| 			 | ||||
| 			if (!IsValid(Blueprint)) | ||||
| 			{ | ||||
| 				UE_LOGFMT(LogUnrealSharpCompiler, Error, "Blueprint {0} is garbage, skipping compilation.", *Blueprint->GetName()); | ||||
| 				continue; | ||||
| 			} | ||||
|  | ||||
| 			FKismetEditorUtilities::CompileBlueprint(Blueprint, EBlueprintCompileOptions::SkipGarbageCollection); | ||||
| 		} | ||||
| 		 | ||||
| 		Blueprints.Reset(); | ||||
| 	}; | ||||
|  | ||||
| 	// Components needs be compiled first, as they are instantiated by the owning actor, and needs their size to be known. | ||||
| 	CompileBlueprints(ManagedComponentsToCompile); | ||||
| 	CompileBlueprints(ManagedClassesToCompile); | ||||
|  | ||||
| 	CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS, true); | ||||
| } | ||||
|  | ||||
| void FUnrealSharpCompilerModule::AddManagedReferences(FCSManagedReferencesCollection& Collection) | ||||
| { | ||||
| 	Collection.ForEachManagedReference([&](UStruct* Struct) | ||||
| 	{ | ||||
| 		if (UCSClass* Class = Cast<UCSClass>(Struct)) | ||||
| 		{ | ||||
| 			OnNewClass(Class); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| void FUnrealSharpCompilerModule::OnNewClass(UCSClass* NewClass) | ||||
| { | ||||
| 	UBlueprint* Blueprint = Cast<UBlueprint>(NewClass->ClassGeneratedBy); | ||||
| 	if (!IsValid(Blueprint)) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	auto AddToCompileList = [this](TArray<UBlueprint*>& List, UBlueprint* Blueprint) | ||||
| 	{ | ||||
| 		if (List.Contains(Blueprint)) | ||||
| 		{ | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		List.Add(Blueprint); | ||||
| 	}; | ||||
| 		 | ||||
| 	if (NewClass->IsChildOf(UActorComponent::StaticClass())) | ||||
| 	{ | ||||
| 		AddToCompileList(ManagedComponentsToCompile, Blueprint); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		AddToCompileList(ManagedClassesToCompile, Blueprint); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void FUnrealSharpCompilerModule::OnNewStruct(UCSScriptStruct* NewStruct) | ||||
| { | ||||
| 	AddManagedReferences(NewStruct->ManagedReferences); | ||||
| } | ||||
|  | ||||
| void FUnrealSharpCompilerModule::OnNewEnum(UCSEnum* NewEnum) | ||||
| { | ||||
| 	AddManagedReferences(NewEnum->ManagedReferences); | ||||
| } | ||||
|  | ||||
| void FUnrealSharpCompilerModule::OnNewInterface(UCSInterface* NewInterface) | ||||
| { | ||||
| 	if (!IsValid(GEditor)) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	 | ||||
| 	FBlueprintActionDatabase::Get().RefreshClassActions(NewInterface); | ||||
| } | ||||
|  | ||||
| void FUnrealSharpCompilerModule::OnManagedAssemblyLoaded(const FName& AssemblyName) | ||||
| { | ||||
| 	RecompileAndReinstanceBlueprints(); | ||||
| } | ||||
|  | ||||
| #undef LOCTEXT_NAMESPACE | ||||
|      | ||||
| IMPLEMENT_MODULE(FUnrealSharpCompilerModule, UnrealSharpCompiler) | ||||
| @ -0,0 +1,36 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "CoreMinimal.h" | ||||
| #include "CSBlueprintCompiler.h" | ||||
| #include "Modules/ModuleManager.h" | ||||
|  | ||||
| class UCSInterface; | ||||
| struct FCSManagedReferencesCollection; | ||||
| class UCSEnum; | ||||
| class UCSScriptStruct; | ||||
| class FCSBlueprintCompiler; | ||||
|  | ||||
| DECLARE_LOG_CATEGORY_EXTERN(LogUnrealSharpCompiler, Log, All); | ||||
|  | ||||
| class FUnrealSharpCompilerModule : public IModuleInterface | ||||
| { | ||||
| public: | ||||
|     virtual void StartupModule() override; | ||||
|     virtual void ShutdownModule() override; | ||||
| private: | ||||
|      | ||||
|     void OnNewClass(UCSClass* NewClass); | ||||
|     void OnNewStruct(UCSScriptStruct* NewStruct); | ||||
|     void OnNewEnum(UCSEnum* NewEnum); | ||||
|     void OnNewInterface(UCSInterface* NewInterface); | ||||
|      | ||||
|     void OnManagedAssemblyLoaded(const FName& AssemblyName); | ||||
|     void RecompileAndReinstanceBlueprints(); | ||||
|  | ||||
|     void AddManagedReferences(FCSManagedReferencesCollection& Collection); | ||||
|  | ||||
|     FCSBlueprintCompiler BlueprintCompiler; | ||||
|      | ||||
|     TArray<UBlueprint*> ManagedClassesToCompile; | ||||
|     TArray<UBlueprint*> ManagedComponentsToCompile; | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user