Browse Source

C#: Add global class support

Co-authored-by: willnationsdev <[email protected]>
Raul Santos 2 years ago
parent
commit
a1f454fee3

+ 71 - 3
modules/mono/csharp_script.cpp

@@ -42,6 +42,7 @@
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 #include "core/os/keyboard.h"
 #include "core/os/keyboard.h"
+#include "editor/editor_file_system.h"
 #include "editor/editor_internal_calls.h"
 #include "editor/editor_internal_calls.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
@@ -535,6 +536,48 @@ String CSharpLanguage::_get_indentation() const {
 	return "\t";
 	return "\t";
 }
 }
 
 
+bool CSharpLanguage::handles_global_class_type(const String &p_type) const {
+	return p_type == get_type();
+}
+
+String CSharpLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
+	Ref<CSharpScript> scr = ResourceLoader::load(p_path, get_type());
+	if (!scr.is_valid() || !scr->valid || !scr->global_class) {
+		// Invalid script or the script is not a global class.
+		return String();
+	}
+
+	String name = scr->class_name;
+	if (unlikely(name.is_empty())) {
+		return String();
+	}
+
+	if (r_icon_path) {
+		if (scr->icon_path.is_empty() || scr->icon_path.is_absolute_path()) {
+			*r_icon_path = scr->icon_path.simplify_path();
+		} else if (scr->icon_path.is_relative_path()) {
+			*r_icon_path = p_path.get_base_dir().path_join(scr->icon_path).simplify_path();
+		}
+	}
+	if (r_base_type) {
+		bool found_global_base_script = false;
+		const CSharpScript *top = scr->base_script.ptr();
+		while (top != nullptr) {
+			if (top->global_class) {
+				*r_base_type = top->class_name;
+				found_global_base_script = true;
+				break;
+			}
+
+			top = top->base_script.ptr();
+		}
+		if (!found_global_base_script) {
+			*r_base_type = scr->get_instance_base_type();
+		}
+	}
+	return name;
+}
+
 String CSharpLanguage::debug_get_error() const {
 String CSharpLanguage::debug_get_error() const {
 	return _debug_error;
 	return _debug_error;
 }
 }
@@ -2203,11 +2246,21 @@ void CSharpScript::reload_registered_script(Ref<CSharpScript> p_script) {
 	update_script_class_info(p_script);
 	update_script_class_info(p_script);
 
 
 	p_script->_update_exports();
 	p_script->_update_exports();
+
+#if TOOLS_ENABLED
+	// If the EditorFileSystem singleton is available, update the file;
+	// otherwise, the file will be updated when the singleton becomes available.
+	EditorFileSystem *efs = EditorFileSystem::get_singleton();
+	if (efs) {
+		efs->update_file(p_script->get_path());
+	}
+#endif
 }
 }
 
 
 // Extract information about the script using the mono class.
 // Extract information about the script using the mono class.
 void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
 void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
 	bool tool = false;
 	bool tool = false;
+	bool global_class = false;
 
 
 	// TODO: Use GDExtension godot_dictionary
 	// TODO: Use GDExtension godot_dictionary
 	Array methods_array;
 	Array methods_array;
@@ -2217,11 +2270,17 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
 	Dictionary signals_dict;
 	Dictionary signals_dict;
 	signals_dict.~Dictionary();
 	signals_dict.~Dictionary();
 
 
+	String class_name;
+	String icon_path;
 	Ref<CSharpScript> base_script;
 	Ref<CSharpScript> base_script;
 	GDMonoCache::managed_callbacks.ScriptManagerBridge_UpdateScriptClassInfo(
 	GDMonoCache::managed_callbacks.ScriptManagerBridge_UpdateScriptClassInfo(
-			p_script.ptr(), &tool, &methods_array, &rpc_functions_dict, &signals_dict, &base_script);
+			p_script.ptr(), &class_name, &tool, &global_class, &icon_path,
+			&methods_array, &rpc_functions_dict, &signals_dict, &base_script);
 
 
+	p_script->class_name = class_name;
 	p_script->tool = tool;
 	p_script->tool = tool;
+	p_script->global_class = global_class;
+	p_script->icon_path = icon_path;
 
 
 	p_script->rpc_config.clear();
 	p_script->rpc_config.clear();
 	p_script->rpc_config = rpc_functions_dict;
 	p_script->rpc_config = rpc_functions_dict;
@@ -2519,6 +2578,15 @@ Error CSharpScript::reload(bool p_keep_state) {
 		update_script_class_info(this);
 		update_script_class_info(this);
 
 
 		_update_exports();
 		_update_exports();
+
+#if TOOLS_ENABLED
+		// If the EditorFileSystem singleton is available, update the file;
+		// otherwise, the file will be updated when the singleton becomes available.
+		EditorFileSystem *efs = EditorFileSystem::get_singleton();
+		if (efs) {
+			efs->update_file(script_path);
+		}
+#endif
 	}
 	}
 
 
 	return OK;
 	return OK;
@@ -2605,11 +2673,11 @@ bool CSharpScript::inherits_script(const Ref<Script> &p_script) const {
 }
 }
 
 
 Ref<Script> CSharpScript::get_base_script() const {
 Ref<Script> CSharpScript::get_base_script() const {
-	return base_script;
+	return base_script.is_valid() && !base_script->get_path().is_empty() ? base_script : nullptr;
 }
 }
 
 
 StringName CSharpScript::get_global_name() const {
 StringName CSharpScript::get_global_name() const {
-	return StringName();
+	return global_class ? StringName(class_name) : StringName();
 }
 }
 
 
 void CSharpScript::get_script_property_list(List<PropertyInfo> *r_list) const {
 void CSharpScript::get_script_property_list(List<PropertyInfo> *r_list) const {

+ 7 - 0
modules/mono/csharp_script.h

@@ -62,6 +62,7 @@ class CSharpScript : public Script {
 	friend class CSharpLanguage;
 	friend class CSharpLanguage;
 
 
 	bool tool = false;
 	bool tool = false;
+	bool global_class = false;
 	bool valid = false;
 	bool valid = false;
 	bool reload_invalidated = false;
 	bool reload_invalidated = false;
 
 
@@ -86,6 +87,8 @@ class CSharpScript : public Script {
 #endif
 #endif
 
 
 	String source;
 	String source;
+	String class_name;
+	String icon_path;
 
 
 	SelfList<CSharpScript> script_list = this;
 	SelfList<CSharpScript> script_list = this;
 
 
@@ -442,6 +445,10 @@ public:
 	/* TODO? */ void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override {}
 	/* TODO? */ void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override {}
 	/* TODO */ void add_global_constant(const StringName &p_variable, const Variant &p_value) override {}
 	/* TODO */ void add_global_constant(const StringName &p_variable, const Variant &p_value) override {}
 
 
+	/* SCRIPT GLOBAL CLASS FUNCTIONS */
+	virtual bool handles_global_class_type(const String &p_type) const override;
+	virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const override;
+
 	/* DEBUGGER FUNCTIONS */
 	/* DEBUGGER FUNCTIONS */
 	String debug_get_error() const override;
 	String debug_get_error() const override;
 	int debug_get_stack_level_count() const override;
 	int debug_get_stack_level_count() const override;

+ 3 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs

@@ -241,6 +241,9 @@ namespace Godot.SourceGenerators
         public static bool IsGodotClassNameAttribute(this INamedTypeSymbol symbol)
         public static bool IsGodotClassNameAttribute(this INamedTypeSymbol symbol)
             => symbol.ToString() == GodotClasses.GodotClassNameAttr;
             => symbol.ToString() == GodotClasses.GodotClassNameAttr;
 
 
+        public static bool IsGodotGlobalClassAttribute(this INamedTypeSymbol symbol)
+            => symbol.ToString() == GodotClasses.GlobalClassAttr;
+
         public static bool IsSystemFlagsAttribute(this INamedTypeSymbol symbol)
         public static bool IsSystemFlagsAttribute(this INamedTypeSymbol symbol)
             => symbol.ToString() == GodotClasses.SystemFlagsAttr;
             => symbol.ToString() == GodotClasses.SystemFlagsAttr;
 
 

+ 1 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs

@@ -11,6 +11,7 @@ namespace Godot.SourceGenerators
         public const string SignalAttr = "Godot.SignalAttribute";
         public const string SignalAttr = "Godot.SignalAttribute";
         public const string MustBeVariantAttr = "Godot.MustBeVariantAttribute";
         public const string MustBeVariantAttr = "Godot.MustBeVariantAttribute";
         public const string GodotClassNameAttr = "Godot.GodotClassNameAttribute";
         public const string GodotClassNameAttr = "Godot.GodotClassNameAttribute";
+        public const string GlobalClassAttr = "Godot.GlobalClassAttribute";
         public const string SystemFlagsAttr = "System.FlagsAttribute";
         public const string SystemFlagsAttr = "System.FlagsAttribute";
     }
     }
 }
 }

+ 13 - 6
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs

@@ -547,25 +547,32 @@ namespace Godot.SourceGenerators
             {
             {
                 if (memberNamedType.InheritsFrom("GodotSharp", "Godot.Resource"))
                 if (memberNamedType.InheritsFrom("GodotSharp", "Godot.Resource"))
                 {
                 {
-                    string nativeTypeName = memberNamedType.GetGodotScriptNativeClassName()!;
-
                     hint = PropertyHint.ResourceType;
                     hint = PropertyHint.ResourceType;
-                    hintString = nativeTypeName;
+                    hintString = GetTypeName(memberNamedType);
 
 
                     return true;
                     return true;
                 }
                 }
 
 
                 if (memberNamedType.InheritsFrom("GodotSharp", "Godot.Node"))
                 if (memberNamedType.InheritsFrom("GodotSharp", "Godot.Node"))
                 {
                 {
-                    string nativeTypeName = memberNamedType.GetGodotScriptNativeClassName()!;
-
                     hint = PropertyHint.NodeType;
                     hint = PropertyHint.NodeType;
-                    hintString = nativeTypeName;
+                    hintString = GetTypeName(memberNamedType);
 
 
                     return true;
                     return true;
                 }
                 }
             }
             }
 
 
+            static string GetTypeName(INamedTypeSymbol memberSymbol)
+            {
+                if (memberSymbol.GetAttributes()
+                    .Any(a => a.AttributeClass?.IsGodotGlobalClassAttribute() ?? false))
+                {
+                    return memberSymbol.Name;
+                }
+
+                return memberSymbol.GetGodotScriptNativeClassName()!;
+            }
+
             static bool GetStringArrayEnumHint(VariantType elementVariantType,
             static bool GetStringArrayEnumHint(VariantType elementVariantType,
                 AttributeData exportAttr, out string? hintString)
                 AttributeData exportAttr, out string? hintString)
             {
             {

+ 12 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GlobalClassAttribute.cs

@@ -0,0 +1,12 @@
+using System;
+
+#nullable enable
+
+namespace Godot
+{
+    /// <summary>
+    /// Exposes the target class as a global script class to Godot Engine.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Class)]
+    public sealed class GlobalClassAttribute : Attribute { }
+}

+ 25 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/IconAttribute.cs

@@ -0,0 +1,25 @@
+using System;
+
+namespace Godot
+{
+    /// <summary>
+    /// Specifies a custom icon for representing this class in the Godot Editor.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Class)]
+    public sealed class IconAttribute : Attribute
+    {
+        /// <summary>
+        /// File path to a custom icon for representing this class in the Godot Editor.
+        /// </summary>
+        public string Path { get; }
+
+        /// <summary>
+        /// Specify the custom icon that represents the class.
+        /// </summary>
+        /// <param name="path">File path to the custom icon.</param>
+        public IconAttribute(string path)
+        {
+            Path = path;
+        }
+    }
+}

+ 1 - 1
modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs

@@ -25,7 +25,7 @@ namespace Godot.Bridge
         public delegate* unmanaged<godot_string*, godot_ref*, void> ScriptManagerBridge_GetOrCreateScriptBridgeForPath;
         public delegate* unmanaged<godot_string*, godot_ref*, void> ScriptManagerBridge_GetOrCreateScriptBridgeForPath;
         public delegate* unmanaged<IntPtr, void> ScriptManagerBridge_RemoveScriptBridge;
         public delegate* unmanaged<IntPtr, void> ScriptManagerBridge_RemoveScriptBridge;
         public delegate* unmanaged<IntPtr, godot_bool> ScriptManagerBridge_TryReloadRegisteredScriptWithClass;
         public delegate* unmanaged<IntPtr, godot_bool> ScriptManagerBridge_TryReloadRegisteredScriptWithClass;
-        public delegate* unmanaged<IntPtr, godot_bool*, godot_array*, godot_dictionary*, godot_dictionary*, godot_ref*, void> ScriptManagerBridge_UpdateScriptClassInfo;
+        public delegate* unmanaged<IntPtr, godot_string*, godot_bool*, godot_bool*, godot_string*, godot_array*, godot_dictionary*, godot_dictionary*, godot_ref*, void> ScriptManagerBridge_UpdateScriptClassInfo;
         public delegate* unmanaged<IntPtr, IntPtr*, godot_bool, godot_bool> ScriptManagerBridge_SwapGCHandleForType;
         public delegate* unmanaged<IntPtr, IntPtr*, godot_bool, godot_bool> ScriptManagerBridge_SwapGCHandleForType;
         public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, godot_string*, void*, int, void>, void> ScriptManagerBridge_GetPropertyInfoList;
         public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, godot_string*, void*, int, void>, void> ScriptManagerBridge_GetPropertyInfoList;
         public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, void*, int, void>, void> ScriptManagerBridge_GetPropertyDefaultValues;
         public delegate* unmanaged<IntPtr, delegate* unmanaged<IntPtr, void*, int, void>, void> ScriptManagerBridge_GetPropertyDefaultValues;

+ 27 - 2
modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs

@@ -298,6 +298,13 @@ namespace Godot.Bridge
 
 
                 _pathTypeBiMap.Add(scriptPathAttr.Path, type);
                 _pathTypeBiMap.Add(scriptPathAttr.Path, type);
 
 
+                // This method may be called before initialization.
+                if (NativeFuncs.godotsharp_dotnet_module_is_initialized().ToBool() && Engine.IsEditorHint())
+                {
+                    using godot_string scriptPath = Marshaling.ConvertStringToNative(scriptPathAttr.Path);
+                    NativeFuncs.godotsharp_internal_editor_file_system_update_file(scriptPath);
+                }
+
                 if (AlcReloadCfg.IsAlcReloadingEnabled)
                 if (AlcReloadCfg.IsAlcReloadingEnabled)
                 {
                 {
                     AddTypeForAlcReloading(type);
                     AddTypeForAlcReloading(type);
@@ -584,7 +591,8 @@ namespace Godot.Bridge
         }
         }
 
 
         [UnmanagedCallersOnly]
         [UnmanagedCallersOnly]
-        internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_bool* outTool,
+        internal static unsafe void UpdateScriptClassInfo(IntPtr scriptPtr, godot_string* outClassName,
+            godot_bool* outTool, godot_bool* outGlobal, godot_string* outIconPath,
             godot_array* outMethodsDest, godot_dictionary* outRpcFunctionsDest,
             godot_array* outMethodsDest, godot_dictionary* outRpcFunctionsDest,
             godot_dictionary* outEventSignalsDest, godot_ref* outBaseScript)
             godot_dictionary* outEventSignalsDest, godot_ref* outBaseScript)
         {
         {
@@ -593,6 +601,8 @@ namespace Godot.Bridge
                 // Performance is not critical here as this will be replaced with source generators.
                 // Performance is not critical here as this will be replaced with source generators.
                 var scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr);
                 var scriptType = _scriptTypeBiMap.GetScriptType(scriptPtr);
 
 
+                *outClassName = Marshaling.ConvertStringToNative(scriptType.Name);
+
                 *outTool = scriptType.GetCustomAttributes(inherit: false)
                 *outTool = scriptType.GetCustomAttributes(inherit: false)
                     .OfType<ToolAttribute>()
                     .OfType<ToolAttribute>()
                     .Any().ToGodotBool();
                     .Any().ToGodotBool();
@@ -607,6 +617,18 @@ namespace Godot.Bridge
                 if (!(*outTool).ToBool() && scriptType.Assembly.GetName().Name == "GodotTools")
                 if (!(*outTool).ToBool() && scriptType.Assembly.GetName().Name == "GodotTools")
                     *outTool = godot_bool.True;
                     *outTool = godot_bool.True;
 
 
+                var globalAttr = scriptType.GetCustomAttributes(inherit: false)
+                    .OfType<GlobalClassAttribute>()
+                    .FirstOrDefault();
+
+                *outGlobal = (globalAttr != null).ToGodotBool();
+
+                var iconAttr = scriptType.GetCustomAttributes(inherit: false)
+                    .OfType<IconAttribute>()
+                    .FirstOrDefault();
+
+                *outIconPath = Marshaling.ConvertStringToNative(iconAttr?.Path);
+
                 // Methods
                 // Methods
 
 
                 // Performance is not critical here as this will be replaced with source generators.
                 // Performance is not critical here as this will be replaced with source generators.
@@ -693,7 +715,7 @@ namespace Godot.Bridge
                 }
                 }
 
 
                 *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy(
                 *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy(
-                    (godot_dictionary)(rpcFunctions).NativeValue);
+                    (godot_dictionary)rpcFunctions.NativeValue);
 
 
                 // Event signals
                 // Event signals
 
 
@@ -755,7 +777,10 @@ namespace Godot.Bridge
             catch (Exception e)
             catch (Exception e)
             {
             {
                 ExceptionUtils.LogException(e);
                 ExceptionUtils.LogException(e);
+                *outClassName = default;
                 *outTool = godot_bool.False;
                 *outTool = godot_bool.False;
+                *outGlobal = godot_bool.False;
+                *outIconPath = default;
                 *outMethodsDest = NativeFuncs.godotsharp_array_new();
                 *outMethodsDest = NativeFuncs.godotsharp_array_new();
                 *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new();
                 *outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new();
                 *outEventSignalsDest = NativeFuncs.godotsharp_dictionary_new();
                 *outEventSignalsDest = NativeFuncs.godotsharp_dictionary_new();

+ 4 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs

@@ -37,6 +37,8 @@ namespace Godot.NativeInterop
 
 
         // Custom functions
         // Custom functions
 
 
+        internal static partial godot_bool godotsharp_dotnet_module_is_initialized();
+
         public static partial IntPtr godotsharp_method_bind_get_method(in godot_string_name p_classname,
         public static partial IntPtr godotsharp_method_bind_get_method(in godot_string_name p_classname,
             in godot_string_name p_methodname);
             in godot_string_name p_methodname);
 
 
@@ -52,6 +54,8 @@ namespace Godot.NativeInterop
         internal static partial void godotsharp_stack_info_vector_destroy(
         internal static partial void godotsharp_stack_info_vector_destroy(
             ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
             ref DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
 
 
+        internal static partial void godotsharp_internal_editor_file_system_update_file(in godot_string p_script_path);
+
         internal static partial void godotsharp_internal_script_debugger_send_error(in godot_string p_func,
         internal static partial void godotsharp_internal_script_debugger_send_error(in godot_string p_func,
             in godot_string p_file, int p_line, in godot_string p_err, in godot_string p_descr,
             in godot_string p_file, int p_line, in godot_string p_err, in godot_string p_descr,
             godot_bool p_warning, in DebuggingUtils.godot_stack_info_vector p_stack_info_vector);
             godot_bool p_warning, in DebuggingUtils.godot_stack_info_vector p_stack_info_vector);

+ 2 - 0
modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj

@@ -60,7 +60,9 @@
     <Compile Include="Core\Attributes\ExportCategoryAttribute.cs" />
     <Compile Include="Core\Attributes\ExportCategoryAttribute.cs" />
     <Compile Include="Core\Attributes\ExportGroupAttribute.cs" />
     <Compile Include="Core\Attributes\ExportGroupAttribute.cs" />
     <Compile Include="Core\Attributes\ExportSubgroupAttribute.cs" />
     <Compile Include="Core\Attributes\ExportSubgroupAttribute.cs" />
+    <Compile Include="Core\Attributes\GlobalClassAttribute.cs" />
     <Compile Include="Core\Attributes\GodotClassNameAttribute.cs" />
     <Compile Include="Core\Attributes\GodotClassNameAttribute.cs" />
+    <Compile Include="Core\Attributes\IconAttribute.cs" />
     <Compile Include="Core\Attributes\MustBeVariantAttribute.cs" />
     <Compile Include="Core\Attributes\MustBeVariantAttribute.cs" />
     <Compile Include="Core\Attributes\RpcAttribute.cs" />
     <Compile Include="Core\Attributes\RpcAttribute.cs" />
     <Compile Include="Core\Attributes\ScriptPathAttribute.cs" />
     <Compile Include="Core\Attributes\ScriptPathAttribute.cs" />

+ 24 - 0
modules/mono/glue/runtime_interop.cpp

@@ -40,6 +40,10 @@
 #include "core/os/os.h"
 #include "core/os/os.h"
 #include "core/string/string_name.h"
 #include "core/string/string_name.h"
 
 
+#ifdef TOOLS_ENABLED
+#include "editor/editor_file_system.h"
+#endif
+
 #include "../interop_types.h"
 #include "../interop_types.h"
 
 
 #include "modules/mono/csharp_script.h"
 #include "modules/mono/csharp_script.h"
@@ -57,6 +61,10 @@ static_assert(sizeof(SafeRefCount) == sizeof(uint32_t));
 
 
 typedef Object *(*godotsharp_class_creation_func)();
 typedef Object *(*godotsharp_class_creation_func)();
 
 
+bool godotsharp_dotnet_module_is_initialized() {
+	return GDMono::get_singleton()->is_initialized();
+}
+
 MethodBind *godotsharp_method_bind_get_method(const StringName *p_classname, const StringName *p_methodname) {
 MethodBind *godotsharp_method_bind_get_method(const StringName *p_classname, const StringName *p_methodname) {
 	return ClassDB::get_method(*p_classname, *p_methodname);
 	return ClassDB::get_method(*p_classname, *p_methodname);
 }
 }
@@ -304,6 +312,20 @@ void godotsharp_internal_new_csharp_script(Ref<CSharpScript> *r_dest) {
 	memnew_placement(r_dest, Ref<CSharpScript>(memnew(CSharpScript)));
 	memnew_placement(r_dest, Ref<CSharpScript>(memnew(CSharpScript)));
 }
 }
 
 
+void godotsharp_internal_editor_file_system_update_file(const String *p_script_path) {
+#if TOOLS_ENABLED
+	// If the EditorFileSystem singleton is available, update the file;
+	// otherwise, the file will be updated when the singleton becomes available.
+	EditorFileSystem *efs = EditorFileSystem::get_singleton();
+	if (efs) {
+		efs->update_file(*p_script_path);
+	}
+#else
+	// EditorFileSystem is only available when running in the Godot editor.
+	DEV_ASSERT(false);
+#endif
+}
+
 bool godotsharp_internal_script_load(const String *p_path, Ref<CSharpScript> *r_dest) {
 bool godotsharp_internal_script_load(const String *p_path, Ref<CSharpScript> *r_dest) {
 	Ref<Resource> res = ResourceLoader::load(*p_path);
 	Ref<Resource> res = ResourceLoader::load(*p_path);
 	if (res.is_valid()) {
 	if (res.is_valid()) {
@@ -1391,11 +1413,13 @@ void godotsharp_object_to_string(Object *p_ptr, godot_string *r_str) {
 // The order in this array must match the declaration order of
 // The order in this array must match the declaration order of
 // the methods in 'GodotSharp/Core/NativeInterop/NativeFuncs.cs'.
 // the methods in 'GodotSharp/Core/NativeInterop/NativeFuncs.cs'.
 static const void *unmanaged_callbacks[]{
 static const void *unmanaged_callbacks[]{
+	(void *)godotsharp_dotnet_module_is_initialized,
 	(void *)godotsharp_method_bind_get_method,
 	(void *)godotsharp_method_bind_get_method,
 	(void *)godotsharp_get_class_constructor,
 	(void *)godotsharp_get_class_constructor,
 	(void *)godotsharp_engine_get_singleton,
 	(void *)godotsharp_engine_get_singleton,
 	(void *)godotsharp_stack_info_vector_resize,
 	(void *)godotsharp_stack_info_vector_resize,
 	(void *)godotsharp_stack_info_vector_destroy,
 	(void *)godotsharp_stack_info_vector_destroy,
+	(void *)godotsharp_internal_editor_file_system_update_file,
 	(void *)godotsharp_internal_script_debugger_send_error,
 	(void *)godotsharp_internal_script_debugger_send_error,
 	(void *)godotsharp_internal_script_debugger_is_active,
 	(void *)godotsharp_internal_script_debugger_is_active,
 	(void *)godotsharp_internal_object_get_associated_gchandle,
 	(void *)godotsharp_internal_object_get_associated_gchandle,

+ 2 - 8
modules/mono/mono_gd/gd_mono.cpp

@@ -443,6 +443,8 @@ void GDMono::initialize() {
 	print_verbose(".NET: GodotPlugins initialized");
 	print_verbose(".NET: GodotPlugins initialized");
 
 
 	_on_core_api_assembly_loaded();
 	_on_core_api_assembly_loaded();
+
+	initialized = true;
 }
 }
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
@@ -527,14 +529,6 @@ Error GDMono::reload_project_assemblies() {
 
 
 GDMono::GDMono() {
 GDMono::GDMono() {
 	singleton = this;
 	singleton = this;
-
-	runtime_initialized = false;
-	finalizing_scripts_domain = false;
-
-	api_core_hash = 0;
-#ifdef TOOLS_ENABLED
-	api_editor_hash = 0;
-#endif
 }
 }
 
 
 GDMono::~GDMono() {
 GDMono::~GDMono() {

+ 8 - 4
modules/mono/mono_gd/gd_mono.h

@@ -59,8 +59,9 @@ struct PluginCallbacks {
 } // namespace gdmono
 } // namespace gdmono
 
 
 class GDMono {
 class GDMono {
-	bool runtime_initialized;
-	bool finalizing_scripts_domain;
+	bool initialized = false;
+	bool runtime_initialized = false;
+	bool finalizing_scripts_domain = false;
 
 
 	void *hostfxr_dll_handle = nullptr;
 	void *hostfxr_dll_handle = nullptr;
 	bool is_native_aot = false;
 	bool is_native_aot = false;
@@ -72,9 +73,9 @@ class GDMono {
 	bool _load_project_assembly();
 	bool _load_project_assembly();
 #endif
 #endif
 
 
-	uint64_t api_core_hash;
+	uint64_t api_core_hash = 0;
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-	uint64_t api_editor_hash;
+	uint64_t api_editor_hash = 0;
 #endif
 #endif
 	void _init_godot_api_hashes();
 	void _init_godot_api_hashes();
 
 
@@ -119,6 +120,9 @@ public:
 		return singleton;
 		return singleton;
 	}
 	}
 
 
+	_FORCE_INLINE_ bool is_initialized() const {
+		return initialized;
+	}
 	_FORCE_INLINE_ bool is_runtime_initialized() const {
 	_FORCE_INLINE_ bool is_runtime_initialized() const {
 		return runtime_initialized;
 		return runtime_initialized;
 	}
 	}

+ 1 - 1
modules/mono/mono_gd/gd_mono_cache.h

@@ -90,7 +90,7 @@ struct ManagedCallbacks {
 	using FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath = void(GD_CLR_STDCALL *)(const String *, Ref<CSharpScript> *);
 	using FuncScriptManagerBridge_GetOrCreateScriptBridgeForPath = void(GD_CLR_STDCALL *)(const String *, Ref<CSharpScript> *);
 	using FuncScriptManagerBridge_RemoveScriptBridge = void(GD_CLR_STDCALL *)(const CSharpScript *);
 	using FuncScriptManagerBridge_RemoveScriptBridge = void(GD_CLR_STDCALL *)(const CSharpScript *);
 	using FuncScriptManagerBridge_TryReloadRegisteredScriptWithClass = bool(GD_CLR_STDCALL *)(const CSharpScript *);
 	using FuncScriptManagerBridge_TryReloadRegisteredScriptWithClass = bool(GD_CLR_STDCALL *)(const CSharpScript *);
-	using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, bool *, Array *, Dictionary *, Dictionary *, Ref<CSharpScript> *);
+	using FuncScriptManagerBridge_UpdateScriptClassInfo = void(GD_CLR_STDCALL *)(const CSharpScript *, String *, bool *, bool *, String *, Array *, Dictionary *, Dictionary *, Ref<CSharpScript> *);
 	using FuncScriptManagerBridge_SwapGCHandleForType = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr *, bool);
 	using FuncScriptManagerBridge_SwapGCHandleForType = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr *, bool);
 	using FuncScriptManagerBridge_GetPropertyInfoList = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyInfoList_Add);
 	using FuncScriptManagerBridge_GetPropertyInfoList = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyInfoList_Add);
 	using FuncScriptManagerBridge_GetPropertyDefaultValues = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add);
 	using FuncScriptManagerBridge_GetPropertyDefaultValues = void(GD_CLR_STDCALL *)(CSharpScript *, Callback_ScriptManagerBridge_GetPropertyDefaultValues_Add);