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