Merge pull request #30584 from neikeq/yatta

Mono: Better handling of missing/outdated API assemblies
This commit is contained in:
Ignacio Roldán Etcheverry 2019-07-14 19:39:26 +02:00 committed by GitHub
commit 7f3490c5e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 333 additions and 594 deletions

View File

@ -114,15 +114,20 @@ void CSharpLanguage::init() {
gdmono = memnew(GDMono);
gdmono->initialize();
#ifndef MONO_GLUE_ENABLED
WARN_PRINT("This binary is built with `mono_glue=no` and cannot be used for scripting");
#endif
#if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED)
// Generate bindings here, before loading assemblies. `initialize_load_assemblies` aborts
// the applications if the api assemblies or the main tools assembly is missing, but this
// is not a problem for BindingsGenerator as it only needs the tools project editor assembly.
List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
BindingsGenerator::handle_cmdline_args(cmdline_args);
#endif
#ifndef MONO_GLUE_ENABLED
print_line("Run this binary with `--generate-mono-glue path/to/modules/mono/glue`");
#endif
gdmono->initialize_load_assemblies();
#ifdef TOOLS_ENABLED
EditorNode::add_init_callback(&_editor_init_callback);
@ -710,14 +715,6 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
return false; // No assembly to load
}
#ifdef TOOLS_ENABLED
if (!gdmono->get_core_api_assembly() && gdmono->metadata_is_api_assembly_invalidated(APIAssembly::API_CORE))
return false; // The core API assembly to load is invalidated
if (!gdmono->get_editor_api_assembly() && gdmono->metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR))
return false; // The editor API assembly to load is invalidated
#endif
return true;
}

View File

@ -1,9 +1,9 @@
using Godot;
using System;
using System.Collections.Generic;
using Godot.Collections;
using GodotTools.Internals;
using GodotTools.ProjectEditor;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
using Directory = GodotTools.Utils.Directory;
@ -26,7 +26,7 @@ namespace GodotTools
public static void AddItem(string projectPath, string itemType, string include)
{
if (!(bool) Internal.GlobalDef("mono/project/auto_update_project", true))
if (!(bool) GlobalDef("mono/project/auto_update_project", true))
return;
ProjectUtils.AddItemToProjectChecked(projectPath, itemType, include);

View File

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using GodotTools.Build;
using GodotTools.Internals;
using GodotTools.Utils;
using static GodotTools.Internals.Globals;
using Error = Godot.Error;
using File = GodotTools.Utils.File;
using Directory = GodotTools.Utils.Directory;
@ -192,134 +193,14 @@ namespace GodotTools
return false;
}
private static bool CopyApiAssembly(string srcDir, string dstDir, string assemblyName, ApiAssemblyType apiType)
{
// Create destination directory if needed
if (!Directory.Exists(dstDir))
{
try
{
Directory.CreateDirectory(dstDir);
}
catch (IOException e)
{
ShowBuildErrorDialog($"Failed to create destination directory for the API assemblies. Exception message: {e.Message}");
return false;
}
}
string assemblyFile = assemblyName + ".dll";
string assemblySrc = Path.Combine(srcDir, assemblyFile);
string assemblyDst = Path.Combine(dstDir, assemblyFile);
if (!File.Exists(assemblyDst) || File.GetLastWriteTime(assemblySrc) > File.GetLastWriteTime(assemblyDst) ||
Internal.MetadataIsApiAssemblyInvalidated(apiType))
{
string xmlFile = $"{assemblyName}.xml";
string pdbFile = $"{assemblyName}.pdb";
try
{
File.Copy(Path.Combine(srcDir, xmlFile), Path.Combine(dstDir, xmlFile));
}
catch (IOException e)
{
Godot.GD.PushWarning(e.ToString());
}
try
{
File.Copy(Path.Combine(srcDir, pdbFile), Path.Combine(dstDir, pdbFile));
}
catch (IOException e)
{
Godot.GD.PushWarning(e.ToString());
}
try
{
File.Copy(assemblySrc, assemblyDst);
}
catch (IOException e)
{
ShowBuildErrorDialog($"Failed to copy {assemblyFile}. Exception message: {e.Message}");
return false;
}
Internal.MetadataSetApiAssemblyInvalidated(apiType, false);
}
return true;
}
public static bool MakeApiAssembly(ApiAssemblyType apiType, string config)
{
string apiName = apiType == ApiAssemblyType.Core ? ApiAssemblyNames.Core : ApiAssemblyNames.Editor;
string editorPrebuiltApiDir = Path.Combine(GodotSharpDirs.DataEditorPrebuiltApiDir, config);
string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, config);
if (File.Exists(Path.Combine(editorPrebuiltApiDir, $"{apiName}.dll")))
{
using (var copyProgress = new EditorProgress("mono_copy_prebuilt_api_assembly", $"Copying prebuilt {apiName} assembly...", 1))
{
copyProgress.Step($"Copying {apiName} assembly", 0);
return CopyApiAssembly(editorPrebuiltApiDir, resAssembliesDir, apiName, apiType);
}
}
const string apiSolutionName = ApiAssemblyNames.SolutionName;
using (var pr = new EditorProgress($"mono_build_release_{apiSolutionName}", $"Building {apiSolutionName} solution...", 3))
{
pr.Step($"Generating {apiSolutionName} solution", 0);
string apiSlnDir = Path.Combine(GodotSharpDirs.MonoSolutionsDir, _ApiFolderName(ApiAssemblyType.Core));
string apiSlnFile = Path.Combine(apiSlnDir, $"{apiSolutionName}.sln");
if (!Directory.Exists(apiSlnDir) || !File.Exists(apiSlnFile))
{
var bindingsGenerator = new BindingsGenerator();
if (!Godot.OS.IsStdoutVerbose())
bindingsGenerator.LogPrintEnabled = false;
Error err = bindingsGenerator.GenerateCsApi(apiSlnDir);
if (err != Error.Ok)
{
ShowBuildErrorDialog($"Failed to generate {apiSolutionName} solution. Error: {err}");
return false;
}
}
pr.Step($"Building {apiSolutionName} solution", 1);
if (!BuildApiSolution(apiSlnDir, config))
return false;
pr.Step($"Copying {apiName} assembly", 2);
// Copy the built assembly to the assemblies directory
string apiAssemblyDir = Path.Combine(apiSlnDir, apiName, "bin", config);
if (!CopyApiAssembly(apiAssemblyDir, resAssembliesDir, apiName, apiType))
return false;
}
return true;
}
public static bool BuildProjectBlocking(string config, IEnumerable<string> godotDefines)
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return true; // No solution to build
string apiConfig = config == "Release" ? "Release" : "Debug";
if (!MakeApiAssembly(ApiAssemblyType.Core, apiConfig))
return false;
if (!MakeApiAssembly(ApiAssemblyType.Editor, apiConfig))
return false;
// Make sure to update the API assemblies if they happen to be missing. Just in
// case the user decided to delete them at some point after they were loaded.
Internal.UpdateApiAssembliesFromPrebuilt();
using (var pr = new EditorProgress("mono_project_debug_build", "Building project solution...", 1))
{
@ -376,7 +257,7 @@ namespace GodotTools
{
// Build tool settings
Internal.EditorDef("mono/builds/build_tool", OS.IsWindows() ? BuildTool.MsBuildVs : BuildTool.MsBuildMono);
EditorDef("mono/builds/build_tool", OS.IsWindows() ? BuildTool.MsBuildVs : BuildTool.MsBuildMono);
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
@ -390,7 +271,7 @@ namespace GodotTools
$"{PropNameMsbuildMono},{PropNameXbuild}"
});
Internal.EditorDef("mono/builds/print_build_output", false);
EditorDef("mono/builds/print_build_output", false);
}
}
}

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.IO;
using GodotTools.Internals;
using GodotTools.ProjectEditor;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
using Path = System.IO.Path;
using OS = GodotTools.Utils.OS;
@ -26,15 +27,15 @@ namespace GodotTools
private MonoDevelopInstance monoDevelopInstance;
private MonoDevelopInstance visualStudioForMacInstance;
private WeakReference<GodotSharpExport> exportPluginWeak;
private WeakRef exportPluginWeak; // TODO Use WeakReference once we have proper serialization
public MonoBottomPanel MonoBottomPanel { get; private set; }
private bool CreateProjectSolution()
{
using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...", 2)) // TTR("Generating solution...")
using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 2))
{
pr.Step("Generating C# project..."); // TTR("Generating C# project...")
pr.Step("Generating C# project...".TTR());
string resourceDir = ProjectSettings.GlobalizePath("res://");
@ -67,96 +68,28 @@ namespace GodotTools
}
catch (IOException e)
{
ShowErrorDialog($"Failed to save solution. Exception message: {e.Message}"); // TTR
ShowErrorDialog("Failed to save solution. Exception message: ".TTR() + e.Message);
return false;
}
string apiConfig = "Debug";
// Make sure to update the API assemblies if they happen to be missing. Just in
// case the user decided to delete them at some point after they were loaded.
Internal.UpdateApiAssembliesFromPrebuilt();
if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Core, apiConfig))
return false;
if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Editor, apiConfig))
return false;
pr.Step("Done"); // TTR("Done")
pr.Step("Done".TTR());
// Here, after all calls to progress_task_step
CallDeferred(nameof(_RemoveCreateSlnMenuOption));
}
else
{
ShowErrorDialog("Failed to create C# project."); // TTR
ShowErrorDialog("Failed to create C# project.".TTR());
}
return true;
}
}
private static int _makeApiSolutionsAttempts = 100;
private static bool _makeApiSolutionsRecursionGuard = false;
private void _MakeApiSolutionsIfNeeded()
{
// I'm sick entirely of ProgressDialog
if (Internal.IsMessageQueueFlushing() || Engine.GetMainLoop() == null)
{
if (_makeApiSolutionsAttempts == 0) // This better never happen or I swear...
throw new TimeoutException();
if (Engine.GetMainLoop() != null)
{
if (!Engine.GetMainLoop().IsConnected("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded)))
Engine.GetMainLoop().Connect("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded));
}
else
{
CallDeferred(nameof(_MakeApiSolutionsIfNeededImpl));
}
_makeApiSolutionsAttempts--;
return;
}
// Recursion guard needed because signals don't play well with ProgressDialog either, but unlike
// the message queue, with signals the collateral damage should be minimal in the worst case.
if (!_makeApiSolutionsRecursionGuard)
{
_makeApiSolutionsRecursionGuard = true;
// Oneshot signals don't play well with ProgressDialog either, so we do it this way instead
if (Engine.GetMainLoop().IsConnected("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded)))
Engine.GetMainLoop().Disconnect("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded));
_MakeApiSolutionsIfNeededImpl();
_makeApiSolutionsRecursionGuard = false;
}
}
private void _MakeApiSolutionsIfNeededImpl()
{
// If the project has a solution and C# project make sure the API assemblies are present and up to date
string api_config = "Debug";
string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, api_config);
if (!File.Exists(Path.Combine(resAssembliesDir, $"{ApiAssemblyNames.Core}.dll")) ||
Internal.MetadataIsApiAssemblyInvalidated(ApiAssemblyType.Core))
{
if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Core, api_config))
return;
}
if (!File.Exists(Path.Combine(resAssembliesDir, $"{ApiAssemblyNames.Editor}.dll")) ||
Internal.MetadataIsApiAssemblyInvalidated(ApiAssemblyType.Editor))
{
if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Editor, api_config))
return; // Redundant? I don't think so!
}
}
private void _RemoveCreateSlnMenuOption()
{
menuPopup.RemoveItem(menuPopup.GetItemIndex((int) MenuOptions.CreateSln));
@ -407,7 +340,7 @@ namespace GodotTools
MonoBottomPanel = new MonoBottomPanel();
bottomPanelBtn = AddControlToBottomPanel(MonoBottomPanel, "Mono"); // TTR("Mono")
bottomPanelBtn = AddControlToBottomPanel(MonoBottomPanel, "Mono".TTR());
AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"});
@ -419,7 +352,7 @@ namespace GodotTools
// TODO: Remove or edit this info dialog once Mono support is no longer in alpha
{
menuPopup.AddItem("About C# support", (int) MenuOptions.AboutCSharp); // TTR("About C# support")
menuPopup.AddItem("About C# support".TTR(), (int) MenuOptions.AboutCSharp);
aboutDialog = new AcceptDialog();
editorBaseControl.AddChild(aboutDialog);
aboutDialog.WindowTitle = "Important: C# support is not feature-complete";
@ -441,7 +374,7 @@ namespace GodotTools
var aboutLabel = new Label();
aboutHBox.AddChild(aboutLabel);
aboutLabel.RectMinSize = new Vector2(600, 150) * Internal.EditorScale;
aboutLabel.RectMinSize = new Vector2(600, 150) * EditorScale;
aboutLabel.SizeFlagsVertical = (int) Control.SizeFlags.ExpandFill;
aboutLabel.Autowrap = true;
aboutLabel.Text =
@ -454,7 +387,7 @@ namespace GodotTools
" https://github.com/godotengine/godot/issues\n\n" +
"Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!";
Internal.EditorDef("mono/editor/show_info_on_start", true);
EditorDef("mono/editor/show_info_on_start", true);
// CheckBox in main container
aboutDialogCheckBox = new CheckBox {Text = "Show this warning when starting the editor"};
@ -464,16 +397,13 @@ namespace GodotTools
if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
{
// Defer this task because EditorProgress calls Main::iterarion() and the main loop is not yet initialized.
CallDeferred(nameof(_MakeApiSolutionsIfNeeded));
// Make sure the existing project has Api assembly references configured correctly
CSharpProject.FixApiHintPath(GodotSharpDirs.ProjectCsProjPath);
}
else
{
bottomPanelBtn.Hide();
menuPopup.AddItem("Create C# solution", (int) MenuOptions.CreateSln); // TTR("Create C# solution")
menuPopup.AddItem("Create C# solution".TTR(), (int) MenuOptions.CreateSln);
}
menuPopup.Connect("id_pressed", this, nameof(_MenuOptionPressed));
@ -488,7 +418,7 @@ namespace GodotTools
AddControlToContainer(CustomControlContainer.Toolbar, buildButton);
// External editor settings
Internal.EditorDef("mono/editor/external_editor", ExternalEditor.None);
EditorDef("mono/editor/external_editor", ExternalEditor.None);
string settingsHintStr = "Disabled";
@ -520,7 +450,7 @@ namespace GodotTools
// Export plugin
var exportPlugin = new GodotSharpExport();
AddExportPlugin(exportPlugin);
exportPluginWeak = new WeakReference<GodotSharpExport>(exportPlugin);
exportPluginWeak = WeakRef(exportPlugin);
GodotSharpBuilds.Initialize();
}
@ -529,13 +459,15 @@ namespace GodotTools
{
base.Dispose(disposing);
if (exportPluginWeak.TryGetTarget(out var exportPlugin))
if (exportPluginWeak != null)
{
// We need to dispose our export plugin before the editor destroys EditorSettings.
// Otherwise, if the GC disposes it at a later time, EditorExportPlatformAndroid
// will be freed after EditorSettings already was, and its device polling thread
// will try to access the EditorSettings singleton, resulting in null dereferencing.
exportPlugin.Dispose();
(exportPluginWeak.GetRef() as GodotSharpExport)?.Dispose();
exportPluginWeak.Dispose();
}
}

View File

@ -44,6 +44,7 @@
<Compile Include="Internals\GodotSharpDirs.cs" />
<Compile Include="Internals\Internal.cs" />
<Compile Include="Internals\ScriptClassParser.cs" />
<Compile Include="Internals\Globals.cs" />
<Compile Include="MonoDevelopInstance.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Build\BuildSystem.cs" />

View File

@ -1,5 +1,6 @@
using Godot;
using GodotTools.Internals;
using static GodotTools.Internals.Globals;
namespace GodotTools
{
@ -37,7 +38,7 @@ namespace GodotTools
watchTimer = new Timer
{
OneShot = false,
WaitTime = (float) Internal.EditorDef("mono/assembly_watch_interval_sec", 0.5)
WaitTime = (float) EditorDef("mono/assembly_watch_interval_sec", 0.5)
};
watchTimer.Connect("timeout", this, nameof(TimerTimeout));
AddChild(watchTimer);

View File

@ -0,0 +1,33 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace GodotTools.Internals
{
public static class Globals
{
public static float EditorScale => internal_EditorScale();
public static object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false) =>
internal_GlobalDef(setting, defaultValue, restartIfChanged);
public static object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) =>
internal_EditorDef(setting, defaultValue, restartIfChanged);
[SuppressMessage("ReSharper", "InconsistentNaming")]
public static string TTR(this string text) => internal_TTR(text);
// Internal Calls
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern float internal_EditorScale();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object internal_GlobalDef(string setting, object defaultValue, bool restartIfChanged);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object internal_EditorDef(string setting, object defaultValue, bool restartIfChanged);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_TTR(string text);
}
}

View File

@ -10,13 +10,8 @@ namespace GodotTools.Internals
public const string CSharpLanguageType = "CSharpScript";
public const string CSharpLanguageExtension = "cs";
public static float EditorScale => internal_EditorScale();
public static object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false) =>
internal_GlobalDef(setting, defaultValue, restartIfChanged);
public static object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) =>
internal_EditorDef(setting, defaultValue, restartIfChanged);
public static string UpdateApiAssembliesFromPrebuilt() =>
internal_UpdateApiAssembliesFromPrebuilt();
public static string FullTemplatesDir =>
internal_FullTemplatesDir();
@ -25,14 +20,6 @@ namespace GodotTools.Internals
public static bool IsOsxAppBundleInstalled(string bundleId) => internal_IsOsxAppBundleInstalled(bundleId);
public static bool MetadataIsApiAssemblyInvalidated(ApiAssemblyType apiType) =>
internal_MetadataIsApiAssemblyInvalidated(apiType);
public static void MetadataSetApiAssemblyInvalidated(ApiAssemblyType apiType, bool invalidated) =>
internal_MetadataSetApiAssemblyInvalidated(apiType, invalidated);
public static bool IsMessageQueueFlushing() => internal_IsMessageQueueFlushing();
public static bool GodotIs32Bits() => internal_GodotIs32Bits();
public static bool GodotIsRealTDouble() => internal_GodotIsRealTDouble();
@ -62,13 +49,7 @@ namespace GodotTools.Internals
// Internal Calls
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern float internal_EditorScale();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object internal_GlobalDef(string setting, object defaultValue, bool restartIfChanged);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern object internal_EditorDef(string setting, object defaultValue, bool restartIfChanged);
private static extern string internal_UpdateApiAssembliesFromPrebuilt();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_FullTemplatesDir();
@ -79,15 +60,6 @@ namespace GodotTools.Internals
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_IsOsxAppBundleInstalled(string bundleId);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_MetadataIsApiAssemblyInvalidated(ApiAssemblyType apiType);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void internal_MetadataSetApiAssemblyInvalidated(ApiAssemblyType apiType, bool invalidated);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_IsMessageQueueFlushing();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool internal_GodotIs32Bits();

View File

@ -3,6 +3,7 @@ using System;
using System.IO;
using Godot.Collections;
using GodotTools.Internals;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
using Path = System.IO.Path;
@ -254,7 +255,7 @@ namespace GodotTools
panelTabs = new TabContainer
{
TabAlign = TabContainer.TabAlignEnum.Left,
RectMinSize = new Vector2(0, 228) * Internal.EditorScale,
RectMinSize = new Vector2(0, 228) * EditorScale,
SizeFlagsVertical = (int) SizeFlags.ExpandFill
};
panelTabs.AddStyleboxOverride("panel", editorBaseControl.GetStylebox("DebuggerPanel", "EditorStyles"));
@ -266,7 +267,7 @@ namespace GodotTools
// Builds tab
panelBuildsTab = new VBoxContainer
{
Name = "Builds", // TTR
Name = "Builds".TTR(),
SizeFlagsHorizontal = (int) SizeFlags.ExpandFill
};
panelTabs.AddChild(panelBuildsTab);
@ -276,7 +277,7 @@ namespace GodotTools
var buildProjectBtn = new Button
{
Text = "Build Project", // TTR
Text = "Build Project".TTR(),
FocusMode = FocusModeEnum.None
};
buildProjectBtn.Connect("pressed", this, nameof(BuildProjectPressed));
@ -286,7 +287,7 @@ namespace GodotTools
warningsBtn = new ToolButton
{
Text = "Warnings", // TTR
Text = "Warnings".TTR(),
ToggleMode = true,
Pressed = true,
Visible = false,
@ -297,7 +298,7 @@ namespace GodotTools
errorsBtn = new ToolButton
{
Text = "Errors", // TTR
Text = "Errors".TTR(),
ToggleMode = true,
Pressed = true,
Visible = false,
@ -310,7 +311,7 @@ namespace GodotTools
viewLogBtn = new Button
{
Text = "View log", // TTR
Text = "View log".TTR(),
FocusMode = FocusModeEnum.None,
Visible = false
};

View File

@ -30,7 +30,6 @@
#include "editor_internal_calls.h"
#include "core/message_queue.h"
#include "core/os/os.h"
#include "core/version.h"
#include "editor/editor_node.h"
@ -231,24 +230,34 @@ uint32_t godot_icall_GodotSharpExport_GetExportedAssemblyDependencies(MonoString
return GodotSharpExport::get_exported_assembly_dependencies(project_dll_name, project_dll_src_path, build_config, custom_lib_dir, dependencies);
}
float godot_icall_Internal_EditorScale() {
float godot_icall_Globals_EditorScale() {
return EDSCALE;
}
MonoObject *godot_icall_Internal_GlobalDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
MonoObject *godot_icall_Globals_GlobalDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed);
return GDMonoMarshal::variant_to_mono_object(result);
}
MonoObject *godot_icall_Internal_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed);
Variant result = _EDITOR_DEF(setting, default_value, (bool)p_restart_if_changed);
return GDMonoMarshal::variant_to_mono_object(result);
}
MonoString *godot_icall_Globals_TTR(MonoString *p_text) {
String text = GDMonoMarshal::mono_string_to_godot(p_text);
return GDMonoMarshal::mono_string_from_godot(TTR(text));
}
MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt() {
String error_str = GDMono::get_singleton()->update_api_assemblies_from_prebuilt();
return GDMonoMarshal::mono_string_from_godot(error_str);
}
MonoString *godot_icall_Internal_FullTemplatesDir() {
String full_templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
return GDMonoMarshal::mono_string_from_godot(full_templates_dir);
@ -269,18 +278,6 @@ MonoBoolean godot_icall_Internal_IsOsxAppBundleInstalled(MonoString *p_bundle_id
#endif
}
MonoBoolean godot_icall_Internal_MetadataIsApiAssemblyInvalidated(int32_t p_api_type) {
return GDMono::get_singleton()->metadata_is_api_assembly_invalidated((APIAssembly::Type)p_api_type);
}
void godot_icall_Internal_MetadataSetApiAssemblyInvalidated(int32_t p_api_type, MonoBoolean p_invalidated) {
GDMono::get_singleton()->metadata_set_api_assembly_invalidated((APIAssembly::Type)p_api_type, (bool)p_invalidated);
}
MonoBoolean godot_icall_Internal_IsMessageQueueFlushing() {
return (MonoBoolean)MessageQueue::get_singleton()->is_flushing();
}
MonoBoolean godot_icall_Internal_GodotIs32Bits() {
return sizeof(void *) == 4;
}
@ -402,15 +399,10 @@ void register_editor_internal_calls() {
mono_add_internal_call("GodotTools.GodotSharpExport::internal_GetExportedAssemblyDependencies", (void *)godot_icall_GodotSharpExport_GetExportedAssemblyDependencies);
// Internals
mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorScale", (void *)godot_icall_Internal_EditorScale);
mono_add_internal_call("GodotTools.Internals.Internal::internal_GlobalDef", (void *)godot_icall_Internal_GlobalDef);
mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorDef", (void *)godot_icall_Internal_EditorDef);
mono_add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", (void *)godot_icall_Internal_UpdateApiAssembliesFromPrebuilt);
mono_add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", (void *)godot_icall_Internal_FullTemplatesDir);
mono_add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", (void *)godot_icall_Internal_SimplifyGodotPath);
mono_add_internal_call("GodotTools.Internals.Internal::internal_IsOsxAppBundleInstalled", (void *)godot_icall_Internal_IsOsxAppBundleInstalled);
mono_add_internal_call("GodotTools.Internals.Internal::internal_MetadataIsApiAssemblyInvalidated", (void *)godot_icall_Internal_MetadataIsApiAssemblyInvalidated);
mono_add_internal_call("GodotTools.Internals.Internal::internal_MetadataSetApiAssemblyInvalidated", (void *)godot_icall_Internal_MetadataSetApiAssemblyInvalidated);
mono_add_internal_call("GodotTools.Internals.Internal::internal_IsMessageQueueFlushing", (void *)godot_icall_Internal_IsMessageQueueFlushing);
mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", (void *)godot_icall_Internal_GodotIs32Bits);
mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", (void *)godot_icall_Internal_GodotIsRealTDouble);
mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", (void *)godot_icall_Internal_GodotMainIteration);
@ -424,6 +416,12 @@ void register_editor_internal_calls() {
mono_add_internal_call("GodotTools.Internals.Internal::internal_GetScriptsMetadataOrNothing", (void *)godot_icall_Internal_GetScriptsMetadataOrNothing);
mono_add_internal_call("GodotTools.Internals.Internal::internal_MonoWindowsInstallRoot", (void *)godot_icall_Internal_MonoWindowsInstallRoot);
// Globals
mono_add_internal_call("GodotTools.Internals.Globals::internal_EditorScale", (void *)godot_icall_Globals_EditorScale);
mono_add_internal_call("GodotTools.Internals.Globals::internal_GlobalDef", (void *)godot_icall_Globals_GlobalDef);
mono_add_internal_call("GodotTools.Internals.Globals::internal_EditorDef", (void *)godot_icall_Globals_EditorDef);
mono_add_internal_call("GodotTools.Internals.Globals::internal_TTR", (void *)godot_icall_Globals_TTR);
// Utils.OS
mono_add_internal_call("GodotTools.Utils.OS::GetPlatformName", (void *)godot_icall_Utils_OS_GetPlatformName);
}

View File

@ -59,10 +59,6 @@
#include "android_mono_config.gen.h"
#endif
#define OUT_OF_SYNC_ERR_MESSAGE(m_assembly_name) "The assembly '" m_assembly_name "' is out of sync. " \
"This error is expected if you just upgraded to a newer Godot version. " \
"Building the project will update the assembly to the correct version."
GDMono *GDMono::singleton = NULL;
namespace {
@ -304,26 +300,9 @@ void GDMono::initialize() {
mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL);
#ifndef TOOLS_ENABLED
if (!DirAccess::exists("res://.mono")) {
// 'res://.mono/' is missing so there is nothing to load. We don't need to initialize mono, but
// we still do so unless mscorlib is missing (which is the case for projects that don't use C#).
String mscorlib_fname("mscorlib.dll");
Vector<String> search_dirs;
GDMonoAssembly::fill_search_dirs(search_dirs);
bool found = false;
for (int i = 0; i < search_dirs.size(); i++) {
if (FileAccess::exists(search_dirs[i].plus_file(mscorlib_fname))) {
found = true;
break;
}
}
if (!found)
return; // mscorlib is missing, do not initialize mono
}
// Export templates only load the Mono runtime if the project uses it
if (!DirAccess::exists("res://.mono"))
return;
#endif
root_domain = mono_jit_init_version("GodotEngine.RootDomain", "v4.0.30319");
@ -354,63 +333,48 @@ void GDMono::initialize() {
_register_internal_calls();
// The following assemblies are not required at initialization
#ifdef MONO_GLUE_ENABLED
if (_load_api_assemblies()) {
// Everything is fine with the api assemblies, load the tools and project assemblies
#if defined(TOOLS_ENABLED)
ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies");
ERR_FAIL_COND(!_load_tools_assemblies());
#endif
_load_project_assembly();
} else {
if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated))
#ifdef TOOLS_ENABLED
|| (editor_api_assembly && editor_api_assembly_out_of_sync)
#endif
) {
#ifdef TOOLS_ENABLED
// The assembly was successfully loaded, but the full api could not be cached.
// This is most likely an outdated assembly loaded because of an invalid version in the
// metadata, so we invalidate the version in the metadata and unload the script domain.
if (core_api_assembly_out_of_sync) {
ERR_PRINT(OUT_OF_SYNC_ERR_MESSAGE(CORE_API_ASSEMBLY_NAME));
metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
} else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed");
metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
}
if (editor_api_assembly_out_of_sync) {
ERR_PRINT(OUT_OF_SYNC_ERR_MESSAGE(EDITOR_API_ASSEMBLY_NAME));
metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true);
}
print_line("Mono: Proceeding to unload scripts domain because of invalid API assemblies.");
Error err = _unload_scripts_domain();
if (err != OK) {
WARN_PRINT("Mono: Failed to unload scripts domain");
}
#else
ERR_PRINT("The loaded API assembly is invalid");
CRASH_NOW();
#endif // TOOLS_ENABLED
}
}
#else
print_verbose("Mono: Glue disabled, ignoring script assemblies.");
#endif // MONO_GLUE_ENABLED
print_verbose("Mono: INITIALIZED");
}
#ifdef MONO_GLUE_ENABLED
void GDMono::initialize_load_assemblies() {
#ifndef MONO_GLUE_ENABLED
ERR_EXPLAIN("Mono: This binary was built with `mono_glue=no`; cannot load assemblies");
CRASH_NOW();
#endif
// Load assemblies. The API and tools assemblies are required,
// the application is aborted if these assemblies cannot be loaded.
_load_api_assemblies();
#if defined(TOOLS_ENABLED)
if (!_load_tools_assemblies()) {
ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies");
CRASH_NOW();
}
#endif
// Load the project's main assembly. This doesn't necessarily need to succeed.
// The game may not be using .NET at all, or if the project does use .NET and
// we're running in the editor, it may just happen to be it wasn't built yet.
if (!_load_project_assembly()) {
if (OS::get_singleton()->is_stdout_verbose())
print_error("Mono: Failed to load project assembly");
}
}
bool GDMono::_are_api_assemblies_out_of_sync() {
bool out_of_sync = core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated);
#ifdef TOOLS_ENABLED
if (!out_of_sync)
out_of_sync = editor_api_assembly && editor_api_assembly_out_of_sync;
#endif
return out_of_sync;
}
namespace GodotSharpBindings {
#ifdef MONO_GLUE_ENABLED
uint64_t get_core_api_hash();
#ifdef TOOLS_ENABLED
@ -419,13 +383,33 @@ uint64_t get_editor_api_hash();
uint32_t get_bindings_version();
void register_generated_icalls();
} // namespace GodotSharpBindings
#else
uint64_t get_core_api_hash() {
CRASH_NOW();
GD_UNREACHABLE();
}
#ifdef TOOLS_ENABLED
uint64_t get_editor_api_hash() {
CRASH_NOW();
GD_UNREACHABLE();
}
#endif
uint32_t get_bindings_version() {
CRASH_NOW();
GD_UNREACHABLE();
}
void register_generated_icalls() {
/* Fine, just do nothing */
}
#endif // MONO_GLUE_ENABLED
} // namespace GodotSharpBindings
void GDMono::_register_internal_calls() {
#ifdef MONO_GLUE_ENABLED
GodotSharpBindings::register_generated_icalls();
#endif
}
void GDMono::_initialize_and_check_api_hashes() {
@ -565,12 +549,21 @@ bool GDMono::_load_corlib_assembly() {
}
#ifdef TOOLS_ENABLED
static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type) {
bool GDMono::copy_prebuilt_api_assembly(APIAssembly::Type p_api_type) {
bool &api_assembly_out_of_sync = (p_api_type == APIAssembly::API_CORE) ?
GDMono::get_singleton()->core_api_assembly_out_of_sync :
GDMono::get_singleton()->editor_api_assembly_out_of_sync;
String src_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
String dst_dir = GodotSharpDirs::get_res_assemblies_dir();
String assembly_name = p_api_type == APIAssembly::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
// Create destination directory if needed
if (!DirAccess::exists(p_dst_dir)) {
DirAccess *da = DirAccess::create_for_path(p_dst_dir);
Error err = da->make_dir_recursive(p_dst_dir);
if (!DirAccess::exists(dst_dir)) {
DirAccess *da = DirAccess::create_for_path(dst_dir);
Error err = da->make_dir_recursive(dst_dir);
memdelete(da);
if (err != OK) {
@ -579,21 +572,19 @@ static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir,
}
}
String assembly_file = p_assembly_name + ".dll";
String assembly_src = p_src_dir.plus_file(assembly_file);
String assembly_dst = p_dst_dir.plus_file(assembly_file);
String assembly_file = assembly_name + ".dll";
String assembly_src = src_dir.plus_file(assembly_file);
String assembly_dst = dst_dir.plus_file(assembly_file);
if (!FileAccess::exists(assembly_dst) ||
FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) ||
GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) {
if (!FileAccess::exists(assembly_dst) || api_assembly_out_of_sync) {
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
String xml_file = p_assembly_name + ".xml";
if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK)
String xml_file = assembly_name + ".xml";
if (da->copy(src_dir.plus_file(xml_file), dst_dir.plus_file(xml_file)) != OK)
WARN_PRINTS("Failed to copy " + xml_file);
String pdb_file = p_assembly_name + ".pdb";
if (da->copy(p_src_dir.plus_file(pdb_file), p_dst_dir.plus_file(pdb_file)) != OK)
String pdb_file = assembly_name + ".pdb";
if (da->copy(src_dir.plus_file(pdb_file), dst_dir.plus_file(pdb_file)) != OK)
WARN_PRINTS("Failed to copy " + pdb_file);
Error err = da->copy(assembly_src, assembly_dst);
@ -603,11 +594,46 @@ static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir,
return false;
}
GDMono::get_singleton()->metadata_set_api_assembly_invalidated(p_api_type, false);
api_assembly_out_of_sync = false;
}
return true;
}
String GDMono::update_api_assemblies_from_prebuilt() {
#define FAIL_REASON(m_out_of_sync, m_prebuilt_exist) \
( \
(m_out_of_sync ? \
String("The assembly is invalidated") : \
String("The assembly was not found")) + \
(m_prebuilt_exist ? \
String(" and the prebuilt assemblies are missing") : \
String(" and we failed to copy the prebuilt assemblies")))
bool api_assembly_out_of_sync = core_api_assembly_out_of_sync || editor_api_assembly_out_of_sync;
String core_assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll");
String editor_assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
if (!api_assembly_out_of_sync && FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path))
return String(); // No update needed
String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
String prebuilt_core_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
String prebuilt_editor_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
if (!FileAccess::exists(prebuilt_core_dll_path) || !FileAccess::exists(prebuilt_editor_dll_path))
return FAIL_REASON(api_assembly_out_of_sync, /* prebuilt_exist: */ false);
// Copy the prebuilt Api
if (!copy_prebuilt_api_assembly(APIAssembly::API_CORE) || !copy_prebuilt_api_assembly(APIAssembly::API_EDITOR))
return FAIL_REASON(api_assembly_out_of_sync, /* prebuilt_exist: */ true);
return String(); // Updated successfully
#undef FAIL_REASON
}
#endif
bool GDMono::_load_core_api_assembly() {
@ -616,35 +642,15 @@ bool GDMono::_load_core_api_assembly() {
return true;
#ifdef TOOLS_ENABLED
if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) {
String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
String prebuilt_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
String invalidated_dll_path = get_invalidated_api_assembly_path(APIAssembly::API_CORE);
if (!FileAccess::exists(prebuilt_dll_path) ||
FileAccess::get_modified_time(invalidated_dll_path) == FileAccess::get_modified_time(prebuilt_dll_path)) {
print_verbose("Mono: Skipping loading of Core API assembly because it was invalidated");
return false;
} else {
// Copy the prebuilt Api
String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
if (!copy_api_assembly(prebuilt_api_dir, res_assemblies_dir, CORE_API_ASSEMBLY_NAME, APIAssembly::API_CORE) ||
!copy_api_assembly(prebuilt_api_dir, res_assemblies_dir, EDITOR_API_ASSEMBLY_NAME, APIAssembly::API_EDITOR)) {
print_verbose("Mono: Failed to copy prebuilt API. Skipping loading of Core API assembly because it was invalidated");
return false;
}
}
}
// For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date
String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll");
bool success = FileAccess::exists(assembly_path) &&
load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &core_api_assembly);
#else
bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &core_api_assembly);
#endif
String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll");
bool success = (FileAccess::exists(assembly_path) &&
load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &core_api_assembly)) ||
load_assembly(CORE_API_ASSEMBLY_NAME, &core_api_assembly);
if (success) {
#ifdef MONO_GLUE_ENABLED
APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(core_api_assembly, APIAssembly::API_CORE);
core_api_assembly_out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash ||
GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
@ -654,9 +660,8 @@ bool GDMono::_load_core_api_assembly() {
_install_trace_listener();
}
#else
GDMonoUtils::update_godot_api_cache();
#endif
} else {
core_api_assembly_out_of_sync = false;
}
return success;
@ -668,44 +673,100 @@ bool GDMono::_load_editor_api_assembly() {
if (editor_api_assembly)
return true;
if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) {
String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
String prebuilt_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
String invalidated_dll_path = get_invalidated_api_assembly_path(APIAssembly::API_EDITOR);
if (!FileAccess::exists(prebuilt_dll_path) ||
FileAccess::get_modified_time(invalidated_dll_path) == FileAccess::get_modified_time(prebuilt_dll_path)) {
print_verbose("Mono: Skipping loading of Editor API assembly because it was invalidated");
return false;
} else {
// Copy the prebuilt editor Api (no need to copy the core api if we got to this point)
String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
if (!copy_api_assembly(prebuilt_api_dir, res_assemblies_dir, EDITOR_API_ASSEMBLY_NAME, APIAssembly::API_EDITOR)) {
print_verbose("Mono: Failed to copy prebuilt API. Skipping loading of Editor API assembly because it was invalidated");
return false;
}
}
}
// For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date
String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
bool success = (FileAccess::exists(assembly_path) &&
load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &editor_api_assembly)) ||
load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
bool success = FileAccess::exists(assembly_path) &&
load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &editor_api_assembly);
if (success) {
#ifdef MONO_GLUE_ENABLED
APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(editor_api_assembly, APIAssembly::API_EDITOR);
editor_api_assembly_out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash ||
GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
CS_GLUE_VERSION != api_assembly_ver.cs_glue_version;
#endif
} else {
editor_api_assembly_out_of_sync = false;
}
return success;
}
#endif
bool GDMono::_try_load_api_assemblies() {
if (!_load_core_api_assembly()) {
if (OS::get_singleton()->is_stdout_verbose())
print_error("Mono: Failed to load Core API assembly");
return false;
}
if (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)
return false;
#ifdef TOOLS_ENABLED
if (!_load_editor_api_assembly()) {
if (OS::get_singleton()->is_stdout_verbose())
print_error("Mono: Failed to load Editor API assembly");
return false;
}
if (editor_api_assembly_out_of_sync)
return false;
#endif
return true;
}
void GDMono::_load_api_assemblies() {
if (!_try_load_api_assemblies()) {
// The API assemblies are out of sync. Fine, try one more time, but this time
// update them from the prebuilt assemblies directory before trying to load them.
// 1. Unload the scripts domain
if (_unload_scripts_domain() != OK) {
ERR_EXPLAIN("Mono: Failed to unload scripts domain");
CRASH_NOW();
}
// 2. Update the API assemblies
String update_error = update_api_assemblies_from_prebuilt();
if (!update_error.empty()) {
ERR_EXPLAIN(update_error);
CRASH_NOW();
}
// 3. Load the scripts domain again
if (_load_scripts_domain() != OK) {
ERR_EXPLAIN("Mono: Failed to load scripts domain");
CRASH_NOW();
}
// 4. Try loading the updated assemblies
if (!_try_load_api_assemblies()) {
// welp... too bad
if (_are_api_assemblies_out_of_sync()) {
if (core_api_assembly_out_of_sync) {
ERR_PRINT("The assembly '" CORE_API_ASSEMBLY_NAME "' is out of sync");
} else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed");
}
#ifdef TOOLS_ENABLED
if (editor_api_assembly_out_of_sync) {
ERR_PRINT("The assembly '" EDITOR_API_ASSEMBLY_NAME "' is out of sync");
}
#endif
CRASH_NOW();
} else {
ERR_EXPLAIN("Failed to load one of the API assemblies");
CRASH_NOW();
}
}
}
}
#ifdef TOOLS_ENABLED
bool GDMono::_load_tools_assemblies() {
@ -734,39 +795,11 @@ bool GDMono::_load_project_assembly() {
if (success) {
mono_assembly_set_main(project_assembly->get_assembly());
} else {
if (OS::get_singleton()->is_stdout_verbose())
print_error("Mono: Failed to load project assembly");
}
return success;
}
bool GDMono::_load_api_assemblies() {
if (!_load_core_api_assembly()) {
if (OS::get_singleton()->is_stdout_verbose())
print_error("Mono: Failed to load Core API assembly");
return false;
}
if (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)
return false;
#ifdef TOOLS_ENABLED
if (!_load_editor_api_assembly()) {
if (OS::get_singleton()->is_stdout_verbose())
print_error("Mono: Failed to load Editor API assembly");
return false;
}
if (editor_api_assembly_out_of_sync)
return false;
#endif
return true;
}
void GDMono::_install_trace_listener() {
#ifdef DEBUG_ENABLED
@ -784,78 +817,6 @@ void GDMono::_install_trace_listener() {
#endif
}
#ifdef TOOLS_ENABLED
String GDMono::_get_api_assembly_metadata_path() {
return GodotSharpDirs::get_res_metadata_dir().plus_file("api_assemblies.cfg");
}
void GDMono::metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated) {
String section = APIAssembly::to_string(p_api_type);
String path = _get_api_assembly_metadata_path();
Ref<ConfigFile> metadata;
metadata.instance();
metadata->load(path);
metadata->set_value(section, "invalidated", p_invalidated);
String assembly_path = GodotSharpDirs::get_res_assemblies_dir()
.plus_file(p_api_type == APIAssembly::API_CORE ?
CORE_API_ASSEMBLY_NAME ".dll" :
EDITOR_API_ASSEMBLY_NAME ".dll");
ERR_FAIL_COND(!FileAccess::exists(assembly_path));
uint64_t modified_time = FileAccess::get_modified_time(assembly_path);
metadata->set_value(section, "invalidated_asm_modified_time", String::num_uint64(modified_time));
String dir = path.get_base_dir();
if (!DirAccess::exists(dir)) {
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
ERR_FAIL_COND(!da);
Error err = da->make_dir_recursive(ProjectSettings::get_singleton()->globalize_path(dir));
ERR_FAIL_COND(err != OK);
}
Error save_err = metadata->save(path);
ERR_FAIL_COND(save_err != OK);
}
bool GDMono::metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type) {
String section = APIAssembly::to_string(p_api_type);
Ref<ConfigFile> metadata;
metadata.instance();
metadata->load(_get_api_assembly_metadata_path());
String assembly_path = GodotSharpDirs::get_res_assemblies_dir()
.plus_file(p_api_type == APIAssembly::API_CORE ?
CORE_API_ASSEMBLY_NAME ".dll" :
EDITOR_API_ASSEMBLY_NAME ".dll");
if (!FileAccess::exists(assembly_path))
return false;
uint64_t modified_time = FileAccess::get_modified_time(assembly_path);
uint64_t stored_modified_time = metadata->get_value(section, "invalidated_asm_modified_time", 0);
return metadata->get_value(section, "invalidated", false) && modified_time <= stored_modified_time;
}
String GDMono::get_invalidated_api_assembly_path(APIAssembly::Type p_api_type) {
return GodotSharpDirs::get_res_assemblies_dir()
.plus_file(p_api_type == APIAssembly::API_CORE ?
CORE_API_ASSEMBLY_NAME ".dll" :
EDITOR_API_ASSEMBLY_NAME ".dll");
}
#endif
Error GDMono::_load_scripts_domain() {
ERR_FAIL_COND_V(scripts_domain != NULL, ERR_BUG);
@ -903,11 +864,6 @@ Error GDMono::_unload_scripts_domain() {
tools_project_editor_assembly = NULL;
#endif
core_api_assembly_out_of_sync = false;
#ifdef TOOLS_ENABLED
editor_api_assembly_out_of_sync = false;
#endif
MonoDomain *domain = scripts_domain;
scripts_domain = NULL;
@ -944,57 +900,25 @@ Error GDMono::reload_scripts_domain() {
return err;
}
#ifdef MONO_GLUE_ENABLED
if (!_load_api_assemblies()) {
if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated))
#ifdef TOOLS_ENABLED
|| (editor_api_assembly && editor_api_assembly_out_of_sync)
#endif
) {
#ifdef TOOLS_ENABLED
// The assembly was successfully loaded, but the full api could not be cached.
// This is most likely an outdated assembly loaded because of an invalid version in the
// metadata, so we invalidate the version in the metadata and unload the script domain.
// Load assemblies. The API and tools assemblies are required,
// the application is aborted if these assemblies cannot be loaded.
if (core_api_assembly_out_of_sync) {
ERR_PRINT(OUT_OF_SYNC_ERR_MESSAGE(CORE_API_ASSEMBLY_NAME));
metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
} else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
ERR_PRINT("The loaded Core API assembly is in sync, but the cache update failed");
metadata_set_api_assembly_invalidated(APIAssembly::API_CORE, true);
}
_load_api_assemblies();
if (editor_api_assembly_out_of_sync) {
ERR_PRINT(OUT_OF_SYNC_ERR_MESSAGE(EDITOR_API_ASSEMBLY_NAME));
metadata_set_api_assembly_invalidated(APIAssembly::API_EDITOR, true);
}
err = _unload_scripts_domain();
if (err != OK) {
WARN_PRINT("Mono: Failed to unload scripts domain");
}
return ERR_CANT_RESOLVE;
#else
ERR_PRINT("The loaded API assembly is invalid");
CRASH_NOW();
#endif
} else {
return ERR_CANT_OPEN;
}
#if defined(TOOLS_ENABLED)
if (!_load_tools_assemblies()) {
ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies");
CRASH_NOW();
}
#ifdef TOOLS_ENABLED
ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies");
ERR_FAIL_COND_V(!_load_tools_assemblies(), ERR_CANT_OPEN);
#endif
// Load the project's main assembly. Here, during hot-reloading, we do
// consider failing to load the project's main assembly to be an error.
// However, unlike the API and tools assemblies, the application can continue working.
if (!_load_project_assembly()) {
print_error("Mono: Failed to load project assembly");
return ERR_CANT_OPEN;
}
#else
print_verbose("Mono: Glue disabled, ignoring script assemblies.");
#endif // MONO_GLUE_ENABLED
return OK;
}

View File

@ -104,6 +104,8 @@ class GDMono {
void _domain_assemblies_cleanup(uint32_t p_domain_id);
bool _are_api_assemblies_out_of_sync();
bool _load_corlib_assembly();
bool _load_core_api_assembly();
#ifdef TOOLS_ENABLED
@ -112,11 +114,8 @@ class GDMono {
#endif
bool _load_project_assembly();
bool _load_api_assemblies();
#ifdef TOOLS_ENABLED
String _get_api_assembly_metadata_path();
#endif
bool _try_load_api_assemblies();
void _load_api_assemblies();
void _install_trace_listener();
@ -157,9 +156,8 @@ public:
#endif
#ifdef TOOLS_ENABLED
void metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated);
bool metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type);
String get_invalidated_api_assembly_path(APIAssembly::Type p_api_type);
bool copy_prebuilt_api_assembly(APIAssembly::Type p_api_type);
String update_api_assemblies_from_prebuilt();
#endif
static GDMono *get_singleton() { return singleton; }
@ -203,6 +201,7 @@ public:
Error finalize_and_unload_domain(MonoDomain *p_domain);
void initialize();
void initialize_load_assemblies();
GDMono();
~GDMono();