679 lines
21 KiB
C++
679 lines
21 KiB
C++
#include "CSManager.h"
|
|
#include "CSManagedGCHandle.h"
|
|
#include "CSAssembly.h"
|
|
#include "UnrealSharpCore.h"
|
|
#include "TypeGenerator/CSClass.h"
|
|
#include "Misc/Paths.h"
|
|
#include "Misc/App.h"
|
|
#include "UObject/Object.h"
|
|
#include "Misc/MessageDialog.h"
|
|
#include "Engine/Blueprint.h"
|
|
#include "UnrealSharpProcHelper/CSProcHelper.h"
|
|
#include <vector>
|
|
#include "CSBindsManager.h"
|
|
#include "CSNamespace.h"
|
|
#include "CSUnrealSharpSettings.h"
|
|
#include "Engine/UserDefinedEnum.h"
|
|
#include "Logging/StructuredLog.h"
|
|
#include "StructUtils/UserDefinedStruct.h"
|
|
#include "TypeGenerator/CSInterface.h"
|
|
#include "TypeGenerator/Factories/CSPropertyFactory.h"
|
|
#include "TypeGenerator/Register/CSBuilderManager.h"
|
|
#include "TypeGenerator/Register/TypeInfo/CSClassInfo.h"
|
|
#include "Utils/CSClassUtilities.h"
|
|
|
|
#ifdef _WIN32
|
|
#define PLATFORM_STRING(string) string
|
|
#elif defined(__unix__)
|
|
#define PLATFORM_STRING(string) TCHAR_TO_ANSI(string)
|
|
#elif defined(__APPLE__)
|
|
#define PLATFORM_STRING(string) TCHAR_TO_ANSI(string)
|
|
#endif
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic ignored "-Wdangling-assignment"
|
|
#endif
|
|
|
|
UCSManager* UCSManager::Instance = nullptr;
|
|
|
|
UPackage* UCSManager::FindOrAddManagedPackage(const FCSNamespace Namespace)
|
|
{
|
|
if (UPackage* NativePackage = Namespace.TryGetAsNativePackage())
|
|
{
|
|
return NativePackage;
|
|
}
|
|
|
|
FCSNamespace CurrentNamespace = Namespace;
|
|
TArray<FCSNamespace> ParentNamespaces;
|
|
while (true)
|
|
{
|
|
ParentNamespaces.Add(CurrentNamespace);
|
|
|
|
if (!CurrentNamespace.GetParentNamespace(CurrentNamespace))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
UPackage* ParentPackage = nullptr;
|
|
for (int32 i = ParentNamespaces.Num() - 1; i >= 0; i--)
|
|
{
|
|
FCSNamespace ParentNamespace = ParentNamespaces[i];
|
|
FName PackageName = ParentNamespace.GetPackageName();
|
|
|
|
for (UPackage* Package : AllPackages)
|
|
{
|
|
if (PackageName == Package->GetFName())
|
|
{
|
|
ParentPackage = Package;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!ParentPackage)
|
|
{
|
|
ParentPackage = NewObject<UPackage>(nullptr, PackageName, RF_Public);
|
|
ParentPackage->SetPackageFlags(PKG_CompiledIn);
|
|
AllPackages.Add(ParentPackage);
|
|
}
|
|
}
|
|
|
|
return ParentPackage;
|
|
}
|
|
|
|
void UCSManager::ForEachManagedField(const TFunction<void(UObject*)>& Callback) const
|
|
{
|
|
for (UPackage* Package : AllPackages)
|
|
{
|
|
ForEachObjectWithPackage(Package, [&Callback](UObject* Object)
|
|
{
|
|
Callback(Object);
|
|
return true;
|
|
}, false);
|
|
}
|
|
}
|
|
|
|
UPackage* UCSManager::GetPackage(const FCSNamespace Namespace)
|
|
{
|
|
UPackage* FoundPackage;
|
|
if (GetDefault<UCSUnrealSharpSettings>()->HasNamespaceSupport())
|
|
{
|
|
FoundPackage = FindOrAddManagedPackage(Namespace);
|
|
}
|
|
else
|
|
{
|
|
FoundPackage = GetGlobalManagedPackage();
|
|
}
|
|
|
|
return FoundPackage;
|
|
}
|
|
|
|
bool UCSManager::IsLoadingAnyAssembly() const
|
|
{
|
|
for (const TPair<FName, TObjectPtr<UCSAssembly>>& LoadedAssembly : LoadedAssemblies)
|
|
{
|
|
UCSAssembly* AssemblyPtr = LoadedAssembly.Value;
|
|
if (IsValid(AssemblyPtr) && AssemblyPtr->IsLoading())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void UCSManager::AddDynamicSubsystemClass(TSubclassOf<UDynamicSubsystem> SubsystemClass)
|
|
{
|
|
if (!IsValid(SubsystemClass))
|
|
{
|
|
UE_LOG(LogUnrealSharp, Warning, TEXT("Tried to add an invalid dynamic subsystem class"));
|
|
return;
|
|
}
|
|
|
|
if (!PendingDynamicSubsystemClasses.Contains(SubsystemClass))
|
|
{
|
|
PendingDynamicSubsystemClasses.Add(SubsystemClass);
|
|
}
|
|
|
|
TryInitializeDynamicSubsystems();
|
|
}
|
|
|
|
void UCSManager::Initialize()
|
|
{
|
|
#if WITH_EDITOR
|
|
FString DotNetInstallationPath = FCSProcHelper::GetDotNetDirectory();
|
|
if (DotNetInstallationPath.IsEmpty())
|
|
{
|
|
FString DialogText = FString::Printf(TEXT("UnrealSharp can't be initialized. An installation of .NET %s SDK can't be found on your system."), TEXT(DOTNET_MAJOR_VERSION));
|
|
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(DialogText));
|
|
return;
|
|
}
|
|
|
|
FString UnrealSharpLibraryPath = FCSProcHelper::GetUnrealSharpPluginsPath();
|
|
if (!FPaths::FileExists(UnrealSharpLibraryPath))
|
|
{
|
|
FString FullPath = FPaths::ConvertRelativePathToFull(UnrealSharpLibraryPath);
|
|
FString DialogText = FString::Printf(TEXT(
|
|
"The bindings library could not be found at the following location:\n%s\n\n"
|
|
"Most likely, the bindings library failed to build due to invalid generated glue."
|
|
), *FullPath);
|
|
|
|
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(DialogText));
|
|
return;
|
|
}
|
|
|
|
TArray<FString> ProjectPaths;
|
|
FCSProcHelper::GetAllProjectPaths(ProjectPaths);
|
|
|
|
// Compile the C# project for any changes done outside the editor.
|
|
if (!ProjectPaths.IsEmpty() && !FApp::IsUnattended() && !FCSProcHelper::InvokeUnrealSharpBuildTool(BUILD_ACTION_BUILD_WEAVE))
|
|
{
|
|
Initialize();
|
|
return;
|
|
}
|
|
|
|
// Remove this listener when the engine is shutting down.
|
|
// Otherwise, we'll get a crash when the GC cleans up all the UObject.
|
|
FCoreDelegates::OnPreExit.AddUObject(this, &UCSManager::OnEnginePreExit);
|
|
#endif
|
|
|
|
TypeBuilderManager = NewObject<UCSTypeBuilderManager>(this);
|
|
TypeBuilderManager->Initialize();
|
|
|
|
GUObjectArray.AddUObjectDeleteListener(this);
|
|
|
|
// Initialize the C# runtime.
|
|
if (!InitializeDotNetRuntime())
|
|
{
|
|
return;
|
|
}
|
|
|
|
GlobalManagedPackage = FindOrAddManagedPackage(FCSNamespace(TEXT("UnrealSharp")));
|
|
|
|
// Initialize the property factory. This is used to create properties for managed structs/classes/functions.
|
|
FCSPropertyFactory::Initialize();
|
|
|
|
// Try to load the user assembly. Can be empty if the user hasn't created any csproj yet.
|
|
LoadAllUserAssemblies();
|
|
|
|
FModuleManager::Get().OnModulesChanged().AddUObject(this, &UCSManager::OnModulesChanged);
|
|
}
|
|
|
|
bool UCSManager::InitializeDotNetRuntime()
|
|
{
|
|
if (!LoadRuntimeHost())
|
|
{
|
|
UE_LOG(LogUnrealSharp, Fatal, TEXT("Failed to load Runtime Host"));
|
|
return false;
|
|
}
|
|
|
|
load_assembly_and_get_function_pointer_fn LoadAssemblyAndGetFunctionPointer = InitializeNativeHost();
|
|
if (!LoadAssemblyAndGetFunctionPointer)
|
|
{
|
|
UE_LOG(LogUnrealSharp, Fatal, TEXT("Failed to initialize Runtime Host. Check logs for more details."));
|
|
return false;
|
|
}
|
|
|
|
const FString EntryPointClassName = TEXT("UnrealSharp.Plugins.Main, UnrealSharp.Plugins");
|
|
const FString EntryPointFunctionName = TEXT("InitializeUnrealSharp");
|
|
|
|
const FString UnrealSharpLibraryAssembly = FPaths::ConvertRelativePathToFull(FCSProcHelper::GetUnrealSharpPluginsPath());
|
|
const FString UserWorkingDirectory = FPaths::ConvertRelativePathToFull(FCSProcHelper::GetUserAssemblyDirectory());
|
|
|
|
FInitializeRuntimeHost InitializeUnrealSharp = nullptr;
|
|
const int32 ErrorCode = LoadAssemblyAndGetFunctionPointer(PLATFORM_STRING(*UnrealSharpLibraryAssembly),
|
|
PLATFORM_STRING(*EntryPointClassName),
|
|
PLATFORM_STRING(*EntryPointFunctionName),
|
|
UNMANAGEDCALLERSONLY_METHOD,
|
|
nullptr,
|
|
reinterpret_cast<void**>(&InitializeUnrealSharp));
|
|
|
|
if (ErrorCode != 0)
|
|
{
|
|
UE_LOGFMT(LogUnrealSharp, Fatal, "Failed to load assembly: {0}", ErrorCode);
|
|
return false;
|
|
}
|
|
|
|
// Entry point to C# to initialize UnrealSharp
|
|
if (!InitializeUnrealSharp(*UserWorkingDirectory,
|
|
*UnrealSharpLibraryAssembly,
|
|
&ManagedPluginsCallbacks,
|
|
(const void*)&FCSBindsManager::GetBoundFunction,
|
|
&FCSManagedCallbacks::ManagedCallbacks))
|
|
{
|
|
UE_LOG(LogUnrealSharp, Fatal, TEXT("Failed to initialize UnrealSharp!"));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool UCSManager::LoadRuntimeHost()
|
|
{
|
|
const FString RuntimeHostPath = FCSProcHelper::GetRuntimeHostPath();
|
|
if (!FPaths::FileExists(RuntimeHostPath))
|
|
{
|
|
UE_LOG(LogUnrealSharp, Error, TEXT("Couldn't find Hostfxr.dll"));
|
|
return false;
|
|
}
|
|
|
|
RuntimeHost = FPlatformProcess::GetDllHandle(*RuntimeHostPath);
|
|
if (RuntimeHost == nullptr)
|
|
{
|
|
UE_LOG(LogUnrealSharp, Fatal, TEXT("Failed to get the RuntimeHost DLL handle!"));
|
|
return false;
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
void* DLLHandle = FPlatformProcess::GetDllExport(RuntimeHost, TEXT("hostfxr_initialize_for_dotnet_command_line"));
|
|
Hostfxr_Initialize_For_Dotnet_Command_Line = static_cast<hostfxr_initialize_for_dotnet_command_line_fn>(DLLHandle);
|
|
|
|
DLLHandle = FPlatformProcess::GetDllExport(RuntimeHost, TEXT("hostfxr_initialize_for_runtime_config"));
|
|
Hostfxr_Initialize_For_Runtime_Config = static_cast<hostfxr_initialize_for_runtime_config_fn>(DLLHandle);
|
|
|
|
DLLHandle = FPlatformProcess::GetDllExport(RuntimeHost, TEXT("hostfxr_get_runtime_delegate"));
|
|
Hostfxr_Get_Runtime_Delegate = static_cast<hostfxr_get_runtime_delegate_fn>(DLLHandle);
|
|
|
|
DLLHandle = FPlatformProcess::GetDllExport(RuntimeHost, TEXT("hostfxr_close"));
|
|
Hostfxr_Close = static_cast<hostfxr_close_fn>(DLLHandle);
|
|
#else
|
|
Hostfxr_Initialize_For_Dotnet_Command_Line = (hostfxr_initialize_for_dotnet_command_line_fn)FPlatformProcess::GetDllExport(RuntimeHost, TEXT("hostfxr_initialize_for_dotnet_command_line"));
|
|
|
|
Hostfxr_Initialize_For_Runtime_Config = (hostfxr_initialize_for_runtime_config_fn)FPlatformProcess::GetDllExport(RuntimeHost, TEXT("hostfxr_initialize_for_runtime_config"));
|
|
|
|
Hostfxr_Get_Runtime_Delegate = (hostfxr_get_runtime_delegate_fn)FPlatformProcess::GetDllExport(RuntimeHost, TEXT("hostfxr_get_runtime_delegate"));
|
|
|
|
Hostfxr_Close = (hostfxr_close_fn)FPlatformProcess::GetDllExport(RuntimeHost, TEXT("hostfxr_close"));
|
|
#endif
|
|
|
|
return Hostfxr_Initialize_For_Dotnet_Command_Line && Hostfxr_Get_Runtime_Delegate && Hostfxr_Close && Hostfxr_Initialize_For_Runtime_Config;
|
|
}
|
|
|
|
bool UCSManager::LoadAllUserAssemblies()
|
|
{
|
|
TArray<FString> UserAssemblyPaths;
|
|
FCSProcHelper::GetAssemblyPathsByLoadOrder(UserAssemblyPaths, true);
|
|
|
|
if (UserAssemblyPaths.IsEmpty())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
for (const FString& UserAssemblyPath : UserAssemblyPaths)
|
|
{
|
|
LoadAssemblyByPath(UserAssemblyPath);
|
|
}
|
|
|
|
OnAssembliesLoaded.Broadcast();
|
|
return true;
|
|
}
|
|
|
|
void UCSManager::NotifyUObjectDeleted(const UObjectBase* Object, int32 Index)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(UCSManager::NotifyUObjectDeleted);
|
|
|
|
TSharedPtr<FGCHandle> Handle;
|
|
if (!ManagedObjectHandles.RemoveAndCopyValueByHash(Index, Index, Handle))
|
|
{
|
|
return;
|
|
}
|
|
|
|
UCSAssembly* Assembly = FindOwningAssembly(Object->GetClass());
|
|
if (!IsValid(Assembly))
|
|
{
|
|
FString ObjectName = Object->GetFName().ToString();
|
|
FString ClassName = Object->GetClass()->GetFName().ToString();
|
|
UE_LOG(LogUnrealSharp, Error, TEXT("Failed to find owning assembly for object %s with class %s. Will cause managed memory leak."), *ObjectName, *ClassName);
|
|
return;
|
|
}
|
|
|
|
TSharedPtr<const FGCHandle> AssemblyHandle = Assembly->GetManagedAssemblyHandle();
|
|
Handle->Dispose(AssemblyHandle->GetHandle());
|
|
|
|
TMap<uint32, TSharedPtr<FGCHandle>>* FoundHandles = ManagedInterfaceWrappers.FindByHash(Index, Index);
|
|
if (FoundHandles == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (auto &[Key, Value] : *FoundHandles)
|
|
{
|
|
Value->Dispose(AssemblyHandle->GetHandle());
|
|
}
|
|
|
|
FoundHandles->Empty();
|
|
ManagedInterfaceWrappers.Remove(Index);
|
|
}
|
|
|
|
void UCSManager::OnModulesChanged(FName InModuleName, EModuleChangeReason InModuleChangeReason)
|
|
{
|
|
if (InModuleChangeReason != EModuleChangeReason::ModuleLoaded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TryInitializeDynamicSubsystems();
|
|
}
|
|
|
|
void UCSManager::TryInitializeDynamicSubsystems()
|
|
{
|
|
// Try to activate Editor/EngineSubsystems
|
|
for (int32 i = PendingDynamicSubsystemClasses.Num() - 1; i >= 0; --i)
|
|
{
|
|
TSubclassOf<UDynamicSubsystem> SubsystemClass = PendingDynamicSubsystemClasses[i];
|
|
if (!IsValid(SubsystemClass))
|
|
{
|
|
UE_LOG(LogUnrealSharp, Warning, TEXT("Tried to activate an invalid dynamic subsystem class"));
|
|
PendingDynamicSubsystemClasses.RemoveAt(i);
|
|
continue;
|
|
}
|
|
|
|
FSubsystemCollectionBase::ActivateExternalSubsystem(SubsystemClass);
|
|
|
|
// Unfortunately no better way to check if the subsystems actually registered.
|
|
{
|
|
if (SubsystemClass->IsChildOf(UEngineSubsystem::StaticClass()))
|
|
{
|
|
if (IsValid(GEngine) && GEngine->GetEngineSubsystemBase(SubsystemClass.Get()))
|
|
{
|
|
PendingDynamicSubsystemClasses.RemoveAt(i);
|
|
}
|
|
}
|
|
#if WITH_EDITOR
|
|
else if (SubsystemClass->IsChildOf(UEditorSubsystem::StaticClass()))
|
|
{
|
|
if (IsValid(GEditor) && GEditor->GetEditorSubsystemBase(SubsystemClass.Get()))
|
|
{
|
|
PendingDynamicSubsystemClasses.RemoveAt(i);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
load_assembly_and_get_function_pointer_fn UCSManager::InitializeNativeHost() const
|
|
{
|
|
#if WITH_EDITOR
|
|
FString DotNetPath = FCSProcHelper::GetDotNetDirectory();
|
|
#else
|
|
FString DotNetPath = FCSProcHelper::GetPluginAssembliesPath();
|
|
#endif
|
|
|
|
if (!FPaths::DirectoryExists(DotNetPath))
|
|
{
|
|
UE_LOG(LogUnrealSharp, Error, TEXT("Dotnet directory does not exist at: %s"), *DotNetPath);
|
|
return nullptr;
|
|
}
|
|
|
|
FString RuntimeHostPath = FCSProcHelper::GetRuntimeHostPath();
|
|
if (!FPaths::FileExists(RuntimeHostPath))
|
|
{
|
|
UE_LOG(LogUnrealSharp, Error, TEXT("Runtime host path does not exist at: %s"), *RuntimeHostPath);
|
|
return nullptr;
|
|
}
|
|
|
|
UE_LOG(LogUnrealSharp, Log, TEXT("DotNetPath: %s"), *DotNetPath);
|
|
UE_LOG(LogUnrealSharp, Log, TEXT("RuntimeHostPath: %s"), *RuntimeHostPath);
|
|
|
|
hostfxr_initialize_parameters InitializeParameters;
|
|
InitializeParameters.dotnet_root = PLATFORM_STRING(*DotNetPath);
|
|
InitializeParameters.host_path = PLATFORM_STRING(*RuntimeHostPath);
|
|
InitializeParameters.size = sizeof(hostfxr_initialize_parameters);
|
|
|
|
hostfxr_handle HostFXR_Handle = nullptr;
|
|
int32 ErrorCode = 0;
|
|
#if WITH_EDITOR
|
|
FString RuntimeConfigPath = FCSProcHelper::GetRuntimeConfigPath();
|
|
|
|
if (!FPaths::FileExists(RuntimeConfigPath))
|
|
{
|
|
UE_LOG(LogUnrealSharp, Error, TEXT("No runtime config found"));
|
|
return nullptr;
|
|
}
|
|
#if defined(_WIN32)
|
|
ErrorCode = Hostfxr_Initialize_For_Runtime_Config(PLATFORM_STRING(*RuntimeConfigPath), &InitializeParameters, &HostFXR_Handle);
|
|
#else
|
|
ErrorCode = Hostfxr_Initialize_For_Runtime_Config(PLATFORM_STRING(*RuntimeConfigPath), nullptr, &HostFXR_Handle);
|
|
#endif
|
|
|
|
#else
|
|
FString PluginAssemblyPath = FCSProcHelper::GetUnrealSharpPluginsPath();
|
|
|
|
if (!FPaths::FileExists(PluginAssemblyPath))
|
|
{
|
|
UE_LOG(LogUnrealSharp, Error, TEXT("UnrealSharp.Plugins.dll does not exist at: %s"), *PluginAssemblyPath);
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector Args { PLATFORM_STRING(*PluginAssemblyPath) };
|
|
|
|
#if defined(_WIN32)
|
|
ErrorCode = Hostfxr_Initialize_For_Dotnet_Command_Line(Args.size(), Args.data(), &InitializeParameters, &HostFXR_Handle);
|
|
#else
|
|
ErrorCode = Hostfxr_Initialize_For_Dotnet_Command_Line(Args.size(), const_cast<const char**>(Args.data()), &InitializeParameters, &HostFXR_Handle);
|
|
#endif
|
|
|
|
#endif
|
|
|
|
if (ErrorCode != 0)
|
|
{
|
|
UE_LOG(LogUnrealSharp, Error, TEXT("hostfxr_initialize_for_runtime_config failed with code: %d"), ErrorCode);
|
|
return nullptr;
|
|
}
|
|
|
|
void* LoadAssemblyAndGetFunctionPointer = nullptr;
|
|
ErrorCode = Hostfxr_Get_Runtime_Delegate(HostFXR_Handle, hdt_load_assembly_and_get_function_pointer, &LoadAssemblyAndGetFunctionPointer);
|
|
Hostfxr_Close(HostFXR_Handle);
|
|
|
|
if (ErrorCode != 0 || !LoadAssemblyAndGetFunctionPointer)
|
|
{
|
|
UE_LOG(LogUnrealSharp, Error, TEXT("hostfxr_get_runtime_delegate failed with code: %d"), ErrorCode);
|
|
return nullptr;
|
|
}
|
|
|
|
return (load_assembly_and_get_function_pointer_fn)LoadAssemblyAndGetFunctionPointer;
|
|
}
|
|
|
|
UCSAssembly* UCSManager::LoadAssemblyByPath(const FString& AssemblyPath, bool bIsCollectible)
|
|
{
|
|
if (!FPaths::FileExists(AssemblyPath))
|
|
{
|
|
UE_LOG(LogUnrealSharp, Error, TEXT("Assembly path does not exist: %s"), *AssemblyPath);
|
|
return nullptr;
|
|
}
|
|
|
|
FString AssemblyName = FPaths::GetBaseFilename(AssemblyPath);
|
|
UCSAssembly* ExistingAssembly = FindAssembly(FName(*AssemblyName));
|
|
|
|
if (IsValid(ExistingAssembly) && ExistingAssembly->IsValidAssembly())
|
|
{
|
|
UE_LOGFMT(LogUnrealSharp, Display, "Assembly {AssemblyName} is already loaded.", *AssemblyName);
|
|
return ExistingAssembly;
|
|
}
|
|
|
|
UCSAssembly* NewAssembly = NewObject<UCSAssembly>(this, *AssemblyName);
|
|
NewAssembly->SetAssemblyPath(AssemblyPath);
|
|
|
|
LoadedAssemblies.Add(NewAssembly->GetAssemblyName(), NewAssembly);
|
|
|
|
if (!NewAssembly->LoadAssembly(bIsCollectible))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
OnManagedAssemblyLoaded.Broadcast(NewAssembly->GetAssemblyName());
|
|
|
|
UE_LOGFMT(LogUnrealSharp, Display, "Successfully loaded AssemblyHandle with path {AssemblyPath}.", *AssemblyPath);
|
|
return NewAssembly;
|
|
}
|
|
|
|
UCSAssembly* UCSManager::LoadUserAssemblyByName(const FName AssemblyName, bool bIsCollectible)
|
|
{
|
|
FString AssemblyPath = FPaths::Combine(FCSProcHelper::GetUserAssemblyDirectory(), AssemblyName.ToString() + ".dll");
|
|
return LoadAssemblyByPath(AssemblyPath, bIsCollectible);
|
|
}
|
|
|
|
UCSAssembly* UCSManager::LoadPluginAssemblyByName(const FName AssemblyName, bool bIsCollectible)
|
|
{
|
|
FString AssemblyPath = FPaths::Combine(FCSProcHelper::GetPluginAssembliesPath(), AssemblyName.ToString() + ".dll");
|
|
return LoadAssemblyByPath(AssemblyPath, bIsCollectible);
|
|
}
|
|
|
|
UCSAssembly* UCSManager::FindOwningAssembly(UClass* Class)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(UCSManager::FindOwningAssembly);
|
|
|
|
if (ICSManagedTypeInterface* ManagedType = FCSClassUtilities::GetManagedType(Class))
|
|
{
|
|
// Fast access to the owning assembly for managed types.
|
|
return ManagedType->GetOwningAssembly();
|
|
}
|
|
|
|
Class = FCSClassUtilities::GetFirstNativeClass(Class);
|
|
uint32 ClassID = Class->GetUniqueID();
|
|
TObjectPtr<UCSAssembly> Assembly = NativeClassToAssemblyMap.FindOrAddByHash(ClassID, ClassID);
|
|
|
|
if (IsValid(Assembly))
|
|
{
|
|
return Assembly;
|
|
}
|
|
|
|
return FindOwningAssemblySlow(Class);
|
|
}
|
|
|
|
UCSAssembly * UCSManager::FindOwningAssembly(UScriptStruct* Struct)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(UCSManager::FindOwningAssembly);
|
|
|
|
if (const ICSManagedTypeInterface* ManagedType = Cast<ICSManagedTypeInterface>(Struct); ManagedType != nullptr)
|
|
{
|
|
// Fast access to the owning assembly for managed types.
|
|
return ManagedType->GetOwningAssembly();
|
|
}
|
|
|
|
if (const UUserDefinedStruct* UserStruct = Cast<UUserDefinedStruct>(Struct); UserStruct != nullptr)
|
|
{
|
|
// This is a Blueprint Struct and we can't use it
|
|
return nullptr;
|
|
}
|
|
|
|
uint32 ClassID = Struct->GetUniqueID();
|
|
TObjectPtr<UCSAssembly> Assembly = NativeClassToAssemblyMap.FindOrAddByHash(ClassID, ClassID);
|
|
|
|
if (IsValid(Assembly))
|
|
{
|
|
return Assembly;
|
|
}
|
|
|
|
return FindOwningAssemblySlow(Struct);
|
|
}
|
|
|
|
UCSAssembly* UCSManager::FindOwningAssembly(UEnum* Enum)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(UCSManager::FindOwningAssembly);
|
|
|
|
if (const ICSManagedTypeInterface* ManagedType = Cast<ICSManagedTypeInterface>(Enum); ManagedType != nullptr)
|
|
{
|
|
// Fast access to the owning assembly for managed types.
|
|
return ManagedType->GetOwningAssembly();
|
|
}
|
|
|
|
if (const UUserDefinedEnum* UserEnum = Cast<UUserDefinedEnum>(Enum); UserEnum != nullptr)
|
|
{
|
|
// This is a Blueprint Enum and we can't use it
|
|
return nullptr;
|
|
}
|
|
|
|
uint32 ClassID = Enum->GetUniqueID();
|
|
TObjectPtr<UCSAssembly> Assembly = NativeClassToAssemblyMap.FindOrAddByHash(ClassID, ClassID);
|
|
|
|
if (IsValid(Assembly))
|
|
{
|
|
return Assembly;
|
|
}
|
|
|
|
return FindOwningAssemblySlow(Enum);
|
|
}
|
|
|
|
|
|
UCSAssembly* UCSManager::FindOwningAssemblySlow(UField *Field)
|
|
{
|
|
// Slow path for native classes. This runs once per new native class.
|
|
const FCSFieldName ClassName = FCSFieldName(Field);
|
|
|
|
for (TPair<FName, TObjectPtr<UCSAssembly>>& LoadedAssembly : LoadedAssemblies)
|
|
{
|
|
if (TSharedPtr<FGCHandle> TypeHandle = LoadedAssembly.Value->TryFindTypeHandle(ClassName); !TypeHandle.IsValid() || TypeHandle->IsNull())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
uint32 FieldID = Field->GetUniqueID();
|
|
NativeClassToAssemblyMap.AddByHash(FieldID, FieldID, LoadedAssembly.Value);
|
|
return LoadedAssembly.Value;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
FGCHandle UCSManager::FindManagedObject(const UObject* Object)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(UCSManager::FindManagedObject);
|
|
|
|
if (!IsValid(Object))
|
|
{
|
|
return FGCHandle::Null();
|
|
}
|
|
|
|
uint32 ObjectID = Object->GetUniqueID();
|
|
if (TSharedPtr<FGCHandle>* FoundHandle = ManagedObjectHandles.FindByHash(ObjectID, ObjectID))
|
|
{
|
|
#if WITH_EDITOR
|
|
// During full hot reload only the managed objects are GCd as we reload the assemblies.
|
|
// So the C# counterpart can be invalid even if the handle can be found, so we need to create a new one.
|
|
TSharedPtr<FGCHandle> HandlePtr = *FoundHandle;
|
|
if (HandlePtr.IsValid() && !HandlePtr->IsNull())
|
|
{
|
|
return *HandlePtr;
|
|
}
|
|
#else
|
|
return **FoundHandle;
|
|
#endif
|
|
}
|
|
|
|
// No existing handle found, we need to create a new managed object.
|
|
UCSAssembly* OwningAssembly = FindOwningAssembly(Object->GetClass());
|
|
if (!IsValid(OwningAssembly))
|
|
{
|
|
UE_LOGFMT(LogUnrealSharp, Error, "Failed to find assembly for {0}", *Object->GetName());
|
|
return FGCHandle::Null();
|
|
}
|
|
|
|
return *OwningAssembly->CreateManagedObject(Object);
|
|
}
|
|
|
|
FGCHandle UCSManager::FindOrCreateManagedInterfaceWrapper(UObject* Object, UClass* InterfaceClass)
|
|
{
|
|
if (!Object->GetClass()->ImplementsInterface(InterfaceClass))
|
|
{
|
|
return FGCHandle::Null();
|
|
}
|
|
|
|
// No existing handle found, we need to create a new managed object.
|
|
UCSAssembly* OwningAssembly = FindOwningAssembly(InterfaceClass);
|
|
if (!IsValid(OwningAssembly))
|
|
{
|
|
UE_LOGFMT(LogUnrealSharp, Error, "Failed to find assembly for {0}", *InterfaceClass->GetName());
|
|
return FGCHandle::Null();
|
|
}
|
|
|
|
TSharedPtr<FGCHandle> FoundHandle = OwningAssembly->FindOrCreateManagedInterfaceWrapper(Object, InterfaceClass);
|
|
if (!FoundHandle.IsValid())
|
|
{
|
|
return FGCHandle::Null();
|
|
}
|
|
|
|
return *FoundHandle;
|
|
}
|