678
									
								
								Plugins/UnrealSharp/Source/UnrealSharpCore/CSManager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										678
									
								
								Plugins/UnrealSharp/Source/UnrealSharpCore/CSManager.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,678 @@ | ||||
| #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; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user