#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(CastChecked(InBlueprint), InMessageLog, InCompileOptions); }); IKismetCompilerInterface& KismetCompilerModule = FModuleManager::LoadModuleChecked("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(Object)) { OnNewClass(static_cast(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& 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(Struct)) { OnNewClass(Class); } }); } void FUnrealSharpCompilerModule::OnNewClass(UCSClass* NewClass) { UBlueprint* Blueprint = Cast(NewClass->ClassGeneratedBy); if (!IsValid(Blueprint)) { return; } auto AddToCompileList = [this](TArray& 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)