private static void GenerateAndCompileMissingAssemblies() { string projectFileName = FPaths.ProjectFilePath; if (!FBuild.WithEditor || string.IsNullOrEmpty(projectFileName)) { return; } // NOTE: This is called before ManagedUnrealModuleInfo.Load / ManagedUnrealTypes.Load so some things may not be accessible. // Maybe call load then unload after this is finished? // NOTE: Most of the functions called here use FSlowTask but the UI isn't available until the engine is fully initialized. // Instead use FFeedbackContext to display a progress bar (FDesktopPlatformModule::Get()->GetNativeFeedbackContext()) // TODO: Redirect some of the FSlowTask messages to the log, so that the "show log" button displays useful info? codeGenContext = FFeedbackContext.GetGetDesktopFeedbackContext(); string dialogTitle = "USharp"; CodeGeneratorSettings settings = new CodeGeneratorSettings(); string engineWrapperDllPath = Path.Combine(settings.GetManagedModulesDir(), "bin", "Debug", "UnrealEngine.dll"); string engineWrapperSlnPath = Path.Combine(settings.GetManagedModulesDir(), "UnrealEngine.sln"); bool compileEngineWrapperCode = false; if (!File.Exists(engineWrapperSlnPath)) { if (FMessage.OpenDialog(EAppMsgType.YesNo, "C# engine wrapper code not found. Generate it now?", dialogTitle) == EAppReturnType.Yes) { codeGenContext.BeginSlowTask("Generating C# engine wrapper code (this might take a while...)", true); CodeGenerator.GenerateCode(new string[] { "modules" }); codeGenContext.EndSlowTask(); compileEngineWrapperCode = true; } } if (compileEngineWrapperCode || (!File.Exists(engineWrapperDllPath) && File.Exists(engineWrapperSlnPath))) { if (compileEngineWrapperCode || FMessage.OpenDialog(EAppMsgType.YesNo, "C# engine wrapper code isn't compiled. Compile it now?", dialogTitle) == EAppReturnType.Yes) { codeGenContext.BeginSlowTask("Compiling C# engine wrapper code (this might take a while...)", true); bool compiled = CodeGenerator.CompileGeneratedCode(); codeGenContext.EndSlowTask(); if (!compiled) { WarnCompileFailed(settings, null, dialogTitle); } } } string projectName = Path.GetFileNameWithoutExtension(projectFileName); string gameSlnPath = Path.Combine(settings.GetManagedDir(), projectName + ".Managed.sln"); string gameDllPath = Path.Combine(FPaths.ProjectDir, "Binaries", "Managed", projectName + ".Managed.dll"); if (!File.Exists(gameSlnPath) && FMessage.OpenDialog(EAppMsgType.YesNo, "USharp is enabled but the C# game project files weren't found. Generate them now?", dialogTitle) == EAppReturnType.Yes) { TemplateProjectGenerator.Generate(); } if (File.Exists(gameSlnPath) && !File.Exists(gameDllPath) && FMessage.OpenDialog(EAppMsgType.YesNo, "C# game project code isn't compiled. Compile it now?", dialogTitle) == EAppReturnType.Yes) { codeGenContext.BeginSlowTask("Compiling C# game project code (this might take a while...)", true); bool compiled = CodeGenerator.CompileCode(gameSlnPath, null); codeGenContext.EndSlowTask(); if (!compiled) { WarnCompileFailed(settings, null, dialogTitle); } } codeGenContext = null; }
private static void OnNativeFunctionsRegistered() { bool reloading = HotReload.IsReloading; HotReload.MinimalReload = Native_SharpHotReloadUtils.Get_MinimalHotReload(); FMessage.Log("Runtime: " + SharedRuntimeState.GetRuntimeInfo(false)); // HACK: Removing EPackageFlags.EditorOnly on the USharp package so that C# classes aren't tagged as // EObjectMark.EditorOnly. The correct thing to do would be to seperate USharp into seperate // Editor/Runtime modules. IntPtr package = NativeReflection.FindPackage(IntPtr.Zero, "/Script/USharp"); if (package != IntPtr.Zero) { Native_UPackage.ClearPackageFlags(package, EPackageFlags.EditorOnly); } using (var timing = HotReload.Timing.Create(HotReload.Timing.UnrealTypes_Load)) { UnrealTypes.Load(); } if (HotReload.IsReloading) { HotReload.OnPreReloadBegin(); } using (var timing = HotReload.Timing.Create(HotReload.Timing.UnrealTypes_LoadNative)) { // Load the underlying native type info for generated types (class address/properties/functions/offsets) UnrealTypes.LoadNative(); } using (var timing = HotReload.Timing.Create(HotReload.Timing.UClass_Load)) { // Load native classes UClass.Load(); } // If any assemblies are loaded make sure to load their unreal types if (!AssemblyContext.IsCoreCLR || CurrentAssemblyContext.Reference.IsInvalid) { // .NET Core should resolve with AssemblyLoadContext.Resolving (unless the contexts aren't set up) CurrentAssemblyContext.AssemblyResolve += CurrentDomain_AssemblyResolve; } CurrentAssemblyContext.AssemblyLoad += OnAssemblyLoad; CurrentAssemblyContext.Resolving += CurrentAssemblyContext_Resolving; using (var timing = HotReload.Timing.Create(HotReload.Timing.NativeFunctions_LoadAssemblies)) { // Load managed assemblies (game assembly, and any others which may need loading) LoadAssemblies(); } if (HotReload.IsReloading) { HotReload.OnPreReloadEnd(); } using (var timing = HotReload.Timing.Create(HotReload.Timing.ManagedUnrealModuleInfo_Load)) { // Load managed module infos ManagedUnrealModuleInfo.Load(); } using (var timing = HotReload.Timing.Create(HotReload.Timing.ManagedUnrealTypes_Load)) { // Load / register managed unreal types ManagedUnrealTypes.Load(); } using (var timing = HotReload.Timing.Create(HotReload.Timing.HotReload_OnReload)) { // Let HotReload handle reloading if this is a reload if (HotReload.IsReloading) { HotReload.OnReload(); } } // Clear the hot-reload data store if it isn't cleared already if (HotReload.Data != null) { HotReload.Data.Close(); HotReload.Data = null; } if (FBuild.WithEditor && reloading) { using (var timing = HotReload.Timing.Create(HotReload.Timing.UObject_CollectGarbage)) { // If we are hotreloading collect garbage to clean up trashed types / reinstanced objects UObject.CollectGarbage(GCHelper.GarbageCollectionKeepFlags, true); } if (!ManagedUnrealTypes.SkipBroadcastHotReload) { using (var timing = HotReload.Timing.Create(HotReload.Timing.SharpHotReloadUtils_BroadcastOnHotReload)) { // Broadcast the native OnHotReload (if we don't do this we would need to reimplement various // handlers to ensure correct reloading. One example is FBlueprintActionDatabase::ReloadAll // which needs to be called otherwise the action database will hold onto our old class members // and would produce erros when opening blueprints). // true will show the C++ "Hot Reload Complete!" notification (are there any other differences?) //Native_SharpHotReloadUtils.BroadcastOnHotReload(true); // The notification rendering gets messed up the longer hotreload takes. Wait 1 frame to ensure // that the notification gets fully rendered (though the audio still seems to mess up) Coroutine.StartCoroutine(null, DeferBroadcastHotReload()); } } } // Generate / update the C# game project TemplateProjectGenerator.Generate(); using (var timing = HotReload.Timing.Create(HotReload.Timing.GC_Collect)) { // We likely created a bunch of garbage, best to clean it up now. GC.Collect(); } }