Lua向C#逻辑迁移 一期 #13

将整个插件代码上传
This commit is contained in:
2025-10-26 21:48:39 +08:00
parent 56994b3927
commit 648386cd73
785 changed files with 53683 additions and 2 deletions

View File

@ -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();
}
}

View File

@ -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
};

View File

@ -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

View File

@ -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;
};

View File

@ -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"
}
);
}
}

View File

@ -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)

View File

@ -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;
};