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